sound.c revision 73127
1/* 2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3 * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * $FreeBSD: head/sys/dev/sound/pcm/sound.c 73127 2001-02-27 07:01:49Z cg $ 28 */ 29 30#include <dev/sound/pcm/sound.h> 31#include <sys/sysctl.h> 32 33static dev_t status_dev = 0; 34static int status_isopen = 0; 35static int status_init(char *buf, int size); 36static int status_read(struct uio *buf); 37 38static d_open_t sndopen; 39static d_close_t sndclose; 40static d_ioctl_t sndioctl; 41static d_read_t sndread; 42static d_write_t sndwrite; 43static d_mmap_t sndmmap; 44static d_poll_t sndpoll; 45 46#define CDEV_MAJOR 30 47static struct cdevsw snd_cdevsw = { 48 /* open */ sndopen, 49 /* close */ sndclose, 50 /* read */ sndread, 51 /* write */ sndwrite, 52 /* ioctl */ sndioctl, 53 /* poll */ sndpoll, 54 /* mmap */ sndmmap, 55 /* strategy */ nostrategy, 56 /* name */ "snd", 57 /* maj */ CDEV_MAJOR, 58 /* dump */ nodump, 59 /* psize */ nopsize, 60 /* flags */ 0, 61 /* bmaj */ -1 62}; 63 64/* 65PROPOSAL: 66each unit needs: 67status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices 68dspW and audio are deprecated. 69dsp needs min 64 channels, will give it 256 70 71minor = (unit << 20) + (dev << 16) + channel 72currently minor = (channel << 16) + (unit << 4) + dev 73 74nomenclature: 75 /dev/pcmX/dsp.(0..255) 76 /dev/pcmX/dspW 77 /dev/pcmX/audio 78 /dev/pcmX/status 79 /dev/pcmX/mixer 80 [etc.] 81*/ 82 83#define PCMMINOR(x) (minor(x)) 84#define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16) 85#define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4) 86#define PCMDEV(x) (PCMMINOR(x) & 0x0000000f) 87#define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f)) 88 89static devclass_t pcm_devclass; 90 91#ifdef USING_DEVFS 92int snd_unit; 93TUNABLE_INT_DECL("hw.snd.unit", 0, snd_unit); 94#endif 95 96SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); 97 98#ifdef USING_DEVFS 99static void 100pcm_makelinks(void *dummy) 101{ 102 int unit; 103 dev_t pdev; 104 static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0; 105 106 if (pcm_devclass == NULL || devfs_present == 0) 107 return; 108 if (dsp) { 109 destroy_dev(dsp); 110 dsp = 0; 111 } 112 if (dspW) { 113 destroy_dev(dspW); 114 dspW = 0; 115 } 116 if (audio) { 117 destroy_dev(audio); 118 audio = 0; 119 } 120 if (mixer) { 121 destroy_dev(mixer); 122 mixer = 0; 123 } 124 125 unit = snd_unit; 126 if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass)) 127 return; 128 if (devclass_get_softc(pcm_devclass, unit) == NULL) 129 return; 130 131 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0)); 132 dsp = make_dev_alias(pdev, "dsp"); 133 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0)); 134 dspW = make_dev_alias(pdev, "dspW"); 135 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0)); 136 audio = make_dev_alias(pdev, "audio"); 137 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 138 mixer = make_dev_alias(pdev, "mixer"); 139} 140 141static int 142sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS) 143{ 144 int error, unit; 145 146 unit = snd_unit; 147 error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); 148 if (error == 0 && req->newptr != NULL) { 149 snd_unit = unit; 150 pcm_makelinks(NULL); 151 } 152 return (error); 153} 154SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 155 0, sizeof(int), sysctl_hw_sndunit, "I", ""); 156#endif 157 158int 159pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) 160{ 161 int unit = device_get_unit(dev), idx; 162 snddev_info *d = device_get_softc(dev); 163 pcm_channel *chns, *ch; 164 char *dirs; 165 int err; 166 167 dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); 168 chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); 169 idx = ((dir == PCMDIR_PLAY)? d->playcount++ : d->reccount++); 170 171 if (chns == NULL) { 172 device_printf(dev, "bad channel add (%s:%d)\n", dirs, idx); 173 return 1; 174 } 175 ch = &chns[idx]; 176 ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); 177 ch->parent = d; 178 err = chn_init(ch, devinfo, dir); 179 if (err) { 180 device_printf(dev, "chn_init() for (%s:%d) failed: err = %d\n", dirs, idx, err); 181 return 1; 182 } 183 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount), 184 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount); 185 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount), 186 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount); 187 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount), 188 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount); 189 /* XXX SND_DEV_NORESET? */ 190 d->chancount++; 191#ifdef USING_DEVFS 192 if (d->chancount == 1) 193 pcm_makelinks(NULL); 194#endif 195 return 0; 196} 197 198static int 199pcm_killchan(device_t dev, int dir) 200{ 201 int unit = device_get_unit(dev), idx; 202 snddev_info *d = device_get_softc(dev); 203 pcm_channel *chns, *ch; 204 char *dirs; 205 dev_t pdev; 206 207 dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); 208 chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); 209 idx = ((dir == PCMDIR_PLAY)? --d->playcount : --d->reccount); 210 211 if (chns == NULL || idx < 0) { 212 device_printf(dev, "bad channel kill (%s:%d)\n", dirs, idx); 213 return 1; 214 } 215 ch = &chns[idx]; 216 if (chn_kill(ch)) { 217 device_printf(dev, "chn_kill() for (%s:%d) failed\n", dirs, idx); 218 return 1; 219 } 220 kobj_delete(ch->methods, M_DEVBUF); 221 ch->methods = NULL; 222 d->chancount--; 223 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount)); 224 destroy_dev(pdev); 225 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount)); 226 destroy_dev(pdev); 227 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount)); 228 destroy_dev(pdev); 229 return 0; 230} 231 232int 233pcm_setstatus(device_t dev, char *str) 234{ 235 snddev_info *d = device_get_softc(dev); 236 strncpy(d->status, str, SND_STATUSLEN); 237 return 0; 238} 239 240u_int32_t 241pcm_getflags(device_t dev) 242{ 243 snddev_info *d = device_get_softc(dev); 244 return d->flags; 245} 246 247void 248pcm_setflags(device_t dev, u_int32_t val) 249{ 250 snddev_info *d = device_get_softc(dev); 251 d->flags = val; 252} 253 254void * 255pcm_getdevinfo(device_t dev) 256{ 257 snddev_info *d = device_get_softc(dev); 258 return d->devinfo; 259} 260 261/* This is the generic init routine */ 262int 263pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 264{ 265 int sz, unit = device_get_unit(dev); 266 snddev_info *d = device_get_softc(dev); 267 268 if (!pcm_devclass) { 269 pcm_devclass = device_get_devclass(dev); 270 status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0), 271 UID_ROOT, GID_WHEEL, 0444, "sndstat"); 272 } 273 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), 274 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 275 d->dev = dev; 276 d->devinfo = devinfo; 277 d->chancount = d->playcount = d->reccount = 0; 278 d->maxchans = numplay + numrec; 279 sz = (numplay + numrec) * sizeof(pcm_channel *); 280 281 if (sz > 0) { 282 d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); 283 if (!d->aplay) goto no; 284 bzero(d->aplay, sz); 285 286 d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); 287 if (!d->arec) goto no; 288 bzero(d->arec, sz); 289 290 sz = (numplay + numrec) * sizeof(int); 291 d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT); 292 if (!d->ref) goto no; 293 bzero(d->ref, sz); 294 } 295 296 if (numplay > 0) { 297 d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel), 298 M_DEVBUF, M_NOWAIT); 299 if (!d->play) goto no; 300 bzero(d->play, numplay * sizeof(pcm_channel)); 301 } else 302 d->play = NULL; 303 304 if (numrec > 0) { 305 d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel), 306 M_DEVBUF, M_NOWAIT); 307 if (!d->rec) goto no; 308 bzero(d->rec, numrec * sizeof(pcm_channel)); 309 } else 310 d->rec = NULL; 311 312#ifdef SND_DYNSYSCTL 313 sysctl_ctx_init(&d->sysctl_tree); 314 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 315 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 316 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 317 if (d->sysctl_tree_top == NULL) { 318 sysctl_ctx_free(&d->sysctl_tree); 319 goto no; 320 } 321#endif 322 323 if (numplay == 0 || numrec == 0) 324 d->flags |= SD_F_SIMPLEX; 325 326 fkchan_setup(&d->fakechan); 327 chn_init(&d->fakechan, NULL, 0); 328 d->magic = MAGIC(unit); /* debugging... */ 329 330 return 0; 331no: 332 if (d->aplay) free(d->aplay, M_DEVBUF); 333 if (d->play) free(d->play, M_DEVBUF); 334 if (d->arec) free(d->arec, M_DEVBUF); 335 if (d->rec) free(d->rec, M_DEVBUF); 336 if (d->ref) free(d->ref, M_DEVBUF); 337 return ENXIO; 338} 339 340int 341pcm_unregister(device_t dev) 342{ 343 int r, i, unit = device_get_unit(dev); 344 snddev_info *d = device_get_softc(dev); 345 dev_t pdev; 346 347#ifdef SND_DYNSYSCTL 348 sysctl_remove_oid(d->sysctl_tree_top, 1, 1); 349 d->sysctl_tree_top = NULL; 350 sysctl_ctx_free(&d->sysctl_tree); 351#endif 352 353 r = 0; 354 for (i = 0; i < d->chancount; i++) 355 if (d->ref[i]) r = EBUSY; 356 if (r) { 357 device_printf(dev, "unregister: channel busy"); 358 return r; 359 } 360 if (mixer_isbusy(d->mixer)) { 361 device_printf(dev, "unregister: mixer busy"); 362 return EBUSY; 363 } 364 365 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 366 destroy_dev(pdev); 367 mixer_uninit(dev); 368 369 while (d->playcount > 0) 370 pcm_killchan(dev, PCMDIR_PLAY); 371 while (d->reccount > 0) 372 pcm_killchan(dev, PCMDIR_REC); 373 d->magic = 0; 374 375 if (d->aplay) free(d->aplay, M_DEVBUF); 376 if (d->play) free(d->play, M_DEVBUF); 377 if (d->arec) free(d->arec, M_DEVBUF); 378 if (d->rec) free(d->rec, M_DEVBUF); 379 if (d->ref) free(d->ref, M_DEVBUF); 380 381 fkchan_kill(&d->fakechan); 382 383#ifdef USING_DEVFS 384 pcm_makelinks(NULL); 385#endif 386 return 0; 387} 388 389/* 390 * a small utility function which, given a device number, returns 391 * a pointer to the associated snddev_info struct, and sets the unit 392 * number. 393 */ 394static snddev_info * 395get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan) 396{ 397 snddev_info *sc; 398 int u, d, c; 399 400 u = PCMUNIT(i_dev); 401 d = PCMDEV(i_dev); 402 c = PCMCHAN(i_dev); 403 if (u > devclass_get_maxunit(pcm_devclass)) u = -1; 404 if (unit) *unit = u; 405 if (dev) *dev = d; 406 if (chan) *chan = c; 407 if (u < 0) return NULL; 408 409 sc = devclass_get_softc(pcm_devclass, u); 410 if (sc == NULL || sc->magic == 0) return NULL; 411 412 switch(d) { 413 case SND_DEV_CTL: /* /dev/mixer handled by pcm */ 414 case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */ 415 case SND_DEV_DSP: 416 case SND_DEV_DSP16: 417 case SND_DEV_AUDIO: 418 return sc; 419 420 case SND_DEV_SEQ: /* XXX when enabled... */ 421 case SND_DEV_SEQ2: 422 case SND_DEV_MIDIN: 423 case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */ 424 default: 425 printf("unsupported subdevice %d\n", d); 426 return NULL; 427 } 428} 429 430static int 431sndopen(dev_t i_dev, int flags, int mode, struct proc *p) 432{ 433 int dev, unit, chan; 434 snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 435 436 DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n", 437 unit, dev, flags, mode)); 438 439 switch(dev) { 440 case SND_DEV_STATUS: 441 if (status_isopen) return EBUSY; 442 status_isopen = 1; 443 return 0; 444 445 case SND_DEV_CTL: 446 return d? mixer_busy(d->mixer, 1) : ENXIO; 447 448 case SND_DEV_AUDIO: 449 case SND_DEV_DSP: 450 case SND_DEV_DSP16: 451 case SND_DEV_NORESET: 452 return d? dsp_open(d, chan, flags, dev) : ENXIO; 453 454 default: 455 return ENXIO; 456 } 457} 458 459static int 460sndclose(dev_t i_dev, int flags, int mode, struct proc *p) 461{ 462 int dev, unit, chan; 463 snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 464 465 DEB(printf("close snd%d subdev %d\n", unit, dev)); 466 467 switch(dev) { /* only those for which close makes sense */ 468 case SND_DEV_STATUS: 469 if (!status_isopen) return EBADF; 470 status_isopen = 0; 471 return 0; 472 473 case SND_DEV_CTL: 474 return d? mixer_busy(d->mixer, 0) : ENXIO; 475 476 case SND_DEV_AUDIO: 477 case SND_DEV_DSP: 478 case SND_DEV_DSP16: 479 return d? dsp_close(d, chan, dev) : ENXIO; 480 481 default: 482 return ENXIO; 483 } 484} 485 486static int 487sndread(dev_t i_dev, struct uio *buf, int flag) 488{ 489 int dev, unit, chan; 490 snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 491 DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag)); 492 493 switch(dev) { 494 case SND_DEV_STATUS: 495 return status_isopen? status_read(buf) : EBADF; 496 497 case SND_DEV_AUDIO: 498 case SND_DEV_DSP: 499 case SND_DEV_DSP16: 500 return d? dsp_read(d, chan, buf, flag) : EBADF; 501 502 default: 503 return ENXIO; 504 } 505} 506 507static int 508sndwrite(dev_t i_dev, struct uio *buf, int flag) 509{ 510 int dev, unit, chan; 511 snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 512 513 DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); 514 515 switch(dev) { /* only writeable devices */ 516 case SND_DEV_DSP: 517 case SND_DEV_DSP16: 518 case SND_DEV_AUDIO: 519 return d? dsp_write(d, chan, buf, flag) : EBADF; 520 521 default: 522 return EPERM; /* for non-writeable devices ; */ 523 } 524} 525 526static int 527sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) 528{ 529 int dev, chan; 530 snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 531 532 if (d == NULL) return ENXIO; 533 534 switch(dev) { 535 case SND_DEV_CTL: 536 return mixer_ioctl(d, cmd, arg); 537 538 case SND_DEV_AUDIO: 539 case SND_DEV_DSP: 540 case SND_DEV_DSP16: 541 if (IOCGROUP(cmd) == 'M') 542 return mixer_ioctl(d, cmd, arg); 543 else 544 return dsp_ioctl(d, chan, cmd, arg); 545 546 default: 547 return ENXIO; 548 } 549} 550 551static int 552sndpoll(dev_t i_dev, int events, struct proc *p) 553{ 554 int dev, chan; 555 snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 556 557 DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events)); 558 559 if (d == NULL) return ENXIO; 560 561 switch(dev) { 562 case SND_DEV_AUDIO: 563 case SND_DEV_DSP: 564 case SND_DEV_DSP16: 565 return dsp_poll(d, chan, events, p); 566 567 default: 568 return (events & 569 (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP; 570 } 571} 572 573/* 574 * The mmap interface allows access to the play and read buffer, 575 * plus the device descriptor. 576 * The various blocks are accessible at the following offsets: 577 * 578 * 0x00000000 ( 0 ) : write buffer ; 579 * 0x01000000 (16 MB) : read buffer ; 580 * 0x02000000 (32 MB) : device descriptor (dangerous!) 581 * 582 * WARNING: the mmap routines assume memory areas are aligned. This 583 * is true (probably) for the dma buffers, but likely false for the 584 * device descriptor. As a consequence, we do not know where it is 585 * located in the requested area. 586 */ 587static int 588sndmmap(dev_t i_dev, vm_offset_t offset, int nprot) 589{ 590 int unit, dev, chan; 591 snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 592 593 DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", 594 d, dev, offset, nprot)); 595 596 if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */ 597 598 switch(dev) { 599 case SND_DEV_AUDIO: 600 case SND_DEV_DSP: 601 case SND_DEV_DSP16: 602 return dsp_mmap(d, chan, offset, nprot); 603 604 default: 605 return -1; 606 } 607} 608 609static int 610status_init(char *buf, int size) 611{ 612 int i; 613 device_t dev; 614 snddev_info *d; 615 616 snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n" 617 "Installed devices:\n", __DATE__, __TIME__); 618 619 for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) { 620 d = devclass_get_softc(pcm_devclass, i); 621 if (!d) continue; 622 dev = devclass_get_device(pcm_devclass, i); 623 if (1) { 624 snprintf(buf + strlen(buf), size - strlen(buf), 625 "pcm%d: <%s> %s", 626 i, device_get_desc(dev), d->status); 627 if (d->chancount > 0) 628 snprintf(buf + strlen(buf), size - strlen(buf), 629 " (%dp/%dr channels%s)\n", 630 d->playcount, d->reccount, 631 (!(d->flags & SD_F_SIMPLEX))? " duplex" : ""); 632 else 633 snprintf(buf + strlen(buf), size - strlen(buf), 634 " (mixer only)\n"); 635 } 636 } 637 return strlen(buf); 638} 639 640static int 641status_read(struct uio *buf) 642{ 643 static char status_buf[4096]; 644 static int bufptr = 0, buflen = 0; 645 int l; 646 647 if (status_isopen == 1) { 648 status_isopen++; 649 bufptr = 0; 650 buflen = status_init(status_buf, sizeof status_buf); 651 } 652 653 l = min(buf->uio_resid, buflen - bufptr); 654 bufptr += l; 655 return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0; 656} 657 658static int 659sndpcm_modevent(module_t mod, int type, void *data) 660{ 661 662 switch (type) { 663 case MOD_LOAD: 664 break; 665 case MOD_UNLOAD: 666 if (status_isopen) 667 return EBUSY; 668 if (status_dev) 669 destroy_dev(status_dev); 670 status_dev = 0; 671 break; 672 default: 673 break; 674 } 675 return 0; 676} 677 678static moduledata_t sndpcm_mod = { 679 "snd_pcm", 680 sndpcm_modevent, 681 NULL 682}; 683DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 684MODULE_VERSION(snd_pcm, PCM_MODVER); 685