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