字符设备驱动
字符设备驱动开发
字符设备驱动
图 1 字符设备驱动框架
字符设备是Linux驱动中最基本的设备驱动。其一个字节一个字节,按照字节流进行读写操作。常见的点灯,按键,IIC,SPI等都是字符驱动设备。
在Linux内部,一切都是设备都被抽象为文件。驱动加载成功,在/dev目录下生成对应的文件。应用程序操作/dev/xxx的方式来实现对于设备硬件操作。
例:对于LED这种硬件操作。在驱动内核中,与LED对应的驱动设备文件/dev/led。使用open,close来打开与关闭驱动设备文件。
使用write函数来向驱动设备文件写入参数,进而达到LED的控制参数的调整,来控制LED等硬件。
备注:open,write,close,read函数是由于C库提供。一条指令的执行,在空间上分为用户空间与内核空间(这种空间方式,主要是为了保护系统,内核空间主要与设备具体操作有关)。
在用户空间使用open,write,read,close函数,通过系统调用,进入内核空间,进而操作具体硬件。
驱动框架
驱动运行方式:1.驱动编译进Linux,随着Linux内核启动而运行。2.将驱动编译成驱动模块(后缀.KO的模块文件),使用ismod或modprobe指令来加载驱动模块,rmmod指令来卸载模块。
驱动加载与卸载注册函数
Module_init(xxx_init),注册模块加载函数,使用insmod,modprobe指令,会调用module_init(xxx_init),进行注册。
Module_exit(xxx_exit),注册模块卸载函数,使用rmmod指令,会调用module_exit(xxx_exit),进行卸载。
图 3 驱动入口与出口函数框架
字符设备注册与注销
驱动模块加载成功,接下来进行注册字符设备。卸载驱动模块,需要注销字符设备。字符设备的注册与注销函数原型:
图 4 字符设备的注册与注销函数
major:主设备号,linux中每个设备都分配一个设备号。设备主要分为主设备号与次设备号。
name:设备名字,是一串字符串。
fops:结构体file_operations类型指针,设备的操作函数集合。
使用register_chrdev函数来完成字符设备的注册。会在对应的 /proc/devices下生成对应的设备。核对信息:设备号,设备名字。
设备操作函数
file_operations结构体包含对设备的具体操作函数。主要实现的内容:open,write,read,close。
图 5 设备操作函数集合
对应的操作函数原型:
图 6 对应的设备操作函数原型
添加LICENSE和作者信息
图 7 作者与LICENSE信息
设备号
每个设备在linux下都有对应的设备号,设备号分为主设备号与次设备号两部分组成。一般主设备号表示一个具体的驱动,次设备号表示使用驱动的各个设备。
图 8 设备号设置
宏 MINORBITS 表示次设备号位数,一共是 20 位。宏 MINORMASK 表示次设备号掩码。
宏 MAJOR 用于从 dev_t 中获取主设备号,将 dev_t 右移 20 位即可。
宏 MINOR 用于从 dev_t 中获取次设备号,取 dev_t 的低 20 位的值即可。
宏 MKDEV 用于将给定的主设备号和次设备号的值组合成 dev_t 类型的设备号。
设备号分配
静态分配设备号:
静态设备号分配,主要针对主设备号分配。注册字符设备时候给设备指定一个设备号。这个设备号是静态指定的一个指定号(有些设备号被Linux内核分配掉)。
具体在/proc/devices 目录下,查看系统中已经使用的设备号。
动态分配:
静态分配需要先查看设备号的分配情况。比较容易引起设备号冲突。推荐使用动态分配,主动申请一个设备号,避免冲突。
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
*dev:保存申请到的设备号。baseminor:次设备号起始地址。一般从0开始。count:申请设备号数量。name:设备名字。
注销字符设备需要释放设备号:
void unregister_chrdev_region(dev_t from, unsigned count)
from:待释放的设备号
count:待释放的设备号数量。
注意事项:
Insmod指令加载驱动成功之后,使用lsmod指令之后,可以查看到驱动模块:查看驱动模块(基于驱动名字查看)。
在/proc/devices目录下,可以查看到具体的设备:基于设备号与设备名字。
设备节点文件创建:
mknod /dev/chrdevbase c 200 0
c:表示一个字符设备
200:主设备号
0:设备的次设备号
后续可以继续操作设备节点文件。