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