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的亮滅。