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