diff --git a/bluedroid/bluetooth.c b/bluedroid/bluetooth.c new file mode 100644 index 0000000..4f9cdb0 --- /dev/null +++ b/bluedroid/bluetooth.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "bluedroid" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#ifndef HCI_DEV_ID +#define HCI_DEV_ID 0 +#endif + +#define HCID_START_DELAY_SEC 3 +#define HCID_STOP_DELAY_USEC 500000 + +#define MIN(x,y) (((x)<(y))?(x):(y)) + + +static int rfkill_id = -1; +static char *rfkill_state_path = NULL; + + +static int init_rfkill(int id) { + char path[64]; + char buf[16]; + int fd; + int sz; + int rfkill_id; + + snprintf(path, sizeof(path), "/sys/class/rfkill/rfkill%d/type", id); + fd = open(path, O_RDONLY); + if (fd < 0) { + LOGW("open(%s) failed: %s (%d)\n", path, strerror(errno), errno); + return -1; + } + sz = read(fd, &buf, sizeof(buf)); + close(fd); + if (sz >= 9 && memcmp(buf, "bluetooth", 9) == 0) { + rfkill_id = id; + if (rfkill_state_path) + free(rfkill_state_path); + asprintf(&rfkill_state_path, "/sys/class/rfkill/rfkill%d/state", rfkill_id); + } else { + LOGW("/sys/class/rfkill/rfkill%d/state is not owned by bluetooth, ignoring", id); + return -1; + } + return 0; +} + +static int check_bluetooth_power() { + int sz; + int fd = -1; + int ret = -1; + char buffer; + + if (init_rfkill(0)) goto out; + + fd = open(rfkill_state_path, O_RDONLY); + if (fd < 0) { + LOGE("open(%s) failed: %s (%d)", rfkill_state_path, strerror(errno), + errno); + goto out; + } + sz = read(fd, &buffer, 1); + if (sz != 1) { + LOGE("read(%s) failed: %s (%d)", rfkill_state_path, strerror(errno), + errno); + goto out; + } + + switch (buffer) { + case '1': + ret = 1; + break; + case '0': + ret = 0; + break; + } + +out: + if (fd >= 0) close(fd); + return ret; +} + +static int set_bluetooth_power(int on) { + int sz; + int fd = -1; + int ret = -1; + const char buffer = (on ? '1' : '0'); + + if (init_rfkill(0)) goto out; + + fd = open(rfkill_state_path, O_WRONLY); + if (fd < 0) { + LOGE("open(%s) for write failed: %s (%d)", rfkill_state_path, + strerror(errno), errno); + goto out; + } + sz = write(fd, &buffer, 1); + if (sz < 0) { + LOGE("write(%s) failed: %s (%d)", rfkill_state_path, strerror(errno), + errno); + goto out; + } + ret = 0; + +out: + if (fd >= 0) close(fd); + return ret; +} + +/* sleep on means allow sleep */ +static int set_bluetooth_sleep(int on) { + int sz; + int fd = -1; + int ret = -1; + const char buffer = (on ? '1' : '0'); + + if (init_rfkill(1)) goto out; + + fd = open(rfkill_state_path, O_WRONLY); + if (fd < 0) { + LOGE("open(%s) for write failed: %s (%d)", rfkill_state_path, + strerror(errno), errno); + goto out; + } + sz = write(fd, &buffer, 1); + if (sz < 0) { + LOGE("write(%s) failed: %s (%d)", rfkill_state_path, strerror(errno), + errno); + goto out; + } + ret = 0; + +out: + if (fd >= 0) close(fd); + return ret; +} + +static inline int create_hci_sock() { + int sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI); + if (sk < 0) { + LOGE("Failed to create bluetooth hci socket: %s (%d)", + strerror(errno), errno); + } + return sk; +} + +int bt_enable() { + LOGV(__FUNCTION__); + + int ret = -1; + int hci_sock = -1; + int attempt; + + if (set_bluetooth_power(1) < 0) goto out; + + LOGI("Starting hciattach daemon"); + if (property_set("ctl.start", "hciattach") < 0) { + LOGE("Failed to start hciattach"); + set_bluetooth_power(0); + goto out; + } + + // Try for 10 seconds, this can only succeed once hciattach has sent the + // firmware and then turned on hci device via HCIUARTSETPROTO ioctl + for (attempt = 1000; attempt > 0; attempt--) { + hci_sock = create_hci_sock(); + if (hci_sock < 0) goto out; + + if (!ioctl(hci_sock, HCIDEVUP, HCI_DEV_ID)) { + break; + } + close(hci_sock); + usleep(10000); // 10 ms retry delay + } + if (attempt == 0) { + LOGE("%s: Timeout waiting for HCI device to come up", __FUNCTION__); + set_bluetooth_power(0); + goto out; + } + + LOGI("Starting bluetoothd deamon"); + if (property_set("ctl.start", "bluetoothd") < 0) { + LOGE("Failed to start bluetoothd"); + set_bluetooth_power(0); + goto out; + } + sleep(HCID_START_DELAY_SEC); + + if (set_bluetooth_sleep(0) < 0) goto out; + + ret = 0; + +out: + if (hci_sock >= 0) close(hci_sock); + return ret; +} + +int bt_disable() { + LOGV(__FUNCTION__); + + int ret = -1; + int hci_sock = -1; + + if (set_bluetooth_sleep(1) < 0) { + goto out; + } + + LOGI("Stopping bluetoothd deamon"); + if (property_set("ctl.stop", "bluetoothd") < 0) { + LOGE("Error stopping bluetoothd"); + goto out; + } + usleep(HCID_STOP_DELAY_USEC); + + hci_sock = create_hci_sock(); + if (hci_sock < 0) goto out; + ioctl(hci_sock, HCIDEVDOWN, HCI_DEV_ID); + + LOGI("Stopping hciattach deamon"); + if (property_set("ctl.stop", "hciattach") < 0) { + LOGE("Error stopping hciattach"); + goto out; + } + + if (set_bluetooth_power(0) < 0) { + goto out; + } + ret = 0; + +out: + if (hci_sock >= 0) close(hci_sock); + return ret; +} + +int bt_is_enabled() { + LOGV(__FUNCTION__); + + int hci_sock = -1; + int ret = -1; + struct hci_dev_info dev_info; + + + // Check power first + ret = check_bluetooth_power(); + if (ret == -1 || ret == 0) goto out; + + ret = -1; + + // Power is on, now check if the HCI interface is up + hci_sock = create_hci_sock(); + if (hci_sock < 0) goto out; + + dev_info.dev_id = HCI_DEV_ID; + if (ioctl(hci_sock, HCIGETDEVINFO, (void *)&dev_info) < 0) { + ret = 0; + goto out; + } + + ret = hci_test_bit(HCI_UP, &dev_info.flags); + +out: + if (hci_sock >= 0) close(hci_sock); + return ret; +} + +int ba2str(const bdaddr_t *ba, char *str) { + return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]); +} + +int str2ba(const char *str, bdaddr_t *ba) { + int i; + for (i = 5; i >= 0; i--) { + ba->b[i] = (uint8_t) strtoul(str, &str, 16); + str++; + } + return 0; +}