chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <protocomm.h>
|
||||
#include <protocomm_security0.h>
|
||||
#include <protocomm_security1.h>
|
||||
#include <protocomm_security2.h>
|
||||
|
||||
#include <esp_local_ctrl.h>
|
||||
#include "esp_local_ctrl_priv.h"
|
||||
#include "esp_local_ctrl.pb-c.h"
|
||||
|
||||
#define ESP_LOCAL_CTRL_VERSION "v1.0"
|
||||
/* JSON format string for version endpoint */
|
||||
#define ESP_LOCAL_CTRL_VER_FMT_STR "{\"local_ctrl\":{\"ver\":\"%s\",\"sec_ver\":%d,\"sec_patch_ver\":%d}}"
|
||||
|
||||
struct inst_ctx {
|
||||
protocomm_t *pc;
|
||||
esp_local_ctrl_config_t config;
|
||||
esp_local_ctrl_prop_t **props;
|
||||
size_t props_count;
|
||||
};
|
||||
|
||||
struct inst_ctx *local_ctrl_inst_ctx;
|
||||
|
||||
static const char *TAG = "esp_local_ctrl";
|
||||
|
||||
esp_err_t esp_local_ctrl_start(const esp_local_ctrl_config_t *config)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
if (!config) {
|
||||
ESP_LOGE(TAG, "NULL configuration provided");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!config->transport) {
|
||||
ESP_LOGE(TAG, "No transport provided");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (config->max_properties == 0) {
|
||||
ESP_LOGE(TAG, "max_properties must be greater than 0");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (!config->handlers.get_prop_values ||
|
||||
!config->handlers.set_prop_values) {
|
||||
ESP_LOGE(TAG, "Handlers cannot be null");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (local_ctrl_inst_ctx) {
|
||||
ESP_LOGW(TAG, "Service already active");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
local_ctrl_inst_ctx = calloc(1, sizeof(struct inst_ctx));
|
||||
if (!local_ctrl_inst_ctx) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for instance");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(&local_ctrl_inst_ctx->config, config, sizeof(local_ctrl_inst_ctx->config));
|
||||
|
||||
local_ctrl_inst_ctx->props = calloc(local_ctrl_inst_ctx->config.max_properties,
|
||||
sizeof(esp_local_ctrl_prop_t *));
|
||||
if (!local_ctrl_inst_ctx->props) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for properties");
|
||||
free(local_ctrl_inst_ctx);
|
||||
local_ctrl_inst_ctx = NULL;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
/* Since the config structure will be different for different transport modes, each transport may
|
||||
* implement a `copy_config()` function, which accepts a configuration structure as input and
|
||||
* creates a copy of that, which can be kept in the context structure of the `esp_local_ctrl` instance.
|
||||
* This copy can be later be freed using `free_config()` */
|
||||
if (config->transport->copy_config) {
|
||||
ret = config->transport->copy_config(&local_ctrl_inst_ctx->config.transport_config,
|
||||
&config->transport_config);
|
||||
if (ret != ESP_OK) {
|
||||
esp_local_ctrl_stop();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* For a selected transport mode, endpoints may need to be declared prior to starting the
|
||||
* `esp_local_ctrl` service, e.g. in case of BLE. By declaration it means that the transport layer
|
||||
* allocates some resources for an endpoint, and later, after service has started, a handler
|
||||
* is assigned for that endpoint */
|
||||
if (config->transport->declare_ep) {
|
||||
/* UUIDs are 16bit unique IDs for each endpoint. This may or may not be relevant for
|
||||
* a chosen transport. We reserve all values from FF50 to FFFF for the internal endpoints.
|
||||
* The remaining endpoints can be used by the application for its own custom endpoints */
|
||||
uint16_t start_uuid = 0xFF50;
|
||||
ret = config->transport->declare_ep(&local_ctrl_inst_ctx->config.transport_config,
|
||||
"esp_local_ctrl/version", start_uuid++);
|
||||
if (ret != ESP_OK) {
|
||||
esp_local_ctrl_stop();
|
||||
return ret;
|
||||
}
|
||||
ret = config->transport->declare_ep(&local_ctrl_inst_ctx->config.transport_config,
|
||||
"esp_local_ctrl/session", start_uuid++);
|
||||
if (ret != ESP_OK) {
|
||||
esp_local_ctrl_stop();
|
||||
return ret;
|
||||
}
|
||||
ret = config->transport->declare_ep(&local_ctrl_inst_ctx->config.transport_config,
|
||||
"esp_local_ctrl/control", start_uuid++);
|
||||
if (ret != ESP_OK) {
|
||||
esp_local_ctrl_stop();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
local_ctrl_inst_ctx->pc = protocomm_new();
|
||||
if (!local_ctrl_inst_ctx->pc) {
|
||||
ESP_LOGE(TAG, "Failed to create new protocomm instance");
|
||||
esp_local_ctrl_stop();
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
if (config->transport->start_service) {
|
||||
ret = config->transport->start_service(local_ctrl_inst_ctx->pc,
|
||||
&local_ctrl_inst_ctx->config.transport_config);
|
||||
if (ret != ESP_OK) {
|
||||
esp_local_ctrl_stop();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
protocomm_security_t *proto_sec_handle = NULL;
|
||||
switch (local_ctrl_inst_ctx->config.proto_sec.version) {
|
||||
case PROTOCOM_SEC_CUSTOM:
|
||||
proto_sec_handle = local_ctrl_inst_ctx->config.proto_sec.custom_handle;
|
||||
break;
|
||||
case PROTOCOM_SEC1:
|
||||
#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1
|
||||
proto_sec_handle = (protocomm_security_t *) &protocomm_security1;
|
||||
#else
|
||||
// Enable SECURITY_VERSION_1 in Protocomm configuration menu
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
break;
|
||||
case PROTOCOM_SEC2:
|
||||
#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2
|
||||
proto_sec_handle = (protocomm_security_t *) &protocomm_security2;
|
||||
break;
|
||||
#else
|
||||
// Enable SECURITY_VERSION_2 in Protocomm configuration menu
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
case PROTOCOM_SEC0:
|
||||
default:
|
||||
#ifdef CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0
|
||||
proto_sec_handle = (protocomm_security_t *) &protocomm_security0;
|
||||
#else
|
||||
// Enable SECURITY_VERSION_0 in Protocomm configuration menu
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
ret = protocomm_set_security(local_ctrl_inst_ctx->pc, "esp_local_ctrl/session",
|
||||
proto_sec_handle, local_ctrl_inst_ctx->config.proto_sec.sec_params);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to set session endpoint");
|
||||
esp_local_ctrl_stop();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sec_ver = 0;
|
||||
uint8_t sec_patch_ver = 0;
|
||||
protocomm_get_sec_version(local_ctrl_inst_ctx->pc, &sec_ver, &sec_patch_ver);
|
||||
|
||||
const int rsize = snprintf(NULL, 0, ESP_LOCAL_CTRL_VER_FMT_STR, ESP_LOCAL_CTRL_VERSION, sec_ver, sec_patch_ver) + 1;
|
||||
char *ver_str = malloc(rsize);
|
||||
if (!ver_str) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for version string");
|
||||
esp_local_ctrl_stop();
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
snprintf(ver_str, rsize, ESP_LOCAL_CTRL_VER_FMT_STR, ESP_LOCAL_CTRL_VERSION, sec_ver, sec_patch_ver);
|
||||
|
||||
ESP_LOGD(TAG, "ver_str: %s", ver_str);
|
||||
ret = protocomm_set_version(local_ctrl_inst_ctx->pc, "esp_local_ctrl/version",
|
||||
ver_str);
|
||||
free(ver_str);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to set version endpoint");
|
||||
esp_local_ctrl_stop();
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = protocomm_add_endpoint(local_ctrl_inst_ctx->pc, "esp_local_ctrl/control",
|
||||
esp_local_ctrl_data_handler, NULL);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to set control endpoint");
|
||||
esp_local_ctrl_stop();
|
||||
return ret;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_local_ctrl_stop(void)
|
||||
{
|
||||
if (local_ctrl_inst_ctx) {
|
||||
if (local_ctrl_inst_ctx->config.transport->free_config) {
|
||||
local_ctrl_inst_ctx->config.transport->free_config(&local_ctrl_inst_ctx->config.transport_config);
|
||||
}
|
||||
if (local_ctrl_inst_ctx->pc) {
|
||||
if (local_ctrl_inst_ctx->config.transport->stop_service) {
|
||||
local_ctrl_inst_ctx->config.transport->stop_service(local_ctrl_inst_ctx->pc);
|
||||
}
|
||||
protocomm_delete(local_ctrl_inst_ctx->pc);
|
||||
}
|
||||
if (local_ctrl_inst_ctx->config.handlers.usr_ctx_free_fn) {
|
||||
local_ctrl_inst_ctx->config.handlers.usr_ctx_free_fn(
|
||||
local_ctrl_inst_ctx->config.handlers.usr_ctx);
|
||||
}
|
||||
|
||||
/* Iterate through all properties one by one and free them */
|
||||
for (uint32_t i = 0; i < local_ctrl_inst_ctx->config.max_properties; i++) {
|
||||
if (local_ctrl_inst_ctx->props[i] == NULL) {
|
||||
continue;
|
||||
}
|
||||
/* Release memory allocated for property data */
|
||||
free(local_ctrl_inst_ctx->props[i]->name);
|
||||
if (local_ctrl_inst_ctx->props[i]->ctx_free_fn) {
|
||||
local_ctrl_inst_ctx->props[i]->ctx_free_fn(local_ctrl_inst_ctx->props[i]->ctx);
|
||||
}
|
||||
free(local_ctrl_inst_ctx->props[i]);
|
||||
}
|
||||
free(local_ctrl_inst_ctx->props);
|
||||
free(local_ctrl_inst_ctx);
|
||||
local_ctrl_inst_ctx = NULL;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int esp_local_ctrl_get_property_index(const char *name)
|
||||
{
|
||||
if (!local_ctrl_inst_ctx || !name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Iterate through all properties one by one
|
||||
* and find the one with matching name */
|
||||
for (uint32_t i = 0; i < local_ctrl_inst_ctx->props_count; i++) {
|
||||
if (strcmp(local_ctrl_inst_ctx->props[i]->name, name) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
esp_err_t esp_local_ctrl_add_property(const esp_local_ctrl_prop_t *prop)
|
||||
{
|
||||
if (!local_ctrl_inst_ctx) {
|
||||
ESP_LOGE(TAG, "Service not running");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!prop || !prop->name) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (esp_local_ctrl_get_property_index(prop->name) >= 0) {
|
||||
ESP_LOGE(TAG, "Property with name %s exists", prop->name);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (local_ctrl_inst_ctx->config.max_properties
|
||||
== local_ctrl_inst_ctx->props_count) {
|
||||
ESP_LOGE(TAG, "Max properties limit reached. Cannot add property %s", prop->name);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
uint32_t i = local_ctrl_inst_ctx->props_count;
|
||||
local_ctrl_inst_ctx->props[i] = calloc(1, sizeof(esp_local_ctrl_prop_t));
|
||||
if (!local_ctrl_inst_ctx->props[i]) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for new property %s", prop->name);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
local_ctrl_inst_ctx->props[i]->name = strdup(prop->name);
|
||||
if (!local_ctrl_inst_ctx->props[i]->name) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for property data %s", prop->name);
|
||||
free(local_ctrl_inst_ctx->props[i]);
|
||||
local_ctrl_inst_ctx->props[i] = NULL;
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
local_ctrl_inst_ctx->props[i]->type = prop->type;
|
||||
local_ctrl_inst_ctx->props[i]->size = prop->size;
|
||||
local_ctrl_inst_ctx->props[i]->flags = prop->flags;
|
||||
local_ctrl_inst_ctx->props[i]->ctx = prop->ctx;
|
||||
local_ctrl_inst_ctx->props[i]->ctx_free_fn = prop->ctx_free_fn;
|
||||
local_ctrl_inst_ctx->props_count++;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_local_ctrl_remove_property(const char *name)
|
||||
{
|
||||
int idx = esp_local_ctrl_get_property_index(name);
|
||||
if (idx < 0) {
|
||||
ESP_LOGE(TAG, "Property %s not found", name);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* Release memory allocated for property data */
|
||||
if (local_ctrl_inst_ctx->props[idx]->ctx_free_fn) {
|
||||
local_ctrl_inst_ctx->props[idx]->ctx_free_fn(
|
||||
local_ctrl_inst_ctx->props[idx]->ctx);
|
||||
}
|
||||
free(local_ctrl_inst_ctx->props[idx]->name);
|
||||
free(local_ctrl_inst_ctx->props[idx]);
|
||||
local_ctrl_inst_ctx->props[idx++] = NULL;
|
||||
|
||||
/* Move the following properties forward, so that there is
|
||||
* no empty space between two properties */
|
||||
for (uint32_t i = idx; i < local_ctrl_inst_ctx->props_count; i++) {
|
||||
if (local_ctrl_inst_ctx->props[i] == NULL) {
|
||||
break;
|
||||
}
|
||||
local_ctrl_inst_ctx->props[i-1] = local_ctrl_inst_ctx->props[i];
|
||||
}
|
||||
/* Clear the stale pointer left in the last slot after compaction to
|
||||
* prevent a double-free if esp_local_ctrl_stop() is called before the
|
||||
* slot is overwritten by a subsequent add_property(). */
|
||||
local_ctrl_inst_ctx->props[local_ctrl_inst_ctx->props_count - 1] = NULL;
|
||||
local_ctrl_inst_ctx->props_count--;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
const esp_local_ctrl_prop_t *esp_local_ctrl_get_property(const char *name)
|
||||
{
|
||||
int idx = esp_local_ctrl_get_property_index(name);
|
||||
if (idx < 0) {
|
||||
ESP_LOGE(TAG, "Property %s not found", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return local_ctrl_inst_ctx->props[idx];
|
||||
}
|
||||
|
||||
esp_err_t esp_local_ctrl_get_prop_count(size_t *count)
|
||||
{
|
||||
if (!local_ctrl_inst_ctx) {
|
||||
ESP_LOGE(TAG, "Service not running");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!count) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
*count = local_ctrl_inst_ctx->props_count;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_local_ctrl_get_prop_values(size_t total_indices, uint32_t *indices,
|
||||
esp_local_ctrl_prop_t *props,
|
||||
esp_local_ctrl_prop_val_t *values)
|
||||
{
|
||||
if (!local_ctrl_inst_ctx) {
|
||||
ESP_LOGE(TAG, "Service not running");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!indices || !props || !values) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Convert indices to names */
|
||||
for (size_t i = 0; i < total_indices; i++) {
|
||||
if (indices[i] >= local_ctrl_inst_ctx->props_count) {
|
||||
ESP_LOGE(TAG, "Invalid property index %" PRId32, indices[i]);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
props[i].name = local_ctrl_inst_ctx->props[indices[i]]->name;
|
||||
props[i].type = local_ctrl_inst_ctx->props[indices[i]]->type;
|
||||
props[i].flags = local_ctrl_inst_ctx->props[indices[i]]->flags;
|
||||
props[i].size = local_ctrl_inst_ctx->props[indices[i]]->size;
|
||||
props[i].ctx = local_ctrl_inst_ctx->props[indices[i]]->ctx;
|
||||
}
|
||||
|
||||
esp_local_ctrl_handlers_t *h = &local_ctrl_inst_ctx->config.handlers;
|
||||
esp_err_t ret = h->get_prop_values(total_indices, props, values, h->usr_ctx);
|
||||
|
||||
/* Properties with fixed sizes need to be checked */
|
||||
for (size_t i = 0; i < total_indices; i++) {
|
||||
if (local_ctrl_inst_ctx->props[indices[i]]->size != 0) {
|
||||
values[i].size = local_ctrl_inst_ctx->props[indices[i]]->size;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_local_ctrl_set_prop_values(size_t total_indices, uint32_t *indices,
|
||||
const esp_local_ctrl_prop_val_t *values)
|
||||
{
|
||||
if (!local_ctrl_inst_ctx) {
|
||||
ESP_LOGE(TAG, "Service not running");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!indices || !values) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_local_ctrl_prop_t *props = calloc(total_indices,
|
||||
sizeof(esp_local_ctrl_prop_t));
|
||||
if (!props) {
|
||||
ESP_LOGE(TAG, "Unable to allocate memory for properties array");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
for (size_t i = 0; i < total_indices; i++) {
|
||||
if (indices[i] >= local_ctrl_inst_ctx->props_count) {
|
||||
ESP_LOGE(TAG, "Invalid property index %" PRId32, indices[i]);
|
||||
free(props);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Properties with fixed sizes need to be checked */
|
||||
if ((local_ctrl_inst_ctx->props[indices[i]]->size != values[i].size) &&
|
||||
(local_ctrl_inst_ctx->props[indices[i]]->size != 0)) {
|
||||
ESP_LOGE(TAG, "Invalid property size %d. Expected %d",
|
||||
values[i].size, local_ctrl_inst_ctx->props[indices[i]]->size);
|
||||
free(props);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
props[i].name = local_ctrl_inst_ctx->props[indices[i]]->name;
|
||||
props[i].type = local_ctrl_inst_ctx->props[indices[i]]->type;
|
||||
props[i].flags = local_ctrl_inst_ctx->props[indices[i]]->flags;
|
||||
props[i].size = local_ctrl_inst_ctx->props[indices[i]]->size;
|
||||
props[i].ctx = local_ctrl_inst_ctx->props[indices[i]]->ctx;
|
||||
}
|
||||
|
||||
esp_local_ctrl_handlers_t *h = &local_ctrl_inst_ctx->config.handlers;
|
||||
esp_err_t ret = h->set_prop_values(total_indices, props, values, h->usr_ctx);
|
||||
|
||||
free(props);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t esp_local_ctrl_set_handler(const char *ep_name,
|
||||
protocomm_req_handler_t handler,
|
||||
void *priv_data)
|
||||
{
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
|
||||
if (local_ctrl_inst_ctx) {
|
||||
ret = protocomm_add_endpoint(local_ctrl_inst_ctx->pc, ep_name,
|
||||
handler, priv_data);
|
||||
}
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to register endpoint handler");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include "esp_compiler.h"
|
||||
#include "esp_local_ctrl.h"
|
||||
#include "esp_local_ctrl_priv.h"
|
||||
#include "esp_local_ctrl.pb-c.h"
|
||||
|
||||
#define SAFE_ALLOCATION(type, var) \
|
||||
type *var = (type *) malloc(sizeof(type)); \
|
||||
if (!var) { \
|
||||
ESP_LOGE(TAG, "Error allocating memory"); \
|
||||
return ESP_ERR_NO_MEM; \
|
||||
}
|
||||
|
||||
static const char* TAG = "esp_local_ctrl_handler";
|
||||
|
||||
typedef struct esp_local_ctrl_cmd {
|
||||
int cmd_num;
|
||||
int expected_payload_case;
|
||||
esp_err_t (*command_handler)(LocalCtrlMessage *req,
|
||||
LocalCtrlMessage *resp, void **ctx);
|
||||
} esp_local_ctrl_cmd_t;
|
||||
|
||||
static esp_err_t cmd_get_prop_count_handler(LocalCtrlMessage *req,
|
||||
LocalCtrlMessage *resp, void **ctx);
|
||||
|
||||
static esp_err_t cmd_get_prop_vals_handler(LocalCtrlMessage *req,
|
||||
LocalCtrlMessage *resp, void **ctx);
|
||||
|
||||
static esp_err_t cmd_set_prop_vals_handler(LocalCtrlMessage *req,
|
||||
LocalCtrlMessage *resp, void **ctx);
|
||||
|
||||
static esp_local_ctrl_cmd_t cmd_table[] = {
|
||||
{
|
||||
.cmd_num = LOCAL_CTRL_MSG_TYPE__TypeCmdGetPropertyCount,
|
||||
.expected_payload_case = LOCAL_CTRL_MESSAGE__PAYLOAD_CMD_GET_PROP_COUNT,
|
||||
.command_handler = cmd_get_prop_count_handler
|
||||
},
|
||||
{
|
||||
.cmd_num = LOCAL_CTRL_MSG_TYPE__TypeCmdGetPropertyValues,
|
||||
.expected_payload_case = LOCAL_CTRL_MESSAGE__PAYLOAD_CMD_GET_PROP_VALS,
|
||||
.command_handler = cmd_get_prop_vals_handler
|
||||
},
|
||||
{
|
||||
.cmd_num = LOCAL_CTRL_MSG_TYPE__TypeCmdSetPropertyValues,
|
||||
.expected_payload_case = LOCAL_CTRL_MESSAGE__PAYLOAD_CMD_SET_PROP_VALS,
|
||||
.command_handler = cmd_set_prop_vals_handler
|
||||
}
|
||||
};
|
||||
|
||||
static uint16_t err_to_status(esp_err_t err)
|
||||
{
|
||||
uint16_t status;
|
||||
switch (err) {
|
||||
case ESP_OK:
|
||||
status = STATUS__Success;
|
||||
break;
|
||||
case ESP_ERR_INVALID_ARG:
|
||||
status = STATUS__InvalidArgument;
|
||||
break;
|
||||
case ESP_ERR_INVALID_STATE:
|
||||
status = STATUS__InvalidProto;
|
||||
break;
|
||||
default:
|
||||
status = STATUS__InternalError;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static esp_err_t cmd_get_prop_count_handler(LocalCtrlMessage *req,
|
||||
LocalCtrlMessage *resp, void **ctx)
|
||||
{
|
||||
SAFE_ALLOCATION(RespGetPropertyCount, resp_payload);
|
||||
resp_get_property_count__init(resp_payload);
|
||||
|
||||
size_t prop_count = 0;
|
||||
resp_payload->status = err_to_status(esp_local_ctrl_get_prop_count(&prop_count));
|
||||
resp_payload->count = prop_count;
|
||||
resp->payload_case = LOCAL_CTRL_MESSAGE__PAYLOAD_RESP_GET_PROP_COUNT;
|
||||
resp->resp_get_prop_count = resp_payload;
|
||||
ESP_LOGD(TAG, "Got properties count %d", prop_count);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
typedef void (*prop_val_free_fn_t)(void *val);
|
||||
|
||||
static esp_err_t cmd_get_prop_vals_handler(LocalCtrlMessage *req,
|
||||
LocalCtrlMessage *resp, void **ctx)
|
||||
{
|
||||
SAFE_ALLOCATION(RespGetPropertyValues, resp_payload);
|
||||
resp_get_property_values__init(resp_payload);
|
||||
|
||||
esp_local_ctrl_prop_val_t *vals = calloc(req->cmd_get_prop_vals->n_indices,
|
||||
sizeof(esp_local_ctrl_prop_val_t));
|
||||
esp_local_ctrl_prop_t *descs = calloc(req->cmd_get_prop_vals->n_indices,
|
||||
sizeof(esp_local_ctrl_prop_t));
|
||||
prop_val_free_fn_t *free_fns = calloc(req->cmd_get_prop_vals->n_indices,
|
||||
sizeof(prop_val_free_fn_t));
|
||||
resp_payload->props = calloc(req->cmd_get_prop_vals->n_indices,
|
||||
sizeof(PropertyInfo *));
|
||||
if (!vals || !descs || !free_fns || !resp_payload->props) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for getting values");
|
||||
free(vals);
|
||||
free(descs);
|
||||
free(free_fns);
|
||||
free(resp_payload->props);
|
||||
free(resp_payload);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
esp_err_t ret = esp_local_ctrl_get_prop_values(req->cmd_get_prop_vals->n_indices,
|
||||
req->cmd_get_prop_vals->indices,
|
||||
descs, vals);
|
||||
resp_payload->status = err_to_status(ret);
|
||||
if (ret == ESP_OK) {
|
||||
resp_payload->n_props = 0;
|
||||
for (size_t i = 0; i < req->cmd_get_prop_vals->n_indices; i++) {
|
||||
ESP_COMPILER_DIAGNOSTIC_PUSH_IGNORE("-Wanalyzer-malloc-leak") // False-positive detection. TODO GCC-366
|
||||
resp_payload->props[i] = malloc(sizeof(PropertyInfo));
|
||||
if (!resp_payload->props[i]) {
|
||||
resp_payload->status = STATUS__InternalError;
|
||||
break;
|
||||
}
|
||||
ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-malloc-leak")
|
||||
resp_payload->n_props++;
|
||||
property_info__init(resp_payload->props[i]);
|
||||
resp_payload->props[i]->name = descs[i].name;
|
||||
resp_payload->props[i]->type = descs[i].type;
|
||||
resp_payload->props[i]->flags = descs[i].flags;
|
||||
resp_payload->props[i]->value.data = vals[i].data;
|
||||
resp_payload->props[i]->value.len = vals[i].size;
|
||||
free_fns[i] = vals[i].free_fn;
|
||||
}
|
||||
}
|
||||
resp->payload_case = LOCAL_CTRL_MESSAGE__PAYLOAD_RESP_GET_PROP_VALS;
|
||||
resp->resp_get_prop_vals = resp_payload;
|
||||
(*ctx) = (void *)free_fns;
|
||||
free(vals);
|
||||
free(descs);
|
||||
|
||||
/* Unless it's a fatal error, always return ESP_OK, otherwise
|
||||
* the underlying connection will be closed by protocomm */
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t cmd_set_prop_vals_handler(LocalCtrlMessage *req,
|
||||
LocalCtrlMessage *resp, void **ctx)
|
||||
{
|
||||
SAFE_ALLOCATION(RespSetPropertyValues, resp_payload);
|
||||
resp_set_property_values__init(resp_payload);
|
||||
|
||||
uint32_t *idxs = calloc(req->cmd_set_prop_vals->n_props, sizeof(uint32_t));
|
||||
esp_local_ctrl_prop_val_t *vals = calloc(req->cmd_set_prop_vals->n_props,
|
||||
sizeof(esp_local_ctrl_prop_val_t));
|
||||
if (!idxs || !vals) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for setting values");
|
||||
free(idxs);
|
||||
free(vals);
|
||||
free(resp_payload);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
for (size_t i = 0; i < req->cmd_set_prop_vals->n_props; i++) {
|
||||
idxs[i] = req->cmd_set_prop_vals->props[i]->index;
|
||||
vals[i].data = req->cmd_set_prop_vals->props[i]->value.data;
|
||||
vals[i].size = req->cmd_set_prop_vals->props[i]->value.len;
|
||||
}
|
||||
|
||||
esp_err_t ret = esp_local_ctrl_set_prop_values(req->cmd_set_prop_vals->n_props,
|
||||
idxs, vals);
|
||||
resp_payload->status = err_to_status(ret);
|
||||
resp->payload_case = LOCAL_CTRL_MESSAGE__PAYLOAD_RESP_SET_PROP_VALS;
|
||||
resp->resp_set_prop_vals = resp_payload;
|
||||
free(idxs);
|
||||
free(vals);
|
||||
|
||||
/* Unless it's a fatal error, always return ESP_OK, otherwise
|
||||
* the underlying connection will be closed by protocomm */
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int lookup_cmd_handler(int cmd_id)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(cmd_table)/sizeof(esp_local_ctrl_cmd_t); i++) {
|
||||
if (cmd_table[i].cmd_num == cmd_id) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void esp_local_ctrl_command_cleanup(LocalCtrlMessage *resp, void **ctx)
|
||||
{
|
||||
if (!resp) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (resp->msg) {
|
||||
case LOCAL_CTRL_MSG_TYPE__TypeRespGetPropertyCount:
|
||||
free(resp->resp_get_prop_count);
|
||||
break;
|
||||
case LOCAL_CTRL_MSG_TYPE__TypeRespGetPropertyValues: {
|
||||
if (resp->resp_get_prop_vals) {
|
||||
prop_val_free_fn_t *free_fns = (prop_val_free_fn_t *)(*ctx);
|
||||
for (size_t i = 0; i < resp->resp_get_prop_vals->n_props; i++) {
|
||||
if (free_fns && free_fns[i]) {
|
||||
free_fns[i](resp->resp_get_prop_vals->props[i]->value.data);
|
||||
}
|
||||
free(resp->resp_get_prop_vals->props[i]);
|
||||
}
|
||||
free(free_fns);
|
||||
free(resp->resp_get_prop_vals->props);
|
||||
free(resp->resp_get_prop_vals);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case LOCAL_CTRL_MSG_TYPE__TypeRespSetPropertyValues:
|
||||
free(resp->resp_set_prop_vals);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unsupported response type in cleanup_handler");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static esp_err_t esp_local_ctrl_command_dispatcher(LocalCtrlMessage *req,
|
||||
LocalCtrlMessage *resp,
|
||||
void **ctx)
|
||||
{
|
||||
int cmd_index = lookup_cmd_handler(req->msg);
|
||||
if (cmd_index < 0) {
|
||||
ESP_LOGE(TAG, "Invalid command handler lookup");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (req->payload_case != cmd_table[cmd_index].expected_payload_case) {
|
||||
ESP_LOGE(TAG, "Payload type mismatch: msg_type %d expects payload %d, got %d",
|
||||
req->msg, cmd_table[cmd_index].expected_payload_case, req->payload_case);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t ret = cmd_table[cmd_index].command_handler(req, resp, ctx);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error executing command handler");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_local_ctrl_data_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen,
|
||||
uint8_t **outbuf, ssize_t *outlen, void *priv_data)
|
||||
{
|
||||
void *temp_ctx = NULL;
|
||||
LocalCtrlMessage *req = local_ctrl_message__unpack(NULL, inlen, inbuf);
|
||||
if (!req) {
|
||||
ESP_LOGE(TAG, "Unable to unpack payload data");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
LocalCtrlMessage resp;
|
||||
local_ctrl_message__init(&resp);
|
||||
resp.msg = req->msg + 1; /* Response is request + 1 */
|
||||
|
||||
esp_err_t ret = esp_local_ctrl_command_dispatcher(req, &resp, &temp_ctx);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "command dispatcher failed");
|
||||
esp_local_ctrl_command_cleanup(&resp, &temp_ctx);
|
||||
local_ctrl_message__free_unpacked(req, NULL);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
local_ctrl_message__free_unpacked(req, NULL);
|
||||
|
||||
*outlen = local_ctrl_message__get_packed_size(&resp);
|
||||
if (*outlen <= 0) {
|
||||
ESP_LOGE(TAG, "Invalid encoding for response");
|
||||
esp_local_ctrl_command_cleanup(&resp, &temp_ctx);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
*outbuf = (uint8_t *) malloc(*outlen);
|
||||
if (!*outbuf) {
|
||||
ESP_LOGE(TAG, "System out of memory");
|
||||
esp_local_ctrl_command_cleanup(&resp, &temp_ctx);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
local_ctrl_message__pack(&resp, *outbuf);
|
||||
esp_local_ctrl_command_cleanup(&resp, &temp_ctx);
|
||||
ESP_LOG_BUFFER_HEX_LEVEL(TAG, *outbuf, *outlen, ESP_LOG_DEBUG);
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
// Copyright 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <esp_err.h>
|
||||
#include <protocomm.h>
|
||||
#include <esp_local_ctrl.h>
|
||||
|
||||
/**
|
||||
* @brief `esp_local_ctrl` transport specific structure
|
||||
*
|
||||
* Every supported transport layer should have the following functions
|
||||
* implemented for starting, stopping and configuring a protocomm service
|
||||
*/
|
||||
struct esp_local_ctrl_transport {
|
||||
/**
|
||||
* Handler for starting a protocomm service as per specified configuration
|
||||
*/
|
||||
esp_err_t (*start_service) (protocomm_t *pc,
|
||||
const esp_local_ctrl_transport_config_t *config);
|
||||
|
||||
/**
|
||||
* Handler for stopping a protocomm service
|
||||
*/
|
||||
void (*stop_service) (protocomm_t *pc);
|
||||
|
||||
/**
|
||||
* Handler for creating a copy of the transport specific configuration
|
||||
*/
|
||||
esp_err_t (*copy_config) (esp_local_ctrl_transport_config_t *dest_config,
|
||||
const esp_local_ctrl_transport_config_t *src_config);
|
||||
|
||||
/**
|
||||
* Handler for allocating resources corresponding to a protocomm endpoint.
|
||||
* Usually when adding a new endpoint `protocomm_endpoint_add()` API is used,
|
||||
* but the transport layer may need to perform resource allocation for
|
||||
* each endpoint, prior to starting the protocomm instance. This handler
|
||||
* is useful in that case, as it is called before `start_service()`.
|
||||
*/
|
||||
esp_err_t (*declare_ep) (esp_local_ctrl_transport_config_t *config,
|
||||
const char *ep_name, uint16_t ep_uuid);
|
||||
|
||||
/**
|
||||
* Handler for freeing a transport specific configuration
|
||||
*/
|
||||
void (*free_config) (esp_local_ctrl_transport_config_t *config);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Protocomm handler for `esp_local_ctrl`
|
||||
*
|
||||
* This is the handler which is responsible for processing incoming requests
|
||||
* over a protocomm channel, then invokes one of the following functions
|
||||
* depending upon the request type:
|
||||
* - `esp_local_ctrl_get_prop_count()`
|
||||
* - `esp_local_ctrl_get_prop_values()`
|
||||
* -` esp_local_ctrl_set_prop_values()`
|
||||
* The output of the above functions are used to form the response messages
|
||||
* corresponding to request types. The formed response messages are packed and
|
||||
* sent back via the protocomm channel.
|
||||
*
|
||||
* @param[in] session_id A number to identify an ongoing session between
|
||||
* device and client
|
||||
* @param[in] inbuf Buffer which holds serialized / packed request data
|
||||
* @param[in] inlen Length of input buffer
|
||||
* @param[out] outbuf Buffer which holds serialized / packed response data
|
||||
* @param[out] outlen Length of output buffer
|
||||
* @param[in] priv_data Private data associated with `esp_local_ctrl` endpoint
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK : Success
|
||||
* - ESP_FAIL : Failure
|
||||
*/
|
||||
esp_err_t esp_local_ctrl_data_handler(uint32_t session_id, const uint8_t *inbuf, ssize_t inlen,
|
||||
uint8_t **outbuf, ssize_t *outlen, void *priv_data);
|
||||
|
||||
/**
|
||||
* @brief Use this for obtaining total number of properties registered
|
||||
* with `esp_local_ctrl` service
|
||||
*
|
||||
* @param[out] count Pointer to variable where the result is to be stored
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK : Success
|
||||
* - ESP_FAIL : Failure
|
||||
*/
|
||||
esp_err_t esp_local_ctrl_get_prop_count(size_t *count);
|
||||
|
||||
/**
|
||||
* @brief Get descriptions and values of multiple properties at the same time.
|
||||
* The properties are requested by indices. This internally calls the
|
||||
* `get_prop_values` handler specified in the `esp_local_ctrl_handlers_t`
|
||||
* structure. Since `get_prop_values` accepts property structure, the
|
||||
* indices are first converted to the corresponding `esp_local_ctrl_prop_t`
|
||||
* internally.
|
||||
*
|
||||
* @param[in] total_indices The number of elements in the `indices` array argument
|
||||
* @param[in] indices An array of indices, that specify which properties to get
|
||||
* @param[out] props A pre-allocated array of empty property structures, elements of
|
||||
* which are to be populated with names, types and flags of those
|
||||
* properties which correspond to the provided indices
|
||||
* @param[out] values A pre-allocated array of empty value structures, elements of
|
||||
* which are to be populated with values and sizes of those
|
||||
* properties which correspond to the provided indices
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK : Success
|
||||
* - ESP_FAIL : Failure
|
||||
*/
|
||||
esp_err_t esp_local_ctrl_get_prop_values(size_t total_indices, uint32_t *indices,
|
||||
esp_local_ctrl_prop_t *props,
|
||||
esp_local_ctrl_prop_val_t *values);
|
||||
|
||||
/**
|
||||
* @brief Set values of multiple properties at the same time. The properties to
|
||||
* set are specified by indices. This internally calls the `set_prop_values`
|
||||
* handler specified in the `esp_local_ctrl_handlers_t` structure. Since
|
||||
* `set_prop_values` accepts property structures, the indices are first
|
||||
* converted to the corresponding `esp_local_ctrl_prop_t` internally.
|
||||
*
|
||||
* @param[in] total_indices The number of elements in the `indices` array argument
|
||||
* @param[in] indices An array of indices, that specify which properties to set
|
||||
* @param[in] values A array of values. Every value should have the correct
|
||||
* size, if it is for setting a fixed size property, else
|
||||
* error will be generated and none of the properties will
|
||||
* be set to any of the given values
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK : Success
|
||||
* - ESP_FAIL : Failure
|
||||
*/
|
||||
esp_err_t esp_local_ctrl_set_prop_values(size_t total_indices, uint32_t *indices,
|
||||
const esp_local_ctrl_prop_val_t *values);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <esp_local_ctrl.h>
|
||||
#include <protocomm_ble.h>
|
||||
|
||||
#include "esp_local_ctrl_priv.h"
|
||||
|
||||
#define LOCAL_CTRL_VERSION "v1.0"
|
||||
|
||||
static const char *TAG = "esp_local_ctrl_transport_ble";
|
||||
|
||||
static esp_err_t
|
||||
start_ble_transport(protocomm_t *pc,
|
||||
const esp_local_ctrl_transport_config_t *config)
|
||||
{
|
||||
if (!config || !config->ble) {
|
||||
ESP_LOGE(TAG, "NULL configuration provided");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
return protocomm_ble_start(pc, config->ble);
|
||||
}
|
||||
|
||||
static void stop_ble_transport(protocomm_t *pc)
|
||||
{
|
||||
protocomm_ble_stop(pc);
|
||||
}
|
||||
|
||||
static esp_err_t
|
||||
copy_ble_config(esp_local_ctrl_transport_config_t *dest_config,
|
||||
const esp_local_ctrl_transport_config_t *src_config)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (!dest_config || !src_config || !src_config->ble) {
|
||||
ESP_LOGE(TAG, "NULL arguments provided");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
dest_config->ble = calloc(1, sizeof(protocomm_ble_config_t));
|
||||
if (!dest_config->ble) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for BLE transport config");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
/* Copy BLE device name */
|
||||
memcpy(dest_config->ble->device_name, src_config->ble->device_name,
|
||||
sizeof(src_config->ble->device_name));
|
||||
|
||||
/* Copy Service UUID */
|
||||
memcpy(dest_config->ble->service_uuid, src_config->ble->service_uuid,
|
||||
sizeof(src_config->ble->service_uuid));
|
||||
|
||||
dest_config->ble->nu_lookup_count = 0;
|
||||
if (src_config->ble->nu_lookup_count) {
|
||||
/* Copy any provided name-uuid lookup table */
|
||||
dest_config->ble->nu_lookup = calloc(src_config->ble->nu_lookup_count,
|
||||
sizeof(protocomm_ble_name_uuid_t));
|
||||
if (!dest_config->ble->nu_lookup) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for BLE characteristic names");
|
||||
free(dest_config->ble);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
for (uint16_t i = 0; i < src_config->ble->nu_lookup_count; i++) {
|
||||
dest_config->ble->nu_lookup[i].uuid = src_config->ble->nu_lookup[i].uuid;
|
||||
if (!src_config->ble->nu_lookup[i].name) {
|
||||
ESP_LOGE(TAG, "Endpoint name cannot be null");
|
||||
ret = ESP_ERR_INVALID_ARG;
|
||||
goto err_free_nu_lookup;
|
||||
}
|
||||
dest_config->ble->nu_lookup[i].name =
|
||||
strdup(src_config->ble->nu_lookup[i].name);
|
||||
if (!dest_config->ble->nu_lookup[i].name) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for endpoint name");
|
||||
ret = ESP_ERR_NO_MEM;
|
||||
goto err_free_nu_lookup;
|
||||
}
|
||||
dest_config->ble->nu_lookup_count++;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
err_free_nu_lookup:
|
||||
for (uint16_t i = 0; i < dest_config->ble->nu_lookup_count; i++) {
|
||||
free((void *)dest_config->ble->nu_lookup[i].name);
|
||||
}
|
||||
free(dest_config->ble->nu_lookup);
|
||||
free(dest_config->ble);
|
||||
dest_config->ble = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t declare_endpoint(esp_local_ctrl_transport_config_t *config,
|
||||
const char *ep_name, uint16_t ep_uuid)
|
||||
{
|
||||
if (!config || !config->ble) {
|
||||
ESP_LOGE(TAG, "NULL configuration provided");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
protocomm_ble_name_uuid_t *nu_lookup =
|
||||
realloc(config->ble->nu_lookup, (config->ble->nu_lookup_count + 1) *
|
||||
sizeof(protocomm_ble_name_uuid_t));
|
||||
if (!nu_lookup) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for new endpoint entry");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
config->ble->nu_lookup = nu_lookup;
|
||||
nu_lookup[config->ble->nu_lookup_count].uuid = ep_uuid;
|
||||
nu_lookup[config->ble->nu_lookup_count].name = strdup(ep_name);
|
||||
if (!nu_lookup[config->ble->nu_lookup_count].name) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for new endpoint name");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
config->ble->nu_lookup_count++;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void free_config(esp_local_ctrl_transport_config_t *config)
|
||||
{
|
||||
if (config && config->ble) {
|
||||
for (unsigned int i = 0; i < config->ble->nu_lookup_count; i++) {
|
||||
free((void *)config->ble->nu_lookup[i].name);
|
||||
}
|
||||
free(config->ble->nu_lookup);
|
||||
free(config->ble);
|
||||
config->ble = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const esp_local_ctrl_transport_t *esp_local_ctrl_get_transport_ble(void)
|
||||
{
|
||||
static const esp_local_ctrl_transport_t tp = {
|
||||
.start_service = start_ble_transport,
|
||||
.stop_service = stop_ble_transport,
|
||||
.copy_config = copy_ble_config,
|
||||
.declare_ep = declare_endpoint,
|
||||
.free_config = free_config
|
||||
};
|
||||
return &tp;
|
||||
};
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#if defined __has_include
|
||||
# if __has_include("mdns.h")
|
||||
# define WITH_MDNS
|
||||
# include "mdns.h"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <esp_netif.h>
|
||||
#include <protocomm_httpd.h>
|
||||
#include <esp_local_ctrl.h>
|
||||
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
|
||||
#include <esp_https_server.h>
|
||||
#else
|
||||
#include <esp_http_server.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
|
||||
#define esp_local_ctrl_httpd_start httpd_ssl_start
|
||||
#define esp_local_ctrl_httpd_stop httpd_ssl_stop
|
||||
#else
|
||||
#define esp_local_ctrl_httpd_start httpd_start
|
||||
#define esp_local_ctrl_httpd_stop httpd_stop
|
||||
#endif
|
||||
|
||||
#include "esp_local_ctrl_priv.h"
|
||||
|
||||
#define LOCAL_CTRL_VERSION "v1.0"
|
||||
|
||||
static const char *TAG = "esp_local_ctrl_transport_httpd";
|
||||
|
||||
static httpd_handle_t server_handle = NULL;
|
||||
|
||||
static esp_err_t start_httpd_transport(protocomm_t *pc, const esp_local_ctrl_transport_config_t *config)
|
||||
{
|
||||
if (!config || !config->httpd) {
|
||||
ESP_LOGE(TAG, "NULL configuration provided");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t err;
|
||||
#ifdef WITH_MDNS
|
||||
uint16_t port = 0;
|
||||
#ifdef CONFIG_ESP_HTTPS_SERVER_ENABLE
|
||||
/* Extract configured port */
|
||||
port = (
|
||||
config->httpd->transport_mode == HTTPD_SSL_TRANSPORT_SECURE ?
|
||||
config->httpd->port_secure :
|
||||
config->httpd->port_insecure
|
||||
);
|
||||
#else
|
||||
port = config->httpd->server_port;
|
||||
#endif
|
||||
err = mdns_service_add("Local Control Service", "_esp_local_ctrl",
|
||||
"_tcp", port, NULL, 0);
|
||||
if (err != ESP_OK) {
|
||||
/* mDNS is not mandatory for provisioning to work,
|
||||
* so print warning and return without failure */
|
||||
ESP_LOGW(TAG, "Error adding mDNS service! Check if mDNS is running");
|
||||
} else {
|
||||
/* Information to identify the roles of the various
|
||||
* protocomm endpoint URIs provided by the service */
|
||||
err |= mdns_service_txt_item_set("_esp_local_ctrl", "_tcp",
|
||||
"version_endpoint", "/esp_local_ctrl/version");
|
||||
err |= mdns_service_txt_item_set("_esp_local_ctrl", "_tcp",
|
||||
"session_endpoint", "/esp_local_ctrl/session");
|
||||
err |= mdns_service_txt_item_set("_esp_local_ctrl", "_tcp",
|
||||
"control_endpoint", "/esp_local_ctrl/control");
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error adding mDNS service text item");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
err = esp_local_ctrl_httpd_start(&server_handle, config->httpd);
|
||||
if (ESP_OK != err) {
|
||||
ESP_LOGE(TAG, "Error starting HTTP/S service!");
|
||||
#ifdef WITH_MDNS
|
||||
mdns_service_remove("_esp_local_ctrl", "_tcp");
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
protocomm_httpd_config_t pc_conf = {
|
||||
.ext_handle_provided = true,
|
||||
.data = {
|
||||
.handle = &server_handle
|
||||
}
|
||||
};
|
||||
|
||||
return protocomm_httpd_start(pc, &pc_conf);
|
||||
}
|
||||
|
||||
static void stop_httpd_transport(protocomm_t *pc)
|
||||
{
|
||||
#ifdef WITH_MDNS
|
||||
mdns_service_remove("_esp_local_ctrl", "_tcp");
|
||||
#endif
|
||||
protocomm_httpd_stop(pc);
|
||||
if (esp_local_ctrl_httpd_stop(server_handle) == ESP_OK) {
|
||||
server_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t copy_httpd_config(esp_local_ctrl_transport_config_t *dest_config, const esp_local_ctrl_transport_config_t *src_config)
|
||||
{
|
||||
if (!dest_config || !src_config || !src_config->httpd) {
|
||||
ESP_LOGE(TAG, "NULL configuration provided");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
dest_config->httpd = calloc(1, sizeof(esp_local_ctrl_transport_config_httpd_t));
|
||||
if (!dest_config->httpd) {
|
||||
ESP_LOGE(TAG, "Failed to allocate memory for HTTPD transport config");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
memcpy(dest_config->httpd,
|
||||
src_config->httpd,
|
||||
sizeof(esp_local_ctrl_transport_config_httpd_t));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void free_config(esp_local_ctrl_transport_config_t *config)
|
||||
{
|
||||
if (config && config->httpd) {
|
||||
free(config->httpd);
|
||||
config->httpd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const esp_local_ctrl_transport_t *esp_local_ctrl_get_transport_httpd(void)
|
||||
{
|
||||
static const esp_local_ctrl_transport_t tp = {
|
||||
.start_service = start_httpd_transport,
|
||||
.stop_service = stop_httpd_transport,
|
||||
.copy_config = copy_httpd_config,
|
||||
.declare_ep = NULL,
|
||||
.free_config = free_config
|
||||
};
|
||||
return &tp;
|
||||
};
|
||||
Reference in New Issue
Block a user