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