1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2005-2009 Ariff Abdullah <ariff@FreeBSD.org> 5 * Portions Copyright (c) Ryan Beasley <ryan.beasley@gmail.com> - GSoC 2006 6 * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> 7 * Copyright (c) 1997 Luigi Rizzo 8 * All rights reserved. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #ifdef HAVE_KERNEL_OPTION_HEADERS 33 #include "opt_snd.h" 34 #endif 35 36 #include <dev/sound/pcm/sound.h> 37 #include <dev/sound/pcm/ac97.h> 38 #include <dev/sound/pcm/vchan.h> 39 #include <dev/sound/pcm/dsp.h> 40 #include <dev/sound/version.h> 41 #include <sys/limits.h> 42 #include <sys/sysctl.h> 43 44 #include "feeder_if.h" 45 46 devclass_t pcm_devclass; 47 48 int pcm_veto_load = 1; 49 50 int snd_unit = -1; 51 52 static int snd_unit_auto = -1; 53 SYSCTL_INT(_hw_snd, OID_AUTO, default_auto, CTLFLAG_RWTUN, 54 &snd_unit_auto, 0, "assign default unit to a newly attached device"); 55 56 int snd_maxautovchans = 16; 57 58 SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 59 "Sound driver"); 60 61 static void pcm_sysinit(device_t); 62 63 /* 64 * XXX I've had enough with people not telling proper version/arch 65 * while reporting problems, not after 387397913213th questions/requests. 66 */ 67 static char snd_driver_version[] = 68 __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; 69 SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, 70 0, "driver version/arch"); 71 72 /** 73 * @brief Unit number allocator for syncgroup IDs 74 */ 75 struct unrhdr *pcmsg_unrhdr = NULL; 76 77 static int 78 sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) 79 { 80 struct snddev_info *d; 81 struct pcm_channel *c; 82 struct pcm_feeder *f; 83 84 d = device_get_softc(dev); 85 PCM_BUSYASSERT(d); 86 87 if (CHN_EMPTY(d, channels.pcm)) { 88 sbuf_printf(s, " (mixer only)"); 89 return (0); 90 } 91 92 if (verbose < 1) { 93 sbuf_printf(s, " (%s%s%s", 94 d->playcount ? "play" : "", 95 (d->playcount && d->reccount) ? "/" : "", 96 d->reccount ? "rec" : ""); 97 } else { 98 sbuf_printf(s, " (%dp:%dv/%dr:%dv", 99 d->playcount, d->pvchancount, 100 d->reccount, d->rvchancount); 101 } 102 sbuf_printf(s, "%s)%s", 103 ((d->playcount != 0 && d->reccount != 0) && 104 (d->flags & SD_F_SIMPLEX)) ? " simplex" : "", 105 (device_get_unit(dev) == snd_unit) ? " default" : ""); 106 107 if (verbose <= 1) 108 return (0); 109 110 sbuf_printf(s, "\n\t"); 111 sbuf_printf(s, "snddev flags=0x%b", d->flags, SD_F_BITS); 112 113 CHN_FOREACH(c, d, channels.pcm) { 114 KASSERT(c->bufhard != NULL && c->bufsoft != NULL, 115 ("hosed pcm channel setup")); 116 117 sbuf_printf(s, "\n\t"); 118 119 sbuf_printf(s, "%s[%s]: ", 120 (c->parentchannel != NULL) ? 121 c->parentchannel->name : "", c->name); 122 sbuf_printf(s, "spd %d", c->speed); 123 if (c->speed != sndbuf_getspd(c->bufhard)) { 124 sbuf_printf(s, "/%d", 125 sndbuf_getspd(c->bufhard)); 126 } 127 sbuf_printf(s, ", fmt 0x%08x", c->format); 128 if (c->format != sndbuf_getfmt(c->bufhard)) { 129 sbuf_printf(s, "/0x%08x", 130 sndbuf_getfmt(c->bufhard)); 131 } 132 sbuf_printf(s, ", flags 0x%08x, 0x%08x", 133 c->flags, c->feederflags); 134 if (c->pid != -1) { 135 sbuf_printf(s, ", pid %d (%s)", 136 c->pid, c->comm); 137 } 138 sbuf_printf(s, "\n\t"); 139 140 sbuf_printf(s, "interrupts %d, ", c->interrupts); 141 142 if (c->direction == PCMDIR_REC) { 143 sbuf_printf(s, 144 "overruns %d, feed %u, hfree %d, " 145 "sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", 146 c->xruns, c->feedcount, 147 sndbuf_getfree(c->bufhard), 148 sndbuf_getfree(c->bufsoft), 149 sndbuf_getsize(c->bufhard), 150 sndbuf_getblksz(c->bufhard), 151 sndbuf_getblkcnt(c->bufhard), 152 sndbuf_getsize(c->bufsoft), 153 sndbuf_getblksz(c->bufsoft), 154 sndbuf_getblkcnt(c->bufsoft)); 155 } else { 156 sbuf_printf(s, 157 "underruns %d, feed %u, ready %d " 158 "[b:%d/%d/%d|bs:%d/%d/%d]", 159 c->xruns, c->feedcount, 160 sndbuf_getready(c->bufsoft), 161 sndbuf_getsize(c->bufhard), 162 sndbuf_getblksz(c->bufhard), 163 sndbuf_getblkcnt(c->bufhard), 164 sndbuf_getsize(c->bufsoft), 165 sndbuf_getblksz(c->bufsoft), 166 sndbuf_getblkcnt(c->bufsoft)); 167 } 168 sbuf_printf(s, "\n\t"); 169 170 sbuf_printf(s, "channel flags=0x%b", c->flags, CHN_F_BITS); 171 sbuf_printf(s, "\n\t"); 172 173 sbuf_printf(s, "{%s}", 174 (c->direction == PCMDIR_REC) ? "hardware" : "userland"); 175 sbuf_printf(s, " -> "); 176 f = c->feeder; 177 while (f->source != NULL) 178 f = f->source; 179 while (f != NULL) { 180 sbuf_printf(s, "%s", f->class->name); 181 if (f->desc->type == FEEDER_FORMAT) { 182 sbuf_printf(s, "(0x%08x -> 0x%08x)", 183 f->desc->in, f->desc->out); 184 } else if (f->desc->type == FEEDER_MATRIX) { 185 sbuf_printf(s, "(%d.%d -> %d.%d)", 186 AFMT_CHANNEL(f->desc->in) - 187 AFMT_EXTCHANNEL(f->desc->in), 188 AFMT_EXTCHANNEL(f->desc->in), 189 AFMT_CHANNEL(f->desc->out) - 190 AFMT_EXTCHANNEL(f->desc->out), 191 AFMT_EXTCHANNEL(f->desc->out)); 192 } else if (f->desc->type == FEEDER_RATE) { 193 sbuf_printf(s, 194 "(0x%08x q:%d %d -> %d)", 195 f->desc->out, 196 FEEDER_GET(f, FEEDRATE_QUALITY), 197 FEEDER_GET(f, FEEDRATE_SRC), 198 FEEDER_GET(f, FEEDRATE_DST)); 199 } else { 200 sbuf_printf(s, "(0x%08x)", 201 f->desc->out); 202 } 203 sbuf_printf(s, " -> "); 204 f = f->parent; 205 } 206 sbuf_printf(s, "{%s}", 207 (c->direction == PCMDIR_REC) ? "userland" : "hardware"); 208 } 209 210 return (0); 211 } 212 213 void * 214 snd_mtxcreate(const char *desc, const char *type) 215 { 216 struct mtx *m; 217 218 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 219 mtx_init(m, desc, type, MTX_DEF); 220 return m; 221 } 222 223 void 224 snd_mtxfree(void *m) 225 { 226 struct mtx *mtx = m; 227 228 mtx_destroy(mtx); 229 free(mtx, M_DEVBUF); 230 } 231 232 void 233 snd_mtxassert(void *m) 234 { 235 #ifdef INVARIANTS 236 struct mtx *mtx = m; 237 238 mtx_assert(mtx, MA_OWNED); 239 #endif 240 } 241 242 int 243 snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 244 { 245 struct snddev_info *d; 246 247 flags &= INTR_MPSAFE; 248 flags |= INTR_TYPE_AV; 249 d = device_get_softc(dev); 250 if (d != NULL && (flags & INTR_MPSAFE)) 251 d->flags |= SD_F_MPSAFE; 252 253 return bus_setup_intr(dev, res, flags, NULL, hand, param, cookiep); 254 } 255 256 static void 257 pcm_clonereset(struct snddev_info *d) 258 { 259 int cmax; 260 261 PCM_BUSYASSERT(d); 262 263 cmax = d->playcount + d->reccount - 1; 264 if (d->pvchancount > 0) 265 cmax += max(d->pvchancount, snd_maxautovchans) - 1; 266 if (d->rvchancount > 0) 267 cmax += max(d->rvchancount, snd_maxautovchans) - 1; 268 if (cmax > PCMMAXCLONE) 269 cmax = PCMMAXCLONE; 270 (void)snd_clone_gc(d->clones); 271 (void)snd_clone_setmaxunit(d->clones, cmax); 272 } 273 274 int 275 pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) 276 { 277 struct pcm_channel *c, *ch, *nch; 278 struct pcmchan_caps *caps; 279 int i, err, vcnt; 280 281 PCM_BUSYASSERT(d); 282 283 if ((direction == PCMDIR_PLAY && d->playcount < 1) || 284 (direction == PCMDIR_REC && d->reccount < 1)) 285 return (ENODEV); 286 287 if (!(d->flags & SD_F_AUTOVCHAN)) 288 return (EINVAL); 289 290 if (newcnt < 0 || newcnt > SND_MAXVCHANS) 291 return (E2BIG); 292 293 if (direction == PCMDIR_PLAY) 294 vcnt = d->pvchancount; 295 else if (direction == PCMDIR_REC) 296 vcnt = d->rvchancount; 297 else 298 return (EINVAL); 299 300 if (newcnt > vcnt) { 301 KASSERT(num == -1 || 302 (num >= 0 && num < SND_MAXVCHANS && (newcnt - 1) == vcnt), 303 ("bogus vchan_create() request num=%d newcnt=%d vcnt=%d", 304 num, newcnt, vcnt)); 305 /* add new vchans - find a parent channel first */ 306 ch = NULL; 307 CHN_FOREACH(c, d, channels.pcm) { 308 CHN_LOCK(c); 309 if (c->direction == direction && 310 ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && 311 c->refcount < 1 && 312 !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { 313 /* 314 * Reuse hw channel with vchans already 315 * created. 316 */ 317 if (c->flags & CHN_F_HAS_VCHAN) { 318 ch = c; 319 break; 320 } 321 /* 322 * No vchans ever created, look for 323 * channels with supported formats. 324 */ 325 caps = chn_getcaps(c); 326 if (caps == NULL) { 327 CHN_UNLOCK(c); 328 continue; 329 } 330 for (i = 0; caps->fmtlist[i] != 0; i++) { 331 if (caps->fmtlist[i] & AFMT_CONVERTIBLE) 332 break; 333 } 334 if (caps->fmtlist[i] != 0) { 335 ch = c; 336 break; 337 } 338 } 339 CHN_UNLOCK(c); 340 } 341 if (ch == NULL) 342 return (EBUSY); 343 ch->flags |= CHN_F_BUSY; 344 err = 0; 345 while (err == 0 && newcnt > vcnt) { 346 err = vchan_create(ch, num); 347 if (err == 0) 348 vcnt++; 349 else if (err == E2BIG && newcnt > vcnt) 350 device_printf(d->dev, 351 "%s: err=%d Maximum channel reached.\n", 352 __func__, err); 353 } 354 if (vcnt == 0) 355 ch->flags &= ~CHN_F_BUSY; 356 CHN_UNLOCK(ch); 357 if (err != 0) 358 return (err); 359 else 360 pcm_clonereset(d); 361 } else if (newcnt < vcnt) { 362 KASSERT(num == -1, 363 ("bogus vchan_destroy() request num=%d", num)); 364 CHN_FOREACH(c, d, channels.pcm) { 365 CHN_LOCK(c); 366 if (c->direction != direction || 367 CHN_EMPTY(c, children) || 368 !(c->flags & CHN_F_HAS_VCHAN)) { 369 CHN_UNLOCK(c); 370 continue; 371 } 372 CHN_FOREACH_SAFE(ch, c, nch, children) { 373 CHN_LOCK(ch); 374 if (vcnt == 1 && c->refcount > 0) { 375 CHN_UNLOCK(ch); 376 break; 377 } 378 if (!(ch->flags & CHN_F_BUSY) && 379 ch->refcount < 1) { 380 err = vchan_destroy(ch); 381 if (err == 0) 382 vcnt--; 383 } else 384 CHN_UNLOCK(ch); 385 if (vcnt == newcnt) 386 break; 387 } 388 CHN_UNLOCK(c); 389 break; 390 } 391 pcm_clonereset(d); 392 } 393 394 return (0); 395 } 396 397 /* return error status and a locked channel */ 398 int 399 pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, 400 pid_t pid, char *comm, int devunit) 401 { 402 struct pcm_channel *c; 403 int err, vchancount, vchan_num; 404 405 KASSERT(d != NULL && ch != NULL && (devunit == -1 || 406 !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && 407 (direction == PCMDIR_PLAY || direction == PCMDIR_REC), 408 ("%s(): invalid d=%p ch=%p direction=%d pid=%d devunit=%d", 409 __func__, d, ch, direction, pid, devunit)); 410 PCM_BUSYASSERT(d); 411 412 /* Double check again. */ 413 if (devunit != -1) { 414 switch (snd_unit2d(devunit)) { 415 case SND_DEV_DSPHW_PLAY: 416 case SND_DEV_DSPHW_VPLAY: 417 if (direction != PCMDIR_PLAY) 418 return (ENOTSUP); 419 break; 420 case SND_DEV_DSPHW_REC: 421 case SND_DEV_DSPHW_VREC: 422 if (direction != PCMDIR_REC) 423 return (ENOTSUP); 424 break; 425 default: 426 if (!(direction == PCMDIR_PLAY || 427 direction == PCMDIR_REC)) 428 return (ENOTSUP); 429 break; 430 } 431 } 432 433 *ch = NULL; 434 vchan_num = 0; 435 vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : 436 d->rvchancount; 437 438 retry_chnalloc: 439 err = ENOTSUP; 440 /* scan for a free channel */ 441 CHN_FOREACH(c, d, channels.pcm) { 442 CHN_LOCK(c); 443 if (devunit == -1 && c->direction == direction && 444 (c->flags & CHN_F_VIRTUAL)) { 445 if (vchancount < snd_maxautovchans && 446 vchan_num < CHN_CHAN(c)) { 447 CHN_UNLOCK(c); 448 goto vchan_alloc; 449 } 450 vchan_num++; 451 } 452 if (c->direction == direction && !(c->flags & CHN_F_BUSY) && 453 (devunit == -1 || devunit == -2 || c->unit == devunit)) { 454 c->flags |= CHN_F_BUSY; 455 c->pid = pid; 456 strlcpy(c->comm, (comm != NULL) ? comm : 457 CHN_COMM_UNKNOWN, sizeof(c->comm)); 458 *ch = c; 459 return (0); 460 } else if (c->unit == devunit) { 461 if (c->direction != direction) 462 err = ENOTSUP; 463 else if (c->flags & CHN_F_BUSY) 464 err = EBUSY; 465 else 466 err = EINVAL; 467 CHN_UNLOCK(c); 468 return (err); 469 } else if ((devunit == -1 || devunit == -2) && 470 c->direction == direction && (c->flags & CHN_F_BUSY)) 471 err = EBUSY; 472 CHN_UNLOCK(c); 473 } 474 475 if (devunit == -2) 476 return (err); 477 478 vchan_alloc: 479 /* no channel available */ 480 if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || 481 snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { 482 if (!(vchancount > 0 && vchancount < snd_maxautovchans) && 483 (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) 484 return (err); 485 err = pcm_setvchans(d, direction, vchancount + 1, 486 (devunit == -1) ? -1 : snd_unit2c(devunit)); 487 if (err == 0) { 488 if (devunit == -1) 489 devunit = -2; 490 goto retry_chnalloc; 491 } 492 } 493 494 return (err); 495 } 496 497 /* release a locked channel and unlock it */ 498 int 499 pcm_chnrelease(struct pcm_channel *c) 500 { 501 PCM_BUSYASSERT(c->parentsnddev); 502 CHN_LOCKASSERT(c); 503 504 c->flags &= ~CHN_F_BUSY; 505 c->pid = -1; 506 strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm)); 507 CHN_UNLOCK(c); 508 509 return (0); 510 } 511 512 int 513 pcm_chnref(struct pcm_channel *c, int ref) 514 { 515 PCM_BUSYASSERT(c->parentsnddev); 516 CHN_LOCKASSERT(c); 517 518 c->refcount += ref; 519 520 return (c->refcount); 521 } 522 523 static void 524 pcm_setmaxautovchans(struct snddev_info *d, int num) 525 { 526 PCM_BUSYASSERT(d); 527 528 if (num < 0) 529 return; 530 531 if (num >= 0 && d->pvchancount > num) 532 (void)pcm_setvchans(d, PCMDIR_PLAY, num, -1); 533 else if (num > 0 && d->pvchancount == 0) 534 (void)pcm_setvchans(d, PCMDIR_PLAY, 1, -1); 535 536 if (num >= 0 && d->rvchancount > num) 537 (void)pcm_setvchans(d, PCMDIR_REC, num, -1); 538 else if (num > 0 && d->rvchancount == 0) 539 (void)pcm_setvchans(d, PCMDIR_REC, 1, -1); 540 541 pcm_clonereset(d); 542 } 543 544 static int 545 sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) 546 { 547 struct snddev_info *d; 548 int error, unit; 549 550 unit = snd_unit; 551 error = sysctl_handle_int(oidp, &unit, 0, req); 552 if (error == 0 && req->newptr != NULL) { 553 d = devclass_get_softc(pcm_devclass, unit); 554 if (!PCM_REGISTERED(d) || CHN_EMPTY(d, channels.pcm)) 555 return EINVAL; 556 snd_unit = unit; 557 snd_unit_auto = 0; 558 } 559 return (error); 560 } 561 /* XXX: do we need a way to let the user change the default unit? */ 562 SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, 563 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_ANYBODY | CTLFLAG_NEEDGIANT, 0, 564 sizeof(int), sysctl_hw_snd_default_unit, "I", 565 "default sound device"); 566 567 static int 568 sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) 569 { 570 struct snddev_info *d; 571 int i, v, error; 572 573 v = snd_maxautovchans; 574 error = sysctl_handle_int(oidp, &v, 0, req); 575 if (error == 0 && req->newptr != NULL) { 576 if (v < 0) 577 v = 0; 578 if (v > SND_MAXVCHANS) 579 v = SND_MAXVCHANS; 580 snd_maxautovchans = v; 581 for (i = 0; pcm_devclass != NULL && 582 i < devclass_get_maxunit(pcm_devclass); i++) { 583 d = devclass_get_softc(pcm_devclass, i); 584 if (!PCM_REGISTERED(d)) 585 continue; 586 PCM_ACQUIRE_QUICK(d); 587 pcm_setmaxautovchans(d, v); 588 PCM_RELEASE_QUICK(d); 589 } 590 } 591 return (error); 592 } 593 SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, 594 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 595 sysctl_hw_snd_maxautovchans, "I", 596 "maximum virtual channel"); 597 598 struct pcm_channel * 599 pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, int num, void *devinfo) 600 { 601 struct pcm_channel *ch; 602 int direction, err, rpnum, *pnum, max; 603 int udc, device, chan; 604 char *dirs, *devname, buf[CHN_NAMELEN]; 605 606 PCM_BUSYASSERT(d); 607 PCM_LOCKASSERT(d); 608 KASSERT(num >= -1, ("invalid num=%d", num)); 609 610 switch (dir) { 611 case PCMDIR_PLAY: 612 dirs = "play"; 613 direction = PCMDIR_PLAY; 614 pnum = &d->playcount; 615 device = SND_DEV_DSPHW_PLAY; 616 max = SND_MAXHWCHAN; 617 break; 618 case PCMDIR_PLAY_VIRTUAL: 619 dirs = "virtual"; 620 direction = PCMDIR_PLAY; 621 pnum = &d->pvchancount; 622 device = SND_DEV_DSPHW_VPLAY; 623 max = SND_MAXVCHANS; 624 break; 625 case PCMDIR_REC: 626 dirs = "record"; 627 direction = PCMDIR_REC; 628 pnum = &d->reccount; 629 device = SND_DEV_DSPHW_REC; 630 max = SND_MAXHWCHAN; 631 break; 632 case PCMDIR_REC_VIRTUAL: 633 dirs = "virtual"; 634 direction = PCMDIR_REC; 635 pnum = &d->rvchancount; 636 device = SND_DEV_DSPHW_VREC; 637 max = SND_MAXVCHANS; 638 break; 639 default: 640 return (NULL); 641 } 642 643 chan = (num == -1) ? 0 : num; 644 645 if (*pnum >= max || chan >= max) 646 return (NULL); 647 648 rpnum = 0; 649 650 CHN_FOREACH(ch, d, channels.pcm) { 651 if (CHN_DEV(ch) != device) 652 continue; 653 if (chan == CHN_CHAN(ch)) { 654 if (num != -1) { 655 device_printf(d->dev, 656 "channel num=%d allocated!\n", chan); 657 return (NULL); 658 } 659 chan++; 660 if (chan >= max) { 661 device_printf(d->dev, 662 "chan=%d > %d\n", chan, max); 663 return (NULL); 664 } 665 } 666 rpnum++; 667 } 668 669 if (*pnum != rpnum) { 670 device_printf(d->dev, 671 "%s(): WARNING: pnum screwed : dirs=%s pnum=%d rpnum=%d\n", 672 __func__, dirs, *pnum, rpnum); 673 return (NULL); 674 } 675 676 udc = snd_mkunit(device_get_unit(d->dev), device, chan); 677 devname = dsp_unit2name(buf, sizeof(buf), udc); 678 679 if (devname == NULL) { 680 device_printf(d->dev, 681 "Failed to query device name udc=0x%08x\n", udc); 682 return (NULL); 683 } 684 685 PCM_UNLOCK(d); 686 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 687 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); 688 ch->unit = udc; 689 ch->pid = -1; 690 strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm)); 691 ch->parentsnddev = d; 692 ch->parentchannel = parent; 693 ch->dev = d->dev; 694 ch->trigger = PCMTRIG_STOP; 695 snprintf(ch->name, sizeof(ch->name), "%s:%s:%s", 696 device_get_nameunit(ch->dev), dirs, devname); 697 698 err = chn_init(ch, devinfo, dir, direction); 699 PCM_LOCK(d); 700 if (err) { 701 device_printf(d->dev, "chn_init(%s) failed: err = %d\n", 702 ch->name, err); 703 kobj_delete(ch->methods, M_DEVBUF); 704 free(ch, M_DEVBUF); 705 return (NULL); 706 } 707 708 return (ch); 709 } 710 711 int 712 pcm_chn_destroy(struct pcm_channel *ch) 713 { 714 struct snddev_info *d __diagused; 715 int err; 716 717 d = ch->parentsnddev; 718 PCM_BUSYASSERT(d); 719 720 err = chn_kill(ch); 721 if (err) { 722 device_printf(ch->dev, "chn_kill(%s) failed, err = %d\n", 723 ch->name, err); 724 return (err); 725 } 726 727 kobj_delete(ch->methods, M_DEVBUF); 728 free(ch, M_DEVBUF); 729 730 return (0); 731 } 732 733 int 734 pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 735 { 736 PCM_BUSYASSERT(d); 737 PCM_LOCKASSERT(d); 738 KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || 739 ch->direction == PCMDIR_REC), ("Invalid pcm channel")); 740 741 CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); 742 743 switch (CHN_DEV(ch)) { 744 case SND_DEV_DSPHW_PLAY: 745 d->playcount++; 746 break; 747 case SND_DEV_DSPHW_VPLAY: 748 d->pvchancount++; 749 break; 750 case SND_DEV_DSPHW_REC: 751 d->reccount++; 752 break; 753 case SND_DEV_DSPHW_VREC: 754 d->rvchancount++; 755 break; 756 default: 757 break; 758 } 759 760 d->devcount++; 761 762 return (0); 763 } 764 765 int 766 pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 767 { 768 struct pcm_channel *tmp; 769 770 PCM_BUSYASSERT(d); 771 PCM_LOCKASSERT(d); 772 773 tmp = NULL; 774 775 CHN_FOREACH(tmp, d, channels.pcm) { 776 if (tmp == ch) 777 break; 778 } 779 780 if (tmp != ch) 781 return (EINVAL); 782 783 CHN_REMOVE(d, ch, channels.pcm); 784 785 switch (CHN_DEV(ch)) { 786 case SND_DEV_DSPHW_PLAY: 787 d->playcount--; 788 break; 789 case SND_DEV_DSPHW_VPLAY: 790 d->pvchancount--; 791 break; 792 case SND_DEV_DSPHW_REC: 793 d->reccount--; 794 break; 795 case SND_DEV_DSPHW_VREC: 796 d->rvchancount--; 797 break; 798 default: 799 break; 800 } 801 802 d->devcount--; 803 804 return (0); 805 } 806 807 int 808 pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 809 { 810 struct snddev_info *d = device_get_softc(dev); 811 struct pcm_channel *ch; 812 int err; 813 814 PCM_BUSYASSERT(d); 815 816 PCM_LOCK(d); 817 ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); 818 if (!ch) { 819 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", 820 cls->name, dir, devinfo); 821 PCM_UNLOCK(d); 822 return (ENODEV); 823 } 824 825 err = pcm_chn_add(d, ch); 826 PCM_UNLOCK(d); 827 if (err) { 828 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", 829 ch->name, err); 830 pcm_chn_destroy(ch); 831 } 832 833 return (err); 834 } 835 836 static int 837 pcm_killchan(device_t dev) 838 { 839 struct snddev_info *d = device_get_softc(dev); 840 struct pcm_channel *ch; 841 int error; 842 843 PCM_BUSYASSERT(d); 844 845 ch = CHN_FIRST(d, channels.pcm); 846 847 PCM_LOCK(d); 848 error = pcm_chn_remove(d, ch); 849 PCM_UNLOCK(d); 850 if (error) 851 return (error); 852 return (pcm_chn_destroy(ch)); 853 } 854 855 static int 856 pcm_best_unit(int old) 857 { 858 struct snddev_info *d; 859 int i, best, bestprio, prio; 860 861 best = -1; 862 bestprio = -100; 863 for (i = 0; pcm_devclass != NULL && 864 i < devclass_get_maxunit(pcm_devclass); i++) { 865 d = devclass_get_softc(pcm_devclass, i); 866 if (!PCM_REGISTERED(d)) 867 continue; 868 prio = 0; 869 if (d->playcount == 0) 870 prio -= 10; 871 if (d->reccount == 0) 872 prio -= 2; 873 if (prio > bestprio || (prio == bestprio && i == old)) { 874 best = i; 875 bestprio = prio; 876 } 877 } 878 return (best); 879 } 880 881 int 882 pcm_setstatus(device_t dev, char *str) 883 { 884 struct snddev_info *d = device_get_softc(dev); 885 886 /* should only be called once */ 887 if (d->flags & SD_F_REGISTERED) 888 return (EINVAL); 889 890 PCM_BUSYASSERT(d); 891 892 if (d->playcount == 0 || d->reccount == 0) 893 d->flags |= SD_F_SIMPLEX; 894 895 if (d->playcount > 0 || d->reccount > 0) 896 d->flags |= SD_F_AUTOVCHAN; 897 898 pcm_setmaxautovchans(d, snd_maxautovchans); 899 900 strlcpy(d->status, str, SND_STATUSLEN); 901 902 PCM_LOCK(d); 903 904 /* Last stage, enable cloning. */ 905 if (d->clones != NULL) 906 (void)snd_clone_enable(d->clones); 907 908 /* Done, we're ready.. */ 909 d->flags |= SD_F_REGISTERED; 910 911 PCM_RELEASE(d); 912 913 PCM_UNLOCK(d); 914 915 /* 916 * Create all sysctls once SD_F_REGISTERED is set else 917 * tunable sysctls won't work: 918 */ 919 pcm_sysinit(dev); 920 921 if (snd_unit_auto < 0) 922 snd_unit_auto = (snd_unit < 0) ? 1 : 0; 923 if (snd_unit < 0 || snd_unit_auto > 1) 924 snd_unit = device_get_unit(dev); 925 else if (snd_unit_auto == 1) 926 snd_unit = pcm_best_unit(snd_unit); 927 928 return (0); 929 } 930 931 uint32_t 932 pcm_getflags(device_t dev) 933 { 934 struct snddev_info *d = device_get_softc(dev); 935 936 return d->flags; 937 } 938 939 void 940 pcm_setflags(device_t dev, uint32_t val) 941 { 942 struct snddev_info *d = device_get_softc(dev); 943 944 d->flags = val; 945 } 946 947 void * 948 pcm_getdevinfo(device_t dev) 949 { 950 struct snddev_info *d = device_get_softc(dev); 951 952 return d->devinfo; 953 } 954 955 unsigned int 956 pcm_getbuffersize(device_t dev, unsigned int minbufsz, unsigned int deflt, unsigned int maxbufsz) 957 { 958 struct snddev_info *d = device_get_softc(dev); 959 int sz, x; 960 961 sz = 0; 962 if (resource_int_value(device_get_name(dev), device_get_unit(dev), "buffersize", &sz) == 0) { 963 x = sz; 964 RANGE(sz, minbufsz, maxbufsz); 965 if (x != sz) 966 device_printf(dev, "'buffersize=%d' hint is out of range (%d-%d), using %d\n", x, minbufsz, maxbufsz, sz); 967 x = minbufsz; 968 while (x < sz) 969 x <<= 1; 970 if (x > sz) 971 x >>= 1; 972 if (x != sz) { 973 device_printf(dev, "'buffersize=%d' hint is not a power of 2, using %d\n", sz, x); 974 sz = x; 975 } 976 } else { 977 sz = deflt; 978 } 979 980 d->bufsz = sz; 981 982 return sz; 983 } 984 985 static int 986 sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) 987 { 988 struct snddev_info *d; 989 int err, val; 990 991 d = oidp->oid_arg1; 992 if (!PCM_REGISTERED(d)) 993 return (ENODEV); 994 995 PCM_LOCK(d); 996 PCM_WAIT(d); 997 val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; 998 PCM_ACQUIRE(d); 999 PCM_UNLOCK(d); 1000 1001 err = sysctl_handle_int(oidp, &val, 0, req); 1002 1003 if (err == 0 && req->newptr != NULL) { 1004 if (!(val == 0 || val == 1)) { 1005 PCM_RELEASE_QUICK(d); 1006 return (EINVAL); 1007 } 1008 1009 PCM_LOCK(d); 1010 1011 d->flags &= ~SD_F_BITPERFECT; 1012 d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; 1013 1014 PCM_RELEASE(d); 1015 PCM_UNLOCK(d); 1016 } else 1017 PCM_RELEASE_QUICK(d); 1018 1019 return (err); 1020 } 1021 1022 #ifdef SND_DEBUG 1023 static int 1024 sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS) 1025 { 1026 struct snddev_info *d; 1027 uint32_t flags; 1028 int err; 1029 1030 d = oidp->oid_arg1; 1031 if (!PCM_REGISTERED(d) || d->clones == NULL) 1032 return (ENODEV); 1033 1034 PCM_ACQUIRE_QUICK(d); 1035 1036 flags = snd_clone_getflags(d->clones); 1037 err = sysctl_handle_int(oidp, &flags, 0, req); 1038 1039 if (err == 0 && req->newptr != NULL) { 1040 if (flags & ~SND_CLONE_MASK) 1041 err = EINVAL; 1042 else 1043 (void)snd_clone_setflags(d->clones, flags); 1044 } 1045 1046 PCM_RELEASE_QUICK(d); 1047 1048 return (err); 1049 } 1050 1051 static int 1052 sysctl_dev_pcm_clone_deadline(SYSCTL_HANDLER_ARGS) 1053 { 1054 struct snddev_info *d; 1055 int err, deadline; 1056 1057 d = oidp->oid_arg1; 1058 if (!PCM_REGISTERED(d) || d->clones == NULL) 1059 return (ENODEV); 1060 1061 PCM_ACQUIRE_QUICK(d); 1062 1063 deadline = snd_clone_getdeadline(d->clones); 1064 err = sysctl_handle_int(oidp, &deadline, 0, req); 1065 1066 if (err == 0 && req->newptr != NULL) { 1067 if (deadline < 0) 1068 err = EINVAL; 1069 else 1070 (void)snd_clone_setdeadline(d->clones, deadline); 1071 } 1072 1073 PCM_RELEASE_QUICK(d); 1074 1075 return (err); 1076 } 1077 1078 static int 1079 sysctl_dev_pcm_clone_gc(SYSCTL_HANDLER_ARGS) 1080 { 1081 struct snddev_info *d; 1082 int err, val; 1083 1084 d = oidp->oid_arg1; 1085 if (!PCM_REGISTERED(d) || d->clones == NULL) 1086 return (ENODEV); 1087 1088 val = 0; 1089 err = sysctl_handle_int(oidp, &val, 0, req); 1090 1091 if (err == 0 && req->newptr != NULL && val != 0) { 1092 PCM_ACQUIRE_QUICK(d); 1093 val = snd_clone_gc(d->clones); 1094 PCM_RELEASE_QUICK(d); 1095 if (bootverbose != 0 || snd_verbose > 3) 1096 device_printf(d->dev, "clone gc: pruned=%d\n", val); 1097 } 1098 1099 return (err); 1100 } 1101 1102 static int 1103 sysctl_hw_snd_clone_gc(SYSCTL_HANDLER_ARGS) 1104 { 1105 struct snddev_info *d; 1106 int i, err, val; 1107 1108 val = 0; 1109 err = sysctl_handle_int(oidp, &val, 0, req); 1110 1111 if (err == 0 && req->newptr != NULL && val != 0) { 1112 for (i = 0; pcm_devclass != NULL && 1113 i < devclass_get_maxunit(pcm_devclass); i++) { 1114 d = devclass_get_softc(pcm_devclass, i); 1115 if (!PCM_REGISTERED(d) || d->clones == NULL) 1116 continue; 1117 PCM_ACQUIRE_QUICK(d); 1118 val = snd_clone_gc(d->clones); 1119 PCM_RELEASE_QUICK(d); 1120 if (bootverbose != 0 || snd_verbose > 3) 1121 device_printf(d->dev, "clone gc: pruned=%d\n", 1122 val); 1123 } 1124 } 1125 1126 return (err); 1127 } 1128 SYSCTL_PROC(_hw_snd, OID_AUTO, clone_gc, 1129 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_NEEDGIANT, 0, sizeof(int), 1130 sysctl_hw_snd_clone_gc, "I", 1131 "global clone garbage collector"); 1132 #endif 1133 1134 static u_int8_t 1135 pcm_mode_init(struct snddev_info *d) 1136 { 1137 u_int8_t mode = 0; 1138 1139 if (d->playcount > 0) 1140 mode |= PCM_MODE_PLAY; 1141 if (d->reccount > 0) 1142 mode |= PCM_MODE_REC; 1143 if (d->mixer_dev != NULL) 1144 mode |= PCM_MODE_MIXER; 1145 1146 return (mode); 1147 } 1148 1149 static void 1150 pcm_sysinit(device_t dev) 1151 { 1152 struct snddev_info *d = device_get_softc(dev); 1153 u_int8_t mode; 1154 1155 mode = pcm_mode_init(d); 1156 1157 /* XXX: a user should be able to set this with a control tool, the 1158 sysadmin then needs min+max sysctls for this */ 1159 SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 1160 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 1161 OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); 1162 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1163 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1164 "bitperfect", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, 1165 sizeof(d), sysctl_dev_pcm_bitperfect, "I", 1166 "bit-perfect playback/recording (0=disable, 1=enable)"); 1167 SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev), 1168 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), 1169 OID_AUTO, "mode", CTLFLAG_RD, NULL, mode, 1170 "mode (1=mixer, 2=play, 4=rec. The values are OR'ed if more than one" 1171 "mode is supported)"); 1172 #ifdef SND_DEBUG 1173 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1174 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1175 "clone_flags", CTLTYPE_UINT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1176 d, sizeof(d), sysctl_dev_pcm_clone_flags, "IU", 1177 "clone flags"); 1178 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1179 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1180 "clone_deadline", CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, 1181 d, sizeof(d), sysctl_dev_pcm_clone_deadline, "I", 1182 "clone expiration deadline (ms)"); 1183 SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 1184 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 1185 "clone_gc", 1186 CTLTYPE_INT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, d, sizeof(d), 1187 sysctl_dev_pcm_clone_gc, "I", "clone garbage collector"); 1188 #endif 1189 if (d->flags & SD_F_AUTOVCHAN) 1190 vchan_initsys(dev); 1191 if (d->flags & SD_F_EQ) 1192 feeder_eq_initsys(dev); 1193 } 1194 1195 int 1196 pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 1197 { 1198 struct snddev_info *d; 1199 int i; 1200 1201 if (pcm_veto_load) { 1202 device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); 1203 1204 return EINVAL; 1205 } 1206 1207 if (device_get_unit(dev) > PCMMAXUNIT) { 1208 device_printf(dev, "PCMMAXUNIT reached : unit=%d > %d\n", 1209 device_get_unit(dev), PCMMAXUNIT); 1210 device_printf(dev, 1211 "Use 'hw.snd.maxunit' tunable to raise the limit.\n"); 1212 return ENODEV; 1213 } 1214 1215 d = device_get_softc(dev); 1216 d->dev = dev; 1217 d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); 1218 cv_init(&d->cv, device_get_nameunit(dev)); 1219 PCM_ACQUIRE_QUICK(d); 1220 dsp_cdevinfo_init(d); 1221 #if 0 1222 /* 1223 * d->flags should be cleared by the allocator of the softc. 1224 * We cannot clear this field here because several devices set 1225 * this flag before calling pcm_register(). 1226 */ 1227 d->flags = 0; 1228 #endif 1229 i = 0; 1230 if (resource_int_value(device_get_name(dev), device_get_unit(dev), 1231 "vpc", &i) != 0 || i != 0) 1232 d->flags |= SD_F_VPC; 1233 1234 if (resource_int_value(device_get_name(dev), device_get_unit(dev), 1235 "bitperfect", &i) == 0 && i != 0) 1236 d->flags |= SD_F_BITPERFECT; 1237 1238 d->devinfo = devinfo; 1239 d->devcount = 0; 1240 d->reccount = 0; 1241 d->playcount = 0; 1242 d->pvchancount = 0; 1243 d->rvchancount = 0; 1244 d->pvchanrate = 0; 1245 d->pvchanformat = 0; 1246 d->rvchanrate = 0; 1247 d->rvchanformat = 0; 1248 1249 /* 1250 * Create clone manager, disabled by default. Cloning will be 1251 * enabled during final stage of driver initialization through 1252 * pcm_setstatus(). 1253 */ 1254 d->clones = snd_clone_create(SND_U_MASK | SND_D_MASK, PCMMAXCLONE, 1255 SND_CLONE_DEADLINE_DEFAULT, SND_CLONE_WAITOK | 1256 SND_CLONE_GC_ENABLE | SND_CLONE_GC_UNREF | 1257 SND_CLONE_GC_LASTREF | SND_CLONE_GC_EXPIRED); 1258 1259 CHN_INIT(d, channels.pcm); 1260 CHN_INIT(d, channels.pcm.busy); 1261 CHN_INIT(d, channels.pcm.opened); 1262 1263 /* XXX This is incorrect, but lets play along for now. */ 1264 if ((numplay == 0 || numrec == 0) && numplay != numrec) 1265 d->flags |= SD_F_SIMPLEX; 1266 1267 sysctl_ctx_init(&d->play_sysctl_ctx); 1268 d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, 1269 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", 1270 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "playback channels node"); 1271 sysctl_ctx_init(&d->rec_sysctl_ctx); 1272 d->rec_sysctl_tree = SYSCTL_ADD_NODE(&d->rec_sysctl_ctx, 1273 SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "rec", 1274 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "recording channels node"); 1275 1276 if (numplay > 0 || numrec > 0) 1277 d->flags |= SD_F_AUTOVCHAN; 1278 1279 sndstat_register(dev, d->status, sndstat_prepare_pcm); 1280 1281 return 0; 1282 } 1283 1284 int 1285 pcm_unregister(device_t dev) 1286 { 1287 struct snddev_info *d; 1288 struct pcm_channel *ch; 1289 1290 d = device_get_softc(dev); 1291 1292 if (!PCM_ALIVE(d)) { 1293 device_printf(dev, "unregister: device not configured\n"); 1294 return (0); 1295 } 1296 1297 PCM_LOCK(d); 1298 PCM_WAIT(d); 1299 1300 d->flags |= SD_F_DETACHING; 1301 1302 PCM_ACQUIRE(d); 1303 PCM_UNLOCK(d); 1304 1305 CHN_FOREACH(ch, d, channels.pcm) { 1306 CHN_LOCK(ch); 1307 if (ch->refcount > 0) { 1308 device_printf(dev, 1309 "unregister: channel %s busy (pid %d)\n", 1310 ch->name, ch->pid); 1311 CHN_UNLOCK(ch); 1312 PCM_RELEASE_QUICK(d); 1313 return (EBUSY); 1314 } 1315 CHN_UNLOCK(ch); 1316 } 1317 1318 if (d->clones != NULL) { 1319 if (snd_clone_busy(d->clones) != 0) { 1320 device_printf(dev, "unregister: clone busy\n"); 1321 PCM_RELEASE_QUICK(d); 1322 return (EBUSY); 1323 } else { 1324 PCM_LOCK(d); 1325 (void)snd_clone_disable(d->clones); 1326 PCM_UNLOCK(d); 1327 } 1328 } 1329 1330 if (mixer_uninit(dev) == EBUSY) { 1331 device_printf(dev, "unregister: mixer busy\n"); 1332 PCM_LOCK(d); 1333 if (d->clones != NULL) 1334 (void)snd_clone_enable(d->clones); 1335 PCM_RELEASE(d); 1336 PCM_UNLOCK(d); 1337 return (EBUSY); 1338 } 1339 1340 /* remove /dev/sndstat entry first */ 1341 sndstat_unregister(dev); 1342 1343 PCM_LOCK(d); 1344 d->flags |= SD_F_DYING; 1345 d->flags &= ~SD_F_REGISTERED; 1346 PCM_UNLOCK(d); 1347 1348 /* 1349 * No lock being held, so this thing can be flushed without 1350 * stucking into devdrn oblivion. 1351 */ 1352 if (d->clones != NULL) { 1353 snd_clone_destroy(d->clones); 1354 d->clones = NULL; 1355 } 1356 1357 if (d->play_sysctl_tree != NULL) { 1358 sysctl_ctx_free(&d->play_sysctl_ctx); 1359 d->play_sysctl_tree = NULL; 1360 } 1361 if (d->rec_sysctl_tree != NULL) { 1362 sysctl_ctx_free(&d->rec_sysctl_ctx); 1363 d->rec_sysctl_tree = NULL; 1364 } 1365 1366 while (!CHN_EMPTY(d, channels.pcm)) 1367 pcm_killchan(dev); 1368 1369 dsp_cdevinfo_flush(d); 1370 1371 PCM_LOCK(d); 1372 PCM_RELEASE(d); 1373 cv_destroy(&d->cv); 1374 PCM_UNLOCK(d); 1375 snd_mtxfree(d->lock); 1376 1377 if (snd_unit == device_get_unit(dev)) { 1378 snd_unit = pcm_best_unit(-1); 1379 if (snd_unit_auto == 0) 1380 snd_unit_auto = 1; 1381 } 1382 1383 return (0); 1384 } 1385 1386 /************************************************************************/ 1387 1388 /** 1389 * @brief Handle OSSv4 SNDCTL_SYSINFO ioctl. 1390 * 1391 * @param si Pointer to oss_sysinfo struct where information about the 1392 * sound subsystem will be written/copied. 1393 * 1394 * This routine returns information about the sound system, such as the 1395 * current OSS version, number of audio, MIDI, and mixer drivers, etc. 1396 * Also includes a bitmask showing which of the above types of devices 1397 * are open (busy). 1398 * 1399 * @note 1400 * Calling threads must not hold any snddev_info or pcm_channel locks. 1401 * 1402 * @author Ryan Beasley <ryanb@FreeBSD.org> 1403 */ 1404 void 1405 sound_oss_sysinfo(oss_sysinfo *si) 1406 { 1407 static char si_product[] = "FreeBSD native OSS ABI"; 1408 static char si_version[] = __XSTRING(__FreeBSD_version); 1409 static char si_license[] = "BSD"; 1410 static int intnbits = sizeof(int) * 8; /* Better suited as macro? 1411 Must pester a C guru. */ 1412 1413 struct snddev_info *d; 1414 struct pcm_channel *c; 1415 int i, j, ncards; 1416 1417 ncards = 0; 1418 1419 strlcpy(si->product, si_product, sizeof(si->product)); 1420 strlcpy(si->version, si_version, sizeof(si->version)); 1421 si->versionnum = SOUND_VERSION; 1422 strlcpy(si->license, si_license, sizeof(si->license)); 1423 1424 /* 1425 * Iterate over PCM devices and their channels, gathering up data 1426 * for the numaudios, ncards, and openedaudio fields. 1427 */ 1428 si->numaudios = 0; 1429 bzero((void *)&si->openedaudio, sizeof(si->openedaudio)); 1430 1431 j = 0; 1432 1433 for (i = 0; pcm_devclass != NULL && 1434 i < devclass_get_maxunit(pcm_devclass); i++) { 1435 d = devclass_get_softc(pcm_devclass, i); 1436 if (!PCM_REGISTERED(d)) 1437 continue; 1438 1439 /* XXX Need Giant magic entry ??? */ 1440 1441 /* See note in function's docblock */ 1442 PCM_UNLOCKASSERT(d); 1443 PCM_LOCK(d); 1444 1445 si->numaudios += d->devcount; 1446 ++ncards; 1447 1448 CHN_FOREACH(c, d, channels.pcm) { 1449 CHN_UNLOCKASSERT(c); 1450 CHN_LOCK(c); 1451 if (c->flags & CHN_F_BUSY) 1452 si->openedaudio[j / intnbits] |= 1453 (1 << (j % intnbits)); 1454 CHN_UNLOCK(c); 1455 j++; 1456 } 1457 1458 PCM_UNLOCK(d); 1459 } 1460 si->numaudioengines = si->numaudios; 1461 1462 si->numsynths = 0; /* OSSv4 docs: this field is obsolete */ 1463 /** 1464 * @todo Collect num{midis,timers}. 1465 * 1466 * Need access to sound/midi/midi.c::midistat_lock in order 1467 * to safely touch midi_devices and get a head count of, well, 1468 * MIDI devices. midistat_lock is a global static (i.e., local to 1469 * midi.c), but midi_devices is a regular global; should the mutex 1470 * be publicized, or is there another way to get this information? 1471 * 1472 * NB: MIDI/sequencer stuff is currently on hold. 1473 */ 1474 si->nummidis = 0; 1475 si->numtimers = 0; 1476 si->nummixers = mixer_count; 1477 si->numcards = ncards; 1478 /* OSSv4 docs: Intended only for test apps; API doesn't 1479 really have much of a concept of cards. Shouldn't be 1480 used by applications. */ 1481 1482 /** 1483 * @todo Fill in "busy devices" fields. 1484 * 1485 * si->openedmidi = " MIDI devices 1486 */ 1487 bzero((void *)&si->openedmidi, sizeof(si->openedmidi)); 1488 1489 /* 1490 * Si->filler is a reserved array, but according to docs each 1491 * element should be set to -1. 1492 */ 1493 for (i = 0; i < sizeof(si->filler)/sizeof(si->filler[0]); i++) 1494 si->filler[i] = -1; 1495 } 1496 1497 int 1498 sound_oss_card_info(oss_card_info *si) 1499 { 1500 struct snddev_info *d; 1501 int i, ncards; 1502 1503 ncards = 0; 1504 1505 for (i = 0; pcm_devclass != NULL && 1506 i < devclass_get_maxunit(pcm_devclass); i++) { 1507 d = devclass_get_softc(pcm_devclass, i); 1508 if (!PCM_REGISTERED(d)) 1509 continue; 1510 1511 if (ncards++ != si->card) 1512 continue; 1513 1514 PCM_UNLOCKASSERT(d); 1515 PCM_LOCK(d); 1516 1517 strlcpy(si->shortname, device_get_nameunit(d->dev), 1518 sizeof(si->shortname)); 1519 strlcpy(si->longname, device_get_desc(d->dev), 1520 sizeof(si->longname)); 1521 strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); 1522 si->intr_count = si->ack_count = 0; 1523 1524 PCM_UNLOCK(d); 1525 1526 return (0); 1527 } 1528 return (ENXIO); 1529 } 1530 1531 /************************************************************************/ 1532 1533 static int 1534 sound_modevent(module_t mod, int type, void *data) 1535 { 1536 int ret; 1537 1538 ret = 0; 1539 switch (type) { 1540 case MOD_LOAD: 1541 pcm_devclass = devclass_create("pcm"); 1542 pcmsg_unrhdr = new_unrhdr(1, INT_MAX, NULL); 1543 break; 1544 case MOD_UNLOAD: 1545 if (pcmsg_unrhdr != NULL) { 1546 delete_unrhdr(pcmsg_unrhdr); 1547 pcmsg_unrhdr = NULL; 1548 } 1549 break; 1550 case MOD_SHUTDOWN: 1551 break; 1552 default: 1553 ret = ENOTSUP; 1554 } 1555 1556 return ret; 1557 } 1558 1559 DEV_MODULE(sound, sound_modevent, NULL); 1560 MODULE_VERSION(sound, SOUND_MODVER); 1561