bta_hh_co.cc revision 80d7f60680f483a71e413f2453ab20013aff5c5c
1/****************************************************************************** 2 * 3 * Copyright (C) 2009-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#include <assert.h> 20#include <ctype.h> 21#include <errno.h> 22#include <fcntl.h> 23#include <linux/uhid.h> 24#include <pthread.h> 25#include <stdio.h> 26#include <string.h> 27#include <stdint.h> 28#include <sys/poll.h> 29#include <unistd.h> 30 31#include "btcore/include/bdaddr.h" 32#include "osi/include/osi.h" 33#include "bta_api.h" 34#include "bta_hh_api.h" 35#include "bta_hh_co.h" 36#include "btif_hh.h" 37#include "btif_util.h" 38 39const char *dev_path = "/dev/uhid"; 40 41#if (BLE_INCLUDED == TRUE && BTA_HH_LE_INCLUDED == TRUE) 42#include "btif_config.h" 43#define BTA_HH_NV_LOAD_MAX 16 44static tBTA_HH_RPT_CACHE_ENTRY sReportCache[BTA_HH_NV_LOAD_MAX]; 45#endif 46 47void uhid_set_non_blocking(int fd) 48{ 49 int opts = fcntl(fd, F_GETFL); 50 if (opts < 0) 51 APPL_TRACE_ERROR("%s() Getting flags failed (%s)", __func__, strerror(errno)); 52 53 opts |= O_NONBLOCK; 54 55 if (fcntl(fd, F_SETFL, opts) < 0) 56 APPL_TRACE_EVENT("%s() Setting non-blocking flag failed (%s)", __func__, strerror(errno)); 57} 58 59/*Internal function to perform UHID write and error checking*/ 60static int uhid_write(int fd, const struct uhid_event *ev) 61{ 62 ssize_t ret; 63 OSI_NO_INTR(ret = write(fd, ev, sizeof(*ev))); 64 65 if (ret < 0){ 66 int rtn = -errno; 67 APPL_TRACE_ERROR("%s: Cannot write to uhid:%s", 68 __func__, strerror(errno)); 69 return rtn; 70 } else if (ret != (ssize_t)sizeof(*ev)) { 71 APPL_TRACE_ERROR("%s: Wrong size written to uhid: %zd != %zu", 72 __func__, ret, sizeof(*ev)); 73 return -EFAULT; 74 } 75 76 return 0; 77} 78 79/* Internal function to parse the events received from UHID driver*/ 80static int uhid_read_event(btif_hh_device_t *p_dev) 81{ 82 assert(p_dev); 83 84 struct uhid_event ev; 85 memset(&ev, 0, sizeof(ev)); 86 87 ssize_t ret; 88 OSI_NO_INTR(ret = read(p_dev->fd, &ev, sizeof(ev))); 89 90 if (ret == 0) { 91 APPL_TRACE_ERROR("%s: Read HUP on uhid-cdev %s", __func__, 92 strerror(errno)); 93 return -EFAULT; 94 } else if (ret < 0) { 95 APPL_TRACE_ERROR("%s: Cannot read uhid-cdev: %s", __func__, 96 strerror(errno)); 97 return -errno; 98 } 99 100 switch (ev.type) { 101 case UHID_START: 102 APPL_TRACE_DEBUG("UHID_START from uhid-dev\n"); 103 p_dev->ready_for_data = true; 104 break; 105 case UHID_STOP: 106 APPL_TRACE_DEBUG("UHID_STOP from uhid-dev\n"); 107 p_dev->ready_for_data = false; 108 break; 109 case UHID_OPEN: 110 APPL_TRACE_DEBUG("UHID_OPEN from uhid-dev\n"); 111 p_dev->ready_for_data = true; 112 break; 113 case UHID_CLOSE: 114 APPL_TRACE_DEBUG("UHID_CLOSE from uhid-dev\n"); 115 p_dev->ready_for_data = false; 116 break; 117 case UHID_OUTPUT: 118 if (ret < (ssize_t)(sizeof(ev.type) + sizeof(ev.u.output))) { 119 APPL_TRACE_ERROR("%s: Invalid size read from uhid-dev: %zd < %zu", 120 __func__, ret, 121 sizeof(ev.type) + sizeof(ev.u.output)); 122 return -EFAULT; 123 } 124 125 APPL_TRACE_DEBUG("UHID_OUTPUT: Report type = %d, report_size = %d" 126 ,ev.u.output.rtype, ev.u.output.size); 127 //Send SET_REPORT with feature report if the report type in output event is FEATURE 128 if(ev.u.output.rtype == UHID_FEATURE_REPORT) 129 btif_hh_setreport(p_dev, BTHH_FEATURE_REPORT, 130 ev.u.output.size, ev.u.output.data); 131 else if(ev.u.output.rtype == UHID_OUTPUT_REPORT) 132 btif_hh_setreport(p_dev, BTHH_OUTPUT_REPORT, 133 ev.u.output.size, ev.u.output.data); 134 else 135 btif_hh_setreport(p_dev, BTHH_INPUT_REPORT, 136 ev.u.output.size, ev.u.output.data); 137 break; 138 case UHID_OUTPUT_EV: 139 if (ret < (ssize_t)(sizeof(ev.type) + sizeof(ev.u.output_ev))) { 140 APPL_TRACE_ERROR("%s: Invalid size read from uhid-dev: %zd < %zu", 141 __func__, ret, sizeof(ev.type) + sizeof(ev.u.output_ev)); 142 return -EFAULT; 143 } 144 APPL_TRACE_DEBUG("UHID_OUTPUT_EV from uhid-dev\n"); 145 break; 146 case UHID_FEATURE: 147 APPL_TRACE_DEBUG("UHID_FEATURE from uhid-dev\n"); 148 break; 149 case UHID_FEATURE_ANSWER: 150 APPL_TRACE_DEBUG("UHID_FEATURE_ANSWER from uhid-dev\n"); 151 break; 152 153 default: 154 APPL_TRACE_DEBUG("Invalid event from uhid-dev: %u\n", ev.type); 155 } 156 157 return 0; 158} 159 160/******************************************************************************* 161** 162** Function create_thread 163** 164** Description creat a select loop 165** 166** Returns pthread_t 167** 168*******************************************************************************/ 169static inline pthread_t create_thread(void *(*start_routine)(void *), void * arg){ 170 APPL_TRACE_DEBUG("create_thread: entered"); 171 pthread_attr_t thread_attr; 172 173 pthread_attr_init(&thread_attr); 174 pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE); 175 pthread_t thread_id = -1; 176 if ( pthread_create(&thread_id, &thread_attr, start_routine, arg)!=0 ) 177 { 178 APPL_TRACE_ERROR("pthread_create : %s", strerror(errno)); 179 return -1; 180 } 181 APPL_TRACE_DEBUG("create_thread: thread created successfully"); 182 return thread_id; 183} 184 185/******************************************************************************* 186** 187** Function btif_hh_poll_event_thread 188** 189** Description the polling thread which polls for event from UHID driver 190** 191** Returns void 192** 193*******************************************************************************/ 194static void *btif_hh_poll_event_thread(void *arg) 195{ 196 btif_hh_device_t *p_dev = (btif_hh_device_t*)arg; 197 APPL_TRACE_DEBUG("%s: Thread created fd = %d", __func__, p_dev->fd); 198 struct pollfd pfds[1]; 199 200 pfds[0].fd = p_dev->fd; 201 pfds[0].events = POLLIN; 202 203 // Set the uhid fd as non-blocking to ensure we never block the BTU thread 204 uhid_set_non_blocking(p_dev->fd); 205 206 while(p_dev->hh_keep_polling){ 207 int ret; 208 OSI_NO_INTR(ret = poll(pfds, 1, 50)); 209 if (ret < 0) { 210 APPL_TRACE_ERROR("%s: Cannot poll for fds: %s\n", __func__, strerror(errno)); 211 break; 212 } 213 if (pfds[0].revents & POLLIN) { 214 APPL_TRACE_DEBUG("%s: POLLIN", __func__); 215 ret = uhid_read_event(p_dev); 216 if (ret != 0) 217 break; 218 } 219 } 220 221 p_dev->hh_poll_thread_id = -1; 222 return 0; 223} 224 225static inline void btif_hh_close_poll_thread(btif_hh_device_t *p_dev) 226{ 227 APPL_TRACE_DEBUG("%s", __func__); 228 p_dev->hh_keep_polling = 0; 229 if(p_dev->hh_poll_thread_id > 0) 230 pthread_join(p_dev->hh_poll_thread_id,NULL); 231 232 return; 233} 234 235void bta_hh_co_destroy(int fd) 236{ 237 struct uhid_event ev; 238 memset(&ev, 0, sizeof(ev)); 239 ev.type = UHID_DESTROY; 240 uhid_write(fd, &ev); 241 APPL_TRACE_DEBUG("%s: Closing fd=%d", __func__, fd); 242 close(fd); 243} 244 245int bta_hh_co_write(int fd, uint8_t* rpt, uint16_t len) 246{ 247 APPL_TRACE_DEBUG("%s: UHID write %d", __func__, len); 248 249 struct uhid_event ev; 250 memset(&ev, 0, sizeof(ev)); 251 ev.type = UHID_INPUT; 252 ev.u.input.size = len; 253 if(len > sizeof(ev.u.input.data)){ 254 APPL_TRACE_WARNING("%s: Report size greater than allowed size", 255 __func__); 256 return -1; 257 } 258 memcpy(ev.u.input.data, rpt, len); 259 260 return uhid_write(fd, &ev); 261 262} 263 264 265/******************************************************************************* 266** 267** Function bta_hh_co_open 268** 269** Description When connection is opened, this call-out function is executed 270** by HH to do platform specific initialization. 271** 272** Returns void. 273*******************************************************************************/ 274void bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class, tBTA_HH_ATTR_MASK attr_mask, 275 uint8_t app_id) 276{ 277 uint32_t i; 278 btif_hh_device_t *p_dev = NULL; 279 280 if (dev_handle == BTA_HH_INVALID_HANDLE) { 281 APPL_TRACE_WARNING("%s: Oops, dev_handle (%d) is invalid...", 282 __func__, dev_handle); 283 return; 284 } 285 286 for (i = 0; i < BTIF_HH_MAX_HID; i++) { 287 p_dev = &btif_hh_cb.devices[i]; 288 if (p_dev->dev_status != BTHH_CONN_STATE_UNKNOWN && 289 p_dev->dev_handle == dev_handle) { 290 // We found a device with the same handle. Must be a device reconnected. 291 APPL_TRACE_WARNING("%s: Found an existing device with the same handle " 292 "dev_status = %d",__func__, 293 p_dev->dev_status); 294 APPL_TRACE_WARNING("%s: bd_addr = [%02X:%02X:%02X:%02X:%02X:]", __func__, 295 p_dev->bd_addr.address[0], p_dev->bd_addr.address[1], p_dev->bd_addr.address[2], 296 p_dev->bd_addr.address[3], p_dev->bd_addr.address[4]); 297 APPL_TRACE_WARNING("%s: attr_mask = 0x%04x, sub_class = 0x%02x, app_id = %d", 298 __func__, p_dev->attr_mask, p_dev->sub_class, p_dev->app_id); 299 300 if(p_dev->fd<0) { 301 p_dev->fd = open(dev_path, O_RDWR | O_CLOEXEC); 302 if (p_dev->fd < 0){ 303 APPL_TRACE_ERROR("%s: Error: failed to open uhid, err:%s", 304 __func__,strerror(errno)); 305 return; 306 }else 307 APPL_TRACE_DEBUG("%s: uhid fd = %d", __func__, p_dev->fd); 308 } 309 310 p_dev->hh_keep_polling = 1; 311 p_dev->hh_poll_thread_id = create_thread(btif_hh_poll_event_thread, p_dev); 312 break; 313 } 314 p_dev = NULL; 315 } 316 317 if (p_dev == NULL) { 318 // Did not find a device reconnection case. Find an empty slot now. 319 for (i = 0; i < BTIF_HH_MAX_HID; i++) { 320 if (btif_hh_cb.devices[i].dev_status == BTHH_CONN_STATE_UNKNOWN) { 321 p_dev = &btif_hh_cb.devices[i]; 322 p_dev->dev_handle = dev_handle; 323 p_dev->attr_mask = attr_mask; 324 p_dev->sub_class = sub_class; 325 p_dev->app_id = app_id; 326 p_dev->local_vup = false; 327 328 btif_hh_cb.device_num++; 329 // This is a new device,open the uhid driver now. 330 p_dev->fd = open(dev_path, O_RDWR | O_CLOEXEC); 331 if (p_dev->fd < 0){ 332 APPL_TRACE_ERROR("%s: Error: failed to open uhid, err:%s", 333 __func__,strerror(errno)); 334 return; 335 }else{ 336 APPL_TRACE_DEBUG("%s: uhid fd = %d", __func__, p_dev->fd); 337 p_dev->hh_keep_polling = 1; 338 p_dev->hh_poll_thread_id = create_thread(btif_hh_poll_event_thread, p_dev); 339 } 340 341 342 break; 343 } 344 } 345 } 346 347 if (p_dev == NULL) { 348 APPL_TRACE_ERROR("%s: Error: too many HID devices are connected", __func__); 349 return; 350 } 351 352 p_dev->dev_status = BTHH_CONN_STATE_CONNECTED; 353 APPL_TRACE_DEBUG("%s: Return device status %d", __func__, p_dev->dev_status); 354} 355 356 357/******************************************************************************* 358** 359** Function bta_hh_co_close 360** 361** Description When connection is closed, this call-out function is executed 362** by HH to do platform specific finalization. 363** 364** Parameters dev_handle - device handle 365** app_id - application id 366** 367** Returns void. 368*******************************************************************************/ 369void bta_hh_co_close(uint8_t dev_handle, uint8_t app_id) 370{ 371 uint32_t i; 372 btif_hh_device_t *p_dev = NULL; 373 374 APPL_TRACE_WARNING("%s: dev_handle = %d, app_id = %d", __func__, dev_handle, app_id); 375 if (dev_handle == BTA_HH_INVALID_HANDLE) { 376 APPL_TRACE_WARNING("%s: Oops, dev_handle (%d) is invalid...", __func__, dev_handle); 377 return; 378 } 379 380 for (i = 0; i < BTIF_HH_MAX_HID; i++) { 381 p_dev = &btif_hh_cb.devices[i]; 382 if (p_dev->dev_status != BTHH_CONN_STATE_UNKNOWN && p_dev->dev_handle == dev_handle) { 383 APPL_TRACE_WARNING("%s: Found an existing device with the same handle " 384 "dev_status = %d, dev_handle =%d" 385 ,__func__,p_dev->dev_status 386 ,p_dev->dev_handle); 387 btif_hh_close_poll_thread(p_dev); 388 break; 389 } 390 } 391} 392 393 394/******************************************************************************* 395** 396** Function bta_hh_co_data 397** 398** Description This function is executed by BTA when HID host receive a data 399** report. 400** 401** Parameters dev_handle - device handle 402** *p_rpt - pointer to the report data 403** len - length of report data 404** mode - Hid host Protocol Mode 405** sub_clas - Device Subclass 406** app_id - application id 407** 408** Returns void 409*******************************************************************************/ 410void bta_hh_co_data(uint8_t dev_handle, uint8_t *p_rpt, uint16_t len, tBTA_HH_PROTO_MODE mode, 411 uint8_t sub_class, uint8_t ctry_code, BD_ADDR peer_addr, uint8_t app_id) 412{ 413 btif_hh_device_t *p_dev; 414 UNUSED(peer_addr); 415 416 APPL_TRACE_DEBUG("%s: dev_handle = %d, subclass = 0x%02X, mode = %d, " 417 "ctry_code = %d, app_id = %d", 418 __func__, dev_handle, sub_class, mode, ctry_code, app_id); 419 420 p_dev = btif_hh_find_connected_dev_by_handle(dev_handle); 421 if (p_dev == NULL) { 422 APPL_TRACE_WARNING("%s: Error: unknown HID device handle %d", __func__, dev_handle); 423 return; 424 } 425 426 // Wait a maximum of MAX_POLLING_ATTEMPTS x POLLING_SLEEP_DURATION in case 427 // device creation is pending. 428 if (p_dev->fd >= 0) { 429 uint32_t polling_attempts = 0; 430 while (!p_dev->ready_for_data && polling_attempts++ < BTIF_HH_MAX_POLLING_ATTEMPTS) { 431 usleep(BTIF_HH_POLLING_SLEEP_DURATION_US); 432 } 433 } 434 435 // Send the HID data to the kernel. 436 if ((p_dev->fd >= 0) && p_dev->ready_for_data) { 437 bta_hh_co_write(p_dev->fd, p_rpt, len); 438 }else { 439 APPL_TRACE_WARNING("%s: Error: fd = %d, ready %d, len = %d", __func__, p_dev->fd, 440 p_dev->ready_for_data, len); 441 } 442} 443 444 445/******************************************************************************* 446** 447** Function bta_hh_co_send_hid_info 448** 449** Description This function is called in btif_hh.c to process DSCP received. 450** 451** Parameters dev_handle - device handle 452** dscp_len - report descriptor length 453** *p_dscp - report descriptor 454** 455** Returns void 456*******************************************************************************/ 457void bta_hh_co_send_hid_info(btif_hh_device_t *p_dev, const char *dev_name, uint16_t vendor_id, 458 uint16_t product_id, uint16_t version, uint8_t ctry_code, 459 int dscp_len, uint8_t *p_dscp) 460{ 461 int result; 462 struct uhid_event ev; 463 464 if (p_dev->fd < 0) { 465 APPL_TRACE_WARNING("%s: Error: fd = %d, dscp_len = %d", __func__, p_dev->fd, dscp_len); 466 return; 467 } 468 469 APPL_TRACE_WARNING("%s: fd = %d, name = [%s], dscp_len = %d", __func__, 470 p_dev->fd, dev_name, dscp_len); 471 APPL_TRACE_WARNING("%s: vendor_id = 0x%04x, product_id = 0x%04x, version= 0x%04x," 472 "ctry_code=0x%02x",__func__, 473 vendor_id, product_id, 474 version, ctry_code); 475 476 //Create and send hid descriptor to kernel 477 memset(&ev, 0, sizeof(ev)); 478 ev.type = UHID_CREATE; 479 strncpy((char*)ev.u.create.name, dev_name, sizeof(ev.u.create.name) - 1); 480 snprintf((char*)ev.u.create.uniq, sizeof(ev.u.create.uniq), 481 "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", 482 p_dev->bd_addr.address[5], p_dev->bd_addr.address[4], 483 p_dev->bd_addr.address[3], p_dev->bd_addr.address[2], 484 p_dev->bd_addr.address[1], p_dev->bd_addr.address[0]); 485 ev.u.create.rd_size = dscp_len; 486 ev.u.create.rd_data = p_dscp; 487 ev.u.create.bus = BUS_BLUETOOTH; 488 ev.u.create.vendor = vendor_id; 489 ev.u.create.product = product_id; 490 ev.u.create.version = version; 491 ev.u.create.country = ctry_code; 492 result = uhid_write(p_dev->fd, &ev); 493 494 APPL_TRACE_WARNING("%s: wrote descriptor to fd = %d, dscp_len = %d, result = %d", __func__, 495 p_dev->fd, dscp_len, result); 496 497 if (result) { 498 APPL_TRACE_WARNING("%s: Error: failed to send DSCP, result = %d", __func__, result); 499 500 /* The HID report descriptor is corrupted. Close the driver. */ 501 close(p_dev->fd); 502 p_dev->fd = -1; 503 } 504} 505 506#if (BLE_INCLUDED == TRUE && BTA_HH_LE_INCLUDED == TRUE) 507/******************************************************************************* 508** 509** Function bta_hh_le_co_rpt_info 510** 511** Description This callout function is to convey the report information on 512** a HOGP device to the application. Application can save this 513** information in NV if device is bonded and load it back when 514** stack reboot. 515** 516** Parameters remote_bda - remote device address 517** p_entry - report entry pointer 518** app_id - application id 519** 520** Returns void. 521** 522*******************************************************************************/ 523void bta_hh_le_co_rpt_info(BD_ADDR remote_bda, tBTA_HH_RPT_CACHE_ENTRY *p_entry, uint8_t app_id) 524{ 525 UNUSED(app_id); 526 527 unsigned idx = 0; 528 529 bdstr_t bdstr; 530 snprintf(bdstr, sizeof(bdstr), "%02x:%02x:%02x:%02x:%02x:%02x", 531 remote_bda[0], remote_bda[1], remote_bda[2], 532 remote_bda[3], remote_bda[4], remote_bda[5]); 533 534 size_t len = btif_config_get_bin_length(bdstr, "HidReport"); 535 if (len >= sizeof(tBTA_HH_RPT_CACHE_ENTRY) && len <= sizeof(sReportCache)) 536 { 537 btif_config_get_bin(bdstr, "HidReport", (uint8_t *)sReportCache, &len); 538 idx = len / sizeof(tBTA_HH_RPT_CACHE_ENTRY); 539 } 540 541 if (idx < BTA_HH_NV_LOAD_MAX) 542 { 543 memcpy(&sReportCache[idx++], p_entry, sizeof(tBTA_HH_RPT_CACHE_ENTRY)); 544 btif_config_set_bin(bdstr, "HidReport", (const uint8_t *)sReportCache, 545 idx * sizeof(tBTA_HH_RPT_CACHE_ENTRY)); 546 BTIF_TRACE_DEBUG("%s() - Saving report; dev=%s, idx=%d", __func__, bdstr, idx); 547 } 548} 549 550 551/******************************************************************************* 552** 553** Function bta_hh_le_co_cache_load 554** 555** Description This callout function is to request the application to load the 556** cached HOGP report if there is any. When cache reading is completed, 557** bta_hh_le_ci_cache_load() is called by the application. 558** 559** Parameters remote_bda - remote device address 560** p_num_rpt: number of cached report 561** app_id - application id 562** 563** Returns the acched report array 564** 565*******************************************************************************/ 566tBTA_HH_RPT_CACHE_ENTRY * bta_hh_le_co_cache_load (BD_ADDR remote_bda, 567 uint8_t *p_num_rpt, uint8_t app_id) 568{ 569 UNUSED(app_id); 570 571 bdstr_t bdstr; 572 snprintf(bdstr, sizeof(bdstr), "%02x:%02x:%02x:%02x:%02x:%02x", 573 remote_bda[0], remote_bda[1], remote_bda[2], 574 remote_bda[3], remote_bda[4], remote_bda[5]); 575 576 size_t len = btif_config_get_bin_length(bdstr, "HidReport"); 577 if (!p_num_rpt && len < sizeof(tBTA_HH_RPT_CACHE_ENTRY)) 578 return NULL; 579 580 if (len > sizeof(sReportCache)) 581 len = sizeof(sReportCache); 582 btif_config_get_bin(bdstr, "HidReport", (uint8_t *)sReportCache, &len); 583 *p_num_rpt = len / sizeof(tBTA_HH_RPT_CACHE_ENTRY); 584 585 BTIF_TRACE_DEBUG("%s() - Loaded %d reports; dev=%s", __func__, *p_num_rpt, bdstr); 586 587 return sReportCache; 588} 589 590/******************************************************************************* 591** 592** Function bta_hh_le_co_reset_rpt_cache 593** 594** Description This callout function is to reset the HOGP device cache. 595** 596** Parameters remote_bda - remote device address 597** 598** Returns none 599** 600*******************************************************************************/ 601void bta_hh_le_co_reset_rpt_cache (BD_ADDR remote_bda, uint8_t app_id) 602{ 603 UNUSED(app_id); 604 605 bdstr_t bdstr; 606 snprintf(bdstr, sizeof(bdstr), "%02x:%02x:%02x:%02x:%02x:%02x", 607 remote_bda[0], remote_bda[1], remote_bda[2], 608 remote_bda[3], remote_bda[4], remote_bda[5]); 609 btif_config_remove(bdstr, "HidReport"); 610 611 BTIF_TRACE_DEBUG("%s() - Reset cache for bda %s", __func__, bdstr); 612} 613 614#endif // (BLE_INCLUDED == TRUE && BTA_HH_LE_INCLUDED == TRUE) 615 616