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.11 2007/06/16 20:07:22 dillon 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>
36 #include "feeder_if.h"
38 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.11 2007/06/16 20:07:22 dillon Exp $");
40 devclass_t pcm_devclass
;
42 int pcm_veto_load
= 1;
46 TUNABLE_INT("hw.snd.unit", &snd_unit
);
49 int snd_maxautovchans
= 4;
50 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans
);
52 SYSCTL_NODE(_hw
, OID_AUTO
, snd
, CTLFLAG_RD
, 0, "Sound driver");
54 static int sndstat_prepare_pcm(struct sbuf
*s
, device_t dev
, int verbose
);
56 struct sysctl_ctx_list
*
57 snd_sysctl_tree(device_t dev
)
59 struct snddev_info
*d
= device_get_softc(dev
);
61 return &d
->sysctl_tree
;
65 snd_sysctl_tree_top(device_t dev
)
67 struct snddev_info
*d
= device_get_softc(dev
);
69 return d
->sysctl_tree_top
;
73 snd_mtxcreate(const char *desc
, const char *type
)
78 m
= kmalloc(sizeof(*m
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
81 lockinit(m
, __DECONST(char *, type
), 0, LK_CANRECURSE
);
84 return (void *)0xcafebabe;
99 snd_mtxassert(void *m
)
114 lockmgr(lk
, LK_EXCLUSIVE
| LK_RETRY
);
119 snd_mtxunlock(void *m
)
124 lockmgr(lk
, LK_RELEASE
);
129 snd_mtxsleep(void *addr
, sndlock_t lock
, int flags
, const char *wmesg
, int timo
)
134 tsleep_interlock(addr
);
136 r
= tsleep(addr
, flags
, wmesg
, timo
);
145 snd_setup_intr(device_t dev
, struct resource
*res
, int flags
, driver_intr_t hand
, void *param
, void **cookiep
)
148 flags
&= INTR_MPSAFE
;
149 flags
|= INTR_TYPE_AV
;
151 flags
= INTR_TYPE_AV
;
153 return bus_setup_intr(dev
, res
, flags
, hand
, param
, cookiep
, NULL
);
156 #ifndef PCM_DEBUG_MTX
158 pcm_lock(struct snddev_info
*d
)
160 snd_mtxlock(d
->lock
);
164 pcm_unlock(struct snddev_info
*d
)
166 snd_mtxunlock(d
->lock
);
171 pcm_getfakechan(struct snddev_info
*d
)
177 pcm_setvchans(struct snddev_info
*d
, int newcnt
)
179 struct snddev_channel
*sce
= NULL
;
180 struct pcm_channel
*c
= NULL
;
181 int err
= 0, vcnt
, dcnt
, i
;
185 if (!(d
->flags
& SD_F_AUTOVCHAN
)) {
190 vcnt
= d
->vchancount
;
191 dcnt
= d
->playcount
+ d
->reccount
;
193 if (newcnt
< 0 || (dcnt
+ newcnt
) > (PCMMAXCHAN
+ 1)) {
201 /* add new vchans - find a parent channel first */
202 SLIST_FOREACH(sce
, &d
->channels
, link
) {
205 if (c
->direction
== PCMDIR_PLAY
&&
206 ((c
->flags
& CHN_F_HAS_VCHAN
) ||
208 !(c
->flags
& (CHN_F_BUSY
| CHN_F_VIRTUAL
)))))
215 c
->flags
|= CHN_F_BUSY
;
216 while (err
== 0 && newcnt
> vcnt
) {
217 if (dcnt
> PCMMAXCHAN
) {
218 device_printf(d
->dev
, "%s: Maximum channel reached.\n", __func__
);
221 err
= vchan_create(c
);
225 } else if (err
== E2BIG
&& newcnt
> vcnt
)
226 device_printf(d
->dev
, "%s: err=%d Maximum channel reached.\n", __func__
, err
);
229 c
->flags
&= ~CHN_F_BUSY
;
231 } else if (newcnt
< vcnt
) {
232 #define ORPHAN_CDEVT(cdevt) \
233 ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \
234 (cdevt)->si_drv2 == NULL))
235 while (err
== 0 && newcnt
< vcnt
) {
237 SLIST_FOREACH(sce
, &d
->channels
, link
) {
240 if (c
->direction
== PCMDIR_PLAY
&&
241 (c
->flags
& CHN_F_VIRTUAL
) &&
243 if (!(c
->flags
& CHN_F_BUSY
) &&
244 ORPHAN_CDEVT(sce
->dsp_devt
) &&
245 ORPHAN_CDEVT(sce
->dspW_devt
) &&
246 ORPHAN_CDEVT(sce
->audio_devt
) &&
247 ORPHAN_CDEVT(sce
->dspr_devt
))
250 * Either we're busy, or our cdev
251 * has been stolen by dsp_clone().
252 * Skip, and increase newcnt.
254 if (!(c
->flags
& CHN_F_BUSY
))
255 device_printf(d
->dev
,
256 "%s: <%s> somebody steal my cdev!\n",
267 err
= vchan_destroy(c
);
271 device_printf(d
->dev
,
272 "%s: WARNING: vchan_destroy() failed!",
282 /* return error status and a locked channel */
284 pcm_chnalloc(struct snddev_info
*d
, struct pcm_channel
**ch
, int direction
,
285 pid_t pid
, int chnum
)
287 struct pcm_channel
*c
;
288 struct snddev_channel
*sce
;
293 /* scan for a free channel */
294 SLIST_FOREACH(sce
, &d
->channels
, link
) {
297 if (c
->direction
== direction
&& !(c
->flags
& CHN_F_BUSY
)) {
298 if (chnum
< 0 || sce
->chan_num
== chnum
) {
299 c
->flags
|= CHN_F_BUSY
;
305 if (sce
->chan_num
== chnum
) {
306 if (c
->direction
!= direction
)
308 else if (c
->flags
& CHN_F_BUSY
)
314 } else if (c
->direction
== direction
&& (c
->flags
& CHN_F_BUSY
))
319 /* no channel available */
320 if (chnum
== -1 && direction
== PCMDIR_PLAY
&& d
->vchancount
> 0 &&
321 d
->vchancount
< snd_maxautovchans
&&
322 d
->devcount
<= PCMMAXCHAN
) {
323 err
= pcm_setvchans(d
, d
->vchancount
+ 1);
333 /* release a locked channel and unlock it */
335 pcm_chnrelease(struct pcm_channel
*c
)
338 c
->flags
&= ~CHN_F_BUSY
;
345 pcm_chnref(struct pcm_channel
*c
, int ref
)
356 pcm_inprog(struct snddev_info
*d
, int delta
)
372 pcm_setmaxautovchans(struct snddev_info
*d
, int num
)
374 if (num
> 0 && d
->vchancount
== 0)
376 else if (num
== 0 && d
->vchancount
> 0)
382 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS
)
384 struct snddev_info
*d
;
388 error
= sysctl_handle_int(oidp
, &unit
, sizeof(unit
), req
);
389 if (error
== 0 && req
->newptr
!= NULL
) {
390 if (unit
< 0 || (pcm_devclass
!= NULL
&&
391 unit
>= devclass_get_maxunit(pcm_devclass
)))
393 d
= devclass_get_softc(pcm_devclass
, unit
);
394 if (d
== NULL
|| SLIST_EMPTY(&d
->channels
))
400 SYSCTL_PROC(_hw_snd
, OID_AUTO
, unit
, CTLTYPE_INT
| CTLFLAG_RW
,
401 0, sizeof(int), sysctl_hw_snd_unit
, "I", "");
405 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS
)
407 struct snddev_info
*d
;
410 v
= snd_maxautovchans
;
411 error
= sysctl_handle_int(oidp
, &v
, sizeof(v
), req
);
412 if (error
== 0 && req
->newptr
!= NULL
) {
413 if (v
< 0 || v
> PCMMAXCHAN
)
415 if (pcm_devclass
!= NULL
&& v
!= snd_maxautovchans
) {
416 for (i
= 0; i
< devclass_get_maxunit(pcm_devclass
); i
++) {
417 d
= devclass_get_softc(pcm_devclass
, i
);
420 pcm_setmaxautovchans(d
, v
);
423 snd_maxautovchans
= v
;
427 SYSCTL_PROC(_hw_snd
, OID_AUTO
, maxautovchans
, CTLTYPE_INT
| CTLFLAG_RW
,
428 0, sizeof(int), sysctl_hw_snd_maxautovchans
, "I", "");
431 pcm_chn_create(struct snddev_info
*d
, struct pcm_channel
*parent
, kobj_class_t cls
, int dir
, void *devinfo
)
433 struct snddev_channel
*sce
;
434 struct pcm_channel
*ch
, *c
;
436 uint32_t flsearch
= 0;
437 int direction
, err
, rpnum
, *pnum
;
442 direction
= PCMDIR_PLAY
;
443 pnum
= &d
->playcount
;
448 direction
= PCMDIR_REC
;
454 direction
= PCMDIR_PLAY
;
455 pnum
= &d
->vchancount
;
456 flsearch
= CHN_F_VIRTUAL
;
463 ch
= kmalloc(sizeof(*ch
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
467 ch
->methods
= kobj_create(cls
, M_DEVBUF
, M_WAITOK
);
474 snd_mtxlock(d
->lock
);
477 SLIST_FOREACH(sce
, &d
->channels
, link
) {
479 if (direction
!= c
->direction
||
480 (c
->flags
& CHN_F_VIRTUAL
) != flsearch
)
482 if (ch
->num
== c
->num
)
486 device_printf(d
->dev
,
487 "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n",
488 __func__
, dirs
, ch
->num
, c
->num
);
490 goto retry_num_search
;
494 goto retry_num_search_out
;
497 SLIST_FOREACH(sce
, &d
->channels
, link
) {
499 if (direction
!= c
->direction
||
500 (c
->flags
& CHN_F_VIRTUAL
) != flsearch
)
502 if (ch
->num
== c
->num
) {
504 goto retry_num_search
;
508 retry_num_search_out
:
509 if (*pnum
!= rpnum
) {
510 device_printf(d
->dev
,
511 "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
512 __func__
, dirs
, *pnum
, rpnum
);
516 snd_mtxunlock(d
->lock
);
519 ch
->parentsnddev
= d
;
520 ch
->parentchannel
= parent
;
522 ksnprintf(ch
->name
, CHN_NAMELEN
, "%s:%s:%d", device_get_nameunit(ch
->dev
), dirs
, ch
->num
);
524 err
= chn_init(ch
, devinfo
, dir
, direction
);
526 device_printf(d
->dev
, "chn_init(%s) failed: err = %d\n", ch
->name
, err
);
527 kobj_delete(ch
->methods
, M_DEVBUF
);
529 snd_mtxlock(d
->lock
);
531 snd_mtxunlock(d
->lock
);
540 pcm_chn_iterate(struct snddev_info
*d
, void **cookie
)
542 struct snddev_channel
**last
= (struct snddev_channel
**)cookie
;
545 *last
= SLIST_FIRST(&d
->channels
);
547 *last
= SLIST_NEXT(*last
, link
);
552 return (*last
)->channel
;
556 pcm_chn_destroy(struct pcm_channel
*ch
)
558 struct snddev_info
*d
;
561 d
= ch
->parentsnddev
;
564 device_printf(d
->dev
, "chn_kill(%s) failed, err = %d\n", ch
->name
, err
);
568 kobj_delete(ch
->methods
, M_DEVBUF
);
575 pcm_chn_add(struct snddev_info
*d
, struct pcm_channel
*ch
)
577 struct snddev_channel
*sce
, *tmp
, *after
;
579 int device
= device_get_unit(d
->dev
);
583 * Note it's confusing nomenclature.
585 * device -> pcm_device
586 * unit -> pcm_channel
587 * channel -> snddev_channel
592 sce
= kmalloc(sizeof(*sce
), M_DEVBUF
, M_WAITOK
| M_ZERO
);
597 snd_mtxlock(d
->lock
);
602 SLIST_FOREACH(tmp
, &d
->channels
, link
) {
603 if (sce
->chan_num
== tmp
->chan_num
)
607 device_printf(d
->dev
,
608 "%s: cdev numbering screwed (Expect: %d, Got: %d)\n",
609 __func__
, sce
->chan_num
, tmp
->chan_num
);
611 goto retry_chan_num_search
;
616 goto retry_chan_num_search_out
;
617 retry_chan_num_search
:
619 * Look for possible channel numbering collision. This may not
620 * be optimized, but it will ensure that no collision occured.
621 * Can be considered cheap since none of the locking/unlocking
622 * operations involved.
626 SLIST_FOREACH(tmp
, &d
->channels
, link
) {
627 if (sce
->chan_num
== tmp
->chan_num
) {
629 goto retry_chan_num_search
;
631 if (sce
->chan_num
> tmp
->chan_num
)
635 retry_chan_num_search_out
:
637 * Don't overflow PCMMKMINOR / PCMMAXCHAN.
639 if (sce
->chan_num
> PCMMAXCHAN
) {
640 snd_mtxunlock(d
->lock
);
641 device_printf(d
->dev
,
642 "%s: WARNING: sce->chan_num overflow! (%d)\n",
643 __func__
, sce
->chan_num
);
644 kfree(sce
, M_DEVBUF
);
647 if (d
->devcount
!= rdevcount
) {
648 device_printf(d
->dev
,
649 "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
650 __func__
, d
->devcount
, rdevcount
);
651 d
->devcount
= rdevcount
;
655 SLIST_INSERT_HEAD(&d
->channels
, sce
, link
);
657 SLIST_INSERT_AFTER(after
, sce
, link
);
662 SLIST_FOREACH(tmp
, &d
->channels
, link
) {
663 if (cnum
!= tmp
->chan_num
)
664 device_printf(d
->dev
,
665 "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n",
666 __func__
, cnum
, tmp
->chan_num
);
672 namelen
= strlen(ch
->name
);
673 if ((CHN_NAMELEN
- namelen
) > 10) { /* ":dspXX.YYY" */
674 ksnprintf(ch
->name
+ namelen
,
675 CHN_NAMELEN
- namelen
, ":dsp%d.%d",
676 device
, sce
->chan_num
);
678 snd_mtxunlock(d
->lock
);
681 * I will revisit these someday, and nuke it mercilessly..
683 dev_ops_add(&dsp_cdevsw
,
684 PCMMKMINOR(-1, -1, 0),
685 PCMMKMINOR(device
, SND_DEV_DSP
, sce
->chan_num
));
686 sce
->dsp_devt
= make_dev(&dsp_cdevsw
,
687 PCMMKMINOR(device
, SND_DEV_DSP
, sce
->chan_num
),
688 UID_ROOT
, GID_WHEEL
, 0666, "dsp%d.%d",
689 device
, sce
->chan_num
);
690 reference_dev(sce
->dsp_devt
);
692 dev_ops_add(&dsp_cdevsw
,
693 PCMMKMINOR(-1, -1, 0),
694 PCMMKMINOR(device
, SND_DEV_DSP16
, sce
->chan_num
));
695 sce
->dspW_devt
= make_dev(&dsp_cdevsw
,
696 PCMMKMINOR(device
, SND_DEV_DSP16
, sce
->chan_num
),
697 UID_ROOT
, GID_WHEEL
, 0666, "dspW%d.%d",
698 device
, sce
->chan_num
);
699 reference_dev(sce
->dspW_devt
);
702 dev_ops_add(&dsp_cdevsw,
703 PCMMKMINOR(-1, -1, 0),
704 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num));
705 sce->audio_devt = make_dev(&dsp_cdevsw,
706 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
707 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
708 device, sce->chan_num);
710 sce
->audio_devt
= sce
->dsp_devt
;
711 reference_dev(sce
->audio_devt
);
713 if (ch
->direction
== PCMDIR_REC
) {
714 dev_ops_add(&dsp_cdevsw
,
715 PCMMKMINOR(-1, -1, 0),
716 PCMMKMINOR(device
, SND_DEV_DSPREC
, sce
->chan_num
));
717 sce
->dspr_devt
= make_dev(&dsp_cdevsw
,
718 PCMMKMINOR(device
, SND_DEV_DSPREC
,
719 sce
->chan_num
), UID_ROOT
, GID_WHEEL
,
720 0666, "dspr%d.%d", device
, sce
->chan_num
);
721 reference_dev(sce
->dspr_devt
);
728 pcm_chn_remove(struct snddev_info
*d
, struct pcm_channel
*ch
)
730 struct snddev_channel
*sce
;
735 if (!mtx_owned(d
->lock
)) {
736 snd_mtxlock(d
->lock
);
741 SLIST_FOREACH(sce
, &d
->channels
, link
) {
742 if (sce
->channel
== ch
)
747 snd_mtxunlock(d
->lock
);
751 SLIST_REMOVE(&d
->channels
, sce
, snddev_channel
, link
);
753 if (ch
->flags
& CHN_F_VIRTUAL
)
755 else if (ch
->direction
== PCMDIR_REC
)
762 snd_mtxunlock(d
->lock
);
764 kfree(sce
, M_DEVBUF
);
770 pcm_addchan(device_t dev
, int dir
, kobj_class_t cls
, void *devinfo
)
772 struct snddev_info
*d
= device_get_softc(dev
);
773 struct pcm_channel
*ch
;
776 ch
= pcm_chn_create(d
, NULL
, cls
, dir
, devinfo
);
778 device_printf(d
->dev
, "pcm_chn_create(%s, %d, %p) failed\n", cls
->name
, dir
, devinfo
);
782 err
= pcm_chn_add(d
, ch
);
784 device_printf(d
->dev
, "pcm_chn_add(%s) failed, err=%d\n", ch
->name
, err
);
793 pcm_killchan(device_t dev
)
795 struct snddev_info
*d
= device_get_softc(dev
);
796 struct snddev_channel
*sce
;
797 struct pcm_channel
*ch
;
800 sce
= SLIST_FIRST(&d
->channels
);
803 error
= pcm_chn_remove(d
, sce
->channel
);
806 return (pcm_chn_destroy(ch
));
810 pcm_setstatus(device_t dev
, char *str
)
812 struct snddev_info
*d
= device_get_softc(dev
);
814 snd_mtxlock(d
->lock
);
815 strncpy(d
->status
, str
, SND_STATUSLEN
);
816 snd_mtxunlock(d
->lock
);
817 if (snd_maxautovchans
> 0)
823 pcm_getflags(device_t dev
)
825 struct snddev_info
*d
= device_get_softc(dev
);
831 pcm_setflags(device_t dev
, uint32_t val
)
833 struct snddev_info
*d
= device_get_softc(dev
);
839 pcm_getdevinfo(device_t dev
)
841 struct snddev_info
*d
= device_get_softc(dev
);
847 pcm_getbuffersize(device_t dev
, unsigned int min
, unsigned int deflt
, unsigned int max
)
849 struct snddev_info
*d
= device_get_softc(dev
);
853 if (resource_int_value(device_get_name(dev
), device_get_unit(dev
), "buffersize", &sz
) == 0) {
857 device_printf(dev
, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x
, min
, max
, sz
);
864 device_printf(dev
, "'buffersize=%d' hint is not a power of 2, using %d\n", sz
, x
);
877 pcm_register(device_t dev
, void *devinfo
, int numplay
, int numrec
)
879 struct snddev_info
*d
= device_get_softc(dev
);
882 device_printf(dev
, "disabled due to an error while initialising: %d\n", pcm_veto_load
);
887 d
->lock
= snd_mtxcreate(device_get_nameunit(dev
), "sound cdev");
891 * d->flags should be cleared by the allocator of the softc.
892 * We cannot clear this field here because several devices set
893 * this flag before calling pcm_register().
898 d
->devinfo
= devinfo
;
905 SLIST_INIT(&d
->channels
);
907 if ((numplay
== 0 || numrec
== 0) && numplay
!= numrec
)
908 d
->flags
|= SD_F_SIMPLEX
;
910 d
->fakechan
= fkchan_setup(dev
);
911 chn_init(d
->fakechan
, NULL
, 0, 0);
914 sysctl_ctx_init(&d
->sysctl_tree
);
915 d
->sysctl_tree_top
= SYSCTL_ADD_NODE(&d
->sysctl_tree
,
916 SYSCTL_STATIC_CHILDREN(_hw_snd
), OID_AUTO
,
917 device_get_nameunit(dev
), CTLFLAG_RD
, 0, "");
918 if (d
->sysctl_tree_top
== NULL
) {
919 sysctl_ctx_free(&d
->sysctl_tree
);
922 SYSCTL_ADD_INT(snd_sysctl_tree(dev
), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev
)),
923 OID_AUTO
, "buffersize", CTLFLAG_RD
, &d
->bufsz
, 0, "");
926 d
->flags
|= SD_F_AUTOVCHAN
;
930 sndstat_register(dev
, d
->status
, sndstat_prepare_pcm
);
933 snd_mtxfree(d
->lock
);
938 pcm_unregister(device_t dev
)
940 struct snddev_info
*d
= device_get_softc(dev
);
941 struct snddev_channel
*sce
;
942 struct pcmchan_children
*pce
;
943 struct pcm_channel
*ch
;
945 if (sndstat_acquire() != 0) {
946 device_printf(dev
, "unregister: sndstat busy\n");
950 snd_mtxlock(d
->lock
);
952 device_printf(dev
, "unregister: operation in progress\n");
953 snd_mtxunlock(d
->lock
);
958 SLIST_FOREACH(sce
, &d
->channels
, link
) {
960 if (ch
->refcount
> 0) {
961 device_printf(dev
, "unregister: channel %s busy (pid %d)\n", ch
->name
, ch
->pid
);
962 snd_mtxunlock(d
->lock
);
968 if (mixer_uninit(dev
) == EBUSY
) {
969 device_printf(dev
, "unregister: mixer busy\n");
970 snd_mtxunlock(d
->lock
);
975 SLIST_FOREACH(sce
, &d
->channels
, link
) {
976 int unit
= device_get_unit(d
->dev
);
979 release_dev(sce
->dsp_devt
);
980 dev_ops_remove(&dsp_cdevsw
,
981 PCMMKMINOR(-1, -1, 0),
982 PCMMKMINOR(unit
, SND_DEV_DSP
, sce
->chan_num
));
983 sce
->dsp_devt
= NULL
;
985 if (sce
->dspW_devt
) {
986 release_dev(sce
->dspW_devt
);
987 dev_ops_remove(&dsp_cdevsw
,
988 PCMMKMINOR(-1, -1, 0),
989 PCMMKMINOR(unit
, SND_DEV_DSP16
, sce
->chan_num
));
990 sce
->dspW_devt
= NULL
;
992 if (sce
->audio_devt
) {
993 release_dev(sce
->audio_devt
);
995 dev_ops_remove(&dsp_cdevsw,
996 PCMMKMINOR(-1, -1, 0),
997 PCMMKMINOR(unit, SND_DEV_DSP16, sce->chan_num));
999 sce
->audio_devt
= NULL
;
1001 if (sce
->dspr_devt
) {
1002 release_dev(sce
->dspr_devt
);
1003 dev_ops_remove(&dsp_cdevsw
,
1004 PCMMKMINOR(-1, -1, 0),
1005 PCMMKMINOR(unit
, SND_DEV_DSPREC
, sce
->chan_num
));
1006 sce
->dspr_devt
= NULL
;
1012 pce
= SLIST_FIRST(&ch
->children
);
1013 while (pce
!= NULL
) {
1015 device_printf(d
->dev
, "<%s> removing <%s>\n",
1016 ch
->name
, (pce
->channel
!= NULL
) ?
1017 pce
->channel
->name
: "unknown");
1019 SLIST_REMOVE(&ch
->children
, pce
, pcmchan_children
, link
);
1020 kfree(pce
, M_DEVBUF
);
1021 pce
= SLIST_FIRST(&ch
->children
);
1025 #ifdef SND_DYNSYSCTL
1026 d
->sysctl_tree_top
= NULL
;
1027 sysctl_ctx_free(&d
->sysctl_tree
);
1031 SLIST_FOREACH(sce
, &d
->channels
, link
) {
1035 if (!SLIST_EMPTY(&ch
->children
))
1036 device_printf(d
->dev
, "%s: WARNING: <%s> dangling child!\n",
1037 __func__
, ch
->name
);
1040 while (!SLIST_EMPTY(&d
->channels
))
1043 chn_kill(d
->fakechan
);
1044 fkchan_kill(d
->fakechan
);
1047 device_printf(d
->dev
, "%s: devcount=%u, playcount=%u, "
1048 "reccount=%u, vchancount=%u\n",
1049 __func__
, d
->devcount
, d
->playcount
, d
->reccount
,
1052 snd_mtxunlock(d
->lock
);
1053 snd_mtxfree(d
->lock
);
1054 sndstat_unregister(dev
);
1059 /************************************************************************/
1062 sndstat_prepare_pcm(struct sbuf
*s
, device_t dev
, int verbose
)
1064 struct snddev_info
*d
;
1065 struct snddev_channel
*sce
;
1066 struct pcm_channel
*c
;
1067 struct pcm_feeder
*f
;
1073 d
= device_get_softc(dev
);
1077 snd_mtxlock(d
->lock
);
1078 if (!SLIST_EMPTY(&d
->channels
)) {
1080 SLIST_FOREACH(sce
, &d
->channels
, link
) {
1082 if (c
->direction
== PCMDIR_PLAY
) {
1083 if (c
->flags
& CHN_F_VIRTUAL
)
1090 sbuf_printf(s
, " (%dp/%dr/%dv channels%s%s)", d
->playcount
, d
->reccount
, d
->vchancount
,
1091 (d
->flags
& SD_F_SIMPLEX
)? "" : " duplex",
1093 (device_get_unit(dev
) == snd_unit
)? " default" : ""
1100 snd_mtxunlock(d
->lock
);
1104 SLIST_FOREACH(sce
, &d
->channels
, link
) {
1107 KASSERT(c
->bufhard
!= NULL
&& c
->bufsoft
!= NULL
,
1108 ("hosed pcm channel setup"));
1110 sbuf_printf(s
, "\n\t");
1112 /* it would be better to indent child channels */
1113 sbuf_printf(s
, "%s[%s]: ", c
->parentchannel
? c
->parentchannel
->name
: "", c
->name
);
1114 sbuf_printf(s
, "spd %d", c
->speed
);
1115 if (c
->speed
!= sndbuf_getspd(c
->bufhard
))
1116 sbuf_printf(s
, "/%d", sndbuf_getspd(c
->bufhard
));
1117 sbuf_printf(s
, ", fmt 0x%08x", c
->format
);
1118 if (c
->format
!= sndbuf_getfmt(c
->bufhard
))
1119 sbuf_printf(s
, "/0x%08x", sndbuf_getfmt(c
->bufhard
));
1120 sbuf_printf(s
, ", flags 0x%08x, 0x%08x", c
->flags
, c
->feederflags
);
1122 sbuf_printf(s
, ", pid %d", c
->pid
);
1123 sbuf_printf(s
, "\n\t");
1125 sbuf_printf(s
, "interrupts %d, ", c
->interrupts
);
1126 if (c
->direction
== PCMDIR_REC
)
1127 sbuf_printf(s
, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1128 c
->xruns
, sndbuf_getfree(c
->bufhard
), sndbuf_getfree(c
->bufsoft
),
1129 sndbuf_getsize(c
->bufhard
), sndbuf_getblksz(c
->bufhard
),
1130 sndbuf_getblkcnt(c
->bufhard
),
1131 sndbuf_getsize(c
->bufsoft
), sndbuf_getblksz(c
->bufsoft
),
1132 sndbuf_getblkcnt(c
->bufsoft
));
1134 sbuf_printf(s
, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1135 c
->xruns
, sndbuf_getready(c
->bufsoft
),
1136 sndbuf_getsize(c
->bufhard
), sndbuf_getblksz(c
->bufhard
),
1137 sndbuf_getblkcnt(c
->bufhard
),
1138 sndbuf_getsize(c
->bufsoft
), sndbuf_getblksz(c
->bufsoft
),
1139 sndbuf_getblkcnt(c
->bufsoft
));
1140 sbuf_printf(s
, "\n\t");
1142 sbuf_printf(s
, "{%s}", (c
->direction
== PCMDIR_REC
)? "hardware" : "userland");
1143 sbuf_printf(s
, " -> ");
1145 while (f
->source
!= NULL
)
1148 sbuf_printf(s
, "%s", f
->class->name
);
1149 if (f
->desc
->type
== FEEDER_FMT
)
1150 sbuf_printf(s
, "(0x%08x -> 0x%08x)", f
->desc
->in
, f
->desc
->out
);
1151 if (f
->desc
->type
== FEEDER_RATE
)
1152 sbuf_printf(s
, "(%d -> %d)", FEEDER_GET(f
, FEEDRATE_SRC
), FEEDER_GET(f
, FEEDRATE_DST
));
1153 if (f
->desc
->type
== FEEDER_ROOT
|| f
->desc
->type
== FEEDER_MIXER
||
1154 f
->desc
->type
== FEEDER_VOLUME
)
1155 sbuf_printf(s
, "(0x%08x)", f
->desc
->out
);
1156 sbuf_printf(s
, " -> ");
1159 sbuf_printf(s
, "{%s}", (c
->direction
== PCMDIR_REC
)? "userland" : "hardware");
1162 sbuf_printf(s
, " (mixer only)");
1163 snd_mtxunlock(d
->lock
);
1168 /************************************************************************/
1170 #ifdef SND_DYNSYSCTL
1172 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS
)
1174 struct snddev_info
*d
;
1179 newcnt
= d
->vchancount
;
1180 err
= sysctl_handle_int(oidp
, &newcnt
, sizeof(newcnt
), req
);
1182 if (err
== 0 && req
->newptr
!= NULL
&& d
->vchancount
!= newcnt
)
1183 err
= pcm_setvchans(d
, newcnt
);
1189 /************************************************************************/
1192 sound_modevent(module_t mod
, int type
, void *data
)
1195 return (midi_modevent(mod
, type
, data
));
1201 DEV_MODULE(sound
, sound_modevent
, NULL
);
1202 MODULE_VERSION(sound
, SOUND_MODVER
);