brintos

brintos / linux-shallow public Read only

0
0
Text · 39.6 KiB · 05fc891 Raw
1381 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 *  OSS emulation layer for the mixer interface4 *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>5 */6 7#include <linux/init.h>8#include <linux/slab.h>9#include <linux/time.h>10#include <linux/string.h>11#include <linux/module.h>12#include <linux/compat.h>13#include <sound/core.h>14#include <sound/minors.h>15#include <sound/control.h>16#include <sound/info.h>17#include <sound/mixer_oss.h>18#include <linux/soundcard.h>19 20#define OSS_ALSAEMULVER         _SIOR ('M', 249, int)21 22MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");23MODULE_DESCRIPTION("Mixer OSS emulation for ALSA.");24MODULE_LICENSE("GPL");25MODULE_ALIAS_SNDRV_MINOR(SNDRV_MINOR_OSS_MIXER);26 27static int snd_mixer_oss_open(struct inode *inode, struct file *file)28{29	struct snd_card *card;30	struct snd_mixer_oss_file *fmixer;31	int err;32 33	err = nonseekable_open(inode, file);34	if (err < 0)35		return err;36 37	card = snd_lookup_oss_minor_data(iminor(inode),38					 SNDRV_OSS_DEVICE_TYPE_MIXER);39	if (card == NULL)40		return -ENODEV;41	if (card->mixer_oss == NULL) {42		snd_card_unref(card);43		return -ENODEV;44	}45	err = snd_card_file_add(card, file);46	if (err < 0) {47		snd_card_unref(card);48		return err;49	}50	fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL);51	if (fmixer == NULL) {52		snd_card_file_remove(card, file);53		snd_card_unref(card);54		return -ENOMEM;55	}56	fmixer->card = card;57	fmixer->mixer = card->mixer_oss;58	file->private_data = fmixer;59	if (!try_module_get(card->module)) {60		kfree(fmixer);61		snd_card_file_remove(card, file);62		snd_card_unref(card);63		return -EFAULT;64	}65	snd_card_unref(card);66	return 0;67}68 69static int snd_mixer_oss_release(struct inode *inode, struct file *file)70{71	struct snd_mixer_oss_file *fmixer;72 73	if (file->private_data) {74		fmixer = file->private_data;75		module_put(fmixer->card->module);76		snd_card_file_remove(fmixer->card, file);77		kfree(fmixer);78	}79	return 0;80}81 82static int snd_mixer_oss_info(struct snd_mixer_oss_file *fmixer,83			      mixer_info __user *_info)84{85	struct snd_card *card = fmixer->card;86	struct snd_mixer_oss *mixer = fmixer->mixer;87	struct mixer_info info;88	89	memset(&info, 0, sizeof(info));90	strscpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));91	strscpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));92	info.modify_counter = card->mixer_oss_change_count;93	if (copy_to_user(_info, &info, sizeof(info)))94		return -EFAULT;95	return 0;96}97 98static int snd_mixer_oss_info_obsolete(struct snd_mixer_oss_file *fmixer,99				       _old_mixer_info __user *_info)100{101	struct snd_card *card = fmixer->card;102	struct snd_mixer_oss *mixer = fmixer->mixer;103	_old_mixer_info info;104	105	memset(&info, 0, sizeof(info));106	strscpy(info.id, mixer && mixer->id[0] ? mixer->id : card->driver, sizeof(info.id));107	strscpy(info.name, mixer && mixer->name[0] ? mixer->name : card->mixername, sizeof(info.name));108	if (copy_to_user(_info, &info, sizeof(info)))109		return -EFAULT;110	return 0;111}112 113static int snd_mixer_oss_caps(struct snd_mixer_oss_file *fmixer)114{115	struct snd_mixer_oss *mixer = fmixer->mixer;116	int result = 0;117 118	if (mixer == NULL)119		return -EIO;120	if (mixer->get_recsrc && mixer->put_recsrc)121		result |= SOUND_CAP_EXCL_INPUT;122	return result;123}124 125static int snd_mixer_oss_devmask(struct snd_mixer_oss_file *fmixer)126{127	struct snd_mixer_oss *mixer = fmixer->mixer;128	struct snd_mixer_oss_slot *pslot;129	int result = 0, chn;130 131	if (mixer == NULL)132		return -EIO;133	guard(mutex)(&mixer->reg_mutex);134	for (chn = 0; chn < 31; chn++) {135		pslot = &mixer->slots[chn];136		if (pslot->put_volume || pslot->put_recsrc)137			result |= 1 << chn;138	}139	return result;140}141 142static int snd_mixer_oss_stereodevs(struct snd_mixer_oss_file *fmixer)143{144	struct snd_mixer_oss *mixer = fmixer->mixer;145	struct snd_mixer_oss_slot *pslot;146	int result = 0, chn;147 148	if (mixer == NULL)149		return -EIO;150	guard(mutex)(&mixer->reg_mutex);151	for (chn = 0; chn < 31; chn++) {152		pslot = &mixer->slots[chn];153		if (pslot->put_volume && pslot->stereo)154			result |= 1 << chn;155	}156	return result;157}158 159static int snd_mixer_oss_recmask(struct snd_mixer_oss_file *fmixer)160{161	struct snd_mixer_oss *mixer = fmixer->mixer;162	int result = 0;163 164	if (mixer == NULL)165		return -EIO;166	guard(mutex)(&mixer->reg_mutex);167	if (mixer->put_recsrc && mixer->get_recsrc) {	/* exclusive */168		result = mixer->mask_recsrc;169	} else {170		struct snd_mixer_oss_slot *pslot;171		int chn;172		for (chn = 0; chn < 31; chn++) {173			pslot = &mixer->slots[chn];174			if (pslot->put_recsrc)175				result |= 1 << chn;176		}177	}178	return result;179}180 181static int snd_mixer_oss_get_recsrc(struct snd_mixer_oss_file *fmixer)182{183	struct snd_mixer_oss *mixer = fmixer->mixer;184	int result = 0;185 186	if (mixer == NULL)187		return -EIO;188	guard(mutex)(&mixer->reg_mutex);189	if (mixer->put_recsrc && mixer->get_recsrc) {	/* exclusive */190		unsigned int index;191		result = mixer->get_recsrc(fmixer, &index);192		if (result < 0)193			return result;194		result = 1 << index;195	} else {196		struct snd_mixer_oss_slot *pslot;197		int chn;198		for (chn = 0; chn < 31; chn++) {199			pslot = &mixer->slots[chn];200			if (pslot->get_recsrc) {201				int active = 0;202				pslot->get_recsrc(fmixer, pslot, &active);203				if (active)204					result |= 1 << chn;205			}206		}207	}208	mixer->oss_recsrc = result;209	return result;210}211 212static int snd_mixer_oss_set_recsrc(struct snd_mixer_oss_file *fmixer, int recsrc)213{214	struct snd_mixer_oss *mixer = fmixer->mixer;215	struct snd_mixer_oss_slot *pslot;216	int chn, active;217	unsigned int index;218	int result = 0;219 220	if (mixer == NULL)221		return -EIO;222	guard(mutex)(&mixer->reg_mutex);223	if (mixer->get_recsrc && mixer->put_recsrc) {	/* exclusive input */224		if (recsrc & ~mixer->oss_recsrc)225			recsrc &= ~mixer->oss_recsrc;226		mixer->put_recsrc(fmixer, ffz(~recsrc));227		mixer->get_recsrc(fmixer, &index);228		result = 1 << index;229	}230	for (chn = 0; chn < 31; chn++) {231		pslot = &mixer->slots[chn];232		if (pslot->put_recsrc) {233			active = (recsrc & (1 << chn)) ? 1 : 0;234			pslot->put_recsrc(fmixer, pslot, active);235		}236	}237	if (! result) {238		for (chn = 0; chn < 31; chn++) {239			pslot = &mixer->slots[chn];240			if (pslot->get_recsrc) {241				active = 0;242				pslot->get_recsrc(fmixer, pslot, &active);243				if (active)244					result |= 1 << chn;245			}246		}247	}248	return result;249}250 251static int snd_mixer_oss_get_volume(struct snd_mixer_oss_file *fmixer, int slot)252{253	struct snd_mixer_oss *mixer = fmixer->mixer;254	struct snd_mixer_oss_slot *pslot;255	int result = 0, left, right;256 257	if (mixer == NULL || slot > 30)258		return -EIO;259	guard(mutex)(&mixer->reg_mutex);260	pslot = &mixer->slots[slot];261	left = pslot->volume[0];262	right = pslot->volume[1];263	if (pslot->get_volume)264		result = pslot->get_volume(fmixer, pslot, &left, &right);265	if (!pslot->stereo)266		right = left;267	if (snd_BUG_ON(left < 0 || left > 100))268		return -EIO;269	if (snd_BUG_ON(right < 0 || right > 100))270		return -EIO;271	if (result >= 0) {272		pslot->volume[0] = left;273		pslot->volume[1] = right;274	 	result = (left & 0xff) | ((right & 0xff) << 8);275	}276	return result;277}278 279static int snd_mixer_oss_set_volume(struct snd_mixer_oss_file *fmixer,280				    int slot, int volume)281{282	struct snd_mixer_oss *mixer = fmixer->mixer;283	struct snd_mixer_oss_slot *pslot;284	int result = 0, left = volume & 0xff, right = (volume >> 8) & 0xff;285 286	if (mixer == NULL || slot > 30)287		return -EIO;288	guard(mutex)(&mixer->reg_mutex);289	pslot = &mixer->slots[slot];290	if (left > 100)291		left = 100;292	if (right > 100)293		right = 100;294	if (!pslot->stereo)295		right = left;296	if (pslot->put_volume)297		result = pslot->put_volume(fmixer, pslot, left, right);298	if (result < 0)299		return result;300	pslot->volume[0] = left;301	pslot->volume[1] = right;302	result = (left & 0xff) | ((right & 0xff) << 8);303	return result;304}305 306static int snd_mixer_oss_ioctl1(struct snd_mixer_oss_file *fmixer, unsigned int cmd, unsigned long arg)307{308	void __user *argp = (void __user *)arg;309	int __user *p = argp;310	int tmp;311 312	if (snd_BUG_ON(!fmixer))313		return -ENXIO;314	if (((cmd >> 8) & 0xff) == 'M') {315		switch (cmd) {316		case SOUND_MIXER_INFO:317			return snd_mixer_oss_info(fmixer, argp);318		case SOUND_OLD_MIXER_INFO:319 			return snd_mixer_oss_info_obsolete(fmixer, argp);320		case SOUND_MIXER_WRITE_RECSRC:321			if (get_user(tmp, p))322				return -EFAULT;323			tmp = snd_mixer_oss_set_recsrc(fmixer, tmp);324			if (tmp < 0)325				return tmp;326			return put_user(tmp, p);327		case OSS_GETVERSION:328			return put_user(SNDRV_OSS_VERSION, p);329		case OSS_ALSAEMULVER:330			return put_user(1, p);331		case SOUND_MIXER_READ_DEVMASK:332			tmp = snd_mixer_oss_devmask(fmixer);333			if (tmp < 0)334				return tmp;335			return put_user(tmp, p);336		case SOUND_MIXER_READ_STEREODEVS:337			tmp = snd_mixer_oss_stereodevs(fmixer);338			if (tmp < 0)339				return tmp;340			return put_user(tmp, p);341		case SOUND_MIXER_READ_RECMASK:342			tmp = snd_mixer_oss_recmask(fmixer);343			if (tmp < 0)344				return tmp;345			return put_user(tmp, p);346		case SOUND_MIXER_READ_CAPS:347			tmp = snd_mixer_oss_caps(fmixer);348			if (tmp < 0)349				return tmp;350			return put_user(tmp, p);351		case SOUND_MIXER_READ_RECSRC:352			tmp = snd_mixer_oss_get_recsrc(fmixer);353			if (tmp < 0)354				return tmp;355			return put_user(tmp, p);356		}357	}358	if (cmd & SIOC_IN) {359		if (get_user(tmp, p))360			return -EFAULT;361		tmp = snd_mixer_oss_set_volume(fmixer, cmd & 0xff, tmp);362		if (tmp < 0)363			return tmp;364		return put_user(tmp, p);365	} else if (cmd & SIOC_OUT) {366		tmp = snd_mixer_oss_get_volume(fmixer, cmd & 0xff);367		if (tmp < 0)368			return tmp;369		return put_user(tmp, p);370	}371	return -ENXIO;372}373 374static long snd_mixer_oss_ioctl(struct file *file, unsigned int cmd, unsigned long arg)375{376	return snd_mixer_oss_ioctl1(file->private_data, cmd, arg);377}378 379int snd_mixer_oss_ioctl_card(struct snd_card *card, unsigned int cmd, unsigned long arg)380{381	struct snd_mixer_oss_file fmixer;382	383	if (snd_BUG_ON(!card))384		return -ENXIO;385	if (card->mixer_oss == NULL)386		return -ENXIO;387	memset(&fmixer, 0, sizeof(fmixer));388	fmixer.card = card;389	fmixer.mixer = card->mixer_oss;390	return snd_mixer_oss_ioctl1(&fmixer, cmd, arg);391}392EXPORT_SYMBOL(snd_mixer_oss_ioctl_card);393 394#ifdef CONFIG_COMPAT395/* all compatible */396static long snd_mixer_oss_ioctl_compat(struct file *file, unsigned int cmd,397				       unsigned long arg)398{399	return snd_mixer_oss_ioctl1(file->private_data, cmd,400				    (unsigned long)compat_ptr(arg));401}402#else403#define snd_mixer_oss_ioctl_compat	NULL404#endif405 406/*407 *  REGISTRATION PART408 */409 410static const struct file_operations snd_mixer_oss_f_ops =411{412	.owner =	THIS_MODULE,413	.open =		snd_mixer_oss_open,414	.release =	snd_mixer_oss_release,415	.unlocked_ioctl =	snd_mixer_oss_ioctl,416	.compat_ioctl =	snd_mixer_oss_ioctl_compat,417};418 419/*420 *  utilities421 */422 423static long snd_mixer_oss_conv(long val, long omin, long omax, long nmin, long nmax)424{425	long orange = omax - omin, nrange = nmax - nmin;426	427	if (orange == 0)428		return 0;429	return DIV_ROUND_CLOSEST(nrange * (val - omin), orange) + nmin;430}431 432/* convert from alsa native to oss values (0-100) */433static long snd_mixer_oss_conv1(long val, long min, long max, int *old)434{435	if (val == snd_mixer_oss_conv(*old, 0, 100, min, max))436		return *old;437	return snd_mixer_oss_conv(val, min, max, 0, 100);438}439 440/* convert from oss to alsa native values */441static long snd_mixer_oss_conv2(long val, long min, long max)442{443	return snd_mixer_oss_conv(val, 0, 100, min, max);444}445 446#if 0447static void snd_mixer_oss_recsrce_set(struct snd_card *card, int slot)448{449	struct snd_mixer_oss *mixer = card->mixer_oss;450	if (mixer)451		mixer->mask_recsrc |= 1 << slot;452}453 454static int snd_mixer_oss_recsrce_get(struct snd_card *card, int slot)455{456	struct snd_mixer_oss *mixer = card->mixer_oss;457	if (mixer && (mixer->mask_recsrc & (1 << slot)))458		return 1;459	return 0;460}461#endif462 463#define SNDRV_MIXER_OSS_SIGNATURE		0x65999250464 465#define SNDRV_MIXER_OSS_ITEM_GLOBAL	0466#define SNDRV_MIXER_OSS_ITEM_GSWITCH	1467#define SNDRV_MIXER_OSS_ITEM_GROUTE	2468#define SNDRV_MIXER_OSS_ITEM_GVOLUME	3469#define SNDRV_MIXER_OSS_ITEM_PSWITCH	4470#define SNDRV_MIXER_OSS_ITEM_PROUTE	5471#define SNDRV_MIXER_OSS_ITEM_PVOLUME	6472#define SNDRV_MIXER_OSS_ITEM_CSWITCH	7473#define SNDRV_MIXER_OSS_ITEM_CROUTE	8474#define SNDRV_MIXER_OSS_ITEM_CVOLUME	9475#define SNDRV_MIXER_OSS_ITEM_CAPTURE	10476 477#define SNDRV_MIXER_OSS_ITEM_COUNT	11478 479#define SNDRV_MIXER_OSS_PRESENT_GLOBAL	(1<<0)480#define SNDRV_MIXER_OSS_PRESENT_GSWITCH	(1<<1)481#define SNDRV_MIXER_OSS_PRESENT_GROUTE	(1<<2)482#define SNDRV_MIXER_OSS_PRESENT_GVOLUME	(1<<3)483#define SNDRV_MIXER_OSS_PRESENT_PSWITCH	(1<<4)484#define SNDRV_MIXER_OSS_PRESENT_PROUTE	(1<<5)485#define SNDRV_MIXER_OSS_PRESENT_PVOLUME	(1<<6)486#define SNDRV_MIXER_OSS_PRESENT_CSWITCH	(1<<7)487#define SNDRV_MIXER_OSS_PRESENT_CROUTE	(1<<8)488#define SNDRV_MIXER_OSS_PRESENT_CVOLUME	(1<<9)489#define SNDRV_MIXER_OSS_PRESENT_CAPTURE	(1<<10)490 491struct slot {492	unsigned int signature;493	unsigned int present;494	unsigned int channels;495	unsigned int numid[SNDRV_MIXER_OSS_ITEM_COUNT];496	unsigned int capture_item;497	const struct snd_mixer_oss_assign_table *assigned;498	unsigned int allocated: 1;499};500 501#define ID_UNKNOWN	((unsigned int)-1)502 503static struct snd_kcontrol *snd_mixer_oss_test_id(struct snd_mixer_oss *mixer, const char *name, int index)504{505	struct snd_card *card = mixer->card;506	struct snd_ctl_elem_id id;507	508	memset(&id, 0, sizeof(id));509	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;510	strscpy(id.name, name, sizeof(id.name));511	id.index = index;512	return snd_ctl_find_id(card, &id);513}514 515static void snd_mixer_oss_get_volume1_vol(struct snd_mixer_oss_file *fmixer,516					  struct snd_mixer_oss_slot *pslot,517					  unsigned int numid,518					  int *left, int *right)519{520	struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;521	struct snd_ctl_elem_value *uctl __free(kfree) = NULL;522	struct snd_kcontrol *kctl;523	struct snd_card *card = fmixer->card;524 525	if (numid == ID_UNKNOWN)526		return;527	guard(rwsem_read)(&card->controls_rwsem);528	kctl = snd_ctl_find_numid(card, numid);529	if (!kctl)530		return;531	uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);532	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);533	if (uinfo == NULL || uctl == NULL)534		return;535	if (kctl->info(kctl, uinfo))536		return;537	if (kctl->get(kctl, uctl))538		return;539	if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&540	    uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)541		return;542	*left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]);543	if (uinfo->count > 1)544		*right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]);545}546 547static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,548					 struct snd_mixer_oss_slot *pslot,549					 unsigned int numid,550					 int *left, int *right,551					 int route)552{553	struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;554	struct snd_ctl_elem_value *uctl __free(kfree) = NULL;555	struct snd_kcontrol *kctl;556	struct snd_card *card = fmixer->card;557 558	if (numid == ID_UNKNOWN)559		return;560	guard(rwsem_read)(&card->controls_rwsem);561	kctl = snd_ctl_find_numid(card, numid);562	if (!kctl)563		return;564	uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);565	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);566	if (uinfo == NULL || uctl == NULL)567		return;568	if (kctl->info(kctl, uinfo))569		return;570	if (kctl->get(kctl, uctl))571		return;572	if (!uctl->value.integer.value[0]) {573		*left = 0;574		if (uinfo->count == 1)575			*right = 0;576	}577	if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1])578		*right = 0;579}580 581static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer,582				     struct snd_mixer_oss_slot *pslot,583				     int *left, int *right)584{585	struct slot *slot = pslot->private_data;586	587	*left = *right = 100;588	if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {589		snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);590	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {591		snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);592	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {593		snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);594	}595	if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {596		snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);597	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {598		snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);599	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {600		snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);601	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {602		snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);603	}604	return 0;605}606 607static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,608					  struct snd_mixer_oss_slot *pslot,609					  unsigned int numid,610					  int left, int right)611{612	struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;613	struct snd_ctl_elem_value *uctl __free(kfree) = NULL;614	struct snd_kcontrol *kctl;615	struct snd_card *card = fmixer->card;616	int res;617 618	if (numid == ID_UNKNOWN)619		return;620	guard(rwsem_read)(&card->controls_rwsem);621	kctl = snd_ctl_find_numid(card, numid);622	if (!kctl)623		return;624	uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);625	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);626	if (uinfo == NULL || uctl == NULL)627		return;628	if (kctl->info(kctl, uinfo))629		return;630	if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&631	    uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)632		return;633	uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max);634	if (uinfo->count > 1)635		uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max);636	res = kctl->put(kctl, uctl);637	if (res < 0)638		return;639	if (res > 0)640		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);641}642 643static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,644					 struct snd_mixer_oss_slot *pslot,645					 unsigned int numid,646					 int left, int right,647					 int route)648{649	struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;650	struct snd_ctl_elem_value *uctl __free(kfree) = NULL;651	struct snd_kcontrol *kctl;652	struct snd_card *card = fmixer->card;653	int res;654 655	if (numid == ID_UNKNOWN)656		return;657	guard(rwsem_read)(&card->controls_rwsem);658	kctl = snd_ctl_find_numid(card, numid);659	if (!kctl)660		return;661	uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);662	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);663	if (uinfo == NULL || uctl == NULL)664		return;665	if (kctl->info(kctl, uinfo))666		return;667	if (uinfo->count > 1) {668		uctl->value.integer.value[0] = left > 0 ? 1 : 0;669		uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0;670		if (route) {671			uctl->value.integer.value[1] =672			uctl->value.integer.value[2] = 0;673		}674	} else {675		uctl->value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0;676	}677	res = kctl->put(kctl, uctl);678	if (res < 0)679		return;680	if (res > 0)681		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);682}683 684static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer,685				     struct snd_mixer_oss_slot *pslot,686				     int left, int right)687{688	struct slot *slot = pslot->private_data;689	690	if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {691		snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);692		if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME)693			snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right);694	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME) {695		snd_mixer_oss_put_volume1_vol(fmixer, pslot,696			slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right);697	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {698		snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);699	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {700		snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);701	}702	if (left || right) {703		if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH)704			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);705		if (slot->present & SNDRV_MIXER_OSS_PRESENT_CSWITCH)706			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], left, right, 0);707		if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH)708			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);709		if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE)710			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);711		if (slot->present & SNDRV_MIXER_OSS_PRESENT_CROUTE)712			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], left, right, 1);713		if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE)714			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);715	} else {716		if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {717			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);718		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) {719			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], left, right, 0);720		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {721			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);722		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {723			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);724		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CROUTE) {725			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], left, right, 1);726		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {727			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);728		}729	}730	return 0;731}732 733static int snd_mixer_oss_get_recsrc1_sw(struct snd_mixer_oss_file *fmixer,734					struct snd_mixer_oss_slot *pslot,735					int *active)736{737	struct slot *slot = pslot->private_data;738	int left, right;739	740	left = right = 1;741	snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], &left, &right, 0);742	*active = (left || right) ? 1 : 0;743	return 0;744}745 746static int snd_mixer_oss_get_recsrc1_route(struct snd_mixer_oss_file *fmixer,747					   struct snd_mixer_oss_slot *pslot,748					   int *active)749{750	struct slot *slot = pslot->private_data;751	int left, right;752	753	left = right = 1;754	snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], &left, &right, 1);755	*active = (left || right) ? 1 : 0;756	return 0;757}758 759static int snd_mixer_oss_put_recsrc1_sw(struct snd_mixer_oss_file *fmixer,760					struct snd_mixer_oss_slot *pslot,761					int active)762{763	struct slot *slot = pslot->private_data;764	765	snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0);766	return 0;767}768 769static int snd_mixer_oss_put_recsrc1_route(struct snd_mixer_oss_file *fmixer,770					   struct snd_mixer_oss_slot *pslot,771					   int active)772{773	struct slot *slot = pslot->private_data;774	775	snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1);776	return 0;777}778 779static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned int *active_index)780{781	struct snd_card *card = fmixer->card;782	struct snd_mixer_oss *mixer = fmixer->mixer;783	struct snd_kcontrol *kctl;784	struct snd_mixer_oss_slot *pslot;785	struct slot *slot;786	struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;787	struct snd_ctl_elem_value *uctl __free(kfree) = NULL;788	int err, idx;789	790	uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);791	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);792	if (uinfo == NULL || uctl == NULL)793		return -ENOMEM;794	guard(rwsem_read)(&card->controls_rwsem);795	kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);796	if (!kctl)797		return -ENOENT;798	err = kctl->info(kctl, uinfo);799	if (err < 0)800		return err;801	err = kctl->get(kctl, uctl);802	if (err < 0)803		return err;804	for (idx = 0; idx < 32; idx++) {805		if (!(mixer->mask_recsrc & (1 << idx)))806			continue;807		pslot = &mixer->slots[idx];808		slot = pslot->private_data;809		if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)810			continue;811		if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))812			continue;813		if (slot->capture_item == uctl->value.enumerated.item[0]) {814			*active_index = idx;815			break;816		}817	}818	return 0;819}820 821static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned int active_index)822{823	struct snd_card *card = fmixer->card;824	struct snd_mixer_oss *mixer = fmixer->mixer;825	struct snd_kcontrol *kctl;826	struct snd_mixer_oss_slot *pslot;827	struct slot *slot = NULL;828	struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;829	struct snd_ctl_elem_value *uctl __free(kfree) = NULL;830	int err;831	unsigned int idx;832 833	uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);834	uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);835	if (uinfo == NULL || uctl == NULL)836		return -ENOMEM;837	guard(rwsem_read)(&card->controls_rwsem);838	kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);839	if (!kctl)840		return -ENOENT;841	err = kctl->info(kctl, uinfo);842	if (err < 0)843		return err;844	for (idx = 0; idx < 32; idx++) {845		if (!(mixer->mask_recsrc & (1 << idx)))846			continue;847		pslot = &mixer->slots[idx];848		slot = pslot->private_data;849		if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)850			continue;851		if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))852			continue;853		if (idx == active_index)854			break;855		slot = NULL;856	}857	if (!slot)858		return 0;859	for (idx = 0; idx < uinfo->count; idx++)860		uctl->value.enumerated.item[idx] = slot->capture_item;861	err = kctl->put(kctl, uctl);862	if (err > 0)863		snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);864	return 0;865}866 867struct snd_mixer_oss_assign_table {868	int oss_id;869	const char *name;870	int index;871};872 873static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *slot, const char *name, int index, int item)874{875	struct snd_ctl_elem_info *info __free(kfree) = NULL;876	struct snd_kcontrol *kcontrol;877	struct snd_card *card = mixer->card;878	int err;879 880	scoped_guard(rwsem_read, &card->controls_rwsem) {881		kcontrol = snd_mixer_oss_test_id(mixer, name, index);882		if (kcontrol == NULL)883			return 0;884		info = kmalloc(sizeof(*info), GFP_KERNEL);885		if (!info)886			return -ENOMEM;887		err = kcontrol->info(kcontrol, info);888		if (err < 0)889			return err;890		slot->numid[item] = kcontrol->id.numid;891	}892	if (info->count > slot->channels)893		slot->channels = info->count;894	slot->present |= 1 << item;895	return 0;896}897 898static void snd_mixer_oss_slot_free(struct snd_mixer_oss_slot *chn)899{900	struct slot *p = chn->private_data;901	if (p) {902		if (p->allocated && p->assigned) {903			kfree(p->assigned->name);904			kfree(p->assigned);905		}906		kfree(p);907	}908}909 910static void mixer_slot_clear(struct snd_mixer_oss_slot *rslot)911{912	int idx = rslot->number; /* remember this */913	if (rslot->private_free)914		rslot->private_free(rslot);915	memset(rslot, 0, sizeof(*rslot));916	rslot->number = idx;917}918 919/* In a separate function to keep gcc 3.2 happy - do NOT merge this in920   snd_mixer_oss_build_input! */921static int snd_mixer_oss_build_test_all(struct snd_mixer_oss *mixer,922					const struct snd_mixer_oss_assign_table *ptr,923					struct slot *slot)924{925	char str[64];926	int err;927 928	err = snd_mixer_oss_build_test(mixer, slot, ptr->name, ptr->index,929				       SNDRV_MIXER_OSS_ITEM_GLOBAL);930	if (err)931		return err;932	sprintf(str, "%s Switch", ptr->name);933	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,934				       SNDRV_MIXER_OSS_ITEM_GSWITCH);935	if (err)936		return err;937	sprintf(str, "%s Route", ptr->name);938	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,939				       SNDRV_MIXER_OSS_ITEM_GROUTE);940	if (err)941		return err;942	sprintf(str, "%s Volume", ptr->name);943	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,944				       SNDRV_MIXER_OSS_ITEM_GVOLUME);945	if (err)946		return err;947	sprintf(str, "%s Playback Switch", ptr->name);948	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,949				       SNDRV_MIXER_OSS_ITEM_PSWITCH);950	if (err)951		return err;952	sprintf(str, "%s Playback Route", ptr->name);953	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,954				       SNDRV_MIXER_OSS_ITEM_PROUTE);955	if (err)956		return err;957	sprintf(str, "%s Playback Volume", ptr->name);958	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,959				       SNDRV_MIXER_OSS_ITEM_PVOLUME);960	if (err)961		return err;962	sprintf(str, "%s Capture Switch", ptr->name);963	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,964				       SNDRV_MIXER_OSS_ITEM_CSWITCH);965	if (err)966		return err;967	sprintf(str, "%s Capture Route", ptr->name);968	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,969				       SNDRV_MIXER_OSS_ITEM_CROUTE);970	if (err)971		return err;972	sprintf(str, "%s Capture Volume", ptr->name);973	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,974				       SNDRV_MIXER_OSS_ITEM_CVOLUME);975	if (err)976		return err;977 978	return 0;979}980 981/*982 * build an OSS mixer element.983 * ptr_allocated means the entry is dynamically allocated (change via proc file).984 * when replace_old = 1, the old entry is replaced with the new one.985 */986static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer,987				     const struct snd_mixer_oss_assign_table *ptr,988				     int ptr_allocated, int replace_old)989{990	struct slot slot;991	struct slot *pslot;992	struct snd_kcontrol *kctl;993	struct snd_mixer_oss_slot *rslot;994	char str[64];	995	996	/* check if already assigned */997	if (mixer->slots[ptr->oss_id].get_volume && ! replace_old)998		return 0;999 1000	memset(&slot, 0, sizeof(slot));1001	memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */1002	if (snd_mixer_oss_build_test_all(mixer, ptr, &slot))1003		return 0;1004	guard(rwsem_read)(&mixer->card->controls_rwsem);1005	kctl = NULL;1006	if (!ptr->index)1007		kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);1008	if (kctl) {1009		struct snd_ctl_elem_info *uinfo __free(kfree) = NULL;1010 1011		uinfo = kzalloc(sizeof(*uinfo), GFP_KERNEL);1012		if (!uinfo)1013			return -ENOMEM;1014			1015		if (kctl->info(kctl, uinfo))1016			return 0;1017		strcpy(str, ptr->name);1018		if (!strcmp(str, "Master"))1019			strcpy(str, "Mix");1020		if (!strcmp(str, "Master Mono"))1021			strcpy(str, "Mix Mono");1022		slot.capture_item = 0;1023		if (!strcmp(uinfo->value.enumerated.name, str)) {1024			slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;1025		} else {1026			for (slot.capture_item = 1; slot.capture_item < uinfo->value.enumerated.items; slot.capture_item++) {1027				uinfo->value.enumerated.item = slot.capture_item;1028				if (kctl->info(kctl, uinfo))1029					return 0;1030				if (!strcmp(uinfo->value.enumerated.name, str)) {1031					slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;1032					break;1033				}1034			}1035		}1036	}1037	if (slot.present != 0) {1038		pslot = kmalloc(sizeof(slot), GFP_KERNEL);1039		if (! pslot)1040			return -ENOMEM;1041		*pslot = slot;1042		pslot->signature = SNDRV_MIXER_OSS_SIGNATURE;1043		pslot->assigned = ptr;1044		pslot->allocated = ptr_allocated;1045		rslot = &mixer->slots[ptr->oss_id];1046		mixer_slot_clear(rslot);1047		rslot->stereo = slot.channels > 1 ? 1 : 0;1048		rslot->get_volume = snd_mixer_oss_get_volume1;1049		rslot->put_volume = snd_mixer_oss_put_volume1;1050		/* note: ES18xx have both Capture Source and XX Capture Volume !!! */1051		if (slot.present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) {1052			rslot->get_recsrc = snd_mixer_oss_get_recsrc1_sw;1053			rslot->put_recsrc = snd_mixer_oss_put_recsrc1_sw;1054		} else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CROUTE) {1055			rslot->get_recsrc = snd_mixer_oss_get_recsrc1_route;1056			rslot->put_recsrc = snd_mixer_oss_put_recsrc1_route;1057		} else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CAPTURE) {1058			mixer->mask_recsrc |= 1 << ptr->oss_id;1059		}1060		rslot->private_data = pslot;1061		rslot->private_free = snd_mixer_oss_slot_free;1062		return 1;1063	}1064	return 0;1065}1066 1067#ifdef CONFIG_SND_PROC_FS1068/*1069 */1070#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name1071static const char * const oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {1072	MIXER_VOL(VOLUME),1073	MIXER_VOL(BASS),1074	MIXER_VOL(TREBLE),1075	MIXER_VOL(SYNTH),1076	MIXER_VOL(PCM),1077	MIXER_VOL(SPEAKER),1078	MIXER_VOL(LINE),1079	MIXER_VOL(MIC),1080	MIXER_VOL(CD),1081	MIXER_VOL(IMIX),1082	MIXER_VOL(ALTPCM),1083	MIXER_VOL(RECLEV),1084	MIXER_VOL(IGAIN),1085	MIXER_VOL(OGAIN),1086	MIXER_VOL(LINE1),1087	MIXER_VOL(LINE2),1088	MIXER_VOL(LINE3),1089	MIXER_VOL(DIGITAL1),1090	MIXER_VOL(DIGITAL2),1091	MIXER_VOL(DIGITAL3),1092	MIXER_VOL(PHONEIN),1093	MIXER_VOL(PHONEOUT),1094	MIXER_VOL(VIDEO),1095	MIXER_VOL(RADIO),1096	MIXER_VOL(MONITOR),1097};1098	1099/*1100 *  /proc interface1101 */1102 1103static void snd_mixer_oss_proc_read(struct snd_info_entry *entry,1104				    struct snd_info_buffer *buffer)1105{1106	struct snd_mixer_oss *mixer = entry->private_data;1107	int i;1108 1109	guard(mutex)(&mixer->reg_mutex);1110	for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) {1111		struct slot *p;1112 1113		if (! oss_mixer_names[i])1114			continue;1115		p = (struct slot *)mixer->slots[i].private_data;1116		snd_iprintf(buffer, "%s ", oss_mixer_names[i]);1117		if (p && p->assigned)1118			snd_iprintf(buffer, "\"%s\" %d\n",1119				    p->assigned->name,1120				    p->assigned->index);1121		else1122			snd_iprintf(buffer, "\"\" 0\n");1123	}1124}1125 1126static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,1127				     struct snd_info_buffer *buffer)1128{1129	struct snd_mixer_oss *mixer = entry->private_data;1130	char line[128], str[32], idxstr[16];1131	const char *cptr;1132	unsigned int idx;1133	int ch;1134	struct snd_mixer_oss_assign_table *tbl;1135	struct slot *slot;1136 1137	while (!snd_info_get_line(buffer, line, sizeof(line))) {1138		cptr = snd_info_get_str(str, line, sizeof(str));1139		for (ch = 0; ch < SNDRV_OSS_MAX_MIXERS; ch++)1140			if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0)1141				break;1142		if (ch >= SNDRV_OSS_MAX_MIXERS) {1143			pr_err("ALSA: mixer_oss: invalid OSS volume '%s'\n",1144			       str);1145			continue;1146		}1147		cptr = snd_info_get_str(str, cptr, sizeof(str));1148		if (! *str) {1149			/* remove the entry */1150			scoped_guard(mutex, &mixer->reg_mutex)1151				mixer_slot_clear(&mixer->slots[ch]);1152			continue;1153		}1154		snd_info_get_str(idxstr, cptr, sizeof(idxstr));1155		idx = simple_strtoul(idxstr, NULL, 10);1156		if (idx >= 0x4000) { /* too big */1157			pr_err("ALSA: mixer_oss: invalid index %d\n", idx);1158			continue;1159		}1160		scoped_guard(mutex, &mixer->reg_mutex) {1161			slot = (struct slot *)mixer->slots[ch].private_data;1162			if (slot && slot->assigned &&1163			    slot->assigned->index == idx && !strcmp(slot->assigned->name, str))1164				/* not changed */1165				break;1166			tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);1167			if (!tbl)1168				break;1169			tbl->oss_id = ch;1170			tbl->name = kstrdup(str, GFP_KERNEL);1171			if (!tbl->name) {1172				kfree(tbl);1173				break;1174			}1175			tbl->index = idx;1176			if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) {1177				kfree(tbl->name);1178				kfree(tbl);1179			}1180		}1181	}1182}1183 1184static void snd_mixer_oss_proc_init(struct snd_mixer_oss *mixer)1185{1186	struct snd_info_entry *entry;1187 1188	entry = snd_info_create_card_entry(mixer->card, "oss_mixer",1189					   mixer->card->proc_root);1190	if (! entry)1191		return;1192	entry->content = SNDRV_INFO_CONTENT_TEXT;1193	entry->mode = S_IFREG | 0644;1194	entry->c.text.read = snd_mixer_oss_proc_read;1195	entry->c.text.write = snd_mixer_oss_proc_write;1196	entry->private_data = mixer;1197	if (snd_info_register(entry) < 0) {1198		snd_info_free_entry(entry);1199		entry = NULL;1200	}1201	mixer->proc_entry = entry;1202}1203 1204static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer)1205{1206	snd_info_free_entry(mixer->proc_entry);1207	mixer->proc_entry = NULL;1208}1209#else /* !CONFIG_SND_PROC_FS */1210#define snd_mixer_oss_proc_init(mix)1211#define snd_mixer_oss_proc_done(mix)1212#endif /* CONFIG_SND_PROC_FS */1213 1214static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)1215{1216	static const struct snd_mixer_oss_assign_table table[] = {1217		{ SOUND_MIXER_VOLUME, 	"Master",		0 },1218		{ SOUND_MIXER_VOLUME, 	"Front",		0 }, /* fallback */1219		{ SOUND_MIXER_BASS,	"Tone Control - Bass",	0 },1220		{ SOUND_MIXER_TREBLE,	"Tone Control - Treble", 0 },1221		{ SOUND_MIXER_SYNTH,	"Synth",		0 },1222		{ SOUND_MIXER_SYNTH,	"FM",			0 }, /* fallback */1223		{ SOUND_MIXER_SYNTH,	"Music",		0 }, /* fallback */1224		{ SOUND_MIXER_PCM,	"PCM",			0 },1225		{ SOUND_MIXER_SPEAKER,	"Beep", 		0 },1226		{ SOUND_MIXER_SPEAKER,	"PC Speaker", 		0 }, /* fallback */1227		{ SOUND_MIXER_SPEAKER,	"Speaker", 		0 }, /* fallback */1228		{ SOUND_MIXER_LINE,	"Line", 		0 },1229		{ SOUND_MIXER_MIC,	"Mic", 			0 },1230		{ SOUND_MIXER_CD,	"CD", 			0 },1231		{ SOUND_MIXER_IMIX,	"Monitor Mix", 		0 },1232		{ SOUND_MIXER_ALTPCM,	"PCM",			1 },1233		{ SOUND_MIXER_ALTPCM,	"Headphone",		0 }, /* fallback */1234		{ SOUND_MIXER_ALTPCM,	"Wave",			0 }, /* fallback */1235		{ SOUND_MIXER_RECLEV,	"-- nothing --",	0 },1236		{ SOUND_MIXER_IGAIN,	"Capture",		0 },1237		{ SOUND_MIXER_OGAIN,	"Playback",		0 },1238		{ SOUND_MIXER_LINE1,	"Aux",			0 },1239		{ SOUND_MIXER_LINE2,	"Aux",			1 },1240		{ SOUND_MIXER_LINE3,	"Aux",			2 },1241		{ SOUND_MIXER_DIGITAL1,	"Digital",		0 },1242		{ SOUND_MIXER_DIGITAL1,	"IEC958",		0 }, /* fallback */1243		{ SOUND_MIXER_DIGITAL1,	"IEC958 Optical",	0 }, /* fallback */1244		{ SOUND_MIXER_DIGITAL1,	"IEC958 Coaxial",	0 }, /* fallback */1245		{ SOUND_MIXER_DIGITAL2,	"Digital",		1 },1246		{ SOUND_MIXER_DIGITAL3,	"Digital",		2 },1247		{ SOUND_MIXER_PHONEIN,	"Phone",		0 },1248		{ SOUND_MIXER_PHONEOUT,	"Master Mono",		0 },1249		{ SOUND_MIXER_PHONEOUT,	"Speaker",		0 }, /*fallback*/1250		{ SOUND_MIXER_PHONEOUT,	"Mono",			0 }, /*fallback*/1251		{ SOUND_MIXER_PHONEOUT,	"Phone",		0 }, /* fallback */1252		{ SOUND_MIXER_VIDEO,	"Video",		0 },1253		{ SOUND_MIXER_RADIO,	"Radio",		0 },1254		{ SOUND_MIXER_MONITOR,	"Monitor",		0 }1255	};1256	unsigned int idx;1257	1258	for (idx = 0; idx < ARRAY_SIZE(table); idx++)1259		snd_mixer_oss_build_input(mixer, &table[idx], 0, 0);1260	if (mixer->mask_recsrc) {1261		mixer->get_recsrc = snd_mixer_oss_get_recsrc2;1262		mixer->put_recsrc = snd_mixer_oss_put_recsrc2;1263	}1264}1265 1266/*1267 *1268 */1269 1270static int snd_mixer_oss_free1(void *private)1271{1272	struct snd_mixer_oss *mixer = private;1273	struct snd_card *card;1274	int idx;1275 1276	if (!mixer)1277		return 0;1278	card = mixer->card;1279	if (snd_BUG_ON(mixer != card->mixer_oss))1280		return -ENXIO;1281	card->mixer_oss = NULL;1282	for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {1283		struct snd_mixer_oss_slot *chn = &mixer->slots[idx];1284		if (chn->private_free)1285			chn->private_free(chn);1286	}1287	kfree(mixer);1288	return 0;1289}1290 1291static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)1292{1293	struct snd_mixer_oss *mixer;1294 1295	if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) {1296		int idx, err;1297 1298		mixer = kcalloc(2, sizeof(*mixer), GFP_KERNEL);1299		if (mixer == NULL)1300			return -ENOMEM;1301		mutex_init(&mixer->reg_mutex);1302		err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,1303					      card, 0,1304					      &snd_mixer_oss_f_ops, card);1305		if (err < 0) {1306			dev_err(card->dev,1307				"unable to register OSS mixer device %i:%i\n",1308				card->number, 0);1309			kfree(mixer);1310			return err;1311		}1312		mixer->oss_dev_alloc = 1;1313		mixer->card = card;1314		if (*card->mixername)1315			strscpy(mixer->name, card->mixername, sizeof(mixer->name));1316		else1317			snprintf(mixer->name, sizeof(mixer->name),1318				 "mixer%i", card->number);1319#ifdef SNDRV_OSS_INFO_DEV_MIXERS1320		snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,1321				      card->number,1322				      mixer->name);1323#endif1324		for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++)1325			mixer->slots[idx].number = idx;1326		card->mixer_oss = mixer;1327		snd_mixer_oss_build(mixer);1328		snd_mixer_oss_proc_init(mixer);1329	} else {1330		mixer = card->mixer_oss;1331		if (mixer == NULL)1332			return 0;1333		if (mixer->oss_dev_alloc) {1334#ifdef SNDRV_OSS_INFO_DEV_MIXERS1335			snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);1336#endif1337			snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);1338			mixer->oss_dev_alloc = 0;1339		}1340		if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT)1341			return 0;1342		snd_mixer_oss_proc_done(mixer);1343		return snd_mixer_oss_free1(mixer);1344	}1345	return 0;1346}1347 1348static int __init alsa_mixer_oss_init(void)1349{1350	struct snd_card *card;1351	int idx;1352	1353	snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;1354	for (idx = 0; idx < SNDRV_CARDS; idx++) {1355		card = snd_card_ref(idx);1356		if (card) {1357			snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_REGISTER);1358			snd_card_unref(card);1359		}1360	}1361	return 0;1362}1363 1364static void __exit alsa_mixer_oss_exit(void)1365{1366	struct snd_card *card;1367	int idx;1368 1369	snd_mixer_oss_notify_callback = NULL;1370	for (idx = 0; idx < SNDRV_CARDS; idx++) {1371		card = snd_card_ref(idx);1372		if (card) {1373			snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_FREE);1374			snd_card_unref(card);1375		}1376	}1377}1378 1379module_init(alsa_mixer_oss_init)1380module_exit(alsa_mixer_oss_exit)1381