2 * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org>
3 * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006
4 * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org>
5 * Copyright (c) 1997 Luigi Rizzo
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 #ifdef HAVE_KERNEL_OPTION_HEADERS
34 #include <dev/sound/pcm/sound.h>
35 #include <dev/sound/pcm/ac97.h>
36 #include <dev/sound/pcm/vchan.h>
37 #include <dev/sound/pcm/dsp.h>
38 #include <dev/sound/pcm/sndstat.h>
39 #include <dev/sound/version.h>
40 #include <sys/devfs.h>
41 #include <sys/limits.h>
42 #include <sys/sysctl.h>
44 #include "feeder_if.h"
46 SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 274035 2014-11-03 11:11:45Z bapt $");
48 devclass_t pcm_devclass
;
50 int pcm_veto_load
= 1;
51 struct cdev
*default_dsp
;
52 struct cdev
*default_mixer
;
55 TUNABLE_INT("hw.snd.default_unit", &snd_unit
);
57 static int snd_unit_auto
= -1;
58 TUNABLE_INT("hw.snd.default_auto", &snd_unit_auto
);
59 SYSCTL_INT(_hw_snd
, OID_AUTO
, default_auto
, CTLFLAG_RW
,
60 &snd_unit_auto
, 0, "assign default unit to a newly attached device");
62 int snd_maxautovchans
= 16;
63 /* XXX: a tunable implies that we may need more than one sound channel before
64 the system can change a sysctl (/etc/sysctl.conf), do we really need
66 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans
);
68 SYSCTL_NODE(_hw
, OID_AUTO
, snd
, CTLFLAG_RD
, 0, "Sound driver");
71 * XXX I've had enough with people not telling proper version/arch
72 * while reporting problems, not after 387397913213th questions/requests.
74 static char snd_driver_version
[] =
75 __XSTRING(SND_DRV_VERSION
)"/"MACHINE_ARCH
;
76 SYSCTL_STRING(_hw_snd
, OID_AUTO
, version
, CTLFLAG_RD
, &snd_driver_version
,
77 0, "driver version/arch");
80 * @brief Unit number allocator for syncgroup IDs
82 struct unrhdr
*pcmsg_unrhdr
= NULL
;
85 sndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS
)
87 SNDSTAT_PREPARE_PCM_BEGIN();
88 SNDSTAT_PREPARE_PCM_END();
92 snd_mtxcreate(const char *desc
, const char *type
)
96 m
= kmalloc(sizeof(*m
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
97 lockinit(m
, desc
, 0, LK_CANRECURSE
);
104 struct lock
*mtx
= m
;
107 kfree(mtx
, M_DEVBUF
);
111 snd_mtxassert(void *m
)
116 KKASSERT(lockstatus(lk
, curthread
) == LK_EXCLUSIVE
);
121 snd_setup_intr(device_t dev
, struct resource
*res
, int flags
, driver_intr_t hand
, void *param
, void **cookiep
)
123 struct snddev_info
*d
;
125 flags
&= INTR_MPSAFE
;
126 d
= device_get_softc(dev
);
127 if (d
!= NULL
&& (flags
& INTR_MPSAFE
))
128 d
->flags
|= SD_F_MPSAFE
;
130 return bus_setup_intr(dev
, res
, flags
, hand
, param
, cookiep
, NULL
);
134 pcm_clonereset(struct snddev_info
*d
)
140 cmax
= d
->playcount
+ d
->reccount
- 1;
141 if (d
->pvchancount
> 0)
142 cmax
+= max(d
->pvchancount
, snd_maxautovchans
) - 1;
143 if (d
->rvchancount
> 0)
144 cmax
+= max(d
->rvchancount
, snd_maxautovchans
) - 1;
145 if (cmax
> PCMMAXCLONE
)
147 (void)snd_clone_gc(d
->clones
);
148 (void)snd_clone_setmaxunit(d
->clones
, cmax
);
152 pcm_setvchans(struct snddev_info
*d
, int direction
, int newcnt
, int num
)
154 struct pcm_channel
*c
, *ch
, *nch
;
155 struct pcmchan_caps
*caps
;
160 if ((direction
== PCMDIR_PLAY
&& d
->playcount
< 1) ||
161 (direction
== PCMDIR_REC
&& d
->reccount
< 1))
164 if (!(d
->flags
& SD_F_AUTOVCHAN
))
167 if (newcnt
< 0 || newcnt
> SND_MAXVCHANS
)
170 if (direction
== PCMDIR_PLAY
)
171 vcnt
= d
->pvchancount
;
172 else if (direction
== PCMDIR_REC
)
173 vcnt
= d
->rvchancount
;
179 (num
>= 0 && num
< SND_MAXVCHANS
&& (newcnt
- 1) == vcnt
),
180 ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d",
182 /* add new vchans - find a parent channel first */
184 CHN_FOREACH(c
, d
, channels
.pcm
) {
186 if (c
->direction
== direction
&&
187 ((c
->flags
& CHN_F_HAS_VCHAN
) || (vcnt
== 0 &&
189 !(c
->flags
& (CHN_F_BUSY
| CHN_F_VIRTUAL
))))) {
191 * Reuse hw channel with vchans already
194 if (c
->flags
& CHN_F_HAS_VCHAN
) {
199 * No vchans ever created, look for
200 * channels with supported formats.
202 caps
= chn_getcaps(c
);
207 for (i
= 0; caps
->fmtlist
[i
] != 0; i
++) {
208 if (caps
->fmtlist
[i
] & AFMT_CONVERTIBLE
)
211 if (caps
->fmtlist
[i
] != 0) {
220 ch
->flags
|= CHN_F_BUSY
;
222 while (err
== 0 && newcnt
> vcnt
) {
223 err
= vchan_create(ch
, num
);
226 else if (err
== E2BIG
&& newcnt
> vcnt
)
227 device_printf(d
->dev
,
228 "%s: err=%d Maximum channel reached.\n",
232 ch
->flags
&= ~CHN_F_BUSY
;
238 } else if (newcnt
< vcnt
) {
240 ("bogus vchan_destroy() request num=%d", num
));
241 CHN_FOREACH(c
, d
, channels
.pcm
) {
243 if (c
->direction
!= direction
||
244 CHN_EMPTY(c
, children
) ||
245 !(c
->flags
& CHN_F_HAS_VCHAN
)) {
249 CHN_FOREACH_SAFE(ch
, c
, nch
, children
) {
251 if (vcnt
== 1 && c
->refcount
> 0) {
255 if (!(ch
->flags
& CHN_F_BUSY
) &&
257 err
= vchan_destroy(ch
);
274 /* return error status and a locked channel */
276 pcm_chnalloc(struct snddev_info
*d
, struct pcm_channel
**ch
, int direction
,
277 pid_t pid
, char *comm
, int devunit
)
279 struct pcm_channel
*c
;
280 int err
, vchancount
, vchan_num
;
282 KASSERT(d
!= NULL
&& ch
!= NULL
&& (devunit
== -1 ||
283 !(devunit
& ~(SND_U_MASK
| SND_D_MASK
| SND_C_MASK
))) &&
284 (direction
== PCMDIR_PLAY
|| direction
== PCMDIR_REC
),
285 ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d",
286 __func__
, d
, ch
, direction
, pid
, devunit
));
289 /* Double check again. */
291 switch (snd_unit2d(devunit
)) {
292 case SND_DEV_DSPHW_PLAY
:
293 case SND_DEV_DSPHW_VPLAY
:
294 if (direction
!= PCMDIR_PLAY
)
297 case SND_DEV_DSPHW_REC
:
298 case SND_DEV_DSPHW_VREC
:
299 if (direction
!= PCMDIR_REC
)
303 if (!(direction
== PCMDIR_PLAY
||
304 direction
== PCMDIR_REC
))
312 vchancount
= (direction
== PCMDIR_PLAY
) ? d
->pvchancount
:
317 /* scan for a free channel */
318 CHN_FOREACH(c
, d
, channels
.pcm
) {
320 if (devunit
== -1 && c
->direction
== direction
&&
321 (c
->flags
& CHN_F_VIRTUAL
)) {
322 if (vchancount
< snd_maxautovchans
&&
323 vchan_num
< CHN_CHAN(c
)) {
329 if (c
->direction
== direction
&& !(c
->flags
& CHN_F_BUSY
) &&
330 (devunit
== -1 || devunit
== -2 || c
->unit
== devunit
)) {
331 c
->flags
|= CHN_F_BUSY
;
333 strlcpy(c
->comm
, (comm
!= NULL
) ? comm
:
334 CHN_COMM_UNKNOWN
, sizeof(c
->comm
));
337 } else if (c
->unit
== devunit
) {
338 if (c
->direction
!= direction
)
340 else if (c
->flags
& CHN_F_BUSY
)
346 } else if ((devunit
== -1 || devunit
== -2) &&
347 c
->direction
== direction
&& (c
->flags
& CHN_F_BUSY
))
356 /* no channel available */
357 if (devunit
== -1 || snd_unit2d(devunit
) == SND_DEV_DSPHW_VPLAY
||
358 snd_unit2d(devunit
) == SND_DEV_DSPHW_VREC
) {
359 if (!(vchancount
> 0 && vchancount
< snd_maxautovchans
) &&
360 (devunit
== -1 || snd_unit2c(devunit
) < snd_maxautovchans
))
362 err
= pcm_setvchans(d
, direction
, vchancount
+ 1,
363 (devunit
== -1) ? -1 : snd_unit2c(devunit
));
374 /* release a locked channel and unlock it */
376 pcm_chnrelease(struct pcm_channel
*c
)
378 PCM_BUSYASSERT(c
->parentsnddev
);
381 c
->flags
&= ~CHN_F_BUSY
;
383 strlcpy(c
->comm
, CHN_COMM_UNUSED
, sizeof(c
->comm
));
390 pcm_chnref(struct pcm_channel
*c
, int ref
)
392 PCM_BUSYASSERT(c
->parentsnddev
);
397 return (c
->refcount
);
401 pcm_inprog(struct snddev_info
*d
, int delta
)
411 pcm_setmaxautovchans(struct snddev_info
*d
, int num
)
418 if (num
>= 0 && d
->pvchancount
> num
)
419 (void)pcm_setvchans(d
, PCMDIR_PLAY
, num
, -1);
420 else if (num
> 0 && d
->pvchancount
== 0)
421 (void)pcm_setvchans(d
, PCMDIR_PLAY
, 1, -1);
423 if (num
>= 0 && d
->rvchancount
> num
)
424 (void)pcm_setvchans(d
, PCMDIR_REC
, num
, -1);
425 else if (num
> 0 && d
->rvchancount
== 0)
426 (void)pcm_setvchans(d
, PCMDIR_REC
, 1, -1);
432 sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS
)
434 struct snddev_info
*d
;
438 error
= sysctl_handle_int(oidp
, &unit
, 0, req
);
439 if (error
== 0 && req
->newptr
!= NULL
) {
440 d
= devclass_get_softc(pcm_devclass
, unit
);
441 if (!PCM_REGISTERED(d
) || CHN_EMPTY(d
, channels
.pcm
))
449 /* XXX: do we need a way to let the user change the default unit? */
450 SYSCTL_PROC(_hw_snd
, OID_AUTO
, default_unit
,
451 CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_ANYBODY
,
452 0, sizeof(int), sysctl_hw_snd_default_unit
, "I",
453 "default sound device");
456 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS
)
458 struct snddev_info
*d
;
461 v
= snd_maxautovchans
;
462 error
= sysctl_handle_int(oidp
, &v
, 0, req
);
463 if (error
== 0 && req
->newptr
!= NULL
) {
466 if (v
> SND_MAXVCHANS
)
468 snd_maxautovchans
= v
;
469 for (i
= 0; pcm_devclass
!= NULL
&&
470 i
< devclass_get_maxunit(pcm_devclass
); i
++) {
471 d
= devclass_get_softc(pcm_devclass
, i
);
472 if (!PCM_REGISTERED(d
))
474 PCM_ACQUIRE_QUICK(d
);
475 pcm_setmaxautovchans(d
, v
);
476 PCM_RELEASE_QUICK(d
);
481 SYSCTL_PROC(_hw_snd
, OID_AUTO
, maxautovchans
, CTLTYPE_INT
| CTLFLAG_RW
,
482 0, sizeof(int), sysctl_hw_snd_maxautovchans
, "I", "maximum virtual channel");
485 pcm_chn_create(struct snddev_info
*d
, struct pcm_channel
*parent
, kobj_class_t cls
, int dir
, int num
, void *devinfo
)
487 struct pcm_channel
*ch
;
488 int direction
, err
, rpnum
, *pnum
, max
;
489 int udc
, device
, chan
;
490 char *dirs
, *devname
, buf
[CHN_NAMELEN
];
494 KASSERT(num
>= -1, ("invalid num=%d", num
));
500 direction
= PCMDIR_PLAY
;
501 pnum
= &d
->playcount
;
502 device
= SND_DEV_DSPHW_PLAY
;
505 case PCMDIR_PLAY_VIRTUAL
:
507 direction
= PCMDIR_PLAY
;
508 pnum
= &d
->pvchancount
;
509 device
= SND_DEV_DSPHW_VPLAY
;
514 direction
= PCMDIR_REC
;
516 device
= SND_DEV_DSPHW_REC
;
519 case PCMDIR_REC_VIRTUAL
:
521 direction
= PCMDIR_REC
;
522 pnum
= &d
->rvchancount
;
523 device
= SND_DEV_DSPHW_VREC
;
530 chan
= (num
== -1) ? 0 : num
;
532 if (*pnum
>= max
|| chan
>= max
)
537 CHN_FOREACH(ch
, d
, channels
.pcm
) {
538 if (CHN_DEV(ch
) != device
)
540 if (chan
== CHN_CHAN(ch
)) {
542 device_printf(d
->dev
,
543 "channel num=%d allocated!\n", chan
);
548 device_printf(d
->dev
,
549 "chan=%d > %d\n", chan
, max
);
556 if (*pnum
!= rpnum
) {
557 device_printf(d
->dev
,
558 "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n",
559 __func__
, dirs
, *pnum
, rpnum
);
563 udc
= snd_mkunit(device_get_unit(d
->dev
), device
, chan
);
564 devname
= dsp_unit2name(buf
, sizeof(buf
), udc
);
566 if (devname
== NULL
) {
567 device_printf(d
->dev
,
568 "Failed to query device name udc=0x%08x\n", udc
);
573 ch
= kmalloc(sizeof(*ch
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
574 ch
->methods
= kobj_create(cls
, M_DEVBUF
, M_WAITOK
| M_ZERO
);
577 strlcpy(ch
->comm
, CHN_COMM_UNUSED
, sizeof(ch
->comm
));
578 ch
->parentsnddev
= d
;
579 ch
->parentchannel
= parent
;
581 ch
->trigger
= PCMTRIG_STOP
;
582 ksnprintf(ch
->name
, sizeof(ch
->name
), "%s:%s:%s",
583 device_get_nameunit(ch
->dev
), dirs
, devname
);
585 err
= chn_init(ch
, devinfo
, dir
, direction
);
588 device_printf(d
->dev
, "chn_init(%s) failed: err = %d\n",
590 kobj_delete(ch
->methods
, M_DEVBUF
);
599 pcm_chn_destroy(struct pcm_channel
*ch
)
601 struct snddev_info
*d
;
604 d
= ch
->parentsnddev
;
609 device_printf(ch
->dev
, "chn_kill(%s) failed, err = %d\n",
614 kobj_delete(ch
->methods
, M_DEVBUF
);
621 pcm_chn_add(struct snddev_info
*d
, struct pcm_channel
*ch
)
625 KASSERT(ch
!= NULL
&& (ch
->direction
== PCMDIR_PLAY
||
626 ch
->direction
== PCMDIR_REC
), ("Invalid pcm channel"));
628 CHN_INSERT_SORT_ASCEND(d
, ch
, channels
.pcm
);
630 switch (CHN_DEV(ch
)) {
631 case SND_DEV_DSPHW_PLAY
:
634 case SND_DEV_DSPHW_VPLAY
:
637 case SND_DEV_DSPHW_REC
:
640 case SND_DEV_DSPHW_VREC
:
653 pcm_chn_remove(struct snddev_info
*d
, struct pcm_channel
*ch
)
655 struct pcm_channel
*tmp
;
662 CHN_FOREACH(tmp
, d
, channels
.pcm
) {
670 CHN_REMOVE(d
, ch
, channels
.pcm
);
672 switch (CHN_DEV(ch
)) {
673 case SND_DEV_DSPHW_PLAY
:
676 case SND_DEV_DSPHW_VPLAY
:
679 case SND_DEV_DSPHW_REC
:
682 case SND_DEV_DSPHW_VREC
:
695 pcm_addchan(device_t dev
, int dir
, kobj_class_t cls
, void *devinfo
)
697 struct snddev_info
*d
= device_get_softc(dev
);
698 struct pcm_channel
*ch
;
704 ch
= pcm_chn_create(d
, NULL
, cls
, dir
, -1, devinfo
);
706 device_printf(d
->dev
, "pcm_chn_create(%s, %d, %p) failed\n",
707 cls
->name
, dir
, devinfo
);
712 err
= pcm_chn_add(d
, ch
);
715 device_printf(d
->dev
, "pcm_chn_add(%s) failed, err=%d\n",
724 pcm_killchan(device_t dev
)
726 struct snddev_info
*d
= device_get_softc(dev
);
727 struct pcm_channel
*ch
;
732 ch
= CHN_FIRST(d
, channels
.pcm
);
735 error
= pcm_chn_remove(d
, ch
);
739 return (pcm_chn_destroy(ch
));
743 pcm_best_unit(int old
)
745 struct snddev_info
*d
;
746 int i
, best
, bestprio
, prio
;
750 for (i
= 0; pcm_devclass
!= NULL
&&
751 i
< devclass_get_maxunit(pcm_devclass
); i
++) {
752 d
= devclass_get_softc(pcm_devclass
, i
);
753 if (!PCM_REGISTERED(d
))
756 if (d
->playcount
== 0)
758 if (d
->reccount
== 0)
760 if (prio
> bestprio
|| (prio
== bestprio
&& i
== old
)) {
769 pcm_setstatus(device_t dev
, char *str
)
771 struct snddev_info
*d
= device_get_softc(dev
);
775 if (d
->playcount
== 0 || d
->reccount
== 0)
776 d
->flags
|= SD_F_SIMPLEX
;
778 if ((d
->playcount
> 0 || d
->reccount
> 0) &&
779 !(d
->flags
& SD_F_AUTOVCHAN
)) {
780 d
->flags
|= SD_F_AUTOVCHAN
;
784 pcm_setmaxautovchans(d
, snd_maxautovchans
);
786 strlcpy(d
->status
, str
, SND_STATUSLEN
);
790 /* Last stage, enable cloning. */
791 if (d
->clones
!= NULL
)
792 (void)snd_clone_enable(d
->clones
);
794 /* Done, we're ready.. */
795 d
->flags
|= SD_F_REGISTERED
;
801 if (snd_unit_auto
< 0)
802 snd_unit_auto
= (snd_unit
< 0) ? 1 : 0;
803 if (snd_unit
< 0 || snd_unit_auto
> 1) {
804 if (device_get_unit(dev
) != snd_unit
)
805 snd_unit
= device_get_unit(dev
);
806 } else if (snd_unit_auto
== 1) {
807 snd_unit
= pcm_best_unit(snd_unit
);
814 pcm_getflags(device_t dev
)
816 struct snddev_info
*d
= device_get_softc(dev
);
822 pcm_setflags(device_t dev
, uint32_t val
)
824 struct snddev_info
*d
= device_get_softc(dev
);
830 pcm_getdevinfo(device_t dev
)
832 struct snddev_info
*d
= device_get_softc(dev
);
838 pcm_getbuffersize(device_t dev
, unsigned int minbufsz
, unsigned int deflt
, unsigned int maxbufsz
)
840 struct snddev_info
*d
= device_get_softc(dev
);
844 if (resource_int_value(device_get_name(dev
), device_get_unit(dev
), "buffersize", &sz
) == 0) {
846 RANGE(sz
, minbufsz
, maxbufsz
);
848 device_printf(dev
, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x
, minbufsz
, maxbufsz
, sz
);
855 device_printf(dev
, "'buffersize=%d' hint is not a power of 2, using %d\n", sz
, x
);
868 sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS
)
870 struct snddev_info
*d
;
874 if (!PCM_REGISTERED(d
))
879 val
= (d
->flags
& SD_F_BITPERFECT
) ? 1 : 0;
883 err
= sysctl_handle_int(oidp
, &val
, 0, req
);
885 if (err
== 0 && req
->newptr
!= NULL
) {
886 if (!(val
== 0 || val
== 1)) {
887 PCM_RELEASE_QUICK(d
);
893 d
->flags
&= ~SD_F_BITPERFECT
;
894 d
->flags
|= (val
!= 0) ? SD_F_BITPERFECT
: 0;
899 PCM_RELEASE_QUICK(d
);
906 sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS
)
908 struct snddev_info
*d
;
913 if (!PCM_REGISTERED(d
) || d
->clones
== NULL
)
916 PCM_ACQUIRE_QUICK(d
);
918 flags
= snd_clone_getflags(d
->clones
);
919 err
= sysctl_handle_int(oidp
, &flags
, 0, req
);
921 if (err
== 0 && req
->newptr
!= NULL
) {
922 if (flags
& ~SND_CLONE_MASK
)
925 (void)snd_clone_setflags(d
->clones
, flags
);
928 PCM_RELEASE_QUICK(d
);
934 sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS
)
936 struct snddev_info
*d
;
940 if (!PCM_REGISTERED(d
) || d
->clones
== NULL
)
943 PCM_ACQUIRE_QUICK(d
);
945 deadline
= snd_clone_getdeadline(d
->clones
);
946 err
= sysctl_handle_int(oidp
, &deadline
, 0, req
);
948 if (err
== 0 && req
->newptr
!= NULL
) {
952 (void)snd_clone_setdeadline(d
->clones
, deadline
);
955 PCM_RELEASE_QUICK(d
);
961 sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS
)
963 struct snddev_info
*d
;
967 if (!PCM_REGISTERED(d
) || d
->clones
== NULL
)
971 err
= sysctl_handle_int(oidp
, &val
, 0, req
);
973 if (err
== 0 && req
->newptr
!= NULL
&& val
!= 0) {
974 PCM_ACQUIRE_QUICK(d
);
975 val
= snd_clone_gc(d
->clones
);
976 PCM_RELEASE_QUICK(d
);
977 if (bootverbose
!= 0 || snd_verbose
> 3)
978 device_printf(d
->dev
, "clone gc: pruned=%d\n", val
);
985 sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS
)
987 struct snddev_info
*d
;
991 err
= sysctl_handle_int(oidp
, &val
, 0, req
);
993 if (err
== 0 && req
->newptr
!= NULL
&& val
!= 0) {
994 for (i
= 0; pcm_devclass
!= NULL
&&
995 i
< devclass_get_maxunit(pcm_devclass
); i
++) {
996 d
= devclass_get_softc(pcm_devclass
, i
);
997 if (!PCM_REGISTERED(d
) || d
->clones
== NULL
)
999 PCM_ACQUIRE_QUICK(d
);
1000 val
= snd_clone_gc(d
->clones
);
1001 PCM_RELEASE_QUICK(d
);
1002 if (bootverbose
!= 0 || snd_verbose
> 3)
1003 device_printf(d
->dev
, "clone gc: pruned=%d\n",
1010 SYSCTL_PROC(_hw_snd
, OID_AUTO
, clone_gc
, CTLTYPE_INT
| CTLFLAG_RW
,
1011 0, sizeof(int), sysctl_hw_snd_clone_gc
, "I",
1012 "global clone garbage collector");
1015 extern struct devfs_bitmap devfs_dsp_clone_bitmap
;
1018 pcm_register(device_t dev
, void *devinfo
, int numplay
, int numrec
)
1020 struct snddev_info
*d
;
1023 if (pcm_veto_load
) {
1024 device_printf(dev
, "disabled due to an error while initialising: %d\n", pcm_veto_load
);
1029 if (device_get_unit(dev
) > PCMMAXUNIT
) {
1030 device_printf(dev
, "PCMMAXUNIT reached : unit=%d > %d\n",
1031 device_get_unit(dev
), PCMMAXUNIT
);
1033 "Use 'hw.snd.maxunit' tunable to raise the limit.\n");
1037 d
= device_get_softc(dev
);
1039 d
->lock
= snd_mtxcreate(device_get_nameunit(dev
), "sound cdev");
1040 cv_init(&d
->cv
, device_get_nameunit(dev
));
1041 PCM_ACQUIRE_QUICK(d
);
1042 dsp_cdevinfo_init(d
);
1045 * d->flags should be cleared by the allocator of the softc.
1046 * We cannot clear this field here because several devices set
1047 * this flag before calling pcm_register().
1052 if (resource_int_value(device_get_name(dev
), device_get_unit(dev
),
1053 "vpc", &i
) != 0 || i
!= 0)
1054 d
->flags
|= SD_F_VPC
;
1056 if (resource_int_value(device_get_name(dev
), device_get_unit(dev
),
1057 "bitperfect", &i
) == 0 && i
!= 0)
1058 d
->flags
|= SD_F_BITPERFECT
;
1060 d
->devinfo
= devinfo
;
1067 d
->pvchanformat
= 0;
1069 d
->rvchanformat
= 0;
1073 * Create clone manager, disabled by default. Cloning will be
1074 * enabled during final stage of driver initialization through
1077 d
->clones
= snd_clone_create(SND_U_MASK
| SND_D_MASK
, PCMMAXCLONE
,
1078 SND_CLONE_DEADLINE_DEFAULT
, SND_CLONE_WAITOK
|
1079 SND_CLONE_GC_ENABLE
| SND_CLONE_GC_UNREF
|
1080 SND_CLONE_GC_LASTREF
| SND_CLONE_GC_EXPIRED
);
1082 CHN_INIT(d
, channels
.pcm
);
1083 CHN_INIT(d
, channels
.pcm
.busy
);
1084 CHN_INIT(d
, channels
.pcm
.opened
);
1086 /* XXX This is incorrect, but lets play along for now. */
1087 if ((numplay
== 0 || numrec
== 0) && numplay
!= numrec
)
1088 d
->flags
|= SD_F_SIMPLEX
;
1090 sysctl_ctx_init(&d
->play_sysctl_ctx
);
1091 d
->play_sysctl_tree
= SYSCTL_ADD_NODE(&d
->play_sysctl_ctx
,
1092 SYSCTL_CHILDREN(device_get_sysctl_tree(dev
)), OID_AUTO
, "play",
1093 CTLFLAG_RD
, 0, "playback channels node");
1094 sysctl_ctx_init(&d
->rec_sysctl_ctx
);
1095 d
->rec_sysctl_tree
= SYSCTL_ADD_NODE(&d
->rec_sysctl_ctx
,
1096 SYSCTL_CHILDREN(device_get_sysctl_tree(dev
)), OID_AUTO
, "rec",
1097 CTLFLAG_RD
, 0, "record channels node");
1098 /* XXX: an user should be able to set this with a control tool, the
1099 sysadmin then needs min+max sysctls for this */
1100 SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev
),
1101 SYSCTL_CHILDREN(device_get_sysctl_tree(dev
)),
1102 OID_AUTO
, "buffersize", CTLFLAG_RD
, &d
->bufsz
, 0, "allocated buffer size");
1103 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev
),
1104 SYSCTL_CHILDREN(device_get_sysctl_tree(dev
)), OID_AUTO
,
1105 "bitperfect", CTLTYPE_INT
| CTLFLAG_RW
, d
, sizeof(d
),
1106 sysctl_dev_pcm_bitperfect
, "I",
1107 "bit-perfect playback/recording (0=disable, 1=enable)");
1109 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev
),
1110 SYSCTL_CHILDREN(device_get_sysctl_tree(dev
)), OID_AUTO
,
1111 "clone_flags", CTLTYPE_UINT
| CTLFLAG_RW
, d
, sizeof(d
),
1112 sysctl_dev_pcm_clone_flags
, "IU",
1114 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev
),
1115 SYSCTL_CHILDREN(device_get_sysctl_tree(dev
)), OID_AUTO
,
1116 "clone_deadline", CTLTYPE_INT
| CTLFLAG_RW
, d
, sizeof(d
),
1117 sysctl_dev_pcm_clone_deadline
, "I",
1118 "clone expiration deadline (ms)");
1119 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev
),
1120 SYSCTL_CHILDREN(device_get_sysctl_tree(dev
)), OID_AUTO
,
1121 "clone_gc", CTLTYPE_INT
| CTLFLAG_RW
, d
, sizeof(d
),
1122 sysctl_dev_pcm_clone_gc
, "I",
1123 "clone garbage collector");
1126 if (numplay
> 0 || numrec
> 0) {
1127 d
->flags
|= SD_F_AUTOVCHAN
;
1131 if (d
->flags
& SD_F_EQ
)
1132 feeder_eq_initsys(dev
);
1134 /* XXX use make_autoclone_dev? */
1135 /* XXX PCMMAXCHAN can be created for regular channels */
1136 d
->dsp_clonedev
= make_dev(&dsp_ops
,
1137 PCMMKMINOR(device_get_unit(dev
), SND_DEV_DSP
, 0),
1138 UID_ROOT
, GID_WHEEL
, 0666, "dsp%d",
1139 device_get_unit(dev
));
1140 devfs_clone_handler_add(devtoname(d
->dsp_clonedev
), dsp_clone
);
1142 sndstat_register(dev
, d
->status
, sndstat_prepare_pcm
);
1148 pcm_unregister(device_t dev
)
1150 struct snddev_info
*d
;
1151 struct pcm_channel
*ch
;
1155 d
= device_get_softc(dev
);
1157 if (!PCM_ALIVE(d
)) {
1158 device_printf(dev
, "unregister: device not configured\n");
1162 if (sndstat_acquire(td
) != 0) {
1163 device_printf(dev
, "unregister: sndstat busy\n");
1170 if (d
->inprog
!= 0) {
1171 device_printf(dev
, "unregister: operation in progress\n");
1173 sndstat_release(td
);
1180 CHN_FOREACH(ch
, d
, channels
.pcm
) {
1182 if (ch
->refcount
> 0) {
1184 "unregister: channel %s busy (pid %d)\n",
1187 PCM_RELEASE_QUICK(d
);
1188 sndstat_release(td
);
1194 if (d
->clones
!= NULL
) {
1195 if (snd_clone_busy(d
->clones
) != 0) {
1196 device_printf(dev
, "unregister: clone busy\n");
1197 PCM_RELEASE_QUICK(d
);
1198 sndstat_release(td
);
1202 (void)snd_clone_disable(d
->clones
);
1207 if (mixer_uninit(dev
) == EBUSY
) {
1208 device_printf(dev
, "unregister: mixer busy\n");
1210 if (d
->clones
!= NULL
)
1211 (void)snd_clone_enable(d
->clones
);
1214 sndstat_release(td
);
1219 d
->flags
|= SD_F_DYING
;
1220 d
->flags
&= ~SD_F_REGISTERED
;
1224 * No lock being held, so this thing can be flushed without
1225 * stucking into devdrn oblivion.
1227 if (d
->clones
!= NULL
) {
1228 snd_clone_destroy(d
->clones
);
1231 devfs_clone_handler_del(devtoname(d
->dsp_clonedev
));
1232 destroy_dev(d
->dsp_clonedev
);
1234 if (d
->play_sysctl_tree
!= NULL
) {
1235 sysctl_ctx_free(&d
->play_sysctl_ctx
);
1236 d
->play_sysctl_tree
= NULL
;
1238 if (d
->rec_sysctl_tree
!= NULL
) {
1239 sysctl_ctx_free(&d
->rec_sysctl_ctx
);
1240 d
->rec_sysctl_tree
= NULL
;
1243 while (!CHN_EMPTY(d
, channels
.pcm
))
1246 dsp_cdevinfo_flush(d
);
1252 snd_mtxfree(d
->lock
);
1253 sndstat_unregister(dev
);
1254 sndstat_release(td
);
1256 if (snd_unit
== device_get_unit(dev
)) {
1257 snd_unit
= pcm_best_unit(-1);
1258 if (snd_unit_auto
== 0)
1265 /************************************************************************/
1268 * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl.
1270 * @param si Pointer to oss_sysinfo struct where information about the
1271 * sound subsystem will be written/copied.
1273 * This routine returns information about the sound system, such as the
1274 * current OSS version, number of audio, MIDI, and mixer drivers, etc.
1275 * Also includes a bitmask showing which of the above types of devices
1279 * Calling threads must not hold any snddev_info or pcm_channel locks.
1281 * @author Ryan Beasley <ryanb@FreeBSD.org>
1284 sound_oss_sysinfo(oss_sysinfo
*si
)
1286 static char si_product
[] = "FreeBSD native OSS ABI";
1287 static char si_version
[] = __XSTRING(__FreeBSD_version
);
1288 static char si_license
[] = "BSD";
1289 static int intnbits
= sizeof(int) * 8; /* Better suited as macro?
1290 Must pester a C guru. */
1292 struct snddev_info
*d
;
1293 struct pcm_channel
*c
;
1298 strlcpy(si
->product
, si_product
, sizeof(si
->product
));
1299 strlcpy(si
->version
, si_version
, sizeof(si
->version
));
1300 si
->versionnum
= SOUND_VERSION
;
1301 strlcpy(si
->license
, si_license
, sizeof(si
->license
));
1304 * Iterate over PCM devices and their channels, gathering up data
1305 * for the numaudios, ncards, and openedaudio fields.
1308 bzero((void *)&si
->openedaudio
, sizeof(si
->openedaudio
));
1312 for (i
= 0; pcm_devclass
!= NULL
&&
1313 i
< devclass_get_maxunit(pcm_devclass
); i
++) {
1314 d
= devclass_get_softc(pcm_devclass
, i
);
1315 if (!PCM_REGISTERED(d
))
1318 /* XXX Need Giant magic entry ??? */
1320 /* See note in function's docblock */
1321 PCM_UNLOCKASSERT(d
);
1324 si
->numaudios
+= d
->devcount
;
1327 CHN_FOREACH(c
, d
, channels
.pcm
) {
1328 CHN_UNLOCKASSERT(c
);
1330 if (c
->flags
& CHN_F_BUSY
)
1331 si
->openedaudio
[j
/ intnbits
] |=
1332 (1 << (j
% intnbits
));
1339 si
->numaudioengines
= si
->numaudios
;
1341 si
->numsynths
= 0; /* OSSv4 docs: this field is obsolete */
1343 * @todo Collect num{midis,timers}.
1345 * Need access to sound/midi/midi.c::midistat_lock in order
1346 * to safely touch midi_devices and get a head count of, well,
1347 * MIDI devices. midistat_lock is a global static (i.e., local to
1348 * midi.c), but midi_devices is a regular global; should the mutex
1349 * be publicized, or is there another way to get this information?
1351 * NB: MIDI/sequencer stuff is currently on hold.
1355 si
->nummixers
= mixer_count
;
1356 si
->numcards
= ncards
;
1357 /* OSSv4 docs: Intended only for test apps; API doesn't
1358 really have much of a concept of cards. Shouldn't be
1359 used by applications. */
1362 * @todo Fill in "busy devices" fields.
1364 * si->openedmidi = " MIDI devices
1366 bzero((void *)&si
->openedmidi
, sizeof(si
->openedmidi
));
1369 * Si->filler is a reserved array, but according to docs each
1370 * element should be set to -1.
1372 for (i
= 0; i
< sizeof(si
->filler
)/sizeof(si
->filler
[0]); i
++)
1377 sound_oss_card_info(oss_card_info
*si
)
1379 struct snddev_info
*d
;
1384 for (i
= 0; pcm_devclass
!= NULL
&&
1385 i
< devclass_get_maxunit(pcm_devclass
); i
++) {
1386 d
= devclass_get_softc(pcm_devclass
, i
);
1387 if (!PCM_REGISTERED(d
))
1390 if (ncards
++ != si
->card
)
1393 PCM_UNLOCKASSERT(d
);
1396 strlcpy(si
->shortname
, device_get_nameunit(d
->dev
),
1397 sizeof(si
->shortname
));
1398 strlcpy(si
->longname
, device_get_desc(d
->dev
),
1399 sizeof(si
->longname
));
1400 strlcpy(si
->hw_info
, d
->status
, sizeof(si
->hw_info
));
1401 si
->intr_count
= si
->ack_count
= 0;
1410 /************************************************************************/
1413 sound_modevent(module_t mod
, int type
, void *data
)
1421 pcmsg_unrhdr
= new_unrhdr(1, INT_MAX
, NULL
);
1422 default_dsp
= make_dev(&dsp_ops
,
1423 PCMMKMINOR(PCMUNIT_DEFAULT
,
1425 UID_ROOT
, GID_WHEEL
, 0666, "dsp");
1426 devfs_clone_handler_add("dsp", dsp_clone
);
1427 default_mixer
= make_dev(&mixer_ops
,
1428 PCMMKMINOR(PCMUNIT_DEFAULT
, 0, 0),
1429 UID_ROOT
, GID_WHEEL
, 0666, "mixer");
1430 devfs_clone_handler_add("mixer", mixer_clone
);
1433 ret
= sndstat_acquire(curthread
);
1437 destroy_dev(default_dsp
);
1439 destroy_dev(default_mixer
);
1440 if (pcmsg_unrhdr
!= NULL
) {
1441 delete_unrhdr(pcmsg_unrhdr
);
1442 pcmsg_unrhdr
= NULL
;
1454 DEV_MODULE(sound
, sound_modevent
, NULL
);
1455 MODULE_VERSION(sound
, SOUND_MODVER
);