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