• 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     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 = &caps;
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