mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(nimble): added HID over Gatt profile support
This commit is contained in:
parent
04b6feb1ad
commit
a95bf9e0c3
@ -537,6 +537,8 @@ if(CONFIG_BT_ENABLED)
|
||||
host/nimble/nimble/nimble/host/services/prox/include
|
||||
host/nimble/nimble/nimble/host/services/cts/include
|
||||
host/nimble/nimble/nimble/host/services/tps/include
|
||||
host/nimble/nimble/nimble/host/services/hid/include
|
||||
host/nimble/nimble/nimble/host/services/sps/include
|
||||
host/nimble/nimble/nimble/host/util/include
|
||||
host/nimble/nimble/nimble/host/store/ram/include
|
||||
host/nimble/nimble/nimble/host/store/config/include
|
||||
@ -557,6 +559,8 @@ if(CONFIG_BT_ENABLED)
|
||||
"host/nimble/nimble/nimble/host/services/lls/src/ble_svc_lls.c"
|
||||
"host/nimble/nimble/nimble/host/services/prox/src/ble_svc_prox.c"
|
||||
"host/nimble/nimble/nimble/host/services/cts/src/ble_svc_cts.c"
|
||||
"host/nimble/nimble/nimble/host/services/hid/src/ble_svc_hid.c"
|
||||
"host/nimble/nimble/nimble/host/services/sps/src/ble_svc_sps.c"
|
||||
"host/nimble/nimble/nimble/host/src/ble_hs_conn.c"
|
||||
"host/nimble/nimble/nimble/host/src/ble_store_util.c"
|
||||
"host/nimble/nimble/nimble/host/src/ble_sm.c"
|
||||
|
@ -659,6 +659,29 @@ config BT_NIMBLE_BLE_GATT_BLOB_TRANSFER
|
||||
This option is used when data to be sent is more than 512 bytes. For peripheral role,
|
||||
BT_NIMBLE_MSYS_1_BLOCK_COUNT needs to be increased according to the need.
|
||||
|
||||
menu "BLE Services"
|
||||
menuconfig BT_NIMBLE_HID_SERVICE
|
||||
bool "HID service"
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
default n
|
||||
help
|
||||
Enable HID service support
|
||||
|
||||
config BT_NIMBLE_SVC_HID_MAX_INSTANCES
|
||||
depends on BT_NIMBLE_HID_SERVICE
|
||||
int "Maximum HID service instances"
|
||||
default 2
|
||||
help
|
||||
Defines maximum number of HID service instances
|
||||
|
||||
config BT_NIMBLE_SVC_HID_MAX_RPTS
|
||||
depends on BT_NIMBLE_HID_SERVICE
|
||||
int "Maximum HID Report characteristics per service instance"
|
||||
default 3
|
||||
help
|
||||
Defines maximum number of report characteristics per service instance
|
||||
endmenu
|
||||
|
||||
config BT_NIMBLE_VS_SUPPORT
|
||||
bool "Enable support for VSC and VSE"
|
||||
help
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 08ac3ca677be417e0087159629d0359c4a9a1585
|
||||
Subproject commit 34e3842741f598c00082ef7d2cea5f22615366f6
|
@ -908,6 +908,20 @@
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM
|
||||
#define MYNEWT_VAL_BLE_SVC_BAS_BATTERY_LEVEL_READ_PERM (0)
|
||||
#endif
|
||||
|
||||
/*** nimble/host/services/hid */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_HID_SERVICE
|
||||
#define MYNEWT_VAL_BLE_SVC_HID_SERVICE CONFIG_BT_NIMBLE_HID_SERVICE
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_HID_MAX_RPTS
|
||||
#define MYNEWT_VAL_BLE_SVC_HID_MAX_RPTS CONFIG_BT_NIMBLE_SVC_HID_MAX_RPTS
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_HID_MAX_SVC_INSTANCES
|
||||
#define MYNEWT_VAL_BLE_SVC_HID_MAX_SVC_INSTANCES CONFIG_BT_NIMBLE_SVC_HID_MAX_INSTANCES
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_MESH_ADV_TASK_PRIO
|
||||
#define MYNEWT_VAL_BLE_MESH_ADV_TASK_PRIO (9)
|
||||
#endif
|
||||
@ -1570,6 +1584,15 @@
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_SYSTEM_ID_READ_PERM (-1)
|
||||
#endif
|
||||
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_DEFAULT (NULL)
|
||||
#endif
|
||||
|
||||
/* Value copied from BLE_SVC_DIS_DEFAULT_READ_PERM */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM
|
||||
#define MYNEWT_VAL_BLE_SVC_DIS_PNP_ID_READ_PERM (-1)
|
||||
#endif
|
||||
|
||||
/*** @apache-mynewt-nimble/nimble/host/services/gap */
|
||||
#ifndef MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE
|
||||
#define MYNEWT_VAL_BLE_SVC_GAP_APPEARANCE CONFIG_BT_NIMBLE_SVC_GAP_APPEARANCE
|
||||
|
@ -13,6 +13,11 @@ if(CONFIG_BT_ENABLED)
|
||||
"src/bt_hidh.c"
|
||||
"src/bt_hidd.c")
|
||||
endif()
|
||||
if(CONFIG_BT_NIMBLE_ENABLED)
|
||||
list(APPEND srcs
|
||||
"src/nimble_hidd.c"
|
||||
"src/nimble_hidh.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
|
28
components/esp_hid/include/esp_hidh_nimble.h
Normal file
28
components/esp_hid/include/esp_hidh_nimble.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
/**
|
||||
* @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(uint8_t *bda, esp_hid_transport_t transport, uint8_t remote_addr_type);
|
||||
#endif /* CONFIG_BT_NIMBLE_ENABLED */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -1,16 +1,8 @@
|
||||
// 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.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -28,6 +20,10 @@ extern "C" {
|
||||
#include "esp_hidh_bluedroid.h"
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "esp_hidh_nimble.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1,16 +1,8 @@
|
||||
// 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.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -22,7 +14,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATTS_ENABLE
|
||||
#if CONFIG_GATTS_ENABLE || CONFIG_BT_NIMBLE_ENABLED
|
||||
|
||||
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);
|
||||
|
||||
|
@ -1,16 +1,8 @@
|
||||
// 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.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
@ -20,12 +12,16 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if CONFIG_GATTC_ENABLE
|
||||
#if CONFIG_GATTC_ENABLE || CONFIG_BT_NIMBLE_ENABLED
|
||||
|
||||
esp_err_t esp_ble_hidh_init(const esp_hidh_config_t *config);
|
||||
esp_err_t esp_ble_hidh_deinit(void);
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
esp_hidh_dev_t *esp_ble_hidh_dev_open(uint8_t *bda, uint8_t address_type);
|
||||
#else
|
||||
esp_hidh_dev_t *esp_ble_hidh_dev_open(esp_bd_addr_t bda, esp_ble_addr_type_t address_type);
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_GATTC_ENABLE */
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -17,6 +17,9 @@
|
||||
#include "esp_event.h"
|
||||
#include "sys/queue.h"
|
||||
#include "esp_timer.h"
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "nimble/ble.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -52,7 +55,11 @@ struct esp_hidh_dev_s {
|
||||
esp_timer_handle_t trans_timer; //transactiion timer
|
||||
uint8_t report_type; //Get_Report tansaction report_type
|
||||
uint8_t report_id; //Get_Report tansaction report_id
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
uint8_t *protocol_mode; // protocol mode is unique for each hid service instance
|
||||
#else
|
||||
uint8_t protocol_mode; //device protocol mode
|
||||
#endif
|
||||
bool connected; //we have all required data to communicate
|
||||
bool opened; //we opened the device manually, else the device connected to us
|
||||
bool added; //If lower layer has added the device
|
||||
@ -82,6 +89,9 @@ struct esp_hidh_dev_s {
|
||||
#if CONFIG_BLUEDROID_ENABLED
|
||||
esp_bd_addr_t bda;
|
||||
#endif /* CONFIG_BLUEDROID_ENABLED */
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
uint8_t bda[6];
|
||||
#endif
|
||||
|
||||
union {
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
@ -102,6 +112,15 @@ struct esp_hidh_dev_s {
|
||||
uint16_t battery_ccc_handle;
|
||||
} ble;
|
||||
#endif /* CONFIG_GATTC_ENABLE */
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
struct {
|
||||
uint8_t address_type;
|
||||
int conn_id;
|
||||
uint16_t appearance;
|
||||
uint16_t battery_handle;
|
||||
uint16_t battery_ccc_handle;
|
||||
} ble;
|
||||
#endif
|
||||
};
|
||||
TAILQ_ENTRY(esp_hidh_dev_s) devices;
|
||||
};
|
||||
@ -115,6 +134,10 @@ 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(uint8_t handle); //Classic Bluetooth Only
|
||||
esp_hidh_dev_t *esp_hidh_dev_get_by_conn_id(uint16_t conn_id); //BLE Only
|
||||
#endif /* CONFIG_BLUEDROID_ENABLED */
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
esp_hidh_dev_t *esp_hidh_dev_get_by_bda(uint8_t* bda); // BLE Only
|
||||
esp_hidh_dev_t *esp_hidh_dev_get_by_conn_id(uint16_t conn_id); //BLE Only
|
||||
#endif
|
||||
|
||||
esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_id_type_proto(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t protocol_mode);
|
||||
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);
|
||||
|
@ -1,16 +1,8 @@
|
||||
// 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.
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "esp_hidd_private.h"
|
||||
#include "esp_event_base.h"
|
||||
|
||||
#if CONFIG_GATTS_ENABLE
|
||||
#if CONFIG_GATTS_ENABLE || CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "ble_hidd.h"
|
||||
#endif /* CONFIG_GATTS_ENABLE */
|
||||
|
||||
@ -27,7 +27,7 @@ esp_err_t esp_hidd_dev_init(const esp_hid_device_config_t *config, esp_hid_trans
|
||||
}
|
||||
|
||||
switch (transport) {
|
||||
#if CONFIG_GATTS_ENABLE
|
||||
#if CONFIG_GATTS_ENABLE || CONFIG_BT_NIMBLE_ENABLED
|
||||
case ESP_HID_TRANSPORT_BLE:
|
||||
ret = esp_ble_hidd_dev_init(dev, config, callback);
|
||||
break;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -88,7 +88,7 @@ esp_err_t esp_hidh_init(const esp_hidh_config_t *config)
|
||||
}
|
||||
#endif /* CONFIG_BT_HID_HOST_ENABLED */
|
||||
|
||||
#if CONFIG_GATTC_ENABLE
|
||||
#if CONFIG_GATTC_ENABLE || CONFIG_BT_NIMBLE_ENABLED
|
||||
if (err == ESP_OK) {
|
||||
err = esp_ble_hidh_init(config);
|
||||
}
|
||||
@ -123,7 +123,7 @@ esp_err_t esp_hidh_deinit(void)
|
||||
}
|
||||
#endif /* CONFIG_BT_HID_HOST_ENABLED */
|
||||
|
||||
#if CONFIG_GATTC_ENABLE
|
||||
#if CONFIG_GATTC_ENABLE || CONFIG_BT_NIMBLE_ENABLED
|
||||
if (err == ESP_OK) {
|
||||
err = esp_ble_hidh_deinit();
|
||||
}
|
||||
@ -150,6 +150,11 @@ esp_hidh_dev_t *esp_hidh_dev_open(esp_bd_addr_t bda, esp_hid_transport_t transpo
|
||||
dev = esp_ble_hidh_dev_open(bda, (esp_ble_addr_type_t)remote_addr_type);
|
||||
}
|
||||
#endif /* CONFIG_GATTC_ENABLE */
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
if (transport == ESP_HID_TRANSPORT_BLE) {
|
||||
dev = esp_ble_hidh_dev_open(bda, remote_addr_type);
|
||||
}
|
||||
#endif /* CONFIG_BT_NIMBLE_ENABLED */
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
if (transport == ESP_HID_TRANSPORT_BT) {
|
||||
dev = esp_bt_hidh_dev_open(bda);
|
||||
@ -159,6 +164,19 @@ esp_hidh_dev_t *esp_hidh_dev_open(esp_bd_addr_t bda, esp_hid_transport_t transpo
|
||||
}
|
||||
#endif /* CONFIG_BLUEDROID_ENABLED */
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
esp_hidh_dev_t *esp_hidh_dev_open(uint8_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;
|
||||
dev = esp_ble_hidh_dev_open(bda, remote_addr_type);
|
||||
return dev;
|
||||
}
|
||||
#endif /* CONFIG_BT_NIMBLE_ENABLED */
|
||||
|
||||
esp_err_t esp_hidh_dev_close(esp_hidh_dev_t *dev)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
@ -329,6 +347,14 @@ const uint8_t *esp_hidh_dev_bda_get(esp_hidh_dev_t *dev)
|
||||
esp_hidh_dev_unlock(dev);
|
||||
}
|
||||
#endif /* CONFIG_BLUEDROID_ENABLED */
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
if (esp_hidh_dev_exists(dev)) {
|
||||
esp_hidh_dev_lock(dev);
|
||||
ret = dev->bda;
|
||||
esp_hidh_dev_unlock(dev);
|
||||
}
|
||||
#endif /* CONFIG_BT_NIMBLE_ENABLED */
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -823,3 +849,82 @@ void esp_hidh_post_process_event_handler(void *event_handler_arg, esp_event_base
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BLUEDROID_ENABLED */
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
esp_hidh_dev_t *esp_hidh_dev_get_by_bda(uint8_t *bda)
|
||||
{
|
||||
esp_hidh_dev_t * d = NULL;
|
||||
lock_devices();
|
||||
TAILQ_FOREACH(d, &s_esp_hidh_devices, devices) {
|
||||
if (memcmp(bda, d->bda, sizeof(uint8_t) * 6) == 0) {
|
||||
unlock_devices();
|
||||
return d;
|
||||
}
|
||||
}
|
||||
unlock_devices();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_hidh_dev_t *esp_hidh_dev_get_by_conn_id(uint16_t conn_id)
|
||||
{
|
||||
esp_hidh_dev_t * d = NULL;
|
||||
lock_devices();
|
||||
TAILQ_FOREACH(d, &s_esp_hidh_devices, devices) {
|
||||
if (d->ble.conn_id == conn_id) {
|
||||
unlock_devices();
|
||||
return d;
|
||||
}
|
||||
}
|
||||
unlock_devices();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* The deep copy data append the end of the esp_hidh_event_data_t, move the data pointer to the correct address. This is
|
||||
* a workaround way, it's better to use flexible array in the interface.
|
||||
*/
|
||||
void esp_hidh_preprocess_event_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id,
|
||||
void *event_data)
|
||||
{
|
||||
esp_hidh_event_t event = (esp_hidh_event_t)event_id;
|
||||
esp_hidh_event_data_t *param = (esp_hidh_event_data_t *)event_data;
|
||||
|
||||
switch (event) {
|
||||
case ESP_HIDH_INPUT_EVENT:
|
||||
if (param->input.length && param->input.data) {
|
||||
param->input.data = (uint8_t *)param + sizeof(esp_hidh_event_data_t);
|
||||
}
|
||||
break;
|
||||
case ESP_HIDH_FEATURE_EVENT:
|
||||
if (param->feature.length && param->feature.data) {
|
||||
param->feature.data = (uint8_t *)param + sizeof(esp_hidh_event_data_t);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void esp_hidh_post_process_event_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id,
|
||||
void *event_data)
|
||||
{
|
||||
esp_hidh_event_t event = (esp_hidh_event_t)event_id;
|
||||
esp_hidh_event_data_t *param = (esp_hidh_event_data_t *)event_data;
|
||||
|
||||
switch (event) {
|
||||
case ESP_HIDH_OPEN_EVENT:
|
||||
if (param->open.status != ESP_OK) {
|
||||
esp_hidh_dev_t *dev = param->open.dev;
|
||||
if (dev) {
|
||||
esp_hidh_dev_free_inner(dev);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ESP_HIDH_CLOSE_EVENT:
|
||||
esp_hidh_dev_free_inner(param->close.dev);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_BT_NIMBLE_ENABLED */
|
||||
|
710
components/esp_hid/src/nimble_hidd.c
Normal file
710
components/esp_hid/src/nimble_hidd.c
Normal file
@ -0,0 +1,710 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "ble_hidd.h"
|
||||
#include "esp_hidd_private.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "nimble/nimble_opt.h"
|
||||
#include "host/ble_hs.h"
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/ble_hs_adv.h"
|
||||
#include "host/ble_hs_hci.h"
|
||||
#include "host/ble_att.h"
|
||||
#include "services/gap/ble_svc_gap.h"
|
||||
#include "services/gatt/ble_svc_gatt.h"
|
||||
#include "services/bas/ble_svc_bas.h"
|
||||
#include "services/hid/ble_svc_hid.h"
|
||||
#include "services/dis/ble_svc_dis.h"
|
||||
#include "services/sps/ble_svc_sps.h"
|
||||
|
||||
#if CONFIG_BT_NIMBLE_HID_SERVICE
|
||||
|
||||
static const char *TAG = "NIMBLE_HIDD";
|
||||
#define BLE_SVC_BAS_UUID16 0x180F
|
||||
|
||||
|
||||
typedef struct esp_ble_hidd_dev_s esp_ble_hidd_dev_t;
|
||||
// there can be only one BLE HID device
|
||||
static esp_ble_hidd_dev_t *s_dev = NULL;
|
||||
|
||||
typedef hidd_report_item_t hidd_le_report_item_t;
|
||||
|
||||
typedef struct {
|
||||
esp_hid_raw_report_map_t reports_map;
|
||||
uint8_t reports_len;
|
||||
hidd_le_report_item_t *reports;
|
||||
uint16_t hid_svc;
|
||||
uint16_t hid_control_handle;
|
||||
uint16_t hid_protocol_handle;
|
||||
} hidd_dev_map_t;
|
||||
|
||||
|
||||
|
||||
struct esp_ble_hidd_dev_s {
|
||||
esp_hidd_dev_t *dev;
|
||||
esp_event_loop_handle_t event_loop_handle;
|
||||
esp_hid_device_config_t config;
|
||||
uint16_t appearance;
|
||||
|
||||
bool connected;
|
||||
uint16_t conn_id;
|
||||
|
||||
uint8_t control; // 0x00 suspend, 0x01 suspend off
|
||||
uint8_t protocol; // 0x00 boot, 0x01 report
|
||||
|
||||
uint16_t bat_svc_handle;
|
||||
uint16_t info_svc_handle;
|
||||
struct ble_gatt_svc hid_incl_svc;
|
||||
|
||||
uint16_t bat_level_handle;
|
||||
uint8_t pnp[7]; /* something related to device info service */
|
||||
hidd_dev_map_t *devices;
|
||||
uint8_t devices_len;
|
||||
};
|
||||
|
||||
// HID Information characteristic value
|
||||
static const uint8_t hidInfo[4] = {
|
||||
0x11, 0x01, // bcdHID (USB HID version)
|
||||
0x00, // bCountryCode
|
||||
ESP_HID_FLAGS_REMOTE_WAKE | ESP_HID_FLAGS_NORMALLY_CONNECTABLE // Flags
|
||||
};
|
||||
|
||||
static int create_hid_db(int device_index)
|
||||
{
|
||||
int rc = 0;
|
||||
struct ble_svc_hid_params hparams = {0};
|
||||
int report_mode_rpts = 0;
|
||||
|
||||
/* fill hid info */
|
||||
memcpy(&hparams.hid_info, hidInfo, sizeof hparams.hid_info);
|
||||
|
||||
/* fill report map */
|
||||
memcpy(&hparams.report_map, (uint8_t *)s_dev->devices[device_index].reports_map.data, s_dev->devices[device_index].reports_map.len);
|
||||
hparams.report_map_len = s_dev->devices[device_index].reports_map.len;
|
||||
hparams.external_rpt_ref = BLE_SVC_BAS_UUID16;
|
||||
|
||||
/* fill protocol mode */
|
||||
hparams.proto_mode_present = 1;
|
||||
hparams.proto_mode = s_dev->protocol;
|
||||
|
||||
for (uint8_t i = 0; i < s_dev->devices[device_index].reports_len; i++) {
|
||||
hidd_le_report_item_t *report = &s_dev->devices[device_index].reports[i];
|
||||
if (report->protocol_mode == ESP_HID_PROTOCOL_MODE_REPORT) {
|
||||
/* only consider report mode reports, all boot mode reports will be registered by default */
|
||||
if (report->report_type == ESP_HID_REPORT_TYPE_INPUT) {
|
||||
/* Input Report */
|
||||
hparams.rpts[report_mode_rpts].type = ESP_HID_REPORT_TYPE_INPUT;
|
||||
} else if (report->report_type == ESP_HID_REPORT_TYPE_OUTPUT) {
|
||||
/* Output Report */
|
||||
hparams.rpts[report_mode_rpts].type = ESP_HID_REPORT_TYPE_OUTPUT;
|
||||
} else {
|
||||
/* Feature Report */
|
||||
hparams.rpts[report_mode_rpts].type = ESP_HID_REPORT_TYPE_FEATURE;
|
||||
}
|
||||
hparams.rpts[report_mode_rpts].id = report->report_id;
|
||||
report_mode_rpts++;
|
||||
} else {
|
||||
if (report->report_type == ESP_HID_REPORT_TYPE_INPUT) {
|
||||
/* Boot mode reports */
|
||||
if (report->usage == ESP_HID_USAGE_KEYBOARD) { //Boot Keyboard Input
|
||||
hparams.kbd_inp_present = 1;
|
||||
} else { //Boot Mouse Input
|
||||
hparams.mouse_inp_present = 1;
|
||||
}
|
||||
} else { //Boot Keyboard Output
|
||||
hparams.kbd_out_present = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
hparams.rpts_len = report_mode_rpts;
|
||||
/* Add service */
|
||||
rc = ble_svc_hid_add(hparams);
|
||||
if(rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int ble_hid_create_info_db() {
|
||||
int rc;
|
||||
|
||||
rc = 0;
|
||||
ble_svc_dis_init();
|
||||
uint8_t pnp_val[7] = {
|
||||
0x02, //0x1=BT, 0x2=USB
|
||||
s_dev->config.vendor_id & 0xFF, (s_dev->config.vendor_id >> 8) & 0xFF, //VID
|
||||
s_dev->config.product_id & 0xFF, (s_dev->config.product_id >> 8) & 0xFF, //PID
|
||||
s_dev->config.version & 0xFF, (s_dev->config.version >> 8) & 0xFF //VERSION
|
||||
};
|
||||
memcpy(s_dev->pnp, pnp_val, 7);
|
||||
ble_svc_dis_pnp_id_set((char *)s_dev->pnp);
|
||||
if (s_dev->config.manufacturer_name && s_dev->config.manufacturer_name[0]) {
|
||||
rc = ble_svc_dis_manufacturer_name_set(s_dev->config.manufacturer_name);
|
||||
}
|
||||
if (s_dev->config.serial_number && s_dev->config.serial_number[0]) {
|
||||
rc = ble_svc_dis_serial_number_set(s_dev->config.serial_number);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int nimble_hid_start_gatts(void)
|
||||
{
|
||||
int rc = ESP_OK;
|
||||
|
||||
ble_svc_gap_init();
|
||||
ble_svc_gatt_init();
|
||||
ble_svc_sps_init(0, 0); // initialize with 0
|
||||
ble_svc_bas_init();
|
||||
ble_hid_create_info_db();
|
||||
|
||||
for (uint8_t d = 0; d < s_dev->devices_len; d++) {
|
||||
rc = create_hid_db(d);
|
||||
if(rc != 0) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
/* init the hid svc */
|
||||
ble_svc_hid_init();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int nimble_hid_stop_gatts(esp_ble_hidd_dev_t *dev)
|
||||
{
|
||||
int rc = ESP_OK;
|
||||
|
||||
/* stop gatt database */
|
||||
ble_gatts_stop();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Identify the reports using the report map */
|
||||
static int ble_hid_init_config(esp_ble_hidd_dev_t *dev, const esp_hid_device_config_t *config)
|
||||
{
|
||||
memset((uint8_t *)(&dev->config), 0, sizeof(esp_hid_device_config_t));
|
||||
dev->config.vendor_id = config->vendor_id;
|
||||
dev->config.product_id = config->product_id;
|
||||
dev->config.version = config->version;
|
||||
if (config->device_name != NULL) {
|
||||
dev->config.device_name = strdup(config->device_name);
|
||||
}
|
||||
if (config->manufacturer_name != NULL) {
|
||||
dev->config.manufacturer_name = strdup(config->manufacturer_name);
|
||||
}
|
||||
if (config->serial_number != NULL) {
|
||||
dev->config.serial_number = strdup(config->serial_number);
|
||||
}
|
||||
dev->appearance = ESP_HID_APPEARANCE_GENERIC;
|
||||
|
||||
if (config->report_maps_len) {
|
||||
dev->devices = (hidd_dev_map_t *)malloc(config->report_maps_len * sizeof(hidd_dev_map_t));
|
||||
if (dev->devices == NULL) {
|
||||
ESP_LOGE(TAG, "devices malloc(%d) failed", config->report_maps_len);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
memset(dev->devices, 0, config->report_maps_len * sizeof(hidd_dev_map_t));
|
||||
dev->devices_len = config->report_maps_len;
|
||||
for (uint8_t d = 0; d < dev->devices_len; d++) {
|
||||
|
||||
//raw report map
|
||||
uint8_t *map = (uint8_t *)malloc(config->report_maps[d].len);
|
||||
if (map == NULL) {
|
||||
ESP_LOGE(TAG, "report map malloc(%d) failed", config->report_maps[d].len);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
memcpy(map, config->report_maps[d].data, config->report_maps[d].len);
|
||||
|
||||
dev->devices[d].reports_map.data = (const uint8_t *)map;
|
||||
dev->devices[d].reports_map.len = config->report_maps[d].len;
|
||||
|
||||
esp_hid_report_map_t *rmap = esp_hid_parse_report_map(config->report_maps[d].data, config->report_maps[d].len);
|
||||
if (rmap == NULL) {
|
||||
ESP_LOGE(TAG, "hid_parse_report_map[%d](%d) failed", d, config->report_maps[d].len);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
dev->appearance = rmap->appearance;
|
||||
dev->devices[d].reports_len = rmap->reports_len;
|
||||
dev->devices[d].reports = (hidd_le_report_item_t *)malloc(rmap->reports_len * sizeof(hidd_le_report_item_t));
|
||||
if (dev->devices[d].reports == NULL) {
|
||||
ESP_LOGE(TAG, "reports malloc(%d) failed", rmap->reports_len * sizeof(hidd_le_report_item_t));
|
||||
free(rmap);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
for (uint8_t r = 0; r < rmap->reports_len; r++) {
|
||||
dev->devices[d].reports[r].map_index = d;
|
||||
dev->devices[d].reports[r].report_id = rmap->reports[r].report_id;
|
||||
dev->devices[d].reports[r].protocol_mode = rmap->reports[r].protocol_mode;
|
||||
dev->devices[d].reports[r].report_type = rmap->reports[r].report_type;
|
||||
dev->devices[d].reports[r].usage = rmap->reports[r].usage;
|
||||
dev->devices[d].reports[r].value_len = rmap->reports[r].value_len;
|
||||
}
|
||||
free(rmap->reports);
|
||||
free(rmap);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int ble_hid_free_config(esp_ble_hidd_dev_t *dev)
|
||||
{
|
||||
for (uint8_t d = 0; d < dev->devices_len; d++) {
|
||||
free((void *)dev->devices[d].reports);
|
||||
free((void *)dev->devices[d].reports_map.data);
|
||||
}
|
||||
|
||||
free((void *)dev->devices);
|
||||
free((void *)dev->config.device_name);
|
||||
free((void *)dev->config.manufacturer_name);
|
||||
free((void *)dev->config.serial_number);
|
||||
if (dev->event_loop_handle != NULL) {
|
||||
esp_event_loop_delete(dev->event_loop_handle);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int nimble_hidd_dev_deinit(void *devp) {
|
||||
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
|
||||
if (!s_dev) {
|
||||
ESP_LOGE(TAG, "HID device profile already uninitialized");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (s_dev != dev) {
|
||||
ESP_LOGE(TAG, "Wrong HID device provided");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
s_dev = NULL;
|
||||
|
||||
nimble_hid_stop_gatts(dev);
|
||||
esp_event_post_to(dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_STOP_EVENT, NULL, 0, portMAX_DELAY);
|
||||
ble_hid_free_config(dev);
|
||||
free(dev);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static bool nimble_hidd_dev_connected(void *devp)
|
||||
{
|
||||
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
|
||||
return (dev != NULL && s_dev == dev && dev->connected);
|
||||
}
|
||||
|
||||
static int nimble_hidd_dev_battery_set(void *devp, uint8_t level)
|
||||
{
|
||||
int rc;
|
||||
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
|
||||
if (!dev || s_dev != dev) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (!dev->connected) {
|
||||
/* Return success if not yet connected */
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
rc = ble_svc_bas_battery_level_set(level);
|
||||
if (rc) {
|
||||
ESP_LOGE(TAG, "esp_ble_gatts_send_notify failed: %d", rc);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* if mode is NULL, find the first matching report */
|
||||
static hidd_le_report_item_t* find_report(uint8_t id, uint8_t type, uint8_t *mode) {
|
||||
hidd_le_report_item_t *rpt;
|
||||
for (uint8_t d = 0; d < s_dev->devices_len; d++) {
|
||||
for (uint8_t i = 0; i < s_dev->devices[d].reports_len; i++) {
|
||||
rpt = &s_dev->devices[d].reports[i];
|
||||
if(rpt->report_id == id && rpt->report_type == type && (!mode || (mode && *mode == rpt->protocol_mode))) {
|
||||
return rpt;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
static hidd_le_report_item_t* find_report_by_usage_and_type(uint8_t usage, uint8_t type, uint8_t *mode) {
|
||||
hidd_le_report_item_t *rpt;
|
||||
for (uint8_t d = 0; d < s_dev->devices_len; d++) {
|
||||
for (uint8_t i = 0; i < s_dev->devices[d].reports_len; i++) {
|
||||
rpt = &s_dev->devices[d].reports[i];
|
||||
if(rpt->usage == usage && rpt->report_type == type && (!mode || (mode && *mode == rpt->protocol_mode))) {
|
||||
return rpt;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int nimble_hidd_dev_input_set(void *devp, size_t index, size_t id, uint8_t *data, size_t length)
|
||||
{
|
||||
hidd_le_report_item_t *p_rpt;
|
||||
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
|
||||
int rc;
|
||||
struct os_mbuf *om;
|
||||
if (!dev || s_dev != dev) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (!dev->connected) {
|
||||
ESP_LOGE(TAG, "%s Device Not Connected", __func__);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* check the protocol mode */
|
||||
/* as the protocol mode is always present, its safe to read the characteristic */
|
||||
rc = ble_att_svr_read_local(s_dev->devices[index].hid_protocol_handle, &om);
|
||||
if(rc != 0) {
|
||||
ESP_LOGE(TAG, "Unable to fetch protocol_mode\n");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
rc = ble_hs_mbuf_to_flat(om, &dev->protocol, sizeof(dev->protocol), NULL);
|
||||
if(rc != 0) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* free the mbuf */
|
||||
os_mbuf_free_chain(om);
|
||||
om = NULL;
|
||||
|
||||
p_rpt = find_report(id, ESP_HID_REPORT_TYPE_INPUT, &dev->protocol);
|
||||
assert(p_rpt != NULL);
|
||||
om = ble_hs_mbuf_from_flat((void*)data, length);
|
||||
assert(om != NULL);
|
||||
/* NOTE : om is freed by stack */
|
||||
rc = ble_att_svr_write_local(p_rpt->handle, om);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "Write Input Report Failed: %d", rc);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int nimble_hidd_dev_feature_set(void *devp, size_t index, size_t id, uint8_t *data, size_t length)
|
||||
{
|
||||
/* This function is a no-op for now */
|
||||
hidd_le_report_item_t *p_rpt;
|
||||
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
|
||||
int rc;
|
||||
struct os_mbuf *om;
|
||||
if (!dev || s_dev != dev) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (!dev->connected) {
|
||||
ESP_LOGE(TAG, "%s Device Not Connected", __func__);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* check the protocol mode */
|
||||
/* as the protocol mode is always present, its safe to read the characteristic */
|
||||
rc = ble_att_svr_read_local(s_dev->devices[index].hid_protocol_handle, &om);
|
||||
if(rc != 0) {
|
||||
ESP_LOGE(TAG, "Unable to fetch protocol_mode\n");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
rc = ble_hs_mbuf_to_flat(om, &dev->protocol, sizeof(dev->protocol), NULL);
|
||||
if(rc != 0) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
/* free the mbuf */
|
||||
os_mbuf_free_chain(om);
|
||||
om = NULL;
|
||||
|
||||
p_rpt = find_report(id, ESP_HID_REPORT_TYPE_FEATURE, &dev->protocol);
|
||||
assert(p_rpt != NULL);
|
||||
om = ble_hs_mbuf_from_flat((void*)data, length);
|
||||
assert(om != NULL);
|
||||
/* NOTE : om is freed by stack*/
|
||||
rc = ble_att_svr_write_local(p_rpt->handle, om);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "Set Feature Report Failed: %d", rc);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int nimble_hidd_dev_event_handler_register(void *devp, esp_event_handler_t callback, esp_hidd_event_t event)
|
||||
{
|
||||
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
|
||||
if (!dev || s_dev != dev) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return esp_event_handler_register_with(dev->event_loop_handle, ESP_HIDD_EVENTS, event, callback, dev->dev);
|
||||
}
|
||||
|
||||
static int esp_ble_hidd_dev_event_handler_unregister(void *devp, esp_event_handler_t callback, esp_hidd_event_t event)
|
||||
{
|
||||
esp_ble_hidd_dev_t *dev = (esp_ble_hidd_dev_t *)devp;
|
||||
if (!dev || s_dev != dev) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return esp_event_handler_unregister_with(dev->event_loop_handle, ESP_HIDD_EVENTS, event, callback);
|
||||
}
|
||||
|
||||
static void ble_hidd_dev_free(void)
|
||||
{
|
||||
if (s_dev) {
|
||||
ble_hid_free_config(s_dev);
|
||||
free(s_dev);
|
||||
s_dev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int nimble_hid_gap_event(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
struct ble_gap_conn_desc desc;
|
||||
struct os_mbuf *om;
|
||||
uint8_t data;
|
||||
int rc;
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_CONNECT:
|
||||
/* A new connection was established or a connection attempt failed. */
|
||||
ESP_LOGD(TAG, "connection %s; status=%d",
|
||||
event->connect.status == 0 ? "established" : "failed",
|
||||
event->connect.status);
|
||||
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
|
||||
/* save connection handle */
|
||||
s_dev->connected = true;
|
||||
s_dev->conn_id = event->connect.conn_handle;
|
||||
esp_hidd_event_data_t cb_param = {
|
||||
.connect.dev = s_dev->dev,
|
||||
.connect.status = event->connect.status
|
||||
};
|
||||
|
||||
/* reset the protocol mode value */
|
||||
data = ESP_HID_PROTOCOL_MODE_REPORT;
|
||||
om = ble_hs_mbuf_from_flat(&data, 1);
|
||||
if(om == NULL) {
|
||||
ESP_LOGD(TAG, "No memory to allocate mbuf");
|
||||
}
|
||||
/* NOTE : om is freed by stack */
|
||||
for(int i = 0; i < s_dev->devices_len; i++) {
|
||||
rc = ble_att_svr_write_local(s_dev->devices[i].hid_protocol_handle, om);
|
||||
if (rc != 0) {
|
||||
ESP_LOGE(TAG, "Write on Protocol Mode Failed: %d", rc);
|
||||
}
|
||||
}
|
||||
|
||||
esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_CONNECT_EVENT,
|
||||
&cb_param, sizeof(esp_hidd_event_data_t), portMAX_DELAY);
|
||||
return 0;
|
||||
break;
|
||||
case BLE_GAP_EVENT_DISCONNECT:
|
||||
ESP_LOGD(TAG, "disconnect; reason=%d", event->disconnect.reason);
|
||||
|
||||
if (s_dev->connected) {
|
||||
s_dev->connected = false;
|
||||
esp_hidd_event_data_t cb_param = {0};
|
||||
cb_param.disconnect.dev = s_dev->dev;
|
||||
cb_param.disconnect.reason = event->disconnect.reason;
|
||||
esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_DISCONNECT_EVENT,
|
||||
&cb_param, sizeof(esp_hidd_event_data_t), portMAX_DELAY);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** service index is used to identify the hid service instance
|
||||
of the registered characteristic.
|
||||
Assuming the first instance of the hid service is registered first.
|
||||
Increment service index as the hid services get registered */
|
||||
static int service_index = -1;
|
||||
static void nimble_gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
|
||||
{
|
||||
char buf[BLE_UUID_STR_LEN];
|
||||
hidd_le_report_item_t *rpt = NULL;
|
||||
struct os_mbuf *om;
|
||||
uint16_t uuid16;
|
||||
uint16_t report_info;
|
||||
uint8_t report_type, report_id;
|
||||
uint16_t report_handle;
|
||||
uint8_t protocol_mode;
|
||||
int rc;
|
||||
switch (ctxt->op) {
|
||||
case BLE_GATT_REGISTER_OP_SVC:
|
||||
ESP_LOGD(TAG, "registered service %s with handle=%d",
|
||||
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
|
||||
ctxt->svc.handle);
|
||||
uuid16 = ble_uuid_u16(ctxt->svc.svc_def->uuid);
|
||||
if(uuid16 == BLE_SVC_HID_UUID16) {
|
||||
++service_index;
|
||||
s_dev->devices[service_index].hid_svc = ctxt->svc.handle;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case BLE_GATT_REGISTER_OP_CHR:
|
||||
ESP_LOGD(TAG, "registering characteristic %s with "
|
||||
"def_handle=%d val_handle=%d\n",
|
||||
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
|
||||
ctxt->chr.def_handle,
|
||||
ctxt->chr.val_handle);
|
||||
uuid16 = ble_uuid_u16(ctxt->chr.chr_def->uuid);
|
||||
if(uuid16 == BLE_SVC_HID_CHR_UUID16_HID_CTRL_PT) {
|
||||
/* assuming this characteristic is from the last registered hid service */
|
||||
s_dev->devices[service_index].hid_control_handle = ctxt->chr.val_handle;
|
||||
}
|
||||
if(uuid16 == BLE_SVC_HID_CHR_UUID16_PROTOCOL_MODE) {
|
||||
/* assuming this characteristic is from the last registered hid service */
|
||||
s_dev->devices[service_index].hid_protocol_handle = ctxt->chr.val_handle;
|
||||
}
|
||||
if(uuid16 == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_INP) {
|
||||
protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
|
||||
rpt = find_report_by_usage_and_type(ESP_HID_USAGE_KEYBOARD, ESP_HID_REPORT_TYPE_INPUT, &protocol_mode);
|
||||
if(rpt == NULL) {
|
||||
ESP_LOGE(TAG, "Unknown boot kbd input report registration");
|
||||
return;
|
||||
}
|
||||
rpt->handle = ctxt->chr.val_handle;
|
||||
}
|
||||
if(uuid16 == BLE_SVC_HID_CHR_UUID16_BOOT_KBD_OUT) {
|
||||
protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
|
||||
rpt = find_report_by_usage_and_type(ESP_HID_USAGE_KEYBOARD, ESP_HID_REPORT_TYPE_OUTPUT, &protocol_mode);
|
||||
if(rpt == NULL) {
|
||||
ESP_LOGE(TAG, "Unknown boot kbd output report registration");
|
||||
return;
|
||||
}
|
||||
rpt->handle = ctxt->chr.val_handle;
|
||||
}
|
||||
if(uuid16 == BLE_SVC_HID_CHR_UUID16_BOOT_MOUSE_INP) {
|
||||
protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT;
|
||||
rpt = find_report_by_usage_and_type(ESP_HID_USAGE_MOUSE, ESP_HID_REPORT_TYPE_INPUT, &protocol_mode);
|
||||
if(rpt == NULL) {
|
||||
ESP_LOGE(TAG, "Unknown boot mouse input report registration");
|
||||
return;
|
||||
}
|
||||
rpt->handle = ctxt->chr.val_handle;
|
||||
}
|
||||
break;
|
||||
|
||||
case BLE_GATT_REGISTER_OP_DSC:
|
||||
ESP_LOGD(TAG, "registering descriptor %s with handle=%d",
|
||||
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
|
||||
ctxt->dsc.handle);
|
||||
uuid16 = ble_uuid_u16(ctxt->dsc.dsc_def->uuid);
|
||||
if(uuid16 == BLE_SVC_HID_DSC_UUID16_RPT_REF) {
|
||||
rc = ble_att_svr_read_local(ctxt->dsc.handle, &om);
|
||||
assert(rc == 0);
|
||||
|
||||
ble_hs_mbuf_to_flat(om, &report_info, sizeof report_info, NULL);
|
||||
report_type = (uint8_t)((report_info & 0xFF00) >> 8);
|
||||
report_id = report_info & 0x00FF;
|
||||
report_handle = (*(uint16_t*)(ctxt->dsc.dsc_def->arg));
|
||||
protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT;
|
||||
rpt = find_report(report_id, report_type, &protocol_mode);
|
||||
assert(rpt != NULL);
|
||||
rpt->handle = report_handle;
|
||||
/* free the mbuf */
|
||||
os_mbuf_free_chain(om);
|
||||
om = NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void nimble_host_synced(void) {
|
||||
esp_event_post_to(s_dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_START_EVENT, NULL, 0, portMAX_DELAY);
|
||||
}
|
||||
|
||||
void nimble_host_reset(int reason)
|
||||
{
|
||||
MODLOG_DFLT(ERROR, "Resetting state; reason=%d\n", reason);
|
||||
}
|
||||
|
||||
static struct ble_gap_event_listener nimble_gap_event_listener;
|
||||
esp_err_t esp_ble_hidd_dev_init(esp_hidd_dev_t *dev_p, const esp_hid_device_config_t *config, esp_event_handler_t callback)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (s_dev) {
|
||||
ESP_LOGE(TAG, "HID device profile already initialized");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
s_dev = (esp_ble_hidd_dev_t *)calloc(1, sizeof(esp_ble_hidd_dev_t));
|
||||
if (s_dev == NULL) {
|
||||
ESP_LOGE(TAG, "HID device could not be allocated");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
// Reset the hid device target environment
|
||||
s_dev->control = ESP_HID_CONTROL_EXIT_SUSPEND;
|
||||
s_dev->protocol = ESP_HID_PROTOCOL_MODE_REPORT;
|
||||
s_dev->event_loop_handle = NULL;
|
||||
s_dev->dev = dev_p;
|
||||
|
||||
esp_event_loop_args_t event_task_args = {
|
||||
.queue_size = 5,
|
||||
.task_name = "ble_hidd_events",
|
||||
.task_priority = uxTaskPriorityGet(NULL),
|
||||
.task_stack_size = 2048,
|
||||
.task_core_id = tskNO_AFFINITY
|
||||
};
|
||||
rc = esp_event_loop_create(&event_task_args, &s_dev->event_loop_handle);
|
||||
if (rc != ESP_OK) {
|
||||
ESP_LOGE(TAG, "HID device event loop could not be created");
|
||||
ble_hidd_dev_free();
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ble_hid_init_config(s_dev, config);
|
||||
if (rc != ESP_OK) {
|
||||
ble_hidd_dev_free();
|
||||
return rc;
|
||||
}
|
||||
|
||||
dev_p->dev = s_dev;
|
||||
dev_p->connected = nimble_hidd_dev_connected;
|
||||
dev_p->deinit = nimble_hidd_dev_deinit;
|
||||
dev_p->battery_set = nimble_hidd_dev_battery_set;
|
||||
dev_p->input_set = nimble_hidd_dev_input_set;
|
||||
dev_p->feature_set = nimble_hidd_dev_feature_set;
|
||||
dev_p->event_handler_register = nimble_hidd_dev_event_handler_register;
|
||||
dev_p->event_handler_unregister = esp_ble_hidd_dev_event_handler_unregister;
|
||||
|
||||
rc = nimble_hidd_dev_event_handler_register(s_dev, esp_hidd_process_event_data_handler, ESP_EVENT_ANY_ID);
|
||||
if (rc != ESP_OK) {
|
||||
ble_hidd_dev_free();
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (callback != NULL) {
|
||||
rc = nimble_hidd_dev_event_handler_register(s_dev, callback, ESP_EVENT_ANY_ID);
|
||||
if (rc != ESP_OK) {
|
||||
ble_hidd_dev_free();
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
ble_hs_cfg.reset_cb = nimble_host_reset;
|
||||
ble_hs_cfg.sync_cb = nimble_host_synced;
|
||||
ble_hs_cfg.gatts_register_cb = nimble_gatt_svr_register_cb;
|
||||
rc = nimble_hid_start_gatts();
|
||||
if(rc != ESP_OK) {
|
||||
return rc;
|
||||
}
|
||||
ble_gap_event_listener_register(&nimble_gap_event_listener,
|
||||
nimble_hid_gap_event, NULL);
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif // CONFIG_BT_NIMBLE_HID_SERVICE
|
1020
components/esp_hid/src/nimble_hidh.c
Normal file
1020
components/esp_hid/src/nimble_hidh.c
Normal file
File diff suppressed because it is too large
Load Diff
30
examples/bluetooth/esp_hid_device/main/Kconfig.projbuild
Normal file
30
examples/bluetooth/esp_hid_device/main/Kconfig.projbuild
Normal file
@ -0,0 +1,30 @@
|
||||
menu "HID Example Configuration"
|
||||
choice EXAMPLE_HID_DEVICE_ROLE
|
||||
prompt "HID Device Role"
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
default EXAMPLE_MEDIA_ENABLE
|
||||
help
|
||||
Three Supported Roles for Device
|
||||
- Media Device
|
||||
- Keyboard
|
||||
- Mouse
|
||||
|
||||
config EXAMPLE_MEDIA_ENABLE
|
||||
select BT_NIMBLE_HID_SERVICE
|
||||
bool "Enable Media Device"
|
||||
|
||||
config EXAMPLE_KBD_ENABLE
|
||||
select BT_NIMBLE_HID_SERVICE
|
||||
bool "Enable Keyboard Device"
|
||||
|
||||
config EXAMPLE_MOUSE_ENABLE
|
||||
select BT_NIMBLE_HID_SERVICE
|
||||
bool "Enable Mouse Device"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_HID_DEVICE_ROLE
|
||||
int
|
||||
default 1 if EXAMPLE_MEDIA_ENABLE
|
||||
default 2 if EXAMPLE_KBD_ENABLE
|
||||
default 3 if EXAMPLE_MOUSE_ENABLE
|
||||
endmenu
|
@ -18,6 +18,12 @@
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_bt.h"
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "host/ble_hs.h"
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#else
|
||||
#include "esp_bt_defs.h"
|
||||
#if CONFIG_BT_BLE_ENABLED
|
||||
#include "esp_gap_ble_api.h"
|
||||
@ -26,6 +32,7 @@
|
||||
#endif
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_bt_device.h"
|
||||
#endif
|
||||
|
||||
#include "esp_hidd.h"
|
||||
#include "esp_hid_gap.h"
|
||||
@ -40,7 +47,7 @@ typedef struct
|
||||
uint8_t *buffer;
|
||||
} local_param_t;
|
||||
|
||||
#if CONFIG_BT_BLE_ENABLED
|
||||
#if CONFIG_BT_BLE_ENABLED || CONFIG_BT_NIMBLE_ENABLED
|
||||
static local_param_t s_ble_hid_param = {0};
|
||||
|
||||
const unsigned char mediaReportMap[] = {
|
||||
@ -102,19 +109,287 @@ const unsigned char mediaReportMap[] = {
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
};
|
||||
#if CONFIG_EXAMPLE_HID_DEVICE_ROLE && CONFIG_EXAMPLE_HID_DEVICE_ROLE == 3
|
||||
const unsigned char mouseReportMap[] = {
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
0x09, 0x02, // USAGE (Mouse)
|
||||
0xa1, 0x01, // COLLECTION (Application)
|
||||
|
||||
0x09, 0x01, // USAGE (Pointer)
|
||||
0xa1, 0x00, // COLLECTION (Physical)
|
||||
|
||||
0x05, 0x09, // USAGE_PAGE (Button)
|
||||
0x19, 0x01, // USAGE_MINIMUM (Button 1)
|
||||
0x29, 0x03, // USAGE_MAXIMUM (Button 3)
|
||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||
0x25, 0x01, // LOGICAL_MAXIMUM (1)
|
||||
0x95, 0x03, // REPORT_COUNT (3)
|
||||
0x75, 0x01, // REPORT_SIZE (1)
|
||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||
0x95, 0x01, // REPORT_COUNT (1)
|
||||
0x75, 0x05, // REPORT_SIZE (5)
|
||||
0x81, 0x03, // INPUT (Cnst,Var,Abs)
|
||||
|
||||
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
|
||||
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)
|
||||
|
||||
0xc0, // END_COLLECTION
|
||||
0xc0 // END_COLLECTION
|
||||
};
|
||||
// send the buttons, change in x, and change in y
|
||||
void send_mouse(uint8_t buttons, char dx, char dy, char wheel)
|
||||
{
|
||||
static uint8_t buffer[4] = {0};
|
||||
buffer[0] = buttons;
|
||||
buffer[1] = dx;
|
||||
buffer[2] = dy;
|
||||
buffer[3] = wheel;
|
||||
esp_hidd_dev_input_set(s_ble_hid_param.hid_dev, 0, 0, buffer, 4);
|
||||
}
|
||||
|
||||
void ble_hid_demo_task_mouse(void *pvParameters)
|
||||
{
|
||||
static const char* help_string = "########################################################################\n"\
|
||||
"BT hid mouse demo usage:\n"\
|
||||
"You can input these value to simulate mouse: 'q', 'w', 'e', 'a', 's', 'd', 'h'\n"\
|
||||
"q -- click the left key\n"\
|
||||
"w -- move up\n"\
|
||||
"e -- click the right key\n"\
|
||||
"a -- move left\n"\
|
||||
"s -- move down\n"\
|
||||
"d -- move right\n"\
|
||||
"h -- show the help\n"\
|
||||
"########################################################################\n";
|
||||
printf("%s\n", help_string);
|
||||
char c;
|
||||
while (1) {
|
||||
c = fgetc(stdin);
|
||||
switch (c) {
|
||||
case 'q':
|
||||
send_mouse(1, 0, 0, 0);
|
||||
break;
|
||||
case 'w':
|
||||
send_mouse(0, 0, -10, 0);
|
||||
break;
|
||||
case 'e':
|
||||
send_mouse(2, 0, 0, 0);
|
||||
break;
|
||||
case 'a':
|
||||
send_mouse(0, -10, 0, 0);
|
||||
break;
|
||||
case 's':
|
||||
send_mouse(0, 0, 10, 0);
|
||||
break;
|
||||
case 'd':
|
||||
send_mouse(0, 10, 0, 0);
|
||||
break;
|
||||
case 'h':
|
||||
printf("%s\n", help_string);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_HID_DEVICE_ROLE && CONFIG_EXAMPLE_HID_DEVICE_ROLE == 2
|
||||
#define CASE(a, b, c) \
|
||||
case a: \
|
||||
buffer[0] = b; \
|
||||
buffer[2] = c; \
|
||||
break;\
|
||||
|
||||
// USB keyboard codes
|
||||
#define USB_HID_MODIFIER_LEFT_CTRL 0x01
|
||||
#define USB_HID_MODIFIER_LEFT_SHIFT 0x02
|
||||
#define USB_HID_MODIFIER_LEFT_ALT 0x04
|
||||
#define USB_HID_MODIFIER_RIGHT_CTRL 0x10
|
||||
#define USB_HID_MODIFIER_RIGHT_SHIFT 0x20
|
||||
#define USB_HID_MODIFIER_RIGHT_ALT 0x40
|
||||
|
||||
#define USB_HID_SPACE 0x2C
|
||||
#define USB_HID_DOT 0x37
|
||||
#define USB_HID_NEWLINE 0x28
|
||||
#define USB_HID_FSLASH 0x38
|
||||
#define USB_HID_BSLASH 0x31
|
||||
#define USB_HID_COMMA 0x36
|
||||
#define USB_HID_DOT 0x37
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
static void char_to_code(uint8_t *buffer, char ch)
|
||||
{
|
||||
// Check if lower or upper case
|
||||
if(ch >= 'a' && ch <= 'z')
|
||||
{
|
||||
buffer[0] = 0;
|
||||
// convert ch to HID letter, starting at a = 4
|
||||
buffer[2] = (uint8_t)(4 + (ch - 'a'));
|
||||
}
|
||||
else if(ch >= 'A' && ch <= 'Z')
|
||||
{
|
||||
// Add left shift
|
||||
buffer[0] = USB_HID_MODIFIER_LEFT_SHIFT;
|
||||
// convert ch to lower case
|
||||
ch = ch - ('A'-'a');
|
||||
// convert ch to HID letter, starting at a = 4
|
||||
buffer[2] = (uint8_t)(4 + (ch - 'a'));
|
||||
}
|
||||
else if(ch >= '0' && ch <= '9') // Check if number
|
||||
{
|
||||
buffer[0] = 0;
|
||||
// convert ch to HID number, starting at 1 = 30, 0 = 39
|
||||
if(ch == '0')
|
||||
{
|
||||
buffer[2] = 39;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[2] = (uint8_t)(30 + (ch - '1'));
|
||||
}
|
||||
}
|
||||
else // not a letter nor a number
|
||||
{
|
||||
switch(ch)
|
||||
{
|
||||
CASE(' ', 0, USB_HID_SPACE);
|
||||
CASE('.', 0,USB_HID_DOT);
|
||||
CASE('\n', 0, USB_HID_NEWLINE);
|
||||
CASE('?', USB_HID_MODIFIER_LEFT_SHIFT, USB_HID_FSLASH);
|
||||
CASE('/', 0 ,USB_HID_FSLASH);
|
||||
CASE('\\', 0, USB_HID_BSLASH);
|
||||
CASE('|', USB_HID_MODIFIER_LEFT_SHIFT, USB_HID_BSLASH);
|
||||
CASE(',', 0, USB_HID_COMMA);
|
||||
CASE('<', USB_HID_MODIFIER_LEFT_SHIFT, USB_HID_COMMA);
|
||||
CASE('>', USB_HID_MODIFIER_LEFT_SHIFT, USB_HID_COMMA);
|
||||
CASE('@', USB_HID_MODIFIER_LEFT_SHIFT, 31);
|
||||
CASE('!', USB_HID_MODIFIER_LEFT_SHIFT, 30);
|
||||
CASE('#', USB_HID_MODIFIER_LEFT_SHIFT, 32);
|
||||
CASE('$', USB_HID_MODIFIER_LEFT_SHIFT, 33);
|
||||
CASE('%', USB_HID_MODIFIER_LEFT_SHIFT, 34);
|
||||
CASE('^', USB_HID_MODIFIER_LEFT_SHIFT,35);
|
||||
CASE('&', USB_HID_MODIFIER_LEFT_SHIFT, 36);
|
||||
CASE('*', USB_HID_MODIFIER_LEFT_SHIFT, 37);
|
||||
CASE('(', USB_HID_MODIFIER_LEFT_SHIFT, 38);
|
||||
CASE(')', USB_HID_MODIFIER_LEFT_SHIFT, 39);
|
||||
CASE('-', 0, 0x2D);
|
||||
CASE('_', USB_HID_MODIFIER_LEFT_SHIFT, 0x2D);
|
||||
CASE('=', 0, 0x2E);
|
||||
CASE('+', USB_HID_MODIFIER_LEFT_SHIFT, 39);
|
||||
CASE(8, 0, 0x2A); // backspace
|
||||
CASE('\t', 0, 0x2B);
|
||||
default:
|
||||
buffer[0] = 0;
|
||||
buffer[2] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void send_keyboard(char c)
|
||||
{
|
||||
static uint8_t buffer[8] = {0};
|
||||
char_to_code(buffer, c);
|
||||
esp_hidd_dev_input_set(s_ble_hid_param.hid_dev, 0, 1, buffer, 8);
|
||||
/* send the keyrelease event with sufficient delay */
|
||||
vTaskDelay(50 / portTICK_PERIOD_MS);
|
||||
memset(buffer, 0, sizeof(uint8_t) * 8);
|
||||
esp_hidd_dev_input_set(s_ble_hid_param.hid_dev, 0, 1, buffer, 8);
|
||||
}
|
||||
|
||||
void ble_hid_demo_task_kbd(void *pvParameters)
|
||||
{
|
||||
static const char* help_string = "########################################################################\n"\
|
||||
"BT hid keyboard demo usage:\n"\
|
||||
"########################################################################\n";
|
||||
/* TODO : Add support for function keys and ctrl, alt, esc, etc. */
|
||||
printf("%s\n", help_string);
|
||||
char c;
|
||||
while (1) {
|
||||
c = fgetc(stdin);
|
||||
|
||||
if(c != 255) {
|
||||
send_keyboard(c);
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
static esp_hid_raw_report_map_t ble_report_maps[] = {
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED || CONFIG_EXAMPLE_HID_DEVICE_ROLE == 1
|
||||
/* This block is compiled for bluedroid as well */
|
||||
{
|
||||
.data = mediaReportMap,
|
||||
.len = sizeof(mediaReportMap)
|
||||
}
|
||||
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE && CONFIG_EXAMPLE_HID_DEVICE_ROLE == 2
|
||||
{
|
||||
.data = keyboardReportMap,
|
||||
.len = sizeof(keyboardReportMap)
|
||||
},
|
||||
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE && CONFIG_EXAMPLE_HID_DEVICE_ROLE == 3
|
||||
{
|
||||
.data = mouseReportMap,
|
||||
.len = sizeof(mouseReportMap)
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static esp_hid_device_config_t ble_hid_config = {
|
||||
.vendor_id = 0x16C0,
|
||||
.product_id = 0x05DF,
|
||||
.version = 0x0100,
|
||||
#if CONFIG_EXAMPLE_HID_DEVICE_ROLE == 2
|
||||
.device_name = "ESP Keyboard",
|
||||
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE == 3
|
||||
.device_name = "ESP Mouse",
|
||||
#else
|
||||
.device_name = "ESP BLE HID2",
|
||||
#endif
|
||||
.manufacturer_name = "Espressif",
|
||||
.serial_number = "1234567890",
|
||||
.report_maps = ble_report_maps,
|
||||
@ -272,6 +547,7 @@ void esp_hidd_send_consumer_value(uint8_t key_cmd, bool key_pressed)
|
||||
return;
|
||||
}
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED || CONFIG_EXAMPLE_HID_DEVICE_ROLE == 1
|
||||
void ble_hid_demo_task(void *pvParameters)
|
||||
{
|
||||
static bool send_volum_up = false;
|
||||
@ -290,6 +566,7 @@ void ble_hid_demo_task(void *pvParameters)
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ble_hid_task_start_up(void)
|
||||
{
|
||||
@ -297,8 +574,23 @@ void ble_hid_task_start_up(void)
|
||||
// Task already exists
|
||||
return;
|
||||
}
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
/* Executed for bluedroid */
|
||||
xTaskCreate(ble_hid_demo_task, "ble_hid_demo_task", 2 * 1024, NULL, configMAX_PRIORITIES - 3,
|
||||
&s_ble_hid_param.task_hdl);
|
||||
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE == 1
|
||||
xTaskCreate(ble_hid_demo_task, "ble_hid_demo_task", 3 * 1024, NULL, configMAX_PRIORITIES - 3,
|
||||
&s_ble_hid_param.task_hdl);
|
||||
|
||||
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE == 2
|
||||
/* Nimble Specific */
|
||||
xTaskCreate(ble_hid_demo_task_kbd, "ble_hid_demo_task_kbd", 3 * 1024, NULL, configMAX_PRIORITIES - 3,
|
||||
&s_ble_hid_param.task_hdl);
|
||||
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE == 3
|
||||
/* Nimble Specific */
|
||||
xTaskCreate(ble_hid_demo_task_mouse, "ble_hid_demo_task_mouse", 3 * 1024, NULL, configMAX_PRIORITIES - 3,
|
||||
&s_ble_hid_param.task_hdl);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ble_hid_task_shut_down(void)
|
||||
@ -557,6 +849,18 @@ static void bt_hidd_event_callback(void *handler_args, esp_event_base_t base, in
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
void ble_hid_device_host_task(void *param)
|
||||
{
|
||||
ESP_LOGI(TAG, "BLE Host Task Started");
|
||||
/* This function will return only when nimble_port_stop() is executed */
|
||||
nimble_port_run();
|
||||
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
void ble_store_config_init(void);
|
||||
#endif
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
@ -575,14 +879,21 @@ void app_main(void)
|
||||
ret = esp_hid_gap_init(HID_DEV_MODE);
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
#if CONFIG_BT_BLE_ENABLED
|
||||
#if CONFIG_BT_BLE_ENABLED || CONFIG_BT_NIMBLE_ENABLED
|
||||
#if CONFIG_EXAMPLE_HID_DEVICE_ROLE == 2
|
||||
ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_KEYBOARD, ble_hid_config.device_name);
|
||||
#elif CONFIG_EXAMPLE_HID_DEVICE_ROLE == 3
|
||||
ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_MOUSE, ble_hid_config.device_name);
|
||||
#else
|
||||
ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_GENERIC, ble_hid_config.device_name);
|
||||
#endif
|
||||
ESP_ERROR_CHECK( ret );
|
||||
|
||||
#if CONFIG_BT_BLE_ENABLED
|
||||
if ((ret = esp_ble_gatts_register_callback(esp_hidd_gatts_event_handler)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "GATTS register callback failed: %d", ret);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
ESP_LOGI(TAG, "setting ble device");
|
||||
ESP_ERROR_CHECK(
|
||||
esp_hidd_dev_init(&ble_hid_config, ESP_HID_TRANSPORT_BLE, ble_hidd_event_callback, &s_ble_hid_param.hid_dev));
|
||||
@ -600,4 +911,15 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(
|
||||
esp_hidd_dev_init(&bt_hid_config, ESP_HID_TRANSPORT_BT, bt_hidd_event_callback, &s_bt_hid_param.hid_dev));
|
||||
#endif
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
/* XXX Need to have template for store */
|
||||
ble_store_config_init();
|
||||
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
/* Starting nimble task after gatts is initialized*/
|
||||
ret = esp_nimble_enable(ble_hid_device_host_task);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_nimble_enable failed: %d", ret);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -15,17 +15,28 @@
|
||||
|
||||
#include "esp_hid_gap.h"
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "host/ble_hs.h"
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/ble_hs_adv.h"
|
||||
#include "nimble/ble.h"
|
||||
#include "host/ble_sm.h"
|
||||
#endif
|
||||
|
||||
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"};
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
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;
|
||||
#endif
|
||||
|
||||
static SemaphoreHandle_t bt_hidh_cb_semaphore = NULL;
|
||||
#define WAIT_BT_CB() xSemaphoreTake(bt_hidh_cb_semaphore, portMAX_DELAY)
|
||||
@ -37,6 +48,7 @@ static SemaphoreHandle_t ble_hidh_cb_semaphore = NULL;
|
||||
|
||||
#define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(*a))
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
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"};
|
||||
@ -64,6 +76,7 @@ const char *bt_gap_evt_str(uint8_t event)
|
||||
}
|
||||
return bt_gap_evt_names[event];
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_BLE_ENABLED
|
||||
const char *esp_ble_key_type_str(esp_ble_key_type_t key_type)
|
||||
@ -106,6 +119,7 @@ const char *esp_ble_key_type_str(esp_ble_key_type_t key_type)
|
||||
}
|
||||
#endif /* CONFIG_BT_BLE_ENABLED */
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
void esp_hid_scan_results_free(esp_hid_scan_result_t *results)
|
||||
{
|
||||
esp_hid_scan_result_t *r = NULL;
|
||||
@ -118,6 +132,7 @@ void esp_hid_scan_results_free(esp_hid_scan_result_t *results)
|
||||
free(r);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (CONFIG_BT_HID_DEVICE_ENABLED || CONFIG_BT_BLE_ENABLED)
|
||||
static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_result_t *results)
|
||||
@ -223,6 +238,7 @@ static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type
|
||||
}
|
||||
#endif /* CONFIG_BT_BLE_ENABLED */
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
void print_uuid(esp_bt_uuid_t *uuid)
|
||||
{
|
||||
if (uuid->len == ESP_UUID_LEN_16) {
|
||||
@ -238,6 +254,7 @@ void print_uuid(esp_bt_uuid_t *uuid)
|
||||
uuid->uuid.uuid128[13], uuid->uuid.uuid128[14], uuid->uuid.uuid128[15]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_HID_DEVICE_ENABLED
|
||||
static void handle_bt_device_result(struct disc_res_param *disc_res)
|
||||
@ -588,6 +605,7 @@ static esp_err_t start_ble_scan(uint32_t seconds)
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name)
|
||||
{
|
||||
|
||||
@ -665,7 +683,7 @@ esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
esp_err_t esp_hid_ble_gap_adv_start(void)
|
||||
{
|
||||
static esp_ble_adv_params_t hidd_adv_params = {
|
||||
@ -679,11 +697,212 @@ esp_err_t esp_hid_ble_gap_adv_start(void)
|
||||
return esp_ble_gap_start_advertising(&hidd_adv_params);
|
||||
}
|
||||
#endif /* CONFIG_BT_BLE_ENABLED */
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
static struct ble_hs_adv_fields fields;
|
||||
#define GATT_SVR_SVC_HID_UUID 0x1812
|
||||
esp_err_t esp_hid_ble_gap_adv_init(uint16_t appearance, const char *device_name)
|
||||
{
|
||||
ble_uuid16_t *uuid16, *uuid16_1;
|
||||
/**
|
||||
* Set the advertisement data included in our advertisements:
|
||||
* o Flags (indicates advertisement type and other general info).
|
||||
* o Advertising tx power.
|
||||
* o Device name.
|
||||
* o 16-bit service UUIDs (HID).
|
||||
*/
|
||||
|
||||
memset(&fields, 0, sizeof fields);
|
||||
|
||||
/* Advertise two flags:
|
||||
* o Discoverability in forthcoming advertisement (general)
|
||||
* o BLE-only (BR/EDR unsupported).
|
||||
*/
|
||||
fields.flags = BLE_HS_ADV_F_DISC_GEN |
|
||||
BLE_HS_ADV_F_BREDR_UNSUP;
|
||||
|
||||
/* Indicate that the TX power level field should be included; have the
|
||||
* stack fill this value automatically. This is done by assigning the
|
||||
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
|
||||
*/
|
||||
fields.tx_pwr_lvl_is_present = 1;
|
||||
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
|
||||
|
||||
fields.name = (uint8_t *)device_name;
|
||||
fields.name_len = strlen(device_name);
|
||||
fields.name_is_complete = 1;
|
||||
|
||||
uuid16 = (ble_uuid16_t *)malloc(sizeof(ble_uuid16_t));
|
||||
uuid16_1 = (ble_uuid16_t[]) {
|
||||
BLE_UUID16_INIT(GATT_SVR_SVC_HID_UUID)
|
||||
};
|
||||
memcpy(uuid16, uuid16_1, sizeof(ble_uuid16_t));
|
||||
fields.uuids16 = uuid16;
|
||||
fields.num_uuids16 = 1;
|
||||
fields.uuids16_is_complete = 1;
|
||||
|
||||
/* Initialize the security configuration */
|
||||
ble_hs_cfg.sm_io_cap = BLE_SM_IO_CAP_DISP_ONLY;
|
||||
ble_hs_cfg.sm_bonding = 1;
|
||||
ble_hs_cfg.sm_mitm = 1;
|
||||
ble_hs_cfg.sm_sc = 1;
|
||||
ble_hs_cfg.sm_our_key_dist = BLE_SM_PAIR_KEY_DIST_ID | BLE_SM_PAIR_KEY_DIST_ENC;
|
||||
ble_hs_cfg.sm_their_key_dist |= BLE_SM_PAIR_KEY_DIST_ID | BLE_SM_PAIR_KEY_DIST_ENC;
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
nimble_hid_gap_event(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
struct ble_gap_conn_desc desc;
|
||||
int rc;
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_CONNECT:
|
||||
/* A new connection was established or a connection attempt failed. */
|
||||
ESP_LOGI(TAG, "connection %s; status=%d",
|
||||
event->connect.status == 0 ? "established" : "failed",
|
||||
event->connect.status);
|
||||
return 0;
|
||||
break;
|
||||
case BLE_GAP_EVENT_DISCONNECT:
|
||||
ESP_LOGI(TAG, "disconnect; reason=%d", event->disconnect.reason);
|
||||
|
||||
return 0;
|
||||
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||
/* The central has updated the connection parameters. */
|
||||
ESP_LOGI(TAG, "connection updated; status=%d",
|
||||
event->conn_update.status);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_ADV_COMPLETE:
|
||||
ESP_LOGI(TAG, "advertise complete; reason=%d",
|
||||
event->adv_complete.reason);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_SUBSCRIBE:
|
||||
ESP_LOGI(TAG, "subscribe event; conn_handle=%d attr_handle=%d "
|
||||
"reason=%d prevn=%d curn=%d previ=%d curi=%d\n",
|
||||
event->subscribe.conn_handle,
|
||||
event->subscribe.attr_handle,
|
||||
event->subscribe.reason,
|
||||
event->subscribe.prev_notify,
|
||||
event->subscribe.cur_notify,
|
||||
event->subscribe.prev_indicate,
|
||||
event->subscribe.cur_indicate);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_MTU:
|
||||
ESP_LOGI(TAG, "mtu update event; conn_handle=%d cid=%d mtu=%d",
|
||||
event->mtu.conn_handle,
|
||||
event->mtu.channel_id,
|
||||
event->mtu.value);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE:
|
||||
/* Encryption has been enabled or disabled for this connection. */
|
||||
MODLOG_DFLT(INFO, "encryption change event; status=%d ",
|
||||
event->enc_change.status);
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_NOTIFY_TX:
|
||||
MODLOG_DFLT(INFO, "notify_tx event; conn_handle=%d attr_handle=%d "
|
||||
"status=%d is_indication=%d",
|
||||
event->notify_tx.conn_handle,
|
||||
event->notify_tx.attr_handle,
|
||||
event->notify_tx.status,
|
||||
event->notify_tx.indication);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_REPEAT_PAIRING:
|
||||
/* We already have a bond with the peer, but it is attempting to
|
||||
* establish a new secure link. This app sacrifices security for
|
||||
* convenience: just throw away the old bond and accept the new link.
|
||||
*/
|
||||
|
||||
/* Delete the old bond. */
|
||||
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
ble_store_util_delete_peer(&desc.peer_id_addr);
|
||||
|
||||
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
|
||||
* continue with the pairing operation.
|
||||
*/
|
||||
return BLE_GAP_REPEAT_PAIRING_RETRY;
|
||||
|
||||
case BLE_GAP_EVENT_PASSKEY_ACTION:
|
||||
ESP_LOGI(TAG, "PASSKEY_ACTION_EVENT started");
|
||||
struct ble_sm_io pkey = {0};
|
||||
int key = 0;
|
||||
|
||||
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = 123456; // This is the passkey to be entered on peer
|
||||
ESP_LOGI(TAG, "Enter passkey %" PRIu32 "on the peer side", pkey.passkey);
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(TAG, "ble_sm_inject_io result: %d", rc);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
|
||||
ESP_LOGI(TAG, "Accepting passkey..");
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.numcmp_accept = key;
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(TAG, "ble_sm_inject_io result: %d", rc);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
|
||||
static uint8_t tem_oob[16] = {0};
|
||||
pkey.action = event->passkey.params.action;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
pkey.oob[i] = tem_oob[i];
|
||||
}
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(TAG, "ble_sm_inject_io result: %d", rc);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
|
||||
ESP_LOGI(TAG, "Input not supported passing -> 123456");
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = 123456;
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(TAG, "ble_sm_inject_io result: %d", rc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
esp_err_t esp_hid_ble_gap_adv_start(void)
|
||||
{
|
||||
int rc;
|
||||
struct ble_gap_adv_params adv_params;
|
||||
/* maximum possible duration for hid device(180s) */
|
||||
int32_t adv_duration_ms = 180000;
|
||||
|
||||
rc = ble_gap_adv_set_fields(&fields);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
/* Begin advertising. */
|
||||
memset(&adv_params, 0, sizeof adv_params);
|
||||
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
|
||||
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
|
||||
adv_params.itvl_min = BLE_GAP_ADV_ITVL_MS(30);/* Recommended interval 30ms to 50ms */
|
||||
adv_params.itvl_max = BLE_GAP_ADV_ITVL_MS(50);
|
||||
rc = ble_gap_adv_start(BLE_OWN_ADDR_PUBLIC, NULL, adv_duration_ms,
|
||||
&adv_params, nimble_hid_gap_event, NULL);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* CONTROLLER INIT
|
||||
* */
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
static esp_err_t init_low_level(uint8_t mode)
|
||||
{
|
||||
esp_err_t ret;
|
||||
@ -745,6 +964,43 @@ static esp_err_t init_low_level(uint8_t mode)
|
||||
#endif /* CONFIG_BT_BLE_ENABLED */
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
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 CONFIG_IDF_TARGET_ESP32
|
||||
bt_cfg.mode = mode;
|
||||
#endif
|
||||
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_nimble_init();
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_nimble_init failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t esp_hid_gap_init(uint8_t mode)
|
||||
{
|
||||
@ -785,6 +1041,7 @@ esp_err_t esp_hid_gap_init(uint8_t mode)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
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) {
|
||||
@ -825,3 +1082,4 @@ esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_resul
|
||||
ble_scan_results = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -20,6 +20,8 @@
|
||||
#endif
|
||||
#elif CONFIG_BT_BLE_ENABLED
|
||||
#define HID_DEV_MODE HIDD_BLE_MODE
|
||||
#elif CONFIG_BT_NIMBLE_ENABLED
|
||||
#define HID_DEV_MODE HIDD_BLE_MODE
|
||||
#else
|
||||
#define HID_DEV_MODE HIDD_IDLE_MODE
|
||||
#endif
|
||||
@ -28,9 +30,11 @@
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_bt.h"
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "esp_bt_defs.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
#endif
|
||||
#include "esp_hid_common.h"
|
||||
#if CONFIG_BT_BLE_ENABLED
|
||||
#include "esp_gattc_api.h"
|
||||
@ -42,6 +46,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
typedef struct esp_hidh_scan_result_s {
|
||||
struct esp_hidh_scan_result_s *next;
|
||||
|
||||
@ -62,16 +67,17 @@ typedef struct esp_hidh_scan_result_s {
|
||||
};
|
||||
} 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);
|
||||
const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type);
|
||||
void print_uuid(esp_bt_uuid_t *uuid);
|
||||
#endif
|
||||
|
||||
esp_err_t esp_hid_gap_init(uint8_t mode);
|
||||
|
||||
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
|
||||
|
9
examples/bluetooth/esp_hid_host/main/Kconfig.projbuild
Normal file
9
examples/bluetooth/esp_hid_host/main/Kconfig.projbuild
Normal file
@ -0,0 +1,9 @@
|
||||
menu "HID Example Configuration"
|
||||
config EXAMPLE_HID_HOST_ENABLED
|
||||
bool "Enable Example HID Host"
|
||||
depends on BT_NIMBLE_ENABLED
|
||||
default y
|
||||
select BT_NIMBLE_HID_SERVICE
|
||||
help
|
||||
This enables Nimble HID Host
|
||||
endmenu
|
@ -14,6 +14,15 @@
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#include "esp_hid_gap.h"
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "host/ble_hs.h"
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "host/ble_gap.h"
|
||||
#include "host/ble_hs_adv.h"
|
||||
#include "nimble/ble.h"
|
||||
#include "host/ble_sm.h"
|
||||
#define BLE_HID_SVC_UUID 0x1812 /* HID Service*/
|
||||
#endif
|
||||
|
||||
static const char *TAG = "ESP_HID_GAP";
|
||||
|
||||
@ -40,6 +49,7 @@ static SemaphoreHandle_t ble_hidh_cb_semaphore = NULL;
|
||||
|
||||
#define SIZEOF_ARRAY(a) (sizeof(a)/sizeof(*a))
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
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"};
|
||||
@ -67,7 +77,7 @@ const char *bt_gap_evt_str(uint8_t event)
|
||||
}
|
||||
return bt_gap_evt_names[event];
|
||||
}
|
||||
|
||||
#endif
|
||||
#if CONFIG_BT_BLE_ENABLED
|
||||
const char *esp_ble_key_type_str(esp_ble_key_type_t key_type)
|
||||
{
|
||||
@ -136,6 +146,19 @@ static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_r
|
||||
}
|
||||
#endif /* (CONFIG_BT_HID_HOST_ENABLED || CONFIG_BT_BLE_ENABLED) */
|
||||
|
||||
#if (CONFIG_BT_NIMBLE_ENABLED)
|
||||
static esp_hid_scan_result_t *find_scan_result(const uint8_t *bda, esp_hid_scan_result_t *results)
|
||||
{
|
||||
esp_hid_scan_result_t *r = results;
|
||||
while (r) {
|
||||
if (memcmp(bda, r->bda, sizeof(r->bda)) == 0) {
|
||||
return r;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
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)
|
||||
{
|
||||
@ -226,6 +249,43 @@ static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type
|
||||
}
|
||||
#endif /* CONFIG_BT_BLE_ENABLED */
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
static void add_ble_scan_result(const uint8_t *bda, uint8_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(r->bda));
|
||||
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++;
|
||||
}
|
||||
#endif /* CONFIG_BT_BLE_ENABLED */
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
void print_uuid(esp_bt_uuid_t *uuid)
|
||||
{
|
||||
if (uuid->len == ESP_UUID_LEN_16) {
|
||||
@ -572,6 +632,7 @@ static void ble_gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_p
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static esp_err_t init_ble_gap(void)
|
||||
{
|
||||
@ -705,6 +766,252 @@ esp_err_t esp_hid_ble_gap_adv_start(void)
|
||||
* CONTROLLER INIT
|
||||
* */
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
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 CONFIG_IDF_TARGET_ESP32
|
||||
bt_cfg.mode = mode;
|
||||
#endif
|
||||
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_nimble_init();
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_nimble_init failed: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void handle_ble_device_result(const struct ble_gap_disc_desc *disc)
|
||||
{
|
||||
int rc;
|
||||
struct ble_hs_adv_fields fields;
|
||||
uint16_t appearance;
|
||||
uint8_t adv_name[BLE_HS_ADV_MAX_SZ];
|
||||
uint8_t adv_name_len = 0;
|
||||
|
||||
appearance = 0; /* silent warnings for now */
|
||||
rc = ble_hs_adv_parse_fields(&fields, disc->data, disc->length_data);
|
||||
if(rc != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fields.name != NULL) {
|
||||
assert(fields.name_len < sizeof adv_name - 1);
|
||||
memcpy(adv_name, fields.name, fields.name_len);
|
||||
adv_name[fields.name_len] = '\0';
|
||||
adv_name_len = fields.name_len;
|
||||
MODLOG_DFLT(DEBUG, " name(%scomplete)=%s\n",
|
||||
fields.name_is_complete ? "" : "in", adv_name);
|
||||
}
|
||||
|
||||
if (fields.appearance_is_present) {
|
||||
MODLOG_DFLT(DEBUG, " appearance=0x%04x\n", fields.appearance);
|
||||
appearance = fields.appearance;
|
||||
}
|
||||
|
||||
for (int i = 0; i < fields.num_uuids16; i++) {
|
||||
if (ble_uuid_u16(&fields.uuids16[i].u) == BLE_HID_SVC_UUID &&
|
||||
((adv_name_len > 0 && memcmp("ESP BLE HID2", adv_name, adv_name_len) == 0) ||
|
||||
(adv_name_len > 0 && memcmp("ESP Mouse", adv_name, adv_name_len) == 0) ||
|
||||
(adv_name_len > 0 && memcmp("ESP Keyboard", adv_name, adv_name_len) == 0))) {
|
||||
add_ble_scan_result(disc->addr.val, disc->addr.type, appearance, adv_name, adv_name_len, disc->rssi);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
static int
|
||||
nimble_hid_gap_event(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
struct ble_gap_conn_desc desc;
|
||||
int rc;
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_DISC:
|
||||
handle_ble_device_result(&event->disc);
|
||||
if (rc != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* An advertisment report was received during GAP discovery. */
|
||||
return 0;
|
||||
break;
|
||||
case BLE_GAP_EVENT_DISC_COMPLETE:
|
||||
MODLOG_DFLT(INFO, "discovery complete; reason=%d\n",
|
||||
event->disc_complete.reason);
|
||||
SEND_BLE_CB();
|
||||
return 0;
|
||||
case BLE_GAP_EVENT_CONNECT:
|
||||
/* A new connection was established or a connection attempt failed. */
|
||||
ESP_LOGI(TAG, "connection %s; status=%d",
|
||||
event->connect.status == 0 ? "established" : "failed",
|
||||
event->connect.status);
|
||||
return 0;
|
||||
break;
|
||||
case BLE_GAP_EVENT_DISCONNECT:
|
||||
ESP_LOGI(TAG, "disconnect; reason=%d", event->disconnect.reason);
|
||||
|
||||
return 0;
|
||||
case BLE_GAP_EVENT_CONN_UPDATE:
|
||||
/* The central has updated the connection parameters. */
|
||||
ESP_LOGI(TAG, "connection updated; status=%d",
|
||||
event->conn_update.status);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_ADV_COMPLETE:
|
||||
ESP_LOGI(TAG, "advertise complete; reason=%d",
|
||||
event->adv_complete.reason);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_SUBSCRIBE:
|
||||
ESP_LOGI(TAG, "subscribe event; conn_handle=%d attr_handle=%d "
|
||||
"reason=%d prevn=%d curn=%d previ=%d curi=%d\n",
|
||||
event->subscribe.conn_handle,
|
||||
event->subscribe.attr_handle,
|
||||
event->subscribe.reason,
|
||||
event->subscribe.prev_notify,
|
||||
event->subscribe.cur_notify,
|
||||
event->subscribe.prev_indicate,
|
||||
event->subscribe.cur_indicate);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_MTU:
|
||||
ESP_LOGI(TAG, "mtu update event; conn_handle=%d cid=%d mtu=%d",
|
||||
event->mtu.conn_handle,
|
||||
event->mtu.channel_id,
|
||||
event->mtu.value);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_ENC_CHANGE:
|
||||
/* Encryption has been enabled or disabled for this connection. */
|
||||
MODLOG_DFLT(INFO, "encryption change event; status=%d ",
|
||||
event->enc_change.status);
|
||||
rc = ble_gap_conn_find(event->enc_change.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_NOTIFY_TX:
|
||||
MODLOG_DFLT(INFO, "notify_tx event; conn_handle=%d attr_handle=%d "
|
||||
"status=%d is_indication=%d",
|
||||
event->notify_tx.conn_handle,
|
||||
event->notify_tx.attr_handle,
|
||||
event->notify_tx.status,
|
||||
event->notify_tx.indication);
|
||||
return 0;
|
||||
|
||||
case BLE_GAP_EVENT_REPEAT_PAIRING:
|
||||
/* We already have a bond with the peer, but it is attempting to
|
||||
* establish a new secure link. This app sacrifices security for
|
||||
* convenience: just throw away the old bond and accept the new link.
|
||||
*/
|
||||
|
||||
/* Delete the old bond. */
|
||||
rc = ble_gap_conn_find(event->repeat_pairing.conn_handle, &desc);
|
||||
assert(rc == 0);
|
||||
ble_store_util_delete_peer(&desc.peer_id_addr);
|
||||
|
||||
/* Return BLE_GAP_REPEAT_PAIRING_RETRY to indicate that the host should
|
||||
* continue with the pairing operation.
|
||||
*/
|
||||
return BLE_GAP_REPEAT_PAIRING_RETRY;
|
||||
|
||||
case BLE_GAP_EVENT_PASSKEY_ACTION:
|
||||
ESP_LOGI(TAG, "PASSKEY_ACTION_EVENT started");
|
||||
struct ble_sm_io pkey = {0};
|
||||
int key = 0;
|
||||
|
||||
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = 123456; // This is the passkey to be entered on peer
|
||||
ESP_LOGI(TAG, "Enter passkey %" PRIu32 "on the peer side", pkey.passkey);
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(TAG, "ble_sm_inject_io result: %d", rc);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_NUMCMP) {
|
||||
ESP_LOGI(TAG, "Accepting passkey..");
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.numcmp_accept = key;
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(TAG, "ble_sm_inject_io result: %d", rc);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_OOB) {
|
||||
static uint8_t tem_oob[16] = {0};
|
||||
pkey.action = event->passkey.params.action;
|
||||
for (int i = 0; i < 16; i++) {
|
||||
pkey.oob[i] = tem_oob[i];
|
||||
}
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(TAG, "ble_sm_inject_io result: %d", rc);
|
||||
} else if (event->passkey.params.action == BLE_SM_IOACT_INPUT) {
|
||||
ESP_LOGI(TAG, "Input not supported passing -> 123456");
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = 123456;
|
||||
rc = ble_sm_inject_io(event->passkey.conn_handle, &pkey);
|
||||
ESP_LOGI(TAG, "ble_sm_inject_io result: %d", rc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static esp_err_t start_nimble_scan(uint32_t seconds)
|
||||
{
|
||||
uint8_t own_addr_type;
|
||||
struct ble_gap_disc_params disc_params;
|
||||
int rc;
|
||||
|
||||
/* Figure out address to use while advertising (no privacy for now) */
|
||||
rc = ble_hs_id_infer_auto(0, &own_addr_type);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "error determining address type; rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Tell the controller to filter duplicates; we don't want to process
|
||||
* repeated advertisements from the same device.
|
||||
*/
|
||||
disc_params.filter_duplicates = 1;
|
||||
|
||||
/**
|
||||
* Perform active scan.
|
||||
*/
|
||||
disc_params.passive = 0;
|
||||
|
||||
/* Use defaults for the rest of the parameters. */
|
||||
disc_params.itvl = 0x50;
|
||||
disc_params.window = 0x30;
|
||||
disc_params.filter_policy = 0;
|
||||
disc_params.limited = 0;
|
||||
|
||||
rc = ble_gap_disc(own_addr_type, seconds * 1000, &disc_params,
|
||||
nimble_hid_gap_event, NULL);
|
||||
if (rc != 0) {
|
||||
MODLOG_DFLT(ERROR, "Error initiating GAP discovery procedure; rc=%d\n",
|
||||
rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#else
|
||||
static esp_err_t init_low_level(uint8_t mode)
|
||||
{
|
||||
esp_err_t ret;
|
||||
@ -766,6 +1073,7 @@ static esp_err_t init_low_level(uint8_t mode)
|
||||
#endif /* CONFIG_BT_BLE_ENABLED */
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t esp_hid_gap_init(uint8_t mode)
|
||||
{
|
||||
@ -820,6 +1128,14 @@ esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_resul
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif /* CONFIG_BT_BLE_ENABLED */
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
if (start_nimble_scan(seconds) == ESP_OK) {
|
||||
WAIT_BLE_CB();
|
||||
} else {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
#endif /* CONFIG_BT_BLE_ENABLED */
|
||||
|
||||
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
if (start_bt_scan(seconds) == ESP_OK) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -21,6 +21,8 @@
|
||||
#endif
|
||||
#elif CONFIG_BT_BLE_ENABLED
|
||||
#define HID_HOST_MODE HIDH_BLE_MODE
|
||||
#elif CONFIG_BT_NIMBLE_ENABLED
|
||||
#define HID_HOST_MODE HIDH_BLE_MODE
|
||||
#else
|
||||
#define HID_HOST_MODE HIDH_IDLE_MODE
|
||||
#endif
|
||||
@ -29,9 +31,11 @@
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "esp_bt.h"
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "esp_bt_defs.h"
|
||||
#include "esp_bt_main.h"
|
||||
#include "esp_gap_bt_api.h"
|
||||
#endif
|
||||
#include "esp_hid_common.h"
|
||||
#if CONFIG_BT_BLE_ENABLED
|
||||
#include "esp_gattc_api.h"
|
||||
@ -39,19 +43,28 @@
|
||||
#include "esp_gap_ble_api.h"
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "nimble/ble.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct esp_hidh_scan_result_s {
|
||||
struct esp_hidh_scan_result_s *next;
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
uint8_t bda[6];
|
||||
#else
|
||||
esp_bd_addr_t bda;
|
||||
#endif
|
||||
|
||||
const char *name;
|
||||
int8_t rssi;
|
||||
esp_hid_usage_t usage;
|
||||
esp_hid_transport_t transport; //BT, BLE or USB
|
||||
union {
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
struct {
|
||||
esp_bt_cod_t cod;
|
||||
esp_bt_uuid_t uuid;
|
||||
@ -60,6 +73,12 @@ typedef struct esp_hidh_scan_result_s {
|
||||
esp_ble_addr_type_t addr_type;
|
||||
uint16_t appearance;
|
||||
} ble;
|
||||
#else
|
||||
struct {
|
||||
uint8_t addr_type;
|
||||
uint16_t appearance;
|
||||
} ble;
|
||||
#endif
|
||||
};
|
||||
} esp_hid_scan_result_t;
|
||||
|
||||
@ -70,8 +89,10 @@ 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);
|
||||
|
||||
#if !CONFIG_BT_NIMBLE_ENABLED
|
||||
void print_uuid(esp_bt_uuid_t *uuid);
|
||||
const char *ble_addr_type_str(esp_ble_addr_type_t ble_addr_type);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@ -17,12 +17,34 @@
|
||||
#include "esp_log.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "esp_bt.h"
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "host/ble_hs.h"
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#else
|
||||
#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"
|
||||
#endif
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
#include "host/ble_hs.h"
|
||||
#include "nimble/nimble_port.h"
|
||||
#include "nimble/nimble_port_freertos.h"
|
||||
#define ESP_BD_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x"
|
||||
#define ESP_BD_ADDR_HEX(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]
|
||||
#else
|
||||
#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"
|
||||
#endif
|
||||
|
||||
#include "esp_hidh.h"
|
||||
#include "esp_hid_gap.h"
|
||||
@ -99,6 +121,13 @@ void hid_demo_task(void *pvParameters)
|
||||
printf("ADDR_TYPE: '%s', ", ble_addr_type_str(r->ble.addr_type));
|
||||
}
|
||||
#endif /* CONFIG_BT_BLE_ENABLED */
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
if (r->transport == ESP_HID_TRANSPORT_BLE) {
|
||||
cr = r;
|
||||
printf("APPEARANCE: 0x%04x, ", r->ble.appearance);
|
||||
printf("ADDR_TYPE: '%d', ", r->ble.addr_type);
|
||||
}
|
||||
#endif /* CONFIG_BT_BLE_ENABLED */
|
||||
#if CONFIG_BT_HID_HOST_ENABLED
|
||||
if (r->transport == ESP_HID_TRANSPORT_BT) {
|
||||
cr = r;
|
||||
@ -123,6 +152,17 @@ void hid_demo_task(void *pvParameters)
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
void ble_hid_host_task(void *param)
|
||||
{
|
||||
ESP_LOGI(TAG, "BLE Host Task Started");
|
||||
/* This function will return only when nimble_port_stop() is executed */
|
||||
nimble_port_run();
|
||||
|
||||
nimble_port_freertos_deinit();
|
||||
}
|
||||
void ble_store_config_init(void);
|
||||
#endif
|
||||
void app_main(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
@ -148,5 +188,16 @@ void app_main(void)
|
||||
};
|
||||
ESP_ERROR_CHECK( esp_hidh_init(&config) );
|
||||
|
||||
#if CONFIG_BT_NIMBLE_ENABLED
|
||||
/* XXX Need to have template for store */
|
||||
ble_store_config_init();
|
||||
|
||||
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
|
||||
/* Starting nimble task after gatts is initialized*/
|
||||
ret = esp_nimble_enable(ble_hid_host_task);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "esp_nimble_enable failed: %d", ret);
|
||||
}
|
||||
#endif
|
||||
xTaskCreate(&hid_demo_task, "hid_task", 6 * 1024, NULL, 2, NULL);
|
||||
}
|
||||
|
@ -416,13 +416,9 @@ components/esp_hid/include/esp_hidd_transport.h
|
||||
components/esp_hid/include/esp_hidh.h
|
||||
components/esp_hid/include/esp_hidh_bluedroid.h
|
||||
components/esp_hid/include/esp_hidh_gattc.h
|
||||
components/esp_hid/include/esp_hidh_transport.h
|
||||
components/esp_hid/private/ble_hidd.h
|
||||
components/esp_hid/private/ble_hidh.h
|
||||
components/esp_hid/private/bt_hidd.h
|
||||
components/esp_hid/private/bt_hidh.h
|
||||
components/esp_hid/private/esp_hidd_private.h
|
||||
components/esp_hid/src/esp_hid_common.c
|
||||
components/esp_hid/test/hid_descriptor.h
|
||||
components/esp_hid/test/test_esp_hid.c
|
||||
components/esp_hw_support/include/esp_clk.h
|
||||
|
Loading…
Reference in New Issue
Block a user