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
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_set和led_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的亮灭。