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 /* return error status and a locked channel */ 1133fdb3676SAriff Abdullah int 1143fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 11525723d66SChristos Margiolis pid_t pid, char *comm) 116285648f9SCameron Grant { 117285648f9SCameron Grant struct pcm_channel *c; 11890da2b28SAriff Abdullah int err, vchancount, vchan_num; 11925723d66SChristos Margiolis bool retry; 120bba4862cSAriff Abdullah 12125723d66SChristos Margiolis KASSERT(d != NULL && ch != NULL && 122bba4862cSAriff Abdullah (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 12325723d66SChristos Margiolis ("%s(): invalid d=%p ch=%p direction=%d pid=%d", 12425723d66SChristos Margiolis __func__, d, ch, direction, pid)); 125e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 126bba4862cSAriff Abdullah 12790da2b28SAriff Abdullah *ch = NULL; 12890da2b28SAriff Abdullah vchan_num = 0; 12990da2b28SAriff Abdullah vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : 13090da2b28SAriff Abdullah d->rvchancount; 13190da2b28SAriff Abdullah 13225723d66SChristos Margiolis retry = false; 1333fdb3676SAriff Abdullah retry_chnalloc: 13490da2b28SAriff Abdullah err = ENOTSUP; 135f637a36cSCameron Grant /* scan for a free channel */ 136bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 13749c5e6e2SCameron Grant CHN_LOCK(c); 13825723d66SChristos Margiolis if (c->direction == direction && (c->flags & CHN_F_VIRTUAL)) { 13990da2b28SAriff Abdullah if (vchancount < snd_maxautovchans && 14025723d66SChristos Margiolis vchan_num < c->unit) { 14190da2b28SAriff Abdullah CHN_UNLOCK(c); 14290da2b28SAriff Abdullah goto vchan_alloc; 14390da2b28SAriff Abdullah } 14490da2b28SAriff Abdullah vchan_num++; 14590da2b28SAriff Abdullah } 14625723d66SChristos Margiolis if (c->direction == direction && !(c->flags & CHN_F_BUSY)) { 147285648f9SCameron Grant c->flags |= CHN_F_BUSY; 148b8f0d9e0SCameron Grant c->pid = pid; 14990da2b28SAriff Abdullah strlcpy(c->comm, (comm != NULL) ? comm : 15090da2b28SAriff Abdullah CHN_COMM_UNKNOWN, sizeof(c->comm)); 1513fdb3676SAriff Abdullah *ch = c; 152bba4862cSAriff Abdullah return (0); 15325723d66SChristos Margiolis } else if (c->direction == direction && (c->flags & CHN_F_BUSY)) 154a1d444e1SAriff Abdullah err = EBUSY; 15549c5e6e2SCameron Grant CHN_UNLOCK(c); 156285648f9SCameron Grant } 157f637a36cSCameron Grant 15825723d66SChristos Margiolis /* 15925723d66SChristos Margiolis * We came from retry_chnalloc and still didn't find a free channel. 16025723d66SChristos Margiolis */ 16125723d66SChristos Margiolis if (retry) 162e4e61333SAriff Abdullah return (err); 163e4e61333SAriff Abdullah 16490da2b28SAriff Abdullah vchan_alloc: 165f637a36cSCameron Grant /* no channel available */ 16625723d66SChristos Margiolis if (!(vchancount > 0 && vchancount < snd_maxautovchans)) 167bba4862cSAriff Abdullah return (err); 1683af2beb8SChristos Margiolis err = vchan_setnew(d, direction, vchancount + 1); 169a1d444e1SAriff Abdullah if (err == 0) { 17025723d66SChristos Margiolis retry = true; 1713fdb3676SAriff Abdullah goto retry_chnalloc; 172f637a36cSCameron Grant } 173f637a36cSCameron Grant 174bba4862cSAriff Abdullah return (err); 175285648f9SCameron Grant } 176285648f9SCameron Grant 17733dbf14aSCameron Grant static int 178851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 17933dbf14aSCameron Grant { 180b8f0d9e0SCameron Grant struct snddev_info *d; 18133dbf14aSCameron Grant int error, unit; 18233dbf14aSCameron Grant 18333dbf14aSCameron Grant unit = snd_unit; 184041b706bSDavid Malone error = sysctl_handle_int(oidp, &unit, 0, req); 18533dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) { 186b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit); 187e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 188b8f0d9e0SCameron Grant return EINVAL; 18933dbf14aSCameron Grant snd_unit = unit; 190cbebc90dSAlexander Motin snd_unit_auto = 0; 19133dbf14aSCameron Grant } 19233dbf14aSCameron Grant return (error); 19333dbf14aSCameron Grant } 194851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */ 195b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, 1967029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0, 1977029da5cSPawel Biernacki sizeof(int), sysctl_hw_snd_default_unit, "I", 198b7d6c6b5SEitan Adler "default sound device"); 199987e5972SCameron Grant 200139bcec8SChristos Margiolis void 2015ee30e27SMathew Kanner pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 202285648f9SCameron Grant { 203e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 20490da2b28SAriff Abdullah PCM_LOCKASSERT(d); 205bba4862cSAriff Abdullah KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 206bba4862cSAriff Abdullah ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 207285648f9SCameron Grant 20890da2b28SAriff Abdullah CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); 2093fdb3676SAriff Abdullah 21025723d66SChristos Margiolis switch (ch->type) { 211e4e61333SAriff Abdullah case SND_DEV_DSPHW_PLAY: 212e4e61333SAriff Abdullah d->playcount++; 213e4e61333SAriff Abdullah break; 214e4e61333SAriff Abdullah case SND_DEV_DSPHW_VPLAY: 215e4e61333SAriff Abdullah d->pvchancount++; 216e4e61333SAriff Abdullah break; 217e4e61333SAriff Abdullah case SND_DEV_DSPHW_REC: 218e4e61333SAriff Abdullah d->reccount++; 219e4e61333SAriff Abdullah break; 220e4e61333SAriff Abdullah case SND_DEV_DSPHW_VREC: 221e4e61333SAriff Abdullah d->rvchancount++; 222e4e61333SAriff Abdullah break; 223e4e61333SAriff Abdullah default: 22476f95baeSChristos Margiolis __assert_unreachable(); 225e4e61333SAriff Abdullah } 22633dbf14aSCameron Grant } 22733dbf14aSCameron Grant 228285648f9SCameron Grant int 2295ee30e27SMathew Kanner pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 23033dbf14aSCameron Grant { 231bba4862cSAriff Abdullah struct pcm_channel *tmp; 23233dbf14aSCameron Grant 233e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 23490da2b28SAriff Abdullah PCM_LOCKASSERT(d); 235e4e61333SAriff Abdullah 236bba4862cSAriff Abdullah tmp = NULL; 237a527dbc7SCameron Grant 238bba4862cSAriff Abdullah CHN_FOREACH(tmp, d, channels.pcm) { 239bba4862cSAriff Abdullah if (tmp == ch) 240bba4862cSAriff Abdullah break; 24133dbf14aSCameron Grant } 242bba4862cSAriff Abdullah 243bba4862cSAriff Abdullah if (tmp != ch) 244e4e61333SAriff Abdullah return (EINVAL); 24567beb5a5SCameron Grant 246bba4862cSAriff Abdullah CHN_REMOVE(d, ch, channels.pcm); 247e4e61333SAriff Abdullah 24825723d66SChristos Margiolis switch (ch->type) { 249bba4862cSAriff Abdullah case SND_DEV_DSPHW_PLAY: 25067beb5a5SCameron Grant d->playcount--; 251bba4862cSAriff Abdullah break; 252bba4862cSAriff Abdullah case SND_DEV_DSPHW_VPLAY: 253bba4862cSAriff Abdullah d->pvchancount--; 254bba4862cSAriff Abdullah break; 255bba4862cSAriff Abdullah case SND_DEV_DSPHW_REC: 256bba4862cSAriff Abdullah d->reccount--; 257bba4862cSAriff Abdullah break; 258bba4862cSAriff Abdullah case SND_DEV_DSPHW_VREC: 259bba4862cSAriff Abdullah d->rvchancount--; 260bba4862cSAriff Abdullah break; 261bba4862cSAriff Abdullah default: 26276f95baeSChristos Margiolis __assert_unreachable(); 263bba4862cSAriff Abdullah } 264285648f9SCameron Grant 265e4e61333SAriff Abdullah return (0); 266987e5972SCameron Grant } 267987e5972SCameron Grant 268987e5972SCameron Grant int 269285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 270285648f9SCameron Grant { 271285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev); 27267b1dce3SCameron Grant struct pcm_channel *ch; 273285648f9SCameron Grant 274e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 275e4e61333SAriff Abdullah 27690da2b28SAriff Abdullah PCM_LOCK(d); 2773af2beb8SChristos Margiolis ch = chn_init(d, NULL, cls, dir, devinfo); 278285648f9SCameron Grant if (!ch) { 2792e9962efSChristos Margiolis device_printf(d->dev, "chn_init(%s, %d, %p) failed\n", 280e4e61333SAriff Abdullah cls->name, dir, devinfo); 28190da2b28SAriff Abdullah PCM_UNLOCK(d); 282e4e61333SAriff Abdullah return (ENODEV); 283285648f9SCameron Grant } 284cd9766c5SCameron Grant 285139bcec8SChristos Margiolis pcm_chn_add(d, ch); 28690da2b28SAriff Abdullah PCM_UNLOCK(d); 287cd9766c5SCameron Grant 288139bcec8SChristos Margiolis return (0); 289285648f9SCameron Grant } 290285648f9SCameron Grant 29103614fcbSChristos Margiolis static void 29203614fcbSChristos Margiolis pcm_killchans(struct snddev_info *d) 293285648f9SCameron Grant { 294e33bee07SOlivier Houchard struct pcm_channel *ch; 295e4e61333SAriff Abdullah int error; 29603614fcbSChristos Margiolis bool found; 297e4e61333SAriff Abdullah 298e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 29903614fcbSChristos Margiolis do { 30003614fcbSChristos Margiolis found = false; 30103614fcbSChristos Margiolis CHN_FOREACH(ch, d, channels.pcm) { 30203614fcbSChristos Margiolis CHN_LOCK(ch); 30303614fcbSChristos Margiolis /* 30403614fcbSChristos Margiolis * Make sure no channel has went to sleep in the 30503614fcbSChristos Margiolis * meantime. 30603614fcbSChristos Margiolis */ 30703614fcbSChristos Margiolis chn_shutdown(ch); 30803614fcbSChristos Margiolis /* 30903614fcbSChristos Margiolis * We have to give a thread sleeping in chn_sleep() a 31003614fcbSChristos Margiolis * chance to observe that the channel is dead. 31103614fcbSChristos Margiolis */ 31203614fcbSChristos Margiolis if ((ch->flags & CHN_F_SLEEPING) == 0) { 31303614fcbSChristos Margiolis found = true; 31403614fcbSChristos Margiolis CHN_UNLOCK(ch); 31503614fcbSChristos Margiolis break; 31603614fcbSChristos Margiolis } 31703614fcbSChristos Margiolis CHN_UNLOCK(ch); 31803614fcbSChristos Margiolis } 319285648f9SCameron Grant 32003614fcbSChristos Margiolis /* 32103614fcbSChristos Margiolis * All channels are still sleeping. Sleep for a bit and try 32203614fcbSChristos Margiolis * again to see if any of them is awake now. 32303614fcbSChristos Margiolis */ 32403614fcbSChristos Margiolis if (!found) { 32503614fcbSChristos Margiolis pause_sbt("pcmkillchans", SBT_1MS * 5, 0, 0); 32603614fcbSChristos Margiolis continue; 32703614fcbSChristos Margiolis } 328285648f9SCameron Grant 32990da2b28SAriff Abdullah PCM_LOCK(d); 330bba4862cSAriff Abdullah error = pcm_chn_remove(d, ch); 33190da2b28SAriff Abdullah PCM_UNLOCK(d); 33203614fcbSChristos Margiolis if (error == 0) 333b3ea087cSChristos Margiolis chn_kill(ch); 33403614fcbSChristos Margiolis } while (!CHN_EMPTY(d, channels.pcm)); 335285648f9SCameron Grant } 336285648f9SCameron Grant 337cbebc90dSAlexander Motin static int 338cbebc90dSAlexander Motin pcm_best_unit(int old) 339cbebc90dSAlexander Motin { 340cbebc90dSAlexander Motin struct snddev_info *d; 341cbebc90dSAlexander Motin int i, best, bestprio, prio; 342cbebc90dSAlexander Motin 343cbebc90dSAlexander Motin best = -1; 344cbebc90dSAlexander Motin bestprio = -100; 345cbebc90dSAlexander Motin for (i = 0; pcm_devclass != NULL && 346cbebc90dSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 347cbebc90dSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 348cbebc90dSAlexander Motin if (!PCM_REGISTERED(d)) 349cbebc90dSAlexander Motin continue; 350cbebc90dSAlexander Motin prio = 0; 351cbebc90dSAlexander Motin if (d->playcount == 0) 352cbebc90dSAlexander Motin prio -= 10; 353cbebc90dSAlexander Motin if (d->reccount == 0) 354cbebc90dSAlexander Motin prio -= 2; 355cbebc90dSAlexander Motin if (prio > bestprio || (prio == bestprio && i == old)) { 356cbebc90dSAlexander Motin best = i; 357cbebc90dSAlexander Motin bestprio = prio; 358cbebc90dSAlexander Motin } 359cbebc90dSAlexander Motin } 360cbebc90dSAlexander Motin return (best); 361cbebc90dSAlexander Motin } 362cbebc90dSAlexander Motin 363285648f9SCameron Grant int 364987e5972SCameron Grant pcm_setstatus(device_t dev, char *str) 365987e5972SCameron Grant { 36666ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 36749c5e6e2SCameron Grant 36832a0e5d5SHans Petter Selasky /* should only be called once */ 36932a0e5d5SHans Petter Selasky if (d->flags & SD_F_REGISTERED) 37032a0e5d5SHans Petter Selasky return (EINVAL); 37132a0e5d5SHans Petter Selasky 372e4e61333SAriff Abdullah PCM_BUSYASSERT(d); 373bba4862cSAriff Abdullah 374e4e61333SAriff Abdullah if (d->playcount == 0 || d->reccount == 0) 375e4e61333SAriff Abdullah d->flags |= SD_F_SIMPLEX; 376e4e61333SAriff Abdullah 37732a0e5d5SHans Petter Selasky if (d->playcount > 0 || d->reccount > 0) 378e4e61333SAriff Abdullah d->flags |= SD_F_AUTOVCHAN; 379e4e61333SAriff Abdullah 3807ad5f383SChristos Margiolis vchan_setmaxauto(d, snd_maxautovchans); 381bba4862cSAriff Abdullah 382a580b31aSAriff Abdullah strlcpy(d->status, str, SND_STATUSLEN); 383bba4862cSAriff Abdullah 38490da2b28SAriff Abdullah PCM_LOCK(d); 385e4e61333SAriff Abdullah 386e4e61333SAriff Abdullah /* Done, we're ready.. */ 387e4e61333SAriff Abdullah d->flags |= SD_F_REGISTERED; 388e4e61333SAriff Abdullah 389e4e61333SAriff Abdullah PCM_RELEASE(d); 390bba4862cSAriff Abdullah 39190da2b28SAriff Abdullah PCM_UNLOCK(d); 392bba4862cSAriff Abdullah 39332a0e5d5SHans Petter Selasky /* 39432a0e5d5SHans Petter Selasky * Create all sysctls once SD_F_REGISTERED is set else 39532a0e5d5SHans Petter Selasky * tunable sysctls won't work: 39632a0e5d5SHans Petter Selasky */ 39732a0e5d5SHans Petter Selasky pcm_sysinit(dev); 39832a0e5d5SHans Petter Selasky 399cbebc90dSAlexander Motin if (snd_unit_auto < 0) 400cbebc90dSAlexander Motin snd_unit_auto = (snd_unit < 0) ? 1 : 0; 401cbebc90dSAlexander Motin if (snd_unit < 0 || snd_unit_auto > 1) 402f3685841SAriff Abdullah snd_unit = device_get_unit(dev); 403cbebc90dSAlexander Motin else if (snd_unit_auto == 1) 404cbebc90dSAlexander Motin snd_unit = pcm_best_unit(snd_unit); 405f3685841SAriff Abdullah 406e4e61333SAriff Abdullah return (0); 407987e5972SCameron Grant } 408987e5972SCameron Grant 409a1d444e1SAriff Abdullah uint32_t 410987e5972SCameron Grant pcm_getflags(device_t dev) 411987e5972SCameron Grant { 41266ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 41349c5e6e2SCameron Grant 414987e5972SCameron Grant return d->flags; 415987e5972SCameron Grant } 416987e5972SCameron Grant 417987e5972SCameron Grant void 418a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val) 419987e5972SCameron Grant { 42066ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 421d95502a8SCameron Grant 422987e5972SCameron Grant d->flags = val; 423987e5972SCameron Grant } 424987e5972SCameron Grant 42539004e69SCameron Grant void * 42639004e69SCameron Grant pcm_getdevinfo(device_t dev) 42739004e69SCameron Grant { 42866ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev); 42949c5e6e2SCameron Grant 43039004e69SCameron Grant return d->devinfo; 43139004e69SCameron Grant } 43239004e69SCameron Grant 433a67fe5c1SCameron Grant unsigned int 434d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 435a67fe5c1SCameron Grant { 436a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev); 4374e60be34SCameron Grant int sz, x; 438a67fe5c1SCameron Grant 439a67fe5c1SCameron Grant sz = 0; 4404e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 4414e60be34SCameron Grant x = sz; 442d55d96f6SAlexander Leidinger RANGE(sz, minbufsz, maxbufsz); 4434e60be34SCameron Grant if (x != sz) 444d55d96f6SAlexander Leidinger device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 445d55d96f6SAlexander Leidinger x = minbufsz; 4464e60be34SCameron Grant while (x < sz) 4474e60be34SCameron Grant x <<= 1; 4484e60be34SCameron Grant if (x > sz) 4494e60be34SCameron Grant x >>= 1; 4504e60be34SCameron Grant if (x != sz) { 4514e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 4524e60be34SCameron Grant sz = x; 4534e60be34SCameron Grant } 4544e60be34SCameron Grant } else { 455a67fe5c1SCameron Grant sz = deflt; 4564e60be34SCameron Grant } 4574e60be34SCameron Grant 458a67fe5c1SCameron Grant d->bufsz = sz; 459a67fe5c1SCameron Grant 460a67fe5c1SCameron Grant return sz; 461a67fe5c1SCameron Grant } 462a67fe5c1SCameron Grant 46390da2b28SAriff Abdullah static int 46490da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 46590da2b28SAriff Abdullah { 46690da2b28SAriff Abdullah struct snddev_info *d; 46790da2b28SAriff Abdullah int err, val; 46890da2b28SAriff Abdullah 46990da2b28SAriff Abdullah d = oidp->oid_arg1; 47090da2b28SAriff Abdullah if (!PCM_REGISTERED(d)) 47190da2b28SAriff Abdullah return (ENODEV); 47290da2b28SAriff Abdullah 47390da2b28SAriff Abdullah PCM_LOCK(d); 47490da2b28SAriff Abdullah PCM_WAIT(d); 47590da2b28SAriff Abdullah val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 47690da2b28SAriff Abdullah PCM_ACQUIRE(d); 47790da2b28SAriff Abdullah PCM_UNLOCK(d); 47890da2b28SAriff Abdullah 47990da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &val, 0, req); 48090da2b28SAriff Abdullah 48190da2b28SAriff Abdullah if (err == 0 && req->newptr != NULL) { 48290da2b28SAriff Abdullah if (!(val == 0 || val == 1)) { 48390da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 48490da2b28SAriff Abdullah return (EINVAL); 48590da2b28SAriff Abdullah } 48690da2b28SAriff Abdullah 48790da2b28SAriff Abdullah PCM_LOCK(d); 48890da2b28SAriff Abdullah 48990da2b28SAriff Abdullah d->flags &= ~SD_F_BITPERFECT; 49090da2b28SAriff Abdullah d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 49190da2b28SAriff Abdullah 49290da2b28SAriff Abdullah PCM_RELEASE(d); 49390da2b28SAriff Abdullah PCM_UNLOCK(d); 49490da2b28SAriff Abdullah } else 49590da2b28SAriff Abdullah PCM_RELEASE_QUICK(d); 49690da2b28SAriff Abdullah 49790da2b28SAriff Abdullah return (err); 49890da2b28SAriff Abdullah } 49990da2b28SAriff Abdullah 500ed2196e5SHans Petter Selasky static u_int8_t 501ed2196e5SHans Petter Selasky pcm_mode_init(struct snddev_info *d) 502ed2196e5SHans Petter Selasky { 503ed2196e5SHans Petter Selasky u_int8_t mode = 0; 504ed2196e5SHans Petter Selasky 505ed2196e5SHans Petter Selasky if (d->playcount > 0) 506ed2196e5SHans Petter Selasky mode |= PCM_MODE_PLAY; 507ed2196e5SHans Petter Selasky if (d->reccount > 0) 508ed2196e5SHans Petter Selasky mode |= PCM_MODE_REC; 509ed2196e5SHans Petter Selasky if (d->mixer_dev != NULL) 510ed2196e5SHans Petter Selasky mode |= PCM_MODE_MIXER; 511ed2196e5SHans Petter Selasky 512ed2196e5SHans Petter Selasky return (mode); 513ed2196e5SHans Petter Selasky } 514ed2196e5SHans Petter Selasky 51532a0e5d5SHans Petter Selasky static void 51632a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev) 51732a0e5d5SHans Petter Selasky { 51832a0e5d5SHans Petter Selasky struct snddev_info *d = device_get_softc(dev); 519ed2196e5SHans Petter Selasky u_int8_t mode; 520ed2196e5SHans Petter Selasky 521ed2196e5SHans Petter Selasky mode = pcm_mode_init(d); 52232a0e5d5SHans Petter Selasky 523132fca63SHans Petter Selasky /* XXX: a user should be able to set this with a control tool, the 52432a0e5d5SHans Petter Selasky sysadmin then needs min+max sysctls for this */ 52532a0e5d5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 52632a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 52732a0e5d5SHans Petter Selasky OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 52832a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 52932a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 5303b4c5433SAlexander Motin "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, 5317029da5cSPawel Biernacki sizeof(d), sysctl_dev_pcm_bitperfect, "I", 53232a0e5d5SHans Petter Selasky "bit-perfect playback/recording (0=disable, 1=enable)"); 533ed2196e5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 534ed2196e5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 535ed2196e5SHans Petter Selasky OID_AUTO, "mode", CTLFLAG_RD, NULL, mode, 536e56c8996SChristos Margiolis "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than " 537e56c8996SChristos Margiolis "one mode is supported)"); 53832a0e5d5SHans Petter Selasky if (d->flags & SD_F_AUTOVCHAN) 53932a0e5d5SHans Petter Selasky vchan_initsys(dev); 54032a0e5d5SHans Petter Selasky if (d->flags & SD_F_EQ) 54132a0e5d5SHans Petter Selasky feeder_eq_initsys(dev); 54232a0e5d5SHans Petter Selasky } 54332a0e5d5SHans Petter Selasky 544987e5972SCameron Grant int 545987e5972SCameron Grant pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 546987e5972SCameron Grant { 547bba4862cSAriff Abdullah struct snddev_info *d; 54890da2b28SAriff Abdullah int i; 549987e5972SCameron Grant 550b8a36395SCameron Grant if (pcm_veto_load) { 551b8a36395SCameron Grant device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 552b8a36395SCameron Grant 553b8a36395SCameron Grant return EINVAL; 554b8a36395SCameron Grant } 555b8a36395SCameron Grant 556bba4862cSAriff Abdullah d = device_get_softc(dev); 557e4e61333SAriff Abdullah d->dev = dev; 5582c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 559e4e61333SAriff Abdullah cv_init(&d->cv, device_get_nameunit(dev)); 560e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d); 5617233ababSAlexander Leidinger #if 0 5627233ababSAlexander Leidinger /* 5637233ababSAlexander Leidinger * d->flags should be cleared by the allocator of the softc. 5647233ababSAlexander Leidinger * We cannot clear this field here because several devices set 5657233ababSAlexander Leidinger * this flag before calling pcm_register(). 5667233ababSAlexander Leidinger */ 567cd9766c5SCameron Grant d->flags = 0; 5687233ababSAlexander Leidinger #endif 56990da2b28SAriff Abdullah i = 0; 57090da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 57190da2b28SAriff Abdullah "vpc", &i) != 0 || i != 0) 57290da2b28SAriff Abdullah d->flags |= SD_F_VPC; 57390da2b28SAriff Abdullah 57490da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 57590da2b28SAriff Abdullah "bitperfect", &i) == 0 && i != 0) 57690da2b28SAriff Abdullah d->flags |= SD_F_BITPERFECT; 57790da2b28SAriff Abdullah 578987e5972SCameron Grant d->devinfo = devinfo; 579506a5308SCameron Grant d->reccount = 0; 580a67fe5c1SCameron Grant d->playcount = 0; 581bba4862cSAriff Abdullah d->pvchancount = 0; 582bba4862cSAriff Abdullah d->rvchancount = 0; 583bba4862cSAriff Abdullah d->pvchanrate = 0; 584bba4862cSAriff Abdullah d->pvchanformat = 0; 585bba4862cSAriff Abdullah d->rvchanrate = 0; 586bba4862cSAriff Abdullah d->rvchanformat = 0; 587833f7023SCameron Grant 588bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm); 589bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm.busy); 59090da2b28SAriff Abdullah CHN_INIT(d, channels.pcm.opened); 59145550658SPoul-Henning Kamp 592e4e61333SAriff Abdullah /* XXX This is incorrect, but lets play along for now. */ 593a1d444e1SAriff Abdullah if ((numplay == 0 || numrec == 0) && numplay != numrec) 594285648f9SCameron Grant d->flags |= SD_F_SIMPLEX; 595d95502a8SCameron Grant 596bba4862cSAriff Abdullah sysctl_ctx_init(&d->play_sysctl_ctx); 597bba4862cSAriff Abdullah d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 598bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 5997029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node"); 600bba4862cSAriff Abdullah sysctl_ctx_init(&d->rec_sysctl_ctx); 601bba4862cSAriff Abdullah d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 602bba4862cSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 603132fca63SHans Petter Selasky CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node"); 604e4e61333SAriff Abdullah 60532a0e5d5SHans Petter Selasky if (numplay > 0 || numrec > 0) 606cd9766c5SCameron Grant d->flags |= SD_F_AUTOVCHAN; 60790da2b28SAriff Abdullah 6089da3b645SChristos Margiolis sndstat_register(dev, d->status); 609e4e61333SAriff Abdullah 610e8c0d15aSChristos Margiolis return (dsp_make_dev(dev)); 611987e5972SCameron Grant } 612987e5972SCameron Grant 61333dbf14aSCameron Grant int 61433dbf14aSCameron Grant pcm_unregister(device_t dev) 6157c438dbeSCameron Grant { 616e4e61333SAriff Abdullah struct snddev_info *d; 617a67fe5c1SCameron Grant struct pcm_channel *ch; 6187c438dbeSCameron Grant 619e4e61333SAriff Abdullah d = device_get_softc(dev); 620e4e61333SAriff Abdullah 621e4e61333SAriff Abdullah if (!PCM_ALIVE(d)) { 622e4e61333SAriff Abdullah device_printf(dev, "unregister: device not configured\n"); 623e4e61333SAriff Abdullah return (0); 624e4e61333SAriff Abdullah } 625bba4862cSAriff Abdullah 62690da2b28SAriff Abdullah PCM_LOCK(d); 627e4e61333SAriff Abdullah PCM_WAIT(d); 628e4e61333SAriff Abdullah 629cc1efc23SHans Petter Selasky d->flags |= SD_F_DETACHING; 630cc1efc23SHans Petter Selasky 631e4e61333SAriff Abdullah PCM_ACQUIRE(d); 63290da2b28SAriff Abdullah PCM_UNLOCK(d); 633e4e61333SAriff Abdullah 634e4e61333SAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) { 635e4e61333SAriff Abdullah CHN_LOCK(ch); 63644e128feSChristos Margiolis /* 63703614fcbSChristos Margiolis * Do not wait for the timeout in chn_read()/chn_write(). Wake 63803614fcbSChristos Margiolis * up the sleeping thread and kill the channel. 63944e128feSChristos Margiolis */ 64003614fcbSChristos Margiolis chn_shutdown(ch); 64144e128feSChristos Margiolis chn_abort(ch); 642e4e61333SAriff Abdullah CHN_UNLOCK(ch); 643c9b53085SCameron Grant } 6445ee30e27SMathew Kanner 6458461d581SHans Petter Selasky /* remove /dev/sndstat entry first */ 6468461d581SHans Petter Selasky sndstat_unregister(dev); 6478461d581SHans Petter Selasky 64890da2b28SAriff Abdullah PCM_LOCK(d); 649e4e61333SAriff Abdullah d->flags |= SD_F_DYING; 650e4e61333SAriff Abdullah d->flags &= ~SD_F_REGISTERED; 65190da2b28SAriff Abdullah PCM_UNLOCK(d); 652e4e61333SAriff Abdullah 653bba4862cSAriff Abdullah if (d->play_sysctl_tree != NULL) { 654bba4862cSAriff Abdullah sysctl_ctx_free(&d->play_sysctl_ctx); 655bba4862cSAriff Abdullah d->play_sysctl_tree = NULL; 656bba4862cSAriff Abdullah } 657bba4862cSAriff Abdullah if (d->rec_sysctl_tree != NULL) { 658bba4862cSAriff Abdullah sysctl_ctx_free(&d->rec_sysctl_ctx); 659bba4862cSAriff Abdullah d->rec_sysctl_tree = NULL; 660a1d444e1SAriff Abdullah } 661bba4862cSAriff Abdullah 662074d6fbeSChristos Margiolis dsp_destroy_dev(dev); 663074d6fbeSChristos Margiolis (void)mixer_uninit(dev); 664074d6fbeSChristos Margiolis 66503614fcbSChristos Margiolis pcm_killchans(d); 6667c438dbeSCameron Grant 66790da2b28SAriff Abdullah PCM_LOCK(d); 668e4e61333SAriff Abdullah PCM_RELEASE(d); 669e4e61333SAriff Abdullah cv_destroy(&d->cv); 67090da2b28SAriff Abdullah PCM_UNLOCK(d); 67149c5e6e2SCameron Grant snd_mtxfree(d->lock); 672bba4862cSAriff Abdullah 673bba4862cSAriff Abdullah if (snd_unit == device_get_unit(dev)) { 674cbebc90dSAlexander Motin snd_unit = pcm_best_unit(-1); 675cbebc90dSAlexander Motin if (snd_unit_auto == 0) 676cbebc90dSAlexander Motin snd_unit_auto = 1; 677e4e61333SAriff Abdullah } 678bba4862cSAriff Abdullah 679e4e61333SAriff Abdullah return (0); 68033dbf14aSCameron Grant } 6817c438dbeSCameron Grant 68267b1dce3SCameron Grant /************************************************************************/ 68367b1dce3SCameron Grant 684b611c801SAlexander Leidinger /** 685b611c801SAlexander Leidinger * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 686b611c801SAlexander Leidinger * 687b611c801SAlexander Leidinger * @param si Pointer to oss_sysinfo struct where information about the 688b611c801SAlexander Leidinger * sound subsystem will be written/copied. 689b611c801SAlexander Leidinger * 690b611c801SAlexander Leidinger * This routine returns information about the sound system, such as the 691b611c801SAlexander Leidinger * current OSS version, number of audio, MIDI, and mixer drivers, etc. 692b611c801SAlexander Leidinger * Also includes a bitmask showing which of the above types of devices 693b611c801SAlexander Leidinger * are open (busy). 694b611c801SAlexander Leidinger * 695b611c801SAlexander Leidinger * @note 696b611c801SAlexander Leidinger * Calling threads must not hold any snddev_info or pcm_channel locks. 697b611c801SAlexander Leidinger * 698b611c801SAlexander Leidinger * @author Ryan Beasley <ryanb@FreeBSD.org> 699b611c801SAlexander Leidinger */ 700b611c801SAlexander Leidinger void 701b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si) 702b611c801SAlexander Leidinger { 703b611c801SAlexander Leidinger static char si_product[] = "FreeBSD native OSS ABI"; 704b611c801SAlexander Leidinger static char si_version[] = __XSTRING(__FreeBSD_version); 705c412d503SAlexander Motin static char si_license[] = "BSD"; 706b611c801SAlexander Leidinger static int intnbits = sizeof(int) * 8; /* Better suited as macro? 707b611c801SAlexander Leidinger Must pester a C guru. */ 708b611c801SAlexander Leidinger 709b611c801SAlexander Leidinger struct snddev_info *d; 710b611c801SAlexander Leidinger struct pcm_channel *c; 71159d98edaSChristos Margiolis int j; 712c597c557SChristos Margiolis size_t i; 713b611c801SAlexander Leidinger 714b611c801SAlexander Leidinger strlcpy(si->product, si_product, sizeof(si->product)); 715b611c801SAlexander Leidinger strlcpy(si->version, si_version, sizeof(si->version)); 716b611c801SAlexander Leidinger si->versionnum = SOUND_VERSION; 717c412d503SAlexander Motin strlcpy(si->license, si_license, sizeof(si->license)); 718b611c801SAlexander Leidinger 719b611c801SAlexander Leidinger /* 720b611c801SAlexander Leidinger * Iterate over PCM devices and their channels, gathering up data 721*e07f9178SChristos Margiolis * for the numaudioengines and openedaudio fields. 722b611c801SAlexander Leidinger */ 723*e07f9178SChristos Margiolis si->numaudioengines = 0; 724b611c801SAlexander Leidinger bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 725b611c801SAlexander Leidinger 726b611c801SAlexander Leidinger j = 0; 727b611c801SAlexander Leidinger 7289c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL && 7299c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) { 730b611c801SAlexander Leidinger d = devclass_get_softc(pcm_devclass, i); 731e4e61333SAriff Abdullah if (!PCM_REGISTERED(d)) 732b611c801SAlexander Leidinger continue; 733b611c801SAlexander Leidinger 734e4e61333SAriff Abdullah /* XXX Need Giant magic entry ??? */ 735e4e61333SAriff Abdullah 736b611c801SAlexander Leidinger /* See note in function's docblock */ 73790da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 73890da2b28SAriff Abdullah PCM_LOCK(d); 739b611c801SAlexander Leidinger 740*e07f9178SChristos Margiolis si->numaudioengines += PCM_CHANCOUNT(d); 741b611c801SAlexander Leidinger 742bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) { 74390da2b28SAriff Abdullah CHN_UNLOCKASSERT(c); 744b611c801SAlexander Leidinger CHN_LOCK(c); 745b611c801SAlexander Leidinger if (c->flags & CHN_F_BUSY) 746b611c801SAlexander Leidinger si->openedaudio[j / intnbits] |= 747b611c801SAlexander Leidinger (1 << (j % intnbits)); 748b611c801SAlexander Leidinger CHN_UNLOCK(c); 749b611c801SAlexander Leidinger j++; 750b611c801SAlexander Leidinger } 751b611c801SAlexander Leidinger 75290da2b28SAriff Abdullah PCM_UNLOCK(d); 753b611c801SAlexander Leidinger } 754b611c801SAlexander Leidinger 755b611c801SAlexander Leidinger si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 756b611c801SAlexander Leidinger /** 757b611c801SAlexander Leidinger * @todo Collect num{midis,timers}. 758b611c801SAlexander Leidinger * 759b611c801SAlexander Leidinger * Need access to sound/midi/midi.c::midistat_lock in order 760b611c801SAlexander Leidinger * to safely touch midi_devices and get a head count of, well, 761b611c801SAlexander Leidinger * MIDI devices. midistat_lock is a global static (i.e., local to 762b611c801SAlexander Leidinger * midi.c), but midi_devices is a regular global; should the mutex 763b611c801SAlexander Leidinger * be publicized, or is there another way to get this information? 764b611c801SAlexander Leidinger * 765b611c801SAlexander Leidinger * NB: MIDI/sequencer stuff is currently on hold. 766b611c801SAlexander Leidinger */ 767b611c801SAlexander Leidinger si->nummidis = 0; 768b611c801SAlexander Leidinger si->numtimers = 0; 769b611c801SAlexander Leidinger si->nummixers = mixer_count; 77059d98edaSChristos Margiolis si->numcards = devclass_get_maxunit(pcm_devclass); 771*e07f9178SChristos Margiolis si->numaudios = devclass_get_maxunit(pcm_devclass); 772b611c801SAlexander Leidinger /* OSSv4 docs: Intended only for test apps; API doesn't 773b611c801SAlexander Leidinger really have much of a concept of cards. Shouldn't be 774b611c801SAlexander Leidinger used by applications. */ 775b611c801SAlexander Leidinger 776b611c801SAlexander Leidinger /** 777b611c801SAlexander Leidinger * @todo Fill in "busy devices" fields. 778b611c801SAlexander Leidinger * 779b611c801SAlexander Leidinger * si->openedmidi = " MIDI devices 780b611c801SAlexander Leidinger */ 781b611c801SAlexander Leidinger bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 782b611c801SAlexander Leidinger 783b611c801SAlexander Leidinger /* 784b611c801SAlexander Leidinger * Si->filler is a reserved array, but according to docs each 785b611c801SAlexander Leidinger * element should be set to -1. 786b611c801SAlexander Leidinger */ 787c597c557SChristos Margiolis for (i = 0; i < nitems(si->filler); i++) 788b611c801SAlexander Leidinger si->filler[i] = -1; 789b611c801SAlexander Leidinger } 790b611c801SAlexander Leidinger 79152f6e09eSAlexander Motin int 79252f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si) 79352f6e09eSAlexander Motin { 79452f6e09eSAlexander Motin struct snddev_info *d; 795305db91dSChristos Margiolis int i; 79652f6e09eSAlexander Motin 79752f6e09eSAlexander Motin for (i = 0; pcm_devclass != NULL && 79852f6e09eSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) { 79952f6e09eSAlexander Motin d = devclass_get_softc(pcm_devclass, i); 80052f6e09eSAlexander Motin if (!PCM_REGISTERED(d)) 80152f6e09eSAlexander Motin continue; 80252f6e09eSAlexander Motin 803305db91dSChristos Margiolis if (i != si->card) 80452f6e09eSAlexander Motin continue; 80552f6e09eSAlexander Motin 80690da2b28SAriff Abdullah PCM_UNLOCKASSERT(d); 80790da2b28SAriff Abdullah PCM_LOCK(d); 80852f6e09eSAlexander Motin 80952f6e09eSAlexander Motin strlcpy(si->shortname, device_get_nameunit(d->dev), 81052f6e09eSAlexander Motin sizeof(si->shortname)); 81152f6e09eSAlexander Motin strlcpy(si->longname, device_get_desc(d->dev), 81252f6e09eSAlexander Motin sizeof(si->longname)); 81352f6e09eSAlexander Motin strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 81452f6e09eSAlexander Motin si->intr_count = si->ack_count = 0; 81590da2b28SAriff Abdullah 81690da2b28SAriff Abdullah PCM_UNLOCK(d); 81790da2b28SAriff Abdullah 81852f6e09eSAlexander Motin return (0); 81952f6e09eSAlexander Motin } 82052f6e09eSAlexander Motin return (ENXIO); 82152f6e09eSAlexander Motin } 82252f6e09eSAlexander Motin 823b611c801SAlexander Leidinger /************************************************************************/ 824b611c801SAlexander Leidinger 8250739ea1dSSeigo Tanimura static int 8260739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data) 8270739ea1dSSeigo Tanimura { 828b611c801SAlexander Leidinger int ret; 829b611c801SAlexander Leidinger 8308109ec9dSJohn Baldwin ret = 0; 831b611c801SAlexander Leidinger switch (type) { 832b611c801SAlexander Leidinger case MOD_LOAD: 83313bebcd3SJohn Baldwin pcm_devclass = devclass_create("pcm"); 834b611c801SAlexander Leidinger pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 835b611c801SAlexander Leidinger break; 836b611c801SAlexander Leidinger case MOD_UNLOAD: 837b611c801SAlexander Leidinger if (pcmsg_unrhdr != NULL) { 838b611c801SAlexander Leidinger delete_unrhdr(pcmsg_unrhdr); 839b611c801SAlexander Leidinger pcmsg_unrhdr = NULL; 840b611c801SAlexander Leidinger } 841b611c801SAlexander Leidinger break; 8425525b53dSAlexander Motin case MOD_SHUTDOWN: 8435525b53dSAlexander Motin break; 844b611c801SAlexander Leidinger default: 84590da2b28SAriff Abdullah ret = ENOTSUP; 846b611c801SAlexander Leidinger } 847b611c801SAlexander Leidinger 848b611c801SAlexander Leidinger return ret; 8490739ea1dSSeigo Tanimura } 8500739ea1dSSeigo Tanimura 8510739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL); 8520739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER); 853