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