From 04b0e71b26a80dbd2921489a65472c4b6846df17 Mon Sep 17 00:00:00 2001 From: Abramo Bagnara Date: Sat, 9 Dec 2000 10:43:21 +0000 Subject: [PATCH] First public version of OSS emulation library --- Makefile.am | 7 + alsa-oss.c | 1394 ++++++++++++++++++++++++++++++++++++++++++++++++++ configure.in | 9 + cvscompile | 11 + 4 files changed, 1421 insertions(+) create mode 100644 Makefile.am create mode 100644 alsa-oss.c create mode 100644 configure.in create mode 100644 cvscompile diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..782e321 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,7 @@ + +CFLAGS = -g -O2 -W -Wall + +lib_LTLIBRARIES = libaoss.la + +libaoss_la_SOURCES = alsa-oss.c +libaoss_la_LIBADD = -ldl -lasound diff --git a/alsa-oss.c b/alsa-oss.c new file mode 100644 index 0000000..3b6dcb3 --- /dev/null +++ b/alsa-oss.c @@ -0,0 +1,1394 @@ +/* + * OSS -> ALSA compatibility layer + * Copyright (c) by Abramo Bagnara + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#define DEBUG_POLL +#define DEBUG_SELECT +#define debug(...) fprintf(stderr, __VA_ARGS__); +#else +#define debug(...) +#endif + +int (*_select)(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); +int (*_poll)(struct pollfd *ufds, unsigned int nfds, int timeout); + +/* Note that to do a fool proof job we need also to trap: + fopen, fdopen, freopen, fclose, fwrite, fread, etc. + I hope that no applications use stdio to access OSS devices */ + +int (*_open)(const char *file, int oflag, ...); +int (*_close)(int fd); +ssize_t (*_write)(int fd, const void *buf, size_t n); +ssize_t (*_read)(int fd, void *buf, size_t n); +int (*_ioctl)(int fd, unsigned long request, ...); +int (*_fcntl)(int fd, int cmd, ...); +void *(*_mmap)(void *addr, size_t len, int prot, int flags, int fd, off_t offset); +int (*_munmap)(void* addr, size_t len); + +typedef struct ops { + int (*open)(const char *file, int oflag, ...); + int (*close)(int fd); + ssize_t (*write)(int fd, const void *buf, size_t n); + ssize_t (*read)(int fd, void *buf, size_t n); + int (*ioctl)(int fd, unsigned long request, ...); + int (*fcntl)(int fd, int cmd, ...); + void *(*mmap)(void *addr, size_t len, int prot, int flags, int fd, off_t offset); + int (*munmap)(int fd, void* addr, size_t len); +} ops_t; + + +#define FD_CLASSES 1 +static ops_t ops[FD_CLASSES]; + +typedef struct { + snd_pcm_t *pcm; + size_t frame_bytes; + size_t fragment_size; + size_t fragments; + size_t buffer_size; + size_t bytes; + size_t boundary; + size_t old_hw_ptr; + unsigned int mmap:1, + disabled:1; +} oss_dsp_stream_t; + +typedef struct { + int channels; + int rate; + int format; + int fragshift; + int maxfrags; + int subdivision; + oss_dsp_stream_t streams[2]; +} oss_dsp_t; + +typedef enum { FD_OSS_DSP = 0, FD_DEFAULT = -1, FD_CLOSED = -2 } fd_class_t; + +typedef struct { + fd_class_t class; + void *private; + void *mmap_area; +} fd_t; + +static fd_t fds[OPEN_MAX]; + +#define RETRY OPEN_MAX +#define OSS_MAJOR 14 +#define OSS_DEVICE_MIXER 0 +#define OSS_DEVICE_SEQUENCER 1 +#define OSS_DEVICE_MIDI 2 +#define OSS_DEVICE_DSP 3 +#define OSS_DEVICE_AUDIO 4 +#define OSS_DEVICE_DSPW 5 +#define OSS_DEVICE_SNDSTAT 6 +#define OSS_DEVICE_MUSIC 8 +#define OSS_DEVICE_DMMIDI 9 +#define OSS_DEVICE_DMFM 10 +#define OSS_DEVICE_AMIXER 11 +#define OSS_DEVICE_ADSP 12 +#define OSS_DEVICE_AMIDI 13 +#define OSS_DEVICE_ADMMIDI 14 + +static int oss_format_to_alsa(int format) +{ + switch (format) { + case AFMT_MU_LAW: return SND_PCM_FORMAT_MU_LAW; + case AFMT_A_LAW: return SND_PCM_FORMAT_A_LAW; + case AFMT_IMA_ADPCM: return SND_PCM_FORMAT_IMA_ADPCM; + case AFMT_U8: return SND_PCM_FORMAT_U8; + case AFMT_S16_LE: return SND_PCM_FORMAT_S16_LE; + case AFMT_S16_BE: return SND_PCM_FORMAT_S16_BE; + case AFMT_S8: return SND_PCM_FORMAT_S8; + case AFMT_U16_LE: return SND_PCM_FORMAT_U16_LE; + case AFMT_U16_BE: return SND_PCM_FORMAT_U16_BE; + case AFMT_MPEG: return SND_PCM_FORMAT_MPEG; + default: return SND_PCM_FORMAT_U8; + } +} + +static int alsa_format_to_oss(int format) +{ + switch (format) { + case SND_PCM_FORMAT_MU_LAW: return AFMT_MU_LAW; + case SND_PCM_FORMAT_A_LAW: return AFMT_A_LAW; + case SND_PCM_FORMAT_IMA_ADPCM: return AFMT_IMA_ADPCM; + case SND_PCM_FORMAT_U8: return AFMT_U8; + case SND_PCM_FORMAT_S16_LE: return AFMT_S16_LE; + case SND_PCM_FORMAT_S16_BE: return AFMT_S16_BE; + case SND_PCM_FORMAT_S8: return AFMT_S8; + case SND_PCM_FORMAT_U16_LE: return AFMT_U16_LE; + case SND_PCM_FORMAT_U16_BE: return AFMT_U16_BE; + case SND_PCM_FORMAT_MPEG: return AFMT_MPEG; + default: return -EINVAL; + } +} + +static int oss_dsp_params(oss_dsp_t *dsp) +{ + int k; + for (k = 1; k >= 0; --k) { + oss_dsp_stream_t *str = &dsp->streams[k]; + snd_pcm_t *pcm = str->pcm; + snd_pcm_hw_params_t hw; + snd_pcm_sw_params_t sw; + snd_pcm_hw_info_t info; + snd_pcm_strategy_t *strategy; + int format; + int frag_length; + int err; + if (!pcm) + continue; + snd_pcm_hw_info_any(&info); + if (str->mmap) + info.access_mask = SND_PCM_ACCBIT_MMAP_INTERLEAVED; + else + info.access_mask = SND_PCM_ACCBIT_RW_INTERLEAVED; + format = oss_format_to_alsa(dsp->format); + info.format_mask = 1 << format; + info.channels_min = info.channels_max = dsp->channels; + + if (dsp->maxfrags > 0) + info.fragments_max = dsp->maxfrags; + if (dsp->fragshift > 0) { + frag_length = 1 << dsp->fragshift; + frag_length /= snd_pcm_format_physical_width(format) / 8; + frag_length = (u_int64_t) frag_length * 1000000 / dsp->rate; + } else + frag_length = 250000; + err = snd_pcm_strategy_simple(&strategy, 1000000, 2000000); + assert(err >= 0); + err = snd_pcm_strategy_simple_near(strategy, 0, SND_PCM_HW_INFO_RATE, + dsp->rate, 1); + assert(err >= 0); + err = snd_pcm_strategy_simple_near(strategy, 1, SND_PCM_HW_INFO_FRAGMENT_LENGTH, + frag_length, 1); + assert(err >= 0); + err = snd_pcm_strategy_simple_near(strategy, 2, SND_PCM_HW_INFO_BUFFER_LENGTH, + 1000000, 1); + err = snd_pcm_hw_info_strategy(pcm, &info, strategy); + snd_pcm_strategy_free(strategy); + if (err < 0) + return err; + err = snd_pcm_hw_params_info(pcm, &hw, &info); + if (err < 0) + return err; + dsp->rate = hw.rate; + dsp->format = alsa_format_to_oss(hw.format); + str->frame_bytes = snd_pcm_format_physical_width(hw.format) * hw.channels / 8; + str->fragment_size = hw.fragment_size; + str->fragments = hw.fragments; + str->buffer_size = hw.fragments * hw.fragment_size; + if (str->disabled) + sw.start_mode = SND_PCM_START_EXPLICIT; + else + sw.start_mode = SND_PCM_START_DATA; + if (str->mmap) + sw.xrun_mode = SND_PCM_XRUN_NONE; + else + sw.xrun_mode = SND_PCM_XRUN_FRAGMENT; + sw.ready_mode = SND_PCM_READY_FRAGMENT; + sw.avail_min = hw.fragment_size; + sw.xfer_min = 1; + sw.xfer_align = 1; + sw.time = 0; + err = snd_pcm_sw_params(pcm, &sw); + if (err < 0) + return err; + str->boundary = sw.boundary; + err = snd_pcm_prepare(pcm); + if (err < 0) + return err; + } + return 0; +} + +static int oss_dsp_close(int fd) +{ + int result = 0; + int k; + oss_dsp_t *dsp = fds[fd].private; + for (k = 0; k < 2; ++k) { + int err; + oss_dsp_stream_t *str = &dsp->streams[k]; + if (!str->pcm) + continue; + err = snd_pcm_close(str->pcm); + if (err < 0) + result = err; + } + _close(fd); + free(dsp); + if (result < 0) { + errno = -result; + return -1; + } + return 0; +} + +static int oss_dsp_open(int card, int device, int oflag, mode_t mode) +{ + oss_dsp_t *dsp; + unsigned int pcm_mode = 0; + unsigned int streams, k; + int format = AFMT_MU_LAW; + int fd = -1; + int result; + char name[64]; + + switch (device) { + case OSS_DEVICE_DSP: + format = AFMT_U8; + sprintf(name, "dsp%d", card); + break; + case OSS_DEVICE_DSPW: + format = AFMT_S16_LE; + sprintf(name, "dspW%d", card); + break; + case OSS_DEVICE_AUDIO: + sprintf(name, "audio%d", card); + break; + case OSS_DEVICE_ADSP: + sprintf(name, "adsp%d", card); + break; + default: + return RETRY; + } + if (mode & O_NONBLOCK) + pcm_mode = SND_PCM_NONBLOCK; + switch (oflag & O_ACCMODE) { + case O_RDONLY: + streams = 1 << SND_PCM_STREAM_CAPTURE; + break; + case O_WRONLY: + streams = 1 << SND_PCM_STREAM_PLAYBACK; + break; + case O_RDWR: + streams = ((1 << SND_PCM_STREAM_PLAYBACK) | + (1 << SND_PCM_STREAM_CAPTURE)); + break; + default: + errno = EINVAL; + return -1; + } + fd = _open("/dev/null", oflag & O_ACCMODE); + assert(fd >= 0); + fds[fd].class = FD_OSS_DSP; + dsp = calloc(1, sizeof(oss_dsp_t)); + if (!dsp) { + errno = -ENOMEM; + return -1; + } + fds[fd].private = dsp; + dsp->channels = 1; + dsp->rate = 8000; + dsp->format = format; + for (k = 0; k < 2; ++k) { + if (!(streams & (1 << k))) + continue; + result = snd_pcm_open(&dsp->streams[k].pcm, name, k, pcm_mode); + if (result < 0) + goto _error; + } + result = oss_dsp_params(dsp); + if (result < 0) + goto _error; + return fd; + + _error: + close(fd); + errno = -result; + return -1; +} + +static int oss_open(const char *file, int oflag, ...) +{ + int result; + int minor, card, device; + struct stat s; + mode_t mode; + va_list args; + va_start(args, oflag); + mode = va_arg(args, mode_t); + va_end(args); + result = stat(file, &s); + if (result < 0) + return RETRY; + if (!S_ISCHR(s.st_mode) || ((s.st_rdev >> 8) & 0xff) != OSS_MAJOR) + return RETRY; + minor = s.st_rdev & 0xff; + card = minor >> 4; + device = minor & 0x0f; + switch (device) { + case OSS_DEVICE_DSP: + case OSS_DEVICE_DSPW: + case OSS_DEVICE_AUDIO: + case OSS_DEVICE_ADSP: + return oss_dsp_open(card, device, oflag, mode); + default: + return RETRY; + } +} + +static ssize_t oss_dsp_write(int fd, const void *buf, size_t n) +{ + ssize_t result; + oss_dsp_t *dsp = fds[fd].private; + oss_dsp_stream_t *str = &dsp->streams[SND_PCM_STREAM_PLAYBACK]; + snd_pcm_t *pcm = str->pcm; + size_t frames; + if (!pcm) { + errno = EBADFD; + return -1; + } + frames = n / str->frame_bytes; + _again: + result = snd_pcm_writei(pcm, buf, frames); + if (result == -EPIPE && + snd_pcm_state(pcm) == SND_PCM_STATE_XRUN && + (result = snd_pcm_prepare(pcm)) == 0) + goto _again; + if (result < 0) { + errno = -result; + return -1; + } + result *= str->frame_bytes; + str->bytes += result; + debug("WRITE %ld %ld\n", (long)n, (long)result); + return result; +} + +static ssize_t oss_dsp_read(int fd, void *buf, size_t n) +{ + ssize_t result; + oss_dsp_t *dsp = fds[fd].private; + oss_dsp_stream_t *str = &dsp->streams[SND_PCM_STREAM_CAPTURE]; + snd_pcm_t *pcm = str->pcm; + size_t frames; + if (!pcm) { + errno = EBADFD; + return -1; + } + frames = n / str->frame_bytes; + _again: + result = snd_pcm_readi(pcm, buf, n); + if (result == -EPIPE && + snd_pcm_state(pcm) == SND_PCM_STATE_XRUN && + (result = snd_pcm_prepare(pcm)) == 0) + goto _again; + if (result < 0) { + errno = -result; + return -1; + } + result *= str->frame_bytes; + str->bytes += result; + return result; +} + +static int oss_dsp_ioctl(int fd, unsigned long request, ...) +{ + int result, err; + va_list args; + void *arg; + oss_dsp_t *dsp = fds[fd].private; + oss_dsp_stream_t *str; + snd_pcm_t *pcm; + + va_start(args, request); + arg = va_arg(args, void *); + va_end(args); + switch (request) { + case OSS_GETVERSION: + debug("OSS_GETVERSION\n"); + *(int*)arg = SOUND_VERSION; + return 0; + case SNDCTL_DSP_RESET: + { + int k; + debug("SNDCTL_DSP_RESET\n"); + result = 0; + for (k = 0; k < 2; ++k) { + str = &dsp->streams[k]; + pcm = str->pcm; + if (!pcm) + continue; + err = snd_pcm_drop(pcm); + if (err >= 0) + err = snd_pcm_prepare(pcm); + if (err < 0) + result = err; + str->bytes = 0; + } + if (result < 0) { + errno = -result; + return -1; + } + return 0; + } + case SNDCTL_DSP_SYNC: + { + int k; + debug("SNDCTL_DSP_SYNC\n"); + result = 0; + for (k = 0; k < 2; ++k) { + str = &dsp->streams[k]; + pcm = str->pcm; + if (!pcm) + continue; + err = snd_pcm_drain(pcm); + if (err >= 0) + err = snd_pcm_prepare(pcm); + if (err < 0) + result = err; + + } + if (result < 0) { + errno = -result; + return -1; + } + return 0; + } + case SNDCTL_DSP_SPEED: + debug("SNDCTL_DSP_SPEED %d\n", *(int *)arg); + dsp->rate = *(int *)arg; + err = oss_dsp_params(dsp); + if (err < 0) { + errno = -err; + return -1; + } + *(int *)arg = dsp->rate; + return 0; + case SNDCTL_DSP_STEREO: + debug("SNDCTL_DSP_STEREO %d\n", *(int *)arg); + if (*(int *)arg) + dsp->channels = 2; + else + dsp->channels = 1; + err = oss_dsp_params(dsp); + if (err < 0) { + errno = -err; + return -1; + } + *(int *)arg = dsp->channels - 1; + return 0; + case SNDCTL_DSP_CHANNELS: + debug("SNDCTL_DSP_CHANNELS %d\n", *(int *)arg); + dsp->channels = (*(int *)arg); + err = oss_dsp_params(dsp); + if (err < 0) { + errno = -err; + return -1; + } + *(int *)arg = dsp->channels; + return 0; + case SNDCTL_DSP_SETFMT: + debug("SNDCTL_DSP_SETFMT %d\n", *(int *)arg); + if (*(int *)arg != AFMT_QUERY) { + dsp->format = *(int *)arg; + err = oss_dsp_params(dsp); + if (err < 0) { + errno = -err; + return -1; + } + } + *(int *) arg = dsp->format; + return 0; + case SNDCTL_DSP_GETBLKSIZE: + str = &dsp->streams[SND_PCM_STREAM_PLAYBACK]; + if (!str->pcm) + str = &dsp->streams[SND_PCM_STREAM_CAPTURE]; + pcm = str->pcm; + *(int *) arg = str->fragment_size * str->frame_bytes; + debug("SNDCTL_DSP_GETBLKSIZE %d\n", *(int *)arg); + return 0; + case SNDCTL_DSP_POST: + debug("SNDCTL_DSP_POST\n"); + return 0; + case SNDCTL_DSP_SUBDIVIDE: + debug("SNDCTL_DSP_SUBDIVIDE %d\n", *(int *)arg); + dsp->subdivision = *(int *)arg; + if (dsp->subdivision < 1) + dsp->subdivision = 1; + err = oss_dsp_params(dsp); + if (err < 0) { + errno = -err; + return -1; + } + return 0; + case SNDCTL_DSP_SETFRAGMENT: + { + debug("SNDCTL_DSP_SETFRAGMENT %x\n", *(int *)arg); + dsp->fragshift = *(int *)arg & 0xffff; + if (dsp->fragshift < 4) + dsp->fragshift = 4; + dsp->maxfrags = ((*(int *)arg) >> 16) & 0xffff; + if (dsp->maxfrags < 2) + dsp->maxfrags = 2; + err = oss_dsp_params(dsp); + if (err < 0) { + errno = -err; + return -1; + } + return 0; + } + case SNDCTL_DSP_GETFMTS: + { + debug("SNDCTL_DSP_GETFMTS\n"); + *(int *)arg = (AFMT_MU_LAW | AFMT_A_LAW | AFMT_IMA_ADPCM | + AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | + AFMT_S8 | AFMT_U16_LE | AFMT_U16_BE); + return 0; + } + case SNDCTL_DSP_NONBLOCK: + { + int k; + debug("SNDCTL_DSP_NONBLOCK\n"); + result = 0; + for (k = 0; k < 2; ++k) { + pcm = dsp->streams[k].pcm; + if (!pcm) + continue; + err = snd_pcm_nonblock(pcm, 1); + if (err < 0) + result = err; + } + if (result < 0) { + errno = -result; + return -1; + } + return 0; + } + case SNDCTL_DSP_GETCAPS: + { + debug("SNDCTL_DSP_GETCAPS\n"); + result = DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP; + if (dsp->streams[SND_PCM_STREAM_PLAYBACK].pcm && + dsp->streams[SND_PCM_STREAM_CAPTURE].pcm) + result |= DSP_CAP_DUPLEX; + *(int*)arg = result; + return 0; + } + case SNDCTL_DSP_GETTRIGGER: + { + int s = 0; + pcm = dsp->streams[SND_PCM_STREAM_PLAYBACK].pcm; + if (pcm) { + err = snd_pcm_state(pcm); + if (err == SND_PCM_STATE_RUNNING) + s |= PCM_ENABLE_OUTPUT; + } + pcm = dsp->streams[SND_PCM_STREAM_CAPTURE].pcm; + if (pcm) { + err = snd_pcm_state(pcm); + if (err == SND_PCM_STATE_RUNNING) + s |= PCM_ENABLE_INPUT; + } + *(int*)arg = s; + debug("SNDCTL_DSP_GETTRIGGER %d\n", *(int*)arg); + return 0; + } + case SNDCTL_DSP_SETTRIGGER: + { + debug("SNDCTL_DSP_SETTRIGGER %d\n", *(int*)arg); + result = *(int*) arg; + str = &dsp->streams[SND_PCM_STREAM_CAPTURE]; + pcm = str->pcm; + if (pcm) { + if (result & PCM_ENABLE_INPUT) { + str->disabled = 0; + if (oss_dsp_params(dsp) >= 0 && + snd_pcm_prepare(pcm) >= 0) + snd_pcm_start(pcm); + } else { + str->disabled = 1; + if (snd_pcm_drop(pcm) >= 0) + oss_dsp_params(dsp); + } + } + str = &dsp->streams[SND_PCM_STREAM_PLAYBACK]; + pcm = str->pcm; + if (pcm) { + if (result & PCM_ENABLE_OUTPUT) { + str->disabled = 0; + if (oss_dsp_params(dsp) >= 0 && + snd_pcm_prepare(pcm) >= 0) + snd_pcm_start(pcm); + } else { + str->disabled = 1; + if (snd_pcm_drop(pcm) >= 0) + oss_dsp_params(dsp); + } + } + return 0; + } + case SNDCTL_DSP_GETISPACE: + { + ssize_t avail, delay; + audio_buf_info *info = arg; + str = &dsp->streams[SND_PCM_STREAM_CAPTURE]; + pcm = str->pcm; + if (!pcm) { + errno = EINVAL; + return -1; + } + if (snd_pcm_state(pcm) == SND_PCM_STATE_RUNNING) { + err = snd_pcm_delay(pcm, &delay); + } + avail = snd_pcm_avail_update(pcm); + if (avail < 0) + avail = 0; + info->fragsize = str->fragment_size * str->frame_bytes; + info->fragstotal = str->fragments; + info->bytes = avail * str->frame_bytes; + info->fragments = avail / str->fragment_size; + return 0; + } + case SNDCTL_DSP_GETOSPACE: + { + ssize_t avail, delay; + audio_buf_info *info = arg; + fprintf(stderr, "OSPACE\n"); + str = &dsp->streams[SND_PCM_STREAM_PLAYBACK]; + pcm = str->pcm; + if (!pcm) { + errno = EINVAL; + return -1; + } + if (snd_pcm_state(pcm) == SND_PCM_STATE_RUNNING) { + err = snd_pcm_delay(pcm, &delay); + } + avail = snd_pcm_avail_update(pcm); + if (avail < 0) + avail = str->buffer_size; + info->fragsize = str->fragment_size * str->frame_bytes; + info->fragstotal = str->fragments; + info->bytes = avail * str->frame_bytes; + info->fragments = avail / str->fragment_size; + return 0; + } + case SNDCTL_DSP_GETIPTR: + { + ssize_t avail, delay; + size_t hw_ptr; + count_info *info = arg; + str = &dsp->streams[SND_PCM_STREAM_CAPTURE]; + pcm = str->pcm; + if (!pcm) { + errno = EINVAL; + return -1; + } + if (snd_pcm_state(pcm) == SND_PCM_STATE_RUNNING) { + err = snd_pcm_delay(pcm, &delay); + if (err < 0) { + errno = -err; + return -1; + } + } else + delay = 0; + avail = snd_pcm_avail_update(pcm); + if (avail < 0) { + errno = -avail; + return -1; + } + hw_ptr = _snd_pcm_mmap_hw_ptr(pcm); + /* FIXME */ + info->bytes = hw_ptr; + info->bytes *= str->frame_bytes; + info->ptr = hw_ptr % str->buffer_size; + info->ptr *= str->frame_bytes; + if (str->mmap) { + ssize_t n = (hw_ptr / str->fragment_size) - (str->old_hw_ptr / str->fragment_size); + if (n < 0) + n += str->boundary / str->fragment_size; + info->blocks = n; + str->old_hw_ptr = hw_ptr; + } else + info->blocks = delay / str->fragment_size; + return 0; + } + case SNDCTL_DSP_GETOPTR: + { + ssize_t avail, delay; + size_t hw_ptr; + count_info *info = arg; + fprintf(stderr, "OPTR\n"); + str = &dsp->streams[SND_PCM_STREAM_PLAYBACK]; + pcm = str->pcm; + if (!pcm) { + errno = EINVAL; + return -1; + } + err = snd_pcm_delay(pcm, &delay); + if (snd_pcm_state(pcm) == SND_PCM_STATE_RUNNING) { + if (err < 0) { + errno = -err; + return -1; + } + } else + delay = 0; + avail = snd_pcm_avail_update(pcm); + if (avail < 0) { + errno = -avail; + return -1; + } + /* FIXME */ + hw_ptr = _snd_pcm_mmap_hw_ptr(pcm); + info->bytes = hw_ptr; + info->bytes *= str->frame_bytes; + info->ptr = hw_ptr % str->buffer_size; + info->ptr *= str->frame_bytes; + if (str->mmap) { + ssize_t n = (hw_ptr / str->fragment_size) - (str->old_hw_ptr / str->fragment_size); + if (n < 0) + n += str->boundary / str->fragment_size; + info->blocks = n; + str->old_hw_ptr = hw_ptr; + } else + info->blocks = delay / str->fragment_size; + return 0; + } + case SNDCTL_DSP_GETODELAY: + { + ssize_t delay; + fprintf(stderr, "ODELAY\n"); + str = &dsp->streams[SND_PCM_STREAM_PLAYBACK]; + pcm = str->pcm; + if (!pcm) { + errno = EINVAL; + return -1; + } + if (snd_pcm_state(pcm) != SND_PCM_STATE_RUNNING || + snd_pcm_delay(pcm, &delay) < 0) + delay = 0; + *(int *)arg = delay * str->frame_bytes; + return 0; + } + case SNDCTL_DSP_SETDUPLEX: + return 0; + case SOUND_PCM_READ_RATE: + { + *(int *)arg = dsp->rate; + return 0; + } + case SOUND_PCM_READ_CHANNELS: + { + *(int *)arg = dsp->channels; + return 0; + } + case SOUND_PCM_READ_BITS: + { + *(int *)arg = snd_pcm_format_width(oss_format_to_alsa(dsp->format)); + return 0; + } + case SNDCTL_DSP_MAPINBUF: + case SNDCTL_DSP_MAPOUTBUF: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + case SOUND_PCM_WRITE_FILTER: + errno = EINVAL; + return -1; + default: + // return oss_mixer_ioctl(...); + errno = ENXIO; + return -1; + } +} + +static int oss_dsp_fcntl(int fd, int cmd, ...) +{ + int result; + va_list args; + long arg; + + va_start(args, cmd); + arg = va_arg(args, long); + va_end(args); + result = _fcntl(fd, cmd, arg); + if (result < 0) + return result; + switch (cmd) { + case F_DUPFD: + fds[arg] = fds[fd]; + return result; + case F_SETFL: + if (arg & O_NONBLOCK) { + int k; + int err; + snd_pcm_t *pcm; + oss_dsp_t *dsp = fds[fd].private; + for (k = 0; k < 2; ++k) { + pcm = dsp->streams[k].pcm; + if (!pcm) + continue; + err = snd_pcm_nonblock(pcm, 1); + if (err < 0) + result = err; + } + if (result < 0) { + errno = -result; + return -1; + } + } + return 0; + default: + return result; + } + return -1; +} + +static void *oss_dsp_mmap(void *addr ATTRIBUTE_UNUSED, size_t len ATTRIBUTE_UNUSED, int prot ATTRIBUTE_UNUSED, int flags ATTRIBUTE_UNUSED, int fd, off_t offset ATTRIBUTE_UNUSED) +{ + int err; + const snd_pcm_channel_area_t *areas; + oss_dsp_t *dsp = fds[fd].private; + oss_dsp_stream_t *str; + str = &dsp->streams[SND_PCM_STREAM_PLAYBACK]; + if (!str->pcm) + str = &dsp->streams[SND_PCM_STREAM_CAPTURE]; + str->mmap = 1; + err = oss_dsp_params(dsp); + if (err < 0) { + errno = -err; + return MAP_FAILED; + } + areas = snd_pcm_mmap_areas(str->pcm); + return areas->addr; +} + +static int oss_dsp_munmap(int fd, void *addr ATTRIBUTE_UNUSED, size_t len ATTRIBUTE_UNUSED) +{ + int err; + oss_dsp_t *dsp = fds[fd].private; + oss_dsp_stream_t *str; + str = &dsp->streams[SND_PCM_STREAM_PLAYBACK]; + if (!str->pcm) + str = &dsp->streams[SND_PCM_STREAM_CAPTURE]; + str->mmap = 0; + err = oss_dsp_params(dsp); + if (err < 0) { + errno = -err; + return -1; + } + return 0; +} + +static ops_t ops[FD_CLASSES] = { + { + open: oss_open, + close: oss_dsp_close, + write: oss_dsp_write, + read: oss_dsp_read, + ioctl: oss_dsp_ioctl, + fcntl: oss_dsp_fcntl, + mmap: oss_dsp_mmap, + munmap: oss_dsp_munmap, + } +}; + +int open(const char *file, int oflag, ...) +{ + va_list args; + mode_t mode = 0; + int k; + int fd; + + if (oflag & O_CREAT) { + va_start(args, oflag); + mode = va_arg(args, mode_t); + va_end(args); + } + for (k = 0; k < FD_CLASSES; ++k) { + if (!ops[k].open) + continue; + fd = ops[k].open(file, oflag, mode); + if (fd != RETRY) + goto _end; + } + fd = _open(file, oflag, mode); + if (fd >= 0) { + if (fds[fd].class != FD_CLOSED) { + _close(fd); + errno = EMFILE; + return -1; + } + fds[fd].class = FD_DEFAULT; + } + _end: + return fd; +} + +int close(int fd) +{ + int result; + if (fd < 0 || fd >= OPEN_MAX || fds[fd].class < 0) + result = _close(fd); + else + result = ops[fds[fd].class].close(fd); + if (result >= 0) + fds[fd].class = FD_CLOSED; + return result; +} + +ssize_t write(int fd, const void *buf, size_t n) +{ + if (fd < 0 || fd >= OPEN_MAX || fds[fd].class < 0) + return _write(fd, buf, n); + else + return ops[fds[fd].class].write(fd, buf, n); +} + +ssize_t read(int fd, void *buf, size_t n) +{ + if (fd < 0 || fd >= OPEN_MAX || fds[fd].class < 0) + return _read(fd, buf, n); + else + return ops[fds[fd].class].read(fd, buf, n); +} + +int ioctl(int fd, unsigned long request, ...) +{ + va_list args; + void *arg; + + va_start(args, request); + arg = va_arg(args, void *); + va_end(args); + if (fd < 0 || fd >= OPEN_MAX || fds[fd].class < 0) + return _ioctl(fd, request, arg); + else + return ops[fds[fd].class].ioctl(fd, request, arg); +} + +int fcntl(int fd, int cmd, ...) +{ + va_list args; + void *arg; + + va_start(args, cmd); + arg = va_arg(args, void *); + va_end(args); + if (fd < 0 || fd >= OPEN_MAX || fds[fd].class < 0) + return _fcntl(fd, cmd, arg); + else + return ops[fds[fd].class].fcntl(fd, cmd, arg); +} + +void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset) +{ + void *result; + if (fd < 0 || fd >= OPEN_MAX || fds[fd].class < 0) + return _mmap(addr, len, prot, flags, fd, offset); + result = ops[fds[fd].class].mmap(addr, len, prot, flags, fd, offset); + if (result != NULL && result != MAP_FAILED) + fds[fd].mmap_area = result; + return result; +} + +int munmap(void *addr, size_t len) +{ + int fd; + for (fd = 0; fd < OPEN_MAX; ++fd) { + if (fds[fd].mmap_area == addr) + break; + } + if (fd >= OPEN_MAX || fds[fd].class < 0) + return _munmap(addr, len); + else + return ops[fds[fd].class].munmap(fd, addr, len); +} + +#ifdef DEBUG_POLL +void dump_poll(struct pollfd *pfds, unsigned long nfds, int timeout) +{ + unsigned int k; + printf("POLL nfds: %ld, timeout: %d\n", nfds, timeout); + for (k = 0; k < nfds; ++k) { + printf("fd=%d, events=%x, revents=%x\n", + pfds[k].fd, pfds[k].events, pfds[k].revents); + } +} +#endif + +#ifdef DEBUG_SELECT +void dump_select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, + struct timeval *timeout) +{ + int k; + printf("SELECT nfds: %d, ", nfds); + if (timeout) + printf("timeout: %ld.%06ld\n", timeout->tv_sec, timeout->tv_usec); + else + printf("no timeout\n"); + if (rfds) { + printf("rfds: "); + for (k = 0; k < nfds; ++k) { + if (FD_ISSET(k, rfds)) + putchar('1'); + else + putchar('0'); + } + putchar('\n'); + } + if (wfds) { + printf("wfds: "); + for (k = 0; k < nfds; ++k) { + if (FD_ISSET(k, wfds)) + putchar('1'); + else + putchar('0'); + } + putchar('\n'); + } + if (efds) { + printf("efds: "); + for (k = 0; k < nfds; ++k) { + if (FD_ISSET(k, efds)) + putchar('1'); + else + putchar('0'); + } + putchar('\n'); + } +} +#endif + +int poll(struct pollfd *pfds, unsigned long nfds, int timeout) +{ + unsigned int k; + unsigned int nfds1; + int count, count1; + int direct = 1; + struct pollfd pfds1[nfds * 2]; + nfds1 = 0; + for (k = 0; k < nfds; ++k) { + int fd = pfds[k].fd; + pfds[k].revents = 0; + if (fd >= OPEN_MAX) + goto _std1; + switch (fds[fd].class) { + case FD_OSS_DSP: + { + oss_dsp_t *dsp = fds[fd].private; + oss_dsp_stream_t *str; + int j; + for (j = 0; j < 2; ++j) { + str = &dsp->streams[j]; + if (str->pcm) { + pfds1[nfds1].fd = snd_pcm_poll_descriptor(str->pcm); + pfds1[nfds1].events = pfds[k].events; + pfds1[nfds1].revents = 0; + nfds1++; + } + } + direct = 0; + break; + } + default: + _std1: + pfds1[nfds1].fd = pfds[k].fd; + pfds1[nfds1].events = pfds[k].events; + pfds1[nfds1].revents = 0; + nfds1++; + break; + } + } + if (direct) + return _poll(pfds, nfds, timeout); +#ifdef DEBUG_POLL + printf("Orig enter "); + dump_poll(pfds, nfds, timeout); + printf("Changed enter "); + dump_poll(pfds1, nfds1, timeout); +#endif + count = _poll(pfds1, nfds1, timeout); + if (count <= 0) + return count; + nfds1 = 0; + count1 = 0; + for (k = 0; k < nfds; ++k) { + int fd = pfds[k].fd; + unsigned int revents; + if (fd >= OPEN_MAX) + goto _std2; + switch (fds[fd].class) { + case FD_OSS_DSP: + { + oss_dsp_t *dsp = fds[fd].private; + oss_dsp_stream_t *str; + int j; + revents = 0; + for (j = 0; j < 2; ++j) { + str = &dsp->streams[j]; + if (str->pcm) { + revents |= pfds1[nfds1].revents; + nfds1++; + } + } + break; + } + default: + _std2: + revents = pfds1[nfds1].revents; + nfds1++; + break; + } + pfds[k].revents = revents; + if (revents) + count1++; + } +#ifdef DEBUG_POLL + printf("Changed exit "); + dump_poll(pfds1, nfds1, timeout); + printf("Orig exit "); + dump_poll(pfds, nfds, timeout); +#endif + return count1; +} + +int select(int nfds, fd_set *rfds, fd_set *wfds, fd_set *efds, + struct timeval *timeout) +{ + fd_set _rfds1, _wfds1, _efds1; + fd_set *rfds1, *wfds1, *efds1; + int nfds1 = nfds; + int count, count1; + int fd; + int direct = 1; + if (rfds) { + _rfds1 = *rfds; + rfds1 = &_rfds1; + } else + rfds1 = NULL; + if (wfds) { + _wfds1 = *wfds; + wfds1 = &_wfds1; + } else + wfds1 = NULL; + if (efds) { + _efds1 = *efds; + efds1 = &_efds1; + } else + efds1 = NULL; + for (fd = 0; fd < nfds; ++fd) { + int r = (rfds && FD_ISSET(fd, rfds)); + int w = (wfds && FD_ISSET(fd, wfds)); + int e = (efds && FD_ISSET(fd, efds)); + if (!(r || w || e)) + continue; + switch (fds[fd].class) { + case FD_OSS_DSP: + { + oss_dsp_t *dsp = fds[fd].private; + oss_dsp_stream_t *str; + int j; + if (r) + FD_CLR(fd, rfds1); + if (w) + FD_CLR(fd, wfds1); + if (e) + FD_CLR(fd, efds1); + for (j = 0; j < 2; ++j) { + str = &dsp->streams[j]; + if (str->pcm) { + int fd1 = snd_pcm_poll_descriptor(str->pcm); + if (fd1 >= nfds1) + nfds1 = fd1 + 1; + if (r) + FD_SET(fd1, rfds1); + if (w) + FD_SET(fd1, wfds1); + if (e) + FD_SET(fd1, efds1); + } + } + direct = 0; + break; + } + default: + break; + } + } + if (direct) + return _select(nfds, rfds, wfds, efds, timeout); +#ifdef DEBUG_SELECT + printf("Orig enter "); + dump_select(nfds, rfds, wfds, efds, timeout); + printf("Changed enter "); + dump_select(nfds1, rfds1, wfds1, efds1, timeout); +#endif + count = _select(nfds1, rfds1, wfds1, efds1, timeout); + if (count < 0) + return count; + if (count == 0) { + if (rfds) + FD_ZERO(rfds); + if (wfds) + FD_ZERO(wfds); + if (efds) + FD_ZERO(efds); + return 0; + } + count1 = 0; + for (fd = 0; fd < nfds; ++fd) { + int r = (rfds && FD_ISSET(fd, rfds)); + int w = (wfds && FD_ISSET(fd, wfds)); + int e = (efds && FD_ISSET(fd, efds)); + int r1, w1, e1; + if (!(r || w || e)) + continue; + switch (fds[fd].class) { + case FD_OSS_DSP: + { + oss_dsp_t *dsp = fds[fd].private; + oss_dsp_stream_t *str; + int j; + r1 = w1 = e1 = 0; + for (j = 0; j < 2; ++j) { + str = &dsp->streams[j]; + if (str->pcm) { + int fd1 = snd_pcm_poll_descriptor(str->pcm); + if (r && FD_ISSET(fd1, rfds1)) + r1++; + if (w && FD_ISSET(fd1, wfds1)) + w1++; + if (e && FD_ISSET(fd1, efds1)) + e1++; +#if 1 + { + size_t delay; + snd_pcm_delay(str->pcm, &delay); + fprintf(stderr, "%d %d %d\n", delay, str->buffer_size, str->fragment_size); + } +#endif + } + } + break; + } + default: + r1 = (r && FD_ISSET(fd, rfds1)); + w1 = (w && FD_ISSET(fd, wfds1)); + e1 = (e && FD_ISSET(fd, efds1)); + break; + } + if (r && !r1) + FD_CLR(fd, rfds); + if (w && !w1) + FD_CLR(fd, wfds); + if (e && !e1) + FD_CLR(fd, efds); + if (r1 || w1 || e1) + count1++; + } +#ifdef DEBUG_SELECT + printf("Changed exit "); + dump_select(nfds1, rfds1, wfds1, efds1, timeout); + printf("Orig exit "); + dump_select(nfds, rfds, wfds, efds, timeout); +#endif + return count1; +} + + +int dup(int fd) +{ + return fcntl(fd, F_DUPFD, 0); +} + +int dup2(int fd, int fd2) +{ + int save; + + if (fd2 < 0 || fd2 >= OPEN_MAX) { + errno = EBADF; + return -1; + } + + if (fcntl(fd, F_GETFL) < 0) + return -1; + + if (fd == fd2) + return fd2; + + save = errno; + close(fd2); + errno = save; + + return fcntl(fd, F_DUPFD, fd2); +} + +#ifndef O_LARGEFILE +#define O_LARGEFILE 0100000 +#endif + +int open64(const char *file, int oflag, ...) +{ + va_list args; + mode_t mode = 0; + + if (oflag & O_CREAT) { + va_start(args, oflag); + mode = va_arg(args, mode_t); + va_end(args); + } + return open(file, oflag | O_LARGEFILE, mode); +} + +static void initialize() __attribute__ ((constructor)); + +static void initialize() +{ + int k; + _open = dlsym(RTLD_NEXT, "open"); + _close = dlsym(RTLD_NEXT, "close"); + _write = dlsym(RTLD_NEXT, "write"); + _read = dlsym(RTLD_NEXT, "read"); + _ioctl = dlsym(RTLD_NEXT, "ioctl"); + _fcntl = dlsym(RTLD_NEXT, "fcntl"); + _mmap = dlsym(RTLD_NEXT, "mmap"); + _munmap = dlsym(RTLD_NEXT, "munmap"); + _select = dlsym(RTLD_NEXT, "select"); + _poll = dlsym(RTLD_NEXT, "poll"); + for (k = 0; k < OPEN_MAX; ++k) { + fds[k].private = 0; + if (_fcntl(k, F_GETFL) < 0) + fds[k].class = FD_CLOSED; + else + fds[k].class = FD_DEFAULT; + } +} + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..3ed9bb4 --- /dev/null +++ b/configure.in @@ -0,0 +1,9 @@ +AC_INIT(alsa-oss.c) +AM_INIT_AUTOMAKE(alsa-oss, 0.6) + +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_LN_S +AM_PROG_LIBTOOL + +AC_OUTPUT(Makefile) diff --git a/cvscompile b/cvscompile new file mode 100644 index 0000000..aa130d4 --- /dev/null +++ b/cvscompile @@ -0,0 +1,11 @@ +#!/bin/bash + +aclocal $ACLOCAL_FLAGS +automake --foreign --add-missing +autoconf +export CFLAGS='-O2 -Wall -W -pipe -g' +echo "CFLAGS=$CFLAGS" +echo "./configure $@" +./configure $@ +unset CFLAGS +make -- 2.47.1