/* * SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "bt_hidd.h" #if CONFIG_BT_HID_DEVICE_ENABLED #include "esp_bt.h" #include "esp_bt_defs.h" #include "esp_bt_main.h" #include "esp_hidd.h" #include "esp_hidd_api.h" #include "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 && param->register_app.bd_addr != NULL) { 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) { if (s_hidd_param.dev->protocol_mode == param->set_protocol.protocol_mode) { break; } osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); s_hidd_param.dev->protocol_mode = param->set_protocol.protocol_mode; 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 */