2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3 * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it)
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * $FreeBSD: src/sys/dev/sound/pcm/sound.c,v 1.93.2.5 2007/06/04 09:06:05 ariff Exp $
28 * $DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.12 2008/01/06 16:55:51 swildner Exp $
31 #include <dev/sound/pcm/sound.h>
32 #include <dev/sound/pcm/vchan.h>
33 #include <dev/sound/pcm/dsp.h>
34 #include <sys/sysctl.h>
35 #include <sys/devfs.h>
37 #include "feeder_if.h"
39 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.12 2008/01/06 16:55:51 swildner Exp $");
41 devclass_t pcm_devclass
;
43 int pcm_veto_load
= 1;
45 int snd_maxautovchans
= 4;
46 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans
);
48 SYSCTL_NODE(_hw
, OID_AUTO
, snd
, CTLFLAG_RD
, 0, "Sound driver");
50 static int sndstat_prepare_pcm(struct sbuf
*s
, device_t dev
, int verbose
);
52 struct sysctl_ctx_list
*
53 snd_sysctl_tree(device_t dev
)
55 struct snddev_info
*d
= device_get_softc(dev
);
57 return &d
->sysctl_tree
;
61 snd_sysctl_tree_top(device_t dev
)
63 struct snddev_info
*d
= device_get_softc(dev
);
65 return d
->sysctl_tree_top
;
69 snd_mtxcreate(const char *desc
, const char *type
)
74 m
= kmalloc(sizeof(*m
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
75 lockinit(m
, __DECONST(char *, type
), 0, LK_CANRECURSE
);
78 return (void *)0xcafebabe;
93 snd_mtxassert(void *m
)
108 lockmgr(lk
, LK_EXCLUSIVE
| LK_RETRY
);
113 snd_mtxunlock(void *m
)
118 lockmgr(lk
, LK_RELEASE
);
123 snd_mtxsleep(void *addr
, sndlock_t lock
, int flags
, const char *wmesg
, int timo
)
127 tsleep_interlock(addr
, flags
);
129 r
= tsleep(addr
, flags
| PINTERLOCKED
, wmesg
, timo
);
137 snd_setup_intr(device_t dev
, struct resource
*res
, int flags
, driver_intr_t hand
, void *param
, void **cookiep
)
140 flags
&= INTR_MPSAFE
;
141 flags
|= INTR_TYPE_AV
;
143 flags
= INTR_TYPE_AV
;
145 return bus_setup_intr(dev
, res
, flags
, hand
, param
, cookiep
, NULL
);
148 #ifndef PCM_DEBUG_MTX
150 pcm_lock(struct snddev_info
*d
)
152 snd_mtxlock(d
->lock
);
156 pcm_unlock(struct snddev_info
*d
)
158 snd_mtxunlock(d
->lock
);
163 pcm_getfakechan(struct snddev_info
*d
)
169 pcm_setvchans(struct snddev_info
*d
, int newcnt
)
171 struct snddev_channel
*sce
= NULL
;
172 struct pcm_channel
*c
= NULL
;
173 int err
= 0, vcnt
, dcnt
, i
;
177 if (!(d
->flags
& SD_F_AUTOVCHAN
)) {
182 vcnt
= d
->vchancount
;
183 dcnt
= d
->playcount
+ d
->reccount
;
185 if (newcnt
< 0 || (dcnt
+ newcnt
) > (PCMMAXCHAN
+ 1)) {
193 /* add new vchans - find a parent channel first */
194 SLIST_FOREACH(sce
, &d
->channels
, link
) {
197 if (c
->direction
== PCMDIR_PLAY
&&
198 ((c
->flags
& CHN_F_HAS_VCHAN
) ||
200 !(c
->flags
& (CHN_F_BUSY
| CHN_F_VIRTUAL
)))))
207 c
->flags
|= CHN_F_BUSY
;
208 while (err
== 0 && newcnt
> vcnt
) {
209 if (dcnt
> PCMMAXCHAN
) {
210 device_printf(d
->dev
, "%s: Maximum channel reached.\n", __func__
);
213 err
= vchan_create(c
);
217 } else if (err
== E2BIG
&& newcnt
> vcnt
)
218 device_printf(d
->dev
, "%s: err=%d Maximum channel reached.\n", __func__
, err
);
221 c
->flags
&= ~CHN_F_BUSY
;
223 } else if (newcnt
< vcnt
) {
224 #define ORPHAN_CDEVT(cdevt) \
225 ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \
226 (cdevt)->si_drv2 == NULL))
227 while (err
== 0 && newcnt
< vcnt
) {
229 SLIST_FOREACH(sce
, &d
->channels
, link
) {
232 if (c
->direction
== PCMDIR_PLAY
&&
233 (c
->flags
& CHN_F_VIRTUAL
) &&
235 if (!(c
->flags
& CHN_F_BUSY
) &&
236 ORPHAN_CDEVT(sce
->dsp_dev
))
239 * Either we're busy, or our cdev
240 * has been stolen by dsp_clone().
241 * Skip, and increase newcnt.
243 if (!(c
->flags
& CHN_F_BUSY
))
244 device_printf(d
->dev
,
245 "%s: <%s> somebody steal my cdev!\n",
256 err
= vchan_destroy(c
);
260 device_printf(d
->dev
,
261 "%s: WARNING: vchan_destroy() failed!",
271 /* return error status and a locked channel */
273 pcm_chnalloc(struct snddev_info
*d
, struct pcm_channel
**ch
, int direction
,
274 pid_t pid
, int chnum
)
276 struct pcm_channel
*c
;
277 struct snddev_channel
*sce
;
281 /* scan for a free channel */
282 SLIST_FOREACH(sce
, &d
->channels
, link
) {
285 if (c
->direction
== direction
&& !(c
->flags
& CHN_F_BUSY
)) {
286 if (chnum
< 0 || sce
->chan_num
== chnum
) {
287 c
->flags
|= CHN_F_BUSY
;
293 if (sce
->chan_num
== chnum
) {
294 if (c
->direction
!= direction
)
296 else if (c
->flags
& CHN_F_BUSY
)
302 } else if (c
->direction
== direction
&& (c
->flags
& CHN_F_BUSY
))
310 /* release a locked channel and unlock it */
312 pcm_chnrelease(struct pcm_channel
*c
)
315 c
->flags
&= ~CHN_F_BUSY
;
322 pcm_chnref(struct pcm_channel
*c
, int ref
)
333 pcm_inprog(struct snddev_info
*d
, int delta
)
349 pcm_setmaxautovchans(struct snddev_info
*d
, int num
)
351 if (num
> 0 && d
->vchancount
== 0)
353 else if (num
== 0 && d
->vchancount
> 0)
359 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS
)
361 struct snddev_info
*d
;
365 error
= sysctl_handle_int(oidp
, &unit
, sizeof(unit
), req
);
366 if (error
== 0 && req
->newptr
!= NULL
) {
367 if (unit
< 0 || (pcm_devclass
!= NULL
&&
368 unit
>= devclass_get_maxunit(pcm_devclass
)))
370 d
= devclass_get_softc(pcm_devclass
, unit
);
371 if (d
== NULL
|| SLIST_EMPTY(&d
->channels
))
377 SYSCTL_PROC(_hw_snd
, OID_AUTO
, unit
, CTLTYPE_INT
| CTLFLAG_RW
,
378 0, sizeof(int), sysctl_hw_snd_unit
, "I", "");
382 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS
)
384 struct snddev_info
*d
;
387 v
= snd_maxautovchans
;
388 error
= sysctl_handle_int(oidp
, &v
, sizeof(v
), req
);
389 if (error
== 0 && req
->newptr
!= NULL
) {
390 if (v
< 0 || v
> PCMMAXCHAN
)
392 if (pcm_devclass
!= NULL
&& v
!= snd_maxautovchans
) {
393 for (i
= 0; i
< devclass_get_maxunit(pcm_devclass
); i
++) {
394 d
= devclass_get_softc(pcm_devclass
, i
);
397 pcm_setmaxautovchans(d
, v
);
400 snd_maxautovchans
= v
;
404 SYSCTL_PROC(_hw_snd
, OID_AUTO
, maxautovchans
, CTLTYPE_INT
| CTLFLAG_RW
,
405 0, sizeof(int), sysctl_hw_snd_maxautovchans
, "I", "");
408 pcm_chn_create(struct snddev_info
*d
, struct pcm_channel
*parent
, kobj_class_t cls
, int dir
, void *devinfo
)
410 struct snddev_channel
*sce
;
411 struct pcm_channel
*ch
, *c
;
413 uint32_t flsearch
= 0;
414 int direction
, err
, rpnum
, *pnum
;
419 direction
= PCMDIR_PLAY
;
420 pnum
= &d
->playcount
;
425 direction
= PCMDIR_REC
;
431 direction
= PCMDIR_PLAY
;
432 pnum
= &d
->vchancount
;
433 flsearch
= CHN_F_VIRTUAL
;
440 ch
= kmalloc(sizeof(*ch
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
442 ch
->methods
= kobj_create(cls
, M_DEVBUF
, M_WAITOK
);
449 snd_mtxlock(d
->lock
);
452 SLIST_FOREACH(sce
, &d
->channels
, link
) {
454 if (direction
!= c
->direction
||
455 (c
->flags
& CHN_F_VIRTUAL
) != flsearch
)
457 if (ch
->num
== c
->num
)
461 device_printf(d
->dev
,
462 "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n",
463 __func__
, dirs
, ch
->num
, c
->num
);
465 goto retry_num_search
;
469 goto retry_num_search_out
;
472 SLIST_FOREACH(sce
, &d
->channels
, link
) {
474 if (direction
!= c
->direction
||
475 (c
->flags
& CHN_F_VIRTUAL
) != flsearch
)
477 if (ch
->num
== c
->num
) {
479 goto retry_num_search
;
483 retry_num_search_out
:
484 if (*pnum
!= rpnum
) {
485 device_printf(d
->dev
,
486 "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
487 __func__
, dirs
, *pnum
, rpnum
);
491 snd_mtxunlock(d
->lock
);
494 ch
->parentsnddev
= d
;
495 ch
->parentchannel
= parent
;
497 ksnprintf(ch
->name
, CHN_NAMELEN
, "%s:%s:%d", device_get_nameunit(ch
->dev
), dirs
, ch
->num
);
499 err
= chn_init(ch
, devinfo
, dir
, direction
);
501 device_printf(d
->dev
, "chn_init(%s) failed: err = %d\n", ch
->name
, err
);
502 kobj_delete(ch
->methods
, M_DEVBUF
);
504 snd_mtxlock(d
->lock
);
506 snd_mtxunlock(d
->lock
);
515 pcm_chn_iterate(struct snddev_info
*d
, void **cookie
)
517 struct snddev_channel
**last
= (struct snddev_channel
**)cookie
;
520 *last
= SLIST_FIRST(&d
->channels
);
522 *last
= SLIST_NEXT(*last
, link
);
527 return (*last
)->channel
;
531 pcm_chn_destroy(struct pcm_channel
*ch
)
533 struct snddev_info
*d
;
536 d
= ch
->parentsnddev
;
539 device_printf(d
->dev
, "chn_kill(%s) failed, err = %d\n", ch
->name
, err
);
543 kobj_delete(ch
->methods
, M_DEVBUF
);
550 pcm_chn_add(struct snddev_info
*d
, struct pcm_channel
*ch
)
552 struct snddev_channel
*sce
, *tmp
, *after
;
554 int device
= device_get_unit(d
->dev
);
558 * Note it's confusing nomenclature.
560 * device -> pcm_device
561 * unit -> pcm_channel
562 * channel -> snddev_channel
567 sce
= kmalloc(sizeof(*sce
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
569 snd_mtxlock(d
->lock
);
574 SLIST_FOREACH(tmp
, &d
->channels
, link
) {
575 if (sce
->chan_num
== tmp
->chan_num
)
579 device_printf(d
->dev
,
580 "%s: cdev numbering screwed (Expect: %d, Got: %d)\n",
581 __func__
, sce
->chan_num
, tmp
->chan_num
);
583 goto retry_chan_num_search
;
588 goto retry_chan_num_search_out
;
589 retry_chan_num_search
:
591 * Look for possible channel numbering collision. This may not
592 * be optimized, but it will ensure that no collision occured.
593 * Can be considered cheap since none of the locking/unlocking
594 * operations involved.
598 SLIST_FOREACH(tmp
, &d
->channels
, link
) {
599 if (sce
->chan_num
== tmp
->chan_num
) {
601 goto retry_chan_num_search
;
603 if (sce
->chan_num
> tmp
->chan_num
)
607 retry_chan_num_search_out
:
609 * Don't overflow PCMMKMINOR / PCMMAXCHAN.
611 if (sce
->chan_num
> PCMMAXCHAN
) {
612 snd_mtxunlock(d
->lock
);
613 device_printf(d
->dev
,
614 "%s: WARNING: sce->chan_num overflow! (%d)\n",
615 __func__
, sce
->chan_num
);
616 kfree(sce
, M_DEVBUF
);
619 if (d
->devcount
!= rdevcount
) {
620 device_printf(d
->dev
,
621 "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
622 __func__
, d
->devcount
, rdevcount
);
623 d
->devcount
= rdevcount
;
627 SLIST_INSERT_HEAD(&d
->channels
, sce
, link
);
629 SLIST_INSERT_AFTER(after
, sce
, link
);
634 SLIST_FOREACH(tmp
, &d
->channels
, link
) {
635 if (cnum
!= tmp
->chan_num
)
636 device_printf(d
->dev
,
637 "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n",
638 __func__
, cnum
, tmp
->chan_num
);
644 namelen
= strlen(ch
->name
);
645 if ((CHN_NAMELEN
- namelen
) > 10) { /* ":dspXX.YYY" */
646 ksnprintf(ch
->name
+ namelen
,
647 CHN_NAMELEN
- namelen
, ":dsp%d.%d",
648 device
, sce
->chan_num
);
650 snd_mtxunlock(d
->lock
);
656 pcm_chn_remove(struct snddev_info
*d
, struct pcm_channel
*ch
)
658 struct snddev_channel
*sce
;
663 if (!mtx_owned(d
->lock
)) {
664 snd_mtxlock(d
->lock
);
669 SLIST_FOREACH(sce
, &d
->channels
, link
) {
670 if (sce
->channel
== ch
)
675 snd_mtxunlock(d
->lock
);
679 SLIST_REMOVE(&d
->channels
, sce
, snddev_channel
, link
);
681 if (ch
->flags
& CHN_F_VIRTUAL
)
683 else if (ch
->direction
== PCMDIR_REC
)
690 snd_mtxunlock(d
->lock
);
692 kfree(sce
, M_DEVBUF
);
698 pcm_addchan(device_t dev
, int dir
, kobj_class_t cls
, void *devinfo
)
700 struct snddev_info
*d
= device_get_softc(dev
);
701 struct pcm_channel
*ch
;
704 ch
= pcm_chn_create(d
, NULL
, cls
, dir
, devinfo
);
706 device_printf(d
->dev
, "pcm_chn_create(%s, %d, %p) failed\n", cls
->name
, dir
, devinfo
);
710 err
= pcm_chn_add(d
, ch
);
712 device_printf(d
->dev
, "pcm_chn_add(%s) failed, err=%d\n", ch
->name
, err
);
721 pcm_killchan(device_t dev
)
723 struct snddev_info
*d
= device_get_softc(dev
);
724 struct snddev_channel
*sce
;
725 struct pcm_channel
*ch
;
728 sce
= SLIST_FIRST(&d
->channels
);
731 error
= pcm_chn_remove(d
, sce
->channel
);
734 return (pcm_chn_destroy(ch
));
738 pcm_setstatus(device_t dev
, char *str
)
740 struct snddev_info
*d
= device_get_softc(dev
);
742 snd_mtxlock(d
->lock
);
743 strncpy(d
->status
, str
, SND_STATUSLEN
);
744 snd_mtxunlock(d
->lock
);
745 if (snd_maxautovchans
> 0)
751 pcm_getflags(device_t dev
)
753 struct snddev_info
*d
= device_get_softc(dev
);
759 pcm_setflags(device_t dev
, uint32_t val
)
761 struct snddev_info
*d
= device_get_softc(dev
);
767 pcm_getdevinfo(device_t dev
)
769 struct snddev_info
*d
= device_get_softc(dev
);
775 pcm_getbuffersize(device_t dev
, unsigned int min
, unsigned int deflt
, unsigned int max
)
777 struct snddev_info
*d
= device_get_softc(dev
);
781 if (resource_int_value(device_get_name(dev
), device_get_unit(dev
), "buffersize", &sz
) == 0) {
785 device_printf(dev
, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x
, min
, max
, sz
);
792 device_printf(dev
, "'buffersize=%d' hint is not a power of 2, using %d\n", sz
, x
);
805 pcm_register(device_t dev
, void *devinfo
, int numplay
, int numrec
)
807 struct snddev_info
*d
= device_get_softc(dev
);
810 device_printf(dev
, "disabled due to an error while initialising: %d\n", pcm_veto_load
);
815 d
->lock
= snd_mtxcreate(device_get_nameunit(dev
), "sound cdev");
819 * d->flags should be cleared by the allocator of the softc.
820 * We cannot clear this field here because several devices set
821 * this flag before calling pcm_register().
826 d
->devinfo
= devinfo
;
833 SLIST_INIT(&d
->channels
);
835 if ((numplay
== 0 || numrec
== 0) && numplay
!= numrec
)
836 d
->flags
|= SD_F_SIMPLEX
;
838 d
->fakechan
= fkchan_setup(dev
);
839 chn_init(d
->fakechan
, NULL
, 0, 0);
842 sysctl_ctx_init(&d
->sysctl_tree
);
843 d
->sysctl_tree_top
= SYSCTL_ADD_NODE(&d
->sysctl_tree
,
844 SYSCTL_STATIC_CHILDREN(_hw_snd
), OID_AUTO
,
845 device_get_nameunit(dev
), CTLFLAG_RD
, 0, "");
846 if (d
->sysctl_tree_top
== NULL
) {
847 sysctl_ctx_free(&d
->sysctl_tree
);
850 SYSCTL_ADD_INT(snd_sysctl_tree(dev
), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev
)),
851 OID_AUTO
, "buffersize", CTLFLAG_RD
, &d
->bufsz
, 0, "");
854 d
->flags
|= SD_F_AUTOVCHAN
;
858 /* XXX use make_autoclone_dev? */
859 /* XXX PCMMAXCHAN can be created for regular channels */
860 d
->dsp_clonedev
= make_dev(&dsp_cdevsw
,
861 PCMMKMINOR(device_get_unit(dev
), PCMMAXCHAN
),
862 UID_ROOT
, GID_WHEEL
, 0666, "dsp%d",
863 device_get_unit(dev
));
864 devfs_clone_handler_add(devtoname(d
->dsp_clonedev
), dsp_clone
);
866 sndstat_register(dev
, d
->status
, sndstat_prepare_pcm
);
869 snd_mtxfree(d
->lock
);
874 pcm_unregister(device_t dev
)
876 struct snddev_info
*d
= device_get_softc(dev
);
877 struct snddev_channel
*sce
;
878 struct pcmchan_children
*pce
;
879 struct pcm_channel
*ch
;
881 if (sndstat_acquire() != 0) {
882 device_printf(dev
, "unregister: sndstat busy\n");
886 snd_mtxlock(d
->lock
);
888 device_printf(dev
, "unregister: operation in progress\n");
889 snd_mtxunlock(d
->lock
);
894 SLIST_FOREACH(sce
, &d
->channels
, link
) {
896 if (ch
->refcount
> 0) {
897 device_printf(dev
, "unregister: channel %s busy (pid %d)\n", ch
->name
, ch
->pid
);
898 snd_mtxunlock(d
->lock
);
904 if (mixer_uninit(dev
) == EBUSY
) {
905 device_printf(dev
, "unregister: mixer busy\n");
906 snd_mtxunlock(d
->lock
);
911 SLIST_FOREACH(sce
, &d
->channels
, link
) {
913 destroy_dev(sce
->dsp_dev
);
920 pce
= SLIST_FIRST(&ch
->children
);
921 while (pce
!= NULL
) {
923 device_printf(d
->dev
, "<%s> removing <%s>\n",
924 ch
->name
, (pce
->channel
!= NULL
) ?
925 pce
->channel
->name
: "unknown");
927 SLIST_REMOVE(&ch
->children
, pce
, pcmchan_children
, link
);
928 kfree(pce
, M_DEVBUF
);
929 pce
= SLIST_FIRST(&ch
->children
);
934 d
->sysctl_tree_top
= NULL
;
935 sysctl_ctx_free(&d
->sysctl_tree
);
939 SLIST_FOREACH(sce
, &d
->channels
, link
) {
943 if (!SLIST_EMPTY(&ch
->children
))
944 device_printf(d
->dev
, "%s: WARNING: <%s> dangling child!\n",
948 while (!SLIST_EMPTY(&d
->channels
))
951 chn_kill(d
->fakechan
);
952 fkchan_kill(d
->fakechan
);
954 destroy_autoclone_dev(d
->dsp_clonedev
, NULL
);
957 device_printf(d
->dev
, "%s: devcount=%u, playcount=%u, "
958 "reccount=%u, vchancount=%u\n",
959 __func__
, d
->devcount
, d
->playcount
, d
->reccount
,
962 snd_mtxunlock(d
->lock
);
963 snd_mtxfree(d
->lock
);
964 sndstat_unregister(dev
);
969 /************************************************************************/
972 sndstat_prepare_pcm(struct sbuf
*s
, device_t dev
, int verbose
)
974 struct snddev_info
*d
;
975 struct snddev_channel
*sce
;
976 struct pcm_channel
*c
;
977 struct pcm_feeder
*f
;
983 d
= device_get_softc(dev
);
987 snd_mtxlock(d
->lock
);
988 if (!SLIST_EMPTY(&d
->channels
)) {
990 SLIST_FOREACH(sce
, &d
->channels
, link
) {
992 if (c
->direction
== PCMDIR_PLAY
) {
993 if (c
->flags
& CHN_F_VIRTUAL
)
1000 sbuf_printf(s
, " (%dp/%dr/%dv channels%s%s)", d
->playcount
, d
->reccount
, d
->vchancount
,
1001 (d
->flags
& SD_F_SIMPLEX
)? "" : " duplex",
1003 (device_get_unit(dev
) == snd_unit
)? " default" : ""
1010 snd_mtxunlock(d
->lock
);
1014 SLIST_FOREACH(sce
, &d
->channels
, link
) {
1017 KASSERT(c
->bufhard
!= NULL
&& c
->bufsoft
!= NULL
,
1018 ("hosed pcm channel setup"));
1020 sbuf_printf(s
, "\n\t");
1022 /* it would be better to indent child channels */
1023 sbuf_printf(s
, "%s[%s]: ", c
->parentchannel
? c
->parentchannel
->name
: "", c
->name
);
1024 sbuf_printf(s
, "spd %d", c
->speed
);
1025 if (c
->speed
!= sndbuf_getspd(c
->bufhard
))
1026 sbuf_printf(s
, "/%d", sndbuf_getspd(c
->bufhard
));
1027 sbuf_printf(s
, ", fmt 0x%08x", c
->format
);
1028 if (c
->format
!= sndbuf_getfmt(c
->bufhard
))
1029 sbuf_printf(s
, "/0x%08x", sndbuf_getfmt(c
->bufhard
));
1030 sbuf_printf(s
, ", flags 0x%08x, 0x%08x", c
->flags
, c
->feederflags
);
1032 sbuf_printf(s
, ", pid %d", c
->pid
);
1033 sbuf_printf(s
, "\n\t");
1035 sbuf_printf(s
, "interrupts %d, ", c
->interrupts
);
1036 if (c
->direction
== PCMDIR_REC
)
1037 sbuf_printf(s
, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1038 c
->xruns
, sndbuf_getfree(c
->bufhard
), sndbuf_getfree(c
->bufsoft
),
1039 sndbuf_getsize(c
->bufhard
), sndbuf_getblksz(c
->bufhard
),
1040 sndbuf_getblkcnt(c
->bufhard
),
1041 sndbuf_getsize(c
->bufsoft
), sndbuf_getblksz(c
->bufsoft
),
1042 sndbuf_getblkcnt(c
->bufsoft
));
1044 sbuf_printf(s
, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1045 c
->xruns
, sndbuf_getready(c
->bufsoft
),
1046 sndbuf_getsize(c
->bufhard
), sndbuf_getblksz(c
->bufhard
),
1047 sndbuf_getblkcnt(c
->bufhard
),
1048 sndbuf_getsize(c
->bufsoft
), sndbuf_getblksz(c
->bufsoft
),
1049 sndbuf_getblkcnt(c
->bufsoft
));
1050 sbuf_printf(s
, "\n\t");
1052 sbuf_printf(s
, "{%s}", (c
->direction
== PCMDIR_REC
)? "hardware" : "userland");
1053 sbuf_printf(s
, " -> ");
1055 while (f
->source
!= NULL
)
1058 sbuf_printf(s
, "%s", f
->class->name
);
1059 if (f
->desc
->type
== FEEDER_FMT
)
1060 sbuf_printf(s
, "(0x%08x -> 0x%08x)", f
->desc
->in
, f
->desc
->out
);
1061 if (f
->desc
->type
== FEEDER_RATE
)
1062 sbuf_printf(s
, "(%d -> %d)", FEEDER_GET(f
, FEEDRATE_SRC
), FEEDER_GET(f
, FEEDRATE_DST
));
1063 if (f
->desc
->type
== FEEDER_ROOT
|| f
->desc
->type
== FEEDER_MIXER
||
1064 f
->desc
->type
== FEEDER_VOLUME
)
1065 sbuf_printf(s
, "(0x%08x)", f
->desc
->out
);
1066 sbuf_printf(s
, " -> ");
1069 sbuf_printf(s
, "{%s}", (c
->direction
== PCMDIR_REC
)? "userland" : "hardware");
1072 sbuf_printf(s
, " (mixer only)");
1073 snd_mtxunlock(d
->lock
);
1078 /************************************************************************/
1080 #ifdef SND_DYNSYSCTL
1082 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS
)
1084 struct snddev_info
*d
;
1089 newcnt
= d
->vchancount
;
1090 err
= sysctl_handle_int(oidp
, &newcnt
, sizeof(newcnt
), req
);
1092 if (err
== 0 && req
->newptr
!= NULL
&& d
->vchancount
!= newcnt
)
1093 err
= pcm_setvchans(d
, newcnt
);
1099 /************************************************************************/
1102 sound_modevent(module_t mod
, int type
, void *data
)
1105 return (midi_modevent(mod
, type
, data
));
1111 DEV_MODULE(sound
, sound_modevent
, NULL
);
1112 MODULE_VERSION(sound
, SOUND_MODVER
);