原创作品,转载时请务必以超链接形式标明文章原始出处:http://blog.csdn.net/gqb666/article/details/8805179,作者:gqb666

一、引言  

最近成都地震令大家心神不宁,可能过了今天就没明天了,导致早打算写的东西现在才发出来。不禁感叹:在自然灾害面前,人是那么渺小,人面对自然灾害就好像脚下的蚂蚁面对人,人不经意间就能踩死一片蚂蚁,自然灾害不经意间就能埋藏一批人。所以,我们活着的人要珍惜眼前人,善待眼前人。

言归正传,很久前接触linux驱动就知道主设备号找驱动,次设备号找设备。这句到底怎么理解呢,如何在驱动中实现呢,在介绍该实现之前先看下内核中主次设备号的管理:

二、Linux内核主次设备号的管理

Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样。为了管理这些设备,系统为设备编了号,每个设备号又分为主设备号和次设备号。主设备号用来区分不同种类的设备,而次设备号用来区分同一类型的多个设备。对于常用设备,Linux有约定俗成的编号,如终端类设备的主设备号是4。

设备号的内部表示
在内核中,dev_t  类型( 在 <linux/types.h>头文件有定义 ) 用来表示设备号,包括主设备号和次设备号两部分。对于 2.6.x内核,dev_t是个32位量,其中高12位用来表示主设备号,低20位用来表示次设备号。
在 linux/types.h 头文件里定义有
typedef __kernel_dev_t          dev_t;
typedef __u32 __kernel_dev_t;
主设备号和次设备号的获取
为了写出可移植的驱动程序,不能假定主设备号和次设备号的位数。不同的机型中,主设备号和次设备号的位数可能是不同的。应该使用MAJOR宏得到主设备号,使用MINOR宏来得到次设备号。下面是两个宏的定义:(linux/kdev_t.h)
#define MINORBITS   20                                 /*次设备号*/  
#define MINORMASK   ((1U << MINORBITS) - 1)            /*次设备号掩码*/  
#define MAJOR(dev)  ((unsigned int) ((dev) >> MINORBITS))  /*dev右移20位得到主设备号*/  
#define MINOR(dev)  ((unsigned int) ((dev) & MINORMASK))   /*与次设备掩码与,得到次设备号*/ 
MAJOR宏将dev_t向右移动20位,得到主设备号;MINOR宏将dev_t的高12位清零,得到次设备号。相反,可以将主设备号和次设备号转换为设备号类型(dev_t),使用宏MKDEV可以完成这个功能。
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
MKDEV宏将主设备号(ma)左移20位,然后与次设备号(mi)相或,得到设备号

三、主设备号找驱动、次设备号找设备的内核实现

Linux内核允许多个驱动共享一个主设备号,但更多的设备都遵循一个驱动对一个主设备号的原则。
内核维护着一个以主设备号为key的全局哈希表,而哈希表中数据部分则为与该主设备号设备对应的驱动程序(只有一个次设备)的指针或者多个同类设备驱动程序组成的数组的指针(设备共享主设备号)。根据所编写的驱动程序,可以从内核那里得到一个直接指向设备驱动的指针,或者使用次设备号作为索引的数组来找到设备驱动程序。但无论哪种方式,内核自身几乎不知道次设备号的什么事情。如下图所示:

图1:应用程序调用open时通过主次设备号找到相应驱动

来看内核中一个简单的字符设备驱动的例子,其主设备号为1,根据LANANA标准,该设备有10个不同的次设备号。每个都提供了一个不同的功能,这些都与内存访问操作有关。下面列出一些次设备号,以及相关的文件名和含义。

表1 用于主设备号1的各个从设备号
     从设备号                                                文件                                                           含义
         1                                                    /dev/mem                                                    物理内存
         2                                                    /dev/kmem                                                内核虚拟地址空间
         3                                                    /dev/null                                                       比特位桶
         4                                                    /dev/port                                                     访问I/O端口
         5                                                    /dev/zero                                                 WULL字符源
         8                                                   /dev/random                                          非确定性随机数发生器

一些设备是我们熟悉的,特别是/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 devicereturn 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:设备驱动程序函数指针的选择过程

Linux驱动开发之主设备号找驱动,次设备号找设备相关推荐

  1. 32驱动_轻松掌握pinctrl子系统驱动开发——一个虚拟pinctrl dev驱动开发

    这周主要对pinctrl子系统进行分析,该分析的基本上已经分析完成,唯一没有细说的估计就是gpio与pinctrl之间的关联了.本章即是pinctrl子系统分析的最后一章,本章我们主要实现一个虚拟的p ...

  2. 驱动开发:配置Visual Studio驱动开发环境

    在正式开始驱动开发之前,需要自行搭建驱动开发的必要环境,首先我们需要安装Visual Studio 2013这款功能强大的程序开发工具,在课件内请双击ISO文件并运行内部的vs_ultimate.ex ...

  3. linux主设备编号从0到多少,Linux驱动开发之主设备号找驱动,次设备号找设备

    一.引言 很久前接触linux驱动就知道主设备号找驱动,次设备号找设备.这句到底怎么理解呢,如何在驱动中实现呢,在介绍该实现之前先看下内核中主次设备号的管理: 二.Linux内核主次设备号的管理 Li ...

  4. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之LED驱动框架--面向对象、分层设计思想

    文章目录 前言 1.LED驱动程序框架 1.1.对于LED驱动,我们想要什么样的接口? 1.2.LED驱动要怎么写,才能支持多个板子?分层写 1.3.程序分析 驱动程序 应用程序 Makefile 1 ...

  5. 基于ARM-contexA9-Linux驱动开发:如何获取板子上独有的ID号

    每个CPU,都有它固定的ID号,ID号就是这个CPU唯一的标识,它可能隐含着CPU的生产日期,版本号,型号等等,那么,在我们的这款友善之臂Tiny4412的板子上,我的这个CPU的ID又是多少呢?从我 ...

  6. Linux嵌入式开发_主设备号与次设备号详解

    前言 在Linux内核中设备号的作用是用来区分不同的设备类型. 比如: 设备号23,对应的是LED 设备号17,对应的是某个存储设备 等等... 主次设备号 主设备号:对应设备的主号码 次设备号:对应 ...

  7. linux驱动开发(一)—GPIO驱动框架

    前言 GPIO驱动是Linux驱动开发中最基础.但却是很常用.很重要的驱动.比如你要点亮一个LED灯.键盘扫描.输出高低电平等等.而Linux内核的强大之处在于对最底层的GPIO硬件操作层的基础上封装 ...

  8. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之按键驱动框架

    文章目录 前言 1.APP怎么读取按键值 1.1.查询方式 1.2.休眠-唤醒方式 1.3.poll方式 1.3.异步通知方式 1.5. 驱动程序提供能力,不提供策略 2.按键驱动程序框架--查询方式 ...

  9. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之LED模板驱动程序的改造:设备树

    文章目录 前言 1.驱动的三种编写方法 2.怎么使用设备树写驱动程序 2.1.设备树节点要与platform_driver能匹配 2.2.修改platform_driver的源码 3.实验和调试技巧 ...

最新文章

  1. vue element-ui Notification 挤在一起,重叠问题 解决办法
  2. RedHat的yum源修改为CentOS的yum源
  3. 题目1105:字符串的反码
  4. numpy条件筛选的坑
  5. ashx PHP文件 优劣,.NET_后缀为 ashx 与 axd 的文件区别浅析,唯一不同的地方是:axd扩展名 - phpStudy...
  6. android 流量统计不准确_汽车里程表上显示的百公里油耗准确吗?是不是真的是欢乐表...
  7. baidumap vue 判断范围_百度地图 vue-baidu-map
  8. nginx 上传 文件超时设置_Nginx在高并发下的性能优化点!有这篇就够了!
  9. 智能城市技术能够更好地改善日常生活
  10. CentOS Linux 7编译安装Redis
  11. 我和一位快递小哥的故事
  12. Android之Activity界面劫持反劫持
  13. JavaScript prototype原型实现继承
  14. 分享一个互联网职业教育免费视频、资料、机构综合类资源平台!
  15. 微信公众账号分为哪几类?区别是什么?
  16. 输入中文转换成拼音首字母
  17. CSR867x — 实现SPP数据收发
  18. 计算机视觉结课论文,计算机视觉与图像识别结课论文教案.doc
  19. 前端HTML上传图片传BASE64数据,图片太大进行压缩
  20. 以IM为例看58同城典型技术架构演变

热门文章

  1. 状态栏显示Volte图标
  2. 一站式报修微信小程序,让报修系统化,便民化。JavaScript this 关键词
  3. 美术宝软件测试面试流程怎么样,“倒数第一”试卷走红,老师气得找家长,美术老师却表示有前途...
  4. 计算机经常死机故障排除,轻松解决电脑频繁死机问题?电脑频繁死机的解决方法...
  5. python中socket与UDP使用与通信详解
  6. ajax定时取数据库,ajax定时访问数据库数据
  7. 微信智慧经营在实践中的应用
  8. 【Word格式修改系列】在两个不同章节间插入空白页
  9. 苹果WWDC 2016总结:四大OS更新
  10. centos7中通过LVM挂载新硬盘