1/******************************************************************************
2 *
3 *  Copyright (C) 1999-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 file contains L2CAP interface functions
22 *
23 ******************************************************************************/
24
25#include <stddef.h>
26#include "bt_target.h"
27#include "btcore/include/counter.h"
28#include "gki.h"
29
30#include "rfcdefs.h"
31#include "port_api.h"
32#include "port_int.h"
33#include "l2c_api.h"
34#include "l2cdefs.h"
35#include "rfc_int.h"
36#include "bt_utils.h"
37
38
39/*
40** Define Callback functions to be called by L2CAP
41*/
42static void RFCOMM_ConnectInd (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id);
43static void RFCOMM_ConnectCnf (UINT16  lcid, UINT16 err);
44static void RFCOMM_ConfigInd (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg);
45static void RFCOMM_ConfigCnf (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg);
46static void RFCOMM_DisconnectInd (UINT16 lcid, BOOLEAN is_clear);
47static void RFCOMM_QoSViolationInd (BD_ADDR bd_addr);
48static void RFCOMM_BufDataInd (UINT16 lcid, BT_HDR *p_buf);
49static void RFCOMM_CongestionStatusInd (UINT16 lcid, BOOLEAN is_congested);
50
51
52/*******************************************************************************
53**
54** Function         rfcomm_l2cap_if_init
55**
56** Description      This function is called during the RFCOMM task startup
57**                  to register interface functions with L2CAP.
58**
59*******************************************************************************/
60void rfcomm_l2cap_if_init (void)
61{
62    tL2CAP_APPL_INFO *p_l2c = &rfc_cb.rfc.reg_info;
63
64    p_l2c->pL2CA_ConnectInd_Cb       = RFCOMM_ConnectInd;
65    p_l2c->pL2CA_ConnectCfm_Cb       = RFCOMM_ConnectCnf;
66    p_l2c->pL2CA_ConnectPnd_Cb       = NULL;
67    p_l2c->pL2CA_ConfigInd_Cb        = RFCOMM_ConfigInd;
68    p_l2c->pL2CA_ConfigCfm_Cb        = RFCOMM_ConfigCnf;
69    p_l2c->pL2CA_DisconnectInd_Cb    = RFCOMM_DisconnectInd;
70    p_l2c->pL2CA_DisconnectCfm_Cb    = NULL;
71    p_l2c->pL2CA_QoSViolationInd_Cb  = RFCOMM_QoSViolationInd;
72    p_l2c->pL2CA_DataInd_Cb          = RFCOMM_BufDataInd;
73    p_l2c->pL2CA_CongestionStatus_Cb = RFCOMM_CongestionStatusInd;
74    p_l2c->pL2CA_TxComplete_Cb       = NULL;
75
76
77    L2CA_Register (BT_PSM_RFCOMM, p_l2c);
78}
79
80
81/*******************************************************************************
82**
83** Function         RFCOMM_ConnectInd
84**
85** Description      This is a callback function called by L2CAP when
86**                  L2CA_ConnectInd received.  Allocate multiplexer control block
87**                  and dispatch the event to it.
88**
89*******************************************************************************/
90void RFCOMM_ConnectInd (BD_ADDR bd_addr, UINT16 lcid, UINT16 psm, UINT8 id)
91{
92    tRFC_MCB *p_mcb = rfc_alloc_multiplexer_channel(bd_addr, FALSE);
93    UNUSED(psm);
94
95    if ((p_mcb)&&(p_mcb->state != RFC_MX_STATE_IDLE))
96    {
97        /* if this is collision case */
98        if ((p_mcb->is_initiator)&&(p_mcb->state == RFC_MX_STATE_WAIT_CONN_CNF))
99        {
100            p_mcb->pending_lcid = lcid;
101            p_mcb->pending_id   = id;
102
103            /* wait random timeout (2 - 12) to resolve collision */
104            /* if peer gives up then local device rejects incoming connection and continues as initiator */
105            /* if timeout, local device disconnects outgoing connection and continues as acceptor */
106            RFCOMM_TRACE_DEBUG ("RFCOMM_ConnectInd start timer for collision, initiator's LCID(0x%x), acceptor's LCID(0x%x)",
107                                  p_mcb->lcid, p_mcb->pending_lcid);
108
109            rfc_timer_start(p_mcb, (UINT16)(GKI_get_os_tick_count()%10 + 2));
110            return;
111        }
112        else
113        {
114            /* we cannot accept connection request from peer at this state */
115            /* don't update lcid */
116            p_mcb = NULL;
117        }
118    }
119    else
120    {
121        /* store mcb even if null */
122        rfc_save_lcid_mcb (p_mcb, lcid);
123    }
124
125    if (p_mcb == NULL)
126    {
127        L2CA_ConnectRsp (bd_addr, id, lcid, L2CAP_CONN_NO_RESOURCES, 0);
128        return;
129    }
130    p_mcb->lcid     = lcid;
131
132    rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &id);
133}
134
135
136/*******************************************************************************
137**
138** Function         RFCOMM_ConnectCnf
139**
140** Description      This is a callback function called by L2CAP when
141**                  L2CA_ConnectCnf received.  Save L2CAP handle and dispatch
142**                  event to the FSM.
143**
144*******************************************************************************/
145void RFCOMM_ConnectCnf (UINT16 lcid, UINT16 result)
146{
147    tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid);
148
149    if (!p_mcb)
150    {
151        RFCOMM_TRACE_ERROR ("RFCOMM_ConnectCnf LCID:0x%x", lcid);
152        return;
153    }
154
155    if (p_mcb->pending_lcid)
156    {
157        /* if peer rejects our connect request but peer's connect request is pending */
158        if (result != L2CAP_CONN_OK )
159        {
160            UINT16 i;
161            UINT8  idx;
162
163            RFCOMM_TRACE_DEBUG ("RFCOMM_ConnectCnf retry as acceptor on pending LCID(0x%x)", p_mcb->pending_lcid);
164
165            /* remove mcb from mapping table */
166            rfc_save_lcid_mcb (NULL, p_mcb->lcid);
167
168            p_mcb->lcid         = p_mcb->pending_lcid;
169            p_mcb->is_initiator = FALSE;
170            p_mcb->state        = RFC_MX_STATE_IDLE;
171
172            /* store mcb into mapping table */
173            rfc_save_lcid_mcb (p_mcb, p_mcb->lcid);
174
175            /* update direction bit */
176            for (i = 0; i < RFCOMM_MAX_DLCI; i += 2)
177            {
178                if ((idx = p_mcb->port_inx[i]) != 0)
179                {
180                    p_mcb->port_inx[i] = 0;
181                    p_mcb->port_inx[i+1] = idx;
182                    rfc_cb.port.port[idx - 1].dlci += 1;
183                    RFCOMM_TRACE_DEBUG ("RFCOMM MX - DLCI:%d -> %d", i, rfc_cb.port.port[idx - 1].dlci);
184                }
185            }
186
187            rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_IND, &(p_mcb->pending_id));
188            return;
189        }
190        else
191        {
192            RFCOMM_TRACE_DEBUG ("RFCOMM_ConnectCnf peer gave up pending LCID(0x%x)", p_mcb->pending_lcid);
193
194            /* Peer gave up his connection request, make sure cleaning up L2CAP channel */
195            L2CA_ConnectRsp (p_mcb->bd_addr, p_mcb->pending_id, p_mcb->pending_lcid, L2CAP_CONN_NO_RESOURCES, 0);
196
197            p_mcb->pending_lcid = 0;
198        }
199    }
200
201    /* Save LCID to be used in all consecutive calls to L2CAP */
202    p_mcb->lcid         = lcid;
203
204    rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONN_CNF, &result);
205}
206
207
208/*******************************************************************************
209**
210** Function         RFCOMM_ConfigInd
211**
212** Description      This is a callback function called by L2CAP when
213**                  L2CA_ConfigInd received.  Save parameters in the control
214**                  block and dispatch event to the FSM.
215**
216*******************************************************************************/
217void RFCOMM_ConfigInd (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg)
218{
219    tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid);
220
221    if (!p_mcb)
222    {
223        RFCOMM_TRACE_ERROR ("RFCOMM_ConfigInd LCID:0x%x", lcid);
224        return;
225    }
226
227    rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONF_IND, (void *)p_cfg);
228}
229
230
231/*******************************************************************************
232**
233** Function         RFCOMM_ConfigCnf
234**
235** Description      This is a callback function called by L2CAP when
236**                  L2CA_ConfigCnf received.  Save L2CAP handle and dispatch
237**                  event to the FSM.
238**
239*******************************************************************************/
240void RFCOMM_ConfigCnf (UINT16 lcid, tL2CAP_CFG_INFO *p_cfg)
241{
242    tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid);
243
244    if (!p_mcb)
245    {
246        RFCOMM_TRACE_ERROR ("RFCOMM_ConfigCnf no MCB LCID:0x%x", lcid);
247        return;
248    }
249
250    rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_CONF_CNF, (void *)p_cfg);
251}
252
253
254/*******************************************************************************
255**
256** Function         RFCOMM_QoSViolationInd
257**
258** Description      This is a callback function called by L2CAP when
259**                  L2CA_QoSViolationIndInd received.  Dispatch event to the FSM.
260**
261*******************************************************************************/
262void RFCOMM_QoSViolationInd (BD_ADDR bd_addr)
263{
264    UNUSED(bd_addr);
265}
266
267
268/*******************************************************************************
269**
270** Function         RFCOMM_DisconnectInd
271**
272** Description      This is a callback function called by L2CAP when
273**                  L2CA_DisconnectInd received.  Dispatch event to the FSM.
274**
275*******************************************************************************/
276void RFCOMM_DisconnectInd (UINT16 lcid, BOOLEAN is_conf_needed)
277{
278    tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid);
279
280    if (is_conf_needed)
281    {
282        L2CA_DisconnectRsp (lcid);
283    }
284
285    if (!p_mcb)
286    {
287        RFCOMM_TRACE_WARNING ("RFCOMM_DisconnectInd LCID:0x%x", lcid);
288        return;
289    }
290
291    rfc_mx_sm_execute (p_mcb, RFC_MX_EVENT_DISC_IND, NULL);
292}
293
294
295/*******************************************************************************
296**
297** Function         RFCOMM_BufDataInd
298**
299** Description      This is a callback function called by L2CAP when
300**                  data RFCOMM frame is received.  Parse the frames, check
301**                  the checksum and dispatch event to multiplexer or port
302**                  state machine depending on the frame destination.
303**
304*******************************************************************************/
305void RFCOMM_BufDataInd (UINT16 lcid, BT_HDR *p_buf)
306{
307    tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid);
308    tPORT    *p_port;
309    UINT8    event;
310
311
312    if (!p_mcb)
313    {
314        RFCOMM_TRACE_WARNING ("RFCOMM_BufDataInd LCID:0x%x", lcid);
315        GKI_freebuf (p_buf);
316        return;
317    }
318
319    event = rfc_parse_data (p_mcb, &rfc_cb.rfc.rx_frame, p_buf);
320
321    /* If the frame did not pass validation just ignore it */
322    if (event == RFC_EVENT_BAD_FRAME)
323    {
324        GKI_freebuf (p_buf);
325        return;
326    }
327
328    if (rfc_cb.rfc.rx_frame.dlci == RFCOMM_MX_DLCI)
329    {
330        /* Take special care of the Multiplexer Control Messages */
331        if (event == RFC_EVENT_UIH)
332        {
333            rfc_process_mx_message (p_mcb, p_buf);
334            return;
335        }
336
337        /* Other multiplexer events go to state machine */
338        rfc_mx_sm_execute (p_mcb, event, NULL);
339        GKI_freebuf (p_buf);
340        return;
341    }
342
343    /* The frame was received on the data channel DLCI, verify that DLC exists */
344    if (((p_port = port_find_mcb_dlci_port (p_mcb, rfc_cb.rfc.rx_frame.dlci)) == NULL)
345     || (!p_port->rfc.p_mcb))
346    {
347        /* If this is a SABME on the new port, check if any appl is waiting for it */
348        if (event != RFC_EVENT_SABME)
349        {
350            if (( p_mcb->is_initiator && !rfc_cb.rfc.rx_frame.cr)
351             || (!p_mcb->is_initiator &&  rfc_cb.rfc.rx_frame.cr))
352                rfc_send_dm (p_mcb, rfc_cb.rfc.rx_frame.dlci, rfc_cb.rfc.rx_frame.pf);
353            GKI_freebuf (p_buf);
354            return;
355        }
356
357        if ((p_port = port_find_dlci_port (rfc_cb.rfc.rx_frame.dlci)) == NULL)
358        {
359            rfc_send_dm (p_mcb, rfc_cb.rfc.rx_frame.dlci, TRUE);
360            GKI_freebuf (p_buf);
361            return;
362        }
363        p_mcb->port_inx[rfc_cb.rfc.rx_frame.dlci] = p_port->inx;
364        p_port->rfc.p_mcb = p_mcb;
365    }
366
367    if (event == RFC_EVENT_UIH)
368    {
369        counter_add("rfcomm.rx.frames", 1);
370        counter_add("rfcomm.rx.bytes", p_buf->len);
371
372        if (p_buf->len > 0)
373            rfc_port_sm_execute (p_port, event, p_buf);
374        else
375            GKI_freebuf (p_buf);
376
377        if (rfc_cb.rfc.rx_frame.credit != 0)
378            rfc_inc_credit (p_port, rfc_cb.rfc.rx_frame.credit);
379
380        return;
381    }
382    rfc_port_sm_execute (p_port, event,  NULL);
383    GKI_freebuf (p_buf);
384}
385
386/*******************************************************************************
387**
388** Function         RFCOMM_CongestionStatusInd
389**
390** Description      This is a callback function called by L2CAP when
391**                  data RFCOMM L2CAP congestion status changes
392**
393*******************************************************************************/
394void RFCOMM_CongestionStatusInd (UINT16 lcid, BOOLEAN is_congested)
395{
396    tRFC_MCB *p_mcb = rfc_find_lcid_mcb (lcid);
397
398    if (!p_mcb)
399    {
400        RFCOMM_TRACE_ERROR ("RFCOMM_CongestionStatusInd dropped LCID:0x%x", lcid);
401        return;
402    }
403    else
404    {
405        RFCOMM_TRACE_EVENT ("RFCOMM_CongestionStatusInd LCID:0x%x", lcid);
406    }
407    rfc_process_l2cap_congestion (p_mcb, is_congested);
408}
409
410/*******************************************************************************
411**
412** Function         rfc_find_lcid_mcb
413**
414** Description      This function returns MCB block supporting local cid
415**
416*******************************************************************************/
417tRFC_MCB *rfc_find_lcid_mcb (UINT16 lcid)
418{
419    tRFC_MCB *p_mcb;
420
421    if (lcid - L2CAP_BASE_APPL_CID >= MAX_L2CAP_CHANNELS)
422    {
423        RFCOMM_TRACE_ERROR ("rfc_find_lcid_mcb LCID:0x%x", lcid);
424        return (NULL);
425    }
426    else
427    {
428        if ((p_mcb = rfc_cb.rfc.p_rfc_lcid_mcb[lcid - L2CAP_BASE_APPL_CID]) != NULL)
429        {
430            if (p_mcb->lcid != lcid)
431            {
432                RFCOMM_TRACE_WARNING ("rfc_find_lcid_mcb LCID reused LCID:0x%x current:0x%x", lcid, p_mcb->lcid);
433                return (NULL);
434            }
435        }
436    }
437    return (p_mcb);
438}
439
440
441/*******************************************************************************
442**
443** Function         rfc_save_lcid_mcb
444**
445** Description      This function returns MCB block supporting local cid
446**
447*******************************************************************************/
448void rfc_save_lcid_mcb (tRFC_MCB *p_mcb, UINT16 lcid)
449{
450    rfc_cb.rfc.p_rfc_lcid_mcb[lcid - L2CAP_BASE_APPL_CID] = p_mcb;
451}
452