linux驱动等于单片机操作+linux各个子系统架构+操作系统,但是学习的时候要把重心放在架构和操作系统上面,这样驱动设计得更好。我一直不满足于在产品侧当一个看代码的bsp驱动工程师,有朝一日我想去大厂,去当芯片端的bsp驱动工程师,让别人用我设计的驱动。因此,我真就只遵循套路来写一些简单的设备驱动就行了吗?
重新学习字符设备的时候发现了第一次学习没有思考的几个问题:
总的来看当我们打开一个设备节点,系统发生了什么?如何和我的驱动操作集关联?想要一个驱动兼容同类的设备时候,次设备号扮演了什么角色?如何去设置cdev?cdev需要多少个?
以上问题并不能直接解答,先看看我们是怎么把一个字符设备以及他的操作集加到系统中的。
设备号的管理
众所周知,设备驱动中有个很重要的概念叫设备号,一个32位设备号分为主设备号和次设备号,高20位是主设备号,次设备号的低12位。主设备号对应的是某个驱动程序.ko,次设备号标识这个驱动管理下的各种同类设备。
我使用alloc_chrdev_region函数来分配和注册设备号
intalloc_chrdev_region(dev_t*dev,unsignedbaseminor,unsignedcount,constchar*name)#第一个参数是你传入的一个变量用于保存系统分配的设备号#第二、三个参数是以次设备号开始申请多少个次设备号#第四个参数是管理这组设备的一个管理员名字
上面说的管理员实际是结构体char_device_struct。
然后现在啥也别想char_device_struct是什么了,全都抛弃。系统中有个管理设备号的数组:chardevs[]。里面每一个元素都是一个char_device_struct指针。
比如我这样分配
alloc_chrdev_region(imx6ull_led_beep.device_num,0,2,"led_beep");
那么对应的状况就会是这样的
为什么major是1或者?因为如果要找的话,是通过求模找的比如1%等于1,%也等于1。如果同时1和主设备号同时存在,那么也可以加到chrdevs[1]下面来管理,按照major大小从小到大链表排列。比如下面这样
这个结论就是:设备号是通过chedevs数组管理的。但是这并没有接近我思考的问题的核心。
字符设备cdev的管理
字符设备驱动中最重要的就是cdev和它的操作集。那它是如何管理的。
涉及两个函数:
voidcdev_init(structcdev*cdev,conststructfile_operations*fops)intcdev_add(structcdev*p,dev_tdev,unsignedcount)
cdev_init就是绑定cdev和操作集,cdev_add就是加入到管理中来。
cdev的管理是由cdev_map指针来管理的,指向一个structkobj_map
比如我向系统这样增加
cdev_init(imx6ull_led_beep.cdev,imx6ull_led_beep_fops);cdev_add(imx6ull_led_beep.cdev,imx6ull_led_beep.device_num,2);或者cdev_init(imx6ull_led_beep.cdev,imx6ull_led_beep_fops);cdev_add(imx6ull_led_beep.cdev,imx6ull_led_beep.device_num,1);cdev_add(imx6ull_led_beep.cdev,imx6ull_led_beep.device_num+1,1);#以上两种方法一样的,为什么呢?因为imx6ull_led_beep.device_num#和imx6ull_led_beep.device_num+1都是同一个major
对应的状况是这样:
因此可以得出结论:一个驱动(主设备号)下,只设置一个cdev也能够管理多个同类设备,只要你认为同类设备都能用同一个操作集。但是注意cdev注册和设备号管理并没有关系!
注意:cdev_map同样是根据求模取得位置的,和上面chrdevs一样,但是这个不要太