1 /* $OpenBSD: ossaudio.c,v 1.3 2001/05/24 04:21:03 aaron Exp $ */ 2 /* $NetBSD: ossaudio.c,v 1.5 1998/03/23 00:39:18 augustss Exp $ */ 3 4 /* 5 * Copyright (c) 1997 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the NetBSD 19 * Foundation, Inc. and its contributors. 20 * 4. Neither the name of The NetBSD Foundation nor the names of its 21 * contributors may be used to endorse or promote products derived 22 * from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 28 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGE. 35 */ 36 37 /* 38 * This is an OSS (Linux) sound API emulator. 39 * It provides the essentials of the API. 40 */ 41 42 /* XXX This file is essentially the same as sys/compat/ossaudio.c. 43 * With some preprocessor magic it could be the same file. 44 */ 45 46 #include <string.h> 47 #include <sys/types.h> 48 #include <sys/ioctl.h> 49 #include <sys/audioio.h> 50 #include <sys/stat.h> 51 #include <errno.h> 52 53 #include "soundcard.h" 54 #undef ioctl 55 56 #define GET_DEV(com) ((com) & 0xff) 57 58 #define TO_OSSVOL(x) ((x) * 100 / 255) 59 #define FROM_OSSVOL(x) ((x) * 255 / 100) 60 61 static struct audiodevinfo *getdevinfo(int); 62 63 static void setblocksize(int, struct audio_info *); 64 65 static int audio_ioctl(int, unsigned long, void *); 66 static int mixer_ioctl(int, unsigned long, void *); 67 68 #define INTARG (*(int*)argp) 69 70 int 71 _oss_ioctl(int fd, unsigned long com, void *argp) 72 { 73 if (IOCGROUP(com) == 'P') 74 return audio_ioctl(fd, com, argp); 75 else if (IOCGROUP(com) == 'M') 76 return mixer_ioctl(fd, com, argp); 77 else 78 return ioctl(fd, com, argp); 79 } 80 81 static int 82 audio_ioctl(int fd, unsigned long com, void *argp) 83 { 84 85 struct audio_info tmpinfo; 86 struct audio_offset tmpoffs; 87 struct audio_buf_info bufinfo; 88 struct count_info cntinfo; 89 struct audio_encoding tmpenc; 90 u_int u; 91 int idat, idata; 92 int retval; 93 94 switch (com) { 95 case SNDCTL_DSP_RESET: 96 retval = ioctl(fd, AUDIO_FLUSH, 0); 97 if (retval < 0) 98 return retval; 99 break; 100 case SNDCTL_DSP_SYNC: 101 case SNDCTL_DSP_POST: 102 retval = ioctl(fd, AUDIO_DRAIN, 0); 103 if (retval < 0) 104 return retval; 105 break; 106 case SNDCTL_DSP_SPEED: 107 AUDIO_INITINFO(&tmpinfo); 108 tmpinfo.play.sample_rate = 109 tmpinfo.record.sample_rate = INTARG; 110 (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); 111 /* fall into ... */ 112 case SOUND_PCM_READ_RATE: 113 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); 114 if (retval < 0) 115 return retval; 116 INTARG = tmpinfo.play.sample_rate; 117 break; 118 case SNDCTL_DSP_STEREO: 119 AUDIO_INITINFO(&tmpinfo); 120 tmpinfo.play.channels = 121 tmpinfo.record.channels = INTARG ? 2 : 1; 122 (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); 123 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); 124 if (retval < 0) 125 return retval; 126 INTARG = tmpinfo.play.channels - 1; 127 break; 128 case SNDCTL_DSP_GETBLKSIZE: 129 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); 130 if (retval < 0) 131 return retval; 132 setblocksize(fd, &tmpinfo); 133 INTARG = tmpinfo.blocksize; 134 break; 135 case SNDCTL_DSP_SETFMT: 136 AUDIO_INITINFO(&tmpinfo); 137 switch (INTARG) { 138 case AFMT_MU_LAW: 139 tmpinfo.play.precision = 140 tmpinfo.record.precision = 8; 141 tmpinfo.play.encoding = 142 tmpinfo.record.encoding = AUDIO_ENCODING_ULAW; 143 break; 144 case AFMT_A_LAW: 145 tmpinfo.play.precision = 146 tmpinfo.record.precision = 8; 147 tmpinfo.play.encoding = 148 tmpinfo.record.encoding = AUDIO_ENCODING_ALAW; 149 break; 150 case AFMT_U8: 151 tmpinfo.play.precision = 152 tmpinfo.record.precision = 8; 153 tmpinfo.play.encoding = 154 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR; 155 break; 156 case AFMT_S8: 157 tmpinfo.play.precision = 158 tmpinfo.record.precision = 8; 159 tmpinfo.play.encoding = 160 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR; 161 break; 162 case AFMT_S16_LE: 163 tmpinfo.play.precision = 164 tmpinfo.record.precision = 16; 165 tmpinfo.play.encoding = 166 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; 167 break; 168 case AFMT_S16_BE: 169 tmpinfo.play.precision = 170 tmpinfo.record.precision = 16; 171 tmpinfo.play.encoding = 172 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; 173 break; 174 case AFMT_U16_LE: 175 tmpinfo.play.precision = 176 tmpinfo.record.precision = 16; 177 tmpinfo.play.encoding = 178 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE; 179 break; 180 case AFMT_U16_BE: 181 tmpinfo.play.precision = 182 tmpinfo.record.precision = 16; 183 tmpinfo.play.encoding = 184 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE; 185 break; 186 default: 187 return EINVAL; 188 } 189 (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); 190 /* fall into ... */ 191 case SOUND_PCM_READ_BITS: 192 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); 193 if (retval < 0) 194 return retval; 195 switch (tmpinfo.play.encoding) { 196 case AUDIO_ENCODING_ULAW: 197 idat = AFMT_MU_LAW; 198 break; 199 case AUDIO_ENCODING_ALAW: 200 idat = AFMT_A_LAW; 201 break; 202 case AUDIO_ENCODING_SLINEAR_LE: 203 if (tmpinfo.play.precision == 16) 204 idat = AFMT_S16_LE; 205 else 206 idat = AFMT_S8; 207 break; 208 case AUDIO_ENCODING_SLINEAR_BE: 209 if (tmpinfo.play.precision == 16) 210 idat = AFMT_S16_BE; 211 else 212 idat = AFMT_S8; 213 break; 214 case AUDIO_ENCODING_ULINEAR_LE: 215 if (tmpinfo.play.precision == 16) 216 idat = AFMT_U16_LE; 217 else 218 idat = AFMT_U8; 219 break; 220 case AUDIO_ENCODING_ULINEAR_BE: 221 if (tmpinfo.play.precision == 16) 222 idat = AFMT_U16_BE; 223 else 224 idat = AFMT_U8; 225 break; 226 case AUDIO_ENCODING_ADPCM: 227 idat = AFMT_IMA_ADPCM; 228 break; 229 } 230 INTARG = idat; 231 break; 232 case SNDCTL_DSP_CHANNELS: 233 AUDIO_INITINFO(&tmpinfo); 234 tmpinfo.play.channels = 235 tmpinfo.record.channels = INTARG; 236 (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); 237 /* fall into ... */ 238 case SOUND_PCM_READ_CHANNELS: 239 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); 240 if (retval < 0) 241 return retval; 242 INTARG = tmpinfo.play.channels; 243 break; 244 case SOUND_PCM_WRITE_FILTER: 245 case SOUND_PCM_READ_FILTER: 246 errno = EINVAL; 247 return -1; /* XXX unimplemented */ 248 case SNDCTL_DSP_SUBDIVIDE: 249 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); 250 if (retval < 0) 251 return retval; 252 setblocksize(fd, &tmpinfo); 253 idat = INTARG; 254 if (idat == 0) 255 idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; 256 idat = (tmpinfo.play.buffer_size / idat) & -4; 257 AUDIO_INITINFO(&tmpinfo); 258 tmpinfo.blocksize = idat; 259 retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); 260 if (retval < 0) 261 return retval; 262 INTARG = tmpinfo.play.buffer_size / tmpinfo.blocksize; 263 break; 264 case SNDCTL_DSP_SETFRAGMENT: 265 AUDIO_INITINFO(&tmpinfo); 266 idat = INTARG; 267 if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) 268 return EINVAL; 269 tmpinfo.blocksize = 1 << (idat & 0xffff); 270 tmpinfo.hiwat = (idat >> 16) & 0x7fff; 271 if (tmpinfo.hiwat == 0) /* 0 means set to max */ 272 tmpinfo.hiwat = 65536; 273 (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); 274 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); 275 if (retval < 0) 276 return retval; 277 u = tmpinfo.blocksize; 278 for(idat = 0; u>1; idat++, u >>= 1) 279 ; 280 idat |= (tmpinfo.hiwat & 0x7fff) << 16; 281 INTARG = idat; 282 break; 283 case SNDCTL_DSP_GETFMTS: 284 for(idat = 0, tmpenc.index = 0; 285 ioctl(fd, AUDIO_GETENC, &tmpenc) == 0; 286 tmpenc.index++) { 287 if (tmpenc.flags & AUDIO_ENCODINGFLAG_EMULATED) 288 continue; /* Don't report emulated modes */ 289 switch(tmpenc.encoding) { 290 case AUDIO_ENCODING_ULAW: 291 idat |= AFMT_MU_LAW; 292 break; 293 case AUDIO_ENCODING_ALAW: 294 idat |= AFMT_A_LAW; 295 break; 296 case AUDIO_ENCODING_SLINEAR: 297 idat |= AFMT_S8; 298 break; 299 case AUDIO_ENCODING_SLINEAR_LE: 300 if (tmpenc.precision == 16) 301 idat |= AFMT_S16_LE; 302 else 303 idat |= AFMT_S8; 304 break; 305 case AUDIO_ENCODING_SLINEAR_BE: 306 if (tmpenc.precision == 16) 307 idat |= AFMT_S16_BE; 308 else 309 idat |= AFMT_S8; 310 break; 311 case AUDIO_ENCODING_ULINEAR: 312 idat |= AFMT_U8; 313 break; 314 case AUDIO_ENCODING_ULINEAR_LE: 315 if (tmpenc.precision == 16) 316 idat |= AFMT_U16_LE; 317 else 318 idat |= AFMT_U8; 319 break; 320 case AUDIO_ENCODING_ULINEAR_BE: 321 if (tmpenc.precision == 16) 322 idat |= AFMT_U16_BE; 323 else 324 idat |= AFMT_U8; 325 break; 326 case AUDIO_ENCODING_ADPCM: 327 idat |= AFMT_IMA_ADPCM; 328 break; 329 default: 330 break; 331 } 332 } 333 INTARG = idat; 334 break; 335 case SNDCTL_DSP_GETOSPACE: 336 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); 337 if (retval < 0) 338 return retval; 339 setblocksize(fd, &tmpinfo); 340 bufinfo.fragsize = tmpinfo.blocksize; 341 bufinfo.fragments = tmpinfo.hiwat - 342 (tmpinfo.play.seek + tmpinfo.blocksize - 1)/tmpinfo.blocksize; 343 bufinfo.fragstotal = tmpinfo.hiwat; 344 bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.play.seek; 345 *(struct audio_buf_info *)argp = bufinfo; 346 break; 347 case SNDCTL_DSP_GETISPACE: 348 retval = ioctl(fd, AUDIO_GETINFO, (caddr_t)&tmpinfo); 349 if (retval < 0) 350 return retval; 351 setblocksize(fd, &tmpinfo); 352 bufinfo.fragsize = tmpinfo.blocksize; 353 bufinfo.fragments = tmpinfo.hiwat - 354 (tmpinfo.record.seek + tmpinfo.blocksize - 1)/tmpinfo.blocksize; 355 bufinfo.fragstotal = tmpinfo.hiwat; 356 bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.record.seek; 357 *(struct audio_buf_info *)argp = bufinfo; 358 break; 359 case SNDCTL_DSP_NONBLOCK: 360 idat = 1; 361 retval = ioctl(fd, FIONBIO, &idat); 362 if (retval < 0) 363 return retval; 364 break; 365 case SNDCTL_DSP_GETCAPS: 366 retval = ioctl(fd, AUDIO_GETPROPS, (caddr_t)&idata); 367 if (retval < 0) 368 return retval; 369 idat = DSP_CAP_TRIGGER; /* pretend we have trigger */ 370 if (idata & AUDIO_PROP_FULLDUPLEX) 371 idat |= DSP_CAP_DUPLEX; 372 if (idata & AUDIO_PROP_MMAP) 373 idat |= DSP_CAP_MMAP; 374 INTARG = idat; 375 break; 376 #if 0 377 case SNDCTL_DSP_GETTRIGGER: 378 retval = ioctl(fd, AUDIO_GETINFO, (caddr_t)&tmpinfo); 379 if (retval < 0) 380 return retval; 381 idat = (tmpinfo.play.pause ? 0 : PCM_ENABLE_OUTPUT) | 382 (tmpinfo.record.pause ? 0 : PCM_ENABLE_INPUT); 383 retval = copyout(&idat, SCARG(uap, data), sizeof idat); 384 if (retval < 0) 385 return retval; 386 break; 387 case SNDCTL_DSP_SETTRIGGER: 388 AUDIO_INITINFO(&tmpinfo); 389 retval = copyin(SCARG(uap, data), &idat, sizeof idat); 390 if (retval < 0) 391 return retval; 392 tmpinfo.play.pause = (idat & PCM_ENABLE_OUTPUT) == 0; 393 tmpinfo.record.pause = (idat & PCM_ENABLE_INPUT) == 0; 394 (void) ioctl(fd, AUDIO_SETINFO, (caddr_t)&tmpinfo); 395 retval = copyout(&idat, SCARG(uap, data), sizeof idat); 396 if (retval < 0) 397 return retval; 398 break; 399 #else 400 case SNDCTL_DSP_GETTRIGGER: 401 case SNDCTL_DSP_SETTRIGGER: 402 /* XXX Do nothing for now. */ 403 INTARG = PCM_ENABLE_OUTPUT; 404 break; 405 #endif 406 case SNDCTL_DSP_GETIPTR: 407 retval = ioctl(fd, AUDIO_GETIOFFS, &tmpoffs); 408 if (retval < 0) 409 return retval; 410 cntinfo.bytes = tmpoffs.samples; 411 cntinfo.blocks = tmpoffs.deltablks; 412 cntinfo.ptr = tmpoffs.offset; 413 *(struct count_info *)argp = cntinfo; 414 break; 415 case SNDCTL_DSP_GETOPTR: 416 retval = ioctl(fd, AUDIO_GETOOFFS, &tmpoffs); 417 if (retval < 0) 418 return retval; 419 cntinfo.bytes = tmpoffs.samples; 420 cntinfo.blocks = tmpoffs.deltablks; 421 cntinfo.ptr = tmpoffs.offset; 422 *(struct count_info *)argp = cntinfo; 423 break; 424 case SNDCTL_DSP_MAPINBUF: 425 case SNDCTL_DSP_MAPOUTBUF: 426 case SNDCTL_DSP_SETSYNCRO: 427 case SNDCTL_DSP_SETDUPLEX: 428 case SNDCTL_DSP_PROFILE: 429 errno = EINVAL; 430 return -1; /* XXX unimplemented */ 431 default: 432 errno = EINVAL; 433 return -1; 434 } 435 436 return 0; 437 } 438 439 440 /* If the NetBSD mixer device should have more than 32 devices 441 * some will not be available to Linux */ 442 #define NETBSD_MAXDEVS 32 443 struct audiodevinfo { 444 int done; 445 dev_t dev; 446 int16_t devmap[SOUND_MIXER_NRDEVICES], 447 rdevmap[NETBSD_MAXDEVS]; 448 u_long devmask, recmask, stereomask; 449 u_long caps, source; 450 }; 451 452 /* 453 * Collect the audio device information to allow faster 454 * emulation of the Linux mixer ioctls. Cache the information 455 * to eliminate the overhead of repeating all the ioctls needed 456 * to collect the information. 457 */ 458 static struct audiodevinfo * 459 getdevinfo(int fd) 460 { 461 mixer_devinfo_t mi; 462 int i; 463 static struct { 464 char *name; 465 int code; 466 } *dp, devs[] = { 467 { AudioNmicrophone, SOUND_MIXER_MIC }, 468 { AudioNline, SOUND_MIXER_LINE }, 469 { AudioNcd, SOUND_MIXER_CD }, 470 { AudioNdac, SOUND_MIXER_PCM }, 471 { AudioNrecord, SOUND_MIXER_IMIX }, 472 { AudioNmaster, SOUND_MIXER_VOLUME }, 473 { AudioNtreble, SOUND_MIXER_TREBLE }, 474 { AudioNbass, SOUND_MIXER_BASS }, 475 { AudioNspeaker, SOUND_MIXER_SPEAKER }, 476 /* { AudioNheadphone, ?? },*/ 477 { AudioNoutput, SOUND_MIXER_OGAIN }, 478 { AudioNinput, SOUND_MIXER_IGAIN }, 479 /* { AudioNmaster, SOUND_MIXER_SPEAKER },*/ 480 /* { AudioNstereo, ?? },*/ 481 /* { AudioNmono, ?? },*/ 482 { AudioNfmsynth, SOUND_MIXER_SYNTH }, 483 /* { AudioNwave, SOUND_MIXER_PCM },*/ 484 { AudioNmidi, SOUND_MIXER_SYNTH }, 485 /* { AudioNmixerout, ?? },*/ 486 { 0, -1 } 487 }; 488 static struct audiodevinfo devcache = { 0 }; 489 struct audiodevinfo *di = &devcache; 490 struct stat sb; 491 492 /* Figure out what device it is so we can check if the 493 * cached data is valid. 494 */ 495 if (fstat(fd, &sb) < 0) 496 return 0; 497 if (di->done && di->dev == sb.st_dev) 498 return di; 499 500 di->done = 1; 501 di->dev = sb.st_dev; 502 di->devmask = 0; 503 di->recmask = 0; 504 di->stereomask = 0; 505 di->source = -1; 506 di->caps = 0; 507 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) 508 di->devmap[i] = -1; 509 for(i = 0; i < NETBSD_MAXDEVS; i++) 510 di->rdevmap[i] = -1; 511 for(i = 0; i < NETBSD_MAXDEVS; i++) { 512 mi.index = i; 513 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) 514 break; 515 switch(mi.type) { 516 case AUDIO_MIXER_VALUE: 517 for(dp = devs; dp->name; dp++) 518 if (strcmp(dp->name, mi.label.name) == 0) 519 break; 520 if (dp->code >= 0) { 521 di->devmap[dp->code] = i; 522 di->rdevmap[i] = dp->code; 523 di->devmask |= 1 << dp->code; 524 if (mi.un.v.num_channels == 2) 525 di->stereomask |= 1 << dp->code; 526 } 527 break; 528 case AUDIO_MIXER_ENUM: 529 if (strcmp(mi.label.name, AudioNsource) == 0) { 530 int j; 531 di->source = i; 532 for(j = 0; j < mi.un.e.num_mem; j++) 533 di->recmask |= 1 << di->rdevmap[mi.un.e.member[j].ord]; 534 di->caps = SOUND_CAP_EXCL_INPUT; 535 } 536 break; 537 case AUDIO_MIXER_SET: 538 if (strcmp(mi.label.name, AudioNsource) == 0) { 539 int j; 540 di->source = i; 541 for(j = 0; j < mi.un.s.num_mem; j++) { 542 int k, mask = mi.un.s.member[j].mask; 543 if (mask) { 544 for(k = 0; !(mask & 1); mask >>= 1, k++) 545 ; 546 di->recmask |= 1 << di->rdevmap[k]; 547 } 548 } 549 } 550 break; 551 } 552 } 553 return di; 554 } 555 556 int 557 mixer_ioctl(int fd, unsigned long com, void *argp) 558 { 559 struct audiodevinfo *di; 560 mixer_ctrl_t mc; 561 int idat; 562 int i; 563 int retval; 564 int l, r, n; 565 566 di = getdevinfo(fd); 567 if (di == 0) 568 return -1; 569 570 switch (com) { 571 case SOUND_MIXER_READ_RECSRC: 572 if (di->source == -1) 573 return EINVAL; 574 mc.dev = di->source; 575 if (di->caps & SOUND_CAP_EXCL_INPUT) { 576 mc.type = AUDIO_MIXER_ENUM; 577 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 578 if (retval < 0) 579 return retval; 580 idat = 1 << di->rdevmap[mc.un.ord]; 581 } else { 582 int k; 583 unsigned int mask; 584 mc.type = AUDIO_MIXER_SET; 585 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 586 if (retval < 0) 587 return retval; 588 idat = 0; 589 for(mask = mc.un.mask, k = 0; mask; mask >>= 1, k++) 590 if (mask & 1) 591 idat |= 1 << di->rdevmap[k]; 592 } 593 break; 594 case SOUND_MIXER_READ_DEVMASK: 595 idat = di->devmask; 596 break; 597 case SOUND_MIXER_READ_RECMASK: 598 idat = di->recmask; 599 break; 600 case SOUND_MIXER_READ_STEREODEVS: 601 idat = di->stereomask; 602 break; 603 case SOUND_MIXER_READ_CAPS: 604 idat = di->caps; 605 break; 606 case SOUND_MIXER_WRITE_RECSRC: 607 case SOUND_MIXER_WRITE_R_RECSRC: 608 if (di->source == -1) 609 return EINVAL; 610 mc.dev = di->source; 611 idat = INTARG; 612 if (di->caps & SOUND_CAP_EXCL_INPUT) { 613 mc.type = AUDIO_MIXER_ENUM; 614 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) 615 if (idat & (1 << i)) 616 break; 617 if (i >= SOUND_MIXER_NRDEVICES || 618 di->devmap[i] == -1) 619 return EINVAL; 620 mc.un.ord = di->devmap[i]; 621 } else { 622 mc.type = AUDIO_MIXER_SET; 623 mc.un.mask = 0; 624 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 625 if (idat & (1 << i)) { 626 if (di->devmap[i] == -1) 627 return EINVAL; 628 mc.un.mask |= 1 << di->devmap[i]; 629 } 630 } 631 } 632 return ioctl(fd, AUDIO_MIXER_WRITE, &mc); 633 default: 634 if (MIXER_READ(SOUND_MIXER_FIRST) <= com && 635 com < MIXER_READ(SOUND_MIXER_NRDEVICES)) { 636 n = GET_DEV(com); 637 if (di->devmap[n] == -1) 638 return EINVAL; 639 mc.dev = di->devmap[n]; 640 mc.type = AUDIO_MIXER_VALUE; 641 doread: 642 mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1; 643 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 644 if (retval < 0) 645 return retval; 646 if (mc.type != AUDIO_MIXER_VALUE) 647 return EINVAL; 648 if (mc.un.value.num_channels != 2) { 649 l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; 650 } else { 651 l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 652 r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 653 } 654 idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); 655 break; 656 } else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com && 657 com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) || 658 (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && 659 com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) { 660 n = GET_DEV(com); 661 if (di->devmap[n] == -1) 662 return EINVAL; 663 idat = INTARG; 664 l = FROM_OSSVOL( idat & 0xff); 665 r = FROM_OSSVOL((idat >> 8) & 0xff); 666 mc.dev = di->devmap[n]; 667 mc.type = AUDIO_MIXER_VALUE; 668 if (di->stereomask & (1<<n)) { 669 mc.un.value.num_channels = 2; 670 mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; 671 mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; 672 } else { 673 mc.un.value.num_channels = 1; 674 mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; 675 } 676 retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc); 677 if (retval < 0) 678 return retval; 679 if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && 680 com < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) 681 return 0; 682 goto doread; 683 } else { 684 errno = EINVAL; 685 return -1; 686 } 687 } 688 INTARG = idat; 689 return 0; 690 } 691 692 /* 693 * Check that the blocksize is a power of 2 as OSS wants. 694 * If not, set it to be. 695 */ 696 static void 697 setblocksize(int fd, struct audio_info *info) 698 { 699 struct audio_info set; 700 int s; 701 702 if (info->blocksize & (info->blocksize-1)) { 703 for(s = 32; s < info->blocksize; s <<= 1) 704 ; 705 AUDIO_INITINFO(&set); 706 set.blocksize = s; 707 ioctl(fd, AUDIO_SETINFO, &set); 708 ioctl(fd, AUDIO_GETINFO, info); 709 } 710 } 711 712