Home | History | Annotate | Download | only in src
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2004-2009  Marcel Holtmann <marcel (at) holtmann.org>
      6  *  Copyright (C) 2009 The Android Open Source Project
      7  *
      8  *  This program is free software; you can redistribute it and/or modify
      9  *  it under the terms of the GNU General Public License as published by
     10  *  the Free Software Foundation; either version 2 of the License, or
     11  *  (at your option) any later version.
     12  *
     13  *  This program is distributed in the hope that it will be useful,
     14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  *  GNU General Public License for more details.
     17  *
     18  *  You should have received a copy of the GNU General Public License
     19  *  along with this program; if not, write to the Free Software
     20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 
     24 #include <unistd.h>
     25 #include <errno.h>
     26 #include <sys/ioctl.h>
     27 #include <sys/types.h>
     28 #include <sys/socket.h>
     29 
     30 #include <private/android_filesystem_config.h>
     31 #include <sys/prctl.h>
     32 #include <linux/capability.h>
     33 
     34 #include <bluetooth/bluetooth.h>
     35 #include <bluetooth/hci.h>
     36 
     37 /* Set UID to bluetooth w/ CAP_NET_RAW, CAP_NET_ADMIN and CAP_NET_BIND_SERVICE
     38  * (Android's init.rc does not yet support applying linux capabilities) */
     39 void android_set_aid_and_cap() {
     40 	prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
     41 	setuid(AID_BLUETOOTH);
     42 
     43 	struct __user_cap_header_struct header;
     44 	struct __user_cap_data_struct cap;
     45 	header.version = _LINUX_CAPABILITY_VERSION;
     46 	header.pid = 0;
     47 	cap.effective = cap.permitted = 1 << CAP_NET_RAW |
     48 					1 << CAP_NET_ADMIN |
     49 					1 << CAP_NET_BIND_SERVICE;
     50 	cap.inheritable = 0;
     51 	capset(&header, &cap);
     52 }
     53 
     54 static int write_flush_timeout(int fd, uint16_t handle,
     55         unsigned int timeout_ms) {
     56     uint16_t timeout = (timeout_ms * 1000) / 625;  // timeout units of 0.625ms
     57     unsigned char hci_write_flush_cmd[] = {
     58         0x01,               // HCI command packet
     59         0x28, 0x0C,         // HCI_Write_Automatic_Flush_Timeout
     60         0x04,               // Length
     61         0x00, 0x00,         // Handle
     62         0x00, 0x00,         // Timeout
     63     };
     64 
     65     hci_write_flush_cmd[4] = (uint8_t)handle;
     66     hci_write_flush_cmd[5] = (uint8_t)(handle >> 8);
     67     hci_write_flush_cmd[6] = (uint8_t)timeout;
     68     hci_write_flush_cmd[7] = (uint8_t)(timeout >> 8);
     69 
     70     int ret = write(fd, hci_write_flush_cmd, sizeof(hci_write_flush_cmd));
     71     if (ret < 0) {
     72         error("write(): %s (%d)]", strerror(errno), errno);
     73         return -1;
     74     } else if (ret != sizeof(hci_write_flush_cmd)) {
     75         error("write(): unexpected length %d", ret);
     76         return -1;
     77     }
     78     return 0;
     79 }
     80 
     81 #ifdef BOARD_HAVE_BLUETOOTH_BCM
     82 static int vendor_high_priority(int fd, uint16_t handle) {
     83     unsigned char hci_sleep_cmd[] = {
     84         0x01,               // HCI command packet
     85         0x57, 0xfc,         // HCI_Write_High_Priority_Connection
     86         0x02,               // Length
     87         0x00, 0x00          // Handle
     88     };
     89 
     90     hci_sleep_cmd[4] = (uint8_t)handle;
     91     hci_sleep_cmd[5] = (uint8_t)(handle >> 8);
     92 
     93     int ret = write(fd, hci_sleep_cmd, sizeof(hci_sleep_cmd));
     94     if (ret < 0) {
     95         error("write(): %s (%d)]", strerror(errno), errno);
     96         return -1;
     97     } else if (ret != sizeof(hci_sleep_cmd)) {
     98         error("write(): unexpected length %d", ret);
     99         return -1;
    100     }
    101     return 0;
    102 }
    103 
    104 static int get_hci_sock() {
    105     int sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
    106     struct sockaddr_hci addr;
    107     int opt;
    108 
    109     if(sock < 0) {
    110         error("Can't create raw HCI socket!");
    111         return -1;
    112     }
    113 
    114     opt = 1;
    115     if (setsockopt(sock, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
    116         error("Error setting data direction\n");
    117         return -1;
    118     }
    119 
    120     /* Bind socket to the HCI device */
    121     memset(&addr, 0, sizeof(addr));
    122     addr.hci_family = AF_BLUETOOTH;
    123     addr.hci_dev = 0;  // hci0
    124     if(bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
    125         error("Can't attach to device hci0. %s(%d)\n",
    126              strerror(errno),
    127              errno);
    128         return -1;
    129     }
    130     return sock;
    131 }
    132 
    133 static int get_acl_handle(int fd, bdaddr_t *bdaddr) {
    134     int i;
    135     int ret = -1;
    136     struct hci_conn_list_req *conn_list;
    137     struct hci_conn_info *conn_info;
    138     int max_conn = 10;
    139 
    140     conn_list = malloc(max_conn * (
    141             sizeof(struct hci_conn_list_req) + sizeof(struct hci_conn_info)));
    142     if (!conn_list) {
    143         error("Out of memory in %s\n", __FUNCTION__);
    144         return -1;
    145     }
    146 
    147     conn_list->dev_id = 0;  /* hardcoded to HCI device 0 */
    148     conn_list->conn_num = max_conn;
    149 
    150     if (ioctl(fd, HCIGETCONNLIST, (void *)conn_list)) {
    151         error("Failed to get connection list\n");
    152         goto out;
    153     }
    154 
    155     for (i=0; i < conn_list->conn_num; i++) {
    156         conn_info = &conn_list->conn_info[i];
    157         if (conn_info->type == ACL_LINK &&
    158                 !memcmp((void *)&conn_info->bdaddr, (void *)bdaddr,
    159                 sizeof(bdaddr_t))) {
    160             ret = conn_info->handle;
    161             goto out;
    162         }
    163     }
    164     ret = 0;
    165 
    166 out:
    167     free(conn_list);
    168     return ret;
    169 }
    170 
    171 /* Request that the ACL link to a given Bluetooth connection be high priority,
    172  * for improved coexistance support
    173  */
    174 int android_set_high_priority(bdaddr_t *ba) {
    175     int ret;
    176     int fd = get_hci_sock();
    177     int acl_handle;
    178 
    179     if (fd < 0)
    180         return fd;
    181 
    182     acl_handle = get_acl_handle(fd, ba);
    183     if (acl_handle < 0) {
    184         ret = acl_handle;
    185         goto out;
    186     }
    187 
    188     ret = vendor_high_priority(fd, acl_handle);
    189     if (ret < 0)
    190         goto out;
    191     ret = write_flush_timeout(fd, acl_handle, 200);
    192 
    193 out:
    194     close(fd);
    195 
    196     return ret;
    197 }
    198 
    199 #else
    200 
    201 int android_set_high_priority(bdaddr_t *ba) {
    202     return 0;
    203 }
    204 
    205 #endif
    206