oss_dsp.c revision 1.1 1 /* $NetBSD: oss_dsp.c,v 1.1 2021/06/08 18:43:54 nia Exp $ */
2
3 /*-
4 * Copyright (c) 1997-2021 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 __RCSID("$NetBSD: oss_dsp.c,v 1.1 2021/06/08 18:43:54 nia Exp $");
31
32 #include <sys/audioio.h>
33 #include <stdbool.h>
34 #include <errno.h>
35 #include "internal.h"
36
37 #define GETPRINFO(info, name) \
38 (((info)->mode == AUMODE_RECORD) \
39 ? (info)->record.name : (info)->play.name)
40
41 static int get_vol(u_int, u_char);
42 static void set_vol(int, int, bool);
43
44 static void set_channels(int, int, int);
45
46 oss_private int
47 _oss_dsp_ioctl(int fd, unsigned long com, void *argp)
48 {
49
50 struct audio_info tmpinfo, hwfmt;
51 struct audio_offset tmpoffs;
52 struct audio_buf_info bufinfo;
53 struct audio_errinfo *tmperrinfo;
54 struct count_info cntinfo;
55 struct audio_encoding tmpenc;
56 u_int u;
57 u_int encoding;
58 u_int precision;
59 int perrors, rerrors;
60 static int totalperrors = 0;
61 static int totalrerrors = 0;
62 oss_mixer_enuminfo *ei;
63 oss_count_t osscount;
64 int idat;
65 int retval;
66
67 idat = 0;
68
69 switch (com) {
70 case SNDCTL_DSP_HALT_INPUT:
71 case SNDCTL_DSP_HALT_OUTPUT:
72 case SNDCTL_DSP_RESET:
73 retval = ioctl(fd, AUDIO_FLUSH, 0);
74 if (retval < 0)
75 return retval;
76 break;
77 case SNDCTL_DSP_SYNC:
78 retval = ioctl(fd, AUDIO_DRAIN, 0);
79 if (retval < 0)
80 return retval;
81 break;
82 case SNDCTL_DSP_GETERROR:
83 tmperrinfo = (struct audio_errinfo *)argp;
84 if (tmperrinfo == NULL) {
85 errno = EINVAL;
86 return -1;
87 }
88 memset(tmperrinfo, 0, sizeof(struct audio_errinfo));
89 if ((retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo)) < 0)
90 return retval;
91 /*
92 * OSS requires that we return counters that are relative to
93 * the last call. We must maintain state here...
94 */
95 if (ioctl(fd, AUDIO_PERROR, &perrors) != -1) {
96 perrors /= ((tmpinfo.play.precision / NBBY) *
97 tmpinfo.play.channels);
98 tmperrinfo->play_underruns =
99 (perrors / tmpinfo.blocksize) - totalperrors;
100 totalperrors += tmperrinfo->play_underruns;
101 }
102 if (ioctl(fd, AUDIO_RERROR, &rerrors) != -1) {
103 rerrors /= ((tmpinfo.record.precision / NBBY) *
104 tmpinfo.record.channels);
105 tmperrinfo->rec_overruns =
106 (rerrors / tmpinfo.blocksize) - totalrerrors;
107 totalrerrors += tmperrinfo->rec_overruns;
108 }
109 break;
110 case SNDCTL_DSP_COOKEDMODE:
111 /*
112 * NetBSD is always running in "cooked mode" - the kernel
113 * always performs format conversions.
114 */
115 INTARG = 1;
116 break;
117 case SNDCTL_DSP_POST:
118 /* This call is merely advisory, and may be a nop. */
119 break;
120 case SNDCTL_DSP_SPEED:
121 /*
122 * In Solaris, 0 is used a special value to query the
123 * current rate. This seems useful to support.
124 */
125 if (INTARG == 0) {
126 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
127 if (retval < 0)
128 return retval;
129 retval = ioctl(fd, AUDIO_GETFORMAT, &hwfmt);
130 if (retval < 0)
131 return retval;
132 INTARG = (tmpinfo.mode == AUMODE_RECORD) ?
133 hwfmt.record.sample_rate :
134 hwfmt.play.sample_rate;
135 }
136 /*
137 * Conform to kernel limits.
138 * NetBSD will reject unsupported sample rates, but OSS
139 * applications need to be able to negotiate a supported one.
140 */
141 if (INTARG < 1000)
142 INTARG = 1000;
143 if (INTARG > 192000)
144 INTARG = 192000;
145 AUDIO_INITINFO(&tmpinfo);
146 tmpinfo.play.sample_rate =
147 tmpinfo.record.sample_rate = INTARG;
148 retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo);
149 if (retval < 0)
150 return retval;
151 /* FALLTHRU */
152 case SOUND_PCM_READ_RATE:
153 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
154 if (retval < 0)
155 return retval;
156 INTARG = GETPRINFO(&tmpinfo, sample_rate);
157 break;
158 case SNDCTL_DSP_STEREO:
159 AUDIO_INITINFO(&tmpinfo);
160 tmpinfo.play.channels =
161 tmpinfo.record.channels = INTARG ? 2 : 1;
162 (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
163 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
164 if (retval < 0)
165 return retval;
166 INTARG = GETPRINFO(&tmpinfo, channels) - 1;
167 break;
168 case SNDCTL_DSP_GETBLKSIZE:
169 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
170 if (retval < 0)
171 return retval;
172 INTARG = tmpinfo.blocksize;
173 break;
174 case SNDCTL_DSP_SETFMT:
175 AUDIO_INITINFO(&tmpinfo);
176 switch (INTARG) {
177 case AFMT_MU_LAW:
178 tmpinfo.play.precision =
179 tmpinfo.record.precision = 8;
180 tmpinfo.play.encoding =
181 tmpinfo.record.encoding = AUDIO_ENCODING_ULAW;
182 break;
183 case AFMT_A_LAW:
184 tmpinfo.play.precision =
185 tmpinfo.record.precision = 8;
186 tmpinfo.play.encoding =
187 tmpinfo.record.encoding = AUDIO_ENCODING_ALAW;
188 break;
189 case AFMT_U8:
190 tmpinfo.play.precision =
191 tmpinfo.record.precision = 8;
192 tmpinfo.play.encoding =
193 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR;
194 break;
195 case AFMT_S8:
196 tmpinfo.play.precision =
197 tmpinfo.record.precision = 8;
198 tmpinfo.play.encoding =
199 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR;
200 break;
201 case AFMT_S16_LE:
202 tmpinfo.play.precision =
203 tmpinfo.record.precision = 16;
204 tmpinfo.play.encoding =
205 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE;
206 break;
207 case AFMT_S16_BE:
208 tmpinfo.play.precision =
209 tmpinfo.record.precision = 16;
210 tmpinfo.play.encoding =
211 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE;
212 break;
213 case AFMT_U16_LE:
214 tmpinfo.play.precision =
215 tmpinfo.record.precision = 16;
216 tmpinfo.play.encoding =
217 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE;
218 break;
219 case AFMT_U16_BE:
220 tmpinfo.play.precision =
221 tmpinfo.record.precision = 16;
222 tmpinfo.play.encoding =
223 tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE;
224 break;
225 /*
226 * XXX: When the kernel supports 24-bit LPCM by default,
227 * the 24-bit formats should be handled properly instead
228 * of falling back to 32 bits.
229 */
230 case AFMT_S24_PACKED:
231 case AFMT_S24_LE:
232 case AFMT_S32_LE:
233 tmpinfo.play.precision =
234 tmpinfo.record.precision = 32;
235 tmpinfo.play.encoding =
236 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE;
237 break;
238 case AFMT_S24_BE:
239 case AFMT_S32_BE:
240 tmpinfo.play.precision =
241 tmpinfo.record.precision = 32;
242 tmpinfo.play.encoding =
243 tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE;
244 break;
245 case AFMT_AC3:
246 tmpinfo.play.precision =
247 tmpinfo.record.precision = 16;
248 tmpinfo.play.encoding =
249 tmpinfo.record.encoding = AUDIO_ENCODING_AC3;
250 break;
251 default:
252 /*
253 * OSSv4 specifies that if an invalid format is chosen
254 * by an application then a sensible format supported
255 * by the hardware is returned.
256 *
257 * In this case, we pick the current hardware format.
258 */
259 retval = ioctl(fd, AUDIO_GETFORMAT, &hwfmt);
260 if (retval < 0)
261 return retval;
262 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
263 if (retval < 0)
264 return retval;
265 tmpinfo.play.encoding =
266 tmpinfo.record.encoding =
267 (tmpinfo.mode == AUMODE_RECORD) ?
268 hwfmt.record.encoding : hwfmt.play.encoding;
269 tmpinfo.play.precision =
270 tmpinfo.record.precision =
271 (tmpinfo.mode == AUMODE_RECORD) ?
272 hwfmt.record.precision : hwfmt.play.precision ;
273 break;
274 }
275 /*
276 * In the post-kernel-mixer world, assume that any error means
277 * it's fatal rather than an unsupported format being selected.
278 */
279 retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo);
280 if (retval < 0)
281 return retval;
282 /* FALLTHRU */
283 case SOUND_PCM_READ_BITS:
284 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
285 if (retval < 0)
286 return retval;
287 encoding = GETPRINFO(&tmpinfo, encoding);
288 precision = GETPRINFO(&tmpinfo, precision);
289 switch (encoding) {
290 case AUDIO_ENCODING_ULAW:
291 idat = AFMT_MU_LAW;
292 break;
293 case AUDIO_ENCODING_ALAW:
294 idat = AFMT_A_LAW;
295 break;
296 case AUDIO_ENCODING_SLINEAR_LE:
297 if (precision == 32)
298 idat = AFMT_S32_LE;
299 else if (precision == 24)
300 idat = AFMT_S24_LE;
301 else if (precision == 16)
302 idat = AFMT_S16_LE;
303 else
304 idat = AFMT_S8;
305 break;
306 case AUDIO_ENCODING_SLINEAR_BE:
307 if (precision == 32)
308 idat = AFMT_S32_BE;
309 else if (precision == 24)
310 idat = AFMT_S24_BE;
311 else if (precision == 16)
312 idat = AFMT_S16_BE;
313 else
314 idat = AFMT_S8;
315 break;
316 case AUDIO_ENCODING_ULINEAR_LE:
317 if (precision == 16)
318 idat = AFMT_U16_LE;
319 else
320 idat = AFMT_U8;
321 break;
322 case AUDIO_ENCODING_ULINEAR_BE:
323 if (precision == 16)
324 idat = AFMT_U16_BE;
325 else
326 idat = AFMT_U8;
327 break;
328 case AUDIO_ENCODING_ADPCM:
329 idat = AFMT_IMA_ADPCM;
330 break;
331 case AUDIO_ENCODING_AC3:
332 idat = AFMT_AC3;
333 break;
334 }
335 INTARG = idat;
336 break;
337 case SNDCTL_DSP_CHANNELS:
338 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
339 if (retval < 0)
340 return retval;
341 set_channels(fd, tmpinfo.mode, INTARG);
342 /* FALLTHRU */
343 case SOUND_PCM_READ_CHANNELS:
344 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
345 if (retval < 0)
346 return retval;
347 INTARG = GETPRINFO(&tmpinfo, channels);
348 break;
349 case SOUND_PCM_WRITE_FILTER:
350 case SOUND_PCM_READ_FILTER:
351 errno = EINVAL;
352 return -1; /* XXX unimplemented */
353 case SNDCTL_DSP_SUBDIVIDE:
354 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
355 if (retval < 0)
356 return retval;
357 idat = INTARG;
358 if (idat == 0)
359 idat = tmpinfo.play.buffer_size / tmpinfo.blocksize;
360 idat = (tmpinfo.play.buffer_size / idat) & -4;
361 AUDIO_INITINFO(&tmpinfo);
362 tmpinfo.blocksize = idat;
363 retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo);
364 if (retval < 0)
365 return retval;
366 INTARG = tmpinfo.play.buffer_size / tmpinfo.blocksize;
367 break;
368 case SNDCTL_DSP_SETFRAGMENT:
369 AUDIO_INITINFO(&tmpinfo);
370 idat = INTARG;
371 tmpinfo.blocksize = 1 << (idat & 0xffff);
372 tmpinfo.hiwat = ((unsigned)idat >> 16) & 0x7fff;
373 if (tmpinfo.hiwat == 0) /* 0 means set to max */
374 tmpinfo.hiwat = 65536;
375 (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo);
376 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
377 if (retval < 0)
378 return retval;
379 u = tmpinfo.blocksize;
380 for(idat = 0; u > 1; idat++, u >>= 1)
381 ;
382 idat |= (tmpinfo.hiwat & 0x7fff) << 16;
383 INTARG = idat;
384 break;
385 case SNDCTL_DSP_GETFMTS:
386 for(idat = 0, tmpenc.index = 0;
387 ioctl(fd, AUDIO_GETENC, &tmpenc) == 0;
388 tmpenc.index++) {
389 switch(tmpenc.encoding) {
390 case AUDIO_ENCODING_ULAW:
391 idat |= AFMT_MU_LAW;
392 break;
393 case AUDIO_ENCODING_ALAW:
394 idat |= AFMT_A_LAW;
395 break;
396 case AUDIO_ENCODING_SLINEAR:
397 idat |= AFMT_S8;
398 break;
399 case AUDIO_ENCODING_SLINEAR_LE:
400 if (tmpenc.precision == 32)
401 idat |= AFMT_S32_LE;
402 else if (tmpenc.precision == 24)
403 idat |= AFMT_S24_LE;
404 else if (tmpenc.precision == 16)
405 idat |= AFMT_S16_LE;
406 else
407 idat |= AFMT_S8;
408 break;
409 case AUDIO_ENCODING_SLINEAR_BE:
410 if (tmpenc.precision == 32)
411 idat |= AFMT_S32_BE;
412 else if (tmpenc.precision == 24)
413 idat |= AFMT_S24_BE;
414 else if (tmpenc.precision == 16)
415 idat |= AFMT_S16_BE;
416 else
417 idat |= AFMT_S8;
418 break;
419 case AUDIO_ENCODING_ULINEAR:
420 idat |= AFMT_U8;
421 break;
422 case AUDIO_ENCODING_ULINEAR_LE:
423 if (tmpenc.precision == 16)
424 idat |= AFMT_U16_LE;
425 else
426 idat |= AFMT_U8;
427 break;
428 case AUDIO_ENCODING_ULINEAR_BE:
429 if (tmpenc.precision == 16)
430 idat |= AFMT_U16_BE;
431 else
432 idat |= AFMT_U8;
433 break;
434 case AUDIO_ENCODING_ADPCM:
435 idat |= AFMT_IMA_ADPCM;
436 break;
437 case AUDIO_ENCODING_AC3:
438 idat |= AFMT_AC3;
439 break;
440 default:
441 break;
442 }
443 }
444 INTARG = idat;
445 break;
446 case SNDCTL_DSP_GETOSPACE:
447 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
448 if (retval < 0)
449 return retval;
450 bufinfo.fragsize = tmpinfo.blocksize;
451 bufinfo.fragments = tmpinfo.hiwat - (tmpinfo.play.seek
452 + tmpinfo.blocksize - 1) / tmpinfo.blocksize;
453 bufinfo.fragstotal = tmpinfo.hiwat;
454 bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize
455 - tmpinfo.play.seek;
456 *(struct audio_buf_info *)argp = bufinfo;
457 break;
458 case SNDCTL_DSP_GETISPACE:
459 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
460 if (retval < 0)
461 return retval;
462 bufinfo.fragsize = tmpinfo.blocksize;
463 bufinfo.fragments = tmpinfo.record.seek / tmpinfo.blocksize;
464 bufinfo.fragstotal =
465 tmpinfo.record.buffer_size / tmpinfo.blocksize;
466 bufinfo.bytes = tmpinfo.record.seek;
467 *(struct audio_buf_info *)argp = bufinfo;
468 break;
469 case SNDCTL_DSP_NONBLOCK:
470 idat = 1;
471 retval = ioctl(fd, FIONBIO, &idat);
472 if (retval < 0)
473 return retval;
474 break;
475 case SNDCTL_DSP_GETCAPS:
476 retval = _oss_get_caps(fd, (int *)argp);
477 if (retval < 0)
478 return retval;
479 break;
480 case SNDCTL_DSP_SETTRIGGER:
481 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
482 if (retval < 0)
483 return retval;
484 AUDIO_INITINFO(&tmpinfo);
485 if (tmpinfo.mode & AUMODE_PLAY)
486 tmpinfo.play.pause = (INTARG & PCM_ENABLE_OUTPUT) == 0;
487 if (tmpinfo.mode & AUMODE_RECORD)
488 tmpinfo.record.pause = (INTARG & PCM_ENABLE_INPUT) == 0;
489 (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo);
490 /* FALLTHRU */
491 case SNDCTL_DSP_GETTRIGGER:
492 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
493 if (retval < 0)
494 return retval;
495 idat = 0;
496 if ((tmpinfo.mode & AUMODE_PLAY) && !tmpinfo.play.pause)
497 idat |= PCM_ENABLE_OUTPUT;
498 if ((tmpinfo.mode & AUMODE_RECORD) && !tmpinfo.record.pause)
499 idat |= PCM_ENABLE_INPUT;
500 INTARG = idat;
501 break;
502 case SNDCTL_DSP_GETIPTR:
503 retval = ioctl(fd, AUDIO_GETIOFFS, &tmpoffs);
504 if (retval < 0)
505 return retval;
506 cntinfo.bytes = tmpoffs.samples;
507 cntinfo.blocks = tmpoffs.deltablks;
508 cntinfo.ptr = tmpoffs.offset;
509 *(struct count_info *)argp = cntinfo;
510 break;
511 case SNDCTL_DSP_CURRENT_IPTR:
512 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
513 if (retval < 0)
514 return retval;
515 /* XXX: 'samples' may wrap */
516 memset(osscount.filler, 0, sizeof(osscount.filler));
517 osscount.samples = tmpinfo.record.samples /
518 ((tmpinfo.record.precision / NBBY) *
519 tmpinfo.record.channels);
520 osscount.fifo_samples = tmpinfo.record.seek /
521 ((tmpinfo.record.precision / NBBY) *
522 tmpinfo.record.channels);
523 *(oss_count_t *)argp = osscount;
524 break;
525 case SNDCTL_DSP_GETOPTR:
526 retval = ioctl(fd, AUDIO_GETOOFFS, &tmpoffs);
527 if (retval < 0)
528 return retval;
529 cntinfo.bytes = tmpoffs.samples;
530 cntinfo.blocks = tmpoffs.deltablks;
531 cntinfo.ptr = tmpoffs.offset;
532 *(struct count_info *)argp = cntinfo;
533 break;
534 case SNDCTL_DSP_CURRENT_OPTR:
535 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
536 if (retval < 0)
537 return retval;
538 /* XXX: 'samples' may wrap */
539 memset(osscount.filler, 0, sizeof(osscount.filler));
540 osscount.samples = tmpinfo.play.samples /
541 ((tmpinfo.play.precision / NBBY) *
542 tmpinfo.play.channels);
543 osscount.fifo_samples = tmpinfo.play.seek /
544 ((tmpinfo.play.precision / NBBY) *
545 tmpinfo.play.channels);
546 *(oss_count_t *)argp = osscount;
547 break;
548 case SNDCTL_DSP_SETPLAYVOL:
549 set_vol(fd, INTARG, false);
550 /* FALLTHRU */
551 case SNDCTL_DSP_GETPLAYVOL:
552 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
553 if (retval < 0)
554 return retval;
555 INTARG = get_vol(tmpinfo.play.gain, tmpinfo.play.balance);
556 break;
557 case SNDCTL_DSP_SETRECVOL:
558 set_vol(fd, INTARG, true);
559 /* FALLTHRU */
560 case SNDCTL_DSP_GETRECVOL:
561 retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo);
562 if (retval < 0)
563 return retval;
564 INTARG = get_vol(tmpinfo.record.gain, tmpinfo.record.balance);
565 break;
566 case SNDCTL_DSP_SKIP:
567 case SNDCTL_DSP_SILENCE:
568 errno = EINVAL;
569 return -1;
570 case SNDCTL_DSP_SETDUPLEX:
571 idat = 1;
572 retval = ioctl(fd, AUDIO_SETFD, &idat);
573 if (retval < 0)
574 return retval;
575 break;
576 case SNDCTL_DSP_GETODELAY:
577 retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo);
578 if (retval < 0)
579 return retval;
580 idat = tmpinfo.play.seek + tmpinfo.blocksize / 2;
581 INTARG = idat;
582 break;
583 case SNDCTL_DSP_PROFILE:
584 /* This gives just a hint to the driver,
585 * implementing it as a NOP is ok
586 */
587 break;
588 case SNDCTL_DSP_MAPINBUF:
589 case SNDCTL_DSP_MAPOUTBUF:
590 case SNDCTL_DSP_SETSYNCRO:
591 errno = EINVAL;
592 return -1; /* XXX unimplemented */
593 case SNDCTL_DSP_GET_PLAYTGT_NAMES:
594 case SNDCTL_DSP_GET_RECSRC_NAMES:
595 ei = (oss_mixer_enuminfo *)argp;
596 ei->nvalues = 1;
597 ei->version = 0;
598 ei->strindex[0] = 0;
599 strlcpy(ei->strings, "primary", OSS_ENUM_STRINGSIZE);
600 break;
601 case SNDCTL_DSP_SET_PLAYTGT:
602 case SNDCTL_DSP_SET_RECSRC:
603 case SNDCTL_DSP_GET_PLAYTGT:
604 case SNDCTL_DSP_GET_RECSRC:
605 /* We have one recording source and play target. */
606 INTARG = 0;
607 break;
608 default:
609 errno = EINVAL;
610 return -1;
611 }
612
613 return 0;
614 }
615
616 static int
617 get_vol(u_int gain, u_char balance)
618 {
619 u_int l, r;
620
621 if (balance == AUDIO_MID_BALANCE) {
622 l = r = gain;
623 } else if (balance < AUDIO_MID_BALANCE) {
624 l = gain;
625 r = (balance * gain) / AUDIO_MID_BALANCE;
626 } else {
627 r = gain;
628 l = ((AUDIO_RIGHT_BALANCE - balance) * gain)
629 / AUDIO_MID_BALANCE;
630 }
631
632 return TO_OSSVOL(l) | (TO_OSSVOL(r) << 8);
633 }
634
635 static void
636 set_vol(int fd, int volume, bool record)
637 {
638 u_int lgain, rgain;
639 struct audio_info tmpinfo;
640 struct audio_prinfo *prinfo;
641
642 AUDIO_INITINFO(&tmpinfo);
643 prinfo = record ? &tmpinfo.record : &tmpinfo.play;
644
645 lgain = FROM_OSSVOL((volume >> 0) & 0xff);
646 rgain = FROM_OSSVOL((volume >> 8) & 0xff);
647
648 if (lgain == rgain) {
649 prinfo->gain = lgain;
650 prinfo->balance = AUDIO_MID_BALANCE;
651 } else if (lgain < rgain) {
652 prinfo->gain = rgain;
653 prinfo->balance = AUDIO_RIGHT_BALANCE -
654 (AUDIO_MID_BALANCE * lgain) / rgain;
655 } else {
656 prinfo->gain = lgain;
657 prinfo->balance = (AUDIO_MID_BALANCE * rgain) / lgain;
658 }
659
660 (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo);
661 }
662
663 /*
664 * When AUDIO_SETINFO fails to set a channel count, the application's chosen
665 * number is out of range of what the kernel allows.
666 *
667 * When this happens, we use the current hardware settings. This is just in
668 * case an application is abusing SNDCTL_DSP_CHANNELS - OSSv4 always sets and
669 * returns a reasonable value, even if it wasn't what the user requested.
670 *
671 * Solaris guarantees this behaviour if nchannels = 0.
672 *
673 * XXX: If a device is opened for both playback and recording, and supports
674 * fewer channels for recording than playback, applications that do both will
675 * behave very strangely. OSS doesn't allow for reporting separate channel
676 * counts for recording and playback. This could be worked around by always
677 * mixing recorded data up to the same number of channels as is being used
678 * for playback.
679 */
680 static void
681 set_channels(int fd, int mode, int nchannels)
682 {
683 struct audio_info tmpinfo, hwfmt;
684
685 if (ioctl(fd, AUDIO_GETFORMAT, &hwfmt) < 0) {
686 errno = 0;
687 hwfmt.record.channels = hwfmt.play.channels = 2;
688 }
689
690 if (mode & AUMODE_PLAY) {
691 AUDIO_INITINFO(&tmpinfo);
692 tmpinfo.play.channels = nchannels;
693 if (ioctl(fd, AUDIO_SETINFO, &tmpinfo) < 0) {
694 errno = 0;
695 AUDIO_INITINFO(&tmpinfo);
696 tmpinfo.play.channels = hwfmt.play.channels;
697 (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo);
698 }
699 }
700
701 if (mode & AUMODE_RECORD) {
702 AUDIO_INITINFO(&tmpinfo);
703 tmpinfo.record.channels = nchannels;
704 if (ioctl(fd, AUDIO_SETINFO, &tmpinfo) < 0) {
705 errno = 0;
706 AUDIO_INITINFO(&tmpinfo);
707 tmpinfo.record.channels = hwfmt.record.channels;
708 (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo);
709 }
710 }
711 }
712