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