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