1/* $NetBSD: oss_dsp.c,v 1.2 2021/06/08 19:26:48 nia Exp $ */ 2 3/*- 4 * Copyright (c) 1997-2021 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__RCSID("$NetBSD: oss_dsp.c,v 1.2 2021/06/08 19:26:48 nia Exp $"); 31 32#include <sys/audioio.h> 33#include <stdbool.h> 34#include <errno.h> 35#include "internal.h" 36 37#define GETPRINFO(info, name) \ 38 (((info)->mode == AUMODE_RECORD) \ 39 ? (info)->record.name : (info)->play.name) 40 41static int encoding_to_format(u_int, u_int); 42static int format_to_encoding(int, struct audio_info *); 43 44static int get_vol(u_int, u_char); 45static void set_vol(int, int, bool); 46 47static void set_channels(int, int, int); 48 49oss_private int 50_oss_dsp_ioctl(int fd, unsigned long com, void *argp) 51{ 52 53 struct audio_info tmpinfo, hwfmt; 54 struct audio_offset tmpoffs; 55 struct audio_buf_info bufinfo; 56 struct audio_errinfo *tmperrinfo; 57 struct count_info cntinfo; 58 struct audio_encoding tmpenc; 59 u_int u; 60 int perrors, rerrors; 61 static int totalperrors = 0; 62 static int totalrerrors = 0; 63 oss_mixer_enuminfo *ei; 64 oss_count_t osscount; 65 int idat; 66 int retval; 67 68 idat = 0; 69 70 switch (com) { 71 case SNDCTL_DSP_HALT_INPUT: 72 case SNDCTL_DSP_HALT_OUTPUT: 73 case SNDCTL_DSP_RESET: 74 retval = ioctl(fd, AUDIO_FLUSH, 0); 75 if (retval < 0) 76 return retval; 77 break; 78 case SNDCTL_DSP_SYNC: 79 retval = ioctl(fd, AUDIO_DRAIN, 0); 80 if (retval < 0) 81 return retval; 82 break; 83 case SNDCTL_DSP_GETERROR: 84 tmperrinfo = (struct audio_errinfo *)argp; 85 if (tmperrinfo == NULL) { 86 errno = EINVAL; 87 return -1; 88 } 89 memset(tmperrinfo, 0, sizeof(struct audio_errinfo)); 90 if ((retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo)) < 0) 91 return retval; 92 /* 93 * OSS requires that we return counters that are relative to 94 * the last call. We must maintain state here... 95 */ 96 if (ioctl(fd, AUDIO_PERROR, &perrors) != -1) { 97 perrors /= ((tmpinfo.play.precision / NBBY) * 98 tmpinfo.play.channels); 99 tmperrinfo->play_underruns = 100 (perrors / tmpinfo.blocksize) - totalperrors; 101 totalperrors += tmperrinfo->play_underruns; 102 } 103 if (ioctl(fd, AUDIO_RERROR, &rerrors) != -1) { 104 rerrors /= ((tmpinfo.record.precision / NBBY) * 105 tmpinfo.record.channels); 106 tmperrinfo->rec_overruns = 107 (rerrors / tmpinfo.blocksize) - totalrerrors; 108 totalrerrors += tmperrinfo->rec_overruns; 109 } 110 break; 111 case SNDCTL_DSP_COOKEDMODE: 112 /* 113 * NetBSD is always running in "cooked mode" - the kernel 114 * always performs format conversions. 115 */ 116 INTARG = 1; 117 break; 118 case SNDCTL_DSP_POST: 119 /* This call is merely advisory, and may be a nop. */ 120 break; 121 case SNDCTL_DSP_SPEED: 122 /* 123 * In Solaris, 0 is used a special value to query the 124 * current rate. This seems useful to support. 125 */ 126 if (INTARG == 0) { 127 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 128 if (retval < 0) 129 return retval; 130 retval = ioctl(fd, AUDIO_GETFORMAT, &hwfmt); 131 if (retval < 0) 132 return retval; 133 INTARG = (tmpinfo.mode == AUMODE_RECORD) ? 134 hwfmt.record.sample_rate : 135 hwfmt.play.sample_rate; 136 } 137 /* 138 * Conform to kernel limits. 139 * NetBSD will reject unsupported sample rates, but OSS 140 * applications need to be able to negotiate a supported one. 141 */ 142 if (INTARG < 1000) 143 INTARG = 1000; 144 if (INTARG > 192000) 145 INTARG = 192000; 146 AUDIO_INITINFO(&tmpinfo); 147 tmpinfo.play.sample_rate = 148 tmpinfo.record.sample_rate = INTARG; 149 retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); 150 if (retval < 0) 151 return retval; 152 /* FALLTHRU */ 153 case SOUND_PCM_READ_RATE: 154 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 155 if (retval < 0) 156 return retval; 157 INTARG = GETPRINFO(&tmpinfo, sample_rate); 158 break; 159 case SNDCTL_DSP_STEREO: 160 AUDIO_INITINFO(&tmpinfo); 161 tmpinfo.play.channels = 162 tmpinfo.record.channels = INTARG ? 2 : 1; 163 (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); 164 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 165 if (retval < 0) 166 return retval; 167 INTARG = GETPRINFO(&tmpinfo, channels) - 1; 168 break; 169 case SNDCTL_DSP_GETBLKSIZE: 170 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 171 if (retval < 0) 172 return retval; 173 INTARG = tmpinfo.blocksize; 174 break; 175 case SNDCTL_DSP_SETFMT: 176 AUDIO_INITINFO(&tmpinfo); 177 retval = format_to_encoding(INTARG, &tmpinfo); 178 if (retval < 0) { 179 /* 180 * OSSv4 specifies that if an invalid format is chosen 181 * by an application then a sensible format supported 182 * by the hardware is returned. 183 * 184 * In this case, we pick the current hardware format. 185 */ 186 retval = ioctl(fd, AUDIO_GETFORMAT, &hwfmt); 187 if (retval < 0) 188 return retval; 189 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); 190 if (retval < 0) 191 return retval; 192 tmpinfo.play.encoding = 193 tmpinfo.record.encoding = 194 (tmpinfo.mode == AUMODE_RECORD) ? 195 hwfmt.record.encoding : hwfmt.play.encoding; 196 tmpinfo.play.precision = 197 tmpinfo.record.precision = 198 (tmpinfo.mode == AUMODE_RECORD) ? 199 hwfmt.record.precision : hwfmt.play.precision ; 200 } 201 /* 202 * In the post-kernel-mixer world, assume that any error means 203 * it's fatal rather than an unsupported format being selected. 204 */ 205 retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); 206 if (retval < 0) 207 return retval; 208 /* FALLTHRU */ 209 case SOUND_PCM_READ_BITS: 210 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 211 if (retval < 0) 212 return retval; 213 if (tmpinfo.mode == AUMODE_RECORD) 214 retval = encoding_to_format(tmpinfo.record.encoding, 215 tmpinfo.record.precision); 216 else 217 retval = encoding_to_format(tmpinfo.play.encoding, 218 tmpinfo.play.precision); 219 if (retval < 0) { 220 errno = EINVAL; 221 return retval; 222 } 223 INTARG = retval; 224 break; 225 case SNDCTL_DSP_CHANNELS: 226 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 227 if (retval < 0) 228 return retval; 229 set_channels(fd, tmpinfo.mode, INTARG); 230 /* FALLTHRU */ 231 case SOUND_PCM_READ_CHANNELS: 232 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 233 if (retval < 0) 234 return retval; 235 INTARG = GETPRINFO(&tmpinfo, channels); 236 break; 237 case SOUND_PCM_WRITE_FILTER: 238 case SOUND_PCM_READ_FILTER: 239 errno = EINVAL; 240 return -1; /* XXX unimplemented */ 241 case SNDCTL_DSP_SUBDIVIDE: 242 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 243 if (retval < 0) 244 return retval; 245 idat = INTARG; 246 if (idat == 0) 247 idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; 248 idat = (tmpinfo.play.buffer_size / idat) & -4; 249 AUDIO_INITINFO(&tmpinfo); 250 tmpinfo.blocksize = idat; 251 retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); 252 if (retval < 0) 253 return retval; 254 INTARG = tmpinfo.play.buffer_size / tmpinfo.blocksize; 255 break; 256 case SNDCTL_DSP_SETFRAGMENT: 257 AUDIO_INITINFO(&tmpinfo); 258 idat = INTARG; 259 tmpinfo.blocksize = 1 << (idat & 0xffff); 260 tmpinfo.hiwat = ((unsigned)idat >> 16) & 0x7fff; 261 if (tmpinfo.hiwat == 0) /* 0 means set to max */ 262 tmpinfo.hiwat = 65536; 263 (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); 264 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 265 if (retval < 0) 266 return retval; 267 u = tmpinfo.blocksize; 268 for(idat = 0; u > 1; idat++, u >>= 1) 269 ; 270 idat |= (tmpinfo.hiwat & 0x7fff) << 16; 271 INTARG = idat; 272 break; 273 case SNDCTL_DSP_GETFMTS: 274 for(idat = 0, tmpenc.index = 0; 275 ioctl(fd, AUDIO_GETENC, &tmpenc) == 0; 276 tmpenc.index++) { 277 retval = encoding_to_format(tmpenc.encoding, 278 tmpenc.precision); 279 if (retval != -1) 280 idat |= retval; 281 } 282 INTARG = idat; 283 break; 284 case SNDCTL_DSP_GETOSPACE: 285 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 286 if (retval < 0) 287 return retval; 288 bufinfo.fragsize = tmpinfo.blocksize; 289 bufinfo.fragments = tmpinfo.hiwat - (tmpinfo.play.seek 290 + tmpinfo.blocksize - 1) / tmpinfo.blocksize; 291 bufinfo.fragstotal = tmpinfo.hiwat; 292 bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize 293 - tmpinfo.play.seek; 294 *(struct audio_buf_info *)argp = bufinfo; 295 break; 296 case SNDCTL_DSP_GETISPACE: 297 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 298 if (retval < 0) 299 return retval; 300 bufinfo.fragsize = tmpinfo.blocksize; 301 bufinfo.fragments = tmpinfo.record.seek / tmpinfo.blocksize; 302 bufinfo.fragstotal = 303 tmpinfo.record.buffer_size / tmpinfo.blocksize; 304 bufinfo.bytes = tmpinfo.record.seek; 305 *(struct audio_buf_info *)argp = bufinfo; 306 break; 307 case SNDCTL_DSP_NONBLOCK: 308 idat = 1; 309 retval = ioctl(fd, FIONBIO, &idat); 310 if (retval < 0) 311 return retval; 312 break; 313 case SNDCTL_DSP_GETCAPS: 314 retval = _oss_get_caps(fd, (int *)argp); 315 if (retval < 0) 316 return retval; 317 break; 318 case SNDCTL_DSP_SETTRIGGER: 319 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 320 if (retval < 0) 321 return retval; 322 AUDIO_INITINFO(&tmpinfo); 323 if (tmpinfo.mode & AUMODE_PLAY) 324 tmpinfo.play.pause = (INTARG & PCM_ENABLE_OUTPUT) == 0; 325 if (tmpinfo.mode & AUMODE_RECORD) 326 tmpinfo.record.pause = (INTARG & PCM_ENABLE_INPUT) == 0; 327 (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); 328 /* FALLTHRU */ 329 case SNDCTL_DSP_GETTRIGGER: 330 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 331 if (retval < 0) 332 return retval; 333 idat = 0; 334 if ((tmpinfo.mode & AUMODE_PLAY) && !tmpinfo.play.pause) 335 idat |= PCM_ENABLE_OUTPUT; 336 if ((tmpinfo.mode & AUMODE_RECORD) && !tmpinfo.record.pause) 337 idat |= PCM_ENABLE_INPUT; 338 INTARG = idat; 339 break; 340 case SNDCTL_DSP_GETIPTR: 341 retval = ioctl(fd, AUDIO_GETIOFFS, &tmpoffs); 342 if (retval < 0) 343 return retval; 344 cntinfo.bytes = tmpoffs.samples; 345 cntinfo.blocks = tmpoffs.deltablks; 346 cntinfo.ptr = tmpoffs.offset; 347 *(struct count_info *)argp = cntinfo; 348 break; 349 case SNDCTL_DSP_CURRENT_IPTR: 350 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 351 if (retval < 0) 352 return retval; 353 /* XXX: 'samples' may wrap */ 354 memset(osscount.filler, 0, sizeof(osscount.filler)); 355 osscount.samples = tmpinfo.record.samples / 356 ((tmpinfo.record.precision / NBBY) * 357 tmpinfo.record.channels); 358 osscount.fifo_samples = tmpinfo.record.seek / 359 ((tmpinfo.record.precision / NBBY) * 360 tmpinfo.record.channels); 361 *(oss_count_t *)argp = osscount; 362 break; 363 case SNDCTL_DSP_GETOPTR: 364 retval = ioctl(fd, AUDIO_GETOOFFS, &tmpoffs); 365 if (retval < 0) 366 return retval; 367 cntinfo.bytes = tmpoffs.samples; 368 cntinfo.blocks = tmpoffs.deltablks; 369 cntinfo.ptr = tmpoffs.offset; 370 *(struct count_info *)argp = cntinfo; 371 break; 372 case SNDCTL_DSP_CURRENT_OPTR: 373 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 374 if (retval < 0) 375 return retval; 376 /* XXX: 'samples' may wrap */ 377 memset(osscount.filler, 0, sizeof(osscount.filler)); 378 osscount.samples = tmpinfo.play.samples / 379 ((tmpinfo.play.precision / NBBY) * 380 tmpinfo.play.channels); 381 osscount.fifo_samples = tmpinfo.play.seek / 382 ((tmpinfo.play.precision / NBBY) * 383 tmpinfo.play.channels); 384 *(oss_count_t *)argp = osscount; 385 break; 386 case SNDCTL_DSP_SETPLAYVOL: 387 set_vol(fd, INTARG, false); 388 /* FALLTHRU */ 389 case SNDCTL_DSP_GETPLAYVOL: 390 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); 391 if (retval < 0) 392 return retval; 393 INTARG = get_vol(tmpinfo.play.gain, tmpinfo.play.balance); 394 break; 395 case SNDCTL_DSP_SETRECVOL: 396 set_vol(fd, INTARG, true); 397 /* FALLTHRU */ 398 case SNDCTL_DSP_GETRECVOL: 399 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); 400 if (retval < 0) 401 return retval; 402 INTARG = get_vol(tmpinfo.record.gain, tmpinfo.record.balance); 403 break; 404 case SNDCTL_DSP_SKIP: 405 case SNDCTL_DSP_SILENCE: 406 errno = EINVAL; 407 return -1; 408 case SNDCTL_DSP_SETDUPLEX: 409 idat = 1; 410 retval = ioctl(fd, AUDIO_SETFD, &idat); 411 if (retval < 0) 412 return retval; 413 break; 414 case SNDCTL_DSP_GETODELAY: 415 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); 416 if (retval < 0) 417 return retval; 418 idat = tmpinfo.play.seek + tmpinfo.blocksize / 2; 419 INTARG = idat; 420 break; 421 case SNDCTL_DSP_PROFILE: 422 /* This gives just a hint to the driver, 423 * implementing it as a NOP is ok 424 */ 425 break; 426 case SNDCTL_DSP_MAPINBUF: 427 case SNDCTL_DSP_MAPOUTBUF: 428 case SNDCTL_DSP_SETSYNCRO: 429 errno = EINVAL; 430 return -1; /* XXX unimplemented */ 431 case SNDCTL_DSP_GET_PLAYTGT_NAMES: 432 case SNDCTL_DSP_GET_RECSRC_NAMES: 433 ei = (oss_mixer_enuminfo *)argp; 434 ei->nvalues = 1; 435 ei->version = 0; 436 ei->strindex[0] = 0; 437 strlcpy(ei->strings, "primary", OSS_ENUM_STRINGSIZE); 438 break; 439 case SNDCTL_DSP_SET_PLAYTGT: 440 case SNDCTL_DSP_SET_RECSRC: 441 case SNDCTL_DSP_GET_PLAYTGT: 442 case SNDCTL_DSP_GET_RECSRC: 443 /* We have one recording source and play target. */ 444 INTARG = 0; 445 break; 446 default: 447 errno = EINVAL; 448 return -1; 449 } 450 451 return 0; 452} 453 454static int 455get_vol(u_int gain, u_char balance) 456{ 457 u_int l, r; 458 459 if (balance == AUDIO_MID_BALANCE) { 460 l = r = gain; 461 } else if (balance < AUDIO_MID_BALANCE) { 462 l = gain; 463 r = (balance * gain) / AUDIO_MID_BALANCE; 464 } else { 465 r = gain; 466 l = ((AUDIO_RIGHT_BALANCE - balance) * gain) 467 / AUDIO_MID_BALANCE; 468 } 469 470 return TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); 471} 472 473static void 474set_vol(int fd, int volume, bool record) 475{ 476 u_int lgain, rgain; 477 struct audio_info tmpinfo; 478 struct audio_prinfo *prinfo; 479 480 AUDIO_INITINFO(&tmpinfo); 481 prinfo = record ? &tmpinfo.record : &tmpinfo.play; 482 483 lgain = FROM_OSSVOL((volume >> 0) & 0xff); 484 rgain = FROM_OSSVOL((volume >> 8) & 0xff); 485 486 if (lgain == rgain) { 487 prinfo->gain = lgain; 488 prinfo->balance = AUDIO_MID_BALANCE; 489 } else if (lgain < rgain) { 490 prinfo->gain = rgain; 491 prinfo->balance = AUDIO_RIGHT_BALANCE - 492 (AUDIO_MID_BALANCE * lgain) / rgain; 493 } else { 494 prinfo->gain = lgain; 495 prinfo->balance = (AUDIO_MID_BALANCE * rgain) / lgain; 496 } 497 498 (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); 499} 500 501/* 502 * When AUDIO_SETINFO fails to set a channel count, the application's chosen 503 * number is out of range of what the kernel allows. 504 * 505 * When this happens, we use the current hardware settings. This is just in 506 * case an application is abusing SNDCTL_DSP_CHANNELS - OSSv4 always sets and 507 * returns a reasonable value, even if it wasn't what the user requested. 508 * 509 * Solaris guarantees this behaviour if nchannels = 0. 510 * 511 * XXX: If a device is opened for both playback and recording, and supports 512 * fewer channels for recording than playback, applications that do both will 513 * behave very strangely. OSS doesn't allow for reporting separate channel 514 * counts for recording and playback. This could be worked around by always 515 * mixing recorded data up to the same number of channels as is being used 516 * for playback. 517 */ 518static void 519set_channels(int fd, int mode, int nchannels) 520{ 521 struct audio_info tmpinfo, hwfmt; 522 523 if (ioctl(fd, AUDIO_GETFORMAT, &hwfmt) < 0) { 524 errno = 0; 525 hwfmt.record.channels = hwfmt.play.channels = 2; 526 } 527 528 if (mode & AUMODE_PLAY) { 529 AUDIO_INITINFO(&tmpinfo); 530 tmpinfo.play.channels = nchannels; 531 if (ioctl(fd, AUDIO_SETINFO, &tmpinfo) < 0) { 532 errno = 0; 533 AUDIO_INITINFO(&tmpinfo); 534 tmpinfo.play.channels = hwfmt.play.channels; 535 (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); 536 } 537 } 538 539 if (mode & AUMODE_RECORD) { 540 AUDIO_INITINFO(&tmpinfo); 541 tmpinfo.record.channels = nchannels; 542 if (ioctl(fd, AUDIO_SETINFO, &tmpinfo) < 0) { 543 errno = 0; 544 AUDIO_INITINFO(&tmpinfo); 545 tmpinfo.record.channels = hwfmt.record.channels; 546 (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); 547 } 548 } 549} 550 551/* Convert a NetBSD "encoding" to a OSS "format". */ 552static int 553encoding_to_format(u_int encoding, u_int precision) 554{ 555 switch(encoding) { 556 case AUDIO_ENCODING_ULAW: 557 return AFMT_MU_LAW; 558 case AUDIO_ENCODING_ALAW: 559 return AFMT_A_LAW; 560 case AUDIO_ENCODING_SLINEAR: 561 if (precision == 32) 562 return AFMT_S32_NE; 563 else if (precision == 24) 564 return AFMT_S24_NE; 565 else if (precision == 16) 566 return AFMT_S16_NE; 567 return AFMT_S8; 568 case AUDIO_ENCODING_SLINEAR_LE: 569 if (precision == 32) 570 return AFMT_S32_LE; 571 else if (precision == 24) 572 return AFMT_S24_LE; 573 else if (precision == 16) 574 return AFMT_S16_LE; 575 return AFMT_S8; 576 case AUDIO_ENCODING_SLINEAR_BE: 577 if (precision == 32) 578 return AFMT_S32_BE; 579 else if (precision == 24) 580 return AFMT_S24_BE; 581 else if (precision == 16) 582 return AFMT_S16_BE; 583 return AFMT_S8; 584 case AUDIO_ENCODING_ULINEAR: 585 if (precision == 16) 586 return AFMT_U16_NE; 587 return AFMT_U8; 588 case AUDIO_ENCODING_ULINEAR_LE: 589 if (precision == 16) 590 return AFMT_U16_LE; 591 return AFMT_U8; 592 case AUDIO_ENCODING_ULINEAR_BE: 593 if (precision == 16) 594 return AFMT_U16_BE; 595 return AFMT_U8; 596 case AUDIO_ENCODING_ADPCM: 597 return AFMT_IMA_ADPCM; 598 case AUDIO_ENCODING_AC3: 599 return AFMT_AC3; 600 } 601 return -1; 602} 603 604/* Convert an OSS "format" to a NetBSD "encoding". */ 605static int 606format_to_encoding(int fmt, struct audio_info *tmpinfo) 607{ 608 switch (fmt) { 609 case AFMT_MU_LAW: 610 tmpinfo->record.precision = 611 tmpinfo->play.precision = 8; 612 tmpinfo->record.encoding = 613 tmpinfo->play.encoding = AUDIO_ENCODING_ULAW; 614 return 0; 615 case AFMT_A_LAW: 616 tmpinfo->record.precision = 617 tmpinfo->play.precision = 8; 618 tmpinfo->record.encoding = 619 tmpinfo->play.encoding = AUDIO_ENCODING_ALAW; 620 return 0; 621 case AFMT_U8: 622 tmpinfo->record.precision = 623 tmpinfo->play.precision = 8; 624 tmpinfo->record.encoding = 625 tmpinfo->play.encoding = AUDIO_ENCODING_ULINEAR; 626 return 0; 627 case AFMT_S8: 628 tmpinfo->record.precision = 629 tmpinfo->play.precision = 8; 630 tmpinfo->record.encoding = 631 tmpinfo->play.encoding = AUDIO_ENCODING_SLINEAR; 632 return 0; 633 case AFMT_S16_LE: 634 tmpinfo->record.precision = 635 tmpinfo->play.precision = 16; 636 tmpinfo->record.encoding = 637 tmpinfo->play.encoding = AUDIO_ENCODING_SLINEAR_LE; 638 return 0; 639 case AFMT_S16_BE: 640 tmpinfo->record.precision = 641 tmpinfo->play.precision = 16; 642 tmpinfo->record.encoding = 643 tmpinfo->play.encoding = AUDIO_ENCODING_SLINEAR_BE; 644 return 0; 645 case AFMT_U16_LE: 646 tmpinfo->record.precision = 647 tmpinfo->play.precision = 16; 648 tmpinfo->record.encoding = 649 tmpinfo->play.encoding = AUDIO_ENCODING_ULINEAR_LE; 650 return 0; 651 case AFMT_U16_BE: 652 tmpinfo->record.precision = 653 tmpinfo->play.precision = 16; 654 tmpinfo->record.encoding = 655 tmpinfo->play.encoding = AUDIO_ENCODING_ULINEAR_BE; 656 return 0; 657 /* 658 * XXX: When the kernel supports 24-bit LPCM by default, 659 * the 24-bit formats should be handled properly instead 660 * of falling back to 32 bits. 661 */ 662 case AFMT_S24_PACKED: 663 case AFMT_S24_LE: 664 case AFMT_S32_LE: 665 tmpinfo->record.precision = 666 tmpinfo->play.precision = 32; 667 tmpinfo->record.encoding = 668 tmpinfo->play.encoding = AUDIO_ENCODING_SLINEAR_LE; 669 return 0; 670 case AFMT_S24_BE: 671 case AFMT_S32_BE: 672 tmpinfo->record.precision = 673 tmpinfo->play.precision = 32; 674 tmpinfo->record.encoding = 675 tmpinfo->play.encoding = AUDIO_ENCODING_SLINEAR_BE; 676 return 0; 677 case AFMT_AC3: 678 tmpinfo->record.precision = 679 tmpinfo->play.precision = 16; 680 tmpinfo->record.encoding = 681 tmpinfo->play.encoding = AUDIO_ENCODING_AC3; 682 return 0; 683 } 684 return -1; 685} 686