From 7d83e5c7816b5e343695a75ba58b32dbe1be969a Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Mon, 1 Oct 2007 20:16:28 +0000 Subject: move all sources down to a seperate src/ tree git-svn-id: file:///home/lennart/svn/public/libsydney/trunk@34 9ba3c220-e4d3-45a2-8aa3-73fcc9aff6ce --- src/oss.c | 858 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 858 insertions(+) create mode 100644 src/oss.c (limited to 'src/oss.c') diff --git a/src/oss.c b/src/oss.c new file mode 100644 index 0000000..d09f8aa --- /dev/null +++ b/src/oss.c @@ -0,0 +1,858 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sydney.h" +#include "common.h" +#include "macro.h" +#include "malloc.h" +#include "converter.h" +#include "driver.h" +#include "thread.h" +#include "bufferq.h" + +#define DEFAULT_DEVICE "/dev/dsp" +#define DRIVER_NAME "oss" + +typedef struct oss_stream oss_stream_t; +#define OSS_STREAM(x) ((oss_stream_t*) (x->private)) + +struct oss_stream { + sa_stream_t *parent; + int fd; + pcm_attrs_t real_pcm_attrs; + sa_converter_t converter_read, converter_write; + size_t read_fragment_size, write_fragment_size; + unsigned read_nfragments, write_nfragments; + sa_thread_t *thread; + int socket_fds[2]; + sa_bufferq_t bufferq; +}; + +static int simple_log2(int v) { + int k = 0; + + for (;;) { + v >>= 1; + if (!v) break; + k++; + } + + return k; +} + +static size_t fixup_bps(size_t s, size_t bps1, size_t bps2) { + return (s*bps2)/bps1; +} + +int driver_open(sa_stream_t *s) { + oss_stream_t *oss; + char *n; + int f, arg, bs, r, phase, i, found, suggested, fs, l, m; + unsigned c; + size_t real_bps = 0, bps; + int loops = 0; + + static const int format_map[_SA_PCM_FORMAT_MAX] = { + [SA_PCM_FORMAT_U8] = AFMT_U8, + [SA_PCM_FORMAT_ULAW] = AFMT_MU_LAW, + [SA_PCM_FORMAT_ALAW] = AFMT_A_LAW, + [SA_PCM_FORMAT_S16_LE] = AFMT_S16_LE, + [SA_PCM_FORMAT_S16_BE] = AFMT_S16_BE, + [SA_PCM_FORMAT_S24_LE] = AFMT_S16_NE, /* OSS doesn't know this format, hence we pick the best we can */ + [SA_PCM_FORMAT_S24_BE] = AFMT_S16_NE, + [SA_PCM_FORMAT_S32_LE] = AFMT_S16_NE, + [SA_PCM_FORMAT_S32_BE] = AFMT_S16_NE, + [SA_PCM_FORMAT_FLOAT32_LE] = AFMT_S16_NE, + [SA_PCM_FORMAT_FLOAT32_BE] = AFMT_S16_NE + }; + static const int try_rates[] = { 8000, 16000, 32000, 44100, 48000, 96000, 192000 }; + + if (s->driver && strcmp(s->driver, DRIVER_NAME)) + return SA_ERROR_NO_DRIVER; + + if (!(s->private = oss = sa_new0(oss_stream_t, 1))) + return SA_ERROR_OOM; + + oss->parent = s; + oss->socket_fds[0] = oss->socket_fds[1] = -1; + + n = s->device ? s->device : DEFAULT_DEVICE; + if (!s->codec) + bps = s->pcm_frame_size * s->pcm_attrs.rate; + + for (;;) { + /* We need to loop here, because we have to call SETFRAGMENT + * as first ioctl after the open, at a point where we + * don't now yet the sample type, freq and the number of + * channels we actually settled on. Hence we have to loop + * here: if the sampling format is too far off we have to call + * SETFRAGMENT again which can do only after reopening the + * device again. */ + + if ((oss->fd = open(n, s->mode == SA_MODE_RDONLY ? O_RDONLY : (s->mode == SA_MODE_WRONLY ? O_WRONLY : O_RDWR) | O_NOCTTY | O_NONBLOCK)) < 0) { + + if (errno == ENODEV || errno == ENOENT) + r = SA_ERROR_NO_DEVICE; + else + r = SA_ERROR_SYSTEM; + + goto fail; + } + + fcntl(oss->fd, F_SETFL, fcntl(oss->fd, F_GETFL) & ~O_NONBLOCK); /* FIXME*/ + + if (!s->device) { + if (!(n = sa_strdup(n))) { + r = SA_ERROR_OOM; + goto fail; + } + + s->device = n; + } + + /* Set fragment settings */ + if (s->mode & SA_MODE_WRONLY) { + bs = s->write_lower_watermark; + } + + if (s->mode & SA_MODE_RDONLY) { + int rbs; + + rbs = s->read_upper_watermark; + + if (s->mode & SA_MODE_WRONLY) + bs = bs > rbs ? bs : rbs; + else + bs = rbs; + } + + if (!s->codec && real_bps) + bs = fixup_bps(bs, bps, real_bps); + + fs = bs/4; + l = simple_log2(fs); + if (l < 1) l = 1; + m = (bs+(1< 0x7FFF) m = 0x7FFF; + + printf("Asking OSS for: %u fragments, %u fragsize\n", m, 1 << l); + + arg = (m << 16) | l; + + ioctl(oss->fd, SNDCTL_DSP_SETFRAGMENT, &arg); + /* We ignore errors on this call, since it's merely a hint anyway */ + + if (s->codec) { + + if (strcmp(s->codec, SA_CODEC_AC3) == 0) + f = AFMT_AC3; + else if (strcmp(s->codec, SA_CODEC_MPEG) == 0) + f = AFMT_MPEG; + else { + r = SA_ERROR_NO_CODEC; + goto fail; + } + + } else + f = format_map[s->pcm_attrs.format]; + + bs = 0; + + for (;;) { + arg = f; + + if (ioctl(oss->fd, SNDCTL_DSP_SETFMT, &arg) < 0) { + r = SA_ERROR_SYSTEM; + goto fail; + } + + if (arg == f) + break; + + /* Hmm, the device doesn't support what we're looking for, + * let's try our luck */ + + if (f == AFMT_S16_LE && !bs) { + f = AFMT_S16_BE; + bs = 1; + } else if (f == AFMT_S16_BE && !bs) { + f = AFMT_S16_LE; + bs = 1; + } else if (f == AFMT_S16_LE || f == AFMT_S16_BE) { + f = AFMT_MU_LAW; + bs = 0; + } else if (f == AFMT_MU_LAW && !bs) { + f = AFMT_A_LAW; + bs = 1; + } else if (f == AFMT_A_LAW && !bs) { + f = AFMT_MU_LAW; + bs = 1; + } else if (f == AFMT_A_LAW || f == AFMT_MU_LAW) { + f = AFMT_U8; + } else if (f == AFMT_AC3 || f == AFMT_MPEG) { + r = SA_ERROR_NO_CODEC; + goto fail; + } else { + r = SA_ERROR_NO_PCM_FORMAT; + goto fail; + } + } + + if (!s->codec) { + + switch (f) { + case AFMT_MU_LAW: + oss->real_pcm_attrs.format = SA_PCM_FORMAT_ULAW; + break; + + case AFMT_A_LAW: + oss->real_pcm_attrs.format = SA_PCM_FORMAT_ALAW; + break; + + case AFMT_U8: + oss->real_pcm_attrs.format = SA_PCM_FORMAT_U8; + break; + + case AFMT_S16_LE: + oss->real_pcm_attrs.format = SA_PCM_FORMAT_S16_LE; + break; + + case AFMT_S16_BE: + oss->real_pcm_attrs.format = SA_PCM_FORMAT_S16_BE; + break; + + default: + sa_assert_not_reached(); + } + + + found = 0; + + if (s->adjust_nchannels >= 0) { + + /* First try more channels ... */ + for (c = s->pcm_attrs.nchannels; c < 8 || c == s->pcm_attrs.nchannels; c ++) { + arg = c; + if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &arg) < 0) { + r = SA_ERROR_SYSTEM; + goto fail; + } + + if (arg == (int) c) { + found = 1; + break; + } + } + + /* ... then try less channels */ + if (!found) { + for (c = s->pcm_attrs.nchannels - 1; c > 0; c --) { + arg = c; + if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &arg) < 0) { + r = SA_ERROR_SYSTEM; + goto fail; + } + + if (arg == (int) c) { + found = 1; + break; + } + } + } + } else { + + /* First try less channels ... */ + for (c = s->pcm_attrs.nchannels; c > 0; c --) { + arg = c; + if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &arg) < 0) { + r = SA_ERROR_SYSTEM; + goto fail; + } + + if (arg == (int) c) { + found = 1; + break; + } + } + + /* ... then try more channels */ + if (!found) { + for (c = s->pcm_attrs.nchannels + 1; c < 8; c ++) { + arg = c; + if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &arg) < 0) { + r = SA_ERROR_SYSTEM; + goto fail; + } + + if (arg == (int) c) { + found = 1; + break; + } + } + } + } + + if (!found) { + errno = EIO; + r = SA_ERROR_SYSTEM; + goto fail; + } + + oss->real_pcm_attrs.nchannels = c; + + if (!(oss->real_pcm_attrs.channel_map = sa_new(sa_channel_t, c))) { + r = SA_ERROR_OOM; + goto fail; + } + + switch (c) { + case 8: + oss->real_pcm_attrs.channel_map[7] = SA_CHANNEL_REAR_RIGHT; + case 7: + oss->real_pcm_attrs.channel_map[6] = SA_CHANNEL_REAR_LEFT; + case 6: + oss->real_pcm_attrs.channel_map[5] = SA_CHANNEL_FRONT_LEFT; + case 5: + oss->real_pcm_attrs.channel_map[4] = SA_CHANNEL_FRONT_RIGHT; + case 4: + oss->real_pcm_attrs.channel_map[3] = SA_CHANNEL_LFE; + case 3: + oss->real_pcm_attrs.channel_map[2] = SA_CHANNEL_CENTER; + case 2: + oss->real_pcm_attrs.channel_map[1] = SA_CHANNEL_RIGHT; + oss->real_pcm_attrs.channel_map[0] = SA_CHANNEL_LEFT; + break; + case 1: + oss->real_pcm_attrs.channel_map[0] = SA_CHANNEL_MONO; + break; + } + + r = s->pcm_attrs.rate; + + if (r < 8000) + r = 8000; + + suggested = 0; + phase = 0; + + for (;;) { + arg = r; + + if (ioctl(oss->fd, SNDCTL_DSP_SPEED, &arg) < 0) { + r = SA_ERROR_SYSTEM; + goto fail; + } + + sa_assert(arg > 0); + + if (arg >= r*0.95 || arg <= r *1.05) + break; + + if (arg > suggested) + suggested = arg; + + if (s->adjust_rate >= 0) { + + if (phase == 0) { + /* Find the next higher sample rate to try */ + + for (i = 0; i < (int) SA_ELEMENTSOF(try_rates); i++) { + /* Yes, we could optimize a little here */ + + if (try_rates[i] > r) { + r = try_rates[i]; + break; + } + } + + + if (i == SA_ELEMENTSOF(try_rates)) { + phase = 1; + r = s->pcm_attrs.rate; + } + } + + if (phase == 1) { + /* Find the next lower sample rate to try */ + + for (i = SA_ELEMENTSOF(try_rates); i > 0; i--) { + if (suggested > try_rates[i-1] && suggested < r) { + r = suggested; + break; + } else if (try_rates[i-1] < r) { + r = try_rates[i-1]; + break; + } + } + + sa_assert(i > 0); + } + + } else { + + if (phase == 0) { + /* Find the next lower sample rate to try */ + + for (i = SA_ELEMENTSOF(try_rates); i > 0; i--) { + + if (try_rates[i-1] < r) { + r = try_rates[i-1]; + break; + } + } + + if (i == 0) { + phase = 1; + r = s->pcm_attrs.rate; + } + } + + if (phase == 1) { + /* Find the next higher sample rate to try */ + + for (i = 0; i < (int) SA_ELEMENTSOF(try_rates); i++) { + if (suggested > r && suggested < try_rates[i]) { + r = suggested; + break; + } else if (try_rates[i] < r) { + r = try_rates[i]; + break; + } + } + + sa_assert(i < (int) SA_ELEMENTSOF(try_rates)); + } + } + + } + + oss->real_pcm_attrs.rate = r; + + printf("Chosen: %u channels, %uHz, format=%u\n", oss->real_pcm_attrs.nchannels, oss->real_pcm_attrs.rate, oss->real_pcm_attrs.format); + + real_bps = oss->real_pcm_attrs.nchannels * oss->real_pcm_attrs.rate * sa_get_pcm_sample_size(oss->real_pcm_attrs.format); + + if (real_bps != bps && loops < 1) { + loops++; + + sa_free(oss->real_pcm_attrs.channel_map); + oss->real_pcm_attrs.channel_map = NULL; + + close(oss->fd); + + printf("bps changed, retrying...\n"); + continue; + } + } + + break; + } + + /* First, let's try GETBLKSIZE */ + if (ioctl(oss->fd, SNDCTL_DSP_GETBLKSIZE, &arg) >= 0) { + if (s->mode & SA_MODE_RDONLY) { + oss->read_fragment_size = arg; + oss->read_nfragments = 2; + } + + if (s->mode & SA_MODE_WRONLY) { + oss->write_fragment_size = arg; + oss->write_nfragments = 2; + } + } + + /* Now, let's use GETxSPACE */ + if (s->mode & SA_MODE_RDONLY) { + audio_buf_info info; + + if (ioctl(oss->fd, SNDCTL_DSP_GETISPACE, &info) >= 0) { + oss->read_fragment_size = info.fragsize; + oss->read_nfragments = info.fragstotal; + } + } + + if (s->mode & SA_MODE_WRONLY) { + audio_buf_info info; + + if (ioctl(oss->fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) { + oss->write_fragment_size = info.fragsize; + oss->write_nfragments = info.fragstotal; + } + } + + if (s->mode & SA_MODE_WRONLY && (oss->write_fragment_size <= 0 || oss->write_nfragments <= 1)) { + errno = EIO; + r = SA_ERROR_SYSTEM; + goto fail; + } + + if (s->mode & SA_MODE_RDONLY && (oss->read_fragment_size <= 0 || oss->read_nfragments <= 1)) { + errno = EIO; + r = SA_ERROR_SYSTEM; + goto fail; + } + + if (!s->codec) { + + if (s->adjust_nchannels != 0) { + sa_channel_t *cm; + + if (!(cm = sa_newdup(sa_channel_t, oss->real_pcm_attrs.channel_map, oss->real_pcm_attrs.nchannels))) { + r = SA_ERROR_OOM; + goto fail; + } + + sa_free(s->pcm_attrs.channel_map); + s->pcm_attrs.channel_map = cm; + + s->pcm_attrs.nchannels = oss->real_pcm_attrs.nchannels; + } + if (s->adjust_rate != 0) + s->pcm_attrs.rate = oss->real_pcm_attrs.rate; + if (s->adjust_pcm_format != 0) + s->pcm_attrs.format = oss->real_pcm_attrs.format; + + if (s->mode & SA_MODE_RDONLY) + if ((r = sa_converter_init(&oss->converter_read, &oss->real_pcm_attrs, &s->pcm_attrs, s->dynamic_rate_enabled)) < 0) + goto fail; + + if (s->mode & SA_MODE_WRONLY) + if ((r = sa_converter_init(&oss->converter_write, &s->pcm_attrs, &oss->real_pcm_attrs, s->dynamic_rate_enabled)) < 0) + goto fail; + } + + if (s->adjust_watermarks) { + + if (s->mode & SA_MODE_RDONLY) { + s->read_lower_watermark = oss->read_fragment_size; + s->read_upper_watermark = oss->read_fragment_size * oss->read_nfragments; + } + + if (s->mode & SA_MODE_WRONLY) { + s->write_lower_watermark = oss->write_fragment_size; + s->write_upper_watermark = oss->write_fragment_size * oss->write_nfragments; + } + } + + if (s->mode & SA_MODE_RDONLY) + printf("Chosen for read: %u fragments, %u fragsize\n", oss->read_nfragments, oss->read_fragment_size); + + if (s->mode & SA_MODE_WRONLY) + printf("Chosen for write: %u fragments, %u fragsize\n", oss->write_nfragments, oss->write_fragment_size); + + if (sa_bufferq_init(&oss->bufferq, s->pcm_attrs.nchannels, s->pcm_sample_size) < 0) + goto fail; + + return SA_SUCCESS; + +fail: + driver_destroy(s); + return r; +} + +int driver_destroy(sa_stream_t *s) { + oss_stream_t *oss = OSS_STREAM(s); + + if (oss) { + + if (oss->thread) + driver_stop_thread(s); + + if (oss->fd >= 0) + close(oss->fd); + + sa_bufferq_done(&oss->bufferq); + + sa_free(oss->real_pcm_attrs.channel_map); + sa_converter_done(&oss->converter_read); + sa_converter_done(&oss->converter_write); + + sa_free(oss); + } + + return SA_SUCCESS; +} + +static int feed(sa_stream_t *s) { + oss_stream_t *oss = OSS_STREAM(s); + size_t w; + int ret; + + sa_assert(s); + + for (;;) { + if ((ret = driver_get_write_size(s, &w)) < 0) + return ret; + + while (w > 0) { + void* i[1]; + size_t nbytes; + uint8_t *d; + int drop; + + sa_bufferq_get(&oss->bufferq, i, &nbytes); + + if (nbytes) { + void **dst; + size_t *stride; + + if ((ret = sa_converter_go_interleaved(&oss->converter_write, i[0], &dst, &stride, 1, &nbytes))) + return ret; + + d = dst[0]; + drop = 1; + + s->state = SA_STATE_RUNNING; + + } else { + nbytes = w > oss->write_fragment_size ? oss->write_fragment_size : w; + + if (!(d = sa_converter_get_zero_buffer(&oss->converter_write, nbytes))) + return SA_ERROR_OOM; + + drop = s->xrun_mode == SA_XRUN_MODE_SPIN; + + if (s->xrun_mode == SA_XRUN_MODE_STOP) + s->state = SA_STATE_STOPPED; + } + + while (nbytes > 0) { + ssize_t l; + + if ((l = write(oss->fd, d, nbytes)) < 0) + return SA_ERROR_SYSTEM; + + sa_assert(l > 0); + + nbytes -= l; + d += l; + + w -= 1; + + if (drop) + sa_bufferq_drop(&oss->bufferq, l); + } + } + } + + return SA_SUCCESS; +} + +enum { + POLLFD_OSS_FD, + POLLFD_SOCKET_FD, + POLLFD_MAX +}; + +static void thread_func(void *data) { + struct pollfd pollfds[POLLFD_MAX]; + sa_stream_t *s = data; + oss_stream_t *oss = OSS_STREAM(s); + sigset_t mask; + + sigfillset(&mask); + pthread_sigmask(SIG_BLOCK, &mask, NULL); + + if (s->callback) { + s->event = SA_EVENT_INIT_THREAD; + if (s->callback(s, SA_EVENT_INIT_THREAD) < 0) + return; + } + + memset(pollfds, 0, sizeof(pollfds)); + + pollfds[POLLFD_SOCKET_FD].fd = oss->socket_fds[0]; + pollfds[POLLFD_SOCKET_FD].events = POLLIN; + + pollfds[POLLFD_OSS_FD].fd = oss->fd; + pollfds[POLLFD_OSS_FD].events = ((s->mode & SA_MODE_RDONLY) ? POLLIN : 0) | ((s->mode & SA_MODE_WRONLY) ? POLLOUT : 0); + + for (;;) { + int ret; + if (poll(pollfds, POLLFD_MAX, -1) < 0) { + if (errno == EINTR) + continue; + + if (s->callback) { + s->event = SA_EVENT_ERROR; + s->error = SA_ERROR_SYSTEM; + s->callback(s, SA_EVENT_ERROR); + } + break; + } + + if (pollfds[POLLFD_SOCKET_FD].revents) + break; + + if (pollfds[POLLFD_OSS_FD].revents & (POLLERR|POLLHUP|POLLNVAL)) { + + if (s->callback) { + s->event = SA_EVENT_ERROR; + s->error = SA_ERROR_SYSTEM; + errno = EIO; + s->callback(s, SA_EVENT_ERROR); + } + break; + } + + if (s->callback) { + s->event = SA_EVENT_REQUEST_IO; + if (s->callback(s, SA_EVENT_REQUEST_IO) < 0) + break; + } + + if ((ret = feed(s)) < 0) { + if (s->callback) { + s->event = SA_EVENT_ERROR; + s->error = ret; + s->callback(s, SA_EVENT_ERROR); + } + break; + } + } +} + +int driver_start_thread(sa_stream_t *s, sa_event_callback_t callback) { + oss_stream_t *oss = OSS_STREAM(s); + sa_return_val_if_fail(!oss->thread, SA_ERROR_STATE); + + s->callback = callback; + + if ((socketpair(AF_UNIX, SOCK_DGRAM, 0, oss->socket_fds)) < 0) + return SA_ERROR_SYSTEM; + + if (!(oss->thread = sa_thread_new(thread_func, s))) + return SA_ERROR_OOM; + + return SA_SUCCESS; +} + +int driver_stop_thread(sa_stream_t *s) { + oss_stream_t *oss = OSS_STREAM(s); + sa_return_val_if_fail(oss->thread, SA_ERROR_STATE); + sa_return_val_if_fail(oss->thread != sa_thread_self(), SA_ERROR_STATE); + + if (oss->socket_fds[0] >= 0) + close(oss->socket_fds[0]); + + if (oss->socket_fds[1] >= 0) + close(oss->socket_fds[1]); + + if (oss->thread) + sa_thread_free(oss->thread); + + oss->thread = NULL; + oss->socket_fds[0] = oss->socket_fds[1] = -1; + + return SA_SUCCESS; +} + +int driver_change_read_volume(sa_stream_t *s, const int32_t vol[]) { + oss_stream_t *oss = OSS_STREAM(s); + + sa_return_val_if_fail(!s->codec, SA_ERROR_NOT_SUPPORTED); + + sa_converter_set_volume(&oss->converter_read, vol); + + return SA_SUCCESS; +} + +int driver_change_write_volume(sa_stream_t *s, const int32_t vol[]) { + oss_stream_t *oss = OSS_STREAM(s); + + sa_return_val_if_fail(!s->codec, SA_ERROR_NOT_SUPPORTED); + + sa_converter_set_volume(&oss->converter_write, vol); + + return SA_SUCCESS; +} + +int driver_change_rate(sa_stream_t *s, unsigned rate) { + oss_stream_t *oss = OSS_STREAM(s); + + if (s->mode & SA_MODE_RDONLY) + sa_converter_set_ratio(&oss->converter_read, oss->real_pcm_attrs.rate, s->pcm_attrs.rate); + if (s->mode & SA_MODE_WRONLY) + sa_converter_set_ratio(&oss->converter_write, s->pcm_attrs.rate, oss->real_pcm_attrs.rate); + + return SA_SUCCESS; +} + +int driver_get_state(sa_stream_t *s, sa_state_t *state) { + *state = s->state; + return SA_SUCCESS; +} + +int driver_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) { + return SA_ERROR_NOT_SUPPORTED; +} + +int driver_read(sa_stream_t *s, void *data, size_t nbytes) { + return SA_ERROR_NOT_SUPPORTED; +} + +int driver_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { + oss_stream_t *oss = OSS_STREAM(s); + int ret, ret2; + + if ((ret = sa_bufferq_push(&oss->bufferq, 0, data, nbytes, offset, whence, SA_BUFFERQ_ITEM_STATIC))) + return ret; + + ret = feed(s); + ret2 = sa_bufferq_realloc(&oss->bufferq); + + return ret ? ret : ret2; +} + +int driver_read_ni(sa_stream_t *s, unsigned channel, void *data, size_t nbytes) { + return SA_ERROR_NOT_SUPPORTED; +} + +int driver_pwrite_ni(sa_stream_t *s, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { + return SA_ERROR_NOT_SUPPORTED; +} + +int driver_get_read_size(sa_stream_t *s, size_t *size) { + return SA_ERROR_NOT_SUPPORTED; +} + +int driver_get_write_size(sa_stream_t *s, size_t *size) { + return SA_ERROR_NOT_SUPPORTED; +} + +int driver_resume(sa_stream_t *s) { + return SA_ERROR_NOT_SUPPORTED; +} + +int driver_pause(sa_stream_t *s) { + return SA_ERROR_NOT_SUPPORTED; +} + +int driver_drain(sa_stream_t *s) { + oss_stream_t *oss = OSS_STREAM(s); + sa_return_val_if_fail(!oss->thread, SA_ERROR_STATE); + + if (ioctl(oss->fd, SNDCTL_DSP_SYNC, NULL) < 0) + return SA_ERROR_SYSTEM; + + return SA_SUCCESS; +} + +/* Unsupported operations */ + +int driver_change_device(sa_stream_t *s, const char *device) { + return SA_ERROR_NOT_SUPPORTED; +} + +int driver_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size) { + return SA_ERROR_NOT_SUPPORTED; +} -- cgit