linux驱动____LED子系统笔记

16 篇文章 2 订阅
2 篇文章 0 订阅

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


  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值