bluetooth.c revision c5c872f46dd2708677c487f190feef24c46ebd88
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "bluedroid" 18 19#include <errno.h> 20#include <fcntl.h> 21#include <sys/ioctl.h> 22#include <sys/socket.h> 23#include <sys/types.h> 24#include <sys/stat.h> 25 26#include <cutils/log.h> 27#include <cutils/properties.h> 28 29#include <bluetooth/bluetooth.h> 30#include <bluetooth/hci.h> 31#include <bluetooth/hci_lib.h> 32 33#include <bluedroid/bluetooth.h> 34 35#ifndef HCI_DEV_ID 36#define HCI_DEV_ID 0 37#endif 38 39#define HCID_START_DELAY_SEC 5 40#define HCID_STOP_DELAY_USEC 500000 41 42#define MIN(x,y) (((x)<(y))?(x):(y)) 43 44 45static int rfkill_id = -1; 46static char *rfkill_state_path = NULL; 47 48 49static int init_rfkill() { 50 char path[64]; 51 char buf[16]; 52 int fd; 53 int sz; 54 int id; 55 for (id = 0; ; id++) { 56 snprintf(path, sizeof(path), "/sys/class/rfkill/rfkill%d/type", id); 57 fd = open(path, O_RDONLY); 58 if (fd < 0) { 59 LOGW("open(%s) failed: %s (%d)\n", path, strerror(errno), errno); 60 return -1; 61 } 62 sz = read(fd, &buf, sizeof(buf)); 63 close(fd); 64 if (sz >= 9 && memcmp(buf, "bluetooth", 9) == 0) { 65 rfkill_id = id; 66 break; 67 } 68 } 69 70 asprintf(&rfkill_state_path, "/sys/class/rfkill/rfkill%d/state", rfkill_id); 71 return 0; 72} 73 74static int check_bluetooth_power() { 75 int sz; 76 int fd = -1; 77 int ret = -1; 78 char buffer; 79 80 if (rfkill_id == -1) { 81 if (init_rfkill()) goto out; 82 } 83 84 fd = open(rfkill_state_path, O_RDONLY); 85 if (fd < 0) { 86 LOGE("open(%s) failed: %s (%d)", rfkill_state_path, strerror(errno), 87 errno); 88 goto out; 89 } 90 sz = read(fd, &buffer, 1); 91 if (sz != 1) { 92 LOGE("read(%s) failed: %s (%d)", rfkill_state_path, strerror(errno), 93 errno); 94 goto out; 95 } 96 97 switch (buffer) { 98 case '1': 99 ret = 1; 100 break; 101 case '0': 102 ret = 0; 103 break; 104 } 105 106out: 107 if (fd >= 0) close(fd); 108 return ret; 109} 110 111static int set_bluetooth_power(int on) { 112 int sz; 113 int fd = -1; 114 int ret = -1; 115 const char buffer = (on ? '1' : '0'); 116 117 if (rfkill_id == -1) { 118 if (init_rfkill()) goto out; 119 } 120 121 fd = open(rfkill_state_path, O_WRONLY); 122 if (fd < 0) { 123 LOGE("open(%s) for write failed: %s (%d)", rfkill_state_path, 124 strerror(errno), errno); 125 goto out; 126 } 127 sz = write(fd, &buffer, 1); 128 if (sz < 0) { 129 LOGE("write(%s) failed: %s (%d)", rfkill_state_path, strerror(errno), 130 errno); 131 goto out; 132 } 133 ret = 0; 134 135out: 136 if (fd >= 0) close(fd); 137 return ret; 138} 139 140static inline int create_hci_sock() { 141 int sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); 142 if (sk < 0) { 143 LOGE("Failed to create bluetooth hci socket: %s (%d)", 144 strerror(errno), errno); 145 } 146 return sk; 147} 148 149/* TODO: Remove this once legacy hciattach is removed */ 150static const char * get_hciattach_script() { 151 if (access("/dev/ttyHS0", F_OK)) { 152 LOGD("Using legacy uart driver (115200 bps)"); 153 return "hciattach_legacy"; 154 } else { 155 LOGD("Using high speed uart driver (4 Mbps)"); 156 return "hciattach"; 157 } 158} 159 160int bt_enable() { 161 LOGV(__FUNCTION__); 162 163 int ret = -1; 164 int hci_sock = -1; 165 int attempt; 166 167 if (set_bluetooth_power(1) < 0) goto out; 168 169 LOGI("Starting hciattach daemon"); 170 if (property_set("ctl.start", get_hciattach_script()) < 0) { 171 LOGE("Failed to start hciattach"); 172 goto out; 173 } 174 175 // Try for 10 seconds, this can only succeed once hciattach has sent the 176 // firmware and then turned on hci device via HCIUARTSETPROTO ioctl 177 for (attempt = 1000; attempt > 0; attempt--) { 178 hci_sock = create_hci_sock(); 179 if (hci_sock < 0) goto out; 180 181 if (!ioctl(hci_sock, HCIDEVUP, HCI_DEV_ID)) { 182 break; 183 } 184 close(hci_sock); 185 usleep(10000); // 10 ms retry delay 186 } 187 if (attempt == 0) { 188 LOGE("%s: Timeout waiting for HCI device to come up", __FUNCTION__); 189 goto out; 190 } 191 192 LOGI("Starting hcid deamon"); 193 if (property_set("ctl.start", "hcid") < 0) { 194 LOGE("Failed to start hcid"); 195 goto out; 196 } 197 sleep(HCID_START_DELAY_SEC); 198 199 ret = 0; 200 201out: 202 if (hci_sock >= 0) close(hci_sock); 203 return ret; 204} 205 206int bt_disable() { 207 LOGV(__FUNCTION__); 208 209 int ret = -1; 210 int hci_sock = -1; 211 212 LOGI("Stopping hcid deamon"); 213 if (property_set("ctl.stop", "hcid") < 0) { 214 LOGE("Error stopping hcid"); 215 goto out; 216 } 217 usleep(HCID_STOP_DELAY_USEC); 218 219 hci_sock = create_hci_sock(); 220 if (hci_sock < 0) goto out; 221 ioctl(hci_sock, HCIDEVDOWN, HCI_DEV_ID); 222 223 LOGI("Stopping hciattach deamon"); 224 if (property_set("ctl.stop", get_hciattach_script()) < 0) { 225 LOGE("Error stopping hciattach"); 226 goto out; 227 } 228 229 if (set_bluetooth_power(0) < 0) { 230 goto out; 231 } 232 ret = 0; 233 234out: 235 if (hci_sock >= 0) close(hci_sock); 236 return ret; 237} 238 239int bt_is_enabled() { 240 LOGV(__FUNCTION__); 241 242 int hci_sock = -1; 243 int ret = -1; 244 struct hci_dev_info dev_info; 245 246 247 // Check power first 248 ret = check_bluetooth_power(); 249 if (ret == -1 || ret == 0) goto out; 250 251 ret = -1; 252 253 // Power is on, now check if the HCI interface is up 254 hci_sock = create_hci_sock(); 255 if (hci_sock < 0) goto out; 256 257 dev_info.dev_id = HCI_DEV_ID; 258 if (ioctl(hci_sock, HCIGETDEVINFO, (void *)&dev_info) < 0) { 259 ret = 0; 260 goto out; 261 } 262 263 ret = hci_test_bit(HCI_UP, &dev_info.flags); 264 265out: 266 if (hci_sock >= 0) close(hci_sock); 267 return ret; 268} 269