6. 字符设备驱动:
cdev结构体——P119
MKDEV(int major, int minor) 通过这个宏可以将主设备号和次设备号生成dev_t
一些用来操作cdev结构体的函数:
cdev_init(),它用来初始化cdev的成员,并且建立cdev和file_operations之间的连接, void cdev_init(struct cdev *cdev, struct file_operations *fops)
cdev_alloc()函数用于动态申请一个cdev内存,TA的实现中用到了kzalloc函数
cdev_add()和cdev_del()函数分别向系统添加和删除一个cdev,完成字符设备的注册和注销,他们通常在模块的加载和卸载中
分配和释放设备号
register_chrdev_region(dev_t from, unsigned count, const char * name)
alloc_chrdev_region(dev_t *dev,unsigned baseminor, unsigned count, const char * name)
unregister_chrdev_region(dev_t from, unsigned count)
由于内核空间和用户空间的内存不能直接互访,要借助于copy_from_user()完成用户空间到内核空间的拷贝,copy_from_user完成内核空间到用户空间的拷贝,如果要复制的仅仅是简单的类型的话,比如char,int,可以简单的put_user和get_user,如get_user(val,(int *)arg) arg为用户空间的地址,put_user(val,(int *) arg)内核到用户,arg是用户空间的地址。具体copy的用法在P127有,实例化了内核空间指针,用户空间什么的。
在驱动中用到比如globalmem_setup_cdev(),它用于完成cdev的初始化和添加,具体函数在——P126
file_operations中的seek()函数,用于文件的定位的,返回定位后的地址。
ioctl()函数是一个很好的操作方法,它可以做出了读写之外其他的事情,通常ioctl命令有着建议的组成格式——P129,像_IO(),_IOC()等也就是一种命名方式,它们仅仅是对ioctl控制命令中的设备类型,序列号,方向,数据尺寸进行给值移位。
在文件操作中,一个潜规则是:将文件的私有数据private_data指向自建的设备结构体,然后file_operations通过这个private_data来访问设备结构体。不过若仅仅是一个设备在用的话,还不如直接用该设备结构体的指针,没必要引出private_data的概念,但是如果如果涉及到两个以上的设备的时候,private_data的优势就会显示出来了。
其中和container_of()配合使用。如dev=container_of(inode->i_cdev, struct globalmem_dev, cdev); flip->private_data=dev;
container_of - cast a member of a structure out to the containing structure
@ptr: the pointer to the member
@type: the type of the container struct this is embedded in
@member: the name of the member within the struct
#define container_of(ptr, type, member) ({ const typeof( ((type *)0)->member ) *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );}) //据我对这句话的理解,它是通过计算type和member之间的地址差值,来算出所要求的结构体的地址位置。
如果启用了sysfs文件系统,将会防线多了一个/sys/module/globalmem目录,其中refcnt记录引用次数,sections下记录BSS,数据段和代码段等地址。
总结:
字符设备的驱动程序中,完成的主要工作就是初始化,添加,删除cdev结构体,申请和释放设备号,以及填充file_operations结构体的操作函数,实现file_operations结构体的read(),write()。ioctl()等函数是驱动设计的主要工作。