mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Add HID Support to IDF
- Adds HID Host support in Buedroid - Adds BLE HID Host and Device support - Adds some general HID utilities and definitions to help integrate with other stacks and native USB
This commit is contained in:
parent
ef47839628
commit
25281ef4de
@ -47,6 +47,7 @@ if(CONFIG_BT_ENABLED)
|
||||
host/bluedroid/btc/profile/esp/blufi/include
|
||||
host/bluedroid/btc/profile/esp/include
|
||||
host/bluedroid/btc/profile/std/a2dp/include
|
||||
host/bluedroid/btc/profile/std/hid/include
|
||||
host/bluedroid/btc/profile/std/include
|
||||
host/bluedroid/btc/include
|
||||
host/bluedroid/stack/btm/include
|
||||
@ -166,6 +167,8 @@ if(CONFIG_BT_ENABLED)
|
||||
"host/bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c"
|
||||
"host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c"
|
||||
"host/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c"
|
||||
"host/bluedroid/btc/profile/std/hid/hidh_api.c"
|
||||
"host/bluedroid/btc/profile/std/hid/hidh_conn.c"
|
||||
"host/bluedroid/btc/profile/std/gap/btc_gap_ble.c"
|
||||
"host/bluedroid/btc/profile/std/gap/btc_gap_bt.c"
|
||||
"host/bluedroid/btc/profile/std/gap/bta_gap_bt_co.c"
|
||||
|
@ -45,6 +45,7 @@ COMPONENT_PRIV_INCLUDEDIRS += host/bluedroid/bta/include \
|
||||
host/bluedroid/btc/profile/std/gatt/include \
|
||||
host/bluedroid/btc/profile/std/gap/include \
|
||||
host/bluedroid/btc/profile/std/a2dp/include \
|
||||
host/bluedroid/btc/profile/std/hid/include \
|
||||
host/bluedroid/btc/profile/std/include \
|
||||
host/bluedroid/btc/include \
|
||||
host/bluedroid/btif/include \
|
||||
@ -96,6 +97,7 @@ COMPONENT_SRCDIRS += host/bluedroid/bta/dm \
|
||||
host/bluedroid/btc/profile/std/spp \
|
||||
host/bluedroid/btc/profile/std/hf_ag \
|
||||
host/bluedroid/btc/profile/std/hf_client \
|
||||
host/bluedroid/btc/profile/std/hid \
|
||||
host/bluedroid/btc/profile \
|
||||
host/bluedroid/stack/btm \
|
||||
host/bluedroid/stack/btu \
|
||||
|
@ -99,6 +99,13 @@ config BT_HFP_WBS_ENABLE
|
||||
This enables Wide Band Speech. Should disable it when SCO data path is PCM.
|
||||
Otherwise there will be no data transmited via GPIOs.
|
||||
|
||||
config BT_HID_HOST_ENABLED
|
||||
bool "Classic BT HID Host"
|
||||
depends on BT_CLASSIC_ENABLED
|
||||
default n
|
||||
help
|
||||
This enables the BT HID Host
|
||||
|
||||
config BT_SSP_ENABLED
|
||||
bool "Secure Simple Pairing"
|
||||
depends on BT_CLASSIC_ENABLED
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "bta_hh_int.h"
|
||||
#include "bta/bta_hh_co.h"
|
||||
#include "bta/utl.h"
|
||||
#include "osi/allocator.h"
|
||||
|
||||
/*****************************************************************************
|
||||
** Constants
|
||||
@ -196,7 +197,7 @@ static void bta_hh_sdp_cback(UINT16 result, UINT16 attr_mask,
|
||||
}
|
||||
|
||||
#if BTA_HH_DEBUG
|
||||
APPL_TRACE_EVENT("bta_hh_sdp_cback: p_cb: %d result 0x%02x, \
|
||||
APPL_TRACE_EVENT("bta_hh_sdp_cback: p_cb: %p result 0x%02x, \
|
||||
attr_mask 0x%02x, handle %x", \
|
||||
p_cb, result, attr_mask, p_cb->hid_handle);
|
||||
#endif
|
||||
@ -261,7 +262,7 @@ static void bta_hh_di_sdp_cback(UINT16 result)
|
||||
tSDP_DI_GET_RECORD di_rec;
|
||||
tHID_STATUS ret;
|
||||
#if BTA_HH_DEBUG
|
||||
APPL_TRACE_EVENT("bta_hh_di_sdp_cback: p_cb: %d result 0x%02x", p_cb, result);
|
||||
APPL_TRACE_EVENT("bta_hh_di_sdp_cback: p_cb: %p result 0x%02x", p_cb, result);
|
||||
#endif
|
||||
|
||||
/* if DI record does not exist on remote device, vendor_id in tBTA_HH_DEV_DSCP_INFO will be
|
||||
|
@ -35,7 +35,7 @@
|
||||
#include "stack/l2c_api.h"
|
||||
#include "bta/utl.h"
|
||||
|
||||
#include "osi/include/log.h"
|
||||
#include "osi/allocator.h"
|
||||
|
||||
/*****************************************************************************
|
||||
** Constants
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "bta/bta_hh_api.h"
|
||||
#include "bta_hh_int.h"
|
||||
#include "osi/allocator.h"
|
||||
|
||||
/*****************************************************************************
|
||||
** Constants and types
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "common/bt_target.h"
|
||||
#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE)
|
||||
|
||||
|
||||
#include "osi/allocator.h"
|
||||
#include "bta_hh_int.h"
|
||||
|
||||
/* if SSR max latency is not defined by remote device, set the default value
|
||||
|
583
components/bt/host/bluedroid/btc/profile/std/hid/hidh_api.c
Normal file
583
components/bt/host/bluedroid/btc/profile/std/hid/hidh_api.c
Normal file
@ -0,0 +1,583 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* 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 "hidh_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;
|
||||
#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 void
|
||||
**
|
||||
*******************************************************************************/
|
||||
void HID_HostInit (void)
|
||||
{
|
||||
memset(&hh_cb, 0, sizeof(tHID_HOST_CTB));
|
||||
|
||||
#if defined(HID_INITIAL_TRACE_LEVEL)
|
||||
hh_cb.trace_level = HID_INITIAL_TRACE_LEVEL;
|
||||
#else
|
||||
hh_cb.trace_level = BT_TRACE_LEVEL_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** 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;
|
||||
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_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 ) ;
|
||||
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
|
1036
components/bt/host/bluedroid/btc/profile/std/hid/hidh_conn.c
Normal file
1036
components/bt/host/bluedroid/btc/profile/std/hid/hidh_conn.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,9 @@
|
||||
#ifndef HID_CONN_H
|
||||
#define HID_CONN_H
|
||||
|
||||
#include "common/bt_defs.h"
|
||||
#if (HID_HOST_INCLUDED == TRUE)
|
||||
|
||||
/* Define the HID Connection Block
|
||||
*/
|
||||
typedef struct hid_conn {
|
||||
@ -56,7 +58,6 @@ typedef struct hid_conn {
|
||||
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
|
||||
|
@ -67,6 +67,13 @@
|
||||
#define UC_BT_HFP_CLIENT_ENABLED FALSE
|
||||
#endif
|
||||
|
||||
//HID HOST(BT)
|
||||
#ifdef CONFIG_BT_HID_HOST_ENABLED
|
||||
#define UC_BT_HID_HOST_ENABLED CONFIG_BT_HID_HOST_ENABLED
|
||||
#else
|
||||
#define UC_BT_HID_HOST_ENABLED FALSE
|
||||
#endif
|
||||
|
||||
//SSP
|
||||
#ifdef CONFIG_BT_SSP_ENABLED
|
||||
#define UC_BT_SSP_ENABLED CONFIG_BT_SSP_ENABLED
|
||||
@ -292,10 +299,10 @@
|
||||
#define UC_BT_LOG_MCA_TRACE_LEVEL UC_TRACE_LEVEL_WARNING
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_LOG_HID_TRACE_LEVEL
|
||||
#define UC_BT_LOG_HID_TRACE_LEVEL CONFIG_BT_LOG_HID_TRACE_LEVEL
|
||||
#ifdef CONFIG_BT_LOG_HIDH_TRACE_LEVEL
|
||||
#define UC_BT_LOG_HIDH_TRACE_LEVEL CONFIG_BT_LOG_HIDH_TRACE_LEVEL
|
||||
#else
|
||||
#define UC_BT_LOG_HID_TRACE_LEVEL UC_TRACE_LEVEL_WARNING
|
||||
#define UC_BT_LOG_HIDH_TRACE_LEVEL UC_TRACE_LEVEL_WARNING
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_LOG_APPL_TRACE_LEVEL
|
||||
|
@ -127,6 +127,11 @@
|
||||
#define BT_SSP_INCLUDED TRUE
|
||||
#endif /* UC_BT_SSP_ENABLED */
|
||||
|
||||
#if UC_BT_HID_HOST_ENABLED
|
||||
#define HID_HOST_INCLUDED TRUE
|
||||
#define BTA_HH_INCLUDED TRUE
|
||||
#endif /* UC_BT_HID_HOST_ENABLED */
|
||||
|
||||
#endif /* UC_BT_CLASSIC_ENABLED */
|
||||
|
||||
#ifndef CLASSIC_BT_INCLUDED
|
||||
|
@ -198,7 +198,7 @@ static inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t l
|
||||
#define AVCT_INITIAL_TRACE_LEVEL UC_BT_LOG_AVCT_TRACE_LEVEL
|
||||
#define AVRC_INITIAL_TRACE_LEVEL UC_BT_LOG_AVRC_TRACE_LEVEL
|
||||
#define MCA_INITIAL_TRACE_LEVEL UC_BT_LOG_MCA_TRACE_LEVEL
|
||||
#define HID_INITIAL_TRACE_LEVEL UC_BT_LOG_HID_TRACE_LEVEL
|
||||
#define HIDH_INITIAL_TRACE_LEVEL UC_BT_LOG_HIDH_TRACE_LEVEL
|
||||
#define APPL_INITIAL_TRACE_LEVEL UC_BT_LOG_APPL_TRACE_LEVEL
|
||||
#define GATT_INITIAL_TRACE_LEVEL UC_BT_LOG_GATT_TRACE_LEVEL
|
||||
#define SMP_INITIAL_TRACE_LEVEL UC_BT_LOG_SMP_TRACE_LEVEL
|
||||
|
20
components/esp_hid/CMakeLists.txt
Normal file
20
components/esp_hid/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
set(srcs "src/esp_hidd.c"
|
||||
"src/esp_hidh.c"
|
||||
"src/esp_hid_common.c")
|
||||
|
||||
set(include_dirs "include")
|
||||
set(priv_include_dirs "private")
|
||||
|
||||
if(CONFIG_BT_ENABLED)
|
||||
if(CONFIG_BT_BLUEDROID_ENABLED)
|
||||
list(APPEND srcs
|
||||
"src/ble_hidd.c"
|
||||
"src/ble_hidh.c"
|
||||
"src/bt_hidh.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "${include_dirs}"
|
||||
PRIV_INCLUDE_DIRS "${priv_include_dirs}"
|
||||
REQUIRES esp_event bt)
|
3
components/esp_hid/component.mk
Normal file
3
components/esp_hid/component.mk
Normal file
@ -0,0 +1,3 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
COMPONENT_PRIV_INCLUDEDIRS := private
|
||||
COMPONENT_SRCDIRS := src
|
257
components/esp_hid/include/esp_hid_common.h
Normal file
257
components/esp_hid/include/esp_hid_common.h
Normal file
@ -0,0 +1,257 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/* HID Report Map Values */
|
||||
#define HID_RM_INPUT 0x80
|
||||
#define HID_RM_OUTPUT 0x90
|
||||
#define HID_RM_FEATURE 0xb0
|
||||
#define HID_RM_COLLECTION 0xa0
|
||||
#define HID_RM_END_COLLECTION 0xc0
|
||||
#define HID_RM_USAGE_PAGE 0x04
|
||||
#define HID_RM_LOGICAL_MINIMUM 0x14
|
||||
#define HID_RM_LOGICAL_MAXIMUM 0x24
|
||||
#define HID_RM_PHYSICAL_MINIMUM 0x34
|
||||
#define HID_RM_PHYSICAL_MAXIMUM 0x44
|
||||
#define HID_RM_UNIT_EXPONENT 0x54
|
||||
#define HID_RM_UNIT 0x64
|
||||
#define HID_RM_REPORT_SIZE 0x74
|
||||
#define HID_RM_REPORT_ID 0x84
|
||||
#define HID_RM_REPORT_COUNT 0x94
|
||||
#define HID_RM_PUSH 0xa4
|
||||
#define HID_RM_POP 0xb4
|
||||
#define HID_RM_USAGE 0x08
|
||||
#define HID_RM_USAGE_MINIMUM 0x18
|
||||
#define HID_RM_USAGE_MAXIMUM 0x28
|
||||
#define HID_RM_DESIGNATOR_INDEX 0x38
|
||||
#define HID_RM_DESIGNATOR_MINIMUM 0x48
|
||||
#define HID_RM_DESIGNATOR_MAXIMUM 0x58
|
||||
#define HID_RM_STRING_INDEX 0x78
|
||||
#define HID_RM_STRING_MINIMUM 0x88
|
||||
#define HID_RM_STRING_MAXIMUM 0x98
|
||||
#define HID_RM_DELIMITER 0xa8
|
||||
|
||||
/* HID Usage Pages and Usages */
|
||||
#define HID_USAGE_PAGE_GENERIC_DESKTOP 0x01
|
||||
#define HID_USAGE_KEYBOARD 0x06
|
||||
#define HID_USAGE_MOUSE 0x02
|
||||
#define HID_USAGE_JOYSTICK 0x04
|
||||
#define HID_USAGE_GAMEPAD 0x05
|
||||
|
||||
#define HID_USAGE_PAGE_CONSUMER_DEVICE 0x0C
|
||||
#define HID_USAGE_CONSUMER_CONTROL 0x01
|
||||
|
||||
/* HID BT COD Peripheral Min Values Main Role */
|
||||
#define ESP_HID_COD_MIN_KEYBOARD 0x10
|
||||
#define ESP_HID_COD_MIN_MOUSE 0x20
|
||||
|
||||
/* HID BLE Appearances */
|
||||
#define ESP_HID_APPEARANCE_GENERIC 0x03C0
|
||||
#define ESP_HID_APPEARANCE_KEYBOARD 0x03C1
|
||||
#define ESP_HID_APPEARANCE_MOUSE 0x03C2
|
||||
#define ESP_HID_APPEARANCE_JOYSTICK 0x03C3
|
||||
#define ESP_HID_APPEARANCE_GAMEPAD 0x03C4
|
||||
|
||||
/* HID Report Types */
|
||||
#define ESP_HID_REPORT_TYPE_INPUT 1
|
||||
#define ESP_HID_REPORT_TYPE_OUTPUT 2
|
||||
#define ESP_HID_REPORT_TYPE_FEATURE 3
|
||||
|
||||
/* HID Protocol Modes */
|
||||
#define ESP_HID_PROTOCOL_MODE_BOOT 0x00 // Boot Protocol Mode
|
||||
#define ESP_HID_PROTOCOL_MODE_REPORT 0x01 // Report Protocol Mode
|
||||
|
||||
/* HID information flags */
|
||||
#define ESP_HID_FLAGS_REMOTE_WAKE 0x01 // RemoteWake
|
||||
#define ESP_HID_FLAGS_NORMALLY_CONNECTABLE 0x02 // NormallyConnectable
|
||||
|
||||
/* Control point commands */
|
||||
#define ESP_HID_CONTROL_SUSPEND 0x00 // Suspend
|
||||
#define ESP_HID_CONTROL_EXIT_SUSPEND 0x01 // Exit Suspend
|
||||
|
||||
/* Client Characteristic Configuration values */
|
||||
#define ESP_HID_CCC_NOTIFICATIONS_ENABLED 0x01 // Notifications enabled
|
||||
#define ESP_HID_CCC_INDICATIONS_ENABLED 0x02 // Indications enabled
|
||||
|
||||
/* HID Transports */
|
||||
typedef enum {
|
||||
ESP_HID_TRANSPORT_BT,
|
||||
ESP_HID_TRANSPORT_BLE,
|
||||
ESP_HID_TRANSPORT_USB,
|
||||
ESP_HID_TRANSPORT_MAX
|
||||
} esp_hid_transport_t;
|
||||
|
||||
/* HID Usage Types */
|
||||
typedef enum {
|
||||
ESP_HID_USAGE_GENERIC = 0,
|
||||
ESP_HID_USAGE_KEYBOARD = 1,
|
||||
ESP_HID_USAGE_MOUSE = 2,
|
||||
ESP_HID_USAGE_JOYSTICK = 4,
|
||||
ESP_HID_USAGE_GAMEPAD = 8,
|
||||
ESP_HID_USAGE_TABLET = 16,
|
||||
ESP_HID_USAGE_CCONTROL = 32,
|
||||
ESP_HID_USAGE_VENDOR = 64
|
||||
} esp_hid_usage_t;
|
||||
|
||||
/* HID BT COD Peripheral Min Values. Mask of (keyboard|mouse|ESP_HIDH_COD_*) */
|
||||
typedef enum {
|
||||
ESP_HID_COD_MIN_GENERIC,
|
||||
ESP_HID_COD_MIN_JOYSTICK,
|
||||
ESP_HID_COD_MIN_GAMEPAD,
|
||||
ESP_HID_COD_MIN_REMOTE,
|
||||
ESP_HID_COD_MIN_SENSOR,
|
||||
ESP_HID_COD_MIN_TABLET,
|
||||
ESP_HID_COD_MIN_CARD_READER,
|
||||
ESP_HID_COD_MIN_MAX
|
||||
} esp_hid_cod_min_t;
|
||||
|
||||
/**
|
||||
* @brief HID report item structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t map_index; /*!< HID report map index */
|
||||
uint8_t report_id; /*!< HID report id */
|
||||
uint8_t report_type; /*!< HID report type */
|
||||
uint8_t protocol_mode; /*!< HID protocol mode */
|
||||
esp_hid_usage_t usage; /*!< HID usage type */
|
||||
uint16_t value_len; /*!< HID report length in bytes */
|
||||
} esp_hid_report_item_t;
|
||||
|
||||
/**
|
||||
* @brief HID parsed report map structure
|
||||
*/
|
||||
typedef struct {
|
||||
esp_hid_usage_t usage; /*!< Dominant HID usage. (keyboard > mouse > joystick > gamepad > generic) */
|
||||
uint16_t appearance; /*!< Calculated HID Appearance based on the dominant usage */
|
||||
uint8_t reports_len; /*!< Number of reports discovered in the report map */
|
||||
esp_hid_report_item_t *reports; /*!< Reports discovered in the report map */
|
||||
} esp_hid_report_map_t;
|
||||
|
||||
/**
|
||||
* @brief HID raw report map structure
|
||||
*/
|
||||
typedef struct {
|
||||
const uint8_t *data; /*!< Pointer to the HID report map data */
|
||||
uint16_t len; /*!< HID report map data length */
|
||||
} esp_hid_raw_report_map_t;
|
||||
|
||||
/**
|
||||
* @brief HID device config structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t vendor_id; /*!< HID Vendor ID */
|
||||
uint16_t product_id; /*!< HID Product ID */
|
||||
uint16_t version; /*!< HID Product Version */
|
||||
const char *device_name; /*!< HID Device Name */
|
||||
const char *manufacturer_name; /*!< HID Manufacturer */
|
||||
const char *serial_number; /*!< HID Serial Number */
|
||||
esp_hid_raw_report_map_t *report_maps; /*!< Array of the raw HID report maps */
|
||||
uint8_t report_maps_len; /*!< number of raw report maps in the array */
|
||||
} esp_hid_device_config_t;
|
||||
|
||||
/*
|
||||
* @brief Parse RAW HID report map
|
||||
* It is a responsibility of the user to free the parsed report map,
|
||||
* when it's no longer needed. Use esp_hid_free_report_map
|
||||
* @param hid_rm : pointer to the hid report map data
|
||||
* @param hid_rm_len : length to the hid report map data
|
||||
*
|
||||
* @return: pointer to the parsed report map
|
||||
*/
|
||||
esp_hid_report_map_t *esp_hid_parse_report_map(const uint8_t *hid_rm, size_t hid_rm_len);
|
||||
|
||||
/*
|
||||
* @brief Free parsed HID report map
|
||||
* @param map : pointer to the parsed hid report map
|
||||
*/
|
||||
void esp_hid_free_report_map(esp_hid_report_map_t *map);
|
||||
|
||||
/**
|
||||
* @brief Calculate the HID Device usage type from the BLE Apperance
|
||||
* @param appearance : BLE Apperance value
|
||||
*
|
||||
* @return: the hid usage type
|
||||
*/
|
||||
esp_hid_usage_t esp_hid_usage_from_appearance(uint16_t appearance);
|
||||
|
||||
/**
|
||||
* @brief Calculate the HID Device usage type from the BT CoD
|
||||
* @param cod : BT CoD value
|
||||
*
|
||||
* @return: the hid usage type
|
||||
*/
|
||||
esp_hid_usage_t esp_hid_usage_from_cod(uint32_t cod);
|
||||
|
||||
/**
|
||||
* @brief Convert device usage type to string
|
||||
* @param usage : The HID usage type to convert
|
||||
*
|
||||
* @return: a pointer to the string or NULL
|
||||
*/
|
||||
const char *esp_hid_usage_str(esp_hid_usage_t usage);
|
||||
|
||||
/**
|
||||
* @brief Convert HID protocol mode to string
|
||||
* @param protocol_mode : The HID protocol mode to convert
|
||||
* BOOT/REPORT
|
||||
*
|
||||
* @return: a pointer to the string or NULL
|
||||
*/
|
||||
const char *esp_hid_protocol_mode_str(uint8_t protocol_mode);
|
||||
|
||||
/**
|
||||
* @brief Convert HID report type to string
|
||||
* @param report_type : The HID report type to convert
|
||||
* INPUT/OUTPUT/FEATURE
|
||||
*
|
||||
* @return: a pointer to the string or NULL
|
||||
*/
|
||||
const char *esp_hid_report_type_str(uint8_t report_type);
|
||||
|
||||
/**
|
||||
* @brief Convert BT CoD major to string
|
||||
* @param cod_major : The CoD major value to convert
|
||||
*
|
||||
* @return: a pointer to the string or NULL
|
||||
*/
|
||||
const char *esp_hid_cod_major_str(uint8_t cod_major);
|
||||
|
||||
/**
|
||||
* @brief Print BT CoD minor value
|
||||
* @param cod_min : The CoD minor value to print
|
||||
* @param fp : pointer to the output file
|
||||
*/
|
||||
void esp_hid_cod_minor_print(uint8_t cod_min, FILE *fp);
|
||||
|
||||
/**
|
||||
* @brief Convert BLE disconnect reason to string
|
||||
* @param reason : The value of the reason
|
||||
*
|
||||
* @return: a pointer to the string or NULL
|
||||
*/
|
||||
const char *esp_hid_disconnect_reason_str(esp_hid_transport_t transport, int reason);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
206
components/esp_hid/include/esp_hidd.h
Normal file
206
components/esp_hid/include/esp_hidd.h
Normal file
@ -0,0 +1,206 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_hid_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_hidd_transport.h"
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(ESP_HIDD_EVENTS); // Declare the event base for HID device
|
||||
|
||||
/**
|
||||
* @brief HIDD callback events enum
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_HIDD_ANY_EVENT = ESP_EVENT_ANY_ID, /*!< HID device any event */
|
||||
ESP_HIDD_START_EVENT = 0, /*!< HID device stack started */
|
||||
ESP_HIDD_CONNECT_EVENT, /*!< HID device connected */
|
||||
ESP_HIDD_PROTOCOL_MODE_EVENT, /*!< HID device protocol mode change */
|
||||
ESP_HIDD_CONTROL_EVENT, /*!< HID device control request */
|
||||
ESP_HIDD_OUTPUT_EVENT, /*!< HID device output report event */
|
||||
ESP_HIDD_FEATURE_EVENT, /*!< HID device feature report event */
|
||||
ESP_HIDD_DISCONNECT_EVENT, /*!< HID device disconnected */
|
||||
ESP_HIDD_STOP_EVENT, /*!< HID device stack stopped */
|
||||
ESP_HIDD_MAX_EVENT, /*!< HID events end marker */
|
||||
} esp_hidd_event_t;
|
||||
|
||||
/**
|
||||
* @brief HIDD structure forward declaration
|
||||
*/
|
||||
struct esp_hidd_dev_s;
|
||||
typedef struct esp_hidd_dev_s esp_hidd_dev_t;
|
||||
|
||||
/**
|
||||
* @brief HIDD callback parameters union
|
||||
*/
|
||||
typedef union {
|
||||
/**
|
||||
* @brief ESP_HIDD_CONNECT_EVENT
|
||||
*/
|
||||
struct {
|
||||
esp_hidd_dev_t *dev; /*!< HID device structure */
|
||||
} connect; /*!< HID callback param of ESP_HIDD_CONNECT_EVENT */
|
||||
|
||||
/**
|
||||
* @brief ESP_HIDD_DISCONNECT_EVENT
|
||||
*/
|
||||
struct {
|
||||
esp_hidd_dev_t *dev; /*!< HID device structure */
|
||||
int reason; /*!< Indicate the reason of disconnection */
|
||||
} disconnect; /*!< HID callback param of ESP_HIDD_DISCONNECT_EVENT */
|
||||
|
||||
/**
|
||||
* @brief ESP_HIDD_OUTPUT_EVENT
|
||||
*/
|
||||
struct {
|
||||
esp_hidd_dev_t *dev; /*!< HID device structure */
|
||||
esp_hid_usage_t usage; /*!< HID report usage */
|
||||
uint16_t report_id; /*!< HID report index */
|
||||
uint16_t length; /*!< data length */
|
||||
uint8_t *data; /*!< The pointer to the data */
|
||||
uint8_t map_index; /*!< HID config report map index */
|
||||
} output; /*!< HID callback param of ESP_HIDD_OUTPUT_EVENT */
|
||||
|
||||
/**
|
||||
* @brief ESP_HIDD_FEATURE_EVENT
|
||||
*/
|
||||
struct {
|
||||
esp_hidd_dev_t *dev; /*!< HID device structure */
|
||||
esp_hid_usage_t usage; /*!< HID report usage */
|
||||
uint16_t report_id; /*!< HID report index */
|
||||
uint16_t length; /*!< data length */
|
||||
uint8_t *data; /*!< The pointer to the data */
|
||||
uint8_t map_index; /*!< HID config report map index */
|
||||
} feature; /*!< HID callback param of ESP_HIDD_FEATURE_EVENT */
|
||||
|
||||
/**
|
||||
* @brief ESP_HIDD_PROTOCOL_MODE_EVENT
|
||||
*/
|
||||
struct {
|
||||
esp_hidd_dev_t *dev; /*!< HID device structure */
|
||||
uint8_t protocol_mode; /*!< HID Protocol Mode */
|
||||
uint8_t map_index; /*!< HID config report map index */
|
||||
} protocol_mode; /*!< HID callback param of ESP_HIDD_PROTOCOL_MODE_EVENT */
|
||||
|
||||
/**
|
||||
* @brief ESP_HIDD_CONTROL_EVENT
|
||||
*/
|
||||
struct {
|
||||
esp_hidd_dev_t *dev; /*!< HID device structure */
|
||||
uint8_t control; /*!< HID Control Point */
|
||||
uint8_t map_index; /*!< HID config report map index */
|
||||
} control; /*!< HID callback param of ESP_HIDD_CONTROL_EVENT */
|
||||
|
||||
} esp_hidd_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Init HID Device
|
||||
* @param config : configuration for the device
|
||||
* @param transport: protocol that the device will use (ESP_HID_TRANSPORT_BLE/BT/USB)
|
||||
* @param callback : function to call when events for this device are generated.
|
||||
* Can be NULL, but will miss the START event.
|
||||
* @param[out] dev : location to return the pointer to the device structure
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidd_dev_init(const esp_hid_device_config_t *config, esp_hid_transport_t transport, esp_event_handler_t callback, esp_hidd_dev_t **dev);
|
||||
|
||||
/**
|
||||
* @brief Deinit HID Device
|
||||
* @param dev : pointer to the device to deinit
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidd_dev_deinit(esp_hidd_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Get the HID Device Transport
|
||||
* @param dev : pointer to the HID Device
|
||||
*
|
||||
* @return: the transport of the connected device or ESP_HID_TRANSPORT_MAX
|
||||
*/
|
||||
esp_hid_transport_t esp_hidd_dev_transport_get(esp_hidd_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Check if HID Device is connected
|
||||
* @param dev : pointer to the device
|
||||
*
|
||||
* @return: true if the device is connected
|
||||
*/
|
||||
bool esp_hidd_dev_connected(esp_hidd_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Set the battery level reported by the HID Device
|
||||
* @param dev : pointer to the device
|
||||
* @param level : battery level (0-100)
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidd_dev_battery_set(esp_hidd_dev_t *dev, uint8_t level);
|
||||
|
||||
/**
|
||||
* @brief Send an INPUT report to the host
|
||||
* @param dev : pointer to the device
|
||||
* @param map_index : index of the device report map in the init config
|
||||
* @param report_id : id of the HID INPUT report
|
||||
* @param data : pointer to the data to send
|
||||
* @param length : length of the data to send
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidd_dev_input_set(esp_hidd_dev_t *dev, size_t map_index, size_t report_id, uint8_t *data, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Send a FEATURE report to the host
|
||||
* @param dev : pointer to the device
|
||||
* @param map_index : index of the device report map in the init config
|
||||
* @param report_id : id of the HID FEATURE report
|
||||
* @param data : pointer to the data to send
|
||||
* @param length : length of the data to send
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidd_dev_feature_set(esp_hidd_dev_t *dev, size_t map_index, size_t report_id, uint8_t *data, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Register function to listen for device events
|
||||
* @param dev : pointer to the device
|
||||
* @param callback : event handler function
|
||||
* @param event : event to listen for (ESP_HIDD_ANY_EVENT for all)
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidd_dev_event_handler_register(esp_hidd_dev_t *dev, esp_event_handler_t callback, esp_hidd_event_t event);
|
||||
|
||||
/**
|
||||
* @brief Unregister function that is listening for device events
|
||||
* @param dev : pointer to the device
|
||||
* @param callback : event handler function
|
||||
* @param event : event that is listening for (ESP_HIDD_ANY_EVENT for all)
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidd_dev_event_handler_unregister(esp_hidd_dev_t *dev, esp_event_handler_t callback, esp_hidd_event_t event);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
40
components/esp_hid/include/esp_hidd_gatts.h
Normal file
40
components/esp_hid/include/esp_hidd_gatts.h
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_GATTS_ENABLE
|
||||
|
||||
#include "esp_gatts_api.h" //for the callback
|
||||
|
||||
/**
|
||||
* @brief HID BLE GATTS System Callback. Attach it in your code
|
||||
* or call it from your gatts event handler to allow the HID stack to function
|
||||
* @param event : Event type
|
||||
* @param gatts_if : GATTS Interface ID
|
||||
* @param param : Point to callback parameter, currently is union type
|
||||
*/
|
||||
void esp_hidd_gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
|
||||
|
||||
#endif /* CONFIG_GATTS_ENABLE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
31
components/esp_hid/include/esp_hidd_transport.h
Normal file
31
components/esp_hid/include/esp_hidd_transport.h
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_GATTS_ENABLE
|
||||
#include "esp_hidd_gatts.h"
|
||||
#else
|
||||
typedef int esp_gatt_conn_reason_t;
|
||||
#endif /* CONFIG_GATTS_ENABLE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
294
components/esp_hid/include/esp_hidh.h
Normal file
294
components/esp_hid/include/esp_hidh.h
Normal file
@ -0,0 +1,294 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_hid_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief HIDH structure forward declaration
|
||||
*/
|
||||
struct esp_hidh_dev_s;
|
||||
typedef struct esp_hidh_dev_s esp_hidh_dev_t;
|
||||
|
||||
ESP_EVENT_DECLARE_BASE(ESP_HIDH_EVENTS);
|
||||
|
||||
/**
|
||||
* @brief HIDH callback events enum
|
||||
*/
|
||||
typedef enum {
|
||||
ESP_HIDH_ANY_EVENT = ESP_EVENT_ANY_ID, /*!< HID device any event */
|
||||
ESP_HIDH_OPEN_EVENT = 0, /*!< HID device opened */
|
||||
ESP_HIDH_BATTERY_EVENT, /*!< HID device battery level changed */
|
||||
ESP_HIDH_INPUT_EVENT, /*!< Received HID device INPUT report */
|
||||
ESP_HIDH_FEATURE_EVENT, /*!< Received HID device FEATURE report */
|
||||
ESP_HIDH_CLOSE_EVENT, /*!< HID device closed */
|
||||
ESP_HIDH_MAX_EVENT, /*!< HID events end marker */
|
||||
} esp_hidh_event_t;
|
||||
|
||||
/**
|
||||
* @brief HIDH callback parameters union
|
||||
*/
|
||||
typedef union {
|
||||
/**
|
||||
* @brief ESP_HIDH_OPEN_EVENT
|
||||
*/
|
||||
struct {
|
||||
esp_hidh_dev_t *dev; /*!< HID Remote bluetooth device */
|
||||
} open; /*!< HID callback param of ESP_HIDH_OPEN_EVENT */
|
||||
|
||||
/**
|
||||
* @brief ESP_HIDH_CLOSE_EVENT
|
||||
*/
|
||||
struct {
|
||||
esp_hidh_dev_t *dev; /*!< HID Remote bluetooth device. */
|
||||
int reason; /*!< Reason why the connection was closed. BLE Only */
|
||||
} close; /*!< HID callback param of ESP_HIDH_CLOSE_EVENT */
|
||||
|
||||
/**
|
||||
* @brief ESP_HIDH_BATTERY_EVENT
|
||||
*/
|
||||
struct {
|
||||
esp_hidh_dev_t *dev; /*!< HID Remote bluetooth device */
|
||||
uint8_t level; /*!< Battery Level (0-100%) */
|
||||
} battery; /*!< HID callback param of ESP_HIDH_BATTERY_EVENT */
|
||||
|
||||
/**
|
||||
* @brief ESP_HIDH_INPUT_EVENT
|
||||
*/
|
||||
struct {
|
||||
esp_hidh_dev_t *dev; /*!< HID Remote bluetooth device */
|
||||
esp_hid_usage_t usage; /*!< HID report usage */
|
||||
uint16_t report_id; /*!< HID report index */
|
||||
uint16_t length; /*!< HID data length */
|
||||
uint8_t *data; /*!< The pointer to the HID data */
|
||||
uint8_t map_index; /*!< HID report map index */
|
||||
} input; /*!< HID callback param of ESP_HIDH_INPUT_EVENT */
|
||||
|
||||
/**
|
||||
* @brief ESP_HIDH_FEATURE_EVENT
|
||||
*/
|
||||
struct {
|
||||
esp_hidh_dev_t *dev; /*!< HID Remote bluetooth device */
|
||||
esp_hid_usage_t usage; /*!< HID report usage */
|
||||
uint16_t report_id; /*!< HID report index */
|
||||
uint16_t length; /*!< HID data length */
|
||||
uint8_t *data; /*!< The pointer to the HID data */
|
||||
uint8_t map_index; /*!< HID report map index */
|
||||
} feature; /*!< HID callback param of ESP_HIDH_FEATURE_EVENT */
|
||||
|
||||
} esp_hidh_event_data_t;
|
||||
|
||||
typedef struct {
|
||||
esp_event_handler_t callback;
|
||||
} esp_hidh_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the HID Host component
|
||||
* @param config : pointer to esp_hidh_config_t structure
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidh_init(const esp_hidh_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief De-initialize the HID Host component
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidh_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Close HID Device
|
||||
* @param dev : pointer to the device
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidh_dev_close(esp_hidh_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Free HID Device Memory
|
||||
* This function MUST be called when handling ESP_HIDH_CLOSE_EVENT
|
||||
* Only then all memory used for the device will be freed.
|
||||
* @param dev : pointer to the device
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidh_dev_free(esp_hidh_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Send an OUTPUT report to the device
|
||||
* @param dev : pointer to the device
|
||||
* @param map_index : index of the device report map
|
||||
* @param report_id : id of the HID OUTPUT report
|
||||
* @param data : pointer to the data to send
|
||||
* @param length : length of the data to send
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidh_dev_output_set(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, uint8_t *data, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Send a FEATURE report to the device
|
||||
* @param dev : pointer to the device
|
||||
* @param map_index : index of the device report map
|
||||
* @param report_id : id of the HID FEATURE report
|
||||
* @param data : pointer to the data to send
|
||||
* @param length : length of the data to send
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidh_dev_feature_set(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, uint8_t *data, size_t length);
|
||||
|
||||
/**
|
||||
* @brief Get the value a FEATURE report from the device
|
||||
* @param dev : pointer to the device
|
||||
* @param map_index : index of the device report map
|
||||
* @param report_id : id of the HID FEATURE report
|
||||
* @param max_len : size of the buffer that will hold the data
|
||||
* @param data : pointer to the data buffer
|
||||
* @param length : pointer to the value that will be set to the number of bytes received
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidh_dev_feature_get(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, size_t max_len, uint8_t *data, size_t *length);
|
||||
|
||||
/**
|
||||
* @brief Dump the properties of HID Device to UART
|
||||
* @param dev : pointer to the HID Device
|
||||
* @param fp : pointer to the output file
|
||||
*/
|
||||
void esp_hidh_dev_dump(esp_hidh_dev_t *dev, FILE *fp);
|
||||
|
||||
/**
|
||||
* @brief Get the BT Device Address of a HID Device
|
||||
* @param dev : pointer to the HID Device
|
||||
*
|
||||
* @return: pointer to the BDA byte array or NULL
|
||||
*/
|
||||
const uint8_t *esp_hidh_dev_bda_get(esp_hidh_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Get the HID Device Transport
|
||||
* @param dev : pointer to the HID Device
|
||||
*
|
||||
* @return: the transport of the connected device or ESP_HID_TRANSPORT_MAX
|
||||
*/
|
||||
esp_hid_transport_t esp_hidh_dev_transport_get(esp_hidh_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Get the HID Device Cofiguration
|
||||
* @param dev : pointer to the HID Device
|
||||
*
|
||||
* @return: pointer to the config structure or NULL
|
||||
*/
|
||||
const esp_hid_device_config_t *esp_hidh_dev_config_get(esp_hidh_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Get the name of a HID Device
|
||||
* @param dev : pointer to the HID Device
|
||||
*
|
||||
* @return: pointer to the character array or NULL
|
||||
*/
|
||||
const char *esp_hidh_dev_name_get(esp_hidh_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Get the manufacturer of a HID Device
|
||||
* @param dev : pointer to the HID Device
|
||||
*
|
||||
* @return: pointer to the character array
|
||||
*/
|
||||
const char *esp_hidh_dev_manufacturer_get(esp_hidh_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Get the serial number of a HID Device
|
||||
* @param dev : pointer to the HID Device
|
||||
*
|
||||
* @return: pointer to the character array or NULL
|
||||
*/
|
||||
const char *esp_hidh_dev_serial_get(esp_hidh_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Get the VID of a HID Device
|
||||
* @param dev : pointer to the HID Device
|
||||
*
|
||||
* @return: the VID value
|
||||
*/
|
||||
uint16_t esp_hidh_dev_vendor_id_get(esp_hidh_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Get the PID of a HID Device
|
||||
* @param dev : pointer to the HID Device
|
||||
*
|
||||
* @return: the PID value
|
||||
*/
|
||||
uint16_t esp_hidh_dev_product_id_get(esp_hidh_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Get the version HID Device
|
||||
* @param dev : pointer to the HID Device
|
||||
*
|
||||
* @return: the version value
|
||||
*/
|
||||
uint16_t esp_hidh_dev_version_get(esp_hidh_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Get the appearance of BLE HID Device
|
||||
* @param dev : pointer to the BLE HID Device
|
||||
*
|
||||
* @return: the appearance value
|
||||
*/
|
||||
uint16_t esp_hidh_dev_appearance_get(esp_hidh_dev_t *dev); //BLE Only
|
||||
|
||||
/**
|
||||
* @brief Get the calculated HID Device usage type
|
||||
* @param dev : pointer to the HID Device
|
||||
*
|
||||
* @return: the hid usage type
|
||||
*/
|
||||
esp_hid_usage_t esp_hidh_dev_usage_get(esp_hidh_dev_t *dev);
|
||||
|
||||
/**
|
||||
* @brief Get an array of all reports found on a device
|
||||
* @param dev : pointer to the device
|
||||
* @param num_reports : pointer to the value that will be set to the number of reports
|
||||
* @param reports : location to set to the pointer of the reports array
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidh_dev_reports_get(esp_hidh_dev_t *dev, size_t *num_reports, esp_hid_report_item_t **reports);
|
||||
|
||||
/**
|
||||
* @brief Get an array of the report maps found on a device
|
||||
* @param dev : pointer to the device
|
||||
* @param num_maps : pointer to the value that will be set to the number of report maps found
|
||||
* @param maps : location to set to the pointer of the report maps array
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_hidh_dev_report_maps_get(esp_hidh_dev_t *dev, size_t *num_maps, esp_hid_raw_report_map_t **maps);
|
||||
|
||||
#include "esp_hidh_transport.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
41
components/esp_hid/include/esp_hidh_bluedroid.h
Normal file
41
components/esp_hid/include/esp_hidh_bluedroid.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_BLUEDROID_ENABLED
|
||||
|
||||
#include "esp_bt_defs.h"
|
||||
|
||||
/**
|
||||
* @brief Open BlueTooth HID Device using BlueDroid
|
||||
* @param bda : BT Device Address
|
||||
* @param transport : BT Device Protocol (Classic/HID)
|
||||
* @param remote_addr_type : BLE Remote address type
|
||||
*
|
||||
* @return: ESP_OK on success
|
||||
*/
|
||||
esp_hidh_dev_t *esp_hidh_dev_open(esp_bd_addr_t bda, esp_hid_transport_t transport, uint8_t remote_addr_type);
|
||||
|
||||
#endif /* CONFIG_BLUEDROID_ENABLED */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
40
components/esp_hid/include/esp_hidh_gattc.h
Normal file
40
components/esp_hid/include/esp_hidh_gattc.h
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_GATTC_ENABLE
|
||||
|
||||
#include "esp_gattc_api.h" //for the callback
|
||||
|
||||
/**
|
||||
* @brief HID BLE GATTC System Callback. Attach it in your code
|
||||
* or call it from your gattc event handler to allow the HID stack to function
|
||||
* @param event : Event type
|
||||
* @param gattc_if : GATTC Interface ID
|
||||
* @param param : Point to callback parameter, currently is union type
|
||||
*/
|
||||
void esp_hidh_gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
||||
|
||||
#endif /* CONFIG_GATTC_ENABLE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
33
components/esp_hid/include/esp_hidh_transport.h
Normal file
33
components/esp_hid/include/esp_hidh_transport.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_GATTC_ENABLE
|
||||
#include "esp_hidh_gattc.h"
|
||||
#endif
|
||||
|
||||
#if CONFIG_BLUEDROID_ENABLED
|
||||
#include "esp_hidh_bluedroid.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
33
components/esp_hid/private/ble_hidd.h
Normal file
33
components/esp_hid/private/ble_hidd.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_hidd.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_hid_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATTS_ENABLE
|
||||
|
||||
esp_err_t esp_ble_hidd_dev_init(esp_hidd_dev_t *dev, const esp_hid_device_config_t *config, esp_event_handler_t callback);
|
||||
|
||||
#endif /* CONFIG_GATTS_ENABLE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
34
components/esp_hid/private/ble_hidh.h
Normal file
34
components/esp_hid/private/ble_hidh.h
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_hidh.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATTC_ENABLE
|
||||
|
||||
esp_err_t esp_ble_hidh_init(const esp_hidh_config_t *config);
|
||||
esp_err_t esp_ble_hidh_deinit(void);
|
||||
|
||||
esp_hidh_dev_t *esp_ble_hidh_dev_open(esp_bd_addr_t bda, esp_ble_addr_type_t address_type);
|
||||
|
||||
#endif /* CONFIG_GATTC_ENABLE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
34
components/esp_hid/private/bt_hidh.h
Normal file
34
components/esp_hid/private/bt_hidh.h
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_hidh.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
|
||||
esp_err_t esp_bt_hidh_init(const esp_hidh_config_t *config);
|
||||
esp_err_t esp_bt_hidh_deinit(void);
|
||||
|
||||
esp_hidh_dev_t *esp_bt_hidh_dev_open(esp_bd_addr_t bda);
|
||||
|
||||
#endif /* CONFIG_BT_HID_HOST_ENABLED */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
367
components/esp_hid/private/esp_bt_hh_api.h
Normal file
367
components/esp_hid/private/esp_bt_hh_api.h
Normal file
@ -0,0 +1,367 @@
|
||||
/******************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005-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.
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _ESP_BT_HH_API_H_
|
||||
#define _ESP_BT_HH_API_H_
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_bt_defs.h"
|
||||
|
||||
/*****************************************************************************
|
||||
** Constants and Type Definitions
|
||||
*****************************************************************************/
|
||||
#ifndef BTA_HH_DEBUG
|
||||
#define BTA_HH_DEBUG TRUE
|
||||
#endif
|
||||
|
||||
#ifndef BTA_HH_SSR_MAX_LATENCY_DEF
|
||||
#define BTA_HH_SSR_MAX_LATENCY_DEF 800 /* 500 ms*/
|
||||
#endif
|
||||
|
||||
#ifndef BTA_HH_SSR_MIN_TOUT_DEF
|
||||
#define BTA_HH_SSR_MIN_TOUT_DEF 2
|
||||
#endif
|
||||
|
||||
/* defined the minimum offset */
|
||||
#define BTA_HH_MIN_OFFSET L2CAP_MIN_OFFSET+1
|
||||
|
||||
/* HID_HOST_MAX_DEVICES can not exceed 15 for th design of BTA HH */
|
||||
#define BTA_HH_IDX_INVALID 0xff
|
||||
#define BTA_HH_MAX_KNOWN HID_HOST_MAX_DEVICES
|
||||
|
||||
/* Security Service Levels [bit mask] (BTM_SetSecurityLevel)
|
||||
** Encryption should not be used without authentication
|
||||
*/
|
||||
#define BTM_SEC_NONE 0x0000 /* Nothing required */
|
||||
#define BTM_SEC_IN_AUTHORIZE 0x0001 /* Inbound call requires authorization */
|
||||
#define BTM_SEC_IN_AUTHENTICATE 0x0002 /* Inbound call requires authentication */
|
||||
#define BTM_SEC_IN_ENCRYPT 0x0004 /* Inbound call requires encryption */
|
||||
#define BTM_SEC_OUT_AUTHORIZE 0x0008 /* Outbound call requires authorization */
|
||||
#define BTM_SEC_OUT_AUTHENTICATE 0x0010 /* Outbound call requires authentication */
|
||||
#define BTM_SEC_OUT_ENCRYPT 0x0020 /* Outbound call requires encryption */
|
||||
#define BTM_SEC_MODE4_LEVEL4 0x0040 /* Secure Connections Only Mode */
|
||||
#define BTM_SEC_FORCE_MASTER 0x0100 /* Need to switch connection to be master */
|
||||
#define BTM_SEC_ATTEMPT_MASTER 0x0200 /* Try to switch connection to be master */
|
||||
#define BTM_SEC_FORCE_SLAVE 0x0400 /* Need to switch connection to be master */
|
||||
#define BTM_SEC_ATTEMPT_SLAVE 0x0800 /* Try to switch connection to be slave */
|
||||
#define BTM_SEC_IN_MITM 0x1000 /* inbound Do man in the middle protection */
|
||||
#define BTM_SEC_OUT_MITM 0x2000 /* outbound Do man in the middle protection */
|
||||
#define BTM_SEC_IN_MIN_16_DIGIT_PIN 0x4000 /* enforce a minimum of 16 digit for sec mode 2 */
|
||||
|
||||
/* Security Setting Mask */
|
||||
#define BTA_SEC_NONE BTM_SEC_NONE /* No security. */
|
||||
#define BTA_SEC_AUTHORIZE (BTM_SEC_IN_AUTHORIZE ) /* Authorization required (only needed for out going connection )*/
|
||||
#define BTA_SEC_AUTHENTICATE (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_OUT_AUTHENTICATE) /* Authentication required. */
|
||||
#define BTA_SEC_ENCRYPT (BTM_SEC_IN_ENCRYPT | BTM_SEC_OUT_ENCRYPT) /* Encryption required. */
|
||||
#define BTA_SEC_MODE4_LEVEL4 (BTM_SEC_MODE4_LEVEL4) /* Mode 4 level 4 service, i.e. incoming/outgoing MITM and P-256 encryption */
|
||||
#define BTA_SEC_MITM (BTM_SEC_IN_MITM | BTM_SEC_OUT_MITM) /* Man-In-The_Middle protection */
|
||||
#define BTA_SEC_IN_16_DIGITS (BTM_SEC_IN_MIN_16_DIGIT_PIN) /* Min 16 digit for pin code */
|
||||
|
||||
#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE)
|
||||
/* GATT_MAX_PHY_CHANNEL can not exceed 14 for the design of BTA HH */
|
||||
#define BTA_HH_LE_MAX_KNOWN GATT_MAX_PHY_CHANNEL
|
||||
#define BTA_HH_MAX_DEVICE (HID_HOST_MAX_DEVICES + GATT_MAX_PHY_CHANNEL)
|
||||
#else
|
||||
#define BTA_HH_MAX_DEVICE HID_HOST_MAX_DEVICES
|
||||
#endif
|
||||
/* invalid device handle */
|
||||
#define BTA_HH_INVALID_HANDLE 0xff
|
||||
|
||||
#define BTA_HH_SSR_PARAM_INVALID HID_SSR_PARAM_INVALID
|
||||
|
||||
/* id DI is not existing in remote device, vendor_id in tBTA_HH_DEV_DSCP_INFO will be set to 0xffff */
|
||||
#define BTA_HH_VENDOR_ID_INVALID 0xffff
|
||||
|
||||
/* application ID(none-zero) for each type of device */
|
||||
#define BTA_HH_APP_ID_MI 1
|
||||
#define BTA_HH_APP_ID_KB 2
|
||||
#define BTA_HH_APP_ID_RMC 3
|
||||
#define BTA_HH_APP_ID_3DSG 4
|
||||
#define BTA_HH_APP_ID_JOY 5
|
||||
#define BTA_HH_APP_ID_GPAD 6
|
||||
#define BTA_HH_APP_ID_LE 0xff
|
||||
|
||||
/* type of devices, bit mask */
|
||||
#define BTA_HH_DEVT_UNKNOWN 0x00
|
||||
#define BTA_HH_DEVT_JOS 0x01 /* joy stick */
|
||||
#define BTA_HH_DEVT_GPD 0x02 /* game pad */
|
||||
#define BTA_HH_DEVT_RMC 0x03 /* remote control */
|
||||
#define BTA_HH_DEVT_SED 0x04 /* sensing device */
|
||||
#define BTA_HH_DEVT_DGT 0x05 /* Digitizer tablet */
|
||||
#define BTA_HH_DEVT_CDR 0x06 /* card reader */
|
||||
#define BTA_HH_DEVT_KBD 0x10 /* keyboard */
|
||||
#define BTA_HH_DEVT_MIC 0x20 /* pointing device */
|
||||
#define BTA_HH_DEVT_COM 0x30 /* Combo keyboard/pointing */
|
||||
#define BTA_HH_DEVT_OTHER 0x80
|
||||
typedef uint8_t tBTA_HH_DEVT;
|
||||
|
||||
|
||||
/* BTA HID Host callback events */
|
||||
#define BTA_HH_ENABLE_EVT 0 /* HH enabled */
|
||||
#define BTA_HH_DISABLE_EVT 1 /* HH disabled */
|
||||
#define BTA_HH_OPEN_EVT 2 /* connection opened */
|
||||
#define BTA_HH_CLOSE_EVT 3 /* connection closed */
|
||||
#define BTA_HH_GET_RPT_EVT 4 /* BTA_HhGetReport callback */
|
||||
#define BTA_HH_SET_RPT_EVT 5 /* BTA_HhSetReport callback */
|
||||
#define BTA_HH_GET_PROTO_EVT 6 /* BTA_GetProtoMode callback */
|
||||
#define BTA_HH_SET_PROTO_EVT 7 /* BTA_HhSetProtoMode callback */
|
||||
#define BTA_HH_GET_IDLE_EVT 8 /* BTA_HhGetIdle comes callback */
|
||||
#define BTA_HH_SET_IDLE_EVT 9 /* BTA_HhSetIdle finish callback */
|
||||
#define BTA_HH_GET_DSCP_EVT 10 /* Get report descriptor */
|
||||
#define BTA_HH_ADD_DEV_EVT 11 /* Add Device callback */
|
||||
#define BTA_HH_RMV_DEV_EVT 12 /* remove device finished */
|
||||
#define BTA_HH_VC_UNPLUG_EVT 13 /* virtually unplugged */
|
||||
#define BTA_HH_DATA_EVT 15
|
||||
#define BTA_HH_API_ERR_EVT 16 /* API error is caught */
|
||||
#define BTA_HH_UPDATE_SCPP_EVT 17 /* update scan paramter complete */
|
||||
typedef uint16_t tBTA_HH_EVT;
|
||||
|
||||
/* type of protocol mode */
|
||||
#define BTA_HH_PROTO_RPT_MODE (0x00)
|
||||
#define BTA_HH_PROTO_BOOT_MODE (0x01)
|
||||
#define BTA_HH_PROTO_UNKNOWN (0xff)
|
||||
typedef uint8_t tBTA_HH_PROTO_MODE;
|
||||
|
||||
#define BTA_HH_VIRTUAL_CABLE HID_VIRTUAL_CABLE
|
||||
#define BTA_HH_NORMALLY_CONNECTABLE HID_NORMALLY_CONNECTABLE
|
||||
#define BTA_HH_RECONN_INIT HID_RECONN_INIT
|
||||
#define BTA_HH_SDP_DISABLE HID_SDP_DISABLE
|
||||
#define BTA_HH_BATTERY_POWER HID_BATTERY_POWER
|
||||
#define BTA_HH_REMOTE_WAKE HID_REMOTE_WAKE
|
||||
#define BTA_HH_SUP_TOUT_AVLBL HID_SUP_TOUT_AVLBL
|
||||
#define BTA_HH_SEC_REQUIRED HID_SEC_REQUIRED
|
||||
typedef uint16_t tBTA_HH_ATTR_MASK;
|
||||
|
||||
enum {
|
||||
BTA_HH_OK,
|
||||
BTA_HH_HS_HID_NOT_READY, /* handshake error : device not ready */
|
||||
BTA_HH_HS_INVALID_RPT_ID, /* handshake error : invalid report ID */
|
||||
BTA_HH_HS_TRANS_NOT_SPT, /* handshake error : transaction not spt */
|
||||
BTA_HH_HS_INVALID_PARAM, /* handshake error : invalid paremter */
|
||||
BTA_HH_HS_ERROR, /* handshake error : unspecified HS error */
|
||||
BTA_HH_ERR, /* general BTA HH error */
|
||||
BTA_HH_ERR_SDP, /* SDP error */
|
||||
BTA_HH_ERR_PROTO, /* SET_Protocol error, only used in BTA_HH_OPEN_EVT callback */
|
||||
BTA_HH_ERR_DB_FULL, /* device database full error, used in BTA_HH_OPEN_EVT/BTA_HH_ADD_DEV_EVT */
|
||||
BTA_HH_ERR_TOD_UNSPT, /* type of device not supported */
|
||||
BTA_HH_ERR_NO_RES, /* out of system resources */
|
||||
BTA_HH_ERR_AUTH_FAILED, /* authentication fail */
|
||||
BTA_HH_ERR_HDL,
|
||||
BTA_HH_ERR_SEC
|
||||
};
|
||||
typedef uint8_t tBTA_HH_STATUS;
|
||||
|
||||
enum {
|
||||
BTA_HH_RPTT_RESRV, /* reserved */
|
||||
BTA_HH_RPTT_INPUT, /* input report */
|
||||
BTA_HH_RPTT_OUTPUT, /* output report */
|
||||
BTA_HH_RPTT_FEATURE /* feature report */
|
||||
};
|
||||
typedef uint8_t tBTA_HH_RPT_TYPE;
|
||||
|
||||
/* HID_CONTROL operation code used in BTA_HhSendCtrl()
|
||||
*/
|
||||
enum {
|
||||
BTA_HH_CTRL_NOP = 0, /* mapping from BTE */
|
||||
BTA_HH_CTRL_HARD_RESET, /* hard reset */
|
||||
BTA_HH_CTRL_SOFT_RESET, /* soft reset */
|
||||
BTA_HH_CTRL_SUSPEND, /* enter suspend */
|
||||
BTA_HH_CTRL_EXIT_SUSPEND, /* exit suspend */
|
||||
BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG /* virtual unplug */
|
||||
};
|
||||
typedef uint8_t tBTA_HH_TRANS_CTRL_TYPE;
|
||||
|
||||
|
||||
typedef struct desc_info {
|
||||
uint16_t dl_len;
|
||||
uint8_t *dsc_list;
|
||||
} tBTA_HH_DEV_DESCR;
|
||||
|
||||
/* Define the header of each buffer used in the Bluetooth stack. */
|
||||
typedef struct {
|
||||
uint16_t event;
|
||||
uint16_t len;
|
||||
uint16_t offset;
|
||||
uint16_t layer_specific;
|
||||
uint8_t data[];
|
||||
} BT_HDR;
|
||||
|
||||
#define BT_HDR_SIZE (sizeof (BT_HDR))
|
||||
|
||||
/* callback event data for BTA_HH_OPEN_EVT */
|
||||
typedef struct {
|
||||
esp_bd_addr_t bda; /* HID device bd address */
|
||||
tBTA_HH_STATUS status; /* operation status */
|
||||
uint8_t handle; /* device handle */
|
||||
|
||||
} tBTA_HH_CONN;
|
||||
|
||||
typedef tBTA_HH_CONN tBTA_HH_DEV_INFO;
|
||||
|
||||
/* callback event data */
|
||||
typedef struct {
|
||||
tBTA_HH_STATUS status; /* operation status */
|
||||
uint8_t handle; /* device handle */
|
||||
} tBTA_HH_CBDATA;
|
||||
|
||||
/* report descriptor information */
|
||||
typedef struct {
|
||||
uint16_t vendor_id; /* vendor ID */
|
||||
uint16_t product_id; /* product ID */
|
||||
uint16_t version; /* version */
|
||||
uint16_t ssr_max_latency;/* SSR max latency, BTA_HH_SSR_PARAM_INVALID if unknown */
|
||||
uint16_t ssr_min_tout; /* SSR min timeout, BTA_HH_SSR_PARAM_INVALID if unknown */
|
||||
uint8_t ctry_code; /*Country Code.*/
|
||||
tBTA_HH_DEV_DESCR descriptor;
|
||||
} tBTA_HH_DEV_DSCP_INFO;
|
||||
|
||||
/* handshake data */
|
||||
typedef struct {
|
||||
tBTA_HH_STATUS status; /* handshake status */
|
||||
uint8_t handle; /* device handle */
|
||||
union {
|
||||
tBTA_HH_PROTO_MODE proto_mode; /* GET_PROTO_EVT :protocol mode */
|
||||
BT_HDR *p_rpt_data;/* GET_RPT_EVT : report data */
|
||||
uint8_t idle_rate; /* GET_IDLE_EVT : idle rate */
|
||||
} rsp_data;
|
||||
|
||||
} tBTA_HH_HSDATA;
|
||||
|
||||
/* union of data associated with HD callback */
|
||||
typedef union {
|
||||
tBTA_HH_DEV_INFO dev_info; /* BTA_HH_ADD_DEV_EVT, BTA_HH_RMV_DEV_EVT */
|
||||
tBTA_HH_CONN conn; /* BTA_HH_OPEN_EVT */
|
||||
tBTA_HH_CBDATA dev_status; /* BTA_HH_CLOSE_EVT,
|
||||
BTA_HH_SET_PROTO_EVT
|
||||
BTA_HH_SET_RPT_EVT
|
||||
BTA_HH_SET_IDLE_EVT
|
||||
BTA_HH_UPDATE_SCPP_EVT */
|
||||
|
||||
tBTA_HH_STATUS status; /* BTA_HH_ENABLE_EVT */
|
||||
tBTA_HH_DEV_DSCP_INFO dscp_info; /* BTA_HH_GET_DSCP_EVT */
|
||||
tBTA_HH_HSDATA hs_data; /* GET_ transaction callback
|
||||
BTA_HH_GET_RPT_EVT
|
||||
BTA_HH_GET_PROTO_EVT
|
||||
BTA_HH_GET_IDLE_EVT */
|
||||
} tBTA_HH;
|
||||
|
||||
/* BTA HH callback function */
|
||||
typedef void (tBTA_HH_CBACK) (tBTA_HH_EVT event, tBTA_HH *p_data);
|
||||
|
||||
/*****************************************************************************
|
||||
** External Function Declarations
|
||||
*****************************************************************************/
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* This function enable HID host and registers HID-Host with lower layers.*/
|
||||
extern void BTA_HhEnable(uint16_t sec_mask, tBTA_HH_CBACK *p_cback);
|
||||
|
||||
/* This function is called when the host is about power down. */
|
||||
extern void BTA_HhDisable(void);
|
||||
|
||||
/* This function is called to start an inquiry and read SDP record of responding devices; connect to a device if only one active HID device is found. */
|
||||
extern void BTA_HhOpen (esp_bd_addr_t dev_bda, uint8_t bd_type, tBTA_HH_PROTO_MODE mode, uint16_t sec_mask);
|
||||
|
||||
/* This function disconnects the device. */
|
||||
extern void BTA_HhClose(uint8_t dev_handle);
|
||||
|
||||
/* This function set the protocol mode at specified HID handle */
|
||||
extern void BTA_HhSetProtoMode(uint8_t handle, tBTA_HH_PROTO_MODE t_type);
|
||||
|
||||
/* This function get the protocol mode of a specified HID device. */
|
||||
extern void BTA_HhGetProtoMode(uint8_t dev_handle);
|
||||
|
||||
/* send SET_REPORT to device. */
|
||||
extern void BTA_HhSetReport(uint8_t dev_handle, tBTA_HH_RPT_TYPE r_type, BT_HDR *p_data);
|
||||
|
||||
/* Send a GET_REPORT to HID device. */
|
||||
extern void BTA_HhGetReport(uint8_t dev_handle, tBTA_HH_RPT_TYPE r_type, uint8_t rpt_id, uint16_t buf_size);
|
||||
|
||||
/* send SET_IDLE to device. */
|
||||
extern void BTA_HhSetIdle(uint8_t dev_handle, uint16_t idle_rate);
|
||||
|
||||
/* Send a GET_IDLE to HID device. */
|
||||
extern void BTA_HhGetIdle(uint8_t dev_handle);
|
||||
|
||||
/* Send HID_CONTROL request to a HID device. */
|
||||
extern void BTA_HhSendCtrl(uint8_t dev_handle, tBTA_HH_TRANS_CTRL_TYPE c_type);
|
||||
|
||||
/* Send DATA transaction to a HID device. */
|
||||
extern void BTA_HhSendData(uint8_t dev_handle, esp_bd_addr_t dev_bda, BT_HDR *p_buf);
|
||||
|
||||
/* Get report descriptor of the device */
|
||||
extern void BTA_HhGetDscpInfo(uint8_t dev_handle);
|
||||
|
||||
/* Add a virtually cabled device into HID-Host device list to manage and assign a device handle for future API call, host applciation call this API at start-up to initialize its virtually cabled devices. */
|
||||
extern void BTA_HhAddDev(esp_bd_addr_t bda, tBTA_HH_ATTR_MASK attr_mask, uint8_t sub_class, uint8_t app_id, tBTA_HH_DEV_DSCP_INFO dscp_info);
|
||||
|
||||
/* Remove a device from the HID host devices list. */
|
||||
extern void BTA_HhRemoveDev(uint8_t dev_handle );
|
||||
|
||||
enum {
|
||||
BTA_HH_MOD_CTRL_KEY,
|
||||
BTA_HH_MOD_SHFT_KEY,
|
||||
BTA_HH_MOD_ALT_KEY,
|
||||
BTA_HH_MOD_GUI_KEY,
|
||||
BTA_HH_MOD_MAX_KEY
|
||||
};
|
||||
|
||||
/* parsed boot mode keyboard report */
|
||||
typedef struct {
|
||||
uint8_t this_char[6]; /* virtual key code */
|
||||
bool mod_key[BTA_HH_MOD_MAX_KEY];/* ctrl, shift, Alt, GUI */
|
||||
bool caps_lock; /* is caps locked */
|
||||
bool num_lock; /* is Num key pressed */
|
||||
} tBTA_HH_KEYBD_RPT;
|
||||
|
||||
/* parsed boot mode mouse report */
|
||||
typedef struct {
|
||||
uint8_t mouse_button; /* mouse button is clicked */
|
||||
int8_t delta_x; /* displacement x */
|
||||
int8_t delta_y; /* displacement y */
|
||||
} tBTA_HH_MICE_RPT;
|
||||
|
||||
enum {
|
||||
BTA_HH_KEYBD_RPT_ID = 1,
|
||||
BTA_HH_MOUSE_RPT_ID
|
||||
};
|
||||
typedef uint8_t tBTA_HH_BOOT_RPT_ID;
|
||||
|
||||
/* parsed Boot report */
|
||||
typedef struct {
|
||||
tBTA_HH_BOOT_RPT_ID dev_type; /* type of device report */
|
||||
union {
|
||||
tBTA_HH_KEYBD_RPT keybd_rpt; /* keyboard report */
|
||||
tBTA_HH_MICE_RPT mice_rpt; /* mouse report */
|
||||
} data_rpt;
|
||||
} tBTA_HH_BOOT_RPT;
|
||||
|
||||
/* This utility function parse a boot mode report. */
|
||||
extern void BTA_HhParseBootRpt(tBTA_HH_BOOT_RPT *p_data, uint8_t *p_report, uint16_t report_len);
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ESP_BT_HH_API_H_ */
|
42
components/esp_hid/private/esp_hidd_private.h
Normal file
42
components/esp_hid/private/esp_hidd_private.h
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_hidd.h"
|
||||
#include "esp_hid_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct esp_hidd_dev_s {
|
||||
void *dev;
|
||||
esp_hid_transport_t transport;
|
||||
|
||||
bool (*connected) (void *dev);
|
||||
esp_err_t (*deinit) (void *dev);
|
||||
esp_err_t (*battery_set) (void *dev, uint8_t level);
|
||||
esp_err_t (*input_set) (void *dev, size_t map_index, size_t report_id, uint8_t *data, size_t length);
|
||||
esp_err_t (*feature_set) (void *dev, size_t map_index, size_t report_id, uint8_t *data, size_t length);
|
||||
esp_err_t (*event_handler_register) (void *dev, esp_event_handler_t callback, esp_hidd_event_t event);
|
||||
esp_err_t (*event_handler_unregister) (void *dev, esp_event_handler_t callback, esp_hidd_event_t event);
|
||||
};
|
||||
|
||||
typedef struct esp_hidd_dev_s esp_hidd_dev_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
121
components/esp_hid/private/esp_hidh_private.h
Normal file
121
components/esp_hid/private/esp_hidh_private.h
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef _ESP_HIDH_PRIVATE_H_
|
||||
#define _ESP_HIDH_PRIVATE_H_
|
||||
|
||||
#include "esp_hidh.h"
|
||||
#if CONFIG_BLUEDROID_ENABLED
|
||||
#include "esp_gap_bt_api.h"
|
||||
#endif /* CONFIG_BLUEDROID_ENABLED */
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_event.h"
|
||||
#include "sys/queue.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief HIDH device report data
|
||||
*/
|
||||
typedef struct esp_hidh_dev_report_s {
|
||||
struct esp_hidh_dev_report_s *next;
|
||||
uint8_t map_index; //the index of the report map
|
||||
uint8_t report_id; //the id of the report
|
||||
uint8_t report_type; //input, output or feature
|
||||
uint8_t protocol_mode; //boot or report
|
||||
size_t value_len; //maximum len of value by report map
|
||||
esp_hid_usage_t usage; //generic, keyboard or mouse
|
||||
//BLE properties
|
||||
uint16_t handle; //handle to the value
|
||||
uint16_t ccc_handle; //handle to client config
|
||||
uint8_t permissions; //report permissions
|
||||
} esp_hidh_dev_report_t;
|
||||
|
||||
/**
|
||||
* @brief HIDH device data
|
||||
*/
|
||||
struct esp_hidh_dev_s {
|
||||
struct esp_hidh_dev_s *next;
|
||||
|
||||
esp_hid_device_config_t config;
|
||||
esp_hid_usage_t usage;
|
||||
esp_hid_transport_t transport; //BT, BLE or USB
|
||||
bool connected; //we have all required data to communicate
|
||||
bool opened; //we opened the device manually, else the device connected to us
|
||||
int status; //status of the last command
|
||||
|
||||
size_t reports_len;
|
||||
esp_hidh_dev_report_t *reports;
|
||||
|
||||
void *tmp;
|
||||
size_t tmp_len;
|
||||
|
||||
xSemaphoreHandle semaphore;
|
||||
|
||||
esp_err_t (*close) (esp_hidh_dev_t *dev);
|
||||
esp_err_t (*report_write) (esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t *data, size_t len);
|
||||
esp_err_t (*report_read) (esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, size_t max_length, uint8_t *value, size_t *value_len);
|
||||
void (*dump) (esp_hidh_dev_t *dev, FILE *fp);
|
||||
|
||||
#if CONFIG_BLUEDROID_ENABLED
|
||||
esp_bd_addr_t bda;
|
||||
#endif /* CONFIG_BLUEDROID_ENABLED */
|
||||
|
||||
union {
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
struct {
|
||||
esp_bt_cod_t cod;
|
||||
int handle;
|
||||
uint8_t sub_class;
|
||||
uint8_t app_id;
|
||||
uint16_t attr_mask;
|
||||
} bt;
|
||||
#endif /* CONFIG_BT_HID_HOST_ENABLED */
|
||||
#if CONFIG_GATTC_ENABLE
|
||||
struct {
|
||||
esp_ble_addr_type_t address_type;
|
||||
int conn_id;
|
||||
uint16_t appearance;
|
||||
uint16_t battery_handle;
|
||||
uint16_t battery_ccc_handle;
|
||||
} ble;
|
||||
#endif /* CONFIG_GATTC_ENABLE */
|
||||
};
|
||||
TAILQ_ENTRY(esp_hidh_dev_s) devices;
|
||||
};
|
||||
|
||||
typedef TAILQ_HEAD(esp_hidh_dev_head_s, esp_hidh_dev_s) esp_hidh_dev_head_t;
|
||||
|
||||
esp_hidh_dev_t *esp_hidh_dev_malloc(void);
|
||||
|
||||
#if CONFIG_BLUEDROID_ENABLED
|
||||
esp_hidh_dev_t *esp_hidh_dev_get_by_bda(esp_bd_addr_t bda); //BT/BLE
|
||||
esp_hidh_dev_t *esp_hidh_dev_get_by_handle(int handle); //BT Only
|
||||
esp_hidh_dev_t *esp_hidh_dev_get_by_conn_id(uint16_t conn_id); //BLE Only
|
||||
#endif /* CONFIG_BLUEDROID_ENABLED */
|
||||
|
||||
esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_id_and_type(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type);
|
||||
esp_hidh_dev_report_t *esp_hidh_dev_get_input_report_by_id_and_proto(esp_hidh_dev_t *dev, size_t report_id, int protocol_mode);
|
||||
esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_handle(esp_hidh_dev_t *dev, uint16_t handle); //BLE Only
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ESP_HIDH_PRIVATE_H_ */
|
1005
components/esp_hid/src/ble_hidd.c
Normal file
1005
components/esp_hid/src/ble_hidd.c
Normal file
File diff suppressed because it is too large
Load Diff
708
components/esp_hid/src/ble_hidh.c
Normal file
708
components/esp_hid/src/ble_hidh.c
Normal file
@ -0,0 +1,708 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <string.h>
|
||||
#include "ble_hidh.h"
|
||||
#if CONFIG_GATTC_ENABLE
|
||||
#include "esp_hidh_private.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_bt.h"
|
||||
#include "esp_bt_defs.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_gattc_api.h"
|
||||
#include "esp_gatt_defs.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "esp_hid_common.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
static const char *TAG = "BLE_HIDH";
|
||||
|
||||
static const char *s_gattc_evt_names[] = {"REG", "UNREG", "OPEN", "READ_CHAR", "WRITE_CHAR", "CLOSE", "SEARCH_CMPL", "SEARCH_RES", "READ_DESCR", "WRITE_DESCR", "NOTIFY", "PREP_WRITE", "EXEC", "ACL", "CANCEL_OPEN", "SRVC_CHG", "", "ENC_CMPL_CB", "CFG_MTU", "ADV_DATA", "MULT_ADV_ENB", "MULT_ADV_UPD", "MULT_ADV_DATA", "MULT_ADV_DIS", "CONGEST", "BTH_SCAN_ENB", "BTH_SCAN_CFG", "BTH_SCAN_RD", "BTH_SCAN_THR", "BTH_SCAN_PARAM", "BTH_SCAN_DIS", "SCAN_FLT_CFG", "SCAN_FLT_PARAM", "SCAN_FLT_STATUS", "ADV_VSC", "", "", "", "REG_FOR_NOTIFY", "UNREG_FOR_NOTIFY", "CONNECT", "DISCONNECT", "READ_MULTIPLE", "QUEUE_FULL", "SET_ASSOC", "GET_ADDR_LIST", "DIS_SRVC_CMPL"};
|
||||
|
||||
const char *gattc_evt_str(uint8_t event)
|
||||
{
|
||||
if (event >= (sizeof(s_gattc_evt_names)/sizeof(*s_gattc_evt_names))) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return s_gattc_evt_names[event];
|
||||
}
|
||||
|
||||
static xSemaphoreHandle s_ble_hidh_cb_semaphore = NULL;
|
||||
|
||||
static inline void WAIT_CB(void)
|
||||
{
|
||||
xSemaphoreTake(s_ble_hidh_cb_semaphore, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static inline void SEND_CB(void)
|
||||
{
|
||||
xSemaphoreGive(s_ble_hidh_cb_semaphore);
|
||||
}
|
||||
|
||||
static esp_event_loop_handle_t event_loop_handle;
|
||||
static uint8_t *s_read_data_val = NULL;
|
||||
static uint16_t s_read_data_len = 0;
|
||||
static esp_gatt_status_t s_read_status = ESP_GATT_OK;
|
||||
|
||||
static esp_gatt_status_t read_char(esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t handle, esp_gatt_auth_req_t auth_req, uint8_t **out, uint16_t *out_len)
|
||||
{
|
||||
s_read_data_val = NULL;
|
||||
s_read_data_len = 0;
|
||||
if (esp_ble_gattc_read_char(gattc_if, conn_id, handle, auth_req) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "read_char failed");
|
||||
return ESP_GATT_ERROR;
|
||||
}
|
||||
WAIT_CB();
|
||||
if (s_read_status == ESP_GATT_OK) {
|
||||
*out = s_read_data_val;
|
||||
*out_len = s_read_data_len;
|
||||
}
|
||||
return s_read_status;
|
||||
}
|
||||
|
||||
static esp_gatt_status_t read_descr(esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t handle, esp_gatt_auth_req_t auth_req, uint8_t **out, uint16_t *out_len)
|
||||
{
|
||||
s_read_data_val = NULL;
|
||||
s_read_data_len = 0;
|
||||
if (esp_ble_gattc_read_char_descr(gattc_if, conn_id, handle, auth_req) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gattc_read_char failed");
|
||||
return ESP_GATT_ERROR;
|
||||
}
|
||||
WAIT_CB();
|
||||
if (s_read_status == ESP_GATT_OK) {
|
||||
*out = s_read_data_val;
|
||||
*out_len = s_read_data_len;
|
||||
}
|
||||
return s_read_status;
|
||||
}
|
||||
|
||||
static void read_device_services(esp_gatt_if_t gattc_if, esp_hidh_dev_t *dev)
|
||||
{
|
||||
uint16_t suuid, cuuid, duuid;
|
||||
uint16_t chandle, dhandle;
|
||||
esp_hidh_dev_report_t *report = NULL;
|
||||
uint8_t *rdata = 0;
|
||||
uint16_t rlen = 0;
|
||||
esp_hid_report_item_t *r;
|
||||
esp_hid_report_map_t *map;
|
||||
|
||||
esp_gattc_service_elem_t service_result[10];
|
||||
uint16_t dcount = 10;
|
||||
uint8_t hidindex = 0;
|
||||
if (esp_ble_gattc_get_service(gattc_if, dev->ble.conn_id, NULL, service_result, &dcount, 0) == ESP_OK) {
|
||||
ESP_LOGD(TAG, "Found %u HID Services", dev->config.report_maps_len);
|
||||
dev->config.report_maps = (esp_hid_raw_report_map_t *)malloc(dev->config.report_maps_len * sizeof(esp_hid_raw_report_map_t));
|
||||
if (dev->config.report_maps == NULL) {
|
||||
ESP_LOGE(TAG, "malloc report maps failed");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint16_t s = 0; s < dcount; s++) {
|
||||
suuid = service_result[s].uuid.uuid.uuid16;
|
||||
ESP_LOGV(TAG, "SRV(%d) %s start_handle %d, end_handle %d, uuid: 0x%04x", s, service_result[s].is_primary ? " PRIMARY" : "", service_result[s].start_handle, service_result[s].end_handle, suuid);
|
||||
|
||||
if (suuid != ESP_GATT_UUID_BATTERY_SERVICE_SVC
|
||||
&& suuid != ESP_GATT_UUID_DEVICE_INFO_SVC
|
||||
&& suuid != ESP_GATT_UUID_HID_SVC
|
||||
&& suuid != 0x1800) {//device name?
|
||||
continue;
|
||||
}
|
||||
|
||||
esp_gattc_char_elem_t char_result[20];
|
||||
uint16_t ccount = 20;
|
||||
if (esp_ble_gattc_get_all_char(gattc_if, dev->ble.conn_id, service_result[s].start_handle, service_result[s].end_handle, char_result, &ccount, 0) == ESP_OK) {
|
||||
for (uint16_t c = 0; c < ccount; c++) {
|
||||
cuuid = char_result[c].uuid.uuid.uuid16;
|
||||
chandle = char_result[c].char_handle;
|
||||
ESP_LOGV(TAG, " CHAR:(%d), handle: %d, perm: 0x%02x, uuid: 0x%04x", c + 1, chandle, char_result[c].properties, cuuid);
|
||||
|
||||
if (suuid == 0x1800) {
|
||||
if (dev->config.device_name == NULL && cuuid == 0x2a00 && (char_result[c].properties & ESP_GATT_CHAR_PROP_BIT_READ) != 0) {
|
||||
if (read_char(gattc_if, dev->ble.conn_id, chandle, ESP_GATT_AUTH_REQ_NO_MITM, &rdata, &rlen) == ESP_GATT_OK && rlen) {
|
||||
dev->config.device_name = (const char *)rdata;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if (suuid == ESP_GATT_UUID_BATTERY_SERVICE_SVC) {
|
||||
if (cuuid == ESP_GATT_UUID_BATTERY_LEVEL && (char_result[c].properties & ESP_GATT_CHAR_PROP_BIT_READ) != 0) {
|
||||
dev->ble.battery_handle = chandle;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else if (suuid == ESP_GATT_UUID_DEVICE_INFO_SVC) {
|
||||
if (char_result[c].properties & ESP_GATT_CHAR_PROP_BIT_READ) {
|
||||
if (cuuid == ESP_GATT_UUID_PNP_ID) {
|
||||
if (read_char(gattc_if, dev->ble.conn_id, chandle, ESP_GATT_AUTH_REQ_NO_MITM, &rdata, &rlen) == ESP_GATT_OK && rlen == 7) {
|
||||
dev->config.vendor_id = *((uint16_t *)&rdata[1]);
|
||||
dev->config.product_id = *((uint16_t *)&rdata[3]);
|
||||
dev->config.version = *((uint16_t *)&rdata[5]);
|
||||
}
|
||||
free(rdata);
|
||||
} else if (cuuid == ESP_GATT_UUID_MANU_NAME) {
|
||||
if (read_char(gattc_if, dev->ble.conn_id, chandle, ESP_GATT_AUTH_REQ_NO_MITM, &rdata, &rlen) == ESP_GATT_OK && rlen) {
|
||||
dev->config.manufacturer_name = (const char *)rdata;
|
||||
}
|
||||
} else if (cuuid == ESP_GATT_UUID_SERIAL_NUMBER_STR) {
|
||||
if (read_char(gattc_if, dev->ble.conn_id, chandle, ESP_GATT_AUTH_REQ_NO_MITM, &rdata, &rlen) == ESP_GATT_OK && rlen) {
|
||||
dev->config.serial_number = (const char *)rdata;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
if (cuuid == ESP_GATT_UUID_HID_REPORT_MAP) {
|
||||
if (char_result[c].properties & ESP_GATT_CHAR_PROP_BIT_READ) {
|
||||
if (read_char(gattc_if, dev->ble.conn_id, chandle, ESP_GATT_AUTH_REQ_NO_MITM, &rdata, &rlen) == ESP_GATT_OK && rlen) {
|
||||
dev->config.report_maps[hidindex].data = (const uint8_t *)rdata;
|
||||
dev->config.report_maps[hidindex].len = rlen;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
} else if (cuuid == ESP_GATT_UUID_HID_BT_KB_INPUT || cuuid == ESP_GATT_UUID_HID_BT_KB_OUTPUT || cuuid == ESP_GATT_UUID_HID_BT_MOUSE_INPUT || cuuid == ESP_GATT_UUID_HID_REPORT) {
|
||||
report = (esp_hidh_dev_report_t *)malloc(sizeof(esp_hidh_dev_report_t));
|
||||
if (report == NULL) {
|
||||
ESP_LOGE(TAG, "malloc esp_hidh_dev_report_t failed");
|
||||
return;
|
||||
}
|
||||
report->next = NULL;
|
||||
report->permissions = char_result[c].properties;
|
||||
report->handle = chandle;
|
||||
report->ccc_handle = 0;
|
||||
report->report_id = 0;
|
||||
report->map_index = hidindex;
|
||||
if (cuuid == ESP_GATT_UUID_HID_BT_KB_INPUT) {
|
||||
report->protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
|
||||
report->report_type = ESP_HID_REPORT_TYPE_INPUT;
|
||||
report->usage = ESP_HID_USAGE_KEYBOARD;
|
||||
report->value_len = 8;
|
||||
} else if (cuuid == ESP_GATT_UUID_HID_BT_KB_OUTPUT) {
|
||||
report->protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
|
||||
report->report_type = ESP_HID_REPORT_TYPE_OUTPUT;
|
||||
report->usage = ESP_HID_USAGE_KEYBOARD;
|
||||
report->value_len = 8;
|
||||
} else if (cuuid == ESP_GATT_UUID_HID_BT_MOUSE_INPUT) {
|
||||
report->protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
|
||||
report->report_type = ESP_HID_REPORT_TYPE_INPUT;
|
||||
report->usage = ESP_HID_USAGE_MOUSE;
|
||||
report->value_len = 8;
|
||||
} else {
|
||||
report->protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT;
|
||||
report->report_type = 0;
|
||||
report->usage = ESP_HID_USAGE_GENERIC;
|
||||
report->value_len = 0;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
esp_gattc_descr_elem_t descr_result[20];
|
||||
uint16_t dcount = 20;
|
||||
if (esp_ble_gattc_get_all_descr(gattc_if, dev->ble.conn_id, char_result[c].char_handle, descr_result, &dcount, 0) == ESP_OK) {
|
||||
for (uint16_t d = 0; d < dcount; d++) {
|
||||
duuid = descr_result[d].uuid.uuid.uuid16;
|
||||
dhandle = descr_result[d].handle;
|
||||
ESP_LOGV(TAG, " DESCR:(%d), handle: %d, uuid: 0x%04x", d + 1, dhandle, duuid);
|
||||
|
||||
if (suuid == ESP_GATT_UUID_BATTERY_SERVICE_SVC) {
|
||||
if (duuid == ESP_GATT_UUID_CHAR_CLIENT_CONFIG && (char_result[c].properties & ESP_GATT_CHAR_PROP_BIT_NOTIFY) != 0) {
|
||||
dev->ble.battery_ccc_handle = dhandle;
|
||||
}
|
||||
} else if (suuid == ESP_GATT_UUID_HID_SVC && report != NULL) {
|
||||
if (duuid == ESP_GATT_UUID_CHAR_CLIENT_CONFIG && (report->permissions & ESP_GATT_CHAR_PROP_BIT_NOTIFY) != 0) {
|
||||
report->ccc_handle = dhandle;
|
||||
} else if (duuid == ESP_GATT_UUID_RPT_REF_DESCR) {
|
||||
if (read_descr(gattc_if, dev->ble.conn_id, dhandle, ESP_GATT_AUTH_REQ_NO_MITM, &rdata, &rlen) == ESP_GATT_OK && rlen) {
|
||||
report->report_id = rdata[0];
|
||||
report->report_type = rdata[1];
|
||||
free(rdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (suuid == ESP_GATT_UUID_HID_SVC && report != NULL) {
|
||||
report->next = dev->reports;
|
||||
dev->reports = report;
|
||||
dev->reports_len++;
|
||||
}
|
||||
}
|
||||
if (suuid == ESP_GATT_UUID_HID_SVC) {
|
||||
hidindex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t d = 0; d < dev->config.report_maps_len; d++) {
|
||||
if (dev->reports_len && dev->config.report_maps[d].len) {
|
||||
map = esp_hid_parse_report_map(dev->config.report_maps[d].data, dev->config.report_maps[d].len);
|
||||
if (map) {
|
||||
if (dev->ble.appearance == 0) {
|
||||
dev->ble.appearance = map->appearance;
|
||||
}
|
||||
report = dev->reports;
|
||||
while (report) {
|
||||
if (report->map_index == d) {
|
||||
for (uint8_t i = 0; i < map->reports_len; i++) {
|
||||
r = &map->reports[i];
|
||||
if (report->protocol_mode == ESP_HID_PROTOCOL_MODE_BOOT
|
||||
&& report->protocol_mode == r->protocol_mode
|
||||
&& report->report_type == r->report_type
|
||||
&& report->usage == r->usage) {
|
||||
report->report_id = r->report_id;
|
||||
report->value_len = r->value_len;
|
||||
} else if (report->protocol_mode == r->protocol_mode
|
||||
&& report->report_type == r->report_type
|
||||
&& report->report_id == r->report_id) {
|
||||
report->usage = r->usage;
|
||||
report->value_len = r->value_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
report = report->next;
|
||||
}
|
||||
free(map->reports);
|
||||
free(map);
|
||||
map = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void register_for_notify(esp_gatt_if_t gattc_if, esp_bd_addr_t bda, uint16_t handle)
|
||||
{
|
||||
esp_ble_gattc_register_for_notify(gattc_if, bda, handle);
|
||||
WAIT_CB();
|
||||
}
|
||||
|
||||
static void write_char_descr(esp_gatt_if_t gattc_if, uint16_t conn_id, uint16_t handle, uint16_t value_len, uint8_t *value, esp_gatt_write_type_t write_type, esp_gatt_auth_req_t auth_req)
|
||||
{
|
||||
esp_ble_gattc_write_char_descr(gattc_if, conn_id, handle, value_len, value, write_type, auth_req);
|
||||
WAIT_CB();
|
||||
}
|
||||
|
||||
static void attach_report_listeners(esp_gatt_if_t gattc_if, esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return;
|
||||
}
|
||||
uint16_t ccc_data = 1;
|
||||
esp_hidh_dev_report_t *report = dev->reports;
|
||||
|
||||
//subscribe to battery notifications
|
||||
if (dev->ble.battery_handle) {
|
||||
register_for_notify(gattc_if, dev->bda, dev->ble.battery_handle);
|
||||
if (dev->ble.battery_ccc_handle) {
|
||||
//Write CCC descr to enable notifications
|
||||
write_char_descr(gattc_if, dev->ble.conn_id, dev->ble.battery_ccc_handle, 2, (uint8_t *)&ccc_data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NO_MITM);
|
||||
}
|
||||
}
|
||||
|
||||
while (report) {
|
||||
//subscribe to notifications
|
||||
if ((report->permissions & ESP_GATT_CHAR_PROP_BIT_NOTIFY) != 0 && report->protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT) {
|
||||
register_for_notify(gattc_if, dev->bda, report->handle);
|
||||
if (report->ccc_handle) {
|
||||
//Write CCC descr to enable notifications
|
||||
write_char_descr(gattc_if, dev->ble.conn_id, report->ccc_handle, 2, (uint8_t *)&ccc_data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NO_MITM);
|
||||
}
|
||||
}
|
||||
report = report->next;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_gatt_if_t hid_gattc_if = 0;
|
||||
|
||||
void esp_hidh_gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
|
||||
{
|
||||
esp_ble_gattc_cb_param_t *p_data = param;
|
||||
esp_hidh_dev_t *dev = NULL;
|
||||
esp_hidh_dev_report_t *report = NULL;
|
||||
|
||||
switch (event) {
|
||||
case ESP_GATTC_REG_EVT:
|
||||
if (param->reg.status == ESP_GATT_OK) {
|
||||
hid_gattc_if = gattc_if;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Reg app failed, app_id %04x, status 0x%x", param->reg.app_id, param->reg.status);
|
||||
return;
|
||||
}
|
||||
SEND_CB();
|
||||
break;
|
||||
|
||||
case ESP_GATTC_OPEN_EVT:
|
||||
ESP_LOGV(TAG, "OPEN bda " ESP_BD_ADDR_STR ", conn_id %d, status 0x%x, mtu %d", ESP_BD_ADDR_HEX(p_data->open.remote_bda), p_data->open.conn_id, p_data->open.status, p_data->open.mtu);
|
||||
dev = esp_hidh_dev_get_by_bda(p_data->open.remote_bda);
|
||||
if (!dev) {
|
||||
ESP_LOGE(TAG, "OPEN received for unknown device");
|
||||
break;
|
||||
}
|
||||
if (p_data->open.status != 0) {
|
||||
//error
|
||||
ESP_LOGE(TAG, "OPEN failed: 0x%x", p_data->open.status);
|
||||
dev->status = p_data->open.status;//ESP_GATT_CONN_FAIL_ESTABLISH;
|
||||
dev->ble.conn_id = -1;
|
||||
SEND_CB();//return from open
|
||||
} else {
|
||||
dev->status = ESP_GATT_NOT_FOUND;//set to not found and clear if HID service is found
|
||||
dev->ble.conn_id = p_data->open.conn_id;
|
||||
esp_ble_gattc_search_service(gattc_if, dev->ble.conn_id, NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
case ESP_GATTC_SEARCH_RES_EVT:
|
||||
dev = esp_hidh_dev_get_by_conn_id(p_data->search_res.conn_id);
|
||||
if (!dev) {
|
||||
ESP_LOGE(TAG, "SEARCH_RES received for unknown device");
|
||||
break;
|
||||
}
|
||||
if (p_data->search_res.srvc_id.uuid.uuid.uuid16 == ESP_GATT_UUID_HID_SVC) {
|
||||
dev->status = ESP_GATT_OK;
|
||||
dev->config.report_maps_len++;
|
||||
ESP_LOGV(TAG, "SEARCH_RES HID Service was found");
|
||||
}
|
||||
break;
|
||||
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT:
|
||||
dev = esp_hidh_dev_get_by_conn_id(p_data->search_cmpl.conn_id);
|
||||
if (!dev) {
|
||||
ESP_LOGE(TAG, "SEARCH_CMPL received for unknown device");
|
||||
break;
|
||||
}
|
||||
if (dev->status == ESP_GATT_NOT_FOUND) {
|
||||
//service not found
|
||||
ESP_LOGE(TAG, "SEARCH_CMPL HID Service was not found on the device");
|
||||
dev->status = ESP_GATT_CONN_NONE;
|
||||
} else if (p_data->search_cmpl.status) {
|
||||
//error
|
||||
dev->status = p_data->search_cmpl.status;
|
||||
}
|
||||
if (dev->status) {
|
||||
esp_ble_gattc_close(gattc_if, dev->ble.conn_id);
|
||||
dev->ble.conn_id = -1;
|
||||
}
|
||||
dev->connected = true;
|
||||
SEND_CB();//return from open
|
||||
break;
|
||||
|
||||
|
||||
case ESP_GATTC_READ_CHAR_EVT:
|
||||
case ESP_GATTC_READ_DESCR_EVT: {
|
||||
dev = esp_hidh_dev_get_by_conn_id(p_data->read.conn_id);
|
||||
if (!dev) {
|
||||
ESP_LOGE(TAG, "READ received for unknown device");
|
||||
break;
|
||||
}
|
||||
dev->status = p_data->read.status;
|
||||
s_read_status = p_data->read.status;
|
||||
s_read_data_len = 0;
|
||||
s_read_data_val = NULL;
|
||||
if (s_read_status == 0 && p_data->read.value_len > 0) {
|
||||
s_read_data_len = p_data->read.value_len;
|
||||
s_read_data_val = (uint8_t *)malloc(s_read_data_len + 1);
|
||||
if (s_read_data_val) {
|
||||
memcpy(s_read_data_val, p_data->read.value, s_read_data_len);
|
||||
s_read_data_val[s_read_data_len] = 0;
|
||||
}
|
||||
}
|
||||
SEND_CB();
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTC_WRITE_DESCR_EVT: {
|
||||
dev = esp_hidh_dev_get_by_conn_id(p_data->write.conn_id);
|
||||
if (!dev) {
|
||||
ESP_LOGE(TAG, "WRITE_DESCR received for unknown device");
|
||||
break;
|
||||
}
|
||||
dev->status = p_data->write.status;
|
||||
SEND_CB();
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTC_WRITE_CHAR_EVT: {
|
||||
dev = esp_hidh_dev_get_by_conn_id(p_data->write.conn_id);
|
||||
if (!dev) {
|
||||
ESP_LOGE(TAG, "WRITE_CHAR received for unknown device");
|
||||
break;
|
||||
}
|
||||
dev->status = p_data->write.status;
|
||||
if (p_data->write.status) {
|
||||
ESP_LOGE(TAG, "WRITE_CHAR: conn_id %d, handle %d, status 0x%x", p_data->write.conn_id, p_data->write.handle, p_data->write.status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
SEND_CB();
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
ESP_LOGV(TAG, "DISCONNECT: bda " ESP_BD_ADDR_STR ", conn_id %u, reason 0x%x", ESP_BD_ADDR_HEX(p_data->disconnect.remote_bda), p_data->disconnect.conn_id, p_data->disconnect.reason);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
dev = esp_hidh_dev_get_by_conn_id(p_data->notify.conn_id);
|
||||
if (!dev) {
|
||||
ESP_LOGE(TAG, "NOTIFY received for unknown device");
|
||||
break;
|
||||
}
|
||||
if (event_loop_handle) {
|
||||
esp_hidh_event_data_t p = {0};
|
||||
if (p_data->notify.handle == dev->ble.battery_handle) {
|
||||
p.battery.dev = dev;
|
||||
p.battery.level = p_data->notify.value[0];
|
||||
esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_BATTERY_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY);
|
||||
} else {
|
||||
report = esp_hidh_dev_get_report_by_handle(dev, p_data->notify.handle);
|
||||
if (report) {
|
||||
if (report->report_type == ESP_HID_REPORT_TYPE_FEATURE) {
|
||||
p.feature.dev = dev;
|
||||
p.feature.map_index = report->map_index;
|
||||
p.feature.report_id = report->report_id;
|
||||
p.feature.usage = report->usage;
|
||||
p.feature.data = p_data->notify.value;
|
||||
p.feature.length = p_data->notify.value_len;
|
||||
esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_INPUT_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY);
|
||||
} else {
|
||||
p.input.dev = dev;
|
||||
p.input.map_index = report->map_index;
|
||||
p.input.report_id = report->report_id;
|
||||
p.input.usage = report->usage;
|
||||
p.input.data = p_data->notify.value;
|
||||
p.input.length = p_data->notify.value_len;
|
||||
esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_INPUT_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ESP_GATTC_CLOSE_EVT: {
|
||||
ESP_LOGV(TAG, "CLOSE bda " ESP_BD_ADDR_STR ", conn_id %d, status 0x%x, reason 0x%x", ESP_BD_ADDR_HEX(p_data->close.remote_bda), p_data->close.conn_id, p_data->close.status, p_data->close.reason);
|
||||
dev = esp_hidh_dev_get_by_bda(p_data->open.remote_bda);
|
||||
if (!dev) {
|
||||
ESP_LOGE(TAG, "CLOSE received for unknown device");
|
||||
break;
|
||||
}
|
||||
if (!dev->connected) {
|
||||
dev->status = p_data->close.reason;
|
||||
dev->ble.conn_id = -1;
|
||||
SEND_CB();//return from open
|
||||
} else {
|
||||
dev->connected = false;
|
||||
dev->status = p_data->close.status;
|
||||
if (event_loop_handle) {
|
||||
esp_hidh_event_data_t p = {0};
|
||||
p.close.dev = dev;
|
||||
p.close.reason = p_data->close.reason;
|
||||
esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_CLOSE_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY);
|
||||
} else {
|
||||
esp_hidh_dev_free(dev);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ESP_LOGV(TAG, "GATTC EVENT %s", gattc_evt_str(event));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Public Functions
|
||||
* */
|
||||
|
||||
static esp_err_t esp_ble_hidh_dev_close(esp_hidh_dev_t *dev)
|
||||
{
|
||||
return esp_ble_gattc_close(hid_gattc_if, dev->ble.conn_id);
|
||||
}
|
||||
|
||||
static esp_err_t esp_ble_hidh_dev_report_write(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t *value, size_t value_len)
|
||||
{
|
||||
esp_hidh_dev_report_t *report = esp_hidh_dev_get_report_by_id_and_type(dev, map_index, report_id, report_type);
|
||||
if (!report) {
|
||||
ESP_LOGE(TAG, "%s report %d not found", esp_hid_report_type_str(report_type), report_id);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (value_len > report->value_len) {
|
||||
ESP_LOGE(TAG, "%s report %d takes maximum %d bytes. you have provided %d", esp_hid_report_type_str(report_type), report_id, report->value_len, value_len);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return esp_ble_gattc_write_char(hid_gattc_if, dev->ble.conn_id, report->handle, value_len, value, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NO_MITM);
|
||||
}
|
||||
|
||||
static esp_err_t esp_ble_hidh_dev_report_read(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, size_t max_length, uint8_t *value, size_t *value_len)
|
||||
{
|
||||
esp_hidh_dev_report_t *report = esp_hidh_dev_get_report_by_id_and_type(dev, map_index, report_id, report_type);
|
||||
if (!report) {
|
||||
ESP_LOGE(TAG, "%s report %d not found", esp_hid_report_type_str(report_type), report_id);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
uint16_t len = max_length;
|
||||
uint8_t *v = NULL;
|
||||
esp_gatt_status_t s = read_char(hid_gattc_if, dev->ble.conn_id, report->handle, ESP_GATT_AUTH_REQ_NO_MITM, &v, &len);
|
||||
if (s == ESP_GATT_OK) {
|
||||
if (len > max_length) {
|
||||
len = max_length;
|
||||
}
|
||||
*value_len = len;
|
||||
memcpy(value, v, len);
|
||||
return ESP_OK;
|
||||
}
|
||||
ESP_LOGE(TAG, "%s report %d read failed: 0x%x", esp_hid_report_type_str(report_type), report_id, s);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
static void esp_ble_hidh_dev_dump(esp_hidh_dev_t *dev, FILE *fp)
|
||||
{
|
||||
fprintf(fp, "BDA:" ESP_BD_ADDR_STR ", Appearance: 0x%04x, Connection ID: %d\n", ESP_BD_ADDR_HEX(dev->bda), dev->ble.appearance, dev->ble.conn_id);
|
||||
fprintf(fp, "Name: %s, Manufacturer: %s, Serial Number: %s\n", dev->config.device_name ? dev->config.device_name : "", dev->config.manufacturer_name ? dev->config.manufacturer_name : "", dev->config.serial_number ? dev->config.serial_number : "");
|
||||
fprintf(fp, "PID: 0x%04x, VID: 0x%04x, VERSION: 0x%04x\n", dev->config.product_id, dev->config.vendor_id, dev->config.version);
|
||||
fprintf(fp, "Battery: Handle: %u, CCC Handle: %u\n", dev->ble.battery_handle, dev->ble.battery_ccc_handle);
|
||||
fprintf(fp, "Report Maps: %d\n", dev->config.report_maps_len);
|
||||
for (uint8_t d = 0; d < dev->config.report_maps_len; d++) {
|
||||
fprintf(fp, " Report Map Length: %d\n", dev->config.report_maps[d].len);
|
||||
esp_hidh_dev_report_t *report = dev->reports;
|
||||
while (report) {
|
||||
if (report->map_index == d) {
|
||||
fprintf(fp, " %8s %7s %6s, ID: %2u, Length: %3u, Permissions: 0x%02x, Handle: %3u, CCC Handle: %3u\n",
|
||||
esp_hid_usage_str(report->usage), esp_hid_report_type_str(report->report_type), esp_hid_protocol_mode_str(report->protocol_mode),
|
||||
report->report_id, report->value_len, report->permissions, report->handle, report->ccc_handle);
|
||||
}
|
||||
report = report->next;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_hidh_init(const esp_hidh_config_t *config)
|
||||
{
|
||||
esp_err_t ret;
|
||||
if (config == NULL) {
|
||||
ESP_LOGE(TAG, "Config is NULL");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (s_ble_hidh_cb_semaphore != NULL) {
|
||||
ESP_LOGE(TAG, "Already initialised");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
s_ble_hidh_cb_semaphore = xSemaphoreCreateBinary();
|
||||
if (s_ble_hidh_cb_semaphore == NULL) {
|
||||
ESP_LOGE(TAG, "xSemaphoreCreateMutex failed!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_event_loop_args_t event_task_args = {
|
||||
.queue_size = 5,
|
||||
.task_name = "esp_ble_hidh_events",
|
||||
.task_priority = uxTaskPriorityGet(NULL),
|
||||
.task_stack_size = 2048,
|
||||
.task_core_id = tskNO_AFFINITY
|
||||
};
|
||||
ret = esp_event_loop_create(&event_task_args, &event_loop_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_event_loop_create failed!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
|
||||
ret = esp_ble_gattc_app_register(0);
|
||||
if (ret != ESP_OK) {
|
||||
vSemaphoreDelete(s_ble_hidh_cb_semaphore);
|
||||
s_ble_hidh_cb_semaphore = NULL;
|
||||
return ret;
|
||||
}
|
||||
WAIT_CB();
|
||||
esp_event_handler_register_with(event_loop_handle, ESP_HIDH_EVENTS, ESP_EVENT_ANY_ID, config->callback, NULL);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_ble_hidh_deinit(void)
|
||||
{
|
||||
if (s_ble_hidh_cb_semaphore == NULL) {
|
||||
ESP_LOGE(TAG, "Already deinitialised");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_ble_gattc_app_unregister(hid_gattc_if);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "App Unregister Failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (event_loop_handle) {
|
||||
esp_event_loop_delete(event_loop_handle);
|
||||
}
|
||||
vSemaphoreDelete(s_ble_hidh_cb_semaphore);
|
||||
s_ble_hidh_cb_semaphore = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_hidh_dev_t *esp_ble_hidh_dev_open(esp_bd_addr_t bda, esp_ble_addr_type_t address_type)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
esp_hidh_dev_t *dev = esp_hidh_dev_malloc();
|
||||
if (dev == NULL) {
|
||||
ESP_LOGE(TAG, "malloc esp_hidh_dev_t failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->transport = ESP_HID_TRANSPORT_BLE;
|
||||
memcpy(dev->bda, bda, sizeof(esp_bd_addr_t));
|
||||
dev->ble.address_type = address_type;
|
||||
dev->ble.appearance = ESP_HID_APPEARANCE_GENERIC;
|
||||
|
||||
ret = esp_ble_gattc_open(hid_gattc_if, dev->bda, dev->ble.address_type, true);
|
||||
if (ret) {
|
||||
esp_hidh_dev_free(dev);
|
||||
ESP_LOGE(TAG, "esp_ble_gattc_open failed: %d", ret);
|
||||
return NULL;
|
||||
}
|
||||
WAIT_CB();
|
||||
if (dev->ble.conn_id < 0) {
|
||||
ret = dev->status;
|
||||
ESP_LOGE(TAG, "dev open failed! status: 0x%x", dev->status);
|
||||
esp_hidh_dev_free(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->close = esp_ble_hidh_dev_close;
|
||||
dev->report_write = esp_ble_hidh_dev_report_write;
|
||||
dev->report_read = esp_ble_hidh_dev_report_read;
|
||||
dev->dump = esp_ble_hidh_dev_dump;
|
||||
|
||||
read_device_services(hid_gattc_if, dev);
|
||||
|
||||
if (event_loop_handle) {
|
||||
esp_hidh_event_data_t p = {0};
|
||||
p.open.dev = dev;
|
||||
esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_OPEN_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
|
||||
attach_report_listeners(hid_gattc_if, dev);
|
||||
return dev;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_GATTC_ENABLE */
|
445
components/esp_hid/src/bt_hidh.c
Normal file
445
components/esp_hid/src/bt_hidh.c
Normal file
@ -0,0 +1,445 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "bt_hidh.h"
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
#include "esp_hidh_private.h"
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp_bt_hh_api.h"
|
||||
|
||||
static const char *TAG = "BT_HIDH";
|
||||
|
||||
static esp_event_loop_handle_t event_loop_handle;
|
||||
|
||||
static const char *s_bta_hh_evt_names[] = {"ENABLE", "DISABLE", "OPEN", "CLOSE", "GET_RPT", "SET_RPT", "GET_PROTO", "SET_PROTO", "GET_IDLE", "SET_IDLE", "GET_DSCP", "ADD_DEV", "RMV_DEV", "VC_UNPLUG", "DATA", "API_ERR", "UPDATE_SCPP"};
|
||||
static const char *s_bta_hh_status_names[] = {"OK", "HS_HID_NOT_READY", "HS_INVALID_RPT_ID", "HS_TRANS_NOT_SPT", "HS_INVALID_PARAM", "HS_ERROR", "ERR", "ERR_SDP", "ERR_PROTO", "ERR_DB_FULL", "ERR_TOD_UNSPT", "ERR_NO_RES", "ERR_AUTH_FAILED", "ERR_HDL", "ERR_SEC"};
|
||||
|
||||
static inline void WAIT_DEV(esp_hidh_dev_t *dev)
|
||||
{
|
||||
xSemaphoreTake(dev->semaphore, portMAX_DELAY);
|
||||
}
|
||||
|
||||
static inline void SEND_DEV(esp_hidh_dev_t *dev)
|
||||
{
|
||||
xSemaphoreGive(dev->semaphore);
|
||||
}
|
||||
|
||||
static void bta_hh_cb(tBTA_HH_EVT event, tBTA_HH *p_data)
|
||||
{
|
||||
static esp_hidh_dev_t *descr_dev = NULL;
|
||||
esp_hidh_dev_t *dev = NULL;
|
||||
switch (event) {
|
||||
case BTA_HH_ENABLE_EVT: {
|
||||
if (p_data->status) {
|
||||
ESP_LOGE(TAG, "ENABLE ERROR: %s", s_bta_hh_status_names[p_data->status]);
|
||||
}
|
||||
} break;
|
||||
case BTA_HH_OPEN_EVT: {
|
||||
dev = esp_hidh_dev_get_by_handle(p_data->conn.handle);
|
||||
if (dev == NULL) {
|
||||
ESP_LOGE(TAG, "OPEN ERROR: Device Not Found");
|
||||
return;
|
||||
}
|
||||
dev->status = p_data->conn.status;
|
||||
memcpy(dev->bda, p_data->conn.bda, sizeof(esp_bd_addr_t));
|
||||
if (dev->status == BTA_HH_OK) {
|
||||
descr_dev = dev;
|
||||
BTA_HhGetDscpInfo(dev->bt.handle);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "OPEN ERROR: %s", s_bta_hh_status_names[dev->status]);
|
||||
if (dev->opened) {
|
||||
SEND_DEV(dev);
|
||||
} else {
|
||||
esp_hidh_dev_free(dev);
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
case BTA_HH_GET_DSCP_EVT: {
|
||||
ESP_LOGV(TAG, "DESCRIPTOR: PID: 0x%04x, VID: 0x%04x, VERSION: 0x%04x, REPORT_LEN: %u", p_data->dscp_info.product_id, p_data->dscp_info.vendor_id, p_data->dscp_info.version, p_data->dscp_info.descriptor.dl_len);
|
||||
if (descr_dev == NULL) {
|
||||
ESP_LOGE(TAG, "Device Not Found");
|
||||
return;
|
||||
}
|
||||
dev = descr_dev;
|
||||
dev->config.product_id = p_data->dscp_info.product_id;
|
||||
dev->config.vendor_id = p_data->dscp_info.vendor_id;
|
||||
dev->config.version = p_data->dscp_info.version;
|
||||
|
||||
|
||||
dev->config.report_maps_len = 1;
|
||||
dev->config.report_maps = (esp_hid_raw_report_map_t *)malloc(dev->config.report_maps_len * sizeof(esp_hid_raw_report_map_t));
|
||||
if (dev->config.report_maps == NULL) {
|
||||
ESP_LOGE(TAG, "malloc report maps failed");
|
||||
return;
|
||||
}
|
||||
|
||||
dev->config.report_maps[0].data = (uint8_t *)malloc(p_data->dscp_info.descriptor.dl_len);
|
||||
if (dev->config.report_maps[0].data == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc Report Map Failed");
|
||||
dev->status = BTA_HH_ERR_NO_RES;
|
||||
} else {
|
||||
dev->config.report_maps[0].len = p_data->dscp_info.descriptor.dl_len;
|
||||
memcpy((uint8_t *)dev->config.report_maps[0].data, p_data->dscp_info.descriptor.dsc_list, dev->config.report_maps[0].len);
|
||||
//generate reports
|
||||
|
||||
if (dev->config.report_maps[0].len && dev->config.report_maps[0].data) {
|
||||
esp_hid_report_map_t *map;
|
||||
esp_hidh_dev_report_t *report;
|
||||
esp_hid_report_item_t *r;
|
||||
map = esp_hid_parse_report_map(dev->config.report_maps[0].data, dev->config.report_maps[0].len);
|
||||
if (map) {
|
||||
if (dev->usage == 0) {
|
||||
dev->usage = map->usage;
|
||||
}
|
||||
dev->connected = true;
|
||||
dev->reports = NULL;
|
||||
for (uint8_t i = 0; i < map->reports_len; i++) {
|
||||
r = &map->reports[i];
|
||||
report = (esp_hidh_dev_report_t *)malloc(sizeof(esp_hidh_dev_report_t));
|
||||
if (report == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc Report Failed");
|
||||
dev->status = BTA_HH_ERR_NO_RES;
|
||||
dev->connected = false;
|
||||
break;
|
||||
}
|
||||
report->map_index = 0;
|
||||
report->protocol_mode = r->protocol_mode;
|
||||
report->report_type = r->report_type;
|
||||
report->report_id = r->report_id;
|
||||
report->value_len = r->value_len;
|
||||
report->usage = r->usage;
|
||||
report->next = dev->reports;
|
||||
dev->reports = report;
|
||||
}
|
||||
dev->reports_len = map->reports_len;
|
||||
free(map->reports);
|
||||
free(map);
|
||||
map = NULL;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Parse Report Map Failed");
|
||||
dev->status = BTA_HH_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
descr_dev = NULL;
|
||||
if (dev->status == BTA_HH_OK) {
|
||||
BTA_HhAddDev(dev->bda, dev->bt.attr_mask, dev->bt.sub_class, dev->bt.app_id, p_data->dscp_info);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Read Report Map Failed, status: %s", s_bta_hh_status_names[dev->status]);
|
||||
if (dev->opened) {
|
||||
SEND_DEV(dev);
|
||||
} else {
|
||||
esp_hidh_dev_free(dev);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case BTA_HH_ADD_DEV_EVT: {
|
||||
ESP_LOGV(TAG, "ADD_DEV: BDA: " ESP_BD_ADDR_STR ", handle: %d, status: %s", ESP_BD_ADDR_HEX(p_data->dev_info.bda), p_data->dev_info.handle, s_bta_hh_status_names[p_data->dev_info.status]);
|
||||
dev = esp_hidh_dev_get_by_handle(p_data->conn.handle);
|
||||
if (dev == NULL) {
|
||||
ESP_LOGE(TAG, "Device Not Found");
|
||||
return;
|
||||
}
|
||||
dev->status = p_data->conn.status;
|
||||
if (dev->status == BTA_HH_OK) {
|
||||
esp_hidh_event_data_t p;
|
||||
p.open.dev = dev;
|
||||
esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_OPEN_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Device Add Failed, status: %s", s_bta_hh_status_names[dev->status]);
|
||||
}
|
||||
if (dev->opened) {
|
||||
SEND_DEV(dev);
|
||||
} else if (dev->status != BTA_HH_OK) {
|
||||
esp_hidh_dev_free(dev);
|
||||
}
|
||||
} break;
|
||||
case BTA_HH_CLOSE_EVT: {
|
||||
ESP_LOGV(TAG, "CLOSE: handle: %d, status: %s", p_data->dev_status.handle, s_bta_hh_status_names[p_data->dev_status.status]);
|
||||
dev = esp_hidh_dev_get_by_handle(p_data->dev_status.handle);
|
||||
if (dev == NULL) {
|
||||
ESP_LOGE(TAG, "Device Not Found");
|
||||
return;
|
||||
}
|
||||
dev->status = p_data->dev_status.status;
|
||||
esp_hidh_event_data_t p;
|
||||
p.close.dev = dev;
|
||||
p.close.reason = 0;
|
||||
esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_CLOSE_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY);
|
||||
} break;
|
||||
case BTA_HH_SET_RPT_EVT: {
|
||||
dev = esp_hidh_dev_get_by_handle(p_data->dev_status.handle);
|
||||
if (dev == NULL) {
|
||||
ESP_LOGE(TAG, "SET_RPT ERROR: hDevice Not Found");
|
||||
return;
|
||||
}
|
||||
if (p_data->dev_status.status) {
|
||||
ESP_LOGE(TAG, "SET_RPT ERROR: handle: %d, status: %s", p_data->dev_status.handle, s_bta_hh_status_names[p_data->dev_status.status]);
|
||||
}
|
||||
dev->status = p_data->dev_status.status;
|
||||
SEND_DEV(dev);
|
||||
} break;
|
||||
case BTA_HH_GET_RPT_EVT: {
|
||||
dev = esp_hidh_dev_get_by_handle(p_data->hs_data.handle);
|
||||
if (dev == NULL) {
|
||||
ESP_LOGE(TAG, "Device Not Found");
|
||||
return;
|
||||
}
|
||||
if (p_data->hs_data.status) {
|
||||
ESP_LOGE(TAG, "GET_RPT ERROR: handle: %d, status: %s", p_data->hs_data.handle, s_bta_hh_status_names[p_data->hs_data.status]);
|
||||
}
|
||||
dev->status = p_data->hs_data.status;
|
||||
BT_HDR *rpt = p_data->hs_data.rsp_data.p_rpt_data;
|
||||
dev->tmp = rpt->data + rpt->offset;
|
||||
dev->tmp_len = rpt->len;
|
||||
SEND_DEV(dev);
|
||||
} break;
|
||||
default:
|
||||
ESP_LOGV(TAG, "BTA_HH EVENT: %s", s_bta_hh_evt_names[event]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Public Functions
|
||||
* */
|
||||
|
||||
static esp_err_t esp_bt_hidh_dev_close(esp_hidh_dev_t *dev)
|
||||
{
|
||||
BTA_HhClose(dev->bt.handle);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_bt_hidh_dev_report_write(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t *data, size_t len)
|
||||
{
|
||||
esp_hidh_dev_report_t *report = esp_hidh_dev_get_report_by_id_and_type(dev, map_index, report_id, report_type);
|
||||
if (!report) {
|
||||
ESP_LOGE(TAG, "%s report %d not found", esp_hid_report_type_str(report_type), report_id);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (len > report->value_len) {
|
||||
ESP_LOGE(TAG, "%s report %d takes maximum %d bytes. you have provided %d", esp_hid_report_type_str(report_type), report_id, report->value_len, len);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
uint8_t *pbuf_data;
|
||||
BT_HDR *p_buf = (BT_HDR *)malloc((uint16_t) (len + 14 + sizeof(BT_HDR)));
|
||||
|
||||
if (p_buf != NULL) {
|
||||
p_buf->len = len + 1;
|
||||
p_buf->offset = 14;
|
||||
|
||||
pbuf_data = (uint8_t *) (p_buf + 1) + p_buf->offset;
|
||||
pbuf_data[0] = report_id;
|
||||
memcpy(pbuf_data + 1, data, len);
|
||||
|
||||
if (report_type == ESP_HID_REPORT_TYPE_OUTPUT) {
|
||||
p_buf->layer_specific = BTA_HH_RPTT_OUTPUT;
|
||||
BTA_HhSendData(dev->bt.handle, dev->bda, p_buf);
|
||||
} else {
|
||||
BTA_HhSetReport(dev->bt.handle, report_type, p_buf);
|
||||
WAIT_DEV(dev);
|
||||
}
|
||||
if (dev->status) {
|
||||
ESP_LOGE(TAG, "Write %s: %s", esp_hid_report_type_str(report_type), s_bta_hh_status_names[dev->status]);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_bt_hidh_dev_report_read(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, size_t max_length, uint8_t *value, size_t *value_len)
|
||||
{
|
||||
esp_hidh_dev_report_t *report = esp_hidh_dev_get_report_by_id_and_type(dev, map_index, report_id, report_type);
|
||||
if (!report) {
|
||||
ESP_LOGE(TAG, "%s report %d not found", esp_hid_report_type_str(report_type), report_id);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
BTA_HhGetReport(dev->bt.handle, report_type, report_id, max_length);
|
||||
if (xSemaphoreTake(dev->semaphore, 500 / portTICK_PERIOD_MS) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "Read Timeout %s", esp_hid_report_type_str(report_type));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (dev->status) {
|
||||
ESP_LOGE(TAG, "Read %s: %s", esp_hid_report_type_str(report_type), s_bta_hh_status_names[dev->status]);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (report_id) {
|
||||
dev->tmp++;
|
||||
dev->tmp_len--;
|
||||
}
|
||||
if (dev->tmp_len > max_length) {
|
||||
dev->tmp_len = max_length;
|
||||
}
|
||||
*value_len = dev->tmp_len;
|
||||
memcpy(value, dev->tmp, dev->tmp_len);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void esp_bt_hidh_dev_dump(esp_hidh_dev_t *dev, FILE *fp)
|
||||
{
|
||||
fprintf(fp, "BDA:" ESP_BD_ADDR_STR ", Status: %s, Connected: %s, Handle: %d, Usage: %s\n", ESP_BD_ADDR_HEX(dev->bda), s_bta_hh_status_names[dev->status], dev->connected ? "YES" : "NO", dev->bt.handle, esp_hid_usage_str(dev->usage));
|
||||
fprintf(fp, "Name: %s, Manufacturer: %s, Serial Number: %s\n", dev->config.device_name ? dev->config.device_name : "", dev->config.manufacturer_name ? dev->config.manufacturer_name : "", dev->config.serial_number ? dev->config.serial_number : "");
|
||||
fprintf(fp, "PID: 0x%04x, VID: 0x%04x, VERSION: 0x%04x\n", dev->config.product_id, dev->config.vendor_id, dev->config.version);
|
||||
fprintf(fp, "Report Map Length: %d\n", dev->config.report_maps[0].len);
|
||||
esp_hidh_dev_report_t *report = dev->reports;
|
||||
while (report) {
|
||||
fprintf(fp, " %8s %7s %6s, ID: %3u, Length: %3u\n",
|
||||
esp_hid_usage_str(report->usage), esp_hid_report_type_str(report->report_type), esp_hid_protocol_mode_str(report->protocol_mode),
|
||||
report->report_id, report->value_len);
|
||||
report = report->next;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t esp_bt_hidh_init(const esp_hidh_config_t *config)
|
||||
{
|
||||
if (config == NULL) {
|
||||
ESP_LOGE(TAG, "Config is NULL");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_event_loop_args_t event_task_args = {
|
||||
.queue_size = 5,
|
||||
.task_name = "esp_bt_hidh_events",
|
||||
.task_priority = uxTaskPriorityGet(NULL),
|
||||
.task_stack_size = 2048,
|
||||
.task_core_id = tskNO_AFFINITY
|
||||
};
|
||||
esp_err_t ret = esp_event_loop_create(&event_task_args, &event_loop_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_event_loop_create failed!");
|
||||
return ret;
|
||||
}
|
||||
esp_event_handler_register_with(event_loop_handle, ESP_HIDH_EVENTS, ESP_EVENT_ANY_ID, config->callback, NULL);
|
||||
BTA_HhEnable(0, bta_hh_cb);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_bt_hidh_deinit(void)
|
||||
{
|
||||
if (event_loop_handle) {
|
||||
esp_event_loop_delete(event_loop_handle);
|
||||
}
|
||||
BTA_HhDisable();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_hidh_dev_t *esp_bt_hidh_dev_open(esp_bd_addr_t bda)
|
||||
{
|
||||
esp_hidh_dev_t *dev = esp_hidh_dev_malloc();
|
||||
if (dev == NULL) {
|
||||
ESP_LOGE(TAG, "malloc esp_hidh_dev_t failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->transport = ESP_HID_TRANSPORT_BT;
|
||||
memcpy(dev->bda, bda, sizeof(esp_bd_addr_t));
|
||||
dev->bt.handle = -1;
|
||||
|
||||
dev->opened = true;
|
||||
BTA_HhOpen(dev->bda, 0, BTA_HH_PROTO_RPT_MODE, (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT));
|
||||
WAIT_DEV(dev);
|
||||
if (dev->status != BTA_HH_OK) {
|
||||
esp_hidh_dev_free(dev);
|
||||
return NULL;
|
||||
}
|
||||
dev->close = esp_bt_hidh_dev_close;
|
||||
dev->report_write = esp_bt_hidh_dev_report_write;
|
||||
dev->report_read = esp_bt_hidh_dev_report_read;
|
||||
dev->dump = esp_bt_hidh_dev_dump;
|
||||
return dev;
|
||||
}
|
||||
|
||||
/*
|
||||
* BlueDroid BT HIDH Stack Callbacks
|
||||
* */
|
||||
|
||||
/* This callback function is executed by BTA_HH when data is received on an interrupt channel. */
|
||||
void bta_hh_co_data(uint8_t handle, uint8_t *p_rpt, uint16_t len, tBTA_HH_PROTO_MODE mode, uint8_t sub_class, uint8_t country_code, esp_bd_addr_t bda, uint8_t app_id)
|
||||
{
|
||||
if (len < 2) {
|
||||
ESP_LOGE(TAG, "Not Enough Data");
|
||||
return;
|
||||
}
|
||||
esp_hidh_dev_t *dev = NULL;
|
||||
esp_hidh_dev_report_t *report = NULL;
|
||||
dev = esp_hidh_dev_get_by_handle(handle);
|
||||
if (dev == NULL) {
|
||||
ESP_LOGE(TAG, "Device Not Found: handle %u", handle);
|
||||
return;
|
||||
}
|
||||
report = esp_hidh_dev_get_input_report_by_id_and_proto(dev, p_rpt[0], mode ? ESP_HID_PROTOCOL_MODE_BOOT : ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
if (report == NULL) {
|
||||
ESP_LOGE(TAG, "Report Not Found: %d mode: %s", p_rpt[0], mode ? "BOOT" : "REPORT");
|
||||
return;
|
||||
}
|
||||
if (len != (report->value_len + 1)) {
|
||||
ESP_LOGW(TAG, "Wrong Data Len: %u != %u", len, (report->value_len + 1));
|
||||
}
|
||||
|
||||
if (event_loop_handle) {
|
||||
esp_hidh_event_data_t p = {0};
|
||||
if (report->report_type == ESP_HID_REPORT_TYPE_FEATURE) {
|
||||
p.feature.dev = dev;
|
||||
p.feature.report_id = report->report_id;
|
||||
p.feature.usage = report->usage;
|
||||
p.feature.data = p_rpt + 1;
|
||||
p.feature.length = len - 1;
|
||||
esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_FEATURE_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY);
|
||||
} else {
|
||||
p.input.dev = dev;
|
||||
p.input.report_id = report->report_id;
|
||||
p.input.usage = report->usage;
|
||||
p.input.data = p_rpt + 1;
|
||||
p.input.length = len - 1;
|
||||
esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_INPUT_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This callback function is executed by BTA_HH when connection is opened, and application may do some device specific initialization. */
|
||||
void bta_hh_co_open(uint8_t handle, uint8_t sub_class, uint16_t attr_mask, uint8_t app_id)
|
||||
{
|
||||
esp_hidh_dev_t *dev = NULL;
|
||||
dev = esp_hidh_dev_get_by_handle(-1);
|
||||
if (dev == NULL) {
|
||||
ESP_LOGI(TAG, "Device Not Found? It's probably a reconnect.");
|
||||
dev = esp_hidh_dev_malloc();
|
||||
if (dev == NULL) {
|
||||
ESP_LOGE(TAG, "DEV Malloc Failed");
|
||||
return;
|
||||
}
|
||||
dev->transport = ESP_HID_TRANSPORT_BT;
|
||||
dev->close = esp_bt_hidh_dev_close;
|
||||
dev->report_write = esp_bt_hidh_dev_report_write;
|
||||
dev->report_read = esp_bt_hidh_dev_report_read;
|
||||
dev->dump = esp_bt_hidh_dev_dump;
|
||||
}
|
||||
dev->bt.attr_mask = attr_mask;
|
||||
dev->bt.app_id = app_id;
|
||||
dev->bt.sub_class = sub_class;
|
||||
dev->bt.handle = handle;
|
||||
}
|
||||
|
||||
/* This callback function is executed by BTA_HH when connection is closed, and device specific finalization may be needed. */
|
||||
void bta_hh_co_close(uint8_t dev_handle, uint8_t app_id) {}
|
||||
|
||||
#endif /* CONFIG_BT_HID_HOST_ENABLED */
|
519
components/esp_hid/src/esp_hid_common.c
Normal file
519
components/esp_hid/src/esp_hid_common.c
Normal file
@ -0,0 +1,519 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_hid_common.h"
|
||||
#if (CONFIG_GATTS_ENABLE || CONFIG_GATTC_ENABLE)
|
||||
#include "esp_gatt_defs.h"
|
||||
#endif
|
||||
const char *TAG = "hid_parser";
|
||||
|
||||
typedef struct {
|
||||
uint16_t appearance;
|
||||
uint8_t usage_mask;
|
||||
uint8_t reports_len;
|
||||
esp_hid_report_item_t reports[64];
|
||||
} temp_hid_report_map_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t cmd;
|
||||
uint8_t len;
|
||||
union {
|
||||
uint32_t value;
|
||||
uint8_t data[4];
|
||||
};
|
||||
} hid_report_cmd_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t usage_page;
|
||||
uint16_t usage;
|
||||
uint16_t inner_usage_page;
|
||||
uint16_t inner_usage;
|
||||
uint8_t report_id;
|
||||
uint16_t input_len;
|
||||
uint16_t output_len;
|
||||
uint16_t feature_len;
|
||||
} hid_report_params_t;
|
||||
|
||||
typedef enum {
|
||||
PARSE_WAIT_USAGE_PAGE, PARSE_WAIT_USAGE, PARSE_WAIT_COLLECTION_APPLICATION, PARSE_WAIT_END_COLLECTION
|
||||
} s_parse_step_t;
|
||||
|
||||
|
||||
static s_parse_step_t s_parse_step = PARSE_WAIT_USAGE_PAGE;
|
||||
static uint8_t s_collection_depth = 0;
|
||||
static hid_report_params_t s_report_params = {0,};
|
||||
static uint16_t s_report_size = 0;
|
||||
static uint16_t s_report_count = 0;
|
||||
|
||||
static bool s_new_map = false;
|
||||
static temp_hid_report_map_t *s_temp_hid_report_map;
|
||||
|
||||
static int add_report(temp_hid_report_map_t *map, esp_hid_report_item_t *item)
|
||||
{
|
||||
if (map->reports_len >= 64) {
|
||||
ESP_LOGE(TAG, "reports overflow");
|
||||
return -1;
|
||||
}
|
||||
memcpy(&(map->reports[map->reports_len]), item, sizeof(esp_hid_report_item_t));
|
||||
map->reports_len++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_report(hid_report_params_t *report, bool first)
|
||||
{
|
||||
if (s_temp_hid_report_map == NULL) {
|
||||
s_temp_hid_report_map = (temp_hid_report_map_t *)calloc(1, sizeof(temp_hid_report_map_t));
|
||||
if (s_temp_hid_report_map == NULL) {
|
||||
ESP_LOGE(TAG, "malloc failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
temp_hid_report_map_t *map = s_temp_hid_report_map;
|
||||
if (first) {
|
||||
memset(map, 0, sizeof(temp_hid_report_map_t));
|
||||
}
|
||||
|
||||
if (report->usage_page == HID_USAGE_PAGE_GENERIC_DESKTOP && report->usage == HID_USAGE_KEYBOARD) {
|
||||
//Keyboard
|
||||
map->usage_mask |= ESP_HID_USAGE_KEYBOARD;
|
||||
if (report->input_len > 0) {
|
||||
esp_hid_report_item_t item = {
|
||||
.usage = ESP_HID_USAGE_KEYBOARD,
|
||||
.report_id = report->report_id,
|
||||
.report_type = ESP_HID_REPORT_TYPE_INPUT,
|
||||
.protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT,
|
||||
.value_len = report->input_len / 8,
|
||||
};
|
||||
if (add_report(map, &item) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
item.protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
|
||||
item.value_len = 8;
|
||||
if (add_report(map, &item) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (report->output_len > 0) {
|
||||
esp_hid_report_item_t item = {
|
||||
.usage = ESP_HID_USAGE_KEYBOARD,
|
||||
.report_id = report->report_id,
|
||||
.report_type = ESP_HID_REPORT_TYPE_OUTPUT,
|
||||
.protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT,
|
||||
.value_len = report->output_len / 8,
|
||||
};
|
||||
if (add_report(map, &item) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
item.protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
|
||||
item.value_len = 1;
|
||||
if (add_report(map, &item) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else if (report->usage_page == HID_USAGE_PAGE_GENERIC_DESKTOP && report->usage == HID_USAGE_MOUSE) {
|
||||
//Mouse
|
||||
map->usage_mask |= ESP_HID_USAGE_MOUSE;
|
||||
if (report->input_len > 0) {
|
||||
esp_hid_report_item_t item = {
|
||||
.usage = ESP_HID_USAGE_MOUSE,
|
||||
.report_id = report->report_id,
|
||||
.report_type = ESP_HID_REPORT_TYPE_INPUT,
|
||||
.protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT,
|
||||
.value_len = report->input_len / 8,
|
||||
};
|
||||
if (add_report(map, &item) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
item.protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
|
||||
item.value_len = 4;
|
||||
if (add_report(map, &item) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
esp_hid_usage_t cusage = ESP_HID_USAGE_GENERIC;
|
||||
if (report->usage_page == HID_USAGE_PAGE_GENERIC_DESKTOP) {
|
||||
if (report->usage == HID_USAGE_JOYSTICK) {
|
||||
//Joystick
|
||||
map->usage_mask |= ESP_HID_USAGE_JOYSTICK;
|
||||
cusage = ESP_HID_USAGE_JOYSTICK;
|
||||
} else if (report->usage == HID_USAGE_GAMEPAD) {
|
||||
//Gamepad
|
||||
map->usage_mask |= ESP_HID_USAGE_GAMEPAD;
|
||||
cusage = ESP_HID_USAGE_GAMEPAD;
|
||||
}
|
||||
} else if (report->usage_page == HID_USAGE_PAGE_CONSUMER_DEVICE && report->usage == HID_USAGE_CONSUMER_CONTROL) {
|
||||
//Consumer Control
|
||||
map->usage_mask |= ESP_HID_USAGE_CCONTROL;
|
||||
cusage = ESP_HID_USAGE_CCONTROL;
|
||||
} else if (report->usage_page >= 0xFF) {
|
||||
//Vendor
|
||||
map->usage_mask |= ESP_HID_USAGE_VENDOR;
|
||||
cusage = ESP_HID_USAGE_VENDOR;
|
||||
}
|
||||
//Generic
|
||||
esp_hid_report_item_t item = {
|
||||
.usage = cusage,
|
||||
.report_id = report->report_id,
|
||||
.report_type = ESP_HID_REPORT_TYPE_INPUT,
|
||||
.protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT,
|
||||
.value_len = report->input_len / 8,
|
||||
};
|
||||
if (report->input_len > 0) {
|
||||
if (add_report(map, &item) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (report->output_len > 0) {
|
||||
item.report_type = ESP_HID_REPORT_TYPE_OUTPUT;
|
||||
item.value_len = report->output_len / 8;
|
||||
if (add_report(map, &item) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (report->feature_len > 0) {
|
||||
item.report_type = ESP_HID_REPORT_TYPE_FEATURE;
|
||||
item.value_len = report->feature_len / 8;
|
||||
if (add_report(map, &item) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int parse_cmd(const uint8_t *data, size_t len, size_t index, hid_report_cmd_t **out)
|
||||
{
|
||||
if (index == len) {
|
||||
return 0;
|
||||
}
|
||||
hid_report_cmd_t *cmd = (hid_report_cmd_t *)malloc(sizeof(hid_report_cmd_t));
|
||||
if (cmd == NULL) {
|
||||
return -1;
|
||||
}
|
||||
const uint8_t *dp = data + index;
|
||||
cmd->cmd = *dp & 0xFC;
|
||||
cmd->len = *dp & 0x03;
|
||||
cmd->value = 0;
|
||||
if (cmd->len == 3) {
|
||||
cmd->len = 4;
|
||||
}
|
||||
if ((len - index - 1) < cmd->len) {
|
||||
ESP_LOGE(TAG, "not enough bytes! cmd: 0x%02x, len: %u, index: %u", cmd->cmd, cmd->len, index);
|
||||
free(cmd);
|
||||
return -1;
|
||||
}
|
||||
memcpy(cmd->data, dp + 1, cmd->len);
|
||||
*out = cmd;
|
||||
return cmd->len + 1;
|
||||
}
|
||||
|
||||
static int handle_cmd(hid_report_cmd_t *cmd)
|
||||
{
|
||||
switch (s_parse_step) {
|
||||
case PARSE_WAIT_USAGE_PAGE: {
|
||||
if (cmd->cmd != HID_RM_USAGE_PAGE) {
|
||||
ESP_LOGE(TAG, "expected USAGE_PAGE, but got 0x%02x", cmd->cmd);
|
||||
return -1;
|
||||
}
|
||||
s_report_size = 0;
|
||||
s_report_count = 0;
|
||||
memset(&s_report_params, 0, sizeof(hid_report_params_t));
|
||||
s_report_params.usage_page = cmd->value;
|
||||
s_parse_step = PARSE_WAIT_USAGE;
|
||||
break;
|
||||
}
|
||||
case PARSE_WAIT_USAGE: {
|
||||
if (cmd->cmd != HID_RM_USAGE) {
|
||||
ESP_LOGE(TAG, "expected USAGE, but got 0x%02x", cmd->cmd);
|
||||
s_parse_step = PARSE_WAIT_USAGE_PAGE;
|
||||
return -1;
|
||||
}
|
||||
s_report_params.usage = cmd->value;
|
||||
s_parse_step = PARSE_WAIT_COLLECTION_APPLICATION;
|
||||
break;
|
||||
}
|
||||
case PARSE_WAIT_COLLECTION_APPLICATION: {
|
||||
if (cmd->cmd != HID_RM_COLLECTION) {
|
||||
ESP_LOGE(TAG, "expected COLLECTION, but got 0x%02x", cmd->cmd);
|
||||
s_parse_step = PARSE_WAIT_USAGE_PAGE;
|
||||
return -1;
|
||||
}
|
||||
if (cmd->value != 1) {
|
||||
ESP_LOGE(TAG, "expected APPLICATION, but got 0x%02x", cmd->value);
|
||||
s_parse_step = PARSE_WAIT_USAGE_PAGE;
|
||||
return -1;
|
||||
}
|
||||
s_report_params.report_id = 0;
|
||||
s_collection_depth = 1;
|
||||
s_parse_step = PARSE_WAIT_END_COLLECTION;
|
||||
break;
|
||||
}
|
||||
case PARSE_WAIT_END_COLLECTION: {
|
||||
if (cmd->cmd == HID_RM_REPORT_ID) {
|
||||
if (s_report_params.report_id && s_report_params.report_id != cmd->value) {
|
||||
//report id changed mid collection
|
||||
if (s_report_params.input_len & 0x7) {
|
||||
ESP_LOGE(TAG, "ERROR: INPUT report does not amount to full bytes! %d (%d)", s_report_params.input_len, s_report_params.input_len & 0x7);
|
||||
} else if (s_report_params.output_len & 0x7) {
|
||||
ESP_LOGE(TAG, "ERROR: OUTPUT report does not amount to full bytes! %d (%d)", s_report_params.output_len, s_report_params.output_len & 0x7);
|
||||
} else if (s_report_params.feature_len & 0x7) {
|
||||
ESP_LOGE(TAG, "ERROR: FEATURE report does not amount to full bytes! %d (%d)", s_report_params.feature_len, s_report_params.feature_len & 0x7);
|
||||
} else {
|
||||
//SUCCESS!!!
|
||||
int res = handle_report(&s_report_params, s_new_map);
|
||||
if (res != 0) {
|
||||
s_parse_step = PARSE_WAIT_USAGE_PAGE;
|
||||
return -1;
|
||||
}
|
||||
s_new_map = false;
|
||||
|
||||
s_report_params.input_len = 0;
|
||||
s_report_params.output_len = 0;
|
||||
s_report_params.feature_len = 0;
|
||||
s_report_params.usage = s_report_params.inner_usage;
|
||||
s_report_params.usage_page = s_report_params.inner_usage_page;
|
||||
}
|
||||
}
|
||||
s_report_params.report_id = cmd->value;
|
||||
} else if (cmd->cmd == HID_RM_USAGE_PAGE) {
|
||||
s_report_params.inner_usage_page = cmd->value;
|
||||
} else if (cmd->cmd == HID_RM_USAGE) {
|
||||
s_report_params.inner_usage = cmd->value;
|
||||
} else if (cmd->cmd == HID_RM_REPORT_SIZE) {
|
||||
s_report_size = cmd->value;
|
||||
} else if (cmd->cmd == HID_RM_REPORT_COUNT) {
|
||||
s_report_count = cmd->value;
|
||||
} else if (cmd->cmd == HID_RM_INPUT) {
|
||||
s_report_params.input_len += (s_report_size * s_report_count);
|
||||
} else if (cmd->cmd == HID_RM_OUTPUT) {
|
||||
s_report_params.output_len += (s_report_size * s_report_count);
|
||||
} else if (cmd->cmd == HID_RM_FEATURE) {
|
||||
s_report_params.feature_len += (s_report_size * s_report_count);
|
||||
} else if (cmd->cmd == HID_RM_COLLECTION) {
|
||||
s_collection_depth += 1;
|
||||
} else if (cmd->cmd == HID_RM_END_COLLECTION) {
|
||||
s_collection_depth -= 1;
|
||||
if (s_collection_depth == 0) {
|
||||
if (s_report_params.input_len & 0x7) {
|
||||
ESP_LOGE(TAG, "ERROR: INPUT report does not amount to full bytes! %d (%d)", s_report_params.input_len, s_report_params.input_len & 0x7);
|
||||
} else if (s_report_params.output_len & 0x7) {
|
||||
ESP_LOGE(TAG, "ERROR: OUTPUT report does not amount to full bytes! %d (%d)", s_report_params.output_len, s_report_params.output_len & 0x7);
|
||||
} else if (s_report_params.feature_len & 0x7) {
|
||||
ESP_LOGE(TAG, "ERROR: FEATURE report does not amount to full bytes! %d (%d)", s_report_params.feature_len, s_report_params.feature_len & 0x7);
|
||||
} else {
|
||||
//SUCCESS!!!
|
||||
int res = handle_report(&s_report_params, s_new_map);
|
||||
if (res != 0) {
|
||||
s_parse_step = PARSE_WAIT_USAGE_PAGE;
|
||||
return -1;
|
||||
}
|
||||
s_new_map = false;
|
||||
}
|
||||
s_parse_step = PARSE_WAIT_USAGE_PAGE;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
s_parse_step = PARSE_WAIT_USAGE_PAGE;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
esp_hid_report_map_t *esp_hid_parse_report_map(const uint8_t *hid_rm, size_t hid_rm_len)
|
||||
{
|
||||
size_t index = 0;
|
||||
int res;
|
||||
s_new_map = true;
|
||||
|
||||
while (index < hid_rm_len) {
|
||||
hid_report_cmd_t *cmd;
|
||||
res = parse_cmd(hid_rm, hid_rm_len, index, &cmd);
|
||||
if (res < 0) {
|
||||
ESP_LOGE(TAG, "Failed parsing the descriptor at index: %u", index);
|
||||
return NULL;
|
||||
}
|
||||
index += res;
|
||||
res = handle_cmd(cmd);
|
||||
free(cmd);
|
||||
if (res != 0) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
esp_hid_report_map_t *out = (esp_hid_report_map_t *)calloc(1, sizeof(esp_hid_report_map_t));
|
||||
if (out == NULL) {
|
||||
ESP_LOGE(TAG, "hid_report_map malloc failed");
|
||||
free(s_temp_hid_report_map);
|
||||
s_temp_hid_report_map = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
temp_hid_report_map_t *map = s_temp_hid_report_map;
|
||||
|
||||
esp_hid_report_item_t *reports = (esp_hid_report_item_t *)calloc(1, map->reports_len * sizeof(esp_hid_report_item_t));
|
||||
if (reports == NULL) {
|
||||
ESP_LOGE(TAG, "hid_report_items malloc failed! %u maps", map->reports_len);
|
||||
free(out);
|
||||
free(s_temp_hid_report_map);
|
||||
s_temp_hid_report_map = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (map->usage_mask & ESP_HID_USAGE_KEYBOARD) {
|
||||
out->usage = ESP_HID_USAGE_KEYBOARD;
|
||||
out->appearance = ESP_HID_APPEARANCE_KEYBOARD;
|
||||
} else if (map->usage_mask & ESP_HID_USAGE_MOUSE) {
|
||||
out->usage = ESP_HID_USAGE_MOUSE;
|
||||
out->appearance = ESP_HID_APPEARANCE_MOUSE;
|
||||
} else if (map->usage_mask & ESP_HID_USAGE_JOYSTICK) {
|
||||
out->usage = ESP_HID_USAGE_JOYSTICK;
|
||||
out->appearance = ESP_HID_APPEARANCE_JOYSTICK;
|
||||
} else if (map->usage_mask & ESP_HID_USAGE_GAMEPAD) {
|
||||
out->usage = ESP_HID_USAGE_GAMEPAD;
|
||||
out->appearance = ESP_HID_APPEARANCE_GAMEPAD;
|
||||
} else if (map->usage_mask & ESP_HID_USAGE_CCONTROL) {
|
||||
out->usage = ESP_HID_USAGE_CCONTROL;
|
||||
out->appearance = ESP_HID_APPEARANCE_KEYBOARD;
|
||||
} else {
|
||||
out->usage = ESP_HID_USAGE_GENERIC;
|
||||
out->appearance = ESP_HID_APPEARANCE_GENERIC;
|
||||
}
|
||||
out->reports_len = map->reports_len;
|
||||
memcpy(reports, map->reports, map->reports_len * sizeof(esp_hid_report_item_t));
|
||||
out->reports = reports;
|
||||
free(s_temp_hid_report_map);
|
||||
s_temp_hid_report_map = NULL;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void esp_hid_free_report_map(esp_hid_report_map_t *map)
|
||||
{
|
||||
if (map != NULL){
|
||||
free(map->reports);
|
||||
free(map);
|
||||
}
|
||||
}
|
||||
|
||||
esp_hid_usage_t esp_hid_usage_from_appearance(uint16_t appearance)
|
||||
{
|
||||
return ESP_HID_USAGE_GENERIC;
|
||||
}
|
||||
|
||||
esp_hid_usage_t esp_hid_usage_from_cod(uint32_t cod)
|
||||
{
|
||||
return ESP_HID_USAGE_GENERIC;
|
||||
}
|
||||
|
||||
static const char *s_unknown_str = "UNKNOWN";
|
||||
static const char *s_hid_protocol_names[] = {"BOOT", "REPORT"};
|
||||
static const char *s_hid_report_type_names[] = {"NULL", "INPUT", "OUTPUT", "FEATURE"};
|
||||
static const char *s_hid_cod_major_names[] = {"MISC", "COMPUTER", "PHONE", "LAN_NAP", "AV", "PERIPHERAL", "IMAGING", "WEARABLE", "TOY", "HEALTH"};
|
||||
static const char *s_hid_cod_minor_names[7] = {"GENERIC", "JOYSTICK", "GAMEPAD", "REMOTE", "SENSOR", "TABLET", "CARD_READER"};
|
||||
|
||||
const char *esp_hid_usage_str(esp_hid_usage_t usage)
|
||||
{
|
||||
switch (usage) {
|
||||
case ESP_HID_USAGE_GENERIC: return "GENERIC";
|
||||
case ESP_HID_USAGE_KEYBOARD: return "KEYBOARD";
|
||||
case ESP_HID_USAGE_MOUSE: return "MOUSE";
|
||||
case ESP_HID_USAGE_JOYSTICK: return "JOYSTICK";
|
||||
case ESP_HID_USAGE_GAMEPAD: return "GAMEPAD";
|
||||
case ESP_HID_USAGE_CCONTROL: return "CCONTROL";
|
||||
case ESP_HID_USAGE_VENDOR: return "VENDOR";
|
||||
default: break;
|
||||
}
|
||||
return s_unknown_str;
|
||||
}
|
||||
|
||||
const char *esp_hid_protocol_mode_str(uint8_t protocol)
|
||||
{
|
||||
if (protocol >= (sizeof(s_hid_protocol_names)/sizeof(s_hid_protocol_names[0]))) {
|
||||
return s_unknown_str;
|
||||
}
|
||||
return s_hid_protocol_names[protocol];
|
||||
}
|
||||
|
||||
const char *esp_hid_report_type_str(uint8_t report_type)
|
||||
{
|
||||
if (report_type >= (sizeof(s_hid_report_type_names)/sizeof(s_hid_report_type_names[0]))) {
|
||||
return s_unknown_str;
|
||||
}
|
||||
return s_hid_report_type_names[report_type];
|
||||
}
|
||||
|
||||
const char *esp_hid_cod_major_str(uint8_t cod_major)
|
||||
{
|
||||
if (cod_major >= (sizeof(s_hid_cod_major_names)/sizeof(s_hid_cod_major_names[0]))) {
|
||||
return s_unknown_str;
|
||||
}
|
||||
return s_hid_cod_major_names[cod_major];
|
||||
}
|
||||
|
||||
void esp_hid_cod_minor_print(uint8_t cod_min, FILE *fp)
|
||||
{
|
||||
if (cod_min & ESP_HID_COD_MIN_KEYBOARD) {
|
||||
fputs("KEYBOARD", fp);
|
||||
}
|
||||
if (cod_min & ESP_HID_COD_MIN_MOUSE) {
|
||||
if (cod_min & ESP_HID_COD_MIN_KEYBOARD) {
|
||||
fputs("+", fp);
|
||||
}
|
||||
fputs("MOUSE", fp);
|
||||
}
|
||||
if (cod_min & 0xF0) {
|
||||
if (cod_min & 0x0F) {
|
||||
fputs("+", fp);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
cod_min &= 0x0F;
|
||||
if (cod_min < ESP_HID_COD_MIN_MAX) {
|
||||
fprintf(fp, "%s", s_hid_cod_minor_names[cod_min]);
|
||||
}
|
||||
}
|
||||
|
||||
const char *esp_hid_disconnect_reason_str(esp_hid_transport_t transport, int reason)
|
||||
{
|
||||
if (transport == ESP_HID_TRANSPORT_BLE) {
|
||||
#if (CONFIG_GATTS_ENABLE || CONFIG_GATTC_ENABLE)
|
||||
switch ((esp_gatt_conn_reason_t)reason) {
|
||||
case ESP_GATT_CONN_L2C_FAILURE: return "L2C_FAILURE";
|
||||
case ESP_GATT_CONN_TIMEOUT: return "TIMEOUT";
|
||||
case ESP_GATT_CONN_TERMINATE_PEER_USER: return "TERMINATE_PEER_USER";
|
||||
case ESP_GATT_CONN_TERMINATE_LOCAL_HOST: return "TERMINATE_LOCAL_HOST";
|
||||
case ESP_GATT_CONN_LMP_TIMEOUT: return "LMP_TIMEOUT";
|
||||
case ESP_GATT_CONN_FAIL_ESTABLISH: return "FAIL_ESTABLISH";
|
||||
case ESP_GATT_CONN_CONN_CANCEL: return "CONN_CANCEL";
|
||||
case ESP_GATT_CONN_NONE: return "NONE";
|
||||
default: break;
|
||||
}
|
||||
#endif /* CONFIG_GATTS_ENABLE || CONFIG_GATTC_ENABLE */
|
||||
}
|
||||
return s_unknown_str;
|
||||
}
|
||||
|
121
components/esp_hid/src/esp_hidd.c
Normal file
121
components/esp_hid/src/esp_hidd.c
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "esp_hidd.h"
|
||||
#include "esp_hidd_private.h"
|
||||
#include "esp_event_base.h"
|
||||
|
||||
#if CONFIG_GATTS_ENABLE
|
||||
#include "ble_hidd.h"
|
||||
#endif /* CONFIG_GATTS_ENABLE */
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(ESP_HIDD_EVENTS);
|
||||
|
||||
esp_err_t esp_hidd_dev_init(const esp_hid_device_config_t *config, esp_hid_transport_t transport, esp_event_handler_t callback, esp_hidd_dev_t **dev_out)
|
||||
{
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_hidd_dev_t *dev = (esp_hidd_dev_t *)calloc(1, sizeof(esp_hidd_dev_t));
|
||||
if (dev == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
switch (transport) {
|
||||
#if CONFIG_GATTS_ENABLE
|
||||
case ESP_HID_TRANSPORT_BLE:
|
||||
ret = esp_ble_hidd_dev_init(dev, config, callback);
|
||||
break;
|
||||
#endif /* CONFIG_GATTS_ENABLE */
|
||||
default:
|
||||
ret = ESP_FAIL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
free(dev);
|
||||
return ret;
|
||||
}
|
||||
dev->transport = transport;
|
||||
*dev_out = dev;
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_hidd_dev_deinit(esp_hidd_dev_t *dev)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
esp_err_t ret = dev->deinit(dev->dev);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
free(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_hid_transport_t esp_hidd_dev_transport_get(esp_hidd_dev_t *dev)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return ESP_HID_TRANSPORT_MAX;
|
||||
}
|
||||
return dev->transport;
|
||||
}
|
||||
|
||||
bool esp_hidd_dev_connected(esp_hidd_dev_t *dev)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return false;
|
||||
}
|
||||
return dev->connected(dev->dev);
|
||||
}
|
||||
|
||||
esp_err_t esp_hidd_dev_battery_set(esp_hidd_dev_t *dev, uint8_t level)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return dev->battery_set(dev->dev, level);
|
||||
}
|
||||
|
||||
esp_err_t esp_hidd_dev_input_set(esp_hidd_dev_t *dev, size_t map_index, size_t report_id, uint8_t *data, size_t length)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return dev->input_set(dev->dev, map_index, report_id, data, length);
|
||||
}
|
||||
|
||||
esp_err_t esp_hidd_dev_feature_set(esp_hidd_dev_t *dev, size_t map_index, size_t report_id, uint8_t *data, size_t length)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return dev->feature_set(dev->dev, map_index, report_id, data, length);
|
||||
}
|
||||
|
||||
esp_err_t esp_hidd_dev_event_handler_register(esp_hidd_dev_t *dev, esp_event_handler_t callback, esp_hidd_event_t event)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return dev->event_handler_register(dev->dev, callback, event);
|
||||
}
|
||||
|
||||
esp_err_t esp_hidd_dev_event_handler_unregister(esp_hidd_dev_t *dev, esp_event_handler_t callback, esp_hidd_event_t event)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return dev->event_handler_unregister(dev->dev, callback, event);
|
||||
}
|
488
components/esp_hid/src/esp_hidh.c
Normal file
488
components/esp_hid/src/esp_hidh.c
Normal file
@ -0,0 +1,488 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "sys/queue.h"
|
||||
#include "esp_hidh_private.h"
|
||||
#include "bt_hidh.h"
|
||||
#include "ble_hidh.h"
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "esp_event_base.h"
|
||||
|
||||
ESP_EVENT_DEFINE_BASE(ESP_HIDH_EVENTS);
|
||||
|
||||
static const char *TAG = "ESP_HIDH";
|
||||
|
||||
static esp_hidh_dev_head_t s_esp_hidh_devices;
|
||||
|
||||
static xSemaphoreHandle s_esp_hidh_devices_semaphore = NULL;
|
||||
|
||||
static inline void lock_devices(void)
|
||||
{
|
||||
if (s_esp_hidh_devices_semaphore != NULL) {
|
||||
xSemaphoreTake(s_esp_hidh_devices_semaphore, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void unlock_devices(void)
|
||||
{
|
||||
if (s_esp_hidh_devices_semaphore != NULL) {
|
||||
xSemaphoreGive(s_esp_hidh_devices_semaphore);
|
||||
}
|
||||
}
|
||||
|
||||
static bool esp_hidh_dev_exists(esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (dev == NULL) {
|
||||
return false;
|
||||
}
|
||||
esp_hidh_dev_t * d = NULL;
|
||||
lock_devices();
|
||||
TAILQ_FOREACH(d, &s_esp_hidh_devices, devices) {
|
||||
if (d == dev) {
|
||||
unlock_devices();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
unlock_devices();
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Public Functions
|
||||
* */
|
||||
|
||||
esp_err_t esp_hidh_init(const esp_hidh_config_t *config)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
if (config == NULL) {
|
||||
ESP_LOGE(TAG, "Config is NULL");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (s_esp_hidh_devices_semaphore != NULL) {
|
||||
ESP_LOGE(TAG, "Already initialized");
|
||||
return err;
|
||||
}
|
||||
|
||||
s_esp_hidh_devices_semaphore = xSemaphoreCreateBinary();
|
||||
if (s_esp_hidh_devices_semaphore == NULL) {
|
||||
ESP_LOGE(TAG, "xSemaphoreCreateMutex failed!");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ESP_OK;
|
||||
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
if (err == ESP_OK) {
|
||||
err = esp_bt_hidh_init(config);
|
||||
}
|
||||
#endif /* CONFIG_BT_HID_HOST_ENABLED */
|
||||
|
||||
#if CONFIG_GATTC_ENABLE
|
||||
if (err == ESP_OK) {
|
||||
err = esp_ble_hidh_init(config);
|
||||
}
|
||||
#endif /* CONFIG_GATTC_ENABLE */
|
||||
|
||||
if (err == ESP_OK) {
|
||||
TAILQ_INIT(&s_esp_hidh_devices);
|
||||
unlock_devices();
|
||||
} else {
|
||||
vSemaphoreDelete(s_esp_hidh_devices_semaphore);
|
||||
s_esp_hidh_devices_semaphore = NULL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_hidh_deinit(void)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
if (s_esp_hidh_devices_semaphore == NULL) {
|
||||
ESP_LOGE(TAG, "Already uninitialized");
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!TAILQ_EMPTY(&s_esp_hidh_devices)) {
|
||||
ESP_LOGE(TAG, "Please disconnect all devices first!");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ESP_OK;
|
||||
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
if (err == ESP_OK) {
|
||||
err = esp_bt_hidh_deinit();
|
||||
}
|
||||
#endif /* CONFIG_BT_HID_HOST_ENABLED */
|
||||
|
||||
#if CONFIG_GATTC_ENABLE
|
||||
if (err == ESP_OK) {
|
||||
err = esp_ble_hidh_deinit();
|
||||
}
|
||||
#endif /* CONFIG_GATTC_ENABLE */
|
||||
|
||||
if (err == ESP_OK) {
|
||||
TAILQ_INIT(&s_esp_hidh_devices);
|
||||
vSemaphoreDelete(s_esp_hidh_devices_semaphore);
|
||||
s_esp_hidh_devices_semaphore = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
#if CONFIG_BLUEDROID_ENABLED
|
||||
esp_hidh_dev_t *esp_hidh_dev_open(esp_bd_addr_t bda, esp_hid_transport_t transport, uint8_t remote_addr_type)
|
||||
{
|
||||
if (esp_hidh_dev_get_by_bda(bda) != NULL) {
|
||||
ESP_LOGE(TAG, "Already Connected");
|
||||
return NULL;
|
||||
}
|
||||
esp_hidh_dev_t *dev = NULL;
|
||||
#if CONFIG_GATTC_ENABLE
|
||||
if (transport == ESP_HID_TRANSPORT_BLE) {
|
||||
dev = esp_ble_hidh_dev_open(bda, (esp_ble_addr_type_t)remote_addr_type);
|
||||
}
|
||||
#endif /* CONFIG_GATTC_ENABLE */
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
if (transport == ESP_HID_TRANSPORT_BT) {
|
||||
dev = esp_bt_hidh_dev_open(bda);
|
||||
}
|
||||
#endif /* CONFIG_BT_HID_HOST_ENABLED */
|
||||
return dev;
|
||||
}
|
||||
#endif /* CONFIG_BLUEDROID_ENABLED */
|
||||
|
||||
esp_err_t esp_hidh_dev_close(esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return dev->close(dev);
|
||||
}
|
||||
|
||||
void esp_hidh_dev_dump(esp_hidh_dev_t *dev, FILE *fp)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return;
|
||||
}
|
||||
dev->dump(dev, fp);
|
||||
}
|
||||
|
||||
esp_err_t esp_hidh_dev_output_set(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, uint8_t *value, size_t value_len)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return dev->report_write(dev, map_index, report_id, ESP_HID_REPORT_TYPE_OUTPUT, value, value_len);
|
||||
}
|
||||
|
||||
esp_err_t esp_hidh_dev_feature_set(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, uint8_t *value, size_t value_len)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return dev->report_write(dev, map_index, report_id, ESP_HID_REPORT_TYPE_FEATURE, value, value_len);
|
||||
}
|
||||
|
||||
esp_err_t esp_hidh_dev_feature_get(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, size_t max_length, uint8_t *value, size_t *value_len)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return dev->report_read(dev, map_index, report_id, ESP_HID_REPORT_TYPE_FEATURE, max_length, value, value_len);
|
||||
}
|
||||
|
||||
const uint8_t *esp_hidh_dev_bda_get(esp_hidh_dev_t *dev)
|
||||
{
|
||||
#if CONFIG_BLUEDROID_ENABLED
|
||||
if (esp_hidh_dev_exists(dev)) {
|
||||
return dev->bda;
|
||||
}
|
||||
#endif /* CONFIG_BLUEDROID_ENABLED */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_hid_transport_t esp_hidh_dev_transport_get(esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return ESP_HID_TRANSPORT_MAX;
|
||||
}
|
||||
return dev->transport;
|
||||
}
|
||||
|
||||
const esp_hid_device_config_t *esp_hidh_dev_config_get(esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return NULL;
|
||||
}
|
||||
return &dev->config;
|
||||
}
|
||||
|
||||
const char *esp_hidh_dev_name_get(esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return NULL;
|
||||
}
|
||||
return dev->config.device_name ? dev->config.device_name : "";
|
||||
}
|
||||
|
||||
const char *esp_hidh_dev_manufacturer_get(esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return NULL;
|
||||
}
|
||||
return dev->config.manufacturer_name ? dev->config.manufacturer_name : "";
|
||||
}
|
||||
|
||||
const char *esp_hidh_dev_serial_get(esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return NULL;
|
||||
}
|
||||
return dev->config.serial_number ? dev->config.serial_number : "";
|
||||
}
|
||||
|
||||
uint16_t esp_hidh_dev_vendor_id_get(esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return 0;
|
||||
}
|
||||
return dev->config.vendor_id;
|
||||
}
|
||||
|
||||
uint16_t esp_hidh_dev_product_id_get(esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return 0;
|
||||
}
|
||||
return dev->config.product_id;
|
||||
}
|
||||
|
||||
uint16_t esp_hidh_dev_version_get(esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return 0;
|
||||
}
|
||||
return dev->config.version;
|
||||
}
|
||||
|
||||
esp_hid_usage_t esp_hidh_dev_usage_get(esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return ESP_HID_USAGE_GENERIC;
|
||||
}
|
||||
return dev->usage;
|
||||
}
|
||||
|
||||
esp_err_t esp_hidh_dev_reports_get(esp_hidh_dev_t *dev, size_t *num_reports, esp_hid_report_item_t **reports)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_hid_report_item_t *r = (esp_hid_report_item_t *)malloc(sizeof(esp_hid_report_item_t) * dev->reports_len);
|
||||
if (r == NULL) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_hidh_dev_report_t *dr = dev->reports;
|
||||
for (uint8_t i = 0; i < dev->reports_len; i++) {
|
||||
if (dr == NULL) {
|
||||
//error
|
||||
return ESP_FAIL;
|
||||
}
|
||||
r[i].map_index = dr->map_index;
|
||||
r[i].protocol_mode = dr->protocol_mode;
|
||||
r[i].usage = dr->usage;
|
||||
r[i].report_id = dr->report_id;
|
||||
r[i].report_type = dr->report_type;
|
||||
r[i].value_len = dr->value_len;
|
||||
|
||||
dr = dr->next;
|
||||
}
|
||||
*reports = r;
|
||||
*num_reports = dev->reports_len;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_hidh_dev_report_maps_get(esp_hidh_dev_t *dev, size_t *num_maps, esp_hid_raw_report_map_t **maps)
|
||||
{
|
||||
if (!esp_hidh_dev_exists(dev)) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
*num_maps = dev->config.report_maps_len;
|
||||
*maps = dev->config.report_maps;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Private Functions
|
||||
* */
|
||||
|
||||
esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_handle(esp_hidh_dev_t *dev, uint16_t handle)
|
||||
{
|
||||
esp_hidh_dev_report_t *r = dev->reports;
|
||||
while (r) {
|
||||
if (r->handle == handle) {
|
||||
return r;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_id_and_type(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type)
|
||||
{
|
||||
esp_hidh_dev_report_t *r = dev->reports;
|
||||
while (r) {
|
||||
if (r->map_index == map_index && r->report_id == report_id && r->report_type == report_type && r->protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT) {
|
||||
return r;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_hidh_dev_report_t *esp_hidh_dev_get_input_report_by_id_and_proto(esp_hidh_dev_t *dev, size_t report_id, int protocol_mode)
|
||||
{
|
||||
esp_hidh_dev_report_t *r = dev->reports;
|
||||
while (r) {
|
||||
if (r->report_id == report_id && (r->report_type & 1) && r->protocol_mode == protocol_mode) {
|
||||
return r;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void esp_hidh_dev_resources_free(esp_hidh_dev_t *dev)
|
||||
{
|
||||
if (dev->semaphore) {
|
||||
vSemaphoreDelete(dev->semaphore);
|
||||
}
|
||||
free((void *)dev->config.device_name);
|
||||
free((void *)dev->config.manufacturer_name);
|
||||
free((void *)dev->config.serial_number);
|
||||
for (uint8_t d = 0; d < dev->config.report_maps_len; d++) {
|
||||
free((void *)dev->config.report_maps[d].data);
|
||||
}
|
||||
free((void *)dev->config.report_maps);
|
||||
esp_hidh_dev_report_t *r;
|
||||
while (dev->reports) {
|
||||
r = dev->reports;
|
||||
dev->reports = dev->reports->next;
|
||||
free(r);
|
||||
}
|
||||
free(dev);
|
||||
}
|
||||
|
||||
esp_hidh_dev_t *esp_hidh_dev_malloc()
|
||||
{
|
||||
esp_hidh_dev_t *dev = (esp_hidh_dev_t *)calloc(1, sizeof(esp_hidh_dev_t));
|
||||
if (dev == NULL) {
|
||||
ESP_LOGE(TAG, "malloc esp_hidh_dev_t failed");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev->semaphore = xSemaphoreCreateBinary();
|
||||
if (dev->semaphore == NULL) {
|
||||
ESP_LOGE(TAG, "malloc semaphore failed");
|
||||
esp_hidh_dev_resources_free(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lock_devices();
|
||||
TAILQ_INSERT_TAIL(&s_esp_hidh_devices, dev, devices);
|
||||
unlock_devices();
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
esp_err_t esp_hidh_dev_free(esp_hidh_dev_t *dev)
|
||||
{
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
|
||||
if (dev == NULL) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_hidh_dev_t *d = NULL;
|
||||
esp_hidh_dev_t *next = NULL;
|
||||
lock_devices();
|
||||
TAILQ_FOREACH_SAFE(d, &s_esp_hidh_devices, devices, next) {
|
||||
if (d == dev) {
|
||||
TAILQ_REMOVE(&s_esp_hidh_devices, d, devices);
|
||||
esp_hidh_dev_resources_free(d);
|
||||
ret = ESP_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unlock_devices();
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "device not found");
|
||||
} else {
|
||||
ESP_LOGD(TAG, "device removed");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if CONFIG_BLUEDROID_ENABLED
|
||||
esp_hidh_dev_t *esp_hidh_dev_get_by_bda(esp_bd_addr_t bda)
|
||||
{
|
||||
esp_hidh_dev_t * d = NULL;
|
||||
lock_devices();
|
||||
TAILQ_FOREACH(d, &s_esp_hidh_devices, devices) {
|
||||
if (memcmp(bda, d->bda, sizeof(esp_bd_addr_t)) == 0) {
|
||||
unlock_devices();
|
||||
return d;
|
||||
}
|
||||
}
|
||||
unlock_devices();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_hidh_dev_t *esp_hidh_dev_get_by_handle(int handle)
|
||||
{
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
esp_hidh_dev_t * d = NULL;
|
||||
lock_devices();
|
||||
TAILQ_FOREACH(d, &s_esp_hidh_devices, devices) {
|
||||
if (d->transport == ESP_HID_TRANSPORT_BT && d->bt.handle == handle) {
|
||||
unlock_devices();
|
||||
return d;
|
||||
}
|
||||
}
|
||||
unlock_devices();
|
||||
#endif /* CONFIG_BT_HID_HOST_ENABLED */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_hidh_dev_t *esp_hidh_dev_get_by_conn_id(uint16_t conn_id)
|
||||
{
|
||||
#if CONFIG_GATTC_ENABLE
|
||||
esp_hidh_dev_t * d = NULL;
|
||||
lock_devices();
|
||||
TAILQ_FOREACH(d, &s_esp_hidh_devices, devices) {
|
||||
if (d->transport == ESP_HID_TRANSPORT_BLE && d->ble.conn_id == conn_id) {
|
||||
unlock_devices();
|
||||
return d;
|
||||
}
|
||||
}
|
||||
unlock_devices();
|
||||
#endif /* CONFIG_GATTC_ENABLE */
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_BLUEDROID_ENABLED */
|
3
components/esp_hid/test/CMakeLists.txt
Normal file
3
components/esp_hid/test/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES unity test_utils esp_hid)
|
40
components/esp_hid/test/README.md
Normal file
40
components/esp_hid/test/README.md
Normal file
@ -0,0 +1,40 @@
|
||||
### Tests have been generated with the following code
|
||||
|
||||
```c
|
||||
|
||||
void dump_report_map(const uint8_t *data, size_t len){
|
||||
esp_hid_report_map_t * report_map = esp_hid_parse_report_map(data, len);
|
||||
printf(" TEST_ASSERT_NOT_NULL(report_map);\n");
|
||||
printf(" TEST_ASSERT(report_map->usage == ESP_HID_USAGE_%s);\n", esp_hid_usage_str(report_map->usage));
|
||||
printf(" TEST_ASSERT(report_map->appearance == 0x%04X);\n", report_map->appearance);
|
||||
printf(" TEST_ASSERT(report_map->reports_len == %u);\n", report_map->reports_len);
|
||||
for(uint8_t i=0; i<report_map->reports_len; i++){
|
||||
printf(" TEST_ASSERT(report_map->reports[%u].report_id == %u);\n", i, report_map->reports[i].report_id);
|
||||
printf(" TEST_ASSERT(report_map->reports[%u].report_type == ESP_HID_REPORT_TYPE_%s);\n", i, esp_hid_report_type_str(report_map->reports[i].report_type));
|
||||
printf(" TEST_ASSERT(report_map->reports[%u].protocol_mode == ESP_HID_PROTOCOL_MODE_%s);\n", i, esp_hid_protocol_mode_str(report_map->reports[i].protocol_mode));
|
||||
printf(" TEST_ASSERT(report_map->reports[%u].usage == ESP_HID_USAGE_%s);\n", i, esp_hid_usage_str(report_map->reports[i].usage));
|
||||
printf(" TEST_ASSERT(report_map->reports[%u].value_len == %u);\n", i, report_map->reports[i].value_len);
|
||||
}
|
||||
printf(" esp_hid_free_report_map(report_map);\n");
|
||||
}
|
||||
|
||||
#define _str(a) #a
|
||||
#define xstr(a) _str(a)
|
||||
#define TEST_DUMP(map) \
|
||||
printf("TEST_CASE(\"can parse " xstr(map) "\", \"[esp_hid]\")\n{\n"); \
|
||||
printf(" esp_hid_report_map_t * report_map = esp_hid_parse_report_map(" xstr(map) ", sizeof(" xstr(map) "));\n"); \
|
||||
dump_report_map(map, sizeof(map)); \
|
||||
printf("}\n\n");
|
||||
|
||||
void generate_tests(){
|
||||
TEST_DUMP(hidReportMap);
|
||||
TEST_DUMP(relMouseReportMap);
|
||||
TEST_DUMP(absMouseReportMap);
|
||||
TEST_DUMP(keyboardReportMap);
|
||||
TEST_DUMP(joystickReportMap);
|
||||
TEST_DUMP(mediaReportMap);
|
||||
TEST_DUMP(mediaReportMap2);
|
||||
TEST_DUMP(hidapiReportMap);
|
||||
}
|
||||
|
||||
```
|
1
components/esp_hid/test/component.mk
Normal file
1
components/esp_hid/test/component.mk
Normal file
@ -0,0 +1 @@
|
||||
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
|
403
components/esp_hid/test/hid_descriptor.h
Normal file
403
components/esp_hid/test/hid_descriptor.h
Normal file
@ -0,0 +1,403 @@
|
||||
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
#pragma once
|
||||
|
||||
const unsigned char hidReportMap[] = {
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x02, // Usage (Mouse)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x01, // Report ID (1)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xA1, 0x00, // Collection (Physical)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x03, // Usage Maximum (0x03)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x03, // Report Count (3)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x75, 0x05, // Report Size (5)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x09, 0x38, // Usage (Wheel)
|
||||
0x15, 0x81, // Logical Minimum (-127)
|
||||
0x25, 0x7F, // Logical Maximum (127)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x03, // Report Count (3)
|
||||
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0xC0, // End Collection
|
||||
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x06, // Usage (Keyboard)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x02, // Report ID (2)
|
||||
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||
0x19, 0xE0, // Usage Minimum (0xE0)
|
||||
0x29, 0xE7, // Usage Maximum (0xE7)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x05, // Report Count (5)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x05, 0x08, // Usage Page (LEDs)
|
||||
0x19, 0x01, // Usage Minimum (Num Lock)
|
||||
0x29, 0x05, // Usage Maximum (Kana)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x03, // Report Size (3)
|
||||
0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x95, 0x06, // Report Count (6)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x65, // Logical Maximum (101)
|
||||
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||
0x19, 0x00, // Usage Minimum (0x00)
|
||||
0x29, 0x65, // Usage Maximum (0x65)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
|
||||
0x05, 0x0C, // Usage Page (Consumer)
|
||||
0x09, 0x01, // Usage (Consumer Control)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x03, // Report ID (3)
|
||||
0x09, 0x02, // Usage (Numeric Key Pad)
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x0A, // Usage Maximum (0x0A)
|
||||
0x15, 0x01, // Logical Minimum (1)
|
||||
0x25, 0x0A, // Logical Maximum (10)
|
||||
0x75, 0x04, // Report Size (4)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0x05, 0x0C, // Usage Page (Consumer)
|
||||
0x09, 0x86, // Usage (Channel)
|
||||
0x15, 0xFF, // Logical Minimum (-1)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x75, 0x02, // Report Size (2)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x46, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,Null State)
|
||||
0x09, 0xE9, // Usage (Volume Increment)
|
||||
0x09, 0xEA, // Usage (Volume Decrement)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x09, 0xE2, // Usage (Mute)
|
||||
0x09, 0x30, // Usage (Power)
|
||||
0x09, 0x83, // Usage (Recall Last)
|
||||
0x09, 0x81, // Usage (Assign Selection)
|
||||
0x09, 0xB0, // Usage (Play)
|
||||
0x09, 0xB1, // Usage (Pause)
|
||||
0x09, 0xB2, // Usage (Record)
|
||||
0x09, 0xB3, // Usage (Fast Forward)
|
||||
0x09, 0xB4, // Usage (Rewind)
|
||||
0x09, 0xB5, // Usage (Scan Next Track)
|
||||
0x09, 0xB6, // Usage (Scan Previous Track)
|
||||
0x09, 0xB7, // Usage (Stop)
|
||||
0x15, 0x01, // Logical Minimum (1)
|
||||
0x25, 0x0C, // Logical Maximum (12)
|
||||
0x75, 0x04, // Report Size (4)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x09, 0x80, // Usage (Selection)
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x03, // Usage Maximum (0x03)
|
||||
0x15, 0x01, // Logical Minimum (1)
|
||||
0x25, 0x03, // Logical Maximum (3)
|
||||
0x75, 0x02, // Report Size (2)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
|
||||
0x06, 0xFF, 0xFF, // Usage Page (Vendor Defined 0xFFFF)
|
||||
0x09, 0xA5, // Usage (0xA5)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x04, // Report ID (4)
|
||||
0x09, 0xA6, // Usage (0xA6)
|
||||
0x09, 0xA9, // Usage (0xA9)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x7F, // Report Count (127)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
|
||||
// 250 bytes
|
||||
};
|
||||
|
||||
const unsigned char relMouseReportMap[] = { //4 bytes (btns,x,y,wheel)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x02, // Usage (Mouse)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xA1, 0x00, // Collection (Physical)
|
||||
0x85, 0x01, // Report ID (1)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x05, // Usage Maximum (0x05)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x95, 0x05, // Report Count (5)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x03, // Report Size (3)
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x09, 0x38, // Usage (Wheel)
|
||||
0x15, 0x81, // Logical Minimum (-127)
|
||||
0x25, 0x7F, // Logical Maximum (127)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x03, // Report Count (3)
|
||||
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0xC0, // End Collection
|
||||
|
||||
// 54 bytes
|
||||
};
|
||||
|
||||
const unsigned char absMouseReportMap[] = { //6 bytes (btns,x*2,y*2,wheel)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x02, // Usage (Mouse)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xA1, 0x00, // Collection (Physical)
|
||||
0x85, 0x01, // Report ID (1)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x05, // Usage Maximum (0x05)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x95, 0x05, // Report Count (5)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x03, // Report Size (3)
|
||||
0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x7F, // Logical Maximum (32767)
|
||||
0x75, 0x10, // Report Size (16)
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x38, // Usage (Wheel)
|
||||
0x15, 0x81, // Logical Minimum (-127)
|
||||
0x25, 0x7F, // Logical Maximum (127)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0xC0, // End Collection
|
||||
|
||||
// 67 bytes
|
||||
};
|
||||
|
||||
const unsigned char keyboardReportMap[] = { //7 bytes input (modifiers, resrvd, keys*5), 1 byte output
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x06, // Usage (Keyboard)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x01, // Report ID (1)
|
||||
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||
0x19, 0xE0, // Usage Minimum (0xE0)
|
||||
0x29, 0xE7, // Usage Maximum (0xE7)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x05, // Report Count (5)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x05, 0x08, // Usage Page (LEDs)
|
||||
0x19, 0x01, // Usage Minimum (Num Lock)
|
||||
0x29, 0x05, // Usage Maximum (Kana)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x03, // Report Size (3)
|
||||
0x91, 0x03, // Output (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x95, 0x05, // Report Count (5)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x65, // Logical Maximum (101)
|
||||
0x05, 0x07, // Usage Page (Kbrd/Keypad)
|
||||
0x19, 0x00, // Usage Minimum (0x00)
|
||||
0x29, 0x65, // Usage Maximum (0x65)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
|
||||
// 65 bytes
|
||||
};
|
||||
|
||||
const unsigned char joystickReportMap[] = { // 8bytes (8but, 4but+hat, r, y, rx, ry, z, rz)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x04, // Usage (Joystick)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x01, // Report ID (1)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x0C, // Usage Maximum (0x0C)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x0C, // Report Count (12)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x39, // Usage (Hat switch)
|
||||
0x15, 0x01, // Logical Minimum (1)
|
||||
0x25, 0x08, // Logical Maximum (8)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x04, // Report Size (4)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0xA1, 0x00, // Collection (Physical)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x09, 0x33, // Usage (Rx)
|
||||
0x09, 0x34, // Usage (Ry)
|
||||
0x15, 0x80, // Logical Minimum (-128)
|
||||
0x25, 0x7F, // Logical Maximum (127)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x09, 0x32, // Usage (Z)
|
||||
0x09, 0x35, // Usage (Rz)
|
||||
0x15, 0x80, // Logical Minimum (-128)
|
||||
0x25, 0x7F, // Logical Maximum (127)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0xC0, // End Collection
|
||||
|
||||
// 76 bytes
|
||||
};
|
||||
|
||||
const unsigned char mediaReportMap[] = { //6 bytes (3 x 16bit)
|
||||
0x05, 0x0C, // Usage Page (Consumer)
|
||||
0x09, 0x01, // Usage (Consumer Control)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x03, // Report ID (3)
|
||||
0x19, 0x00, // Usage Minimum (Unassigned)
|
||||
0x2A, 0xFF, 0x03, // Usage Maximum (0x03FF)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x03, // Logical Maximum (1023)
|
||||
0x95, 0x03, // Report Count (3)
|
||||
0x75, 0x10, // Report Size (16)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
|
||||
// 25 bytes
|
||||
};
|
||||
|
||||
const unsigned char mediaReportMap2[] = {
|
||||
0x05, 0x0C, // Usage Page (Consumer)
|
||||
0x09, 0x01, // Usage (Consumer Control)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x03, // Report ID (3)
|
||||
0x09, 0x02, // Usage (Numeric Key Pad)
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x0A, // Usage Maximum (0x0A)
|
||||
0x15, 0x01, // Logical Minimum (1)
|
||||
0x25, 0x0A, // Logical Maximum (10)
|
||||
0x75, 0x04, // Report Size (4)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0x05, 0x0C, // Usage Page (Consumer)
|
||||
0x09, 0x86, // Usage (Channel)
|
||||
0x15, 0xFF, // Logical Minimum (-1)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x75, 0x02, // Report Size (2)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x46, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,Null State)
|
||||
0x09, 0xE9, // Usage (Volume Increment)
|
||||
0x09, 0xEA, // Usage (Volume Decrement)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x09, 0xE2, // Usage (Mute)
|
||||
0x09, 0x30, // Usage (Power)
|
||||
0x09, 0x83, // Usage (Recall Last)
|
||||
0x09, 0x81, // Usage (Assign Selection)
|
||||
0x09, 0xB0, // Usage (Play)
|
||||
0x09, 0xB1, // Usage (Pause)
|
||||
0x09, 0xB2, // Usage (Record)
|
||||
0x09, 0xB3, // Usage (Fast Forward)
|
||||
0x09, 0xB4, // Usage (Rewind)
|
||||
0x09, 0xB5, // Usage (Scan Next Track)
|
||||
0x09, 0xB6, // Usage (Scan Previous Track)
|
||||
0x09, 0xB7, // Usage (Stop)
|
||||
0x15, 0x01, // Logical Minimum (1)
|
||||
0x25, 0x0C, // Logical Maximum (12)
|
||||
0x75, 0x04, // Report Size (4)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x09, 0x80, // Usage (Selection)
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x03, // Usage Maximum (0x03)
|
||||
0x15, 0x01, // Logical Minimum (1)
|
||||
0x25, 0x03, // Logical Maximum (3)
|
||||
0x75, 0x02, // Report Size (2)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
};
|
||||
|
||||
const unsigned char hidapiReportMap[] = { //8 bytes input, 8 bytes feature
|
||||
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
|
||||
0x0A, 0x00, 0x01, // Usage (0x0100)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x01, // Report ID (1)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x09, 0x01, // Usage (0x01)
|
||||
0x82, 0x02, 0x01, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Buffered Bytes)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x09, 0x02, // Usage (0x02)
|
||||
0xB2, 0x02, 0x01, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x09, 0x03, // Usage (0x03)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
|
||||
// 38 bytes
|
||||
};
|
214
components/esp_hid/test/test_esp_hid.c
Normal file
214
components/esp_hid/test/test_esp_hid.c
Normal file
@ -0,0 +1,214 @@
|
||||
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/unistd.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "test_utils.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_hid_common.h"
|
||||
#include "hid_descriptor.h"
|
||||
|
||||
TEST_CASE("can parse hidReportMap", "[esp_hid]")
|
||||
{
|
||||
esp_hid_report_map_t * report_map = esp_hid_parse_report_map(hidReportMap, sizeof(hidReportMap));
|
||||
TEST_ASSERT_NOT_NULL(report_map);
|
||||
TEST_ASSERT(report_map->usage == ESP_HID_USAGE_KEYBOARD);
|
||||
TEST_ASSERT(report_map->appearance == 0x03C1);
|
||||
TEST_ASSERT(report_map->reports_len == 8);
|
||||
TEST_ASSERT(report_map->reports[0].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[0].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[0].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[0].usage == ESP_HID_USAGE_MOUSE);
|
||||
TEST_ASSERT(report_map->reports[0].value_len == 4);
|
||||
TEST_ASSERT(report_map->reports[1].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[1].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[1].protocol_mode == ESP_HID_PROTOCOL_MODE_BOOT);
|
||||
TEST_ASSERT(report_map->reports[1].usage == ESP_HID_USAGE_MOUSE);
|
||||
TEST_ASSERT(report_map->reports[1].value_len == 4);
|
||||
TEST_ASSERT(report_map->reports[2].report_id == 2);
|
||||
TEST_ASSERT(report_map->reports[2].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[2].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[2].usage == ESP_HID_USAGE_KEYBOARD);
|
||||
TEST_ASSERT(report_map->reports[2].value_len == 8);
|
||||
TEST_ASSERT(report_map->reports[3].report_id == 2);
|
||||
TEST_ASSERT(report_map->reports[3].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[3].protocol_mode == ESP_HID_PROTOCOL_MODE_BOOT);
|
||||
TEST_ASSERT(report_map->reports[3].usage == ESP_HID_USAGE_KEYBOARD);
|
||||
TEST_ASSERT(report_map->reports[3].value_len == 8);
|
||||
TEST_ASSERT(report_map->reports[4].report_id == 2);
|
||||
TEST_ASSERT(report_map->reports[4].report_type == ESP_HID_REPORT_TYPE_OUTPUT);
|
||||
TEST_ASSERT(report_map->reports[4].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[4].usage == ESP_HID_USAGE_KEYBOARD);
|
||||
TEST_ASSERT(report_map->reports[4].value_len == 1);
|
||||
TEST_ASSERT(report_map->reports[5].report_id == 2);
|
||||
TEST_ASSERT(report_map->reports[5].report_type == ESP_HID_REPORT_TYPE_OUTPUT);
|
||||
TEST_ASSERT(report_map->reports[5].protocol_mode == ESP_HID_PROTOCOL_MODE_BOOT);
|
||||
TEST_ASSERT(report_map->reports[5].usage == ESP_HID_USAGE_KEYBOARD);
|
||||
TEST_ASSERT(report_map->reports[5].value_len == 1);
|
||||
TEST_ASSERT(report_map->reports[6].report_id == 3);
|
||||
TEST_ASSERT(report_map->reports[6].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[6].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[6].usage == ESP_HID_USAGE_CCONTROL);
|
||||
TEST_ASSERT(report_map->reports[6].value_len == 2);
|
||||
TEST_ASSERT(report_map->reports[7].report_id == 4);
|
||||
TEST_ASSERT(report_map->reports[7].report_type == ESP_HID_REPORT_TYPE_OUTPUT);
|
||||
TEST_ASSERT(report_map->reports[7].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[7].usage == ESP_HID_USAGE_VENDOR);
|
||||
TEST_ASSERT(report_map->reports[7].value_len == 127);
|
||||
esp_hid_free_report_map(report_map);
|
||||
}
|
||||
|
||||
TEST_CASE("can parse relMouseReportMap", "[esp_hid]")
|
||||
{
|
||||
esp_hid_report_map_t * report_map = esp_hid_parse_report_map(relMouseReportMap, sizeof(relMouseReportMap));
|
||||
TEST_ASSERT_NOT_NULL(report_map);
|
||||
TEST_ASSERT(report_map->usage == ESP_HID_USAGE_MOUSE);
|
||||
TEST_ASSERT(report_map->appearance == 0x03C2);
|
||||
TEST_ASSERT(report_map->reports_len == 2);
|
||||
TEST_ASSERT(report_map->reports[0].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[0].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[0].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[0].usage == ESP_HID_USAGE_MOUSE);
|
||||
TEST_ASSERT(report_map->reports[0].value_len == 4);
|
||||
TEST_ASSERT(report_map->reports[1].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[1].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[1].protocol_mode == ESP_HID_PROTOCOL_MODE_BOOT);
|
||||
TEST_ASSERT(report_map->reports[1].usage == ESP_HID_USAGE_MOUSE);
|
||||
TEST_ASSERT(report_map->reports[1].value_len == 4);
|
||||
esp_hid_free_report_map(report_map);
|
||||
}
|
||||
|
||||
TEST_CASE("can parse absMouseReportMap", "[esp_hid]")
|
||||
{
|
||||
esp_hid_report_map_t * report_map = esp_hid_parse_report_map(absMouseReportMap, sizeof(absMouseReportMap));
|
||||
TEST_ASSERT_NOT_NULL(report_map);
|
||||
TEST_ASSERT(report_map->usage == ESP_HID_USAGE_MOUSE);
|
||||
TEST_ASSERT(report_map->appearance == 0x03C2);
|
||||
TEST_ASSERT(report_map->reports_len == 2);
|
||||
TEST_ASSERT(report_map->reports[0].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[0].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[0].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[0].usage == ESP_HID_USAGE_MOUSE);
|
||||
TEST_ASSERT(report_map->reports[0].value_len == 6);
|
||||
TEST_ASSERT(report_map->reports[1].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[1].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[1].protocol_mode == ESP_HID_PROTOCOL_MODE_BOOT);
|
||||
TEST_ASSERT(report_map->reports[1].usage == ESP_HID_USAGE_MOUSE);
|
||||
TEST_ASSERT(report_map->reports[1].value_len == 4);
|
||||
esp_hid_free_report_map(report_map);
|
||||
}
|
||||
|
||||
TEST_CASE("can parse keyboardReportMap", "[esp_hid]")
|
||||
{
|
||||
esp_hid_report_map_t * report_map = esp_hid_parse_report_map(keyboardReportMap, sizeof(keyboardReportMap));
|
||||
TEST_ASSERT_NOT_NULL(report_map);
|
||||
TEST_ASSERT(report_map->usage == ESP_HID_USAGE_KEYBOARD);
|
||||
TEST_ASSERT(report_map->appearance == 0x03C1);
|
||||
TEST_ASSERT(report_map->reports_len == 4);
|
||||
TEST_ASSERT(report_map->reports[0].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[0].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[0].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[0].usage == ESP_HID_USAGE_KEYBOARD);
|
||||
TEST_ASSERT(report_map->reports[0].value_len == 7);
|
||||
TEST_ASSERT(report_map->reports[1].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[1].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[1].protocol_mode == ESP_HID_PROTOCOL_MODE_BOOT);
|
||||
TEST_ASSERT(report_map->reports[1].usage == ESP_HID_USAGE_KEYBOARD);
|
||||
TEST_ASSERT(report_map->reports[1].value_len == 8);
|
||||
TEST_ASSERT(report_map->reports[2].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[2].report_type == ESP_HID_REPORT_TYPE_OUTPUT);
|
||||
TEST_ASSERT(report_map->reports[2].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[2].usage == ESP_HID_USAGE_KEYBOARD);
|
||||
TEST_ASSERT(report_map->reports[2].value_len == 1);
|
||||
TEST_ASSERT(report_map->reports[3].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[3].report_type == ESP_HID_REPORT_TYPE_OUTPUT);
|
||||
TEST_ASSERT(report_map->reports[3].protocol_mode == ESP_HID_PROTOCOL_MODE_BOOT);
|
||||
TEST_ASSERT(report_map->reports[3].usage == ESP_HID_USAGE_KEYBOARD);
|
||||
TEST_ASSERT(report_map->reports[3].value_len == 1);
|
||||
esp_hid_free_report_map(report_map);
|
||||
}
|
||||
|
||||
TEST_CASE("can parse joystickReportMap", "[esp_hid]")
|
||||
{
|
||||
esp_hid_report_map_t * report_map = esp_hid_parse_report_map(joystickReportMap, sizeof(joystickReportMap));
|
||||
TEST_ASSERT_NOT_NULL(report_map);
|
||||
TEST_ASSERT(report_map->usage == ESP_HID_USAGE_JOYSTICK);
|
||||
TEST_ASSERT(report_map->appearance == 0x03C3);
|
||||
TEST_ASSERT(report_map->reports_len == 1);
|
||||
TEST_ASSERT(report_map->reports[0].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[0].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[0].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[0].usage == ESP_HID_USAGE_JOYSTICK);
|
||||
TEST_ASSERT(report_map->reports[0].value_len == 8);
|
||||
esp_hid_free_report_map(report_map);
|
||||
}
|
||||
|
||||
TEST_CASE("can parse mediaReportMap", "[esp_hid]")
|
||||
{
|
||||
esp_hid_report_map_t * report_map = esp_hid_parse_report_map(mediaReportMap, sizeof(mediaReportMap));
|
||||
TEST_ASSERT_NOT_NULL(report_map);
|
||||
TEST_ASSERT(report_map->usage == ESP_HID_USAGE_CCONTROL);
|
||||
TEST_ASSERT(report_map->appearance == 0x03C1);
|
||||
TEST_ASSERT(report_map->reports_len == 1);
|
||||
TEST_ASSERT(report_map->reports[0].report_id == 3);
|
||||
TEST_ASSERT(report_map->reports[0].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[0].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[0].usage == ESP_HID_USAGE_CCONTROL);
|
||||
TEST_ASSERT(report_map->reports[0].value_len == 6);
|
||||
esp_hid_free_report_map(report_map);
|
||||
}
|
||||
|
||||
TEST_CASE("can parse mediaReportMap2", "[esp_hid]")
|
||||
{
|
||||
esp_hid_report_map_t * report_map = esp_hid_parse_report_map(mediaReportMap2, sizeof(mediaReportMap2));
|
||||
TEST_ASSERT_NOT_NULL(report_map);
|
||||
TEST_ASSERT(report_map->usage == ESP_HID_USAGE_CCONTROL);
|
||||
TEST_ASSERT(report_map->appearance == 0x03C1);
|
||||
TEST_ASSERT(report_map->reports_len == 1);
|
||||
TEST_ASSERT(report_map->reports[0].report_id == 3);
|
||||
TEST_ASSERT(report_map->reports[0].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[0].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[0].usage == ESP_HID_USAGE_CCONTROL);
|
||||
TEST_ASSERT(report_map->reports[0].value_len == 2);
|
||||
esp_hid_free_report_map(report_map);
|
||||
}
|
||||
|
||||
TEST_CASE("can parse hidapiReportMap", "[esp_hid]")
|
||||
{
|
||||
esp_hid_report_map_t * report_map = esp_hid_parse_report_map(hidapiReportMap, sizeof(hidapiReportMap));
|
||||
TEST_ASSERT_NOT_NULL(report_map);
|
||||
TEST_ASSERT(report_map->usage == ESP_HID_USAGE_GENERIC);
|
||||
TEST_ASSERT(report_map->appearance == 0x03C0);
|
||||
TEST_ASSERT(report_map->reports_len == 3);
|
||||
TEST_ASSERT(report_map->reports[0].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[0].report_type == ESP_HID_REPORT_TYPE_INPUT);
|
||||
TEST_ASSERT(report_map->reports[0].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[0].usage == ESP_HID_USAGE_VENDOR);
|
||||
TEST_ASSERT(report_map->reports[0].value_len == 8);
|
||||
TEST_ASSERT(report_map->reports[1].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[1].report_type == ESP_HID_REPORT_TYPE_OUTPUT);
|
||||
TEST_ASSERT(report_map->reports[1].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[1].usage == ESP_HID_USAGE_VENDOR);
|
||||
TEST_ASSERT(report_map->reports[1].value_len == 8);
|
||||
TEST_ASSERT(report_map->reports[2].report_id == 1);
|
||||
TEST_ASSERT(report_map->reports[2].report_type == ESP_HID_REPORT_TYPE_FEATURE);
|
||||
TEST_ASSERT(report_map->reports[2].protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT);
|
||||
TEST_ASSERT(report_map->reports[2].usage == ESP_HID_USAGE_VENDOR);
|
||||
TEST_ASSERT(report_map->reports[2].value_len == 8);
|
||||
esp_hid_free_report_map(report_map);
|
||||
}
|
7
examples/bluetooth/esp_hid_device/CMakeLists.txt
Normal file
7
examples/bluetooth/esp_hid_device/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(SUPPORTED_TARGETS esp32)
|
||||
project(esp_hid_device)
|
8
examples/bluetooth/esp_hid_device/Makefile
Normal file
8
examples/bluetooth/esp_hid_device/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := esp_hid_device
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
5
examples/bluetooth/esp_hid_device/main/CMakeLists.txt
Normal file
5
examples/bluetooth/esp_hid_device/main/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
set(COMPONENT_SRCS "esp_hid_device_main.c"
|
||||
"esp_hid_gap.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
3
examples/bluetooth/esp_hid_device/main/component.mk
Normal file
3
examples/bluetooth/esp_hid_device/main/component.mk
Normal file
@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
385
examples/bluetooth/esp_hid_device/main/esp_hid_device_main.c
Normal file
385
examples/bluetooth/esp_hid_device/main/esp_hid_device_main.c
Normal file
@ -0,0 +1,385 @@
|
||||
/* This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
Unless required by applicable law or agreed to in writing, this software is
|
||||
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_bt.h"
|
||||
#include "esp_bt_defs.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "esp_gatts_api.h"
|
||||
#include "esp_gatt_defs.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
|
||||
#include "esp_hidd.h"
|
||||
#include "esp_hid_gap.h"
|
||||
|
||||
static const char *TAG = "HID_DEV_DEMO";
|
||||
|
||||
const unsigned char hidapiReportMap[] = { //8 bytes input, 8 bytes feature
|
||||
0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00)
|
||||
0x0A, 0x00, 0x01, // Usage (0x0100)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x01, // Report ID (1)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x09, 0x01, // Usage (0x01)
|
||||
0x82, 0x02, 0x01, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Buffered Bytes)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x09, 0x02, // Usage (0x02)
|
||||
0xB2, 0x02, 0x01, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x09, 0x03, // Usage (0x03)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
|
||||
// 38 bytes
|
||||
};
|
||||
|
||||
const unsigned char mediaReportMap[] = {
|
||||
0x05, 0x0C, // Usage Page (Consumer)
|
||||
0x09, 0x01, // Usage (Consumer Control)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x03, // Report ID (3)
|
||||
0x09, 0x02, // Usage (Numeric Key Pad)
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x0A, // Usage Maximum (0x0A)
|
||||
0x15, 0x01, // Logical Minimum (1)
|
||||
0x25, 0x0A, // Logical Maximum (10)
|
||||
0x75, 0x04, // Report Size (4)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0x05, 0x0C, // Usage Page (Consumer)
|
||||
0x09, 0x86, // Usage (Channel)
|
||||
0x15, 0xFF, // Logical Minimum (-1)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x75, 0x02, // Report Size (2)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x46, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,Null State)
|
||||
0x09, 0xE9, // Usage (Volume Increment)
|
||||
0x09, 0xEA, // Usage (Volume Decrement)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x09, 0xE2, // Usage (Mute)
|
||||
0x09, 0x30, // Usage (Power)
|
||||
0x09, 0x83, // Usage (Recall Last)
|
||||
0x09, 0x81, // Usage (Assign Selection)
|
||||
0x09, 0xB0, // Usage (Play)
|
||||
0x09, 0xB1, // Usage (Pause)
|
||||
0x09, 0xB2, // Usage (Record)
|
||||
0x09, 0xB3, // Usage (Fast Forward)
|
||||
0x09, 0xB4, // Usage (Rewind)
|
||||
0x09, 0xB5, // Usage (Scan Next Track)
|
||||
0x09, 0xB6, // Usage (Scan Previous Track)
|
||||
0x09, 0xB7, // Usage (Stop)
|
||||
0x15, 0x01, // Logical Minimum (1)
|
||||
0x25, 0x0C, // Logical Maximum (12)
|
||||
0x75, 0x04, // Report Size (4)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x09, 0x80, // Usage (Selection)
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (0x01)
|
||||
0x29, 0x03, // Usage Maximum (0x03)
|
||||
0x15, 0x01, // Logical Minimum (1)
|
||||
0x25, 0x03, // Logical Maximum (3)
|
||||
0x75, 0x02, // Report Size (2)
|
||||
0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
};
|
||||
|
||||
static esp_hid_raw_report_map_t report_maps[] = {
|
||||
{
|
||||
.data = hidapiReportMap,
|
||||
.len = sizeof(hidapiReportMap)
|
||||
},
|
||||
{
|
||||
.data = mediaReportMap,
|
||||
.len = sizeof(mediaReportMap)
|
||||
}
|
||||
};
|
||||
|
||||
static esp_hid_device_config_t hid_config = {
|
||||
.vendor_id = 0x16C0,
|
||||
.product_id = 0x05DF,
|
||||
.version = 0x0100,
|
||||
.device_name = "ESP BLE HID2",
|
||||
.manufacturer_name = "Espressif",
|
||||
.serial_number = "1234567890",
|
||||
.report_maps = report_maps,
|
||||
.report_maps_len = 2
|
||||
};
|
||||
|
||||
static esp_hidd_dev_t *hid_dev = NULL;
|
||||
static bool dev_connected = false;
|
||||
|
||||
#define HID_CC_RPT_MUTE 1
|
||||
#define HID_CC_RPT_POWER 2
|
||||
#define HID_CC_RPT_LAST 3
|
||||
#define HID_CC_RPT_ASSIGN_SEL 4
|
||||
#define HID_CC_RPT_PLAY 5
|
||||
#define HID_CC_RPT_PAUSE 6
|
||||
#define HID_CC_RPT_RECORD 7
|
||||
#define HID_CC_RPT_FAST_FWD 8
|
||||
#define HID_CC_RPT_REWIND 9
|
||||
#define HID_CC_RPT_SCAN_NEXT_TRK 10
|
||||
#define HID_CC_RPT_SCAN_PREV_TRK 11
|
||||
#define HID_CC_RPT_STOP 12
|
||||
|
||||
#define HID_CC_RPT_CHANNEL_UP 0x10
|
||||
#define HID_CC_RPT_CHANNEL_DOWN 0x30
|
||||
#define HID_CC_RPT_VOLUME_UP 0x40
|
||||
#define HID_CC_RPT_VOLUME_DOWN 0x80
|
||||
|
||||
// HID Consumer Control report bitmasks
|
||||
#define HID_CC_RPT_NUMERIC_BITS 0xF0
|
||||
#define HID_CC_RPT_CHANNEL_BITS 0xCF
|
||||
#define HID_CC_RPT_VOLUME_BITS 0x3F
|
||||
#define HID_CC_RPT_BUTTON_BITS 0xF0
|
||||
#define HID_CC_RPT_SELECTION_BITS 0xCF
|
||||
|
||||
// Macros for the HID Consumer Control 2-byte report
|
||||
#define HID_CC_RPT_SET_NUMERIC(s, x) (s)[0] &= HID_CC_RPT_NUMERIC_BITS; (s)[0] = (x)
|
||||
#define HID_CC_RPT_SET_CHANNEL(s, x) (s)[0] &= HID_CC_RPT_CHANNEL_BITS; (s)[0] |= ((x) & 0x03) << 4
|
||||
#define HID_CC_RPT_SET_VOLUME_UP(s) (s)[0] &= HID_CC_RPT_VOLUME_BITS; (s)[0] |= 0x40
|
||||
#define HID_CC_RPT_SET_VOLUME_DOWN(s) (s)[0] &= HID_CC_RPT_VOLUME_BITS; (s)[0] |= 0x80
|
||||
#define HID_CC_RPT_SET_BUTTON(s, x) (s)[1] &= HID_CC_RPT_BUTTON_BITS; (s)[1] |= (x)
|
||||
#define HID_CC_RPT_SET_SELECTION(s, x) (s)[1] &= HID_CC_RPT_SELECTION_BITS; (s)[1] |= ((x) & 0x03) << 4
|
||||
|
||||
// HID Consumer Usage IDs (subset of the codes available in the USB HID Usage Tables spec)
|
||||
#define HID_CONSUMER_POWER 48 // Power
|
||||
#define HID_CONSUMER_RESET 49 // Reset
|
||||
#define HID_CONSUMER_SLEEP 50 // Sleep
|
||||
|
||||
#define HID_CONSUMER_MENU 64 // Menu
|
||||
#define HID_CONSUMER_SELECTION 128 // Selection
|
||||
#define HID_CONSUMER_ASSIGN_SEL 129 // Assign Selection
|
||||
#define HID_CONSUMER_MODE_STEP 130 // Mode Step
|
||||
#define HID_CONSUMER_RECALL_LAST 131 // Recall Last
|
||||
#define HID_CONSUMER_QUIT 148 // Quit
|
||||
#define HID_CONSUMER_HELP 149 // Help
|
||||
#define HID_CONSUMER_CHANNEL_UP 156 // Channel Increment
|
||||
#define HID_CONSUMER_CHANNEL_DOWN 157 // Channel Decrement
|
||||
|
||||
#define HID_CONSUMER_PLAY 176 // Play
|
||||
#define HID_CONSUMER_PAUSE 177 // Pause
|
||||
#define HID_CONSUMER_RECORD 178 // Record
|
||||
#define HID_CONSUMER_FAST_FORWARD 179 // Fast Forward
|
||||
#define HID_CONSUMER_REWIND 180 // Rewind
|
||||
#define HID_CONSUMER_SCAN_NEXT_TRK 181 // Scan Next Track
|
||||
#define HID_CONSUMER_SCAN_PREV_TRK 182 // Scan Previous Track
|
||||
#define HID_CONSUMER_STOP 183 // Stop
|
||||
#define HID_CONSUMER_EJECT 184 // Eject
|
||||
#define HID_CONSUMER_RANDOM_PLAY 185 // Random Play
|
||||
#define HID_CONSUMER_SELECT_DISC 186 // Select Disk
|
||||
#define HID_CONSUMER_ENTER_DISC 187 // Enter Disc
|
||||
#define HID_CONSUMER_REPEAT 188 // Repeat
|
||||
#define HID_CONSUMER_STOP_EJECT 204 // Stop/Eject
|
||||
#define HID_CONSUMER_PLAY_PAUSE 205 // Play/Pause
|
||||
#define HID_CONSUMER_PLAY_SKIP 206 // Play/Skip
|
||||
|
||||
#define HID_CONSUMER_VOLUME 224 // Volume
|
||||
#define HID_CONSUMER_BALANCE 225 // Balance
|
||||
#define HID_CONSUMER_MUTE 226 // Mute
|
||||
#define HID_CONSUMER_BASS 227 // Bass
|
||||
#define HID_CONSUMER_VOLUME_UP 233 // Volume Increment
|
||||
#define HID_CONSUMER_VOLUME_DOWN 234 // Volume Decrement
|
||||
|
||||
#define HID_RPT_ID_CC_IN 3 // Consumer Control input report ID
|
||||
#define HID_CC_IN_RPT_LEN 2 // Consumer Control input report Len
|
||||
void esp_hidd_send_consumer_value(uint8_t key_cmd, bool key_pressed)
|
||||
{
|
||||
uint8_t buffer[HID_CC_IN_RPT_LEN] = {0, 0};
|
||||
if (key_pressed) {
|
||||
switch (key_cmd) {
|
||||
case HID_CONSUMER_CHANNEL_UP:
|
||||
HID_CC_RPT_SET_CHANNEL(buffer, HID_CC_RPT_CHANNEL_UP);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_CHANNEL_DOWN:
|
||||
HID_CC_RPT_SET_CHANNEL(buffer, HID_CC_RPT_CHANNEL_DOWN);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_VOLUME_UP:
|
||||
HID_CC_RPT_SET_VOLUME_UP(buffer);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_VOLUME_DOWN:
|
||||
HID_CC_RPT_SET_VOLUME_DOWN(buffer);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_MUTE:
|
||||
HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_MUTE);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_POWER:
|
||||
HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_POWER);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_RECALL_LAST:
|
||||
HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_LAST);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_ASSIGN_SEL:
|
||||
HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_ASSIGN_SEL);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_PLAY:
|
||||
HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_PLAY);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_PAUSE:
|
||||
HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_PAUSE);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_RECORD:
|
||||
HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_RECORD);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_FAST_FORWARD:
|
||||
HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_FAST_FWD);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_REWIND:
|
||||
HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_REWIND);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_SCAN_NEXT_TRK:
|
||||
HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_SCAN_NEXT_TRK);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_SCAN_PREV_TRK:
|
||||
HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_SCAN_PREV_TRK);
|
||||
break;
|
||||
|
||||
case HID_CONSUMER_STOP:
|
||||
HID_CC_RPT_SET_BUTTON(buffer, HID_CC_RPT_STOP);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
esp_hidd_dev_input_set(hid_dev, 1, HID_RPT_ID_CC_IN, buffer, HID_CC_IN_RPT_LEN);
|
||||
return;
|
||||
}
|
||||
|
||||
static void hidd_event_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data)
|
||||
{
|
||||
esp_hidd_event_t event = (esp_hidd_event_t)id;
|
||||
esp_hidd_event_data_t *param = (esp_hidd_event_data_t *)event_data;
|
||||
|
||||
switch (event) {
|
||||
case ESP_HIDD_START_EVENT: {
|
||||
ESP_LOGI(TAG, "START");
|
||||
esp_hid_ble_gap_adv_start();
|
||||
break;
|
||||
}
|
||||
case ESP_HIDD_CONNECT_EVENT: {
|
||||
ESP_LOGI(TAG, "CONNECT");
|
||||
dev_connected = true;//todo: this should be on auth_complete (in GAP)
|
||||
break;
|
||||
}
|
||||
case ESP_HIDD_PROTOCOL_MODE_EVENT: {
|
||||
ESP_LOGI(TAG, "PROTOCOL MODE[%u]: %s", param->protocol_mode.map_index, param->protocol_mode.protocol_mode ? "REPORT" : "BOOT");
|
||||
break;
|
||||
}
|
||||
case ESP_HIDD_CONTROL_EVENT: {
|
||||
ESP_LOGI(TAG, "CONTROL[%u]: %sSUSPEND", param->control.map_index, param->control.control ? "EXIT_" : "");
|
||||
break;
|
||||
}
|
||||
case ESP_HIDD_OUTPUT_EVENT: {
|
||||
ESP_LOGI(TAG, "OUTPUT[%u]: %8s ID: %2u, Len: %d, Data:", param->output.map_index, esp_hid_usage_str(param->output.usage), param->output.report_id, param->output.length);
|
||||
ESP_LOG_BUFFER_HEX(TAG, param->output.data, param->output.length);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDD_FEATURE_EVENT: {
|
||||
ESP_LOGI(TAG, "FEATURE[%u]: %8s ID: %2u, Len: %d, Data:", param->feature.map_index, esp_hid_usage_str(param->feature.usage), param->feature.report_id, param->feature.length);
|
||||
ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDD_DISCONNECT_EVENT: {
|
||||
ESP_LOGI(TAG, "DISCONNECT: %s", esp_hid_disconnect_reason_str(esp_hidd_dev_transport_get(param->disconnect.dev), param->disconnect.reason));
|
||||
dev_connected = false;
|
||||
esp_hid_ble_gap_adv_start();
|
||||
break;
|
||||
}
|
||||
case ESP_HIDD_STOP_EVENT: {
|
||||
ESP_LOGI(TAG, "STOP");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void hid_demo_task(void *pvParameters)
|
||||
{
|
||||
static bool send_volum_up = false;
|
||||
while (1) {
|
||||
if (dev_connected) {
|
||||
ESP_LOGI(TAG, "Send the volume");
|
||||
if (send_volum_up) {
|
||||
esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_UP, true);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_UP, false);
|
||||
} else {
|
||||
esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_DOWN, true);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_DOWN, false);
|
||||
}
|
||||
send_volum_up = !send_volum_up;
|
||||
}
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
ret = esp_hid_gap_init(ESP_BT_MODE_BTDM);
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_GENERIC, hid_config.device_name);
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
if ((ret = esp_ble_gatts_register_callback(esp_hidd_gatts_event_handler)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GATTS register callback failed: %d", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK( esp_hidd_dev_init(&hid_config, ESP_HID_TRANSPORT_BLE, hidd_event_callback, &hid_dev) );
|
||||
xTaskCreate(&hid_demo_task, "hid_task", 2048, NULL, 2, NULL);
|
||||
|
||||
}
|
807
examples/bluetooth/esp_hid_device/main/esp_hid_gap.c
Normal file
807
examples/bluetooth/esp_hid_device/main/esp_hid_gap.c
Normal file
@ -0,0 +1,807 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp_hid_gap.h"
|
||||
|
||||
static const char *TAG = "ESP_HID_GAP";
|
||||
|
||||
// uncomment to print all devices that were seen during a scan
|
||||
#define GAP_DBG_PRINTF(...) //printf(__VA_ARGS__)
|
||||
//static const char * gap_bt_prop_type_names[5] = {"","BDNAME","COD","RSSI","EIR"};
|
||||
|
||||
static esp_hid_scan_result_t *bt_scan_results = NULL;
|
||||
static size_t num_bt_scan_results = 0;
|
||||
|
||||
static esp_hid_scan_result_t *ble_scan_results = NULL;
|
||||
static size_t num_ble_scan_results = 0;
|
||||
|
||||
static xSemaphoreHandle bt_hidh_cb_semaphore = NULL;
|
||||
#define WAIT_BT_CB() xSemaphoreTake(bt_hidh_cb_semaphore, portMAX_DELAY)
|
||||
#define SEND_BT_CB() xSemaphoreGive(bt_hidh_cb_semaphore)
|
||||
|
||||
static xSemaphoreHandle ble_hidh_cb_semaphore = NULL;
|
||||
#define WAIT_BLE_CB() xSemaphoreTake(ble_hidh_cb_semaphore, portMAX_DELAY)
|
||||
#define SEND_BLE_CB() xSemaphoreGive(ble_hidh_cb_semaphore)
|
||||
|
||||
#define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(*a))
|
||||
|
||||
static const char *ble_gap_evt_names[] = { "ADV_DATA_SET_COMPLETE", "SCAN_RSP_DATA_SET_COMPLETE", "SCAN_PARAM_SET_COMPLETE", "SCAN_RESULT", "ADV_DATA_RAW_SET_COMPLETE", "SCAN_RSP_DATA_RAW_SET_COMPLETE", "ADV_START_COMPLETE", "SCAN_START_COMPLETE", "AUTH_CMPL", "KEY", "SEC_REQ", "PASSKEY_NOTIF", "PASSKEY_REQ", "OOB_REQ", "LOCAL_IR", "LOCAL_ER", "NC_REQ", "ADV_STOP_COMPLETE", "SCAN_STOP_COMPLETE", "SET_STATIC_RAND_ADDR", "UPDATE_CONN_PARAMS", "SET_PKT_LENGTH_COMPLETE", "SET_LOCAL_PRIVACY_COMPLETE", "REMOVE_BOND_DEV_COMPLETE", "CLEAR_BOND_DEV_COMPLETE", "GET_BOND_DEV_COMPLETE", "READ_RSSI_COMPLETE", "UPDATE_WHITELIST_COMPLETE"};
|
||||
static const char *bt_gap_evt_names[] = { "DISC_RES", "DISC_STATE_CHANGED", "RMT_SRVCS", "RMT_SRVC_REC", "AUTH_CMPL", "PIN_REQ", "CFM_REQ", "KEY_NOTIF", "KEY_REQ", "READ_RSSI_DELTA"};
|
||||
static const char *ble_addr_type_names[] = {"PUBLIC", "RANDOM", "RPA_PUBLIC", "RPA_RANDOM"};
|
||||
|
||||
const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type)
|
||||
{
|
||||
if (ble_addr_type > BLE_ADDR_TYPE_RPA_RANDOM) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return ble_addr_type_names[ble_addr_type];
|
||||
}
|
||||
|
||||
const char *ble_gap_evt_str(uint8_t event)
|
||||
{
|
||||
if (event >= SIZEOF_ARRAY(ble_gap_evt_names)) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return ble_gap_evt_names[event];
|
||||
}
|
||||
|
||||
const char *bt_gap_evt_str(uint8_t event)
|
||||
{
|
||||
if (event >= SIZEOF_ARRAY(bt_gap_evt_names)) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return bt_gap_evt_names[event];
|
||||
}
|
||||
|
||||
const char *esp_ble_key_type_str(esp_ble_key_type_t key_type)
|
||||
{
|
||||
const char *key_str = NULL;
|
||||
switch (key_type) {
|
||||
case ESP_LE_KEY_NONE:
|
||||
key_str = "ESP_LE_KEY_NONE";
|
||||
break;
|
||||
case ESP_LE_KEY_PENC:
|
||||
key_str = "ESP_LE_KEY_PENC";
|
||||
break;
|
||||
case ESP_LE_KEY_PID:
|
||||
key_str = "ESP_LE_KEY_PID";
|
||||
break;
|
||||
case ESP_LE_KEY_PCSRK:
|
||||
key_str = "ESP_LE_KEY_PCSRK";
|
||||
break;
|
||||
case ESP_LE_KEY_PLK:
|
||||
key_str = "ESP_LE_KEY_PLK";
|
||||
break;
|
||||
case ESP_LE_KEY_LLK:
|
||||
key_str = "ESP_LE_KEY_LLK";
|
||||
break;
|
||||
case ESP_LE_KEY_LENC:
|
||||
key_str = "ESP_LE_KEY_LENC";
|
||||
break;
|
||||
case ESP_LE_KEY_LID:
|
||||
key_str = "ESP_LE_KEY_LID";
|
||||
break;
|
||||
case ESP_LE_KEY_LCSRK:
|
||||
key_str = "ESP_LE_KEY_LCSRK";
|
||||
break;
|
||||
default:
|
||||
key_str = "INVALID BLE KEY TYPE";
|
||||
break;
|
||||
|
||||
}
|
||||
return key_str;
|
||||
}
|
||||
|
||||
void esp_hid_scan_results_free(esp_hid_scan_result_t *results)
|
||||
{
|
||||
esp_hid_scan_result_t *r = NULL;
|
||||
while (results) {
|
||||
r = results;
|
||||
results = results->next;
|
||||
if (r->name != NULL) {
|
||||
free((char *)r->name);
|
||||
}
|
||||
free(r);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_result_t *results)
|
||||
{
|
||||
esp_hid_scan_result_t *r = results;
|
||||
while (r) {
|
||||
if (memcmp(bda, r->bda, sizeof(esp_bd_addr_t)) == 0) {
|
||||
return r;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_bt_scan_result(esp_bd_addr_t bda, esp_bt_cod_t *cod, esp_bt_uuid_t *uuid, uint8_t *name, uint8_t name_len, int rssi)
|
||||
{
|
||||
esp_hid_scan_result_t *r = find_scan_result(bda, bt_scan_results);
|
||||
if (r) {
|
||||
//Some info may come later
|
||||
if (r->name == NULL && name && name_len) {
|
||||
char *name_s = (char *)malloc(name_len + 1);
|
||||
if (name_s == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc result name failed!");
|
||||
return;
|
||||
}
|
||||
memcpy(name_s, name, name_len);
|
||||
name_s[name_len] = 0;
|
||||
r->name = (const char *)name_s;
|
||||
}
|
||||
if (r->bt.uuid.len == 0 && uuid->len) {
|
||||
memcpy(&r->bt.uuid, uuid, sizeof(esp_bt_uuid_t));
|
||||
}
|
||||
if (rssi != 0) {
|
||||
r->rssi = rssi;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
r = (esp_hid_scan_result_t *)malloc(sizeof(esp_hid_scan_result_t));
|
||||
if (r == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc bt_hidh_scan_result_t failed!");
|
||||
return;
|
||||
}
|
||||
r->transport = ESP_HID_TRANSPORT_BT;
|
||||
memcpy(r->bda, bda, sizeof(esp_bd_addr_t));
|
||||
memcpy(&r->bt.cod, cod, sizeof(esp_bt_cod_t));
|
||||
memcpy(&r->bt.uuid, uuid, sizeof(esp_bt_uuid_t));
|
||||
r->usage = esp_hid_usage_from_cod((uint32_t)cod);
|
||||
r->rssi = rssi;
|
||||
r->name = NULL;
|
||||
if (name_len && name) {
|
||||
char *name_s = (char *)malloc(name_len + 1);
|
||||
if (name_s == NULL) {
|
||||
free(r);
|
||||
ESP_LOGE(TAG, "Malloc result name failed!");
|
||||
return;
|
||||
}
|
||||
memcpy(name_s, name, name_len);
|
||||
name_s[name_len] = 0;
|
||||
r->name = (const char *)name_s;
|
||||
}
|
||||
r->next = bt_scan_results;
|
||||
bt_scan_results = r;
|
||||
num_bt_scan_results++;
|
||||
}
|
||||
|
||||
static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type, uint16_t appearance, uint8_t *name, uint8_t name_len, int rssi)
|
||||
{
|
||||
if (find_scan_result(bda, ble_scan_results)) {
|
||||
ESP_LOGW(TAG, "Result already exists!");
|
||||
return;
|
||||
}
|
||||
esp_hid_scan_result_t *r = (esp_hid_scan_result_t *)malloc(sizeof(esp_hid_scan_result_t));
|
||||
if (r == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc ble_hidh_scan_result_t failed!");
|
||||
return;
|
||||
}
|
||||
r->transport = ESP_HID_TRANSPORT_BLE;
|
||||
memcpy(r->bda, bda, sizeof(esp_bd_addr_t));
|
||||
r->ble.appearance = appearance;
|
||||
r->ble.addr_type = addr_type;
|
||||
r->usage = esp_hid_usage_from_appearance(appearance);
|
||||
r->rssi = rssi;
|
||||
r->name = NULL;
|
||||
if (name_len && name) {
|
||||
char *name_s = (char *)malloc(name_len + 1);
|
||||
if (name_s == NULL) {
|
||||
free(r);
|
||||
ESP_LOGE(TAG, "Malloc result name failed!");
|
||||
return;
|
||||
}
|
||||
memcpy(name_s, name, name_len);
|
||||
name_s[name_len] = 0;
|
||||
r->name = (const char *)name_s;
|
||||
}
|
||||
r->next = ble_scan_results;
|
||||
ble_scan_results = r;
|
||||
num_ble_scan_results++;
|
||||
}
|
||||
|
||||
void print_uuid(esp_bt_uuid_t *uuid)
|
||||
{
|
||||
if (uuid->len == ESP_UUID_LEN_16) {
|
||||
GAP_DBG_PRINTF("UUID16: 0x%04x", uuid->uuid.uuid16);
|
||||
} else if (uuid->len == ESP_UUID_LEN_32) {
|
||||
GAP_DBG_PRINTF("UUID32: 0x%08x", uuid->uuid.uuid32);
|
||||
} else if (uuid->len == ESP_UUID_LEN_128) {
|
||||
GAP_DBG_PRINTF("UUID128: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x", uuid->uuid.uuid128[0],
|
||||
uuid->uuid.uuid128[1], uuid->uuid.uuid128[2], uuid->uuid.uuid128[3],
|
||||
uuid->uuid.uuid128[4], uuid->uuid.uuid128[5], uuid->uuid.uuid128[6],
|
||||
uuid->uuid.uuid128[7], uuid->uuid.uuid128[8], uuid->uuid.uuid128[9],
|
||||
uuid->uuid.uuid128[10], uuid->uuid.uuid128[11], uuid->uuid.uuid128[12],
|
||||
uuid->uuid.uuid128[13], uuid->uuid.uuid128[14], uuid->uuid.uuid128[15]);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_bt_device_result(struct disc_res_param *disc_res)
|
||||
{
|
||||
GAP_DBG_PRINTF("BT : " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(disc_res->bda));
|
||||
uint32_t codv = 0;
|
||||
esp_bt_cod_t *cod = (esp_bt_cod_t *)&codv;
|
||||
int8_t rssi = 0;
|
||||
uint8_t *name = NULL;
|
||||
uint8_t name_len = 0;
|
||||
esp_bt_uuid_t uuid;
|
||||
|
||||
uuid.len = ESP_UUID_LEN_16;
|
||||
uuid.uuid.uuid16 = 0;
|
||||
|
||||
for (int i = 0; i < disc_res->num_prop; i++) {
|
||||
esp_bt_gap_dev_prop_t *prop = &disc_res->prop[i];
|
||||
if (prop->type != ESP_BT_GAP_DEV_PROP_EIR) {
|
||||
GAP_DBG_PRINTF(", %s: ", gap_bt_prop_type_names[prop->type]);
|
||||
}
|
||||
if (prop->type == ESP_BT_GAP_DEV_PROP_BDNAME) {
|
||||
name = (uint8_t *)prop->val;
|
||||
name_len = strlen((const char *)name);
|
||||
GAP_DBG_PRINTF("%s", (const char *)name);
|
||||
} else if (prop->type == ESP_BT_GAP_DEV_PROP_RSSI) {
|
||||
rssi = *((int8_t *)prop->val);
|
||||
GAP_DBG_PRINTF("%d", rssi);
|
||||
} else if (prop->type == ESP_BT_GAP_DEV_PROP_COD) {
|
||||
memcpy(&codv, prop->val, sizeof(uint32_t));
|
||||
GAP_DBG_PRINTF("major: %s, minor: %d, service: 0x%03x", esp_hid_cod_major_str(cod->major), cod->minor, cod->service);
|
||||
} else if (prop->type == ESP_BT_GAP_DEV_PROP_EIR) {
|
||||
uint8_t len = 0;
|
||||
uint8_t *data = 0;
|
||||
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_CMPL_16BITS_UUID, &len);
|
||||
if (data == NULL) {
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_INCMPL_16BITS_UUID, &len);
|
||||
}
|
||||
if (data && len == ESP_UUID_LEN_16) {
|
||||
uuid.len = ESP_UUID_LEN_16;
|
||||
uuid.uuid.uuid16 = data[0] + (data[1] << 8);
|
||||
GAP_DBG_PRINTF(", "); print_uuid(&uuid);
|
||||
continue;
|
||||
}
|
||||
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_CMPL_32BITS_UUID, &len);
|
||||
if (data == NULL) {
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_INCMPL_32BITS_UUID, &len);
|
||||
}
|
||||
if (data && len == ESP_UUID_LEN_32) {
|
||||
uuid.len = len;
|
||||
memcpy(&uuid.uuid.uuid32, data, sizeof(uint32_t));
|
||||
GAP_DBG_PRINTF(", "); print_uuid(&uuid);
|
||||
continue;
|
||||
}
|
||||
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_CMPL_128BITS_UUID, &len);
|
||||
if (data == NULL) {
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_INCMPL_128BITS_UUID, &len);
|
||||
}
|
||||
if (data && len == ESP_UUID_LEN_128) {
|
||||
uuid.len = len;
|
||||
memcpy(uuid.uuid.uuid128, (uint8_t *)data, len);
|
||||
GAP_DBG_PRINTF(", "); print_uuid(&uuid);
|
||||
continue;
|
||||
}
|
||||
|
||||
//try to find a name
|
||||
if (name == NULL) {
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &len);
|
||||
if (data == NULL) {
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &len);
|
||||
}
|
||||
if (data && len) {
|
||||
name = data;
|
||||
name_len = len;
|
||||
GAP_DBG_PRINTF(", NAME: ");
|
||||
for (int x = 0; x < len; x++) {
|
||||
GAP_DBG_PRINTF("%c", (char)data[x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GAP_DBG_PRINTF("\n");
|
||||
|
||||
if (cod->major == ESP_BT_COD_MAJOR_DEV_PERIPHERAL || (find_scan_result(disc_res->bda, bt_scan_results) != NULL)) {
|
||||
add_bt_scan_result(disc_res->bda, cod, &uuid, name, name_len, rssi);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_ble_device_result(struct ble_scan_result_evt_param *scan_rst)
|
||||
{
|
||||
|
||||
uint16_t uuid = 0;
|
||||
uint16_t appearance = 0;
|
||||
char name[64] = {0};
|
||||
|
||||
uint8_t uuid_len = 0;
|
||||
uint8_t *uuid_d = esp_ble_resolve_adv_data(scan_rst->ble_adv, ESP_BLE_AD_TYPE_16SRV_CMPL, &uuid_len);
|
||||
if (uuid_d != NULL && uuid_len) {
|
||||
uuid = uuid_d[0] + (uuid_d[1] << 8);
|
||||
}
|
||||
|
||||
uint8_t appearance_len = 0;
|
||||
uint8_t *appearance_d = esp_ble_resolve_adv_data(scan_rst->ble_adv, ESP_BLE_AD_TYPE_APPEARANCE, &appearance_len);
|
||||
if (appearance_d != NULL && appearance_len) {
|
||||
appearance = appearance_d[0] + (appearance_d[1] << 8);
|
||||
}
|
||||
|
||||
uint8_t adv_name_len = 0;
|
||||
uint8_t *adv_name = esp_ble_resolve_adv_data(scan_rst->ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
|
||||
|
||||
if (adv_name == NULL) {
|
||||
adv_name = esp_ble_resolve_adv_data(scan_rst->ble_adv, ESP_BLE_AD_TYPE_NAME_SHORT, &adv_name_len);
|
||||
}
|
||||
|
||||
if (adv_name != NULL && adv_name_len) {
|
||||
memcpy(name, adv_name, adv_name_len);
|
||||
name[adv_name_len] = 0;
|
||||
}
|
||||
|
||||
GAP_DBG_PRINTF("BLE: " ESP_BD_ADDR_STR ", ", ESP_BD_ADDR_HEX(scan_rst->bda));
|
||||
GAP_DBG_PRINTF("RSSI: %d, ", scan_rst->rssi);
|
||||
GAP_DBG_PRINTF("UUID: 0x%04x, ", uuid);
|
||||
GAP_DBG_PRINTF("APPEARANCE: 0x%04x, ", appearance);
|
||||
GAP_DBG_PRINTF("ADDR_TYPE: '%s'", ble_addr_type_str(scan_rst->ble_addr_type));
|
||||
if (adv_name_len) {
|
||||
GAP_DBG_PRINTF(", NAME: '%s'", name);
|
||||
}
|
||||
GAP_DBG_PRINTF("\n");
|
||||
|
||||
if (uuid == ESP_GATT_UUID_HID_SVC) {
|
||||
add_ble_scan_result(scan_rst->bda, scan_rst->ble_addr_type, appearance, adv_name, adv_name_len, scan_rst->rssi);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* BT GAP
|
||||
* */
|
||||
|
||||
static void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
|
||||
{
|
||||
static bool scan_running = false;
|
||||
static bool scan_wait_stop = false;
|
||||
switch (event) {
|
||||
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: {
|
||||
ESP_LOGV(TAG, "BT GAP DISC_STATE %s", (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) ? "START" : "STOP");
|
||||
if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) {
|
||||
scan_running = true;
|
||||
scan_wait_stop = true;
|
||||
} else if (scan_wait_stop) {
|
||||
scan_wait_stop = false;
|
||||
} else if (scan_running) {
|
||||
scan_running = false;
|
||||
SEND_BT_CB();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_BT_GAP_DISC_RES_EVT: {
|
||||
handle_bt_device_result(¶m->disc_res);
|
||||
break;
|
||||
}
|
||||
case ESP_BT_GAP_KEY_NOTIF_EVT:
|
||||
ESP_LOGI(TAG, "BT GAP KEY_NOTIF passkey:%d", param->key_notif.passkey);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGV(TAG, "BT GAP EVENT %s", bt_gap_evt_str(event));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t init_bt_gap(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
|
||||
esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
|
||||
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
|
||||
/*
|
||||
* Set default parameters for Legacy Pairing
|
||||
* Use fixed pin code
|
||||
*/
|
||||
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED;
|
||||
esp_bt_pin_code_t pin_code;
|
||||
pin_code[0] = '1';
|
||||
pin_code[1] = '2';
|
||||
pin_code[2] = '3';
|
||||
pin_code[3] = '4';
|
||||
esp_bt_gap_set_pin(pin_type, 4, pin_code);
|
||||
|
||||
if ((ret = esp_bt_gap_register_callback(bt_gap_event_handler)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_bt_gap_register_callback failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Allow BT devices to connect back to us
|
||||
if ((ret = esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_NON_DISCOVERABLE)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_bt_gap_set_scan_mode failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t start_bt_scan(uint32_t seconds)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if ((ret = esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, (int)(seconds / 1.28), 0)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_bt_gap_start_discovery failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* BLE GAP
|
||||
* */
|
||||
|
||||
static void ble_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
/*
|
||||
* SCAN
|
||||
* */
|
||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: {
|
||||
ESP_LOGV(TAG, "BLE GAP EVENT SCAN_PARAM_SET_COMPLETE");
|
||||
SEND_BLE_CB();
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT: {
|
||||
esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
|
||||
switch (scan_result->scan_rst.search_evt) {
|
||||
case ESP_GAP_SEARCH_INQ_RES_EVT: {
|
||||
handle_ble_device_result(&scan_result->scan_rst);
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_SEARCH_INQ_CMPL_EVT:
|
||||
ESP_LOGV(TAG, "BLE GAP EVENT SCAN DONE: %d", scan_result->scan_rst.num_resps);
|
||||
SEND_BLE_CB();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: {
|
||||
ESP_LOGV(TAG, "BLE GAP EVENT SCAN CANCELED");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* ADVERTISEMENT
|
||||
* */
|
||||
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
|
||||
ESP_LOGV(TAG, "BLE GAP ADV_DATA_SET_COMPLETE");
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
|
||||
ESP_LOGV(TAG, "BLE GAP ADV_START_COMPLETE");
|
||||
break;
|
||||
|
||||
/*
|
||||
* AUTHENTICATION
|
||||
* */
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT:
|
||||
if (!param->ble_security.auth_cmpl.success) {
|
||||
ESP_LOGE(TAG, "BLE GAP AUTH ERROR: 0x%x", param->ble_security.auth_cmpl.fail_reason);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "BLE GAP AUTH SUCCESS");
|
||||
}
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_KEY_EVT: //shows the ble key info share with peer device to the user.
|
||||
ESP_LOGI(TAG, "BLE GAP KEY type = %s", esp_ble_key_type_str(param->ble_security.ble_key.key_type));
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: // ESP_IO_CAP_OUT
|
||||
// The app will receive this evt when the IO has Output capability and the peer device IO has Input capability.
|
||||
// Show the passkey number to the user to input it in the peer device.
|
||||
ESP_LOGI(TAG, "BLE GAP PASSKEY_NOTIF passkey:%d", param->ble_security.key_notif.passkey);
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_NC_REQ_EVT: // ESP_IO_CAP_IO
|
||||
// The app will receive this event when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability.
|
||||
// show the passkey number to the user to confirm it with the number displayed by peer device.
|
||||
ESP_LOGI(TAG, "BLE GAP NC_REQ passkey:%d", param->ble_security.key_notif.passkey);
|
||||
esp_ble_confirm_reply(param->ble_security.key_notif.bd_addr, true);
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_PASSKEY_REQ_EVT: // ESP_IO_CAP_IN
|
||||
// The app will receive this evt when the IO has Input capability and the peer device IO has Output capability.
|
||||
// See the passkey number on the peer device and send it back.
|
||||
ESP_LOGI(TAG, "BLE GAP PASSKEY_REQ");
|
||||
//esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, 1234);
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_SEC_REQ_EVT:
|
||||
ESP_LOGI(TAG, "BLE GAP SEC_REQ");
|
||||
// Send the positive(true) security response to the peer device to accept the security request.
|
||||
// If not accept the security request, should send the security response with negative(false) accept value.
|
||||
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGV(TAG, "BLE GAP EVENT %s", ble_gap_evt_str(event));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t init_ble_gap(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
if ((ret = esp_ble_gap_register_callback(ble_gap_event_handler)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_ble_scan_params_t hid_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0x50,
|
||||
.scan_window = 0x30,
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_ENABLE,
|
||||
};
|
||||
|
||||
static esp_err_t start_ble_scan(uint32_t seconds)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if ((ret = esp_ble_gap_set_scan_params(&hid_scan_params)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_set_scan_params failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
WAIT_BLE_CB();
|
||||
|
||||
if ((ret = esp_ble_gap_start_scanning(seconds)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_start_scanning failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name)
|
||||
{
|
||||
|
||||
esp_err_t ret;
|
||||
|
||||
const uint8_t hidd_service_uuid128[] = {
|
||||
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x12, 0x18, 0x00, 0x00,
|
||||
};
|
||||
|
||||
esp_ble_adv_data_t ble_adv_data = {
|
||||
.set_scan_rsp = false,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
|
||||
.max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
|
||||
.appearance = appearance,
|
||||
.manufacturer_len = 0,
|
||||
.p_manufacturer_data = NULL,
|
||||
.service_data_len = 0,
|
||||
.p_service_data = NULL,
|
||||
.service_uuid_len = sizeof(hidd_service_uuid128),
|
||||
.p_service_uuid = (uint8_t *)hidd_service_uuid128,
|
||||
.flag = 0x6,
|
||||
};
|
||||
|
||||
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND;
|
||||
//esp_ble_io_cap_t iocap = ESP_IO_CAP_OUT;//you have to enter the key on the host
|
||||
//esp_ble_io_cap_t iocap = ESP_IO_CAP_IN;//you have to enter the key on the device
|
||||
esp_ble_io_cap_t iocap = ESP_IO_CAP_IO;//you have to agree that key matches on both
|
||||
//esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;//device is not capable of input or output, unsecure
|
||||
uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
uint8_t key_size = 16; //the key size should be 7~16 bytes
|
||||
uint32_t passkey = 1234;//ESP_IO_CAP_OUT
|
||||
|
||||
if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, 1)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_security_param AUTHEN_REQ_MODE failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, 1)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_security_param IOCAP_MODE failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, 1)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_security_param SET_INIT_KEY failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, 1)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_security_param SET_RSP_KEY failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, 1)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_security_param MAX_KEY_SIZE failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t))) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_security_param SET_STATIC_PASSKEY failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_set_device_name(device_name)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_device_name failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_config_adv_data(&ble_adv_data)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP config_adv_data failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_hid_ble_gap_adv_start(void)
|
||||
{
|
||||
static esp_ble_adv_params_t hidd_adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x30,
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
};
|
||||
return esp_ble_gap_start_advertising(&hidd_adv_params);
|
||||
}
|
||||
|
||||
/*
|
||||
* CONTROLLER INIT
|
||||
* */
|
||||
|
||||
static esp_err_t init_low_level(uint8_t mode)
|
||||
{
|
||||
esp_err_t ret;
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
if (mode & ESP_BT_MODE_CLASSIC_BT) {
|
||||
bt_cfg.mode = mode;
|
||||
bt_cfg.bt_max_acl_conn = 3;
|
||||
bt_cfg.bt_max_sync_conn = 3;
|
||||
} else {
|
||||
ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_bt_controller_mem_release failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = esp_bt_controller_init(&bt_cfg);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_bt_controller_init failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = esp_bt_controller_enable(mode);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_bt_controller_enable failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = esp_bluedroid_init();
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = esp_bluedroid_enable();
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mode & ESP_BT_MODE_CLASSIC_BT) {
|
||||
ret = init_bt_gap();
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode & ESP_BT_MODE_BLE) {
|
||||
ret = init_ble_gap();
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
esp_err_t esp_hid_gap_init(uint8_t mode)
|
||||
{
|
||||
esp_err_t ret;
|
||||
if (!mode || mode > ESP_BT_MODE_BTDM) {
|
||||
ESP_LOGE(TAG, "Invalid mode given!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (bt_hidh_cb_semaphore != NULL) {
|
||||
ESP_LOGE(TAG, "Already initialised");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
bt_hidh_cb_semaphore = xSemaphoreCreateBinary();
|
||||
if (bt_hidh_cb_semaphore == NULL) {
|
||||
ESP_LOGE(TAG, "xSemaphoreCreateMutex failed!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ble_hidh_cb_semaphore = xSemaphoreCreateBinary();
|
||||
if (ble_hidh_cb_semaphore == NULL) {
|
||||
ESP_LOGE(TAG, "xSemaphoreCreateMutex failed!");
|
||||
vSemaphoreDelete(bt_hidh_cb_semaphore);
|
||||
bt_hidh_cb_semaphore = NULL;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret = init_low_level(mode);
|
||||
if (ret != ESP_OK) {
|
||||
vSemaphoreDelete(bt_hidh_cb_semaphore);
|
||||
bt_hidh_cb_semaphore = NULL;
|
||||
vSemaphoreDelete(ble_hidh_cb_semaphore);
|
||||
ble_hidh_cb_semaphore = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_result_t **results)
|
||||
{
|
||||
if (num_bt_scan_results || bt_scan_results || num_ble_scan_results || ble_scan_results) {
|
||||
ESP_LOGE(TAG, "There are old scan results. Free them first!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (start_ble_scan(seconds) == ESP_OK) {
|
||||
if (start_bt_scan(seconds) == ESP_OK) {
|
||||
WAIT_BT_CB();
|
||||
}
|
||||
WAIT_BLE_CB();
|
||||
} else {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
*num_results = num_bt_scan_results + num_ble_scan_results;
|
||||
*results = bt_scan_results;
|
||||
if (num_bt_scan_results) {
|
||||
while (bt_scan_results->next != NULL) {
|
||||
bt_scan_results = bt_scan_results->next;
|
||||
}
|
||||
bt_scan_results->next = ble_scan_results;
|
||||
} else {
|
||||
*results = ble_scan_results;
|
||||
}
|
||||
|
||||
num_bt_scan_results = 0;
|
||||
bt_scan_results = NULL;
|
||||
num_ble_scan_results = 0;
|
||||
ble_scan_results = NULL;
|
||||
return ESP_OK;
|
||||
}
|
68
examples/bluetooth/esp_hid_device/main/esp_hid_gap.h
Normal file
68
examples/bluetooth/esp_hid_device/main/esp_hid_gap.h
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef _ESP_HID_GAP_H_
|
||||
#define _ESP_HID_GAP_H_
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_bt.h"
|
||||
#include "esp_bt_defs.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_gattc_api.h"
|
||||
#include "esp_gatt_defs.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
#include "esp_hid_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct esp_hidh_scan_result_s {
|
||||
struct esp_hidh_scan_result_s *next;
|
||||
|
||||
esp_bd_addr_t bda;
|
||||
const char *name;
|
||||
int8_t rssi;
|
||||
esp_hid_usage_t usage;
|
||||
esp_hid_transport_t transport; //BT, BLE or USB
|
||||
union {
|
||||
struct {
|
||||
esp_bt_cod_t cod;
|
||||
esp_bt_uuid_t uuid;
|
||||
} bt;
|
||||
struct {
|
||||
esp_ble_addr_type_t addr_type;
|
||||
uint16_t appearance;
|
||||
} ble;
|
||||
};
|
||||
} esp_hid_scan_result_t;
|
||||
|
||||
esp_err_t esp_hid_gap_init(uint8_t mode);
|
||||
esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_result_t **results);
|
||||
void esp_hid_scan_results_free(esp_hid_scan_result_t *results);
|
||||
|
||||
esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name);
|
||||
esp_err_t esp_hid_ble_gap_adv_start(void);
|
||||
|
||||
void print_uuid(esp_bt_uuid_t *uuid);
|
||||
const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ESP_HIDH_GAP_H_ */
|
6
examples/bluetooth/esp_hid_device/sdkconfig.defaults
Normal file
6
examples/bluetooth/esp_hid_device/sdkconfig.defaults
Normal file
@ -0,0 +1,6 @@
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=y
|
||||
CONFIG_BTDM_CTRL_HCI_MODE_VHCI=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
CONFIG_BT_CLASSIC_ENABLED=y
|
||||
CONFIG_BT_HID_HOST_ENABLED=y
|
7
examples/bluetooth/esp_hid_host/CMakeLists.txt
Normal file
7
examples/bluetooth/esp_hid_host/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
set(SUPPORTED_TARGETS esp32)
|
||||
project(esp_hid_host)
|
8
examples/bluetooth/esp_hid_host/Makefile
Normal file
8
examples/bluetooth/esp_hid_host/Makefile
Normal file
@ -0,0 +1,8 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := esp_hid_host
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
5
examples/bluetooth/esp_hid_host/main/CMakeLists.txt
Normal file
5
examples/bluetooth/esp_hid_host/main/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
set(COMPONENT_SRCS "esp_hid_host_main.c"
|
||||
"esp_hid_gap.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
3
examples/bluetooth/esp_hid_host/main/component.mk
Normal file
3
examples/bluetooth/esp_hid_host/main/component.mk
Normal file
@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
807
examples/bluetooth/esp_hid_host/main/esp_hid_gap.c
Normal file
807
examples/bluetooth/esp_hid_host/main/esp_hid_gap.c
Normal file
@ -0,0 +1,807 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp_hid_gap.h"
|
||||
|
||||
static const char *TAG = "ESP_HID_GAP";
|
||||
|
||||
// uncomment to print all devices that were seen during a scan
|
||||
#define GAP_DBG_PRINTF(...) //printf(__VA_ARGS__)
|
||||
//static const char * gap_bt_prop_type_names[5] = {"","BDNAME","COD","RSSI","EIR"};
|
||||
|
||||
static esp_hid_scan_result_t *bt_scan_results = NULL;
|
||||
static size_t num_bt_scan_results = 0;
|
||||
|
||||
static esp_hid_scan_result_t *ble_scan_results = NULL;
|
||||
static size_t num_ble_scan_results = 0;
|
||||
|
||||
static xSemaphoreHandle bt_hidh_cb_semaphore = NULL;
|
||||
#define WAIT_BT_CB() xSemaphoreTake(bt_hidh_cb_semaphore, portMAX_DELAY)
|
||||
#define SEND_BT_CB() xSemaphoreGive(bt_hidh_cb_semaphore)
|
||||
|
||||
static xSemaphoreHandle ble_hidh_cb_semaphore = NULL;
|
||||
#define WAIT_BLE_CB() xSemaphoreTake(ble_hidh_cb_semaphore, portMAX_DELAY)
|
||||
#define SEND_BLE_CB() xSemaphoreGive(ble_hidh_cb_semaphore)
|
||||
|
||||
#define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(*a))
|
||||
|
||||
static const char *ble_gap_evt_names[] = { "ADV_DATA_SET_COMPLETE", "SCAN_RSP_DATA_SET_COMPLETE", "SCAN_PARAM_SET_COMPLETE", "SCAN_RESULT", "ADV_DATA_RAW_SET_COMPLETE", "SCAN_RSP_DATA_RAW_SET_COMPLETE", "ADV_START_COMPLETE", "SCAN_START_COMPLETE", "AUTH_CMPL", "KEY", "SEC_REQ", "PASSKEY_NOTIF", "PASSKEY_REQ", "OOB_REQ", "LOCAL_IR", "LOCAL_ER", "NC_REQ", "ADV_STOP_COMPLETE", "SCAN_STOP_COMPLETE", "SET_STATIC_RAND_ADDR", "UPDATE_CONN_PARAMS", "SET_PKT_LENGTH_COMPLETE", "SET_LOCAL_PRIVACY_COMPLETE", "REMOVE_BOND_DEV_COMPLETE", "CLEAR_BOND_DEV_COMPLETE", "GET_BOND_DEV_COMPLETE", "READ_RSSI_COMPLETE", "UPDATE_WHITELIST_COMPLETE"};
|
||||
static const char *bt_gap_evt_names[] = { "DISC_RES", "DISC_STATE_CHANGED", "RMT_SRVCS", "RMT_SRVC_REC", "AUTH_CMPL", "PIN_REQ", "CFM_REQ", "KEY_NOTIF", "KEY_REQ", "READ_RSSI_DELTA"};
|
||||
static const char *ble_addr_type_names[] = {"PUBLIC", "RANDOM", "RPA_PUBLIC", "RPA_RANDOM"};
|
||||
|
||||
const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type)
|
||||
{
|
||||
if (ble_addr_type > BLE_ADDR_TYPE_RPA_RANDOM) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return ble_addr_type_names[ble_addr_type];
|
||||
}
|
||||
|
||||
const char *ble_gap_evt_str(uint8_t event)
|
||||
{
|
||||
if (event >= SIZEOF_ARRAY(ble_gap_evt_names)) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return ble_gap_evt_names[event];
|
||||
}
|
||||
|
||||
const char *bt_gap_evt_str(uint8_t event)
|
||||
{
|
||||
if (event >= SIZEOF_ARRAY(bt_gap_evt_names)) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return bt_gap_evt_names[event];
|
||||
}
|
||||
|
||||
const char *esp_ble_key_type_str(esp_ble_key_type_t key_type)
|
||||
{
|
||||
const char *key_str = NULL;
|
||||
switch (key_type) {
|
||||
case ESP_LE_KEY_NONE:
|
||||
key_str = "ESP_LE_KEY_NONE";
|
||||
break;
|
||||
case ESP_LE_KEY_PENC:
|
||||
key_str = "ESP_LE_KEY_PENC";
|
||||
break;
|
||||
case ESP_LE_KEY_PID:
|
||||
key_str = "ESP_LE_KEY_PID";
|
||||
break;
|
||||
case ESP_LE_KEY_PCSRK:
|
||||
key_str = "ESP_LE_KEY_PCSRK";
|
||||
break;
|
||||
case ESP_LE_KEY_PLK:
|
||||
key_str = "ESP_LE_KEY_PLK";
|
||||
break;
|
||||
case ESP_LE_KEY_LLK:
|
||||
key_str = "ESP_LE_KEY_LLK";
|
||||
break;
|
||||
case ESP_LE_KEY_LENC:
|
||||
key_str = "ESP_LE_KEY_LENC";
|
||||
break;
|
||||
case ESP_LE_KEY_LID:
|
||||
key_str = "ESP_LE_KEY_LID";
|
||||
break;
|
||||
case ESP_LE_KEY_LCSRK:
|
||||
key_str = "ESP_LE_KEY_LCSRK";
|
||||
break;
|
||||
default:
|
||||
key_str = "INVALID BLE KEY TYPE";
|
||||
break;
|
||||
|
||||
}
|
||||
return key_str;
|
||||
}
|
||||
|
||||
void esp_hid_scan_results_free(esp_hid_scan_result_t *results)
|
||||
{
|
||||
esp_hid_scan_result_t *r = NULL;
|
||||
while (results) {
|
||||
r = results;
|
||||
results = results->next;
|
||||
if (r->name != NULL) {
|
||||
free((char *)r->name);
|
||||
}
|
||||
free(r);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_result_t *results)
|
||||
{
|
||||
esp_hid_scan_result_t *r = results;
|
||||
while (r) {
|
||||
if (memcmp(bda, r->bda, sizeof(esp_bd_addr_t)) == 0) {
|
||||
return r;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_bt_scan_result(esp_bd_addr_t bda, esp_bt_cod_t *cod, esp_bt_uuid_t *uuid, uint8_t *name, uint8_t name_len, int rssi)
|
||||
{
|
||||
esp_hid_scan_result_t *r = find_scan_result(bda, bt_scan_results);
|
||||
if (r) {
|
||||
//Some info may come later
|
||||
if (r->name == NULL && name && name_len) {
|
||||
char *name_s = (char *)malloc(name_len + 1);
|
||||
if (name_s == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc result name failed!");
|
||||
return;
|
||||
}
|
||||
memcpy(name_s, name, name_len);
|
||||
name_s[name_len] = 0;
|
||||
r->name = (const char *)name_s;
|
||||
}
|
||||
if (r->bt.uuid.len == 0 && uuid->len) {
|
||||
memcpy(&r->bt.uuid, uuid, sizeof(esp_bt_uuid_t));
|
||||
}
|
||||
if (rssi != 0) {
|
||||
r->rssi = rssi;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
r = (esp_hid_scan_result_t *)malloc(sizeof(esp_hid_scan_result_t));
|
||||
if (r == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc bt_hidh_scan_result_t failed!");
|
||||
return;
|
||||
}
|
||||
r->transport = ESP_HID_TRANSPORT_BT;
|
||||
memcpy(r->bda, bda, sizeof(esp_bd_addr_t));
|
||||
memcpy(&r->bt.cod, cod, sizeof(esp_bt_cod_t));
|
||||
memcpy(&r->bt.uuid, uuid, sizeof(esp_bt_uuid_t));
|
||||
r->usage = esp_hid_usage_from_cod((uint32_t)cod);
|
||||
r->rssi = rssi;
|
||||
r->name = NULL;
|
||||
if (name_len && name) {
|
||||
char *name_s = (char *)malloc(name_len + 1);
|
||||
if (name_s == NULL) {
|
||||
free(r);
|
||||
ESP_LOGE(TAG, "Malloc result name failed!");
|
||||
return;
|
||||
}
|
||||
memcpy(name_s, name, name_len);
|
||||
name_s[name_len] = 0;
|
||||
r->name = (const char *)name_s;
|
||||
}
|
||||
r->next = bt_scan_results;
|
||||
bt_scan_results = r;
|
||||
num_bt_scan_results++;
|
||||
}
|
||||
|
||||
static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type, uint16_t appearance, uint8_t *name, uint8_t name_len, int rssi)
|
||||
{
|
||||
if (find_scan_result(bda, ble_scan_results)) {
|
||||
ESP_LOGW(TAG, "Result already exists!");
|
||||
return;
|
||||
}
|
||||
esp_hid_scan_result_t *r = (esp_hid_scan_result_t *)malloc(sizeof(esp_hid_scan_result_t));
|
||||
if (r == NULL) {
|
||||
ESP_LOGE(TAG, "Malloc ble_hidh_scan_result_t failed!");
|
||||
return;
|
||||
}
|
||||
r->transport = ESP_HID_TRANSPORT_BLE;
|
||||
memcpy(r->bda, bda, sizeof(esp_bd_addr_t));
|
||||
r->ble.appearance = appearance;
|
||||
r->ble.addr_type = addr_type;
|
||||
r->usage = esp_hid_usage_from_appearance(appearance);
|
||||
r->rssi = rssi;
|
||||
r->name = NULL;
|
||||
if (name_len && name) {
|
||||
char *name_s = (char *)malloc(name_len + 1);
|
||||
if (name_s == NULL) {
|
||||
free(r);
|
||||
ESP_LOGE(TAG, "Malloc result name failed!");
|
||||
return;
|
||||
}
|
||||
memcpy(name_s, name, name_len);
|
||||
name_s[name_len] = 0;
|
||||
r->name = (const char *)name_s;
|
||||
}
|
||||
r->next = ble_scan_results;
|
||||
ble_scan_results = r;
|
||||
num_ble_scan_results++;
|
||||
}
|
||||
|
||||
void print_uuid(esp_bt_uuid_t *uuid)
|
||||
{
|
||||
if (uuid->len == ESP_UUID_LEN_16) {
|
||||
GAP_DBG_PRINTF("UUID16: 0x%04x", uuid->uuid.uuid16);
|
||||
} else if (uuid->len == ESP_UUID_LEN_32) {
|
||||
GAP_DBG_PRINTF("UUID32: 0x%08x", uuid->uuid.uuid32);
|
||||
} else if (uuid->len == ESP_UUID_LEN_128) {
|
||||
GAP_DBG_PRINTF("UUID128: %02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x", uuid->uuid.uuid128[0],
|
||||
uuid->uuid.uuid128[1], uuid->uuid.uuid128[2], uuid->uuid.uuid128[3],
|
||||
uuid->uuid.uuid128[4], uuid->uuid.uuid128[5], uuid->uuid.uuid128[6],
|
||||
uuid->uuid.uuid128[7], uuid->uuid.uuid128[8], uuid->uuid.uuid128[9],
|
||||
uuid->uuid.uuid128[10], uuid->uuid.uuid128[11], uuid->uuid.uuid128[12],
|
||||
uuid->uuid.uuid128[13], uuid->uuid.uuid128[14], uuid->uuid.uuid128[15]);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_bt_device_result(struct disc_res_param *disc_res)
|
||||
{
|
||||
GAP_DBG_PRINTF("BT : " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(disc_res->bda));
|
||||
uint32_t codv = 0;
|
||||
esp_bt_cod_t *cod = (esp_bt_cod_t *)&codv;
|
||||
int8_t rssi = 0;
|
||||
uint8_t *name = NULL;
|
||||
uint8_t name_len = 0;
|
||||
esp_bt_uuid_t uuid;
|
||||
|
||||
uuid.len = ESP_UUID_LEN_16;
|
||||
uuid.uuid.uuid16 = 0;
|
||||
|
||||
for (int i = 0; i < disc_res->num_prop; i++) {
|
||||
esp_bt_gap_dev_prop_t *prop = &disc_res->prop[i];
|
||||
if (prop->type != ESP_BT_GAP_DEV_PROP_EIR) {
|
||||
GAP_DBG_PRINTF(", %s: ", gap_bt_prop_type_names[prop->type]);
|
||||
}
|
||||
if (prop->type == ESP_BT_GAP_DEV_PROP_BDNAME) {
|
||||
name = (uint8_t *)prop->val;
|
||||
name_len = strlen((const char *)name);
|
||||
GAP_DBG_PRINTF("%s", (const char *)name);
|
||||
} else if (prop->type == ESP_BT_GAP_DEV_PROP_RSSI) {
|
||||
rssi = *((int8_t *)prop->val);
|
||||
GAP_DBG_PRINTF("%d", rssi);
|
||||
} else if (prop->type == ESP_BT_GAP_DEV_PROP_COD) {
|
||||
memcpy(&codv, prop->val, sizeof(uint32_t));
|
||||
GAP_DBG_PRINTF("major: %s, minor: %d, service: 0x%03x", esp_hid_cod_major_str(cod->major), cod->minor, cod->service);
|
||||
} else if (prop->type == ESP_BT_GAP_DEV_PROP_EIR) {
|
||||
uint8_t len = 0;
|
||||
uint8_t *data = 0;
|
||||
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_CMPL_16BITS_UUID, &len);
|
||||
if (data == NULL) {
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_INCMPL_16BITS_UUID, &len);
|
||||
}
|
||||
if (data && len == ESP_UUID_LEN_16) {
|
||||
uuid.len = ESP_UUID_LEN_16;
|
||||
uuid.uuid.uuid16 = data[0] + (data[1] << 8);
|
||||
GAP_DBG_PRINTF(", "); print_uuid(&uuid);
|
||||
continue;
|
||||
}
|
||||
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_CMPL_32BITS_UUID, &len);
|
||||
if (data == NULL) {
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_INCMPL_32BITS_UUID, &len);
|
||||
}
|
||||
if (data && len == ESP_UUID_LEN_32) {
|
||||
uuid.len = len;
|
||||
memcpy(&uuid.uuid.uuid32, data, sizeof(uint32_t));
|
||||
GAP_DBG_PRINTF(", "); print_uuid(&uuid);
|
||||
continue;
|
||||
}
|
||||
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_CMPL_128BITS_UUID, &len);
|
||||
if (data == NULL) {
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_INCMPL_128BITS_UUID, &len);
|
||||
}
|
||||
if (data && len == ESP_UUID_LEN_128) {
|
||||
uuid.len = len;
|
||||
memcpy(uuid.uuid.uuid128, (uint8_t *)data, len);
|
||||
GAP_DBG_PRINTF(", "); print_uuid(&uuid);
|
||||
continue;
|
||||
}
|
||||
|
||||
//try to find a name
|
||||
if (name == NULL) {
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_CMPL_LOCAL_NAME, &len);
|
||||
if (data == NULL) {
|
||||
data = esp_bt_gap_resolve_eir_data((uint8_t *)prop->val, ESP_BT_EIR_TYPE_SHORT_LOCAL_NAME, &len);
|
||||
}
|
||||
if (data && len) {
|
||||
name = data;
|
||||
name_len = len;
|
||||
GAP_DBG_PRINTF(", NAME: ");
|
||||
for (int x = 0; x < len; x++) {
|
||||
GAP_DBG_PRINTF("%c", (char)data[x]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GAP_DBG_PRINTF("\n");
|
||||
|
||||
if (cod->major == ESP_BT_COD_MAJOR_DEV_PERIPHERAL || (find_scan_result(disc_res->bda, bt_scan_results) != NULL)) {
|
||||
add_bt_scan_result(disc_res->bda, cod, &uuid, name, name_len, rssi);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_ble_device_result(struct ble_scan_result_evt_param *scan_rst)
|
||||
{
|
||||
|
||||
uint16_t uuid = 0;
|
||||
uint16_t appearance = 0;
|
||||
char name[64] = {0};
|
||||
|
||||
uint8_t uuid_len = 0;
|
||||
uint8_t *uuid_d = esp_ble_resolve_adv_data(scan_rst->ble_adv, ESP_BLE_AD_TYPE_16SRV_CMPL, &uuid_len);
|
||||
if (uuid_d != NULL && uuid_len) {
|
||||
uuid = uuid_d[0] + (uuid_d[1] << 8);
|
||||
}
|
||||
|
||||
uint8_t appearance_len = 0;
|
||||
uint8_t *appearance_d = esp_ble_resolve_adv_data(scan_rst->ble_adv, ESP_BLE_AD_TYPE_APPEARANCE, &appearance_len);
|
||||
if (appearance_d != NULL && appearance_len) {
|
||||
appearance = appearance_d[0] + (appearance_d[1] << 8);
|
||||
}
|
||||
|
||||
uint8_t adv_name_len = 0;
|
||||
uint8_t *adv_name = esp_ble_resolve_adv_data(scan_rst->ble_adv, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
|
||||
|
||||
if (adv_name == NULL) {
|
||||
adv_name = esp_ble_resolve_adv_data(scan_rst->ble_adv, ESP_BLE_AD_TYPE_NAME_SHORT, &adv_name_len);
|
||||
}
|
||||
|
||||
if (adv_name != NULL && adv_name_len) {
|
||||
memcpy(name, adv_name, adv_name_len);
|
||||
name[adv_name_len] = 0;
|
||||
}
|
||||
|
||||
GAP_DBG_PRINTF("BLE: " ESP_BD_ADDR_STR ", ", ESP_BD_ADDR_HEX(scan_rst->bda));
|
||||
GAP_DBG_PRINTF("RSSI: %d, ", scan_rst->rssi);
|
||||
GAP_DBG_PRINTF("UUID: 0x%04x, ", uuid);
|
||||
GAP_DBG_PRINTF("APPEARANCE: 0x%04x, ", appearance);
|
||||
GAP_DBG_PRINTF("ADDR_TYPE: '%s'", ble_addr_type_str(scan_rst->ble_addr_type));
|
||||
if (adv_name_len) {
|
||||
GAP_DBG_PRINTF(", NAME: '%s'", name);
|
||||
}
|
||||
GAP_DBG_PRINTF("\n");
|
||||
|
||||
if (uuid == ESP_GATT_UUID_HID_SVC) {
|
||||
add_ble_scan_result(scan_rst->bda, scan_rst->ble_addr_type, appearance, adv_name, adv_name_len, scan_rst->rssi);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* BT GAP
|
||||
* */
|
||||
|
||||
static void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
|
||||
{
|
||||
static bool scan_running = false;
|
||||
static bool scan_wait_stop = false;
|
||||
switch (event) {
|
||||
case ESP_BT_GAP_DISC_STATE_CHANGED_EVT: {
|
||||
ESP_LOGV(TAG, "BT GAP DISC_STATE %s", (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) ? "START" : "STOP");
|
||||
if (param->disc_st_chg.state == ESP_BT_GAP_DISCOVERY_STARTED) {
|
||||
scan_running = true;
|
||||
scan_wait_stop = true;
|
||||
} else if (scan_wait_stop) {
|
||||
scan_wait_stop = false;
|
||||
} else if (scan_running) {
|
||||
scan_running = false;
|
||||
SEND_BT_CB();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_BT_GAP_DISC_RES_EVT: {
|
||||
handle_bt_device_result(¶m->disc_res);
|
||||
break;
|
||||
}
|
||||
case ESP_BT_GAP_KEY_NOTIF_EVT:
|
||||
ESP_LOGI(TAG, "BT GAP KEY_NOTIF passkey:%d", param->key_notif.passkey);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGV(TAG, "BT GAP EVENT %s", bt_gap_evt_str(event));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t init_bt_gap(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE;
|
||||
esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO;
|
||||
esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t));
|
||||
/*
|
||||
* Set default parameters for Legacy Pairing
|
||||
* Use fixed pin code
|
||||
*/
|
||||
esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_FIXED;
|
||||
esp_bt_pin_code_t pin_code;
|
||||
pin_code[0] = '1';
|
||||
pin_code[1] = '2';
|
||||
pin_code[2] = '3';
|
||||
pin_code[3] = '4';
|
||||
esp_bt_gap_set_pin(pin_type, 4, pin_code);
|
||||
|
||||
if ((ret = esp_bt_gap_register_callback(bt_gap_event_handler)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_bt_gap_register_callback failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Allow BT devices to connect back to us
|
||||
if ((ret = esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_NON_DISCOVERABLE)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_bt_gap_set_scan_mode failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t start_bt_scan(uint32_t seconds)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if ((ret = esp_bt_gap_start_discovery(ESP_BT_INQ_MODE_GENERAL_INQUIRY, (int)(seconds / 1.28), 0)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_bt_gap_start_discovery failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* BLE GAP
|
||||
* */
|
||||
|
||||
static void ble_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
|
||||
{
|
||||
switch (event) {
|
||||
/*
|
||||
* SCAN
|
||||
* */
|
||||
case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT: {
|
||||
ESP_LOGV(TAG, "BLE GAP EVENT SCAN_PARAM_SET_COMPLETE");
|
||||
SEND_BLE_CB();
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_SCAN_RESULT_EVT: {
|
||||
esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
|
||||
switch (scan_result->scan_rst.search_evt) {
|
||||
case ESP_GAP_SEARCH_INQ_RES_EVT: {
|
||||
handle_ble_device_result(&scan_result->scan_rst);
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_SEARCH_INQ_CMPL_EVT:
|
||||
ESP_LOGV(TAG, "BLE GAP EVENT SCAN DONE: %d", scan_result->scan_rst.num_resps);
|
||||
SEND_BLE_CB();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT: {
|
||||
ESP_LOGV(TAG, "BLE GAP EVENT SCAN CANCELED");
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* ADVERTISEMENT
|
||||
* */
|
||||
case ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT:
|
||||
ESP_LOGV(TAG, "BLE GAP ADV_DATA_SET_COMPLETE");
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
|
||||
ESP_LOGV(TAG, "BLE GAP ADV_START_COMPLETE");
|
||||
break;
|
||||
|
||||
/*
|
||||
* AUTHENTICATION
|
||||
* */
|
||||
case ESP_GAP_BLE_AUTH_CMPL_EVT:
|
||||
if (!param->ble_security.auth_cmpl.success) {
|
||||
ESP_LOGE(TAG, "BLE GAP AUTH ERROR: 0x%x", param->ble_security.auth_cmpl.fail_reason);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "BLE GAP AUTH SUCCESS");
|
||||
}
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_KEY_EVT: //shows the ble key info share with peer device to the user.
|
||||
ESP_LOGI(TAG, "BLE GAP KEY type = %s", esp_ble_key_type_str(param->ble_security.ble_key.key_type));
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_PASSKEY_NOTIF_EVT: // ESP_IO_CAP_OUT
|
||||
// The app will receive this evt when the IO has Output capability and the peer device IO has Input capability.
|
||||
// Show the passkey number to the user to input it in the peer device.
|
||||
ESP_LOGI(TAG, "BLE GAP PASSKEY_NOTIF passkey:%d", param->ble_security.key_notif.passkey);
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_NC_REQ_EVT: // ESP_IO_CAP_IO
|
||||
// The app will receive this event when the IO has DisplayYesNO capability and the peer device IO also has DisplayYesNo capability.
|
||||
// show the passkey number to the user to confirm it with the number displayed by peer device.
|
||||
ESP_LOGI(TAG, "BLE GAP NC_REQ passkey:%d", param->ble_security.key_notif.passkey);
|
||||
esp_ble_confirm_reply(param->ble_security.key_notif.bd_addr, true);
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_PASSKEY_REQ_EVT: // ESP_IO_CAP_IN
|
||||
// The app will receive this evt when the IO has Input capability and the peer device IO has Output capability.
|
||||
// See the passkey number on the peer device and send it back.
|
||||
ESP_LOGI(TAG, "BLE GAP PASSKEY_REQ");
|
||||
//esp_ble_passkey_reply(param->ble_security.ble_req.bd_addr, true, 1234);
|
||||
break;
|
||||
|
||||
case ESP_GAP_BLE_SEC_REQ_EVT:
|
||||
ESP_LOGI(TAG, "BLE GAP SEC_REQ");
|
||||
// Send the positive(true) security response to the peer device to accept the security request.
|
||||
// If not accept the security request, should send the security response with negative(false) accept value.
|
||||
esp_ble_gap_security_rsp(param->ble_security.ble_req.bd_addr, true);
|
||||
break;
|
||||
|
||||
default:
|
||||
ESP_LOGV(TAG, "BLE GAP EVENT %s", ble_gap_evt_str(event));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t init_ble_gap(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
if ((ret = esp_ble_gap_register_callback(ble_gap_event_handler)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_register_callback failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_ble_scan_params_t hid_scan_params = {
|
||||
.scan_type = BLE_SCAN_TYPE_ACTIVE,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
|
||||
.scan_interval = 0x50,
|
||||
.scan_window = 0x30,
|
||||
.scan_duplicate = BLE_SCAN_DUPLICATE_ENABLE,
|
||||
};
|
||||
|
||||
static esp_err_t start_ble_scan(uint32_t seconds)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if ((ret = esp_ble_gap_set_scan_params(&hid_scan_params)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_set_scan_params failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
WAIT_BLE_CB();
|
||||
|
||||
if ((ret = esp_ble_gap_start_scanning(seconds)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "esp_ble_gap_start_scanning failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name)
|
||||
{
|
||||
|
||||
esp_err_t ret;
|
||||
|
||||
const uint8_t hidd_service_uuid128[] = {
|
||||
0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x12, 0x18, 0x00, 0x00,
|
||||
};
|
||||
|
||||
esp_ble_adv_data_t ble_adv_data = {
|
||||
.set_scan_rsp = false,
|
||||
.include_name = true,
|
||||
.include_txpower = true,
|
||||
.min_interval = 0x0006, //slave connection min interval, Time = min_interval * 1.25 msec
|
||||
.max_interval = 0x0010, //slave connection max interval, Time = max_interval * 1.25 msec
|
||||
.appearance = appearance,
|
||||
.manufacturer_len = 0,
|
||||
.p_manufacturer_data = NULL,
|
||||
.service_data_len = 0,
|
||||
.p_service_data = NULL,
|
||||
.service_uuid_len = sizeof(hidd_service_uuid128),
|
||||
.p_service_uuid = (uint8_t *)hidd_service_uuid128,
|
||||
.flag = 0x6,
|
||||
};
|
||||
|
||||
esp_ble_auth_req_t auth_req = ESP_LE_AUTH_REQ_SC_MITM_BOND;
|
||||
//esp_ble_io_cap_t iocap = ESP_IO_CAP_OUT;//you have to enter the key on the host
|
||||
//esp_ble_io_cap_t iocap = ESP_IO_CAP_IN;//you have to enter the key on the device
|
||||
esp_ble_io_cap_t iocap = ESP_IO_CAP_IO;//you have to agree that key matches on both
|
||||
//esp_ble_io_cap_t iocap = ESP_IO_CAP_NONE;//device is not capable of input or output, unsecure
|
||||
uint8_t init_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
uint8_t rsp_key = ESP_BLE_ENC_KEY_MASK | ESP_BLE_ID_KEY_MASK;
|
||||
uint8_t key_size = 16; //the key size should be 7~16 bytes
|
||||
uint32_t passkey = 1234;//ESP_IO_CAP_OUT
|
||||
|
||||
if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_AUTHEN_REQ_MODE, &auth_req, 1)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_security_param AUTHEN_REQ_MODE failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_IOCAP_MODE, &iocap, 1)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_security_param IOCAP_MODE failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_SET_INIT_KEY, &init_key, 1)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_security_param SET_INIT_KEY failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_SET_RSP_KEY, &rsp_key, 1)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_security_param SET_RSP_KEY failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_MAX_KEY_SIZE, &key_size, 1)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_security_param MAX_KEY_SIZE failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_set_security_param(ESP_BLE_SM_SET_STATIC_PASSKEY, &passkey, sizeof(uint32_t))) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_security_param SET_STATIC_PASSKEY failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_set_device_name(device_name)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP set_device_name failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if ((ret = esp_ble_gap_config_adv_data(&ble_adv_data)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GAP config_adv_data failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_hid_ble_gap_adv_start(void)
|
||||
{
|
||||
static esp_ble_adv_params_t hidd_adv_params = {
|
||||
.adv_int_min = 0x20,
|
||||
.adv_int_max = 0x30,
|
||||
.adv_type = ADV_TYPE_IND,
|
||||
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
};
|
||||
return esp_ble_gap_start_advertising(&hidd_adv_params);
|
||||
}
|
||||
|
||||
/*
|
||||
* CONTROLLER INIT
|
||||
* */
|
||||
|
||||
static esp_err_t init_low_level(uint8_t mode)
|
||||
{
|
||||
esp_err_t ret;
|
||||
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
||||
if (mode & ESP_BT_MODE_CLASSIC_BT) {
|
||||
bt_cfg.mode = mode;
|
||||
bt_cfg.bt_max_acl_conn = 3;
|
||||
bt_cfg.bt_max_sync_conn = 3;
|
||||
} else {
|
||||
ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_bt_controller_mem_release failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = esp_bt_controller_init(&bt_cfg);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_bt_controller_init failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = esp_bt_controller_enable(mode);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_bt_controller_enable failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = esp_bluedroid_init();
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_bluedroid_init failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = esp_bluedroid_enable();
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mode & ESP_BT_MODE_CLASSIC_BT) {
|
||||
ret = init_bt_gap();
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode & ESP_BT_MODE_BLE) {
|
||||
ret = init_ble_gap();
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
esp_err_t esp_hid_gap_init(uint8_t mode)
|
||||
{
|
||||
esp_err_t ret;
|
||||
if (!mode || mode > ESP_BT_MODE_BTDM) {
|
||||
ESP_LOGE(TAG, "Invalid mode given!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (bt_hidh_cb_semaphore != NULL) {
|
||||
ESP_LOGE(TAG, "Already initialised");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
bt_hidh_cb_semaphore = xSemaphoreCreateBinary();
|
||||
if (bt_hidh_cb_semaphore == NULL) {
|
||||
ESP_LOGE(TAG, "xSemaphoreCreateMutex failed!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ble_hidh_cb_semaphore = xSemaphoreCreateBinary();
|
||||
if (ble_hidh_cb_semaphore == NULL) {
|
||||
ESP_LOGE(TAG, "xSemaphoreCreateMutex failed!");
|
||||
vSemaphoreDelete(bt_hidh_cb_semaphore);
|
||||
bt_hidh_cb_semaphore = NULL;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ret = init_low_level(mode);
|
||||
if (ret != ESP_OK) {
|
||||
vSemaphoreDelete(bt_hidh_cb_semaphore);
|
||||
bt_hidh_cb_semaphore = NULL;
|
||||
vSemaphoreDelete(ble_hidh_cb_semaphore);
|
||||
ble_hidh_cb_semaphore = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_result_t **results)
|
||||
{
|
||||
if (num_bt_scan_results || bt_scan_results || num_ble_scan_results || ble_scan_results) {
|
||||
ESP_LOGE(TAG, "There are old scan results. Free them first!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (start_ble_scan(seconds) == ESP_OK) {
|
||||
if (start_bt_scan(seconds) == ESP_OK) {
|
||||
WAIT_BT_CB();
|
||||
}
|
||||
WAIT_BLE_CB();
|
||||
} else {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
*num_results = num_bt_scan_results + num_ble_scan_results;
|
||||
*results = bt_scan_results;
|
||||
if (num_bt_scan_results) {
|
||||
while (bt_scan_results->next != NULL) {
|
||||
bt_scan_results = bt_scan_results->next;
|
||||
}
|
||||
bt_scan_results->next = ble_scan_results;
|
||||
} else {
|
||||
*results = ble_scan_results;
|
||||
}
|
||||
|
||||
num_bt_scan_results = 0;
|
||||
bt_scan_results = NULL;
|
||||
num_ble_scan_results = 0;
|
||||
ble_scan_results = NULL;
|
||||
return ESP_OK;
|
||||
}
|
68
examples/bluetooth/esp_hid_host/main/esp_hid_gap.h
Normal file
68
examples/bluetooth/esp_hid_host/main/esp_hid_gap.h
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef _ESP_HID_GAP_H_
|
||||
#define _ESP_HID_GAP_H_
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_bt.h"
|
||||
#include "esp_bt_defs.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_gattc_api.h"
|
||||
#include "esp_gatt_defs.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
#include "esp_hid_common.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct esp_hidh_scan_result_s {
|
||||
struct esp_hidh_scan_result_s *next;
|
||||
|
||||
esp_bd_addr_t bda;
|
||||
const char *name;
|
||||
int8_t rssi;
|
||||
esp_hid_usage_t usage;
|
||||
esp_hid_transport_t transport; //BT, BLE or USB
|
||||
union {
|
||||
struct {
|
||||
esp_bt_cod_t cod;
|
||||
esp_bt_uuid_t uuid;
|
||||
} bt;
|
||||
struct {
|
||||
esp_ble_addr_type_t addr_type;
|
||||
uint16_t appearance;
|
||||
} ble;
|
||||
};
|
||||
} esp_hid_scan_result_t;
|
||||
|
||||
esp_err_t esp_hid_gap_init(uint8_t mode);
|
||||
esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_result_t **results);
|
||||
void esp_hid_scan_results_free(esp_hid_scan_result_t *results);
|
||||
|
||||
esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name);
|
||||
esp_err_t esp_hid_ble_gap_adv_start(void);
|
||||
|
||||
void print_uuid(esp_bt_uuid_t *uuid);
|
||||
const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ESP_HIDH_GAP_H_ */
|
134
examples/bluetooth/esp_hid_host/main/esp_hid_host_main.c
Normal file
134
examples/bluetooth/esp_hid_host/main/esp_hid_host_main.c
Normal file
@ -0,0 +1,134 @@
|
||||
/* This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
Unless required by applicable law or agreed to in writing, this software is
|
||||
distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_bt.h"
|
||||
#include "esp_bt_defs.h"
|
||||
#include "esp_gap_ble_api.h"
|
||||
#include "esp_gatts_api.h"
|
||||
#include "esp_gatt_defs.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
|
||||
#include "esp_hidh.h"
|
||||
#include "esp_hid_gap.h"
|
||||
|
||||
static const char *TAG = "ESP_HIDH_DEMO";
|
||||
|
||||
void hidh_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data)
|
||||
{
|
||||
esp_hidh_event_t event = (esp_hidh_event_t)id;
|
||||
esp_hidh_event_data_t *param = (esp_hidh_event_data_t *)event_data;
|
||||
|
||||
switch (event) {
|
||||
case ESP_HIDH_OPEN_EVENT: {
|
||||
const uint8_t *bda = esp_hidh_dev_bda_get(param->open.dev);
|
||||
ESP_LOGI(TAG, ESP_BD_ADDR_STR " OPEN: %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->open.dev));
|
||||
esp_hidh_dev_dump(param->open.dev, stdout);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_BATTERY_EVENT: {
|
||||
const uint8_t *bda = esp_hidh_dev_bda_get(param->battery.dev);
|
||||
ESP_LOGI(TAG, ESP_BD_ADDR_STR " BATTERY: %d%%", ESP_BD_ADDR_HEX(bda), param->battery.level);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_INPUT_EVENT: {
|
||||
const uint8_t *bda = esp_hidh_dev_bda_get(param->input.dev);
|
||||
ESP_LOGI(TAG, ESP_BD_ADDR_STR " INPUT: %8s, MAP: %2u, ID: %3u, Len: %d, Data:", ESP_BD_ADDR_HEX(bda), esp_hid_usage_str(param->input.usage), param->input.map_index, param->input.report_id, param->input.length);
|
||||
ESP_LOG_BUFFER_HEX(TAG, param->input.data, param->input.length);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_FEATURE_EVENT: {
|
||||
const uint8_t *bda = esp_hidh_dev_bda_get(param->feature.dev);
|
||||
ESP_LOGI(TAG, ESP_BD_ADDR_STR " FEATURE: %8s, MAP: %2u, ID: %3u, Len: %d", ESP_BD_ADDR_HEX(bda), esp_hid_usage_str(param->feature.usage), param->feature.map_index, param->feature.report_id, param->feature.length);
|
||||
ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length);
|
||||
break;
|
||||
}
|
||||
case ESP_HIDH_CLOSE_EVENT: {
|
||||
const uint8_t *bda = esp_hidh_dev_bda_get(param->close.dev);
|
||||
ESP_LOGI(TAG, ESP_BD_ADDR_STR " CLOSE: '%s' %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->close.dev), esp_hid_disconnect_reason_str(esp_hidh_dev_transport_get(param->close.dev), param->close.reason));
|
||||
//MUST call this function to free all allocated memory by this device
|
||||
esp_hidh_dev_free(param->close.dev);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGI(TAG, "EVENT: %d", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define SCAN_DURATION_SECONDS 5
|
||||
|
||||
void hid_demo_task(void *pvParameters)
|
||||
{
|
||||
size_t results_len = 0;
|
||||
esp_hid_scan_result_t *results = NULL;
|
||||
ESP_LOGI(TAG, "SCAN...");
|
||||
//start scan for HID devices
|
||||
esp_hid_scan(SCAN_DURATION_SECONDS, &results_len, &results);
|
||||
ESP_LOGI(TAG, "SCAN: %u results", results_len);
|
||||
if (results_len) {
|
||||
esp_hid_scan_result_t *r = results;
|
||||
esp_hid_scan_result_t *cr = NULL;
|
||||
while (r) {
|
||||
printf(" %s: " ESP_BD_ADDR_STR ", ", (r->transport == ESP_HID_TRANSPORT_BLE) ? "BLE" : "BT ", ESP_BD_ADDR_HEX(r->bda));
|
||||
printf("RSSI: %d, ", r->rssi);
|
||||
printf("USAGE: %s, ", esp_hid_usage_str(r->usage));
|
||||
if (r->transport == ESP_HID_TRANSPORT_BLE) {
|
||||
cr = r;
|
||||
printf("APPEARANCE: 0x%04x, ", r->ble.appearance);
|
||||
printf("ADDR_TYPE: '%s', ", ble_addr_type_str(r->ble.addr_type));
|
||||
} else {
|
||||
cr = r;
|
||||
printf("COD: %s[", esp_hid_cod_major_str(r->bt.cod.major));
|
||||
esp_hid_cod_minor_print(r->bt.cod.minor, stdout);
|
||||
printf("] srv 0x%03x, ", r->bt.cod.service);
|
||||
print_uuid(&r->bt.uuid);
|
||||
printf(", ");
|
||||
}
|
||||
printf("NAME: %s ", r->name ? r->name : "");
|
||||
printf("\n");
|
||||
r = r->next;
|
||||
}
|
||||
if (cr) {
|
||||
//open the last result
|
||||
esp_hidh_dev_open(cr->bda, cr->transport, cr->ble.addr_type);
|
||||
}
|
||||
//free the results
|
||||
esp_hid_scan_results_free(results);
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
ret = nvs_flash_init();
|
||||
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
ret = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK( ret );
|
||||
ESP_ERROR_CHECK( esp_hid_gap_init(ESP_BT_MODE_BTDM) );
|
||||
ESP_ERROR_CHECK( esp_ble_gattc_register_callback(esp_hidh_gattc_event_handler) );
|
||||
esp_hidh_config_t config = {
|
||||
.callback = hidh_callback,
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_hidh_init(&config) );
|
||||
|
||||
xTaskCreate(&hid_demo_task, "hid_task", 6 * 1024, NULL, 2, NULL);
|
||||
}
|
6
examples/bluetooth/esp_hid_host/sdkconfig.defaults
Normal file
6
examples/bluetooth/esp_hid_host/sdkconfig.defaults
Normal file
@ -0,0 +1,6 @@
|
||||
CONFIG_BT_ENABLED=y
|
||||
CONFIG_BTDM_CTRL_MODE_BTDM=y
|
||||
CONFIG_BTDM_CTRL_HCI_MODE_VHCI=y
|
||||
CONFIG_BT_BLUEDROID_ENABLED=y
|
||||
CONFIG_BT_CLASSIC_ENABLED=y
|
||||
CONFIG_BT_HID_HOST_ENABLED=y
|
@ -336,7 +336,7 @@ example_test_012:
|
||||
|
||||
UT_001:
|
||||
extends: .unit_test_template
|
||||
parallel: 37
|
||||
parallel: 38
|
||||
tags:
|
||||
- ESP32_IDF
|
||||
- UT_T1_1
|
||||
@ -345,7 +345,7 @@ UT_001:
|
||||
|
||||
UT_002:
|
||||
extends: .unit_test_template
|
||||
parallel: 14
|
||||
parallel: 15
|
||||
tags:
|
||||
- ESP32_IDF
|
||||
- UT_T1_1
|
||||
@ -419,7 +419,7 @@ UT_017:
|
||||
|
||||
UT_018:
|
||||
extends: .unit_test_template
|
||||
parallel: 4
|
||||
parallel: 5
|
||||
tags:
|
||||
- ESP32_IDF
|
||||
- UT_T1_1
|
||||
@ -495,7 +495,7 @@ UT_034:
|
||||
|
||||
UT_035:
|
||||
extends: .unit_test_s2_template
|
||||
parallel: 33
|
||||
parallel: 34
|
||||
tags:
|
||||
- ESP32S2_IDF
|
||||
- UT_T1_1
|
||||
|
Loading…
Reference in New Issue
Block a user