/* * Copyright (c) 2012, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include typedef struct dbus_glue_s dbus_glue_t; typedef struct { dbus_glue_t *glue; mrp_io_watch_t *mw; DBusWatch *dw; mrp_list_hook_t hook; } watch_t; typedef struct { dbus_glue_t *glue; mrp_timer_t *mt; DBusTimeout *dt; mrp_list_hook_t hook; } timeout_t; struct dbus_glue_s { DBusConnection *conn; mrp_mainloop_t *ml; mrp_list_hook_t watches; mrp_list_hook_t timers; mrp_deferred_t *pump; }; static dbus_int32_t data_slot = -1; static void dispatch_watch(mrp_io_watch_t *mw, int fd, mrp_io_event_t events, void *user_data) { watch_t *watch = (watch_t *)user_data; DBusConnection *conn = watch->glue->conn; unsigned int mask = 0; MRP_UNUSED(mw); MRP_UNUSED(fd); if (events & MRP_IO_EVENT_IN) mask |= DBUS_WATCH_READABLE; if (events & MRP_IO_EVENT_OUT) mask |= DBUS_WATCH_WRITABLE; if (events & MRP_IO_EVENT_HUP) mask |= DBUS_WATCH_HANGUP; if (events & MRP_IO_EVENT_ERR) mask |= DBUS_WATCH_ERROR; dbus_connection_ref(conn); dbus_watch_handle(watch->dw, mask); dbus_connection_unref(conn); } static void watch_freed_cb(void *data) { watch_t *watch = (watch_t *)data; if (watch != NULL) { mrp_list_delete(&watch->hook); mrp_del_io_watch(watch->mw); mrp_free(watch); } } static dbus_bool_t add_watch(DBusWatch *dw, void *data) { dbus_glue_t *glue = (dbus_glue_t *)data; watch_t *watch; mrp_io_watch_t *mw; mrp_io_event_t mask; int fd; unsigned int flags; mrp_debug("adding D-BUS watch %p (%s)", dw, dbus_watch_get_enabled(dw) ? "enabled" : "disabled"); if (!dbus_watch_get_enabled(dw)) return TRUE; fd = dbus_watch_get_unix_fd(dw); flags = dbus_watch_get_flags(dw); mask = MRP_IO_EVENT_HUP | MRP_IO_EVENT_ERR; if (flags & DBUS_WATCH_READABLE) mask |= MRP_IO_EVENT_IN; if (flags & DBUS_WATCH_WRITABLE) mask |= MRP_IO_EVENT_OUT; mrp_debug("event mask for fd %d: %s%s", fd, mask & MRP_IO_EVENT_IN ? "read" : "", mask & MRP_IO_EVENT_OUT ? "write" : ""); if ((watch = mrp_allocz(sizeof(*watch))) != NULL) { mrp_list_init(&watch->hook); mw = mrp_add_io_watch(glue->ml, fd, mask, dispatch_watch, watch); if (mw != NULL) { watch->glue = glue; watch->mw = mw; watch->dw = dw; dbus_watch_set_data(dw, watch, watch_freed_cb); mrp_list_append(&glue->watches, &watch->hook); return TRUE; } else mrp_free(watch); } return FALSE; } static void del_watch(DBusWatch *dw, void *data) { watch_t *watch = (watch_t *)dbus_watch_get_data(dw); MRP_UNUSED(data); mrp_debug("deleting D-BUS watch %p...", dw); if (watch != NULL) { mrp_del_io_watch(watch->mw); watch->mw = NULL; } } static void toggle_watch(DBusWatch *dw, void *data) { mrp_debug("Toggling D-BUS watch %p...", dw); if (dbus_watch_get_enabled(dw)) add_watch(dw, data); else del_watch(dw, data); } static void dispatch_timeout(mrp_timer_t *mt, void *user_data) { timeout_t *timer = (timeout_t *)user_data; MRP_UNUSED(mt); mrp_debug("dispatching D-BUS timeout %p...", timer->dt); dbus_timeout_handle(timer->dt); } static void timeout_freed_cb(void *data) { timeout_t *timer = (timeout_t *)data; if (timer != NULL) { mrp_list_delete(&timer->hook); mrp_del_timer(timer->mt); mrp_free(timer); } } static dbus_bool_t add_timeout(DBusTimeout *dt, void *data) { dbus_glue_t *glue = (dbus_glue_t *)data; timeout_t *timer; mrp_timer_t *mt; unsigned int msecs; mrp_debug("adding D-BUS timeout %p...", dt); if ((timer = mrp_allocz(sizeof(*timer))) != NULL) { mrp_list_init(&timer->hook); msecs = dbus_timeout_get_interval(dt); mt = mrp_add_timer(glue->ml, msecs, dispatch_timeout, timer); if (mt != NULL) { timer->glue = glue; timer->mt = mt; timer->dt = dt; dbus_timeout_set_data(dt, timer, timeout_freed_cb); mrp_list_append(&glue->timers, &timer->hook); return TRUE; } else mrp_free(timer); } return FALSE; } static void del_timeout(DBusTimeout *dt, void *data) { timeout_t *timer = (timeout_t *)dbus_timeout_get_data(dt); MRP_UNUSED(data); mrp_debug("deleting D-BUS timeout %p...", dt); if (timer != NULL) { mrp_del_timer(timer->mt); timer->mt = NULL; } } static void toggle_timeout(DBusTimeout *dt, void *data) { mrp_debug("toggling D-BUS timeout %p...", dt); if (dbus_timeout_get_enabled(dt)) add_timeout(dt, data); else del_timeout(dt, data); } static void wakeup_mainloop(void *data) { dbus_glue_t *glue = (dbus_glue_t *)data; mrp_debug("waking up mainloop..."); mrp_enable_deferred(glue->pump); } static void glue_free_cb(void *data) { dbus_glue_t *glue = (dbus_glue_t *)data; mrp_list_hook_t *p, *n; watch_t *watch; timeout_t *timer; mrp_list_foreach(&glue->watches, p, n) { watch = mrp_list_entry(p, typeof(*watch), hook); mrp_list_delete(&watch->hook); mrp_del_io_watch(watch->mw); mrp_free(watch); } mrp_list_foreach(&glue->timers, p, n) { timer = mrp_list_entry(p, typeof(*timer), hook); mrp_list_delete(&timer->hook); mrp_del_timer(timer->mt); mrp_free(timer); } mrp_free(glue); } static void pump_cb(mrp_deferred_t *d, void *user_data) { dbus_glue_t *glue = (dbus_glue_t *)user_data; mrp_debug("dispatching dbus connection %p...", glue->conn); if (dbus_connection_dispatch(glue->conn) == DBUS_DISPATCH_COMPLETE) mrp_disable_deferred(d); } static void dispatch_status_cb(DBusConnection *conn, DBusDispatchStatus status, void *user_data) { dbus_glue_t *glue = (dbus_glue_t *)user_data; MRP_UNUSED(conn); switch (status) { case DBUS_DISPATCH_COMPLETE: mrp_debug("dispatching status for %p: complete", conn); mrp_disable_deferred(glue->pump); break; case DBUS_DISPATCH_DATA_REMAINS: case DBUS_DISPATCH_NEED_MEMORY: default: mrp_debug("dispatching status for %p: not complete yet", conn); mrp_enable_deferred(glue->pump); break; } } int mrp_dbus_setup_connection(mrp_mainloop_t *ml, DBusConnection *conn) { dbus_glue_t *glue; if (!dbus_connection_allocate_data_slot(&data_slot)) return FALSE; if (dbus_connection_get_data(conn, data_slot) != NULL) return FALSE; if ((glue = mrp_allocz(sizeof(*glue))) != NULL) { mrp_list_init(&glue->watches); mrp_list_init(&glue->timers); glue->pump = mrp_add_deferred(ml, pump_cb, glue); if (glue->pump == NULL) { mrp_free(glue); return FALSE; } glue->ml = ml; glue->conn = conn; } else return FALSE; if (!dbus_connection_set_data(conn, data_slot, glue, glue_free_cb)) return FALSE; dbus_connection_set_dispatch_status_function(conn, dispatch_status_cb, glue, NULL); dbus_connection_set_wakeup_main_function(conn, wakeup_mainloop, glue, NULL); return dbus_connection_set_watch_functions(conn, add_watch, del_watch, toggle_watch, glue, NULL) && dbus_connection_set_timeout_functions(conn, add_timeout, del_timeout, toggle_timeout, glue, NULL); }