blob: ab8bad48d94a171eea343d4c57d295f4a3c7d2ad [file] [log] [blame]
/******************************************************************************
*
* Copyright (c) 2013, The Linux Foundation. All rights reserved.
* Not a Contribution.
* 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 <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "gki.h"
#include "bt_types.h"
#include "hiddefs.h"
#include "hidd_api.h"
#include "hidd_int.h"
#include "btm_api.h"
#include "btu.h"
#if HID_DYNAMIC_MEMORY == FALSE
tHID_DEV_CTB hd_cb;
#endif
/*******************************************************************************
**
** Function HID_DevInit
**
** Description Initializes control block
**
** Returns void
**
*******************************************************************************/
void HID_DevInit(void)
{
UINT8 log_level = hd_cb.trace_level;
HIDD_TRACE_API("%s", __FUNCTION__);
memset(&hd_cb, 0, sizeof(tHID_DEV_CTB));
hd_cb.trace_level = log_level;
}
/*******************************************************************************
**
** 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 HID_DevSetTraceLevel (UINT8 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", __FUNCTION__);
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)
{
GKI_freebuf(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", __FUNCTION__);
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 sec_lvl)
{
HIDD_TRACE_API("%s", __FUNCTION__);
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 handle, char *p_name, char *p_description, char *p_provider,
UINT16 subclass, UINT16 desc_len, UINT8 *p_desc_data)
{
BOOLEAN result = TRUE;
HIDD_TRACE_API("%s", __FUNCTION__);
// Service Class ID List
if (result)
{
UINT16 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 *) srv_name);
result &= SDP_AddAttribute(handle, ATTR_ID_SERVICE_DESCRIPTION, TEXT_STR_DESC_TYPE,
strlen(srv_desc) + 1, (UINT8 *) srv_desc);
result &= SDP_AddAttribute(handle, ATTR_ID_PROVIDER_NAME, TEXT_STR_DESC_TYPE,
strlen(provider_name) + 1, (UINT8 *) provider_name);
}
// Bluetooth Profile Descriptor List
if (result)
{
const UINT16 profile_uuid = UUID_SERVCLASS_HUMAN_INTERFACE;
const UINT16 version = 0x0100;
result &= SDP_AddProfileDescriptorList(handle, profile_uuid, version);
}
// HID Parser Version
if (result)
{
UINT8 *p;
const UINT16 rel_num = 0x0100;
const UINT16 parser_version = 0x0111;
const UINT16 prof_ver = 0x0100;
const UINT8 dev_subclass = subclass;
const UINT8 country_code = 0x21;
const UINT8 bool_false = 0x00;
const UINT8 bool_true = 0x01;
UINT16 temp;
p = (UINT8*) &temp;
UINT16_TO_BE_STREAM(p, rel_num);
result &= SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_RELNUM, UINT_DESC_TYPE, 2,
(UINT8*) &temp);
p = (UINT8*) &temp;
UINT16_TO_BE_STREAM(p, parser_version);
result &= SDP_AddAttribute(handle, ATTR_ID_HID_PARSER_VERSION, UINT_DESC_TYPE, 2,
(UINT8*) &temp);
result &= SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_SUBCLASS, UINT_DESC_TYPE, 1,
(UINT8*) &dev_subclass);
result &= SDP_AddAttribute(handle, ATTR_ID_HID_COUNTRY_CODE, UINT_DESC_TYPE, 1,
(UINT8*) &country_code);
result &= SDP_AddAttribute(handle, ATTR_ID_HID_VIRTUAL_CABLE, BOOLEAN_DESC_TYPE, 1,
(UINT8*) &bool_true);
result &= SDP_AddAttribute(handle, ATTR_ID_HID_RECONNECT_INITIATE, BOOLEAN_DESC_TYPE, 1,
(UINT8*) &bool_true);
{
static UINT8 cdt = 0x22;
UINT8 *p_buf;
UINT8 seq_len = 4 + desc_len;
p_buf = (UINT8 *) GKI_getbuf(2048);
if (p_buf == NULL)
{
HIDD_TRACE_ERROR("%s: Buffer allocation failure for size = 2048 ",
__FUNCTION__);
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);
GKI_freebuf(p_buf);
}
{
UINT8 lang_buf[8];
p = lang_buf;
UINT8 seq_len = 6;
UINT16 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*) &bool_true);
result &= SDP_AddAttribute(handle, ATTR_ID_HID_REMOTE_WAKE, BOOLEAN_DESC_TYPE, 1,
(UINT8*) &bool_false);
result &= SDP_AddAttribute(handle, ATTR_ID_HID_NORMALLY_CONNECTABLE, BOOLEAN_DESC_TYPE, 1,
(UINT8*) &bool_true);
result &= SDP_AddAttribute(handle, ATTR_ID_HID_BOOT_DEVICE, BOOLEAN_DESC_TYPE, 1,
(UINT8*) &bool_true);
p = (UINT8*) &temp;
UINT16_TO_BE_STREAM(p, prof_ver);
result &= SDP_AddAttribute(handle, ATTR_ID_HID_PROFILE_VERSION, UINT_DESC_TYPE, 2,
(UINT8*) &temp);
}
if (result)
{
UINT16 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", __FUNCTION__);
return HID_ERR_NOT_REGISTERED;
}
return HID_SUCCESS;
}
/*******************************************************************************
**
** Function HID_DevSendReport
**
** Description Sends report
**
** Returns tHID_STATUS
**
*******************************************************************************/
tHID_STATUS HID_DevSendReport(UINT8 channel, UINT8 type, UINT8 id, UINT16 len, UINT8 *p_data)
{
HIDD_TRACE_VERBOSE("%s: channel=%d type=%d id=%d len=%d", __FUNCTION__, 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", __FUNCTION__);
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(BOOLEAN 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 error)
{
UINT8 handshake_param;
HIDD_TRACE_API("%s: error = %d", __FUNCTION__, 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", __FUNCTION__);
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 service_type, UINT32 token_rate,
UINT32 token_bucket_size, UINT32 peak_bandwidth,
UINT32 latency, UINT32 delay_variation)
{
HIDD_TRACE_API("%s", __FUNCTION__);
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 service_type, UINT32 token_rate,
UINT32 token_bucket_size, UINT32 peak_bandwidth,
UINT32 latency, UINT32 delay_variation)
{
HIDD_TRACE_API("%s", __FUNCTION__);
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;
}