内核LED驱动框架讲解以及led设备注册示例代码

1、驱动框架介绍

(1)内核中驱动部分维护者针对每个种类的驱动设计一套成熟的、标准的、典型的驱动实现,然后把不同厂家的同类硬件驱动中相同的部分抽出来自己实现好,再把不同部分留出接口给具体的驱动开发工程师来实现,这就叫驱动框架。
(2)内核维护者在内核中设计了一些统一管控系统资源的体系,这些体系让内核能够对资源在各个驱动之间的使用统一协调和分配,保证整个内核的稳定健康运行。譬如系统中所有的GPIO就属于系统资源,每个驱动模块如果要使用某个GPIO就要先调用特殊的接口先申请,申请到后使用,使用完后要释放。
(3)一些特定的接口函数、一些特定的数据结构,这些是驱动框架的直接表现。

2、LED框架相关文件

(1)drivers/leds目录,这个目录就是驱动框架规定的LED这种硬件的驱动源码放的路径。
(2)led-class.c和led-core.c,这两个文件加起来属于LED驱动框架的第一部分,这两个文件是内核开发者提供的,他们描述的是内核中所有厂家的不同LED硬件的相同部分的逻辑。
(3)leds-xxxx.c,这个文件是LED驱动框架的第2部分,是由不同厂商的驱动工程师编写添加的,厂商驱动工程师结合自己公司的硬件的不同情况来对LED进行操作,使用第一部分提供的接口来和驱动框架
进行交互,最终实现驱动的功能。

3、LED框架分析

3.1、内核支持LED框架

make menuconfig
	Device Drivers 
		LED Support
			LED Class Support #选择y或者m

(1)执行"make menuconfig"命令,一般选择"LED Class Support"为y,直接编译进内核;选择为y会后,再次编译内核就会把led-class.c编译进内核;
(2)或者直接去修改内核配置文件的"CONFIG_LEDS_CLASS=y"配置项;

3.2、LED框架的加载和卸载

//导出LED驱动的注册/卸载函数
EXPORT_SYMBOL_GPL(led_classdev_register);
EXPORT_SYMBOL_GPL(led_classdev_unregister);

static int __init leds_init(void)
{
	leds_class = class_create(THIS_MODULE, "leds");	//创建"leds"设备类
	if (IS_ERR(leds_class))
		return PTR_ERR(leds_class);
	leds_class->suspend = led_suspend;	//设备休眠的函数指针
	leds_class->resume = led_resume;	//设备唤醒的函数指针
	leds_class->dev_attrs = led_class_attrs;//初始化设备属性
	return 0;
}

static void __exit leds_exit(void)
{
	class_destroy(leds_class);	//销毁"leds"设备类
}

subsys_initcall(leds_init); //加载LED框架
module_exit(leds_exit);	//卸载LED框架

(1)LED框架的代码和一本的字符驱动几乎一样,既可以编译进内核又可以单独编译成ko文件,但是需要注意驱动的加载函数用subsys_initcall宏声明;
(2)我们利用LED驱动框架写led驱动,就是利用led_classdev_register()函数来注册一个驱动,重点是理解led_classdev_register()函数;

3.3、subsys_initcall宏

	#define subsys_initcall(fn)		__define_initcall("4",fn,4)
	
	subsys_initcall(leds_init);

(1)效果:把leds_init函数放到".initcall4.init"段,具体宏分析参考:《内核加载驱动机制详解(module_init & module_exit)》
(2)我们自己编写的LED驱动加载时用"module_init"宏,这样我们的LED驱动代码会被放在".initcall6.init"段;
(3)“.initcall4.init"段会比”.initcall6.init"段先加载,而我们写的LED驱动代码是依赖内核的LED框架的,所以内核LED框架被先加载也是必须的;

3.4、struct class结构体和struct device_attribute结构体

参考博客:《struct class结构体》

3.5、内核LED框架创建leds类

/* interface for exporting device attributes */
struct device_attribute {
	struct attribute	attr;
	ssize_t (*show)(struct device *dev, struct device_attribute *attr,
			char *buf);
	ssize_t (*store)(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count);
};

static struct device_attribute led_class_attrs[] = {
	__ATTR(brightness, 0644, led_brightness_show, led_brightness_store),
	__ATTR(max_brightness, 0444, led_max_brightness_show, NULL),
#ifdef CONFIG_LEDS_TRIGGERS
	__ATTR(trigger, 0644, led_trigger_show, led_trigger_store),
#endif
	__ATTR_NULL,
};

在这里插入图片描述

(1)在leds_init()创建leds类时,将led_class_attrs变量赋予leds_class->dev_attrs,作为类的设备属性;
(2)效果:每一个创建的属于LED类的设备,在设备文件夹下面都会有brightness、max_brightness、trigger文件,在读取这些文件时调用对应的show方法,在写入这些文件时调用store方法,如果对应的show/store方法是NULL,则不支持读取/写入;
(3)brightness:用来控制亮度;max_brightness:保存允许的最大亮度;trigger:触发方式;

4.用LED框架注册led设备

4.1、注册和卸载接口

int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
	led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
				      "%s", led_cdev->name);

	······
	return 0;
}
EXPORT_SYMBOL_GPL(led_classdev_register);

void led_classdev_unregister(struct led_classdev *led_cdev)
{
	······

	device_unregister(led_cdev->dev);
	······
}
EXPORT_SYMBOL_GPL(led_classdev_unregister);

(1)LED框架中导出了led_classdev_register()函数和led_classdev_unregister()函数,给我们注册led驱动;
(2)注册接口:主要功能就是调用device_create()函数在leds类下面创建名字为led_cdev->name的设备;
(3)卸载接口:主要功能就是调用device_unregister()函数注销之前注册驱动时创建的设备;
总结:注册led驱动的重点就是构建注册接口led_classdev_register()函数需要的struct led_classdev结构体;

4.2、struct led_classdev结构体

struct led_classdev {
	const char		*name;	//将来在/sys/class/leds/目录下,创建名字为name的设备
	int			 brightness;	//led设备的亮度
	int			 max_brightness;	//led设备的最大亮度
	int			 flags;				//led设备的标志位

	void		(*brightness_set)(struct led_classdev *led_cdev,
					  enum led_brightness brightness);	//设置led设备亮度的方法

	enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);	//获取led设备亮度的方法

	struct device		*dev;	//设备结构体,用来在内核中表示设备
	struct list_head	 node;			/* LED Device list */
	······
};

(1)上面是结构体的部分成员变量,在LED框架中,就是用struct led_classdev结构体来描述一个led设备;
(2)如何填充struct led_classdev结构体是注册led设备的重点;

4.3、怎么添加自己的LED驱动

在这里插入图片描述

红框里的就是之前别人添加好的LED驱动代码,我们只需要仿造别人的添加过程去添加自己的驱动代码;
大致步骤:
(1)将自己的led驱动源码文件放到kernel/drivers/leds目录下;
(2)在kernel/drivers/leds目录的Makefile和Kconfig文件中支持我们新添加的驱动源文件;

5、用LED驱动框架注册led设备的示例代码分析

参考博客:《用LED驱动框架注册led设备的示例代码》

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

正在起飞的蜗牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值