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