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 = ∩︀
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