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
41 static int encoding_to_format(u_int, u_int);
42 static int format_to_encoding(int, struct audio_info *);
43
44 static int get_vol(u_int, u_char);
45 static void set_vol(int, int, bool);
46
47 static void set_channels(int, int, int);
48
49 oss_private int
_oss_dsp_ioctl(int fd,unsigned long com,void * argp)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
454 static int
get_vol(u_int gain,u_char balance)455 get_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
473 static void
set_vol(int fd,int volume,bool record)474 set_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 */
518 static void
set_channels(int fd,int mode,int nchannels)519 set_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". */
552 static int
encoding_to_format(u_int encoding,u_int precision)553 encoding_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". */
605 static int
format_to_encoding(int fmt,struct audio_info * tmpinfo)606 format_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