1 /*- 2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: src/sys/dev/sound/pcm/dsp.c,v 1.80.2.6 2006/04/04 17:43:48 ariff Exp $ 27 * $DragonFly: src/sys/dev/sound/pcm/dsp.c,v 1.17 2008/02/28 17:19:11 tgen Exp $ 28 */ 29 30 #include <sys/param.h> 31 #include <sys/queue.h> 32 #include <sys/event.h> 33 34 #include <dev/sound/pcm/dsp.h> 35 #include <dev/sound/pcm/sound.h> 36 37 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/dsp.c,v 1.17 2008/02/28 17:19:11 tgen Exp $"); 38 39 #define OLDPCM_IOCTL 40 41 static d_open_t dsp_open; 42 static d_close_t dsp_close; 43 static d_read_t dsp_read; 44 static d_write_t dsp_write; 45 static d_ioctl_t dsp_ioctl; 46 static d_kqfilter_t dsp_kqfilter; 47 static d_mmap_t dsp_mmap; 48 49 static void dsp_filter_detach(struct knote *); 50 static int dsp_filter_read(struct knote *, long); 51 static int dsp_filter_write(struct knote *, long); 52 53 struct dev_ops dsp_cdevsw = { 54 { "dsp", SND_CDEV_MAJOR, 0}, 55 /*.d_flags = D_NEEDGIANT,*/ 56 .d_open = dsp_open, 57 .d_close = dsp_close, 58 .d_read = dsp_read, 59 .d_write = dsp_write, 60 .d_ioctl = dsp_ioctl, 61 .d_kqfilter = dsp_kqfilter, 62 .d_mmap = dsp_mmap, 63 }; 64 65 struct snddev_info * 66 dsp_get_info(struct cdev *dev) 67 { 68 struct snddev_info *d; 69 int unit; 70 71 unit = PCMUNIT(dev); 72 if (unit >= devclass_get_maxunit(pcm_devclass)) 73 return NULL; 74 d = devclass_get_softc(pcm_devclass, unit); 75 76 return d; 77 } 78 79 static u_int32_t 80 dsp_get_flags(struct cdev *dev) 81 { 82 device_t bdev; 83 int unit; 84 85 unit = PCMUNIT(dev); 86 if (unit >= devclass_get_maxunit(pcm_devclass)) 87 return 0xffffffff; 88 bdev = devclass_get_device(pcm_devclass, unit); 89 90 return pcm_getflags(bdev); 91 } 92 93 static void 94 dsp_set_flags(struct cdev *dev, u_int32_t flags) 95 { 96 device_t bdev; 97 int unit; 98 99 unit = PCMUNIT(dev); 100 if (unit >= devclass_get_maxunit(pcm_devclass)) 101 return; 102 bdev = devclass_get_device(pcm_devclass, unit); 103 104 pcm_setflags(bdev, flags); 105 } 106 107 /* 108 * return the channels associated with an open device instance. 109 * set the priority if the device is simplex and one direction (only) is 110 * specified. 111 * lock channels specified. 112 */ 113 static int 114 getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio) 115 { 116 struct snddev_info *d; 117 u_int32_t flags; 118 119 flags = dsp_get_flags(dev); 120 d = dsp_get_info(dev); 121 pcm_inprog(d, 1); 122 pcm_lock(d); 123 KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \ 124 ("getchns: read and write both prioritised")); 125 126 if ((flags & SD_F_PRIO_SET) == 0 && (prio != (SD_F_PRIO_RD | SD_F_PRIO_WR))) { 127 flags |= prio & (SD_F_PRIO_RD | SD_F_PRIO_WR); 128 dsp_set_flags(dev, flags); 129 } 130 131 *rdch = dev->si_drv1; 132 *wrch = dev->si_drv2; 133 if ((flags & SD_F_SIMPLEX) && (flags & SD_F_PRIO_SET)) { 134 if (prio) { 135 if (*rdch && flags & SD_F_PRIO_WR) { 136 dev->si_drv1 = NULL; 137 *rdch = pcm_getfakechan(d); 138 } else if (*wrch && flags & SD_F_PRIO_RD) { 139 dev->si_drv2 = NULL; 140 *wrch = pcm_getfakechan(d); 141 } 142 } 143 144 pcm_getfakechan(d)->flags |= CHN_F_BUSY; 145 } 146 pcm_unlock(d); 147 148 if (*rdch && *rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD)) 149 CHN_LOCK(*rdch); 150 if (*wrch && *wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR)) 151 CHN_LOCK(*wrch); 152 153 return 0; 154 } 155 156 /* unlock specified channels */ 157 static void 158 relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio) 159 { 160 struct snddev_info *d; 161 162 d = dsp_get_info(dev); 163 if (wrch && wrch != pcm_getfakechan(d) && (prio & SD_F_PRIO_WR)) 164 CHN_UNLOCK(wrch); 165 if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD)) 166 CHN_UNLOCK(rdch); 167 pcm_inprog(d, -1); 168 } 169 170 static int 171 dsp_open(struct dev_open_args *ap) 172 { 173 struct cdev *i_dev = ap->a_head.a_dev; 174 struct thread *td = curthread; 175 int flags = ap->a_oflags; 176 struct pcm_channel *rdch, *wrch; 177 struct snddev_info *d = NULL; 178 struct snddev_channel *sce = NULL; 179 u_int32_t fmt = AFMT_U8; 180 int error; 181 int chnum; 182 183 if (i_dev == NULL) { 184 error = ENODEV; 185 goto out; 186 } 187 188 d = dsp_get_info(i_dev); 189 SLIST_FOREACH(sce, &d->channels, link) { 190 if (sce->dsp_dev == i_dev) 191 break; 192 } 193 194 if (sce == NULL) { 195 error = ENODEV; 196 goto out; 197 } 198 199 if (td == NULL) { 200 error = ENODEV; 201 goto out; 202 } 203 204 if ((flags & (FREAD | FWRITE)) == 0) { 205 error = EINVAL; 206 goto out; 207 } 208 209 chnum = PCMCHAN(i_dev); 210 211 /* lock snddev so nobody else can monkey with it */ 212 pcm_lock(d); 213 214 rdch = i_dev->si_drv1; 215 wrch = i_dev->si_drv2; 216 217 if (rdch || wrch || ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && 218 (flags & (FREAD | FWRITE)) == (FREAD | FWRITE))) { 219 /* simplex or not, better safe than sorry. */ 220 pcm_unlock(d); 221 error = EBUSY; 222 goto out; 223 } 224 225 /* 226 * if we get here, the open request is valid- either: 227 * * we were previously not open 228 * * we were open for play xor record and the opener wants 229 * the non-open direction 230 */ 231 if (flags & FREAD) { 232 /* open for read */ 233 pcm_unlock(d); 234 error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, chnum); 235 if (error != 0 && error != EBUSY && chnum != -1 && (flags & FWRITE)) 236 error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, -1); 237 238 if (error == 0 && (chn_reset(rdch, fmt) || 239 (fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED)))) 240 error = ENODEV; 241 242 if (error != 0) { 243 if (rdch) 244 pcm_chnrelease(rdch); 245 goto out; 246 } 247 248 pcm_chnref(rdch, 1); 249 CHN_UNLOCK(rdch); 250 pcm_lock(d); 251 } 252 253 if (flags & FWRITE) { 254 /* open for write */ 255 pcm_unlock(d); 256 error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, chnum); 257 if (error != 0 && error != EBUSY && chnum != -1 && (flags & FREAD)) 258 error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, -1); 259 260 if (error == 0 && (chn_reset(wrch, fmt) || 261 (fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED)))) 262 error = ENODEV; 263 264 if (error != 0) { 265 if (wrch) 266 pcm_chnrelease(wrch); 267 if (rdch) { 268 /* 269 * Lock, deref and release previously created record channel 270 */ 271 CHN_LOCK(rdch); 272 pcm_chnref(rdch, -1); 273 pcm_chnrelease(rdch); 274 } 275 276 goto out; 277 } 278 279 pcm_chnref(wrch, 1); 280 CHN_UNLOCK(wrch); 281 pcm_lock(d); 282 } 283 284 i_dev->si_drv1 = rdch; 285 i_dev->si_drv2 = wrch; 286 287 sce->open++; 288 289 pcm_unlock(d); 290 return 0; 291 292 out: 293 if (i_dev != NULL && sce != NULL && sce->open == 0) { 294 pcm_lock(d); 295 destroy_dev(i_dev); 296 sce->dsp_dev = NULL; 297 pcm_unlock(d); 298 } 299 return (error); 300 } 301 302 static int 303 dsp_close(struct dev_close_args *ap) 304 { 305 struct cdev *i_dev = ap->a_head.a_dev; 306 struct pcm_channel *rdch, *wrch; 307 struct snddev_info *d; 308 struct snddev_channel *sce = NULL; 309 int refs; 310 311 d = dsp_get_info(i_dev); 312 pcm_lock(d); 313 rdch = i_dev->si_drv1; 314 wrch = i_dev->si_drv2; 315 i_dev->si_drv1 = NULL; 316 i_dev->si_drv2 = NULL; 317 318 SLIST_FOREACH(sce, &d->channels, link) { 319 if (sce->dsp_dev == i_dev) 320 break; 321 } 322 sce->dsp_dev = NULL; 323 destroy_dev(i_dev); 324 325 pcm_unlock(d); 326 327 if (rdch || wrch) { 328 refs = 0; 329 if (rdch) { 330 CHN_LOCK(rdch); 331 refs += pcm_chnref(rdch, -1); 332 chn_abort(rdch); /* won't sleep */ 333 rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); 334 chn_reset(rdch, 0); 335 pcm_chnrelease(rdch); 336 } 337 if (wrch) { 338 CHN_LOCK(wrch); 339 refs += pcm_chnref(wrch, -1); 340 /* 341 * XXX: Maybe the right behaviour is to abort on non_block. 342 * It seems that mplayer flushes the audio queue by quickly 343 * closing and re-opening. In FBSD, there's a long pause 344 * while the audio queue flushes that I presume isn't there in 345 * linux. 346 */ 347 chn_flush(wrch); /* may sleep */ 348 wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); 349 chn_reset(wrch, 0); 350 pcm_chnrelease(wrch); 351 } 352 353 pcm_lock(d); 354 /* 355 * If there are no more references, release the channels. 356 */ 357 if (refs == 0) { 358 if (pcm_getfakechan(d)) 359 pcm_getfakechan(d)->flags = 0; 360 /* What is this?!? */ 361 dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT); 362 } 363 pcm_unlock(d); 364 } 365 return 0; 366 } 367 368 static int 369 dsp_read(struct dev_read_args *ap) 370 { 371 struct cdev *i_dev = ap->a_head.a_dev; 372 struct uio *buf = ap->a_uio; 373 int flag = ap->a_ioflag; 374 struct pcm_channel *rdch, *wrch; 375 int ret; 376 377 getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD); 378 379 KASSERT(rdch, ("dsp_read: nonexistant channel")); 380 KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel")); 381 382 if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) { 383 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD); 384 return EINVAL; 385 } 386 if (!(rdch->flags & CHN_F_RUNNING)) 387 rdch->flags |= CHN_F_RUNNING; 388 ret = chn_read(rdch, buf, flag); 389 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD); 390 391 return ret; 392 } 393 394 static int 395 dsp_write(struct dev_write_args *ap) 396 { 397 struct cdev *i_dev = ap->a_head.a_dev; 398 struct uio *buf = ap->a_uio; 399 int flag = ap->a_ioflag; 400 struct pcm_channel *rdch, *wrch; 401 int ret; 402 403 getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR); 404 405 KASSERT(wrch, ("dsp_write: nonexistant channel")); 406 KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel")); 407 408 if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) { 409 relchns(i_dev, rdch, wrch, SD_F_PRIO_WR); 410 return EINVAL; 411 } 412 if (!(wrch->flags & CHN_F_RUNNING)) 413 wrch->flags |= CHN_F_RUNNING; 414 ret = chn_write(wrch, buf, flag); 415 relchns(i_dev, rdch, wrch, SD_F_PRIO_WR); 416 417 return ret; 418 } 419 420 static int 421 dsp_ioctl(struct dev_ioctl_args *ap) 422 { 423 struct cdev *i_dev = ap->a_head.a_dev; 424 u_long cmd = ap->a_cmd; 425 caddr_t arg = ap->a_data; 426 struct pcm_channel *chn, *rdch, *wrch; 427 struct snddev_info *d; 428 int kill; 429 int ret = 0, *arg_i = (int *)arg, tmp; 430 431 d = dsp_get_info(i_dev); 432 getchns(i_dev, &rdch, &wrch, 0); 433 434 kill = 0; 435 if (wrch && (wrch->flags & CHN_F_DEAD)) 436 kill |= 1; 437 if (rdch && (rdch->flags & CHN_F_DEAD)) 438 kill |= 2; 439 if (kill == 3) { 440 relchns(i_dev, rdch, wrch, 0); 441 return EINVAL; 442 } 443 if (kill & 1) 444 wrch = NULL; 445 if (kill & 2) 446 rdch = NULL; 447 448 /* 449 * 4Front OSS specifies that dsp devices allow mixer controls to 450 * control PCM == their volume. 451 */ 452 if (IOCGROUP(cmd) == 'M') { 453 /* 454 * For now only set the channel volume for vchans, pass 455 * all others to the mixer. 456 */ 457 if (wrch != NULL && wrch->flags & CHN_F_VIRTUAL && 458 (cmd & 0xff) == SOUND_MIXER_PCM) { 459 if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { 460 int vol_raw = *(int *)arg; 461 int vol_left, vol_right; 462 463 vol_left = min(vol_raw & 0x00ff, 100); 464 vol_right = min((vol_raw & 0xff00) >> 8, 100); 465 ret = chn_setvolume(wrch, vol_left, vol_right); 466 } else { 467 *(int *)arg = wrch->volume; 468 } 469 } else { 470 ap->a_head.a_dev = d->mixer_dev; 471 ret = mixer_ioctl(ap); 472 } 473 474 relchns(i_dev, rdch, wrch, 0); 475 return ret; 476 } 477 478 switch(cmd) { 479 #ifdef OLDPCM_IOCTL 480 /* 481 * we start with the new ioctl interface. 482 */ 483 case AIONWRITE: /* how many bytes can write ? */ 484 if (wrch) { 485 CHN_LOCK(wrch); 486 /* 487 if (wrch && wrch->bufhard.dl) 488 while (chn_wrfeed(wrch) == 0); 489 */ 490 *arg_i = sndbuf_getfree(wrch->bufsoft); 491 CHN_UNLOCK(wrch); 492 } else { 493 *arg_i = 0; 494 ret = EINVAL; 495 } 496 break; 497 498 case AIOSSIZE: /* set the current blocksize */ 499 { 500 struct snd_size *p = (struct snd_size *)arg; 501 502 p->play_size = 0; 503 p->rec_size = 0; 504 if (wrch) { 505 CHN_LOCK(wrch); 506 chn_setblocksize(wrch, 2, p->play_size); 507 p->play_size = sndbuf_getblksz(wrch->bufsoft); 508 CHN_UNLOCK(wrch); 509 } 510 if (rdch) { 511 CHN_LOCK(rdch); 512 chn_setblocksize(rdch, 2, p->rec_size); 513 p->rec_size = sndbuf_getblksz(rdch->bufsoft); 514 CHN_UNLOCK(rdch); 515 } 516 } 517 break; 518 case AIOGSIZE: /* get the current blocksize */ 519 { 520 struct snd_size *p = (struct snd_size *)arg; 521 522 if (wrch) { 523 CHN_LOCK(wrch); 524 p->play_size = sndbuf_getblksz(wrch->bufsoft); 525 CHN_UNLOCK(wrch); 526 } 527 if (rdch) { 528 CHN_LOCK(rdch); 529 p->rec_size = sndbuf_getblksz(rdch->bufsoft); 530 CHN_UNLOCK(rdch); 531 } 532 } 533 break; 534 535 case AIOSFMT: 536 case AIOGFMT: 537 { 538 snd_chan_param *p = (snd_chan_param *)arg; 539 540 if (cmd == AIOSFMT && 541 ((p->play_format != 0 && p->play_rate == 0) || 542 (p->rec_format != 0 && p->rec_rate == 0))) { 543 ret = EINVAL; 544 break; 545 } 546 if (wrch) { 547 CHN_LOCK(wrch); 548 if (cmd == AIOSFMT && p->play_format != 0) { 549 chn_setformat(wrch, p->play_format); 550 chn_setspeed(wrch, p->play_rate); 551 } 552 p->play_rate = wrch->speed; 553 p->play_format = wrch->format; 554 CHN_UNLOCK(wrch); 555 } else { 556 p->play_rate = 0; 557 p->play_format = 0; 558 } 559 if (rdch) { 560 CHN_LOCK(rdch); 561 if (cmd == AIOSFMT && p->rec_format != 0) { 562 chn_setformat(rdch, p->rec_format); 563 chn_setspeed(rdch, p->rec_rate); 564 } 565 p->rec_rate = rdch->speed; 566 p->rec_format = rdch->format; 567 CHN_UNLOCK(rdch); 568 } else { 569 p->rec_rate = 0; 570 p->rec_format = 0; 571 } 572 } 573 break; 574 575 case AIOGCAP: /* get capabilities */ 576 { 577 snd_capabilities *p = (snd_capabilities *)arg; 578 struct pcmchan_caps *pcaps = NULL, *rcaps = NULL; 579 struct cdev *pdev; 580 581 if (rdch) { 582 CHN_LOCK(rdch); 583 rcaps = chn_getcaps(rdch); 584 } 585 if (wrch) { 586 CHN_LOCK(wrch); 587 pcaps = chn_getcaps(wrch); 588 } 589 p->rate_min = max(rcaps? rcaps->minspeed : 0, 590 pcaps? pcaps->minspeed : 0); 591 p->rate_max = min(rcaps? rcaps->maxspeed : 1000000, 592 pcaps? pcaps->maxspeed : 1000000); 593 p->bufsize = min(rdch? sndbuf_getsize(rdch->bufsoft) : 1000000, 594 wrch? sndbuf_getsize(wrch->bufsoft) : 1000000); 595 /* XXX bad on sb16 */ 596 p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) & 597 (wrch? chn_getformats(wrch) : 0xffffffff); 598 if (rdch && wrch) 599 p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX; 600 pdev = d->mixer_dev; 601 p->mixers = 1; /* default: one mixer */ 602 p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0; 603 p->left = p->right = 100; 604 if (rdch) 605 CHN_UNLOCK(rdch); 606 if (wrch) 607 CHN_UNLOCK(wrch); 608 } 609 break; 610 611 case AIOSTOP: 612 if (*arg_i == AIOSYNC_PLAY && wrch) { 613 CHN_LOCK(wrch); 614 *arg_i = chn_abort(wrch); 615 CHN_UNLOCK(wrch); 616 } else if (*arg_i == AIOSYNC_CAPTURE && rdch) { 617 CHN_LOCK(rdch); 618 *arg_i = chn_abort(rdch); 619 CHN_UNLOCK(rdch); 620 } else { 621 kprintf("AIOSTOP: bad channel 0x%x\n", *arg_i); 622 *arg_i = 0; 623 } 624 break; 625 626 case AIOSYNC: 627 kprintf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n", 628 ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos); 629 break; 630 #endif 631 /* 632 * here follow the standard ioctls (filio.h etc.) 633 */ 634 case FIONREAD: /* get # bytes to read */ 635 if (rdch) { 636 CHN_LOCK(rdch); 637 /* if (rdch && rdch->bufhard.dl) 638 while (chn_rdfeed(rdch) == 0); 639 */ 640 *arg_i = sndbuf_getready(rdch->bufsoft); 641 CHN_UNLOCK(rdch); 642 } else { 643 *arg_i = 0; 644 ret = EINVAL; 645 } 646 break; 647 648 case FIOASYNC: /*set/clear async i/o */ 649 DEB( kprintf("FIOASYNC\n") ; ) 650 break; 651 652 case SNDCTL_DSP_NONBLOCK: 653 case FIONBIO: /* set/clear non-blocking i/o */ 654 if (rdch) { 655 CHN_LOCK(rdch); 656 if (*arg_i) 657 rdch->flags |= CHN_F_NBIO; 658 else 659 rdch->flags &= ~CHN_F_NBIO; 660 CHN_UNLOCK(rdch); 661 } 662 if (wrch) { 663 CHN_LOCK(wrch); 664 if (*arg_i) 665 wrch->flags |= CHN_F_NBIO; 666 else 667 wrch->flags &= ~CHN_F_NBIO; 668 CHN_UNLOCK(wrch); 669 } 670 break; 671 672 /* 673 * Finally, here is the linux-compatible ioctl interface 674 */ 675 #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) 676 case THE_REAL_SNDCTL_DSP_GETBLKSIZE: 677 case SNDCTL_DSP_GETBLKSIZE: 678 chn = wrch ? wrch : rdch; 679 if (chn) { 680 CHN_LOCK(chn); 681 *arg_i = sndbuf_getblksz(chn->bufsoft); 682 CHN_UNLOCK(chn); 683 } else { 684 *arg_i = 0; 685 ret = EINVAL; 686 } 687 break ; 688 689 case SNDCTL_DSP_SETBLKSIZE: 690 RANGE(*arg_i, 16, 65536); 691 if (wrch) { 692 CHN_LOCK(wrch); 693 chn_setblocksize(wrch, 2, *arg_i); 694 CHN_UNLOCK(wrch); 695 } 696 if (rdch) { 697 CHN_LOCK(rdch); 698 chn_setblocksize(rdch, 2, *arg_i); 699 CHN_UNLOCK(rdch); 700 } 701 break; 702 703 case SNDCTL_DSP_RESET: 704 DEB(kprintf("dsp reset\n")); 705 if (wrch) { 706 CHN_LOCK(wrch); 707 chn_abort(wrch); 708 chn_resetbuf(wrch); 709 CHN_UNLOCK(wrch); 710 } 711 if (rdch) { 712 CHN_LOCK(rdch); 713 chn_abort(rdch); 714 chn_resetbuf(rdch); 715 CHN_UNLOCK(rdch); 716 } 717 break; 718 719 case SNDCTL_DSP_SYNC: 720 DEB(kprintf("dsp sync\n")); 721 /* chn_sync may sleep */ 722 if (wrch) { 723 CHN_LOCK(wrch); 724 chn_sync(wrch, sndbuf_getsize(wrch->bufsoft) - 4); 725 CHN_UNLOCK(wrch); 726 } 727 break; 728 729 case SNDCTL_DSP_SPEED: 730 /* chn_setspeed may sleep */ 731 tmp = 0; 732 if (wrch) { 733 CHN_LOCK(wrch); 734 ret = chn_setspeed(wrch, *arg_i); 735 tmp = wrch->speed; 736 CHN_UNLOCK(wrch); 737 } 738 if (rdch && ret == 0) { 739 CHN_LOCK(rdch); 740 ret = chn_setspeed(rdch, *arg_i); 741 if (tmp == 0) 742 tmp = rdch->speed; 743 CHN_UNLOCK(rdch); 744 } 745 *arg_i = tmp; 746 break; 747 748 case SOUND_PCM_READ_RATE: 749 chn = wrch ? wrch : rdch; 750 if (chn) { 751 CHN_LOCK(chn); 752 *arg_i = chn->speed; 753 CHN_UNLOCK(chn); 754 } else { 755 *arg_i = 0; 756 ret = EINVAL; 757 } 758 break; 759 760 case SNDCTL_DSP_STEREO: 761 tmp = -1; 762 *arg_i = (*arg_i)? AFMT_STEREO : 0; 763 if (wrch) { 764 CHN_LOCK(wrch); 765 ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); 766 tmp = (wrch->format & AFMT_STEREO)? 1 : 0; 767 CHN_UNLOCK(wrch); 768 } 769 if (rdch && ret == 0) { 770 CHN_LOCK(rdch); 771 ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i); 772 if (tmp == -1) 773 tmp = (rdch->format & AFMT_STEREO)? 1 : 0; 774 CHN_UNLOCK(rdch); 775 } 776 *arg_i = tmp; 777 break; 778 779 case SOUND_PCM_WRITE_CHANNELS: 780 /* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */ 781 if (*arg_i != 0) { 782 tmp = 0; 783 *arg_i = (*arg_i != 1)? AFMT_STEREO : 0; 784 if (wrch) { 785 CHN_LOCK(wrch); 786 ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); 787 tmp = (wrch->format & AFMT_STEREO)? 2 : 1; 788 CHN_UNLOCK(wrch); 789 } 790 if (rdch && ret == 0) { 791 CHN_LOCK(rdch); 792 ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i); 793 if (tmp == 0) 794 tmp = (rdch->format & AFMT_STEREO)? 2 : 1; 795 CHN_UNLOCK(rdch); 796 } 797 *arg_i = tmp; 798 } else { 799 chn = wrch ? wrch : rdch; 800 CHN_LOCK(chn); 801 *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; 802 CHN_UNLOCK(chn); 803 } 804 break; 805 806 case SOUND_PCM_READ_CHANNELS: 807 chn = wrch ? wrch : rdch; 808 if (chn) { 809 CHN_LOCK(chn); 810 *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; 811 CHN_UNLOCK(chn); 812 } else { 813 *arg_i = 0; 814 ret = EINVAL; 815 } 816 break; 817 818 case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */ 819 chn = wrch ? wrch : rdch; 820 if (chn) { 821 CHN_LOCK(chn); 822 *arg_i = chn_getformats(chn); 823 CHN_UNLOCK(chn); 824 } else { 825 *arg_i = 0; 826 ret = EINVAL; 827 } 828 break ; 829 830 case SNDCTL_DSP_SETFMT: /* sets _one_ format */ 831 if ((*arg_i != AFMT_QUERY)) { 832 tmp = 0; 833 if (wrch) { 834 CHN_LOCK(wrch); 835 ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO)); 836 tmp = wrch->format & ~AFMT_STEREO; 837 CHN_UNLOCK(wrch); 838 } 839 if (rdch && ret == 0) { 840 CHN_LOCK(rdch); 841 ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO)); 842 if (tmp == 0) 843 tmp = rdch->format & ~AFMT_STEREO; 844 CHN_UNLOCK(rdch); 845 } 846 *arg_i = tmp; 847 } else { 848 chn = wrch ? wrch : rdch; 849 CHN_LOCK(chn); 850 *arg_i = chn->format & ~AFMT_STEREO; 851 CHN_UNLOCK(chn); 852 } 853 break; 854 855 case SNDCTL_DSP_SETFRAGMENT: 856 DEB(kprintf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg)); 857 { 858 u_int32_t fragln = (*arg_i) & 0x0000ffff; 859 u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16; 860 u_int32_t fragsz; 861 u_int32_t r_maxfrags, r_fragsz; 862 863 RANGE(fragln, 4, 16); 864 fragsz = 1 << fragln; 865 866 if (maxfrags == 0) 867 maxfrags = CHN_2NDBUFMAXSIZE / fragsz; 868 if (maxfrags < 2) 869 maxfrags = 2; 870 if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE) 871 maxfrags = CHN_2NDBUFMAXSIZE / fragsz; 872 873 DEB(kprintf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz)); 874 if (rdch) { 875 CHN_LOCK(rdch); 876 ret = chn_setblocksize(rdch, maxfrags, fragsz); 877 r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft); 878 r_fragsz = sndbuf_getblksz(rdch->bufsoft); 879 CHN_UNLOCK(rdch); 880 } else { 881 r_maxfrags = maxfrags; 882 r_fragsz = fragsz; 883 } 884 if (wrch && ret == 0) { 885 CHN_LOCK(wrch); 886 ret = chn_setblocksize(wrch, maxfrags, fragsz); 887 maxfrags = sndbuf_getblkcnt(wrch->bufsoft); 888 fragsz = sndbuf_getblksz(wrch->bufsoft); 889 CHN_UNLOCK(wrch); 890 } else { /* use whatever came from the read channel */ 891 maxfrags = r_maxfrags; 892 fragsz = r_fragsz; 893 } 894 895 fragln = 0; 896 while (fragsz > 1) { 897 fragln++; 898 fragsz >>= 1; 899 } 900 *arg_i = (maxfrags << 16) | fragln; 901 } 902 break; 903 904 case SNDCTL_DSP_GETISPACE: 905 /* return the size of data available in the input queue */ 906 { 907 audio_buf_info *a = (audio_buf_info *)arg; 908 if (rdch) { 909 struct snd_dbuf *bs = rdch->bufsoft; 910 911 CHN_LOCK(rdch); 912 a->bytes = sndbuf_getready(bs); 913 a->fragments = a->bytes / sndbuf_getblksz(bs); 914 a->fragstotal = sndbuf_getblkcnt(bs); 915 a->fragsize = sndbuf_getblksz(bs); 916 CHN_UNLOCK(rdch); 917 } 918 } 919 break; 920 921 case SNDCTL_DSP_GETOSPACE: 922 /* return space available in the output queue */ 923 { 924 audio_buf_info *a = (audio_buf_info *)arg; 925 if (wrch) { 926 struct snd_dbuf *bs = wrch->bufsoft; 927 928 CHN_LOCK(wrch); 929 /* XXX abusive DMA update: chn_wrupdate(wrch); */ 930 a->bytes = sndbuf_getfree(bs); 931 a->fragments = a->bytes / sndbuf_getblksz(bs); 932 a->fragstotal = sndbuf_getblkcnt(bs); 933 a->fragsize = sndbuf_getblksz(bs); 934 CHN_UNLOCK(wrch); 935 } 936 } 937 break; 938 939 case SNDCTL_DSP_GETIPTR: 940 { 941 count_info *a = (count_info *)arg; 942 if (rdch) { 943 struct snd_dbuf *bs = rdch->bufsoft; 944 945 CHN_LOCK(rdch); 946 /* XXX abusive DMA update: chn_rdupdate(rdch); */ 947 a->bytes = sndbuf_gettotal(bs); 948 a->blocks = sndbuf_getblocks(bs) - rdch->blocks; 949 a->ptr = sndbuf_getreadyptr(bs); 950 rdch->blocks = sndbuf_getblocks(bs); 951 CHN_UNLOCK(rdch); 952 } else 953 ret = EINVAL; 954 } 955 break; 956 957 case SNDCTL_DSP_GETOPTR: 958 { 959 count_info *a = (count_info *)arg; 960 if (wrch) { 961 struct snd_dbuf *bs = wrch->bufsoft; 962 963 CHN_LOCK(wrch); 964 /* XXX abusive DMA update: chn_wrupdate(wrch); */ 965 a->bytes = sndbuf_gettotal(bs); 966 a->blocks = sndbuf_getblocks(bs) - wrch->blocks; 967 a->ptr = sndbuf_getreadyptr(bs); 968 wrch->blocks = sndbuf_getblocks(bs); 969 CHN_UNLOCK(wrch); 970 } else 971 ret = EINVAL; 972 } 973 break; 974 975 case SNDCTL_DSP_GETCAPS: 976 *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER; 977 if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX)) 978 *arg_i |= DSP_CAP_DUPLEX; 979 break; 980 981 case SOUND_PCM_READ_BITS: 982 chn = wrch ? wrch : rdch; 983 if (chn) { 984 CHN_LOCK(chn); 985 if (chn->format & AFMT_8BIT) 986 *arg_i = 8; 987 else if (chn->format & AFMT_16BIT) 988 *arg_i = 16; 989 else if (chn->format & AFMT_24BIT) 990 *arg_i = 24; 991 else if (chn->format & AFMT_32BIT) 992 *arg_i = 32; 993 else 994 ret = EINVAL; 995 CHN_UNLOCK(chn); 996 } else { 997 *arg_i = 0; 998 ret = EINVAL; 999 } 1000 break; 1001 1002 case SNDCTL_DSP_SETTRIGGER: 1003 if (rdch) { 1004 CHN_LOCK(rdch); 1005 rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER); 1006 if (*arg_i & PCM_ENABLE_INPUT) 1007 chn_start(rdch, 1); 1008 else 1009 rdch->flags |= CHN_F_NOTRIGGER; 1010 CHN_UNLOCK(rdch); 1011 } 1012 if (wrch) { 1013 CHN_LOCK(wrch); 1014 wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER); 1015 if (*arg_i & PCM_ENABLE_OUTPUT) 1016 chn_start(wrch, 1); 1017 else 1018 wrch->flags |= CHN_F_NOTRIGGER; 1019 CHN_UNLOCK(wrch); 1020 } 1021 break; 1022 1023 case SNDCTL_DSP_GETTRIGGER: 1024 *arg_i = 0; 1025 if (wrch) { 1026 CHN_LOCK(wrch); 1027 if (wrch->flags & CHN_F_TRIGGERED) 1028 *arg_i |= PCM_ENABLE_OUTPUT; 1029 CHN_UNLOCK(wrch); 1030 } 1031 if (rdch) { 1032 CHN_LOCK(rdch); 1033 if (rdch->flags & CHN_F_TRIGGERED) 1034 *arg_i |= PCM_ENABLE_INPUT; 1035 CHN_UNLOCK(rdch); 1036 } 1037 break; 1038 1039 case SNDCTL_DSP_GETODELAY: 1040 if (wrch) { 1041 struct snd_dbuf *b = wrch->bufhard; 1042 struct snd_dbuf *bs = wrch->bufsoft; 1043 1044 CHN_LOCK(wrch); 1045 /* XXX abusive DMA update: chn_wrupdate(wrch); */ 1046 *arg_i = sndbuf_getready(b) + sndbuf_getready(bs); 1047 CHN_UNLOCK(wrch); 1048 } else 1049 ret = EINVAL; 1050 break; 1051 1052 case SNDCTL_DSP_POST: 1053 if (wrch) { 1054 CHN_LOCK(wrch); 1055 wrch->flags &= ~CHN_F_NOTRIGGER; 1056 chn_start(wrch, 1); 1057 CHN_UNLOCK(wrch); 1058 } 1059 break; 1060 1061 case SNDCTL_DSP_SETDUPLEX: 1062 /* 1063 * switch to full-duplex mode if card is in half-duplex 1064 * mode and is able to work in full-duplex mode 1065 */ 1066 if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX)) 1067 dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX); 1068 break; 1069 1070 case SNDCTL_DSP_MAPINBUF: 1071 case SNDCTL_DSP_MAPOUTBUF: 1072 case SNDCTL_DSP_SETSYNCRO: 1073 /* undocumented */ 1074 1075 case SNDCTL_DSP_SUBDIVIDE: 1076 case SOUND_PCM_WRITE_FILTER: 1077 case SOUND_PCM_READ_FILTER: 1078 /* dunno what these do, don't sound important */ 1079 1080 default: 1081 DEB(kprintf("default ioctl fn 0x%08lx fail\n", cmd)); 1082 ret = EINVAL; 1083 break; 1084 } 1085 relchns(i_dev, rdch, wrch, 0); 1086 return ret; 1087 } 1088 1089 static struct filterops dsp_read_filtops = 1090 { FILTEROP_ISFD, NULL, dsp_filter_detach, dsp_filter_read }; 1091 static struct filterops dsp_write_filtops = 1092 { FILTEROP_ISFD, NULL, dsp_filter_detach, dsp_filter_write }; 1093 1094 static int 1095 dsp_kqfilter(struct dev_kqfilter_args *ap) 1096 { 1097 struct knote *kn = ap->a_kn; 1098 struct klist *klist; 1099 struct cdev *i_dev = ap->a_head.a_dev; 1100 struct pcm_channel *wrch = NULL, *rdch = NULL; 1101 struct snd_dbuf *bs = NULL; 1102 1103 getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 1104 1105 switch (kn->kn_filter) { 1106 case EVFILT_READ: 1107 if (rdch) { 1108 kn->kn_fop = &dsp_read_filtops; 1109 kn->kn_hook = (caddr_t)rdch; 1110 bs = rdch->bufsoft; 1111 ap->a_result = 0; 1112 } 1113 break; 1114 case EVFILT_WRITE: 1115 if (wrch) { 1116 kn->kn_fop = &dsp_write_filtops; 1117 kn->kn_hook = (caddr_t)wrch; 1118 bs = wrch->bufsoft; 1119 ap->a_result = 0; 1120 } 1121 break; 1122 default: 1123 ap->a_result = EOPNOTSUPP; 1124 break; 1125 } 1126 1127 if (ap->a_result == 0) { 1128 klist = &sndbuf_getkq(bs)->ki_note; 1129 knote_insert(klist, kn); 1130 } 1131 1132 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 1133 1134 return (0); 1135 } 1136 1137 static void 1138 dsp_filter_detach(struct knote *kn) 1139 { 1140 struct pcm_channel *ch = (struct pcm_channel *)kn->kn_hook; 1141 struct snd_dbuf *bs = ch->bufsoft; 1142 struct klist *klist; 1143 1144 CHN_LOCK(ch); 1145 klist = &sndbuf_getkq(bs)->ki_note; 1146 knote_remove(klist, kn); 1147 CHN_UNLOCK(ch); 1148 } 1149 1150 static int 1151 dsp_filter_read(struct knote *kn, long hint) 1152 { 1153 struct pcm_channel *rdch = (struct pcm_channel *)kn->kn_hook; 1154 struct thread *td = curthread; 1155 int ready; 1156 1157 CHN_LOCK(rdch); 1158 ready = chn_poll(rdch, 1, td); 1159 CHN_UNLOCK(rdch); 1160 1161 return (ready); 1162 } 1163 1164 static int 1165 dsp_filter_write(struct knote *kn, long hint) 1166 { 1167 struct pcm_channel *wrch = (struct pcm_channel *)kn->kn_hook; 1168 struct thread *td = curthread; 1169 int ready; 1170 1171 CHN_LOCK(wrch); 1172 ready = chn_poll(wrch, 1, td); 1173 CHN_UNLOCK(wrch); 1174 1175 return (ready); 1176 } 1177 1178 static int 1179 dsp_mmap(struct dev_mmap_args *ap) 1180 { 1181 struct cdev *i_dev = ap->a_head.a_dev; 1182 vm_offset_t offset = ap->a_offset; 1183 int nprot = ap->a_nprot; 1184 struct pcm_channel *wrch = NULL, *rdch = NULL, *c; 1185 1186 if (nprot & PROT_EXEC) 1187 return -1; 1188 1189 getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 1190 #if 0 1191 /* 1192 * XXX the linux api uses the nprot to select read/write buffer 1193 * our vm system doesn't allow this, so force write buffer 1194 */ 1195 1196 if (wrch && (nprot & PROT_WRITE)) { 1197 c = wrch; 1198 } else if (rdch && (nprot & PROT_READ)) { 1199 c = rdch; 1200 } else { 1201 return -1; 1202 } 1203 #else 1204 c = wrch; 1205 #endif 1206 1207 if (c == NULL) { 1208 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 1209 return -1; 1210 } 1211 1212 if (offset >= sndbuf_getsize(c->bufsoft)) { 1213 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 1214 return -1; 1215 } 1216 1217 if (!(c->flags & CHN_F_MAPPED)) 1218 c->flags |= CHN_F_MAPPED; 1219 1220 ap->a_result = atop(vtophys(sndbuf_getbufofs(c->bufsoft, offset))); 1221 relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); 1222 1223 return (0); 1224 } 1225 1226 /* 1227 * for i = 0 to channels of device N 1228 * if dspN.i isn't busy and in the right dir, create a dev_t and return it 1229 */ 1230 int 1231 dsp_clone(struct dev_clone_args *ap) 1232 { 1233 struct cdev *i_dev = ap->a_head.a_dev; 1234 struct cdev *pdev; 1235 struct snddev_info *pcm_dev; 1236 struct snddev_channel *pcm_chan; 1237 struct pcm_channel *c; 1238 int err = EBUSY; 1239 int dir; 1240 1241 pcm_dev = dsp_get_info(i_dev); 1242 1243 if (pcm_dev == NULL) 1244 return (ENODEV); 1245 1246 dir = ap->a_mode & FWRITE ? PCMDIR_PLAY : PCMDIR_REC; 1247 1248 retry_chnalloc: 1249 SLIST_FOREACH(pcm_chan, &pcm_dev->channels, link) { 1250 c = pcm_chan->channel; 1251 CHN_LOCK(c); 1252 pdev = pcm_chan->dsp_dev; 1253 1254 /* 1255 * Make sure that the channel has not been assigned 1256 * to a device yet (and vice versa). 1257 * The direction has to match and the channel may not 1258 * be busy. 1259 * dsp_open will use exactly this channel number to 1260 * avoid (possible?) races between clone and open. 1261 */ 1262 if (pdev == NULL && c->direction == dir && 1263 !(c->flags & CHN_F_BUSY)) { 1264 CHN_UNLOCK(c); 1265 pcm_lock(pcm_dev); 1266 pcm_chan->dsp_dev = make_only_dev(&dsp_cdevsw, 1267 PCMMKMINOR(PCMUNIT(i_dev), pcm_chan->chan_num), 1268 UID_ROOT, GID_WHEEL, 1269 0666, 1270 "%s.%d", 1271 devtoname(i_dev), 1272 pcm_chan->chan_num); 1273 pcm_unlock(pcm_dev); 1274 1275 ap->a_dev = pcm_chan->dsp_dev; 1276 return (0); 1277 } 1278 CHN_UNLOCK(c); 1279 1280 #if DEBUG 1281 if ((pdev != NULL) && (pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) { 1282 kprintf("%s: dangling device\n", devtoname(pdev)); 1283 } 1284 #endif 1285 } 1286 1287 /* no channel available, create vchannel */ 1288 if (dir == PCMDIR_PLAY && 1289 pcm_dev->vchancount > 0 && 1290 pcm_dev->vchancount < snd_maxautovchans && 1291 pcm_dev->devcount < PCMMAXCHAN) { 1292 err = pcm_setvchans(pcm_dev, pcm_dev->vchancount + 1); 1293 if (err == 0) 1294 goto retry_chnalloc; 1295 /* 1296 * If we can't use vchans, because the main output is 1297 * blocked for something else, we should not return 1298 * any vchan create error, but the more descriptive 1299 * EBUSY. 1300 * After all, the user didn't ask us to clone, but 1301 * only opened /dev/dsp. 1302 */ 1303 err = EBUSY; 1304 } 1305 1306 return (err); 1307 } 1308