天天看點

linux 驅動之led子系統(一)

linux系統基于3.4

平台msm8x26

android 4.4.2

linux 中存在衆多的子系統,包括led子系統,input子系統等等,那麼就先從最簡單的led子系統開始分析了。這次要盡量分析徹底。

在andorid中的led系統是在\kernel\drivers\leds,在經常分析代碼的過程中,首先需要看的是Kconfig和makefile檔案,那好先看Kconfig

menuconfig NEW_LEDS
	bool "LED Support"
	help
	  Say Y to enable Linux LED support.  This allows control of supported
	  LEDs from both userspace and optionally, by kernel events (triggers).

	  This is not related to standard keyboard LEDs which are controlled
	  via the input system.
           
if NEW_LEDS


config LEDS_CLASS
tristate "LED Class Support"
help
  This option enables the led sysfs class in /sys/class/leds.  You'll
  need this to do anything useful with LEDs.  If unsure, say N.
           

這類似于在linux系統執行make menuconfig,首先是NEW_LEDS,看注釋就是說必須使能LED Support,才能有所謂的平台的led的支援。這些一般是支援的,而且一般的平台也是會使能的,高通平台上有Msm_defconfig (arch\arm\configs),看出來支援新增led以及led的class的。

CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
           

看到這些的話,應該看出msm平台是支援led的(想想也支援)接下來就是看makefile了

# LED Core
obj-$(CONFIG_NEW_LEDS)			+= led-core.o
obj-$(CONFIG_LEDS_CLASS)		+= led-class.o
obj-$(CONFIG_LEDS_TRIGGERS)		+= led-triggers.o
           

以上的是和平台無關的,而且從我查找的這些個obj-$(tmp),以上的檔案都是會編譯的。即led-core.c,led-class.c,led-triggers.c。這三個檔案是led子系統的3個重要的檔案,配合平台相關的led驅動,構成了一個完整的led子系統。

好,先看led-core.c

在led-core.c中導出了兩個接口,一個是用來控制led的閃爍的,以及閃爍的時間,一個接口是用來直接設定led的亮滅的。

void led_blink_set(struct led_classdev *led_cdev,
		   unsigned long *delay_on,
		   unsigned long *delay_off)
           
void led_brightness_set(struct led_classdev *led_cdev,
			enum led_brightness brightness)
           

再看led-class.c,再看這個檔案的時候我們再看一下led-class.txt

If you're reading this and thinking about keyboard leds, these are handled by the input subsystem and the led class is *not* needed.
           

好,說鍵盤燈是屬于input的子系統的,不為led-class所控制。

In its simplest form, the LED class just allows control of LEDs from userspace. LEDs appear in /sys/class/leds/. The maximum brightness of the LED is defined in max_brightness file. The brightness file will set the brightness of the LED (taking a value 0-max_brightness). Most LEDs don't have hardware brightness support so will just be turned on for non-zero brightness settings.
           

說led-class僅能從使用者空間的 /sys/class/leds/控制,而且可以控制最大的亮度,但是一般平台沒有硬體控制,是以隻能非0控制亮,0控制不亮。

The class also introduces the optional concept of an LED trigger. A trigger is a kernel based source of led events. Triggers can either be simple or complex. A simple trigger isn't configurable and is designed to slot into existing subsystems with minimal additional code. Examples are the ide-disk, nand-disk and sharpsl-charge triggers. With led triggers disabled, the code optimises away.
           

led子系統還導入了一個LED-trigger的概念,當需要的時候我們需要使能CONFIG_LEDS_TRIGGERS = y,否則不支援。

Complex triggers whilst available to all LEDs have LED specific parameters and work on a per LED basis. The timer trigger is an example. The timer trigger will periodically change the LED brightness between LED_OFF and the current brightness setting. The "on" and "off" time can be specified via /sys/class/leds/<device>/delay_{on,off} in milliseconds. You can change the brightness value of a LED independently of the timer trigger. However, if you set the brightness value to LED_OFF it will also disable the timer trigger.
           

複雜的led-trigger例如可以更改滅和目前設定的led的亮度之間切換,時間可以通過 /sys/class/leds/<device>/delay_{on,off}接口來控制時間(ms級别)。

You can change triggers in a similar manner to the way an IO scheduler is chosen (via /sys/class/leds/<device>/trigger). Trigger specific parameters can appear in /sys/class/leds/<device> once a given trigger is selected.
           

可以通過 via /sys/class/leds/<device>/trigger的接口來使用led-trigger。

2.led-class.c 分析

static int __init leds_init(void)
{
	leds_class = class_create(THIS_MODULE, "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);
}
           

led_class 被定義為 static struct class *leds_class;這個類顯示在/sys/class/ 下,當調用 class_create(owner, name)函數的時候會在/sys/class/下建立name的目錄,這裡建立的是leds,同時将led-class中的suspend的指針以及resume的指針初始化了,一般來說是當系統休眠的時候系統上層會層層通知各個裝置進入睡眠狀态,那麼負責這個裝置的驅動則實際執行睡眠,例如手機的休眠鍵位,喚醒時調用的是resume,恢複裝置的運作狀态,這也是為了省電。即電源管理。其中将dev_attrs初始化為led_class_attrs,其中led_class_attrs為

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

這個作用是在每個裝置下建立brightness,max_brightness,trigger(當然得定義CONFIG_LEDS_TRIGGERS)的屬性檔案,權限是0644,讀檔案是調用show,寫檔案時調用store函數。注意最後要加上NULL。

接着分析:在led-class中還導出了幾個接口:

int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
           

當led類裝置驅動注冊時調用

void led_classdev_unregister(struct led_classdev *led_cdev)
           

當led類裝置驅動登出時調用

void led_classdev_resume(struct led_classdev *led_cdev)
           

led類裝置喚醒時調用

void led_classdev_suspend(struct led_classdev *led_cdev)
           

led類裝置挂起時調用

還有幾個關于對裝置的屬性檔案的讀寫函數的實作。

3.led-trigger.c

這個檔案中大部分是接口,用來注冊一個trigger,

void led_trigger_register_simple(const char *name, struct led_trigger **tp)
           

登出一個trigger

void led_trigger_unregister_simple(struct led_trigger *trigger)
           

led觸發閃爍,實際調用的是led-core.c中的led_blink_set函數

void led_trigger_blink(struct led_trigger *trigger,
		       unsigned long *delay_on,
		       unsigned long *delay_off)
           

led出觸發亮滅,實際調用的是led-core.c中的led_set_brightness函數

void led_trigger_event(struct led_trigger *trigger,
			enum led_brightness brightness)
           

以上的全是基于led-trigger的簡單的接口,就像前面說的txt中。

接下說較為複雜的

涉及使用led-class的時候使用

當對驅動的trigger的屬性進行寫時調用的接口。

ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
		const char *buf, size_t count)
           

對驅動的trigger的屬性進行讀的時候調用的接口

ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
		char *buf)
           

繼續閱讀