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. 9*4f854658SChristos Margiolis * Copyright (c) 2024 The FreeBSD Foundation 10*4f854658SChristos Margiolis * 11*4f854658SChristos Margiolis * Portions of this software were developed by Christos Margiolis 12*4f854658SChristos Margiolis * <christos@FreeBSD.org> under sponsorship from the FreeBSD Foundation. 13987e5972SCameron Grant * 14987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without 15987e5972SCameron Grant * modification, are permitted provided that the following conditions 16987e5972SCameron Grant * are met: 17987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright 18987e5972SCameron Grant * notice, this list of conditions and the following disclaimer. 19987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 20987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the 21987e5972SCameron Grant * documentation and/or other materials provided with the distribution. 22987e5972SCameron Grant * 23987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33987e5972SCameron Grant * SUCH DAMAGE. 34987e5972SCameron Grant */ 35987e5972SCameron Grant 3690da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 3790da2b28SAriff Abdullah #include "opt_snd.h" 3890da2b28SAriff Abdullah #endif 3990da2b28SAriff Abdullah 40ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 41a580b31aSAriff Abdullah #include <dev/sound/pcm/ac97.h> 42285648f9SCameron Grant #include <dev/sound/pcm/vchan.h> 435ee30e27SMathew Kanner #include <dev/sound/pcm/dsp.h> 44bba4862cSAriff Abdullah #include <dev/sound/version.h> 45b611c801SAlexander Leidinger #include <sys/limits.h> 467c438dbeSCameron Grant #include <sys/sysctl.h> 47285648f9SCameron Grant 4867b1dce3SCameron Grant #include "feeder_if.h" 4967b1dce3SCameron Grant 50d95502a8SCameron Grant devclass_t pcm_devclass; 5182db23e2SCameron Grant 52b8a36395SCameron Grant int pcm_veto_load = 1; 53b8a36395SCameron Grant 54f3685841SAriff Abdullah int snd_unit = -1; 55cbe7d6a3SCameron Grant 56cbebc90dSAlexander Motin static int snd_unit_auto = -1; 57af3b2549SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN, 58838d3589SAriff Abdullah &snd_unit_auto, 0, "assign default unit to a newly attached device"); 59ad8612b9SAriff Abdullah 60bba4862cSAriff Abdullah int snd_maxautovchans = 16; 61987e5972SCameron Grant 627029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 637029da5cSPawel Biernacki "Sound driver"); 6482db23e2SCameron Grant 6532a0e5d5SHans Petter Selasky static void pcm_sysinit(device_t); 6632a0e5d5SHans Petter Selasky 67bba4862cSAriff Abdullah /* 68bba4862cSAriff Abdullah * XXX I've had enough with people not telling proper version/arch 69bba4862cSAriff Abdullah * while reporting problems, not after 387397913213th questions/requests. 70bba4862cSAriff Abdullah */ 71a9bdb5d3SAndriy Gapon static char snd_driver_version[] = 72bba4862cSAriff Abdullah __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; 73bba4862cSAriff Abdullah SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, 7490da2b28SAriff Abdullah 0, "driver version/arch"); 75bba4862cSAriff Abdullah 76b611c801SAlexander Leidinger /** 77b611c801SAlexander Leidinger * @brief Unit number allocator for syncgroup IDs 78b611c801SAlexander Leidinger */ 79b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL; 80b611c801SAlexander Leidinger 8137209180SCameron Grant void * 822c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type) 8337209180SCameron Grant { 8437209180SCameron Grant struct mtx *m; 8537209180SCameron Grant 86a163d034SWarner Losh m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 8712e524a2SDon Lewis mtx_init(m, desc, type, MTX_DEF); 8812e524a2SDon Lewis return m; 8912e524a2SDon Lewis } 9012e524a2SDon Lewis 9137209180SCameron Grant void 9237209180SCameron Grant snd_mtxfree(void *m) 9337209180SCameron Grant { 9437209180SCameron Grant struct mtx *mtx = m; 9537209180SCameron Grant 9637209180SCameron Grant mtx_destroy(mtx); 9737209180SCameron Grant free(mtx, M_DEVBUF); 9837209180SCameron Grant } 9937209180SCameron Grant 10037209180SCameron Grant void 10137209180SCameron Grant snd_mtxassert(void *m) 10237209180SCameron Grant { 103f00f162aSCameron Grant #ifdef INVARIANTS 10437209180SCameron Grant struct mtx *mtx = m; 10537209180SCameron Grant 10637209180SCameron Grant mtx_assert(mtx, MA_OWNED); 10737209180SCameron Grant #endif 10837209180SCameron Grant } 10937209180SCameron Grant 11037209180SCameron Grant int 11137209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 11237209180SCameron Grant { 113e4e61333SAriff Abdullah struct snddev_info *d; 11490da2b28SAriff Abdullah 11537209180SCameron Grant flags &= INTR_MPSAFE; 11646700f12SPeter Wemm flags |= INTR_TYPE_AV; 117e4e61333SAriff Abdullah d = device_get_softc(dev); 118e4e61333SAriff Abdullah if (d != NULL && (flags & INTR_MPSAFE)) 119e4e61333SAriff Abdullah d->flags |= SD_F_MPSAFE; 120e4e61333SAriff Abdullah 121775fcb6eSBaptiste Daroussin return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep); 12237209180SCameron Grant } 12337209180SCameron Grant 12490da2b28SAriff Abdullah int 125bba4862cSAriff Abdullah pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) 126bba4862cSAriff Abdullah { 127bba4862cSAriff Abdullah struct pcm_channel *c, *ch, *nch; 12890da2b28SAriff Abdullah struct pcmchan_caps *caps; 12990da2b28SAriff Abdullah int i, err, vcnt; 130bba4862cSAriff Abdullah 131e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 132a1d444e1SAriff Abdullah 133bba4862cSAriff Abdullah if ((direction == PCMDIR_PLAY && d->playcount < 1) || 134e4e61333SAriff Abdullah (direction == PCMDIR_REC && d->reccount < 1)) 135e4e61333SAriff Abdullah return (ENODEV); 136a580b31aSAriff Abdullah 137e4e61333SAriff Abdullah if (!(d->flags & SD_F_AUTOVCHAN)) 138e4e61333SAriff Abdullah return (EINVAL); 139a1d444e1SAriff Abdullah 140e4e61333SAriff Abdullah if (newcnt < 0 || newcnt > SND_MAXVCHANS) 141e4e61333SAriff Abdullah return (E2BIG); 142a1d444e1SAriff Abdullah 143bba4862cSAriff Abdullah if (direction == PCMDIR_PLAY) 144bba4862cSAriff Abdullah vcnt = d->pvchancount; 145bba4862cSAriff Abdullah else if (direction == PCMDIR_REC) 146bba4862cSAriff Abdullah vcnt = d->rvchancount; 147e4e61333SAriff Abdullah else 148e4e61333SAriff Abdullah return (EINVAL); 149a1d444e1SAriff Abdullah 150a1d444e1SAriff Abdullah if (newcnt > vcnt) { 151bba4862cSAriff Abdullah KASSERT(num == -1 || 152bba4862cSAriff Abdullah (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), 153bba4862cSAriff Abdullah ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", 154bba4862cSAriff Abdullah num, newcnt, vcnt)); 155a1d444e1SAriff Abdullah /* add new vchans - find a parent channel first */ 156e4e61333SAriff Abdullah ch = NULL; 157bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 158a1d444e1SAriff Abdullah CHN_LOCK(c); 159bba4862cSAriff Abdullah if (c->direction == direction && 160bba4862cSAriff Abdullah ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 16190da2b28SAriff Abdullah c->refcount < 1 && 162e4e61333SAriff Abdullah !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 16390da2b28SAriff Abdullah /* 16490da2b28SAriff Abdullah * Reuse hw channel with vchans already 16590da2b28SAriff Abdullah * created. 16690da2b28SAriff Abdullah */ 16790da2b28SAriff Abdullah if (c->flags & CHN_F_HAS_VCHAN) { 168e4e61333SAriff Abdullah ch = c; 169e4e61333SAriff Abdullah break; 170e4e61333SAriff Abdullah } 17190da2b28SAriff Abdullah /* 17290da2b28SAriff Abdullah * No vchans ever created, look for 17390da2b28SAriff Abdullah * channels with supported formats. 17490da2b28SAriff Abdullah */ 17590da2b28SAriff Abdullah caps = chn_getcaps(c); 17690da2b28SAriff Abdullah if (caps == NULL) { 17790da2b28SAriff Abdullah CHN_UNLOCK(c); 17890da2b28SAriff Abdullah continue; 17990da2b28SAriff Abdullah } 18090da2b28SAriff Abdullah for (i = 0; caps->fmtlist[i] != 0; i++) { 18190da2b28SAriff Abdullah if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 18290da2b28SAriff Abdullah break; 18390da2b28SAriff Abdullah } 18490da2b28SAriff Abdullah if (caps->fmtlist[i] != 0) { 18590da2b28SAriff Abdullah ch = c; 18690da2b28SAriff Abdullah break; 18790da2b28SAriff Abdullah } 18890da2b28SAriff Abdullah } 189a1d444e1SAriff Abdullah CHN_UNLOCK(c); 190a1d444e1SAriff Abdullah } 191e4e61333SAriff Abdullah if (ch == NULL) 192e4e61333SAriff Abdullah return (EBUSY); 193e4e61333SAriff Abdullah ch->flags |= CHN_F_BUSY; 194e4e61333SAriff Abdullah err = 0; 195a1d444e1SAriff Abdullah while (err == 0 && newcnt > vcnt) { 196e4e61333SAriff Abdullah err = vchan_create(ch, num); 197bba4862cSAriff Abdullah if (err == 0) 198a1d444e1SAriff Abdullah vcnt++; 199bba4862cSAriff Abdullah else if (err == E2BIG && newcnt > vcnt) 200bba4862cSAriff Abdullah device_printf(d->dev, 201bba4862cSAriff Abdullah "%s: err=%d Maximum channel reached.\n", 202bba4862cSAriff Abdullah __func__, err); 203a1d444e1SAriff Abdullah } 204a1d444e1SAriff Abdullah if (vcnt == 0) 205e4e61333SAriff Abdullah ch->flags &= ~CHN_F_BUSY; 206e4e61333SAriff Abdullah CHN_UNLOCK(ch); 207e4e61333SAriff Abdullah if (err != 0) 208e4e61333SAriff Abdullah return (err); 209a1d444e1SAriff Abdullah } else if (newcnt < vcnt) { 210bba4862cSAriff Abdullah KASSERT(num == -1, 211bba4862cSAriff Abdullah ("bogus vchan_destroy() request num=%d", num)); 212bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 213a1d444e1SAriff Abdullah CHN_LOCK(c); 214bba4862cSAriff Abdullah if (c->direction != direction || 215bba4862cSAriff Abdullah CHN_EMPTY(c, children) || 216bba4862cSAriff Abdullah !(c->flags & CHN_F_HAS_VCHAN)) { 217a1d444e1SAriff Abdullah CHN_UNLOCK(c); 218bba4862cSAriff Abdullah continue; 219a1d444e1SAriff Abdullah } 220bba4862cSAriff Abdullah CHN_FOREACH_SAFE(ch, c, nch, children) { 221bba4862cSAriff Abdullah CHN_LOCK(ch); 22290da2b28SAriff Abdullah if (vcnt == 1 && c->refcount > 0) { 223bba4862cSAriff Abdullah CHN_UNLOCK(ch); 22490da2b28SAriff Abdullah break; 22590da2b28SAriff Abdullah } 22690da2b28SAriff Abdullah if (!(ch->flags & CHN_F_BUSY) && 22790da2b28SAriff Abdullah ch->refcount < 1) { 228bba4862cSAriff Abdullah err = vchan_destroy(ch); 229a1d444e1SAriff Abdullah if (err == 0) 230a1d444e1SAriff Abdullah vcnt--; 231bba4862cSAriff Abdullah } else 232bba4862cSAriff Abdullah CHN_UNLOCK(ch); 233e4e61333SAriff Abdullah if (vcnt == newcnt) 234bba4862cSAriff Abdullah break; 235a1d444e1SAriff Abdullah } 236bba4862cSAriff Abdullah CHN_UNLOCK(c); 237bba4862cSAriff Abdullah break; 238bba4862cSAriff Abdullah } 239bba4862cSAriff Abdullah } 240a1d444e1SAriff Abdullah 241e4e61333SAriff Abdullah return (0); 242a1d444e1SAriff Abdullah } 243a1d444e1SAriff Abdullah 2443fdb3676SAriff Abdullah /* return error status and a locked channel */ 2453fdb3676SAriff Abdullah int 2463fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 24790da2b28SAriff Abdullah pid_t pid, char *comm, int devunit) 248285648f9SCameron Grant { 249285648f9SCameron Grant struct pcm_channel *c; 25090da2b28SAriff Abdullah int err, vchancount, vchan_num; 251bba4862cSAriff Abdullah 252bba4862cSAriff Abdullah KASSERT(d != NULL && ch != NULL && (devunit == -1 || 253bba4862cSAriff Abdullah !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && 254bba4862cSAriff Abdullah (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 255e4e61333SAriff Abdullah ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d", 256bba4862cSAriff Abdullah __func__, d, ch, direction, pid, devunit)); 257e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 258bba4862cSAriff Abdullah 259bba4862cSAriff Abdullah /* Double check again. */ 260bba4862cSAriff Abdullah if (devunit != -1) { 261bba4862cSAriff Abdullah switch (snd_unit2d(devunit)) { 262bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 263bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 264bba4862cSAriff Abdullah if (direction != PCMDIR_PLAY) 26590da2b28SAriff Abdullah return (ENOTSUP); 266bba4862cSAriff Abdullah break; 267bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 268bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 269bba4862cSAriff Abdullah if (direction != PCMDIR_REC) 27090da2b28SAriff Abdullah return (ENOTSUP); 271bba4862cSAriff Abdullah break; 272bba4862cSAriff Abdullah default: 273bba4862cSAriff Abdullah if (!(direction == PCMDIR_PLAY || 274bba4862cSAriff Abdullah direction == PCMDIR_REC)) 27590da2b28SAriff Abdullah return (ENOTSUP); 276bba4862cSAriff Abdullah break; 277bba4862cSAriff Abdullah } 278bba4862cSAriff Abdullah } 279285648f9SCameron Grant 28090da2b28SAriff Abdullah *ch = NULL; 28190da2b28SAriff Abdullah vchan_num = 0; 28290da2b28SAriff Abdullah vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : 28390da2b28SAriff Abdullah d->rvchancount; 28490da2b28SAriff Abdullah 2853fdb3676SAriff Abdullah retry_chnalloc: 28690da2b28SAriff Abdullah err = ENOTSUP; 287f637a36cSCameron Grant /* scan for a free channel */ 288bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 28949c5e6e2SCameron Grant CHN_LOCK(c); 29090da2b28SAriff Abdullah if (devunit == -1 && c->direction == direction && 29190da2b28SAriff Abdullah (c->flags & CHN_F_VIRTUAL)) { 29290da2b28SAriff Abdullah if (vchancount < snd_maxautovchans && 29390da2b28SAriff Abdullah vchan_num < CHN_CHAN(c)) { 29490da2b28SAriff Abdullah CHN_UNLOCK(c); 29590da2b28SAriff Abdullah goto vchan_alloc; 29690da2b28SAriff Abdullah } 29790da2b28SAriff Abdullah vchan_num++; 29890da2b28SAriff Abdullah } 299bba4862cSAriff Abdullah if (c->direction == direction && !(c->flags & CHN_F_BUSY) && 300bba4862cSAriff Abdullah (devunit == -1 || devunit == -2 || c->unit == devunit)) { 301285648f9SCameron Grant c->flags |= CHN_F_BUSY; 302b8f0d9e0SCameron Grant c->pid = pid; 30390da2b28SAriff Abdullah strlcpy(c->comm, (comm != NULL) ? comm : 30490da2b28SAriff Abdullah CHN_COMM_UNKNOWN, sizeof(c->comm)); 3053fdb3676SAriff Abdullah *ch = c; 306bba4862cSAriff Abdullah return (0); 307bba4862cSAriff Abdullah } else if (c->unit == devunit) { 3083fdb3676SAriff Abdullah if (c->direction != direction) 30990da2b28SAriff Abdullah err = ENOTSUP; 3103fdb3676SAriff Abdullah else if (c->flags & CHN_F_BUSY) 3113fdb3676SAriff Abdullah err = EBUSY; 3123fdb3676SAriff Abdullah else 3133fdb3676SAriff Abdullah err = EINVAL; 3143fdb3676SAriff Abdullah CHN_UNLOCK(c); 315bba4862cSAriff Abdullah return (err); 316bba4862cSAriff Abdullah } else if ((devunit == -1 || devunit == -2) && 317bba4862cSAriff Abdullah c->direction == direction && (c->flags & CHN_F_BUSY)) 318a1d444e1SAriff Abdullah err = EBUSY; 31949c5e6e2SCameron Grant CHN_UNLOCK(c); 320285648f9SCameron Grant } 321f637a36cSCameron Grant 322e4e61333SAriff Abdullah if (devunit == -2) 323e4e61333SAriff Abdullah return (err); 324e4e61333SAriff Abdullah 32590da2b28SAriff Abdullah vchan_alloc: 326f637a36cSCameron Grant /* no channel available */ 327e4e61333SAriff Abdullah if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || 328e4e61333SAriff Abdullah snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { 329bba4862cSAriff Abdullah if (!(vchancount > 0 && vchancount < snd_maxautovchans) && 330bba4862cSAriff Abdullah (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) 331bba4862cSAriff Abdullah return (err); 332bba4862cSAriff Abdullah err = pcm_setvchans(d, direction, vchancount + 1, 333bba4862cSAriff Abdullah (devunit == -1) ? -1 : snd_unit2c(devunit)); 334a1d444e1SAriff Abdullah if (err == 0) { 335bba4862cSAriff Abdullah if (devunit == -1) 336bba4862cSAriff Abdullah devunit = -2; 3373fdb3676SAriff Abdullah goto retry_chnalloc; 338f637a36cSCameron Grant } 339f637a36cSCameron Grant } 340f637a36cSCameron Grant 341bba4862cSAriff Abdullah return (err); 342285648f9SCameron Grant } 343285648f9SCameron Grant 344b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 345285648f9SCameron Grant int 346b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 347285648f9SCameron Grant { 348e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 349b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 350e4e61333SAriff Abdullah 351285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 352b8f0d9e0SCameron Grant c->pid = -1; 35390da2b28SAriff Abdullah strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm)); 35449c5e6e2SCameron Grant CHN_UNLOCK(c); 355e4e61333SAriff Abdullah 356e4e61333SAriff Abdullah return (0); 357285648f9SCameron Grant } 358285648f9SCameron Grant 359285648f9SCameron Grant int 360285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 361285648f9SCameron Grant { 362e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 363b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 364e4e61333SAriff Abdullah 365285648f9SCameron Grant c->refcount += ref; 366e4e61333SAriff Abdullah 367e4e61333SAriff Abdullah return (c->refcount); 368285648f9SCameron Grant } 369285648f9SCameron Grant 37067b1dce3SCameron Grant static void 37167b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 37267b1dce3SCameron Grant { 373e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 374e4e61333SAriff Abdullah 375bba4862cSAriff Abdullah if (num < 0) 376bba4862cSAriff Abdullah return; 377bba4862cSAriff Abdullah 378bba4862cSAriff Abdullah if (num >= 0 && d->pvchancount > num) 379bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); 380bba4862cSAriff Abdullah else if (num > 0 && d->pvchancount == 0) 381bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); 382bba4862cSAriff Abdullah 383bba4862cSAriff Abdullah if (num >= 0 && d->rvchancount > num) 384bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, num, -1); 385bba4862cSAriff Abdullah else if (num > 0 && d->rvchancount == 0) 386bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); 38767b1dce3SCameron Grant } 38867b1dce3SCameron Grant 38933dbf14aSCameron Grant static int 390851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 39133dbf14aSCameron Grant { 392b8f0d9e0SCameron Grant struct snddev_info *d; 39333dbf14aSCameron Grant int error, unit; 39433dbf14aSCameron Grant 39533dbf14aSCameron Grant unit = snd_unit; 396041b706bSDavid Malone error = sysctl_handle_int(oidp, &unit, 0, req); 39733dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 398b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 399e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 400b8f0d9e0SCameron Grant return EINVAL; 40133dbf14aSCameron Grant snd_unit = unit; 402cbebc90dSAlexander Motin snd_unit_auto = 0; 40333dbf14aSCameron Grant } 40433dbf14aSCameron Grant return (error); 40533dbf14aSCameron Grant } 406851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */ 407b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, 4087029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0, 4097029da5cSPawel Biernacki sizeof(int), sysctl_hw_snd_default_unit, "I", 410b7d6c6b5SEitan Adler "default sound device"); 411987e5972SCameron Grant 412cd9766c5SCameron Grant static int 41367b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 414cd9766c5SCameron Grant { 41567b1dce3SCameron Grant struct snddev_info *d; 41667b1dce3SCameron Grant int i, v, error; 417cd9766c5SCameron Grant 41867b1dce3SCameron Grant v = snd_maxautovchans; 419041b706bSDavid Malone error = sysctl_handle_int(oidp, &v, 0, req); 420cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 421bba4862cSAriff Abdullah if (v < 0) 422bba4862cSAriff Abdullah v = 0; 423bba4862cSAriff Abdullah if (v > SND_MAXVCHANS) 424bba4862cSAriff Abdullah v = SND_MAXVCHANS; 425bba4862cSAriff Abdullah snd_maxautovchans = v; 4269c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 4279c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 42867b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 429e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 43067b1dce3SCameron Grant continue; 431e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 43267b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 433e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 43467b1dce3SCameron Grant } 43567b1dce3SCameron Grant } 436cd9766c5SCameron Grant return (error); 437cd9766c5SCameron Grant } 4387029da5cSPawel Biernacki SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, 4397029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 4407029da5cSPawel Biernacki sysctl_hw_snd_maxautovchans, "I", 4417029da5cSPawel Biernacki "maximum virtual channel"); 442f637a36cSCameron Grant 443285648f9SCameron Grant struct pcm_channel * 444bba4862cSAriff Abdullah pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo) 445987e5972SCameron Grant { 446bba4862cSAriff Abdullah struct pcm_channel *ch; 447bba4862cSAriff Abdullah int direction, err, rpnum, *pnum, max; 448bba4862cSAriff Abdullah int udc, device, chan; 449bba4862cSAriff Abdullah char *dirs, *devname, buf[CHN_NAMELEN]; 450bba4862cSAriff Abdullah 451e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 45290da2b28SAriff Abdullah PCM_LOCKASSERT(d); 453bba4862cSAriff Abdullah KASSERT(num >= -1, ("invalid num=%d", num)); 454bba4862cSAriff Abdullah 455285648f9SCameron Grant switch (dir) { 456285648f9SCameron Grant case PCMDIR_PLAY: 457285648f9SCameron Grant dirs = "play"; 458a3193a9cSDon Lewis direction = PCMDIR_PLAY; 459a67fe5c1SCameron Grant pnum = &d->playcount; 460bba4862cSAriff Abdullah device = SND_DEV_DSPHW_PLAY; 461bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 462285648f9SCameron Grant break; 463bba4862cSAriff Abdullah case PCMDIR_PLAY_VIRTUAL: 464bba4862cSAriff Abdullah dirs = "virtual"; 465bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 466bba4862cSAriff Abdullah pnum = &d->pvchancount; 467bba4862cSAriff Abdullah device = SND_DEV_DSPHW_VPLAY; 468bba4862cSAriff Abdullah max = SND_MAXVCHANS; 469bba4862cSAriff Abdullah break; 470285648f9SCameron Grant case PCMDIR_REC: 471285648f9SCameron Grant dirs = "record"; 472a3193a9cSDon Lewis direction = PCMDIR_REC; 473a67fe5c1SCameron Grant pnum = &d->reccount; 474bba4862cSAriff Abdullah device = SND_DEV_DSPHW_REC; 475bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 476285648f9SCameron Grant break; 477bba4862cSAriff Abdullah case PCMDIR_REC_VIRTUAL: 478285648f9SCameron Grant dirs = "virtual"; 479bba4862cSAriff Abdullah direction = PCMDIR_REC; 480bba4862cSAriff Abdullah pnum = &d->rvchancount; 481bba4862cSAriff Abdullah device = SND_DEV_DSPHW_VREC; 482bba4862cSAriff Abdullah max = SND_MAXVCHANS; 483285648f9SCameron Grant break; 484285648f9SCameron Grant default: 485e4e61333SAriff Abdullah return (NULL); 4869c326820SCameron Grant } 487285648f9SCameron Grant 488bba4862cSAriff Abdullah chan = (num == -1) ? 0 : num; 489285648f9SCameron Grant 490e4e61333SAriff Abdullah if (*pnum >= max || chan >= max) 491e4e61333SAriff Abdullah return (NULL); 492bba4862cSAriff Abdullah 4933fdb3676SAriff Abdullah rpnum = 0; 494bba4862cSAriff Abdullah 495bba4862cSAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 496bba4862cSAriff Abdullah if (CHN_DEV(ch) != device) 4973fdb3676SAriff Abdullah continue; 498bba4862cSAriff Abdullah if (chan == CHN_CHAN(ch)) { 499bba4862cSAriff Abdullah if (num != -1) { 5003fdb3676SAriff Abdullah device_printf(d->dev, 501bba4862cSAriff Abdullah "channel num=%d allocated!\n", chan); 502e4e61333SAriff Abdullah return (NULL); 503bba4862cSAriff Abdullah } 504bba4862cSAriff Abdullah chan++; 505bba4862cSAriff Abdullah if (chan >= max) { 506bba4862cSAriff Abdullah device_printf(d->dev, 507bba4862cSAriff Abdullah "chan=%d > %d\n", chan, max); 508e4e61333SAriff Abdullah return (NULL); 509bba4862cSAriff Abdullah } 5103fdb3676SAriff Abdullah } 5113fdb3676SAriff Abdullah rpnum++; 5123fdb3676SAriff Abdullah } 513bba4862cSAriff Abdullah 5143fdb3676SAriff Abdullah if (*pnum != rpnum) { 5153fdb3676SAriff Abdullah device_printf(d->dev, 516bba4862cSAriff Abdullah "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", 5173fdb3676SAriff Abdullah __func__, dirs, *pnum, rpnum); 518e4e61333SAriff Abdullah return (NULL); 5193fdb3676SAriff Abdullah } 520a67fe5c1SCameron Grant 521bba4862cSAriff Abdullah udc = snd_mkunit(device_get_unit(d->dev), device, chan); 522bba4862cSAriff Abdullah devname = dsp_unit2name(buf, sizeof(buf), udc); 523bba4862cSAriff Abdullah 524bba4862cSAriff Abdullah if (devname == NULL) { 525bba4862cSAriff Abdullah device_printf(d->dev, 526bba4862cSAriff Abdullah "Failed to query device name udc=0x%08x\n", udc); 527e4e61333SAriff Abdullah return (NULL); 528bba4862cSAriff Abdullah } 529bba4862cSAriff Abdullah 53090da2b28SAriff Abdullah PCM_UNLOCK(d); 531bba4862cSAriff Abdullah ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 532bba4862cSAriff Abdullah ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); 533bba4862cSAriff Abdullah ch->unit = udc; 534285648f9SCameron Grant ch->pid = -1; 53590da2b28SAriff Abdullah strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm)); 536285648f9SCameron Grant ch->parentsnddev = d; 537285648f9SCameron Grant ch->parentchannel = parent; 538436c9b65SScott Long ch->dev = d->dev; 539bba4862cSAriff Abdullah ch->trigger = PCMTRIG_STOP; 540bba4862cSAriff Abdullah snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", 541bba4862cSAriff Abdullah device_get_nameunit(ch->dev), dirs, devname); 542285648f9SCameron Grant 543a3193a9cSDon Lewis err = chn_init(ch, devinfo, dir, direction); 54490da2b28SAriff Abdullah PCM_LOCK(d); 5450f55ac6cSCameron Grant if (err) { 546bba4862cSAriff Abdullah device_printf(d->dev, "chn_init(%s) failed: err = %d\n", 547bba4862cSAriff Abdullah ch->name, err); 548285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 549285648f9SCameron Grant free(ch, M_DEVBUF); 550e4e61333SAriff Abdullah return (NULL); 551bbb5bf3dSCameron Grant } 552285648f9SCameron Grant 553e4e61333SAriff Abdullah return (ch); 554285648f9SCameron Grant } 555285648f9SCameron Grant 556285648f9SCameron Grant int 557285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 558285648f9SCameron Grant { 559cf8e3ea2SMateusz Guzik struct snddev_info *d __diagused; 560285648f9SCameron Grant int err; 561285648f9SCameron Grant 562a67fe5c1SCameron Grant d = ch->parentsnddev; 563e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 564e4e61333SAriff Abdullah 565285648f9SCameron Grant err = chn_kill(ch); 566285648f9SCameron Grant if (err) { 567e4e61333SAriff Abdullah device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n", 568e4e61333SAriff Abdullah ch->name, err); 569e4e61333SAriff Abdullah return (err); 570285648f9SCameron Grant } 571285648f9SCameron Grant 572285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 573285648f9SCameron Grant free(ch, M_DEVBUF); 574285648f9SCameron Grant 575e4e61333SAriff Abdullah return (0); 576285648f9SCameron Grant } 577285648f9SCameron Grant 578285648f9SCameron Grant int 5795ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 580285648f9SCameron Grant { 581e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 58290da2b28SAriff Abdullah PCM_LOCKASSERT(d); 583bba4862cSAriff Abdullah KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 584bba4862cSAriff Abdullah ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 585285648f9SCameron Grant 58690da2b28SAriff Abdullah CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); 5873fdb3676SAriff Abdullah 588e4e61333SAriff Abdullah switch (CHN_DEV(ch)) { 589e4e61333SAriff Abdullah case SND_DEV_DSPHW_PLAY: 590e4e61333SAriff Abdullah d->playcount++; 591e4e61333SAriff Abdullah break; 592e4e61333SAriff Abdullah case SND_DEV_DSPHW_VPLAY: 593e4e61333SAriff Abdullah d->pvchancount++; 594e4e61333SAriff Abdullah break; 595e4e61333SAriff Abdullah case SND_DEV_DSPHW_REC: 596e4e61333SAriff Abdullah d->reccount++; 597e4e61333SAriff Abdullah break; 598e4e61333SAriff Abdullah case SND_DEV_DSPHW_VREC: 599e4e61333SAriff Abdullah d->rvchancount++; 600e4e61333SAriff Abdullah break; 601e4e61333SAriff Abdullah default: 602e4e61333SAriff Abdullah break; 603e4e61333SAriff Abdullah } 604b8f0d9e0SCameron Grant 605e4e61333SAriff Abdullah d->devcount++; 606e4e61333SAriff Abdullah 607e4e61333SAriff Abdullah return (0); 60833dbf14aSCameron Grant } 60933dbf14aSCameron Grant 610285648f9SCameron Grant int 6115ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 61233dbf14aSCameron Grant { 613bba4862cSAriff Abdullah struct pcm_channel *tmp; 61433dbf14aSCameron Grant 615e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 61690da2b28SAriff Abdullah PCM_LOCKASSERT(d); 617e4e61333SAriff Abdullah 618bba4862cSAriff Abdullah tmp = NULL; 619a527dbc7SCameron Grant 620bba4862cSAriff Abdullah CHN_FOREACH(tmp, d, channels.pcm) { 621bba4862cSAriff Abdullah if (tmp == ch) 622bba4862cSAriff Abdullah break; 62333dbf14aSCameron Grant } 624bba4862cSAriff Abdullah 625bba4862cSAriff Abdullah if (tmp != ch) 626e4e61333SAriff Abdullah return (EINVAL); 62767beb5a5SCameron Grant 628bba4862cSAriff Abdullah CHN_REMOVE(d, ch, channels.pcm); 629e4e61333SAriff Abdullah 630bba4862cSAriff Abdullah switch (CHN_DEV(ch)) { 631bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 63267beb5a5SCameron Grant d->playcount--; 633bba4862cSAriff Abdullah break; 634bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 635bba4862cSAriff Abdullah d->pvchancount--; 636bba4862cSAriff Abdullah break; 637bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 638bba4862cSAriff Abdullah d->reccount--; 639bba4862cSAriff Abdullah break; 640bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 641bba4862cSAriff Abdullah d->rvchancount--; 642bba4862cSAriff Abdullah break; 643bba4862cSAriff Abdullah default: 644bba4862cSAriff Abdullah break; 645bba4862cSAriff Abdullah } 646285648f9SCameron Grant 647e4e61333SAriff Abdullah d->devcount--; 648e4e61333SAriff Abdullah 649e4e61333SAriff Abdullah return (0); 650987e5972SCameron Grant } 651987e5972SCameron Grant 652987e5972SCameron Grant int 653285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 654285648f9SCameron Grant { 655285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 65667b1dce3SCameron Grant struct pcm_channel *ch; 65767b1dce3SCameron Grant int err; 658285648f9SCameron Grant 659e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 660e4e61333SAriff Abdullah 66190da2b28SAriff Abdullah PCM_LOCK(d); 662bba4862cSAriff Abdullah ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); 663285648f9SCameron Grant if (!ch) { 664e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", 665e4e61333SAriff Abdullah cls->name, dir, devinfo); 66690da2b28SAriff Abdullah PCM_UNLOCK(d); 667e4e61333SAriff Abdullah return (ENODEV); 668285648f9SCameron Grant } 669cd9766c5SCameron Grant 6705ee30e27SMathew Kanner err = pcm_chn_add(d, ch); 67190da2b28SAriff Abdullah PCM_UNLOCK(d); 672285648f9SCameron Grant if (err) { 673e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", 674e4e61333SAriff Abdullah ch->name, err); 675285648f9SCameron Grant pcm_chn_destroy(ch); 676cd9766c5SCameron Grant } 677cd9766c5SCameron Grant 678e4e61333SAriff Abdullah return (err); 679285648f9SCameron Grant } 680285648f9SCameron Grant 681285648f9SCameron Grant static int 682285648f9SCameron Grant pcm_killchan(device_t dev) 683285648f9SCameron Grant { 684285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 685e33bee07SOlivier Houchard struct pcm_channel *ch; 686e4e61333SAriff Abdullah int error; 687e4e61333SAriff Abdullah 688e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 689285648f9SCameron Grant 690bba4862cSAriff Abdullah ch = CHN_FIRST(d, channels.pcm); 691285648f9SCameron Grant 69290da2b28SAriff Abdullah PCM_LOCK(d); 693bba4862cSAriff Abdullah error = pcm_chn_remove(d, ch); 69490da2b28SAriff Abdullah PCM_UNLOCK(d); 695e33bee07SOlivier Houchard if (error) 696e33bee07SOlivier Houchard return (error); 697e33bee07SOlivier Houchard return (pcm_chn_destroy(ch)); 698285648f9SCameron Grant } 699285648f9SCameron Grant 700cbebc90dSAlexander Motin static int 701cbebc90dSAlexander Motin pcm_best_unit(int old) 702cbebc90dSAlexander Motin { 703cbebc90dSAlexander Motin struct snddev_info *d; 704cbebc90dSAlexander Motin int i, best, bestprio, prio; 705cbebc90dSAlexander Motin 706cbebc90dSAlexander Motin best = -1; 707cbebc90dSAlexander Motin bestprio = -100; 708cbebc90dSAlexander Motin for (i = 0; pcm_devclass != NULL && 709cbebc90dSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 710cbebc90dSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 711cbebc90dSAlexander Motin if (!PCM_REGISTERED(d)) 712cbebc90dSAlexander Motin continue; 713cbebc90dSAlexander Motin prio = 0; 714cbebc90dSAlexander Motin if (d->playcount == 0) 715cbebc90dSAlexander Motin prio -= 10; 716cbebc90dSAlexander Motin if (d->reccount == 0) 717cbebc90dSAlexander Motin prio -= 2; 718cbebc90dSAlexander Motin if (prio > bestprio || (prio == bestprio && i == old)) { 719cbebc90dSAlexander Motin best = i; 720cbebc90dSAlexander Motin bestprio = prio; 721cbebc90dSAlexander Motin } 722cbebc90dSAlexander Motin } 723cbebc90dSAlexander Motin return (best); 724cbebc90dSAlexander Motin } 725cbebc90dSAlexander Motin 726285648f9SCameron Grant int 727987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 728987e5972SCameron Grant { 72966ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 73049c5e6e2SCameron Grant 73132a0e5d5SHans Petter Selasky /* should only be called once */ 73232a0e5d5SHans Petter Selasky if (d->flags & SD_F_REGISTERED) 73332a0e5d5SHans Petter Selasky return (EINVAL); 73432a0e5d5SHans Petter Selasky 735e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 736bba4862cSAriff Abdullah 737e4e61333SAriff Abdullah if (d->playcount == 0 || d->reccount == 0) 738e4e61333SAriff Abdullah d->flags |= SD_F_SIMPLEX; 739e4e61333SAriff Abdullah 74032a0e5d5SHans Petter Selasky if (d->playcount > 0 || d->reccount > 0) 741e4e61333SAriff Abdullah d->flags |= SD_F_AUTOVCHAN; 742e4e61333SAriff Abdullah 743e4e61333SAriff Abdullah pcm_setmaxautovchans(d, snd_maxautovchans); 744bba4862cSAriff Abdullah 745a580b31aSAriff Abdullah strlcpy(d->status, str, SND_STATUSLEN); 746bba4862cSAriff Abdullah 74790da2b28SAriff Abdullah PCM_LOCK(d); 748e4e61333SAriff Abdullah 749e4e61333SAriff Abdullah /* Done, we're ready.. */ 750e4e61333SAriff Abdullah d->flags |= SD_F_REGISTERED; 751e4e61333SAriff Abdullah 752e4e61333SAriff Abdullah PCM_RELEASE(d); 753bba4862cSAriff Abdullah 75490da2b28SAriff Abdullah PCM_UNLOCK(d); 755bba4862cSAriff Abdullah 75632a0e5d5SHans Petter Selasky /* 75732a0e5d5SHans Petter Selasky * Create all sysctls once SD_F_REGISTERED is set else 75832a0e5d5SHans Petter Selasky * tunable sysctls won't work: 75932a0e5d5SHans Petter Selasky */ 76032a0e5d5SHans Petter Selasky pcm_sysinit(dev); 76132a0e5d5SHans Petter Selasky 762cbebc90dSAlexander Motin if (snd_unit_auto < 0) 763cbebc90dSAlexander Motin snd_unit_auto = (snd_unit < 0) ? 1 : 0; 764cbebc90dSAlexander Motin if (snd_unit < 0 || snd_unit_auto > 1) 765f3685841SAriff Abdullah snd_unit = device_get_unit(dev); 766cbebc90dSAlexander Motin else if (snd_unit_auto == 1) 767cbebc90dSAlexander Motin snd_unit = pcm_best_unit(snd_unit); 768f3685841SAriff Abdullah 769e4e61333SAriff Abdullah return (0); 770987e5972SCameron Grant } 771987e5972SCameron Grant 772a1d444e1SAriff Abdullah uint32_t 773987e5972SCameron Grant pcm_getflags(device_t dev) 774987e5972SCameron Grant { 77566ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 77649c5e6e2SCameron Grant 777987e5972SCameron Grant return d->flags; 778987e5972SCameron Grant } 779987e5972SCameron Grant 780987e5972SCameron Grant void 781a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val) 782987e5972SCameron Grant { 78366ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 784d95502a8SCameron Grant 785987e5972SCameron Grant d->flags = val; 786987e5972SCameron Grant } 787987e5972SCameron Grant 78839004e69SCameron Grant void * 78939004e69SCameron Grant pcm_getdevinfo(device_t dev) 79039004e69SCameron Grant { 79166ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 79249c5e6e2SCameron Grant 79339004e69SCameron Grant return d->devinfo; 79439004e69SCameron Grant } 79539004e69SCameron Grant 796a67fe5c1SCameron Grant unsigned int 797d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 798a67fe5c1SCameron Grant { 799a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 8004e60be34SCameron Grant int sz, x; 801a67fe5c1SCameron Grant 802a67fe5c1SCameron Grant sz = 0; 8034e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 8044e60be34SCameron Grant x = sz; 805d55d96f6SAlexander Leidinger RANGE(sz, minbufsz, maxbufsz); 8064e60be34SCameron Grant if (x != sz) 807d55d96f6SAlexander Leidinger device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 808d55d96f6SAlexander Leidinger x = minbufsz; 8094e60be34SCameron Grant while (x < sz) 8104e60be34SCameron Grant x <<= 1; 8114e60be34SCameron Grant if (x > sz) 8124e60be34SCameron Grant x >>= 1; 8134e60be34SCameron Grant if (x != sz) { 8144e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 8154e60be34SCameron Grant sz = x; 8164e60be34SCameron Grant } 8174e60be34SCameron Grant } else { 818a67fe5c1SCameron Grant sz = deflt; 8194e60be34SCameron Grant } 8204e60be34SCameron Grant 821a67fe5c1SCameron Grant d->bufsz = sz; 822a67fe5c1SCameron Grant 823a67fe5c1SCameron Grant return sz; 824a67fe5c1SCameron Grant } 825a67fe5c1SCameron Grant 82690da2b28SAriff Abdullah static int 82790da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 82890da2b28SAriff Abdullah { 82990da2b28SAriff Abdullah struct snddev_info *d; 83090da2b28SAriff Abdullah int err, val; 83190da2b28SAriff Abdullah 83290da2b28SAriff Abdullah d = oidp->oid_arg1; 83390da2b28SAriff Abdullah if (!PCM_REGISTERED(d)) 83490da2b28SAriff Abdullah return (ENODEV); 83590da2b28SAriff Abdullah 83690da2b28SAriff Abdullah PCM_LOCK(d); 83790da2b28SAriff Abdullah PCM_WAIT(d); 83890da2b28SAriff Abdullah val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 83990da2b28SAriff Abdullah PCM_ACQUIRE(d); 84090da2b28SAriff Abdullah PCM_UNLOCK(d); 84190da2b28SAriff Abdullah 84290da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &val, 0, req); 84390da2b28SAriff Abdullah 84490da2b28SAriff Abdullah if (err == 0 && req->newptr != NULL) { 84590da2b28SAriff Abdullah if (!(val == 0 || val == 1)) { 84690da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 84790da2b28SAriff Abdullah return (EINVAL); 84890da2b28SAriff Abdullah } 84990da2b28SAriff Abdullah 85090da2b28SAriff Abdullah PCM_LOCK(d); 85190da2b28SAriff Abdullah 85290da2b28SAriff Abdullah d->flags &= ~SD_F_BITPERFECT; 85390da2b28SAriff Abdullah d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 85490da2b28SAriff Abdullah 85590da2b28SAriff Abdullah PCM_RELEASE(d); 85690da2b28SAriff Abdullah PCM_UNLOCK(d); 85790da2b28SAriff Abdullah } else 85890da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 85990da2b28SAriff Abdullah 86090da2b28SAriff Abdullah return (err); 86190da2b28SAriff Abdullah } 86290da2b28SAriff Abdullah 863ed2196e5SHans Petter Selasky static u_int8_t 864ed2196e5SHans Petter Selasky pcm_mode_init(struct snddev_info *d) 865ed2196e5SHans Petter Selasky { 866ed2196e5SHans Petter Selasky u_int8_t mode = 0; 867ed2196e5SHans Petter Selasky 868ed2196e5SHans Petter Selasky if (d->playcount > 0) 869ed2196e5SHans Petter Selasky mode |= PCM_MODE_PLAY; 870ed2196e5SHans Petter Selasky if (d->reccount > 0) 871ed2196e5SHans Petter Selasky mode |= PCM_MODE_REC; 872ed2196e5SHans Petter Selasky if (d->mixer_dev != NULL) 873ed2196e5SHans Petter Selasky mode |= PCM_MODE_MIXER; 874ed2196e5SHans Petter Selasky 875ed2196e5SHans Petter Selasky return (mode); 876ed2196e5SHans Petter Selasky } 877ed2196e5SHans Petter Selasky 87832a0e5d5SHans Petter Selasky static void 87932a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev) 88032a0e5d5SHans Petter Selasky { 88132a0e5d5SHans Petter Selasky struct snddev_info *d = device_get_softc(dev); 882ed2196e5SHans Petter Selasky u_int8_t mode; 883ed2196e5SHans Petter Selasky 884ed2196e5SHans Petter Selasky mode = pcm_mode_init(d); 88532a0e5d5SHans Petter Selasky 886132fca63SHans Petter Selasky /* XXX: a user should be able to set this with a control tool, the 88732a0e5d5SHans Petter Selasky sysadmin then needs min+max sysctls for this */ 88832a0e5d5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 88932a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 89032a0e5d5SHans Petter Selasky OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 89132a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 89232a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 8933b4c5433SAlexander Motin "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, 8947029da5cSPawel Biernacki sizeof(d), sysctl_dev_pcm_bitperfect, "I", 89532a0e5d5SHans Petter Selasky "bit-perfect playback/recording (0=disable, 1=enable)"); 896ed2196e5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 897ed2196e5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 898ed2196e5SHans Petter Selasky OID_AUTO, "mode", CTLFLAG_RD, NULL, mode, 899ed2196e5SHans Petter Selasky "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than one" 900ed2196e5SHans Petter Selasky "mode is supported)"); 90132a0e5d5SHans Petter Selasky if (d->flags & SD_F_AUTOVCHAN) 90232a0e5d5SHans Petter Selasky vchan_initsys(dev); 90332a0e5d5SHans Petter Selasky if (d->flags & SD_F_EQ) 90432a0e5d5SHans Petter Selasky feeder_eq_initsys(dev); 90532a0e5d5SHans Petter Selasky } 90632a0e5d5SHans Petter Selasky 907987e5972SCameron Grant int 908987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 909987e5972SCameron Grant { 910bba4862cSAriff Abdullah struct snddev_info *d; 91190da2b28SAriff Abdullah int i; 912987e5972SCameron Grant 913b8a36395SCameron Grant if (pcm_veto_load) { 914b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 915b8a36395SCameron Grant 916b8a36395SCameron Grant return EINVAL; 917b8a36395SCameron Grant } 918b8a36395SCameron Grant 919bba4862cSAriff Abdullah if (device_get_unit(dev) > PCMMAXUNIT) { 920bba4862cSAriff Abdullah device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n", 921bba4862cSAriff Abdullah device_get_unit(dev), PCMMAXUNIT); 922bba4862cSAriff Abdullah device_printf(dev, 923bba4862cSAriff Abdullah "Use 'hw.snd.maxunit' tunable to raise the limit.\n"); 924bba4862cSAriff Abdullah return ENODEV; 925bba4862cSAriff Abdullah } 926bba4862cSAriff Abdullah 927bba4862cSAriff Abdullah d = device_get_softc(dev); 928e4e61333SAriff Abdullah d->dev = dev; 9292c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 930e4e61333SAriff Abdullah cv_init(&d->cv, device_get_nameunit(dev)); 931e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 9327233ababSAlexander Leidinger #if 0 9337233ababSAlexander Leidinger /* 9347233ababSAlexander Leidinger * d->flags should be cleared by the allocator of the softc. 9357233ababSAlexander Leidinger * We cannot clear this field here because several devices set 9367233ababSAlexander Leidinger * this flag before calling pcm_register(). 9377233ababSAlexander Leidinger */ 938cd9766c5SCameron Grant d->flags = 0; 9397233ababSAlexander Leidinger #endif 94090da2b28SAriff Abdullah i = 0; 94190da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 94290da2b28SAriff Abdullah "vpc", &i) != 0 || i != 0) 94390da2b28SAriff Abdullah d->flags |= SD_F_VPC; 94490da2b28SAriff Abdullah 94590da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 94690da2b28SAriff Abdullah "bitperfect", &i) == 0 && i != 0) 94790da2b28SAriff Abdullah d->flags |= SD_F_BITPERFECT; 94890da2b28SAriff Abdullah 949987e5972SCameron Grant d->devinfo = devinfo; 950f637a36cSCameron Grant d->devcount = 0; 951506a5308SCameron Grant d->reccount = 0; 952a67fe5c1SCameron Grant d->playcount = 0; 953bba4862cSAriff Abdullah d->pvchancount = 0; 954bba4862cSAriff Abdullah d->rvchancount = 0; 955bba4862cSAriff Abdullah d->pvchanrate = 0; 956bba4862cSAriff Abdullah d->pvchanformat = 0; 957bba4862cSAriff Abdullah d->rvchanrate = 0; 958bba4862cSAriff Abdullah d->rvchanformat = 0; 959833f7023SCameron Grant 960bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm); 961bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm.busy); 96290da2b28SAriff Abdullah CHN_INIT(d, channels.pcm.opened); 96345550658SPoul-Henning Kamp 964e4e61333SAriff Abdullah /* XXX This is incorrect, but lets play along for now. */ 965a1d444e1SAriff Abdullah if ((numplay == 0 || numrec == 0) && numplay != numrec) 966285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 967d95502a8SCameron Grant 968bba4862cSAriff Abdullah sysctl_ctx_init(&d->play_sysctl_ctx); 969bba4862cSAriff Abdullah d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 970bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 9717029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node"); 972bba4862cSAriff Abdullah sysctl_ctx_init(&d->rec_sysctl_ctx); 973bba4862cSAriff Abdullah d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 974bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 975132fca63SHans Petter Selasky CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node"); 976e4e61333SAriff Abdullah 97732a0e5d5SHans Petter Selasky if (numplay > 0 || numrec > 0) 978cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 97990da2b28SAriff Abdullah 9809da3b645SChristos Margiolis sndstat_register(dev, d->status); 981e4e61333SAriff Abdullah 982e8c0d15aSChristos Margiolis return (dsp_make_dev(dev)); 983987e5972SCameron Grant } 984987e5972SCameron Grant 98533dbf14aSCameron Grant int 98633dbf14aSCameron Grant pcm_unregister(device_t dev) 9877c438dbeSCameron Grant { 988e4e61333SAriff Abdullah struct snddev_info *d; 989a67fe5c1SCameron Grant struct pcm_channel *ch; 9907c438dbeSCameron Grant 991e4e61333SAriff Abdullah d = device_get_softc(dev); 992e4e61333SAriff Abdullah 993e4e61333SAriff Abdullah if (!PCM_ALIVE(d)) { 994e4e61333SAriff Abdullah device_printf(dev, "unregister: device not configured\n"); 995e4e61333SAriff Abdullah return (0); 996e4e61333SAriff Abdullah } 997bba4862cSAriff Abdullah 99890da2b28SAriff Abdullah PCM_LOCK(d); 999e4e61333SAriff Abdullah PCM_WAIT(d); 1000e4e61333SAriff Abdullah 1001cc1efc23SHans Petter Selasky d->flags |= SD_F_DETACHING; 1002cc1efc23SHans Petter Selasky 1003e4e61333SAriff Abdullah PCM_ACQUIRE(d); 100490da2b28SAriff Abdullah PCM_UNLOCK(d); 1005e4e61333SAriff Abdullah 1006e4e61333SAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 1007e4e61333SAriff Abdullah CHN_LOCK(ch); 100844e128feSChristos Margiolis if (ch->flags & CHN_F_SLEEPING) { 100944e128feSChristos Margiolis /* 101044e128feSChristos Margiolis * We are detaching, so do not wait for the timeout in 101144e128feSChristos Margiolis * chn_read()/chn_write(). Wake up the thread and kill 101244e128feSChristos Margiolis * the channel immediately. 101344e128feSChristos Margiolis */ 101444e128feSChristos Margiolis CHN_BROADCAST(&ch->intr_cv); 101544e128feSChristos Margiolis ch->flags |= CHN_F_DEAD; 1016285648f9SCameron Grant } 101744e128feSChristos Margiolis chn_abort(ch); 1018e4e61333SAriff Abdullah CHN_UNLOCK(ch); 1019c9b53085SCameron Grant } 10205ee30e27SMathew Kanner 1021e8c0d15aSChristos Margiolis dsp_destroy_dev(dev); 1022bba4862cSAriff Abdullah 102344e128feSChristos Margiolis (void)mixer_uninit(dev); 10247233ababSAlexander Leidinger 10258461d581SHans Petter Selasky /* remove /dev/sndstat entry first */ 10268461d581SHans Petter Selasky sndstat_unregister(dev); 10278461d581SHans Petter Selasky 102890da2b28SAriff Abdullah PCM_LOCK(d); 1029e4e61333SAriff Abdullah d->flags |= SD_F_DYING; 1030e4e61333SAriff Abdullah d->flags &= ~SD_F_REGISTERED; 103190da2b28SAriff Abdullah PCM_UNLOCK(d); 1032e4e61333SAriff Abdullah 1033bba4862cSAriff Abdullah if (d->play_sysctl_tree != NULL) { 1034bba4862cSAriff Abdullah sysctl_ctx_free(&d->play_sysctl_ctx); 1035bba4862cSAriff Abdullah d->play_sysctl_tree = NULL; 1036bba4862cSAriff Abdullah } 1037bba4862cSAriff Abdullah if (d->rec_sysctl_tree != NULL) { 1038bba4862cSAriff Abdullah sysctl_ctx_free(&d->rec_sysctl_ctx); 1039bba4862cSAriff Abdullah d->rec_sysctl_tree = NULL; 1040a1d444e1SAriff Abdullah } 1041bba4862cSAriff Abdullah 1042bba4862cSAriff Abdullah while (!CHN_EMPTY(d, channels.pcm)) 1043285648f9SCameron Grant pcm_killchan(dev); 10447c438dbeSCameron Grant 104590da2b28SAriff Abdullah PCM_LOCK(d); 1046e4e61333SAriff Abdullah PCM_RELEASE(d); 1047e4e61333SAriff Abdullah cv_destroy(&d->cv); 104890da2b28SAriff Abdullah PCM_UNLOCK(d); 104949c5e6e2SCameron Grant snd_mtxfree(d->lock); 1050bba4862cSAriff Abdullah 1051bba4862cSAriff Abdullah if (snd_unit == device_get_unit(dev)) { 1052cbebc90dSAlexander Motin snd_unit = pcm_best_unit(-1); 1053cbebc90dSAlexander Motin if (snd_unit_auto == 0) 1054cbebc90dSAlexander Motin snd_unit_auto = 1; 1055e4e61333SAriff Abdullah } 1056bba4862cSAriff Abdullah 1057e4e61333SAriff Abdullah return (0); 105833dbf14aSCameron Grant } 10597c438dbeSCameron Grant 106067b1dce3SCameron Grant /************************************************************************/ 106167b1dce3SCameron Grant 1062b611c801SAlexander Leidinger /** 1063b611c801SAlexander Leidinger * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 1064b611c801SAlexander Leidinger * 1065b611c801SAlexander Leidinger * @param si Pointer to oss_sysinfo struct where information about the 1066b611c801SAlexander Leidinger * sound subsystem will be written/copied. 1067b611c801SAlexander Leidinger * 1068b611c801SAlexander Leidinger * This routine returns information about the sound system, such as the 1069b611c801SAlexander Leidinger * current OSS version, number of audio, MIDI, and mixer drivers, etc. 1070b611c801SAlexander Leidinger * Also includes a bitmask showing which of the above types of devices 1071b611c801SAlexander Leidinger * are open (busy). 1072b611c801SAlexander Leidinger * 1073b611c801SAlexander Leidinger * @note 1074b611c801SAlexander Leidinger * Calling threads must not hold any snddev_info or pcm_channel locks. 1075b611c801SAlexander Leidinger * 1076b611c801SAlexander Leidinger * @author Ryan Beasley <ryanb@FreeBSD.org> 1077b611c801SAlexander Leidinger */ 1078b611c801SAlexander Leidinger void 1079b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si) 1080b611c801SAlexander Leidinger { 1081b611c801SAlexander Leidinger static char si_product[] = "FreeBSD native OSS ABI"; 1082b611c801SAlexander Leidinger static char si_version[] = __XSTRING(__FreeBSD_version); 1083c412d503SAlexander Motin static char si_license[] = "BSD"; 1084b611c801SAlexander Leidinger static int intnbits = sizeof(int) * 8; /* Better suited as macro? 1085b611c801SAlexander Leidinger Must pester a C guru. */ 1086b611c801SAlexander Leidinger 1087b611c801SAlexander Leidinger struct snddev_info *d; 1088b611c801SAlexander Leidinger struct pcm_channel *c; 1089b611c801SAlexander Leidinger int i, j, ncards; 1090b611c801SAlexander Leidinger 1091b611c801SAlexander Leidinger ncards = 0; 1092b611c801SAlexander Leidinger 1093b611c801SAlexander Leidinger strlcpy(si->product, si_product, sizeof(si->product)); 1094b611c801SAlexander Leidinger strlcpy(si->version, si_version, sizeof(si->version)); 1095b611c801SAlexander Leidinger si->versionnum = SOUND_VERSION; 1096c412d503SAlexander Motin strlcpy(si->license, si_license, sizeof(si->license)); 1097b611c801SAlexander Leidinger 1098b611c801SAlexander Leidinger /* 1099b611c801SAlexander Leidinger * Iterate over PCM devices and their channels, gathering up data 1100b611c801SAlexander Leidinger * for the numaudios, ncards, and openedaudio fields. 1101b611c801SAlexander Leidinger */ 1102b611c801SAlexander Leidinger si->numaudios = 0; 1103b611c801SAlexander Leidinger bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 1104b611c801SAlexander Leidinger 1105b611c801SAlexander Leidinger j = 0; 1106b611c801SAlexander Leidinger 11079c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 11089c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 1109b611c801SAlexander Leidinger d = devclass_get_softc(pcm_devclass, i); 1110e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 1111b611c801SAlexander Leidinger continue; 1112b611c801SAlexander Leidinger 1113e4e61333SAriff Abdullah /* XXX Need Giant magic entry ??? */ 1114e4e61333SAriff Abdullah 1115b611c801SAlexander Leidinger /* See note in function's docblock */ 111690da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 111790da2b28SAriff Abdullah PCM_LOCK(d); 1118b611c801SAlexander Leidinger 1119b611c801SAlexander Leidinger si->numaudios += d->devcount; 1120b611c801SAlexander Leidinger ++ncards; 1121b611c801SAlexander Leidinger 1122bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 112390da2b28SAriff Abdullah CHN_UNLOCKASSERT(c); 1124b611c801SAlexander Leidinger CHN_LOCK(c); 1125b611c801SAlexander Leidinger if (c->flags & CHN_F_BUSY) 1126b611c801SAlexander Leidinger si->openedaudio[j / intnbits] |= 1127b611c801SAlexander Leidinger (1 << (j % intnbits)); 1128b611c801SAlexander Leidinger CHN_UNLOCK(c); 1129b611c801SAlexander Leidinger j++; 1130b611c801SAlexander Leidinger } 1131b611c801SAlexander Leidinger 113290da2b28SAriff Abdullah PCM_UNLOCK(d); 1133b611c801SAlexander Leidinger } 1134c412d503SAlexander Motin si->numaudioengines = si->numaudios; 1135b611c801SAlexander Leidinger 1136b611c801SAlexander Leidinger si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 1137b611c801SAlexander Leidinger /** 1138b611c801SAlexander Leidinger * @todo Collect num{midis,timers}. 1139b611c801SAlexander Leidinger * 1140b611c801SAlexander Leidinger * Need access to sound/midi/midi.c::midistat_lock in order 1141b611c801SAlexander Leidinger * to safely touch midi_devices and get a head count of, well, 1142b611c801SAlexander Leidinger * MIDI devices. midistat_lock is a global static (i.e., local to 1143b611c801SAlexander Leidinger * midi.c), but midi_devices is a regular global; should the mutex 1144b611c801SAlexander Leidinger * be publicized, or is there another way to get this information? 1145b611c801SAlexander Leidinger * 1146b611c801SAlexander Leidinger * NB: MIDI/sequencer stuff is currently on hold. 1147b611c801SAlexander Leidinger */ 1148b611c801SAlexander Leidinger si->nummidis = 0; 1149b611c801SAlexander Leidinger si->numtimers = 0; 1150b611c801SAlexander Leidinger si->nummixers = mixer_count; 1151b611c801SAlexander Leidinger si->numcards = ncards; 1152b611c801SAlexander Leidinger /* OSSv4 docs: Intended only for test apps; API doesn't 1153b611c801SAlexander Leidinger really have much of a concept of cards. Shouldn't be 1154b611c801SAlexander Leidinger used by applications. */ 1155b611c801SAlexander Leidinger 1156b611c801SAlexander Leidinger /** 1157b611c801SAlexander Leidinger * @todo Fill in "busy devices" fields. 1158b611c801SAlexander Leidinger * 1159b611c801SAlexander Leidinger * si->openedmidi = " MIDI devices 1160b611c801SAlexander Leidinger */ 1161b611c801SAlexander Leidinger bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 1162b611c801SAlexander Leidinger 1163b611c801SAlexander Leidinger /* 1164b611c801SAlexander Leidinger * Si->filler is a reserved array, but according to docs each 1165b611c801SAlexander Leidinger * element should be set to -1. 1166b611c801SAlexander Leidinger */ 1167b611c801SAlexander Leidinger for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++) 1168b611c801SAlexander Leidinger si->filler[i] = -1; 1169b611c801SAlexander Leidinger } 1170b611c801SAlexander Leidinger 117152f6e09eSAlexander Motin int 117252f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si) 117352f6e09eSAlexander Motin { 117452f6e09eSAlexander Motin struct snddev_info *d; 117552f6e09eSAlexander Motin int i, ncards; 117652f6e09eSAlexander Motin 117752f6e09eSAlexander Motin ncards = 0; 117852f6e09eSAlexander Motin 117952f6e09eSAlexander Motin for (i = 0; pcm_devclass != NULL && 118052f6e09eSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 118152f6e09eSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 118252f6e09eSAlexander Motin if (!PCM_REGISTERED(d)) 118352f6e09eSAlexander Motin continue; 118452f6e09eSAlexander Motin 118552f6e09eSAlexander Motin if (ncards++ != si->card) 118652f6e09eSAlexander Motin continue; 118752f6e09eSAlexander Motin 118890da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 118990da2b28SAriff Abdullah PCM_LOCK(d); 119052f6e09eSAlexander Motin 119152f6e09eSAlexander Motin strlcpy(si->shortname, device_get_nameunit(d->dev), 119252f6e09eSAlexander Motin sizeof(si->shortname)); 119352f6e09eSAlexander Motin strlcpy(si->longname, device_get_desc(d->dev), 119452f6e09eSAlexander Motin sizeof(si->longname)); 119552f6e09eSAlexander Motin strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 119652f6e09eSAlexander Motin si->intr_count = si->ack_count = 0; 119790da2b28SAriff Abdullah 119890da2b28SAriff Abdullah PCM_UNLOCK(d); 119990da2b28SAriff Abdullah 120052f6e09eSAlexander Motin return (0); 120152f6e09eSAlexander Motin } 120252f6e09eSAlexander Motin return (ENXIO); 120352f6e09eSAlexander Motin } 120452f6e09eSAlexander Motin 1205b611c801SAlexander Leidinger /************************************************************************/ 1206b611c801SAlexander Leidinger 12070739ea1dSSeigo Tanimura static int 12080739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data) 12090739ea1dSSeigo Tanimura { 1210b611c801SAlexander Leidinger int ret; 1211b611c801SAlexander Leidinger 12128109ec9dSJohn Baldwin ret = 0; 1213b611c801SAlexander Leidinger switch (type) { 1214b611c801SAlexander Leidinger case MOD_LOAD: 121513bebcd3SJohn Baldwin pcm_devclass = devclass_create("pcm"); 1216b611c801SAlexander Leidinger pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 1217b611c801SAlexander Leidinger break; 1218b611c801SAlexander Leidinger case MOD_UNLOAD: 1219b611c801SAlexander Leidinger if (pcmsg_unrhdr != NULL) { 1220b611c801SAlexander Leidinger delete_unrhdr(pcmsg_unrhdr); 1221b611c801SAlexander Leidinger pcmsg_unrhdr = NULL; 1222b611c801SAlexander Leidinger } 1223b611c801SAlexander Leidinger break; 12245525b53dSAlexander Motin case MOD_SHUTDOWN: 12255525b53dSAlexander Motin break; 1226b611c801SAlexander Leidinger default: 122790da2b28SAriff Abdullah ret = ENOTSUP; 1228b611c801SAlexander Leidinger } 1229b611c801SAlexander Leidinger 1230b611c801SAlexander Leidinger return ret; 12310739ea1dSSeigo Tanimura } 12320739ea1dSSeigo Tanimura 12330739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL); 12340739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER); 1235