97#endif 98 99SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 100 101void * 102snd_mtxcreate(const char *desc) 103{ 104#ifdef USING_MUTEX 105 struct mtx *m; 106 107 m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); 108 if (m == NULL) 109 return NULL; 110 mtx_init(m, desc, MTX_RECURSE); 111 return m; 112#else 113 return (void *)0xcafebabe; 114#endif 115} 116 117void 118snd_mtxfree(void *m) 119{ 120#ifdef USING_MUTEX 121 struct mtx *mtx = m; 122 123 mtx_assert(mtx, MA_OWNED); 124 mtx_destroy(mtx); 125 free(mtx, M_DEVBUF); 126#endif 127} 128 129void 130snd_mtxassert(void *m) 131{ 132#ifdef USING_MUTEX 133 struct mtx *mtx = m; 134 135 mtx_assert(mtx, MA_OWNED); 136#endif 137} 138 139void 140snd_mtxlock(void *m) 141{ 142#ifdef USING_MUTEX 143 struct mtx *mtx = m; 144 145 mtx_lock(mtx); 146#endif 147} 148 149void 150snd_mtxunlock(void *m) 151{ 152#ifdef USING_MUTEX 153 struct mtx *mtx = m; 154 155 mtx_unlock(mtx); 156#endif 157} 158 159int 160snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) 161{ 162#ifdef USING_MUTEX 163 flags &= INTR_MPSAFE; 164 flags |= INTR_TYPE_TTY; 165#else 166 flags = INTR_TYPE_TTY; 167#endif 168 return bus_setup_intr(dev, res, flags, hand, param, cookiep); 169} 170 171struct pcm_channel * 172pcm_chnalloc(struct snddev_info *d, int direction) 173{ 174 struct pcm_channel *c; 175 struct snddev_channel *sce; 176 177 SLIST_FOREACH(sce, &d->channels, link) { 178 c = sce->channel; 179 if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { 180 c->flags |= CHN_F_BUSY; 181 return c; 182 } 183 } 184 return NULL; 185} 186 187int 188pcm_chnfree(struct pcm_channel *c) 189{ 190 c->flags &= ~CHN_F_BUSY; 191 return 0; 192} 193 194int 195pcm_chnref(struct pcm_channel *c, int ref) 196{ 197 c->refcount += ref; 198 return c->refcount; 199} 200 201#ifdef USING_DEVFS 202static void 203pcm_makelinks(void *dummy) 204{ 205 int unit; 206 dev_t pdev; 207 static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0; 208 struct snddev_info *d; 209 210 if (pcm_devclass == NULL || devfs_present == 0) 211 return; 212 if (dsp) { 213 destroy_dev(dsp); 214 dsp = 0; 215 } 216 if (dspW) { 217 destroy_dev(dspW); 218 dspW = 0; 219 } 220 if (audio) { 221 destroy_dev(audio); 222 audio = 0; 223 } 224 if (mixer) { 225 destroy_dev(mixer); 226 mixer = 0; 227 } 228 229 unit = snd_unit; 230 if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass)) 231 return; 232 d = devclass_get_softc(pcm_devclass, unit); 233 if (d == NULL || d->chancount == 0) 234 return; 235 236 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0)); 237 dsp = make_dev_alias(pdev, "dsp"); 238 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0)); 239 dspW = make_dev_alias(pdev, "dspW"); 240 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0)); 241 audio = make_dev_alias(pdev, "audio"); 242 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 243 mixer = make_dev_alias(pdev, "mixer"); 244} 245 246static int 247sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS) 248{ 249 int error, unit; 250 251 unit = snd_unit; 252 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 253 if (error == 0 && req->newptr != NULL) { 254 snd_unit = unit; 255 pcm_makelinks(NULL); 256 } 257 return (error); 258} 259SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 260 0, sizeof(int), sysctl_hw_sndunit, "I", ""); 261#endif 262 263struct pcm_channel * 264pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) 265{ 266 struct pcm_channel *ch; 267 char *dirs; 268 int err; 269 270 switch(dir) { 271 case PCMDIR_PLAY: 272 dirs = "play"; 273 break; 274 case PCMDIR_REC: 275 dirs = "record"; 276 break; 277 case PCMDIR_VIRTUAL: 278 dirs = "virtual"; 279 dir = PCMDIR_PLAY; 280 break; 281 default: 282 return NULL; 283 } 284 285 ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); 286 if (!ch) 287 return NULL; 288 289 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 290 if (!ch->methods) { 291 free(ch, M_DEVBUF); 292 return NULL; 293 } 294 295 ch->pid = -1; 296 ch->parentsnddev = d; 297 ch->parentchannel = parent; 298 snprintf(ch->name, 32, "%s:%d:%s", device_get_nameunit(d->dev), d->chancount, dirs); 299 300 err = chn_init(ch, devinfo, dir); 301 if (err) { 302 device_printf(d->dev, "chn_init() for channel %d (%s) failed: err = %d\n", d->chancount, dirs, err); 303 kobj_delete(ch->methods, M_DEVBUF); 304 free(ch, M_DEVBUF); 305 return NULL; 306 } 307 308 return ch; 309} 310 311int 312pcm_chn_destroy(struct pcm_channel *ch) 313{ 314 int err; 315 316 err = chn_kill(ch); 317 if (err) { 318 device_printf(ch->parentsnddev->dev, "chn_kill() for %s failed, err = %d\n", ch->name, err); 319 return err; 320 } 321 322 kobj_delete(ch->methods, M_DEVBUF); 323 free(ch, M_DEVBUF); 324 325 return 0; 326} 327 328int 329pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) 330{ 331 struct snddev_channel *sce; 332 int unit = device_get_unit(d->dev); 333 334 sce = malloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); 335 if (!sce) { 336 free(ch, M_DEVBUF); 337 return ENOMEM; 338 } 339 340 sce->channel = ch; 341 SLIST_INSERT_HEAD(&d->channels, sce, link); 342 343 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount), 344 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount); 345 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount), 346 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount); 347 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount), 348 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount); 349 /* XXX SND_DEV_NORESET? */ 350 351#ifdef USING_DEVFS 352 if (d->chancount++ == 0) 353 pcm_makelinks(NULL); 354#endif 355 356 return 0; 357} 358 359int 360pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) 361{ 362 struct snddev_channel *sce; 363 int unit = device_get_unit(d->dev); 364 dev_t pdev; 365 366 SLIST_FOREACH(sce, &d->channels, link) { 367 if (sce->channel == ch) 368 goto gotit; 369 } 370 return EINVAL; 371gotit: 372 d->chancount--; 373 SLIST_REMOVE(&d->channels, sce, snddev_channel, link); 374 free(sce, M_DEVBUF); 375 376#ifdef USING_DEVFS 377 if (d->chancount == 0) 378 pcm_makelinks(NULL); 379#endif 380 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount)); 381 destroy_dev(pdev); 382 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount)); 383 destroy_dev(pdev); 384 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount)); 385 destroy_dev(pdev); 386 387 return 0; 388} 389 390int 391pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 392{ 393 struct snddev_info *d = device_get_softc(dev); 394 struct pcm_channel *ch; 395 int err; 396 397 ch = pcm_chn_create(d, NULL, cls, dir, devinfo); 398 if (!ch) { 399 device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); 400 return ENODEV; 401 } 402 err = pcm_chn_add(d, ch); 403 if (err) { 404 device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); 405 pcm_chn_destroy(ch); 406 } 407 408 return err; 409} 410 411static int 412pcm_killchan(device_t dev) 413{ 414 struct snddev_info *d = device_get_softc(dev); 415 struct snddev_channel *sce; 416 417 sce = SLIST_FIRST(&d->channels); 418 419 return pcm_chn_remove(d, sce->channel); 420} 421 422int 423pcm_setstatus(device_t dev, char *str) 424{ 425 struct snddev_info *d = device_get_softc(dev); 426 strncpy(d->status, str, SND_STATUSLEN); 427 return 0; 428} 429 430u_int32_t 431pcm_getflags(device_t dev) 432{ 433 struct snddev_info *d = device_get_softc(dev); 434 return d->flags; 435} 436 437void 438pcm_setflags(device_t dev, u_int32_t val) 439{ 440 struct snddev_info *d = device_get_softc(dev); 441 d->flags = val; 442} 443 444void * 445pcm_getdevinfo(device_t dev) 446{ 447 struct snddev_info *d = device_get_softc(dev); 448 return d->devinfo; 449} 450 451/* This is the generic init routine */ 452int 453pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 454{ 455 int sz, unit = device_get_unit(dev); 456 struct snddev_info *d = device_get_softc(dev); 457 458 if (!pcm_devclass) { 459 pcm_devclass = device_get_devclass(dev); 460 status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0), 461 UID_ROOT, GID_WHEEL, 0444, "sndstat"); 462 } 463 464 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), 465 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 466 467 d->dev = dev; 468 d->devinfo = devinfo; 469 d->chancount = 0; 470 d->maxchans = numplay + numrec; 471 sz = d->maxchans * sizeof(struct pcm_channel *); 472 473 if (sz > 0) { 474 d->aplay = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO); 475 d->arec = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_WAITOK | M_ZERO); 476 if (!d->arec || !d->aplay) goto no; 477 478 if (numplay == 0 || numrec == 0) 479 d->flags |= SD_F_SIMPLEX; 480 481 d->fakechan = fkchan_setup(dev); 482 chn_init(d->fakechan, NULL, 0); 483 } 484 485#ifdef SND_DYNSYSCTL 486 sysctl_ctx_init(&d->sysctl_tree); 487 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 488 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 489 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 490 if (d->sysctl_tree_top == NULL) { 491 sysctl_ctx_free(&d->sysctl_tree); 492 goto no; 493 } 494#endif 495#if 0 496 vchan_initsys(d); 497#endif 498 return 0; 499no: 500 if (d->aplay) free(d->aplay, M_DEVBUF); 501 if (d->arec) free(d->arec, M_DEVBUF); 502 return ENXIO; 503} 504 505int 506pcm_unregister(device_t dev) 507{ 508 int unit = device_get_unit(dev); 509 struct snddev_info *d = device_get_softc(dev); 510 struct snddev_channel *sce; 511 dev_t pdev; 512 513 SLIST_FOREACH(sce, &d->channels, link) { 514 if (sce->channel->refcount > 0) { 515 device_printf(dev, "unregister: channel busy"); 516 return EBUSY; 517 } 518 } 519 if (mixer_isbusy(d->mixer)) { 520 device_printf(dev, "unregister: mixer busy"); 521 return EBUSY; 522 } 523 524#ifdef SND_DYNSYSCTL 525 d->sysctl_tree_top = NULL; 526 sysctl_ctx_free(&d->sysctl_tree); 527#endif 528 529 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 530 destroy_dev(pdev); 531 mixer_uninit(dev); 532 533 while (d->chancount > 0) 534 pcm_killchan(dev); 535 536 if (d->aplay) free(d->aplay, M_DEVBUF); 537 if (d->arec) free(d->arec, M_DEVBUF); 538 539 chn_kill(d->fakechan); 540 fkchan_kill(d->fakechan); 541 542 return 0; 543} 544 545/* 546 * a small utility function which, given a device number, returns 547 * a pointer to the associated struct snddev_info struct, and sets the unit 548 * number. 549 */ 550static struct snddev_info * 551get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan) 552{ 553 struct snddev_info *sc; 554 int u, d, c; 555 556 u = PCMUNIT(i_dev); 557 d = PCMDEV(i_dev); 558 c = PCMCHAN(i_dev); 559 if (u > devclass_get_maxunit(pcm_devclass)) u = -1; 560 if (unit) *unit = u; 561 if (dev) *dev = d; 562 if (chan) *chan = c; 563 if (u < 0) return NULL; 564 565 sc = devclass_get_softc(pcm_devclass, u); 566 if (sc == NULL) return NULL; 567 568 switch(d) { 569 case SND_DEV_CTL: /* /dev/mixer handled by pcm */ 570 case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */ 571 case SND_DEV_DSP: 572 case SND_DEV_DSP16: 573 case SND_DEV_AUDIO: 574 return sc; 575 576 case SND_DEV_SEQ: /* XXX when enabled... */ 577 case SND_DEV_SEQ2: 578 case SND_DEV_MIDIN: 579 case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */ 580 default: 581 printf("unsupported subdevice %d\n", d); 582 return NULL; 583 } 584} 585 586static int 587sndopen(dev_t i_dev, int flags, int mode, struct proc *p) 588{ 589 int dev, unit, chan; 590 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 591 592 DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n", 593 unit, dev, flags, mode)); 594 595 switch(dev) { 596 case SND_DEV_STATUS: 597 return do_status(0, NULL); 598 599 case SND_DEV_CTL: 600 return d? mixer_busy(d->mixer, 1) : ENXIO; 601 602 case SND_DEV_AUDIO: 603 case SND_DEV_DSP: 604 case SND_DEV_DSP16: 605 case SND_DEV_NORESET: 606 return d? dsp_open(d, chan, flags, dev, p->p_pid) : ENXIO; 607 608 default: 609 return ENXIO; 610 } 611} 612 613static int 614sndclose(dev_t i_dev, int flags, int mode, struct proc *p) 615{ 616 int dev, unit, chan; 617 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 618 619 DEB(printf("close snd%d subdev %d\n", unit, dev)); 620 621 switch(dev) { /* only those for which close makes sense */ 622 case SND_DEV_STATUS: 623 return do_status(1, NULL); 624 625 case SND_DEV_CTL: 626 return d? mixer_busy(d->mixer, 0) : ENXIO; 627 628 case SND_DEV_AUDIO: 629 case SND_DEV_DSP: 630 case SND_DEV_DSP16: 631 return d? dsp_close(d, chan, dev) : ENXIO; 632 633 default: 634 return ENXIO; 635 } 636} 637 638static int 639sndread(dev_t i_dev, struct uio *buf, int flag) 640{ 641 int dev, unit, chan; 642 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 643 DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag)); 644 645 switch(dev) { 646 case SND_DEV_STATUS: 647 return do_status(2, buf); 648 649 case SND_DEV_AUDIO: 650 case SND_DEV_DSP: 651 case SND_DEV_DSP16: 652 return d? dsp_read(d, chan, buf, flag) : EBADF; 653 654 default: 655 return ENXIO; 656 } 657} 658 659static int 660sndwrite(dev_t i_dev, struct uio *buf, int flag) 661{ 662 int dev, unit, chan; 663 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 664 665 DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); 666 667 switch(dev) { /* only writeable devices */ 668 case SND_DEV_DSP: 669 case SND_DEV_DSP16: 670 case SND_DEV_AUDIO: 671 return d? dsp_write(d, chan, buf, flag) : EBADF; 672 673 default: 674 return EPERM; /* for non-writeable devices ; */ 675 } 676} 677 678static int 679sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) 680{ 681 int dev, chan; 682 struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 683 684 if (d == NULL) return ENXIO; 685 686 switch(dev) { 687 case SND_DEV_CTL: 688 return mixer_ioctl(d, cmd, arg); 689 690 case SND_DEV_AUDIO: 691 case SND_DEV_DSP: 692 case SND_DEV_DSP16: 693 if (IOCGROUP(cmd) == 'M') 694 return mixer_ioctl(d, cmd, arg); 695 else 696 return dsp_ioctl(d, chan, cmd, arg); 697 698 default: 699 return ENXIO; 700 } 701} 702 703static int 704sndpoll(dev_t i_dev, int events, struct proc *p) 705{ 706 int dev, chan; 707 struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 708 709 DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events)); 710 711 if (d == NULL) return ENXIO; 712 713 switch(dev) { 714 case SND_DEV_AUDIO: 715 case SND_DEV_DSP: 716 case SND_DEV_DSP16: 717 return dsp_poll(d, chan, events, p); 718 719 default: 720 return (events & 721 (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP; 722 } 723} 724 725/* 726 * The mmap interface allows access to the play and read buffer, 727 * plus the device descriptor. 728 * The various blocks are accessible at the following offsets: 729 * 730 * 0x00000000 ( 0 ) : write buffer ; 731 * 0x01000000 (16 MB) : read buffer ; 732 * 0x02000000 (32 MB) : device descriptor (dangerous!) 733 * 734 * WARNING: the mmap routines assume memory areas are aligned. This 735 * is true (probably) for the dma buffers, but likely false for the 736 * device descriptor. As a consequence, we do not know where it is 737 * located in the requested area. 738 */ 739static int 740sndmmap(dev_t i_dev, vm_offset_t offset, int nprot) 741{ 742 int unit, dev, chan; 743 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 744 745 DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", 746 d, dev, offset, nprot)); 747 748 if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */ 749 750 switch(dev) { 751 case SND_DEV_AUDIO: 752 case SND_DEV_DSP: 753 case SND_DEV_DSP16: 754 return dsp_mmap(d, chan, offset, nprot); 755 756 default: 757 return -1; 758 } 759} 760 761static int 762status_init(struct sbuf *s) 763{ 764 int i, pc, rc, vc; 765 device_t dev; 766 struct snddev_info *d; 767 struct snddev_channel *sce; 768 struct pcm_channel *c; 769 struct pcm_feeder *f; 770 771 sbuf_printf(s, "FreeBSD Audio Driver (newpcm) %s %s\nInstalled devices:\n", 772 __DATE__, __TIME__); 773 774 for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) { 775 d = devclass_get_softc(pcm_devclass, i); 776 if (!d) 777 continue; 778 dev = devclass_get_device(pcm_devclass, i); 779 sbuf_printf(s, "pcm%d: <%s> %s", i, device_get_desc(dev), d->status); 780 if (d->chancount > 0) { 781 pc = rc = vc = 0; 782 SLIST_FOREACH(sce, &d->channels, link) { 783 c = sce->channel; 784 if (c->direction == PCMDIR_PLAY) { 785 if (c->flags & CHN_F_VIRTUAL) 786 vc++; 787 else 788 pc++; 789 } else 790 rc++; 791 } 792 sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)\n", pc, rc, vc, 793 (d->flags & SD_F_SIMPLEX)? "" : " duplex", 794#ifdef USING_DEVFS 795 (i == snd_unit)? " default" : "" 796#else 797 "" 798#endif 799 ); 800#ifdef SNDSTAT_VERBOSE 801 SLIST_FOREACH(sce, &d->channels, link) { 802 c = sce->channel; 803 sbuf_printf(s, "\t%s[%s]: speed %d, format %08x, flags %08x", 804 c->parentchannel? c->parentchannel->name : "", 805 c->name, c->speed, c->format, c->flags); 806 if (c->pid != -1) 807 sbuf_printf(s, ", pid %d", c->pid); 808 sbuf_printf(s, "\n\t"); 809 f = c->feeder; 810 while (f) { 811 sbuf_printf(s, "%s", f->class->name); 812 if (f->desc->type == FEEDER_FMT) 813 sbuf_printf(s, "(%08x <- %08x)", f->desc->out, f->desc->in); 814 if (f->desc->type == FEEDER_RATE) 815 sbuf_printf(s, "(%d <- %d)", FEEDER_GET(f, FEEDRATE_DST), FEEDER_GET(f, FEEDRATE_SRC)); 816 if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) 817 sbuf_printf(s, "(%08x)", f->desc->out); 818 if (f->source) 819 sbuf_printf(s, " <- "); 820 f = f->source; 821 } 822 sbuf_printf(s, "\n"); 823 } 824#endif 825 } else 826 sbuf_printf(s, " (mixer only)\n"); 827 } 828 sbuf_finish(s); 829 return sbuf_len(s); 830} 831 832static int 833do_status(int action, struct uio *buf) 834{ 835 static struct sbuf s; 836 static int bufptr = 0; 837 static int status_open = 0; 838 int l, err; 839 840 switch(action) { 841 case 0: /* open */ 842 if (status_open) 843 return EBUSY; 844 if (sbuf_new(&s, NULL, 4096, 0)) 845 return ENXIO; 846 bufptr = 0; 847 err = (status_init(&s) > 0)? 0 : ENOMEM; 848 if (!err) 849 status_open = 1; 850 return err; 851 852 case 1: /* close */ 853 if (!status_open) 854 return EBADF; 855 sbuf_delete(&s); 856 status_open = 0; 857 return 0; 858 859 case 2: 860 if (!status_open) 861 return EBADF; 862 l = min(buf->uio_resid, sbuf_len(&s) - bufptr); 863 err = (l > 0)? uiomove(sbuf_data(&s) + bufptr, l, buf) : 0; 864 bufptr += l; 865 return err; 866 867 case 3: 868 return status_open; 869 } 870 871 return EBADF; 872} 873 874static int 875sndpcm_modevent(module_t mod, int type, void *data) 876{ 877 878 switch (type) { 879 case MOD_LOAD: 880 break; 881 case MOD_UNLOAD: 882 if (do_status(3, NULL)) 883 return EBUSY; 884 if (status_dev) 885 destroy_dev(status_dev); 886 status_dev = 0; 887 break; 888 default: 889 break; 890 } 891 return 0; 892} 893 894static moduledata_t sndpcm_mod = { 895 "snd_pcm", 896 sndpcm_modevent, 897 NULL 898}; 899DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 900MODULE_VERSION(snd_pcm, PCM_MODVER);
|