1 /* $OpenBSD: ossaudio.c,v 1.2 1998/05/25 21:37:46 provos 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 case SNDCTL_DSP_GETISPACE: 337 retval = ioctl(fd, AUDIO_GETINFO, (caddr_t)&tmpinfo); 338 if (retval < 0) 339 return retval; 340 setblocksize(fd, &tmpinfo); 341 bufinfo.fragsize = tmpinfo.blocksize; 342 bufinfo.fragments = /* XXX */ 343 bufinfo.fragstotal = tmpinfo.play.buffer_size / bufinfo.fragsize; 344 bufinfo.bytes = tmpinfo.play.buffer_size; 345 *(struct audio_buf_info *)argp = bufinfo; 346 break; 347 case SNDCTL_DSP_NONBLOCK: 348 idat = 1; 349 retval = ioctl(fd, FIONBIO, &idat); 350 if (retval < 0) 351 return retval; 352 break; 353 case SNDCTL_DSP_GETCAPS: 354 retval = ioctl(fd, AUDIO_GETPROPS, (caddr_t)&idata); 355 if (retval < 0) 356 return retval; 357 idat = DSP_CAP_TRIGGER; /* pretend we have trigger */ 358 if (idata & AUDIO_PROP_FULLDUPLEX) 359 idat |= DSP_CAP_DUPLEX; 360 if (idata & AUDIO_PROP_MMAP) 361 idat |= DSP_CAP_MMAP; 362 INTARG = idat; 363 break; 364 #if 0 365 case SNDCTL_DSP_GETTRIGGER: 366 retval = ioctl(fd, AUDIO_GETINFO, (caddr_t)&tmpinfo); 367 if (retval < 0) 368 return retval; 369 idat = (tmpinfo.play.pause ? 0 : PCM_ENABLE_OUTPUT) | 370 (tmpinfo.record.pause ? 0 : PCM_ENABLE_INPUT); 371 retval = copyout(&idat, SCARG(uap, data), sizeof idat); 372 if (retval < 0) 373 return retval; 374 break; 375 case SNDCTL_DSP_SETTRIGGER: 376 AUDIO_INITINFO(&tmpinfo); 377 retval = copyin(SCARG(uap, data), &idat, sizeof idat); 378 if (retval < 0) 379 return retval; 380 tmpinfo.play.pause = (idat & PCM_ENABLE_OUTPUT) == 0; 381 tmpinfo.record.pause = (idat & PCM_ENABLE_INPUT) == 0; 382 (void) ioctl(fd, AUDIO_SETINFO, (caddr_t)&tmpinfo); 383 retval = copyout(&idat, SCARG(uap, data), sizeof idat); 384 if (retval < 0) 385 return retval; 386 break; 387 #else 388 case SNDCTL_DSP_GETTRIGGER: 389 case SNDCTL_DSP_SETTRIGGER: 390 /* XXX Do nothing for now. */ 391 INTARG = PCM_ENABLE_OUTPUT; 392 break; 393 #endif 394 case SNDCTL_DSP_GETIPTR: 395 retval = ioctl(fd, AUDIO_GETIOFFS, &tmpoffs); 396 if (retval < 0) 397 return retval; 398 cntinfo.bytes = tmpoffs.samples; 399 cntinfo.blocks = tmpoffs.deltablks; 400 cntinfo.ptr = tmpoffs.offset; 401 *(struct count_info *)argp = cntinfo; 402 break; 403 case SNDCTL_DSP_GETOPTR: 404 retval = ioctl(fd, AUDIO_GETOOFFS, &tmpoffs); 405 if (retval < 0) 406 return retval; 407 cntinfo.bytes = tmpoffs.samples; 408 cntinfo.blocks = tmpoffs.deltablks; 409 cntinfo.ptr = tmpoffs.offset; 410 *(struct count_info *)argp = cntinfo; 411 break; 412 case SNDCTL_DSP_MAPINBUF: 413 case SNDCTL_DSP_MAPOUTBUF: 414 case SNDCTL_DSP_SETSYNCRO: 415 case SNDCTL_DSP_SETDUPLEX: 416 case SNDCTL_DSP_PROFILE: 417 errno = EINVAL; 418 return -1; /* XXX unimplemented */ 419 default: 420 errno = EINVAL; 421 return -1; 422 } 423 424 return 0; 425 } 426 427 428 /* If the NetBSD mixer device should have more than 32 devices 429 * some will not be available to Linux */ 430 #define NETBSD_MAXDEVS 32 431 struct audiodevinfo { 432 int done; 433 dev_t dev; 434 int16_t devmap[SOUND_MIXER_NRDEVICES], 435 rdevmap[NETBSD_MAXDEVS]; 436 u_long devmask, recmask, stereomask; 437 u_long caps, source; 438 }; 439 440 /* 441 * Collect the audio device information to allow faster 442 * emulation of the Linux mixer ioctls. Cache the information 443 * to eliminate the overhead of repeating all the ioctls needed 444 * to collect the information. 445 */ 446 static struct audiodevinfo * 447 getdevinfo(int fd) 448 { 449 mixer_devinfo_t mi; 450 int i; 451 static struct { 452 char *name; 453 int code; 454 } *dp, devs[] = { 455 { AudioNmicrophone, SOUND_MIXER_MIC }, 456 { AudioNline, SOUND_MIXER_LINE }, 457 { AudioNcd, SOUND_MIXER_CD }, 458 { AudioNdac, SOUND_MIXER_PCM }, 459 { AudioNrecord, SOUND_MIXER_IMIX }, 460 { AudioNmaster, SOUND_MIXER_VOLUME }, 461 { AudioNtreble, SOUND_MIXER_TREBLE }, 462 { AudioNbass, SOUND_MIXER_BASS }, 463 { AudioNspeaker, SOUND_MIXER_SPEAKER }, 464 /* { AudioNheadphone, ?? },*/ 465 { AudioNoutput, SOUND_MIXER_OGAIN }, 466 { AudioNinput, SOUND_MIXER_IGAIN }, 467 /* { AudioNmaster, SOUND_MIXER_SPEAKER },*/ 468 /* { AudioNstereo, ?? },*/ 469 /* { AudioNmono, ?? },*/ 470 { AudioNfmsynth, SOUND_MIXER_SYNTH }, 471 /* { AudioNwave, SOUND_MIXER_PCM },*/ 472 { AudioNmidi, SOUND_MIXER_SYNTH }, 473 /* { AudioNmixerout, ?? },*/ 474 { 0, -1 } 475 }; 476 static struct audiodevinfo devcache = { 0 }; 477 struct audiodevinfo *di = &devcache; 478 struct stat sb; 479 480 /* Figure out what device it is so we can check if the 481 * cached data is valid. 482 */ 483 if (fstat(fd, &sb) < 0) 484 return 0; 485 if (di->done && di->dev == sb.st_dev) 486 return di; 487 488 di->done = 1; 489 di->dev = sb.st_dev; 490 di->devmask = 0; 491 di->recmask = 0; 492 di->stereomask = 0; 493 di->source = -1; 494 di->caps = 0; 495 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) 496 di->devmap[i] = -1; 497 for(i = 0; i < NETBSD_MAXDEVS; i++) 498 di->rdevmap[i] = -1; 499 for(i = 0; i < NETBSD_MAXDEVS; i++) { 500 mi.index = i; 501 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) 502 break; 503 switch(mi.type) { 504 case AUDIO_MIXER_VALUE: 505 for(dp = devs; dp->name; dp++) 506 if (strcmp(dp->name, mi.label.name) == 0) 507 break; 508 if (dp->code >= 0) { 509 di->devmap[dp->code] = i; 510 di->rdevmap[i] = dp->code; 511 di->devmask |= 1 << dp->code; 512 if (mi.un.v.num_channels == 2) 513 di->stereomask |= 1 << dp->code; 514 } 515 break; 516 case AUDIO_MIXER_ENUM: 517 if (strcmp(mi.label.name, AudioNsource) == 0) { 518 int j; 519 di->source = i; 520 for(j = 0; j < mi.un.e.num_mem; j++) 521 di->recmask |= 1 << di->rdevmap[mi.un.e.member[j].ord]; 522 di->caps = SOUND_CAP_EXCL_INPUT; 523 } 524 break; 525 case AUDIO_MIXER_SET: 526 if (strcmp(mi.label.name, AudioNsource) == 0) { 527 int j; 528 di->source = i; 529 for(j = 0; j < mi.un.s.num_mem; j++) { 530 int k, mask = mi.un.s.member[j].mask; 531 if (mask) { 532 for(k = 0; !(mask & 1); mask >>= 1, k++) 533 ; 534 di->recmask |= 1 << di->rdevmap[k]; 535 } 536 } 537 } 538 break; 539 } 540 } 541 return di; 542 } 543 544 int 545 mixer_ioctl(int fd, unsigned long com, void *argp) 546 { 547 struct audiodevinfo *di; 548 mixer_ctrl_t mc; 549 int idat; 550 int i; 551 int retval; 552 int l, r, n; 553 554 di = getdevinfo(fd); 555 if (di == 0) 556 return -1; 557 558 switch (com) { 559 case SOUND_MIXER_READ_RECSRC: 560 if (di->source == -1) 561 return EINVAL; 562 mc.dev = di->source; 563 if (di->caps & SOUND_CAP_EXCL_INPUT) { 564 mc.type = AUDIO_MIXER_ENUM; 565 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 566 if (retval < 0) 567 return retval; 568 idat = 1 << di->rdevmap[mc.un.ord]; 569 } else { 570 int k; 571 unsigned int mask; 572 mc.type = AUDIO_MIXER_SET; 573 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 574 if (retval < 0) 575 return retval; 576 idat = 0; 577 for(mask = mc.un.mask, k = 0; mask; mask >>= 1, k++) 578 if (mask & 1) 579 idat |= 1 << di->rdevmap[k]; 580 } 581 break; 582 case SOUND_MIXER_READ_DEVMASK: 583 idat = di->devmask; 584 break; 585 case SOUND_MIXER_READ_RECMASK: 586 idat = di->recmask; 587 break; 588 case SOUND_MIXER_READ_STEREODEVS: 589 idat = di->stereomask; 590 break; 591 case SOUND_MIXER_READ_CAPS: 592 idat = di->caps; 593 break; 594 case SOUND_MIXER_WRITE_RECSRC: 595 case SOUND_MIXER_WRITE_R_RECSRC: 596 if (di->source == -1) 597 return EINVAL; 598 mc.dev = di->source; 599 idat = INTARG; 600 if (di->caps & SOUND_CAP_EXCL_INPUT) { 601 mc.type = AUDIO_MIXER_ENUM; 602 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) 603 if (idat & (1 << i)) 604 break; 605 if (i >= SOUND_MIXER_NRDEVICES || 606 di->devmap[i] == -1) 607 return EINVAL; 608 mc.un.ord = di->devmap[i]; 609 } else { 610 mc.type = AUDIO_MIXER_SET; 611 mc.un.mask = 0; 612 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 613 if (idat & (1 << i)) { 614 if (di->devmap[i] == -1) 615 return EINVAL; 616 mc.un.mask |= 1 << di->devmap[i]; 617 } 618 } 619 } 620 return ioctl(fd, AUDIO_MIXER_WRITE, &mc); 621 default: 622 if (MIXER_READ(SOUND_MIXER_FIRST) <= com && 623 com < MIXER_READ(SOUND_MIXER_NRDEVICES)) { 624 n = GET_DEV(com); 625 if (di->devmap[n] == -1) 626 return EINVAL; 627 mc.dev = di->devmap[n]; 628 mc.type = AUDIO_MIXER_VALUE; 629 doread: 630 mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1; 631 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 632 if (retval < 0) 633 return retval; 634 if (mc.type != AUDIO_MIXER_VALUE) 635 return EINVAL; 636 if (mc.un.value.num_channels != 2) { 637 l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; 638 } else { 639 l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 640 r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 641 } 642 idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); 643 break; 644 } else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com && 645 com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) || 646 (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && 647 com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) { 648 n = GET_DEV(com); 649 if (di->devmap[n] == -1) 650 return EINVAL; 651 idat = INTARG; 652 l = FROM_OSSVOL( idat & 0xff); 653 r = FROM_OSSVOL((idat >> 8) & 0xff); 654 mc.dev = di->devmap[n]; 655 mc.type = AUDIO_MIXER_VALUE; 656 if (di->stereomask & (1<<n)) { 657 mc.un.value.num_channels = 2; 658 mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; 659 mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; 660 } else { 661 mc.un.value.num_channels = 1; 662 mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; 663 } 664 retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc); 665 if (retval < 0) 666 return retval; 667 if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && 668 com < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) 669 return 0; 670 goto doread; 671 } else { 672 errno = EINVAL; 673 return -1; 674 } 675 } 676 INTARG = idat; 677 return 0; 678 } 679 680 /* 681 * Check that the blocksize is a power of 2 as OSS wants. 682 * If not, set it to be. 683 */ 684 static void 685 setblocksize(int fd, struct audio_info *info) 686 { 687 struct audio_info set; 688 int s; 689 690 if (info->blocksize & (info->blocksize-1)) { 691 for(s = 32; s < info->blocksize; s <<= 1) 692 ; 693 AUDIO_INITINFO(&set); 694 set.blocksize = s; 695 ioctl(fd, AUDIO_SETINFO, &set); 696 ioctl(fd, AUDIO_GETINFO, info); 697 } 698 } 699 700