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