1098ca2bdSWarner Losh /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 490da2b28SAriff Abdullah * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 590da2b28SAriff Abdullah * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 690da2b28SAriff Abdullah * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> 740ac6e17SJoel Dahl * Copyright (c) 1997 Luigi Rizzo 8987e5972SCameron Grant * All rights reserved. 9987e5972SCameron Grant * 10987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without 11987e5972SCameron Grant * modification, are permitted provided that the following conditions 12987e5972SCameron Grant * are met: 13987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright 14987e5972SCameron Grant * notice, this list of conditions and the following disclaimer. 15987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 16987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the 17987e5972SCameron Grant * documentation and/or other materials provided with the distribution. 18987e5972SCameron Grant * 19987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29987e5972SCameron Grant * SUCH DAMAGE. 30987e5972SCameron Grant */ 31987e5972SCameron Grant 3290da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 3390da2b28SAriff Abdullah #include "opt_snd.h" 3490da2b28SAriff Abdullah #endif 3590da2b28SAriff Abdullah 36ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 37a580b31aSAriff Abdullah #include <dev/sound/pcm/ac97.h> 38285648f9SCameron Grant #include <dev/sound/pcm/vchan.h> 395ee30e27SMathew Kanner #include <dev/sound/pcm/dsp.h> 40bba4862cSAriff Abdullah #include <dev/sound/version.h> 41b611c801SAlexander Leidinger #include <sys/limits.h> 427c438dbeSCameron Grant #include <sys/sysctl.h> 43285648f9SCameron Grant 4467b1dce3SCameron Grant #include "feeder_if.h" 4567b1dce3SCameron Grant 46d95502a8SCameron Grant devclass_t pcm_devclass; 4782db23e2SCameron Grant 48b8a36395SCameron Grant int pcm_veto_load = 1; 49b8a36395SCameron Grant 50f3685841SAriff Abdullah int snd_unit = -1; 51cbe7d6a3SCameron Grant 52cbebc90dSAlexander Motin static int snd_unit_auto = -1; 53af3b2549SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN, 54838d3589SAriff Abdullah &snd_unit_auto, 0, "assign default unit to a newly attached device"); 55ad8612b9SAriff Abdullah 56bba4862cSAriff Abdullah int snd_maxautovchans = 16; 57987e5972SCameron Grant 587029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 597029da5cSPawel Biernacki "Sound driver"); 6082db23e2SCameron Grant 6132a0e5d5SHans Petter Selasky static void pcm_sysinit(device_t); 6232a0e5d5SHans Petter Selasky 63bba4862cSAriff Abdullah /* 64bba4862cSAriff Abdullah * XXX I've had enough with people not telling proper version/arch 65bba4862cSAriff Abdullah * while reporting problems, not after 387397913213th questions/requests. 66bba4862cSAriff Abdullah */ 67a9bdb5d3SAndriy Gapon static char snd_driver_version[] = 68bba4862cSAriff Abdullah __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; 69bba4862cSAriff Abdullah SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, 7090da2b28SAriff Abdullah 0, "driver version/arch"); 71bba4862cSAriff Abdullah 72b611c801SAlexander Leidinger /** 73b611c801SAlexander Leidinger * @brief Unit number allocator for syncgroup IDs 74b611c801SAlexander Leidinger */ 75b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL; 76b611c801SAlexander Leidinger 7737209180SCameron Grant void * 782c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type) 7937209180SCameron Grant { 8037209180SCameron Grant struct mtx *m; 8137209180SCameron Grant 82a163d034SWarner Losh m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 8312e524a2SDon Lewis mtx_init(m, desc, type, MTX_DEF); 8412e524a2SDon Lewis return m; 8512e524a2SDon Lewis } 8612e524a2SDon Lewis 8737209180SCameron Grant void 8837209180SCameron Grant snd_mtxfree(void *m) 8937209180SCameron Grant { 9037209180SCameron Grant struct mtx *mtx = m; 9137209180SCameron Grant 9237209180SCameron Grant mtx_destroy(mtx); 9337209180SCameron Grant free(mtx, M_DEVBUF); 9437209180SCameron Grant } 9537209180SCameron Grant 9637209180SCameron Grant void 9737209180SCameron Grant snd_mtxassert(void *m) 9837209180SCameron Grant { 99f00f162aSCameron Grant #ifdef INVARIANTS 10037209180SCameron Grant struct mtx *mtx = m; 10137209180SCameron Grant 10237209180SCameron Grant mtx_assert(mtx, MA_OWNED); 10337209180SCameron Grant #endif 10437209180SCameron Grant } 10537209180SCameron Grant 10637209180SCameron Grant int 10737209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 10837209180SCameron Grant { 109e4e61333SAriff Abdullah struct snddev_info *d; 11090da2b28SAriff Abdullah 11137209180SCameron Grant flags &= INTR_MPSAFE; 11246700f12SPeter Wemm flags |= INTR_TYPE_AV; 113e4e61333SAriff Abdullah d = device_get_softc(dev); 114e4e61333SAriff Abdullah if (d != NULL && (flags & INTR_MPSAFE)) 115e4e61333SAriff Abdullah d->flags |= SD_F_MPSAFE; 116e4e61333SAriff Abdullah 117775fcb6eSBaptiste Daroussin return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep); 11837209180SCameron Grant } 11937209180SCameron Grant 12090da2b28SAriff Abdullah int 121bba4862cSAriff Abdullah pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) 122bba4862cSAriff Abdullah { 123bba4862cSAriff Abdullah struct pcm_channel *c, *ch, *nch; 12490da2b28SAriff Abdullah struct pcmchan_caps *caps; 12590da2b28SAriff Abdullah int i, err, vcnt; 126bba4862cSAriff Abdullah 127e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 128a1d444e1SAriff Abdullah 129bba4862cSAriff Abdullah if ((direction == PCMDIR_PLAY && d->playcount < 1) || 130e4e61333SAriff Abdullah (direction == PCMDIR_REC && d->reccount < 1)) 131e4e61333SAriff Abdullah return (ENODEV); 132a580b31aSAriff Abdullah 133e4e61333SAriff Abdullah if (!(d->flags & SD_F_AUTOVCHAN)) 134e4e61333SAriff Abdullah return (EINVAL); 135a1d444e1SAriff Abdullah 136e4e61333SAriff Abdullah if (newcnt < 0 || newcnt > SND_MAXVCHANS) 137e4e61333SAriff Abdullah return (E2BIG); 138a1d444e1SAriff Abdullah 139bba4862cSAriff Abdullah if (direction == PCMDIR_PLAY) 140bba4862cSAriff Abdullah vcnt = d->pvchancount; 141bba4862cSAriff Abdullah else if (direction == PCMDIR_REC) 142bba4862cSAriff Abdullah vcnt = d->rvchancount; 143e4e61333SAriff Abdullah else 144e4e61333SAriff Abdullah return (EINVAL); 145a1d444e1SAriff Abdullah 146a1d444e1SAriff Abdullah if (newcnt > vcnt) { 147bba4862cSAriff Abdullah KASSERT(num == -1 || 148bba4862cSAriff Abdullah (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), 149bba4862cSAriff Abdullah ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", 150bba4862cSAriff Abdullah num, newcnt, vcnt)); 151a1d444e1SAriff Abdullah /* add new vchans - find a parent channel first */ 152e4e61333SAriff Abdullah ch = NULL; 153bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 154a1d444e1SAriff Abdullah CHN_LOCK(c); 155bba4862cSAriff Abdullah if (c->direction == direction && 156bba4862cSAriff Abdullah ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 15790da2b28SAriff Abdullah c->refcount < 1 && 158e4e61333SAriff Abdullah !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 15990da2b28SAriff Abdullah /* 16090da2b28SAriff Abdullah * Reuse hw channel with vchans already 16190da2b28SAriff Abdullah * created. 16290da2b28SAriff Abdullah */ 16390da2b28SAriff Abdullah if (c->flags & CHN_F_HAS_VCHAN) { 164e4e61333SAriff Abdullah ch = c; 165e4e61333SAriff Abdullah break; 166e4e61333SAriff Abdullah } 16790da2b28SAriff Abdullah /* 16890da2b28SAriff Abdullah * No vchans ever created, look for 16990da2b28SAriff Abdullah * channels with supported formats. 17090da2b28SAriff Abdullah */ 17190da2b28SAriff Abdullah caps = chn_getcaps(c); 17290da2b28SAriff Abdullah if (caps == NULL) { 17390da2b28SAriff Abdullah CHN_UNLOCK(c); 17490da2b28SAriff Abdullah continue; 17590da2b28SAriff Abdullah } 17690da2b28SAriff Abdullah for (i = 0; caps->fmtlist[i] != 0; i++) { 17790da2b28SAriff Abdullah if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 17890da2b28SAriff Abdullah break; 17990da2b28SAriff Abdullah } 18090da2b28SAriff Abdullah if (caps->fmtlist[i] != 0) { 18190da2b28SAriff Abdullah ch = c; 18290da2b28SAriff Abdullah break; 18390da2b28SAriff Abdullah } 18490da2b28SAriff Abdullah } 185a1d444e1SAriff Abdullah CHN_UNLOCK(c); 186a1d444e1SAriff Abdullah } 187e4e61333SAriff Abdullah if (ch == NULL) 188e4e61333SAriff Abdullah return (EBUSY); 189e4e61333SAriff Abdullah ch->flags |= CHN_F_BUSY; 190e4e61333SAriff Abdullah err = 0; 191a1d444e1SAriff Abdullah while (err == 0 && newcnt > vcnt) { 192e4e61333SAriff Abdullah err = vchan_create(ch, num); 193bba4862cSAriff Abdullah if (err == 0) 194a1d444e1SAriff Abdullah vcnt++; 195bba4862cSAriff Abdullah else if (err == E2BIG && newcnt > vcnt) 196bba4862cSAriff Abdullah device_printf(d->dev, 197bba4862cSAriff Abdullah "%s: err=%d Maximum channel reached.\n", 198bba4862cSAriff Abdullah __func__, err); 199a1d444e1SAriff Abdullah } 200a1d444e1SAriff Abdullah if (vcnt == 0) 201e4e61333SAriff Abdullah ch->flags &= ~CHN_F_BUSY; 202e4e61333SAriff Abdullah CHN_UNLOCK(ch); 203e4e61333SAriff Abdullah if (err != 0) 204e4e61333SAriff Abdullah return (err); 205a1d444e1SAriff Abdullah } else if (newcnt < vcnt) { 206bba4862cSAriff Abdullah KASSERT(num == -1, 207bba4862cSAriff Abdullah ("bogus vchan_destroy() request num=%d", num)); 208bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 209a1d444e1SAriff Abdullah CHN_LOCK(c); 210bba4862cSAriff Abdullah if (c->direction != direction || 211bba4862cSAriff Abdullah CHN_EMPTY(c, children) || 212bba4862cSAriff Abdullah !(c->flags & CHN_F_HAS_VCHAN)) { 213a1d444e1SAriff Abdullah CHN_UNLOCK(c); 214bba4862cSAriff Abdullah continue; 215a1d444e1SAriff Abdullah } 216bba4862cSAriff Abdullah CHN_FOREACH_SAFE(ch, c, nch, children) { 217bba4862cSAriff Abdullah CHN_LOCK(ch); 21890da2b28SAriff Abdullah if (vcnt == 1 && c->refcount > 0) { 219bba4862cSAriff Abdullah CHN_UNLOCK(ch); 22090da2b28SAriff Abdullah break; 22190da2b28SAriff Abdullah } 22290da2b28SAriff Abdullah if (!(ch->flags & CHN_F_BUSY) && 22390da2b28SAriff Abdullah ch->refcount < 1) { 224bba4862cSAriff Abdullah err = vchan_destroy(ch); 225a1d444e1SAriff Abdullah if (err == 0) 226a1d444e1SAriff Abdullah vcnt--; 227bba4862cSAriff Abdullah } else 228bba4862cSAriff Abdullah CHN_UNLOCK(ch); 229e4e61333SAriff Abdullah if (vcnt == newcnt) 230bba4862cSAriff Abdullah break; 231a1d444e1SAriff Abdullah } 232bba4862cSAriff Abdullah CHN_UNLOCK(c); 233bba4862cSAriff Abdullah break; 234bba4862cSAriff Abdullah } 235bba4862cSAriff Abdullah } 236a1d444e1SAriff Abdullah 237e4e61333SAriff Abdullah return (0); 238a1d444e1SAriff Abdullah } 239a1d444e1SAriff Abdullah 2403fdb3676SAriff Abdullah /* return error status and a locked channel */ 2413fdb3676SAriff Abdullah int 2423fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 24390da2b28SAriff Abdullah pid_t pid, char *comm, int devunit) 244285648f9SCameron Grant { 245285648f9SCameron Grant struct pcm_channel *c; 24690da2b28SAriff Abdullah int err, vchancount, vchan_num; 247bba4862cSAriff Abdullah 248bba4862cSAriff Abdullah KASSERT(d != NULL && ch != NULL && (devunit == -1 || 249bba4862cSAriff Abdullah !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && 250bba4862cSAriff Abdullah (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 251e4e61333SAriff Abdullah ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d", 252bba4862cSAriff Abdullah __func__, d, ch, direction, pid, devunit)); 253e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 254bba4862cSAriff Abdullah 255bba4862cSAriff Abdullah /* Double check again. */ 256bba4862cSAriff Abdullah if (devunit != -1) { 257bba4862cSAriff Abdullah switch (snd_unit2d(devunit)) { 258bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 259bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 260bba4862cSAriff Abdullah if (direction != PCMDIR_PLAY) 26190da2b28SAriff Abdullah return (ENOTSUP); 262bba4862cSAriff Abdullah break; 263bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 264bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 265bba4862cSAriff Abdullah if (direction != PCMDIR_REC) 26690da2b28SAriff Abdullah return (ENOTSUP); 267bba4862cSAriff Abdullah break; 268bba4862cSAriff Abdullah default: 269bba4862cSAriff Abdullah if (!(direction == PCMDIR_PLAY || 270bba4862cSAriff Abdullah direction == PCMDIR_REC)) 27190da2b28SAriff Abdullah return (ENOTSUP); 272bba4862cSAriff Abdullah break; 273bba4862cSAriff Abdullah } 274bba4862cSAriff Abdullah } 275285648f9SCameron Grant 27690da2b28SAriff Abdullah *ch = NULL; 27790da2b28SAriff Abdullah vchan_num = 0; 27890da2b28SAriff Abdullah vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : 27990da2b28SAriff Abdullah d->rvchancount; 28090da2b28SAriff Abdullah 2813fdb3676SAriff Abdullah retry_chnalloc: 28290da2b28SAriff Abdullah err = ENOTSUP; 283f637a36cSCameron Grant /* scan for a free channel */ 284bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 28549c5e6e2SCameron Grant CHN_LOCK(c); 28690da2b28SAriff Abdullah if (devunit == -1 && c->direction == direction && 28790da2b28SAriff Abdullah (c->flags & CHN_F_VIRTUAL)) { 28890da2b28SAriff Abdullah if (vchancount < snd_maxautovchans && 28990da2b28SAriff Abdullah vchan_num < CHN_CHAN(c)) { 29090da2b28SAriff Abdullah CHN_UNLOCK(c); 29190da2b28SAriff Abdullah goto vchan_alloc; 29290da2b28SAriff Abdullah } 29390da2b28SAriff Abdullah vchan_num++; 29490da2b28SAriff Abdullah } 295bba4862cSAriff Abdullah if (c->direction == direction && !(c->flags & CHN_F_BUSY) && 296bba4862cSAriff Abdullah (devunit == -1 || devunit == -2 || c->unit == devunit)) { 297285648f9SCameron Grant c->flags |= CHN_F_BUSY; 298b8f0d9e0SCameron Grant c->pid = pid; 29990da2b28SAriff Abdullah strlcpy(c->comm, (comm != NULL) ? comm : 30090da2b28SAriff Abdullah CHN_COMM_UNKNOWN, sizeof(c->comm)); 3013fdb3676SAriff Abdullah *ch = c; 302bba4862cSAriff Abdullah return (0); 303bba4862cSAriff Abdullah } else if (c->unit == devunit) { 3043fdb3676SAriff Abdullah if (c->direction != direction) 30590da2b28SAriff Abdullah err = ENOTSUP; 3063fdb3676SAriff Abdullah else if (c->flags & CHN_F_BUSY) 3073fdb3676SAriff Abdullah err = EBUSY; 3083fdb3676SAriff Abdullah else 3093fdb3676SAriff Abdullah err = EINVAL; 3103fdb3676SAriff Abdullah CHN_UNLOCK(c); 311bba4862cSAriff Abdullah return (err); 312bba4862cSAriff Abdullah } else if ((devunit == -1 || devunit == -2) && 313bba4862cSAriff Abdullah c->direction == direction && (c->flags & CHN_F_BUSY)) 314a1d444e1SAriff Abdullah err = EBUSY; 31549c5e6e2SCameron Grant CHN_UNLOCK(c); 316285648f9SCameron Grant } 317f637a36cSCameron Grant 318e4e61333SAriff Abdullah if (devunit == -2) 319e4e61333SAriff Abdullah return (err); 320e4e61333SAriff Abdullah 32190da2b28SAriff Abdullah vchan_alloc: 322f637a36cSCameron Grant /* no channel available */ 323e4e61333SAriff Abdullah if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || 324e4e61333SAriff Abdullah snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { 325bba4862cSAriff Abdullah if (!(vchancount > 0 && vchancount < snd_maxautovchans) && 326bba4862cSAriff Abdullah (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) 327bba4862cSAriff Abdullah return (err); 328bba4862cSAriff Abdullah err = pcm_setvchans(d, direction, vchancount + 1, 329bba4862cSAriff Abdullah (devunit == -1) ? -1 : snd_unit2c(devunit)); 330a1d444e1SAriff Abdullah if (err == 0) { 331bba4862cSAriff Abdullah if (devunit == -1) 332bba4862cSAriff Abdullah devunit = -2; 3333fdb3676SAriff Abdullah goto retry_chnalloc; 334f637a36cSCameron Grant } 335f637a36cSCameron Grant } 336f637a36cSCameron Grant 337bba4862cSAriff Abdullah return (err); 338285648f9SCameron Grant } 339285648f9SCameron Grant 340b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 341285648f9SCameron Grant int 342b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 343285648f9SCameron Grant { 344e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 345b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 346e4e61333SAriff Abdullah 347285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 348b8f0d9e0SCameron Grant c->pid = -1; 34990da2b28SAriff Abdullah strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm)); 35049c5e6e2SCameron Grant CHN_UNLOCK(c); 351e4e61333SAriff Abdullah 352e4e61333SAriff Abdullah return (0); 353285648f9SCameron Grant } 354285648f9SCameron Grant 355285648f9SCameron Grant int 356285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 357285648f9SCameron Grant { 358e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 359b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 360e4e61333SAriff Abdullah 361285648f9SCameron Grant c->refcount += ref; 362e4e61333SAriff Abdullah 363e4e61333SAriff Abdullah return (c->refcount); 364285648f9SCameron Grant } 365285648f9SCameron Grant 36667b1dce3SCameron Grant static void 36767b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 36867b1dce3SCameron Grant { 369e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 370e4e61333SAriff Abdullah 371bba4862cSAriff Abdullah if (num < 0) 372bba4862cSAriff Abdullah return; 373bba4862cSAriff Abdullah 374bba4862cSAriff Abdullah if (num >= 0 && d->pvchancount > num) 375bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); 376bba4862cSAriff Abdullah else if (num > 0 && d->pvchancount == 0) 377bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); 378bba4862cSAriff Abdullah 379bba4862cSAriff Abdullah if (num >= 0 && d->rvchancount > num) 380bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, num, -1); 381bba4862cSAriff Abdullah else if (num > 0 && d->rvchancount == 0) 382bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); 38367b1dce3SCameron Grant } 38467b1dce3SCameron Grant 38533dbf14aSCameron Grant static int 386851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 38733dbf14aSCameron Grant { 388b8f0d9e0SCameron Grant struct snddev_info *d; 38933dbf14aSCameron Grant int error, unit; 39033dbf14aSCameron Grant 39133dbf14aSCameron Grant unit = snd_unit; 392041b706bSDavid Malone error = sysctl_handle_int(oidp, &unit, 0, req); 39333dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 394b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 395e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 396b8f0d9e0SCameron Grant return EINVAL; 39733dbf14aSCameron Grant snd_unit = unit; 398cbebc90dSAlexander Motin snd_unit_auto = 0; 39933dbf14aSCameron Grant } 40033dbf14aSCameron Grant return (error); 40133dbf14aSCameron Grant } 402851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */ 403b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, 4047029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0, 4057029da5cSPawel Biernacki sizeof(int), sysctl_hw_snd_default_unit, "I", 406b7d6c6b5SEitan Adler "default sound device"); 407987e5972SCameron Grant 408cd9766c5SCameron Grant static int 40967b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 410cd9766c5SCameron Grant { 41167b1dce3SCameron Grant struct snddev_info *d; 41267b1dce3SCameron Grant int i, v, error; 413cd9766c5SCameron Grant 41467b1dce3SCameron Grant v = snd_maxautovchans; 415041b706bSDavid Malone error = sysctl_handle_int(oidp, &v, 0, req); 416cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 417bba4862cSAriff Abdullah if (v < 0) 418bba4862cSAriff Abdullah v = 0; 419bba4862cSAriff Abdullah if (v > SND_MAXVCHANS) 420bba4862cSAriff Abdullah v = SND_MAXVCHANS; 421bba4862cSAriff Abdullah snd_maxautovchans = v; 4229c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 4239c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 42467b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 425e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 42667b1dce3SCameron Grant continue; 427e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 42867b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 429e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 43067b1dce3SCameron Grant } 43167b1dce3SCameron Grant } 432cd9766c5SCameron Grant return (error); 433cd9766c5SCameron Grant } 4347029da5cSPawel Biernacki SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, 4357029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 4367029da5cSPawel Biernacki sysctl_hw_snd_maxautovchans, "I", 4377029da5cSPawel Biernacki "maximum virtual channel"); 438f637a36cSCameron Grant 439285648f9SCameron Grant struct pcm_channel * 440bba4862cSAriff Abdullah pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo) 441987e5972SCameron Grant { 442bba4862cSAriff Abdullah struct pcm_channel *ch; 443bba4862cSAriff Abdullah int direction, err, rpnum, *pnum, max; 444bba4862cSAriff Abdullah int udc, device, chan; 445bba4862cSAriff Abdullah char *dirs, *devname, buf[CHN_NAMELEN]; 446bba4862cSAriff Abdullah 447e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 44890da2b28SAriff Abdullah PCM_LOCKASSERT(d); 449bba4862cSAriff Abdullah KASSERT(num >= -1, ("invalid num=%d", num)); 450bba4862cSAriff Abdullah 451285648f9SCameron Grant switch (dir) { 452285648f9SCameron Grant case PCMDIR_PLAY: 453285648f9SCameron Grant dirs = "play"; 454a3193a9cSDon Lewis direction = PCMDIR_PLAY; 455a67fe5c1SCameron Grant pnum = &d->playcount; 456bba4862cSAriff Abdullah device = SND_DEV_DSPHW_PLAY; 457bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 458285648f9SCameron Grant break; 459bba4862cSAriff Abdullah case PCMDIR_PLAY_VIRTUAL: 460bba4862cSAriff Abdullah dirs = "virtual"; 461bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 462bba4862cSAriff Abdullah pnum = &d->pvchancount; 463bba4862cSAriff Abdullah device = SND_DEV_DSPHW_VPLAY; 464bba4862cSAriff Abdullah max = SND_MAXVCHANS; 465bba4862cSAriff Abdullah break; 466285648f9SCameron Grant case PCMDIR_REC: 467285648f9SCameron Grant dirs = "record"; 468a3193a9cSDon Lewis direction = PCMDIR_REC; 469a67fe5c1SCameron Grant pnum = &d->reccount; 470bba4862cSAriff Abdullah device = SND_DEV_DSPHW_REC; 471bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 472285648f9SCameron Grant break; 473bba4862cSAriff Abdullah case PCMDIR_REC_VIRTUAL: 474285648f9SCameron Grant dirs = "virtual"; 475bba4862cSAriff Abdullah direction = PCMDIR_REC; 476bba4862cSAriff Abdullah pnum = &d->rvchancount; 477bba4862cSAriff Abdullah device = SND_DEV_DSPHW_VREC; 478bba4862cSAriff Abdullah max = SND_MAXVCHANS; 479285648f9SCameron Grant break; 480285648f9SCameron Grant default: 481e4e61333SAriff Abdullah return (NULL); 4829c326820SCameron Grant } 483285648f9SCameron Grant 484bba4862cSAriff Abdullah chan = (num == -1) ? 0 : num; 485285648f9SCameron Grant 486e4e61333SAriff Abdullah if (*pnum >= max || chan >= max) 487e4e61333SAriff Abdullah return (NULL); 488bba4862cSAriff Abdullah 4893fdb3676SAriff Abdullah rpnum = 0; 490bba4862cSAriff Abdullah 491bba4862cSAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 492bba4862cSAriff Abdullah if (CHN_DEV(ch) != device) 4933fdb3676SAriff Abdullah continue; 494bba4862cSAriff Abdullah if (chan == CHN_CHAN(ch)) { 495bba4862cSAriff Abdullah if (num != -1) { 4963fdb3676SAriff Abdullah device_printf(d->dev, 497bba4862cSAriff Abdullah "channel num=%d allocated!\n", chan); 498e4e61333SAriff Abdullah return (NULL); 499bba4862cSAriff Abdullah } 500bba4862cSAriff Abdullah chan++; 501bba4862cSAriff Abdullah if (chan >= max) { 502bba4862cSAriff Abdullah device_printf(d->dev, 503bba4862cSAriff Abdullah "chan=%d > %d\n", chan, max); 504e4e61333SAriff Abdullah return (NULL); 505bba4862cSAriff Abdullah } 5063fdb3676SAriff Abdullah } 5073fdb3676SAriff Abdullah rpnum++; 5083fdb3676SAriff Abdullah } 509bba4862cSAriff Abdullah 5103fdb3676SAriff Abdullah if (*pnum != rpnum) { 5113fdb3676SAriff Abdullah device_printf(d->dev, 512bba4862cSAriff Abdullah "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", 5133fdb3676SAriff Abdullah __func__, dirs, *pnum, rpnum); 514e4e61333SAriff Abdullah return (NULL); 5153fdb3676SAriff Abdullah } 516a67fe5c1SCameron Grant 517bba4862cSAriff Abdullah udc = snd_mkunit(device_get_unit(d->dev), device, chan); 518bba4862cSAriff Abdullah devname = dsp_unit2name(buf, sizeof(buf), udc); 519bba4862cSAriff Abdullah 520bba4862cSAriff Abdullah if (devname == NULL) { 521bba4862cSAriff Abdullah device_printf(d->dev, 522bba4862cSAriff Abdullah "Failed to query device name udc=0x%08x\n", udc); 523e4e61333SAriff Abdullah return (NULL); 524bba4862cSAriff Abdullah } 525bba4862cSAriff Abdullah 52690da2b28SAriff Abdullah PCM_UNLOCK(d); 527bba4862cSAriff Abdullah ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 528bba4862cSAriff Abdullah ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); 529bba4862cSAriff Abdullah ch->unit = udc; 530285648f9SCameron Grant ch->pid = -1; 53190da2b28SAriff Abdullah strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm)); 532285648f9SCameron Grant ch->parentsnddev = d; 533285648f9SCameron Grant ch->parentchannel = parent; 534436c9b65SScott Long ch->dev = d->dev; 535bba4862cSAriff Abdullah ch->trigger = PCMTRIG_STOP; 536bba4862cSAriff Abdullah snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", 537bba4862cSAriff Abdullah device_get_nameunit(ch->dev), dirs, devname); 538285648f9SCameron Grant 539a3193a9cSDon Lewis err = chn_init(ch, devinfo, dir, direction); 54090da2b28SAriff Abdullah PCM_LOCK(d); 5410f55ac6cSCameron Grant if (err) { 542bba4862cSAriff Abdullah device_printf(d->dev, "chn_init(%s) failed: err = %d\n", 543bba4862cSAriff Abdullah ch->name, err); 544285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 545285648f9SCameron Grant free(ch, M_DEVBUF); 546e4e61333SAriff Abdullah return (NULL); 547bbb5bf3dSCameron Grant } 548285648f9SCameron Grant 549e4e61333SAriff Abdullah return (ch); 550285648f9SCameron Grant } 551285648f9SCameron Grant 552285648f9SCameron Grant int 553285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 554285648f9SCameron Grant { 555cf8e3ea2SMateusz Guzik struct snddev_info *d __diagused; 556285648f9SCameron Grant int err; 557285648f9SCameron Grant 558a67fe5c1SCameron Grant d = ch->parentsnddev; 559e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 560e4e61333SAriff Abdullah 561285648f9SCameron Grant err = chn_kill(ch); 562285648f9SCameron Grant if (err) { 563e4e61333SAriff Abdullah device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n", 564e4e61333SAriff Abdullah ch->name, err); 565e4e61333SAriff Abdullah return (err); 566285648f9SCameron Grant } 567285648f9SCameron Grant 568285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 569285648f9SCameron Grant free(ch, M_DEVBUF); 570285648f9SCameron Grant 571e4e61333SAriff Abdullah return (0); 572285648f9SCameron Grant } 573285648f9SCameron Grant 574285648f9SCameron Grant int 5755ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 576285648f9SCameron Grant { 577e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 57890da2b28SAriff Abdullah PCM_LOCKASSERT(d); 579bba4862cSAriff Abdullah KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 580bba4862cSAriff Abdullah ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 581285648f9SCameron Grant 58290da2b28SAriff Abdullah CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); 5833fdb3676SAriff Abdullah 584e4e61333SAriff Abdullah switch (CHN_DEV(ch)) { 585e4e61333SAriff Abdullah case SND_DEV_DSPHW_PLAY: 586e4e61333SAriff Abdullah d->playcount++; 587e4e61333SAriff Abdullah break; 588e4e61333SAriff Abdullah case SND_DEV_DSPHW_VPLAY: 589e4e61333SAriff Abdullah d->pvchancount++; 590e4e61333SAriff Abdullah break; 591e4e61333SAriff Abdullah case SND_DEV_DSPHW_REC: 592e4e61333SAriff Abdullah d->reccount++; 593e4e61333SAriff Abdullah break; 594e4e61333SAriff Abdullah case SND_DEV_DSPHW_VREC: 595e4e61333SAriff Abdullah d->rvchancount++; 596e4e61333SAriff Abdullah break; 597e4e61333SAriff Abdullah default: 598e4e61333SAriff Abdullah break; 599e4e61333SAriff Abdullah } 600b8f0d9e0SCameron Grant 601e4e61333SAriff Abdullah d->devcount++; 602e4e61333SAriff Abdullah 603e4e61333SAriff Abdullah return (0); 60433dbf14aSCameron Grant } 60533dbf14aSCameron Grant 606285648f9SCameron Grant int 6075ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 60833dbf14aSCameron Grant { 609bba4862cSAriff Abdullah struct pcm_channel *tmp; 61033dbf14aSCameron Grant 611e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 61290da2b28SAriff Abdullah PCM_LOCKASSERT(d); 613e4e61333SAriff Abdullah 614bba4862cSAriff Abdullah tmp = NULL; 615a527dbc7SCameron Grant 616bba4862cSAriff Abdullah CHN_FOREACH(tmp, d, channels.pcm) { 617bba4862cSAriff Abdullah if (tmp == ch) 618bba4862cSAriff Abdullah break; 61933dbf14aSCameron Grant } 620bba4862cSAriff Abdullah 621bba4862cSAriff Abdullah if (tmp != ch) 622e4e61333SAriff Abdullah return (EINVAL); 62367beb5a5SCameron Grant 624bba4862cSAriff Abdullah CHN_REMOVE(d, ch, channels.pcm); 625e4e61333SAriff Abdullah 626bba4862cSAriff Abdullah switch (CHN_DEV(ch)) { 627bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 62867beb5a5SCameron Grant d->playcount--; 629bba4862cSAriff Abdullah break; 630bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 631bba4862cSAriff Abdullah d->pvchancount--; 632bba4862cSAriff Abdullah break; 633bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 634bba4862cSAriff Abdullah d->reccount--; 635bba4862cSAriff Abdullah break; 636bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 637bba4862cSAriff Abdullah d->rvchancount--; 638bba4862cSAriff Abdullah break; 639bba4862cSAriff Abdullah default: 640bba4862cSAriff Abdullah break; 641bba4862cSAriff Abdullah } 642285648f9SCameron Grant 643e4e61333SAriff Abdullah d->devcount--; 644e4e61333SAriff Abdullah 645e4e61333SAriff Abdullah return (0); 646987e5972SCameron Grant } 647987e5972SCameron Grant 648987e5972SCameron Grant int 649285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 650285648f9SCameron Grant { 651285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 65267b1dce3SCameron Grant struct pcm_channel *ch; 65367b1dce3SCameron Grant int err; 654285648f9SCameron Grant 655e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 656e4e61333SAriff Abdullah 65790da2b28SAriff Abdullah PCM_LOCK(d); 658bba4862cSAriff Abdullah ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); 659285648f9SCameron Grant if (!ch) { 660e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", 661e4e61333SAriff Abdullah cls->name, dir, devinfo); 66290da2b28SAriff Abdullah PCM_UNLOCK(d); 663e4e61333SAriff Abdullah return (ENODEV); 664285648f9SCameron Grant } 665cd9766c5SCameron Grant 6665ee30e27SMathew Kanner err = pcm_chn_add(d, ch); 66790da2b28SAriff Abdullah PCM_UNLOCK(d); 668285648f9SCameron Grant if (err) { 669e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", 670e4e61333SAriff Abdullah ch->name, err); 671285648f9SCameron Grant pcm_chn_destroy(ch); 672cd9766c5SCameron Grant } 673cd9766c5SCameron Grant 674e4e61333SAriff Abdullah return (err); 675285648f9SCameron Grant } 676285648f9SCameron Grant 677285648f9SCameron Grant static int 678285648f9SCameron Grant pcm_killchan(device_t dev) 679285648f9SCameron Grant { 680285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 681e33bee07SOlivier Houchard struct pcm_channel *ch; 682e4e61333SAriff Abdullah int error; 683e4e61333SAriff Abdullah 684e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 685285648f9SCameron Grant 686bba4862cSAriff Abdullah ch = CHN_FIRST(d, channels.pcm); 687285648f9SCameron Grant 68890da2b28SAriff Abdullah PCM_LOCK(d); 689bba4862cSAriff Abdullah error = pcm_chn_remove(d, ch); 69090da2b28SAriff Abdullah PCM_UNLOCK(d); 691e33bee07SOlivier Houchard if (error) 692e33bee07SOlivier Houchard return (error); 693e33bee07SOlivier Houchard return (pcm_chn_destroy(ch)); 694285648f9SCameron Grant } 695285648f9SCameron Grant 696cbebc90dSAlexander Motin static int 697cbebc90dSAlexander Motin pcm_best_unit(int old) 698cbebc90dSAlexander Motin { 699cbebc90dSAlexander Motin struct snddev_info *d; 700cbebc90dSAlexander Motin int i, best, bestprio, prio; 701cbebc90dSAlexander Motin 702cbebc90dSAlexander Motin best = -1; 703cbebc90dSAlexander Motin bestprio = -100; 704cbebc90dSAlexander Motin for (i = 0; pcm_devclass != NULL && 705cbebc90dSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 706cbebc90dSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 707cbebc90dSAlexander Motin if (!PCM_REGISTERED(d)) 708cbebc90dSAlexander Motin continue; 709cbebc90dSAlexander Motin prio = 0; 710cbebc90dSAlexander Motin if (d->playcount == 0) 711cbebc90dSAlexander Motin prio -= 10; 712cbebc90dSAlexander Motin if (d->reccount == 0) 713cbebc90dSAlexander Motin prio -= 2; 714cbebc90dSAlexander Motin if (prio > bestprio || (prio == bestprio && i == old)) { 715cbebc90dSAlexander Motin best = i; 716cbebc90dSAlexander Motin bestprio = prio; 717cbebc90dSAlexander Motin } 718cbebc90dSAlexander Motin } 719cbebc90dSAlexander Motin return (best); 720cbebc90dSAlexander Motin } 721cbebc90dSAlexander Motin 722285648f9SCameron Grant int 723987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 724987e5972SCameron Grant { 72566ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 72649c5e6e2SCameron Grant 72732a0e5d5SHans Petter Selasky /* should only be called once */ 72832a0e5d5SHans Petter Selasky if (d->flags & SD_F_REGISTERED) 72932a0e5d5SHans Petter Selasky return (EINVAL); 73032a0e5d5SHans Petter Selasky 731e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 732bba4862cSAriff Abdullah 733e4e61333SAriff Abdullah if (d->playcount == 0 || d->reccount == 0) 734e4e61333SAriff Abdullah d->flags |= SD_F_SIMPLEX; 735e4e61333SAriff Abdullah 73632a0e5d5SHans Petter Selasky if (d->playcount > 0 || d->reccount > 0) 737e4e61333SAriff Abdullah d->flags |= SD_F_AUTOVCHAN; 738e4e61333SAriff Abdullah 739e4e61333SAriff Abdullah pcm_setmaxautovchans(d, snd_maxautovchans); 740bba4862cSAriff Abdullah 741a580b31aSAriff Abdullah strlcpy(d->status, str, SND_STATUSLEN); 742bba4862cSAriff Abdullah 74390da2b28SAriff Abdullah PCM_LOCK(d); 744e4e61333SAriff Abdullah 745e4e61333SAriff Abdullah /* Done, we're ready.. */ 746e4e61333SAriff Abdullah d->flags |= SD_F_REGISTERED; 747e4e61333SAriff Abdullah 748e4e61333SAriff Abdullah PCM_RELEASE(d); 749bba4862cSAriff Abdullah 75090da2b28SAriff Abdullah PCM_UNLOCK(d); 751bba4862cSAriff Abdullah 75232a0e5d5SHans Petter Selasky /* 75332a0e5d5SHans Petter Selasky * Create all sysctls once SD_F_REGISTERED is set else 75432a0e5d5SHans Petter Selasky * tunable sysctls won't work: 75532a0e5d5SHans Petter Selasky */ 75632a0e5d5SHans Petter Selasky pcm_sysinit(dev); 75732a0e5d5SHans Petter Selasky 758cbebc90dSAlexander Motin if (snd_unit_auto < 0) 759cbebc90dSAlexander Motin snd_unit_auto = (snd_unit < 0) ? 1 : 0; 760cbebc90dSAlexander Motin if (snd_unit < 0 || snd_unit_auto > 1) 761f3685841SAriff Abdullah snd_unit = device_get_unit(dev); 762cbebc90dSAlexander Motin else if (snd_unit_auto == 1) 763cbebc90dSAlexander Motin snd_unit = pcm_best_unit(snd_unit); 764f3685841SAriff Abdullah 765e4e61333SAriff Abdullah return (0); 766987e5972SCameron Grant } 767987e5972SCameron Grant 768a1d444e1SAriff Abdullah uint32_t 769987e5972SCameron Grant pcm_getflags(device_t dev) 770987e5972SCameron Grant { 77166ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 77249c5e6e2SCameron Grant 773987e5972SCameron Grant return d->flags; 774987e5972SCameron Grant } 775987e5972SCameron Grant 776987e5972SCameron Grant void 777a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val) 778987e5972SCameron Grant { 77966ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 780d95502a8SCameron Grant 781987e5972SCameron Grant d->flags = val; 782987e5972SCameron Grant } 783987e5972SCameron Grant 78439004e69SCameron Grant void * 78539004e69SCameron Grant pcm_getdevinfo(device_t dev) 78639004e69SCameron Grant { 78766ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 78849c5e6e2SCameron Grant 78939004e69SCameron Grant return d->devinfo; 79039004e69SCameron Grant } 79139004e69SCameron Grant 792a67fe5c1SCameron Grant unsigned int 793d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 794a67fe5c1SCameron Grant { 795a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 7964e60be34SCameron Grant int sz, x; 797a67fe5c1SCameron Grant 798a67fe5c1SCameron Grant sz = 0; 7994e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 8004e60be34SCameron Grant x = sz; 801d55d96f6SAlexander Leidinger RANGE(sz, minbufsz, maxbufsz); 8024e60be34SCameron Grant if (x != sz) 803d55d96f6SAlexander Leidinger device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 804d55d96f6SAlexander Leidinger x = minbufsz; 8054e60be34SCameron Grant while (x < sz) 8064e60be34SCameron Grant x <<= 1; 8074e60be34SCameron Grant if (x > sz) 8084e60be34SCameron Grant x >>= 1; 8094e60be34SCameron Grant if (x != sz) { 8104e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 8114e60be34SCameron Grant sz = x; 8124e60be34SCameron Grant } 8134e60be34SCameron Grant } else { 814a67fe5c1SCameron Grant sz = deflt; 8154e60be34SCameron Grant } 8164e60be34SCameron Grant 817a67fe5c1SCameron Grant d->bufsz = sz; 818a67fe5c1SCameron Grant 819a67fe5c1SCameron Grant return sz; 820a67fe5c1SCameron Grant } 821a67fe5c1SCameron Grant 82290da2b28SAriff Abdullah static int 82390da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 82490da2b28SAriff Abdullah { 82590da2b28SAriff Abdullah struct snddev_info *d; 82690da2b28SAriff Abdullah int err, val; 82790da2b28SAriff Abdullah 82890da2b28SAriff Abdullah d = oidp->oid_arg1; 82990da2b28SAriff Abdullah if (!PCM_REGISTERED(d)) 83090da2b28SAriff Abdullah return (ENODEV); 83190da2b28SAriff Abdullah 83290da2b28SAriff Abdullah PCM_LOCK(d); 83390da2b28SAriff Abdullah PCM_WAIT(d); 83490da2b28SAriff Abdullah val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 83590da2b28SAriff Abdullah PCM_ACQUIRE(d); 83690da2b28SAriff Abdullah PCM_UNLOCK(d); 83790da2b28SAriff Abdullah 83890da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &val, 0, req); 83990da2b28SAriff Abdullah 84090da2b28SAriff Abdullah if (err == 0 && req->newptr != NULL) { 84190da2b28SAriff Abdullah if (!(val == 0 || val == 1)) { 84290da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 84390da2b28SAriff Abdullah return (EINVAL); 84490da2b28SAriff Abdullah } 84590da2b28SAriff Abdullah 84690da2b28SAriff Abdullah PCM_LOCK(d); 84790da2b28SAriff Abdullah 84890da2b28SAriff Abdullah d->flags &= ~SD_F_BITPERFECT; 84990da2b28SAriff Abdullah d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 85090da2b28SAriff Abdullah 85190da2b28SAriff Abdullah PCM_RELEASE(d); 85290da2b28SAriff Abdullah PCM_UNLOCK(d); 85390da2b28SAriff Abdullah } else 85490da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 85590da2b28SAriff Abdullah 85690da2b28SAriff Abdullah return (err); 85790da2b28SAriff Abdullah } 85890da2b28SAriff Abdullah 859ed2196e5SHans Petter Selasky static u_int8_t 860ed2196e5SHans Petter Selasky pcm_mode_init(struct snddev_info *d) 861ed2196e5SHans Petter Selasky { 862ed2196e5SHans Petter Selasky u_int8_t mode = 0; 863ed2196e5SHans Petter Selasky 864ed2196e5SHans Petter Selasky if (d->playcount > 0) 865ed2196e5SHans Petter Selasky mode |= PCM_MODE_PLAY; 866ed2196e5SHans Petter Selasky if (d->reccount > 0) 867ed2196e5SHans Petter Selasky mode |= PCM_MODE_REC; 868ed2196e5SHans Petter Selasky if (d->mixer_dev != NULL) 869ed2196e5SHans Petter Selasky mode |= PCM_MODE_MIXER; 870ed2196e5SHans Petter Selasky 871ed2196e5SHans Petter Selasky return (mode); 872ed2196e5SHans Petter Selasky } 873ed2196e5SHans Petter Selasky 87432a0e5d5SHans Petter Selasky static void 87532a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev) 87632a0e5d5SHans Petter Selasky { 87732a0e5d5SHans Petter Selasky struct snddev_info *d = device_get_softc(dev); 878ed2196e5SHans Petter Selasky u_int8_t mode; 879ed2196e5SHans Petter Selasky 880ed2196e5SHans Petter Selasky mode = pcm_mode_init(d); 88132a0e5d5SHans Petter Selasky 882132fca63SHans Petter Selasky /* XXX: a user should be able to set this with a control tool, the 88332a0e5d5SHans Petter Selasky sysadmin then needs min+max sysctls for this */ 88432a0e5d5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 88532a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 88632a0e5d5SHans Petter Selasky OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 88732a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 88832a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 8893b4c5433SAlexander Motin "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, 8907029da5cSPawel Biernacki sizeof(d), sysctl_dev_pcm_bitperfect, "I", 89132a0e5d5SHans Petter Selasky "bit-perfect playback/recording (0=disable, 1=enable)"); 892ed2196e5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 893ed2196e5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 894ed2196e5SHans Petter Selasky OID_AUTO, "mode", CTLFLAG_RD, NULL, mode, 895ed2196e5SHans Petter Selasky "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than one" 896ed2196e5SHans Petter Selasky "mode is supported)"); 89732a0e5d5SHans Petter Selasky if (d->flags & SD_F_AUTOVCHAN) 89832a0e5d5SHans Petter Selasky vchan_initsys(dev); 89932a0e5d5SHans Petter Selasky if (d->flags & SD_F_EQ) 90032a0e5d5SHans Petter Selasky feeder_eq_initsys(dev); 90132a0e5d5SHans Petter Selasky } 90232a0e5d5SHans Petter Selasky 903987e5972SCameron Grant int 904987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 905987e5972SCameron Grant { 906bba4862cSAriff Abdullah struct snddev_info *d; 90790da2b28SAriff Abdullah int i; 908987e5972SCameron Grant 909b8a36395SCameron Grant if (pcm_veto_load) { 910b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 911b8a36395SCameron Grant 912b8a36395SCameron Grant return EINVAL; 913b8a36395SCameron Grant } 914b8a36395SCameron Grant 915bba4862cSAriff Abdullah if (device_get_unit(dev) > PCMMAXUNIT) { 916bba4862cSAriff Abdullah device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n", 917bba4862cSAriff Abdullah device_get_unit(dev), PCMMAXUNIT); 918bba4862cSAriff Abdullah device_printf(dev, 919bba4862cSAriff Abdullah "Use 'hw.snd.maxunit' tunable to raise the limit.\n"); 920bba4862cSAriff Abdullah return ENODEV; 921bba4862cSAriff Abdullah } 922bba4862cSAriff Abdullah 923bba4862cSAriff Abdullah d = device_get_softc(dev); 924e4e61333SAriff Abdullah d->dev = dev; 9252c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 926e4e61333SAriff Abdullah cv_init(&d->cv, device_get_nameunit(dev)); 927e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 9287233ababSAlexander Leidinger #if 0 9297233ababSAlexander Leidinger /* 9307233ababSAlexander Leidinger * d->flags should be cleared by the allocator of the softc. 9317233ababSAlexander Leidinger * We cannot clear this field here because several devices set 9327233ababSAlexander Leidinger * this flag before calling pcm_register(). 9337233ababSAlexander Leidinger */ 934cd9766c5SCameron Grant d->flags = 0; 9357233ababSAlexander Leidinger #endif 93690da2b28SAriff Abdullah i = 0; 93790da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 93890da2b28SAriff Abdullah "vpc", &i) != 0 || i != 0) 93990da2b28SAriff Abdullah d->flags |= SD_F_VPC; 94090da2b28SAriff Abdullah 94190da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 94290da2b28SAriff Abdullah "bitperfect", &i) == 0 && i != 0) 94390da2b28SAriff Abdullah d->flags |= SD_F_BITPERFECT; 94490da2b28SAriff Abdullah 945987e5972SCameron Grant d->devinfo = devinfo; 946f637a36cSCameron Grant d->devcount = 0; 947506a5308SCameron Grant d->reccount = 0; 948a67fe5c1SCameron Grant d->playcount = 0; 949bba4862cSAriff Abdullah d->pvchancount = 0; 950bba4862cSAriff Abdullah d->rvchancount = 0; 951bba4862cSAriff Abdullah d->pvchanrate = 0; 952bba4862cSAriff Abdullah d->pvchanformat = 0; 953bba4862cSAriff Abdullah d->rvchanrate = 0; 954bba4862cSAriff Abdullah d->rvchanformat = 0; 955833f7023SCameron Grant 956bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm); 957bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm.busy); 95890da2b28SAriff Abdullah CHN_INIT(d, channels.pcm.opened); 95945550658SPoul-Henning Kamp 960e4e61333SAriff Abdullah /* XXX This is incorrect, but lets play along for now. */ 961a1d444e1SAriff Abdullah if ((numplay == 0 || numrec == 0) && numplay != numrec) 962285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 963d95502a8SCameron Grant 964bba4862cSAriff Abdullah sysctl_ctx_init(&d->play_sysctl_ctx); 965bba4862cSAriff Abdullah d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 966bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 9677029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node"); 968bba4862cSAriff Abdullah sysctl_ctx_init(&d->rec_sysctl_ctx); 969bba4862cSAriff Abdullah d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 970bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 971132fca63SHans Petter Selasky CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node"); 972e4e61333SAriff Abdullah 97332a0e5d5SHans Petter Selasky if (numplay > 0 || numrec > 0) 974cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 97590da2b28SAriff Abdullah 9769da3b645SChristos Margiolis sndstat_register(dev, d->status); 977e4e61333SAriff Abdullah 978e8c0d15aSChristos Margiolis return (dsp_make_dev(dev)); 979987e5972SCameron Grant } 980987e5972SCameron Grant 98133dbf14aSCameron Grant int 98233dbf14aSCameron Grant pcm_unregister(device_t dev) 9837c438dbeSCameron Grant { 984e4e61333SAriff Abdullah struct snddev_info *d; 985a67fe5c1SCameron Grant struct pcm_channel *ch; 9867c438dbeSCameron Grant 987e4e61333SAriff Abdullah d = device_get_softc(dev); 988e4e61333SAriff Abdullah 989e4e61333SAriff Abdullah if (!PCM_ALIVE(d)) { 990e4e61333SAriff Abdullah device_printf(dev, "unregister: device not configured\n"); 991e4e61333SAriff Abdullah return (0); 992e4e61333SAriff Abdullah } 993bba4862cSAriff Abdullah 99490da2b28SAriff Abdullah PCM_LOCK(d); 995e4e61333SAriff Abdullah PCM_WAIT(d); 996e4e61333SAriff Abdullah 997cc1efc23SHans Petter Selasky d->flags |= SD_F_DETACHING; 998cc1efc23SHans Petter Selasky 999e4e61333SAriff Abdullah PCM_ACQUIRE(d); 100090da2b28SAriff Abdullah PCM_UNLOCK(d); 1001e4e61333SAriff Abdullah 1002e4e61333SAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 1003e4e61333SAriff Abdullah CHN_LOCK(ch); 1004*44e128feSChristos Margiolis if (ch->flags & CHN_F_SLEEPING) { 1005*44e128feSChristos Margiolis /* 1006*44e128feSChristos Margiolis * We are detaching, so do not wait for the timeout in 1007*44e128feSChristos Margiolis * chn_read()/chn_write(). Wake up the thread and kill 1008*44e128feSChristos Margiolis * the channel immediately. 1009*44e128feSChristos Margiolis */ 1010*44e128feSChristos Margiolis CHN_BROADCAST(&ch->intr_cv); 1011*44e128feSChristos Margiolis ch->flags |= CHN_F_DEAD; 1012285648f9SCameron Grant } 1013*44e128feSChristos Margiolis chn_abort(ch); 1014e4e61333SAriff Abdullah CHN_UNLOCK(ch); 1015c9b53085SCameron Grant } 10165ee30e27SMathew Kanner 1017e8c0d15aSChristos Margiolis dsp_destroy_dev(dev); 1018bba4862cSAriff Abdullah 1019*44e128feSChristos Margiolis (void)mixer_uninit(dev); 10207233ababSAlexander Leidinger 10218461d581SHans Petter Selasky /* remove /dev/sndstat entry first */ 10228461d581SHans Petter Selasky sndstat_unregister(dev); 10238461d581SHans Petter Selasky 102490da2b28SAriff Abdullah PCM_LOCK(d); 1025e4e61333SAriff Abdullah d->flags |= SD_F_DYING; 1026e4e61333SAriff Abdullah d->flags &= ~SD_F_REGISTERED; 102790da2b28SAriff Abdullah PCM_UNLOCK(d); 1028e4e61333SAriff Abdullah 1029bba4862cSAriff Abdullah if (d->play_sysctl_tree != NULL) { 1030bba4862cSAriff Abdullah sysctl_ctx_free(&d->play_sysctl_ctx); 1031bba4862cSAriff Abdullah d->play_sysctl_tree = NULL; 1032bba4862cSAriff Abdullah } 1033bba4862cSAriff Abdullah if (d->rec_sysctl_tree != NULL) { 1034bba4862cSAriff Abdullah sysctl_ctx_free(&d->rec_sysctl_ctx); 1035bba4862cSAriff Abdullah d->rec_sysctl_tree = NULL; 1036a1d444e1SAriff Abdullah } 1037bba4862cSAriff Abdullah 1038bba4862cSAriff Abdullah while (!CHN_EMPTY(d, channels.pcm)) 1039285648f9SCameron Grant pcm_killchan(dev); 10407c438dbeSCameron Grant 104190da2b28SAriff Abdullah PCM_LOCK(d); 1042e4e61333SAriff Abdullah PCM_RELEASE(d); 1043e4e61333SAriff Abdullah cv_destroy(&d->cv); 104490da2b28SAriff Abdullah PCM_UNLOCK(d); 104549c5e6e2SCameron Grant snd_mtxfree(d->lock); 1046bba4862cSAriff Abdullah 1047bba4862cSAriff Abdullah if (snd_unit == device_get_unit(dev)) { 1048cbebc90dSAlexander Motin snd_unit = pcm_best_unit(-1); 1049cbebc90dSAlexander Motin if (snd_unit_auto == 0) 1050cbebc90dSAlexander Motin snd_unit_auto = 1; 1051e4e61333SAriff Abdullah } 1052bba4862cSAriff Abdullah 1053e4e61333SAriff Abdullah return (0); 105433dbf14aSCameron Grant } 10557c438dbeSCameron Grant 105667b1dce3SCameron Grant /************************************************************************/ 105767b1dce3SCameron Grant 1058b611c801SAlexander Leidinger /** 1059b611c801SAlexander Leidinger * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 1060b611c801SAlexander Leidinger * 1061b611c801SAlexander Leidinger * @param si Pointer to oss_sysinfo struct where information about the 1062b611c801SAlexander Leidinger * sound subsystem will be written/copied. 1063b611c801SAlexander Leidinger * 1064b611c801SAlexander Leidinger * This routine returns information about the sound system, such as the 1065b611c801SAlexander Leidinger * current OSS version, number of audio, MIDI, and mixer drivers, etc. 1066b611c801SAlexander Leidinger * Also includes a bitmask showing which of the above types of devices 1067b611c801SAlexander Leidinger * are open (busy). 1068b611c801SAlexander Leidinger * 1069b611c801SAlexander Leidinger * @note 1070b611c801SAlexander Leidinger * Calling threads must not hold any snddev_info or pcm_channel locks. 1071b611c801SAlexander Leidinger * 1072b611c801SAlexander Leidinger * @author Ryan Beasley <ryanb@FreeBSD.org> 1073b611c801SAlexander Leidinger */ 1074b611c801SAlexander Leidinger void 1075b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si) 1076b611c801SAlexander Leidinger { 1077b611c801SAlexander Leidinger static char si_product[] = "FreeBSD native OSS ABI"; 1078b611c801SAlexander Leidinger static char si_version[] = __XSTRING(__FreeBSD_version); 1079c412d503SAlexander Motin static char si_license[] = "BSD"; 1080b611c801SAlexander Leidinger static int intnbits = sizeof(int) * 8; /* Better suited as macro? 1081b611c801SAlexander Leidinger Must pester a C guru. */ 1082b611c801SAlexander Leidinger 1083b611c801SAlexander Leidinger struct snddev_info *d; 1084b611c801SAlexander Leidinger struct pcm_channel *c; 1085b611c801SAlexander Leidinger int i, j, ncards; 1086b611c801SAlexander Leidinger 1087b611c801SAlexander Leidinger ncards = 0; 1088b611c801SAlexander Leidinger 1089b611c801SAlexander Leidinger strlcpy(si->product, si_product, sizeof(si->product)); 1090b611c801SAlexander Leidinger strlcpy(si->version, si_version, sizeof(si->version)); 1091b611c801SAlexander Leidinger si->versionnum = SOUND_VERSION; 1092c412d503SAlexander Motin strlcpy(si->license, si_license, sizeof(si->license)); 1093b611c801SAlexander Leidinger 1094b611c801SAlexander Leidinger /* 1095b611c801SAlexander Leidinger * Iterate over PCM devices and their channels, gathering up data 1096b611c801SAlexander Leidinger * for the numaudios, ncards, and openedaudio fields. 1097b611c801SAlexander Leidinger */ 1098b611c801SAlexander Leidinger si->numaudios = 0; 1099b611c801SAlexander Leidinger bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 1100b611c801SAlexander Leidinger 1101b611c801SAlexander Leidinger j = 0; 1102b611c801SAlexander Leidinger 11039c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 11049c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 1105b611c801SAlexander Leidinger d = devclass_get_softc(pcm_devclass, i); 1106e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 1107b611c801SAlexander Leidinger continue; 1108b611c801SAlexander Leidinger 1109e4e61333SAriff Abdullah /* XXX Need Giant magic entry ??? */ 1110e4e61333SAriff Abdullah 1111b611c801SAlexander Leidinger /* See note in function's docblock */ 111290da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 111390da2b28SAriff Abdullah PCM_LOCK(d); 1114b611c801SAlexander Leidinger 1115b611c801SAlexander Leidinger si->numaudios += d->devcount; 1116b611c801SAlexander Leidinger ++ncards; 1117b611c801SAlexander Leidinger 1118bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 111990da2b28SAriff Abdullah CHN_UNLOCKASSERT(c); 1120b611c801SAlexander Leidinger CHN_LOCK(c); 1121b611c801SAlexander Leidinger if (c->flags & CHN_F_BUSY) 1122b611c801SAlexander Leidinger si->openedaudio[j / intnbits] |= 1123b611c801SAlexander Leidinger (1 << (j % intnbits)); 1124b611c801SAlexander Leidinger CHN_UNLOCK(c); 1125b611c801SAlexander Leidinger j++; 1126b611c801SAlexander Leidinger } 1127b611c801SAlexander Leidinger 112890da2b28SAriff Abdullah PCM_UNLOCK(d); 1129b611c801SAlexander Leidinger } 1130c412d503SAlexander Motin si->numaudioengines = si->numaudios; 1131b611c801SAlexander Leidinger 1132b611c801SAlexander Leidinger si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 1133b611c801SAlexander Leidinger /** 1134b611c801SAlexander Leidinger * @todo Collect num{midis,timers}. 1135b611c801SAlexander Leidinger * 1136b611c801SAlexander Leidinger * Need access to sound/midi/midi.c::midistat_lock in order 1137b611c801SAlexander Leidinger * to safely touch midi_devices and get a head count of, well, 1138b611c801SAlexander Leidinger * MIDI devices. midistat_lock is a global static (i.e., local to 1139b611c801SAlexander Leidinger * midi.c), but midi_devices is a regular global; should the mutex 1140b611c801SAlexander Leidinger * be publicized, or is there another way to get this information? 1141b611c801SAlexander Leidinger * 1142b611c801SAlexander Leidinger * NB: MIDI/sequencer stuff is currently on hold. 1143b611c801SAlexander Leidinger */ 1144b611c801SAlexander Leidinger si->nummidis = 0; 1145b611c801SAlexander Leidinger si->numtimers = 0; 1146b611c801SAlexander Leidinger si->nummixers = mixer_count; 1147b611c801SAlexander Leidinger si->numcards = ncards; 1148b611c801SAlexander Leidinger /* OSSv4 docs: Intended only for test apps; API doesn't 1149b611c801SAlexander Leidinger really have much of a concept of cards. Shouldn't be 1150b611c801SAlexander Leidinger used by applications. */ 1151b611c801SAlexander Leidinger 1152b611c801SAlexander Leidinger /** 1153b611c801SAlexander Leidinger * @todo Fill in "busy devices" fields. 1154b611c801SAlexander Leidinger * 1155b611c801SAlexander Leidinger * si->openedmidi = " MIDI devices 1156b611c801SAlexander Leidinger */ 1157b611c801SAlexander Leidinger bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 1158b611c801SAlexander Leidinger 1159b611c801SAlexander Leidinger /* 1160b611c801SAlexander Leidinger * Si->filler is a reserved array, but according to docs each 1161b611c801SAlexander Leidinger * element should be set to -1. 1162b611c801SAlexander Leidinger */ 1163b611c801SAlexander Leidinger for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++) 1164b611c801SAlexander Leidinger si->filler[i] = -1; 1165b611c801SAlexander Leidinger } 1166b611c801SAlexander Leidinger 116752f6e09eSAlexander Motin int 116852f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si) 116952f6e09eSAlexander Motin { 117052f6e09eSAlexander Motin struct snddev_info *d; 117152f6e09eSAlexander Motin int i, ncards; 117252f6e09eSAlexander Motin 117352f6e09eSAlexander Motin ncards = 0; 117452f6e09eSAlexander Motin 117552f6e09eSAlexander Motin for (i = 0; pcm_devclass != NULL && 117652f6e09eSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 117752f6e09eSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 117852f6e09eSAlexander Motin if (!PCM_REGISTERED(d)) 117952f6e09eSAlexander Motin continue; 118052f6e09eSAlexander Motin 118152f6e09eSAlexander Motin if (ncards++ != si->card) 118252f6e09eSAlexander Motin continue; 118352f6e09eSAlexander Motin 118490da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 118590da2b28SAriff Abdullah PCM_LOCK(d); 118652f6e09eSAlexander Motin 118752f6e09eSAlexander Motin strlcpy(si->shortname, device_get_nameunit(d->dev), 118852f6e09eSAlexander Motin sizeof(si->shortname)); 118952f6e09eSAlexander Motin strlcpy(si->longname, device_get_desc(d->dev), 119052f6e09eSAlexander Motin sizeof(si->longname)); 119152f6e09eSAlexander Motin strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 119252f6e09eSAlexander Motin si->intr_count = si->ack_count = 0; 119390da2b28SAriff Abdullah 119490da2b28SAriff Abdullah PCM_UNLOCK(d); 119590da2b28SAriff Abdullah 119652f6e09eSAlexander Motin return (0); 119752f6e09eSAlexander Motin } 119852f6e09eSAlexander Motin return (ENXIO); 119952f6e09eSAlexander Motin } 120052f6e09eSAlexander Motin 1201b611c801SAlexander Leidinger /************************************************************************/ 1202b611c801SAlexander Leidinger 12030739ea1dSSeigo Tanimura static int 12040739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data) 12050739ea1dSSeigo Tanimura { 1206b611c801SAlexander Leidinger int ret; 1207b611c801SAlexander Leidinger 12088109ec9dSJohn Baldwin ret = 0; 1209b611c801SAlexander Leidinger switch (type) { 1210b611c801SAlexander Leidinger case MOD_LOAD: 121113bebcd3SJohn Baldwin pcm_devclass = devclass_create("pcm"); 1212b611c801SAlexander Leidinger pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 1213b611c801SAlexander Leidinger break; 1214b611c801SAlexander Leidinger case MOD_UNLOAD: 1215b611c801SAlexander Leidinger if (pcmsg_unrhdr != NULL) { 1216b611c801SAlexander Leidinger delete_unrhdr(pcmsg_unrhdr); 1217b611c801SAlexander Leidinger pcmsg_unrhdr = NULL; 1218b611c801SAlexander Leidinger } 1219b611c801SAlexander Leidinger break; 12205525b53dSAlexander Motin case MOD_SHUTDOWN: 12215525b53dSAlexander Motin break; 1222b611c801SAlexander Leidinger default: 122390da2b28SAriff Abdullah ret = ENOTSUP; 1224b611c801SAlexander Leidinger } 1225b611c801SAlexander Leidinger 1226b611c801SAlexander Leidinger return ret; 12270739ea1dSSeigo Tanimura } 12280739ea1dSSeigo Tanimura 12290739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL); 12300739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER); 1231