Linux设备驱动程序学习笔记03:字符设备驱动程序I

时间:2022-12-21 11:20:52

在Linux内核中用cdev结构体来表示一个字符设备,cdev的定义在linux/Cdev.h中:

struct cdev {
struct kobject kobj; //内嵌的kobject
struct module *owner; //指向实现驱动程序的模块(如果有的话)
const struct file_operations *ops; //指向驱动程序的文件操作表
struct list_head list; //与字符设备对应的设备文件的链表头
dev_t dev; //驱动的设备号
unsigned int count; //设备号的范围
};

该结构体的dev成员定义了设备号。Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。使用ll命令查看/dev目录下的内容,可看到同下面类型的输出:

Linux设备驱动程序学习笔记03:字符设备驱动程序I

其中tty是设备名称。5, 0 分别是主设备号和次设备号。主设备号标识设备对应的驱动。次设备号由内核使用,用于确定设备文件所指的设备。

在内核中用dev_t类型来保存设备号。在Linux内核的2.6版本中,dev_t是一个32位的数,其中12为用来表示主设备号,而其余的20位用来表示次设备号。

Dev_t 类型:

//<linux/types.h>
typedef __u32 __kernel_dev_t;
typedef __kernel_dev_tdev_t;

我们的代码不应该对设备编号的组织做任何假定,而应该始终使用<linux/kdev_t.h>中定义的宏来操作设备号。

MAJOR(dev_t dev); //由dev_t类型变量中解析出主设备号
MINOR(dev_t dev); //由dev_t类型变量中解析出次设备号
MKDEV(int major, int minor); //由主设备号和次设备号生成dev_t变量

在创建一个字符设备之前,驱动程序首先要做的就是申请设备号。有两个函数可以为字符设备分配设备号:

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);

register_chrdev_region函数的三个参数:from---请求分配的设备号的起始值。count---请求的设备号的个数。name---所申请的设备号对应的驱动程序名称。同大部分的内核函数一样,该函数在分配成功时返回0,在错误的情况下将返回一个负的错误码。

alloc_chrdev_region函数的四个参数:dev---指向输出的参数,在成功完成后将保存已分配范围的第一个编号。Baseminor---指定被请求的第一个次设备号。Count和name同register_chrdev_region函数是一样的。该函数的主设备号是内核动态分配的。

由于内核已经将部分设备号分配给了一些常见设备(可在源码中Documentation/devices.txt文件中查。并且也不知道将要运行驱动程序的主机上那些设备号已经被分配了。为了避免可能造成的冲突,我们应该尽量采用alloc_chrdev_region来申请设备号。

设备号也是属于一种系统资源,当不在需要设备号时需要进行释放。可以通过下面的函数释放设备号:

void unregister_chrdev_region(dev_t from, unsigned count);

通常在模块的清除函数中调用unregister_chrdev_region。

下面列出了通常的设备号申请代码:

#define MEMDEV_MAJOR 0
static int memdev_major = MEMDEV_MAJOR;
dev_t devno;

if (memdev_major) {
devno = MKDEV(memdev_major, 0);
ret = register_chrdev_region(devno, 1 , "memdev");
} else {
ret = alloc_chrdev_region(&devno, 0, 1, "memdev");
memdev_major = MAJOR(devno);
}

[1].《Linux设备驱动程序》第三版 Jonathan Corbet etc. 魏永明等译  中国电力出版社

[2].《深入理解Linux内核》第三版 Daniel P.Bovet etc. 陈莉君等译  中国电力出版社

[3].《Linux设备驱动开发详解》第二版  宋宝华  人民邮电出版社