xref: /netbsd-src/sys/compat/ossaudio/ossaudio.c (revision 220b5c059a84c51ea44107ea8951a57ffaecdc8c)
1 /*	$NetBSD: ossaudio.c,v 1.38 2001/11/13 02:09:17 lukem Exp $	*/
2 
3 /*-
4  * Copyright (c) 1997 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *        This product includes software developed by the NetBSD
18  *        Foundation, Inc. and its contributors.
19  * 4. Neither the name of The NetBSD Foundation nor the names of its
20  *    contributors may be used to endorse or promote products derived
21  *    from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: ossaudio.c,v 1.38 2001/11/13 02:09:17 lukem Exp $");
38 
39 #include <sys/param.h>
40 #include <sys/proc.h>
41 #include <sys/systm.h>
42 #include <sys/file.h>
43 #include <sys/vnode.h>
44 #include <sys/filedesc.h>
45 #include <sys/ioctl.h>
46 #include <sys/mount.h>
47 #include <sys/kernel.h>
48 #include <sys/audioio.h>
49 #include <sys/midiio.h>
50 
51 #include <sys/syscallargs.h>
52 
53 #include <compat/ossaudio/ossaudio.h>
54 #include <compat/ossaudio/ossaudiovar.h>
55 
56 #ifdef AUDIO_DEBUG
57 #define DPRINTF(x) if (ossdebug) printf x
58 int ossdebug = 0;
59 #else
60 #define DPRINTF(x)
61 #endif
62 
63 #define TO_OSSVOL(x)	(((x) * 100 + 127) / 255)
64 #define FROM_OSSVOL(x)	((((x) > 100 ? 100 : (x)) * 255 + 50) / 100)
65 
66 static struct audiodevinfo *getdevinfo __P((struct file *, struct proc *));
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 static void setblocksize __P((struct file *, struct audio_info *, struct proc *));
72 
73 
74 int
75 oss_ioctl_audio(p, uap, retval)
76 	struct proc *p;
77 	struct oss_sys_ioctl_args /* {
78 		syscallarg(int) fd;
79 		syscallarg(u_long) com;
80 		syscallarg(caddr_t) data;
81 	} */ *uap;
82 	register_t *retval;
83 {
84 	struct file *fp;
85 	struct filedesc *fdp;
86 	u_long com;
87 	struct audio_info tmpinfo;
88 	struct audio_offset tmpoffs;
89 	struct oss_audio_buf_info bufinfo;
90 	struct oss_count_info cntinfo;
91 	struct audio_encoding tmpenc;
92 	u_int u;
93 	int idat, idata;
94 	int error = 0;
95 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *));
96 
97 	fdp = p->p_fd;
98 	if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
99 		return (EBADF);
100 
101 	FILE_USE(fp);
102 
103 	if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
104 		error = EBADF;
105 		goto out;
106 	}
107 
108 	com = SCARG(uap, com);
109 	DPRINTF(("oss_ioctl_audio: com=%08lx\n", com));
110 
111 	retval[0] = 0;
112 
113 	ioctlf = fp->f_ops->fo_ioctl;
114 	switch (com) {
115 	case OSS_SNDCTL_DSP_RESET:
116 		error = ioctlf(fp, AUDIO_FLUSH, (caddr_t)0, p);
117 		if (error)
118 			goto out;
119 		break;
120 	case OSS_SNDCTL_DSP_SYNC:
121 	case OSS_SNDCTL_DSP_POST:
122 		error = ioctlf(fp, AUDIO_DRAIN, (caddr_t)0, p);
123 		if (error)
124 			goto out;
125 		break;
126 	case OSS_SNDCTL_DSP_SPEED:
127 		AUDIO_INITINFO(&tmpinfo);
128 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
129 		if (error)
130 			goto out;
131 		tmpinfo.play.sample_rate =
132 		tmpinfo.record.sample_rate = idat;
133 		error = ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
134 		DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_SPEED %d = %d\n",
135 			 idat, error));
136 		if (error)
137 			goto out;
138 		/* fall into ... */
139 	case OSS_SOUND_PCM_READ_RATE:
140 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
141 		if (error)
142 			goto out;
143 		idat = tmpinfo.play.sample_rate;
144 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
145 		if (error)
146 			goto out;
147 		break;
148 	case OSS_SNDCTL_DSP_STEREO:
149 		AUDIO_INITINFO(&tmpinfo);
150 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
151 		if (error)
152 			goto out;
153 		tmpinfo.play.channels =
154 		tmpinfo.record.channels = idat ? 2 : 1;
155 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
156 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
157 		if (error)
158 			goto out;
159 		idat = tmpinfo.play.channels - 1;
160 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
161 		if (error)
162 			goto out;
163 		break;
164 	case OSS_SNDCTL_DSP_GETBLKSIZE:
165 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
166 		if (error)
167 			goto out;
168 		setblocksize(fp, &tmpinfo, p);
169 		idat = tmpinfo.blocksize;
170 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
171 		if (error)
172 			goto out;
173 		break;
174 	case OSS_SNDCTL_DSP_SETFMT:
175 		AUDIO_INITINFO(&tmpinfo);
176 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
177 		if (error)
178 			goto out;
179 		switch (idat) {
180 		case OSS_AFMT_MU_LAW:
181 			tmpinfo.play.precision =
182 			tmpinfo.record.precision = 8;
183 			tmpinfo.play.encoding =
184 			tmpinfo.record.encoding = AUDIO_ENCODING_ULAW;
185 			break;
186 		case OSS_AFMT_A_LAW:
187 			tmpinfo.play.precision =
188 			tmpinfo.record.precision = 8;
189 			tmpinfo.play.encoding =
190 			tmpinfo.record.encoding = AUDIO_ENCODING_ALAW;
191 			break;
192 		case OSS_AFMT_U8:
193 			tmpinfo.play.precision =
194 			tmpinfo.record.precision = 8;
195 			tmpinfo.play.encoding =
196 			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR;
197 			break;
198 		case OSS_AFMT_S8:
199 			tmpinfo.play.precision =
200 			tmpinfo.record.precision = 8;
201 			tmpinfo.play.encoding =
202 			tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR;
203 			break;
204 		case OSS_AFMT_S16_LE:
205 			tmpinfo.play.precision =
206 			tmpinfo.record.precision = 16;
207 			tmpinfo.play.encoding =
208 			tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE;
209 			break;
210 		case OSS_AFMT_S16_BE:
211 			tmpinfo.play.precision =
212 			tmpinfo.record.precision = 16;
213 			tmpinfo.play.encoding =
214 			tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE;
215 			break;
216 		case OSS_AFMT_U16_LE:
217 			tmpinfo.play.precision =
218 			tmpinfo.record.precision = 16;
219 			tmpinfo.play.encoding =
220 			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE;
221 			break;
222 		case OSS_AFMT_U16_BE:
223 			tmpinfo.play.precision =
224 			tmpinfo.record.precision = 16;
225 			tmpinfo.play.encoding =
226 			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE;
227 			break;
228 		default:
229 			error = EINVAL;
230 			goto out;
231 		}
232 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
233 		/* fall into ... */
234 	case OSS_SOUND_PCM_READ_BITS:
235 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
236 		if (error)
237 			goto out;
238 		switch (tmpinfo.play.encoding) {
239 		case AUDIO_ENCODING_ULAW:
240 			idat = OSS_AFMT_MU_LAW;
241 			break;
242 		case AUDIO_ENCODING_ALAW:
243 			idat = OSS_AFMT_A_LAW;
244 			break;
245 		case AUDIO_ENCODING_SLINEAR_LE:
246 			if (tmpinfo.play.precision == 16)
247 				idat = OSS_AFMT_S16_LE;
248 			else
249 				idat = OSS_AFMT_S8;
250 			break;
251 		case AUDIO_ENCODING_SLINEAR_BE:
252 			if (tmpinfo.play.precision == 16)
253 				idat = OSS_AFMT_S16_BE;
254 			else
255 				idat = OSS_AFMT_S8;
256 			break;
257 		case AUDIO_ENCODING_ULINEAR_LE:
258 			if (tmpinfo.play.precision == 16)
259 				idat = OSS_AFMT_U16_LE;
260 			else
261 				idat = OSS_AFMT_U8;
262 			break;
263 		case AUDIO_ENCODING_ULINEAR_BE:
264 			if (tmpinfo.play.precision == 16)
265 				idat = OSS_AFMT_U16_BE;
266 			else
267 				idat = OSS_AFMT_U8;
268 			break;
269 		case AUDIO_ENCODING_ADPCM:
270 			idat = OSS_AFMT_IMA_ADPCM;
271 			break;
272 		}
273 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
274 		if (error)
275 			goto out;
276 		break;
277 	case OSS_SNDCTL_DSP_CHANNELS:
278 		AUDIO_INITINFO(&tmpinfo);
279 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
280 		if (error)
281 			goto out;
282 		tmpinfo.play.channels =
283 		tmpinfo.record.channels = idat;
284 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
285 		/* fall into ... */
286 	case OSS_SOUND_PCM_READ_CHANNELS:
287 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
288 		if (error)
289 			goto out;
290 		idat = tmpinfo.play.channels;
291 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
292 		if (error)
293 			goto out;
294 		break;
295 	case OSS_SOUND_PCM_WRITE_FILTER:
296 	case OSS_SOUND_PCM_READ_FILTER:
297 		error = EINVAL; /* XXX unimplemented */
298 		goto out;
299 	case OSS_SNDCTL_DSP_SUBDIVIDE:
300 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
301 		if (error)
302 			goto out;
303 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
304 		setblocksize(fp, &tmpinfo, p);
305 		if (error)
306 			goto out;
307 		if (idat == 0)
308 			idat = tmpinfo.play.buffer_size / tmpinfo.blocksize;
309 		idat = (tmpinfo.play.buffer_size / idat) & -4;
310 		AUDIO_INITINFO(&tmpinfo);
311 		tmpinfo.blocksize = idat;
312 		error = ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
313 		if (error)
314 			goto out;
315 		idat = tmpinfo.play.buffer_size / tmpinfo.blocksize;
316 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
317 		if (error)
318 			goto out;
319 		break;
320 	case OSS_SNDCTL_DSP_SETFRAGMENT:
321 		AUDIO_INITINFO(&tmpinfo);
322 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
323 		if (error)
324 			goto out;
325 		if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) {
326 			error = EINVAL;
327 			goto out;
328 		}
329 		tmpinfo.blocksize = 1 << (idat & 0xffff);
330 		tmpinfo.hiwat = (idat >> 16) & 0x7fff;
331 		DPRINTF(("oss_audio: SETFRAGMENT blksize=%d, hiwat=%d\n",
332 			 tmpinfo.blocksize, tmpinfo.hiwat));
333 		if (tmpinfo.hiwat == 0)	/* 0 means set to max */
334 			tmpinfo.hiwat = 65536;
335 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
336 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
337 		if (error)
338 			goto out;
339 		u = tmpinfo.blocksize;
340 		for(idat = 0; u > 1; idat++, u >>= 1)
341 			;
342 		idat |= (tmpinfo.hiwat & 0x7fff) << 16;
343 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
344 		if (error)
345 			goto out;
346 		break;
347 	case OSS_SNDCTL_DSP_GETFMTS:
348 		for(idat = 0, tmpenc.index = 0;
349 		    ioctlf(fp, AUDIO_GETENC, (caddr_t)&tmpenc, p) == 0;
350 		    tmpenc.index++) {
351 			switch(tmpenc.encoding) {
352 			case AUDIO_ENCODING_ULAW:
353 				idat |= OSS_AFMT_MU_LAW;
354 				break;
355 			case AUDIO_ENCODING_ALAW:
356 				idat |= OSS_AFMT_A_LAW;
357 				break;
358 			case AUDIO_ENCODING_SLINEAR:
359 				idat |= OSS_AFMT_S8;
360 				break;
361 			case AUDIO_ENCODING_SLINEAR_LE:
362 				if (tmpenc.precision == 16)
363 					idat |= OSS_AFMT_S16_LE;
364 				else
365 					idat |= OSS_AFMT_S8;
366 				break;
367 			case AUDIO_ENCODING_SLINEAR_BE:
368 				if (tmpenc.precision == 16)
369 					idat |= OSS_AFMT_S16_BE;
370 				else
371 					idat |= OSS_AFMT_S8;
372 				break;
373 			case AUDIO_ENCODING_ULINEAR:
374 				idat |= OSS_AFMT_U8;
375 				break;
376 			case AUDIO_ENCODING_ULINEAR_LE:
377 				if (tmpenc.precision == 16)
378 					idat |= OSS_AFMT_U16_LE;
379 				else
380 					idat |= OSS_AFMT_U8;
381 				break;
382 			case AUDIO_ENCODING_ULINEAR_BE:
383 				if (tmpenc.precision == 16)
384 					idat |= OSS_AFMT_U16_BE;
385 				else
386 					idat |= OSS_AFMT_U8;
387 				break;
388 			case AUDIO_ENCODING_ADPCM:
389 				idat |= OSS_AFMT_IMA_ADPCM;
390 				break;
391 			default:
392 				break;
393 			}
394 		}
395 		DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETFMTS = %x\n", idat));
396 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
397 		if (error)
398 			goto out;
399 		break;
400 	case OSS_SNDCTL_DSP_GETOSPACE:
401 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
402 		if (error)
403 			goto out;
404 		setblocksize(fp, &tmpinfo, p);
405 		bufinfo.fragsize = tmpinfo.blocksize;
406 		bufinfo.fragments = tmpinfo.hiwat -
407 		    (tmpinfo.play.seek + tmpinfo.blocksize - 1) /
408 		    tmpinfo.blocksize;
409 		bufinfo.fragstotal = tmpinfo.hiwat;
410 		bufinfo.bytes =
411 		    tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.play.seek;
412 		error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo);
413 		if (error)
414 			goto out;
415 		break;
416 	case OSS_SNDCTL_DSP_GETISPACE:
417 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
418 		if (error)
419 			goto out;
420 		setblocksize(fp, &tmpinfo, p);
421 		bufinfo.fragsize = tmpinfo.blocksize;
422 		bufinfo.fragments = tmpinfo.hiwat -
423 		    (tmpinfo.record.seek + tmpinfo.blocksize - 1) /
424 		    tmpinfo.blocksize;
425                 bufinfo.fragstotal = tmpinfo.hiwat;
426 		bufinfo.bytes =
427 		    tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.record.seek;
428 		DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETxSPACE = %d %d %d %d\n",
429 			 bufinfo.fragsize, bufinfo.fragments,
430 			 bufinfo.fragstotal, bufinfo.bytes));
431 		error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo);
432 		if (error)
433 			goto out;
434 		break;
435 	case OSS_SNDCTL_DSP_NONBLOCK:
436 		idat = 1;
437 		error = ioctlf(fp, FIONBIO, (caddr_t)&idat, p);
438 		if (error)
439 			goto out;
440 		break;
441 	case OSS_SNDCTL_DSP_GETCAPS:
442 		error = ioctlf(fp, AUDIO_GETPROPS, (caddr_t)&idata, p);
443 		if (error)
444 			goto out;
445 		idat = OSS_DSP_CAP_TRIGGER; /* pretend we have trigger */
446 		if (idata & AUDIO_PROP_FULLDUPLEX)
447 			idat |= OSS_DSP_CAP_DUPLEX;
448 		if (idata & AUDIO_PROP_MMAP)
449 			idat |= OSS_DSP_CAP_MMAP;
450 		DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETCAPS = %x\n", idat));
451 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
452 		if (error)
453 			goto out;
454 		break;
455 #if 0
456 	case OSS_SNDCTL_DSP_GETTRIGGER:
457 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
458 		if (error)
459 			goto out;
460 		idat = (tmpinfo.play.pause ? 0 : OSS_PCM_ENABLE_OUTPUT) |
461 		       (tmpinfo.record.pause ? 0 : OSS_PCM_ENABLE_INPUT);
462 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
463 		if (error)
464 			goto out;
465 		break;
466 	case OSS_SNDCTL_DSP_SETTRIGGER:
467 		AUDIO_INITINFO(&tmpinfo);
468 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
469 		if (error)
470 			goto out;
471 		tmpinfo.play.pause = (idat & OSS_PCM_ENABLE_OUTPUT) == 0;
472 		tmpinfo.record.pause = (idat & OSS_PCM_ENABLE_INPUT) == 0;
473 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
474 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
475 		if (error)
476 			goto out;
477 		break;
478 #else
479 	case OSS_SNDCTL_DSP_GETTRIGGER:
480 	case OSS_SNDCTL_DSP_SETTRIGGER:
481 		/* XXX Do nothing for now. */
482 		idat = OSS_PCM_ENABLE_OUTPUT;
483 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
484 		goto out;
485 #endif
486 	case OSS_SNDCTL_DSP_GETIPTR:
487 		error = ioctlf(fp, AUDIO_GETIOFFS, (caddr_t)&tmpoffs, p);
488 		if (error)
489 			goto out;
490 		cntinfo.bytes = tmpoffs.samples;
491 		cntinfo.blocks = tmpoffs.deltablks;
492 		cntinfo.ptr = tmpoffs.offset;
493 		error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo);
494 		if (error)
495 			goto out;
496 		break;
497 	case OSS_SNDCTL_DSP_GETOPTR:
498 		error = ioctlf(fp, AUDIO_GETOOFFS, (caddr_t)&tmpoffs, p);
499 		if (error)
500 			goto out;
501 		cntinfo.bytes = tmpoffs.samples;
502 		cntinfo.blocks = tmpoffs.deltablks;
503 		cntinfo.ptr = tmpoffs.offset;
504 		error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo);
505 		if (error)
506 			goto out;
507 		break;
508 	case OSS_SNDCTL_DSP_MAPINBUF:
509 	case OSS_SNDCTL_DSP_MAPOUTBUF:
510 	case OSS_SNDCTL_DSP_SETSYNCRO:
511 	case OSS_SNDCTL_DSP_SETDUPLEX:
512 	case OSS_SNDCTL_DSP_PROFILE:
513 		error = EINVAL;
514 		goto out;
515 	default:
516 		error = EINVAL;
517 		goto out;
518 	}
519 
520  out:
521 	FILE_UNUSE(fp, p);
522 	return error;
523 }
524 
525 /* If the NetBSD mixer device should have more than 32 devices
526  * some will not be available to Linux */
527 #define NETBSD_MAXDEVS 64
528 struct audiodevinfo {
529 	int done;
530 	dev_t dev;
531 	int16_t devmap[OSS_SOUND_MIXER_NRDEVICES],
532 	        rdevmap[NETBSD_MAXDEVS];
533 	char names[NETBSD_MAXDEVS][MAX_AUDIO_DEV_LEN];
534 	int enum2opaque[NETBSD_MAXDEVS];
535         u_long devmask, recmask, stereomask;
536 	u_long caps, source;
537 };
538 
539 static int
540 opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq)
541 {
542 	int i, o;
543 
544 	for (i = 0; i < NETBSD_MAXDEVS; i++) {
545 		o = di->enum2opaque[i];
546 		if (o == opq)
547 			break;
548 		if (o == -1 && label != NULL &&
549 		    !strncmp(di->names[i], label->name, sizeof di->names[i])) {
550 			di->enum2opaque[i] = opq;
551 			break;
552 		}
553 	}
554 	if (i >= NETBSD_MAXDEVS)
555 		i = -1;
556 	/*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/
557 	return (i);
558 }
559 
560 static int
561 enum_to_ord(struct audiodevinfo *di, int enm)
562 {
563 	if (enm >= NETBSD_MAXDEVS)
564 		return (-1);
565 
566 	/*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/
567 	return (di->enum2opaque[enm]);
568 }
569 
570 static int
571 enum_to_mask(struct audiodevinfo *di, int enm)
572 {
573 	int m;
574 	if (enm >= NETBSD_MAXDEVS)
575 		return (0);
576 
577 	m = di->enum2opaque[enm];
578 	if (m == -1)
579 		m = 0;
580 	/*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/
581 	return (m);
582 }
583 
584 /*
585  * Collect the audio device information to allow faster
586  * emulation of the Linux mixer ioctls.  Cache the information
587  * to eliminate the overhead of repeating all the ioctls needed
588  * to collect the information.
589  */
590 static struct audiodevinfo *
591 getdevinfo(fp, p)
592 	struct file *fp;
593 	struct proc *p;
594 {
595 	mixer_devinfo_t mi;
596 	int i, j, e;
597 	static const struct {
598 		const char *name;
599 		int code;
600 	} *dp, devs[] = {
601 		{ AudioNmicrophone,	OSS_SOUND_MIXER_MIC },
602 		{ AudioNline,		OSS_SOUND_MIXER_LINE },
603 		{ AudioNcd,		OSS_SOUND_MIXER_CD },
604 		{ AudioNdac,		OSS_SOUND_MIXER_PCM },
605 		{ AudioNaux,		OSS_SOUND_MIXER_LINE1 },
606 		{ AudioNrecord,		OSS_SOUND_MIXER_IMIX },
607 		{ AudioNmaster,		OSS_SOUND_MIXER_VOLUME },
608 		{ AudioNtreble,		OSS_SOUND_MIXER_TREBLE },
609 		{ AudioNbass,		OSS_SOUND_MIXER_BASS },
610 		{ AudioNspeaker,	OSS_SOUND_MIXER_SPEAKER },
611 /*		{ AudioNheadphone,	?? },*/
612 		{ AudioNoutput,		OSS_SOUND_MIXER_OGAIN },
613 		{ AudioNinput,		OSS_SOUND_MIXER_IGAIN },
614 /*		{ AudioNmaster,		OSS_SOUND_MIXER_SPEAKER },*/
615 /*		{ AudioNstereo,		?? },*/
616 /*		{ AudioNmono,		?? },*/
617 		{ AudioNfmsynth,	OSS_SOUND_MIXER_SYNTH },
618 /*		{ AudioNwave,		OSS_SOUND_MIXER_PCM },*/
619 		{ AudioNmidi,		OSS_SOUND_MIXER_SYNTH },
620 /*		{ AudioNmixerout,	?? },*/
621 		{ 0, -1 }
622 	};
623 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)) =
624 	    fp->f_ops->fo_ioctl;
625 	struct vnode *vp;
626 	struct vattr va;
627 	static struct audiodevinfo devcache = { 0 };
628 	struct audiodevinfo *di = &devcache;
629 
630 	/*
631 	 * Figure out what device it is so we can check if the
632 	 * cached data is valid.
633 	 */
634 	vp = (struct vnode *)fp->f_data;
635 	if (vp->v_type != VCHR)
636 		return 0;
637 	if (VOP_GETATTR(vp, &va, p->p_ucred, p))
638 		return 0;
639 	if (di->done && di->dev == va.va_rdev)
640 		return di;
641 
642 	di->done = 1;
643 	di->dev = va.va_rdev;
644 	di->devmask = 0;
645 	di->recmask = 0;
646 	di->stereomask = 0;
647 	di->source = ~0;
648 	di->caps = 0;
649 	for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
650 		di->devmap[i] = -1;
651 	for(i = 0; i < NETBSD_MAXDEVS; i++) {
652 		di->rdevmap[i] = -1;
653 		di->names[i][0] = '\0';
654 		di->enum2opaque[i] = -1;
655 	}
656 	for(i = 0; i < NETBSD_MAXDEVS; i++) {
657 		mi.index = i;
658 		if (ioctlf(fp, AUDIO_MIXER_DEVINFO, (caddr_t)&mi, p) < 0)
659 			break;
660 		switch(mi.type) {
661 		case AUDIO_MIXER_VALUE:
662 			for(dp = devs; dp->name; dp++)
663 		    		if (strcmp(dp->name, mi.label.name) == 0)
664 					break;
665 			if (dp->code >= 0) {
666 				di->devmap[dp->code] = i;
667 				di->rdevmap[i] = dp->code;
668 				di->devmask |= 1 << dp->code;
669 				if (mi.un.v.num_channels == 2)
670 					di->stereomask |= 1 << dp->code;
671 				strncpy(di->names[i], mi.label.name,
672 					sizeof di->names[i]);
673 			}
674 			break;
675 		}
676 	}
677 	for(i = 0; i < NETBSD_MAXDEVS; i++) {
678 		mi.index = i;
679 		if (ioctlf(fp, AUDIO_MIXER_DEVINFO, (caddr_t)&mi, p) < 0)
680 			break;
681 		if (strcmp(mi.label.name, AudioNsource) != 0)
682 			continue;
683 		di->source = i;
684 		switch(mi.type) {
685 		case AUDIO_MIXER_ENUM:
686 			for(j = 0; j < mi.un.e.num_mem; j++) {
687 				e = opaque_to_enum(di,
688 						   &mi.un.e.member[j].label,
689 						   mi.un.e.member[j].ord);
690 				if (e >= 0)
691 					di->recmask |= 1 << di->rdevmap[e];
692 			}
693 			di->caps = OSS_SOUND_CAP_EXCL_INPUT;
694 			break;
695 		case AUDIO_MIXER_SET:
696 			for(j = 0; j < mi.un.s.num_mem; j++) {
697 				e = opaque_to_enum(di,
698 						   &mi.un.s.member[j].label,
699 						   mi.un.s.member[j].mask);
700 				if (e >= 0)
701 					di->recmask |= 1 << di->rdevmap[e];
702 			}
703 			break;
704 		}
705 	}
706 	return di;
707 }
708 
709 int
710 oss_ioctl_mixer(p, uap, retval)
711 	struct proc *p;
712 	struct oss_sys_ioctl_args /* {
713 		syscallarg(int) fd;
714 		syscallarg(u_long) com;
715 		syscallarg(caddr_t) data;
716 	} */ *uap;
717 	register_t *retval;
718 {
719 	struct file *fp;
720 	struct filedesc *fdp;
721 	u_long com;
722 	struct audiodevinfo *di;
723 	mixer_ctrl_t mc;
724 	struct oss_mixer_info omi;
725 	struct audio_device adev;
726 	int idat;
727 	int i;
728 	int error;
729 	int l, r, n, e;
730 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *));
731 
732 	fdp = p->p_fd;
733 	if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
734 		return (EBADF);
735 
736 	FILE_USE(fp);
737 
738 	if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
739 		error = EBADF;
740 		goto out;
741 	}
742 
743 	com = SCARG(uap, com);
744 	DPRINTF(("oss_ioctl_mixer: com=%08lx\n", com));
745 
746 	retval[0] = 0;
747 
748 	di = getdevinfo(fp, p);
749 	if (di == 0) {
750 		error = EINVAL;
751 		goto out;
752 	}
753 
754 	ioctlf = fp->f_ops->fo_ioctl;
755 	switch (com) {
756 	case OSS_GET_VERSION:
757 		idat = OSS_SOUND_VERSION;
758 		break;
759 	case OSS_SOUND_MIXER_INFO:
760 	case OSS_SOUND_OLD_MIXER_INFO:
761 		error = ioctlf(fp, AUDIO_GETDEV, (caddr_t)&adev, p);
762 		if (error)
763 			return (error);
764 		omi.modify_counter = 1;
765 		strncpy(omi.id, adev.name, sizeof omi.id);
766 		strncpy(omi.name, adev.name, sizeof omi.name);
767 		return copyout(&omi, SCARG(uap, data), OSS_IOCTL_SIZE(com));
768 	case OSS_SOUND_MIXER_READ_RECSRC:
769 		if (di->source == -1) {
770 			error = EINVAL;
771 			goto out;
772 		}
773 		mc.dev = di->source;
774 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
775 			mc.type = AUDIO_MIXER_ENUM;
776 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
777 			if (error)
778 				goto out;
779 			e = opaque_to_enum(di, NULL, mc.un.ord);
780 			if (e >= 0)
781 				idat = 1 << di->rdevmap[e];
782 		} else {
783 			mc.type = AUDIO_MIXER_SET;
784 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
785 			if (error)
786 				goto out;
787 			e = opaque_to_enum(di, NULL, mc.un.mask);
788 			if (e >= 0)
789 				idat = 1 << di->rdevmap[e];
790 		}
791 		break;
792 	case OSS_SOUND_MIXER_READ_DEVMASK:
793 		idat = di->devmask;
794 		break;
795 	case OSS_SOUND_MIXER_READ_RECMASK:
796 		idat = di->recmask;
797 		break;
798 	case OSS_SOUND_MIXER_READ_STEREODEVS:
799 		idat = di->stereomask;
800 		break;
801 	case OSS_SOUND_MIXER_READ_CAPS:
802 		idat = di->caps;
803 		break;
804 	case OSS_SOUND_MIXER_WRITE_RECSRC:
805 	case OSS_SOUND_MIXER_WRITE_R_RECSRC:
806 		if (di->source == -1) {
807 			error = EINVAL;
808 			goto out;
809 		}
810 		mc.dev = di->source;
811 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
812 		if (error)
813 			goto out;
814 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
815 			mc.type = AUDIO_MIXER_ENUM;
816 			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
817 				if (idat & (1 << i))
818 					break;
819 			if (i >= OSS_SOUND_MIXER_NRDEVICES ||
820 			    di->devmap[i] == -1)
821 				return EINVAL;
822 			mc.un.ord = enum_to_ord(di, di->devmap[i]);
823 		} else {
824 			mc.type = AUDIO_MIXER_SET;
825 			mc.un.mask = 0;
826 			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) {
827 				if (idat & (1 << i)) {
828 					if (di->devmap[i] == -1)
829 						return EINVAL;
830 					mc.un.mask |= enum_to_mask(di, di->devmap[i]);
831 				}
832 			}
833 		}
834 		error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p);
835 		goto out;
836 	default:
837 		if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com &&
838 		    com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) {
839 			n = OSS_GET_DEV(com);
840 			if (di->devmap[n] == -1) {
841 				error = EINVAL;
842 				goto out;
843 			}
844 		    doread:
845 			mc.dev = di->devmap[n];
846 			mc.type = AUDIO_MIXER_VALUE;
847 			mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1;
848 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
849 			if (error)
850 				goto out;
851 			if (mc.un.value.num_channels != 2) {
852 				l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO];
853 			} else {
854 				l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT];
855 				r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
856 			}
857 			idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8);
858 			DPRINTF(("OSS_MIXER_READ  n=%d (dev=%d) l=%d, r=%d, idat=%04x\n",
859 				 n, di->devmap[n], l, r, idat));
860 			break;
861 		} else if ((OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_FIRST) <= com &&
862 			   com < OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_NRDEVICES)) ||
863 			   (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com &&
864 			   com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES))) {
865 			n = OSS_GET_DEV(com);
866 			if (di->devmap[n] == -1) {
867 				error = EINVAL;
868 				goto out;
869 			}
870 			error = copyin(SCARG(uap, data), &idat, sizeof idat);
871 			if (error)
872 				goto out;
873 			l = FROM_OSSVOL( idat       & 0xff);
874 			r = FROM_OSSVOL((idat >> 8) & 0xff);
875 			mc.dev = di->devmap[n];
876 			mc.type = AUDIO_MIXER_VALUE;
877 			if (di->stereomask & (1<<n)) {
878 				mc.un.value.num_channels = 2;
879 				mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
880 				mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
881 			} else {
882 				mc.un.value.num_channels = 1;
883 				mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2;
884 			}
885 			DPRINTF(("OSS_MIXER_WRITE n=%d (dev=%d) l=%d, r=%d, idat=%04x\n",
886 				 n, di->devmap[n], l, r, idat));
887 			error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p);
888 			if (error)
889 				goto out;
890 			if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com &&
891 			   com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) {
892 				error = 0;
893 				goto out;
894 			}
895 			goto doread;
896 		} else {
897 #ifdef AUDIO_DEBUG
898 			printf("oss_audio: unknown mixer ioctl %04lx\n", com);
899 #endif
900 			error = EINVAL;
901 			goto out;
902 		}
903 	}
904 	error = copyout(&idat, SCARG(uap, data), sizeof idat);
905  out:
906 	FILE_UNUSE(fp, p);
907 	return error;
908 }
909 
910 /* Sequencer emulation */
911 int
912 oss_ioctl_sequencer(p, uap, retval)
913 	struct proc *p;
914 	struct oss_sys_ioctl_args /* {
915 		syscallarg(int) fd;
916 		syscallarg(u_long) com;
917 		syscallarg(caddr_t) data;
918 	} */ *uap;
919 	register_t *retval;
920 {
921 	struct file *fp;
922 	struct filedesc *fdp;
923 	u_long com;
924 	int idat, idat1;
925 	struct synth_info si;
926 	struct oss_synth_info osi;
927 	struct oss_seq_event_rec oser;
928 	int error;
929 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *));
930 
931 	fdp = p->p_fd;
932 	if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
933 		return (EBADF);
934 
935 	FILE_USE(fp);
936 
937 	if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
938 		error = EBADF;
939 		goto out;
940 	}
941 
942 	com = SCARG(uap, com);
943 	DPRINTF(("oss_ioctl_sequencer: com=%08lx\n", com));
944 
945 	retval[0] = 0;
946 
947 	ioctlf = fp->f_ops->fo_ioctl;
948 	switch (com) {
949 	case OSS_SEQ_RESET:
950 		error = ioctlf(fp, SEQUENCER_RESET, (caddr_t)&idat, p);
951 		goto out;
952 	case OSS_SEQ_SYNC:
953 		error = ioctlf(fp, SEQUENCER_SYNC, (caddr_t)&idat, p);
954 		goto out;
955 	case OSS_SYNTH_INFO:
956 		error = copyin(SCARG(uap, data), &osi, sizeof osi);
957 		if (error)
958 			goto out;
959 		si.device = osi.device;
960 		error = ioctlf(fp, SEQUENCER_INFO, (caddr_t)&si, p);
961 		if (error)
962 			goto out;
963 		strncpy(osi.name, si.name, sizeof osi.name);
964 		osi.device = si.device;
965 		switch(si.synth_type) {
966 		case SYNTH_TYPE_FM:
967 			osi.synth_type = OSS_SYNTH_TYPE_FM; break;
968 		case SYNTH_TYPE_SAMPLE:
969 			osi.synth_type = OSS_SYNTH_TYPE_SAMPLE; break;
970 		case SYNTH_TYPE_MIDI:
971 			osi.synth_type = OSS_SYNTH_TYPE_MIDI; break;
972 		default:
973 			osi.synth_type = 0; break;
974 		}
975 		switch(si.synth_subtype) {
976 		case SYNTH_SUB_FM_TYPE_ADLIB:
977 			osi.synth_subtype = OSS_FM_TYPE_ADLIB; break;
978 		case SYNTH_SUB_FM_TYPE_OPL3:
979 			osi.synth_subtype = OSS_FM_TYPE_OPL3; break;
980 		case SYNTH_SUB_MIDI_TYPE_MPU401:
981 			osi.synth_subtype = OSS_MIDI_TYPE_MPU401; break;
982 		case SYNTH_SUB_SAMPLE_TYPE_BASIC:
983 			osi.synth_subtype = OSS_SAMPLE_TYPE_BASIC; break;
984 		default:
985 			osi.synth_subtype = 0; break;
986 		}
987 		osi.perc_mode = 0;
988 		osi.nr_voices = si.nr_voices;
989 		osi.nr_drums = 0;
990 		osi.instr_bank_size = si.instr_bank_size;
991 		osi.capabilities = 0;
992 		if (si.capabilities & SYNTH_CAP_OPL3)
993 			osi.capabilities |= OSS_SYNTH_CAP_OPL3;
994 		if (si.capabilities & SYNTH_CAP_INPUT)
995 			osi.capabilities |= OSS_SYNTH_CAP_INPUT;
996 		error = copyout(&osi, SCARG(uap, data), sizeof osi);
997 		goto out;
998 	case OSS_SEQ_CTRLRATE:
999 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
1000 		if (error)
1001 			goto out;
1002 		error = ioctlf(fp, SEQUENCER_CTRLRATE, (caddr_t)&idat, p);
1003 		if (error)
1004 			goto out;
1005 		retval[0] = idat;
1006 		break;
1007 	case OSS_SEQ_GETOUTCOUNT:
1008 		error = ioctlf(fp, SEQUENCER_GETOUTCOUNT, (caddr_t)&idat, p);
1009 		if (error)
1010 			goto out;
1011 		retval[0] = idat;
1012 		break;
1013 	case OSS_SEQ_GETINCOUNT:
1014 		error = ioctlf(fp, SEQUENCER_GETINCOUNT, (caddr_t)&idat, p);
1015 		if (error)
1016 			goto out;
1017 		retval[0] = idat;
1018 		break;
1019 	case OSS_SEQ_NRSYNTHS:
1020 		error = ioctlf(fp, SEQUENCER_NRSYNTHS, (caddr_t)&idat, p);
1021 		if (error)
1022 			goto out;
1023 		retval[0] = idat;
1024 		break;
1025 	case OSS_SEQ_NRMIDIS:
1026 		error = ioctlf(fp, SEQUENCER_NRMIDIS, (caddr_t)&idat, p);
1027 		if (error)
1028 			goto out;
1029 		retval[0] = idat;
1030 		break;
1031 	case OSS_SEQ_THRESHOLD:
1032 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
1033 		if (error)
1034 			goto out;
1035 		error = ioctlf(fp, SEQUENCER_THRESHOLD, (caddr_t)&idat, p);
1036 		goto out;
1037 	case OSS_MEMAVL:
1038 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
1039 		if (error)
1040 			goto out;
1041 		error = ioctlf(fp, SEQUENCER_MEMAVL, (caddr_t)&idat, p);
1042 		if (error)
1043 			goto out;
1044 		retval[0] = idat;
1045 		break;
1046 	case OSS_SEQ_PANIC:
1047 		error = ioctlf(fp, SEQUENCER_PANIC, (caddr_t)&idat, p);
1048 		goto out;
1049 	case OSS_SEQ_OUTOFBAND:
1050 		error = copyin(SCARG(uap, data), &oser, sizeof oser);
1051 		if (error)
1052 			goto out;
1053 		error = ioctlf(fp, SEQUENCER_OUTOFBAND, (caddr_t)&oser, p);
1054 		if (error)
1055 			goto out;
1056 		break;
1057 	case OSS_SEQ_GETTIME:
1058 		error = ioctlf(fp, SEQUENCER_GETTIME, (caddr_t)&idat, p);
1059 		if (error)
1060 			goto out;
1061 		retval[0] = idat;
1062 		break;
1063 	case OSS_TMR_TIMEBASE:
1064 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
1065 		if (error)
1066 			goto out;
1067 		error = ioctlf(fp, SEQUENCER_TMR_TIMEBASE, (caddr_t)&idat, p);
1068 		if (error)
1069 			goto out;
1070 		retval[0] = idat;
1071 		break;
1072 	case OSS_TMR_START:
1073 		error = ioctlf(fp, SEQUENCER_TMR_START, (caddr_t)&idat, p);
1074 		goto out;
1075 	case OSS_TMR_STOP:
1076 		error = ioctlf(fp, SEQUENCER_TMR_STOP, (caddr_t)&idat, p);
1077 		goto out;
1078 	case OSS_TMR_CONTINUE:
1079 		error = ioctlf(fp, SEQUENCER_TMR_CONTINUE, (caddr_t)&idat, p);
1080 		goto out;
1081 	case OSS_TMR_TEMPO:
1082 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
1083 		if (error)
1084 			goto out;
1085 		error = ioctlf(fp, SEQUENCER_TMR_TEMPO, (caddr_t)&idat, p);
1086 		if (error)
1087 			goto out;
1088 		retval[0] = idat;
1089 		break;
1090 	case OSS_TMR_SOURCE:
1091 		error = copyin(SCARG(uap, data), &idat1, sizeof idat);
1092 		if (error)
1093 			goto out;
1094 		idat = 0;
1095 		if (idat1 & OSS_TMR_INTERNAL) idat |= SEQUENCER_TMR_INTERNAL;
1096 		error = ioctlf(fp, SEQUENCER_TMR_SOURCE, (caddr_t)&idat, p);
1097 		if (error)
1098 			goto out;
1099 		idat1 = idat;
1100 		if (idat1 & SEQUENCER_TMR_INTERNAL) idat |= OSS_TMR_INTERNAL;
1101 		retval[0] = idat;
1102 		break;
1103 	case OSS_TMR_METRONOME:
1104 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
1105 		if (error)
1106 			goto out;
1107 		error = ioctlf(fp, SEQUENCER_TMR_METRONOME, (caddr_t)&idat, p);
1108 		goto out;
1109 	case OSS_TMR_SELECT:
1110 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
1111 		if (error)
1112 			goto out;
1113 		retval[0] = idat;
1114 		error = ioctlf(fp, SEQUENCER_TMR_SELECT, (caddr_t)&idat, p);
1115 		goto out;
1116 	default:
1117 		error = EINVAL;
1118 		goto out;
1119 	}
1120 
1121 	error = copyout(&idat, SCARG(uap, data), sizeof idat);
1122  out:
1123 	FILE_UNUSE(fp, p);
1124 	return error;
1125 }
1126 
1127 /*
1128  * Check that the blocksize is a power of 2 as OSS wants.
1129  * If not, set it to be.
1130  */
1131 static void
1132 setblocksize(fp, info, p)
1133 	struct file *fp;
1134 	struct audio_info *info;
1135 	struct proc *p;
1136 {
1137 	struct audio_info set;
1138 	int s;
1139 
1140 	if (info->blocksize & (info->blocksize-1)) {
1141 		for(s = 32; s < info->blocksize; s <<= 1)
1142 			;
1143 		AUDIO_INITINFO(&set);
1144 		set.blocksize = s;
1145 		fp->f_ops->fo_ioctl(fp, AUDIO_SETINFO, (caddr_t)&set, p);
1146 		fp->f_ops->fo_ioctl(fp, AUDIO_GETINFO, (caddr_t)info, p);
1147 	}
1148 }
1149