xref: /netbsd-src/sys/compat/ossaudio/ossaudio.c (revision d0fed6c87ddc40a8bffa6f99e7433ddfc864dd83)
1 /*	$NetBSD: ossaudio.c,v 1.7 1997/04/06 23:49:32 augustss Exp $	*/
2 #include <sys/param.h>
3 #include <sys/proc.h>
4 #include <sys/systm.h>
5 #include <sys/file.h>
6 #include <sys/vnode.h>
7 #include <sys/filedesc.h>
8 #include <sys/ioctl.h>
9 #include <sys/mount.h>
10 #include <sys/audioio.h>
11 
12 #include <sys/syscallargs.h>
13 
14 #include <compat/ossaudio/ossaudio.h>
15 #include <compat/ossaudio/ossaudiovar.h>
16 
17 static struct audiodevinfo *getdevinfo __P((struct file *, struct proc *));
18 
19 int
20 oss_ioctl_audio(p, uap, retval)
21 	register struct proc *p;
22 	register struct oss_sys_ioctl_args /* {
23 		syscallarg(int) fd;
24 		syscallarg(u_long) com;
25 		syscallarg(caddr_t) data;
26 	} */ *uap;
27 	register_t *retval;
28 {
29 	register struct file *fp;
30 	register struct filedesc *fdp;
31 	u_long com;
32 	struct audio_info tmpinfo;
33 	int idat;
34 	int error;
35 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *));
36 
37 	fdp = p->p_fd;
38 	if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles ||
39 	    (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL)
40 		return (EBADF);
41 
42 	if ((fp->f_flag & (FREAD | FWRITE)) == 0)
43 		return (EBADF);
44 
45 	ioctlf = fp->f_ops->fo_ioctl;
46 
47 	com = SCARG(uap, com);
48 	retval[0] = 0;
49 
50 	switch (com) {
51 	case OSS_SNDCTL_DSP_RESET:
52 		error = ioctlf(fp, AUDIO_FLUSH, (caddr_t)0, p);
53 		if (error)
54 			return error;
55 		break;
56 	case OSS_SNDCTL_DSP_SYNC:
57 	case OSS_SNDCTL_DSP_POST:
58 		error = ioctlf(fp, AUDIO_DRAIN, (caddr_t)0, p);
59 		if (error)
60 			return error;
61 		break;
62 	case OSS_SNDCTL_DSP_SPEED:
63 		AUDIO_INITINFO(&tmpinfo);
64 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
65 		if (error)
66 			return error;
67 		tmpinfo.play.sample_rate =
68 		tmpinfo.record.sample_rate = idat;
69 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
70 		/* fall into ... */
71 	case OSS_SOUND_PCM_READ_RATE:
72 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
73 		if (error)
74 			return error;
75 		idat = tmpinfo.play.sample_rate;
76 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
77 		if (error)
78 			return error;
79 		break;
80 	case OSS_SNDCTL_DSP_STEREO:
81 		AUDIO_INITINFO(&tmpinfo);
82 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
83 		if (error)
84 			return error;
85 		tmpinfo.play.channels =
86 		tmpinfo.record.channels = idat ? 2 : 1;
87 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
88 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
89 		if (error)
90 			return error;
91 		idat = tmpinfo.play.channels - 1;
92 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
93 		if (error)
94 			return error;
95 		break;
96 	case OSS_SNDCTL_DSP_GETBLKSIZE:
97 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
98 		if (error)
99 			return error;
100 		idat = tmpinfo.blocksize;
101 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
102 		if (error)
103 			return error;
104 		break;
105 	case OSS_SNDCTL_DSP_SETFMT:
106 		AUDIO_INITINFO(&tmpinfo);
107 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
108 		if (error)
109 			return error;
110 		switch (idat) {
111 		case OSS_AFMT_MU_LAW:
112 			tmpinfo.play.precision =
113 			tmpinfo.record.precision = 8;
114 			tmpinfo.play.encoding =
115 			tmpinfo.record.encoding = AUDIO_ENCODING_ULAW;
116 			break;
117 		case OSS_AFMT_A_LAW:
118 			tmpinfo.play.precision =
119 			tmpinfo.record.precision = 8;
120 			tmpinfo.play.encoding =
121 			tmpinfo.record.encoding = AUDIO_ENCODING_ALAW;
122 			break;
123 		case OSS_AFMT_U8:
124 			tmpinfo.play.precision =
125 			tmpinfo.record.precision = 8;
126 			tmpinfo.play.encoding =
127 			tmpinfo.record.encoding = AUDIO_ENCODING_LINEAR;
128 			break;
129 		case OSS_AFMT_S16_LE:
130 			tmpinfo.play.precision =
131 			tmpinfo.record.precision = 16;
132 			tmpinfo.play.encoding =
133 			tmpinfo.record.encoding = AUDIO_ENCODING_LINEAR;
134 			break;
135 		default:
136 			return EINVAL;
137 		}
138 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
139 		/* fall into ... */
140 	case OSS_SOUND_PCM_READ_BITS:
141 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
142 		if (error)
143 			return error;
144 		switch (tmpinfo.play.encoding) {
145 		case AUDIO_ENCODING_ULAW:
146 			idat = OSS_AFMT_MU_LAW;
147 			break;
148 		case AUDIO_ENCODING_ALAW:
149 			idat = OSS_AFMT_A_LAW;
150 			break;
151 		case AUDIO_ENCODING_PCM16:
152 			idat = OSS_AFMT_S16_LE;
153 			break;
154 		case AUDIO_ENCODING_PCM8:
155 			idat = OSS_AFMT_U8;
156 			break;
157 		case AUDIO_ENCODING_ADPCM:
158 			idat = OSS_AFMT_IMA_ADPCM;
159 			break;
160 		}
161 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
162 		if (error)
163 			return error;
164 		break;
165 	case OSS_SNDCTL_DSP_CHANNELS:
166 		AUDIO_INITINFO(&tmpinfo);
167 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
168 		if (error)
169 			return error;
170 		tmpinfo.play.channels =
171 		tmpinfo.record.channels = idat;
172 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
173 		/* fall into ... */
174 	case OSS_SOUND_PCM_READ_CHANNELS:
175 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
176 		if (error)
177 			return error;
178 		idat = tmpinfo.play.channels;
179 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
180 		if (error)
181 			return error;
182 		break;
183 	case OSS_SOUND_PCM_WRITE_FILTER:
184 	case OSS_SOUND_PCM_READ_FILTER:
185 		return EINVAL; /* XXX unimplemented */
186 	case OSS_SNDCTL_DSP_SUBDIVIDE:
187 		return EINVAL; /* XXX unimplemented */
188 	case OSS_SNDCTL_DSP_SETFRAGMENT:
189 		AUDIO_INITINFO(&tmpinfo);
190 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
191 		if (error)
192 			return error;
193 		if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17)
194 			return EINVAL;
195 		tmpinfo.blocksize = 1 << (idat & 0xffff);
196 		tmpinfo.hiwat = (idat >> 16) & 0xffff;
197 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
198 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
199 		if (error)
200 			return error;
201 		idat = tmpinfo.blocksize;
202 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
203 		if (error)
204 			return error;
205 		break;
206 	case OSS_SNDCTL_DSP_GETFMTS:
207 		idat = OSS_AFMT_MU_LAW | OSS_AFMT_U8 | OSS_AFMT_S16_LE;
208 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
209 		if (error)
210 			return error;
211 		break;
212 	case OSS_SNDCTL_DSP_GETOSPACE:
213 	case OSS_SNDCTL_DSP_GETISPACE:
214 	case OSS_SNDCTL_DSP_NONBLOCK:
215 		return EINVAL; /* XXX unimplemented */
216 	case OSS_SNDCTL_DSP_GETCAPS:
217 		idat = 0; /* XXX full duplex info should be set */
218 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
219 		if (error)
220 			return error;
221 		break;
222 	case OSS_SNDCTL_DSP_GETTRIGGER:
223 	case OSS_SNDCTL_DSP_SETTRIGGER:
224 	case OSS_SNDCTL_DSP_GETIPTR:
225 	case OSS_SNDCTL_DSP_GETOPTR:
226 	case OSS_SNDCTL_DSP_MAPINBUF:
227 	case OSS_SNDCTL_DSP_MAPOUTBUF:
228 	case OSS_SNDCTL_DSP_SETSYNCRO:
229 	case OSS_SNDCTL_DSP_SETDUPLEX:
230 	case OSS_SNDCTL_DSP_PROFILE:
231 		return EINVAL; /* XXX unimplemented */
232 	default:
233 		return EINVAL;
234 	}
235 
236 	return 0;
237 }
238 
239 /* If the NetBSD mixer device should have more than 32 devices
240  * some will not be available to Linux */
241 #define NETBSD_MAXDEVS 32
242 struct audiodevinfo {
243 	int done;
244 	dev_t dev;
245 	int16_t devmap[OSS_SOUND_MIXER_NRDEVICES],
246 	        rdevmap[NETBSD_MAXDEVS];
247         u_long devmask, recmask, stereomask;
248 	u_long caps, source;
249 };
250 
251 /*
252  * Collect the audio device information to allow faster
253  * emulation of the Linux mixer ioctls.  Cache the information
254  * to eliminate the overhead of repeating all the ioctls needed
255  * to collect the information.
256  */
257 static struct audiodevinfo *
258 getdevinfo(fp, p)
259 	struct file *fp;
260 	struct proc *p;
261 {
262 	mixer_devinfo_t mi;
263 	int i;
264 	static struct {
265 		char *name;
266 		int code;
267 	} *dp, devs[] = {
268 		{ AudioNmicrophone,	OSS_SOUND_MIXER_MIC },
269 		{ AudioNline,		OSS_SOUND_MIXER_LINE },
270 		{ AudioNcd,		OSS_SOUND_MIXER_CD },
271 		{ AudioNdac,		OSS_SOUND_MIXER_PCM },
272 		{ AudioNrecord,		OSS_SOUND_MIXER_IMIX },
273 		{ AudioNvolume,		OSS_SOUND_MIXER_VOLUME },
274 		{ AudioNtreble,		OSS_SOUND_MIXER_TREBLE },
275 		{ AudioNbass,		OSS_SOUND_MIXER_BASS },
276 		{ AudioNspeaker,	OSS_SOUND_MIXER_SPEAKER },
277 /*		{ AudioNheadphone,	?? },*/
278 		{ AudioNoutput,		OSS_SOUND_MIXER_OGAIN },
279 		{ AudioNinput,		OSS_SOUND_MIXER_IGAIN },
280 /*		{ AudioNmaster,		OSS_SOUND_MIXER_MASTER },*/
281 /*		{ AudioNstereo,		?? },*/
282 /*		{ AudioNmono,		?? },*/
283 /*		{ AudioNsource,		?? },*/
284 		{ AudioNfmsynth,	OSS_SOUND_MIXER_SYNTH },
285 /*		{ AudioNwave,		OSS_SOUND_MIXER_PCM },*/
286 /*		{ AudioNmidi,		?? },*/
287 /*		{ AudioNmixerout,	?? },*/
288 		{ 0, -1 }
289 	};
290 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)) =
291 	    fp->f_ops->fo_ioctl;
292 	struct vnode *vp;
293 	struct vattr va;
294 	static struct audiodevinfo devcache = { 0 };
295 	struct audiodevinfo *di = &devcache;
296 
297 	/* Figure out what device it is so we can check if the
298 	 * cached data is valid.
299 	 */
300 	vp = (struct vnode *)fp->f_data;
301 	if (vp->v_type != VCHR)
302 		return 0;
303 	if (VOP_GETATTR(vp, &va, p->p_ucred, p))
304 		return 0;
305 	if (di->done && di->dev == va.va_rdev)
306 		return di;
307 
308 	di->done = 1;
309 	di->dev = va.va_rdev;
310 	di->devmask = 0;
311 	di->recmask = 0;
312 	di->stereomask = 0;
313 	di->source = -1;
314 	di->caps = 0;
315 	for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
316 		di->devmap[i] = -1;
317 	for(i = 0; i < NETBSD_MAXDEVS; i++)
318 		di->rdevmap[i] = -1;
319 	for(i = 0; i < NETBSD_MAXDEVS; i++) {
320 		mi.index = i;
321 		if (ioctlf(fp, AUDIO_MIXER_DEVINFO, (caddr_t)&mi, p) < 0)
322 			break;
323 		switch(mi.type) {
324 		case AUDIO_MIXER_VALUE:
325 			for(dp = devs; dp->name; dp++)
326 		    		if (strcmp(dp->name, mi.label.name) == 0)
327 					break;
328 			if (dp->code >= 0) {
329 				di->devmap[dp->code] = i;
330 				di->rdevmap[i] = dp->code;
331 				di->devmask |= 1 << dp->code;
332 				if (mi.un.v.num_channels == 2)
333 					di->stereomask |= 1 << dp->code;
334 			}
335 			break;
336 		case AUDIO_MIXER_ENUM:
337 			if (strcmp(mi.label.name, AudioNsource) == 0) {
338 				int j;
339 				di->source = i;
340 				for(j = 0; j < mi.un.e.num_mem; j++) {
341 					di->recmask |= 1 << di->rdevmap[mi.un.e.member[j].ord];
342 				}
343 				di->caps = OSS_SOUND_CAP_EXCL_INPUT;
344 			}
345 			break;
346 		case AUDIO_MIXER_SET:
347 			if (strcmp(mi.label.name, AudioNsource) == 0) {
348 				int j;
349 				di->source = i;
350 				for(j = 0; j < mi.un.s.num_mem; j++) {
351 					int k, mask = mi.un.s.member[j].mask;
352 					if (mask) {
353 						for(k = 0; !(mask & (1<<k)); k++)
354 							;
355 						di->recmask |= 1 << di->rdevmap[k];
356 					}
357 				}
358 			}
359 			break;
360 		}
361 	}
362 	return di;
363 }
364 
365 int
366 oss_ioctl_mixer(p, uap, retval)
367 	register struct proc *p;
368 	register struct oss_sys_ioctl_args /* {
369 		syscallarg(int) fd;
370 		syscallarg(u_long) com;
371 		syscallarg(caddr_t) data;
372 	} */ *uap;
373 	register_t *retval;
374 {
375 	struct file *fp;
376 	struct filedesc *fdp;
377 	u_long com;
378 	struct audiodevinfo *di;
379 	mixer_ctrl_t mc;
380 	int idat;
381 	int i;
382 	int error;
383 	int l, r, n;
384 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *));
385 
386 	fdp = p->p_fd;
387 	if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles ||
388 	    (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL)
389 		return (EBADF);
390 
391 	if ((fp->f_flag & (FREAD | FWRITE)) == 0)
392 		return (EBADF);
393 
394 	com = SCARG(uap, com);
395 	retval[0] = 0;
396 
397 	di = getdevinfo(fp, p);
398 	if (di == 0)
399 		return EINVAL;
400 
401 	ioctlf = fp->f_ops->fo_ioctl;
402 	switch (com) {
403 	case OSS_SOUND_MIXER_READ_RECSRC:
404 		if (di->source == -1)
405 			return EINVAL;
406 		mc.dev = di->source;
407 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
408 			mc.type = AUDIO_MIXER_ENUM;
409 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
410 			if (error)
411 				return error;
412 			idat = 1 << di->rdevmap[mc.un.ord];
413 		} else {
414 			int k;
415 			unsigned int mask;
416 			mc.type = AUDIO_MIXER_SET;
417 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
418 			if (error)
419 				return error;
420 			idat = 0;
421 			for(mask = mc.un.mask, k = 0; mask; mask >>= 1, k++)
422 				idat |= 1 << di->rdevmap[k];
423 		}
424 		break;
425 	case OSS_SOUND_MIXER_READ_DEVMASK:
426 		idat = di->devmask;
427 		break;
428 	case OSS_SOUND_MIXER_READ_RECMASK:
429 		idat = di->recmask;
430 		break;
431 	case OSS_SOUND_MIXER_READ_STEREODEVS:
432 		idat = di->stereomask;
433 		break;
434 	case OSS_SOUND_MIXER_READ_CAPS:
435 		idat = di->caps;
436 		break;
437 	case OSS_SOUND_MIXER_WRITE_RECSRC:
438 		if (di->source == -1)
439 			return EINVAL;
440 		mc.dev = di->source;
441 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
442 		if (error)
443 			return error;
444 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
445 			mc.type = AUDIO_MIXER_ENUM;
446 			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
447 				if (idat & (1 << i))
448 					break;
449 			if (i >= OSS_SOUND_MIXER_NRDEVICES ||
450 			    di->devmap[i] == -1)
451 				return EINVAL;
452 			mc.un.ord = di->devmap[i];
453 		} else {
454 			mc.type = AUDIO_MIXER_SET;
455 			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
456 				if (idat & (1 << i)) {
457 					if (di->devmap[i] == -1)
458 						return EINVAL;
459 					mc.un.mask |= 1 << di->devmap[i];
460 				}
461 		}
462 		return ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p);
463 	default:
464 		if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com &&
465 		    com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) {
466 			n = OSS_GET_DEV(com);
467 			if (di->devmap[n] == -1)
468 				return EINVAL;
469 			mc.dev = di->devmap[n];
470 			mc.type = AUDIO_MIXER_VALUE;
471 		    doread:
472 			mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1;
473 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
474 			if (error)
475 				return error;
476 			if (mc.type != AUDIO_MIXER_VALUE)
477 				return EINVAL;
478 			if (mc.un.value.num_channels != 2) {
479 				l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO];
480 			} else {
481 				l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT];
482 				r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
483 			}
484 			l = l * 100 / 255;
485 			r = r * 100 / 255;
486 			idat = l | (r << 8);
487 			break;
488 		} else if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com &&
489 			   com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) {
490 			n = OSS_GET_DEV(com);
491 			if (di->devmap[n] == -1)
492 				return EINVAL;
493 			error = copyin(SCARG(uap, data), &idat, sizeof idat);
494 			if (error)
495 				return error;
496 			l = (idat & 0xff) * 255 / 100;
497 			r = (idat >> 8) * 255 / 100;
498 			mc.dev = di->devmap[n];
499 			mc.type = AUDIO_MIXER_VALUE;
500 			if (di->stereomask & (1<<n)) {
501 				mc.un.value.num_channels = 2;
502 				mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
503 				mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
504 			} else {
505 				mc.un.value.num_channels = 1;
506 				mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2;
507 			}
508 			error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p);
509 			if (error)
510 				return error;
511 			goto doread;
512 		} else
513 			return EINVAL;
514 	}
515 	return copyout(&idat, SCARG(uap, data), sizeof idat);
516 }
517 
518 /* XXX hook for sequencer emulation */
519 int
520 oss_ioctl_sequencer(p, uap, retval)
521 	register struct proc *p;
522 	register struct oss_sys_ioctl_args /* {
523 		syscallarg(int) fd;
524 		syscallarg(u_long) com;
525 		syscallarg(caddr_t) data;
526 	} */ *uap;
527 	register_t *retval;
528 {
529 	register struct file *fp;
530 	register struct filedesc *fdp;
531 #if 0
532 	u_long com;
533 	int idat;
534 	int error;
535 #endif
536 
537 	fdp = p->p_fd;
538 	if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles ||
539 	    (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL)
540 		return (EBADF);
541 
542 	if ((fp->f_flag & (FREAD | FWRITE)) == 0)
543 		return (EBADF);
544 
545 #if 0
546 	com = SCARG(uap, com);
547 #endif
548 	retval[0] = 0;
549 
550 	return EINVAL;
551 }
552