在Linux2.6内核中使用cdev结构来描述字符设备,cdev结构体的一个定义如下:
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
这其中dev_t成员定义了32位的设备号,其中高12位为主设备号,低20位为次设备号。 而cdev结构体的另一个成员file_operations则定义了字符设备驱动程序提供给虚拟文件系统的接口函数。下面的函数可以用来操作cdev结构体。
void cdev_init(struct cdev* dev, struct file_operations * fops);
struct cdev* cdev_alloc(void);
void cdev_put(struct cdev* p);
int cdev_add(struct cdev*, dev_t, unsigned);
void cdev_del(struct cdev*);
函数cdev_alloc()函数用于动态申请一个cdev内存。
函数cdev_init用于初始化cdev的成员,并建立cdev和 file_operations之间的连接。而函数cdev_add和cdev_del分别向系统添加和删除一个cdev结构,完成字符设备的注册和注 销。
当我们需要向系统添加字符设备的时候,我们需要调用cdev_add函数来注册字符设备。而在此之前,我们需要先调用register_chrdev_region()或 alloc_chrdev_region()来向系统申请设备号。下面是这两个函数的定义:
int register_chrdev_region(dev_t from, unsigned count, const char* name);
int alloc_chrdev_region(dev_t *dev, unsigned baseminor,
unsigned count, const char* name);
void unregister_chrdev_region(dev_t from, unsigned count);
函数register_chrdev_region用于已知起始设备的设备号的情况,而函数alloc_chrdev_region则用于设备号未知,向系统动态申请未被占用的设备号的情况。函数调用成功后,会自动在第一个参数中返回得到的设备号。同样,在调用cdev_del函数从系统注销字符设备之后,应该使用unregister_chrdev_region来释放原来申请的设备号。
下面是字符设备驱动程序的加载和卸载的模板
static struct cdev cdev;
// module loading routine.
static int __init xxx_init(void) {
……
cdev_init(&cdev, &xxx_fops);
// 初始化cdev 结构
cdev.owner = THIS_MODULE;
// 获取并登记主代码号。
if (xxx_major) {
// 如果已经有主代码号,可以使用这个函数。
register_chrdev_region(xxx_dev_no, 1, DEV_NAME);
}
else {
// 如果没有主代码号,则使用这个函数。
alloc_chrdev_region(&xxx_dev_no, 0, 1, DEV_NAME);
}
// 将这个设备登记到内核中去.
ret = cdev_add(&cdev, xxx_dev_no, 1);
…..
}
// module unloading routine.
static void __exit xxx_exit(void) {
// 释放已经申请的主ID号。
unregister_chrdev_region(xxx_dev_no, 1);
// 释放这个设备。
cdev_del(&cdev); ……
}