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
28 #include <dev/sound/pcm/sound.h>
29 #include <dev/sound/pcm/vchan.h>
30 #include <sys/sysctl.h>
32 #include "feeder_if.h"
34 SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/sound.c,v 1.17.2.14 2002/11/07 23:17:18 cognet Exp $");
36 struct snddev_channel {
37 SLIST_ENTRY(snddev_channel) link;
38 struct pcm_channel *channel;
42 SLIST_HEAD(, snddev_channel) channels;
43 struct pcm_channel *fakechan;
44 unsigned devcount, playcount, reccount, vchancount;
50 char status[SND_STATUSLEN];
51 struct sysctl_ctx_list sysctl_tree;
52 struct sysctl_oid *sysctl_tree_top;
56 devclass_t pcm_devclass;
58 int pcm_veto_load = 1;
62 TUNABLE_INT("hw.snd.unit", &snd_unit);
65 int snd_maxautovchans = 0;
66 TUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans);
68 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver");
70 static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose);
72 struct sysctl_ctx_list *
73 snd_sysctl_tree(device_t dev)
75 struct snddev_info *d = device_get_softc(dev);
77 return &d->sysctl_tree;
81 snd_sysctl_tree_top(device_t dev)
83 struct snddev_info *d = device_get_softc(dev);
85 return d->sysctl_tree_top;
89 snd_mtxcreate(const char *desc, const char *type)
94 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
97 mtx_init(m, desc, type, MTX_RECURSE);
100 return (void *)0xcafebabe;
110 mtx_assert(mtx, MA_OWNED);
117 snd_mtxassert(void *m)
123 mtx_assert(mtx, MA_OWNED);
139 snd_mtxunlock(void *m)
149 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
152 flags &= INTR_MPSAFE;
153 flags |= INTR_TYPE_AV;
155 flags = INTR_TYPE_AV;
157 return bus_setup_intr(dev, res, flags, hand, param, cookiep);
161 pcm_lock(struct snddev_info *d)
163 snd_mtxlock(d->lock);
167 pcm_unlock(struct snddev_info *d)
169 snd_mtxunlock(d->lock);
173 pcm_getfakechan(struct snddev_info *d)
178 /* return a locked channel */
180 pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum)
182 struct pcm_channel *c;
183 struct snddev_channel *sce;
186 snd_mtxassert(d->lock);
188 /* scan for a free channel */
189 SLIST_FOREACH(sce, &d->channels, link) {
192 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) {
193 if (chnum == -1 || c->num == chnum) {
194 c->flags |= CHN_F_BUSY;
202 /* no channel available */
203 if (direction == PCMDIR_PLAY) {
204 if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) {
205 /* try to create a vchan */
206 SLIST_FOREACH(sce, &d->channels, link) {
208 if (!SLIST_EMPTY(&c->children)) {
209 err = vchan_create(c);
211 return pcm_chnalloc(d, direction, pid, -1);
213 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
222 /* release a locked channel and unlock it */
224 pcm_chnrelease(struct pcm_channel *c)
227 c->flags &= ~CHN_F_BUSY;
234 pcm_chnref(struct pcm_channel *c, int ref)
245 pcm_inprog(struct snddev_info *d, int delta)
252 pcm_setmaxautovchans(struct snddev_info *d, int num)
254 struct pcm_channel *c;
255 struct snddev_channel *sce;
258 if (num > 0 && d->vchancount == 0) {
259 SLIST_FOREACH(sce, &d->channels, link) {
261 if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) {
262 c->flags |= CHN_F_BUSY;
263 err = vchan_create(c);
265 device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err);
266 c->flags &= ~CHN_F_BUSY;
272 if (num == 0 && d->vchancount > 0) {
276 SLIST_FOREACH(sce, &d->channels, link) {
278 if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) {
280 err = vchan_destroy(c);
282 device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err);
293 sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS)
295 struct snddev_info *d;
299 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req);
300 if (error == 0 && req->newptr != NULL) {
301 if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass))
303 d = devclass_get_softc(pcm_devclass, unit);
304 if (d == NULL || SLIST_EMPTY(&d->channels))
310 SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW,
311 0, sizeof(int), sysctl_hw_snd_unit, "I", "");
315 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS)
317 struct snddev_info *d;
320 v = snd_maxautovchans;
321 error = sysctl_handle_int(oidp, &v, sizeof(v), req);
322 if (error == 0 && req->newptr != NULL) {
323 if (v < 0 || v >= SND_MAXVCHANS)
325 if (v != snd_maxautovchans) {
326 for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) {
327 d = devclass_get_softc(pcm_devclass, i);
330 pcm_setmaxautovchans(d, v);
333 snd_maxautovchans = v;
337 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW,
338 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "");
341 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo)
343 struct pcm_channel *ch;
350 pnum = &d->playcount;
361 pnum = &d->vchancount;
368 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
372 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK);
382 ch->parentsnddev = d;
383 ch->parentchannel = parent;
385 snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num);
387 err = chn_init(ch, devinfo, dir);
389 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err);
390 kobj_delete(ch->methods, M_DEVBUF);
401 pcm_chn_destroy(struct pcm_channel *ch)
403 struct snddev_info *d;
406 d = ch->parentsnddev;
409 device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err);
413 if (ch->direction == PCMDIR_REC)
415 else if (ch->flags & CHN_F_VIRTUAL)
420 kobj_delete(ch->methods, M_DEVBUF);
427 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev)
429 struct snddev_channel *sce, *tmp, *after;
430 int unit = device_get_unit(d->dev);
432 snd_mtxlock(d->lock);
434 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO);
436 snd_mtxunlock(d->lock);
441 if (SLIST_EMPTY(&d->channels)) {
442 SLIST_INSERT_HEAD(&d->channels, sce, link);
445 SLIST_FOREACH(tmp, &d->channels, link) {
448 SLIST_INSERT_AFTER(after, sce, link);
452 dsp_register(unit, d->devcount++);
453 if (ch->direction == PCMDIR_REC)
454 dsp_registerrec(unit, ch->num);
457 snd_mtxunlock(d->lock);
463 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev)
465 struct snddev_channel *sce;
466 int unit = device_get_unit(d->dev);
468 snd_mtxlock(d->lock);
469 SLIST_FOREACH(sce, &d->channels, link) {
470 if (sce->channel == ch)
473 snd_mtxunlock(d->lock);
476 SLIST_REMOVE(&d->channels, sce, snddev_channel, link);
480 dsp_unregister(unit, --d->devcount);
481 if (ch->direction == PCMDIR_REC)
482 dsp_unregisterrec(unit, ch->num);
484 snd_mtxunlock(d->lock);
490 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
492 struct snddev_info *d = device_get_softc(dev);
493 struct pcm_channel *ch;
496 ch = pcm_chn_create(d, NULL, cls, dir, devinfo);
498 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo);
502 err = pcm_chn_add(d, ch, 1);
504 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err);
509 if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) {
510 ch->flags |= CHN_F_BUSY;
511 err = vchan_create(ch);
513 device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err);
514 ch->flags &= ~CHN_F_BUSY;
522 pcm_killchan(device_t dev)
524 struct snddev_info *d = device_get_softc(dev);
525 struct snddev_channel *sce;
526 struct pcm_channel *ch;
529 snd_mtxlock(d->lock);
530 sce = SLIST_FIRST(&d->channels);
531 snd_mtxunlock(d->lock);
534 error = pcm_chn_remove(d, sce->channel, 1);
537 return (pcm_chn_destroy(ch));
541 pcm_setstatus(device_t dev, char *str)
543 struct snddev_info *d = device_get_softc(dev);
545 snd_mtxlock(d->lock);
546 strncpy(d->status, str, SND_STATUSLEN);
547 snd_mtxunlock(d->lock);
552 pcm_getflags(device_t dev)
554 struct snddev_info *d = device_get_softc(dev);
560 pcm_setflags(device_t dev, u_int32_t val)
562 struct snddev_info *d = device_get_softc(dev);
568 pcm_getdevinfo(device_t dev)
570 struct snddev_info *d = device_get_softc(dev);
576 pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max)
578 struct snddev_info *d = device_get_softc(dev);
582 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
586 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz);
593 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
606 pcm_register(device_t dev, void *devinfo, int numplay, int numrec)
608 struct snddev_info *d = device_get_softc(dev);
611 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load);
616 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
617 snd_mtxlock(d->lock);
621 d->devinfo = devinfo;
628 if (((numplay == 0) || (numrec == 0)) && (numplay != numrec))
629 d->flags |= SD_F_SIMPLEX;
631 d->fakechan = fkchan_setup(dev);
632 chn_init(d->fakechan, NULL, 0);
635 sysctl_ctx_init(&d->sysctl_tree);
636 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree,
637 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO,
638 device_get_nameunit(dev), CTLFLAG_RD, 0, "");
639 if (d->sysctl_tree_top == NULL) {
640 sysctl_ctx_free(&d->sysctl_tree);
643 SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
644 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "");
649 d->flags |= SD_F_AUTOVCHAN;
651 snd_mtxunlock(d->lock);
652 sndstat_register(dev, d->status, sndstat_prepare_pcm);
655 snd_mtxfree(d->lock);
660 pcm_unregister(device_t dev)
662 struct snddev_info *d = device_get_softc(dev);
663 struct snddev_channel *sce;
664 struct pcm_channel *ch;
666 snd_mtxlock(d->lock);
668 device_printf(dev, "unregister: operation in progress\n");
669 snd_mtxunlock(d->lock);
672 if (sndstat_busy() != 0) {
673 device_printf(dev, "unregister: sndstat busy\n");
674 snd_mtxunlock(d->lock);
677 SLIST_FOREACH(sce, &d->channels, link) {
679 if (ch->refcount > 0) {
680 device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid);
681 snd_mtxunlock(d->lock);
685 if (mixer_uninit(dev)) {
686 device_printf(dev, "unregister: mixer busy\n");
687 snd_mtxunlock(d->lock);
692 d->sysctl_tree_top = NULL;
693 sysctl_ctx_free(&d->sysctl_tree);
695 while (!SLIST_EMPTY(&d->channels))
698 chn_kill(d->fakechan);
699 fkchan_kill(d->fakechan);
701 snd_mtxfree(d->lock);
705 /************************************************************************/
708 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose)
710 struct snddev_info *d;
711 struct snddev_channel *sce;
712 struct pcm_channel *c;
713 struct pcm_feeder *f;
719 d = device_get_softc(dev);
723 snd_mtxlock(d->lock);
724 if (!SLIST_EMPTY(&d->channels)) {
726 SLIST_FOREACH(sce, &d->channels, link) {
728 if (c->direction == PCMDIR_PLAY) {
729 if (c->flags & CHN_F_VIRTUAL)
736 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount,
737 (d->flags & SD_F_SIMPLEX)? "" : " duplex",
739 (device_get_unit(dev) == snd_unit)? " default" : ""
746 SLIST_FOREACH(sce, &d->channels, link) {
748 sbuf_printf(s, "\n\t");
750 sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name);
751 sbuf_printf(s, "spd %d", c->speed);
752 if (c->speed != sndbuf_getspd(c->bufhard))
753 sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard));
754 sbuf_printf(s, ", fmt 0x%08x", c->format);
755 if (c->format != sndbuf_getfmt(c->bufhard))
756 sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard));
757 sbuf_printf(s, ", flags %08x", c->flags);
759 sbuf_printf(s, ", pid %d", c->pid);
760 sbuf_printf(s, "\n\t");
762 if (c->bufhard != NULL && c->bufsoft != NULL) {
763 sbuf_printf(s, "interrupts %d, ", c->interrupts);
764 if (c->direction == PCMDIR_REC)
765 sbuf_printf(s, "overruns %d, hfree %d, sfree %d",
766 c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft));
768 sbuf_printf(s, "underruns %d, ready %d",
769 c->xruns, sndbuf_getready(c->bufsoft));
770 sbuf_printf(s, "\n\t");
773 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland");
774 sbuf_printf(s, " -> ");
776 while (f->source != NULL)
779 sbuf_printf(s, "%s", f->class->name);
780 if (f->desc->type == FEEDER_FMT)
781 sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out);
782 if (f->desc->type == FEEDER_RATE)
783 sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST));
784 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER)
785 sbuf_printf(s, "(0x%08x)", f->desc->out);
786 sbuf_printf(s, " -> ");
789 sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware");
793 sbuf_printf(s, " (mixer only)");
794 snd_mtxunlock(d->lock);
799 /************************************************************************/
803 sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS)
805 struct snddev_info *d;
806 struct snddev_channel *sce;
807 struct pcm_channel *c;
808 int err, oldcnt, newcnt, cnt;
814 SLIST_FOREACH(sce, &d->channels, link) {
816 if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL))
822 err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req);
823 if (err == 0 && req->newptr != NULL) {
824 if (newcnt < 0 || newcnt > SND_MAXVCHANS) {
830 /* add new vchans - find a parent channel first */
831 SLIST_FOREACH(sce, &d->channels, link) {
833 /* not a candidate if not a play channel */
834 if (c->direction != PCMDIR_PLAY)
836 /* not a candidate if a virtual channel */
837 if (c->flags & CHN_F_VIRTUAL)
839 /* not a candidate if it's in use */
840 if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children)))
843 * if we get here we're a nonvirtual play channel, and either
845 * 2) busy with children, not directly open
847 * thus we can add children
855 c->flags |= CHN_F_BUSY;
856 while (err == 0 && newcnt > cnt) {
857 err = vchan_create(c);
861 if (SLIST_EMPTY(&c->children))
862 c->flags &= ~CHN_F_BUSY;
863 } else if (newcnt < cnt) {
864 while (err == 0 && newcnt < cnt) {
865 SLIST_FOREACH(sce, &d->channels, link) {
867 if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL)
873 err = vchan_destroy(c);
885 /************************************************************************/
887 static moduledata_t sndpcm_mod = {
892 DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
893 MODULE_VERSION(snd_pcm, PCM_MODVER);