153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2004-2006 Lennart Poettering 553a5a1b3Sopenharmony_ci Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB 653a5a1b3Sopenharmony_ci 753a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 853a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 953a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 1053a5a1b3Sopenharmony_ci or (at your option) any later version. 1153a5a1b3Sopenharmony_ci 1253a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1353a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1453a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1553a5a1b3Sopenharmony_ci General Public License for more details. 1653a5a1b3Sopenharmony_ci 1753a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1853a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1953a5a1b3Sopenharmony_ci***/ 2053a5a1b3Sopenharmony_ci 2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2253a5a1b3Sopenharmony_ci#include <config.h> 2353a5a1b3Sopenharmony_ci#endif 2453a5a1b3Sopenharmony_ci 2553a5a1b3Sopenharmony_ci#include <sys/soundcard.h> 2653a5a1b3Sopenharmony_ci#include <sys/ioctl.h> 2753a5a1b3Sopenharmony_ci#include <stdio.h> 2853a5a1b3Sopenharmony_ci#include <errno.h> 2953a5a1b3Sopenharmony_ci#include <string.h> 3053a5a1b3Sopenharmony_ci#include <unistd.h> 3153a5a1b3Sopenharmony_ci#include <sys/types.h> 3253a5a1b3Sopenharmony_ci#include <fcntl.h> 3353a5a1b3Sopenharmony_ci 3453a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3553a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h> 3653a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 3753a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 3853a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 3953a5a1b3Sopenharmony_ci 4053a5a1b3Sopenharmony_ci#include "oss-util.h" 4153a5a1b3Sopenharmony_ci 4253a5a1b3Sopenharmony_ciint pa_oss_open(const char *device, int *mode, int* pcaps) { 4353a5a1b3Sopenharmony_ci static const int nonblock_io = 1; 4453a5a1b3Sopenharmony_ci int fd = -1; 4553a5a1b3Sopenharmony_ci int caps; 4653a5a1b3Sopenharmony_ci char *t; 4753a5a1b3Sopenharmony_ci 4853a5a1b3Sopenharmony_ci pa_assert(device); 4953a5a1b3Sopenharmony_ci pa_assert(mode); 5053a5a1b3Sopenharmony_ci pa_assert(*mode == O_RDWR || *mode == O_RDONLY || *mode == O_WRONLY); 5153a5a1b3Sopenharmony_ci 5253a5a1b3Sopenharmony_ci if (!pcaps) 5353a5a1b3Sopenharmony_ci pcaps = ∩︀ 5453a5a1b3Sopenharmony_ci 5553a5a1b3Sopenharmony_ci if (*mode == O_RDWR) { 5653a5a1b3Sopenharmony_ci if ((fd = pa_open_cloexec(device, O_RDWR|O_NDELAY, 0)) >= 0) { 5753a5a1b3Sopenharmony_ci ioctl(fd, SNDCTL_DSP_SETDUPLEX, 0); 5853a5a1b3Sopenharmony_ci 5953a5a1b3Sopenharmony_ci if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) { 6053a5a1b3Sopenharmony_ci pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno)); 6153a5a1b3Sopenharmony_ci goto fail; 6253a5a1b3Sopenharmony_ci } 6353a5a1b3Sopenharmony_ci 6453a5a1b3Sopenharmony_ci if (*pcaps & DSP_CAP_DUPLEX) 6553a5a1b3Sopenharmony_ci goto success; 6653a5a1b3Sopenharmony_ci 6753a5a1b3Sopenharmony_ci pa_log_warn("'%s' doesn't support full duplex", device); 6853a5a1b3Sopenharmony_ci 6953a5a1b3Sopenharmony_ci pa_close(fd); 7053a5a1b3Sopenharmony_ci } 7153a5a1b3Sopenharmony_ci 7253a5a1b3Sopenharmony_ci if ((fd = pa_open_cloexec(device, (*mode = O_WRONLY)|O_NDELAY, 0)) < 0) { 7353a5a1b3Sopenharmony_ci if ((fd = pa_open_cloexec(device, (*mode = O_RDONLY)|O_NDELAY, 0)) < 0) { 7453a5a1b3Sopenharmony_ci pa_log("open('%s'): %s", device, pa_cstrerror(errno)); 7553a5a1b3Sopenharmony_ci goto fail; 7653a5a1b3Sopenharmony_ci } 7753a5a1b3Sopenharmony_ci } 7853a5a1b3Sopenharmony_ci } else { 7953a5a1b3Sopenharmony_ci if ((fd = pa_open_cloexec(device, *mode|O_NDELAY, 0)) < 0) { 8053a5a1b3Sopenharmony_ci pa_log("open('%s'): %s", device, pa_cstrerror(errno)); 8153a5a1b3Sopenharmony_ci goto fail; 8253a5a1b3Sopenharmony_ci } 8353a5a1b3Sopenharmony_ci } 8453a5a1b3Sopenharmony_ci 8553a5a1b3Sopenharmony_ci *pcaps = 0; 8653a5a1b3Sopenharmony_ci 8753a5a1b3Sopenharmony_ci if (ioctl(fd, SNDCTL_DSP_GETCAPS, pcaps) < 0) { 8853a5a1b3Sopenharmony_ci pa_log("SNDCTL_DSP_GETCAPS: %s", pa_cstrerror(errno)); 8953a5a1b3Sopenharmony_ci goto fail; 9053a5a1b3Sopenharmony_ci } 9153a5a1b3Sopenharmony_ci 9253a5a1b3Sopenharmony_cisuccess: 9353a5a1b3Sopenharmony_ci if (ioctl(fd, FIONBIO, &nonblock_io) < 0) { 9453a5a1b3Sopenharmony_ci pa_log("FIONBIO: %s", pa_cstrerror(errno)); 9553a5a1b3Sopenharmony_ci goto fail; 9653a5a1b3Sopenharmony_ci } 9753a5a1b3Sopenharmony_ci 9853a5a1b3Sopenharmony_ci t = pa_sprintf_malloc( 9953a5a1b3Sopenharmony_ci "%s%s%s%s%s%s%s%s%s%s%s%s%s%s", 10053a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_BATCH ? " BATCH" : "", 10153a5a1b3Sopenharmony_ci#ifdef DSP_CAP_BIND 10253a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_BIND ? " BIND" : "", 10353a5a1b3Sopenharmony_ci#else 10453a5a1b3Sopenharmony_ci "", 10553a5a1b3Sopenharmony_ci#endif 10653a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_COPROC ? " COPROC" : "", 10753a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_DUPLEX ? " DUPLEX" : "", 10853a5a1b3Sopenharmony_ci#ifdef DSP_CAP_FREERATE 10953a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_FREERATE ? " FREERATE" : "", 11053a5a1b3Sopenharmony_ci#else 11153a5a1b3Sopenharmony_ci "", 11253a5a1b3Sopenharmony_ci#endif 11353a5a1b3Sopenharmony_ci#ifdef DSP_CAP_INPUT 11453a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_INPUT ? " INPUT" : "", 11553a5a1b3Sopenharmony_ci#else 11653a5a1b3Sopenharmony_ci "", 11753a5a1b3Sopenharmony_ci#endif 11853a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_MMAP ? " MMAP" : "", 11953a5a1b3Sopenharmony_ci#ifdef DSP_CAP_MODEM 12053a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_MODEM ? " MODEM" : "", 12153a5a1b3Sopenharmony_ci#else 12253a5a1b3Sopenharmony_ci "", 12353a5a1b3Sopenharmony_ci#endif 12453a5a1b3Sopenharmony_ci#ifdef DSP_CAP_MULTI 12553a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_MULTI ? " MULTI" : "", 12653a5a1b3Sopenharmony_ci#else 12753a5a1b3Sopenharmony_ci "", 12853a5a1b3Sopenharmony_ci#endif 12953a5a1b3Sopenharmony_ci#ifdef DSP_CAP_OUTPUT 13053a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_OUTPUT ? " OUTPUT" : "", 13153a5a1b3Sopenharmony_ci#else 13253a5a1b3Sopenharmony_ci "", 13353a5a1b3Sopenharmony_ci#endif 13453a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_REALTIME ? " REALTIME" : "", 13553a5a1b3Sopenharmony_ci#ifdef DSP_CAP_SHADOW 13653a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_SHADOW ? " SHADOW" : "", 13753a5a1b3Sopenharmony_ci#else 13853a5a1b3Sopenharmony_ci "", 13953a5a1b3Sopenharmony_ci#endif 14053a5a1b3Sopenharmony_ci#ifdef DSP_CAP_VIRTUAL 14153a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_VIRTUAL ? " VIRTUAL" : "", 14253a5a1b3Sopenharmony_ci#else 14353a5a1b3Sopenharmony_ci "", 14453a5a1b3Sopenharmony_ci#endif 14553a5a1b3Sopenharmony_ci *pcaps & DSP_CAP_TRIGGER ? " TRIGGER" : ""); 14653a5a1b3Sopenharmony_ci 14753a5a1b3Sopenharmony_ci pa_log_debug("capabilities:%s", t); 14853a5a1b3Sopenharmony_ci pa_xfree(t); 14953a5a1b3Sopenharmony_ci 15053a5a1b3Sopenharmony_ci return fd; 15153a5a1b3Sopenharmony_ci 15253a5a1b3Sopenharmony_cifail: 15353a5a1b3Sopenharmony_ci if (fd >= 0) 15453a5a1b3Sopenharmony_ci pa_close(fd); 15553a5a1b3Sopenharmony_ci return -1; 15653a5a1b3Sopenharmony_ci} 15753a5a1b3Sopenharmony_ci 15853a5a1b3Sopenharmony_ciint pa_oss_auto_format(int fd, pa_sample_spec *ss) { 15953a5a1b3Sopenharmony_ci int format, channels, speed, reqformat; 16053a5a1b3Sopenharmony_ci pa_sample_format_t orig_format; 16153a5a1b3Sopenharmony_ci 16253a5a1b3Sopenharmony_ci static const int format_trans[PA_SAMPLE_MAX] = { 16353a5a1b3Sopenharmony_ci [PA_SAMPLE_U8] = AFMT_U8, 16453a5a1b3Sopenharmony_ci [PA_SAMPLE_ALAW] = AFMT_A_LAW, 16553a5a1b3Sopenharmony_ci [PA_SAMPLE_ULAW] = AFMT_MU_LAW, 16653a5a1b3Sopenharmony_ci [PA_SAMPLE_S16LE] = AFMT_S16_LE, 16753a5a1b3Sopenharmony_ci [PA_SAMPLE_S16BE] = AFMT_S16_BE, 16853a5a1b3Sopenharmony_ci [PA_SAMPLE_FLOAT32LE] = AFMT_QUERY, /* not supported */ 16953a5a1b3Sopenharmony_ci [PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */ 17053a5a1b3Sopenharmony_ci [PA_SAMPLE_S32LE] = AFMT_QUERY, /* not supported */ 17153a5a1b3Sopenharmony_ci [PA_SAMPLE_S32BE] = AFMT_QUERY, /* not supported */ 17253a5a1b3Sopenharmony_ci#if defined(AFMT_S24_LE) && defined(AFMT_S24_BE) 17353a5a1b3Sopenharmony_ci [PA_SAMPLE_S24LE] = AFMT_S24_LE, 17453a5a1b3Sopenharmony_ci [PA_SAMPLE_S24BE] = AFMT_S24_BE, 17553a5a1b3Sopenharmony_ci#else 17653a5a1b3Sopenharmony_ci [PA_SAMPLE_S24LE] = AFMT_QUERY, /* not supported */ 17753a5a1b3Sopenharmony_ci [PA_SAMPLE_S24BE] = AFMT_QUERY, /* not supported */ 17853a5a1b3Sopenharmony_ci#endif 17953a5a1b3Sopenharmony_ci [PA_SAMPLE_S24_32LE] = AFMT_QUERY, /* not supported */ 18053a5a1b3Sopenharmony_ci [PA_SAMPLE_S24_32BE] = AFMT_QUERY, /* not supported */ 18153a5a1b3Sopenharmony_ci }; 18253a5a1b3Sopenharmony_ci 18353a5a1b3Sopenharmony_ci pa_assert(fd >= 0); 18453a5a1b3Sopenharmony_ci pa_assert(ss); 18553a5a1b3Sopenharmony_ci 18653a5a1b3Sopenharmony_ci orig_format = ss->format; 18753a5a1b3Sopenharmony_ci 18853a5a1b3Sopenharmony_ci reqformat = format = format_trans[ss->format]; 18953a5a1b3Sopenharmony_ci if (reqformat == AFMT_QUERY || ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != reqformat) { 19053a5a1b3Sopenharmony_ci format = AFMT_S16_NE; 19153a5a1b3Sopenharmony_ci if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_S16_NE) { 19253a5a1b3Sopenharmony_ci int f = AFMT_S16_NE == AFMT_S16_LE ? AFMT_S16_BE : AFMT_S16_LE; 19353a5a1b3Sopenharmony_ci format = f; 19453a5a1b3Sopenharmony_ci if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != f) { 19553a5a1b3Sopenharmony_ci format = AFMT_U8; 19653a5a1b3Sopenharmony_ci if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) < 0 || format != AFMT_U8) { 19753a5a1b3Sopenharmony_ci pa_log("SNDCTL_DSP_SETFMT: %s", format != AFMT_U8 ? "No supported sample format" : pa_cstrerror(errno)); 19853a5a1b3Sopenharmony_ci return -1; 19953a5a1b3Sopenharmony_ci } else 20053a5a1b3Sopenharmony_ci ss->format = PA_SAMPLE_U8; 20153a5a1b3Sopenharmony_ci } else 20253a5a1b3Sopenharmony_ci ss->format = f == AFMT_S16_LE ? PA_SAMPLE_S16LE : PA_SAMPLE_S16BE; 20353a5a1b3Sopenharmony_ci } else 20453a5a1b3Sopenharmony_ci ss->format = PA_SAMPLE_S16NE; 20553a5a1b3Sopenharmony_ci } 20653a5a1b3Sopenharmony_ci 20753a5a1b3Sopenharmony_ci if (orig_format != ss->format) 20853a5a1b3Sopenharmony_ci pa_log_warn("device doesn't support sample format %s, changed to %s.", 20953a5a1b3Sopenharmony_ci pa_sample_format_to_string(orig_format), 21053a5a1b3Sopenharmony_ci pa_sample_format_to_string(ss->format)); 21153a5a1b3Sopenharmony_ci 21253a5a1b3Sopenharmony_ci channels = ss->channels; 21353a5a1b3Sopenharmony_ci if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) < 0) { 21453a5a1b3Sopenharmony_ci pa_log("SNDCTL_DSP_CHANNELS: %s", pa_cstrerror(errno)); 21553a5a1b3Sopenharmony_ci return -1; 21653a5a1b3Sopenharmony_ci } 21753a5a1b3Sopenharmony_ci pa_assert(channels > 0); 21853a5a1b3Sopenharmony_ci 21953a5a1b3Sopenharmony_ci if (ss->channels != channels) { 22053a5a1b3Sopenharmony_ci pa_log_warn("device doesn't support %i channels, using %i channels.", ss->channels, channels); 22153a5a1b3Sopenharmony_ci ss->channels = (uint8_t) channels; 22253a5a1b3Sopenharmony_ci } 22353a5a1b3Sopenharmony_ci 22453a5a1b3Sopenharmony_ci speed = (int) ss->rate; 22553a5a1b3Sopenharmony_ci if (ioctl(fd, SNDCTL_DSP_SPEED, &speed) < 0) { 22653a5a1b3Sopenharmony_ci pa_log("SNDCTL_DSP_SPEED: %s", pa_cstrerror(errno)); 22753a5a1b3Sopenharmony_ci return -1; 22853a5a1b3Sopenharmony_ci } 22953a5a1b3Sopenharmony_ci pa_assert(speed > 0); 23053a5a1b3Sopenharmony_ci 23153a5a1b3Sopenharmony_ci if (ss->rate != (unsigned) speed) { 23253a5a1b3Sopenharmony_ci pa_log_warn("device doesn't support %i Hz, changed to %i Hz.", ss->rate, speed); 23353a5a1b3Sopenharmony_ci 23453a5a1b3Sopenharmony_ci /* If the sample rate deviates too much, we need to resample */ 23553a5a1b3Sopenharmony_ci if (speed < ss->rate*.95 || speed > ss->rate*1.05) 23653a5a1b3Sopenharmony_ci ss->rate = (uint32_t) speed; 23753a5a1b3Sopenharmony_ci } 23853a5a1b3Sopenharmony_ci 23953a5a1b3Sopenharmony_ci return 0; 24053a5a1b3Sopenharmony_ci} 24153a5a1b3Sopenharmony_ci 24253a5a1b3Sopenharmony_ciint pa_oss_set_fragments(int fd, int nfrags, int frag_size) { 24353a5a1b3Sopenharmony_ci int arg; 24453a5a1b3Sopenharmony_ci 24553a5a1b3Sopenharmony_ci pa_assert(frag_size >= 0); 24653a5a1b3Sopenharmony_ci 24753a5a1b3Sopenharmony_ci arg = ((int) nfrags << 16) | pa_ulog2(frag_size); 24853a5a1b3Sopenharmony_ci 24953a5a1b3Sopenharmony_ci pa_log_debug("Asking for %i fragments of size %i (requested %i)", nfrags, 1 << pa_ulog2(frag_size), frag_size); 25053a5a1b3Sopenharmony_ci 25153a5a1b3Sopenharmony_ci if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) { 25253a5a1b3Sopenharmony_ci pa_log("SNDCTL_DSP_SETFRAGMENT: %s", pa_cstrerror(errno)); 25353a5a1b3Sopenharmony_ci return -1; 25453a5a1b3Sopenharmony_ci } 25553a5a1b3Sopenharmony_ci 25653a5a1b3Sopenharmony_ci return 0; 25753a5a1b3Sopenharmony_ci} 25853a5a1b3Sopenharmony_ci 25953a5a1b3Sopenharmony_ciint pa_oss_get_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, pa_cvolume *volume) { 26053a5a1b3Sopenharmony_ci char cv[PA_CVOLUME_SNPRINT_VERBOSE_MAX]; 26153a5a1b3Sopenharmony_ci unsigned vol; 26253a5a1b3Sopenharmony_ci 26353a5a1b3Sopenharmony_ci pa_assert(fd >= 0); 26453a5a1b3Sopenharmony_ci pa_assert(ss); 26553a5a1b3Sopenharmony_ci pa_assert(volume); 26653a5a1b3Sopenharmony_ci 26753a5a1b3Sopenharmony_ci if (ioctl(fd, mixer, &vol) < 0) 26853a5a1b3Sopenharmony_ci return -1; 26953a5a1b3Sopenharmony_ci 27053a5a1b3Sopenharmony_ci pa_cvolume_reset(volume, ss->channels); 27153a5a1b3Sopenharmony_ci 27253a5a1b3Sopenharmony_ci volume->values[0] = PA_CLAMP_VOLUME(((vol & 0xFF) * PA_VOLUME_NORM) / 100); 27353a5a1b3Sopenharmony_ci 27453a5a1b3Sopenharmony_ci if (volume->channels >= 2) 27553a5a1b3Sopenharmony_ci volume->values[1] = PA_CLAMP_VOLUME((((vol >> 8) & 0xFF) * PA_VOLUME_NORM) / 100); 27653a5a1b3Sopenharmony_ci 27753a5a1b3Sopenharmony_ci pa_log_debug("Read mixer settings: %s", pa_cvolume_snprint_verbose(cv, sizeof(cv), volume, NULL, false)); 27853a5a1b3Sopenharmony_ci return 0; 27953a5a1b3Sopenharmony_ci} 28053a5a1b3Sopenharmony_ci 28153a5a1b3Sopenharmony_ciint pa_oss_set_volume(int fd, unsigned long mixer, const pa_sample_spec *ss, const pa_cvolume *volume) { 28253a5a1b3Sopenharmony_ci char cv[PA_CVOLUME_SNPRINT_MAX]; 28353a5a1b3Sopenharmony_ci unsigned vol; 28453a5a1b3Sopenharmony_ci pa_volume_t l, r; 28553a5a1b3Sopenharmony_ci 28653a5a1b3Sopenharmony_ci l = volume->values[0] > PA_VOLUME_NORM ? PA_VOLUME_NORM : volume->values[0]; 28753a5a1b3Sopenharmony_ci 28853a5a1b3Sopenharmony_ci vol = (l*100)/PA_VOLUME_NORM; 28953a5a1b3Sopenharmony_ci 29053a5a1b3Sopenharmony_ci if (ss->channels >= 2) { 29153a5a1b3Sopenharmony_ci r = volume->values[1] > PA_VOLUME_NORM ? PA_VOLUME_NORM : volume->values[1]; 29253a5a1b3Sopenharmony_ci vol |= ((r*100)/PA_VOLUME_NORM) << 8; 29353a5a1b3Sopenharmony_ci } 29453a5a1b3Sopenharmony_ci 29553a5a1b3Sopenharmony_ci if (ioctl(fd, mixer, &vol) < 0) 29653a5a1b3Sopenharmony_ci return -1; 29753a5a1b3Sopenharmony_ci 29853a5a1b3Sopenharmony_ci pa_log_debug("Wrote mixer settings: %s", pa_cvolume_snprint(cv, sizeof(cv), volume)); 29953a5a1b3Sopenharmony_ci return 0; 30053a5a1b3Sopenharmony_ci} 30153a5a1b3Sopenharmony_ci 30253a5a1b3Sopenharmony_cistatic int get_device_number(const char *dev) { 30353a5a1b3Sopenharmony_ci const char *p; 30453a5a1b3Sopenharmony_ci const char *e; 30553a5a1b3Sopenharmony_ci char *rp = NULL; 30653a5a1b3Sopenharmony_ci int r = -1; 30753a5a1b3Sopenharmony_ci 30853a5a1b3Sopenharmony_ci if (!(p = rp = pa_readlink(dev))) { 30953a5a1b3Sopenharmony_ci if (errno != EINVAL && errno != ENOLINK) 31053a5a1b3Sopenharmony_ci return -2; 31153a5a1b3Sopenharmony_ci p = dev; 31253a5a1b3Sopenharmony_ci } 31353a5a1b3Sopenharmony_ci 31453a5a1b3Sopenharmony_ci /* find the last forward slash */ 31553a5a1b3Sopenharmony_ci while ((e = strrchr(p, '/'))) 31653a5a1b3Sopenharmony_ci p = e + 1; 31753a5a1b3Sopenharmony_ci 31853a5a1b3Sopenharmony_ci /* collect unit number at end, if any */ 31953a5a1b3Sopenharmony_ci while (*p) { 32053a5a1b3Sopenharmony_ci if (*p >= '0' && *p <= '9') { 32153a5a1b3Sopenharmony_ci if (r < 0) 32253a5a1b3Sopenharmony_ci r = 0; 32353a5a1b3Sopenharmony_ci else 32453a5a1b3Sopenharmony_ci r *= 10; 32553a5a1b3Sopenharmony_ci r += *p - '0'; 32653a5a1b3Sopenharmony_ci } else { 32753a5a1b3Sopenharmony_ci r = -1; 32853a5a1b3Sopenharmony_ci } 32953a5a1b3Sopenharmony_ci p++; 33053a5a1b3Sopenharmony_ci } 33153a5a1b3Sopenharmony_ci 33253a5a1b3Sopenharmony_ci pa_xfree(rp); 33353a5a1b3Sopenharmony_ci return r; 33453a5a1b3Sopenharmony_ci} 33553a5a1b3Sopenharmony_ci 33653a5a1b3Sopenharmony_ciint pa_oss_get_hw_description(const char *dev, char *name, size_t l) { 33753a5a1b3Sopenharmony_ci FILE *f; 33853a5a1b3Sopenharmony_ci int n, r = -1; 33953a5a1b3Sopenharmony_ci int b = 0; 34053a5a1b3Sopenharmony_ci 34153a5a1b3Sopenharmony_ci if ((n = get_device_number(dev)) == -2) 34253a5a1b3Sopenharmony_ci return -1; 34353a5a1b3Sopenharmony_ci 34453a5a1b3Sopenharmony_ci if (!(f = pa_fopen_cloexec("/dev/sndstat", "r")) && 34553a5a1b3Sopenharmony_ci !(f = pa_fopen_cloexec("/proc/sndstat", "r")) && 34653a5a1b3Sopenharmony_ci !(f = pa_fopen_cloexec("/proc/asound/oss/sndstat", "r"))) { 34753a5a1b3Sopenharmony_ci 34853a5a1b3Sopenharmony_ci if (errno != ENOENT) 34953a5a1b3Sopenharmony_ci pa_log_warn("failed to open OSS sndstat device: %s", pa_cstrerror(errno)); 35053a5a1b3Sopenharmony_ci 35153a5a1b3Sopenharmony_ci return -1; 35253a5a1b3Sopenharmony_ci } 35353a5a1b3Sopenharmony_ci 35453a5a1b3Sopenharmony_ci while (!feof(f)) { 35553a5a1b3Sopenharmony_ci char line[1024] = { 0 }; 35653a5a1b3Sopenharmony_ci unsigned device; 35753a5a1b3Sopenharmony_ci 35853a5a1b3Sopenharmony_ci if (!fgets(line, sizeof(line), f)) 35953a5a1b3Sopenharmony_ci break; 36053a5a1b3Sopenharmony_ci 36153a5a1b3Sopenharmony_ci line[strcspn(line, "\r\n")] = 0; 36253a5a1b3Sopenharmony_ci 36353a5a1b3Sopenharmony_ci if (!b) { 36453a5a1b3Sopenharmony_ci b = pa_streq(line, "Audio devices:") || pa_streq(line, "Installed devices:"); 36553a5a1b3Sopenharmony_ci continue; 36653a5a1b3Sopenharmony_ci } 36753a5a1b3Sopenharmony_ci 36853a5a1b3Sopenharmony_ci if (line[0] == 0) 36953a5a1b3Sopenharmony_ci break; 37053a5a1b3Sopenharmony_ci 37153a5a1b3Sopenharmony_ci if (sscanf(line, "%u: ", &device) != 1 && sscanf(line, "pcm%u: ", &device) != 1) 37253a5a1b3Sopenharmony_ci continue; 37353a5a1b3Sopenharmony_ci 37453a5a1b3Sopenharmony_ci if (device == n) { 37553a5a1b3Sopenharmony_ci char *k = strchr(line, ':'); 37653a5a1b3Sopenharmony_ci pa_assert(k); 37753a5a1b3Sopenharmony_ci k++; 37853a5a1b3Sopenharmony_ci k += strspn(k, " <"); 37953a5a1b3Sopenharmony_ci 38053a5a1b3Sopenharmony_ci if (pa_endswith(k, " (DUPLEX)")) 38153a5a1b3Sopenharmony_ci k[strlen(k)-9] = 0; 38253a5a1b3Sopenharmony_ci 38353a5a1b3Sopenharmony_ci k[strcspn(k, ">")] = 0; 38453a5a1b3Sopenharmony_ci 38553a5a1b3Sopenharmony_ci // Include the number to disambiguate devices with the same name 38653a5a1b3Sopenharmony_ci pa_snprintf(name, l, "%u - %s", device, k); 38753a5a1b3Sopenharmony_ci r = 0; 38853a5a1b3Sopenharmony_ci break; 38953a5a1b3Sopenharmony_ci } 39053a5a1b3Sopenharmony_ci } 39153a5a1b3Sopenharmony_ci 39253a5a1b3Sopenharmony_ci fclose(f); 39353a5a1b3Sopenharmony_ci return r; 39453a5a1b3Sopenharmony_ci} 39553a5a1b3Sopenharmony_ci 39653a5a1b3Sopenharmony_cistatic int open_mixer(const char *mixer) { 39753a5a1b3Sopenharmony_ci int fd; 39853a5a1b3Sopenharmony_ci 39953a5a1b3Sopenharmony_ci if ((fd = pa_open_cloexec(mixer, O_RDWR|O_NDELAY, 0)) >= 0) 40053a5a1b3Sopenharmony_ci return fd; 40153a5a1b3Sopenharmony_ci 40253a5a1b3Sopenharmony_ci return -1; 40353a5a1b3Sopenharmony_ci} 40453a5a1b3Sopenharmony_ci 40553a5a1b3Sopenharmony_ciint pa_oss_open_mixer_for_device(const char *device) { 40653a5a1b3Sopenharmony_ci int n; 40753a5a1b3Sopenharmony_ci char *fn; 40853a5a1b3Sopenharmony_ci int fd; 40953a5a1b3Sopenharmony_ci 41053a5a1b3Sopenharmony_ci if ((n = get_device_number(device)) == -2) 41153a5a1b3Sopenharmony_ci return -1; 41253a5a1b3Sopenharmony_ci 41353a5a1b3Sopenharmony_ci if (n == -1) 41453a5a1b3Sopenharmony_ci if ((fd = open_mixer("/dev/mixer")) >= 0) 41553a5a1b3Sopenharmony_ci return fd; 41653a5a1b3Sopenharmony_ci 41753a5a1b3Sopenharmony_ci fn = pa_sprintf_malloc("/dev/mixer%i", n); 41853a5a1b3Sopenharmony_ci fd = open_mixer(fn); 41953a5a1b3Sopenharmony_ci pa_xfree(fn); 42053a5a1b3Sopenharmony_ci 42153a5a1b3Sopenharmony_ci if (fd < 0) 42253a5a1b3Sopenharmony_ci pa_log_warn("Failed to open mixer '%s': %s", device, pa_cstrerror(errno)); 42353a5a1b3Sopenharmony_ci 42453a5a1b3Sopenharmony_ci return fd; 42553a5a1b3Sopenharmony_ci} 426