客户需求亮屏灯常亮,休眠灯闪烁,默认的指示灯使用的pattern模式,把相关的代码捋一捋。
展讯平台打开呼吸灯的方式
cd /sys/class/leds/sc27xx:blue/
echo pattern > trigger
echo 50 255 875 255 75 255 1000 255 > hw_pattern
把pattern模式注册到trigger_list
static struct attribute *pattern_trig_attrs[] = {
&dev_attr_pattern.attr,
&dev_attr_hw_pattern.attr,
&dev_attr_repeat.attr,
NULL
};
static const struct attribute_group pattern_trig_group = {
.attrs = pattern_trig_attrs,
.is_visible = pattern_trig_attrs_mode,
};
static const struct attribute_group *pattern_trig_groups[] = {
&pattern_trig_group,
NULL,
};
extern struct list_head trigger_list;
static struct led_trigger pattern_led_trigger = {
.name = "pattern",
.activate = pattern_trig_activate,
.deactivate = pattern_trig_deactivate,
.groups = pattern_trig_groups,
};
static int __init pattern_trig_init(void)
{
return led_trigger_register(&pattern_led_trigger);
}
int led_trigger_register(struct led_trigger *trig)
{
...
list_add_tail(&trig->next_trig, &trigger_list);
...
}
echo pattern>trigger 选中pattern模式
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);
struct led_trigger *trig;
int ret = count;
list_for_each_entry(trig, &trigger_list, next_trig) {
if (sysfs_streq(buf, trig->name)) {
led_trigger_set(led_cdev, trig);
}
}
}
EXPORT_SYMBOL_GPL(led_trigger_store);
int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
{
...
if (trig) {
list_add_tail(&led_cdev->trig_list, &trig->led_cdevs);
led_cdev->trigger = trig;
if (trig->activate)
ret = trig->activate(led_cdev);
ret = device_add_groups(led_cdev->dev, trig->groups);
if (ret) {
dev_err(led_cdev->dev, "Failed to add trigger attributes\n");
goto err_add_groups;
}
}
...
}
device_add_groups生成pattern/pattern_hw/repeat节点
static int pattern_trig_activate(struct led_classdev *led_cdev)
{
struct pattern_trig_data *data;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->is_indefinite = true;
data->last_repeat = -1;
data->led_cdev = led_cdev;
led_set_trigger_data(led_cdev, data);
timer_setup(&data->timer, pattern_trig_timer_function, 0);
led_cdev->activated = true;
return 0;
}
static ssize_t hw_pattern_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t count)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
return pattern_trig_store_patterns(led_cdev, buf, count, true);
}
static ssize_t pattern_trig_store_patterns(struct led_classdev *led_cdev,const char *buf, size_t count, bool hw_pattern)
{
struct pattern_trig_data *data = led_cdev->trigger_data;
while (offset < count - 1 && data->npatterns < MAX_PATTERNS) {
cr = 0;
ccount = sscanf(buf + offset, "%d %u %n",&data->patterns[data->npatterns].brightness,&data->patterns[data->npatterns].delta_t, &cr);
if (ccount != 2) {
data->npatterns = 0;
err = -EINVAL;
goto out;
}
offset += cr;
data->npatterns++;
}
err = pattern_trig_start_pattern(led_cdev);
if (err)
data->npatterns = 0;
}
static int pattern_trig_start_pattern(struct led_classdev *led_cdev)
{
struct pattern_trig_data *data = led_cdev->trigger_data;
if (data->is_hw_pattern) {
return led_cdev->pattern_set(led_cdev, data->patterns,
data->npatterns, data->repeat);
}
}
把数据写到led_cdev->pattern_set(平台的控制器已实现),如
static int sc27xx_led_pattern_set(struct led_classdev *ldev,
struct led_pattern *pattern,
u32 len, int repeat)
{
struct sc27xx_led *leds = to_sc27xx_led(ldev);
u32 base = sc27xx_led_get_offset(leds);
u32 ctrl_base = leds->priv->base + SC27XX_LEDS_CTRL;
u8 ctrl_shift = SC27XX_CTRL_SHIFT * leds->line;
struct regmap *regmap = leds->priv->regmap;
int err;
/*
* Must contain 4 tuples to configure the rise time, high time, fall
* time and low time to enable the breathing mode.
*/
if (len != SC27XX_LEDS_PATTERN_CNT)
return -EINVAL;
mutex_lock(&leds->priv->lock);
sc27xx_led_clamp_align_delta_t(&pattern[0].delta_t);
err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
SC27XX_CURVE_L_MASK,
pattern[0].delta_t / SC27XX_LEDS_STEP);
if (err)
goto out;
sc27xx_led_clamp_align_delta_t(&pattern[1].delta_t);
err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
SC27XX_CURVE_L_MASK,
pattern[1].delta_t / SC27XX_LEDS_STEP);
if (err)
goto out;
sc27xx_led_clamp_align_delta_t(&pattern[2].delta_t);
err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE0,
SC27XX_CURVE_H_MASK,
(pattern[2].delta_t / SC27XX_LEDS_STEP) <<
SC27XX_CURVE_SHIFT);
if (err)
goto out;
sc27xx_led_clamp_align_delta_t(&pattern[3].delta_t);
err = regmap_update_bits(regmap, base + SC27XX_LEDS_CURVE1,
SC27XX_CURVE_H_MASK,
(pattern[3].delta_t / SC27XX_LEDS_STEP) <<
SC27XX_CURVE_SHIFT);
if (err)
goto out;
err = regmap_update_bits(regmap, base + SC27XX_LEDS_DUTY,
SC27XX_DUTY_MASK,
(pattern[1].brightness << SC27XX_DUTY_SHIFT) |
SC27XX_MOD_MASK);
if (err)
goto out;
/* Enable the LED breathing mode */
err = regmap_update_bits(regmap, ctrl_base,
SC27XX_LED_RUN << ctrl_shift,
SC27XX_LED_RUN << ctrl_shift);
if (!err)
ldev->brightness = pattern[1].brightness;
out:
mutex_unlock(&leds->priv->lock);
return err;
}