~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/core/control_led.c

Version: ~ [ linux-6.0-rc1 ] ~ [ linux-5.19.1 ] ~ [ linux-5.18.17 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.60 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.136 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.210 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.255 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.290 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.325 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.302 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp