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 <stdlib.h>
22#include <sys/ioctl.h>
23#include <sys/socket.h>
24#include <sys/types.h>
25#include <sys/stat.h>
26
27#include <cutils/log.h>
28#include <cutils/properties.h>
29
30#include <bluetooth/bluetooth.h>
31#include <bluetooth/hci.h>
32#include <bluetooth/hci_lib.h>
33
34#include <bluedroid/bluetooth.h>
35
36#ifndef HCI_DEV_ID
37#define HCI_DEV_ID 0
38#endif
39
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#ifndef BLUETOOTH_DOES_NOT_USE_RFKILL
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            ALOGW("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        ALOGE("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        ALOGE("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        ALOGE("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        ALOGE("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#endif
140
141static inline int create_hci_sock() {
142    int sk = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
143    if (sk < 0) {
144        ALOGE("Failed to create bluetooth hci socket: %s (%d)",
145             strerror(errno), errno);
146    }
147    return sk;
148}
149
150int bt_enable() {
151    ALOGV(__FUNCTION__);
152
153    int ret = -1;
154    int hci_sock = -1;
155    int attempt;
156
157#ifndef BLUETOOTH_DOES_NOT_USE_RFKILL
158    if (set_bluetooth_power(1) < 0) goto out;
159#endif
160
161#ifndef BLUETOOTH_HCIATTACH_USING_PROPERTY
162    ALOGI("Starting hciattach daemon");
163    if (property_set("ctl.start", "hciattach") < 0)
164#else
165    ALOGI("Enable hci tranport");
166    if (property_set("bluetooth.hciattach", "true") < 0)
167#endif
168    {
169        ALOGE("Failed to start hciattach");
170#ifndef BLUETOOTH_DOES_NOT_USE_RFKILL
171        set_bluetooth_power(0);
172#endif
173        goto out;
174    }
175
176    // Try for 10 seconds, this can only succeed once hciattach has sent the
177    // firmware and then turned on hci device via HCIUARTSETPROTO ioctl
178    for (attempt = 100; attempt > 0;  attempt--) {
179        hci_sock = create_hci_sock();
180        if (hci_sock < 0) goto out;
181
182        ret = ioctl(hci_sock, HCIDEVUP, HCI_DEV_ID);
183
184        if (!ret) {
185            break;
186        } else if (errno == EALREADY) {
187            ALOGW("Bluetoothd already started, unexpectedly!");
188            break;
189        }
190
191        ALOGI("%s: ioctl(%d, HCIDEVUP, HCI_DEV_ID) failed: %s (%d)",
192              __FUNCTION__, hci_sock, strerror(errno), errno);
193
194        close(hci_sock);
195        usleep(100 * 1000);  // 100 ms retry delay
196    }
197    if (attempt == 0) {
198        ALOGE("%s: Timeout waiting for HCI device to come up, error- %d, ",
199            __FUNCTION__, ret);
200        if (property_set("ctl.stop", "hciattach") < 0) {
201            ALOGE("Error stopping hciattach");
202        }
203#ifndef BLUETOOTH_DOES_NOT_USE_RFKILL
204        set_bluetooth_power(0);
205#endif
206        goto out;
207    }
208
209    ALOGI("Starting bluetoothd deamon");
210    if (property_set("ctl.start", "bluetoothd") < 0) {
211        ALOGE("Failed to start bluetoothd");
212#ifndef BLUETOOTH_DOES_NOT_USE_RFKILL
213        set_bluetooth_power(0);
214#endif
215        goto out;
216    }
217
218    ret = 0;
219
220out:
221    if (hci_sock >= 0) close(hci_sock);
222    return ret;
223}
224
225int bt_disable() {
226    ALOGV(__FUNCTION__);
227
228    int ret = -1;
229    int hci_sock = -1;
230
231    ALOGI("Stopping bluetoothd deamon");
232    if (property_set("ctl.stop", "bluetoothd") < 0) {
233        ALOGE("Error stopping bluetoothd");
234        goto out;
235    }
236    usleep(HCID_STOP_DELAY_USEC);
237
238    hci_sock = create_hci_sock();
239    if (hci_sock < 0) goto out;
240    ioctl(hci_sock, HCIDEVDOWN, HCI_DEV_ID);
241
242#ifndef BLUETOOTH_HCIATTACH_USING_PROPERTY
243    ALOGI("Stopping hciattach deamon");
244    if (property_set("ctl.stop", "hciattach") < 0)
245#else
246    ALOGI("Disable hci tranport");
247    if (property_set("bluetooth.hciattach", "false") < 0)
248#endif
249    {
250        ALOGE("Error stopping hciattach");
251        goto out;
252    }
253
254#ifndef BLUETOOTH_DOES_NOT_USE_RFKILL
255    if (set_bluetooth_power(0) < 0) {
256        goto out;
257    }
258#endif
259    ret = 0;
260
261out:
262    if (hci_sock >= 0) close(hci_sock);
263    return ret;
264}
265
266int bt_is_enabled() {
267    ALOGV(__FUNCTION__);
268
269    int hci_sock = -1;
270    int ret = -1;
271    struct hci_dev_info dev_info;
272
273
274#ifndef BLUETOOTH_DOES_NOT_USE_RFKILL
275    // Check power first
276    ret = check_bluetooth_power();
277    if (ret == -1 || ret == 0) goto out;
278#endif
279
280    ret = -1;
281
282    // Power is on, now check if the HCI interface is up
283    hci_sock = create_hci_sock();
284    if (hci_sock < 0) goto out;
285
286    dev_info.dev_id = HCI_DEV_ID;
287    if (ioctl(hci_sock, HCIGETDEVINFO, (void *)&dev_info) < 0) {
288        ret = 0;
289        goto out;
290    }
291
292    if (dev_info.flags & (1 << (HCI_UP & 31))) {
293        ret = 1;
294    } else {
295        ret = 0;
296    }
297
298out:
299    if (hci_sock >= 0) close(hci_sock);
300    return ret;
301}
302
303int ba2str(const bdaddr_t *ba, char *str) {
304    return sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
305                ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]);
306}
307
308int str2ba(const char *str, bdaddr_t *ba) {
309    int i;
310    for (i = 5; i >= 0; i--) {
311        ba->b[i] = (uint8_t) strtoul(str, (char **) &str, 16);
312        str++;
313    }
314    return 0;
315}
316