esp_nano_hosted/src/nh_ctrl_api.c

354 lines
11 KiB
C

#include <string.h>
/* Private */
#include "nh_ctrl_api.h"
#include "protobuf-c/protobuf-c.h"
/* PB config */
#include "esp_hosted_config.pb-c.h"
#define NH_COMMAND_TIMEOUT_MSEC 30000
#define NH_COMMAND_REQ_MEM_SIZE 256
#define NH_CTRL_API_CREATE_ALLOCATOR(allocator_name, allocator_ctx) \
ProtobufCAllocator allocator_name = { \
.alloc = nh_ctrl_api_protobuf_allocate, \
.free = nh_ctrl_api_protobuf_free, \
.allocator_data = allocator_ctx, \
}
#define NH_CTRL_API_TAKE_SEM(s_api, timeout) s_api->osa->sem_take(s_api->osa->user_data, s_api->p_sem_req, timeout)
#define NH_CTRL_API_GIVE_SEM(s_api) s_api->osa->sem_give(api->osa->user_data, api->p_sem_req)
#define NH_CTRL_API_ALLOC(s_api, ptr, p_size) s_api->osa->buf_allocate(s_api->osa->user_data, ptr, p_size)
#define NH_CTRL_API_FREE(s_api, ptr) s_api->osa->buf_free(s_api->osa->user_data, ptr)
static nh_ret_t nh_ctrl_api_parse_event(nh_ctrl_api_t *api, CtrlMsgId *event_id, uint8_t *buf, uint32_t buf_len);
static void nh_ctrl_api_dispatch_event(nh_ctrl_api_t *api, CtrlMsgId event_id);
static nh_ret_t nh_ctrl_api_general_request(nh_ctrl_api_t *api, CtrlMsg *tx_msg);
static nh_ret_t nh_ctrl_api_general_response(nh_ctrl_api_t *api, CtrlMsg **rx_msg, ProtobufCAllocator *allocator);
static void *nh_ctrl_api_protobuf_allocate(void *handle, size_t size);
static void nh_ctrl_api_protobuf_free(void *handle, void *data);
nh_ret_t nh_ctrl_api_init(nh_ctrl_api_t *api) {
nh_ret_t ret = NH_RET_SUCCESS;
ret = api->osa->sem_create(api->osa->user_data, &api->p_sem_req);
if (ret != NH_RET_SUCCESS) {
return ret;
}
ret = api->osa->sem_create(api->osa->user_data, &api->p_sem_resp);
if (ret != NH_RET_SUCCESS) {
goto free_sem_req;
}
ret = api->osa->sem_give(api->osa->user_data, api->p_sem_req);
if (ret != NH_RET_SUCCESS) {
goto free_sem_resp;
}
return ret;
free_sem_resp:
api->osa->sem_destroy(api->osa->user_data, api->p_sem_resp);
free_sem_req:
api->osa->sem_destroy(api->osa->user_data, api->p_sem_req);
return ret;
}
nh_ret_t nh_ctrl_api_get_mac_address(nh_ctrl_api_t *api, uint8_t *mac_addr, nh_ctrl_wifi_mode_t mode) {
nh_ret_t ret = NH_RET_SUCCESS;
NH_CTRL_API_CREATE_ALLOCATOR(c_alloc, api);
ret = NH_CTRL_API_TAKE_SEM(api, NH_COMMAND_TIMEOUT_MSEC);
if (ret != NH_RET_SUCCESS) return ret;
CtrlMsg tx_msg;
ctrl_msg__init(&tx_msg);
tx_msg.msg_id = CTRL_MSG_ID__Req_GetMACAddress;
tx_msg.payload_case = CTRL_MSG__PAYLOAD_REQ_GET_MAC_ADDRESS;
ret = NH_CTRL_API_ALLOC(api, (uint8_t **)&tx_msg.req_get_mac_address, sizeof(CtrlMsgReqGetMacAddress));
if (ret != NH_RET_SUCCESS) goto give_sem_exit;
ctrl_msg__req__get_mac_address__init(tx_msg.req_get_mac_address);
tx_msg.req_get_mac_address->mode = (mode == NH_CTRL_WIFI_MODE_AP) ? CTRL__WIFI_MODE__AP : CTRL__WIFI_MODE__STA;
ret = nh_ctrl_api_general_request(api, &tx_msg);
if (ret != NH_RET_SUCCESS) goto free_payload_exit;
CtrlMsg *rx_msg;
ret = nh_ctrl_api_general_response(api, &rx_msg, &c_alloc);
if (ret != NH_RET_SUCCESS || rx_msg->msg_id != CTRL_MSG_ID__Resp_GetMACAddress) goto free_msg_exit;
/* Sanity check */
if (rx_msg->resp_get_mac_address->mac.len != NH_CTRL_API_MAC_LENGTH) goto free_msg_exit;
memcpy(mac_addr, rx_msg->resp_get_mac_address->mac.data, rx_msg->resp_get_mac_address->mac.len);
mac_addr[rx_msg->resp_get_mac_address->mac.len] = '\0';
free_msg_exit:
ctrl_msg__free_unpacked(rx_msg, &c_alloc);
free_payload_exit:
NH_CTRL_API_FREE(api, (uint8_t *)tx_msg.req_get_mac_address);
give_sem_exit:
NH_CTRL_API_GIVE_SEM(api);
return ret;
}
nh_ret_t nh_ctrl_api_get_ap_scan_list(nh_ctrl_api_t *api, nh_ctrl_api_ap_scan_list_cb_t cb) {
nh_ret_t ret = NH_RET_SUCCESS;
NH_CTRL_API_CREATE_ALLOCATOR(c_alloc, api);
ret = NH_CTRL_API_TAKE_SEM(api, NH_COMMAND_TIMEOUT_MSEC);
if (ret != NH_RET_SUCCESS) return ret;
CtrlMsg tx_msg;
ctrl_msg__init(&tx_msg);
tx_msg.msg_id = CTRL_MSG_ID__Req_GetAPScanList;
tx_msg.payload_case = CTRL_MSG__PAYLOAD_REQ_SCAN_AP_LIST;
ret = NH_CTRL_API_ALLOC(api, (uint8_t **)&tx_msg.req_scan_ap_list, sizeof(CtrlMsgReqScanResult));
if (ret != NH_RET_SUCCESS) goto give_sem_exit;
ctrl_msg__req__scan_result__init(tx_msg.req_scan_ap_list);
ret = nh_ctrl_api_general_request(api, &tx_msg);
if (ret != NH_RET_SUCCESS) goto free_payload_exit;
CtrlMsg *rx_msg;
ret = nh_ctrl_api_general_response(api, &rx_msg, &c_alloc);
if (ret != NH_RET_SUCCESS || rx_msg->msg_id != CTRL_MSG_ID__Resp_GetAPScanList) goto free_msg_exit;
for (uint32_t i = 0; i < rx_msg->resp_scan_ap_list->count; i++) {
nh_ctrl_ap_scan_item_t item = {
.rssi = rx_msg->resp_scan_ap_list->entries[i]->rssi,
.channel = rx_msg->resp_scan_ap_list->entries[i]->chnl,
.encryption_mode = (nh_ctrl_ap_enc_mode_t)rx_msg->resp_scan_ap_list->entries[i]->sec_prot,
};
uint32_t ssid_len = rx_msg->resp_scan_ap_list->entries[i]->ssid.len;
uint32_t bssid_len = rx_msg->resp_scan_ap_list->entries[i]->bssid.len;
ret = NH_CTRL_API_ALLOC(api, (uint8_t **)&item.ssid, ssid_len + 1);
if (ret != NH_RET_SUCCESS) goto free_msg_exit;
ret = NH_CTRL_API_ALLOC(api, (uint8_t **)&item.bssid, bssid_len + 1);
if (ret != NH_RET_SUCCESS) {
NH_CTRL_API_FREE(api, (uint8_t *)item.ssid);
goto free_msg_exit;
}
memcpy(item.ssid, rx_msg->resp_scan_ap_list->entries[i]->ssid.data, ssid_len);
memcpy(item.bssid, rx_msg->resp_scan_ap_list->entries[i]->bssid.data, bssid_len);
item.ssid[ssid_len] = '\0';
item.bssid[bssid_len] = '\0';
if (cb) {
cb(api->user_data, &item);
}
NH_CTRL_API_FREE(api, (uint8_t *)item.ssid);
NH_CTRL_API_FREE(api, (uint8_t *)item.bssid);
}
free_msg_exit:
ctrl_msg__free_unpacked(rx_msg, &c_alloc);
free_payload_exit:
NH_CTRL_API_FREE(api, (uint8_t *)tx_msg.req_get_mac_address);
give_sem_exit:
NH_CTRL_API_GIVE_SEM(api);
return ret;
}
nh_ret_t nh_ctrl_api_connect_ap(nh_ctrl_api_t *api, nh_ctrl_ap_conn_params_t *params,
nh_ctrl_ap_conn_result_t *result) {
nh_ret_t ret = NH_RET_SUCCESS;
NH_CTRL_API_CREATE_ALLOCATOR(c_alloc, api);
ret = NH_CTRL_API_TAKE_SEM(api, NH_COMMAND_TIMEOUT_MSEC);
if (ret != NH_RET_SUCCESS) return ret;
CtrlMsg tx_msg;
ctrl_msg__init(&tx_msg);
tx_msg.msg_id = CTRL_MSG_ID__Req_ConnectAP;
tx_msg.payload_case = CTRL_MSG__PAYLOAD_REQ_CONNECT_AP;
ret = NH_CTRL_API_ALLOC(api, (uint8_t **)&tx_msg.req_connect_ap, sizeof(CtrlMsgReqConnectAP));
if (ret != NH_RET_SUCCESS) goto give_sem_exit;
ctrl_msg__req__connect_ap__init(tx_msg.req_connect_ap);
tx_msg.req_connect_ap->ssid = params->ssid;
tx_msg.req_connect_ap->bssid = params->bssid;
tx_msg.req_connect_ap->pwd = params->password;
tx_msg.req_connect_ap->is_wpa3_supported = params->wpa3_supported;
tx_msg.req_connect_ap->listen_interval = params->listen_interval;
ret = nh_ctrl_api_general_request(api, &tx_msg);
if (ret != NH_RET_SUCCESS) goto free_payload_exit;
CtrlMsg *rx_msg;
ret = nh_ctrl_api_general_response(api, &rx_msg, &c_alloc);
if (ret != NH_RET_SUCCESS || rx_msg->msg_id != CTRL_MSG_ID__Resp_ConnectAP) goto free_msg_exit;
uint32_t mac_len = rx_msg->resp_connect_ap->mac.len;
if (mac_len != NH_CTRL_API_MAC_LENGTH) goto free_msg_exit;
memcpy(result->mac_addr, rx_msg->resp_connect_ap->mac.data, mac_len);
result->mac_addr[mac_len] = '\0';
result->status = (nh_ctrl_conn_status_t)rx_msg->resp_connect_ap->resp;
free_msg_exit:
ctrl_msg__free_unpacked(rx_msg, &c_alloc);
free_payload_exit:
NH_CTRL_API_FREE(api, (uint8_t *)tx_msg.req_connect_ap);
give_sem_exit:
NH_CTRL_API_GIVE_SEM(api);
return ret;
}
void nh_ctrl_api_task(nh_ctrl_api_t *api) {
nh_ret_t ret = NH_RET_SUCCESS;
uint8_t *payload;
uint32_t payload_size;
nh_serial_ep_type_t ep_type;
ret = nh_shared_if_serial_if_recv(api->shared_if, &ep_type, &payload, &payload_size, NH_COMMAND_TIMEOUT_MSEC);
if (ret != NH_RET_SUCCESS) {
return;
}
if (ep_type == NH_SERIAL_EP_RESP) {
api->p_buf_rx = payload;
api->p_buf_rx_len = payload_size;
ret = api->osa->sem_give(api->osa->user_data, api->p_sem_resp);
if (ret != NH_RET_SUCCESS) {
nh_shared_if_serial_if_free(api->shared_if, payload);
return;
}
} else if (ep_type == NH_SERIAL_EP_EVENT) {
CtrlMsgId event_id;
ret = nh_ctrl_api_parse_event(api, &event_id, payload, payload_size);
if (ret == NH_RET_SUCCESS) {
nh_ctrl_api_dispatch_event(api, event_id);
}
nh_shared_if_serial_if_free(api->shared_if, payload);
}
}
static nh_ret_t nh_ctrl_api_parse_event(nh_ctrl_api_t *api, CtrlMsgId *event_id, uint8_t *buf, uint32_t buf_len) {
NH_CTRL_API_CREATE_ALLOCATOR(c_alloc, api);
CtrlMsg *msg = ctrl_msg__unpack(&c_alloc, buf_len, buf);
if (msg == NULL) {
return NH_RET_FAIL;
}
*event_id = msg->msg_id;
ctrl_msg__free_unpacked(msg, &c_alloc);
return NH_RET_SUCCESS;
}
static void nh_ctrl_api_dispatch_event(nh_ctrl_api_t *api, CtrlMsgId event_id) {
switch (event_id) {
case CTRL_MSG_ID__Event_ESPInit: {
if (api->cb.init) {
api->cb.init(api->user_data);
}
break;
}
default:
break;
}
}
static nh_ret_t nh_ctrl_api_general_request(nh_ctrl_api_t *api, CtrlMsg *tx_msg) {
nh_ret_t ret = NH_RET_SUCCESS;
size_t packed_size = ctrl_msg__get_packed_size(tx_msg);
if (packed_size == 0) {
return NH_RET_FAIL;
}
uint8_t *req_buf;
ret = NH_CTRL_API_ALLOC(api, &req_buf, packed_size);
if (ret != NH_RET_SUCCESS) {
return ret;
}
if (ctrl_msg__pack(tx_msg, req_buf) != packed_size) {
ret = NH_RET_FAIL;
goto free_tx_exit;
}
ret = nh_shared_if_serial_if_send(api->shared_if, req_buf, packed_size, NH_COMMAND_TIMEOUT_MSEC);
free_tx_exit:
NH_CTRL_API_FREE(api, req_buf);
return ret;
}
static nh_ret_t nh_ctrl_api_general_response(nh_ctrl_api_t *api, CtrlMsg **rx_msg, ProtobufCAllocator *allocator) {
nh_ret_t ret = NH_RET_SUCCESS;
ret = api->osa->sem_take(api->osa->user_data, api->p_sem_resp, NH_COMMAND_TIMEOUT_MSEC);
if (ret != NH_RET_SUCCESS) {
return ret;
}
*rx_msg = ctrl_msg__unpack(allocator, api->p_buf_rx_len, api->p_buf_rx);
if (*rx_msg == NULL) {
ret = NH_RET_FAIL;
}
nh_shared_if_serial_if_free(api->shared_if, api->p_buf_rx);
return ret;
}
static void *nh_ctrl_api_protobuf_allocate(void *handle, size_t size) {
nh_ctrl_api_t *api = handle;
uint8_t *buf = NULL;
if (NH_CTRL_API_ALLOC(api, &buf, size) != NH_RET_SUCCESS) {
return NULL;
}
return buf;
}
static void nh_ctrl_api_protobuf_free(void *handle, void *data) {
nh_ctrl_api_t *api = handle;
NH_CTRL_API_FREE(api, data);
}