Git init
[framework/multimedia/pulseaudio.git] / 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 }