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. 94f854658SChristos Margiolis * Copyright (c) 2024 The FreeBSD Foundation 104f854658SChristos Margiolis * 114f854658SChristos Margiolis * Portions of this software were developed by Christos Margiolis 124f854658SChristos 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, 247*25723d66SChristos Margiolis pid_t pid, char *comm) 248285648f9SCameron Grant { 249285648f9SCameron Grant struct pcm_channel *c; 25090da2b28SAriff Abdullah int err, vchancount, vchan_num; 251*25723d66SChristos Margiolis bool retry; 252bba4862cSAriff Abdullah 253*25723d66SChristos Margiolis KASSERT(d != NULL && ch != NULL && 254bba4862cSAriff Abdullah (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 255*25723d66SChristos Margiolis ("%s(): invalid d=%p ch=%p direction=%d pid=%d", 256*25723d66SChristos Margiolis __func__, d, ch, direction, pid)); 257e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 258bba4862cSAriff Abdullah 25990da2b28SAriff Abdullah *ch = NULL; 26090da2b28SAriff Abdullah vchan_num = 0; 26190da2b28SAriff Abdullah vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : 26290da2b28SAriff Abdullah d->rvchancount; 26390da2b28SAriff Abdullah 264*25723d66SChristos Margiolis retry = false; 2653fdb3676SAriff Abdullah retry_chnalloc: 26690da2b28SAriff Abdullah err = ENOTSUP; 267f637a36cSCameron Grant /* scan for a free channel */ 268bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 26949c5e6e2SCameron Grant CHN_LOCK(c); 270*25723d66SChristos Margiolis if (c->direction == direction && (c->flags & CHN_F_VIRTUAL)) { 27190da2b28SAriff Abdullah if (vchancount < snd_maxautovchans && 272*25723d66SChristos Margiolis vchan_num < c->unit) { 27390da2b28SAriff Abdullah CHN_UNLOCK(c); 27490da2b28SAriff Abdullah goto vchan_alloc; 27590da2b28SAriff Abdullah } 27690da2b28SAriff Abdullah vchan_num++; 27790da2b28SAriff Abdullah } 278*25723d66SChristos Margiolis if (c->direction == direction && !(c->flags & CHN_F_BUSY)) { 279285648f9SCameron Grant c->flags |= CHN_F_BUSY; 280b8f0d9e0SCameron Grant c->pid = pid; 28190da2b28SAriff Abdullah strlcpy(c->comm, (comm != NULL) ? comm : 28290da2b28SAriff Abdullah CHN_COMM_UNKNOWN, sizeof(c->comm)); 2833fdb3676SAriff Abdullah *ch = c; 284bba4862cSAriff Abdullah return (0); 285*25723d66SChristos Margiolis } else if (c->direction == direction && (c->flags & CHN_F_BUSY)) 286a1d444e1SAriff Abdullah err = EBUSY; 28749c5e6e2SCameron Grant CHN_UNLOCK(c); 288285648f9SCameron Grant } 289f637a36cSCameron Grant 290*25723d66SChristos Margiolis /* 291*25723d66SChristos Margiolis * We came from retry_chnalloc and still didn't find a free channel. 292*25723d66SChristos Margiolis */ 293*25723d66SChristos Margiolis if (retry) 294e4e61333SAriff Abdullah return (err); 295e4e61333SAriff Abdullah 29690da2b28SAriff Abdullah vchan_alloc: 297f637a36cSCameron Grant /* no channel available */ 298*25723d66SChristos Margiolis if (!(vchancount > 0 && vchancount < snd_maxautovchans)) 299bba4862cSAriff Abdullah return (err); 300*25723d66SChristos Margiolis err = pcm_setvchans(d, direction, vchancount + 1, -1); 301a1d444e1SAriff Abdullah if (err == 0) { 302*25723d66SChristos Margiolis retry = true; 3033fdb3676SAriff Abdullah goto retry_chnalloc; 304f637a36cSCameron Grant } 305f637a36cSCameron Grant 306bba4862cSAriff Abdullah return (err); 307285648f9SCameron Grant } 308285648f9SCameron Grant 309b8f0d9e0SCameron Grant /* release a locked channel and unlock it */ 310285648f9SCameron Grant int 311b8f0d9e0SCameron Grant pcm_chnrelease(struct pcm_channel *c) 312285648f9SCameron Grant { 313e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 314b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 315e4e61333SAriff Abdullah 316285648f9SCameron Grant c->flags &= ~CHN_F_BUSY; 317b8f0d9e0SCameron Grant c->pid = -1; 31890da2b28SAriff Abdullah strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm)); 31949c5e6e2SCameron Grant CHN_UNLOCK(c); 320e4e61333SAriff Abdullah 321e4e61333SAriff Abdullah return (0); 322285648f9SCameron Grant } 323285648f9SCameron Grant 324285648f9SCameron Grant int 325285648f9SCameron Grant pcm_chnref(struct pcm_channel *c, int ref) 326285648f9SCameron Grant { 327e4e61333SAriff Abdullah PCM_BUSYASSERT(c->parentsnddev); 328b8f0d9e0SCameron Grant CHN_LOCKASSERT(c); 329e4e61333SAriff Abdullah 330285648f9SCameron Grant c->refcount += ref; 331e4e61333SAriff Abdullah 332e4e61333SAriff Abdullah return (c->refcount); 333285648f9SCameron Grant } 334285648f9SCameron Grant 33567b1dce3SCameron Grant static void 33667b1dce3SCameron Grant pcm_setmaxautovchans(struct snddev_info *d, int num) 33767b1dce3SCameron Grant { 338e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 339e4e61333SAriff Abdullah 340bba4862cSAriff Abdullah if (num < 0) 341bba4862cSAriff Abdullah return; 342bba4862cSAriff Abdullah 343bba4862cSAriff Abdullah if (num >= 0 && d->pvchancount > num) 344bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); 345bba4862cSAriff Abdullah else if (num > 0 && d->pvchancount == 0) 346bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); 347bba4862cSAriff Abdullah 348bba4862cSAriff Abdullah if (num >= 0 && d->rvchancount > num) 349bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, num, -1); 350bba4862cSAriff Abdullah else if (num > 0 && d->rvchancount == 0) 351bba4862cSAriff Abdullah (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); 35267b1dce3SCameron Grant } 35367b1dce3SCameron Grant 35433dbf14aSCameron Grant static int 355851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 35633dbf14aSCameron Grant { 357b8f0d9e0SCameron Grant struct snddev_info *d; 35833dbf14aSCameron Grant int error, unit; 35933dbf14aSCameron Grant 36033dbf14aSCameron Grant unit = snd_unit; 361041b706bSDavid Malone error = sysctl_handle_int(oidp, &unit, 0, req); 36233dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 363b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 364e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 365b8f0d9e0SCameron Grant return EINVAL; 36633dbf14aSCameron Grant snd_unit = unit; 367cbebc90dSAlexander Motin snd_unit_auto = 0; 36833dbf14aSCameron Grant } 36933dbf14aSCameron Grant return (error); 37033dbf14aSCameron Grant } 371851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */ 372b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, 3737029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0, 3747029da5cSPawel Biernacki sizeof(int), sysctl_hw_snd_default_unit, "I", 375b7d6c6b5SEitan Adler "default sound device"); 376987e5972SCameron Grant 377cd9766c5SCameron Grant static int 37867b1dce3SCameron Grant sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 379cd9766c5SCameron Grant { 38067b1dce3SCameron Grant struct snddev_info *d; 38167b1dce3SCameron Grant int i, v, error; 382cd9766c5SCameron Grant 38367b1dce3SCameron Grant v = snd_maxautovchans; 384041b706bSDavid Malone error = sysctl_handle_int(oidp, &v, 0, req); 385cd9766c5SCameron Grant if (error == 0 && req->newptr != NULL) { 386bba4862cSAriff Abdullah if (v < 0) 387bba4862cSAriff Abdullah v = 0; 388bba4862cSAriff Abdullah if (v > SND_MAXVCHANS) 389bba4862cSAriff Abdullah v = SND_MAXVCHANS; 390bba4862cSAriff Abdullah snd_maxautovchans = v; 3919c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 3929c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 39367b1dce3SCameron Grant d = devclass_get_softc(pcm_devclass, i); 394e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 39567b1dce3SCameron Grant continue; 396e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 39767b1dce3SCameron Grant pcm_setmaxautovchans(d, v); 398e4e61333SAriff Abdullah PCM_RELEASE_QUICK(d); 39967b1dce3SCameron Grant } 40067b1dce3SCameron Grant } 401cd9766c5SCameron Grant return (error); 402cd9766c5SCameron Grant } 4037029da5cSPawel Biernacki SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, 4047029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 4057029da5cSPawel Biernacki sysctl_hw_snd_maxautovchans, "I", 4067029da5cSPawel Biernacki "maximum virtual channel"); 407f637a36cSCameron Grant 408285648f9SCameron Grant struct pcm_channel * 409bba4862cSAriff Abdullah pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo) 410987e5972SCameron Grant { 411bba4862cSAriff Abdullah struct pcm_channel *ch; 412bba4862cSAriff Abdullah int direction, err, rpnum, *pnum, max; 413*25723d66SChristos Margiolis int type, unit; 414bba4862cSAriff Abdullah char *dirs, *devname, buf[CHN_NAMELEN]; 415bba4862cSAriff Abdullah 416e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 41790da2b28SAriff Abdullah PCM_LOCKASSERT(d); 418bba4862cSAriff Abdullah KASSERT(num >= -1, ("invalid num=%d", num)); 419bba4862cSAriff Abdullah 420285648f9SCameron Grant switch (dir) { 421285648f9SCameron Grant case PCMDIR_PLAY: 422285648f9SCameron Grant dirs = "play"; 423a3193a9cSDon Lewis direction = PCMDIR_PLAY; 424a67fe5c1SCameron Grant pnum = &d->playcount; 425*25723d66SChristos Margiolis type = SND_DEV_DSPHW_PLAY; 426bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 427285648f9SCameron Grant break; 428bba4862cSAriff Abdullah case PCMDIR_PLAY_VIRTUAL: 429d0032e6aSChristos Margiolis dirs = "virtual_play"; 430bba4862cSAriff Abdullah direction = PCMDIR_PLAY; 431bba4862cSAriff Abdullah pnum = &d->pvchancount; 432*25723d66SChristos Margiolis type = SND_DEV_DSPHW_VPLAY; 433bba4862cSAriff Abdullah max = SND_MAXVCHANS; 434bba4862cSAriff Abdullah break; 435285648f9SCameron Grant case PCMDIR_REC: 436285648f9SCameron Grant dirs = "record"; 437a3193a9cSDon Lewis direction = PCMDIR_REC; 438a67fe5c1SCameron Grant pnum = &d->reccount; 439*25723d66SChristos Margiolis type = SND_DEV_DSPHW_REC; 440bba4862cSAriff Abdullah max = SND_MAXHWCHAN; 441285648f9SCameron Grant break; 442bba4862cSAriff Abdullah case PCMDIR_REC_VIRTUAL: 443d0032e6aSChristos Margiolis dirs = "virtual_record"; 444bba4862cSAriff Abdullah direction = PCMDIR_REC; 445bba4862cSAriff Abdullah pnum = &d->rvchancount; 446*25723d66SChristos Margiolis type = SND_DEV_DSPHW_VREC; 447bba4862cSAriff Abdullah max = SND_MAXVCHANS; 448285648f9SCameron Grant break; 449285648f9SCameron Grant default: 450e4e61333SAriff Abdullah return (NULL); 4519c326820SCameron Grant } 452285648f9SCameron Grant 453*25723d66SChristos Margiolis unit = (num == -1) ? 0 : num; 454285648f9SCameron Grant 455*25723d66SChristos Margiolis if (*pnum >= max || unit >= max) 456e4e61333SAriff Abdullah return (NULL); 457bba4862cSAriff Abdullah 4583fdb3676SAriff Abdullah rpnum = 0; 459bba4862cSAriff Abdullah 460bba4862cSAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 461*25723d66SChristos Margiolis if (ch->type != type) 4623fdb3676SAriff Abdullah continue; 463*25723d66SChristos Margiolis if (unit == ch->unit && num != -1) { 4643fdb3676SAriff Abdullah device_printf(d->dev, 465*25723d66SChristos Margiolis "channel num=%d allocated!\n", unit); 466e4e61333SAriff Abdullah return (NULL); 467bba4862cSAriff Abdullah } 468*25723d66SChristos Margiolis unit++; 469*25723d66SChristos Margiolis if (unit >= max) { 470bba4862cSAriff Abdullah device_printf(d->dev, 471*25723d66SChristos Margiolis "chan=%d > %d\n", unit, max); 472e4e61333SAriff Abdullah return (NULL); 473bba4862cSAriff Abdullah } 4743fdb3676SAriff Abdullah rpnum++; 4753fdb3676SAriff Abdullah } 476bba4862cSAriff Abdullah 4773fdb3676SAriff Abdullah if (*pnum != rpnum) { 4783fdb3676SAriff Abdullah device_printf(d->dev, 479bba4862cSAriff Abdullah "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", 4803fdb3676SAriff Abdullah __func__, dirs, *pnum, rpnum); 481e4e61333SAriff Abdullah return (NULL); 4823fdb3676SAriff Abdullah } 483a67fe5c1SCameron Grant 48490da2b28SAriff Abdullah PCM_UNLOCK(d); 485bba4862cSAriff Abdullah ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 486bba4862cSAriff Abdullah ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); 487*25723d66SChristos Margiolis ch->type = type; 488*25723d66SChristos Margiolis ch->unit = unit; 489285648f9SCameron Grant ch->pid = -1; 49090da2b28SAriff Abdullah strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm)); 491285648f9SCameron Grant ch->parentsnddev = d; 492285648f9SCameron Grant ch->parentchannel = parent; 493436c9b65SScott Long ch->dev = d->dev; 494bba4862cSAriff Abdullah ch->trigger = PCMTRIG_STOP; 495*25723d66SChristos Margiolis devname = dsp_unit2name(buf, sizeof(buf), ch); 496*25723d66SChristos Margiolis if (devname == NULL) { 497*25723d66SChristos Margiolis device_printf(d->dev, "Failed to query device name"); 498*25723d66SChristos Margiolis kobj_delete(ch->methods, M_DEVBUF); 499*25723d66SChristos Margiolis free(ch, M_DEVBUF); 500*25723d66SChristos Margiolis return (NULL); 501*25723d66SChristos Margiolis } 502bba4862cSAriff Abdullah snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", 503bba4862cSAriff Abdullah device_get_nameunit(ch->dev), dirs, devname); 504285648f9SCameron Grant 505a3193a9cSDon Lewis err = chn_init(ch, devinfo, dir, direction); 50690da2b28SAriff Abdullah PCM_LOCK(d); 5070f55ac6cSCameron Grant if (err) { 508bba4862cSAriff Abdullah device_printf(d->dev, "chn_init(%s) failed: err = %d\n", 509bba4862cSAriff Abdullah ch->name, err); 510285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 511285648f9SCameron Grant free(ch, M_DEVBUF); 512e4e61333SAriff Abdullah return (NULL); 513bbb5bf3dSCameron Grant } 514285648f9SCameron Grant 515e4e61333SAriff Abdullah return (ch); 516285648f9SCameron Grant } 517285648f9SCameron Grant 518285648f9SCameron Grant int 519285648f9SCameron Grant pcm_chn_destroy(struct pcm_channel *ch) 520285648f9SCameron Grant { 521cf8e3ea2SMateusz Guzik struct snddev_info *d __diagused; 522285648f9SCameron Grant int err; 523285648f9SCameron Grant 524a67fe5c1SCameron Grant d = ch->parentsnddev; 525e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 526e4e61333SAriff Abdullah 527285648f9SCameron Grant err = chn_kill(ch); 528285648f9SCameron Grant if (err) { 529e4e61333SAriff Abdullah device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n", 530e4e61333SAriff Abdullah ch->name, err); 531e4e61333SAriff Abdullah return (err); 532285648f9SCameron Grant } 533285648f9SCameron Grant 534285648f9SCameron Grant kobj_delete(ch->methods, M_DEVBUF); 535285648f9SCameron Grant free(ch, M_DEVBUF); 536285648f9SCameron Grant 537e4e61333SAriff Abdullah return (0); 538285648f9SCameron Grant } 539285648f9SCameron Grant 540285648f9SCameron Grant int 5415ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 542285648f9SCameron Grant { 543e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 54490da2b28SAriff Abdullah PCM_LOCKASSERT(d); 545bba4862cSAriff Abdullah KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 546bba4862cSAriff Abdullah ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 547285648f9SCameron Grant 54890da2b28SAriff Abdullah CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); 5493fdb3676SAriff Abdullah 550*25723d66SChristos Margiolis switch (ch->type) { 551e4e61333SAriff Abdullah case SND_DEV_DSPHW_PLAY: 552e4e61333SAriff Abdullah d->playcount++; 553e4e61333SAriff Abdullah break; 554e4e61333SAriff Abdullah case SND_DEV_DSPHW_VPLAY: 555e4e61333SAriff Abdullah d->pvchancount++; 556e4e61333SAriff Abdullah break; 557e4e61333SAriff Abdullah case SND_DEV_DSPHW_REC: 558e4e61333SAriff Abdullah d->reccount++; 559e4e61333SAriff Abdullah break; 560e4e61333SAriff Abdullah case SND_DEV_DSPHW_VREC: 561e4e61333SAriff Abdullah d->rvchancount++; 562e4e61333SAriff Abdullah break; 563e4e61333SAriff Abdullah default: 564e4e61333SAriff Abdullah break; 565e4e61333SAriff Abdullah } 566b8f0d9e0SCameron Grant 567e4e61333SAriff Abdullah return (0); 56833dbf14aSCameron Grant } 56933dbf14aSCameron Grant 570285648f9SCameron Grant int 5715ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 57233dbf14aSCameron Grant { 573bba4862cSAriff Abdullah struct pcm_channel *tmp; 57433dbf14aSCameron Grant 575e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 57690da2b28SAriff Abdullah PCM_LOCKASSERT(d); 577e4e61333SAriff Abdullah 578bba4862cSAriff Abdullah tmp = NULL; 579a527dbc7SCameron Grant 580bba4862cSAriff Abdullah CHN_FOREACH(tmp, d, channels.pcm) { 581bba4862cSAriff Abdullah if (tmp == ch) 582bba4862cSAriff Abdullah break; 58333dbf14aSCameron Grant } 584bba4862cSAriff Abdullah 585bba4862cSAriff Abdullah if (tmp != ch) 586e4e61333SAriff Abdullah return (EINVAL); 58767beb5a5SCameron Grant 588bba4862cSAriff Abdullah CHN_REMOVE(d, ch, channels.pcm); 589e4e61333SAriff Abdullah 590*25723d66SChristos Margiolis switch (ch->type) { 591bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 59267beb5a5SCameron Grant d->playcount--; 593bba4862cSAriff Abdullah break; 594bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 595bba4862cSAriff Abdullah d->pvchancount--; 596bba4862cSAriff Abdullah break; 597bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 598bba4862cSAriff Abdullah d->reccount--; 599bba4862cSAriff Abdullah break; 600bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 601bba4862cSAriff Abdullah d->rvchancount--; 602bba4862cSAriff Abdullah break; 603bba4862cSAriff Abdullah default: 604bba4862cSAriff Abdullah break; 605bba4862cSAriff Abdullah } 606285648f9SCameron Grant 607e4e61333SAriff Abdullah return (0); 608987e5972SCameron Grant } 609987e5972SCameron Grant 610987e5972SCameron Grant int 611285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 612285648f9SCameron Grant { 613285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 61467b1dce3SCameron Grant struct pcm_channel *ch; 61567b1dce3SCameron Grant int err; 616285648f9SCameron Grant 617e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 618e4e61333SAriff Abdullah 61990da2b28SAriff Abdullah PCM_LOCK(d); 620bba4862cSAriff Abdullah ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); 621285648f9SCameron Grant if (!ch) { 622e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", 623e4e61333SAriff Abdullah cls->name, dir, devinfo); 62490da2b28SAriff Abdullah PCM_UNLOCK(d); 625e4e61333SAriff Abdullah return (ENODEV); 626285648f9SCameron Grant } 627cd9766c5SCameron Grant 6285ee30e27SMathew Kanner err = pcm_chn_add(d, ch); 62990da2b28SAriff Abdullah PCM_UNLOCK(d); 630285648f9SCameron Grant if (err) { 631e4e61333SAriff Abdullah device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", 632e4e61333SAriff Abdullah ch->name, err); 633285648f9SCameron Grant pcm_chn_destroy(ch); 634cd9766c5SCameron Grant } 635cd9766c5SCameron Grant 636e4e61333SAriff Abdullah return (err); 637285648f9SCameron Grant } 638285648f9SCameron Grant 63903614fcbSChristos Margiolis static void 64003614fcbSChristos Margiolis pcm_killchans(struct snddev_info *d) 641285648f9SCameron Grant { 642e33bee07SOlivier Houchard struct pcm_channel *ch; 643e4e61333SAriff Abdullah int error; 64403614fcbSChristos Margiolis bool found; 645e4e61333SAriff Abdullah 646e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 64703614fcbSChristos Margiolis do { 64803614fcbSChristos Margiolis found = false; 64903614fcbSChristos Margiolis CHN_FOREACH(ch, d, channels.pcm) { 65003614fcbSChristos Margiolis CHN_LOCK(ch); 65103614fcbSChristos Margiolis /* 65203614fcbSChristos Margiolis * Make sure no channel has went to sleep in the 65303614fcbSChristos Margiolis * meantime. 65403614fcbSChristos Margiolis */ 65503614fcbSChristos Margiolis chn_shutdown(ch); 65603614fcbSChristos Margiolis /* 65703614fcbSChristos Margiolis * We have to give a thread sleeping in chn_sleep() a 65803614fcbSChristos Margiolis * chance to observe that the channel is dead. 65903614fcbSChristos Margiolis */ 66003614fcbSChristos Margiolis if ((ch->flags & CHN_F_SLEEPING) == 0) { 66103614fcbSChristos Margiolis found = true; 66203614fcbSChristos Margiolis CHN_UNLOCK(ch); 66303614fcbSChristos Margiolis break; 66403614fcbSChristos Margiolis } 66503614fcbSChristos Margiolis CHN_UNLOCK(ch); 66603614fcbSChristos Margiolis } 667285648f9SCameron Grant 66803614fcbSChristos Margiolis /* 66903614fcbSChristos Margiolis * All channels are still sleeping. Sleep for a bit and try 67003614fcbSChristos Margiolis * again to see if any of them is awake now. 67103614fcbSChristos Margiolis */ 67203614fcbSChristos Margiolis if (!found) { 67303614fcbSChristos Margiolis pause_sbt("pcmkillchans", SBT_1MS * 5, 0, 0); 67403614fcbSChristos Margiolis continue; 67503614fcbSChristos Margiolis } 676285648f9SCameron Grant 67790da2b28SAriff Abdullah PCM_LOCK(d); 678bba4862cSAriff Abdullah error = pcm_chn_remove(d, ch); 67990da2b28SAriff Abdullah PCM_UNLOCK(d); 68003614fcbSChristos Margiolis if (error == 0) 68103614fcbSChristos Margiolis pcm_chn_destroy(ch); 68203614fcbSChristos Margiolis } while (!CHN_EMPTY(d, channels.pcm)); 683285648f9SCameron Grant } 684285648f9SCameron Grant 685cbebc90dSAlexander Motin static int 686cbebc90dSAlexander Motin pcm_best_unit(int old) 687cbebc90dSAlexander Motin { 688cbebc90dSAlexander Motin struct snddev_info *d; 689cbebc90dSAlexander Motin int i, best, bestprio, prio; 690cbebc90dSAlexander Motin 691cbebc90dSAlexander Motin best = -1; 692cbebc90dSAlexander Motin bestprio = -100; 693cbebc90dSAlexander Motin for (i = 0; pcm_devclass != NULL && 694cbebc90dSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 695cbebc90dSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 696cbebc90dSAlexander Motin if (!PCM_REGISTERED(d)) 697cbebc90dSAlexander Motin continue; 698cbebc90dSAlexander Motin prio = 0; 699cbebc90dSAlexander Motin if (d->playcount == 0) 700cbebc90dSAlexander Motin prio -= 10; 701cbebc90dSAlexander Motin if (d->reccount == 0) 702cbebc90dSAlexander Motin prio -= 2; 703cbebc90dSAlexander Motin if (prio > bestprio || (prio == bestprio && i == old)) { 704cbebc90dSAlexander Motin best = i; 705cbebc90dSAlexander Motin bestprio = prio; 706cbebc90dSAlexander Motin } 707cbebc90dSAlexander Motin } 708cbebc90dSAlexander Motin return (best); 709cbebc90dSAlexander Motin } 710cbebc90dSAlexander Motin 711285648f9SCameron Grant int 712987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 713987e5972SCameron Grant { 71466ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 71549c5e6e2SCameron Grant 71632a0e5d5SHans Petter Selasky /* should only be called once */ 71732a0e5d5SHans Petter Selasky if (d->flags & SD_F_REGISTERED) 71832a0e5d5SHans Petter Selasky return (EINVAL); 71932a0e5d5SHans Petter Selasky 720e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 721bba4862cSAriff Abdullah 722e4e61333SAriff Abdullah if (d->playcount == 0 || d->reccount == 0) 723e4e61333SAriff Abdullah d->flags |= SD_F_SIMPLEX; 724e4e61333SAriff Abdullah 72532a0e5d5SHans Petter Selasky if (d->playcount > 0 || d->reccount > 0) 726e4e61333SAriff Abdullah d->flags |= SD_F_AUTOVCHAN; 727e4e61333SAriff Abdullah 728e4e61333SAriff Abdullah pcm_setmaxautovchans(d, snd_maxautovchans); 729bba4862cSAriff Abdullah 730a580b31aSAriff Abdullah strlcpy(d->status, str, SND_STATUSLEN); 731bba4862cSAriff Abdullah 73290da2b28SAriff Abdullah PCM_LOCK(d); 733e4e61333SAriff Abdullah 734e4e61333SAriff Abdullah /* Done, we're ready.. */ 735e4e61333SAriff Abdullah d->flags |= SD_F_REGISTERED; 736e4e61333SAriff Abdullah 737e4e61333SAriff Abdullah PCM_RELEASE(d); 738bba4862cSAriff Abdullah 73990da2b28SAriff Abdullah PCM_UNLOCK(d); 740bba4862cSAriff Abdullah 74132a0e5d5SHans Petter Selasky /* 74232a0e5d5SHans Petter Selasky * Create all sysctls once SD_F_REGISTERED is set else 74332a0e5d5SHans Petter Selasky * tunable sysctls won't work: 74432a0e5d5SHans Petter Selasky */ 74532a0e5d5SHans Petter Selasky pcm_sysinit(dev); 74632a0e5d5SHans Petter Selasky 747cbebc90dSAlexander Motin if (snd_unit_auto < 0) 748cbebc90dSAlexander Motin snd_unit_auto = (snd_unit < 0) ? 1 : 0; 749cbebc90dSAlexander Motin if (snd_unit < 0 || snd_unit_auto > 1) 750f3685841SAriff Abdullah snd_unit = device_get_unit(dev); 751cbebc90dSAlexander Motin else if (snd_unit_auto == 1) 752cbebc90dSAlexander Motin snd_unit = pcm_best_unit(snd_unit); 753f3685841SAriff Abdullah 754e4e61333SAriff Abdullah return (0); 755987e5972SCameron Grant } 756987e5972SCameron Grant 757a1d444e1SAriff Abdullah uint32_t 758987e5972SCameron Grant pcm_getflags(device_t dev) 759987e5972SCameron Grant { 76066ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 76149c5e6e2SCameron Grant 762987e5972SCameron Grant return d->flags; 763987e5972SCameron Grant } 764987e5972SCameron Grant 765987e5972SCameron Grant void 766a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val) 767987e5972SCameron Grant { 76866ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 769d95502a8SCameron Grant 770987e5972SCameron Grant d->flags = val; 771987e5972SCameron Grant } 772987e5972SCameron Grant 77339004e69SCameron Grant void * 77439004e69SCameron Grant pcm_getdevinfo(device_t dev) 77539004e69SCameron Grant { 77666ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 77749c5e6e2SCameron Grant 77839004e69SCameron Grant return d->devinfo; 77939004e69SCameron Grant } 78039004e69SCameron Grant 781a67fe5c1SCameron Grant unsigned int 782d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 783a67fe5c1SCameron Grant { 784a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 7854e60be34SCameron Grant int sz, x; 786a67fe5c1SCameron Grant 787a67fe5c1SCameron Grant sz = 0; 7884e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 7894e60be34SCameron Grant x = sz; 790d55d96f6SAlexander Leidinger RANGE(sz, minbufsz, maxbufsz); 7914e60be34SCameron Grant if (x != sz) 792d55d96f6SAlexander Leidinger device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 793d55d96f6SAlexander Leidinger x = minbufsz; 7944e60be34SCameron Grant while (x < sz) 7954e60be34SCameron Grant x <<= 1; 7964e60be34SCameron Grant if (x > sz) 7974e60be34SCameron Grant x >>= 1; 7984e60be34SCameron Grant if (x != sz) { 7994e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 8004e60be34SCameron Grant sz = x; 8014e60be34SCameron Grant } 8024e60be34SCameron Grant } else { 803a67fe5c1SCameron Grant sz = deflt; 8044e60be34SCameron Grant } 8054e60be34SCameron Grant 806a67fe5c1SCameron Grant d->bufsz = sz; 807a67fe5c1SCameron Grant 808a67fe5c1SCameron Grant return sz; 809a67fe5c1SCameron Grant } 810a67fe5c1SCameron Grant 81190da2b28SAriff Abdullah static int 81290da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 81390da2b28SAriff Abdullah { 81490da2b28SAriff Abdullah struct snddev_info *d; 81590da2b28SAriff Abdullah int err, val; 81690da2b28SAriff Abdullah 81790da2b28SAriff Abdullah d = oidp->oid_arg1; 81890da2b28SAriff Abdullah if (!PCM_REGISTERED(d)) 81990da2b28SAriff Abdullah return (ENODEV); 82090da2b28SAriff Abdullah 82190da2b28SAriff Abdullah PCM_LOCK(d); 82290da2b28SAriff Abdullah PCM_WAIT(d); 82390da2b28SAriff Abdullah val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 82490da2b28SAriff Abdullah PCM_ACQUIRE(d); 82590da2b28SAriff Abdullah PCM_UNLOCK(d); 82690da2b28SAriff Abdullah 82790da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &val, 0, req); 82890da2b28SAriff Abdullah 82990da2b28SAriff Abdullah if (err == 0 && req->newptr != NULL) { 83090da2b28SAriff Abdullah if (!(val == 0 || val == 1)) { 83190da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 83290da2b28SAriff Abdullah return (EINVAL); 83390da2b28SAriff Abdullah } 83490da2b28SAriff Abdullah 83590da2b28SAriff Abdullah PCM_LOCK(d); 83690da2b28SAriff Abdullah 83790da2b28SAriff Abdullah d->flags &= ~SD_F_BITPERFECT; 83890da2b28SAriff Abdullah d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 83990da2b28SAriff Abdullah 84090da2b28SAriff Abdullah PCM_RELEASE(d); 84190da2b28SAriff Abdullah PCM_UNLOCK(d); 84290da2b28SAriff Abdullah } else 84390da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 84490da2b28SAriff Abdullah 84590da2b28SAriff Abdullah return (err); 84690da2b28SAriff Abdullah } 84790da2b28SAriff Abdullah 848ed2196e5SHans Petter Selasky static u_int8_t 849ed2196e5SHans Petter Selasky pcm_mode_init(struct snddev_info *d) 850ed2196e5SHans Petter Selasky { 851ed2196e5SHans Petter Selasky u_int8_t mode = 0; 852ed2196e5SHans Petter Selasky 853ed2196e5SHans Petter Selasky if (d->playcount > 0) 854ed2196e5SHans Petter Selasky mode |= PCM_MODE_PLAY; 855ed2196e5SHans Petter Selasky if (d->reccount > 0) 856ed2196e5SHans Petter Selasky mode |= PCM_MODE_REC; 857ed2196e5SHans Petter Selasky if (d->mixer_dev != NULL) 858ed2196e5SHans Petter Selasky mode |= PCM_MODE_MIXER; 859ed2196e5SHans Petter Selasky 860ed2196e5SHans Petter Selasky return (mode); 861ed2196e5SHans Petter Selasky } 862ed2196e5SHans Petter Selasky 86332a0e5d5SHans Petter Selasky static void 86432a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev) 86532a0e5d5SHans Petter Selasky { 86632a0e5d5SHans Petter Selasky struct snddev_info *d = device_get_softc(dev); 867ed2196e5SHans Petter Selasky u_int8_t mode; 868ed2196e5SHans Petter Selasky 869ed2196e5SHans Petter Selasky mode = pcm_mode_init(d); 87032a0e5d5SHans Petter Selasky 871132fca63SHans Petter Selasky /* XXX: a user should be able to set this with a control tool, the 87232a0e5d5SHans Petter Selasky sysadmin then needs min+max sysctls for this */ 87332a0e5d5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 87432a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 87532a0e5d5SHans Petter Selasky OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 87632a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 87732a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 8783b4c5433SAlexander Motin "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, 8797029da5cSPawel Biernacki sizeof(d), sysctl_dev_pcm_bitperfect, "I", 88032a0e5d5SHans Petter Selasky "bit-perfect playback/recording (0=disable, 1=enable)"); 881ed2196e5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 882ed2196e5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 883ed2196e5SHans Petter Selasky OID_AUTO, "mode", CTLFLAG_RD, NULL, mode, 884ed2196e5SHans Petter Selasky "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than one" 885ed2196e5SHans Petter Selasky "mode is supported)"); 88632a0e5d5SHans Petter Selasky if (d->flags & SD_F_AUTOVCHAN) 88732a0e5d5SHans Petter Selasky vchan_initsys(dev); 88832a0e5d5SHans Petter Selasky if (d->flags & SD_F_EQ) 88932a0e5d5SHans Petter Selasky feeder_eq_initsys(dev); 89032a0e5d5SHans Petter Selasky } 89132a0e5d5SHans Petter Selasky 892987e5972SCameron Grant int 893987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 894987e5972SCameron Grant { 895bba4862cSAriff Abdullah struct snddev_info *d; 89690da2b28SAriff Abdullah int i; 897987e5972SCameron Grant 898b8a36395SCameron Grant if (pcm_veto_load) { 899b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 900b8a36395SCameron Grant 901b8a36395SCameron Grant return EINVAL; 902b8a36395SCameron Grant } 903b8a36395SCameron Grant 904bba4862cSAriff Abdullah d = device_get_softc(dev); 905e4e61333SAriff Abdullah d->dev = dev; 9062c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 907e4e61333SAriff Abdullah cv_init(&d->cv, device_get_nameunit(dev)); 908e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 9097233ababSAlexander Leidinger #if 0 9107233ababSAlexander Leidinger /* 9117233ababSAlexander Leidinger * d->flags should be cleared by the allocator of the softc. 9127233ababSAlexander Leidinger * We cannot clear this field here because several devices set 9137233ababSAlexander Leidinger * this flag before calling pcm_register(). 9147233ababSAlexander Leidinger */ 915cd9766c5SCameron Grant d->flags = 0; 9167233ababSAlexander Leidinger #endif 91790da2b28SAriff Abdullah i = 0; 91890da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 91990da2b28SAriff Abdullah "vpc", &i) != 0 || i != 0) 92090da2b28SAriff Abdullah d->flags |= SD_F_VPC; 92190da2b28SAriff Abdullah 92290da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 92390da2b28SAriff Abdullah "bitperfect", &i) == 0 && i != 0) 92490da2b28SAriff Abdullah d->flags |= SD_F_BITPERFECT; 92590da2b28SAriff Abdullah 926987e5972SCameron Grant d->devinfo = devinfo; 927506a5308SCameron Grant d->reccount = 0; 928a67fe5c1SCameron Grant d->playcount = 0; 929bba4862cSAriff Abdullah d->pvchancount = 0; 930bba4862cSAriff Abdullah d->rvchancount = 0; 931bba4862cSAriff Abdullah d->pvchanrate = 0; 932bba4862cSAriff Abdullah d->pvchanformat = 0; 933bba4862cSAriff Abdullah d->rvchanrate = 0; 934bba4862cSAriff Abdullah d->rvchanformat = 0; 935833f7023SCameron Grant 936bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm); 937bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm.busy); 93890da2b28SAriff Abdullah CHN_INIT(d, channels.pcm.opened); 93945550658SPoul-Henning Kamp 940e4e61333SAriff Abdullah /* XXX This is incorrect, but lets play along for now. */ 941a1d444e1SAriff Abdullah if ((numplay == 0 || numrec == 0) && numplay != numrec) 942285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 943d95502a8SCameron Grant 944bba4862cSAriff Abdullah sysctl_ctx_init(&d->play_sysctl_ctx); 945bba4862cSAriff Abdullah d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 946bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 9477029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node"); 948bba4862cSAriff Abdullah sysctl_ctx_init(&d->rec_sysctl_ctx); 949bba4862cSAriff Abdullah d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 950bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 951132fca63SHans Petter Selasky CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node"); 952e4e61333SAriff Abdullah 95332a0e5d5SHans Petter Selasky if (numplay > 0 || numrec > 0) 954cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 95590da2b28SAriff Abdullah 9569da3b645SChristos Margiolis sndstat_register(dev, d->status); 957e4e61333SAriff Abdullah 958e8c0d15aSChristos Margiolis return (dsp_make_dev(dev)); 959987e5972SCameron Grant } 960987e5972SCameron Grant 96133dbf14aSCameron Grant int 96233dbf14aSCameron Grant pcm_unregister(device_t dev) 9637c438dbeSCameron Grant { 964e4e61333SAriff Abdullah struct snddev_info *d; 965a67fe5c1SCameron Grant struct pcm_channel *ch; 9667c438dbeSCameron Grant 967e4e61333SAriff Abdullah d = device_get_softc(dev); 968e4e61333SAriff Abdullah 969e4e61333SAriff Abdullah if (!PCM_ALIVE(d)) { 970e4e61333SAriff Abdullah device_printf(dev, "unregister: device not configured\n"); 971e4e61333SAriff Abdullah return (0); 972e4e61333SAriff Abdullah } 973bba4862cSAriff Abdullah 97490da2b28SAriff Abdullah PCM_LOCK(d); 975e4e61333SAriff Abdullah PCM_WAIT(d); 976e4e61333SAriff Abdullah 977cc1efc23SHans Petter Selasky d->flags |= SD_F_DETACHING; 978cc1efc23SHans Petter Selasky 979e4e61333SAriff Abdullah PCM_ACQUIRE(d); 98090da2b28SAriff Abdullah PCM_UNLOCK(d); 981e4e61333SAriff Abdullah 982e4e61333SAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 983e4e61333SAriff Abdullah CHN_LOCK(ch); 98444e128feSChristos Margiolis /* 98503614fcbSChristos Margiolis * Do not wait for the timeout in chn_read()/chn_write(). Wake 98603614fcbSChristos Margiolis * up the sleeping thread and kill the channel. 98744e128feSChristos Margiolis */ 98803614fcbSChristos Margiolis chn_shutdown(ch); 98944e128feSChristos Margiolis chn_abort(ch); 990e4e61333SAriff Abdullah CHN_UNLOCK(ch); 991c9b53085SCameron Grant } 9925ee30e27SMathew Kanner 9938461d581SHans Petter Selasky /* remove /dev/sndstat entry first */ 9948461d581SHans Petter Selasky sndstat_unregister(dev); 9958461d581SHans Petter Selasky 99690da2b28SAriff Abdullah PCM_LOCK(d); 997e4e61333SAriff Abdullah d->flags |= SD_F_DYING; 998e4e61333SAriff Abdullah d->flags &= ~SD_F_REGISTERED; 99990da2b28SAriff Abdullah PCM_UNLOCK(d); 1000e4e61333SAriff Abdullah 1001bba4862cSAriff Abdullah if (d->play_sysctl_tree != NULL) { 1002bba4862cSAriff Abdullah sysctl_ctx_free(&d->play_sysctl_ctx); 1003bba4862cSAriff Abdullah d->play_sysctl_tree = NULL; 1004bba4862cSAriff Abdullah } 1005bba4862cSAriff Abdullah if (d->rec_sysctl_tree != NULL) { 1006bba4862cSAriff Abdullah sysctl_ctx_free(&d->rec_sysctl_ctx); 1007bba4862cSAriff Abdullah d->rec_sysctl_tree = NULL; 1008a1d444e1SAriff Abdullah } 1009bba4862cSAriff Abdullah 1010074d6fbeSChristos Margiolis dsp_destroy_dev(dev); 1011074d6fbeSChristos Margiolis (void)mixer_uninit(dev); 1012074d6fbeSChristos Margiolis 101303614fcbSChristos Margiolis pcm_killchans(d); 10147c438dbeSCameron Grant 101590da2b28SAriff Abdullah PCM_LOCK(d); 1016e4e61333SAriff Abdullah PCM_RELEASE(d); 1017e4e61333SAriff Abdullah cv_destroy(&d->cv); 101890da2b28SAriff Abdullah PCM_UNLOCK(d); 101949c5e6e2SCameron Grant snd_mtxfree(d->lock); 1020bba4862cSAriff Abdullah 1021bba4862cSAriff Abdullah if (snd_unit == device_get_unit(dev)) { 1022cbebc90dSAlexander Motin snd_unit = pcm_best_unit(-1); 1023cbebc90dSAlexander Motin if (snd_unit_auto == 0) 1024cbebc90dSAlexander Motin snd_unit_auto = 1; 1025e4e61333SAriff Abdullah } 1026bba4862cSAriff Abdullah 1027e4e61333SAriff Abdullah return (0); 102833dbf14aSCameron Grant } 10297c438dbeSCameron Grant 103067b1dce3SCameron Grant /************************************************************************/ 103167b1dce3SCameron Grant 1032b611c801SAlexander Leidinger /** 1033b611c801SAlexander Leidinger * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 1034b611c801SAlexander Leidinger * 1035b611c801SAlexander Leidinger * @param si Pointer to oss_sysinfo struct where information about the 1036b611c801SAlexander Leidinger * sound subsystem will be written/copied. 1037b611c801SAlexander Leidinger * 1038b611c801SAlexander Leidinger * This routine returns information about the sound system, such as the 1039b611c801SAlexander Leidinger * current OSS version, number of audio, MIDI, and mixer drivers, etc. 1040b611c801SAlexander Leidinger * Also includes a bitmask showing which of the above types of devices 1041b611c801SAlexander Leidinger * are open (busy). 1042b611c801SAlexander Leidinger * 1043b611c801SAlexander Leidinger * @note 1044b611c801SAlexander Leidinger * Calling threads must not hold any snddev_info or pcm_channel locks. 1045b611c801SAlexander Leidinger * 1046b611c801SAlexander Leidinger * @author Ryan Beasley <ryanb@FreeBSD.org> 1047b611c801SAlexander Leidinger */ 1048b611c801SAlexander Leidinger void 1049b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si) 1050b611c801SAlexander Leidinger { 1051b611c801SAlexander Leidinger static char si_product[] = "FreeBSD native OSS ABI"; 1052b611c801SAlexander Leidinger static char si_version[] = __XSTRING(__FreeBSD_version); 1053c412d503SAlexander Motin static char si_license[] = "BSD"; 1054b611c801SAlexander Leidinger static int intnbits = sizeof(int) * 8; /* Better suited as macro? 1055b611c801SAlexander Leidinger Must pester a C guru. */ 1056b611c801SAlexander Leidinger 1057b611c801SAlexander Leidinger struct snddev_info *d; 1058b611c801SAlexander Leidinger struct pcm_channel *c; 1059b611c801SAlexander Leidinger int i, j, ncards; 1060b611c801SAlexander Leidinger 1061b611c801SAlexander Leidinger ncards = 0; 1062b611c801SAlexander Leidinger 1063b611c801SAlexander Leidinger strlcpy(si->product, si_product, sizeof(si->product)); 1064b611c801SAlexander Leidinger strlcpy(si->version, si_version, sizeof(si->version)); 1065b611c801SAlexander Leidinger si->versionnum = SOUND_VERSION; 1066c412d503SAlexander Motin strlcpy(si->license, si_license, sizeof(si->license)); 1067b611c801SAlexander Leidinger 1068b611c801SAlexander Leidinger /* 1069b611c801SAlexander Leidinger * Iterate over PCM devices and their channels, gathering up data 1070b611c801SAlexander Leidinger * for the numaudios, ncards, and openedaudio fields. 1071b611c801SAlexander Leidinger */ 1072b611c801SAlexander Leidinger si->numaudios = 0; 1073b611c801SAlexander Leidinger bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 1074b611c801SAlexander Leidinger 1075b611c801SAlexander Leidinger j = 0; 1076b611c801SAlexander Leidinger 10779c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 10789c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 1079b611c801SAlexander Leidinger d = devclass_get_softc(pcm_devclass, i); 1080e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 1081b611c801SAlexander Leidinger continue; 1082b611c801SAlexander Leidinger 1083e4e61333SAriff Abdullah /* XXX Need Giant magic entry ??? */ 1084e4e61333SAriff Abdullah 1085b611c801SAlexander Leidinger /* See note in function's docblock */ 108690da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 108790da2b28SAriff Abdullah PCM_LOCK(d); 1088b611c801SAlexander Leidinger 10894d2be7beSChristos Margiolis si->numaudios += PCM_CHANCOUNT(d); 1090b611c801SAlexander Leidinger ++ncards; 1091b611c801SAlexander Leidinger 1092bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 109390da2b28SAriff Abdullah CHN_UNLOCKASSERT(c); 1094b611c801SAlexander Leidinger CHN_LOCK(c); 1095b611c801SAlexander Leidinger if (c->flags & CHN_F_BUSY) 1096b611c801SAlexander Leidinger si->openedaudio[j / intnbits] |= 1097b611c801SAlexander Leidinger (1 << (j % intnbits)); 1098b611c801SAlexander Leidinger CHN_UNLOCK(c); 1099b611c801SAlexander Leidinger j++; 1100b611c801SAlexander Leidinger } 1101b611c801SAlexander Leidinger 110290da2b28SAriff Abdullah PCM_UNLOCK(d); 1103b611c801SAlexander Leidinger } 1104c412d503SAlexander Motin si->numaudioengines = si->numaudios; 1105b611c801SAlexander Leidinger 1106b611c801SAlexander Leidinger si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 1107b611c801SAlexander Leidinger /** 1108b611c801SAlexander Leidinger * @todo Collect num{midis,timers}. 1109b611c801SAlexander Leidinger * 1110b611c801SAlexander Leidinger * Need access to sound/midi/midi.c::midistat_lock in order 1111b611c801SAlexander Leidinger * to safely touch midi_devices and get a head count of, well, 1112b611c801SAlexander Leidinger * MIDI devices. midistat_lock is a global static (i.e., local to 1113b611c801SAlexander Leidinger * midi.c), but midi_devices is a regular global; should the mutex 1114b611c801SAlexander Leidinger * be publicized, or is there another way to get this information? 1115b611c801SAlexander Leidinger * 1116b611c801SAlexander Leidinger * NB: MIDI/sequencer stuff is currently on hold. 1117b611c801SAlexander Leidinger */ 1118b611c801SAlexander Leidinger si->nummidis = 0; 1119b611c801SAlexander Leidinger si->numtimers = 0; 1120b611c801SAlexander Leidinger si->nummixers = mixer_count; 1121b611c801SAlexander Leidinger si->numcards = ncards; 1122b611c801SAlexander Leidinger /* OSSv4 docs: Intended only for test apps; API doesn't 1123b611c801SAlexander Leidinger really have much of a concept of cards. Shouldn't be 1124b611c801SAlexander Leidinger used by applications. */ 1125b611c801SAlexander Leidinger 1126b611c801SAlexander Leidinger /** 1127b611c801SAlexander Leidinger * @todo Fill in "busy devices" fields. 1128b611c801SAlexander Leidinger * 1129b611c801SAlexander Leidinger * si->openedmidi = " MIDI devices 1130b611c801SAlexander Leidinger */ 1131b611c801SAlexander Leidinger bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 1132b611c801SAlexander Leidinger 1133b611c801SAlexander Leidinger /* 1134b611c801SAlexander Leidinger * Si->filler is a reserved array, but according to docs each 1135b611c801SAlexander Leidinger * element should be set to -1. 1136b611c801SAlexander Leidinger */ 1137b611c801SAlexander Leidinger for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++) 1138b611c801SAlexander Leidinger si->filler[i] = -1; 1139b611c801SAlexander Leidinger } 1140b611c801SAlexander Leidinger 114152f6e09eSAlexander Motin int 114252f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si) 114352f6e09eSAlexander Motin { 114452f6e09eSAlexander Motin struct snddev_info *d; 114552f6e09eSAlexander Motin int i, ncards; 114652f6e09eSAlexander Motin 114752f6e09eSAlexander Motin ncards = 0; 114852f6e09eSAlexander Motin 114952f6e09eSAlexander Motin for (i = 0; pcm_devclass != NULL && 115052f6e09eSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 115152f6e09eSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 115252f6e09eSAlexander Motin if (!PCM_REGISTERED(d)) 115352f6e09eSAlexander Motin continue; 115452f6e09eSAlexander Motin 115552f6e09eSAlexander Motin if (ncards++ != si->card) 115652f6e09eSAlexander Motin continue; 115752f6e09eSAlexander Motin 115890da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 115990da2b28SAriff Abdullah PCM_LOCK(d); 116052f6e09eSAlexander Motin 116152f6e09eSAlexander Motin strlcpy(si->shortname, device_get_nameunit(d->dev), 116252f6e09eSAlexander Motin sizeof(si->shortname)); 116352f6e09eSAlexander Motin strlcpy(si->longname, device_get_desc(d->dev), 116452f6e09eSAlexander Motin sizeof(si->longname)); 116552f6e09eSAlexander Motin strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 116652f6e09eSAlexander Motin si->intr_count = si->ack_count = 0; 116790da2b28SAriff Abdullah 116890da2b28SAriff Abdullah PCM_UNLOCK(d); 116990da2b28SAriff Abdullah 117052f6e09eSAlexander Motin return (0); 117152f6e09eSAlexander Motin } 117252f6e09eSAlexander Motin return (ENXIO); 117352f6e09eSAlexander Motin } 117452f6e09eSAlexander Motin 1175b611c801SAlexander Leidinger /************************************************************************/ 1176b611c801SAlexander Leidinger 11770739ea1dSSeigo Tanimura static int 11780739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data) 11790739ea1dSSeigo Tanimura { 1180b611c801SAlexander Leidinger int ret; 1181b611c801SAlexander Leidinger 11828109ec9dSJohn Baldwin ret = 0; 1183b611c801SAlexander Leidinger switch (type) { 1184b611c801SAlexander Leidinger case MOD_LOAD: 118513bebcd3SJohn Baldwin pcm_devclass = devclass_create("pcm"); 1186b611c801SAlexander Leidinger pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 1187b611c801SAlexander Leidinger break; 1188b611c801SAlexander Leidinger case MOD_UNLOAD: 1189b611c801SAlexander Leidinger if (pcmsg_unrhdr != NULL) { 1190b611c801SAlexander Leidinger delete_unrhdr(pcmsg_unrhdr); 1191b611c801SAlexander Leidinger pcmsg_unrhdr = NULL; 1192b611c801SAlexander Leidinger } 1193b611c801SAlexander Leidinger break; 11945525b53dSAlexander Motin case MOD_SHUTDOWN: 11955525b53dSAlexander Motin break; 1196b611c801SAlexander Leidinger default: 119790da2b28SAriff Abdullah ret = ENOTSUP; 1198b611c801SAlexander Leidinger } 1199b611c801SAlexander Leidinger 1200b611c801SAlexander Leidinger return ret; 12010739ea1dSSeigo Tanimura } 12020739ea1dSSeigo Tanimura 12030739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL); 12040739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER); 1205