上一篇博客我们改进了最基本的LED字符设备驱动,将通用的驱动代码提取出来形成系统级的LED字符设备驱动部分,并且将与芯片相关的部分交给用户实现,形成芯片级的LED字符设备驱动部分。这两部分构成完整的Linux LED字符设备驱动,并且系统级的部分可以不依赖于具体的芯片,这样我们就把系统级的LED驱动框架给解放出来了。
现在,我们更进一步想,使用同一款芯片的不同产品或板子,上面的LED布局是不是都不一样啊,有可能数量不一样,有可能使用的引脚不一样。那我们之前改进的驱动程序,它虽然可以兼容不同的芯片了,但是却不能兼容使用同一款芯片的不同板子啊,因为我们把LED的板级资源也写在芯片级驱动imx6ull_led.c里面了。如果板级的硬件设计改变了,比如LED换了引脚,或者新增了LED,那得去修改芯片级驱动了。
这个时候,我们想把芯片级的操作也给分离出来,因为对于同一款芯片,操作GPIO的方法都是通用的。这样,之前的芯片级驱动就可以进一步拆分为 芯片级驱动+板级驱动 两部分,芯片级驱动保留通用的GPIO操作部分,板级驱动用于定义和注册板级资源。这样,板级硬件改动的时候,就只需要更新板级驱动就可以了。实际情况也是这样的,芯片级驱动由芯片原厂的BSP工程师负责实现,我们只需要负责板级驱动就可以了。
那经过进一步的分层之后,整个驱动 = 系统级驱动框架(由内核开发人员实现) + 芯片级驱动(芯片原厂BSP工程师实现) + 板级驱动(我们实现)。通常来说,我们说的驱动开发都是指板级驱动了,也是最简单的部分。
接下来,我们就在上一个驱动代码的基础上,分离出芯片级驱动和板级驱动,让它能够兼容使用同一款芯片的不同板子,这里主要做了下面的改进:
1)芯片级LED驱动开放了如下几个调用接口给板级LED驱动进行调用,使得板级驱动能够向芯片级驱动注册LED设备资源。
//板级驱动向芯片驱动注册LED设备
int imx6ull_register_leds(const struct gpio_regs *led_regs, int count);
void imx6ull_unregister_leds(void);
2)imx6ull_led.c里面保留跟IMX6ULL GPIO输出相关的设置和操作,作为IMX6ULL通用的芯片级LED驱动。
3)将跟LED引脚定义相关的部分放到alpha_led.c里面(如果使用了IMX6ULL的其它板子,则更换此文件)。
由于源文件和头文件比较多,就不贴代码了,大家可以点击链接下载对应的驱动源码压缩包: 最终的Linux LED字符设备驱动
使用Makefile编译这个驱动后,会生成leddrv.ko imx6ull_led.ko alpha_led.ko共三个.ko驱动模块,alpha_led.ko会依赖于上层的imx6ull_led.ko,imx6ull_led.ko会依赖于上层的leddrv.ko,所以驱动模块加载顺序为 leddrv.ko > imx6ull_led.ko > alpha_led.ko
那这样,这个简单的Linux LED驱动我们就完成了,虽然不能和内核提供的LED驱动相比,但是作为入门学习已经完全足够了,主要是我们在整个过程中逐步养成和强化了驱动程序的分层设计思想,有助于我们后面学习和理解各种驱动框架。