| @@ -1,1790 +1,1795 @@ | | | @@ -1,1790 +1,1795 @@ |
1 | /* $NetBSD: ossaudio.c,v 1.65 2020/12/19 12:55:28 nia Exp $ */ | | 1 | /* $NetBSD: ossaudio.c,v 1.66 2021/03/15 10:58:05 nia Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 1997, 2020 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 1997, 2020 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * Redistribution and use in source and binary forms, with or without | | 7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | | 8 | * modification, are permitted provided that the following conditions |
9 | * are met: | | 9 | * are met: |
10 | * 1. Redistributions of source code must retain the above copyright | | 10 | * 1. Redistributions of source code must retain the above copyright |
11 | * notice, this list of conditions and the following disclaimer. | | 11 | * notice, this list of conditions and the following disclaimer. |
12 | * 2. Redistributions in binary form must reproduce the above copyright | | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
13 | * notice, this list of conditions and the following disclaimer in the | | 13 | * notice, this list of conditions and the following disclaimer in the |
14 | * documentation and/or other materials provided with the distribution. | | 14 | * documentation and/or other materials provided with the distribution. |
15 | * | | 15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | | 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 | | 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 | | 18 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | | 19 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
20 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 20 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 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 | | 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 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 | | 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
26 | * POSSIBILITY OF SUCH DAMAGE. | | 26 | * POSSIBILITY OF SUCH DAMAGE. |
27 | */ | | 27 | */ |
28 | | | 28 | |
29 | #include <sys/cdefs.h> | | 29 | #include <sys/cdefs.h> |
30 | __RCSID("$NetBSD: ossaudio.c,v 1.65 2020/12/19 12:55:28 nia Exp $"); | | 30 | __RCSID("$NetBSD: ossaudio.c,v 1.66 2021/03/15 10:58:05 nia Exp $"); |
31 | | | 31 | |
32 | /* | | 32 | /* |
33 | * This is an Open Sound System compatibility layer, which provides | | 33 | * This is an Open Sound System compatibility layer, which provides |
34 | * fairly complete ioctl emulation for OSSv3 and some of OSSv4. | | 34 | * fairly complete ioctl emulation for OSSv3 and some of OSSv4. |
35 | * | | 35 | * |
36 | * The canonical OSS specification is available at | | 36 | * The canonical OSS specification is available at |
37 | * http://manuals.opensound.com/developer/ | | 37 | * http://manuals.opensound.com/developer/ |
38 | * | | 38 | * |
39 | * This file is similar to sys/compat/ossaudio.c with additional OSSv4 | | 39 | * This file is similar to sys/compat/ossaudio.c with additional OSSv4 |
40 | * compatibility. | | 40 | * compatibility. |
41 | */ | | 41 | */ |
42 | | | 42 | |
43 | #include <string.h> | | 43 | #include <string.h> |
44 | #include <sys/types.h> | | 44 | #include <sys/types.h> |
45 | #include <sys/ioctl.h> | | 45 | #include <sys/ioctl.h> |
46 | #include <sys/audioio.h> | | 46 | #include <sys/audioio.h> |
47 | #include <sys/stat.h> | | 47 | #include <sys/stat.h> |
48 | #include <errno.h> | | 48 | #include <errno.h> |
49 | #include <fcntl.h> | | 49 | #include <fcntl.h> |
50 | #include <stdio.h> | | 50 | #include <stdio.h> |
51 | #include <unistd.h> | | 51 | #include <unistd.h> |
52 | #include <limits.h> | | 52 | #include <limits.h> |
53 | #include <stdarg.h> | | 53 | #include <stdarg.h> |
54 | #include <stdbool.h> | | 54 | #include <stdbool.h> |
55 | | | 55 | |
56 | #include "soundcard.h" | | 56 | #include "soundcard.h" |
57 | #undef ioctl | | 57 | #undef ioctl |
58 | | | 58 | |
59 | #define GET_DEV(com) ((com) & 0xff) | | 59 | #define GET_DEV(com) ((com) & 0xff) |
60 | | | 60 | |
61 | #define TO_OSSVOL(x) (((x) * 100 + 127) / 255) | | 61 | #define TO_OSSVOL(x) (((x) * 100 + 127) / 255) |
62 | #define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100) | | 62 | #define FROM_OSSVOL(x) ((((x) > 100 ? 100 : (x)) * 255 + 50) / 100) |
63 | | | 63 | |
64 | #define GETPRINFO(info, name) \ | | 64 | #define GETPRINFO(info, name) \ |
65 | (((info)->mode == AUMODE_RECORD) \ | | 65 | (((info)->mode == AUMODE_RECORD) \ |
66 | ? (info)->record.name : (info)->play.name) | | 66 | ? (info)->record.name : (info)->play.name) |
67 | | | 67 | |
68 | static struct audiodevinfo *getdevinfo(int); | | 68 | static struct audiodevinfo *getdevinfo(int); |
69 | | | 69 | |
70 | static int getaudiocount(void); | | 70 | static int getaudiocount(void); |
71 | static int getmixercount(void); | | 71 | static int getmixercount(void); |
72 | static int getmixercontrolcount(int); | | 72 | static int getmixercontrolcount(int); |
73 | | | 73 | |
74 | static int getcaps(int, int *); | | 74 | static int getcaps(int, int *); |
75 | | | 75 | |
76 | static int getvol(u_int, u_char); | | 76 | static int getvol(u_int, u_char); |
77 | static void setvol(int, int, bool); | | 77 | static void setvol(int, int, bool); |
78 | | | 78 | |
79 | static void setchannels(int, int, int); | | 79 | static void setchannels(int, int, int); |
80 | static void setblocksize(int, struct audio_info *); | | 80 | static void setblocksize(int, struct audio_info *); |
81 | | | 81 | |
82 | static int audio_ioctl(int, unsigned long, void *); | | 82 | static int audio_ioctl(int, unsigned long, void *); |
83 | static int mixer_oss3_ioctl(int, unsigned long, void *); | | 83 | static int mixer_oss3_ioctl(int, unsigned long, void *); |
84 | static int mixer_oss4_ioctl(int, unsigned long, void *); | | 84 | static int mixer_oss4_ioctl(int, unsigned long, void *); |
85 | static int global_oss4_ioctl(int, unsigned long, void *); | | 85 | static int global_oss4_ioctl(int, unsigned long, void *); |
86 | static int opaque_to_enum(struct audiodevinfo *, audio_mixer_name_t *, int); | | 86 | static int opaque_to_enum(struct audiodevinfo *, audio_mixer_name_t *, int); |
87 | static int enum_to_ord(struct audiodevinfo *, int); | | 87 | static int enum_to_ord(struct audiodevinfo *, int); |
88 | static int enum_to_mask(struct audiodevinfo *, int); | | 88 | static int enum_to_mask(struct audiodevinfo *, int); |
89 | | | 89 | |
90 | #define INTARG (*(int*)argp) | | 90 | #define INTARG (*(int*)argp) |
91 | | | 91 | |
92 | int | | 92 | int |
93 | _oss_ioctl(int fd, unsigned long com, ...) | | 93 | _oss_ioctl(int fd, unsigned long com, ...) |
94 | { | | 94 | { |
95 | va_list ap; | | 95 | va_list ap; |
96 | void *argp; | | 96 | void *argp; |
97 | | | 97 | |
98 | va_start(ap, com); | | 98 | va_start(ap, com); |
99 | argp = va_arg(ap, void *); | | 99 | argp = va_arg(ap, void *); |
100 | va_end(ap); | | 100 | va_end(ap); |
101 | | | 101 | |
102 | if (IOCGROUP(com) == 'P') | | 102 | if (IOCGROUP(com) == 'P') |
103 | return audio_ioctl(fd, com, argp); | | 103 | return audio_ioctl(fd, com, argp); |
104 | else if (IOCGROUP(com) == 'M') | | 104 | else if (IOCGROUP(com) == 'M') |
105 | return mixer_oss3_ioctl(fd, com, argp); | | 105 | return mixer_oss3_ioctl(fd, com, argp); |
106 | else if (IOCGROUP(com) == 'X') | | 106 | else if (IOCGROUP(com) == 'X') |
107 | return mixer_oss4_ioctl(fd, com, argp); | | 107 | return mixer_oss4_ioctl(fd, com, argp); |
108 | else if (IOCGROUP(com) == 'Y') | | 108 | else if (IOCGROUP(com) == 'Y') |
109 | return global_oss4_ioctl(fd, com, argp); | | 109 | return global_oss4_ioctl(fd, com, argp); |
110 | else | | 110 | else |
111 | return ioctl(fd, com, argp); | | 111 | return ioctl(fd, com, argp); |
112 | } | | 112 | } |
113 | | | 113 | |
114 | static int | | 114 | static int |
115 | audio_ioctl(int fd, unsigned long com, void *argp) | | 115 | audio_ioctl(int fd, unsigned long com, void *argp) |
116 | { | | 116 | { |
117 | | | 117 | |
118 | struct audio_info tmpinfo, hwfmt; | | 118 | struct audio_info tmpinfo, hwfmt; |
119 | struct audio_offset tmpoffs; | | 119 | struct audio_offset tmpoffs; |
120 | struct audio_buf_info bufinfo; | | 120 | struct audio_buf_info bufinfo; |
121 | struct audio_errinfo *tmperrinfo; | | 121 | struct audio_errinfo *tmperrinfo; |
122 | struct count_info cntinfo; | | 122 | struct count_info cntinfo; |
123 | struct audio_encoding tmpenc; | | 123 | struct audio_encoding tmpenc; |
124 | u_int u; | | 124 | u_int u; |
125 | u_int encoding; | | 125 | u_int encoding; |
126 | u_int precision; | | 126 | u_int precision; |
127 | int perrors, rerrors; | | 127 | int perrors, rerrors; |
128 | static int totalperrors = 0; | | 128 | static int totalperrors = 0; |
129 | static int totalrerrors = 0; | | 129 | static int totalrerrors = 0; |
130 | oss_count_t osscount; | | 130 | oss_count_t osscount; |
131 | int idat; | | 131 | int idat; |
132 | int retval; | | 132 | int retval; |
133 | | | 133 | |
134 | idat = 0; | | 134 | idat = 0; |
135 | | | 135 | |
136 | switch (com) { | | 136 | switch (com) { |
137 | case SNDCTL_DSP_RESET: | | 137 | case SNDCTL_DSP_RESET: |
138 | retval = ioctl(fd, AUDIO_FLUSH, 0); | | 138 | retval = ioctl(fd, AUDIO_FLUSH, 0); |
139 | if (retval < 0) | | 139 | if (retval < 0) |
140 | return retval; | | 140 | return retval; |
141 | break; | | 141 | break; |
142 | case SNDCTL_DSP_SYNC: | | 142 | case SNDCTL_DSP_SYNC: |
143 | retval = ioctl(fd, AUDIO_DRAIN, 0); | | 143 | retval = ioctl(fd, AUDIO_DRAIN, 0); |
144 | if (retval < 0) | | 144 | if (retval < 0) |
145 | return retval; | | 145 | return retval; |
146 | break; | | 146 | break; |
147 | case SNDCTL_DSP_GETERROR: | | 147 | case SNDCTL_DSP_GETERROR: |
148 | tmperrinfo = (struct audio_errinfo *)argp; | | 148 | tmperrinfo = (struct audio_errinfo *)argp; |
149 | if (tmperrinfo == NULL) { | | 149 | if (tmperrinfo == NULL) { |
150 | errno = EINVAL; | | 150 | errno = EINVAL; |
151 | return -1; | | 151 | return -1; |
152 | } | | 152 | } |
153 | memset(tmperrinfo, 0, sizeof(struct audio_errinfo)); | | 153 | memset(tmperrinfo, 0, sizeof(struct audio_errinfo)); |
154 | if ((retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo)) < 0) | | 154 | if ((retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo)) < 0) |
155 | return retval; | | 155 | return retval; |
156 | /* | | 156 | /* |
157 | * OSS requires that we return counters that are relative to | | 157 | * OSS requires that we return counters that are relative to |
158 | * the last call. We must maintain state here... | | 158 | * the last call. We must maintain state here... |
159 | */ | | 159 | */ |
160 | if (ioctl(fd, AUDIO_PERROR, &perrors) != -1) { | | 160 | if (ioctl(fd, AUDIO_PERROR, &perrors) != -1) { |
161 | perrors /= ((tmpinfo.play.precision / NBBY) * | | 161 | perrors /= ((tmpinfo.play.precision / NBBY) * |
162 | tmpinfo.play.channels); | | 162 | tmpinfo.play.channels); |
163 | tmperrinfo->play_underruns = | | 163 | tmperrinfo->play_underruns = |
164 | (perrors / tmpinfo.blocksize) - totalperrors; | | 164 | (perrors / tmpinfo.blocksize) - totalperrors; |
165 | totalperrors += tmperrinfo->play_underruns; | | 165 | totalperrors += tmperrinfo->play_underruns; |
166 | } | | 166 | } |
167 | if (ioctl(fd, AUDIO_RERROR, &rerrors) != -1) { | | 167 | if (ioctl(fd, AUDIO_RERROR, &rerrors) != -1) { |
168 | rerrors /= ((tmpinfo.record.precision / NBBY) * | | 168 | rerrors /= ((tmpinfo.record.precision / NBBY) * |
169 | tmpinfo.record.channels); | | 169 | tmpinfo.record.channels); |
170 | tmperrinfo->rec_overruns = | | 170 | tmperrinfo->rec_overruns = |
171 | (rerrors / tmpinfo.blocksize) - totalrerrors; | | 171 | (rerrors / tmpinfo.blocksize) - totalrerrors; |
172 | totalrerrors += tmperrinfo->rec_overruns; | | 172 | totalrerrors += tmperrinfo->rec_overruns; |
173 | } | | 173 | } |
174 | break; | | 174 | break; |
175 | case SNDCTL_DSP_COOKEDMODE: | | 175 | case SNDCTL_DSP_COOKEDMODE: |
176 | /* | | 176 | /* |
177 | * NetBSD is always running in "cooked mode" - the kernel | | 177 | * NetBSD is always running in "cooked mode" - the kernel |
178 | * always performs format conversions. | | 178 | * always performs format conversions. |
179 | */ | | 179 | */ |
180 | INTARG = 1; | | 180 | INTARG = 1; |
181 | break; | | 181 | break; |
182 | case SNDCTL_DSP_POST: | | 182 | case SNDCTL_DSP_POST: |
183 | /* This call is merely advisory, and may be a nop. */ | | 183 | /* This call is merely advisory, and may be a nop. */ |
184 | break; | | 184 | break; |
185 | case SNDCTL_DSP_SPEED: | | 185 | case SNDCTL_DSP_SPEED: |
186 | /* | | 186 | /* |
187 | * In Solaris, 0 is used a special value to query the | | 187 | * In Solaris, 0 is used a special value to query the |
188 | * current rate. This seems useful to support. | | 188 | * current rate. This seems useful to support. |
189 | */ | | 189 | */ |
190 | if (INTARG == 0) { | | 190 | if (INTARG == 0) { |
191 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 191 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
192 | if (retval < 0) | | 192 | if (retval < 0) |
193 | return retval; | | 193 | return retval; |
194 | retval = ioctl(fd, AUDIO_GETFORMAT, &hwfmt); | | 194 | retval = ioctl(fd, AUDIO_GETFORMAT, &hwfmt); |
195 | if (retval < 0) | | 195 | if (retval < 0) |
196 | return retval; | | 196 | return retval; |
197 | INTARG = (tmpinfo.mode == AUMODE_RECORD) ? | | 197 | INTARG = (tmpinfo.mode == AUMODE_RECORD) ? |
198 | hwfmt.record.sample_rate : | | 198 | hwfmt.record.sample_rate : |
199 | hwfmt.play.sample_rate; | | 199 | hwfmt.play.sample_rate; |
200 | } | | 200 | } |
201 | /* | | 201 | /* |
202 | * Conform to kernel limits. | | 202 | * Conform to kernel limits. |
203 | * NetBSD will reject unsupported sample rates, but OSS | | 203 | * NetBSD will reject unsupported sample rates, but OSS |
204 | * applications need to be able to negotiate a supported one. | | 204 | * applications need to be able to negotiate a supported one. |
205 | */ | | 205 | */ |
206 | if (INTARG < 1000) | | 206 | if (INTARG < 1000) |
207 | INTARG = 1000; | | 207 | INTARG = 1000; |
208 | if (INTARG > 192000) | | 208 | if (INTARG > 192000) |
209 | INTARG = 192000; | | 209 | INTARG = 192000; |
210 | AUDIO_INITINFO(&tmpinfo); | | 210 | AUDIO_INITINFO(&tmpinfo); |
211 | tmpinfo.play.sample_rate = | | 211 | tmpinfo.play.sample_rate = |
212 | tmpinfo.record.sample_rate = INTARG; | | 212 | tmpinfo.record.sample_rate = INTARG; |
213 | retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 213 | retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
214 | if (retval < 0) | | 214 | if (retval < 0) |
215 | return retval; | | 215 | return retval; |
216 | /* FALLTHRU */ | | 216 | /* FALLTHRU */ |
217 | case SOUND_PCM_READ_RATE: | | 217 | case SOUND_PCM_READ_RATE: |
218 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 218 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
219 | if (retval < 0) | | 219 | if (retval < 0) |
220 | return retval; | | 220 | return retval; |
221 | INTARG = GETPRINFO(&tmpinfo, sample_rate); | | 221 | INTARG = GETPRINFO(&tmpinfo, sample_rate); |
222 | break; | | 222 | break; |
223 | case SNDCTL_DSP_STEREO: | | 223 | case SNDCTL_DSP_STEREO: |
224 | AUDIO_INITINFO(&tmpinfo); | | 224 | AUDIO_INITINFO(&tmpinfo); |
225 | tmpinfo.play.channels = | | 225 | tmpinfo.play.channels = |
226 | tmpinfo.record.channels = INTARG ? 2 : 1; | | 226 | tmpinfo.record.channels = INTARG ? 2 : 1; |
227 | (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 227 | (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
228 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 228 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
229 | if (retval < 0) | | 229 | if (retval < 0) |
230 | return retval; | | 230 | return retval; |
231 | INTARG = GETPRINFO(&tmpinfo, channels) - 1; | | 231 | INTARG = GETPRINFO(&tmpinfo, channels) - 1; |
232 | break; | | 232 | break; |
233 | case SNDCTL_DSP_GETBLKSIZE: | | 233 | case SNDCTL_DSP_GETBLKSIZE: |
234 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 234 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
235 | if (retval < 0) | | 235 | if (retval < 0) |
236 | return retval; | | 236 | return retval; |
237 | setblocksize(fd, &tmpinfo); | | 237 | setblocksize(fd, &tmpinfo); |
238 | INTARG = tmpinfo.blocksize; | | 238 | INTARG = tmpinfo.blocksize; |
239 | break; | | 239 | break; |
240 | case SNDCTL_DSP_SETFMT: | | 240 | case SNDCTL_DSP_SETFMT: |
241 | AUDIO_INITINFO(&tmpinfo); | | 241 | AUDIO_INITINFO(&tmpinfo); |
242 | switch (INTARG) { | | 242 | switch (INTARG) { |
243 | case AFMT_MU_LAW: | | 243 | case AFMT_MU_LAW: |
244 | tmpinfo.play.precision = | | 244 | tmpinfo.play.precision = |
245 | tmpinfo.record.precision = 8; | | 245 | tmpinfo.record.precision = 8; |
246 | tmpinfo.play.encoding = | | 246 | tmpinfo.play.encoding = |
247 | tmpinfo.record.encoding = AUDIO_ENCODING_ULAW; | | 247 | tmpinfo.record.encoding = AUDIO_ENCODING_ULAW; |
248 | break; | | 248 | break; |
249 | case AFMT_A_LAW: | | 249 | case AFMT_A_LAW: |
250 | tmpinfo.play.precision = | | 250 | tmpinfo.play.precision = |
251 | tmpinfo.record.precision = 8; | | 251 | tmpinfo.record.precision = 8; |
252 | tmpinfo.play.encoding = | | 252 | tmpinfo.play.encoding = |
253 | tmpinfo.record.encoding = AUDIO_ENCODING_ALAW; | | 253 | tmpinfo.record.encoding = AUDIO_ENCODING_ALAW; |
254 | break; | | 254 | break; |
255 | case AFMT_U8: | | 255 | case AFMT_U8: |
256 | tmpinfo.play.precision = | | 256 | tmpinfo.play.precision = |
257 | tmpinfo.record.precision = 8; | | 257 | tmpinfo.record.precision = 8; |
258 | tmpinfo.play.encoding = | | 258 | tmpinfo.play.encoding = |
259 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR; | | 259 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR; |
260 | break; | | 260 | break; |
261 | case AFMT_S8: | | 261 | case AFMT_S8: |
262 | tmpinfo.play.precision = | | 262 | tmpinfo.play.precision = |
263 | tmpinfo.record.precision = 8; | | 263 | tmpinfo.record.precision = 8; |
264 | tmpinfo.play.encoding = | | 264 | tmpinfo.play.encoding = |
265 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR; | | 265 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR; |
266 | break; | | 266 | break; |
267 | case AFMT_S16_LE: | | 267 | case AFMT_S16_LE: |
268 | tmpinfo.play.precision = | | 268 | tmpinfo.play.precision = |
269 | tmpinfo.record.precision = 16; | | 269 | tmpinfo.record.precision = 16; |
270 | tmpinfo.play.encoding = | | 270 | tmpinfo.play.encoding = |
271 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; | | 271 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; |
272 | break; | | 272 | break; |
273 | case AFMT_S16_BE: | | 273 | case AFMT_S16_BE: |
274 | tmpinfo.play.precision = | | 274 | tmpinfo.play.precision = |
275 | tmpinfo.record.precision = 16; | | 275 | tmpinfo.record.precision = 16; |
276 | tmpinfo.play.encoding = | | 276 | tmpinfo.play.encoding = |
277 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; | | 277 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; |
278 | break; | | 278 | break; |
279 | case AFMT_U16_LE: | | 279 | case AFMT_U16_LE: |
280 | tmpinfo.play.precision = | | 280 | tmpinfo.play.precision = |
281 | tmpinfo.record.precision = 16; | | 281 | tmpinfo.record.precision = 16; |
282 | tmpinfo.play.encoding = | | 282 | tmpinfo.play.encoding = |
283 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE; | | 283 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE; |
284 | break; | | 284 | break; |
285 | case AFMT_U16_BE: | | 285 | case AFMT_U16_BE: |
286 | tmpinfo.play.precision = | | 286 | tmpinfo.play.precision = |
287 | tmpinfo.record.precision = 16; | | 287 | tmpinfo.record.precision = 16; |
288 | tmpinfo.play.encoding = | | 288 | tmpinfo.play.encoding = |
289 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE; | | 289 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE; |
290 | break; | | 290 | break; |
291 | /* | | 291 | /* |
292 | * XXX: When the kernel supports 24-bit LPCM by default, | | 292 | * XXX: When the kernel supports 24-bit LPCM by default, |
293 | * the 24-bit formats should be handled properly instead | | 293 | * the 24-bit formats should be handled properly instead |
294 | * of falling back to 32 bits. | | 294 | * of falling back to 32 bits. |
295 | */ | | 295 | */ |
296 | case AFMT_S24_LE: | | 296 | case AFMT_S24_LE: |
297 | case AFMT_S32_LE: | | 297 | case AFMT_S32_LE: |
298 | tmpinfo.play.precision = | | 298 | tmpinfo.play.precision = |
299 | tmpinfo.record.precision = 32; | | 299 | tmpinfo.record.precision = 32; |
300 | tmpinfo.play.encoding = | | 300 | tmpinfo.play.encoding = |
301 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; | | 301 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; |
302 | break; | | 302 | break; |
303 | case AFMT_S24_BE: | | 303 | case AFMT_S24_BE: |
304 | case AFMT_S32_BE: | | 304 | case AFMT_S32_BE: |
305 | tmpinfo.play.precision = | | 305 | tmpinfo.play.precision = |
306 | tmpinfo.record.precision = 32; | | 306 | tmpinfo.record.precision = 32; |
307 | tmpinfo.play.encoding = | | 307 | tmpinfo.play.encoding = |
308 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; | | 308 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; |
309 | break; | | 309 | break; |
310 | case AFMT_AC3: | | 310 | case AFMT_AC3: |
311 | tmpinfo.play.precision = | | 311 | tmpinfo.play.precision = |
312 | tmpinfo.record.precision = 16; | | 312 | tmpinfo.record.precision = 16; |
313 | tmpinfo.play.encoding = | | 313 | tmpinfo.play.encoding = |
314 | tmpinfo.record.encoding = AUDIO_ENCODING_AC3; | | 314 | tmpinfo.record.encoding = AUDIO_ENCODING_AC3; |
315 | break; | | 315 | break; |
316 | default: | | 316 | default: |
317 | /* | | 317 | /* |
318 | * OSSv4 specifies that if an invalid format is chosen | | 318 | * OSSv4 specifies that if an invalid format is chosen |
319 | * by an application then a sensible format supported | | 319 | * by an application then a sensible format supported |
320 | * by the hardware is returned. | | 320 | * by the hardware is returned. |
321 | * | | 321 | * |
322 | * In this case, we pick the current hardware format. | | 322 | * In this case, we pick the current hardware format. |
323 | */ | | 323 | */ |
324 | retval = ioctl(fd, AUDIO_GETFORMAT, &hwfmt); | | 324 | retval = ioctl(fd, AUDIO_GETFORMAT, &hwfmt); |
325 | if (retval < 0) | | 325 | if (retval < 0) |
326 | return retval; | | 326 | return retval; |
327 | retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); | | 327 | retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); |
328 | if (retval < 0) | | 328 | if (retval < 0) |
329 | return retval; | | 329 | return retval; |
330 | tmpinfo.play.encoding = | | 330 | tmpinfo.play.encoding = |
331 | tmpinfo.record.encoding = | | 331 | tmpinfo.record.encoding = |
332 | (tmpinfo.mode == AUMODE_RECORD) ? | | 332 | (tmpinfo.mode == AUMODE_RECORD) ? |
333 | hwfmt.record.encoding : hwfmt.play.encoding; | | 333 | hwfmt.record.encoding : hwfmt.play.encoding; |
334 | tmpinfo.play.precision = | | 334 | tmpinfo.play.precision = |
335 | tmpinfo.record.precision = | | 335 | tmpinfo.record.precision = |
336 | (tmpinfo.mode == AUMODE_RECORD) ? | | 336 | (tmpinfo.mode == AUMODE_RECORD) ? |
337 | hwfmt.record.precision : hwfmt.play.precision ; | | 337 | hwfmt.record.precision : hwfmt.play.precision ; |
338 | break; | | 338 | break; |
339 | } | | 339 | } |
340 | /* | | 340 | /* |
341 | * In the post-kernel-mixer world, assume that any error means | | 341 | * In the post-kernel-mixer world, assume that any error means |
342 | * it's fatal rather than an unsupported format being selected. | | 342 | * it's fatal rather than an unsupported format being selected. |
343 | */ | | 343 | */ |
344 | retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 344 | retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
345 | if (retval < 0) | | 345 | if (retval < 0) |
346 | return retval; | | 346 | return retval; |
347 | /* FALLTHRU */ | | 347 | /* FALLTHRU */ |
348 | case SOUND_PCM_READ_BITS: | | 348 | case SOUND_PCM_READ_BITS: |
349 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 349 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
350 | if (retval < 0) | | 350 | if (retval < 0) |
351 | return retval; | | 351 | return retval; |
352 | encoding = GETPRINFO(&tmpinfo, encoding); | | 352 | encoding = GETPRINFO(&tmpinfo, encoding); |
353 | precision = GETPRINFO(&tmpinfo, precision); | | 353 | precision = GETPRINFO(&tmpinfo, precision); |
354 | switch (encoding) { | | 354 | switch (encoding) { |
355 | case AUDIO_ENCODING_ULAW: | | 355 | case AUDIO_ENCODING_ULAW: |
356 | idat = AFMT_MU_LAW; | | 356 | idat = AFMT_MU_LAW; |
357 | break; | | 357 | break; |
358 | case AUDIO_ENCODING_ALAW: | | 358 | case AUDIO_ENCODING_ALAW: |
359 | idat = AFMT_A_LAW; | | 359 | idat = AFMT_A_LAW; |
360 | break; | | 360 | break; |
361 | case AUDIO_ENCODING_SLINEAR_LE: | | 361 | case AUDIO_ENCODING_SLINEAR_LE: |
362 | if (precision == 32) | | 362 | if (precision == 32) |
363 | idat = AFMT_S32_LE; | | 363 | idat = AFMT_S32_LE; |
364 | else if (precision == 24) | | 364 | else if (precision == 24) |
365 | idat = AFMT_S24_LE; | | 365 | idat = AFMT_S24_LE; |
366 | else if (precision == 16) | | 366 | else if (precision == 16) |
367 | idat = AFMT_S16_LE; | | 367 | idat = AFMT_S16_LE; |
368 | else | | 368 | else |
369 | idat = AFMT_S8; | | 369 | idat = AFMT_S8; |
370 | break; | | 370 | break; |
371 | case AUDIO_ENCODING_SLINEAR_BE: | | 371 | case AUDIO_ENCODING_SLINEAR_BE: |
372 | if (precision == 32) | | 372 | if (precision == 32) |
373 | idat = AFMT_S32_BE; | | 373 | idat = AFMT_S32_BE; |
374 | else if (precision == 24) | | 374 | else if (precision == 24) |
375 | idat = AFMT_S24_BE; | | 375 | idat = AFMT_S24_BE; |
376 | else if (precision == 16) | | 376 | else if (precision == 16) |
377 | idat = AFMT_S16_BE; | | 377 | idat = AFMT_S16_BE; |
378 | else | | 378 | else |
379 | idat = AFMT_S8; | | 379 | idat = AFMT_S8; |
380 | break; | | 380 | break; |
381 | case AUDIO_ENCODING_ULINEAR_LE: | | 381 | case AUDIO_ENCODING_ULINEAR_LE: |
382 | if (precision == 16) | | 382 | if (precision == 16) |
383 | idat = AFMT_U16_LE; | | 383 | idat = AFMT_U16_LE; |
384 | else | | 384 | else |
385 | idat = AFMT_U8; | | 385 | idat = AFMT_U8; |
386 | break; | | 386 | break; |
387 | case AUDIO_ENCODING_ULINEAR_BE: | | 387 | case AUDIO_ENCODING_ULINEAR_BE: |
388 | if (precision == 16) | | 388 | if (precision == 16) |
389 | idat = AFMT_U16_BE; | | 389 | idat = AFMT_U16_BE; |
390 | else | | 390 | else |
391 | idat = AFMT_U8; | | 391 | idat = AFMT_U8; |
392 | break; | | 392 | break; |
393 | case AUDIO_ENCODING_ADPCM: | | 393 | case AUDIO_ENCODING_ADPCM: |
394 | idat = AFMT_IMA_ADPCM; | | 394 | idat = AFMT_IMA_ADPCM; |
395 | break; | | 395 | break; |
396 | case AUDIO_ENCODING_AC3: | | 396 | case AUDIO_ENCODING_AC3: |
397 | idat = AFMT_AC3; | | 397 | idat = AFMT_AC3; |
398 | break; | | 398 | break; |
399 | } | | 399 | } |
400 | INTARG = idat; | | 400 | INTARG = idat; |
401 | break; | | 401 | break; |
402 | case SNDCTL_DSP_CHANNELS: | | 402 | case SNDCTL_DSP_CHANNELS: |
403 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 403 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
404 | if (retval < 0) | | 404 | if (retval < 0) |
405 | return retval; | | 405 | return retval; |
406 | setchannels(fd, tmpinfo.mode, INTARG); | | 406 | setchannels(fd, tmpinfo.mode, INTARG); |
407 | /* FALLTHRU */ | | 407 | /* FALLTHRU */ |
408 | case SOUND_PCM_READ_CHANNELS: | | 408 | case SOUND_PCM_READ_CHANNELS: |
409 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 409 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
410 | if (retval < 0) | | 410 | if (retval < 0) |
411 | return retval; | | 411 | return retval; |
412 | INTARG = GETPRINFO(&tmpinfo, channels); | | 412 | INTARG = GETPRINFO(&tmpinfo, channels); |
413 | break; | | 413 | break; |
414 | case SOUND_PCM_WRITE_FILTER: | | 414 | case SOUND_PCM_WRITE_FILTER: |
415 | case SOUND_PCM_READ_FILTER: | | 415 | case SOUND_PCM_READ_FILTER: |
416 | errno = EINVAL; | | 416 | errno = EINVAL; |
417 | return -1; /* XXX unimplemented */ | | 417 | return -1; /* XXX unimplemented */ |
418 | case SNDCTL_DSP_SUBDIVIDE: | | 418 | case SNDCTL_DSP_SUBDIVIDE: |
419 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 419 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
420 | if (retval < 0) | | 420 | if (retval < 0) |
421 | return retval; | | 421 | return retval; |
422 | setblocksize(fd, &tmpinfo); | | 422 | setblocksize(fd, &tmpinfo); |
423 | idat = INTARG; | | 423 | idat = INTARG; |
424 | if (idat == 0) | | 424 | if (idat == 0) |
425 | idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; | | 425 | idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; |
426 | idat = (tmpinfo.play.buffer_size / idat) & -4; | | 426 | idat = (tmpinfo.play.buffer_size / idat) & -4; |
427 | AUDIO_INITINFO(&tmpinfo); | | 427 | AUDIO_INITINFO(&tmpinfo); |
428 | tmpinfo.blocksize = idat; | | 428 | tmpinfo.blocksize = idat; |
429 | retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 429 | retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
430 | if (retval < 0) | | 430 | if (retval < 0) |
431 | return retval; | | 431 | return retval; |
432 | INTARG = tmpinfo.play.buffer_size / tmpinfo.blocksize; | | 432 | INTARG = tmpinfo.play.buffer_size / tmpinfo.blocksize; |
433 | break; | | 433 | break; |
434 | case SNDCTL_DSP_SETFRAGMENT: | | 434 | case SNDCTL_DSP_SETFRAGMENT: |
435 | AUDIO_INITINFO(&tmpinfo); | | 435 | AUDIO_INITINFO(&tmpinfo); |
436 | idat = INTARG; | | 436 | idat = INTARG; |
437 | if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) { | | 437 | if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) { |
438 | errno = EINVAL; | | 438 | errno = EINVAL; |
439 | return -1; | | 439 | return -1; |
440 | } | | 440 | } |
441 | tmpinfo.blocksize = 1 << (idat & 0xffff); | | 441 | tmpinfo.blocksize = 1 << (idat & 0xffff); |
442 | tmpinfo.hiwat = ((unsigned)idat >> 16) & 0x7fff; | | 442 | tmpinfo.hiwat = ((unsigned)idat >> 16) & 0x7fff; |
443 | if (tmpinfo.hiwat == 0) /* 0 means set to max */ | | 443 | if (tmpinfo.hiwat == 0) /* 0 means set to max */ |
444 | tmpinfo.hiwat = 65536; | | 444 | tmpinfo.hiwat = 65536; |
445 | (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 445 | (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
446 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 446 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
447 | if (retval < 0) | | 447 | if (retval < 0) |
448 | return retval; | | 448 | return retval; |
449 | u = tmpinfo.blocksize; | | 449 | u = tmpinfo.blocksize; |
450 | for(idat = 0; u > 1; idat++, u >>= 1) | | 450 | for(idat = 0; u > 1; idat++, u >>= 1) |
451 | ; | | 451 | ; |
452 | idat |= (tmpinfo.hiwat & 0x7fff) << 16; | | 452 | idat |= (tmpinfo.hiwat & 0x7fff) << 16; |
453 | INTARG = idat; | | 453 | INTARG = idat; |
454 | break; | | 454 | break; |
455 | case SNDCTL_DSP_GETFMTS: | | 455 | case SNDCTL_DSP_GETFMTS: |
456 | for(idat = 0, tmpenc.index = 0; | | 456 | for(idat = 0, tmpenc.index = 0; |
457 | ioctl(fd, AUDIO_GETENC, &tmpenc) == 0; | | 457 | ioctl(fd, AUDIO_GETENC, &tmpenc) == 0; |
458 | tmpenc.index++) { | | 458 | tmpenc.index++) { |
459 | switch(tmpenc.encoding) { | | 459 | switch(tmpenc.encoding) { |
460 | case AUDIO_ENCODING_ULAW: | | 460 | case AUDIO_ENCODING_ULAW: |
461 | idat |= AFMT_MU_LAW; | | 461 | idat |= AFMT_MU_LAW; |
462 | break; | | 462 | break; |
463 | case AUDIO_ENCODING_ALAW: | | 463 | case AUDIO_ENCODING_ALAW: |
464 | idat |= AFMT_A_LAW; | | 464 | idat |= AFMT_A_LAW; |
465 | break; | | 465 | break; |
466 | case AUDIO_ENCODING_SLINEAR: | | 466 | case AUDIO_ENCODING_SLINEAR: |
467 | idat |= AFMT_S8; | | 467 | idat |= AFMT_S8; |
468 | break; | | 468 | break; |
469 | case AUDIO_ENCODING_SLINEAR_LE: | | 469 | case AUDIO_ENCODING_SLINEAR_LE: |
470 | if (tmpenc.precision == 32) | | 470 | if (tmpenc.precision == 32) |
471 | idat |= AFMT_S32_LE; | | 471 | idat |= AFMT_S32_LE; |
472 | else if (tmpenc.precision == 24) | | 472 | else if (tmpenc.precision == 24) |
473 | idat |= AFMT_S24_LE; | | 473 | idat |= AFMT_S24_LE; |
474 | else if (tmpenc.precision == 16) | | 474 | else if (tmpenc.precision == 16) |
475 | idat |= AFMT_S16_LE; | | 475 | idat |= AFMT_S16_LE; |
476 | else | | 476 | else |
477 | idat |= AFMT_S8; | | 477 | idat |= AFMT_S8; |
478 | break; | | 478 | break; |
479 | case AUDIO_ENCODING_SLINEAR_BE: | | 479 | case AUDIO_ENCODING_SLINEAR_BE: |
480 | if (tmpenc.precision == 32) | | 480 | if (tmpenc.precision == 32) |
481 | idat |= AFMT_S32_BE; | | 481 | idat |= AFMT_S32_BE; |
482 | else if (tmpenc.precision == 24) | | 482 | else if (tmpenc.precision == 24) |
483 | idat |= AFMT_S24_BE; | | 483 | idat |= AFMT_S24_BE; |
484 | else if (tmpenc.precision == 16) | | 484 | else if (tmpenc.precision == 16) |
485 | idat |= AFMT_S16_BE; | | 485 | idat |= AFMT_S16_BE; |
486 | else | | 486 | else |
487 | idat |= AFMT_S8; | | 487 | idat |= AFMT_S8; |
488 | break; | | 488 | break; |
489 | case AUDIO_ENCODING_ULINEAR: | | 489 | case AUDIO_ENCODING_ULINEAR: |
490 | idat |= AFMT_U8; | | 490 | idat |= AFMT_U8; |
491 | break; | | 491 | break; |
492 | case AUDIO_ENCODING_ULINEAR_LE: | | 492 | case AUDIO_ENCODING_ULINEAR_LE: |
493 | if (tmpenc.precision == 16) | | 493 | if (tmpenc.precision == 16) |
494 | idat |= AFMT_U16_LE; | | 494 | idat |= AFMT_U16_LE; |
495 | else | | 495 | else |
496 | idat |= AFMT_U8; | | 496 | idat |= AFMT_U8; |
497 | break; | | 497 | break; |
498 | case AUDIO_ENCODING_ULINEAR_BE: | | 498 | case AUDIO_ENCODING_ULINEAR_BE: |
499 | if (tmpenc.precision == 16) | | 499 | if (tmpenc.precision == 16) |
500 | idat |= AFMT_U16_BE; | | 500 | idat |= AFMT_U16_BE; |
501 | else | | 501 | else |
502 | idat |= AFMT_U8; | | 502 | idat |= AFMT_U8; |
503 | break; | | 503 | break; |
504 | case AUDIO_ENCODING_ADPCM: | | 504 | case AUDIO_ENCODING_ADPCM: |
505 | idat |= AFMT_IMA_ADPCM; | | 505 | idat |= AFMT_IMA_ADPCM; |
506 | break; | | 506 | break; |
507 | case AUDIO_ENCODING_AC3: | | 507 | case AUDIO_ENCODING_AC3: |
508 | idat |= AFMT_AC3; | | 508 | idat |= AFMT_AC3; |
509 | break; | | 509 | break; |
510 | default: | | 510 | default: |
511 | break; | | 511 | break; |
512 | } | | 512 | } |
513 | } | | 513 | } |
514 | INTARG = idat; | | 514 | INTARG = idat; |
515 | break; | | 515 | break; |
516 | case SNDCTL_DSP_GETOSPACE: | | 516 | case SNDCTL_DSP_GETOSPACE: |
517 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 517 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
518 | if (retval < 0) | | 518 | if (retval < 0) |
519 | return retval; | | 519 | return retval; |
520 | setblocksize(fd, &tmpinfo); | | 520 | setblocksize(fd, &tmpinfo); |
521 | bufinfo.fragsize = tmpinfo.blocksize; | | 521 | bufinfo.fragsize = tmpinfo.blocksize; |
522 | bufinfo.fragments = tmpinfo.hiwat - (tmpinfo.play.seek | | 522 | bufinfo.fragments = tmpinfo.hiwat - (tmpinfo.play.seek |
523 | + tmpinfo.blocksize - 1) / tmpinfo.blocksize; | | 523 | + tmpinfo.blocksize - 1) / tmpinfo.blocksize; |
524 | bufinfo.fragstotal = tmpinfo.hiwat; | | 524 | bufinfo.fragstotal = tmpinfo.hiwat; |
525 | bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize | | 525 | bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize |
526 | - tmpinfo.play.seek; | | 526 | - tmpinfo.play.seek; |
527 | *(struct audio_buf_info *)argp = bufinfo; | | 527 | *(struct audio_buf_info *)argp = bufinfo; |
528 | break; | | 528 | break; |
529 | case SNDCTL_DSP_GETISPACE: | | 529 | case SNDCTL_DSP_GETISPACE: |
530 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 530 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
531 | if (retval < 0) | | 531 | if (retval < 0) |
532 | return retval; | | 532 | return retval; |
533 | setblocksize(fd, &tmpinfo); | | 533 | setblocksize(fd, &tmpinfo); |
534 | bufinfo.fragsize = tmpinfo.blocksize; | | 534 | bufinfo.fragsize = tmpinfo.blocksize; |
535 | bufinfo.fragments = tmpinfo.record.seek / tmpinfo.blocksize; | | 535 | bufinfo.fragments = tmpinfo.record.seek / tmpinfo.blocksize; |
536 | bufinfo.fragstotal = | | 536 | bufinfo.fragstotal = |
537 | tmpinfo.record.buffer_size / tmpinfo.blocksize; | | 537 | tmpinfo.record.buffer_size / tmpinfo.blocksize; |
538 | bufinfo.bytes = tmpinfo.record.seek; | | 538 | bufinfo.bytes = tmpinfo.record.seek; |
539 | *(struct audio_buf_info *)argp = bufinfo; | | 539 | *(struct audio_buf_info *)argp = bufinfo; |
540 | break; | | 540 | break; |
541 | case SNDCTL_DSP_NONBLOCK: | | 541 | case SNDCTL_DSP_NONBLOCK: |
542 | idat = 1; | | 542 | idat = 1; |
543 | retval = ioctl(fd, FIONBIO, &idat); | | 543 | retval = ioctl(fd, FIONBIO, &idat); |
544 | if (retval < 0) | | 544 | if (retval < 0) |
545 | return retval; | | 545 | return retval; |
546 | break; | | 546 | break; |
547 | case SNDCTL_DSP_GETCAPS: | | 547 | case SNDCTL_DSP_GETCAPS: |
548 | retval = getcaps(fd, (int *)argp); | | 548 | retval = getcaps(fd, (int *)argp); |
549 | if (retval < 0) | | 549 | if (retval < 0) |
550 | return retval; | | 550 | return retval; |
551 | break; | | 551 | break; |
552 | case SNDCTL_DSP_SETTRIGGER: | | 552 | case SNDCTL_DSP_SETTRIGGER: |
553 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 553 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
554 | if (retval < 0) | | 554 | if (retval < 0) |
555 | return retval; | | 555 | return retval; |
556 | AUDIO_INITINFO(&tmpinfo); | | 556 | AUDIO_INITINFO(&tmpinfo); |
557 | if (tmpinfo.mode & AUMODE_PLAY) | | 557 | if (tmpinfo.mode & AUMODE_PLAY) |
558 | tmpinfo.play.pause = (INTARG & PCM_ENABLE_OUTPUT) == 0; | | 558 | tmpinfo.play.pause = (INTARG & PCM_ENABLE_OUTPUT) == 0; |
559 | if (tmpinfo.mode & AUMODE_RECORD) | | 559 | if (tmpinfo.mode & AUMODE_RECORD) |
560 | tmpinfo.record.pause = (INTARG & PCM_ENABLE_INPUT) == 0; | | 560 | tmpinfo.record.pause = (INTARG & PCM_ENABLE_INPUT) == 0; |
561 | (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 561 | (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
562 | /* FALLTHRU */ | | 562 | /* FALLTHRU */ |
563 | case SNDCTL_DSP_GETTRIGGER: | | 563 | case SNDCTL_DSP_GETTRIGGER: |
564 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 564 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
565 | if (retval < 0) | | 565 | if (retval < 0) |
566 | return retval; | | 566 | return retval; |
567 | idat = 0; | | 567 | idat = 0; |
568 | if ((tmpinfo.mode & AUMODE_PLAY) && !tmpinfo.play.pause) | | 568 | if ((tmpinfo.mode & AUMODE_PLAY) && !tmpinfo.play.pause) |
569 | idat |= PCM_ENABLE_OUTPUT; | | 569 | idat |= PCM_ENABLE_OUTPUT; |
570 | if ((tmpinfo.mode & AUMODE_RECORD) && !tmpinfo.record.pause) | | 570 | if ((tmpinfo.mode & AUMODE_RECORD) && !tmpinfo.record.pause) |
571 | idat |= PCM_ENABLE_INPUT; | | 571 | idat |= PCM_ENABLE_INPUT; |
572 | INTARG = idat; | | 572 | INTARG = idat; |
573 | break; | | 573 | break; |
574 | case SNDCTL_DSP_GETIPTR: | | 574 | case SNDCTL_DSP_GETIPTR: |
575 | retval = ioctl(fd, AUDIO_GETIOFFS, &tmpoffs); | | 575 | retval = ioctl(fd, AUDIO_GETIOFFS, &tmpoffs); |
576 | if (retval < 0) | | 576 | if (retval < 0) |
577 | return retval; | | 577 | return retval; |
578 | cntinfo.bytes = tmpoffs.samples; | | 578 | cntinfo.bytes = tmpoffs.samples; |
579 | cntinfo.blocks = tmpoffs.deltablks; | | 579 | cntinfo.blocks = tmpoffs.deltablks; |
580 | cntinfo.ptr = tmpoffs.offset; | | 580 | cntinfo.ptr = tmpoffs.offset; |
581 | *(struct count_info *)argp = cntinfo; | | 581 | *(struct count_info *)argp = cntinfo; |
582 | break; | | 582 | break; |
583 | case SNDCTL_DSP_CURRENT_IPTR: | | 583 | case SNDCTL_DSP_CURRENT_IPTR: |
584 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 584 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
585 | if (retval < 0) | | 585 | if (retval < 0) |
586 | return retval; | | 586 | return retval; |
587 | /* XXX: 'samples' may wrap */ | | 587 | /* XXX: 'samples' may wrap */ |
588 | memset(osscount.filler, 0, sizeof(osscount.filler)); | | 588 | memset(osscount.filler, 0, sizeof(osscount.filler)); |
589 | osscount.samples = tmpinfo.record.samples / | | 589 | osscount.samples = tmpinfo.record.samples / |
590 | ((tmpinfo.record.precision / NBBY) * | | 590 | ((tmpinfo.record.precision / NBBY) * |
591 | tmpinfo.record.channels); | | 591 | tmpinfo.record.channels); |
592 | osscount.fifo_samples = tmpinfo.record.seek / | | 592 | osscount.fifo_samples = tmpinfo.record.seek / |
593 | ((tmpinfo.record.precision / NBBY) * | | 593 | ((tmpinfo.record.precision / NBBY) * |
594 | tmpinfo.record.channels); | | 594 | tmpinfo.record.channels); |
595 | *(oss_count_t *)argp = osscount; | | 595 | *(oss_count_t *)argp = osscount; |
596 | break; | | 596 | break; |
597 | case SNDCTL_DSP_GETOPTR: | | 597 | case SNDCTL_DSP_GETOPTR: |
598 | retval = ioctl(fd, AUDIO_GETOOFFS, &tmpoffs); | | 598 | retval = ioctl(fd, AUDIO_GETOOFFS, &tmpoffs); |
599 | if (retval < 0) | | 599 | if (retval < 0) |
600 | return retval; | | 600 | return retval; |
601 | cntinfo.bytes = tmpoffs.samples; | | 601 | cntinfo.bytes = tmpoffs.samples; |
602 | cntinfo.blocks = tmpoffs.deltablks; | | 602 | cntinfo.blocks = tmpoffs.deltablks; |
603 | cntinfo.ptr = tmpoffs.offset; | | 603 | cntinfo.ptr = tmpoffs.offset; |
604 | *(struct count_info *)argp = cntinfo; | | 604 | *(struct count_info *)argp = cntinfo; |
605 | break; | | 605 | break; |
606 | case SNDCTL_DSP_CURRENT_OPTR: | | 606 | case SNDCTL_DSP_CURRENT_OPTR: |
607 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 607 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
608 | if (retval < 0) | | 608 | if (retval < 0) |
609 | return retval; | | 609 | return retval; |
610 | /* XXX: 'samples' may wrap */ | | 610 | /* XXX: 'samples' may wrap */ |
611 | memset(osscount.filler, 0, sizeof(osscount.filler)); | | 611 | memset(osscount.filler, 0, sizeof(osscount.filler)); |
612 | osscount.samples = tmpinfo.play.samples / | | 612 | osscount.samples = tmpinfo.play.samples / |
613 | ((tmpinfo.play.precision / NBBY) * | | 613 | ((tmpinfo.play.precision / NBBY) * |
614 | tmpinfo.play.channels); | | 614 | tmpinfo.play.channels); |
615 | osscount.fifo_samples = tmpinfo.play.seek / | | 615 | osscount.fifo_samples = tmpinfo.play.seek / |
616 | ((tmpinfo.play.precision / NBBY) * | | 616 | ((tmpinfo.play.precision / NBBY) * |
617 | tmpinfo.play.channels); | | 617 | tmpinfo.play.channels); |
618 | *(oss_count_t *)argp = osscount; | | 618 | *(oss_count_t *)argp = osscount; |
619 | break; | | 619 | break; |
620 | case SNDCTL_DSP_SETPLAYVOL: | | 620 | case SNDCTL_DSP_SETPLAYVOL: |
621 | setvol(fd, INTARG, false); | | 621 | setvol(fd, INTARG, false); |
622 | /* FALLTHRU */ | | 622 | /* FALLTHRU */ |
623 | case SNDCTL_DSP_GETPLAYVOL: | | 623 | case SNDCTL_DSP_GETPLAYVOL: |
624 | retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); | | 624 | retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); |
625 | if (retval < 0) | | 625 | if (retval < 0) |
626 | return retval; | | 626 | return retval; |
627 | INTARG = getvol(tmpinfo.play.gain, tmpinfo.play.balance); | | 627 | INTARG = getvol(tmpinfo.play.gain, tmpinfo.play.balance); |
628 | break; | | 628 | break; |
629 | case SNDCTL_DSP_SETRECVOL: | | 629 | case SNDCTL_DSP_SETRECVOL: |
630 | setvol(fd, INTARG, true); | | 630 | setvol(fd, INTARG, true); |
631 | /* FALLTHRU */ | | 631 | /* FALLTHRU */ |
632 | case SNDCTL_DSP_GETRECVOL: | | 632 | case SNDCTL_DSP_GETRECVOL: |
633 | retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); | | 633 | retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); |
634 | if (retval < 0) | | 634 | if (retval < 0) |
635 | return retval; | | 635 | return retval; |
636 | INTARG = getvol(tmpinfo.record.gain, tmpinfo.record.balance); | | 636 | INTARG = getvol(tmpinfo.record.gain, tmpinfo.record.balance); |
637 | break; | | 637 | break; |
638 | case SNDCTL_DSP_SKIP: | | 638 | case SNDCTL_DSP_SKIP: |
639 | case SNDCTL_DSP_SILENCE: | | 639 | case SNDCTL_DSP_SILENCE: |
640 | errno = EINVAL; | | 640 | errno = EINVAL; |
641 | return -1; | | 641 | return -1; |
642 | case SNDCTL_DSP_SETDUPLEX: | | 642 | case SNDCTL_DSP_SETDUPLEX: |
643 | idat = 1; | | 643 | idat = 1; |
644 | retval = ioctl(fd, AUDIO_SETFD, &idat); | | 644 | retval = ioctl(fd, AUDIO_SETFD, &idat); |
645 | if (retval < 0) | | 645 | if (retval < 0) |
646 | return retval; | | 646 | return retval; |
647 | break; | | 647 | break; |
648 | case SNDCTL_DSP_GETODELAY: | | 648 | case SNDCTL_DSP_GETODELAY: |
649 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 649 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
650 | if (retval < 0) | | 650 | if (retval < 0) |
651 | return retval; | | 651 | return retval; |
652 | idat = tmpinfo.play.seek + tmpinfo.blocksize / 2; | | 652 | idat = tmpinfo.play.seek + tmpinfo.blocksize / 2; |
653 | INTARG = idat; | | 653 | INTARG = idat; |
654 | break; | | 654 | break; |
655 | case SNDCTL_DSP_PROFILE: | | 655 | case SNDCTL_DSP_PROFILE: |
656 | /* This gives just a hint to the driver, | | 656 | /* This gives just a hint to the driver, |
657 | * implementing it as a NOP is ok | | 657 | * implementing it as a NOP is ok |
658 | */ | | 658 | */ |
659 | break; | | 659 | break; |
660 | case SNDCTL_DSP_MAPINBUF: | | 660 | case SNDCTL_DSP_MAPINBUF: |
661 | case SNDCTL_DSP_MAPOUTBUF: | | 661 | case SNDCTL_DSP_MAPOUTBUF: |
662 | case SNDCTL_DSP_SETSYNCRO: | | 662 | case SNDCTL_DSP_SETSYNCRO: |
663 | errno = EINVAL; | | 663 | errno = EINVAL; |
664 | return -1; /* XXX unimplemented */ | | 664 | return -1; /* XXX unimplemented */ |
665 | default: | | 665 | default: |
666 | errno = EINVAL; | | 666 | errno = EINVAL; |
667 | return -1; | | 667 | return -1; |
668 | } | | 668 | } |
669 | | | 669 | |
670 | return 0; | | 670 | return 0; |
671 | } | | 671 | } |
672 | | | 672 | |
673 | | | 673 | |
674 | /* If the NetBSD mixer device should have more than NETBSD_MAXDEVS devices | | 674 | /* If the NetBSD mixer device should have more than NETBSD_MAXDEVS devices |
675 | * some will not be available to OSS applications */ | | 675 | * some will not be available to OSS applications */ |
676 | #define NETBSD_MAXDEVS 64 | | 676 | #define NETBSD_MAXDEVS 64 |
677 | struct audiodevinfo { | | 677 | struct audiodevinfo { |
678 | int done; | | 678 | int done; |
679 | dev_t dev; | | 679 | dev_t dev; |
680 | int16_t devmap[SOUND_MIXER_NRDEVICES], | | 680 | int16_t devmap[SOUND_MIXER_NRDEVICES], |
681 | rdevmap[NETBSD_MAXDEVS]; | | 681 | rdevmap[NETBSD_MAXDEVS]; |
682 | char names[NETBSD_MAXDEVS][MAX_AUDIO_DEV_LEN]; | | 682 | char names[NETBSD_MAXDEVS][MAX_AUDIO_DEV_LEN]; |
683 | int enum2opaque[NETBSD_MAXDEVS]; | | 683 | int enum2opaque[NETBSD_MAXDEVS]; |
684 | u_long devmask, recmask, stereomask; | | 684 | u_long devmask, recmask, stereomask; |
685 | u_long caps; | | 685 | u_long caps; |
686 | int source; | | 686 | int source; |
687 | }; | | 687 | }; |
688 | | | 688 | |
689 | static int | | 689 | static int |
690 | opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq) | | 690 | opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq) |
691 | { | | 691 | { |
692 | int i, o; | | 692 | int i, o; |
693 | | | 693 | |
694 | for (i = 0; i < NETBSD_MAXDEVS; i++) { | | 694 | for (i = 0; i < NETBSD_MAXDEVS; i++) { |
695 | o = di->enum2opaque[i]; | | 695 | o = di->enum2opaque[i]; |
696 | if (o == opq) | | 696 | if (o == opq) |
697 | break; | | 697 | break; |
698 | if (o == -1 && label != NULL && | | 698 | if (o == -1 && label != NULL && |
699 | !strncmp(di->names[i], label->name, sizeof di->names[i])) { | | 699 | !strncmp(di->names[i], label->name, sizeof di->names[i])) { |
700 | di->enum2opaque[i] = opq; | | 700 | di->enum2opaque[i] = opq; |
701 | break; | | 701 | break; |
702 | } | | 702 | } |
703 | } | | 703 | } |
704 | if (i >= NETBSD_MAXDEVS) | | 704 | if (i >= NETBSD_MAXDEVS) |
705 | i = -1; | | 705 | i = -1; |
706 | /*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/ | | 706 | /*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/ |
707 | return (i); | | 707 | return (i); |
708 | } | | 708 | } |
709 | | | 709 | |
710 | static int | | 710 | static int |
711 | enum_to_ord(struct audiodevinfo *di, int enm) | | 711 | enum_to_ord(struct audiodevinfo *di, int enm) |
712 | { | | 712 | { |
713 | if (enm >= NETBSD_MAXDEVS) | | 713 | if (enm >= NETBSD_MAXDEVS) |
714 | return (-1); | | 714 | return (-1); |
715 | | | 715 | |
716 | /*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/ | | 716 | /*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/ |
717 | return (di->enum2opaque[enm]); | | 717 | return (di->enum2opaque[enm]); |
718 | } | | 718 | } |
719 | | | 719 | |
720 | static int | | 720 | static int |
721 | enum_to_mask(struct audiodevinfo *di, int enm) | | 721 | enum_to_mask(struct audiodevinfo *di, int enm) |
722 | { | | 722 | { |
723 | int m; | | 723 | int m; |
724 | if (enm >= NETBSD_MAXDEVS) | | 724 | if (enm >= NETBSD_MAXDEVS) |
725 | return (0); | | 725 | return (0); |
726 | | | 726 | |
727 | m = di->enum2opaque[enm]; | | 727 | m = di->enum2opaque[enm]; |
728 | if (m == -1) | | 728 | if (m == -1) |
729 | m = 0; | | 729 | m = 0; |
730 | /*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/ | | 730 | /*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/ |
731 | return (m); | | 731 | return (m); |
732 | } | | 732 | } |
733 | | | 733 | |
734 | /* | | 734 | /* |
735 | * Collect the audio device information to allow faster | | 735 | * Collect the audio device information to allow faster |
736 | * emulation of the OSSv3 mixer ioctls. Cache the information | | 736 | * emulation of the OSSv3 mixer ioctls. Cache the information |
737 | * to eliminate the overhead of repeating all the ioctls needed | | 737 | * to eliminate the overhead of repeating all the ioctls needed |
738 | * to collect the information. | | 738 | * to collect the information. |
739 | */ | | 739 | */ |
740 | static struct audiodevinfo * | | 740 | static struct audiodevinfo * |
741 | getdevinfo(int fd) | | 741 | getdevinfo(int fd) |
742 | { | | 742 | { |
743 | mixer_devinfo_t mi; | | 743 | mixer_devinfo_t mi; |
744 | int i, j, e; | | 744 | int i, j, e; |
745 | static struct { | | 745 | static struct { |
746 | const char *name; | | 746 | const char *name; |
747 | int code; | | 747 | int code; |
748 | } *dp, devs[] = { | | 748 | } *dp, devs[] = { |
749 | { AudioNmicrophone, SOUND_MIXER_MIC }, | | 749 | { AudioNmicrophone, SOUND_MIXER_MIC }, |
750 | { AudioNline, SOUND_MIXER_LINE }, | | 750 | { AudioNline, SOUND_MIXER_LINE }, |
751 | { AudioNcd, SOUND_MIXER_CD }, | | 751 | { AudioNcd, SOUND_MIXER_CD }, |
752 | { AudioNdac, SOUND_MIXER_PCM }, | | 752 | { AudioNdac, SOUND_MIXER_PCM }, |
753 | { AudioNaux, SOUND_MIXER_LINE1 }, | | 753 | { AudioNaux, SOUND_MIXER_LINE1 }, |
754 | { AudioNrecord, SOUND_MIXER_IMIX }, | | 754 | { AudioNrecord, SOUND_MIXER_IMIX }, |
755 | { AudioNmaster, SOUND_MIXER_VOLUME }, | | 755 | { AudioNmaster, SOUND_MIXER_VOLUME }, |
756 | { AudioNtreble, SOUND_MIXER_TREBLE }, | | 756 | { AudioNtreble, SOUND_MIXER_TREBLE }, |
757 | { AudioNbass, SOUND_MIXER_BASS }, | | 757 | { AudioNbass, SOUND_MIXER_BASS }, |
758 | { AudioNspeaker, SOUND_MIXER_SPEAKER }, | | 758 | { AudioNspeaker, SOUND_MIXER_SPEAKER }, |
759 | /* { AudioNheadphone, ?? },*/ | | 759 | /* { AudioNheadphone, ?? },*/ |
760 | { AudioNoutput, SOUND_MIXER_OGAIN }, | | 760 | { AudioNoutput, SOUND_MIXER_OGAIN }, |
761 | { AudioNinput, SOUND_MIXER_IGAIN }, | | 761 | { AudioNinput, SOUND_MIXER_IGAIN }, |
762 | /* { AudioNmaster, SOUND_MIXER_SPEAKER },*/ | | 762 | /* { AudioNmaster, SOUND_MIXER_SPEAKER },*/ |
763 | /* { AudioNstereo, ?? },*/ | | 763 | /* { AudioNstereo, ?? },*/ |
764 | /* { AudioNmono, ?? },*/ | | 764 | /* { AudioNmono, ?? },*/ |
765 | { AudioNfmsynth, SOUND_MIXER_SYNTH }, | | 765 | { AudioNfmsynth, SOUND_MIXER_SYNTH }, |
766 | /* { AudioNwave, SOUND_MIXER_PCM },*/ | | 766 | /* { AudioNwave, SOUND_MIXER_PCM },*/ |
767 | { AudioNmidi, SOUND_MIXER_SYNTH }, | | 767 | { AudioNmidi, SOUND_MIXER_SYNTH }, |
768 | /* { AudioNmixerout, ?? },*/ | | 768 | /* { AudioNmixerout, ?? },*/ |
769 | { 0, -1 } | | 769 | { 0, -1 } |
770 | }; | | 770 | }; |
771 | static struct audiodevinfo devcache = { .done = 0 }; | | 771 | static struct audiodevinfo devcache = { .done = 0 }; |
772 | struct audiodevinfo *di = &devcache; | | 772 | struct audiodevinfo *di = &devcache; |
773 | struct stat sb; | | 773 | struct stat sb; |
774 | size_t mlen, dlen; | | 774 | size_t mlen, dlen; |
775 | | | 775 | |
776 | /* Figure out what device it is so we can check if the | | 776 | /* Figure out what device it is so we can check if the |
777 | * cached data is valid. | | 777 | * cached data is valid. |
778 | */ | | 778 | */ |
779 | if (fstat(fd, &sb) < 0) | | 779 | if (fstat(fd, &sb) < 0) |
780 | return 0; | | 780 | return 0; |
781 | if (di->done && di->dev == sb.st_dev) | | 781 | if (di->done && di->dev == sb.st_dev) |
782 | return di; | | 782 | return di; |
783 | | | 783 | |
784 | di->done = 1; | | 784 | di->done = 1; |
785 | di->dev = sb.st_dev; | | 785 | di->dev = sb.st_dev; |
786 | di->devmask = 0; | | 786 | di->devmask = 0; |
787 | di->recmask = 0; | | 787 | di->recmask = 0; |
788 | di->stereomask = 0; | | 788 | di->stereomask = 0; |
789 | di->source = ~0; | | 789 | di->source = ~0; |
790 | di->caps = 0; | | 790 | di->caps = 0; |
791 | for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) | | 791 | for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) |
792 | di->devmap[i] = -1; | | 792 | di->devmap[i] = -1; |
793 | for(i = 0; i < NETBSD_MAXDEVS; i++) { | | 793 | for(i = 0; i < NETBSD_MAXDEVS; i++) { |
794 | di->rdevmap[i] = -1; | | 794 | di->rdevmap[i] = -1; |
795 | di->names[i][0] = '\0'; | | 795 | di->names[i][0] = '\0'; |
796 | di->enum2opaque[i] = -1; | | 796 | di->enum2opaque[i] = -1; |
797 | } | | 797 | } |
798 | for(i = 0; i < NETBSD_MAXDEVS; i++) { | | 798 | for(i = 0; i < NETBSD_MAXDEVS; i++) { |
799 | mi.index = i; | | 799 | mi.index = i; |
800 | if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) | | 800 | if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) |
801 | break; | | 801 | break; |
802 | switch(mi.type) { | | 802 | switch(mi.type) { |
803 | case AUDIO_MIXER_VALUE: | | 803 | case AUDIO_MIXER_VALUE: |
804 | for(dp = devs; dp->name; dp++) { | | 804 | for(dp = devs; dp->name; dp++) { |
805 | if (strcmp(dp->name, mi.label.name) == 0) | | 805 | if (strcmp(dp->name, mi.label.name) == 0) |
806 | break; | | 806 | break; |
807 | dlen = strlen(dp->name); | | 807 | dlen = strlen(dp->name); |
808 | mlen = strlen(mi.label.name); | | 808 | mlen = strlen(mi.label.name); |
809 | if (dlen < mlen | | 809 | if (dlen < mlen |
810 | && mi.label.name[mlen-dlen-1] == '.' | | 810 | && mi.label.name[mlen-dlen-1] == '.' |
811 | && strcmp(dp->name, | | 811 | && strcmp(dp->name, |
812 | mi.label.name + mlen - dlen) == 0) | | 812 | mi.label.name + mlen - dlen) == 0) |
813 | break; | | 813 | break; |
814 | } | | 814 | } |
815 | if (dp->code >= 0) { | | 815 | if (dp->code >= 0) { |
816 | di->devmap[dp->code] = i; | | 816 | di->devmap[dp->code] = i; |
817 | di->rdevmap[i] = dp->code; | | 817 | di->rdevmap[i] = dp->code; |
818 | di->devmask |= 1 << dp->code; | | 818 | di->devmask |= 1 << dp->code; |
819 | if (mi.un.v.num_channels == 2) | | 819 | if (mi.un.v.num_channels == 2) |
820 | di->stereomask |= 1 << dp->code; | | 820 | di->stereomask |= 1 << dp->code; |
821 | strlcpy(di->names[i], mi.label.name, | | 821 | strlcpy(di->names[i], mi.label.name, |
822 | sizeof di->names[i]); | | 822 | sizeof di->names[i]); |
823 | } | | 823 | } |
824 | break; | | 824 | break; |
825 | } | | 825 | } |
826 | } | | 826 | } |
827 | for(i = 0; i < NETBSD_MAXDEVS; i++) { | | 827 | for(i = 0; i < NETBSD_MAXDEVS; i++) { |
828 | mi.index = i; | | 828 | mi.index = i; |
829 | if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) | | 829 | if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) |
830 | break; | | 830 | break; |
831 | if (strcmp(mi.label.name, AudioNsource) != 0) | | 831 | if (strcmp(mi.label.name, AudioNsource) != 0) |
832 | continue; | | 832 | continue; |
833 | di->source = i; | | 833 | di->source = i; |
834 | switch(mi.type) { | | 834 | switch(mi.type) { |
835 | case AUDIO_MIXER_ENUM: | | 835 | case AUDIO_MIXER_ENUM: |
836 | for(j = 0; j < mi.un.e.num_mem; j++) { | | 836 | for(j = 0; j < mi.un.e.num_mem; j++) { |
837 | e = opaque_to_enum(di, | | 837 | e = opaque_to_enum(di, |
838 | &mi.un.e.member[j].label, | | 838 | &mi.un.e.member[j].label, |
839 | mi.un.e.member[j].ord); | | 839 | mi.un.e.member[j].ord); |
840 | if (e >= 0) | | 840 | if (e >= 0) |
841 | di->recmask |= 1 << di->rdevmap[e]; | | 841 | di->recmask |= 1 << di->rdevmap[e]; |
842 | } | | 842 | } |
843 | di->caps = SOUND_CAP_EXCL_INPUT; | | 843 | di->caps = SOUND_CAP_EXCL_INPUT; |
844 | break; | | 844 | break; |
845 | case AUDIO_MIXER_SET: | | 845 | case AUDIO_MIXER_SET: |
846 | for(j = 0; j < mi.un.s.num_mem; j++) { | | 846 | for(j = 0; j < mi.un.s.num_mem; j++) { |
847 | e = opaque_to_enum(di, | | 847 | e = opaque_to_enum(di, |
848 | &mi.un.s.member[j].label, | | 848 | &mi.un.s.member[j].label, |
849 | mi.un.s.member[j].mask); | | 849 | mi.un.s.member[j].mask); |
850 | if (e >= 0) | | 850 | if (e >= 0) |
851 | di->recmask |= 1 << di->rdevmap[e]; | | 851 | di->recmask |= 1 << di->rdevmap[e]; |
852 | } | | 852 | } |
853 | break; | | 853 | break; |
854 | } | | 854 | } |
855 | } | | 855 | } |
856 | return di; | | 856 | return di; |
857 | } | | 857 | } |
858 | | | 858 | |
859 | static int | | 859 | static int |
860 | mixer_oss3_ioctl(int fd, unsigned long com, void *argp) | | 860 | mixer_oss3_ioctl(int fd, unsigned long com, void *argp) |
861 | { | | 861 | { |
862 | struct audiodevinfo *di; | | 862 | struct audiodevinfo *di; |
863 | struct mixer_info *omi; | | 863 | struct mixer_info *omi; |
864 | struct audio_device adev; | | 864 | struct audio_device adev; |
865 | mixer_ctrl_t mc; | | 865 | mixer_ctrl_t mc; |
866 | u_long idat, n; | | 866 | u_long idat, n; |
867 | int i; | | 867 | int i; |
868 | int retval; | | 868 | int retval; |
869 | int l, r, error, e; | | 869 | int l, r, error, e; |
870 | | | 870 | |
871 | idat = 0; | | 871 | idat = 0; |
872 | di = getdevinfo(fd); | | 872 | di = getdevinfo(fd); |
873 | if (di == 0) | | 873 | if (di == 0) |
874 | return -1; | | 874 | return -1; |
875 | | | 875 | |
876 | switch (com) { | | 876 | switch (com) { |
877 | case OSS_GETVERSION: | | 877 | case OSS_GETVERSION: |
878 | idat = SOUND_VERSION; | | 878 | idat = SOUND_VERSION; |
879 | break; | | 879 | break; |
880 | case SOUND_MIXER_INFO: | | 880 | case SOUND_MIXER_INFO: |
881 | case SOUND_OLD_MIXER_INFO: | | 881 | case SOUND_OLD_MIXER_INFO: |
882 | error = ioctl(fd, AUDIO_GETDEV, &adev); | | 882 | error = ioctl(fd, AUDIO_GETDEV, &adev); |
883 | if (error) | | 883 | if (error) |
884 | return (error); | | 884 | return (error); |
885 | omi = argp; | | 885 | omi = argp; |
886 | if (com == SOUND_MIXER_INFO) | | 886 | if (com == SOUND_MIXER_INFO) |
887 | omi->modify_counter = 1; | | 887 | omi->modify_counter = 1; |
888 | strlcpy(omi->id, adev.name, sizeof omi->id); | | 888 | strlcpy(omi->id, adev.name, sizeof omi->id); |
889 | strlcpy(omi->name, adev.name, sizeof omi->name); | | 889 | strlcpy(omi->name, adev.name, sizeof omi->name); |
890 | return 0; | | 890 | return 0; |
891 | case SOUND_MIXER_READ_RECSRC: | | 891 | case SOUND_MIXER_READ_RECSRC: |
892 | if (di->source == -1) { | | 892 | if (di->source == -1) { |
893 | errno = EINVAL; | | 893 | errno = EINVAL; |
894 | return -1; | | 894 | return -1; |
895 | } | | 895 | } |
896 | mc.dev = di->source; | | 896 | mc.dev = di->source; |
897 | if (di->caps & SOUND_CAP_EXCL_INPUT) { | | 897 | if (di->caps & SOUND_CAP_EXCL_INPUT) { |
898 | mc.type = AUDIO_MIXER_ENUM; | | 898 | mc.type = AUDIO_MIXER_ENUM; |
899 | retval = ioctl(fd, AUDIO_MIXER_READ, &mc); | | 899 | retval = ioctl(fd, AUDIO_MIXER_READ, &mc); |
900 | if (retval < 0) | | 900 | if (retval < 0) |
901 | return retval; | | 901 | return retval; |
902 | e = opaque_to_enum(di, NULL, mc.un.ord); | | 902 | e = opaque_to_enum(di, NULL, mc.un.ord); |
903 | if (e >= 0) | | 903 | if (e >= 0) |
904 | idat = 1 << di->rdevmap[e]; | | 904 | idat = 1 << di->rdevmap[e]; |
905 | } else { | | 905 | } else { |
906 | mc.type = AUDIO_MIXER_SET; | | 906 | mc.type = AUDIO_MIXER_SET; |
907 | retval = ioctl(fd, AUDIO_MIXER_READ, &mc); | | 907 | retval = ioctl(fd, AUDIO_MIXER_READ, &mc); |
908 | if (retval < 0) | | 908 | if (retval < 0) |
909 | return retval; | | 909 | return retval; |
910 | e = opaque_to_enum(di, NULL, mc.un.mask); | | 910 | e = opaque_to_enum(di, NULL, mc.un.mask); |
911 | if (e >= 0) | | 911 | if (e >= 0) |
912 | idat = 1 << di->rdevmap[e]; | | 912 | idat = 1 << di->rdevmap[e]; |
913 | } | | 913 | } |
914 | break; | | 914 | break; |
915 | case SOUND_MIXER_READ_DEVMASK: | | 915 | case SOUND_MIXER_READ_DEVMASK: |
916 | idat = di->devmask; | | 916 | idat = di->devmask; |
917 | break; | | 917 | break; |
918 | case SOUND_MIXER_READ_RECMASK: | | 918 | case SOUND_MIXER_READ_RECMASK: |
919 | idat = di->recmask; | | 919 | idat = di->recmask; |
920 | break; | | 920 | break; |
921 | case SOUND_MIXER_READ_STEREODEVS: | | 921 | case SOUND_MIXER_READ_STEREODEVS: |
922 | idat = di->stereomask; | | 922 | idat = di->stereomask; |
923 | break; | | 923 | break; |
924 | case SOUND_MIXER_READ_CAPS: | | 924 | case SOUND_MIXER_READ_CAPS: |
925 | idat = di->caps; | | 925 | idat = di->caps; |
926 | break; | | 926 | break; |
927 | case SOUND_MIXER_WRITE_RECSRC: | | 927 | case SOUND_MIXER_WRITE_RECSRC: |
928 | case SOUND_MIXER_WRITE_R_RECSRC: | | 928 | case SOUND_MIXER_WRITE_R_RECSRC: |
929 | if (di->source == -1) { | | 929 | if (di->source == -1) { |
930 | errno = EINVAL; | | 930 | errno = EINVAL; |
931 | return -1; | | 931 | return -1; |
932 | } | | 932 | } |
933 | mc.dev = di->source; | | 933 | mc.dev = di->source; |
934 | idat = INTARG; | | 934 | idat = INTARG; |
935 | if (di->caps & SOUND_CAP_EXCL_INPUT) { | | 935 | if (di->caps & SOUND_CAP_EXCL_INPUT) { |
936 | mc.type = AUDIO_MIXER_ENUM; | | 936 | mc.type = AUDIO_MIXER_ENUM; |
937 | for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) | | 937 | for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) |
938 | if (idat & (1 << i)) | | 938 | if (idat & (1 << i)) |
939 | break; | | 939 | break; |
940 | if (i >= SOUND_MIXER_NRDEVICES || | | 940 | if (i >= SOUND_MIXER_NRDEVICES || |
941 | di->devmap[i] == -1) { | | 941 | di->devmap[i] == -1) { |
942 | errno = EINVAL; | | 942 | errno = EINVAL; |
943 | return -1; | | 943 | return -1; |
944 | } | | 944 | } |
945 | mc.un.ord = enum_to_ord(di, di->devmap[i]); | | 945 | mc.un.ord = enum_to_ord(di, di->devmap[i]); |
946 | } else { | | 946 | } else { |
947 | mc.type = AUDIO_MIXER_SET; | | 947 | mc.type = AUDIO_MIXER_SET; |
948 | mc.un.mask = 0; | | 948 | mc.un.mask = 0; |
949 | for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) { | | 949 | for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) { |
950 | if (idat & (1 << i)) { | | 950 | if (idat & (1 << i)) { |
951 | if (di->devmap[i] == -1) { | | 951 | if (di->devmap[i] == -1) { |
952 | errno = EINVAL; | | 952 | errno = EINVAL; |
953 | return -1; | | 953 | return -1; |
954 | } | | 954 | } |
955 | mc.un.mask |= | | 955 | mc.un.mask |= |
956 | enum_to_mask(di, di->devmap[i]); | | 956 | enum_to_mask(di, di->devmap[i]); |
957 | } | | 957 | } |
958 | } | | 958 | } |
959 | } | | 959 | } |
960 | return ioctl(fd, AUDIO_MIXER_WRITE, &mc); | | 960 | return ioctl(fd, AUDIO_MIXER_WRITE, &mc); |
961 | default: | | 961 | default: |
962 | if (MIXER_READ(SOUND_MIXER_FIRST) <= com && | | 962 | if (MIXER_READ(SOUND_MIXER_FIRST) <= com && |
963 | com < MIXER_READ(SOUND_MIXER_NRDEVICES)) { | | 963 | com < MIXER_READ(SOUND_MIXER_NRDEVICES)) { |
964 | n = GET_DEV(com); | | 964 | n = GET_DEV(com); |
965 | if (di->devmap[n] == -1) { | | 965 | if (di->devmap[n] == -1) { |
966 | errno = EINVAL; | | 966 | errno = EINVAL; |
967 | return -1; | | 967 | return -1; |
968 | } | | 968 | } |
969 | mc.dev = di->devmap[n]; | | 969 | mc.dev = di->devmap[n]; |
970 | mc.type = AUDIO_MIXER_VALUE; | | 970 | mc.type = AUDIO_MIXER_VALUE; |
971 | doread: | | 971 | doread: |
972 | mc.un.value.num_channels = | | 972 | mc.un.value.num_channels = |
973 | di->stereomask & (1 << (u_int)n) ? 2 : 1; | | 973 | di->stereomask & (1 << (u_int)n) ? 2 : 1; |
974 | retval = ioctl(fd, AUDIO_MIXER_READ, &mc); | | 974 | retval = ioctl(fd, AUDIO_MIXER_READ, &mc); |
975 | if (retval < 0) | | 975 | if (retval < 0) |
976 | return retval; | | 976 | return retval; |
977 | if (mc.type != AUDIO_MIXER_VALUE) { | | 977 | if (mc.type != AUDIO_MIXER_VALUE) { |
978 | errno = EINVAL; | | 978 | errno = EINVAL; |
979 | return -1; | | 979 | return -1; |
980 | } | | 980 | } |
981 | if (mc.un.value.num_channels != 2) { | | 981 | if (mc.un.value.num_channels != 2) { |
982 | l = r = | | 982 | l = r = |
983 | mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; | | 983 | mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; |
984 | } else { | | 984 | } else { |
985 | l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; | | 985 | l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; |
986 | r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; | | 986 | r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; |
987 | } | | 987 | } |
988 | idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); | | 988 | idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); |
989 | break; | | 989 | break; |
990 | } else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com && | | 990 | } else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com && |
991 | com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) || | | 991 | com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) || |
992 | (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && | | 992 | (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && |
993 | com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) { | | 993 | com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) { |
994 | n = GET_DEV(com); | | 994 | n = GET_DEV(com); |
995 | if (di->devmap[n] == -1) { | | 995 | if (di->devmap[n] == -1) { |
996 | errno = EINVAL; | | 996 | errno = EINVAL; |
997 | return -1; | | 997 | return -1; |
998 | } | | 998 | } |
999 | idat = INTARG; | | 999 | idat = INTARG; |
1000 | l = FROM_OSSVOL((u_int)idat & 0xff); | | 1000 | l = FROM_OSSVOL((u_int)idat & 0xff); |
1001 | r = FROM_OSSVOL(((u_int)idat >> 8) & 0xff); | | 1001 | r = FROM_OSSVOL(((u_int)idat >> 8) & 0xff); |
1002 | mc.dev = di->devmap[n]; | | 1002 | mc.dev = di->devmap[n]; |
1003 | mc.type = AUDIO_MIXER_VALUE; | | 1003 | mc.type = AUDIO_MIXER_VALUE; |
1004 | if (di->stereomask & (1 << (u_int)n)) { | | 1004 | if (di->stereomask & (1 << (u_int)n)) { |
1005 | mc.un.value.num_channels = 2; | | 1005 | mc.un.value.num_channels = 2; |
1006 | mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; | | 1006 | mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; |
1007 | mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; | | 1007 | mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; |
1008 | } else { | | 1008 | } else { |
1009 | mc.un.value.num_channels = 1; | | 1009 | mc.un.value.num_channels = 1; |
1010 | mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = | | 1010 | mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = |
1011 | (l + r) / 2; | | 1011 | (l + r) / 2; |
1012 | } | | 1012 | } |
1013 | retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc); | | 1013 | retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc); |
1014 | if (retval < 0) | | 1014 | if (retval < 0) |
1015 | return retval; | | 1015 | return retval; |
1016 | if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && | | 1016 | if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && |
1017 | com < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) | | 1017 | com < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) |
1018 | return 0; | | 1018 | return 0; |
1019 | goto doread; | | 1019 | goto doread; |
1020 | } else { | | 1020 | } else { |
1021 | errno = EINVAL; | | 1021 | errno = EINVAL; |
1022 | return -1; | | 1022 | return -1; |
1023 | } | | 1023 | } |
1024 | } | | 1024 | } |
1025 | INTARG = (int)idat; | | 1025 | INTARG = (int)idat; |
1026 | return 0; | | 1026 | return 0; |
1027 | } | | 1027 | } |
1028 | | | 1028 | |
1029 | static int | | 1029 | static int |
1030 | mixer_oss4_ioctl(int fd, unsigned long com, void *argp) | | 1030 | mixer_oss4_ioctl(int fd, unsigned long com, void *argp) |
1031 | { | | 1031 | { |
1032 | oss_audioinfo *tmpai; | | 1032 | oss_audioinfo *tmpai; |
1033 | oss_card_info *cardinfo; | | 1033 | oss_card_info *cardinfo; |
1034 | oss_mixext *ext; | | 1034 | oss_mixext *ext; |
1035 | oss_mixext_root root; | | 1035 | oss_mixext_root root; |
1036 | oss_mixer_enuminfo *ei; | | 1036 | oss_mixer_enuminfo *ei; |
1037 | oss_mixer_value *mv; | | 1037 | oss_mixer_value *mv; |
1038 | oss_mixerinfo *mi; | | 1038 | oss_mixerinfo *mi; |
1039 | oss_sysinfo sysinfo; | | 1039 | oss_sysinfo sysinfo; |
1040 | dev_t devno; | | 1040 | dev_t devno; |
1041 | struct stat tmpstat; | | 1041 | struct stat tmpstat; |
1042 | struct audio_device dev; | | 1042 | struct audio_device dev; |
1043 | struct audio_format_query fmtq; | | 1043 | struct audio_format_query fmtq; |
1044 | struct mixer_devinfo mdi; | | 1044 | struct mixer_devinfo mdi; |
1045 | struct mixer_ctrl mc; | | 1045 | struct mixer_ctrl mc; |
1046 | char devname[32]; | | 1046 | char devname[32]; |
1047 | size_t len; | | 1047 | size_t len; |
1048 | int newfd = -1, tmperrno; | | 1048 | int newfd = -1, tmperrno; |
1049 | int i, noffs; | | 1049 | int i, noffs; |
1050 | int retval; | | 1050 | int retval; |
1051 | | | 1051 | |
1052 | /* | | 1052 | /* |
1053 | * Note: it is difficult to translate the NetBSD concept of a "set" | | 1053 | * Note: it is difficult to translate the NetBSD concept of a "set" |
1054 | * mixer control type to the OSSv4 API, as far as I can tell. | | 1054 | * mixer control type to the OSSv4 API, as far as I can tell. |
1055 | * | | 1055 | * |
1056 | * This means they are treated like enums, i.e. only one entry in the | | 1056 | * This means they are treated like enums, i.e. only one entry in the |
1057 | * set can be selected at a time. | | 1057 | * set can be selected at a time. |
1058 | */ | | 1058 | */ |
1059 | | | 1059 | |
1060 | switch (com) { | | 1060 | switch (com) { |
1061 | case SNDCTL_AUDIOINFO: | | 1061 | case SNDCTL_AUDIOINFO: |
1062 | /* | | 1062 | /* |
1063 | * SNDCTL_AUDIOINFO_EX is intended for underlying hardware devices | | 1063 | * SNDCTL_AUDIOINFO_EX is intended for underlying hardware devices |
1064 | * that are to be opened in "exclusive mode" (bypassing the normal | | 1064 | * that are to be opened in "exclusive mode" (bypassing the normal |
1065 | * kernel mixer for exclusive control). NetBSD does not support | | 1065 | * kernel mixer for exclusive control). NetBSD does not support |
1066 | * bypassing the kernel mixer, so it's an alias of SNDCTL_AUDIOINFO. | | 1066 | * bypassing the kernel mixer, so it's an alias of SNDCTL_AUDIOINFO. |
1067 | */ | | 1067 | */ |
1068 | case SNDCTL_AUDIOINFO_EX: | | 1068 | case SNDCTL_AUDIOINFO_EX: |
1069 | case SNDCTL_ENGINEINFO: | | 1069 | case SNDCTL_ENGINEINFO: |
1070 | devno = 0; | | 1070 | devno = 0; |
1071 | tmpai = (struct oss_audioinfo*)argp; | | 1071 | tmpai = (struct oss_audioinfo*)argp; |
1072 | if (tmpai == NULL) { | | 1072 | if (tmpai == NULL) { |
1073 | errno = EINVAL; | | 1073 | errno = EINVAL; |
1074 | return -1; | | 1074 | return -1; |
1075 | } | | 1075 | } |
1076 | | | 1076 | |
1077 | /* | | 1077 | /* |
1078 | * If the input device is -1, guess the device related to | | 1078 | * If the input device is -1, guess the device related to |
1079 | * the open mixer device. | | 1079 | * the open mixer device. |
1080 | */ | | 1080 | */ |
1081 | if (tmpai->dev < 0) { | | 1081 | if (tmpai->dev < 0) { |
1082 | fstat(fd, &tmpstat); | | 1082 | fstat(fd, &tmpstat); |
1083 | if ((tmpstat.st_rdev & 0xff00) == 0x2a00) | | 1083 | if ((tmpstat.st_rdev & 0xff00) == 0x2a00) |
1084 | devno = tmpstat.st_rdev & 0xff; | | 1084 | devno = tmpstat.st_rdev & 0xff; |
1085 | if (devno >= 0x80) | | 1085 | if (devno >= 0x80) |
1086 | tmpai->dev = devno & 0x7f; | | 1086 | tmpai->dev = devno & 0x7f; |
1087 | } | | 1087 | } |
1088 | if (tmpai->dev < 0) | | 1088 | if (tmpai->dev < 0) |
1089 | tmpai->dev = 0; | | 1089 | tmpai->dev = 0; |
1090 | | | 1090 | |
1091 | snprintf(tmpai->devnode, sizeof(tmpai->devnode), | | 1091 | snprintf(tmpai->devnode, sizeof(tmpai->devnode), |
1092 | "/dev/audio%d", tmpai->dev); | | 1092 | "/dev/audio%d", tmpai->dev); |
1093 | | | 1093 | |
1094 | if ((newfd = open(tmpai->devnode, O_WRONLY)) < 0) { | | 1094 | if ((newfd = open(tmpai->devnode, O_WRONLY)) < 0) { |
1095 | if ((newfd = open(tmpai->devnode, O_RDONLY)) < 0) { | | 1095 | if ((newfd = open(tmpai->devnode, O_RDONLY)) < 0) { |
1096 | return newfd; | | 1096 | return newfd; |
1097 | } | | 1097 | } |
1098 | } | | 1098 | } |
1099 | | | 1099 | |
1100 | retval = ioctl(newfd, AUDIO_GETDEV, &dev); | | 1100 | retval = ioctl(newfd, AUDIO_GETDEV, &dev); |
1101 | if (retval < 0) { | | 1101 | if (retval < 0) { |
1102 | tmperrno = errno; | | 1102 | tmperrno = errno; |
1103 | close(newfd); | | 1103 | close(newfd); |
1104 | errno = tmperrno; | | 1104 | errno = tmperrno; |
1105 | return retval; | | 1105 | return retval; |
1106 | } | | 1106 | } |
1107 | if (getcaps(newfd, &tmpai->caps) < 0) { | | 1107 | if (getcaps(newfd, &tmpai->caps) < 0) { |
1108 | tmperrno = errno; | | 1108 | tmperrno = errno; |
1109 | close(newfd); | | 1109 | close(newfd); |
1110 | errno = tmperrno; | | 1110 | errno = tmperrno; |
1111 | return retval; | | 1111 | return retval; |
1112 | } | | 1112 | } |
1113 | snprintf(tmpai->name, sizeof(tmpai->name), | | 1113 | snprintf(tmpai->name, sizeof(tmpai->name), |
1114 | "%s %s", dev.name, dev.version); | | 1114 | "%s %s", dev.name, dev.version); |
1115 | tmpai->busy = 0; | | 1115 | tmpai->busy = 0; |
1116 | tmpai->pid = -1; | | 1116 | tmpai->pid = -1; |
1117 | ioctl(newfd, SNDCTL_DSP_GETFMTS, &tmpai->iformats); | | 1117 | ioctl(newfd, SNDCTL_DSP_GETFMTS, &tmpai->iformats); |
1118 | tmpai->oformats = tmpai->iformats; | | 1118 | tmpai->oformats = tmpai->iformats; |
1119 | tmpai->magic = -1; /* reserved for "internal use" */ | | 1119 | tmpai->magic = -1; /* reserved for "internal use" */ |
1120 | memset(tmpai->cmd, 0, sizeof(tmpai->cmd)); | | 1120 | memset(tmpai->cmd, 0, sizeof(tmpai->cmd)); |
1121 | tmpai->card_number = -1; | | 1121 | tmpai->card_number = -1; |
1122 | memset(tmpai->song_name, 0, | | 1122 | memset(tmpai->song_name, 0, |
1123 | sizeof(tmpai->song_name)); | | 1123 | sizeof(tmpai->song_name)); |
1124 | memset(tmpai->label, 0, sizeof(tmpai->label)); | | 1124 | memset(tmpai->label, 0, sizeof(tmpai->label)); |
1125 | tmpai->port_number = 0; | | 1125 | tmpai->port_number = 0; |
1126 | tmpai->mixer_dev = tmpai->dev; | | 1126 | tmpai->mixer_dev = tmpai->dev; |
1127 | tmpai->legacy_device = tmpai->dev; | | 1127 | tmpai->legacy_device = tmpai->dev; |
1128 | tmpai->enabled = 1; | | 1128 | tmpai->enabled = 1; |
1129 | tmpai->flags = -1; /* reserved for "future versions" */ | | 1129 | tmpai->flags = -1; /* reserved for "future versions" */ |
1130 | tmpai->min_rate = 1000; | | 1130 | tmpai->min_rate = 1000; |
1131 | tmpai->max_rate = 192000; | | 1131 | tmpai->max_rate = 192000; |
1132 | tmpai->nrates = 0; | | 1132 | tmpai->nrates = 0; |
1133 | tmpai->min_channels = 1; | | 1133 | tmpai->min_channels = 1; |
1134 | tmpai->max_channels = 2; | | 1134 | tmpai->max_channels = 2; |
1135 | for (fmtq.index = 0; | | 1135 | for (fmtq.index = 0; |
1136 | ioctl(newfd, AUDIO_QUERYFORMAT, &fmtq) != -1; ++fmtq.index) { | | 1136 | ioctl(newfd, AUDIO_QUERYFORMAT, &fmtq) != -1; ++fmtq.index) { |
1137 | if (fmtq.fmt.channels > (unsigned)tmpai->max_channels) | | 1137 | if (fmtq.fmt.channels > (unsigned)tmpai->max_channels) |
1138 | tmpai->max_channels = fmtq.fmt.channels; | | 1138 | tmpai->max_channels = fmtq.fmt.channels; |
1139 | } | | 1139 | } |
1140 | tmpai->binding = -1; /* reserved for "future versions" */ | | 1140 | tmpai->binding = -1; /* reserved for "future versions" */ |
1141 | tmpai->rate_source = -1; | | 1141 | tmpai->rate_source = -1; |
1142 | /* | | 1142 | /* |
1143 | * 'handle' is supposed to be globally unique. The closest | | 1143 | * 'handle' is supposed to be globally unique. The closest |
1144 | * we have to that is probably device nodes. | | 1144 | * we have to that is probably device nodes. |
1145 | */ | | 1145 | */ |
1146 | strlcpy(tmpai->handle, tmpai->devnode, | | 1146 | strlcpy(tmpai->handle, tmpai->devnode, |
1147 | sizeof(tmpai->handle)); | | 1147 | sizeof(tmpai->handle)); |
1148 | tmpai->next_play_engine = 0; | | 1148 | tmpai->next_play_engine = 0; |
1149 | tmpai->next_rec_engine = 0; | | 1149 | tmpai->next_rec_engine = 0; |
1150 | argp = tmpai; | | 1150 | argp = tmpai; |
1151 | close(newfd); | | 1151 | close(newfd); |
1152 | break; | | 1152 | break; |
1153 | case SNDCTL_CARDINFO: | | 1153 | case SNDCTL_CARDINFO: |
1154 | cardinfo = (oss_card_info *)argp; | | 1154 | cardinfo = (oss_card_info *)argp; |
1155 | if (cardinfo == NULL) { | | 1155 | if (cardinfo == NULL) { |
1156 | errno = EINVAL; | | 1156 | errno = EINVAL; |
1157 | return -1; | | 1157 | return -1; |
1158 | } | | 1158 | } |
1159 | if (cardinfo->card != -1) { | | 1159 | if (cardinfo->card != -1) { |
1160 | snprintf(devname, sizeof(devname), | | 1160 | snprintf(devname, sizeof(devname), |
1161 | "/dev/audio%d", cardinfo->card); | | 1161 | "/dev/audio%d", cardinfo->card); |
1162 | newfd = open(devname, O_RDONLY); | | 1162 | newfd = open(devname, O_RDONLY); |
1163 | if (newfd < 0) | | 1163 | if (newfd < 0) |
1164 | return newfd; | | 1164 | return newfd; |
1165 | } else { | | 1165 | } else { |
1166 | newfd = fd; | | 1166 | newfd = fd; |
1167 | } | | 1167 | } |
1168 | retval = ioctl(newfd, AUDIO_GETDEV, &dev); | | 1168 | retval = ioctl(newfd, AUDIO_GETDEV, &dev); |
1169 | tmperrno = errno; | | 1169 | tmperrno = errno; |
1170 | if (newfd != fd) | | 1170 | if (newfd != fd) |
1171 | close(newfd); | | 1171 | close(newfd); |
1172 | if (retval < 0) { | | 1172 | if (retval < 0) { |
1173 | errno = tmperrno; | | 1173 | errno = tmperrno; |
1174 | return retval; | | 1174 | return retval; |
1175 | } | | 1175 | } |
1176 | strlcpy(cardinfo->shortname, dev.name, | | 1176 | strlcpy(cardinfo->shortname, dev.name, |
1177 | sizeof(cardinfo->shortname)); | | 1177 | sizeof(cardinfo->shortname)); |
1178 | snprintf(cardinfo->longname, sizeof(cardinfo->longname), | | 1178 | snprintf(cardinfo->longname, sizeof(cardinfo->longname), |
1179 | "%s %s %s", dev.name, dev.version, dev.config); | | 1179 | "%s %s %s", dev.name, dev.version, dev.config); |
1180 | memset(cardinfo->hw_info, 0, sizeof(cardinfo->hw_info)); | | 1180 | memset(cardinfo->hw_info, 0, sizeof(cardinfo->hw_info)); |
1181 | /* | | 1181 | /* |
1182 | * OSSv4 does not document this ioctl, and claims it should | | 1182 | * OSSv4 does not document this ioctl, and claims it should |
1183 | * not be used by applications and is provided for "utiltiy | | 1183 | * not be used by applications and is provided for "utiltiy |
1184 | * programs included in OSS". We follow the Solaris | | 1184 | * programs included in OSS". We follow the Solaris |
1185 | * implementation (which is documented) and leave these fields | | 1185 | * implementation (which is documented) and leave these fields |
1186 | * unset. | | 1186 | * unset. |
1187 | */ | | 1187 | */ |
1188 | cardinfo->flags = 0; | | 1188 | cardinfo->flags = 0; |
1189 | cardinfo->intr_count = 0; | | 1189 | cardinfo->intr_count = 0; |
1190 | cardinfo->ack_count = 0; | | 1190 | cardinfo->ack_count = 0; |
1191 | break; | | 1191 | break; |
1192 | case SNDCTL_SYSINFO: | | 1192 | case SNDCTL_SYSINFO: |
1193 | memset(&sysinfo, 0, sizeof(sysinfo)); | | 1193 | memset(&sysinfo, 0, sizeof(sysinfo)); |
1194 | strlcpy(sysinfo.product, | | 1194 | strlcpy(sysinfo.product, |
1195 | "OSS/NetBSD", sizeof(sysinfo.product)); | | 1195 | "OSS/NetBSD", sizeof(sysinfo.product)); |
1196 | strlcpy(sysinfo.version, | | 1196 | strlcpy(sysinfo.version, |
1197 | "4.01", sizeof(sysinfo.version)); | | 1197 | "4.01", sizeof(sysinfo.version)); |
1198 | strlcpy(sysinfo.license, | | 1198 | strlcpy(sysinfo.license, |
1199 | "BSD", sizeof(sysinfo.license)); | | 1199 | "BSD", sizeof(sysinfo.license)); |
1200 | sysinfo.versionnum = SOUND_VERSION; | | 1200 | sysinfo.versionnum = SOUND_VERSION; |
1201 | sysinfo.numaudios = | | 1201 | sysinfo.numaudios = |
1202 | sysinfo.numcards = | | 1202 | sysinfo.numcards = |
1203 | getaudiocount(); | | 1203 | getaudiocount(); |
1204 | sysinfo.numaudioengines = 1; | | 1204 | sysinfo.numaudioengines = 1; |
1205 | sysinfo.numsynths = 1; | | 1205 | sysinfo.numsynths = 1; |
1206 | sysinfo.nummidis = -1; | | 1206 | sysinfo.nummidis = -1; |
1207 | sysinfo.numtimers = -1; | | 1207 | sysinfo.numtimers = -1; |
1208 | sysinfo.nummixers = getmixercount(); | | 1208 | sysinfo.nummixers = getmixercount(); |
1209 | *(struct oss_sysinfo *)argp = sysinfo; | | 1209 | *(struct oss_sysinfo *)argp = sysinfo; |
1210 | break; | | 1210 | break; |
1211 | case SNDCTL_MIXERINFO: | | 1211 | case SNDCTL_MIXERINFO: |
1212 | mi = (oss_mixerinfo *)argp; | | 1212 | mi = (oss_mixerinfo *)argp; |
1213 | if (mi == NULL) { | | 1213 | if (mi == NULL) { |
1214 | errno = EINVAL; | | 1214 | errno = EINVAL; |
1215 | return -1; | | 1215 | return -1; |
1216 | } | | 1216 | } |
1217 | snprintf(devname, sizeof(devname), "/dev/mixer%d", mi->dev); | | 1217 | snprintf(devname, sizeof(devname), "/dev/mixer%d", mi->dev); |
1218 | if ((newfd = open(devname, O_RDONLY)) < 0) | | 1218 | if ((newfd = open(devname, O_RDONLY)) < 0) |
1219 | return newfd; | | 1219 | return newfd; |
1220 | retval = ioctl(newfd, AUDIO_GETDEV, &dev); | | 1220 | retval = ioctl(newfd, AUDIO_GETDEV, &dev); |
1221 | if (retval < 0) { | | 1221 | if (retval < 0) { |
1222 | tmperrno = errno; | | 1222 | tmperrno = errno; |
1223 | close(newfd); | | 1223 | close(newfd); |
1224 | errno = tmperrno; | | 1224 | errno = tmperrno; |
1225 | return retval; | | 1225 | return retval; |
1226 | } | | 1226 | } |
1227 | strlcpy(mi->id, devname, sizeof(mi->id)); | | 1227 | strlcpy(mi->id, devname, sizeof(mi->id)); |
1228 | snprintf(mi->name, sizeof(mi->name), | | 1228 | snprintf(mi->name, sizeof(mi->name), |
1229 | "%s %s", dev.name, dev.version); | | 1229 | "%s %s", dev.name, dev.version); |
1230 | mi->card_number = mi->dev; | | 1230 | mi->card_number = mi->dev; |
1231 | mi->port_number = 0; | | 1231 | mi->port_number = 0; |
1232 | mi->magic = 0; | | 1232 | mi->magic = 0; |
1233 | mi->enabled = 1; | | 1233 | mi->enabled = 1; |
1234 | mi->caps = 0; | | 1234 | mi->caps = 0; |
1235 | mi->flags = 0; | | 1235 | mi->flags = 0; |
1236 | mi->nrext = getmixercontrolcount(newfd) + 1; | | 1236 | mi->nrext = getmixercontrolcount(newfd) + 1; |
1237 | mi->priority = UCHAR_MAX - mi->dev; | | 1237 | mi->priority = UCHAR_MAX - mi->dev; |
1238 | strlcpy(mi->devnode, devname, sizeof(mi->devnode)); | | 1238 | strlcpy(mi->devnode, devname, sizeof(mi->devnode)); |
1239 | mi->legacy_device = mi->dev; | | 1239 | mi->legacy_device = mi->dev; |
1240 | break; | | 1240 | break; |
1241 | case SNDCTL_MIX_DESCRIPTION: | | 1241 | case SNDCTL_MIX_DESCRIPTION: |
1242 | /* No description available. */ | | 1242 | /* No description available. */ |
1243 | errno = ENOSYS; | | 1243 | errno = ENOSYS; |
1244 | return -1; | | 1244 | return -1; |
1245 | case SNDCTL_MIX_NRMIX: | | 1245 | case SNDCTL_MIX_NRMIX: |
1246 | INTARG = getmixercount(); | | 1246 | INTARG = getmixercount(); |
1247 | break; | | 1247 | break; |
1248 | case SNDCTL_MIX_NREXT: | | 1248 | case SNDCTL_MIX_NREXT: |
1249 | snprintf(devname, sizeof(devname), "/dev/mixer%d", INTARG); | | 1249 | snprintf(devname, sizeof(devname), "/dev/mixer%d", INTARG); |
1250 | if ((newfd = open(devname, O_RDONLY)) < 0) | | 1250 | if ((newfd = open(devname, O_RDONLY)) < 0) |
1251 | return newfd; | | 1251 | return newfd; |
1252 | INTARG = getmixercontrolcount(newfd) + 1; | | 1252 | INTARG = getmixercontrolcount(newfd) + 1; |
1253 | close(newfd); | | 1253 | close(newfd); |
1254 | break; | | 1254 | break; |
1255 | case SNDCTL_MIX_EXTINFO: | | 1255 | case SNDCTL_MIX_EXTINFO: |
1256 | ext = (oss_mixext *)argp; | | 1256 | ext = (oss_mixext *)argp; |
1257 | snprintf(devname, sizeof(devname), "/dev/mixer%d", ext->dev); | | 1257 | snprintf(devname, sizeof(devname), "/dev/mixer%d", ext->dev); |
1258 | if ((newfd = open(devname, O_RDONLY)) < 0) | | 1258 | if ((newfd = open(devname, O_RDONLY)) < 0) |
1259 | return newfd; | | 1259 | return newfd; |
1260 | if (ext->ctrl == 0) { | | 1260 | if (ext->ctrl == 0) { |
1261 | /* | | 1261 | /* |
1262 | * NetBSD has no concept of a "root mixer control", but | | 1262 | * NetBSD has no concept of a "root mixer control", but |
1263 | * OSSv4 requires one to work. We fake one at 0 and | | 1263 | * OSSv4 requires one to work. We fake one at 0 and |
1264 | * simply add 1 to all real control indexes. | | 1264 | * simply add 1 to all real control indexes. |
1265 | */ | | 1265 | */ |
1266 | retval = ioctl(newfd, AUDIO_GETDEV, &dev); | | 1266 | retval = ioctl(newfd, AUDIO_GETDEV, &dev); |
1267 | tmperrno = errno; | | 1267 | tmperrno = errno; |
1268 | close(newfd); | | 1268 | close(newfd); |
1269 | if (retval < 0) { | | 1269 | if (retval < 0) { |
1270 | errno = tmperrno; | | 1270 | errno = tmperrno; |
1271 | return -1; | | 1271 | return -1; |
1272 | } | | 1272 | } |
1273 | memset(&root, 0, sizeof(root)); | | 1273 | memset(&root, 0, sizeof(root)); |
1274 | strlcpy(root.id, devname, sizeof(root.id)); | | 1274 | strlcpy(root.id, devname, sizeof(root.id)); |
1275 | snprintf(root.name, sizeof(root.name), | | 1275 | snprintf(root.name, sizeof(root.name), |
1276 | "%s %s", dev.name, dev.version); | | 1276 | "%s %s", dev.name, dev.version); |
1277 | strlcpy(ext->id, devname, sizeof(ext->id)); | | 1277 | strlcpy(ext->id, devname, sizeof(ext->id)); |
1278 | snprintf(ext->extname, sizeof(ext->extname), | | 1278 | snprintf(ext->extname, sizeof(ext->extname), |
1279 | "%s %s", dev.name, dev.version); | | 1279 | "%s %s", dev.name, dev.version); |
1280 | strlcpy(ext->extname, "root", sizeof(ext->extname)); | | 1280 | strlcpy(ext->extname, "root", sizeof(ext->extname)); |
1281 | ext->type = MIXT_DEVROOT; | | 1281 | ext->type = MIXT_DEVROOT; |
1282 | ext->minvalue = 0; | | 1282 | ext->minvalue = 0; |
1283 | ext->maxvalue = 0; | | 1283 | ext->maxvalue = 0; |
1284 | ext->flags = 0; | | 1284 | ext->flags = 0; |
1285 | ext->parent = -1; | | 1285 | ext->parent = -1; |
1286 | ext->control_no = -1; | | 1286 | ext->control_no = -1; |
1287 | ext->update_counter = 0; | | 1287 | ext->update_counter = 0; |
1288 | ext->rgbcolor = 0; | | 1288 | ext->rgbcolor = 0; |
1289 | memcpy(&ext->data, &root, | | 1289 | memcpy(&ext->data, &root, |
1290 | sizeof(root) > sizeof(ext->data) ? | | 1290 | sizeof(root) > sizeof(ext->data) ? |
1291 | sizeof(ext->data) : sizeof(root)); | | 1291 | sizeof(ext->data) : sizeof(root)); |
1292 | return 0; | | 1292 | return 0; |
1293 | } | | 1293 | } |
1294 | mdi.index = ext->ctrl - 1; | | 1294 | mdi.index = ext->ctrl - 1; |
1295 | retval = ioctl(newfd, AUDIO_MIXER_DEVINFO, &mdi); | | 1295 | retval = ioctl(newfd, AUDIO_MIXER_DEVINFO, &mdi); |
1296 | if (retval < 0) { | | 1296 | if (retval < 0) { |
1297 | tmperrno = errno; | | 1297 | tmperrno = errno; |
1298 | close(newfd); | | 1298 | close(newfd); |
1299 | errno = tmperrno; | | 1299 | errno = tmperrno; |
1300 | return retval; | | 1300 | return retval; |
1301 | } | | 1301 | } |
1302 | ext->flags = MIXF_READABLE | MIXF_WRITEABLE | MIXF_POLL; | | 1302 | ext->flags = MIXF_READABLE | MIXF_WRITEABLE | MIXF_POLL; |
1303 | ext->parent = mdi.mixer_class + 1; | | 1303 | ext->parent = mdi.mixer_class + 1; |
1304 | strlcpy(ext->id, mdi.label.name, sizeof(ext->id)); | | 1304 | strlcpy(ext->id, mdi.label.name, sizeof(ext->id)); |
1305 | strlcpy(ext->extname, mdi.label.name, sizeof(ext->extname)); | | 1305 | strlcpy(ext->extname, mdi.label.name, sizeof(ext->extname)); |
1306 | len = strlen(ext->extname); | | 1306 | len = strlen(ext->extname); |
1307 | memset(ext->data, 0, sizeof(ext->data)); | | 1307 | memset(ext->data, 0, sizeof(ext->data)); |
1308 | ext->control_no = -1; | | 1308 | ext->control_no = -1; |
1309 | ext->update_counter = 0; | | 1309 | ext->update_counter = 0; |
1310 | ext->rgbcolor = 0; | | 1310 | ext->rgbcolor = 0; |
1311 | switch (mdi.type) { | | 1311 | switch (mdi.type) { |
1312 | case AUDIO_MIXER_CLASS: | | 1312 | case AUDIO_MIXER_CLASS: |
1313 | ext->type = MIXT_GROUP; | | 1313 | ext->type = MIXT_GROUP; |
1314 | ext->parent = 0; | | 1314 | ext->parent = 0; |
1315 | ext->minvalue = 0; | | 1315 | ext->minvalue = 0; |
1316 | ext->maxvalue = 0; | | 1316 | ext->maxvalue = 0; |
1317 | break; | | 1317 | break; |
1318 | case AUDIO_MIXER_ENUM: | | 1318 | case AUDIO_MIXER_ENUM: |
1319 | ext->maxvalue = mdi.un.e.num_mem; | | 1319 | ext->maxvalue = mdi.un.e.num_mem; |
1320 | ext->minvalue = 0; | | 1320 | ext->minvalue = 0; |
1321 | for (i = 0; i < mdi.un.e.num_mem; ++i) { | | 1321 | for (i = 0; i < mdi.un.e.num_mem; ++i) { |
1322 | ext->enum_present[i / 8] |= (1 << (i % 8)); | | 1322 | ext->enum_present[i / 8] |= (1 << (i % 8)); |
1323 | } | | 1323 | } |
1324 | if (mdi.un.e.num_mem == 2) { | | 1324 | if (mdi.un.e.num_mem == 2) { |
1325 | if (!strcmp(mdi.un.e.member[0].label.name, AudioNoff) && | | 1325 | if (!strcmp(mdi.un.e.member[0].label.name, AudioNoff) && |
1326 | !strcmp(mdi.un.e.member[1].label.name, AudioNon)) { | | 1326 | !strcmp(mdi.un.e.member[1].label.name, AudioNon)) { |
1327 | ext->type = MIXT_MUTE; | | 1327 | ext->type = MIXT_MUTE; |
1328 | } else { | | 1328 | } else { |
1329 | ext->type = MIXT_ENUM; | | 1329 | ext->type = MIXT_ENUM; |
1330 | } | | 1330 | } |
1331 | } else { | | 1331 | } else { |
1332 | ext->type = MIXT_ENUM; | | 1332 | ext->type = MIXT_ENUM; |
1333 | } | | 1333 | } |
1334 | break; | | 1334 | break; |
1335 | case AUDIO_MIXER_SET: | | 1335 | case AUDIO_MIXER_SET: |
1336 | ext->maxvalue = mdi.un.s.num_mem; | | 1336 | ext->maxvalue = mdi.un.s.num_mem; |
1337 | ext->minvalue = 0; | | 1337 | ext->minvalue = 0; |
1338 | #ifdef notyet | | 1338 | #ifdef notyet |
1339 | /* | | 1339 | /* |
1340 | * XXX: This is actually the correct type for "set" | | 1340 | * XXX: This is actually the correct type for "set" |
1341 | * controls, but it seems no real world software | | 1341 | * controls, but it seems no real world software |
1342 | * supports it. The only documentation exists in | | 1342 | * supports it. The only documentation exists in |
1343 | * the OSSv4 headers and describes it as "reserved | | 1343 | * the OSSv4 headers and describes it as "reserved |
1344 | * for Sun's implementation". | | 1344 | * for Sun's implementation". |
1345 | */ | | 1345 | */ |
1346 | ext->type = MIXT_ENUM_MULTI; | | 1346 | ext->type = MIXT_ENUM_MULTI; |
1347 | #else | | 1347 | #else |
1348 | ext->type = MIXT_ENUM; | | 1348 | ext->type = MIXT_ENUM; |
1349 | #endif | | 1349 | #endif |
1350 | for (i = 0; i < mdi.un.s.num_mem; ++i) { | | 1350 | for (i = 0; i < mdi.un.s.num_mem; ++i) { |
1351 | ext->enum_present[i / 8] |= (1 << (i % 8)); | | 1351 | ext->enum_present[i / 8] |= (1 << (i % 8)); |
1352 | } | | 1352 | } |
1353 | break; | | 1353 | break; |
1354 | case AUDIO_MIXER_VALUE: | | 1354 | case AUDIO_MIXER_VALUE: |
1355 | ext->maxvalue = UCHAR_MAX + 1; | | 1355 | ext->maxvalue = UCHAR_MAX + 1; |
1356 | ext->minvalue = 0; | | 1356 | ext->minvalue = 0; |
1357 | if (mdi.un.v.num_channels == 2) { | | 1357 | if (mdi.un.v.num_channels == 2) { |
1358 | ext->type = MIXT_STEREOSLIDER; | | 1358 | ext->type = MIXT_STEREOSLIDER; |
1359 | } else { | | 1359 | } else { |
1360 | ext->type = MIXT_MONOSLIDER; | | 1360 | ext->type = MIXT_MONOSLIDER; |
1361 | } | | 1361 | } |
1362 | break; | | 1362 | break; |
1363 | } | | 1363 | } |
1364 | close(newfd); | | 1364 | close(newfd); |
1365 | break; | | 1365 | break; |
1366 | case SNDCTL_MIX_ENUMINFO: | | 1366 | case SNDCTL_MIX_ENUMINFO: |
1367 | ei = (oss_mixer_enuminfo *)argp; | | 1367 | ei = (oss_mixer_enuminfo *)argp; |
1368 | if (ei == NULL) { | | 1368 | if (ei == NULL) { |
1369 | errno = EINVAL; | | 1369 | errno = EINVAL; |
1370 | return -1; | | 1370 | return -1; |
1371 | } | | 1371 | } |
1372 | if (ei->ctrl == 0) { | | 1372 | if (ei->ctrl == 0) { |
1373 | errno = EINVAL; | | 1373 | errno = EINVAL; |
1374 | return -1; | | 1374 | return -1; |
1375 | } | | 1375 | } |
1376 | snprintf(devname, sizeof(devname), "/dev/mixer%d", ei->dev); | | 1376 | snprintf(devname, sizeof(devname), "/dev/mixer%d", ei->dev); |
1377 | if ((newfd = open(devname, O_RDONLY)) < 0) | | 1377 | if ((newfd = open(devname, O_RDONLY)) < 0) |
1378 | return newfd; | | 1378 | return newfd; |
1379 | mdi.index = ei->ctrl - 1; | | 1379 | mdi.index = ei->ctrl - 1; |
1380 | retval = ioctl(newfd, AUDIO_MIXER_DEVINFO, &mdi); | | 1380 | retval = ioctl(newfd, AUDIO_MIXER_DEVINFO, &mdi); |
1381 | tmperrno = errno; | | 1381 | tmperrno = errno; |
1382 | close(newfd); | | 1382 | close(newfd); |
1383 | if (retval < 0) { | | 1383 | if (retval < 0) { |
1384 | errno = tmperrno; | | 1384 | errno = tmperrno; |
1385 | return retval; | | 1385 | return retval; |
1386 | } | | 1386 | } |
1387 | ei->version = 0; | | 1387 | ei->version = 0; |
1388 | switch (mdi.type) { | | 1388 | switch (mdi.type) { |
1389 | case AUDIO_MIXER_ENUM: | | 1389 | case AUDIO_MIXER_ENUM: |
1390 | ei->nvalues = mdi.un.e.num_mem; | | 1390 | ei->nvalues = mdi.un.e.num_mem; |
1391 | noffs = 0; | | 1391 | noffs = 0; |
1392 | for (i = 0; i < ei->nvalues; ++i) { | | 1392 | for (i = 0; i < ei->nvalues; ++i) { |
1393 | ei->strindex[i] = noffs; | | 1393 | ei->strindex[i] = noffs; |
1394 | len = strlen(mdi.un.e.member[i].label.name) + 1; | | 1394 | len = strlen(mdi.un.e.member[i].label.name) + 1; |
1395 | if ((noffs + len) >= sizeof(ei->strings)) { | | 1395 | if ((noffs + len) >= sizeof(ei->strings)) { |
1396 | errno = ENOMEM; | | 1396 | errno = ENOMEM; |
1397 | return -1; | | 1397 | return -1; |
1398 | } | | 1398 | } |
1399 | memcpy(ei->strings + noffs, | | 1399 | memcpy(ei->strings + noffs, |
1400 | mdi.un.e.member[i].label.name, len); | | 1400 | mdi.un.e.member[i].label.name, len); |
1401 | noffs += len; | | 1401 | noffs += len; |
1402 | } | | 1402 | } |
1403 | break; | | 1403 | break; |
1404 | case AUDIO_MIXER_SET: | | 1404 | case AUDIO_MIXER_SET: |
1405 | ei->nvalues = mdi.un.s.num_mem; | | 1405 | ei->nvalues = mdi.un.s.num_mem; |
1406 | noffs = 0; | | 1406 | noffs = 0; |
1407 | for (i = 0; i < ei->nvalues; ++i) { | | 1407 | for (i = 0; i < ei->nvalues; ++i) { |
1408 | ei->strindex[i] = noffs; | | 1408 | ei->strindex[i] = noffs; |
1409 | len = strlen(mdi.un.s.member[i].label.name) + 1; | | 1409 | len = strlen(mdi.un.s.member[i].label.name) + 1; |
1410 | if ((noffs + len) >= sizeof(ei->strings)) { | | 1410 | if ((noffs + len) >= sizeof(ei->strings)) { |
1411 | errno = ENOMEM; | | 1411 | errno = ENOMEM; |
1412 | return -1; | | 1412 | return -1; |
1413 | } | | 1413 | } |
1414 | memcpy(ei->strings + noffs, | | 1414 | memcpy(ei->strings + noffs, |
1415 | mdi.un.s.member[i].label.name, len); | | 1415 | mdi.un.s.member[i].label.name, len); |
1416 | noffs += len; | | 1416 | noffs += len; |
1417 | } | | 1417 | } |
1418 | break; | | 1418 | break; |
1419 | default: | | 1419 | default: |
1420 | errno = EINVAL; | | 1420 | errno = EINVAL; |
1421 | return -1; | | 1421 | return -1; |
1422 | } | | 1422 | } |
1423 | break; | | 1423 | break; |
1424 | case SNDCTL_MIX_WRITE: | | 1424 | case SNDCTL_MIX_WRITE: |
1425 | mv = (oss_mixer_value *)argp; | | 1425 | mv = (oss_mixer_value *)argp; |
1426 | if (mv == NULL) { | | 1426 | if (mv == NULL) { |
1427 | errno = EINVAL; | | 1427 | errno = EINVAL; |
1428 | return -1; | | 1428 | return -1; |
1429 | } | | 1429 | } |
1430 | if (mv->ctrl == 0) { | | 1430 | if (mv->ctrl == 0) { |
1431 | errno = EINVAL; | | 1431 | errno = EINVAL; |
1432 | return -1; | | 1432 | return -1; |
1433 | } | | 1433 | } |
1434 | snprintf(devname, sizeof(devname), "/dev/mixer%d", mv->dev); | | 1434 | snprintf(devname, sizeof(devname), "/dev/mixer%d", mv->dev); |
1435 | if ((newfd = open(devname, O_RDWR)) < 0) | | 1435 | if ((newfd = open(devname, O_RDWR)) < 0) |
1436 | return newfd; | | 1436 | return newfd; |
1437 | mdi.index = mc.dev = mv->ctrl - 1; | | 1437 | mdi.index = mc.dev = mv->ctrl - 1; |
1438 | retval = ioctl(newfd, AUDIO_MIXER_DEVINFO, &mdi); | | 1438 | retval = ioctl(newfd, AUDIO_MIXER_DEVINFO, &mdi); |
1439 | if (retval < 0) { | | 1439 | if (retval < 0) { |
1440 | tmperrno = errno; | | 1440 | tmperrno = errno; |
1441 | close(newfd); | | 1441 | close(newfd); |
1442 | errno = tmperrno; | | 1442 | errno = tmperrno; |
1443 | return retval; | | 1443 | return retval; |
1444 | } | | 1444 | } |
| | | 1445 | mc.type = mdi.type; |
1445 | switch (mdi.type) { | | 1446 | switch (mdi.type) { |
1446 | case AUDIO_MIXER_ENUM: | | 1447 | case AUDIO_MIXER_ENUM: |
1447 | if (mv->value >= mdi.un.e.num_mem) { | | 1448 | if (mv->value >= mdi.un.e.num_mem) { |
1448 | close(newfd); | | 1449 | close(newfd); |
1449 | errno = EINVAL; | | 1450 | errno = EINVAL; |
1450 | return -1; | | 1451 | return -1; |
1451 | } | | 1452 | } |
1452 | mc.un.ord = mdi.un.e.member[mv->value].ord; | | 1453 | mc.un.ord = mdi.un.e.member[mv->value].ord; |
1453 | break; | | 1454 | break; |
1454 | case AUDIO_MIXER_SET: | | 1455 | case AUDIO_MIXER_SET: |
1455 | if (mv->value >= mdi.un.s.num_mem) { | | 1456 | if (mv->value >= mdi.un.s.num_mem) { |
1456 | close(newfd); | | 1457 | close(newfd); |
1457 | errno = EINVAL; | | 1458 | errno = EINVAL; |
1458 | return -1; | | 1459 | return -1; |
1459 | } | | 1460 | } |
1460 | #ifdef notyet | | 1461 | #ifdef notyet |
1461 | mc.un.mask = 0; | | 1462 | mc.un.mask = 0; |
1462 | for (i = 0; i < mdi.un.s.num_mem; ++i) { | | 1463 | for (i = 0; i < mdi.un.s.num_mem; ++i) { |
1463 | if (mv->value & (1 << i)) { | | 1464 | if (mv->value & (1 << i)) { |
1464 | mc.un.mask |= mdi.un.s.member[mv->value].mask; | | 1465 | mc.un.mask |= mdi.un.s.member[mv->value].mask; |
1465 | } | | 1466 | } |
1466 | } | | 1467 | } |
1467 | #else | | 1468 | #else |
1468 | mc.un.mask = mdi.un.s.member[mv->value].mask; | | 1469 | mc.un.mask = mdi.un.s.member[mv->value].mask; |
1469 | #endif | | 1470 | #endif |
1470 | break; | | 1471 | break; |
1471 | case AUDIO_MIXER_VALUE: | | 1472 | case AUDIO_MIXER_VALUE: |
| | | 1473 | mc.un.value.num_channels = mdi.un.v.num_channels; |
1472 | if (mdi.un.v.num_channels != 2) { | | 1474 | if (mdi.un.v.num_channels != 2) { |
1473 | for (i = 0; i < mdi.un.v.num_channels; ++i) { | | 1475 | for (i = 0; i < mdi.un.v.num_channels; ++i) { |
1474 | mc.un.value.level[i] = mv->value; | | 1476 | mc.un.value.level[i] = mv->value; |
1475 | } | | 1477 | } |
1476 | } else { | | 1478 | } else { |
1477 | mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = | | 1479 | mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = |
1478 | (mv->value >> 0) & 0xFF; | | 1480 | (mv->value >> 0) & 0xFF; |
1479 | mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = | | 1481 | mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = |
1480 | (mv->value >> 8) & 0xFF; | | 1482 | (mv->value >> 8) & 0xFF; |
1481 | } | | 1483 | } |
1482 | break; | | 1484 | break; |
1483 | } | | 1485 | } |
1484 | retval = ioctl(newfd, AUDIO_MIXER_WRITE, &mc); | | 1486 | retval = ioctl(newfd, AUDIO_MIXER_WRITE, &mc); |
1485 | if (retval < 0) { | | 1487 | if (retval < 0) { |
1486 | tmperrno = errno; | | 1488 | tmperrno = errno; |
1487 | close(newfd); | | 1489 | close(newfd); |
1488 | errno = tmperrno; | | 1490 | errno = tmperrno; |
1489 | return retval; | | 1491 | return retval; |
1490 | } | | 1492 | } |
1491 | close(newfd); | | 1493 | close(newfd); |
1492 | break; | | 1494 | break; |
1493 | case SNDCTL_MIX_READ: | | 1495 | case SNDCTL_MIX_READ: |
1494 | mv = (oss_mixer_value *)argp; | | 1496 | mv = (oss_mixer_value *)argp; |
1495 | if (mv == NULL) { | | 1497 | if (mv == NULL) { |
1496 | errno = EINVAL; | | 1498 | errno = EINVAL; |
1497 | return -1; | | 1499 | return -1; |
1498 | } | | 1500 | } |
1499 | if (mv->ctrl == 0) { | | 1501 | if (mv->ctrl == 0) { |
1500 | errno = EINVAL; | | 1502 | errno = EINVAL; |
1501 | return -1; | | 1503 | return -1; |
1502 | } | | 1504 | } |
1503 | snprintf(devname, sizeof(devname), "/dev/mixer%d", mv->dev); | | 1505 | snprintf(devname, sizeof(devname), "/dev/mixer%d", mv->dev); |
1504 | if ((newfd = open(devname, O_RDWR)) < 0) | | 1506 | if ((newfd = open(devname, O_RDWR)) < 0) |
1505 | return newfd; | | 1507 | return newfd; |
1506 | mdi.index = mc.dev = (mv->ctrl - 1); | | 1508 | mdi.index = mc.dev = (mv->ctrl - 1); |
1507 | retval = ioctl(newfd, AUDIO_MIXER_DEVINFO, &mdi); | | 1509 | retval = ioctl(newfd, AUDIO_MIXER_DEVINFO, &mdi); |
1508 | if (retval < 0) { | | 1510 | if (retval < 0) { |
1509 | tmperrno = errno; | | 1511 | tmperrno = errno; |
1510 | close(newfd); | | 1512 | close(newfd); |
1511 | errno = tmperrno; | | 1513 | errno = tmperrno; |
1512 | return retval; | | 1514 | return retval; |
1513 | } | | 1515 | } |
1514 | mc.dev = mdi.index; | | 1516 | mc.dev = mdi.index; |
| | | 1517 | mc.type = mdi.type; |
| | | 1518 | if (mdi.type == AUDIO_MIXER_VALUE) |
| | | 1519 | mc.un.value.num_channels = mdi.un.v.num_channels; |
1515 | retval = ioctl(newfd, AUDIO_MIXER_READ, &mc); | | 1520 | retval = ioctl(newfd, AUDIO_MIXER_READ, &mc); |
1516 | if (retval < 0) { | | 1521 | if (retval < 0) { |
1517 | tmperrno = errno; | | 1522 | tmperrno = errno; |
1518 | close(newfd); | | 1523 | close(newfd); |
1519 | errno = tmperrno; | | 1524 | errno = tmperrno; |
1520 | return retval; | | 1525 | return retval; |
1521 | } | | 1526 | } |
1522 | close(newfd); | | 1527 | close(newfd); |
1523 | mv->value = 0; | | 1528 | mv->value = 0; |
1524 | switch (mdi.type) { | | 1529 | switch (mdi.type) { |
1525 | case AUDIO_MIXER_ENUM: | | 1530 | case AUDIO_MIXER_ENUM: |
1526 | for (i = 0; i < mdi.un.e.num_mem; ++i) { | | 1531 | for (i = 0; i < mdi.un.e.num_mem; ++i) { |
1527 | if (mc.un.ord == mdi.un.e.member[i].ord) { | | 1532 | if (mc.un.ord == mdi.un.e.member[i].ord) { |
1528 | mv->value = i; | | 1533 | mv->value = i; |
1529 | break; | | 1534 | break; |
1530 | } | | 1535 | } |
1531 | } | | 1536 | } |
1532 | break; | | 1537 | break; |
1533 | case AUDIO_MIXER_SET: | | 1538 | case AUDIO_MIXER_SET: |
1534 | for (i = 0; i < mdi.un.s.num_mem; ++i) { | | 1539 | for (i = 0; i < mdi.un.s.num_mem; ++i) { |
1535 | #ifdef notyet | | 1540 | #ifdef notyet |
1536 | if (mc.un.mask & mdi.un.s.member[i].mask) | | 1541 | if (mc.un.mask & mdi.un.s.member[i].mask) |
1537 | mv->value |= (1 << i); | | 1542 | mv->value |= (1 << i); |
1538 | #else | | 1543 | #else |
1539 | if (mc.un.mask == mdi.un.s.member[i].mask) { | | 1544 | if (mc.un.mask == mdi.un.s.member[i].mask) { |
1540 | mv->value = i; | | 1545 | mv->value = i; |
1541 | break; | | 1546 | break; |
1542 | } | | 1547 | } |
1543 | #endif | | 1548 | #endif |
1544 | } | | 1549 | } |
1545 | break; | | 1550 | break; |
1546 | case AUDIO_MIXER_VALUE: | | 1551 | case AUDIO_MIXER_VALUE: |
1547 | if (mdi.un.v.num_channels != 2) { | | 1552 | if (mdi.un.v.num_channels != 2) { |
1548 | mv->value = mc.un.value.level[0]; | | 1553 | mv->value = mc.un.value.level[0]; |
1549 | } else { | | 1554 | } else { |
1550 | mv->value = \ | | 1555 | mv->value = \ |
1551 | ((mc.un.value.level[1] & 0xFF) << 8) | | | 1556 | ((mc.un.value.level[1] & 0xFF) << 8) | |
1552 | ((mc.un.value.level[0] & 0xFF) << 0); | | 1557 | ((mc.un.value.level[0] & 0xFF) << 0); |
1553 | } | | 1558 | } |
1554 | break; | | 1559 | break; |
1555 | default: | | 1560 | default: |
1556 | errno = EINVAL; | | 1561 | errno = EINVAL; |
1557 | return -1; | | 1562 | return -1; |
1558 | } | | 1563 | } |
1559 | break; | | 1564 | break; |
1560 | default: | | 1565 | default: |
1561 | errno = EINVAL; | | 1566 | errno = EINVAL; |
1562 | return -1; | | 1567 | return -1; |
1563 | } | | 1568 | } |
1564 | return 0; | | 1569 | return 0; |
1565 | } | | 1570 | } |
1566 | | | 1571 | |
1567 | static int | | 1572 | static int |
1568 | global_oss4_ioctl(int fd, unsigned long com, void *argp) | | 1573 | global_oss4_ioctl(int fd, unsigned long com, void *argp) |
1569 | { | | 1574 | { |
1570 | int retval = 0; | | 1575 | int retval = 0; |
1571 | | | 1576 | |
1572 | switch (com) { | | 1577 | switch (com) { |
1573 | /* | | 1578 | /* |
1574 | * These ioctls were added in OSSv4 with the idea that | | 1579 | * These ioctls were added in OSSv4 with the idea that |
1575 | * applications could apply strings to audio devices to | | 1580 | * applications could apply strings to audio devices to |
1576 | * display what they are using them for (e.g. with song | | 1581 | * display what they are using them for (e.g. with song |
1577 | * names) in mixer applications. In practice, the popular | | 1582 | * names) in mixer applications. In practice, the popular |
1578 | * implementations of the API in FreeBSD and Solaris treat | | 1583 | * implementations of the API in FreeBSD and Solaris treat |
1579 | * these as a no-op and return EINVAL, and no software in the | | 1584 | * these as a no-op and return EINVAL, and no software in the |
1580 | * wild seems to use them. | | 1585 | * wild seems to use them. |
1581 | */ | | 1586 | */ |
1582 | case SNDCTL_SETSONG: | | 1587 | case SNDCTL_SETSONG: |
1583 | case SNDCTL_GETSONG: | | 1588 | case SNDCTL_GETSONG: |
1584 | case SNDCTL_SETNAME: | | 1589 | case SNDCTL_SETNAME: |
1585 | case SNDCTL_SETLABEL: | | 1590 | case SNDCTL_SETLABEL: |
1586 | case SNDCTL_GETLABEL: | | 1591 | case SNDCTL_GETLABEL: |
1587 | errno = EINVAL; | | 1592 | errno = EINVAL; |
1588 | retval = -1; | | 1593 | retval = -1; |
1589 | break; | | 1594 | break; |
1590 | default: | | 1595 | default: |
1591 | errno = EINVAL; | | 1596 | errno = EINVAL; |
1592 | retval = -1; | | 1597 | retval = -1; |
1593 | break; | | 1598 | break; |
1594 | } | | 1599 | } |
1595 | return retval; | | 1600 | return retval; |
1596 | } | | 1601 | } |
1597 | | | 1602 | |
1598 | static int | | 1603 | static int |
1599 | getcaps(int fd, int *out) | | 1604 | getcaps(int fd, int *out) |
1600 | { | | 1605 | { |
1601 | int props, caps; | | 1606 | int props, caps; |
1602 | | | 1607 | |
1603 | if (ioctl(fd, AUDIO_GETPROPS, &props) < 0) | | 1608 | if (ioctl(fd, AUDIO_GETPROPS, &props) < 0) |
1604 | return -1; | | 1609 | return -1; |
1605 | | | 1610 | |
1606 | caps = DSP_CAP_TRIGGER; | | 1611 | caps = DSP_CAP_TRIGGER; |
1607 | | | 1612 | |
1608 | if (props & AUDIO_PROP_FULLDUPLEX) | | 1613 | if (props & AUDIO_PROP_FULLDUPLEX) |
1609 | caps |= DSP_CAP_DUPLEX; | | 1614 | caps |= DSP_CAP_DUPLEX; |
1610 | if (props & AUDIO_PROP_MMAP) | | 1615 | if (props & AUDIO_PROP_MMAP) |
1611 | caps |= DSP_CAP_MMAP; | | 1616 | caps |= DSP_CAP_MMAP; |
1612 | if (props & AUDIO_PROP_CAPTURE) | | 1617 | if (props & AUDIO_PROP_CAPTURE) |
1613 | caps |= PCM_CAP_INPUT; | | 1618 | caps |= PCM_CAP_INPUT; |
1614 | if (props & AUDIO_PROP_PLAYBACK) | | 1619 | if (props & AUDIO_PROP_PLAYBACK) |
1615 | caps |= PCM_CAP_OUTPUT; | | 1620 | caps |= PCM_CAP_OUTPUT; |
1616 | | | 1621 | |
1617 | *out = caps; | | 1622 | *out = caps; |
1618 | return 0; | | 1623 | return 0; |
1619 | } | | 1624 | } |
1620 | | | 1625 | |
1621 | static int | | 1626 | static int |
1622 | getaudiocount(void) | | 1627 | getaudiocount(void) |
1623 | { | | 1628 | { |
1624 | char devname[32]; | | 1629 | char devname[32]; |
1625 | int ndevs = 0; | | 1630 | int ndevs = 0; |
1626 | int tmpfd; | | 1631 | int tmpfd; |
1627 | int tmperrno = errno; | | 1632 | int tmperrno = errno; |
1628 | | | 1633 | |
1629 | do { | | 1634 | do { |
1630 | snprintf(devname, sizeof(devname), | | 1635 | snprintf(devname, sizeof(devname), |
1631 | "/dev/audio%d", ndevs); | | 1636 | "/dev/audio%d", ndevs); |
1632 | if ((tmpfd = open(devname, O_RDONLY)) != -1 || | | 1637 | if ((tmpfd = open(devname, O_RDONLY)) != -1 || |
1633 | (tmpfd = open(devname, O_WRONLY)) != -1) { | | 1638 | (tmpfd = open(devname, O_WRONLY)) != -1) { |
1634 | ndevs++; | | 1639 | ndevs++; |
1635 | close(tmpfd); | | 1640 | close(tmpfd); |
1636 | } | | 1641 | } |
1637 | } while (tmpfd != -1); | | 1642 | } while (tmpfd != -1); |
1638 | errno = tmperrno; | | 1643 | errno = tmperrno; |
1639 | return ndevs; | | 1644 | return ndevs; |
1640 | } | | 1645 | } |
1641 | | | 1646 | |
1642 | static int | | 1647 | static int |
1643 | getmixercount(void) | | 1648 | getmixercount(void) |
1644 | { | | 1649 | { |
1645 | char devname[32]; | | 1650 | char devname[32]; |
1646 | int ndevs = 0; | | 1651 | int ndevs = 0; |
1647 | int tmpfd; | | 1652 | int tmpfd; |
1648 | int tmperrno = errno; | | 1653 | int tmperrno = errno; |
1649 | | | 1654 | |
1650 | do { | | 1655 | do { |
1651 | snprintf(devname, sizeof(devname), | | 1656 | snprintf(devname, sizeof(devname), |
1652 | "/dev/mixer%d", ndevs); | | 1657 | "/dev/mixer%d", ndevs); |
1653 | if ((tmpfd = open(devname, O_RDONLY)) != -1) { | | 1658 | if ((tmpfd = open(devname, O_RDONLY)) != -1) { |
1654 | ndevs++; | | 1659 | ndevs++; |
1655 | close(tmpfd); | | 1660 | close(tmpfd); |
1656 | } | | 1661 | } |
1657 | } while (tmpfd != -1); | | 1662 | } while (tmpfd != -1); |
1658 | errno = tmperrno; | | 1663 | errno = tmperrno; |
1659 | return ndevs; | | 1664 | return ndevs; |
1660 | } | | 1665 | } |
1661 | | | 1666 | |
1662 | static int | | 1667 | static int |
1663 | getmixercontrolcount(int fd) | | 1668 | getmixercontrolcount(int fd) |
1664 | { | | 1669 | { |
1665 | struct mixer_devinfo mdi; | | 1670 | struct mixer_devinfo mdi; |
1666 | int ndevs = 0; | | 1671 | int ndevs = 0; |
1667 | | | 1672 | |
1668 | do { | | 1673 | do { |
1669 | mdi.index = ndevs++; | | 1674 | mdi.index = ndevs++; |
1670 | } while (ioctl(fd, AUDIO_MIXER_DEVINFO, &mdi) != -1); | | 1675 | } while (ioctl(fd, AUDIO_MIXER_DEVINFO, &mdi) != -1); |
1671 | | | 1676 | |
1672 | return ndevs > 0 ? ndevs - 1 : 0; | | 1677 | return ndevs > 0 ? ndevs - 1 : 0; |
1673 | } | | 1678 | } |
1674 | | | 1679 | |
1675 | static int | | 1680 | static int |
1676 | getvol(u_int gain, u_char balance) | | 1681 | getvol(u_int gain, u_char balance) |
1677 | { | | 1682 | { |
1678 | u_int l, r; | | 1683 | u_int l, r; |
1679 | | | 1684 | |
1680 | if (balance == AUDIO_MID_BALANCE) { | | 1685 | if (balance == AUDIO_MID_BALANCE) { |
1681 | l = r = gain; | | 1686 | l = r = gain; |
1682 | } else if (balance < AUDIO_MID_BALANCE) { | | 1687 | } else if (balance < AUDIO_MID_BALANCE) { |
1683 | l = gain; | | 1688 | l = gain; |
1684 | r = (balance * gain) / AUDIO_MID_BALANCE; | | 1689 | r = (balance * gain) / AUDIO_MID_BALANCE; |
1685 | } else { | | 1690 | } else { |
1686 | r = gain; | | 1691 | r = gain; |
1687 | l = ((AUDIO_RIGHT_BALANCE - balance) * gain) | | 1692 | l = ((AUDIO_RIGHT_BALANCE - balance) * gain) |
1688 | / AUDIO_MID_BALANCE; | | 1693 | / AUDIO_MID_BALANCE; |
1689 | } | | 1694 | } |
1690 | | | 1695 | |
1691 | return TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); | | 1696 | return TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); |
1692 | } | | 1697 | } |
1693 | | | 1698 | |
1694 | static void | | 1699 | static void |
1695 | setvol(int fd, int volume, bool record) | | 1700 | setvol(int fd, int volume, bool record) |
1696 | { | | 1701 | { |
1697 | u_int lgain, rgain; | | 1702 | u_int lgain, rgain; |
1698 | struct audio_info tmpinfo; | | 1703 | struct audio_info tmpinfo; |
1699 | struct audio_prinfo *prinfo; | | 1704 | struct audio_prinfo *prinfo; |
1700 | | | 1705 | |
1701 | AUDIO_INITINFO(&tmpinfo); | | 1706 | AUDIO_INITINFO(&tmpinfo); |
1702 | prinfo = record ? &tmpinfo.record : &tmpinfo.play; | | 1707 | prinfo = record ? &tmpinfo.record : &tmpinfo.play; |
1703 | | | 1708 | |
1704 | lgain = FROM_OSSVOL((volume >> 0) & 0xff); | | 1709 | lgain = FROM_OSSVOL((volume >> 0) & 0xff); |
1705 | rgain = FROM_OSSVOL((volume >> 8) & 0xff); | | 1710 | rgain = FROM_OSSVOL((volume >> 8) & 0xff); |
1706 | | | 1711 | |
1707 | if (lgain == rgain) { | | 1712 | if (lgain == rgain) { |
1708 | prinfo->gain = lgain; | | 1713 | prinfo->gain = lgain; |
1709 | prinfo->balance = AUDIO_MID_BALANCE; | | 1714 | prinfo->balance = AUDIO_MID_BALANCE; |
1710 | } else if (lgain < rgain) { | | 1715 | } else if (lgain < rgain) { |
1711 | prinfo->gain = rgain; | | 1716 | prinfo->gain = rgain; |
1712 | prinfo->balance = AUDIO_RIGHT_BALANCE - | | 1717 | prinfo->balance = AUDIO_RIGHT_BALANCE - |
1713 | (AUDIO_MID_BALANCE * lgain) / rgain; | | 1718 | (AUDIO_MID_BALANCE * lgain) / rgain; |
1714 | } else { | | 1719 | } else { |
1715 | prinfo->gain = lgain; | | 1720 | prinfo->gain = lgain; |
1716 | prinfo->balance = (AUDIO_MID_BALANCE * rgain) / lgain; | | 1721 | prinfo->balance = (AUDIO_MID_BALANCE * rgain) / lgain; |
1717 | } | | 1722 | } |
1718 | | | 1723 | |
1719 | (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 1724 | (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
1720 | } | | 1725 | } |
1721 | | | 1726 | |
1722 | /* | | 1727 | /* |
1723 | * When AUDIO_SETINFO fails to set a channel count, the application's chosen | | 1728 | * When AUDIO_SETINFO fails to set a channel count, the application's chosen |
1724 | * number is out of range of what the kernel allows. | | 1729 | * number is out of range of what the kernel allows. |
1725 | * | | 1730 | * |
1726 | * When this happens, we use the current hardware settings. This is just in | | 1731 | * When this happens, we use the current hardware settings. This is just in |
1727 | * case an application is abusing SNDCTL_DSP_CHANNELS - OSSv4 always sets and | | 1732 | * case an application is abusing SNDCTL_DSP_CHANNELS - OSSv4 always sets and |
1728 | * returns a reasonable value, even if it wasn't what the user requested. | | 1733 | * returns a reasonable value, even if it wasn't what the user requested. |
1729 | * | | 1734 | * |
1730 | * Solaris guarantees this behaviour if nchannels = 0. | | 1735 | * Solaris guarantees this behaviour if nchannels = 0. |
1731 | * | | 1736 | * |
1732 | * XXX: If a device is opened for both playback and recording, and supports | | 1737 | * XXX: If a device is opened for both playback and recording, and supports |
1733 | * fewer channels for recording than playback, applications that do both will | | 1738 | * fewer channels for recording than playback, applications that do both will |
1734 | * behave very strangely. OSS doesn't allow for reporting separate channel | | 1739 | * behave very strangely. OSS doesn't allow for reporting separate channel |
1735 | * counts for recording and playback. This could be worked around by always | | 1740 | * counts for recording and playback. This could be worked around by always |
1736 | * mixing recorded data up to the same number of channels as is being used | | 1741 | * mixing recorded data up to the same number of channels as is being used |
1737 | * for playback. | | 1742 | * for playback. |
1738 | */ | | 1743 | */ |
1739 | static void | | 1744 | static void |
1740 | setchannels(int fd, int mode, int nchannels) | | 1745 | setchannels(int fd, int mode, int nchannels) |
1741 | { | | 1746 | { |
1742 | struct audio_info tmpinfo, hwfmt; | | 1747 | struct audio_info tmpinfo, hwfmt; |
1743 | | | 1748 | |
1744 | if (ioctl(fd, AUDIO_GETFORMAT, &hwfmt) < 0) { | | 1749 | if (ioctl(fd, AUDIO_GETFORMAT, &hwfmt) < 0) { |
1745 | errno = 0; | | 1750 | errno = 0; |
1746 | hwfmt.record.channels = hwfmt.play.channels = 2; | | 1751 | hwfmt.record.channels = hwfmt.play.channels = 2; |
1747 | } | | 1752 | } |
1748 | | | 1753 | |
1749 | if (mode & AUMODE_PLAY) { | | 1754 | if (mode & AUMODE_PLAY) { |
1750 | AUDIO_INITINFO(&tmpinfo); | | 1755 | AUDIO_INITINFO(&tmpinfo); |
1751 | tmpinfo.play.channels = nchannels; | | 1756 | tmpinfo.play.channels = nchannels; |
1752 | if (ioctl(fd, AUDIO_SETINFO, &tmpinfo) < 0) { | | 1757 | if (ioctl(fd, AUDIO_SETINFO, &tmpinfo) < 0) { |
1753 | errno = 0; | | 1758 | errno = 0; |
1754 | AUDIO_INITINFO(&tmpinfo); | | 1759 | AUDIO_INITINFO(&tmpinfo); |
1755 | tmpinfo.play.channels = hwfmt.play.channels; | | 1760 | tmpinfo.play.channels = hwfmt.play.channels; |
1756 | (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 1761 | (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
1757 | } | | 1762 | } |
1758 | } | | 1763 | } |
1759 | | | 1764 | |
1760 | if (mode & AUMODE_RECORD) { | | 1765 | if (mode & AUMODE_RECORD) { |
1761 | AUDIO_INITINFO(&tmpinfo); | | 1766 | AUDIO_INITINFO(&tmpinfo); |
1762 | tmpinfo.record.channels = nchannels; | | 1767 | tmpinfo.record.channels = nchannels; |
1763 | if (ioctl(fd, AUDIO_SETINFO, &tmpinfo) < 0) { | | 1768 | if (ioctl(fd, AUDIO_SETINFO, &tmpinfo) < 0) { |
1764 | errno = 0; | | 1769 | errno = 0; |
1765 | AUDIO_INITINFO(&tmpinfo); | | 1770 | AUDIO_INITINFO(&tmpinfo); |
1766 | tmpinfo.record.channels = hwfmt.record.channels; | | 1771 | tmpinfo.record.channels = hwfmt.record.channels; |
1767 | (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 1772 | (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
1768 | } | | 1773 | } |
1769 | } | | 1774 | } |
1770 | } | | 1775 | } |
1771 | | | 1776 | |
1772 | /* | | 1777 | /* |
1773 | * Check that the blocksize is a power of 2 as OSS wants. | | 1778 | * Check that the blocksize is a power of 2 as OSS wants. |
1774 | * If not, set it to be. | | 1779 | * If not, set it to be. |
1775 | */ | | 1780 | */ |
1776 | static void | | 1781 | static void |
1777 | setblocksize(int fd, struct audio_info *info) | | 1782 | setblocksize(int fd, struct audio_info *info) |
1778 | { | | 1783 | { |
1779 | struct audio_info set; | | 1784 | struct audio_info set; |
1780 | size_t s; | | 1785 | size_t s; |
1781 | | | 1786 | |
1782 | if (info->blocksize & (info->blocksize-1)) { | | 1787 | if (info->blocksize & (info->blocksize-1)) { |
1783 | for(s = 32; s < info->blocksize; s <<= 1) | | 1788 | for(s = 32; s < info->blocksize; s <<= 1) |
1784 | ; | | 1789 | ; |
1785 | AUDIO_INITINFO(&set); | | 1790 | AUDIO_INITINFO(&set); |
1786 | set.blocksize = s; | | 1791 | set.blocksize = s; |
1787 | ioctl(fd, AUDIO_SETINFO, &set); | | 1792 | ioctl(fd, AUDIO_SETINFO, &set); |
1788 | ioctl(fd, AUDIO_GETBUFINFO, info); | | 1793 | ioctl(fd, AUDIO_GETBUFINFO, info); |
1789 | } | | 1794 | } |
1790 | } | | 1795 | } |