1139749Simp/*- 2193640Sariff * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 3193640Sariff * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 4193640Sariff * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> 5192920Sjoel * Copyright (c) 1997 Luigi Rizzo 650724Scg * All rights reserved. 750724Scg * 850724Scg * Redistribution and use in source and binary forms, with or without 950724Scg * modification, are permitted provided that the following conditions 1050724Scg * are met: 1150724Scg * 1. Redistributions of source code must retain the above copyright 1250724Scg * notice, this list of conditions and the following disclaimer. 1350724Scg * 2. Redistributions in binary form must reproduce the above copyright 1450724Scg * notice, this list of conditions and the following disclaimer in the 1550724Scg * documentation and/or other materials provided with the distribution. 1650724Scg * 1750724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1850724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1950724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2050724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2150724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2250724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2350724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2450724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2550724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2650724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2750724Scg * SUCH DAMAGE. 2850724Scg */ 2950724Scg 30193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 31193640Sariff#include "opt_snd.h" 32193640Sariff#endif 33193640Sariff 3453465Scg#include <dev/sound/pcm/sound.h> 35164614Sariff#include <dev/sound/pcm/ac97.h> 3677269Scg#include <dev/sound/pcm/vchan.h> 37124740Smatk#include <dev/sound/pcm/dsp.h> 38193640Sariff#include <dev/sound/pcm/sndstat.h> 39170161Sariff#include <dev/sound/version.h> 40162588Snetchild#include <sys/limits.h> 4165207Scg#include <sys/sysctl.h> 4250724Scg 4382180Scg#include "feeder_if.h" 4482180Scg 4582180ScgSND_DECLARE_FILE("$FreeBSD: stable/10/sys/dev/sound/pcm/sound.c 358879 2020-03-11 08:26:11Z hselasky $"); 4682180Scg 4778362Scgdevclass_t pcm_devclass; 4850724Scg 4989834Scgint pcm_veto_load = 1; 5089834Scg 51170884Sariffint snd_unit = -1; 52159732SnetchildTUNABLE_INT("hw.snd.default_unit", &snd_unit); 5379141Scg 54222826Smavstatic int snd_unit_auto = -1; 55170885SariffTUNABLE_INT("hw.snd.default_auto", &snd_unit_auto); 56170885SariffSYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RW, 57170893Sariff &snd_unit_auto, 0, "assign default unit to a newly attached device"); 58170885Sariff 59170161Sariffint snd_maxautovchans = 16; 60159732Snetchild/* XXX: a tunable implies that we may need more than one sound channel before 61159732Snetchild the system can change a sysctl (/etc/sysctl.conf), do we really need 62159732Snetchild this? */ 6382180ScgTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 6450724Scg 6573127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 6673127Scg 67170161Sariff/* 68170161Sariff * XXX I've had enough with people not telling proper version/arch 69170161Sariff * while reporting problems, not after 387397913213th questions/requests. 70170161Sariff */ 71209193Savgstatic char snd_driver_version[] = 72170161Sariff __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; 73170161SariffSYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, 74193640Sariff 0, "driver version/arch"); 75170161Sariff 76162588Snetchild/** 77162588Snetchild * @brief Unit number allocator for syncgroup IDs 78162588Snetchild */ 79162588Snetchildstruct unrhdr *pcmsg_unrhdr = NULL; 80162588Snetchild 81193640Sariffstatic int 82193640Sariffsndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS) 83193640Sariff{ 84193640Sariff SNDSTAT_PREPARE_PCM_BEGIN(); 85193640Sariff SNDSTAT_PREPARE_PCM_END(); 86193640Sariff} 8782180Scg 8873131Scgvoid * 8993814Sjhbsnd_mtxcreate(const char *desc, const char *type) 9073131Scg{ 9173131Scg struct mtx *m; 9273131Scg 93111119Simp m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 94125136Struckman mtx_init(m, desc, type, MTX_DEF); 9573131Scg return m; 9673131Scg} 9773131Scg 9873131Scgvoid 9973131Scgsnd_mtxfree(void *m) 10073131Scg{ 10173131Scg struct mtx *mtx = m; 10273131Scg 10373131Scg mtx_destroy(mtx); 10473131Scg free(mtx, M_DEVBUF); 10573131Scg} 10673131Scg 10773131Scgvoid 10873131Scgsnd_mtxassert(void *m) 10973131Scg{ 11078670Scg#ifdef INVARIANTS 11173131Scg struct mtx *mtx = m; 11273131Scg 11373131Scg mtx_assert(mtx, MA_OWNED); 11473131Scg#endif 11573131Scg} 11673131Scg 11773131Scgint 11873131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 11973131Scg{ 120170815Sariff struct snddev_info *d; 121193640Sariff 12273131Scg flags &= INTR_MPSAFE; 12378366Speter flags |= INTR_TYPE_AV; 124170815Sariff d = device_get_softc(dev); 125170815Sariff if (d != NULL && (flags & INTR_MPSAFE)) 126170815Sariff d->flags |= SD_F_MPSAFE; 127170815Sariff 128166918Sariff return bus_setup_intr(dev, res, flags, 129166918Sariff#if __FreeBSD_version >= 700031 130166918Sariff NULL, 131166918Sariff#endif 132166918Sariff hand, param, cookiep); 13373131Scg} 13473131Scg 135170161Sariffstatic void 136170161Sariffpcm_clonereset(struct snddev_info *d) 137170161Sariff{ 138170161Sariff int cmax; 139170161Sariff 140170815Sariff PCM_BUSYASSERT(d); 141170161Sariff 142170161Sariff cmax = d->playcount + d->reccount - 1; 143170161Sariff if (d->pvchancount > 0) 144193640Sariff cmax += max(d->pvchancount, snd_maxautovchans) - 1; 145170161Sariff if (d->rvchancount > 0) 146193640Sariff cmax += max(d->rvchancount, snd_maxautovchans) - 1; 147170161Sariff if (cmax > PCMMAXCLONE) 148170161Sariff cmax = PCMMAXCLONE; 149170161Sariff (void)snd_clone_gc(d->clones); 150170161Sariff (void)snd_clone_setmaxunit(d->clones, cmax); 151170161Sariff} 152170161Sariff 153193640Sariffint 154170161Sariffpcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) 155157331Sariff{ 156170161Sariff struct pcm_channel *c, *ch, *nch; 157193640Sariff struct pcmchan_caps *caps; 158193640Sariff int i, err, vcnt; 159157331Sariff 160170815Sariff PCM_BUSYASSERT(d); 161170161Sariff 162170161Sariff if ((direction == PCMDIR_PLAY && d->playcount < 1) || 163170815Sariff (direction == PCMDIR_REC && d->reccount < 1)) 164170815Sariff return (ENODEV); 165164614Sariff 166170815Sariff if (!(d->flags & SD_F_AUTOVCHAN)) 167170815Sariff return (EINVAL); 168157331Sariff 169170815Sariff if (newcnt < 0 || newcnt > SND_MAXVCHANS) 170170815Sariff return (E2BIG); 171157331Sariff 172170161Sariff if (direction == PCMDIR_PLAY) 173170161Sariff vcnt = d->pvchancount; 174170161Sariff else if (direction == PCMDIR_REC) 175170161Sariff vcnt = d->rvchancount; 176170815Sariff else 177170815Sariff return (EINVAL); 178157331Sariff 179157331Sariff if (newcnt > vcnt) { 180170161Sariff KASSERT(num == -1 || 181170161Sariff (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), 182170161Sariff ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", 183170161Sariff num, newcnt, vcnt)); 184157331Sariff /* add new vchans - find a parent channel first */ 185170815Sariff ch = NULL; 186170161Sariff CHN_FOREACH(c, d, channels.pcm) { 187157331Sariff CHN_LOCK(c); 188170161Sariff if (c->direction == direction && 189170161Sariff ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 190193640Sariff c->refcount < 1 && 191170815Sariff !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 192193640Sariff /* 193193640Sariff * Reuse hw channel with vchans already 194193640Sariff * created. 195193640Sariff */ 196193640Sariff if (c->flags & CHN_F_HAS_VCHAN) { 197193640Sariff ch = c; 198193640Sariff break; 199193640Sariff } 200193640Sariff /* 201193640Sariff * No vchans ever created, look for 202193640Sariff * channels with supported formats. 203193640Sariff */ 204193640Sariff caps = chn_getcaps(c); 205193640Sariff if (caps == NULL) { 206193640Sariff CHN_UNLOCK(c); 207193640Sariff continue; 208193640Sariff } 209193640Sariff for (i = 0; caps->fmtlist[i] != 0; i++) { 210193640Sariff if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 211193640Sariff break; 212193640Sariff } 213193640Sariff if (caps->fmtlist[i] != 0) { 214193640Sariff ch = c; 215193640Sariff break; 216193640Sariff } 217170815Sariff } 218157331Sariff CHN_UNLOCK(c); 219157331Sariff } 220170815Sariff if (ch == NULL) 221170815Sariff return (EBUSY); 222170815Sariff ch->flags |= CHN_F_BUSY; 223170815Sariff err = 0; 224157331Sariff while (err == 0 && newcnt > vcnt) { 225170815Sariff err = vchan_create(ch, num); 226170161Sariff if (err == 0) 227157331Sariff vcnt++; 228170161Sariff else if (err == E2BIG && newcnt > vcnt) 229170161Sariff device_printf(d->dev, 230170161Sariff "%s: err=%d Maximum channel reached.\n", 231170161Sariff __func__, err); 232157331Sariff } 233157331Sariff if (vcnt == 0) 234170815Sariff ch->flags &= ~CHN_F_BUSY; 235170815Sariff CHN_UNLOCK(ch); 236170815Sariff if (err != 0) 237170815Sariff return (err); 238170815Sariff else 239170815Sariff pcm_clonereset(d); 240157331Sariff } else if (newcnt < vcnt) { 241170161Sariff KASSERT(num == -1, 242170161Sariff ("bogus vchan_destroy() request num=%d", num)); 243170161Sariff CHN_FOREACH(c, d, channels.pcm) { 244170161Sariff CHN_LOCK(c); 245170161Sariff if (c->direction != direction || 246170161Sariff CHN_EMPTY(c, children) || 247170161Sariff !(c->flags & CHN_F_HAS_VCHAN)) { 248157331Sariff CHN_UNLOCK(c); 249170161Sariff continue; 250157331Sariff } 251170161Sariff CHN_FOREACH_SAFE(ch, c, nch, children) { 252170161Sariff CHN_LOCK(ch); 253193640Sariff if (vcnt == 1 && c->refcount > 0) { 254170161Sariff CHN_UNLOCK(ch); 255193640Sariff break; 256193640Sariff } 257193640Sariff if (!(ch->flags & CHN_F_BUSY) && 258193640Sariff ch->refcount < 1) { 259170161Sariff err = vchan_destroy(ch); 260170161Sariff if (err == 0) 261170161Sariff vcnt--; 262170161Sariff } else 263170161Sariff CHN_UNLOCK(ch); 264170815Sariff if (vcnt == newcnt) 265170161Sariff break; 266170161Sariff } 267170161Sariff CHN_UNLOCK(c); 268157331Sariff break; 269157331Sariff } 270170161Sariff pcm_clonereset(d); 271157331Sariff } 272157331Sariff 273170815Sariff return (0); 274157331Sariff} 275157331Sariff 276156929Sariff/* return error status and a locked channel */ 277156929Sariffint 278156929Sariffpcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 279193640Sariff pid_t pid, char *comm, int devunit) 28077269Scg{ 28177269Scg struct pcm_channel *c; 282193640Sariff int err, vchancount, vchan_num; 28377269Scg 284170161Sariff KASSERT(d != NULL && ch != NULL && (devunit == -1 || 285170161Sariff !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && 286170161Sariff (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 287170815Sariff ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d", 288170161Sariff __func__, d, ch, direction, pid, devunit)); 289170815Sariff PCM_BUSYASSERT(d); 290170161Sariff 291170161Sariff /* Double check again. */ 292170161Sariff if (devunit != -1) { 293170161Sariff switch (snd_unit2d(devunit)) { 294170161Sariff case SND_DEV_DSPHW_PLAY: 295170161Sariff case SND_DEV_DSPHW_VPLAY: 296170161Sariff if (direction != PCMDIR_PLAY) 297193640Sariff return (ENOTSUP); 298170161Sariff break; 299170161Sariff case SND_DEV_DSPHW_REC: 300170161Sariff case SND_DEV_DSPHW_VREC: 301170161Sariff if (direction != PCMDIR_REC) 302193640Sariff return (ENOTSUP); 303170161Sariff break; 304170161Sariff default: 305170161Sariff if (!(direction == PCMDIR_PLAY || 306170161Sariff direction == PCMDIR_REC)) 307193640Sariff return (ENOTSUP); 308170161Sariff break; 309170161Sariff } 310170161Sariff } 311170161Sariff 312193640Sariff *ch = NULL; 313193640Sariff vchan_num = 0; 314193640Sariff vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : 315193640Sariff d->rvchancount; 316193640Sariff 317156929Sariffretry_chnalloc: 318193640Sariff err = ENOTSUP; 31978895Scg /* scan for a free channel */ 320170161Sariff CHN_FOREACH(c, d, channels.pcm) { 32177882Scg CHN_LOCK(c); 322193640Sariff if (devunit == -1 && c->direction == direction && 323193640Sariff (c->flags & CHN_F_VIRTUAL)) { 324193640Sariff if (vchancount < snd_maxautovchans && 325193640Sariff vchan_num < CHN_CHAN(c)) { 326193640Sariff CHN_UNLOCK(c); 327193640Sariff goto vchan_alloc; 328193640Sariff } 329193640Sariff vchan_num++; 330193640Sariff } 331170161Sariff if (c->direction == direction && !(c->flags & CHN_F_BUSY) && 332170161Sariff (devunit == -1 || devunit == -2 || c->unit == devunit)) { 333170161Sariff c->flags |= CHN_F_BUSY; 334170161Sariff c->pid = pid; 335193640Sariff strlcpy(c->comm, (comm != NULL) ? comm : 336193640Sariff CHN_COMM_UNKNOWN, sizeof(c->comm)); 337170161Sariff *ch = c; 338170161Sariff return (0); 339170161Sariff } else if (c->unit == devunit) { 340156929Sariff if (c->direction != direction) 341193640Sariff err = ENOTSUP; 342156929Sariff else if (c->flags & CHN_F_BUSY) 343156929Sariff err = EBUSY; 344156929Sariff else 345156929Sariff err = EINVAL; 346156929Sariff CHN_UNLOCK(c); 347170161Sariff return (err); 348170161Sariff } else if ((devunit == -1 || devunit == -2) && 349170161Sariff c->direction == direction && (c->flags & CHN_F_BUSY)) 350157331Sariff err = EBUSY; 35177882Scg CHN_UNLOCK(c); 35277269Scg } 35378895Scg 354170815Sariff if (devunit == -2) 355170815Sariff return (err); 356170815Sariff 357193640Sariffvchan_alloc: 35878895Scg /* no channel available */ 359170815Sariff if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || 360170815Sariff snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { 361170161Sariff if (!(vchancount > 0 && vchancount < snd_maxautovchans) && 362170161Sariff (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) 363170161Sariff return (err); 364170161Sariff err = pcm_setvchans(d, direction, vchancount + 1, 365170161Sariff (devunit == -1) ? -1 : snd_unit2c(devunit)); 366157331Sariff if (err == 0) { 367170161Sariff if (devunit == -1) 368170161Sariff devunit = -2; 369157331Sariff goto retry_chnalloc; 37078895Scg } 37178895Scg } 37278895Scg 373170161Sariff return (err); 37477269Scg} 37577269Scg 37678214Scg/* release a locked channel and unlock it */ 37777269Scgint 37878214Scgpcm_chnrelease(struct pcm_channel *c) 37977269Scg{ 380170815Sariff PCM_BUSYASSERT(c->parentsnddev); 38178214Scg CHN_LOCKASSERT(c); 382170815Sariff 38377269Scg c->flags &= ~CHN_F_BUSY; 38478214Scg c->pid = -1; 385193640Sariff strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm)); 38677882Scg CHN_UNLOCK(c); 387170815Sariff 388170815Sariff return (0); 38977269Scg} 39077269Scg 39177269Scgint 39277269Scgpcm_chnref(struct pcm_channel *c, int ref) 39377269Scg{ 394170815Sariff PCM_BUSYASSERT(c->parentsnddev); 395170815Sariff CHN_LOCKASSERT(c); 39677882Scg 39777269Scg c->refcount += ref; 398170815Sariff 399170815Sariff return (c->refcount); 40077269Scg} 40177269Scg 40282180Scgint 40382180Scgpcm_inprog(struct snddev_info *d, int delta) 40482180Scg{ 405193640Sariff PCM_LOCKASSERT(d); 406119096Scg 407170815Sariff d->inprog += delta; 408119096Scg 409170815Sariff return (d->inprog); 41082180Scg} 41182180Scg 41282180Scgstatic void 41382180Scgpcm_setmaxautovchans(struct snddev_info *d, int num) 41482180Scg{ 415170815Sariff PCM_BUSYASSERT(d); 416170815Sariff 417170161Sariff if (num < 0) 418170161Sariff return; 419170161Sariff 420170161Sariff if (num >= 0 && d->pvchancount > num) 421170161Sariff (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); 422170161Sariff else if (num > 0 && d->pvchancount == 0) 423170161Sariff (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); 424170161Sariff 425170161Sariff if (num >= 0 && d->rvchancount > num) 426170161Sariff (void)pcm_setvchans(d, PCMDIR_REC, num, -1); 427170161Sariff else if (num > 0 && d->rvchancount == 0) 428170161Sariff (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); 429170161Sariff 430170161Sariff pcm_clonereset(d); 43182180Scg} 43282180Scg 43365340Scgstatic int 434159732Snetchildsysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 43565340Scg{ 43678214Scg struct snddev_info *d; 43765340Scg int error, unit; 43865340Scg 43965340Scg unit = snd_unit; 440170289Sdwmalone error = sysctl_handle_int(oidp, &unit, 0, req); 44165340Scg if (error == 0 && req->newptr != NULL) { 44278214Scg d = devclass_get_softc(pcm_devclass, unit); 443170815Sariff if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 44478214Scg return EINVAL; 44565340Scg snd_unit = unit; 446222826Smav snd_unit_auto = 0; 44765340Scg } 44865340Scg return (error); 44965340Scg} 450159732Snetchild/* XXX: do we need a way to let the user change the default unit? */ 451159732SnetchildSYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW, 452164614Sariff 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device"); 45365340Scg 45478853Scgstatic int 45582180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 45678853Scg{ 45782180Scg struct snddev_info *d; 45882180Scg int i, v, error; 45978853Scg 46082180Scg v = snd_maxautovchans; 461170289Sdwmalone error = sysctl_handle_int(oidp, &v, 0, req); 46278853Scg if (error == 0 && req->newptr != NULL) { 463170161Sariff if (v < 0) 464170161Sariff v = 0; 465170161Sariff if (v > SND_MAXVCHANS) 466170161Sariff v = SND_MAXVCHANS; 467170161Sariff snd_maxautovchans = v; 468170235Sariff for (i = 0; pcm_devclass != NULL && 469170235Sariff i < devclass_get_maxunit(pcm_devclass); i++) { 470170161Sariff d = devclass_get_softc(pcm_devclass, i); 471170815Sariff if (!PCM_REGISTERED(d)) 472170161Sariff continue; 473170815Sariff PCM_ACQUIRE_QUICK(d); 474170161Sariff pcm_setmaxautovchans(d, v); 475170815Sariff PCM_RELEASE_QUICK(d); 47682180Scg } 47778853Scg } 47878853Scg return (error); 47978853Scg} 48082180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 481164614Sariff 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 48278853Scg 48377269Scgstruct pcm_channel * 484170161Sariffpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo) 48550724Scg{ 486170161Sariff struct pcm_channel *ch; 487170161Sariff int direction, err, rpnum, *pnum, max; 488170161Sariff int udc, device, chan; 489170161Sariff char *dirs, *devname, buf[CHN_NAMELEN]; 49050724Scg 491170815Sariff PCM_BUSYASSERT(d); 492193640Sariff PCM_LOCKASSERT(d); 493170161Sariff KASSERT(num >= -1, ("invalid num=%d", num)); 494170161Sariff 495170161Sariff 496170815Sariff switch (dir) { 49777269Scg case PCMDIR_PLAY: 49877269Scg dirs = "play"; 499126367Struckman direction = PCMDIR_PLAY; 50083614Scg pnum = &d->playcount; 501170161Sariff device = SND_DEV_DSPHW_PLAY; 502170161Sariff max = SND_MAXHWCHAN; 50377269Scg break; 504170161Sariff case PCMDIR_PLAY_VIRTUAL: 505170161Sariff dirs = "virtual"; 506170161Sariff direction = PCMDIR_PLAY; 507170161Sariff pnum = &d->pvchancount; 508170161Sariff device = SND_DEV_DSPHW_VPLAY; 509170161Sariff max = SND_MAXVCHANS; 510170161Sariff break; 51177269Scg case PCMDIR_REC: 51277269Scg dirs = "record"; 513126367Struckman direction = PCMDIR_REC; 51483614Scg pnum = &d->reccount; 515170161Sariff device = SND_DEV_DSPHW_REC; 516170161Sariff max = SND_MAXHWCHAN; 51777269Scg break; 518170161Sariff case PCMDIR_REC_VIRTUAL: 51977269Scg dirs = "virtual"; 520170161Sariff direction = PCMDIR_REC; 521170161Sariff pnum = &d->rvchancount; 522170161Sariff device = SND_DEV_DSPHW_VREC; 523170161Sariff max = SND_MAXVCHANS; 52477269Scg break; 52577269Scg default: 526170815Sariff return (NULL); 52777269Scg } 52865340Scg 529170161Sariff chan = (num == -1) ? 0 : num; 53083614Scg 531170815Sariff if (*pnum >= max || chan >= max) 532170815Sariff return (NULL); 533170161Sariff 534156929Sariff rpnum = 0; 535170161Sariff 536170161Sariff CHN_FOREACH(ch, d, channels.pcm) { 537170161Sariff if (CHN_DEV(ch) != device) 538156929Sariff continue; 539170161Sariff if (chan == CHN_CHAN(ch)) { 540170161Sariff if (num != -1) { 541170161Sariff device_printf(d->dev, 542170161Sariff "channel num=%d allocated!\n", chan); 543170815Sariff return (NULL); 544170161Sariff } 545170161Sariff chan++; 546170161Sariff if (chan >= max) { 547170161Sariff device_printf(d->dev, 548170161Sariff "chan=%d > %d\n", chan, max); 549170815Sariff return (NULL); 550170161Sariff } 551156929Sariff } 552156929Sariff rpnum++; 553156929Sariff } 554170161Sariff 555156929Sariff if (*pnum != rpnum) { 556156929Sariff device_printf(d->dev, 557170161Sariff "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", 558170161Sariff __func__, dirs, *pnum, rpnum); 559170815Sariff return (NULL); 560156929Sariff } 561170161Sariff 562170161Sariff udc = snd_mkunit(device_get_unit(d->dev), device, chan); 563170161Sariff devname = dsp_unit2name(buf, sizeof(buf), udc); 564170161Sariff 565170161Sariff if (devname == NULL) { 566170161Sariff device_printf(d->dev, 567170161Sariff "Failed to query device name udc=0x%08x\n", udc); 568170815Sariff return (NULL); 569170161Sariff } 570170161Sariff 571193640Sariff PCM_UNLOCK(d); 572170161Sariff ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 573170161Sariff ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); 574170161Sariff ch->unit = udc; 57577269Scg ch->pid = -1; 576193640Sariff strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm)); 57777269Scg ch->parentsnddev = d; 57877269Scg ch->parentchannel = parent; 57989774Sscottl ch->dev = d->dev; 580170161Sariff ch->trigger = PCMTRIG_STOP; 581170161Sariff snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", 582170161Sariff device_get_nameunit(ch->dev), dirs, devname); 58377269Scg 584126367Struckman err = chn_init(ch, devinfo, dir, direction); 585193640Sariff PCM_LOCK(d); 58670134Scg if (err) { 587170161Sariff device_printf(d->dev, "chn_init(%s) failed: err = %d\n", 588170161Sariff ch->name, err); 58977269Scg kobj_delete(ch->methods, M_DEVBUF); 59077269Scg free(ch, M_DEVBUF); 591170815Sariff return (NULL); 59255483Scg } 59377269Scg 594170815Sariff return (ch); 59577269Scg} 59677269Scg 59777269Scgint 59877269Scgpcm_chn_destroy(struct pcm_channel *ch) 59977269Scg{ 60083614Scg struct snddev_info *d; 60177269Scg int err; 60277269Scg 60383614Scg d = ch->parentsnddev; 604170815Sariff PCM_BUSYASSERT(d); 605170815Sariff 60677269Scg err = chn_kill(ch); 60777269Scg if (err) { 608170815Sariff device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n", 609170815Sariff ch->name, err); 610170815Sariff return (err); 61177269Scg } 61277269Scg 61377269Scg kobj_delete(ch->methods, M_DEVBUF); 61477269Scg free(ch, M_DEVBUF); 61577269Scg 616170815Sariff return (0); 61777269Scg} 61877269Scg 61977269Scgint 620124740Smatkpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 62177269Scg{ 622170815Sariff PCM_BUSYASSERT(d); 623193640Sariff PCM_LOCKASSERT(d); 624170161Sariff KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 625170161Sariff ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 62677269Scg 627193640Sariff CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); 628170161Sariff 629170815Sariff switch (CHN_DEV(ch)) { 630170815Sariff case SND_DEV_DSPHW_PLAY: 631170815Sariff d->playcount++; 632170815Sariff break; 633170815Sariff case SND_DEV_DSPHW_VPLAY: 634170815Sariff d->pvchancount++; 635170815Sariff break; 636170815Sariff case SND_DEV_DSPHW_REC: 637170815Sariff d->reccount++; 638170815Sariff break; 639170815Sariff case SND_DEV_DSPHW_VREC: 640170815Sariff d->rvchancount++; 641170815Sariff break; 642170815Sariff default: 643170815Sariff break; 644170815Sariff } 645170815Sariff 646170161Sariff d->devcount++; 647164614Sariff 648170815Sariff return (0); 64950724Scg} 65050724Scg 65177269Scgint 652124740Smatkpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 65365340Scg{ 654170161Sariff struct pcm_channel *tmp; 65565340Scg 656170815Sariff PCM_BUSYASSERT(d); 657193640Sariff PCM_LOCKASSERT(d); 658170815Sariff 659170161Sariff tmp = NULL; 660119096Scg 661170161Sariff CHN_FOREACH(tmp, d, channels.pcm) { 662170161Sariff if (tmp == ch) 663170161Sariff break; 66465340Scg } 665107237Scg 666170161Sariff if (tmp != ch) 667170815Sariff return (EINVAL); 668170161Sariff 669170161Sariff CHN_REMOVE(d, ch, channels.pcm); 670170815Sariff 671170161Sariff switch (CHN_DEV(ch)) { 672170161Sariff case SND_DEV_DSPHW_PLAY: 673170161Sariff d->playcount--; 674170161Sariff break; 675170161Sariff case SND_DEV_DSPHW_VPLAY: 676170161Sariff d->pvchancount--; 677170161Sariff break; 678170161Sariff case SND_DEV_DSPHW_REC: 679107237Scg d->reccount--; 680170161Sariff break; 681170161Sariff case SND_DEV_DSPHW_VREC: 682170161Sariff d->rvchancount--; 683170161Sariff break; 684170161Sariff default: 685170161Sariff break; 686170161Sariff } 687107237Scg 688170815Sariff d->devcount--; 689170815Sariff 690170815Sariff return (0); 69165340Scg} 69265340Scg 69350724Scgint 69477269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 69577269Scg{ 696157331Sariff struct snddev_info *d = device_get_softc(dev); 69782180Scg struct pcm_channel *ch; 698157331Sariff int err; 69977269Scg 700170815Sariff PCM_BUSYASSERT(d); 701170815Sariff 702193640Sariff PCM_LOCK(d); 703170161Sariff ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); 70477269Scg if (!ch) { 705170815Sariff device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", 706170815Sariff cls->name, dir, devinfo); 707193640Sariff PCM_UNLOCK(d); 708170815Sariff return (ENODEV); 70977269Scg } 71078853Scg 711124740Smatk err = pcm_chn_add(d, ch); 712193640Sariff PCM_UNLOCK(d); 71377269Scg if (err) { 714170815Sariff device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", 715170815Sariff ch->name, err); 71677269Scg pcm_chn_destroy(ch); 71777269Scg } 71877269Scg 719170815Sariff return (err); 72077269Scg} 72177269Scg 72277269Scgstatic int 72377269Scgpcm_killchan(device_t dev) 72477269Scg{ 725157331Sariff struct snddev_info *d = device_get_softc(dev); 726106420Scognet struct pcm_channel *ch; 727170815Sariff int error; 72877269Scg 729170815Sariff PCM_BUSYASSERT(d); 730170815Sariff 731170161Sariff ch = CHN_FIRST(d, channels.pcm); 73277269Scg 733193640Sariff PCM_LOCK(d); 734170161Sariff error = pcm_chn_remove(d, ch); 735193640Sariff PCM_UNLOCK(d); 736106420Scognet if (error) 737106420Scognet return (error); 738106420Scognet return (pcm_chn_destroy(ch)); 73977269Scg} 74077269Scg 741222826Smavstatic int 742222826Smavpcm_best_unit(int old) 743222826Smav{ 744222826Smav struct snddev_info *d; 745222826Smav int i, best, bestprio, prio; 746222826Smav 747222826Smav best = -1; 748222826Smav bestprio = -100; 749222826Smav for (i = 0; pcm_devclass != NULL && 750222826Smav i < devclass_get_maxunit(pcm_devclass); i++) { 751222826Smav d = devclass_get_softc(pcm_devclass, i); 752222826Smav if (!PCM_REGISTERED(d)) 753222826Smav continue; 754222826Smav prio = 0; 755222826Smav if (d->playcount == 0) 756222826Smav prio -= 10; 757222826Smav if (d->reccount == 0) 758222826Smav prio -= 2; 759222826Smav if (prio > bestprio || (prio == bestprio && i == old)) { 760222826Smav best = i; 761222826Smav bestprio = prio; 762222826Smav } 763222826Smav } 764222826Smav return (best); 765222826Smav} 766222826Smav 76777269Scgint 76850724Scgpcm_setstatus(device_t dev, char *str) 76950724Scg{ 770157331Sariff struct snddev_info *d = device_get_softc(dev); 77177882Scg 772170815Sariff PCM_BUSYASSERT(d); 773170815Sariff 774170815Sariff if (d->playcount == 0 || d->reccount == 0) 775170815Sariff d->flags |= SD_F_SIMPLEX; 776170815Sariff 777170815Sariff if ((d->playcount > 0 || d->reccount > 0) && 778170815Sariff !(d->flags & SD_F_AUTOVCHAN)) { 779170815Sariff d->flags |= SD_F_AUTOVCHAN; 780170815Sariff vchan_initsys(dev); 781170815Sariff } 782170815Sariff 783170161Sariff pcm_setmaxautovchans(d, snd_maxautovchans); 784170161Sariff 785170815Sariff strlcpy(d->status, str, SND_STATUSLEN); 786170815Sariff 787193640Sariff PCM_LOCK(d); 788170161Sariff 789170161Sariff /* Last stage, enable cloning. */ 790170815Sariff if (d->clones != NULL) 791170161Sariff (void)snd_clone_enable(d->clones); 792170161Sariff 793170815Sariff /* Done, we're ready.. */ 794170815Sariff d->flags |= SD_F_REGISTERED; 795170815Sariff 796170815Sariff PCM_RELEASE(d); 797170815Sariff 798193640Sariff PCM_UNLOCK(d); 799170161Sariff 800222826Smav if (snd_unit_auto < 0) 801222826Smav snd_unit_auto = (snd_unit < 0) ? 1 : 0; 802222826Smav if (snd_unit < 0 || snd_unit_auto > 1) 803170884Sariff snd_unit = device_get_unit(dev); 804222826Smav else if (snd_unit_auto == 1) 805222826Smav snd_unit = pcm_best_unit(snd_unit); 806170884Sariff 807170815Sariff return (0); 80850724Scg} 80950724Scg 810157331Sariffuint32_t 81150724Scgpcm_getflags(device_t dev) 81250724Scg{ 813157331Sariff struct snddev_info *d = device_get_softc(dev); 81477882Scg 81550724Scg return d->flags; 81650724Scg} 81750724Scg 81850724Scgvoid 819157331Sariffpcm_setflags(device_t dev, uint32_t val) 82050724Scg{ 821157331Sariff struct snddev_info *d = device_get_softc(dev); 82278362Scg 82350724Scg d->flags = val; 82450724Scg} 82550724Scg 82658384Scgvoid * 82758384Scgpcm_getdevinfo(device_t dev) 82858384Scg{ 829157331Sariff struct snddev_info *d = device_get_softc(dev); 83077882Scg 83158384Scg return d->devinfo; 83258384Scg} 83358384Scg 83483614Scgunsigned int 835160439Snetchildpcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 83683614Scg{ 837157331Sariff struct snddev_info *d = device_get_softc(dev); 83889690Scg int sz, x; 83983614Scg 84083614Scg sz = 0; 84189690Scg if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 84289690Scg x = sz; 843160439Snetchild RANGE(sz, minbufsz, maxbufsz); 84489690Scg if (x != sz) 845160439Snetchild device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 846160439Snetchild x = minbufsz; 84789690Scg while (x < sz) 84889690Scg x <<= 1; 84989690Scg if (x > sz) 85089690Scg x >>= 1; 85189690Scg if (x != sz) { 85289690Scg device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 85389690Scg sz = x; 85489690Scg } 85589690Scg } else { 85683614Scg sz = deflt; 85789690Scg } 85889690Scg 85983614Scg d->bufsz = sz; 86083614Scg 86183614Scg return sz; 86283614Scg} 86383614Scg 864170161Sariffstatic int 865193640Sariffsysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 866193640Sariff{ 867193640Sariff struct snddev_info *d; 868193640Sariff int err, val; 869193640Sariff 870193640Sariff d = oidp->oid_arg1; 871193640Sariff if (!PCM_REGISTERED(d)) 872193640Sariff return (ENODEV); 873193640Sariff 874193640Sariff PCM_LOCK(d); 875193640Sariff PCM_WAIT(d); 876193640Sariff val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 877193640Sariff PCM_ACQUIRE(d); 878193640Sariff PCM_UNLOCK(d); 879193640Sariff 880193640Sariff err = sysctl_handle_int(oidp, &val, 0, req); 881193640Sariff 882193640Sariff if (err == 0 && req->newptr != NULL) { 883193640Sariff if (!(val == 0 || val == 1)) { 884193640Sariff PCM_RELEASE_QUICK(d); 885193640Sariff return (EINVAL); 886193640Sariff } 887193640Sariff 888193640Sariff PCM_LOCK(d); 889193640Sariff 890193640Sariff d->flags &= ~SD_F_BITPERFECT; 891193640Sariff d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 892193640Sariff 893193640Sariff PCM_RELEASE(d); 894193640Sariff PCM_UNLOCK(d); 895193640Sariff } else 896193640Sariff PCM_RELEASE_QUICK(d); 897193640Sariff 898193640Sariff return (err); 899193640Sariff} 900193640Sariff 901193640Sariff#ifdef SND_DEBUG 902193640Sariffstatic int 903170161Sariffsysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS) 904170161Sariff{ 905170161Sariff struct snddev_info *d; 906170161Sariff uint32_t flags; 907170161Sariff int err; 908170161Sariff 909170161Sariff d = oidp->oid_arg1; 910170815Sariff if (!PCM_REGISTERED(d) || d->clones == NULL) 911170161Sariff return (ENODEV); 912170161Sariff 913170815Sariff PCM_ACQUIRE_QUICK(d); 914170815Sariff 915170161Sariff flags = snd_clone_getflags(d->clones); 916170289Sdwmalone err = sysctl_handle_int(oidp, &flags, 0, req); 917170161Sariff 918170161Sariff if (err == 0 && req->newptr != NULL) { 919170815Sariff if (flags & ~SND_CLONE_MASK) 920170161Sariff err = EINVAL; 921170815Sariff else 922170161Sariff (void)snd_clone_setflags(d->clones, flags); 923170161Sariff } 924170161Sariff 925170815Sariff PCM_RELEASE_QUICK(d); 926170815Sariff 927170161Sariff return (err); 928170161Sariff} 929170161Sariff 930170161Sariffstatic int 931170161Sariffsysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS) 932170161Sariff{ 933170161Sariff struct snddev_info *d; 934170161Sariff int err, deadline; 935170161Sariff 936170161Sariff d = oidp->oid_arg1; 937170815Sariff if (!PCM_REGISTERED(d) || d->clones == NULL) 938170161Sariff return (ENODEV); 939170161Sariff 940170815Sariff PCM_ACQUIRE_QUICK(d); 941170815Sariff 942170161Sariff deadline = snd_clone_getdeadline(d->clones); 943170289Sdwmalone err = sysctl_handle_int(oidp, &deadline, 0, req); 944170161Sariff 945170161Sariff if (err == 0 && req->newptr != NULL) { 946170161Sariff if (deadline < 0) 947170161Sariff err = EINVAL; 948170815Sariff else 949170161Sariff (void)snd_clone_setdeadline(d->clones, deadline); 950170161Sariff } 951170161Sariff 952170815Sariff PCM_RELEASE_QUICK(d); 953170815Sariff 954170161Sariff return (err); 955170161Sariff} 956170161Sariff 957170161Sariffstatic int 958170161Sariffsysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS) 959170161Sariff{ 960170161Sariff struct snddev_info *d; 961170161Sariff int err, val; 962170161Sariff 963170161Sariff d = oidp->oid_arg1; 964170815Sariff if (!PCM_REGISTERED(d) || d->clones == NULL) 965170161Sariff return (ENODEV); 966170161Sariff 967170161Sariff val = 0; 968170289Sdwmalone err = sysctl_handle_int(oidp, &val, 0, req); 969170161Sariff 970170161Sariff if (err == 0 && req->newptr != NULL && val != 0) { 971170815Sariff PCM_ACQUIRE_QUICK(d); 972170815Sariff val = snd_clone_gc(d->clones); 973170815Sariff PCM_RELEASE_QUICK(d); 974170815Sariff if (bootverbose != 0 || snd_verbose > 3) 975170815Sariff device_printf(d->dev, "clone gc: pruned=%d\n", val); 976170161Sariff } 977170161Sariff 978170161Sariff return (err); 979170161Sariff} 980170161Sariff 981170161Sariffstatic int 982170161Sariffsysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS) 983170161Sariff{ 984170161Sariff struct snddev_info *d; 985170161Sariff int i, err, val; 986170161Sariff 987170161Sariff val = 0; 988170289Sdwmalone err = sysctl_handle_int(oidp, &val, 0, req); 989170161Sariff 990170161Sariff if (err == 0 && req->newptr != NULL && val != 0) { 991170235Sariff for (i = 0; pcm_devclass != NULL && 992170235Sariff i < devclass_get_maxunit(pcm_devclass); i++) { 993170161Sariff d = devclass_get_softc(pcm_devclass, i); 994170815Sariff if (!PCM_REGISTERED(d) || d->clones == NULL) 995170161Sariff continue; 996170815Sariff PCM_ACQUIRE_QUICK(d); 997170815Sariff val = snd_clone_gc(d->clones); 998170815Sariff PCM_RELEASE_QUICK(d); 999170815Sariff if (bootverbose != 0 || snd_verbose > 3) 1000170815Sariff device_printf(d->dev, "clone gc: pruned=%d\n", 1001170815Sariff val); 1002170161Sariff } 1003170161Sariff } 1004170161Sariff 1005170161Sariff return (err); 1006170161Sariff} 1007170161SariffSYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW, 1008170161Sariff 0, sizeof(int), sysctl_hw_snd_clone_gc, "I", 1009170161Sariff "global clone garbage collector"); 1010170161Sariff#endif 1011170161Sariff 101250724Scgint 101350724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec) 101450724Scg{ 1015170161Sariff struct snddev_info *d; 1016193640Sariff int i; 101750724Scg 101889834Scg if (pcm_veto_load) { 101989834Scg device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 102089834Scg 102189834Scg return EINVAL; 102289834Scg } 102389834Scg 1024170161Sariff if (device_get_unit(dev) > PCMMAXUNIT) { 1025170161Sariff device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n", 1026170161Sariff device_get_unit(dev), PCMMAXUNIT); 1027170161Sariff device_printf(dev, 1028170161Sariff "Use 'hw.snd.maxunit' tunable to raise the limit.\n"); 1029170161Sariff return ENODEV; 1030170161Sariff } 1031170161Sariff 1032170161Sariff d = device_get_softc(dev); 1033170815Sariff d->dev = dev; 103493814Sjhb d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 1035170815Sariff cv_init(&d->cv, device_get_nameunit(dev)); 1036170815Sariff PCM_ACQUIRE_QUICK(d); 1037170815Sariff dsp_cdevinfo_init(d); 1038148587Snetchild#if 0 1039148587Snetchild /* 1040148587Snetchild * d->flags should be cleared by the allocator of the softc. 1041148587Snetchild * We cannot clear this field here because several devices set 1042148587Snetchild * this flag before calling pcm_register(). 1043148587Snetchild */ 104478853Scg d->flags = 0; 1045148587Snetchild#endif 1046193640Sariff i = 0; 1047193640Sariff if (resource_int_value(device_get_name(dev), device_get_unit(dev), 1048193640Sariff "vpc", &i) != 0 || i != 0) 1049193640Sariff d->flags |= SD_F_VPC; 1050193640Sariff 1051193640Sariff if (resource_int_value(device_get_name(dev), device_get_unit(dev), 1052193640Sariff "bitperfect", &i) == 0 && i != 0) 1053193640Sariff d->flags |= SD_F_BITPERFECT; 1054193640Sariff 105550724Scg d->devinfo = devinfo; 105678895Scg d->devcount = 0; 105783089Scg d->reccount = 0; 105883614Scg d->playcount = 0; 1059170161Sariff d->pvchancount = 0; 1060170161Sariff d->rvchancount = 0; 1061170161Sariff d->pvchanrate = 0; 1062170161Sariff d->pvchanformat = 0; 1063170161Sariff d->rvchanrate = 0; 1064170161Sariff d->rvchanformat = 0; 106578362Scg d->inprog = 0; 106650724Scg 1067170161Sariff /* 1068170161Sariff * Create clone manager, disabled by default. Cloning will be 1069193640Sariff * enabled during final stage of driver initialization through 1070170161Sariff * pcm_setstatus(). 1071170161Sariff */ 1072170815Sariff d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE, 1073170815Sariff SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK | 1074170161Sariff SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF | 1075170161Sariff SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED); 1076124617Sphk 1077170161Sariff CHN_INIT(d, channels.pcm); 1078170161Sariff CHN_INIT(d, channels.pcm.busy); 1079193640Sariff CHN_INIT(d, channels.pcm.opened); 1080170161Sariff 1081170815Sariff /* XXX This is incorrect, but lets play along for now. */ 1082157331Sariff if ((numplay == 0 || numrec == 0) && numplay != numrec) 108378214Scg d->flags |= SD_F_SIMPLEX; 108478362Scg 1085170161Sariff sysctl_ctx_init(&d->play_sysctl_ctx); 1086170161Sariff d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 1087170161Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 1088170161Sariff CTLFLAG_RD, 0, "playback channels node"); 1089170161Sariff sysctl_ctx_init(&d->rec_sysctl_ctx); 1090170161Sariff d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 1091170161Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 1092170161Sariff CTLFLAG_RD, 0, "record channels node"); 1093159732Snetchild /* XXX: an user should be able to set this with a control tool, the 1094159732Snetchild sysadmin then needs min+max sysctls for this */ 1095217323Smdf SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 1096164614Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 1097164614Sariff OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 1098193640Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1099193640Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1100193640Sariff "bitperfect", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 1101193640Sariff sysctl_dev_pcm_bitperfect, "I", 1102193640Sariff "bit-perfect playback/recording (0=disable, 1=enable)"); 1103170161Sariff#ifdef SND_DEBUG 1104170161Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1105170161Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1106170161Sariff "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d), 1107170161Sariff sysctl_dev_pcm_clone_flags, "IU", 1108170161Sariff "clone flags"); 1109170161Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1110170161Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1111170161Sariff "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 1112170161Sariff sysctl_dev_pcm_clone_deadline, "I", 1113170161Sariff "clone expiration deadline (ms)"); 1114170161Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1115170161Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1116170161Sariff "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 1117170161Sariff sysctl_dev_pcm_clone_gc, "I", 1118170161Sariff "clone garbage collector"); 111973127Scg#endif 1120170815Sariff 1121170161Sariff if (numplay > 0 || numrec > 0) { 1122156929Sariff d->flags |= SD_F_AUTOVCHAN; 112382180Scg vchan_initsys(dev); 1124149953Snetchild } 112578853Scg 1126193640Sariff if (d->flags & SD_F_EQ) 1127193640Sariff feeder_eq_initsys(dev); 1128193640Sariff 112982180Scg sndstat_register(dev, d->status, sndstat_prepare_pcm); 1130170815Sariff 1131157331Sariff return 0; 113250724Scg} 113350724Scg 113465340Scgint 113565340Scgpcm_unregister(device_t dev) 113665207Scg{ 1137170815Sariff struct snddev_info *d; 113883614Scg struct pcm_channel *ch; 1139170161Sariff struct thread *td; 114065207Scg 1141170161Sariff td = curthread; 1142170815Sariff d = device_get_softc(dev); 1143170161Sariff 1144170815Sariff if (!PCM_ALIVE(d)) { 1145170815Sariff device_printf(dev, "unregister: device not configured\n"); 1146170815Sariff return (0); 1147170815Sariff } 1148170815Sariff 1149170161Sariff if (sndstat_acquire(td) != 0) { 1150150827Snetchild device_printf(dev, "unregister: sndstat busy\n"); 1151170815Sariff return (EBUSY); 1152150827Snetchild } 1153150827Snetchild 1154193640Sariff PCM_LOCK(d); 1155170815Sariff PCM_WAIT(d); 1156170815Sariff 1157358879Shselasky d->flags |= SD_F_DETACHING; 1158358879Shselasky 1159170815Sariff if (d->inprog != 0) { 116083476Sgreid device_printf(dev, "unregister: operation in progress\n"); 1161193640Sariff PCM_UNLOCK(d); 1162170161Sariff sndstat_release(td); 1163170815Sariff return (EBUSY); 116478362Scg } 1165124740Smatk 1166170815Sariff PCM_ACQUIRE(d); 1167193640Sariff PCM_UNLOCK(d); 1168170815Sariff 1169170161Sariff CHN_FOREACH(ch, d, channels.pcm) { 1170170815Sariff CHN_LOCK(ch); 117183614Scg if (ch->refcount > 0) { 1172170815Sariff device_printf(dev, 1173170815Sariff "unregister: channel %s busy (pid %d)\n", 1174170815Sariff ch->name, ch->pid); 1175170815Sariff CHN_UNLOCK(ch); 1176170815Sariff PCM_RELEASE_QUICK(d); 1177170161Sariff sndstat_release(td); 1178170815Sariff return (EBUSY); 117977269Scg } 1180170815Sariff CHN_UNLOCK(ch); 118165487Scg } 1182124740Smatk 1183170161Sariff if (d->clones != NULL) { 1184170161Sariff if (snd_clone_busy(d->clones) != 0) { 1185170161Sariff device_printf(dev, "unregister: clone busy\n"); 1186170815Sariff PCM_RELEASE_QUICK(d); 1187170161Sariff sndstat_release(td); 1188170815Sariff return (EBUSY); 1189170815Sariff } else { 1190193640Sariff PCM_LOCK(d); 1191170161Sariff (void)snd_clone_disable(d->clones); 1192193640Sariff PCM_UNLOCK(d); 1193170815Sariff } 1194170161Sariff } 1195170161Sariff 1196157022Sariff if (mixer_uninit(dev) == EBUSY) { 1197148587Snetchild device_printf(dev, "unregister: mixer busy\n"); 1198193640Sariff PCM_LOCK(d); 1199170161Sariff if (d->clones != NULL) 1200170161Sariff (void)snd_clone_enable(d->clones); 1201170815Sariff PCM_RELEASE(d); 1202193640Sariff PCM_UNLOCK(d); 1203170161Sariff sndstat_release(td); 1204170815Sariff return (EBUSY); 1205148587Snetchild } 1206148587Snetchild 1207193640Sariff PCM_LOCK(d); 1208170815Sariff d->flags |= SD_F_DYING; 1209170815Sariff d->flags &= ~SD_F_REGISTERED; 1210193640Sariff PCM_UNLOCK(d); 1211170815Sariff 1212170815Sariff /* 1213170815Sariff * No lock being held, so this thing can be flushed without 1214170815Sariff * stucking into devdrn oblivion. 1215170815Sariff */ 1216170161Sariff if (d->clones != NULL) { 1217170161Sariff snd_clone_destroy(d->clones); 1218170161Sariff d->clones = NULL; 1219124740Smatk } 1220124740Smatk 1221170161Sariff if (d->play_sysctl_tree != NULL) { 1222170161Sariff sysctl_ctx_free(&d->play_sysctl_ctx); 1223170161Sariff d->play_sysctl_tree = NULL; 1224170161Sariff } 1225170161Sariff if (d->rec_sysctl_tree != NULL) { 1226170161Sariff sysctl_ctx_free(&d->rec_sysctl_ctx); 1227170161Sariff d->rec_sysctl_tree = NULL; 1228170161Sariff } 1229157331Sariff 1230170161Sariff while (!CHN_EMPTY(d, channels.pcm)) 123177269Scg pcm_killchan(dev); 123265207Scg 1233170815Sariff dsp_cdevinfo_flush(d); 1234170815Sariff 1235193640Sariff PCM_LOCK(d); 1236170815Sariff PCM_RELEASE(d); 1237170815Sariff cv_destroy(&d->cv); 1238193640Sariff PCM_UNLOCK(d); 123977882Scg snd_mtxfree(d->lock); 1240150827Snetchild sndstat_unregister(dev); 1241170161Sariff sndstat_release(td); 1242170161Sariff 1243170161Sariff if (snd_unit == device_get_unit(dev)) { 1244222826Smav snd_unit = pcm_best_unit(-1); 1245222826Smav if (snd_unit_auto == 0) 1246222826Smav snd_unit_auto = 1; 1247170161Sariff } 1248170161Sariff 1249170815Sariff return (0); 125065207Scg} 125165207Scg 125282180Scg/************************************************************************/ 125382180Scg 1254162588Snetchild/** 1255162588Snetchild * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 1256162588Snetchild * 1257162588Snetchild * @param si Pointer to oss_sysinfo struct where information about the 1258162588Snetchild * sound subsystem will be written/copied. 1259162588Snetchild * 1260162588Snetchild * This routine returns information about the sound system, such as the 1261162588Snetchild * current OSS version, number of audio, MIDI, and mixer drivers, etc. 1262162588Snetchild * Also includes a bitmask showing which of the above types of devices 1263162588Snetchild * are open (busy). 1264162588Snetchild * 1265162588Snetchild * @note 1266162588Snetchild * Calling threads must not hold any snddev_info or pcm_channel locks. 1267162588Snetchild * 1268162588Snetchild * @author Ryan Beasley <ryanb@FreeBSD.org> 1269162588Snetchild */ 1270162588Snetchildvoid 1271162588Snetchildsound_oss_sysinfo(oss_sysinfo *si) 1272162588Snetchild{ 1273162588Snetchild static char si_product[] = "FreeBSD native OSS ABI"; 1274162588Snetchild static char si_version[] = __XSTRING(__FreeBSD_version); 1275186875Smav static char si_license[] = "BSD"; 1276162588Snetchild static int intnbits = sizeof(int) * 8; /* Better suited as macro? 1277162588Snetchild Must pester a C guru. */ 1278162588Snetchild 1279162588Snetchild struct snddev_info *d; 1280162588Snetchild struct pcm_channel *c; 1281162588Snetchild int i, j, ncards; 1282162588Snetchild 1283162588Snetchild ncards = 0; 1284162588Snetchild 1285162588Snetchild strlcpy(si->product, si_product, sizeof(si->product)); 1286162588Snetchild strlcpy(si->version, si_version, sizeof(si->version)); 1287162588Snetchild si->versionnum = SOUND_VERSION; 1288186875Smav strlcpy(si->license, si_license, sizeof(si->license)); 1289162588Snetchild 1290162588Snetchild /* 1291162588Snetchild * Iterate over PCM devices and their channels, gathering up data 1292162588Snetchild * for the numaudios, ncards, and openedaudio fields. 1293162588Snetchild */ 1294162588Snetchild si->numaudios = 0; 1295162588Snetchild bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 1296162588Snetchild 1297170815Sariff j = 0; 1298162588Snetchild 1299170815Sariff for (i = 0; pcm_devclass != NULL && 1300170815Sariff i < devclass_get_maxunit(pcm_devclass); i++) { 1301170815Sariff d = devclass_get_softc(pcm_devclass, i); 1302170815Sariff if (!PCM_REGISTERED(d)) 1303170815Sariff continue; 1304162588Snetchild 1305170815Sariff /* XXX Need Giant magic entry ??? */ 1306162588Snetchild 1307170815Sariff /* See note in function's docblock */ 1308193640Sariff PCM_UNLOCKASSERT(d); 1309193640Sariff PCM_LOCK(d); 1310162588Snetchild 1311170815Sariff si->numaudios += d->devcount; 1312170815Sariff ++ncards; 1313162588Snetchild 1314170815Sariff CHN_FOREACH(c, d, channels.pcm) { 1315193640Sariff CHN_UNLOCKASSERT(c); 1316170815Sariff CHN_LOCK(c); 1317170815Sariff if (c->flags & CHN_F_BUSY) 1318170815Sariff si->openedaudio[j / intnbits] |= 1319170815Sariff (1 << (j % intnbits)); 1320170815Sariff CHN_UNLOCK(c); 1321170815Sariff j++; 1322162588Snetchild } 1323170815Sariff 1324193640Sariff PCM_UNLOCK(d); 1325162588Snetchild } 1326186875Smav si->numaudioengines = si->numaudios; 1327162588Snetchild 1328162588Snetchild si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 1329162588Snetchild /** 1330162588Snetchild * @todo Collect num{midis,timers}. 1331162588Snetchild * 1332162588Snetchild * Need access to sound/midi/midi.c::midistat_lock in order 1333162588Snetchild * to safely touch midi_devices and get a head count of, well, 1334162588Snetchild * MIDI devices. midistat_lock is a global static (i.e., local to 1335162588Snetchild * midi.c), but midi_devices is a regular global; should the mutex 1336162588Snetchild * be publicized, or is there another way to get this information? 1337162588Snetchild * 1338162588Snetchild * NB: MIDI/sequencer stuff is currently on hold. 1339162588Snetchild */ 1340162588Snetchild si->nummidis = 0; 1341162588Snetchild si->numtimers = 0; 1342162588Snetchild si->nummixers = mixer_count; 1343162588Snetchild si->numcards = ncards; 1344162588Snetchild /* OSSv4 docs: Intended only for test apps; API doesn't 1345162588Snetchild really have much of a concept of cards. Shouldn't be 1346162588Snetchild used by applications. */ 1347162588Snetchild 1348162588Snetchild /** 1349162588Snetchild * @todo Fill in "busy devices" fields. 1350162588Snetchild * 1351162588Snetchild * si->openedmidi = " MIDI devices 1352162588Snetchild */ 1353162588Snetchild bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 1354162588Snetchild 1355162588Snetchild /* 1356162588Snetchild * Si->filler is a reserved array, but according to docs each 1357162588Snetchild * element should be set to -1. 1358162588Snetchild */ 1359162588Snetchild for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++) 1360162588Snetchild si->filler[i] = -1; 1361162588Snetchild} 1362162588Snetchild 1363187030Smavint 1364187030Smavsound_oss_card_info(oss_card_info *si) 1365187030Smav{ 1366187030Smav struct snddev_info *d; 1367187030Smav int i, ncards; 1368187030Smav 1369187030Smav ncards = 0; 1370187030Smav 1371187030Smav for (i = 0; pcm_devclass != NULL && 1372187030Smav i < devclass_get_maxunit(pcm_devclass); i++) { 1373187030Smav d = devclass_get_softc(pcm_devclass, i); 1374187030Smav if (!PCM_REGISTERED(d)) 1375187030Smav continue; 1376187030Smav 1377187030Smav if (ncards++ != si->card) 1378187030Smav continue; 1379187030Smav 1380193640Sariff PCM_UNLOCKASSERT(d); 1381193640Sariff PCM_LOCK(d); 1382187030Smav 1383187030Smav strlcpy(si->shortname, device_get_nameunit(d->dev), 1384187030Smav sizeof(si->shortname)); 1385187030Smav strlcpy(si->longname, device_get_desc(d->dev), 1386187030Smav sizeof(si->longname)); 1387187030Smav strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 1388187030Smav si->intr_count = si->ack_count = 0; 1389193640Sariff 1390193640Sariff PCM_UNLOCK(d); 1391193640Sariff 1392187030Smav return (0); 1393187030Smav } 1394187030Smav return (ENXIO); 1395187030Smav} 1396187030Smav 1397162588Snetchild/************************************************************************/ 1398162588Snetchild 1399132236Stanimurastatic int 1400132236Stanimurasound_modevent(module_t mod, int type, void *data) 1401132236Stanimura{ 1402162588Snetchild int ret; 1403148587Snetchild#if 0 1404132236Stanimura return (midi_modevent(mod, type, data)); 1405148587Snetchild#else 1406162588Snetchild ret = 0; 1407162588Snetchild 1408162588Snetchild switch(type) { 1409162588Snetchild case MOD_LOAD: 1410162588Snetchild pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 1411162588Snetchild break; 1412162588Snetchild case MOD_UNLOAD: 1413170161Sariff ret = sndstat_acquire(curthread); 1414170161Sariff if (ret != 0) 1415170161Sariff break; 1416162588Snetchild if (pcmsg_unrhdr != NULL) { 1417162588Snetchild delete_unrhdr(pcmsg_unrhdr); 1418162588Snetchild pcmsg_unrhdr = NULL; 1419162588Snetchild } 1420162588Snetchild break; 1421231647Smav case MOD_SHUTDOWN: 1422231647Smav break; 1423162588Snetchild default: 1424193640Sariff ret = ENOTSUP; 1425162588Snetchild } 1426162588Snetchild 1427162588Snetchild return ret; 1428148587Snetchild#endif 1429132236Stanimura} 1430132236Stanimura 1431132236StanimuraDEV_MODULE(sound, sound_modevent, NULL); 1432132236StanimuraMODULE_VERSION(sound, SOUND_MODVER); 1433