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