1 /******************************************************************************
2 *
3 * Copyright 2009-2012 Broadcom Corporation
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 /******************************************************************************
20 *
21 * This is the implementation file for the MCAP at L2CAP Interface.
22 *
23 ******************************************************************************/
24 #include <string.h>
25
26 #include "bt_target.h"
27 #include "bt_utils.h"
28 #include "btm_api.h"
29 #include "btm_int.h"
30 #include "mca_api.h"
31 #include "mca_defs.h"
32 #include "mca_int.h"
33 #include "osi/include/osi.h"
34
35 /* L2CAP callback function structure */
36 const tL2CAP_APPL_INFO mca_l2c_int_appl = {
37 NULL,
38 mca_l2c_connect_cfm_cback,
39 NULL,
40 mca_l2c_config_ind_cback,
41 mca_l2c_config_cfm_cback,
42 mca_l2c_disconnect_ind_cback,
43 mca_l2c_disconnect_cfm_cback,
44 NULL,
45 mca_l2c_data_ind_cback,
46 mca_l2c_congestion_ind_cback,
47 NULL,
48 NULL /* tL2CA_CREDITS_RECEIVED_CB */};
49
50 /* Control channel eL2CAP default options */
51 const tL2CAP_FCR_OPTS mca_l2c_fcr_opts_def = {
52 L2CAP_FCR_ERTM_MODE, /* Mandatory for MCAP */
53 MCA_FCR_OPT_TX_WINDOW_SIZE, /* Tx window size */
54 MCA_FCR_OPT_MAX_TX_B4_DISCNT, /* Maximum transmissions before
55 disconnecting */
56 MCA_FCR_OPT_RETX_TOUT, /* Retransmission timeout (2 secs) */
57 MCA_FCR_OPT_MONITOR_TOUT, /* Monitor timeout (12 secs) */
58 MCA_FCR_OPT_MPS_SIZE /* MPS segment size */
59 };
60
61 /*******************************************************************************
62 *
63 * Function mca_sec_check_complete_term
64 *
65 * Description The function called when Security Manager finishes
66 * verification of the service side connection
67 *
68 * Returns void
69 *
70 ******************************************************************************/
mca_sec_check_complete_term(const RawAddress * bd_addr,UNUSED_ATTR tBT_TRANSPORT transport,void * p_ref_data,uint8_t res)71 static void mca_sec_check_complete_term(const RawAddress* bd_addr,
72 UNUSED_ATTR tBT_TRANSPORT transport,
73 void* p_ref_data, uint8_t res) {
74 tMCA_TC_TBL* p_tbl = (tMCA_TC_TBL*)p_ref_data;
75 tL2CAP_CFG_INFO cfg;
76 tL2CAP_ERTM_INFO ertm_info;
77
78 MCA_TRACE_DEBUG("mca_sec_check_complete_term res: %d", res);
79
80 if (res == BTM_SUCCESS) {
81 MCA_TRACE_DEBUG("lcid:x%x id:x%x", p_tbl->lcid, p_tbl->id);
82 /* Set the FCR options: control channel mandates ERTM */
83 ertm_info.preferred_mode = mca_l2c_fcr_opts_def.mode;
84 ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
85 ertm_info.user_rx_buf_size = MCA_USER_RX_BUF_SIZE;
86 ertm_info.user_tx_buf_size = MCA_USER_TX_BUF_SIZE;
87 ertm_info.fcr_rx_buf_size = MCA_FCR_RX_BUF_SIZE;
88 ertm_info.fcr_tx_buf_size = MCA_FCR_TX_BUF_SIZE;
89 /* Send response to the L2CAP layer. */
90 L2CA_ErtmConnectRsp(*bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_OK,
91 L2CAP_CONN_OK, &ertm_info);
92
93 /* transition to configuration state */
94 p_tbl->state = MCA_TC_ST_CFG;
95
96 /* Send L2CAP config req */
97 mca_set_cfg_by_tbl(&cfg, p_tbl);
98 L2CA_ConfigReq(p_tbl->lcid, &cfg);
99 } else {
100 L2CA_ConnectRsp(*bd_addr, p_tbl->id, p_tbl->lcid, L2CAP_CONN_SECURITY_BLOCK,
101 L2CAP_CONN_OK);
102 mca_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK);
103 }
104 }
105
106 /*******************************************************************************
107 *
108 * Function mca_sec_check_complete_orig
109 *
110 * Description The function called when Security Manager finishes
111 * verification of the service side connection
112 *
113 * Returns void
114 *
115 ******************************************************************************/
mca_sec_check_complete_orig(UNUSED_ATTR const RawAddress * bd_addr,UNUSED_ATTR tBT_TRANSPORT transport,void * p_ref_data,uint8_t res)116 static void mca_sec_check_complete_orig(UNUSED_ATTR const RawAddress* bd_addr,
117 UNUSED_ATTR tBT_TRANSPORT transport,
118 void* p_ref_data, uint8_t res) {
119 tMCA_TC_TBL* p_tbl = (tMCA_TC_TBL*)p_ref_data;
120 tL2CAP_CFG_INFO cfg;
121
122 MCA_TRACE_DEBUG("mca_sec_check_complete_orig res: %d", res);
123
124 if (res == BTM_SUCCESS) {
125 /* set channel state */
126 p_tbl->state = MCA_TC_ST_CFG;
127
128 /* Send L2CAP config req */
129 mca_set_cfg_by_tbl(&cfg, p_tbl);
130 L2CA_ConfigReq(p_tbl->lcid, &cfg);
131 } else {
132 L2CA_DisconnectReq(p_tbl->lcid);
133 mca_tc_close_ind(p_tbl, L2CAP_CONN_SECURITY_BLOCK);
134 }
135 }
136 /*******************************************************************************
137 *
138 * Function mca_l2c_cconn_ind_cback
139 *
140 * Description This is the L2CAP connect indication callback function.
141 *
142 * Returns void
143 *
144 ******************************************************************************/
mca_l2c_cconn_ind_cback(const RawAddress & bd_addr,uint16_t lcid,uint16_t psm,uint8_t id)145 void mca_l2c_cconn_ind_cback(const RawAddress& bd_addr, uint16_t lcid,
146 uint16_t psm, uint8_t id) {
147 tMCA_HANDLE handle = mca_handle_by_cpsm(psm);
148 tMCA_CCB* p_ccb;
149 tMCA_TC_TBL* p_tbl = NULL;
150 uint16_t result = L2CAP_CONN_NO_RESOURCES;
151 tBTM_STATUS rc;
152 tL2CAP_ERTM_INFO ertm_info, *p_ertm_info = NULL;
153 tL2CAP_CFG_INFO cfg;
154
155 MCA_TRACE_EVENT("mca_l2c_cconn_ind_cback: lcid:x%x psm:x%x id:x%x", lcid, psm,
156 id);
157
158 /* do we already have a control channel for this peer? */
159 p_ccb = mca_ccb_by_bd(handle, bd_addr);
160 if (p_ccb == NULL) {
161 /* no, allocate ccb */
162 p_ccb = mca_ccb_alloc(handle, bd_addr);
163 if (p_ccb != NULL) {
164 /* allocate and set up entry */
165 p_ccb->lcid = lcid;
166 p_tbl = mca_tc_tbl_calloc(p_ccb);
167 p_tbl->id = id;
168 p_tbl->cfg_flags = MCA_L2C_CFG_CONN_ACP;
169 /* proceed with connection */
170 /* Check the security */
171 rc = btm_sec_mx_access_request(bd_addr, psm, false, BTM_SEC_PROTO_MCA, 0,
172 &mca_sec_check_complete_term, p_tbl);
173 if (rc == BTM_CMD_STARTED) {
174 /* Set the FCR options: control channel mandates ERTM */
175 ertm_info.preferred_mode = mca_l2c_fcr_opts_def.mode;
176 ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
177 ertm_info.user_rx_buf_size = MCA_USER_RX_BUF_SIZE;
178 ertm_info.user_tx_buf_size = MCA_USER_TX_BUF_SIZE;
179 ertm_info.fcr_rx_buf_size = MCA_FCR_RX_BUF_SIZE;
180 ertm_info.fcr_tx_buf_size = MCA_FCR_TX_BUF_SIZE;
181 p_ertm_info = &ertm_info;
182 result = L2CAP_CONN_PENDING;
183 } else
184 result = L2CAP_CONN_OK;
185 }
186
187 /* deal with simultaneous control channel connect case */
188 }
189 /* else reject their connection */
190
191 if (!p_tbl || (p_tbl->state != MCA_TC_ST_CFG)) {
192 /* Send L2CAP connect rsp */
193 L2CA_ErtmConnectRsp(bd_addr, id, lcid, result, L2CAP_CONN_OK, p_ertm_info);
194
195 /* if result ok, proceed with connection and send L2CAP
196 config req */
197 if (result == L2CAP_CONN_OK) {
198 /* set channel state */
199 p_tbl->state = MCA_TC_ST_CFG;
200
201 /* Send L2CAP config req */
202 mca_set_cfg_by_tbl(&cfg, p_tbl);
203 L2CA_ConfigReq(p_tbl->lcid, &cfg);
204 }
205 }
206 }
207
208 /*******************************************************************************
209 *
210 * Function mca_l2c_dconn_ind_cback
211 *
212 * Description This is the L2CAP connect indication callback function.
213 *
214 *
215 * Returns void
216 *
217 ******************************************************************************/
mca_l2c_dconn_ind_cback(const RawAddress & bd_addr,uint16_t lcid,uint16_t psm,uint8_t id)218 void mca_l2c_dconn_ind_cback(const RawAddress& bd_addr, uint16_t lcid,
219 uint16_t psm, uint8_t id) {
220 tMCA_HANDLE handle = mca_handle_by_dpsm(psm);
221 tMCA_CCB* p_ccb;
222 tMCA_DCB* p_dcb;
223 tMCA_TC_TBL* p_tbl = NULL;
224 uint16_t result;
225 tL2CAP_CFG_INFO cfg;
226 tL2CAP_ERTM_INFO *p_ertm_info = NULL, ertm_info;
227 const tMCA_CHNL_CFG* p_chnl_cfg;
228
229 MCA_TRACE_EVENT("mca_l2c_dconn_ind_cback: lcid:x%x psm:x%x ", lcid, psm);
230
231 if (((p_ccb = mca_ccb_by_bd(handle, bd_addr)) != NULL) && /* find the CCB */
232 (p_ccb->status ==
233 MCA_CCB_STAT_PENDING) && /* this CCB is expecting a MDL */
234 (p_ccb->p_tx_req &&
235 (p_dcb = mca_dcb_by_hdl(p_ccb->p_tx_req->dcb_idx)) != NULL)) {
236 /* found the associated dcb in listening mode */
237 /* proceed with connection */
238 p_dcb->lcid = lcid;
239 p_tbl = mca_tc_tbl_dalloc(p_dcb);
240 p_tbl->id = id;
241 p_tbl->cfg_flags = MCA_L2C_CFG_CONN_ACP;
242 p_chnl_cfg = p_dcb->p_chnl_cfg;
243 /* assume that control channel has verified the security requirement */
244 /* Set the FCR options: control channel mandates ERTM */
245 ertm_info.preferred_mode = p_chnl_cfg->fcr_opt.mode;
246 ertm_info.allowed_modes = (1 << p_chnl_cfg->fcr_opt.mode);
247 ertm_info.user_rx_buf_size = p_chnl_cfg->user_rx_buf_size;
248 ertm_info.user_tx_buf_size = p_chnl_cfg->user_tx_buf_size;
249 ertm_info.fcr_rx_buf_size = p_chnl_cfg->fcr_rx_buf_size;
250 ertm_info.fcr_tx_buf_size = p_chnl_cfg->fcr_tx_buf_size;
251 p_ertm_info = &ertm_info;
252 result = L2CAP_CONN_OK;
253 } else {
254 /* else we're not listening for traffic channel; reject
255 * (this error code is specified by MCAP spec) */
256 result = L2CAP_CONN_NO_RESOURCES;
257 }
258
259 /* Send L2CAP connect rsp */
260 L2CA_ErtmConnectRsp(bd_addr, id, lcid, result, result, p_ertm_info);
261
262 /* if result ok, proceed with connection */
263 if (result == L2CAP_CONN_OK) {
264 /* transition to configuration state */
265 p_tbl->state = MCA_TC_ST_CFG;
266
267 /* Send L2CAP config req */
268 mca_set_cfg_by_tbl(&cfg, p_tbl);
269 L2CA_ConfigReq(lcid, &cfg);
270 }
271 }
272
273 /*******************************************************************************
274 *
275 * Function mca_l2c_connect_cfm_cback
276 *
277 * Description This is the L2CAP connect confirm callback function.
278 *
279 *
280 * Returns void
281 *
282 ******************************************************************************/
mca_l2c_connect_cfm_cback(uint16_t lcid,uint16_t result)283 void mca_l2c_connect_cfm_cback(uint16_t lcid, uint16_t result) {
284 tMCA_TC_TBL* p_tbl;
285 tL2CAP_CFG_INFO cfg;
286 tMCA_CCB* p_ccb;
287
288 MCA_TRACE_DEBUG("mca_l2c_connect_cfm_cback lcid: x%x, result: %d", lcid,
289 result);
290 /* look up info for this channel */
291 p_tbl = mca_tc_tbl_by_lcid(lcid);
292 if (p_tbl != NULL) {
293 MCA_TRACE_DEBUG("p_tbl state: %d, tcid: %d", p_tbl->state, p_tbl->tcid);
294 /* if in correct state */
295 if (p_tbl->state == MCA_TC_ST_CONN) {
296 /* if result successful */
297 if (result == L2CAP_CONN_OK) {
298 if (p_tbl->tcid != 0) {
299 /* set channel state */
300 p_tbl->state = MCA_TC_ST_CFG;
301
302 /* Send L2CAP config req */
303 mca_set_cfg_by_tbl(&cfg, p_tbl);
304 L2CA_ConfigReq(lcid, &cfg);
305 } else {
306 p_ccb = mca_ccb_by_hdl((tMCA_CL)p_tbl->cb_idx);
307 if (p_ccb == NULL) {
308 result = L2CAP_CONN_NO_RESOURCES;
309 } else {
310 /* set channel state */
311 p_tbl->state = MCA_TC_ST_SEC_INT;
312 p_tbl->lcid = lcid;
313 p_tbl->cfg_flags = MCA_L2C_CFG_CONN_INT;
314
315 /* Check the security */
316 btm_sec_mx_access_request(p_ccb->peer_addr, p_ccb->ctrl_vpsm, true,
317 BTM_SEC_PROTO_MCA, p_tbl->tcid,
318 &mca_sec_check_complete_orig, p_tbl);
319 }
320 }
321 }
322
323 /* failure; notify adaption that channel closed */
324 if (result != L2CAP_CONN_OK) {
325 p_tbl->cfg_flags |= MCA_L2C_CFG_DISCN_INT;
326 mca_tc_close_ind(p_tbl, result);
327 }
328 }
329 }
330 }
331
332 /*******************************************************************************
333 *
334 * Function mca_l2c_config_cfm_cback
335 *
336 * Description This is the L2CAP config confirm callback function.
337 *
338 *
339 * Returns void
340 *
341 ******************************************************************************/
mca_l2c_config_cfm_cback(uint16_t lcid,tL2CAP_CFG_INFO * p_cfg)342 void mca_l2c_config_cfm_cback(uint16_t lcid, tL2CAP_CFG_INFO* p_cfg) {
343 tMCA_TC_TBL* p_tbl;
344
345 /* look up info for this channel */
346 p_tbl = mca_tc_tbl_by_lcid(lcid);
347 if (p_tbl != NULL) {
348 /* if in correct state */
349 if (p_tbl->state == MCA_TC_ST_CFG) {
350 /* if result successful */
351 if (p_cfg->result == L2CAP_CONN_OK) {
352 /* update cfg_flags */
353 p_tbl->cfg_flags |= MCA_L2C_CFG_CFM_DONE;
354
355 /* if configuration complete */
356 if (p_tbl->cfg_flags & MCA_L2C_CFG_IND_DONE) {
357 mca_tc_open_ind(p_tbl);
358 }
359 }
360 /* else failure */
361 else {
362 /* Send L2CAP disconnect req */
363 L2CA_DisconnectReq(lcid);
364 }
365 }
366 }
367 }
368
369 /*******************************************************************************
370 *
371 * Function mca_l2c_config_ind_cback
372 *
373 * Description This is the L2CAP config indication callback function.
374 *
375 *
376 * Returns void
377 *
378 ******************************************************************************/
mca_l2c_config_ind_cback(uint16_t lcid,tL2CAP_CFG_INFO * p_cfg)379 void mca_l2c_config_ind_cback(uint16_t lcid, tL2CAP_CFG_INFO* p_cfg) {
380 tMCA_TC_TBL* p_tbl;
381 uint16_t result = L2CAP_CFG_OK;
382
383 /* look up info for this channel */
384 p_tbl = mca_tc_tbl_by_lcid(lcid);
385 if (p_tbl != NULL) {
386 /* store the mtu in tbl */
387 if (p_cfg->mtu_present) {
388 p_tbl->peer_mtu = p_cfg->mtu;
389 if (p_tbl->peer_mtu < MCA_MIN_MTU) {
390 result = L2CAP_CFG_UNACCEPTABLE_PARAMS;
391 }
392 } else {
393 p_tbl->peer_mtu = L2CAP_DEFAULT_MTU;
394 }
395 MCA_TRACE_DEBUG("peer_mtu: %d, lcid: x%x mtu_present:%d", p_tbl->peer_mtu,
396 lcid, p_cfg->mtu_present);
397
398 /* send L2CAP configure response */
399 memset(p_cfg, 0, sizeof(tL2CAP_CFG_INFO));
400 p_cfg->result = result;
401 L2CA_ConfigRsp(lcid, p_cfg);
402
403 /* if first config ind */
404 if ((p_tbl->cfg_flags & MCA_L2C_CFG_IND_DONE) == 0) {
405 /* update cfg_flags */
406 p_tbl->cfg_flags |= MCA_L2C_CFG_IND_DONE;
407
408 /* if configuration complete */
409 if (p_tbl->cfg_flags & MCA_L2C_CFG_CFM_DONE) {
410 mca_tc_open_ind(p_tbl);
411 }
412 }
413 }
414 }
415
416 /*******************************************************************************
417 *
418 * Function mca_l2c_disconnect_ind_cback
419 *
420 * Description This is the L2CAP disconnect indication callback function.
421 *
422 *
423 * Returns void
424 *
425 ******************************************************************************/
mca_l2c_disconnect_ind_cback(uint16_t lcid,bool ack_needed)426 void mca_l2c_disconnect_ind_cback(uint16_t lcid, bool ack_needed) {
427 tMCA_TC_TBL* p_tbl;
428 uint16_t reason = L2CAP_DISC_TIMEOUT;
429
430 MCA_TRACE_DEBUG("mca_l2c_disconnect_ind_cback lcid: %d, ack_needed: %d", lcid,
431 ack_needed);
432 /* look up info for this channel */
433 p_tbl = mca_tc_tbl_by_lcid(lcid);
434 if (p_tbl != NULL) {
435 if (ack_needed) {
436 /* send L2CAP disconnect response */
437 L2CA_DisconnectRsp(lcid);
438 }
439
440 p_tbl->cfg_flags = MCA_L2C_CFG_DISCN_ACP;
441 if (ack_needed) reason = L2CAP_DISC_OK;
442 mca_tc_close_ind(p_tbl, reason);
443 }
444 }
445
446 /*******************************************************************************
447 *
448 * Function mca_l2c_disconnect_cfm_cback
449 *
450 * Description This is the L2CAP disconnect confirm callback function.
451 *
452 *
453 * Returns void
454 *
455 ******************************************************************************/
mca_l2c_disconnect_cfm_cback(uint16_t lcid,uint16_t result)456 void mca_l2c_disconnect_cfm_cback(uint16_t lcid, uint16_t result) {
457 tMCA_TC_TBL* p_tbl;
458
459 MCA_TRACE_DEBUG("mca_l2c_disconnect_cfm_cback lcid: x%x, result: %d", lcid,
460 result);
461 /* look up info for this channel */
462 p_tbl = mca_tc_tbl_by_lcid(lcid);
463 if (p_tbl != NULL) {
464 p_tbl->cfg_flags = MCA_L2C_CFG_DISCN_INT;
465 mca_tc_close_ind(p_tbl, result);
466 }
467 }
468
469 /*******************************************************************************
470 *
471 * Function mca_l2c_congestion_ind_cback
472 *
473 * Description This is the L2CAP congestion indication callback function.
474 *
475 *
476 * Returns void
477 *
478 ******************************************************************************/
mca_l2c_congestion_ind_cback(uint16_t lcid,bool is_congested)479 void mca_l2c_congestion_ind_cback(uint16_t lcid, bool is_congested) {
480 tMCA_TC_TBL* p_tbl;
481
482 /* look up info for this channel */
483 p_tbl = mca_tc_tbl_by_lcid(lcid);
484 if (p_tbl != NULL) {
485 mca_tc_cong_ind(p_tbl, is_congested);
486 }
487 }
488
489 /*******************************************************************************
490 *
491 * Function mca_l2c_data_ind_cback
492 *
493 * Description This is the L2CAP data indication callback function.
494 *
495 *
496 * Returns void
497 *
498 ******************************************************************************/
mca_l2c_data_ind_cback(uint16_t lcid,BT_HDR * p_buf)499 void mca_l2c_data_ind_cback(uint16_t lcid, BT_HDR* p_buf) {
500 tMCA_TC_TBL* p_tbl;
501
502 /* look up info for this channel */
503 p_tbl = mca_tc_tbl_by_lcid(lcid);
504 if (p_tbl != NULL) {
505 mca_tc_data_ind(p_tbl, p_buf);
506 } else /* prevent buffer leak */
507 osi_free(p_buf);
508 }
509
510 /*******************************************************************************
511 *
512 * Function mca_l2c_open_req
513 *
514 * Description This function calls L2CA_ConnectReq() to initiate a L2CAP
515 * channel.
516 *
517 * Returns void.
518 *
519 ******************************************************************************/
mca_l2c_open_req(const RawAddress & bd_addr,uint16_t psm,const tMCA_CHNL_CFG * p_chnl_cfg)520 uint16_t mca_l2c_open_req(const RawAddress& bd_addr, uint16_t psm,
521 const tMCA_CHNL_CFG* p_chnl_cfg) {
522 tL2CAP_ERTM_INFO ertm_info;
523
524 if (p_chnl_cfg) {
525 ertm_info.preferred_mode = p_chnl_cfg->fcr_opt.mode;
526 ertm_info.allowed_modes = (1 << p_chnl_cfg->fcr_opt.mode);
527 ertm_info.user_rx_buf_size = p_chnl_cfg->user_rx_buf_size;
528 ertm_info.user_tx_buf_size = p_chnl_cfg->user_tx_buf_size;
529 ertm_info.fcr_rx_buf_size = p_chnl_cfg->fcr_rx_buf_size;
530 ertm_info.fcr_tx_buf_size = p_chnl_cfg->fcr_tx_buf_size;
531 } else {
532 ertm_info.preferred_mode = mca_l2c_fcr_opts_def.mode;
533 ertm_info.allowed_modes = L2CAP_FCR_CHAN_OPT_ERTM;
534 ertm_info.user_rx_buf_size = MCA_USER_RX_BUF_SIZE;
535 ertm_info.user_tx_buf_size = MCA_USER_TX_BUF_SIZE;
536 ertm_info.fcr_rx_buf_size = MCA_FCR_RX_BUF_SIZE;
537 ertm_info.fcr_tx_buf_size = MCA_FCR_TX_BUF_SIZE;
538 }
539 return L2CA_ErtmConnectReq(psm, bd_addr, &ertm_info);
540 }
541