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