1/* 2 * 3 * BlueZ - Bluetooth protocol stack for Linux 4 * 5 * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org> 6 * 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23 24#ifdef HAVE_CONFIG_H 25#include <config.h> 26#endif 27 28#include <errno.h> 29#include <stdio.h> 30#include <fcntl.h> 31#include <unistd.h> 32#include <stdint.h> 33#include <stdlib.h> 34#include <string.h> 35 36#include <glib.h> 37 38#include "logging.h" 39#include "manager.h" 40#include "adapter.h" 41#include "hcid.h" 42 43enum rfkill_type { 44 RFKILL_TYPE_ALL = 0, 45 RFKILL_TYPE_WLAN, 46 RFKILL_TYPE_BLUETOOTH, 47 RFKILL_TYPE_UWB, 48 RFKILL_TYPE_WIMAX, 49 RFKILL_TYPE_WWAN, 50}; 51 52enum rfkill_operation { 53 RFKILL_OP_ADD = 0, 54 RFKILL_OP_DEL, 55 RFKILL_OP_CHANGE, 56 RFKILL_OP_CHANGE_ALL, 57}; 58 59struct rfkill_event { 60 uint32_t idx; 61 uint8_t type; 62 uint8_t op; 63 uint8_t soft; 64 uint8_t hard; 65}; 66 67static gboolean rfkill_event(GIOChannel *chan, 68 GIOCondition cond, gpointer data) 69{ 70 unsigned char buf[32]; 71 struct rfkill_event *event = (void *) buf; 72 struct btd_adapter *adapter; 73 char sysname[PATH_MAX]; 74 gsize len; 75 GIOError err; 76 int fd, id; 77 78 if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR)) 79 return FALSE; 80 81 memset(buf, 0, sizeof(buf)); 82 83 err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len); 84 if (err) { 85 if (err == G_IO_ERROR_AGAIN) 86 return TRUE; 87 return FALSE; 88 } 89 90 if (len != sizeof(struct rfkill_event)) 91 return TRUE; 92 93 debug("RFKILL event idx %u type %u op %u soft %u hard %u", 94 event->idx, event->type, event->op, 95 event->soft, event->hard); 96 97 if (event->soft || event->hard) 98 return TRUE; 99 100 if (event->op != RFKILL_OP_CHANGE) 101 return TRUE; 102 103 if (event->type != RFKILL_TYPE_BLUETOOTH && 104 event->type != RFKILL_TYPE_ALL) 105 return TRUE; 106 107 snprintf(sysname, sizeof(sysname) - 1, 108 "/sys/class/rfkill/rfkill%u/name", event->idx); 109 110 fd = open(sysname, O_RDONLY); 111 if (fd < 0) 112 return TRUE; 113 114 memset(sysname, 0, sizeof(sysname)); 115 116 if (read(fd, sysname, sizeof(sysname)) < 4) { 117 close(fd); 118 return TRUE; 119 } 120 121 close(fd); 122 123 if (g_str_has_prefix(sysname, "hci") == FALSE) 124 return TRUE; 125 126 id = atoi(sysname + 3); 127 if (id < 0) 128 return TRUE; 129 130 adapter = manager_find_adapter_by_id(id); 131 if (!adapter) 132 return TRUE; 133 134 debug("RFKILL unblock for hci%d", id); 135 136 btd_adapter_restore_powered(adapter); 137 138 return TRUE; 139} 140 141static GIOChannel *channel = NULL; 142 143void rfkill_init(void) 144{ 145 int fd; 146 147 if (!main_opts.remember_powered) 148 return; 149 150 fd = open("/dev/rfkill", O_RDWR); 151 if (fd < 0) { 152 error("Failed to open RFKILL control device"); 153 return; 154 } 155 156 channel = g_io_channel_unix_new(fd); 157 g_io_channel_set_close_on_unref(channel, TRUE); 158 159 g_io_add_watch(channel, G_IO_IN | G_IO_NVAL | G_IO_HUP | G_IO_ERR, 160 rfkill_event, NULL); 161} 162 163void rfkill_exit(void) 164{ 165 if (!channel) 166 return; 167 168 g_io_channel_shutdown(channel, TRUE, NULL); 169 g_io_channel_unref(channel); 170 171 channel = NULL; 172} 173