内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev()。
一、register_chrdev和unregister_chrdev
在Linux2.6内核以前注册字符设备的函数接口是register_chrdev,注销字符设备接口函数是unregister_chrdev.
register_chrdev()---老版本字符设备注册函数
static inline int register_chrdev(unsigned int major, const char *name, const struct file_opertations * fops)
参数 |
说明 |
Major |
表示设备编号。major=0,则表示采用系统动态分配的主设备号;不为0,则表示静态注册 |
Name |
表示设备名字 |
Fops |
在内核内部与驱动设备挂钩 |
返回值:
major值为0,正常注册后,返回分配的主设备号。如果分配失败,返回 EBUSY 的负值(-EBUSY)。major值若大于linux/major.h(2.4内核)中声明的最大值(#define MAX_CHRDEV255),则返回EINVAL 的负值(-EINVAL)。
指定major值后,若该设备号已被占用,返回EBUSY的负值(-EBUSY)。若正常注册,则返回0值。
unregister_chrdev() --- 老版本字符设备注销函数
功能:注销设备
原型:
1. #include <linux.fs.h> 2. 3. int unregister_chrdev (unsigned int major, const char *name)
说明:
注销设备驱动程序的内核函数
major主设备号
name设备文件
返回值:
major值若大于linux/major.h(2.4内核)中声明的最大值 (#define MAX_CHRDEV255),返回 EINVAL的负值(-EINVAL)。指定了major的值后,若将要注销的major值并不是注册的设备驱动程序,返回EINVAL的负值(-EINVAL)。正常注销则返回0值。
二、register_chrdev_region(静态注册)
新接口注册字符设备驱动需要两步:
step1:注册/分配主次设备号
step2:注册字符驱动设备
1. <linux/fs.h> 2. 3. int register_chrdev_region(dev_t first, unsigned int count, char *name);
参数说明:
参数 |
说明 |
first |
要分配的设备编号范围的初始值,这组连续设备号的起始设备号,相当于register_chrdefv()中主设备号 |
count |
连续编号范围,是这组设备号的大小(也就是次设备号个数) |
name |
编号相关的设备名称,(proc/devices);本组设备的驱动名称 |
三、alloc_chrdev_region(动态注册)
1. <linux/fs.h> 2. 3. int alloc_chrdev_region(dev_t *dev,unisgned baseminor,unsigned count,const char*name)
参数说明:
参数 |
说明 |
dev |
系统分配所得的设备号。可以用MAJOR和MINOR将主次设备号打印查看 |
baseminor |
次设备号的基准,从第几个设备号开始分配 |
count |
次设备号的个数 |
name |
驱动的名字 |
返回值:分配成功,返回设备号的第一个参数;分配错误,返回小于零的参数
cdev结构体
cdev是一个结构体,里面的成员来共同帮助我们注册驱动到内核中,表达字符设备的,将这个struct cdev结构体进行填充,主要填充的内容就是
1. struct cdev { 2. 3. struct kobject kobj; 4. 5. struct module *owner;//填充时,值要为 THIS_MODULE,表示模块 6. 7. const struct file_operations *ops;//这个file_operations结构体,注册驱动的关键,要填充成这个结构体变量 8. 9. struct list_head list; 10. 11. dev_t dev;//设备号,主设备号+次设备号 12. 13. unsigned int count;//次设备号个数 14. 15. };
cdev结构体中ops变量让file_operations结构体变量成为cdev的成员,这个结构体会被cdev_add函数向内核注册cdev结构体。
cdev结构体,可以用很多函数来操作他。
如:
1. cdev_alloc:让内核为这个结构体分配内存的 2. 3. cdev_init:将struct cdev类型的结构体变量和file_operations结构体进行绑定的 4. 5. cdev_add:向内核里面添加一个驱动,注册驱动 6. 7. cdev_del:从内核中注销掉一个驱动。注销驱动
设备号
(1)dev_t类型(包括了主设备号和次设备号 不同的内核中定义不一样有的是16位次设备号和16位主设备号构成 有的是20为次设备号12位主设备号 )
(2)MKDEV、MAJOR、MINOR三个宏
MKDEV: 是用来将主设备号和次设备号,转换成一个主次设备号的。(设备号)
MAJOR: 从设备号里面提取出来主设备号的。
MINOR宏:从设备号中提取出来次设备号的。
关于设备文件的创建
刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。
内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类, 内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用 device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应 device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
注意,在2.6较早的内核版本中,device_create(…)函数名称不同,是class_device_create(…),所以在新的内核中编译以前的模块程序有时会报错,就是因为函数名称不同,而且里面的参数设置也有一些变化。
struct class和device_create(…) 以及device_create(…)都定义在/include/linux/device.h中,使用的时候一定要包含这个头文件,否则编译器会报错。
class_create()
struct class *class_create(struct module *owner, const char *name);
owner 指向拥有这个struct类的模块的指针
name 指向该类名称的字符串指针
device_create()
struct device *device_create(struct class *class, struct device *parent, dev_t devt, const char *fmt, ...)
第一个参数指定所要创建的设备所从属的类
第二个参数是这个设备的父设备,如果没有就指定为NULL
第三个参数是设备号
第四个参数是设备名称
第五个参数是从设备号