xref: /freebsd/sys/dev/sound/pcm/mixer.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
1 /*-
2  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <dev/sound/pcm/sound.h>
28 
29 #include "mixer_if.h"
30 
31 SND_DECLARE_FILE("$FreeBSD$");
32 
33 MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
34 
35 #define MIXER_NAMELEN	16
36 struct snd_mixer {
37 	KOBJ_FIELDS;
38 	const char *type;
39 	void *devinfo;
40 	int busy;
41 	int hwvol_muted;
42 	int hwvol_mixer;
43 	int hwvol_step;
44 	device_t dev;
45 	u_int32_t hwvol_mute_level;
46 	u_int32_t devs;
47 	u_int32_t recdevs;
48 	u_int32_t recsrc;
49 	u_int16_t level[32];
50 	char name[MIXER_NAMELEN];
51 	struct mtx *lock;
52 };
53 
54 static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
55 	[SOUND_MIXER_VOLUME]	= 75,
56 	[SOUND_MIXER_BASS]	= 50,
57 	[SOUND_MIXER_TREBLE]	= 50,
58 	[SOUND_MIXER_SYNTH]	= 75,
59 	[SOUND_MIXER_PCM]	= 75,
60 	[SOUND_MIXER_SPEAKER]	= 75,
61 	[SOUND_MIXER_LINE]	= 75,
62 	[SOUND_MIXER_MIC] 	= 0,
63 	[SOUND_MIXER_CD]	= 75,
64 	[SOUND_MIXER_IGAIN]	= 0,
65 	[SOUND_MIXER_LINE1]	= 75,
66 	[SOUND_MIXER_VIDEO]	= 75,
67 	[SOUND_MIXER_RECLEV]	= 0,
68 	[SOUND_MIXER_OGAIN]	= 50,
69 	[SOUND_MIXER_MONITOR]	= 75,
70 };
71 
72 static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
73 
74 static d_open_t mixer_open;
75 static d_close_t mixer_close;
76 
77 static struct cdevsw mixer_cdevsw = {
78 	.d_version =	D_VERSION,
79 	.d_flags =	D_TRACKCLOSE | D_NEEDGIANT,
80 	.d_open =	mixer_open,
81 	.d_close =	mixer_close,
82 	.d_ioctl =	mixer_ioctl,
83 	.d_name =	"mixer",
84 };
85 
86 #ifdef USING_DEVFS
87 static eventhandler_tag mixer_ehtag;
88 #endif
89 
90 static struct cdev *
91 mixer_get_devt(device_t dev)
92 {
93 	struct snddev_info *snddev;
94 
95 	snddev = device_get_softc(dev);
96 
97 	return snddev->mixer_dev;
98 }
99 
100 #ifdef SND_DYNSYSCTL
101 static int
102 mixer_lookup(char *devname)
103 {
104 	int i;
105 
106 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
107 		if (strncmp(devname, snd_mixernames[i],
108 		    strlen(snd_mixernames[i])) == 0)
109 			return i;
110 	return -1;
111 }
112 #endif
113 
114 static int
115 mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev)
116 {
117 	struct snddev_info *d;
118 	unsigned l, r;
119 	int v;
120 
121 	if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev))))
122 		return -1;
123 
124 	l = min((lev & 0x00ff), 100);
125 	r = min(((lev & 0xff00) >> 8), 100);
126 
127 	d = device_get_softc(mixer->dev);
128 	if (dev == SOUND_MIXER_PCM && d &&
129 			(d->flags & SD_F_SOFTVOL)) {
130 		struct snddev_channel *sce;
131 		struct pcm_channel *ch;
132 #ifdef USING_MUTEX
133 		int locked = (mixer->lock && mtx_owned((struct mtx *)(mixer->lock))) ? 1 : 0;
134 
135 		if (locked)
136 			snd_mtxunlock(mixer->lock);
137 #endif
138 		SLIST_FOREACH(sce, &d->channels, link) {
139 			ch = sce->channel;
140 			CHN_LOCK(ch);
141 			if (ch->direction == PCMDIR_PLAY &&
142 					(ch->feederflags & (1 << FEEDER_VOLUME)))
143 				chn_setvolume(ch, l, r);
144 			CHN_UNLOCK(ch);
145 		}
146 #ifdef USING_MUTEX
147 		if (locked)
148 			snd_mtxlock(mixer->lock);
149 #endif
150 	} else {
151 		v = MIXER_SET(mixer, dev, l, r);
152 		if (v < 0)
153 			return -1;
154 	}
155 
156 	mixer->level[dev] = l | (r << 8);
157 	return 0;
158 }
159 
160 static int
161 mixer_get(struct snd_mixer *mixer, int dev)
162 {
163 	if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
164 		return mixer->level[dev];
165 	else return -1;
166 }
167 
168 static int
169 mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
170 {
171 	src &= mixer->recdevs;
172 	if (src == 0)
173 		src = SOUND_MASK_MIC;
174 	mixer->recsrc = MIXER_SETRECSRC(mixer, src);
175 	return 0;
176 }
177 
178 static int
179 mixer_getrecsrc(struct snd_mixer *mixer)
180 {
181 	return mixer->recsrc;
182 }
183 
184 void
185 mix_setdevs(struct snd_mixer *m, u_int32_t v)
186 {
187 	struct snddev_info *d = device_get_softc(m->dev);
188 	if (d && (d->flags & SD_F_SOFTVOL))
189 		v |= SOUND_MASK_PCM;
190 	m->devs = v;
191 }
192 
193 void
194 mix_setrecdevs(struct snd_mixer *m, u_int32_t v)
195 {
196 	m->recdevs = v;
197 }
198 
199 u_int32_t
200 mix_getdevs(struct snd_mixer *m)
201 {
202 	return m->devs;
203 }
204 
205 u_int32_t
206 mix_getrecdevs(struct snd_mixer *m)
207 {
208 	return m->recdevs;
209 }
210 
211 void *
212 mix_getdevinfo(struct snd_mixer *m)
213 {
214 	return m->devinfo;
215 }
216 
217 int
218 mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
219 {
220 	struct snddev_info *snddev;
221 	struct snd_mixer *m;
222 	u_int16_t v;
223 	struct cdev *pdev;
224 	int i, unit, val;
225 
226 	m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
227 	snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev));
228 	m->lock = snd_mtxcreate(m->name, "pcm mixer");
229 	m->type = cls->name;
230 	m->devinfo = devinfo;
231 	m->busy = 0;
232 	m->dev = dev;
233 
234 	if (MIXER_INIT(m))
235 		goto bad;
236 
237 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
238 		v = snd_mixerdefaults[i];
239 
240 		if (resource_int_value(device_get_name(dev),
241 		    device_get_unit(dev), snd_mixernames[i], &val) == 0) {
242 			if (val >= 0 && val <= 100) {
243 				v = (u_int16_t) val;
244 			}
245 		}
246 
247 		mixer_set(m, i, v | (v << 8));
248 	}
249 
250 	mixer_setrecsrc(m, SOUND_MASK_MIC);
251 
252 	unit = device_get_unit(dev);
253 	pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
254 		 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
255 	pdev->si_drv1 = m;
256 	snddev = device_get_softc(dev);
257 	snddev->mixer_dev = pdev;
258 
259 	return 0;
260 
261 bad:
262 	snd_mtxlock(m->lock);
263 	snd_mtxfree(m->lock);
264 	kobj_delete((kobj_t)m, M_MIXER);
265 	return -1;
266 }
267 
268 int
269 mixer_uninit(device_t dev)
270 {
271 	int i;
272 	struct snd_mixer *m;
273 	struct cdev *pdev;
274 
275 	pdev = mixer_get_devt(dev);
276 	m = pdev->si_drv1;
277 	snd_mtxlock(m->lock);
278 
279 	if (m->busy) {
280 		snd_mtxunlock(m->lock);
281 		return EBUSY;
282 	}
283 
284 	pdev->si_drv1 = NULL;
285 	destroy_dev(pdev);
286 
287 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
288 		mixer_set(m, i, 0);
289 
290 	mixer_setrecsrc(m, SOUND_MASK_MIC);
291 
292 	MIXER_UNINIT(m);
293 
294 	snd_mtxfree(m->lock);
295 	kobj_delete((kobj_t)m, M_MIXER);
296 
297 	return 0;
298 }
299 
300 int
301 mixer_reinit(device_t dev)
302 {
303 	struct snd_mixer *m;
304 	struct cdev *pdev;
305 	int i;
306 
307 	pdev = mixer_get_devt(dev);
308 	m = pdev->si_drv1;
309 	snd_mtxlock(m->lock);
310 
311 	i = MIXER_REINIT(m);
312 	if (i) {
313 		snd_mtxunlock(m->lock);
314 		return i;
315 	}
316 
317 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
318 		mixer_set(m, i, m->level[i]);
319 
320 	mixer_setrecsrc(m, m->recsrc);
321 	snd_mtxunlock(m->lock);
322 
323 	return 0;
324 }
325 
326 #ifdef SND_DYNSYSCTL
327 static int
328 sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
329 {
330 	char devname[32];
331 	int error, dev;
332 	struct snd_mixer *m;
333 
334 	m = oidp->oid_arg1;
335 	snd_mtxlock(m->lock);
336 	strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
337 	snd_mtxunlock(m->lock);
338 	error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
339 	snd_mtxlock(m->lock);
340 	if (error == 0 && req->newptr != NULL) {
341 		dev = mixer_lookup(devname);
342 		if (dev == -1) {
343 			snd_mtxunlock(m->lock);
344 			return EINVAL;
345 		}
346 		else if (dev != m->hwvol_mixer) {
347 			m->hwvol_mixer = dev;
348 			m->hwvol_muted = 0;
349 		}
350 	}
351 	snd_mtxunlock(m->lock);
352 	return error;
353 }
354 #endif
355 
356 int
357 mixer_hwvol_init(device_t dev)
358 {
359 	struct snd_mixer *m;
360 	struct cdev *pdev;
361 
362 	pdev = mixer_get_devt(dev);
363 	m = pdev->si_drv1;
364 
365 	m->hwvol_mixer = SOUND_MIXER_VOLUME;
366 	m->hwvol_step = 5;
367 #ifdef SND_DYNSYSCTL
368 	SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
369             OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
370 	SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
371             OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
372 	    sysctl_hw_snd_hwvol_mixer, "A", "");
373 #endif
374 	return 0;
375 }
376 
377 void
378 mixer_hwvol_mute(device_t dev)
379 {
380 	struct snd_mixer *m;
381 	struct cdev *pdev;
382 
383 	pdev = mixer_get_devt(dev);
384 	m = pdev->si_drv1;
385 	snd_mtxlock(m->lock);
386 	if (m->hwvol_muted) {
387 		m->hwvol_muted = 0;
388 		mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
389 	} else {
390 		m->hwvol_muted++;
391 		m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
392 		mixer_set(m, m->hwvol_mixer, 0);
393 	}
394 	snd_mtxunlock(m->lock);
395 }
396 
397 void
398 mixer_hwvol_step(device_t dev, int left_step, int right_step)
399 {
400 	struct snd_mixer *m;
401 	int level, left, right;
402 	struct cdev *pdev;
403 
404 	pdev = mixer_get_devt(dev);
405 	m = pdev->si_drv1;
406 	snd_mtxlock(m->lock);
407 	if (m->hwvol_muted) {
408 		m->hwvol_muted = 0;
409 		level = m->hwvol_mute_level;
410 	} else
411 		level = mixer_get(m, m->hwvol_mixer);
412 	if (level != -1) {
413 		left = level & 0xff;
414 		right = level >> 8;
415 		left += left_step * m->hwvol_step;
416 		if (left < 0)
417 			left = 0;
418 		right += right_step * m->hwvol_step;
419 		if (right < 0)
420 			right = 0;
421 		mixer_set(m, m->hwvol_mixer, left | right << 8);
422 	}
423 	snd_mtxunlock(m->lock);
424 }
425 
426 /* ----------------------------------------------------------------------- */
427 
428 static int
429 mixer_open(struct cdev *i_dev, int flags, int mode, struct thread *td)
430 {
431 	struct snd_mixer *m;
432 
433 	m = i_dev->si_drv1;
434 	snd_mtxlock(m->lock);
435 
436 	m->busy++;
437 
438 	snd_mtxunlock(m->lock);
439 	return 0;
440 }
441 
442 static int
443 mixer_close(struct cdev *i_dev, int flags, int mode, struct thread *td)
444 {
445 	struct snd_mixer *m;
446 
447 	m = i_dev->si_drv1;
448 	snd_mtxlock(m->lock);
449 
450 	if (!m->busy) {
451 		snd_mtxunlock(m->lock);
452 		return EBADF;
453 	}
454 	m->busy--;
455 
456 	snd_mtxunlock(m->lock);
457 	return 0;
458 }
459 
460 int
461 mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
462 {
463 	struct snd_mixer *m;
464 	int ret, *arg_i = (int *)arg;
465 	int v = -1, j = cmd & 0xff;
466 
467 	m = i_dev->si_drv1;
468 	if (mode != -1 && !m->busy)
469 		return EBADF;
470 
471 	snd_mtxlock(m->lock);
472 	if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
473 		if (j == SOUND_MIXER_RECSRC)
474 			ret = mixer_setrecsrc(m, *arg_i);
475 		else
476 			ret = mixer_set(m, j, *arg_i);
477 		snd_mtxunlock(m->lock);
478 		return (ret == 0)? 0 : ENXIO;
479 	}
480 
481     	if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
482 		switch (j) {
483     		case SOUND_MIXER_DEVMASK:
484     		case SOUND_MIXER_CAPS:
485     		case SOUND_MIXER_STEREODEVS:
486 			v = mix_getdevs(m);
487 			break;
488 
489     		case SOUND_MIXER_RECMASK:
490 			v = mix_getrecdevs(m);
491 			break;
492 
493     		case SOUND_MIXER_RECSRC:
494 			v = mixer_getrecsrc(m);
495 			break;
496 
497 		default:
498 			v = mixer_get(m, j);
499 		}
500 		*arg_i = v;
501 		snd_mtxunlock(m->lock);
502 		return (v != -1)? 0 : ENXIO;
503 	}
504 	snd_mtxunlock(m->lock);
505 	return ENXIO;
506 }
507 
508 #ifdef USING_DEVFS
509 static void
510 mixer_clone(void *arg, struct ucred *cred, char *name, int namelen,
511     struct cdev **dev)
512 {
513 	struct snddev_info *sd;
514 
515 	if (*dev != NULL)
516 		return;
517 	if (strcmp(name, "mixer") == 0) {
518 		sd = devclass_get_softc(pcm_devclass, snd_unit);
519 		if (sd != NULL && sd->mixer_dev != NULL) {
520 			*dev = sd->mixer_dev;
521 			dev_ref(*dev);
522 		}
523 	}
524 }
525 
526 static void
527 mixer_sysinit(void *p)
528 {
529 	mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000);
530 }
531 
532 static void
533 mixer_sysuninit(void *p)
534 {
535 	if (mixer_ehtag != NULL)
536 		EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag);
537 }
538 
539 SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
540 SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
541 #endif
542 
543 
544