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