#include <linux/soundcard.h>
#include <sys/asoundlib.h>
+snd_pcm_uframes_t _snd_pcm_boundary(snd_pcm_t *pcm);
+snd_pcm_uframes_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm);
+
#define _GNU_SOURCE
static int debug = 0;
} ops_t;
-#define FD_CLASSES 1
-static ops_t ops[FD_CLASSES];
-
typedef struct {
snd_pcm_t *pcm;
size_t frame_bytes;
oss_dsp_stream_t streams[2];
} oss_dsp_t;
-typedef enum { FD_OSS_DSP = 0 } fd_class_t;
+
+typedef struct {
+ snd_mixer_t *mix;
+ unsigned int modify_counter;
+ snd_mixer_elem_t *elems[SOUND_MIXER_NRDEVICES];
+} oss_mixer_t;
+
+typedef enum {
+ FD_OSS_DSP,
+ FD_OSS_MIXER,
+ FD_CLASSES,
+} fd_class_t;
+
+static ops_t ops[FD_CLASSES];
typedef struct {
int count;
free(dsp);
if (result < 0) {
errno = -result;
- return -1;
+ result = -1;
}
DEBUG("close(%d) -> %d", fd, result);
if (result < 0)
return -1;
}
+int oss_mixer_dev(const char *name, unsigned int index)
+{
+ static struct {
+ char *name;
+ unsigned int index;
+ } id[SOUND_MIXER_NRDEVICES] = {
+ [SOUND_MIXER_VOLUME] = { "Master", 0 },
+ [SOUND_MIXER_BASS] = { "Tone Control - Bass", 0 },
+ [SOUND_MIXER_TREBLE] = { "Tone Control - Treble", 0 },
+ [SOUND_MIXER_SYNTH] = { "Synth", 0 },
+ [SOUND_MIXER_PCM] = { "PCM", 0 },
+ [SOUND_MIXER_SPEAKER] = { "PC Speaker", 0 },
+ [SOUND_MIXER_LINE] = { "Line", 0 },
+ [SOUND_MIXER_MIC] = { "Mic", 0 },
+ [SOUND_MIXER_CD] = { "CD", 0 },
+ [SOUND_MIXER_IMIX] = { "Monitor Mix", 0 },
+ [SOUND_MIXER_ALTPCM] = { "PCM", 1 },
+ [SOUND_MIXER_RECLEV] = { "-- nothing --", 0 },
+ [SOUND_MIXER_IGAIN] = { "Capture", 0 },
+ [SOUND_MIXER_OGAIN] = { "Playback", 0 },
+ [SOUND_MIXER_LINE1] = { "Aux", 0 },
+ [SOUND_MIXER_LINE2] = { "Aux", 1 },
+ [SOUND_MIXER_LINE3] = { "Aux", 2 },
+ [SOUND_MIXER_DIGITAL1] = { "Digital", 0 },
+ [SOUND_MIXER_DIGITAL2] = { "Digital", 1 },
+ [SOUND_MIXER_DIGITAL3] = { "Digital", 2 },
+ [SOUND_MIXER_PHONEIN] = { "Phone", 0 },
+ [SOUND_MIXER_PHONEOUT] = { "Phone", 1 },
+ [SOUND_MIXER_VIDEO] = { "Video", 0 },
+ [SOUND_MIXER_RADIO] = { "Radio", 0 },
+ [SOUND_MIXER_MONITOR] = { "Monitor", 0 },
+ };
+ unsigned int k;
+ for (k = 0; k < SOUND_MIXER_NRDEVICES; ++k) {
+ if (index == id[k].index &&
+ strcmp(name, id[k].name) == 0)
+ return k;
+ }
+ return -1;
+}
+
+static int oss_mixer_close(int fd)
+{
+ int err, result = 0;
+ oss_mixer_t *mixer = fds[fd]->private;
+ err = snd_mixer_close(mixer->mix);
+ if (err < 0)
+ result = err;
+ free(mixer);
+ if (result < 0) {
+ errno = -result;
+ result = -1;
+ }
+ DEBUG("close(%d) -> %d", fd, result);
+ if (result < 0)
+ DEBUG("(errno=%d)\n", errno);
+ else
+ DEBUG("\n");
+ return 0;
+}
+
+int mixer_elem_callback(snd_mixer_elem_t *elem, unsigned int mask)
+{
+ oss_mixer_t *mixer = snd_mixer_elem_get_callback_private(elem);
+ if (mask == SND_CTL_EVENT_MASK_REMOVE) {
+ int idx = oss_mixer_dev(snd_mixer_selem_get_name(elem),
+ snd_mixer_selem_get_index(elem));
+ if (idx >= 0)
+ mixer->elems[idx] = 0;
+ return 0;
+ }
+ if (mask & SND_CTL_EVENT_MASK_VALUE) {
+ mixer->modify_counter++;
+ }
+ return 0;
+}
+
+int mixer_callback(snd_mixer_t *mixer, unsigned int mask,
+ snd_mixer_elem_t *elem)
+{
+ if (mask & SND_CTL_EVENT_MASK_ADD) {
+ oss_mixer_t *mix = snd_mixer_get_callback_private(mixer);
+ int idx = oss_mixer_dev(snd_mixer_selem_get_name(elem),
+ snd_mixer_selem_get_index(elem));
+ if (idx >= 0) {
+ mix->elems[idx] = elem;
+ snd_mixer_selem_set_playback_volume_range(elem, 0, 100);
+ snd_mixer_selem_set_capture_volume_range(elem, 0, 100);
+ snd_mixer_elem_set_callback(elem, mixer_elem_callback);
+ snd_mixer_elem_set_callback_private(elem, snd_mixer_get_callback_private(mixer));
+ }
+ }
+ return 0;
+}
+
+static int oss_mixer_open(int card, int device, int oflag, mode_t mode ATTRIBUTE_UNUSED)
+{
+ oss_mixer_t *mixer;
+ int fd = -1;
+ int result;
+ char name[64];
+
+ switch (device) {
+ case OSS_DEVICE_MIXER:
+ sprintf(name, "mixer%d", card);
+ break;
+ case OSS_DEVICE_AMIXER:
+ sprintf(name, "amixer%d", card);
+ break;
+ default:
+ return RETRY;
+ }
+ switch (oflag & O_ACCMODE) {
+ case O_RDONLY:
+ case O_WRONLY:
+ case O_RDWR:
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ fd = _open("/dev/null", oflag & O_ACCMODE);
+ assert(fd >= 0);
+ fds[fd] = calloc(1, sizeof(fd_t));
+ fds[fd]->class = FD_OSS_MIXER;
+ mixer = calloc(1, sizeof(oss_mixer_t));
+ if (!mixer) {
+ errno = -ENOMEM;
+ return -1;
+ }
+ fds[fd]->private = mixer;
+ result = snd_mixer_open(&mixer->mix);
+ if (result < 0)
+ goto _error;
+ result = snd_mixer_attach(mixer->mix, name);
+ if (result < 0)
+ goto _error1;
+ result = snd_mixer_selem_register(mixer->mix, NULL, NULL);
+ if (result < 0)
+ goto _error1;
+ snd_mixer_set_callback(mixer->mix, mixer_callback);
+ snd_mixer_set_callback_private(mixer->mix, mixer);
+ result = snd_mixer_load(mixer->mix);
+ if (result < 0)
+ goto _error1;
+ return fd;
+ _error1:
+ snd_mixer_close(mixer->mix);
+ _error:
+ close(fd);
+ errno = -result;
+ return -1;
+}
+
static int oss_open(const char *file, int oflag, ...)
{
int result;
result = oss_dsp_open(card, device, oflag, mode);
DEBUG("open(\"%s\", %d, %d) -> %d\n", file, oflag, mode, result);
return result;
+ case OSS_DEVICE_MIXER:
+ case OSS_DEVICE_AMIXER:
+ result = oss_mixer_open(card, device, oflag, mode);
+ DEBUG("open(\"%s\", %d, %d) -> %d\n", file, oflag, mode, result);
+ return result;
default:
return RETRY;
}
return 0;
}
-static ops_t ops[FD_CLASSES] = {
+static ssize_t oss_mixer_write(int fd ATTRIBUTE_UNUSED, const void *buf ATTRIBUTE_UNUSED, size_t n ATTRIBUTE_UNUSED)
+{
+ errno = -EBADFD;
+ return -1;
+}
+
+static ssize_t oss_mixer_read(int fd ATTRIBUTE_UNUSED, void *buf ATTRIBUTE_UNUSED, size_t n ATTRIBUTE_UNUSED)
+{
+ errno = -EBADFD;
+ return -1;
+}
+
+
+static int oss_mixer_read_recsrc(oss_mixer_t *mixer, unsigned int *ret)
+{
+ unsigned int mask = 0;
+ unsigned int k;
+ int err = 0;
+ for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
+ snd_mixer_elem_t *elem = mixer->elems[k];
+ if (elem &&
+ snd_mixer_selem_has_capture_switch(elem)) {
+ int sw;
+ err = snd_mixer_selem_get_capture_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &sw);
+ if (err < 0)
+ break;
+ if (sw)
+ mask |= 1 << k;
+ }
+ }
+ *ret = mask;
+ return err;
+}
+
+
+static int oss_mixer_ioctl(int fd, unsigned long cmd, ...)
+{
+ int err = 0;
+ va_list args;
+ void *arg;
+ oss_mixer_t *mixer = fds[fd]->private;
+ snd_mixer_t *mix = mixer->mix;
+ unsigned int dev;
+
+ va_start(args, cmd);
+ arg = va_arg(args, void *);
+ va_end(args);
+ DEBUG("ioctl(%d, ", fd);
+ switch (cmd) {
+ case OSS_GETVERSION:
+ *(int*)arg = SOUND_VERSION;
+ DEBUG("OSS_GETVERSION, %p) -> [%d]\n", arg, *(int*)arg);
+ break;
+ case SOUND_MIXER_INFO:
+ {
+ mixer_info *info = arg;
+ snd_mixer_handle_events(mix);
+ strcpy(info->id, "alsa-oss");
+ strcpy(info->name, "alsa-oss");
+ info->modify_counter = mixer->modify_counter;
+ DEBUG("SOUND_MIXER_INFO, %p) -> {%s, %s, %d}\n", info, info->id, info->name, info->modify_counter);
+ break;
+ }
+ case SOUND_OLD_MIXER_INFO:
+ {
+ _old_mixer_info *info = arg;
+ strcpy(info->id, "alsa-oss");
+ strcpy(info->name, "alsa-oss");
+ DEBUG("SOUND_OLD_MIXER_INFO, %p) -> {%s, %s}\n", info, info->id, info->name);
+ break;
+ }
+ case SOUND_MIXER_WRITE_RECSRC:
+ {
+ unsigned int k, mask = *(unsigned int *) arg;
+ unsigned int old;
+ int excl = 0;
+ DEBUG("SOUND_MIXER_WRITE_RECSRC, %p) -> [%x]", arg, mask);
+ err = oss_mixer_read_recsrc(mixer, &old);
+ if (err < 0)
+ break;
+ for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
+ snd_mixer_elem_t *elem = mixer->elems[k];
+ if (elem &&
+ snd_mixer_selem_has_capture_switch(elem)) {
+ if (!excl &&
+ snd_mixer_selem_has_capture_switch_exclusive(elem) &&
+ mask & ~old) {
+ mask &= ~old;
+ excl = 1;
+ }
+ err = snd_mixer_selem_set_capture_switch_all(elem, !!(mask & 1 << k));
+ if (err < 0)
+ break;
+ }
+ }
+ if (err < 0)
+ break;
+ goto __read_recsrc;
+ }
+ case SOUND_MIXER_READ_RECSRC:
+ {
+ unsigned int mask;
+ DEBUG("SOUND_MIXER_READ_RECSRC, %p) ->", arg);
+ __read_recsrc:
+ err = oss_mixer_read_recsrc(mixer, &mask);
+ *(int *)arg = mask;
+ DEBUG(" [%x]\n", mask);
+ break;
+ }
+ case SOUND_MIXER_READ_DEVMASK:
+ {
+ int k, mask = 0;
+ for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
+ snd_mixer_elem_t *elem = mixer->elems[k];
+ if (elem &&
+ snd_mixer_selem_has_playback_volume(elem))
+ mask |= 1 << k;
+ }
+ *(int *)arg = mask;
+ DEBUG("SOUND_MIXER_READ_DEVMASK, %p) -> [%x]\n", arg, mask);
+ break;
+ }
+ case SOUND_MIXER_READ_RECMASK:
+ {
+ int k, mask = 0;
+ for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
+ snd_mixer_elem_t *elem = mixer->elems[k];
+ if (elem &&
+ snd_mixer_selem_has_capture_switch(elem))
+ mask |= 1 << k;
+ }
+ *(int *)arg = mask;
+ DEBUG("SOUND_MIXER_READ_RECMASK, %p) -> [%x]\n", arg, mask);
+ break;
+ }
+ case SOUND_MIXER_READ_STEREODEVS:
{
+ int k, mask = 0;
+ for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
+ snd_mixer_elem_t *elem = mixer->elems[k];
+ if (elem &&
+ snd_mixer_selem_has_playback_volume(elem) &&
+ !snd_mixer_selem_is_playback_mono(elem))
+ mask |= 1 << k;
+ }
+ *(int *)arg = mask;
+ DEBUG("SOUND_MIXER_READ_STEREODEVS, %p) -> [%x]\n", arg, mask);
+ break;
+ }
+ case SOUND_MIXER_READ_CAPS:
+ {
+ int k;
+ *(int *)arg = 0;
+ for (k = 0; k < SOUND_MIXER_NRDEVICES; k++) {
+ snd_mixer_elem_t *elem = mixer->elems[k];
+ if (elem &&
+ snd_mixer_selem_has_capture_switch_exclusive(elem)) {
+ * (int*) arg = SOUND_CAP_EXCL_INPUT;
+ break;
+ }
+ }
+ DEBUG("SOUND_MIXER_READ_CAPS, %p) -> [%x]\n", arg, *(int*) arg);
+ break;
+ }
+ default:
+ if (cmd >= MIXER_WRITE(0) && cmd < MIXER_WRITE(SOUND_MIXER_NRDEVICES)) {
+ snd_mixer_elem_t *elem;
+ long lvol, rvol;
+ dev = cmd & 0xff;
+ lvol = *(int *)arg & 0xff;
+ if (lvol > 100)
+ lvol = 100;
+ rvol = (*(int *)arg >> 8) & 0xff;
+ if (rvol > 100)
+ rvol = 100;
+ DEBUG("SOUND_MIXER_WRITE[%d], %p) -> {%ld, %ld}", dev, arg, lvol, rvol);
+ elem = mixer->elems[dev];
+ if (!elem) {
+ err = -EINVAL;
+ break;
+ }
+ if (!snd_mixer_selem_has_playback_volume(elem)) {
+ err = -EINVAL;
+ break;
+ }
+ err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol);
+ if (err < 0)
+ break;
+ if (snd_mixer_selem_is_playback_mono(elem)) {
+ if (snd_mixer_selem_has_playback_switch(elem))
+ err = snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol != 0);
+ if (err < 0)
+ break;
+ } else {
+ err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, rvol);
+ if (err < 0)
+ break;
+ if (snd_mixer_selem_has_playback_switch(elem)) {
+ if (snd_mixer_selem_has_playback_switch_joined(elem))
+ err = snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol != 0 || rvol != 0);
+ else {
+ err = snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol != 0);
+ if (err < 0)
+ break;
+ err = snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, rvol != 0);
+ if (err < 0)
+ break;
+ }
+ }
+ }
+ if (!snd_mixer_selem_has_capture_volume(elem))
+ break;
+ err = snd_mixer_selem_set_capture_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, lvol);
+ if (err < 0)
+ break;
+ if (!snd_mixer_selem_is_capture_mono(elem)) {
+ err = snd_mixer_selem_set_capture_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, rvol);
+ if (err < 0)
+ break;
+ }
+ goto __read;
+ }
+ if (cmd >= MIXER_READ(0) && cmd < MIXER_READ(SOUND_MIXER_NRDEVICES)) {
+ snd_mixer_elem_t *elem;
+ long lvol, rvol;
+ int sw;
+ dev = cmd & 0xff;
+ DEBUG("SOUND_MIXER_READ[%d], %p) ->", dev, arg);
+ __read:
+ elem = mixer->elems[dev];
+ if (!elem) {
+ err = -EINVAL;
+ break;
+ }
+ if (!snd_mixer_selem_has_playback_volume(elem)) {
+ err = -EINVAL;
+ break;
+ }
+ err = snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &sw);
+ if (err < 0)
+ break;
+ if (sw) {
+ err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &lvol);
+ if (err < 0)
+ break;
+ } else
+ lvol = 0;
+ if (snd_mixer_selem_is_playback_mono(elem)) {
+ rvol = lvol;
+ } else {
+ err = snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, &sw);
+ if (err < 0)
+ break;
+ if (sw) {
+ err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &rvol);
+ if (err < 0)
+ break;
+ } else
+ rvol = 0;
+ }
+ * (int*) arg = lvol | (rvol << 8);
+ DEBUG("{%ld, %ld}\n", lvol, rvol);
+ break;
+ }
+ DEBUG("%lx, %p)\n", cmd, arg);
+ err = -ENXIO;
+ break;
+ }
+ if (err >= 0)
+ return 0;
+ errno = -err;
+ return -1;
+}
+
+static int oss_mixer_fcntl(int fd, int cmd, ...)
+{
+ int result;
+ va_list args;
+ long arg;
+
+ va_start(args, cmd);
+ arg = va_arg(args, long);
+ va_end(args);
+
+ DEBUG("fcntl(%d, ", fd);
+ result = _fcntl(fd, cmd, arg);
+ if (result < 0)
+ return result;
+ switch (cmd) {
+ case F_DUPFD:
+ DEBUG("F_DUPFD, %ld)\n", arg);
+ fds[arg] = fds[fd];
+ return result;
+ default:
+ DEBUG("%x, %ld)\n", cmd, arg);
+ return result;
+ }
+ return -1;
+}
+
+static void *oss_mixer_mmap(void *addr ATTRIBUTE_UNUSED, size_t len ATTRIBUTE_UNUSED, int prot ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, int fd ATTRIBUTE_UNUSED, off_t offset ATTRIBUTE_UNUSED)
+{
+ errno = -EBADFD;
+ return MAP_FAILED;
+}
+
+static int oss_mixer_munmap(int fd ATTRIBUTE_UNUSED, void *addr ATTRIBUTE_UNUSED, size_t len ATTRIBUTE_UNUSED)
+{
+ errno = -EBADFD;
+ return -1;
+}
+
+static ops_t ops[FD_CLASSES] = {
+ [FD_OSS_DSP] = {
open: oss_open,
close: oss_dsp_close,
write: oss_dsp_write,
fcntl: oss_dsp_fcntl,
mmap: oss_dsp_mmap,
munmap: oss_dsp_munmap,
- }
+ },
+ [FD_OSS_MIXER] = {
+ open: oss_open,
+ close: oss_mixer_close,
+ write: oss_mixer_write,
+ read: oss_mixer_read,
+ ioctl: oss_mixer_ioctl,
+ fcntl: oss_mixer_fcntl,
+ mmap: oss_mixer_mmap,
+ munmap: oss_mixer_munmap,
+ },
};
int open(const char *file, int oflag, ...)
}
#endif
+int snd_pcm_poll_descriptor(snd_pcm_t *pcm)
+{
+ int err;
+ struct pollfd pfds[2];
+ err = snd_pcm_poll_descriptors(pcm, pfds, 2);
+ assert(err == 1);
+ return pfds[0].fd;
+}
+
int poll(struct pollfd *pfds, unsigned long nfds, int timeout)
{
unsigned int k;