1139749Simp/*-
2193640Sariff * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
3193640Sariff * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
4193640Sariff * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
550724Scg * All rights reserved.
650724Scg *
750724Scg * Redistribution and use in source and binary forms, with or without
850724Scg * modification, are permitted provided that the following conditions
950724Scg * are met:
1050724Scg * 1. Redistributions of source code must retain the above copyright
1150724Scg *    notice, this list of conditions and the following disclaimer.
1250724Scg * 2. Redistributions in binary form must reproduce the above copyright
1350724Scg *    notice, this list of conditions and the following disclaimer in the
1450724Scg *    documentation and/or other materials provided with the distribution.
1550724Scg *
1650724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1750724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1850724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1950724Scg * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2050724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2150724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2250724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2350724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2450724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2550724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2650724Scg * SUCH DAMAGE.
2750724Scg */
2850724Scg
29193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS
30193640Sariff#include "opt_snd.h"
31193640Sariff#endif
32193640Sariff
3353465Scg#include <dev/sound/pcm/sound.h>
3450724Scg
35193640Sariff#include "feeder_if.h"
3670134Scg#include "mixer_if.h"
3770134Scg
3882180ScgSND_DECLARE_FILE("$FreeBSD: stable/11/sys/dev/sound/pcm/mixer.c 359886 2020-04-13 16:31:46Z hselasky $");
3982180Scg
40227293Sedstatic MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
4170134Scg
42193640Sariffstatic int mixer_bypass = 1;
43267992ShselaskySYSCTL_INT(_hw_snd, OID_AUTO, vpc_mixer_bypass, CTLFLAG_RWTUN,
44193640Sariff    &mixer_bypass, 0,
45193640Sariff    "control channel pcm/rec volume, bypassing real mixer device");
46193640Sariff
4774763Scg#define MIXER_NAMELEN	16
4874763Scgstruct snd_mixer {
4974763Scg	KOBJ_FIELDS;
5074763Scg	void *devinfo;
51168264Sariff	int busy;
5274763Scg	int hwvol_muted;
5374763Scg	int hwvol_mixer;
5474763Scg	int hwvol_step;
55170815Sariff	int type;
56150825Snetchild	device_t dev;
5774763Scg	u_int32_t hwvol_mute_level;
5874763Scg	u_int32_t devs;
5974763Scg	u_int32_t recdevs;
6074763Scg	u_int32_t recsrc;
6174763Scg	u_int16_t level[32];
62162828Sariff	u_int8_t parent[32];
63162738Sariff	u_int32_t child[32];
64162828Sariff	u_int8_t realdev[32];
6574763Scg	char name[MIXER_NAMELEN];
66107285Scg	struct mtx *lock;
67162588Snetchild	oss_mixer_enuminfo enuminfo;
68162588Snetchild	/**
69162588Snetchild	 * Counter is incremented when applications change any of this
70162588Snetchild	 * mixer's controls.  A change in value indicates that persistent
71162588Snetchild	 * mixer applications should update their displays.
72162588Snetchild	 */
73162588Snetchild	int modify_counter;
7474763Scg};
7574763Scg
7650724Scgstatic u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
7750724Scg	[SOUND_MIXER_VOLUME]	= 75,
7850724Scg	[SOUND_MIXER_BASS]	= 50,
7950724Scg	[SOUND_MIXER_TREBLE]	= 50,
8062947Stanimura	[SOUND_MIXER_SYNTH]	= 75,
8150724Scg	[SOUND_MIXER_PCM]	= 75,
8250724Scg	[SOUND_MIXER_SPEAKER]	= 75,
8350724Scg	[SOUND_MIXER_LINE]	= 75,
84359883Shselasky	[SOUND_MIXER_MIC] 	= 25,
8550724Scg	[SOUND_MIXER_CD]	= 75,
86152422Sariff	[SOUND_MIXER_IGAIN]	= 0,
8750724Scg	[SOUND_MIXER_LINE1]	= 75,
8850724Scg	[SOUND_MIXER_VIDEO]	= 75,
89202166Smav	[SOUND_MIXER_RECLEV]	= 75,
9053203Scg	[SOUND_MIXER_OGAIN]	= 50,
9179044Scg	[SOUND_MIXER_MONITOR]	= 75,
9250724Scg};
9350724Scg
9470680Sjhbstatic char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
9570680Sjhb
9678362Scgstatic d_open_t mixer_open;
9778362Scgstatic d_close_t mixer_close;
98170815Sariffstatic d_ioctl_t mixer_ioctl;
9978362Scg
10078362Scgstatic struct cdevsw mixer_cdevsw = {
101126080Sphk	.d_version =	D_VERSION,
102111815Sphk	.d_open =	mixer_open,
103111815Sphk	.d_close =	mixer_close,
104111815Sphk	.d_ioctl =	mixer_ioctl,
105111815Sphk	.d_name =	"mixer",
10678362Scg};
10778362Scg
108162588Snetchild/**
109162588Snetchild * Keeps a count of mixer devices; used only by OSSv4 SNDCTL_SYSINFO ioctl.
110162588Snetchild */
111162588Snetchildint mixer_count = 0;
112162588Snetchild
113170161Sariffstatic eventhandler_tag mixer_ehtag = NULL;
11478362Scg
115130585Sphkstatic struct cdev *
11678362Scgmixer_get_devt(device_t dev)
11778362Scg{
118124617Sphk	struct snddev_info *snddev;
11978362Scg
120124617Sphk	snddev = device_get_softc(dev);
12178362Scg
122124617Sphk	return snddev->mixer_dev;
12378362Scg}
12478362Scg
12565390Speterstatic int
12670680Sjhbmixer_lookup(char *devname)
12770680Sjhb{
12870680Sjhb	int i;
12970680Sjhb
13070680Sjhb	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
13170680Sjhb		if (strncmp(devname, snd_mixernames[i],
13270680Sjhb		    strlen(snd_mixernames[i])) == 0)
13370680Sjhb			return i;
13470680Sjhb	return -1;
13570680Sjhb}
13670680Sjhb
137170815Sariff#define MIXER_SET_UNLOCK(x, y)		do {				\
138170815Sariff	if ((y) != 0)							\
139170815Sariff		snd_mtxunlock((x)->lock);				\
140193640Sariff} while (0)
141170815Sariff
142170815Sariff#define MIXER_SET_LOCK(x, y)		do {				\
143170815Sariff	if ((y) != 0)							\
144170815Sariff		snd_mtxlock((x)->lock);					\
145193640Sariff} while (0)
146170815Sariff
14770680Sjhbstatic int
148170815Sariffmixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d,
149193640Sariff    u_int left, u_int right)
15065390Speter{
151170161Sariff	struct pcm_channel *c;
152170815Sariff	int dropmtx, acquiremtx;
153162738Sariff
154358878Shselasky	if (PCM_DETACHING(d) || !PCM_REGISTERED(d))
155170815Sariff		return (EINVAL);
156170161Sariff
157170815Sariff	if (mtx_owned(m->lock))
158170815Sariff		dropmtx = 1;
159170815Sariff	else
160170815Sariff		dropmtx = 0;
161170815Sariff
162170815Sariff	if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0)
163170815Sariff		acquiremtx = 0;
164170815Sariff	else
165170815Sariff		acquiremtx = 1;
166170815Sariff
167170815Sariff	/*
168170815Sariff	 * Be careful here. If we're coming from cdev ioctl, it is OK to
169170815Sariff	 * not doing locking AT ALL (except on individual channel) since
170170815Sariff	 * we've been heavily guarded by pcm cv, or if we're still
171170815Sariff	 * under Giant influence. Since we also have mix_* calls, we cannot
172170815Sariff	 * assume such protection and just do the lock as usuall.
173170815Sariff	 */
174170815Sariff	MIXER_SET_UNLOCK(m, dropmtx);
175170815Sariff	MIXER_SET_LOCK(d, acquiremtx);
176170815Sariff
177193640Sariff	CHN_FOREACH(c, d, channels.pcm.busy) {
178193640Sariff		CHN_LOCK(c);
179193640Sariff		if (c->direction == PCMDIR_PLAY &&
180193640Sariff		    (c->feederflags & (1 << FEEDER_VOLUME)))
181193640Sariff			chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right,
182193640Sariff			    (left + right) >> 1);
183193640Sariff		CHN_UNLOCK(c);
184162738Sariff	}
185170161Sariff
186170815Sariff	MIXER_SET_UNLOCK(d, acquiremtx);
187170815Sariff	MIXER_SET_LOCK(m, dropmtx);
188170161Sariff
189170815Sariff	return (0);
190162738Sariff}
191162738Sariff
192162738Sariffstatic int
193193640Sariffmixer_set_eq(struct snd_mixer *m, struct snddev_info *d,
194193640Sariff    u_int dev, u_int level)
195162738Sariff{
196193640Sariff	struct pcm_channel *c;
197193640Sariff	struct pcm_feeder *f;
198193640Sariff	int tone, dropmtx, acquiremtx;
199193640Sariff
200193640Sariff	if (dev == SOUND_MIXER_TREBLE)
201193640Sariff		tone = FEEDEQ_TREBLE;
202193640Sariff	else if (dev == SOUND_MIXER_BASS)
203193640Sariff		tone = FEEDEQ_BASS;
204193640Sariff	else
205193640Sariff		return (EINVAL);
206193640Sariff
207358878Shselasky	if (PCM_DETACHING(d) || !PCM_REGISTERED(d))
208193640Sariff		return (EINVAL);
209193640Sariff
210193640Sariff	if (mtx_owned(m->lock))
211193640Sariff		dropmtx = 1;
212193640Sariff	else
213193640Sariff		dropmtx = 0;
214193640Sariff
215193640Sariff	if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0)
216193640Sariff		acquiremtx = 0;
217193640Sariff	else
218193640Sariff		acquiremtx = 1;
219193640Sariff
220193640Sariff	/*
221193640Sariff	 * Be careful here. If we're coming from cdev ioctl, it is OK to
222193640Sariff	 * not doing locking AT ALL (except on individual channel) since
223193640Sariff	 * we've been heavily guarded by pcm cv, or if we're still
224193640Sariff	 * under Giant influence. Since we also have mix_* calls, we cannot
225193640Sariff	 * assume such protection and just do the lock as usuall.
226193640Sariff	 */
227193640Sariff	MIXER_SET_UNLOCK(m, dropmtx);
228193640Sariff	MIXER_SET_LOCK(d, acquiremtx);
229193640Sariff
230193640Sariff	CHN_FOREACH(c, d, channels.pcm.busy) {
231193640Sariff		CHN_LOCK(c);
232193640Sariff		f = chn_findfeeder(c, FEEDER_EQ);
233193640Sariff		if (f != NULL)
234193640Sariff			(void)FEEDER_SET(f, tone, level);
235193640Sariff		CHN_UNLOCK(c);
236193640Sariff	}
237193640Sariff
238193640Sariff	MIXER_SET_UNLOCK(d, acquiremtx);
239193640Sariff	MIXER_SET_LOCK(m, dropmtx);
240193640Sariff
241193640Sariff	return (0);
242193640Sariff}
243193640Sariff
244193640Sariffstatic int
245193640Sariffmixer_set(struct snd_mixer *m, u_int dev, u_int lev)
246193640Sariff{
247150825Snetchild	struct snddev_info *d;
248193640Sariff	u_int l, r, tl, tr;
249162738Sariff	u_int32_t parent = SOUND_MIXER_NONE, child = 0;
250162738Sariff	u_int32_t realdev;
251170815Sariff	int i, dropmtx;
25270134Scg
253162738Sariff	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES ||
254162738Sariff	    (0 == (m->devs & (1 << dev))))
25570134Scg		return -1;
25670134Scg
25770134Scg	l = min((lev & 0x00ff), 100);
25870134Scg	r = min(((lev & 0xff00) >> 8), 100);
259162738Sariff	realdev = m->realdev[dev];
26070134Scg
261162738Sariff	d = device_get_softc(m->dev);
262162738Sariff	if (d == NULL)
263162738Sariff		return -1;
26470134Scg
265170815Sariff	/* It is safe to drop this mutex due to Giant. */
266170815Sariff	if (!(d->flags & SD_F_MPSAFE) && mtx_owned(m->lock) != 0)
267170815Sariff		dropmtx = 1;
268170815Sariff	else
269170815Sariff		dropmtx = 0;
270170815Sariff
271170815Sariff	MIXER_SET_UNLOCK(m, dropmtx);
272170815Sariff
273162738Sariff	/* TODO: recursive handling */
274162738Sariff	parent = m->parent[dev];
275162738Sariff	if (parent >= SOUND_MIXER_NRDEVICES)
276162738Sariff		parent = SOUND_MIXER_NONE;
277162738Sariff	if (parent == SOUND_MIXER_NONE)
278162738Sariff		child = m->child[dev];
279162738Sariff
280162738Sariff	if (parent != SOUND_MIXER_NONE) {
281162738Sariff		tl = (l * (m->level[parent] & 0x00ff)) / 100;
282162738Sariff		tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100;
283162738Sariff		if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
284170815Sariff			(void)mixer_set_softpcmvol(m, d, tl, tr);
285162738Sariff		else if (realdev != SOUND_MIXER_NONE &&
286170815Sariff		    MIXER_SET(m, realdev, tl, tr) < 0) {
287170815Sariff			MIXER_SET_LOCK(m, dropmtx);
288162738Sariff			return -1;
289170815Sariff		}
290162738Sariff	} else if (child != 0) {
291162738Sariff		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
292162738Sariff			if (!(child & (1 << i)) || m->parent[i] != dev)
293162738Sariff				continue;
294162738Sariff			realdev = m->realdev[i];
295162738Sariff			tl = (l * (m->level[i] & 0x00ff)) / 100;
296162738Sariff			tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100;
297193640Sariff			if (i == SOUND_MIXER_PCM &&
298193640Sariff			    (d->flags & SD_F_SOFTPCMVOL))
299170815Sariff				(void)mixer_set_softpcmvol(m, d, tl, tr);
300162738Sariff			else if (realdev != SOUND_MIXER_NONE)
301162738Sariff				MIXER_SET(m, realdev, tl, tr);
302150825Snetchild		}
303162738Sariff		realdev = m->realdev[dev];
304162738Sariff		if (realdev != SOUND_MIXER_NONE &&
305170815Sariff		    MIXER_SET(m, realdev, l, r) < 0) {
306170815Sariff				MIXER_SET_LOCK(m, dropmtx);
307162738Sariff				return -1;
308170815Sariff		}
309150825Snetchild	} else {
310162738Sariff		if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
311170815Sariff			(void)mixer_set_softpcmvol(m, d, l, r);
312193640Sariff		else if ((dev == SOUND_MIXER_TREBLE ||
313193640Sariff		    dev == SOUND_MIXER_BASS) && (d->flags & SD_F_EQ))
314193640Sariff			(void)mixer_set_eq(m, d, dev, (l + r) >> 1);
315162738Sariff		else if (realdev != SOUND_MIXER_NONE &&
316170815Sariff		    MIXER_SET(m, realdev, l, r) < 0) {
317170815Sariff			MIXER_SET_LOCK(m, dropmtx);
318150825Snetchild			return -1;
319170815Sariff		}
320150825Snetchild	}
321150825Snetchild
322193640Sariff	MIXER_SET_LOCK(m, dropmtx);
323193640Sariff
324162738Sariff	m->level[dev] = l | (r << 8);
325336889Shselasky	m->modify_counter++;
326162738Sariff
32770134Scg	return 0;
32865390Speter}
32965390Speter
33065390Speterstatic int
33174763Scgmixer_get(struct snd_mixer *mixer, int dev)
33265390Speter{
33370134Scg	if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
33470134Scg		return mixer->level[dev];
335170815Sariff	else
336170815Sariff		return -1;
33765390Speter}
33865390Speter
33965390Speterstatic int
34074763Scgmixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
34165390Speter{
342170815Sariff	struct snddev_info *d;
343193640Sariff	u_int32_t recsrc;
344170815Sariff	int dropmtx;
345170815Sariff
346170815Sariff	d = device_get_softc(mixer->dev);
347170815Sariff	if (d == NULL)
348170815Sariff		return -1;
349170815Sariff	if (!(d->flags & SD_F_MPSAFE) && mtx_owned(mixer->lock) != 0)
350170815Sariff		dropmtx = 1;
351170815Sariff	else
352170815Sariff		dropmtx = 0;
35370134Scg	src &= mixer->recdevs;
35470134Scg	if (src == 0)
355202166Smav		src = mixer->recdevs & SOUND_MASK_MIC;
356202166Smav	if (src == 0)
357202166Smav		src = mixer->recdevs & SOUND_MASK_MONITOR;
358202166Smav	if (src == 0)
359202166Smav		src = mixer->recdevs & SOUND_MASK_LINE;
360202166Smav	if (src == 0 && mixer->recdevs != 0)
361202166Smav		src = (1 << (ffs(mixer->recdevs) - 1));
362170815Sariff	/* It is safe to drop this mutex due to Giant. */
363170815Sariff	MIXER_SET_UNLOCK(mixer, dropmtx);
364193640Sariff	recsrc = MIXER_SETRECSRC(mixer, src);
365170815Sariff	MIXER_SET_LOCK(mixer, dropmtx);
366193640Sariff
367193640Sariff	mixer->recsrc = recsrc;
368193640Sariff
36965390Speter	return 0;
37065390Speter}
37165390Speter
37265390Speterstatic int
37374763Scgmixer_getrecsrc(struct snd_mixer *mixer)
37465390Speter{
37570134Scg	return mixer->recsrc;
37665390Speter}
37765390Speter
378162588Snetchild/**
379162588Snetchild * @brief Retrieve the route number of the current recording device
380162588Snetchild *
381162588Snetchild * OSSv4 assigns routing numbers to recording devices, unlike the previous
382162588Snetchild * API which relied on a fixed table of device numbers and names.  This
383162588Snetchild * function returns the routing number of the device currently selected
384162588Snetchild * for recording.
385162588Snetchild *
386162588Snetchild * For now, this function is kind of a goofy compatibility stub atop the
387162588Snetchild * existing sound system.  (For example, in theory, the old sound system
388162588Snetchild * allows multiple recording devices to be specified via a bitmask.)
389162588Snetchild *
390162588Snetchild * @param m	mixer context container thing
391162588Snetchild *
392162588Snetchild * @retval 0		success
393162588Snetchild * @retval EIDRM	no recording device found (generally not possible)
394162588Snetchild * @todo Ask about error code
395162588Snetchild */
396162588Snetchildstatic int
397162588Snetchildmixer_get_recroute(struct snd_mixer *m, int *route)
398162588Snetchild{
399162588Snetchild	int i, cnt;
400162588Snetchild
401162588Snetchild	cnt = 0;
402162588Snetchild
403162588Snetchild	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
404162588Snetchild		/** @todo can user set a multi-device mask? (== or &?) */
405162588Snetchild		if ((1 << i) == m->recsrc)
406162588Snetchild			break;
407162588Snetchild		if ((1 << i) & m->recdevs)
408162588Snetchild			++cnt;
409162588Snetchild	}
410162588Snetchild
411162588Snetchild	if (i == SOUND_MIXER_NRDEVICES)
412162588Snetchild		return EIDRM;
413162588Snetchild
414162588Snetchild	*route = cnt;
415162588Snetchild	return 0;
416162588Snetchild}
417162588Snetchild
418162588Snetchild/**
419162588Snetchild * @brief Select a device for recording
420162588Snetchild *
421162588Snetchild * This function sets a recording source based on a recording device's
422162588Snetchild * routing number.  Said number is translated to an old school recdev
423162588Snetchild * mask and passed over mixer_setrecsrc.
424162588Snetchild *
425162588Snetchild * @param m	mixer context container thing
426162588Snetchild *
427162588Snetchild * @retval 0		success(?)
428162588Snetchild * @retval EINVAL	User specified an invalid device number
429162588Snetchild * @retval otherwise	error from mixer_setrecsrc
430162588Snetchild */
431162588Snetchildstatic int
432162588Snetchildmixer_set_recroute(struct snd_mixer *m, int route)
433162588Snetchild{
434162588Snetchild	int i, cnt, ret;
435162588Snetchild
436162588Snetchild	ret = 0;
437162588Snetchild	cnt = 0;
438162588Snetchild
439162588Snetchild	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
440162588Snetchild		if ((1 << i) & m->recdevs) {
441162588Snetchild			if (route == cnt)
442162588Snetchild				break;
443162588Snetchild			++cnt;
444162588Snetchild		}
445162588Snetchild	}
446162588Snetchild
447162588Snetchild	if (i == SOUND_MIXER_NRDEVICES)
448162588Snetchild		ret = EINVAL;
449162588Snetchild	else
450162588Snetchild		ret = mixer_setrecsrc(m, (1 << i));
451162588Snetchild
452162588Snetchild	return ret;
453162588Snetchild}
454162588Snetchild
45570134Scgvoid
45674763Scgmix_setdevs(struct snd_mixer *m, u_int32_t v)
45770134Scg{
458162791Sariff	struct snddev_info *d;
459162738Sariff	int i;
460162738Sariff
461162738Sariff	if (m == NULL)
462162738Sariff		return;
463162791Sariff
464162791Sariff	d = device_get_softc(m->dev);
465162738Sariff	if (d != NULL && (d->flags & SD_F_SOFTPCMVOL))
466150825Snetchild		v |= SOUND_MASK_PCM;
467193640Sariff	if (d != NULL && (d->flags & SD_F_EQ))
468193640Sariff		v |= SOUND_MASK_TREBLE | SOUND_MASK_BASS;
469162738Sariff	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
470162738Sariff		if (m->parent[i] < SOUND_MIXER_NRDEVICES)
471162738Sariff			v |= 1 << m->parent[i];
472162738Sariff		v |= m->child[i];
473162738Sariff	}
47470134Scg	m->devs = v;
47570134Scg}
47670134Scg
477162588Snetchild/**
478162588Snetchild * @brief Record mask of available recording devices
479162588Snetchild *
480162588Snetchild * Calling functions are responsible for defining the mask of available
481162588Snetchild * recording devices.  This function records that value in a structure
482162588Snetchild * used by the rest of the mixer code.
483162588Snetchild *
484162588Snetchild * This function also populates a structure used by the SNDCTL_DSP_*RECSRC*
485162588Snetchild * family of ioctls that are part of OSSV4.  All recording device labels
486162588Snetchild * are concatenated in ascending order corresponding to their routing
487162588Snetchild * numbers.  (Ex:  a system might have 0 => 'vol', 1 => 'cd', 2 => 'line',
488162588Snetchild * etc.)  For now, these labels are just the standard recording device
489162588Snetchild * names (cd, line1, etc.), but will eventually be fully dynamic and user
490162588Snetchild * controlled.
491162588Snetchild *
492162588Snetchild * @param m	mixer device context container thing
493162588Snetchild * @param v	mask of recording devices
494162588Snetchild */
49570134Scgvoid
49674763Scgmix_setrecdevs(struct snd_mixer *m, u_int32_t v)
49770134Scg{
498162588Snetchild	oss_mixer_enuminfo *ei;
499162588Snetchild	char *loc;
500162588Snetchild	int i, nvalues, nwrote, nleft, ncopied;
501162588Snetchild
502162588Snetchild	ei = &m->enuminfo;
503162588Snetchild
504162588Snetchild	nvalues = 0;
505162588Snetchild	nwrote = 0;
506162588Snetchild	nleft = sizeof(ei->strings);
507162588Snetchild	loc = ei->strings;
508162588Snetchild
509162588Snetchild	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
510162588Snetchild		if ((1 << i) & v) {
511162588Snetchild			ei->strindex[nvalues] = nwrote;
512162588Snetchild			ncopied = strlcpy(loc, snd_mixernames[i], nleft) + 1;
513162588Snetchild			    /* strlcpy retval doesn't include terminator */
514162588Snetchild
515162588Snetchild			nwrote += ncopied;
516162588Snetchild			nleft -= ncopied;
517162588Snetchild			nvalues++;
518162588Snetchild
519162588Snetchild			/*
520162588Snetchild			 * XXX I don't think this should ever be possible.
521162588Snetchild			 * Even with a move to dynamic device/channel names,
522162588Snetchild			 * each label is limited to ~16 characters, so that'd
523162588Snetchild			 * take a LOT to fill this buffer.
524162588Snetchild			 */
525162588Snetchild			if ((nleft <= 0) || (nvalues >= OSS_ENUM_MAXVALUE)) {
526162588Snetchild				device_printf(m->dev,
527162588Snetchild				    "mix_setrecdevs:  Not enough room to store device names--please file a bug report.\n");
528162588Snetchild				device_printf(m->dev,
529162588Snetchild				    "mix_setrecdevs:  Please include details about your sound hardware, OS version, etc.\n");
530162588Snetchild				break;
531162588Snetchild			}
532162588Snetchild
533162588Snetchild			loc = &ei->strings[nwrote];
534162588Snetchild		}
535162588Snetchild	}
536162588Snetchild
537162588Snetchild	/*
538162588Snetchild	 * NB:	The SNDCTL_DSP_GET_RECSRC_NAMES ioctl ignores the dev
539162588Snetchild	 * 	and ctrl fields.
540162588Snetchild	 */
541162588Snetchild	ei->nvalues = nvalues;
54270134Scg	m->recdevs = v;
54370134Scg}
54470134Scg
545162738Sariffvoid
546162738Sariffmix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs)
547162738Sariff{
548162738Sariff	u_int32_t mask = 0;
549162738Sariff	int i;
550162738Sariff
551162738Sariff	if (m == NULL || parent >= SOUND_MIXER_NRDEVICES)
552162738Sariff		return;
553162738Sariff	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
554162738Sariff		if (i == parent)
555162738Sariff			continue;
556162738Sariff		if (childs & (1 << i)) {
557162738Sariff			mask |= 1 << i;
558162738Sariff			if (m->parent[i] < SOUND_MIXER_NRDEVICES)
559162738Sariff				m->child[m->parent[i]] &= ~(1 << i);
560162738Sariff			m->parent[i] = parent;
561162738Sariff			m->child[i] = 0;
562162738Sariff		}
563162738Sariff	}
564162738Sariff	mask &= ~(1 << parent);
565162738Sariff	m->child[parent] = mask;
566162738Sariff}
567162738Sariff
568162738Sariffvoid
569162738Sariffmix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev)
570162738Sariff{
571162738Sariff	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES ||
572162738Sariff	    !(realdev == SOUND_MIXER_NONE || realdev < SOUND_MIXER_NRDEVICES))
573162738Sariff		return;
574162738Sariff	m->realdev[dev] = realdev;
575162738Sariff}
576162738Sariff
57770134Scgu_int32_t
578162738Sariffmix_getparent(struct snd_mixer *m, u_int32_t dev)
579162738Sariff{
580162738Sariff	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES)
581162738Sariff		return SOUND_MIXER_NONE;
582162738Sariff	return m->parent[dev];
583162738Sariff}
584162738Sariff
585162738Sariffu_int32_t
586162738Sariffmix_getchild(struct snd_mixer *m, u_int32_t dev)
587162738Sariff{
588162738Sariff	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES)
589162738Sariff		return 0;
590162738Sariff	return m->child[dev];
591162738Sariff}
592162738Sariff
593162738Sariffu_int32_t
59474763Scgmix_getdevs(struct snd_mixer *m)
59570134Scg{
59670134Scg	return m->devs;
59770134Scg}
59870134Scg
59970134Scgu_int32_t
60074763Scgmix_getrecdevs(struct snd_mixer *m)
60170134Scg{
60270134Scg	return m->recdevs;
60370134Scg}
60470134Scg
60570134Scgvoid *
60674763Scgmix_getdevinfo(struct snd_mixer *m)
60770134Scg{
60870134Scg	return m->devinfo;
60970134Scg}
61070134Scg
611170815Sariffstatic struct snd_mixer *
612170815Sariffmixer_obj_create(device_t dev, kobj_class_t cls, void *devinfo,
613170815Sariff    int type, const char *desc)
61470134Scg{
61574763Scg	struct snd_mixer *m;
616170815Sariff	int i;
61770134Scg
618170815Sariff	KASSERT(dev != NULL && cls != NULL && devinfo != NULL,
619170815Sariff	    ("%s(): NULL data dev=%p cls=%p devinfo=%p",
620170815Sariff	    __func__, dev, cls, devinfo));
621170815Sariff	KASSERT(type == MIXER_TYPE_PRIMARY || type == MIXER_TYPE_SECONDARY,
622170815Sariff	    ("invalid mixer type=%d", type));
623170815Sariff
624111119Simp	m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
625170815Sariff	snprintf(m->name, sizeof(m->name), "%s:mixer",
626170815Sariff	    device_get_nameunit(dev));
627170815Sariff	if (desc != NULL) {
628170815Sariff		strlcat(m->name, ":", sizeof(m->name));
629170815Sariff		strlcat(m->name, desc, sizeof(m->name));
630170815Sariff	}
631170815Sariff	m->lock = snd_mtxcreate(m->name, (type == MIXER_TYPE_PRIMARY) ?
632170815Sariff	    "primary pcm mixer" : "secondary pcm mixer");
633170815Sariff	m->type = type;
63470134Scg	m->devinfo = devinfo;
63578362Scg	m->busy = 0;
636150825Snetchild	m->dev = dev;
637170815Sariff	for (i = 0; i < (sizeof(m->parent) / sizeof(m->parent[0])); i++) {
638162738Sariff		m->parent[i] = SOUND_MIXER_NONE;
639162738Sariff		m->child[i] = 0;
640162738Sariff		m->realdev[i] = i;
641162738Sariff	}
64270134Scg
643170815Sariff	if (MIXER_INIT(m)) {
644170815Sariff		snd_mtxlock(m->lock);
645170815Sariff		snd_mtxfree(m->lock);
646170815Sariff		kobj_delete((kobj_t)m, M_MIXER);
647170815Sariff		return (NULL);
648170815Sariff	}
64970134Scg
650170815Sariff	return (m);
651170815Sariff}
652170815Sariff
653170815Sariffint
654170815Sariffmixer_delete(struct snd_mixer *m)
655170815Sariff{
656170815Sariff	KASSERT(m != NULL, ("NULL snd_mixer"));
657170815Sariff	KASSERT(m->type == MIXER_TYPE_SECONDARY,
658170815Sariff	    ("%s(): illegal mixer type=%d", __func__, m->type));
659170815Sariff
660184610Salfred	/* mixer uninit can sleep --hps */
661170815Sariff
662170815Sariff	MIXER_UNINIT(m);
663170815Sariff
664170815Sariff	snd_mtxfree(m->lock);
665170815Sariff	kobj_delete((kobj_t)m, M_MIXER);
666170815Sariff
667170815Sariff	--mixer_count;
668170815Sariff
669170815Sariff	return (0);
670170815Sariff}
671170815Sariff
672170815Sariffstruct snd_mixer *
673170815Sariffmixer_create(device_t dev, kobj_class_t cls, void *devinfo, const char *desc)
674170815Sariff{
675170815Sariff	struct snd_mixer *m;
676170815Sariff
677170815Sariff	m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_SECONDARY, desc);
678170815Sariff
679170815Sariff	if (m != NULL)
680170815Sariff		++mixer_count;
681170815Sariff
682170815Sariff	return (m);
683170815Sariff}
684170815Sariff
685170815Sariffint
686170815Sariffmixer_init(device_t dev, kobj_class_t cls, void *devinfo)
687170815Sariff{
688170815Sariff	struct snddev_info *snddev;
689170815Sariff	struct snd_mixer *m;
690170815Sariff	u_int16_t v;
691170815Sariff	struct cdev *pdev;
692170815Sariff	int i, unit, devunit, val;
693170815Sariff
694193640Sariff	snddev = device_get_softc(dev);
695193640Sariff	if (snddev == NULL)
696193640Sariff		return (-1);
697193640Sariff
698193640Sariff	if (resource_int_value(device_get_name(dev),
699193640Sariff	    device_get_unit(dev), "eq", &val) == 0 && val != 0) {
700193640Sariff		snddev->flags |= SD_F_EQ;
701193640Sariff		if ((val & SD_F_EQ_MASK) == val)
702193640Sariff			snddev->flags |= val;
703193640Sariff		else
704193640Sariff			snddev->flags |= SD_F_EQ_DEFAULT;
705193640Sariff		snddev->eqpreamp = 0;
706193640Sariff	}
707193640Sariff
708170815Sariff	m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_PRIMARY, NULL);
709170815Sariff	if (m == NULL)
710170815Sariff		return (-1);
711170815Sariff
71270134Scg	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
713131105Sjosef		v = snd_mixerdefaults[i];
714131105Sjosef
715131064Sjosef		if (resource_int_value(device_get_name(dev),
716131064Sjosef		    device_get_unit(dev), snd_mixernames[i], &val) == 0) {
717131064Sjosef			if (val >= 0 && val <= 100) {
718131064Sjosef				v = (u_int16_t) val;
719131064Sjosef			}
720131064Sjosef		}
721130792Sjosef
72270134Scg		mixer_set(m, i, v | (v << 8));
72370134Scg	}
72470134Scg
725202166Smav	mixer_setrecsrc(m, 0); /* Set default input. */
72670134Scg
72778362Scg	unit = device_get_unit(dev);
728170161Sariff	devunit = snd_mkunit(unit, SND_DEV_CTL, 0);
729193640Sariff	pdev = make_dev(&mixer_cdevsw, PCMMINOR(devunit),
73078362Scg		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
73178362Scg	pdev->si_drv1 = m;
732124617Sphk	snddev->mixer_dev = pdev;
73370134Scg
734162588Snetchild	++mixer_count;
735162588Snetchild
736162738Sariff	if (bootverbose) {
737162738Sariff		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
738162738Sariff			if (!(m->devs & (1 << i)))
739162738Sariff				continue;
740162738Sariff			if (m->realdev[i] != i) {
741162738Sariff				device_printf(dev, "Mixer \"%s\" -> \"%s\":",
742162738Sariff				    snd_mixernames[i],
743162738Sariff				    (m->realdev[i] < SOUND_MIXER_NRDEVICES) ?
744162738Sariff				    snd_mixernames[m->realdev[i]] : "none");
745162738Sariff			} else {
746162738Sariff				device_printf(dev, "Mixer \"%s\":",
747162738Sariff				    snd_mixernames[i]);
748162738Sariff			}
749162738Sariff			if (m->parent[i] < SOUND_MIXER_NRDEVICES)
750162738Sariff				printf(" parent=\"%s\"",
751162738Sariff				    snd_mixernames[m->parent[i]]);
752162738Sariff			if (m->child[i] != 0)
753162738Sariff				printf(" child=0x%08x", m->child[i]);
754162738Sariff			printf("\n");
755162738Sariff		}
756162738Sariff		if (snddev->flags & SD_F_SOFTPCMVOL)
757162738Sariff			device_printf(dev, "Soft PCM mixer ENABLED\n");
758193640Sariff		if (snddev->flags & SD_F_EQ)
759193640Sariff			device_printf(dev, "EQ Treble/Bass ENABLED\n");
760162738Sariff	}
761162738Sariff
762170815Sariff	return (0);
76370134Scg}
76470134Scg
76570134Scgint
76665340Scgmixer_uninit(device_t dev)
76758383Scg{
76858383Scg	int i;
769156929Sariff	struct snddev_info *d;
77074763Scg	struct snd_mixer *m;
771130585Sphk	struct cdev *pdev;
77270134Scg
773156929Sariff	d = device_get_softc(dev);
77478362Scg	pdev = mixer_get_devt(dev);
775156929Sariff	if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL)
776156929Sariff		return EBADF;
777170815Sariff
77878362Scg	m = pdev->si_drv1;
779170815Sariff	KASSERT(m != NULL, ("NULL snd_mixer"));
780170815Sariff	KASSERT(m->type == MIXER_TYPE_PRIMARY,
781170815Sariff	    ("%s(): illegal mixer type=%d", __func__, m->type));
782170815Sariff
78374763Scg	snd_mtxlock(m->lock);
78470134Scg
78578362Scg	if (m->busy) {
78678362Scg		snd_mtxunlock(m->lock);
78778362Scg		return EBUSY;
78878362Scg	}
78978362Scg
790184610Salfred	/* destroy dev can sleep --hps */
791184610Salfred
792184610Salfred	snd_mtxunlock(m->lock);
793184610Salfred
79478362Scg	pdev->si_drv1 = NULL;
79578362Scg	destroy_dev(pdev);
79678362Scg
797184610Salfred	snd_mtxlock(m->lock);
798184610Salfred
79965340Scg	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
80070134Scg		mixer_set(m, i, 0);
80170134Scg
80270134Scg	mixer_setrecsrc(m, SOUND_MASK_MIC);
80370134Scg
804184610Salfred	snd_mtxunlock(m->lock);
805184610Salfred
806184610Salfred	/* mixer uninit can sleep --hps */
807184610Salfred
80870134Scg	MIXER_UNINIT(m);
80970134Scg
81074763Scg	snd_mtxfree(m->lock);
81170134Scg	kobj_delete((kobj_t)m, M_MIXER);
81270134Scg
813156929Sariff	d->mixer_dev = NULL;
814156929Sariff
815162588Snetchild	--mixer_count;
816162588Snetchild
81765340Scg	return 0;
81865340Scg}
81965340Scg
82065340Scgint
82165340Scgmixer_reinit(device_t dev)
82265340Scg{
82378362Scg	struct snd_mixer *m;
824130585Sphk	struct cdev *pdev;
82565340Scg	int i;
82667652Scg
82778362Scg	pdev = mixer_get_devt(dev);
82878362Scg	m = pdev->si_drv1;
82974763Scg	snd_mtxlock(m->lock);
83070134Scg
83170134Scg	i = MIXER_REINIT(m);
83274763Scg	if (i) {
83374763Scg		snd_mtxunlock(m->lock);
83467652Scg		return i;
83574763Scg	}
83670134Scg
83767652Scg	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
83870134Scg		mixer_set(m, i, m->level[i]);
83970134Scg
84070134Scg	mixer_setrecsrc(m, m->recsrc);
84174763Scg	snd_mtxunlock(m->lock);
84270134Scg
84367652Scg	return 0;
84458383Scg}
84558383Scg
84670680Sjhbstatic int
84770680Sjhbsysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
84870680Sjhb{
84970680Sjhb	char devname[32];
85070680Sjhb	int error, dev;
85174763Scg	struct snd_mixer *m;
85270618Sjhb
85370680Sjhb	m = oidp->oid_arg1;
85474763Scg	snd_mtxlock(m->lock);
855164614Sariff	strlcpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
856100654Sgreen	snd_mtxunlock(m->lock);
85770680Sjhb	error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
858100654Sgreen	snd_mtxlock(m->lock);
85970680Sjhb	if (error == 0 && req->newptr != NULL) {
86070680Sjhb		dev = mixer_lookup(devname);
86174763Scg		if (dev == -1) {
86274763Scg			snd_mtxunlock(m->lock);
86370680Sjhb			return EINVAL;
86474763Scg		}
86570944Sjhb		else if (dev != m->hwvol_mixer) {
86670680Sjhb			m->hwvol_mixer = dev;
86770944Sjhb			m->hwvol_muted = 0;
86870944Sjhb		}
86970680Sjhb	}
87074763Scg	snd_mtxunlock(m->lock);
87170680Sjhb	return error;
87270680Sjhb}
87370618Sjhb
87470680Sjhbint
87570944Sjhbmixer_hwvol_init(device_t dev)
87670680Sjhb{
87774763Scg	struct snd_mixer *m;
878130585Sphk	struct cdev *pdev;
87970680Sjhb
88078362Scg	pdev = mixer_get_devt(dev);
88178362Scg	m = pdev->si_drv1;
88278362Scg
88370680Sjhb	m->hwvol_mixer = SOUND_MIXER_VOLUME;
88470680Sjhb	m->hwvol_step = 5;
885164614Sariff	SYSCTL_ADD_INT(device_get_sysctl_ctx(dev),
886164614Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
887280442Shselasky            OID_AUTO, "hwvol_step", CTLFLAG_RWTUN, &m->hwvol_step, 0, "");
888164614Sariff	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
889164614Sariff	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
890280442Shselasky            OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RWTUN, m, 0,
891100071Smarkm	    sysctl_hw_snd_hwvol_mixer, "A", "");
89270680Sjhb	return 0;
89370680Sjhb}
89470680Sjhb
89570618Sjhbvoid
896246454Shselaskymixer_hwvol_mute_locked(struct snd_mixer *m)
89770618Sjhb{
89870944Sjhb	if (m->hwvol_muted) {
89970944Sjhb		m->hwvol_muted = 0;
90070944Sjhb		mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
90170680Sjhb	} else {
90270944Sjhb		m->hwvol_muted++;
90370944Sjhb		m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
90470680Sjhb		mixer_set(m, m->hwvol_mixer, 0);
90570680Sjhb	}
90670618Sjhb}
90770618Sjhb
90870618Sjhbvoid
909246454Shselaskymixer_hwvol_mute(device_t dev)
91070618Sjhb{
91174763Scg	struct snd_mixer *m;
912130585Sphk	struct cdev *pdev;
91370618Sjhb
91478362Scg	pdev = mixer_get_devt(dev);
91578362Scg	m = pdev->si_drv1;
91674763Scg	snd_mtxlock(m->lock);
917246454Shselasky	mixer_hwvol_mute_locked(m);
918246454Shselasky	snd_mtxunlock(m->lock);
919246454Shselasky}
920246454Shselasky
921246454Shselaskyvoid
922246454Shselaskymixer_hwvol_step_locked(struct snd_mixer *m, int left_step, int right_step)
923246454Shselasky{
924246454Shselasky	int level, left, right;
925246454Shselasky
92670944Sjhb	if (m->hwvol_muted) {
92770944Sjhb		m->hwvol_muted = 0;
92870944Sjhb		level = m->hwvol_mute_level;
92970944Sjhb	} else
93070944Sjhb		level = mixer_get(m, m->hwvol_mixer);
93170618Sjhb	if (level != -1) {
93270618Sjhb		left = level & 0xff;
933246454Shselasky		right = (level >> 8) & 0xff;
93470680Sjhb		left += left_step * m->hwvol_step;
93570618Sjhb		if (left < 0)
93670618Sjhb			left = 0;
937246454Shselasky		else if (left > 100)
938246454Shselasky			left = 100;
93970680Sjhb		right += right_step * m->hwvol_step;
94070618Sjhb		if (right < 0)
94170618Sjhb			right = 0;
942246454Shselasky		else if (right > 100)
943246454Shselasky			right = 100;
94470680Sjhb		mixer_set(m, m->hwvol_mixer, left | right << 8);
94570618Sjhb	}
946246454Shselasky}
947246454Shselasky
948246454Shselaskyvoid
949246454Shselaskymixer_hwvol_step(device_t dev, int left_step, int right_step)
950246454Shselasky{
951246454Shselasky	struct snd_mixer *m;
952246454Shselasky	struct cdev *pdev;
953246454Shselasky
954246454Shselasky	pdev = mixer_get_devt(dev);
955246454Shselasky	m = pdev->si_drv1;
956246454Shselasky	snd_mtxlock(m->lock);
957246454Shselasky	mixer_hwvol_step_locked(m, left_step, right_step);
95874763Scg	snd_mtxunlock(m->lock);
95970618Sjhb}
96070618Sjhb
961170815Sariffint
962170815Sariffmixer_busy(struct snd_mixer *m)
963170815Sariff{
964170815Sariff	KASSERT(m != NULL, ("NULL snd_mixer"));
965170815Sariff
966170815Sariff	return (m->busy);
967170815Sariff}
968170815Sariff
969170815Sariffint
970170815Sariffmix_set(struct snd_mixer *m, u_int dev, u_int left, u_int right)
971170815Sariff{
972170815Sariff	int ret;
973170815Sariff
974170815Sariff	KASSERT(m != NULL, ("NULL snd_mixer"));
975170815Sariff
976170815Sariff	snd_mtxlock(m->lock);
977170815Sariff	ret = mixer_set(m, dev, left | (right << 8));
978170815Sariff	snd_mtxunlock(m->lock);
979170815Sariff
980170815Sariff	return ((ret != 0) ? ENXIO : 0);
981170815Sariff}
982170815Sariff
983170815Sariffint
984170815Sariffmix_get(struct snd_mixer *m, u_int dev)
985170815Sariff{
986170815Sariff	int ret;
987170815Sariff
988170815Sariff	KASSERT(m != NULL, ("NULL snd_mixer"));
989170815Sariff
990170815Sariff	snd_mtxlock(m->lock);
991170815Sariff	ret = mixer_get(m, dev);
992170815Sariff	snd_mtxunlock(m->lock);
993170815Sariff
994170815Sariff	return (ret);
995170815Sariff}
996170815Sariff
997170815Sariffint
998170815Sariffmix_setrecsrc(struct snd_mixer *m, u_int32_t src)
999170815Sariff{
1000170815Sariff	int ret;
1001170815Sariff
1002170815Sariff	KASSERT(m != NULL, ("NULL snd_mixer"));
1003170815Sariff
1004170815Sariff	snd_mtxlock(m->lock);
1005170815Sariff	ret = mixer_setrecsrc(m, src);
1006170815Sariff	snd_mtxunlock(m->lock);
1007170815Sariff
1008170815Sariff	return ((ret != 0) ? ENXIO : 0);
1009170815Sariff}
1010170815Sariff
1011170815Sariffu_int32_t
1012170815Sariffmix_getrecsrc(struct snd_mixer *m)
1013170815Sariff{
1014170815Sariff	u_int32_t ret;
1015170815Sariff
1016170815Sariff	KASSERT(m != NULL, ("NULL snd_mixer"));
1017170815Sariff
1018170815Sariff	snd_mtxlock(m->lock);
1019170815Sariff	ret = mixer_getrecsrc(m);
1020170815Sariff	snd_mtxunlock(m->lock);
1021170815Sariff
1022170815Sariff	return (ret);
1023170815Sariff}
1024170815Sariff
1025170815Sariffint
1026170815Sariffmix_get_type(struct snd_mixer *m)
1027170815Sariff{
1028170815Sariff	KASSERT(m != NULL, ("NULL snd_mixer"));
1029170815Sariff
1030170815Sariff	return (m->type);
1031170815Sariff}
1032170815Sariff
1033359886Shselaskydevice_t
1034359886Shselaskymix_get_dev(struct snd_mixer *m)
1035359886Shselasky{
1036359886Shselasky	KASSERT(m != NULL, ("NULL snd_mixer"));
1037359886Shselasky
1038359886Shselasky	return (m->dev);
1039359886Shselasky}
1040359886Shselasky
104178362Scg/* ----------------------------------------------------------------------- */
104250724Scg
104378362Scgstatic int
1044130585Sphkmixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
104578362Scg{
1046170815Sariff	struct snddev_info *d;
104778362Scg	struct snd_mixer *m;
104878362Scg
1049170815Sariff
1050170815Sariff	if (i_dev == NULL || i_dev->si_drv1 == NULL)
1051170815Sariff		return (EBADF);
1052170815Sariff
105382181Scg	m = i_dev->si_drv1;
1054170815Sariff	d = device_get_softc(m->dev);
1055358878Shselasky	if (PCM_DETACHING(d) || !PCM_REGISTERED(d))
1056170815Sariff		return (EBADF);
1057170815Sariff
1058170815Sariff	/* XXX Need Giant magic entry ??? */
1059170815Sariff
106082181Scg	snd_mtxlock(m->lock);
1061168247Sariff	m->busy = 1;
1062170815Sariff	snd_mtxunlock(m->lock);
106382181Scg
1064170815Sariff	return (0);
106578362Scg}
106678362Scg
106778362Scgstatic int
1068130585Sphkmixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
106978362Scg{
1070170815Sariff	struct snddev_info *d;
107178362Scg	struct snd_mixer *m;
1072170815Sariff	int ret;
107378362Scg
1074170815Sariff	if (i_dev == NULL || i_dev->si_drv1 == NULL)
1075170815Sariff		return (EBADF);
1076170815Sariff
107782181Scg	m = i_dev->si_drv1;
1078170815Sariff	d = device_get_softc(m->dev);
1079170815Sariff	if (!PCM_REGISTERED(d))
1080170815Sariff		return (EBADF);
1081170815Sariff
1082170815Sariff	/* XXX Need Giant magic entry ??? */
1083170815Sariff
108482181Scg	snd_mtxlock(m->lock);
1085170815Sariff	ret = (m->busy == 0) ? EBADF : 0;
1086168247Sariff	m->busy = 0;
1087170815Sariff	snd_mtxunlock(m->lock);
108878362Scg
1089170815Sariff	return (ret);
109078362Scg}
109178362Scg
1092170815Sariffstatic int
1093193640Sariffmixer_ioctl_channel(struct cdev *dev, u_long cmd, caddr_t arg, int mode,
1094193640Sariff    struct thread *td, int from)
1095193640Sariff{
1096193640Sariff	struct snddev_info *d;
1097193640Sariff	struct snd_mixer *m;
1098193640Sariff	struct pcm_channel *c, *rdch, *wrch;
1099193640Sariff	pid_t pid;
1100193640Sariff	int j, ret;
1101193640Sariff
1102193640Sariff	if (td == NULL || td->td_proc == NULL)
1103193640Sariff		return (-1);
1104193640Sariff
1105193640Sariff	m = dev->si_drv1;
1106193640Sariff	d = device_get_softc(m->dev);
1107193640Sariff	j = cmd & 0xff;
1108193640Sariff
1109193640Sariff	switch (j) {
1110193640Sariff	case SOUND_MIXER_PCM:
1111193640Sariff	case SOUND_MIXER_RECLEV:
1112193640Sariff	case SOUND_MIXER_DEVMASK:
1113193640Sariff	case SOUND_MIXER_CAPS:
1114193640Sariff	case SOUND_MIXER_STEREODEVS:
1115193640Sariff		break;
1116193640Sariff	default:
1117193640Sariff		return (-1);
1118193640Sariff		break;
1119193640Sariff	}
1120193640Sariff
1121193640Sariff	pid = td->td_proc->p_pid;
1122193640Sariff	rdch = NULL;
1123193640Sariff	wrch = NULL;
1124193640Sariff	c = NULL;
1125193640Sariff	ret = -1;
1126193640Sariff
1127193640Sariff	/*
1128193640Sariff	 * This is unfair. Imagine single proc opening multiple
1129193640Sariff	 * instances of same direction. What we do right now
1130193640Sariff	 * is looking for the first matching proc/pid, and just
1131193640Sariff	 * that. Nothing more. Consider it done.
1132193640Sariff	 *
1133193640Sariff	 * The better approach of controlling specific channel
1134193640Sariff	 * pcm or rec volume is by doing mixer ioctl
1135193640Sariff	 * (SNDCTL_DSP_[SET|GET][PLAY|REC]VOL / SOUND_MIXER_[PCM|RECLEV]
1136193640Sariff	 * on its open fd, rather than cracky mixer bypassing here.
1137193640Sariff	 */
1138193640Sariff	CHN_FOREACH(c, d, channels.pcm.opened) {
1139193640Sariff		CHN_LOCK(c);
1140193640Sariff		if (c->pid != pid ||
1141193640Sariff		    !(c->feederflags & (1 << FEEDER_VOLUME))) {
1142193640Sariff			CHN_UNLOCK(c);
1143193640Sariff			continue;
1144193640Sariff		}
1145193640Sariff		if (rdch == NULL && c->direction == PCMDIR_REC) {
1146193640Sariff			rdch = c;
1147193640Sariff			if (j == SOUND_MIXER_RECLEV)
1148193640Sariff				goto mixer_ioctl_channel_proc;
1149193640Sariff		} else if (wrch == NULL && c->direction == PCMDIR_PLAY) {
1150193640Sariff			wrch = c;
1151193640Sariff			if (j == SOUND_MIXER_PCM)
1152193640Sariff				goto mixer_ioctl_channel_proc;
1153193640Sariff		}
1154193640Sariff		CHN_UNLOCK(c);
1155193640Sariff		if (rdch != NULL && wrch != NULL)
1156193640Sariff			break;
1157193640Sariff	}
1158193640Sariff
1159193640Sariff	if (rdch == NULL && wrch == NULL)
1160193640Sariff		return (-1);
1161193640Sariff
1162193640Sariff	if ((j == SOUND_MIXER_DEVMASK || j == SOUND_MIXER_CAPS ||
1163193640Sariff	    j == SOUND_MIXER_STEREODEVS) &&
1164202150Smav	    (cmd & ~0xff) == MIXER_READ(0)) {
1165193640Sariff		snd_mtxlock(m->lock);
1166193640Sariff		*(int *)arg = mix_getdevs(m);
1167193640Sariff		snd_mtxunlock(m->lock);
1168193640Sariff		if (rdch != NULL)
1169193640Sariff			*(int *)arg |= SOUND_MASK_RECLEV;
1170193640Sariff		if (wrch != NULL)
1171193640Sariff			*(int *)arg |= SOUND_MASK_PCM;
1172193640Sariff		ret = 0;
1173193640Sariff	}
1174193640Sariff
1175193640Sariff	return (ret);
1176193640Sariff
1177193640Sariffmixer_ioctl_channel_proc:
1178193640Sariff
1179193640Sariff	KASSERT(c != NULL, ("%s(): NULL channel", __func__));
1180193640Sariff	CHN_LOCKASSERT(c);
1181193640Sariff
1182202150Smav	if ((cmd & ~0xff) == MIXER_WRITE(0)) {
1183193640Sariff		int left, right, center;
1184193640Sariff
1185193640Sariff		left = *(int *)arg & 0x7f;
1186193640Sariff		right = (*(int *)arg >> 8) & 0x7f;
1187193640Sariff		center = (left + right) >> 1;
1188193640Sariff		chn_setvolume_multi(c, SND_VOL_C_PCM, left, right, center);
1189202150Smav	} else if ((cmd & ~0xff) == MIXER_READ(0)) {
1190193640Sariff		*(int *)arg = CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL);
1191193640Sariff		*(int *)arg |=
1192193640Sariff		    CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR) << 8;
1193193640Sariff	}
1194193640Sariff
1195193640Sariff	CHN_UNLOCK(c);
1196193640Sariff
1197193640Sariff	return (0);
1198193640Sariff}
1199193640Sariff
1200193640Sariffstatic int
1201170815Sariffmixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
1202170815Sariff    struct thread *td)
1203170815Sariff{
1204170815Sariff	struct snddev_info *d;
1205170815Sariff	int ret;
1206170815Sariff
1207170815Sariff	if (i_dev == NULL || i_dev->si_drv1 == NULL)
1208170815Sariff		return (EBADF);
1209170815Sariff
1210170815Sariff	d = device_get_softc(((struct snd_mixer *)i_dev->si_drv1)->dev);
1211358878Shselasky	if (PCM_DETACHING(d) || !PCM_REGISTERED(d))
1212170815Sariff		return (EBADF);
1213170815Sariff
1214170815Sariff	PCM_GIANT_ENTER(d);
1215170815Sariff	PCM_ACQUIRE_QUICK(d);
1216170815Sariff
1217193640Sariff	ret = -1;
1218170815Sariff
1219193640Sariff	if (mixer_bypass != 0 && (d->flags & SD_F_VPC))
1220193640Sariff		ret = mixer_ioctl_channel(i_dev, cmd, arg, mode, td,
1221193640Sariff		    MIXER_CMD_CDEV);
1222193640Sariff
1223193640Sariff	if (ret == -1)
1224193640Sariff		ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td,
1225193640Sariff		    MIXER_CMD_CDEV);
1226193640Sariff
1227170815Sariff	PCM_RELEASE_QUICK(d);
1228170815Sariff	PCM_GIANT_LEAVE(d);
1229170815Sariff
1230170815Sariff	return (ret);
1231170815Sariff}
1232170815Sariff
1233269228Smavstatic void
1234269228Smavmixer_mixerinfo(struct snd_mixer *m, mixer_info *mi)
1235269228Smav{
1236269228Smav	bzero((void *)mi, sizeof(*mi));
1237269228Smav	strlcpy(mi->id, m->name, sizeof(mi->id));
1238269228Smav	strlcpy(mi->name, device_get_desc(m->dev), sizeof(mi->name));
1239269228Smav	mi->modify_counter = m->modify_counter;
1240269228Smav}
1241269228Smav
1242170815Sariff/*
1243170815Sariff * XXX Make sure you can guarantee concurrency safety before calling this
1244193640Sariff *     function, be it through Giant, PCM_*, etc !
1245170815Sariff */
124678362Scgint
1247170815Sariffmixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode,
1248170815Sariff    struct thread *td, int from)
124978362Scg{
125078670Scg	struct snd_mixer *m;
1251202150Smav	int ret = EINVAL, *arg_i = (int *)arg;
125278362Scg	int v = -1, j = cmd & 0xff;
125378362Scg
1254187030Smav	/*
1255187030Smav	 * Certain ioctls may be made on any type of device (audio, mixer,
1256187030Smav	 * and MIDI).  Handle those special cases here.
1257187030Smav	 */
1258187030Smav	if (IOCGROUP(cmd) == 'X') {
1259187030Smav		switch (cmd) {
1260187030Smav		case SNDCTL_SYSINFO:
1261187030Smav			sound_oss_sysinfo((oss_sysinfo *)arg);
1262187030Smav			return (0);
1263187030Smav		case SNDCTL_CARDINFO:
1264187030Smav			return (sound_oss_card_info((oss_card_info *)arg));
1265187030Smav	    	case SNDCTL_AUDIOINFO:
1266187030Smav	    	case SNDCTL_AUDIOINFO_EX:
1267187030Smav	    	case SNDCTL_ENGINEINFO:
1268187030Smav			return (dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg));
1269187030Smav		case SNDCTL_MIXERINFO:
1270187030Smav			return (mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg));
1271187030Smav		}
1272187036Smav		return (EINVAL);
1273187030Smav	}
1274187030Smav
127578362Scg	m = i_dev->si_drv1;
1276156929Sariff
1277156929Sariff	if (m == NULL)
1278170815Sariff		return (EBADF);
127978362Scg
128078362Scg	snd_mtxlock(m->lock);
1281170815Sariff	if (from == MIXER_CMD_CDEV && !m->busy) {
1282156929Sariff		snd_mtxunlock(m->lock);
1283170815Sariff		return (EBADF);
1284156929Sariff	}
1285202150Smav	switch (cmd) {
1286202150Smav	case SNDCTL_DSP_GET_RECSRC_NAMES:
1287202150Smav		bcopy((void *)&m->enuminfo, arg, sizeof(oss_mixer_enuminfo));
1288202150Smav		ret = 0;
1289202150Smav		goto done;
1290202150Smav	case SNDCTL_DSP_GET_RECSRC:
1291202150Smav		ret = mixer_get_recroute(m, arg_i);
1292202150Smav		goto done;
1293202150Smav	case SNDCTL_DSP_SET_RECSRC:
1294202150Smav		ret = mixer_set_recroute(m, *arg_i);
1295202150Smav		goto done;
1296202150Smav	case OSS_GETVERSION:
1297202150Smav		*arg_i = SOUND_VERSION;
1298202150Smav		ret = 0;
1299202150Smav		goto done;
1300269228Smav	case SOUND_MIXER_INFO:
1301269228Smav		mixer_mixerinfo(m, (mixer_info *)arg);
1302269228Smav		ret = 0;
1303269228Smav		goto done;
1304202150Smav	}
1305202150Smav	if ((cmd & ~0xff) == MIXER_WRITE(0)) {
130678362Scg		if (j == SOUND_MIXER_RECSRC)
130778362Scg			ret = mixer_setrecsrc(m, *arg_i);
130878362Scg		else
130978362Scg			ret = mixer_set(m, j, *arg_i);
131078362Scg		snd_mtxunlock(m->lock);
1311170815Sariff		return ((ret == 0) ? 0 : ENXIO);
131278362Scg	}
1313202150Smav	if ((cmd & ~0xff) == MIXER_READ(0)) {
131478362Scg		switch (j) {
1315202150Smav		case SOUND_MIXER_DEVMASK:
1316202150Smav		case SOUND_MIXER_CAPS:
1317202150Smav		case SOUND_MIXER_STEREODEVS:
131878362Scg			v = mix_getdevs(m);
131978362Scg			break;
1320202150Smav		case SOUND_MIXER_RECMASK:
132178362Scg			v = mix_getrecdevs(m);
132278362Scg			break;
1323202150Smav		case SOUND_MIXER_RECSRC:
132478362Scg			v = mixer_getrecsrc(m);
132578362Scg			break;
132678362Scg		default:
132778362Scg			v = mixer_get(m, j);
132878362Scg		}
132978362Scg		*arg_i = v;
133078362Scg		snd_mtxunlock(m->lock);
1331170815Sariff		return ((v != -1) ? 0 : ENXIO);
133278362Scg	}
1333202150Smavdone:
133478362Scg	snd_mtxunlock(m->lock);
1335170815Sariff	return (ret);
133678362Scg}
133778362Scg
133878362Scgstatic void
1339170161Sariffmixer_clone(void *arg,
1340170161Sariff    struct ucred *cred,
1341170161Sariff    char *name, int namelen, struct cdev **dev)
134278362Scg{
1343170161Sariff	struct snddev_info *d;
134478362Scg
1345130640Sphk	if (*dev != NULL)
134678362Scg		return;
134778362Scg	if (strcmp(name, "mixer") == 0) {
1348170161Sariff		d = devclass_get_softc(pcm_devclass, snd_unit);
1349170815Sariff		if (PCM_REGISTERED(d) && d->mixer_dev != NULL) {
1350170161Sariff			*dev = d->mixer_dev;
1351144389Sphk			dev_ref(*dev);
1352144389Sphk		}
135378362Scg	}
135478362Scg}
135578362Scg
135678362Scgstatic void
135778362Scgmixer_sysinit(void *p)
135878362Scg{
1359170161Sariff	if (mixer_ehtag != NULL)
1360170161Sariff		return;
136178362Scg	mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000);
136278362Scg}
136378362Scg
136478362Scgstatic void
136578362Scgmixer_sysuninit(void *p)
136678362Scg{
1367170161Sariff	if (mixer_ehtag == NULL)
1368170161Sariff		return;
1369170161Sariff	EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag);
1370170161Sariff	mixer_ehtag = NULL;
137178362Scg}
137278362Scg
137378362ScgSYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
137478362ScgSYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
137578362Scg
1376162588Snetchild/**
1377162588Snetchild * @brief Handler for SNDCTL_MIXERINFO
1378162588Snetchild *
1379162588Snetchild * This function searches for a mixer based on the numeric ID stored
1380162588Snetchild * in oss_miserinfo::dev.  If set to -1, then information about the
1381162588Snetchild * current mixer handling the request is provided.  Note, however, that
1382162588Snetchild * this ioctl may be made with any sound device (audio, mixer, midi).
1383162588Snetchild *
1384162588Snetchild * @note Caller must not hold any PCM device, channel, or mixer locks.
1385162588Snetchild *
1386162588Snetchild * See http://manuals.opensound.com/developer/SNDCTL_MIXERINFO.html for
1387162588Snetchild * more information.
1388162588Snetchild *
1389162588Snetchild * @param i_dev	character device on which the ioctl arrived
1390162588Snetchild * @param arg	user argument (oss_mixerinfo *)
1391162588Snetchild *
1392162588Snetchild * @retval EINVAL	oss_mixerinfo::dev specified a bad value
1393162588Snetchild * @retval 0		success
1394162588Snetchild */
1395162588Snetchildint
1396162588Snetchildmixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
1397162588Snetchild{
1398162588Snetchild	struct snddev_info *d;
1399162588Snetchild	struct snd_mixer *m;
1400170815Sariff	int nmix, i;
140178362Scg
1402162588Snetchild	/*
1403162588Snetchild	 * If probing the device handling the ioctl, make sure it's a mixer
1404162588Snetchild	 * device.  (This ioctl is valid on audio, mixer, and midi devices.)
1405162588Snetchild	 */
1406170815Sariff	if (mi->dev == -1 && i_dev->si_devsw != &mixer_cdevsw)
1407170815Sariff		return (EINVAL);
1408162588Snetchild
1409162606Snetchild	d = NULL;
1410162588Snetchild	m = NULL;
1411162588Snetchild	nmix = 0;
1412162588Snetchild
1413162588Snetchild	/*
1414162588Snetchild	 * There's a 1:1 relationship between mixers and PCM devices, so
1415162588Snetchild	 * begin by iterating over PCM devices and search for our mixer.
1416162588Snetchild	 */
1417170235Sariff	for (i = 0; pcm_devclass != NULL &&
1418170235Sariff	    i < devclass_get_maxunit(pcm_devclass); i++) {
1419162588Snetchild		d = devclass_get_softc(pcm_devclass, i);
1420358878Shselasky		if (PCM_DETACHING(d) || !PCM_REGISTERED(d))
1421162588Snetchild			continue;
1422162588Snetchild
1423170815Sariff		/* XXX Need Giant magic entry */
1424170815Sariff
1425162588Snetchild		/* See the note in function docblock. */
1426193640Sariff		PCM_UNLOCKASSERT(d);
1427193640Sariff		PCM_LOCK(d);
1428162588Snetchild
1429170815Sariff		if (d->mixer_dev != NULL && d->mixer_dev->si_drv1 != NULL &&
1430170815Sariff		    ((mi->dev == -1 && d->mixer_dev == i_dev) ||
1431170815Sariff		    mi->dev == nmix)) {
1432170815Sariff			m = d->mixer_dev->si_drv1;
1433170815Sariff			mtx_lock(m->lock);
1434162588Snetchild
1435170815Sariff			/*
1436170815Sariff			 * At this point, the following synchronization stuff
1437170815Sariff			 * has happened:
1438170815Sariff			 * - a specific PCM device is locked.
1439170815Sariff			 * - a specific mixer device has been locked, so be
1440170815Sariff			 *   sure to unlock when existing.
1441170815Sariff			 */
1442170815Sariff			bzero((void *)mi, sizeof(*mi));
1443170815Sariff			mi->dev = nmix;
1444170815Sariff			snprintf(mi->id, sizeof(mi->id), "mixer%d", i);
1445170815Sariff			strlcpy(mi->name, m->name, sizeof(mi->name));
1446170815Sariff			mi->modify_counter = m->modify_counter;
1447170815Sariff			mi->card_number = i;
1448170815Sariff			/*
1449170815Sariff			 * Currently, FreeBSD assumes 1:1 relationship between
1450170815Sariff			 * a pcm and mixer devices, so this is hardcoded to 0.
1451170815Sariff			 */
1452170815Sariff			mi->port_number = 0;
1453162588Snetchild
1454170815Sariff			/**
1455170815Sariff			 * @todo Fill in @sa oss_mixerinfo::mixerhandle.
1456170815Sariff			 * @note From 4Front:  "mixerhandle is an arbitrary
1457170815Sariff			 *       string that identifies the mixer better than
1458170815Sariff			 *       the device number (mixerinfo.dev).  Device
1459170815Sariff			 *       numbers may change depending on the order the
1460170815Sariff			 *       drivers are loaded. However the handle should
1461170815Sariff			 *       remain the same provided that the sound card
1462170815Sariff			 *       is not moved to another PCI slot."
1463170815Sariff			 */
1464162588Snetchild
1465170815Sariff			/**
1466170815Sariff			 * @note
1467170815Sariff			 * @sa oss_mixerinfo::magic is a reserved field.
1468170815Sariff			 *
1469170815Sariff			 * @par
1470170815Sariff			 * From 4Front:  "magic is usually 0. However some
1471170815Sariff			 * devices may have dedicated setup utilities and the
1472170815Sariff			 * magic field may contain an unique driver specific
1473170815Sariff			 * value (managed by [4Front])."
1474170815Sariff			 */
1475162588Snetchild
1476170815Sariff			mi->enabled = device_is_attached(m->dev) ? 1 : 0;
1477170815Sariff			/**
1478170815Sariff			 * The only flag for @sa oss_mixerinfo::caps is
1479170815Sariff			 * currently MIXER_CAP_VIRTUAL, which I'm not sure we
1480170815Sariff			 * really worry about.
1481170815Sariff			 */
1482170815Sariff			/**
1483170815Sariff			 * Mixer extensions currently aren't supported, so
1484170815Sariff			 * leave @sa oss_mixerinfo::nrext blank for now.
1485170815Sariff			 */
1486170815Sariff			/**
1487170815Sariff			 * @todo Fill in @sa oss_mixerinfo::priority (requires
1488170815Sariff			 *       touching drivers?)
1489170815Sariff			 * @note The priority field is for mixer applets to
1490170815Sariff			 * determine which mixer should be the default, with 0
1491170815Sariff			 * being least preferred and 10 being most preferred.
1492170815Sariff			 * From 4Front:  "OSS drivers like ICH use higher
1493170815Sariff			 * values (10) because such chips are known to be used
1494170815Sariff			 * only on motherboards.  Drivers for high end pro
1495170815Sariff			 * devices use 0 because they will never be the
1496170815Sariff			 * default mixer. Other devices use values 1 to 9
1497170815Sariff			 * depending on the estimated probability of being the
1498170815Sariff			 * default device.
1499170815Sariff			 *
1500170815Sariff			 * XXX Described by Hannu@4Front, but not found in
1501170815Sariff			 *     soundcard.h.
1502231378Sed			strlcpy(mi->devnode, devtoname(d->mixer_dev),
1503170815Sariff			sizeof(mi->devnode));
1504170815Sariff			mi->legacy_device = i;
1505170815Sariff			 */
1506170815Sariff			mtx_unlock(m->lock);
1507170815Sariff		} else
1508170815Sariff			++nmix;
1509162588Snetchild
1510193640Sariff		PCM_UNLOCK(d);
1511162588Snetchild
1512170815Sariff		if (m != NULL)
1513170815Sariff			return (0);
1514170815Sariff	}
1515162588Snetchild
1516170815Sariff	return (EINVAL);
1517162588Snetchild}
1518184610Salfred
1519184610Salfred/*
1520184610Salfred * Allow the sound driver to use the mixer lock to protect its mixer
1521184610Salfred * data:
1522184610Salfred */
1523184610Salfredstruct mtx *
1524184610Salfredmixer_get_lock(struct snd_mixer *m)
1525184610Salfred{
1526184610Salfred	if (m->lock == NULL) {
1527184610Salfred		return (&Giant);
1528184610Salfred	}
1529184610Salfred	return (m->lock);
1530184610Salfred}
1531246421Shselasky
1532246421Shselaskyint
1533246421Shselaskymix_get_locked(struct snd_mixer *m, u_int dev, int *pleft, int *pright)
1534246421Shselasky{
1535246421Shselasky	int level;
1536246421Shselasky
1537246421Shselasky	level = mixer_get(m, dev);
1538246421Shselasky	if (level < 0) {
1539246421Shselasky		*pright = *pleft = -1;
1540246421Shselasky		return (-1);
1541246421Shselasky	}
1542246421Shselasky
1543246421Shselasky	*pleft = level & 0xFF;
1544246421Shselasky	*pright = (level >> 8) & 0xFF;
1545246421Shselasky
1546246421Shselasky	return (0);
1547246421Shselasky}
1548246421Shselasky
1549246421Shselaskyint
1550246421Shselaskymix_set_locked(struct snd_mixer *m, u_int dev, int left, int right)
1551246421Shselasky{
1552246421Shselasky	int level;
1553246421Shselasky
1554246421Shselasky	level = (left & 0xFF) | ((right & 0xFF) << 8);
1555246421Shselasky
1556246421Shselasky	return (mixer_set(m, dev, level));
1557246421Shselasky}
1558