Linux驱动开发

Posted XXX_UUU_XXX

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux驱动开发相关的知识,希望对你有一定的参考价值。

通常不直接操作寄存器,而是使用各种驱动框架进行开发。

驱动表示为/dev/下的文件

设备树 .dts文件,描述板子的设备信息

驱动获取设备传感器数据

查看函数缺少哪个头文件,在终端使用man命令查看,安装sudo apt-get install manpages-dev,例如

  • man 2 read    得到#include <unistd.h>
  • man atoi         得到 #include <stdlib.h>
  • man memcpy  得到#include <string.h>

模块加载和卸载函数

  • module_init(xxx_init);
  • module_exit(xxx_exit);

模块加载

  • insmod xxx.ko
  • modprobe xxx.ko (推荐)第一次加载驱动时需要运行命令:depmod

模块卸载

  • rmmod xxx.ko (推荐)
  • modprobe -r xxx.ko

旧版本注册和注销字符设备

旧版本注册字符设备和注销字符设备,需要我们事先确定好哪些主设备号没有使用;会将一个主设备号下的所有次设备号都使用掉。

  • static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
  • static inline void unregister_chrdev(unsigned int major, const char *name)

查看使用的设备号

  • cat /proc/devices

设备号

  • 32位unsigned int类型,主设备号高12位,次设备号低20位,主设备号范围0-4095

内核空间打开设备

  • static int xxx_open(struct inode *inode, struct file *filp)
  • inode:传递给驱动的inode,offt:相对于文件首地址的偏移

内核空间从设备读取

  • static ssize_t xxx_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
  • filp:文件描述符,buf:读取用户空间的数据,cnt:读取数据的长度,offt:相对于文件首地址的偏移
  • 内核中read,copy_to_user(目的to, 源from, 长度count)内核把数据发送给应用
  • copy_to_user(用户buf, 内核buf, 数据长度count) ,返回0表示成功

内核空间向设备写数据

  • static ssize_t xxx_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
  • filp:文件描述符,buf:写入设备的数据,cnt:写入数据的长度,offt:相对于文件首地址的偏移
  • 内核中write,copy_from_user(目的to, 源from, 长度count)内核接受应用的数据
  • copy_from_user(内核buf, 用户buf, 数据长度count),返回0表示成功 

内核空间关闭设备

  • static int xxx_release(struct inode *inode, struct file *filp)
  • inode:传递给驱动的inode,offt:相对于文件首地址的偏移

内核输出信息

  • printk
  • 8个级别,0优先级最高,7优先级最低,默认为4

用户空间打开

  • int open(const char *pathname, int flags);
  • pathname:设备名,flags:打开模式(O_RDONLY只读模式,O_WRONLY只写模式,O_RDWR读写模式,等)

用户空间读取

  • ssize_t read(int fd, void *buf, size_t count);
  • fd:文件描述符,buf:数据缓存区,count:读取数据的长度

用户空间写入

  • ssize_t write(int fd, const void *buf, size_t count);
  • fd:文件描述符,buf:数据缓存区,count:写入数据的长度

用户空间关闭

  • int close(int fd);
  • fd:文件描述符

创建设备节点文件

  • mknod /dev/xxx c major minor
  • c:表示字符设备,major:主设备号,minor:次设备号
  • 驱动加载成功需要在/dev 目录下创建一个与之对应的设备节点文件,应用程序就是通过操作这个设备节点文件来完成对具体设备的操作

内存管理单元MMU(Memory Manage Unit)

MMU功能

  1. 完成虚拟空间到物理空间的映射
  2. 内存保护,设置存储器的访问权限,设置虚拟存储空间的缓冲特性。

地址映射

Linux驱动开发不可以直接对寄存器的物理地址进行读写操作,需要得到物理地址在Linux系统中对应的虚拟地址。Linux内核启动时会初始化MMU,设置好内存映射后CPU访问的都是虚拟地址。使用函数 ioremap 和 iounmap 。

ioremap

  • 获取指定物理地址空间对应的虚拟地址空间
  • ioremap(phys_addr, size)
  • phys_addr:映射的物理地址的起始地址,size:映射的内存字节大小,返回虚拟地址

iounmap

  • 释放ioremap所做的映射
  • ioremap(virt_addr)
  • virt_addr:虚拟地址的起始地址

内存访问函数

Linux使用操作函数来对映射后的内存进行读写操作

  • 读操作函数readb(addr)、readw(addr)、readl(addr)分别对应8位、16位、32位读操作,addr:读取的内存地址,返回值为读取到的数据。
  • 写操作函数writeb(value, addr)、writew(value, addr)、writel(value, addr)分别对应8位、16位、32位写操作,value:写入值,addr:写入的内存地址

新版本注册和注销字符设备

新版本注册字符设备和注销字符设备,需要几个就申请几个,由 Linux 内核分配设备可以使用的设备号

如果没有指定设备号的话就使用如下函数来申请设备号

  • int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
  • dev:要申请的设备号(指针类型),baseminor:次设备号(通常为0),count:要申请的数量,一 般都是一个,name:设备名
  • 设备号申请成功后使用 MAJOR 和 MINOR 来提取出主设备号和次设备号

如果给定了设备的主设备号和次设备号就使用如下所示函数来注册设备号即可

  • int register_chrdev_region(dev_t from, unsigned count, const char *name)
  • from:要申请的起始设备号,也就是给定的设备号,count:要申请的数量,一 般都是一个,name:设备名

注销字符设备

  • void unregister_chrdev_region(dev_t from, unsigned count)

以上是关于Linux驱动开发的主要内容,如果未能解决你的问题,请参考以下文章

Linux驱动开发:字符设备驱动开发

Linux驱动开发:字符设备驱动开发

linux驱动开发 - 01_字符设备驱动开发

嵌入式开发(七):linux字符型设备驱动初步

Linux驱动开发:设备号

Linux驱动开发:设备号