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