Linux驱动分析——LED子系统+驱动框架简介

1、驱动是谁写的:

(1)驱动开发工程师

(2)内核维护者

2、驱动编程写作要求:

(1)接口标准化

(2)尽量降低驱动开发者难度

3、到底什么是驱动框架:

(1)内核中驱动部分维护者针对每个种类的驱动设计一套成熟的、标准的、典型的驱动实现,然后把不同厂家的同类硬件驱动中相同的部分抽出来自己实现好,再把不同部分留出接口给具体的驱动开发工程师来实现,这就叫驱动框架。

(2)内核维护者在内核中设计了一些统一管控系统资源的体系,这些体系让内核能够对资源在各个驱动之间的使用统一协调和分配,保证整个内核的稳定健康运行。譬如系统中所有的GPIO就属于系统资源,每个驱动模块如果要使用某个GPIO就要先调用特殊的接口先申请,申请到后使用,使用完后要释放。又譬如中断号也是一种资源,驱动在使用前也必须去申请。这也是驱动框架的组成部分。

(3)一些特定的接口函数、一些特定的数据结构,这些是驱动框架的直接表现。

Linux驱动分析——LED子系统+驱动框架简介_第1张图片

Linux驱动分析——LED子系统+驱动框架简介_第2张图片

Linux驱动分析——LED子系统+驱动框架简介_第3张图片

Linux驱动分析——LED子系统+驱动框架简介_第4张图片

以下是Linux内核的段链接文件:

Linux驱动分析——LED子系统+驱动框架简介_第5张图片

Linux驱动分析——LED子系统+驱动框架简介_第6张图片

Linux驱动分析——LED子系统+驱动框架简介_第7张图片

Linux驱动分析——LED子系统+驱动框架简介_第8张图片

Linux驱动分析——LED子系统+驱动框架简介_第9张图片

Linux驱动分析——LED子系统+驱动框架简介_第10张图片

Linux驱动分析——LED子系统+驱动框架简介_第11张图片

Linux驱动分析——LED子系统+驱动框架简介_第12张图片

Linux驱动分析——LED子系统+驱动框架简介_第13张图片

以上是朱有鹏老师“驱动框架入门之LED-linux驱动开发第4部分”视频的笔记截图

stm32mp157a芯片、盘古开发板、Linux kernel版本: 4.19.43+

在其盘古wiki上看到LED相关的操作:

Linux驱动分析——LED子系统+驱动框架简介_第14张图片

这就是linux kernel的led子系统,我在设备树里面添加了几个led设备(设备树这里不做过多描述,上面朱老师的视频是2.6的内核,里面还没有设备树这个功能或者模式),设备树文件如下:

	leds {
		compatible = "gpio-leds";
		cpu {
			label = "cpu";
			gpios = <&gpioi 11 GPIO_ACTIVE_LOW>;
			linux,default-trigger = "heartbeat";
			default-state = "on";
		};
		led2g {
			label = "led2g";
			gpios = <&gpiod 13 GPIO_ACTIVE_LOW>;
			linux,default-trigger = "heartbeat";
			default-state = "on";
		};
		led2r {
			label = "led2r";
			gpios = <&gpiod 12 GPIO_ACTIVE_HIGH>;
			default-state = "on";
		};
		led3r {
			label = "led3r";
			gpios = <&gpiob 10 GPIO_ACTIVE_HIGH>;
			default-state = "on";
		};
		led3g {
			label = "led3g";
			gpios = <&gpiof 12 GPIO_ACTIVE_HIGH>;
			default-state = "on";
		};
		led3b {
			label = "led3b";
			gpios = <&gpiog 7 GPIO_ACTIVE_HIGH>;
			default-state = "on";
		};
	};

这个leds节点,是根节点下的一个子节点。设备树编译更新到电路板上之后:

Linux驱动分析——LED子系统+驱动框架简介_第15张图片

这里可以理解为在目录/sys/class下放的都是类,比如这个leds就是一个类,下面的就是这个leds类实例化之后的对象,即cpu  led2g  led2r  led3b  led3g  led3r可以认为是对象,每个对象代表着一个led灯,那么对象的目录下面,即brightness  device  invert  max_brightness  power  subsystem  trigger  uevent,这些都是该对象的属性和方法。

那么这些属性有什么用或者说怎么用呢?参考内核目录下的描述文件:linux-st\Documentation\ABI\testing\sysfs-class-led,内容如下:

What:		/sys/class/leds//brightness
Date:		March 2006
KernelVersion:	2.6.17
Contact:	Richard Purdie 
Description:
		Set the brightness of the LED. Most LEDs don't
		have hardware brightness support, so will just be turned on for
		non-zero brightness settings. The value is between 0 and
		/sys/class/leds//max_brightness.

		Writing 0 to this file clears active trigger.

		Writing non-zero to this file while trigger is active changes the
		top brightness trigger is going to use.

What:		/sys/class/leds//max_brightness
Date:		March 2006
KernelVersion:	2.6.17
Contact:	Richard Purdie 
Description:
		Maximum brightness level for this LED, default is 255 (LED_FULL).

		If the LED does not support different brightness levels, this
		should be 1.

What:		/sys/class/leds//brightness_hw_changed
Date:		January 2017
KernelVersion:	4.11
Description:
		Last hardware set brightness level for this LED. Some LEDs
		may be changed autonomously by hardware/firmware. Only LEDs
		where this happens and the driver can detect this, will have
		this file.

		This file supports poll() to detect when the hardware changes
		the brightness.

		Reading this file will return the last brightness level set
		by the hardware, this may be different from the current
		brightness. Reading this file when no hw brightness change
		event has happened will return an ENODATA error.

What:		/sys/class/leds//trigger
Date:		March 2006
KernelVersion:	2.6.17
Contact:	Richard Purdie 
Description:
		Set the trigger for this LED. A trigger is a kernel based source
		of LED events.
		You can change triggers in a similar manner to the way an IO
		scheduler is chosen. Trigger specific parameters can appear in
		/sys/class/leds/ once a given trigger is selected. For
		their documentation see sysfs-class-led-trigger-*.

What:		/sys/class/leds//inverted
Date:		January 2011
KernelVersion:	2.6.38
Contact:	Richard Purdie 
Description:
		Invert the LED on/off state. This parameter is specific to
		gpio and backlight triggers. In case of the backlight trigger,
		it is useful when driving a LED which is intended to indicate
		a device in a standby like state.

在这个目录下面还有好几个描述文件:

Linux驱动分析——LED子系统+驱动框架简介_第16张图片

通过这几个文件的描述,以及百度的各种搜索可以发现:往brightness  里面写入0或者1可以控制灯的亮或者灭,当然不一定0就是灭,1就是亮,要看硬件的链接方式。max_brightness 灯的最大亮度,默认是255。查完之后也就知道了这两个属性的含义和使用。

疑问,设备树里面:

Linux驱动分析——LED子系统+驱动框架简介_第17张图片

可以增加灯的属性“heartbeat”,这个属性值还有其它的吗?有的话都可以是什么?最终在这里找到了答案:

root@pangu:/sys/class/leds/cpu# cat trigger 
none rfkill-any rfkill-none kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock mmc0 mmc1 timer oneshot [heartbeat] backlight gpio cpu cpu0 cpu1 default-on transient flash torch 
root@pangu:/sys/class/leds/cpu# cat ../led2r/trigger 
[none] rfkill-any rfkill-none kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock mmc0 mmc1 timer oneshot heartbeat backlight gpio cpu cpu0 cpu1 default-on transient flash torch 

截图更直观:

发现在每一个灯的下面这个trigger文件里面,有很多,比如heartbeat  oneshot  timer 等等。。。

root@pangu:/sys/class/leds/led2r# ls
brightness  device  max_brightness  power  subsystem  trigger  uevent
root@pangu:/sys/class/leds/led2r# cat trigger 
[none] rfkill-any rfkill-none kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock mmc0 mmc1 timer oneshot heartbeat backlight gpio cpu cpu0 cpu1 default-on transient flash torch 
root@pangu:/sys/class/leds/led2r# echo timer > trigger 
root@pangu:/sys/class/leds/led2r# cat trigger 
none rfkill-any rfkill-none kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock mmc0 mmc1 [timer] oneshot heartbeat backlight gpio cpu cpu0 cpu1 default-on transient flash torch 
root@pangu:/sys/class/leds/led2r# ls
brightness  delay_off  delay_on  device  max_brightness  power  subsystem  trigger  uevent
root@pangu:/sys/class/leds/led2r# 

截图更直观:

Linux驱动分析——LED子系统+驱动框架简介_第18张图片

即,这个timer模式下,可以通过设置这个delay_off和delay_on里面的值让等交替亮灭。

下面再看一个oneshot

这几个属性怎么使用,可以看内核里面的说明文档:linux-st\Documentation\ABI\testing\sysfs-class-led-trigger-oneshot,内容如下:

What:		/sys/class/leds//delay_on
Date:		Jun 2012
KernelVersion:	3.6
Contact:	[email protected]
Description:
		Specifies for how many milliseconds the LED has to stay at
		LED_FULL brightness after it has been armed.
		Defaults to 100 ms.

What:		/sys/class/leds//delay_off
Date:		Jun 2012
KernelVersion:	3.6
Contact:	[email protected]
Description:
		Specifies for how many milliseconds the LED has to stay at
		LED_OFF brightness after it has been armed.
		Defaults to 100 ms.

What:		/sys/class/leds//invert
Date:		Jun 2012
KernelVersion:	3.6
Contact:	[email protected]
Description:
		Reverse the blink logic. If set to 0 (default) blink on for
		delay_on ms, then blink off for delay_off ms, leaving the LED
		normally off. If set to 1, blink off for delay_off ms, then
		blink on for delay_on ms, leaving the LED normally on.
		Setting this value also immediately changes the LED state.

What:		/sys/class/leds//shot
Date:		Jun 2012
KernelVersion:	3.6
Contact:	[email protected]
Description:
		Write any non-empty string to signal an events, this starts a
		blink sequence if not already running.

好了,这个模式就记录到这里,后面具体需要用哪个再去研究。

接下来简单分析一下这个在LED驱动框架下的LED驱动:

朱老师的视频是2.6的内核,我的是4.19的内核,还是有着一些区别,按照朱老师的方法分析4.19内核的led-class.c文件:

Linux驱动分析——LED子系统+驱动框架简介_第19张图片

Linux驱动分析——LED子系统+驱动框架简介_第20张图片

Linux驱动分析——LED子系统+驱动框架简介_第21张图片

这里稍微有点复杂,这是在设置属性,led_groups这个连续调用了好几个函数,中间的函数可以直接跟过去去看,这里只写出来最后的部分:
static struct attribute *led_class_attrs[] = {
      &dev_attr_brightness.attr,
      &dev_attr_max_brightness.attr,
      NULL,
};

这里可以看到有dev_attr_brightness和dev_attr_max_brightness,接下来一下这两个变量(函数)怎么产生的:
在Led-class.c(drivers\leds)中有定义:
static DEVICE_ATTR_RW(brightness);
static DEVICE_ATTR(max_brightness, 0444,led_max_brightness_show, NULL);

DEVICE_ATTR_RW和 DEVICE_ATTR是两个宏,他们定义在Device.h (include\linux)中 
#define DEVICE_ATTR_RW(_name) \
      structdevice_attribute dev_attr_##_name = __ATTR_RW(_name)
#define DEVICE_ATTR(_name, _mode, _show,_store) \
      structdevice_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

通过这两个宏替换后就成为:
dev_attr_ brightness=__ATTR_RW(brightness)
dev_attr_max_brightness=__ATTR(max_brightness,0444, led_max_brightness_show, NULL)

既然这样,继续向下看,在Sysfs.h (include\linux)中有定义:
#define __ATTR_RW(_name) __ATTR(_name,(S_IWUSR | S_IRUGO),              \
                     _name##_show, _name##_store)
#define __ATTR(_name, _mode, _show, _store) {                        \
      .attr  = {.name = __stringify(_name), .mode = _mode },          \
      .show  = _show,                                          \
      .store = _store,                                          \
}

再次宏定义替换得到:
dev_attr_ brightness=__ATTR(_name, (S_IWUSR |S_IRUGO), _name##_show, _name##_store)
					={
						.attr  = {.name = __stringify(brightness), .mode =(S_IWUSR | S_IRUGO) },
						.show  = brightness _show,                                      \
						.store = brightness _store,   
					 }

dev_attr_ max_brightness={
                            .attr  = {.name = __stringify(max_brightness), .mode =0444 },
							.show  = max_brightness _show,                                    \
							.store = max_brightness _store, 
}

通过跟中代码,可以看到:
最终的 __stringify(x)被宏定义为#x
所以得到最后的结果就是:
dev_attr_ brightness = {
                            .attr  = {.name =”brightness”, .mode =6444 },
							.show  = brightness _show,                                      \
                            .store = brightness _store,   
}

dev_attr_ max_brightness={
                           .attr  = {.name = “max_brightness”, .mode =0444 },
						   .show  = max_brightness _show,                                    \
                           .store = max_brightness _store, 
}

这下在理解一下#define __ATTR(_name, _mode, _show, _store)其中四个参数的名字
-name:名字,对于本例led,就是在对应的LEDn文件下的max_brightness和brightness文件,下面就以brightness为例子介绍
mode:设置该文件的权限,对于本例brightness的权限就是6444
show:这是指当对brightness进行读操作的时候,调用该函数
store:这是指当对brightness进行写操作的时候,调用该函数

参考:http://blog.csdn.net/zclongembedded/article/details/8689099

这一堆分析下来,其实就是在特定位置创建特定功能的文件。

这个地方搞清楚之后。

有了led驱动框架之后,那么这个驱动框架下面应该有一个驱动程序的,来驱动stm32mp157的led,查看目录linux-st\drivers\leds下,被编译的文件只有:

Linux驱动分析——LED子系统+驱动框架简介_第22张图片

Linux驱动分析——LED子系统+驱动框架简介_第23张图片

只有这几个文件被编译了

排查发现,实现驱动程序的是leds-gpio.c

简单看一下这个文件:

首先是平台设备驱动模块的注册和退出:

Linux驱动分析——LED子系统+驱动框架简介_第24张图片

然后是驱动和设备的匹配函数:

这个匹配函数里面的名字是和设备树里面的名字一直的:

Linux驱动分析——LED子系统+驱动框架简介_第25张图片

然后再看下刺探(probe)函数:

Linux驱动分析——LED子系统+驱动框架简介_第26张图片

Linux驱动分析——LED子系统+驱动框架简介_第27张图片

参考:

https://blog.csdn.net/woshidahuaidan2011/article/details/51695106

http://www.cnblogs.com/leaven/archive/2010/04/24/1719191.html

http://blog.csdn.net/liuhaoyutz/article/details/13502557

http://blog.chinaunix.net/uid-27664726-id-3334662.html

http://www.linuxidc.com/Linux/2012-05/60757.htm

http://blog.sina.com.cn/s/blog_69dd1a0901010cr5.html

http://blog.chinaunix.net/uid-11319766-id-3253414.html

 

 

你可能感兴趣的:(Linux)