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