1987e5972SCameron Grant /* 2987e5972SCameron Grant * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3987e5972SCameron Grant * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) 4987e5972SCameron Grant * All rights reserved. 5987e5972SCameron Grant * 6987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without 7987e5972SCameron Grant * modification, are permitted provided that the following conditions 8987e5972SCameron Grant * are met: 9987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright 10987e5972SCameron Grant * notice, this list of conditions and the following disclaimer. 11987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 12987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the 13987e5972SCameron Grant * documentation and/or other materials provided with the distribution. 14987e5972SCameron Grant * 15987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25987e5972SCameron Grant * SUCH DAMAGE. 26987e5972SCameron Grant * 2753c5a968SPeter Wemm * $FreeBSD$ 28987e5972SCameron Grant */ 29987e5972SCameron Grant 30ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 31285648f9SCameron Grant #include <dev/sound/pcm/vchan.h> 327c438dbeSCameron Grant #include <sys/sysctl.h> 33285648f9SCameron Grant 34d95502a8SCameron Grant devclass_t pcm_devclass; 3582db23e2SCameron Grant 3682db23e2SCameron Grant #ifdef USING_DEVFS 37d95502a8SCameron Grant int snd_unit = 0; 3809786698SPeter Wemm TUNABLE_INT("hw.snd.unit", &snd_unit); 3982db23e2SCameron Grant #endif 40cbe7d6a3SCameron Grant 41cd9766c5SCameron Grant int snd_autovchans = 0; 42f637a36cSCameron Grant int snd_maxvchans = 0; 43bc0e6469SBrian Feldman #if __FreeBSD_version > 500000 440cfa4761SBrian Feldman TUNABLE_INT("hw.snd.autovchans", &snd_autovchans); 45f637a36cSCameron Grant TUNABLE_INT("hw.snd.maxvchans", &snd_maxvchans); 460cfa4761SBrian Feldman #else 470cfa4761SBrian Feldman TUNABLE_INT("hw.snd.autovchans", 0, snd_autovchans); 480cfa4761SBrian Feldman TUNABLE_INT("hw.snd.maxvchans", 0, snd_maxvchans); 490cfa4761SBrian Feldman #endif 50987e5972SCameron Grant 5182db23e2SCameron Grant SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 5282db23e2SCameron Grant 5337209180SCameron Grant void * 5437209180SCameron Grant snd_mtxcreate(const char *desc) 5537209180SCameron Grant { 5637209180SCameron Grant #ifdef USING_MUTEX 5737209180SCameron Grant struct mtx *m; 5837209180SCameron Grant 5937209180SCameron Grant m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 6037209180SCameron Grant if (m == NULL) 6137209180SCameron Grant return NULL; 6237209180SCameron Grant mtx_init(m, desc, MTX_RECURSE); 6337209180SCameron Grant return m; 6437209180SCameron Grant #else 65a983d575SCameron Grant return (void *)0xcafebabe; 6637209180SCameron Grant #endif 6737209180SCameron Grant } 6837209180SCameron Grant 6937209180SCameron Grant void 7037209180SCameron Grant snd_mtxfree(void *m) 7137209180SCameron Grant { 7237209180SCameron Grant #ifdef USING_MUTEX 7337209180SCameron Grant struct mtx *mtx = m; 7437209180SCameron Grant 7537209180SCameron Grant mtx_assert(mtx, MA_OWNED); 7637209180SCameron Grant mtx_destroy(mtx); 7737209180SCameron Grant free(mtx, M_DEVBUF); 7837209180SCameron Grant #endif 7937209180SCameron Grant } 8037209180SCameron Grant 8137209180SCameron Grant void 8237209180SCameron Grant snd_mtxassert(void *m) 8337209180SCameron Grant { 8437209180SCameron Grant #ifdef USING_MUTEX 85f00f162aSCameron Grant #ifdef INVARIANTS 8637209180SCameron Grant struct mtx *mtx = m; 8737209180SCameron Grant 8837209180SCameron Grant mtx_assert(mtx, MA_OWNED); 8937209180SCameron Grant #endif 90f00f162aSCameron Grant #endif 9137209180SCameron Grant } 9237209180SCameron Grant 9337209180SCameron Grant void 9437209180SCameron Grant snd_mtxlock(void *m) 9537209180SCameron Grant { 9637209180SCameron Grant #ifdef USING_MUTEX 9737209180SCameron Grant struct mtx *mtx = m; 9837209180SCameron Grant 9937209180SCameron Grant mtx_lock(mtx); 10037209180SCameron Grant #endif 10137209180SCameron Grant } 10237209180SCameron Grant 10337209180SCameron Grant void 10437209180SCameron Grant snd_mtxunlock(void *m) 10537209180SCameron Grant { 10637209180SCameron Grant #ifdef USING_MUTEX 10737209180SCameron Grant struct mtx *mtx = m; 10837209180SCameron Grant 10937209180SCameron Grant mtx_unlock(mtx); 11037209180SCameron Grant #endif 11137209180SCameron Grant } 11237209180SCameron Grant 11337209180SCameron Grant int 11437209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 11537209180SCameron Grant { 11637209180SCameron Grant #ifdef USING_MUTEX 11737209180SCameron Grant flags &= INTR_MPSAFE; 11846700f12SPeter Wemm flags |= INTR_TYPE_AV; 11937209180SCameron Grant #else 12046700f12SPeter Wemm flags = INTR_TYPE_AV; 12137209180SCameron Grant #endif 12237209180SCameron Grant return bus_setup_intr(dev, res, flags, hand, param, cookiep); 12337209180SCameron Grant } 12437209180SCameron Grant 125b8f0d9e0SCameron Grant /* return a locked channel */ 126285648f9SCameron Grant struct pcm_channel * 127b8f0d9e0SCameron Grant pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid) 128285648f9SCameron Grant { 129285648f9SCameron Grant struct pcm_channel *c; 130285648f9SCameron Grant struct snddev_channel *sce; 131f637a36cSCameron Grant int err; 132285648f9SCameron Grant 133b8f0d9e0SCameron Grant snd_mtxassert(d->lock); 134f637a36cSCameron Grant 135f637a36cSCameron Grant /* scan for a free channel */ 136285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 137285648f9SCameron Grant c = sce->channel; 13849c5e6e2SCameron Grant CHN_LOCK(c); 139285648f9SCameron Grant if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 140285648f9SCameron Grant c->flags |= CHN_F_BUSY; 141b8f0d9e0SCameron Grant c->pid = pid; 142285648f9SCameron Grant return c; 143285648f9SCameron Grant } 14449c5e6e2SCameron Grant CHN_UNLOCK(c); 145285648f9SCameron Grant } 146f637a36cSCameron Grant 147f637a36cSCameron Grant /* no channel available */ 148f637a36cSCameron Grant if (direction == PCMDIR_PLAY) { 149f637a36cSCameron Grant if ((d->vchancount > 0) && (d->vchancount < snd_maxvchans)) { 150f637a36cSCameron Grant /* try to create a vchan */ 151f637a36cSCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 152f637a36cSCameron Grant c = sce->channel; 153f637a36cSCameron Grant if (!SLIST_EMPTY(&c->children)) { 154f637a36cSCameron Grant err = vchan_create(c); 155f637a36cSCameron Grant if (!err) 156f637a36cSCameron Grant return pcm_chnalloc(d, direction, pid); 157f637a36cSCameron Grant else 158f637a36cSCameron Grant device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); 159f637a36cSCameron Grant } 160f637a36cSCameron Grant } 161f637a36cSCameron Grant } 162f637a36cSCameron Grant } 163f637a36cSCameron Grant 164285648f9SCameron Grant return NULL; 165285648f9SCameron Grant } 166285648f9SCameron Grant 167b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 168285648f9SCameron Grant int 169b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 170285648f9SCameron Grant { 171b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 172285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 173b8f0d9e0SCameron Grant c->pid = -1; 17449c5e6e2SCameron Grant CHN_UNLOCK(c); 175285648f9SCameron Grant return 0; 176285648f9SCameron Grant } 177285648f9SCameron Grant 178285648f9SCameron Grant int 179285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 180285648f9SCameron Grant { 18149c5e6e2SCameron Grant int r; 18249c5e6e2SCameron Grant 183b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 184285648f9SCameron Grant c->refcount += ref; 18549c5e6e2SCameron Grant r = c->refcount; 18649c5e6e2SCameron Grant return r; 187285648f9SCameron Grant } 188285648f9SCameron Grant 18982db23e2SCameron Grant #ifdef USING_DEVFS 19033dbf14aSCameron Grant static int 191cd9766c5SCameron Grant sysctl_hw_snd_unit(SYSCTL_HANDLER_ARGS) 19233dbf14aSCameron Grant { 193b8f0d9e0SCameron Grant struct snddev_info *d; 19433dbf14aSCameron Grant int error, unit; 19533dbf14aSCameron Grant 19633dbf14aSCameron Grant unit = snd_unit; 19733dbf14aSCameron Grant error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 19833dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 19974ffd138SCameron Grant if (unit < 0 || unit >= devclass_get_maxunit(pcm_devclass)) 200b8f0d9e0SCameron Grant return EINVAL; 201b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 202faeebea2SCameron Grant if (d == NULL || SLIST_EMPTY(&d->channels)) 203b8f0d9e0SCameron Grant return EINVAL; 20433dbf14aSCameron Grant snd_unit = unit; 20533dbf14aSCameron Grant } 20633dbf14aSCameron Grant return (error); 20733dbf14aSCameron Grant } 208b3b7ccfeSJohn Baldwin SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 209cd9766c5SCameron Grant 0, sizeof(int), sysctl_hw_snd_unit, "I", ""); 21082db23e2SCameron Grant #endif 211987e5972SCameron Grant 212cd9766c5SCameron Grant static int 213cd9766c5SCameron Grant sysctl_hw_snd_autovchans(SYSCTL_HANDLER_ARGS) 214cd9766c5SCameron Grant { 215cd9766c5SCameron Grant int v, error; 216cd9766c5SCameron Grant 217cd9766c5SCameron Grant v = snd_autovchans; 218cd9766c5SCameron Grant error = sysctl_handle_int(oidp, &v, sizeof(v), req); 219cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 220cd9766c5SCameron Grant if (v < 0 || v >= SND_MAXVCHANS) 221cd9766c5SCameron Grant return EINVAL; 222cd9766c5SCameron Grant snd_autovchans = v; 223cd9766c5SCameron Grant } 224cd9766c5SCameron Grant return (error); 225cd9766c5SCameron Grant } 226cd9766c5SCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, autovchans, CTLTYPE_INT | CTLFLAG_RW, 227cd9766c5SCameron Grant 0, sizeof(int), sysctl_hw_snd_autovchans, "I", ""); 228cd9766c5SCameron Grant 229f637a36cSCameron Grant static int 230f637a36cSCameron Grant sysctl_hw_snd_maxvchans(SYSCTL_HANDLER_ARGS) 231f637a36cSCameron Grant { 232f637a36cSCameron Grant int v, error; 233f637a36cSCameron Grant 234f637a36cSCameron Grant v = snd_maxvchans; 235f637a36cSCameron Grant error = sysctl_handle_int(oidp, &v, sizeof(v), req); 236f637a36cSCameron Grant if (error == 0 && req->newptr != NULL) { 237f637a36cSCameron Grant if (v < 0 || v >= SND_MAXVCHANS) 238f637a36cSCameron Grant return EINVAL; 239f637a36cSCameron Grant snd_maxvchans = v; 240f637a36cSCameron Grant } 241f637a36cSCameron Grant return (error); 242f637a36cSCameron Grant } 243f637a36cSCameron Grant SYSCTL_PROC(_hw_snd, OID_AUTO, maxvchans, CTLTYPE_INT | CTLFLAG_RW, 244f637a36cSCameron Grant 0, sizeof(int), sysctl_hw_snd_maxvchans, "I", ""); 245f637a36cSCameron Grant 246285648f9SCameron Grant struct pcm_channel * 247285648f9SCameron Grant pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 248987e5972SCameron Grant { 249285648f9SCameron Grant struct pcm_channel *ch; 25033dbf14aSCameron Grant char *dirs; 2510f55ac6cSCameron Grant int err; 252987e5972SCameron Grant 253285648f9SCameron Grant switch(dir) { 254285648f9SCameron Grant case PCMDIR_PLAY: 255285648f9SCameron Grant dirs = "play"; 256285648f9SCameron Grant break; 257285648f9SCameron Grant case PCMDIR_REC: 258285648f9SCameron Grant dirs = "record"; 259285648f9SCameron Grant break; 260285648f9SCameron Grant case PCMDIR_VIRTUAL: 261285648f9SCameron Grant dirs = "virtual"; 262285648f9SCameron Grant dir = PCMDIR_PLAY; 263285648f9SCameron Grant break; 264285648f9SCameron Grant default: 265285648f9SCameron Grant return NULL; 2669c326820SCameron Grant } 267285648f9SCameron Grant 268285648f9SCameron Grant ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 269285648f9SCameron Grant if (!ch) 270285648f9SCameron Grant return NULL; 271285648f9SCameron Grant 2720f55ac6cSCameron Grant ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 273285648f9SCameron Grant if (!ch->methods) { 274285648f9SCameron Grant free(ch, M_DEVBUF); 275285648f9SCameron Grant return NULL; 276285648f9SCameron Grant } 277285648f9SCameron Grant 278285648f9SCameron Grant ch->pid = -1; 279285648f9SCameron Grant ch->parentsnddev = d; 280285648f9SCameron Grant ch->parentchannel = parent; 281285648f9SCameron Grant snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs); 282285648f9SCameron Grant 2830f55ac6cSCameron Grant err = chn_init(ch, devinfo, dir); 2840f55ac6cSCameron Grant if (err) { 285285648f9SCameron Grant device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err); 286285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 287285648f9SCameron Grant free(ch, M_DEVBUF); 288285648f9SCameron Grant return NULL; 289bbb5bf3dSCameron Grant } 290285648f9SCameron Grant 291285648f9SCameron Grant return ch; 292285648f9SCameron Grant } 293285648f9SCameron Grant 294285648f9SCameron Grant int 295285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 296285648f9SCameron Grant { 297285648f9SCameron Grant int err; 298285648f9SCameron Grant 299285648f9SCameron Grant err = chn_kill(ch); 300285648f9SCameron Grant if (err) { 301285648f9SCameron Grant device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err); 302285648f9SCameron Grant return err; 303285648f9SCameron Grant } 304285648f9SCameron Grant 305285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 306285648f9SCameron Grant free(ch, M_DEVBUF); 307285648f9SCameron Grant 308285648f9SCameron Grant return 0; 309285648f9SCameron Grant } 310285648f9SCameron Grant 311285648f9SCameron Grant int 312f637a36cSCameron Grant pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) 313285648f9SCameron Grant { 314285648f9SCameron Grant struct snddev_channel *sce; 315285648f9SCameron Grant int unit = device_get_unit(d->dev); 316b8f0d9e0SCameron Grant 317b8f0d9e0SCameron Grant snd_mtxlock(d->lock); 318285648f9SCameron Grant 319285648f9SCameron Grant sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 320285648f9SCameron Grant if (!sce) { 321b8f0d9e0SCameron Grant snd_mtxunlock(d->lock); 322285648f9SCameron Grant return ENOMEM; 323285648f9SCameron Grant } 324285648f9SCameron Grant 325285648f9SCameron Grant sce->channel = ch; 326285648f9SCameron Grant SLIST_INSERT_HEAD(&d->channels, sce, link); 327285648f9SCameron Grant 328f637a36cSCameron Grant if (mkdev) 329f637a36cSCameron Grant dsp_register(unit, d->devcount++); 330d95502a8SCameron Grant d->chancount++; 331f637a36cSCameron Grant if (ch->flags & CHN_F_VIRTUAL) 332f637a36cSCameron Grant d->vchancount++; 333b8f0d9e0SCameron Grant 33449c5e6e2SCameron Grant snd_mtxunlock(d->lock); 335285648f9SCameron Grant 33633dbf14aSCameron Grant return 0; 33733dbf14aSCameron Grant } 33833dbf14aSCameron Grant 339285648f9SCameron Grant int 340f637a36cSCameron Grant pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) 34133dbf14aSCameron Grant { 342285648f9SCameron Grant struct snddev_channel *sce; 343285648f9SCameron Grant int unit = device_get_unit(d->dev); 34433dbf14aSCameron Grant 34549c5e6e2SCameron Grant snd_mtxlock(d->lock); 346285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 347285648f9SCameron Grant if (sce->channel == ch) 348285648f9SCameron Grant goto gotit; 34933dbf14aSCameron Grant } 35049c5e6e2SCameron Grant snd_mtxunlock(d->lock); 351285648f9SCameron Grant return EINVAL; 352285648f9SCameron Grant gotit: 353f637a36cSCameron Grant if (ch->flags & CHN_F_VIRTUAL) 354f637a36cSCameron Grant d->vchancount--; 35533dbf14aSCameron Grant d->chancount--; 356285648f9SCameron Grant SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 357285648f9SCameron Grant free(sce, M_DEVBUF); 358285648f9SCameron Grant 359f637a36cSCameron Grant if (rmdev) 360f637a36cSCameron Grant dsp_unregister(unit, --d->devcount); 36149c5e6e2SCameron Grant snd_mtxunlock(d->lock); 362285648f9SCameron Grant 363987e5972SCameron Grant return 0; 364987e5972SCameron Grant } 365987e5972SCameron Grant 366987e5972SCameron Grant int 367285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 368285648f9SCameron Grant { 369285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 370cd9766c5SCameron Grant struct pcm_channel *ch, *child; 371cd9766c5SCameron Grant struct pcmchan_children *pce; 372cd9766c5SCameron Grant int i, err; 373285648f9SCameron Grant 374285648f9SCameron Grant ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 375285648f9SCameron Grant if (!ch) { 376285648f9SCameron Grant device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 377285648f9SCameron Grant return ENODEV; 378285648f9SCameron Grant } 379cd9766c5SCameron Grant 380f637a36cSCameron Grant err = pcm_chn_add(d, ch, 1); 381285648f9SCameron Grant if (err) { 382285648f9SCameron Grant device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 383285648f9SCameron Grant pcm_chn_destroy(ch); 384cd9766c5SCameron Grant return err; 385cd9766c5SCameron Grant } 386cd9766c5SCameron Grant 387f637a36cSCameron Grant if ((dir == PCMDIR_PLAY) && (d->flags & SD_F_AUTOVCHAN) && (snd_autovchans > 0)) { 388cd9766c5SCameron Grant ch->flags |= CHN_F_BUSY; 389cd9766c5SCameron Grant for (i = 0; err == 0 && i < snd_autovchans; i++) 390cd9766c5SCameron Grant err = vchan_create(ch); 391cd9766c5SCameron Grant if (err) { 392cd9766c5SCameron Grant device_printf(d->dev, "vchan_create(%d) failed, err=%d\n", i - 1, err); 393cd9766c5SCameron Grant SLIST_FOREACH(pce, &ch->children, link) { 394cd9766c5SCameron Grant child = pce->channel; 395cd9766c5SCameron Grant vchan_destroy(child); 396cd9766c5SCameron Grant } 397cd9766c5SCameron Grant return err; 398cd9766c5SCameron Grant } 399285648f9SCameron Grant } 400285648f9SCameron Grant 401285648f9SCameron Grant return err; 402285648f9SCameron Grant } 403285648f9SCameron Grant 404285648f9SCameron Grant static int 405285648f9SCameron Grant pcm_killchan(device_t dev) 406285648f9SCameron Grant { 407285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 408285648f9SCameron Grant struct snddev_channel *sce; 409285648f9SCameron Grant 41049c5e6e2SCameron Grant snd_mtxlock(d->lock); 411285648f9SCameron Grant sce = SLIST_FIRST(&d->channels); 41249c5e6e2SCameron Grant snd_mtxunlock(d->lock); 413285648f9SCameron Grant 414f637a36cSCameron Grant return pcm_chn_remove(d, sce->channel, 1); 415285648f9SCameron Grant } 416285648f9SCameron Grant 417285648f9SCameron Grant int 418987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 419987e5972SCameron Grant { 42066ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 42149c5e6e2SCameron Grant 42249c5e6e2SCameron Grant snd_mtxlock(d->lock); 423987e5972SCameron Grant strncpy(d->status, str, SND_STATUSLEN); 42449c5e6e2SCameron Grant snd_mtxunlock(d->lock); 425987e5972SCameron Grant return 0; 426987e5972SCameron Grant } 427987e5972SCameron Grant 428987e5972SCameron Grant u_int32_t 429987e5972SCameron Grant pcm_getflags(device_t dev) 430987e5972SCameron Grant { 43166ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 43249c5e6e2SCameron Grant 433987e5972SCameron Grant return d->flags; 434987e5972SCameron Grant } 435987e5972SCameron Grant 436987e5972SCameron Grant void 437987e5972SCameron Grant pcm_setflags(device_t dev, u_int32_t val) 438987e5972SCameron Grant { 43966ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 440d95502a8SCameron Grant 441987e5972SCameron Grant d->flags = val; 442987e5972SCameron Grant } 443987e5972SCameron Grant 44439004e69SCameron Grant void * 44539004e69SCameron Grant pcm_getdevinfo(device_t dev) 44639004e69SCameron Grant { 44766ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 44849c5e6e2SCameron Grant 44939004e69SCameron Grant return d->devinfo; 45039004e69SCameron Grant } 45139004e69SCameron Grant 452987e5972SCameron Grant /* This is the generic init routine */ 453987e5972SCameron Grant int 454987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 455987e5972SCameron Grant { 45666ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 457987e5972SCameron Grant 45849c5e6e2SCameron Grant d->lock = snd_mtxcreate(device_get_nameunit(dev)); 45949c5e6e2SCameron Grant snd_mtxlock(d->lock); 460285648f9SCameron Grant 461cd9766c5SCameron Grant d->flags = 0; 462e4d5b250SCameron Grant d->dev = dev; 463987e5972SCameron Grant d->devinfo = devinfo; 464f637a36cSCameron Grant d->devcount = 0; 465285648f9SCameron Grant d->chancount = 0; 466f637a36cSCameron Grant d->vchancount = 0; 467d95502a8SCameron Grant d->inprog = 0; 468833f7023SCameron Grant 469d95502a8SCameron Grant if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) 470285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 471d95502a8SCameron Grant 472285648f9SCameron Grant d->fakechan = fkchan_setup(dev); 473285648f9SCameron Grant chn_init(d->fakechan, NULL, 0); 474987e5972SCameron Grant 47582db23e2SCameron Grant #ifdef SND_DYNSYSCTL 476cc486d80SJohn Baldwin sysctl_ctx_init(&d->sysctl_tree); 477cc486d80SJohn Baldwin d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 478a3e893e0SJohn Baldwin SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 479cc486d80SJohn Baldwin device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 480a3e893e0SJohn Baldwin if (d->sysctl_tree_top == NULL) { 481cc486d80SJohn Baldwin sysctl_ctx_free(&d->sysctl_tree); 482cc486d80SJohn Baldwin goto no; 483cc486d80SJohn Baldwin } 48482db23e2SCameron Grant #endif 485b8f0d9e0SCameron Grant if (numplay > 0) 486285648f9SCameron Grant vchan_initsys(d); 487cd9766c5SCameron Grant if (numplay == 1) 488cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 489cd9766c5SCameron Grant 49049c5e6e2SCameron Grant snd_mtxunlock(d->lock); 491987e5972SCameron Grant return 0; 492987e5972SCameron Grant no: 49349c5e6e2SCameron Grant snd_mtxfree(d->lock); 494987e5972SCameron Grant return ENXIO; 495987e5972SCameron Grant } 496987e5972SCameron Grant 49733dbf14aSCameron Grant int 49833dbf14aSCameron Grant pcm_unregister(device_t dev) 4997c438dbeSCameron Grant { 50066ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 501285648f9SCameron Grant struct snddev_channel *sce; 5027c438dbeSCameron Grant 50349c5e6e2SCameron Grant snd_mtxlock(d->lock); 504d95502a8SCameron Grant if (d->inprog) { 505d95502a8SCameron Grant device_printf(dev, "unregister: operation in progress"); 506d95502a8SCameron Grant snd_mtxunlock(d->lock); 507d95502a8SCameron Grant return EBUSY; 508d95502a8SCameron Grant } 509285648f9SCameron Grant SLIST_FOREACH(sce, &d->channels, link) { 510285648f9SCameron Grant if (sce->channel->refcount > 0) { 511c9b53085SCameron Grant device_printf(dev, "unregister: channel busy"); 51249c5e6e2SCameron Grant snd_mtxunlock(d->lock); 513285648f9SCameron Grant return EBUSY; 514285648f9SCameron Grant } 515c9b53085SCameron Grant } 516d95502a8SCameron Grant if (mixer_uninit(dev)) { 517c9b53085SCameron Grant device_printf(dev, "unregister: mixer busy"); 51849c5e6e2SCameron Grant snd_mtxunlock(d->lock); 519c9b53085SCameron Grant return EBUSY; 520c9b53085SCameron Grant } 5217c438dbeSCameron Grant 52266ef8af5SCameron Grant #ifdef SND_DYNSYSCTL 52366ef8af5SCameron Grant d->sysctl_tree_top = NULL; 52466ef8af5SCameron Grant sysctl_ctx_free(&d->sysctl_tree); 52566ef8af5SCameron Grant #endif 526faeebea2SCameron Grant while (!SLIST_EMPTY(&d->channels)) 527285648f9SCameron Grant pcm_killchan(dev); 5287c438dbeSCameron Grant 52966ef8af5SCameron Grant chn_kill(d->fakechan); 53066ef8af5SCameron Grant fkchan_kill(d->fakechan); 53182db23e2SCameron Grant 53249c5e6e2SCameron Grant snd_mtxfree(d->lock); 53333dbf14aSCameron Grant return 0; 53433dbf14aSCameron Grant } 5357c438dbeSCameron Grant 53633dbf14aSCameron Grant static moduledata_t sndpcm_mod = { 53733dbf14aSCameron Grant "snd_pcm", 538d95502a8SCameron Grant NULL, 53933dbf14aSCameron Grant NULL 54033dbf14aSCameron Grant }; 54133dbf14aSCameron Grant DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 54233dbf14aSCameron Grant MODULE_VERSION(snd_pcm, PCM_MODVER); 543