File:
[DragonFly] /
src /
sys /
dev /
sound /
pcm /
mixer.c
Revision
1.5:
download - view:
text,
annotated -
select for diffs
Thu May 13 23:49:21 2004 UTC (9 years ago) by
dillon
Branches:
MAIN
CVS tags:
HEAD
device switch 1/many: Remove d_autoq, add d_clone (where d_autoq was).
d_autoq was used to allow the device port dispatch to mix old-style synchronous
calls with new style messaging calls within a particular device. It was never
used for that purpose.
d_clone will be more fully implemented as work continues. We are going to
install d_port in the dev_t (struct specinfo) structure itself and d_clone
will be needed to allow devices to 'revector' the port on a minor-number
by minor-number basis, in particular allowing minor numbers to be directly
dispatched to distinct threads. This is something we will be needing later
on.
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: src/sys/dev/sound/pcm/mixer.c,v 1.4.2.8 2002/04/22 15:49:36 cg Exp $
27: * $DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.5 2004/05/13 23:49:21 dillon Exp $
28: */
29:
30: #include <dev/sound/pcm/sound.h>
31:
32: #include "mixer_if.h"
33:
34: SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.5 2004/05/13 23:49:21 dillon Exp $");
35:
36: MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
37:
38: #define MIXER_NAMELEN 16
39: struct snd_mixer {
40: KOBJ_FIELDS;
41: const char *type;
42: void *devinfo;
43: int busy;
44: int hwvol_muted;
45: int hwvol_mixer;
46: int hwvol_step;
47: u_int32_t hwvol_mute_level;
48: u_int32_t devs;
49: u_int32_t recdevs;
50: u_int32_t recsrc;
51: u_int16_t level[32];
52: char name[MIXER_NAMELEN];
53: void *lock;
54: };
55:
56: static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
57: [SOUND_MIXER_VOLUME] = 75,
58: [SOUND_MIXER_BASS] = 50,
59: [SOUND_MIXER_TREBLE] = 50,
60: [SOUND_MIXER_SYNTH] = 75,
61: [SOUND_MIXER_PCM] = 75,
62: [SOUND_MIXER_SPEAKER] = 75,
63: [SOUND_MIXER_LINE] = 75,
64: [SOUND_MIXER_MIC] = 0,
65: [SOUND_MIXER_CD] = 75,
66: [SOUND_MIXER_LINE1] = 75,
67: [SOUND_MIXER_VIDEO] = 75,
68: [SOUND_MIXER_RECLEV] = 0,
69: [SOUND_MIXER_OGAIN] = 50,
70: [SOUND_MIXER_MONITOR] = 75,
71: };
72:
73: static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
74:
75: static d_open_t mixer_open;
76: static d_close_t mixer_close;
77:
78: static struct cdevsw mixer_cdevsw = {
79: /* name */ "mixer",
80: /* maj */ SND_CDEV_MAJOR,
81: /* flags */ 0,
82: /* port */ NULL,
83: /* clone */ NULL,
84:
85: /* open */ mixer_open,
86: /* close */ mixer_close,
87: /* read */ noread,
88: /* write */ nowrite,
89: /* ioctl */ mixer_ioctl,
90: /* poll */ nopoll,
91: /* mmap */ nommap,
92: /* strategy */ nostrategy,
93: /* dump */ nodump,
94: /* psize */ nopsize
95: };
96:
97: #ifdef USING_DEVFS
98: static eventhandler_tag mixer_ehtag;
99: #endif
100:
101: static dev_t
102: mixer_get_devt(device_t dev)
103: {
104: dev_t pdev;
105: int unit;
106:
107: unit = device_get_unit(dev);
108: pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0));
109:
110: return pdev;
111: }
112:
113: #ifdef SND_DYNSYSCTL
114: static int
115: mixer_lookup(char *devname)
116: {
117: int i;
118:
119: for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
120: if (strncmp(devname, snd_mixernames[i],
121: strlen(snd_mixernames[i])) == 0)
122: return i;
123: return -1;
124: }
125: #endif
126:
127: static int
128: mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev)
129: {
130: unsigned l, r;
131: int v;
132:
133: if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev))))
134: return -1;
135:
136: l = min((lev & 0x00ff), 100);
137: r = min(((lev & 0xff00) >> 8), 100);
138:
139: v = MIXER_SET(mixer, dev, l, r);
140: if (v < 0)
141: return -1;
142:
143: mixer->level[dev] = l | (r << 8);
144: return 0;
145: }
146:
147: static int
148: mixer_get(struct snd_mixer *mixer, int dev)
149: {
150: if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
151: return mixer->level[dev];
152: else return -1;
153: }
154:
155: static int
156: mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
157: {
158: src &= mixer->recdevs;
159: if (src == 0)
160: src = SOUND_MASK_MIC;
161: mixer->recsrc = MIXER_SETRECSRC(mixer, src);
162: return 0;
163: }
164:
165: static int
166: mixer_getrecsrc(struct snd_mixer *mixer)
167: {
168: return mixer->recsrc;
169: }
170:
171: void
172: mix_setdevs(struct snd_mixer *m, u_int32_t v)
173: {
174: m->devs = v;
175: }
176:
177: void
178: mix_setrecdevs(struct snd_mixer *m, u_int32_t v)
179: {
180: m->recdevs = v;
181: }
182:
183: u_int32_t
184: mix_getdevs(struct snd_mixer *m)
185: {
186: return m->devs;
187: }
188:
189: u_int32_t
190: mix_getrecdevs(struct snd_mixer *m)
191: {
192: return m->recdevs;
193: }
194:
195: void *
196: mix_getdevinfo(struct snd_mixer *m)
197: {
198: return m->devinfo;
199: }
200:
201: int
202: mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
203: {
204: struct snd_mixer *m;
205: u_int16_t v;
206: dev_t pdev;
207: int i, unit;
208:
209: m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
210: snprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev));
211: m->lock = snd_mtxcreate(m->name, "pcm mixer");
212: m->type = cls->name;
213: m->devinfo = devinfo;
214: m->busy = 0;
215:
216: if (MIXER_INIT(m))
217: goto bad;
218:
219: for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
220: v = snd_mixerdefaults[i];
221: mixer_set(m, i, v | (v << 8));
222: }
223:
224: mixer_setrecsrc(m, SOUND_MASK_MIC);
225:
226: unit = device_get_unit(dev);
227: pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
228: UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
229: pdev->si_drv1 = m;
230:
231: return 0;
232:
233: bad:
234: snd_mtxlock(m->lock);
235: snd_mtxfree(m->lock);
236: kobj_delete((kobj_t)m, M_MIXER);
237: return -1;
238: }
239:
240: int
241: mixer_uninit(device_t dev)
242: {
243: int i;
244: struct snd_mixer *m;
245: dev_t pdev;
246:
247: pdev = mixer_get_devt(dev);
248: m = pdev->si_drv1;
249: snd_mtxlock(m->lock);
250:
251: if (m->busy) {
252: snd_mtxunlock(m->lock);
253: return EBUSY;
254: }
255:
256: pdev->si_drv1 = NULL;
257: destroy_dev(pdev);
258:
259: for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
260: mixer_set(m, i, 0);
261:
262: mixer_setrecsrc(m, SOUND_MASK_MIC);
263:
264: MIXER_UNINIT(m);
265:
266: snd_mtxfree(m->lock);
267: kobj_delete((kobj_t)m, M_MIXER);
268:
269: return 0;
270: }
271:
272: int
273: mixer_reinit(device_t dev)
274: {
275: struct snd_mixer *m;
276: dev_t pdev;
277: int i;
278:
279: pdev = mixer_get_devt(dev);
280: m = pdev->si_drv1;
281: snd_mtxlock(m->lock);
282:
283: i = MIXER_REINIT(m);
284: if (i) {
285: snd_mtxunlock(m->lock);
286: return i;
287: }
288:
289: for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
290: mixer_set(m, i, m->level[i]);
291:
292: mixer_setrecsrc(m, m->recsrc);
293: snd_mtxunlock(m->lock);
294:
295: return 0;
296: }
297:
298: #ifdef SND_DYNSYSCTL
299: static int
300: sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
301: {
302: char devname[32];
303: int error, dev;
304: struct snd_mixer *m;
305:
306: m = oidp->oid_arg1;
307: snd_mtxlock(m->lock);
308: strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
309: error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
310: if (error == 0 && req->newptr != NULL) {
311: dev = mixer_lookup(devname);
312: if (dev == -1) {
313: snd_mtxunlock(m->lock);
314: return EINVAL;
315: }
316: else if (dev != m->hwvol_mixer) {
317: m->hwvol_mixer = dev;
318: m->hwvol_muted = 0;
319: }
320: }
321: snd_mtxunlock(m->lock);
322: return error;
323: }
324: #endif
325:
326: int
327: mixer_hwvol_init(device_t dev)
328: {
329: struct snd_mixer *m;
330: dev_t pdev;
331:
332: pdev = mixer_get_devt(dev);
333: m = pdev->si_drv1;
334: snd_mtxlock(m->lock);
335:
336: m->hwvol_mixer = SOUND_MIXER_VOLUME;
337: m->hwvol_step = 5;
338: #ifdef SND_DYNSYSCTL
339: SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
340: OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
341: SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
342: OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
343: sysctl_hw_snd_hwvol_mixer, "A", "")
344: #endif
345: snd_mtxunlock(m->lock);
346: return 0;
347: }
348:
349: void
350: mixer_hwvol_mute(device_t dev)
351: {
352: struct snd_mixer *m;
353: dev_t pdev;
354:
355: pdev = mixer_get_devt(dev);
356: m = pdev->si_drv1;
357: snd_mtxlock(m->lock);
358: if (m->hwvol_muted) {
359: m->hwvol_muted = 0;
360: mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
361: } else {
362: m->hwvol_muted++;
363: m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
364: mixer_set(m, m->hwvol_mixer, 0);
365: }
366: snd_mtxunlock(m->lock);
367: }
368:
369: void
370: mixer_hwvol_step(device_t dev, int left_step, int right_step)
371: {
372: struct snd_mixer *m;
373: int level, left, right;
374: dev_t pdev;
375:
376: pdev = mixer_get_devt(dev);
377: m = pdev->si_drv1;
378: snd_mtxlock(m->lock);
379: if (m->hwvol_muted) {
380: m->hwvol_muted = 0;
381: level = m->hwvol_mute_level;
382: } else
383: level = mixer_get(m, m->hwvol_mixer);
384: if (level != -1) {
385: left = level & 0xff;
386: right = level >> 8;
387: left += left_step * m->hwvol_step;
388: if (left < 0)
389: left = 0;
390: right += right_step * m->hwvol_step;
391: if (right < 0)
392: right = 0;
393: mixer_set(m, m->hwvol_mixer, left | right << 8);
394: }
395: snd_mtxunlock(m->lock);
396: }
397:
398: /* ----------------------------------------------------------------------- */
399:
400: static int
401: mixer_open(dev_t i_dev, int flags, int mode, struct thread *td)
402: {
403: struct snd_mixer *m;
404: intrmask_t s;
405:
406: m = i_dev->si_drv1;
407: s = spltty();
408: snd_mtxlock(m->lock);
409:
410: m->busy++;
411:
412: snd_mtxunlock(m->lock);
413: splx(s);
414: return 0;
415: }
416:
417: static int
418: mixer_close(dev_t i_dev, int flags, int mode, struct thread *td)
419: {
420: struct snd_mixer *m;
421: intrmask_t s;
422:
423: m = i_dev->si_drv1;
424: s = spltty();
425: snd_mtxlock(m->lock);
426:
427: if (!m->busy) {
428: snd_mtxunlock(m->lock);
429: splx(s);
430: return EBADF;
431: }
432: m->busy--;
433:
434: snd_mtxunlock(m->lock);
435: splx(s);
436: return 0;
437: }
438:
439: int
440: mixer_ioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
441: {
442: struct snd_mixer *m;
443: intrmask_t s;
444: int ret, *arg_i = (int *)arg;
445: int v = -1, j = cmd & 0xff;
446:
447: m = i_dev->si_drv1;
448: if (!m->busy)
449: return EBADF;
450:
451: s = spltty();
452: snd_mtxlock(m->lock);
453: if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
454: if (j == SOUND_MIXER_RECSRC)
455: ret = mixer_setrecsrc(m, *arg_i);
456: else
457: ret = mixer_set(m, j, *arg_i);
458: snd_mtxunlock(m->lock);
459: splx(s);
460: return (ret == 0)? 0 : ENXIO;
461: }
462:
463: if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
464: switch (j) {
465: case SOUND_MIXER_DEVMASK:
466: case SOUND_MIXER_CAPS:
467: case SOUND_MIXER_STEREODEVS:
468: v = mix_getdevs(m);
469: break;
470:
471: case SOUND_MIXER_RECMASK:
472: v = mix_getrecdevs(m);
473: break;
474:
475: case SOUND_MIXER_RECSRC:
476: v = mixer_getrecsrc(m);
477: break;
478:
479: default:
480: v = mixer_get(m, j);
481: }
482: *arg_i = v;
483: snd_mtxunlock(m->lock);
484: return (v != -1)? 0 : ENXIO;
485: }
486: snd_mtxunlock(m->lock);
487: splx(s);
488: return ENXIO;
489: }
490:
491: #ifdef USING_DEVFS
492: static void
493: mixer_clone(void *arg, char *name, int namelen, dev_t *dev)
494: {
495: dev_t pdev;
496:
497: if (*dev != NODEV)
498: return;
499: if (strcmp(name, "mixer") == 0) {
500: pdev = makedev(SND_CDEV_MAJOR, PCMMKMINOR(snd_unit, SND_DEV_CTL, 0));
501: if (pdev->si_flags & SI_NAMED)
502: *dev = pdev;
503: }
504: }
505:
506: static void
507: mixer_sysinit(void *p)
508: {
509: mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000);
510: }
511:
512: static void
513: mixer_sysuninit(void *p)
514: {
515: if (mixer_ehtag != NULL)
516: EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag);
517: }
518:
519: SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
520: SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
521: #endif
522:
523: