1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published
9 by the Free Software Foundation; either version 2.1 of the License,
10 or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <sys/soundcard.h>
26 #include <sys/ioctl.h>
27 #include <stdio.h>
28 #include <errno.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <fcntl.h>
33
34 #include <pulse/xmalloc.h>
35 #include <pulsecore/core-error.h>
36 #include <pulsecore/core-util.h>
37 #include <pulsecore/log.h>
38 #include <pulsecore/macro.h>
39
40 #include "oss-util.h"
41
pa_oss_open(const char * device,int * mode,int * pcaps)42 int pa_oss_open(const char *device, int *mode, int* pcaps) {
43 int fd = -1;
44 int caps;
45 char *t;
46
47 pa_assert(device);
48 pa_assert(mode);
49 pa_assert(*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY);
50
51 if (!pcaps)
52 pcaps = ∩︀
53
54 if (*mode == O_RDWR) {
55 if ((fd = pa_open_cloexec(device, O_RDWR|O_NDELAY, 0)) >= 0) {
56 ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0);
57
58 if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
59 pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno));
60 goto fail;
61 }
62
63 if (*pcaps & DSP_CAP_DUPLEX)
64 goto success;
65
66 pa_log_warn("'%s' doesn't support full duplex", device);
67
68 pa_close(fd);
69 }
70
71 if ((fd = pa_open_cloexec(device, (*mode = O_WRONLY)|O_NDELAY, 0)) < 0) {
72 if ((fd = pa_open_cloexec(device, (*mode = O_RDONLY)|O_NDELAY, 0)) < 0) {
73 pa_log("open('%s'): %s", device, pa_cstrerror(errno));
74 goto fail;
75 }
76 }
77 } else {
78 if ((fd = pa_open_cloexec(device, *mode|O_NDELAY, 0)) < 0) {
79 pa_log("open('%s'): %s", device, pa_cstrerror(errno));
80 goto fail;
81 }
82 }
83
84 *pcaps = 0;
85
86 if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) {
87 pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno));
88 goto fail;
89 }
90
91 success:
92
93 t = pa_sprintf_malloc(
94 "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
95 *pcaps & DSP_CAP_BATCH ? " BATCH" : "",
96 #ifdef DSP_CAP_BIND
97 *pcaps & DSP_CAP_BIND ? " BIND" : "",
98 #else
99 "",
100 #endif
101 *pcaps & DSP_CAP_COPROC ? " COPROC" : "",
102 *pcaps & DSP_CAP_DUPLEX ? " DUPLEX" : "",
103 #ifdef DSP_CAP_FREERATE
104 *pcaps & DSP_CAP_FREERATE ? " FREERATE" : "",
105 #else
106 "",
107 #endif
108 #ifdef DSP_CAP_INPUT
109 *pcaps & DSP_CAP_INPUT ? " INPUT" : "",
110 #else
111 "",
112 #endif
113 *pcaps & DSP_CAP_MMAP ? " MMAP" : "",
114 #ifdef DSP_CAP_MODEM
115 *pcaps & DSP_CAP_MODEM ? " MODEM" : "",
116 #else
117 "",
118 #endif
119 #ifdef DSP_CAP_MULTI
120 *pcaps & DSP_CAP_MULTI ? " MULTI" : "",
121 #else
122 "",
123 #endif
124 #ifdef DSP_CAP_OUTPUT
125 *pcaps & DSP_CAP_OUTPUT ? " OUTPUT" : "",
126 #else
127 "",
128 #endif
129 *pcaps & DSP_CAP_REALTIME ? " REALTIME" : "",
130 #ifdef DSP_CAP_SHADOW
131 *pcaps & DSP_CAP_SHADOW ? " SHADOW" : "",
132 #else
133 "",
134 #endif
135 #ifdef DSP_CAP_VIRTUAL
136 *pcaps & DSP_CAP_VIRTUAL ? " VIRTUAL" : "",
137 #else
138 "",
139 #endif
140 *pcaps & DSP_CAP_TRIGGER ? " TRIGGER" : "");
141
142 pa_log_debug("capabilities:%s", t);
143 pa_xfree(t);
144
145 return fd;
146
147 fail:
148 if (fd >= 0)
149 pa_close(fd);
150 return -1;
151 }
152
pa_oss_auto_format(int fd,pa_sample_spec * ss)153 int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
154 int format, channels, speed, reqformat;
155 pa_sample_format_t orig_format;
156
157 static const int format_trans[PA_SAMPLE_MAX] = {
158 [PA_SAMPLE_U8] = AFMT_U8,
159 [PA_SAMPLE_ALAW] = AFMT_A_LAW,
160 [PA_SAMPLE_ULAW] = AFMT_MU_LAW,
161 [PA_SAMPLE_S16LE] = AFMT_S16_LE,
162 [PA_SAMPLE_S16BE] = AFMT_S16_BE,
163 [PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */
164 [PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
165 [PA_SAMPLE_S32LE] = AFMT_QUERY, /* not supported */
166 [PA_SAMPLE_S32BE] = AFMT_QUERY, /* not supported */
167 [PA_SAMPLE_S24LE] = AFMT_QUERY, /* not supported */
168 [PA_SAMPLE_S24BE] = AFMT_QUERY, /* not supported */
169 [PA_SAMPLE_S24_32LE] = AFMT_QUERY, /* not supported */
170 [PA_SAMPLE_S24_32BE] = AFMT_QUERY, /* not supported */
171 };
172
173 pa_assert(fd >= 0);
174 pa_assert(ss);
175
176 orig_format = ss->format;
177
178 reqformat = format = format_trans[ss->format];
179 if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) {
180 format = AFMT_S16_NE;
181 if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) {
182 int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE;
183 format = f;
184 if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) {
185 format = AFMT_U8;
186 if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) {
187 pa_log("SNDCTL_DSP_SETFMT: %s", format != AFMT_U8 ? "No supported sample format" : pa_cstrerror(errno));
188 return -1;
189 } else
190 ss->format = PA_SAMPLE_U8;
191 } else
192 ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE;
193 } else
194 ss->format = PA_SAMPLE_S16NE;
195 }
196
197 if (orig_format != ss->format)
198 pa_log_warn("device doesn't support sample format %s, changed to %s.",
199 pa_sample_format_to_string(orig_format),
200 pa_sample_format_to_string(ss->format));
201
202 channels = ss->channels;
203 if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
204 pa_log("SNDCTL_DSP_CHANNELS: %s", pa_cstrerror(errno));
205 return -1;
206 }
207 pa_assert(channels > 0);
208
209 if (ss->channels != channels) {
210 pa_log_warn("device doesn't support %i channels, using %i channels.", ss->channels, channels);
211 ss->channels = (uint8_t) channels;
212 }
213
214 speed = (int) ss->rate;
215 if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {
216 pa_log("SNDCTL_DSP_SPEED: %s", pa_cstrerror(errno));
217 return -1;
218 }
219 pa_assert(speed > 0);
220
221 if (ss->rate != (unsigned) speed) {
222 pa_log_warn("device doesn't support %i Hz, changed to %i Hz.", ss->rate, speed);
223
224 /* If the sample rate deviates too much, we need to resample */
225 if (speed < ss->rate*.95 || speed > ss->rate*1.05)
226 ss->rate = (uint32_t) speed;
227 }
228
229 return 0;
230 }
231
pa_oss_set_fragments(int fd,int nfrags,int frag_size)232 int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
233 int arg;
234
235 pa_assert(frag_size >= 0);
236
237 arg = ((int) nfrags << 16) | pa_ulog2(frag_size);
238
239 pa_log_debug("Asking for %i fragments of size %i (requested %i)", nfrags, 1 << pa_ulog2(frag_size), frag_size);
240
241 if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
242 pa_log("SNDCTL_DSP_SETFRAGMENT: %s", pa_cstrerror(errno));
243 return -1;
244 }
245
246 return 0;
247 }
248
pa_oss_get_volume(int fd,unsigned long mixer,const pa_sample_spec * ss,pa_cvolume * volume)249 int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
250 char cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
251 unsigned vol;
252
253 pa_assert(fd >= 0);
254 pa_assert(ss);
255 pa_assert(volume);
256
257 if (ioctl(fd, mixer, &vol) < 0)
258 return -1;
259
260 pa_cvolume_reset(volume, ss->channels);
261
262 volume->values[0] = PA_CLAMP_VOLUME(((vol & 0xFF) * PA_VOLUME_NORM) / 100);
263
264 if (volume->channels >= 2)
265 volume->values[1] = PA_CLAMP_VOLUME((((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100);
266
267 pa_log_debug("Read mixer settings: %s", pa_cvolume_snprint_verbose(cv, sizeof(cv), volume, NULL, false));
268 return 0;
269 }
270
pa_oss_set_volume(int fd,unsigned long mixer,const pa_sample_spec * ss,const pa_cvolume * volume)271 int pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
272 char cv[PA_CVOLUME_SNPRINT_MAX];
273 unsigned vol;
274 pa_volume_t l, r;
275
276 l = volume->values[0] > PA_VOLUME_NORM ? PA_VOLUME_NORM : volume->values[0];
277
278 vol = (l*100)/PA_VOLUME_NORM;
279
280 if (ss->channels >= 2) {
281 r = volume->values[1] > PA_VOLUME_NORM ? PA_VOLUME_NORM : volume->values[1];
282 vol |= ((r*100)/PA_VOLUME_NORM) << 8;
283 }
284
285 if (ioctl(fd, mixer, &vol) < 0)
286 return -1;
287
288 pa_log_debug("Wrote mixer settings: %s", pa_cvolume_snprint(cv, sizeof(cv), volume));
289 return 0;
290 }
291
get_device_number(const char * dev)292 static int get_device_number(const char *dev) {
293 const char *p, *e;
294 char *rp = NULL;
295 int r;
296
297 if (!(p = rp = pa_readlink(dev))) {
298 #ifdef ENOLINK
299 if (errno != EINVAL && errno != ENOLINK) {
300 #else
301 if (errno != EINVAL) {
302 #endif
303 r = -1;
304 goto finish;
305 }
306
307 p = dev;
308 }
309
310 if ((e = strrchr(p, '/')))
311 p = e+1;
312
313 if (p == 0) {
314 r = 0;
315 goto finish;
316 }
317
318 p = strchr(p, 0) -1;
319
320 if (*p >= '0' && *p <= '9') {
321 r = *p - '0';
322 goto finish;
323 }
324
325 r = -1;
326
327 finish:
328 pa_xfree(rp);
329 return r;
330 }
331
332 int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
333 FILE *f;
334 int n, r = -1;
335 int b = 0;
336
337 if ((n = get_device_number(dev)) < 0)
338 return -1;
339
340 if (!(f = pa_fopen_cloexec("/dev/sndstat", "r")) &&
341 !(f = pa_fopen_cloexec("/proc/sndstat", "r")) &&
342 !(f = pa_fopen_cloexec("/proc/asound/oss/sndstat", "r"))) {
343
344 if (errno != ENOENT)
345 pa_log_warn("failed to open OSS sndstat device: %s", pa_cstrerror(errno));
346
347 return -1;
348 }
349
350 while (!feof(f)) {
351 char line[64];
352 int device;
353
354 if (!fgets(line, sizeof(line), f))
355 break;
356
357 line[strcspn(line, "\r\n")] = 0;
358
359 if (!b) {
360 b = pa_streq(line, "Audio devices:");
361 continue;
362 }
363
364 if (line[0] == 0)
365 break;
366
367 if (sscanf(line, "%i: ", &device) != 1)
368 continue;
369
370 if (device == n) {
371 char *k = strchr(line, ':');
372 pa_assert(k);
373 k++;
374 k += strspn(k, " ");
375
376 if (pa_endswith(k, " (DUPLEX)"))
377 k[strlen(k)-9] = 0;
378
379 pa_strlcpy(name, k, l);
380 r = 0;
381 break;
382 }
383 }
384
385 fclose(f);
386 return r;
387 }
388
389 static int open_mixer(const char *mixer) {
390 int fd;
391
392 if ((fd = pa_open_cloexec(mixer, O_RDWR|O_NDELAY, 0)) >= 0)
393 return fd;
394
395 return -1;
396 }
397
398 int pa_oss_open_mixer_for_device(const char *device) {
399 int n;
400 char *fn;
401 int fd;
402
403 if ((n = get_device_number(device)) < 0)
404 return -1;
405
406 if (n == 0)
407 if ((fd = open_mixer("/dev/mixer")) >= 0)
408 return fd;
409
410 fn = pa_sprintf_malloc("/dev/mixer%i", n);
411 fd = open_mixer(fn);
412 pa_xfree(fn);
413
414 if (fd < 0)
415 pa_log_warn("Failed to open mixer '%s': %s", device, pa_cstrerror(errno));
416
417 return fd;
418 }
419