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