Skip to content

Commit cb17fe0

Browse files
perexgtiwai
authored andcommitted
ALSA: control - add sysfs support to the LED trigger module
Create SYSFS/devices/virtual/sound/ctl-led tree (with SYSFS/class/sound/ctl-led symlink). speaker/ +-- mode +-- brightness mic/ +-- mode +-- brightness Copy the idea from the HDA driver and allow to set the audio LEDs based on the various modes: - follow mute - follow moute (inverted to follow mute) - off - on Also, the actual LED state is exposed. Signed-off-by: Jaroslav Kysela <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]>
1 parent e65bf99 commit cb17fe0

File tree

1 file changed

+163
-29
lines changed

1 file changed

+163
-29
lines changed

sound/core/control_led.c

Lines changed: 163 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,23 @@ MODULE_LICENSE("GPL");
1717
#define MAX_LED (((SNDRV_CTL_ELEM_ACCESS_MIC_LED - SNDRV_CTL_ELEM_ACCESS_SPK_LED) \
1818
>> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) + 1)
1919

20+
enum snd_ctl_led_mode {
21+
MODE_FOLLOW_MUTE = 0,
22+
MODE_FOLLOW_ROUTE,
23+
MODE_OFF,
24+
MODE_ON,
25+
};
26+
2027
struct snd_ctl_led {
28+
struct device dev;
29+
struct list_head controls;
30+
const char *name;
31+
unsigned int group;
32+
enum led_audio trigger_type;
33+
enum snd_ctl_led_mode mode;
34+
};
35+
36+
struct snd_ctl_led_ctl {
2137
struct list_head list;
2238
struct snd_card *card;
2339
unsigned int access;
@@ -26,8 +42,21 @@ struct snd_ctl_led {
2642
};
2743

2844
static DEFINE_MUTEX(snd_ctl_led_mutex);
29-
static struct list_head snd_ctl_led_controls[MAX_LED];
3045
static bool snd_ctl_led_card_valid[SNDRV_CARDS];
46+
static struct snd_ctl_led snd_ctl_leds[MAX_LED] = {
47+
{
48+
.name = "speaker",
49+
.group = (SNDRV_CTL_ELEM_ACCESS_SPK_LED >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) - 1,
50+
.trigger_type = LED_AUDIO_MUTE,
51+
.mode = MODE_FOLLOW_MUTE,
52+
},
53+
{
54+
.name = "mic",
55+
.group = (SNDRV_CTL_ELEM_ACCESS_MIC_LED >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) - 1,
56+
.trigger_type = LED_AUDIO_MICMUTE,
57+
.mode = MODE_FOLLOW_MUTE,
58+
},
59+
};
3160

3261
#define UPDATE_ROUTE(route, cb) \
3362
do { \
@@ -47,15 +76,15 @@ static inline unsigned int group_to_access(unsigned int group)
4776
return (group + 1) << SNDRV_CTL_ELEM_ACCESS_LED_SHIFT;
4877
}
4978

50-
static struct list_head *snd_ctl_led_controls_by_access(unsigned int access)
79+
static struct snd_ctl_led *snd_ctl_led_get_by_access(unsigned int access)
5180
{
5281
unsigned int group = access_to_group(access);
5382
if (group >= MAX_LED)
5483
return NULL;
55-
return &snd_ctl_led_controls[group];
84+
return &snd_ctl_leds[group];
5685
}
5786

58-
static int snd_ctl_led_get(struct snd_ctl_led *lctl)
87+
static int snd_ctl_led_get(struct snd_ctl_led_ctl *lctl)
5988
{
6089
struct snd_kcontrol *kctl = lctl->kctl;
6190
struct snd_ctl_elem_info info;
@@ -91,22 +120,14 @@ static int snd_ctl_led_get(struct snd_ctl_led *lctl)
91120
static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access,
92121
struct snd_kcontrol *kctl, unsigned int ioff)
93122
{
94-
struct list_head *controls;
95-
struct snd_ctl_led *lctl;
96-
enum led_audio led_trigger_type;
123+
struct snd_ctl_led *led;
124+
struct snd_ctl_led_ctl *lctl;
97125
int route;
98126
bool found;
99127

100-
controls = snd_ctl_led_controls_by_access(access);
101-
if (!controls)
128+
led = snd_ctl_led_get_by_access(access);
129+
if (!led)
102130
return;
103-
if (access == SNDRV_CTL_ELEM_ACCESS_SPK_LED) {
104-
led_trigger_type = LED_AUDIO_MUTE;
105-
} else if (access == SNDRV_CTL_ELEM_ACCESS_MIC_LED) {
106-
led_trigger_type = LED_AUDIO_MICMUTE;
107-
} else {
108-
return;
109-
}
110131
route = -1;
111132
found = false;
112133
mutex_lock(&snd_ctl_led_mutex);
@@ -115,7 +136,7 @@ static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access,
115136
mutex_unlock(&snd_ctl_led_mutex);
116137
return;
117138
}
118-
list_for_each_entry(lctl, controls, list) {
139+
list_for_each_entry(lctl, &led->controls, list) {
119140
if (lctl->kctl == kctl && lctl->index_offset == ioff)
120141
found = true;
121142
UPDATE_ROUTE(route, snd_ctl_led_get(lctl));
@@ -127,23 +148,29 @@ static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access,
127148
lctl->access = access;
128149
lctl->kctl = kctl;
129150
lctl->index_offset = ioff;
130-
list_add(&lctl->list, controls);
151+
list_add(&lctl->list, &led->controls);
131152
UPDATE_ROUTE(route, snd_ctl_led_get(lctl));
132153
}
133154
}
134155
mutex_unlock(&snd_ctl_led_mutex);
156+
switch (led->mode) {
157+
case MODE_OFF: route = 1; break;
158+
case MODE_ON: route = 0; break;
159+
case MODE_FOLLOW_ROUTE: if (route >= 0) route ^= 1; break;
160+
case MODE_FOLLOW_MUTE: /* noop */ break;
161+
}
135162
if (route >= 0)
136-
ledtrig_audio_set(led_trigger_type, route ? LED_OFF : LED_ON);
163+
ledtrig_audio_set(led->trigger_type, route ? LED_OFF : LED_ON);
137164
}
138165

139-
static struct snd_ctl_led *snd_ctl_led_find(struct snd_kcontrol *kctl, unsigned int ioff)
166+
static struct snd_ctl_led_ctl *snd_ctl_led_find(struct snd_kcontrol *kctl, unsigned int ioff)
140167
{
141168
struct list_head *controls;
142-
struct snd_ctl_led *lctl;
169+
struct snd_ctl_led_ctl *lctl;
143170
unsigned int group;
144171

145172
for (group = 0; group < MAX_LED; group++) {
146-
controls = &snd_ctl_led_controls[group];
173+
controls = &snd_ctl_leds[group].controls;
147174
list_for_each_entry(lctl, controls, list)
148175
if (lctl->kctl == kctl && lctl->index_offset == ioff)
149176
return lctl;
@@ -154,7 +181,7 @@ static struct snd_ctl_led *snd_ctl_led_find(struct snd_kcontrol *kctl, unsigned
154181
static unsigned int snd_ctl_led_remove(struct snd_kcontrol *kctl, unsigned int ioff,
155182
unsigned int access)
156183
{
157-
struct snd_ctl_led *lctl;
184+
struct snd_ctl_led_ctl *lctl;
158185
unsigned int ret = 0;
159186

160187
mutex_lock(&snd_ctl_led_mutex);
@@ -206,13 +233,13 @@ static void snd_ctl_led_refresh(void)
206233
static void snd_ctl_led_clean(struct snd_card *card)
207234
{
208235
unsigned int group;
209-
struct list_head *controls;
210-
struct snd_ctl_led *lctl;
236+
struct snd_ctl_led *led;
237+
struct snd_ctl_led_ctl *lctl;
211238

212239
for (group = 0; group < MAX_LED; group++) {
213-
controls = &snd_ctl_led_controls[group];
240+
led = &snd_ctl_leds[group];
214241
repeat:
215-
list_for_each_entry(lctl, controls, list)
242+
list_for_each_entry(lctl, &led->controls, list)
216243
if (!card || lctl->card == card) {
217244
list_del(&lctl->list);
218245
kfree(lctl);
@@ -248,6 +275,82 @@ static void snd_ctl_led_disconnect(struct snd_card *card)
248275
snd_ctl_led_refresh();
249276
}
250277

278+
/*
279+
* sysfs
280+
*/
281+
282+
static ssize_t show_mode(struct device *dev,
283+
struct device_attribute *attr, char *buf)
284+
{
285+
struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
286+
const char *str;
287+
288+
switch (led->mode) {
289+
case MODE_FOLLOW_MUTE: str = "follow-mute"; break;
290+
case MODE_FOLLOW_ROUTE: str = "follow-route"; break;
291+
case MODE_ON: str = "on"; break;
292+
case MODE_OFF: str = "off"; break;
293+
}
294+
return sprintf(buf, "%s\n", str);
295+
}
296+
297+
static ssize_t store_mode(struct device *dev, struct device_attribute *attr,
298+
const char *buf, size_t count)
299+
{
300+
struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
301+
char _buf[16];
302+
size_t l = min(count, sizeof(_buf) - 1) + 1;
303+
enum snd_ctl_led_mode mode;
304+
305+
memcpy(_buf, buf, l);
306+
_buf[l] = '\0';
307+
if (strstr(_buf, "mute"))
308+
mode = MODE_FOLLOW_MUTE;
309+
else if (strstr(_buf, "route"))
310+
mode = MODE_FOLLOW_ROUTE;
311+
else if (strncmp(_buf, "off", 3) == 0 || strncmp(_buf, "0", 1) == 0)
312+
mode = MODE_OFF;
313+
else if (strncmp(_buf, "on", 2) == 0 || strncmp(_buf, "1", 1) == 0)
314+
mode = MODE_ON;
315+
else
316+
return count;
317+
318+
mutex_lock(&snd_ctl_led_mutex);
319+
led->mode = mode;
320+
mutex_unlock(&snd_ctl_led_mutex);
321+
322+
snd_ctl_led_set_state(NULL, group_to_access(led->group), NULL, 0);
323+
return count;
324+
}
325+
326+
static ssize_t show_brightness(struct device *dev,
327+
struct device_attribute *attr, char *buf)
328+
{
329+
struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
330+
331+
return sprintf(buf, "%u\n", ledtrig_audio_get(led->trigger_type));
332+
}
333+
334+
static DEVICE_ATTR(mode, 0644, show_mode, store_mode);
335+
static DEVICE_ATTR(brightness, 0444, show_brightness, NULL);
336+
337+
static struct attribute *snd_ctl_led_dev_attrs[] = {
338+
&dev_attr_mode.attr,
339+
&dev_attr_brightness.attr,
340+
NULL,
341+
};
342+
343+
static const struct attribute_group snd_ctl_led_dev_attr_group = {
344+
.attrs = snd_ctl_led_dev_attrs,
345+
};
346+
347+
static const struct attribute_group *snd_ctl_led_dev_attr_groups[] = {
348+
&snd_ctl_led_dev_attr_group,
349+
NULL,
350+
};
351+
352+
static struct device snd_ctl_led_dev;
353+
251354
/*
252355
* Control layer registration
253356
*/
@@ -260,16 +363,47 @@ static struct snd_ctl_layer_ops snd_ctl_led_lops = {
260363

261364
static int __init snd_ctl_led_init(void)
262365
{
366+
struct snd_ctl_led *led;
263367
unsigned int group;
264368

265-
for (group = 0; group < MAX_LED; group++)
266-
INIT_LIST_HEAD(&snd_ctl_led_controls[group]);
369+
device_initialize(&snd_ctl_led_dev);
370+
snd_ctl_led_dev.class = sound_class;
371+
dev_set_name(&snd_ctl_led_dev, "ctl-led");
372+
if (device_add(&snd_ctl_led_dev)) {
373+
put_device(&snd_ctl_led_dev);
374+
return -ENOMEM;
375+
}
376+
for (group = 0; group < MAX_LED; group++) {
377+
led = &snd_ctl_leds[group];
378+
INIT_LIST_HEAD(&led->controls);
379+
device_initialize(&led->dev);
380+
led->dev.parent = &snd_ctl_led_dev;
381+
led->dev.groups = snd_ctl_led_dev_attr_groups;
382+
dev_set_name(&led->dev, led->name);
383+
if (device_add(&led->dev)) {
384+
put_device(&led->dev);
385+
for (; group > 0; group--) {
386+
led = &snd_ctl_leds[group];
387+
device_del(&led->dev);
388+
}
389+
device_del(&snd_ctl_led_dev);
390+
return -ENOMEM;
391+
}
392+
}
267393
snd_ctl_register_layer(&snd_ctl_led_lops);
268394
return 0;
269395
}
270396

271397
static void __exit snd_ctl_led_exit(void)
272398
{
399+
struct snd_ctl_led *led;
400+
unsigned int group;
401+
402+
for (group = 0; group < MAX_LED; group++) {
403+
led = &snd_ctl_leds[group];
404+
device_del(&led->dev);
405+
}
406+
device_del(&snd_ctl_led_dev);
273407
snd_ctl_disconnect_layer(&snd_ctl_led_lops);
274408
snd_ctl_led_clean(NULL);
275409
}

0 commit comments

Comments
 (0)