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 42int 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 92success: 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 152fail: 153 if (fd >= 0) 154 pa_close(fd); 155 return -1; 156} 157 158int 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 242int 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 259int 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 281int 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 302static 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 336int 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 396static 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 405int 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