xref: /openbsd-src/lib/libossaudio/ossaudio.c (revision 2badd5e3f47d2d4252969cd98d7042b4e701b5ac)
1 /*	$OpenBSD: ossaudio.c,v 1.4 2001/08/18 19:33:30 brad Exp $	*/
2 /*	$NetBSD: ossaudio.c,v 1.14 2001/05/10 01:53:48 augustss Exp $	*/
3 
4 /*-
5  * Copyright (c) 1997 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *        This product includes software developed by the NetBSD
19  *        Foundation, Inc. and its contributors.
20  * 4. Neither the name of The NetBSD Foundation nor the names of its
21  *    contributors may be used to endorse or promote products derived
22  *    from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
26  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
28  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGE.
35  */
36 
37 /*
38  * This is an OSS (Linux) sound API emulator.
39  * It provides the essentials of the API.
40  */
41 
42 /* XXX This file is essentially the same as sys/compat/ossaudio.c.
43  * With some preprocessor magic it could be the same file.
44  */
45 
46 #include <string.h>
47 #include <sys/types.h>
48 #include <sys/ioctl.h>
49 #include <sys/audioio.h>
50 #include <sys/stat.h>
51 #include <errno.h>
52 
53 #include "soundcard.h"
54 #undef ioctl
55 
56 #define GET_DEV(com) ((com) & 0xff)
57 
58 #define TO_OSSVOL(x)	(((x) * 100 + 127) / 255)
59 #define FROM_OSSVOL(x)	((((x) > 100 ? 100 : (x)) * 255 + 50) / 100)
60 
61 static struct audiodevinfo *getdevinfo(int);
62 
63 static void setblocksize(int, struct audio_info *);
64 
65 static int audio_ioctl(int, unsigned long, void *);
66 static int mixer_ioctl(int, unsigned long, void *);
67 static int opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq);
68 static int enum_to_ord(struct audiodevinfo *di, int enm);
69 static int enum_to_mask(struct audiodevinfo *di, int enm);
70 
71 #define INTARG (*(int*)argp)
72 
73 int
74 _oss_ioctl(int fd, unsigned long com, void *argp)
75 {
76 	if (IOCGROUP(com) == 'P')
77 		return audio_ioctl(fd, com, argp);
78 	else if (IOCGROUP(com) == 'M')
79 		return mixer_ioctl(fd, com, argp);
80 	else
81 		return ioctl(fd, com, argp);
82 }
83 
84 static int
85 audio_ioctl(int fd, unsigned long com, void *argp)
86 {
87 
88 	struct audio_info tmpinfo;
89 	struct audio_offset tmpoffs;
90 	struct audio_buf_info bufinfo;
91 	struct count_info cntinfo;
92 	struct audio_encoding tmpenc;
93 	u_int u;
94 	int idat, idata;
95 	int retval;
96 
97 	switch (com) {
98 	case SNDCTL_DSP_RESET:
99 		retval = ioctl(fd, AUDIO_FLUSH, 0);
100 		if (retval < 0)
101 			return retval;
102 		break;
103 	case SNDCTL_DSP_SYNC:
104 	case SNDCTL_DSP_POST:
105 		retval = ioctl(fd, AUDIO_DRAIN, 0);
106 		if (retval < 0)
107 			return retval;
108 		break;
109 	case SNDCTL_DSP_SPEED:
110 		AUDIO_INITINFO(&tmpinfo);
111 		tmpinfo.play.sample_rate =
112 		tmpinfo.record.sample_rate = INTARG;
113 		(void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
114 		/* FALLTHRU */
115 	case SOUND_PCM_READ_RATE:
116 		retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
117 		if (retval < 0)
118 			return retval;
119 		INTARG = tmpinfo.play.sample_rate;
120 		break;
121 	case SNDCTL_DSP_STEREO:
122 		AUDIO_INITINFO(&tmpinfo);
123 		tmpinfo.play.channels =
124 		tmpinfo.record.channels = INTARG ? 2 : 1;
125 		(void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
126 		retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
127 		if (retval < 0)
128 			return retval;
129 		INTARG = tmpinfo.play.channels - 1;
130 		break;
131 	case SNDCTL_DSP_GETBLKSIZE:
132 		retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
133 		if (retval < 0)
134 			return retval;
135 		setblocksize(fd, &tmpinfo);
136 		INTARG = tmpinfo.blocksize;
137 		break;
138 	case SNDCTL_DSP_SETFMT:
139 		AUDIO_INITINFO(&tmpinfo);
140 		switch (INTARG) {
141 		case AFMT_MU_LAW:
142 			tmpinfo.play.precision =
143 			tmpinfo.record.precision = 8;
144 			tmpinfo.play.encoding =
145 			tmpinfo.record.encoding = AUDIO_ENCODING_ULAW;
146 			break;
147 		case AFMT_A_LAW:
148 			tmpinfo.play.precision =
149 			tmpinfo.record.precision = 8;
150 			tmpinfo.play.encoding =
151 			tmpinfo.record.encoding = AUDIO_ENCODING_ALAW;
152 			break;
153 		case AFMT_U8:
154 			tmpinfo.play.precision =
155 			tmpinfo.record.precision = 8;
156 			tmpinfo.play.encoding =
157 			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR;
158 			break;
159 		case AFMT_S8:
160 			tmpinfo.play.precision =
161 			tmpinfo.record.precision = 8;
162 			tmpinfo.play.encoding =
163 			tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR;
164 			break;
165 		case AFMT_S16_LE:
166 			tmpinfo.play.precision =
167 			tmpinfo.record.precision = 16;
168 			tmpinfo.play.encoding =
169 			tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE;
170 			break;
171 		case AFMT_S16_BE:
172 			tmpinfo.play.precision =
173 			tmpinfo.record.precision = 16;
174 			tmpinfo.play.encoding =
175 			tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE;
176 			break;
177 		case AFMT_U16_LE:
178 			tmpinfo.play.precision =
179 			tmpinfo.record.precision = 16;
180 			tmpinfo.play.encoding =
181 			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE;
182 			break;
183 		case AFMT_U16_BE:
184 			tmpinfo.play.precision =
185 			tmpinfo.record.precision = 16;
186 			tmpinfo.play.encoding =
187 			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE;
188 			break;
189 		default:
190 			return EINVAL;
191 		}
192 		(void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
193 		/* FALLTHRU */
194 	case SOUND_PCM_READ_BITS:
195 		retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
196 		if (retval < 0)
197 			return retval;
198 		switch (tmpinfo.play.encoding) {
199 		case AUDIO_ENCODING_ULAW:
200 			idat = AFMT_MU_LAW;
201 			break;
202 		case AUDIO_ENCODING_ALAW:
203 			idat = AFMT_A_LAW;
204 			break;
205 		case AUDIO_ENCODING_SLINEAR_LE:
206 			if (tmpinfo.play.precision == 16)
207 				idat = AFMT_S16_LE;
208 			else
209 				idat = AFMT_S8;
210 			break;
211 		case AUDIO_ENCODING_SLINEAR_BE:
212 			if (tmpinfo.play.precision == 16)
213 				idat = AFMT_S16_BE;
214 			else
215 				idat = AFMT_S8;
216 			break;
217 		case AUDIO_ENCODING_ULINEAR_LE:
218 			if (tmpinfo.play.precision == 16)
219 				idat = AFMT_U16_LE;
220 			else
221 				idat = AFMT_U8;
222 			break;
223 		case AUDIO_ENCODING_ULINEAR_BE:
224 			if (tmpinfo.play.precision == 16)
225 				idat = AFMT_U16_BE;
226 			else
227 				idat = AFMT_U8;
228 			break;
229 		case AUDIO_ENCODING_ADPCM:
230 			idat = AFMT_IMA_ADPCM;
231 			break;
232 		}
233 		INTARG = idat;
234 		break;
235 	case SNDCTL_DSP_CHANNELS:
236 		AUDIO_INITINFO(&tmpinfo);
237 		tmpinfo.play.channels =
238 		tmpinfo.record.channels = INTARG;
239 		(void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
240 		/* FALLTHRU */
241 	case SOUND_PCM_READ_CHANNELS:
242 		retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
243 		if (retval < 0)
244 			return retval;
245 		INTARG = tmpinfo.play.channels;
246 		break;
247 	case SOUND_PCM_WRITE_FILTER:
248 	case SOUND_PCM_READ_FILTER:
249 		errno = EINVAL;
250 		return -1; /* XXX unimplemented */
251 	case SNDCTL_DSP_SUBDIVIDE:
252 		retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
253 		if (retval < 0)
254 			return retval;
255 		setblocksize(fd, &tmpinfo);
256 		idat = INTARG;
257 		if (idat == 0)
258 			idat = tmpinfo.play.buffer_size / tmpinfo.blocksize;
259 		idat = (tmpinfo.play.buffer_size / idat) & -4;
260 		AUDIO_INITINFO(&tmpinfo);
261 		tmpinfo.blocksize = idat;
262 		retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo);
263 		if (retval < 0)
264 			return retval;
265 		INTARG = tmpinfo.play.buffer_size / tmpinfo.blocksize;
266 		break;
267 	case SNDCTL_DSP_SETFRAGMENT:
268 		AUDIO_INITINFO(&tmpinfo);
269 		idat = INTARG;
270 		if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17)
271 			return EINVAL;
272 		tmpinfo.blocksize = 1 << (idat & 0xffff);
273 		tmpinfo.hiwat = ((unsigned)idat >> 16) & 0x7fff;
274 		if (tmpinfo.hiwat == 0)	/* 0 means set to max */
275 			tmpinfo.hiwat = 65536;
276 		(void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
277 		retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
278 		if (retval < 0)
279 			return retval;
280 		u = tmpinfo.blocksize;
281 		for(idat = 0; u > 1; idat++, u >>= 1)
282 			;
283 		idat |= (tmpinfo.hiwat & 0x7fff) << 16;
284 		INTARG = idat;
285 		break;
286 	case SNDCTL_DSP_GETFMTS:
287 		for(idat = 0, tmpenc.index = 0;
288 		    ioctl(fd, AUDIO_GETENC, &tmpenc) == 0;
289 		    tmpenc.index++) {
290 			switch(tmpenc.encoding) {
291 			case AUDIO_ENCODING_ULAW:
292 				idat |= AFMT_MU_LAW;
293 				break;
294 			case AUDIO_ENCODING_ALAW:
295 				idat |= AFMT_A_LAW;
296 				break;
297 			case AUDIO_ENCODING_SLINEAR:
298 				idat |= AFMT_S8;
299 				break;
300 			case AUDIO_ENCODING_SLINEAR_LE:
301 				if (tmpenc.precision == 16)
302 					idat |= AFMT_S16_LE;
303 				else
304 					idat |= AFMT_S8;
305 				break;
306 			case AUDIO_ENCODING_SLINEAR_BE:
307 				if (tmpenc.precision == 16)
308 					idat |= AFMT_S16_BE;
309 				else
310 					idat |= AFMT_S8;
311 				break;
312 			case AUDIO_ENCODING_ULINEAR:
313 				idat |= AFMT_U8;
314 				break;
315 			case AUDIO_ENCODING_ULINEAR_LE:
316 				if (tmpenc.precision == 16)
317 					idat |= AFMT_U16_LE;
318 				else
319 					idat |= AFMT_U8;
320 				break;
321 			case AUDIO_ENCODING_ULINEAR_BE:
322 				if (tmpenc.precision == 16)
323 					idat |= AFMT_U16_BE;
324 				else
325 					idat |= AFMT_U8;
326 				break;
327 			case AUDIO_ENCODING_ADPCM:
328 				idat |= AFMT_IMA_ADPCM;
329 				break;
330 			default:
331 				break;
332 			}
333 		}
334 		INTARG = idat;
335 		break;
336 	case SNDCTL_DSP_GETOSPACE:
337 		retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
338 		if (retval < 0)
339 			return retval;
340 		setblocksize(fd, &tmpinfo);
341 		bufinfo.fragsize = tmpinfo.blocksize;
342 		bufinfo.fragments = tmpinfo.hiwat -
343 			(tmpinfo.play.seek + tmpinfo.blocksize - 1)/tmpinfo.blocksize;
344 		bufinfo.fragstotal = tmpinfo.hiwat;
345 		bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.play.seek;
346 		*(struct audio_buf_info *)argp = bufinfo;
347 		break;
348 	case SNDCTL_DSP_GETISPACE:
349 		retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
350 		if (retval < 0)
351 			return retval;
352 		setblocksize(fd, &tmpinfo);
353 		bufinfo.fragsize = tmpinfo.blocksize;
354 		bufinfo.fragments = tmpinfo.hiwat -
355 			(tmpinfo.record.seek + tmpinfo.blocksize - 1)/tmpinfo.blocksize;
356 		bufinfo.fragstotal = tmpinfo.hiwat;
357 		bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.record.seek;
358 		*(struct audio_buf_info *)argp = bufinfo;
359 		break;
360 	case SNDCTL_DSP_NONBLOCK:
361 		idat = 1;
362 		retval = ioctl(fd, FIONBIO, &idat);
363 		if (retval < 0)
364 			return retval;
365 		break;
366 	case SNDCTL_DSP_GETCAPS:
367 		retval = ioctl(fd, AUDIO_GETPROPS, &idata);
368 		if (retval < 0)
369 			return retval;
370 		idat = DSP_CAP_TRIGGER; /* pretend we have trigger */
371 		if (idata & AUDIO_PROP_FULLDUPLEX)
372 			idat |= DSP_CAP_DUPLEX;
373 		if (idata & AUDIO_PROP_MMAP)
374 			idat |= DSP_CAP_MMAP;
375 		INTARG = idat;
376 		break;
377 #if 0
378 	case SNDCTL_DSP_GETTRIGGER:
379 		retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
380 		if (retval < 0)
381 			return retval;
382 		idat = (tmpinfo.play.pause ? 0 : PCM_ENABLE_OUTPUT) |
383 		       (tmpinfo.record.pause ? 0 : PCM_ENABLE_INPUT);
384 		retval = copyout(&idat, SCARG(uap, data), sizeof idat);
385 		if (retval < 0)
386 			return retval;
387 		break;
388 	case SNDCTL_DSP_SETTRIGGER:
389 		AUDIO_INITINFO(&tmpinfo);
390 		retval = copyin(SCARG(uap, data), &idat, sizeof idat);
391 		if (retval < 0)
392 			return retval;
393 		tmpinfo.play.pause = (idat & PCM_ENABLE_OUTPUT) == 0;
394 		tmpinfo.record.pause = (idat & PCM_ENABLE_INPUT) == 0;
395 		(void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
396 		retval = copyout(&idat, SCARG(uap, data), sizeof idat);
397 		if (retval < 0)
398 			return retval;
399 		break;
400 #else
401 	case SNDCTL_DSP_GETTRIGGER:
402 	case SNDCTL_DSP_SETTRIGGER:
403 		/* XXX Do nothing for now. */
404 		INTARG = PCM_ENABLE_OUTPUT;
405 		break;
406 #endif
407 	case SNDCTL_DSP_GETIPTR:
408 		retval = ioctl(fd, AUDIO_GETIOFFS, &tmpoffs);
409 		if (retval < 0)
410 			return retval;
411 		cntinfo.bytes = tmpoffs.samples;
412 		cntinfo.blocks = tmpoffs.deltablks;
413 		cntinfo.ptr = tmpoffs.offset;
414 		*(struct count_info *)argp = cntinfo;
415 		break;
416 	case SNDCTL_DSP_GETOPTR:
417 		retval = ioctl(fd, AUDIO_GETOOFFS, &tmpoffs);
418 		if (retval < 0)
419 			return retval;
420 		cntinfo.bytes = tmpoffs.samples;
421 		cntinfo.blocks = tmpoffs.deltablks;
422 		cntinfo.ptr = tmpoffs.offset;
423 		*(struct count_info *)argp = cntinfo;
424 		break;
425 	case SNDCTL_DSP_MAPINBUF:
426 	case SNDCTL_DSP_MAPOUTBUF:
427 	case SNDCTL_DSP_SETSYNCRO:
428 	case SNDCTL_DSP_SETDUPLEX:
429 	case SNDCTL_DSP_PROFILE:
430 		errno = EINVAL;
431 		return -1; /* XXX unimplemented */
432 	default:
433 		errno = EINVAL;
434 		return -1;
435 	}
436 
437 	return 0;
438 }
439 
440 
441 /* If the NetBSD mixer device should have more than NETBSD_MAXDEVS devices
442  * some will not be available to Linux */
443 #define NETBSD_MAXDEVS 64
444 struct audiodevinfo {
445 	int done;
446 	dev_t dev;
447 	int16_t devmap[SOUND_MIXER_NRDEVICES],
448 	        rdevmap[NETBSD_MAXDEVS];
449 	char names[NETBSD_MAXDEVS][MAX_AUDIO_DEV_LEN];
450 	int enum2opaque[NETBSD_MAXDEVS];
451         u_long devmask, recmask, stereomask;
452 	u_long caps, source;
453 };
454 
455 static int
456 opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq)
457 {
458 	int i, o;
459 
460 	for (i = 0; i < NETBSD_MAXDEVS; i++) {
461 		o = di->enum2opaque[i];
462 		if (o == opq)
463 			break;
464 		if (o == -1 && label != NULL &&
465 		    !strncmp(di->names[i], label->name, sizeof di->names[i])) {
466 			di->enum2opaque[i] = opq;
467 			break;
468 		}
469 	}
470 	if (i >= NETBSD_MAXDEVS)
471 		i = -1;
472 	/*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/
473 	return (i);
474 }
475 
476 static int
477 enum_to_ord(struct audiodevinfo *di, int enm)
478 {
479 	if (enm >= NETBSD_MAXDEVS)
480 		return (-1);
481 
482 	/*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/
483 	return (di->enum2opaque[enm]);
484 }
485 
486 static int
487 enum_to_mask(struct audiodevinfo *di, int enm)
488 {
489 	int m;
490 	if (enm >= NETBSD_MAXDEVS)
491 		return (0);
492 
493 	m = di->enum2opaque[enm];
494 	if (m == -1)
495 		m = 0;
496 	/*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/
497 	return (m);
498 }
499 
500 /*
501  * Collect the audio device information to allow faster
502  * emulation of the Linux mixer ioctls.  Cache the information
503  * to eliminate the overhead of repeating all the ioctls needed
504  * to collect the information.
505  */
506 static struct audiodevinfo *
507 getdevinfo(int fd)
508 {
509 	mixer_devinfo_t mi;
510 	int i, j, e;
511 	static struct {
512 		char *name;
513 		int code;
514 	} *dp, devs[] = {
515 		{ AudioNmicrophone,	SOUND_MIXER_MIC },
516 		{ AudioNline,		SOUND_MIXER_LINE },
517 		{ AudioNcd,		SOUND_MIXER_CD },
518 		{ AudioNdac,		SOUND_MIXER_PCM },
519 		{ AudioNaux,		SOUND_MIXER_LINE1 },
520 		{ AudioNrecord,		SOUND_MIXER_IMIX },
521 		{ AudioNmaster,		SOUND_MIXER_VOLUME },
522 		{ AudioNtreble,		SOUND_MIXER_TREBLE },
523 		{ AudioNbass,		SOUND_MIXER_BASS },
524 		{ AudioNspeaker,	SOUND_MIXER_SPEAKER },
525 /*		{ AudioNheadphone,	?? },*/
526 		{ AudioNoutput,		SOUND_MIXER_OGAIN },
527 		{ AudioNinput,		SOUND_MIXER_IGAIN },
528 /*		{ AudioNmaster,		SOUND_MIXER_SPEAKER },*/
529 /*		{ AudioNstereo,		?? },*/
530 /*		{ AudioNmono,		?? },*/
531 		{ AudioNfmsynth,	SOUND_MIXER_SYNTH },
532 /*		{ AudioNwave,		SOUND_MIXER_PCM },*/
533 		{ AudioNmidi,		SOUND_MIXER_SYNTH },
534 /*		{ AudioNmixerout,	?? },*/
535 		{ 0, -1 }
536 	};
537 	static struct audiodevinfo devcache = { 0 };
538 	struct audiodevinfo *di = &devcache;
539 	struct stat sb;
540 
541 	/* Figure out what device it is so we can check if the
542 	 * cached data is valid.
543 	 */
544 	if (fstat(fd, &sb) < 0)
545 		return 0;
546 	if (di->done && di->dev == sb.st_dev)
547 		return di;
548 
549 	di->done = 1;
550 	di->dev = sb.st_dev;
551 	di->devmask = 0;
552 	di->recmask = 0;
553 	di->stereomask = 0;
554 	di->source = ~0;
555 	di->caps = 0;
556 	for(i = 0; i < SOUND_MIXER_NRDEVICES; i++)
557 		di->devmap[i] = -1;
558 	for(i = 0; i < NETBSD_MAXDEVS; i++) {
559 		di->rdevmap[i] = -1;
560 		di->names[i][0] = '\0';
561 		di->enum2opaque[i] = -1;
562 	}
563 	for(i = 0; i < NETBSD_MAXDEVS; i++) {
564 		mi.index = i;
565 		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0)
566 			break;
567 		switch(mi.type) {
568 		case AUDIO_MIXER_VALUE:
569 			for(dp = devs; dp->name; dp++)
570 		    		if (strcmp(dp->name, mi.label.name) == 0)
571 					break;
572 			if (dp->code >= 0) {
573 				di->devmap[dp->code] = i;
574 				di->rdevmap[i] = dp->code;
575 				di->devmask |= 1 << dp->code;
576 				if (mi.un.v.num_channels == 2)
577 					di->stereomask |= 1 << dp->code;
578 				strncpy(di->names[i], mi.label.name,
579 					sizeof di->names[i]);
580 			}
581 			break;
582 		}
583 	}
584 	for(i = 0; i < NETBSD_MAXDEVS; i++) {
585 		mi.index = i;
586 		if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0)
587 			break;
588 		if (strcmp(mi.label.name, AudioNsource) != 0)
589 			continue;
590 		di->source = i;
591 		switch(mi.type) {
592 		case AUDIO_MIXER_ENUM:
593 			for(j = 0; j < mi.un.e.num_mem; j++) {
594 				e = opaque_to_enum(di,
595 						   &mi.un.e.member[j].label,
596 						   mi.un.e.member[j].ord);
597 				if (e >= 0)
598 					di->recmask |= 1 << di->rdevmap[e];
599 			}
600 			di->caps = SOUND_CAP_EXCL_INPUT;
601 			break;
602 		case AUDIO_MIXER_SET:
603 			for(j = 0; j < mi.un.s.num_mem; j++) {
604 				e = opaque_to_enum(di,
605 						   &mi.un.s.member[j].label,
606 						   mi.un.s.member[j].mask);
607 				if (e >= 0)
608 					di->recmask |= 1 << di->rdevmap[e];
609 			}
610 			break;
611 		}
612 	}
613 	return di;
614 }
615 
616 int
617 mixer_ioctl(int fd, unsigned long com, void *argp)
618 {
619 	struct audiodevinfo *di;
620 	struct mixer_info *omi;
621 	struct audio_device adev;
622 	mixer_ctrl_t mc;
623 	int idat;
624 	int i;
625 	int retval;
626 	int l, r, n, error, e;
627 
628 	di = getdevinfo(fd);
629 	if (di == 0)
630 		return -1;
631 
632 	switch (com) {
633 	case OSS_GETVERSION:
634 		idat = SOUND_VERSION;
635 		break;
636 	case SOUND_MIXER_INFO:
637 	case SOUND_OLD_MIXER_INFO:
638 		error = ioctl(fd, AUDIO_GETDEV, &adev);
639 		if (error)
640 			return (error);
641 		omi = argp;
642 		if (com == SOUND_MIXER_INFO)
643 			omi->modify_counter = 1;
644 		strncpy(omi->id, adev.name, sizeof omi->id);
645 		strncpy(omi->name, adev.name, sizeof omi->name);
646 		return 0;
647 	case SOUND_MIXER_READ_RECSRC:
648 		if (di->source == -1)
649 			return EINVAL;
650 		mc.dev = di->source;
651 		if (di->caps & SOUND_CAP_EXCL_INPUT) {
652 			mc.type = AUDIO_MIXER_ENUM;
653 			retval = ioctl(fd, AUDIO_MIXER_READ, &mc);
654 			if (retval < 0)
655 				return retval;
656 			e = opaque_to_enum(di, NULL, mc.un.ord);
657 			if (e >= 0)
658 				idat = 1 << di->rdevmap[e];
659 		} else {
660 			mc.type = AUDIO_MIXER_SET;
661 			retval = ioctl(fd, AUDIO_MIXER_READ, &mc);
662 			if (retval < 0)
663 				return retval;
664 			e = opaque_to_enum(di, NULL, mc.un.mask);
665 			if (e >= 0)
666 				idat = 1 << di->rdevmap[e];
667 		}
668 		break;
669 	case SOUND_MIXER_READ_DEVMASK:
670 		idat = di->devmask;
671 		break;
672 	case SOUND_MIXER_READ_RECMASK:
673 		idat = di->recmask;
674 		break;
675 	case SOUND_MIXER_READ_STEREODEVS:
676 		idat = di->stereomask;
677 		break;
678 	case SOUND_MIXER_READ_CAPS:
679 		idat = di->caps;
680 		break;
681 	case SOUND_MIXER_WRITE_RECSRC:
682 	case SOUND_MIXER_WRITE_R_RECSRC:
683 		if (di->source == -1)
684 			return EINVAL;
685 		mc.dev = di->source;
686 		idat = INTARG;
687 		if (di->caps & SOUND_CAP_EXCL_INPUT) {
688 			mc.type = AUDIO_MIXER_ENUM;
689 			for(i = 0; i < SOUND_MIXER_NRDEVICES; i++)
690 				if (idat & (1 << i))
691 					break;
692 			if (i >= SOUND_MIXER_NRDEVICES ||
693 			    di->devmap[i] == -1)
694 				return EINVAL;
695 			mc.un.ord = enum_to_ord(di, di->devmap[i]);
696 		} else {
697 			mc.type = AUDIO_MIXER_SET;
698 			mc.un.mask = 0;
699 			for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
700 				if (idat & (1 << i)) {
701 					if (di->devmap[i] == -1)
702 						return EINVAL;
703 					mc.un.mask |= enum_to_mask(di, di->devmap[i]);
704 				}
705 			}
706 		}
707 		return ioctl(fd, AUDIO_MIXER_WRITE, &mc);
708 	default:
709 		if (MIXER_READ(SOUND_MIXER_FIRST) <= com &&
710 		    com < MIXER_READ(SOUND_MIXER_NRDEVICES)) {
711 			n = GET_DEV(com);
712 			if (di->devmap[n] == -1)
713 				return EINVAL;
714 			mc.dev = di->devmap[n];
715 			mc.type = AUDIO_MIXER_VALUE;
716 		    doread:
717 			mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1;
718 			retval = ioctl(fd, AUDIO_MIXER_READ, &mc);
719 			if (retval < 0)
720 				return retval;
721 			if (mc.type != AUDIO_MIXER_VALUE)
722 				return EINVAL;
723 			if (mc.un.value.num_channels != 2) {
724 				l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO];
725 			} else {
726 				l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT];
727 				r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
728 			}
729 			idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8);
730 			break;
731 		} else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com &&
732 			   com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) ||
733 			   (MIXER_WRITE(SOUND_MIXER_FIRST) <= com &&
734 			   com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) {
735 			n = GET_DEV(com);
736 			if (di->devmap[n] == -1)
737 				return EINVAL;
738 			idat = INTARG;
739 			l = FROM_OSSVOL( idat       & 0xff);
740 			r = FROM_OSSVOL((idat >> 8) & 0xff);
741 			mc.dev = di->devmap[n];
742 			mc.type = AUDIO_MIXER_VALUE;
743 			if (di->stereomask & (1<<n)) {
744 				mc.un.value.num_channels = 2;
745 				mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
746 				mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
747 			} else {
748 				mc.un.value.num_channels = 1;
749 				mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2;
750 			}
751 			retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc);
752 			if (retval < 0)
753 				return retval;
754 			if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com &&
755 			   com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))
756 				return 0;
757 			goto doread;
758 		} else {
759 			errno = EINVAL;
760 			return -1;
761 		}
762 	}
763 	INTARG = idat;
764 	return 0;
765 }
766 
767 /*
768  * Check that the blocksize is a power of 2 as OSS wants.
769  * If not, set it to be.
770  */
771 static void
772 setblocksize(int fd, struct audio_info *info)
773 {
774 	struct audio_info set;
775 	int s;
776 
777 	if (info->blocksize & (info->blocksize-1)) {
778 		for(s = 32; s < info->blocksize; s <<= 1)
779 			;
780 		AUDIO_INITINFO(&set);
781 		set.blocksize = s;
782 		ioctl(fd, AUDIO_SETINFO, &set);
783 		ioctl(fd, AUDIO_GETINFO, info);
784 	}
785 }
786