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