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
51f3685841SAriff Abdullah int snd_unit = -1;
52cbe7d6a3SCameron Grant
53cbebc90dSAlexander Motin static int snd_unit_auto = -1;
54af3b2549SHans Petter Selasky SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN,
55838d3589SAriff Abdullah &snd_unit_auto, 0, "assign default unit to a newly attached device");
56ad8612b9SAriff Abdullah
577029da5cSPawel Biernacki SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
587029da5cSPawel Biernacki "Sound driver");
5982db23e2SCameron Grant
6032a0e5d5SHans Petter Selasky static void pcm_sysinit(device_t);
6132a0e5d5SHans Petter Selasky
62b611c801SAlexander Leidinger /**
63b611c801SAlexander Leidinger * @brief Unit number allocator for syncgroup IDs
64b611c801SAlexander Leidinger */
65b611c801SAlexander Leidinger struct unrhdr *pcmsg_unrhdr = NULL;
66b611c801SAlexander Leidinger
6737209180SCameron Grant void *
snd_mtxcreate(const char * desc,const char * type)682c69ba87SJohn Baldwin snd_mtxcreate(const char *desc, const char *type)
6937209180SCameron Grant {
7037209180SCameron Grant struct mtx *m;
7137209180SCameron Grant
72a163d034SWarner Losh m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO);
7312e524a2SDon Lewis mtx_init(m, desc, type, MTX_DEF);
7412e524a2SDon Lewis return m;
7512e524a2SDon Lewis }
7612e524a2SDon Lewis
7737209180SCameron Grant void
snd_mtxfree(void * m)7837209180SCameron Grant snd_mtxfree(void *m)
7937209180SCameron Grant {
8037209180SCameron Grant struct mtx *mtx = m;
8137209180SCameron Grant
8237209180SCameron Grant mtx_destroy(mtx);
8337209180SCameron Grant free(mtx, M_DEVBUF);
8437209180SCameron Grant }
8537209180SCameron Grant
8637209180SCameron Grant void
snd_mtxassert(void * m)8737209180SCameron Grant snd_mtxassert(void *m)
8837209180SCameron Grant {
89f00f162aSCameron Grant #ifdef INVARIANTS
9037209180SCameron Grant struct mtx *mtx = m;
9137209180SCameron Grant
9237209180SCameron Grant mtx_assert(mtx, MA_OWNED);
9337209180SCameron Grant #endif
9437209180SCameron Grant }
9537209180SCameron Grant
9637209180SCameron Grant int
snd_setup_intr(device_t dev,struct resource * res,int flags,driver_intr_t hand,void * param,void ** cookiep)9737209180SCameron Grant snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep)
9837209180SCameron Grant {
99e4e61333SAriff Abdullah struct snddev_info *d;
10090da2b28SAriff Abdullah
10137209180SCameron Grant flags &= INTR_MPSAFE;
10246700f12SPeter Wemm flags |= INTR_TYPE_AV;
103e4e61333SAriff Abdullah d = device_get_softc(dev);
104e4e61333SAriff Abdullah if (d != NULL && (flags & INTR_MPSAFE))
105e4e61333SAriff Abdullah d->flags |= SD_F_MPSAFE;
106e4e61333SAriff Abdullah
107775fcb6eSBaptiste Daroussin return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep);
10837209180SCameron Grant }
10937209180SCameron Grant
1103fdb3676SAriff Abdullah int
pcm_chnalloc(struct snddev_info * d,struct pcm_channel ** ch,int direction,pid_t pid,char * comm)1113fdb3676SAriff Abdullah pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction,
11225723d66SChristos Margiolis pid_t pid, char *comm)
113285648f9SCameron Grant {
114285648f9SCameron Grant struct pcm_channel *c;
115b973a14dSChristos Margiolis int err, vchancount;
11625723d66SChristos Margiolis bool retry;
117bba4862cSAriff Abdullah
11825723d66SChristos Margiolis KASSERT(d != NULL && ch != NULL &&
119bba4862cSAriff Abdullah (direction == PCMDIR_PLAY || direction == PCMDIR_REC),
12025723d66SChristos Margiolis ("%s(): invalid d=%p ch=%p direction=%d pid=%d",
12125723d66SChristos Margiolis __func__, d, ch, direction, pid));
122e4e61333SAriff Abdullah PCM_BUSYASSERT(d);
123bba4862cSAriff Abdullah
12490da2b28SAriff Abdullah *ch = NULL;
12590da2b28SAriff Abdullah vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount :
12690da2b28SAriff Abdullah d->rvchancount;
12725723d66SChristos Margiolis retry = false;
128b973a14dSChristos Margiolis
1293fdb3676SAriff Abdullah retry_chnalloc:
130b973a14dSChristos Margiolis /* Scan for a free channel. */
131bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) {
13249c5e6e2SCameron Grant CHN_LOCK(c);
133b973a14dSChristos Margiolis if (c->direction != direction) {
13490da2b28SAriff Abdullah CHN_UNLOCK(c);
135b973a14dSChristos Margiolis continue;
13690da2b28SAriff Abdullah }
137b973a14dSChristos Margiolis if (!(c->flags & CHN_F_BUSY)) {
138285648f9SCameron Grant c->flags |= CHN_F_BUSY;
139b8f0d9e0SCameron Grant c->pid = pid;
14090da2b28SAriff Abdullah strlcpy(c->comm, (comm != NULL) ? comm :
14190da2b28SAriff Abdullah CHN_COMM_UNKNOWN, sizeof(c->comm));
1423fdb3676SAriff Abdullah *ch = c;
143b973a14dSChristos Margiolis
144bba4862cSAriff Abdullah return (0);
145b973a14dSChristos Margiolis }
14649c5e6e2SCameron Grant CHN_UNLOCK(c);
147285648f9SCameron Grant }
148b973a14dSChristos Margiolis /* Maybe next time... */
14925723d66SChristos Margiolis if (retry)
150b973a14dSChristos Margiolis return (EBUSY);
151e4e61333SAriff Abdullah
152b973a14dSChristos Margiolis /* No channel available. We also cannot create more VCHANs. */
15325723d66SChristos Margiolis if (!(vchancount > 0 && vchancount < snd_maxautovchans))
154b973a14dSChristos Margiolis return (ENOTSUP);
155b973a14dSChristos Margiolis
156b973a14dSChristos Margiolis /* Increase the VCHAN count and try to get the new channel. */
1573af2beb8SChristos Margiolis err = vchan_setnew(d, direction, vchancount + 1);
158a1d444e1SAriff Abdullah if (err == 0) {
15925723d66SChristos Margiolis retry = true;
1603fdb3676SAriff Abdullah goto retry_chnalloc;
161f637a36cSCameron Grant }
162f637a36cSCameron Grant
163bba4862cSAriff Abdullah return (err);
164285648f9SCameron Grant }
165285648f9SCameron Grant
16633dbf14aSCameron Grant static int
sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)167851a904aSAlexander Leidinger sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS)
16833dbf14aSCameron Grant {
169b8f0d9e0SCameron Grant struct snddev_info *d;
17033dbf14aSCameron Grant int error, unit;
17133dbf14aSCameron Grant
17233dbf14aSCameron Grant unit = snd_unit;
173041b706bSDavid Malone error = sysctl_handle_int(oidp, &unit, 0, req);
17433dbf14aSCameron Grant if (error == 0 && req->newptr != NULL) {
175b8f0d9e0SCameron Grant d = devclass_get_softc(pcm_devclass, unit);
176e4e61333SAriff Abdullah if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm))
177b8f0d9e0SCameron Grant return EINVAL;
17833dbf14aSCameron Grant snd_unit = unit;
179cbebc90dSAlexander Motin snd_unit_auto = 0;
18033dbf14aSCameron Grant }
18133dbf14aSCameron Grant return (error);
18233dbf14aSCameron Grant }
183851a904aSAlexander Leidinger /* XXX: do we need a way to let the user change the default unit? */
184b7d6c6b5SEitan Adler SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit,
1857029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0,
1867029da5cSPawel Biernacki sizeof(int), sysctl_hw_snd_default_unit, "I",
187b7d6c6b5SEitan Adler "default sound device");
188987e5972SCameron Grant
189987e5972SCameron Grant int
pcm_addchan(device_t dev,int dir,kobj_class_t cls,void * devinfo)190285648f9SCameron Grant pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo)
191285648f9SCameron Grant {
192285648f9SCameron Grant struct snddev_info *d = device_get_softc(dev);
19367b1dce3SCameron Grant struct pcm_channel *ch;
194285648f9SCameron Grant
195e4e61333SAriff Abdullah PCM_BUSYASSERT(d);
196e4e61333SAriff Abdullah
19790da2b28SAriff Abdullah PCM_LOCK(d);
1983af2beb8SChristos Margiolis ch = chn_init(d, NULL, cls, dir, devinfo);
199285648f9SCameron Grant if (!ch) {
2002e9962efSChristos Margiolis device_printf(d->dev, "chn_init(%s, %d, %p) failed\n",
201e4e61333SAriff Abdullah cls->name, dir, devinfo);
20290da2b28SAriff Abdullah PCM_UNLOCK(d);
203e4e61333SAriff Abdullah return (ENODEV);
204285648f9SCameron Grant }
20590da2b28SAriff Abdullah PCM_UNLOCK(d);
206cd9766c5SCameron Grant
207139bcec8SChristos Margiolis return (0);
208285648f9SCameron Grant }
209285648f9SCameron Grant
21003614fcbSChristos Margiolis static void
pcm_killchans(struct snddev_info * d)21103614fcbSChristos Margiolis pcm_killchans(struct snddev_info *d)
212285648f9SCameron Grant {
213e33bee07SOlivier Houchard struct pcm_channel *ch;
21403614fcbSChristos Margiolis bool found;
215e4e61333SAriff Abdullah
216e4e61333SAriff Abdullah PCM_BUSYASSERT(d);
21703614fcbSChristos Margiolis do {
21803614fcbSChristos Margiolis found = false;
21903614fcbSChristos Margiolis CHN_FOREACH(ch, d, channels.pcm) {
22003614fcbSChristos Margiolis CHN_LOCK(ch);
22103614fcbSChristos Margiolis /*
22203614fcbSChristos Margiolis * Make sure no channel has went to sleep in the
22303614fcbSChristos Margiolis * meantime.
22403614fcbSChristos Margiolis */
22503614fcbSChristos Margiolis chn_shutdown(ch);
22603614fcbSChristos Margiolis /*
22703614fcbSChristos Margiolis * We have to give a thread sleeping in chn_sleep() a
22803614fcbSChristos Margiolis * chance to observe that the channel is dead.
22903614fcbSChristos Margiolis */
23003614fcbSChristos Margiolis if ((ch->flags & CHN_F_SLEEPING) == 0) {
23103614fcbSChristos Margiolis found = true;
23203614fcbSChristos Margiolis CHN_UNLOCK(ch);
23303614fcbSChristos Margiolis break;
23403614fcbSChristos Margiolis }
23503614fcbSChristos Margiolis CHN_UNLOCK(ch);
23603614fcbSChristos Margiolis }
237285648f9SCameron Grant
23803614fcbSChristos Margiolis /*
23903614fcbSChristos Margiolis * All channels are still sleeping. Sleep for a bit and try
24003614fcbSChristos Margiolis * again to see if any of them is awake now.
24103614fcbSChristos Margiolis */
24203614fcbSChristos Margiolis if (!found) {
24303614fcbSChristos Margiolis pause_sbt("pcmkillchans", SBT_1MS * 5, 0, 0);
24403614fcbSChristos Margiolis continue;
24503614fcbSChristos Margiolis }
246b3ea087cSChristos Margiolis chn_kill(ch);
24703614fcbSChristos Margiolis } while (!CHN_EMPTY(d, channels.pcm));
248285648f9SCameron Grant }
249285648f9SCameron Grant
250cbebc90dSAlexander Motin static int
pcm_best_unit(int old)251cbebc90dSAlexander Motin pcm_best_unit(int old)
252cbebc90dSAlexander Motin {
253cbebc90dSAlexander Motin struct snddev_info *d;
254cbebc90dSAlexander Motin int i, best, bestprio, prio;
255cbebc90dSAlexander Motin
256cbebc90dSAlexander Motin best = -1;
257cbebc90dSAlexander Motin bestprio = -100;
258cbebc90dSAlexander Motin for (i = 0; pcm_devclass != NULL &&
259cbebc90dSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) {
260cbebc90dSAlexander Motin d = devclass_get_softc(pcm_devclass, i);
261cbebc90dSAlexander Motin if (!PCM_REGISTERED(d))
262cbebc90dSAlexander Motin continue;
263cbebc90dSAlexander Motin prio = 0;
264cbebc90dSAlexander Motin if (d->playcount == 0)
265cbebc90dSAlexander Motin prio -= 10;
266cbebc90dSAlexander Motin if (d->reccount == 0)
267cbebc90dSAlexander Motin prio -= 2;
268cbebc90dSAlexander Motin if (prio > bestprio || (prio == bestprio && i == old)) {
269cbebc90dSAlexander Motin best = i;
270cbebc90dSAlexander Motin bestprio = prio;
271cbebc90dSAlexander Motin }
272cbebc90dSAlexander Motin }
273cbebc90dSAlexander Motin return (best);
274cbebc90dSAlexander Motin }
275cbebc90dSAlexander Motin
276a1d444e1SAriff Abdullah uint32_t
pcm_getflags(device_t dev)277987e5972SCameron Grant pcm_getflags(device_t dev)
278987e5972SCameron Grant {
27966ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev);
28049c5e6e2SCameron Grant
281987e5972SCameron Grant return d->flags;
282987e5972SCameron Grant }
283987e5972SCameron Grant
284987e5972SCameron Grant void
pcm_setflags(device_t dev,uint32_t val)285a1d444e1SAriff Abdullah pcm_setflags(device_t dev, uint32_t val)
286987e5972SCameron Grant {
28766ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev);
288d95502a8SCameron Grant
289987e5972SCameron Grant d->flags = val;
290987e5972SCameron Grant }
291987e5972SCameron Grant
29239004e69SCameron Grant void *
pcm_getdevinfo(device_t dev)29339004e69SCameron Grant pcm_getdevinfo(device_t dev)
29439004e69SCameron Grant {
29566ef8af5SCameron Grant struct snddev_info *d = device_get_softc(dev);
29649c5e6e2SCameron Grant
29739004e69SCameron Grant return d->devinfo;
29839004e69SCameron Grant }
29939004e69SCameron Grant
300a67fe5c1SCameron Grant unsigned int
pcm_getbuffersize(device_t dev,unsigned int minbufsz,unsigned int deflt,unsigned int maxbufsz)301d55d96f6SAlexander Leidinger pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz)
302a67fe5c1SCameron Grant {
303a67fe5c1SCameron Grant struct snddev_info *d = device_get_softc(dev);
3044e60be34SCameron Grant int sz, x;
305a67fe5c1SCameron Grant
306a67fe5c1SCameron Grant sz = 0;
3074e60be34SCameron Grant if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) {
3084e60be34SCameron Grant x = sz;
309d55d96f6SAlexander Leidinger RANGE(sz, minbufsz, maxbufsz);
3104e60be34SCameron Grant if (x != sz)
311d55d96f6SAlexander Leidinger device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz);
312d55d96f6SAlexander Leidinger x = minbufsz;
3134e60be34SCameron Grant while (x < sz)
3144e60be34SCameron Grant x <<= 1;
3154e60be34SCameron Grant if (x > sz)
3164e60be34SCameron Grant x >>= 1;
3174e60be34SCameron Grant if (x != sz) {
3184e60be34SCameron Grant device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x);
3194e60be34SCameron Grant sz = x;
3204e60be34SCameron Grant }
3214e60be34SCameron Grant } else {
322a67fe5c1SCameron Grant sz = deflt;
3234e60be34SCameron Grant }
3244e60be34SCameron Grant
325a67fe5c1SCameron Grant d->bufsz = sz;
326a67fe5c1SCameron Grant
327a67fe5c1SCameron Grant return sz;
328a67fe5c1SCameron Grant }
329a67fe5c1SCameron Grant
33090da2b28SAriff Abdullah static int
sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)33190da2b28SAriff Abdullah sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS)
33290da2b28SAriff Abdullah {
33390da2b28SAriff Abdullah struct snddev_info *d;
33490da2b28SAriff Abdullah int err, val;
33590da2b28SAriff Abdullah
33690da2b28SAriff Abdullah d = oidp->oid_arg1;
33790da2b28SAriff Abdullah if (!PCM_REGISTERED(d))
33890da2b28SAriff Abdullah return (ENODEV);
33990da2b28SAriff Abdullah
34090da2b28SAriff Abdullah PCM_LOCK(d);
34190da2b28SAriff Abdullah PCM_WAIT(d);
34290da2b28SAriff Abdullah val = (d->flags & SD_F_BITPERFECT) ? 1 : 0;
34390da2b28SAriff Abdullah PCM_ACQUIRE(d);
34490da2b28SAriff Abdullah PCM_UNLOCK(d);
34590da2b28SAriff Abdullah
34690da2b28SAriff Abdullah err = sysctl_handle_int(oidp, &val, 0, req);
34790da2b28SAriff Abdullah
34890da2b28SAriff Abdullah if (err == 0 && req->newptr != NULL) {
34990da2b28SAriff Abdullah if (!(val == 0 || val == 1)) {
35090da2b28SAriff Abdullah PCM_RELEASE_QUICK(d);
35190da2b28SAriff Abdullah return (EINVAL);
35290da2b28SAriff Abdullah }
35390da2b28SAriff Abdullah
35490da2b28SAriff Abdullah PCM_LOCK(d);
35590da2b28SAriff Abdullah
35690da2b28SAriff Abdullah d->flags &= ~SD_F_BITPERFECT;
35790da2b28SAriff Abdullah d->flags |= (val != 0) ? SD_F_BITPERFECT : 0;
35890da2b28SAriff Abdullah
35990da2b28SAriff Abdullah PCM_RELEASE(d);
36090da2b28SAriff Abdullah PCM_UNLOCK(d);
36190da2b28SAriff Abdullah } else
36290da2b28SAriff Abdullah PCM_RELEASE_QUICK(d);
36390da2b28SAriff Abdullah
36490da2b28SAriff Abdullah return (err);
36590da2b28SAriff Abdullah }
36690da2b28SAriff Abdullah
367ed2196e5SHans Petter Selasky static u_int8_t
pcm_mode_init(struct snddev_info * d)368ed2196e5SHans Petter Selasky pcm_mode_init(struct snddev_info *d)
369ed2196e5SHans Petter Selasky {
370ed2196e5SHans Petter Selasky u_int8_t mode = 0;
371ed2196e5SHans Petter Selasky
372ed2196e5SHans Petter Selasky if (d->playcount > 0)
373ed2196e5SHans Petter Selasky mode |= PCM_MODE_PLAY;
374ed2196e5SHans Petter Selasky if (d->reccount > 0)
375ed2196e5SHans Petter Selasky mode |= PCM_MODE_REC;
376ed2196e5SHans Petter Selasky if (d->mixer_dev != NULL)
377ed2196e5SHans Petter Selasky mode |= PCM_MODE_MIXER;
378ed2196e5SHans Petter Selasky
379ed2196e5SHans Petter Selasky return (mode);
380ed2196e5SHans Petter Selasky }
381ed2196e5SHans Petter Selasky
38232a0e5d5SHans Petter Selasky static void
pcm_sysinit(device_t dev)38332a0e5d5SHans Petter Selasky pcm_sysinit(device_t dev)
38432a0e5d5SHans Petter Selasky {
38532a0e5d5SHans Petter Selasky struct snddev_info *d = device_get_softc(dev);
386ed2196e5SHans Petter Selasky u_int8_t mode;
387ed2196e5SHans Petter Selasky
388ed2196e5SHans Petter Selasky mode = pcm_mode_init(d);
38932a0e5d5SHans Petter Selasky
39066f3eb14SChristos Margiolis sysctl_ctx_init(&d->play_sysctl_ctx);
39166f3eb14SChristos Margiolis d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx,
39266f3eb14SChristos Margiolis SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play",
39366f3eb14SChristos Margiolis CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node");
39466f3eb14SChristos Margiolis sysctl_ctx_init(&d->rec_sysctl_ctx);
39566f3eb14SChristos Margiolis d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx,
39666f3eb14SChristos Margiolis SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec",
39766f3eb14SChristos Margiolis CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node");
39866f3eb14SChristos Margiolis
399132fca63SHans Petter Selasky /* XXX: a user should be able to set this with a control tool, the
40032a0e5d5SHans Petter Selasky sysadmin then needs min+max sysctls for this */
40132a0e5d5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
40232a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
40332a0e5d5SHans Petter Selasky OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size");
40432a0e5d5SHans Petter Selasky SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
40532a0e5d5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
4063b4c5433SAlexander Motin "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d,
4077029da5cSPawel Biernacki sizeof(d), sysctl_dev_pcm_bitperfect, "I",
40832a0e5d5SHans Petter Selasky "bit-perfect playback/recording (0=disable, 1=enable)");
409ed2196e5SHans Petter Selasky SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
410ed2196e5SHans Petter Selasky SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
411ed2196e5SHans Petter Selasky OID_AUTO, "mode", CTLFLAG_RD, NULL, mode,
412e56c8996SChristos Margiolis "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than "
413e56c8996SChristos Margiolis "one mode is supported)");
41432a0e5d5SHans Petter Selasky if (d->flags & SD_F_AUTOVCHAN)
41532a0e5d5SHans Petter Selasky vchan_initsys(dev);
41632a0e5d5SHans Petter Selasky if (d->flags & SD_F_EQ)
41732a0e5d5SHans Petter Selasky feeder_eq_initsys(dev);
41832a0e5d5SHans Petter Selasky }
41932a0e5d5SHans Petter Selasky
420*516a9c02SChristos Margiolis /*
421*516a9c02SChristos Margiolis * Basic initialization so that drivers can use pcm_addchan() before
422*516a9c02SChristos Margiolis * pcm_register().
423*516a9c02SChristos Margiolis */
424*516a9c02SChristos Margiolis void
pcm_init(device_t dev,void * devinfo)425*516a9c02SChristos Margiolis pcm_init(device_t dev, void *devinfo)
426987e5972SCameron Grant {
427bba4862cSAriff Abdullah struct snddev_info *d;
42890da2b28SAriff Abdullah int i;
429987e5972SCameron Grant
430bba4862cSAriff Abdullah d = device_get_softc(dev);
431e4e61333SAriff Abdullah d->dev = dev;
4322c69ba87SJohn Baldwin d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev");
433e4e61333SAriff Abdullah cv_init(&d->cv, device_get_nameunit(dev));
434e4e61333SAriff Abdullah PCM_ACQUIRE_QUICK(d);
435a9f08df3SChristos Margiolis
43690da2b28SAriff Abdullah i = 0;
43790da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev),
43890da2b28SAriff Abdullah "vpc", &i) != 0 || i != 0)
43990da2b28SAriff Abdullah d->flags |= SD_F_VPC;
44090da2b28SAriff Abdullah
44190da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev),
44290da2b28SAriff Abdullah "bitperfect", &i) == 0 && i != 0)
44390da2b28SAriff Abdullah d->flags |= SD_F_BITPERFECT;
44490da2b28SAriff Abdullah
445987e5972SCameron Grant d->devinfo = devinfo;
446506a5308SCameron Grant d->reccount = 0;
447a67fe5c1SCameron Grant d->playcount = 0;
448bba4862cSAriff Abdullah d->pvchancount = 0;
449bba4862cSAriff Abdullah d->rvchancount = 0;
450bba4862cSAriff Abdullah d->pvchanrate = 0;
451bba4862cSAriff Abdullah d->pvchanformat = 0;
452bba4862cSAriff Abdullah d->rvchanrate = 0;
453bba4862cSAriff Abdullah d->rvchanformat = 0;
454ad4c8671SChristos Margiolis d->p_unr = new_unrhdr(0, INT_MAX, NULL);
455ad4c8671SChristos Margiolis d->vp_unr = new_unrhdr(0, INT_MAX, NULL);
456ad4c8671SChristos Margiolis d->r_unr = new_unrhdr(0, INT_MAX, NULL);
457ad4c8671SChristos Margiolis d->vr_unr = new_unrhdr(0, INT_MAX, NULL);
458833f7023SCameron Grant
459bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm);
460bba4862cSAriff Abdullah CHN_INIT(d, channels.pcm.busy);
46190da2b28SAriff Abdullah CHN_INIT(d, channels.pcm.opened);
462*516a9c02SChristos Margiolis }
46345550658SPoul-Henning Kamp
464*516a9c02SChristos Margiolis int
pcm_register(device_t dev,char * str)465*516a9c02SChristos Margiolis pcm_register(device_t dev, char *str)
466*516a9c02SChristos Margiolis {
467*516a9c02SChristos Margiolis struct snddev_info *d = device_get_softc(dev);
468*516a9c02SChristos Margiolis
469*516a9c02SChristos Margiolis /* should only be called once */
470*516a9c02SChristos Margiolis if (d->flags & SD_F_REGISTERED)
471*516a9c02SChristos Margiolis return (EINVAL);
472*516a9c02SChristos Margiolis
473*516a9c02SChristos Margiolis PCM_BUSYASSERT(d);
474*516a9c02SChristos Margiolis
475*516a9c02SChristos Margiolis if (d->playcount == 0 || d->reccount == 0)
476*516a9c02SChristos Margiolis d->flags |= SD_F_SIMPLEX;
477*516a9c02SChristos Margiolis
478*516a9c02SChristos Margiolis if (d->playcount > 0 || d->reccount > 0)
479*516a9c02SChristos Margiolis d->flags |= SD_F_AUTOVCHAN;
480*516a9c02SChristos Margiolis
481*516a9c02SChristos Margiolis vchan_setmaxauto(d, snd_maxautovchans);
482*516a9c02SChristos Margiolis
483*516a9c02SChristos Margiolis strlcpy(d->status, str, SND_STATUSLEN);
484*516a9c02SChristos Margiolis sndstat_register(dev, d->status);
485*516a9c02SChristos Margiolis
486*516a9c02SChristos Margiolis PCM_LOCK(d);
487*516a9c02SChristos Margiolis
488*516a9c02SChristos Margiolis /* Done, we're ready.. */
489*516a9c02SChristos Margiolis d->flags |= SD_F_REGISTERED;
490*516a9c02SChristos Margiolis
491*516a9c02SChristos Margiolis PCM_RELEASE(d);
492*516a9c02SChristos Margiolis
493*516a9c02SChristos Margiolis PCM_UNLOCK(d);
494*516a9c02SChristos Margiolis
495*516a9c02SChristos Margiolis /*
496*516a9c02SChristos Margiolis * Create all sysctls once SD_F_REGISTERED is set else
497*516a9c02SChristos Margiolis * tunable sysctls won't work:
498*516a9c02SChristos Margiolis */
499*516a9c02SChristos Margiolis pcm_sysinit(dev);
500*516a9c02SChristos Margiolis
501*516a9c02SChristos Margiolis if (snd_unit_auto < 0)
502*516a9c02SChristos Margiolis snd_unit_auto = (snd_unit < 0) ? 1 : 0;
503*516a9c02SChristos Margiolis if (snd_unit < 0 || snd_unit_auto > 1)
504*516a9c02SChristos Margiolis snd_unit = device_get_unit(dev);
505*516a9c02SChristos Margiolis else if (snd_unit_auto == 1)
506*516a9c02SChristos Margiolis snd_unit = pcm_best_unit(snd_unit);
507*516a9c02SChristos Margiolis
508*516a9c02SChristos Margiolis return (dsp_make_dev(dev));
509987e5972SCameron Grant }
510987e5972SCameron Grant
51133dbf14aSCameron Grant int
pcm_unregister(device_t dev)51233dbf14aSCameron Grant pcm_unregister(device_t dev)
5137c438dbeSCameron Grant {
514e4e61333SAriff Abdullah struct snddev_info *d;
515a67fe5c1SCameron Grant struct pcm_channel *ch;
5167c438dbeSCameron Grant
517e4e61333SAriff Abdullah d = device_get_softc(dev);
518e4e61333SAriff Abdullah
519e4e61333SAriff Abdullah if (!PCM_ALIVE(d)) {
520e4e61333SAriff Abdullah device_printf(dev, "unregister: device not configured\n");
521e4e61333SAriff Abdullah return (0);
522e4e61333SAriff Abdullah }
523bba4862cSAriff Abdullah
52490da2b28SAriff Abdullah PCM_LOCK(d);
525e4e61333SAriff Abdullah PCM_WAIT(d);
526e4e61333SAriff Abdullah
527cc1efc23SHans Petter Selasky d->flags |= SD_F_DETACHING;
528cc1efc23SHans Petter Selasky
529e4e61333SAriff Abdullah PCM_ACQUIRE(d);
53090da2b28SAriff Abdullah PCM_UNLOCK(d);
531e4e61333SAriff Abdullah
532e4e61333SAriff Abdullah CHN_FOREACH(ch, d, channels.pcm) {
533e4e61333SAriff Abdullah CHN_LOCK(ch);
53444e128feSChristos Margiolis /*
53503614fcbSChristos Margiolis * Do not wait for the timeout in chn_read()/chn_write(). Wake
53603614fcbSChristos Margiolis * up the sleeping thread and kill the channel.
53744e128feSChristos Margiolis */
53803614fcbSChristos Margiolis chn_shutdown(ch);
53944e128feSChristos Margiolis chn_abort(ch);
540e4e61333SAriff Abdullah CHN_UNLOCK(ch);
541c9b53085SCameron Grant }
5425ee30e27SMathew Kanner
5438461d581SHans Petter Selasky /* remove /dev/sndstat entry first */
5448461d581SHans Petter Selasky sndstat_unregister(dev);
5458461d581SHans Petter Selasky
54690da2b28SAriff Abdullah PCM_LOCK(d);
547e4e61333SAriff Abdullah d->flags |= SD_F_DYING;
548e4e61333SAriff Abdullah d->flags &= ~SD_F_REGISTERED;
54990da2b28SAriff Abdullah PCM_UNLOCK(d);
550e4e61333SAriff Abdullah
551bba4862cSAriff Abdullah if (d->play_sysctl_tree != NULL) {
552bba4862cSAriff Abdullah sysctl_ctx_free(&d->play_sysctl_ctx);
553bba4862cSAriff Abdullah d->play_sysctl_tree = NULL;
554bba4862cSAriff Abdullah }
555bba4862cSAriff Abdullah if (d->rec_sysctl_tree != NULL) {
556bba4862cSAriff Abdullah sysctl_ctx_free(&d->rec_sysctl_ctx);
557bba4862cSAriff Abdullah d->rec_sysctl_tree = NULL;
558a1d444e1SAriff Abdullah }
559bba4862cSAriff Abdullah
560074d6fbeSChristos Margiolis dsp_destroy_dev(dev);
561074d6fbeSChristos Margiolis (void)mixer_uninit(dev);
562074d6fbeSChristos Margiolis
56303614fcbSChristos Margiolis pcm_killchans(d);
5647c438dbeSCameron Grant
56590da2b28SAriff Abdullah PCM_LOCK(d);
566e4e61333SAriff Abdullah PCM_RELEASE(d);
567e4e61333SAriff Abdullah cv_destroy(&d->cv);
56890da2b28SAriff Abdullah PCM_UNLOCK(d);
56949c5e6e2SCameron Grant snd_mtxfree(d->lock);
570ad4c8671SChristos Margiolis if (d->p_unr != NULL)
571ad4c8671SChristos Margiolis delete_unrhdr(d->p_unr);
572ad4c8671SChristos Margiolis if (d->vp_unr != NULL)
573ad4c8671SChristos Margiolis delete_unrhdr(d->vp_unr);
574ad4c8671SChristos Margiolis if (d->r_unr != NULL)
575ad4c8671SChristos Margiolis delete_unrhdr(d->r_unr);
576ad4c8671SChristos Margiolis if (d->vr_unr != NULL)
577ad4c8671SChristos Margiolis delete_unrhdr(d->vr_unr);
578bba4862cSAriff Abdullah
579bba4862cSAriff Abdullah if (snd_unit == device_get_unit(dev)) {
580cbebc90dSAlexander Motin snd_unit = pcm_best_unit(-1);
581cbebc90dSAlexander Motin if (snd_unit_auto == 0)
582cbebc90dSAlexander Motin snd_unit_auto = 1;
583e4e61333SAriff Abdullah }
584bba4862cSAriff Abdullah
585e4e61333SAriff Abdullah return (0);
58633dbf14aSCameron Grant }
5877c438dbeSCameron Grant
58867b1dce3SCameron Grant /************************************************************************/
58967b1dce3SCameron Grant
590b611c801SAlexander Leidinger /**
591b611c801SAlexander Leidinger * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl.
592b611c801SAlexander Leidinger *
593b611c801SAlexander Leidinger * @param si Pointer to oss_sysinfo struct where information about the
594b611c801SAlexander Leidinger * sound subsystem will be written/copied.
595b611c801SAlexander Leidinger *
596b611c801SAlexander Leidinger * This routine returns information about the sound system, such as the
597b611c801SAlexander Leidinger * current OSS version, number of audio, MIDI, and mixer drivers, etc.
598b611c801SAlexander Leidinger * Also includes a bitmask showing which of the above types of devices
599b611c801SAlexander Leidinger * are open (busy).
600b611c801SAlexander Leidinger *
601b611c801SAlexander Leidinger * @note
602b611c801SAlexander Leidinger * Calling threads must not hold any snddev_info or pcm_channel locks.
603b611c801SAlexander Leidinger *
604b611c801SAlexander Leidinger * @author Ryan Beasley <ryanb@FreeBSD.org>
605b611c801SAlexander Leidinger */
606b611c801SAlexander Leidinger void
sound_oss_sysinfo(oss_sysinfo * si)607b611c801SAlexander Leidinger sound_oss_sysinfo(oss_sysinfo *si)
608b611c801SAlexander Leidinger {
609b611c801SAlexander Leidinger static char si_product[] = "FreeBSD native OSS ABI";
610b611c801SAlexander Leidinger static char si_version[] = __XSTRING(__FreeBSD_version);
611c412d503SAlexander Motin static char si_license[] = "BSD";
612b611c801SAlexander Leidinger static int intnbits = sizeof(int) * 8; /* Better suited as macro?
613b611c801SAlexander Leidinger Must pester a C guru. */
614b611c801SAlexander Leidinger
615b611c801SAlexander Leidinger struct snddev_info *d;
616b611c801SAlexander Leidinger struct pcm_channel *c;
61759d98edaSChristos Margiolis int j;
618c597c557SChristos Margiolis size_t i;
619b611c801SAlexander Leidinger
620b611c801SAlexander Leidinger strlcpy(si->product, si_product, sizeof(si->product));
621b611c801SAlexander Leidinger strlcpy(si->version, si_version, sizeof(si->version));
622b611c801SAlexander Leidinger si->versionnum = SOUND_VERSION;
623c412d503SAlexander Motin strlcpy(si->license, si_license, sizeof(si->license));
624b611c801SAlexander Leidinger
625b611c801SAlexander Leidinger /*
626b611c801SAlexander Leidinger * Iterate over PCM devices and their channels, gathering up data
627e07f9178SChristos Margiolis * for the numaudioengines and openedaudio fields.
628b611c801SAlexander Leidinger */
629e07f9178SChristos Margiolis si->numaudioengines = 0;
630b611c801SAlexander Leidinger bzero((void *)&si->openedaudio, sizeof(si->openedaudio));
631b611c801SAlexander Leidinger
632b611c801SAlexander Leidinger j = 0;
633b611c801SAlexander Leidinger
6349c271f79SAriff Abdullah for (i = 0; pcm_devclass != NULL &&
6359c271f79SAriff Abdullah i < devclass_get_maxunit(pcm_devclass); i++) {
636b611c801SAlexander Leidinger d = devclass_get_softc(pcm_devclass, i);
637e4e61333SAriff Abdullah if (!PCM_REGISTERED(d))
638b611c801SAlexander Leidinger continue;
639b611c801SAlexander Leidinger
640e4e61333SAriff Abdullah /* XXX Need Giant magic entry ??? */
641e4e61333SAriff Abdullah
642b611c801SAlexander Leidinger /* See note in function's docblock */
64390da2b28SAriff Abdullah PCM_UNLOCKASSERT(d);
64490da2b28SAriff Abdullah PCM_LOCK(d);
645b611c801SAlexander Leidinger
646e07f9178SChristos Margiolis si->numaudioengines += PCM_CHANCOUNT(d);
647b611c801SAlexander Leidinger
648bba4862cSAriff Abdullah CHN_FOREACH(c, d, channels.pcm) {
64990da2b28SAriff Abdullah CHN_UNLOCKASSERT(c);
650b611c801SAlexander Leidinger CHN_LOCK(c);
651b611c801SAlexander Leidinger if (c->flags & CHN_F_BUSY)
652b611c801SAlexander Leidinger si->openedaudio[j / intnbits] |=
653b611c801SAlexander Leidinger (1 << (j % intnbits));
654b611c801SAlexander Leidinger CHN_UNLOCK(c);
655b611c801SAlexander Leidinger j++;
656b611c801SAlexander Leidinger }
657b611c801SAlexander Leidinger
65890da2b28SAriff Abdullah PCM_UNLOCK(d);
659b611c801SAlexander Leidinger }
660b611c801SAlexander Leidinger
661b611c801SAlexander Leidinger si->numsynths = 0; /* OSSv4 docs: this field is obsolete */
662b611c801SAlexander Leidinger /**
663b611c801SAlexander Leidinger * @todo Collect num{midis,timers}.
664b611c801SAlexander Leidinger *
665b611c801SAlexander Leidinger * Need access to sound/midi/midi.c::midistat_lock in order
666b611c801SAlexander Leidinger * to safely touch midi_devices and get a head count of, well,
667b611c801SAlexander Leidinger * MIDI devices. midistat_lock is a global static (i.e., local to
668b611c801SAlexander Leidinger * midi.c), but midi_devices is a regular global; should the mutex
669b611c801SAlexander Leidinger * be publicized, or is there another way to get this information?
670b611c801SAlexander Leidinger *
671b611c801SAlexander Leidinger * NB: MIDI/sequencer stuff is currently on hold.
672b611c801SAlexander Leidinger */
673b611c801SAlexander Leidinger si->nummidis = 0;
674b611c801SAlexander Leidinger si->numtimers = 0;
6755d980fadSChristos Margiolis /*
6765d980fadSChristos Margiolis * Set this to the maximum unit number so that applications will not
6775d980fadSChristos Margiolis * break if they try to loop through all mixers and some of them are
6785d980fadSChristos Margiolis * not available.
6795d980fadSChristos Margiolis */
6805d980fadSChristos Margiolis si->nummixers = devclass_get_maxunit(pcm_devclass);
68159d98edaSChristos Margiolis si->numcards = devclass_get_maxunit(pcm_devclass);
682e07f9178SChristos Margiolis si->numaudios = devclass_get_maxunit(pcm_devclass);
683b611c801SAlexander Leidinger /* OSSv4 docs: Intended only for test apps; API doesn't
684b611c801SAlexander Leidinger really have much of a concept of cards. Shouldn't be
685b611c801SAlexander Leidinger used by applications. */
686b611c801SAlexander Leidinger
687b611c801SAlexander Leidinger /**
688b611c801SAlexander Leidinger * @todo Fill in "busy devices" fields.
689b611c801SAlexander Leidinger *
690b611c801SAlexander Leidinger * si->openedmidi = " MIDI devices
691b611c801SAlexander Leidinger */
692b611c801SAlexander Leidinger bzero((void *)&si->openedmidi, sizeof(si->openedmidi));
693b611c801SAlexander Leidinger
694b611c801SAlexander Leidinger /*
695b611c801SAlexander Leidinger * Si->filler is a reserved array, but according to docs each
696b611c801SAlexander Leidinger * element should be set to -1.
697b611c801SAlexander Leidinger */
698c597c557SChristos Margiolis for (i = 0; i < nitems(si->filler); i++)
699b611c801SAlexander Leidinger si->filler[i] = -1;
700b611c801SAlexander Leidinger }
701b611c801SAlexander Leidinger
70252f6e09eSAlexander Motin int
sound_oss_card_info(oss_card_info * si)70352f6e09eSAlexander Motin sound_oss_card_info(oss_card_info *si)
70452f6e09eSAlexander Motin {
70552f6e09eSAlexander Motin struct snddev_info *d;
706305db91dSChristos Margiolis int i;
70752f6e09eSAlexander Motin
70852f6e09eSAlexander Motin for (i = 0; pcm_devclass != NULL &&
70952f6e09eSAlexander Motin i < devclass_get_maxunit(pcm_devclass); i++) {
71052f6e09eSAlexander Motin d = devclass_get_softc(pcm_devclass, i);
711305db91dSChristos Margiolis if (i != si->card)
71252f6e09eSAlexander Motin continue;
71352f6e09eSAlexander Motin
7145d980fadSChristos Margiolis if (!PCM_REGISTERED(d)) {
7155d980fadSChristos Margiolis snprintf(si->shortname, sizeof(si->shortname),
7165d980fadSChristos Margiolis "pcm%d (n/a)", i);
7175d980fadSChristos Margiolis strlcpy(si->longname, "Device unavailable",
7185d980fadSChristos Margiolis sizeof(si->longname));
7195d980fadSChristos Margiolis si->hw_info[0] = '\0';
7205d980fadSChristos Margiolis si->intr_count = si->ack_count = 0;
7215d980fadSChristos Margiolis } else {
72290da2b28SAriff Abdullah PCM_UNLOCKASSERT(d);
72390da2b28SAriff Abdullah PCM_LOCK(d);
72452f6e09eSAlexander Motin
72552f6e09eSAlexander Motin strlcpy(si->shortname, device_get_nameunit(d->dev),
72652f6e09eSAlexander Motin sizeof(si->shortname));
72752f6e09eSAlexander Motin strlcpy(si->longname, device_get_desc(d->dev),
72852f6e09eSAlexander Motin sizeof(si->longname));
72952f6e09eSAlexander Motin strlcpy(si->hw_info, d->status, sizeof(si->hw_info));
73052f6e09eSAlexander Motin si->intr_count = si->ack_count = 0;
73190da2b28SAriff Abdullah
73290da2b28SAriff Abdullah PCM_UNLOCK(d);
7335d980fadSChristos Margiolis }
73490da2b28SAriff Abdullah
73552f6e09eSAlexander Motin return (0);
73652f6e09eSAlexander Motin }
73752f6e09eSAlexander Motin return (ENXIO);
73852f6e09eSAlexander Motin }
73952f6e09eSAlexander Motin
740b611c801SAlexander Leidinger /************************************************************************/
741b611c801SAlexander Leidinger
7420a0301deSChristos Margiolis static void
sound_global_init(void)7430a0301deSChristos Margiolis sound_global_init(void)
7440a0301deSChristos Margiolis {
7450a0301deSChristos Margiolis if (snd_verbose < 0 || snd_verbose > 4)
7460a0301deSChristos Margiolis snd_verbose = 1;
7470a0301deSChristos Margiolis
7480a0301deSChristos Margiolis if (snd_unit < 0)
7490a0301deSChristos Margiolis snd_unit = -1;
7500a0301deSChristos Margiolis
7510a0301deSChristos Margiolis if (snd_maxautovchans < 0 ||
7520a0301deSChristos Margiolis snd_maxautovchans > SND_MAXVCHANS)
7530a0301deSChristos Margiolis snd_maxautovchans = 0;
7540a0301deSChristos Margiolis
7550a0301deSChristos Margiolis if (chn_latency < CHN_LATENCY_MIN ||
7560a0301deSChristos Margiolis chn_latency > CHN_LATENCY_MAX)
7570a0301deSChristos Margiolis chn_latency = CHN_LATENCY_DEFAULT;
7580a0301deSChristos Margiolis
7590a0301deSChristos Margiolis if (chn_latency_profile < CHN_LATENCY_PROFILE_MIN ||
7600a0301deSChristos Margiolis chn_latency_profile > CHN_LATENCY_PROFILE_MAX)
7610a0301deSChristos Margiolis chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT;
7620a0301deSChristos Margiolis
7630a0301deSChristos Margiolis if (feeder_rate_min < FEEDRATE_MIN ||
7640a0301deSChristos Margiolis feeder_rate_max < FEEDRATE_MIN ||
7650a0301deSChristos Margiolis feeder_rate_min > FEEDRATE_MAX ||
7660a0301deSChristos Margiolis feeder_rate_max > FEEDRATE_MAX ||
7670a0301deSChristos Margiolis !(feeder_rate_min < feeder_rate_max)) {
7680a0301deSChristos Margiolis feeder_rate_min = FEEDRATE_RATEMIN;
7690a0301deSChristos Margiolis feeder_rate_max = FEEDRATE_RATEMAX;
7700a0301deSChristos Margiolis }
7710a0301deSChristos Margiolis
7720a0301deSChristos Margiolis if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN ||
7730a0301deSChristos Margiolis feeder_rate_round > FEEDRATE_ROUNDHZ_MAX)
7740a0301deSChristos Margiolis feeder_rate_round = FEEDRATE_ROUNDHZ;
7750a0301deSChristos Margiolis
7760a0301deSChristos Margiolis if (bootverbose)
7770a0301deSChristos Margiolis printf("%s: snd_unit=%d snd_maxautovchans=%d "
7780a0301deSChristos Margiolis "latency=%d "
7790a0301deSChristos Margiolis "feeder_rate_min=%d feeder_rate_max=%d "
7800a0301deSChristos Margiolis "feeder_rate_round=%d\n",
7810a0301deSChristos Margiolis __func__, snd_unit, snd_maxautovchans,
7820a0301deSChristos Margiolis chn_latency,
7830a0301deSChristos Margiolis feeder_rate_min, feeder_rate_max,
7840a0301deSChristos Margiolis feeder_rate_round);
7850a0301deSChristos Margiolis }
7860a0301deSChristos Margiolis
7870739ea1dSSeigo Tanimura static int
sound_modevent(module_t mod,int type,void * data)7880739ea1dSSeigo Tanimura sound_modevent(module_t mod, int type, void *data)
7890739ea1dSSeigo Tanimura {
790b611c801SAlexander Leidinger int ret;
791b611c801SAlexander Leidinger
7928109ec9dSJohn Baldwin ret = 0;
793b611c801SAlexander Leidinger switch (type) {
794b611c801SAlexander Leidinger case MOD_LOAD:
79513bebcd3SJohn Baldwin pcm_devclass = devclass_create("pcm");
796b611c801SAlexander Leidinger pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL);
7970a0301deSChristos Margiolis sound_global_init();
798b611c801SAlexander Leidinger break;
799b611c801SAlexander Leidinger case MOD_UNLOAD:
800b611c801SAlexander Leidinger if (pcmsg_unrhdr != NULL) {
801b611c801SAlexander Leidinger delete_unrhdr(pcmsg_unrhdr);
802b611c801SAlexander Leidinger pcmsg_unrhdr = NULL;
803b611c801SAlexander Leidinger }
804b611c801SAlexander Leidinger break;
8055525b53dSAlexander Motin case MOD_SHUTDOWN:
8065525b53dSAlexander Motin break;
807b611c801SAlexander Leidinger default:
80890da2b28SAriff Abdullah ret = ENOTSUP;
809b611c801SAlexander Leidinger }
810b611c801SAlexander Leidinger
811b611c801SAlexander Leidinger return ret;
8120739ea1dSSeigo Tanimura }
8130739ea1dSSeigo Tanimura
8140739ea1dSSeigo Tanimura DEV_MODULE(sound, sound_modevent, NULL);
8150739ea1dSSeigo Tanimura MODULE_VERSION(sound, SOUND_MODVER);
816