1/****************************************************************************** 2 * 3 * Copyright 2016 The Android Open Source Project 4 * Copyright 2002-2012 Broadcom Corporation 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 ******************************************************************************/ 19 20/****************************************************************************** 21 * 22 * This file contains the HID Device API entry points 23 * 24 ******************************************************************************/ 25 26#include <stdio.h> 27#include <stdlib.h> 28#include <string.h> 29 30#include "bt_types.h" 31#include "btm_api.h" 32#include "btu.h" 33#include "hidd_api.h" 34#include "hidd_int.h" 35#include "hiddefs.h" 36 37tHID_DEV_CTB hd_cb; 38 39/******************************************************************************* 40 * 41 * Function HID_DevInit 42 * 43 * Description Initializes control block 44 * 45 * Returns void 46 * 47 ******************************************************************************/ 48void HID_DevInit(void) { 49 uint8_t log_level = hd_cb.trace_level; 50 51 HIDD_TRACE_API("%s", __func__); 52 53 memset(&hd_cb, 0, sizeof(tHID_DEV_CTB)); 54 hd_cb.trace_level = log_level; 55} 56 57/******************************************************************************* 58 * 59 * Function HID_DevSetTraceLevel 60 * 61 * Description This function sets the trace level for HID Dev. If called 62*with 63 * a value of 0xFF, it simply reads the current trace level. 64 * 65 * Returns the new (current) trace level 66 * 67 ******************************************************************************/ 68uint8_t HID_DevSetTraceLevel(uint8_t new_level) { 69 if (new_level != 0xFF) hd_cb.trace_level = new_level; 70 71 return (hd_cb.trace_level); 72} 73 74/******************************************************************************* 75 * 76 * Function HID_DevRegister 77 * 78 * Description Registers HID device with lower layers 79 * 80 * Returns tHID_STATUS 81 * 82 ******************************************************************************/ 83tHID_STATUS HID_DevRegister(tHID_DEV_HOST_CALLBACK* host_cback) { 84 tHID_STATUS st; 85 86 HIDD_TRACE_API("%s", __func__); 87 88 if (hd_cb.reg_flag) return HID_ERR_ALREADY_REGISTERED; 89 90 if (host_cback == NULL) return HID_ERR_INVALID_PARAM; 91 92 /* Register with L2CAP */ 93 st = hidd_conn_reg(); 94 if (st != HID_SUCCESS) return st; 95 96 hd_cb.callback = host_cback; 97 hd_cb.reg_flag = TRUE; 98 99 if (hd_cb.pending_data) { 100 osi_free(hd_cb.pending_data); 101 hd_cb.pending_data = NULL; 102 } 103 104 return (HID_SUCCESS); 105} 106 107/******************************************************************************* 108 * 109 * Function HID_DevDeregister 110 * 111 * Description Deregisters HID device with lower layers 112 * 113 * Returns tHID_STATUS 114 * 115 ******************************************************************************/ 116tHID_STATUS HID_DevDeregister(void) { 117 HIDD_TRACE_API("%s", __func__); 118 119 if (!hd_cb.reg_flag) return (HID_ERR_NOT_REGISTERED); 120 121 hidd_conn_dereg(); 122 123 hd_cb.reg_flag = FALSE; 124 125 return (HID_SUCCESS); 126} 127 128tHID_STATUS HID_DevSetSecurityLevel(uint8_t sec_lvl) { 129 HIDD_TRACE_API("%s", __func__); 130 131 if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_SEC_CTRL, sec_lvl, 132 HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HIDD_SEC_CHN)) { 133 HIDD_TRACE_ERROR("Security Registration 1 failed"); 134 return (HID_ERR_NO_RESOURCES); 135 } 136 137 if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_SEC_CTRL, sec_lvl, 138 HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HIDD_SEC_CHN)) { 139 HIDD_TRACE_ERROR("Security Registration 2 failed"); 140 return (HID_ERR_NO_RESOURCES); 141 } 142 143 if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_NOSEC_CTRL, 144 BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, 145 HIDD_NOSEC_CHN)) { 146 HIDD_TRACE_ERROR("Security Registration 3 failed"); 147 return (HID_ERR_NO_RESOURCES); 148 } 149 150 if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_NOSEC_CTRL, 151 BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, 152 HIDD_NOSEC_CHN)) { 153 HIDD_TRACE_ERROR("Security Registration 4 failed"); 154 return (HID_ERR_NO_RESOURCES); 155 } 156 157 if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_INTR, BTM_SEC_NONE, 158 HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) { 159 HIDD_TRACE_ERROR("Security Registration 5 failed"); 160 return (HID_ERR_NO_RESOURCES); 161 } 162 163 if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_INTR, BTM_SEC_NONE, 164 HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) { 165 HIDD_TRACE_ERROR("Security Registration 6 failed"); 166 return (HID_ERR_NO_RESOURCES); 167 } 168 169 return (HID_SUCCESS); 170} 171 172/******************************************************************************* 173 * 174 * Function HID_DevAddRecord 175 * 176 * Description Creates SDP record for HID device 177 * 178 * Returns tHID_STATUS 179 * 180 ******************************************************************************/ 181tHID_STATUS HID_DevAddRecord(uint32_t handle, char* p_name, char* p_description, 182 char* p_provider, uint16_t subclass, 183 uint16_t desc_len, uint8_t* p_desc_data) { 184 bool result = TRUE; 185 186 HIDD_TRACE_API("%s", __func__); 187 188 // Service Class ID List 189 if (result) { 190 uint16_t uuid = UUID_SERVCLASS_HUMAN_INTERFACE; 191 result &= SDP_AddServiceClassIdList(handle, 1, &uuid); 192 } 193 194 // Protocol Descriptor List 195 if (result) { 196 tSDP_PROTOCOL_ELEM proto_list[2]; 197 198 proto_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; 199 proto_list[0].num_params = 1; 200 proto_list[0].params[0] = BT_PSM_HIDC; 201 202 proto_list[1].protocol_uuid = UUID_PROTOCOL_HIDP; 203 proto_list[1].num_params = 0; 204 205 result &= SDP_AddProtocolList(handle, 2, proto_list); 206 } 207 208 // Language Base Attribute ID List 209 if (result) { 210 result &= SDP_AddLanguageBaseAttrIDList(handle, LANG_ID_CODE_ENGLISH, 211 LANG_ID_CHAR_ENCODE_UTF8, 212 LANGUAGE_BASE_ID); 213 } 214 215 // Additional Protocol Descriptor List 216 if (result) { 217 tSDP_PROTO_LIST_ELEM add_proto_list; 218 219 add_proto_list.num_elems = 2; 220 add_proto_list.list_elem[0].protocol_uuid = UUID_PROTOCOL_L2CAP; 221 add_proto_list.list_elem[0].num_params = 1; 222 add_proto_list.list_elem[0].params[0] = BT_PSM_HIDI; 223 add_proto_list.list_elem[1].protocol_uuid = UUID_PROTOCOL_HIDP; 224 add_proto_list.list_elem[1].num_params = 0; 225 226 result &= SDP_AddAdditionProtoLists(handle, 1, &add_proto_list); 227 } 228 229 // Service Name (O) 230 // Service Description (O) 231 // Provider Name (O) 232 if (result) { 233 const char* srv_name = p_name; 234 const char* srv_desc = p_description; 235 const char* provider_name = p_provider; 236 237 result &= SDP_AddAttribute(handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, 238 strlen(srv_name) + 1, (uint8_t*)srv_name); 239 240 result &= SDP_AddAttribute(handle, ATTR_ID_SERVICE_DESCRIPTION, 241 TEXT_STR_DESC_TYPE, strlen(srv_desc) + 1, 242 (uint8_t*)srv_desc); 243 244 result &= 245 SDP_AddAttribute(handle, ATTR_ID_PROVIDER_NAME, TEXT_STR_DESC_TYPE, 246 strlen(provider_name) + 1, (uint8_t*)provider_name); 247 } 248 249 // Bluetooth Profile Descriptor List 250 if (result) { 251 const uint16_t profile_uuid = UUID_SERVCLASS_HUMAN_INTERFACE; 252 const uint16_t version = 0x0100; 253 254 result &= SDP_AddProfileDescriptorList(handle, profile_uuid, version); 255 } 256 257 // HID Parser Version 258 if (result) { 259 uint8_t* p; 260 const uint16_t rel_num = 0x0100; 261 const uint16_t parser_version = 0x0111; 262 const uint16_t prof_ver = 0x0100; 263 const uint8_t dev_subclass = subclass; 264 const uint8_t country_code = 0x21; 265 const uint8_t bool_false = 0x00; 266 const uint8_t bool_true = 0x01; 267 uint16_t temp; 268 269 p = (uint8_t*)&temp; 270 UINT16_TO_BE_STREAM(p, rel_num); 271 result &= SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_RELNUM, 272 UINT_DESC_TYPE, 2, (uint8_t*)&temp); 273 274 p = (uint8_t*)&temp; 275 UINT16_TO_BE_STREAM(p, parser_version); 276 result &= SDP_AddAttribute(handle, ATTR_ID_HID_PARSER_VERSION, 277 UINT_DESC_TYPE, 2, (uint8_t*)&temp); 278 279 result &= SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_SUBCLASS, 280 UINT_DESC_TYPE, 1, (uint8_t*)&dev_subclass); 281 282 result &= SDP_AddAttribute(handle, ATTR_ID_HID_COUNTRY_CODE, UINT_DESC_TYPE, 283 1, (uint8_t*)&country_code); 284 285 result &= SDP_AddAttribute(handle, ATTR_ID_HID_VIRTUAL_CABLE, 286 BOOLEAN_DESC_TYPE, 1, (uint8_t*)&bool_true); 287 288 result &= SDP_AddAttribute(handle, ATTR_ID_HID_RECONNECT_INITIATE, 289 BOOLEAN_DESC_TYPE, 1, (uint8_t*)&bool_true); 290 291 { 292 static uint8_t cdt = 0x22; 293 uint8_t* p_buf; 294 uint8_t seq_len = 4 + desc_len; 295 296 p_buf = (uint8_t*)osi_malloc(2048); 297 298 if (p_buf == NULL) { 299 HIDD_TRACE_ERROR("%s: Buffer allocation failure for size = 2048 ", 300 __func__); 301 return HID_ERR_NOT_REGISTERED; 302 } 303 304 p = p_buf; 305 306 UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); 307 308 UINT8_TO_BE_STREAM(p, seq_len); 309 310 UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_ONE_BYTE); 311 UINT8_TO_BE_STREAM(p, cdt); 312 313 UINT8_TO_BE_STREAM(p, (TEXT_STR_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); 314 UINT8_TO_BE_STREAM(p, desc_len); 315 ARRAY_TO_BE_STREAM(p, p_desc_data, (int)desc_len); 316 317 result &= SDP_AddAttribute(handle, ATTR_ID_HID_DESCRIPTOR_LIST, 318 DATA_ELE_SEQ_DESC_TYPE, p - p_buf, p_buf); 319 320 osi_free(p_buf); 321 } 322 323 { 324 uint8_t lang_buf[8]; 325 p = lang_buf; 326 uint8_t seq_len = 6; 327 uint16_t lang_english = 0x0409; 328 UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); 329 UINT8_TO_BE_STREAM(p, seq_len); 330 UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); 331 UINT16_TO_BE_STREAM(p, lang_english); 332 UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); 333 UINT16_TO_BE_STREAM(p, LANGUAGE_BASE_ID); 334 result &= 335 SDP_AddAttribute(handle, ATTR_ID_HID_LANGUAGE_ID_BASE, 336 DATA_ELE_SEQ_DESC_TYPE, p - lang_buf, lang_buf); 337 } 338 339 result &= SDP_AddAttribute(handle, ATTR_ID_HID_BATTERY_POWER, 340 BOOLEAN_DESC_TYPE, 1, (uint8_t*)&bool_true); 341 342 result &= SDP_AddAttribute(handle, ATTR_ID_HID_REMOTE_WAKE, 343 BOOLEAN_DESC_TYPE, 1, (uint8_t*)&bool_false); 344 345 result &= SDP_AddAttribute(handle, ATTR_ID_HID_NORMALLY_CONNECTABLE, 346 BOOLEAN_DESC_TYPE, 1, (uint8_t*)&bool_true); 347 348 result &= SDP_AddAttribute(handle, ATTR_ID_HID_BOOT_DEVICE, 349 BOOLEAN_DESC_TYPE, 1, (uint8_t*)&bool_true); 350 351 p = (uint8_t*)&temp; 352 UINT16_TO_BE_STREAM(p, prof_ver); 353 result &= SDP_AddAttribute(handle, ATTR_ID_HID_PROFILE_VERSION, 354 UINT_DESC_TYPE, 2, (uint8_t*)&temp); 355 } 356 357 if (result) { 358 uint16_t browse_group = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; 359 result &= SDP_AddUuidSequence(handle, ATTR_ID_BROWSE_GROUP_LIST, 1, 360 &browse_group); 361 } 362 363 if (!result) { 364 HIDD_TRACE_ERROR("%s: failed to complete SDP record", __func__); 365 366 return HID_ERR_NOT_REGISTERED; 367 } 368 369 return HID_SUCCESS; 370} 371 372/******************************************************************************* 373 * 374 * Function HID_DevSendReport 375 * 376 * Description Sends report 377 * 378 * Returns tHID_STATUS 379 * 380 ******************************************************************************/ 381tHID_STATUS HID_DevSendReport(uint8_t channel, uint8_t type, uint8_t id, 382 uint16_t len, uint8_t* p_data) { 383 HIDD_TRACE_VERBOSE("%s: channel=%d type=%d id=%d len=%d", __func__, channel, 384 type, id, len); 385 386 if (channel == HID_CHANNEL_CTRL) { 387 return hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, type, id, len, 388 p_data); 389 } 390 391 if (channel == HID_CHANNEL_INTR && type == HID_PAR_REP_TYPE_INPUT) { 392 // on INTR we can only send INPUT 393 return hidd_conn_send_data(HID_CHANNEL_INTR, HID_TRANS_DATA, 394 HID_PAR_REP_TYPE_INPUT, id, len, p_data); 395 } 396 397 return HID_ERR_INVALID_PARAM; 398} 399 400/******************************************************************************* 401 * 402 * Function HID_DevVirtualCableUnplug 403 * 404 * Description Sends Virtual Cable Unplug 405 * 406 * Returns tHID_STATUS 407 * 408 ******************************************************************************/ 409tHID_STATUS HID_DevVirtualCableUnplug(void) { 410 HIDD_TRACE_API("%s", __func__); 411 412 return hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_CONTROL, 413 HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG, 0, 0, NULL); 414} 415 416/******************************************************************************* 417 * 418 * Function HID_DevPlugDevice 419 * 420 * Description Establishes virtual cable to given host 421 * 422 * Returns tHID_STATUS 423 * 424 ******************************************************************************/ 425tHID_STATUS HID_DevPlugDevice(const RawAddress& addr) { 426 hd_cb.device.in_use = TRUE; 427 hd_cb.device.addr = addr; 428 429 return HID_SUCCESS; 430} 431 432/******************************************************************************* 433 * 434 * Function HID_DevUnplugDevice 435 * 436 * Description Unplugs virtual cable from given host 437 * 438 * Returns tHID_STATUS 439 * 440 ******************************************************************************/ 441tHID_STATUS HID_DevUnplugDevice(const RawAddress& addr) { 442 if (hd_cb.device.addr == addr) { 443 hd_cb.device.in_use = FALSE; 444 hd_cb.device.conn.conn_state = HID_CONN_STATE_UNUSED; 445 hd_cb.device.conn.ctrl_cid = 0; 446 hd_cb.device.conn.intr_cid = 0; 447 } 448 449 return HID_SUCCESS; 450} 451 452/******************************************************************************* 453 * 454 * Function HID_DevConnect 455 * 456 * Description Connects to device 457 * 458 * Returns tHID_STATUS 459 * 460 ******************************************************************************/ 461tHID_STATUS HID_DevConnect(void) { 462 if (!hd_cb.reg_flag) { 463 return HID_ERR_NOT_REGISTERED; 464 } 465 466 if (!hd_cb.device.in_use) { 467 return HID_ERR_INVALID_PARAM; 468 } 469 470 if (hd_cb.device.state != HIDD_DEV_NO_CONN) { 471 return HID_ERR_ALREADY_CONN; 472 } 473 474 return hidd_conn_initiate(); 475} 476 477/******************************************************************************* 478 * 479 * Function HID_DevDisconnect 480 * 481 * Description Disconnects from device 482 * 483 * Returns tHID_STATUS 484 * 485 ******************************************************************************/ 486tHID_STATUS HID_DevDisconnect(void) { 487 if (!hd_cb.reg_flag) { 488 return HID_ERR_NOT_REGISTERED; 489 } 490 491 if (!hd_cb.device.in_use) { 492 return HID_ERR_INVALID_PARAM; 493 } 494 495 if (hd_cb.device.state == HIDD_DEV_NO_CONN) { 496 /* If we are still trying to connect, just close the connection. */ 497 if (hd_cb.device.conn.conn_state != HID_CONN_STATE_UNUSED) { 498 tHID_STATUS ret = hidd_conn_disconnect(); 499 hd_cb.device.conn.conn_state = HID_CONN_STATE_UNUSED; 500 hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, 501 HID_ERR_DISCONNECTING, NULL); 502 return ret; 503 } 504 return HID_ERR_NO_CONNECTION; 505 } 506 507 return hidd_conn_disconnect(); 508} 509 510/******************************************************************************* 511 * 512 * Function HID_DevSetIncomingPolicy 513 * 514 * Description Sets policy for incoming connections (allowed/disallowed) 515 * 516 * Returns tHID_STATUS 517 * 518 ******************************************************************************/ 519tHID_STATUS HID_DevSetIncomingPolicy(bool allow) { 520 hd_cb.allow_incoming = allow; 521 522 return HID_SUCCESS; 523} 524 525/******************************************************************************* 526 * 527 * Function HID_DevReportError 528 * 529 * Description Reports error for Set Report via HANDSHAKE 530 * 531 * Returns tHID_STATUS 532 * 533 ******************************************************************************/ 534tHID_STATUS HID_DevReportError(uint8_t error) { 535 uint8_t handshake_param; 536 537 HIDD_TRACE_API("%s: error = %d", __func__, error); 538 539 switch (error) { 540 case HID_PAR_HANDSHAKE_RSP_SUCCESS: 541 case HID_PAR_HANDSHAKE_RSP_NOT_READY: 542 case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID: 543 case HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ: 544 case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM: 545 case HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN: 546 case HID_PAR_HANDSHAKE_RSP_ERR_FATAL: 547 handshake_param = error; 548 break; 549 default: 550 handshake_param = HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN; 551 break; 552 } 553 554 return hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, handshake_param, 0, 0, 555 NULL); 556} 557 558/******************************************************************************* 559 * 560 * Function HID_DevGetDevice 561 * 562 * Description Returns the BD Address of virtually cabled device 563 * 564 * Returns tHID_STATUS 565 * 566 ******************************************************************************/ 567tHID_STATUS HID_DevGetDevice(RawAddress* addr) { 568 HIDD_TRACE_API("%s", __func__); 569 570 if (hd_cb.device.in_use) { 571 *addr = hd_cb.device.addr; 572 } else { 573 return HID_ERR_NOT_REGISTERED; 574 } 575 576 return HID_SUCCESS; 577} 578 579/******************************************************************************* 580 * 581 * Function HID_DevSetIncomingQos 582 * 583 * Description Sets Incoming QoS values for Interrupt L2CAP Channel 584 * 585 * Returns tHID_STATUS 586 * 587 ******************************************************************************/ 588tHID_STATUS HID_DevSetIncomingQos(uint8_t service_type, uint32_t token_rate, 589 uint32_t token_bucket_size, 590 uint32_t peak_bandwidth, uint32_t latency, 591 uint32_t delay_variation) { 592 HIDD_TRACE_API("%s", __func__); 593 594 hd_cb.use_in_qos = TRUE; 595 596 hd_cb.in_qos.service_type = service_type; 597 hd_cb.in_qos.token_rate = token_rate; 598 hd_cb.in_qos.token_bucket_size = token_bucket_size; 599 hd_cb.in_qos.peak_bandwidth = peak_bandwidth; 600 hd_cb.in_qos.latency = latency; 601 hd_cb.in_qos.delay_variation = delay_variation; 602 603 return HID_SUCCESS; 604} 605 606/******************************************************************************* 607 * 608 * Function HID_DevSetOutgoingQos 609 * 610 * Description Sets Outgoing QoS values for Interrupt L2CAP Channel 611 * 612 * Returns tHID_STATUS 613 * 614 ******************************************************************************/ 615tHID_STATUS HID_DevSetOutgoingQos(uint8_t service_type, uint32_t token_rate, 616 uint32_t token_bucket_size, 617 uint32_t peak_bandwidth, uint32_t latency, 618 uint32_t delay_variation) { 619 HIDD_TRACE_API("%s", __func__); 620 621 hd_cb.l2cap_intr_cfg.qos_present = TRUE; 622 623 hd_cb.l2cap_intr_cfg.qos.service_type = service_type; 624 hd_cb.l2cap_intr_cfg.qos.token_rate = token_rate; 625 hd_cb.l2cap_intr_cfg.qos.token_bucket_size = token_bucket_size; 626 hd_cb.l2cap_intr_cfg.qos.peak_bandwidth = peak_bandwidth; 627 hd_cb.l2cap_intr_cfg.qos.latency = latency; 628 hd_cb.l2cap_intr_cfg.qos.delay_variation = delay_variation; 629 630 return HID_SUCCESS; 631} 632