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