hidh_conn.c revision 5738f83aeb59361a0a2eda2460113f6dc9194271
1/******************************************************************************
2 *
3 *  Copyright (C) 2002-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 the connection interface functions
22 *
23 ******************************************************************************/
24
25#include <stdlib.h>
26#include <string.h>
27#include <stdio.h>
28
29
30#include "gki.h"
31#include "bt_types.h"
32
33#include "l2cdefs.h"
34#include "l2c_api.h"
35
36#include "btu.h"
37#include "btm_api.h"
38#include "btm_int.h"
39
40#include "hiddefs.h"
41
42#include "hidh_api.h"
43#include "hidh_int.h"
44
45static UINT8 find_conn_by_cid (UINT16 cid);
46static void hidh_conn_retry (UINT8 dhandle);
47
48/********************************************************************************/
49/*              L O C A L    F U N C T I O N     P R O T O T Y P E S            */
50/********************************************************************************/
51static void hidh_l2cif_connect_ind (BD_ADDR  bd_addr, UINT16 l2cap_cid,
52                                    UINT16 psm, UINT8 l2cap_id);
53static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result);
54static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
55static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
56static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed);
57static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg);
58static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result);
59static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested);
60
61static const tL2CAP_APPL_INFO hst_reg_info =
62{
63    hidh_l2cif_connect_ind,
64    hidh_l2cif_connect_cfm,
65    NULL,
66    hidh_l2cif_config_ind,
67    hidh_l2cif_config_cfm,
68    hidh_l2cif_disconnect_ind,
69    hidh_l2cif_disconnect_cfm,
70    NULL,
71    hidh_l2cif_data_ind,
72    hidh_l2cif_cong_ind,
73    NULL                        /* tL2CA_TX_COMPLETE_CB */
74};
75
76/*******************************************************************************
77**
78** Function         hidh_l2cif_reg
79**
80** Description      This function initializes the SDP unit.
81**
82** Returns          void
83**
84*******************************************************************************/
85tHID_STATUS hidh_conn_reg (void)
86{
87    int xx;
88
89    /* Initialize the L2CAP configuration. We only care about MTU and flush */
90    memset(&hh_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO));
91
92    hh_cb.l2cap_cfg.mtu_present          = TRUE;
93    hh_cb.l2cap_cfg.mtu                  = HID_HOST_MTU;
94    hh_cb.l2cap_cfg.flush_to_present     = TRUE;
95    hh_cb.l2cap_cfg.flush_to             = HID_HOST_FLUSH_TO;
96
97    /* Now, register with L2CAP */
98    if (!L2CA_Register (HID_PSM_CONTROL, (tL2CAP_APPL_INFO *) &hst_reg_info))
99    {
100        HIDH_TRACE_ERROR0 ("HID Control Registration failed");
101        return (HID_ERR_L2CAP_FAILED) ;
102    }
103    if (!L2CA_Register (HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *) &hst_reg_info))
104    {
105        L2CA_Deregister( HID_PSM_CONTROL ) ;
106        HIDH_TRACE_ERROR0 ("HID Interrupt Registration failed");
107        return (HID_ERR_L2CAP_FAILED) ;
108    }
109
110    for (xx = 0; xx < HID_HOST_MAX_DEVICES; xx++)
111    {
112        hh_cb.devices[xx].in_use = FALSE ;
113        hh_cb.devices[xx].conn.conn_state = HID_CONN_STATE_UNUSED;
114    }
115
116    return (HID_SUCCESS);
117}
118
119/*******************************************************************************
120**
121** Function         hidh_conn_disconnect
122**
123** Description      This function disconnects a connection.
124**
125** Returns          TRUE if disconnect started, FALSE if already disconnected
126**
127*******************************************************************************/
128tHID_STATUS hidh_conn_disconnect (UINT8 dhandle)
129{
130    tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn;
131
132    HIDH_TRACE_EVENT0 ("HID - disconnect");
133
134    if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0))
135    {
136        p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING;
137
138        /* Disconnect both interrupt and control channels */
139        if (p_hcon->intr_cid)
140            L2CA_DisconnectReq (p_hcon->intr_cid);
141
142        if (p_hcon->ctrl_cid)
143            L2CA_DisconnectReq (p_hcon->ctrl_cid);
144    }
145    else
146    {
147        p_hcon->conn_state = HID_CONN_STATE_UNUSED;
148    }
149
150    return (HID_SUCCESS);
151}
152
153/*******************************************************************************
154**
155** Function         hidh_sec_check_complete_term
156**
157** Description      HID security check complete callback function.
158**
159** Returns          Send L2CA_ConnectRsp OK if secutiry check succeed; otherwise
160**                  send security block L2C connection response.
161**
162*******************************************************************************/
163void hidh_sec_check_complete_term (BD_ADDR bd_addr, void *p_ref_data, UINT8 res)
164{
165    tHID_HOST_DEV_CTB *p_dev= (tHID_HOST_DEV_CTB *) p_ref_data;
166
167    if( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY )
168    {
169        p_dev->conn.disc_reason = HID_SUCCESS;  /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */
170
171        p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR;
172
173        /* Send response to the L2CAP layer. */
174        L2CA_ConnectRsp (p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
175
176        /* Send a Configuration Request. */
177        L2CA_ConfigReq (p_dev->conn.ctrl_cid, &hh_cb.l2cap_cfg);
178
179    }
180    /* security check fail */
181    else if (res != BTM_SUCCESS)
182    {
183        p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED;      /* Save reason for disconnecting */
184        p_dev->conn.conn_state = HID_CONN_STATE_UNUSED;
185        L2CA_ConnectRsp (p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_SECURITY_BLOCK, L2CAP_CONN_OK);
186    }
187}
188
189/*******************************************************************************
190**
191** Function         hidh_l2cif_connect_ind
192**
193** Description      This function handles an inbound connection indication
194**                  from L2CAP. This is the case where we are acting as a
195**                  server.
196**
197** Returns          void
198**
199*******************************************************************************/
200static void hidh_l2cif_connect_ind (BD_ADDR  bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id)
201{
202    tHID_CONN    *p_hcon;
203    BOOLEAN      bAccept = TRUE;
204    int i;
205    tHID_HOST_DEV_CTB *p_dev;
206
207    HIDH_TRACE_EVENT2 ("HID - Rcvd L2CAP conn ind, PSM: 0x%04x  CID 0x%x", psm, l2cap_cid);
208
209    for( i=0; i < HID_HOST_MAX_DEVICES; i++ )
210    {
211        if( hh_cb.devices[i].in_use && (!memcmp(bd_addr, hh_cb.devices[i].addr, sizeof(BD_ADDR))) )
212            break;
213    }
214
215    if (i >= HID_HOST_MAX_DEVICES)
216    {
217        L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_SECURITY_BLOCK, 0);
218        return;
219    }
220
221    p_hcon = &hh_cb.devices[i].conn;
222    p_dev  = &hh_cb.devices[i];
223
224    /* Check we are in the correct state for this */
225    if (psm == HID_PSM_INTERRUPT)
226    {
227        if (p_hcon->ctrl_cid == 0)
228        {
229            HIDH_TRACE_WARNING0 ("HID - Rcvd INTR L2CAP conn ind, but no CTL channel");
230            bAccept = FALSE;
231        }
232        if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR)
233        {
234            HIDH_TRACE_WARNING1 ("HID - Rcvd INTR L2CAP conn ind, wrong state: %d", p_hcon->conn_state);
235            bAccept = FALSE;
236        }
237    }
238    else /* CTRL channel */
239    {
240#if defined(HID_HOST_ACPT_NEW_CONN) && (HID_HOST_ACPT_NEW_CONN == TRUE)
241        p_hcon->ctrl_cid = p_hcon->intr_cid = 0;
242        p_hcon->conn_state = HID_CONN_STATE_UNUSED;
243#else
244        if (p_hcon->conn_state != HID_CONN_STATE_UNUSED)
245        {
246            HIDH_TRACE_WARNING1 ("HID - Rcvd CTL L2CAP conn ind, wrong state: %d", p_hcon->conn_state);
247            bAccept = FALSE;
248        }
249#endif
250    }
251
252    if (!bAccept)
253    {
254        L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_RESOURCES, 0);
255        return;
256    }
257
258    if (psm == HID_PSM_CONTROL)
259    {
260        p_hcon->conn_flags = 0;
261        p_hcon->ctrl_cid   = l2cap_cid;
262        p_hcon->ctrl_id    = l2cap_id;
263        p_hcon->disc_reason = HID_L2CAP_CONN_FAIL;  /* In case disconnection occurs before security is completed, then set CLOSE_EVT reason code to 'connection failure' */
264
265        p_hcon->conn_state = HID_CONN_STATE_SECURITY;
266        if(btm_sec_mx_access_request (p_dev->addr, HID_PSM_CONTROL,
267            FALSE, BTM_SEC_PROTO_HID,
268            (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN,
269            &hidh_sec_check_complete_term, p_dev) == BTM_CMD_STARTED)
270        {
271            L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK);
272        }
273
274        return;
275    }
276
277    /* Transition to the next appropriate state, configuration */
278    p_hcon->conn_state = HID_CONN_STATE_CONFIG;
279    p_hcon->intr_cid   = l2cap_cid;
280
281    /* Send response to the L2CAP layer. */
282    L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
283
284    /* Send a Configuration Request. */
285    L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg);
286
287    HIDH_TRACE_EVENT2 ("HID - Rcvd L2CAP conn ind, sent config req, PSM: 0x%04x  CID 0x%x", psm, l2cap_cid);
288}
289
290/*******************************************************************************
291**
292** Function         hidh_proc_repage_timeout
293**
294** Description      This function handles timeout (to page device).
295**
296** Returns          void
297**
298*******************************************************************************/
299void hidh_proc_repage_timeout (TIMER_LIST_ENT *p_tle)
300{
301    hidh_conn_initiate( (UINT8) p_tle->param ) ;
302    hh_cb.devices[p_tle->param].conn_tries++;
303    hh_cb.callback( (UINT8) p_tle->param, HID_HDEV_EVT_RETRYING, hh_cb.devices[p_tle->param].conn_tries, NULL ) ;
304}
305
306/*******************************************************************************
307**
308** Function         hidh_sec_check_complete_orig
309**
310** Description      This function checks to see if security procedures are being
311**                  carried out or not..
312**
313** Returns          void
314**
315*******************************************************************************/
316void hidh_sec_check_complete_orig (BD_ADDR bd_addr, void *p_ref_data, UINT8 res)
317{
318    tHID_HOST_DEV_CTB *p_dev = (tHID_HOST_DEV_CTB *) p_ref_data;
319    UINT8 dhandle;
320#if (HID_HOST_MAX_CONN_RETRY > 0)
321    UINT32 cb_res = HID_ERR_AUTH_FAILED;
322#endif
323    UINT32 reason;
324
325    dhandle = p_dev - &(hh_cb.devices[0]) ;
326    if( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY )
327    {
328        HIDH_TRACE_EVENT0 ("HID - Originator security pass.");
329        p_dev->conn.disc_reason = HID_SUCCESS;  /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */
330
331        /* Check if L2CAP started the connection process for interrupt channel */
332        if ((p_dev->conn.intr_cid = L2CA_ConnectReq (HID_PSM_INTERRUPT, hh_cb.devices[dhandle].addr)) == 0)
333        {
334            HIDH_TRACE_WARNING0 ("HID - INTR Originate failed");
335            reason = HID_L2CAP_REQ_FAIL ;
336            hidh_conn_disconnect (dhandle);
337            hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, reason, NULL ) ;
338            return;
339        }
340        else
341        {
342            /* Transition to the next appropriate state, waiting for connection confirm on control channel. */
343            p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR;
344        }
345    }
346
347    if( res != BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY )
348    {
349#if (HID_HOST_MAX_CONN_RETRY > 0)
350        if( res == BTM_DEVICE_TIMEOUT )
351        {
352            if( p_dev->conn_tries <= HID_HOST_MAX_CONN_RETRY )
353            {
354                hidh_conn_retry (dhandle);
355                return;
356            }
357            else
358                cb_res = HID_L2CAP_CONN_FAIL | HCI_ERR_PAGE_TIMEOUT ;
359        }
360#endif
361        p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED;      /* Save reason for disconnecting */
362        hidh_conn_disconnect(dhandle);
363    }
364
365}
366
367/*******************************************************************************
368**
369** Function         hidh_l2cif_connect_cfm
370**
371** Description      This function handles the connect confirm events
372**                  from L2CAP. This is the case when we are acting as a
373**                  client and have sent a connect request.
374**
375** Returns          void
376**
377*******************************************************************************/
378static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result)
379{
380    UINT8 dhandle;
381    tHID_CONN    *p_hcon = NULL;
382    UINT32  reason;
383    tHID_HOST_DEV_CTB *p_dev = NULL;
384
385    /* Find CCB based on CID, and verify we are in a state to accept this message */
386    if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
387    {
388        p_dev = &hh_cb.devices[dhandle];
389        p_hcon = &hh_cb.devices[dhandle].conn;
390    }
391
392    if ((p_hcon == NULL)
393     || (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG))
394     || ((l2cap_cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL))
395     || ((l2cap_cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR)))
396    {
397        HIDH_TRACE_WARNING1 ("HID - Rcvd unexpected conn cnf, CID 0x%x ", l2cap_cid);
398        return;
399    }
400
401    if (result != L2CAP_CONN_OK)
402    {
403        if (l2cap_cid == p_hcon->ctrl_cid)
404            p_hcon->ctrl_cid = 0;
405        else
406            p_hcon->intr_cid = 0;
407
408        hidh_conn_disconnect(dhandle);
409
410#if (HID_HOST_MAX_CONN_RETRY > 0)
411        if( (hh_cb.devices[dhandle].conn_tries <= HID_HOST_MAX_CONN_RETRY) &&
412            (result == HCI_ERR_CONNECTION_TOUT || result == HCI_ERR_UNSPECIFIED ||
413             result == HCI_ERR_PAGE_TIMEOUT) )
414        {
415            hidh_conn_retry(dhandle);
416        }
417        else
418#endif
419        {
420            reason = HID_L2CAP_CONN_FAIL | (UINT32) result ;
421            hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, reason, NULL ) ;
422        }
423        return;
424    }
425    /* receive Control Channel connect confirmation */
426    if (l2cap_cid == p_hcon->ctrl_cid)
427    {
428        /* check security requirement */
429        p_hcon->conn_state = HID_CONN_STATE_SECURITY;
430        p_hcon->disc_reason = HID_L2CAP_CONN_FAIL;  /* In case disconnection occurs before security is completed, then set CLOSE_EVT reason code to "connection failure" */
431
432        btm_sec_mx_access_request (p_dev->addr, HID_PSM_CONTROL,
433            TRUE, BTM_SEC_PROTO_HID,
434            (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN,
435            &hidh_sec_check_complete_orig, p_dev);
436    }
437    else
438    {
439        p_hcon->conn_state = HID_CONN_STATE_CONFIG;
440    }
441
442    /* Send a Configuration Request. */
443    L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg);
444
445    HIDH_TRACE_EVENT1 ("HID - got CTRL conn cnf, sent cfg req, CID: 0x%x", l2cap_cid);
446    return;
447}
448
449/*******************************************************************************
450**
451** Function         hidh_l2cif_config_ind
452**
453** Description      This function processes the L2CAP configuration indication
454**                  event.
455**
456** Returns          void
457**
458*******************************************************************************/
459static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
460{
461    UINT8 dhandle;
462    tHID_CONN    *p_hcon = NULL;
463    tHID_HOST_DEV_CTB *p_dev;
464
465    /* Find CCB based on CID */
466    if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
467    {
468        p_dev = &hh_cb.devices[dhandle];
469        p_hcon = &hh_cb.devices[dhandle].conn;
470    }
471
472    if (p_hcon == NULL)
473    {
474        HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
475        return;
476    }
477
478    HIDH_TRACE_EVENT1 ("HID - Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid);
479
480    /* Remember the remote MTU size */
481    if ((!p_cfg->mtu_present) || (p_cfg->mtu > HID_HOST_MTU))
482        p_hcon->rem_mtu_size = HID_HOST_MTU;
483    else
484        p_hcon->rem_mtu_size = p_cfg->mtu;
485
486    /* For now, always accept configuration from the other side */
487    p_cfg->flush_to_present = FALSE;
488    p_cfg->mtu_present      = FALSE;
489    p_cfg->result           = L2CAP_CFG_OK;
490
491    L2CA_ConfigRsp (l2cap_cid, p_cfg);
492
493    if (l2cap_cid == p_hcon->ctrl_cid)
494        p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_CTRL_CFG_DONE;
495    else
496        p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_INTR_CFG_DONE;
497
498    /* If all configuration is complete, change state and tell management we are up */
499    if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED)
500     && (p_hcon->conn_state == HID_CONN_STATE_CONFIG))
501    {
502        p_hcon->conn_state = HID_CONN_STATE_CONNECTED;
503
504        hh_cb.devices[dhandle].state = HID_DEV_CONNECTED;
505        hh_cb.callback( dhandle, HID_HDEV_EVT_OPEN, 0, NULL ) ;
506    }
507}
508
509
510/*******************************************************************************
511**
512** Function         hidh_l2cif_config_cfm
513**
514** Description      This function processes the L2CAP configuration confirmation
515**                  event.
516**
517** Returns          void
518**
519*******************************************************************************/
520static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
521{
522    UINT8 dhandle;
523    tHID_CONN    *p_hcon = NULL;
524    UINT32  reason;
525
526    HIDH_TRACE_EVENT2 ("HID - Rcvd cfg cfm, CID: 0x%x  Result: %d", l2cap_cid, p_cfg->result);
527
528    /* Find CCB based on CID */
529    if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
530        p_hcon = &hh_cb.devices[dhandle].conn;
531
532    if (p_hcon == NULL)
533    {
534        HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
535        return;
536    }
537
538    /* If configuration failed, disconnect the channel(s) */
539    if (p_cfg->result != L2CAP_CFG_OK)
540    {
541        hidh_conn_disconnect (dhandle);
542        reason = HID_L2CAP_CFG_FAIL | (UINT32) p_cfg->result ;
543        hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, reason, NULL ) ;
544        return;
545    }
546
547    if (l2cap_cid == p_hcon->ctrl_cid)
548        p_hcon->conn_flags |= HID_CONN_FLAGS_MY_CTRL_CFG_DONE;
549    else
550        p_hcon->conn_flags |= HID_CONN_FLAGS_MY_INTR_CFG_DONE;
551
552    /* If all configuration is complete, change state and tell management we are up */
553    if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED)
554     && (p_hcon->conn_state == HID_CONN_STATE_CONFIG))
555    {
556        p_hcon->conn_state = HID_CONN_STATE_CONNECTED;
557
558        hh_cb.devices[dhandle].state = HID_DEV_CONNECTED;
559        hh_cb.callback( dhandle, HID_HDEV_EVT_OPEN, 0, NULL ) ;
560    }
561}
562
563
564/*******************************************************************************
565**
566** Function         hidh_l2cif_disconnect_ind
567**
568** Description      This function handles a disconnect event from L2CAP. If
569**                  requested to, we ack the disconnect before dropping the CCB
570**
571** Returns          void
572**
573*******************************************************************************/
574static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed)
575{
576    UINT8 dhandle;
577    tHID_CONN    *p_hcon = NULL;
578    UINT16 disc_res = HCI_SUCCESS;
579    UINT16 hid_close_evt_reason;
580
581    /* Find CCB based on CID */
582    if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
583        p_hcon = &hh_cb.devices[dhandle].conn;
584
585    if (p_hcon == NULL)
586    {
587        HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid);
588        return;
589    }
590
591    if (ack_needed)
592        L2CA_DisconnectRsp (l2cap_cid);
593
594    HIDH_TRACE_EVENT1 ("HID - Rcvd L2CAP disc, CID: 0x%x", l2cap_cid);
595
596    p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING;
597
598    if (l2cap_cid == p_hcon->ctrl_cid)
599        p_hcon->ctrl_cid = 0;
600    else
601        p_hcon->intr_cid = 0;
602
603    if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0))
604    {
605        hh_cb.devices[dhandle].state = HID_DEV_NO_CONN;
606        p_hcon->conn_state = HID_CONN_STATE_UNUSED;
607
608        if( !ack_needed )
609            disc_res = btm_get_acl_disc_reason_code();
610
611#if (HID_HOST_MAX_CONN_RETRY > 0)
612        if( (disc_res == HCI_ERR_CONNECTION_TOUT || disc_res == HCI_ERR_UNSPECIFIED) &&
613            (!(hh_cb.devices[dhandle].attr_mask & HID_RECONN_INIT)) &&
614            (hh_cb.devices[dhandle].attr_mask & HID_NORMALLY_CONNECTABLE))
615        {
616            hh_cb.devices[dhandle].conn_tries = 0;
617            hh_cb.devices[dhandle].conn.timer_entry.param = (UINT32) dhandle;
618            btu_start_timer (&(hh_cb.devices[dhandle].conn.timer_entry), BTU_TTYPE_HID_HOST_REPAGE_TO, HID_HOST_REPAGE_WIN);
619        }
620        else
621#endif
622        {
623            /* Set reason code for HID_HDEV_EVT_CLOSE */
624            hid_close_evt_reason = p_hcon->disc_reason;
625
626            /* If we got baseband sent HCI_DISCONNECT_COMPLETE_EVT due to security failure, then set reason to HID_ERR_AUTH_FAILED */
627            if ((disc_res == HCI_ERR_AUTH_FAILURE)                        ||
628                (disc_res == HCI_ERR_KEY_MISSING)                         ||
629                (disc_res == HCI_ERR_HOST_REJECT_SECURITY)                ||
630                (disc_res == HCI_ERR_PAIRING_NOT_ALLOWED)                 ||
631                (disc_res == HCI_ERR_UNIT_KEY_USED)                       ||
632                (disc_res == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) ||
633                (disc_res == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE)           ||
634                (disc_res == HCI_ERR_REPEATED_ATTEMPTS))
635            {
636                hid_close_evt_reason = HID_ERR_AUTH_FAILED;
637            }
638
639            hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, hid_close_evt_reason, NULL ) ;
640        }
641    }
642}
643
644
645/*******************************************************************************
646**
647** Function         hidh_l2cif_disconnect_cfm
648**
649** Description      This function handles a disconnect confirm event from L2CAP.
650**
651** Returns          void
652**
653*******************************************************************************/
654static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result)
655{
656    UINT8 dhandle;
657    tHID_CONN    *p_hcon = NULL;
658
659    /* Find CCB based on CID */
660    if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
661        p_hcon = &hh_cb.devices[dhandle].conn;
662
663    if (p_hcon == NULL)
664    {
665        HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid);
666        return;
667    }
668
669    HIDH_TRACE_EVENT1 ("HID - Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid);
670
671    if (l2cap_cid == p_hcon->ctrl_cid)
672        p_hcon->ctrl_cid = 0;
673    else
674        p_hcon->intr_cid = 0;
675
676    if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0))
677    {
678        hh_cb.devices[dhandle].state = HID_DEV_NO_CONN;
679        p_hcon->conn_state = HID_CONN_STATE_UNUSED;
680        hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, p_hcon->disc_reason, NULL ) ;
681    }
682}
683
684
685/*******************************************************************************
686**
687** Function         hidh_l2cif_cong_ind
688**
689** Description      This function handles a congestion status event from L2CAP.
690**
691** Returns          void
692**
693*******************************************************************************/
694static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested)
695{
696    UINT8 dhandle;
697    tHID_CONN    *p_hcon = NULL;
698
699    /* Find CCB based on CID */
700    if( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES )
701        p_hcon = &hh_cb.devices[dhandle].conn;
702
703    if (p_hcon == NULL)
704    {
705        HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP congestion status, unknown CID: 0x%x", l2cap_cid);
706        return;
707    }
708
709    HIDH_TRACE_EVENT2 ("HID - Rcvd L2CAP congestion status, CID: 0x%x  Cong: %d", l2cap_cid, congested);
710
711    if (congested)
712        p_hcon->conn_flags |= HID_CONN_FLAGS_CONGESTED;
713    else
714    {
715        p_hcon->conn_flags &= ~HID_CONN_FLAGS_CONGESTED;
716
717    }
718}
719
720
721/*******************************************************************************
722**
723** Function         hidh_l2cif_data_ind
724**
725** Description      This function is called when data is received from L2CAP.
726**                  if we are the originator of the connection, we are the SDP
727**                  client, and the received message is queued up for the client.
728**
729**                  If we are the destination of the connection, we are the SDP
730**                  server, so the message is passed to the server processing
731**                  function.
732**
733** Returns          void
734**
735*******************************************************************************/
736static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg)
737{
738    UINT8           *p_data = (UINT8 *)(p_msg + 1) + p_msg->offset;
739    UINT8           ttype, param, rep_type, evt;
740    UINT8 dhandle;
741    tHID_CONN    *p_hcon = NULL;
742
743    HIDH_TRACE_DEBUG1 ("HID - hidh_l2cif_data_ind [l2cap_cid=0x%04x]", l2cap_cid);
744
745    /* Find CCB based on CID */
746     if ((dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES)
747        p_hcon = &hh_cb.devices[dhandle].conn;
748
749    if (p_hcon == NULL)
750    {
751        HIDH_TRACE_WARNING1 ("HID - Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid);
752        GKI_freebuf (p_msg);
753        return;
754    }
755
756
757    ttype    = HID_GET_TRANS_FROM_HDR(*p_data);
758    param    = HID_GET_PARAM_FROM_HDR(*p_data);
759    rep_type = param & HID_PAR_REP_TYPE_MASK;
760    p_data++;
761
762    /* Get rid of the data type */
763    p_msg->len--;
764    p_msg->offset++;
765
766    switch (ttype)
767    {
768    case HID_TRANS_HANDSHAKE:
769        hh_cb.callback(dhandle, HID_HDEV_EVT_HANDSHAKE, param, NULL);
770        GKI_freebuf (p_msg);
771        break;
772
773    case HID_TRANS_CONTROL:
774        switch (param)
775        {
776        case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG:
777            hidh_conn_disconnect( dhandle ) ;
778            /* Device is unplugging from us. Tell USB */
779            hh_cb.callback(dhandle, HID_HDEV_EVT_VC_UNPLUG, 0, NULL);
780            break;
781
782        default:
783            break;
784        }
785        GKI_freebuf (p_msg);
786        break;
787
788
789    case HID_TRANS_DATA:
790        evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ?
791                    HID_HDEV_EVT_INTR_DATA : HID_HDEV_EVT_CTRL_DATA;
792        hh_cb.callback(dhandle, evt, rep_type, p_msg);
793        break;
794
795    case HID_TRANS_DATAC:
796        evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ?
797                    HID_HDEV_EVT_INTR_DATC : HID_HDEV_EVT_CTRL_DATC;
798        hh_cb.callback(dhandle, evt, rep_type, p_msg);
799        break;
800
801    default:
802        GKI_freebuf (p_msg);
803        break;
804    }
805
806}
807
808/*******************************************************************************
809**
810** Function         hidh_conn_snd_data
811**
812** Description      This function is sends out data.
813**
814** Returns          tHID_STATUS
815**
816*******************************************************************************/
817tHID_STATUS hidh_conn_snd_data (UINT8 dhandle, UINT8 trans_type, UINT8 param,
818                                UINT16 data, UINT8 report_id, BT_HDR *buf)
819{
820    tHID_CONN   *p_hcon = &hh_cb.devices[dhandle].conn;
821    BT_HDR      *p_buf;
822    UINT8       *p_out;
823    UINT16      bytes_copied;
824    BOOLEAN     seg_req = FALSE;
825    UINT16      data_size;
826    UINT16      cid;
827    UINT8       pool_id;
828    UINT8       use_data = 0 ;
829    BOOLEAN     blank_datc = FALSE;
830
831    if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED)
832    {
833        if (buf)
834            GKI_freebuf ((void *)buf);
835        return( HID_ERR_CONGESTED );
836    }
837
838    switch( trans_type )
839    {
840    case HID_TRANS_CONTROL:
841    case HID_TRANS_GET_REPORT:
842    case HID_TRANS_SET_REPORT:
843    case HID_TRANS_GET_PROTOCOL:
844    case HID_TRANS_SET_PROTOCOL:
845    case HID_TRANS_GET_IDLE:
846    case HID_TRANS_SET_IDLE:
847        cid = p_hcon->ctrl_cid;
848        pool_id = HID_CONTROL_POOL_ID;
849        break;
850    case HID_TRANS_DATA:
851        cid = p_hcon->intr_cid;
852        pool_id = HID_INTERRUPT_POOL_ID;
853        break;
854    default:
855        return (HID_ERR_INVALID_PARAM) ;
856    }
857
858    if( trans_type == HID_TRANS_SET_IDLE )
859        use_data = 1;
860    else if( (trans_type == HID_TRANS_GET_REPORT) && (param & 0x08) )
861        use_data = 2;
862
863    do
864    {
865        if ( buf == NULL || blank_datc )
866        {
867            if((p_buf = (BT_HDR *)GKI_getpoolbuf (pool_id)) == NULL)
868                return (HID_ERR_NO_RESOURCES);
869
870            p_buf->offset = L2CAP_MIN_OFFSET;
871            seg_req = FALSE;
872            data_size = 0;
873            bytes_copied = 0;
874            blank_datc = FALSE;
875        }
876        else if ( (buf->len > (p_hcon->rem_mtu_size - 1)))
877        {
878            if((p_buf = (BT_HDR *)GKI_getpoolbuf (pool_id)) == NULL)
879                return (HID_ERR_NO_RESOURCES);
880
881            p_buf->offset = L2CAP_MIN_OFFSET;
882            seg_req = TRUE;
883            data_size = buf->len;
884            bytes_copied = p_hcon->rem_mtu_size - 1;
885        }
886        else
887        {
888            p_buf = buf ;
889            p_buf->offset -= 1;
890            seg_req = FALSE;
891            data_size = buf->len;
892            bytes_copied = buf->len;
893        }
894
895        p_out         = (UINT8 *)(p_buf + 1) + p_buf->offset;
896        *p_out++      = HID_BUILD_HDR(trans_type, param);
897
898        /* If report ID required for this device */
899        if( (trans_type == HID_TRANS_GET_REPORT) && (report_id != 0) )
900        {
901            *p_out = report_id;
902            data_size = bytes_copied = 1;
903        }
904
905
906        if (seg_req)
907        {
908            memcpy (p_out, (((UINT8 *)(buf+1)) + buf->offset), bytes_copied);
909            buf->offset += bytes_copied;
910            buf->len -= bytes_copied;
911        }
912        else if( use_data == 1)
913        {
914            *(p_out+bytes_copied) = data & 0xff;
915        }
916        else if( use_data == 2 )
917        {
918            *(p_out+bytes_copied) = data & 0xff;
919            *(p_out+bytes_copied+1) = (data >> 8) & 0xff ;
920        }
921
922        p_buf->len   = bytes_copied + 1 + use_data;
923        data_size    -= bytes_copied;
924
925        /* Send the buffer through L2CAP */
926        if ((p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) || (!L2CA_DataWrite (cid, p_buf)))
927            return (HID_ERR_CONGESTED);
928
929        if (data_size)
930            trans_type = HID_TRANS_DATAC;
931        else if( bytes_copied == (p_hcon->rem_mtu_size - 1) )
932        {
933            trans_type = HID_TRANS_DATAC;
934            blank_datc = TRUE;
935        }
936
937    } while ((data_size != 0) || blank_datc ) ;
938
939    return (HID_SUCCESS);
940}
941/*******************************************************************************
942**
943** Function         hidh_conn_initiate
944**
945** Description      This function is called by the management to create a connection.
946**
947** Returns          void
948**
949*******************************************************************************/
950tHID_STATUS hidh_conn_initiate (UINT8 dhandle)
951{
952    UINT8   service_id = BTM_SEC_SERVICE_HID_NOSEC_CTRL;
953    UINT32  mx_chan_id = HID_NOSEC_CHN;
954
955    tHID_HOST_DEV_CTB *p_dev = &hh_cb.devices[dhandle];
956
957    if( p_dev->conn.conn_state != HID_CONN_STATE_UNUSED )
958        return( HID_ERR_CONN_IN_PROCESS );
959
960    p_dev->conn.ctrl_cid = 0;
961    p_dev->conn.intr_cid = 0;
962    p_dev->conn.disc_reason = HID_L2CAP_CONN_FAIL;  /* Reset initial reason for CLOSE_EVT: Connection Attempt was made but failed */
963
964    /* We are the originator of this connection */
965    p_dev->conn.conn_flags = HID_CONN_FLAGS_IS_ORIG;
966
967    if(p_dev->attr_mask & HID_SEC_REQUIRED)
968    {
969        service_id = BTM_SEC_SERVICE_HID_SEC_CTRL;
970        mx_chan_id = HID_SEC_CHN;
971    }
972    BTM_SetOutService (p_dev->addr, service_id, mx_chan_id);
973
974    /* Check if L2CAP started the connection process */
975    if ((p_dev->conn.ctrl_cid = L2CA_ConnectReq (HID_PSM_CONTROL, p_dev->addr)) == 0)
976    {
977        HIDH_TRACE_WARNING0 ("HID - Originate failed");
978        dhandle = (p_dev - &(hh_cb.devices[0]))/(sizeof( tHID_HOST_DEV_CTB )) ;
979        hh_cb.callback( dhandle, HID_HDEV_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL ) ;
980    }
981    else
982    {
983        /* Transition to the next appropriate state, waiting for connection confirm on control channel. */
984        p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL;
985    }
986
987    return( HID_SUCCESS );
988}
989
990
991/*******************************************************************************
992**
993** Function         find_conn_by_cid
994**
995** Description      This function finds a connection control block based on CID
996**
997** Returns          address of control block, or NULL if not found
998**
999*******************************************************************************/
1000static UINT8 find_conn_by_cid (UINT16 cid)
1001{
1002    UINT8      xx;
1003
1004    for (xx = 0; xx < HID_HOST_MAX_DEVICES; xx++)
1005    {
1006        if ((hh_cb.devices[xx].in_use) && (hh_cb.devices[xx].conn.conn_state != HID_CONN_STATE_UNUSED)
1007            && ((hh_cb.devices[xx].conn.ctrl_cid == cid) || (hh_cb.devices[xx].conn.intr_cid == cid)))
1008            break;
1009    }
1010
1011    return (xx);
1012}
1013
1014void hidh_conn_dereg( void )
1015{
1016    L2CA_Deregister (HID_PSM_CONTROL);
1017    L2CA_Deregister (HID_PSM_INTERRUPT);
1018}
1019
1020/*******************************************************************************
1021**
1022** Function         hidh_conn_retry
1023**
1024** Description      This function is called to retry a failed connection.
1025**
1026** Returns          void
1027**
1028*******************************************************************************/
1029static void hidh_conn_retry(  UINT8 dhandle )
1030{
1031    tHID_HOST_DEV_CTB *p_dev = &hh_cb.devices[dhandle];
1032
1033    p_dev->conn.conn_state = HID_CONN_STATE_UNUSED;
1034    p_dev->conn.timer_entry.param = (UINT32) dhandle;
1035#if (HID_HOST_REPAGE_WIN > 0)
1036    btu_start_timer (&(p_dev->conn.timer_entry), BTU_TTYPE_HID_HOST_REPAGE_TO, HID_HOST_REPAGE_WIN);
1037#else
1038    hidh_proc_repage_timeout( &(p_dev->conn.timer_entry) );
1039#endif
1040}
1041