1/****************************************************************************** 2 * 3 * Copyright (C) 2014 Google, Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at: 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 ******************************************************************************/ 18 19#define LOG_TAG "bt_btif_sock_sco" 20 21#include <assert.h> 22#include <errno.h> 23#include <hardware/bluetooth.h> 24#include <hardware/bt_sock.h> 25#include <string.h> 26#include <sys/socket.h> 27#include <sys/types.h> 28 29#include "btif_common.h" 30#include "osi/include/allocator.h" 31#include "osi/include/list.h" 32#include "osi/include/osi.h" 33#include "osi/include/log.h" 34#include "osi/include/socket.h" 35#include "osi/include/thread.h" 36 37// This module provides a socket abstraction for SCO connections to a higher 38// layer. It returns file descriptors representing two types of sockets: 39// listening (server) and connected (client) sockets. No SCO data is 40// transferred across these sockets; instead, they are used to manage SCO 41// connection lifecycles while the data routing takes place over the I2S bus. 42// 43// This code bridges the gap between the BTM layer, which implements SCO 44// connections, and the Android HAL. It adapts the BTM representation of SCO 45// connections (integer handles) to a file descriptor representation usable by 46// Android's LocalSocket implementation. 47// 48// Sample flow for an incoming connection: 49// btsock_sco_listen() - listen for incoming connections 50// connection_request_cb() - incoming connection request from remote host 51// connect_completed_cb() - connection successfully established 52// socket_read_ready_cb() - local host closed SCO socket 53// disconnect_completed_cb() - connection terminated 54 55typedef struct { 56 uint16_t sco_handle; 57 socket_t *socket; 58 bool connect_completed; 59} sco_socket_t; 60 61// TODO: verify packet types that are being sent OTA. 62static tBTM_ESCO_PARAMS sco_parameters = { 63 BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */ 64 BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */ 65 0x000a, /* 10 ms (HS/HF can use EV3, 2-EV3, 3-EV3) */ 66 0x0060, /* Inp Linear, Air CVSD, 2s Comp, 16bit */ 67 (BTM_SCO_LINK_ALL_PKT_MASK | 68 BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | 69 BTM_SCO_PKT_TYPES_MASK_NO_3_EV5), 70 BTM_ESCO_RETRANS_POWER /* Retransmission effort */ 71}; 72 73static sco_socket_t *sco_socket_establish_locked(bool is_listening, const bt_bdaddr_t *bd_addr, int *sock_fd); 74static sco_socket_t *sco_socket_new(void); 75static void sco_socket_free_locked(sco_socket_t *socket); 76static sco_socket_t *sco_socket_find_locked(uint16_t sco_handle); 77static void connection_request_cb(tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *data); 78static void connect_completed_cb(uint16_t sco_handle); 79static void disconnect_completed_cb(uint16_t sco_handle); 80static void socket_read_ready_cb(socket_t *socket, void *context); 81 82// |lock| protects all of the static variables below and 83// calls into the BTM layer. 84static pthread_mutex_t lock; 85static list_t *sco_sockets; // Owns a collection of sco_socket_t objects. 86static sco_socket_t *listen_sco_socket; // Not owned, do not free. 87static thread_t *thread; // Not owned, do not free. 88 89bt_status_t btsock_sco_init(thread_t *thread_) { 90 assert(thread_ != NULL); 91 92 sco_sockets = list_new((list_free_cb)sco_socket_free_locked); 93 if (!sco_sockets) 94 return BT_STATUS_FAIL; 95 96 pthread_mutex_init(&lock, NULL); 97 98 thread = thread_; 99 BTM_SetEScoMode(BTM_LINK_TYPE_ESCO, &sco_parameters); 100 101 return BT_STATUS_SUCCESS; 102} 103 104bt_status_t btsock_sco_cleanup(void) { 105 list_free(sco_sockets); 106 sco_sockets = NULL; 107 pthread_mutex_destroy(&lock); 108 return BT_STATUS_SUCCESS; 109} 110 111bt_status_t btsock_sco_listen(int *sock_fd, UNUSED_ATTR int flags) { 112 assert(sock_fd != NULL); 113 114 pthread_mutex_lock(&lock); 115 116 sco_socket_t *sco_socket = sco_socket_establish_locked(true, NULL, sock_fd); 117 if (sco_socket) { 118 BTM_RegForEScoEvts(sco_socket->sco_handle, connection_request_cb); 119 listen_sco_socket = sco_socket; 120 } 121 122 pthread_mutex_unlock(&lock); 123 124 return sco_socket ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; 125} 126 127bt_status_t btsock_sco_connect(const bt_bdaddr_t *bd_addr, int *sock_fd, UNUSED_ATTR int flags) { 128 assert(bd_addr != NULL); 129 assert(sock_fd != NULL); 130 131 pthread_mutex_lock(&lock); 132 sco_socket_t *sco_socket = sco_socket_establish_locked(false, bd_addr, sock_fd); 133 pthread_mutex_unlock(&lock); 134 135 return (sco_socket != NULL) ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; 136} 137 138// Must be called with |lock| held. 139static sco_socket_t *sco_socket_establish_locked(bool is_listening, const bt_bdaddr_t *bd_addr, int *sock_fd) { 140 int pair[2] = { INVALID_FD, INVALID_FD }; 141 sco_socket_t *sco_socket = NULL; 142 143 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pair) == -1) { 144 LOG_ERROR("%s unable to allocate socket pair: %s", __func__, strerror(errno)); 145 goto error; 146 } 147 148 sco_socket = sco_socket_new(); 149 if (!sco_socket) { 150 LOG_ERROR("%s unable to allocate new SCO socket.", __func__); 151 goto error; 152 } 153 154 tBTM_STATUS status = BTM_CreateSco((uint8_t *)bd_addr, !is_listening, sco_parameters.packet_types, &sco_socket->sco_handle, connect_completed_cb, disconnect_completed_cb); 155 if (status != BTM_CMD_STARTED) { 156 LOG_ERROR("%s unable to create SCO socket: %d", __func__, status); 157 goto error; 158 } 159 160 socket_t *socket = socket_new_from_fd(pair[1]); 161 if (!socket) { 162 LOG_ERROR("%s unable to allocate socket from file descriptor %d.", __func__, pair[1]); 163 goto error; 164 } 165 166 *sock_fd = pair[0]; // Transfer ownership of one end to caller. 167 sco_socket->socket = socket; // Hang on to the other end. 168 list_append(sco_sockets, sco_socket); 169 170 socket_register(socket, thread_get_reactor(thread), sco_socket, socket_read_ready_cb, NULL); 171 return sco_socket; 172 173error:; 174 if (pair[0] != INVALID_FD) 175 close(pair[0]); 176 if (pair[1] != INVALID_FD) 177 close(pair[1]); 178 179 sco_socket_free_locked(sco_socket); 180 return NULL; 181} 182 183static sco_socket_t *sco_socket_new(void) { 184 sco_socket_t *sco_socket = (sco_socket_t *)osi_calloc(sizeof(sco_socket_t)); 185 if (sco_socket) 186 sco_socket->sco_handle = BTM_INVALID_SCO_INDEX; 187 return sco_socket; 188} 189 190// Must be called with |lock| held except during teardown when we know the socket thread 191// is no longer alive. 192static void sco_socket_free_locked(sco_socket_t *sco_socket) { 193 if (!sco_socket) 194 return; 195 196 if (sco_socket->sco_handle != BTM_INVALID_SCO_INDEX) 197 BTM_RemoveSco(sco_socket->sco_handle); 198 socket_free(sco_socket->socket); 199 osi_free(sco_socket); 200} 201 202// Must be called with |lock| held. 203static sco_socket_t *sco_socket_find_locked(uint16_t sco_handle) { 204 for (const list_node_t *node = list_begin(sco_sockets); node != list_end(sco_sockets); node = list_next(node)) { 205 sco_socket_t *sco_socket = (sco_socket_t *)list_node(node); 206 if (sco_socket->sco_handle == sco_handle) 207 return sco_socket; 208 } 209 return NULL; 210} 211 212static void connection_request_cb(tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *data) { 213 assert(data != NULL); 214 215 // Don't care about change of link parameters, only connection requests. 216 if (event != BTM_ESCO_CONN_REQ_EVT) 217 return; 218 219 pthread_mutex_lock(&lock); 220 221 const tBTM_ESCO_CONN_REQ_EVT_DATA *conn_data = &data->conn_evt; 222 sco_socket_t *sco_socket = sco_socket_find_locked(conn_data->sco_inx); 223 int client_fd = INVALID_FD; 224 225 if (!sco_socket) { 226 LOG_ERROR("%s unable to find sco_socket for handle: %hu", __func__, conn_data->sco_inx); 227 goto error; 228 } 229 230 if (sco_socket != listen_sco_socket) { 231 LOG_ERROR("%s received connection request on non-listening socket handle: %hu", __func__, conn_data->sco_inx); 232 goto error; 233 } 234 235 sco_socket_t *new_sco_socket = sco_socket_establish_locked(true, NULL, &client_fd); 236 if (!new_sco_socket) { 237 LOG_ERROR("%s unable to allocate new sco_socket.", __func__); 238 goto error; 239 } 240 241 // Swap socket->sco_handle and new_socket->sco_handle 242 uint16_t temp = sco_socket->sco_handle; 243 sco_socket->sco_handle = new_sco_socket->sco_handle; 244 new_sco_socket->sco_handle = temp; 245 246 sock_connect_signal_t connect_signal; 247 connect_signal.size = sizeof(connect_signal); 248 memcpy(&connect_signal.bd_addr, conn_data->bd_addr, sizeof(bt_bdaddr_t)); 249 connect_signal.channel = 0; 250 connect_signal.status = 0; 251 252 if (socket_write_and_transfer_fd(sco_socket->socket, &connect_signal, sizeof(connect_signal), client_fd) != sizeof(connect_signal)) { 253 LOG_ERROR("%s unable to send new file descriptor to listening socket.", __func__); 254 goto error; 255 } 256 257 BTM_RegForEScoEvts(listen_sco_socket->sco_handle, connection_request_cb); 258 BTM_EScoConnRsp(conn_data->sco_inx, HCI_SUCCESS, NULL); 259 260 pthread_mutex_unlock(&lock); 261 return; 262 263error:; 264 pthread_mutex_unlock(&lock); 265 266 if (client_fd != INVALID_FD) 267 close(client_fd); 268 BTM_EScoConnRsp(conn_data->sco_inx, HCI_ERR_HOST_REJECT_RESOURCES, NULL); 269} 270 271static void connect_completed_cb(uint16_t sco_handle) { 272 pthread_mutex_lock(&lock); 273 274 sco_socket_t *sco_socket = sco_socket_find_locked(sco_handle); 275 if (!sco_socket) { 276 LOG_ERROR("%s SCO socket not found on connect for handle: %hu", __func__, sco_handle); 277 goto out; 278 } 279 280 // If sco_socket->socket was closed, we should tear down because there is no app-level 281 // interest in the SCO socket. 282 if (!sco_socket->socket) { 283 BTM_RemoveSco(sco_socket->sco_handle); 284 list_remove(sco_sockets, sco_socket); 285 goto out; 286 } 287 288 sco_socket->connect_completed = true; 289 290out:; 291 pthread_mutex_unlock(&lock); 292} 293 294static void disconnect_completed_cb(uint16_t sco_handle) { 295 pthread_mutex_lock(&lock); 296 297 sco_socket_t *sco_socket = sco_socket_find_locked(sco_handle); 298 if (!sco_socket) { 299 LOG_ERROR("%s SCO socket not found on disconnect for handle: %hu", __func__, sco_handle); 300 goto out; 301 } 302 303 list_remove(sco_sockets, sco_socket); 304 305out:; 306 pthread_mutex_unlock(&lock); 307} 308 309static void socket_read_ready_cb(UNUSED_ATTR socket_t *socket, void *context) { 310 pthread_mutex_lock(&lock); 311 312 sco_socket_t *sco_socket = (sco_socket_t *)context; 313 socket_free(sco_socket->socket); 314 sco_socket->socket = NULL; 315 316 // Defer the underlying disconnect until the connection completes 317 // since the BTM code doesn't behave correctly when a disconnect 318 // request is issued while a connect is in progress. The fact that 319 // sco_socket->socket == NULL indicates to the connect callback 320 // routine that the socket is no longer desired and should be torn 321 // down. 322 if (sco_socket->connect_completed || sco_socket == listen_sco_socket) { 323 if (BTM_RemoveSco(sco_socket->sco_handle) == BTM_SUCCESS) 324 list_remove(sco_sockets, sco_socket); 325 if (sco_socket == listen_sco_socket) 326 listen_sco_socket = NULL; 327 } 328 329 pthread_mutex_unlock(&lock); 330} 331