• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************
2  *
3  *  Copyright 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_l2cap_client"
20 
21 #include "stack/include/l2cap_client.h"
22 
23 #include <base/logging.h>
24 #include <string.h>
25 
26 #include "osi/include/allocator.h"
27 #include "osi/include/buffer.h"
28 #include "osi/include/list.h"
29 #include "osi/include/log.h"
30 #include "osi/include/osi.h"
31 #include "stack/include/l2c_api.h"
32 
33 struct l2cap_client_t {
34   l2cap_client_callbacks_t callbacks;
35   void* context;
36 
37   uint16_t local_channel_id;
38   uint16_t remote_mtu;
39   bool configured_self;
40   bool configured_peer;
41   bool is_congested;
42   list_t* outbound_fragments;
43 };
44 
45 static void connect_completed_cb(uint16_t local_channel_id,
46                                  uint16_t error_code);
47 static void config_request_cb(uint16_t local_channel_id,
48                               tL2CAP_CFG_INFO* requested_parameters);
49 static void config_completed_cb(uint16_t local_channel_id,
50                                 tL2CAP_CFG_INFO* negotiated_parameters);
51 static void disconnect_request_cb(uint16_t local_channel_id, bool ack_required);
52 static void disconnect_completed_cb(uint16_t local_channel_id,
53                                     uint16_t error_code);
54 static void congestion_cb(uint16_t local_channel_id, bool is_congested);
55 static void read_ready_cb(uint16_t local_channel_id, BT_HDR* packet);
56 static void write_completed_cb(uint16_t local_channel_id,
57                                uint16_t packets_completed);
58 
59 static void fragment_packet(l2cap_client_t* client, buffer_t* packet);
60 static void dispatch_fragments(l2cap_client_t* client);
61 static l2cap_client_t* find(uint16_t local_channel_id);
62 
63 // From the Bluetooth Core specification.
64 static const uint16_t L2CAP_MTU_DEFAULT = 672;
65 static const uint16_t L2CAP_MTU_MINIMUM = 48;
66 
67 static const tL2CAP_APPL_INFO l2cap_callbacks = {
68     .pL2CA_ConnectCfm_Cb = connect_completed_cb,
69     .pL2CA_ConfigInd_Cb = config_request_cb,
70     .pL2CA_ConfigCfm_Cb = config_completed_cb,
71     .pL2CA_DisconnectInd_Cb = disconnect_request_cb,
72     .pL2CA_DisconnectCfm_Cb = disconnect_completed_cb,
73     .pL2CA_CongestionStatus_Cb = congestion_cb,
74     .pL2CA_DataInd_Cb = read_ready_cb,
75     .pL2CA_TxComplete_Cb = write_completed_cb,
76 };
77 
78 static list_t*
79     l2cap_clients;  // A list of l2cap_client_t. Container does not own objects.
80 
l2cap_buffer_new(size_t size)81 buffer_t* l2cap_buffer_new(size_t size) {
82   buffer_t* buf = buffer_new(size + L2CAP_MIN_OFFSET);
83   buffer_t* slice = NULL;
84   if (buf) slice = buffer_new_slice(buf, size);
85   buffer_free(buf);
86   return slice;
87 }
88 
l2cap_client_new(const l2cap_client_callbacks_t * callbacks,void * context)89 l2cap_client_t* l2cap_client_new(const l2cap_client_callbacks_t* callbacks,
90                                  void* context) {
91   CHECK(callbacks != NULL);
92   CHECK(callbacks->connected != NULL);
93   CHECK(callbacks->disconnected != NULL);
94   CHECK(callbacks->read_ready != NULL);
95   CHECK(callbacks->write_ready != NULL);
96 
97   if (!l2cap_clients) {
98     l2cap_clients = list_new(NULL);
99     if (!l2cap_clients) {
100       LOG_ERROR(LOG_TAG, "%s unable to allocate space for L2CAP client list.",
101                 __func__);
102       return NULL;
103     }
104   }
105 
106   l2cap_client_t* ret = (l2cap_client_t*)osi_calloc(sizeof(l2cap_client_t));
107 
108   ret->callbacks = *callbacks;
109   ret->context = context;
110 
111   ret->remote_mtu = L2CAP_MTU_DEFAULT;
112   ret->outbound_fragments = list_new(NULL);
113 
114   list_append(l2cap_clients, ret);
115 
116   return ret;
117 }
118 
l2cap_client_free(l2cap_client_t * client)119 void l2cap_client_free(l2cap_client_t* client) {
120   if (!client) return;
121 
122   list_remove(l2cap_clients, client);
123   l2cap_client_disconnect(client);
124   list_free(client->outbound_fragments);
125   osi_free(client);
126 }
127 
l2cap_client_connect(l2cap_client_t * client,const RawAddress & remote_bdaddr,uint16_t psm)128 bool l2cap_client_connect(l2cap_client_t* client,
129                           const RawAddress& remote_bdaddr, uint16_t psm) {
130   CHECK(client != NULL);
131   CHECK(psm != 0);
132   CHECK(!remote_bdaddr.IsEmpty());
133   CHECK(client->local_channel_id == 0);
134   CHECK(!client->configured_self);
135   CHECK(!client->configured_peer);
136   CHECK(!L2C_INVALID_PSM(psm));
137 
138   client->local_channel_id = L2CA_ConnectReq(psm, remote_bdaddr);
139   if (!client->local_channel_id) {
140     LOG_ERROR(LOG_TAG, "%s unable to create L2CAP connection.", __func__);
141     return false;
142   }
143 
144   L2CA_SetConnectionCallbacks(client->local_channel_id, &l2cap_callbacks);
145   return true;
146 }
147 
l2cap_client_disconnect(l2cap_client_t * client)148 void l2cap_client_disconnect(l2cap_client_t* client) {
149   CHECK(client != NULL);
150 
151   if (client->local_channel_id && !L2CA_DisconnectReq(client->local_channel_id))
152     LOG_ERROR(LOG_TAG, "%s unable to send disconnect message for LCID 0x%04x.",
153               __func__, client->local_channel_id);
154 
155   client->local_channel_id = 0;
156   client->remote_mtu = L2CAP_MTU_DEFAULT;
157   client->configured_self = false;
158   client->configured_peer = false;
159   client->is_congested = false;
160 
161   for (const list_node_t* node = list_begin(client->outbound_fragments);
162        node != list_end(client->outbound_fragments); node = list_next(node))
163     osi_free(list_node(node));
164 
165   list_clear(client->outbound_fragments);
166 }
167 
l2cap_client_is_connected(const l2cap_client_t * client)168 bool l2cap_client_is_connected(const l2cap_client_t* client) {
169   CHECK(client != NULL);
170 
171   return client->local_channel_id != 0 && client->configured_self &&
172          client->configured_peer;
173 }
174 
l2cap_client_write(l2cap_client_t * client,buffer_t * packet)175 bool l2cap_client_write(l2cap_client_t* client, buffer_t* packet) {
176   CHECK(client != NULL);
177   CHECK(packet != NULL);
178   CHECK(l2cap_client_is_connected(client));
179 
180   if (client->is_congested) return false;
181 
182   fragment_packet(client, packet);
183   dispatch_fragments(client);
184   return true;
185 }
186 
connect_completed_cb(uint16_t local_channel_id,uint16_t error_code)187 static void connect_completed_cb(uint16_t local_channel_id,
188                                  uint16_t error_code) {
189   CHECK(local_channel_id != 0);
190 
191   l2cap_client_t* client = find(local_channel_id);
192   if (!client) {
193     LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client for LCID 0x%04x.",
194               __func__, local_channel_id);
195     return;
196   }
197 
198   if (error_code != L2CAP_CONN_OK) {
199     LOG_ERROR(LOG_TAG, "%s error connecting L2CAP channel: %d.", __func__,
200               error_code);
201     client->callbacks.disconnected(client, client->context);
202     return;
203   }
204 
205   // Use default L2CAP parameters.
206   tL2CAP_CFG_INFO desired_parameters;
207   memset(&desired_parameters, 0, sizeof(desired_parameters));
208   if (!L2CA_ConfigReq(local_channel_id, &desired_parameters)) {
209     LOG_ERROR(LOG_TAG, "%s error sending L2CAP config parameters.", __func__);
210     client->callbacks.disconnected(client, client->context);
211   }
212 }
213 
config_request_cb(uint16_t local_channel_id,tL2CAP_CFG_INFO * requested_parameters)214 static void config_request_cb(uint16_t local_channel_id,
215                               tL2CAP_CFG_INFO* requested_parameters) {
216   tL2CAP_CFG_INFO response;
217   l2cap_client_t* client = find(local_channel_id);
218 
219   if (!client) {
220     LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client matching LCID 0x%04x.",
221               __func__, local_channel_id);
222     return;
223   }
224 
225   memset(&response, 0, sizeof(response));
226   response.result = L2CAP_CFG_OK;
227 
228   if (requested_parameters->mtu_present) {
229     // Make sure the peer chose an MTU at least as large as the minimum L2CAP
230     // MTU defined by the Bluetooth Core spec.
231     if (requested_parameters->mtu < L2CAP_MTU_MINIMUM) {
232       response.mtu = L2CAP_MTU_MINIMUM;
233       response.mtu_present = true;
234       response.result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
235     } else {
236       client->remote_mtu = requested_parameters->mtu;
237     }
238   }
239 
240   if (requested_parameters->fcr_present) {
241     if (requested_parameters->fcr.mode != L2CAP_FCR_BASIC_MODE) {
242       response.fcr_present = true;
243       response.fcr = requested_parameters->fcr;
244       response.fcr.mode = L2CAP_FCR_BASIC_MODE;
245       response.result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
246     }
247   }
248 
249   if (!L2CA_ConfigRsp(local_channel_id, &response)) {
250     LOG_ERROR(LOG_TAG, "%s unable to send config response for LCID 0x%04x.",
251               __func__, local_channel_id);
252     l2cap_client_disconnect(client);
253     return;
254   }
255 
256   // If we've configured both endpoints, let the listener know we've connected.
257   client->configured_peer = true;
258   if (l2cap_client_is_connected(client))
259     client->callbacks.connected(client, client->context);
260 }
261 
config_completed_cb(uint16_t local_channel_id,tL2CAP_CFG_INFO * negotiated_parameters)262 static void config_completed_cb(uint16_t local_channel_id,
263                                 tL2CAP_CFG_INFO* negotiated_parameters) {
264   l2cap_client_t* client = find(local_channel_id);
265 
266   if (!client) {
267     LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client matching LCID 0x%04x.",
268               __func__, local_channel_id);
269     return;
270   }
271 
272   switch (negotiated_parameters->result) {
273     // We'll get another configuration response later.
274     case L2CAP_CFG_PENDING:
275       break;
276 
277     case L2CAP_CFG_UNACCEPTABLE_PARAMS:
278       // TODO: see if we can renegotiate parameters instead of dropping the
279       // connection.
280       LOG_WARN(
281           LOG_TAG,
282           "%s dropping L2CAP connection due to unacceptable config parameters.",
283           __func__);
284       l2cap_client_disconnect(client);
285       break;
286 
287     case L2CAP_CFG_OK:
288       // If we've configured both endpoints, let the listener know we've
289       // connected.
290       client->configured_self = true;
291       if (l2cap_client_is_connected(client))
292         client->callbacks.connected(client, client->context);
293       break;
294 
295     // Failure, no further parameter negotiation possible.
296     default:
297       LOG_WARN(LOG_TAG,
298                "%s L2CAP parameter negotiation failed with error code %d.",
299                __func__, negotiated_parameters->result);
300       l2cap_client_disconnect(client);
301       break;
302   }
303 }
304 
disconnect_request_cb(uint16_t local_channel_id,bool ack_required)305 static void disconnect_request_cb(uint16_t local_channel_id,
306                                   bool ack_required) {
307   l2cap_client_t* client = find(local_channel_id);
308   if (!client) {
309     LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client with LCID 0x%04x.",
310               __func__, local_channel_id);
311     return;
312   }
313 
314   if (ack_required) L2CA_DisconnectRsp(local_channel_id);
315 
316   // We already sent a disconnect response so this LCID is now invalid.
317   client->local_channel_id = 0;
318   l2cap_client_disconnect(client);
319 
320   client->callbacks.disconnected(client, client->context);
321 }
322 
disconnect_completed_cb(uint16_t local_channel_id,UNUSED_ATTR uint16_t error_code)323 static void disconnect_completed_cb(uint16_t local_channel_id,
324                                     UNUSED_ATTR uint16_t error_code) {
325   CHECK(local_channel_id != 0);
326 
327   l2cap_client_t* client = find(local_channel_id);
328   if (!client) {
329     LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client with LCID 0x%04x.",
330               __func__, local_channel_id);
331     return;
332   }
333 
334   client->local_channel_id = 0;
335   l2cap_client_disconnect(client);
336 
337   client->callbacks.disconnected(client, client->context);
338 }
339 
congestion_cb(uint16_t local_channel_id,bool is_congested)340 static void congestion_cb(uint16_t local_channel_id, bool is_congested) {
341   CHECK(local_channel_id != 0);
342 
343   l2cap_client_t* client = find(local_channel_id);
344   if (!client) {
345     LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client matching LCID 0x%04x.",
346               __func__, local_channel_id);
347     return;
348   }
349 
350   client->is_congested = is_congested;
351 
352   if (!is_congested) {
353     // If we just decongested, dispatch whatever we have left over in our queue.
354     // Once that's done, if we're still decongested, notify the listener so it
355     // can start writing again.
356     dispatch_fragments(client);
357     if (!client->is_congested)
358       client->callbacks.write_ready(client, client->context);
359   }
360 }
361 
read_ready_cb(uint16_t local_channel_id,BT_HDR * packet)362 static void read_ready_cb(uint16_t local_channel_id, BT_HDR* packet) {
363   CHECK(local_channel_id != 0);
364 
365   l2cap_client_t* client = find(local_channel_id);
366   if (!client) {
367     LOG_ERROR(LOG_TAG, "%s unable to find L2CAP client matching LCID 0x%04x.",
368               __func__, local_channel_id);
369     return;
370   }
371 
372   // TODO(sharvil): eliminate copy from BT_HDR.
373   buffer_t* buffer = buffer_new(packet->len);
374   memcpy(buffer_ptr(buffer), packet->data + packet->offset, packet->len);
375   osi_free(packet);
376 
377   client->callbacks.read_ready(client, buffer, client->context);
378   buffer_free(buffer);
379 }
380 
write_completed_cb(UNUSED_ATTR uint16_t local_channel_id,UNUSED_ATTR uint16_t packets_completed)381 static void write_completed_cb(UNUSED_ATTR uint16_t local_channel_id,
382                                UNUSED_ATTR uint16_t packets_completed) {
383   // Do nothing. We update congestion state based on the congestion callback
384   // and we've already removed items from outbound_fragments list so we don't
385   // really care how many packets were successfully dispatched.
386 }
387 
fragment_packet(l2cap_client_t * client,buffer_t * packet)388 static void fragment_packet(l2cap_client_t* client, buffer_t* packet) {
389   CHECK(client != NULL);
390   CHECK(packet != NULL);
391 
392   // TODO(sharvil): eliminate copy into BT_HDR.
393   BT_HDR* bt_packet = static_cast<BT_HDR*>(
394       osi_malloc(buffer_length(packet) + L2CAP_MIN_OFFSET + sizeof(BT_HDR)));
395   bt_packet->offset = L2CAP_MIN_OFFSET;
396   bt_packet->len = buffer_length(packet);
397   memcpy(bt_packet->data + bt_packet->offset, buffer_ptr(packet),
398          buffer_length(packet));
399 
400   for (;;) {
401     if (bt_packet->len <= client->remote_mtu) {
402       if (bt_packet->len > 0)
403         list_append(client->outbound_fragments, bt_packet);
404       else
405         osi_free(bt_packet);
406       break;
407     }
408 
409     BT_HDR* fragment = static_cast<BT_HDR*>(
410         osi_malloc(client->remote_mtu + L2CAP_MIN_OFFSET + sizeof(BT_HDR)));
411     fragment->offset = L2CAP_MIN_OFFSET;
412     fragment->len = client->remote_mtu;
413     memcpy(fragment->data + fragment->offset,
414            bt_packet->data + bt_packet->offset, client->remote_mtu);
415 
416     list_append(client->outbound_fragments, fragment);
417 
418     bt_packet->offset += client->remote_mtu;
419     bt_packet->len -= client->remote_mtu;
420   }
421 }
422 
dispatch_fragments(l2cap_client_t * client)423 static void dispatch_fragments(l2cap_client_t* client) {
424   CHECK(client != NULL);
425   CHECK(!client->is_congested);
426 
427   while (!list_is_empty(client->outbound_fragments)) {
428     BT_HDR* packet = (BT_HDR*)list_front(client->outbound_fragments);
429     list_remove(client->outbound_fragments, packet);
430 
431     switch (L2CA_DataWrite(client->local_channel_id, packet)) {
432       case L2CAP_DW_CONGESTED:
433         client->is_congested = true;
434         return;
435 
436       case L2CAP_DW_FAILED:
437         LOG_ERROR(LOG_TAG,
438                   "%s error writing data to L2CAP connection LCID 0x%04x; "
439                   "disconnecting.",
440                   __func__, client->local_channel_id);
441         l2cap_client_disconnect(client);
442         return;
443 
444       case L2CAP_DW_SUCCESS:
445         break;
446     }
447   }
448 }
449 
find(uint16_t local_channel_id)450 static l2cap_client_t* find(uint16_t local_channel_id) {
451   CHECK(local_channel_id != 0);
452 
453   for (const list_node_t* node = list_begin(l2cap_clients);
454        node != list_end(l2cap_clients); node = list_next(node)) {
455     l2cap_client_t* client = (l2cap_client_t*)list_node(node);
456     if (client->local_channel_id == local_channel_id) return client;
457   }
458 
459   return NULL;
460 }
461