brintos

brintos / linux-shallow public Read only

0
0
Text · 20.1 KiB · 65a1ebe Raw
784 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  LED state routines for driver control interface4 *  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 led_trigger *snd_ctl_ledtrig_audio[NUM_AUDIO_LEDS];57static 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 72static void snd_ctl_led_sysfs_add(struct snd_card *card);73static 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 82static 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 88static inline unsigned int group_to_access(unsigned int group)89{90	return (group + 1) << SNDRV_CTL_ELEM_ACCESS_LED_SHIFT;91}92 93static 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 */105static 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 138static 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 185static 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 200static 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 216static 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 243DEFINE_FREE(snd_card_unref, struct snd_card *, if (_T) snd_card_unref(_T))244 245static 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 275static 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 283static void snd_ctl_led_ctl_destroy(struct snd_ctl_led_ctl *lctl)284{285	list_del(&lctl->list);286	kfree(lctl);287}288 289static 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 303static 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 332static 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 350static 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 360static 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 367static void snd_ctl_led_release(struct device *dev)368{369}370 371static void snd_ctl_led_dev_release(struct device *dev)372{373}374 375/*376 * sysfs377 */378 379static 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 394static 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	else414		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 423static 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 432static DEVICE_ATTR_RW(mode);433static DEVICE_ATTR_RO(brightness);434 435static struct attribute *snd_ctl_led_dev_attrs[] = {436	&dev_attr_mode.attr,437	&dev_attr_brightness.attr,438	NULL,439};440 441static const struct attribute_group snd_ctl_led_dev_attr_group = {442	.attrs = snd_ctl_led_dev_attrs,443};444 445static const struct attribute_group *snd_ctl_led_dev_attr_groups[] = {446	&snd_ctl_led_dev_attr_group,447	NULL,448};449 450static char *find_eos(char *s)451{452	while (*s && *s != ',')453		s++;454	if (*s)455		s++;456	return s;457}458 459static 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 468static 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 495static 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=UINT510 *   [iface=MIXER,][device=UINT,][subdevice=UINT,]name=STR[,index=UINT]511 */512static 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 566static 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 574static 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 582static 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 597static 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 623static DEVICE_ATTR_WO(attach);624static DEVICE_ATTR_WO(detach);625static DEVICE_ATTR_WO(reset);626static DEVICE_ATTR_RO(list);627 628static 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 636static const struct attribute_group snd_ctl_led_card_attr_group = {637	.attrs = snd_ctl_led_card_attrs,638};639 640static const struct attribute_group *snd_ctl_led_card_attr_groups[] = {641	&snd_ctl_led_card_attr_group,642	NULL,643};644 645static struct device snd_ctl_led_dev;646 647static 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		WARN(sysfs_create_link(&card->ctl_dev->kobj, &led_card->dev.kobj, link_name),672			"can't create symlink to controlC%i device\n", card->number);673		WARN(sysfs_create_link(&led_card->dev.kobj, &card->card_dev.kobj, "card"),674			"can't create symlink to card%i\n", card->number);675 676		continue;677cerr:678		put_device(&led_card->dev);679cerr2:680		dev_err(card->dev, "snd_ctl_led: unable to add card%d", card->number);681	}682}683 684static void snd_ctl_led_sysfs_remove(struct snd_card *card)685{686	unsigned int group;687	struct snd_ctl_led_card *led_card;688	struct snd_ctl_led *led;689	char link_name[32];690 691	for (group = 0; group < MAX_LED; group++) {692		led = &snd_ctl_leds[group];693		led_card = led->cards[card->number];694		if (!led_card)695			continue;696		snprintf(link_name, sizeof(link_name), "led-%s", led->name);697		sysfs_remove_link(&card->ctl_dev->kobj, link_name);698		sysfs_remove_link(&led_card->dev.kobj, "card");699		device_unregister(&led_card->dev);700		led->cards[card->number] = NULL;701	}702}703 704/*705 * Control layer registration706 */707static struct snd_ctl_layer_ops snd_ctl_led_lops = {708	.module_name = SND_CTL_LAYER_MODULE_LED,709	.lregister = snd_ctl_led_register,710	.ldisconnect = snd_ctl_led_disconnect,711	.lnotify = snd_ctl_led_notify,712};713 714static int __init snd_ctl_led_init(void)715{716	struct snd_ctl_led *led;717	unsigned int group;718 719	led_trigger_register_simple("audio-mute", &snd_ctl_ledtrig_audio[LED_AUDIO_MUTE]);720	led_trigger_register_simple("audio-micmute", &snd_ctl_ledtrig_audio[LED_AUDIO_MICMUTE]);721 722	device_initialize(&snd_ctl_led_dev);723	snd_ctl_led_dev.class = &sound_class;724	snd_ctl_led_dev.release = snd_ctl_led_dev_release;725	dev_set_name(&snd_ctl_led_dev, "ctl-led");726	if (device_add(&snd_ctl_led_dev)) {727		put_device(&snd_ctl_led_dev);728		return -ENOMEM;729	}730	for (group = 0; group < MAX_LED; group++) {731		led = &snd_ctl_leds[group];732		INIT_LIST_HEAD(&led->controls);733		device_initialize(&led->dev);734		led->dev.parent = &snd_ctl_led_dev;735		led->dev.release = snd_ctl_led_release;736		led->dev.groups = snd_ctl_led_dev_attr_groups;737		dev_set_name(&led->dev, led->name);738		if (device_add(&led->dev)) {739			put_device(&led->dev);740			for (; group > 0; group--) {741				led = &snd_ctl_leds[group - 1];742				device_unregister(&led->dev);743			}744			device_unregister(&snd_ctl_led_dev);745			return -ENOMEM;746		}747	}748	snd_ctl_register_layer(&snd_ctl_led_lops);749	return 0;750}751 752static void __exit snd_ctl_led_exit(void)753{754	struct snd_ctl_led *led;755	struct snd_card *card;756	unsigned int group, card_number;757 758	snd_ctl_disconnect_layer(&snd_ctl_led_lops);759	for (card_number = 0; card_number < SNDRV_CARDS; card_number++) {760		if (!snd_ctl_led_card_valid[card_number])761			continue;762		card = snd_card_ref(card_number);763		if (card) {764			snd_ctl_led_sysfs_remove(card);765			snd_card_unref(card);766		}767	}768	for (group = 0; group < MAX_LED; group++) {769		led = &snd_ctl_leds[group];770		device_unregister(&led->dev);771	}772	device_unregister(&snd_ctl_led_dev);773	snd_ctl_led_clean(NULL);774 775	led_trigger_unregister_simple(snd_ctl_ledtrig_audio[LED_AUDIO_MUTE]);776	led_trigger_unregister_simple(snd_ctl_ledtrig_audio[LED_AUDIO_MICMUTE]);777}778 779module_init(snd_ctl_led_init)780module_exit(snd_ctl_led_exit)781 782MODULE_ALIAS("ledtrig:audio-mute");783MODULE_ALIAS("ledtrig:audio-micmute");784