1 | /* $NetBSD: ossaudio.c,v 1.69 2014/09/05 09:21:55 matt Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1997, 2008 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 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
17 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
20 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | * POSSIBILITY OF SUCH DAMAGE. |
27 | */ |
28 | |
29 | #include <sys/cdefs.h> |
30 | __KERNEL_RCSID(0, "$NetBSD: ossaudio.c,v 1.69 2014/09/05 09:21:55 matt Exp $" ); |
31 | |
32 | #include <sys/param.h> |
33 | #include <sys/proc.h> |
34 | #include <sys/systm.h> |
35 | #include <sys/file.h> |
36 | #include <sys/vnode.h> |
37 | #include <sys/filedesc.h> |
38 | #include <sys/ioctl.h> |
39 | #include <sys/mount.h> |
40 | #include <sys/kernel.h> |
41 | #include <sys/audioio.h> |
42 | #include <sys/midiio.h> |
43 | #include <sys/kauth.h> |
44 | #include <sys/syscallargs.h> |
45 | #include <sys/module.h> |
46 | |
47 | #include <compat/ossaudio/ossaudio.h> |
48 | #include <compat/ossaudio/ossaudiovar.h> |
49 | |
50 | MODULE(MODULE_CLASS_EXEC, compat_ossaudio, NULL); |
51 | |
52 | #ifdef AUDIO_DEBUG |
53 | #define DPRINTF(x) if (ossdebug) printf x |
54 | int ossdebug = 0; |
55 | #else |
56 | #define DPRINTF(x) |
57 | #endif |
58 | |
59 | #define TO_OSSVOL(x) (((x) * 100 + 127) / 255) |
60 | #define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100) |
61 | |
62 | static struct audiodevinfo *getdevinfo(file_t *); |
63 | static int opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq); |
64 | static int enum_to_ord(struct audiodevinfo *di, int enm); |
65 | static int enum_to_mask(struct audiodevinfo *di, int enm); |
66 | |
67 | static void setblocksize(file_t *, struct audio_info *); |
68 | |
69 | #ifdef AUDIO_DEBUG |
70 | static const char * |
71 | compat_ossaudio_getcmd(u_long cmd) |
72 | { |
73 | static char buf[64]; |
74 | switch (cmd) { |
75 | #define _DO(_a) \ |
76 | case _a: \ |
77 | return # _a; |
78 | _DO(OSS_SNDCTL_DSP_RESET) |
79 | _DO(OSS_SNDCTL_DSP_SYNC) |
80 | _DO(OSS_SNDCTL_DSP_SPEED) |
81 | _DO(OSS_SOUND_PCM_READ_RATE) |
82 | _DO(OSS_SNDCTL_DSP_STEREO) |
83 | _DO(OSS_SNDCTL_DSP_GETBLKSIZE) |
84 | _DO(OSS_SNDCTL_DSP_SETFMT) |
85 | _DO(OSS_SOUND_PCM_READ_BITS) |
86 | _DO(OSS_SNDCTL_DSP_CHANNELS) |
87 | _DO(OSS_SOUND_PCM_READ_CHANNELS) |
88 | _DO(OSS_SOUND_PCM_WRITE_FILTER) |
89 | _DO(OSS_SOUND_PCM_READ_FILTER) |
90 | _DO(OSS_SNDCTL_DSP_POST) |
91 | _DO(OSS_SNDCTL_DSP_SUBDIVIDE) |
92 | _DO(OSS_SNDCTL_DSP_SETFRAGMENT) |
93 | _DO(OSS_SNDCTL_DSP_GETFMTS) |
94 | _DO(OSS_SNDCTL_DSP_GETOSPACE) |
95 | _DO(OSS_SNDCTL_DSP_GETISPACE) |
96 | _DO(OSS_SNDCTL_DSP_NONBLOCK) |
97 | _DO(OSS_SNDCTL_DSP_GETCAPS) |
98 | _DO(OSS_SNDCTL_DSP_GETTRIGGER) |
99 | _DO(OSS_SNDCTL_DSP_SETTRIGGER) |
100 | _DO(OSS_SNDCTL_DSP_GETIPTR) |
101 | _DO(OSS_SNDCTL_DSP_GETOPTR) |
102 | _DO(OSS_SNDCTL_DSP_MAPINBUF) |
103 | _DO(OSS_SNDCTL_DSP_MAPOUTBUF) |
104 | _DO(OSS_SNDCTL_DSP_SETSYNCRO) |
105 | _DO(OSS_SNDCTL_DSP_SETDUPLEX) |
106 | _DO(OSS_SNDCTL_DSP_GETODELAY) |
107 | _DO(OSS_SNDCTL_DSP_PROFILE) |
108 | _DO(OSS_SOUND_MIXER_INFO) |
109 | _DO(OSS_SOUND_OLD_MIXER_INFO) |
110 | _DO(OSS_GET_VERSION) |
111 | _DO(OSS_SEQ_RESET) |
112 | _DO(OSS_SEQ_SYNC) |
113 | _DO(OSS_SYNTH_INFO) |
114 | _DO(OSS_SEQ_CTRLRATE) |
115 | _DO(OSS_SEQ_GETOUTCOUNT) |
116 | _DO(OSS_SEQ_GETINCOUNT) |
117 | _DO(OSS_SEQ_PERCMODE) |
118 | _DO(OSS_SEQ_TESTMIDI) |
119 | _DO(OSS_SEQ_RESETSAMPLES) |
120 | _DO(OSS_SEQ_NRSYNTHS) |
121 | _DO(OSS_SEQ_NRMIDIS) |
122 | #ifdef notyet |
123 | _DO(OSS_MIDI_INFO) |
124 | #endif |
125 | _DO(OSS_SEQ_THRESHOLD) |
126 | _DO(OSS_MEMAVL) |
127 | _DO(OSS_FM_4OP_ENABLE) |
128 | _DO(OSS_SEQ_PANIC) |
129 | _DO(OSS_SEQ_OUTOFBAND) |
130 | _DO(OSS_SEQ_GETTIME) |
131 | _DO(OSS_ID) |
132 | _DO(OSS_CONTROL) |
133 | _DO(OSS_REMOVESAMPLE) |
134 | _DO(OSS_TMR_TIMEBASE) |
135 | _DO(OSS_TMR_START) |
136 | _DO(OSS_TMR_STOP) |
137 | _DO(OSS_TMR_CONTINUE) |
138 | _DO(OSS_TMR_TEMPO) |
139 | _DO(OSS_TMR_SOURCE) |
140 | _DO(OSS_TMR_METRONOME) |
141 | _DO(OSS_TMR_SELECT) |
142 | #undef _DO |
143 | default: |
144 | (void)snprintf(buf, sizeof(buf), "*0x%lx*" , cmd); |
145 | return buf; |
146 | } |
147 | } |
148 | #endif |
149 | |
150 | |
151 | static int |
152 | compat_ossaudio_modcmd(modcmd_t cmd, void *arg) |
153 | { |
154 | |
155 | switch (cmd) { |
156 | case MODULE_CMD_INIT: |
157 | case MODULE_CMD_FINI: |
158 | return 0; |
159 | default: |
160 | return ENOTTY; |
161 | } |
162 | } |
163 | |
164 | int |
165 | oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t *retval) |
166 | { |
167 | /* { |
168 | syscallarg(int) fd; |
169 | syscallarg(u_long) com; |
170 | syscallarg(void *) data; |
171 | } */ |
172 | file_t *fp; |
173 | u_long com; |
174 | struct audio_info tmpinfo; |
175 | struct audio_offset tmpoffs; |
176 | struct oss_audio_buf_info bufinfo; |
177 | struct oss_count_info cntinfo; |
178 | struct audio_encoding tmpenc; |
179 | u_int u; |
180 | int idat, idata; |
181 | int error = 0; |
182 | int (*ioctlf)(file_t *, u_long, void *); |
183 | |
184 | if ((fp = fd_getfile(SCARG(uap, fd))) == NULL) |
185 | return (EBADF); |
186 | |
187 | if ((fp->f_flag & (FREAD | FWRITE)) == 0) { |
188 | error = EBADF; |
189 | goto out; |
190 | } |
191 | |
192 | com = SCARG(uap, com); |
193 | DPRINTF(("%s: com=%s\n" , __func__, compat_ossaudio_getcmd(com))); |
194 | |
195 | retval[0] = 0; |
196 | |
197 | ioctlf = fp->f_ops->fo_ioctl; |
198 | switch (com) { |
199 | case OSS_SNDCTL_DSP_RESET: |
200 | error = ioctlf(fp, AUDIO_FLUSH, NULL); |
201 | if (error) { |
202 | DPRINTF(("%s: AUDIO_FLUSH %d\n" , __func__, error)); |
203 | goto out; |
204 | } |
205 | break; |
206 | case OSS_SNDCTL_DSP_SYNC: |
207 | error = ioctlf(fp, AUDIO_DRAIN, NULL); |
208 | if (error) { |
209 | DPRINTF(("%s: AUDIO_DRAIN %d\n" , __func__, error)); |
210 | goto out; |
211 | } |
212 | break; |
213 | case OSS_SNDCTL_DSP_POST: |
214 | /* This call is merely advisory, and may be a nop. */ |
215 | break; |
216 | case OSS_SNDCTL_DSP_SPEED: |
217 | AUDIO_INITINFO(&tmpinfo); |
218 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
219 | if (error) { |
220 | DPRINTF(("%s: SNDCTL_DSP_SPEED %d\n" , |
221 | __func__, error)); |
222 | goto out; |
223 | } |
224 | tmpinfo.play.sample_rate = |
225 | tmpinfo.record.sample_rate = idat; |
226 | DPRINTF(("%s: SNDCTL_DSP_SPEED > %d\n" , __func__, idat)); |
227 | error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo); |
228 | if (error) { |
229 | DPRINTF(("%s: SNDCTL_DSP_SPEED %d = %d\n" , |
230 | __func__, idat, error)); |
231 | goto out; |
232 | } |
233 | /* fall into ... */ |
234 | case OSS_SOUND_PCM_READ_RATE: |
235 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
236 | if (error) { |
237 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
238 | __func__, error)); |
239 | goto out; |
240 | } |
241 | idat = tmpinfo.play.sample_rate; |
242 | DPRINTF(("%s: SNDCTL_PCM_READ_RATE < %d\n" , __func__, idat)); |
243 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
244 | if (error) { |
245 | DPRINTF(("%s: SOUND_PCM_READ_RATE %d = %d\n" , |
246 | __func__, idat, error)); |
247 | goto out; |
248 | } |
249 | break; |
250 | case OSS_SNDCTL_DSP_STEREO: |
251 | AUDIO_INITINFO(&tmpinfo); |
252 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
253 | if (error) { |
254 | DPRINTF(("%s: SNDCTL_DSP_STEREO %d\n" , |
255 | __func__, error)); |
256 | goto out; |
257 | } |
258 | tmpinfo.play.channels = |
259 | tmpinfo.record.channels = idat ? 2 : 1; |
260 | error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo); |
261 | if (error) { |
262 | DPRINTF(("%s: AUDIO_SETINFO %d\n" , |
263 | __func__, error)); |
264 | goto out; |
265 | } |
266 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
267 | if (error) { |
268 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
269 | __func__, error)); |
270 | goto out; |
271 | } |
272 | idat = tmpinfo.play.channels - 1; |
273 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
274 | if (error) { |
275 | DPRINTF(("%s: SNDCTL_DSP_STEREO %d = %d\n" , |
276 | __func__, idat, error)); |
277 | goto out; |
278 | } |
279 | break; |
280 | case OSS_SNDCTL_DSP_GETBLKSIZE: |
281 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
282 | if (error) { |
283 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
284 | __func__, error)); |
285 | goto out; |
286 | } |
287 | setblocksize(fp, &tmpinfo); |
288 | idat = tmpinfo.blocksize; |
289 | DPRINTF(("%s: SNDCTL_DSP_GETBLKSIZE < %d\n" , |
290 | __func__, idat)); |
291 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
292 | if (error) { |
293 | DPRINTF(("%s: SNDCTL_DSP_GETBLKSIZE %d = %d\n" , |
294 | __func__, idat, error)); |
295 | goto out; |
296 | } |
297 | break; |
298 | case OSS_SNDCTL_DSP_SETFMT: |
299 | AUDIO_INITINFO(&tmpinfo); |
300 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
301 | if (error) { |
302 | DPRINTF(("%s: SNDCTL_DSP_SETFMT %d\n" , |
303 | __func__, error)); |
304 | goto out; |
305 | } |
306 | switch (idat) { |
307 | case OSS_AFMT_MU_LAW: |
308 | tmpinfo.play.precision = |
309 | tmpinfo.record.precision = 8; |
310 | tmpinfo.play.encoding = |
311 | tmpinfo.record.encoding = AUDIO_ENCODING_ULAW; |
312 | break; |
313 | case OSS_AFMT_A_LAW: |
314 | tmpinfo.play.precision = |
315 | tmpinfo.record.precision = 8; |
316 | tmpinfo.play.encoding = |
317 | tmpinfo.record.encoding = AUDIO_ENCODING_ALAW; |
318 | break; |
319 | case OSS_AFMT_U8: |
320 | tmpinfo.play.precision = |
321 | tmpinfo.record.precision = 8; |
322 | tmpinfo.play.encoding = |
323 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR; |
324 | break; |
325 | case OSS_AFMT_S8: |
326 | tmpinfo.play.precision = |
327 | tmpinfo.record.precision = 8; |
328 | tmpinfo.play.encoding = |
329 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR; |
330 | break; |
331 | case OSS_AFMT_S16_LE: |
332 | tmpinfo.play.precision = |
333 | tmpinfo.record.precision = 16; |
334 | tmpinfo.play.encoding = |
335 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; |
336 | break; |
337 | case OSS_AFMT_S16_BE: |
338 | tmpinfo.play.precision = |
339 | tmpinfo.record.precision = 16; |
340 | tmpinfo.play.encoding = |
341 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; |
342 | break; |
343 | case OSS_AFMT_U16_LE: |
344 | tmpinfo.play.precision = |
345 | tmpinfo.record.precision = 16; |
346 | tmpinfo.play.encoding = |
347 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE; |
348 | break; |
349 | case OSS_AFMT_U16_BE: |
350 | tmpinfo.play.precision = |
351 | tmpinfo.record.precision = 16; |
352 | tmpinfo.play.encoding = |
353 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE; |
354 | break; |
355 | case OSS_AFMT_AC3: |
356 | tmpinfo.play.precision = |
357 | tmpinfo.record.precision = 16; |
358 | tmpinfo.play.encoding = |
359 | tmpinfo.record.encoding = AUDIO_ENCODING_AC3; |
360 | break; |
361 | default: |
362 | DPRINTF(("%s: SNDCTL_DSP_SETFMT bad fmt %d\n" , |
363 | __func__, idat)); |
364 | error = EINVAL; |
365 | goto out; |
366 | } |
367 | DPRINTF(("%s: SNDCTL_DSP_SETFMT > 0x%x\n" , |
368 | __func__, idat)); |
369 | error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo); |
370 | if (error) { |
371 | DPRINTF(("%s: AUDIO_SETINFO %d\n" , |
372 | __func__, error)); |
373 | goto out; |
374 | } |
375 | /* fall into ... */ |
376 | case OSS_SOUND_PCM_READ_BITS: |
377 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
378 | if (error) { |
379 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
380 | __func__, error)); |
381 | goto out; |
382 | } |
383 | switch (tmpinfo.play.encoding) { |
384 | case AUDIO_ENCODING_ULAW: |
385 | idat = OSS_AFMT_MU_LAW; |
386 | break; |
387 | case AUDIO_ENCODING_ALAW: |
388 | idat = OSS_AFMT_A_LAW; |
389 | break; |
390 | case AUDIO_ENCODING_SLINEAR_LE: |
391 | if (tmpinfo.play.precision == 16) |
392 | idat = OSS_AFMT_S16_LE; |
393 | else |
394 | idat = OSS_AFMT_S8; |
395 | break; |
396 | case AUDIO_ENCODING_SLINEAR_BE: |
397 | if (tmpinfo.play.precision == 16) |
398 | idat = OSS_AFMT_S16_BE; |
399 | else |
400 | idat = OSS_AFMT_S8; |
401 | break; |
402 | case AUDIO_ENCODING_ULINEAR_LE: |
403 | if (tmpinfo.play.precision == 16) |
404 | idat = OSS_AFMT_U16_LE; |
405 | else |
406 | idat = OSS_AFMT_U8; |
407 | break; |
408 | case AUDIO_ENCODING_ULINEAR_BE: |
409 | if (tmpinfo.play.precision == 16) |
410 | idat = OSS_AFMT_U16_BE; |
411 | else |
412 | idat = OSS_AFMT_U8; |
413 | break; |
414 | case AUDIO_ENCODING_ADPCM: |
415 | idat = OSS_AFMT_IMA_ADPCM; |
416 | break; |
417 | case AUDIO_ENCODING_AC3: |
418 | idat = OSS_AFMT_AC3; |
419 | break; |
420 | default: |
421 | DPRINTF(("%s: SOUND_PCM_READ_BITS bad encoding %d\n" , |
422 | __func__, tmpinfo.play.encoding)); |
423 | error = EINVAL; |
424 | goto out; |
425 | } |
426 | DPRINTF(("%s: SOUND_PCM_READ_BITS < 0x%x\n" , |
427 | __func__, idat)); |
428 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
429 | if (error) { |
430 | DPRINTF(("%s: SOUND_PCM_READ_BITS %d = %d\n" , |
431 | __func__, idat, error)); |
432 | goto out; |
433 | } |
434 | break; |
435 | case OSS_SNDCTL_DSP_CHANNELS: |
436 | AUDIO_INITINFO(&tmpinfo); |
437 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
438 | if (error) { |
439 | DPRINTF(("%s: SNDCTL_DSP_CHANNELS %d\n" , |
440 | __func__, error)); |
441 | goto out; |
442 | } |
443 | tmpinfo.play.channels = |
444 | tmpinfo.record.channels = idat; |
445 | DPRINTF(("%s: SNDCTL_DSP_CHANNELS > %d\n" , __func__, idat)); |
446 | error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo); |
447 | if (error) { |
448 | DPRINTF(("%s: AUDIO_SETINFO %d\n" , |
449 | __func__, error)); |
450 | goto out; |
451 | } |
452 | /* fall into ... */ |
453 | case OSS_SOUND_PCM_READ_CHANNELS: |
454 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
455 | if (error) { |
456 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
457 | __func__, error)); |
458 | goto out; |
459 | } |
460 | idat = tmpinfo.play.channels; |
461 | DPRINTF(("%s: SOUND_PCM_READ_CHANNELS < %d\n" , __func__, idat)); |
462 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
463 | if (error) { |
464 | DPRINTF(("%s: SOUND_PCM_READ_CHANNELS %d = %d\n" , |
465 | __func__, idat, error)); |
466 | goto out; |
467 | } |
468 | break; |
469 | case OSS_SOUND_PCM_WRITE_FILTER: |
470 | case OSS_SOUND_PCM_READ_FILTER: |
471 | error = EINVAL; /* XXX unimplemented */ |
472 | DPRINTF(("%s: SOUND_PCM_{READ,WRITE}_FILTER filter\n" , |
473 | __func__)); |
474 | goto out; |
475 | case OSS_SNDCTL_DSP_SUBDIVIDE: |
476 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
477 | if (error) { |
478 | DPRINTF(("%s: SNDCTL_DSP_SUBDIVIDE %d\n" , |
479 | __func__, error)); |
480 | goto out; |
481 | } |
482 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
483 | if (error) { |
484 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
485 | __func__, error)); |
486 | goto out; |
487 | } |
488 | setblocksize(fp, &tmpinfo); |
489 | if (idat == 0) |
490 | idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; |
491 | idat = (tmpinfo.play.buffer_size / idat) & -4; |
492 | AUDIO_INITINFO(&tmpinfo); |
493 | tmpinfo.blocksize = idat; |
494 | error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo); |
495 | if (error) { |
496 | DPRINTF(("%s: AUDIO_SETINFO %d\n" , |
497 | __func__, error)); |
498 | goto out; |
499 | } |
500 | idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; |
501 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
502 | if (error) { |
503 | DPRINTF(("%s: SNDCTL_DSP_SUBDIVIDE %d = %d\n" , |
504 | __func__, idat, error)); |
505 | goto out; |
506 | } |
507 | break; |
508 | case OSS_SNDCTL_DSP_SETFRAGMENT: |
509 | AUDIO_INITINFO(&tmpinfo); |
510 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
511 | if (error) { |
512 | DPRINTF(("%s: DSP_SETFRAGMENT %d\n" , |
513 | __func__, error)); |
514 | goto out; |
515 | } |
516 | if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) { |
517 | DPRINTF(("%s: DSP_SETFRAGMENT bad ival%d\n" , |
518 | __func__, idat)); |
519 | error = EINVAL; |
520 | goto out; |
521 | } |
522 | tmpinfo.blocksize = 1 << (idat & 0xffff); |
523 | tmpinfo.hiwat = (idat >> 16) & 0x7fff; |
524 | DPRINTF(("%s: SNDCTL_DSP_SETFRAGMENT blksize=%d, " |
525 | "hiwat=%d\n" , __func__, tmpinfo.blocksize, tmpinfo.hiwat)); |
526 | if (tmpinfo.hiwat == 0) /* 0 means set to max */ |
527 | tmpinfo.hiwat = 65536; |
528 | error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo); |
529 | if (error) { |
530 | DPRINTF(("%s: AUDIO_SETINFO %d\n" , |
531 | __func__, error)); |
532 | goto out; |
533 | } |
534 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
535 | if (error) { |
536 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
537 | __func__, error)); |
538 | goto out; |
539 | } |
540 | u = tmpinfo.blocksize; |
541 | for(idat = 0; u > 1; idat++, u >>= 1) |
542 | ; |
543 | idat |= (tmpinfo.hiwat & 0x7fff) << 16; |
544 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
545 | if (error) { |
546 | DPRINTF(("%s: SNDCTL_DSP_SETFRAGMENT %d = %d\n" , |
547 | __func__, idat, error)); |
548 | goto out; |
549 | } |
550 | break; |
551 | case OSS_SNDCTL_DSP_GETFMTS: |
552 | for (idat = 0, tmpenc.index = 0; |
553 | ioctlf(fp, AUDIO_GETENC, &tmpenc) == 0; |
554 | tmpenc.index++) { |
555 | switch(tmpenc.encoding) { |
556 | case AUDIO_ENCODING_ULAW: |
557 | idat |= OSS_AFMT_MU_LAW; |
558 | break; |
559 | case AUDIO_ENCODING_ALAW: |
560 | idat |= OSS_AFMT_A_LAW; |
561 | break; |
562 | case AUDIO_ENCODING_SLINEAR: |
563 | idat |= OSS_AFMT_S8; |
564 | break; |
565 | case AUDIO_ENCODING_SLINEAR_LE: |
566 | if (tmpenc.precision == 16) |
567 | idat |= OSS_AFMT_S16_LE; |
568 | else |
569 | idat |= OSS_AFMT_S8; |
570 | break; |
571 | case AUDIO_ENCODING_SLINEAR_BE: |
572 | if (tmpenc.precision == 16) |
573 | idat |= OSS_AFMT_S16_BE; |
574 | else |
575 | idat |= OSS_AFMT_S8; |
576 | break; |
577 | case AUDIO_ENCODING_ULINEAR: |
578 | idat |= OSS_AFMT_U8; |
579 | break; |
580 | case AUDIO_ENCODING_ULINEAR_LE: |
581 | if (tmpenc.precision == 16) |
582 | idat |= OSS_AFMT_U16_LE; |
583 | else |
584 | idat |= OSS_AFMT_U8; |
585 | break; |
586 | case AUDIO_ENCODING_ULINEAR_BE: |
587 | if (tmpenc.precision == 16) |
588 | idat |= OSS_AFMT_U16_BE; |
589 | else |
590 | idat |= OSS_AFMT_U8; |
591 | break; |
592 | case AUDIO_ENCODING_ADPCM: |
593 | idat |= OSS_AFMT_IMA_ADPCM; |
594 | break; |
595 | case AUDIO_ENCODING_AC3: |
596 | idat |= OSS_AFMT_AC3; |
597 | break; |
598 | default: |
599 | DPRINTF(("%s: SNDCTL_DSP_GETFMTS unknown %d\n" , |
600 | __func__, tmpenc.encoding)); |
601 | break; |
602 | } |
603 | } |
604 | DPRINTF(("%s: SNDCTL_DSP_GETFMTS < 0x%x\n" , |
605 | __func__, idat)); |
606 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
607 | if (error) { |
608 | DPRINTF(("%s: SNDCTL_DSP_GETFMTS = %x = %d\n" , |
609 | __func__, idat, error)); |
610 | goto out; |
611 | } |
612 | break; |
613 | case OSS_SNDCTL_DSP_GETOSPACE: |
614 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
615 | if (error) { |
616 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
617 | __func__, error)); |
618 | goto out; |
619 | } |
620 | setblocksize(fp, &tmpinfo); |
621 | bufinfo.fragsize = tmpinfo.blocksize; |
622 | bufinfo.fragments = tmpinfo.hiwat - |
623 | (tmpinfo.play.seek + tmpinfo.blocksize - 1) / |
624 | tmpinfo.blocksize; |
625 | bufinfo.fragstotal = tmpinfo.hiwat; |
626 | bufinfo.bytes = |
627 | tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.play.seek; |
628 | error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo); |
629 | if (error) { |
630 | DPRINTF(("%s: SNDCTL_DSP_GETOSPACE = %d\n" , |
631 | __func__, error)); |
632 | goto out; |
633 | } |
634 | break; |
635 | case OSS_SNDCTL_DSP_GETISPACE: |
636 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
637 | if (error) { |
638 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
639 | __func__, error)); |
640 | goto out; |
641 | } |
642 | setblocksize(fp, &tmpinfo); |
643 | bufinfo.fragsize = tmpinfo.blocksize; |
644 | bufinfo.fragments = tmpinfo.hiwat - |
645 | (tmpinfo.record.seek + tmpinfo.blocksize - 1) / |
646 | tmpinfo.blocksize; |
647 | bufinfo.fragstotal = tmpinfo.hiwat; |
648 | bufinfo.bytes = |
649 | tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.record.seek; |
650 | error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo); |
651 | if (error) { |
652 | DPRINTF(("%s: SNDCTL_DSP_GETISPACE %d %d %d %d = %d\n" , |
653 | __func__, bufinfo.fragsize, bufinfo.fragments, |
654 | bufinfo.fragstotal, bufinfo.bytes, error)); |
655 | goto out; |
656 | } |
657 | break; |
658 | case OSS_SNDCTL_DSP_NONBLOCK: |
659 | idat = 1; |
660 | error = ioctlf(fp, FIONBIO, &idat); |
661 | if (error) { |
662 | DPRINTF(("%s: SENDCLT_DSP_NONBLOCK %d\n" , |
663 | __func__, error)); |
664 | goto out; |
665 | } |
666 | break; |
667 | case OSS_SNDCTL_DSP_GETCAPS: |
668 | error = ioctlf(fp, AUDIO_GETPROPS, &idata); |
669 | if (error) { |
670 | DPRINTF(("%s: AUDIO_GETPROPS %d\n" , |
671 | __func__, error)); |
672 | goto out; |
673 | } |
674 | idat = OSS_DSP_CAP_TRIGGER; /* pretend we have trigger */ |
675 | if (idata & AUDIO_PROP_FULLDUPLEX) |
676 | idat |= OSS_DSP_CAP_DUPLEX; |
677 | if (idata & AUDIO_PROP_MMAP) |
678 | idat |= OSS_DSP_CAP_MMAP; |
679 | DPRINTF(("%s: SNDCL_DSP_GETCAPS %s duplex, %smmap\n" , |
680 | __func__, (idat & OSS_DSP_CAP_DUPLEX) ? "full" : "half" , |
681 | (idat & OSS_DSP_CAP_MMAP) ? "" : "no " )); |
682 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
683 | if (error) { |
684 | DPRINTF(("%s: SNDCTL_DSP_GETCAPS %x = %d\n" , __func__, |
685 | idat, error)); |
686 | goto out; |
687 | } |
688 | break; |
689 | #if 0 |
690 | case OSS_SNDCTL_DSP_GETTRIGGER: |
691 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
692 | if (error) { |
693 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
694 | __func__, error)); |
695 | goto out; |
696 | } |
697 | idat = (tmpinfo.play.pause ? 0 : OSS_PCM_ENABLE_OUTPUT) | |
698 | (tmpinfo.record.pause ? 0 : OSS_PCM_ENABLE_INPUT); |
699 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
700 | if (error) { |
701 | DPRINTF(("%s: SNDCTL_DSP_SETRIGGER %x = %d\n" , |
702 | __func__, idat, error)); |
703 | goto out; |
704 | } |
705 | break; |
706 | case OSS_SNDCTL_DSP_SETTRIGGER: |
707 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo, p); |
708 | if (error) { |
709 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
710 | __func__, error)); |
711 | goto out; |
712 | } |
713 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
714 | if (error) { |
715 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
716 | __func__, error)); |
717 | goto out; |
718 | } |
719 | tmpinfo.play.pause = (idat & OSS_PCM_ENABLE_OUTPUT) == 0; |
720 | tmpinfo.record.pause = (idat & OSS_PCM_ENABLE_INPUT) == 0; |
721 | error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo); |
722 | if (error) { |
723 | DPRINTF(("%s: AUDIO_SETINFO %d\n" , |
724 | __func__, error)); |
725 | goto out; |
726 | } |
727 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
728 | if (error) { |
729 | DPRINTF(("%s: SNDCTL_DSP_SETRIGGER %x = %d\n" , |
730 | __func__, idat, error)); |
731 | goto out; |
732 | } |
733 | break; |
734 | #else |
735 | case OSS_SNDCTL_DSP_GETTRIGGER: |
736 | case OSS_SNDCTL_DSP_SETTRIGGER: |
737 | /* XXX Do nothing for now. */ |
738 | idat = OSS_PCM_ENABLE_OUTPUT; |
739 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
740 | if (error) { |
741 | DPRINTF(("%s: SNDCTL_DSP_{GET,SET}RIGGER %x = %d\n" , |
742 | __func__, idat, error)); |
743 | goto out; |
744 | } |
745 | break; |
746 | #endif |
747 | case OSS_SNDCTL_DSP_GETIPTR: |
748 | error = ioctlf(fp, AUDIO_GETIOFFS, &tmpoffs); |
749 | if (error) { |
750 | DPRINTF(("%s: AUDIO_GETIOFFS %d\n" , |
751 | __func__, error)); |
752 | goto out; |
753 | } |
754 | cntinfo.bytes = tmpoffs.samples; |
755 | cntinfo.blocks = tmpoffs.deltablks; |
756 | cntinfo.ptr = tmpoffs.offset; |
757 | error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo); |
758 | if (error) { |
759 | DPRINTF(("%s: SNDCTL_DSP_GETIPTR %d\n" , |
760 | __func__, error)); |
761 | goto out; |
762 | } |
763 | break; |
764 | case OSS_SNDCTL_DSP_GETOPTR: |
765 | error = ioctlf(fp, AUDIO_GETOOFFS, &tmpoffs); |
766 | if (error) { |
767 | DPRINTF(("%s: AUDIO_GETOOFFS %d\n" , |
768 | __func__, error)); |
769 | goto out; |
770 | } |
771 | cntinfo.bytes = tmpoffs.samples; |
772 | cntinfo.blocks = tmpoffs.deltablks; |
773 | cntinfo.ptr = tmpoffs.offset; |
774 | error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo); |
775 | if (error) { |
776 | DPRINTF(("%s: SNDCTL_DSP_GETOPTR %d\n" , |
777 | __func__, error)); |
778 | goto out; |
779 | } |
780 | break; |
781 | case OSS_SNDCTL_DSP_SETDUPLEX: |
782 | idat = 1; |
783 | error = ioctlf(fp, AUDIO_SETFD, &idat); |
784 | if (error) { |
785 | DPRINTF(("%s: AUDIO_SETFD %d = %d\n" , |
786 | __func__, idat, error)); |
787 | goto out; |
788 | } |
789 | break; |
790 | case OSS_SNDCTL_DSP_MAPINBUF: |
791 | DPRINTF(("%s: Unimplemented SNDCTL_DSP_MAPINBUF\n" , |
792 | __func__)); |
793 | error = EINVAL; |
794 | goto out; |
795 | case OSS_SNDCTL_DSP_MAPOUTBUF: |
796 | DPRINTF(("%s: Unimplemented SNDCTL_DSP_MAPOUTBUF\n" , |
797 | __func__)); |
798 | error = EINVAL; |
799 | goto out; |
800 | case OSS_SNDCTL_DSP_SETSYNCRO: |
801 | DPRINTF(("%s: Unimplemented SNDCTL_DSP_GETSYNCHRO\n" , |
802 | __func__)); |
803 | error = EINVAL; |
804 | goto out; |
805 | case OSS_SNDCTL_DSP_GETODELAY: |
806 | error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo); |
807 | if (error) { |
808 | DPRINTF(("%s: AUDIO_GETBUFINFO %d\n" , |
809 | __func__, error)); |
810 | goto out; |
811 | } |
812 | idat = tmpinfo.play.seek + tmpinfo.blocksize / 2; |
813 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
814 | if (error) { |
815 | DPRINTF(("%s: SNDCTL_DSP_GETODELAY %d\n" , |
816 | __func__, error)); |
817 | goto out; |
818 | } |
819 | break; |
820 | case OSS_SNDCTL_DSP_PROFILE: |
821 | /* This gives just a hint to the driver, |
822 | * implementing it as a NOP is ok |
823 | */ |
824 | DPRINTF(("%s: SNDCTL_DSP_PROFILE\n" , __func__)); |
825 | break; |
826 | default: |
827 | DPRINTF(("%s: Unimplemented 0x%lx\n" , __func__, com)); |
828 | error = EINVAL; |
829 | goto out; |
830 | } |
831 | |
832 | out: |
833 | fd_putfile(SCARG(uap, fd)); |
834 | return error; |
835 | } |
836 | |
837 | /* If the NetBSD mixer device should have more than 32 devices |
838 | * some will not be available to Linux */ |
839 | #define NETBSD_MAXDEVS 64 |
840 | struct audiodevinfo { |
841 | int done; |
842 | dev_t dev; |
843 | int16_t devmap[OSS_SOUND_MIXER_NRDEVICES], |
844 | rdevmap[NETBSD_MAXDEVS]; |
845 | char names[NETBSD_MAXDEVS][MAX_AUDIO_DEV_LEN]; |
846 | int enum2opaque[NETBSD_MAXDEVS]; |
847 | u_long devmask, recmask, stereomask; |
848 | u_long caps, source; |
849 | }; |
850 | |
851 | static int |
852 | opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq) |
853 | { |
854 | int i, o; |
855 | |
856 | for (i = 0; i < NETBSD_MAXDEVS; i++) { |
857 | o = di->enum2opaque[i]; |
858 | if (o == opq) |
859 | break; |
860 | if (o == -1 && label != NULL && |
861 | !strncmp(di->names[i], label->name, sizeof di->names[i])) { |
862 | di->enum2opaque[i] = opq; |
863 | break; |
864 | } |
865 | } |
866 | if (i >= NETBSD_MAXDEVS) |
867 | i = -1; |
868 | /*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/ |
869 | return (i); |
870 | } |
871 | |
872 | static int |
873 | enum_to_ord(struct audiodevinfo *di, int enm) |
874 | { |
875 | if (enm >= NETBSD_MAXDEVS) |
876 | return (-1); |
877 | |
878 | /*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/ |
879 | return (di->enum2opaque[enm]); |
880 | } |
881 | |
882 | static int |
883 | enum_to_mask(struct audiodevinfo *di, int enm) |
884 | { |
885 | int m; |
886 | if (enm >= NETBSD_MAXDEVS) |
887 | return (0); |
888 | |
889 | m = di->enum2opaque[enm]; |
890 | if (m == -1) |
891 | m = 0; |
892 | /*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/ |
893 | return (m); |
894 | } |
895 | |
896 | /* |
897 | * Collect the audio device information to allow faster |
898 | * emulation of the Linux mixer ioctls. Cache the information |
899 | * to eliminate the overhead of repeating all the ioctls needed |
900 | * to collect the information. |
901 | */ |
902 | static struct audiodevinfo * |
903 | getdevinfo(file_t *fp) |
904 | { |
905 | mixer_devinfo_t mi; |
906 | int i, j, e; |
907 | static const struct { |
908 | const char *name; |
909 | int code; |
910 | } *dp, devs[] = { |
911 | { AudioNmicrophone, OSS_SOUND_MIXER_MIC }, |
912 | { AudioNline, OSS_SOUND_MIXER_LINE }, |
913 | { AudioNcd, OSS_SOUND_MIXER_CD }, |
914 | { AudioNdac, OSS_SOUND_MIXER_PCM }, |
915 | { AudioNaux, OSS_SOUND_MIXER_LINE1 }, |
916 | { AudioNrecord, OSS_SOUND_MIXER_IMIX }, |
917 | { AudioNmaster, OSS_SOUND_MIXER_VOLUME }, |
918 | { AudioNtreble, OSS_SOUND_MIXER_TREBLE }, |
919 | { AudioNbass, OSS_SOUND_MIXER_BASS }, |
920 | { AudioNspeaker, OSS_SOUND_MIXER_SPEAKER }, |
921 | /* { AudioNheadphone, ?? },*/ |
922 | { AudioNoutput, OSS_SOUND_MIXER_OGAIN }, |
923 | { AudioNinput, OSS_SOUND_MIXER_IGAIN }, |
924 | /* { AudioNmaster, OSS_SOUND_MIXER_SPEAKER },*/ |
925 | /* { AudioNstereo, ?? },*/ |
926 | /* { AudioNmono, ?? },*/ |
927 | { AudioNfmsynth, OSS_SOUND_MIXER_SYNTH }, |
928 | /* { AudioNwave, OSS_SOUND_MIXER_PCM },*/ |
929 | { AudioNmidi, OSS_SOUND_MIXER_SYNTH }, |
930 | /* { AudioNmixerout, ?? },*/ |
931 | { 0, -1 } |
932 | }; |
933 | int (*ioctlf)(file_t *, u_long, void *) = fp->f_ops->fo_ioctl; |
934 | struct vnode *vp; |
935 | struct vattr va; |
936 | static struct audiodevinfo devcache; |
937 | struct audiodevinfo *di = &devcache; |
938 | int error, mlen, dlen; |
939 | |
940 | /* |
941 | * Figure out what device it is so we can check if the |
942 | * cached data is valid. |
943 | */ |
944 | vp = fp->f_vnode; |
945 | if (vp->v_type != VCHR) |
946 | return 0; |
947 | vn_lock(vp, LK_SHARED | LK_RETRY); |
948 | error = VOP_GETATTR(vp, &va, kauth_cred_get()); |
949 | VOP_UNLOCK(vp); |
950 | if (error) |
951 | return 0; |
952 | if (di->done && di->dev == va.va_rdev) |
953 | return di; |
954 | |
955 | di->done = 1; |
956 | di->dev = va.va_rdev; |
957 | di->devmask = 0; |
958 | di->recmask = 0; |
959 | di->stereomask = 0; |
960 | di->source = ~0; |
961 | di->caps = 0; |
962 | for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) |
963 | di->devmap[i] = -1; |
964 | for(i = 0; i < NETBSD_MAXDEVS; i++) { |
965 | di->rdevmap[i] = -1; |
966 | di->names[i][0] = '\0'; |
967 | di->enum2opaque[i] = -1; |
968 | } |
969 | for(i = 0; i < NETBSD_MAXDEVS; i++) { |
970 | mi.index = i; |
971 | if (ioctlf(fp, AUDIO_MIXER_DEVINFO, &mi) < 0) |
972 | break; |
973 | switch(mi.type) { |
974 | case AUDIO_MIXER_VALUE: |
975 | for(dp = devs; dp->name; dp++) { |
976 | if (strcmp(dp->name, mi.label.name) == 0) |
977 | break; |
978 | dlen = strlen(dp->name); |
979 | mlen = strlen(mi.label.name); |
980 | if (dlen < mlen |
981 | && mi.label.name[mlen-dlen-1] == '.' |
982 | && strcmp(dp->name, mi.label.name + mlen - dlen) == 0) |
983 | break; |
984 | } |
985 | if (dp->code >= 0) { |
986 | di->devmap[dp->code] = i; |
987 | di->rdevmap[i] = dp->code; |
988 | di->devmask |= 1 << dp->code; |
989 | if (mi.un.v.num_channels == 2) |
990 | di->stereomask |= 1 << dp->code; |
991 | strncpy(di->names[i], mi.label.name, |
992 | sizeof di->names[i]); |
993 | } |
994 | break; |
995 | } |
996 | } |
997 | for(i = 0; i < NETBSD_MAXDEVS; i++) { |
998 | mi.index = i; |
999 | if (ioctlf(fp, AUDIO_MIXER_DEVINFO, &mi) < 0) |
1000 | break; |
1001 | if (strcmp(mi.label.name, AudioNsource) != 0) |
1002 | continue; |
1003 | di->source = i; |
1004 | switch(mi.type) { |
1005 | case AUDIO_MIXER_ENUM: |
1006 | for(j = 0; j < mi.un.e.num_mem; j++) { |
1007 | e = opaque_to_enum(di, |
1008 | &mi.un.e.member[j].label, |
1009 | mi.un.e.member[j].ord); |
1010 | if (e >= 0) |
1011 | di->recmask |= 1 << di->rdevmap[e]; |
1012 | } |
1013 | di->caps = OSS_SOUND_CAP_EXCL_INPUT; |
1014 | break; |
1015 | case AUDIO_MIXER_SET: |
1016 | for(j = 0; j < mi.un.s.num_mem; j++) { |
1017 | e = opaque_to_enum(di, |
1018 | &mi.un.s.member[j].label, |
1019 | mi.un.s.member[j].mask); |
1020 | if (e >= 0) |
1021 | di->recmask |= 1 << di->rdevmap[e]; |
1022 | } |
1023 | break; |
1024 | } |
1025 | } |
1026 | return di; |
1027 | } |
1028 | |
1029 | int |
1030 | oss_ioctl_mixer(struct lwp *lwp, const struct oss_sys_ioctl_args *uap, register_t *retval) |
1031 | { |
1032 | /* { |
1033 | syscallarg(int) fd; |
1034 | syscallarg(u_long) com; |
1035 | syscallarg(void *) data; |
1036 | } */ |
1037 | file_t *fp; |
1038 | u_long com; |
1039 | struct audiodevinfo *di; |
1040 | mixer_ctrl_t mc; |
1041 | struct oss_mixer_info omi; |
1042 | struct audio_device adev; |
1043 | int idat; |
1044 | int i; |
1045 | int error; |
1046 | int l, r, n, e; |
1047 | int (*ioctlf)(file_t *, u_long, void *); |
1048 | |
1049 | if ((error = fd_getvnode(SCARG(uap, fd), &fp)) != 0) |
1050 | return error; |
1051 | |
1052 | if ((fp->f_flag & (FREAD | FWRITE)) == 0) { |
1053 | error = EBADF; |
1054 | goto out; |
1055 | } |
1056 | |
1057 | com = SCARG(uap, com); |
1058 | DPRINTF(("%s: com=%s\n" , __func__, compat_ossaudio_getcmd(com))); |
1059 | |
1060 | retval[0] = 0; |
1061 | |
1062 | di = getdevinfo(fp); |
1063 | if (di == 0) { |
1064 | error = EINVAL; |
1065 | goto out; |
1066 | } |
1067 | |
1068 | ioctlf = fp->f_ops->fo_ioctl; |
1069 | switch (com) { |
1070 | case OSS_GET_VERSION: |
1071 | idat = OSS_SOUND_VERSION; |
1072 | break; |
1073 | case OSS_SOUND_MIXER_INFO: |
1074 | case OSS_SOUND_OLD_MIXER_INFO: |
1075 | error = ioctlf(fp, AUDIO_GETDEV, &adev); |
1076 | if (error) { |
1077 | DPRINTF(("%s: AUDIO_GETDEV %d\n" , |
1078 | __func__, error)); |
1079 | goto out; |
1080 | } |
1081 | omi.modify_counter = 1; |
1082 | strncpy(omi.id, adev.name, sizeof omi.id); |
1083 | strncpy(omi.name, adev.name, sizeof omi.name); |
1084 | error = copyout(&omi, SCARG(uap, data), OSS_IOCTL_SIZE(com)); |
1085 | if (error) { |
1086 | DPRINTF(("%s: OSS_SOUND_MIXER_INFO %d\n" , |
1087 | __func__, error)); |
1088 | goto out; |
1089 | } |
1090 | break; |
1091 | case OSS_SOUND_MIXER_READ_RECSRC: |
1092 | if (di->source == -1) { |
1093 | DPRINTF(("%s: OSS_SOUND_MIXER_READ_RECSRC bad source\n" , |
1094 | __func__)); |
1095 | error = EINVAL; |
1096 | goto out; |
1097 | } |
1098 | mc.dev = di->source; |
1099 | if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { |
1100 | mc.type = AUDIO_MIXER_ENUM; |
1101 | error = ioctlf(fp, AUDIO_MIXER_READ, &mc); |
1102 | if (error) { |
1103 | DPRINTF(("%s: AUDIO_MIXER_READ %d\n" , |
1104 | __func__, error)); |
1105 | goto out; |
1106 | } |
1107 | e = opaque_to_enum(di, NULL, mc.un.ord); |
1108 | if (e >= 0) |
1109 | idat = 1 << di->rdevmap[e]; |
1110 | } else { |
1111 | mc.type = AUDIO_MIXER_SET; |
1112 | error = ioctlf(fp, AUDIO_MIXER_READ, &mc); |
1113 | if (error) { |
1114 | DPRINTF(("%s: AUDIO_MIXER_READ %d\n" , |
1115 | __func__, error)); |
1116 | goto out; |
1117 | } |
1118 | e = opaque_to_enum(di, NULL, mc.un.mask); |
1119 | if (e >= 0) |
1120 | idat = 1 << di->rdevmap[e]; |
1121 | } |
1122 | break; |
1123 | case OSS_SOUND_MIXER_READ_DEVMASK: |
1124 | idat = di->devmask; |
1125 | break; |
1126 | case OSS_SOUND_MIXER_READ_RECMASK: |
1127 | idat = di->recmask; |
1128 | break; |
1129 | case OSS_SOUND_MIXER_READ_STEREODEVS: |
1130 | idat = di->stereomask; |
1131 | break; |
1132 | case OSS_SOUND_MIXER_READ_CAPS: |
1133 | idat = di->caps; |
1134 | break; |
1135 | case OSS_SOUND_MIXER_WRITE_RECSRC: |
1136 | case OSS_SOUND_MIXER_WRITE_R_RECSRC: |
1137 | if (di->source == -1) { |
1138 | DPRINTF(("%s: OSS_SOUND_MIXER_WRITE_RECSRC bad " |
1139 | "source\n" , __func__)); |
1140 | error = EINVAL; |
1141 | goto out; |
1142 | } |
1143 | mc.dev = di->source; |
1144 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
1145 | if (error) { |
1146 | DPRINTF(("%s: OSS_SOUND_MIXER_WRITE_RECSRC %d\n" , |
1147 | __func__, error)); |
1148 | goto out; |
1149 | } |
1150 | if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) { |
1151 | mc.type = AUDIO_MIXER_ENUM; |
1152 | for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) |
1153 | if (idat & (1 << i)) |
1154 | break; |
1155 | if (i >= OSS_SOUND_MIXER_NRDEVICES || |
1156 | di->devmap[i] == -1) { |
1157 | error = EINVAL; |
1158 | DPRINTF(("%s: OSS_SOUND_MIXER_WRITE_RECSRC " |
1159 | "bad index %d\n" , __func__, i)); |
1160 | goto out; |
1161 | } |
1162 | mc.un.ord = enum_to_ord(di, di->devmap[i]); |
1163 | } else { |
1164 | mc.type = AUDIO_MIXER_SET; |
1165 | mc.un.mask = 0; |
1166 | for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) { |
1167 | if (idat & (1 << i)) { |
1168 | if (di->devmap[i] == -1) { |
1169 | DPRINTF(("%s: OSS_SOUND_MIXER_" |
1170 | "WRITE_RECSRC bad devmap " |
1171 | "%d\n" , __func__, i)); |
1172 | error = EINVAL; |
1173 | goto out; |
1174 | } |
1175 | mc.un.mask |= enum_to_mask(di, |
1176 | di->devmap[i]); |
1177 | } |
1178 | } |
1179 | } |
1180 | error = ioctlf(fp, AUDIO_MIXER_WRITE, &mc); |
1181 | if (error) { |
1182 | DPRINTF(("%s: AUDIO_MIXER_WRITE %d\n" , |
1183 | __func__, error)); |
1184 | goto out; |
1185 | } |
1186 | goto out; |
1187 | default: |
1188 | if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com && |
1189 | com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) { |
1190 | n = OSS_GET_DEV(com); |
1191 | if (di->devmap[n] == -1) { |
1192 | DPRINTF(("%s: 0x%lx bad devmap %d\n" , |
1193 | __func__, com, n)); |
1194 | error = EINVAL; |
1195 | goto out; |
1196 | } |
1197 | doread: |
1198 | mc.dev = di->devmap[n]; |
1199 | mc.type = AUDIO_MIXER_VALUE; |
1200 | mc.un.value.num_channels = di->stereomask & |
1201 | (1 << n) ? 2 : 1; |
1202 | error = ioctlf(fp, AUDIO_MIXER_READ, &mc); |
1203 | if (error) { |
1204 | DPRINTF(("%s: AUDIO_MIXER_READ %d\n" , |
1205 | __func__, error)); |
1206 | goto out; |
1207 | } |
1208 | if (mc.un.value.num_channels != 2) { |
1209 | l = r = |
1210 | mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; |
1211 | } else { |
1212 | l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; |
1213 | r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; |
1214 | } |
1215 | idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); |
1216 | DPRINTF(("%s: n=%d (dev=%d) l=%d, r=%d, idat=%04x\n" , |
1217 | __func__, n, di->devmap[n], l, r, idat)); |
1218 | break; |
1219 | } else if ((OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_FIRST) <= com && |
1220 | com < OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_NRDEVICES)) || |
1221 | (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com && |
1222 | com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES))) { |
1223 | n = OSS_GET_DEV(com); |
1224 | if (di->devmap[n] == -1) { |
1225 | DPRINTF(("%s: 0x%lx bad devmap %d\n" , |
1226 | __func__, com, n)); |
1227 | error = EINVAL; |
1228 | goto out; |
1229 | } |
1230 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
1231 | if (error) { |
1232 | DPRINTF(("%s: 0x%lx error %d\n" , |
1233 | __func__, com, error)); |
1234 | goto out; |
1235 | } |
1236 | l = FROM_OSSVOL( idat & 0xff); |
1237 | r = FROM_OSSVOL((idat >> 8) & 0xff); |
1238 | mc.dev = di->devmap[n]; |
1239 | mc.type = AUDIO_MIXER_VALUE; |
1240 | if (di->stereomask & (1 << n)) { |
1241 | mc.un.value.num_channels = 2; |
1242 | mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; |
1243 | mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; |
1244 | } else { |
1245 | mc.un.value.num_channels = 1; |
1246 | mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = |
1247 | (l + r) / 2; |
1248 | } |
1249 | DPRINTF(("%s: n=%d (dev=%d) l=%d, r=%d, idat=%04x\n" , |
1250 | __func__, n, di->devmap[n], l, r, idat)); |
1251 | error = ioctlf(fp, AUDIO_MIXER_WRITE, &mc); |
1252 | if (error) { |
1253 | DPRINTF(("%s: AUDIO_MIXER_WRITE %d\n" , |
1254 | __func__, error)); |
1255 | goto out; |
1256 | } |
1257 | if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com && |
1258 | com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) { |
1259 | error = 0; |
1260 | goto out; |
1261 | } |
1262 | goto doread; |
1263 | } else { |
1264 | DPRINTF(("%s: Unknown mixer ioctl 0x%lx\n" , __func__, |
1265 | com)); |
1266 | error = EINVAL; |
1267 | goto out; |
1268 | } |
1269 | } |
1270 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
1271 | out: |
1272 | fd_putfile(SCARG(uap, fd)); |
1273 | return error; |
1274 | } |
1275 | |
1276 | /* Sequencer emulation */ |
1277 | int |
1278 | oss_ioctl_sequencer(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t *retval) |
1279 | { |
1280 | /* { |
1281 | syscallarg(int) fd; |
1282 | syscallarg(u_long) com; |
1283 | syscallarg(void *) data; |
1284 | } */ |
1285 | file_t *fp; |
1286 | u_long com; |
1287 | int idat, idat1; |
1288 | struct synth_info si; |
1289 | struct oss_synth_info osi; |
1290 | struct oss_seq_event_rec oser; |
1291 | int error; |
1292 | int (*ioctlf)(file_t *, u_long, void *); |
1293 | |
1294 | if ((fp = fd_getfile(SCARG(uap, fd))) == NULL) |
1295 | return (EBADF); |
1296 | |
1297 | if ((fp->f_flag & (FREAD | FWRITE)) == 0) { |
1298 | error = EBADF; |
1299 | goto out; |
1300 | } |
1301 | |
1302 | com = SCARG(uap, com); |
1303 | DPRINTF(("%s: com=%s\n" , __func__, compat_ossaudio_getcmd(com))); |
1304 | |
1305 | retval[0] = 0; |
1306 | |
1307 | ioctlf = fp->f_ops->fo_ioctl; |
1308 | switch (com) { |
1309 | case OSS_SEQ_RESET: |
1310 | error = ioctlf(fp, SEQUENCER_RESET, &idat); |
1311 | goto out; |
1312 | case OSS_SEQ_SYNC: |
1313 | error = ioctlf(fp, SEQUENCER_SYNC, &idat); |
1314 | goto out; |
1315 | case OSS_SYNTH_INFO: |
1316 | error = copyin(SCARG(uap, data), &osi, sizeof osi); |
1317 | if (error) |
1318 | goto out; |
1319 | si.device = osi.device; |
1320 | error = ioctlf(fp, SEQUENCER_INFO, &si); |
1321 | if (error) |
1322 | goto out; |
1323 | strncpy(osi.name, si.name, sizeof osi.name); |
1324 | osi.device = si.device; |
1325 | switch(si.synth_type) { |
1326 | case SYNTH_TYPE_FM: |
1327 | osi.synth_type = OSS_SYNTH_TYPE_FM; break; |
1328 | case SYNTH_TYPE_SAMPLE: |
1329 | osi.synth_type = OSS_SYNTH_TYPE_SAMPLE; break; |
1330 | case SYNTH_TYPE_MIDI: |
1331 | osi.synth_type = OSS_SYNTH_TYPE_MIDI; break; |
1332 | default: |
1333 | osi.synth_type = 0; break; |
1334 | } |
1335 | switch(si.synth_subtype) { |
1336 | case SYNTH_SUB_FM_TYPE_ADLIB: |
1337 | osi.synth_subtype = OSS_FM_TYPE_ADLIB; break; |
1338 | case SYNTH_SUB_FM_TYPE_OPL3: |
1339 | osi.synth_subtype = OSS_FM_TYPE_OPL3; break; |
1340 | case SYNTH_SUB_MIDI_TYPE_MPU401: |
1341 | osi.synth_subtype = OSS_MIDI_TYPE_MPU401; break; |
1342 | case SYNTH_SUB_SAMPLE_TYPE_BASIC: |
1343 | osi.synth_subtype = OSS_SAMPLE_TYPE_BASIC; break; |
1344 | default: |
1345 | osi.synth_subtype = 0; break; |
1346 | } |
1347 | osi.perc_mode = 0; |
1348 | osi.nr_voices = si.nr_voices; |
1349 | osi.nr_drums = 0; |
1350 | osi.instr_bank_size = si.instr_bank_size; |
1351 | osi.capabilities = 0; |
1352 | if (si.capabilities & SYNTH_CAP_OPL3) |
1353 | osi.capabilities |= OSS_SYNTH_CAP_OPL3; |
1354 | if (si.capabilities & SYNTH_CAP_INPUT) |
1355 | osi.capabilities |= OSS_SYNTH_CAP_INPUT; |
1356 | error = copyout(&osi, SCARG(uap, data), sizeof osi); |
1357 | goto out; |
1358 | case OSS_SEQ_CTRLRATE: |
1359 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
1360 | if (error) |
1361 | goto out; |
1362 | error = ioctlf(fp, SEQUENCER_CTRLRATE, &idat); |
1363 | if (error) |
1364 | goto out; |
1365 | retval[0] = idat; |
1366 | break; |
1367 | case OSS_SEQ_GETOUTCOUNT: |
1368 | error = ioctlf(fp, SEQUENCER_GETOUTCOUNT, &idat); |
1369 | if (error) |
1370 | goto out; |
1371 | retval[0] = idat; |
1372 | break; |
1373 | case OSS_SEQ_GETINCOUNT: |
1374 | error = ioctlf(fp, SEQUENCER_GETINCOUNT, &idat); |
1375 | if (error) |
1376 | goto out; |
1377 | retval[0] = idat; |
1378 | break; |
1379 | case OSS_SEQ_NRSYNTHS: |
1380 | error = ioctlf(fp, SEQUENCER_NRSYNTHS, &idat); |
1381 | if (error) |
1382 | goto out; |
1383 | retval[0] = idat; |
1384 | break; |
1385 | case OSS_SEQ_NRMIDIS: |
1386 | error = ioctlf(fp, SEQUENCER_NRMIDIS, &idat); |
1387 | if (error) |
1388 | goto out; |
1389 | retval[0] = idat; |
1390 | break; |
1391 | case OSS_SEQ_THRESHOLD: |
1392 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
1393 | if (error) |
1394 | goto out; |
1395 | error = ioctlf(fp, SEQUENCER_THRESHOLD, &idat); |
1396 | goto out; |
1397 | case OSS_MEMAVL: |
1398 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
1399 | if (error) |
1400 | goto out; |
1401 | error = ioctlf(fp, SEQUENCER_MEMAVL, &idat); |
1402 | if (error) |
1403 | goto out; |
1404 | retval[0] = idat; |
1405 | break; |
1406 | case OSS_SEQ_PANIC: |
1407 | error = ioctlf(fp, SEQUENCER_PANIC, &idat); |
1408 | goto out; |
1409 | case OSS_SEQ_OUTOFBAND: |
1410 | error = copyin(SCARG(uap, data), &oser, sizeof oser); |
1411 | if (error) |
1412 | goto out; |
1413 | error = ioctlf(fp, SEQUENCER_OUTOFBAND, &oser); |
1414 | if (error) |
1415 | goto out; |
1416 | break; |
1417 | case OSS_SEQ_GETTIME: |
1418 | error = ioctlf(fp, SEQUENCER_GETTIME, &idat); |
1419 | if (error) |
1420 | goto out; |
1421 | retval[0] = idat; |
1422 | break; |
1423 | case OSS_TMR_TIMEBASE: |
1424 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
1425 | if (error) |
1426 | goto out; |
1427 | error = ioctlf(fp, SEQUENCER_TMR_TIMEBASE, &idat); |
1428 | if (error) |
1429 | goto out; |
1430 | retval[0] = idat; |
1431 | break; |
1432 | case OSS_TMR_START: |
1433 | error = ioctlf(fp, SEQUENCER_TMR_START, &idat); |
1434 | goto out; |
1435 | case OSS_TMR_STOP: |
1436 | error = ioctlf(fp, SEQUENCER_TMR_STOP, &idat); |
1437 | goto out; |
1438 | case OSS_TMR_CONTINUE: |
1439 | error = ioctlf(fp, SEQUENCER_TMR_CONTINUE, &idat); |
1440 | goto out; |
1441 | case OSS_TMR_TEMPO: |
1442 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
1443 | if (error) |
1444 | goto out; |
1445 | error = ioctlf(fp, SEQUENCER_TMR_TEMPO, &idat); |
1446 | if (error) |
1447 | goto out; |
1448 | retval[0] = idat; |
1449 | break; |
1450 | case OSS_TMR_SOURCE: |
1451 | error = copyin(SCARG(uap, data), &idat1, sizeof idat); |
1452 | if (error) |
1453 | goto out; |
1454 | idat = 0; |
1455 | if (idat1 & OSS_TMR_INTERNAL) idat |= SEQUENCER_TMR_INTERNAL; |
1456 | error = ioctlf(fp, SEQUENCER_TMR_SOURCE, &idat); |
1457 | if (error) |
1458 | goto out; |
1459 | idat1 = idat; |
1460 | if (idat1 & SEQUENCER_TMR_INTERNAL) idat |= OSS_TMR_INTERNAL; |
1461 | retval[0] = idat; |
1462 | break; |
1463 | case OSS_TMR_METRONOME: |
1464 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
1465 | if (error) |
1466 | goto out; |
1467 | error = ioctlf(fp, SEQUENCER_TMR_METRONOME, &idat); |
1468 | goto out; |
1469 | case OSS_TMR_SELECT: |
1470 | error = copyin(SCARG(uap, data), &idat, sizeof idat); |
1471 | if (error) |
1472 | goto out; |
1473 | retval[0] = idat; |
1474 | error = ioctlf(fp, SEQUENCER_TMR_SELECT, &idat); |
1475 | goto out; |
1476 | default: |
1477 | DPRINTF(("%s: Unknown sequencer command 0x%lx\n" , __func__, |
1478 | com)); |
1479 | error = EINVAL; |
1480 | goto out; |
1481 | } |
1482 | |
1483 | error = copyout(&idat, SCARG(uap, data), sizeof idat); |
1484 | out: |
1485 | fd_putfile(SCARG(uap, fd)); |
1486 | return error; |
1487 | } |
1488 | |
1489 | /* |
1490 | * Check that the blocksize is a power of 2 as OSS wants. |
1491 | * If not, set it to be. |
1492 | */ |
1493 | static void |
1494 | setblocksize(file_t *fp, struct audio_info *info) |
1495 | { |
1496 | struct audio_info set; |
1497 | int s; |
1498 | |
1499 | if (info->blocksize & (info->blocksize-1)) { |
1500 | for(s = 32; s < info->blocksize; s <<= 1) |
1501 | ; |
1502 | AUDIO_INITINFO(&set); |
1503 | set.blocksize = s; |
1504 | fp->f_ops->fo_ioctl(fp, AUDIO_SETINFO, &set); |
1505 | fp->f_ops->fo_ioctl(fp, AUDIO_GETBUFINFO, info); |
1506 | } |
1507 | } |
1508 | |