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