1 /* $OpenBSD: ossaudio.c,v 1.16 2008/06/26 05:42:05 ray 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 NetBSD mixer device should have more than NETBSD_MAXDEVS devices 460 * some will not be available to Linux */ 461 #define NETBSD_MAXDEVS 64 462 struct audiodevinfo { 463 int done; 464 dev_t dev; 465 ino_t ino; 466 int16_t devmap[SOUND_MIXER_NRDEVICES], 467 rdevmap[NETBSD_MAXDEVS]; 468 char names[NETBSD_MAXDEVS][MAX_AUDIO_DEV_LEN]; 469 int enum2opaque[NETBSD_MAXDEVS]; 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 < NETBSD_MAXDEVS; 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 >= NETBSD_MAXDEVS) 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 >= NETBSD_MAXDEVS) 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 >= NETBSD_MAXDEVS) 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 /* { AudioNheadphone, ?? },*/ 545 { AudioNoutput, SOUND_MIXER_OGAIN }, 546 { AudioNinput, SOUND_MIXER_IGAIN }, 547 /* { AudioNmaster, SOUND_MIXER_SPEAKER },*/ 548 /* { AudioNstereo, ?? },*/ 549 /* { AudioNmono, ?? },*/ 550 { AudioNfmsynth, SOUND_MIXER_SYNTH }, 551 /* { AudioNwave, SOUND_MIXER_PCM },*/ 552 { AudioNmidi, SOUND_MIXER_SYNTH }, 553 /* { AudioNmixerout, ?? },*/ 554 { 0, -1 } 555 }; 556 static struct audiodevinfo devcache = { 0 }; 557 struct audiodevinfo *di = &devcache; 558 struct stat sb; 559 560 /* Figure out what device it is so we can check if the 561 * cached data is valid. 562 */ 563 if (fstat(fd, &sb) < 0) 564 return 0; 565 if (di->done && (di->dev == sb.st_dev && di->ino == sb.st_ino)) 566 return di; 567 568 di->done = 1; 569 di->dev = sb.st_dev; 570 di->ino = sb.st_ino; 571 di->devmask = 0; 572 di->recmask = 0; 573 di->stereomask = 0; 574 di->recsource = ~0; 575 di->caps = 0; 576 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) 577 di->devmap[i] = -1; 578 for(i = 0; i < NETBSD_MAXDEVS; i++) { 579 di->rdevmap[i] = -1; 580 di->names[i][0] = '\0'; 581 di->enum2opaque[i] = -1; 582 } 583 for(i = 0; i < NETBSD_MAXDEVS; i++) { 584 mi.index = i; 585 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) 586 break; 587 switch(mi.type) { 588 case AUDIO_MIXER_VALUE: 589 for(dp = devs; dp->name; dp++) 590 if (strcmp(dp->name, mi.label.name) == 0) 591 break; 592 if (dp->code >= 0) { 593 di->devmap[dp->code] = i; 594 di->rdevmap[i] = dp->code; 595 di->devmask |= 1 << dp->code; 596 if (mi.un.v.num_channels == 2) 597 di->stereomask |= 1 << dp->code; 598 strncpy(di->names[i], mi.label.name, 599 sizeof di->names[i]); 600 } 601 break; 602 } 603 } 604 for(i = 0; i < NETBSD_MAXDEVS; i++) { 605 mi.index = i; 606 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) 607 break; 608 if (strcmp(mi.label.name, AudioNsource) != 0) 609 continue; 610 cl.index = mi.mixer_class; 611 if (ioctl(fd, AUDIO_MIXER_DEVINFO, &cl) < 0) 612 break; 613 if ((cl.type != AUDIO_MIXER_CLASS) || 614 (strcmp(cl.label.name, AudioCrecord) != 0)) 615 continue; 616 di->recsource = i; 617 switch(mi.type) { 618 case AUDIO_MIXER_ENUM: 619 for(j = 0; j < mi.un.e.num_mem; j++) { 620 e = opaque_to_enum(di, 621 &mi.un.e.member[j].label, 622 mi.un.e.member[j].ord); 623 if (e >= 0) 624 di->recmask |= 1 << di->rdevmap[e]; 625 } 626 di->caps = SOUND_CAP_EXCL_INPUT; 627 break; 628 case AUDIO_MIXER_SET: 629 for(j = 0; j < mi.un.s.num_mem; j++) { 630 e = opaque_to_enum(di, 631 &mi.un.s.member[j].label, 632 mi.un.s.member[j].mask); 633 if (e >= 0) 634 di->recmask |= 1 << di->rdevmap[e]; 635 } 636 break; 637 } 638 } 639 return di; 640 } 641 642 int 643 mixer_ioctl(int fd, unsigned long com, void *argp) 644 { 645 struct audiodevinfo *di; 646 struct mixer_info *omi; 647 struct audio_device adev; 648 mixer_ctrl_t mc; 649 int idat = 0; 650 int i; 651 int retval; 652 int l, r, n, error, e; 653 654 di = getdevinfo(fd); 655 if (di == 0) 656 return -1; 657 658 switch (com) { 659 case OSS_GETVERSION: 660 idat = SOUND_VERSION; 661 break; 662 case SOUND_MIXER_INFO: 663 case SOUND_OLD_MIXER_INFO: 664 error = ioctl(fd, AUDIO_GETDEV, &adev); 665 if (error) 666 return (error); 667 omi = argp; 668 if (com == SOUND_MIXER_INFO) 669 omi->modify_counter = 1; 670 strncpy(omi->id, adev.name, sizeof omi->id); 671 strncpy(omi->name, adev.name, sizeof omi->name); 672 return 0; 673 case SOUND_MIXER_READ_RECSRC: 674 if (di->recsource == -1) 675 return EINVAL; 676 mc.dev = di->recsource; 677 if (di->caps & SOUND_CAP_EXCL_INPUT) { 678 mc.type = AUDIO_MIXER_ENUM; 679 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 680 if (retval < 0) 681 return retval; 682 e = opaque_to_enum(di, NULL, mc.un.ord); 683 if (e >= 0) 684 idat = 1 << di->rdevmap[e]; 685 } else { 686 mc.type = AUDIO_MIXER_SET; 687 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 688 if (retval < 0) 689 return retval; 690 e = opaque_to_enum(di, NULL, mc.un.mask); 691 if (e >= 0) 692 idat = 1 << di->rdevmap[e]; 693 } 694 break; 695 case SOUND_MIXER_READ_DEVMASK: 696 idat = di->devmask; 697 break; 698 case SOUND_MIXER_READ_RECMASK: 699 idat = di->recmask; 700 break; 701 case SOUND_MIXER_READ_STEREODEVS: 702 idat = di->stereomask; 703 break; 704 case SOUND_MIXER_READ_CAPS: 705 idat = di->caps; 706 break; 707 case SOUND_MIXER_WRITE_RECSRC: 708 case SOUND_MIXER_WRITE_R_RECSRC: 709 if (di->recsource == -1) 710 return EINVAL; 711 mc.dev = di->recsource; 712 idat = INTARG; 713 if (di->caps & SOUND_CAP_EXCL_INPUT) { 714 mc.type = AUDIO_MIXER_ENUM; 715 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) 716 if (idat & (1 << i)) 717 break; 718 if (i >= SOUND_MIXER_NRDEVICES || 719 di->devmap[i] == -1) 720 return EINVAL; 721 mc.un.ord = enum_to_ord(di, di->devmap[i]); 722 } else { 723 mc.type = AUDIO_MIXER_SET; 724 mc.un.mask = 0; 725 for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) { 726 if (idat & (1 << i)) { 727 if (di->devmap[i] == -1) 728 return EINVAL; 729 mc.un.mask |= enum_to_mask(di, di->devmap[i]); 730 } 731 } 732 } 733 return ioctl(fd, AUDIO_MIXER_WRITE, &mc); 734 default: 735 if (MIXER_READ(SOUND_MIXER_FIRST) <= com && 736 com < MIXER_READ(SOUND_MIXER_NRDEVICES)) { 737 n = GET_DEV(com); 738 if (di->devmap[n] == -1) 739 return EINVAL; 740 mc.dev = di->devmap[n]; 741 mc.type = AUDIO_MIXER_VALUE; 742 doread: 743 mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1; 744 retval = ioctl(fd, AUDIO_MIXER_READ, &mc); 745 if (retval < 0) 746 return retval; 747 if (mc.type != AUDIO_MIXER_VALUE) 748 return EINVAL; 749 if (mc.un.value.num_channels != 2) { 750 l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; 751 } else { 752 l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; 753 r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; 754 } 755 idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); 756 break; 757 } else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com && 758 com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) || 759 (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && 760 com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) { 761 n = GET_DEV(com); 762 if (di->devmap[n] == -1) 763 return EINVAL; 764 idat = INTARG; 765 l = FROM_OSSVOL( idat & 0xff); 766 r = FROM_OSSVOL((idat >> 8) & 0xff); 767 mc.dev = di->devmap[n]; 768 mc.type = AUDIO_MIXER_VALUE; 769 if (di->stereomask & (1<<n)) { 770 mc.un.value.num_channels = 2; 771 mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; 772 mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; 773 } else { 774 mc.un.value.num_channels = 1; 775 mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2; 776 } 777 retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc); 778 if (retval < 0) 779 return retval; 780 if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && 781 com < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) 782 return 0; 783 goto doread; 784 } else { 785 errno = EINVAL; 786 return -1; 787 } 788 } 789 INTARG = idat; 790 return 0; 791 } 792 793 /* 794 * Check that the blocksize is a power of 2 as OSS wants. 795 * If not, set it to be. 796 */ 797 static void 798 setblocksize(int fd, struct audio_info *info) 799 { 800 struct audio_info set; 801 int s; 802 803 if (info->blocksize & (info->blocksize-1)) { 804 for(s = 32; s < info->blocksize; s <<= 1) 805 ; 806 AUDIO_INITINFO(&set); 807 set.blocksize = s; 808 ioctl(fd, AUDIO_SETINFO, &set); 809 ioctl(fd, AUDIO_GETINFO, info); 810 } 811 } 812