1/*-
2 * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
3 * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
4 * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#ifdef HAVE_KERNEL_OPTION_HEADERS
30#include "opt_snd.h"
31#endif
32
33#include <dev/sound/pcm/sound.h>
34
35#include "feeder_if.h"
36#include "mixer_if.h"
37
38SND_DECLARE_FILE("$FreeBSD: stable/11/sys/dev/sound/pcm/mixer.c 359886 2020-04-13 16:31:46Z hselasky $");
39
40static MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
41
42static int mixer_bypass = 1;
43SYSCTL_INT(_hw_snd, OID_AUTO, vpc_mixer_bypass, CTLFLAG_RWTUN,
44    &mixer_bypass, 0,
45    "control channel pcm/rec volume, bypassing real mixer device");
46
47#define MIXER_NAMELEN	16
48struct snd_mixer {
49	KOBJ_FIELDS;
50	void *devinfo;
51	int busy;
52	int hwvol_muted;
53	int hwvol_mixer;
54	int hwvol_step;
55	int type;
56	device_t dev;
57	u_int32_t hwvol_mute_level;
58	u_int32_t devs;
59	u_int32_t recdevs;
60	u_int32_t recsrc;
61	u_int16_t level[32];
62	u_int8_t parent[32];
63	u_int32_t child[32];
64	u_int8_t realdev[32];
65	char name[MIXER_NAMELEN];
66	struct mtx *lock;
67	oss_mixer_enuminfo enuminfo;
68	/**
69	 * Counter is incremented when applications change any of this
70	 * mixer's controls.  A change in value indicates that persistent
71	 * mixer applications should update their displays.
72	 */
73	int modify_counter;
74};
75
76static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
77	[SOUND_MIXER_VOLUME]	= 75,
78	[SOUND_MIXER_BASS]	= 50,
79	[SOUND_MIXER_TREBLE]	= 50,
80	[SOUND_MIXER_SYNTH]	= 75,
81	[SOUND_MIXER_PCM]	= 75,
82	[SOUND_MIXER_SPEAKER]	= 75,
83	[SOUND_MIXER_LINE]	= 75,
84	[SOUND_MIXER_MIC] 	= 25,
85	[SOUND_MIXER_CD]	= 75,
86	[SOUND_MIXER_IGAIN]	= 0,
87	[SOUND_MIXER_LINE1]	= 75,
88	[SOUND_MIXER_VIDEO]	= 75,
89	[SOUND_MIXER_RECLEV]	= 75,
90	[SOUND_MIXER_OGAIN]	= 50,
91	[SOUND_MIXER_MONITOR]	= 75,
92};
93
94static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
95
96static d_open_t mixer_open;
97static d_close_t mixer_close;
98static d_ioctl_t mixer_ioctl;
99
100static struct cdevsw mixer_cdevsw = {
101	.d_version =	D_VERSION,
102	.d_open =	mixer_open,
103	.d_close =	mixer_close,
104	.d_ioctl =	mixer_ioctl,
105	.d_name =	"mixer",
106};
107
108/**
109 * Keeps a count of mixer devices; used only by OSSv4 SNDCTL_SYSINFO ioctl.
110 */
111int mixer_count = 0;
112
113static eventhandler_tag mixer_ehtag = NULL;
114
115static struct cdev *
116mixer_get_devt(device_t dev)
117{
118	struct snddev_info *snddev;
119
120	snddev = device_get_softc(dev);
121
122	return snddev->mixer_dev;
123}
124
125static int
126mixer_lookup(char *devname)
127{
128	int i;
129
130	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
131		if (strncmp(devname, snd_mixernames[i],
132		    strlen(snd_mixernames[i])) == 0)
133			return i;
134	return -1;
135}
136
137#define MIXER_SET_UNLOCK(x, y)		do {				\
138	if ((y) != 0)							\
139		snd_mtxunlock((x)->lock);				\
140} while (0)
141
142#define MIXER_SET_LOCK(x, y)		do {				\
143	if ((y) != 0)							\
144		snd_mtxlock((x)->lock);					\
145} while (0)
146
147static int
148mixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d,
149    u_int left, u_int right)
150{
151	struct pcm_channel *c;
152	int dropmtx, acquiremtx;
153
154	if (PCM_DETACHING(d) || !PCM_REGISTERED(d))
155		return (EINVAL);
156
157	if (mtx_owned(m->lock))
158		dropmtx = 1;
159	else
160		dropmtx = 0;
161
162	if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0)
163		acquiremtx = 0;
164	else
165		acquiremtx = 1;
166
167	/*
168	 * Be careful here. If we're coming from cdev ioctl, it is OK to
169	 * not doing locking AT ALL (except on individual channel) since
170	 * we've been heavily guarded by pcm cv, or if we're still
171	 * under Giant influence. Since we also have mix_* calls, we cannot
172	 * assume such protection and just do the lock as usuall.
173	 */
174	MIXER_SET_UNLOCK(m, dropmtx);
175	MIXER_SET_LOCK(d, acquiremtx);
176
177	CHN_FOREACH(c, d, channels.pcm.busy) {
178		CHN_LOCK(c);
179		if (c->direction == PCMDIR_PLAY &&
180		    (c->feederflags & (1 << FEEDER_VOLUME)))
181			chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right,
182			    (left + right) >> 1);
183		CHN_UNLOCK(c);
184	}
185
186	MIXER_SET_UNLOCK(d, acquiremtx);
187	MIXER_SET_LOCK(m, dropmtx);
188
189	return (0);
190}
191
192static int
193mixer_set_eq(struct snd_mixer *m, struct snddev_info *d,
194    u_int dev, u_int level)
195{
196	struct pcm_channel *c;
197	struct pcm_feeder *f;
198	int tone, dropmtx, acquiremtx;
199
200	if (dev == SOUND_MIXER_TREBLE)
201		tone = FEEDEQ_TREBLE;
202	else if (dev == SOUND_MIXER_BASS)
203		tone = FEEDEQ_BASS;
204	else
205		return (EINVAL);
206
207	if (PCM_DETACHING(d) || !PCM_REGISTERED(d))
208		return (EINVAL);
209
210	if (mtx_owned(m->lock))
211		dropmtx = 1;
212	else
213		dropmtx = 0;
214
215	if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0)
216		acquiremtx = 0;
217	else
218		acquiremtx = 1;
219
220	/*
221	 * Be careful here. If we're coming from cdev ioctl, it is OK to
222	 * not doing locking AT ALL (except on individual channel) since
223	 * we've been heavily guarded by pcm cv, or if we're still
224	 * under Giant influence. Since we also have mix_* calls, we cannot
225	 * assume such protection and just do the lock as usuall.
226	 */
227	MIXER_SET_UNLOCK(m, dropmtx);
228	MIXER_SET_LOCK(d, acquiremtx);
229
230	CHN_FOREACH(c, d, channels.pcm.busy) {
231		CHN_LOCK(c);
232		f = chn_findfeeder(c, FEEDER_EQ);
233		if (f != NULL)
234			(void)FEEDER_SET(f, tone, level);
235		CHN_UNLOCK(c);
236	}
237
238	MIXER_SET_UNLOCK(d, acquiremtx);
239	MIXER_SET_LOCK(m, dropmtx);
240
241	return (0);
242}
243
244static int
245mixer_set(struct snd_mixer *m, u_int dev, u_int lev)
246{
247	struct snddev_info *d;
248	u_int l, r, tl, tr;
249	u_int32_t parent = SOUND_MIXER_NONE, child = 0;
250	u_int32_t realdev;
251	int i, dropmtx;
252
253	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES ||
254	    (0 == (m->devs & (1 << dev))))
255		return -1;
256
257	l = min((lev & 0x00ff), 100);
258	r = min(((lev & 0xff00) >> 8), 100);
259	realdev = m->realdev[dev];
260
261	d = device_get_softc(m->dev);
262	if (d == NULL)
263		return -1;
264
265	/* It is safe to drop this mutex due to Giant. */
266	if (!(d->flags & SD_F_MPSAFE) && mtx_owned(m->lock) != 0)
267		dropmtx = 1;
268	else
269		dropmtx = 0;
270
271	MIXER_SET_UNLOCK(m, dropmtx);
272
273	/* TODO: recursive handling */
274	parent = m->parent[dev];
275	if (parent >= SOUND_MIXER_NRDEVICES)
276		parent = SOUND_MIXER_NONE;
277	if (parent == SOUND_MIXER_NONE)
278		child = m->child[dev];
279
280	if (parent != SOUND_MIXER_NONE) {
281		tl = (l * (m->level[parent] & 0x00ff)) / 100;
282		tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100;
283		if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
284			(void)mixer_set_softpcmvol(m, d, tl, tr);
285		else if (realdev != SOUND_MIXER_NONE &&
286		    MIXER_SET(m, realdev, tl, tr) < 0) {
287			MIXER_SET_LOCK(m, dropmtx);
288			return -1;
289		}
290	} else if (child != 0) {
291		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
292			if (!(child & (1 << i)) || m->parent[i] != dev)
293				continue;
294			realdev = m->realdev[i];
295			tl = (l * (m->level[i] & 0x00ff)) / 100;
296			tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100;
297			if (i == SOUND_MIXER_PCM &&
298			    (d->flags & SD_F_SOFTPCMVOL))
299				(void)mixer_set_softpcmvol(m, d, tl, tr);
300			else if (realdev != SOUND_MIXER_NONE)
301				MIXER_SET(m, realdev, tl, tr);
302		}
303		realdev = m->realdev[dev];
304		if (realdev != SOUND_MIXER_NONE &&
305		    MIXER_SET(m, realdev, l, r) < 0) {
306				MIXER_SET_LOCK(m, dropmtx);
307				return -1;
308		}
309	} else {
310		if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
311			(void)mixer_set_softpcmvol(m, d, l, r);
312		else if ((dev == SOUND_MIXER_TREBLE ||
313		    dev == SOUND_MIXER_BASS) && (d->flags & SD_F_EQ))
314			(void)mixer_set_eq(m, d, dev, (l + r) >> 1);
315		else if (realdev != SOUND_MIXER_NONE &&
316		    MIXER_SET(m, realdev, l, r) < 0) {
317			MIXER_SET_LOCK(m, dropmtx);
318			return -1;
319		}
320	}
321
322	MIXER_SET_LOCK(m, dropmtx);
323
324	m->level[dev] = l | (r << 8);
325	m->modify_counter++;
326
327	return 0;
328}
329
330static int
331mixer_get(struct snd_mixer *mixer, int dev)
332{
333	if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
334		return mixer->level[dev];
335	else
336		return -1;
337}
338
339static int
340mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
341{
342	struct snddev_info *d;
343	u_int32_t recsrc;
344	int dropmtx;
345
346	d = device_get_softc(mixer->dev);
347	if (d == NULL)
348		return -1;
349	if (!(d->flags & SD_F_MPSAFE) && mtx_owned(mixer->lock) != 0)
350		dropmtx = 1;
351	else
352		dropmtx = 0;
353	src &= mixer->recdevs;
354	if (src == 0)
355		src = mixer->recdevs & SOUND_MASK_MIC;
356	if (src == 0)
357		src = mixer->recdevs & SOUND_MASK_MONITOR;
358	if (src == 0)
359		src = mixer->recdevs & SOUND_MASK_LINE;
360	if (src == 0 && mixer->recdevs != 0)
361		src = (1 << (ffs(mixer->recdevs) - 1));
362	/* It is safe to drop this mutex due to Giant. */
363	MIXER_SET_UNLOCK(mixer, dropmtx);
364	recsrc = MIXER_SETRECSRC(mixer, src);
365	MIXER_SET_LOCK(mixer, dropmtx);
366
367	mixer->recsrc = recsrc;
368
369	return 0;
370}
371
372static int
373mixer_getrecsrc(struct snd_mixer *mixer)
374{
375	return mixer->recsrc;
376}
377
378/**
379 * @brief Retrieve the route number of the current recording device
380 *
381 * OSSv4 assigns routing numbers to recording devices, unlike the previous
382 * API which relied on a fixed table of device numbers and names.  This
383 * function returns the routing number of the device currently selected
384 * for recording.
385 *
386 * For now, this function is kind of a goofy compatibility stub atop the
387 * existing sound system.  (For example, in theory, the old sound system
388 * allows multiple recording devices to be specified via a bitmask.)
389 *
390 * @param m	mixer context container thing
391 *
392 * @retval 0		success
393 * @retval EIDRM	no recording device found (generally not possible)
394 * @todo Ask about error code
395 */
396static int
397mixer_get_recroute(struct snd_mixer *m, int *route)
398{
399	int i, cnt;
400
401	cnt = 0;
402
403	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
404		/** @todo can user set a multi-device mask? (== or &?) */
405		if ((1 << i) == m->recsrc)
406			break;
407		if ((1 << i) & m->recdevs)
408			++cnt;
409	}
410
411	if (i == SOUND_MIXER_NRDEVICES)
412		return EIDRM;
413
414	*route = cnt;
415	return 0;
416}
417
418/**
419 * @brief Select a device for recording
420 *
421 * This function sets a recording source based on a recording device's
422 * routing number.  Said number is translated to an old school recdev
423 * mask and passed over mixer_setrecsrc.
424 *
425 * @param m	mixer context container thing
426 *
427 * @retval 0		success(?)
428 * @retval EINVAL	User specified an invalid device number
429 * @retval otherwise	error from mixer_setrecsrc
430 */
431static int
432mixer_set_recroute(struct snd_mixer *m, int route)
433{
434	int i, cnt, ret;
435
436	ret = 0;
437	cnt = 0;
438
439	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
440		if ((1 << i) & m->recdevs) {
441			if (route == cnt)
442				break;
443			++cnt;
444		}
445	}
446
447	if (i == SOUND_MIXER_NRDEVICES)
448		ret = EINVAL;
449	else
450		ret = mixer_setrecsrc(m, (1 << i));
451
452	return ret;
453}
454
455void
456mix_setdevs(struct snd_mixer *m, u_int32_t v)
457{
458	struct snddev_info *d;
459	int i;
460
461	if (m == NULL)
462		return;
463
464	d = device_get_softc(m->dev);
465	if (d != NULL && (d->flags & SD_F_SOFTPCMVOL))
466		v |= SOUND_MASK_PCM;
467	if (d != NULL && (d->flags & SD_F_EQ))
468		v |= SOUND_MASK_TREBLE | SOUND_MASK_BASS;
469	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
470		if (m->parent[i] < SOUND_MIXER_NRDEVICES)
471			v |= 1 << m->parent[i];
472		v |= m->child[i];
473	}
474	m->devs = v;
475}
476
477/**
478 * @brief Record mask of available recording devices
479 *
480 * Calling functions are responsible for defining the mask of available
481 * recording devices.  This function records that value in a structure
482 * used by the rest of the mixer code.
483 *
484 * This function also populates a structure used by the SNDCTL_DSP_*RECSRC*
485 * family of ioctls that are part of OSSV4.  All recording device labels
486 * are concatenated in ascending order corresponding to their routing
487 * numbers.  (Ex:  a system might have 0 => 'vol', 1 => 'cd', 2 => 'line',
488 * etc.)  For now, these labels are just the standard recording device
489 * names (cd, line1, etc.), but will eventually be fully dynamic and user
490 * controlled.
491 *
492 * @param m	mixer device context container thing
493 * @param v	mask of recording devices
494 */
495void
496mix_setrecdevs(struct snd_mixer *m, u_int32_t v)
497{
498	oss_mixer_enuminfo *ei;
499	char *loc;
500	int i, nvalues, nwrote, nleft, ncopied;
501
502	ei = &m->enuminfo;
503
504	nvalues = 0;
505	nwrote = 0;
506	nleft = sizeof(ei->strings);
507	loc = ei->strings;
508
509	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
510		if ((1 << i) & v) {
511			ei->strindex[nvalues] = nwrote;
512			ncopied = strlcpy(loc, snd_mixernames[i], nleft) + 1;
513			    /* strlcpy retval doesn't include terminator */
514
515			nwrote += ncopied;
516			nleft -= ncopied;
517			nvalues++;
518
519			/*
520			 * XXX I don't think this should ever be possible.
521			 * Even with a move to dynamic device/channel names,
522			 * each label is limited to ~16 characters, so that'd
523			 * take a LOT to fill this buffer.
524			 */
525			if ((nleft <= 0) || (nvalues >= OSS_ENUM_MAXVALUE)) {
526				device_printf(m->dev,
527				    "mix_setrecdevs:  Not enough room to store device names--please file a bug report.\n");
528				device_printf(m->dev,
529				    "mix_setrecdevs:  Please include details about your sound hardware, OS version, etc.\n");
530				break;
531			}
532
533			loc = &ei->strings[nwrote];
534		}
535	}
536
537	/*
538	 * NB:	The SNDCTL_DSP_GET_RECSRC_NAMES ioctl ignores the dev
539	 * 	and ctrl fields.
540	 */
541	ei->nvalues = nvalues;
542	m->recdevs = v;
543}
544
545void
546mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs)
547{
548	u_int32_t mask = 0;
549	int i;
550
551	if (m == NULL || parent >= SOUND_MIXER_NRDEVICES)
552		return;
553	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
554		if (i == parent)
555			continue;
556		if (childs & (1 << i)) {
557			mask |= 1 << i;
558			if (m->parent[i] < SOUND_MIXER_NRDEVICES)
559				m->child[m->parent[i]] &= ~(1 << i);
560			m->parent[i] = parent;
561			m->child[i] = 0;
562		}
563	}
564	mask &= ~(1 << parent);
565	m->child[parent] = mask;
566}
567
568void
569mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev)
570{
571	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES ||
572	    !(realdev == SOUND_MIXER_NONE || realdev < SOUND_MIXER_NRDEVICES))
573		return;
574	m->realdev[dev] = realdev;
575}
576
577u_int32_t
578mix_getparent(struct snd_mixer *m, u_int32_t dev)
579{
580	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES)
581		return SOUND_MIXER_NONE;
582	return m->parent[dev];
583}
584
585u_int32_t
586mix_getchild(struct snd_mixer *m, u_int32_t dev)
587{
588	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES)
589		return 0;
590	return m->child[dev];
591}
592
593u_int32_t
594mix_getdevs(struct snd_mixer *m)
595{
596	return m->devs;
597}
598
599u_int32_t
600mix_getrecdevs(struct snd_mixer *m)
601{
602	return m->recdevs;
603}
604
605void *
606mix_getdevinfo(struct snd_mixer *m)
607{
608	return m->devinfo;
609}
610
611static struct snd_mixer *
612mixer_obj_create(device_t dev, kobj_class_t cls, void *devinfo,
613    int type, const char *desc)
614{
615	struct snd_mixer *m;
616	int i;
617
618	KASSERT(dev != NULL && cls != NULL && devinfo != NULL,
619	    ("%s(): NULL data dev=%p cls=%p devinfo=%p",
620	    __func__, dev, cls, devinfo));
621	KASSERT(type == MIXER_TYPE_PRIMARY || type == MIXER_TYPE_SECONDARY,
622	    ("invalid mixer type=%d", type));
623
624	m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
625	snprintf(m->name, sizeof(m->name), "%s:mixer",
626	    device_get_nameunit(dev));
627	if (desc != NULL) {
628		strlcat(m->name, ":", sizeof(m->name));
629		strlcat(m->name, desc, sizeof(m->name));
630	}
631	m->lock = snd_mtxcreate(m->name, (type == MIXER_TYPE_PRIMARY) ?
632	    "primary pcm mixer" : "secondary pcm mixer");
633	m->type = type;
634	m->devinfo = devinfo;
635	m->busy = 0;
636	m->dev = dev;
637	for (i = 0; i < (sizeof(m->parent) / sizeof(m->parent[0])); i++) {
638		m->parent[i] = SOUND_MIXER_NONE;
639		m->child[i] = 0;
640		m->realdev[i] = i;
641	}
642
643	if (MIXER_INIT(m)) {
644		snd_mtxlock(m->lock);
645		snd_mtxfree(m->lock);
646		kobj_delete((kobj_t)m, M_MIXER);
647		return (NULL);
648	}
649
650	return (m);
651}
652
653int
654mixer_delete(struct snd_mixer *m)
655{
656	KASSERT(m != NULL, ("NULL snd_mixer"));
657	KASSERT(m->type == MIXER_TYPE_SECONDARY,
658	    ("%s(): illegal mixer type=%d", __func__, m->type));
659
660	/* mixer uninit can sleep --hps */
661
662	MIXER_UNINIT(m);
663
664	snd_mtxfree(m->lock);
665	kobj_delete((kobj_t)m, M_MIXER);
666
667	--mixer_count;
668
669	return (0);
670}
671
672struct snd_mixer *
673mixer_create(device_t dev, kobj_class_t cls, void *devinfo, const char *desc)
674{
675	struct snd_mixer *m;
676
677	m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_SECONDARY, desc);
678
679	if (m != NULL)
680		++mixer_count;
681
682	return (m);
683}
684
685int
686mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
687{
688	struct snddev_info *snddev;
689	struct snd_mixer *m;
690	u_int16_t v;
691	struct cdev *pdev;
692	int i, unit, devunit, val;
693
694	snddev = device_get_softc(dev);
695	if (snddev == NULL)
696		return (-1);
697
698	if (resource_int_value(device_get_name(dev),
699	    device_get_unit(dev), "eq", &val) == 0 && val != 0) {
700		snddev->flags |= SD_F_EQ;
701		if ((val & SD_F_EQ_MASK) == val)
702			snddev->flags |= val;
703		else
704			snddev->flags |= SD_F_EQ_DEFAULT;
705		snddev->eqpreamp = 0;
706	}
707
708	m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_PRIMARY, NULL);
709	if (m == NULL)
710		return (-1);
711
712	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
713		v = snd_mixerdefaults[i];
714
715		if (resource_int_value(device_get_name(dev),
716		    device_get_unit(dev), snd_mixernames[i], &val) == 0) {
717			if (val >= 0 && val <= 100) {
718				v = (u_int16_t) val;
719			}
720		}
721
722		mixer_set(m, i, v | (v << 8));
723	}
724
725	mixer_setrecsrc(m, 0); /* Set default input. */
726
727	unit = device_get_unit(dev);
728	devunit = snd_mkunit(unit, SND_DEV_CTL, 0);
729	pdev = make_dev(&mixer_cdevsw, PCMMINOR(devunit),
730		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
731	pdev->si_drv1 = m;
732	snddev->mixer_dev = pdev;
733
734	++mixer_count;
735
736	if (bootverbose) {
737		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
738			if (!(m->devs & (1 << i)))
739				continue;
740			if (m->realdev[i] != i) {
741				device_printf(dev, "Mixer \"%s\" -> \"%s\":",
742				    snd_mixernames[i],
743				    (m->realdev[i] < SOUND_MIXER_NRDEVICES) ?
744				    snd_mixernames[m->realdev[i]] : "none");
745			} else {
746				device_printf(dev, "Mixer \"%s\":",
747				    snd_mixernames[i]);
748			}
749			if (m->parent[i] < SOUND_MIXER_NRDEVICES)
750				printf(" parent=\"%s\"",
751				    snd_mixernames[m->parent[i]]);
752			if (m->child[i] != 0)
753				printf(" child=0x%08x", m->child[i]);
754			printf("\n");
755		}
756		if (snddev->flags & SD_F_SOFTPCMVOL)
757			device_printf(dev, "Soft PCM mixer ENABLED\n");
758		if (snddev->flags & SD_F_EQ)
759			device_printf(dev, "EQ Treble/Bass ENABLED\n");
760	}
761
762	return (0);
763}
764
765int
766mixer_uninit(device_t dev)
767{
768	int i;
769	struct snddev_info *d;
770	struct snd_mixer *m;
771	struct cdev *pdev;
772
773	d = device_get_softc(dev);
774	pdev = mixer_get_devt(dev);
775	if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL)
776		return EBADF;
777
778	m = pdev->si_drv1;
779	KASSERT(m != NULL, ("NULL snd_mixer"));
780	KASSERT(m->type == MIXER_TYPE_PRIMARY,
781	    ("%s(): illegal mixer type=%d", __func__, m->type));
782
783	snd_mtxlock(m->lock);
784
785	if (m->busy) {
786		snd_mtxunlock(m->lock);
787		return EBUSY;
788	}
789
790	/* destroy dev can sleep --hps */
791
792	snd_mtxunlock(m->lock);
793
794	pdev->si_drv1 = NULL;
795	destroy_dev(pdev);
796
797	snd_mtxlock(m->lock);
798
799	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
800		mixer_set(m, i, 0);
801
802	mixer_setrecsrc(m, SOUND_MASK_MIC);
803
804	snd_mtxunlock(m->lock);
805
806	/* mixer uninit can sleep --hps */
807
808	MIXER_UNINIT(m);
809
810	snd_mtxfree(m->lock);
811	kobj_delete((kobj_t)m, M_MIXER);
812
813	d->mixer_dev = NULL;
814
815	--mixer_count;
816
817	return 0;
818}
819
820int
821mixer_reinit(device_t dev)
822{
823	struct snd_mixer *m;
824	struct cdev *pdev;
825	int i;
826
827	pdev = mixer_get_devt(dev);
828	m = pdev->si_drv1;
829	snd_mtxlock(m->lock);
830
831	i = MIXER_REINIT(m);
832	if (i) {
833		snd_mtxunlock(m->lock);
834		return i;
835	}
836
837	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
838		mixer_set(m, i, m->level[i]);
839
840	mixer_setrecsrc(m, m->recsrc);
841	snd_mtxunlock(m->lock);
842
843	return 0;
844}
845
846static int
847sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
848{
849	char devname[32];
850	int error, dev;
851	struct snd_mixer *m;
852
853	m = oidp->oid_arg1;
854	snd_mtxlock(m->lock);
855	strlcpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
856	snd_mtxunlock(m->lock);
857	error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
858	snd_mtxlock(m->lock);
859	if (error == 0 && req->newptr != NULL) {
860		dev = mixer_lookup(devname);
861		if (dev == -1) {
862			snd_mtxunlock(m->lock);
863			return EINVAL;
864		}
865		else if (dev != m->hwvol_mixer) {
866			m->hwvol_mixer = dev;
867			m->hwvol_muted = 0;
868		}
869	}
870	snd_mtxunlock(m->lock);
871	return error;
872}
873
874int
875mixer_hwvol_init(device_t dev)
876{
877	struct snd_mixer *m;
878	struct cdev *pdev;
879
880	pdev = mixer_get_devt(dev);
881	m = pdev->si_drv1;
882
883	m->hwvol_mixer = SOUND_MIXER_VOLUME;
884	m->hwvol_step = 5;
885	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
886	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
887            OID_AUTO, "hwvol_step", CTLFLAG_RWTUN, &m->hwvol_step, 0, "");
888	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
889	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
890            OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RWTUN, m, 0,
891	    sysctl_hw_snd_hwvol_mixer, "A", "");
892	return 0;
893}
894
895void
896mixer_hwvol_mute_locked(struct snd_mixer *m)
897{
898	if (m->hwvol_muted) {
899		m->hwvol_muted = 0;
900		mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
901	} else {
902		m->hwvol_muted++;
903		m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
904		mixer_set(m, m->hwvol_mixer, 0);
905	}
906}
907
908void
909mixer_hwvol_mute(device_t dev)
910{
911	struct snd_mixer *m;
912	struct cdev *pdev;
913
914	pdev = mixer_get_devt(dev);
915	m = pdev->si_drv1;
916	snd_mtxlock(m->lock);
917	mixer_hwvol_mute_locked(m);
918	snd_mtxunlock(m->lock);
919}
920
921void
922mixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step)
923{
924	int level, left, right;
925
926	if (m->hwvol_muted) {
927		m->hwvol_muted = 0;
928		level = m->hwvol_mute_level;
929	} else
930		level = mixer_get(m, m->hwvol_mixer);
931	if (level != -1) {
932		left = level & 0xff;
933		right = (level >> 8) & 0xff;
934		left += left_step * m->hwvol_step;
935		if (left < 0)
936			left = 0;
937		else if (left > 100)
938			left = 100;
939		right += right_step * m->hwvol_step;
940		if (right < 0)
941			right = 0;
942		else if (right > 100)
943			right = 100;
944		mixer_set(m, m->hwvol_mixer, left | right << 8);
945	}
946}
947
948void
949mixer_hwvol_step(device_t dev, int left_step, int right_step)
950{
951	struct snd_mixer *m;
952	struct cdev *pdev;
953
954	pdev = mixer_get_devt(dev);
955	m = pdev->si_drv1;
956	snd_mtxlock(m->lock);
957	mixer_hwvol_step_locked(m, left_step, right_step);
958	snd_mtxunlock(m->lock);
959}
960
961int
962mixer_busy(struct snd_mixer *m)
963{
964	KASSERT(m != NULL, ("NULL snd_mixer"));
965
966	return (m->busy);
967}
968
969int
970mix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right)
971{
972	int ret;
973
974	KASSERT(m != NULL, ("NULL snd_mixer"));
975
976	snd_mtxlock(m->lock);
977	ret = mixer_set(m, dev, left | (right << 8));
978	snd_mtxunlock(m->lock);
979
980	return ((ret != 0) ? ENXIO : 0);
981}
982
983int
984mix_get(struct snd_mixer *m, u_int dev)
985{
986	int ret;
987
988	KASSERT(m != NULL, ("NULL snd_mixer"));
989
990	snd_mtxlock(m->lock);
991	ret = mixer_get(m, dev);
992	snd_mtxunlock(m->lock);
993
994	return (ret);
995}
996
997int
998mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
999{
1000	int ret;
1001
1002	KASSERT(m != NULL, ("NULL snd_mixer"));
1003
1004	snd_mtxlock(m->lock);
1005	ret = mixer_setrecsrc(m, src);
1006	snd_mtxunlock(m->lock);
1007
1008	return ((ret != 0) ? ENXIO : 0);
1009}
1010
1011u_int32_t
1012mix_getrecsrc(struct snd_mixer *m)
1013{
1014	u_int32_t ret;
1015
1016	KASSERT(m != NULL, ("NULL snd_mixer"));
1017
1018	snd_mtxlock(m->lock);
1019	ret = mixer_getrecsrc(m);
1020	snd_mtxunlock(m->lock);
1021
1022	return (ret);
1023}
1024
1025int
1026mix_get_type(struct snd_mixer *m)
1027{
1028	KASSERT(m != NULL, ("NULL snd_mixer"));
1029
1030	return (m->type);
1031}
1032
1033device_t
1034mix_get_dev(struct snd_mixer *m)
1035{
1036	KASSERT(m != NULL, ("NULL snd_mixer"));
1037
1038	return (m->dev);
1039}
1040
1041/* ----------------------------------------------------------------------- */
1042
1043static int
1044mixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
1045{
1046	struct snddev_info *d;
1047	struct snd_mixer *m;
1048
1049
1050	if (i_dev == NULL || i_dev->si_drv1 == NULL)
1051		return (EBADF);
1052
1053	m = i_dev->si_drv1;
1054	d = device_get_softc(m->dev);
1055	if (PCM_DETACHING(d) || !PCM_REGISTERED(d))
1056		return (EBADF);
1057
1058	/* XXX Need Giant magic entry ??? */
1059
1060	snd_mtxlock(m->lock);
1061	m->busy = 1;
1062	snd_mtxunlock(m->lock);
1063
1064	return (0);
1065}
1066
1067static int
1068mixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
1069{
1070	struct snddev_info *d;
1071	struct snd_mixer *m;
1072	int ret;
1073
1074	if (i_dev == NULL || i_dev->si_drv1 == NULL)
1075		return (EBADF);
1076
1077	m = i_dev->si_drv1;
1078	d = device_get_softc(m->dev);
1079	if (!PCM_REGISTERED(d))
1080		return (EBADF);
1081
1082	/* XXX Need Giant magic entry ??? */
1083
1084	snd_mtxlock(m->lock);
1085	ret = (m->busy == 0) ? EBADF : 0;
1086	m->busy = 0;
1087	snd_mtxunlock(m->lock);
1088
1089	return (ret);
1090}
1091
1092static int
1093mixer_ioctl_channel(struct cdev *dev, u_long cmd, caddr_t arg, int mode,
1094    struct thread *td, int from)
1095{
1096	struct snddev_info *d;
1097	struct snd_mixer *m;
1098	struct pcm_channel *c, *rdch, *wrch;
1099	pid_t pid;
1100	int j, ret;
1101
1102	if (td == NULL || td->td_proc == NULL)
1103		return (-1);
1104
1105	m = dev->si_drv1;
1106	d = device_get_softc(m->dev);
1107	j = cmd & 0xff;
1108
1109	switch (j) {
1110	case SOUND_MIXER_PCM:
1111	case SOUND_MIXER_RECLEV:
1112	case SOUND_MIXER_DEVMASK:
1113	case SOUND_MIXER_CAPS:
1114	case SOUND_MIXER_STEREODEVS:
1115		break;
1116	default:
1117		return (-1);
1118		break;
1119	}
1120
1121	pid = td->td_proc->p_pid;
1122	rdch = NULL;
1123	wrch = NULL;
1124	c = NULL;
1125	ret = -1;
1126
1127	/*
1128	 * This is unfair. Imagine single proc opening multiple
1129	 * instances of same direction. What we do right now
1130	 * is looking for the first matching proc/pid, and just
1131	 * that. Nothing more. Consider it done.
1132	 *
1133	 * The better approach of controlling specific channel
1134	 * pcm or rec volume is by doing mixer ioctl
1135	 * (SNDCTL_DSP_[SET|GET][PLAY|REC]VOL / SOUND_MIXER_[PCM|RECLEV]
1136	 * on its open fd, rather than cracky mixer bypassing here.
1137	 */
1138	CHN_FOREACH(c, d, channels.pcm.opened) {
1139		CHN_LOCK(c);
1140		if (c->pid != pid ||
1141		    !(c->feederflags & (1 << FEEDER_VOLUME))) {
1142			CHN_UNLOCK(c);
1143			continue;
1144		}
1145		if (rdch == NULL && c->direction == PCMDIR_REC) {
1146			rdch = c;
1147			if (j == SOUND_MIXER_RECLEV)
1148				goto mixer_ioctl_channel_proc;
1149		} else if (wrch == NULL && c->direction == PCMDIR_PLAY) {
1150			wrch = c;
1151			if (j == SOUND_MIXER_PCM)
1152				goto mixer_ioctl_channel_proc;
1153		}
1154		CHN_UNLOCK(c);
1155		if (rdch != NULL && wrch != NULL)
1156			break;
1157	}
1158
1159	if (rdch == NULL && wrch == NULL)
1160		return (-1);
1161
1162	if ((j == SOUND_MIXER_DEVMASK || j == SOUND_MIXER_CAPS ||
1163	    j == SOUND_MIXER_STEREODEVS) &&
1164	    (cmd & ~0xff) == MIXER_READ(0)) {
1165		snd_mtxlock(m->lock);
1166		*(int *)arg = mix_getdevs(m);
1167		snd_mtxunlock(m->lock);
1168		if (rdch != NULL)
1169			*(int *)arg |= SOUND_MASK_RECLEV;
1170		if (wrch != NULL)
1171			*(int *)arg |= SOUND_MASK_PCM;
1172		ret = 0;
1173	}
1174
1175	return (ret);
1176
1177mixer_ioctl_channel_proc:
1178
1179	KASSERT(c != NULL, ("%s(): NULL channel", __func__));
1180	CHN_LOCKASSERT(c);
1181
1182	if ((cmd & ~0xff) == MIXER_WRITE(0)) {
1183		int left, right, center;
1184
1185		left = *(int *)arg & 0x7f;
1186		right = (*(int *)arg >> 8) & 0x7f;
1187		center = (left + right) >> 1;
1188		chn_setvolume_multi(c, SND_VOL_C_PCM, left, right, center);
1189	} else if ((cmd & ~0xff) == MIXER_READ(0)) {
1190		*(int *)arg = CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL);
1191		*(int *)arg |=
1192		    CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
1193	}
1194
1195	CHN_UNLOCK(c);
1196
1197	return (0);
1198}
1199
1200static int
1201mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
1202    struct thread *td)
1203{
1204	struct snddev_info *d;
1205	int ret;
1206
1207	if (i_dev == NULL || i_dev->si_drv1 == NULL)
1208		return (EBADF);
1209
1210	d = device_get_softc(((struct snd_mixer *)i_dev->si_drv1)->dev);
1211	if (PCM_DETACHING(d) || !PCM_REGISTERED(d))
1212		return (EBADF);
1213
1214	PCM_GIANT_ENTER(d);
1215	PCM_ACQUIRE_QUICK(d);
1216
1217	ret = -1;
1218
1219	if (mixer_bypass != 0 && (d->flags & SD_F_VPC))
1220		ret = mixer_ioctl_channel(i_dev, cmd, arg, mode, td,
1221		    MIXER_CMD_CDEV);
1222
1223	if (ret == -1)
1224		ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td,
1225		    MIXER_CMD_CDEV);
1226
1227	PCM_RELEASE_QUICK(d);
1228	PCM_GIANT_LEAVE(d);
1229
1230	return (ret);
1231}
1232
1233static void
1234mixer_mixerinfo(struct snd_mixer *m, mixer_info *mi)
1235{
1236	bzero((void *)mi, sizeof(*mi));
1237	strlcpy(mi->id, m->name, sizeof(mi->id));
1238	strlcpy(mi->name, device_get_desc(m->dev), sizeof(mi->name));
1239	mi->modify_counter = m->modify_counter;
1240}
1241
1242/*
1243 * XXX Make sure you can guarantee concurrency safety before calling this
1244 *     function, be it through Giant, PCM_*, etc !
1245 */
1246int
1247mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
1248    struct thread *td, int from)
1249{
1250	struct snd_mixer *m;
1251	int ret = EINVAL, *arg_i = (int *)arg;
1252	int v = -1, j = cmd & 0xff;
1253
1254	/*
1255	 * Certain ioctls may be made on any type of device (audio, mixer,
1256	 * and MIDI).  Handle those special cases here.
1257	 */
1258	if (IOCGROUP(cmd) == 'X') {
1259		switch (cmd) {
1260		case SNDCTL_SYSINFO:
1261			sound_oss_sysinfo((oss_sysinfo *)arg);
1262			return (0);
1263		case SNDCTL_CARDINFO:
1264			return (sound_oss_card_info((oss_card_info *)arg));
1265	    	case SNDCTL_AUDIOINFO:
1266	    	case SNDCTL_AUDIOINFO_EX:
1267	    	case SNDCTL_ENGINEINFO:
1268			return (dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg));
1269		case SNDCTL_MIXERINFO:
1270			return (mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg));
1271		}
1272		return (EINVAL);
1273	}
1274
1275	m = i_dev->si_drv1;
1276
1277	if (m == NULL)
1278		return (EBADF);
1279
1280	snd_mtxlock(m->lock);
1281	if (from == MIXER_CMD_CDEV && !m->busy) {
1282		snd_mtxunlock(m->lock);
1283		return (EBADF);
1284	}
1285	switch (cmd) {
1286	case SNDCTL_DSP_GET_RECSRC_NAMES:
1287		bcopy((void *)&m->enuminfo, arg, sizeof(oss_mixer_enuminfo));
1288		ret = 0;
1289		goto done;
1290	case SNDCTL_DSP_GET_RECSRC:
1291		ret = mixer_get_recroute(m, arg_i);
1292		goto done;
1293	case SNDCTL_DSP_SET_RECSRC:
1294		ret = mixer_set_recroute(m, *arg_i);
1295		goto done;
1296	case OSS_GETVERSION:
1297		*arg_i = SOUND_VERSION;
1298		ret = 0;
1299		goto done;
1300	case SOUND_MIXER_INFO:
1301		mixer_mixerinfo(m, (mixer_info *)arg);
1302		ret = 0;
1303		goto done;
1304	}
1305	if ((cmd & ~0xff) == MIXER_WRITE(0)) {
1306		if (j == SOUND_MIXER_RECSRC)
1307			ret = mixer_setrecsrc(m, *arg_i);
1308		else
1309			ret = mixer_set(m, j, *arg_i);
1310		snd_mtxunlock(m->lock);
1311		return ((ret == 0) ? 0 : ENXIO);
1312	}
1313	if ((cmd & ~0xff) == MIXER_READ(0)) {
1314		switch (j) {
1315		case SOUND_MIXER_DEVMASK:
1316		case SOUND_MIXER_CAPS:
1317		case SOUND_MIXER_STEREODEVS:
1318			v = mix_getdevs(m);
1319			break;
1320		case SOUND_MIXER_RECMASK:
1321			v = mix_getrecdevs(m);
1322			break;
1323		case SOUND_MIXER_RECSRC:
1324			v = mixer_getrecsrc(m);
1325			break;
1326		default:
1327			v = mixer_get(m, j);
1328		}
1329		*arg_i = v;
1330		snd_mtxunlock(m->lock);
1331		return ((v != -1) ? 0 : ENXIO);
1332	}
1333done:
1334	snd_mtxunlock(m->lock);
1335	return (ret);
1336}
1337
1338static void
1339mixer_clone(void *arg,
1340    struct ucred *cred,
1341    char *name, int namelen, struct cdev **dev)
1342{
1343	struct snddev_info *d;
1344
1345	if (*dev != NULL)
1346		return;
1347	if (strcmp(name, "mixer") == 0) {
1348		d = devclass_get_softc(pcm_devclass, snd_unit);
1349		if (PCM_REGISTERED(d) && d->mixer_dev != NULL) {
1350			*dev = d->mixer_dev;
1351			dev_ref(*dev);
1352		}
1353	}
1354}
1355
1356static void
1357mixer_sysinit(void *p)
1358{
1359	if (mixer_ehtag != NULL)
1360		return;
1361	mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000);
1362}
1363
1364static void
1365mixer_sysuninit(void *p)
1366{
1367	if (mixer_ehtag == NULL)
1368		return;
1369	EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag);
1370	mixer_ehtag = NULL;
1371}
1372
1373SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
1374SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
1375
1376/**
1377 * @brief Handler for SNDCTL_MIXERINFO
1378 *
1379 * This function searches for a mixer based on the numeric ID stored
1380 * in oss_miserinfo::dev.  If set to -1, then information about the
1381 * current mixer handling the request is provided.  Note, however, that
1382 * this ioctl may be made with any sound device (audio, mixer, midi).
1383 *
1384 * @note Caller must not hold any PCM device, channel, or mixer locks.
1385 *
1386 * See http://manuals.opensound.com/developer/SNDCTL_MIXERINFO.html for
1387 * more information.
1388 *
1389 * @param i_dev	character device on which the ioctl arrived
1390 * @param arg	user argument (oss_mixerinfo *)
1391 *
1392 * @retval EINVAL	oss_mixerinfo::dev specified a bad value
1393 * @retval 0		success
1394 */
1395int
1396mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
1397{
1398	struct snddev_info *d;
1399	struct snd_mixer *m;
1400	int nmix, i;
1401
1402	/*
1403	 * If probing the device handling the ioctl, make sure it's a mixer
1404	 * device.  (This ioctl is valid on audio, mixer, and midi devices.)
1405	 */
1406	if (mi->dev == -1 && i_dev->si_devsw != &mixer_cdevsw)
1407		return (EINVAL);
1408
1409	d = NULL;
1410	m = NULL;
1411	nmix = 0;
1412
1413	/*
1414	 * There's a 1:1 relationship between mixers and PCM devices, so
1415	 * begin by iterating over PCM devices and search for our mixer.
1416	 */
1417	for (i = 0; pcm_devclass != NULL &&
1418	    i < devclass_get_maxunit(pcm_devclass); i++) {
1419		d = devclass_get_softc(pcm_devclass, i);
1420		if (PCM_DETACHING(d) || !PCM_REGISTERED(d))
1421			continue;
1422
1423		/* XXX Need Giant magic entry */
1424
1425		/* See the note in function docblock. */
1426		PCM_UNLOCKASSERT(d);
1427		PCM_LOCK(d);
1428
1429		if (d->mixer_dev != NULL && d->mixer_dev->si_drv1 != NULL &&
1430		    ((mi->dev == -1 && d->mixer_dev == i_dev) ||
1431		    mi->dev == nmix)) {
1432			m = d->mixer_dev->si_drv1;
1433			mtx_lock(m->lock);
1434
1435			/*
1436			 * At this point, the following synchronization stuff
1437			 * has happened:
1438			 * - a specific PCM device is locked.
1439			 * - a specific mixer device has been locked, so be
1440			 *   sure to unlock when existing.
1441			 */
1442			bzero((void *)mi, sizeof(*mi));
1443			mi->dev = nmix;
1444			snprintf(mi->id, sizeof(mi->id), "mixer%d", i);
1445			strlcpy(mi->name, m->name, sizeof(mi->name));
1446			mi->modify_counter = m->modify_counter;
1447			mi->card_number = i;
1448			/*
1449			 * Currently, FreeBSD assumes 1:1 relationship between
1450			 * a pcm and mixer devices, so this is hardcoded to 0.
1451			 */
1452			mi->port_number = 0;
1453
1454			/**
1455			 * @todo Fill in @sa oss_mixerinfo::mixerhandle.
1456			 * @note From 4Front:  "mixerhandle is an arbitrary
1457			 *       string that identifies the mixer better than
1458			 *       the device number (mixerinfo.dev).  Device
1459			 *       numbers may change depending on the order the
1460			 *       drivers are loaded. However the handle should
1461			 *       remain the same provided that the sound card
1462			 *       is not moved to another PCI slot."
1463			 */
1464
1465			/**
1466			 * @note
1467			 * @sa oss_mixerinfo::magic is a reserved field.
1468			 *
1469			 * @par
1470			 * From 4Front:  "magic is usually 0. However some
1471			 * devices may have dedicated setup utilities and the
1472			 * magic field may contain an unique driver specific
1473			 * value (managed by [4Front])."
1474			 */
1475
1476			mi->enabled = device_is_attached(m->dev) ? 1 : 0;
1477			/**
1478			 * The only flag for @sa oss_mixerinfo::caps is
1479			 * currently MIXER_CAP_VIRTUAL, which I'm not sure we
1480			 * really worry about.
1481			 */
1482			/**
1483			 * Mixer extensions currently aren't supported, so
1484			 * leave @sa oss_mixerinfo::nrext blank for now.
1485			 */
1486			/**
1487			 * @todo Fill in @sa oss_mixerinfo::priority (requires
1488			 *       touching drivers?)
1489			 * @note The priority field is for mixer applets to
1490			 * determine which mixer should be the default, with 0
1491			 * being least preferred and 10 being most preferred.
1492			 * From 4Front:  "OSS drivers like ICH use higher
1493			 * values (10) because such chips are known to be used
1494			 * only on motherboards.  Drivers for high end pro
1495			 * devices use 0 because they will never be the
1496			 * default mixer. Other devices use values 1 to 9
1497			 * depending on the estimated probability of being the
1498			 * default device.
1499			 *
1500			 * XXX Described by Hannu@4Front, but not found in
1501			 *     soundcard.h.
1502			strlcpy(mi->devnode, devtoname(d->mixer_dev),
1503			sizeof(mi->devnode));
1504			mi->legacy_device = i;
1505			 */
1506			mtx_unlock(m->lock);
1507		} else
1508			++nmix;
1509
1510		PCM_UNLOCK(d);
1511
1512		if (m != NULL)
1513			return (0);
1514	}
1515
1516	return (EINVAL);
1517}
1518
1519/*
1520 * Allow the sound driver to use the mixer lock to protect its mixer
1521 * data:
1522 */
1523struct mtx *
1524mixer_get_lock(struct snd_mixer *m)
1525{
1526	if (m->lock == NULL) {
1527		return (&Giant);
1528	}
1529	return (m->lock);
1530}
1531
1532int
1533mix_get_locked(struct snd_mixer *m, u_int dev, int *pleft, int *pright)
1534{
1535	int level;
1536
1537	level = mixer_get(m, dev);
1538	if (level < 0) {
1539		*pright = *pleft = -1;
1540		return (-1);
1541	}
1542
1543	*pleft = level & 0xFF;
1544	*pright = (level >> 8) & 0xFF;
1545
1546	return (0);
1547}
1548
1549int
1550mix_set_locked(struct snd_mixer *m, u_int dev, int left, int right)
1551{
1552	int level;
1553
1554	level = (left & 0xFF) | ((right & 0xFF) << 8);
1555
1556	return (mixer_set(m, dev, level));
1557}
1558