2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
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 $
30 #include <dev/sound/pcm/sound.h>
31 #include <dev/sound/pcm/dsp.h>
35 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.18 2007/06/22 21:41:16 corecode Exp $");
37 MALLOC_DEFINE(M_MIXER
, "mixer", "mixer");
39 #define MIXER_NAMELEN 16
49 u_int32_t hwvol_mute_level
;
57 char name
[MIXER_NAMELEN
];
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,
79 static char* snd_mixernames
[SOUND_MIXER_NRDEVICES
] = SOUND_DEVICE_NAMES
;
81 static d_open_t mixer_open
;
82 static d_close_t mixer_close
;
84 static struct dev_ops mixer_cdevsw
= {
85 { "mixer", SND_CDEV_MAJOR
, D_TRACKCLOSE
},
86 /* .d_flags = D_TRACKCLOSE | D_NEEDGIANT, */
88 .d_close
= mixer_close
,
89 .d_ioctl
= mixer_ioctl
,
93 static eventhandler_tag mixer_ehtag
;
97 mixer_get_devt(device_t dev
)
99 struct snddev_info
*snddev
;
101 snddev
= device_get_softc(dev
);
103 return snddev
->mixer_dev
;
108 mixer_lookup(char *devname
)
112 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
113 if (strncmp(devname
, snd_mixernames
[i
],
114 strlen(snd_mixernames
[i
])) == 0)
121 * Always called with mixer->lock held.
124 mixer_set_softpcmvol(struct snd_mixer
*mixer
, struct snddev_info
*d
,
125 unsigned left
, unsigned right
)
127 struct snddev_channel
*sce
;
128 struct pcm_channel
*ch
;
130 snd_mtxunlock(mixer
->lock
);
131 SLIST_FOREACH(sce
, &d
->channels
, link
) {
134 if (ch
->direction
== PCMDIR_PLAY
&&
135 (ch
->feederflags
& (1 << FEEDER_VOLUME
)))
136 chn_setvolume(ch
, left
, right
);
139 snd_mtxlock(mixer
->lock
);
144 mixer_set(struct snd_mixer
*m
, unsigned dev
, unsigned lev
)
146 struct snddev_info
*d
;
147 unsigned l
, r
, tl
, tr
;
148 u_int32_t parent
= SOUND_MIXER_NONE
, child
= 0;
152 if (m
== NULL
|| dev
>= SOUND_MIXER_NRDEVICES
||
153 (0 == (m
->devs
& (1 << dev
))))
156 l
= min((lev
& 0x00ff), 100);
157 r
= min(((lev
& 0xff00) >> 8), 100);
158 realdev
= m
->realdev
[dev
];
160 d
= device_get_softc(m
->dev
);
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
];
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)
179 } else if (child
!= 0) {
180 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++) {
181 if (!(child
& (1 << i
)) || m
->parent
[i
] != dev
)
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
);
191 realdev
= m
->realdev
[dev
];
192 if (realdev
!= SOUND_MIXER_NONE
&&
193 MIXER_SET(m
, realdev
, l
, r
) < 0)
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)
203 m
->level
[dev
] = l
| (r
<< 8);
209 mixer_get(struct snd_mixer
*mixer
, int dev
)
211 if ((dev
< SOUND_MIXER_NRDEVICES
) && (mixer
->devs
& (1 << dev
)))
212 return mixer
->level
[dev
];
217 mixer_setrecsrc(struct snd_mixer
*mixer
, u_int32_t src
)
219 src
&= mixer
->recdevs
;
221 src
= SOUND_MASK_MIC
;
222 mixer
->recsrc
= MIXER_SETRECSRC(mixer
, src
);
227 mixer_getrecsrc(struct snd_mixer
*mixer
)
229 return mixer
->recsrc
;
233 mix_setdevs(struct snd_mixer
*m
, u_int32_t v
)
235 struct snddev_info
*d
;
241 d
= device_get_softc(m
->dev
);
242 if (d
!= NULL
&& (d
->flags
& SD_F_SOFTPCMVOL
))
244 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++) {
245 if (m
->parent
[i
] < SOUND_MIXER_NRDEVICES
)
246 v
|= 1 << m
->parent
[i
];
253 mix_setrecdevs(struct snd_mixer
*m
, u_int32_t v
)
259 mix_setparentchild(struct snd_mixer
*m
, u_int32_t parent
, u_int32_t childs
)
264 if (m
== NULL
|| parent
>= SOUND_MIXER_NRDEVICES
)
266 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++) {
269 if (childs
& (1 << i
)) {
271 if (m
->parent
[i
] < SOUND_MIXER_NRDEVICES
)
272 m
->child
[m
->parent
[i
]] &= ~(1 << i
);
273 m
->parent
[i
] = parent
;
277 mask
&= ~(1 << parent
);
278 m
->child
[parent
] = mask
;
282 mix_setrealdev(struct snd_mixer
*m
, u_int32_t dev
, u_int32_t realdev
)
284 if (m
== NULL
|| dev
>= SOUND_MIXER_NRDEVICES
||
285 !(realdev
== SOUND_MIXER_NONE
|| realdev
< SOUND_MIXER_NRDEVICES
))
287 m
->realdev
[dev
] = realdev
;
291 mix_getparent(struct snd_mixer
*m
, u_int32_t dev
)
293 if (m
== NULL
|| dev
>= SOUND_MIXER_NRDEVICES
)
294 return SOUND_MIXER_NONE
;
295 return m
->parent
[dev
];
299 mix_getchild(struct snd_mixer
*m
, u_int32_t dev
)
301 if (m
== NULL
|| dev
>= SOUND_MIXER_NRDEVICES
)
303 return m
->child
[dev
];
307 mix_getdevs(struct snd_mixer
*m
)
313 mix_getrecdevs(struct snd_mixer
*m
)
319 mix_getdevinfo(struct snd_mixer
*m
)
325 mixer_init(device_t dev
, kobj_class_t cls
, void *devinfo
)
327 struct snddev_info
*snddev
;
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");
337 m
->devinfo
= devinfo
;
340 for (i
= 0; i
< 32; i
++) {
341 m
->parent
[i
] = SOUND_MIXER_NONE
;
349 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++) {
350 v
= snd_mixerdefaults
[i
];
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) {
359 snd_mtxlock(m
->lock
);
360 mixer_set(m
, i
, v
| (v
<< 8));
361 snd_mtxunlock(m
->lock
);
364 mixer_setrecsrc(m
, SOUND_MASK_MIC
);
366 unit
= device_get_unit(dev
);
367 pdev
= make_dev(&mixer_cdevsw
, PCMMKMINOR(unit
, 0),
368 UID_ROOT
, GID_WHEEL
, 0666, "mixer%d", unit
);
371 snddev
= device_get_softc(dev
);
372 snddev
->mixer_dev
= pdev
;
375 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++) {
376 if (!(m
->devs
& (1 << i
)))
378 if (m
->realdev
[i
] != i
) {
379 device_printf(dev
, "Mixer \"%s\" -> \"%s\":",
381 (m
->realdev
[i
] < SOUND_MIXER_NRDEVICES
) ?
382 snd_mixernames
[m
->realdev
[i
]] : "none");
384 device_printf(dev
, "Mixer \"%s\":",
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
]);
394 if (snddev
->flags
& SD_F_SOFTPCMVOL
)
395 device_printf(dev
, "Soft PCM mixer ENABLED\n");
401 snd_mtxfree(m
->lock
);
402 kobj_delete((kobj_t
)m
, M_MIXER
);
407 mixer_uninit(device_t dev
)
411 struct snddev_info
*d
;
415 d
= device_get_softc(dev
);
416 pdev
= mixer_get_devt(dev
);
417 if (d
== NULL
|| pdev
== NULL
|| pdev
->si_drv1
== NULL
)
420 snd_mtxlock(m
->lock
);
423 snd_mtxunlock(m
->lock
);
427 pdev
->si_drv1
= NULL
;
429 unit
= device_get_unit(dev
);
430 dev_ops_remove_minor(&mixer_cdevsw
, /*-1, */PCMMKMINOR(unit
, 0));
432 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
435 mixer_setrecsrc(m
, SOUND_MASK_MIC
);
439 snd_mtxunlock(m
->lock
);
440 snd_mtxfree(m
->lock
);
441 kobj_delete((kobj_t
)m
, M_MIXER
);
449 mixer_reinit(device_t dev
)
455 pdev
= mixer_get_devt(dev
);
457 snd_mtxlock(m
->lock
);
461 snd_mtxunlock(m
->lock
);
465 for (i
= 0; i
< SOUND_MIXER_NRDEVICES
; i
++)
466 mixer_set(m
, i
, m
->level
[i
]);
468 mixer_setrecsrc(m
, m
->recsrc
);
469 snd_mtxunlock(m
->lock
);
476 sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS
)
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
);
491 snd_mtxunlock(m
->lock
);
494 else if (dev
!= m
->hwvol_mixer
) {
495 m
->hwvol_mixer
= dev
;
499 snd_mtxunlock(m
->lock
);
505 mixer_hwvol_init(device_t dev
)
510 pdev
= mixer_get_devt(dev
);
513 m
->hwvol_mixer
= SOUND_MIXER_VOLUME
;
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", "");
526 mixer_hwvol_mute(device_t dev
)
531 pdev
= mixer_get_devt(dev
);
533 snd_mtxlock(m
->lock
);
534 if (m
->hwvol_muted
) {
536 mixer_set(m
, m
->hwvol_mixer
, m
->hwvol_mute_level
);
539 m
->hwvol_mute_level
= mixer_get(m
, m
->hwvol_mixer
);
540 mixer_set(m
, m
->hwvol_mixer
, 0);
542 snd_mtxunlock(m
->lock
);
546 mixer_hwvol_step(device_t dev
, int left_step
, int right_step
)
549 int level
, left
, right
;
552 pdev
= mixer_get_devt(dev
);
554 snd_mtxlock(m
->lock
);
555 if (m
->hwvol_muted
) {
557 level
= m
->hwvol_mute_level
;
559 level
= mixer_get(m
, m
->hwvol_mixer
);
563 left
+= left_step
* m
->hwvol_step
;
566 right
+= right_step
* m
->hwvol_step
;
569 mixer_set(m
, m
->hwvol_mixer
, left
| right
<< 8);
571 snd_mtxunlock(m
->lock
);
574 /* ----------------------------------------------------------------------- */
577 vchanvolume(cdev_t i_dev
, int write
, int *volume
, int *ret
,
580 struct snddev_info
*d
;
582 struct pcm_channel
*ch
;
583 int vol_left
, vol_right
;
586 d
= dsp_get_info(i_dev
);
592 * We search for a vchan which is owned by the current process.
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
)
605 vol_left
= min(*volume
& 0x00ff, 100);
606 vol_right
= min((*volume
& 0xff00) >> 8, 100);
607 *ret
= chn_setvolume(ch
, vol_left
, vol_right
);
609 *volume
= ch
->volume
;
616 /* ----------------------------------------------------------------------- */
619 mixer_open(struct dev_open_args
*ap
)
621 struct cdev
*i_dev
= ap
->a_head
.a_dev
;
625 snd_mtxlock(m
->lock
);
629 snd_mtxunlock(m
->lock
);
634 mixer_close(struct dev_close_args
*ap
)
636 struct cdev
*i_dev
= ap
->a_head
.a_dev
;
640 snd_mtxlock(m
->lock
);
643 snd_mtxunlock(m
->lock
);
648 snd_mtxunlock(m
->lock
);
653 mixer_ioctl(struct dev_ioctl_args
*ap
)
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
;
660 int ret
, *arg_i
= (int *)arg
;
661 int v
= -1, j
= cmd
& 0xff;
669 * If we are handling PCM, maybe the app really wants to
670 * set its vchan, and fails to use the correct fd.
672 if (j
== SOUND_MIXER_PCM
) {
673 if (vchanvolume(i_dev
,
674 (cmd
& MIXER_WRITE(0)) == MIXER_WRITE(0),
675 (int *)arg
, &ret
, curthread
))
677 /* else proceed as usual */
680 snd_mtxlock(m
->lock
);
681 if (mode
!= -1 && !m
->busy
) {
682 snd_mtxunlock(m
->lock
);
686 if ((cmd
& MIXER_WRITE(0)) == MIXER_WRITE(0)) {
687 if (j
== SOUND_MIXER_RECSRC
)
688 ret
= mixer_setrecsrc(m
, *arg_i
);
690 ret
= mixer_set(m
, j
, *arg_i
);
691 snd_mtxunlock(m
->lock
);
692 return (ret
== 0)? 0 : ENXIO
;
695 if ((cmd
& MIXER_READ(0)) == MIXER_READ(0)) {
697 case SOUND_MIXER_DEVMASK
:
698 case SOUND_MIXER_CAPS
:
699 case SOUND_MIXER_STEREODEVS
:
703 case SOUND_MIXER_RECMASK
:
704 v
= mix_getrecdevs(m
);
707 case SOUND_MIXER_RECSRC
:
708 v
= mixer_getrecsrc(m
);
715 snd_mtxunlock(m
->lock
);
716 return (v
!= -1)? 0 : ENXIO
;
718 snd_mtxunlock(m
->lock
);
724 mixer_clone(void *arg
, struct ucred
*cred
, char *name
, int namelen
,
727 struct snddev_info
*sd
;
731 if (strcmp(name
, "mixer") == 0) {
732 sd
= devclass_get_softc(pcm_devclass
, snd_unit
);
733 if (sd
!= NULL
&& sd
->mixer_dev
!= NULL
) {
734 *dev
= sd
->mixer_dev
;
741 mixer_sysinit(void *p
)
743 mixer_ehtag
= EVENTHANDLER_REGISTER(dev_clone
, mixer_clone
, 0, 1000);
747 mixer_sysuninit(void *p
)
749 if (mixer_ehtag
!= NULL
)
750 EVENTHANDLER_DEREGISTER(dev_clone
, mixer_ehtag
);
753 SYSINIT(mixer_sysinit
, SI_SUB_DRIVERS
, SI_ORDER_MIDDLE
, mixer_sysinit
, NULL
);
754 SYSUNINIT(mixer_sysuninit
, SI_SUB_DRIVERS
, SI_ORDER_MIDDLE
, mixer_sysuninit
, NULL
);