feat(nimble): added HID over Gatt profile support

This commit is contained in:
Roshan Bangar
2023-07-31 15:32:07 +05:30
committed by Rahul Tank
parent 204c194ce2
commit 96ed1ae7a7
24 changed files with 3002 additions and 78 deletions
+5 -13
View File
@@ -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"
+2 -2
View File
@@ -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;
+108 -3
View File
@@ -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
View 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
File diff suppressed because it is too large Load Diff