mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
868 lines
31 KiB
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 */
|