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