/*
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() // 模块入口