mixer.c revision 74763
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
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 * $FreeBSD: head/sys/dev/sound/pcm/mixer.c 74763 2001-03-24 23:10:29Z cg $
27 */
28
29#include <dev/sound/pcm/sound.h>
30
31#include "mixer_if.h"
32
33MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
34
35#define MIXER_NAMELEN	16
36struct 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	u_int32_t hwvol_mute_level;
45	u_int32_t devs;
46	u_int32_t recdevs;
47	u_int32_t recsrc;
48	u_int16_t level[32];
49	char name[MIXER_NAMELEN];
50	void *lock;
51};
52
53static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
54	[SOUND_MIXER_VOLUME]	= 75,
55	[SOUND_MIXER_BASS]	= 50,
56	[SOUND_MIXER_TREBLE]	= 50,
57	[SOUND_MIXER_SYNTH]	= 75,
58	[SOUND_MIXER_PCM]	= 75,
59	[SOUND_MIXER_SPEAKER]	= 75,
60	[SOUND_MIXER_LINE]	= 75,
61	[SOUND_MIXER_MIC] 	= 0,
62	[SOUND_MIXER_CD]	= 75,
63	[SOUND_MIXER_LINE1]	= 75,
64	[SOUND_MIXER_VIDEO]	= 75,
65	[SOUND_MIXER_RECLEV]	= 0,
66	[SOUND_MIXER_OGAIN]	= 50,
67};
68
69static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
70
71#ifdef SND_DYNSYSCTL
72static int
73mixer_lookup(char *devname)
74{
75	int i;
76
77	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
78		if (strncmp(devname, snd_mixernames[i],
79		    strlen(snd_mixernames[i])) == 0)
80			return i;
81	return -1;
82}
83#endif
84
85static int
86mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev)
87{
88	unsigned l, r;
89	int v;
90
91	if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev))))
92		return -1;
93
94	l = min((lev & 0x00ff), 100);
95	r = min(((lev & 0xff00) >> 8), 100);
96
97	v = MIXER_SET(mixer, dev, l, r);
98	if (v < 0)
99		return -1;
100
101	mixer->level[dev] = l | (r << 8);
102	return 0;
103}
104
105static int
106mixer_get(struct snd_mixer *mixer, int dev)
107{
108	if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
109		return mixer->level[dev];
110	else return -1;
111}
112
113static int
114mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
115{
116	src &= mixer->recdevs;
117	if (src == 0)
118		src = SOUND_MASK_MIC;
119	mixer->recsrc = MIXER_SETRECSRC(mixer, src);
120	return 0;
121}
122
123static int
124mixer_getrecsrc(struct snd_mixer *mixer)
125{
126	return mixer->recsrc;
127}
128
129void
130mix_setdevs(struct snd_mixer *m, u_int32_t v)
131{
132	m->devs = v;
133}
134
135void
136mix_setrecdevs(struct snd_mixer *m, u_int32_t v)
137{
138	m->recdevs = v;
139}
140
141u_int32_t
142mix_getdevs(struct snd_mixer *m)
143{
144	return m->devs;
145}
146
147u_int32_t
148mix_getrecdevs(struct snd_mixer *m)
149{
150	return m->recdevs;
151}
152
153void *
154mix_getdevinfo(struct snd_mixer *m)
155{
156	return m->devinfo;
157}
158
159int
160mixer_busy(struct snd_mixer *m, int busy)
161{
162	m->busy = busy;
163	return 0;
164}
165
166int
167mixer_isbusy(struct snd_mixer *m)
168{
169	return m->busy;
170}
171
172int
173mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
174{
175    	struct snddev_info *d;
176	struct snd_mixer *m;
177	u_int16_t v;
178	int i;
179
180	m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
181	snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev));
182	m->lock = snd_mtxcreate(m->name);
183	m->type = cls->name;
184	m->devinfo = devinfo;
185
186	if (MIXER_INIT(m))
187		goto bad;
188
189	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
190		v = snd_mixerdefaults[i];
191		mixer_set(m, i, v | (v << 8));
192	}
193
194	mixer_setrecsrc(m, SOUND_MASK_MIC);
195
196	d = device_get_softc(dev);
197	d->mixer = m;
198
199	return 0;
200
201bad:
202	snd_mtxlock(m->lock);
203	snd_mtxfree(m->lock);
204	kobj_delete((kobj_t)m, M_MIXER);
205	return -1;
206}
207
208int
209mixer_uninit(device_t dev)
210{
211	int i;
212    	struct snddev_info *d;
213	struct snd_mixer *m;
214
215	d = device_get_softc(dev);
216	m = d->mixer;
217	snd_mtxlock(m->lock);
218
219	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
220		mixer_set(m, i, 0);
221
222	mixer_setrecsrc(m, SOUND_MASK_MIC);
223
224	MIXER_UNINIT(m);
225
226	snd_mtxfree(m->lock);
227	kobj_delete((kobj_t)m, M_MIXER);
228	d->mixer = NULL;
229
230	return 0;
231}
232
233int
234mixer_reinit(device_t dev)
235{
236	int i;
237    	struct snddev_info *d;
238	struct snd_mixer *m;
239
240	d = device_get_softc(dev);
241	m = d->mixer;
242	snd_mtxlock(m->lock);
243
244	i = MIXER_REINIT(m);
245	if (i) {
246		snd_mtxunlock(m->lock);
247		return i;
248	}
249
250	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
251		mixer_set(m, i, m->level[i]);
252
253	mixer_setrecsrc(m, m->recsrc);
254	snd_mtxunlock(m->lock);
255
256	return 0;
257}
258
259int
260mixer_ioctl(struct snddev_info *d, u_long cmd, caddr_t arg)
261{
262	int ret, *arg_i = (int *)arg;
263	int v = -1, j = cmd & 0xff;
264	struct snd_mixer *m;
265
266	m = d->mixer;
267
268	snd_mtxlock(m->lock);
269	if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
270		if (j == SOUND_MIXER_RECSRC)
271			ret = mixer_setrecsrc(m, *arg_i);
272		else
273			ret = mixer_set(m, j, *arg_i);
274		snd_mtxunlock(m->lock);
275		return (ret == 0)? 0 : ENXIO;
276	}
277
278    	if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
279		switch (j) {
280    		case SOUND_MIXER_DEVMASK:
281    		case SOUND_MIXER_CAPS:
282    		case SOUND_MIXER_STEREODEVS:
283			v = mix_getdevs(m);
284			break;
285
286    		case SOUND_MIXER_RECMASK:
287			v = mix_getrecdevs(m);
288			break;
289
290    		case SOUND_MIXER_RECSRC:
291			v = mixer_getrecsrc(m);
292			break;
293
294		default:
295			v = mixer_get(m, j);
296		}
297		*arg_i = v;
298		snd_mtxunlock(m->lock);
299		return (v != -1)? 0 : ENXIO;
300	}
301	snd_mtxunlock(m->lock);
302	return ENXIO;
303}
304
305#ifdef SND_DYNSYSCTL
306static int
307sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
308{
309	char devname[32];
310	int error, dev;
311	struct snd_mixer *m;
312
313	m = oidp->oid_arg1;
314	snd_mtxlock(m->lock);
315	strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
316	error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
317	if (error == 0 && req->newptr != NULL) {
318		dev = mixer_lookup(devname);
319		if (dev == -1) {
320			snd_mtxunlock(m->lock);
321			return EINVAL;
322		}
323		else if (dev != m->hwvol_mixer) {
324			m->hwvol_mixer = dev;
325			m->hwvol_muted = 0;
326		}
327	}
328	snd_mtxunlock(m->lock);
329	return error;
330}
331#endif
332
333int
334mixer_hwvol_init(device_t dev)
335{
336    	struct snddev_info *d;
337	struct snd_mixer *m;
338
339	d = device_get_softc(dev);
340	m = d->mixer;
341	snd_mtxlock(m->lock);
342	m->hwvol_mixer = SOUND_MIXER_VOLUME;
343	m->hwvol_step = 5;
344#ifdef SND_DYNSYSCTL
345	SYSCTL_ADD_INT(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top),
346            OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
347	SYSCTL_ADD_PROC(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top),
348            OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
349	    sysctl_hw_snd_hwvol_mixer, "A", "")
350#endif
351	snd_mtxunlock(m->lock);
352	return 0;
353}
354
355void
356mixer_hwvol_mute(device_t dev)
357{
358    	struct snddev_info *d;
359	struct snd_mixer *m;
360
361	d = device_get_softc(dev);
362	m = d->mixer;
363	snd_mtxlock(m->lock);
364	if (m->hwvol_muted) {
365		m->hwvol_muted = 0;
366		mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
367	} else {
368		m->hwvol_muted++;
369		m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
370		mixer_set(m, m->hwvol_mixer, 0);
371	}
372	snd_mtxunlock(m->lock);
373}
374
375void
376mixer_hwvol_step(device_t dev, int left_step, int right_step)
377{
378    	struct snddev_info *d;
379	struct snd_mixer *m;
380	int level, left, right;
381
382	d = device_get_softc(dev);
383	m = d->mixer;
384	snd_mtxlock(m->lock);
385	if (m->hwvol_muted) {
386		m->hwvol_muted = 0;
387		level = m->hwvol_mute_level;
388	} else
389		level = mixer_get(m, m->hwvol_mixer);
390	if (level != -1) {
391		left = level & 0xff;
392		right = level >> 8;
393		left += left_step * m->hwvol_step;
394		if (left < 0)
395			left = 0;
396		right += right_step * m->hwvol_step;
397		if (right < 0)
398			right = 0;
399		mixer_set(m, m->hwvol_mixer, left | right << 8);
400	}
401	snd_mtxunlock(m->lock);
402}
403
404
405