设备号由主设备号和次设备号组成。linux下,一切设备皆文件,所有的设备都能在/dev目录下找到相应的文件。这些文件除了名字不一样以外,还每个设备文件都有不一样的设备号;
一般地,主设备号对应一个类型的驱动设备,之所以有次设备号,它是用来驱动同类型的设备。如串口,所有的串口共用一个主设备号,每个串口有不同的次设备号。
dev_t类型用来保存设备编号(包含主设备号和次设备号),实际上是一个32位整数,12位用来表示主设备号,后20位表示次设备号。
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
//提取主设备号
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
//提取次设备号
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
//生成设备号
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
/**
* register_chrdev_region() - register a range of device numbers
* @from: the first in the desired range of device numbers; must include
* the major number.
* @count: the number of consecutive device numbers required
* @name: the name of the device or driver.
*
* Return value is zero on success, a negative error code on failure.
*/
int register_chrdev_region(dev_t from, unsigned count, const char *name)
指定从设备号from开始,申请count个设备号,在/proc/devices中的名字为name
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
动态申请从次设备号baseminor开始的count个设备号,在/proc/devices中的名字为name,并通过dev指针把分配到的设备号返回给调用函数者。
void unregister_chrdev_region(dev_t from, unsigned count)
字符设备struct cdev
struct cdev {
struct kobject kobj;
struct module *owner;//一般初始化为THIS_MODULE
const struct file_operations *ops;//文件操作结构体
struct list_head list;
dev_t dev;//设备号
unsigned int count;//添加的设备个数
};
注册的三个步骤:
1)分配cdev;
2)初始化cdev;
3)添加cdev;
直接定义struct cdev test_cdev;
或者动态分配
truct cdev* test_cdev;
test_cdev = cdev_alloc();
/**
* cdev_init() - initialize a cdev structure
* @cdev: the structure to initialize
* @fops: the file_operations for this device
*
* Initializes @cdev, remembering @fops, making it ready to add to the
* system with cdev_add().
*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops)
这个函数干了两件事情:
1)内核自己填充了结构体中list和kobj的内容
2)把传入的文件操作结构体也填充进去
一般的,还要手工定义结构体成员owner。
将cdev结构体与设备号关联起来:
int cdev_add(struct cdev *cdev, dev_t dev, unsigned count)
参数:
cdev:指定要被添加的cdev结构体;
dev:对应的设备号
count:从设备号dev开始添加count个设备.
函数干了也两件事:
1)把cdev结构体中还没填充的两个成员dev和count按照传入参数赋值。
2)把cdev结构体中传入内核,这样内核就知道对应设备号和具体的文件操作结构体了。
void cdev_del(struct cdev *p)