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

本文探讨了学习内核自带LED驱动的重要性,介绍了如何找到并分析这些驱动源码的方法。通过对比自制驱动,读者能深入了解优秀代码的设计思路和技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为什么学习内核自带的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来指定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值