在内核中,dev_t类型用来保存设备编号——包括主设备号和次设备号。
内核内部使用struct cdev结构来表示字符设备。
根据设备编号获取主设备号和次设备号:
MAJOR(dev_t dev); //获取主设备号 MINOR(dev_t dev); //获取次设备号
根据主设备号和次设备号得到设备编号:
MKDEV(int major, int minor);
分配和释放设备编号:
int register_chrdev_region(dev_t from, unsigned count, const char *name); /* * 在主设备号确定的情况下使用 *from: 要分配的设备编号范围的起始值,主要是有一个确定的主设备号,次设备号一般为0。 *count: 所请求的连续设备编号的个数(次设备号的个数) *name: 将出现在/proc/devices和sysfs中 */ int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name); /* * 动态分配主设备号 *dev: 输出参数,将动态分配好的首个设备号返回。 *baseminor: 第一个次设备号的编号,一般为0。 */ void unregister_chrdev_region(dev_t from, unsigned count);
inode数据结构中有类型为dev_t的i_rdev字段,当inode指向的是设备文件时,它描述的是设备编号。
从inode数据结构中获得主设备号和次设备号:
unsigned int iminor(struct inode* inode); unsigned int imajor(struct inode* inode);
字符设备的注册
struct cdev *cdev_alloc(void); //函数原型 struct cdev *my_cdev = cdev_alloc(); my_cdev->owner = THIS_MODULE; void cdev_init(struct cdev *cdev, struct file_operation * fops); //函数原型 my_cdev->ops = &my_fops; 或者: cdev_init(my_cdev, &my_fops); int cdev_add(struct cdev* cdev, dev_t num, unsigned int count); //函数原型 /* *count: 是应该和该设备关联的设备编号的数量,常取1. */ cdev_add(my_cdev, devno, 1); void cdev_del(struct cdev *dev); //函数原型 cdev_del(my_cdev);
早期的接口
注册字符设备驱动程序的接口:
int register_chardev(unsigned int major, const char *name, struct file_operation *fops); //函数原型 /* *major: 设备的主设备号。 *name: 驱动程序的名称,出现在/proc/devices中 */ 实现方法: { __register_chrdev(major, 0, 256, name, fops);{ //major, baseminor, count, name, fops __register_chrdev_region(major, baseminor, count, name);{ //注册设备编号,若传入的major为0,则自动分配 } cdev = cdev_alloc(); cdev->owner = fops->owner; cdev->ops = fops; cdev_add(cdev, MKDEV(cd->major, baseminor), count); } }
移除设备驱动程序的接口:
int unregister_chrdev(unsigned int major, const char *name); //函数原型 实现方法: { __unregister_chrdev(major, 0, 256, name);{ __unregister_chrdev_region(major, baseminor, count);{ //释放前面分配设备编号时申请的空间。 } cdev_del(cd->cdev); } }