]> code.delx.au - pulseaudio/blob - src/modules/oss/oss-util.c
5a109ae98196ee374196fa04e7824e4e26d485e5
[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 = open(device, O_RDWR|O_NDELAY|O_NOCTTY)) >= 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 = open(device, (*mode = O_WRONLY)|O_NDELAY|O_NOCTTY)) < 0) {
75 if ((fd = open(device, (*mode = O_RDONLY)|O_NDELAY|O_NOCTTY)) < 0) {
76 pa_log("open('%s'): %s", device, pa_cstrerror(errno));
77 goto fail;
78 }
79 }
80 } else {
81 if ((fd = open(device, *mode|O_NDELAY|O_NOCTTY)) < 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 pa_make_fd_cloexec(fd);
149
150 return fd;
151
152 fail:
153 if (fd >= 0)
154 pa_close(fd);
155 return -1;
156 }
157
158 int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
159 int format, channels, speed, reqformat;
160 pa_sample_format_t orig_format;
161
162 static const int format_trans[PA_SAMPLE_MAX] = {
163 [PA_SAMPLE_U8] = AFMT_U8,
164 [PA_SAMPLE_ALAW] = AFMT_A_LAW,
165 [PA_SAMPLE_ULAW] = AFMT_MU_LAW,
166 [PA_SAMPLE_S16LE] = AFMT_S16_LE,
167 [PA_SAMPLE_S16BE] = AFMT_S16_BE,
168 [PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */
169 [PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
170 [PA_SAMPLE_S32LE] = AFMT_QUERY, /* not supported */
171 [PA_SAMPLE_S32BE] = AFMT_QUERY, /* not supported */
172 [PA_SAMPLE_S24LE] = AFMT_QUERY, /* not supported */
173 [PA_SAMPLE_S24BE] = AFMT_QUERY, /* not supported */
174 [PA_SAMPLE_S24_32LE] = AFMT_QUERY, /* not supported */
175 [PA_SAMPLE_S24_32BE] = AFMT_QUERY, /* not supported */
176 };
177
178 pa_assert(fd >= 0);
179 pa_assert(ss);
180
181 orig_format = ss->format;
182
183 reqformat = format = format_trans[ss->format];
184 if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) {
185 format = AFMT_S16_NE;
186 if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) {
187 int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE;
188 format = f;
189 if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) {
190 format = AFMT_U8;
191 if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) {
192 pa_log("SNDCTL_DSP_SETFMT: %s", format != AFMT_U8 ? "No supported sample format" : pa_cstrerror(errno));
193 return -1;
194 } else
195 ss->format = PA_SAMPLE_U8;
196 } else
197 ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE;
198 } else
199 ss->format = PA_SAMPLE_S16NE;
200 }
201
202 if (orig_format != ss->format)
203 pa_log_warn("device doesn't support sample format %s, changed to %s.",
204 pa_sample_format_to_string(orig_format),
205 pa_sample_format_to_string(ss->format));
206
207 channels = ss->channels;
208 if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) {
209 pa_log("SNDCTL_DSP_CHANNELS: %s", pa_cstrerror(errno));
210 return -1;
211 }
212 pa_assert(channels > 0);
213
214 if (ss->channels != channels) {
215 pa_log_warn("device doesn't support %i channels, using %i channels.", ss->channels, channels);
216 ss->channels = (uint8_t) channels;
217 }
218
219 speed = (int) ss->rate;
220 if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) {
221 pa_log("SNDCTL_DSP_SPEED: %s", pa_cstrerror(errno));
222 return -1;
223 }
224 pa_assert(speed > 0);
225
226 if (ss->rate != (unsigned) speed) {
227 pa_log_warn("device doesn't support %i Hz, changed to %i Hz.", ss->rate, speed);
228
229 /* If the sample rate deviates too much, we need to resample */
230 if (speed < ss->rate*.95 || speed > ss->rate*1.05)
231 ss->rate = (uint32_t) speed;
232 }
233
234 return 0;
235 }
236
237 static int simple_log2(int v) {
238 int k = 0;
239
240 for (;;) {
241 v >>= 1;
242 if (!v) break;
243 k++;
244 }
245
246 return k;
247 }
248
249 int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
250 int arg;
251 arg = ((int) nfrags << 16) | simple_log2(frag_size);
252
253 pa_log_debug("Asking for %i fragments of size %i (requested %i)", nfrags, 1 << simple_log2(frag_size), frag_size);
254
255 if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
256 pa_log("SNDCTL_DSP_SETFRAGMENT: %s", pa_cstrerror(errno));
257 return -1;
258 }
259
260 return 0;
261 }
262
263 int pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume) {
264 char cv[PA_CVOLUME_SNPRINT_MAX];
265 unsigned vol;
266
267 pa_assert(fd >= 0);
268 pa_assert(ss);
269 pa_assert(volume);
270
271 if (ioctl(fd, mixer, &vol) < 0)
272 return -1;
273
274 pa_cvolume_reset(volume, ss->channels);
275
276 volume->values[0] = ((vol & 0xFF) * PA_VOLUME_NORM) / 100;
277
278 if (volume->channels >= 2)
279 volume->values[1] = (((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100;
280
281 pa_log_debug("Read mixer settings: %s", pa_cvolume_snprint(cv, sizeof(cv), volume));
282 return 0;
283 }
284
285 int pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) {
286 char cv[PA_CVOLUME_SNPRINT_MAX];
287 unsigned vol;
288 pa_volume_t l, r;
289
290 l = volume->values[0] > PA_VOLUME_NORM ? PA_VOLUME_NORM : volume->values[0];
291
292 vol = (l*100)/PA_VOLUME_NORM;
293
294 if (ss->channels >= 2) {
295 r = volume->values[1] > PA_VOLUME_NORM ? PA_VOLUME_NORM : volume->values[1];
296 vol |= ((r*100)/PA_VOLUME_NORM) << 8;
297 }
298
299 if (ioctl(fd, mixer, &vol) < 0)
300 return -1;
301
302 pa_log_debug("Wrote mixer settings: %s", pa_cvolume_snprint(cv, sizeof(cv), volume));
303 return 0;
304 }
305
306 static int get_device_number(const char *dev) {
307 const char *p, *e;
308 char *rp = NULL;
309 int r;
310
311 if (!(p = rp = pa_readlink(dev))) {
312 #ifdef ENOLINK
313 if (errno != EINVAL && errno != ENOLINK) {
314 #else
315 if (errno != EINVAL) {
316 #endif
317 r = -1;
318 goto finish;
319 }
320
321 p = dev;
322 }
323
324 if ((e = strrchr(p, '/')))
325 p = e+1;
326
327 if (p == 0) {
328 r = 0;
329 goto finish;
330 }
331
332 p = strchr(p, 0) -1;
333
334 if (*p >= '0' && *p <= '9') {
335 r = *p - '0';
336 goto finish;
337 }
338
339 r = -1;
340
341 finish:
342 pa_xfree(rp);
343 return r;
344 }
345
346 int pa_oss_get_hw_description(const char *dev, char *name, size_t l) {
347 FILE *f;
348 int n, r = -1;
349 int b = 0;
350
351 if ((n = get_device_number(dev)) < 0)
352 return -1;
353
354 if (!(f = fopen("/dev/sndstat", "r")) &&
355 !(f = fopen("/proc/sndstat", "r")) &&
356 !(f = fopen("/proc/asound/oss/sndstat", "r"))) {
357
358 if (errno != ENOENT)
359 pa_log_warn("failed to open OSS sndstat device: %s", pa_cstrerror(errno));
360
361 return -1;
362 }
363
364 while (!feof(f)) {
365 char line[64];
366 int device;
367
368 if (!fgets(line, sizeof(line), f))
369 break;
370
371 line[strcspn(line, "\r\n")] = 0;
372
373 if (!b) {
374 b = strcmp(line, "Audio devices:") == 0;
375 continue;
376 }
377
378 if (line[0] == 0)
379 break;
380
381 if (sscanf(line, "%i: ", &device) != 1)
382 continue;
383
384 if (device == n) {
385 char *k = strchr(line, ':');
386 pa_assert(k);
387 k++;
388 k += strspn(k, " ");
389
390 if (pa_endswith(k, " (DUPLEX)"))
391 k[strlen(k)-9] = 0;
392
393 pa_strlcpy(name, k, l);
394 r = 0;
395 break;
396 }
397 }
398
399 fclose(f);
400 return r;
401 }
402
403 static int open_mixer(const char *mixer) {
404 int fd;
405
406 if ((fd = open(mixer, O_RDWR|O_NDELAY|O_NOCTTY)) >= 0)
407 return fd;
408
409 return -1;
410 }
411
412 int pa_oss_open_mixer_for_device(const char *device) {
413 int n;
414 char *fn;
415 int fd;
416
417 if ((n = get_device_number(device)) < 0)
418 return -1;
419
420 if (n == 0)
421 if ((fd = open_mixer("/dev/mixer")) >= 0)
422 return fd;
423
424 fn = pa_sprintf_malloc("/dev/mixer%i", n);
425 fd = open_mixer(fn);
426 pa_xfree(fn);
427
428 if (fd < 0)
429 pa_log_warn("Failed to open mixer '%s': %s", device, pa_cstrerror(errno));
430
431 return fd;
432 }