summaryrefslogtreecommitdiffstats
path: root/src/dso.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dso.c')
-rw-r--r--src/dso.c565
1 files changed, 565 insertions, 0 deletions
diff --git a/src/dso.c b/src/dso.c
new file mode 100644
index 0000000..71c8fd7
--- /dev/null
+++ b/src/dso.c
@@ -0,0 +1,565 @@
+/***
+ This file is part of libsydney.
+
+ Copyright 2007-2008 Lennart Poettering
+
+ libsydney is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Lesser General Public License as
+ published by the Free Software Foundation, either version 2.1 of the
+ License, or (at your option) any later version.
+
+ libsydney 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with libsydney. If not, see
+ <http://www.gnu.org/licenses/>.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ltdl.h>
+#include <string.h>
+#include <errno.h>
+
+#include "driver.h"
+#include "common.h"
+#include "malloc.h"
+#include "driver-order.h"
+
+struct private_dso {
+ lt_dlhandle module;
+ sa_bool_t ltdl_initialized;
+
+ int (*driver_open)(sa_stream *s);
+ int (*driver_destroy)(sa_stream *s);
+
+
+ int (*driver_change_write_latency)(sa_stream *s, size_t *latency, size_t *process_time);
+ int (*driver_change_read_latency)(sa_stream *s, size_t *latency, size_t *process_time);
+
+ int (*driver_change_device)(sa_stream *s, const char *device);
+ int (*driver_change_read_volume)(sa_stream *s, const int32_t vol[]);
+ int (*driver_change_write_volume)(sa_stream *s, const int32_t vol[]);
+ int (*driver_change_pcm_rate)(sa_stream *s, unsigned rate);
+
+ int (*driver_change_props)(sa_stream *s, sa_proplist *changed, sa_proplist *merged);
+
+ int (*driver_get_state)(sa_stream *s, sa_state_t *state);
+ int (*driver_get_position)(sa_stream *s, sa_position_t position, int64_t *pos);
+
+ int (*driver_wait)(sa_stream *s);
+
+ int (*driver_read)(sa_stream *s, void *data, size_t nbytes);
+ int (*driver_read_ni)(sa_stream *s, unsigned channel, void *data, size_t nbytes);
+
+ int (*driver_pwrite)(sa_stream *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
+ int (*driver_pwrite_ni)(sa_stream *s, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence);
+
+ int (*driver_get_read_size)(sa_stream *s, size_t *size);
+ int (*driver_get_write_size)(sa_stream *s, size_t *size);
+
+ int (*driver_resume)(sa_stream *s);
+ int (*driver_pause)(sa_stream *s);
+ int (*driver_drain)(sa_stream *s);
+};
+
+#define PRIVATE_DSO(s) ((struct private_dso *) ((s)->private_dso))
+
+static int sa_error_from_lt_error(int code) {
+
+ static const int table[] = {
+ [LT_ERROR_UNKNOWN] = SA_ERROR_INTERNAL,
+ [LT_ERROR_DLOPEN_NOT_SUPPORTED] = SA_ERROR_NOTSUPPORTED,
+ [LT_ERROR_INVALID_LOADER] = SA_ERROR_INTERNAL,
+ [LT_ERROR_INIT_LOADER] = SA_ERROR_INTERNAL,
+ [LT_ERROR_REMOVE_LOADER] = SA_ERROR_INTERNAL,
+ [LT_ERROR_FILE_NOT_FOUND] = SA_ERROR_NOTFOUND,
+ [LT_ERROR_DEPLIB_NOT_FOUND] = SA_ERROR_NOTFOUND,
+ [LT_ERROR_NO_SYMBOLS] = SA_ERROR_NOTFOUND,
+ [LT_ERROR_CANNOT_OPEN] = SA_ERROR_ACCESS,
+ [LT_ERROR_CANNOT_CLOSE] = SA_ERROR_INTERNAL,
+ [LT_ERROR_SYMBOL_NOT_FOUND] = SA_ERROR_NOTFOUND,
+ [LT_ERROR_NO_MEMORY] = SA_ERROR_OOM,
+ [LT_ERROR_INVALID_HANDLE] = SA_ERROR_INVALID,
+ [LT_ERROR_BUFFER_OVERFLOW] = SA_ERROR_TOOBIG,
+ [LT_ERROR_INVALID_ERRORCODE] = SA_ERROR_INVALID,
+ [LT_ERROR_SHUTDOWN] = SA_ERROR_INTERNAL,
+ [LT_ERROR_CLOSE_RESIDENT_MODULE] = SA_ERROR_INTERNAL,
+ [LT_ERROR_INVALID_MUTEX_ARGS] = SA_ERROR_INTERNAL,
+ [LT_ERROR_INVALID_POSITION] = SA_ERROR_INTERNAL
+ };
+
+ if (code < 0 || code >= LT_ERROR_MAX)
+ return SA_ERROR_INTERNAL;
+
+ return table[code];
+}
+
+static int lt_error_from_string(const char *t) {
+
+ struct lt_error_code {
+ int code;
+ const char *text;
+ };
+
+ static const struct lt_error_code lt_error_codes[] = {
+ /* This is so disgustingly ugly, it makes me vomit. But that's
+ * all ltdl's fault. */
+#define LT_ERROR(u, s) { .code = LT_ERROR_ ## u, .text = s },
+ lt_dlerror_table
+#undef LT_ERROR
+
+ { .code = 0, .text = NULL }
+ };
+
+ const struct lt_error_code *c;
+
+ for (c = lt_error_codes; c->text; c++)
+ if (sa_streq(t, c->text))
+ return c->code;
+
+ return -1;
+}
+
+static int sa_error_from_string(const char *t) {
+ int err;
+
+ if ((err = lt_error_from_string(t)) < 0)
+ return SA_ERROR_INTERNAL;
+
+ return sa_error_from_lt_error(err);
+}
+
+static int try_open(sa_stream *s, const char *t) {
+ char *mn;
+ struct private_dso *p;
+
+ p = PRIVATE_DSO(s);
+
+ if (!(mn = sa_sprintf_malloc(SA_PLUGIN_PATH "/libsydney-%s", t)))
+ return SA_ERROR_OOM;
+
+ errno = 0;
+ p->module = lt_dlopenext(mn);
+ sa_free(mn);
+
+ if (!p->module) {
+ int ret;
+
+ if (errno == ENOENT)
+ ret = SA_ERROR_NOTFOUND;
+ else
+ ret = sa_error_from_string(lt_dlerror());
+
+ if (ret == SA_ERROR_NOTFOUND)
+ ret = SA_ERROR_NODRIVER;
+
+ return ret;
+ }
+
+ return SA_SUCCESS;
+}
+
+static void* real_dlsym(lt_module m, const char *name, const char *symbol) {
+ char sn[256];
+ char *s;
+ void *r;
+
+ sa_return_null_if_fail(m);
+ sa_return_null_if_fail(name);
+ sa_return_null_if_fail(symbol);
+
+ snprintf(sn, sizeof(sn), "%s_%s", name, symbol);
+ sn[sizeof(sn)-1] = 0;
+
+ for (s = sn; *s; s++) {
+ if (*s >= 'a' && *s <= 'z')
+ continue;
+ if (*s >= 'A' && *s <= 'Z')
+ continue;
+ if (*s >= '0' && *s <= '9')
+ continue;
+
+ *s = '_';
+ }
+
+ if ((r = lt_dlsym(m, sn)))
+ return r;
+
+ return lt_dlsym(m, symbol);
+}
+
+#define MAKE_FUNC_PTR(ret, args, x) ((ret (*) args ) (size_t) (x))
+#define GET_FUNC_PTR(module, name, symbol, ret, args) MAKE_FUNC_PTR(ret, args, real_dlsym((module), (name), (symbol)))
+
+int driver_open(sa_stream *s) {
+ int ret;
+ struct private_dso *p;
+ char *driver;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(!PRIVATE_DSO(s), SA_ERROR_STATE);
+
+ if (!(s->private_dso = p = sa_new0(struct private_dso, 1)))
+ return SA_ERROR_OOM;
+
+ if (lt_dlinit() != 0) {
+ ret = sa_error_from_string(lt_dlerror());
+ driver_destroy(s);
+ return ret;
+ }
+
+ p->ltdl_initialized = TRUE;
+
+ if (s->driver) {
+ char *e;
+ size_t n;
+
+ if (!(e = sa_strdup(s->driver))) {
+ driver_destroy(s);
+ return SA_ERROR_OOM;
+ }
+
+ n = strcspn(e, ",:");
+ e[n] = 0;
+
+ if (n == 0) {
+ driver_destroy(s);
+ sa_free(e);
+ return SA_ERROR_INVALID;
+ }
+
+ if ((ret = try_open(s, e)) < 0) {
+ driver_destroy(s);
+ sa_free(e);
+ return ret;
+ }
+
+ driver = e;
+
+ } else {
+ const char *const * e;
+
+ for (e = sa_driver_order; *e; e++) {
+
+ if ((ret = try_open(s, *e)) == SA_SUCCESS)
+ break;
+
+ if (ret != SA_ERROR_NODRIVER &&
+ ret != SA_ERROR_NOTAVAILABLE &&
+ ret != SA_ERROR_NOTFOUND) {
+
+ driver_destroy(s);
+ return ret;
+ }
+ }
+
+ if (!*e) {
+ driver_destroy(s);
+ return SA_ERROR_NODRIVER;
+ }
+
+ if (!(driver = sa_strdup(*e))) {
+ driver_destroy(s);
+ return SA_ERROR_OOM;
+ }
+ }
+
+ sa_assert(p->module);
+
+ if (!(p->driver_open = GET_FUNC_PTR(p->module, driver, "driver_open", int, (sa_stream*))) ||
+ !(p->driver_destroy = GET_FUNC_PTR(p->module, driver, "driver_destroy", int, (sa_stream*))) ||
+ !(p->driver_change_write_latency = GET_FUNC_PTR(p->module, driver, "driver_change_write_latency", int, (sa_stream *s, size_t *latency, size_t *process_time))) ||
+ !(p->driver_change_read_latency = GET_FUNC_PTR(p->module, driver, "driver_change_read_latency", int, (sa_stream *s, size_t *latency, size_t *process_time))) ||
+ !(p->driver_change_device = GET_FUNC_PTR(p->module, driver, "driver_change_device", int, (sa_stream*, const char *))) ||
+ !(p->driver_change_read_volume = GET_FUNC_PTR(p->module, driver, "driver_change_read_volume", int, (sa_stream *s, const int32_t vol[]))) ||
+ !(p->driver_change_write_volume = GET_FUNC_PTR(p->module, driver, "driver_change_write_volume", int, (sa_stream *s, const int32_t vol[]))) ||
+ !(p->driver_change_pcm_rate = GET_FUNC_PTR(p->module, driver, "driver_change_pcm_rate", int, (sa_stream *s, unsigned rate))) ||
+ !(p->driver_change_props = GET_FUNC_PTR(p->module, driver, "driver_change_props", int, (sa_stream *, sa_proplist *, sa_proplist *))) ||
+ !(p->driver_get_state = GET_FUNC_PTR(p->module, driver, "driver_get_state", int, (sa_stream *s, sa_state_t *state))) ||
+ !(p->driver_get_position = GET_FUNC_PTR(p->module, driver, "driver_get_position", int, (sa_stream *s, sa_position_t position, int64_t *pos))) ||
+ !(p->driver_wait = GET_FUNC_PTR(p->module, driver, "driver_wait", int, (sa_stream *s))) ||
+ !(p->driver_read = GET_FUNC_PTR(p->module, driver, "driver_read", int, (sa_stream *s, void *data, size_t nbytes))) ||
+ !(p->driver_read_ni = GET_FUNC_PTR(p->module, driver, "driver_read_ni", int, (sa_stream *s, unsigned channel, void *data, size_t nbytes))) ||
+ !(p->driver_pwrite = GET_FUNC_PTR(p->module, driver, "driver_pwrite", int, (sa_stream *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))) ||
+ !(p->driver_pwrite_ni = GET_FUNC_PTR(p->module, driver, "driver_pwrite_ni", int, (sa_stream *s, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence))) ||
+ !(p->driver_get_read_size = GET_FUNC_PTR(p->module, driver, "driver_get_read_size", int, (sa_stream *s, size_t *size))) ||
+ !(p->driver_get_write_size = GET_FUNC_PTR(p->module, driver, "driver_get_write_size", int, (sa_stream *s, size_t *size))) ||
+ !(p->driver_resume = GET_FUNC_PTR(p->module, driver, "driver_resume", int, (sa_stream *s))) ||
+ !(p->driver_pause = GET_FUNC_PTR(p->module, driver, "driver_pause", int, (sa_stream *s))) ||
+ !(p->driver_drain = GET_FUNC_PTR(p->module, driver, "driver_drain", int, (sa_stream *s)))) {
+
+ sa_free(driver);
+ driver_destroy(s);
+ return SA_ERROR_CORRUPT;
+ }
+
+ sa_free(driver);
+
+ if ((ret = p->driver_open(s)) < 0) {
+ driver_destroy(s);
+ return ret;
+ }
+
+ return SA_SUCCESS;
+}
+
+int driver_destroy(sa_stream *s) {
+ struct private_dso *p;
+ int ret = SA_SUCCESS;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+
+ if (p->driver_destroy)
+ ret = p->driver_destroy(s);
+
+ if (p->module)
+ lt_dlclose(p->module);
+
+ if (p->ltdl_initialized) {
+ lt_dlexit();
+ p->ltdl_initialized = FALSE;
+ }
+
+ sa_free(p);
+
+ s->private_dso = NULL;
+
+ return ret;
+}
+
+int driver_change_write_latency(sa_stream *s, size_t *latency, size_t *process_time) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_change_write_latency, SA_ERROR_STATE);
+
+ return p->driver_change_write_latency(s, latency, process_time);
+}
+
+int driver_change_read_latency(sa_stream *s, size_t *latency, size_t *process_time) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_change_read_latency, SA_ERROR_STATE);
+
+ return p->driver_change_read_latency(s, latency, process_time);
+}
+
+int driver_change_device(sa_stream *s, const char *device) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_change_device, SA_ERROR_STATE);
+
+ return p->driver_change_device(s, device);
+}
+
+int driver_change_read_volume(sa_stream *s, const int32_t vol[]) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_change_read_volume, SA_ERROR_STATE);
+
+ return p->driver_change_read_volume(s, vol);
+}
+
+int driver_change_write_volume(sa_stream *s, const int32_t vol[]) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_change_write_volume, SA_ERROR_STATE);
+
+ return p->driver_change_write_volume(s, vol);
+}
+
+int driver_change_pcm_rate(sa_stream *s, unsigned rate) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_change_pcm_rate, SA_ERROR_STATE);
+
+ return p->driver_change_pcm_rate(s, rate);
+}
+
+int driver_change_props(sa_stream *s, sa_proplist *changed, sa_proplist *merged) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_change_props, SA_ERROR_STATE);
+
+ return p->driver_change_props(s, changed, merged);
+}
+
+int driver_get_state(sa_stream *s, sa_state_t *state) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_get_state, SA_ERROR_STATE);
+
+ return p->driver_get_state(s, state);
+}
+
+int driver_get_position(sa_stream *s, sa_position_t position, int64_t *pos) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_get_position, SA_ERROR_STATE);
+
+ return p->driver_get_position(s, position, pos);
+}
+
+int driver_wait(sa_stream *s) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_wait, SA_ERROR_STATE);
+
+ return p->driver_wait(s);
+}
+
+int driver_read(sa_stream *s, void *data, size_t nbytes) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_read, SA_ERROR_STATE);
+
+ return p->driver_read(s, data, nbytes);
+}
+
+int driver_read_ni(sa_stream *s, unsigned channel, void *data, size_t nbytes) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_read_ni, SA_ERROR_STATE);
+
+ return p->driver_read_ni(s, channel, data, nbytes);
+}
+
+int driver_pwrite(sa_stream *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_pwrite, SA_ERROR_STATE);
+
+ return p->driver_pwrite(s, data, nbytes, offset, whence);
+}
+
+int driver_pwrite_ni(sa_stream *s, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_pwrite_ni, SA_ERROR_STATE);
+
+ return p->driver_pwrite_ni(s, channel, data, nbytes, offset, whence);
+}
+
+int driver_get_read_size(sa_stream *s, size_t *size) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_get_read_size, SA_ERROR_STATE);
+
+ return p->driver_get_read_size(s, size);
+}
+
+int driver_get_write_size(sa_stream *s, size_t *size) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_get_write_size, SA_ERROR_STATE);
+
+ return p->driver_get_write_size(s, size);
+}
+
+int driver_resume(sa_stream *s) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_resume, SA_ERROR_STATE);
+
+ return p->driver_resume(s);
+}
+
+int driver_pause(sa_stream *s) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_pause, SA_ERROR_STATE);
+
+ return p->driver_pause(s);
+}
+
+int driver_drain(sa_stream *s) {
+ struct private_dso *p;
+
+ sa_return_val_if_fail(s, SA_ERROR_INVALID);
+ sa_return_val_if_fail(s->private_dso, SA_ERROR_STATE);
+
+ p = PRIVATE_DSO(s);
+ sa_return_val_if_fail(p->driver_drain, SA_ERROR_STATE);
+
+ return p->driver_drain(s);
+}