本文目标

本文在上一节的基础上,采用可以指定主设备号,次设备号,手动申请dev_t和自动申请dev_t的API,封装一个全局的对象结构体并用private_data在方法间传递,减少全局变量使用,寄存器操作改用readl和writel,引入cdev、class、device对象。

驱动里面的全局结构体是对象私有数据,fops是对象内部的操作函数,可以通过系统调用对外public。
cdev、class、device应该是驱动对象的各种父类,用来抽象各种子系统的数据和行为,以便实现统一框架。

以上是我个人理解,没有严格意义上的验证。

API介绍

I/O内存访问函数:
采用ioremap将物理地址映射到虚拟地址之后,我们可以直接通过指针访问这些地址,但是Linux内核不建议这么做,而是提供了一系列函数。

1、读操作函数

u8 readb(const volatile void __iomem *addr);
u16 readw(const volatile void __iomem *addr);
u32 readl(const volatile void __iomem *addr);

从返回值类型可以看出其功能。

2、写操作函数

void writeb(u8 value,volatile void __iomem *addr);
void writew(u16 value,volatile void __iomem *addr);
void writel(u32 value,volatile void __iomem *addr);

3、register_chrdev的不足:
ret = register_chrdev(major,module_name,&ldd2_fops);
需要提前知道系统里面不冲突的major,使用过程中发现代码里写的100这个号不能用,难道系统集成商需要把程序员叫过来重新改?

另外,这个API会把一个主设备号下面的2^20-1个次设备号全部用掉。
所以,自然而然有人提出了以下api:

a、指定主设备号,和次设备号个数,一次n个,不浪费。

int register_chrdev_region(dev_t from,unsigned count,const char *name);

b、不指定设备号,只需要指定次设备号的基号,自动分配n个,完美。

int alloc_chrdev_region(dev_t *devid,unsigned baseminior,unsigned count,const char *name);

这个dev_t由内部生成,拿到之后,用下面的宏得到设备号:

major = MAJOR(devid);
minor = MINOR(devid);

释放:

void unregister_chrdev_region(dev_t from,unsigned count);

cdev对象绑定成员函数和设备id

struct cdev{struct kobject kobj;                       //基础数据结构struct module *owner;                  //module驱动this指针const struct file_operations *ops; //对象方法struct list_head list;dev_t dev;             //对象idunsigned int count;
};

在构造的时候,需要一个全局cdev,以及一个全局的file_operations,并通过下面函数构造:

void cdev_init(struct cdev *cdev,const struct file_operations *fops);

然后将构造好的对象,和申请到的devid绑定:

int cdev_add(struct cdev *cdev,dev_t dev,unsigned count);

删除:

void cdev_del(struct cdev *cdev);

如何让驱动自动创建/dev/xxx

老的设备驱动,需要在加载模块之前,在脚本里,在脚本里加上mknod …,这个也是不合理,因为使用者还需要去查看驱动代码,得知其设计的设备节点号是多少,而且后面就不能改了,万一冲突了咋办?

基于这个需求,内核牛人想出了在内核里搞一个框架,功能如下:
1、加载驱动时,自动创建设备文件,卸载时将它删除。
2、设备驱动可以指定设备名,所有者和权限位,应用同样可以修改。
3、不再需要指定主设备号,给register_chrdev传递0获得可用的主设备号,后续调用devfs_register指定次设备号即可。

这玩意叫做devfs,主要目的是在模块加载和卸载的时候,调用如下接口进行文件系统操作。

ret=register_chrdev(XXX_MAJOR,DEVICE_NAME,&xxx_fops);devfs_handle = devfs_register(NULL,DEVICE_NAME,DEVFS_FL_DEFAULT,XXX_MAJOR,0,S_IFCHR|S_IRUSR,&xxx_fops,NULL);devfs_unregister(devfs_handle);

但这个devfs已成过往,因为内核维护者认为这个devfs有缺点:
1、devfs的工作可以在用户态完成。
2、devfs有无法修复的bug。
3、主要原因是有人认为这个设计是不合理的,policy不应该位于内核态,内核提供的是机制,机制和策略应该是分离的。内核提供的机制是某样事情的固定步骤、方法、而策略是每个步骤采取不同的方式,即用户决定的。机制是稳定的,而策略是灵活的。

比如:内核提供API给人们调节线程优先级,但内核不管具体哪个线程是什么优先级,既然如此,devfs为何要在驱动层直接帮用户创建了设备文件,设置了名字,还设置好了权限呢?
所以,核心原因是devfs管了不该管的事情:)

因此,devfs被位于用户态的udev取代了。

udev设计的出发点:
内核驱动可以实现一个功能,但不能限制如何使用这个功能,关于使用的各种灵活度,应该放在用户空间。
比如,对于devfs,使用mydrv的第一个设备被命名为dev1,第二个为dev2,…devn,但是在用户空间其实可以自由命名。

udev具体实现:
利用hotplug event来工作,热插拔时,设备的详细信息会由内核通过netlink套接字发送出来,发出的事情叫uevent。另外,/sys/module/psmouse/uevent是用来处理冷插拔的(此处以psmouse为例),往里面写入一个add,内核就会重新发出uevent事件,解决冷插拔的问题。用户态只需要有一个进程监听这个套接字即可。

devfs和udev的驱动加载时机:
devfs在设备节点被打开的时候,会加载驱动,无论节点对应的设备是否存在。
而udev认为,设备发出热插拔事件的时候,才需要创建节点,并加载驱动,这个比较好理解,确实更合理,当然用户态有一个进程来监听热插拔事件的UDP socket(PF_NETLINK)。

我们可以根据udev捕获到的消息,创建一个规则,以便每次插入u盘的时候,为u盘创建一个/dev/huqiaoxin_udisk的软链接。

规则文件的大概意思是,新硬件属于什么class,比如net,需要执行什么动作,比如add,mac地址跟xxx匹配,deviid属性跟yyy匹配,type属性为zzz,此时,对这个硬件在udev层面执行的动作是创建/dev/ooo。

udev的衍生
mdev是嵌入式版本,集成到busybox里了。
vold是android版本的。

sysfs和proc
两者都是虚拟文件系统,是为了提供一个给应用层获取硬件和进程信息的直观窗口。
sysfs提供了设备、总线、驱动模型的信息,proc提供了进程和状态信息。
个人理解为,proc以进程为单位来展示系统,sysfs以硬件为单位来展示系统,角度不同。

sysfs下面的block、bus、dev、devices、class、fs、kernel、power、firmware含义
block过滤所有的devices里面的块设备。
devices包含所有设备。
dev按照节点设备号排序和组织,指向具体的devices。
bus按总线分类,比如spi,i2c等,下面有设备和驱动,这里体现总线设备模型
class包含设备类型,也可以叫子系统吧,比如gpio、input、i2c-master等

大部分都是软链接,指向devices目录下的条目。

画一个图可能更清晰:

对总线设备驱动的个人理解

个人理解,总线是抽象出来的,用于操作各个ic平台硬件控制器的通用接口,打个比方,可以把大量ic的i2c控制接口统一了,否则的话,每个厂家的操作方式都不一样。因此,总线是基于内部控制器层面的方法集合,是面向内部控制器的,因为驱动要通信,少不了调用控制器接口。

而设备,是挂在特定总线上具体的物理硬件,也可以理解为DTS里面描述的信息,所以他是数据。

驱动,是针对特定硬件的操作方法,所以,他是控制器外挂载的设备对应的操作方法集合,我们要干的就是这个事情,维护他的数据和方法。

这里的类,则是根据相似行为,抽象出的共同基类,用于简化驱动的实现,同时也是为了统一框架,避免应用层调用驱动的差异化,这样框框定好之后,应用层可以统一实现。硬件变了,驱动变了,应用一行代码不用改,照样可以用。否则的话,同一个功能,一万个人有一万种接口定义方法。

总而言之,一个设备接好之后,他对应的控制器就定了,那么这个方法集合就定了,有现成的一堆给你。同时,他是什么东东,也是定了的,无非是从class下面选一个,或多个,那么又有了一对操作集合。这样看的话,我们写驱动就是给设备定性,获得一堆父类的现有操作接口,而个性化的东西在驱动里面自己整就好了。其中最重要的是,这个关联关系确定之后,底层会自动的做很多事情,比如probe、match之类的,这就是抽象和框架的好处。

欢迎留言讨论。

Linux设备驱动开发(5.4.58)-3-NEWCHR相关推荐

  1. Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用

    关与设备树的概念,我们在Exynos4412 内核移植(六)-- 设备树解析 里面已经学习过,下面看一下设备树在设备驱动开发中起到的作用 Device Tree是一种描述硬件的数据结构,设备树源(De ...

  2. 《Linux设备驱动开发详解 A》一一2.3 接口与总线

    本节书摘来华章计算机出版社<Linux设备驱动开发详解 A>一书中的第2章,第2.3节,作者:宋宝华 更多章节内容可以访问云栖社区"华章计算机"公众号查看.1 2.3 ...

  3. Linux设备驱动开发概述

    作者:宋宝华 email:author@linuxdriver.cn 在过去这些年,Linux已经成功应用于服务器和桌面系统,而近年来,随着嵌入式系统应用的持续升温,Linux也开始广泛应用于嵌入式领 ...

  4. linux设备驱动开发详解源码,linux设备驱动开发详解光盘源码.rar

    压缩包 : linux设备驱动开发详解光盘源码.rar 列表 19/busybox源代码/busybox-1.2.1.tar.bz2 19/MTD工具/mtd-utils-1.0.0.tar.gz 1 ...

  5. Linux 设备驱动开发思想 —— 驱动分层与驱动分离

    前面我们学习I2C.USB.SD驱动时,有没有发现一个共性,就是在驱动开发时,每个驱动都分层三部分,由上到下分别是: 1.XXX 设备驱动 2.XXX 核心层 3.XXX 主机控制器驱动 而需要我们编 ...

  6. linux设备驱动开发专业论坛www.linuxdriver.cn诚征斑竹

    www.linuxdriver.cn诚征斑竹,一起繁荣Linux设备驱动开发专业论坛的人气,共同致力于提高中国工程师的Linux设备驱动开发水平,普及Linux设备驱动开发知识.[url]www.li ...

  7. 《Linux设备驱动开发详解》学习笔记一

    Linux设备驱动开发详解学习笔记<一> 书名:<Linux设备驱动开发详解>第二版 主机环境:Linux version 2.6.25-14.fc9.i686@Fedora ...

  8. 6月14日Linux设备驱动开发免费讲座PPT

    这次讲座主要针对的是有一定开发经验的人士,谈了一些开发体会. 6月14日Linux设备驱动开发免费讲座PPT [url]http://www.linuxdriver.cn/20086/20086159 ...

  9. 《Linux 设备驱动开发详解(第2版)》——1.4 Linux设备驱动

    本节书摘来自异步社区<Linux 设备驱动开发详解(第2版)>一书中的第1章,第1.1节,作者:宋宝华著,更多章节内容可以访问云栖社区"异步社区"公众号查看 1.4 L ...

最新文章

  1. Jquery each和map 的区别
  2. 原因代码10044-Erdos number Time limit exceeded
  3. Matlab atan2
  4. 单例模式引发的内存泄漏:_资源泄漏:救援的命令模式
  5. 函数参数传递、数组指针、二级指针、左值、引用
  6. c语言指针慕课,C语言-指针
  7. Linux 下 Open××× 安装和 Windows Open××× GUI 安装笔记
  8. 及时复盘的好处_还不会复盘?这篇有最全的复盘介绍
  9. ofo这事吧,其实也挺好的
  10. Leetcode 颜色分类
  11. autojs读取文字_Auto.js 获取识别图片文本
  12. 计算机内存延迟,内存延迟有多重要?游戏性能测试说明真相:酷睿i9-9900K依然无敌...
  13. WebServer项目的亮点和难点
  14. 城市地铁站点接驳公交多目标优化方法
  15. 小习题:猴子吃桃问题
  16. 解决“E: Package ‘libqtgui4‘ has no installation candidate”无法安装qt4
  17. 状态同步和帧同步的优缺点
  18. python 爬虫斗图吧 多页图片
  19. java题算工资_Java模版方法的小练习——工资系统
  20. ubuntu windows远程桌面 xfce4

热门文章

  1. lucene 3.02源代码统计
  2. 检测服务器或者PC是否支持IntelSGX的原理和方式
  3. opencv滤镜-浮雕雕刻特效
  4. 机器学习实战项目-拉勾网
  5. 信息发布系统在校园中的应用-校园直播发布,校园媒体发布
  6. Python Web开发之WSGI
  7. Facebook表态不放弃中国市场,还在跟政府谈判
  8. 应用案例 | 2009 款长安福特马自达 3 车行驶中发动机突然熄火
  9. Outlook - 如何设置会议应答后不自动删除会议邮件?
  10. 设计数据库的画图软件