cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

control_led.c (20428B)


      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 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
    411static 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, "0", 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
    441static 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
    449static DEVICE_ATTR_RW(mode);
    450static DEVICE_ATTR_RO(brightness);
    451
    452static struct attribute *snd_ctl_led_dev_attrs[] = {
    453	&dev_attr_mode.attr,
    454	&dev_attr_brightness.attr,
    455	NULL,
    456};
    457
    458static const struct attribute_group snd_ctl_led_dev_attr_group = {
    459	.attrs = snd_ctl_led_dev_attrs,
    460};
    461
    462static const struct attribute_group *snd_ctl_led_dev_attr_groups[] = {
    463	&snd_ctl_led_dev_attr_group,
    464	NULL,
    465};
    466
    467static char *find_eos(char *s)
    468{
    469	while (*s && *s != ',')
    470		s++;
    471	if (*s)
    472		s++;
    473	return s;
    474}
    475
    476static 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
    485static 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
    512static char *parse_iface(char *s, snd_ctl_elem_iface_t *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 */
    529static 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 < '0' || *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
    584static 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
    592static 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
    600static 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
    615static 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
    649static DEVICE_ATTR_WO(attach);
    650static DEVICE_ATTR_WO(detach);
    651static DEVICE_ATTR_WO(reset);
    652static DEVICE_ATTR_RO(list);
    653
    654static 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
    662static const struct attribute_group snd_ctl_led_card_attr_group = {
    663	.attrs = snd_ctl_led_card_attrs,
    664};
    665
    666static const struct attribute_group *snd_ctl_led_card_attr_groups[] = {
    667	&snd_ctl_led_card_attr_group,
    668	NULL,
    669};
    670
    671static struct device snd_ctl_led_dev;
    672
    673static 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;
    703cerr:
    704		put_device(&led_card->dev);
    705cerr2:
    706		printk(KERN_ERR "snd_ctl_led: unable to add card%d", card->number);
    707	}
    708}
    709
    710static 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 */
    733static 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
    740static 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
    775static 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
    799module_init(snd_ctl_led_init)
    800module_exit(snd_ctl_led_exit)