字符设备驱动框架
1、测试函数
1)open(),打开字符设备;
2)ioctl(),操作字符设备;
3)close(),关闭字符设备;
2、字符设备函数
1)smartchip _init(),模块加载;(insmod smartChip.ko)
A、register_chrdev_region(),申请设备号;
B、alloc_chrdev_region(),申请设备号;
C、smartchip_setup_cdev();
a、cdev_init(),初始化cdev的成员;
b、cdev_add(),注册字符设备;
2)smartchip_open(),打开设备;
3)smartchip_ioctl(),模块操作;
A、get_user(),用户空间到内核空间;
B、put_user(),内核空间到用户空间;
4)smartchip_release(),关闭设备;
5)smartchip_cleanup(),模块卸载;(rmmod smartChip.ko)
A、cdev_del(),注销字符设备;
B、unregister_chrdev_region(),释放设备号;
3、 I2C设备访问
1)smartchip_i2c_read();
A、get_fs();
B、set_fs();
C、sys_open(),打开I2C设备;
D、sys_ioctl(),操作I2C设备;
E、sys_read(),读I2C设备;
F、sys_close(),关闭I2C设备;
2)smartchip_i2c_write();
A、get_fs();
B、set_fs();
C、sys_open(),打开I2C设备;
D、sys_ioctl(),操作I2C设备;
E、sys_write(),写I2C设备;
F、sys_close(),关闭I2C设备;
细节:
1、cdev 结构体描述一个字符设备;
2、MKDEV(intmajor, int minor),通过主设备号和次设备号生成dev_t;
MAJOR(dev_t dev),从dev_t获得主设备号;
MINOR(dev_t dev),从dev_t获得从设备号;
3、cdev_init()函数用于初始化cdev的成员,并建立cdev和file_operations之间的连接;
cdev_add()函数向系统添加一个cdev,完成字符设备的注册;
cdev_del()函数向系统删除一个cdev,完成字符设备的注销;
register_chrdev_region()或alloc_chrdev_region()函数向系统申请设备号,前者已知起始设备的设备号,后者设备号未知,向系统动态申请未被占用的设备号;
unregister_chrdev_region()函数用于释放原先申请的设备号;
4、file_operations 结构体中的成员函数是字符设备驱动程序的主体内容,是字符设备驱动与内核的接口,是用户空间对Linux进行系统调用最终的落实者;
5、当用户空间调用Linux API函数open()打开设备文件时,设备驱动的open()函数最终被调用。驱动程序可以不实现这个函数,在这种情况下,设备的打开操作永远成功;
6、filp是文件结构体指针;
7、私有数据private_data,在open()函数中将文件的私有数据private_data指向设备结构体,然后在ioctl()函数中通过private_data访问设备结构体;
8、open()函数中的container_of()的作用是通过结构体成员的指针找到对应结构体的指针,第一个参数为结构体成员的指针,第二个参数为整个结构体的类型,第三个参数为传入的第一个参数的结构体成员的类型,container_of()返回值为整个结构体的指针;
9、内核空间与用户空间访问函数:
A、copy_from_user()完成用户空间到内核空间的拷贝,copy_to_user()完成内核空间到用户空间的拷贝,这两个函数均返回不能被复制的字节数,如果复制成功,返回值为0;
B、如果要复制的内存是简单类型,如char、int、long等,则可以使用简单的put_user()和get_user(),get_user(val,(int *) arg),用户空间到内核空间,arg是用户空间的地址;
10、I/O控制函数的cmd参数是事先定义的I/O控制命令,arg是对应于该命令的参数;
三个重要数据结构
1、file_operations结构
2、file结构:
A.file结构与用户空间程序中的FILE没有任何关联,前者是一个内核结构,不会出现在用户程序中,后者在C库中定义且不会出现在内核代码中。
B.file结构代表一个打开的文件,由内核在open时创建,并传递给该文件上进行操作的所有函数,直到最后的close函数。在文件的所有实例都被关闭之后,内核会释放这个数据结构。
3、inode结构:
内核用inode结构在内部表示文件,因此它和file结构不同,后者表示打开的文件描述符。对单个文件,可能会有许多个表示文件打开的文件描述符的file结构,但它们都指向单个inode结构。
open函数
int(*open) (struct inode *inode, struct file *filp);
其中的inode参数在其i_cdev字段中包含了我们所需要的信息,即我们先前设置的cdev结构。唯一的问题是,我们通常不需要cdev结构本身,而是希望得到包含cdev结构的smartchip_dev结构。
container_of(pointer, container_type, container_field);
这个宏需要一个container_field字段的指针,即pointer,该字段包含在container_type类型的结构中,然后返回包含该字段的结构指针。
疑惑:
使用class_create()的目的是什么?
创建一个类,在/dev/目录下建立设备节点,然后通过device_create()创建设备节点。