fb0a6cf9a5c2eb89cbc8aee720eba402b038148f
[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.17 2007/06/16 20:07:22 dillon 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.17 2007/06/16 20:07:22 dillon 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         dev_ops_add(&mixer_cdevsw, -1, PCMMKMINOR(unit, SND_DEV_CTL, 0));
368         pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0),
369                  UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit);
370         reference_dev(pdev);
371         pdev->si_drv1 = m;
372         snddev = device_get_softc(dev);
373         snddev->mixer_dev = pdev;
374
375         if (bootverbose) {
376                 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
377                         if (!(m->devs & (1 << i)))
378                                 continue;
379                         if (m->realdev[i] != i) {
380                                 device_printf(dev, "Mixer \"%s\" -> \"%s\":",
381                                     snd_mixernames[i],
382                                     (m->realdev[i] < SOUND_MIXER_NRDEVICES) ?
383                                     snd_mixernames[m->realdev[i]] : "none");
384                         } else {
385                                 device_printf(dev, "Mixer \"%s\":",
386                                     snd_mixernames[i]);
387                         }
388                         if (m->parent[i] < SOUND_MIXER_NRDEVICES)
389                                 kprintf(" parent=\"%s\"",
390                                     snd_mixernames[m->parent[i]]);
391                         if (m->child[i] != 0)
392                                 kprintf(" child=0x%08x", m->child[i]);
393                         kprintf("\n");
394                 }
395                 if (snddev->flags & SD_F_SOFTPCMVOL)
396                         device_printf(dev, "Soft PCM mixer ENABLED\n");
397         }
398
399         return 0;
400
401 bad:
402         snd_mtxfree(m->lock);
403         kobj_delete((kobj_t)m, M_MIXER);
404         return -1;
405 }
406
407 int
408 mixer_uninit(device_t dev)
409 {
410         int i;
411         int unit;
412         struct snddev_info *d;
413         struct snd_mixer *m;
414         struct cdev *pdev;
415
416         d = device_get_softc(dev);
417         pdev = mixer_get_devt(dev);
418         if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL)
419                 return EBADF;
420         m = pdev->si_drv1;
421         snd_mtxlock(m->lock);
422
423         if (m->busy) {
424                 snd_mtxunlock(m->lock);
425                 return EBUSY;
426         }
427
428         pdev->si_drv1 = NULL;
429         release_dev(pdev);
430         unit = device_get_unit(dev);
431         dev_ops_remove(&mixer_cdevsw, -1, PCMMKMINOR(unit, SND_DEV_CTL, 0));
432
433         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
434                 mixer_set(m, i, 0);
435
436         mixer_setrecsrc(m, SOUND_MASK_MIC);
437
438         MIXER_UNINIT(m);
439
440         snd_mtxunlock(m->lock);
441         snd_mtxfree(m->lock);
442         kobj_delete((kobj_t)m, M_MIXER);
443
444         d->mixer_dev = NULL;
445
446         return 0;
447 }
448
449 int
450 mixer_reinit(device_t dev)
451 {
452         struct snd_mixer *m;
453         struct cdev *pdev;
454         int i;
455
456         pdev = mixer_get_devt(dev);
457         m = pdev->si_drv1;
458         snd_mtxlock(m->lock);
459
460         i = MIXER_REINIT(m);
461         if (i) {
462                 snd_mtxunlock(m->lock);
463                 return i;
464         }
465
466         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
467                 mixer_set(m, i, m->level[i]);
468
469         mixer_setrecsrc(m, m->recsrc);
470         snd_mtxunlock(m->lock);
471
472         return 0;
473 }
474
475 #ifdef SND_DYNSYSCTL
476 static int
477 sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
478 {
479         char devname[32];
480         int error, dev;
481         struct snd_mixer *m;
482
483         m = oidp->oid_arg1;
484         snd_mtxlock(m->lock);
485         strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
486         snd_mtxunlock(m->lock);
487         error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
488         snd_mtxlock(m->lock);
489         if (error == 0 && req->newptr != NULL) {
490                 dev = mixer_lookup(devname);
491                 if (dev == -1) {
492                         snd_mtxunlock(m->lock);
493                         return EINVAL;
494                 }
495                 else if (dev != m->hwvol_mixer) {
496                         m->hwvol_mixer = dev;
497                         m->hwvol_muted = 0;
498                 }
499         }
500         snd_mtxunlock(m->lock);
501         return error;
502 }
503 #endif
504
505 int
506 mixer_hwvol_init(device_t dev)
507 {
508         struct snd_mixer *m;
509         struct cdev *pdev;
510
511         pdev = mixer_get_devt(dev);
512         m = pdev->si_drv1;
513
514         m->hwvol_mixer = SOUND_MIXER_VOLUME;
515         m->hwvol_step = 5;
516 #ifdef SND_DYNSYSCTL
517         SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
518             OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
519         SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
520             OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
521             sysctl_hw_snd_hwvol_mixer, "A", "");
522 #endif
523         return 0;
524 }
525
526 void
527 mixer_hwvol_mute(device_t dev)
528 {
529         struct snd_mixer *m;
530         struct cdev *pdev;
531
532         pdev = mixer_get_devt(dev);
533         m = pdev->si_drv1;
534         snd_mtxlock(m->lock);
535         if (m->hwvol_muted) {
536                 m->hwvol_muted = 0;
537                 mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level);
538         } else {
539                 m->hwvol_muted++;
540                 m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer);
541                 mixer_set(m, m->hwvol_mixer, 0);
542         }
543         snd_mtxunlock(m->lock);
544 }
545
546 void
547 mixer_hwvol_step(device_t dev, int left_step, int right_step)
548 {
549         struct snd_mixer *m;
550         int level, left, right;
551         struct cdev *pdev;
552
553         pdev = mixer_get_devt(dev);
554         m = pdev->si_drv1;
555         snd_mtxlock(m->lock);
556         if (m->hwvol_muted) {
557                 m->hwvol_muted = 0;
558                 level = m->hwvol_mute_level;
559         } else
560                 level = mixer_get(m, m->hwvol_mixer);
561         if (level != -1) {
562                 left = level & 0xff;
563                 right = level >> 8;
564                 left += left_step * m->hwvol_step;
565                 if (left < 0)
566                         left = 0;
567                 right += right_step * m->hwvol_step;
568                 if (right < 0)
569                         right = 0;
570                 mixer_set(m, m->hwvol_mixer, left | right << 8);
571         }
572         snd_mtxunlock(m->lock);
573 }
574
575 /* ----------------------------------------------------------------------- */
576
577 static int
578 vchanvolume(cdev_t i_dev, int write, int *volume, int *ret,
579                 struct thread *td)
580 {
581         struct snddev_info *d;
582         void *cookie;
583         struct pcm_channel *ch;
584         int vol_left, vol_right;
585
586         crit_enter();
587         d = dsp_get_info(i_dev);
588         if (d == NULL) {
589                 crit_exit();
590                 return 0;
591         }
592         /*
593          * We search for a vchan which is owned by the current process.
594          */
595         for (cookie = NULL; (ch = pcm_chn_iterate(d, &cookie)) != NULL;)
596                 if (ch->flags & CHN_F_VIRTUAL &&
597                     ch->pid == td->td_proc->p_pid)
598                         break;
599
600         if (ch == NULL) {
601                 crit_exit();
602                 return 0;
603         }
604
605         if (write) {
606                 vol_left = min(*volume & 0x00ff, 100);
607                 vol_right = min((*volume & 0xff00) >> 8, 100);
608                 *ret = chn_setvolume(ch, vol_left, vol_right);
609         } else {
610                 *volume = ch->volume;
611                 *ret = 0;
612         }
613         crit_exit();
614         return 1;
615 }
616
617 /* ----------------------------------------------------------------------- */
618
619 static int
620 mixer_open(struct dev_open_args *ap)
621 {
622         struct cdev *i_dev = ap->a_head.a_dev;
623         struct snd_mixer *m;
624
625         m = i_dev->si_drv1;
626         snd_mtxlock(m->lock);
627
628         m->busy++;
629
630         snd_mtxunlock(m->lock);
631         return 0;
632 }
633
634 static int
635 mixer_close(struct dev_close_args *ap)
636 {
637         struct cdev *i_dev = ap->a_head.a_dev;
638         struct snd_mixer *m;
639
640         m = i_dev->si_drv1;
641         snd_mtxlock(m->lock);
642
643         if (!m->busy) {
644                 snd_mtxunlock(m->lock);
645                 return EBADF;
646         }
647         m->busy--;
648
649         snd_mtxunlock(m->lock);
650         return 0;
651 }
652
653 int
654 mixer_ioctl(struct dev_ioctl_args *ap)
655 {
656         struct cdev *i_dev = ap->a_head.a_dev;
657         u_long cmd = ap->a_cmd;
658         caddr_t arg = ap->a_data;
659         int mode = ap->a_fflag;
660         struct snd_mixer *m;
661         int ret, *arg_i = (int *)arg;
662         int v = -1, j = cmd & 0xff;
663
664         m = i_dev->si_drv1;
665
666         if (m == NULL)
667                 return EBADF;
668
669         /*
670          * If we are handling PCM, maybe the app really wants to
671          * set its vchan, and fails to use the correct fd.
672          */
673         if (j == SOUND_MIXER_PCM) {
674                 cdev_t pdev;
675
676                 if (vchanvolume(i_dev,
677                             (cmd & MIXER_WRITE(0)) == MIXER_WRITE(0),
678                             (int *)arg, &ret, curthread))
679                         return ret;
680                 /* else proceed as usual */
681         }
682
683         snd_mtxlock(m->lock);
684         if (mode != -1 && !m->busy) {
685                 snd_mtxunlock(m->lock);
686                 return EBADF;
687         }
688
689         if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
690                 if (j == SOUND_MIXER_RECSRC)
691                         ret = mixer_setrecsrc(m, *arg_i);
692                 else
693                         ret = mixer_set(m, j, *arg_i);
694                 snd_mtxunlock(m->lock);
695                 return (ret == 0)? 0 : ENXIO;
696         }
697
698         if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
699                 switch (j) {
700                 case SOUND_MIXER_DEVMASK:
701                 case SOUND_MIXER_CAPS:
702                 case SOUND_MIXER_STEREODEVS:
703                         v = mix_getdevs(m);
704                         break;
705
706                 case SOUND_MIXER_RECMASK:
707                         v = mix_getrecdevs(m);
708                         break;
709
710                 case SOUND_MIXER_RECSRC:
711                         v = mixer_getrecsrc(m);
712                         break;
713
714                 default:
715                         v = mixer_get(m, j);
716                 }
717                 *arg_i = v;
718                 snd_mtxunlock(m->lock);
719                 return (v != -1)? 0 : ENXIO;
720         }
721         snd_mtxunlock(m->lock);
722         return ENXIO;
723 }
724
725 #ifdef USING_DEVFS
726 static void
727 mixer_clone(void *arg, struct ucred *cred, char *name, int namelen,
728     struct cdev **dev)
729 {
730         struct snddev_info *sd;
731
732         if (*dev != NULL)
733                 return;
734         if (strcmp(name, "mixer") == 0) {
735                 sd = devclass_get_softc(pcm_devclass, snd_unit);
736                 if (sd != NULL && sd->mixer_dev != NULL) {
737                         *dev = sd->mixer_dev;
738                         dev_ref(*dev);
739                 }
740         }
741 }
742
743 static void
744 mixer_sysinit(void *p)
745 {
746         mixer_ehtag = EVENTHANDLER_REGISTER(dev_clone, mixer_clone, 0, 1000);
747 }
748
749 static void
750 mixer_sysuninit(void *p)
751 {
752         if (mixer_ehtag != NULL)
753                 EVENTHANDLER_DEREGISTER(dev_clone, mixer_ehtag);
754 }
755
756 SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL);
757 SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL);
758 #endif
759
760