From bf8b7c07542bd7acb4f9f98ba2165475f0ad9d65 Mon Sep 17 00:00:00 2001 From: Bastien Nocera Date: Tue, 28 Jul 2009 17:25:34 +0100 Subject: [PATCH] Add rfkill plugin The plugin allows us to restore the previous power state on adapters when the killswitch on them has been unblocked. --- plugins/Makefile.am | 3 + plugins/rfkill.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/adapter.c | 23 ++++-- src/adapter.h | 1 + 4 files changed, 219 insertions(+), 7 deletions(-) create mode 100644 plugins/rfkill.c diff --git a/plugins/Makefile.am b/plugins/Makefile.am index 9d9f970..9ba8180 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -15,6 +15,9 @@ endif builtin_modules += hciops builtin_sources += hciops.c +builtin_modules += rfkill +builtin_sources += rfkill.c + if NETLINK plugin_LTLIBRARIES += netlink.la netlink_la_LIBADD = @NETLINK_LIBS@ diff --git a/plugins/rfkill.c b/plugins/rfkill.c new file mode 100644 index 0000000..fad8b47 --- /dev/null +++ b/plugins/rfkill.c @@ -0,0 +1,199 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2009 Bastien Nocera + * Copyright (C) 2007-2009 Intel Corporation. All rights reserved. + * + * Author: + * Bastien Nocera , based on code by + * Johannes Berg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "hcid.h" +#include "plugin.h" +#include "logging.h" + +#include "manager.h" +#include "adapter.h" +#include "device.h" + +enum rfkill_type { + RFKILL_TYPE_ALL = 0, + RFKILL_TYPE_WLAN, + RFKILL_TYPE_BLUETOOTH, + RFKILL_TYPE_UWB, + RFKILL_TYPE_WIMAX, + RFKILL_TYPE_WWAN, +}; + +enum rfkill_operation { + RFKILL_OP_ADD = 0, + RFKILL_OP_DEL, + RFKILL_OP_CHANGE, + RFKILL_OP_CHANGE_ALL, +}; + +struct rfkill_event { + uint32_t idx; + uint8_t type; + uint8_t op; + uint8_t soft; + uint8_t hard; +}; + +static char *get_name(uint32_t idx) +{ + char *filename, *name, *pos; + + filename = g_strdup_printf("/sys/class/rfkill/rfkill%u/name", idx); + if (g_file_get_contents(filename, &name, NULL, NULL) == FALSE) { + g_free(filename); + return NULL; + } + + g_free(filename); + + pos = strchr(name, '\n'); + if (pos) + *pos = '\0'; + + return name; +} + +static gboolean rfkill_event(GIOChannel *chan, + GIOCondition cond, gpointer data) +{ + unsigned char buf[32]; + struct rfkill_event *event = (void *) buf; + char *sysname; + gboolean blocked; + gsize len; + GIOError err; + + if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) + return FALSE; + + memset(buf, 0, sizeof(buf)); + + err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len); + if (err) { + if (err == G_IO_ERROR_AGAIN) + return TRUE; + return FALSE; + } + + if (len != sizeof(struct rfkill_event)) + return TRUE; + + debug("idx %u type %u op %u soft %u hard %u", + event->idx, event->type, event->op, + event->soft, event->hard); + + blocked = (event->soft || event->hard) ? TRUE : FALSE; + /* We already disable devices correctly when rfkilled */ + if (blocked) + return TRUE; + + sysname = get_name(event->idx); + if (sysname == NULL) + return TRUE; + if (g_str_has_prefix(sysname, "hci") == FALSE) { + debug("Ignoring unblocked killswitch '%s'", sysname); + g_free(sysname); + return TRUE; + } + + switch (event->type) { + case RFKILL_TYPE_ALL: + case RFKILL_TYPE_BLUETOOTH: { + struct btd_adapter *adapter; + int id; + + id = atoi(sysname + strlen("hci")); + adapter = manager_find_adapter_by_id(id); + if (adapter) + adapter_set_powered(adapter, TRUE); + break; + } + default: + break; + } + + g_free(sysname); + + return TRUE; +} + +static GIOChannel *channel = NULL; + +static int rfkill_init(void) +{ + int fd; + + debug("Init rfkill plugin"); + + if (main_opts.remember_powered == FALSE) + return 0; + + fd = open("/dev/rfkill", O_RDWR); + if (fd < 0) { + debug("No rfkill support in the kernel"); + return -EIO; + } + + channel = g_io_channel_unix_new(fd); + g_io_channel_set_close_on_unref(channel, TRUE); + + g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, + rfkill_event, NULL); + + return 0; +} + +static void rfkill_exit(void) +{ + debug("Cleanup rfkill plugin"); + + if (channel == NULL) + return; + + g_io_channel_shutdown(channel, TRUE, NULL); + g_io_channel_unref(channel); + + channel = NULL; +} + +BLUETOOTH_PLUGIN_DEFINE(rfkill, VERSION, + BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, rfkill_init, rfkill_exit) diff --git a/src/adapter.c b/src/adapter.c index 06640e7..06c3018 100644 --- a/src/adapter.c +++ b/src/adapter.c @@ -483,21 +483,30 @@ done: return 0; } -static DBusMessage *set_powered(DBusConnection *conn, DBusMessage *msg, - gboolean powered, void *data) +int adapter_set_powered(struct btd_adapter *adapter, gboolean powered) { - struct btd_adapter *adapter = data; uint8_t mode; - int err; mode = powered ? get_mode(&adapter->bdaddr, "on") : MODE_OFF; if (mode == adapter->mode) - return dbus_message_new_method_return(msg); + return -EALREADY; - err = set_mode(adapter, mode); - if (err < 0) + return set_mode(adapter, mode); +} + +static DBusMessage *set_powered(DBusConnection *conn, DBusMessage *msg, + gboolean powered, void *data) +{ + struct btd_adapter *adapter = data; + int err; + + err = adapter_set_powered(adapter, powered); + if (err < 0) { + if (err == -EALREADY) + return dbus_message_new_method_return(msg); return failed_strerror(msg, -err); + } return dbus_message_new_method_return(msg); } diff --git a/src/adapter.h b/src/adapter.h index d34fb80..fa02d5d 100644 --- a/src/adapter.h +++ b/src/adapter.h @@ -110,6 +110,7 @@ void adapter_get_address(struct btd_adapter *adapter, bdaddr_t *bdaddr); void adapter_set_state(struct btd_adapter *adapter, int state); int adapter_get_state(struct btd_adapter *adapter); gboolean adapter_is_ready(struct btd_adapter *adapter); +int adapter_set_powered(struct btd_adapter *adapter, gboolean powered); struct remote_dev_info *adapter_search_found_devices(struct btd_adapter *adapter, struct remote_dev_info *match); void adapter_update_found_devices(struct btd_adapter *adapter, bdaddr_t *bdaddr, -- 1.6.2.5