在内核源码include/linux/cdev.h里对cdev结构体的定义:
1 struct cdev { 2 struct kobject kobj; // 内嵌的kobject对象 3 struct module *owner; // 所属模块 4 const struct file_operations *ops; // 文件操作结构体 5 struct list_head list; //linux内核所维护的链表指针 6 dev_t dev; //设备号 7 unsigned int count; //设备数目 8 };
1. 重要成员:
1.1 dev_t dev;设备号,31位,高12位是主设备号,低20位是次设备号。以下函数可以操作设备号:
1 #define MINORBITS 20 2 #define MINORMASK ((1U << MINORBITS) - 1) 3 4 #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS)) //获得主设备号 5 #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK)) //获得此设备号 6 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) //由主次设备号得到设备号
1.2 struct file_operations *ops;
2. 初始化cdev
2.1 静态初始化
1 /** 2 * cdev_init() - initialize a cdev structure 3 * @cdev: the structure to initialize 4 * @fops: the file_operations for this device 5 * 6 * Initializes @cdev, remembering @fops, making it ready to add to the 7 * system with cdev_add(). 8 */ 9 void cdev_init(struct cdev *cdev, const struct file_operations *fops) 10 { 11 memset(cdev, 0, sizeof *cdev); 12 INIT_LIST_HEAD(&cdev->list); 13 kobject_init(&cdev->kobj, &ktype_cdev_default); 14 cdev->ops = fops; 15 }
2.2 动态初始化
1 /** 2 * cdev_alloc() - allocate a cdev structure 3 * 4 * Allocates and returns a cdev structure, or NULL on failure. 5 */ 6 struct cdev *cdev_alloc(void) 7 { 8 struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL); 9 if (p) { 10 INIT_LIST_HEAD(&p->list); 11 kobject_init(&p->kobj, &ktype_cdev_dynamic); 12 } 13 return p; 14 }
3. 分配设备号
3.1 指定主设备号
1 /** 2 * register_chrdev_region() - register a range of device numbers 3 * @from: the first in the desired range of device numbers; must include 4 * the major number. 5 * @count: the number of consecutive device numbers required 6 * @name: the name of the device or driver. 7 * 8 * Return value is zero on success, a negative error code on failure. 9 */ 10 int register_chrdev_region(dev_t from, unsigned count, const char *name) 11 { 12 struct char_device_struct *cd; 13 dev_t to = from + count; 14 dev_t n, next; 15 16 for (n = from; n < to; n = next) { 17 next = MKDEV(MAJOR(n)+1, 0); 18 if (next > to) 19 next = to; 20 cd = __register_chrdev_region(MAJOR(n), MINOR(n), 21 next - n, name); 22 if (IS_ERR(cd)) 23 goto fail; 24 } 25 return 0; 26 fail: 27 to = n; 28 for (n = from; n < to; n = next) { 29 next = MKDEV(MAJOR(n)+1, 0); 30 kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); 31 } 32 return PTR_ERR(cd); 33 }
3.2 系统自动分配主设备号
1 /** 2 * alloc_chrdev_region() - register a range of char device numbers 3 * @dev: output parameter for first assigned number 4 * @baseminor: first of the requested range of minor numbers 5 * @count: the number of minor numbers required 6 * @name: the name of the associated device or driver 7 * 8 * Allocates a range of char device numbers. The major number will be 9 * chosen dynamically, and returned (along with the first minor number) 10 * in @dev. Returns zero or a negative error code. 11 */ 12 int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, 13 const char *name) 14 { 15 struct char_device_struct *cd; 16 cd = __register_chrdev_region(0, baseminor, count, name); 17 if (IS_ERR(cd)) 18 return PTR_ERR(cd); 19 *dev = MKDEV(cd->major, cd->baseminor); 20 return 0; 21 }
4 注销设备号
与上述两个分配设备号对应的注销设备号的函数如下:
1 /** 2 * unregister_chrdev_region() - return a range of device numbers 3 * @from: the first in the range of numbers to unregister 4 * @count: the number of device numbers to unregister 5 * 6 * This function will unregister a range of @count device numbers, 7 * starting with @from. The caller should normally be the one who 8 * allocated those numbers in the first place... 9 */ 10 void unregister_chrdev_region(dev_t from, unsigned count) 11 { 12 dev_t to = from + count; 13 dev_t n, next; 14 15 for (n = from; n < to; n = next) { 16 next = MKDEV(MAJOR(n)+1, 0); 17 if (next > to) 18 next = to; 19 kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n)); 20 } 21 }