linux/sound/core/control_led.c
<<
>>
Prefs
   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
  13MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
  14MODULE_DESCRIPTION("ALSA control interface to LED trigger code.");
  15MODULE_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
  23enum snd_ctl_led_mode {
  24         MODE_FOLLOW_MUTE = 0,
  25         MODE_FOLLOW_ROUTE,
  26         MODE_OFF,
  27         MODE_ON,
  28};
  29
  30struct snd_ctl_led_card {
  31        struct device dev;
  32        int number;
  33        struct snd_ctl_led *led;
  34};
  35
  36struct 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
  46struct 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
  54static DEFINE_MUTEX(snd_ctl_led_mutex);
  55static bool snd_ctl_led_card_valid[SNDRV_CARDS];
  56static 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
  71static void snd_ctl_led_sysfs_add(struct snd_card *card);
  72static 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
  81static 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
  87static inline unsigned int group_to_access(unsigned int group)
  88{
  89        return (group + 1) << SNDRV_CTL_ELEM_ACCESS_LED_SHIFT;
  90}
  91
  92static 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 */
 104static 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
 137static 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
 183static 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
 198static 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
 215static 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
 242static 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                }
 273unlock:
 274                up_write(&card->controls_rwsem);
 275                snd_card_unref(card);
 276        } else {
 277                err = -ENXIO;
 278        }
 279        return err;
 280}
 281
 282static 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
 290static void snd_ctl_led_ctl_destroy(struct snd_ctl_led_ctl *lctl)
 291{
 292        list_del(&lctl->list);
 293        kfree(lctl);
 294}
 295
 296static 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];
 304repeat:
 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
 313static 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];
 332repeat:
 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
 348static 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
 367static 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
 377static 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
 384static void snd_ctl_led_release(struct device *dev)
 385{
 386}
 387
 388static void snd_ctl_led_dev_release(struct device *dev)
 389{
 390}
 391
 392/*
 393 * sysfs
 394 */
 395
 396static ssize_t show_mode(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
 411static ssize_t store_mode(struct device *dev, struct device_attribute *attr,
 412                          const char *buf, size_t count)
 413{
 414        struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
 415        char _buf[16];
 416        size_t l = min(count, sizeof(_buf) - 1);
 417        enum snd_ctl_led_mode mode;
 418
 419        memcpy(_buf, buf, l);
 420        _buf[l] = '\0';
 421        if (strstr(_buf, "mute"))
 422                mode = MODE_FOLLOW_MUTE;
 423        else if (strstr(_buf, "route"))
 424                mode = MODE_FOLLOW_ROUTE;
 425        else if (strncmp(_buf, "off", 3) == 0 || strncmp(_buf, "0", 1) == 0)
 426                mode = MODE_OFF;
 427        else if (strncmp(_buf, "on", 2) == 0 || strncmp(_buf, "1", 1) == 0)
 428                mode = MODE_ON;
 429        else
 430                return count;
 431
 432        mutex_lock(&snd_ctl_led_mutex);
 433        led->mode = mode;
 434        mutex_unlock(&snd_ctl_led_mutex);
 435
 436        snd_ctl_led_set_state(NULL, group_to_access(led->group), NULL, 0);
 437        return count;
 438}
 439
 440static ssize_t show_brightness(struct device *dev,
 441                               struct device_attribute *attr, char *buf)
 442{
 443        struct snd_ctl_led *led = container_of(dev, struct snd_ctl_led, dev);
 444
 445        return sprintf(buf, "%u\n", ledtrig_audio_get(led->trigger_type));
 446}
 447
 448static DEVICE_ATTR(mode, 0644, show_mode, store_mode);
 449static DEVICE_ATTR(brightness, 0444, show_brightness, NULL);
 450
 451static struct attribute *snd_ctl_led_dev_attrs[] = {
 452        &dev_attr_mode.attr,
 453        &dev_attr_brightness.attr,
 454        NULL,
 455};
 456
 457static const struct attribute_group snd_ctl_led_dev_attr_group = {
 458        .attrs = snd_ctl_led_dev_attrs,
 459};
 460
 461static const struct attribute_group *snd_ctl_led_dev_attr_groups[] = {
 462        &snd_ctl_led_dev_attr_group,
 463        NULL,
 464};
 465
 466static char *find_eos(char *s)
 467{
 468        while (*s && *s != ',')
 469                s++;
 470        if (*s)
 471                s++;
 472        return s;
 473}
 474
 475static char *parse_uint(char *s, unsigned int *val)
 476{
 477        unsigned long long res;
 478        if (kstrtoull(s, 10, &res))
 479                res = 0;
 480        *val = res;
 481        return find_eos(s);
 482}
 483
 484static char *parse_string(char *s, char *val, size_t val_size)
 485{
 486        if (*s == '"' || *s == '\'') {
 487                char c = *s;
 488                s++;
 489                while (*s && *s != c) {
 490                        if (val_size > 1) {
 491                                *val++ = *s;
 492                                val_size--;
 493                        }
 494                        s++;
 495                }
 496        } else {
 497                while (*s && *s != ',') {
 498                        if (val_size > 1) {
 499                                *val++ = *s;
 500                                val_size--;
 501                        }
 502                        s++;
 503                }
 504        }
 505        *val = '\0';
 506        if (*s)
 507                s++;
 508        return s;
 509}
 510
 511static char *parse_iface(char *s, unsigned int *val)
 512{
 513        if (!strncasecmp(s, "card", 4))
 514                *val = SNDRV_CTL_ELEM_IFACE_CARD;
 515        else if (!strncasecmp(s, "mixer", 5))
 516                *val = SNDRV_CTL_ELEM_IFACE_MIXER;
 517        return find_eos(s);
 518}
 519
 520/*
 521 * These types of input strings are accepted:
 522 *
 523 *   unsigned integer - numid (equivaled to numid=UINT)
 524 *   string - basic mixer name (equivalent to iface=MIXER,name=STR)
 525 *   numid=UINT
 526 *   [iface=MIXER,][device=UINT,][subdevice=UINT,]name=STR[,index=UINT]
 527 */
 528static ssize_t set_led_id(struct snd_ctl_led_card *led_card, const char *buf, size_t count,
 529                          bool attach)
 530{
 531        char buf2[256], *s, *os;
 532        size_t len = max(sizeof(s) - 1, count);
 533        struct snd_ctl_elem_id id;
 534        int err;
 535
 536        strncpy(buf2, buf, len);
 537        buf2[len] = '\0';
 538        memset(&id, 0, sizeof(id));
 539        id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 540        s = buf2;
 541        while (*s) {
 542                os = s;
 543                if (!strncasecmp(s, "numid=", 6)) {
 544                        s = parse_uint(s + 6, &id.numid);
 545                } else if (!strncasecmp(s, "iface=", 6)) {
 546                        s = parse_iface(s + 6, &id.iface);
 547                } else if (!strncasecmp(s, "device=", 7)) {
 548                        s = parse_uint(s + 7, &id.device);
 549                } else if (!strncasecmp(s, "subdevice=", 10)) {
 550                        s = parse_uint(s + 10, &id.subdevice);
 551                } else if (!strncasecmp(s, "name=", 5)) {
 552                        s = parse_string(s + 5, id.name, sizeof(id.name));
 553                } else if (!strncasecmp(s, "index=", 6)) {
 554                        s = parse_uint(s + 6, &id.index);
 555                } else if (s == buf2) {
 556                        while (*s) {
 557                                if (*s < '0' || *s > '9')
 558                                        break;
 559                                s++;
 560                        }
 561                        if (*s == '\0')
 562                                parse_uint(buf2, &id.numid);
 563                        else {
 564                                for (; *s >= ' '; s++);
 565                                *s = '\0';
 566                                strlcpy(id.name, buf2, sizeof(id.name));
 567                        }
 568                        break;
 569                }
 570                if (*s == ',')
 571                        s++;
 572                if (s == os)
 573                        break;
 574        }
 575
 576        err = snd_ctl_led_set_id(led_card->number, &id, led_card->led->group, attach);
 577        if (err < 0)
 578                return err;
 579
 580        return count;
 581}
 582
 583static ssize_t parse_attach(struct device *dev, 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        return set_led_id(led_card, buf, count, true);
 588}
 589
 590static ssize_t parse_detach(struct device *dev, struct device_attribute *attr,
 591                            const char *buf, size_t count)
 592{
 593        struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
 594        return set_led_id(led_card, buf, count, false);
 595}
 596
 597static ssize_t ctl_reset(struct device *dev, struct device_attribute *attr,
 598                         const char *buf, size_t count)
 599{
 600        struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
 601        int err;
 602
 603        if (count > 0 && buf[0] == '1') {
 604                err = snd_ctl_led_reset(led_card->number, led_card->led->group);
 605                if (err < 0)
 606                        return err;
 607        }
 608        return count;
 609}
 610
 611static ssize_t ctl_list(struct device *dev,
 612                        struct device_attribute *attr, char *buf)
 613{
 614        struct snd_ctl_led_card *led_card = container_of(dev, struct snd_ctl_led_card, dev);
 615        struct snd_card *card;
 616        struct snd_ctl_led_ctl *lctl;
 617        char *buf2 = buf;
 618        size_t l;
 619
 620        card = snd_card_ref(led_card->number);
 621        if (!card)
 622                return -ENXIO;
 623        down_read(&card->controls_rwsem);
 624        mutex_lock(&snd_ctl_led_mutex);
 625        if (snd_ctl_led_card_valid[led_card->number]) {
 626                list_for_each_entry(lctl, &led_card->led->controls, list)
 627                        if (lctl->card == card) {
 628                                if (buf2 - buf > PAGE_SIZE - 16)
 629                                        break;
 630                                if (buf2 != buf)
 631                                        *buf2++ = ' ';
 632                                l = scnprintf(buf2, 15, "%u",
 633                                                lctl->kctl->id.numid +
 634                                                        lctl->index_offset);
 635                                buf2[l] = '\0';
 636                                buf2 += l + 1;
 637                        }
 638        }
 639        mutex_unlock(&snd_ctl_led_mutex);
 640        up_read(&card->controls_rwsem);
 641        snd_card_unref(card);
 642        return buf2 - buf;
 643}
 644
 645static DEVICE_ATTR(attach, 0200, NULL, parse_attach);
 646static DEVICE_ATTR(detach, 0200, NULL, parse_detach);
 647static DEVICE_ATTR(reset, 0200, NULL, ctl_reset);
 648static DEVICE_ATTR(list, 0444, ctl_list, NULL);
 649
 650static struct attribute *snd_ctl_led_card_attrs[] = {
 651        &dev_attr_attach.attr,
 652        &dev_attr_detach.attr,
 653        &dev_attr_reset.attr,
 654        &dev_attr_list.attr,
 655        NULL,
 656};
 657
 658static const struct attribute_group snd_ctl_led_card_attr_group = {
 659        .attrs = snd_ctl_led_card_attrs,
 660};
 661
 662static const struct attribute_group *snd_ctl_led_card_attr_groups[] = {
 663        &snd_ctl_led_card_attr_group,
 664        NULL,
 665};
 666
 667static struct device snd_ctl_led_dev;
 668
 669static void snd_ctl_led_sysfs_add(struct snd_card *card)
 670{
 671        unsigned int group;
 672        struct snd_ctl_led_card *led_card;
 673        struct snd_ctl_led *led;
 674        char link_name[32];
 675
 676        for (group = 0; group < MAX_LED; group++) {
 677                led = &snd_ctl_leds[group];
 678                led_card = kzalloc(sizeof(*led_card), GFP_KERNEL);
 679                if (!led_card)
 680                        goto cerr2;
 681                led_card->number = card->number;
 682                led_card->led = led;
 683                device_initialize(&led_card->dev);
 684                led_card->dev.release = snd_ctl_led_card_release;
 685                if (dev_set_name(&led_card->dev, "card%d", card->number) < 0)
 686                        goto cerr;
 687                led_card->dev.parent = &led->dev;
 688                led_card->dev.groups = snd_ctl_led_card_attr_groups;
 689                if (device_add(&led_card->dev))
 690                        goto cerr;
 691                led->cards[card->number] = led_card;
 692                snprintf(link_name, sizeof(link_name), "led-%s", led->name);
 693                WARN(sysfs_create_link(&card->ctl_dev.kobj, &led_card->dev.kobj, link_name),
 694                        "can't create symlink to controlC%i device\n", card->number);
 695                WARN(sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj, "card"),
 696                        "can't create symlink to card%i\n", card->number);
 697
 698                continue;
 699cerr:
 700                put_device(&led_card->dev);
 701cerr2:
 702                printk(KERN_ERR "snd_ctl_led: unable to add card%d", card->number);
 703        }
 704}
 705
 706static void snd_ctl_led_sysfs_remove(struct snd_card *card)
 707{
 708        unsigned int group;
 709        struct snd_ctl_led_card *led_card;
 710        struct snd_ctl_led *led;
 711        char link_name[32];
 712
 713        for (group = 0; group < MAX_LED; group++) {
 714                led = &snd_ctl_leds[group];
 715                led_card = led->cards[card->number];
 716                if (!led_card)
 717                        continue;
 718                snprintf(link_name, sizeof(link_name), "led-%s", led->name);
 719                sysfs_remove_link(&card->ctl_dev.kobj, link_name);
 720                sysfs_remove_link(&led_card->dev.kobj, "card");
 721                device_unregister(&led_card->dev);
 722                led->cards[card->number] = NULL;
 723        }
 724}
 725
 726/*
 727 * Control layer registration
 728 */
 729static struct snd_ctl_layer_ops snd_ctl_led_lops = {
 730        .module_name = SND_CTL_LAYER_MODULE_LED,
 731        .lregister = snd_ctl_led_register,
 732        .ldisconnect = snd_ctl_led_disconnect,
 733        .lnotify = snd_ctl_led_notify,
 734};
 735
 736static int __init snd_ctl_led_init(void)
 737{
 738        struct snd_ctl_led *led;
 739        unsigned int group;
 740
 741        device_initialize(&snd_ctl_led_dev);
 742        snd_ctl_led_dev.class = sound_class;
 743        snd_ctl_led_dev.release = snd_ctl_led_dev_release;
 744        dev_set_name(&snd_ctl_led_dev, "ctl-led");
 745        if (device_add(&snd_ctl_led_dev)) {
 746                put_device(&snd_ctl_led_dev);
 747                return -ENOMEM;
 748        }
 749        for (group = 0; group < MAX_LED; group++) {
 750                led = &snd_ctl_leds[group];
 751                INIT_LIST_HEAD(&led->controls);
 752                device_initialize(&led->dev);
 753                led->dev.parent = &snd_ctl_led_dev;
 754                led->dev.release = snd_ctl_led_release;
 755                led->dev.groups = snd_ctl_led_dev_attr_groups;
 756                dev_set_name(&led->dev, led->name);
 757                if (device_add(&led->dev)) {
 758                        put_device(&led->dev);
 759                        for (; group > 0; group--) {
 760                                led = &snd_ctl_leds[group - 1];
 761                                device_unregister(&led->dev);
 762                        }
 763                        device_unregister(&snd_ctl_led_dev);
 764                        return -ENOMEM;
 765                }
 766        }
 767        snd_ctl_register_layer(&snd_ctl_led_lops);
 768        return 0;
 769}
 770
 771static void __exit snd_ctl_led_exit(void)
 772{
 773        struct snd_ctl_led *led;
 774        struct snd_card *card;
 775        unsigned int group, card_number;
 776
 777        snd_ctl_disconnect_layer(&snd_ctl_led_lops);
 778        for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {
 779                if (!snd_ctl_led_card_valid[card_number])
 780                        continue;
 781                card = snd_card_ref(card_number);
 782                if (card) {
 783                        snd_ctl_led_sysfs_remove(card);
 784                        snd_card_unref(card);
 785                }
 786        }
 787        for (group = 0; group < MAX_LED; group++) {
 788                led = &snd_ctl_leds[group];
 789                device_unregister(&led->dev);
 790        }
 791        device_unregister(&snd_ctl_led_dev);
 792        snd_ctl_led_clean(NULL);
 793}
 794
 795module_init(snd_ctl_led_init)
 796module_exit(snd_ctl_led_exit)
 797