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