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