为什么学习内核自带的LED驱动?
前面已经学习过了基于纯字符设备的LED驱动,也学习过了基于平台驱动的LED驱动,但是感觉都是按照教程在生搬硬套,到底我们写出来的驱动能不能拿得上台面,是否能在实际的生产环境中使用呢?其实我自己在学习的时候,就在不断地问自己这个问题,自己学习时写的驱动代码,跟大公司里面使用的驱动代码,是否有区别?如果有,怎么向他们靠齐呢?比如说对于LED,原理上来说是很简单的,在大家都知晓LED工作原理的情况下,我写出来的驱动代码,跟大牛写出来的,会有多大区别?怎么来进一步提高自己?我想,最好的方法就是学习别人优秀的代码,内核自带的驱动无疑是最好的学习例程了。同样是驱动LED灯,通过学习内核自带的驱动,然后对比自己写的驱动,就能够让自己写驱动的能力更上一个台阶。

怎么找到内核自带的LED驱动源码?
首先,大家可以先尝试着自己在内核目录下面去找LED的驱动源码,在哪里找?因为我们找的是驱动源码,所以肯定是在driver目录下。driver目录下有很多子目录,LED是由GPIO驱动的,所以可以尝试找找有没有gpio或led的子目录,我就是这样找的,然后我发现还真的就有一个leds的子目录,这里面就存放了LED的驱动源码文件,虽然多了一个s,但是八九不离十了吧。
其次,如果大家自己找不到源码的话,可以通过驱动的Makefile来找到。因为内核自带的LED驱动是可以通过配置界面来进行配置的,对应的配置项是CONFIG_LEDS_GPIO,大家只要在driver子目录下的Makefile里面找到CONFIG_LEDS_GPIO,然后看看后面添加了哪些.o文件,就知道对应的源文件了。我这找到的结果如下:

obj-$(CONFIG_LEDS_GPIO)  +=  leds-gpio.o

所以,对应的源文件就是leds-gpio.c了,我们分析这个源文件就可以了。可能有的人会问,你怎么知道LED对应的配置项是CONFIG_LEDS_GPIO呢?我自己肯定不知道的,不过一搜不就知道了吗,像LED这种简单又常用的外设,肯定是很好搜到信息的,其它不常用的外设,可能还真就得到自己去问,或者摸索了。

怎么分析驱动源码?
根据前面学习字符设备驱动的经验,我猜内核自带的LED驱动应该也是按照字符设备驱动的套路来的吧,肯定会用module_init和module_exit这两个宏来定义驱动的入口和出口函数,那我们就从驱动的入口和出口函数为着眼点,来进行阅读不就行了吗?行,那下面就按照这个思路开始学习吧。

1)找驱动入口函数
刚刚才说源码里面肯定用了module_init来定义驱动的入口函数,这里马上就打脸了。我竟然没有找到module_init,从头到尾拉了一遍也没有看到。我心里在纳闷,难道内核自带的驱动程序不需要入库出库函数吗?但是马上我就在心里骂了自己:“怎么可能呢,我怎么可以有这种想法呢,它不是可以编译成模块吗,加载的时候肯定需要入库函数的,肯定有地方定义了module_init”。于是我仔细一找,果然在源文件末尾几行发现一个陌生的宏定义
module_platform_driver(gpio_led_driver);
于是我猜,module_init应该就在这个宏里面了吧。module_platform_driver宏的定义位于include/linux/platform_device.h,定义如下:

/* module_platform_driver() - Helper macro for drivers that don't do* anything special in module init/exit.  This eliminates a lot of* boilerplate.  Each module may only use this macro once, and* calling it replaces module_init() and module_exit()*/
#define module_platform_driver(__platform_driver) \module_driver(__platform_driver, platform_driver_register, \platform_driver_unregister)

module_platform_driver宏继续被展开为一个module_driver的宏,其定义位于include/linux/device.h,定义如下:

/*** module_driver() - Helper macro for drivers that don't do anything* special in module init/exit. This eliminates a lot of boilerplate.* Each module may only use this macro once, and calling it replaces* module_init() and module_exit().** @__driver: driver name* @__register: register function for this driver type* @__unregister: unregister function for this driver type* @...: Additional arguments to be passed to __register and __unregister.** Use this macro to construct bus specific macros for registering* drivers, and do not use it on its own.*/
#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

到这里,我们就看到module_platform_driver被展开之后,就有module_init和module_exit了,module_platform_driver(gpio_led_driver)具体展开后,如下:

static int __init gpio_led_driver_init(void)
{return platform_driver_register(&gpio_led_driver);
}
module_init(gpio_led_driver_init);
static void __exit gpio_led_driver_exit(void)
{platform_driver_unregister(&gpio_led_driver);
}
module_exit(gpio_led_driver_exit);

其中,gpio_led_driver是平台驱动对应的platform driver结构体,定义如下:

static struct platform_driver gpio_led_driver = {.probe     = gpio_led_probe,.remove       = gpio_led_remove,.driver      = {.name   = "leds-gpio",.of_match_table = of_gpio_leds_match,},
};

由此,这个内核自带的LED驱动又回到之前字符设备驱动的老套路了。

知识点总结:基于platform driver的字符设备驱动,如果在驱动的入口函数里面不需要做其它工作的话,可以直接使用module_platform_driver宏,来设置驱动的入口和出口函数,里面会调用platform_driver_register()和platform_driver_unregister()来帮我们完成平台驱动的注册和去注册。如果我们想在驱动的入口函数里做一些其它的工作(比如创建class),那就需要自己写入口函数和出口函数,并用module_init和module_exit来指定。

内核自带的基于GPIO的LED驱动学习(一)相关推荐

  1. 内核自带的基于GPIO的LED驱动学习(二)

    2)分析平台驱动的probe函数 好,既然这个LED驱动使用的是平台驱动框架,当设备和驱动匹配上之后,就会执行指定的probe函数,那接下来的工作就转移到分析对应的probe函数了.为了直观,我把pr ...

  2. 内核自带的基于GPIO的LED驱动学习(三)

    上篇文章讲到了gpio_leds_create函数(),其定义位于drivers/leds/leds-gpio.c,如下: static struct gpio_leds_priv *gpio_led ...

  3. 不写一行代码(一):实现安卓基于GPIO的LED设备驱动

    文章目录 系列文章 一.前言 二.准备工作 2.1 内核版本 2.2 内核文档:bindings->leds 2.3 文档解析: leds-gpio.txt 三.编写DTS 3.1 查原理图,挑 ...

  4. 基于触摸屏的LED驱动电路设计

    本系统是利用触摸屏控制的可调颜色的照明灯具.灯源为大功率超高亮三基色发光二极管组成.本设计最大的亮点是触摸屏上所指示的颜色与实际灯照出的颜色一致.到通过c语言程序对单片机的PWM 功能进行控制实现相应 ...

  5. 基于exynos4412的led驱动编程

    本文基于华清4412开发板,讲解如何从零开始编写led驱动程序和测试程序.首先介绍一下该4412开发板的led硬件原理图. 从原理图上我们可以看出,让led点亮的条件是往对应端口送高电平,熄灭的条件是 ...

  6. 基于RK3399的LED驱动开发

    1.添加设备树 在设备树 arch/arm64/boot/dts/rockchip/rk3399-firefly-linux.dts 中添加 gpio-led{status = "okay& ...

  7. 字符设备驱动之Led驱动学习记录

    一.概述 Linux内核就是由各种驱动组成的,内核源码中大约有85%的各种渠道程序的代码.一般来说,编写Linux设备驱动大致流程如下: 1.查看原理图,数据手册,了解设备的操作方法. 2.在内核中找 ...

  8. 基于RK3399PRo的串口驱动学习-XR21V1414IM48

    目录 原理图 XR21V1414IM48简介 重点代码 函数入口初始化 tty串口操作集 USB转串初始化 设备ID 测试代码 头文件 宏定义 输入参数提示 打开设备 设置波特率 配置数据位.停止位. ...

  9. 基于sys文件系统的LED驱动的移植【原创】

    基于RK3188平台LED驱动程序的移植的移植.如有不正确之处,欢迎大家指点. 本文的LED驱动程序不是通过打开设备节点来访问和控制LED的,是通过sys文件系统来控制LED. 板子上有四盏灯以及对应 ...

最新文章

  1. Facebook面部识别新突破:可识别未标记照片中用户
  2. 多线程-010-后台线程
  3. iptables如何添加容许某个端口的访问
  4. mathtype批量修改公式的字体大小
  5. php设置文件权限问题,php以fastCGI的方式运行时文件系统权限问题及解决方法
  6. java显示文件_java 显示文件夹结构
  7. 查看ORACLE的实际执行计划
  8. Google Wave 的失败给现代实时协作办公的一个重大教训!
  9. Vue Router 4 快速入门
  10. java字符编码详解_Java中字符编码格式详解
  11. 一步步实现windows版ijkplayer系列文章之一Windows10平台编译ffmpeg 4.0.2,生成ffplay
  12. JPDA 架构研究4 - JDWP的传输器
  13. 完全卸载VS 2015各版本
  14. Vue实现登录记住密码
  15. MDM授权管理设计方案
  16. HYSPLIT模型后向轨迹制作
  17. Excel中的相对引用和绝对引用详解
  18. uni-app 退出app操作
  19. 马云:阿里巴巴要培养更多的京东并让这些企业挣钱
  20. JNA二次开发华视身份证阅读器

热门文章

  1. 数字图像处理第九章——形态学图像处理
  2. python 协程池
  3. php 连接已重置,[百思不得其解] Nginx 连接已重置 ERR_CONNECTION_RESET
  4. 亚马逊欧洲站点遇见kyc问题审核了怎么办?
  5. 矩阵理论| 特殊矩阵:初等矩阵(1) - (行列式、逆矩阵、特征向量)、初等矩阵的相关定理和性质
  6. 特斯拉向上,蔚来汽车向前
  7. 青语易呼电销机器人源码独立部署搭建步骤
  8. PAT 乙级练习 1069 微博转发抽奖
  9. VOL框架学习(一)从零开始搭建项目
  10. splint 错误。c99