sound.c revision 74763
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 74763 2001-03-24 23:10:29Z 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 */ D_TRACKCLOSE, 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 struct snddev_info *d = device_get_softc(dev); 233 struct 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 snprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(dev), dirs, idx); 249 err = chn_init(ch, devinfo, dir); 250 if (err) { 251 device_printf(dev, "chn_init() for (%s:%d) failed: err = %d\n", dirs, idx, err); 252 return 1; 253 } 254 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount), 255 UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount); 256 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount), 257 UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount); 258 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount), 259 UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount); 260 /* XXX SND_DEV_NORESET? */ 261 d->chancount++; 262#ifdef USING_DEVFS 263 if (d->chancount == 1) 264 pcm_makelinks(NULL); 265#endif 266 return 0; 267} 268 269static int 270pcm_killchan(device_t dev, int dir) 271{ 272 int unit = device_get_unit(dev), idx; 273 struct snddev_info *d = device_get_softc(dev); 274 struct pcm_channel *chns, *ch; 275 char *dirs; 276 dev_t pdev; 277 278 dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); 279 chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); 280 idx = ((dir == PCMDIR_PLAY)? --d->playcount : --d->reccount); 281 282 if (chns == NULL || idx < 0) { 283 device_printf(dev, "bad channel kill (%s:%d)\n", dirs, idx); 284 return 1; 285 } 286 ch = &chns[idx]; 287 if (chn_kill(ch)) { 288 device_printf(dev, "chn_kill() for (%s:%d) failed\n", dirs, idx); 289 return 1; 290 } 291 kobj_delete(ch->methods, M_DEVBUF); 292 ch->methods = NULL; 293 d->chancount--; 294 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount)); 295 destroy_dev(pdev); 296 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount)); 297 destroy_dev(pdev); 298 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount)); 299 destroy_dev(pdev); 300 return 0; 301} 302 303int 304pcm_setstatus(device_t dev, char *str) 305{ 306 struct snddev_info *d = device_get_softc(dev); 307 strncpy(d->status, str, SND_STATUSLEN); 308 return 0; 309} 310 311u_int32_t 312pcm_getflags(device_t dev) 313{ 314 struct snddev_info *d = device_get_softc(dev); 315 return d->flags; 316} 317 318void 319pcm_setflags(device_t dev, u_int32_t val) 320{ 321 struct snddev_info *d = device_get_softc(dev); 322 d->flags = val; 323} 324 325void * 326pcm_getdevinfo(device_t dev) 327{ 328 struct snddev_info *d = device_get_softc(dev); 329 return d->devinfo; 330} 331 332/* This is the generic init routine */ 333int 334pcm_register(device_t dev, void *devinfo, int numplay, int numrec) 335{ 336 int sz, unit = device_get_unit(dev); 337 struct snddev_info *d = device_get_softc(dev); 338 339 if (!pcm_devclass) { 340 pcm_devclass = device_get_devclass(dev); 341 status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0), 342 UID_ROOT, GID_WHEEL, 0444, "sndstat"); 343 } 344 make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), 345 UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); 346 d->dev = dev; 347 d->devinfo = devinfo; 348 d->chancount = d->playcount = d->reccount = 0; 349 d->maxchans = numplay + numrec; 350 sz = (numplay + numrec) * sizeof(struct pcm_channel *); 351 352 if (sz > 0) { 353 d->aplay = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); 354 if (!d->aplay) goto no; 355 bzero(d->aplay, sz); 356 357 d->arec = (struct pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); 358 if (!d->arec) goto no; 359 bzero(d->arec, sz); 360 361 sz = (numplay + numrec) * sizeof(int); 362 d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT); 363 if (!d->ref) goto no; 364 bzero(d->ref, sz); 365 } 366 367 if (numplay > 0) { 368 d->play = (struct pcm_channel *)malloc(numplay * sizeof(struct pcm_channel), 369 M_DEVBUF, M_NOWAIT); 370 if (!d->play) goto no; 371 bzero(d->play, numplay * sizeof(struct pcm_channel)); 372 } else 373 d->play = NULL; 374 375 if (numrec > 0) { 376 d->rec = (struct pcm_channel *)malloc(numrec * sizeof(struct pcm_channel), 377 M_DEVBUF, M_NOWAIT); 378 if (!d->rec) goto no; 379 bzero(d->rec, numrec * sizeof(struct pcm_channel)); 380 } else 381 d->rec = NULL; 382 383#ifdef SND_DYNSYSCTL 384 sysctl_ctx_init(&d->sysctl_tree); 385 d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, 386 SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, 387 device_get_nameunit(dev), CTLFLAG_RD, 0, ""); 388 if (d->sysctl_tree_top == NULL) { 389 sysctl_ctx_free(&d->sysctl_tree); 390 goto no; 391 } 392#endif 393 394 if (numplay == 0 || numrec == 0) 395 d->flags |= SD_F_SIMPLEX; 396 397 d->fakechan = fkchan_setup(dev); 398 chn_init(d->fakechan, NULL, 0); 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 struct snddev_info *d = device_get_softc(dev); 415 dev_t pdev; 416 417 r = 0; 418 for (i = 0; i < d->chancount; i++) 419 if (d->ref[i]) r = EBUSY; 420 if (r) { 421 device_printf(dev, "unregister: channel busy"); 422 return r; 423 } 424 if (mixer_isbusy(d->mixer)) { 425 device_printf(dev, "unregister: mixer busy"); 426 return EBUSY; 427 } 428 429#ifdef SND_DYNSYSCTL 430 d->sysctl_tree_top = NULL; 431 sysctl_ctx_free(&d->sysctl_tree); 432#endif 433 434 pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); 435 destroy_dev(pdev); 436 mixer_uninit(dev); 437 438 while (d->playcount > 0) 439 pcm_killchan(dev, PCMDIR_PLAY); 440 while (d->reccount > 0) 441 pcm_killchan(dev, PCMDIR_REC); 442 443 if (d->aplay) free(d->aplay, M_DEVBUF); 444 if (d->play) free(d->play, M_DEVBUF); 445 if (d->arec) free(d->arec, M_DEVBUF); 446 if (d->rec) free(d->rec, M_DEVBUF); 447 if (d->ref) free(d->ref, M_DEVBUF); 448 449 chn_kill(d->fakechan); 450 fkchan_kill(d->fakechan); 451 452#ifdef USING_DEVFS 453 pcm_makelinks(NULL); 454#endif 455 return 0; 456} 457 458/* 459 * a small utility function which, given a device number, returns 460 * a pointer to the associated struct snddev_info struct, and sets the unit 461 * number. 462 */ 463static struct snddev_info * 464get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan) 465{ 466 struct snddev_info *sc; 467 int u, d, c; 468 469 u = PCMUNIT(i_dev); 470 d = PCMDEV(i_dev); 471 c = PCMCHAN(i_dev); 472 if (u > devclass_get_maxunit(pcm_devclass)) u = -1; 473 if (unit) *unit = u; 474 if (dev) *dev = d; 475 if (chan) *chan = c; 476 if (u < 0) return NULL; 477 478 sc = devclass_get_softc(pcm_devclass, u); 479 if (sc == NULL) return NULL; 480 481 switch(d) { 482 case SND_DEV_CTL: /* /dev/mixer handled by pcm */ 483 case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */ 484 case SND_DEV_DSP: 485 case SND_DEV_DSP16: 486 case SND_DEV_AUDIO: 487 return sc; 488 489 case SND_DEV_SEQ: /* XXX when enabled... */ 490 case SND_DEV_SEQ2: 491 case SND_DEV_MIDIN: 492 case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */ 493 default: 494 printf("unsupported subdevice %d\n", d); 495 return NULL; 496 } 497} 498 499static int 500sndopen(dev_t i_dev, int flags, int mode, struct proc *p) 501{ 502 int dev, unit, chan; 503 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 504 505 DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n", 506 unit, dev, flags, mode)); 507 508 switch(dev) { 509 case SND_DEV_STATUS: 510 if (status_isopen) return EBUSY; 511 status_isopen = 1; 512 return 0; 513 514 case SND_DEV_CTL: 515 return d? mixer_busy(d->mixer, 1) : ENXIO; 516 517 case SND_DEV_AUDIO: 518 case SND_DEV_DSP: 519 case SND_DEV_DSP16: 520 case SND_DEV_NORESET: 521 return d? dsp_open(d, chan, flags, dev) : ENXIO; 522 523 default: 524 return ENXIO; 525 } 526} 527 528static int 529sndclose(dev_t i_dev, int flags, int mode, struct proc *p) 530{ 531 int dev, unit, chan; 532 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 533 534 DEB(printf("close snd%d subdev %d\n", unit, dev)); 535 536 switch(dev) { /* only those for which close makes sense */ 537 case SND_DEV_STATUS: 538 if (!status_isopen) return EBADF; 539 status_isopen = 0; 540 return 0; 541 542 case SND_DEV_CTL: 543 return d? mixer_busy(d->mixer, 0) : ENXIO; 544 545 case SND_DEV_AUDIO: 546 case SND_DEV_DSP: 547 case SND_DEV_DSP16: 548 return d? dsp_close(d, chan, dev) : ENXIO; 549 550 default: 551 return ENXIO; 552 } 553} 554 555static int 556sndread(dev_t i_dev, struct uio *buf, int flag) 557{ 558 int dev, unit, chan; 559 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 560 DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag)); 561 562 switch(dev) { 563 case SND_DEV_STATUS: 564 return status_isopen? status_read(buf) : EBADF; 565 566 case SND_DEV_AUDIO: 567 case SND_DEV_DSP: 568 case SND_DEV_DSP16: 569 return d? dsp_read(d, chan, buf, flag) : EBADF; 570 571 default: 572 return ENXIO; 573 } 574} 575 576static int 577sndwrite(dev_t i_dev, struct uio *buf, int flag) 578{ 579 int dev, unit, chan; 580 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 581 582 DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); 583 584 switch(dev) { /* only writeable devices */ 585 case SND_DEV_DSP: 586 case SND_DEV_DSP16: 587 case SND_DEV_AUDIO: 588 return d? dsp_write(d, chan, buf, flag) : EBADF; 589 590 default: 591 return EPERM; /* for non-writeable devices ; */ 592 } 593} 594 595static int 596sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) 597{ 598 int dev, chan; 599 struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 600 601 if (d == NULL) return ENXIO; 602 603 switch(dev) { 604 case SND_DEV_CTL: 605 return mixer_ioctl(d, cmd, arg); 606 607 case SND_DEV_AUDIO: 608 case SND_DEV_DSP: 609 case SND_DEV_DSP16: 610 if (IOCGROUP(cmd) == 'M') 611 return mixer_ioctl(d, cmd, arg); 612 else 613 return dsp_ioctl(d, chan, cmd, arg); 614 615 default: 616 return ENXIO; 617 } 618} 619 620static int 621sndpoll(dev_t i_dev, int events, struct proc *p) 622{ 623 int dev, chan; 624 struct snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); 625 626 DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events)); 627 628 if (d == NULL) return ENXIO; 629 630 switch(dev) { 631 case SND_DEV_AUDIO: 632 case SND_DEV_DSP: 633 case SND_DEV_DSP16: 634 return dsp_poll(d, chan, events, p); 635 636 default: 637 return (events & 638 (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP; 639 } 640} 641 642/* 643 * The mmap interface allows access to the play and read buffer, 644 * plus the device descriptor. 645 * The various blocks are accessible at the following offsets: 646 * 647 * 0x00000000 ( 0 ) : write buffer ; 648 * 0x01000000 (16 MB) : read buffer ; 649 * 0x02000000 (32 MB) : device descriptor (dangerous!) 650 * 651 * WARNING: the mmap routines assume memory areas are aligned. This 652 * is true (probably) for the dma buffers, but likely false for the 653 * device descriptor. As a consequence, we do not know where it is 654 * located in the requested area. 655 */ 656static int 657sndmmap(dev_t i_dev, vm_offset_t offset, int nprot) 658{ 659 int unit, dev, chan; 660 struct snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); 661 662 DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", 663 d, dev, offset, nprot)); 664 665 if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */ 666 667 switch(dev) { 668 case SND_DEV_AUDIO: 669 case SND_DEV_DSP: 670 case SND_DEV_DSP16: 671 return dsp_mmap(d, chan, offset, nprot); 672 673 default: 674 return -1; 675 } 676} 677 678static int 679status_init(char *buf, int size) 680{ 681 int i; 682 device_t dev; 683 struct snddev_info *d; 684 685 snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n" 686 "Installed devices:\n", __DATE__, __TIME__); 687 688 for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) { 689 d = devclass_get_softc(pcm_devclass, i); 690 if (!d) continue; 691 dev = devclass_get_device(pcm_devclass, i); 692 if (1) { 693 snprintf(buf + strlen(buf), size - strlen(buf), 694 "pcm%d: <%s> %s", 695 i, device_get_desc(dev), d->status); 696 if (d->chancount > 0) 697 snprintf(buf + strlen(buf), size - strlen(buf), 698 " (%dp/%dr channels%s)\n", 699 d->playcount, d->reccount, 700 (!(d->flags & SD_F_SIMPLEX))? " duplex" : ""); 701 else 702 snprintf(buf + strlen(buf), size - strlen(buf), 703 " (mixer only)\n"); 704 } 705 } 706 return strlen(buf); 707} 708 709static int 710status_read(struct uio *buf) 711{ 712 static char status_buf[4096]; 713 static int bufptr = 0, buflen = 0; 714 int l; 715 716 if (status_isopen == 1) { 717 status_isopen++; 718 bufptr = 0; 719 buflen = status_init(status_buf, sizeof status_buf); 720 } 721 722 l = min(buf->uio_resid, buflen - bufptr); 723 bufptr += l; 724 return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0; 725} 726 727static int 728sndpcm_modevent(module_t mod, int type, void *data) 729{ 730 731 switch (type) { 732 case MOD_LOAD: 733 break; 734 case MOD_UNLOAD: 735 if (status_isopen) 736 return EBUSY; 737 if (status_dev) 738 destroy_dev(status_dev); 739 status_dev = 0; 740 break; 741 default: 742 break; 743 } 744 return 0; 745} 746 747static moduledata_t sndpcm_mod = { 748 "snd_pcm", 749 sndpcm_modevent, 750 NULL 751}; 752DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); 753MODULE_VERSION(snd_pcm, PCM_MODVER); 754