上层应用程序通过对节点的操作,对设备进行控制。这个节点的接口就是由字符设备驱动提供,向前面的HelloWorld驱动是没有上层操作的接口的,它仅仅只有模块加载接口和退出接口。所以也不需要对应的节点。
struct file_operations这个结构体它在linux/fs.h中定义,用来存储驱动内核模块提供的对 设备进行各种操作的函数的指针。该结构体的每个域都对应着驱动内核模块用来处理某个被请求的 事务的函数的地址。
驱动内核模块是不需要实现每个函数的。
在内核中没一个设备都有一个cdev结构变量,具体我参考的是这个:
cdev有两种初始化方式:静态和动态
静态内存定义初始化:struct cdev my_cdev;cdev_init(&my_cdev, &fops);my_cdev.owner = THIS_MODULE;动态内存定义初始化:struct cdev *my_cdev = cdev_alloc();my_cdev->ops = &fops;my_cdev->owner = THIS_MODULE;两种使用方式的功能是一样的,只是使用的内存区不一样,一般视实际的数据结构需求而定。下面贴出了两个函数的代码,以具体看一下它们之间的差异。struct cdev *cdev_alloc(void){ struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL); if (p) { INIT_LIST_HEAD(&p->list); kobject_init(&p->kobj, &ktype_cdev_dynamic); } return p;}void cdev_init(struct cdev *cdev, const struct file_operations *fops){ memset(cdev, 0, sizeof *cdev); INIT_LIST_HEAD(&cdev->list); kobject_init(&cdev->kobj, &ktype_cdev_default); cdev->ops = fops;}由此可见,两个函数完成都功能基本一致,只是 cdev_init() 还多赋了一个 cdev->ops 的值。初始化 cdev 后,需要把它添加到系统中去。为此可以调用 cdev_add() 函数。传入 cdev 结构的指针,起始设备编号,以及设备编号范围。int cdev_add(struct cdev *p, dev_t dev, unsigned count){ p->dev = dev; p->count = count; return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p);}关于 kobj_map() 函数就不展开了,我只是大致讲一下它的原理。内核中所有都字符设备都会记录在一个 kobj_map 结构的 cdev_map 变量中。这个结构的变量中包含一个散列表用来快速存取所有的对象。kobj_map() 函数就是用来把字符设备编号和 cdev 结构变量一起保存到 cdev_map 这个散列表里。当后续要打开一个字符设备文件时,通过调用 kobj_lookup() 函数,根据设备编号就可以找到 cdev 结构变量,从而取出其中的 ops 字段。当一个字符设备驱动不再需要的时候(比如模块卸载),就可以用 cdev_del() 函数来释放 cdev 占用的内存。void cdev_del(struct cdev *p){ cdev_unmap(p->dev, p->count); kobject_put(&p->kobj);}其中 cdev_unmap() 调用 kobj_unmap() 来释放 cdev_map 散列表中的对象。kobject_put() 释放 cdev 结构本身。
设备号分配
#define NAME "name"int major = xxx;int result;dev_t devno = MKDEV(MAJOR,0);if(MAJOR) result = register_chrdev_region(devno,1,NAME);//静态else{ result = alloc_chrdev_region(&devno,0,10,NAME);//动态 major = MAJOR(devno); }
记录以备忘
附加:
还可以用misc设备。在书中它是把led字符驱动程序,注册成misc设备,这个步骤也很简单
首先要定义好struct miscdevice misc这个结构体
这个结果体主要初始化minor,name,fops这三个值
.minor = MISC_DYNAMIC_MINOR 这个宏是内核定义好了的
然后在注册时调用misc_register(&misc)注册
misc_deregister(&misc)释放
感觉misc比上面的要方便很多。
这是网上关于misc设备的说明