esp-idf/components/esp_hid/src/bt_hidd.c

868 lines
31 KiB
C

/*
* SPDX-FileCopyrightText: 2017-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bt_hidd.h"
#if CONFIG_BT_HID_DEVICE_ENABLED
#include "esp_bt_defs.h"
#include "esp_bt_main.h"
#include "esp_hidd.h"
#include "esp_hidd_api.h"
#include "esp_private/esp_hidd_private.h"
#include "esp_log.h"
#include "osi/mutex.h"
#include "string.h"
/* Values for service_type */
#define NO_TRAFFIC 0
#define BEST_EFFORT 1
#define GUARANTEED 2
static const char *TAG = "BT_HIDD";
typedef struct {
esp_hid_raw_report_map_t reports_map;
uint8_t reports_len;
hidd_report_item_t *reports;
} hidd_dev_map_t;
typedef struct {
esp_hidd_dev_t *dev;
esp_event_loop_handle_t event_loop_handle;
esp_hid_device_config_t config;
uint16_t appearance;
bool registered;
bool connected;
esp_bd_addr_t remote_bda;
uint8_t bat_level; // 0 - 100 - battery percentage
uint8_t control; // 0x00 suspend, 0x01 suspend off
uint8_t protocol_mode; // 0x00 boot, 0x01 report
hidd_dev_map_t *devices;
uint8_t devices_len;
} esp_bt_hidd_dev_t;
typedef struct {
osi_mutex_t mutex;
esp_bt_hidd_dev_t *dev;
esp_hidd_app_param_t app_param;
esp_hidd_qos_param_t in_qos;
esp_hidd_qos_param_t out_qos;
} hidd_param_t;
static hidd_param_t s_hidd_param = {0};
#define is_init() (s_hidd_param.dev != NULL)
#define UNUSED(x) (void)(x)
static esp_err_t bt_hidd_get_status(esp_hidd_status_t status)
{
esp_err_t ret = ESP_OK;
switch (status) {
case ESP_HIDD_SUCCESS:
ret = ESP_OK;
break;
case ESP_HIDD_NO_RES:
ret = ESP_ERR_NO_MEM;
break;
default:
ret = ESP_FAIL;
break;
}
return ret;
}
static esp_err_t bt_hidd_init_config(esp_bt_hidd_dev_t *dev, const esp_hid_device_config_t *config)
{
if (config->report_maps == NULL || config->report_maps_len == 0 || config->report_maps_len > 1) {
return ESP_ERR_INVALID_ARG;
}
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_ERR_NO_MEM;
}
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_ERR_NO_MEM;
}
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_report_item_t *)malloc(rmap->reports_len * sizeof(hidd_report_item_t));
if (dev->devices[d].reports == NULL) {
ESP_LOGE(TAG, "reports malloc(%d) failed", rmap->reports_len * sizeof(hidd_report_item_t));
free(rmap);
return ESP_ERR_NO_MEM;
}
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 hidd_report_item_t *get_report_by_idx_id_type(esp_bt_hidd_dev_t *dev, size_t index, uint8_t id, uint8_t type)
{
hidd_report_item_t *rpt = NULL;
if (index >= dev->devices_len) {
ESP_LOGE(TAG, "index out of range[0-%d]", dev->devices_len - 1);
return NULL;
}
for (uint8_t i = 0; i < dev->devices[index].reports_len; i++) {
rpt = &dev->devices[index].reports[i];
if (rpt->report_id == id && rpt->report_type == type && rpt->protocol_mode == dev->protocol_mode) {
return rpt;
}
}
return NULL;
}
static hidd_report_item_t *get_report_by_id_and_type(esp_bt_hidd_dev_t *dev, uint8_t id, uint8_t type, uint8_t *index)
{
hidd_report_item_t *rpt = NULL;
for (uint8_t idx = 0; idx < dev->devices_len; idx++) {
for (uint8_t i = 0; i < dev->devices[idx].reports_len; i++) {
rpt = &dev->devices[idx].reports[i];
if (rpt->report_id == id && rpt->report_type == type && rpt->protocol_mode == dev->protocol_mode) {
if (index) {
*index = idx;
}
return rpt;
}
}
}
return NULL;
}
static esp_err_t bt_hid_free_config(esp_bt_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);
dev->event_loop_handle = NULL;
}
return ESP_OK;
}
static void bt_hidd_dev_free(void)
{
if (s_hidd_param.dev) {
osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT);
bt_hid_free_config(s_hidd_param.dev);
free(s_hidd_param.dev);
s_hidd_param.dev = NULL;
osi_mutex_unlock(&s_hidd_param.mutex);
osi_mutex_free(&s_hidd_param.mutex);
}
}
static void build_default_in_qos(esp_hidd_qos_param_t *in_qos, uint32_t value_len)
{
if (value_len > 0) {
in_qos->service_type = GUARANTEED;
in_qos->token_rate = value_len * 100;
in_qos->token_bucket_size = value_len;
in_qos->peak_bandwidth = value_len * 100;
in_qos->access_latency = 10;
in_qos->delay_variation = 10;
} else {
memset(in_qos, 0, sizeof(esp_hidd_qos_param_t));
}
}
static void build_default_out_qos(esp_hidd_qos_param_t *out_qos, uint32_t value_len)
{
if (value_len > 0) {
out_qos->service_type = GUARANTEED;
out_qos->token_rate = value_len * 100;
out_qos->token_bucket_size = value_len;
out_qos->peak_bandwidth = value_len * 100;
out_qos->access_latency = 10;
out_qos->delay_variation = 10;
} else {
memset(out_qos, 0, sizeof(esp_hidd_qos_param_t));
}
}
static uint8_t get_subclass_by_appearance(uint16_t appearance)
{
uint8_t ret = ESP_HID_CLASS_UNKNOWN;
switch (appearance) {
case ESP_HID_APPEARANCE_KEYBOARD:
ret = ESP_HID_CLASS_KBD;
break;
case ESP_HID_APPEARANCE_MOUSE:
ret = ESP_HID_CLASS_MIC;
break;
case ESP_HID_APPEARANCE_JOYSTICK:
ret = ESP_HID_CLASS_JOS;
break;
case ESP_HID_APPEARANCE_GAMEPAD:
ret = ESP_HID_CLASS_GPD;
break;
default:
ret = ESP_HID_CLASS_UNKNOWN;
break;
}
return ret;
}
static uint32_t get_value_len_by_type_protocol(esp_bt_hidd_dev_t *dev, uint8_t report_type, uint8_t protocol_mode)
{
uint32_t value_len = 0;
hidd_report_item_t *rpt = NULL;
for (uint8_t d = 0; d < dev->devices_len; d++) {
for (uint8_t i = 0; i < dev->devices[d].reports_len; i++) {
rpt = &dev->devices[d].reports[i];
if (rpt->report_type == report_type && rpt->protocol_mode == dev->protocol_mode) {
value_len += rpt->value_len;
}
}
}
return value_len;
}
static void bt_hidd_init_app(void)
{
esp_hid_device_config_t *p_config = &s_hidd_param.dev->config;
s_hidd_param.app_param.name = p_config->device_name;
s_hidd_param.app_param.description = p_config->device_name;
s_hidd_param.app_param.provider = p_config->manufacturer_name;
s_hidd_param.app_param.subclass = get_subclass_by_appearance(s_hidd_param.dev->appearance);
s_hidd_param.app_param.desc_list = (uint8_t *)s_hidd_param.dev->devices[0].reports_map.data;
s_hidd_param.app_param.desc_list_len = s_hidd_param.dev->devices[0].reports_map.len;
}
static void bt_hidd_init_qos(void)
{
uint32_t value_len = 0;
value_len =
get_value_len_by_type_protocol(s_hidd_param.dev, ESP_HID_REPORT_TYPE_INPUT, s_hidd_param.dev->protocol_mode);
build_default_in_qos(&s_hidd_param.in_qos, value_len);
value_len =
get_value_len_by_type_protocol(s_hidd_param.dev, ESP_HID_REPORT_TYPE_INPUT, s_hidd_param.dev->protocol_mode);
build_default_out_qos(&s_hidd_param.out_qos, value_len);
}
static bool esp_bt_hidd_dev_connected(void *devp)
{
esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp;
bool ret = true;
osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT);
do {
if (!is_init()) {
ESP_LOGE(TAG, "HID device profile is uninit");
ret = false;
break;
}
if (s_hidd_param.dev != dev) {
ESP_LOGE(TAG, "Wrong HID device provided");
ret = false;
break;
}
} while (0);
if (ret) {
ret = dev->connected;
}
osi_mutex_unlock(&s_hidd_param.mutex);
return ret;
}
static esp_err_t esp_bt_hidd_dev_deinit(void *devp)
{
esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp;
esp_err_t ret = ESP_OK;
osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT);
do {
if (!is_init()) {
osi_mutex_unlock(&s_hidd_param.mutex);
ESP_LOGE(TAG, "HID device profile already uninitialized");
return ESP_OK;
}
if (s_hidd_param.dev != dev) {
ESP_LOGE(TAG, "Wrong HID device provided");
ret = ESP_FAIL;
}
} while (0);
osi_mutex_unlock(&s_hidd_param.mutex);
if (ret == ESP_OK) {
ret = esp_bt_hid_device_deinit();
}
return ret;
}
static esp_err_t esp_bt_hidd_dev_disconnect(void *devp)
{
esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp;
esp_err_t ret = ESP_OK;
osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT);
do {
if (!is_init()) {
ESP_LOGE(TAG, "HID device is uninit");
ret = ESP_FAIL;
break;
}
if (s_hidd_param.dev != dev) {
ESP_LOGE(TAG, "Wrong HID device provided");
ret = ESP_FAIL;
break;
}
if (!dev->connected) {
osi_mutex_unlock(&s_hidd_param.mutex);
ESP_LOGW(TAG, "already disconnected");
return ESP_OK;
}
} while (0);
osi_mutex_unlock(&s_hidd_param.mutex);
if (ret == ESP_OK) {
ret = esp_bt_hid_device_disconnect();
}
return ret;
}
static esp_err_t esp_bt_hidd_dev_battery_set(void *devp, uint8_t level)
{
UNUSED(devp);
UNUSED(level);
ESP_LOGW(TAG, "Not implement yet!");
return ESP_OK;
}
static esp_err_t esp_bt_hidd_dev_input_set(void *devp, size_t index, size_t id, uint8_t *data, size_t length)
{
hidd_report_item_t *p_rpt;
esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp;
esp_err_t ret = ESP_OK;
osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT);
do {
if (!is_init()) {
ESP_LOGE(TAG, "HID device is uninit");
ret = ESP_FAIL;
break;
}
if (s_hidd_param.dev != dev) {
ESP_LOGE(TAG, "Wrong HID device provided");
ret = ESP_FAIL;
break;
}
if (!dev->connected) {
ESP_LOGE(TAG, "HID device not connected!");
ret = ESP_FAIL;
break;
}
p_rpt = get_report_by_idx_id_type(dev, index, id, ESP_HID_REPORT_TYPE_INPUT);
if (p_rpt == NULL) {
ESP_LOGE(TAG, "HID device not connected!");
ret = ESP_FAIL;
break;
}
if (length > p_rpt->value_len) {
ESP_LOGE(TAG, "Data size over %d!", p_rpt->value_len);
ret = ESP_FAIL;
break;
}
} while (0);
osi_mutex_unlock(&s_hidd_param.mutex);
if (ret == ESP_OK) {
ret = esp_bt_hid_device_send_report(ESP_HIDD_REPORT_TYPE_INTRDATA, id, length, data);
}
return ret;
}
static esp_err_t esp_bt_hidd_dev_feature_set(void *devp, size_t index, size_t id, uint8_t *data, size_t length)
{
hidd_report_item_t *p_rpt;
esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp;
esp_err_t ret = ESP_OK;
osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT);
do {
if (!is_init()) {
ESP_LOGE(TAG, "HID device is uninit");
ret = ESP_FAIL;
break;
}
if (s_hidd_param.dev != dev) {
ESP_LOGE(TAG, "Wrong HID device provided");
ret = ESP_FAIL;
break;
}
if (!dev->connected) {
ESP_LOGE(TAG, "HID device not connected!");
ret = ESP_FAIL;
break;
}
p_rpt = get_report_by_idx_id_type(dev, index, id, ESP_HID_REPORT_TYPE_FEATURE);
if (p_rpt == NULL) {
ESP_LOGE(TAG, "HID device not connected!");
ret = ESP_FAIL;
break;
}
if (length > p_rpt->value_len) {
ESP_LOGE(TAG, "Data size over %d!", p_rpt->value_len);
ret = ESP_FAIL;
break;
}
} while (0);
osi_mutex_unlock(&s_hidd_param.mutex);
if (ret == ESP_OK) {
ret = esp_bt_hid_device_send_report(ESP_HID_REPORT_TYPE_FEATURE, id, length, data);
}
return ret;
}
static esp_err_t esp_bt_hidd_dev_event_handler_register(void *devp, esp_event_handler_t callback, esp_hidd_event_t event)
{
esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp;
esp_err_t ret = ESP_OK;
osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT);
do {
if (!is_init()) {
ESP_LOGE(TAG, "HID device is uninit");
ret = ESP_FAIL;
break;
}
if (s_hidd_param.dev != dev) {
ESP_LOGE(TAG, "Wrong HID device provided");
ret = ESP_FAIL;
break;
}
ret = esp_event_handler_register_with(dev->event_loop_handle, ESP_HIDD_EVENTS, event, callback, dev->dev);
} while (0);
osi_mutex_unlock(&s_hidd_param.mutex);
return ret;
}
static esp_err_t esp_bt_hidd_dev_event_handler_unregister(void *devp, esp_event_handler_t callback, esp_hidd_event_t event)
{
esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp;
esp_err_t ret = ESP_OK;
osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT);
do {
if (!is_init()) {
ESP_LOGE(TAG, "HID device is uninit");
ret = ESP_FAIL;
break;
}
if (s_hidd_param.dev != dev) {
ESP_LOGE(TAG, "Wrong HID device provided");
ret = ESP_FAIL;
break;
}
ret = esp_event_handler_unregister_with(dev->event_loop_handle, ESP_HIDD_EVENTS, event, callback);
} while (0);
osi_mutex_unlock(&s_hidd_param.mutex);
return ret;
}
void bt_hidd_cb(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param)
{
esp_hidd_event_data_t cb_param = {0};
esp_hidd_event_data_t *p_cb_param = NULL;
size_t event_data_size = 0;
uint8_t map_index = 0;
hidd_report_item_t *p_rpt = NULL;
if (!is_init()) {
ESP_LOGE(TAG, "HID device is uninit, event(%d)", event);
return;
}
switch (event) {
case ESP_HIDD_INIT_EVT: {
if (param->init.status == ESP_HIDD_SUCCESS) {
ESP_LOGD(TAG, "Setting hid parameters in_qos:%d, out_qos:%d", s_hidd_param.in_qos.token_bucket_size,
s_hidd_param.out_qos.token_bucket_size);
esp_bt_hid_device_register_app(&s_hidd_param.app_param, &s_hidd_param.in_qos, &s_hidd_param.out_qos);
} else {
ESP_LOGE(TAG, "Init hidd failed (%d)!", param->init.status);
cb_param.start.status = bt_hidd_get_status(param->init.status);
esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_START_EVENT, &cb_param,
sizeof(esp_hidd_event_data_t), portMAX_DELAY);
bt_hidd_dev_free();
}
break;
}
case ESP_HIDD_DEINIT_EVT: {
cb_param.stop.status = bt_hidd_get_status(param->deinit.status);
esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_STOP_EVENT, &cb_param,
sizeof(esp_hidd_event_data_t), portMAX_DELAY);
if (param->deinit.status == ESP_HIDD_SUCCESS) {
bt_hidd_dev_free();
} else {
ESP_LOGE(TAG, "Deinit hidd failed (%d)!", param->deinit.status);
}
break;
}
case ESP_HIDD_REGISTER_APP_EVT: {
if (param->register_app.status == ESP_HIDD_SUCCESS) {
ESP_LOGD(TAG, "Setting hid parameters success!");
if (param->register_app.in_use) {
ESP_LOGI(TAG, "Start virtual cable plug!");
esp_bt_hid_device_connect(param->register_app.bd_addr);
}
osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT);
s_hidd_param.dev->registered = true;
osi_mutex_unlock(&s_hidd_param.mutex);
cb_param.start.status = bt_hidd_get_status(param->init.status);
esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_START_EVENT, &cb_param,
sizeof(esp_hidd_event_data_t), portMAX_DELAY);
} else {
ESP_LOGE(TAG, "Setting hid parameters failed (%d), now deint!", param->register_app.status);
esp_bt_hid_device_deinit();
}
break;
}
case ESP_HIDD_UNREGISTER_APP_EVT: {
break;
}
case ESP_HIDD_OPEN_EVT: {
if (param->open.conn_status == ESP_HIDD_CONN_STATE_CONNECTING) {
break;
}
if (param->open.status == ESP_HIDD_SUCCESS && param->open.conn_status == ESP_HIDD_CONN_STATE_CONNECTED) {
ESP_LOGI(TAG, "Connected to %02x:%02x:%02x:%02x:%02x:%02x", param->open.bd_addr[0], param->open.bd_addr[1],
param->open.bd_addr[2], param->open.bd_addr[3], param->open.bd_addr[4], param->open.bd_addr[5]);
osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT);
s_hidd_param.dev->connected = true;
memcpy(s_hidd_param.dev->remote_bda, param->open.bd_addr, ESP_BD_ADDR_LEN);
osi_mutex_unlock(&s_hidd_param.mutex);
} else {
ESP_LOGE(TAG, "Connect failed (%d)!", param->open.status);
}
cb_param.connect.status = bt_hidd_get_status(param->open.status);
cb_param.connect.dev = s_hidd_param.dev->dev;
esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_CONNECT_EVENT, &cb_param,
sizeof(esp_hidd_event_data_t), portMAX_DELAY);
break;
}
case ESP_HIDD_CLOSE_EVT: {
if (param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTING) {
break;
}
if (param->close.status == ESP_HIDD_SUCCESS && param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTED) {
osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT);
s_hidd_param.dev->connected = false;
memset(s_hidd_param.dev->remote_bda, 0, ESP_BD_ADDR_LEN);
osi_mutex_unlock(&s_hidd_param.mutex);
} else {
ESP_LOGE(TAG, "Disconnect failed (%d)!", param->close.status);
}
cb_param.disconnect.status = bt_hidd_get_status(param->close.status);
cb_param.disconnect.dev = s_hidd_param.dev->dev;
cb_param.disconnect.reason = param->close.status;
esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_DISCONNECT_EVENT, &cb_param,
sizeof(esp_hidd_event_data_t), portMAX_DELAY);
break;
}
case ESP_HIDD_SEND_REPORT_EVT:
break;
case ESP_HIDD_REPORT_ERR_EVT:
break;
case ESP_HIDD_GET_REPORT_EVT: {
uint8_t *data_ptr = NULL;
p_rpt = get_report_by_id_and_type(s_hidd_param.dev, param->get_report.report_id, param->get_report.report_type,
&map_index);
if (p_rpt == NULL) {
ESP_LOGE(TAG, "Can not find report!");
esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID);
break;
}
if (param->get_report.buffer_size > p_rpt->value_len) {
ESP_LOGE(TAG, "Data size over %d!", p_rpt->value_len);
esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM);
break;
}
event_data_size = sizeof(esp_hidd_event_data_t);
if (param->get_report.buffer_size) {
event_data_size += 2;
}
if ((p_cb_param = (esp_hidd_event_data_t *)malloc(event_data_size)) == NULL) {
ESP_LOGE(TAG, "%s malloc event data failed!", __func__);
break;
}
memset(p_cb_param, 0, event_data_size);
p_cb_param->feature.dev = s_hidd_param.dev->dev;
p_cb_param->feature.trans_type = ESP_HID_TRANS_GET_REPORT;
p_cb_param->feature.report_type = param->get_report.report_type;
p_cb_param->feature.report_id = p_rpt->report_id;
p_cb_param->feature.usage = p_rpt->usage;
p_cb_param->feature.length = param->get_report.buffer_size ? 2 : 0;
p_cb_param->feature.data = ((uint8_t *)p_cb_param) + sizeof(esp_hidd_event_data_t);
p_cb_param->feature.map_index = map_index;
if (param->get_report.buffer_size) {
data_ptr = ((uint8_t *)p_cb_param) + sizeof(esp_hidd_event_data_t);
*data_ptr++ = (uint8_t)param->get_report.buffer_size;
*data_ptr++ = (uint8_t)(param->get_report.buffer_size >> 8);
}
esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_FEATURE_EVENT, p_cb_param,
event_data_size, portMAX_DELAY);
break;
}
case ESP_HIDD_SET_REPORT_EVT: {
p_rpt = get_report_by_id_and_type(s_hidd_param.dev, param->set_report.report_id, param->set_report.report_type,
&map_index);
if (p_rpt == NULL) {
ESP_LOGE(TAG, "Can not find report!");
esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID);
break;
}
if (param->set_report.len > p_rpt->value_len) {
ESP_LOGE(TAG, "Data size over %d!", p_rpt->value_len);
esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM);
break;
}
event_data_size = sizeof(esp_hidd_event_data_t);
if (param->set_report.len && param->set_report.data) {
event_data_size += param->set_report.len;
}
if ((p_cb_param = (esp_hidd_event_data_t *)malloc(event_data_size)) == NULL) {
ESP_LOGE(TAG, "%s malloc event data failed!", __func__);
break;
}
memset(p_cb_param, 0, event_data_size);
p_cb_param->feature.dev = s_hidd_param.dev->dev;
p_cb_param->feature.trans_type = ESP_HID_TRANS_SET_REPORT;
p_cb_param->feature.report_type = param->set_report.report_type;
p_cb_param->feature.report_id = p_rpt->report_id;
p_cb_param->feature.usage = p_rpt->usage;
p_cb_param->feature.length = param->set_report.len;
p_cb_param->feature.data = param->set_report.data;
p_cb_param->feature.map_index = map_index;
if (param->set_report.len && param->set_report.data) {
memcpy(((uint8_t *)p_cb_param) + sizeof(esp_hidd_event_data_t), param->set_report.data,
param->set_report.len);
}
esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_FEATURE_EVENT, p_cb_param,
event_data_size, portMAX_DELAY);
break;
}
case ESP_HIDD_SET_PROTOCOL_EVT: {
if (param->set_protocol.protocol_mode != ESP_HIDD_UNSUPPORTED_MODE) {
osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT);
s_hidd_param.dev->protocol_mode =
param->set_protocol.protocol_mode ? ESP_HID_PROTOCOL_MODE_BOOT : ESP_HID_PROTOCOL_MODE_REPORT;
osi_mutex_unlock(&s_hidd_param.mutex);
cb_param.protocol_mode.dev = s_hidd_param.dev->dev;
cb_param.protocol_mode.protocol_mode = s_hidd_param.dev->protocol_mode;
cb_param.protocol_mode.map_index = 0;
esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_PROTOCOL_MODE_EVENT,
&cb_param, sizeof(esp_hidd_event_data_t), portMAX_DELAY);
} else {
ESP_LOGE(TAG, "Unsupported protocol mode!");
break;
}
break;
}
case ESP_HIDD_INTR_DATA_EVT: {
p_rpt = get_report_by_id_and_type(s_hidd_param.dev, param->intr_data.report_id, ESP_HID_REPORT_TYPE_OUTPUT,
&map_index);
if (p_rpt == NULL) {
ESP_LOGE(TAG, "Can not find report!");
break;
}
event_data_size = sizeof(esp_hidd_event_data_t);
if (param->intr_data.len && param->intr_data.data) {
event_data_size += param->intr_data.len;
}
if ((p_cb_param = (esp_hidd_event_data_t *)malloc(event_data_size)) == NULL) {
ESP_LOGE(TAG, "%s malloc event data failed!", __func__);
break;
}
memset(p_cb_param, 0, event_data_size);
p_cb_param->output.dev = s_hidd_param.dev->dev;
p_cb_param->output.report_id = p_rpt->report_id;
p_cb_param->output.usage = p_rpt->usage;
p_cb_param->output.length = param->intr_data.len;
p_cb_param->output.data = param->intr_data.data;
p_cb_param->output.map_index = map_index;
if (param->intr_data.len && param->intr_data.data) {
memcpy(((uint8_t *)p_cb_param) + sizeof(esp_hidd_event_data_t), param->intr_data.data,
param->intr_data.len);
}
esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_OUTPUT_EVENT, p_cb_param,
event_data_size, portMAX_DELAY);
break;
}
default:
break;
}
if (p_cb_param) {
free(p_cb_param);
p_cb_param = NULL;
}
}
esp_err_t esp_bt_hidd_dev_init(esp_hidd_dev_t *dev_p, const esp_hid_device_config_t *config, esp_event_handler_t callback)
{
esp_err_t ret = ESP_OK;
if (dev_p == NULL || config == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (is_init()) {
ESP_LOGE(TAG, "HID device profile already initialized");
return ESP_FAIL;
}
osi_mutex_new(&s_hidd_param.mutex);
if (s_hidd_param.mutex == NULL) {
ESP_LOGE(TAG, "HID device mutex could not be allocated");
return ESP_ERR_NO_MEM;
}
s_hidd_param.dev = (esp_bt_hidd_dev_t *)calloc(1, sizeof(esp_bt_hidd_dev_t));
if (s_hidd_param.dev == NULL) {
ESP_LOGE(TAG, "HID device could not be allocated");
return ESP_ERR_NO_MEM;
}
//[1] Reset the hid device target environment
s_hidd_param.dev->connected = false;
s_hidd_param.dev->registered = false;
s_hidd_param.dev->bat_level = 100;
s_hidd_param.dev->control = ESP_HID_CONTROL_EXIT_SUSPEND;
s_hidd_param.dev->protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT;
s_hidd_param.dev->event_loop_handle = NULL;
s_hidd_param.dev->dev = dev_p;
esp_event_loop_args_t event_task_args = {
.queue_size = 5,
.task_name = "bt_hidd_events",
.task_priority = uxTaskPriorityGet(NULL),
.task_stack_size = 2048,
.task_core_id = tskNO_AFFINITY
};
ret = esp_event_loop_create(&event_task_args, &s_hidd_param.dev->event_loop_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "HID device event loop could not be created");
bt_hidd_dev_free();
return ret;
}
//[2] parse hid descriptor
ret = bt_hidd_init_config(s_hidd_param.dev, config);
if (ret != ESP_OK) {
bt_hidd_dev_free();
return ret;
}
//[3] configure hidd app param and qos param
bt_hidd_init_app();
bt_hidd_init_qos();
//[4] implement the interface
dev_p->dev = s_hidd_param.dev;
dev_p->connected = esp_bt_hidd_dev_connected;
dev_p->disconnect = esp_bt_hidd_dev_disconnect;
dev_p->deinit = esp_bt_hidd_dev_deinit;
dev_p->battery_set = esp_bt_hidd_dev_battery_set;
dev_p->input_set = esp_bt_hidd_dev_input_set;
dev_p->feature_set = esp_bt_hidd_dev_feature_set;
dev_p->event_handler_register = esp_bt_hidd_dev_event_handler_register;
dev_p->event_handler_unregister = esp_bt_hidd_dev_event_handler_unregister;
ret = esp_bt_hidd_dev_event_handler_register(s_hidd_param.dev, esp_hidd_process_event_data_handler, ESP_EVENT_ANY_ID);
if (ret != ESP_OK) {
bt_hidd_dev_free();
return ret;
}
if (callback != NULL) {
ret = esp_bt_hidd_dev_event_handler_register(s_hidd_param.dev, callback, ESP_EVENT_ANY_ID);
if (ret != ESP_OK) {
bt_hidd_dev_free();
return ret;
}
}
ret = esp_bt_hid_device_register_callback(bt_hidd_cb);
ret |= esp_bt_hid_device_init();
if (ret != ESP_OK) {
bt_hidd_dev_free();
return ret;
}
return ret;
}
#endif /* CONFIG_BT_HID_DEVICE_ENABLED */