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 */
35const 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 */
51const 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*******************************************************************************/
72static 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*******************************************************************************/
117static 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*******************************************************************************/
148void 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*******************************************************************************/
224void 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*******************************************************************************/
291void 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*******************************************************************************/
361void 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*******************************************************************************/
403void 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*******************************************************************************/
456void 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*******************************************************************************/
489void 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*******************************************************************************/
514void 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*******************************************************************************/
535void 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*******************************************************************************/
558UINT16 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