android button light 流程分析(一) — driver

一、led-class

led-class是button light driver的核心层,代码位于drivers/leds/目录下,提供了一些数据结构和接口,其中创建了一个leds设备类,用于管理系统所有的led,代码如下:

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;
	return 0;
}

static void __exit leds_exit(void)
{
	class_destroy(leds_class);
}

subsys_initcall(leds_init);
module_exit(leds_exit);
创建成功后我们可以在/sys/class目录下面看到leds目录,并注册了统一的休眠和唤醒函数:

/**
 * led_classdev_suspend - suspend an led_classdev.
 * @led_cdev: the led_classdev to suspend.
 */
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);

static int led_suspend(struct device *dev, pm_message_t state)
{
	struct led_classdev *led_cdev = dev_get_drvdata(dev);

	if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
		led_classdev_suspend(led_cdev);

	return 0;
}

static int led_resume(struct device *dev)
{
	struct led_classdev *led_cdev = dev_get_drvdata(dev);

	if (led_cdev->flags & LED_CORE_SUSPENDRESUME)
		led_classdev_resume(led_cdev);

	return 0;
}
suspend主要设置一下标志并将led关闭,resume主要清除标志并将led恢复之前亮度。同时led-class提供了结构体led_classdev作为统一的led设备模型:

struct led_classdev {
	const char		*name;
	int			 brightness;
	int			 max_brightness;
	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 */
	/* Must not sleep, use a workqueue if needed */
	void		(*brightness_set)(struct led_classdev *led_cdev,
					  enum led_brightness brightness);
	/* Get LED brightness level */
	enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);

	/* Activate hardware accelerated blink, delays are in
	 * miliseconds and if none is provided then a sensible default
	 * should be chosen. The call can adjust the timings if it can't
	 * match the values specified exactly. */
	int		(*blink_set)(struct led_classdev *led_cdev,
				     unsigned long *delay_on,
				     unsigned long *delay_off);

	struct device		*dev;
	struct list_head	 node;			/* LED Device list */
	const char		*default_trigger;	/* Trigger to use */

#ifdef CONFIG_LEDS_TRIGGERS
	/* Protects the trigger data below */
	struct rw_semaphore	 trigger_lock;

	struct led_trigger	*trigger;
	struct list_head	 trig_list;
	void			*trigger_data;
#endif
};

其中提供了设置亮度、获取亮度、闪烁led几个接口、还有trigger域以及相关链表节点。在led-class中还定义了brightness、max_brightness两个属性提供给HAL层使用:

static void led_update_brightness(struct led_classdev *led_cdev)
{
	if (led_cdev->brightness_get)
		led_cdev->brightness = led_cdev->brightness_get(led_cdev);
}

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

	/* no lock needed for this */
	led_update_brightness(led_cdev);

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

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

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

	if (count == size) {
		ret = count;

		if (state == LED_OFF)
			led_trigger_remove(led_cdev);
		led_set_brightness(led_cdev, state);
	}

	return ret;
}

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

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

static DEVICE_ATTR(brightness, 0644, led_brightness_show, led_brightness_store);
static DEVICE_ATTR(max_brightness, 0444, led_max_brightness_show, NULL);
其中brightness主要用于设置和读取亮度值,max_brightness则用于读取硬件支持的最大亮度值。led-class还提供了注册led_classdev的统一接口:

/**
 * led_classdev_register - register a new object of led_classdev class.
 * @parent: The device to register.
 * @led_cdev: the led_classdev structure for this device.
 */
int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
{
	int rc;

	led_cdev->dev = device_create(leds_class, parent, 0, led_cdev,
				      "%s", led_cdev->name);  // 在leds_class下创建设备
	if (IS_ERR(led_cdev->dev))
		return PTR_ERR(led_cdev->dev);

	/* 创建brightness属性文件 */
	rc = device_create_file(led_cdev->dev, &dev_attr_brightness);
	if (rc)
		goto err_out;

#ifdef CONFIG_LEDS_TRIGGERS
	init_rwsem(&led_cdev->trigger_lock);
#endif
	/* 将该设备添加到链表leds_list */
	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;

	rc = device_create_file(led_cdev->dev, &dev_attr_max_brightness);
	if (rc)
		goto err_out_attr_max;

	led_update_brightness(led_cdev);  // 更新亮度值

#ifdef CONFIG_LEDS_TRIGGERS
	rc = device_create_file(led_cdev->dev, &dev_attr_trigger);
	if (rc)
		goto err_out_led_list;

	led_trigger_set_default(led_cdev);
#endif

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

	return 0;

#ifdef CONFIG_LEDS_TRIGGERS
err_out_led_list:
	device_remove_file(led_cdev->dev, &dev_attr_max_brightness);
#endif
err_out_attr_max:
	device_remove_file(led_cdev->dev, &dev_attr_brightness);
	list_del(&led_cdev->node);
err_out:
	device_unregister(led_cdev->dev);
	return rc;
}
EXPORT_SYMBOL_GPL(led_classdev_register);
二、led-driver
在平台的驱动中我们注册一个platform led设备作为所有led设备的父设备,注册代码如下:

static struct platform_driver atxxled_driver = {
	.probe      = atxxled_probe,
	.remove     = atxxled_remove,
	.driver     = {
		.name       = "led",
		.owner      = THIS_MODULE,
	},
};

static int __init atxxled_init(void)
{
	return platform_driver_register(&atxxled_driver);
}

static void __exit atxxled_exit(void)
{
	platform_driver_unregister(&atxxled_driver);
}

module_init(atxxled_init);
module_exit(atxxled_exit);
然后再分别注册每一个led:

static void atxxled_set(struct led_classdev *led_cdev, enum ATXX_LED led,
                   enum led_brightness value)
{
	int is_on = (value == LED_OFF) ? 0 : 1;
	enum pmu_led_module led_module;
	switch (led) { 
	case RED_LED:
		led_module = PMU_LED_MODULE_2;
		break;
	case GREEN_LED:
		led_module = PMU_LED_MODULE_1;
		break;
	default:
		return;
		break;
	}
	pmu_led_on_off_set(led_module, is_on, 0);  // led电源控制接口
}

static void atxxled_green_set(struct led_classdev *led_cdev,
                   enum led_brightness value)
{
	atxxled_set(led_cdev, GREEN_LED, value);
}

static void atxxled_red_set(struct led_classdev *led_cdev,
                 enum led_brightness value)
{
	atxxled_set(led_cdev, RED_LED, value);
}

static void atxxled_buttons_set(struct led_classdev *led_cdev,
                 enum led_brightness value)
{
	enum power_supply_mode is_on = (value == LED_OFF) ? PPS_OFF : PPS_ON;
	pmu_led_on_off_set(PMU_LED_KEY_1, is_on, 40);
}

static int atxxled_blink_set(struct led_classdev *led_cdev, enum ATXX_LED led,
            unsigned long *delay_on, unsigned long *delay_off)
{
	enum pmu_led_module led_module;
	switch (led) { 
	case RED_LED:
		led_module = PMU_LED_MODULE_2;
		break;
	case GREEN_LED:
		led_module = PMU_LED_MODULE_1;
		break;
	default:
		return 0;
		break;
	}
	pmu_led_blink_set(led_module, delay_on, delay_off);  // led闪烁控制接口
	return 0;
}

static int atxxled_green_blink_set(struct led_classdev *led_cdev,
            unsigned long *delay_on, unsigned long *delay_off)
{
	return atxxled_blink_set(led_cdev, GREEN_LED, delay_on, delay_off);
}

static int atxxled_red_blink_set(struct led_classdev *led_cdev,
            unsigned long *delay_on, unsigned long *delay_off)
{
	return atxxled_blink_set(led_cdev, RED_LED, delay_on, delay_off);
}

static int atxxled_buttons_blink_set(struct led_classdev *led_cdev,
            unsigned long *delay_on, unsigned long *delay_off)
{
	return -1;
}

static struct led_classdev atxx_red_led = {
	.name           = "red",
	.brightness_set = atxxled_red_set,
	.blink_set      = atxxled_red_blink_set,
	.flags          = 0,
};

static struct led_classdev atxx_green_led = {
	.name           = "green",
	.brightness_set = atxxled_green_set,
	.blink_set      = atxxled_green_blink_set,
	.flags          = 0,
};

static struct led_classdev atxx_buttons_led = {
	.name           = "button-backlight",
	.brightness_set = atxxled_buttons_set,  // led亮度调节
	.blink_set      = atxxled_buttons_blink_set,  // led闪烁
	.flags          = 0,
};

static int atxxled_probe(struct platform_device *pdev)
{
	int ret;

	ret = led_classdev_register(&pdev->dev, &atxx_buttons_led);
	if (ret < 0)
		return ret;

	ret = led_classdev_register(&pdev->dev, &atxx_red_led);
	if (ret < 0)
		return ret;
	
	ret = led_classdev_register(&pdev->dev, &atxx_green_led);
	if (ret < 0)
		led_classdev_unregister(&atxx_red_led);

	return ret;
}

三、led-trigger

trigger直译为触发器,即用户有操作时就触发led点亮或者熄灭,通过输入命令:

cat /sys/class/leds/button-backlight/trigger

可以得到系统已经注册的trigger

none nand-disk ac-online USB-online Battery-charging-or-full Battery-charging Battery-full mmc0 mmc1 [timer] rfkill0

用户空间可以直接写值到trigger文件改变led的触发方式。trigger的数据结构如下:

struct led_trigger {
	/* Trigger Properties */
	const char	 *name;
	void		(*activate)(struct led_classdev *led_cdev);
	void		(*deactivate)(struct led_classdev *led_cdev);

	/* LEDs under control by this trigger (for simple triggers) */
	rwlock_t	  leddev_list_lock;
	struct list_head  led_cdevs;

	/* Link to next registered trigger */
	struct list_head  next_trig;
};

分别为trigger名称、激活以及分离接口、读写锁、led设备链表头、trigger_list链表节点。下面是led-trigger核心部分源码的分析:

static DECLARE_RWSEM(triggers_list_lock);  // trigger链表读写锁
static LIST_HEAD(trigger_list);  // trigger链表

// 用户空间写操作函数
ssize_t led_trigger_store(struct device *dev, struct device_attribute *attr,
		const char *buf, size_t count)
{
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
	char trigger_name[TRIG_NAME_MAX];
	struct led_trigger *trig;
	size_t len;
	// 获取trigger_name的有效长度
	trigger_name[sizeof(trigger_name) - 1] = '\0';
	strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
	len = strlen(trigger_name);

	if (len && trigger_name[len - 1] == '\n')
		trigger_name[len - 1] = '\0';
	// 比较是否为none,如果是则将led_cdev->trigger清除
	if (!strcmp(trigger_name, "none")) {
		led_trigger_remove(led_cdev);
		return count;
	}

	down_read(&triggers_list_lock);
	// 遍历trigger_list,如果本trigger在trigger_list中有注册
	// 则调用led_trigger_set()将本设备添加到trigger的设备链表中
	list_for_each_entry(trig, &trigger_list, next_trig) {
		if (!strcmp(trigger_name, trig->name)) {
			down_write(&led_cdev->trigger_lock);
			led_trigger_set(led_cdev, trig);
			up_write(&led_cdev->trigger_lock);

			up_read(&triggers_list_lock);
			return count;
		}
	}
	up_read(&triggers_list_lock);

	return -EINVAL;
}
EXPORT_SYMBOL_GPL(led_trigger_store);
// 用户空间读操作函数
ssize_t led_trigger_show(struct device *dev, struct device_attribute *attr,
		char *buf)
{
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
	struct led_trigger *trig;
	int len = 0;

	down_read(&triggers_list_lock);
	down_read(&led_cdev->trigger_lock);
	// 如果本设备不存在trigger,则显示[none]
	if (!led_cdev->trigger)
		len += sprintf(buf+len, "[none] ");
	else
		len += sprintf(buf+len, "none ");
	// 遍历trigger_list,如果本设备当前trigger在链表中存在则用"[xxx]"显示
	// 如果不存在则用"xxx"显示链表中其余的trigger
	list_for_each_entry(trig, &trigger_list, next_trig) {
		if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
							trig->name))
			len += sprintf(buf+len, "[%s] ", trig->name);
		else
			len += sprintf(buf+len, "%s ", trig->name);
	}
	up_read(&led_cdev->trigger_lock);
	up_read(&triggers_list_lock);

	len += sprintf(len+buf, "\n");
	return len;
}
EXPORT_SYMBOL_GPL(led_trigger_show);

// trigger设置函数,调用者需要持有led_cdev->trigger_lock锁
void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
{
	unsigned long flags;

	// 如果当前设备存在trigger,则清除,保证一个设备只有一个trigger
	if (led_cdev->trigger) {
		write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
		list_del(&led_cdev->trig_list);  // 将当前设备从旧trigger的设备链表中删除
		write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock,
			flags);
		if (led_cdev->trigger->deactivate)
			led_cdev->trigger->deactivate(led_cdev);  // 执行旧trigger分离函数
		led_cdev->trigger = NULL;  // 将当前设备的trigger域清空
		led_set_brightness(led_cdev, LED_OFF);  // 关闭设备
	}
	// 为设备添加一个新trigger
	if (trigger) {
		write_lock_irqsave(&trigger->leddev_list_lock, flags);
		// 将当前设备添加到新trigger的设备链表中
		list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
		write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
		// 设置设备的trigger域并执行新trigger的激活函数
		led_cdev->trigger = trigger;
		if (trigger->activate)
			trigger->activate(led_cdev);
	}
}
EXPORT_SYMBOL_GPL(led_trigger_set);
// trigger移除函数
void led_trigger_remove(struct led_classdev *led_cdev)
{
	down_write(&led_cdev->trigger_lock);
	led_trigger_set(led_cdev, NULL);
	up_write(&led_cdev->trigger_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_remove);
// 设置设备的默认trigger
void led_trigger_set_default(struct led_classdev *led_cdev)
{
	struct led_trigger *trig;

	if (!led_cdev->default_trigger)
		return;

	down_read(&triggers_list_lock);
	down_write(&led_cdev->trigger_lock);
	// 如果设备默认的trigger在trigger_list有注册,则设置trigger
	list_for_each_entry(trig, &trigger_list, next_trig) {
		if (!strcmp(led_cdev->default_trigger, trig->name))
			led_trigger_set(led_cdev, trig);
	}
	up_write(&led_cdev->trigger_lock);
	up_read(&triggers_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_set_default);

// 注册一个新的trigger到trigger_list中
int led_trigger_register(struct led_trigger *trigger)
{
	struct led_classdev *led_cdev;
	struct led_trigger *trig;

	rwlock_init(&trigger->leddev_list_lock);  // 初始化读写锁
	INIT_LIST_HEAD(&trigger->led_cdevs);  // 初始化设备链表

	down_write(&triggers_list_lock);
	// 确认trigger是否注册过
	list_for_each_entry(trig, &trigger_list, next_trig) {
		if (!strcmp(trig->name, trigger->name)) {
			up_write(&triggers_list_lock);
			return -EEXIST;
		}
	}
	// 将trigger添加到trigger_list中
	list_add_tail(&trigger->next_trig, &trigger_list);
	up_write(&triggers_list_lock);

	// 遍历当前已经注册的led,如果led的默认trigger是当前注册的trigger
	// 则调用led_trigger_set()将led添加到trigger的设备链表中
	down_read(&leds_list_lock);
	list_for_each_entry(led_cdev, &leds_list, node) {
		down_write(&led_cdev->trigger_lock);
		if (!led_cdev->trigger && led_cdev->default_trigger &&
			    !strcmp(led_cdev->default_trigger, trigger->name))
			led_trigger_set(led_cdev, trigger);
		up_write(&led_cdev->trigger_lock);
	}
	up_read(&leds_list_lock);

	return 0;
}
EXPORT_SYMBOL_GPL(led_trigger_register);
// 注销trigger
void led_trigger_unregister(struct led_trigger *trigger)
{
	struct led_classdev *led_cdev;

	// 将trigger从trigger_list中删除
	down_write(&triggers_list_lock);
	list_del(&trigger->next_trig);
	up_write(&triggers_list_lock);

	// 遍历当前已经注册的led,如果led使用的trigger是当前要删除的trigger
	// 则调用led_trigger_set()将led的trigger域清空
	down_read(&leds_list_lock);
	list_for_each_entry(led_cdev, &leds_list, node) {
		down_write(&led_cdev->trigger_lock);
		if (led_cdev->trigger == trigger)
			led_trigger_set(led_cdev, NULL);
		up_write(&led_cdev->trigger_lock);
	}
	up_read(&leds_list_lock);
}
EXPORT_SYMBOL_GPL(led_trigger_unregister);

// 统一设置本trigger下所有led的亮度
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);
	// 遍历当前trigger的设备链表,并设置每个设备的亮度
	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);
// 简单注册一个新的trigger
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)
			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);
// 简单注销一个trigger
void led_trigger_unregister_simple(struct led_trigger *trigger)
{
	if (trigger)
		led_trigger_unregister(trigger);
	kfree(trigger);
}
EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);

核心层主要维护了trigger_list链表,保存了系统中注册的所有trigger;每个trigger都有自己的led设备链表,用于统一控制该trigger下所有led的亮度;同时每个led设备都有自己的trigger域,用于指向相应的trigger,如果trigger被触发则会调用相应trigger的激活函数。

四、ledtrig-timer
ledtrig-timer作为timer触发器,用于控制led闪烁。led-trigtimer通过led-trigger核心层调用会在/sys/class/leds/button-backlight创建delay_on、delay_off属性文件来控制闪烁时间。代码分析如下:

struct timer_trig_data {
	int brightness_on;		/* LED brightness during "on" period.
					 * (LED_OFF < brightness_on <= LED_FULL)
					 */
	unsigned long delay_on;		/* milliseconds on */
	unsigned long delay_off;	/* milliseconds off */
	struct timer_list timer;
};

static void led_timer_function(unsigned long data)
{
	struct led_classdev *led_cdev = (struct led_classdev *) data;
	struct timer_trig_data *timer_data = led_cdev->trigger_data;
	unsigned long brightness;
	unsigned long delay;
	// 如果delay_on或者delay_off为0则关闭led
	if (!timer_data->delay_on || !timer_data->delay_off) {
		led_set_brightness(led_cdev, LED_OFF);
		return;
	}
	// 获取当前亮度值
	brightness = led_get_brightness(led_cdev);
	if (!brightness) {
		// 如果当前led为关闭状态则设置led到默认亮度值
		brightness = timer_data->brightness_on;
		delay = timer_data->delay_on;
	} else {
		// 如果当前led为开启状态则关闭led并保存当前亮度值
		timer_data->brightness_on = brightness;
		brightness = LED_OFF;
		delay = timer_data->delay_off;
	}
	// 设置亮度值,因为此时在soft irq中,所以不能调用耗时函数
	led_set_brightness(led_cdev, brightness);
	// 在delay后更新led状态,实现闪烁
	mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay));
}
// 显示delay_on参数
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);
	struct timer_trig_data *timer_data = led_cdev->trigger_data;

	return sprintf(buf, "%lu\n", timer_data->delay_on);
}
// 存储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);
	struct timer_trig_data *timer_data = led_cdev->trigger_data;
	int ret = -EINVAL;
	char *after;
	unsigned long state = simple_strtoul(buf, &after, 10);
	size_t count = after - buf;

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

	if (count == size) {
		if (timer_data->delay_on != state) {
			// 如果delay_on值不同则保存新值
			timer_data->delay_on = state;

			// 删除之前的timer
			del_timer_sync(&timer_data->timer);

			// 尝试调用blink_set()设置led
			if (!led_cdev->blink_set ||
			    led_cdev->blink_set(led_cdev,
			      &timer_data->delay_on, &timer_data->delay_off)) {
				// 如果没有或者设置失败就调用timer设置led
				mod_timer(&timer_data->timer, jiffies + 1);
			}
		}
		ret = count;
	}

	return ret;
}
// 显示delay_off参数
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);
	struct timer_trig_data *timer_data = led_cdev->trigger_data;

	return sprintf(buf, "%lu\n", timer_data->delay_off);
}
// 存储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);
	struct timer_trig_data *timer_data = led_cdev->trigger_data;
	int ret = -EINVAL;
	char *after;
	unsigned long state = simple_strtoul(buf, &after, 10);
	size_t count = after - buf;

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

	if (count == size) {
		if (timer_data->delay_off != state) {
			/* the new value differs from the previous */
			timer_data->delay_off = state;

			/* deactivate previous settings */
			del_timer_sync(&timer_data->timer);

			/* try to activate hardware acceleration, if any */
			if (!led_cdev->blink_set ||
			    led_cdev->blink_set(led_cdev,
			      &timer_data->delay_on, &timer_data->delay_off)) {
				/* no hardware acceleration, blink via timer */
				mod_timer(&timer_data->timer, jiffies + 1);
			}
		}
		ret = count;
	}

	return ret;
}
// 属性文件delay_on,delay_off
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);
// timer_led_trigger的激活函数
static void timer_trig_activate(struct led_classdev *led_cdev)
{
	struct timer_trig_data *timer_data;
	int rc;
	// 申请timer_trig_data的内存空间
	timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL);
	if (!timer_data)
		return;
	// 获取led亮度
	timer_data->brightness_on = led_get_brightness(led_cdev);
	// 如果为0为设置为最大值
	if (timer_data->brightness_on == LED_OFF)
		timer_data->brightness_on = led_cdev->max_brightness;
	// 赋值led的trigger_data域
	led_cdev->trigger_data = timer_data;
	// 初始化timer
	init_timer(&timer_data->timer);
	timer_data->timer.function = led_timer_function;
	timer_data->timer.data = (unsigned long) led_cdev;
	// 创建属性文件delay_on,delay_off
	rc = device_create_file(led_cdev->dev, &dev_attr_delay_on);
	if (rc)
		goto err_out;
	rc = device_create_file(led_cdev->dev, &dev_attr_delay_off);
	if (rc)
		goto err_out_delayon;

	/* If there is hardware support for blinking, start one
	 * user friendly blink rate chosen by the driver.
	 */
	// 如果硬件支持led闪烁,则开始使用默认值闪烁
	if (led_cdev->blink_set)
		led_cdev->blink_set(led_cdev,
			&timer_data->delay_on, &timer_data->delay_off);

	return;

err_out_delayon:
	device_remove_file(led_cdev->dev, &dev_attr_delay_on);
err_out:
	led_cdev->trigger_data = NULL;
	kfree(timer_data);
}
// timer_led_trigger的分离函数
static void timer_trig_deactivate(struct led_classdev *led_cdev)
{
	struct timer_trig_data *timer_data = led_cdev->trigger_data;
	unsigned long on = 0, off = 0;
	// 删除属性文件、timer、释放内存
	if (timer_data) {
		device_remove_file(led_cdev->dev, &dev_attr_delay_on);
		device_remove_file(led_cdev->dev, &dev_attr_delay_off);
		del_timer_sync(&timer_data->timer);
		kfree(timer_data);
	}

	// 停止闪烁
	if (led_cdev->blink_set)
		led_cdev->blink_set(led_cdev, &on, &off);
}
// timer_led_trigger注册函数
static struct led_trigger timer_led_trigger = {
	.name     = "timer",
	.activate = timer_trig_activate,
	.deactivate = timer_trig_deactivate,
};

static int __init timer_trig_init(void)
{
	return led_trigger_register(&timer_led_trigger);
}

static void __exit timer_trig_exit(void)
{
	led_trigger_unregister(&timer_led_trigger);
}

这个timer触发器的实现有个bug,是在soft irq函数中去设置亮度,如果平台控制led亮度比较复杂,比如说通过i2c设备,将会引起内核crash,所以需要创建一个工作队列来实现亮度控制。

你可能感兴趣的:(android button light 流程分析(一) — driver)