mixer.c revision 162738
1263970Sdes/*-
257429Smarkm * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
392555Sdes * All rights reserved.
457429Smarkm *
557429Smarkm * Redistribution and use in source and binary forms, with or without
657429Smarkm * modification, are permitted provided that the following conditions
757429Smarkm * are met:
857429Smarkm * 1. Redistributions of source code must retain the above copyright
957429Smarkm *    notice, this list of conditions and the following disclaimer.
1057429Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1157429Smarkm *    notice, this list of conditions and the following disclaimer in the
1257429Smarkm *    documentation and/or other materials provided with the distribution.
1357429Smarkm *
1492555Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1592555Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1692555Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1792555Sdes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1892555Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1992555Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2092555Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2192555Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2292555Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2392555Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2457429Smarkm * SUCH DAMAGE.
2557429Smarkm */
2657429Smarkm
2757429Smarkm#include <dev/sound/pcm/sound.h>
28162852Sdes
29162852Sdes#include "mixer_if.h"
30162852Sdes
31162852SdesSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/mixer.c 162738 2006-09-28 17:29:00Z ariff $");
32162852Sdes
33162852SdesMALLOC_DEFINE(M_MIXER, "mixer", "mixer");
34162852Sdes
35162852Sdes#define MIXER_NAMELEN	16
36221420Sdesstruct snd_mixer {
37162852Sdes	KOBJ_FIELDS;
38162852Sdes	const char *type;
39162852Sdes	void *devinfo;
40162852Sdes	int busy;
41162852Sdes	int hwvol_muted;
42162852Sdes	int hwvol_mixer;
4357429Smarkm	int hwvol_step;
44137015Sdes	device_t dev;
4576259Sgreen	u_int32_t hwvol_mute_level;
4676259Sgreen	u_int32_t devs;
4757429Smarkm	u_int32_t recdevs;
48162852Sdes	u_int32_t recsrc;
4957429Smarkm	u_int16_t level[32];
5092555Sdes	u_int32_t parent[32];
5192555Sdes	u_int32_t child[32];
5276259Sgreen	u_int32_t realdev[32];
53221420Sdes	char name[MIXER_NAMELEN];
5476259Sgreen	struct mtx *lock;
5592555Sdes	oss_mixer_enuminfo enuminfo;
56221420Sdes	/**
5776259Sgreen	 * Counter is incremented when applications change any of this
58221420Sdes	 * mixer's controls.  A change in value indicates that persistent
5976259Sgreen	 * mixer applications should update their displays.
6076259Sgreen	 */
6176259Sgreen	int modify_counter;
6276259Sgreen};
6376259Sgreen
6492555Sdesstatic u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
6592555Sdes	[SOUND_MIXER_VOLUME]	= 75,
66113908Sdes	[SOUND_MIXER_BASS]	= 50,
6792555Sdes	[SOUND_MIXER_TREBLE]	= 50,
68221420Sdes	[SOUND_MIXER_SYNTH]	= 75,
6992555Sdes	[SOUND_MIXER_PCM]	= 75,
7092555Sdes	[SOUND_MIXER_SPEAKER]	= 75,
71221420Sdes	[SOUND_MIXER_LINE]	= 75,
72113908Sdes	[SOUND_MIXER_MIC] 	= 0,
7392555Sdes	[SOUND_MIXER_CD]	= 75,
7476259Sgreen	[SOUND_MIXER_IGAIN]	= 0,
75162852Sdes	[SOUND_MIXER_LINE1]	= 75,
7676259Sgreen	[SOUND_MIXER_VIDEO]	= 75,
7776259Sgreen	[SOUND_MIXER_RECLEV]	= 0,
7876259Sgreen	[SOUND_MIXER_OGAIN]	= 50,
7976259Sgreen	[SOUND_MIXER_MONITOR]	= 75,
8076259Sgreen};
8176259Sgreen
8276259Sgreenstatic char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
8392555Sdes
84221420Sdesstatic d_open_t mixer_open;
8592555Sdesstatic d_close_t mixer_close;
86221420Sdes
87221420Sdesstatic struct cdevsw mixer_cdevsw = {
88221420Sdes	.d_version =	D_VERSION,
8992555Sdes	.d_flags =	D_TRACKCLOSE | D_NEEDGIANT,
90221420Sdes	.d_open =	mixer_open,
9192555Sdes	.d_close =	mixer_close,
92221420Sdes	.d_ioctl =	mixer_ioctl,
9392555Sdes	.d_name =	"mixer",
9492555Sdes};
9592555Sdes
9676259Sgreen/**
97221420Sdes * Keeps a count of mixer devices; used only by OSSv4 SNDCTL_SYSINFO ioctl.
9876259Sgreen */
9976259Sgreenint mixer_count = 0;
100221420Sdes
101221420Sdes#ifdef USING_DEVFS
102263970Sdesstatic eventhandler_tag mixer_ehtag;
103113908Sdes#endif
104113908Sdes
105113908Sdesstatic struct cdev *
10692555Sdesmixer_get_devt(device_t dev)
10776259Sgreen{
108263970Sdes	struct snddev_info *snddev;
10976259Sgreen
11076259Sgreen	snddev = device_get_softc(dev);
11176259Sgreen
11257429Smarkm	return snddev->mixer_dev;
11392555Sdes}
11492555Sdes
11592555Sdes#ifdef SND_DYNSYSCTL
11692555Sdesstatic int
11757429Smarkmmixer_lookup(char *devname)
11857429Smarkm{
11992555Sdes	int i;
12057429Smarkm
12192555Sdes	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
12292555Sdes		if (strncmp(devname, snd_mixernames[i],
12376259Sgreen		    strlen(snd_mixernames[i])) == 0)
12492555Sdes			return i;
125137015Sdes	return -1;
126137015Sdes}
127137015Sdes#endif
128149749Sdes
129149749Sdesstatic int
13076259Sgreenmixer_set_softpcmvol(struct snd_mixer *mixer, struct snddev_info *d,
131149749Sdes						unsigned left, unsigned right)
13276259Sgreen{
13392555Sdes	struct snddev_channel *sce;
13492555Sdes	struct pcm_channel *ch;
13576259Sgreen#ifdef USING_MUTEX
13676259Sgreen	int locked = (mixer->lock && mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0;
137149749Sdes
138149749Sdes	if (locked)
139149749Sdes		snd_mtxunlock(mixer->lock);
14076259Sgreen#endif
141149749Sdes	SLIST_FOREACH(sce, &d->channels, link) {
14276259Sgreen		ch = sce->channel;
14376259Sgreen		CHN_LOCK(ch);
144137015Sdes		if (ch->direction == PCMDIR_PLAY &&
145137015Sdes				(ch->feederflags & (1 << FEEDER_VOLUME)))
146137015Sdes			chn_setvolume(ch, left, right);
14776259Sgreen		CHN_UNLOCK(ch);
14876259Sgreen	}
14976259Sgreen#ifdef USING_MUTEX
15076259Sgreen	if (locked)
15176259Sgreen		snd_mtxlock(mixer->lock);
152113908Sdes#endif
153113908Sdes	return 0;
154113908Sdes}
155113908Sdes
15676259Sgreenstatic int
15776259Sgreenmixer_set(struct snd_mixer *m, unsigned dev, unsigned lev)
15898675Sdes{
15998675Sdes	struct snddev_info *d;
16098675Sdes	unsigned l, r, tl, tr;
16192555Sdes	u_int32_t parent = SOUND_MIXER_NONE, child = 0;
16298675Sdes	u_int32_t realdev;
16392555Sdes	int i;
16492555Sdes
165263970Sdes	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES ||
16692555Sdes	    (0 == (m->devs & (1 << dev))))
16757429Smarkm		return -1;
168146998Sdes
169146998Sdes	l = min((lev & 0x00ff), 100);
170146998Sdes	r = min(((lev & 0xff00) >> 8), 100);
171146998Sdes	realdev = m->realdev[dev];
172146998Sdes
173146998Sdes	d = device_get_softc(m->dev);
174146998Sdes	if (d == NULL)
175146998Sdes		return -1;
176146998Sdes
177146998Sdes	/* TODO: recursive handling */
178146998Sdes	parent = m->parent[dev];
179146998Sdes	if (parent >= SOUND_MIXER_NRDEVICES)
180146998Sdes		parent = SOUND_MIXER_NONE;
181146998Sdes	if (parent == SOUND_MIXER_NONE)
182146998Sdes		child = m->child[dev];
183146998Sdes
184146998Sdes	if (parent != SOUND_MIXER_NONE) {
185146998Sdes		tl = (l * (m->level[parent] & 0x00ff)) / 100;
186146998Sdes		tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100;
187146998Sdes		if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
188146998Sdes			mixer_set_softpcmvol(m, d, tl, tr);
189263970Sdes		else if (realdev != SOUND_MIXER_NONE &&
190146998Sdes		    MIXER_SET(m, realdev, tl, tr) < 0)
191146998Sdes			return -1;
192146998Sdes	} else if (child != 0) {
193146998Sdes		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
194			if (!(child & (1 << i)) || m->parent[i] != dev)
195				continue;
196			realdev = m->realdev[i];
197			tl = (l * (m->level[i] & 0x00ff)) / 100;
198			tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100;
199			if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
200				mixer_set_softpcmvol(m, d, tl, tr);
201			else if (realdev != SOUND_MIXER_NONE)
202				MIXER_SET(m, realdev, tl, tr);
203		}
204		realdev = m->realdev[dev];
205		if (realdev != SOUND_MIXER_NONE &&
206		    MIXER_SET(m, realdev, l, r) < 0)
207				return -1;
208	} else {
209		if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
210			mixer_set_softpcmvol(m, d, l, r);
211		else if (realdev != SOUND_MIXER_NONE &&
212		    MIXER_SET(m, realdev, l, r) < 0)
213			return -1;
214	}
215
216	m->level[dev] = l | (r << 8);
217
218	return 0;
219}
220
221static int
222mixer_get(struct snd_mixer *mixer, int dev)
223{
224	if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
225		return mixer->level[dev];
226	else return -1;
227}
228
229static int
230mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
231{
232	src &= mixer->recdevs;
233	if (src == 0)
234		src = SOUND_MASK_MIC;
235	mixer->recsrc = MIXER_SETRECSRC(mixer, src);
236	return 0;
237}
238
239static int
240mixer_getrecsrc(struct snd_mixer *mixer)
241{
242	return mixer->recsrc;
243}
244
245/**
246 * @brief Retrieve the route number of the current recording device
247 *
248 * OSSv4 assigns routing numbers to recording devices, unlike the previous
249 * API which relied on a fixed table of device numbers and names.  This
250 * function returns the routing number of the device currently selected
251 * for recording.
252 *
253 * For now, this function is kind of a goofy compatibility stub atop the
254 * existing sound system.  (For example, in theory, the old sound system
255 * allows multiple recording devices to be specified via a bitmask.)
256 *
257 * @param m	mixer context container thing
258 *
259 * @retval 0		success
260 * @retval EIDRM	no recording device found (generally not possible)
261 * @todo Ask about error code
262 */
263static int
264mixer_get_recroute(struct snd_mixer *m, int *route)
265{
266	int i, cnt;
267
268	cnt = 0;
269
270	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
271		/** @todo can user set a multi-device mask? (== or &?) */
272		if ((1 << i) == m->recsrc)
273			break;
274		if ((1 << i) & m->recdevs)
275			++cnt;
276	}
277
278	if (i == SOUND_MIXER_NRDEVICES)
279		return EIDRM;
280
281	*route = cnt;
282	return 0;
283}
284
285/**
286 * @brief Select a device for recording
287 *
288 * This function sets a recording source based on a recording device's
289 * routing number.  Said number is translated to an old school recdev
290 * mask and passed over mixer_setrecsrc.
291 *
292 * @param m	mixer context container thing
293 *
294 * @retval 0		success(?)
295 * @retval EINVAL	User specified an invalid device number
296 * @retval otherwise	error from mixer_setrecsrc
297 */
298static int
299mixer_set_recroute(struct snd_mixer *m, int route)
300{
301	int i, cnt, ret;
302
303	ret = 0;
304	cnt = 0;
305
306	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
307		if ((1 << i) & m->recdevs) {
308			if (route == cnt)
309				break;
310			++cnt;
311		}
312	}
313
314	if (i == SOUND_MIXER_NRDEVICES)
315		ret = EINVAL;
316	else
317		ret = mixer_setrecsrc(m, (1 << i));
318
319	return ret;
320}
321
322void
323mix_setdevs(struct snd_mixer *m, u_int32_t v)
324{
325	struct snddev_info *d = device_get_softc(m->dev);
326	int i;
327
328	if (m == NULL)
329		return;
330	if (d != NULL && (d->flags & SD_F_SOFTPCMVOL))
331		v |= SOUND_MASK_PCM;
332	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
333		if (m->parent[i] < SOUND_MIXER_NRDEVICES)
334			v |= 1 << m->parent[i];
335		v |= m->child[i];
336	}
337	m->devs = v;
338}
339
340/**
341 * @brief Record mask of available recording devices
342 *
343 * Calling functions are responsible for defining the mask of available
344 * recording devices.  This function records that value in a structure
345 * used by the rest of the mixer code.
346 *
347 * This function also populates a structure used by the SNDCTL_DSP_*RECSRC*
348 * family of ioctls that are part of OSSV4.  All recording device labels
349 * are concatenated in ascending order corresponding to their routing
350 * numbers.  (Ex:  a system might have 0 => 'vol', 1 => 'cd', 2 => 'line',
351 * etc.)  For now, these labels are just the standard recording device
352 * names (cd, line1, etc.), but will eventually be fully dynamic and user
353 * controlled.
354 *
355 * @param m	mixer device context container thing
356 * @param v	mask of recording devices
357 */
358void
359mix_setrecdevs(struct snd_mixer *m, u_int32_t v)
360{
361	oss_mixer_enuminfo *ei;
362	char *loc;
363	int i, nvalues, nwrote, nleft, ncopied;
364
365	ei = &m->enuminfo;
366
367	nvalues = 0;
368	nwrote = 0;
369	nleft = sizeof(ei->strings);
370	loc = ei->strings;
371
372	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
373		if ((1 << i) & v) {
374			ei->strindex[nvalues] = nwrote;
375			ncopied = strlcpy(loc, snd_mixernames[i], nleft) + 1;
376			    /* strlcpy retval doesn't include terminator */
377
378			nwrote += ncopied;
379			nleft -= ncopied;
380			nvalues++;
381
382			/*
383			 * XXX I don't think this should ever be possible.
384			 * Even with a move to dynamic device/channel names,
385			 * each label is limited to ~16 characters, so that'd
386			 * take a LOT to fill this buffer.
387			 */
388			if ((nleft <= 0) || (nvalues >= OSS_ENUM_MAXVALUE)) {
389				device_printf(m->dev,
390				    "mix_setrecdevs:  Not enough room to store device names--please file a bug report.\n");
391				device_printf(m->dev,
392				    "mix_setrecdevs:  Please include details about your sound hardware, OS version, etc.\n");
393				break;
394			}
395
396			loc = &ei->strings[nwrote];
397		}
398	}
399
400	/*
401	 * NB:	The SNDCTL_DSP_GET_RECSRC_NAMES ioctl ignores the dev
402	 * 	and ctrl fields.
403	 */
404	ei->nvalues = nvalues;
405	m->recdevs = v;
406}
407
408void
409mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs)
410{
411	u_int32_t mask = 0;
412	int i;
413
414	if (m == NULL || parent >= SOUND_MIXER_NRDEVICES)
415		return;
416	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
417		if (i == parent)
418			continue;
419		if (childs & (1 << i)) {
420			mask |= 1 << i;
421			if (m->parent[i] < SOUND_MIXER_NRDEVICES)
422				m->child[m->parent[i]] &= ~(1 << i);
423			m->parent[i] = parent;
424			m->child[i] = 0;
425		}
426	}
427	mask &= ~(1 << parent);
428	m->child[parent] = mask;
429}
430
431void
432mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev)
433{
434	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES ||
435	    !(realdev == SOUND_MIXER_NONE || realdev < SOUND_MIXER_NRDEVICES))
436		return;
437	m->realdev[dev] = realdev;
438}
439
440u_int32_t
441mix_getparent(struct snd_mixer *m, u_int32_t dev)
442{
443	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES)
444		return SOUND_MIXER_NONE;
445	return m->parent[dev];
446}
447
448u_int32_t
449mix_getchild(struct snd_mixer *m, u_int32_t dev)
450{
451	if (m == NULL || dev >= SOUND_MIXER_NRDEVICES)
452		return 0;
453	return m->child[dev];
454}
455
456u_int32_t
457mix_getdevs(struct snd_mixer *m)
458{
459	return m->devs;
460}
461
462u_int32_t
463mix_getrecdevs(struct snd_mixer *m)
464{
465	return m->recdevs;
466}
467
468void *
469mix_getdevinfo(struct snd_mixer *m)
470{
471	return m->devinfo;
472}
473
474int
475mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
476{
477	struct snddev_info *snddev;
478	struct snd_mixer *m;
479	u_int16_t v;
480	struct cdev *pdev;
481	int i, unit, val;
482
483	m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
484	snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev));
485	m->lock = snd_mtxcreate(m->name, "pcm mixer");
486	m->type = cls->name;
487	m->devinfo = devinfo;
488	m->busy = 0;
489	m->dev = dev;
490	for (i = 0; i < 32; i++) {
491		m->parent[i] = SOUND_MIXER_NONE;
492		m->child[i] = 0;
493		m->realdev[i] = i;
494	}
495
496	if (MIXER_INIT(m))
497		goto bad;
498
499	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
500		v = snd_mixerdefaults[i];
501
502		if (resource_int_value(device_get_name(dev),
503		    device_get_unit(dev), snd_mixernames[i], &val) == 0) {
504			if (val >= 0 && val <= 100) {
505				v = (u_int16_t) val;
506			}
507		}
508
509		mixer_set(m, i, v | (v << 8));
510	}
511
512	mixer_setrecsrc(m, SOUND_MASK_MIC);
513
514	unit = device_get_unit(dev);
515	pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
516		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
517	pdev->si_drv1 = m;
518	snddev = device_get_softc(dev);
519	snddev->mixer_dev = pdev;
520
521	++mixer_count;
522
523	if (bootverbose) {
524		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
525			if (!(m->devs & (1 << i)))
526				continue;
527			if (m->realdev[i] != i) {
528				device_printf(dev, "Mixer \"%s\" -> \"%s\":",
529				    snd_mixernames[i],
530				    (m->realdev[i] < SOUND_MIXER_NRDEVICES) ?
531				    snd_mixernames[m->realdev[i]] : "none");
532			} else {
533				device_printf(dev, "Mixer \"%s\":",
534				    snd_mixernames[i]);
535			}
536			if (m->parent[i] < SOUND_MIXER_NRDEVICES)
537				printf(" parent=\"%s\"",
538				    snd_mixernames[m->parent[i]]);
539			if (m->child[i] != 0)
540				printf(" child=0x%08x", m->child[i]);
541			printf("\n");
542		}
543		if (snddev->flags & SD_F_SOFTPCMVOL)
544			device_printf(dev, "Soft PCM mixer ENABLED\n");
545	}
546
547	return 0;
548
549bad:
550	snd_mtxlock(m->lock);
551	snd_mtxfree(m->lock);
552	kobj_delete((kobj_t)m, M_MIXER);
553	return -1;
554}
555
556int
557mixer_uninit(device_t dev)
558{
559	int i;
560	struct snddev_info *d;
561	struct snd_mixer *m;
562	struct cdev *pdev;
563
564	d = device_get_softc(dev);
565	pdev = mixer_get_devt(dev);
566	if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL)
567		return EBADF;
568	m = pdev->si_drv1;
569	snd_mtxlock(m->lock);
570
571	if (m->busy) {
572		snd_mtxunlock(m->lock);
573		return EBUSY;
574	}
575
576	pdev->si_drv1 = NULL;
577	destroy_dev(pdev);
578
579	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
580		mixer_set(m, i, 0);
581
582	mixer_setrecsrc(m, SOUND_MASK_MIC);
583
584	MIXER_UNINIT(m);
585
586	snd_mtxfree(m->lock);
587	kobj_delete((kobj_t)m, M_MIXER);
588
589	d->mixer_dev = NULL;
590
591	--mixer_count;
592
593	return 0;
594}
595
596int
597mixer_reinit(device_t dev)
598{
599	struct snd_mixer *m;
600	struct cdev *pdev;
601	int i;
602
603	pdev = mixer_get_devt(dev);
604	m = pdev->si_drv1;
605	snd_mtxlock(m->lock);
606
607	i = MIXER_REINIT(m);
608	if (i) {
609		snd_mtxunlock(m->lock);
610		return i;
611	}
612
613	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
614		mixer_set(m, i, m->level[i]);
615
616	mixer_setrecsrc(m, m->recsrc);
617	snd_mtxunlock(m->lock);
618
619	return 0;
620}
621
622#ifdef SND_DYNSYSCTL
623static int
624sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
625{
626	char devname[32];
627	int error, dev;
628	struct snd_mixer *m;
629
630	m = oidp->oid_arg1;
631	snd_mtxlock(m->lock);
632	strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
633	snd_mtxunlock(m->lock);
634	error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
635	snd_mtxlock(m->lock);
636	if (error == 0 && req->newptr != NULL) {
637		dev = mixer_lookup(devname);
638		if (dev == -1) {
639			snd_mtxunlock(m->lock);
640			return EINVAL;
641		}
642		else if (dev != m->hwvol_mixer) {
643			m->hwvol_mixer = dev;
644			m->hwvol_muted = 0;
645		}
646	}
647	snd_mtxunlock(m->lock);
648	return error;
649}
650#endif
651
652int
653mixer_hwvol_init(device_t dev)
654{
655	struct snd_mixer *m;
656	struct cdev *pdev;
657
658	pdev = mixer_get_devt(dev);
659	m = pdev->si_drv1;
660
661	m->hwvol_mixer = SOUND_MIXER_VOLUME;
662	m->hwvol_step = 5;
663#ifdef SND_DYNSYSCTL
664	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
665            OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
666	SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
667            OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
668	    sysctl_hw_snd_hwvol_mixer, "A", "");
669#endif
670	return 0;
671}
672
673void
674mixer_hwvol_mute(device_t dev)
675{
676	struct snd_mixer *m;
677	struct cdev *pdev;
678
679	pdev = mixer_get_devt(dev);
680	m = pdev->si_drv1;
681	snd_mtxlock(m->lock);
682	if (m->hwvol_muted) {
683		m->hwvol_muted = 0;
684		mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
685	} else {
686		m->hwvol_muted++;
687		m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
688		mixer_set(m, m->hwvol_mixer, 0);
689	}
690	snd_mtxunlock(m->lock);
691}
692
693void
694mixer_hwvol_step(device_t dev, int left_step, int right_step)
695{
696	struct snd_mixer *m;
697	int level, left, right;
698	struct cdev *pdev;
699
700	pdev = mixer_get_devt(dev);
701	m = pdev->si_drv1;
702	snd_mtxlock(m->lock);
703	if (m->hwvol_muted) {
704		m->hwvol_muted = 0;
705		level = m->hwvol_mute_level;
706	} else
707		level = mixer_get(m, m->hwvol_mixer);
708	if (level != -1) {
709		left = level & 0xff;
710		right = level >> 8;
711		left += left_step * m->hwvol_step;
712		if (left < 0)
713			left = 0;
714		right += right_step * m->hwvol_step;
715		if (right < 0)
716			right = 0;
717		mixer_set(m, m->hwvol_mixer, left | right << 8);
718	}
719	snd_mtxunlock(m->lock);
720}
721
722/* ----------------------------------------------------------------------- */
723
724static int
725mixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
726{
727	struct snd_mixer *m;
728
729	m = i_dev->si_drv1;
730	snd_mtxlock(m->lock);
731
732	m->busy++;
733
734	snd_mtxunlock(m->lock);
735	return 0;
736}
737
738static int
739mixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
740{
741	struct snd_mixer *m;
742
743	m = i_dev->si_drv1;
744	snd_mtxlock(m->lock);
745
746	if (!m->busy) {
747		snd_mtxunlock(m->lock);
748		return EBADF;
749	}
750	m->busy--;
751
752	snd_mtxunlock(m->lock);
753	return 0;
754}
755
756int
757mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
758{
759	struct snd_mixer *m;
760	int ret, *arg_i = (int *)arg;
761	int v = -1, j = cmd & 0xff;
762
763	m = i_dev->si_drv1;
764
765	if (m == NULL)
766		return EBADF;
767
768	snd_mtxlock(m->lock);
769	if (mode != -1 && !m->busy) {
770		snd_mtxunlock(m->lock);
771		return EBADF;
772	}
773
774	if (cmd == SNDCTL_MIXERINFO) {
775		snd_mtxunlock(m->lock);
776		return mixer_oss_mixerinfo(i_dev, (oss_mixerinfo *)arg);
777	}
778
779	if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
780		if (j == SOUND_MIXER_RECSRC)
781			ret = mixer_setrecsrc(m, *arg_i);
782		else
783			ret = mixer_set(m, j, *arg_i);
784		snd_mtxunlock(m->lock);
785		return (ret == 0)? 0 : ENXIO;
786	}
787
788    	if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
789		switch (j) {
790    		case SOUND_MIXER_DEVMASK:
791    		case SOUND_MIXER_CAPS:
792    		case SOUND_MIXER_STEREODEVS:
793			v = mix_getdevs(m);
794			break;
795
796    		case SOUND_MIXER_RECMASK:
797			v = mix_getrecdevs(m);
798			break;
799
800    		case SOUND_MIXER_RECSRC:
801			v = mixer_getrecsrc(m);
802			break;
803
804		default:
805			v = mixer_get(m, j);
806		}
807		*arg_i = v;
808		snd_mtxunlock(m->lock);
809		return (v != -1)? 0 : ENXIO;
810	}
811
812	ret = 0;
813
814	switch (cmd) {
815 	/** @todo Double check return values, error codes. */
816	case SNDCTL_SYSINFO:
817		sound_oss_sysinfo((oss_sysinfo *)arg);
818		break;
819	case SNDCTL_AUDIOINFO:
820		ret = dsp_oss_audioinfo(i_dev, (oss_audioinfo *)arg);
821		break;
822	case SNDCTL_DSP_GET_RECSRC_NAMES:
823		bcopy((void *)&m->enuminfo, arg, sizeof(oss_mixer_enuminfo));
824		break;
825	case SNDCTL_DSP_GET_RECSRC:
826		ret = mixer_get_recroute(m, arg_i);
827		break;
828	case SNDCTL_DSP_SET_RECSRC:
829		ret = mixer_set_recroute(m, *arg_i);
830		break;
831	default:
832		ret = ENXIO;
833	}
834
835	snd_mtxunlock(m->lock);
836	return ret;
837}
838
839#ifdef USING_DEVFS
840static void
841mixer_clone(void *arg, struct ucred *cred, char *name, int namelen,
842    struct cdev **dev)
843{
844	struct snddev_info *sd;
845
846	if (*dev != NULL)
847		return;
848	if (strcmp(name, "mixer") == 0) {
849		sd = devclass_get_softc(pcm_devclass, snd_unit);
850		if (sd != NULL && sd->mixer_dev != NULL) {
851			*dev = sd->mixer_dev;
852			dev_ref(*dev);
853		}
854	}
855}
856
857static void
858mixer_sysinit(void *p)
859{
860	mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000);
861}
862
863static void
864mixer_sysuninit(void *p)
865{
866	if (mixer_ehtag != NULL)
867		EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag);
868}
869
870SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
871SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
872#endif
873
874/**
875 * @brief Handler for SNDCTL_MIXERINFO
876 *
877 * This function searches for a mixer based on the numeric ID stored
878 * in oss_miserinfo::dev.  If set to -1, then information about the
879 * current mixer handling the request is provided.  Note, however, that
880 * this ioctl may be made with any sound device (audio, mixer, midi).
881 *
882 * @note Caller must not hold any PCM device, channel, or mixer locks.
883 *
884 * See http://manuals.opensound.com/developer/SNDCTL_MIXERINFO.html for
885 * more information.
886 *
887 * @param i_dev	character device on which the ioctl arrived
888 * @param arg	user argument (oss_mixerinfo *)
889 *
890 * @retval EINVAL	oss_mixerinfo::dev specified a bad value
891 * @retval 0		success
892 */
893int
894mixer_oss_mixerinfo(struct cdev *i_dev, oss_mixerinfo *mi)
895{
896	struct snddev_info *d;
897	struct snd_mixer *m;
898	struct cdev *t_cdev;
899	int nmix, ret, pcmunit, i;
900
901	/*
902	 * If probing the device handling the ioctl, make sure it's a mixer
903	 * device.  (This ioctl is valid on audio, mixer, and midi devices.)
904	 */
905	if ((mi->dev == -1) && (i_dev->si_devsw != &mixer_cdevsw))
906		return EINVAL;
907
908	d = NULL;
909	m = NULL;
910	t_cdev = NULL;
911	nmix = 0;
912	ret = 0;
913	pcmunit = -1; /* pcmX */
914
915	/*
916	 * There's a 1:1 relationship between mixers and PCM devices, so
917	 * begin by iterating over PCM devices and search for our mixer.
918	 */
919	for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
920		d = devclass_get_softc(pcm_devclass, i);
921		if (d == NULL)
922			continue;
923
924		/* See the note in function docblock. */
925		mtx_assert(d->lock, MA_NOTOWNED);
926		pcm_inprog(d, 1);
927		pcm_lock(d);
928
929		if (d->mixer_dev != NULL) {
930			if (((mi->dev == -1) && (d->mixer_dev == i_dev)) || (mi->dev == nmix)) {
931				t_cdev = d->mixer_dev;
932				pcmunit = i;
933				break;
934			}
935			++nmix;
936		}
937
938		pcm_unlock(d);
939		pcm_inprog(d, -1);
940	}
941
942	/*
943	 * If t_cdev is NULL, then search was exhausted and device wasn't
944	 * found.  No locks are held, so just return.
945	 */
946	if (t_cdev == NULL)
947		return EINVAL;
948
949	m = t_cdev->si_drv1;
950	mtx_lock(m->lock);
951
952	/*
953	 * At this point, the following synchronization stuff has happened:
954	 *   - a specific PCM device is locked and its "in progress
955	 *     operations" counter has been incremented, so be sure to unlock
956	 *     and decrement when exiting;
957	 *   - a specific mixer device has been locked, so be sure to unlock
958	 *     when existing.
959	 */
960
961	bzero((void *)mi, sizeof(*mi));
962
963	mi->dev = nmix;
964	snprintf(mi->id, sizeof(mi->id), "mixer%d", dev2unit(t_cdev));
965	strlcpy(mi->name, m->name, sizeof(mi->name));
966	mi->modify_counter = m->modify_counter;
967	mi->card_number = pcmunit;
968	/*
969	 * Currently, FreeBSD assumes 1:1 relationship between a pcm and
970	 * mixer devices, so this is hardcoded to 0.
971	 */
972	mi->port_number = 0;
973
974	/**
975	 * @todo Fill in @sa oss_mixerinfo::mixerhandle.
976	 * @note From 4Front:  "mixerhandle is an arbitrary string that
977	 * 	 identifies the mixer better than the device number
978	 * 	 (mixerinfo.dev). Device numbers may change depending on
979	 * 	 the order the drivers are loaded. However the handle
980	 * 	 should remain the same provided that the sound card is
981	 * 	 not moved to another PCI slot."
982	 */
983
984	/**
985	 * @note
986	 * @sa oss_mixerinfo::magic is a reserved field.
987	 *
988	 * @par
989	 * From 4Front:  "magic is usually 0. However some devices may have
990	 * dedicated setup utilities and the magic field may contain an
991	 * unique driver specific value (managed by [4Front])."
992	 */
993
994	mi->enabled = device_is_attached(m->dev) ? 1 : 0;
995	/**
996	 * The only flag for @sa oss_mixerinfo::caps is currently
997	 * MIXER_CAP_VIRTUAL, which I'm not sure we really worry about.
998	 */
999	/**
1000	 * Mixer extensions currently aren't supported, so leave
1001	 * @sa oss_mixerinfo::nrext blank for now.
1002	 */
1003	/**
1004	 * @todo Fill in @sa oss_mixerinfo::priority (requires touching
1005	 * 	 drivers?)
1006	 * @note The priority field is for mixer applets to determine which
1007	 * mixer should be the default, with 0 being least preferred and 10
1008	 * being most preferred.  From 4Front:  "OSS drivers like ICH use
1009	 * higher values (10) because such chips are known to be used only
1010	 * on motherboards.  Drivers for high end pro devices use 0 because
1011	 * they will never be the default mixer. Other devices use values 1
1012	 * to 9 depending on the estimated probability of being the default
1013	 * device.
1014	 *
1015	 * XXX Described by Hannu@4Front, but not found in soundcard.h.
1016	strlcpy(mi->devnode, t_cdev->si_name, sizeof(mi->devnode));
1017	mi->legacy_device = pcmunit;
1018	 */
1019
1020	mtx_unlock(m->lock);
1021	pcm_unlock(d);
1022	pcm_inprog(d, -1);
1023
1024	return ret;
1025}
1026