bluetooth.c revision 2dbc46f687f9329695156133fc9feefc750d1526
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_STOP_DELAY_USEC 500000
40
41#define MIN(x,y) (((x)<(y))?(x):(y))
42
43
44static int rfkill_id = -1;
45static char *rfkill_state_path = NULL;
46
47
48static int init_rfkill() {
49    char path[64];
50    char buf[16];
51    int fd;
52    int sz;
53    int id;
54    for (id = 0; ; id++) {
55        snprintf(path, sizeof(path), "/sys/class/rfkill/rfkill%d/type", id);
56        fd = open(path, O_RDONLY);
57        if (fd < 0) {
58            LOGW("open(%s) failed: %s (%d)\n", path, strerror(errno), errno);
59            return -1;
60        }
61        sz = read(fd, &buf, sizeof(buf));
62        close(fd);
63        if (sz >= 9 && memcmp(buf, "bluetooth", 9) == 0) {
64            rfkill_id = id;
65            break;
66        }
67    }
68
69    asprintf(&rfkill_state_path, "/sys/class/rfkill/rfkill%d/state", rfkill_id);
70    return 0;
71}
72
73static int check_bluetooth_power() {
74    int sz;
75    int fd = -1;
76    int ret = -1;
77    char buffer;
78
79    if (rfkill_id == -1) {
80        if (init_rfkill()) goto out;
81    }
82
83    fd = open(rfkill_state_path, O_RDONLY);
84    if (fd < 0) {
85        LOGE("open(%s) failed: %s (%d)", rfkill_state_path, strerror(errno),
86             errno);
87        goto out;
88    }
89    sz = read(fd, &buffer, 1);
90    if (sz != 1) {
91        LOGE("read(%s) failed: %s (%d)", rfkill_state_path, strerror(errno),
92             errno);
93        goto out;
94    }
95
96    switch (buffer) {
97    case '1':
98        ret = 1;
99        break;
100    case '0':
101        ret = 0;
102        break;
103    }
104
105out:
106    if (fd >= 0) close(fd);
107    return ret;
108}
109
110static int set_bluetooth_power(int on) {
111    int sz;
112    int fd = -1;
113    int ret = -1;
114    const char buffer = (on ? '1' : '0');
115
116    if (rfkill_id == -1) {
117        if (init_rfkill()) goto out;
118    }
119
120    fd = open(rfkill_state_path, O_WRONLY);
121    if (fd < 0) {
122        LOGE("open(%s) for write failed: %s (%d)", rfkill_state_path,
123             strerror(errno), errno);
124        goto out;
125    }
126    sz = write(fd, &buffer, 1);
127    if (sz < 0) {
128        LOGE("write(%s) failed: %s (%d)", rfkill_state_path, strerror(errno),
129             errno);
130        goto out;
131    }
132    ret = 0;
133
134out:
135    if (fd >= 0) close(fd);
136    return ret;
137}
138
139static inline int create_hci_sock() {
140    int sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
141    if (sk < 0) {
142        LOGE("Failed to create bluetooth hci socket: %s (%d)",
143             strerror(errno), errno);
144    }
145    return sk;
146}
147
148int bt_enable() {
149    LOGV(__FUNCTION__);
150
151    int ret = -1;
152    int hci_sock = -1;
153    int attempt;
154
155    if (set_bluetooth_power(1) < 0) goto out;
156
157    LOGI("Starting hciattach daemon");
158    if (property_set("ctl.start", "hciattach") < 0) {
159        LOGE("Failed to start hciattach");
160        goto out;
161    }
162
163    // Try for 10 seconds, this can only succeed once hciattach has sent the
164    // firmware and then turned on hci device via HCIUARTSETPROTO ioctl
165    for (attempt = 1000; attempt > 0;  attempt--) {
166        hci_sock = create_hci_sock();
167        if (hci_sock < 0) goto out;
168
169        if (!ioctl(hci_sock, HCIDEVUP, HCI_DEV_ID)) {
170            break;
171        }
172        close(hci_sock);
173        usleep(10000);  // 10 ms retry delay
174    }
175    if (attempt == 0) {
176        LOGE("%s: Timeout waiting for HCI device to come up", __FUNCTION__);
177        goto out;
178    }
179
180    LOGI("Starting bluetoothd deamon");
181    if (property_set("ctl.start", "bluetoothd") < 0) {
182        LOGE("Failed to start bluetoothd");
183        goto out;
184    }
185
186    ret = 0;
187
188out:
189    if (hci_sock >= 0) close(hci_sock);
190    return ret;
191}
192
193int bt_disable() {
194    LOGV(__FUNCTION__);
195
196    int ret = -1;
197    int hci_sock = -1;
198
199    LOGI("Stopping bluetoothd deamon");
200    if (property_set("ctl.stop", "bluetoothd") < 0) {
201        LOGE("Error stopping bluetoothd");
202        goto out;
203    }
204    usleep(HCID_STOP_DELAY_USEC);
205
206    hci_sock = create_hci_sock();
207    if (hci_sock < 0) goto out;
208    ioctl(hci_sock, HCIDEVDOWN, HCI_DEV_ID);
209
210    LOGI("Stopping hciattach deamon");
211    if (property_set("ctl.stop", "hciattach") < 0) {
212        LOGE("Error stopping hciattach");
213        goto out;
214    }
215
216    if (set_bluetooth_power(0) < 0) {
217        goto out;
218    }
219    ret = 0;
220
221out:
222    if (hci_sock >= 0) close(hci_sock);
223    return ret;
224}
225
226int bt_is_enabled() {
227    LOGV(__FUNCTION__);
228
229    int hci_sock = -1;
230    int ret = -1;
231    struct hci_dev_info dev_info;
232
233
234    // Check power first
235    ret = check_bluetooth_power();
236    if (ret == -1 || ret == 0) goto out;
237
238    ret = -1;
239
240    // Power is on, now check if the HCI interface is up
241    hci_sock = create_hci_sock();
242    if (hci_sock < 0) goto out;
243
244    dev_info.dev_id = HCI_DEV_ID;
245    if (ioctl(hci_sock, HCIGETDEVINFO, (void *)&dev_info) < 0) {
246        ret = 0;
247        goto out;
248    }
249
250    ret = hci_test_bit(HCI_UP, &dev_info.flags);
251
252out:
253    if (hci_sock >= 0) close(hci_sock);
254    return ret;
255}
256
257int ba2str(const bdaddr_t *ba, char *str) {
258    return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
259                ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]);
260}
261
262int str2ba(const char *str, bdaddr_t *ba) {
263    int i;
264    for (i = 5; i >= 0; i--) {
265        ba->b[i] = (uint8_t) strtoul(str, &str, 16);
266        str++;
267    }
268    return 0;
269}
270