From 181e9c6d5d11cb1e5d36a2777eeb233ad8ed00e5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 9 Oct 2008 18:15:23 +0200 Subject: big pile of updates to match more what happened with libcanberra --- src/dso.c | 565 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 565 insertions(+) create mode 100644 src/dso.c (limited to 'src/dso.c') 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 + . +***/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#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); +} -- cgit