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