sound.c revision 111119
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 111119 2003-02-19 05:47:46Z imp $"); 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; 4483614Scg unsigned devcount, playcount, reccount, vchancount; 4582180Scg unsigned flags; 4682180Scg int inprog; 4783614Scg unsigned int bufsz; 4882180Scg void *devinfo; 4982180Scg device_t dev; 5082180Scg char status[SND_STATUSLEN]; 5182180Scg struct sysctl_ctx_list sysctl_tree; 5282180Scg struct sysctl_oid *sysctl_tree_top; 53107285Scg struct mtx *lock; 5482180Scg}; 5582180Scg 5678362Scgdevclass_t pcm_devclass; 5750724Scg 5889834Scgint pcm_veto_load = 1; 5989834Scg 6073127Scg#ifdef USING_DEVFS 6178362Scgint snd_unit = 0; 6277900SpeterTUNABLE_INT("hw.snd.unit", &snd_unit); 6373127Scg#endif 6479141Scg 6582180Scgint snd_maxautovchans = 0; 6682180ScgTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 6750724Scg 6873127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 6973127Scg 7082180Scgstatic int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 7182180Scg 7282180Scgstruct sysctl_ctx_list * 7382180Scgsnd_sysctl_tree(device_t dev) 7482180Scg{ 7582180Scg struct snddev_info *d = device_get_softc(dev); 7682180Scg 7782180Scg return &d->sysctl_tree; 7882180Scg} 7982180Scg 8082180Scgstruct sysctl_oid * 8182180Scgsnd_sysctl_tree_top(device_t dev) 8282180Scg{ 8382180Scg struct snddev_info *d = device_get_softc(dev); 8482180Scg 8582180Scg return d->sysctl_tree_top; 8682180Scg} 8782180Scg 8873131Scgvoid * 8993814Sjhbsnd_mtxcreate(const char *desc, const char *type) 9073131Scg{ 9173131Scg#ifdef USING_MUTEX 9273131Scg struct mtx *m; 9373131Scg 94111119Simp m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 9573131Scg if (m == NULL) 9673131Scg return NULL; 9793814Sjhb mtx_init(m, desc, type, MTX_RECURSE); 9873131Scg return m; 9973131Scg#else 10073757Scg return (void *)0xcafebabe; 10173131Scg#endif 10273131Scg} 10373131Scg 10473131Scgvoid 10573131Scgsnd_mtxfree(void *m) 10673131Scg{ 10773131Scg#ifdef USING_MUTEX 10873131Scg struct mtx *mtx = m; 10973131Scg 110107237Scg /* mtx_assert(mtx, MA_OWNED); */ 11173131Scg mtx_destroy(mtx); 11273131Scg free(mtx, M_DEVBUF); 11373131Scg#endif 11473131Scg} 11573131Scg 11673131Scgvoid 11773131Scgsnd_mtxassert(void *m) 11873131Scg{ 11973131Scg#ifdef USING_MUTEX 12078670Scg#ifdef INVARIANTS 12173131Scg struct mtx *mtx = m; 12273131Scg 12373131Scg mtx_assert(mtx, MA_OWNED); 12473131Scg#endif 12578670Scg#endif 12673131Scg} 127107237Scg/* 12873131Scgvoid 12973131Scgsnd_mtxlock(void *m) 13073131Scg{ 13173131Scg#ifdef USING_MUTEX 13273131Scg struct mtx *mtx = m; 13373131Scg 13473131Scg mtx_lock(mtx); 13573131Scg#endif 13673131Scg} 13773131Scg 13873131Scgvoid 13973131Scgsnd_mtxunlock(void *m) 14073131Scg{ 14173131Scg#ifdef USING_MUTEX 14273131Scg struct mtx *mtx = m; 14373131Scg 14473131Scg mtx_unlock(mtx); 14573131Scg#endif 14673131Scg} 147107237Scg*/ 14873131Scgint 14973131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 15073131Scg{ 15173131Scg#ifdef USING_MUTEX 15273131Scg flags &= INTR_MPSAFE; 15378366Speter flags |= INTR_TYPE_AV; 15473131Scg#else 15578366Speter flags = INTR_TYPE_AV; 15673131Scg#endif 15773131Scg return bus_setup_intr(dev, res, flags, hand, param, cookiep); 15873131Scg} 15973131Scg 16082180Scgvoid 16182180Scgpcm_lock(struct snddev_info *d) 16282180Scg{ 16382180Scg snd_mtxlock(d->lock); 16482180Scg} 16582180Scg 16682180Scgvoid 16782180Scgpcm_unlock(struct snddev_info *d) 16882180Scg{ 16982180Scg snd_mtxunlock(d->lock); 17082180Scg} 17182180Scg 17282180Scgstruct pcm_channel * 17382180Scgpcm_getfakechan(struct snddev_info *d) 17482180Scg{ 17582180Scg return d->fakechan; 17682180Scg} 17782180Scg 17878214Scg/* return a locked channel */ 17977269Scgstruct pcm_channel * 18083089Scgpcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) 18177269Scg{ 18277269Scg struct pcm_channel *c; 18377269Scg struct snddev_channel *sce; 18478895Scg int err; 18577269Scg 18678214Scg snd_mtxassert(d->lock); 18778895Scg 18878895Scg /* scan for a free channel */ 18977269Scg SLIST_FOREACH(sce, &d->channels, link) { 19077269Scg c = sce->channel; 19177882Scg CHN_LOCK(c); 19277269Scg if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 19383089Scg if (chnum == -1 || c->num == chnum) { 19483089Scg c->flags |= CHN_F_BUSY; 19583089Scg c->pid = pid; 19683089Scg return c; 19783089Scg } 19877269Scg } 19977882Scg CHN_UNLOCK(c); 20077269Scg } 20178895Scg 20278895Scg /* no channel available */ 20378895Scg if (direction == PCMDIR_PLAY) { 20482180Scg if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { 20578895Scg /* try to create a vchan */ 20678895Scg SLIST_FOREACH(sce, &d->channels, link) { 20778895Scg c = sce->channel; 20878895Scg if (!SLIST_EMPTY(&c->children)) { 20978895Scg err = vchan_create(c); 21078895Scg if (!err) 21183089Scg return pcm_chnalloc(d, direction, pid, -1); 21278895Scg else 21378895Scg device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 21478895Scg } 21578895Scg } 21678895Scg } 21778895Scg } 21878895Scg 21977269Scg return NULL; 22077269Scg} 22177269Scg 22278214Scg/* release a locked channel and unlock it */ 22377269Scgint 22478214Scgpcm_chnrelease(struct pcm_channel *c) 22577269Scg{ 22678214Scg CHN_LOCKASSERT(c); 22777269Scg c->flags &= ~CHN_F_BUSY; 22878214Scg c->pid = -1; 22977882Scg CHN_UNLOCK(c); 23077269Scg return 0; 23177269Scg} 23277269Scg 23377269Scgint 23477269Scgpcm_chnref(struct pcm_channel *c, int ref) 23577269Scg{ 23677882Scg int r; 23777882Scg 23878214Scg CHN_LOCKASSERT(c); 23977269Scg c->refcount += ref; 24077882Scg r = c->refcount; 24177882Scg return r; 24277269Scg} 24377269Scg 24482180Scgint 24582180Scgpcm_inprog(struct snddev_info *d, int delta) 24682180Scg{ 24782180Scg d->inprog += delta; 24882180Scg return d->inprog; 24982180Scg} 25082180Scg 25182180Scgstatic void 25282180Scgpcm_setmaxautovchans(struct snddev_info *d, int num) 25382180Scg{ 25482180Scg struct pcm_channel *c; 25582180Scg struct snddev_channel *sce; 25682180Scg int err, done; 25782180Scg 25882180Scg if (num > 0 && d->vchancount == 0) { 25982180Scg SLIST_FOREACH(sce, &d->channels, link) { 26082180Scg c = sce->channel; 26182180Scg if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { 26282180Scg c->flags |= CHN_F_BUSY; 26382180Scg err = vchan_create(c); 26482180Scg if (err) { 26582180Scg device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 26682180Scg c->flags &= ~CHN_F_BUSY; 26782180Scg } 26882180Scg return; 26982180Scg } 27082180Scg } 27182180Scg } 27282180Scg if (num == 0 && d->vchancount > 0) { 27382180Scg done = 0; 27482180Scg while (!done) { 27582180Scg done = 1; 27682180Scg SLIST_FOREACH(sce, &d->channels, link) { 27782180Scg c = sce->channel; 27882180Scg if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { 27982180Scg done = 0; 28082180Scg err = vchan_destroy(c); 28182180Scg if (err) 28282180Scg device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); 28396928Speter break; /* restart */ 28482180Scg } 28582180Scg } 28682180Scg } 28782180Scg } 28882180Scg} 28982180Scg 29073127Scg#ifdef USING_DEVFS 29165340Scgstatic int 29278853Scgsysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 29365340Scg{ 29478214Scg struct snddev_info *d; 29565340Scg int error, unit; 29665340Scg 29765340Scg unit = snd_unit; 29865340Scg error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 29965340Scg if (error == 0 && req->newptr != NULL) { 30078396Scg if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 30178214Scg return EINVAL; 30278214Scg d = devclass_get_softc(pcm_devclass, unit); 30378395Scg if (d == NULL || SLIST_EMPTY(&d->channels)) 30478214Scg return EINVAL; 30565340Scg snd_unit = unit; 30665340Scg } 30765340Scg return (error); 30865340Scg} 30970617SjhbSYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 31078853Scg 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 31173127Scg#endif 31265340Scg 31378853Scgstatic int 31482180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 31578853Scg{ 31682180Scg struct snddev_info *d; 31782180Scg int i, v, error; 31878853Scg 31982180Scg v = snd_maxautovchans; 32078853Scg error = sysctl_handle_int(oidp, &v, sizeof(v), req); 32178853Scg if (error == 0 && req->newptr != NULL) { 32278853Scg if (v < 0 || v >= SND_MAXVCHANS) 32378853Scg return EINVAL; 32482180Scg if (v != snd_maxautovchans) { 32582180Scg for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 32682180Scg d = devclass_get_softc(pcm_devclass, i); 32782180Scg if (!d) 32882180Scg continue; 32982180Scg pcm_setmaxautovchans(d, v); 33082180Scg } 33182180Scg } 33282180Scg snd_maxautovchans = v; 33378853Scg } 33478853Scg return (error); 33578853Scg} 33682180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 33782180Scg 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 33878853Scg 33977269Scgstruct pcm_channel * 34077269Scgpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 34150724Scg{ 34277269Scg struct pcm_channel *ch; 34365340Scg char *dirs; 34483614Scg int err, *pnum; 34550724Scg 34677269Scg switch(dir) { 34777269Scg case PCMDIR_PLAY: 34877269Scg dirs = "play"; 34983614Scg pnum = &d->playcount; 35077269Scg break; 35183614Scg 35277269Scg case PCMDIR_REC: 35377269Scg dirs = "record"; 35483614Scg pnum = &d->reccount; 35577269Scg break; 35683614Scg 35777269Scg case PCMDIR_VIRTUAL: 35877269Scg dirs = "virtual"; 35977269Scg dir = PCMDIR_PLAY; 36083614Scg pnum = &d->vchancount; 36177269Scg break; 36283614Scg 36377269Scg default: 36477269Scg return NULL; 36577269Scg } 36665340Scg 367111119Simp ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 36877269Scg if (!ch) 36977269Scg return NULL; 37077269Scg 371111119Simp ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 37277269Scg if (!ch->methods) { 37377269Scg free(ch, M_DEVBUF); 37483614Scg 37577269Scg return NULL; 37661344Scg } 37777269Scg 378107237Scg snd_mtxlock(d->lock); 37983614Scg ch->num = (*pnum)++; 380107237Scg snd_mtxunlock(d->lock); 38183614Scg 38277269Scg ch->pid = -1; 38377269Scg ch->parentsnddev = d; 38477269Scg ch->parentchannel = parent; 38589774Sscottl ch->dev = d->dev; 386107237Scg snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); 38777269Scg 38870134Scg err = chn_init(ch, devinfo, dir); 38970134Scg if (err) { 39083614Scg device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 39177269Scg kobj_delete(ch->methods, M_DEVBUF); 39277269Scg free(ch, M_DEVBUF); 393107237Scg snd_mtxlock(d->lock); 39483614Scg (*pnum)--; 395107237Scg snd_mtxunlock(d->lock); 39683614Scg 39777269Scg return NULL; 39855483Scg } 39977269Scg 40077269Scg return ch; 40177269Scg} 40277269Scg 40377269Scgint 40477269Scgpcm_chn_destroy(struct pcm_channel *ch) 40577269Scg{ 40683614Scg struct snddev_info *d; 40777269Scg int err; 40877269Scg 40983614Scg d = ch->parentsnddev; 41077269Scg err = chn_kill(ch); 41177269Scg if (err) { 41283614Scg device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 41377269Scg return err; 41477269Scg } 41577269Scg 41677269Scg kobj_delete(ch->methods, M_DEVBUF); 41777269Scg free(ch, M_DEVBUF); 41877269Scg 41977269Scg return 0; 42077269Scg} 42177269Scg 42277269Scgint 42378895Scgpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 42477269Scg{ 42582180Scg struct snddev_channel *sce, *tmp, *after; 42677269Scg int unit = device_get_unit(d->dev); 427107237Scg int x = -1; 42877269Scg 429111119Simp sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 43077269Scg if (!sce) { 43177269Scg return ENOMEM; 43277269Scg } 43377269Scg 434100478Sorion snd_mtxlock(d->lock); 43577269Scg sce->channel = ch; 43682180Scg if (SLIST_EMPTY(&d->channels)) { 43782180Scg SLIST_INSERT_HEAD(&d->channels, sce, link); 43882180Scg } else { 43982180Scg after = NULL; 44082180Scg SLIST_FOREACH(tmp, &d->channels, link) { 44182180Scg after = tmp; 44282180Scg } 44382180Scg SLIST_INSERT_AFTER(after, sce, link); 44482180Scg } 445107237Scg if (mkdev) 446107237Scg x = d->devcount++; 447107237Scg snd_mtxunlock(d->lock); 44877269Scg 44983089Scg if (mkdev) { 450107237Scg dsp_register(unit, x); 45183089Scg if (ch->direction == PCMDIR_REC) 45283089Scg dsp_registerrec(unit, ch->num); 45383089Scg } 45477269Scg 45550724Scg return 0; 45650724Scg} 45750724Scg 45877269Scgint 45978895Scgpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 46065340Scg{ 46177269Scg struct snddev_channel *sce; 46277269Scg int unit = device_get_unit(d->dev); 46365340Scg 46477882Scg snd_mtxlock(d->lock); 46577269Scg SLIST_FOREACH(sce, &d->channels, link) { 46677269Scg if (sce->channel == ch) 46777269Scg goto gotit; 46865340Scg } 46977882Scg snd_mtxunlock(d->lock); 47077269Scg return EINVAL; 47177269Scggotit: 47277269Scg SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 47383089Scg if (rmdev) { 47478895Scg dsp_unregister(unit, --d->devcount); 47583089Scg if (ch->direction == PCMDIR_REC) 47683614Scg dsp_unregisterrec(unit, ch->num); 47783089Scg } 478107237Scg 479107237Scg if (ch->direction == PCMDIR_REC) 480107237Scg d->reccount--; 481107237Scg else if (ch->flags & CHN_F_VIRTUAL) 482107237Scg d->vchancount--; 483107237Scg else 484107237Scg d->playcount--; 485107237Scg 48677882Scg snd_mtxunlock(d->lock); 487107237Scg free(sce, M_DEVBUF); 48877269Scg 48965340Scg return 0; 49065340Scg} 49165340Scg 49250724Scgint 49377269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 49477269Scg{ 49577269Scg struct snddev_info *d = device_get_softc(dev); 49682180Scg struct pcm_channel *ch; 49782180Scg int err; 49877269Scg 49977269Scg ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 50077269Scg if (!ch) { 50177269Scg device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 50277269Scg return ENODEV; 50377269Scg } 50478853Scg 50578895Scg err = pcm_chn_add(d, ch, 1); 50677269Scg if (err) { 50777269Scg device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 508107237Scg snd_mtxunlock(d->lock); 50977269Scg pcm_chn_destroy(ch); 51078853Scg return err; 51177269Scg } 51277269Scg 513100576Skan if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) && 514100576Skan ch->direction == PCMDIR_PLAY && d->vchancount == 0) { 51578853Scg ch->flags |= CHN_F_BUSY; 51682180Scg err = vchan_create(ch); 51778853Scg if (err) { 51882180Scg device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 51982180Scg ch->flags &= ~CHN_F_BUSY; 52078853Scg } 52178853Scg } 52278853Scg 52377269Scg return err; 52477269Scg} 52577269Scg 52677269Scgstatic int 52777269Scgpcm_killchan(device_t dev) 52877269Scg{ 52977269Scg struct snddev_info *d = device_get_softc(dev); 53077269Scg struct snddev_channel *sce; 531106420Scognet struct pcm_channel *ch; 532106420Scognet int error = 0; 53377269Scg 53477882Scg snd_mtxlock(d->lock); 53577269Scg sce = SLIST_FIRST(&d->channels); 53677882Scg snd_mtxunlock(d->lock); 537106420Scognet ch = sce->channel; 53877269Scg 539109236Scognet error = pcm_chn_remove(d, sce->channel, SLIST_EMPTY(&ch->children)); 540106420Scognet if (error) 541106420Scognet return (error); 542106420Scognet return (pcm_chn_destroy(ch)); 54377269Scg} 54477269Scg 54577269Scgint 54650724Scgpcm_setstatus(device_t dev, char *str) 54750724Scg{ 54874763Scg struct snddev_info *d = device_get_softc(dev); 54977882Scg 55077882Scg snd_mtxlock(d->lock); 55150724Scg strncpy(d->status, str, SND_STATUSLEN); 55277882Scg snd_mtxunlock(d->lock); 55350724Scg return 0; 55450724Scg} 55550724Scg 55650724Scgu_int32_t 55750724Scgpcm_getflags(device_t dev) 55850724Scg{ 55974763Scg struct snddev_info *d = device_get_softc(dev); 56077882Scg 56150724Scg return d->flags; 56250724Scg} 56350724Scg 56450724Scgvoid 56550724Scgpcm_setflags(device_t dev, u_int32_t val) 56650724Scg{ 56774763Scg struct snddev_info *d = device_get_softc(dev); 56878362Scg 56950724Scg d->flags = val; 57050724Scg} 57150724Scg 57258384Scgvoid * 57358384Scgpcm_getdevinfo(device_t dev) 57458384Scg{ 57574763Scg struct snddev_info *d = device_get_softc(dev); 57677882Scg 57758384Scg return d->devinfo; 57858384Scg} 57958384Scg 58083614Scgunsigned int 58183614Scgpcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 58283614Scg{ 58383614Scg struct snddev_info *d = device_get_softc(dev); 58489690Scg int sz, x; 58583614Scg 58683614Scg sz = 0; 58789690Scg if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 58889690Scg x = sz; 58983614Scg RANGE(sz, min, max); 59089690Scg if (x != sz) 59189690Scg device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 59289690Scg x = min; 59389690Scg while (x < sz) 59489690Scg x <<= 1; 59589690Scg if (x > sz) 59689690Scg x >>= 1; 59789690Scg if (x != sz) { 59889690Scg device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 59989690Scg sz = x; 60089690Scg } 60189690Scg } else { 60283614Scg sz = deflt; 60389690Scg } 60489690Scg 60583614Scg d->bufsz = sz; 60683614Scg 60783614Scg return sz; 60883614Scg} 60983614Scg 61050724Scgint 61150724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec) 61250724Scg{ 61374763Scg struct snddev_info *d = device_get_softc(dev); 61450724Scg 61589834Scg if (pcm_veto_load) { 61689834Scg device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 61789834Scg 61889834Scg return EINVAL; 61989834Scg } 62089834Scg 62193814Sjhb d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 62277269Scg 62378853Scg d->flags = 0; 62461886Scg d->dev = dev; 62550724Scg d->devinfo = devinfo; 62678895Scg d->devcount = 0; 62783089Scg d->reccount = 0; 62883614Scg d->playcount = 0; 62978895Scg d->vchancount = 0; 63078362Scg d->inprog = 0; 63150724Scg 63278362Scg if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 63378214Scg d->flags |= SD_F_SIMPLEX; 63478362Scg 63578214Scg d->fakechan = fkchan_setup(dev); 63678214Scg chn_init(d->fakechan, NULL, 0); 63755494Scg 63873127Scg#ifdef SND_DYNSYSCTL 63970680Sjhb sysctl_ctx_init(&d->sysctl_tree); 64070680Sjhb d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 64170943Sjhb SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 64270680Sjhb device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 64370943Sjhb if (d->sysctl_tree_top == NULL) { 64470680Sjhb sysctl_ctx_free(&d->sysctl_tree); 64570680Sjhb goto no; 64670680Sjhb } 64783614Scg SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 64883614Scg OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 64973127Scg#endif 65078214Scg if (numplay > 0) 65182180Scg vchan_initsys(dev); 65278853Scg if (numplay == 1) 65378853Scg d->flags |= SD_F_AUTOVCHAN; 65478853Scg 65582180Scg sndstat_register(dev, d->status, sndstat_prepare_pcm); 65650724Scg return 0; 65750724Scgno: 65877882Scg snd_mtxfree(d->lock); 65950724Scg return ENXIO; 66050724Scg} 66150724Scg 66265340Scgint 66365340Scgpcm_unregister(device_t dev) 66465207Scg{ 66574763Scg struct snddev_info *d = device_get_softc(dev); 66677269Scg struct snddev_channel *sce; 66783614Scg struct pcm_channel *ch; 66865207Scg 66977882Scg snd_mtxlock(d->lock); 67078362Scg if (d->inprog) { 67183476Sgreid device_printf(dev, "unregister: operation in progress\n"); 67278362Scg snd_mtxunlock(d->lock); 67378362Scg return EBUSY; 67478362Scg } 67583476Sgreid if (sndstat_busy() != 0) { 67683476Sgreid device_printf(dev, "unregister: sndstat busy\n"); 67783476Sgreid snd_mtxunlock(d->lock); 67883476Sgreid return EBUSY; 67983476Sgreid } 68077269Scg SLIST_FOREACH(sce, &d->channels, link) { 68183614Scg ch = sce->channel; 68283614Scg if (ch->refcount > 0) { 68395684Scg device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 68477882Scg snd_mtxunlock(d->lock); 68577269Scg return EBUSY; 68677269Scg } 68765487Scg } 68878362Scg if (mixer_uninit(dev)) { 68983476Sgreid device_printf(dev, "unregister: mixer busy\n"); 69077882Scg snd_mtxunlock(d->lock); 69165487Scg return EBUSY; 69265487Scg } 69365207Scg 69474763Scg#ifdef SND_DYNSYSCTL 69574763Scg d->sysctl_tree_top = NULL; 69674763Scg sysctl_ctx_free(&d->sysctl_tree); 69774763Scg#endif 69878395Scg while (!SLIST_EMPTY(&d->channels)) 69977269Scg pcm_killchan(dev); 70065207Scg 70174763Scg chn_kill(d->fakechan); 70274763Scg fkchan_kill(d->fakechan); 70373127Scg 704102374Snsayer sndstat_unregister(dev); 70577882Scg snd_mtxfree(d->lock); 70665340Scg return 0; 70765207Scg} 70865207Scg 70982180Scg/************************************************************************/ 71082180Scg 71182180Scgstatic int 71282180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 71382180Scg{ 71482180Scg struct snddev_info *d; 71582180Scg struct snddev_channel *sce; 71682180Scg struct pcm_channel *c; 71782180Scg struct pcm_feeder *f; 71882180Scg int pc, rc, vc; 71982180Scg 72082180Scg if (verbose < 1) 72182180Scg return 0; 72282180Scg 72382180Scg d = device_get_softc(dev); 72482180Scg if (!d) 72582180Scg return ENXIO; 72682180Scg 72782180Scg snd_mtxlock(d->lock); 72882180Scg if (!SLIST_EMPTY(&d->channels)) { 72982180Scg pc = rc = vc = 0; 73082180Scg SLIST_FOREACH(sce, &d->channels, link) { 73182180Scg c = sce->channel; 73282180Scg if (c->direction == PCMDIR_PLAY) { 73382180Scg if (c->flags & CHN_F_VIRTUAL) 73482180Scg vc++; 73582180Scg else 73682180Scg pc++; 73782180Scg } else 73882180Scg rc++; 73982180Scg } 74083614Scg sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 74182180Scg (d->flags & SD_F_SIMPLEX)? "" : " duplex", 74282180Scg#ifdef USING_DEVFS 74382180Scg (device_get_unit(dev) == snd_unit)? " default" : "" 74482180Scg#else 74582180Scg "" 74682180Scg#endif 74782180Scg ); 74882180Scg if (verbose <= 1) 74982180Scg goto skipverbose; 75082180Scg SLIST_FOREACH(sce, &d->channels, link) { 75182180Scg c = sce->channel; 75282479Scg sbuf_printf(s, "\n\t"); 75382479Scg 75482479Scg sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 75589691Scg sbuf_printf(s, "spd %d", c->speed); 75689691Scg if (c->speed != sndbuf_getspd(c->bufhard)) 75789691Scg sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 75889691Scg sbuf_printf(s, ", fmt 0x%08x", c->format); 75989691Scg if (c->format != sndbuf_getfmt(c->bufhard)) 76089691Scg sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 76189691Scg sbuf_printf(s, ", flags %08x", c->flags); 76282180Scg if (c->pid != -1) 76382180Scg sbuf_printf(s, ", pid %d", c->pid); 76482180Scg sbuf_printf(s, "\n\t"); 76589691Scg 76682492Scg if (c->bufhard != NULL && c->bufsoft != NULL) { 76782479Scg sbuf_printf(s, "interrupts %d, ", c->interrupts); 76882479Scg if (c->direction == PCMDIR_REC) 76982479Scg sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 77082479Scg c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 77182479Scg else 77282492Scg sbuf_printf(s, "underruns %d, ready %d", 77382492Scg c->xruns, sndbuf_getready(c->bufsoft)); 77482479Scg sbuf_printf(s, "\n\t"); 77582479Scg } 77689691Scg 77789691Scg sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 77889691Scg sbuf_printf(s, " -> "); 77982180Scg f = c->feeder; 78089691Scg while (f->source != NULL) 78189691Scg f = f->source; 78289691Scg while (f != NULL) { 78382180Scg sbuf_printf(s, "%s", f->class->name); 78482180Scg if (f->desc->type == FEEDER_FMT) 78589691Scg sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 78682180Scg if (f->desc->type == FEEDER_RATE) 78789691Scg sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 78882180Scg if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 78989691Scg sbuf_printf(s, "(0x%08x)", f->desc->out); 79089691Scg sbuf_printf(s, " -> "); 79189691Scg f = f->parent; 79282180Scg } 79389691Scg sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 79482180Scg } 79582180Scg } else 79682180Scg sbuf_printf(s, " (mixer only)"); 79796928Speterskipverbose: 79882180Scg snd_mtxunlock(d->lock); 79982180Scg 80082180Scg return 0; 80182180Scg} 80282180Scg 80382180Scg/************************************************************************/ 80482180Scg 80582180Scg#ifdef SND_DYNSYSCTL 80682180Scgint 80782180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 80882180Scg{ 80982180Scg struct snddev_info *d; 81082180Scg struct snddev_channel *sce; 81182180Scg struct pcm_channel *c; 812100654Sgreen int err, newcnt, cnt; 81382180Scg 81482180Scg d = oidp->oid_arg1; 81582180Scg 81682180Scg pcm_lock(d); 81782180Scg cnt = 0; 81882180Scg SLIST_FOREACH(sce, &d->channels, link) { 81982180Scg c = sce->channel; 82082180Scg if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) 82182180Scg cnt++; 82282180Scg } 82382180Scg newcnt = cnt; 82482180Scg 825100654Sgreen pcm_unlock(d); 82682180Scg err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 827100654Sgreen pcm_lock(d); 828100654Sgreen /* 829100654Sgreen * Since we dropped the pcm_lock, reload cnt now as it may 830100654Sgreen * have changed. 831100654Sgreen */ 832100654Sgreen cnt = 0; 833100654Sgreen SLIST_FOREACH(sce, &d->channels, link) { 834100654Sgreen c = sce->channel; 835100654Sgreen if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) 836100654Sgreen cnt++; 837100654Sgreen } 83882180Scg if (err == 0 && req->newptr != NULL) { 83982180Scg if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 84082180Scg pcm_unlock(d); 84182180Scg return EINVAL; 84282180Scg } 84382180Scg 84482180Scg if (newcnt > cnt) { 84582180Scg /* add new vchans - find a parent channel first */ 84682180Scg SLIST_FOREACH(sce, &d->channels, link) { 84782180Scg c = sce->channel; 84882180Scg /* not a candidate if not a play channel */ 84982180Scg if (c->direction != PCMDIR_PLAY) 85096928Speter continue; 85182180Scg /* not a candidate if a virtual channel */ 85282180Scg if (c->flags & CHN_F_VIRTUAL) 85396928Speter continue; 85482180Scg /* not a candidate if it's in use */ 85582180Scg if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 85696928Speter continue; 85782180Scg /* 85882180Scg * if we get here we're a nonvirtual play channel, and either 85982180Scg * 1) not busy 86082180Scg * 2) busy with children, not directly open 86182180Scg * 86282180Scg * thus we can add children 86382180Scg */ 86482180Scg goto addok; 86582180Scg } 86682180Scg pcm_unlock(d); 86782180Scg return EBUSY; 86882180Scgaddok: 86982180Scg c->flags |= CHN_F_BUSY; 87082180Scg while (err == 0 && newcnt > cnt) { 87182180Scg err = vchan_create(c); 87282180Scg if (err == 0) 87382180Scg cnt++; 87482180Scg } 87582180Scg if (SLIST_EMPTY(&c->children)) 87682180Scg c->flags &= ~CHN_F_BUSY; 87782180Scg } else if (newcnt < cnt) { 87882180Scg while (err == 0 && newcnt < cnt) { 87982180Scg SLIST_FOREACH(sce, &d->channels, link) { 88082180Scg c = sce->channel; 88182180Scg if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 88282180Scg goto remok; 88382180Scg } 88482180Scg pcm_unlock(d); 88582180Scg return EINVAL; 88682180Scgremok: 88782180Scg err = vchan_destroy(c); 88882180Scg if (err == 0) 88982180Scg cnt--; 89082180Scg } 89182180Scg } 89282180Scg } 89382180Scg 89482180Scg pcm_unlock(d); 89582180Scg return err; 89682180Scg} 89782180Scg#endif 89882180Scg 89982180Scg/************************************************************************/ 90082180Scg 90165340Scgstatic moduledata_t sndpcm_mod = { 90265340Scg "snd_pcm", 90378362Scg NULL, 90465340Scg NULL 90565340Scg}; 90665340ScgDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 90765340ScgMODULE_VERSION(snd_pcm, PCM_MODVER); 908