linux驅動____LED子系統筆記

LED在linux系統中居然組織了一個子系統,個人完全可以自定義LED系統,但是爲了統一到,便於代碼移植的一致性,我們還是多用系統帶的LED子系統吧!如果你是開發android驅動的,那HAL層用的就是LED子系統提供的sys接口。本文以linux kernel 3.4.0爲例。使用時只要包含頭文件#include<linux/leds.h>即可。

LED子系統大概數據結構圖如下:



註冊的led_classdev設備都會鏈接到leds_list鏈表上,通過node字段連接,同樣註冊的led_trigger類型都會鏈接到trigger_list鏈表上,通過next_trig字段連接;同一個trigger可以關聯多個LED同時執行動作,通過trig_list字段進行關聯。


LED設備基本數據結構

struct led_classdev {
	const char		*name; 	// /sys/class/leds/下顯示的設備名字
	int			 brightness;// 當前亮度值,一般爲0~255
	int			 max_brightness;//最大亮度值,一般爲255
	int			 flags;		//取值爲以下

	/* Lower 16 bits reflect status 休眠時設置的標誌位,喚醒則清除*/
#define LED_SUSPENDED		(1 << 0)
	/* Upper 16 bits reflect control information 是否使用休眠喚醒功能*/
#define LED_CORE_SUSPENDRESUME	(1 << 16)

	/* Set LED brightness level 設置LED亮度回調函數指針,函數禁止休眠,可用workqueue代替*/
	/* Must not sleep, use a workqueue if needed */
	void		(*brightness_set)(struct led_classdev *led_cdev,
					  enum led_brightness brightness);
	/* Get LED brightness level 獲取當前LED亮度回調函數指針*/
	enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);

	/*
	 * Activate hardware accelerated blink, delays are in milliseconds
	 * and if both are zero then a sensible default should be chosen.
	 * The call should adjust the timings in that case and if it can't
	 * match the values specified exactly.
	 * Deactivate blinking again when the brightness is set to a fixed
	 * value via the brightness_set() callback.
	 *執行硬件動作的閃爍回調函數指針,如果沒有LED子系統會使用軟定時器1HZ頻率執行brightness_set來閃爍*/
	int		(*blink_set)(struct led_classdev *led_cdev,
				     unsigned long *delay_on,
				     unsigned long *delay_off);

	struct device		*dev;// led設備
	struct list_head	 node;	/* LED Device list 註冊led設備時,靜態全局leds_list的一個結點*/
	/*常用default_trigger有"default-on", "backlight", "gpio", "heartbeat", "ide-disk", "sleep", "timer"*/
	const char		*default_trigger;	/* Trigger to use 默認的trigger類型*/

	unsigned long		 blink_delay_on, blink_delay_off;//blink閃爍時,on和off的時間,單位ms
	struct timer_list	 blink_timer;	//如註冊led設備未提供blink_set函數指針,則使用該定時器blink
	int			 blink_brightness;//執行blink時,on的亮度
//使用trigger機制
#ifdef CONFIG_LEDS_TRIGGERS
	/* Protects the trigger data below */
	struct rw_semaphore	 trigger_lock;//對這個led_cdev執行trigger_set時用的讀寫信號量

	struct led_trigger	*trigger;	//對這個led_cdev通過trigger_set設置的trigger
	struct list_head	 trig_list;	/* trigger->led_cdevs鏈表中的一個節點,一個trigger可控制多個LED*/
	void			*trigger_data;	//一般在trigger的activate和deactivate用,不同情況賦值不同
#endif
}


LED核心文件:

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


led-core.c

DECLARE_RWSEM(leds_list_lock);
EXPORT_SYMBOL_GPL(leds_list_lock);

LIST_HEAD(leds_list);
EXPORT_SYMBOL_GPL(leds_list);

.....

void led_blink_set(struct led_classdev *led_cdev,
		   unsigned long *delay_on,
		   unsigned long *delay_off)
{
	del_timer_sync(&led_cdev->blink_timer);

	if (led_cdev->blink_set &&
	    !led_cdev->blink_set(led_cdev, delay_on, delay_off))
		return;

	/* blink with 1 Hz as default if nothing specified */
	if (!*delay_on && !*delay_off)
		*delay_on = *delay_off = 500;

	led_set_software_blink(led_cdev, *delay_on, *delay_off);
}
EXPORT_SYMBOL(led_blink_set);

void led_brightness_set(struct led_classdev *led_cdev,
			enum led_brightness brightness)
{
	led_stop_software_blink(led_cdev);
	led_cdev->brightness_set(led_cdev, brightness);
}
EXPORT_SYMBOL(led_brightness_set);


對外提供2個全局變量和2個函數。
2個全局變量,DECLARE_RWSEM(leds_list_lock); LIST_HEAD(leds_list);當註冊led_cdev設備時,就會在leds_list中增加一個結點,而對該leds_list的操作需要同步,就是使用讀寫信號量leds_list_lock的down_write,down_read和up_write,up_write來同步的;另外還提供了led_blink_setled_brightness_set兩個函數,分別用於HAL對屬性節點blink和brightness的echo/write操作或者由trigger觸發,這2個函數函數最終調用了你註冊的led_cdev結構體中對應的函數指針,如led_blink_set中當沒有對應的函數指針,就會調用軟定時器以1HZ頻率閃爍,而led_cdev中blink_set必須在註冊的時候提供。


led-class.c

......

/**
 * led_classdev_suspend - suspend an led_classdev.
 * @led_cdev: the led_classdev to suspend.休眠,將對應的LED設爲0
 */
void led_classdev_suspend(struct led_classdev *led_cdev)
{
	led_cdev->flags |= LED_SUSPENDED;
	led_cdev->brightness_set(led_cdev, 0);
}
EXPORT_SYMBOL_GPL(led_classdev_suspend);

/**
 * led_classdev_resume - resume an led_classdev.
 * @led_cdev: the led_classdev to resume.喚醒後,恢復爲原來的亮度值
 */
void led_classdev_resume(struct led_classdev *led_cdev)
{
	led_cdev->brightness_set(led_cdev, led_cdev->brightness);
	led_cdev->flags &= ~LED_SUSPENDED;
}
EXPORT_SYMBOL_GPL(led_classdev_resume);

......

/**
 * led_classdev_register - register a new object of led_classdev class.
 * @parent: The device to register.LED設備註冊,需要提供led_classdev結構體,註冊後會生成設備節點和屬性節點
 * @led_cdev: the led_classdev structure for this device.
 */
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);
	if (IS_ERR(led_cdev->dev))
		return PTR_ERR(led_cdev->dev);

#ifdef CONFIG_LEDS_TRIGGERS
	init_rwsem(&led_cdev->trigger_lock);
#endif
	/* add to the list of leds */
	down_write(&leds_list_lock);
	list_add_tail(&led_cdev->node, &leds_list);
	up_write(&leds_list_lock);

	if (!led_cdev->max_brightness)
		led_cdev->max_brightness = LED_FULL;

	led_update_brightness(led_cdev);

	init_timer(&led_cdev->blink_timer);
	led_cdev->blink_timer.function = led_timer_function;
	led_cdev->blink_timer.data = (unsigned long)led_cdev;

#ifdef CONFIG_LEDS_TRIGGERS
	led_trigger_set_default(led_cdev);
#endif

	printk(KERN_DEBUG "Registered led device: %s\n",
			led_cdev->name);

	return 0;
}
EXPORT_SYMBOL_GPL(led_classdev_register);

/**
 * led_classdev_unregister - unregisters a object of led_properties class.
 * @led_cdev: the led device to unregister
 *
 * Unregisters a previously registered via led_classdev_register object.
 */
void led_classdev_unregister(struct led_classdev *led_cdev)
{
#ifdef CONFIG_LEDS_TRIGGERS
	down_write(&led_cdev->trigger_lock);
	if (led_cdev->trigger)
		led_trigger_set(led_cdev, NULL);
	up_write(&led_cdev->trigger_lock);
#endif

	/* Stop blinking */
	led_brightness_set(led_cdev, LED_OFF);

	device_unregister(led_cdev->dev);

	down_write(&leds_list_lock);
	list_del(&led_cdev->node);
	up_write(&leds_list_lock);
}
EXPORT_SYMBOL_GPL(led_classdev_unregister);

static int __init leds_init(void)
{/* 在/sys/class下產生leds類設備文件夾,所有此類下的設備都有led_class_attrs指定的屬性節點和一致的休眠喚醒動作 */
	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);
}

subsys_initcall(leds_init);
module_exit(leds_exit);

此文件會在/sys/class下產生leds類設備文件夾,並提供led設備的註冊、休眠喚醒函數。

對外提供休眠喚醒函數led_classdev_suspend和led_classdev_resume,led設備註冊函數led_classdev_register。註冊之後的led_cdev設備都具有屬性brightness、blink、max_brightness,trigger屬性可選。

led-trigger.c

led_trigger的結構體如下:

struct led_trigger {
	/* Trigger Properties */
	const char	 *name;	//trigger的name
	void		(*activate)(struct led_classdev *led_cdev);//激活該trigger所掛載的所有LED的打開動作
	void		(*deactivate)(struct led_classdev *led_cdev);//關閉所有LED動作

	/* LEDs under control by this trigger (for simple triggers) */
	rwlock_t	  leddev_list_lock;//對trigger下的LED進行操作是的讀寫信號量
	struct list_head  led_cdevs;	//該trigger下關聯的所有LED設備鏈表

	/* Link to next registered trigger */
	struct list_head  next_trig;	//全局trigger_list鏈表,指向下一類trigger
};


當你開啓CONFIG_LEDS_TRIGGERS選項時,則使用相對高級點的trigger特性。而trigger一般常用有"default-on", "backlight", "gpio", "heartbeat", "ide-disk", "sleep", "timer"。提供trigger屬性的write/read的操作函數,led_cdev設備設置trigger類型接口led_trigger_set,設置默認的trigger函數led_trigger_set_default,還有trigger註冊函數。當你使用某個trigger時,你的LED就會按照那種方式進行activate。


led-trigger.c主要提供trigger的註冊led_trigger_register,led-class.c要使用的trigger屬性的echo和show的函數,還有給led設置trigger的函數led_trigger_set。

led-trigger.c還提供simple版本的trigger使用:

simple trigger註冊後無任何動作,只是添加到trig_list中,驅動中必須調用led_trigger_event才能執行與此trigger關聯的所有LED設備的亮滅動作,必須調用led_trigger_blink才能執行與此trigger關聯的所有LED的閃爍動作。simple版本的trigger代碼接口片如下:

<span style="font-size:14px;">/* Simple LED Tigger Interface */

void led_trigger_event(struct led_trigger *trigger,
			enum led_brightness brightness)
{
	struct list_head *entry;

	if (!trigger)
		return;

	read_lock(&trigger->leddev_list_lock);
	list_for_each(entry, &trigger->led_cdevs) {
		struct led_classdev *led_cdev;

		led_cdev = list_entry(entry, struct led_classdev, trig_list);
		led_set_brightness(led_cdev, brightness);
	}
	read_unlock(&trigger->leddev_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_event);

void led_trigger_blink(struct led_trigger *trigger,
		       unsigned long *delay_on,
		       unsigned long *delay_off)
{
	struct list_head *entry;

	if (!trigger)
		return;

	read_lock(&trigger->leddev_list_lock);
	list_for_each(entry, &trigger->led_cdevs) {
		struct led_classdev *led_cdev;

		led_cdev = list_entry(entry, struct led_classdev, trig_list);
		led_blink_set(led_cdev, delay_on, delay_off);
	}
	read_unlock(&trigger->leddev_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_blink);

void led_trigger_register_simple(const char *name, struct led_trigger **tp)
{
	struct led_trigger *trigger;
	int err;

	trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);

	if (trigger) {
		trigger->name = name;
		err = led_trigger_register(trigger);
		if (err < 0) {
			kfree(trigger);
			trigger = NULL;
			printk(KERN_WARNING "LED trigger %s failed to register"
				" (%d)\n", name, err);
		}
	} else
		printk(KERN_WARNING "LED trigger %s failed to register"
			" (no memory)\n", name);

	*tp = trigger;
}
EXPORT_SYMBOL_GPL(led_trigger_register_simple);</span>



常用trigger說明:

什麼時候才叫激活?!

即activate,它的反動作叫deactivate,激活的2種方式:在led_cdev在註冊時,led_cdev->default_trigger存在且與已註冊的trigger_list中有匹配,就會在註冊後立即activate,或者在user空間,對該led_cdev的trigger屬性執行echo/write且與已註冊的trigger_list中有匹配,也會立即執行該trigger中的activate。

其實trigger的類型,並不侷限與上面提到的常用幾個,你自己也可以自己定義註冊。

"default-on"

static void defon_trig_activate(struct led_classdev *led_cdev)
{
	led_set_brightness(led_cdev, led_cdev->max_brightness);
}

static struct led_trigger defon_led_trigger = {
	.name     = "default-on",
	.activate = defon_trig_activate,
};

功能就是激活時,以最大亮度常亮。

"backlight"

#define BLANK		1
#define UNBLANK		0

struct bl_trig_notifier {
	struct led_classdev *led;
	int brightness;
	int old_status;
	struct notifier_block notifier;
	unsigned invert;
};

static int fb_notifier_callback(struct notifier_block *p,
				unsigned long event, void *data)
{
	struct bl_trig_notifier *n = container_of(p,
					struct bl_trig_notifier, notifier);
	struct led_classdev *led = n->led;
	struct fb_event *fb_event = data;
	int *blank = fb_event->data;
	int new_status = *blank ? BLANK : UNBLANK;

	switch (event) {
	case FB_EVENT_BLANK :
		if (new_status == n->old_status)
			break;

		if ((n->old_status == UNBLANK) ^ n->invert) {
			n->brightness = led->brightness;
			led_set_brightness(led, LED_OFF);
		} else {
			led_set_brightness(led, n->brightness);
		}

		n->old_status = new_status;

		break;
	}

	return 0;
}

static ssize_t bl_trig_invert_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct led_classdev *led = dev_get_drvdata(dev);
	struct bl_trig_notifier *n = led->trigger_data;

	return sprintf(buf, "%u\n", n->invert);
}

static ssize_t bl_trig_invert_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t num)
{
	struct led_classdev *led = dev_get_drvdata(dev);
	struct bl_trig_notifier *n = led->trigger_data;
	unsigned long invert;
	int ret;

	ret = strict_strtoul(buf, 10, &invert);
	if (ret < 0)
		return ret;

	if (invert > 1)
		return -EINVAL;

	n->invert = invert;

	/* After inverting, we need to update the LED. */
	if ((n->old_status == BLANK) ^ n->invert)
		led_set_brightness(led, LED_OFF);
	else
		led_set_brightness(led, n->brightness);

	return num;
}
static DEVICE_ATTR(inverted, 0644, bl_trig_invert_show, bl_trig_invert_store);

static void bl_trig_activate(struct led_classdev *led)
{
	int ret;

	struct bl_trig_notifier *n;

	n = kzalloc(sizeof(struct bl_trig_notifier), GFP_KERNEL);
	led->trigger_data = n;
	if (!n) {
		dev_err(led->dev, "unable to allocate backlight trigger\n");
		return;
	}

	ret = device_create_file(led->dev, &dev_attr_inverted);
	if (ret)
		goto err_invert;

	n->led = led;
	n->brightness = led->brightness;
	n->old_status = UNBLANK;
	n->notifier.notifier_call = fb_notifier_callback;

	ret = fb_register_client(&n->notifier);
	if (ret)
		dev_err(led->dev, "unable to register backlight trigger\n");

	return;

err_invert:
	led->trigger_data = NULL;
	kfree(n);
}

static void bl_trig_deactivate(struct led_classdev *led)
{
	struct bl_trig_notifier *n =
		(struct bl_trig_notifier *) led->trigger_data;

	if (n) {
		device_remove_file(led->dev, &dev_attr_inverted);
		fb_unregister_client(&n->notifier);
		kfree(n);
	}
}

static struct led_trigger bl_led_trigger = {
	.name		= "backlight",
	.activate	= bl_trig_activate,
	.deactivate	= bl_trig_deactivate
};

從代碼可以看出功能是:當前led_cdev下建立invert屬性結點(當用戶空間進行write 1 時,背光的點亮邏輯翻轉),並註冊fb_notifier,這樣當framebuff的BLANK和UNBLANK動作都會來執行回調函數,以達到背光的調整,當然最終執行的背光亮度調試函數是,led_cdev註冊時其中的led_cdev->brightness_set執行的。


"heartbeat"

struct heartbeat_trig_data {
	unsigned int phase;
	unsigned int period;
	struct timer_list timer;
};

static void led_heartbeat_function(unsigned long data)
{
	struct led_classdev *led_cdev = (struct led_classdev *) data;
	struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
	unsigned long brightness = LED_OFF;
	unsigned long delay = 0;

	/* acts like an actual heart beat -- ie thump-thump-pause... */
	switch (heartbeat_data->phase) {
	case 0:
		/*
		 * The hyperbolic function below modifies the
		 * heartbeat period length in dependency of the
		 * current (1min) load. It goes through the points
		 * f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
		 */
		heartbeat_data->period = 300 +
			(6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
		heartbeat_data->period =
			msecs_to_jiffies(heartbeat_data->period);
		delay = msecs_to_jiffies(70);
		heartbeat_data->phase++;
		brightness = led_cdev->max_brightness;
		break;
	case 1:
		delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
		heartbeat_data->phase++;
		break;
	case 2:
		delay = msecs_to_jiffies(70);
		heartbeat_data->phase++;
		brightness = led_cdev->max_brightness;
		break;
	default:
		delay = heartbeat_data->period - heartbeat_data->period / 4 -
			msecs_to_jiffies(70);
		heartbeat_data->phase = 0;
		break;
	}

	led_set_brightness(led_cdev, brightness);
	mod_timer(&heartbeat_data->timer, jiffies + delay);
}

static void heartbeat_trig_activate(struct led_classdev *led_cdev)
{
	struct heartbeat_trig_data *heartbeat_data;

	heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
	if (!heartbeat_data)
		return;

	led_cdev->trigger_data = heartbeat_data;
	setup_timer(&heartbeat_data->timer,
		    led_heartbeat_function, (unsigned long) led_cdev);
	heartbeat_data->phase = 0;
	led_heartbeat_function(heartbeat_data->timer.data);
}

static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
{
	struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;

	if (heartbeat_data) {
		del_timer_sync(&heartbeat_data->timer);
		kfree(heartbeat_data);
	}
}

static struct led_trigger heartbeat_led_trigger = {
	.name     = "heartbeat",
	.activate = heartbeat_trig_activate,
	.deactivate = heartbeat_trig_deactivate,
};

功能是:激活時,設置個軟定時器,執行心跳式亮滅動作。


"ide-disk"

static void ledtrig_ide_timerfunc(unsigned long data);

DEFINE_LED_TRIGGER(ledtrig_ide);
static DEFINE_TIMER(ledtrig_ide_timer, ledtrig_ide_timerfunc, 0, 0);
static int ide_activity;
static int ide_lastactivity;

void ledtrig_ide_activity(void)
{
	ide_activity++;
	if (!timer_pending(&ledtrig_ide_timer))
		mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
}
EXPORT_SYMBOL(ledtrig_ide_activity);

static void ledtrig_ide_timerfunc(unsigned long data)
{
	if (ide_lastactivity != ide_activity) {
		ide_lastactivity = ide_activity;
		/* INT_MAX will set each LED to its maximum brightness */
		led_trigger_event(ledtrig_ide, INT_MAX);
		mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
	} else {
		led_trigger_event(ledtrig_ide, LED_OFF);
	}
}

static int __init ledtrig_ide_init(void)
{
	led_trigger_register_simple("ide-disk", &ledtrig_ide);
	return 0;
}

static void __exit ledtrig_ide_exit(void)
{
	led_trigger_unregister_simple(ledtrig_ide);
}

module_init(ledtrig_ide_init);
module_exit(ledtrig_ide_exit);
功能是:註冊後,關聯的LED沒有任何反應,其他模塊的外界函數調用下ledtrig_ide_activity( )執行一個週期動作,啓動軟定時器,LED就亮10ms,滅10ms,一般用於硬盤燈。

"timer"

static ssize_t led_delay_on_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct led_classdev *led_cdev = dev_get_drvdata(dev);

	return sprintf(buf, "%lu\n", led_cdev->blink_delay_on);
}

static ssize_t led_delay_on_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
	int ret = -EINVAL;
	char *after;
	unsigned long state = simple_strtoul(buf, &after, 10);
	size_t count = after - buf;

	if (isspace(*after))
		count++;

	if (count == size) {
		led_blink_set(led_cdev, &state, &led_cdev->blink_delay_off);
		led_cdev->blink_delay_on = state;
		ret = count;
	}

	return ret;
}

static ssize_t led_delay_off_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct led_classdev *led_cdev = dev_get_drvdata(dev);

	return sprintf(buf, "%lu\n", led_cdev->blink_delay_off);
}

static ssize_t led_delay_off_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
	int ret = -EINVAL;
	char *after;
	unsigned long state = simple_strtoul(buf, &after, 10);
	size_t count = after - buf;

	if (isspace(*after))
		count++;

	if (count == size) {
		led_blink_set(led_cdev, &led_cdev->blink_delay_on, &state);
		led_cdev->blink_delay_off = state;
		ret = count;
	}

	return ret;
}

static DEVICE_ATTR(delay_on, 0644, led_delay_on_show, led_delay_on_store);
static DEVICE_ATTR(delay_off, 0644, led_delay_off_show, led_delay_off_store);

static void timer_trig_activate(struct led_classdev *led_cdev)
{
	int rc;

	led_cdev->trigger_data = NULL;

	rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
	if (rc)
		return;
	rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
	if (rc)
		goto err_out_delayon;

	led_blink_set(led_cdev, &led_cdev->blink_delay_on,
		      &led_cdev->blink_delay_off);

	led_cdev->trigger_data = (void *)1;

	return;

err_out_delayon:
	device_remove_file(led_cdev->dev, &dev_attr_delay_on);
}

static void timer_trig_deactivate(struct led_classdev *led_cdev)
{
	if (led_cdev->trigger_data) {
		device_remove_file(led_cdev->dev, &dev_attr_delay_on);
		device_remove_file(led_cdev->dev, &dev_attr_delay_off);
	}

	/* Stop blinking */
	led_brightness_set(led_cdev, LED_OFF);
}

static struct led_trigger timer_led_trigger = {
	.name     = "timer",
	.activate = timer_trig_activate,
	.deactivate = timer_trig_deactivate,
};


功能是:關聯的LED設備下增加2個屬性結點delay_on和delay_off,應用層對這些個結點的read/write會,修改led_cdev->blink_delay_on和led_cdev->blink_delay_off的值,註冊該trigger後,關聯的LED立即閃爍,由blink_delay_on/off確定閃爍的頻率。


"sleep"

static int ledtrig_sleep_pm_callback(struct notifier_block *nfb,
					unsigned long action,
					void *ignored);

DEFINE_LED_TRIGGER(ledtrig_sleep)
static struct notifier_block ledtrig_sleep_pm_notifier = {
	.notifier_call = ledtrig_sleep_pm_callback,
	.priority = 0,
};

static void ledtrig_sleep_early_suspend(struct early_suspend *h)
{
	led_trigger_event(ledtrig_sleep, LED_FULL);
}

static void ledtrig_sleep_early_resume(struct early_suspend *h)
{
	led_trigger_event(ledtrig_sleep, LED_OFF);
}

static struct early_suspend ledtrig_sleep_early_suspend_handler = {
	.suspend = ledtrig_sleep_early_suspend,
	.resume = ledtrig_sleep_early_resume,
};

static int ledtrig_sleep_pm_callback(struct notifier_block *nfb,
					unsigned long action,
					void *ignored)
{
	switch (action) {
	case PM_HIBERNATION_PREPARE:
	case PM_SUSPEND_PREPARE:
		led_trigger_event(ledtrig_sleep, LED_OFF);
		return NOTIFY_OK;
	case PM_POST_HIBERNATION:
	case PM_POST_SUSPEND:
		led_trigger_event(ledtrig_sleep, LED_FULL);
		return NOTIFY_OK;
	}

	return NOTIFY_DONE;
}

static int __init ledtrig_sleep_init(void)
{
	led_trigger_register_simple("sleep", &ledtrig_sleep);
	register_pm_notifier(&ledtrig_sleep_pm_notifier);
	register_early_suspend(&ledtrig_sleep_early_suspend_handler);
	return 0;
}

功能是:這個功能幾乎看不到有人用。註冊該trigger後,關聯的LED不會有任何反應,只是在pm_notifier和early_suspend機制中執行,即going to hibernate/suspend時關閉關聯的LED,在hibernate/suspend finished時最大亮度點亮關聯的LED;early_suspend時最大亮度點亮,early_resume時關閉LED。


"gpio"


功能是:由外部gpio中斷觸發LED亮滅動作(比如按下亮,彈起滅)。該trigger在LED設備下增加gpio,inverted,desired_brightness三個屬性結點,gpio屬性須由應用層寫入來觸發LED的gpio(申請gpio上下降沿中斷及其中斷處理函數),inverted屬性修改可翻轉LED亮滅邏輯,desired_brightness爲亮的時候亮的強度。每當中斷髮生或者修改desired_brightness時,就發生LED的亮滅。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章