设备驱动初学之—— 字符设备驱动
1. 结构
cdev结构体(所属模块、操作结构体、设备号)、分配和释放设备号、file_operations结构体
- cdev结构体的设备号分为主设备号和次设备号
- 一些linux内核指令(常用):
void cdev_init (srtuct cdev *,struct file_operation *) //初始化cdev的成员 ,建立cdev和file_operation 的联系
struct cdev *cdev_alloc(void) //申请一个cdev动态内存
void cdev_put (struct cdev *p) //???
int cdev_add (struct cdev *,cdev_t,unsigned) //增加一个cdev
void cdev_del (struct cdev *) //删除一个cdev
- 分配和释放设备号:在向系统注册字符设备之前,调用register_chrdev_region()、alloc_chrdev_region()
- file_operations结构体:它的成员函数是字符驱动和内核虚拟文件系统的接口API,是用户控件对LINUX操作系统的最终落实者
read() write() ioctl(),对file_operations结构体的成员函数实例化是设备驱动的关键。
2. 设备驱动的组成
- 字符设备驱动模块的加载与卸载函数
- 字符设备驱动的file_operations结构体的成员函数
ioctl()例子 IO控制函数
long xxx_ioctl(struct file *filp,unsigned ing cmd ) //file为文件结构体指针
{
...'
switch(cmd){
case xxx_cmd1:
...
break;
case xxx_cmd2:
...
break;
default:
/*不能支持的命令*/
return —ENOTTY;
}
return 0;
}
ssize_t xxx_read (struct file *filp ,char __user *buf,size_t count, loff_t *f_pos)
{
...
copy_to_user(buf,...,...)
...
}
3、使文件私有数据
参考https://blog.csdn.net/u013377887/article/details/72792867
潜规则:文件的私有数据file->private.data 指向设备结构体。存在多个设备时,不对 xxx_read()等函数修改的前提下 ,只对xxx_init()、xxx_exit()、xxx_open()修改,即可添加N个设备。
支持N个设备的xxx驱动
自定义的设备结构体,通常有以下操作
1、定义一个全局的变量
2、在probe 或者入口函数中初始化 全局变量
3、在 file_operation open 中,将全局变量赋值给file的private_data
4、在read() 、write、ioctl()的操作中,通过file取中private_data 进行操作(使文件私有数据)局部作用域???
4、container_of
通过结构体成员的指针、结构体的类型、结构体成员类型。找到结构体的指针。
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
offsetof 通过将 0 地址,强制转为为TYPE类型的指针,然后取它的成员 NUMBER , 在转为size_t 类型。即得到NUMBER 在这个TYPE的位置。