bluetooth.c revision cd60add87bf628e96d3a91dc96d4bf945a0ccb17
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        set_bluetooth_power(0);
161        goto out;
162    }
163
164    // Try for 10 seconds, this can only succeed once hciattach has sent the
165    // firmware and then turned on hci device via HCIUARTSETPROTO ioctl
166    for (attempt = 1000; attempt > 0;  attempt--) {
167        hci_sock = create_hci_sock();
168        if (hci_sock < 0) goto out;
169
170        ret = ioctl(hci_sock, HCIDEVUP, HCI_DEV_ID);
171        if (!ret) {
172            break;
173        }
174        close(hci_sock);
175        usleep(10000);  // 10 ms retry delay
176    }
177    if (attempt == 0) {
178        LOGE("%s: Timeout waiting for HCI device to come up, error- %d, ",
179            __FUNCTION__, ret);
180        if (property_set("ctl.stop", "hciattach") < 0) {
181            LOGE("Error stopping hciattach");
182        }
183        set_bluetooth_power(0);
184        goto out;
185    }
186
187    LOGI("Starting bluetoothd deamon");
188    if (property_set("ctl.start", "bluetoothd") < 0) {
189        LOGE("Failed to start bluetoothd");
190        set_bluetooth_power(0);
191        goto out;
192    }
193
194    ret = 0;
195
196out:
197    if (hci_sock >= 0) close(hci_sock);
198    return ret;
199}
200
201int bt_disable() {
202    LOGV(__FUNCTION__);
203
204    int ret = -1;
205    int hci_sock = -1;
206
207    LOGI("Stopping bluetoothd deamon");
208    if (property_set("ctl.stop", "bluetoothd") < 0) {
209        LOGE("Error stopping bluetoothd");
210        goto out;
211    }
212    usleep(HCID_STOP_DELAY_USEC);
213
214    hci_sock = create_hci_sock();
215    if (hci_sock < 0) goto out;
216    ioctl(hci_sock, HCIDEVDOWN, HCI_DEV_ID);
217
218    LOGI("Stopping hciattach deamon");
219    if (property_set("ctl.stop", "hciattach") < 0) {
220        LOGE("Error stopping hciattach");
221        goto out;
222    }
223
224    if (set_bluetooth_power(0) < 0) {
225        goto out;
226    }
227    ret = 0;
228
229out:
230    if (hci_sock >= 0) close(hci_sock);
231    return ret;
232}
233
234int bt_is_enabled() {
235    LOGV(__FUNCTION__);
236
237    int hci_sock = -1;
238    int ret = -1;
239    struct hci_dev_info dev_info;
240
241
242    // Check power first
243    ret = check_bluetooth_power();
244    if (ret == -1 || ret == 0) goto out;
245
246    ret = -1;
247
248    // Power is on, now check if the HCI interface is up
249    hci_sock = create_hci_sock();
250    if (hci_sock < 0) goto out;
251
252    dev_info.dev_id = HCI_DEV_ID;
253    if (ioctl(hci_sock, HCIGETDEVINFO, (void *)&dev_info) < 0) {
254        ret = 0;
255        goto out;
256    }
257
258    if (dev_info.flags & (1 << (HCI_UP & 31))) {
259        ret = 1;
260    } else {
261        ret = 0;
262    }
263
264out:
265    if (hci_sock >= 0) close(hci_sock);
266    return ret;
267}
268
269int ba2str(const bdaddr_t *ba, char *str) {
270    return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
271                ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]);
272}
273
274int str2ba(const char *str, bdaddr_t *ba) {
275    int i;
276    for (i = 5; i >= 0; i--) {
277        ba->b[i] = (uint8_t) strtoul(str, &str, 16);
278        str++;
279    }
280    return 0;
281}
282