| @@ -1,1191 +1,1191 @@ | | | @@ -1,1191 +1,1191 @@ |
1 | /* $NetBSD: ossaudio.c,v 1.59 2020/10/30 21:44:49 nia Exp $ */ | | 1 | /* $NetBSD: ossaudio.c,v 1.60 2020/11/03 08:24:33 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.59 2020/10/30 21:44:49 nia Exp $"); | | 30 | __RCSID("$NetBSD: ossaudio.c,v 1.60 2020/11/03 08:24:33 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 getvol(u_int, u_char); | | 74 | static int getvol(u_int, u_char); |
75 | static void setvol(int, int, bool); | | 75 | static void setvol(int, int, bool); |
76 | | | 76 | |
77 | static void setchannels(int, int, int); | | 77 | static void setchannels(int, int, int); |
78 | static void setblocksize(int, struct audio_info *); | | 78 | static void setblocksize(int, struct audio_info *); |
79 | | | 79 | |
80 | static int audio_ioctl(int, unsigned long, void *); | | 80 | static int audio_ioctl(int, unsigned long, void *); |
81 | static int mixer_oss3_ioctl(int, unsigned long, void *); | | 81 | static int mixer_oss3_ioctl(int, unsigned long, void *); |
82 | static int mixer_oss4_ioctl(int, unsigned long, void *); | | 82 | static int mixer_oss4_ioctl(int, unsigned long, void *); |
83 | static int global_oss4_ioctl(int, unsigned long, void *); | | 83 | static int global_oss4_ioctl(int, unsigned long, void *); |
84 | static int opaque_to_enum(struct audiodevinfo *, audio_mixer_name_t *, int); | | 84 | static int opaque_to_enum(struct audiodevinfo *, audio_mixer_name_t *, int); |
85 | static int enum_to_ord(struct audiodevinfo *, int); | | 85 | static int enum_to_ord(struct audiodevinfo *, int); |
86 | static int enum_to_mask(struct audiodevinfo *, int); | | 86 | static int enum_to_mask(struct audiodevinfo *, int); |
87 | | | 87 | |
88 | #define INTARG (*(int*)argp) | | 88 | #define INTARG (*(int*)argp) |
89 | | | 89 | |
90 | int | | 90 | int |
91 | _oss_ioctl(int fd, unsigned long com, ...) | | 91 | _oss_ioctl(int fd, unsigned long com, ...) |
92 | { | | 92 | { |
93 | va_list ap; | | 93 | va_list ap; |
94 | void *argp; | | 94 | void *argp; |
95 | | | 95 | |
96 | va_start(ap, com); | | 96 | va_start(ap, com); |
97 | argp = va_arg(ap, void *); | | 97 | argp = va_arg(ap, void *); |
98 | va_end(ap); | | 98 | va_end(ap); |
99 | | | 99 | |
100 | if (IOCGROUP(com) == 'P') | | 100 | if (IOCGROUP(com) == 'P') |
101 | return audio_ioctl(fd, com, argp); | | 101 | return audio_ioctl(fd, com, argp); |
102 | else if (IOCGROUP(com) == 'M') | | 102 | else if (IOCGROUP(com) == 'M') |
103 | return mixer_oss3_ioctl(fd, com, argp); | | 103 | return mixer_oss3_ioctl(fd, com, argp); |
104 | else if (IOCGROUP(com) == 'X') | | 104 | else if (IOCGROUP(com) == 'X') |
105 | return mixer_oss4_ioctl(fd, com, argp); | | 105 | return mixer_oss4_ioctl(fd, com, argp); |
106 | else if (IOCGROUP(com) == 'Y') | | 106 | else if (IOCGROUP(com) == 'Y') |
107 | return global_oss4_ioctl(fd, com, argp); | | 107 | return global_oss4_ioctl(fd, com, argp); |
108 | else | | 108 | else |
109 | return ioctl(fd, com, argp); | | 109 | return ioctl(fd, com, argp); |
110 | } | | 110 | } |
111 | | | 111 | |
112 | static int | | 112 | static int |
113 | audio_ioctl(int fd, unsigned long com, void *argp) | | 113 | audio_ioctl(int fd, unsigned long com, void *argp) |
114 | { | | 114 | { |
115 | | | 115 | |
116 | struct audio_info tmpinfo, hwfmt; | | 116 | struct audio_info tmpinfo, hwfmt; |
117 | struct audio_offset tmpoffs; | | 117 | struct audio_offset tmpoffs; |
118 | struct audio_buf_info bufinfo; | | 118 | struct audio_buf_info bufinfo; |
119 | struct audio_errinfo *tmperrinfo; | | 119 | struct audio_errinfo *tmperrinfo; |
120 | struct count_info cntinfo; | | 120 | struct count_info cntinfo; |
121 | struct audio_encoding tmpenc; | | 121 | struct audio_encoding tmpenc; |
122 | u_int u; | | 122 | u_int u; |
123 | u_int encoding; | | 123 | u_int encoding; |
124 | u_int precision; | | 124 | u_int precision; |
125 | int perrors, rerrors; | | 125 | int perrors, rerrors; |
126 | static int totalperrors = 0; | | 126 | static int totalperrors = 0; |
127 | static int totalrerrors = 0; | | 127 | static int totalrerrors = 0; |
128 | oss_count_t osscount; | | 128 | oss_count_t osscount; |
129 | int idat, idata; | | 129 | int idat, idata; |
130 | int retval; | | 130 | int retval; |
131 | | | 131 | |
132 | idat = 0; | | 132 | idat = 0; |
133 | | | 133 | |
134 | switch (com) { | | 134 | switch (com) { |
135 | case SNDCTL_DSP_RESET: | | 135 | case SNDCTL_DSP_RESET: |
136 | retval = ioctl(fd, AUDIO_FLUSH, 0); | | 136 | retval = ioctl(fd, AUDIO_FLUSH, 0); |
137 | if (retval < 0) | | 137 | if (retval < 0) |
138 | return retval; | | 138 | return retval; |
139 | break; | | 139 | break; |
140 | case SNDCTL_DSP_SYNC: | | 140 | case SNDCTL_DSP_SYNC: |
141 | retval = ioctl(fd, AUDIO_DRAIN, 0); | | 141 | retval = ioctl(fd, AUDIO_DRAIN, 0); |
142 | if (retval < 0) | | 142 | if (retval < 0) |
143 | return retval; | | 143 | return retval; |
144 | break; | | 144 | break; |
145 | case SNDCTL_DSP_GETERROR: | | 145 | case SNDCTL_DSP_GETERROR: |
146 | tmperrinfo = (struct audio_errinfo *)argp; | | 146 | tmperrinfo = (struct audio_errinfo *)argp; |
147 | if (tmperrinfo == NULL) | | 147 | if (tmperrinfo == NULL) |
148 | return EINVAL; | | 148 | return EINVAL; |
149 | memset(tmperrinfo, 0, sizeof(struct audio_errinfo)); | | 149 | memset(tmperrinfo, 0, sizeof(struct audio_errinfo)); |
150 | if ((retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo)) < 0) | | 150 | if ((retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo)) < 0) |
151 | return retval; | | 151 | return retval; |
152 | /* | | 152 | /* |
153 | * OSS requires that we return counters that are relative to | | 153 | * OSS requires that we return counters that are relative to |
154 | * the last call. We must maintain state here... | | 154 | * the last call. We must maintain state here... |
155 | */ | | 155 | */ |
156 | if (ioctl(fd, AUDIO_PERROR, &perrors) != -1) { | | 156 | if (ioctl(fd, AUDIO_PERROR, &perrors) != -1) { |
157 | perrors /= ((tmpinfo.play.precision / NBBY) * | | 157 | perrors /= ((tmpinfo.play.precision / NBBY) * |
158 | tmpinfo.play.channels); | | 158 | tmpinfo.play.channels); |
159 | tmperrinfo->play_underruns = | | 159 | tmperrinfo->play_underruns = |
160 | (perrors / tmpinfo.blocksize) - totalperrors; | | 160 | (perrors / tmpinfo.blocksize) - totalperrors; |
161 | totalperrors += tmperrinfo->play_underruns; | | 161 | totalperrors += tmperrinfo->play_underruns; |
162 | } | | 162 | } |
163 | if (ioctl(fd, AUDIO_RERROR, &rerrors) != -1) { | | 163 | if (ioctl(fd, AUDIO_RERROR, &rerrors) != -1) { |
164 | rerrors /= ((tmpinfo.record.precision / NBBY) * | | 164 | rerrors /= ((tmpinfo.record.precision / NBBY) * |
165 | tmpinfo.record.channels); | | 165 | tmpinfo.record.channels); |
166 | tmperrinfo->rec_overruns = | | 166 | tmperrinfo->rec_overruns = |
167 | (rerrors / tmpinfo.blocksize) - totalrerrors; | | 167 | (rerrors / tmpinfo.blocksize) - totalrerrors; |
168 | totalrerrors += tmperrinfo->rec_overruns; | | 168 | totalrerrors += tmperrinfo->rec_overruns; |
169 | } | | 169 | } |
170 | break; | | 170 | break; |
171 | case SNDCTL_DSP_COOKEDMODE: | | 171 | case SNDCTL_DSP_COOKEDMODE: |
172 | /* | | 172 | /* |
173 | * NetBSD is always running in "cooked mode" - the kernel | | 173 | * NetBSD is always running in "cooked mode" - the kernel |
174 | * always performs format conversions. | | 174 | * always performs format conversions. |
175 | */ | | 175 | */ |
176 | INTARG = 1; | | 176 | INTARG = 1; |
177 | break; | | 177 | break; |
178 | case SNDCTL_DSP_POST: | | 178 | case SNDCTL_DSP_POST: |
179 | /* This call is merely advisory, and may be a nop. */ | | 179 | /* This call is merely advisory, and may be a nop. */ |
180 | break; | | 180 | break; |
181 | case SNDCTL_DSP_SPEED: | | 181 | case SNDCTL_DSP_SPEED: |
182 | AUDIO_INITINFO(&tmpinfo); | | 182 | AUDIO_INITINFO(&tmpinfo); |
183 | /* Conform to kernel limits. */ | | 183 | /* Conform to kernel limits. */ |
184 | if (INTARG < 1000) | | 184 | if (INTARG < 1000) |
185 | INTARG = 1000; | | 185 | INTARG = 1000; |
186 | if (INTARG > 192000) | | 186 | if (INTARG > 192000) |
187 | INTARG = 192000; | | 187 | INTARG = 192000; |
188 | tmpinfo.play.sample_rate = | | 188 | tmpinfo.play.sample_rate = |
189 | tmpinfo.record.sample_rate = INTARG; | | 189 | tmpinfo.record.sample_rate = INTARG; |
190 | if (ioctl(fd, AUDIO_SETINFO, &tmpinfo) < 0) { | | 190 | retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
| | | 191 | if (retval < 0) |
191 | return retval; | | 192 | return retval; |
192 | } | | | |
193 | /* FALLTHRU */ | | 193 | /* FALLTHRU */ |
194 | case SOUND_PCM_READ_RATE: | | 194 | case SOUND_PCM_READ_RATE: |
195 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 195 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
196 | if (retval < 0) | | 196 | if (retval < 0) |
197 | return retval; | | 197 | return retval; |
198 | INTARG = GETPRINFO(&tmpinfo, sample_rate); | | 198 | INTARG = GETPRINFO(&tmpinfo, sample_rate); |
199 | break; | | 199 | break; |
200 | case SNDCTL_DSP_STEREO: | | 200 | case SNDCTL_DSP_STEREO: |
201 | AUDIO_INITINFO(&tmpinfo); | | 201 | AUDIO_INITINFO(&tmpinfo); |
202 | tmpinfo.play.channels = | | 202 | tmpinfo.play.channels = |
203 | tmpinfo.record.channels = INTARG ? 2 : 1; | | 203 | tmpinfo.record.channels = INTARG ? 2 : 1; |
204 | (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 204 | (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
205 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 205 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
206 | if (retval < 0) | | 206 | if (retval < 0) |
207 | return retval; | | 207 | return retval; |
208 | INTARG = GETPRINFO(&tmpinfo, channels) - 1; | | 208 | INTARG = GETPRINFO(&tmpinfo, channels) - 1; |
209 | break; | | 209 | break; |
210 | case SNDCTL_DSP_GETBLKSIZE: | | 210 | case SNDCTL_DSP_GETBLKSIZE: |
211 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 211 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
212 | if (retval < 0) | | 212 | if (retval < 0) |
213 | return retval; | | 213 | return retval; |
214 | setblocksize(fd, &tmpinfo); | | 214 | setblocksize(fd, &tmpinfo); |
215 | INTARG = tmpinfo.blocksize; | | 215 | INTARG = tmpinfo.blocksize; |
216 | break; | | 216 | break; |
217 | case SNDCTL_DSP_SETFMT: | | 217 | case SNDCTL_DSP_SETFMT: |
218 | AUDIO_INITINFO(&tmpinfo); | | 218 | AUDIO_INITINFO(&tmpinfo); |
219 | switch (INTARG) { | | 219 | switch (INTARG) { |
220 | case AFMT_MU_LAW: | | 220 | case AFMT_MU_LAW: |
221 | tmpinfo.play.precision = | | 221 | tmpinfo.play.precision = |
222 | tmpinfo.record.precision = 8; | | 222 | tmpinfo.record.precision = 8; |
223 | tmpinfo.play.encoding = | | 223 | tmpinfo.play.encoding = |
224 | tmpinfo.record.encoding = AUDIO_ENCODING_ULAW; | | 224 | tmpinfo.record.encoding = AUDIO_ENCODING_ULAW; |
225 | break; | | 225 | break; |
226 | case AFMT_A_LAW: | | 226 | case AFMT_A_LAW: |
227 | tmpinfo.play.precision = | | 227 | tmpinfo.play.precision = |
228 | tmpinfo.record.precision = 8; | | 228 | tmpinfo.record.precision = 8; |
229 | tmpinfo.play.encoding = | | 229 | tmpinfo.play.encoding = |
230 | tmpinfo.record.encoding = AUDIO_ENCODING_ALAW; | | 230 | tmpinfo.record.encoding = AUDIO_ENCODING_ALAW; |
231 | break; | | 231 | break; |
232 | case AFMT_U8: | | 232 | case AFMT_U8: |
233 | tmpinfo.play.precision = | | 233 | tmpinfo.play.precision = |
234 | tmpinfo.record.precision = 8; | | 234 | tmpinfo.record.precision = 8; |
235 | tmpinfo.play.encoding = | | 235 | tmpinfo.play.encoding = |
236 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR; | | 236 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR; |
237 | break; | | 237 | break; |
238 | case AFMT_S8: | | 238 | case AFMT_S8: |
239 | tmpinfo.play.precision = | | 239 | tmpinfo.play.precision = |
240 | tmpinfo.record.precision = 8; | | 240 | tmpinfo.record.precision = 8; |
241 | tmpinfo.play.encoding = | | 241 | tmpinfo.play.encoding = |
242 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR; | | 242 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR; |
243 | break; | | 243 | break; |
244 | case AFMT_S16_LE: | | 244 | case AFMT_S16_LE: |
245 | tmpinfo.play.precision = | | 245 | tmpinfo.play.precision = |
246 | tmpinfo.record.precision = 16; | | 246 | tmpinfo.record.precision = 16; |
247 | tmpinfo.play.encoding = | | 247 | tmpinfo.play.encoding = |
248 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; | | 248 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; |
249 | break; | | 249 | break; |
250 | case AFMT_S16_BE: | | 250 | case AFMT_S16_BE: |
251 | tmpinfo.play.precision = | | 251 | tmpinfo.play.precision = |
252 | tmpinfo.record.precision = 16; | | 252 | tmpinfo.record.precision = 16; |
253 | tmpinfo.play.encoding = | | 253 | tmpinfo.play.encoding = |
254 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; | | 254 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; |
255 | break; | | 255 | break; |
256 | case AFMT_U16_LE: | | 256 | case AFMT_U16_LE: |
257 | tmpinfo.play.precision = | | 257 | tmpinfo.play.precision = |
258 | tmpinfo.record.precision = 16; | | 258 | tmpinfo.record.precision = 16; |
259 | tmpinfo.play.encoding = | | 259 | tmpinfo.play.encoding = |
260 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE; | | 260 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE; |
261 | break; | | 261 | break; |
262 | case AFMT_U16_BE: | | 262 | case AFMT_U16_BE: |
263 | tmpinfo.play.precision = | | 263 | tmpinfo.play.precision = |
264 | tmpinfo.record.precision = 16; | | 264 | tmpinfo.record.precision = 16; |
265 | tmpinfo.play.encoding = | | 265 | tmpinfo.play.encoding = |
266 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE; | | 266 | tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE; |
267 | break; | | 267 | break; |
268 | /* | | 268 | /* |
269 | * XXX: When the kernel supports 24-bit LPCM by default, | | 269 | * XXX: When the kernel supports 24-bit LPCM by default, |
270 | * the 24-bit formats should be handled properly instead | | 270 | * the 24-bit formats should be handled properly instead |
271 | * of falling back to 32 bits. | | 271 | * of falling back to 32 bits. |
272 | */ | | 272 | */ |
273 | case AFMT_S24_LE: | | 273 | case AFMT_S24_LE: |
274 | case AFMT_S32_LE: | | 274 | case AFMT_S32_LE: |
275 | tmpinfo.play.precision = | | 275 | tmpinfo.play.precision = |
276 | tmpinfo.record.precision = 32; | | 276 | tmpinfo.record.precision = 32; |
277 | tmpinfo.play.encoding = | | 277 | tmpinfo.play.encoding = |
278 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; | | 278 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE; |
279 | break; | | 279 | break; |
280 | case AFMT_S24_BE: | | 280 | case AFMT_S24_BE: |
281 | case AFMT_S32_BE: | | 281 | case AFMT_S32_BE: |
282 | tmpinfo.play.precision = | | 282 | tmpinfo.play.precision = |
283 | tmpinfo.record.precision = 32; | | 283 | tmpinfo.record.precision = 32; |
284 | tmpinfo.play.encoding = | | 284 | tmpinfo.play.encoding = |
285 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; | | 285 | tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE; |
286 | break; | | 286 | break; |
287 | case AFMT_AC3: | | 287 | case AFMT_AC3: |
288 | tmpinfo.play.precision = | | 288 | tmpinfo.play.precision = |
289 | tmpinfo.record.precision = 16; | | 289 | tmpinfo.record.precision = 16; |
290 | tmpinfo.play.encoding = | | 290 | tmpinfo.play.encoding = |
291 | tmpinfo.record.encoding = AUDIO_ENCODING_AC3; | | 291 | tmpinfo.record.encoding = AUDIO_ENCODING_AC3; |
292 | break; | | 292 | break; |
293 | default: | | 293 | default: |
294 | /* | | 294 | /* |
295 | * OSSv4 specifies that if an invalid format is chosen | | 295 | * OSSv4 specifies that if an invalid format is chosen |
296 | * by an application then a sensible format supported | | 296 | * by an application then a sensible format supported |
297 | * by the hardware is returned. | | 297 | * by the hardware is returned. |
298 | * | | 298 | * |
299 | * In this case, we pick the current hardware format. | | 299 | * In this case, we pick the current hardware format. |
300 | */ | | 300 | */ |
301 | retval = ioctl(fd, AUDIO_GETFORMAT, &hwfmt); | | 301 | retval = ioctl(fd, AUDIO_GETFORMAT, &hwfmt); |
302 | if (retval < 0) | | 302 | if (retval < 0) |
303 | return retval; | | 303 | return retval; |
304 | retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); | | 304 | retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); |
305 | if (retval < 0) | | 305 | if (retval < 0) |
306 | return retval; | | 306 | return retval; |
307 | tmpinfo.play.encoding = | | 307 | tmpinfo.play.encoding = |
308 | tmpinfo.record.encoding = | | 308 | tmpinfo.record.encoding = |
309 | (tmpinfo.mode == AUMODE_RECORD) ? | | 309 | (tmpinfo.mode == AUMODE_RECORD) ? |
310 | hwfmt.record.encoding : hwfmt.play.encoding; | | 310 | hwfmt.record.encoding : hwfmt.play.encoding; |
311 | tmpinfo.play.precision = | | 311 | tmpinfo.play.precision = |
312 | tmpinfo.record.precision = | | 312 | tmpinfo.record.precision = |
313 | (tmpinfo.mode == AUMODE_RECORD) ? | | 313 | (tmpinfo.mode == AUMODE_RECORD) ? |
314 | hwfmt.record.precision : hwfmt.play.precision ; | | 314 | hwfmt.record.precision : hwfmt.play.precision ; |
315 | break; | | 315 | break; |
316 | } | | 316 | } |
317 | /* | | 317 | /* |
318 | * In the post-kernel-mixer world, assume that any error means | | 318 | * In the post-kernel-mixer world, assume that any error means |
319 | * it's fatal rather than an unsupported format being selected. | | 319 | * it's fatal rather than an unsupported format being selected. |
320 | */ | | 320 | */ |
321 | retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 321 | retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
322 | if (retval < 0) | | 322 | if (retval < 0) |
323 | return retval; | | 323 | return retval; |
324 | /* FALLTHRU */ | | 324 | /* FALLTHRU */ |
325 | case SOUND_PCM_READ_BITS: | | 325 | case SOUND_PCM_READ_BITS: |
326 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 326 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
327 | if (retval < 0) | | 327 | if (retval < 0) |
328 | return retval; | | 328 | return retval; |
329 | encoding = GETPRINFO(&tmpinfo, encoding); | | 329 | encoding = GETPRINFO(&tmpinfo, encoding); |
330 | precision = GETPRINFO(&tmpinfo, precision); | | 330 | precision = GETPRINFO(&tmpinfo, precision); |
331 | switch (encoding) { | | 331 | switch (encoding) { |
332 | case AUDIO_ENCODING_ULAW: | | 332 | case AUDIO_ENCODING_ULAW: |
333 | idat = AFMT_MU_LAW; | | 333 | idat = AFMT_MU_LAW; |
334 | break; | | 334 | break; |
335 | case AUDIO_ENCODING_ALAW: | | 335 | case AUDIO_ENCODING_ALAW: |
336 | idat = AFMT_A_LAW; | | 336 | idat = AFMT_A_LAW; |
337 | break; | | 337 | break; |
338 | case AUDIO_ENCODING_SLINEAR_LE: | | 338 | case AUDIO_ENCODING_SLINEAR_LE: |
339 | if (precision == 32) | | 339 | if (precision == 32) |
340 | idat = AFMT_S32_LE; | | 340 | idat = AFMT_S32_LE; |
341 | else if (precision == 24) | | 341 | else if (precision == 24) |
342 | idat = AFMT_S24_LE; | | 342 | idat = AFMT_S24_LE; |
343 | else if (precision == 16) | | 343 | else if (precision == 16) |
344 | idat = AFMT_S16_LE; | | 344 | idat = AFMT_S16_LE; |
345 | else | | 345 | else |
346 | idat = AFMT_S8; | | 346 | idat = AFMT_S8; |
347 | break; | | 347 | break; |
348 | case AUDIO_ENCODING_SLINEAR_BE: | | 348 | case AUDIO_ENCODING_SLINEAR_BE: |
349 | if (precision == 32) | | 349 | if (precision == 32) |
350 | idat = AFMT_S32_BE; | | 350 | idat = AFMT_S32_BE; |
351 | else if (precision == 24) | | 351 | else if (precision == 24) |
352 | idat = AFMT_S24_BE; | | 352 | idat = AFMT_S24_BE; |
353 | else if (precision == 16) | | 353 | else if (precision == 16) |
354 | idat = AFMT_S16_BE; | | 354 | idat = AFMT_S16_BE; |
355 | else | | 355 | else |
356 | idat = AFMT_S8; | | 356 | idat = AFMT_S8; |
357 | break; | | 357 | break; |
358 | case AUDIO_ENCODING_ULINEAR_LE: | | 358 | case AUDIO_ENCODING_ULINEAR_LE: |
359 | if (precision == 16) | | 359 | if (precision == 16) |
360 | idat = AFMT_U16_LE; | | 360 | idat = AFMT_U16_LE; |
361 | else | | 361 | else |
362 | idat = AFMT_U8; | | 362 | idat = AFMT_U8; |
363 | break; | | 363 | break; |
364 | case AUDIO_ENCODING_ULINEAR_BE: | | 364 | case AUDIO_ENCODING_ULINEAR_BE: |
365 | if (precision == 16) | | 365 | if (precision == 16) |
366 | idat = AFMT_U16_BE; | | 366 | idat = AFMT_U16_BE; |
367 | else | | 367 | else |
368 | idat = AFMT_U8; | | 368 | idat = AFMT_U8; |
369 | break; | | 369 | break; |
370 | case AUDIO_ENCODING_ADPCM: | | 370 | case AUDIO_ENCODING_ADPCM: |
371 | idat = AFMT_IMA_ADPCM; | | 371 | idat = AFMT_IMA_ADPCM; |
372 | break; | | 372 | break; |
373 | case AUDIO_ENCODING_AC3: | | 373 | case AUDIO_ENCODING_AC3: |
374 | idat = AFMT_AC3; | | 374 | idat = AFMT_AC3; |
375 | break; | | 375 | break; |
376 | } | | 376 | } |
377 | INTARG = idat; | | 377 | INTARG = idat; |
378 | break; | | 378 | break; |
379 | case SNDCTL_DSP_CHANNELS: | | 379 | case SNDCTL_DSP_CHANNELS: |
380 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 380 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
381 | if (retval < 0) | | 381 | if (retval < 0) |
382 | return retval; | | 382 | return retval; |
383 | setchannels(fd, tmpinfo.mode, INTARG); | | 383 | setchannels(fd, tmpinfo.mode, INTARG); |
384 | /* FALLTHRU */ | | 384 | /* FALLTHRU */ |
385 | case SOUND_PCM_READ_CHANNELS: | | 385 | case SOUND_PCM_READ_CHANNELS: |
386 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 386 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
387 | if (retval < 0) | | 387 | if (retval < 0) |
388 | return retval; | | 388 | return retval; |
389 | INTARG = GETPRINFO(&tmpinfo, channels); | | 389 | INTARG = GETPRINFO(&tmpinfo, channels); |
390 | break; | | 390 | break; |
391 | case SOUND_PCM_WRITE_FILTER: | | 391 | case SOUND_PCM_WRITE_FILTER: |
392 | case SOUND_PCM_READ_FILTER: | | 392 | case SOUND_PCM_READ_FILTER: |
393 | errno = EINVAL; | | 393 | errno = EINVAL; |
394 | return -1; /* XXX unimplemented */ | | 394 | return -1; /* XXX unimplemented */ |
395 | case SNDCTL_DSP_SUBDIVIDE: | | 395 | case SNDCTL_DSP_SUBDIVIDE: |
396 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 396 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
397 | if (retval < 0) | | 397 | if (retval < 0) |
398 | return retval; | | 398 | return retval; |
399 | setblocksize(fd, &tmpinfo); | | 399 | setblocksize(fd, &tmpinfo); |
400 | idat = INTARG; | | 400 | idat = INTARG; |
401 | if (idat == 0) | | 401 | if (idat == 0) |
402 | idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; | | 402 | idat = tmpinfo.play.buffer_size / tmpinfo.blocksize; |
403 | idat = (tmpinfo.play.buffer_size / idat) & -4; | | 403 | idat = (tmpinfo.play.buffer_size / idat) & -4; |
404 | AUDIO_INITINFO(&tmpinfo); | | 404 | AUDIO_INITINFO(&tmpinfo); |
405 | tmpinfo.blocksize = idat; | | 405 | tmpinfo.blocksize = idat; |
406 | retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 406 | retval = ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
407 | if (retval < 0) | | 407 | if (retval < 0) |
408 | return retval; | | 408 | return retval; |
409 | INTARG = tmpinfo.play.buffer_size / tmpinfo.blocksize; | | 409 | INTARG = tmpinfo.play.buffer_size / tmpinfo.blocksize; |
410 | break; | | 410 | break; |
411 | case SNDCTL_DSP_SETFRAGMENT: | | 411 | case SNDCTL_DSP_SETFRAGMENT: |
412 | AUDIO_INITINFO(&tmpinfo); | | 412 | AUDIO_INITINFO(&tmpinfo); |
413 | idat = INTARG; | | 413 | idat = INTARG; |
414 | if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) | | 414 | if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) |
415 | return EINVAL; | | 415 | return EINVAL; |
416 | tmpinfo.blocksize = 1 << (idat & 0xffff); | | 416 | tmpinfo.blocksize = 1 << (idat & 0xffff); |
417 | tmpinfo.hiwat = ((unsigned)idat >> 16) & 0x7fff; | | 417 | tmpinfo.hiwat = ((unsigned)idat >> 16) & 0x7fff; |
418 | if (tmpinfo.hiwat == 0) /* 0 means set to max */ | | 418 | if (tmpinfo.hiwat == 0) /* 0 means set to max */ |
419 | tmpinfo.hiwat = 65536; | | 419 | tmpinfo.hiwat = 65536; |
420 | (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 420 | (void) ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
421 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 421 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
422 | if (retval < 0) | | 422 | if (retval < 0) |
423 | return retval; | | 423 | return retval; |
424 | u = tmpinfo.blocksize; | | 424 | u = tmpinfo.blocksize; |
425 | for(idat = 0; u > 1; idat++, u >>= 1) | | 425 | for(idat = 0; u > 1; idat++, u >>= 1) |
426 | ; | | 426 | ; |
427 | idat |= (tmpinfo.hiwat & 0x7fff) << 16; | | 427 | idat |= (tmpinfo.hiwat & 0x7fff) << 16; |
428 | INTARG = idat; | | 428 | INTARG = idat; |
429 | break; | | 429 | break; |
430 | case SNDCTL_DSP_GETFMTS: | | 430 | case SNDCTL_DSP_GETFMTS: |
431 | for(idat = 0, tmpenc.index = 0; | | 431 | for(idat = 0, tmpenc.index = 0; |
432 | ioctl(fd, AUDIO_GETENC, &tmpenc) == 0; | | 432 | ioctl(fd, AUDIO_GETENC, &tmpenc) == 0; |
433 | tmpenc.index++) { | | 433 | tmpenc.index++) { |
434 | switch(tmpenc.encoding) { | | 434 | switch(tmpenc.encoding) { |
435 | case AUDIO_ENCODING_ULAW: | | 435 | case AUDIO_ENCODING_ULAW: |
436 | idat |= AFMT_MU_LAW; | | 436 | idat |= AFMT_MU_LAW; |
437 | break; | | 437 | break; |
438 | case AUDIO_ENCODING_ALAW: | | 438 | case AUDIO_ENCODING_ALAW: |
439 | idat |= AFMT_A_LAW; | | 439 | idat |= AFMT_A_LAW; |
440 | break; | | 440 | break; |
441 | case AUDIO_ENCODING_SLINEAR: | | 441 | case AUDIO_ENCODING_SLINEAR: |
442 | idat |= AFMT_S8; | | 442 | idat |= AFMT_S8; |
443 | break; | | 443 | break; |
444 | case AUDIO_ENCODING_SLINEAR_LE: | | 444 | case AUDIO_ENCODING_SLINEAR_LE: |
445 | if (tmpenc.precision == 32) | | 445 | if (tmpenc.precision == 32) |
446 | idat |= AFMT_S32_LE; | | 446 | idat |= AFMT_S32_LE; |
447 | else if (tmpenc.precision == 24) | | 447 | else if (tmpenc.precision == 24) |
448 | idat |= AFMT_S24_LE; | | 448 | idat |= AFMT_S24_LE; |
449 | else if (tmpenc.precision == 16) | | 449 | else if (tmpenc.precision == 16) |
450 | idat |= AFMT_S16_LE; | | 450 | idat |= AFMT_S16_LE; |
451 | else | | 451 | else |
452 | idat |= AFMT_S8; | | 452 | idat |= AFMT_S8; |
453 | break; | | 453 | break; |
454 | case AUDIO_ENCODING_SLINEAR_BE: | | 454 | case AUDIO_ENCODING_SLINEAR_BE: |
455 | if (tmpenc.precision == 32) | | 455 | if (tmpenc.precision == 32) |
456 | idat |= AFMT_S32_BE; | | 456 | idat |= AFMT_S32_BE; |
457 | else if (tmpenc.precision == 24) | | 457 | else if (tmpenc.precision == 24) |
458 | idat |= AFMT_S24_BE; | | 458 | idat |= AFMT_S24_BE; |
459 | else if (tmpenc.precision == 16) | | 459 | else if (tmpenc.precision == 16) |
460 | idat |= AFMT_S16_BE; | | 460 | idat |= AFMT_S16_BE; |
461 | else | | 461 | else |
462 | idat |= AFMT_S8; | | 462 | idat |= AFMT_S8; |
463 | break; | | 463 | break; |
464 | case AUDIO_ENCODING_ULINEAR: | | 464 | case AUDIO_ENCODING_ULINEAR: |
465 | idat |= AFMT_U8; | | 465 | idat |= AFMT_U8; |
466 | break; | | 466 | break; |
467 | case AUDIO_ENCODING_ULINEAR_LE: | | 467 | case AUDIO_ENCODING_ULINEAR_LE: |
468 | if (tmpenc.precision == 16) | | 468 | if (tmpenc.precision == 16) |
469 | idat |= AFMT_U16_LE; | | 469 | idat |= AFMT_U16_LE; |
470 | else | | 470 | else |
471 | idat |= AFMT_U8; | | 471 | idat |= AFMT_U8; |
472 | break; | | 472 | break; |
473 | case AUDIO_ENCODING_ULINEAR_BE: | | 473 | case AUDIO_ENCODING_ULINEAR_BE: |
474 | if (tmpenc.precision == 16) | | 474 | if (tmpenc.precision == 16) |
475 | idat |= AFMT_U16_BE; | | 475 | idat |= AFMT_U16_BE; |
476 | else | | 476 | else |
477 | idat |= AFMT_U8; | | 477 | idat |= AFMT_U8; |
478 | break; | | 478 | break; |
479 | case AUDIO_ENCODING_ADPCM: | | 479 | case AUDIO_ENCODING_ADPCM: |
480 | idat |= AFMT_IMA_ADPCM; | | 480 | idat |= AFMT_IMA_ADPCM; |
481 | break; | | 481 | break; |
482 | case AUDIO_ENCODING_AC3: | | 482 | case AUDIO_ENCODING_AC3: |
483 | idat |= AFMT_AC3; | | 483 | idat |= AFMT_AC3; |
484 | break; | | 484 | break; |
485 | default: | | 485 | default: |
486 | break; | | 486 | break; |
487 | } | | 487 | } |
488 | } | | 488 | } |
489 | INTARG = idat; | | 489 | INTARG = idat; |
490 | break; | | 490 | break; |
491 | case SNDCTL_DSP_GETOSPACE: | | 491 | case SNDCTL_DSP_GETOSPACE: |
492 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 492 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
493 | if (retval < 0) | | 493 | if (retval < 0) |
494 | return retval; | | 494 | return retval; |
495 | setblocksize(fd, &tmpinfo); | | 495 | setblocksize(fd, &tmpinfo); |
496 | bufinfo.fragsize = tmpinfo.blocksize; | | 496 | bufinfo.fragsize = tmpinfo.blocksize; |
497 | bufinfo.fragments = tmpinfo.hiwat - (tmpinfo.play.seek | | 497 | bufinfo.fragments = tmpinfo.hiwat - (tmpinfo.play.seek |
498 | + tmpinfo.blocksize - 1) / tmpinfo.blocksize; | | 498 | + tmpinfo.blocksize - 1) / tmpinfo.blocksize; |
499 | bufinfo.fragstotal = tmpinfo.hiwat; | | 499 | bufinfo.fragstotal = tmpinfo.hiwat; |
500 | bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize | | 500 | bufinfo.bytes = tmpinfo.hiwat * tmpinfo.blocksize |
501 | - tmpinfo.play.seek; | | 501 | - tmpinfo.play.seek; |
502 | *(struct audio_buf_info *)argp = bufinfo; | | 502 | *(struct audio_buf_info *)argp = bufinfo; |
503 | break; | | 503 | break; |
504 | case SNDCTL_DSP_GETISPACE: | | 504 | case SNDCTL_DSP_GETISPACE: |
505 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 505 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
506 | if (retval < 0) | | 506 | if (retval < 0) |
507 | return retval; | | 507 | return retval; |
508 | setblocksize(fd, &tmpinfo); | | 508 | setblocksize(fd, &tmpinfo); |
509 | bufinfo.fragsize = tmpinfo.blocksize; | | 509 | bufinfo.fragsize = tmpinfo.blocksize; |
510 | bufinfo.fragments = tmpinfo.record.seek / tmpinfo.blocksize; | | 510 | bufinfo.fragments = tmpinfo.record.seek / tmpinfo.blocksize; |
511 | bufinfo.fragstotal = | | 511 | bufinfo.fragstotal = |
512 | tmpinfo.record.buffer_size / tmpinfo.blocksize; | | 512 | tmpinfo.record.buffer_size / tmpinfo.blocksize; |
513 | bufinfo.bytes = tmpinfo.record.seek; | | 513 | bufinfo.bytes = tmpinfo.record.seek; |
514 | *(struct audio_buf_info *)argp = bufinfo; | | 514 | *(struct audio_buf_info *)argp = bufinfo; |
515 | break; | | 515 | break; |
516 | case SNDCTL_DSP_NONBLOCK: | | 516 | case SNDCTL_DSP_NONBLOCK: |
517 | idat = 1; | | 517 | idat = 1; |
518 | retval = ioctl(fd, FIONBIO, &idat); | | 518 | retval = ioctl(fd, FIONBIO, &idat); |
519 | if (retval < 0) | | 519 | if (retval < 0) |
520 | return retval; | | 520 | return retval; |
521 | break; | | 521 | break; |
522 | case SNDCTL_DSP_GETCAPS: | | 522 | case SNDCTL_DSP_GETCAPS: |
523 | retval = ioctl(fd, AUDIO_GETPROPS, &idata); | | 523 | retval = ioctl(fd, AUDIO_GETPROPS, &idata); |
524 | if (retval < 0) | | 524 | if (retval < 0) |
525 | return retval; | | 525 | return retval; |
526 | idat = DSP_CAP_TRIGGER; | | 526 | idat = DSP_CAP_TRIGGER; |
527 | if (idata & AUDIO_PROP_FULLDUPLEX) | | 527 | if (idata & AUDIO_PROP_FULLDUPLEX) |
528 | idat |= DSP_CAP_DUPLEX; | | 528 | idat |= DSP_CAP_DUPLEX; |
529 | if (idata & AUDIO_PROP_MMAP) | | 529 | if (idata & AUDIO_PROP_MMAP) |
530 | idat |= DSP_CAP_MMAP; | | 530 | idat |= DSP_CAP_MMAP; |
531 | INTARG = idat; | | 531 | INTARG = idat; |
532 | break; | | 532 | break; |
533 | case SNDCTL_DSP_SETTRIGGER: | | 533 | case SNDCTL_DSP_SETTRIGGER: |
534 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 534 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
535 | if (retval < 0) | | 535 | if (retval < 0) |
536 | return retval; | | 536 | return retval; |
537 | AUDIO_INITINFO(&tmpinfo); | | 537 | AUDIO_INITINFO(&tmpinfo); |
538 | if (tmpinfo.mode & AUMODE_PLAY) | | 538 | if (tmpinfo.mode & AUMODE_PLAY) |
539 | tmpinfo.play.pause = (INTARG & PCM_ENABLE_OUTPUT) == 0; | | 539 | tmpinfo.play.pause = (INTARG & PCM_ENABLE_OUTPUT) == 0; |
540 | if (tmpinfo.mode & AUMODE_RECORD) | | 540 | if (tmpinfo.mode & AUMODE_RECORD) |
541 | tmpinfo.record.pause = (INTARG & PCM_ENABLE_INPUT) == 0; | | 541 | tmpinfo.record.pause = (INTARG & PCM_ENABLE_INPUT) == 0; |
542 | (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); | | 542 | (void)ioctl(fd, AUDIO_SETINFO, &tmpinfo); |
543 | /* FALLTHRU */ | | 543 | /* FALLTHRU */ |
544 | case SNDCTL_DSP_GETTRIGGER: | | 544 | case SNDCTL_DSP_GETTRIGGER: |
545 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 545 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
546 | if (retval < 0) | | 546 | if (retval < 0) |
547 | return retval; | | 547 | return retval; |
548 | idat = 0; | | 548 | idat = 0; |
549 | if ((tmpinfo.mode & AUMODE_PLAY) && !tmpinfo.play.pause) | | 549 | if ((tmpinfo.mode & AUMODE_PLAY) && !tmpinfo.play.pause) |
550 | idat |= PCM_ENABLE_OUTPUT; | | 550 | idat |= PCM_ENABLE_OUTPUT; |
551 | if ((tmpinfo.mode & AUMODE_RECORD) && !tmpinfo.record.pause) | | 551 | if ((tmpinfo.mode & AUMODE_RECORD) && !tmpinfo.record.pause) |
552 | idat |= PCM_ENABLE_INPUT; | | 552 | idat |= PCM_ENABLE_INPUT; |
553 | INTARG = idat; | | 553 | INTARG = idat; |
554 | break; | | 554 | break; |
555 | case SNDCTL_DSP_GETIPTR: | | 555 | case SNDCTL_DSP_GETIPTR: |
556 | retval = ioctl(fd, AUDIO_GETIOFFS, &tmpoffs); | | 556 | retval = ioctl(fd, AUDIO_GETIOFFS, &tmpoffs); |
557 | if (retval < 0) | | 557 | if (retval < 0) |
558 | return retval; | | 558 | return retval; |
559 | cntinfo.bytes = tmpoffs.samples; | | 559 | cntinfo.bytes = tmpoffs.samples; |
560 | cntinfo.blocks = tmpoffs.deltablks; | | 560 | cntinfo.blocks = tmpoffs.deltablks; |
561 | cntinfo.ptr = tmpoffs.offset; | | 561 | cntinfo.ptr = tmpoffs.offset; |
562 | *(struct count_info *)argp = cntinfo; | | 562 | *(struct count_info *)argp = cntinfo; |
563 | break; | | 563 | break; |
564 | case SNDCTL_DSP_CURRENT_IPTR: | | 564 | case SNDCTL_DSP_CURRENT_IPTR: |
565 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 565 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
566 | if (retval < 0) | | 566 | if (retval < 0) |
567 | return retval; | | 567 | return retval; |
568 | /* XXX: 'samples' may wrap */ | | 568 | /* XXX: 'samples' may wrap */ |
569 | memset(osscount.filler, 0, sizeof(osscount.filler)); | | 569 | memset(osscount.filler, 0, sizeof(osscount.filler)); |
570 | osscount.samples = tmpinfo.record.samples / | | 570 | osscount.samples = tmpinfo.record.samples / |
571 | ((tmpinfo.record.precision / NBBY) * | | 571 | ((tmpinfo.record.precision / NBBY) * |
572 | tmpinfo.record.channels); | | 572 | tmpinfo.record.channels); |
573 | osscount.fifo_samples = tmpinfo.record.seek / | | 573 | osscount.fifo_samples = tmpinfo.record.seek / |
574 | ((tmpinfo.record.precision / NBBY) * | | 574 | ((tmpinfo.record.precision / NBBY) * |
575 | tmpinfo.record.channels); | | 575 | tmpinfo.record.channels); |
576 | *(oss_count_t *)argp = osscount; | | 576 | *(oss_count_t *)argp = osscount; |
577 | break; | | 577 | break; |
578 | case SNDCTL_DSP_GETOPTR: | | 578 | case SNDCTL_DSP_GETOPTR: |
579 | retval = ioctl(fd, AUDIO_GETOOFFS, &tmpoffs); | | 579 | retval = ioctl(fd, AUDIO_GETOOFFS, &tmpoffs); |
580 | if (retval < 0) | | 580 | if (retval < 0) |
581 | return retval; | | 581 | return retval; |
582 | cntinfo.bytes = tmpoffs.samples; | | 582 | cntinfo.bytes = tmpoffs.samples; |
583 | cntinfo.blocks = tmpoffs.deltablks; | | 583 | cntinfo.blocks = tmpoffs.deltablks; |
584 | cntinfo.ptr = tmpoffs.offset; | | 584 | cntinfo.ptr = tmpoffs.offset; |
585 | *(struct count_info *)argp = cntinfo; | | 585 | *(struct count_info *)argp = cntinfo; |
586 | break; | | 586 | break; |
587 | case SNDCTL_DSP_CURRENT_OPTR: | | 587 | case SNDCTL_DSP_CURRENT_OPTR: |
588 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 588 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
589 | if (retval < 0) | | 589 | if (retval < 0) |
590 | return retval; | | 590 | return retval; |
591 | /* XXX: 'samples' may wrap */ | | 591 | /* XXX: 'samples' may wrap */ |
592 | memset(osscount.filler, 0, sizeof(osscount.filler)); | | 592 | memset(osscount.filler, 0, sizeof(osscount.filler)); |
593 | osscount.samples = tmpinfo.play.samples / | | 593 | osscount.samples = tmpinfo.play.samples / |
594 | ((tmpinfo.play.precision / NBBY) * | | 594 | ((tmpinfo.play.precision / NBBY) * |
595 | tmpinfo.play.channels); | | 595 | tmpinfo.play.channels); |
596 | osscount.fifo_samples = tmpinfo.play.seek / | | 596 | osscount.fifo_samples = tmpinfo.play.seek / |
597 | ((tmpinfo.play.precision / NBBY) * | | 597 | ((tmpinfo.play.precision / NBBY) * |
598 | tmpinfo.play.channels); | | 598 | tmpinfo.play.channels); |
599 | *(oss_count_t *)argp = osscount; | | 599 | *(oss_count_t *)argp = osscount; |
600 | break; | | 600 | break; |
601 | case SNDCTL_DSP_SETPLAYVOL: | | 601 | case SNDCTL_DSP_SETPLAYVOL: |
602 | setvol(fd, INTARG, false); | | 602 | setvol(fd, INTARG, false); |
603 | /* FALLTHRU */ | | 603 | /* FALLTHRU */ |
604 | case SNDCTL_DSP_GETPLAYVOL: | | 604 | case SNDCTL_DSP_GETPLAYVOL: |
605 | retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); | | 605 | retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); |
606 | if (retval < 0) | | 606 | if (retval < 0) |
607 | return retval; | | 607 | return retval; |
608 | INTARG = getvol(tmpinfo.play.gain, tmpinfo.play.balance); | | 608 | INTARG = getvol(tmpinfo.play.gain, tmpinfo.play.balance); |
609 | break; | | 609 | break; |
610 | case SNDCTL_DSP_SETRECVOL: | | 610 | case SNDCTL_DSP_SETRECVOL: |
611 | setvol(fd, INTARG, true); | | 611 | setvol(fd, INTARG, true); |
612 | /* FALLTHRU */ | | 612 | /* FALLTHRU */ |
613 | case SNDCTL_DSP_GETRECVOL: | | 613 | case SNDCTL_DSP_GETRECVOL: |
614 | retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); | | 614 | retval = ioctl(fd, AUDIO_GETINFO, &tmpinfo); |
615 | if (retval < 0) | | 615 | if (retval < 0) |
616 | return retval; | | 616 | return retval; |
617 | INTARG = getvol(tmpinfo.record.gain, tmpinfo.record.balance); | | 617 | INTARG = getvol(tmpinfo.record.gain, tmpinfo.record.balance); |
618 | break; | | 618 | break; |
619 | case SNDCTL_DSP_SKIP: | | 619 | case SNDCTL_DSP_SKIP: |
620 | case SNDCTL_DSP_SILENCE: | | 620 | case SNDCTL_DSP_SILENCE: |
621 | return EINVAL; | | 621 | return EINVAL; |
622 | case SNDCTL_DSP_SETDUPLEX: | | 622 | case SNDCTL_DSP_SETDUPLEX: |
623 | idat = 1; | | 623 | idat = 1; |
624 | retval = ioctl(fd, AUDIO_SETFD, &idat); | | 624 | retval = ioctl(fd, AUDIO_SETFD, &idat); |
625 | if (retval < 0) | | 625 | if (retval < 0) |
626 | return retval; | | 626 | return retval; |
627 | break; | | 627 | break; |
628 | case SNDCTL_DSP_GETODELAY: | | 628 | case SNDCTL_DSP_GETODELAY: |
629 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); | | 629 | retval = ioctl(fd, AUDIO_GETBUFINFO, &tmpinfo); |
630 | if (retval < 0) | | 630 | if (retval < 0) |
631 | return retval; | | 631 | return retval; |
632 | idat = tmpinfo.play.seek + tmpinfo.blocksize / 2; | | 632 | idat = tmpinfo.play.seek + tmpinfo.blocksize / 2; |
633 | INTARG = idat; | | 633 | INTARG = idat; |
634 | break; | | 634 | break; |
635 | case SNDCTL_DSP_PROFILE: | | 635 | case SNDCTL_DSP_PROFILE: |
636 | /* This gives just a hint to the driver, | | 636 | /* This gives just a hint to the driver, |
637 | * implementing it as a NOP is ok | | 637 | * implementing it as a NOP is ok |
638 | */ | | 638 | */ |
639 | break; | | 639 | break; |
640 | case SNDCTL_DSP_MAPINBUF: | | 640 | case SNDCTL_DSP_MAPINBUF: |
641 | case SNDCTL_DSP_MAPOUTBUF: | | 641 | case SNDCTL_DSP_MAPOUTBUF: |
642 | case SNDCTL_DSP_SETSYNCRO: | | 642 | case SNDCTL_DSP_SETSYNCRO: |
643 | errno = EINVAL; | | 643 | errno = EINVAL; |
644 | return -1; /* XXX unimplemented */ | | 644 | return -1; /* XXX unimplemented */ |
645 | default: | | 645 | default: |
646 | errno = EINVAL; | | 646 | errno = EINVAL; |
647 | return -1; | | 647 | return -1; |
648 | } | | 648 | } |
649 | | | 649 | |
650 | return 0; | | 650 | return 0; |
651 | } | | 651 | } |
652 | | | 652 | |
653 | | | 653 | |
654 | /* If the NetBSD mixer device should have more than NETBSD_MAXDEVS devices | | 654 | /* If the NetBSD mixer device should have more than NETBSD_MAXDEVS devices |
655 | * some will not be available to OSS applications */ | | 655 | * some will not be available to OSS applications */ |
656 | #define NETBSD_MAXDEVS 64 | | 656 | #define NETBSD_MAXDEVS 64 |
657 | struct audiodevinfo { | | 657 | struct audiodevinfo { |
658 | int done; | | 658 | int done; |
659 | dev_t dev; | | 659 | dev_t dev; |
660 | int16_t devmap[SOUND_MIXER_NRDEVICES], | | 660 | int16_t devmap[SOUND_MIXER_NRDEVICES], |
661 | rdevmap[NETBSD_MAXDEVS]; | | 661 | rdevmap[NETBSD_MAXDEVS]; |
662 | char names[NETBSD_MAXDEVS][MAX_AUDIO_DEV_LEN]; | | 662 | char names[NETBSD_MAXDEVS][MAX_AUDIO_DEV_LEN]; |
663 | int enum2opaque[NETBSD_MAXDEVS]; | | 663 | int enum2opaque[NETBSD_MAXDEVS]; |
664 | u_long devmask, recmask, stereomask; | | 664 | u_long devmask, recmask, stereomask; |
665 | u_long caps; | | 665 | u_long caps; |
666 | int source; | | 666 | int source; |
667 | }; | | 667 | }; |
668 | | | 668 | |
669 | static int | | 669 | static int |
670 | opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq) | | 670 | opaque_to_enum(struct audiodevinfo *di, audio_mixer_name_t *label, int opq) |
671 | { | | 671 | { |
672 | int i, o; | | 672 | int i, o; |
673 | | | 673 | |
674 | for (i = 0; i < NETBSD_MAXDEVS; i++) { | | 674 | for (i = 0; i < NETBSD_MAXDEVS; i++) { |
675 | o = di->enum2opaque[i]; | | 675 | o = di->enum2opaque[i]; |
676 | if (o == opq) | | 676 | if (o == opq) |
677 | break; | | 677 | break; |
678 | if (o == -1 && label != NULL && | | 678 | if (o == -1 && label != NULL && |
679 | !strncmp(di->names[i], label->name, sizeof di->names[i])) { | | 679 | !strncmp(di->names[i], label->name, sizeof di->names[i])) { |
680 | di->enum2opaque[i] = opq; | | 680 | di->enum2opaque[i] = opq; |
681 | break; | | 681 | break; |
682 | } | | 682 | } |
683 | } | | 683 | } |
684 | if (i >= NETBSD_MAXDEVS) | | 684 | if (i >= NETBSD_MAXDEVS) |
685 | i = -1; | | 685 | i = -1; |
686 | /*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/ | | 686 | /*printf("opq_to_enum %s %d -> %d\n", label->name, opq, i);*/ |
687 | return (i); | | 687 | return (i); |
688 | } | | 688 | } |
689 | | | 689 | |
690 | static int | | 690 | static int |
691 | enum_to_ord(struct audiodevinfo *di, int enm) | | 691 | enum_to_ord(struct audiodevinfo *di, int enm) |
692 | { | | 692 | { |
693 | if (enm >= NETBSD_MAXDEVS) | | 693 | if (enm >= NETBSD_MAXDEVS) |
694 | return (-1); | | 694 | return (-1); |
695 | | | 695 | |
696 | /*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/ | | 696 | /*printf("enum_to_ord %d -> %d\n", enm, di->enum2opaque[enm]);*/ |
697 | return (di->enum2opaque[enm]); | | 697 | return (di->enum2opaque[enm]); |
698 | } | | 698 | } |
699 | | | 699 | |
700 | static int | | 700 | static int |
701 | enum_to_mask(struct audiodevinfo *di, int enm) | | 701 | enum_to_mask(struct audiodevinfo *di, int enm) |
702 | { | | 702 | { |
703 | int m; | | 703 | int m; |
704 | if (enm >= NETBSD_MAXDEVS) | | 704 | if (enm >= NETBSD_MAXDEVS) |
705 | return (0); | | 705 | return (0); |
706 | | | 706 | |
707 | m = di->enum2opaque[enm]; | | 707 | m = di->enum2opaque[enm]; |
708 | if (m == -1) | | 708 | if (m == -1) |
709 | m = 0; | | 709 | m = 0; |
710 | /*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/ | | 710 | /*printf("enum_to_mask %d -> %d\n", enm, di->enum2opaque[enm]);*/ |
711 | return (m); | | 711 | return (m); |
712 | } | | 712 | } |
713 | | | 713 | |
714 | /* | | 714 | /* |
715 | * Collect the audio device information to allow faster | | 715 | * Collect the audio device information to allow faster |
716 | * emulation of the OSSv3 mixer ioctls. Cache the information | | 716 | * emulation of the OSSv3 mixer ioctls. Cache the information |
717 | * to eliminate the overhead of repeating all the ioctls needed | | 717 | * to eliminate the overhead of repeating all the ioctls needed |
718 | * to collect the information. | | 718 | * to collect the information. |
719 | */ | | 719 | */ |
720 | static struct audiodevinfo * | | 720 | static struct audiodevinfo * |
721 | getdevinfo(int fd) | | 721 | getdevinfo(int fd) |
722 | { | | 722 | { |
723 | mixer_devinfo_t mi; | | 723 | mixer_devinfo_t mi; |
724 | int i, j, e; | | 724 | int i, j, e; |
725 | static struct { | | 725 | static struct { |
726 | const char *name; | | 726 | const char *name; |
727 | int code; | | 727 | int code; |
728 | } *dp, devs[] = { | | 728 | } *dp, devs[] = { |
729 | { AudioNmicrophone, SOUND_MIXER_MIC }, | | 729 | { AudioNmicrophone, SOUND_MIXER_MIC }, |
730 | { AudioNline, SOUND_MIXER_LINE }, | | 730 | { AudioNline, SOUND_MIXER_LINE }, |
731 | { AudioNcd, SOUND_MIXER_CD }, | | 731 | { AudioNcd, SOUND_MIXER_CD }, |
732 | { AudioNdac, SOUND_MIXER_PCM }, | | 732 | { AudioNdac, SOUND_MIXER_PCM }, |
733 | { AudioNaux, SOUND_MIXER_LINE1 }, | | 733 | { AudioNaux, SOUND_MIXER_LINE1 }, |
734 | { AudioNrecord, SOUND_MIXER_IMIX }, | | 734 | { AudioNrecord, SOUND_MIXER_IMIX }, |
735 | { AudioNmaster, SOUND_MIXER_VOLUME }, | | 735 | { AudioNmaster, SOUND_MIXER_VOLUME }, |
736 | { AudioNtreble, SOUND_MIXER_TREBLE }, | | 736 | { AudioNtreble, SOUND_MIXER_TREBLE }, |
737 | { AudioNbass, SOUND_MIXER_BASS }, | | 737 | { AudioNbass, SOUND_MIXER_BASS }, |
738 | { AudioNspeaker, SOUND_MIXER_SPEAKER }, | | 738 | { AudioNspeaker, SOUND_MIXER_SPEAKER }, |
739 | /* { AudioNheadphone, ?? },*/ | | 739 | /* { AudioNheadphone, ?? },*/ |
740 | { AudioNoutput, SOUND_MIXER_OGAIN }, | | 740 | { AudioNoutput, SOUND_MIXER_OGAIN }, |
741 | { AudioNinput, SOUND_MIXER_IGAIN }, | | 741 | { AudioNinput, SOUND_MIXER_IGAIN }, |
742 | /* { AudioNmaster, SOUND_MIXER_SPEAKER },*/ | | 742 | /* { AudioNmaster, SOUND_MIXER_SPEAKER },*/ |
743 | /* { AudioNstereo, ?? },*/ | | 743 | /* { AudioNstereo, ?? },*/ |
744 | /* { AudioNmono, ?? },*/ | | 744 | /* { AudioNmono, ?? },*/ |
745 | { AudioNfmsynth, SOUND_MIXER_SYNTH }, | | 745 | { AudioNfmsynth, SOUND_MIXER_SYNTH }, |
746 | /* { AudioNwave, SOUND_MIXER_PCM },*/ | | 746 | /* { AudioNwave, SOUND_MIXER_PCM },*/ |
747 | { AudioNmidi, SOUND_MIXER_SYNTH }, | | 747 | { AudioNmidi, SOUND_MIXER_SYNTH }, |
748 | /* { AudioNmixerout, ?? },*/ | | 748 | /* { AudioNmixerout, ?? },*/ |
749 | { 0, -1 } | | 749 | { 0, -1 } |
750 | }; | | 750 | }; |
751 | static struct audiodevinfo devcache = { .done = 0 }; | | 751 | static struct audiodevinfo devcache = { .done = 0 }; |
752 | struct audiodevinfo *di = &devcache; | | 752 | struct audiodevinfo *di = &devcache; |
753 | struct stat sb; | | 753 | struct stat sb; |
754 | size_t mlen, dlen; | | 754 | size_t mlen, dlen; |
755 | | | 755 | |
756 | /* Figure out what device it is so we can check if the | | 756 | /* Figure out what device it is so we can check if the |
757 | * cached data is valid. | | 757 | * cached data is valid. |
758 | */ | | 758 | */ |
759 | if (fstat(fd, &sb) < 0) | | 759 | if (fstat(fd, &sb) < 0) |
760 | return 0; | | 760 | return 0; |
761 | if (di->done && di->dev == sb.st_dev) | | 761 | if (di->done && di->dev == sb.st_dev) |
762 | return di; | | 762 | return di; |
763 | | | 763 | |
764 | di->done = 1; | | 764 | di->done = 1; |
765 | di->dev = sb.st_dev; | | 765 | di->dev = sb.st_dev; |
766 | di->devmask = 0; | | 766 | di->devmask = 0; |
767 | di->recmask = 0; | | 767 | di->recmask = 0; |
768 | di->stereomask = 0; | | 768 | di->stereomask = 0; |
769 | di->source = ~0; | | 769 | di->source = ~0; |
770 | di->caps = 0; | | 770 | di->caps = 0; |
771 | for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) | | 771 | for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) |
772 | di->devmap[i] = -1; | | 772 | di->devmap[i] = -1; |
773 | for(i = 0; i < NETBSD_MAXDEVS; i++) { | | 773 | for(i = 0; i < NETBSD_MAXDEVS; i++) { |
774 | di->rdevmap[i] = -1; | | 774 | di->rdevmap[i] = -1; |
775 | di->names[i][0] = '\0'; | | 775 | di->names[i][0] = '\0'; |
776 | di->enum2opaque[i] = -1; | | 776 | di->enum2opaque[i] = -1; |
777 | } | | 777 | } |
778 | for(i = 0; i < NETBSD_MAXDEVS; i++) { | | 778 | for(i = 0; i < NETBSD_MAXDEVS; i++) { |
779 | mi.index = i; | | 779 | mi.index = i; |
780 | if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) | | 780 | if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) |
781 | break; | | 781 | break; |
782 | switch(mi.type) { | | 782 | switch(mi.type) { |
783 | case AUDIO_MIXER_VALUE: | | 783 | case AUDIO_MIXER_VALUE: |
784 | for(dp = devs; dp->name; dp++) { | | 784 | for(dp = devs; dp->name; dp++) { |
785 | if (strcmp(dp->name, mi.label.name) == 0) | | 785 | if (strcmp(dp->name, mi.label.name) == 0) |
786 | break; | | 786 | break; |
787 | dlen = strlen(dp->name); | | 787 | dlen = strlen(dp->name); |
788 | mlen = strlen(mi.label.name); | | 788 | mlen = strlen(mi.label.name); |
789 | if (dlen < mlen | | 789 | if (dlen < mlen |
790 | && mi.label.name[mlen-dlen-1] == '.' | | 790 | && mi.label.name[mlen-dlen-1] == '.' |
791 | && strcmp(dp->name, | | 791 | && strcmp(dp->name, |
792 | mi.label.name + mlen - dlen) == 0) | | 792 | mi.label.name + mlen - dlen) == 0) |
793 | break; | | 793 | break; |
794 | } | | 794 | } |
795 | if (dp->code >= 0) { | | 795 | if (dp->code >= 0) { |
796 | di->devmap[dp->code] = i; | | 796 | di->devmap[dp->code] = i; |
797 | di->rdevmap[i] = dp->code; | | 797 | di->rdevmap[i] = dp->code; |
798 | di->devmask |= 1 << dp->code; | | 798 | di->devmask |= 1 << dp->code; |
799 | if (mi.un.v.num_channels == 2) | | 799 | if (mi.un.v.num_channels == 2) |
800 | di->stereomask |= 1 << dp->code; | | 800 | di->stereomask |= 1 << dp->code; |
801 | strlcpy(di->names[i], mi.label.name, | | 801 | strlcpy(di->names[i], mi.label.name, |
802 | sizeof di->names[i]); | | 802 | sizeof di->names[i]); |
803 | } | | 803 | } |
804 | break; | | 804 | break; |
805 | } | | 805 | } |
806 | } | | 806 | } |
807 | for(i = 0; i < NETBSD_MAXDEVS; i++) { | | 807 | for(i = 0; i < NETBSD_MAXDEVS; i++) { |
808 | mi.index = i; | | 808 | mi.index = i; |
809 | if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) | | 809 | if (ioctl(fd, AUDIO_MIXER_DEVINFO, &mi) < 0) |
810 | break; | | 810 | break; |
811 | if (strcmp(mi.label.name, AudioNsource) != 0) | | 811 | if (strcmp(mi.label.name, AudioNsource) != 0) |
812 | continue; | | 812 | continue; |
813 | di->source = i; | | 813 | di->source = i; |
814 | switch(mi.type) { | | 814 | switch(mi.type) { |
815 | case AUDIO_MIXER_ENUM: | | 815 | case AUDIO_MIXER_ENUM: |
816 | for(j = 0; j < mi.un.e.num_mem; j++) { | | 816 | for(j = 0; j < mi.un.e.num_mem; j++) { |
817 | e = opaque_to_enum(di, | | 817 | e = opaque_to_enum(di, |
818 | &mi.un.e.member[j].label, | | 818 | &mi.un.e.member[j].label, |
819 | mi.un.e.member[j].ord); | | 819 | mi.un.e.member[j].ord); |
820 | if (e >= 0) | | 820 | if (e >= 0) |
821 | di->recmask |= 1 << di->rdevmap[e]; | | 821 | di->recmask |= 1 << di->rdevmap[e]; |
822 | } | | 822 | } |
823 | di->caps = SOUND_CAP_EXCL_INPUT; | | 823 | di->caps = SOUND_CAP_EXCL_INPUT; |
824 | break; | | 824 | break; |
825 | case AUDIO_MIXER_SET: | | 825 | case AUDIO_MIXER_SET: |
826 | for(j = 0; j < mi.un.s.num_mem; j++) { | | 826 | for(j = 0; j < mi.un.s.num_mem; j++) { |
827 | e = opaque_to_enum(di, | | 827 | e = opaque_to_enum(di, |
828 | &mi.un.s.member[j].label, | | 828 | &mi.un.s.member[j].label, |
829 | mi.un.s.member[j].mask); | | 829 | mi.un.s.member[j].mask); |
830 | if (e >= 0) | | 830 | if (e >= 0) |
831 | di->recmask |= 1 << di->rdevmap[e]; | | 831 | di->recmask |= 1 << di->rdevmap[e]; |
832 | } | | 832 | } |
833 | break; | | 833 | break; |
834 | } | | 834 | } |
835 | } | | 835 | } |
836 | return di; | | 836 | return di; |
837 | } | | 837 | } |
838 | | | 838 | |
839 | static int | | 839 | static int |
840 | mixer_oss3_ioctl(int fd, unsigned long com, void *argp) | | 840 | mixer_oss3_ioctl(int fd, unsigned long com, void *argp) |
841 | { | | 841 | { |
842 | struct audiodevinfo *di; | | 842 | struct audiodevinfo *di; |
843 | struct mixer_info *omi; | | 843 | struct mixer_info *omi; |
844 | struct audio_device adev; | | 844 | struct audio_device adev; |
845 | mixer_ctrl_t mc; | | 845 | mixer_ctrl_t mc; |
846 | u_long idat, n; | | 846 | u_long idat, n; |
847 | int i; | | 847 | int i; |
848 | int retval; | | 848 | int retval; |
849 | int l, r, error, e; | | 849 | int l, r, error, e; |
850 | | | 850 | |
851 | idat = 0; | | 851 | idat = 0; |
852 | di = getdevinfo(fd); | | 852 | di = getdevinfo(fd); |
853 | if (di == 0) | | 853 | if (di == 0) |
854 | return -1; | | 854 | return -1; |
855 | | | 855 | |
856 | switch (com) { | | 856 | switch (com) { |
857 | case OSS_GETVERSION: | | 857 | case OSS_GETVERSION: |
858 | idat = SOUND_VERSION; | | 858 | idat = SOUND_VERSION; |
859 | break; | | 859 | break; |
860 | case SOUND_MIXER_INFO: | | 860 | case SOUND_MIXER_INFO: |
861 | case SOUND_OLD_MIXER_INFO: | | 861 | case SOUND_OLD_MIXER_INFO: |
862 | error = ioctl(fd, AUDIO_GETDEV, &adev); | | 862 | error = ioctl(fd, AUDIO_GETDEV, &adev); |
863 | if (error) | | 863 | if (error) |
864 | return (error); | | 864 | return (error); |
865 | omi = argp; | | 865 | omi = argp; |
866 | if (com == SOUND_MIXER_INFO) | | 866 | if (com == SOUND_MIXER_INFO) |
867 | omi->modify_counter = 1; | | 867 | omi->modify_counter = 1; |
868 | strlcpy(omi->id, adev.name, sizeof omi->id); | | 868 | strlcpy(omi->id, adev.name, sizeof omi->id); |
869 | strlcpy(omi->name, adev.name, sizeof omi->name); | | 869 | strlcpy(omi->name, adev.name, sizeof omi->name); |
870 | return 0; | | 870 | return 0; |
871 | case SOUND_MIXER_READ_RECSRC: | | 871 | case SOUND_MIXER_READ_RECSRC: |
872 | if (di->source == -1) | | 872 | if (di->source == -1) |
873 | return EINVAL; | | 873 | return EINVAL; |
874 | mc.dev = di->source; | | 874 | mc.dev = di->source; |
875 | if (di->caps & SOUND_CAP_EXCL_INPUT) { | | 875 | if (di->caps & SOUND_CAP_EXCL_INPUT) { |
876 | mc.type = AUDIO_MIXER_ENUM; | | 876 | mc.type = AUDIO_MIXER_ENUM; |
877 | retval = ioctl(fd, AUDIO_MIXER_READ, &mc); | | 877 | retval = ioctl(fd, AUDIO_MIXER_READ, &mc); |
878 | if (retval < 0) | | 878 | if (retval < 0) |
879 | return retval; | | 879 | return retval; |
880 | e = opaque_to_enum(di, NULL, mc.un.ord); | | 880 | e = opaque_to_enum(di, NULL, mc.un.ord); |
881 | if (e >= 0) | | 881 | if (e >= 0) |
882 | idat = 1 << di->rdevmap[e]; | | 882 | idat = 1 << di->rdevmap[e]; |
883 | } else { | | 883 | } else { |
884 | mc.type = AUDIO_MIXER_SET; | | 884 | mc.type = AUDIO_MIXER_SET; |
885 | retval = ioctl(fd, AUDIO_MIXER_READ, &mc); | | 885 | retval = ioctl(fd, AUDIO_MIXER_READ, &mc); |
886 | if (retval < 0) | | 886 | if (retval < 0) |
887 | return retval; | | 887 | return retval; |
888 | e = opaque_to_enum(di, NULL, mc.un.mask); | | 888 | e = opaque_to_enum(di, NULL, mc.un.mask); |
889 | if (e >= 0) | | 889 | if (e >= 0) |
890 | idat = 1 << di->rdevmap[e]; | | 890 | idat = 1 << di->rdevmap[e]; |
891 | } | | 891 | } |
892 | break; | | 892 | break; |
893 | case SOUND_MIXER_READ_DEVMASK: | | 893 | case SOUND_MIXER_READ_DEVMASK: |
894 | idat = di->devmask; | | 894 | idat = di->devmask; |
895 | break; | | 895 | break; |
896 | case SOUND_MIXER_READ_RECMASK: | | 896 | case SOUND_MIXER_READ_RECMASK: |
897 | idat = di->recmask; | | 897 | idat = di->recmask; |
898 | break; | | 898 | break; |
899 | case SOUND_MIXER_READ_STEREODEVS: | | 899 | case SOUND_MIXER_READ_STEREODEVS: |
900 | idat = di->stereomask; | | 900 | idat = di->stereomask; |
901 | break; | | 901 | break; |
902 | case SOUND_MIXER_READ_CAPS: | | 902 | case SOUND_MIXER_READ_CAPS: |
903 | idat = di->caps; | | 903 | idat = di->caps; |
904 | break; | | 904 | break; |
905 | case SOUND_MIXER_WRITE_RECSRC: | | 905 | case SOUND_MIXER_WRITE_RECSRC: |
906 | case SOUND_MIXER_WRITE_R_RECSRC: | | 906 | case SOUND_MIXER_WRITE_R_RECSRC: |
907 | if (di->source == -1) | | 907 | if (di->source == -1) |
908 | return EINVAL; | | 908 | return EINVAL; |
909 | mc.dev = di->source; | | 909 | mc.dev = di->source; |
910 | idat = INTARG; | | 910 | idat = INTARG; |
911 | if (di->caps & SOUND_CAP_EXCL_INPUT) { | | 911 | if (di->caps & SOUND_CAP_EXCL_INPUT) { |
912 | mc.type = AUDIO_MIXER_ENUM; | | 912 | mc.type = AUDIO_MIXER_ENUM; |
913 | for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) | | 913 | for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) |
914 | if (idat & (1 << i)) | | 914 | if (idat & (1 << i)) |
915 | break; | | 915 | break; |
916 | if (i >= SOUND_MIXER_NRDEVICES || | | 916 | if (i >= SOUND_MIXER_NRDEVICES || |
917 | di->devmap[i] == -1) | | 917 | di->devmap[i] == -1) |
918 | return EINVAL; | | 918 | return EINVAL; |
919 | mc.un.ord = enum_to_ord(di, di->devmap[i]); | | 919 | mc.un.ord = enum_to_ord(di, di->devmap[i]); |
920 | } else { | | 920 | } else { |
921 | mc.type = AUDIO_MIXER_SET; | | 921 | mc.type = AUDIO_MIXER_SET; |
922 | mc.un.mask = 0; | | 922 | mc.un.mask = 0; |
923 | for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) { | | 923 | for(i = 0; i < SOUND_MIXER_NRDEVICES; i++) { |
924 | if (idat & (1 << i)) { | | 924 | if (idat & (1 << i)) { |
925 | if (di->devmap[i] == -1) | | 925 | if (di->devmap[i] == -1) |
926 | return EINVAL; | | 926 | return EINVAL; |
927 | mc.un.mask |= | | 927 | mc.un.mask |= |
928 | enum_to_mask(di, di->devmap[i]); | | 928 | enum_to_mask(di, di->devmap[i]); |
929 | } | | 929 | } |
930 | } | | 930 | } |
931 | } | | 931 | } |
932 | return ioctl(fd, AUDIO_MIXER_WRITE, &mc); | | 932 | return ioctl(fd, AUDIO_MIXER_WRITE, &mc); |
933 | default: | | 933 | default: |
934 | if (MIXER_READ(SOUND_MIXER_FIRST) <= com && | | 934 | if (MIXER_READ(SOUND_MIXER_FIRST) <= com && |
935 | com < MIXER_READ(SOUND_MIXER_NRDEVICES)) { | | 935 | com < MIXER_READ(SOUND_MIXER_NRDEVICES)) { |
936 | n = GET_DEV(com); | | 936 | n = GET_DEV(com); |
937 | if (di->devmap[n] == -1) | | 937 | if (di->devmap[n] == -1) |
938 | return EINVAL; | | 938 | return EINVAL; |
939 | mc.dev = di->devmap[n]; | | 939 | mc.dev = di->devmap[n]; |
940 | mc.type = AUDIO_MIXER_VALUE; | | 940 | mc.type = AUDIO_MIXER_VALUE; |
941 | doread: | | 941 | doread: |
942 | mc.un.value.num_channels = | | 942 | mc.un.value.num_channels = |
943 | di->stereomask & (1 << (u_int)n) ? 2 : 1; | | 943 | di->stereomask & (1 << (u_int)n) ? 2 : 1; |
944 | retval = ioctl(fd, AUDIO_MIXER_READ, &mc); | | 944 | retval = ioctl(fd, AUDIO_MIXER_READ, &mc); |
945 | if (retval < 0) | | 945 | if (retval < 0) |
946 | return retval; | | 946 | return retval; |
947 | if (mc.type != AUDIO_MIXER_VALUE) | | 947 | if (mc.type != AUDIO_MIXER_VALUE) |
948 | return EINVAL; | | 948 | return EINVAL; |
949 | if (mc.un.value.num_channels != 2) { | | 949 | if (mc.un.value.num_channels != 2) { |
950 | l = r = | | 950 | l = r = |
951 | mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; | | 951 | mc.un.value.level[AUDIO_MIXER_LEVEL_MONO]; |
952 | } else { | | 952 | } else { |
953 | l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; | | 953 | l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT]; |
954 | r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; | | 954 | r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT]; |
955 | } | | 955 | } |
956 | idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); | | 956 | idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8); |
957 | break; | | 957 | break; |
958 | } else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com && | | 958 | } else if ((MIXER_WRITE_R(SOUND_MIXER_FIRST) <= com && |
959 | com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) || | | 959 | com < MIXER_WRITE_R(SOUND_MIXER_NRDEVICES)) || |
960 | (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && | | 960 | (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && |
961 | com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) { | | 961 | com < MIXER_WRITE(SOUND_MIXER_NRDEVICES))) { |
962 | n = GET_DEV(com); | | 962 | n = GET_DEV(com); |
963 | if (di->devmap[n] == -1) | | 963 | if (di->devmap[n] == -1) |
964 | return EINVAL; | | 964 | return EINVAL; |
965 | idat = INTARG; | | 965 | idat = INTARG; |
966 | l = FROM_OSSVOL((u_int)idat & 0xff); | | 966 | l = FROM_OSSVOL((u_int)idat & 0xff); |
967 | r = FROM_OSSVOL(((u_int)idat >> 8) & 0xff); | | 967 | r = FROM_OSSVOL(((u_int)idat >> 8) & 0xff); |
968 | mc.dev = di->devmap[n]; | | 968 | mc.dev = di->devmap[n]; |
969 | mc.type = AUDIO_MIXER_VALUE; | | 969 | mc.type = AUDIO_MIXER_VALUE; |
970 | if (di->stereomask & (1 << (u_int)n)) { | | 970 | if (di->stereomask & (1 << (u_int)n)) { |
971 | mc.un.value.num_channels = 2; | | 971 | mc.un.value.num_channels = 2; |
972 | mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; | | 972 | mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l; |
973 | mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; | | 973 | mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r; |
974 | } else { | | 974 | } else { |
975 | mc.un.value.num_channels = 1; | | 975 | mc.un.value.num_channels = 1; |
976 | mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = | | 976 | mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = |
977 | (l + r) / 2; | | 977 | (l + r) / 2; |
978 | } | | 978 | } |
979 | retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc); | | 979 | retval = ioctl(fd, AUDIO_MIXER_WRITE, &mc); |
980 | if (retval < 0) | | 980 | if (retval < 0) |
981 | return retval; | | 981 | return retval; |
982 | if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && | | 982 | if (MIXER_WRITE(SOUND_MIXER_FIRST) <= com && |
983 | com < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) | | 983 | com < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) |
984 | return 0; | | 984 | return 0; |
985 | goto doread; | | 985 | goto doread; |
986 | } else { | | 986 | } else { |
987 | errno = EINVAL; | | 987 | errno = EINVAL; |
988 | return -1; | | 988 | return -1; |
989 | } | | 989 | } |
990 | } | | 990 | } |
991 | INTARG = (int)idat; | | 991 | INTARG = (int)idat; |
992 | return 0; | | 992 | return 0; |
993 | } | | 993 | } |
994 | | | 994 | |
995 | static int | | 995 | static int |
996 | mixer_oss4_ioctl(int fd, unsigned long com, void *argp) | | 996 | mixer_oss4_ioctl(int fd, unsigned long com, void *argp) |
997 | { | | 997 | { |
998 | oss_audioinfo *tmpai; | | 998 | oss_audioinfo *tmpai; |
999 | oss_card_info *cardinfo; | | 999 | oss_card_info *cardinfo; |
1000 | oss_mixext *ext; | | 1000 | oss_mixext *ext; |
1001 | oss_mixext_root root; | | 1001 | oss_mixext_root root; |
1002 | oss_mixer_enuminfo *ei; | | 1002 | oss_mixer_enuminfo *ei; |
1003 | oss_mixer_value *mv; | | 1003 | oss_mixer_value *mv; |
1004 | oss_mixerinfo *mi; | | 1004 | oss_mixerinfo *mi; |
1005 | oss_sysinfo sysinfo; | | 1005 | oss_sysinfo sysinfo; |
1006 | dev_t devno; | | 1006 | dev_t devno; |
1007 | struct stat tmpstat; | | 1007 | struct stat tmpstat; |
1008 | struct audio_device dev; | | 1008 | struct audio_device dev; |
1009 | struct audio_format_query fmtq; | | 1009 | struct audio_format_query fmtq; |
1010 | struct mixer_devinfo mdi; | | 1010 | struct mixer_devinfo mdi; |
1011 | struct mixer_ctrl mc; | | 1011 | struct mixer_ctrl mc; |
1012 | char devname[32]; | | 1012 | char devname[32]; |
1013 | size_t len; | | 1013 | size_t len; |
1014 | int newfd = -1, tmperrno; | | 1014 | int newfd = -1, tmperrno; |
1015 | int i, noffs; | | 1015 | int i, noffs; |
1016 | int retval; | | 1016 | int retval; |
1017 | int props, caps; | | 1017 | int props, caps; |
1018 | | | 1018 | |
1019 | /* | | 1019 | /* |
1020 | * Note: it is difficult to translate the NetBSD concept of a "set" | | 1020 | * Note: it is difficult to translate the NetBSD concept of a "set" |
1021 | * mixer control type to the OSSv4 API, as far as I can tell. | | 1021 | * mixer control type to the OSSv4 API, as far as I can tell. |
1022 | * | | 1022 | * |
1023 | * This means they are treated like enums, i.e. only one entry in the | | 1023 | * This means they are treated like enums, i.e. only one entry in the |
1024 | * set can be selected at a time. | | 1024 | * set can be selected at a time. |
1025 | */ | | 1025 | */ |
1026 | | | 1026 | |
1027 | switch (com) { | | 1027 | switch (com) { |
1028 | case SNDCTL_AUDIOINFO: | | 1028 | case SNDCTL_AUDIOINFO: |
1029 | /* | | 1029 | /* |
1030 | * SNDCTL_AUDIOINFO_EX is intended for underlying hardware devices | | 1030 | * SNDCTL_AUDIOINFO_EX is intended for underlying hardware devices |
1031 | * that are to be opened in "exclusive mode" (bypassing the normal | | 1031 | * that are to be opened in "exclusive mode" (bypassing the normal |
1032 | * kernel mixer for exclusive control). NetBSD does not support | | 1032 | * kernel mixer for exclusive control). NetBSD does not support |
1033 | * bypassing the kernel mixer, so it's an alias of SNDCTL_AUDIOINFO. | | 1033 | * bypassing the kernel mixer, so it's an alias of SNDCTL_AUDIOINFO. |
1034 | */ | | 1034 | */ |
1035 | case SNDCTL_AUDIOINFO_EX: | | 1035 | case SNDCTL_AUDIOINFO_EX: |
1036 | case SNDCTL_ENGINEINFO: | | 1036 | case SNDCTL_ENGINEINFO: |
1037 | devno = 0; | | 1037 | devno = 0; |
1038 | tmpai = (struct oss_audioinfo*)argp; | | 1038 | tmpai = (struct oss_audioinfo*)argp; |
1039 | if (tmpai == NULL) { | | 1039 | if (tmpai == NULL) { |
1040 | errno = EINVAL; | | 1040 | errno = EINVAL; |
1041 | return -1; | | 1041 | return -1; |
1042 | } | | 1042 | } |
1043 | | | 1043 | |
1044 | /* | | 1044 | /* |
1045 | * If the input device is -1, guess the device related to | | 1045 | * If the input device is -1, guess the device related to |
1046 | * the open mixer device. | | 1046 | * the open mixer device. |
1047 | */ | | 1047 | */ |
1048 | if (tmpai->dev < 0) { | | 1048 | if (tmpai->dev < 0) { |
1049 | fstat(fd, &tmpstat); | | 1049 | fstat(fd, &tmpstat); |
1050 | if ((tmpstat.st_rdev & 0xff00) == 0x2a00) | | 1050 | if ((tmpstat.st_rdev & 0xff00) == 0x2a00) |
1051 | devno = tmpstat.st_rdev & 0xff; | | 1051 | devno = tmpstat.st_rdev & 0xff; |
1052 | if (devno >= 0x80) | | 1052 | if (devno >= 0x80) |
1053 | tmpai->dev = devno & 0x7f; | | 1053 | tmpai->dev = devno & 0x7f; |
1054 | } | | 1054 | } |
1055 | if (tmpai->dev < 0) | | 1055 | if (tmpai->dev < 0) |
1056 | tmpai->dev = 0; | | 1056 | tmpai->dev = 0; |
1057 | | | 1057 | |
1058 | snprintf(tmpai->devnode, sizeof(tmpai->devnode), | | 1058 | snprintf(tmpai->devnode, sizeof(tmpai->devnode), |
1059 | "/dev/audio%d", tmpai->dev); | | 1059 | "/dev/audio%d", tmpai->dev); |
1060 | | | 1060 | |
1061 | if ((newfd = open(tmpai->devnode, O_WRONLY)) < 0) { | | 1061 | if ((newfd = open(tmpai->devnode, O_WRONLY)) < 0) { |
1062 | if ((newfd = open(tmpai->devnode, O_RDONLY)) < 0) { | | 1062 | if ((newfd = open(tmpai->devnode, O_RDONLY)) < 0) { |
1063 | return newfd; | | 1063 | return newfd; |
1064 | } | | 1064 | } |
1065 | } | | 1065 | } |
1066 | | | 1066 | |
1067 | retval = ioctl(newfd, AUDIO_GETDEV, &dev); | | 1067 | retval = ioctl(newfd, AUDIO_GETDEV, &dev); |
1068 | if (retval < 0) { | | 1068 | if (retval < 0) { |
1069 | tmperrno = errno; | | 1069 | tmperrno = errno; |
1070 | close(newfd); | | 1070 | close(newfd); |
1071 | errno = tmperrno; | | 1071 | errno = tmperrno; |
1072 | return retval; | | 1072 | return retval; |
1073 | } | | 1073 | } |
1074 | retval = ioctl(newfd, AUDIO_GETPROPS, &props); | | 1074 | retval = ioctl(newfd, AUDIO_GETPROPS, &props); |
1075 | if (retval < 0) { | | 1075 | if (retval < 0) { |
1076 | tmperrno = errno; | | 1076 | tmperrno = errno; |
1077 | close(newfd); | | 1077 | close(newfd); |
1078 | errno = tmperrno; | | 1078 | errno = tmperrno; |
1079 | return retval; | | 1079 | return retval; |
1080 | } | | 1080 | } |
1081 | caps = DSP_CAP_TRIGGER; | | 1081 | caps = DSP_CAP_TRIGGER; |
1082 | if (props & AUDIO_PROP_FULLDUPLEX) | | 1082 | if (props & AUDIO_PROP_FULLDUPLEX) |
1083 | caps |= DSP_CAP_DUPLEX; | | 1083 | caps |= DSP_CAP_DUPLEX; |
1084 | if (props & AUDIO_PROP_MMAP) | | 1084 | if (props & AUDIO_PROP_MMAP) |
1085 | caps |= DSP_CAP_MMAP; | | 1085 | caps |= DSP_CAP_MMAP; |
1086 | if (props & AUDIO_PROP_CAPTURE) | | 1086 | if (props & AUDIO_PROP_CAPTURE) |
1087 | caps |= PCM_CAP_INPUT; | | 1087 | caps |= PCM_CAP_INPUT; |
1088 | if (props & AUDIO_PROP_PLAYBACK) | | 1088 | if (props & AUDIO_PROP_PLAYBACK) |
1089 | caps |= PCM_CAP_OUTPUT; | | 1089 | caps |= PCM_CAP_OUTPUT; |
1090 | snprintf(tmpai->name, sizeof(tmpai->name), | | 1090 | snprintf(tmpai->name, sizeof(tmpai->name), |
1091 | "%s %s", dev.name, dev.version); | | 1091 | "%s %s", dev.name, dev.version); |
1092 | tmpai->busy = 0; | | 1092 | tmpai->busy = 0; |
1093 | tmpai->pid = -1; | | 1093 | tmpai->pid = -1; |
1094 | tmpai->caps = caps; | | 1094 | tmpai->caps = caps; |
1095 | ioctl(newfd, SNDCTL_DSP_GETFMTS, &tmpai->iformats); | | 1095 | ioctl(newfd, SNDCTL_DSP_GETFMTS, &tmpai->iformats); |
1096 | tmpai->oformats = tmpai->iformats; | | 1096 | tmpai->oformats = tmpai->iformats; |
1097 | tmpai->magic = -1; /* reserved for "internal use" */ | | 1097 | tmpai->magic = -1; /* reserved for "internal use" */ |
1098 | memset(tmpai->cmd, 0, sizeof(tmpai->cmd)); | | 1098 | memset(tmpai->cmd, 0, sizeof(tmpai->cmd)); |
1099 | tmpai->card_number = -1; | | 1099 | tmpai->card_number = -1; |
1100 | memset(tmpai->song_name, 0, | | 1100 | memset(tmpai->song_name, 0, |
1101 | sizeof(tmpai->song_name)); | | 1101 | sizeof(tmpai->song_name)); |
1102 | memset(tmpai->label, 0, sizeof(tmpai->label)); | | 1102 | memset(tmpai->label, 0, sizeof(tmpai->label)); |
1103 | tmpai->port_number = 0; | | 1103 | tmpai->port_number = 0; |
1104 | tmpai->mixer_dev = tmpai->dev; | | 1104 | tmpai->mixer_dev = tmpai->dev; |
1105 | tmpai->legacy_device = tmpai->dev; | | 1105 | tmpai->legacy_device = tmpai->dev; |
1106 | tmpai->enabled = 1; | | 1106 | tmpai->enabled = 1; |
1107 | tmpai->flags = -1; /* reserved for "future versions" */ | | 1107 | tmpai->flags = -1; /* reserved for "future versions" */ |
1108 | tmpai->min_rate = 1000; | | 1108 | tmpai->min_rate = 1000; |
1109 | tmpai->max_rate = 192000; | | 1109 | tmpai->max_rate = 192000; |
1110 | tmpai->nrates = 0; | | 1110 | tmpai->nrates = 0; |
1111 | tmpai->min_channels = 1; | | 1111 | tmpai->min_channels = 1; |
1112 | tmpai->max_channels = 2; | | 1112 | tmpai->max_channels = 2; |
1113 | for (fmtq.index = 0; | | 1113 | for (fmtq.index = 0; |
1114 | ioctl(newfd, AUDIO_QUERYFORMAT, &fmtq) != -1; ++fmtq.index) { | | 1114 | ioctl(newfd, AUDIO_QUERYFORMAT, &fmtq) != -1; ++fmtq.index) { |
1115 | if (fmtq.fmt.channels > (unsigned)tmpai->max_channels) | | 1115 | if (fmtq.fmt.channels > (unsigned)tmpai->max_channels) |
1116 | tmpai->max_channels = fmtq.fmt.channels; | | 1116 | tmpai->max_channels = fmtq.fmt.channels; |
1117 | } | | 1117 | } |
1118 | tmpai->binding = -1; /* reserved for "future versions" */ | | 1118 | tmpai->binding = -1; /* reserved for "future versions" */ |
1119 | tmpai->rate_source = -1; | | 1119 | tmpai->rate_source = -1; |
1120 | /* | | 1120 | /* |
1121 | * 'handle' is supposed to be globally unique. The closest | | 1121 | * 'handle' is supposed to be globally unique. The closest |
1122 | * we have to that is probably device nodes. | | 1122 | * we have to that is probably device nodes. |
1123 | */ | | 1123 | */ |
1124 | strlcpy(tmpai->handle, tmpai->devnode, | | 1124 | strlcpy(tmpai->handle, tmpai->devnode, |
1125 | sizeof(tmpai->handle)); | | 1125 | sizeof(tmpai->handle)); |
1126 | tmpai->next_play_engine = 0; | | 1126 | tmpai->next_play_engine = 0; |
1127 | tmpai->next_rec_engine = 0; | | 1127 | tmpai->next_rec_engine = 0; |
1128 | argp = tmpai; | | 1128 | argp = tmpai; |
1129 | close(newfd); | | 1129 | close(newfd); |
1130 | break; | | 1130 | break; |
1131 | case SNDCTL_CARDINFO: | | 1131 | case SNDCTL_CARDINFO: |
1132 | cardinfo = (oss_card_info *)argp; | | 1132 | cardinfo = (oss_card_info *)argp; |
1133 | if (cardinfo == NULL) | | 1133 | if (cardinfo == NULL) |
1134 | return EINVAL; | | 1134 | return EINVAL; |
1135 | if (cardinfo->card != -1) { | | 1135 | if (cardinfo->card != -1) { |
1136 | snprintf(devname, sizeof(devname), | | 1136 | snprintf(devname, sizeof(devname), |
1137 | "/dev/audio%d", cardinfo->card); | | 1137 | "/dev/audio%d", cardinfo->card); |
1138 | newfd = open(devname, O_RDONLY); | | 1138 | newfd = open(devname, O_RDONLY); |
1139 | if (newfd < 0) | | 1139 | if (newfd < 0) |
1140 | return newfd; | | 1140 | return newfd; |
1141 | } else { | | 1141 | } else { |
1142 | newfd = fd; | | 1142 | newfd = fd; |
1143 | } | | 1143 | } |
1144 | retval = ioctl(newfd, AUDIO_GETDEV, &dev); | | 1144 | retval = ioctl(newfd, AUDIO_GETDEV, &dev); |
1145 | tmperrno = errno; | | 1145 | tmperrno = errno; |
1146 | if (newfd != fd) | | 1146 | if (newfd != fd) |
1147 | close(newfd); | | 1147 | close(newfd); |
1148 | if (retval < 0) { | | 1148 | if (retval < 0) { |
1149 | errno = tmperrno; | | 1149 | errno = tmperrno; |
1150 | return retval; | | 1150 | return retval; |
1151 | } | | 1151 | } |
1152 | strlcpy(cardinfo->shortname, dev.name, | | 1152 | strlcpy(cardinfo->shortname, dev.name, |
1153 | sizeof(cardinfo->shortname)); | | 1153 | sizeof(cardinfo->shortname)); |
1154 | snprintf(cardinfo->longname, sizeof(cardinfo->longname), | | 1154 | snprintf(cardinfo->longname, sizeof(cardinfo->longname), |
1155 | "%s %s %s", dev.name, dev.version, dev.config); | | 1155 | "%s %s %s", dev.name, dev.version, dev.config); |
1156 | memset(cardinfo->hw_info, 0, sizeof(cardinfo->hw_info)); | | 1156 | memset(cardinfo->hw_info, 0, sizeof(cardinfo->hw_info)); |
1157 | /* | | 1157 | /* |
1158 | * OSSv4 does not document this ioctl, and claims it should | | 1158 | * OSSv4 does not document this ioctl, and claims it should |
1159 | * not be used by applications and is provided for "utiltiy | | 1159 | * not be used by applications and is provided for "utiltiy |
1160 | * programs included in OSS". We follow the Solaris | | 1160 | * programs included in OSS". We follow the Solaris |
1161 | * implementation (which is documented) and leave these fields | | 1161 | * implementation (which is documented) and leave these fields |
1162 | * unset. | | 1162 | * unset. |
1163 | */ | | 1163 | */ |
1164 | cardinfo->flags = 0; | | 1164 | cardinfo->flags = 0; |
1165 | cardinfo->intr_count = 0; | | 1165 | cardinfo->intr_count = 0; |
1166 | cardinfo->ack_count = 0; | | 1166 | cardinfo->ack_count = 0; |
1167 | break; | | 1167 | break; |
1168 | case SNDCTL_SYSINFO: | | 1168 | case SNDCTL_SYSINFO: |
1169 | memset(&sysinfo, 0, sizeof(sysinfo)); | | 1169 | memset(&sysinfo, 0, sizeof(sysinfo)); |
1170 | strlcpy(sysinfo.product, | | 1170 | strlcpy(sysinfo.product, |
1171 | "OSS/NetBSD", sizeof(sysinfo.product)); | | 1171 | "OSS/NetBSD", sizeof(sysinfo.product)); |
1172 | strlcpy(sysinfo.version, | | 1172 | strlcpy(sysinfo.version, |
1173 | "4.01", sizeof(sysinfo.version)); | | 1173 | "4.01", sizeof(sysinfo.version)); |
1174 | strlcpy(sysinfo.license, | | 1174 | strlcpy(sysinfo.license, |
1175 | "BSD", sizeof(sysinfo.license)); | | 1175 | "BSD", sizeof(sysinfo.license)); |
1176 | sysinfo.versionnum = SOUND_VERSION; | | 1176 | sysinfo.versionnum = SOUND_VERSION; |
1177 | sysinfo.numaudios = | | 1177 | sysinfo.numaudios = |
1178 | sysinfo.numcards = | | 1178 | sysinfo.numcards = |
1179 | getaudiocount(); | | 1179 | getaudiocount(); |
1180 | sysinfo.numaudioengines = 1; | | 1180 | sysinfo.numaudioengines = 1; |
1181 | sysinfo.numsynths = 1; | | 1181 | sysinfo.numsynths = 1; |
1182 | sysinfo.nummidis = -1; | | 1182 | sysinfo.nummidis = -1; |
1183 | sysinfo.numtimers = -1; | | 1183 | sysinfo.numtimers = -1; |
1184 | sysinfo.nummixers = getmixercount(); | | 1184 | sysinfo.nummixers = getmixercount(); |
1185 | *(struct oss_sysinfo *)argp = sysinfo; | | 1185 | *(struct oss_sysinfo *)argp = sysinfo; |
1186 | break; | | 1186 | break; |
1187 | case SNDCTL_MIXERINFO: | | 1187 | case SNDCTL_MIXERINFO: |
1188 | mi = (oss_mixerinfo *)argp; | | 1188 | mi = (oss_mixerinfo *)argp; |
1189 | if (mi == NULL) { | | 1189 | if (mi == NULL) { |
1190 | errno = EINVAL; | | 1190 | errno = EINVAL; |
1191 | return -1; | | 1191 | return -1; |