sound.c revision 170893
1139749Simp/*- 2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 3166427Sjoel * (C) 1997 Luigi Rizzo 450724Scg * All rights reserved. 550724Scg * 650724Scg * Redistribution and use in source and binary forms, with or without 750724Scg * modification, are permitted provided that the following conditions 850724Scg * are met: 950724Scg * 1. Redistributions of source code must retain the above copyright 1050724Scg * notice, this list of conditions and the following disclaimer. 1150724Scg * 2. Redistributions in binary form must reproduce the above copyright 1250724Scg * notice, this list of conditions and the following disclaimer in the 1350724Scg * documentation and/or other materials provided with the distribution. 1450724Scg * 1550724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1650724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1750724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1850724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1950724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2050724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2150724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2250724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2350724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2450724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2550724Scg * SUCH DAMAGE. 2650724Scg */ 2750724Scg 2853465Scg#include <dev/sound/pcm/sound.h> 29164614Sariff#include <dev/sound/pcm/ac97.h> 3077269Scg#include <dev/sound/pcm/vchan.h> 31124740Smatk#include <dev/sound/pcm/dsp.h> 32170161Sariff#include <dev/sound/version.h> 33162588Snetchild#include <sys/limits.h> 3465207Scg#include <sys/sysctl.h> 3550724Scg 3682180Scg#include "feeder_if.h" 3782180Scg 3882180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/sound.c 170893 2007-06-17 19:02:05Z ariff $"); 3982180Scg 4078362Scgdevclass_t pcm_devclass; 4150724Scg 4289834Scgint pcm_veto_load = 1; 4389834Scg 4473127Scg#ifdef USING_DEVFS 45170884Sariffint snd_unit = -1; 46159732SnetchildTUNABLE_INT("hw.snd.default_unit", &snd_unit); 4773127Scg#endif 4879141Scg 49170885Sariffstatic int snd_unit_auto = 0; 50170885SariffTUNABLE_INT("hw.snd.default_auto", &snd_unit_auto); 51170885SariffSYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RW, 52170893Sariff &snd_unit_auto, 0, "assign default unit to a newly attached device"); 53170885Sariff 54170161Sariffint snd_maxautovchans = 16; 55159732Snetchild/* XXX: a tunable implies that we may need more than one sound channel before 56159732Snetchild the system can change a sysctl (/etc/sysctl.conf), do we really need 57159732Snetchild this? */ 5882180ScgTUNABLE_INT("hw.snd.maxautovchans", &snd_maxautovchans); 5950724Scg 6073127ScgSYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 6173127Scg 62170161Sariff/* 63170161Sariff * XXX I've had enough with people not telling proper version/arch 64170161Sariff * while reporting problems, not after 387397913213th questions/requests. 65170161Sariff */ 66170161Sariffstatic const char snd_driver_version[] = 67170161Sariff __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; 68170161SariffSYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, 69170161Sariff 0, "Driver version/arch"); 70170161Sariff 71162588Snetchild/** 72162588Snetchild * @brief Unit number allocator for syncgroup IDs 73162588Snetchild */ 74162588Snetchildstruct unrhdr *pcmsg_unrhdr = NULL; 75162588Snetchild 7682180Scgstatic int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); 7782180Scg 7873131Scgvoid * 7993814Sjhbsnd_mtxcreate(const char *desc, const char *type) 8073131Scg{ 8173131Scg#ifdef USING_MUTEX 8273131Scg struct mtx *m; 8373131Scg 84111119Simp m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 85125136Struckman mtx_init(m, desc, type, MTX_DEF); 8673131Scg return m; 8773131Scg#else 8873757Scg return (void *)0xcafebabe; 8973131Scg#endif 9073131Scg} 9173131Scg 9273131Scgvoid 9373131Scgsnd_mtxfree(void *m) 9473131Scg{ 9573131Scg#ifdef USING_MUTEX 9673131Scg struct mtx *mtx = m; 9773131Scg 98107237Scg /* mtx_assert(mtx, MA_OWNED); */ 9973131Scg mtx_destroy(mtx); 10073131Scg free(mtx, M_DEVBUF); 10173131Scg#endif 10273131Scg} 10373131Scg 10473131Scgvoid 10573131Scgsnd_mtxassert(void *m) 10673131Scg{ 10773131Scg#ifdef USING_MUTEX 10878670Scg#ifdef INVARIANTS 10973131Scg struct mtx *mtx = m; 11073131Scg 11173131Scg mtx_assert(mtx, MA_OWNED); 11273131Scg#endif 11378670Scg#endif 11473131Scg} 115107237Scg/* 11673131Scgvoid 11773131Scgsnd_mtxlock(void *m) 11873131Scg{ 11973131Scg#ifdef USING_MUTEX 12073131Scg struct mtx *mtx = m; 12173131Scg 12273131Scg mtx_lock(mtx); 12373131Scg#endif 12473131Scg} 12573131Scg 12673131Scgvoid 12773131Scgsnd_mtxunlock(void *m) 12873131Scg{ 12973131Scg#ifdef USING_MUTEX 13073131Scg struct mtx *mtx = m; 13173131Scg 13273131Scg mtx_unlock(mtx); 13373131Scg#endif 13473131Scg} 135107237Scg*/ 13673131Scgint 13773131Scgsnd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 13873131Scg{ 139170815Sariff struct snddev_info *d; 14073131Scg#ifdef USING_MUTEX 14173131Scg flags &= INTR_MPSAFE; 14278366Speter flags |= INTR_TYPE_AV; 14373131Scg#else 14478366Speter flags = INTR_TYPE_AV; 14573131Scg#endif 146170815Sariff d = device_get_softc(dev); 147170815Sariff if (d != NULL && (flags & INTR_MPSAFE)) 148170815Sariff d->flags |= SD_F_MPSAFE; 149170815Sariff 150166918Sariff return bus_setup_intr(dev, res, flags, 151166918Sariff#if __FreeBSD_version >= 700031 152166918Sariff NULL, 153166918Sariff#endif 154166918Sariff hand, param, cookiep); 15573131Scg} 15673131Scg 157119096Scg#ifndef PCM_DEBUG_MTX 15882180Scgvoid 15982180Scgpcm_lock(struct snddev_info *d) 16082180Scg{ 16182180Scg snd_mtxlock(d->lock); 16282180Scg} 16382180Scg 16482180Scgvoid 16582180Scgpcm_unlock(struct snddev_info *d) 16682180Scg{ 16782180Scg snd_mtxunlock(d->lock); 16882180Scg} 169119096Scg#endif 17082180Scg 17182180Scgstruct pcm_channel * 17282180Scgpcm_getfakechan(struct snddev_info *d) 17382180Scg{ 17482180Scg return d->fakechan; 17582180Scg} 17682180Scg 177170161Sariffstatic void 178170161Sariffpcm_clonereset(struct snddev_info *d) 179170161Sariff{ 180170161Sariff int cmax; 181170161Sariff 182170815Sariff PCM_BUSYASSERT(d); 183170161Sariff 184170161Sariff cmax = d->playcount + d->reccount - 1; 185170161Sariff if (d->pvchancount > 0) 186170161Sariff cmax += MAX(d->pvchancount, snd_maxautovchans) - 1; 187170161Sariff if (d->rvchancount > 0) 188170161Sariff cmax += MAX(d->rvchancount, snd_maxautovchans) - 1; 189170161Sariff if (cmax > PCMMAXCLONE) 190170161Sariff cmax = PCMMAXCLONE; 191170161Sariff (void)snd_clone_gc(d->clones); 192170161Sariff (void)snd_clone_setmaxunit(d->clones, cmax); 193170161Sariff} 194170161Sariff 195157331Sariffstatic int 196170161Sariffpcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) 197157331Sariff{ 198170161Sariff struct pcm_channel *c, *ch, *nch; 199170161Sariff int err, vcnt; 200157331Sariff 201170815Sariff PCM_BUSYASSERT(d); 202170161Sariff 203170161Sariff if ((direction == PCMDIR_PLAY && d->playcount < 1) || 204170815Sariff (direction == PCMDIR_REC && d->reccount < 1)) 205170815Sariff return (ENODEV); 206164614Sariff 207170815Sariff if (!(d->flags & SD_F_AUTOVCHAN)) 208170815Sariff return (EINVAL); 209157331Sariff 210170815Sariff if (newcnt < 0 || newcnt > SND_MAXVCHANS) 211170815Sariff return (E2BIG); 212157331Sariff 213170161Sariff if (direction == PCMDIR_PLAY) 214170161Sariff vcnt = d->pvchancount; 215170161Sariff else if (direction == PCMDIR_REC) 216170161Sariff vcnt = d->rvchancount; 217170815Sariff else 218170815Sariff return (EINVAL); 219157331Sariff 220157331Sariff if (newcnt > vcnt) { 221170161Sariff KASSERT(num == -1 || 222170161Sariff (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), 223170161Sariff ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", 224170161Sariff num, newcnt, vcnt)); 225157331Sariff /* add new vchans - find a parent channel first */ 226170815Sariff ch = NULL; 227170161Sariff CHN_FOREACH(c, d, channels.pcm) { 228157331Sariff CHN_LOCK(c); 229170161Sariff if (c->direction == direction && 230170161Sariff ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 231170815Sariff !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 232170815Sariff ch = c; 233170815Sariff break; 234170815Sariff } 235157331Sariff CHN_UNLOCK(c); 236157331Sariff } 237170815Sariff if (ch == NULL) 238170815Sariff return (EBUSY); 239170815Sariff ch->flags |= CHN_F_BUSY; 240170815Sariff err = 0; 241157331Sariff while (err == 0 && newcnt > vcnt) { 242170815Sariff err = vchan_create(ch, num); 243170161Sariff if (err == 0) 244157331Sariff vcnt++; 245170161Sariff else if (err == E2BIG && newcnt > vcnt) 246170161Sariff device_printf(d->dev, 247170161Sariff "%s: err=%d Maximum channel reached.\n", 248170161Sariff __func__, err); 249157331Sariff } 250157331Sariff if (vcnt == 0) 251170815Sariff ch->flags &= ~CHN_F_BUSY; 252170815Sariff CHN_UNLOCK(ch); 253170815Sariff if (err != 0) 254170815Sariff return (err); 255170815Sariff else 256170815Sariff pcm_clonereset(d); 257157331Sariff } else if (newcnt < vcnt) { 258170161Sariff KASSERT(num == -1, 259170161Sariff ("bogus vchan_destroy() request num=%d", num)); 260170161Sariff CHN_FOREACH(c, d, channels.pcm) { 261170161Sariff CHN_LOCK(c); 262170161Sariff if (c->direction != direction || 263170161Sariff CHN_EMPTY(c, children) || 264170161Sariff !(c->flags & CHN_F_HAS_VCHAN)) { 265157331Sariff CHN_UNLOCK(c); 266170161Sariff continue; 267157331Sariff } 268170161Sariff CHN_FOREACH_SAFE(ch, c, nch, children) { 269170161Sariff CHN_LOCK(ch); 270170161Sariff if (!(ch->flags & CHN_F_BUSY)) { 271170161Sariff CHN_UNLOCK(ch); 272170161Sariff CHN_UNLOCK(c); 273170161Sariff err = vchan_destroy(ch); 274170161Sariff CHN_LOCK(c); 275170161Sariff if (err == 0) 276170161Sariff vcnt--; 277170161Sariff } else 278170161Sariff CHN_UNLOCK(ch); 279170815Sariff if (vcnt == newcnt) 280170161Sariff break; 281170161Sariff } 282170161Sariff CHN_UNLOCK(c); 283157331Sariff break; 284157331Sariff } 285170161Sariff pcm_clonereset(d); 286157331Sariff } 287157331Sariff 288170815Sariff return (0); 289157331Sariff} 290157331Sariff 291156929Sariff/* return error status and a locked channel */ 292156929Sariffint 293156929Sariffpcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 294170161Sariff pid_t pid, int devunit) 29577269Scg{ 29677269Scg struct pcm_channel *c; 297170161Sariff int err, vchancount; 29877269Scg 299170161Sariff KASSERT(d != NULL && ch != NULL && (devunit == -1 || 300170161Sariff !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && 301170161Sariff (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 302170815Sariff ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d", 303170161Sariff __func__, d, ch, direction, pid, devunit)); 304170815Sariff PCM_BUSYASSERT(d); 305170161Sariff 306170161Sariff /* Double check again. */ 307170161Sariff if (devunit != -1) { 308170161Sariff switch (snd_unit2d(devunit)) { 309170161Sariff case SND_DEV_DSPHW_PLAY: 310170161Sariff case SND_DEV_DSPHW_VPLAY: 311170161Sariff if (direction != PCMDIR_PLAY) 312170161Sariff return (EOPNOTSUPP); 313170161Sariff break; 314170161Sariff case SND_DEV_DSPHW_REC: 315170161Sariff case SND_DEV_DSPHW_VREC: 316170161Sariff if (direction != PCMDIR_REC) 317170161Sariff return (EOPNOTSUPP); 318170161Sariff break; 319170161Sariff default: 320170161Sariff if (!(direction == PCMDIR_PLAY || 321170161Sariff direction == PCMDIR_REC)) 322170161Sariff return (EOPNOTSUPP); 323170161Sariff break; 324170161Sariff } 325170161Sariff } 326170161Sariff 327156929Sariffretry_chnalloc: 328170815Sariff err = EOPNOTSUPP; 32978895Scg /* scan for a free channel */ 330170161Sariff CHN_FOREACH(c, d, channels.pcm) { 33177882Scg CHN_LOCK(c); 332170161Sariff if (c->direction == direction && !(c->flags & CHN_F_BUSY) && 333170161Sariff (devunit == -1 || devunit == -2 || c->unit == devunit)) { 334170161Sariff c->flags |= CHN_F_BUSY; 335170161Sariff c->pid = pid; 336170161Sariff *ch = c; 337170161Sariff return (0); 338170161Sariff } else if (c->unit == devunit) { 339156929Sariff if (c->direction != direction) 340156929Sariff err = EOPNOTSUPP; 341156929Sariff else if (c->flags & CHN_F_BUSY) 342156929Sariff err = EBUSY; 343156929Sariff else 344156929Sariff err = EINVAL; 345156929Sariff CHN_UNLOCK(c); 346170161Sariff return (err); 347170161Sariff } else if ((devunit == -1 || devunit == -2) && 348170161Sariff c->direction == direction && (c->flags & CHN_F_BUSY)) 349157331Sariff err = EBUSY; 35077882Scg CHN_UNLOCK(c); 35177269Scg } 35278895Scg 353170815Sariff if (devunit == -2) 354170815Sariff return (err); 355170815Sariff 35678895Scg /* no channel available */ 357170815Sariff if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || 358170815Sariff snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { 359170161Sariff if (direction == PCMDIR_PLAY) 360170161Sariff vchancount = d->pvchancount; 361170161Sariff else 362170161Sariff vchancount = d->rvchancount; 363170161Sariff if (!(vchancount > 0 && vchancount < snd_maxautovchans) && 364170161Sariff (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) 365170161Sariff return (err); 366170161Sariff err = pcm_setvchans(d, direction, vchancount + 1, 367170161Sariff (devunit == -1) ? -1 : snd_unit2c(devunit)); 368157331Sariff if (err == 0) { 369170161Sariff if (devunit == -1) 370170161Sariff devunit = -2; 371157331Sariff goto retry_chnalloc; 37278895Scg } 37378895Scg } 37478895Scg 375170161Sariff return (err); 37677269Scg} 37777269Scg 37878214Scg/* release a locked channel and unlock it */ 37977269Scgint 38078214Scgpcm_chnrelease(struct pcm_channel *c) 38177269Scg{ 382170815Sariff PCM_BUSYASSERT(c->parentsnddev); 38378214Scg CHN_LOCKASSERT(c); 384170815Sariff 38577269Scg c->flags &= ~CHN_F_BUSY; 38678214Scg c->pid = -1; 38777882Scg CHN_UNLOCK(c); 388170815Sariff 389170815Sariff return (0); 39077269Scg} 39177269Scg 39277269Scgint 39377269Scgpcm_chnref(struct pcm_channel *c, int ref) 39477269Scg{ 395170815Sariff PCM_BUSYASSERT(c->parentsnddev); 396170815Sariff CHN_LOCKASSERT(c); 39777882Scg 39877269Scg c->refcount += ref; 399170815Sariff 400170815Sariff return (c->refcount); 40177269Scg} 40277269Scg 40382180Scgint 40482180Scgpcm_inprog(struct snddev_info *d, int delta) 40582180Scg{ 406170815Sariff snd_mtxassert(d->lock); 407119096Scg 408170815Sariff d->inprog += delta; 409119096Scg 410170815Sariff return (d->inprog); 41182180Scg} 41282180Scg 41382180Scgstatic void 41482180Scgpcm_setmaxautovchans(struct snddev_info *d, int num) 41582180Scg{ 416170815Sariff PCM_BUSYASSERT(d); 417170815Sariff 418170161Sariff if (num < 0) 419170161Sariff return; 420170161Sariff 421170161Sariff if (num >= 0 && d->pvchancount > num) 422170161Sariff (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); 423170161Sariff else if (num > 0 && d->pvchancount == 0) 424170161Sariff (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); 425170161Sariff 426170161Sariff if (num >= 0 && d->rvchancount > num) 427170161Sariff (void)pcm_setvchans(d, PCMDIR_REC, num, -1); 428170161Sariff else if (num > 0 && d->rvchancount == 0) 429170161Sariff (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); 430170161Sariff 431170161Sariff pcm_clonereset(d); 43282180Scg} 43382180Scg 43473127Scg#ifdef USING_DEVFS 43565340Scgstatic int 436159732Snetchildsysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 43765340Scg{ 43878214Scg struct snddev_info *d; 43965340Scg int error, unit; 44065340Scg 44165340Scg unit = snd_unit; 442170289Sdwmalone error = sysctl_handle_int(oidp, &unit, 0, req); 44365340Scg if (error == 0 && req->newptr != NULL) { 44478214Scg d = devclass_get_softc(pcm_devclass, unit); 445170815Sariff if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 44678214Scg return EINVAL; 44765340Scg snd_unit = unit; 44865340Scg } 44965340Scg return (error); 45065340Scg} 451159732Snetchild/* XXX: do we need a way to let the user change the default unit? */ 452159732SnetchildSYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW, 453164614Sariff 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device"); 45473127Scg#endif 45565340Scg 45678853Scgstatic int 45782180Scgsysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 45878853Scg{ 45982180Scg struct snddev_info *d; 46082180Scg int i, v, error; 46178853Scg 46282180Scg v = snd_maxautovchans; 463170289Sdwmalone error = sysctl_handle_int(oidp, &v, 0, req); 46478853Scg if (error == 0 && req->newptr != NULL) { 465170161Sariff if (v < 0) 466170161Sariff v = 0; 467170161Sariff if (v > SND_MAXVCHANS) 468170161Sariff v = SND_MAXVCHANS; 469170161Sariff snd_maxautovchans = v; 470170235Sariff for (i = 0; pcm_devclass != NULL && 471170235Sariff i < devclass_get_maxunit(pcm_devclass); i++) { 472170161Sariff d = devclass_get_softc(pcm_devclass, i); 473170815Sariff if (!PCM_REGISTERED(d)) 474170161Sariff continue; 475170815Sariff PCM_ACQUIRE_QUICK(d); 476170161Sariff pcm_setmaxautovchans(d, v); 477170815Sariff PCM_RELEASE_QUICK(d); 47882180Scg } 47978853Scg } 48078853Scg return (error); 48178853Scg} 48282180ScgSYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, 483164614Sariff 0, sizeof(int), sysctl_hw_snd_maxautovchans, "I", "maximum virtual channel"); 48478853Scg 48577269Scgstruct pcm_channel * 486170161Sariffpcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo) 48750724Scg{ 488170161Sariff struct pcm_channel *ch; 489170161Sariff int direction, err, rpnum, *pnum, max; 490170161Sariff int udc, device, chan; 491170161Sariff char *dirs, *devname, buf[CHN_NAMELEN]; 49250724Scg 493170815Sariff PCM_BUSYASSERT(d); 494170815Sariff snd_mtxassert(d->lock); 495170161Sariff KASSERT(num >= -1, ("invalid num=%d", num)); 496170161Sariff 497170161Sariff 498170815Sariff switch (dir) { 49977269Scg case PCMDIR_PLAY: 50077269Scg dirs = "play"; 501126367Struckman direction = PCMDIR_PLAY; 50283614Scg pnum = &d->playcount; 503170161Sariff device = SND_DEV_DSPHW_PLAY; 504170161Sariff max = SND_MAXHWCHAN; 50577269Scg break; 506170161Sariff case PCMDIR_PLAY_VIRTUAL: 507170161Sariff dirs = "virtual"; 508170161Sariff direction = PCMDIR_PLAY; 509170161Sariff pnum = &d->pvchancount; 510170161Sariff device = SND_DEV_DSPHW_VPLAY; 511170161Sariff max = SND_MAXVCHANS; 512170161Sariff break; 51377269Scg case PCMDIR_REC: 51477269Scg dirs = "record"; 515126367Struckman direction = PCMDIR_REC; 51683614Scg pnum = &d->reccount; 517170161Sariff device = SND_DEV_DSPHW_REC; 518170161Sariff max = SND_MAXHWCHAN; 51977269Scg break; 520170161Sariff case PCMDIR_REC_VIRTUAL: 52177269Scg dirs = "virtual"; 522170161Sariff direction = PCMDIR_REC; 523170161Sariff pnum = &d->rvchancount; 524170161Sariff device = SND_DEV_DSPHW_VREC; 525170161Sariff max = SND_MAXVCHANS; 52677269Scg break; 52777269Scg default: 528170815Sariff return (NULL); 52977269Scg } 53065340Scg 531170161Sariff chan = (num == -1) ? 0 : num; 53283614Scg 533170815Sariff if (*pnum >= max || chan >= max) 534170815Sariff return (NULL); 535170161Sariff 536156929Sariff rpnum = 0; 537170161Sariff 538170161Sariff CHN_FOREACH(ch, d, channels.pcm) { 539170161Sariff if (CHN_DEV(ch) != device) 540156929Sariff continue; 541170161Sariff if (chan == CHN_CHAN(ch)) { 542170161Sariff if (num != -1) { 543170161Sariff device_printf(d->dev, 544170161Sariff "channel num=%d allocated!\n", chan); 545170815Sariff return (NULL); 546170161Sariff } 547170161Sariff chan++; 548170161Sariff if (chan >= max) { 549170161Sariff device_printf(d->dev, 550170161Sariff "chan=%d > %d\n", chan, max); 551170815Sariff return (NULL); 552170161Sariff } 553156929Sariff } 554156929Sariff rpnum++; 555156929Sariff } 556170161Sariff 557156929Sariff if (*pnum != rpnum) { 558156929Sariff device_printf(d->dev, 559170161Sariff "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", 560170161Sariff __func__, dirs, *pnum, rpnum); 561170815Sariff return (NULL); 562156929Sariff } 563170161Sariff 564170161Sariff udc = snd_mkunit(device_get_unit(d->dev), device, chan); 565170161Sariff devname = dsp_unit2name(buf, sizeof(buf), udc); 566170161Sariff 567170161Sariff if (devname == NULL) { 568170161Sariff device_printf(d->dev, 569170161Sariff "Failed to query device name udc=0x%08x\n", udc); 570170815Sariff return (NULL); 571170161Sariff } 572170161Sariff 573170161Sariff pcm_unlock(d); 574170161Sariff ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 575170161Sariff ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); 576170161Sariff ch->unit = udc; 57777269Scg ch->pid = -1; 57877269Scg ch->parentsnddev = d; 57977269Scg ch->parentchannel = parent; 58089774Sscottl ch->dev = d->dev; 581170161Sariff ch->trigger = PCMTRIG_STOP; 582170161Sariff snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", 583170161Sariff device_get_nameunit(ch->dev), dirs, devname); 58477269Scg 585126367Struckman err = chn_init(ch, devinfo, dir, direction); 586170815Sariff pcm_lock(d); 58770134Scg if (err) { 588170161Sariff device_printf(d->dev, "chn_init(%s) failed: err = %d\n", 589170161Sariff ch->name, err); 59077269Scg kobj_delete(ch->methods, M_DEVBUF); 59177269Scg free(ch, M_DEVBUF); 592170815Sariff return (NULL); 59355483Scg } 59477269Scg 595170815Sariff return (ch); 59677269Scg} 59777269Scg 59877269Scgint 59977269Scgpcm_chn_destroy(struct pcm_channel *ch) 60077269Scg{ 60183614Scg struct snddev_info *d; 60277269Scg int err; 60377269Scg 60483614Scg d = ch->parentsnddev; 605170815Sariff PCM_BUSYASSERT(d); 606170815Sariff 60777269Scg err = chn_kill(ch); 60877269Scg if (err) { 609170815Sariff device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n", 610170815Sariff ch->name, err); 611170815Sariff return (err); 61277269Scg } 61377269Scg 61477269Scg kobj_delete(ch->methods, M_DEVBUF); 61577269Scg free(ch, M_DEVBUF); 61677269Scg 617170815Sariff return (0); 61877269Scg} 61977269Scg 62077269Scgint 621124740Smatkpcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 62277269Scg{ 623170161Sariff struct pcm_channel *tmp, *after; 624170161Sariff int num; 62577269Scg 626170815Sariff PCM_BUSYASSERT(d); 627170815Sariff snd_mtxassert(d->lock); 628170161Sariff KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 629170161Sariff ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 63077269Scg 631156929Sariff after = NULL; 632170161Sariff tmp = NULL; 633170161Sariff num = 0; 634170161Sariff 635156929Sariff /* 636170161Sariff * Look for possible device collision. 637156929Sariff */ 638170161Sariff CHN_FOREACH(tmp, d, channels.pcm) { 639170161Sariff if (tmp->unit == ch->unit) { 640170161Sariff device_printf(d->dev, "%s(): Device collision " 641170161Sariff "old=%p new=%p devunit=0x%08x\n", 642170161Sariff __func__, tmp, ch, ch->unit); 643170815Sariff return (ENODEV); 644156762Sariff } 645170161Sariff if (CHN_DEV(tmp) < CHN_DEV(ch)) { 646170161Sariff if (num == 0) 647170161Sariff after = tmp; 648170161Sariff continue; 649170161Sariff } else if (CHN_DEV(tmp) > CHN_DEV(ch)) 650170161Sariff break; 651170161Sariff num++; 652170161Sariff if (CHN_CHAN(tmp) < CHN_CHAN(ch)) 653157331Sariff after = tmp; 654170161Sariff else if (CHN_CHAN(tmp) > CHN_CHAN(ch)) 655170161Sariff break; 65682180Scg } 657170161Sariff 658170161Sariff if (after != NULL) { 659170161Sariff CHN_INSERT_AFTER(after, ch, channels.pcm); 660156929Sariff } else { 661170161Sariff CHN_INSERT_HEAD(d, ch, channels.pcm); 662156929Sariff } 663156929Sariff 664170815Sariff switch (CHN_DEV(ch)) { 665170815Sariff case SND_DEV_DSPHW_PLAY: 666170815Sariff d->playcount++; 667170815Sariff break; 668170815Sariff case SND_DEV_DSPHW_VPLAY: 669170815Sariff d->pvchancount++; 670170815Sariff break; 671170815Sariff case SND_DEV_DSPHW_REC: 672170815Sariff d->reccount++; 673170815Sariff break; 674170815Sariff case SND_DEV_DSPHW_VREC: 675170815Sariff d->rvchancount++; 676170815Sariff break; 677170815Sariff default: 678170815Sariff break; 679170815Sariff } 680170815Sariff 681170161Sariff d->devcount++; 682164614Sariff 683170815Sariff return (0); 68450724Scg} 68550724Scg 68677269Scgint 687124740Smatkpcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 68865340Scg{ 689170161Sariff struct pcm_channel *tmp; 69065340Scg 691170815Sariff PCM_BUSYASSERT(d); 692170815Sariff snd_mtxassert(d->lock); 693170815Sariff 694170161Sariff tmp = NULL; 695119096Scg 696170161Sariff CHN_FOREACH(tmp, d, channels.pcm) { 697170161Sariff if (tmp == ch) 698170161Sariff break; 69965340Scg } 700107237Scg 701170161Sariff if (tmp != ch) 702170815Sariff return (EINVAL); 703170161Sariff 704170161Sariff CHN_REMOVE(d, ch, channels.pcm); 705170815Sariff 706170161Sariff switch (CHN_DEV(ch)) { 707170161Sariff case SND_DEV_DSPHW_PLAY: 708170161Sariff d->playcount--; 709170161Sariff break; 710170161Sariff case SND_DEV_DSPHW_VPLAY: 711170161Sariff d->pvchancount--; 712170161Sariff break; 713170161Sariff case SND_DEV_DSPHW_REC: 714107237Scg d->reccount--; 715170161Sariff break; 716170161Sariff case SND_DEV_DSPHW_VREC: 717170161Sariff d->rvchancount--; 718170161Sariff break; 719170161Sariff default: 720170161Sariff break; 721170161Sariff } 722107237Scg 723170815Sariff d->devcount--; 724170815Sariff 725170815Sariff return (0); 72665340Scg} 72765340Scg 72850724Scgint 72977269Scgpcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 73077269Scg{ 731157331Sariff struct snddev_info *d = device_get_softc(dev); 73282180Scg struct pcm_channel *ch; 733157331Sariff int err; 73477269Scg 735170815Sariff PCM_BUSYASSERT(d); 736170815Sariff 737170815Sariff pcm_lock(d); 738170161Sariff ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); 73977269Scg if (!ch) { 740170815Sariff device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", 741170815Sariff cls->name, dir, devinfo); 742170815Sariff pcm_unlock(d); 743170815Sariff return (ENODEV); 74477269Scg } 74578853Scg 746124740Smatk err = pcm_chn_add(d, ch); 747170815Sariff pcm_unlock(d); 74877269Scg if (err) { 749170815Sariff device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", 750170815Sariff ch->name, err); 75177269Scg pcm_chn_destroy(ch); 75277269Scg } 75377269Scg 754170815Sariff return (err); 75577269Scg} 75677269Scg 75777269Scgstatic int 75877269Scgpcm_killchan(device_t dev) 75977269Scg{ 760157331Sariff struct snddev_info *d = device_get_softc(dev); 761106420Scognet struct pcm_channel *ch; 762170815Sariff int error; 76377269Scg 764170815Sariff PCM_BUSYASSERT(d); 765170815Sariff 766170161Sariff ch = CHN_FIRST(d, channels.pcm); 76777269Scg 768170815Sariff pcm_lock(d); 769170161Sariff error = pcm_chn_remove(d, ch); 770170815Sariff pcm_unlock(d); 771106420Scognet if (error) 772106420Scognet return (error); 773106420Scognet return (pcm_chn_destroy(ch)); 77477269Scg} 77577269Scg 77677269Scgint 77750724Scgpcm_setstatus(device_t dev, char *str) 77850724Scg{ 779157331Sariff struct snddev_info *d = device_get_softc(dev); 78077882Scg 781170815Sariff PCM_BUSYASSERT(d); 782170815Sariff 783170815Sariff if (d->playcount == 0 || d->reccount == 0) 784170815Sariff d->flags |= SD_F_SIMPLEX; 785170815Sariff 786170815Sariff if ((d->playcount > 0 || d->reccount > 0) && 787170815Sariff !(d->flags & SD_F_AUTOVCHAN)) { 788170815Sariff d->flags |= SD_F_AUTOVCHAN; 789170815Sariff vchan_initsys(dev); 790170815Sariff } 791170815Sariff 792170161Sariff pcm_setmaxautovchans(d, snd_maxautovchans); 793170161Sariff 794170815Sariff strlcpy(d->status, str, SND_STATUSLEN); 795170815Sariff 796170161Sariff pcm_lock(d); 797170161Sariff 798170161Sariff /* Last stage, enable cloning. */ 799170815Sariff if (d->clones != NULL) 800170161Sariff (void)snd_clone_enable(d->clones); 801170161Sariff 802170815Sariff /* Done, we're ready.. */ 803170815Sariff d->flags |= SD_F_REGISTERED; 804170815Sariff 805170815Sariff PCM_RELEASE(d); 806170815Sariff 807170161Sariff pcm_unlock(d); 808170161Sariff 809170885Sariff if (snd_unit < 0 || snd_unit_auto != 0) 810170884Sariff snd_unit = device_get_unit(dev); 811170884Sariff 812170815Sariff return (0); 81350724Scg} 81450724Scg 815157331Sariffuint32_t 81650724Scgpcm_getflags(device_t dev) 81750724Scg{ 818157331Sariff struct snddev_info *d = device_get_softc(dev); 81977882Scg 82050724Scg return d->flags; 82150724Scg} 82250724Scg 82350724Scgvoid 824157331Sariffpcm_setflags(device_t dev, uint32_t val) 82550724Scg{ 826157331Sariff struct snddev_info *d = device_get_softc(dev); 82778362Scg 82850724Scg d->flags = val; 82950724Scg} 83050724Scg 83158384Scgvoid * 83258384Scgpcm_getdevinfo(device_t dev) 83358384Scg{ 834157331Sariff struct snddev_info *d = device_get_softc(dev); 83577882Scg 83658384Scg return d->devinfo; 83758384Scg} 83858384Scg 83983614Scgunsigned int 840160439Snetchildpcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 84183614Scg{ 842157331Sariff struct snddev_info *d = device_get_softc(dev); 84389690Scg int sz, x; 84483614Scg 84583614Scg sz = 0; 84689690Scg if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 84789690Scg x = sz; 848160439Snetchild RANGE(sz, minbufsz, maxbufsz); 84989690Scg if (x != sz) 850160439Snetchild device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 851160439Snetchild x = minbufsz; 85289690Scg while (x < sz) 85389690Scg x <<= 1; 85489690Scg if (x > sz) 85589690Scg x >>= 1; 85689690Scg if (x != sz) { 85789690Scg device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 85889690Scg sz = x; 85989690Scg } 86089690Scg } else { 86183614Scg sz = deflt; 86289690Scg } 86389690Scg 86483614Scg d->bufsz = sz; 86583614Scg 86683614Scg return sz; 86783614Scg} 86883614Scg 869170161Sariff#if defined(SND_DYNSYSCTL) && defined(SND_DEBUG) 870170161Sariffstatic int 871170161Sariffsysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS) 872170161Sariff{ 873170161Sariff struct snddev_info *d; 874170161Sariff uint32_t flags; 875170161Sariff int err; 876170161Sariff 877170161Sariff d = oidp->oid_arg1; 878170815Sariff if (!PCM_REGISTERED(d) || d->clones == NULL) 879170161Sariff return (ENODEV); 880170161Sariff 881170815Sariff PCM_ACQUIRE_QUICK(d); 882170815Sariff 883170161Sariff flags = snd_clone_getflags(d->clones); 884170289Sdwmalone err = sysctl_handle_int(oidp, &flags, 0, req); 885170161Sariff 886170161Sariff if (err == 0 && req->newptr != NULL) { 887170815Sariff if (flags & ~SND_CLONE_MASK) 888170161Sariff err = EINVAL; 889170815Sariff else 890170161Sariff (void)snd_clone_setflags(d->clones, flags); 891170161Sariff } 892170161Sariff 893170815Sariff PCM_RELEASE_QUICK(d); 894170815Sariff 895170161Sariff return (err); 896170161Sariff} 897170161Sariff 898170161Sariffstatic int 899170161Sariffsysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS) 900170161Sariff{ 901170161Sariff struct snddev_info *d; 902170161Sariff int err, deadline; 903170161Sariff 904170161Sariff d = oidp->oid_arg1; 905170815Sariff if (!PCM_REGISTERED(d) || d->clones == NULL) 906170161Sariff return (ENODEV); 907170161Sariff 908170815Sariff PCM_ACQUIRE_QUICK(d); 909170815Sariff 910170161Sariff deadline = snd_clone_getdeadline(d->clones); 911170289Sdwmalone err = sysctl_handle_int(oidp, &deadline, 0, req); 912170161Sariff 913170161Sariff if (err == 0 && req->newptr != NULL) { 914170161Sariff if (deadline < 0) 915170161Sariff err = EINVAL; 916170815Sariff else 917170161Sariff (void)snd_clone_setdeadline(d->clones, deadline); 918170161Sariff } 919170161Sariff 920170815Sariff PCM_RELEASE_QUICK(d); 921170815Sariff 922170161Sariff return (err); 923170161Sariff} 924170161Sariff 925170161Sariffstatic int 926170161Sariffsysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS) 927170161Sariff{ 928170161Sariff struct snddev_info *d; 929170161Sariff int err, val; 930170161Sariff 931170161Sariff d = oidp->oid_arg1; 932170815Sariff if (!PCM_REGISTERED(d) || d->clones == NULL) 933170161Sariff return (ENODEV); 934170161Sariff 935170161Sariff val = 0; 936170289Sdwmalone err = sysctl_handle_int(oidp, &val, 0, req); 937170161Sariff 938170161Sariff if (err == 0 && req->newptr != NULL && val != 0) { 939170815Sariff PCM_ACQUIRE_QUICK(d); 940170815Sariff val = snd_clone_gc(d->clones); 941170815Sariff PCM_RELEASE_QUICK(d); 942170815Sariff if (bootverbose != 0 || snd_verbose > 3) 943170815Sariff device_printf(d->dev, "clone gc: pruned=%d\n", val); 944170161Sariff } 945170161Sariff 946170161Sariff return (err); 947170161Sariff} 948170161Sariff 949170161Sariffstatic int 950170161Sariffsysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS) 951170161Sariff{ 952170161Sariff struct snddev_info *d; 953170161Sariff int i, err, val; 954170161Sariff 955170161Sariff val = 0; 956170289Sdwmalone err = sysctl_handle_int(oidp, &val, 0, req); 957170161Sariff 958170161Sariff if (err == 0 && req->newptr != NULL && val != 0) { 959170235Sariff for (i = 0; pcm_devclass != NULL && 960170235Sariff i < devclass_get_maxunit(pcm_devclass); i++) { 961170161Sariff d = devclass_get_softc(pcm_devclass, i); 962170815Sariff if (!PCM_REGISTERED(d) || d->clones == NULL) 963170161Sariff continue; 964170815Sariff PCM_ACQUIRE_QUICK(d); 965170815Sariff val = snd_clone_gc(d->clones); 966170815Sariff PCM_RELEASE_QUICK(d); 967170815Sariff if (bootverbose != 0 || snd_verbose > 3) 968170815Sariff device_printf(d->dev, "clone gc: pruned=%d\n", 969170815Sariff val); 970170161Sariff } 971170161Sariff } 972170161Sariff 973170161Sariff return (err); 974170161Sariff} 975170161SariffSYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, CTLTYPE_INT | CTLFLAG_RW, 976170161Sariff 0, sizeof(int), sysctl_hw_snd_clone_gc, "I", 977170161Sariff "global clone garbage collector"); 978170161Sariff#endif 979170161Sariff 98050724Scgint 98150724Scgpcm_register(device_t dev, void *devinfo, int numplay, int numrec) 98250724Scg{ 983170161Sariff struct snddev_info *d; 98450724Scg 98589834Scg if (pcm_veto_load) { 98689834Scg device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 98789834Scg 98889834Scg return EINVAL; 98989834Scg } 99089834Scg 991170161Sariff if (device_get_unit(dev) > PCMMAXUNIT) { 992170161Sariff device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n", 993170161Sariff device_get_unit(dev), PCMMAXUNIT); 994170161Sariff device_printf(dev, 995170161Sariff "Use 'hw.snd.maxunit' tunable to raise the limit.\n"); 996170161Sariff return ENODEV; 997170161Sariff } 998170161Sariff 999170161Sariff d = device_get_softc(dev); 1000170815Sariff d->dev = dev; 100193814Sjhb d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 1002170815Sariff cv_init(&d->cv, device_get_nameunit(dev)); 1003170815Sariff PCM_ACQUIRE_QUICK(d); 1004170815Sariff dsp_cdevinfo_init(d); 1005148587Snetchild#if 0 1006148587Snetchild /* 1007148587Snetchild * d->flags should be cleared by the allocator of the softc. 1008148587Snetchild * We cannot clear this field here because several devices set 1009148587Snetchild * this flag before calling pcm_register(). 1010148587Snetchild */ 101178853Scg d->flags = 0; 1012148587Snetchild#endif 101350724Scg d->devinfo = devinfo; 101478895Scg d->devcount = 0; 101583089Scg d->reccount = 0; 101683614Scg d->playcount = 0; 1017170161Sariff d->pvchancount = 0; 1018170161Sariff d->rvchancount = 0; 1019170161Sariff d->pvchanrate = 0; 1020170161Sariff d->pvchanformat = 0; 1021170161Sariff d->rvchanrate = 0; 1022170161Sariff d->rvchanformat = 0; 102378362Scg d->inprog = 0; 102450724Scg 1025170161Sariff /* 1026170161Sariff * Create clone manager, disabled by default. Cloning will be 1027170161Sariff * enabled during final stage of driver iniialization through 1028170161Sariff * pcm_setstatus(). 1029170161Sariff */ 1030170815Sariff d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE, 1031170815Sariff SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK | 1032170161Sariff SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF | 1033170161Sariff SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED); 1034124617Sphk 1035170161Sariff if (bootverbose != 0 || snd_verbose > 3) { 1036170161Sariff device_printf(dev, 1037170161Sariff "clone manager: deadline=%dms flags=0x%08x\n", 1038170161Sariff snd_clone_getdeadline(d->clones), 1039170161Sariff snd_clone_getflags(d->clones)); 1040170161Sariff } 1041170161Sariff 1042170161Sariff CHN_INIT(d, channels.pcm); 1043170161Sariff CHN_INIT(d, channels.pcm.busy); 1044170161Sariff 1045170815Sariff /* XXX This is incorrect, but lets play along for now. */ 1046157331Sariff if ((numplay == 0 || numrec == 0) && numplay != numrec) 104778214Scg d->flags |= SD_F_SIMPLEX; 104878362Scg 104978214Scg d->fakechan = fkchan_setup(dev); 1050126367Struckman chn_init(d->fakechan, NULL, 0, 0); 105155494Scg 105273127Scg#ifdef SND_DYNSYSCTL 1053170161Sariff sysctl_ctx_init(&d->play_sysctl_ctx); 1054170161Sariff d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 1055170161Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 1056170161Sariff CTLFLAG_RD, 0, "playback channels node"); 1057170161Sariff sysctl_ctx_init(&d->rec_sysctl_ctx); 1058170161Sariff d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 1059170161Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 1060170161Sariff CTLFLAG_RD, 0, "record channels node"); 1061159732Snetchild /* XXX: an user should be able to set this with a control tool, the 1062159732Snetchild sysadmin then needs min+max sysctls for this */ 1063164614Sariff SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 1064164614Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 1065164614Sariff OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 1066170161Sariff#ifdef SND_DEBUG 1067170161Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1068170161Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1069170161Sariff "clone_flags", CTLTYPE_UINT | CTLFLAG_RW, d, sizeof(d), 1070170161Sariff sysctl_dev_pcm_clone_flags, "IU", 1071170161Sariff "clone flags"); 1072170161Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1073170161Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1074170161Sariff "clone_deadline", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 1075170161Sariff sysctl_dev_pcm_clone_deadline, "I", 1076170161Sariff "clone expiration deadline (ms)"); 1077170161Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1078170161Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1079170161Sariff "clone_gc", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 1080170161Sariff sysctl_dev_pcm_clone_gc, "I", 1081170161Sariff "clone garbage collector"); 108273127Scg#endif 1083170161Sariff#endif 1084170815Sariff 1085170161Sariff if (numplay > 0 || numrec > 0) { 1086156929Sariff d->flags |= SD_F_AUTOVCHAN; 108782180Scg vchan_initsys(dev); 1088149953Snetchild } 108978853Scg 109082180Scg sndstat_register(dev, d->status, sndstat_prepare_pcm); 1091170815Sariff 1092157331Sariff return 0; 109350724Scg} 109450724Scg 109565340Scgint 109665340Scgpcm_unregister(device_t dev) 109765207Scg{ 1098170815Sariff struct snddev_info *d; 109983614Scg struct pcm_channel *ch; 1100170161Sariff struct thread *td; 1101170161Sariff int i; 110265207Scg 1103170161Sariff td = curthread; 1104170815Sariff d = device_get_softc(dev); 1105170161Sariff 1106170815Sariff if (!PCM_ALIVE(d)) { 1107170815Sariff device_printf(dev, "unregister: device not configured\n"); 1108170815Sariff return (0); 1109170815Sariff } 1110170815Sariff 1111170161Sariff if (sndstat_acquire(td) != 0) { 1112150827Snetchild device_printf(dev, "unregister: sndstat busy\n"); 1113170815Sariff return (EBUSY); 1114150827Snetchild } 1115150827Snetchild 1116170161Sariff pcm_lock(d); 1117170815Sariff PCM_WAIT(d); 1118170815Sariff 1119170815Sariff if (d->inprog != 0) { 112083476Sgreid device_printf(dev, "unregister: operation in progress\n"); 1121170161Sariff pcm_unlock(d); 1122170161Sariff sndstat_release(td); 1123170815Sariff return (EBUSY); 112478362Scg } 1125124740Smatk 1126170815Sariff PCM_ACQUIRE(d); 1127170815Sariff pcm_unlock(d); 1128170815Sariff 1129170161Sariff CHN_FOREACH(ch, d, channels.pcm) { 1130170815Sariff CHN_LOCK(ch); 113183614Scg if (ch->refcount > 0) { 1132170815Sariff device_printf(dev, 1133170815Sariff "unregister: channel %s busy (pid %d)\n", 1134170815Sariff ch->name, ch->pid); 1135170815Sariff CHN_UNLOCK(ch); 1136170815Sariff PCM_RELEASE_QUICK(d); 1137170161Sariff sndstat_release(td); 1138170815Sariff return (EBUSY); 113977269Scg } 1140170815Sariff CHN_UNLOCK(ch); 114165487Scg } 1142124740Smatk 1143170161Sariff if (d->clones != NULL) { 1144170161Sariff if (snd_clone_busy(d->clones) != 0) { 1145170161Sariff device_printf(dev, "unregister: clone busy\n"); 1146170815Sariff PCM_RELEASE_QUICK(d); 1147170161Sariff sndstat_release(td); 1148170815Sariff return (EBUSY); 1149170815Sariff } else { 1150170815Sariff pcm_lock(d); 1151170161Sariff (void)snd_clone_disable(d->clones); 1152170815Sariff pcm_unlock(d); 1153170815Sariff } 1154170161Sariff } 1155170161Sariff 1156157022Sariff if (mixer_uninit(dev) == EBUSY) { 1157148587Snetchild device_printf(dev, "unregister: mixer busy\n"); 1158170815Sariff pcm_lock(d); 1159170161Sariff if (d->clones != NULL) 1160170161Sariff (void)snd_clone_enable(d->clones); 1161170815Sariff PCM_RELEASE(d); 1162170161Sariff pcm_unlock(d); 1163170161Sariff sndstat_release(td); 1164170815Sariff return (EBUSY); 1165148587Snetchild } 1166148587Snetchild 1167170815Sariff pcm_lock(d); 1168170815Sariff d->flags |= SD_F_DYING; 1169170815Sariff d->flags &= ~SD_F_REGISTERED; 1170170815Sariff pcm_unlock(d); 1171170815Sariff 1172170815Sariff /* 1173170815Sariff * No lock being held, so this thing can be flushed without 1174170815Sariff * stucking into devdrn oblivion. 1175170815Sariff */ 1176170161Sariff if (d->clones != NULL) { 1177170161Sariff snd_clone_destroy(d->clones); 1178170161Sariff d->clones = NULL; 1179124740Smatk } 1180124740Smatk 118174763Scg#ifdef SND_DYNSYSCTL 1182170161Sariff if (d->play_sysctl_tree != NULL) { 1183170161Sariff sysctl_ctx_free(&d->play_sysctl_ctx); 1184170161Sariff d->play_sysctl_tree = NULL; 1185170161Sariff } 1186170161Sariff if (d->rec_sysctl_tree != NULL) { 1187170161Sariff sysctl_ctx_free(&d->rec_sysctl_ctx); 1188170161Sariff d->rec_sysctl_tree = NULL; 1189170161Sariff } 119074763Scg#endif 1191157331Sariff 1192170161Sariff while (!CHN_EMPTY(d, channels.pcm)) 119377269Scg pcm_killchan(dev); 119465207Scg 119574763Scg chn_kill(d->fakechan); 119674763Scg fkchan_kill(d->fakechan); 119773127Scg 1198170815Sariff dsp_cdevinfo_flush(d); 1199170815Sariff 1200170815Sariff pcm_lock(d); 1201170815Sariff PCM_RELEASE(d); 1202170815Sariff cv_destroy(&d->cv); 1203170161Sariff pcm_unlock(d); 120477882Scg snd_mtxfree(d->lock); 1205150827Snetchild sndstat_unregister(dev); 1206170161Sariff sndstat_release(td); 1207170161Sariff 1208170161Sariff if (snd_unit == device_get_unit(dev)) { 1209170161Sariff /* 1210170884Sariff * Reassign default unit to the next available dev, but 1211170884Sariff * first, reset snd_unit to something ridiculous. 1212170161Sariff */ 1213170884Sariff snd_unit = -1; 1214170235Sariff for (i = 0; pcm_devclass != NULL && 1215170235Sariff i < devclass_get_maxunit(pcm_devclass); i++) { 1216170815Sariff if (device_get_unit(dev) == i) 1217170161Sariff continue; 1218170815Sariff d = devclass_get_softc(pcm_devclass, i); 1219170815Sariff if (PCM_REGISTERED(d)) { 1220170815Sariff snd_unit = i; 1221170815Sariff break; 1222170815Sariff } 1223170161Sariff } 1224170161Sariff } 1225170161Sariff 1226170815Sariff return (0); 122765207Scg} 122865207Scg 122982180Scg/************************************************************************/ 123082180Scg 123182180Scgstatic int 123282180Scgsndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 123382180Scg{ 1234157331Sariff struct snddev_info *d; 123582180Scg struct pcm_channel *c; 123682180Scg struct pcm_feeder *f; 123782180Scg 123882180Scg if (verbose < 1) 123982180Scg return 0; 124082180Scg 124182180Scg d = device_get_softc(dev); 124282180Scg if (!d) 124382180Scg return ENXIO; 124482180Scg 1245170815Sariff PCM_BUSYASSERT(d); 1246170815Sariff 1247170815Sariff if (CHN_EMPTY(d, channels.pcm)) { 1248170815Sariff sbuf_printf(s, " (mixer only)"); 1249170815Sariff return 0; 1250170815Sariff } 1251170815Sariff 1252170815Sariff sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", 1253170815Sariff d->playcount, d->pvchancount, 1254170815Sariff d->reccount, d->rvchancount, 1255170815Sariff (d->flags & SD_F_SIMPLEX)? "" : " duplex", 125682180Scg#ifdef USING_DEVFS 1257170815Sariff (device_get_unit(dev) == snd_unit)? " default" : "" 125882180Scg#else 1259170815Sariff "" 126082180Scg#endif 1261170815Sariff ); 1262119096Scg 1263170815Sariff if (verbose <= 1) 1264170815Sariff return 0; 1265119096Scg 1266170815Sariff CHN_FOREACH(c, d, channels.pcm) { 1267155342Snetchild 1268170815Sariff KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 1269170815Sariff ("hosed pcm channel setup")); 1270155342Snetchild 1271170815Sariff sbuf_printf(s, "\n\t"); 127282479Scg 1273170815Sariff /* it would be better to indent child channels */ 1274170815Sariff sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); 1275170815Sariff sbuf_printf(s, "spd %d", c->speed); 1276170815Sariff if (c->speed != sndbuf_getspd(c->bufhard)) 1277170815Sariff sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); 1278170815Sariff sbuf_printf(s, ", fmt 0x%08x", c->format); 1279170815Sariff if (c->format != sndbuf_getfmt(c->bufhard)) 1280170815Sariff sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); 1281170815Sariff sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); 1282170815Sariff if (c->pid != -1) 1283170815Sariff sbuf_printf(s, ", pid %d", c->pid); 1284170815Sariff sbuf_printf(s, "\n\t"); 128589691Scg 1286170815Sariff sbuf_printf(s, "interrupts %d, ", c->interrupts); 1287170815Sariff if (c->direction == PCMDIR_REC) 1288170815Sariff sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 1289170815Sariff c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), 1290170815Sariff sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1291170815Sariff sndbuf_getblkcnt(c->bufhard), 1292170815Sariff sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1293170815Sariff sndbuf_getblkcnt(c->bufsoft)); 1294170815Sariff else 1295170815Sariff sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", 1296170815Sariff c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), 1297170815Sariff sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), 1298170815Sariff sndbuf_getblkcnt(c->bufhard), 1299170815Sariff sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), 1300170815Sariff sndbuf_getblkcnt(c->bufsoft)); 1301170815Sariff sbuf_printf(s, "\n\t"); 130289691Scg 1303170815Sariff sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); 1304170815Sariff sbuf_printf(s, " -> "); 1305170815Sariff f = c->feeder; 1306170815Sariff while (f->source != NULL) 1307170815Sariff f = f->source; 1308170815Sariff while (f != NULL) { 1309170815Sariff sbuf_printf(s, "%s", f->class->name); 1310170815Sariff if (f->desc->type == FEEDER_FMT) 1311170815Sariff sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); 1312170815Sariff if (f->desc->type == FEEDER_RATE) 1313170815Sariff sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); 1314170815Sariff if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || 1315170815Sariff f->desc->type == FEEDER_VOLUME) 1316170815Sariff sbuf_printf(s, "(0x%08x)", f->desc->out); 131789691Scg sbuf_printf(s, " -> "); 1318170815Sariff f = f->parent; 131982180Scg } 1320170815Sariff sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); 1321170815Sariff } 132282180Scg 132382180Scg return 0; 132482180Scg} 132582180Scg 132682180Scg/************************************************************************/ 132782180Scg 132882180Scg#ifdef SND_DYNSYSCTL 132982180Scgint 133082180Scgsysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) 133182180Scg{ 133282180Scg struct snddev_info *d; 1333170161Sariff int direction, vchancount; 1334170815Sariff int err, cnt; 133582180Scg 1336170161Sariff d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); 1337170815Sariff if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) 1338170815Sariff return (EINVAL); 133982180Scg 1340170815Sariff pcm_lock(d); 1341170815Sariff PCM_WAIT(d); 1342170815Sariff 1343170161Sariff switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { 1344170161Sariff case VCHAN_PLAY: 1345170161Sariff direction = PCMDIR_PLAY; 1346170161Sariff vchancount = d->pvchancount; 1347170815Sariff cnt = d->playcount; 1348170161Sariff break; 1349170161Sariff case VCHAN_REC: 1350170161Sariff direction = PCMDIR_REC; 1351170161Sariff vchancount = d->rvchancount; 1352170815Sariff cnt = d->reccount; 1353170161Sariff break; 1354170161Sariff default: 1355170815Sariff pcm_unlock(d); 1356170815Sariff return (EINVAL); 1357170161Sariff break; 1358170161Sariff } 1359170161Sariff 1360170815Sariff if (cnt < 1) { 1361170815Sariff pcm_unlock(d); 1362170815Sariff return (ENODEV); 1363170815Sariff } 1364119096Scg 1365170815Sariff PCM_ACQUIRE(d); 1366170815Sariff pcm_unlock(d); 1367170815Sariff 1368170815Sariff cnt = vchancount; 1369170815Sariff err = sysctl_handle_int(oidp, &cnt, 0, req); 1370170815Sariff 1371170815Sariff if (err == 0 && req->newptr != NULL && vchancount != cnt) { 1372170815Sariff if (cnt < 0) 1373170815Sariff cnt = 0; 1374170815Sariff if (cnt > SND_MAXVCHANS) 1375170815Sariff cnt = SND_MAXVCHANS; 1376170815Sariff err = pcm_setvchans(d, direction, cnt, -1); 1377170161Sariff } 1378119096Scg 1379170815Sariff PCM_RELEASE_QUICK(d); 1380170815Sariff 138182180Scg return err; 138282180Scg} 138382180Scg#endif 138482180Scg 138582180Scg/************************************************************************/ 138682180Scg 1387162588Snetchild/** 1388162588Snetchild * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 1389162588Snetchild * 1390162588Snetchild * @param si Pointer to oss_sysinfo struct where information about the 1391162588Snetchild * sound subsystem will be written/copied. 1392162588Snetchild * 1393162588Snetchild * This routine returns information about the sound system, such as the 1394162588Snetchild * current OSS version, number of audio, MIDI, and mixer drivers, etc. 1395162588Snetchild * Also includes a bitmask showing which of the above types of devices 1396162588Snetchild * are open (busy). 1397162588Snetchild * 1398162588Snetchild * @note 1399162588Snetchild * Calling threads must not hold any snddev_info or pcm_channel locks. 1400162588Snetchild * 1401162588Snetchild * @author Ryan Beasley <ryanb@FreeBSD.org> 1402162588Snetchild */ 1403162588Snetchildvoid 1404162588Snetchildsound_oss_sysinfo(oss_sysinfo *si) 1405162588Snetchild{ 1406162588Snetchild static char si_product[] = "FreeBSD native OSS ABI"; 1407162588Snetchild static char si_version[] = __XSTRING(__FreeBSD_version); 1408162588Snetchild static int intnbits = sizeof(int) * 8; /* Better suited as macro? 1409162588Snetchild Must pester a C guru. */ 1410162588Snetchild 1411162588Snetchild struct snddev_info *d; 1412162588Snetchild struct pcm_channel *c; 1413162588Snetchild int i, j, ncards; 1414162588Snetchild 1415162588Snetchild ncards = 0; 1416162588Snetchild 1417162588Snetchild strlcpy(si->product, si_product, sizeof(si->product)); 1418162588Snetchild strlcpy(si->version, si_version, sizeof(si->version)); 1419162588Snetchild si->versionnum = SOUND_VERSION; 1420162588Snetchild 1421162588Snetchild /* 1422162588Snetchild * Iterate over PCM devices and their channels, gathering up data 1423162588Snetchild * for the numaudios, ncards, and openedaudio fields. 1424162588Snetchild */ 1425162588Snetchild si->numaudios = 0; 1426162588Snetchild bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 1427162588Snetchild 1428170815Sariff j = 0; 1429162588Snetchild 1430170815Sariff for (i = 0; pcm_devclass != NULL && 1431170815Sariff i < devclass_get_maxunit(pcm_devclass); i++) { 1432170815Sariff d = devclass_get_softc(pcm_devclass, i); 1433170815Sariff if (!PCM_REGISTERED(d)) 1434170815Sariff continue; 1435162588Snetchild 1436170815Sariff /* XXX Need Giant magic entry ??? */ 1437162588Snetchild 1438170815Sariff /* See note in function's docblock */ 1439170815Sariff mtx_assert(d->lock, MA_NOTOWNED); 1440170815Sariff pcm_lock(d); 1441162588Snetchild 1442170815Sariff si->numaudios += d->devcount; 1443170815Sariff ++ncards; 1444162588Snetchild 1445170815Sariff CHN_FOREACH(c, d, channels.pcm) { 1446170815Sariff mtx_assert(c->lock, MA_NOTOWNED); 1447170815Sariff CHN_LOCK(c); 1448170815Sariff if (c->flags & CHN_F_BUSY) 1449170815Sariff si->openedaudio[j / intnbits] |= 1450170815Sariff (1 << (j % intnbits)); 1451170815Sariff CHN_UNLOCK(c); 1452170815Sariff j++; 1453162588Snetchild } 1454170815Sariff 1455170815Sariff pcm_unlock(d); 1456162588Snetchild } 1457162588Snetchild 1458162588Snetchild si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 1459162588Snetchild /** 1460162588Snetchild * @todo Collect num{midis,timers}. 1461162588Snetchild * 1462162588Snetchild * Need access to sound/midi/midi.c::midistat_lock in order 1463162588Snetchild * to safely touch midi_devices and get a head count of, well, 1464162588Snetchild * MIDI devices. midistat_lock is a global static (i.e., local to 1465162588Snetchild * midi.c), but midi_devices is a regular global; should the mutex 1466162588Snetchild * be publicized, or is there another way to get this information? 1467162588Snetchild * 1468162588Snetchild * NB: MIDI/sequencer stuff is currently on hold. 1469162588Snetchild */ 1470162588Snetchild si->nummidis = 0; 1471162588Snetchild si->numtimers = 0; 1472162588Snetchild si->nummixers = mixer_count; 1473162588Snetchild si->numcards = ncards; 1474162588Snetchild /* OSSv4 docs: Intended only for test apps; API doesn't 1475162588Snetchild really have much of a concept of cards. Shouldn't be 1476162588Snetchild used by applications. */ 1477162588Snetchild 1478162588Snetchild /** 1479162588Snetchild * @todo Fill in "busy devices" fields. 1480162588Snetchild * 1481162588Snetchild * si->openedmidi = " MIDI devices 1482162588Snetchild */ 1483162588Snetchild bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 1484162588Snetchild 1485162588Snetchild /* 1486162588Snetchild * Si->filler is a reserved array, but according to docs each 1487162588Snetchild * element should be set to -1. 1488162588Snetchild */ 1489162588Snetchild for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++) 1490162588Snetchild si->filler[i] = -1; 1491162588Snetchild} 1492162588Snetchild 1493162588Snetchild/************************************************************************/ 1494162588Snetchild 1495132236Stanimurastatic int 1496132236Stanimurasound_modevent(module_t mod, int type, void *data) 1497132236Stanimura{ 1498162588Snetchild int ret; 1499148587Snetchild#if 0 1500132236Stanimura return (midi_modevent(mod, type, data)); 1501148587Snetchild#else 1502162588Snetchild ret = 0; 1503162588Snetchild 1504162588Snetchild switch(type) { 1505162588Snetchild case MOD_LOAD: 1506162588Snetchild pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 1507162588Snetchild break; 1508162588Snetchild case MOD_UNLOAD: 1509162588Snetchild case MOD_SHUTDOWN: 1510170161Sariff ret = sndstat_acquire(curthread); 1511170161Sariff if (ret != 0) 1512170161Sariff break; 1513162588Snetchild if (pcmsg_unrhdr != NULL) { 1514162588Snetchild delete_unrhdr(pcmsg_unrhdr); 1515162588Snetchild pcmsg_unrhdr = NULL; 1516162588Snetchild } 1517162588Snetchild break; 1518162588Snetchild default: 1519162588Snetchild ret = EOPNOTSUPP; 1520162588Snetchild } 1521162588Snetchild 1522162588Snetchild return ret; 1523148587Snetchild#endif 1524132236Stanimura} 1525132236Stanimura 1526132236StanimuraDEV_MODULE(sound, sound_modevent, NULL); 1527132236StanimuraMODULE_VERSION(sound, SOUND_MODVER); 1528