sound.c revision 156929
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 156929 2006-03-21 06:35:48Z ariff $"); 3682180Scg 3778362Scgdevclass_t pcm_devclass; 3850724Scg 3989834Scgint pcm_veto_load = 1; 4089834Scg 4173127Scg#ifdef USING_DEVFS 4278362Scgint snd_unit = 0; 4377900SpeterTUNABLE_INT("hw.snd.unit", &snd_unit); 4473127Scg#endif 4579141Scg 4682180Scgint snd_maxautovchans = 0; 4782180ScgTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 4850724Scg 4973127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 5073127Scg 5182180Scgstatic int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 5282180Scg 5382180Scgstruct sysctl_ctx_list * 5482180Scgsnd_sysctl_tree(device_t dev) 5582180Scg{ 5682180Scg struct snddev_info *d = device_get_softc(dev); 5782180Scg 5882180Scg return &d->sysctl_tree; 5982180Scg} 6082180Scg 6182180Scgstruct sysctl_oid * 6282180Scgsnd_sysctl_tree_top(device_t dev) 6382180Scg{ 6482180Scg struct snddev_info *d = device_get_softc(dev); 6582180Scg 6682180Scg return d->sysctl_tree_top; 6782180Scg} 6882180Scg 6973131Scgvoid * 7093814Sjhbsnd_mtxcreate(const char *desc, const char *type) 7173131Scg{ 7273131Scg#ifdef USING_MUTEX 7373131Scg struct mtx *m; 7473131Scg 75111119Simp m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 7673131Scg if (m == NULL) 7773131Scg return NULL; 78125136Struckman mtx_init(m, desc, type, MTX_DEF); 7973131Scg return m; 8073131Scg#else 8173757Scg return (void *)0xcafebabe; 8273131Scg#endif 8373131Scg} 8473131Scg 8573131Scgvoid 8673131Scgsnd_mtxfree(void *m) 8773131Scg{ 8873131Scg#ifdef USING_MUTEX 8973131Scg struct mtx *mtx = m; 9073131Scg 91107237Scg /* mtx_assert(mtx, MA_OWNED); */ 9273131Scg mtx_destroy(mtx); 9373131Scg free(mtx, M_DEVBUF); 9473131Scg#endif 9573131Scg} 9673131Scg 9773131Scgvoid 9873131Scgsnd_mtxassert(void *m) 9973131Scg{ 10073131Scg#ifdef USING_MUTEX 10178670Scg#ifdef INVARIANTS 10273131Scg struct mtx *mtx = m; 10373131Scg 10473131Scg mtx_assert(mtx, MA_OWNED); 10573131Scg#endif 10678670Scg#endif 10773131Scg} 108107237Scg/* 10973131Scgvoid 11073131Scgsnd_mtxlock(void *m) 11173131Scg{ 11273131Scg#ifdef USING_MUTEX 11373131Scg struct mtx *mtx = m; 11473131Scg 11573131Scg mtx_lock(mtx); 11673131Scg#endif 11773131Scg} 11873131Scg 11973131Scgvoid 12073131Scgsnd_mtxunlock(void *m) 12173131Scg{ 12273131Scg#ifdef USING_MUTEX 12373131Scg struct mtx *mtx = m; 12473131Scg 12573131Scg mtx_unlock(mtx); 12673131Scg#endif 12773131Scg} 128107237Scg*/ 12973131Scgint 13073131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 13173131Scg{ 13273131Scg#ifdef USING_MUTEX 13373131Scg flags &= INTR_MPSAFE; 13478366Speter flags |= INTR_TYPE_AV; 13573131Scg#else 13678366Speter flags = INTR_TYPE_AV; 13773131Scg#endif 13873131Scg return bus_setup_intr(dev, res, flags, hand, param, cookiep); 13973131Scg} 14073131Scg 141119096Scg#ifndef PCM_DEBUG_MTX 14282180Scgvoid 14382180Scgpcm_lock(struct snddev_info *d) 14482180Scg{ 14582180Scg snd_mtxlock(d->lock); 14682180Scg} 14782180Scg 14882180Scgvoid 14982180Scgpcm_unlock(struct snddev_info *d) 15082180Scg{ 15182180Scg snd_mtxunlock(d->lock); 15282180Scg} 153119096Scg#endif 15482180Scg 15582180Scgstruct pcm_channel * 15682180Scgpcm_getfakechan(struct snddev_info *d) 15782180Scg{ 15882180Scg return d->fakechan; 15982180Scg} 16082180Scg 161156929Sariff/* return error status and a locked channel */ 162156929Sariffint 163156929Sariffpcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 164156929Sariff pid_t pid, int chnum) 16577269Scg{ 16677269Scg struct pcm_channel *c; 16777269Scg struct snddev_channel *sce; 168156929Sariff int err, ret; 16977269Scg 170156929Sariffretry_chnalloc: 171156929Sariff ret = ENODEV; 17278895Scg /* scan for a free channel */ 17377269Scg SLIST_FOREACH(sce, &d->channels, link) { 17477269Scg c = sce->channel; 17577882Scg CHN_LOCK(c); 17677269Scg if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 177156929Sariff if (chnum < 0 || sce->chan_num == chnum) { 17883089Scg c->flags |= CHN_F_BUSY; 17983089Scg c->pid = pid; 180156929Sariff *ch = c; 181156929Sariff return 0; 18283089Scg } 18377269Scg } 184156929Sariff if (sce->chan_num == chnum) { 185156929Sariff if (c->direction != direction) 186156929Sariff err = EOPNOTSUPP; 187156929Sariff else if (c->flags & CHN_F_BUSY) 188156929Sariff err = EBUSY; 189156929Sariff else 190156929Sariff err = EINVAL; 191156929Sariff CHN_UNLOCK(c); 192156929Sariff return err; 193156929Sariff } else if (c->direction == direction && (c->flags & CHN_F_BUSY)) 194156929Sariff ret = EBUSY; 19577882Scg CHN_UNLOCK(c); 19677269Scg } 19778895Scg 19878895Scg /* no channel available */ 199156929Sariff if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 && 200156762Sariff d->vchancount < snd_maxautovchans && 201156762Sariff d->devcount <= PCMMAXCHAN) { 202156762Sariff /* try to create a vchan */ 203156762Sariff SLIST_FOREACH(sce, &d->channels, link) { 204156762Sariff c = sce->channel; 205156762Sariff CHN_LOCK(c); 206156762Sariff if ((c->flags & CHN_F_HAS_VCHAN) && 207156762Sariff !SLIST_EMPTY(&c->children)) { 208156762Sariff err = vchan_create(c); 209156762Sariff CHN_UNLOCK(c); 210156929Sariff if (!err) { 211156929Sariff chnum = -2; 212156929Sariff goto retry_chnalloc; 213156929Sariff } else 214156929Sariff device_printf(d->dev, "%s: vchan_create(%s) == %d\n", __func__, c->name, err); 215156762Sariff } else 216156762Sariff CHN_UNLOCK(c); 21778895Scg } 21878895Scg } 21978895Scg 220156929Sariff return ret; 22177269Scg} 22277269Scg 22378214Scg/* release a locked channel and unlock it */ 22477269Scgint 22578214Scgpcm_chnrelease(struct pcm_channel *c) 22677269Scg{ 22778214Scg CHN_LOCKASSERT(c); 22877269Scg c->flags &= ~CHN_F_BUSY; 22978214Scg c->pid = -1; 23077882Scg CHN_UNLOCK(c); 23177269Scg return 0; 23277269Scg} 23377269Scg 23477269Scgint 23577269Scgpcm_chnref(struct pcm_channel *c, int ref) 23677269Scg{ 23777882Scg int r; 23877882Scg 23978214Scg CHN_LOCKASSERT(c); 24077269Scg c->refcount += ref; 24177882Scg r = c->refcount; 24277882Scg return r; 24377269Scg} 24477269Scg 24582180Scgint 24682180Scgpcm_inprog(struct snddev_info *d, int delta) 24782180Scg{ 248119096Scg int r; 249119096Scg 250119096Scg if (delta == 0) 251119096Scg return d->inprog; 252119096Scg 253119096Scg /* backtrace(); */ 254119096Scg pcm_lock(d); 25582180Scg d->inprog += delta; 256119096Scg r = d->inprog; 257119096Scg pcm_unlock(d); 258119096Scg return r; 25982180Scg} 26082180Scg 26182180Scgstatic void 26282180Scgpcm_setmaxautovchans(struct snddev_info *d, int num) 26382180Scg{ 264149953Snetchild struct pcm_channel *c, *ch; 26582180Scg struct snddev_channel *sce; 26682180Scg int err, done; 26782180Scg 268149953Snetchild /* 269149953Snetchild * XXX WOAH... NEED SUPER CLEANUP!!! 270149953Snetchild * Robust, yet confusing. Understanding these will 271149953Snetchild * cause your brain spinning like a Doki Doki Dynamo. 272149953Snetchild */ 27382180Scg if (num > 0 && d->vchancount == 0) { 27482180Scg SLIST_FOREACH(sce, &d->channels, link) { 27582180Scg c = sce->channel; 276125136Struckman CHN_LOCK(c); 277149953Snetchild if ((c->direction == PCMDIR_PLAY) && 278156929Sariff (c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == 0 && 279149953Snetchild SLIST_EMPTY(&c->children)) { 28082180Scg c->flags |= CHN_F_BUSY; 28182180Scg err = vchan_create(c); 28282180Scg if (err) { 283125136Struckman c->flags &= ~CHN_F_BUSY; 284156929Sariff device_printf(d->dev, "%s: vchan_create(%s) == %d\n", __func__, c->name, err); 285156929Sariff } else { 286156929Sariff CHN_UNLOCK(c); 287156929Sariff return; 288149953Snetchild } 28982180Scg } 290125136Struckman CHN_UNLOCK(c); 29182180Scg } 292149953Snetchild return; 29382180Scg } 29482180Scg if (num == 0 && d->vchancount > 0) { 295149953Snetchild /* 296149953Snetchild * XXX Keep retrying... 297149953Snetchild */ 298149953Snetchild for (done = 0; done < 1024; done++) { 299149953Snetchild ch = NULL; 30082180Scg SLIST_FOREACH(sce, &d->channels, link) { 30182180Scg c = sce->channel; 302149953Snetchild CHN_LOCK(c); 303149953Snetchild if (c->direction == PCMDIR_PLAY && 304149953Snetchild !(c->flags & CHN_F_BUSY) && 305149953Snetchild (c->flags & CHN_F_VIRTUAL)) { 306149953Snetchild ch = c; 307149953Snetchild break; 30882180Scg } 309149953Snetchild CHN_UNLOCK(c); 31082180Scg } 311149953Snetchild if (ch != NULL) { 312149953Snetchild CHN_UNLOCK(ch); 313149953Snetchild snd_mtxlock(d->lock); 314149953Snetchild err = vchan_destroy(ch); 315149953Snetchild if (err) 316149953Snetchild device_printf(d->dev, "vchan_destroy(%s) == %d\n", 317149953Snetchild ch->name, err); 318149953Snetchild snd_mtxunlock(d->lock); 319149953Snetchild } else 320149953Snetchild return; 32182180Scg } 322149953Snetchild return; 32382180Scg } 32482180Scg} 32582180Scg 32673127Scg#ifdef USING_DEVFS 32765340Scgstatic int 32878853Scgsysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 32965340Scg{ 33078214Scg struct snddev_info *d; 33165340Scg int error, unit; 33265340Scg 33365340Scg unit = snd_unit; 33465340Scg error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 33565340Scg if (error == 0 && req->newptr != NULL) { 33678396Scg if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 33778214Scg return EINVAL; 33878214Scg d = devclass_get_softc(pcm_devclass, unit); 33978395Scg if (d == NULL || SLIST_EMPTY(&d->channels)) 34078214Scg return EINVAL; 34165340Scg snd_unit = unit; 34265340Scg } 34365340Scg return (error); 34465340Scg} 34570617SjhbSYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 34678853Scg 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 34773127Scg#endif 34865340Scg 34978853Scgstatic int 35082180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 35178853Scg{ 35282180Scg struct snddev_info *d; 35382180Scg int i, v, error; 35478853Scg 35582180Scg v = snd_maxautovchans; 35678853Scg error = sysctl_handle_int(oidp, &v, sizeof(v), req); 35778853Scg if (error == 0 && req->newptr != NULL) { 358156929Sariff if (v < 0 || v > (PCMMAXCHAN + 1)) 35978853Scg return EINVAL; 360156929Sariff if (pcm_devclass != NULL && v != snd_maxautovchans) { 36182180Scg for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 36282180Scg d = devclass_get_softc(pcm_devclass, i); 36382180Scg if (!d) 36482180Scg continue; 365149953Snetchild if (d->flags & SD_F_AUTOVCHAN) { 366149953Snetchild if (pcm_inprog(d, 1) == 1) 367149953Snetchild pcm_setmaxautovchans(d, v); 368149953Snetchild pcm_inprog(d, -1); 369149953Snetchild } 37082180Scg } 37182180Scg } 37282180Scg snd_maxautovchans = v; 37378853Scg } 37478853Scg return (error); 37578853Scg} 37682180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 37782180Scg 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 37878853Scg 37977269Scgstruct pcm_channel * 38077269Scgpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 38150724Scg{ 382156929Sariff struct snddev_channel *sce; 383156929Sariff struct pcm_channel *ch, *tmpch; 38465340Scg char *dirs; 385156929Sariff u_int32_t flsearch = 0; 386156929Sariff int direction, err, rpnum, *pnum; 38750724Scg 38877269Scg switch(dir) { 38977269Scg case PCMDIR_PLAY: 39077269Scg dirs = "play"; 391126367Struckman direction = PCMDIR_PLAY; 39283614Scg pnum = &d->playcount; 39377269Scg break; 39483614Scg 39577269Scg case PCMDIR_REC: 39677269Scg dirs = "record"; 397126367Struckman direction = PCMDIR_REC; 39883614Scg pnum = &d->reccount; 39977269Scg break; 40083614Scg 40177269Scg case PCMDIR_VIRTUAL: 40277269Scg dirs = "virtual"; 403126367Struckman direction = PCMDIR_PLAY; 40483614Scg pnum = &d->vchancount; 405156929Sariff flsearch = CHN_F_VIRTUAL; 40677269Scg break; 40783614Scg 40877269Scg default: 40977269Scg return NULL; 41077269Scg } 41165340Scg 412111119Simp ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 41377269Scg if (!ch) 41477269Scg return NULL; 41577269Scg 416111119Simp ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 41777269Scg if (!ch->methods) { 41877269Scg free(ch, M_DEVBUF); 41983614Scg 42077269Scg return NULL; 42161344Scg } 42277269Scg 423107237Scg snd_mtxlock(d->lock); 424156929Sariff ch->num = 0; 425156929Sariff rpnum = 0; 426156929Sariff SLIST_FOREACH(sce, &d->channels, link) { 427156929Sariff if (sce == NULL || sce->channel == NULL) 428156929Sariff continue; 429156929Sariff tmpch = sce->channel; 430156929Sariff if (direction != tmpch->direction || 431156929Sariff (tmpch->flags & CHN_F_VIRTUAL) != flsearch) 432156929Sariff continue; 433156929Sariff if (ch->num == tmpch->num) 434156929Sariff ch->num++; 435156929Sariff else { 436156929Sariff /* 437156929Sariff * Channel numbering screwed. Bail out, and do the 438156929Sariff * hard way lookup. 439156929Sariff */ 440156929Sariff device_printf(d->dev, 441156929Sariff "%s: channel numbering dirs=%s screwed.\n", 442156929Sariff __func__, dirs); 443156929Sariff ch->num = 0; 444156929Sariff goto retry_num_search; 445156929Sariff } 446156929Sariff rpnum++; 447156929Sariff } 448156929Sariff goto retry_num_search_out; 449156929Sariffretry_num_search: 450156929Sariff rpnum = 0; 451156929Sariff SLIST_FOREACH(sce, &d->channels, link) { 452156929Sariff if (sce == NULL || sce->channel == NULL) 453156929Sariff continue; 454156929Sariff tmpch = sce->channel; 455156929Sariff if (direction != tmpch->direction || 456156929Sariff (tmpch->flags & CHN_F_VIRTUAL) != flsearch) 457156929Sariff continue; 458156929Sariff if (ch->num == tmpch->num) { 459156929Sariff ch->num++; 460156929Sariff goto retry_num_search; 461156929Sariff } 462156929Sariff rpnum++; 463156929Sariff } 464156929Sariffretry_num_search_out: 465156929Sariff if (*pnum != rpnum) { 466156929Sariff device_printf(d->dev, 467156929Sariff "%s: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n", 468156929Sariff __func__, dirs, *pnum, rpnum); 469156929Sariff *pnum = rpnum; 470156929Sariff } 471156929Sariff (*pnum)++; 472107237Scg snd_mtxunlock(d->lock); 47383614Scg 47477269Scg ch->pid = -1; 47577269Scg ch->parentsnddev = d; 47677269Scg ch->parentchannel = parent; 47789774Sscottl ch->dev = d->dev; 478156929Sariff snprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); 47977269Scg 480126367Struckman err = chn_init(ch, devinfo, dir, direction); 48170134Scg if (err) { 48283614Scg device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 48377269Scg kobj_delete(ch->methods, M_DEVBUF); 48477269Scg free(ch, M_DEVBUF); 485107237Scg snd_mtxlock(d->lock); 48683614Scg (*pnum)--; 487107237Scg snd_mtxunlock(d->lock); 48883614Scg 48977269Scg return NULL; 49055483Scg } 49177269Scg 49277269Scg return ch; 49377269Scg} 49477269Scg 49577269Scgint 49677269Scgpcm_chn_destroy(struct pcm_channel *ch) 49777269Scg{ 49883614Scg struct snddev_info *d; 49977269Scg int err; 50077269Scg 50183614Scg d = ch->parentsnddev; 50277269Scg err = chn_kill(ch); 50377269Scg if (err) { 50483614Scg device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 50577269Scg return err; 50677269Scg } 50777269Scg 50877269Scg kobj_delete(ch->methods, M_DEVBUF); 50977269Scg free(ch, M_DEVBUF); 51077269Scg 51177269Scg return 0; 51277269Scg} 51377269Scg 51477269Scgint 515124740Smatkpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 51677269Scg{ 51782180Scg struct snddev_channel *sce, *tmp, *after; 518156929Sariff unsigned rdevcount; 519124740Smatk int device = device_get_unit(d->dev); 520156929Sariff int stop; 521156929Sariff size_t namelen; 52277269Scg 523124740Smatk /* 524124740Smatk * Note it's confusing nomenclature. 525124740Smatk * dev_t 526124740Smatk * device -> pcm_device 527124740Smatk * unit -> pcm_channel 528124740Smatk * channel -> snddev_channel 529124740Smatk * device_t 530124740Smatk * unit -> pcm_device 531124740Smatk */ 532124740Smatk 533111119Simp sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 53477269Scg if (!sce) { 53577269Scg return ENOMEM; 53677269Scg } 53777269Scg 538100478Sorion snd_mtxlock(d->lock); 53977269Scg sce->channel = ch; 540156929Sariff sce->chan_num = 0; 541156929Sariff after = NULL; 542156929Sariff stop = 0; 543156929Sariffretry_chan_num_search: 544156929Sariff /* 545156929Sariff * Look for possible channel numbering collision. This may not 546156929Sariff * be optimized, but it will ensure that no collision occured. 547156929Sariff * Creating maximum possible channels (256 channels) will cost 548156929Sariff * us at most 32895 cycles, but this can be considered cheap 549156929Sariff * since none of the locking/unlocking operations involved. 550156929Sariff * 551156929Sariff * Micro optimization, channel ordering: 552156929Sariff * hw,hw,hw,vch,vch,vch,rec 553156929Sariff */ 554156929Sariff rdevcount = 0; 555156929Sariff SLIST_FOREACH(tmp, &d->channels, link) { 556156929Sariff if (tmp == NULL || tmp->channel == NULL) 557156929Sariff continue; 558156929Sariff if (sce->chan_num == tmp->chan_num) { 559156929Sariff sce->chan_num++; 560156929Sariff after = NULL; 561156929Sariff stop = 0; 562156929Sariff goto retry_chan_num_search; 563156762Sariff } 564156929Sariff if (stop == 0) { 565156929Sariff if (ch->flags & CHN_F_VIRTUAL) { 566149953Snetchild if (tmp->channel->direction == PCMDIR_REC) 567156929Sariff stop = 1; 568156929Sariff else 569156929Sariff after = tmp; 570156929Sariff } else if (ch->direction == PCMDIR_REC) { 571149953Snetchild after = tmp; 572156929Sariff } else { 573156929Sariff if (tmp->channel->direction != PCMDIR_PLAY || 574156929Sariff (tmp->channel->flags & CHN_F_VIRTUAL)) { 575156929Sariff stop = 1; 576156929Sariff } else { 577149953Snetchild after = tmp; 578149953Snetchild } 579149953Snetchild } 58082180Scg } 581156929Sariff rdevcount++; 58282180Scg } 583156929Sariff /* 584156929Sariff * Don't overflow PCMMKMINOR / PCMMAXCHAN. 585156929Sariff */ 586156929Sariff if (sce->chan_num > PCMMAXCHAN) { 587156929Sariff snd_mtxunlock(d->lock); 588156929Sariff device_printf(d->dev, 589156929Sariff "%s: WARNING: sce->chan_num overflow! (%d)\n", 590156929Sariff __func__, sce->chan_num); 591156929Sariff free(sce, M_DEVBUF); 592156929Sariff return E2BIG; 593156929Sariff } 594156929Sariff if (d->devcount != rdevcount) { 595156929Sariff device_printf(d->dev, 596156929Sariff "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n", 597156929Sariff __func__, d->devcount, rdevcount); 598156929Sariff d->devcount = rdevcount; 599156929Sariff } 600156762Sariff d->devcount++; 601156929Sariff if (after == NULL) { 602156929Sariff SLIST_INSERT_HEAD(&d->channels, sce, link); 603156929Sariff } else { 604156929Sariff SLIST_INSERT_AFTER(after, sce, link); 605156929Sariff } 606156929Sariff 607156929Sariff namelen = strlen(ch->name); 608156929Sariff if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */ 609156929Sariff snprintf(ch->name + namelen, 610156929Sariff CHN_NAMELEN - namelen, ":dsp%d.%d", 611156929Sariff device, sce->chan_num); 612156929Sariff } 613107237Scg snd_mtxunlock(d->lock); 614156929Sariff sce->dsp_devt = make_dev(&dsp_cdevsw, 615124740Smatk PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), 616124740Smatk UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", 617124740Smatk device, sce->chan_num); 61877269Scg 619156929Sariff sce->dspW_devt = make_dev(&dsp_cdevsw, 620124740Smatk PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), 621124740Smatk UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", 622124740Smatk device, sce->chan_num); 62377269Scg 624156929Sariff sce->audio_devt = make_dev(&dsp_cdevsw, 625124740Smatk PCMMKMINOR(device, SND_DEV_AUDIO, sce->chan_num), 626124740Smatk UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", 627124740Smatk device, sce->chan_num); 628124740Smatk 629124740Smatk if (ch->direction == PCMDIR_REC) 630124740Smatk sce->dspr_devt = make_dev(&dsp_cdevsw, 631124740Smatk PCMMKMINOR(device, SND_DEV_DSPREC, 632124740Smatk sce->chan_num), UID_ROOT, GID_WHEEL, 633124740Smatk 0666, "dspr%d.%d", device, sce->chan_num); 634124740Smatk 63550724Scg return 0; 63650724Scg} 63750724Scg 63877269Scgint 639124740Smatkpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 64065340Scg{ 64177269Scg struct snddev_channel *sce; 642124617Sphk#if 0 643119096Scg int ourlock; 64465340Scg 645119096Scg ourlock = 0; 646119096Scg if (!mtx_owned(d->lock)) { 647119096Scg snd_mtxlock(d->lock); 648119096Scg ourlock = 1; 649119096Scg } 650124617Sphk#endif 651119096Scg 65277269Scg SLIST_FOREACH(sce, &d->channels, link) { 65377269Scg if (sce->channel == ch) 65477269Scg goto gotit; 65565340Scg } 656124617Sphk#if 0 657119096Scg if (ourlock) 658119096Scg snd_mtxunlock(d->lock); 659124617Sphk#endif 66077269Scg return EINVAL; 66177269Scggotit: 66277269Scg SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 663107237Scg 664149953Snetchild if (ch->flags & CHN_F_VIRTUAL) 665149953Snetchild d->vchancount--; 666149953Snetchild else if (ch->direction == PCMDIR_REC) 667107237Scg d->reccount--; 668107237Scg else 669107237Scg d->playcount--; 670107237Scg 671124617Sphk#if 0 672119096Scg if (ourlock) 673119096Scg snd_mtxunlock(d->lock); 674124617Sphk#endif 675107237Scg free(sce, M_DEVBUF); 67677269Scg 67765340Scg return 0; 67865340Scg} 67965340Scg 68050724Scgint 68177269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 68277269Scg{ 68377269Scg struct snddev_info *d = device_get_softc(dev); 68482180Scg struct pcm_channel *ch; 68582180Scg int err; 68677269Scg 68777269Scg ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 68877269Scg if (!ch) { 68977269Scg device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 69077269Scg return ENODEV; 69177269Scg } 69278853Scg 693124740Smatk err = pcm_chn_add(d, ch); 69477269Scg if (err) { 69577269Scg device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 69677269Scg pcm_chn_destroy(ch); 69778853Scg return err; 69877269Scg } 69977269Scg 70077269Scg return err; 70177269Scg} 70277269Scg 70377269Scgstatic int 70477269Scgpcm_killchan(device_t dev) 70577269Scg{ 70677269Scg struct snddev_info *d = device_get_softc(dev); 70777269Scg struct snddev_channel *sce; 708106420Scognet struct pcm_channel *ch; 709106420Scognet int error = 0; 71077269Scg 71177269Scg sce = SLIST_FIRST(&d->channels); 712106420Scognet ch = sce->channel; 71377269Scg 714124740Smatk error = pcm_chn_remove(d, sce->channel); 715106420Scognet if (error) 716106420Scognet return (error); 717106420Scognet return (pcm_chn_destroy(ch)); 71877269Scg} 71977269Scg 72077269Scgint 72150724Scgpcm_setstatus(device_t dev, char *str) 72250724Scg{ 72374763Scg struct snddev_info *d = device_get_softc(dev); 724156929Sariff struct snddev_channel *sce; 725156929Sariff struct pcm_channel *ch; 726156929Sariff int err; 72777882Scg 72877882Scg snd_mtxlock(d->lock); 72950724Scg strncpy(d->status, str, SND_STATUSLEN); 73077882Scg snd_mtxunlock(d->lock); 731156929Sariff if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) && 732156929Sariff d->vchancount == 0) { 733156929Sariff SLIST_FOREACH(sce, &d->channels, link) { 734156929Sariff if (sce == NULL || sce->channel == NULL) 735156929Sariff continue; 736156929Sariff ch = sce->channel; 737156929Sariff CHN_LOCK(ch); 738156929Sariff if (ch->direction == PCMDIR_PLAY && 739156929Sariff (ch->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == 0 && 740156929Sariff SLIST_EMPTY(&ch->children)) { 741156929Sariff ch->flags |= CHN_F_BUSY; 742156929Sariff err = vchan_create(ch); 743156929Sariff if (err) { 744156929Sariff ch->flags &= ~CHN_F_BUSY; 745156929Sariff device_printf(d->dev, "%s: vchan_create(%s) == %d\n", 746156929Sariff __func__, ch->name, err); 747156929Sariff } else { 748156929Sariff CHN_UNLOCK(ch); 749156929Sariff return 0; 750156929Sariff } 751156929Sariff } 752156929Sariff CHN_UNLOCK(ch); 753156929Sariff } 754156929Sariff } 75550724Scg return 0; 75650724Scg} 75750724Scg 75850724Scgu_int32_t 75950724Scgpcm_getflags(device_t dev) 76050724Scg{ 76174763Scg struct snddev_info *d = device_get_softc(dev); 76277882Scg 76350724Scg return d->flags; 76450724Scg} 76550724Scg 76650724Scgvoid 76750724Scgpcm_setflags(device_t dev, u_int32_t val) 76850724Scg{ 76974763Scg struct snddev_info *d = device_get_softc(dev); 77078362Scg 77150724Scg d->flags = val; 77250724Scg} 77350724Scg 77458384Scgvoid * 77558384Scgpcm_getdevinfo(device_t dev) 77658384Scg{ 77774763Scg struct snddev_info *d = device_get_softc(dev); 77877882Scg 77958384Scg return d->devinfo; 78058384Scg} 78158384Scg 78283614Scgunsigned int 78383614Scgpcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 78483614Scg{ 78583614Scg struct snddev_info *d = device_get_softc(dev); 78689690Scg int sz, x; 78783614Scg 78883614Scg sz = 0; 78989690Scg if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 79089690Scg x = sz; 79183614Scg RANGE(sz, min, max); 79289690Scg if (x != sz) 79389690Scg device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 79489690Scg x = min; 79589690Scg while (x < sz) 79689690Scg x <<= 1; 79789690Scg if (x > sz) 79889690Scg x >>= 1; 79989690Scg if (x != sz) { 80089690Scg device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 80189690Scg sz = x; 80289690Scg } 80389690Scg } else { 80483614Scg sz = deflt; 80589690Scg } 80689690Scg 80783614Scg d->bufsz = sz; 80883614Scg 80983614Scg return sz; 81083614Scg} 81183614Scg 81250724Scgint 81350724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec) 81450724Scg{ 81574763Scg struct snddev_info *d = device_get_softc(dev); 81650724Scg 81789834Scg if (pcm_veto_load) { 81889834Scg device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 81989834Scg 82089834Scg return EINVAL; 82189834Scg } 82289834Scg 82393814Sjhb d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 82477269Scg 825148587Snetchild#if 0 826148587Snetchild /* 827148587Snetchild * d->flags should be cleared by the allocator of the softc. 828148587Snetchild * We cannot clear this field here because several devices set 829148587Snetchild * this flag before calling pcm_register(). 830148587Snetchild */ 83178853Scg d->flags = 0; 832148587Snetchild#endif 83361886Scg d->dev = dev; 83450724Scg d->devinfo = devinfo; 83578895Scg d->devcount = 0; 83683089Scg d->reccount = 0; 83783614Scg d->playcount = 0; 83878895Scg d->vchancount = 0; 83978362Scg d->inprog = 0; 84050724Scg 841124617Sphk SLIST_INIT(&d->channels); 842124617Sphk 84378362Scg if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 84478214Scg d->flags |= SD_F_SIMPLEX; 84578362Scg 84678214Scg d->fakechan = fkchan_setup(dev); 847126367Struckman chn_init(d->fakechan, NULL, 0, 0); 84855494Scg 84973127Scg#ifdef SND_DYNSYSCTL 85070680Sjhb sysctl_ctx_init(&d->sysctl_tree); 85170680Sjhb d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 85270943Sjhb SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 85370680Sjhb device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 85470943Sjhb if (d->sysctl_tree_top == NULL) { 85570680Sjhb sysctl_ctx_free(&d->sysctl_tree); 85670680Sjhb goto no; 85770680Sjhb } 85883614Scg SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 85983614Scg OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 86073127Scg#endif 861149953Snetchild if (numplay > 0) { 862156929Sariff d->flags |= SD_F_AUTOVCHAN; 86382180Scg vchan_initsys(dev); 864149953Snetchild } 86578853Scg 86682180Scg sndstat_register(dev, d->status, sndstat_prepare_pcm); 86750724Scg return 0; 86850724Scgno: 86977882Scg snd_mtxfree(d->lock); 87050724Scg return ENXIO; 87150724Scg} 87250724Scg 87365340Scgint 87465340Scgpcm_unregister(device_t dev) 87565207Scg{ 87674763Scg struct snddev_info *d = device_get_softc(dev); 87777269Scg struct snddev_channel *sce; 87883614Scg struct pcm_channel *ch; 87965207Scg 880150827Snetchild if (sndstat_acquire() != 0) { 881150827Snetchild device_printf(dev, "unregister: sndstat busy\n"); 882150827Snetchild return EBUSY; 883150827Snetchild } 884150827Snetchild 88577882Scg snd_mtxlock(d->lock); 88678362Scg if (d->inprog) { 88783476Sgreid device_printf(dev, "unregister: operation in progress\n"); 88878362Scg snd_mtxunlock(d->lock); 889150827Snetchild sndstat_release(); 89078362Scg return EBUSY; 89178362Scg } 892124740Smatk 89377269Scg SLIST_FOREACH(sce, &d->channels, link) { 89483614Scg ch = sce->channel; 89583614Scg if (ch->refcount > 0) { 89695684Scg device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 89777882Scg snd_mtxunlock(d->lock); 898150827Snetchild sndstat_release(); 89977269Scg return EBUSY; 90077269Scg } 90165487Scg } 902124740Smatk 903148587Snetchild if (mixer_uninit(dev)) { 904148587Snetchild device_printf(dev, "unregister: mixer busy\n"); 905148587Snetchild snd_mtxunlock(d->lock); 906150827Snetchild sndstat_release(); 907148587Snetchild return EBUSY; 908148587Snetchild } 909148587Snetchild 910124740Smatk SLIST_FOREACH(sce, &d->channels, link) { 911156762Sariff if (sce->dsp_devt) { 912149953Snetchild destroy_dev(sce->dsp_devt); 913156762Sariff sce->dsp_devt = NULL; 914156762Sariff } 915156762Sariff if (sce->dspW_devt) { 916149953Snetchild destroy_dev(sce->dspW_devt); 917156762Sariff sce->dspW_devt = NULL; 918156762Sariff } 919156762Sariff if (sce->audio_devt) { 920149953Snetchild destroy_dev(sce->audio_devt); 921156762Sariff sce->audio_devt = NULL; 922156762Sariff } 923156762Sariff if (sce->dspr_devt) { 924124740Smatk destroy_dev(sce->dspr_devt); 925156762Sariff sce->dspr_devt = NULL; 926156762Sariff } 927156762Sariff d->devcount--; 928124740Smatk } 929124740Smatk 93074763Scg#ifdef SND_DYNSYSCTL 93174763Scg d->sysctl_tree_top = NULL; 93274763Scg sysctl_ctx_free(&d->sysctl_tree); 93374763Scg#endif 93478395Scg while (!SLIST_EMPTY(&d->channels)) 93577269Scg pcm_killchan(dev); 93665207Scg 93774763Scg chn_kill(d->fakechan); 93874763Scg fkchan_kill(d->fakechan); 93973127Scg 940124617Sphk snd_mtxunlock(d->lock); 94177882Scg snd_mtxfree(d->lock); 942150827Snetchild sndstat_unregister(dev); 943150827Snetchild sndstat_release(); 94465340Scg return 0; 94565207Scg} 94665207Scg 94782180Scg/************************************************************************/ 94882180Scg 94982180Scgstatic int 95082180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 95182180Scg{ 95282180Scg struct snddev_info *d; 95382180Scg struct snddev_channel *sce; 95482180Scg struct pcm_channel *c; 95582180Scg struct pcm_feeder *f; 95682180Scg int pc, rc, vc; 95782180Scg 95882180Scg if (verbose < 1) 95982180Scg return 0; 96082180Scg 96182180Scg d = device_get_softc(dev); 96282180Scg if (!d) 96382180Scg return ENXIO; 96482180Scg 96582180Scg snd_mtxlock(d->lock); 96682180Scg if (!SLIST_EMPTY(&d->channels)) { 96782180Scg pc = rc = vc = 0; 96882180Scg SLIST_FOREACH(sce, &d->channels, link) { 96982180Scg c = sce->channel; 97082180Scg if (c->direction == PCMDIR_PLAY) { 97182180Scg if (c->flags & CHN_F_VIRTUAL) 97282180Scg vc++; 97382180Scg else 97482180Scg pc++; 97582180Scg } else 97682180Scg rc++; 97782180Scg } 97883614Scg sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 97982180Scg (d->flags & SD_F_SIMPLEX)? "" : " duplex", 98082180Scg#ifdef USING_DEVFS 98182180Scg (device_get_unit(dev) == snd_unit)? " default" : "" 98282180Scg#else 98382180Scg "" 98482180Scg#endif 98582180Scg ); 986119096Scg 987119096Scg if (verbose <= 1) { 988119096Scg snd_mtxunlock(d->lock); 989119096Scg return 0; 990119096Scg } 991119096Scg 99282180Scg SLIST_FOREACH(sce, &d->channels, link) { 99382180Scg c = sce->channel; 994155342Snetchild 995155342Snetchild KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 996155342Snetchild ("hosed pcm channel setup")); 997155342Snetchild 99882479Scg sbuf_printf(s, "\n\t"); 99982479Scg 1000124617Sphk /* it would be better to indent child channels */ 100182479Scg sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 100289691Scg sbuf_printf(s, "spd %d", c->speed); 100389691Scg if (c->speed != sndbuf_getspd(c->bufhard)) 100489691Scg sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 100589691Scg sbuf_printf(s, ", fmt 0x%08x", c->format); 100689691Scg if (c->format != sndbuf_getfmt(c->bufhard)) 100789691Scg sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 1008119096Scg sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 100982180Scg if (c->pid != -1) 101082180Scg sbuf_printf(s, ", pid %d", c->pid); 101182180Scg sbuf_printf(s, "\n\t"); 101289691Scg 1013155342Snetchild sbuf_printf(s, "interrupts %d, ", c->interrupts); 1014155342Snetchild if (c->direction == PCMDIR_REC) 1015155342Snetchild sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 1016155342Snetchild c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), 1017155342Snetchild sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1018155342Snetchild sndbuf_getblkcnt(c->bufhard), 1019155342Snetchild sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1020155342Snetchild sndbuf_getblkcnt(c->bufsoft)); 1021155342Snetchild else 1022155342Snetchild sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", 1023155342Snetchild c->xruns, sndbuf_getready(c->bufsoft), 1024155342Snetchild sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1025155342Snetchild sndbuf_getblkcnt(c->bufhard), 1026155342Snetchild sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1027155342Snetchild sndbuf_getblkcnt(c->bufsoft)); 1028155342Snetchild sbuf_printf(s, "\n\t"); 102989691Scg 103089691Scg sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 103189691Scg sbuf_printf(s, " -> "); 103282180Scg f = c->feeder; 103389691Scg while (f->source != NULL) 103489691Scg f = f->source; 103589691Scg while (f != NULL) { 103682180Scg sbuf_printf(s, "%s", f->class->name); 103782180Scg if (f->desc->type == FEEDER_FMT) 103889691Scg sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 103982180Scg if (f->desc->type == FEEDER_RATE) 104089691Scg sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 1041150827Snetchild if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || 1042150827Snetchild f->desc->type == FEEDER_VOLUME) 104389691Scg sbuf_printf(s, "(0x%08x)", f->desc->out); 104489691Scg sbuf_printf(s, " -> "); 104589691Scg f = f->parent; 104682180Scg } 104789691Scg sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 104882180Scg } 104982180Scg } else 105082180Scg sbuf_printf(s, " (mixer only)"); 105182180Scg snd_mtxunlock(d->lock); 105282180Scg 105382180Scg return 0; 105482180Scg} 105582180Scg 105682180Scg/************************************************************************/ 105782180Scg 105882180Scg#ifdef SND_DYNSYSCTL 105982180Scgint 106082180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 106182180Scg{ 106282180Scg struct snddev_info *d; 106382180Scg struct snddev_channel *sce; 106482180Scg struct pcm_channel *c; 1065149953Snetchild int err, newcnt, cnt; 106682180Scg 1067149953Snetchild /* 1068149953Snetchild * XXX WOAH... NEED SUPER CLEANUP!!! 1069149953Snetchild * Robust, yet confusing. Understanding these will 1070149953Snetchild * cause your brain spinning like a Doki Doki Dynamo. 1071149953Snetchild */ 107282180Scg d = oidp->oid_arg1; 107382180Scg 1074156929Sariff if (!(d->flags & SD_F_AUTOVCHAN)) 1075149953Snetchild return EINVAL; 1076119096Scg 107782180Scg cnt = 0; 107882180Scg SLIST_FOREACH(sce, &d->channels, link) { 107982180Scg c = sce->channel; 1080125136Struckman CHN_LOCK(c); 1081119096Scg if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) { 108282180Scg cnt++; 1083149953Snetchild if (req->newptr != NULL && c->flags & CHN_F_BUSY) { 1084149953Snetchild /* Better safe than sorry */ 1085149953Snetchild CHN_UNLOCK(c); 1086149953Snetchild return EBUSY; 1087149953Snetchild } 1088119096Scg } 1089125136Struckman CHN_UNLOCK(c); 109082180Scg } 1091119096Scg 109282180Scg newcnt = cnt; 109382180Scg 109482180Scg err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 1095119096Scg 109682180Scg if (err == 0 && req->newptr != NULL) { 1097119096Scg 1098156762Sariff if (newcnt < 0 || newcnt > (PCMMAXCHAN + 1) || 1099156762Sariff (d->playcount + d->reccount + newcnt) > (PCMMAXCHAN + 1)) 1100149953Snetchild return E2BIG; 1101149953Snetchild 1102149953Snetchild if (pcm_inprog(d, 1) != 1) { 1103119096Scg pcm_inprog(d, -1); 1104149953Snetchild return EINPROGRESS; 110582180Scg } 110682180Scg 110782180Scg if (newcnt > cnt) { 110882180Scg /* add new vchans - find a parent channel first */ 110982180Scg SLIST_FOREACH(sce, &d->channels, link) { 111082180Scg c = sce->channel; 1111125136Struckman CHN_LOCK(c); 111282180Scg /* not a candidate if not a play channel */ 111382180Scg if (c->direction != PCMDIR_PLAY) 1114125136Struckman goto next; 111582180Scg /* not a candidate if a virtual channel */ 111682180Scg if (c->flags & CHN_F_VIRTUAL) 1117125136Struckman goto next; 111882180Scg /* not a candidate if it's in use */ 1119125136Struckman if (!(c->flags & CHN_F_BUSY) || 1120125136Struckman !(SLIST_EMPTY(&c->children))) 1121125136Struckman /* 1122125136Struckman * if we get here we're a nonvirtual 1123125136Struckman * play channel, and either 1124125136Struckman * 1) not busy 1125125136Struckman * 2) busy with children, not directly 1126125136Struckman * open 1127125136Struckman * 1128125136Struckman * thus we can add children 1129125136Struckman */ 1130125136Struckman goto addok; 1131125136Struckmannext: 1132125136Struckman CHN_UNLOCK(c); 113382180Scg } 1134119096Scg pcm_inprog(d, -1); 113582180Scg return EBUSY; 113682180Scgaddok: 113782180Scg c->flags |= CHN_F_BUSY; 113882180Scg while (err == 0 && newcnt > cnt) { 1139156762Sariff if (d->devcount > PCMMAXCHAN) { 1140156762Sariff device_printf(d->dev, "%s: Maximum channel reached.\n", __func__); 1141156762Sariff break; 1142156762Sariff } 114382180Scg err = vchan_create(c); 114482180Scg if (err == 0) 114582180Scg cnt++; 1146156762Sariff if (newcnt > cnt && err == E2BIG) { 1147156762Sariff device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err); 1148156762Sariff err = 0; 1149156762Sariff break; 1150156762Sariff } 115182180Scg } 1152125136Struckman CHN_UNLOCK(c); 115382180Scg } else if (newcnt < cnt) { 1154119096Scg snd_mtxlock(d->lock); 115582180Scg while (err == 0 && newcnt < cnt) { 1156156762Sariff c = NULL; 115782180Scg SLIST_FOREACH(sce, &d->channels, link) { 1158156762Sariff CHN_LOCK(sce->channel); 1159156762Sariff if (sce->channel->direction == PCMDIR_PLAY && 1160156765Sariff (sce->channel->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 1161156762Sariff c = sce->channel; 1162156762Sariff CHN_UNLOCK(sce->channel); 116382180Scg } 1164156762Sariff if (c != NULL) 1165156762Sariff goto remok; 1166119096Scg snd_mtxunlock(d->lock); 1167119096Scg pcm_inprog(d, -1); 116882180Scg return EINVAL; 116982180Scgremok: 117082180Scg err = vchan_destroy(c); 117182180Scg if (err == 0) 117282180Scg cnt--; 117382180Scg } 1174119096Scg snd_mtxunlock(d->lock); 117582180Scg } 1176149953Snetchild pcm_inprog(d, -1); 117782180Scg } 117882180Scg return err; 117982180Scg} 118082180Scg#endif 118182180Scg 118282180Scg/************************************************************************/ 118382180Scg 1184132236Stanimurastatic int 1185132236Stanimurasound_modevent(module_t mod, int type, void *data) 1186132236Stanimura{ 1187148587Snetchild#if 0 1188132236Stanimura return (midi_modevent(mod, type, data)); 1189148587Snetchild#else 1190148587Snetchild return 0; 1191148587Snetchild#endif 1192132236Stanimura} 1193132236Stanimura 1194132236StanimuraDEV_MODULE(sound, sound_modevent, NULL); 1195132236StanimuraMODULE_VERSION(sound, SOUND_MODVER); 1196