阅读背景:

led子系统分析:源码核心层1. kernel-3.18\drivers\leds\led-class.c

来源:互联网 
/*
	led-class.c分析:

	1.leds_init()						// 模块入口
		leds_class = class_create(THIS_MODULE, "leds");
		leds_class->pm = &leds_class_dev_pm_ops;
		leds_class->dev_groups = led_groups;
*/

/*
	SIMPLE_DEV_PM_OPS宏

	将led-class中的suspend中的指针,以及resume的指针初始化
	当系统休眠或唤醒的时候,遍历class,调用其中的pm的书suspend() 或resume()
		
	static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);

	==>
	static const struct dev_pm_ops leds_class_dev_pm_ops = { 
		SET_SYSTEM_SLEEP_PM_OPS(led_suspend, led_resume) 
	}

	==>
	static const struct dev_pm_ops leds_class_dev_pm_ops = {
		.suspend = led_suspend,
		.resume = led_resume,
	}
*/

/*
	sysfs创建节点方法:

	共创建3个节点:brightness、max_brightness、trigger
	
static DEVICE_ATTR_RW(brightness);			// 可读可写
static DEVICE_ATTR_RO(max_brightness);		// 只读
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);	// 可读可写 - 控制led闪烁
*/

/*
应用层控制led的方法:
	读: cat /sys/class/leds/xxx/brightness
	写: echo 255 > /sys/class/leds/xxx/brightness

读 cat:
static ssize_t brightness_show(struct device *dev, struct device_attribute *attr, char *buf)
	|
	return sprintf(buf, "%u\n", led_cdev->brightness);

写 echo:
static ssize_t brightness_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
	|
	__led_set_brightness(led_cdev, state);
		|
		led_cdev->brightness_set(led_cdev, value);
			// g_leds_data[i]->cdev.brightness_set = mt65xx_led_set;		// 注册led的时候设定了函数指针	
			// led_classdev_register(&pdev->dev, &g_leds_data[i]->cdev);	// 注册一个led(如red)到一个全局leds_list链表中
		
		即mt65xx_led_set(led_cdev, value)
			|
			mt_mt65xx_led_set(led_cdev, level);
				|
				if (strcmp(led_data->cust.name, "lcd-backlight") != 0) {	// led走这里
					schedule_work(&led_data->work);
						|
						// INIT_WORK(&g_leds_data[i]->work, mt_mt65xx_led_work);// 初始化一个工作
						mt_mt65xx_led_work()
							|
							mt_mt65xx_led_set_cust(&led_data->cust, led_data->level);
								|
								switch (cust->mode) {
								case MT65XX_LED_MODE_PWM:				// 模式1
								case MT65XX_LED_MODE_GPIO:				// 模式2 - B
									return mt_brightness_set_gpio(cust->data, level);
											|
											nleds_get_gpio_pinctrl();	
											if (level == 0){
												if(data == 0)
												pinctrl_select_state(led_pinctrl, pinctrl_led0_low);
												else if(data == 1) 
												pinctrl_select_state(led_pinctrl, pinctrl_led1_low);
											}
											else {
												if(data == 0) 
												pinctrl_select_state(led_pinctrl, pinctrl_led0_high);
												else if(data == 1) 
												pinctrl_select_state(led_pinctrl, pinctrl_led1_high);
											}
								case MT65XX_LED_MODE_PMIC:				// 模式3 - RG 根据原理图可知:三色灯中的R、G、B分别接在ISINK0、ISINK1、GPIO63(AD27)上
									return mt_brightness_set_pmic(cust->data, level, bl_div_hal);
										   mt_brightness_set_pmic(enum mt65xx_led_pmic pmic_type, u32 level, u32 div)
											|
											if (pmic_type == MT65XX_LED_PMIC_NLED_ISINK0) {				// 三色灯中的R
												pmic_set_register_value(PMIC_ISINK_CH0_STEP, ISINK_3);	// 16mA 在此处修改亮度,4.8.16.20.24共5个等级
												if (level)
													pmic_set_register_value(PMIC_ISINK_CH0_EN, NLED_ON);
												else
													pmic_set_register_value(PMIC_ISINK_CH0_EN, NLED_OFF);
											} else if (pmic_type == MT65XX_LED_PMIC_NLED_ISINK1) {		// 三色灯中的G
												pmic_set_register_value(PMIC_ISINK_CH1_STEP, ISINK_3);	// 16mA 在此处修改亮度,4.8.16.20.24共5个等级
												if (level)
													pmic_set_register_value(PMIC_ISINK_CH1_EN, NLED_ON);
												else
													pmic_set_register_value(PMIC_ISINK_CH1_EN, NLED_OFF);

								case MT65XX_LED_MODE_CUST_BLS_PWM:		// 模式5 - lcd-backlight 
									if (strcmp(cust->name, "lcd-backlight") == 0)
										bl_brightness_hal = level;
									return ((cust_set_brightness) (cust->data)) (level);	// typedef int (*cust_set_brightness) (int level); 强制类型转化

				} else {													// lcd-backlight走这里
				 // level = level*70/100;								// 这里可以添加修改lcm亮度的代码 : 改为70%
				disp_aal_notify_backlight_changed((((1 <<				// 关键 - 设置lcd-backlight亮度值
					|			     MT_LED_INTERNAL_LEVEL_BIT_CNT)		// 将0-255  换算成0-1023
					|			    - 1) * level +						// 注: 降低亮度可在这之前level = level*70/100
					|			   127) / 255);
					backlight_brightness_set(bl_1024);
						|
						mt_mt65xx_led_set_cust(&cust_led_list[MT65XX_LED_TYPE_LCD],	level); // 设置亮度值
					   

*/

/*
	trigger 触发器

	用于控制led闪烁,常见的有以下几种:

	[*]   LED Trigger support  --->            // 会编译driver/leds/trigger/led-triggers.c
      [*]   LED Timer Trigger                  // 常用的
      [ ]   LED One-shot Trigger              
      [ ]   LED Heartbeat Trigger          
      [ ]   LED backlight Trigger            
      [ ]   LED CPU Trigger                   
      [ ]   LED GPIO Trigger              
      [ ]   LED Default ON Trigger         
      .....


	1. led-class.c中
		leds_init()						// 模块入口
			
|
			leds_class = class_create(THIS_MODULE, "leds");

	2. leds_drv.c中
		2.1 mt65xx_leds_init()			// 模块入口
				platform_driver_register(&mt65xx_leds_driver);
		
		2.2 static struct platform_driver mt65xx_leds_driver = {
				.probe 		= mt65xx_leds_probe,
				
		2.3 static int mt65xx_leds_probe(struct platform_device *pdev)	// 获取dts中led列表,逐一注册
		 		|
				struct cust_mt65xx_led *cust_led_list = mt_get_cust_led_list();	// 获取dts中led的信息:led_mode==3 data==2
				for (i = 0; i < MT65XX_LED_TYPE_TOTAL; i++) {
					g_leds_data[i] = kzalloc(sizeof(struct mt65xx_led_data), GFP_KERNEL);
					g_leds_data[i]->cdev.name = cust_led_list[i].name;			// [dts]compatible = "mediatek,red";
					g_leds_data[i]->cdev.blink_set = mt65xx_blink_set;			// 函数指针

					led_classdev_register(&pdev->dev, &g_leds_data[i]->cdev);	// 将cdev注册到一个led链表中
						|
						list_add_tail(&led_cdev->node, &leds_list);				// 注册一个led(red、blue、lcm_backlight等)到链表
						init_timer(&led_cdev->blink_timer);						// 初始化定时器 - 用于trigger控制led闪烁
						led_cdev->blink_timer.function = led_timer_function;
						led_cdev->blink_timer.data = (unsigned long)led_cdev;

	3. driver/leds/trigger/led-triggers.c
		3.1 timer_trig_init(void)			// module_init 模块入口 log显示有运行
				|
				led_trigger_register(&timer_led_trigger);
				led_trigger_register(struct led_trigger *trig)
					|
					list_for_each_entry(_trig, &trigger_list, next_trig) {	// 遍历trigger_list链表,看同名trigger是否已注册了
						if (!strcmp(_trig->name, trig->name)) {
							up_write(&triggers_list_lock);
							return -EEXIST;
						}
					}
					list_add_tail(&trig->next_trig, &trigger_list);			// 注册trigger到trigger_list链表

					// 遍历所有led,(无trigger,且有默认trigger,且默认tigger名字不等于当前trigger)的led,全都指定本trigger
					list_for_each_entry(led_cdev, &leds_list, node) {
						if (!led_cdev->trigger && led_cdev->default_trigger && !strcmp(led_cdev->default_trigger, trig->name))	// 无trigger,且有默认trigger,且默认tigger名字不等于当前trigger
							led_trigger_set(led_cdev, trig);
								|
								led_cdev->trigger = trig;
*/


#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/leds.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include "leds.h"

static struct class *leds_class;

static ssize_t 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 brightness_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t size)
{
	struct led_classdev *led_cdev = dev_get_drvdata(dev);
	unsigned long state;
	ssize_t ret = -EINVAL;

	ret = kstrtoul(buf, 10, &state);
	if (ret)
		return ret;

	if (state == LED_OFF)
		led_trigger_remove(led_cdev);
	__led_set_brightness(led_cdev, state);

	return size;
}
static DEVICE_ATTR_RW(brightness);

static ssize_t 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_RO(max_brightness);

#ifdef CONFIG_LEDS_TRIGGERS
static DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
static struct attribute *led_trigger_attrs[] = {
	&dev_attr_trigger.attr,
	NULL,
};
static const struct attribute_group led_trigger_group = {
	.attrs = led_trigger_attrs,
};
#endif

static struct attribute *led_class_attrs[] = {
	&dev_attr_brightness.attr,
	&dev_attr_max_brightness.attr,
	NULL,
};

static const struct attribute_group led_group = {
	.attrs = led_class_attrs,
};

static const struct attribute_group *led_groups[] = {
	&led_group,
#ifdef CONFIG_LEDS_TRIGGERS
	&led_trigger_group,
#endif
	NULL,
};

static void led_timer_function(unsigned long data)
{
	struct led_classdev *led_cdev = (void *)data;
	unsigned long brightness;
	unsigned long delay;

	if (!led_cdev->blink_delay_on || !led_cdev->blink_delay_off) {
		__led_set_brightness(led_cdev, LED_OFF);
		return;
	}

	if (led_cdev->flags & LED_BLINK_ONESHOT_STOP) {
		led_cdev->flags &= ~LED_BLINK_ONESHOT_STOP;
		return;
	}

	brightness = led_get_brightness(led_cdev);
	if (!brightness) {
		/* Time to switch the LED on. */
		brightness = led_cdev->blink_brightness;
		delay = led_cdev->blink_delay_on;
	} else {
		/* Store the current brightness value to be able
		 * to restore it when the delay_off period is over.
		 */
		led_cdev->blink_brightness = brightness;
		brightness = LED_OFF;
		delay = led_cdev->blink_delay_off;
	}

	__led_set_brightness(led_cdev, brightness);

	/* Return in next iteration if led is in one-shot mode and we are in
	 * the final blink state so that the led is toggled each delay_on +
	 * delay_off milliseconds in worst case.
	 */
	if (led_cdev->flags & LED_BLINK_ONESHOT) {
		if (led_cdev->flags & LED_BLINK_INVERT) {
			if (brightness)
				led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
		} else {
			if (!brightness)
				led_cdev->flags |= LED_BLINK_ONESHOT_STOP;
		}
	}

	mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
}

static void set_brightness_delayed(struct work_struct *ws)
{
	struct led_classdev *led_cdev =
		container_of(ws, struct led_classdev, set_brightness_work);

	led_stop_software_blink(led_cdev);

	__led_set_brightness(led_cdev, led_cdev->delayed_set_value);
}

/**
 * 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);

#ifdef CONFIG_PM_SLEEP
static int led_suspend(struct device *dev)
{
	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;
}
#endif

static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);

/**
 * 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)
{
	led_cdev->dev = device_create_with_groups(leds_class, parent, 0,// 在/sys/class/leds/目录下创建xxx设备(red、blue、lcm_backlight等)
					led_cdev, led_cdev->groups,						// 并在/sys/class/leds/red/目录下创建attr属性
					"%s", led_cdev->name);							// 如: brightness max_brightness trigger
	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);		// 注册一个led(red、blue、lcm_backlight等)到链表
	up_write(&leds_list_lock);

	if (!led_cdev->max_brightness)			// 将max_brightness设置为默认满即255,三色灯未设置
		led_cdev->max_brightness = LED_FULL;

	led_update_brightness(led_cdev);		// 如果设置了get_brightness方法, 则读出当前的brightness并更新,三色灯未设置

	INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);	// 初始化工作

	init_timer(&led_cdev->blink_timer);									// 初始化定时器 - 用于trigger控制led闪烁
	led_cdev->blink_timer.function = led_timer_function;
	led_cdev->blink_timer.data = (unsigned long)led_cdev;

#ifdef CONFIG_LEDS_TRIGGERS		// 此宏 开, 设置默认的trigger
	led_trigger_set_default(led_cdev);
		|
		list_for_each_entry(trig, &trigger_list, next_trig) {
			if (!strcmp(led_cdev->default_trigger, trig->name))
				led_trigger_set(led_cdev, trig);
					|
					led_cdev->trigger = trig;
	}
#endif

	dev_dbg(parent, "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

	cancel_work_sync(&led_cdev->set_brightness_work);

	/* Stop blinking */
	led_stop_software_blink(led_cdev);
	led_set_brightness(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)
{
	leds_class = class_create(THIS_MODULE, "leds");	// 在/sys/class/目录下面创建一个leds类目录
	if (IS_ERR(leds_class))
		return PTR_ERR(leds_class);
	leds_class->pm = &leds_class_dev_pm_ops;		// 【每个class都有这步骤】当系统休眠或唤醒的时候,遍历class,调用其中的pm的书suspend() 或resume()
						|
						static SIMPLE_DEV_PM_OPS(leds_class_dev_pm_ops, led_suspend, led_resume);
	leds_class->dev_groups = led_groups;			// 为基于这个class的所有设备创建属性,sysfs节点
	return 0;
}

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

subsys_initcall(leds_init);		// 优先级比较高
module_exit(leds_exit);

MODULE_AUTHOR("John Lenz, Richard Purdie");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("LED Class Interface");



/*
	led-class.c分析:

	1.leds_init()						// 模块入口



你的当前访问异常,请进行认证后继续阅读剩余内容。

分享到: