kernel - Tear out device polling
[dragonfly.git] / sys / dev / sound / pcm / mixer.c
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  * $FreeBSD: src/sys/dev/sound/pcm/mixer.c,v 1.43.2.5 2007/05/13 20:53:39 ariff Exp $
27  * $DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.18 2007/06/22 21:41:16 corecode Exp $
28  */
29
30 #include <dev/sound/pcm/sound.h>
31 #include <dev/sound/pcm/dsp.h>
32
33 #include "mixer_if.h"
34
35 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.18 2007/06/22 21:41:16 corecode Exp $");
36
37 MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
38
39 #define MIXER_NAMELEN   16
40 struct snd_mixer {
41         KOBJ_FIELDS;
42         const char *type;
43         void *devinfo;
44         int busy;
45         int hwvol_muted;
46         int hwvol_mixer;
47         int hwvol_step;
48         device_t dev;
49         u_int32_t hwvol_mute_level;
50         u_int32_t devs;
51         u_int32_t recdevs;
52         u_int32_t recsrc;
53         u_int16_t level[32];
54         u_int8_t parent[32];
55         u_int32_t child[32];
56         u_int8_t realdev[32];
57         char name[MIXER_NAMELEN];
58         sndlock_t       lock;
59 };
60
61 static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
62         [SOUND_MIXER_VOLUME]    = 75,
63         [SOUND_MIXER_BASS]      = 50,
64         [SOUND_MIXER_TREBLE]    = 50,
65         [SOUND_MIXER_SYNTH]     = 75,
66         [SOUND_MIXER_PCM]       = 75,
67         [SOUND_MIXER_SPEAKER]   = 75,
68         [SOUND_MIXER_LINE]      = 75,
69         [SOUND_MIXER_MIC]       = 0,
70         [SOUND_MIXER_CD]        = 75,
71         [SOUND_MIXER_IGAIN]     = 0,
72         [SOUND_MIXER_LINE1]     = 75,
73         [SOUND_MIXER_VIDEO]     = 75,
74         [SOUND_MIXER_RECLEV]    = 0,
75         [SOUND_MIXER_OGAIN]     = 50,
76         [SOUND_MIXER_MONITOR]   = 75,
77 };
78
79 static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
80
81 static d_open_t mixer_open;
82 static d_close_t mixer_close;
83
84 static struct dev_ops mixer_cdevsw = {
85         { "mixer", SND_CDEV_MAJOR, D_TRACKCLOSE },
86         /* .d_flags =   D_TRACKCLOSE | D_NEEDGIANT, */
87         .d_open =       mixer_open,
88         .d_close =      mixer_close,
89         .d_ioctl =      mixer_ioctl,
90 };
91
92 #ifdef USING_DEVFS
93 static eventhandler_tag mixer_ehtag;
94 #endif
95
96 static struct cdev *
97 mixer_get_devt(device_t dev)
98 {
99         struct snddev_info *snddev;
100
101         snddev = device_get_softc(dev);
102
103         return snddev->mixer_dev;
104 }
105
106 #ifdef SND_DYNSYSCTL
107 static int
108 mixer_lookup(char *devname)
109 {
110         int i;
111
112         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
113                 if (strncmp(devname, snd_mixernames[i],
114                     strlen(snd_mixernames[i])) == 0)
115                         return i;
116         return -1;
117 }
118 #endif
119
120 /*
121  * Always called with mixer->lock held.
122  */
123 static int
124 mixer_set_softpcmvol(struct snd_mixer *mixer, struct snddev_info *d,
125                                                 unsigned left, unsigned right)
126 {
127         struct snddev_channel *sce;
128         struct pcm_channel *ch;
129         
130         snd_mtxunlock(mixer->lock);
131         SLIST_FOREACH(sce, &d->channels, link) {
132                 ch = sce->channel;
133                 CHN_LOCK(ch);
134                 if (ch->direction == PCMDIR_PLAY &&
135                                 (ch->feederflags & (1 << FEEDER_VOLUME)))
136                         chn_setvolume(ch, left, right);
137                 CHN_UNLOCK(ch);
138         }
139         snd_mtxlock(mixer->lock);
140         return 0;
141 }
142
143 static int
144 mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev)
145 {
146         struct snddev_info *d;
147         unsigned l, r, tl, tr;
148         u_int32_t parent = SOUND_MIXER_NONE, child = 0;
149         u_int32_t realdev;
150         int i;
151
152         if (m == NULL || dev >= SOUND_MIXER_NRDEVICES ||
153             (0 == (m->devs & (1 << dev))))
154                 return -1;
155
156         l = min((lev & 0x00ff), 100);
157         r = min(((lev & 0xff00) >> 8), 100);
158         realdev = m->realdev[dev];
159
160         d = device_get_softc(m->dev);
161         if (d == NULL)
162                 return -1;
163
164         /* TODO: recursive handling */
165         parent = m->parent[dev];
166         if (parent >= SOUND_MIXER_NRDEVICES)
167                 parent = SOUND_MIXER_NONE;
168         if (parent == SOUND_MIXER_NONE)
169                 child = m->child[dev];
170
171         if (parent != SOUND_MIXER_NONE) {
172                 tl = (l * (m->level[parent] & 0x00ff)) / 100;
173                 tr = (r * ((m->level[parent] & 0xff00) >> 8)) / 100;
174                 if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
175                         mixer_set_softpcmvol(m, d, tl, tr);
176                 else if (realdev != SOUND_MIXER_NONE &&
177                     MIXER_SET(m, realdev, tl, tr) < 0)
178                         return -1;
179         } else if (child != 0) {
180                 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
181                         if (!(child & (1 << i)) || m->parent[i] != dev)
182                                 continue;
183                         realdev = m->realdev[i];
184                         tl = (l * (m->level[i] & 0x00ff)) / 100;
185                         tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100;
186                         if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
187                                 mixer_set_softpcmvol(m, d, tl, tr);
188                         else if (realdev != SOUND_MIXER_NONE)
189                                 MIXER_SET(m, realdev, tl, tr);
190                 }
191                 realdev = m->realdev[dev];
192                 if (realdev != SOUND_MIXER_NONE &&
193                     MIXER_SET(m, realdev, l, r) < 0)
194                         return -1;
195         } else {
196                 if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL))
197                         mixer_set_softpcmvol(m, d, l, r);
198                 else if (realdev != SOUND_MIXER_NONE &&
199                     MIXER_SET(m, realdev, l, r) < 0)
200                         return -1;
201         }
202
203         m->level[dev] = l | (r << 8);
204
205         return 0;
206 }
207
208 static int
209 mixer_get(struct snd_mixer *mixer, int dev)
210 {
211         if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
212                 return mixer->level[dev];
213         else return -1;
214 }
215
216 static int
217 mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
218 {
219         src &= mixer->recdevs;
220         if (src == 0)
221                 src = SOUND_MASK_MIC;
222         mixer->recsrc = MIXER_SETRECSRC(mixer, src);
223         return 0;
224 }
225
226 static int
227 mixer_getrecsrc(struct snd_mixer *mixer)
228 {
229         return mixer->recsrc;
230 }
231
232 void
233 mix_setdevs(struct snd_mixer *m, u_int32_t v)
234 {
235         struct snddev_info *d;
236         int i;
237
238         if (m == NULL)
239                 return;
240
241         d = device_get_softc(m->dev);
242         if (d != NULL && (d->flags & SD_F_SOFTPCMVOL))
243                 v |= SOUND_MASK_PCM;
244         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
245                 if (m->parent[i] < SOUND_MIXER_NRDEVICES)
246                         v |= 1 << m->parent[i];
247                 v |= m->child[i];
248         }
249         m->devs = v;
250 }
251
252 void
253 mix_setrecdevs(struct snd_mixer *m, u_int32_t v)
254 {
255         m->recdevs = v;
256 }
257
258 void
259 mix_setparentchild(struct snd_mixer *m, u_int32_t parent, u_int32_t childs)
260 {
261         u_int32_t mask = 0;
262         int i;
263
264         if (m == NULL || parent >= SOUND_MIXER_NRDEVICES)
265                 return;
266         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
267                 if (i == parent)
268                         continue;
269                 if (childs & (1 << i)) {
270                         mask |= 1 << i;
271                         if (m->parent[i] < SOUND_MIXER_NRDEVICES)
272                                 m->child[m->parent[i]] &= ~(1 << i);
273                         m->parent[i] = parent;
274                         m->child[i] = 0;
275                 }
276         }
277         mask &= ~(1 << parent);
278         m->child[parent] = mask;
279 }
280
281 void
282 mix_setrealdev(struct snd_mixer *m, u_int32_t dev, u_int32_t realdev)
283 {
284         if (m == NULL || dev >= SOUND_MIXER_NRDEVICES ||
285             !(realdev == SOUND_MIXER_NONE || realdev < SOUND_MIXER_NRDEVICES))
286                 return;
287         m->realdev[dev] = realdev;
288 }
289
290 u_int32_t
291 mix_getparent(struct snd_mixer *m, u_int32_t dev)
292 {
293         if (m == NULL || dev >= SOUND_MIXER_NRDEVICES)
294                 return SOUND_MIXER_NONE;
295         return m->parent[dev];
296 }
297
298 u_int32_t
299 mix_getchild(struct snd_mixer *m, u_int32_t dev)
300 {
301         if (m == NULL || dev >= SOUND_MIXER_NRDEVICES)
302                 return 0;
303         return m->child[dev];
304 }
305
306 u_int32_t
307 mix_getdevs(struct snd_mixer *m)
308 {
309         return m->devs;
310 }
311
312 u_int32_t
313 mix_getrecdevs(struct snd_mixer *m)
314 {
315         return m->recdevs;
316 }
317
318 void *
319 mix_getdevinfo(struct snd_mixer *m)
320 {
321         return m->devinfo;
322 }
323
324 int
325 mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
326 {
327         struct snddev_info *snddev;
328         struct snd_mixer *m;
329         u_int16_t v;
330         struct cdev *pdev;
331         int i, unit, val;
332
333         m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
334         ksnprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev));
335         m->lock = snd_mtxcreate(m->name, "pcm mixer");
336         m->type = cls->name;
337         m->devinfo = devinfo;
338         m->busy = 0;
339         m->dev = dev;
340         for (i = 0; i < 32; i++) {
341                 m->parent[i] = SOUND_MIXER_NONE;
342                 m->child[i] = 0;
343                 m->realdev[i] = i;
344         }
345
346         if (MIXER_INIT(m))
347                 goto bad;
348
349         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
350                 v = snd_mixerdefaults[i];
351
352                 if (resource_int_value(device_get_name(dev),
353                     device_get_unit(dev), snd_mixernames[i], &val) == 0) {
354                         if (val >= 0 && val <= 100) {
355                                 v = (u_int16_t) val;
356                         }
357                 }
358
359                 snd_mtxlock(m->lock);
360                 mixer_set(m, i, v | (v << 8));
361                 snd_mtxunlock(m->lock);
362         }
363
364         mixer_setrecsrc(m, SOUND_MASK_MIC);
365
366         unit = device_get_unit(dev);
367         pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, 0),
368                         UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
369         reference_dev(pdev);
370         pdev->si_drv1 = m;
371         snddev = device_get_softc(dev);
372         snddev->mixer_dev = pdev;
373
374         if (bootverbose) {
375                 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
376                         if (!(m->devs & (1 << i)))
377                                 continue;
378                         if (m->realdev[i] != i) {
379                                 device_printf(dev, "Mixer \"%s\" -> \"%s\":",
380                                     snd_mixernames[i],
381                                     (m->realdev[i] < SOUND_MIXER_NRDEVICES) ?
382                                     snd_mixernames[m->realdev[i]] : "none");
383                         } else {
384                                 device_printf(dev, "Mixer \"%s\":",
385                                     snd_mixernames[i]);
386                         }
387                         if (m->parent[i] < SOUND_MIXER_NRDEVICES)
388                                 kprintf(" parent=\"%s\"",
389                                     snd_mixernames[m->parent[i]]);
390                         if (m->child[i] != 0)
391                                 kprintf(" child=0x%08x", m->child[i]);
392                         kprintf("\n");
393                 }
394                 if (snddev->flags & SD_F_SOFTPCMVOL)
395                         device_printf(dev, "Soft PCM mixer ENABLED\n");
396         }
397
398         return 0;
399
400 bad:
401         snd_mtxfree(m->lock);
402         kobj_delete((kobj_t)m, M_MIXER);
403         return -1;
404 }
405
406 int
407 mixer_uninit(device_t dev)
408 {
409         int i;
410         int unit;
411         struct snddev_info *d;
412         struct snd_mixer *m;
413         struct cdev *pdev;
414
415         d = device_get_softc(dev);
416         pdev = mixer_get_devt(dev);
417         if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL)
418                 return EBADF;
419         m = pdev->si_drv1;
420         snd_mtxlock(m->lock);
421
422         if (m->busy) {
423                 snd_mtxunlock(m->lock);
424                 return EBUSY;
425         }
426
427         pdev->si_drv1 = NULL;
428         release_dev(pdev);
429         unit = device_get_unit(dev);
430         dev_ops_remove_minor(&mixer_cdevsw, /*-1, */PCMMKMINOR(unit, 0));
431
432         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
433                 mixer_set(m, i, 0);
434
435         mixer_setrecsrc(m, SOUND_MASK_MIC);
436
437         MIXER_UNINIT(m);
438
439         snd_mtxunlock(m->lock);
440         snd_mtxfree(m->lock);
441         kobj_delete((kobj_t)m, M_MIXER);
442
443         d->mixer_dev = NULL;
444
445         return 0;
446 }
447
448 int
449 mixer_reinit(device_t dev)
450 {
451         struct snd_mixer *m;
452         struct cdev *pdev;
453         int i;
454
455         pdev = mixer_get_devt(dev);
456         m = pdev->si_drv1;
457         snd_mtxlock(m->lock);
458
459         i = MIXER_REINIT(m);
460         if (i) {
461                 snd_mtxunlock(m->lock);
462                 return i;
463         }
464
465         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
466                 mixer_set(m, i, m->level[i]);
467
468         mixer_setrecsrc(m, m->recsrc);
469         snd_mtxunlock(m->lock);
470
471         return 0;
472 }
473
474 #ifdef SND_DYNSYSCTL
475 static int
476 sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
477 {
478         char devname[32];
479         int error, dev;
480         struct snd_mixer *m;
481
482         m = oidp->oid_arg1;
483         snd_mtxlock(m->lock);
484         strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
485         snd_mtxunlock(m->lock);
486         error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
487         snd_mtxlock(m->lock);
488         if (error == 0 && req->newptr != NULL) {
489                 dev = mixer_lookup(devname);
490                 if (dev == -1) {
491                         snd_mtxunlock(m->lock);
492                         return EINVAL;
493                 }
494                 else if (dev != m->hwvol_mixer) {
495                         m->hwvol_mixer = dev;
496                         m->hwvol_muted = 0;
497                 }
498         }
499         snd_mtxunlock(m->lock);
500         return error;
501 }
502 #endif
503
504 int
505 mixer_hwvol_init(device_t dev)
506 {
507         struct snd_mixer *m;
508         struct cdev *pdev;
509
510         pdev = mixer_get_devt(dev);
511         m = pdev->si_drv1;
512
513         m->hwvol_mixer = SOUND_MIXER_VOLUME;
514         m->hwvol_step = 5;
515 #ifdef SND_DYNSYSCTL
516         SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
517             OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
518         SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
519             OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
520             sysctl_hw_snd_hwvol_mixer, "A", "");
521 #endif
522         return 0;
523 }
524
525 void
526 mixer_hwvol_mute(device_t dev)
527 {
528         struct snd_mixer *m;
529         struct cdev *pdev;
530
531         pdev = mixer_get_devt(dev);
532         m = pdev->si_drv1;
533         snd_mtxlock(m->lock);
534         if (m->hwvol_muted) {
535                 m->hwvol_muted = 0;
536                 mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
537         } else {
538                 m->hwvol_muted++;
539                 m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
540                 mixer_set(m, m->hwvol_mixer, 0);
541         }
542         snd_mtxunlock(m->lock);
543 }
544
545 void
546 mixer_hwvol_step(device_t dev, int left_step, int right_step)
547 {
548         struct snd_mixer *m;
549         int level, left, right;
550         struct cdev *pdev;
551
552         pdev = mixer_get_devt(dev);
553         m = pdev->si_drv1;
554         snd_mtxlock(m->lock);
555         if (m->hwvol_muted) {
556                 m->hwvol_muted = 0;
557                 level = m->hwvol_mute_level;
558         } else
559                 level = mixer_get(m, m->hwvol_mixer);
560         if (level != -1) {
561                 left = level & 0xff;
562                 right = level >> 8;
563                 left += left_step * m->hwvol_step;
564                 if (left < 0)
565                         left = 0;
566                 right += right_step * m->hwvol_step;
567                 if (right < 0)
568                         right = 0;
569                 mixer_set(m, m->hwvol_mixer, left | right << 8);
570         }
571         snd_mtxunlock(m->lock);
572 }
573
574 /* ----------------------------------------------------------------------- */
575
576 static int
577 vchanvolume(cdev_t i_dev, int write, int *volume, int *ret,
578                 struct thread *td)
579 {
580         struct snddev_info *d;
581         void *cookie;
582         struct pcm_channel *ch;
583         int vol_left, vol_right;
584
585         crit_enter();
586         d = dsp_get_info(i_dev);
587         if (d == NULL) {
588                 crit_exit();
589                 return 0;
590         }
591         /*
592          * We search for a vchan which is owned by the current process.
593          */
594         for (cookie = NULL; (ch = pcm_chn_iterate(d, &cookie)) != NULL;)
595                 if (ch->flags & CHN_F_VIRTUAL &&
596                     ch->pid == td->td_proc->p_pid)
597                         break;
598
599         if (ch == NULL) {
600                 crit_exit();
601                 return 0;
602         }
603
604         if (write) {
605                 vol_left = min(*volume & 0x00ff, 100);
606                 vol_right = min((*volume & 0xff00) >> 8, 100);
607                 *ret = chn_setvolume(ch, vol_left, vol_right);
608         } else {
609                 *volume = ch->volume;
610                 *ret = 0;
611         }
612         crit_exit();
613         return 1;
614 }
615
616 /* ----------------------------------------------------------------------- */
617
618 static int
619 mixer_open(struct dev_open_args *ap)
620 {
621         struct cdev *i_dev = ap->a_head.a_dev;
622         struct snd_mixer *m;
623
624         m = i_dev->si_drv1;
625         snd_mtxlock(m->lock);
626
627         m->busy++;
628
629         snd_mtxunlock(m->lock);
630         return 0;
631 }
632
633 static int
634 mixer_close(struct dev_close_args *ap)
635 {
636         struct cdev *i_dev = ap->a_head.a_dev;
637         struct snd_mixer *m;
638
639         m = i_dev->si_drv1;
640         snd_mtxlock(m->lock);
641
642         if (!m->busy) {
643                 snd_mtxunlock(m->lock);
644                 return EBADF;
645         }
646         m->busy--;
647
648         snd_mtxunlock(m->lock);
649         return 0;
650 }
651
652 int
653 mixer_ioctl(struct dev_ioctl_args *ap)
654 {
655         struct cdev *i_dev = ap->a_head.a_dev;
656         u_long cmd = ap->a_cmd;
657         caddr_t arg = ap->a_data;
658         int mode = ap->a_fflag;
659         struct snd_mixer *m;
660         int ret, *arg_i = (int *)arg;
661         int v = -1, j = cmd & 0xff;
662
663         m = i_dev->si_drv1;
664
665         if (m == NULL)
666                 return EBADF;
667
668         /*
669          * If we are handling PCM, maybe the app really wants to
670          * set its vchan, and fails to use the correct fd.
671          */
672         if (j == SOUND_MIXER_PCM && curthread->td_proc) {
673                 if (vchanvolume(i_dev,
674                             (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0),
675                             (int *)arg, &ret, curthread))
676                         return ret;
677                 /* else proceed as usual */
678         }
679
680         snd_mtxlock(m->lock);
681         if (mode != -1 && !m->busy) {
682                 snd_mtxunlock(m->lock);
683                 return EBADF;
684         }
685
686         if (cmd == OSS_GETVERSION) {
687                 *arg_i = SOUND_VERSION;
688                 snd_mtxunlock(m->lock);
689                 return 0;
690         }
691
692         if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
693                 if (j == SOUND_MIXER_RECSRC)
694                         ret = mixer_setrecsrc(m, *arg_i);
695                 else
696                         ret = mixer_set(m, j, *arg_i);
697                 snd_mtxunlock(m->lock);
698                 return (ret == 0)? 0 : ENXIO;
699         }
700
701         if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
702                 switch (j) {
703                 case SOUND_MIXER_DEVMASK:
704                 case SOUND_MIXER_CAPS:
705                 case SOUND_MIXER_STEREODEVS:
706                         v = mix_getdevs(m);
707                         break;
708
709                 case SOUND_MIXER_RECMASK:
710                         v = mix_getrecdevs(m);
711                         break;
712
713                 case SOUND_MIXER_RECSRC:
714                         v = mixer_getrecsrc(m);
715                         break;
716
717                 default:
718                         v = mixer_get(m, j);
719                 }
720                 *arg_i = v;
721                 snd_mtxunlock(m->lock);
722                 return (v != -1)? 0 : ENXIO;
723         }
724         snd_mtxunlock(m->lock);
725         return ENXIO;
726 }
727
728 #ifdef USING_DEVFS
729 static void
730 mixer_clone(void *arg, struct ucred *cred, char *name, int namelen,
731     struct cdev **dev)
732 {
733         struct snddev_info *sd;
734
735         if (*dev != NULL)
736                 return;
737         if (strcmp(name, "mixer") == 0) {
738                 sd = devclass_get_softc(pcm_devclass, snd_unit);
739                 if (sd != NULL && sd->mixer_dev != NULL) {
740                         *dev = sd->mixer_dev;
741                         dev_ref(*dev);
742                 }
743         }
744 }
745
746 static void
747 mixer_sysinit(void *p)
748 {
749         mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000);
750 }
751
752 static void
753 mixer_sysuninit(void *p)
754 {
755         if (mixer_ehtag != NULL)
756                 EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag);
757 }
758
759 SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
760 SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
761 #endif
762
763