sound.c revision 83476
150724Scg/* 250724Scg * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 350724Scg * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) 450724Scg * All rights reserved. 550724Scg * 650724Scg * Redistribution and use in source and binary forms, with or without 750724Scg * modification, are permitted provided that the following conditions 850724Scg * are met: 950724Scg * 1. Redistributions of source code must retain the above copyright 1050724Scg * notice, this list of conditions and the following disclaimer. 1150724Scg * 2. Redistributions in binary form must reproduce the above copyright 1250724Scg * notice, this list of conditions and the following disclaimer in the 1350724Scg * documentation and/or other materials provided with the distribution. 1450724Scg * 1550724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1650724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1750724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1850724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1950724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2050724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2150724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2250724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2350724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2450724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2550724Scg * SUCH DAMAGE. 2650724Scg */ 2750724Scg 2853465Scg#include <dev/sound/pcm/sound.h> 2977269Scg#include <dev/sound/pcm/vchan.h> 3065207Scg#include <sys/sysctl.h> 3150724Scg 3282180Scg#include "feeder_if.h" 3382180Scg 3482180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 83476 2001-09-14 20:26:03Z greid $"); 3582180Scg 3682180Scgstruct snddev_channel { 3782180Scg SLIST_ENTRY(snddev_channel) link; 3882180Scg struct pcm_channel *channel; 3982180Scg}; 4082180Scg 4182180Scgstruct snddev_info { 4282180Scg SLIST_HEAD(, snddev_channel) channels; 4382180Scg struct pcm_channel *fakechan; 4483089Scg unsigned devcount, reccount, chancount, vchancount; 4582180Scg unsigned flags; 4682180Scg int inprog; 4782180Scg void *devinfo; 4882180Scg device_t dev; 4982180Scg char status[SND_STATUSLEN]; 5082180Scg struct sysctl_ctx_list sysctl_tree; 5182180Scg struct sysctl_oid *sysctl_tree_top; 5282180Scg void *lock; 5382180Scg}; 5482180Scg 5578362Scgdevclass_t pcm_devclass; 5650724Scg 5773127Scg#ifdef USING_DEVFS 5878362Scgint snd_unit = 0; 5977900SpeterTUNABLE_INT("hw.snd.unit", &snd_unit); 6073127Scg#endif 6179141Scg 6282180Scgint snd_maxautovchans = 0; 6382180ScgTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 6450724Scg 6573127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 6673127Scg 6782180Scgstatic int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 6882180Scg 6982180Scgstruct sysctl_ctx_list * 7082180Scgsnd_sysctl_tree(device_t dev) 7182180Scg{ 7282180Scg struct snddev_info *d = device_get_softc(dev); 7382180Scg 7482180Scg return &d->sysctl_tree; 7582180Scg} 7682180Scg 7782180Scgstruct sysctl_oid * 7882180Scgsnd_sysctl_tree_top(device_t dev) 7982180Scg{ 8082180Scg struct snddev_info *d = device_get_softc(dev); 8182180Scg 8282180Scg return d->sysctl_tree_top; 8382180Scg} 8482180Scg 8573131Scgvoid * 8673131Scgsnd_mtxcreate(const char *desc) 8773131Scg{ 8873131Scg#ifdef USING_MUTEX 8973131Scg struct mtx *m; 9073131Scg 9173131Scg m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 9273131Scg if (m == NULL) 9373131Scg return NULL; 9473131Scg mtx_init(m, desc, MTX_RECURSE); 9573131Scg return m; 9673131Scg#else 9773757Scg return (void *)0xcafebabe; 9873131Scg#endif 9973131Scg} 10073131Scg 10173131Scgvoid 10273131Scgsnd_mtxfree(void *m) 10373131Scg{ 10473131Scg#ifdef USING_MUTEX 10573131Scg struct mtx *mtx = m; 10673131Scg 10773131Scg mtx_assert(mtx, MA_OWNED); 10873131Scg mtx_destroy(mtx); 10973131Scg free(mtx, M_DEVBUF); 11073131Scg#endif 11173131Scg} 11273131Scg 11373131Scgvoid 11473131Scgsnd_mtxassert(void *m) 11573131Scg{ 11673131Scg#ifdef USING_MUTEX 11778670Scg#ifdef INVARIANTS 11873131Scg struct mtx *mtx = m; 11973131Scg 12073131Scg mtx_assert(mtx, MA_OWNED); 12173131Scg#endif 12278670Scg#endif 12373131Scg} 12473131Scg 12573131Scgvoid 12673131Scgsnd_mtxlock(void *m) 12773131Scg{ 12873131Scg#ifdef USING_MUTEX 12973131Scg struct mtx *mtx = m; 13073131Scg 13173131Scg mtx_lock(mtx); 13273131Scg#endif 13373131Scg} 13473131Scg 13573131Scgvoid 13673131Scgsnd_mtxunlock(void *m) 13773131Scg{ 13873131Scg#ifdef USING_MUTEX 13973131Scg struct mtx *mtx = m; 14073131Scg 14173131Scg mtx_unlock(mtx); 14273131Scg#endif 14373131Scg} 14473131Scg 14573131Scgint 14673131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 14773131Scg{ 14873131Scg#ifdef USING_MUTEX 14973131Scg flags &= INTR_MPSAFE; 15078366Speter flags |= INTR_TYPE_AV; 15173131Scg#else 15278366Speter flags = INTR_TYPE_AV; 15373131Scg#endif 15473131Scg return bus_setup_intr(dev, res, flags, hand, param, cookiep); 15573131Scg} 15673131Scg 15782180Scgvoid 15882180Scgpcm_lock(struct snddev_info *d) 15982180Scg{ 16082180Scg snd_mtxlock(d->lock); 16182180Scg} 16282180Scg 16382180Scgvoid 16482180Scgpcm_unlock(struct snddev_info *d) 16582180Scg{ 16682180Scg snd_mtxunlock(d->lock); 16782180Scg} 16882180Scg 16982180Scgstruct pcm_channel * 17082180Scgpcm_getfakechan(struct snddev_info *d) 17182180Scg{ 17282180Scg return d->fakechan; 17382180Scg} 17482180Scg 17578214Scg/* return a locked channel */ 17677269Scgstruct pcm_channel * 17783089Scgpcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) 17877269Scg{ 17977269Scg struct pcm_channel *c; 18077269Scg struct snddev_channel *sce; 18178895Scg int err; 18277269Scg 18378214Scg snd_mtxassert(d->lock); 18478895Scg 18578895Scg /* scan for a free channel */ 18677269Scg SLIST_FOREACH(sce, &d->channels, link) { 18777269Scg c = sce->channel; 18877882Scg CHN_LOCK(c); 18977269Scg if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 19083089Scg if (chnum == -1 || c->num == chnum) { 19183089Scg c->flags |= CHN_F_BUSY; 19283089Scg c->pid = pid; 19383089Scg return c; 19483089Scg } 19577269Scg } 19677882Scg CHN_UNLOCK(c); 19777269Scg } 19878895Scg 19978895Scg /* no channel available */ 20078895Scg if (direction == PCMDIR_PLAY) { 20182180Scg if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { 20278895Scg /* try to create a vchan */ 20378895Scg SLIST_FOREACH(sce, &d->channels, link) { 20478895Scg c = sce->channel; 20578895Scg if (!SLIST_EMPTY(&c->children)) { 20678895Scg err = vchan_create(c); 20778895Scg if (!err) 20883089Scg return pcm_chnalloc(d, direction, pid, -1); 20978895Scg else 21078895Scg device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 21178895Scg } 21278895Scg } 21378895Scg } 21478895Scg } 21578895Scg 21677269Scg return NULL; 21777269Scg} 21877269Scg 21978214Scg/* release a locked channel and unlock it */ 22077269Scgint 22178214Scgpcm_chnrelease(struct pcm_channel *c) 22277269Scg{ 22378214Scg CHN_LOCKASSERT(c); 22477269Scg c->flags &= ~CHN_F_BUSY; 22578214Scg c->pid = -1; 22677882Scg CHN_UNLOCK(c); 22777269Scg return 0; 22877269Scg} 22977269Scg 23077269Scgint 23177269Scgpcm_chnref(struct pcm_channel *c, int ref) 23277269Scg{ 23377882Scg int r; 23477882Scg 23578214Scg CHN_LOCKASSERT(c); 23677269Scg c->refcount += ref; 23777882Scg r = c->refcount; 23877882Scg return r; 23977269Scg} 24077269Scg 24182180Scgint 24282180Scgpcm_inprog(struct snddev_info *d, int delta) 24382180Scg{ 24482180Scg d->inprog += delta; 24582180Scg return d->inprog; 24682180Scg} 24782180Scg 24882180Scgstatic void 24982180Scgpcm_setmaxautovchans(struct snddev_info *d, int num) 25082180Scg{ 25182180Scg struct pcm_channel *c; 25282180Scg struct snddev_channel *sce; 25382180Scg int err, done; 25482180Scg 25582180Scg if (num > 0 && d->vchancount == 0) { 25682180Scg SLIST_FOREACH(sce, &d->channels, link) { 25782180Scg c = sce->channel; 25882180Scg if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { 25982180Scg c->flags |= CHN_F_BUSY; 26082180Scg err = vchan_create(c); 26182180Scg if (err) { 26282180Scg device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 26382180Scg c->flags &= ~CHN_F_BUSY; 26482180Scg } 26582180Scg return; 26682180Scg } 26782180Scg } 26882180Scg } 26982180Scg if (num == 0 && d->vchancount > 0) { 27082180Scg done = 0; 27182180Scg while (!done) { 27282180Scg done = 1; 27382180Scg SLIST_FOREACH(sce, &d->channels, link) { 27482180Scg c = sce->channel; 27582180Scg if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { 27682180Scg done = 0; 27782180Scg err = vchan_destroy(c); 27882180Scg if (err) 27982180Scg device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); 28082180Scg goto restart; 28182180Scg } 28282180Scg } 28382180Scgrestart: 28482180Scg } 28582180Scg } 28682180Scg} 28782180Scg 28873127Scg#ifdef USING_DEVFS 28965340Scgstatic int 29078853Scgsysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 29165340Scg{ 29278214Scg struct snddev_info *d; 29365340Scg int error, unit; 29465340Scg 29565340Scg unit = snd_unit; 29665340Scg error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 29765340Scg if (error == 0 && req->newptr != NULL) { 29878396Scg if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 29978214Scg return EINVAL; 30078214Scg d = devclass_get_softc(pcm_devclass, unit); 30178395Scg if (d == NULL || SLIST_EMPTY(&d->channels)) 30278214Scg return EINVAL; 30365340Scg snd_unit = unit; 30465340Scg } 30565340Scg return (error); 30665340Scg} 30770617SjhbSYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 30878853Scg 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 30973127Scg#endif 31065340Scg 31178853Scgstatic int 31282180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 31378853Scg{ 31482180Scg struct snddev_info *d; 31582180Scg int i, v, error; 31678853Scg 31782180Scg v = snd_maxautovchans; 31878853Scg error = sysctl_handle_int(oidp, &v, sizeof(v), req); 31978853Scg if (error == 0 && req->newptr != NULL) { 32078853Scg if (v < 0 || v >= SND_MAXVCHANS) 32178853Scg return EINVAL; 32282180Scg if (v != snd_maxautovchans) { 32382180Scg for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 32482180Scg d = devclass_get_softc(pcm_devclass, i); 32582180Scg if (!d) 32682180Scg continue; 32782180Scg pcm_setmaxautovchans(d, v); 32882180Scg } 32982180Scg } 33082180Scg snd_maxautovchans = v; 33178853Scg } 33278853Scg return (error); 33378853Scg} 33482180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 33582180Scg 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 33678853Scg 33777269Scgstruct pcm_channel * 33877269Scgpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 33950724Scg{ 34077269Scg struct pcm_channel *ch; 34165340Scg char *dirs; 34277269Scg int err; 34350724Scg 34477269Scg switch(dir) { 34577269Scg case PCMDIR_PLAY: 34677269Scg dirs = "play"; 34777269Scg break; 34877269Scg case PCMDIR_REC: 34977269Scg dirs = "record"; 35077269Scg break; 35177269Scg case PCMDIR_VIRTUAL: 35277269Scg dirs = "virtual"; 35377269Scg dir = PCMDIR_PLAY; 35477269Scg break; 35577269Scg default: 35677269Scg return NULL; 35777269Scg } 35865340Scg 35977269Scg ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 36077269Scg if (!ch) 36177269Scg return NULL; 36277269Scg 36377269Scg ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 36477269Scg if (!ch->methods) { 36577269Scg free(ch, M_DEVBUF); 36677269Scg return NULL; 36761344Scg } 36877269Scg 36977269Scg ch->pid = -1; 37077269Scg ch->parentsnddev = d; 37177269Scg ch->parentchannel = parent; 37277269Scg snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs); 37377269Scg 37470134Scg err = chn_init(ch, devinfo, dir); 37570134Scg if (err) { 37677269Scg device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err); 37777269Scg kobj_delete(ch->methods, M_DEVBUF); 37877269Scg free(ch, M_DEVBUF); 37977269Scg return NULL; 38055483Scg } 38177269Scg 38277269Scg return ch; 38377269Scg} 38477269Scg 38577269Scgint 38677269Scgpcm_chn_destroy(struct pcm_channel *ch) 38777269Scg{ 38877269Scg int err; 38977269Scg 39077269Scg err = chn_kill(ch); 39177269Scg if (err) { 39277269Scg device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err); 39377269Scg return err; 39477269Scg } 39577269Scg 39677269Scg kobj_delete(ch->methods, M_DEVBUF); 39777269Scg free(ch, M_DEVBUF); 39877269Scg 39977269Scg return 0; 40077269Scg} 40177269Scg 40277269Scgint 40378895Scgpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 40477269Scg{ 40582180Scg struct snddev_channel *sce, *tmp, *after; 40677269Scg int unit = device_get_unit(d->dev); 40777269Scg 40878214Scg snd_mtxlock(d->lock); 40978214Scg 41077269Scg sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 41177269Scg if (!sce) { 41278214Scg snd_mtxunlock(d->lock); 41377269Scg return ENOMEM; 41477269Scg } 41577269Scg 41677269Scg sce->channel = ch; 41782180Scg if (SLIST_EMPTY(&d->channels)) { 41882180Scg SLIST_INSERT_HEAD(&d->channels, sce, link); 41982180Scg } else { 42082180Scg after = NULL; 42182180Scg SLIST_FOREACH(tmp, &d->channels, link) { 42282180Scg after = tmp; 42382180Scg } 42482180Scg SLIST_INSERT_AFTER(after, sce, link); 42582180Scg } 42677269Scg 42783089Scg if (ch->direction == PCMDIR_REC) 42883089Scg ch->num = d->reccount++; 42983089Scg/* 43083089Scg else 43183089Scg ch->num = d->playcount++; 43283089Scg*/ 43383089Scg 43483089Scg if (mkdev) { 43578895Scg dsp_register(unit, d->devcount++); 43683089Scg if (ch->direction == PCMDIR_REC) 43783089Scg dsp_registerrec(unit, ch->num); 43883089Scg } 43978362Scg d->chancount++; 44078895Scg if (ch->flags & CHN_F_VIRTUAL) 44178895Scg d->vchancount++; 44277269Scg 44377882Scg snd_mtxunlock(d->lock); 44477269Scg 44550724Scg return 0; 44650724Scg} 44750724Scg 44877269Scgint 44978895Scgpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 45065340Scg{ 45177269Scg struct snddev_channel *sce; 45277269Scg int unit = device_get_unit(d->dev); 45365340Scg 45477882Scg snd_mtxlock(d->lock); 45577269Scg SLIST_FOREACH(sce, &d->channels, link) { 45677269Scg if (sce->channel == ch) 45777269Scg goto gotit; 45865340Scg } 45977882Scg snd_mtxunlock(d->lock); 46077269Scg return EINVAL; 46177269Scggotit: 46278895Scg if (ch->flags & CHN_F_VIRTUAL) 46378895Scg d->vchancount--; 46465340Scg d->chancount--; 46577269Scg SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 46677269Scg free(sce, M_DEVBUF); 46777269Scg 46883089Scg if (rmdev) { 46978895Scg dsp_unregister(unit, --d->devcount); 47083089Scg if (ch->direction == PCMDIR_REC) 47183089Scg dsp_unregisterrec(unit, --d->reccount); 47283089Scg } 47377882Scg snd_mtxunlock(d->lock); 47477269Scg 47565340Scg return 0; 47665340Scg} 47765340Scg 47850724Scgint 47977269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 48077269Scg{ 48177269Scg struct snddev_info *d = device_get_softc(dev); 48282180Scg struct pcm_channel *ch; 48382180Scg int err; 48477269Scg 48577269Scg ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 48677269Scg if (!ch) { 48777269Scg device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 48877269Scg return ENODEV; 48977269Scg } 49078853Scg 49178895Scg err = pcm_chn_add(d, ch, 1); 49277269Scg if (err) { 49377269Scg device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 49477269Scg pcm_chn_destroy(ch); 49578853Scg return err; 49677269Scg } 49777269Scg 49882180Scg if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) { 49978853Scg ch->flags |= CHN_F_BUSY; 50082180Scg err = vchan_create(ch); 50178853Scg if (err) { 50282180Scg device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 50382180Scg ch->flags &= ~CHN_F_BUSY; 50478853Scg } 50578853Scg } 50678853Scg 50777269Scg return err; 50877269Scg} 50977269Scg 51077269Scgstatic int 51177269Scgpcm_killchan(device_t dev) 51277269Scg{ 51377269Scg struct snddev_info *d = device_get_softc(dev); 51477269Scg struct snddev_channel *sce; 51577269Scg 51677882Scg snd_mtxlock(d->lock); 51777269Scg sce = SLIST_FIRST(&d->channels); 51877882Scg snd_mtxunlock(d->lock); 51977269Scg 52078895Scg return pcm_chn_remove(d, sce->channel, 1); 52177269Scg} 52277269Scg 52377269Scgint 52450724Scgpcm_setstatus(device_t dev, char *str) 52550724Scg{ 52674763Scg struct snddev_info *d = device_get_softc(dev); 52777882Scg 52877882Scg snd_mtxlock(d->lock); 52950724Scg strncpy(d->status, str, SND_STATUSLEN); 53077882Scg snd_mtxunlock(d->lock); 53150724Scg return 0; 53250724Scg} 53350724Scg 53450724Scgu_int32_t 53550724Scgpcm_getflags(device_t dev) 53650724Scg{ 53774763Scg struct snddev_info *d = device_get_softc(dev); 53877882Scg 53950724Scg return d->flags; 54050724Scg} 54150724Scg 54250724Scgvoid 54350724Scgpcm_setflags(device_t dev, u_int32_t val) 54450724Scg{ 54574763Scg struct snddev_info *d = device_get_softc(dev); 54678362Scg 54750724Scg d->flags = val; 54850724Scg} 54950724Scg 55058384Scgvoid * 55158384Scgpcm_getdevinfo(device_t dev) 55258384Scg{ 55374763Scg struct snddev_info *d = device_get_softc(dev); 55477882Scg 55558384Scg return d->devinfo; 55658384Scg} 55758384Scg 55850724Scgint 55950724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec) 56050724Scg{ 56174763Scg struct snddev_info *d = device_get_softc(dev); 56250724Scg 56377882Scg d->lock = snd_mtxcreate(device_get_nameunit(dev)); 56477882Scg snd_mtxlock(d->lock); 56577269Scg 56678853Scg d->flags = 0; 56761886Scg d->dev = dev; 56850724Scg d->devinfo = devinfo; 56978895Scg d->devcount = 0; 57083089Scg d->reccount = 0; 57177269Scg d->chancount = 0; 57278895Scg d->vchancount = 0; 57378362Scg d->inprog = 0; 57450724Scg 57578362Scg if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 57678214Scg d->flags |= SD_F_SIMPLEX; 57778362Scg 57878214Scg d->fakechan = fkchan_setup(dev); 57978214Scg chn_init(d->fakechan, NULL, 0); 58055494Scg 58173127Scg#ifdef SND_DYNSYSCTL 58270680Sjhb sysctl_ctx_init(&d->sysctl_tree); 58370680Sjhb d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 58470943Sjhb SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 58570680Sjhb device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 58670943Sjhb if (d->sysctl_tree_top == NULL) { 58770680Sjhb sysctl_ctx_free(&d->sysctl_tree); 58870680Sjhb goto no; 58970680Sjhb } 59073127Scg#endif 59178214Scg if (numplay > 0) 59282180Scg vchan_initsys(dev); 59378853Scg if (numplay == 1) 59478853Scg d->flags |= SD_F_AUTOVCHAN; 59578853Scg 59677882Scg snd_mtxunlock(d->lock); 59782180Scg sndstat_register(dev, d->status, sndstat_prepare_pcm); 59850724Scg return 0; 59950724Scgno: 60077882Scg snd_mtxfree(d->lock); 60150724Scg return ENXIO; 60250724Scg} 60350724Scg 60465340Scgint 60565340Scgpcm_unregister(device_t dev) 60665207Scg{ 60774763Scg struct snddev_info *d = device_get_softc(dev); 60877269Scg struct snddev_channel *sce; 60965207Scg 61077882Scg snd_mtxlock(d->lock); 61178362Scg if (d->inprog) { 61283476Sgreid device_printf(dev, "unregister: operation in progress\n"); 61378362Scg snd_mtxunlock(d->lock); 61478362Scg return EBUSY; 61578362Scg } 61683476Sgreid if (sndstat_busy() != 0) { 61783476Sgreid device_printf(dev, "unregister: sndstat busy\n"); 61883476Sgreid snd_mtxunlock(d->lock); 61983476Sgreid return EBUSY; 62083476Sgreid } 62177269Scg SLIST_FOREACH(sce, &d->channels, link) { 62277269Scg if (sce->channel->refcount > 0) { 62383476Sgreid device_printf(dev, "unregister: channel busy\n"); 62477882Scg snd_mtxunlock(d->lock); 62577269Scg return EBUSY; 62677269Scg } 62765487Scg } 62878362Scg if (mixer_uninit(dev)) { 62983476Sgreid device_printf(dev, "unregister: mixer busy\n"); 63077882Scg snd_mtxunlock(d->lock); 63165487Scg return EBUSY; 63265487Scg } 63365207Scg 63474763Scg#ifdef SND_DYNSYSCTL 63574763Scg d->sysctl_tree_top = NULL; 63674763Scg sysctl_ctx_free(&d->sysctl_tree); 63774763Scg#endif 63878395Scg while (!SLIST_EMPTY(&d->channels)) 63977269Scg pcm_killchan(dev); 64065207Scg 64174763Scg chn_kill(d->fakechan); 64274763Scg fkchan_kill(d->fakechan); 64373127Scg 64477882Scg snd_mtxfree(d->lock); 64565340Scg return 0; 64665207Scg} 64765207Scg 64882180Scg/************************************************************************/ 64982180Scg 65082180Scgstatic int 65182180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 65282180Scg{ 65382180Scg struct snddev_info *d; 65482180Scg struct snddev_channel *sce; 65582180Scg struct pcm_channel *c; 65682180Scg struct pcm_feeder *f; 65782479Scg char *fsep; 65882180Scg int pc, rc, vc; 65982180Scg 66082180Scg if (verbose < 1) 66182180Scg return 0; 66282180Scg 66382180Scg d = device_get_softc(dev); 66482180Scg if (!d) 66582180Scg return ENXIO; 66682180Scg 66782180Scg snd_mtxlock(d->lock); 66882180Scg if (!SLIST_EMPTY(&d->channels)) { 66982180Scg pc = rc = vc = 0; 67082180Scg SLIST_FOREACH(sce, &d->channels, link) { 67182180Scg c = sce->channel; 67282180Scg if (c->direction == PCMDIR_PLAY) { 67382180Scg if (c->flags & CHN_F_VIRTUAL) 67482180Scg vc++; 67582180Scg else 67682180Scg pc++; 67782180Scg } else 67882180Scg rc++; 67982180Scg } 68082180Scg sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", pc, rc, vc, 68182180Scg (d->flags & SD_F_SIMPLEX)? "" : " duplex", 68282180Scg#ifdef USING_DEVFS 68382180Scg (device_get_unit(dev) == snd_unit)? " default" : "" 68482180Scg#else 68582180Scg "" 68682180Scg#endif 68782180Scg ); 68882180Scg if (verbose <= 1) 68982180Scg goto skipverbose; 69082180Scg SLIST_FOREACH(sce, &d->channels, link) { 69182180Scg c = sce->channel; 69282479Scg sbuf_printf(s, "\n\t"); 69382479Scg 69482479Scg sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 69582479Scg sbuf_printf(s, "speed %d, format %08x, flags %08x", c->speed, c->format, c->flags); 69682180Scg if (c->pid != -1) 69782180Scg sbuf_printf(s, ", pid %d", c->pid); 69882180Scg sbuf_printf(s, "\n\t"); 69982492Scg if (c->bufhard != NULL && c->bufsoft != NULL) { 70082479Scg sbuf_printf(s, "interrupts %d, ", c->interrupts); 70182479Scg if (c->direction == PCMDIR_REC) 70282479Scg sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 70382479Scg c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 70482479Scg else 70582492Scg sbuf_printf(s, "underruns %d, ready %d", 70682492Scg c->xruns, sndbuf_getready(c->bufsoft)); 70782479Scg sbuf_printf(s, "\n\t"); 70882479Scg } 70982479Scg fsep = (c->direction == PCMDIR_REC)? " -> " : " <- "; 71082492Scg sbuf_printf(s, "{hardware}%s", fsep); 71182180Scg f = c->feeder; 71282180Scg while (f) { 71382180Scg sbuf_printf(s, "%s", f->class->name); 71482180Scg if (f->desc->type == FEEDER_FMT) 71582479Scg sbuf_printf(s, "(%08x%s%08x)", f->desc->out, fsep, f->desc->in); 71682180Scg if (f->desc->type == FEEDER_RATE) 71782479Scg sbuf_printf(s, "(%d%s%d)", FEEDER_GET(f, FEEDRATE_DST), fsep, FEEDER_GET(f, FEEDRATE_SRC)); 71882180Scg if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 71982180Scg sbuf_printf(s, "(%08x)", f->desc->out); 72082479Scg sbuf_printf(s, "%s", fsep); 72182180Scg f = f->source; 72282180Scg } 72382492Scg sbuf_printf(s, "{userland}"); 72482180Scg } 72582180Scgskipverbose: 72682180Scg } else 72782180Scg sbuf_printf(s, " (mixer only)"); 72882180Scg snd_mtxunlock(d->lock); 72982180Scg 73082180Scg return 0; 73182180Scg} 73282180Scg 73382180Scg/************************************************************************/ 73482180Scg 73582180Scg#ifdef SND_DYNSYSCTL 73682180Scgint 73782180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 73882180Scg{ 73982180Scg struct snddev_info *d; 74082180Scg struct snddev_channel *sce; 74182180Scg struct pcm_channel *c; 74282180Scg int err, oldcnt, newcnt, cnt; 74382180Scg 74482180Scg d = oidp->oid_arg1; 74582180Scg 74682180Scg pcm_lock(d); 74782180Scg cnt = 0; 74882180Scg SLIST_FOREACH(sce, &d->channels, link) { 74982180Scg c = sce->channel; 75082180Scg if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) 75182180Scg cnt++; 75282180Scg } 75382180Scg oldcnt = cnt; 75482180Scg newcnt = cnt; 75582180Scg 75682180Scg err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 75782180Scg if (err == 0 && req->newptr != NULL) { 75882180Scg if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 75982180Scg pcm_unlock(d); 76082180Scg return EINVAL; 76182180Scg } 76282180Scg 76382180Scg if (newcnt > cnt) { 76482180Scg /* add new vchans - find a parent channel first */ 76582180Scg SLIST_FOREACH(sce, &d->channels, link) { 76682180Scg c = sce->channel; 76782180Scg /* not a candidate if not a play channel */ 76882180Scg if (c->direction != PCMDIR_PLAY) 76982180Scg goto addskip; 77082180Scg /* not a candidate if a virtual channel */ 77182180Scg if (c->flags & CHN_F_VIRTUAL) 77282180Scg goto addskip; 77382180Scg /* not a candidate if it's in use */ 77482180Scg if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 77582180Scg goto addskip; 77682180Scg /* 77782180Scg * if we get here we're a nonvirtual play channel, and either 77882180Scg * 1) not busy 77982180Scg * 2) busy with children, not directly open 78082180Scg * 78182180Scg * thus we can add children 78282180Scg */ 78382180Scg goto addok; 78482180Scgaddskip: 78582180Scg } 78682180Scg pcm_unlock(d); 78782180Scg return EBUSY; 78882180Scgaddok: 78982180Scg c->flags |= CHN_F_BUSY; 79082180Scg while (err == 0 && newcnt > cnt) { 79182180Scg err = vchan_create(c); 79282180Scg if (err == 0) 79382180Scg cnt++; 79482180Scg } 79582180Scg if (SLIST_EMPTY(&c->children)) 79682180Scg c->flags &= ~CHN_F_BUSY; 79782180Scg } else if (newcnt < cnt) { 79882180Scg while (err == 0 && newcnt < cnt) { 79982180Scg SLIST_FOREACH(sce, &d->channels, link) { 80082180Scg c = sce->channel; 80182180Scg if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 80282180Scg goto remok; 80382180Scg } 80482180Scg pcm_unlock(d); 80582180Scg return EINVAL; 80682180Scgremok: 80782180Scg err = vchan_destroy(c); 80882180Scg if (err == 0) 80982180Scg cnt--; 81082180Scg } 81182180Scg } 81282180Scg } 81382180Scg 81482180Scg pcm_unlock(d); 81582180Scg return err; 81682180Scg} 81782180Scg#endif 81882180Scg 81982180Scg/************************************************************************/ 82082180Scg 82165340Scgstatic moduledata_t sndpcm_mod = { 82265340Scg "snd_pcm", 82378362Scg NULL, 82465340Scg NULL 82565340Scg}; 82665340ScgDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 82765340ScgMODULE_VERSION(snd_pcm, PCM_MODVER); 828