sound.c revision 160439
1139749Simp/*- 2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 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> 30124740Smatk#include <dev/sound/pcm/dsp.h> 3165207Scg#include <sys/sysctl.h> 3250724Scg 3382180Scg#include "feeder_if.h" 3482180Scg 3582180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 160439 2006-07-17 17:43:06Z netchild $"); 3682180Scg 3778362Scgdevclass_t pcm_devclass; 3850724Scg 3989834Scgint pcm_veto_load = 1; 4089834Scg 4173127Scg#ifdef USING_DEVFS 4278362Scgint snd_unit = 0; 43159732SnetchildTUNABLE_INT("hw.snd.default_unit", &snd_unit); 4473127Scg#endif 4579141Scg 46159732Snetchildint snd_maxautovchans = 4; 47159732Snetchild/* XXX: a tunable implies that we may need more than one sound channel before 48159732Snetchild the system can change a sysctl (/etc/sysctl.conf), do we really need 49159732Snetchild this? */ 5082180ScgTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 5150724Scg 5273127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 5373127Scg 5482180Scgstatic int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 5582180Scg 5682180Scgstruct sysctl_ctx_list * 5782180Scgsnd_sysctl_tree(device_t dev) 5882180Scg{ 59157331Sariff struct snddev_info *d = device_get_softc(dev); 6082180Scg 6182180Scg return &d->sysctl_tree; 6282180Scg} 6382180Scg 6482180Scgstruct sysctl_oid * 6582180Scgsnd_sysctl_tree_top(device_t dev) 6682180Scg{ 67157331Sariff struct snddev_info *d = device_get_softc(dev); 6882180Scg 6982180Scg return d->sysctl_tree_top; 7082180Scg} 7182180Scg 7273131Scgvoid * 7393814Sjhbsnd_mtxcreate(const char *desc, const char *type) 7473131Scg{ 7573131Scg#ifdef USING_MUTEX 7673131Scg struct mtx *m; 7773131Scg 78111119Simp m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 7973131Scg if (m == NULL) 8073131Scg return NULL; 81125136Struckman mtx_init(m, desc, type, MTX_DEF); 8273131Scg return m; 8373131Scg#else 8473757Scg return (void *)0xcafebabe; 8573131Scg#endif 8673131Scg} 8773131Scg 8873131Scgvoid 8973131Scgsnd_mtxfree(void *m) 9073131Scg{ 9173131Scg#ifdef USING_MUTEX 9273131Scg struct mtx *mtx = m; 9373131Scg 94107237Scg /* mtx_assert(mtx, MA_OWNED); */ 9573131Scg mtx_destroy(mtx); 9673131Scg free(mtx, M_DEVBUF); 9773131Scg#endif 9873131Scg} 9973131Scg 10073131Scgvoid 10173131Scgsnd_mtxassert(void *m) 10273131Scg{ 10373131Scg#ifdef USING_MUTEX 10478670Scg#ifdef INVARIANTS 10573131Scg struct mtx *mtx = m; 10673131Scg 10773131Scg mtx_assert(mtx, MA_OWNED); 10873131Scg#endif 10978670Scg#endif 11073131Scg} 111107237Scg/* 11273131Scgvoid 11373131Scgsnd_mtxlock(void *m) 11473131Scg{ 11573131Scg#ifdef USING_MUTEX 11673131Scg struct mtx *mtx = m; 11773131Scg 11873131Scg mtx_lock(mtx); 11973131Scg#endif 12073131Scg} 12173131Scg 12273131Scgvoid 12373131Scgsnd_mtxunlock(void *m) 12473131Scg{ 12573131Scg#ifdef USING_MUTEX 12673131Scg struct mtx *mtx = m; 12773131Scg 12873131Scg mtx_unlock(mtx); 12973131Scg#endif 13073131Scg} 131107237Scg*/ 13273131Scgint 13373131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 13473131Scg{ 13573131Scg#ifdef USING_MUTEX 13673131Scg flags &= INTR_MPSAFE; 13778366Speter flags |= INTR_TYPE_AV; 13873131Scg#else 13978366Speter flags = INTR_TYPE_AV; 14073131Scg#endif 14173131Scg return bus_setup_intr(dev, res, flags, hand, param, cookiep); 14273131Scg} 14373131Scg 144119096Scg#ifndef PCM_DEBUG_MTX 14582180Scgvoid 14682180Scgpcm_lock(struct snddev_info *d) 14782180Scg{ 14882180Scg snd_mtxlock(d->lock); 14982180Scg} 15082180Scg 15182180Scgvoid 15282180Scgpcm_unlock(struct snddev_info *d) 15382180Scg{ 15482180Scg snd_mtxunlock(d->lock); 15582180Scg} 156119096Scg#endif 15782180Scg 15882180Scgstruct pcm_channel * 15982180Scgpcm_getfakechan(struct snddev_info *d) 16082180Scg{ 16182180Scg return d->fakechan; 16282180Scg} 16382180Scg 164157331Sariffstatic int 165157331Sariffpcm_setvchans(struct snddev_info *d, int newcnt) 166157331Sariff{ 167157331Sariff struct snddev_channel *sce = NULL; 168157331Sariff struct pcm_channel *c = NULL; 169157331Sariff int err = 0, vcnt, dcnt, i; 170157331Sariff 171157331Sariff pcm_inprog(d, 1); 172157331Sariff 173157331Sariff if (!(d->flags & SD_F_AUTOVCHAN)) { 174157331Sariff err = EINVAL; 175157331Sariff goto setvchans_out; 176157331Sariff } 177157331Sariff 178157331Sariff vcnt = d->vchancount; 179157331Sariff dcnt = d->playcount + d->reccount; 180157331Sariff 181157331Sariff if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) { 182157331Sariff err = E2BIG; 183157331Sariff goto setvchans_out; 184157331Sariff } 185157331Sariff 186157331Sariff dcnt += vcnt; 187157331Sariff 188157331Sariff if (newcnt > vcnt) { 189157331Sariff /* add new vchans - find a parent channel first */ 190157331Sariff SLIST_FOREACH(sce, &d->channels, link) { 191157331Sariff c = sce->channel; 192157331Sariff CHN_LOCK(c); 193157331Sariff if (c->direction == PCMDIR_PLAY && 194157331Sariff ((c->flags & CHN_F_HAS_VCHAN) || 195157331Sariff (vcnt == 0 && 196157331Sariff !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) 197157331Sariff goto addok; 198157331Sariff CHN_UNLOCK(c); 199157331Sariff } 200157331Sariff err = EBUSY; 201157331Sariff goto setvchans_out; 202157331Sariffaddok: 203157331Sariff c->flags |= CHN_F_BUSY; 204157331Sariff while (err == 0 && newcnt > vcnt) { 205157331Sariff if (dcnt > PCMMAXCHAN) { 206157331Sariff device_printf(d->dev, "%s: Maximum channel reached.\n", __func__); 207157331Sariff break; 208157331Sariff } 209157331Sariff err = vchan_create(c); 210157331Sariff if (err == 0) { 211157331Sariff vcnt++; 212157331Sariff dcnt++; 213157331Sariff } else if (err == E2BIG && newcnt > vcnt) 214157331Sariff device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err); 215157331Sariff } 216157331Sariff if (vcnt == 0) 217157331Sariff c->flags &= ~CHN_F_BUSY; 218157331Sariff CHN_UNLOCK(c); 219157331Sariff } else if (newcnt < vcnt) { 220157331Sariff#define ORPHAN_CDEVT(cdevt) \ 221157331Sariff ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \ 222157331Sariff (cdevt)->si_drv2 == NULL)) 223157331Sariff while (err == 0 && newcnt < vcnt) { 224157331Sariff i = 0; 225157331Sariff SLIST_FOREACH(sce, &d->channels, link) { 226157331Sariff c = sce->channel; 227157331Sariff CHN_LOCK(c); 228157331Sariff if (c->direction == PCMDIR_PLAY && 229157331Sariff (c->flags & CHN_F_VIRTUAL) && 230157331Sariff (i++ == newcnt)) { 231157331Sariff if (!(c->flags & CHN_F_BUSY) && 232157331Sariff ORPHAN_CDEVT(sce->dsp_devt) && 233157331Sariff ORPHAN_CDEVT(sce->dspW_devt) && 234157331Sariff ORPHAN_CDEVT(sce->audio_devt) && 235157331Sariff ORPHAN_CDEVT(sce->dspr_devt)) 236157331Sariff goto remok; 237157331Sariff /* 238157331Sariff * Either we're busy, or our cdev 239157331Sariff * has been stolen by dsp_clone(). 240157331Sariff * Skip, and increase newcnt. 241157331Sariff */ 242157331Sariff if (!(c->flags & CHN_F_BUSY)) 243157331Sariff device_printf(d->dev, 244157331Sariff "%s: <%s> somebody steal my cdev!\n", 245157331Sariff __func__, c->name); 246157331Sariff newcnt++; 247157331Sariff } 248157331Sariff CHN_UNLOCK(c); 249157331Sariff } 250157331Sariff if (vcnt != newcnt) 251157331Sariff err = EBUSY; 252157331Sariff break; 253157331Sariffremok: 254157331Sariff CHN_UNLOCK(c); 255157331Sariff err = vchan_destroy(c); 256157331Sariff if (err == 0) 257157331Sariff vcnt--; 258157331Sariff else 259157331Sariff device_printf(d->dev, 260157331Sariff "%s: WARNING: vchan_destroy() failed!", 261157331Sariff __func__); 262157331Sariff } 263157331Sariff } 264157331Sariff 265157331Sariffsetvchans_out: 266157331Sariff pcm_inprog(d, -1); 267157331Sariff return err; 268157331Sariff} 269157331Sariff 270156929Sariff/* return error status and a locked channel */ 271156929Sariffint 272156929Sariffpcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 273156929Sariff pid_t pid, int chnum) 27477269Scg{ 27577269Scg struct pcm_channel *c; 276157331Sariff struct snddev_channel *sce; 277157331Sariff int err; 27877269Scg 279156929Sariffretry_chnalloc: 280157331Sariff err = ENODEV; 28178895Scg /* scan for a free channel */ 28277269Scg SLIST_FOREACH(sce, &d->channels, link) { 28377269Scg c = sce->channel; 28477882Scg CHN_LOCK(c); 285157331Sariff if (c->direction == direction && !(c->flags & CHN_F_BUSY)) { 286156929Sariff if (chnum < 0 || sce->chan_num == chnum) { 28783089Scg c->flags |= CHN_F_BUSY; 28883089Scg c->pid = pid; 289156929Sariff *ch = c; 290156929Sariff return 0; 29183089Scg } 29277269Scg } 293156929Sariff if (sce->chan_num == chnum) { 294156929Sariff if (c->direction != direction) 295156929Sariff err = EOPNOTSUPP; 296156929Sariff else if (c->flags & CHN_F_BUSY) 297156929Sariff err = EBUSY; 298156929Sariff else 299156929Sariff err = EINVAL; 300156929Sariff CHN_UNLOCK(c); 301156929Sariff return err; 302156929Sariff } else if (c->direction == direction && (c->flags & CHN_F_BUSY)) 303157331Sariff err = EBUSY; 30477882Scg CHN_UNLOCK(c); 30577269Scg } 30678895Scg 30778895Scg /* no channel available */ 308156929Sariff if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 && 309156762Sariff d->vchancount < snd_maxautovchans && 310156762Sariff d->devcount <= PCMMAXCHAN) { 311157331Sariff err = pcm_setvchans(d, d->vchancount + 1); 312157331Sariff if (err == 0) { 313157331Sariff chnum = -2; 314157331Sariff goto retry_chnalloc; 31578895Scg } 31678895Scg } 31778895Scg 318157331Sariff return err; 31977269Scg} 32077269Scg 32178214Scg/* release a locked channel and unlock it */ 32277269Scgint 32378214Scgpcm_chnrelease(struct pcm_channel *c) 32477269Scg{ 32578214Scg CHN_LOCKASSERT(c); 32677269Scg c->flags &= ~CHN_F_BUSY; 32778214Scg c->pid = -1; 32877882Scg CHN_UNLOCK(c); 32977269Scg return 0; 33077269Scg} 33177269Scg 33277269Scgint 33377269Scgpcm_chnref(struct pcm_channel *c, int ref) 33477269Scg{ 33577882Scg int r; 33677882Scg 33778214Scg CHN_LOCKASSERT(c); 33877269Scg c->refcount += ref; 33977882Scg r = c->refcount; 34077882Scg return r; 34177269Scg} 34277269Scg 34382180Scgint 34482180Scgpcm_inprog(struct snddev_info *d, int delta) 34582180Scg{ 346119096Scg int r; 347119096Scg 348119096Scg if (delta == 0) 349119096Scg return d->inprog; 350119096Scg 351119096Scg /* backtrace(); */ 352119096Scg pcm_lock(d); 35382180Scg d->inprog += delta; 354119096Scg r = d->inprog; 355119096Scg pcm_unlock(d); 356119096Scg return r; 35782180Scg} 35882180Scg 35982180Scgstatic void 36082180Scgpcm_setmaxautovchans(struct snddev_info *d, int num) 36182180Scg{ 362157331Sariff if (num > 0 && d->vchancount == 0) 363157331Sariff pcm_setvchans(d, 1); 364157331Sariff else if (num == 0 && d->vchancount > 0) 365157331Sariff pcm_setvchans(d, 0); 36682180Scg} 36782180Scg 36873127Scg#ifdef USING_DEVFS 36965340Scgstatic int 370159732Snetchildsysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 37165340Scg{ 37278214Scg struct snddev_info *d; 37365340Scg int error, unit; 37465340Scg 37565340Scg unit = snd_unit; 37665340Scg error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 37765340Scg if (error == 0 && req->newptr != NULL) { 37878396Scg if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 37978214Scg return EINVAL; 38078214Scg d = devclass_get_softc(pcm_devclass, unit); 38178395Scg if (d == NULL || SLIST_EMPTY(&d->channels)) 38278214Scg return EINVAL; 38365340Scg snd_unit = unit; 38465340Scg } 38565340Scg return (error); 38665340Scg} 387159732Snetchild/* XXX: do we need a way to let the user change the default unit? */ 388159732SnetchildSYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW, 389159732Snetchild 0, sizeof(int), sysctl_hw_snd_default_unit, "I", ""); 39073127Scg#endif 39165340Scg 39278853Scgstatic int 39382180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 39478853Scg{ 39582180Scg struct snddev_info *d; 39682180Scg int i, v, error; 39778853Scg 39882180Scg v = snd_maxautovchans; 39978853Scg error = sysctl_handle_int(oidp, &v, sizeof(v), req); 40078853Scg if (error == 0 && req->newptr != NULL) { 401157331Sariff if (v < 0 || v > PCMMAXCHAN) 402157331Sariff return E2BIG; 403156929Sariff if (pcm_devclass != NULL && v != snd_maxautovchans) { 40482180Scg for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 40582180Scg d = devclass_get_softc(pcm_devclass, i); 40682180Scg if (!d) 40782180Scg continue; 408157331Sariff pcm_setmaxautovchans(d, v); 40982180Scg } 41082180Scg } 41182180Scg snd_maxautovchans = v; 41278853Scg } 41378853Scg return (error); 41478853Scg} 41582180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 41682180Scg 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 41778853Scg 41877269Scgstruct pcm_channel * 41977269Scgpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 42050724Scg{ 421156929Sariff struct snddev_channel *sce; 422157331Sariff struct pcm_channel *ch, *c; 42365340Scg char *dirs; 424157331Sariff uint32_t flsearch = 0; 425156929Sariff int direction, err, rpnum, *pnum; 42650724Scg 42777269Scg switch(dir) { 42877269Scg case PCMDIR_PLAY: 42977269Scg dirs = "play"; 430126367Struckman direction = PCMDIR_PLAY; 43183614Scg pnum = &d->playcount; 43277269Scg break; 43383614Scg 43477269Scg case PCMDIR_REC: 43577269Scg dirs = "record"; 436126367Struckman direction = PCMDIR_REC; 43783614Scg pnum = &d->reccount; 43877269Scg break; 43983614Scg 44077269Scg case PCMDIR_VIRTUAL: 44177269Scg dirs = "virtual"; 442126367Struckman direction = PCMDIR_PLAY; 44383614Scg pnum = &d->vchancount; 444156929Sariff flsearch = CHN_F_VIRTUAL; 44577269Scg break; 44683614Scg 44777269Scg default: 44877269Scg return NULL; 44977269Scg } 45065340Scg 451111119Simp ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 45277269Scg if (!ch) 45377269Scg return NULL; 45477269Scg 455111119Simp ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 45677269Scg if (!ch->methods) { 45777269Scg free(ch, M_DEVBUF); 45883614Scg 45977269Scg return NULL; 46061344Scg } 46177269Scg 462107237Scg snd_mtxlock(d->lock); 463156929Sariff ch->num = 0; 464156929Sariff rpnum = 0; 465156929Sariff SLIST_FOREACH(sce, &d->channels, link) { 466157331Sariff c = sce->channel; 467157331Sariff if (direction != c->direction || 468157331Sariff (c->flags & CHN_F_VIRTUAL) != flsearch) 469156929Sariff continue; 470157331Sariff if (ch->num == c->num) 471156929Sariff ch->num++; 472156929Sariff else { 473157331Sariff#if 0 474156929Sariff device_printf(d->dev, 475157331Sariff "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n", 476157331Sariff __func__, dirs, ch->num, c->num); 477157331Sariff#endif 478156929Sariff goto retry_num_search; 479156929Sariff } 480156929Sariff rpnum++; 481156929Sariff } 482156929Sariff goto retry_num_search_out; 483156929Sariffretry_num_search: 484156929Sariff rpnum = 0; 485156929Sariff SLIST_FOREACH(sce, &d->channels, link) { 486157331Sariff c = sce->channel; 487157331Sariff if (direction != c->direction || 488157331Sariff (c->flags & CHN_F_VIRTUAL) != flsearch) 489156929Sariff continue; 490157331Sariff if (ch->num == c->num) { 491156929Sariff ch->num++; 492156929Sariff goto retry_num_search; 493156929Sariff } 494156929Sariff rpnum++; 495156929Sariff } 496156929Sariffretry_num_search_out: 497156929Sariff if (*pnum != rpnum) { 498156929Sariff device_printf(d->dev, 499157331Sariff "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n", 500156929Sariff __func__, dirs, *pnum, rpnum); 501156929Sariff *pnum = rpnum; 502156929Sariff } 503156929Sariff (*pnum)++; 504107237Scg snd_mtxunlock(d->lock); 50583614Scg 50677269Scg ch->pid = -1; 50777269Scg ch->parentsnddev = d; 50877269Scg ch->parentchannel = parent; 50989774Sscottl ch->dev = d->dev; 510156929Sariff snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); 51177269Scg 512126367Struckman err = chn_init(ch, devinfo, dir, direction); 51370134Scg if (err) { 51483614Scg device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 51577269Scg kobj_delete(ch->methods, M_DEVBUF); 51677269Scg free(ch, M_DEVBUF); 517107237Scg snd_mtxlock(d->lock); 51883614Scg (*pnum)--; 519107237Scg snd_mtxunlock(d->lock); 52083614Scg 52177269Scg return NULL; 52255483Scg } 52377269Scg 52477269Scg return ch; 52577269Scg} 52677269Scg 52777269Scgint 52877269Scgpcm_chn_destroy(struct pcm_channel *ch) 52977269Scg{ 53083614Scg struct snddev_info *d; 53177269Scg int err; 53277269Scg 53383614Scg d = ch->parentsnddev; 53477269Scg err = chn_kill(ch); 53577269Scg if (err) { 53683614Scg device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 53777269Scg return err; 53877269Scg } 53977269Scg 54077269Scg kobj_delete(ch->methods, M_DEVBUF); 54177269Scg free(ch, M_DEVBUF); 54277269Scg 54377269Scg return 0; 54477269Scg} 54577269Scg 54677269Scgint 547124740Smatkpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 54877269Scg{ 549157331Sariff struct snddev_channel *sce, *tmp, *after; 550156929Sariff unsigned rdevcount; 551157331Sariff int device = device_get_unit(d->dev); 552156929Sariff size_t namelen; 55377269Scg 554124740Smatk /* 555124740Smatk * Note it's confusing nomenclature. 556124740Smatk * dev_t 557124740Smatk * device -> pcm_device 558157331Sariff * unit -> pcm_channel 559124740Smatk * channel -> snddev_channel 560124740Smatk * device_t 561124740Smatk * unit -> pcm_device 562124740Smatk */ 563124740Smatk 564111119Simp sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 56577269Scg if (!sce) { 56677269Scg return ENOMEM; 56777269Scg } 56877269Scg 569100478Sorion snd_mtxlock(d->lock); 57077269Scg sce->channel = ch; 571156929Sariff sce->chan_num = 0; 572157331Sariff rdevcount = 0; 573156929Sariff after = NULL; 574157331Sariff SLIST_FOREACH(tmp, &d->channels, link) { 575157331Sariff if (sce->chan_num == tmp->chan_num) 576157331Sariff sce->chan_num++; 577157331Sariff else { 578157331Sariff#if 0 579157331Sariff device_printf(d->dev, 580157331Sariff "%s: cdev numbering screwed (Expect: %d, Got: %d)\n", 581157331Sariff __func__, sce->chan_num, tmp->chan_num); 582157331Sariff#endif 583157331Sariff goto retry_chan_num_search; 584157331Sariff } 585157331Sariff after = tmp; 586157331Sariff rdevcount++; 587157331Sariff } 588157331Sariff goto retry_chan_num_search_out; 589156929Sariffretry_chan_num_search: 590156929Sariff /* 591156929Sariff * Look for possible channel numbering collision. This may not 592156929Sariff * be optimized, but it will ensure that no collision occured. 593157331Sariff * Can be considered cheap since none of the locking/unlocking 594157331Sariff * operations involved. 595156929Sariff */ 596156929Sariff rdevcount = 0; 597157331Sariff after = NULL; 598156929Sariff SLIST_FOREACH(tmp, &d->channels, link) { 599156929Sariff if (sce->chan_num == tmp->chan_num) { 600156929Sariff sce->chan_num++; 601156929Sariff goto retry_chan_num_search; 602156762Sariff } 603157331Sariff if (sce->chan_num > tmp->chan_num) 604157331Sariff after = tmp; 605156929Sariff rdevcount++; 60682180Scg } 607157331Sariffretry_chan_num_search_out: 608156929Sariff /* 609156929Sariff * Don't overflow PCMMKMINOR / PCMMAXCHAN. 610156929Sariff */ 611156929Sariff if (sce->chan_num > PCMMAXCHAN) { 612156929Sariff snd_mtxunlock(d->lock); 613156929Sariff device_printf(d->dev, 614156929Sariff "%s: WARNING: sce->chan_num overflow! (%d)\n", 615156929Sariff __func__, sce->chan_num); 616156929Sariff free(sce, M_DEVBUF); 617156929Sariff return E2BIG; 618156929Sariff } 619156929Sariff if (d->devcount != rdevcount) { 620156929Sariff device_printf(d->dev, 621156929Sariff "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n", 622156929Sariff __func__, d->devcount, rdevcount); 623156929Sariff d->devcount = rdevcount; 624156929Sariff } 625156762Sariff d->devcount++; 626156929Sariff if (after == NULL) { 627156929Sariff SLIST_INSERT_HEAD(&d->channels, sce, link); 628156929Sariff } else { 629156929Sariff SLIST_INSERT_AFTER(after, sce, link); 630156929Sariff } 631157331Sariff#if 0 632157331Sariff if (1) { 633157331Sariff int cnum = 0; 634157331Sariff SLIST_FOREACH(tmp, &d->channels, link) { 635157331Sariff if (cnum != tmp->chan_num) 636157331Sariff device_printf(d->dev, 637157331Sariff "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n", 638157331Sariff __func__, cnum, tmp->chan_num); 639157331Sariff cnum++; 640157331Sariff } 641157331Sariff } 642157331Sariff#endif 643156929Sariff 644156929Sariff namelen = strlen(ch->name); 645156929Sariff if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */ 646156929Sariff snprintf(ch->name + namelen, 647156929Sariff CHN_NAMELEN - namelen, ":dsp%d.%d", 648156929Sariff device, sce->chan_num); 649156929Sariff } 650107237Scg snd_mtxunlock(d->lock); 651157331Sariff 652157331Sariff /* 653157331Sariff * I will revisit these someday, and nuke it mercilessly.. 654157331Sariff */ 655156929Sariff sce->dsp_devt = make_dev(&dsp_cdevsw, 656124740Smatk PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), 657124740Smatk UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", 658124740Smatk device, sce->chan_num); 65977269Scg 660156929Sariff sce->dspW_devt = make_dev(&dsp_cdevsw, 661124740Smatk PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), 662124740Smatk UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", 663124740Smatk device, sce->chan_num); 66477269Scg 665156929Sariff sce->audio_devt = make_dev(&dsp_cdevsw, 666124740Smatk PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num), 667124740Smatk UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", 668124740Smatk device, sce->chan_num); 669124740Smatk 670124740Smatk if (ch->direction == PCMDIR_REC) 671124740Smatk sce->dspr_devt = make_dev(&dsp_cdevsw, 672124740Smatk PCMMKMINOR(device, SND_DEV_DSPREC, 673124740Smatk sce->chan_num), UID_ROOT, GID_WHEEL, 674124740Smatk 0666, "dspr%d.%d", device, sce->chan_num); 675124740Smatk 67650724Scg return 0; 67750724Scg} 67850724Scg 67977269Scgint 680124740Smatkpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 68165340Scg{ 682157331Sariff struct snddev_channel *sce; 683124617Sphk#if 0 684119096Scg int ourlock; 68565340Scg 686119096Scg ourlock = 0; 687119096Scg if (!mtx_owned(d->lock)) { 688119096Scg snd_mtxlock(d->lock); 689119096Scg ourlock = 1; 690119096Scg } 691124617Sphk#endif 692119096Scg 69377269Scg SLIST_FOREACH(sce, &d->channels, link) { 69477269Scg if (sce->channel == ch) 69577269Scg goto gotit; 69665340Scg } 697124617Sphk#if 0 698119096Scg if (ourlock) 699119096Scg snd_mtxunlock(d->lock); 700124617Sphk#endif 70177269Scg return EINVAL; 70277269Scggotit: 70377269Scg SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 704107237Scg 705149953Snetchild if (ch->flags & CHN_F_VIRTUAL) 706149953Snetchild d->vchancount--; 707149953Snetchild else if (ch->direction == PCMDIR_REC) 708107237Scg d->reccount--; 709107237Scg else 710107237Scg d->playcount--; 711107237Scg 712124617Sphk#if 0 713119096Scg if (ourlock) 714119096Scg snd_mtxunlock(d->lock); 715124617Sphk#endif 716107237Scg free(sce, M_DEVBUF); 71777269Scg 71865340Scg return 0; 71965340Scg} 72065340Scg 72150724Scgint 72277269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 72377269Scg{ 724157331Sariff struct snddev_info *d = device_get_softc(dev); 72582180Scg struct pcm_channel *ch; 726157331Sariff int err; 72777269Scg 72877269Scg ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 72977269Scg if (!ch) { 73077269Scg device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 73177269Scg return ENODEV; 73277269Scg } 73378853Scg 734124740Smatk err = pcm_chn_add(d, ch); 73577269Scg if (err) { 73677269Scg device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 73777269Scg pcm_chn_destroy(ch); 73878853Scg return err; 73977269Scg } 74077269Scg 74177269Scg return err; 74277269Scg} 74377269Scg 74477269Scgstatic int 74577269Scgpcm_killchan(device_t dev) 74677269Scg{ 747157331Sariff struct snddev_info *d = device_get_softc(dev); 748157331Sariff struct snddev_channel *sce; 749106420Scognet struct pcm_channel *ch; 750106420Scognet int error = 0; 75177269Scg 75277269Scg sce = SLIST_FIRST(&d->channels); 753106420Scognet ch = sce->channel; 75477269Scg 755124740Smatk error = pcm_chn_remove(d, sce->channel); 756106420Scognet if (error) 757106420Scognet return (error); 758106420Scognet return (pcm_chn_destroy(ch)); 75977269Scg} 76077269Scg 76177269Scgint 76250724Scgpcm_setstatus(device_t dev, char *str) 76350724Scg{ 764157331Sariff struct snddev_info *d = device_get_softc(dev); 76577882Scg 76677882Scg snd_mtxlock(d->lock); 76750724Scg strncpy(d->status, str, SND_STATUSLEN); 76877882Scg snd_mtxunlock(d->lock); 769157331Sariff if (snd_maxautovchans > 0) 770157331Sariff pcm_setvchans(d, 1); 77150724Scg return 0; 77250724Scg} 77350724Scg 774157331Sariffuint32_t 77550724Scgpcm_getflags(device_t dev) 77650724Scg{ 777157331Sariff struct snddev_info *d = device_get_softc(dev); 77877882Scg 77950724Scg return d->flags; 78050724Scg} 78150724Scg 78250724Scgvoid 783157331Sariffpcm_setflags(device_t dev, uint32_t val) 78450724Scg{ 785157331Sariff struct snddev_info *d = device_get_softc(dev); 78678362Scg 78750724Scg d->flags = val; 78850724Scg} 78950724Scg 79058384Scgvoid * 79158384Scgpcm_getdevinfo(device_t dev) 79258384Scg{ 793157331Sariff struct snddev_info *d = device_get_softc(dev); 79477882Scg 79558384Scg return d->devinfo; 79658384Scg} 79758384Scg 79883614Scgunsigned int 799160439Snetchildpcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 80083614Scg{ 801157331Sariff struct snddev_info *d = device_get_softc(dev); 80289690Scg int sz, x; 80383614Scg 80483614Scg sz = 0; 80589690Scg if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 80689690Scg x = sz; 807160439Snetchild RANGE(sz, minbufsz, maxbufsz); 80889690Scg if (x != sz) 809160439Snetchild device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 810160439Snetchild x = minbufsz; 81189690Scg while (x < sz) 81289690Scg x <<= 1; 81389690Scg if (x > sz) 81489690Scg x >>= 1; 81589690Scg if (x != sz) { 81689690Scg device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 81789690Scg sz = x; 81889690Scg } 81989690Scg } else { 82083614Scg sz = deflt; 82189690Scg } 82289690Scg 82383614Scg d->bufsz = sz; 82483614Scg 82583614Scg return sz; 82683614Scg} 82783614Scg 82850724Scgint 82950724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec) 83050724Scg{ 831157331Sariff struct snddev_info *d = device_get_softc(dev); 83250724Scg 83389834Scg if (pcm_veto_load) { 83489834Scg device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 83589834Scg 83689834Scg return EINVAL; 83789834Scg } 83889834Scg 83993814Sjhb d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 84077269Scg 841148587Snetchild#if 0 842148587Snetchild /* 843148587Snetchild * d->flags should be cleared by the allocator of the softc. 844148587Snetchild * We cannot clear this field here because several devices set 845148587Snetchild * this flag before calling pcm_register(). 846148587Snetchild */ 84778853Scg d->flags = 0; 848148587Snetchild#endif 84961886Scg d->dev = dev; 85050724Scg d->devinfo = devinfo; 85178895Scg d->devcount = 0; 85283089Scg d->reccount = 0; 85383614Scg d->playcount = 0; 85478895Scg d->vchancount = 0; 85578362Scg d->inprog = 0; 85650724Scg 857124617Sphk SLIST_INIT(&d->channels); 858124617Sphk 859157331Sariff if ((numplay == 0 || numrec == 0) && numplay != numrec) 86078214Scg d->flags |= SD_F_SIMPLEX; 86178362Scg 86278214Scg d->fakechan = fkchan_setup(dev); 863126367Struckman chn_init(d->fakechan, NULL, 0, 0); 86455494Scg 86573127Scg#ifdef SND_DYNSYSCTL 86670680Sjhb sysctl_ctx_init(&d->sysctl_tree); 86770680Sjhb d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 86870943Sjhb SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 86970680Sjhb device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 87070943Sjhb if (d->sysctl_tree_top == NULL) { 87170680Sjhb sysctl_ctx_free(&d->sysctl_tree); 87270680Sjhb goto no; 87370680Sjhb } 874159732Snetchild /* XXX: an user should be able to set this with a control tool, the 875159732Snetchild sysadmin then needs min+max sysctls for this */ 87683614Scg SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 877159732Snetchild OID_AUTO, "_buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 87873127Scg#endif 879149953Snetchild if (numplay > 0) { 880156929Sariff d->flags |= SD_F_AUTOVCHAN; 88182180Scg vchan_initsys(dev); 882149953Snetchild } 88378853Scg 88482180Scg sndstat_register(dev, d->status, sndstat_prepare_pcm); 885157331Sariff return 0; 88650724Scgno: 88777882Scg snd_mtxfree(d->lock); 88850724Scg return ENXIO; 88950724Scg} 89050724Scg 89165340Scgint 89265340Scgpcm_unregister(device_t dev) 89365207Scg{ 894157331Sariff struct snddev_info *d = device_get_softc(dev); 895157331Sariff struct snddev_channel *sce; 896157331Sariff struct pcmchan_children *pce; 89783614Scg struct pcm_channel *ch; 89865207Scg 899150827Snetchild if (sndstat_acquire() != 0) { 900150827Snetchild device_printf(dev, "unregister: sndstat busy\n"); 901150827Snetchild return EBUSY; 902150827Snetchild } 903150827Snetchild 90477882Scg snd_mtxlock(d->lock); 90578362Scg if (d->inprog) { 90683476Sgreid device_printf(dev, "unregister: operation in progress\n"); 90778362Scg snd_mtxunlock(d->lock); 908150827Snetchild sndstat_release(); 90978362Scg return EBUSY; 91078362Scg } 911124740Smatk 91277269Scg SLIST_FOREACH(sce, &d->channels, link) { 91383614Scg ch = sce->channel; 91483614Scg if (ch->refcount > 0) { 91595684Scg device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 91677882Scg snd_mtxunlock(d->lock); 917150827Snetchild sndstat_release(); 91877269Scg return EBUSY; 91977269Scg } 92065487Scg } 921124740Smatk 922157022Sariff if (mixer_uninit(dev) == EBUSY) { 923148587Snetchild device_printf(dev, "unregister: mixer busy\n"); 924148587Snetchild snd_mtxunlock(d->lock); 925150827Snetchild sndstat_release(); 926148587Snetchild return EBUSY; 927148587Snetchild } 928148587Snetchild 929124740Smatk SLIST_FOREACH(sce, &d->channels, link) { 930156762Sariff if (sce->dsp_devt) { 931149953Snetchild destroy_dev(sce->dsp_devt); 932156762Sariff sce->dsp_devt = NULL; 933156762Sariff } 934156762Sariff if (sce->dspW_devt) { 935149953Snetchild destroy_dev(sce->dspW_devt); 936156762Sariff sce->dspW_devt = NULL; 937156762Sariff } 938156762Sariff if (sce->audio_devt) { 939149953Snetchild destroy_dev(sce->audio_devt); 940156762Sariff sce->audio_devt = NULL; 941156762Sariff } 942156762Sariff if (sce->dspr_devt) { 943124740Smatk destroy_dev(sce->dspr_devt); 944156762Sariff sce->dspr_devt = NULL; 945156762Sariff } 946156762Sariff d->devcount--; 947157331Sariff ch = sce->channel; 948157331Sariff if (ch == NULL) 949157331Sariff continue; 950157331Sariff pce = SLIST_FIRST(&ch->children); 951157331Sariff while (pce != NULL) { 952157331Sariff#if 0 953157331Sariff device_printf(d->dev, "<%s> removing <%s>\n", 954157331Sariff ch->name, (pce->channel != NULL) ? 955157331Sariff pce->channel->name : "unknown"); 956157331Sariff#endif 957157331Sariff SLIST_REMOVE(&ch->children, pce, pcmchan_children, link); 958157331Sariff free(pce, M_DEVBUF); 959157331Sariff pce = SLIST_FIRST(&ch->children); 960157331Sariff } 961124740Smatk } 962124740Smatk 96374763Scg#ifdef SND_DYNSYSCTL 96474763Scg d->sysctl_tree_top = NULL; 96574763Scg sysctl_ctx_free(&d->sysctl_tree); 96674763Scg#endif 967157331Sariff 968157331Sariff#if 0 969157331Sariff SLIST_FOREACH(sce, &d->channels, link) { 970157331Sariff ch = sce->channel; 971157331Sariff if (ch == NULL) 972157331Sariff continue; 973157331Sariff if (!SLIST_EMPTY(&ch->children)) 974157331Sariff device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n", 975157331Sariff __func__, ch->name); 976157331Sariff } 977157331Sariff#endif 97878395Scg while (!SLIST_EMPTY(&d->channels)) 97977269Scg pcm_killchan(dev); 98065207Scg 98174763Scg chn_kill(d->fakechan); 98274763Scg fkchan_kill(d->fakechan); 98373127Scg 984157331Sariff#if 0 985157331Sariff device_printf(d->dev, "%s: devcount=%u, playcount=%u, " 986157331Sariff "reccount=%u, vchancount=%u\n", 987157331Sariff __func__, d->devcount, d->playcount, d->reccount, 988157331Sariff d->vchancount); 989157331Sariff#endif 990124617Sphk snd_mtxunlock(d->lock); 99177882Scg snd_mtxfree(d->lock); 992150827Snetchild sndstat_unregister(dev); 993150827Snetchild sndstat_release(); 99465340Scg return 0; 99565207Scg} 99665207Scg 99782180Scg/************************************************************************/ 99882180Scg 99982180Scgstatic int 100082180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 100182180Scg{ 1002157331Sariff struct snddev_info *d; 1003157331Sariff struct snddev_channel *sce; 100482180Scg struct pcm_channel *c; 100582180Scg struct pcm_feeder *f; 1006157331Sariff int pc, rc, vc; 100782180Scg 100882180Scg if (verbose < 1) 100982180Scg return 0; 101082180Scg 101182180Scg d = device_get_softc(dev); 101282180Scg if (!d) 101382180Scg return ENXIO; 101482180Scg 101582180Scg snd_mtxlock(d->lock); 101682180Scg if (!SLIST_EMPTY(&d->channels)) { 101782180Scg pc = rc = vc = 0; 101882180Scg SLIST_FOREACH(sce, &d->channels, link) { 101982180Scg c = sce->channel; 102082180Scg if (c->direction == PCMDIR_PLAY) { 102182180Scg if (c->flags & CHN_F_VIRTUAL) 102282180Scg vc++; 102382180Scg else 102482180Scg pc++; 102582180Scg } else 102682180Scg rc++; 102782180Scg } 102883614Scg sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 102982180Scg (d->flags & SD_F_SIMPLEX)? "" : " duplex", 103082180Scg#ifdef USING_DEVFS 103182180Scg (device_get_unit(dev) == snd_unit)? " default" : "" 103282180Scg#else 103382180Scg "" 103482180Scg#endif 103582180Scg ); 1036119096Scg 1037119096Scg if (verbose <= 1) { 1038119096Scg snd_mtxunlock(d->lock); 1039119096Scg return 0; 1040119096Scg } 1041119096Scg 104282180Scg SLIST_FOREACH(sce, &d->channels, link) { 104382180Scg c = sce->channel; 1044155342Snetchild 1045155342Snetchild KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 1046155342Snetchild ("hosed pcm channel setup")); 1047155342Snetchild 104882479Scg sbuf_printf(s, "\n\t"); 104982479Scg 1050124617Sphk /* it would be better to indent child channels */ 105182479Scg sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 105289691Scg sbuf_printf(s, "spd %d", c->speed); 105389691Scg if (c->speed != sndbuf_getspd(c->bufhard)) 105489691Scg sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 105589691Scg sbuf_printf(s, ", fmt 0x%08x", c->format); 105689691Scg if (c->format != sndbuf_getfmt(c->bufhard)) 105789691Scg sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 1058119096Scg sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 105982180Scg if (c->pid != -1) 106082180Scg sbuf_printf(s, ", pid %d", c->pid); 106182180Scg sbuf_printf(s, "\n\t"); 106289691Scg 1063155342Snetchild sbuf_printf(s, "interrupts %d, ", c->interrupts); 1064155342Snetchild if (c->direction == PCMDIR_REC) 1065155342Snetchild sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 1066155342Snetchild c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), 1067155342Snetchild sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1068155342Snetchild sndbuf_getblkcnt(c->bufhard), 1069155342Snetchild sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1070155342Snetchild sndbuf_getblkcnt(c->bufsoft)); 1071155342Snetchild else 1072155342Snetchild sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", 1073155342Snetchild c->xruns, sndbuf_getready(c->bufsoft), 1074155342Snetchild sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1075155342Snetchild sndbuf_getblkcnt(c->bufhard), 1076155342Snetchild sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1077155342Snetchild sndbuf_getblkcnt(c->bufsoft)); 1078155342Snetchild sbuf_printf(s, "\n\t"); 107989691Scg 108089691Scg sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 108189691Scg sbuf_printf(s, " -> "); 108282180Scg f = c->feeder; 108389691Scg while (f->source != NULL) 108489691Scg f = f->source; 108589691Scg while (f != NULL) { 108682180Scg sbuf_printf(s, "%s", f->class->name); 108782180Scg if (f->desc->type == FEEDER_FMT) 108889691Scg sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 108982180Scg if (f->desc->type == FEEDER_RATE) 109089691Scg sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 1091150827Snetchild if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || 1092150827Snetchild f->desc->type == FEEDER_VOLUME) 109389691Scg sbuf_printf(s, "(0x%08x)", f->desc->out); 109489691Scg sbuf_printf(s, " -> "); 109589691Scg f = f->parent; 109682180Scg } 109789691Scg sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 109882180Scg } 109982180Scg } else 110082180Scg sbuf_printf(s, " (mixer only)"); 110182180Scg snd_mtxunlock(d->lock); 110282180Scg 110382180Scg return 0; 110482180Scg} 110582180Scg 110682180Scg/************************************************************************/ 110782180Scg 110882180Scg#ifdef SND_DYNSYSCTL 110982180Scgint 111082180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 111182180Scg{ 111282180Scg struct snddev_info *d; 1113157331Sariff int err, newcnt; 111482180Scg 111582180Scg d = oidp->oid_arg1; 111682180Scg 1117157331Sariff newcnt = d->vchancount; 111882180Scg err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 1119119096Scg 1120157331Sariff if (err == 0 && req->newptr != NULL && d->vchancount != newcnt) 1121157331Sariff err = pcm_setvchans(d, newcnt); 1122119096Scg 112382180Scg return err; 112482180Scg} 112582180Scg#endif 112682180Scg 112782180Scg/************************************************************************/ 112882180Scg 1129132236Stanimurastatic int 1130132236Stanimurasound_modevent(module_t mod, int type, void *data) 1131132236Stanimura{ 1132148587Snetchild#if 0 1133132236Stanimura return (midi_modevent(mod, type, data)); 1134148587Snetchild#else 1135148587Snetchild return 0; 1136148587Snetchild#endif 1137132236Stanimura} 1138132236Stanimura 1139132236StanimuraDEV_MODULE(sound, sound_modevent, NULL); 1140132236StanimuraMODULE_VERSION(sound, SOUND_MODVER); 1141