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