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