sound.c revision 93814
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 93814 2002-04-04 20:54:27Z jhb $"); 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; 5382180Scg void *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 9473131Scg 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 11073131Scg 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} 12773131Scg 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} 14773131Scg 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); 28382180Scg goto restart; 28482180Scg } 28582180Scg } 28682180Scgrestart: 28782180Scg } 28882180Scg } 28982180Scg} 29082180Scg 29173127Scg#ifdef USING_DEVFS 29265340Scgstatic int 29378853Scgsysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 29465340Scg{ 29578214Scg struct snddev_info *d; 29665340Scg int error, unit; 29765340Scg 29865340Scg unit = snd_unit; 29965340Scg error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 30065340Scg if (error == 0 && req->newptr != NULL) { 30178396Scg if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 30278214Scg return EINVAL; 30378214Scg d = devclass_get_softc(pcm_devclass, unit); 30478395Scg if (d == NULL || SLIST_EMPTY(&d->channels)) 30578214Scg return EINVAL; 30665340Scg snd_unit = unit; 30765340Scg } 30865340Scg return (error); 30965340Scg} 31070617SjhbSYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 31178853Scg 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 31273127Scg#endif 31365340Scg 31478853Scgstatic int 31582180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 31678853Scg{ 31782180Scg struct snddev_info *d; 31882180Scg int i, v, error; 31978853Scg 32082180Scg v = snd_maxautovchans; 32178853Scg error = sysctl_handle_int(oidp, &v, sizeof(v), req); 32278853Scg if (error == 0 && req->newptr != NULL) { 32378853Scg if (v < 0 || v >= SND_MAXVCHANS) 32478853Scg return EINVAL; 32582180Scg if (v != snd_maxautovchans) { 32682180Scg for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 32782180Scg d = devclass_get_softc(pcm_devclass, i); 32882180Scg if (!d) 32982180Scg continue; 33082180Scg pcm_setmaxautovchans(d, v); 33182180Scg } 33282180Scg } 33382180Scg snd_maxautovchans = v; 33478853Scg } 33578853Scg return (error); 33678853Scg} 33782180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 33882180Scg 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 33978853Scg 34077269Scgstruct pcm_channel * 34177269Scgpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 34250724Scg{ 34377269Scg struct pcm_channel *ch; 34465340Scg char *dirs; 34583614Scg int err, *pnum; 34650724Scg 34777269Scg switch(dir) { 34877269Scg case PCMDIR_PLAY: 34977269Scg dirs = "play"; 35083614Scg pnum = &d->playcount; 35177269Scg break; 35283614Scg 35377269Scg case PCMDIR_REC: 35477269Scg dirs = "record"; 35583614Scg pnum = &d->reccount; 35677269Scg break; 35783614Scg 35877269Scg case PCMDIR_VIRTUAL: 35977269Scg dirs = "virtual"; 36077269Scg dir = PCMDIR_PLAY; 36183614Scg pnum = &d->vchancount; 36277269Scg break; 36383614Scg 36477269Scg default: 36577269Scg return NULL; 36677269Scg } 36765340Scg 36877269Scg ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 36977269Scg if (!ch) 37077269Scg return NULL; 37177269Scg 37277269Scg ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 37377269Scg if (!ch->methods) { 37477269Scg free(ch, M_DEVBUF); 37583614Scg 37677269Scg return NULL; 37761344Scg } 37877269Scg 37983614Scg ch->num = (*pnum)++; 38083614Scg 38177269Scg ch->pid = -1; 38277269Scg ch->parentsnddev = d; 38377269Scg ch->parentchannel = parent; 38489774Sscottl ch->dev = d->dev; 38583614Scg snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num); 38677269Scg 38770134Scg err = chn_init(ch, devinfo, dir); 38870134Scg if (err) { 38983614Scg device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 39077269Scg kobj_delete(ch->methods, M_DEVBUF); 39177269Scg free(ch, M_DEVBUF); 39283614Scg (*pnum)--; 39383614Scg 39477269Scg return NULL; 39555483Scg } 39677269Scg 39777269Scg return ch; 39877269Scg} 39977269Scg 40077269Scgint 40177269Scgpcm_chn_destroy(struct pcm_channel *ch) 40277269Scg{ 40383614Scg struct snddev_info *d; 40477269Scg int err; 40577269Scg 40683614Scg d = ch->parentsnddev; 40777269Scg err = chn_kill(ch); 40877269Scg if (err) { 40983614Scg device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 41077269Scg return err; 41177269Scg } 41277269Scg 41383614Scg if (ch->direction == PCMDIR_REC) 41483614Scg d->reccount--; 41583614Scg else if (ch->flags & CHN_F_VIRTUAL) 41683614Scg d->vchancount--; 41783614Scg else 41883614Scg d->playcount--; 41983614Scg 42077269Scg kobj_delete(ch->methods, M_DEVBUF); 42177269Scg free(ch, M_DEVBUF); 42277269Scg 42377269Scg return 0; 42477269Scg} 42577269Scg 42677269Scgint 42778895Scgpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 42877269Scg{ 42982180Scg struct snddev_channel *sce, *tmp, *after; 43077269Scg int unit = device_get_unit(d->dev); 43177269Scg 43278214Scg snd_mtxlock(d->lock); 43378214Scg 43477269Scg sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 43577269Scg if (!sce) { 43678214Scg snd_mtxunlock(d->lock); 43777269Scg return ENOMEM; 43877269Scg } 43977269Scg 44077269Scg sce->channel = ch; 44182180Scg if (SLIST_EMPTY(&d->channels)) { 44282180Scg SLIST_INSERT_HEAD(&d->channels, sce, link); 44382180Scg } else { 44482180Scg after = NULL; 44582180Scg SLIST_FOREACH(tmp, &d->channels, link) { 44682180Scg after = tmp; 44782180Scg } 44882180Scg SLIST_INSERT_AFTER(after, sce, link); 44982180Scg } 45077269Scg 45183089Scg if (mkdev) { 45278895Scg dsp_register(unit, d->devcount++); 45383089Scg if (ch->direction == PCMDIR_REC) 45483089Scg dsp_registerrec(unit, ch->num); 45583089Scg } 45677269Scg 45777882Scg snd_mtxunlock(d->lock); 45877269Scg 45950724Scg return 0; 46050724Scg} 46150724Scg 46277269Scgint 46378895Scgpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 46465340Scg{ 46577269Scg struct snddev_channel *sce; 46677269Scg int unit = device_get_unit(d->dev); 46765340Scg 46877882Scg snd_mtxlock(d->lock); 46977269Scg SLIST_FOREACH(sce, &d->channels, link) { 47077269Scg if (sce->channel == ch) 47177269Scg goto gotit; 47265340Scg } 47377882Scg snd_mtxunlock(d->lock); 47477269Scg return EINVAL; 47577269Scggotit: 47677269Scg SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 47777269Scg free(sce, M_DEVBUF); 47877269Scg 47983089Scg if (rmdev) { 48078895Scg dsp_unregister(unit, --d->devcount); 48183089Scg if (ch->direction == PCMDIR_REC) 48283614Scg dsp_unregisterrec(unit, ch->num); 48383089Scg } 48477882Scg snd_mtxunlock(d->lock); 48577269Scg 48665340Scg return 0; 48765340Scg} 48865340Scg 48950724Scgint 49077269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 49177269Scg{ 49277269Scg struct snddev_info *d = device_get_softc(dev); 49382180Scg struct pcm_channel *ch; 49482180Scg int err; 49577269Scg 49677269Scg ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 49777269Scg if (!ch) { 49877269Scg device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 49977269Scg return ENODEV; 50077269Scg } 50178853Scg 50278895Scg err = pcm_chn_add(d, ch, 1); 50377269Scg if (err) { 50477269Scg device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 50577269Scg pcm_chn_destroy(ch); 50678853Scg return err; 50777269Scg } 50877269Scg 50982180Scg if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) { 51078853Scg ch->flags |= CHN_F_BUSY; 51182180Scg err = vchan_create(ch); 51278853Scg if (err) { 51382180Scg device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 51482180Scg ch->flags &= ~CHN_F_BUSY; 51578853Scg } 51678853Scg } 51778853Scg 51877269Scg return err; 51977269Scg} 52077269Scg 52177269Scgstatic int 52277269Scgpcm_killchan(device_t dev) 52377269Scg{ 52477269Scg struct snddev_info *d = device_get_softc(dev); 52577269Scg struct snddev_channel *sce; 52677269Scg 52777882Scg snd_mtxlock(d->lock); 52877269Scg sce = SLIST_FIRST(&d->channels); 52977882Scg snd_mtxunlock(d->lock); 53077269Scg 53178895Scg return pcm_chn_remove(d, sce->channel, 1); 53277269Scg} 53377269Scg 53477269Scgint 53550724Scgpcm_setstatus(device_t dev, char *str) 53650724Scg{ 53774763Scg struct snddev_info *d = device_get_softc(dev); 53877882Scg 53977882Scg snd_mtxlock(d->lock); 54050724Scg strncpy(d->status, str, SND_STATUSLEN); 54177882Scg snd_mtxunlock(d->lock); 54250724Scg return 0; 54350724Scg} 54450724Scg 54550724Scgu_int32_t 54650724Scgpcm_getflags(device_t dev) 54750724Scg{ 54874763Scg struct snddev_info *d = device_get_softc(dev); 54977882Scg 55050724Scg return d->flags; 55150724Scg} 55250724Scg 55350724Scgvoid 55450724Scgpcm_setflags(device_t dev, u_int32_t val) 55550724Scg{ 55674763Scg struct snddev_info *d = device_get_softc(dev); 55778362Scg 55850724Scg d->flags = val; 55950724Scg} 56050724Scg 56158384Scgvoid * 56258384Scgpcm_getdevinfo(device_t dev) 56358384Scg{ 56474763Scg struct snddev_info *d = device_get_softc(dev); 56577882Scg 56658384Scg return d->devinfo; 56758384Scg} 56858384Scg 56983614Scgunsigned int 57083614Scgpcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 57183614Scg{ 57283614Scg struct snddev_info *d = device_get_softc(dev); 57389690Scg int sz, x; 57483614Scg 57583614Scg sz = 0; 57689690Scg if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 57789690Scg x = sz; 57883614Scg RANGE(sz, min, max); 57989690Scg if (x != sz) 58089690Scg device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 58189690Scg x = min; 58289690Scg while (x < sz) 58389690Scg x <<= 1; 58489690Scg if (x > sz) 58589690Scg x >>= 1; 58689690Scg if (x != sz) { 58789690Scg device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 58889690Scg sz = x; 58989690Scg } 59089690Scg } else { 59183614Scg sz = deflt; 59289690Scg } 59389690Scg 59483614Scg d->bufsz = sz; 59583614Scg 59683614Scg return sz; 59783614Scg} 59883614Scg 59950724Scgint 60050724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec) 60150724Scg{ 60274763Scg struct snddev_info *d = device_get_softc(dev); 60350724Scg 60489834Scg if (pcm_veto_load) { 60589834Scg device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 60689834Scg 60789834Scg return EINVAL; 60889834Scg } 60989834Scg 61093814Sjhb d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 61177882Scg snd_mtxlock(d->lock); 61277269Scg 61378853Scg d->flags = 0; 61461886Scg d->dev = dev; 61550724Scg d->devinfo = devinfo; 61678895Scg d->devcount = 0; 61783089Scg d->reccount = 0; 61883614Scg d->playcount = 0; 61978895Scg d->vchancount = 0; 62078362Scg d->inprog = 0; 62150724Scg 62278362Scg if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 62378214Scg d->flags |= SD_F_SIMPLEX; 62478362Scg 62578214Scg d->fakechan = fkchan_setup(dev); 62678214Scg chn_init(d->fakechan, NULL, 0); 62755494Scg 62873127Scg#ifdef SND_DYNSYSCTL 62970680Sjhb sysctl_ctx_init(&d->sysctl_tree); 63070680Sjhb d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 63170943Sjhb SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 63270680Sjhb device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 63370943Sjhb if (d->sysctl_tree_top == NULL) { 63470680Sjhb sysctl_ctx_free(&d->sysctl_tree); 63570680Sjhb goto no; 63670680Sjhb } 63783614Scg SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 63883614Scg OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 63973127Scg#endif 64078214Scg if (numplay > 0) 64182180Scg vchan_initsys(dev); 64278853Scg if (numplay == 1) 64378853Scg d->flags |= SD_F_AUTOVCHAN; 64478853Scg 64577882Scg snd_mtxunlock(d->lock); 64682180Scg sndstat_register(dev, d->status, sndstat_prepare_pcm); 64750724Scg return 0; 64850724Scgno: 64977882Scg snd_mtxfree(d->lock); 65050724Scg return ENXIO; 65150724Scg} 65250724Scg 65365340Scgint 65465340Scgpcm_unregister(device_t dev) 65565207Scg{ 65674763Scg struct snddev_info *d = device_get_softc(dev); 65777269Scg struct snddev_channel *sce; 65883614Scg struct pcm_channel *ch; 65965207Scg 66077882Scg snd_mtxlock(d->lock); 66178362Scg if (d->inprog) { 66283476Sgreid device_printf(dev, "unregister: operation in progress\n"); 66378362Scg snd_mtxunlock(d->lock); 66478362Scg return EBUSY; 66578362Scg } 66683476Sgreid if (sndstat_busy() != 0) { 66783476Sgreid device_printf(dev, "unregister: sndstat busy\n"); 66883476Sgreid snd_mtxunlock(d->lock); 66983476Sgreid return EBUSY; 67083476Sgreid } 67177269Scg SLIST_FOREACH(sce, &d->channels, link) { 67283614Scg ch = sce->channel; 67383614Scg if (ch->refcount > 0) { 67483614Scg device_printf(dev, "unregister: channel %s busy (pid %d)", ch->name, ch->pid); 67577882Scg snd_mtxunlock(d->lock); 67677269Scg return EBUSY; 67777269Scg } 67865487Scg } 67978362Scg if (mixer_uninit(dev)) { 68083476Sgreid device_printf(dev, "unregister: mixer busy\n"); 68177882Scg snd_mtxunlock(d->lock); 68265487Scg return EBUSY; 68365487Scg } 68465207Scg 68574763Scg#ifdef SND_DYNSYSCTL 68674763Scg d->sysctl_tree_top = NULL; 68774763Scg sysctl_ctx_free(&d->sysctl_tree); 68874763Scg#endif 68978395Scg while (!SLIST_EMPTY(&d->channels)) 69077269Scg pcm_killchan(dev); 69165207Scg 69274763Scg chn_kill(d->fakechan); 69374763Scg fkchan_kill(d->fakechan); 69473127Scg 69577882Scg snd_mtxfree(d->lock); 69665340Scg return 0; 69765207Scg} 69865207Scg 69982180Scg/************************************************************************/ 70082180Scg 70182180Scgstatic int 70282180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 70382180Scg{ 70482180Scg struct snddev_info *d; 70582180Scg struct snddev_channel *sce; 70682180Scg struct pcm_channel *c; 70782180Scg struct pcm_feeder *f; 70882180Scg int pc, rc, vc; 70982180Scg 71082180Scg if (verbose < 1) 71182180Scg return 0; 71282180Scg 71382180Scg d = device_get_softc(dev); 71482180Scg if (!d) 71582180Scg return ENXIO; 71682180Scg 71782180Scg snd_mtxlock(d->lock); 71882180Scg if (!SLIST_EMPTY(&d->channels)) { 71982180Scg pc = rc = vc = 0; 72082180Scg SLIST_FOREACH(sce, &d->channels, link) { 72182180Scg c = sce->channel; 72282180Scg if (c->direction == PCMDIR_PLAY) { 72382180Scg if (c->flags & CHN_F_VIRTUAL) 72482180Scg vc++; 72582180Scg else 72682180Scg pc++; 72782180Scg } else 72882180Scg rc++; 72982180Scg } 73083614Scg sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 73182180Scg (d->flags & SD_F_SIMPLEX)? "" : " duplex", 73282180Scg#ifdef USING_DEVFS 73382180Scg (device_get_unit(dev) == snd_unit)? " default" : "" 73482180Scg#else 73582180Scg "" 73682180Scg#endif 73782180Scg ); 73882180Scg if (verbose <= 1) 73982180Scg goto skipverbose; 74082180Scg SLIST_FOREACH(sce, &d->channels, link) { 74182180Scg c = sce->channel; 74282479Scg sbuf_printf(s, "\n\t"); 74382479Scg 74482479Scg sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 74589691Scg sbuf_printf(s, "spd %d", c->speed); 74689691Scg if (c->speed != sndbuf_getspd(c->bufhard)) 74789691Scg sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 74889691Scg sbuf_printf(s, ", fmt 0x%08x", c->format); 74989691Scg if (c->format != sndbuf_getfmt(c->bufhard)) 75089691Scg sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 75189691Scg sbuf_printf(s, ", flags %08x", c->flags); 75282180Scg if (c->pid != -1) 75382180Scg sbuf_printf(s, ", pid %d", c->pid); 75482180Scg sbuf_printf(s, "\n\t"); 75589691Scg 75682492Scg if (c->bufhard != NULL && c->bufsoft != NULL) { 75782479Scg sbuf_printf(s, "interrupts %d, ", c->interrupts); 75882479Scg if (c->direction == PCMDIR_REC) 75982479Scg sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 76082479Scg c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 76182479Scg else 76282492Scg sbuf_printf(s, "underruns %d, ready %d", 76382492Scg c->xruns, sndbuf_getready(c->bufsoft)); 76482479Scg sbuf_printf(s, "\n\t"); 76582479Scg } 76689691Scg 76789691Scg sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 76889691Scg sbuf_printf(s, " -> "); 76982180Scg f = c->feeder; 77089691Scg while (f->source != NULL) 77189691Scg f = f->source; 77289691Scg while (f != NULL) { 77382180Scg sbuf_printf(s, "%s", f->class->name); 77482180Scg if (f->desc->type == FEEDER_FMT) 77589691Scg sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 77682180Scg if (f->desc->type == FEEDER_RATE) 77789691Scg sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 77882180Scg if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 77989691Scg sbuf_printf(s, "(0x%08x)", f->desc->out); 78089691Scg sbuf_printf(s, " -> "); 78189691Scg f = f->parent; 78282180Scg } 78389691Scg sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 78482180Scg } 78582180Scgskipverbose: 78682180Scg } else 78782180Scg sbuf_printf(s, " (mixer only)"); 78882180Scg snd_mtxunlock(d->lock); 78982180Scg 79082180Scg return 0; 79182180Scg} 79282180Scg 79382180Scg/************************************************************************/ 79482180Scg 79582180Scg#ifdef SND_DYNSYSCTL 79682180Scgint 79782180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 79882180Scg{ 79982180Scg struct snddev_info *d; 80082180Scg struct snddev_channel *sce; 80182180Scg struct pcm_channel *c; 80282180Scg int err, oldcnt, newcnt, cnt; 80382180Scg 80482180Scg d = oidp->oid_arg1; 80582180Scg 80682180Scg pcm_lock(d); 80782180Scg cnt = 0; 80882180Scg SLIST_FOREACH(sce, &d->channels, link) { 80982180Scg c = sce->channel; 81082180Scg if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) 81182180Scg cnt++; 81282180Scg } 81382180Scg oldcnt = cnt; 81482180Scg newcnt = cnt; 81582180Scg 81682180Scg err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 81782180Scg if (err == 0 && req->newptr != NULL) { 81882180Scg if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 81982180Scg pcm_unlock(d); 82082180Scg return EINVAL; 82182180Scg } 82282180Scg 82382180Scg if (newcnt > cnt) { 82482180Scg /* add new vchans - find a parent channel first */ 82582180Scg SLIST_FOREACH(sce, &d->channels, link) { 82682180Scg c = sce->channel; 82782180Scg /* not a candidate if not a play channel */ 82882180Scg if (c->direction != PCMDIR_PLAY) 82982180Scg goto addskip; 83082180Scg /* not a candidate if a virtual channel */ 83182180Scg if (c->flags & CHN_F_VIRTUAL) 83282180Scg goto addskip; 83382180Scg /* not a candidate if it's in use */ 83482180Scg if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 83582180Scg goto addskip; 83682180Scg /* 83782180Scg * if we get here we're a nonvirtual play channel, and either 83882180Scg * 1) not busy 83982180Scg * 2) busy with children, not directly open 84082180Scg * 84182180Scg * thus we can add children 84282180Scg */ 84382180Scg goto addok; 84482180Scgaddskip: 84582180Scg } 84682180Scg pcm_unlock(d); 84782180Scg return EBUSY; 84882180Scgaddok: 84982180Scg c->flags |= CHN_F_BUSY; 85082180Scg while (err == 0 && newcnt > cnt) { 85182180Scg err = vchan_create(c); 85282180Scg if (err == 0) 85382180Scg cnt++; 85482180Scg } 85582180Scg if (SLIST_EMPTY(&c->children)) 85682180Scg c->flags &= ~CHN_F_BUSY; 85782180Scg } else if (newcnt < cnt) { 85882180Scg while (err == 0 && newcnt < cnt) { 85982180Scg SLIST_FOREACH(sce, &d->channels, link) { 86082180Scg c = sce->channel; 86182180Scg if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 86282180Scg goto remok; 86382180Scg } 86482180Scg pcm_unlock(d); 86582180Scg return EINVAL; 86682180Scgremok: 86782180Scg err = vchan_destroy(c); 86882180Scg if (err == 0) 86982180Scg cnt--; 87082180Scg } 87182180Scg } 87282180Scg } 87382180Scg 87482180Scg pcm_unlock(d); 87582180Scg return err; 87682180Scg} 87782180Scg#endif 87882180Scg 87982180Scg/************************************************************************/ 88082180Scg 88165340Scgstatic moduledata_t sndpcm_mod = { 88265340Scg "snd_pcm", 88378362Scg NULL, 88465340Scg NULL 88565340Scg}; 88665340ScgDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 88765340ScgMODULE_VERSION(snd_pcm, PCM_MODVER); 888