sound.c revision 100576
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 100576 2002-07-23 14:50:51Z kan $"); 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); 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 36777269Scg ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 36877269Scg if (!ch) 36977269Scg return NULL; 37077269Scg 37177269Scg ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 37277269Scg if (!ch->methods) { 37377269Scg free(ch, M_DEVBUF); 37483614Scg 37577269Scg return NULL; 37661344Scg } 37777269Scg 37883614Scg ch->num = (*pnum)++; 37983614Scg 38077269Scg ch->pid = -1; 38177269Scg ch->parentsnddev = d; 38277269Scg ch->parentchannel = parent; 38389774Sscottl ch->dev = d->dev; 38483614Scg snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num); 38577269Scg 38670134Scg err = chn_init(ch, devinfo, dir); 38770134Scg if (err) { 38883614Scg device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 38977269Scg kobj_delete(ch->methods, M_DEVBUF); 39077269Scg free(ch, M_DEVBUF); 39183614Scg (*pnum)--; 39283614Scg 39377269Scg return NULL; 39455483Scg } 39577269Scg 39677269Scg return ch; 39777269Scg} 39877269Scg 39977269Scgint 40077269Scgpcm_chn_destroy(struct pcm_channel *ch) 40177269Scg{ 40283614Scg struct snddev_info *d; 40377269Scg int err; 40477269Scg 40583614Scg d = ch->parentsnddev; 40677269Scg err = chn_kill(ch); 40777269Scg if (err) { 40883614Scg device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 40977269Scg return err; 41077269Scg } 41177269Scg 41283614Scg if (ch->direction == PCMDIR_REC) 41383614Scg d->reccount--; 41483614Scg else if (ch->flags & CHN_F_VIRTUAL) 41583614Scg d->vchancount--; 41683614Scg else 41783614Scg d->playcount--; 41883614Scg 41977269Scg kobj_delete(ch->methods, M_DEVBUF); 42077269Scg free(ch, M_DEVBUF); 42177269Scg 42277269Scg return 0; 42377269Scg} 42477269Scg 42577269Scgint 42678895Scgpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 42777269Scg{ 42882180Scg struct snddev_channel *sce, *tmp, *after; 42977269Scg int unit = device_get_unit(d->dev); 43077269Scg 43177269Scg sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 43277269Scg if (!sce) { 43377269Scg return ENOMEM; 43477269Scg } 43577269Scg 436100478Sorion snd_mtxlock(d->lock); 437100478Sorion 43877269Scg sce->channel = ch; 43982180Scg if (SLIST_EMPTY(&d->channels)) { 44082180Scg SLIST_INSERT_HEAD(&d->channels, sce, link); 44182180Scg } else { 44282180Scg after = NULL; 44382180Scg SLIST_FOREACH(tmp, &d->channels, link) { 44482180Scg after = tmp; 44582180Scg } 44682180Scg SLIST_INSERT_AFTER(after, sce, link); 44782180Scg } 44877269Scg 44983089Scg if (mkdev) { 45078895Scg dsp_register(unit, d->devcount++); 45183089Scg if (ch->direction == PCMDIR_REC) 45283089Scg dsp_registerrec(unit, ch->num); 45383089Scg } 45477269Scg 45577882Scg snd_mtxunlock(d->lock); 45677269Scg 45750724Scg return 0; 45850724Scg} 45950724Scg 46077269Scgint 46178895Scgpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 46265340Scg{ 46377269Scg struct snddev_channel *sce; 46477269Scg int unit = device_get_unit(d->dev); 46565340Scg 46677882Scg snd_mtxlock(d->lock); 46777269Scg SLIST_FOREACH(sce, &d->channels, link) { 46877269Scg if (sce->channel == ch) 46977269Scg goto gotit; 47065340Scg } 47177882Scg snd_mtxunlock(d->lock); 47277269Scg return EINVAL; 47377269Scggotit: 47477269Scg SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 47577269Scg free(sce, M_DEVBUF); 47677269Scg 47783089Scg if (rmdev) { 47878895Scg dsp_unregister(unit, --d->devcount); 47983089Scg if (ch->direction == PCMDIR_REC) 48083614Scg dsp_unregisterrec(unit, ch->num); 48183089Scg } 48277882Scg snd_mtxunlock(d->lock); 48377269Scg 48465340Scg return 0; 48565340Scg} 48665340Scg 48750724Scgint 48877269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 48977269Scg{ 49077269Scg struct snddev_info *d = device_get_softc(dev); 49182180Scg struct pcm_channel *ch; 49282180Scg int err; 49377269Scg 49477269Scg ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 49577269Scg if (!ch) { 49677269Scg device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 49777269Scg return ENODEV; 49877269Scg } 49978853Scg 50078895Scg err = pcm_chn_add(d, ch, 1); 50177269Scg if (err) { 50277269Scg device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 50377269Scg pcm_chn_destroy(ch); 50478853Scg return err; 50577269Scg } 50677269Scg 507100576Skan if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) && 508100576Skan ch->direction == PCMDIR_PLAY && d->vchancount == 0) { 50978853Scg ch->flags |= CHN_F_BUSY; 51082180Scg err = vchan_create(ch); 51178853Scg if (err) { 51282180Scg device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 51382180Scg ch->flags &= ~CHN_F_BUSY; 51478853Scg } 51578853Scg } 51678853Scg 51777269Scg return err; 51877269Scg} 51977269Scg 52077269Scgstatic int 52177269Scgpcm_killchan(device_t dev) 52277269Scg{ 52377269Scg struct snddev_info *d = device_get_softc(dev); 52477269Scg struct snddev_channel *sce; 52577269Scg 52677882Scg snd_mtxlock(d->lock); 52777269Scg sce = SLIST_FIRST(&d->channels); 52877882Scg snd_mtxunlock(d->lock); 52977269Scg 53078895Scg return pcm_chn_remove(d, sce->channel, 1); 53177269Scg} 53277269Scg 53377269Scgint 53450724Scgpcm_setstatus(device_t dev, char *str) 53550724Scg{ 53674763Scg struct snddev_info *d = device_get_softc(dev); 53777882Scg 53877882Scg snd_mtxlock(d->lock); 53950724Scg strncpy(d->status, str, SND_STATUSLEN); 54077882Scg snd_mtxunlock(d->lock); 54150724Scg return 0; 54250724Scg} 54350724Scg 54450724Scgu_int32_t 54550724Scgpcm_getflags(device_t dev) 54650724Scg{ 54774763Scg struct snddev_info *d = device_get_softc(dev); 54877882Scg 54950724Scg return d->flags; 55050724Scg} 55150724Scg 55250724Scgvoid 55350724Scgpcm_setflags(device_t dev, u_int32_t val) 55450724Scg{ 55574763Scg struct snddev_info *d = device_get_softc(dev); 55678362Scg 55750724Scg d->flags = val; 55850724Scg} 55950724Scg 56058384Scgvoid * 56158384Scgpcm_getdevinfo(device_t dev) 56258384Scg{ 56374763Scg struct snddev_info *d = device_get_softc(dev); 56477882Scg 56558384Scg return d->devinfo; 56658384Scg} 56758384Scg 56883614Scgunsigned int 56983614Scgpcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 57083614Scg{ 57183614Scg struct snddev_info *d = device_get_softc(dev); 57289690Scg int sz, x; 57383614Scg 57483614Scg sz = 0; 57589690Scg if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 57689690Scg x = sz; 57783614Scg RANGE(sz, min, max); 57889690Scg if (x != sz) 57989690Scg device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 58089690Scg x = min; 58189690Scg while (x < sz) 58289690Scg x <<= 1; 58389690Scg if (x > sz) 58489690Scg x >>= 1; 58589690Scg if (x != sz) { 58689690Scg device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 58789690Scg sz = x; 58889690Scg } 58989690Scg } else { 59083614Scg sz = deflt; 59189690Scg } 59289690Scg 59383614Scg d->bufsz = sz; 59483614Scg 59583614Scg return sz; 59683614Scg} 59783614Scg 59850724Scgint 59950724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec) 60050724Scg{ 60174763Scg struct snddev_info *d = device_get_softc(dev); 60250724Scg 60389834Scg if (pcm_veto_load) { 60489834Scg device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 60589834Scg 60689834Scg return EINVAL; 60789834Scg } 60889834Scg 60993814Sjhb d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 61077882Scg snd_mtxlock(d->lock); 61177269Scg 61278853Scg d->flags = 0; 61361886Scg d->dev = dev; 61450724Scg d->devinfo = devinfo; 61578895Scg d->devcount = 0; 61683089Scg d->reccount = 0; 61783614Scg d->playcount = 0; 61878895Scg d->vchancount = 0; 61978362Scg d->inprog = 0; 62050724Scg 62178362Scg if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 62278214Scg d->flags |= SD_F_SIMPLEX; 62378362Scg 62478214Scg d->fakechan = fkchan_setup(dev); 62578214Scg chn_init(d->fakechan, NULL, 0); 62655494Scg 62773127Scg#ifdef SND_DYNSYSCTL 62870680Sjhb sysctl_ctx_init(&d->sysctl_tree); 62970680Sjhb d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 63070943Sjhb SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 63170680Sjhb device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 63270943Sjhb if (d->sysctl_tree_top == NULL) { 63370680Sjhb sysctl_ctx_free(&d->sysctl_tree); 63470680Sjhb goto no; 63570680Sjhb } 63683614Scg SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 63783614Scg OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 63873127Scg#endif 63978214Scg if (numplay > 0) 64082180Scg vchan_initsys(dev); 64178853Scg if (numplay == 1) 64278853Scg d->flags |= SD_F_AUTOVCHAN; 64378853Scg 64477882Scg snd_mtxunlock(d->lock); 64582180Scg sndstat_register(dev, d->status, sndstat_prepare_pcm); 64650724Scg return 0; 64750724Scgno: 64877882Scg snd_mtxfree(d->lock); 64950724Scg return ENXIO; 65050724Scg} 65150724Scg 65265340Scgint 65365340Scgpcm_unregister(device_t dev) 65465207Scg{ 65574763Scg struct snddev_info *d = device_get_softc(dev); 65677269Scg struct snddev_channel *sce; 65783614Scg struct pcm_channel *ch; 65865207Scg 65977882Scg snd_mtxlock(d->lock); 66078362Scg if (d->inprog) { 66183476Sgreid device_printf(dev, "unregister: operation in progress\n"); 66278362Scg snd_mtxunlock(d->lock); 66378362Scg return EBUSY; 66478362Scg } 66583476Sgreid if (sndstat_busy() != 0) { 66683476Sgreid device_printf(dev, "unregister: sndstat busy\n"); 66783476Sgreid snd_mtxunlock(d->lock); 66883476Sgreid return EBUSY; 66983476Sgreid } 67077269Scg SLIST_FOREACH(sce, &d->channels, link) { 67183614Scg ch = sce->channel; 67283614Scg if (ch->refcount > 0) { 67395684Scg device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 67477882Scg snd_mtxunlock(d->lock); 67577269Scg return EBUSY; 67677269Scg } 67765487Scg } 67878362Scg if (mixer_uninit(dev)) { 67983476Sgreid device_printf(dev, "unregister: mixer busy\n"); 68077882Scg snd_mtxunlock(d->lock); 68165487Scg return EBUSY; 68265487Scg } 68365207Scg 68474763Scg#ifdef SND_DYNSYSCTL 68574763Scg d->sysctl_tree_top = NULL; 68674763Scg sysctl_ctx_free(&d->sysctl_tree); 68774763Scg#endif 68878395Scg while (!SLIST_EMPTY(&d->channels)) 68977269Scg pcm_killchan(dev); 69065207Scg 69174763Scg chn_kill(d->fakechan); 69274763Scg fkchan_kill(d->fakechan); 69373127Scg 69477882Scg snd_mtxfree(d->lock); 69565340Scg return 0; 69665207Scg} 69765207Scg 69882180Scg/************************************************************************/ 69982180Scg 70082180Scgstatic int 70182180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 70282180Scg{ 70382180Scg struct snddev_info *d; 70482180Scg struct snddev_channel *sce; 70582180Scg struct pcm_channel *c; 70682180Scg struct pcm_feeder *f; 70782180Scg int pc, rc, vc; 70882180Scg 70982180Scg if (verbose < 1) 71082180Scg return 0; 71182180Scg 71282180Scg d = device_get_softc(dev); 71382180Scg if (!d) 71482180Scg return ENXIO; 71582180Scg 71682180Scg snd_mtxlock(d->lock); 71782180Scg if (!SLIST_EMPTY(&d->channels)) { 71882180Scg pc = rc = vc = 0; 71982180Scg SLIST_FOREACH(sce, &d->channels, link) { 72082180Scg c = sce->channel; 72182180Scg if (c->direction == PCMDIR_PLAY) { 72282180Scg if (c->flags & CHN_F_VIRTUAL) 72382180Scg vc++; 72482180Scg else 72582180Scg pc++; 72682180Scg } else 72782180Scg rc++; 72882180Scg } 72983614Scg sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 73082180Scg (d->flags & SD_F_SIMPLEX)? "" : " duplex", 73182180Scg#ifdef USING_DEVFS 73282180Scg (device_get_unit(dev) == snd_unit)? " default" : "" 73382180Scg#else 73482180Scg "" 73582180Scg#endif 73682180Scg ); 73782180Scg if (verbose <= 1) 73882180Scg goto skipverbose; 73982180Scg SLIST_FOREACH(sce, &d->channels, link) { 74082180Scg c = sce->channel; 74182479Scg sbuf_printf(s, "\n\t"); 74282479Scg 74382479Scg sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 74489691Scg sbuf_printf(s, "spd %d", c->speed); 74589691Scg if (c->speed != sndbuf_getspd(c->bufhard)) 74689691Scg sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 74789691Scg sbuf_printf(s, ", fmt 0x%08x", c->format); 74889691Scg if (c->format != sndbuf_getfmt(c->bufhard)) 74989691Scg sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 75089691Scg sbuf_printf(s, ", flags %08x", c->flags); 75182180Scg if (c->pid != -1) 75282180Scg sbuf_printf(s, ", pid %d", c->pid); 75382180Scg sbuf_printf(s, "\n\t"); 75489691Scg 75582492Scg if (c->bufhard != NULL && c->bufsoft != NULL) { 75682479Scg sbuf_printf(s, "interrupts %d, ", c->interrupts); 75782479Scg if (c->direction == PCMDIR_REC) 75882479Scg sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 75982479Scg c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 76082479Scg else 76182492Scg sbuf_printf(s, "underruns %d, ready %d", 76282492Scg c->xruns, sndbuf_getready(c->bufsoft)); 76382479Scg sbuf_printf(s, "\n\t"); 76482479Scg } 76589691Scg 76689691Scg sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 76789691Scg sbuf_printf(s, " -> "); 76882180Scg f = c->feeder; 76989691Scg while (f->source != NULL) 77089691Scg f = f->source; 77189691Scg while (f != NULL) { 77282180Scg sbuf_printf(s, "%s", f->class->name); 77382180Scg if (f->desc->type == FEEDER_FMT) 77489691Scg sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 77582180Scg if (f->desc->type == FEEDER_RATE) 77689691Scg sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 77782180Scg if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 77889691Scg sbuf_printf(s, "(0x%08x)", f->desc->out); 77989691Scg sbuf_printf(s, " -> "); 78089691Scg f = f->parent; 78182180Scg } 78289691Scg sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 78382180Scg } 78482180Scg } else 78582180Scg sbuf_printf(s, " (mixer only)"); 78696928Speterskipverbose: 78782180Scg snd_mtxunlock(d->lock); 78882180Scg 78982180Scg return 0; 79082180Scg} 79182180Scg 79282180Scg/************************************************************************/ 79382180Scg 79482180Scg#ifdef SND_DYNSYSCTL 79582180Scgint 79682180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 79782180Scg{ 79882180Scg struct snddev_info *d; 79982180Scg struct snddev_channel *sce; 80082180Scg struct pcm_channel *c; 80182180Scg int err, oldcnt, newcnt, cnt; 80282180Scg 80382180Scg d = oidp->oid_arg1; 80482180Scg 80582180Scg pcm_lock(d); 80682180Scg cnt = 0; 80782180Scg SLIST_FOREACH(sce, &d->channels, link) { 80882180Scg c = sce->channel; 80982180Scg if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) 81082180Scg cnt++; 81182180Scg } 81282180Scg oldcnt = cnt; 81382180Scg newcnt = cnt; 81482180Scg 81582180Scg err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 81682180Scg if (err == 0 && req->newptr != NULL) { 81782180Scg if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 81882180Scg pcm_unlock(d); 81982180Scg return EINVAL; 82082180Scg } 82182180Scg 82282180Scg if (newcnt > cnt) { 82382180Scg /* add new vchans - find a parent channel first */ 82482180Scg SLIST_FOREACH(sce, &d->channels, link) { 82582180Scg c = sce->channel; 82682180Scg /* not a candidate if not a play channel */ 82782180Scg if (c->direction != PCMDIR_PLAY) 82896928Speter continue; 82982180Scg /* not a candidate if a virtual channel */ 83082180Scg if (c->flags & CHN_F_VIRTUAL) 83196928Speter continue; 83282180Scg /* not a candidate if it's in use */ 83382180Scg if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 83496928Speter continue; 83582180Scg /* 83682180Scg * if we get here we're a nonvirtual play channel, and either 83782180Scg * 1) not busy 83882180Scg * 2) busy with children, not directly open 83982180Scg * 84082180Scg * thus we can add children 84182180Scg */ 84282180Scg goto addok; 84382180Scg } 84482180Scg pcm_unlock(d); 84582180Scg return EBUSY; 84682180Scgaddok: 84782180Scg c->flags |= CHN_F_BUSY; 84882180Scg while (err == 0 && newcnt > cnt) { 84982180Scg err = vchan_create(c); 85082180Scg if (err == 0) 85182180Scg cnt++; 85282180Scg } 85382180Scg if (SLIST_EMPTY(&c->children)) 85482180Scg c->flags &= ~CHN_F_BUSY; 85582180Scg } else if (newcnt < cnt) { 85682180Scg while (err == 0 && newcnt < cnt) { 85782180Scg SLIST_FOREACH(sce, &d->channels, link) { 85882180Scg c = sce->channel; 85982180Scg if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 86082180Scg goto remok; 86182180Scg } 86282180Scg pcm_unlock(d); 86382180Scg return EINVAL; 86482180Scgremok: 86582180Scg err = vchan_destroy(c); 86682180Scg if (err == 0) 86782180Scg cnt--; 86882180Scg } 86982180Scg } 87082180Scg } 87182180Scg 87282180Scg pcm_unlock(d); 87382180Scg return err; 87482180Scg} 87582180Scg#endif 87682180Scg 87782180Scg/************************************************************************/ 87882180Scg 87965340Scgstatic moduledata_t sndpcm_mod = { 88065340Scg "snd_pcm", 88178362Scg NULL, 88265340Scg NULL 88365340Scg}; 88465340ScgDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 88565340ScgMODULE_VERSION(snd_pcm, PCM_MODVER); 886