Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/sound/core/control_led.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* LED state routines for driver control interface
4
* Copyright (c) 2021 by Jaroslav Kysela <[email protected]>
5
*/
6
7
#include <linux/slab.h>
8
#include <linux/module.h>
9
#include <linux/leds.h>
10
#include <sound/core.h>
11
#include <sound/control.h>
12
13
MODULE_AUTHOR("Jaroslav Kysela <[email protected]>");
14
MODULE_DESCRIPTION("ALSA control interface to LED trigger code.");
15
MODULE_LICENSE("GPL");
16
17
#define MAX_LED (((SNDRV_CTL_ELEM_ACCESS_MIC_LED - SNDRV_CTL_ELEM_ACCESS_SPK_LED) \
18
>> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) + 1)
19
20
#define to_led_card_dev(_dev) \
21
container_of(_dev, struct snd_ctl_led_card, dev)
22
23
enum snd_ctl_led_mode {
24
MODE_FOLLOW_MUTE = 0,
25
MODE_FOLLOW_ROUTE,
26
MODE_OFF,
27
MODE_ON,
28
};
29
30
struct snd_ctl_led_card {
31
struct device dev;
32
int number;
33
struct snd_ctl_led *led;
34
};
35
36
struct snd_ctl_led {
37
struct device dev;
38
struct list_head controls;
39
const char *name;
40
unsigned int group;
41
enum led_audio trigger_type;
42
enum snd_ctl_led_mode mode;
43
struct snd_ctl_led_card *cards[SNDRV_CARDS];
44
};
45
46
struct snd_ctl_led_ctl {
47
struct list_head list;
48
struct snd_card *card;
49
unsigned int access;
50
struct snd_kcontrol *kctl;
51
unsigned int index_offset;
52
};
53
54
static DEFINE_MUTEX(snd_ctl_led_mutex);
55
static bool snd_ctl_led_card_valid[SNDRV_CARDS];
56
static struct led_trigger *snd_ctl_ledtrig_audio[NUM_AUDIO_LEDS];
57
static struct snd_ctl_led snd_ctl_leds[MAX_LED] = {
58
{
59
.name = "speaker",
60
.group = (SNDRV_CTL_ELEM_ACCESS_SPK_LED >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) - 1,
61
.trigger_type = LED_AUDIO_MUTE,
62
.mode = MODE_FOLLOW_MUTE,
63
},
64
{
65
.name = "mic",
66
.group = (SNDRV_CTL_ELEM_ACCESS_MIC_LED >> SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) - 1,
67
.trigger_type = LED_AUDIO_MICMUTE,
68
.mode = MODE_FOLLOW_MUTE,
69
},
70
};
71
72
static void snd_ctl_led_sysfs_add(struct snd_card *card);
73
static void snd_ctl_led_sysfs_remove(struct snd_card *card);
74
75
#define UPDATE_ROUTE(route, cb) \
76
do { \
77
int route2 = (cb); \
78
if (route2 >= 0) \
79
route = route < 0 ? route2 : (route | route2); \
80
} while (0)
81
82
static inline unsigned int access_to_group(unsigned int access)
83
{
84
return ((access & SNDRV_CTL_ELEM_ACCESS_LED_MASK) >>
85
SNDRV_CTL_ELEM_ACCESS_LED_SHIFT) - 1;
86
}
87
88
static inline unsigned int group_to_access(unsigned int group)
89
{
90
return (group + 1) << SNDRV_CTL_ELEM_ACCESS_LED_SHIFT;
91
}
92
93
static struct snd_ctl_led *snd_ctl_led_get_by_access(unsigned int access)
94
{
95
unsigned int group = access_to_group(access);
96
if (group >= MAX_LED)
97
return NULL;
98
return &snd_ctl_leds[group];
99
}
100
101
/*
102
* A note for callers:
103
* The two static variables info and value are protected using snd_ctl_led_mutex.
104
*/
105
static int snd_ctl_led_get(struct snd_ctl_led_ctl *lctl)
106
{
107
static struct snd_ctl_elem_info info;
108
static struct snd_ctl_elem_value value;
109
struct snd_kcontrol *kctl = lctl->kctl;
110
unsigned int i;
111
int result;
112
113
memset(&info, 0, sizeof(info));
114
info.id = kctl->id;
115
info.id.index += lctl->index_offset;
116
info.id.numid += lctl->index_offset;
117
result = kctl->info(kctl, &info);
118
if (result < 0)
119
return -1;
120
memset(&value, 0, sizeof(value));
121
value.id = info.id;
122
result = kctl->get(kctl, &value);
123
if (result < 0)
124
return -1;
125
if (info.type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
126
info.type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
127
for (i = 0; i < info.count; i++)
128
if (value.value.integer.value[i] != info.value.integer.min)
129
return 1;
130
} else if (info.type == SNDRV_CTL_ELEM_TYPE_INTEGER64) {
131
for (i = 0; i < info.count; i++)
132
if (value.value.integer64.value[i] != info.value.integer64.min)
133
return 1;
134
}
135
return 0;
136
}
137
138
static void snd_ctl_led_set_state(struct snd_card *card, unsigned int access,
139
struct snd_kcontrol *kctl, unsigned int ioff)
140
{
141
struct snd_ctl_led *led;
142
struct snd_ctl_led_ctl *lctl;
143
int route;
144
bool found;
145
146
led = snd_ctl_led_get_by_access(access);
147
if (!led)
148
return;
149
route = -1;
150
found = false;
151
scoped_guard(mutex, &snd_ctl_led_mutex) {
152
/* the card may not be registered (active) at this point */
153
if (card && !snd_ctl_led_card_valid[card->number])
154
return;
155
list_for_each_entry(lctl, &led->controls, list) {
156
if (lctl->kctl == kctl && lctl->index_offset == ioff)
157
found = true;
158
UPDATE_ROUTE(route, snd_ctl_led_get(lctl));
159
}
160
if (!found && kctl && card) {
161
lctl = kzalloc(sizeof(*lctl), GFP_KERNEL);
162
if (lctl) {
163
lctl->card = card;
164
lctl->access = access;
165
lctl->kctl = kctl;
166
lctl->index_offset = ioff;
167
list_add(&lctl->list, &led->controls);
168
UPDATE_ROUTE(route, snd_ctl_led_get(lctl));
169
}
170
}
171
}
172
switch (led->mode) {
173
case MODE_OFF: route = 1; break;
174
case MODE_ON: route = 0; break;
175
case MODE_FOLLOW_ROUTE: if (route >= 0) route ^= 1; break;
176
case MODE_FOLLOW_MUTE: /* noop */ break;
177
}
178
if (route >= 0) {
179
struct led_trigger *trig = snd_ctl_ledtrig_audio[led->trigger_type];
180
181
led_trigger_event(trig, route ? LED_OFF : LED_ON);
182
}
183
}
184
185
static struct snd_ctl_led_ctl *snd_ctl_led_find(struct snd_kcontrol *kctl, unsigned int ioff)
186
{
187
struct list_head *controls;
188
struct snd_ctl_led_ctl *lctl;
189
unsigned int group;
190
191
for (group = 0; group < MAX_LED; group++) {
192
controls = &snd_ctl_leds[group].controls;
193
list_for_each_entry(lctl, controls, list)
194
if (lctl->kctl == kctl && lctl->index_offset == ioff)
195
return lctl;
196
}
197
return NULL;
198
}
199
200
static unsigned int snd_ctl_led_remove(struct snd_kcontrol *kctl, unsigned int ioff,
201
unsigned int access)
202
{
203
struct snd_ctl_led_ctl *lctl;
204
unsigned int ret = 0;
205
206
guard(mutex)(&snd_ctl_led_mutex);
207
lctl = snd_ctl_led_find(kctl, ioff);
208
if (lctl && (access == 0 || access != lctl->access)) {
209
ret = lctl->access;
210
list_del(&lctl->list);
211
kfree(lctl);
212
}
213
return ret;
214
}
215
216
static void snd_ctl_led_notify(struct snd_card *card, unsigned int mask,
217
struct snd_kcontrol *kctl, unsigned int ioff)
218
{
219
struct snd_kcontrol_volatile *vd;
220
unsigned int access, access2;
221
222
if (mask == SNDRV_CTL_EVENT_MASK_REMOVE) {
223
access = snd_ctl_led_remove(kctl, ioff, 0);
224
if (access)
225
snd_ctl_led_set_state(card, access, NULL, 0);
226
} else if (mask & SNDRV_CTL_EVENT_MASK_INFO) {
227
vd = &kctl->vd[ioff];
228
access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK;
229
access2 = snd_ctl_led_remove(kctl, ioff, access);
230
if (access2)
231
snd_ctl_led_set_state(card, access2, NULL, 0);
232
if (access)
233
snd_ctl_led_set_state(card, access, kctl, ioff);
234
} else if ((mask & (SNDRV_CTL_EVENT_MASK_ADD |
235
SNDRV_CTL_EVENT_MASK_VALUE)) != 0) {
236
vd = &kctl->vd[ioff];
237
access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK;
238
if (access)
239
snd_ctl_led_set_state(card, access, kctl, ioff);
240
}
241
}
242
243
DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T))
244
245
static int snd_ctl_led_set_id(int card_number, struct snd_ctl_elem_id *id,
246
unsigned int group, bool set)
247
{
248
struct snd_card *card __free(snd_card_unref) = NULL;
249
struct snd_kcontrol *kctl;
250
struct snd_kcontrol_volatile *vd;
251
unsigned int ioff, access, new_access;
252
253
card = snd_card_ref(card_number);
254
if (!card)
255
return -ENXIO;
256
guard(rwsem_write)(&card->controls_rwsem);
257
kctl = snd_ctl_find_id(card, id);
258
if (!kctl)
259
return -ENOENT;
260
ioff = snd_ctl_get_ioff(kctl, id);
261
vd = &kctl->vd[ioff];
262
access = vd->access & SNDRV_CTL_ELEM_ACCESS_LED_MASK;
263
if (access != 0 && access != group_to_access(group))
264
return -EXDEV;
265
new_access = vd->access & ~SNDRV_CTL_ELEM_ACCESS_LED_MASK;
266
if (set)
267
new_access |= group_to_access(group);
268
if (new_access != vd->access) {
269
vd->access = new_access;
270
snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_INFO, kctl, ioff);
271
}
272
return 0;
273
}
274
275
static void snd_ctl_led_refresh(void)
276
{
277
unsigned int group;
278
279
for (group = 0; group < MAX_LED; group++)
280
snd_ctl_led_set_state(NULL, group_to_access(group), NULL, 0);
281
}
282
283
static void snd_ctl_led_ctl_destroy(struct snd_ctl_led_ctl *lctl)
284
{
285
list_del(&lctl->list);
286
kfree(lctl);
287
}
288
289
static void snd_ctl_led_clean(struct snd_card *card)
290
{
291
unsigned int group;
292
struct snd_ctl_led_ctl *lctl, *_lctl;
293
struct snd_ctl_led *led;
294
295
for (group = 0; group < MAX_LED; group++) {
296
led = &snd_ctl_leds[group];
297
list_for_each_entry_safe(lctl, _lctl, &led->controls, list)
298
if (!card || lctl->card == card)
299
snd_ctl_led_ctl_destroy(lctl);
300
}
301
}
302
303
static int snd_ctl_led_reset(int card_number, unsigned int group)
304
{
305
struct snd_card *card __free(snd_card_unref) = NULL;
306
struct snd_ctl_led_ctl *lctl, *_lctl;
307
struct snd_ctl_led *led;
308
struct snd_kcontrol_volatile *vd;
309
bool change = false;
310
311
card = snd_card_ref(card_number);
312
if (!card)
313
return -ENXIO;
314
315
scoped_guard(mutex, &snd_ctl_led_mutex) {
316
if (!snd_ctl_led_card_valid[card_number])
317
return -ENXIO;
318
led = &snd_ctl_leds[group];
319
list_for_each_entry_safe(lctl, _lctl, &led->controls, list)
320
if (lctl->card == card) {
321
vd = &lctl->kctl->vd[lctl->index_offset];
322
vd->access &= ~group_to_access(group);
323
snd_ctl_led_ctl_destroy(lctl);
324
change = true;
325
}
326
}
327
if (change)
328
snd_ctl_led_set_state(NULL, group_to_access(group), NULL, 0);
329
return 0;
330
}
331
332
static void snd_ctl_led_register(struct snd_card *card)
333
{
334
struct snd_kcontrol *kctl;
335
unsigned int ioff;
336
337
if (snd_BUG_ON(card->number < 0 ||
338
card->number >= ARRAY_SIZE(snd_ctl_led_card_valid)))
339
return;
340
scoped_guard(mutex, &snd_ctl_led_mutex)
341
snd_ctl_led_card_valid[card->number] = true;
342
/* the register callback is already called with held card->controls_rwsem */
343
list_for_each_entry(kctl, &card->controls, list)
344
for (ioff = 0; ioff < kctl->count; ioff++)
345
snd_ctl_led_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, kctl, ioff);
346
snd_ctl_led_refresh();
347
snd_ctl_led_sysfs_add(card);
348
}
349
350
static void snd_ctl_led_disconnect(struct snd_card *card)
351
{
352
snd_ctl_led_sysfs_remove(card);
353
scoped_guard(mutex, &snd_ctl_led_mutex) {
354
snd_ctl_led_card_valid[card->number] = false;
355
snd_ctl_led_clean(card);
356
}
357
snd_ctl_led_refresh();
358
}
359
360
static void snd_ctl_led_card_release(struct device *dev)
361
{
362
struct snd_ctl_led_card *led_card = to_led_card_dev(dev);
363
364
kfree(led_card);
365
}
366
367
static void snd_ctl_led_release(struct device *dev)
368
{
369
}
370
371
static void snd_ctl_led_dev_release(struct device *dev)
372
{
373
}
374
375
/*
376
* sysfs
377
*/
378
379
static ssize_t mode_show(struct device *dev,
380
struct device_attribute *attr, char *buf)
381
{
382
struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
383
const char *str = NULL;
384
385
switch (led->mode) {
386
case MODE_FOLLOW_MUTE: str = "follow-mute"; break;
387
case MODE_FOLLOW_ROUTE: str = "follow-route"; break;
388
case MODE_ON: str = "on"; break;
389
case MODE_OFF: str = "off"; break;
390
}
391
return sysfs_emit(buf, "%s\n", str);
392
}
393
394
static ssize_t mode_store(struct device *dev,
395
struct device_attribute *attr,
396
const char *buf, size_t count)
397
{
398
struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
399
char _buf[16];
400
size_t l = min(count, sizeof(_buf) - 1);
401
enum snd_ctl_led_mode mode;
402
403
memcpy(_buf, buf, l);
404
_buf[l] = '\0';
405
if (strstr(_buf, "mute"))
406
mode = MODE_FOLLOW_MUTE;
407
else if (strstr(_buf, "route"))
408
mode = MODE_FOLLOW_ROUTE;
409
else if (strncmp(_buf, "off", 3) == 0 || strncmp(_buf, "0", 1) == 0)
410
mode = MODE_OFF;
411
else if (strncmp(_buf, "on", 2) == 0 || strncmp(_buf, "1", 1) == 0)
412
mode = MODE_ON;
413
else
414
return count;
415
416
scoped_guard(mutex, &snd_ctl_led_mutex)
417
led->mode = mode;
418
419
snd_ctl_led_set_state(NULL, group_to_access(led->group), NULL, 0);
420
return count;
421
}
422
423
static ssize_t brightness_show(struct device *dev,
424
struct device_attribute *attr, char *buf)
425
{
426
struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
427
struct led_trigger *trig = snd_ctl_ledtrig_audio[led->trigger_type];
428
429
return sysfs_emit(buf, "%u\n", led_trigger_get_brightness(trig));
430
}
431
432
static DEVICE_ATTR_RW(mode);
433
static DEVICE_ATTR_RO(brightness);
434
435
static struct attribute *snd_ctl_led_dev_attrs[] = {
436
&dev_attr_mode.attr,
437
&dev_attr_brightness.attr,
438
NULL,
439
};
440
441
static const struct attribute_group snd_ctl_led_dev_attr_group = {
442
.attrs = snd_ctl_led_dev_attrs,
443
};
444
445
static const struct attribute_group *snd_ctl_led_dev_attr_groups[] = {
446
&snd_ctl_led_dev_attr_group,
447
NULL,
448
};
449
450
static char *find_eos(char *s)
451
{
452
while (*s && *s != ',')
453
s++;
454
if (*s)
455
s++;
456
return s;
457
}
458
459
static char *parse_uint(char *s, unsigned int *val)
460
{
461
unsigned long long res;
462
if (kstrtoull(s, 10, &res))
463
res = 0;
464
*val = res;
465
return find_eos(s);
466
}
467
468
static char *parse_string(char *s, char *val, size_t val_size)
469
{
470
if (*s == '"' || *s == '\'') {
471
char c = *s;
472
s++;
473
while (*s && *s != c) {
474
if (val_size > 1) {
475
*val++ = *s;
476
val_size--;
477
}
478
s++;
479
}
480
} else {
481
while (*s && *s != ',') {
482
if (val_size > 1) {
483
*val++ = *s;
484
val_size--;
485
}
486
s++;
487
}
488
}
489
*val = '\0';
490
if (*s)
491
s++;
492
return s;
493
}
494
495
static char *parse_iface(char *s, snd_ctl_elem_iface_t *val)
496
{
497
if (!strncasecmp(s, "card", 4))
498
*val = SNDRV_CTL_ELEM_IFACE_CARD;
499
else if (!strncasecmp(s, "mixer", 5))
500
*val = SNDRV_CTL_ELEM_IFACE_MIXER;
501
return find_eos(s);
502
}
503
504
/*
505
* These types of input strings are accepted:
506
*
507
* unsigned integer - numid (equivaled to numid=UINT)
508
* string - basic mixer name (equivalent to iface=MIXER,name=STR)
509
* numid=UINT
510
* [iface=MIXER,][device=UINT,][subdevice=UINT,]name=STR[,index=UINT]
511
*/
512
static ssize_t set_led_id(struct snd_ctl_led_card *led_card, const char *buf, size_t count,
513
bool attach)
514
{
515
char buf2[256], *s, *os;
516
struct snd_ctl_elem_id id;
517
int err;
518
519
if (strscpy(buf2, buf, sizeof(buf2)) < 0)
520
return -E2BIG;
521
memset(&id, 0, sizeof(id));
522
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
523
s = buf2;
524
while (*s) {
525
os = s;
526
if (!strncasecmp(s, "numid=", 6)) {
527
s = parse_uint(s + 6, &id.numid);
528
} else if (!strncasecmp(s, "iface=", 6)) {
529
s = parse_iface(s + 6, &id.iface);
530
} else if (!strncasecmp(s, "device=", 7)) {
531
s = parse_uint(s + 7, &id.device);
532
} else if (!strncasecmp(s, "subdevice=", 10)) {
533
s = parse_uint(s + 10, &id.subdevice);
534
} else if (!strncasecmp(s, "name=", 5)) {
535
s = parse_string(s + 5, id.name, sizeof(id.name));
536
} else if (!strncasecmp(s, "index=", 6)) {
537
s = parse_uint(s + 6, &id.index);
538
} else if (s == buf2) {
539
while (*s) {
540
if (*s < '0' || *s > '9')
541
break;
542
s++;
543
}
544
if (*s == '\0')
545
parse_uint(buf2, &id.numid);
546
else {
547
for (; *s >= ' '; s++);
548
*s = '\0';
549
strscpy(id.name, buf2, sizeof(id.name));
550
}
551
break;
552
}
553
if (*s == ',')
554
s++;
555
if (s == os)
556
break;
557
}
558
559
err = snd_ctl_led_set_id(led_card->number, &id, led_card->led->group, attach);
560
if (err < 0)
561
return err;
562
563
return count;
564
}
565
566
static ssize_t attach_store(struct device *dev,
567
struct device_attribute *attr,
568
const char *buf, size_t count)
569
{
570
struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
571
return set_led_id(led_card, buf, count, true);
572
}
573
574
static ssize_t detach_store(struct device *dev,
575
struct device_attribute *attr,
576
const char *buf, size_t count)
577
{
578
struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
579
return set_led_id(led_card, buf, count, false);
580
}
581
582
static ssize_t reset_store(struct device *dev,
583
struct device_attribute *attr,
584
const char *buf, size_t count)
585
{
586
struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
587
int err;
588
589
if (count > 0 && buf[0] == '1') {
590
err = snd_ctl_led_reset(led_card->number, led_card->led->group);
591
if (err < 0)
592
return err;
593
}
594
return count;
595
}
596
597
static ssize_t list_show(struct device *dev,
598
struct device_attribute *attr, char *buf)
599
{
600
struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
601
struct snd_card *card __free(snd_card_unref) = NULL;
602
struct snd_ctl_led_ctl *lctl;
603
size_t l = 0;
604
605
card = snd_card_ref(led_card->number);
606
if (!card)
607
return -ENXIO;
608
guard(rwsem_read)(&card->controls_rwsem);
609
guard(mutex)(&snd_ctl_led_mutex);
610
if (snd_ctl_led_card_valid[led_card->number]) {
611
list_for_each_entry(lctl, &led_card->led->controls, list) {
612
if (lctl->card != card)
613
continue;
614
if (l)
615
l += sysfs_emit_at(buf, l, " ");
616
l += sysfs_emit_at(buf, l, "%u",
617
lctl->kctl->id.numid + lctl->index_offset);
618
}
619
}
620
return l;
621
}
622
623
static DEVICE_ATTR_WO(attach);
624
static DEVICE_ATTR_WO(detach);
625
static DEVICE_ATTR_WO(reset);
626
static DEVICE_ATTR_RO(list);
627
628
static struct attribute *snd_ctl_led_card_attrs[] = {
629
&dev_attr_attach.attr,
630
&dev_attr_detach.attr,
631
&dev_attr_reset.attr,
632
&dev_attr_list.attr,
633
NULL,
634
};
635
636
static const struct attribute_group snd_ctl_led_card_attr_group = {
637
.attrs = snd_ctl_led_card_attrs,
638
};
639
640
static const struct attribute_group *snd_ctl_led_card_attr_groups[] = {
641
&snd_ctl_led_card_attr_group,
642
NULL,
643
};
644
645
static struct device snd_ctl_led_dev;
646
647
static void snd_ctl_led_sysfs_add(struct snd_card *card)
648
{
649
unsigned int group;
650
struct snd_ctl_led_card *led_card;
651
struct snd_ctl_led *led;
652
char link_name[32];
653
654
for (group = 0; group < MAX_LED; group++) {
655
led = &snd_ctl_leds[group];
656
led_card = kzalloc(sizeof(*led_card), GFP_KERNEL);
657
if (!led_card)
658
goto cerr2;
659
led_card->number = card->number;
660
led_card->led = led;
661
device_initialize(&led_card->dev);
662
led_card->dev.release = snd_ctl_led_card_release;
663
if (dev_set_name(&led_card->dev, "card%d", card->number) < 0)
664
goto cerr;
665
led_card->dev.parent = &led->dev;
666
led_card->dev.groups = snd_ctl_led_card_attr_groups;
667
if (device_add(&led_card->dev))
668
goto cerr;
669
led->cards[card->number] = led_card;
670
snprintf(link_name, sizeof(link_name), "led-%s", led->name);
671
if (sysfs_create_link(&card->ctl_dev->kobj, &led_card->dev.kobj,
672
link_name))
673
dev_err(card->dev,
674
"%s: can't create symlink to controlC%i device\n",
675
__func__, card->number);
676
if (sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj,
677
"card"))
678
dev_err(card->dev,
679
"%s: can't create symlink to card%i\n",
680
__func__, card->number);
681
682
continue;
683
cerr:
684
put_device(&led_card->dev);
685
cerr2:
686
dev_err(card->dev, "snd_ctl_led: unable to add card%d", card->number);
687
}
688
}
689
690
static void snd_ctl_led_sysfs_remove(struct snd_card *card)
691
{
692
unsigned int group;
693
struct snd_ctl_led_card *led_card;
694
struct snd_ctl_led *led;
695
char link_name[32];
696
697
for (group = 0; group < MAX_LED; group++) {
698
led = &snd_ctl_leds[group];
699
led_card = led->cards[card->number];
700
if (!led_card)
701
continue;
702
snprintf(link_name, sizeof(link_name), "led-%s", led->name);
703
sysfs_remove_link(&card->ctl_dev->kobj, link_name);
704
sysfs_remove_link(&led_card->dev.kobj, "card");
705
device_unregister(&led_card->dev);
706
led->cards[card->number] = NULL;
707
}
708
}
709
710
/*
711
* Control layer registration
712
*/
713
static struct snd_ctl_layer_ops snd_ctl_led_lops = {
714
.module_name = SND_CTL_LAYER_MODULE_LED,
715
.lregister = snd_ctl_led_register,
716
.ldisconnect = snd_ctl_led_disconnect,
717
.lnotify = snd_ctl_led_notify,
718
};
719
720
static int __init snd_ctl_led_init(void)
721
{
722
struct snd_ctl_led *led;
723
unsigned int group;
724
725
led_trigger_register_simple("audio-mute", &snd_ctl_ledtrig_audio[LED_AUDIO_MUTE]);
726
led_trigger_register_simple("audio-micmute", &snd_ctl_ledtrig_audio[LED_AUDIO_MICMUTE]);
727
728
device_initialize(&snd_ctl_led_dev);
729
snd_ctl_led_dev.class = &sound_class;
730
snd_ctl_led_dev.release = snd_ctl_led_dev_release;
731
dev_set_name(&snd_ctl_led_dev, "ctl-led");
732
if (device_add(&snd_ctl_led_dev)) {
733
put_device(&snd_ctl_led_dev);
734
return -ENOMEM;
735
}
736
for (group = 0; group < MAX_LED; group++) {
737
led = &snd_ctl_leds[group];
738
INIT_LIST_HEAD(&led->controls);
739
device_initialize(&led->dev);
740
led->dev.parent = &snd_ctl_led_dev;
741
led->dev.release = snd_ctl_led_release;
742
led->dev.groups = snd_ctl_led_dev_attr_groups;
743
dev_set_name(&led->dev, led->name);
744
if (device_add(&led->dev)) {
745
put_device(&led->dev);
746
for (; group > 0; group--) {
747
led = &snd_ctl_leds[group - 1];
748
device_unregister(&led->dev);
749
}
750
device_unregister(&snd_ctl_led_dev);
751
return -ENOMEM;
752
}
753
}
754
snd_ctl_register_layer(&snd_ctl_led_lops);
755
return 0;
756
}
757
758
static void __exit snd_ctl_led_exit(void)
759
{
760
struct snd_ctl_led *led;
761
struct snd_card *card;
762
unsigned int group, card_number;
763
764
snd_ctl_disconnect_layer(&snd_ctl_led_lops);
765
for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
766
if (!snd_ctl_led_card_valid[card_number])
767
continue;
768
card = snd_card_ref(card_number);
769
if (card) {
770
snd_ctl_led_sysfs_remove(card);
771
snd_card_unref(card);
772
}
773
}
774
for (group = 0; group < MAX_LED; group++) {
775
led = &snd_ctl_leds[group];
776
device_unregister(&led->dev);
777
}
778
device_unregister(&snd_ctl_led_dev);
779
snd_ctl_led_clean(NULL);
780
781
led_trigger_unregister_simple(snd_ctl_ledtrig_audio[LED_AUDIO_MUTE]);
782
led_trigger_unregister_simple(snd_ctl_ledtrig_audio[LED_AUDIO_MICMUTE]);
783
}
784
785
module_init(snd_ctl_led_init)
786
module_exit(snd_ctl_led_exit)
787
788
MODULE_ALIAS("ledtrig:audio-mute");
789
MODULE_ALIAS("ledtrig:audio-micmute");
790
791