前面举了一个HelloWorld的例子。在实际字符设备驱动编写中还要加入好多东西。
首先要确定的就是主设备号和次设备号
主设备号用于区分是否为同一类,次设备号用于区分是否为同一个。
在Documentation/devices.txt中有对预定义的设备号的说明。
内核用32bit来表示设备号
typedef unsigned long dev_t //32bit
#define MINORBITS 20 #define MINORMASK ((1U<<MINORBITS)-1) #define MAJOR(dev) ((unsigned int)((dev)>>MINORBITS)) #define MINOR(dev) ((unsigned int)((dev)&MINORMASK))
设备号分配有静态和动态两种,
int register_chrdev_region(dev_t first, unsigned int count, char *name); //静态
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name); //动态
获取主设备号部分代码
int scull_major = SCULL_MAJOR; //这个宏用于控制是否动态申请设备号用的 int scull_minor = 0; if(scull_major) { dev = MKDEV(scull_major,scull_minor); result = register_chrdev_region(dev, scull_nr_devs, "scull"); }else { result = alloc_chrdev_region(&dev, scull_minor, scull_nr_devs, "scull"); scull_major = MAJOR(dev); } if(result<0) { printk(KERN_WARNING"scull: can't get major %d\n", scull_major);
return result;
}
unregister_chrdev_region(dev,scull_nr_devs);//释放设备号
书上说早期使用下面两个API来分配、释放设备号的
int register_chrdev(unsigned int major, const char *name, struct file_operations fops); int unregister_chrdev(unsigned int major, const char *name);
然后就是创建设备文件,这里使用mknod命令,格式如下
mknod Name { b | c } Major Minor
这个bolg中有介绍如何自动创建设备节点
http://blog.csdn.net/fontlose/article/details/8225651
字符设备是如何在操作系统中被注册和注销的
就我个人理解,所谓的注册就是将cdev给初始化
struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; };
这个blog中介绍了cdev结构的相关的函数
http://blog.csdn.net/z08053520/article/details/5390604
其中cdev中file_operations的主要字段包括以下几个:
- struct module *owner:指向模块自身
- open:打开设备
- release:释放设备,即关闭设备,以我蹩脚的英语是很难知道这个意思的,一开始我很不解,直接定义成close不就得了,和open相呼应,多好
- read:从设备上读数据
- write:向设备上写数据
- ioctl:操作设备函数,这个很有用
- llseek:定位读写指针
- mmap:映射设备空间到进程的地址空间
用户程序调用open时,系统先根据设备文件名得到设备号,再根据设备号找到cdev,进而找到fops。所以这里涉及到3张表:文件描述表(file descriptor table)、文件表(file table)、i节点表(i-node table)
这个可以具体参见《Linux Device Driver》第三版的3.3节(我也要去好好看看)
未经整理,书讲到哪里就记哪里,记录的有点杂,等到以后熟悉了再来排版。