| /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <dbus/dbus.h> |
| #include <errno.h> |
| #include <poll.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <syslog.h> |
| #include <sys/select.h> |
| #include <unistd.h> |
| |
| #include "cras_system_state.h" |
| #include "cras_tm.h" |
| |
| static void dbus_watch_callback(void *arg, int revents) |
| { |
| DBusWatch *watch = (DBusWatch *)arg; |
| int r, flags; |
| struct pollfd pollfd; |
| |
| pollfd.fd = dbus_watch_get_unix_fd(watch); |
| pollfd.events = POLLIN | POLLOUT; |
| |
| r = poll(&pollfd, 1, 0); |
| if (r <= 0) |
| return; |
| |
| flags = 0; |
| if (pollfd.revents & POLLIN) |
| flags |= DBUS_WATCH_READABLE; |
| if (pollfd.revents & POLLOUT) |
| flags |= DBUS_WATCH_WRITABLE; |
| |
| if (!dbus_watch_handle(watch, flags)) |
| syslog(LOG_WARNING, "Failed to handle D-Bus watch."); |
| } |
| |
| static dbus_bool_t dbus_watch_add(DBusWatch *watch, void *data) |
| { |
| int r; |
| unsigned int flags = dbus_watch_get_flags(watch); |
| |
| /* Only select the read watch. |
| * TODO(hychao): select on write watch when we have a use case. |
| */ |
| if ((flags & DBUS_WATCH_READABLE) && dbus_watch_get_enabled(watch)) { |
| r = cras_system_add_select_fd(dbus_watch_get_unix_fd(watch), |
| dbus_watch_callback, watch, |
| POLLIN); |
| if (r != 0) |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| static void dbus_watch_remove(DBusWatch *watch, void *data) |
| { |
| unsigned int flags = dbus_watch_get_flags(watch); |
| |
| /* Only select the read watch. */ |
| if (flags & DBUS_WATCH_READABLE) |
| cras_system_rm_select_fd(dbus_watch_get_unix_fd(watch)); |
| } |
| |
| static void dbus_watch_toggled(DBusWatch *watch, void *data) |
| { |
| if (dbus_watch_get_enabled(watch)) { |
| dbus_watch_add(watch, NULL); |
| } else { |
| dbus_watch_remove(watch, NULL); |
| } |
| } |
| |
| static void dbus_timeout_callback(struct cras_timer *t, void *data) |
| { |
| struct cras_tm *tm = cras_system_state_get_tm(); |
| struct DBusTimeout *timeout = data; |
| |
| /* Timer is automatically removed after it fires. Add a new one so this |
| * fires until it is removed by dbus. */ |
| t = cras_tm_create_timer(tm, dbus_timeout_get_interval(timeout), |
| dbus_timeout_callback, timeout); |
| dbus_timeout_set_data(timeout, t, NULL); |
| |
| if (!dbus_timeout_handle(timeout)) |
| syslog(LOG_WARNING, "Failed to handle D-Bus timeout."); |
| } |
| |
| static dbus_bool_t dbus_timeout_add(DBusTimeout *timeout, void *arg) |
| { |
| struct cras_tm *tm = cras_system_state_get_tm(); |
| struct cras_timer *t = dbus_timeout_get_data(timeout); |
| |
| if (t) { |
| dbus_timeout_set_data(timeout, NULL, NULL); |
| cras_tm_cancel_timer(tm, t); |
| } |
| |
| if (dbus_timeout_get_enabled(timeout)) { |
| t = cras_tm_create_timer(tm, dbus_timeout_get_interval(timeout), |
| dbus_timeout_callback, timeout); |
| dbus_timeout_set_data(timeout, t, NULL); |
| if (t == NULL) |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| static void dbus_timeout_remove(DBusTimeout *timeout, void *arg) |
| { |
| struct cras_tm *tm = cras_system_state_get_tm(); |
| struct cras_timer *t = dbus_timeout_get_data(timeout); |
| |
| if (t) { |
| dbus_timeout_set_data(timeout, NULL, NULL); |
| cras_tm_cancel_timer(tm, t); |
| } |
| } |
| |
| static void dbus_timeout_toggled(DBusTimeout *timeout, void *arg) |
| { |
| if (dbus_timeout_get_enabled(timeout)) |
| dbus_timeout_add(timeout, NULL); |
| else |
| dbus_timeout_remove(timeout, NULL); |
| } |
| |
| DBusConnection *cras_dbus_connect_system_bus() |
| { |
| DBusError dbus_error; |
| DBusConnection *conn; |
| int rc; |
| |
| dbus_error_init(&dbus_error); |
| |
| conn = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error); |
| if (!conn) { |
| syslog(LOG_WARNING, "Failed to connect to D-Bus: %s", |
| dbus_error.message); |
| dbus_error_free(&dbus_error); |
| return NULL; |
| } |
| |
| /* Request a name on the bus. */ |
| rc = dbus_bus_request_name(conn, "org.chromium.cras", 0, &dbus_error); |
| if (dbus_error_is_set(&dbus_error)) { |
| syslog(LOG_ERR, "Requesting dbus name %s", dbus_error.message); |
| dbus_error_free(&dbus_error); |
| } |
| if (rc != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) |
| syslog(LOG_ERR, "Not primary owner of dbus name."); |
| |
| if (!dbus_connection_set_watch_functions( |
| conn, dbus_watch_add, dbus_watch_remove, dbus_watch_toggled, |
| NULL, NULL)) |
| goto error; |
| if (!dbus_connection_set_timeout_functions( |
| conn, dbus_timeout_add, dbus_timeout_remove, |
| dbus_timeout_toggled, NULL, NULL)) |
| goto error; |
| |
| return conn; |
| |
| error: |
| syslog(LOG_WARNING, "Failed to setup D-Bus connection."); |
| dbus_connection_unref(conn); |
| return NULL; |
| } |
| |
| void cras_dbus_dispatch(DBusConnection *conn) |
| { |
| while (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) |
| ; |
| } |
| |
| void cras_dbus_disconnect_system_bus(DBusConnection *conn) |
| { |
| dbus_connection_unref(conn); |
| } |