Linux驱动开发之主设备号找驱动,次设备号找设备原创作品,转载时请务必以超链接形式标明文章原始出处:http://blog.csdn.net/gqb666/article/details/8805179,作者:gqb666 一、引言 最近成都地震令大家心神不宁,可能过了今天就没明天了,早打算写的东西导致现在才发出来。不禁感叹:在自然灾害面前,人是那么渺小,人面对自然灾害就好像脚下的蚂蚁面对人,人不经意间就能踩死一片蚂蚁,自然灾害不经意间就能埋藏一批人。所以,我们活着的人要珍惜眼前人,善待眼前人。 言归正传,很久前接触linux驱动就知道主设备号找驱动,次设备号找设备。这句到底怎么理解呢,如何在驱动中实现呢,在介绍该实现之前先看下内核中主次设备号的管理: 二、Linux内核主次设备号的管理
Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。对于常用设备,Linux有约定俗成的编号,如终端类设备的主设备号是4。 设备号的内部表示 三、主设备号找驱动、次设备号找设备的内核实现 Linux内核允许多个驱动共享一个主设备号,但更多的设备都遵循一个驱动对一个主设备号的原则。
图1:应用程序调用open时通过主次设备号找到相应驱动 来看内核中一个简单的字符设备驱动的例子,其主设备号为1,根据LANANA标准,该设备有10个不同的次设备号。每个都提供了一个不同的功能,这些都与内存访问操作有关。下面列出一些从设备号,以及相关的文件名和含义。 表1 用于主设备号1的各个从设备号 一些设备是我们熟悉的,特别是/dev/null。根据设备描述我们可以很清楚,尽管这些从设备都涉及到内存访问,但所实现功能有很大差别。然后来看下图1中主设备号为1的memory_fops中定义了哪些函数指针。代码如下: driver/char/mem.c
static const struct file_operations memory_fops = { .open = memory_open, .llseek = noop_llseek, }; 其中函数memory_open最为关键 ,其作用是根据次设备号找到次设备的驱动程序。
static int memory_open(struct inode *inode, struct file *filp) { int minor; const struct memdev *dev; minor = iminor(inode); /* get the minor device number commented by guoqingbo */ if (minor >= ARRAY_SIZE(devlist)) return -ENXIO; dev = &devlist[minor];/* select the specific file_operations */ if (!dev->fops) return -ENXIO; filp->f_op = dev->fops; if (dev->dev_info) filp->f_mapping->backing_dev_info = dev->dev_info; /* Is /dev/mem or /dev/kmem ? */ if (dev->dev_info == &directly_mappable_cdev_bdi) filp->f_mode |= FMODE_UNSIGNED_OFFSET; if (dev->fops->open) //open the device return dev->fops->open(inode, filp); return 0; } 该函数用到的图1中的devlist数组定义如下:
static const struct memdev { const char *name; mode_t mode; const struct file_operations *fops; struct backing_dev_info *dev_info; } devlist[] = { [1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi }, #ifdef CONFIG_DEVKMEM [2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi }, #endif [3] = { "null", 0666, &null_fops, NULL }, #ifdef CONFIG_DEVPORT [4] = { "port", 0, &port_fops, NULL }, #endif [5] = { "zero", 0666, &zero_fops, &zero_bdi }, [7] = { "full", 0666, &full_fops, NULL }, [8] = { "random", 0666, &random_fops, NULL }, [9] = { "urandom", 0666, &urandom_fops, NULL }, [11] = { "kmsg", 0, &kmsg_fops, NULL }, #ifdef CONFIG_CRASH_DUMP [12] = { "oldmem", 0, &oldmem_fops, NULL }, #endif }; 通过上面代码及图1可看出,memory_open实际上实现了一个分配器(根据次设备号区分各个设备,并且选择适当的file_operations),图2说明了打开内存设备时,文件操作是如何改变的。所涉及的函数逐渐反映了设备的具体特性。最初只知道用于打开设备的一般函数,然后由打开与内存相关设备文件的具体函数所替代。接下来根据选择的次设备号,进一步细化函数指针 ,为不同的次设备号最终选定函数指针。 图2:设备驱动程序函数指针的选择过程 |
|
来自: 灰色浪漫939 > 《Linux驱动及节点》