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     addr.hci_family = AF_BLUETOOTH;
    122     addr.hci_dev = 0;  // hci0
    123     if(bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
    124         error("Can't attach to device hci0. %s(%d)\n",
    125              strerror(errno),
    126              errno);
    127         return -1;
    128     }
    129     return sock;
    130 }
    131 
    132 static int get_acl_handle(int fd, bdaddr_t *bdaddr) {
    133     int i;
    134     int ret = -1;
    135     struct hci_conn_list_req *conn_list;
    136     struct hci_conn_info *conn_info;
    137     int max_conn = 10;
    138 
    139     conn_list = malloc(max_conn * (
    140             sizeof(struct hci_conn_list_req) + sizeof(struct hci_conn_info)));
    141     if (!conn_list) {
    142         error("Out of memory in %s\n", __FUNCTION__);
    143         return -1;
    144     }
    145 
    146     conn_list->dev_id = 0;  /* hardcoded to HCI device 0 */
    147     conn_list->conn_num = max_conn;
    148 
    149     if (ioctl(fd, HCIGETCONNLIST, (void *)conn_list)) {
    150         error("Failed to get connection list\n");
    151         goto out;
    152     }
    153 
    154     for (i=0; i < conn_list->conn_num; i++) {
    155         conn_info = &conn_list->conn_info[i];
    156         if (conn_info->type == ACL_LINK &&
    157                 !memcmp((void *)&conn_info->bdaddr, (void *)bdaddr,
    158                 sizeof(bdaddr_t))) {
    159             ret = conn_info->handle;
    160             goto out;
    161         }
    162     }
    163     ret = 0;
    164 
    165 out:
    166     free(conn_list);
    167     return ret;
    168 }
    169 
    170 /* Request that the ACL link to a given Bluetooth connection be high priority,
    171  * for improved coexistance support
    172  */
    173 int android_set_high_priority(bdaddr_t *ba) {
    174     int ret;
    175     int fd = get_hci_sock();
    176     int acl_handle;
    177 
    178     if (fd < 0)
    179         return fd;
    180 
    181     acl_handle = get_acl_handle(fd, ba);
    182     if (acl_handle < 0) {
    183         ret = acl_handle;
    184         goto out;
    185     }
    186 
    187     ret = vendor_high_priority(fd, acl_handle);
    188     if (ret < 0)
    189         goto out;
    190     ret = write_flush_timeout(fd, acl_handle, 200);
    191 
    192 out:
    193     close(fd);
    194 
    195     return ret;
    196 }
    197 
    198 #else
    199 
    200 int android_set_high_priority(bdaddr_t *ba) {
    201     return 0;
    202 }
    203 
    204 #endif
    205