File Index
Files and directories
Sign in
FILE

mixer_oss.c

File preview with syntax highlighting.

linux-6.19.14/sound/core/oss/mixer_oss.c C
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  OSS emulation layer for the mixer interface
   4 *  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_COMPAT
 395/* 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#else
 403#define snd_mixer_oss_ioctl_compat	NULL
 404#endif
 405
 406/*
 407 *  REGISTRATION PART
 408 */
 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 *  utilities
 421 */
 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 0
 447static 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#endif
 462
 463#define SNDRV_MIXER_OSS_SIGNATURE		0x65999250
 464
 465#define SNDRV_MIXER_OSS_ITEM_GLOBAL	0
 466#define SNDRV_MIXER_OSS_ITEM_GSWITCH	1
 467#define SNDRV_MIXER_OSS_ITEM_GROUTE	2
 468#define SNDRV_MIXER_OSS_ITEM_GVOLUME	3
 469#define SNDRV_MIXER_OSS_ITEM_PSWITCH	4
 470#define SNDRV_MIXER_OSS_ITEM_PROUTE	5
 471#define SNDRV_MIXER_OSS_ITEM_PVOLUME	6
 472#define SNDRV_MIXER_OSS_ITEM_CSWITCH	7
 473#define SNDRV_MIXER_OSS_ITEM_CROUTE	8
 474#define SNDRV_MIXER_OSS_ITEM_CVOLUME	9
 475#define SNDRV_MIXER_OSS_ITEM_CAPTURE	10
 476
 477#define SNDRV_MIXER_OSS_ITEM_COUNT	11
 478
 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_kcontrol *kctl;
 521	struct snd_card *card = fmixer->card;
 522
 523	if (numid == ID_UNKNOWN)
 524		return;
 525	guard(rwsem_read)(&card->controls_rwsem);
 526	if (card->shutdown)
 527		return;
 528	kctl = snd_ctl_find_numid(card, numid);
 529	if (!kctl)
 530		return;
 531
 532	struct snd_ctl_elem_info *uinfo __free(kfree) =
 533		kzalloc(sizeof(*uinfo), GFP_KERNEL);
 534	struct snd_ctl_elem_value *uctl __free(kfree) =
 535		kzalloc(sizeof(*uctl), GFP_KERNEL);
 536	if (uinfo == NULL || uctl == NULL)
 537		return;
 538	if (kctl->info(kctl, uinfo))
 539		return;
 540	if (kctl->get(kctl, uctl))
 541		return;
 542	if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&
 543	    uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)
 544		return;
 545	*left = snd_mixer_oss_conv1(uctl->value.integer.value[0], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[0]);
 546	if (uinfo->count > 1)
 547		*right = snd_mixer_oss_conv1(uctl->value.integer.value[1], uinfo->value.integer.min, uinfo->value.integer.max, &pslot->volume[1]);
 548}
 549
 550static void snd_mixer_oss_get_volume1_sw(struct snd_mixer_oss_file *fmixer,
 551					 struct snd_mixer_oss_slot *pslot,
 552					 unsigned int numid,
 553					 int *left, int *right,
 554					 int route)
 555{
 556	struct snd_kcontrol *kctl;
 557	struct snd_card *card = fmixer->card;
 558
 559	if (numid == ID_UNKNOWN)
 560		return;
 561	guard(rwsem_read)(&card->controls_rwsem);
 562	if (card->shutdown)
 563		return;
 564	kctl = snd_ctl_find_numid(card, numid);
 565	if (!kctl)
 566		return;
 567
 568	struct snd_ctl_elem_info *uinfo __free(kfree) =
 569		kzalloc(sizeof(*uinfo), GFP_KERNEL);
 570	struct snd_ctl_elem_value *uctl __free(kfree) =
 571		kzalloc(sizeof(*uctl), GFP_KERNEL);
 572	if (uinfo == NULL || uctl == NULL)
 573		return;
 574	if (kctl->info(kctl, uinfo))
 575		return;
 576	if (kctl->get(kctl, uctl))
 577		return;
 578	if (!uctl->value.integer.value[0]) {
 579		*left = 0;
 580		if (uinfo->count == 1)
 581			*right = 0;
 582	}
 583	if (uinfo->count > 1 && !uctl->value.integer.value[route ? 3 : 1])
 584		*right = 0;
 585}
 586
 587static int snd_mixer_oss_get_volume1(struct snd_mixer_oss_file *fmixer,
 588				     struct snd_mixer_oss_slot *pslot,
 589				     int *left, int *right)
 590{
 591	struct slot *slot = pslot->private_data;
 592	
 593	*left = *right = 100;
 594	if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
 595		snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
 596	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
 597		snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
 598	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
 599		snd_mixer_oss_get_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);
 600	}
 601	if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
 602		snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
 603	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
 604		snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
 605	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
 606		snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
 607	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
 608		snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
 609	}
 610	return 0;
 611}
 612
 613static void snd_mixer_oss_put_volume1_vol(struct snd_mixer_oss_file *fmixer,
 614					  struct snd_mixer_oss_slot *pslot,
 615					  unsigned int numid,
 616					  int left, int right)
 617{
 618	struct snd_kcontrol *kctl;
 619	struct snd_card *card = fmixer->card;
 620	int res;
 621
 622	if (numid == ID_UNKNOWN)
 623		return;
 624	guard(rwsem_read)(&card->controls_rwsem);
 625	if (card->shutdown)
 626		return;
 627	kctl = snd_ctl_find_numid(card, numid);
 628	if (!kctl)
 629		return;
 630
 631	struct snd_ctl_elem_info *uinfo __free(kfree) =
 632		kzalloc(sizeof(*uinfo), GFP_KERNEL);
 633	struct snd_ctl_elem_value *uctl __free(kfree) =
 634		kzalloc(sizeof(*uctl), GFP_KERNEL);
 635	if (uinfo == NULL || uctl == NULL)
 636		return;
 637	if (kctl->info(kctl, uinfo))
 638		return;
 639	if (uinfo->type == SNDRV_CTL_ELEM_TYPE_BOOLEAN &&
 640	    uinfo->value.integer.min == 0 && uinfo->value.integer.max == 1)
 641		return;
 642	uctl->value.integer.value[0] = snd_mixer_oss_conv2(left, uinfo->value.integer.min, uinfo->value.integer.max);
 643	if (uinfo->count > 1)
 644		uctl->value.integer.value[1] = snd_mixer_oss_conv2(right, uinfo->value.integer.min, uinfo->value.integer.max);
 645	res = kctl->put(kctl, uctl);
 646	if (res < 0)
 647		return;
 648	if (res > 0)
 649		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
 650}
 651
 652static void snd_mixer_oss_put_volume1_sw(struct snd_mixer_oss_file *fmixer,
 653					 struct snd_mixer_oss_slot *pslot,
 654					 unsigned int numid,
 655					 int left, int right,
 656					 int route)
 657{
 658	struct snd_kcontrol *kctl;
 659	struct snd_card *card = fmixer->card;
 660	int res;
 661
 662	if (numid == ID_UNKNOWN)
 663		return;
 664	guard(rwsem_read)(&card->controls_rwsem);
 665	if (card->shutdown)
 666		return;
 667	kctl = snd_ctl_find_numid(card, numid);
 668	if (!kctl)
 669		return;
 670
 671	struct snd_ctl_elem_info *uinfo __free(kfree) =
 672		kzalloc(sizeof(*uinfo), GFP_KERNEL);
 673	struct snd_ctl_elem_value *uctl __free(kfree) =
 674		kzalloc(sizeof(*uctl), GFP_KERNEL);
 675	if (uinfo == NULL || uctl == NULL)
 676		return;
 677	if (kctl->info(kctl, uinfo))
 678		return;
 679	if (uinfo->count > 1) {
 680		uctl->value.integer.value[0] = left > 0 ? 1 : 0;
 681		uctl->value.integer.value[route ? 3 : 1] = right > 0 ? 1 : 0;
 682		if (route) {
 683			uctl->value.integer.value[1] =
 684			uctl->value.integer.value[2] = 0;
 685		}
 686	} else {
 687		uctl->value.integer.value[0] = (left > 0 || right > 0) ? 1 : 0;
 688	}
 689	res = kctl->put(kctl, uctl);
 690	if (res < 0)
 691		return;
 692	if (res > 0)
 693		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
 694}
 695
 696static int snd_mixer_oss_put_volume1(struct snd_mixer_oss_file *fmixer,
 697				     struct snd_mixer_oss_slot *pslot,
 698				     int left, int right)
 699{
 700	struct slot *slot = pslot->private_data;
 701	
 702	if (slot->present & SNDRV_MIXER_OSS_PRESENT_PVOLUME) {
 703		snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PVOLUME], left, right);
 704		if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME)
 705			snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right);
 706	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CVOLUME) {
 707		snd_mixer_oss_put_volume1_vol(fmixer, pslot,
 708			slot->numid[SNDRV_MIXER_OSS_ITEM_CVOLUME], left, right);
 709	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GVOLUME) {
 710		snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GVOLUME], left, right);
 711	} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GLOBAL) {
 712		snd_mixer_oss_put_volume1_vol(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GLOBAL], left, right);
 713	}
 714	if (left || right) {
 715		if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH)
 716			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
 717		if (slot->present & SNDRV_MIXER_OSS_PRESENT_CSWITCH)
 718			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], left, right, 0);
 719		if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH)
 720			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
 721		if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE)
 722			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
 723		if (slot->present & SNDRV_MIXER_OSS_PRESENT_CROUTE)
 724			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], left, right, 1);
 725		if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE)
 726			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
 727	} else {
 728		if (slot->present & SNDRV_MIXER_OSS_PRESENT_PSWITCH) {
 729			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PSWITCH], left, right, 0);
 730		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) {
 731			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], left, right, 0);
 732		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GSWITCH) {
 733			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GSWITCH], left, right, 0);
 734		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_PROUTE) {
 735			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_PROUTE], left, right, 1);
 736		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_CROUTE) {
 737			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], left, right, 1);
 738		} else if (slot->present & SNDRV_MIXER_OSS_PRESENT_GROUTE) {
 739			snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_GROUTE], left, right, 1);
 740		}
 741	}
 742	return 0;
 743}
 744
 745static int snd_mixer_oss_get_recsrc1_sw(struct snd_mixer_oss_file *fmixer,
 746					struct snd_mixer_oss_slot *pslot,
 747					int *active)
 748{
 749	struct slot *slot = pslot->private_data;
 750	int left, right;
 751	
 752	left = right = 1;
 753	snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], &left, &right, 0);
 754	*active = (left || right) ? 1 : 0;
 755	return 0;
 756}
 757
 758static int snd_mixer_oss_get_recsrc1_route(struct snd_mixer_oss_file *fmixer,
 759					   struct snd_mixer_oss_slot *pslot,
 760					   int *active)
 761{
 762	struct slot *slot = pslot->private_data;
 763	int left, right;
 764	
 765	left = right = 1;
 766	snd_mixer_oss_get_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], &left, &right, 1);
 767	*active = (left || right) ? 1 : 0;
 768	return 0;
 769}
 770
 771static int snd_mixer_oss_put_recsrc1_sw(struct snd_mixer_oss_file *fmixer,
 772					struct snd_mixer_oss_slot *pslot,
 773					int active)
 774{
 775	struct slot *slot = pslot->private_data;
 776	
 777	snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CSWITCH], active, active, 0);
 778	return 0;
 779}
 780
 781static int snd_mixer_oss_put_recsrc1_route(struct snd_mixer_oss_file *fmixer,
 782					   struct snd_mixer_oss_slot *pslot,
 783					   int active)
 784{
 785	struct slot *slot = pslot->private_data;
 786	
 787	snd_mixer_oss_put_volume1_sw(fmixer, pslot, slot->numid[SNDRV_MIXER_OSS_ITEM_CROUTE], active, active, 1);
 788	return 0;
 789}
 790
 791static int snd_mixer_oss_get_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned int *active_index)
 792{
 793	struct snd_card *card = fmixer->card;
 794	struct snd_mixer_oss *mixer = fmixer->mixer;
 795	struct snd_kcontrol *kctl;
 796	struct snd_mixer_oss_slot *pslot;
 797	struct slot *slot;
 798	int err, idx;
 799	
 800	struct snd_ctl_elem_info *uinfo __free(kfree) =
 801		kzalloc(sizeof(*uinfo), GFP_KERNEL);
 802	struct snd_ctl_elem_value *uctl __free(kfree) =
 803		kzalloc(sizeof(*uctl), GFP_KERNEL);
 804	if (uinfo == NULL || uctl == NULL)
 805		return -ENOMEM;
 806	guard(rwsem_read)(&card->controls_rwsem);
 807	if (card->shutdown)
 808		return -ENODEV;
 809	kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
 810	if (!kctl)
 811		return -ENOENT;
 812	err = kctl->info(kctl, uinfo);
 813	if (err < 0)
 814		return err;
 815	err = kctl->get(kctl, uctl);
 816	if (err < 0)
 817		return err;
 818	for (idx = 0; idx < 32; idx++) {
 819		if (!(mixer->mask_recsrc & (1 << idx)))
 820			continue;
 821		pslot = &mixer->slots[idx];
 822		slot = pslot->private_data;
 823		if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
 824			continue;
 825		if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
 826			continue;
 827		if (slot->capture_item == uctl->value.enumerated.item[0]) {
 828			*active_index = idx;
 829			break;
 830		}
 831	}
 832	return 0;
 833}
 834
 835static int snd_mixer_oss_put_recsrc2(struct snd_mixer_oss_file *fmixer, unsigned int active_index)
 836{
 837	struct snd_card *card = fmixer->card;
 838	struct snd_mixer_oss *mixer = fmixer->mixer;
 839	struct snd_kcontrol *kctl;
 840	struct snd_mixer_oss_slot *pslot;
 841	struct slot *slot = NULL;
 842	int err;
 843	unsigned int idx;
 844
 845	struct snd_ctl_elem_info *uinfo __free(kfree) =
 846		kzalloc(sizeof(*uinfo), GFP_KERNEL);
 847	struct snd_ctl_elem_value *uctl __free(kfree) =
 848		kzalloc(sizeof(*uctl), GFP_KERNEL);
 849	if (uinfo == NULL || uctl == NULL)
 850		return -ENOMEM;
 851	guard(rwsem_read)(&card->controls_rwsem);
 852	if (card->shutdown)
 853		return -ENODEV;
 854	kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
 855	if (!kctl)
 856		return -ENOENT;
 857	err = kctl->info(kctl, uinfo);
 858	if (err < 0)
 859		return err;
 860	for (idx = 0; idx < 32; idx++) {
 861		if (!(mixer->mask_recsrc & (1 << idx)))
 862			continue;
 863		pslot = &mixer->slots[idx];
 864		slot = pslot->private_data;
 865		if (slot->signature != SNDRV_MIXER_OSS_SIGNATURE)
 866			continue;
 867		if (!(slot->present & SNDRV_MIXER_OSS_PRESENT_CAPTURE))
 868			continue;
 869		if (idx == active_index)
 870			break;
 871		slot = NULL;
 872	}
 873	if (!slot)
 874		return 0;
 875	for (idx = 0; idx < uinfo->count; idx++)
 876		uctl->value.enumerated.item[idx] = slot->capture_item;
 877	err = kctl->put(kctl, uctl);
 878	if (err > 0)
 879		snd_ctl_notify(fmixer->card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
 880	return 0;
 881}
 882
 883struct snd_mixer_oss_assign_table {
 884	int oss_id;
 885	const char *name;
 886	int index;
 887};
 888
 889static int snd_mixer_oss_build_test(struct snd_mixer_oss *mixer, struct slot *slot, const char *name, int index, int item)
 890{
 891	struct snd_kcontrol *kcontrol;
 892	struct snd_card *card = mixer->card;
 893	int err;
 894
 895	struct snd_ctl_elem_info *info __free(kfree) =
 896		kmalloc(sizeof(*info), GFP_KERNEL);
 897	if (!info)
 898		return -ENOMEM;
 899	scoped_guard(rwsem_read, &card->controls_rwsem) {
 900		if (card->shutdown)
 901			return -ENODEV;
 902		kcontrol = snd_mixer_oss_test_id(mixer, name, index);
 903		if (kcontrol == NULL)
 904			return 0;
 905		err = kcontrol->info(kcontrol, info);
 906		if (err < 0)
 907			return err;
 908		slot->numid[item] = kcontrol->id.numid;
 909	}
 910	if (info->count > slot->channels)
 911		slot->channels = info->count;
 912	slot->present |= 1 << item;
 913	return 0;
 914}
 915
 916static void snd_mixer_oss_slot_free(struct snd_mixer_oss_slot *chn)
 917{
 918	struct slot *p = chn->private_data;
 919	if (p) {
 920		if (p->allocated && p->assigned) {
 921			kfree(p->assigned->name);
 922			kfree(p->assigned);
 923		}
 924		kfree(p);
 925	}
 926}
 927
 928static void mixer_slot_clear(struct snd_mixer_oss_slot *rslot)
 929{
 930	int idx = rslot->number; /* remember this */
 931	if (rslot->private_free)
 932		rslot->private_free(rslot);
 933	memset(rslot, 0, sizeof(*rslot));
 934	rslot->number = idx;
 935}
 936
 937/* In a separate function to keep gcc 3.2 happy - do NOT merge this in
 938   snd_mixer_oss_build_input! */
 939static int snd_mixer_oss_build_test_all(struct snd_mixer_oss *mixer,
 940					const struct snd_mixer_oss_assign_table *ptr,
 941					struct slot *slot)
 942{
 943	char str[64];
 944	int err;
 945
 946	err = snd_mixer_oss_build_test(mixer, slot, ptr->name, ptr->index,
 947				       SNDRV_MIXER_OSS_ITEM_GLOBAL);
 948	if (err)
 949		return err;
 950	sprintf(str, "%s Switch", ptr->name);
 951	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 952				       SNDRV_MIXER_OSS_ITEM_GSWITCH);
 953	if (err)
 954		return err;
 955	sprintf(str, "%s Route", ptr->name);
 956	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 957				       SNDRV_MIXER_OSS_ITEM_GROUTE);
 958	if (err)
 959		return err;
 960	sprintf(str, "%s Volume", ptr->name);
 961	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 962				       SNDRV_MIXER_OSS_ITEM_GVOLUME);
 963	if (err)
 964		return err;
 965	sprintf(str, "%s Playback Switch", ptr->name);
 966	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 967				       SNDRV_MIXER_OSS_ITEM_PSWITCH);
 968	if (err)
 969		return err;
 970	sprintf(str, "%s Playback Route", ptr->name);
 971	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 972				       SNDRV_MIXER_OSS_ITEM_PROUTE);
 973	if (err)
 974		return err;
 975	sprintf(str, "%s Playback Volume", ptr->name);
 976	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 977				       SNDRV_MIXER_OSS_ITEM_PVOLUME);
 978	if (err)
 979		return err;
 980	sprintf(str, "%s Capture Switch", ptr->name);
 981	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 982				       SNDRV_MIXER_OSS_ITEM_CSWITCH);
 983	if (err)
 984		return err;
 985	sprintf(str, "%s Capture Route", ptr->name);
 986	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 987				       SNDRV_MIXER_OSS_ITEM_CROUTE);
 988	if (err)
 989		return err;
 990	sprintf(str, "%s Capture Volume", ptr->name);
 991	err = snd_mixer_oss_build_test(mixer, slot, str, ptr->index,
 992				       SNDRV_MIXER_OSS_ITEM_CVOLUME);
 993	if (err)
 994		return err;
 995
 996	return 0;
 997}
 998
 999/*
1000 * build an OSS mixer element.
1001 * ptr_allocated means the entry is dynamically allocated (change via proc file).
1002 * when replace_old = 1, the old entry is replaced with the new one.
1003 */
1004static int snd_mixer_oss_build_input(struct snd_mixer_oss *mixer,
1005				     const struct snd_mixer_oss_assign_table *ptr,
1006				     int ptr_allocated, int replace_old)
1007{
1008	struct slot slot;
1009	struct slot *pslot;
1010	struct snd_kcontrol *kctl;
1011	struct snd_mixer_oss_slot *rslot;
1012	const char *str;
1013	
1014	/* check if already assigned */
1015	if (mixer->slots[ptr->oss_id].get_volume && ! replace_old)
1016		return 0;
1017
1018	memset(&slot, 0, sizeof(slot));
1019	memset(slot.numid, 0xff, sizeof(slot.numid)); /* ID_UNKNOWN */
1020	if (snd_mixer_oss_build_test_all(mixer, ptr, &slot))
1021		return 0;
1022	guard(rwsem_read)(&mixer->card->controls_rwsem);
1023	if (mixer->card->shutdown)
1024		return -ENODEV;
1025	kctl = NULL;
1026	if (!ptr->index)
1027		kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0);
1028	if (kctl) {
1029		struct snd_ctl_elem_info *uinfo __free(kfree) =
1030			kzalloc(sizeof(*uinfo), GFP_KERNEL);
1031
1032		if (!uinfo)
1033			return -ENOMEM;
1034			
1035		if (kctl->info(kctl, uinfo))
1036			return 0;
1037		str = ptr->name;
1038		if (!strcmp(str, "Master"))
1039			str = "Mix";
1040		else if (!strcmp(str, "Master Mono"))
1041			str = "Mix Mono";
1042		slot.capture_item = 0;
1043		if (!strcmp(uinfo->value.enumerated.name, str)) {
1044			slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
1045		} else {
1046			for (slot.capture_item = 1; slot.capture_item < uinfo->value.enumerated.items; slot.capture_item++) {
1047				uinfo->value.enumerated.item = slot.capture_item;
1048				if (kctl->info(kctl, uinfo))
1049					return 0;
1050				if (!strcmp(uinfo->value.enumerated.name, str)) {
1051					slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
1052					break;
1053				}
1054			}
1055		}
1056	}
1057	if (slot.present != 0) {
1058		pslot = kmalloc(sizeof(slot), GFP_KERNEL);
1059		if (! pslot)
1060			return -ENOMEM;
1061		*pslot = slot;
1062		pslot->signature = SNDRV_MIXER_OSS_SIGNATURE;
1063		pslot->assigned = ptr;
1064		pslot->allocated = ptr_allocated;
1065		rslot = &mixer->slots[ptr->oss_id];
1066		mixer_slot_clear(rslot);
1067		rslot->stereo = slot.channels > 1 ? 1 : 0;
1068		rslot->get_volume = snd_mixer_oss_get_volume1;
1069		rslot->put_volume = snd_mixer_oss_put_volume1;
1070		/* note: ES18xx have both Capture Source and XX Capture Volume !!! */
1071		if (slot.present & SNDRV_MIXER_OSS_PRESENT_CSWITCH) {
1072			rslot->get_recsrc = snd_mixer_oss_get_recsrc1_sw;
1073			rslot->put_recsrc = snd_mixer_oss_put_recsrc1_sw;
1074		} else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CROUTE) {
1075			rslot->get_recsrc = snd_mixer_oss_get_recsrc1_route;
1076			rslot->put_recsrc = snd_mixer_oss_put_recsrc1_route;
1077		} else if (slot.present & SNDRV_MIXER_OSS_PRESENT_CAPTURE) {
1078			mixer->mask_recsrc |= 1 << ptr->oss_id;
1079		}
1080		rslot->private_data = pslot;
1081		rslot->private_free = snd_mixer_oss_slot_free;
1082		return 1;
1083	}
1084	return 0;
1085}
1086
1087#ifdef CONFIG_SND_PROC_FS
1088/*
1089 */
1090#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name
1091static const char * const oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {
1092	MIXER_VOL(VOLUME),
1093	MIXER_VOL(BASS),
1094	MIXER_VOL(TREBLE),
1095	MIXER_VOL(SYNTH),
1096	MIXER_VOL(PCM),
1097	MIXER_VOL(SPEAKER),
1098	MIXER_VOL(LINE),
1099	MIXER_VOL(MIC),
1100	MIXER_VOL(CD),
1101	MIXER_VOL(IMIX),
1102	MIXER_VOL(ALTPCM),
1103	MIXER_VOL(RECLEV),
1104	MIXER_VOL(IGAIN),
1105	MIXER_VOL(OGAIN),
1106	MIXER_VOL(LINE1),
1107	MIXER_VOL(LINE2),
1108	MIXER_VOL(LINE3),
1109	MIXER_VOL(DIGITAL1),
1110	MIXER_VOL(DIGITAL2),
1111	MIXER_VOL(DIGITAL3),
1112	MIXER_VOL(PHONEIN),
1113	MIXER_VOL(PHONEOUT),
1114	MIXER_VOL(VIDEO),
1115	MIXER_VOL(RADIO),
1116	MIXER_VOL(MONITOR),
1117};
1118	
1119/*
1120 *  /proc interface
1121 */
1122
1123static void snd_mixer_oss_proc_read(struct snd_info_entry *entry,
1124				    struct snd_info_buffer *buffer)
1125{
1126	struct snd_mixer_oss *mixer = entry->private_data;
1127	int i;
1128
1129	guard(mutex)(&mixer->reg_mutex);
1130	for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) {
1131		struct slot *p;
1132
1133		if (! oss_mixer_names[i])
1134			continue;
1135		p = (struct slot *)mixer->slots[i].private_data;
1136		snd_iprintf(buffer, "%s ", oss_mixer_names[i]);
1137		if (p && p->assigned)
1138			snd_iprintf(buffer, "\"%s\" %d\n",
1139				    p->assigned->name,
1140				    p->assigned->index);
1141		else
1142			snd_iprintf(buffer, "\"\" 0\n");
1143	}
1144}
1145
1146static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
1147				     struct snd_info_buffer *buffer)
1148{
1149	struct snd_mixer_oss *mixer = entry->private_data;
1150	char line[128], str[32], idxstr[16];
1151	const char *cptr;
1152	unsigned int idx;
1153	int ch;
1154	struct snd_mixer_oss_assign_table *tbl;
1155	struct slot *slot;
1156
1157	while (!snd_info_get_line(buffer, line, sizeof(line))) {
1158		cptr = snd_info_get_str(str, line, sizeof(str));
1159		for (ch = 0; ch < SNDRV_OSS_MAX_MIXERS; ch++)
1160			if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0)
1161				break;
1162		if (ch >= SNDRV_OSS_MAX_MIXERS) {
1163			pr_err("ALSA: mixer_oss: invalid OSS volume '%s'\n",
1164			       str);
1165			continue;
1166		}
1167		cptr = snd_info_get_str(str, cptr, sizeof(str));
1168		if (! *str) {
1169			/* remove the entry */
1170			scoped_guard(mutex, &mixer->reg_mutex)
1171				mixer_slot_clear(&mixer->slots[ch]);
1172			continue;
1173		}
1174		snd_info_get_str(idxstr, cptr, sizeof(idxstr));
1175		idx = simple_strtoul(idxstr, NULL, 10);
1176		if (idx >= 0x4000) { /* too big */
1177			pr_err("ALSA: mixer_oss: invalid index %d\n", idx);
1178			continue;
1179		}
1180		scoped_guard(mutex, &mixer->reg_mutex) {
1181			slot = (struct slot *)mixer->slots[ch].private_data;
1182			if (slot && slot->assigned &&
1183			    slot->assigned->index == idx && !strcmp(slot->assigned->name, str))
1184				/* not changed */
1185				break;
1186			tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
1187			if (!tbl)
1188				break;
1189			tbl->oss_id = ch;
1190			tbl->name = kstrdup(str, GFP_KERNEL);
1191			if (!tbl->name) {
1192				kfree(tbl);
1193				break;
1194			}
1195			tbl->index = idx;
1196			if (snd_mixer_oss_build_input(mixer, tbl, 1, 1) <= 0) {
1197				kfree(tbl->name);
1198				kfree(tbl);
1199			}
1200		}
1201	}
1202}
1203
1204static void snd_mixer_oss_proc_init(struct snd_mixer_oss *mixer)
1205{
1206	struct snd_info_entry *entry;
1207
1208	entry = snd_info_create_card_entry(mixer->card, "oss_mixer",
1209					   mixer->card->proc_root);
1210	if (! entry)
1211		return;
1212	entry->content = SNDRV_INFO_CONTENT_TEXT;
1213	entry->mode = S_IFREG | 0644;
1214	entry->c.text.read = snd_mixer_oss_proc_read;
1215	entry->c.text.write = snd_mixer_oss_proc_write;
1216	entry->private_data = mixer;
1217	if (snd_info_register(entry) < 0) {
1218		snd_info_free_entry(entry);
1219		entry = NULL;
1220	}
1221	mixer->proc_entry = entry;
1222}
1223
1224static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer)
1225{
1226	snd_info_free_entry(mixer->proc_entry);
1227	mixer->proc_entry = NULL;
1228}
1229#else /* !CONFIG_SND_PROC_FS */
1230#define snd_mixer_oss_proc_init(mix)
1231#define snd_mixer_oss_proc_done(mix)
1232#endif /* CONFIG_SND_PROC_FS */
1233
1234static void snd_mixer_oss_build(struct snd_mixer_oss *mixer)
1235{
1236	static const struct snd_mixer_oss_assign_table table[] = {
1237		{ SOUND_MIXER_VOLUME, 	"Master",		0 },
1238		{ SOUND_MIXER_VOLUME, 	"Front",		0 }, /* fallback */
1239		{ SOUND_MIXER_BASS,	"Tone Control - Bass",	0 },
1240		{ SOUND_MIXER_TREBLE,	"Tone Control - Treble", 0 },
1241		{ SOUND_MIXER_SYNTH,	"Synth",		0 },
1242		{ SOUND_MIXER_SYNTH,	"FM",			0 }, /* fallback */
1243		{ SOUND_MIXER_SYNTH,	"Music",		0 }, /* fallback */
1244		{ SOUND_MIXER_PCM,	"PCM",			0 },
1245		{ SOUND_MIXER_SPEAKER,	"Beep", 		0 },
1246		{ SOUND_MIXER_SPEAKER,	"PC Speaker", 		0 }, /* fallback */
1247		{ SOUND_MIXER_SPEAKER,	"Speaker", 		0 }, /* fallback */
1248		{ SOUND_MIXER_LINE,	"Line", 		0 },
1249		{ SOUND_MIXER_MIC,	"Mic", 			0 },
1250		{ SOUND_MIXER_CD,	"CD", 			0 },
1251		{ SOUND_MIXER_IMIX,	"Monitor Mix", 		0 },
1252		{ SOUND_MIXER_ALTPCM,	"PCM",			1 },
1253		{ SOUND_MIXER_ALTPCM,	"Headphone",		0 }, /* fallback */
1254		{ SOUND_MIXER_ALTPCM,	"Wave",			0 }, /* fallback */
1255		{ SOUND_MIXER_RECLEV,	"-- nothing --",	0 },
1256		{ SOUND_MIXER_IGAIN,	"Capture",		0 },
1257		{ SOUND_MIXER_OGAIN,	"Playback",		0 },
1258		{ SOUND_MIXER_LINE1,	"Aux",			0 },
1259		{ SOUND_MIXER_LINE2,	"Aux",			1 },
1260		{ SOUND_MIXER_LINE3,	"Aux",			2 },
1261		{ SOUND_MIXER_DIGITAL1,	"Digital",		0 },
1262		{ SOUND_MIXER_DIGITAL1,	"IEC958",		0 }, /* fallback */
1263		{ SOUND_MIXER_DIGITAL1,	"IEC958 Optical",	0 }, /* fallback */
1264		{ SOUND_MIXER_DIGITAL1,	"IEC958 Coaxial",	0 }, /* fallback */
1265		{ SOUND_MIXER_DIGITAL2,	"Digital",		1 },
1266		{ SOUND_MIXER_DIGITAL3,	"Digital",		2 },
1267		{ SOUND_MIXER_PHONEIN,	"Phone",		0 },
1268		{ SOUND_MIXER_PHONEOUT,	"Master Mono",		0 },
1269		{ SOUND_MIXER_PHONEOUT,	"Speaker",		0 }, /*fallback*/
1270		{ SOUND_MIXER_PHONEOUT,	"Mono",			0 }, /*fallback*/
1271		{ SOUND_MIXER_PHONEOUT,	"Phone",		0 }, /* fallback */
1272		{ SOUND_MIXER_VIDEO,	"Video",		0 },
1273		{ SOUND_MIXER_RADIO,	"Radio",		0 },
1274		{ SOUND_MIXER_MONITOR,	"Monitor",		0 }
1275	};
1276	unsigned int idx;
1277	
1278	for (idx = 0; idx < ARRAY_SIZE(table); idx++)
1279		snd_mixer_oss_build_input(mixer, &table[idx], 0, 0);
1280	if (mixer->mask_recsrc) {
1281		mixer->get_recsrc = snd_mixer_oss_get_recsrc2;
1282		mixer->put_recsrc = snd_mixer_oss_put_recsrc2;
1283	}
1284}
1285
1286/*
1287 *
1288 */
1289
1290static int snd_mixer_oss_free1(void *private)
1291{
1292	struct snd_mixer_oss *mixer = private;
1293	struct snd_card *card;
1294	int idx;
1295 
1296	if (!mixer)
1297		return 0;
1298	card = mixer->card;
1299	if (snd_BUG_ON(mixer != card->mixer_oss))
1300		return -ENXIO;
1301	card->mixer_oss = NULL;
1302	for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {
1303		struct snd_mixer_oss_slot *chn = &mixer->slots[idx];
1304		if (chn->private_free)
1305			chn->private_free(chn);
1306	}
1307	kfree(mixer);
1308	return 0;
1309}
1310
1311static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
1312{
1313	struct snd_mixer_oss *mixer;
1314
1315	if (cmd == SND_MIXER_OSS_NOTIFY_REGISTER) {
1316		int idx, err;
1317
1318		mixer = kcalloc(2, sizeof(*mixer), GFP_KERNEL);
1319		if (mixer == NULL)
1320			return -ENOMEM;
1321		mutex_init(&mixer->reg_mutex);
1322		err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
1323					      card, 0,
1324					      &snd_mixer_oss_f_ops, card);
1325		if (err < 0) {
1326			dev_err(card->dev,
1327				"unable to register OSS mixer device %i:%i\n",
1328				card->number, 0);
1329			kfree(mixer);
1330			return err;
1331		}
1332		mixer->oss_dev_alloc = 1;
1333		mixer->card = card;
1334		if (*card->mixername)
1335			strscpy(mixer->name, card->mixername, sizeof(mixer->name));
1336		else
1337			snprintf(mixer->name, sizeof(mixer->name),
1338				 "mixer%i", card->number);
1339#ifdef SNDRV_OSS_INFO_DEV_MIXERS
1340		snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,
1341				      card->number,
1342				      mixer->name);
1343#endif
1344		for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++)
1345			mixer->slots[idx].number = idx;
1346		card->mixer_oss = mixer;
1347		snd_mixer_oss_build(mixer);
1348		snd_mixer_oss_proc_init(mixer);
1349	} else {
1350		mixer = card->mixer_oss;
1351		if (mixer == NULL)
1352			return 0;
1353		if (mixer->oss_dev_alloc) {
1354#ifdef SNDRV_OSS_INFO_DEV_MIXERS
1355			snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
1356#endif
1357			snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
1358			mixer->oss_dev_alloc = 0;
1359		}
1360		if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT)
1361			return 0;
1362		snd_mixer_oss_proc_done(mixer);
1363		return snd_mixer_oss_free1(mixer);
1364	}
1365	return 0;
1366}
1367
1368static int __init alsa_mixer_oss_init(void)
1369{
1370	struct snd_card *card;
1371	int idx;
1372	
1373	snd_mixer_oss_notify_callback = snd_mixer_oss_notify_handler;
1374	for (idx = 0; idx < SNDRV_CARDS; idx++) {
1375		card = snd_card_ref(idx);
1376		if (card) {
1377			snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_REGISTER);
1378			snd_card_unref(card);
1379		}
1380	}
1381	return 0;
1382}
1383
1384static void __exit alsa_mixer_oss_exit(void)
1385{
1386	struct snd_card *card;
1387	int idx;
1388
1389	snd_mixer_oss_notify_callback = NULL;
1390	for (idx = 0; idx < SNDRV_CARDS; idx++) {
1391		card = snd_card_ref(idx);
1392		if (card) {
1393			snd_mixer_oss_notify_handler(card, SND_MIXER_OSS_NOTIFY_FREE);
1394			snd_card_unref(card);
1395		}
1396	}
1397}
1398
1399module_init(alsa_mixer_oss_init)
1400module_exit(alsa_mixer_oss_exit)
~/ Read-only file preview.