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> 44b611c801SAlexander Leidinger #include <sys/limits.h> 457c438dbeSCameron Grant #include <sys/sysctl.h> 46285648f9SCameron Grant 4767b1dce3SCameron Grant #include "feeder_if.h" 4867b1dce3SCameron Grant 49d95502a8SCameron Grant devclass_t pcm_devclass; 5082db23e2SCameron Grant 51b8a36395SCameron Grant int pcm_veto_load = 1; 52b8a36395SCameron Grant 53f3685841SAriff Abdullah int snd_unit = -1; 54cbe7d6a3SCameron Grant 55cbebc90dSAlexander Motin static int snd_unit_auto = -1; 56af3b2549SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN, 57838d3589SAriff Abdullah &snd_unit_auto, 0, "assign default unit to a newly attached device"); 58ad8612b9SAriff Abdullah 597029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 607029da5cSPawel Biernacki "Sound driver"); 6182db23e2SCameron Grant 6232a0e5d5SHans Petter Selasky static void pcm_sysinit(device_t); 6332a0e5d5SHans Petter Selasky 64b611c801SAlexander Leidinger /** 65b611c801SAlexander Leidinger * @brief Unit number allocator for syncgroup IDs 66b611c801SAlexander Leidinger */ 67b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL; 68b611c801SAlexander Leidinger 6937209180SCameron Grant void * 702c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type) 7137209180SCameron Grant { 7237209180SCameron Grant struct mtx *m; 7337209180SCameron Grant 74a163d034SWarner Losh m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 7512e524a2SDon Lewis mtx_init(m, desc, type, MTX_DEF); 7612e524a2SDon Lewis return m; 7712e524a2SDon Lewis } 7812e524a2SDon Lewis 7937209180SCameron Grant void 8037209180SCameron Grant snd_mtxfree(void *m) 8137209180SCameron Grant { 8237209180SCameron Grant struct mtx *mtx = m; 8337209180SCameron Grant 8437209180SCameron Grant mtx_destroy(mtx); 8537209180SCameron Grant free(mtx, M_DEVBUF); 8637209180SCameron Grant } 8737209180SCameron Grant 8837209180SCameron Grant void 8937209180SCameron Grant snd_mtxassert(void *m) 9037209180SCameron Grant { 91f00f162aSCameron Grant #ifdef INVARIANTS 9237209180SCameron Grant struct mtx *mtx = m; 9337209180SCameron Grant 9437209180SCameron Grant mtx_assert(mtx, MA_OWNED); 9537209180SCameron Grant #endif 9637209180SCameron Grant } 9737209180SCameron Grant 9837209180SCameron Grant int 9937209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 10037209180SCameron Grant { 101e4e61333SAriff Abdullah struct snddev_info *d; 10290da2b28SAriff Abdullah 10337209180SCameron Grant flags &= INTR_MPSAFE; 10446700f12SPeter Wemm flags |= INTR_TYPE_AV; 105e4e61333SAriff Abdullah d = device_get_softc(dev); 106e4e61333SAriff Abdullah if (d != NULL && (flags & INTR_MPSAFE)) 107e4e61333SAriff Abdullah d->flags |= SD_F_MPSAFE; 108e4e61333SAriff Abdullah 109775fcb6eSBaptiste Daroussin return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep); 11037209180SCameron Grant } 11137209180SCameron Grant 1123fdb3676SAriff Abdullah int 1133fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 11425723d66SChristos Margiolis pid_t pid, char *comm) 115285648f9SCameron Grant { 116285648f9SCameron Grant struct pcm_channel *c; 117*b973a14dSChristos Margiolis int err, vchancount; 11825723d66SChristos Margiolis bool retry; 119bba4862cSAriff Abdullah 12025723d66SChristos Margiolis KASSERT(d != NULL && ch != NULL && 121bba4862cSAriff Abdullah (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 12225723d66SChristos Margiolis ("%s(): invalid d=%p ch=%p direction=%d pid=%d", 12325723d66SChristos Margiolis __func__, d, ch, direction, pid)); 124e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 125bba4862cSAriff Abdullah 12690da2b28SAriff Abdullah *ch = NULL; 12790da2b28SAriff Abdullah vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : 12890da2b28SAriff Abdullah d->rvchancount; 12925723d66SChristos Margiolis retry = false; 130*b973a14dSChristos Margiolis 1313fdb3676SAriff Abdullah retry_chnalloc: 132*b973a14dSChristos Margiolis /* Scan for a free channel. */ 133bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 13449c5e6e2SCameron Grant CHN_LOCK(c); 135*b973a14dSChristos Margiolis if (c->direction != direction) { 13690da2b28SAriff Abdullah CHN_UNLOCK(c); 137*b973a14dSChristos Margiolis continue; 13890da2b28SAriff Abdullah } 139*b973a14dSChristos Margiolis if (!(c->flags & CHN_F_BUSY)) { 140285648f9SCameron Grant c->flags |= CHN_F_BUSY; 141b8f0d9e0SCameron Grant c->pid = pid; 14290da2b28SAriff Abdullah strlcpy(c->comm, (comm != NULL) ? comm : 14390da2b28SAriff Abdullah CHN_COMM_UNKNOWN, sizeof(c->comm)); 1443fdb3676SAriff Abdullah *ch = c; 145*b973a14dSChristos Margiolis 146bba4862cSAriff Abdullah return (0); 147*b973a14dSChristos Margiolis } 14849c5e6e2SCameron Grant CHN_UNLOCK(c); 149285648f9SCameron Grant } 150*b973a14dSChristos Margiolis /* Maybe next time... */ 15125723d66SChristos Margiolis if (retry) 152*b973a14dSChristos Margiolis return (EBUSY); 153e4e61333SAriff Abdullah 154*b973a14dSChristos Margiolis /* No channel available. We also cannot create more VCHANs. */ 15525723d66SChristos Margiolis if (!(vchancount > 0 && vchancount < snd_maxautovchans)) 156*b973a14dSChristos Margiolis return (ENOTSUP); 157*b973a14dSChristos Margiolis 158*b973a14dSChristos Margiolis /* Increase the VCHAN count and try to get the new channel. */ 1593af2beb8SChristos Margiolis err = vchan_setnew(d, direction, vchancount + 1); 160a1d444e1SAriff Abdullah if (err == 0) { 16125723d66SChristos Margiolis retry = true; 1623fdb3676SAriff Abdullah goto retry_chnalloc; 163f637a36cSCameron Grant } 164f637a36cSCameron Grant 165bba4862cSAriff Abdullah return (err); 166285648f9SCameron Grant } 167285648f9SCameron Grant 16833dbf14aSCameron Grant static int 169851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 17033dbf14aSCameron Grant { 171b8f0d9e0SCameron Grant struct snddev_info *d; 17233dbf14aSCameron Grant int error, unit; 17333dbf14aSCameron Grant 17433dbf14aSCameron Grant unit = snd_unit; 175041b706bSDavid Malone error = sysctl_handle_int(oidp, &unit, 0, req); 17633dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 177b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 178e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 179b8f0d9e0SCameron Grant return EINVAL; 18033dbf14aSCameron Grant snd_unit = unit; 181cbebc90dSAlexander Motin snd_unit_auto = 0; 18233dbf14aSCameron Grant } 18333dbf14aSCameron Grant return (error); 18433dbf14aSCameron Grant } 185851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */ 186b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, 1877029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0, 1887029da5cSPawel Biernacki sizeof(int), sysctl_hw_snd_default_unit, "I", 189b7d6c6b5SEitan Adler "default sound device"); 190987e5972SCameron Grant 191139bcec8SChristos Margiolis void 1925ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 193285648f9SCameron Grant { 194e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 19590da2b28SAriff Abdullah PCM_LOCKASSERT(d); 196bba4862cSAriff Abdullah KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 197bba4862cSAriff Abdullah ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 198285648f9SCameron Grant 19990da2b28SAriff Abdullah CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); 2003fdb3676SAriff Abdullah 20125723d66SChristos Margiolis switch (ch->type) { 202e4e61333SAriff Abdullah case SND_DEV_DSPHW_PLAY: 203e4e61333SAriff Abdullah d->playcount++; 204e4e61333SAriff Abdullah break; 205e4e61333SAriff Abdullah case SND_DEV_DSPHW_VPLAY: 206e4e61333SAriff Abdullah d->pvchancount++; 207e4e61333SAriff Abdullah break; 208e4e61333SAriff Abdullah case SND_DEV_DSPHW_REC: 209e4e61333SAriff Abdullah d->reccount++; 210e4e61333SAriff Abdullah break; 211e4e61333SAriff Abdullah case SND_DEV_DSPHW_VREC: 212e4e61333SAriff Abdullah d->rvchancount++; 213e4e61333SAriff Abdullah break; 214e4e61333SAriff Abdullah default: 21576f95baeSChristos Margiolis __assert_unreachable(); 216e4e61333SAriff Abdullah } 21733dbf14aSCameron Grant } 21833dbf14aSCameron Grant 219285648f9SCameron Grant int 2205ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 22133dbf14aSCameron Grant { 222bba4862cSAriff Abdullah struct pcm_channel *tmp; 22333dbf14aSCameron Grant 224e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 22590da2b28SAriff Abdullah PCM_LOCKASSERT(d); 226e4e61333SAriff Abdullah 227bba4862cSAriff Abdullah tmp = NULL; 228a527dbc7SCameron Grant 229bba4862cSAriff Abdullah CHN_FOREACH(tmp, d, channels.pcm) { 230bba4862cSAriff Abdullah if (tmp == ch) 231bba4862cSAriff Abdullah break; 23233dbf14aSCameron Grant } 233bba4862cSAriff Abdullah 234bba4862cSAriff Abdullah if (tmp != ch) 235e4e61333SAriff Abdullah return (EINVAL); 23667beb5a5SCameron Grant 237bba4862cSAriff Abdullah CHN_REMOVE(d, ch, channels.pcm); 238e4e61333SAriff Abdullah 23925723d66SChristos Margiolis switch (ch->type) { 240bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 24167beb5a5SCameron Grant d->playcount--; 242bba4862cSAriff Abdullah break; 243bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 244bba4862cSAriff Abdullah d->pvchancount--; 245bba4862cSAriff Abdullah break; 246bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 247bba4862cSAriff Abdullah d->reccount--; 248bba4862cSAriff Abdullah break; 249bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 250bba4862cSAriff Abdullah d->rvchancount--; 251bba4862cSAriff Abdullah break; 252bba4862cSAriff Abdullah default: 25376f95baeSChristos Margiolis __assert_unreachable(); 254bba4862cSAriff Abdullah } 255285648f9SCameron Grant 256e4e61333SAriff Abdullah return (0); 257987e5972SCameron Grant } 258987e5972SCameron Grant 259987e5972SCameron Grant int 260285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 261285648f9SCameron Grant { 262285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 26367b1dce3SCameron Grant struct pcm_channel *ch; 264285648f9SCameron Grant 265e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 266e4e61333SAriff Abdullah 26790da2b28SAriff Abdullah PCM_LOCK(d); 2683af2beb8SChristos Margiolis ch = chn_init(d, NULL, cls, dir, devinfo); 269285648f9SCameron Grant if (!ch) { 2702e9962efSChristos Margiolis device_printf(d->dev, "chn_init(%s, %d, %p) failed\n", 271e4e61333SAriff Abdullah cls->name, dir, devinfo); 27290da2b28SAriff Abdullah PCM_UNLOCK(d); 273e4e61333SAriff Abdullah return (ENODEV); 274285648f9SCameron Grant } 275cd9766c5SCameron Grant 276139bcec8SChristos Margiolis pcm_chn_add(d, ch); 27790da2b28SAriff Abdullah PCM_UNLOCK(d); 278cd9766c5SCameron Grant 279139bcec8SChristos Margiolis return (0); 280285648f9SCameron Grant } 281285648f9SCameron Grant 28203614fcbSChristos Margiolis static void 28303614fcbSChristos Margiolis pcm_killchans(struct snddev_info *d) 284285648f9SCameron Grant { 285e33bee07SOlivier Houchard struct pcm_channel *ch; 286e4e61333SAriff Abdullah int error; 28703614fcbSChristos Margiolis bool found; 288e4e61333SAriff Abdullah 289e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 29003614fcbSChristos Margiolis do { 29103614fcbSChristos Margiolis found = false; 29203614fcbSChristos Margiolis CHN_FOREACH(ch, d, channels.pcm) { 29303614fcbSChristos Margiolis CHN_LOCK(ch); 29403614fcbSChristos Margiolis /* 29503614fcbSChristos Margiolis * Make sure no channel has went to sleep in the 29603614fcbSChristos Margiolis * meantime. 29703614fcbSChristos Margiolis */ 29803614fcbSChristos Margiolis chn_shutdown(ch); 29903614fcbSChristos Margiolis /* 30003614fcbSChristos Margiolis * We have to give a thread sleeping in chn_sleep() a 30103614fcbSChristos Margiolis * chance to observe that the channel is dead. 30203614fcbSChristos Margiolis */ 30303614fcbSChristos Margiolis if ((ch->flags & CHN_F_SLEEPING) == 0) { 30403614fcbSChristos Margiolis found = true; 30503614fcbSChristos Margiolis CHN_UNLOCK(ch); 30603614fcbSChristos Margiolis break; 30703614fcbSChristos Margiolis } 30803614fcbSChristos Margiolis CHN_UNLOCK(ch); 30903614fcbSChristos Margiolis } 310285648f9SCameron Grant 31103614fcbSChristos Margiolis /* 31203614fcbSChristos Margiolis * All channels are still sleeping. Sleep for a bit and try 31303614fcbSChristos Margiolis * again to see if any of them is awake now. 31403614fcbSChristos Margiolis */ 31503614fcbSChristos Margiolis if (!found) { 31603614fcbSChristos Margiolis pause_sbt("pcmkillchans", SBT_1MS * 5, 0, 0); 31703614fcbSChristos Margiolis continue; 31803614fcbSChristos Margiolis } 319285648f9SCameron Grant 32090da2b28SAriff Abdullah PCM_LOCK(d); 321bba4862cSAriff Abdullah error = pcm_chn_remove(d, ch); 32290da2b28SAriff Abdullah PCM_UNLOCK(d); 32303614fcbSChristos Margiolis if (error == 0) 324b3ea087cSChristos Margiolis chn_kill(ch); 32503614fcbSChristos Margiolis } while (!CHN_EMPTY(d, channels.pcm)); 326285648f9SCameron Grant } 327285648f9SCameron Grant 328cbebc90dSAlexander Motin static int 329cbebc90dSAlexander Motin pcm_best_unit(int old) 330cbebc90dSAlexander Motin { 331cbebc90dSAlexander Motin struct snddev_info *d; 332cbebc90dSAlexander Motin int i, best, bestprio, prio; 333cbebc90dSAlexander Motin 334cbebc90dSAlexander Motin best = -1; 335cbebc90dSAlexander Motin bestprio = -100; 336cbebc90dSAlexander Motin for (i = 0; pcm_devclass != NULL && 337cbebc90dSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 338cbebc90dSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 339cbebc90dSAlexander Motin if (!PCM_REGISTERED(d)) 340cbebc90dSAlexander Motin continue; 341cbebc90dSAlexander Motin prio = 0; 342cbebc90dSAlexander Motin if (d->playcount == 0) 343cbebc90dSAlexander Motin prio -= 10; 344cbebc90dSAlexander Motin if (d->reccount == 0) 345cbebc90dSAlexander Motin prio -= 2; 346cbebc90dSAlexander Motin if (prio > bestprio || (prio == bestprio && i == old)) { 347cbebc90dSAlexander Motin best = i; 348cbebc90dSAlexander Motin bestprio = prio; 349cbebc90dSAlexander Motin } 350cbebc90dSAlexander Motin } 351cbebc90dSAlexander Motin return (best); 352cbebc90dSAlexander Motin } 353cbebc90dSAlexander Motin 354285648f9SCameron Grant int 355987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 356987e5972SCameron Grant { 35766ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 35849c5e6e2SCameron Grant 35932a0e5d5SHans Petter Selasky /* should only be called once */ 36032a0e5d5SHans Petter Selasky if (d->flags & SD_F_REGISTERED) 36132a0e5d5SHans Petter Selasky return (EINVAL); 36232a0e5d5SHans Petter Selasky 363e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 364bba4862cSAriff Abdullah 365e4e61333SAriff Abdullah if (d->playcount == 0 || d->reccount == 0) 366e4e61333SAriff Abdullah d->flags |= SD_F_SIMPLEX; 367e4e61333SAriff Abdullah 36832a0e5d5SHans Petter Selasky if (d->playcount > 0 || d->reccount > 0) 369e4e61333SAriff Abdullah d->flags |= SD_F_AUTOVCHAN; 370e4e61333SAriff Abdullah 3717ad5f383SChristos Margiolis vchan_setmaxauto(d, snd_maxautovchans); 372bba4862cSAriff Abdullah 373a580b31aSAriff Abdullah strlcpy(d->status, str, SND_STATUSLEN); 374bba4862cSAriff Abdullah 37590da2b28SAriff Abdullah PCM_LOCK(d); 376e4e61333SAriff Abdullah 377e4e61333SAriff Abdullah /* Done, we're ready.. */ 378e4e61333SAriff Abdullah d->flags |= SD_F_REGISTERED; 379e4e61333SAriff Abdullah 380e4e61333SAriff Abdullah PCM_RELEASE(d); 381bba4862cSAriff Abdullah 38290da2b28SAriff Abdullah PCM_UNLOCK(d); 383bba4862cSAriff Abdullah 38432a0e5d5SHans Petter Selasky /* 38532a0e5d5SHans Petter Selasky * Create all sysctls once SD_F_REGISTERED is set else 38632a0e5d5SHans Petter Selasky * tunable sysctls won't work: 38732a0e5d5SHans Petter Selasky */ 38832a0e5d5SHans Petter Selasky pcm_sysinit(dev); 38932a0e5d5SHans Petter Selasky 390cbebc90dSAlexander Motin if (snd_unit_auto < 0) 391cbebc90dSAlexander Motin snd_unit_auto = (snd_unit < 0) ? 1 : 0; 392cbebc90dSAlexander Motin if (snd_unit < 0 || snd_unit_auto > 1) 393f3685841SAriff Abdullah snd_unit = device_get_unit(dev); 394cbebc90dSAlexander Motin else if (snd_unit_auto == 1) 395cbebc90dSAlexander Motin snd_unit = pcm_best_unit(snd_unit); 396f3685841SAriff Abdullah 397e4e61333SAriff Abdullah return (0); 398987e5972SCameron Grant } 399987e5972SCameron Grant 400a1d444e1SAriff Abdullah uint32_t 401987e5972SCameron Grant pcm_getflags(device_t dev) 402987e5972SCameron Grant { 40366ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 40449c5e6e2SCameron Grant 405987e5972SCameron Grant return d->flags; 406987e5972SCameron Grant } 407987e5972SCameron Grant 408987e5972SCameron Grant void 409a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val) 410987e5972SCameron Grant { 41166ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 412d95502a8SCameron Grant 413987e5972SCameron Grant d->flags = val; 414987e5972SCameron Grant } 415987e5972SCameron Grant 41639004e69SCameron Grant void * 41739004e69SCameron Grant pcm_getdevinfo(device_t dev) 41839004e69SCameron Grant { 41966ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 42049c5e6e2SCameron Grant 42139004e69SCameron Grant return d->devinfo; 42239004e69SCameron Grant } 42339004e69SCameron Grant 424a67fe5c1SCameron Grant unsigned int 425d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 426a67fe5c1SCameron Grant { 427a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 4284e60be34SCameron Grant int sz, x; 429a67fe5c1SCameron Grant 430a67fe5c1SCameron Grant sz = 0; 4314e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 4324e60be34SCameron Grant x = sz; 433d55d96f6SAlexander Leidinger RANGE(sz, minbufsz, maxbufsz); 4344e60be34SCameron Grant if (x != sz) 435d55d96f6SAlexander Leidinger device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 436d55d96f6SAlexander Leidinger x = minbufsz; 4374e60be34SCameron Grant while (x < sz) 4384e60be34SCameron Grant x <<= 1; 4394e60be34SCameron Grant if (x > sz) 4404e60be34SCameron Grant x >>= 1; 4414e60be34SCameron Grant if (x != sz) { 4424e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 4434e60be34SCameron Grant sz = x; 4444e60be34SCameron Grant } 4454e60be34SCameron Grant } else { 446a67fe5c1SCameron Grant sz = deflt; 4474e60be34SCameron Grant } 4484e60be34SCameron Grant 449a67fe5c1SCameron Grant d->bufsz = sz; 450a67fe5c1SCameron Grant 451a67fe5c1SCameron Grant return sz; 452a67fe5c1SCameron Grant } 453a67fe5c1SCameron Grant 45490da2b28SAriff Abdullah static int 45590da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 45690da2b28SAriff Abdullah { 45790da2b28SAriff Abdullah struct snddev_info *d; 45890da2b28SAriff Abdullah int err, val; 45990da2b28SAriff Abdullah 46090da2b28SAriff Abdullah d = oidp->oid_arg1; 46190da2b28SAriff Abdullah if (!PCM_REGISTERED(d)) 46290da2b28SAriff Abdullah return (ENODEV); 46390da2b28SAriff Abdullah 46490da2b28SAriff Abdullah PCM_LOCK(d); 46590da2b28SAriff Abdullah PCM_WAIT(d); 46690da2b28SAriff Abdullah val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 46790da2b28SAriff Abdullah PCM_ACQUIRE(d); 46890da2b28SAriff Abdullah PCM_UNLOCK(d); 46990da2b28SAriff Abdullah 47090da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &val, 0, req); 47190da2b28SAriff Abdullah 47290da2b28SAriff Abdullah if (err == 0 && req->newptr != NULL) { 47390da2b28SAriff Abdullah if (!(val == 0 || val == 1)) { 47490da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 47590da2b28SAriff Abdullah return (EINVAL); 47690da2b28SAriff Abdullah } 47790da2b28SAriff Abdullah 47890da2b28SAriff Abdullah PCM_LOCK(d); 47990da2b28SAriff Abdullah 48090da2b28SAriff Abdullah d->flags &= ~SD_F_BITPERFECT; 48190da2b28SAriff Abdullah d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 48290da2b28SAriff Abdullah 48390da2b28SAriff Abdullah PCM_RELEASE(d); 48490da2b28SAriff Abdullah PCM_UNLOCK(d); 48590da2b28SAriff Abdullah } else 48690da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 48790da2b28SAriff Abdullah 48890da2b28SAriff Abdullah return (err); 48990da2b28SAriff Abdullah } 49090da2b28SAriff Abdullah 491ed2196e5SHans Petter Selasky static u_int8_t 492ed2196e5SHans Petter Selasky pcm_mode_init(struct snddev_info *d) 493ed2196e5SHans Petter Selasky { 494ed2196e5SHans Petter Selasky u_int8_t mode = 0; 495ed2196e5SHans Petter Selasky 496ed2196e5SHans Petter Selasky if (d->playcount > 0) 497ed2196e5SHans Petter Selasky mode |= PCM_MODE_PLAY; 498ed2196e5SHans Petter Selasky if (d->reccount > 0) 499ed2196e5SHans Petter Selasky mode |= PCM_MODE_REC; 500ed2196e5SHans Petter Selasky if (d->mixer_dev != NULL) 501ed2196e5SHans Petter Selasky mode |= PCM_MODE_MIXER; 502ed2196e5SHans Petter Selasky 503ed2196e5SHans Petter Selasky return (mode); 504ed2196e5SHans Petter Selasky } 505ed2196e5SHans Petter Selasky 50632a0e5d5SHans Petter Selasky static void 50732a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev) 50832a0e5d5SHans Petter Selasky { 50932a0e5d5SHans Petter Selasky struct snddev_info *d = device_get_softc(dev); 510ed2196e5SHans Petter Selasky u_int8_t mode; 511ed2196e5SHans Petter Selasky 512ed2196e5SHans Petter Selasky mode = pcm_mode_init(d); 51332a0e5d5SHans Petter Selasky 514132fca63SHans Petter Selasky /* XXX: a user should be able to set this with a control tool, the 51532a0e5d5SHans Petter Selasky sysadmin then needs min+max sysctls for this */ 51632a0e5d5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 51732a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 51832a0e5d5SHans Petter Selasky OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 51932a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 52032a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 5213b4c5433SAlexander Motin "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, 5227029da5cSPawel Biernacki sizeof(d), sysctl_dev_pcm_bitperfect, "I", 52332a0e5d5SHans Petter Selasky "bit-perfect playback/recording (0=disable, 1=enable)"); 524ed2196e5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 525ed2196e5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 526ed2196e5SHans Petter Selasky OID_AUTO, "mode", CTLFLAG_RD, NULL, mode, 527e56c8996SChristos Margiolis "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than " 528e56c8996SChristos Margiolis "one mode is supported)"); 52932a0e5d5SHans Petter Selasky if (d->flags & SD_F_AUTOVCHAN) 53032a0e5d5SHans Petter Selasky vchan_initsys(dev); 53132a0e5d5SHans Petter Selasky if (d->flags & SD_F_EQ) 53232a0e5d5SHans Petter Selasky feeder_eq_initsys(dev); 53332a0e5d5SHans Petter Selasky } 53432a0e5d5SHans Petter Selasky 535987e5972SCameron Grant int 536987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 537987e5972SCameron Grant { 538bba4862cSAriff Abdullah struct snddev_info *d; 53990da2b28SAriff Abdullah int i; 540987e5972SCameron Grant 541b8a36395SCameron Grant if (pcm_veto_load) { 542b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 543b8a36395SCameron Grant 544b8a36395SCameron Grant return EINVAL; 545b8a36395SCameron Grant } 546b8a36395SCameron Grant 547bba4862cSAriff Abdullah d = device_get_softc(dev); 548e4e61333SAriff Abdullah d->dev = dev; 5492c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 550e4e61333SAriff Abdullah cv_init(&d->cv, device_get_nameunit(dev)); 551e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 552a9f08df3SChristos Margiolis 55390da2b28SAriff Abdullah i = 0; 55490da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 55590da2b28SAriff Abdullah "vpc", &i) != 0 || i != 0) 55690da2b28SAriff Abdullah d->flags |= SD_F_VPC; 55790da2b28SAriff Abdullah 55890da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 55990da2b28SAriff Abdullah "bitperfect", &i) == 0 && i != 0) 56090da2b28SAriff Abdullah d->flags |= SD_F_BITPERFECT; 56190da2b28SAriff Abdullah 562987e5972SCameron Grant d->devinfo = devinfo; 563506a5308SCameron Grant d->reccount = 0; 564a67fe5c1SCameron Grant d->playcount = 0; 565bba4862cSAriff Abdullah d->pvchancount = 0; 566bba4862cSAriff Abdullah d->rvchancount = 0; 567bba4862cSAriff Abdullah d->pvchanrate = 0; 568bba4862cSAriff Abdullah d->pvchanformat = 0; 569bba4862cSAriff Abdullah d->rvchanrate = 0; 570bba4862cSAriff Abdullah d->rvchanformat = 0; 571833f7023SCameron Grant 572bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm); 573bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm.busy); 57490da2b28SAriff Abdullah CHN_INIT(d, channels.pcm.opened); 57545550658SPoul-Henning Kamp 576e4e61333SAriff Abdullah /* XXX This is incorrect, but lets play along for now. */ 577a1d444e1SAriff Abdullah if ((numplay == 0 || numrec == 0) && numplay != numrec) 578285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 579d95502a8SCameron Grant 580bba4862cSAriff Abdullah sysctl_ctx_init(&d->play_sysctl_ctx); 581bba4862cSAriff Abdullah d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 582bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 5837029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node"); 584bba4862cSAriff Abdullah sysctl_ctx_init(&d->rec_sysctl_ctx); 585bba4862cSAriff Abdullah d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 586bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 587132fca63SHans Petter Selasky CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node"); 588e4e61333SAriff Abdullah 58932a0e5d5SHans Petter Selasky if (numplay > 0 || numrec > 0) 590cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 59190da2b28SAriff Abdullah 5929da3b645SChristos Margiolis sndstat_register(dev, d->status); 593e4e61333SAriff Abdullah 594e8c0d15aSChristos Margiolis return (dsp_make_dev(dev)); 595987e5972SCameron Grant } 596987e5972SCameron Grant 59733dbf14aSCameron Grant int 59833dbf14aSCameron Grant pcm_unregister(device_t dev) 5997c438dbeSCameron Grant { 600e4e61333SAriff Abdullah struct snddev_info *d; 601a67fe5c1SCameron Grant struct pcm_channel *ch; 6027c438dbeSCameron Grant 603e4e61333SAriff Abdullah d = device_get_softc(dev); 604e4e61333SAriff Abdullah 605e4e61333SAriff Abdullah if (!PCM_ALIVE(d)) { 606e4e61333SAriff Abdullah device_printf(dev, "unregister: device not configured\n"); 607e4e61333SAriff Abdullah return (0); 608e4e61333SAriff Abdullah } 609bba4862cSAriff Abdullah 61090da2b28SAriff Abdullah PCM_LOCK(d); 611e4e61333SAriff Abdullah PCM_WAIT(d); 612e4e61333SAriff Abdullah 613cc1efc23SHans Petter Selasky d->flags |= SD_F_DETACHING; 614cc1efc23SHans Petter Selasky 615e4e61333SAriff Abdullah PCM_ACQUIRE(d); 61690da2b28SAriff Abdullah PCM_UNLOCK(d); 617e4e61333SAriff Abdullah 618e4e61333SAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 619e4e61333SAriff Abdullah CHN_LOCK(ch); 62044e128feSChristos Margiolis /* 62103614fcbSChristos Margiolis * Do not wait for the timeout in chn_read()/chn_write(). Wake 62203614fcbSChristos Margiolis * up the sleeping thread and kill the channel. 62344e128feSChristos Margiolis */ 62403614fcbSChristos Margiolis chn_shutdown(ch); 62544e128feSChristos Margiolis chn_abort(ch); 626e4e61333SAriff Abdullah CHN_UNLOCK(ch); 627c9b53085SCameron Grant } 6285ee30e27SMathew Kanner 6298461d581SHans Petter Selasky /* remove /dev/sndstat entry first */ 6308461d581SHans Petter Selasky sndstat_unregister(dev); 6318461d581SHans Petter Selasky 63290da2b28SAriff Abdullah PCM_LOCK(d); 633e4e61333SAriff Abdullah d->flags |= SD_F_DYING; 634e4e61333SAriff Abdullah d->flags &= ~SD_F_REGISTERED; 63590da2b28SAriff Abdullah PCM_UNLOCK(d); 636e4e61333SAriff Abdullah 637bba4862cSAriff Abdullah if (d->play_sysctl_tree != NULL) { 638bba4862cSAriff Abdullah sysctl_ctx_free(&d->play_sysctl_ctx); 639bba4862cSAriff Abdullah d->play_sysctl_tree = NULL; 640bba4862cSAriff Abdullah } 641bba4862cSAriff Abdullah if (d->rec_sysctl_tree != NULL) { 642bba4862cSAriff Abdullah sysctl_ctx_free(&d->rec_sysctl_ctx); 643bba4862cSAriff Abdullah d->rec_sysctl_tree = NULL; 644a1d444e1SAriff Abdullah } 645bba4862cSAriff Abdullah 646074d6fbeSChristos Margiolis dsp_destroy_dev(dev); 647074d6fbeSChristos Margiolis (void)mixer_uninit(dev); 648074d6fbeSChristos Margiolis 64903614fcbSChristos Margiolis pcm_killchans(d); 6507c438dbeSCameron Grant 65190da2b28SAriff Abdullah PCM_LOCK(d); 652e4e61333SAriff Abdullah PCM_RELEASE(d); 653e4e61333SAriff Abdullah cv_destroy(&d->cv); 65490da2b28SAriff Abdullah PCM_UNLOCK(d); 65549c5e6e2SCameron Grant snd_mtxfree(d->lock); 656bba4862cSAriff Abdullah 657bba4862cSAriff Abdullah if (snd_unit == device_get_unit(dev)) { 658cbebc90dSAlexander Motin snd_unit = pcm_best_unit(-1); 659cbebc90dSAlexander Motin if (snd_unit_auto == 0) 660cbebc90dSAlexander Motin snd_unit_auto = 1; 661e4e61333SAriff Abdullah } 662bba4862cSAriff Abdullah 663e4e61333SAriff Abdullah return (0); 66433dbf14aSCameron Grant } 6657c438dbeSCameron Grant 66667b1dce3SCameron Grant /************************************************************************/ 66767b1dce3SCameron Grant 668b611c801SAlexander Leidinger /** 669b611c801SAlexander Leidinger * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 670b611c801SAlexander Leidinger * 671b611c801SAlexander Leidinger * @param si Pointer to oss_sysinfo struct where information about the 672b611c801SAlexander Leidinger * sound subsystem will be written/copied. 673b611c801SAlexander Leidinger * 674b611c801SAlexander Leidinger * This routine returns information about the sound system, such as the 675b611c801SAlexander Leidinger * current OSS version, number of audio, MIDI, and mixer drivers, etc. 676b611c801SAlexander Leidinger * Also includes a bitmask showing which of the above types of devices 677b611c801SAlexander Leidinger * are open (busy). 678b611c801SAlexander Leidinger * 679b611c801SAlexander Leidinger * @note 680b611c801SAlexander Leidinger * Calling threads must not hold any snddev_info or pcm_channel locks. 681b611c801SAlexander Leidinger * 682b611c801SAlexander Leidinger * @author Ryan Beasley <ryanb@FreeBSD.org> 683b611c801SAlexander Leidinger */ 684b611c801SAlexander Leidinger void 685b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si) 686b611c801SAlexander Leidinger { 687b611c801SAlexander Leidinger static char si_product[] = "FreeBSD native OSS ABI"; 688b611c801SAlexander Leidinger static char si_version[] = __XSTRING(__FreeBSD_version); 689c412d503SAlexander Motin static char si_license[] = "BSD"; 690b611c801SAlexander Leidinger static int intnbits = sizeof(int) * 8; /* Better suited as macro? 691b611c801SAlexander Leidinger Must pester a C guru. */ 692b611c801SAlexander Leidinger 693b611c801SAlexander Leidinger struct snddev_info *d; 694b611c801SAlexander Leidinger struct pcm_channel *c; 69559d98edaSChristos Margiolis int j; 696c597c557SChristos Margiolis size_t i; 697b611c801SAlexander Leidinger 698b611c801SAlexander Leidinger strlcpy(si->product, si_product, sizeof(si->product)); 699b611c801SAlexander Leidinger strlcpy(si->version, si_version, sizeof(si->version)); 700b611c801SAlexander Leidinger si->versionnum = SOUND_VERSION; 701c412d503SAlexander Motin strlcpy(si->license, si_license, sizeof(si->license)); 702b611c801SAlexander Leidinger 703b611c801SAlexander Leidinger /* 704b611c801SAlexander Leidinger * Iterate over PCM devices and their channels, gathering up data 705e07f9178SChristos Margiolis * for the numaudioengines and openedaudio fields. 706b611c801SAlexander Leidinger */ 707e07f9178SChristos Margiolis si->numaudioengines = 0; 708b611c801SAlexander Leidinger bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 709b611c801SAlexander Leidinger 710b611c801SAlexander Leidinger j = 0; 711b611c801SAlexander Leidinger 7129c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 7139c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 714b611c801SAlexander Leidinger d = devclass_get_softc(pcm_devclass, i); 715e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 716b611c801SAlexander Leidinger continue; 717b611c801SAlexander Leidinger 718e4e61333SAriff Abdullah /* XXX Need Giant magic entry ??? */ 719e4e61333SAriff Abdullah 720b611c801SAlexander Leidinger /* See note in function's docblock */ 72190da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 72290da2b28SAriff Abdullah PCM_LOCK(d); 723b611c801SAlexander Leidinger 724e07f9178SChristos Margiolis si->numaudioengines += PCM_CHANCOUNT(d); 725b611c801SAlexander Leidinger 726bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 72790da2b28SAriff Abdullah CHN_UNLOCKASSERT(c); 728b611c801SAlexander Leidinger CHN_LOCK(c); 729b611c801SAlexander Leidinger if (c->flags & CHN_F_BUSY) 730b611c801SAlexander Leidinger si->openedaudio[j / intnbits] |= 731b611c801SAlexander Leidinger (1 << (j % intnbits)); 732b611c801SAlexander Leidinger CHN_UNLOCK(c); 733b611c801SAlexander Leidinger j++; 734b611c801SAlexander Leidinger } 735b611c801SAlexander Leidinger 73690da2b28SAriff Abdullah PCM_UNLOCK(d); 737b611c801SAlexander Leidinger } 738b611c801SAlexander Leidinger 739b611c801SAlexander Leidinger si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 740b611c801SAlexander Leidinger /** 741b611c801SAlexander Leidinger * @todo Collect num{midis,timers}. 742b611c801SAlexander Leidinger * 743b611c801SAlexander Leidinger * Need access to sound/midi/midi.c::midistat_lock in order 744b611c801SAlexander Leidinger * to safely touch midi_devices and get a head count of, well, 745b611c801SAlexander Leidinger * MIDI devices. midistat_lock is a global static (i.e., local to 746b611c801SAlexander Leidinger * midi.c), but midi_devices is a regular global; should the mutex 747b611c801SAlexander Leidinger * be publicized, or is there another way to get this information? 748b611c801SAlexander Leidinger * 749b611c801SAlexander Leidinger * NB: MIDI/sequencer stuff is currently on hold. 750b611c801SAlexander Leidinger */ 751b611c801SAlexander Leidinger si->nummidis = 0; 752b611c801SAlexander Leidinger si->numtimers = 0; 7535d980fadSChristos Margiolis /* 7545d980fadSChristos Margiolis * Set this to the maximum unit number so that applications will not 7555d980fadSChristos Margiolis * break if they try to loop through all mixers and some of them are 7565d980fadSChristos Margiolis * not available. 7575d980fadSChristos Margiolis */ 7585d980fadSChristos Margiolis si->nummixers = devclass_get_maxunit(pcm_devclass); 75959d98edaSChristos Margiolis si->numcards = devclass_get_maxunit(pcm_devclass); 760e07f9178SChristos Margiolis si->numaudios = devclass_get_maxunit(pcm_devclass); 761b611c801SAlexander Leidinger /* OSSv4 docs: Intended only for test apps; API doesn't 762b611c801SAlexander Leidinger really have much of a concept of cards. Shouldn't be 763b611c801SAlexander Leidinger used by applications. */ 764b611c801SAlexander Leidinger 765b611c801SAlexander Leidinger /** 766b611c801SAlexander Leidinger * @todo Fill in "busy devices" fields. 767b611c801SAlexander Leidinger * 768b611c801SAlexander Leidinger * si->openedmidi = " MIDI devices 769b611c801SAlexander Leidinger */ 770b611c801SAlexander Leidinger bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 771b611c801SAlexander Leidinger 772b611c801SAlexander Leidinger /* 773b611c801SAlexander Leidinger * Si->filler is a reserved array, but according to docs each 774b611c801SAlexander Leidinger * element should be set to -1. 775b611c801SAlexander Leidinger */ 776c597c557SChristos Margiolis for (i = 0; i < nitems(si->filler); i++) 777b611c801SAlexander Leidinger si->filler[i] = -1; 778b611c801SAlexander Leidinger } 779b611c801SAlexander Leidinger 78052f6e09eSAlexander Motin int 78152f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si) 78252f6e09eSAlexander Motin { 78352f6e09eSAlexander Motin struct snddev_info *d; 784305db91dSChristos Margiolis int i; 78552f6e09eSAlexander Motin 78652f6e09eSAlexander Motin for (i = 0; pcm_devclass != NULL && 78752f6e09eSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 78852f6e09eSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 789305db91dSChristos Margiolis if (i != si->card) 79052f6e09eSAlexander Motin continue; 79152f6e09eSAlexander Motin 7925d980fadSChristos Margiolis if (!PCM_REGISTERED(d)) { 7935d980fadSChristos Margiolis snprintf(si->shortname, sizeof(si->shortname), 7945d980fadSChristos Margiolis "pcm%d (n/a)", i); 7955d980fadSChristos Margiolis strlcpy(si->longname, "Device unavailable", 7965d980fadSChristos Margiolis sizeof(si->longname)); 7975d980fadSChristos Margiolis si->hw_info[0] = '\0'; 7985d980fadSChristos Margiolis si->intr_count = si->ack_count = 0; 7995d980fadSChristos Margiolis } else { 80090da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 80190da2b28SAriff Abdullah PCM_LOCK(d); 80252f6e09eSAlexander Motin 80352f6e09eSAlexander Motin strlcpy(si->shortname, device_get_nameunit(d->dev), 80452f6e09eSAlexander Motin sizeof(si->shortname)); 80552f6e09eSAlexander Motin strlcpy(si->longname, device_get_desc(d->dev), 80652f6e09eSAlexander Motin sizeof(si->longname)); 80752f6e09eSAlexander Motin strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 80852f6e09eSAlexander Motin si->intr_count = si->ack_count = 0; 80990da2b28SAriff Abdullah 81090da2b28SAriff Abdullah PCM_UNLOCK(d); 8115d980fadSChristos Margiolis } 81290da2b28SAriff Abdullah 81352f6e09eSAlexander Motin return (0); 81452f6e09eSAlexander Motin } 81552f6e09eSAlexander Motin return (ENXIO); 81652f6e09eSAlexander Motin } 81752f6e09eSAlexander Motin 818b611c801SAlexander Leidinger /************************************************************************/ 819b611c801SAlexander Leidinger 8200739ea1dSSeigo Tanimura static int 8210739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data) 8220739ea1dSSeigo Tanimura { 823b611c801SAlexander Leidinger int ret; 824b611c801SAlexander Leidinger 8258109ec9dSJohn Baldwin ret = 0; 826b611c801SAlexander Leidinger switch (type) { 827b611c801SAlexander Leidinger case MOD_LOAD: 82813bebcd3SJohn Baldwin pcm_devclass = devclass_create("pcm"); 829b611c801SAlexander Leidinger pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 830b611c801SAlexander Leidinger break; 831b611c801SAlexander Leidinger case MOD_UNLOAD: 832b611c801SAlexander Leidinger if (pcmsg_unrhdr != NULL) { 833b611c801SAlexander Leidinger delete_unrhdr(pcmsg_unrhdr); 834b611c801SAlexander Leidinger pcmsg_unrhdr = NULL; 835b611c801SAlexander Leidinger } 836b611c801SAlexander Leidinger break; 8375525b53dSAlexander Motin case MOD_SHUTDOWN: 8385525b53dSAlexander Motin break; 839b611c801SAlexander Leidinger default: 84090da2b28SAriff Abdullah ret = ENOTSUP; 841b611c801SAlexander Leidinger } 842b611c801SAlexander Leidinger 843b611c801SAlexander Leidinger return ret; 8440739ea1dSSeigo Tanimura } 8450739ea1dSSeigo Tanimura 8460739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL); 8470739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER); 848