2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
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.17.2.14 2002/11/07 23:17:18 cognet Exp $
28 * $DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.7 2006/12/20 18:14:41 dillon Exp $
31 #include <dev/sound/pcm/sound.h>
32 #include <dev/sound/pcm/vchan.h>
33 #include <sys/sysctl.h>
35 #include "feeder_if.h"
37 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.7 2006/12/20 18:14:41 dillon Exp $");
39 struct snddev_channel {
40 SLIST_ENTRY(snddev_channel) link;
41 struct pcm_channel *channel;
45 SLIST_HEAD(, snddev_channel) channels;
46 struct pcm_channel *fakechan;
47 unsigned devcount, playcount, reccount, vchancount;
53 char status[SND_STATUSLEN];
54 struct sysctl_ctx_list sysctl_tree;
55 struct sysctl_oid *sysctl_tree_top;
59 devclass_t pcm_devclass;
61 int pcm_veto_load = 1;
65 TUNABLE_INT("hw.snd.unit", &snd_unit);
68 int snd_maxautovchans = 0;
69 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
71 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
73 static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
75 struct sysctl_ctx_list *
76 snd_sysctl_tree(device_t dev)
78 struct snddev_info *d = device_get_softc(dev);
80 return &d->sysctl_tree;
84 snd_sysctl_tree_top(device_t dev)
86 struct snddev_info *d = device_get_softc(dev);
88 return d->sysctl_tree_top;
92 snd_mtxcreate(const char *desc, const char *type)
97 m = kmalloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
100 mtx_init(m, desc, type, MTX_RECURSE);
103 return (void *)0xcafebabe;
113 mtx_assert(mtx, MA_OWNED);
115 kfree(mtx, M_DEVBUF);
120 snd_mtxassert(void *m)
126 mtx_assert(mtx, MA_OWNED);
142 snd_mtxunlock(void *m)
152 snd_setup_intr(device_t dev, struct resource *res, int flags,
153 driver_intr_t hand, void *param, void **cookiep,
154 lwkt_serialize_t serializer)
159 flags &= INTR_MPSAFE;
163 error = bus_setup_intr(dev, res, flags, hand, param,
164 cookiep, serializer);
169 pcm_lock(struct snddev_info *d)
171 snd_mtxlock(d->lock);
175 pcm_unlock(struct snddev_info *d)
177 snd_mtxunlock(d->lock);
181 pcm_getfakechan(struct snddev_info *d)
186 /* return a locked channel */
188 pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
190 struct pcm_channel *c;
191 struct snddev_channel *sce;
194 snd_mtxassert(d->lock);
196 /* scan for a free channel */
197 SLIST_FOREACH(sce, &d->channels, link) {
200 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
201 if (chnum == -1 || c->num == chnum) {
202 c->flags |= CHN_F_BUSY;
210 /* no channel available */
211 if (direction == PCMDIR_PLAY) {
212 if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
213 /* try to create a vchan */
214 SLIST_FOREACH(sce, &d->channels, link) {
216 if (!SLIST_EMPTY(&c->children)) {
217 err = vchan_create(c);
219 return pcm_chnalloc(d, direction, pid, -1);
221 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
230 /* release a locked channel and unlock it */
232 pcm_chnrelease(struct pcm_channel *c)
235 c->flags &= ~CHN_F_BUSY;
242 pcm_chnref(struct pcm_channel *c, int ref)
253 pcm_inprog(struct snddev_info *d, int delta)
260 pcm_setmaxautovchans(struct snddev_info *d, int num)
262 struct pcm_channel *c;
263 struct snddev_channel *sce;
266 if (num > 0 && d->vchancount == 0) {
267 SLIST_FOREACH(sce, &d->channels, link) {
269 if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
270 c->flags |= CHN_F_BUSY;
271 err = vchan_create(c);
273 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
274 c->flags &= ~CHN_F_BUSY;
280 if (num == 0 && d->vchancount > 0) {
284 SLIST_FOREACH(sce, &d->channels, link) {
286 if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
288 err = vchan_destroy(c);
290 device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
300 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
302 struct snddev_info *d;
306 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
307 if (error == 0 && req->newptr != NULL) {
308 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
310 d = devclass_get_softc(pcm_devclass, unit);
311 if (d == NULL || SLIST_EMPTY(&d->channels))
317 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
318 0, sizeof(int), sysctl_hw_snd_unit, "I", "");
322 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
324 struct snddev_info *d;
327 v = snd_maxautovchans;
328 error = sysctl_handle_int(oidp, &v, sizeof(v), req);
329 if (error == 0 && req->newptr != NULL) {
330 if (v < 0 || v >= SND_MAXVCHANS)
332 if (v != snd_maxautovchans) {
333 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
334 d = devclass_get_softc(pcm_devclass, i);
337 pcm_setmaxautovchans(d, v);
340 snd_maxautovchans = v;
344 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
345 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
348 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
350 struct pcm_channel *ch;
357 pnum = &d->playcount;
368 pnum = &d->vchancount;
375 ch = kmalloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
379 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
389 ch->parentsnddev = d;
390 ch->parentchannel = parent;
392 ksnprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num);
394 err = chn_init(ch, devinfo, dir);
396 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
397 kobj_delete(ch->methods, M_DEVBUF);
408 pcm_chn_destroy(struct pcm_channel *ch)
410 struct snddev_info *d;
413 d = ch->parentsnddev;
416 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
420 if (ch->direction == PCMDIR_REC)
422 else if (ch->flags & CHN_F_VIRTUAL)
427 kobj_delete(ch->methods, M_DEVBUF);
434 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
436 struct snddev_channel *sce, *tmp, *after;
437 int unit = device_get_unit(d->dev);
439 snd_mtxlock(d->lock);
441 sce = kmalloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
443 snd_mtxunlock(d->lock);
448 if (SLIST_EMPTY(&d->channels)) {
449 SLIST_INSERT_HEAD(&d->channels, sce, link);
452 SLIST_FOREACH(tmp, &d->channels, link) {
455 SLIST_INSERT_AFTER(after, sce, link);
459 dsp_register(unit, d->devcount++);
460 if (ch->direction == PCMDIR_REC)
461 dsp_registerrec(unit, ch->num);
464 snd_mtxunlock(d->lock);
470 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
472 struct snddev_channel *sce;
473 int unit = device_get_unit(d->dev);
475 snd_mtxlock(d->lock);
476 SLIST_FOREACH(sce, &d->channels, link) {
477 if (sce->channel == ch)
480 snd_mtxunlock(d->lock);
483 SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
484 kfree(sce, M_DEVBUF);
487 dsp_unregister(unit, --d->devcount);
488 if (ch->direction == PCMDIR_REC)
489 dsp_unregisterrec(unit, ch->num);
491 snd_mtxunlock(d->lock);
497 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
499 struct snddev_info *d = device_get_softc(dev);
500 struct pcm_channel *ch;
503 ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
505 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
509 err = pcm_chn_add(d, ch, 1);
511 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
516 if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) {
517 ch->flags |= CHN_F_BUSY;
518 err = vchan_create(ch);
520 device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
521 ch->flags &= ~CHN_F_BUSY;
529 pcm_killchan(device_t dev)
531 struct snddev_info *d = device_get_softc(dev);
532 struct snddev_channel *sce;
533 struct pcm_channel *ch;
536 snd_mtxlock(d->lock);
537 sce = SLIST_FIRST(&d->channels);
538 snd_mtxunlock(d->lock);
541 error = pcm_chn_remove(d, sce->channel, 1);
544 return (pcm_chn_destroy(ch));
548 pcm_setstatus(device_t dev, char *str)
550 struct snddev_info *d = device_get_softc(dev);
552 snd_mtxlock(d->lock);
553 strncpy(d->status, str, SND_STATUSLEN);
554 snd_mtxunlock(d->lock);
559 pcm_getflags(device_t dev)
561 struct snddev_info *d = device_get_softc(dev);
567 pcm_setflags(device_t dev, u_int32_t val)
569 struct snddev_info *d = device_get_softc(dev);
575 pcm_getdevinfo(device_t dev)
577 struct snddev_info *d = device_get_softc(dev);
583 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
585 struct snddev_info *d = device_get_softc(dev);
589 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
593 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
600 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
613 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
615 struct snddev_info *d = device_get_softc(dev);
618 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
623 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
624 snd_mtxlock(d->lock);
628 d->devinfo = devinfo;
635 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
636 d->flags |= SD_F_SIMPLEX;
638 d->fakechan = fkchan_setup(dev);
639 chn_init(d->fakechan, NULL, 0);
642 sysctl_ctx_init(&d->sysctl_tree);
643 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
644 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
645 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
646 if (d->sysctl_tree_top == NULL) {
647 sysctl_ctx_free(&d->sysctl_tree);
650 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
651 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
656 d->flags |= SD_F_AUTOVCHAN;
658 snd_mtxunlock(d->lock);
659 sndstat_register(dev, d->status, sndstat_prepare_pcm);
662 snd_mtxfree(d->lock);
667 pcm_unregister(device_t dev)
669 struct snddev_info *d = device_get_softc(dev);
670 struct snddev_channel *sce;
671 struct pcm_channel *ch;
673 snd_mtxlock(d->lock);
675 device_printf(dev, "unregister: operation in progress\n");
676 snd_mtxunlock(d->lock);
679 if (sndstat_busy() != 0) {
680 device_printf(dev, "unregister: sndstat busy\n");
681 snd_mtxunlock(d->lock);
684 SLIST_FOREACH(sce, &d->channels, link) {
686 if (ch->refcount > 0) {
687 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
688 snd_mtxunlock(d->lock);
692 if (mixer_uninit(dev)) {
693 device_printf(dev, "unregister: mixer busy\n");
694 snd_mtxunlock(d->lock);
699 d->sysctl_tree_top = NULL;
700 sysctl_ctx_free(&d->sysctl_tree);
702 while (!SLIST_EMPTY(&d->channels))
705 chn_kill(d->fakechan);
706 fkchan_kill(d->fakechan);
708 snd_mtxfree(d->lock);
712 /************************************************************************/
715 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
717 struct snddev_info *d;
718 struct snddev_channel *sce;
719 struct pcm_channel *c;
720 struct pcm_feeder *f;
726 d = device_get_softc(dev);
730 snd_mtxlock(d->lock);
731 if (!SLIST_EMPTY(&d->channels)) {
733 SLIST_FOREACH(sce, &d->channels, link) {
735 if (c->direction == PCMDIR_PLAY) {
736 if (c->flags & CHN_F_VIRTUAL)
743 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
744 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
746 (device_get_unit(dev) == snd_unit)? " default" : ""
753 SLIST_FOREACH(sce, &d->channels, link) {
755 sbuf_printf(s, "\n\t");
757 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
758 sbuf_printf(s, "spd %d", c->speed);
759 if (c->speed != sndbuf_getspd(c->bufhard))
760 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
761 sbuf_printf(s, ", fmt 0x%08x", c->format);
762 if (c->format != sndbuf_getfmt(c->bufhard))
763 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
764 sbuf_printf(s, ", flags %08x", c->flags);
766 sbuf_printf(s, ", pid %d", c->pid);
767 sbuf_printf(s, "\n\t");
769 if (c->bufhard != NULL && c->bufsoft != NULL) {
770 sbuf_printf(s, "interrupts %d, ", c->interrupts);
771 if (c->direction == PCMDIR_REC)
772 sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
773 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
775 sbuf_printf(s, "underruns %d, ready %d",
776 c->xruns, sndbuf_getready(c->bufsoft));
777 sbuf_printf(s, "\n\t");
780 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
781 sbuf_printf(s, " -> ");
783 while (f->source != NULL)
786 sbuf_printf(s, "%s", f->class->name);
787 if (f->desc->type == FEEDER_FMT)
788 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
789 if (f->desc->type == FEEDER_RATE)
790 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
791 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
792 sbuf_printf(s, "(0x%08x)", f->desc->out);
793 sbuf_printf(s, " -> ");
796 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
799 sbuf_printf(s, " (mixer only)");
802 snd_mtxunlock(d->lock);
807 /************************************************************************/
811 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
813 struct snddev_info *d;
814 struct snddev_channel *sce;
815 struct pcm_channel *c;
816 int err, oldcnt, newcnt, cnt;
822 SLIST_FOREACH(sce, &d->channels, link) {
824 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
830 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
831 if (err == 0 && req->newptr != NULL) {
832 if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
838 /* add new vchans - find a parent channel first */
839 SLIST_FOREACH(sce, &d->channels, link) {
841 /* not a candidate if not a play channel */
842 if (c->direction != PCMDIR_PLAY)
844 /* not a candidate if a virtual channel */
845 if (c->flags & CHN_F_VIRTUAL)
847 /* not a candidate if it's in use */
848 if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
851 * if we get here we're a nonvirtual play channel, and either
853 * 2) busy with children, not directly open
855 * thus we can add children
862 c->flags |= CHN_F_BUSY;
863 while (err == 0 && newcnt > cnt) {
864 err = vchan_create(c);
868 if (SLIST_EMPTY(&c->children))
869 c->flags &= ~CHN_F_BUSY;
870 } else if (newcnt < cnt) {
871 while (err == 0 && newcnt < cnt) {
872 SLIST_FOREACH(sce, &d->channels, link) {
874 if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
880 err = vchan_destroy(c);
892 /************************************************************************/
894 static moduledata_t sndpcm_mod = {
899 DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
900 MODULE_VERSION(snd_pcm, PCM_MODVER);