Fork ESP-IDF's bluetooth component
i want better sbc encoding, and no cla will stop me
This commit is contained in:
@@ -0,0 +1,586 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* Copyright (C) 2002-2012 Broadcom Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
/******************************************************************************
|
||||
*
|
||||
* This file contains the HID Device API entry points
|
||||
*
|
||||
******************************************************************************/
|
||||
//#include <errno.h>
|
||||
//#include <hardware/bluetooth.h>
|
||||
//#include <hardware/bt_hd.h>
|
||||
#include "stack/hidd_api.h"
|
||||
#include "esp_hidd_api.h"
|
||||
#include "hid_int.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "stack/btm_api.h"
|
||||
#include "stack/btu.h"
|
||||
#include "stack/hiddefs.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if (HID_DEV_INCLUDED == TRUE)
|
||||
|
||||
#if HID_DYNAMIC_MEMORY == FALSE
|
||||
tHID_DEV_CTB hd_cb;
|
||||
#else
|
||||
tHID_DEV_CTB *hidd_cb_ptr = NULL;
|
||||
#endif
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevInit
|
||||
*
|
||||
* Description Initializes control block
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevInit(void)
|
||||
{
|
||||
#if (HID_DYNAMIC_MEMORY)
|
||||
if (!hidd_cb_ptr) {
|
||||
hidd_cb_ptr = (tHID_DEV_CTB *)osi_malloc(sizeof(tHID_DEV_CTB));
|
||||
if (!hidd_cb_ptr) {
|
||||
return HID_ERR_NO_RESOURCES;
|
||||
}
|
||||
}
|
||||
#endif /* #if (HID_DYNAMIC_MEMORY) */
|
||||
memset(&hd_cb, 0, sizeof(tHID_DEV_CTB));
|
||||
#if defined(HIDD_INITIAL_TRACE_LEVEL)
|
||||
hd_cb.trace_level = HIDD_INITIAL_TRACE_LEVEL;
|
||||
#else
|
||||
hd_cb.trace_level = BT_TRACE_LEVEL_NONE;
|
||||
#endif
|
||||
return HID_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevDeinit
|
||||
*
|
||||
* Description Deinitializes control block
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
void HID_DevDeinit(void)
|
||||
{
|
||||
#if (HID_DYNAMIC_MEMORY)
|
||||
if (hidd_cb_ptr) {
|
||||
osi_free(hidd_cb_ptr);
|
||||
hidd_cb_ptr = NULL;
|
||||
}
|
||||
#endif /* #if (HID_DYNAMIC_MEMORY) */
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevSetTraceLevel
|
||||
*
|
||||
* Description This function sets the trace level for HID Dev. If called
|
||||
* with
|
||||
* a value of 0xFF, it simply reads the current trace level.
|
||||
*
|
||||
* Returns the new (current) trace level
|
||||
*
|
||||
******************************************************************************/
|
||||
uint8_t HID_DevSetTraceLevel(uint8_t new_level)
|
||||
{
|
||||
if (new_level != 0xFF) {
|
||||
hd_cb.trace_level = new_level;
|
||||
}
|
||||
|
||||
return (hd_cb.trace_level);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevRegister
|
||||
*
|
||||
* Description Registers HID device with lower layers
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevRegister(tHID_DEV_HOST_CALLBACK *host_cback)
|
||||
{
|
||||
tHID_STATUS st;
|
||||
HIDD_TRACE_API("%s", __func__);
|
||||
|
||||
if (hd_cb.reg_flag) {
|
||||
return HID_ERR_ALREADY_REGISTERED;
|
||||
}
|
||||
|
||||
if (host_cback == NULL) {
|
||||
return HID_ERR_INVALID_PARAM;
|
||||
}
|
||||
/* Register with L2CAP */
|
||||
if ((st = hidd_conn_reg()) != HID_SUCCESS) {
|
||||
return st;
|
||||
}
|
||||
|
||||
hd_cb.callback = host_cback;
|
||||
hd_cb.reg_flag = TRUE;
|
||||
|
||||
if (hd_cb.pending_data) {
|
||||
osi_free(hd_cb.pending_data);
|
||||
hd_cb.pending_data = NULL;
|
||||
}
|
||||
return (HID_SUCCESS);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevDeregister
|
||||
*
|
||||
* Description Deregisters HID device with lower layers
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevDeregister(void)
|
||||
{
|
||||
HIDD_TRACE_API("%s", __func__);
|
||||
|
||||
if (!hd_cb.reg_flag)
|
||||
return (HID_ERR_NOT_REGISTERED);
|
||||
hidd_conn_dereg();
|
||||
hd_cb.reg_flag = FALSE;
|
||||
|
||||
return (HID_SUCCESS);
|
||||
}
|
||||
|
||||
tHID_STATUS HID_DevSetSecurityLevel(uint8_t sec_lvl)
|
||||
{
|
||||
HIDD_TRACE_API("%s", __func__);
|
||||
if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_SEC_CTRL, sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID,
|
||||
HIDD_SEC_CHN)) {
|
||||
HIDD_TRACE_ERROR("Security Registration 1 failed");
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
}
|
||||
if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_SEC_CTRL, sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID,
|
||||
HIDD_SEC_CHN)) {
|
||||
HIDD_TRACE_ERROR("Security Registration 2 failed");
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
}
|
||||
if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_NOSEC_CTRL, BTM_SEC_NONE, HID_PSM_CONTROL,
|
||||
BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN)) {
|
||||
HIDD_TRACE_ERROR("Security Registration 3 failed");
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
}
|
||||
if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_NOSEC_CTRL, BTM_SEC_NONE, HID_PSM_CONTROL,
|
||||
BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN)) {
|
||||
HIDD_TRACE_ERROR("Security Registration 4 failed");
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
}
|
||||
if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_INTR, BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID,
|
||||
0)) {
|
||||
HIDD_TRACE_ERROR("Security Registration 5 failed");
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
}
|
||||
if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_INTR, BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID,
|
||||
0)) {
|
||||
HIDD_TRACE_ERROR("Security Registration 6 failed");
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
}
|
||||
return (HID_SUCCESS);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevAddRecord
|
||||
*
|
||||
* Description Creates SDP record for HID device
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevAddRecord(uint32_t handle, char *p_name, char *p_description, char *p_provider, uint16_t subclass,
|
||||
uint16_t desc_len, uint8_t *p_desc_data)
|
||||
{
|
||||
bool result = TRUE;
|
||||
|
||||
HIDD_TRACE_API("%s", __func__);
|
||||
|
||||
// Service Class ID List
|
||||
if (result) {
|
||||
uint16_t uuid = UUID_SERVCLASS_HUMAN_INTERFACE;
|
||||
result &= SDP_AddServiceClassIdList(handle, 1, &uuid);
|
||||
}
|
||||
// Protocol Descriptor List
|
||||
if (result) {
|
||||
tSDP_PROTOCOL_ELEM proto_list[2];
|
||||
proto_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP;
|
||||
proto_list[0].num_params = 1;
|
||||
proto_list[0].params[0] = BT_PSM_HIDC;
|
||||
proto_list[1].protocol_uuid = UUID_PROTOCOL_HIDP;
|
||||
proto_list[1].num_params = 0;
|
||||
result &= SDP_AddProtocolList(handle, 2, proto_list);
|
||||
}
|
||||
// Language Base Attribute ID List
|
||||
if (result) {
|
||||
result &=
|
||||
SDP_AddLanguageBaseAttrIDList(handle, LANG_ID_CODE_ENGLISH, LANG_ID_CHAR_ENCODE_UTF8, LANGUAGE_BASE_ID);
|
||||
}
|
||||
// Additional Protocol Descriptor List
|
||||
if (result) {
|
||||
tSDP_PROTO_LIST_ELEM add_proto_list;
|
||||
add_proto_list.num_elems = 2;
|
||||
add_proto_list.list_elem[0].protocol_uuid = UUID_PROTOCOL_L2CAP;
|
||||
add_proto_list.list_elem[0].num_params = 1;
|
||||
add_proto_list.list_elem[0].params[0] = BT_PSM_HIDI;
|
||||
add_proto_list.list_elem[1].protocol_uuid = UUID_PROTOCOL_HIDP;
|
||||
add_proto_list.list_elem[1].num_params = 0;
|
||||
result &= SDP_AddAdditionProtoLists(handle, 1, &add_proto_list);
|
||||
}
|
||||
// Service Name (O)
|
||||
// Service Description (O)
|
||||
// Provider Name (O)
|
||||
if (result) {
|
||||
const char *srv_name = p_name;
|
||||
const char *srv_desc = p_description;
|
||||
const char *provider_name = p_provider;
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, strlen(srv_name) + 1,
|
||||
(uint8_t *)srv_name);
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_SERVICE_DESCRIPTION, TEXT_STR_DESC_TYPE, strlen(srv_desc) + 1,
|
||||
(uint8_t *)srv_desc);
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_PROVIDER_NAME, TEXT_STR_DESC_TYPE, strlen(provider_name) + 1,
|
||||
(uint8_t *)provider_name);
|
||||
}
|
||||
// Bluetooth Profile Descriptor List
|
||||
if (result) {
|
||||
const uint16_t profile_uuid = UUID_SERVCLASS_HUMAN_INTERFACE;
|
||||
const uint16_t version = 0x0100;
|
||||
result &= SDP_AddProfileDescriptorList(handle, profile_uuid, version);
|
||||
}
|
||||
// HID Parser Version
|
||||
if (result) {
|
||||
uint8_t *p;
|
||||
const uint16_t rel_num = 0x0100;
|
||||
const uint16_t parser_version = 0x0111;
|
||||
const uint16_t prof_ver = 0x0100;
|
||||
const uint8_t dev_subclass = subclass;
|
||||
const uint8_t country_code = 0x21;
|
||||
const uint8_t bool_false = 0x00;
|
||||
const uint8_t bool_true = 0x01;
|
||||
uint16_t temp;
|
||||
p = (uint8_t *)&temp;
|
||||
UINT16_TO_BE_STREAM(p, rel_num);
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_RELNUM, UINT_DESC_TYPE, 2, (uint8_t *)&temp);
|
||||
p = (uint8_t *)&temp;
|
||||
UINT16_TO_BE_STREAM(p, parser_version);
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_HID_PARSER_VERSION, UINT_DESC_TYPE, 2, (uint8_t *)&temp);
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_SUBCLASS, UINT_DESC_TYPE, 1, (uint8_t *)&dev_subclass);
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_HID_COUNTRY_CODE, UINT_DESC_TYPE, 1, (uint8_t *)&country_code);
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_HID_VIRTUAL_CABLE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true);
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_HID_RECONNECT_INITIATE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true);
|
||||
{
|
||||
static uint8_t cdt = 0x22;
|
||||
uint8_t *p_buf;
|
||||
uint8_t seq_len = 4 + desc_len;
|
||||
p_buf = (uint8_t *)osi_malloc(2048);
|
||||
if (p_buf == NULL) {
|
||||
HIDD_TRACE_ERROR("%s: Buffer allocation failure for size = 2048 ", __func__);
|
||||
return HID_ERR_NOT_REGISTERED;
|
||||
}
|
||||
p = p_buf;
|
||||
UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
|
||||
UINT8_TO_BE_STREAM(p, seq_len);
|
||||
UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_ONE_BYTE);
|
||||
UINT8_TO_BE_STREAM(p, cdt);
|
||||
UINT8_TO_BE_STREAM(p, (TEXT_STR_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
|
||||
UINT8_TO_BE_STREAM(p, desc_len);
|
||||
ARRAY_TO_BE_STREAM(p, p_desc_data, (int)desc_len);
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_HID_DESCRIPTOR_LIST, DATA_ELE_SEQ_DESC_TYPE, p - p_buf, p_buf);
|
||||
osi_free(p_buf);
|
||||
}
|
||||
{
|
||||
uint8_t lang_buf[8];
|
||||
p = lang_buf;
|
||||
uint8_t seq_len = 6;
|
||||
uint16_t lang_english = 0x0409;
|
||||
UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE);
|
||||
UINT8_TO_BE_STREAM(p, seq_len);
|
||||
UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);
|
||||
UINT16_TO_BE_STREAM(p, lang_english);
|
||||
UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES);
|
||||
UINT16_TO_BE_STREAM(p, LANGUAGE_BASE_ID);
|
||||
result &=
|
||||
SDP_AddAttribute(handle, ATTR_ID_HID_LANGUAGE_ID_BASE, DATA_ELE_SEQ_DESC_TYPE, p - lang_buf, lang_buf);
|
||||
}
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_HID_BATTERY_POWER, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true);
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_HID_REMOTE_WAKE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_false);
|
||||
result &=
|
||||
SDP_AddAttribute(handle, ATTR_ID_HID_NORMALLY_CONNECTABLE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true);
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_HID_BOOT_DEVICE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true);
|
||||
p = (uint8_t *)&temp;
|
||||
UINT16_TO_BE_STREAM(p, prof_ver);
|
||||
result &= SDP_AddAttribute(handle, ATTR_ID_HID_PROFILE_VERSION, UINT_DESC_TYPE, 2, (uint8_t *)&temp);
|
||||
}
|
||||
if (result) {
|
||||
uint16_t browse_group = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP;
|
||||
result &= SDP_AddUuidSequence(handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse_group);
|
||||
}
|
||||
if (!result) {
|
||||
HIDD_TRACE_ERROR("%s: failed to complete SDP record", __func__);
|
||||
return HID_ERR_NOT_REGISTERED;
|
||||
}
|
||||
return HID_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevSendReport
|
||||
*
|
||||
* Description Sends report
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevSendReport(uint8_t channel, uint8_t type, uint8_t id, uint16_t len, uint8_t *p_data)
|
||||
{
|
||||
HIDD_TRACE_VERBOSE("%s: channel=%d type=%d id=%d len=%d", __func__, channel, type, id, len);
|
||||
|
||||
if (channel == HID_CHANNEL_CTRL) {
|
||||
return hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, type, id, len, p_data);
|
||||
}
|
||||
|
||||
if (channel == HID_CHANNEL_INTR && type == HID_PAR_REP_TYPE_INPUT) {
|
||||
// on INTR we can only send INPUT
|
||||
return hidd_conn_send_data(HID_CHANNEL_INTR, HID_TRANS_DATA, HID_PAR_REP_TYPE_INPUT, id, len, p_data);
|
||||
}
|
||||
|
||||
return HID_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevVirtualCableUnplug
|
||||
*
|
||||
* Description Sends Virtual Cable Unplug
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevVirtualCableUnplug(void)
|
||||
{
|
||||
HIDD_TRACE_API("%s", __func__);
|
||||
|
||||
return hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_CONTROL, HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG, 0, 0, NULL);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevPlugDevice
|
||||
*
|
||||
* Description Establishes virtual cable to given host
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevPlugDevice(BD_ADDR addr)
|
||||
{
|
||||
hd_cb.device.in_use = TRUE;
|
||||
memcpy(hd_cb.device.addr, addr, sizeof(BD_ADDR));
|
||||
|
||||
return HID_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevUnplugDevice
|
||||
*
|
||||
* Description Unplugs virtual cable from given host
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevUnplugDevice(BD_ADDR addr)
|
||||
{
|
||||
if (!memcmp(hd_cb.device.addr, addr, sizeof(BD_ADDR))) {
|
||||
hd_cb.device.in_use = FALSE;
|
||||
hd_cb.device.conn.conn_state = HID_CONN_STATE_UNUSED;
|
||||
hd_cb.device.conn.ctrl_cid = 0;
|
||||
hd_cb.device.conn.intr_cid = 0;
|
||||
}
|
||||
return HID_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevConnect
|
||||
*
|
||||
* Description Connects to device
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevConnect(void)
|
||||
{
|
||||
if (!hd_cb.reg_flag) {
|
||||
return HID_ERR_NOT_REGISTERED;
|
||||
}
|
||||
if (!hd_cb.device.in_use) {
|
||||
return HID_ERR_INVALID_PARAM;
|
||||
}
|
||||
if (hd_cb.device.state != HIDD_DEV_NO_CONN) {
|
||||
return HID_ERR_ALREADY_CONN;
|
||||
}
|
||||
return hidd_conn_initiate();
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevDisconnect
|
||||
*
|
||||
* Description Disconnects from device
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevDisconnect(void)
|
||||
{
|
||||
if (!hd_cb.reg_flag) {
|
||||
return HID_ERR_NOT_REGISTERED;
|
||||
}
|
||||
if (!hd_cb.device.in_use) {
|
||||
return HID_ERR_INVALID_PARAM;
|
||||
}
|
||||
if (hd_cb.device.state == HIDD_DEV_NO_CONN) {
|
||||
return HID_ERR_NO_CONNECTION;
|
||||
}
|
||||
return hidd_conn_disconnect();
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevSetIncomingPolicy
|
||||
*
|
||||
* Description Sets policy for incoming connections (allowed/disallowed)
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevSetIncomingPolicy(bool allow)
|
||||
{
|
||||
hd_cb.allow_incoming = allow;
|
||||
return HID_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevReportError
|
||||
*
|
||||
* Description Reports error for Set Report via HANDSHAKE
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevReportError(uint8_t error)
|
||||
{
|
||||
uint8_t handshake_param;
|
||||
|
||||
HIDD_TRACE_API("%s: error = %d", __func__, error);
|
||||
|
||||
switch (error) {
|
||||
case HID_PAR_HANDSHAKE_RSP_SUCCESS:
|
||||
case HID_PAR_HANDSHAKE_RSP_NOT_READY:
|
||||
case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID:
|
||||
case HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ:
|
||||
case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM:
|
||||
case HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN:
|
||||
case HID_PAR_HANDSHAKE_RSP_ERR_FATAL:
|
||||
handshake_param = error;
|
||||
break;
|
||||
default:
|
||||
handshake_param = HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
return hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, handshake_param, 0, 0, NULL);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevGetDevice
|
||||
*
|
||||
* Description Returns the BD Address of virtually cabled device
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevGetDevice(BD_ADDR *addr)
|
||||
{
|
||||
HIDD_TRACE_API("%s", __func__);
|
||||
|
||||
if (hd_cb.device.in_use) {
|
||||
memcpy(addr, hd_cb.device.addr, sizeof(BD_ADDR));
|
||||
} else {
|
||||
return HID_ERR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
return HID_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevSetIncomingQos
|
||||
*
|
||||
* Description Sets Incoming QoS values for Interrupt L2CAP Channel
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevSetIncomingQos(uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size,
|
||||
uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation)
|
||||
{
|
||||
HIDD_TRACE_API("%s", __func__);
|
||||
hd_cb.use_in_qos = TRUE;
|
||||
hd_cb.in_qos.service_type = service_type;
|
||||
hd_cb.in_qos.token_rate = token_rate;
|
||||
hd_cb.in_qos.token_bucket_size = token_bucket_size;
|
||||
hd_cb.in_qos.peak_bandwidth = peak_bandwidth;
|
||||
hd_cb.in_qos.latency = latency;
|
||||
hd_cb.in_qos.delay_variation = delay_variation;
|
||||
return HID_SUCCESS;
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function HID_DevSetOutgoingQos
|
||||
*
|
||||
* Description Sets Outgoing QoS values for Interrupt L2CAP Channel
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS HID_DevSetOutgoingQos(uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size,
|
||||
uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation)
|
||||
{
|
||||
HIDD_TRACE_API("%s", __func__);
|
||||
hd_cb.l2cap_intr_cfg.qos_present = TRUE;
|
||||
hd_cb.l2cap_intr_cfg.qos.service_type = service_type;
|
||||
hd_cb.l2cap_intr_cfg.qos.token_rate = token_rate;
|
||||
hd_cb.l2cap_intr_cfg.qos.token_bucket_size = token_bucket_size;
|
||||
hd_cb.l2cap_intr_cfg.qos.peak_bandwidth = peak_bandwidth;
|
||||
hd_cb.l2cap_intr_cfg.qos.latency = latency;
|
||||
hd_cb.l2cap_intr_cfg.qos.delay_variation = delay_variation;
|
||||
return HID_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,784 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* Copyright (C) 2002-2012 Broadcom Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
/******************************************************************************
|
||||
*
|
||||
* this file contains the connection interface functions
|
||||
*
|
||||
******************************************************************************/
|
||||
#include "btm_int.h"
|
||||
#include "hid_conn.h"
|
||||
#include "hid_int.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "osi/osi.h"
|
||||
#include "stack/btm_api.h"
|
||||
#include "stack/btu.h"
|
||||
#include "stack/hidd_api.h"
|
||||
#include "stack/hiddefs.h"
|
||||
#include "stack/l2c_api.h"
|
||||
#include "stack/l2cdefs.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if (HID_DEV_INCLUDED == TRUE)
|
||||
|
||||
static void hidd_l2cif_connect_ind(BD_ADDR bd_addr, uint16_t cid, uint16_t psm, uint8_t id);
|
||||
static void hidd_l2cif_connect_cfm(uint16_t cid, uint16_t result);
|
||||
static void hidd_l2cif_config_ind(uint16_t cid, tL2CAP_CFG_INFO *p_cfg);
|
||||
static void hidd_l2cif_config_cfm(uint16_t cid, tL2CAP_CFG_INFO *p_cfg);
|
||||
static void hidd_l2cif_disconnect_ind(uint16_t cid, bool ack_needed);
|
||||
static void hidd_l2cif_disconnect_cfm(uint16_t cid, uint16_t result);
|
||||
static void hidd_l2cif_data_ind(uint16_t cid, BT_HDR *p_msg);
|
||||
static void hidd_l2cif_cong_ind(uint16_t cid, bool congested);
|
||||
|
||||
static const tL2CAP_APPL_INFO dev_reg_info = {hidd_l2cif_connect_ind,
|
||||
hidd_l2cif_connect_cfm,
|
||||
NULL,
|
||||
hidd_l2cif_config_ind,
|
||||
hidd_l2cif_config_cfm,
|
||||
hidd_l2cif_disconnect_ind,
|
||||
hidd_l2cif_disconnect_cfm,
|
||||
NULL,
|
||||
hidd_l2cif_data_ind,
|
||||
hidd_l2cif_cong_ind,
|
||||
NULL};
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_check_config_done
|
||||
*
|
||||
* Description Checks if connection is configured and callback can be fired
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
static void hidd_check_config_done(void)
|
||||
{
|
||||
tHID_CONN *p_hcon;
|
||||
p_hcon = &hd_cb.device.conn;
|
||||
if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED) &&
|
||||
(p_hcon->conn_state == HID_CONN_STATE_CONFIG)) {
|
||||
p_hcon->conn_state = HID_CONN_STATE_CONNECTED;
|
||||
hd_cb.device.state = HIDD_DEV_CONNECTED;
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_OPEN, 0, NULL);
|
||||
// send outstanding data on intr
|
||||
if (hd_cb.pending_data) {
|
||||
L2CA_DataWrite(p_hcon->intr_cid, hd_cb.pending_data);
|
||||
hd_cb.pending_data = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidh_sec_check_complete_term
|
||||
*
|
||||
* Description HID security check complete callback function.
|
||||
*
|
||||
* Returns Send L2CA_ConnectRsp OK if secutiry check succeed; otherwise
|
||||
* send security block L2C connection response.
|
||||
*
|
||||
******************************************************************************/
|
||||
static void hidd_sec_check_complete(UNUSED_ATTR BD_ADDR bd_addr, UNUSED_ATTR tBT_TRANSPORT transport, void *p_ref_data,
|
||||
uint8_t res)
|
||||
{
|
||||
tHID_DEV_DEV_CTB *p_dev = (tHID_DEV_DEV_CTB *)p_ref_data;
|
||||
if (res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY) {
|
||||
p_dev->conn.disc_reason = HID_SUCCESS;
|
||||
p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR;
|
||||
L2CA_ConnectRsp(p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
|
||||
L2CA_ConfigReq(p_dev->conn.ctrl_cid, &hd_cb.l2cap_cfg);
|
||||
} else if (res != BTM_SUCCESS) {
|
||||
HIDD_TRACE_WARNING("%s: connection rejected by security", __func__);
|
||||
p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED;
|
||||
p_dev->conn.conn_state = HID_CONN_STATE_UNUSED;
|
||||
L2CA_ConnectRsp(p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_SECURITY_BLOCK,
|
||||
L2CAP_CONN_OK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_sec_check_complete_orig
|
||||
*
|
||||
* Description HID security check complete callback function (device
|
||||
*originated)
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
void hidd_sec_check_complete_orig(UNUSED_ATTR BD_ADDR bd_addr, UNUSED_ATTR tBT_TRANSPORT transport, void *p_ref_data,
|
||||
uint8_t res)
|
||||
{
|
||||
tHID_DEV_DEV_CTB *p_dev = (tHID_DEV_DEV_CTB *)p_ref_data;
|
||||
if (p_dev->conn.conn_state != HID_CONN_STATE_SECURITY) {
|
||||
HIDD_TRACE_WARNING("%s: invalid state (%02x)", __func__, p_dev->conn.conn_state);
|
||||
return;
|
||||
}
|
||||
if (res == BTM_SUCCESS) {
|
||||
HIDD_TRACE_EVENT("%s: security ok", __func__);
|
||||
p_dev->conn.disc_reason = HID_SUCCESS;
|
||||
p_dev->conn.conn_state = HID_CONN_STATE_CONFIG;
|
||||
L2CA_ConfigReq(p_dev->conn.ctrl_cid, &hd_cb.l2cap_cfg);
|
||||
} else {
|
||||
HIDD_TRACE_WARNING("%s: security check failed (%02x)", __func__, res);
|
||||
p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED;
|
||||
hidd_conn_disconnect();
|
||||
}
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_l2cif_connect_ind
|
||||
*
|
||||
* Description Handles incoming L2CAP connection (we act as server)
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
static void hidd_l2cif_connect_ind(BD_ADDR bd_addr, uint16_t cid, uint16_t psm, uint8_t id)
|
||||
{
|
||||
tHID_CONN *p_hcon;
|
||||
tHID_DEV_DEV_CTB *p_dev;
|
||||
bool accept = TRUE; // accept by default
|
||||
HIDD_TRACE_EVENT("%s: psm=%04x cid=%04x id=%02x", __func__, psm, cid, id);
|
||||
p_dev = &hd_cb.device;
|
||||
if (!hd_cb.allow_incoming) {
|
||||
HIDD_TRACE_WARNING("%s: incoming connections not allowed, rejecting", __func__);
|
||||
L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0);
|
||||
return;
|
||||
}
|
||||
if (p_dev->in_use && memcmp(bd_addr, p_dev->addr, sizeof(BD_ADDR))) {
|
||||
HIDD_TRACE_WARNING("%s: incoming connections from different device, rejecting", __func__);
|
||||
L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0);
|
||||
return;
|
||||
} else if (!p_dev->in_use) {
|
||||
p_dev->in_use = TRUE;
|
||||
memcpy(p_dev->addr, bd_addr, sizeof(BD_ADDR));
|
||||
p_dev->state = HIDD_DEV_NO_CONN;
|
||||
}
|
||||
p_hcon = &hd_cb.device.conn;
|
||||
switch (psm) {
|
||||
case HID_PSM_INTERRUPT:
|
||||
if (p_hcon->ctrl_cid == 0) {
|
||||
accept = FALSE;
|
||||
HIDD_TRACE_WARNING("%s: incoming INTR without CTRL, rejecting", __func__);
|
||||
}
|
||||
if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) {
|
||||
accept = FALSE;
|
||||
HIDD_TRACE_WARNING("%s: incoming INTR in invalid state (%d), rejecting", __func__, p_hcon->conn_state);
|
||||
}
|
||||
break;
|
||||
case HID_PSM_CONTROL:
|
||||
if (p_hcon->conn_state != HID_CONN_STATE_UNUSED) {
|
||||
accept = FALSE;
|
||||
HIDD_TRACE_WARNING("%s: incoming CTRL in invalid state (%d), rejecting", __func__, p_hcon->conn_state);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
accept = FALSE;
|
||||
HIDD_TRACE_ERROR("%s: received invalid PSM, rejecting", __func__);
|
||||
break;
|
||||
}
|
||||
if (!accept) {
|
||||
L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0);
|
||||
return;
|
||||
}
|
||||
// for CTRL we need to go through security and we reply in callback from there
|
||||
if (psm == HID_PSM_CONTROL) {
|
||||
p_hcon->conn_flags = 0;
|
||||
p_hcon->ctrl_cid = cid;
|
||||
p_hcon->ctrl_id = id;
|
||||
p_hcon->disc_reason = HID_L2CAP_CONN_FAIL;
|
||||
p_hcon->conn_state = HID_CONN_STATE_SECURITY;
|
||||
if (btm_sec_mx_access_request(p_dev->addr, HID_PSM_CONTROL, FALSE, BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN,
|
||||
&hidd_sec_check_complete, p_dev) == BTM_CMD_STARTED) {
|
||||
L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// for INTR we go directly to config state
|
||||
p_hcon->conn_state = HID_CONN_STATE_CONFIG;
|
||||
p_hcon->intr_cid = cid;
|
||||
L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
|
||||
L2CA_ConfigReq(cid, &hd_cb.l2cap_intr_cfg);
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_l2cif_connect_cfm
|
||||
*
|
||||
* Description Handles L2CAP connection response (we act as client)
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
static void hidd_l2cif_connect_cfm(uint16_t cid, uint16_t result)
|
||||
{
|
||||
tHID_DEV_DEV_CTB *p_dev = &hd_cb.device;
|
||||
tHID_CONN *p_hcon = &hd_cb.device.conn;
|
||||
HIDD_TRACE_EVENT("%s: cid=%04x result=%d, conn_state=%d", __func__, cid, result, p_hcon->conn_state);
|
||||
if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) {
|
||||
HIDD_TRACE_WARNING("%s: unknown cid", __func__);
|
||||
return;
|
||||
}
|
||||
if (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) ||
|
||||
((cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_INTR))) ||
|
||||
((cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_CTRL))) {
|
||||
HIDD_TRACE_WARNING("%s: unexpected, cid:0x%04x, ctrl_cid:0x%04x, intr_cid:0x%04x, conn_state:%d", __func__, cid,
|
||||
p_hcon->ctrl_cid, p_hcon->intr_cid, p_hcon->conn_state);
|
||||
return;
|
||||
}
|
||||
if (result != L2CAP_CONN_OK) {
|
||||
HIDD_TRACE_WARNING("%s: connection failed, now disconnect", __func__);
|
||||
if (cid == p_hcon->ctrl_cid)
|
||||
p_hcon->ctrl_cid = 0;
|
||||
else
|
||||
p_hcon->intr_cid = 0;
|
||||
hidd_conn_disconnect();
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_L2CAP_CONN_FAIL | (uint32_t)result, NULL);
|
||||
return;
|
||||
}
|
||||
/* CTRL connect conf */
|
||||
if (cid == p_hcon->ctrl_cid) {
|
||||
p_hcon->conn_state = HID_CONN_STATE_SECURITY;
|
||||
p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* in case disconnected before sec completed */
|
||||
btm_sec_mx_access_request(p_dev->addr, HID_PSM_CONTROL, TRUE, BTM_SEC_PROTO_HID, HIDD_SEC_CHN,
|
||||
&hidd_sec_check_complete_orig, p_dev);
|
||||
} else {
|
||||
p_hcon->conn_state = HID_CONN_STATE_CONFIG;
|
||||
L2CA_ConfigReq(cid, &hd_cb.l2cap_intr_cfg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_l2cif_config_ind
|
||||
*
|
||||
* Description Handles incoming L2CAP configuration request
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
static void hidd_l2cif_config_ind(uint16_t cid, tL2CAP_CFG_INFO *p_cfg)
|
||||
{
|
||||
tHID_CONN *p_hcon;
|
||||
HIDD_TRACE_EVENT("%s: cid=%04x", __func__, cid);
|
||||
p_hcon = &hd_cb.device.conn;
|
||||
if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) {
|
||||
HIDD_TRACE_WARNING("%s: unknown cid", __func__);
|
||||
return;
|
||||
}
|
||||
if ((!p_cfg->mtu_present) || (p_cfg->mtu > HID_DEV_MTU_SIZE))
|
||||
p_hcon->rem_mtu_size = HID_DEV_MTU_SIZE;
|
||||
else
|
||||
p_hcon->rem_mtu_size = p_cfg->mtu;
|
||||
// accept without changes
|
||||
p_cfg->flush_to_present = FALSE;
|
||||
p_cfg->mtu_present = FALSE;
|
||||
p_cfg->result = L2CAP_CFG_OK;
|
||||
if (cid == p_hcon->intr_cid && hd_cb.use_in_qos && !p_cfg->qos_present) {
|
||||
p_cfg->qos_present = TRUE;
|
||||
memcpy(&p_cfg->qos, &hd_cb.in_qos, sizeof(FLOW_SPEC));
|
||||
}
|
||||
L2CA_ConfigRsp(cid, p_cfg);
|
||||
// update flags
|
||||
if (cid == p_hcon->ctrl_cid) {
|
||||
p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_CTRL_CFG_DONE;
|
||||
if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) && (p_hcon->conn_flags & HID_CONN_FLAGS_MY_CTRL_CFG_DONE)) {
|
||||
p_hcon->disc_reason = HID_L2CAP_CONN_FAIL;
|
||||
if ((p_hcon->intr_cid = L2CA_ConnectReq(HID_PSM_INTERRUPT, hd_cb.device.addr)) == 0) {
|
||||
p_hcon->conn_state = HID_CONN_STATE_UNUSED;
|
||||
hidd_conn_disconnect();
|
||||
HIDD_TRACE_WARNING("%s: could not start L2CAP connection for INTR", __func__);
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL);
|
||||
return;
|
||||
} else {
|
||||
p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_INTR_CFG_DONE;
|
||||
}
|
||||
hidd_check_config_done();
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_l2cif_config_cfm
|
||||
*
|
||||
* Description Handles incoming L2CAP configuration response
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
static void hidd_l2cif_config_cfm(uint16_t cid, tL2CAP_CFG_INFO *p_cfg)
|
||||
{
|
||||
tHID_CONN *p_hcon;
|
||||
uint32_t reason;
|
||||
HIDD_TRACE_EVENT("%s: cid=%04x pcfg->result=%d", __func__, cid, p_cfg->result);
|
||||
p_hcon = &hd_cb.device.conn;
|
||||
if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) {
|
||||
HIDD_TRACE_WARNING("%s: unknown cid", __func__);
|
||||
return;
|
||||
}
|
||||
if (p_hcon->intr_cid == cid && p_cfg->result == L2CAP_CFG_UNACCEPTABLE_PARAMS && p_cfg->qos_present) {
|
||||
tL2CAP_CFG_INFO new_qos;
|
||||
// QoS parameters not accepted for intr, try again with host proposal
|
||||
memcpy(&new_qos, &hd_cb.l2cap_intr_cfg, sizeof(new_qos));
|
||||
memcpy(&new_qos.qos, &p_cfg->qos, sizeof(FLOW_SPEC));
|
||||
new_qos.qos_present = TRUE;
|
||||
HIDD_TRACE_WARNING("%s: config failed, retry", __func__);
|
||||
L2CA_ConfigReq(cid, &new_qos);
|
||||
return;
|
||||
} else if (p_hcon->intr_cid == cid && p_cfg->result == L2CAP_CFG_UNKNOWN_OPTIONS) {
|
||||
// QoS not understood by remote device, try configuring without QoS
|
||||
HIDD_TRACE_WARNING("%s: config failed, retry without QoS", __func__);
|
||||
L2CA_ConfigReq(cid, &hd_cb.l2cap_cfg);
|
||||
return;
|
||||
} else if (p_cfg->result != L2CAP_CFG_OK) {
|
||||
HIDD_TRACE_WARNING("%s: config failed, disconnecting", __func__);
|
||||
hidd_conn_disconnect();
|
||||
reason = HID_L2CAP_CFG_FAIL | (uint32_t)p_cfg->result;
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, reason, NULL);
|
||||
return;
|
||||
}
|
||||
// update flags
|
||||
if (cid == p_hcon->ctrl_cid) {
|
||||
p_hcon->conn_flags |= HID_CONN_FLAGS_MY_CTRL_CFG_DONE;
|
||||
if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) && (p_hcon->conn_flags & HID_CONN_FLAGS_HIS_CTRL_CFG_DONE)) {
|
||||
p_hcon->disc_reason = HID_L2CAP_CONN_FAIL;
|
||||
if ((p_hcon->intr_cid = L2CA_ConnectReq(HID_PSM_INTERRUPT, hd_cb.device.addr)) == 0) {
|
||||
p_hcon->conn_state = HID_CONN_STATE_UNUSED;
|
||||
hidd_conn_disconnect();
|
||||
HIDD_TRACE_WARNING("%s: could not start L2CAP connection for INTR", __func__);
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL);
|
||||
return;
|
||||
} else {
|
||||
p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
p_hcon->conn_flags |= HID_CONN_FLAGS_MY_INTR_CFG_DONE;
|
||||
}
|
||||
hidd_check_config_done();
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_l2cif_disconnect_ind
|
||||
*
|
||||
* Description Handler incoming L2CAP disconnection request
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
static void hidd_l2cif_disconnect_ind(uint16_t cid, bool ack_needed)
|
||||
{
|
||||
tHID_CONN *p_hcon;
|
||||
HIDD_TRACE_EVENT("%s: cid=%04x ack_needed=%d", __func__, cid, ack_needed);
|
||||
p_hcon = &hd_cb.device.conn;
|
||||
if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) {
|
||||
HIDD_TRACE_WARNING("%s: unknown cid", __func__);
|
||||
return;
|
||||
}
|
||||
if (ack_needed)
|
||||
L2CA_DisconnectRsp(cid);
|
||||
if (cid == p_hcon->ctrl_cid) {
|
||||
p_hcon->ctrl_cid = 0;
|
||||
p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL;
|
||||
} else {
|
||||
p_hcon->intr_cid = 0;
|
||||
p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR;
|
||||
}
|
||||
if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) {
|
||||
HIDD_TRACE_EVENT("%s: INTR and CTRL disconnected", __func__);
|
||||
// clean any outstanding data on intr
|
||||
if (hd_cb.pending_data) {
|
||||
osi_free(hd_cb.pending_data);
|
||||
hd_cb.pending_data = NULL;
|
||||
}
|
||||
hd_cb.device.state = HIDD_DEV_NO_CONN;
|
||||
p_hcon->conn_state = HID_CONN_STATE_UNUSED;
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, p_hcon->disc_reason, NULL);
|
||||
}
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_l2cif_disconnect_cfm
|
||||
*
|
||||
* Description Handles L2CAP disconection response
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
static void hidd_l2cif_disconnect_cfm(uint16_t cid, uint16_t result)
|
||||
{
|
||||
tHID_CONN *p_hcon;
|
||||
HIDD_TRACE_EVENT("%s: cid=%04x result=%d", __func__, cid, result);
|
||||
p_hcon = &hd_cb.device.conn;
|
||||
if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) {
|
||||
HIDD_TRACE_WARNING("%s: unknown cid", __func__);
|
||||
return;
|
||||
}
|
||||
if (cid == p_hcon->ctrl_cid) {
|
||||
p_hcon->ctrl_cid = 0;
|
||||
} else {
|
||||
p_hcon->intr_cid = 0;
|
||||
// now disconnect CTRL
|
||||
L2CA_DisconnectReq(p_hcon->ctrl_cid);
|
||||
}
|
||||
if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) {
|
||||
HIDD_TRACE_EVENT("%s: INTR and CTRL disconnected", __func__);
|
||||
hd_cb.device.state = HIDD_DEV_NO_CONN;
|
||||
p_hcon->conn_state = HID_CONN_STATE_UNUSED;
|
||||
if (hd_cb.pending_vc_unplug) {
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_VC_UNPLUG, p_hcon->disc_reason, NULL);
|
||||
hd_cb.pending_vc_unplug = FALSE;
|
||||
} else {
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, p_hcon->disc_reason, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_l2cif_cong_ind
|
||||
*
|
||||
* Description Handles L2CAP congestion status event
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
static void hidd_l2cif_cong_ind(uint16_t cid, bool congested)
|
||||
{
|
||||
tHID_CONN *p_hcon;
|
||||
HIDD_TRACE_EVENT("%s: cid=%04x congested=%d", __func__, cid, congested);
|
||||
p_hcon = &hd_cb.device.conn;
|
||||
if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) {
|
||||
HIDD_TRACE_WARNING("%s: unknown cid", __func__);
|
||||
return;
|
||||
}
|
||||
if (congested) {
|
||||
p_hcon->conn_flags |= HID_CONN_FLAGS_CONGESTED;
|
||||
} else {
|
||||
p_hcon->conn_flags &= ~HID_CONN_FLAGS_CONGESTED;
|
||||
}
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_l2cif_data_ind
|
||||
*
|
||||
* Description Handler incoming data on L2CAP channel
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
static void hidd_l2cif_data_ind(uint16_t cid, BT_HDR *p_msg)
|
||||
{
|
||||
tHID_CONN *p_hcon;
|
||||
uint8_t *p_data = (uint8_t *)(p_msg + 1) + p_msg->offset;
|
||||
uint8_t msg_type, param;
|
||||
bool err = FALSE;
|
||||
HIDD_TRACE_EVENT("%s: cid=%04x", __func__, cid);
|
||||
p_hcon = &hd_cb.device.conn;
|
||||
if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) {
|
||||
HIDD_TRACE_WARNING("%s: unknown cid", __func__);
|
||||
osi_free(p_msg);
|
||||
return;
|
||||
}
|
||||
msg_type = HID_GET_TRANS_FROM_HDR(*p_data);
|
||||
param = HID_GET_PARAM_FROM_HDR(*p_data);
|
||||
if (msg_type == HID_TRANS_DATA && cid == p_hcon->intr_cid) {
|
||||
// skip HID header
|
||||
p_msg->offset++;
|
||||
p_msg->len--;
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_INTR_DATA, 0, p_msg);
|
||||
return;
|
||||
}
|
||||
switch (msg_type) {
|
||||
case HID_TRANS_GET_REPORT:
|
||||
// at this stage we don't know if Report Id shall be included in request
|
||||
// so we pass complete packet in callback and let other code analyze this
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_GET_REPORT, !!(param & HID_PAR_GET_REP_BUFSIZE_FOLLOWS), p_msg);
|
||||
break;
|
||||
case HID_TRANS_SET_REPORT:
|
||||
// as above
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SET_REPORT, 0, p_msg);
|
||||
break;
|
||||
case HID_TRANS_GET_IDLE:
|
||||
hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, HID_PAR_REP_TYPE_OTHER, hd_cb.device.idle_time, 0, NULL);
|
||||
osi_free(p_msg);
|
||||
break;
|
||||
case HID_TRANS_SET_IDLE:
|
||||
if (p_msg->len != 2) {
|
||||
HIDD_TRACE_ERROR("%s: invalid len (%d) set idle request received", __func__, p_msg->len);
|
||||
err = TRUE;
|
||||
} else {
|
||||
hd_cb.device.idle_time = p_data[1];
|
||||
HIDD_TRACE_DEBUG("%s: idle_time = %d", __func__, hd_cb.device.idle_time);
|
||||
if (hd_cb.device.idle_time) {
|
||||
HIDD_TRACE_WARNING("%s: idle_time of %d ms not supported by HID Device", __func__,
|
||||
(hd_cb.device.idle_time * 4));
|
||||
err = TRUE;
|
||||
}
|
||||
}
|
||||
if (!err) {
|
||||
hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_SUCCESS, 0, 0, NULL);
|
||||
} else {
|
||||
hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM, 0, 0, NULL);
|
||||
}
|
||||
osi_free(p_msg);
|
||||
break;
|
||||
case HID_TRANS_GET_PROTOCOL:
|
||||
hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, HID_PAR_REP_TYPE_OTHER, !hd_cb.device.boot_mode, 0, NULL);
|
||||
osi_free(p_msg);
|
||||
break;
|
||||
case HID_TRANS_SET_PROTOCOL:
|
||||
hd_cb.device.boot_mode = !(param & HID_PAR_PROTOCOL_MASK);
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SET_PROTOCOL, param & HID_PAR_PROTOCOL_MASK, NULL);
|
||||
hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_SUCCESS, 0, 0, NULL);
|
||||
osi_free(p_msg);
|
||||
break;
|
||||
case HID_TRANS_CONTROL:
|
||||
switch (param) {
|
||||
case HID_PAR_CONTROL_SUSPEND:
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SUSPEND, 0, NULL);
|
||||
break;
|
||||
case HID_PAR_CONTROL_EXIT_SUSPEND:
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_EXIT_SUSPEND, 0, NULL);
|
||||
break;
|
||||
case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG:
|
||||
hidd_conn_disconnect();
|
||||
// set flag so we can notify properly when disconnected
|
||||
hd_cb.pending_vc_unplug = TRUE;
|
||||
break;
|
||||
}
|
||||
osi_free(p_msg);
|
||||
break;
|
||||
case HID_TRANS_DATA:
|
||||
default:
|
||||
HIDD_TRACE_WARNING("%s: got unsupported msg (%d)", __func__, msg_type);
|
||||
hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ, 0, 0, NULL);
|
||||
osi_free(p_msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_conn_reg
|
||||
*
|
||||
* Description Registers L2CAP channels
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS hidd_conn_reg(void)
|
||||
{
|
||||
HIDD_TRACE_API("%s", __func__);
|
||||
memset(&hd_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO));
|
||||
hd_cb.l2cap_cfg.mtu_present = TRUE;
|
||||
hd_cb.l2cap_cfg.mtu = HID_DEV_MTU_SIZE;
|
||||
hd_cb.l2cap_cfg.flush_to_present = TRUE;
|
||||
hd_cb.l2cap_cfg.flush_to = HID_DEV_FLUSH_TO;
|
||||
memset(&hd_cb.l2cap_intr_cfg, 0, sizeof(tL2CAP_CFG_INFO));
|
||||
hd_cb.l2cap_intr_cfg.mtu_present = TRUE;
|
||||
hd_cb.l2cap_intr_cfg.mtu = HID_DEV_MTU_SIZE;
|
||||
hd_cb.l2cap_intr_cfg.flush_to_present = TRUE;
|
||||
hd_cb.l2cap_intr_cfg.flush_to = HID_DEV_FLUSH_TO;
|
||||
if (!L2CA_Register(HID_PSM_CONTROL, (tL2CAP_APPL_INFO *)&dev_reg_info)) {
|
||||
HIDD_TRACE_ERROR("HID Control (device) registration failed");
|
||||
return (HID_ERR_L2CAP_FAILED);
|
||||
}
|
||||
if (!L2CA_Register(HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *)&dev_reg_info)) {
|
||||
L2CA_Deregister(HID_PSM_CONTROL);
|
||||
HIDD_TRACE_ERROR("HID Interrupt (device) registration failed");
|
||||
return (HID_ERR_L2CAP_FAILED);
|
||||
}
|
||||
return (HID_SUCCESS);
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_conn_dereg
|
||||
*
|
||||
* Description Deregisters L2CAP channels
|
||||
*
|
||||
* Returns void
|
||||
*
|
||||
******************************************************************************/
|
||||
void hidd_conn_dereg(void)
|
||||
{
|
||||
HIDD_TRACE_API("%s", __func__);
|
||||
L2CA_Deregister(HID_PSM_CONTROL);
|
||||
L2CA_Deregister(HID_PSM_INTERRUPT);
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_conn_initiate
|
||||
*
|
||||
* Description Initiates HID connection to plugged device
|
||||
*
|
||||
* Returns HID_SUCCESS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS hidd_conn_initiate(void)
|
||||
{
|
||||
tHID_DEV_DEV_CTB *p_dev = &hd_cb.device;
|
||||
HIDD_TRACE_API("%s", __func__);
|
||||
if (!p_dev->in_use) {
|
||||
HIDD_TRACE_WARNING("%s: no virtual cable established", __func__);
|
||||
return (HID_ERR_NOT_REGISTERED);
|
||||
}
|
||||
if (p_dev->conn.conn_state != HID_CONN_STATE_UNUSED) {
|
||||
HIDD_TRACE_WARNING("%s: connection already in progress", __func__);
|
||||
return (HID_ERR_CONN_IN_PROCESS);
|
||||
}
|
||||
p_dev->conn.ctrl_cid = 0;
|
||||
p_dev->conn.intr_cid = 0;
|
||||
p_dev->conn.disc_reason = HID_L2CAP_CONN_FAIL;
|
||||
p_dev->conn.conn_flags = HID_CONN_FLAGS_IS_ORIG;
|
||||
BTM_SetOutService(p_dev->addr, BTM_SEC_SERVICE_HIDD_SEC_CTRL, HIDD_SEC_CHN);
|
||||
/* Check if L2CAP started the connection process */
|
||||
if ((p_dev->conn.ctrl_cid = L2CA_ConnectReq(HID_PSM_CONTROL, p_dev->addr)) == 0) {
|
||||
HIDD_TRACE_WARNING("%s: could not start L2CAP connection", __func__);
|
||||
hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL);
|
||||
} else {
|
||||
p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL;
|
||||
}
|
||||
return (HID_SUCCESS);
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_conn_disconnect
|
||||
*
|
||||
* Description Disconnects existing HID connection
|
||||
*
|
||||
* Returns HID_SUCCESS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS hidd_conn_disconnect(void)
|
||||
{
|
||||
tHID_CONN *p_hcon;
|
||||
HIDD_TRACE_API("%s", __func__);
|
||||
// clean any outstanding data on intr
|
||||
if (hd_cb.pending_data) {
|
||||
osi_free(hd_cb.pending_data);
|
||||
hd_cb.pending_data = NULL;
|
||||
}
|
||||
p_hcon = &hd_cb.device.conn;
|
||||
if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) {
|
||||
/* Set l2cap idle timeout to 0 (so ACL link is disconnected
|
||||
* immediately after last channel is closed) */
|
||||
L2CA_SetIdleTimeoutByBdAddr(hd_cb.device.addr, 0, BT_TRANSPORT_BR_EDR);
|
||||
if (p_hcon->intr_cid) {
|
||||
p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR;
|
||||
L2CA_DisconnectReq(p_hcon->intr_cid);
|
||||
} else if (p_hcon->ctrl_cid) {
|
||||
p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL;
|
||||
L2CA_DisconnectReq(p_hcon->ctrl_cid);
|
||||
}
|
||||
} else {
|
||||
HIDD_TRACE_WARNING("%s: already disconnected", __func__);
|
||||
p_hcon->conn_state = HID_CONN_STATE_UNUSED;
|
||||
}
|
||||
return (HID_SUCCESS);
|
||||
}
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Function hidd_conn_send_data
|
||||
*
|
||||
* Description Sends data to host
|
||||
*
|
||||
* Returns tHID_STATUS
|
||||
*
|
||||
******************************************************************************/
|
||||
tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param, uint8_t data, uint16_t len,
|
||||
uint8_t *p_data)
|
||||
{
|
||||
tHID_CONN *p_hcon;
|
||||
BT_HDR *p_buf;
|
||||
uint8_t *p_out;
|
||||
uint16_t cid;
|
||||
uint16_t buf_size;
|
||||
|
||||
HIDD_TRACE_VERBOSE("%s: channel(%d), msg_type(%d), len(%d)", __func__, channel, msg_type, len);
|
||||
|
||||
p_hcon = &hd_cb.device.conn;
|
||||
if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) {
|
||||
return HID_ERR_CONGESTED;
|
||||
}
|
||||
|
||||
switch (msg_type) {
|
||||
case HID_TRANS_HANDSHAKE:
|
||||
case HID_TRANS_CONTROL:
|
||||
cid = p_hcon->ctrl_cid;
|
||||
buf_size = HID_CONTROL_BUF_SIZE;
|
||||
break;
|
||||
case HID_TRANS_DATA:
|
||||
if (channel == HID_CHANNEL_CTRL) {
|
||||
cid = p_hcon->ctrl_cid;
|
||||
buf_size = HID_CONTROL_BUF_SIZE;
|
||||
} else {
|
||||
cid = p_hcon->intr_cid;
|
||||
buf_size = HID_INTERRUPT_BUF_SIZE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return (HID_ERR_INVALID_PARAM);
|
||||
}
|
||||
p_buf = (BT_HDR *)osi_malloc(buf_size);
|
||||
if (p_buf == NULL)
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
p_buf->offset = L2CAP_MIN_OFFSET;
|
||||
p_out = (uint8_t *)(p_buf + 1) + p_buf->offset;
|
||||
*p_out = HID_BUILD_HDR(msg_type, param);
|
||||
p_out++;
|
||||
p_buf->len = 1; // start with header only
|
||||
// add report id prefix only if non-zero (which is reserved)
|
||||
if (msg_type == HID_TRANS_DATA && (data || param == HID_PAR_REP_TYPE_OTHER)) {
|
||||
*p_out = data; // report_id
|
||||
p_out++;
|
||||
p_buf->len++;
|
||||
}
|
||||
if (len > 0 && p_data != NULL) {
|
||||
memcpy(p_out, p_data, len);
|
||||
p_buf->len += len;
|
||||
}
|
||||
// check if connected
|
||||
if (hd_cb.device.state != HIDD_DEV_CONNECTED) {
|
||||
// for DATA on intr we hold transfer and try to reconnect
|
||||
if (msg_type == HID_TRANS_DATA && cid == p_hcon->intr_cid) {
|
||||
// drop previous data, we do not queue it for now
|
||||
if (hd_cb.pending_data) {
|
||||
osi_free(hd_cb.pending_data);
|
||||
}
|
||||
hd_cb.pending_data = p_buf;
|
||||
if (hd_cb.device.conn.conn_state == HID_CONN_STATE_UNUSED) {
|
||||
HIDD_TRACE_WARNING("%s: try to reconnect!", __func__);
|
||||
return hidd_conn_initiate();
|
||||
}
|
||||
return HID_SUCCESS;
|
||||
}
|
||||
return HID_ERR_NO_CONNECTION;
|
||||
}
|
||||
#ifdef REPORT_TRANSFER_TIMESTAMP
|
||||
if (report_transfer) {
|
||||
HIDD_TRACE_ERROR("%s: report sent", __func__);
|
||||
}
|
||||
#endif
|
||||
HIDD_TRACE_VERBOSE("%s: report sent", __func__);
|
||||
if (L2CA_DataWrite(cid, p_buf) == L2CAP_DW_FAILED)
|
||||
return (HID_ERR_CONGESTED);
|
||||
return (HID_SUCCESS);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,654 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2002-2012 Broadcom Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* This file contains the HID HOST API entry points
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "common/bt_target.h"
|
||||
#include "osi/allocator.h"
|
||||
#include "stack/bt_types.h"
|
||||
#include "stack/hiddefs.h"
|
||||
#include "stack/hidh_api.h"
|
||||
#include "hid_int.h"
|
||||
#include "stack/btm_api.h"
|
||||
#include "stack/btu.h"
|
||||
#include "btm_int.h"
|
||||
|
||||
#if (HID_HOST_INCLUDED == TRUE)
|
||||
|
||||
#if HID_DYNAMIC_MEMORY == FALSE
|
||||
tHID_HOST_CTB hh_cb;
|
||||
#else
|
||||
tHID_HOST_CTB *hidh_cb_ptr = NULL;
|
||||
#endif
|
||||
|
||||
static void hidh_search_callback (UINT16 sdp_result);
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function HID_HostGetSDPRecord
|
||||
**
|
||||
** Description This function reads the device SDP record
|
||||
**
|
||||
** Returns tHID_STATUS
|
||||
**
|
||||
*******************************************************************************/
|
||||
tHID_STATUS HID_HostGetSDPRecord ( BD_ADDR addr, tSDP_DISCOVERY_DB *p_db, UINT32 db_len,
|
||||
tHID_HOST_SDP_CALLBACK *sdp_cback )
|
||||
{
|
||||
tSDP_UUID uuid_list;
|
||||
|
||||
if ( hh_cb.sdp_busy ) {
|
||||
return HID_ERR_SDP_BUSY;
|
||||
}
|
||||
|
||||
uuid_list.len = 2;
|
||||
uuid_list.uu.uuid16 = UUID_SERVCLASS_HUMAN_INTERFACE;
|
||||
|
||||
hh_cb.p_sdp_db = p_db;
|
||||
SDP_InitDiscoveryDb (p_db, db_len, 1, &uuid_list, 0, NULL);
|
||||
|
||||
if (SDP_ServiceSearchRequest (addr, p_db, hidh_search_callback)) {
|
||||
hh_cb.sdp_cback = sdp_cback ;
|
||||
hh_cb.sdp_busy = TRUE;
|
||||
return HID_SUCCESS;
|
||||
} else {
|
||||
return HID_ERR_NO_RESOURCES;
|
||||
}
|
||||
}
|
||||
|
||||
void hidh_get_str_attr( tSDP_DISC_REC *p_rec, UINT16 attr_id, UINT16 max_len, char *str )
|
||||
{
|
||||
tSDP_DISC_ATTR *p_attr;
|
||||
UINT16 name_len;
|
||||
|
||||
if ((p_attr = SDP_FindAttributeInRec(p_rec, attr_id)) != NULL) {
|
||||
if ((name_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type)) < max_len ) {
|
||||
memcpy( str, (char *) p_attr->attr_value.v.array, name_len );
|
||||
str[name_len] = '\0';
|
||||
} else {
|
||||
memcpy( str, (char *) p_attr->attr_value.v.array, max_len - 1 );
|
||||
str[max_len - 1] = '\0';
|
||||
}
|
||||
} else {
|
||||
str[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void hidh_search_callback (UINT16 sdp_result)
|
||||
{
|
||||
tSDP_DISCOVERY_DB *p_db = hh_cb.p_sdp_db;
|
||||
tSDP_DISC_REC *p_rec;
|
||||
tSDP_DISC_ATTR *p_attr, *p_subattr1, *p_subattr2, *p_repdesc;
|
||||
tBT_UUID hid_uuid;
|
||||
tHID_DEV_SDP_INFO *p_nvi = &hh_cb.sdp_rec;
|
||||
UINT16 attr_mask = 0;
|
||||
|
||||
hid_uuid.len = LEN_UUID_16;
|
||||
hid_uuid.uu.uuid16 = UUID_SERVCLASS_HUMAN_INTERFACE;
|
||||
|
||||
hh_cb.sdp_busy = FALSE;
|
||||
|
||||
if (sdp_result != SDP_SUCCESS) {
|
||||
hh_cb.sdp_cback(sdp_result, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((p_rec = SDP_FindServiceUUIDInDb (p_db, &hid_uuid, NULL)) == NULL) {
|
||||
hh_cb.sdp_cback(HID_SDP_NO_SERV_UUID, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
memset (&hh_cb.sdp_rec, 0, sizeof( tHID_DEV_SDP_INFO ));
|
||||
|
||||
/* First, verify the mandatory fields we care about */
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DESCRIPTOR_LIST)) == NULL)
|
||||
|| (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE)
|
||||
|| ((p_subattr1 = p_attr->attr_value.v.p_sub_attr) == NULL)
|
||||
|| (SDP_DISC_ATTR_TYPE(p_subattr1->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE)
|
||||
|| ((p_subattr2 = p_subattr1->attr_value.v.p_sub_attr) == NULL)
|
||||
|| ((p_repdesc = p_subattr2->p_next_attr) == NULL)
|
||||
|| (SDP_DISC_ATTR_TYPE(p_repdesc->attr_len_type) != TEXT_STR_DESC_TYPE)) {
|
||||
hh_cb.sdp_cback(HID_SDP_MANDATORY_MISSING, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((p_nvi->dscp_info.dl_len = SDP_DISC_ATTR_LEN(p_repdesc->attr_len_type)) != 0) {
|
||||
p_nvi->dscp_info.dsc_list = (UINT8 *) &p_repdesc->attr_value;
|
||||
}
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_VIRTUAL_CABLE)) != NULL) &&
|
||||
(p_attr->attr_value.v.u8) ) {
|
||||
attr_mask |= HID_VIRTUAL_CABLE;
|
||||
}
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_RECONNECT_INITIATE)) != NULL) &&
|
||||
(p_attr->attr_value.v.u8) ) {
|
||||
attr_mask |= HID_RECONN_INIT;
|
||||
}
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_NORMALLY_CONNECTABLE)) != NULL) &&
|
||||
(p_attr->attr_value.v.u8) ) {
|
||||
attr_mask |= HID_NORMALLY_CONNECTABLE;
|
||||
}
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SDP_DISABLE)) != NULL) &&
|
||||
(p_attr->attr_value.v.u8) ) {
|
||||
attr_mask |= HID_SDP_DISABLE;
|
||||
}
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_BATTERY_POWER)) != NULL) &&
|
||||
(p_attr->attr_value.v.u8) ) {
|
||||
attr_mask |= HID_BATTERY_POWER;
|
||||
}
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_REMOTE_WAKE)) != NULL) &&
|
||||
(p_attr->attr_value.v.u8) ) {
|
||||
attr_mask |= HID_REMOTE_WAKE;
|
||||
}
|
||||
|
||||
hidh_get_str_attr( p_rec, ATTR_ID_SERVICE_NAME, HID_MAX_SVC_NAME_LEN, p_nvi->svc_name );
|
||||
hidh_get_str_attr( p_rec, ATTR_ID_SERVICE_DESCRIPTION, HID_MAX_SVC_DESCR_LEN, p_nvi->svc_descr );
|
||||
hidh_get_str_attr( p_rec, ATTR_ID_PROVIDER_NAME, HID_MAX_PROV_NAME_LEN, p_nvi->prov_name );
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DEVICE_RELNUM)) != NULL)) {
|
||||
p_nvi->rel_num = p_attr->attr_value.v.u16;
|
||||
}
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_COUNTRY_CODE)) != NULL)) {
|
||||
p_nvi->ctry_code = p_attr->attr_value.v.u8;
|
||||
}
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DEVICE_SUBCLASS)) != NULL)) {
|
||||
p_nvi->sub_class = p_attr->attr_value.v.u8;
|
||||
}
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_PARSER_VERSION)) != NULL)) {
|
||||
p_nvi->hpars_ver = p_attr->attr_value.v.u16;
|
||||
}
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_LINK_SUPERVISION_TO)) != NULL)) {
|
||||
attr_mask |= HID_SUP_TOUT_AVLBL;
|
||||
p_nvi->sup_timeout = p_attr->attr_value.v.u16;
|
||||
}
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SSR_HOST_MAX_LAT)) != NULL)) {
|
||||
attr_mask |= HID_SSR_MAX_LATENCY;
|
||||
p_nvi->ssr_max_latency = p_attr->attr_value.v.u16;
|
||||
} else {
|
||||
p_nvi->ssr_max_latency = HID_SSR_PARAM_INVALID;
|
||||
}
|
||||
|
||||
if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SSR_HOST_MIN_TOUT)) != NULL)) {
|
||||
attr_mask |= HID_SSR_MIN_TOUT;
|
||||
p_nvi->ssr_min_tout = p_attr->attr_value.v.u16;
|
||||
} else {
|
||||
p_nvi->ssr_min_tout = HID_SSR_PARAM_INVALID;
|
||||
}
|
||||
|
||||
hh_cb.sdp_rec.p_sdp_layer_rec = p_rec;
|
||||
hh_cb.sdp_cback(SDP_SUCCESS, attr_mask, &hh_cb.sdp_rec);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function HID_HostInit
|
||||
**
|
||||
** Description This function initializes the control block and trace variable
|
||||
**
|
||||
** Returns tHID_STATUS
|
||||
**
|
||||
*******************************************************************************/
|
||||
tHID_STATUS HID_HostInit (void)
|
||||
{
|
||||
#if (HID_DYNAMIC_MEMORY)
|
||||
if (!hidh_cb_ptr) {
|
||||
hidh_cb_ptr = (tHID_HOST_CTB *)osi_malloc(sizeof(tHID_HOST_CTB));
|
||||
if (!hidh_cb_ptr) {
|
||||
return HID_ERR_NO_RESOURCES;
|
||||
}
|
||||
}
|
||||
#endif /* #if (HID_DYNAMIC_MEMORY) */
|
||||
memset(&hh_cb, 0, sizeof(tHID_HOST_CTB));
|
||||
|
||||
#if defined(HIDH_INITIAL_TRACE_LEVEL)
|
||||
hh_cb.trace_level = HIDH_INITIAL_TRACE_LEVEL;
|
||||
#else
|
||||
hh_cb.trace_level = BT_TRACE_LEVEL_NONE;
|
||||
#endif
|
||||
return HID_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function HID_HostInit
|
||||
**
|
||||
** Description This function deinitializes the control block
|
||||
**
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
void HID_HostDeinit (void)
|
||||
{
|
||||
#if (HID_DYNAMIC_MEMORY)
|
||||
if (hidh_cb_ptr) {
|
||||
osi_free(hidh_cb_ptr);
|
||||
hidh_cb_ptr = NULL;
|
||||
}
|
||||
#endif /* #if (HID_DYNAMIC_MEMORY) */
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function HID_HostSetTraceLevel
|
||||
**
|
||||
** Description This function sets the trace level for HID Host. If called with
|
||||
** a value of 0xFF, it simply reads the current trace level.
|
||||
**
|
||||
** Returns the new (current) trace level
|
||||
**
|
||||
*******************************************************************************/
|
||||
UINT8 HID_HostSetTraceLevel (UINT8 new_level)
|
||||
{
|
||||
if (new_level != 0xFF) {
|
||||
hh_cb.trace_level = new_level;
|
||||
}
|
||||
|
||||
return (hh_cb.trace_level);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function HID_HostRegister
|
||||
**
|
||||
** Description This function registers HID-Host with lower layers
|
||||
**
|
||||
** Returns tHID_STATUS
|
||||
**
|
||||
*******************************************************************************/
|
||||
tHID_STATUS HID_HostRegister (tHID_HOST_DEV_CALLBACK *dev_cback)
|
||||
{
|
||||
tHID_STATUS st;
|
||||
|
||||
if ( hh_cb.reg_flag ) {
|
||||
return HID_ERR_ALREADY_REGISTERED;
|
||||
}
|
||||
|
||||
if ( dev_cback == NULL ) {
|
||||
return HID_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
/* Register with L2CAP */
|
||||
if ( (st = hidh_conn_reg()) != HID_SUCCESS ) {
|
||||
return st;
|
||||
}
|
||||
|
||||
hh_cb.callback = dev_cback ;
|
||||
hh_cb.reg_flag = TRUE;
|
||||
|
||||
return (HID_SUCCESS);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function HID_HostDeregister
|
||||
**
|
||||
** Description This function is called when the host is about power down.
|
||||
**
|
||||
** Returns tHID_STATUS
|
||||
**
|
||||
*******************************************************************************/
|
||||
tHID_STATUS HID_HostDeregister(void)
|
||||
{
|
||||
UINT8 i;
|
||||
|
||||
if ( !hh_cb.reg_flag ) {
|
||||
return (HID_ERR_NOT_REGISTERED);
|
||||
}
|
||||
|
||||
for ( i = 0; i < HID_HOST_MAX_DEVICES; i++ ) {
|
||||
HID_HostRemoveDev( i ) ;
|
||||
}
|
||||
|
||||
hidh_conn_dereg();
|
||||
hh_cb.reg_flag = FALSE;
|
||||
|
||||
return (HID_SUCCESS) ;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function HID_HostAddDev
|
||||
**
|
||||
** Description This is called so HID-host may manage this device.
|
||||
**
|
||||
** Returns tHID_STATUS
|
||||
**
|
||||
*******************************************************************************/
|
||||
tHID_STATUS HID_HostAddDev ( BD_ADDR addr, UINT16 attr_mask, UINT8 *handle )
|
||||
{
|
||||
int i;
|
||||
/* Find an entry for this device in hh_cb.devices array */
|
||||
if ( !hh_cb.reg_flag ) {
|
||||
return (HID_ERR_NOT_REGISTERED);
|
||||
}
|
||||
|
||||
for ( i = 0; i < HID_HOST_MAX_DEVICES; i++) {
|
||||
if ((hh_cb.devices[i].in_use) &&
|
||||
(!memcmp(addr, hh_cb.devices[i].addr, BD_ADDR_LEN))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == HID_HOST_MAX_DEVICES ) {
|
||||
for ( i = 0; i < HID_HOST_MAX_DEVICES; i++) {
|
||||
if ( !hh_cb.devices[i].in_use) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( i == HID_HOST_MAX_DEVICES ) {
|
||||
return HID_ERR_NO_RESOURCES;
|
||||
}
|
||||
|
||||
if (!hh_cb.devices[i].in_use) {
|
||||
hh_cb.devices[i].in_use = TRUE;
|
||||
hh_cb.devices[i].delay_remove = FALSE;
|
||||
memcpy( hh_cb.devices[i].addr, addr, sizeof( BD_ADDR ) ) ;
|
||||
hh_cb.devices[i].state = HID_DEV_NO_CONN;
|
||||
hh_cb.devices[i].conn_tries = 0 ;
|
||||
}
|
||||
|
||||
if (attr_mask != HID_ATTR_MASK_IGNORE) {
|
||||
hh_cb.devices[i].attr_mask = attr_mask;
|
||||
}
|
||||
|
||||
*handle = i;
|
||||
|
||||
return (HID_SUCCESS);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function HID_HostGetDev
|
||||
**
|
||||
** Description This is called so HID-host can find this device.
|
||||
**
|
||||
** Returns tHID_STATUS
|
||||
**
|
||||
*******************************************************************************/
|
||||
tHID_STATUS HID_HostGetDev(BD_ADDR addr, UINT8 *handle)
|
||||
{
|
||||
int i;
|
||||
/* Find an entry for this device in hh_cb.devices array */
|
||||
if (!hh_cb.reg_flag) {
|
||||
return (HID_ERR_NOT_REGISTERED);
|
||||
}
|
||||
|
||||
for (i = 0; i < HID_HOST_MAX_DEVICES; i++) {
|
||||
if ((hh_cb.devices[i].in_use) && (!memcmp(addr, hh_cb.devices[i].addr, BD_ADDR_LEN))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == HID_HOST_MAX_DEVICES) {
|
||||
*handle = 0xff;
|
||||
} else {
|
||||
*handle = i;
|
||||
}
|
||||
return (HID_SUCCESS);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function HID_HostRemoveDev
|
||||
**
|
||||
** Description This removes the device from list devices that host has to manage.
|
||||
**
|
||||
** Returns tHID_STATUS
|
||||
**
|
||||
*******************************************************************************/
|
||||
tHID_STATUS HID_HostRemoveDev ( UINT8 dev_handle )
|
||||
{
|
||||
if ( !hh_cb.reg_flag ) {
|
||||
return (HID_ERR_NOT_REGISTERED);
|
||||
}
|
||||
|
||||
if ( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) {
|
||||
return HID_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
HID_HostCloseDev( dev_handle ) ;
|
||||
|
||||
if (hh_cb.devices[dev_handle].conn.conn_state == HID_CONN_STATE_DISCONNECTING_INTR ||
|
||||
hh_cb.devices[dev_handle].conn.conn_state == HID_CONN_STATE_DISCONNECTING_CTRL) {
|
||||
// delay the remove action, to close the control and the interrupt channel
|
||||
hh_cb.devices[dev_handle].delay_remove = TRUE;
|
||||
} else {
|
||||
HIDH_TRACE_WARNING("%s dev_handle:%d conn_state:%d", __func__, dev_handle,
|
||||
hh_cb.devices[dev_handle].conn.conn_state);
|
||||
hh_cb.devices[dev_handle].in_use = FALSE;
|
||||
hh_cb.devices[dev_handle].conn.conn_state = HID_CONN_STATE_UNUSED;
|
||||
hh_cb.devices[dev_handle].conn.ctrl_cid = hh_cb.devices[dev_handle].conn.intr_cid = 0;
|
||||
hh_cb.devices[dev_handle].attr_mask = 0;
|
||||
}
|
||||
|
||||
return HID_SUCCESS;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function HID_HostOpenDev
|
||||
**
|
||||
** Description This function is called when the user wants to initiate a
|
||||
** connection attempt to a device.
|
||||
**
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
tHID_STATUS HID_HostOpenDev ( UINT8 dev_handle )
|
||||
{
|
||||
if ( !hh_cb.reg_flag ) {
|
||||
return (HID_ERR_NOT_REGISTERED);
|
||||
}
|
||||
|
||||
if ( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) {
|
||||
return HID_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
if ( hh_cb.devices[dev_handle].state != HID_DEV_NO_CONN ) {
|
||||
return HID_ERR_ALREADY_CONN;
|
||||
}
|
||||
|
||||
hh_cb.devices[dev_handle].conn_tries = 1;
|
||||
return hidh_conn_initiate( dev_handle );
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function HID_HostWriteDev
|
||||
**
|
||||
** Description This function is called when the host has a report to send.
|
||||
**
|
||||
** report_id: is only used on GET_REPORT transaction if is specified.
|
||||
** only valid when it's a non-zero value.
|
||||
**
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
tHID_STATUS HID_HostWriteDev( UINT8 dev_handle, UINT8 t_type,
|
||||
UINT8 param, UINT16 data, UINT8 report_id, BT_HDR *pbuf )
|
||||
{
|
||||
tHID_STATUS status = HID_SUCCESS;
|
||||
|
||||
if ( !hh_cb.reg_flag ) {
|
||||
HIDH_TRACE_ERROR("HID_ERR_NOT_REGISTERED");
|
||||
status = HID_ERR_NOT_REGISTERED;
|
||||
}
|
||||
|
||||
if ( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) {
|
||||
HIDH_TRACE_ERROR("HID_ERR_INVALID_PARAM");
|
||||
status = HID_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
else if ( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED ) {
|
||||
HIDH_TRACE_ERROR("HID_ERR_NO_CONNECTION dev_handle %d", dev_handle);
|
||||
status = HID_ERR_NO_CONNECTION;
|
||||
}
|
||||
|
||||
if (status != HID_SUCCESS) {
|
||||
if (pbuf) {
|
||||
osi_free ((void *)pbuf);
|
||||
}
|
||||
} else {
|
||||
status = hidh_conn_snd_data( dev_handle, t_type, param, data, report_id, pbuf ) ;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Function HID_HostCloseDev
|
||||
**
|
||||
** Description This function disconnects the device.
|
||||
**
|
||||
** Returns void
|
||||
**
|
||||
*******************************************************************************/
|
||||
tHID_STATUS HID_HostCloseDev( UINT8 dev_handle )
|
||||
{
|
||||
if ( !hh_cb.reg_flag ) {
|
||||
return (HID_ERR_NOT_REGISTERED);
|
||||
}
|
||||
|
||||
if ( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) {
|
||||
return HID_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY + 1;
|
||||
btu_stop_timer( &(hh_cb.devices[dev_handle].conn.timer_entry) ) ;
|
||||
|
||||
if ( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED ) {
|
||||
return HID_ERR_NO_CONNECTION;
|
||||
}
|
||||
|
||||
hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY + 1;
|
||||
return hidh_conn_disconnect( dev_handle );
|
||||
}
|
||||
|
||||
tHID_STATUS HID_HostSetSecurityLevel( char serv_name[], UINT8 sec_lvl )
|
||||
{
|
||||
if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HIDH_SEC_CTRL,
|
||||
sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN)) {
|
||||
HIDH_TRACE_ERROR ("Security Registration 1 failed");
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
}
|
||||
|
||||
if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HIDH_SEC_CTRL,
|
||||
sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN)) {
|
||||
HIDH_TRACE_ERROR ("Security Registration 2 failed");
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
}
|
||||
|
||||
if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HIDH_NOSEC_CTRL,
|
||||
BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN)) {
|
||||
HIDH_TRACE_ERROR ("Security Registration 3 failed");
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
}
|
||||
|
||||
if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HIDH_NOSEC_CTRL,
|
||||
BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN)) {
|
||||
HIDH_TRACE_ERROR ("Security Registration 4 failed");
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
}
|
||||
|
||||
if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HIDH_INTR,
|
||||
BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) {
|
||||
HIDH_TRACE_ERROR ("Security Registration 5 failed");
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
}
|
||||
|
||||
if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HIDH_INTR,
|
||||
BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) {
|
||||
HIDH_TRACE_ERROR ("Security Registration 6 failed");
|
||||
return (HID_ERR_NO_RESOURCES);
|
||||
}
|
||||
|
||||
return ( HID_SUCCESS );
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
**
|
||||
** Function hid_known_hid_device
|
||||
**
|
||||
** Description check if this device is of type HID Device
|
||||
**
|
||||
** Returns TRUE if device is HID Device else FALSE
|
||||
**
|
||||
*******************************************************************************/
|
||||
BOOLEAN hid_known_hid_device (BD_ADDR bd_addr)
|
||||
{
|
||||
UINT8 i;
|
||||
tBTM_INQ_INFO *p_inq_info = BTM_InqDbRead(bd_addr);
|
||||
|
||||
if ( !hh_cb.reg_flag ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* First check for class of device , if Inq DB has information about this device*/
|
||||
if (p_inq_info != NULL) {
|
||||
/* Check if remote major device class is of type BTM_COD_MAJOR_PERIPHERAL */
|
||||
if ((p_inq_info->results.dev_class[1] & BTM_COD_MAJOR_CLASS_MASK)
|
||||
== BTM_COD_MAJOR_PERIPHERAL ) {
|
||||
HIDH_TRACE_DEBUG("hid_known_hid_device:dev found in InqDB & COD matches HID dev");
|
||||
return TRUE;
|
||||
}
|
||||
} else {
|
||||
/* Look for this device in security device DB */
|
||||
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr);
|
||||
if ((p_dev_rec != NULL) &&
|
||||
((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL )) {
|
||||
HIDH_TRACE_DEBUG("hid_known_hid_device:dev found in SecDevDB & COD matches HID dev");
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find an entry for this device in hh_cb.devices array */
|
||||
for ( i = 0; i < HID_HOST_MAX_DEVICES; i++) {
|
||||
if ((hh_cb.devices[i].in_use) &&
|
||||
(memcmp(bd_addr, hh_cb.devices[i].addr, BD_ADDR_LEN) == 0)) {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
/* Check if this device is marked as HID Device in IOP Dev */
|
||||
HIDH_TRACE_DEBUG("hid_known_hid_device:remote is not HID device");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#endif //HID_HOST_INCLUDED
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,72 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2002-2012 Broadcom Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* This file contains HID connection internal definitions
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef HID_CONN_H
|
||||
#define HID_CONN_H
|
||||
|
||||
#include "common/bt_defs.h"
|
||||
|
||||
#if (BT_HID_INCLUDED == TRUE)
|
||||
|
||||
/* Define the HID Connection Block
|
||||
*/
|
||||
typedef struct hid_conn {
|
||||
#define HID_CONN_STATE_UNUSED (0)
|
||||
#define HID_CONN_STATE_CONNECTING_CTRL (1)
|
||||
#define HID_CONN_STATE_CONNECTING_INTR (2)
|
||||
#define HID_CONN_STATE_CONFIG (3)
|
||||
#define HID_CONN_STATE_CONNECTED (4)
|
||||
#define HID_CONN_STATE_DISCONNECTING (5)
|
||||
#define HID_CONN_STATE_SECURITY (6)
|
||||
#define HID_CONN_STATE_DISCONNECTING_CTRL (7)
|
||||
#define HID_CONN_STATE_DISCONNECTING_INTR (8)
|
||||
|
||||
UINT8 conn_state;
|
||||
|
||||
#define HID_CONN_FLAGS_IS_ORIG (0x01)
|
||||
#define HID_CONN_FLAGS_HIS_CTRL_CFG_DONE (0x02)
|
||||
#define HID_CONN_FLAGS_MY_CTRL_CFG_DONE (0x04)
|
||||
#define HID_CONN_FLAGS_HIS_INTR_CFG_DONE (0x08)
|
||||
#define HID_CONN_FLAGS_MY_INTR_CFG_DONE (0x10)
|
||||
#define HID_CONN_FLAGS_ALL_CONFIGURED (0x1E) /* All the config done */
|
||||
#define HID_CONN_FLAGS_CONGESTED (0x20)
|
||||
#define HID_CONN_FLAGS_INACTIVE (0x40)
|
||||
|
||||
UINT8 conn_flags;
|
||||
|
||||
UINT8 ctrl_id;
|
||||
UINT16 ctrl_cid;
|
||||
UINT16 intr_cid;
|
||||
UINT16 rem_mtu_size;
|
||||
UINT16 disc_reason; /* Reason for disconnecting (for HID_HDEV_EVT_CLOSE) */
|
||||
TIMER_LIST_ENT timer_entry;
|
||||
} tHID_CONN;
|
||||
|
||||
#define HID_SEC_CHN 1
|
||||
#define HID_NOSEC_CHN 2
|
||||
#define HIDD_SEC_CHN 3
|
||||
#define HIDD_NOSEC_CHN 4
|
||||
|
||||
#endif ///BT_HID_INCLUDED == TRUE
|
||||
#endif
|
||||
@@ -0,0 +1,144 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
* Copyright (C) 2002-2012 Broadcom Corporation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at:
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
******************************************************************************/
|
||||
/******************************************************************************
|
||||
*
|
||||
* This file contains HID DEVICE internal definitions
|
||||
*
|
||||
******************************************************************************/
|
||||
#ifndef HID_INT_H
|
||||
#define HID_INT_H
|
||||
|
||||
#include "hid_conn.h"
|
||||
#include "stack/l2c_api.h"
|
||||
#if (BT_HID_INCLUDED == TRUE)
|
||||
|
||||
#if (HID_HOST_INCLUDED == TRUE)
|
||||
#include "stack/hidh_api.h"
|
||||
enum { HID_DEV_NO_CONN, HID_DEV_CONNECTED };
|
||||
|
||||
typedef struct per_device_ctb {
|
||||
BOOLEAN in_use;
|
||||
BOOLEAN delay_remove;
|
||||
BD_ADDR addr; /* BD-Addr of the host device */
|
||||
UINT16 attr_mask; /* 0x01- virtual_cable; 0x02- normally_connectable; 0x03- reconn_initiate;
|
||||
0x04- sdp_disable; */
|
||||
UINT8 state; /* Device state if in HOST-KNOWN mode */
|
||||
UINT8 conn_substate;
|
||||
UINT8 conn_tries; /* Remembers to the number of connection attempts while CONNECTING */
|
||||
|
||||
tHID_CONN conn; /* L2CAP channel info */
|
||||
} tHID_HOST_DEV_CTB;
|
||||
|
||||
typedef struct host_ctb {
|
||||
tHID_HOST_DEV_CTB devices[HID_HOST_MAX_DEVICES];
|
||||
tHID_HOST_DEV_CALLBACK *callback; /* Application callbacks */
|
||||
tL2CAP_CFG_INFO l2cap_cfg;
|
||||
|
||||
#define MAX_SERVICE_DB_SIZE 4000
|
||||
|
||||
BOOLEAN sdp_busy;
|
||||
tHID_HOST_SDP_CALLBACK *sdp_cback;
|
||||
tSDP_DISCOVERY_DB *p_sdp_db;
|
||||
tHID_DEV_SDP_INFO sdp_rec;
|
||||
BOOLEAN reg_flag;
|
||||
UINT8 trace_level;
|
||||
} tHID_HOST_CTB;
|
||||
|
||||
extern tHID_STATUS hidh_conn_snd_data(UINT8 dhandle, UINT8 trans_type, UINT8 param, \
|
||||
UINT16 data, UINT8 rpt_id, BT_HDR *buf);
|
||||
extern tHID_STATUS hidh_conn_reg (void);
|
||||
extern void hidh_conn_dereg( void );
|
||||
extern tHID_STATUS hidh_conn_disconnect (UINT8 dhandle);
|
||||
extern tHID_STATUS hidh_conn_initiate (UINT8 dhandle);
|
||||
extern void hidh_proc_repage_timeout (TIMER_LIST_ENT *p_tle);
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* Main Control Block
|
||||
******************************************************************************/
|
||||
|
||||
#if HID_DYNAMIC_MEMORY == FALSE
|
||||
extern tHID_HOST_CTB hh_cb;
|
||||
#else
|
||||
extern tHID_HOST_CTB *hidh_cb_ptr;
|
||||
#define hh_cb (*hidh_cb_ptr)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* HID_HOST_INCLUDED == TRUE */
|
||||
|
||||
#if (HID_DEV_INCLUDED == TRUE)
|
||||
#include "stack/hidd_api.h"
|
||||
enum { HIDD_DEV_NO_CONN, HIDD_DEV_CONNECTED };
|
||||
|
||||
typedef struct device_ctb {
|
||||
bool in_use;
|
||||
BD_ADDR addr;
|
||||
uint8_t state;
|
||||
tHID_CONN conn;
|
||||
bool boot_mode;
|
||||
uint8_t idle_time;
|
||||
} tHID_DEV_DEV_CTB;
|
||||
|
||||
typedef struct dev_ctb {
|
||||
tHID_DEV_DEV_CTB device;
|
||||
tHID_DEV_HOST_CALLBACK *callback;
|
||||
tL2CAP_CFG_INFO l2cap_cfg;
|
||||
tL2CAP_CFG_INFO l2cap_intr_cfg;
|
||||
bool use_in_qos;
|
||||
FLOW_SPEC in_qos;
|
||||
bool reg_flag;
|
||||
uint8_t trace_level;
|
||||
bool allow_incoming;
|
||||
BT_HDR *pending_data;
|
||||
bool pending_vc_unplug;
|
||||
} tHID_DEV_CTB;
|
||||
|
||||
extern tHID_STATUS hidd_conn_reg(void);
|
||||
extern void hidd_conn_dereg(void);
|
||||
extern tHID_STATUS hidd_conn_initiate(void);
|
||||
extern tHID_STATUS hidd_conn_disconnect(void);
|
||||
extern tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param, uint8_t data, uint16_t len,
|
||||
uint8_t *p_data);
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/******************************************************************************
|
||||
* Main Control Block
|
||||
******************************************************************************/
|
||||
|
||||
#if HID_DYNAMIC_MEMORY == FALSE
|
||||
extern tHID_DEV_CTB hd_cb;
|
||||
#else
|
||||
extern tHID_DEV_CTB *hidd_cb_ptr;
|
||||
#define hd_cb (*hidd_cb_ptr)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* HID_DEV_INCLUDED == TRUE */
|
||||
|
||||
#endif /* BT_HID_INCLUDED == TRUE */
|
||||
#endif /* HID_INT_H */
|
||||
Reference in New Issue
Block a user