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