sound.c revision 124617
1264790Sbapt/* 2264790Sbapt * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 3319297Sdelphij * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) 4264790Sbapt * All rights reserved. 5264790Sbapt * 6264790Sbapt * Redistribution and use in source and binary forms, with or without 7264790Sbapt * modification, are permitted provided that the following conditions 8264790Sbapt * are met: 9264790Sbapt * 1. Redistributions of source code must retain the above copyright 10264790Sbapt * notice, this list of conditions and the following disclaimer. 11264790Sbapt * 2. Redistributions in binary form must reproduce the above copyright 12264790Sbapt * notice, this list of conditions and the following disclaimer in the 13264790Sbapt * documentation and/or other materials provided with the distribution. 14264790Sbapt * 15264790Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16264790Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17264790Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18264790Sbapt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19264790Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20264790Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21264790Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22264790Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23264790Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24264790Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25264790Sbapt * SUCH DAMAGE. 26264790Sbapt */ 27264790Sbapt 28264790Sbapt#include <dev/sound/pcm/sound.h> 29264790Sbapt#include <dev/sound/pcm/vchan.h> 30264790Sbapt#include <sys/sysctl.h> 31264790Sbapt 32264790Sbapt#include "feeder_if.h" 33264790Sbapt 34264790SbaptSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 124617 2004-01-17 10:37:11Z phk $"); 35264790Sbapt 36264790Sbaptdevclass_t pcm_devclass; 37264790Sbapt 38264790Sbaptint pcm_veto_load = 1; 39264790Sbapt 40264790Sbapt#ifdef USING_DEVFS 41264790Sbaptint snd_unit = 0; 42264790SbaptTUNABLE_INT("hw.snd.unit", &snd_unit); 43264790Sbapt#endif 44264790Sbapt 45264790Sbaptint snd_maxautovchans = 0; 46264790SbaptTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 47264790Sbapt 48264790SbaptSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 49264790Sbapt 50264790Sbaptstatic int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 51264790Sbapt 52264790Sbaptstruct sysctl_ctx_list * 53264790Sbaptsnd_sysctl_tree(device_t dev) 54264790Sbapt{ 55264790Sbapt struct snddev_info *d = device_get_softc(dev); 56264790Sbapt 57264790Sbapt return &d->sysctl_tree; 58264790Sbapt} 59264790Sbapt 60264790Sbaptstruct sysctl_oid * 61264790Sbaptsnd_sysctl_tree_top(device_t dev) 62264790Sbapt{ 63264790Sbapt struct snddev_info *d = device_get_softc(dev); 64264790Sbapt 65264790Sbapt return d->sysctl_tree_top; 66264790Sbapt} 67264790Sbapt 68264790Sbaptvoid * 69264790Sbaptsnd_mtxcreate(const char *desc, const char *type) 70264790Sbapt{ 71264790Sbapt#ifdef USING_MUTEX 72264790Sbapt struct mtx *m; 73264790Sbapt 74264790Sbapt m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 75264790Sbapt if (m == NULL) 76264790Sbapt return NULL; 77264790Sbapt mtx_init(m, desc, type, MTX_DEF | MTX_RECURSE); 78264790Sbapt return m; 79264790Sbapt#else 80264790Sbapt return (void *)0xcafebabe; 81264790Sbapt#endif 82264790Sbapt} 83264790Sbapt 84264790Sbaptvoid 85264790Sbaptsnd_mtxfree(void *m) 86264790Sbapt{ 87264790Sbapt#ifdef USING_MUTEX 88264790Sbapt struct mtx *mtx = m; 89264790Sbapt 90264790Sbapt /* mtx_assert(mtx, MA_OWNED); */ 91264790Sbapt mtx_destroy(mtx); 92264790Sbapt free(mtx, M_DEVBUF); 93264790Sbapt#endif 94264790Sbapt} 95264790Sbapt 96264790Sbaptvoid 97264790Sbaptsnd_mtxassert(void *m) 98264790Sbapt{ 99264790Sbapt#ifdef USING_MUTEX 100264790Sbapt#ifdef INVARIANTS 101264790Sbapt struct mtx *mtx = m; 102264790Sbapt 103264790Sbapt mtx_assert(mtx, MA_OWNED); 104264790Sbapt#endif 105264790Sbapt#endif 106264790Sbapt} 107264790Sbapt/* 108264790Sbaptvoid 109264790Sbaptsnd_mtxlock(void *m) 110264790Sbapt{ 111264790Sbapt#ifdef USING_MUTEX 112264790Sbapt struct mtx *mtx = m; 113264790Sbapt 114264790Sbapt mtx_lock(mtx); 115264790Sbapt#endif 116264790Sbapt} 117264790Sbapt 118264790Sbaptvoid 119264790Sbaptsnd_mtxunlock(void *m) 120264790Sbapt{ 121264790Sbapt#ifdef USING_MUTEX 122264790Sbapt struct mtx *mtx = m; 123264790Sbapt 124264790Sbapt mtx_unlock(mtx); 125264790Sbapt#endif 126264790Sbapt} 127264790Sbapt*/ 128264790Sbaptint 129264790Sbaptsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 130264790Sbapt{ 131264790Sbapt#ifdef USING_MUTEX 132264790Sbapt flags &= INTR_MPSAFE; 133264790Sbapt flags |= INTR_TYPE_AV; 134264790Sbapt#else 135264790Sbapt flags = INTR_TYPE_AV; 136264790Sbapt#endif 137264790Sbapt return bus_setup_intr(dev, res, flags, hand, param, cookiep); 138264790Sbapt} 139264790Sbapt 140264790Sbapt#ifndef PCM_DEBUG_MTX 141264790Sbaptvoid 142264790Sbaptpcm_lock(struct snddev_info *d) 143264790Sbapt{ 144264790Sbapt snd_mtxlock(d->lock); 145264790Sbapt} 146264790Sbapt 147319297Sdelphijvoid 148264790Sbaptpcm_unlock(struct snddev_info *d) 149264790Sbapt{ 150264790Sbapt snd_mtxunlock(d->lock); 151264790Sbapt} 152264790Sbapt#endif 153264790Sbapt 154264790Sbaptstruct pcm_channel * 155264790Sbaptpcm_getfakechan(struct snddev_info *d) 156264790Sbapt{ 157264790Sbapt return d->fakechan; 158264790Sbapt} 159264790Sbapt 160264790Sbapt/* return a locked channel */ 161264790Sbaptstruct pcm_channel * 162264790Sbaptpcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) 163264790Sbapt{ 164264790Sbapt struct pcm_channel *c; 165264790Sbapt struct snddev_channel *sce; 166264790Sbapt int err; 167264790Sbapt 168264790Sbapt snd_mtxassert(d->lock); 169264790Sbapt 170264790Sbapt /* scan for a free channel */ 171264790Sbapt SLIST_FOREACH(sce, &d->channels, link) { 172264790Sbapt c = sce->channel; 173264790Sbapt CHN_LOCK(c); 174264790Sbapt if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 175264790Sbapt if (chnum == -1 || c->num == chnum) { 176264790Sbapt c->flags |= CHN_F_BUSY; 177264790Sbapt c->pid = pid; 178264790Sbapt return c; 179264790Sbapt } 180264790Sbapt } 181264790Sbapt CHN_UNLOCK(c); 182264790Sbapt } 183264790Sbapt 184264790Sbapt /* no channel available */ 185264790Sbapt if (direction == PCMDIR_PLAY) { 186264790Sbapt if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { 187264790Sbapt /* try to create a vchan */ 188264790Sbapt SLIST_FOREACH(sce, &d->channels, link) { 189264790Sbapt c = sce->channel; 190264790Sbapt if (!SLIST_EMPTY(&c->children)) { 191264790Sbapt err = vchan_create(c); 192264790Sbapt if (!err) 193264790Sbapt return pcm_chnalloc(d, direction, pid, -1); 194264790Sbapt else 195264790Sbapt device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 196264790Sbapt } 197264790Sbapt } 198264790Sbapt } 199264790Sbapt } 200264790Sbapt 201264790Sbapt return NULL; 202264790Sbapt} 203264790Sbapt 204264790Sbapt/* release a locked channel and unlock it */ 205264790Sbaptint 206264790Sbaptpcm_chnrelease(struct pcm_channel *c) 207264790Sbapt{ 208319297Sdelphij CHN_LOCKASSERT(c); 209264790Sbapt c->flags &= ~CHN_F_BUSY; 210264790Sbapt c->pid = -1; 211264790Sbapt CHN_UNLOCK(c); 212264790Sbapt return 0; 213319297Sdelphij} 214264790Sbapt 215264790Sbaptint 216264790Sbaptpcm_chnref(struct pcm_channel *c, int ref) 217264790Sbapt{ 218264790Sbapt int r; 219264790Sbapt 220264790Sbapt CHN_LOCKASSERT(c); 221264790Sbapt c->refcount += ref; 222264790Sbapt r = c->refcount; 223264790Sbapt return r; 224264790Sbapt} 225264790Sbapt 226264790Sbaptint 227264790Sbaptpcm_inprog(struct snddev_info *d, int delta) 228264790Sbapt{ 229264790Sbapt int r; 230264790Sbapt 231264790Sbapt if (delta == 0) 232264790Sbapt return d->inprog; 233264790Sbapt 234264790Sbapt /* backtrace(); */ 235264790Sbapt pcm_lock(d); 236264790Sbapt d->inprog += delta; 237264790Sbapt r = d->inprog; 238264790Sbapt pcm_unlock(d); 239264790Sbapt return r; 240264790Sbapt} 241264790Sbapt 242264790Sbaptstatic void 243272655Sbaptpcm_setmaxautovchans(struct snddev_info *d, int num) 244272655Sbapt{ 245272655Sbapt struct pcm_channel *c; 246272655Sbapt struct snddev_channel *sce; 247272655Sbapt int err, done; 248264790Sbapt 249264790Sbapt if (num > 0 && d->vchancount == 0) { 250264790Sbapt SLIST_FOREACH(sce, &d->channels, link) { 251264790Sbapt c = sce->channel; 252264790Sbapt if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { 253264790Sbapt c->flags |= CHN_F_BUSY; 254264790Sbapt err = vchan_create(c); 255264790Sbapt if (err) { 256264790Sbapt device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 257264790Sbapt c->flags &= ~CHN_F_BUSY; 258264790Sbapt } 259264790Sbapt return; 260264790Sbapt } 261264790Sbapt } 262264790Sbapt } 263264790Sbapt if (num == 0 && d->vchancount > 0) { 264264790Sbapt done = 0; 265264790Sbapt while (!done) { 266264790Sbapt done = 1; 267264790Sbapt SLIST_FOREACH(sce, &d->channels, link) { 268264790Sbapt c = sce->channel; 269264790Sbapt if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { 270264790Sbapt done = 0; 271264790Sbapt snd_mtxlock(d->lock); 272264790Sbapt err = vchan_destroy(c); 273264790Sbapt snd_mtxunlock(d->lock); 274264790Sbapt if (err) 275264790Sbapt device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); 276264790Sbapt break; /* restart */ 277264790Sbapt } 278264790Sbapt } 279264790Sbapt } 280264790Sbapt } 281264790Sbapt} 282264790Sbapt 283264790Sbapt#ifdef USING_DEVFS 284264790Sbaptstatic int 285264790Sbaptsysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 286264790Sbapt{ 287264790Sbapt struct snddev_info *d; 288264790Sbapt int error, unit; 289264790Sbapt 290264790Sbapt unit = snd_unit; 291264790Sbapt error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 292264790Sbapt if (error == 0 && req->newptr != NULL) { 293264790Sbapt if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 294264790Sbapt return EINVAL; 295264790Sbapt d = devclass_get_softc(pcm_devclass, unit); 296264790Sbapt if (d == NULL || SLIST_EMPTY(&d->channels)) 297264790Sbapt return EINVAL; 298264790Sbapt snd_unit = unit; 299264790Sbapt } 300264790Sbapt return (error); 301264790Sbapt} 302264790SbaptSYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 303264790Sbapt 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 304264790Sbapt#endif 305264790Sbapt 306264790Sbaptstatic int 307264790Sbaptsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 308264790Sbapt{ 309264790Sbapt struct snddev_info *d; 310264790Sbapt int i, v, error; 311264790Sbapt 312264790Sbapt v = snd_maxautovchans; 313264790Sbapt error = sysctl_handle_int(oidp, &v, sizeof(v), req); 314264790Sbapt if (error == 0 && req->newptr != NULL) { 315264790Sbapt if (v < 0 || v >= SND_MAXVCHANS) 316264790Sbapt return EINVAL; 317264790Sbapt if (v != snd_maxautovchans) { 318264790Sbapt for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { 319264790Sbapt d = devclass_get_softc(pcm_devclass, i); 320264790Sbapt if (!d) 321264790Sbapt continue; 322264790Sbapt pcm_setmaxautovchans(d, v); 323264790Sbapt } 324264790Sbapt } 325264790Sbapt snd_maxautovchans = v; 326264790Sbapt } 327264790Sbapt return (error); 328264790Sbapt} 329264790SbaptSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 330264790Sbapt 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", ""); 331264790Sbapt 332264790Sbaptstruct pcm_channel * 333264790Sbaptpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 334264790Sbapt{ 335264790Sbapt struct pcm_channel *ch; 336264790Sbapt char *dirs; 337264790Sbapt int err, *pnum; 338264790Sbapt 339264790Sbapt switch(dir) { 340264790Sbapt case PCMDIR_PLAY: 341264790Sbapt dirs = "play"; 342264790Sbapt pnum = &d->playcount; 343264790Sbapt break; 344264790Sbapt 345319297Sdelphij case PCMDIR_REC: 346319297Sdelphij dirs = "record"; 347319297Sdelphij pnum = &d->reccount; 348264790Sbapt break; 349264790Sbapt 350264790Sbapt case PCMDIR_VIRTUAL: 351264790Sbapt dirs = "virtual"; 352264790Sbapt dir = PCMDIR_PLAY; 353264790Sbapt pnum = &d->vchancount; 354264790Sbapt break; 355264790Sbapt 356264790Sbapt default: 357264790Sbapt return NULL; 358264790Sbapt } 359264790Sbapt 360264790Sbapt ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 361264790Sbapt if (!ch) 362264790Sbapt return NULL; 363264790Sbapt 364264790Sbapt ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 365264790Sbapt if (!ch->methods) { 366264790Sbapt free(ch, M_DEVBUF); 367264790Sbapt 368264790Sbapt return NULL; 369264790Sbapt } 370264790Sbapt 371264790Sbapt snd_mtxlock(d->lock); 372264790Sbapt ch->num = (*pnum)++; 373264790Sbapt snd_mtxunlock(d->lock); 374264790Sbapt 375264790Sbapt ch->pid = -1; 376264790Sbapt ch->parentsnddev = d; 377264790Sbapt ch->parentchannel = parent; 378264790Sbapt ch->dev = d->dev; 379264790Sbapt snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); 380264790Sbapt 381264790Sbapt err = chn_init(ch, devinfo, dir); 382264790Sbapt if (err) { 383264790Sbapt device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); 384264790Sbapt kobj_delete(ch->methods, M_DEVBUF); 385264790Sbapt free(ch, M_DEVBUF); 386264790Sbapt snd_mtxlock(d->lock); 387264790Sbapt (*pnum)--; 388264790Sbapt snd_mtxunlock(d->lock); 389264790Sbapt 390264790Sbapt return NULL; 391264790Sbapt } 392264790Sbapt 393264790Sbapt return ch; 394264790Sbapt} 395264790Sbapt 396264790Sbaptint 397264790Sbaptpcm_chn_destroy(struct pcm_channel *ch) 398264790Sbapt{ 399264790Sbapt struct snddev_info *d; 400264790Sbapt int err; 401264790Sbapt 402264790Sbapt d = ch->parentsnddev; 403264790Sbapt err = chn_kill(ch); 404264790Sbapt if (err) { 405319297Sdelphij device_printf(d->dev, "chn_kill(%s) failed, err = %d\n", ch->name, err); 406264790Sbapt return err; 407319297Sdelphij } 408264790Sbapt 409264790Sbapt kobj_delete(ch->methods, M_DEVBUF); 410264790Sbapt free(ch, M_DEVBUF); 411264790Sbapt 412264790Sbapt return 0; 413319297Sdelphij} 414264790Sbapt 415264790Sbaptint 416264790Sbaptpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 417264790Sbapt{ 418264790Sbapt struct snddev_channel *sce, *tmp, *after; 419264790Sbapt int unit = device_get_unit(d->dev); 420264790Sbapt int x = -1; 421264790Sbapt 422319297Sdelphij sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 423264790Sbapt if (!sce) { 424264790Sbapt return ENOMEM; 425319297Sdelphij } 426319297Sdelphij 427264790Sbapt snd_mtxlock(d->lock); 428264790Sbapt sce->channel = ch; 429264790Sbapt if (SLIST_EMPTY(&d->channels)) { 430264790Sbapt SLIST_INSERT_HEAD(&d->channels, sce, link); 431264790Sbapt } else { 432264790Sbapt after = NULL; 433319297Sdelphij SLIST_FOREACH(tmp, &d->channels, link) { 434264790Sbapt after = tmp; 435264790Sbapt } 436264790Sbapt SLIST_INSERT_AFTER(after, sce, link); 437264790Sbapt } 438264790Sbapt if (mkdev) 439264790Sbapt x = d->devcount++; 440264790Sbapt snd_mtxunlock(d->lock); 441264790Sbapt 442264790Sbapt if (mkdev) { 443264790Sbapt dsp_register(unit, x); 444264790Sbapt if (ch->direction == PCMDIR_REC) 445264790Sbapt dsp_registerrec(unit, ch->num); 446264790Sbapt } 447319297Sdelphij 448264790Sbapt return 0; 449264790Sbapt} 450264790Sbapt 451264790Sbaptint 452264790Sbaptpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 453264790Sbapt{ 454264790Sbapt struct snddev_channel *sce; 455264790Sbapt int unit = device_get_unit(d->dev); 456264790Sbapt#if 0 457264790Sbapt int ourlock; 458264790Sbapt 459264790Sbapt ourlock = 0; 460264790Sbapt if (!mtx_owned(d->lock)) { 461264790Sbapt snd_mtxlock(d->lock); 462264790Sbapt ourlock = 1; 463264790Sbapt } 464264790Sbapt#endif 465264790Sbapt 466264790Sbapt SLIST_FOREACH(sce, &d->channels, link) { 467264790Sbapt if (sce->channel == ch) 468264790Sbapt goto gotit; 469264790Sbapt } 470264790Sbapt#if 0 471264790Sbapt if (ourlock) 472264790Sbapt snd_mtxunlock(d->lock); 473264790Sbapt#endif 474264790Sbapt return EINVAL; 475264790Sbaptgotit: 476264790Sbapt SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 477264790Sbapt if (rmdev) { 478264790Sbapt dsp_unregister(unit, --d->devcount); 479264790Sbapt if (ch->direction == PCMDIR_REC) 480264790Sbapt dsp_unregisterrec(unit, ch->num); 481264790Sbapt } 482264790Sbapt 483264790Sbapt if (ch->direction == PCMDIR_REC) 484264790Sbapt d->reccount--; 485264790Sbapt else if (ch->flags & CHN_F_VIRTUAL) 486264790Sbapt d->vchancount--; 487264790Sbapt else 488264790Sbapt d->playcount--; 489264790Sbapt 490264790Sbapt#if 0 491264790Sbapt if (ourlock) 492264790Sbapt snd_mtxunlock(d->lock); 493264790Sbapt#endif 494264790Sbapt free(sce, M_DEVBUF); 495264790Sbapt 496264790Sbapt return 0; 497264790Sbapt} 498264790Sbapt 499264790Sbaptint 500264790Sbaptpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 501264790Sbapt{ 502264790Sbapt struct snddev_info *d = device_get_softc(dev); 503264790Sbapt struct pcm_channel *ch; 504264790Sbapt int err; 505264790Sbapt 506264790Sbapt ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 507264790Sbapt if (!ch) { 508264790Sbapt device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 509264790Sbapt return ENODEV; 510264790Sbapt } 511319297Sdelphij 512264790Sbapt err = pcm_chn_add(d, ch, 1); 513264790Sbapt if (err) { 514264790Sbapt device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 515264790Sbapt snd_mtxunlock(d->lock); 516264790Sbapt pcm_chn_destroy(ch); 517264790Sbapt return err; 518264790Sbapt } 519264790Sbapt 520264790Sbapt if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN) && 521264790Sbapt ch->direction == PCMDIR_PLAY && d->vchancount == 0) { 522264790Sbapt ch->flags |= CHN_F_BUSY; 523264790Sbapt err = vchan_create(ch); 524264790Sbapt if (err) { 525264790Sbapt device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); 526264790Sbapt ch->flags &= ~CHN_F_BUSY; 527264790Sbapt } 528264790Sbapt } 529264790Sbapt 530264790Sbapt return err; 531264790Sbapt} 532264790Sbapt 533264790Sbaptstatic int 534264790Sbaptpcm_killchan(device_t dev) 535264790Sbapt{ 536264790Sbapt struct snddev_info *d = device_get_softc(dev); 537264790Sbapt struct snddev_channel *sce; 538264790Sbapt struct pcm_channel *ch; 539264790Sbapt int error = 0; 540264790Sbapt 541264790Sbapt sce = SLIST_FIRST(&d->channels); 542264790Sbapt ch = sce->channel; 543264790Sbapt 544264790Sbapt error = pcm_chn_remove(d, sce->channel, SLIST_EMPTY(&ch->children)); 545264790Sbapt if (error) 546264790Sbapt return (error); 547264790Sbapt return (pcm_chn_destroy(ch)); 548264790Sbapt} 549264790Sbapt 550264790Sbaptint 551264790Sbaptpcm_setstatus(device_t dev, char *str) 552264790Sbapt{ 553264790Sbapt struct snddev_info *d = device_get_softc(dev); 554264790Sbapt 555264790Sbapt snd_mtxlock(d->lock); 556264790Sbapt strncpy(d->status, str, SND_STATUSLEN); 557264790Sbapt snd_mtxunlock(d->lock); 558264790Sbapt return 0; 559264790Sbapt} 560264790Sbapt 561264790Sbaptu_int32_t 562264790Sbaptpcm_getflags(device_t dev) 563264790Sbapt{ 564264790Sbapt struct snddev_info *d = device_get_softc(dev); 565264790Sbapt 566264790Sbapt return d->flags; 567264790Sbapt} 568264790Sbapt 569264790Sbaptvoid 570264790Sbaptpcm_setflags(device_t dev, u_int32_t val) 571319297Sdelphij{ 572319297Sdelphij struct snddev_info *d = device_get_softc(dev); 573264790Sbapt 574264790Sbapt d->flags = val; 575264790Sbapt} 576264790Sbapt 577264790Sbaptvoid * 578264790Sbaptpcm_getdevinfo(device_t dev) 579264790Sbapt{ 580264790Sbapt struct snddev_info *d = device_get_softc(dev); 581264790Sbapt 582264790Sbapt return d->devinfo; 583264790Sbapt} 584264790Sbapt 585264790Sbaptunsigned int 586264790Sbaptpcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) 587264790Sbapt{ 588264790Sbapt struct snddev_info *d = device_get_softc(dev); 589264790Sbapt int sz, x; 590264790Sbapt 591264790Sbapt sz = 0; 592264790Sbapt if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 593264790Sbapt x = sz; 594264790Sbapt RANGE(sz, min, max); 595264790Sbapt if (x != sz) 596264790Sbapt device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, min, max, sz); 597264790Sbapt x = min; 598264790Sbapt while (x < sz) 599264790Sbapt x <<= 1; 600264790Sbapt if (x > sz) 601264790Sbapt x >>= 1; 602264790Sbapt if (x != sz) { 603264790Sbapt device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 604264790Sbapt sz = x; 605264790Sbapt } 606264790Sbapt } else { 607264790Sbapt sz = deflt; 608264790Sbapt } 609264790Sbapt 610264790Sbapt d->bufsz = sz; 611264790Sbapt 612272655Sbapt return sz; 613272655Sbapt} 614264790Sbapt 615264790Sbaptint 616319297Sdelphijpcm_register(device_t dev, void *devinfo, int numplay, int numrec) 617319297Sdelphij{ 618264790Sbapt struct snddev_info *d = device_get_softc(dev); 619319297Sdelphij 620264790Sbapt if (pcm_veto_load) { 621264790Sbapt device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 622264790Sbapt 623264790Sbapt return EINVAL; 624264790Sbapt } 625264790Sbapt 626264790Sbapt d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 627264790Sbapt 628264790Sbapt d->flags = 0; 629319297Sdelphij d->dev = dev; 630264790Sbapt d->devinfo = devinfo; 631264790Sbapt d->devcount = 0; 632264790Sbapt d->reccount = 0; 633264790Sbapt d->playcount = 0; 634264790Sbapt d->vchancount = 0; 635264790Sbapt d->inprog = 0; 636264790Sbapt 637264790Sbapt SLIST_INIT(&d->channels); 638264790Sbapt SLIST_INIT(&d->channels); 639264790Sbapt 640264790Sbapt if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 641264790Sbapt d->flags |= SD_F_SIMPLEX; 642264790Sbapt 643264790Sbapt d->fakechan = fkchan_setup(dev); 644264790Sbapt chn_init(d->fakechan, NULL, 0); 645264790Sbapt 646264790Sbapt#ifdef SND_DYNSYSCTL 647264790Sbapt sysctl_ctx_init(&d->sysctl_tree); 648264790Sbapt d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 649319297Sdelphij SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 650264790Sbapt device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 651264790Sbapt if (d->sysctl_tree_top == NULL) { 652264790Sbapt sysctl_ctx_free(&d->sysctl_tree); 653264790Sbapt goto no; 654264790Sbapt } 655264790Sbapt SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), 656264790Sbapt OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); 657264790Sbapt#endif 658264790Sbapt if (numplay > 0) 659264790Sbapt vchan_initsys(dev); 660264790Sbapt if (numplay == 1) 661264790Sbapt d->flags |= SD_F_AUTOVCHAN; 662264790Sbapt 663264790Sbapt sndstat_register(dev, d->status, sndstat_prepare_pcm); 664264790Sbapt return 0; 665264790Sbaptno: 666264790Sbapt snd_mtxfree(d->lock); 667264790Sbapt return ENXIO; 668264790Sbapt} 669264790Sbapt 670264790Sbaptint 671264790Sbaptpcm_unregister(device_t dev) 672264790Sbapt{ 673264790Sbapt struct snddev_info *d = device_get_softc(dev); 674264790Sbapt struct snddev_channel *sce; 675264790Sbapt struct pcm_channel *ch; 676264790Sbapt 677264790Sbapt snd_mtxlock(d->lock); 678264790Sbapt if (d->inprog) { 679264790Sbapt device_printf(dev, "unregister: operation in progress\n"); 680264790Sbapt snd_mtxunlock(d->lock); 681264790Sbapt return EBUSY; 682264790Sbapt } 683264790Sbapt if (sndstat_busy() != 0) { 684264790Sbapt device_printf(dev, "unregister: sndstat busy\n"); 685264790Sbapt snd_mtxunlock(d->lock); 686264790Sbapt return EBUSY; 687264790Sbapt } 688264790Sbapt SLIST_FOREACH(sce, &d->channels, link) { 689264790Sbapt ch = sce->channel; 690264790Sbapt if (ch->refcount > 0) { 691264790Sbapt device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); 692264790Sbapt snd_mtxunlock(d->lock); 693264790Sbapt return EBUSY; 694264790Sbapt } 695264790Sbapt } 696264790Sbapt if (mixer_uninit(dev)) { 697264790Sbapt device_printf(dev, "unregister: mixer busy\n"); 698264790Sbapt snd_mtxunlock(d->lock); 699264790Sbapt return EBUSY; 700264790Sbapt } 701264790Sbapt 702264790Sbapt#ifdef SND_DYNSYSCTL 703264790Sbapt d->sysctl_tree_top = NULL; 704264790Sbapt sysctl_ctx_free(&d->sysctl_tree); 705264790Sbapt#endif 706264790Sbapt while (!SLIST_EMPTY(&d->channels)) 707264790Sbapt pcm_killchan(dev); 708319297Sdelphij 709264790Sbapt chn_kill(d->fakechan); 710272655Sbapt fkchan_kill(d->fakechan); 711264790Sbapt 712264790Sbapt sndstat_unregister(dev); 713272655Sbapt snd_mtxunlock(d->lock); 714264790Sbapt snd_mtxfree(d->lock); 715264790Sbapt return 0; 716264790Sbapt} 717264790Sbapt 718264790Sbaptint 719264790Sbaptpcm_regdevt(dev_t dev, unsigned unit, unsigned type, unsigned channel) 720264790Sbapt{ 721264790Sbapt struct snddev_info *d; 722264790Sbapt struct snddev_devt *dt; 723264790Sbapt 724264790Sbapt d = devclass_get_softc(pcm_devclass, unit); 725264790Sbapt KASSERT((d != NULL), ("bad d")); 726264790Sbapt KASSERT((dev != NULL), ("bad dev")); 727264790Sbapt 728264790Sbapt dt = malloc(sizeof(*dt), M_DEVBUF, M_ZERO | M_WAITOK); 729264790Sbapt if (dt == NULL) 730319297Sdelphij return ENOMEM; 731264790Sbapt dt->dev = dev; 732264790Sbapt dt->type = type; 733264790Sbapt dt->channel = channel; 734264790Sbapt 735264790Sbapt snd_mtxlock(d->lock); 736264790Sbapt SLIST_INSERT_HEAD(&d->devs, dt, link); 737264790Sbapt snd_mtxunlock(d->lock); 738264790Sbapt 739264790Sbapt return 0; 740264790Sbapt} 741264790Sbapt 742264790Sbaptdev_t 743264790Sbaptpcm_getdevt(unsigned unit, unsigned type, unsigned channel) 744264790Sbapt{ 745264790Sbapt struct snddev_info *d; 746264790Sbapt struct snddev_devt *dt; 747264790Sbapt 748264790Sbapt d = devclass_get_softc(pcm_devclass, unit); 749264790Sbapt KASSERT((d != NULL), ("bad d")); 750264790Sbapt 751264790Sbapt#if 0 752264790Sbapt snd_mtxlock(d->lock); 753264790Sbapt#endif 754319297Sdelphij SLIST_FOREACH(dt, &d->devs, link) { 755264790Sbapt if ((dt->type == type) && (dt->channel == channel)) 756264790Sbapt return dt->dev; 757264790Sbapt } 758264790Sbapt#if 0 759264790Sbapt snd_mtxunlock(d->lock); 760264790Sbapt#endif 761264790Sbapt 762264790Sbapt return NULL; 763264790Sbapt} 764264790Sbapt 765264790Sbaptint 766264790Sbaptpcm_unregdevt(unsigned unit, unsigned type, unsigned channel) 767264790Sbapt{ 768272655Sbapt struct snddev_info *d; 769264790Sbapt struct snddev_devt *dt; 770264790Sbapt 771264790Sbapt d = devclass_get_softc(pcm_devclass, unit); 772264790Sbapt KASSERT((d != NULL), ("bad d")); 773264790Sbapt 774264790Sbapt#if 0 775264790Sbapt snd_mtxlock(d->lock); 776264790Sbapt#endif 777264790Sbapt SLIST_FOREACH(dt, &d->devs, link) { 778264790Sbapt if ((dt->type == type) && (dt->channel == channel)) { 779264790Sbapt SLIST_REMOVE(&d->devs, dt, snddev_devt, link); 780264790Sbapt free(dt, M_DEVBUF); 781264790Sbapt#if 0 782264790Sbapt snd_mtxunlock(d->lock); 783264790Sbapt#endif 784264790Sbapt return 0; 785264790Sbapt } 786264790Sbapt } 787264790Sbapt#if 0 788264790Sbapt snd_mtxunlock(d->lock); 789319297Sdelphij#endif 790264790Sbapt 791264790Sbapt return ENOENT; 792264790Sbapt} 793264790Sbapt 794264790Sbapt/************************************************************************/ 795264790Sbapt 796264790Sbaptstatic int 797264790Sbaptsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 798264790Sbapt{ 799264790Sbapt struct snddev_info *d; 800264790Sbapt struct snddev_channel *sce; 801264790Sbapt struct pcm_channel *c; 802264790Sbapt struct pcm_feeder *f; 803264790Sbapt int pc, rc, vc; 804264790Sbapt 805264790Sbapt if (verbose < 1) 806264790Sbapt return 0; 807264790Sbapt 808264790Sbapt d = device_get_softc(dev); 809264790Sbapt if (!d) 810264790Sbapt return ENXIO; 811264790Sbapt 812264790Sbapt snd_mtxlock(d->lock); 813264790Sbapt if (!SLIST_EMPTY(&d->channels)) { 814264790Sbapt pc = rc = vc = 0; 815264790Sbapt SLIST_FOREACH(sce, &d->channels, link) { 816264790Sbapt c = sce->channel; 817264790Sbapt if (c->direction == PCMDIR_PLAY) { 818264790Sbapt if (c->flags & CHN_F_VIRTUAL) 819264790Sbapt vc++; 820264790Sbapt else 821264790Sbapt pc++; 822264790Sbapt } else 823264790Sbapt rc++; 824264790Sbapt } 825264790Sbapt sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", d->playcount, d->reccount, d->vchancount, 826264790Sbapt (d->flags & SD_F_SIMPLEX)? "" : " duplex", 827264790Sbapt#ifdef USING_DEVFS 828264790Sbapt (device_get_unit(dev) == snd_unit)? " default" : "" 829264790Sbapt#else 830264790Sbapt "" 831264790Sbapt#endif 832264790Sbapt ); 833264790Sbapt 834264790Sbapt if (verbose <= 1) { 835319297Sdelphij snd_mtxunlock(d->lock); 836264790Sbapt return 0; 837264790Sbapt } 838319297Sdelphij 839319297Sdelphij SLIST_FOREACH(sce, &d->channels, link) { 840319297Sdelphij c = sce->channel; 841319297Sdelphij sbuf_printf(s, "\n\t"); 842319297Sdelphij 843319297Sdelphij /* it would be better to indent child channels */ 844319297Sdelphij sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 845319297Sdelphij sbuf_printf(s, "spd %d", c->speed); 846319297Sdelphij if (c->speed != sndbuf_getspd(c->bufhard)) 847264790Sbapt sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 848264790Sbapt sbuf_printf(s, ", fmt 0x%08x", c->format); 849264790Sbapt if (c->format != sndbuf_getfmt(c->bufhard)) 850264790Sbapt sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 851264790Sbapt sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 852264790Sbapt if (c->pid != -1) 853264790Sbapt sbuf_printf(s, ", pid %d", c->pid); 854264790Sbapt sbuf_printf(s, "\n\t"); 855264790Sbapt 856264790Sbapt if (c->bufhard != NULL && c->bufsoft != NULL) { 857264790Sbapt sbuf_printf(s, "interrupts %d, ", c->interrupts); 858264790Sbapt if (c->direction == PCMDIR_REC) 859264790Sbapt sbuf_printf(s, "overruns %d, hfree %d, sfree %d", 860264790Sbapt c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); 861264790Sbapt else 862264790Sbapt sbuf_printf(s, "underruns %d, ready %d", 863264790Sbapt c->xruns, sndbuf_getready(c->bufsoft)); 864264790Sbapt sbuf_printf(s, "\n\t"); 865264790Sbapt } 866264790Sbapt 867264790Sbapt sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 868264790Sbapt sbuf_printf(s, " -> "); 869264790Sbapt f = c->feeder; 870264790Sbapt while (f->source != NULL) 871319297Sdelphij f = f->source; 872264790Sbapt while (f != NULL) { 873272655Sbapt sbuf_printf(s, "%s", f->class->name); 874264790Sbapt if (f->desc->type == FEEDER_FMT) 875264790Sbapt sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 876272655Sbapt if (f->desc->type == FEEDER_RATE) 877264790Sbapt sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 878272655Sbapt if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 879264790Sbapt sbuf_printf(s, "(0x%08x)", f->desc->out); 880264790Sbapt sbuf_printf(s, " -> "); 881264790Sbapt f = f->parent; 882264790Sbapt } 883264790Sbapt sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 884264790Sbapt } 885264790Sbapt } else 886264790Sbapt sbuf_printf(s, " (mixer only)"); 887319297Sdelphij snd_mtxunlock(d->lock); 888264790Sbapt 889272655Sbapt return 0; 890264790Sbapt} 891264790Sbapt 892272655Sbapt/************************************************************************/ 893264790Sbapt 894264790Sbapt#ifdef SND_DYNSYSCTL 895264790Sbaptint 896264790Sbaptsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 897264790Sbapt{ 898264790Sbapt struct snddev_info *d; 899264790Sbapt struct snddev_channel *sce; 900264790Sbapt struct pcm_channel *c; 901264790Sbapt int err, newcnt, cnt, busy; 902264790Sbapt int x; 903264790Sbapt 904264790Sbapt d = oidp->oid_arg1; 905264790Sbapt 906264790Sbapt x = pcm_inprog(d, 1); 907264790Sbapt if (x != 1) { 908264790Sbapt printf("x: %d\n", x); 909264790Sbapt pcm_inprog(d, -1); 910264790Sbapt return EINPROGRESS; 911264790Sbapt } 912264790Sbapt 913264790Sbapt busy = 0; 914264790Sbapt cnt = 0; 915264790Sbapt SLIST_FOREACH(sce, &d->channels, link) { 916264790Sbapt c = sce->channel; 917264790Sbapt if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) { 918264790Sbapt cnt++; 919264790Sbapt if (c->flags & CHN_F_BUSY) 920264790Sbapt busy++; 921264790Sbapt } 922319297Sdelphij } 923264790Sbapt 924272655Sbapt newcnt = cnt; 925264790Sbapt 926264790Sbapt err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); 927272655Sbapt 928264790Sbapt if (err == 0 && req->newptr != NULL) { 929264790Sbapt 930264790Sbapt if (newcnt < 0 || newcnt > SND_MAXVCHANS) { 931264790Sbapt pcm_inprog(d, -1); 932264790Sbapt return E2BIG; 933264790Sbapt } 934264790Sbapt 935264790Sbapt if (newcnt > cnt) { 936264790Sbapt /* add new vchans - find a parent channel first */ 937264790Sbapt SLIST_FOREACH(sce, &d->channels, link) { 938264790Sbapt c = sce->channel; 939264790Sbapt /* not a candidate if not a play channel */ 940264790Sbapt if (c->direction != PCMDIR_PLAY) 941264790Sbapt continue; 942264790Sbapt /* not a candidate if a virtual channel */ 943264790Sbapt if (c->flags & CHN_F_VIRTUAL) 944319297Sdelphij continue; 945264790Sbapt /* not a candidate if it's in use */ 946264790Sbapt if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) 947264790Sbapt continue; 948264790Sbapt /* 949264790Sbapt * if we get here we're a nonvirtual play channel, and either 950264790Sbapt * 1) not busy 951264790Sbapt * 2) busy with children, not directly open 952264790Sbapt * 953264790Sbapt * thus we can add children 954264790Sbapt */ 955264790Sbapt goto addok; 956264790Sbapt } 957264790Sbapt pcm_inprog(d, -1); 958264790Sbapt return EBUSY; 959264790Sbaptaddok: 960264790Sbapt c->flags |= CHN_F_BUSY; 961264790Sbapt while (err == 0 && newcnt > cnt) { 962264790Sbapt err = vchan_create(c); 963264790Sbapt if (err == 0) 964264790Sbapt cnt++; 965264790Sbapt } 966264790Sbapt if (SLIST_EMPTY(&c->children)) 967264790Sbapt c->flags &= ~CHN_F_BUSY; 968264790Sbapt } else if (newcnt < cnt) { 969264790Sbapt if (busy > newcnt) { 970264790Sbapt printf("cnt %d, newcnt %d, busy %d\n", cnt, newcnt, busy); 971264790Sbapt pcm_inprog(d, -1); 972264790Sbapt return EBUSY; 973264790Sbapt } 974264790Sbapt 975264790Sbapt snd_mtxlock(d->lock); 976264790Sbapt while (err == 0 && newcnt < cnt) { 977264790Sbapt SLIST_FOREACH(sce, &d->channels, link) { 978264790Sbapt c = sce->channel; 979264790Sbapt if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) 980264790Sbapt goto remok; 981264790Sbapt } 982264790Sbapt snd_mtxunlock(d->lock); 983264790Sbapt pcm_inprog(d, -1); 984264790Sbapt return EINVAL; 985264790Sbaptremok: 986264790Sbapt err = vchan_destroy(c); 987264790Sbapt if (err == 0) 988264790Sbapt cnt--; 989264790Sbapt } 990264790Sbapt snd_mtxunlock(d->lock); 991264790Sbapt } 992264790Sbapt } 993264790Sbapt pcm_inprog(d, -1); 994264790Sbapt return err; 995264790Sbapt} 996264790Sbapt#endif 997264790Sbapt 998264790Sbapt/************************************************************************/ 999264790Sbapt 1000264790Sbaptstatic moduledata_t sndpcm_mod = { 1001264790Sbapt "snd_pcm", 1002264790Sbapt NULL, 1003264790Sbapt NULL 1004264790Sbapt}; 1005264790SbaptDECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 1006264790SbaptMODULE_VERSION(snd_pcm, PCM_MODVER); 1007264790Sbapt