ossaudio.c revision 1.7 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