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.3 2006/04/04 17:43:48 ariff Exp $
28 * $DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.8 2007/01/04 21:47:03 corecode 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.8 2007/01/04 21:47:03 corecode Exp $");
40 devclass_t pcm_devclass;
42 int pcm_veto_load = 1;
46 TUNABLE_INT("hw.snd.unit", &snd_unit);
49 int snd_maxautovchans = 0;
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);
84 return (void *)0xcafebabe;
92 struct spinlock *mtx = m;
94 /* mtx_assert(mtx, MA_OWNED); */
101 snd_mtxassert(void *m)
105 /* XXX can't assert spinlocks */
114 struct spinlock *mtx = m;
121 snd_mtxunlock(void *m)
124 struct spinlock *mtx = m;
131 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
134 flags &= INTR_MPSAFE;
135 flags |= INTR_TYPE_AV;
137 flags = INTR_TYPE_AV;
139 return bus_setup_intr(dev, res, flags, hand, param, cookiep, NULL);
142 #ifndef PCM_DEBUG_MTX
144 pcm_lock(struct snddev_info *d)
146 snd_mtxlock(d->lock);
150 pcm_unlock(struct snddev_info *d)
152 snd_mtxunlock(d->lock);
157 pcm_getfakechan(struct snddev_info *d)
163 pcm_setvchans(struct snddev_info *d, int newcnt)
165 struct snddev_channel *sce = NULL;
166 struct pcm_channel *c = NULL;
167 int err = 0, vcnt, dcnt, i;
171 if (!(d->flags & SD_F_AUTOVCHAN)) {
176 vcnt = d->vchancount;
177 dcnt = d->playcount + d->reccount;
179 if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) {
187 /* add new vchans - find a parent channel first */
188 SLIST_FOREACH(sce, &d->channels, link) {
191 if (c->direction == PCMDIR_PLAY &&
192 ((c->flags & CHN_F_HAS_VCHAN) ||
194 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)))))
201 c->flags |= CHN_F_BUSY;
202 while (err == 0 && newcnt > vcnt) {
203 if (dcnt > PCMMAXCHAN) {
204 device_printf(d->dev, "%s: Maximum channel reached.\n", __func__);
207 err = vchan_create(c);
211 } else if (err == E2BIG && newcnt > vcnt)
212 device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err);
215 c->flags &= ~CHN_F_BUSY;
217 } else if (newcnt < vcnt) {
218 #define ORPHAN_CDEVT(cdevt) \
219 ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \
220 (cdevt)->si_drv2 == NULL))
221 while (err == 0 && newcnt < vcnt) {
223 SLIST_FOREACH(sce, &d->channels, link) {
226 if (c->direction == PCMDIR_PLAY &&
227 (c->flags & CHN_F_VIRTUAL) &&
229 if (!(c->flags & CHN_F_BUSY) &&
230 ORPHAN_CDEVT(sce->dsp_devt) &&
231 ORPHAN_CDEVT(sce->dspW_devt) &&
232 ORPHAN_CDEVT(sce->audio_devt) &&
233 ORPHAN_CDEVT(sce->dspr_devt))
236 * Either we're busy, or our cdev
237 * has been stolen by dsp_clone().
238 * Skip, and increase newcnt.
240 if (!(c->flags & CHN_F_BUSY))
241 device_printf(d->dev,
242 "%s: <%s> somebody steal my cdev!\n",
253 err = vchan_destroy(c);
257 device_printf(d->dev,
258 "%s: WARNING: vchan_destroy() failed!",
268 /* return error status and a locked channel */
270 pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
271 pid_t pid, int chnum)
273 struct pcm_channel *c;
274 struct snddev_channel *sce;
279 /* scan for a free channel */
280 SLIST_FOREACH(sce, &d->channels, link) {
283 if (c->direction == direction && !(c->flags & CHN_F_BUSY)) {
284 if (chnum < 0 || sce->chan_num == chnum) {
285 c->flags |= CHN_F_BUSY;
291 if (sce->chan_num == chnum) {
292 if (c->direction != direction)
294 else if (c->flags & CHN_F_BUSY)
300 } else if (c->direction == direction && (c->flags & CHN_F_BUSY))
305 /* no channel available */
306 if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 &&
307 d->vchancount < snd_maxautovchans &&
308 d->devcount <= PCMMAXCHAN) {
309 err = pcm_setvchans(d, d->vchancount + 1);
319 /* release a locked channel and unlock it */
321 pcm_chnrelease(struct pcm_channel *c)
324 c->flags &= ~CHN_F_BUSY;
331 pcm_chnref(struct pcm_channel *c, int ref)
342 pcm_inprog(struct snddev_info *d, int delta)
358 pcm_setmaxautovchans(struct snddev_info *d, int num)
360 if (num > 0 && d->vchancount == 0)
362 else if (num == 0 && d->vchancount > 0)
368 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
370 struct snddev_info *d;
374 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
375 if (error == 0 && req->newptr != NULL) {
376 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
378 d = devclass_get_softc(pcm_devclass, unit);
379 if (d == NULL || SLIST_EMPTY(&d->channels))
385 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
386 0, sizeof(int), sysctl_hw_snd_unit, "I", "");
390 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
392 struct snddev_info *d;
395 v = snd_maxautovchans;
396 error = sysctl_handle_int(oidp, &v, sizeof(v), req);
397 if (error == 0 && req->newptr != NULL) {
398 if (v < 0 || v > PCMMAXCHAN)
400 if (pcm_devclass != NULL && v != snd_maxautovchans) {
401 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
402 d = devclass_get_softc(pcm_devclass, i);
405 pcm_setmaxautovchans(d, v);
408 snd_maxautovchans = v;
412 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
413 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
416 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
418 struct snddev_channel *sce;
419 struct pcm_channel *ch, *c;
421 uint32_t flsearch = 0;
422 int direction, err, rpnum, *pnum;
427 direction = PCMDIR_PLAY;
428 pnum = &d->playcount;
433 direction = PCMDIR_REC;
439 direction = PCMDIR_PLAY;
440 pnum = &d->vchancount;
441 flsearch = CHN_F_VIRTUAL;
448 ch = kmalloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
452 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
459 snd_mtxlock(d->lock);
462 SLIST_FOREACH(sce, &d->channels, link) {
464 if (direction != c->direction ||
465 (c->flags & CHN_F_VIRTUAL) != flsearch)
467 if (ch->num == c->num)
471 device_printf(d->dev,
472 "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n",
473 __func__, dirs, ch->num, c->num);
475 goto retry_num_search;
479 goto retry_num_search_out;
482 SLIST_FOREACH(sce, &d->channels, link) {
484 if (direction != c->direction ||
485 (c->flags & CHN_F_VIRTUAL) != flsearch)
487 if (ch->num == c->num) {
489 goto retry_num_search;
493 retry_num_search_out:
494 if (*pnum != rpnum) {
495 device_printf(d->dev,
496 "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n",
497 __func__, dirs, *pnum, rpnum);
501 snd_mtxunlock(d->lock);
504 ch->parentsnddev = d;
505 ch->parentchannel = parent;
507 ksnprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num);
509 err = chn_init(ch, devinfo, dir, direction);
511 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
512 kobj_delete(ch->methods, M_DEVBUF);
514 snd_mtxlock(d->lock);
516 snd_mtxunlock(d->lock);
525 pcm_chn_destroy(struct pcm_channel *ch)
527 struct snddev_info *d;
530 d = ch->parentsnddev;
533 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
537 kobj_delete(ch->methods, M_DEVBUF);
544 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch)
546 struct snddev_channel *sce, *tmp, *after;
548 int device = device_get_unit(d->dev);
552 * Note it's confusing nomenclature.
554 * device -> pcm_device
555 * unit -> pcm_channel
556 * channel -> snddev_channel
561 sce = kmalloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
566 snd_mtxlock(d->lock);
571 SLIST_FOREACH(tmp, &d->channels, link) {
572 if (sce->chan_num == tmp->chan_num)
576 device_printf(d->dev,
577 "%s: cdev numbering screwed (Expect: %d, Got: %d)\n",
578 __func__, sce->chan_num, tmp->chan_num);
580 goto retry_chan_num_search;
585 goto retry_chan_num_search_out;
586 retry_chan_num_search:
588 * Look for possible channel numbering collision. This may not
589 * be optimized, but it will ensure that no collision occured.
590 * Can be considered cheap since none of the locking/unlocking
591 * operations involved.
595 SLIST_FOREACH(tmp, &d->channels, link) {
596 if (sce->chan_num == tmp->chan_num) {
598 goto retry_chan_num_search;
600 if (sce->chan_num > tmp->chan_num)
604 retry_chan_num_search_out:
606 * Don't overflow PCMMKMINOR / PCMMAXCHAN.
608 if (sce->chan_num > PCMMAXCHAN) {
609 snd_mtxunlock(d->lock);
610 device_printf(d->dev,
611 "%s: WARNING: sce->chan_num overflow! (%d)\n",
612 __func__, sce->chan_num);
613 kfree(sce, M_DEVBUF);
616 if (d->devcount != rdevcount) {
617 device_printf(d->dev,
618 "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n",
619 __func__, d->devcount, rdevcount);
620 d->devcount = rdevcount;
624 SLIST_INSERT_HEAD(&d->channels, sce, link);
626 SLIST_INSERT_AFTER(after, sce, link);
631 SLIST_FOREACH(tmp, &d->channels, link) {
632 if (cnum != tmp->chan_num)
633 device_printf(d->dev,
634 "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n",
635 __func__, cnum, tmp->chan_num);
641 namelen = strlen(ch->name);
642 if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */
643 ksnprintf(ch->name + namelen,
644 CHN_NAMELEN - namelen, ":dsp%d.%d",
645 device, sce->chan_num);
647 snd_mtxunlock(d->lock);
650 * I will revisit these someday, and nuke it mercilessly..
652 dev_ops_add(&dsp_cdevsw,
653 PCMMKMINOR(-1, -1, 0),
654 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num));
655 sce->dsp_devt = make_dev(&dsp_cdevsw,
656 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
657 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d",
658 device, sce->chan_num);
659 reference_dev(sce->dsp_devt);
661 dev_ops_add(&dsp_cdevsw,
662 PCMMKMINOR(-1, -1, 0),
663 PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num));
664 sce->dspW_devt = make_dev(&dsp_cdevsw,
665 PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num),
666 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d",
667 device, sce->chan_num);
668 reference_dev(sce->dspW_devt);
671 dev_ops_add(&dsp_cdevsw,
672 PCMMKMINOR(-1, -1, 0),
673 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num));
674 sce->audio_devt = make_dev(&dsp_cdevsw,
675 PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num),
676 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d",
677 device, sce->chan_num);
679 sce->audio_devt = sce->dsp_devt;
680 reference_dev(sce->audio_devt);
682 if (ch->direction == PCMDIR_REC) {
683 dev_ops_add(&dsp_cdevsw,
684 PCMMKMINOR(-1, -1, 0),
685 PCMMKMINOR(device, SND_DEV_DSPREC, sce->chan_num));
686 sce->dspr_devt = make_dev(&dsp_cdevsw,
687 PCMMKMINOR(device, SND_DEV_DSPREC,
688 sce->chan_num), UID_ROOT, GID_WHEEL,
689 0666, "dspr%d.%d", device, sce->chan_num);
690 reference_dev(sce->dspr_devt);
697 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch)
699 struct snddev_channel *sce;
704 if (!mtx_owned(d->lock)) {
705 snd_mtxlock(d->lock);
710 SLIST_FOREACH(sce, &d->channels, link) {
711 if (sce->channel == ch)
716 snd_mtxunlock(d->lock);
720 SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
722 if (ch->flags & CHN_F_VIRTUAL)
724 else if (ch->direction == PCMDIR_REC)
731 snd_mtxunlock(d->lock);
733 kfree(sce, M_DEVBUF);
739 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
741 struct snddev_info *d = device_get_softc(dev);
742 struct pcm_channel *ch;
745 ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
747 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
751 err = pcm_chn_add(d, ch);
753 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
762 pcm_killchan(device_t dev)
764 struct snddev_info *d = device_get_softc(dev);
765 struct snddev_channel *sce;
766 struct pcm_channel *ch;
769 sce = SLIST_FIRST(&d->channels);
772 error = pcm_chn_remove(d, sce->channel);
775 return (pcm_chn_destroy(ch));
779 pcm_setstatus(device_t dev, char *str)
781 struct snddev_info *d = device_get_softc(dev);
783 snd_mtxlock(d->lock);
784 strncpy(d->status, str, SND_STATUSLEN);
785 snd_mtxunlock(d->lock);
786 if (snd_maxautovchans > 0)
792 pcm_getflags(device_t dev)
794 struct snddev_info *d = device_get_softc(dev);
800 pcm_setflags(device_t dev, uint32_t val)
802 struct snddev_info *d = device_get_softc(dev);
808 pcm_getdevinfo(device_t dev)
810 struct snddev_info *d = device_get_softc(dev);
816 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
818 struct snddev_info *d = device_get_softc(dev);
822 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
826 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
833 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
846 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
848 struct snddev_info *d = device_get_softc(dev);
851 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
856 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
860 * d->flags should be cleared by the allocator of the softc.
861 * We cannot clear this field here because several devices set
862 * this flag before calling pcm_register().
867 d->devinfo = devinfo;
874 SLIST_INIT(&d->channels);
876 if ((numplay == 0 || numrec == 0) && numplay != numrec)
877 d->flags |= SD_F_SIMPLEX;
879 d->fakechan = fkchan_setup(dev);
880 chn_init(d->fakechan, NULL, 0, 0);
883 sysctl_ctx_init(&d->sysctl_tree);
884 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
885 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
886 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
887 if (d->sysctl_tree_top == NULL) {
888 sysctl_ctx_free(&d->sysctl_tree);
891 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
892 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
895 d->flags |= SD_F_AUTOVCHAN;
899 sndstat_register(dev, d->status, sndstat_prepare_pcm);
902 snd_mtxfree(d->lock);
907 pcm_unregister(device_t dev)
909 struct snddev_info *d = device_get_softc(dev);
910 struct snddev_channel *sce;
911 struct pcmchan_children *pce;
912 struct pcm_channel *ch;
914 if (sndstat_acquire() != 0) {
915 device_printf(dev, "unregister: sndstat busy\n");
919 snd_mtxlock(d->lock);
921 device_printf(dev, "unregister: operation in progress\n");
922 snd_mtxunlock(d->lock);
927 SLIST_FOREACH(sce, &d->channels, link) {
929 if (ch->refcount > 0) {
930 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
931 snd_mtxunlock(d->lock);
937 if (mixer_uninit(dev) == EBUSY) {
938 device_printf(dev, "unregister: mixer busy\n");
939 snd_mtxunlock(d->lock);
944 SLIST_FOREACH(sce, &d->channels, link) {
945 int unit = device_get_unit(d->dev);
948 release_dev(sce->dsp_devt);
949 dev_ops_remove(&dsp_cdevsw,
950 PCMMKMINOR(-1, -1, 0),
951 PCMMKMINOR(unit, SND_DEV_DSP, sce->chan_num));
952 sce->dsp_devt = NULL;
954 if (sce->dspW_devt) {
955 release_dev(sce->dspW_devt);
956 dev_ops_remove(&dsp_cdevsw,
957 PCMMKMINOR(-1, -1, 0),
958 PCMMKMINOR(unit, SND_DEV_DSP16, sce->chan_num));
959 sce->dspW_devt = NULL;
961 if (sce->audio_devt) {
962 release_dev(sce->audio_devt);
964 dev_ops_remove(&dsp_cdevsw,
965 PCMMKMINOR(-1, -1, 0),
966 PCMMKMINOR(unit, SND_DEV_DSP16, sce->chan_num));
968 sce->audio_devt = NULL;
970 if (sce->dspr_devt) {
971 release_dev(sce->dspr_devt);
972 dev_ops_remove(&dsp_cdevsw,
973 PCMMKMINOR(-1, -1, 0),
974 PCMMKMINOR(unit, SND_DEV_DSPREC, sce->chan_num));
975 sce->dspr_devt = NULL;
981 pce = SLIST_FIRST(&ch->children);
982 while (pce != NULL) {
984 device_printf(d->dev, "<%s> removing <%s>\n",
985 ch->name, (pce->channel != NULL) ?
986 pce->channel->name : "unknown");
988 SLIST_REMOVE(&ch->children, pce, pcmchan_children, link);
989 kfree(pce, M_DEVBUF);
990 pce = SLIST_FIRST(&ch->children);
995 d->sysctl_tree_top = NULL;
996 sysctl_ctx_free(&d->sysctl_tree);
1000 SLIST_FOREACH(sce, &d->channels, link) {
1004 if (!SLIST_EMPTY(&ch->children))
1005 device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n",
1006 __func__, ch->name);
1009 while (!SLIST_EMPTY(&d->channels))
1012 chn_kill(d->fakechan);
1013 fkchan_kill(d->fakechan);
1016 device_printf(d->dev, "%s: devcount=%u, playcount=%u, "
1017 "reccount=%u, vchancount=%u\n",
1018 __func__, d->devcount, d->playcount, d->reccount,
1021 snd_mtxunlock(d->lock);
1022 snd_mtxfree(d->lock);
1023 sndstat_unregister(dev);
1028 /************************************************************************/
1031 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
1033 struct snddev_info *d;
1034 struct snddev_channel *sce;
1035 struct pcm_channel *c;
1036 struct pcm_feeder *f;
1042 d = device_get_softc(dev);
1046 snd_mtxlock(d->lock);
1047 if (!SLIST_EMPTY(&d->channels)) {
1049 SLIST_FOREACH(sce, &d->channels, link) {
1051 if (c->direction == PCMDIR_PLAY) {
1052 if (c->flags & CHN_F_VIRTUAL)
1059 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
1060 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
1062 (device_get_unit(dev) == snd_unit)? " default" : ""
1069 snd_mtxunlock(d->lock);
1073 SLIST_FOREACH(sce, &d->channels, link) {
1076 KASSERT(c->bufhard != NULL && c->bufsoft != NULL,
1077 ("hosed pcm channel setup"));
1079 sbuf_printf(s, "\n\t");
1081 /* it would be better to indent child channels */
1082 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
1083 sbuf_printf(s, "spd %d", c->speed);
1084 if (c->speed != sndbuf_getspd(c->bufhard))
1085 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
1086 sbuf_printf(s, ", fmt 0x%08x", c->format);
1087 if (c->format != sndbuf_getfmt(c->bufhard))
1088 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
1089 sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags);
1091 sbuf_printf(s, ", pid %d", c->pid);
1092 sbuf_printf(s, "\n\t");
1094 sbuf_printf(s, "interrupts %d, ", c->interrupts);
1095 if (c->direction == PCMDIR_REC)
1096 sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]",
1097 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft),
1098 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1099 sndbuf_getblkcnt(c->bufhard),
1100 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1101 sndbuf_getblkcnt(c->bufsoft));
1103 sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]",
1104 c->xruns, sndbuf_getready(c->bufsoft),
1105 sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard),
1106 sndbuf_getblkcnt(c->bufhard),
1107 sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft),
1108 sndbuf_getblkcnt(c->bufsoft));
1109 sbuf_printf(s, "\n\t");
1111 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
1112 sbuf_printf(s, " -> ");
1114 while (f->source != NULL)
1117 sbuf_printf(s, "%s", f->class->name);
1118 if (f->desc->type == FEEDER_FMT)
1119 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
1120 if (f->desc->type == FEEDER_RATE)
1121 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
1122 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER ||
1123 f->desc->type == FEEDER_VOLUME)
1124 sbuf_printf(s, "(0x%08x)", f->desc->out);
1125 sbuf_printf(s, " -> ");
1128 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
1131 sbuf_printf(s, " (mixer only)");
1132 snd_mtxunlock(d->lock);
1137 /************************************************************************/
1139 #ifdef SND_DYNSYSCTL
1141 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
1143 struct snddev_info *d;
1148 newcnt = d->vchancount;
1149 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
1151 if (err == 0 && req->newptr != NULL && d->vchancount != newcnt)
1152 err = pcm_setvchans(d, newcnt);
1158 /************************************************************************/
1161 sound_modevent(module_t mod, int type, void *data)
1164 return (midi_modevent(mod, type, data));
1170 DEV_MODULE(sound, sound_modevent, NULL);
1171 MODULE_VERSION(sound, SOUND_MODVER);