1 /**	$MirOS: src/sys/compat/ossaudio/ossaudio.c,v 1.2 2005/07/19 13:10:42 tg Exp $ */
2 /*	$OpenBSD: ossaudio.c,v 1.8 2002/03/14 01:26:50 millert Exp $	*/
3 /*	$NetBSD: ossaudio.c,v 1.23 1997/10/19 07:41:52 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 #include <sys/param.h>
39 #include <sys/proc.h>
40 #include <sys/systm.h>
41 #include <sys/file.h>
42 #include <sys/vnode.h>
43 #include <sys/filedesc.h>
44 #include <sys/ioctl.h>
45 #include <sys/mount.h>
46 #include <sys/audioio.h>
47 
48 #include <sys/syscallargs.h>
49 
50 #include <compat/ossaudio/ossaudio.h>
51 #include <compat/ossaudio/ossaudiovar.h>
52 
53 #ifdef AUDIO_DEBUG
54 #define DPRINTF(x) if (ossdebug) printf x
55 int ossdebug = 0;
56 #else
57 #define DPRINTF(x)
58 #endif
59 
60 #define TO_OSSVOL(x) ((x) * 100 / 255)
61 #define FROM_OSSVOL(x) ((x) * 255 / 100)
62 
63 static struct audiodevinfo *getdevinfo(struct file *, struct proc *);
64 
65 static void setblocksize(struct file *, struct audio_info *, struct proc *);
66 
67 
68 int
oss_ioctl_audio(p,uap,retval)69 oss_ioctl_audio(p, uap, retval)
70 	struct proc *p;
71 	struct oss_sys_ioctl_args /* {
72 		syscallarg(int) fd;
73 		syscallarg(u_long) com;
74 		syscallarg(caddr_t) data;
75 	} */ *uap;
76 	register_t *retval;
77 {
78 	struct file *fp;
79 	struct filedesc *fdp;
80 	u_long com;
81 	struct audio_info tmpinfo;
82 	struct audio_offset tmpoffs;
83 	struct oss_audio_buf_info bufinfo;
84 	struct oss_count_info cntinfo;
85 	struct audio_encoding tmpenc;
86 	u_int u;
87 	int idat, idata;
88 	int error;
89 	int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *);
90 
91 	fdp = p->p_fd;
92 	if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
93 		return (EBADF);
94 	FREF(fp);
95 
96 	if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
97 		error = EBADF;
98 		goto out;
99 	}
100 
101 	ioctlf = fp->f_ops->fo_ioctl;
102 
103 	com = SCARG(uap, com);
104 	retval[0] = 0;
105 
106 	DPRINTF(("oss_sys_ioctl: com=%08lx\n", com));
107 	switch (com) {
108 	case OSS_SNDCTL_DSP_RESET:
109 		error = ioctlf(fp, AUDIO_FLUSH, (caddr_t)0, p);
110 		if (error)
111 			goto out;
112 		break;
113 	case OSS_SNDCTL_DSP_SYNC:
114 	case OSS_SNDCTL_DSP_POST:
115 		error = ioctlf(fp, AUDIO_DRAIN, (caddr_t)0, p);
116 		if (error)
117 			goto out;
118 		break;
119 	case OSS_SNDCTL_DSP_SPEED:
120 		AUDIO_INITINFO(&tmpinfo);
121 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
122 		if (error)
123 			goto out;
124 		tmpinfo.play.sample_rate =
125 		tmpinfo.record.sample_rate = idat;
126 		error = ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
127 		DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_SPEED %d = %d\n",
128 			 idat, error));
129 		if (error)
130 			goto out;
131 		/* fall into ... */
132 	case OSS_SOUND_PCM_READ_RATE:
133 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
134 		if (error)
135 			goto out;
136 		idat = tmpinfo.play.sample_rate;
137 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
138 		if (error)
139 			goto out;
140 		break;
141 	case OSS_SNDCTL_DSP_STEREO:
142 		AUDIO_INITINFO(&tmpinfo);
143 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
144 		if (error)
145 			goto out;
146 		tmpinfo.play.channels =
147 		tmpinfo.record.channels = idat ? 2 : 1;
148 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
149 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
150 		if (error)
151 			goto out;
152 		idat = tmpinfo.play.channels - 1;
153 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
154 		if (error)
155 			goto out;
156 		break;
157 	case OSS_SNDCTL_DSP_GETBLKSIZE:
158 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
159 		if (error)
160 			goto out;
161 		setblocksize(fp, &tmpinfo, p);
162 		idat = tmpinfo.blocksize;
163 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
164 		if (error)
165 			goto out;
166 		break;
167 	case OSS_SNDCTL_DSP_SETFMT:
168 		AUDIO_INITINFO(&tmpinfo);
169 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
170 		if (error)
171 			goto out;
172 		switch (idat) {
173 		case OSS_AFMT_MU_LAW:
174 			tmpinfo.play.precision =
175 			tmpinfo.record.precision = 8;
176 			tmpinfo.play.encoding =
177 			tmpinfo.record.encoding = AUDIO_ENCODING_ULAW;
178 			break;
179 		case OSS_AFMT_A_LAW:
180 			tmpinfo.play.precision =
181 			tmpinfo.record.precision = 8;
182 			tmpinfo.play.encoding =
183 			tmpinfo.record.encoding = AUDIO_ENCODING_ALAW;
184 			break;
185 		case OSS_AFMT_U8:
186 			tmpinfo.play.precision =
187 			tmpinfo.record.precision = 8;
188 			tmpinfo.play.encoding =
189 			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR;
190 			break;
191 		case OSS_AFMT_S8:
192 			tmpinfo.play.precision =
193 			tmpinfo.record.precision = 8;
194 			tmpinfo.play.encoding =
195 			tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR;
196 			break;
197 		case OSS_AFMT_S16_LE:
198 			tmpinfo.play.precision =
199 			tmpinfo.record.precision = 16;
200 			tmpinfo.play.encoding =
201 			tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE;
202 			break;
203 		case OSS_AFMT_S16_BE:
204 			tmpinfo.play.precision =
205 			tmpinfo.record.precision = 16;
206 			tmpinfo.play.encoding =
207 			tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE;
208 			break;
209 		case OSS_AFMT_U16_LE:
210 			tmpinfo.play.precision =
211 			tmpinfo.record.precision = 16;
212 			tmpinfo.play.encoding =
213 			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE;
214 			break;
215 		case OSS_AFMT_U16_BE:
216 			tmpinfo.play.precision =
217 			tmpinfo.record.precision = 16;
218 			tmpinfo.play.encoding =
219 			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE;
220 			break;
221 		default:
222 			error = EINVAL;
223 			goto out;
224 		}
225 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
226 		/* fall into ... */
227 	case OSS_SOUND_PCM_READ_BITS:
228 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
229 		if (error)
230 			goto out;
231 		switch (tmpinfo.play.encoding) {
232 		case AUDIO_ENCODING_ULAW:
233 			idat = OSS_AFMT_MU_LAW;
234 			break;
235 		case AUDIO_ENCODING_ALAW:
236 			idat = OSS_AFMT_A_LAW;
237 			break;
238 		case AUDIO_ENCODING_SLINEAR_LE:
239 			if (tmpinfo.play.precision == 16)
240 				idat = OSS_AFMT_S16_LE;
241 			else
242 				idat = OSS_AFMT_S8;
243 			break;
244 		case AUDIO_ENCODING_SLINEAR_BE:
245 			if (tmpinfo.play.precision == 16)
246 				idat = OSS_AFMT_S16_BE;
247 			else
248 				idat = OSS_AFMT_S8;
249 			break;
250 		case AUDIO_ENCODING_ULINEAR_LE:
251 			if (tmpinfo.play.precision == 16)
252 				idat = OSS_AFMT_U16_LE;
253 			else
254 				idat = OSS_AFMT_U8;
255 			break;
256 		case AUDIO_ENCODING_ULINEAR_BE:
257 			if (tmpinfo.play.precision == 16)
258 				idat = OSS_AFMT_U16_BE;
259 			else
260 				idat = OSS_AFMT_U8;
261 			break;
262 		case AUDIO_ENCODING_ADPCM:
263 			idat = OSS_AFMT_IMA_ADPCM;
264 			break;
265 		}
266 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
267 		if (error)
268 			goto out;
269 		break;
270 	case OSS_SNDCTL_DSP_CHANNELS:
271 		AUDIO_INITINFO(&tmpinfo);
272 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
273 		if (error)
274 			goto out;
275 		tmpinfo.play.channels =
276 		tmpinfo.record.channels = idat;
277 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
278 		/* fall into ... */
279 	case OSS_SOUND_PCM_READ_CHANNELS:
280 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
281 		if (error)
282 			goto out;
283 		idat = tmpinfo.play.channels;
284 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
285 		if (error)
286 			goto out;
287 		break;
288 	case OSS_SOUND_PCM_WRITE_FILTER:
289 	case OSS_SOUND_PCM_READ_FILTER:
290 		error = EINVAL; /* XXX unimplemented */
291 		goto out;
292 	case OSS_SNDCTL_DSP_SUBDIVIDE:
293 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
294 		if (error)
295 			goto out;
296 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
297 		setblocksize(fp, &tmpinfo, p);
298 		if (error)
299 			goto out;
300 		if (idat == 0)
301 			idat = tmpinfo.play.buffer_size / tmpinfo.blocksize;
302 		idat = (tmpinfo.play.buffer_size / idat) & -4;
303 		AUDIO_INITINFO(&tmpinfo);
304 		tmpinfo.blocksize = idat;
305 		error = ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
306 		if (error)
307 			goto out;
308 		idat = tmpinfo.play.buffer_size / tmpinfo.blocksize;
309 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
310 		if (error)
311 			goto out;
312 		break;
313 	case OSS_SNDCTL_DSP_SETFRAGMENT:
314 		AUDIO_INITINFO(&tmpinfo);
315 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
316 		if (error)
317 			goto out;
318 		if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) {
319 			error = EINVAL;
320 			goto out;
321 		}
322 		tmpinfo.blocksize = 1 << (idat & 0xffff);
323 		tmpinfo.hiwat = (idat >> 16) & 0x7fff;
324 		DPRINTF(("oss_audio: SETFRAGMENT blksize=%d, hiwat=%d\n",
325 			 tmpinfo.blocksize, tmpinfo.hiwat));
326 		if (tmpinfo.hiwat == 0)	/* 0 means set to max */
327 			tmpinfo.hiwat = 65536;
328 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
329 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
330 		if (error)
331 			goto out;
332 		u = tmpinfo.blocksize;
333 		for(idat = 0; u > 1; idat++, u >>= 1)
334 			;
335 		idat |= (tmpinfo.hiwat & 0x7fff) << 16;
336 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
337 		if (error)
338 			goto out;
339 		break;
340 	case OSS_SNDCTL_DSP_GETFMTS:
341 		for(idat = 0, tmpenc.index = 0;
342 		    ioctlf(fp, AUDIO_GETENC, (caddr_t)&tmpenc, p) == 0;
343 		    tmpenc.index++) {
344 			if (tmpenc.flags & AUDIO_ENCODINGFLAG_EMULATED)
345 				continue; /* Don't report emulated modes */
346 			switch(tmpenc.encoding) {
347 			case AUDIO_ENCODING_ULAW:
348 				idat |= OSS_AFMT_MU_LAW;
349 				break;
350 			case AUDIO_ENCODING_ALAW:
351 				idat |= OSS_AFMT_A_LAW;
352 				break;
353 			case AUDIO_ENCODING_SLINEAR:
354 				idat |= OSS_AFMT_S8;
355 				break;
356 			case AUDIO_ENCODING_SLINEAR_LE:
357 				if (tmpenc.precision == 16)
358 					idat |= OSS_AFMT_S16_LE;
359 				else
360 					idat |= OSS_AFMT_S8;
361 				break;
362 			case AUDIO_ENCODING_SLINEAR_BE:
363 				if (tmpenc.precision == 16)
364 					idat |= OSS_AFMT_S16_BE;
365 				else
366 					idat |= OSS_AFMT_S8;
367 				break;
368 			case AUDIO_ENCODING_ULINEAR:
369 				idat |= OSS_AFMT_U8;
370 				break;
371 			case AUDIO_ENCODING_ULINEAR_LE:
372 				if (tmpenc.precision == 16)
373 					idat |= OSS_AFMT_U16_LE;
374 				else
375 					idat |= OSS_AFMT_U8;
376 				break;
377 			case AUDIO_ENCODING_ULINEAR_BE:
378 				if (tmpenc.precision == 16)
379 					idat |= OSS_AFMT_U16_BE;
380 				else
381 					idat |= OSS_AFMT_U8;
382 				break;
383 			case AUDIO_ENCODING_ADPCM:
384 				idat |= OSS_AFMT_IMA_ADPCM;
385 				break;
386 			default:
387 				break;
388 			}
389 		}
390 		DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETFMTS = %x\n", idat));
391 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
392 		if (error)
393 			goto out;
394 		break;
395 	case OSS_SNDCTL_DSP_GETOSPACE:
396 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
397 		if (error)
398 			goto out;
399 		setblocksize(fp, &tmpinfo, p);
400 		bufinfo.fragsize = tmpinfo.blocksize;
401 		bufinfo.fragments = tmpinfo.hiwat -
402 		    (tmpinfo.play.seek + tmpinfo.blocksize - 1) /
403 		    tmpinfo.blocksize;
404 		bufinfo.fragstotal = tmpinfo.hiwat;
405 		bufinfo.bytes =
406 		    tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.play.seek;
407 		error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo);
408 		if (error)
409 			goto out;
410 		break;
411 	case OSS_SNDCTL_DSP_GETISPACE:
412 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
413 		if (error)
414 			goto out;
415 		setblocksize(fp, &tmpinfo, p);
416 		bufinfo.fragsize = tmpinfo.blocksize;
417 		bufinfo.fragments = tmpinfo.hiwat -
418 		    (tmpinfo.record.seek + tmpinfo.blocksize - 1) /
419 		    tmpinfo.blocksize;
420 		bufinfo.fragstotal = tmpinfo.hiwat;
421 		bufinfo.bytes =
422 		    tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.record.seek;
423 		DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETxSPACE = %d %d %d %d\n",
424 			 bufinfo.fragsize, bufinfo.fragments,
425 			 bufinfo.fragstotal, bufinfo.bytes));
426 		error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo);
427 		if (error)
428 			goto out;
429 		break;
430 	case OSS_SNDCTL_DSP_NONBLOCK:
431 		idat = 1;
432 		error = ioctlf(fp, FIONBIO, (caddr_t)&idat, p);
433 		if (error)
434 			goto out;
435 		break;
436 	case OSS_SNDCTL_DSP_GETCAPS:
437 		error = ioctlf(fp, AUDIO_GETPROPS, (caddr_t)&idata, p);
438 		if (error)
439 			goto out;
440 		idat = OSS_DSP_CAP_TRIGGER; /* pretend we have trigger */
441 		if (idata & AUDIO_PROP_FULLDUPLEX)
442 			idat |= OSS_DSP_CAP_DUPLEX;
443 		if (idata & AUDIO_PROP_MMAP)
444 			idat |= OSS_DSP_CAP_MMAP;
445 		DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETCAPS = %x\n", idat));
446 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
447 		if (error)
448 			goto out;
449 		break;
450 #if 0
451 	case OSS_SNDCTL_DSP_GETTRIGGER:
452 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
453 		if (error)
454 			goto out;
455 		idat = (tmpinfo.play.pause ? 0 : OSS_PCM_ENABLE_OUTPUT) |
456 		       (tmpinfo.record.pause ? 0 : OSS_PCM_ENABLE_INPUT);
457 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
458 		if (error)
459 			goto out;
460 		break;
461 	case OSS_SNDCTL_DSP_SETTRIGGER:
462 		AUDIO_INITINFO(&tmpinfo);
463 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
464 		if (error)
465 			goto out;
466 		tmpinfo.play.pause = (idat & OSS_PCM_ENABLE_OUTPUT) == 0;
467 		tmpinfo.record.pause = (idat & OSS_PCM_ENABLE_INPUT) == 0;
468 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
469 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
470 		if (error)
471 			goto out;
472 		break;
473 #else
474 	case OSS_SNDCTL_DSP_GETTRIGGER:
475 	case OSS_SNDCTL_DSP_SETTRIGGER:
476 		/* XXX Do nothing for now. */
477 		idat = OSS_PCM_ENABLE_OUTPUT;
478 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
479 		goto out;
480 #endif
481 	case OSS_SNDCTL_DSP_GETIPTR:
482 		error = ioctlf(fp, AUDIO_GETIOFFS, (caddr_t)&tmpoffs, p);
483 		if (error)
484 			goto out;
485 		cntinfo.bytes = tmpoffs.samples;
486 		cntinfo.blocks = tmpoffs.deltablks;
487 		cntinfo.ptr = tmpoffs.offset;
488 		error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo);
489 		if (error)
490 			goto out;
491 		break;
492 	case OSS_SNDCTL_DSP_GETOPTR:
493 		error = ioctlf(fp, AUDIO_GETOOFFS, (caddr_t)&tmpoffs, p);
494 		if (error)
495 			goto out;
496 		cntinfo.bytes = tmpoffs.samples;
497 		cntinfo.blocks = tmpoffs.deltablks;
498 		cntinfo.ptr = tmpoffs.offset;
499 		error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo);
500 		if (error)
501 			goto out;
502 		break;
503 	case OSS_SNDCTL_DSP_SETDUPLEX:
504 		idat = 1;
505 		error = ioctlf(fp, AUDIO_SETFD, (caddr_t)&idat, p);
506 		if (error)
507 			goto out;
508 		break;
509 	case OSS_SNDCTL_DSP_MAPINBUF:
510 	case OSS_SNDCTL_DSP_MAPOUTBUF:
511 	case OSS_SNDCTL_DSP_SETSYNCRO:
512 	case OSS_SNDCTL_DSP_PROFILE:
513 		error = EINVAL; /* XXX unimplemented */
514 		goto out;
515 	default:
516 		error = EINVAL;
517 		goto out;
518 	}
519 
520 	error = 0;
521 
522 out:
523 	FRELE(fp);
524 	return (error);
525 }
526 
527 /* If the NetBSD mixer device should have more than 32 devices
528  * some will not be available to Linux */
529 #define NETBSD_MAXDEVS 64
530 struct audiodevinfo {
531 	int done;
532 	dev_t dev;
533 	int16_t devmap[OSS_SOUND_MIXER_NRDEVICES],
534 	        rdevmap[NETBSD_MAXDEVS];
535         u_long devmask, recmask, stereomask;
536 	u_long caps, source;
537 };
538 
539 /*
540  * Collect the audio device information to allow faster
541  * emulation of the Linux mixer ioctls.  Cache the information
542  * to eliminate the overhead of repeating all the ioctls needed
543  * to collect the information.
544  */
545 static struct audiodevinfo *
getdevinfo(fp,p)546 getdevinfo(fp, p)
547 	struct file *fp;
548 	struct proc *p;
549 {
550 	mixer_devinfo_t mi;
551 	int i;
552 	static const struct oss_devs {
553 		const char *name;
554 		int code;
555 	} devs[] = {
556 		{ AudioNmicrophone,	OSS_SOUND_MIXER_MIC },
557 		{ AudioNline,		OSS_SOUND_MIXER_LINE },
558 		{ AudioNcd,		OSS_SOUND_MIXER_CD },
559 		{ AudioNdac,		OSS_SOUND_MIXER_PCM },
560 		{ AudioNrecord,		OSS_SOUND_MIXER_IMIX },
561 		{ AudioNmaster,		OSS_SOUND_MIXER_VOLUME },
562 		{ AudioNtreble,		OSS_SOUND_MIXER_TREBLE },
563 		{ AudioNbass,		OSS_SOUND_MIXER_BASS },
564 		{ AudioNspeaker,	OSS_SOUND_MIXER_SPEAKER },
565 /*		{ AudioNheadphone,	?? },*/
566 		{ AudioNoutput,		OSS_SOUND_MIXER_OGAIN },
567 		{ AudioNinput,		OSS_SOUND_MIXER_IGAIN },
568 /*		{ AudioNmaster,		OSS_SOUND_MIXER_SPEAKER },*/
569 /*		{ AudioNstereo,		?? },*/
570 /*		{ AudioNmono,		?? },*/
571 		{ AudioNfmsynth,	OSS_SOUND_MIXER_SYNTH },
572 /*		{ AudioNwave,		OSS_SOUND_MIXER_PCM },*/
573 		{ AudioNmidi,		OSS_SOUND_MIXER_SYNTH },
574 /*		{ AudioNmixerout,	?? },*/
575 		{ 0, -1 }
576 	};
577 	register const struct oss_devs *dp;
578 	int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *) =
579 	    fp->f_ops->fo_ioctl;
580 	struct vnode *vp;
581 	struct vattr va;
582 	static struct audiodevinfo devcache = { 0 };
583 	register struct audiodevinfo *di = &devcache;
584 
585 	/* Figure out what device it is so we can check if the
586 	 * cached data is valid.
587 	 */
588 	vp = (struct vnode *)fp->f_data;
589 	if (vp->v_type != VCHR)
590 		return 0;
591 	if (VOP_GETATTR(vp, &va, p->p_ucred, p))
592 		return 0;
593 	if (di->done && di->dev == va.va_rdev)
594 		return di;
595 
596 	di->done = 1;
597 	di->dev = va.va_rdev;
598 	di->devmask = 0;
599 	di->recmask = 0;
600 	di->stereomask = 0;
601 	di->source = -1;
602 	di->caps = 0;
603 	for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
604 		di->devmap[i] = -1;
605 	for(i = 0; i < NETBSD_MAXDEVS; i++)
606 		di->rdevmap[i] = -1;
607 	for(i = 0; i < NETBSD_MAXDEVS; i++) {
608 		mi.index = i;
609 		if (ioctlf(fp, AUDIO_MIXER_DEVINFO, (caddr_t)&mi, p) < 0)
610 			break;
611 		switch(mi.type) {
612 		case AUDIO_MIXER_VALUE:
613 			for(dp = devs; dp->name; dp++)
614 		    		if (strcmp(dp->name, mi.label.name) == 0)
615 					break;
616 			if (dp->code >= 0) {
617 				di->devmap[dp->code] = i;
618 				di->rdevmap[i] = dp->code;
619 				di->devmask |= 1 << dp->code;
620 				if (mi.un.v.num_channels == 2)
621 					di->stereomask |= 1 << dp->code;
622 			}
623 			break;
624 		case AUDIO_MIXER_ENUM:
625 			if (strcmp(mi.label.name, AudioNsource) == 0) {
626 				int j;
627 				di->source = i;
628 				for(j = 0; j < mi.un.e.num_mem; j++)
629 					di->recmask |= 1 << di->rdevmap[mi.un.e.member[j].ord];
630 				di->caps = OSS_SOUND_CAP_EXCL_INPUT;
631 			}
632 			break;
633 		case AUDIO_MIXER_SET:
634 			if (strcmp(mi.label.name, AudioNsource) == 0) {
635 				int j;
636 				di->source = i;
637 				for(j = 0; j < mi.un.s.num_mem; j++) {
638 					int k, mask = mi.un.s.member[j].mask;
639 					if (mask) {
640 						for(k = 0; !(mask & 1); mask >>= 1, k++)
641 							;
642 						di->recmask |= 1 << di->rdevmap[k];
643 					}
644 				}
645 			}
646 			break;
647 		}
648 	}
649 	return di;
650 }
651 
652 int
oss_ioctl_mixer(p,uap,retval)653 oss_ioctl_mixer(p, uap, retval)
654 	struct proc *p;
655 	struct oss_sys_ioctl_args /* {
656 		syscallarg(int) fd;
657 		syscallarg(u_long) com;
658 		syscallarg(caddr_t) data;
659 	} */ *uap;
660 	register_t *retval;
661 {
662 	struct file *fp;
663 	struct filedesc *fdp;
664 	u_long com;
665 	struct audiodevinfo *di;
666 	mixer_ctrl_t mc;
667 	int idat;
668 	int i;
669 	int error;
670 	int l, r, n;
671 	int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *);
672 
673 	fdp = p->p_fd;
674 	if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
675 		return (EBADF);
676 	FREF(fp);
677 
678 	if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
679 		error = EBADF;
680 		goto out;
681 	}
682 
683 	com = SCARG(uap, com);
684 	retval[0] = 0;
685 
686 	di = getdevinfo(fp, p);
687 	if (di == 0) {
688 		error = EINVAL;
689 		goto out;
690 	}
691 
692 	ioctlf = fp->f_ops->fo_ioctl;
693 	switch (com) {
694 	case OSS_SOUND_MIXER_READ_RECSRC:
695 		if (di->source == -1) {
696 			error = EINVAL;
697 			goto out;
698 		}
699 		mc.dev = di->source;
700 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
701 			mc.type = AUDIO_MIXER_ENUM;
702 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
703 			if (error)
704 				goto out;
705 			idat = 1 << di->rdevmap[mc.un.ord];
706 		} else {
707 			int k;
708 			unsigned int mask;
709 			mc.type = AUDIO_MIXER_SET;
710 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
711 			if (error)
712 				goto out;
713 			idat = 0;
714 			for(mask = mc.un.mask, k = 0; mask; mask >>= 1, k++)
715 				if (mask & 1)
716 					idat |= 1 << di->rdevmap[k];
717 		}
718 		break;
719 	case OSS_SOUND_MIXER_READ_DEVMASK:
720 		idat = di->devmask;
721 		break;
722 	case OSS_SOUND_MIXER_READ_RECMASK:
723 		idat = di->recmask;
724 		break;
725 	case OSS_SOUND_MIXER_READ_STEREODEVS:
726 		idat = di->stereomask;
727 		break;
728 	case OSS_SOUND_MIXER_READ_CAPS:
729 		idat = di->caps;
730 		break;
731 	case OSS_SOUND_MIXER_WRITE_RECSRC:
732 	case OSS_SOUND_MIXER_WRITE_R_RECSRC:
733 		if (di->source == -1) {
734 			error = EINVAL;
735 			goto out;
736 		}
737 		mc.dev = di->source;
738 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
739 		if (error)
740 			goto out;
741 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
742 			mc.type = AUDIO_MIXER_ENUM;
743 			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
744 				if (idat & (1 << i))
745 					break;
746 			if (i >= OSS_SOUND_MIXER_NRDEVICES ||
747 			    di->devmap[i] == -1) {
748 				error = EINVAL;
749 				goto out;
750 			}
751 			mc.un.ord = di->devmap[i];
752 		} else {
753 			mc.type = AUDIO_MIXER_SET;
754 			mc.un.mask = 0;
755 			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) {
756 				if (idat & (1 << i)) {
757 					if (di->devmap[i] == -1) {
758 						error = EINVAL;
759 						goto out;
760 					}
761 					mc.un.mask |= 1 << di->devmap[i];
762 				}
763 			}
764 		}
765 		error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p);
766 		goto out;
767 	default:
768 		if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com &&
769 		    com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) {
770 			n = OSS_GET_DEV(com);
771 			if (di->devmap[n] == -1) {
772 				error = EINVAL;
773 				goto out;
774 			}
775 		    doread:
776 			mc.dev = di->devmap[n];
777 			mc.type = AUDIO_MIXER_VALUE;
778 			mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1;
779 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
780 			if (error)
781 				goto out;
782 			if (mc.un.value.num_channels != 2) {
783 				l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO];
784 			} else {
785 				l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT];
786 				r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
787 			}
788 			idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8);
789 			DPRINTF(("OSS_MIXER_READ  n=%d (dev=%d) l=%d, r=%d, idat=%04x\n",
790 				 n, di->devmap[n], l, r, idat));
791 			break;
792 		} else if ((OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_FIRST) <= com &&
793 			   com < OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_NRDEVICES)) ||
794 			   (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com &&
795 			   com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES))) {
796 			n = OSS_GET_DEV(com);
797 			if (di->devmap[n] == -1) {
798 				error = EINVAL;
799 				goto out;
800 			}
801 			error = copyin(SCARG(uap, data), &idat, sizeof idat);
802 			if (error)
803 				goto out;
804 			l = FROM_OSSVOL( idat       & 0xff);
805 			r = FROM_OSSVOL((idat >> 8) & 0xff);
806 			mc.dev = di->devmap[n];
807 			mc.type = AUDIO_MIXER_VALUE;
808 			if (di->stereomask & (1<<n)) {
809 				mc.un.value.num_channels = 2;
810 				mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
811 				mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
812 			} else {
813 				mc.un.value.num_channels = 1;
814 				mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2;
815 			}
816 			DPRINTF(("OSS_MIXER_WRITE n=%d (dev=%d) l=%d, r=%d, idat=%04x\n",
817 				 n, di->devmap[n], l, r, idat));
818 			error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p);
819 			if (error)
820 				goto out;
821 			if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com &&
822 			   com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) {
823 				error = 0;
824 				goto out;
825 			}
826 			goto doread;
827 		} else {
828 #ifdef AUDIO_DEBUG
829 			printf("oss_audio: unknown mixer ioctl %04lx\n", com);
830 #endif
831 			error = EINVAL;
832 			goto out;
833 		}
834 	}
835 	error = copyout(&idat, SCARG(uap, data), sizeof idat);
836 
837 out:
838 	FRELE(fp);
839 	return (error);
840 }
841 
842 /* XXX hook for sequencer emulation */
843 int
oss_ioctl_sequencer(p,uap,retval)844 oss_ioctl_sequencer(p, uap, retval)
845 	struct proc *p;
846 	struct oss_sys_ioctl_args /* {
847 		syscallarg(int) fd;
848 		syscallarg(u_long) com;
849 		syscallarg(caddr_t) data;
850 	} */ *uap;
851 	register_t *retval;
852 {
853 	struct file *fp;
854 	struct filedesc *fdp;
855 #if 0
856 	u_long com;
857 	int idat;
858 	int error;
859 #endif
860 
861 	fdp = p->p_fd;
862 	if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
863 		return (EBADF);
864 
865 	if ((fp->f_flag & (FREAD | FWRITE)) == 0)
866 		return (EBADF);
867 
868 #if 0
869 	com = SCARG(uap, com);
870 #endif
871 	retval[0] = 0;
872 
873 	return EINVAL;
874 }
875 
876 /*
877  * Check that the blocksize is a power of 2 as OSS wants.
878  * If not, set it to be.
879  */
setblocksize(fp,info,p)880 static void setblocksize(fp, info, p)
881 	struct file *fp;
882 	struct audio_info *info;
883 	struct proc *p;
884 {
885 	struct audio_info set;
886 	int s;
887 
888 	if (info->blocksize & (info->blocksize-1)) {
889 		for(s = 32; s < info->blocksize; s <<= 1)
890 			;
891 		AUDIO_INITINFO(&set);
892 		set.blocksize = s;
893 		fp->f_ops->fo_ioctl(fp, AUDIO_SETINFO, (caddr_t)&set, p);
894 		fp->f_ops->fo_ioctl(fp, AUDIO_GETINFO, (caddr_t)info, p);
895 	}
896 }
897