From 35f33adb70379a456f5223c25defbd36fd7b256e Mon Sep 17 00:00:00 2001 From: Yilin Sun Date: Tue, 3 Jan 2023 00:01:31 +0800 Subject: [PATCH] Added README.md and TO-DOs. Signed-off-by: Yilin Sun --- CMakeLists.txt | 1 + README.md | 85 ++++++++++++++++++++++++++++++++++++++++++ include/nh_common.h | 34 +++++++++++++++++ include/nh_ctrl_api.h | 53 +++----------------------- include/nh_shared_if.h | 41 ++++++++++++++++++++ src/nh_ctrl_api.c | 83 +++++++++-------------------------------- src/nh_shared_if.c | 75 +++++++++++++++++++++++++++++++++++++ 7 files changed, 259 insertions(+), 113 deletions(-) create mode 100644 README.md create mode 100644 include/nh_common.h create mode 100644 include/nh_shared_if.h create mode 100644 src/nh_shared_if.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0159697..9dd32b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,7 @@ project(esp_nano_hosted) set(NH_SOURCES "proto/esp_hosted_config.pb.c" "src/nh_ctrl_api.c" + "src/nh_shared_if.c" ) set(NH_INCLUDES diff --git a/README.md b/README.md new file mode 100644 index 0000000..185d830 --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# ESP Nano Hosted + +## Introduction + +This project is trying to interface with `esp-hosted` firmware using a MCU-friendly method. + +## SPI Frame Format + +* Each SPI transaction is fixed 1600 bytes +* Each transaction is prefixed by a 12-byte header, defined as follows: +```c +struct esp_payload_header { + uint8_t if_type:4; + uint8_t if_num:4; + uint8_t flags; + uint16_t len; + uint16_t offset; + uint16_t checksum; + uint16_t seq_num; + uint8_t reserved2; + union { + uint8_t reserved3; + uint8_t hci_pkt_type; + uint8_t priv_pkt_type; + }; +} __attribute__((packed)); +``` + +* The `if_type` field is one of the following enum: +```c +typedef enum { + ESP_STA_IF, + ESP_AP_IF, + ESP_SERIAL_IF, + ESP_HCI_IF, + ESP_PRIV_IF, + ESP_TEST_IF, + ESP_MAX_IF, +} ESP_INTERFACE_TYPE; +``` + +* Control requests and responses are handled through `ESP_SERIAL_IF`, which has the following TLV structure: + +```c +/* + * TLV (Type - Length - Value) structure is as follows: + * -------------------------------------------------------------------------------------------- + * Endpoint Type | Endpoint Length | Endpoint Value | Data Type | Data Length | Data Value | + * -------------------------------------------------------------------------------------------- + * + * Bytes used per field as follows: + * -------------------------------------------------------------------------------------------- + * 1 | 2 | Endpoint length | 1 | 2 | Data length | + * -------------------------------------------------------------------------------------------- + */ + +/* For type fields: */ +#define PROTO_PSER_TLV_T_EPNAME 0x01 +#define PROTO_PSER_TLV_T_DATA 0x02 + + +/* Some stupid constraints in the original code expects the length should be same... even they don't have to */ +#define CTRL_EP_NAME_RESP "ctrlResp" +#define CTRL_EP_NAME_EVENT "ctrlEvnt" + +``` + +* The control messages are encapsulated in protobuf, in the data field of the above TLV. +* For Host-to-ESP messages, the endpoint name `CTRL_EP_NAME_RESP` is used. +* For ESP-to-Host messages, the endpoint name `CTRL_EP_NAME_EVENT` or `CTRL_EP_NAME_RESP` are used. + +## Issues + +Who owns the buffers? How many buffers should we allocate? + +* Minimize the memory block operations (malloc and free) +* Control plane and data plane can be async +* Control API can be blocking until responded +* At least 2 full-sized buffers are required (for full duplex operations) +* To re-use the bus while a request is underway, control plane and data plane RX buffers should be copied +* Bus TX operation should be blocking, however the previous RX data (if any) can be received. +* The response for the request being transmitted will never arrive within the same transaction + +## License +Not decided yet, please be patient. At least not before the project is usable. \ No newline at end of file diff --git a/include/nh_common.h b/include/nh_common.h new file mode 100644 index 0000000..7afccaf --- /dev/null +++ b/include/nh_common.h @@ -0,0 +1,34 @@ +#ifndef NH_COMMON_H +#define NH_COMMON_H + +#include +#include + +typedef enum { + NH_RET_SUCCESS = 0, + NH_RET_FAIL = 1, + NH_RET_TIMEOUT = 2, +} nh_ret_t; + +/* OSA */ +typedef void *nh_osa_semaphore_t; +typedef nh_ret_t (*nh_osa_semaphore_create_t)(void *handle, nh_osa_semaphore_t *sem); +typedef nh_ret_t (*nh_osa_semaphore_take_t)(void *handle, nh_osa_semaphore_t sem, uint32_t timeout_msec); +typedef nh_ret_t (*nh_osa_semaphore_give_t)(void *handle, nh_osa_semaphore_t sem); +typedef nh_ret_t (*nh_osa_semaphore_destroy_t)(void *handle, nh_osa_semaphore_t sem); + +typedef nh_ret_t (*nh_osa_buf_allocate_t)(void *handle, uint8_t **buf, uint32_t buf_size); +typedef nh_ret_t (*nh_osa_buf_free_t)(void *handle, uint8_t *buf); + +typedef struct { + nh_osa_buf_allocate_t buf_allocate; + nh_osa_buf_free_t buf_free; + nh_osa_semaphore_create_t sem_create; + nh_osa_semaphore_take_t sem_take; + nh_osa_semaphore_give_t sem_give; + nh_osa_semaphore_destroy_t sem_destroy; + + void *user_data; +} nh_osa_t; + +#endif // NH_COMMON_H diff --git a/include/nh_ctrl_api.h b/include/nh_ctrl_api.h index 526e17a..500af65 100644 --- a/include/nh_ctrl_api.h +++ b/include/nh_ctrl_api.h @@ -1,28 +1,7 @@ #ifndef NH_CTRL_API_H #define NH_CTRL_API_H -#include -#include - -typedef enum { - NH_RET_SUCCESS = 0, - NH_RET_FAIL = 1, - NH_RET_TIMEOUT = 2, -} nh_ret_t; - -/* OSA */ -typedef void *nh_osa_semaphore_t; -typedef nh_ret_t (*nh_osa_semaphore_create_t)(void *handle, nh_osa_semaphore_t *sem); -typedef nh_ret_t (*nh_osa_semaphore_take_t)(void *handle, nh_osa_semaphore_t sem, uint32_t timeout_msec); -typedef nh_ret_t (*nh_osa_semaphore_give_t)(void *handle, nh_osa_semaphore_t sem); - -typedef nh_ret_t (*nh_osa_buf_allocate_t)(void *handle, uint8_t **buf, uint32_t buf_size); -typedef nh_ret_t (*nh_osa_buf_free_t)(void *handle, uint8_t *buf); - -/* OPS */ -typedef nh_ret_t (*nh_ops_spi_xfer_t)(void *handle, uint8_t *tx_data, uint16_t *rx_data, uint16_t len); -typedef nh_ret_t (*nh_ops_hs_poll_t)(void *handle); -typedef nh_ret_t (*nh_ops_drdy_read_t)(void *handle, bool *rdy); +#include "nh_shared_if.h" /* Event callbacks */ typedef void (*nh_cb_init_t)(void *handle); @@ -30,20 +9,6 @@ typedef void (*nh_cb_heartbeat_t)(void *handle); typedef void (*nh_cb_sta_disconn_from_ap_t)(void *handle); typedef void (*nh_cb_sta_disconn_from_soft_ap_t)(void *handle); -typedef struct { - nh_osa_buf_allocate_t buf_allocate; - nh_osa_buf_free_t buf_free; - nh_osa_semaphore_create_t sem_create; - nh_osa_semaphore_take_t sem_take; - nh_osa_semaphore_give_t sem_give; -} nh_ctrl_api_osa_t; - -typedef struct { - nh_ops_spi_xfer_t xfer; - nh_ops_hs_poll_t handshake_poll; - nh_ops_drdy_read_t data_ready_read; -} nh_ctrl_api_ops_t; - typedef struct { nh_cb_init_t init; nh_cb_heartbeat_t heartbeat; @@ -52,22 +17,14 @@ typedef struct { } nh_ctrl_api_cb_t; typedef struct { - nh_ctrl_api_osa_t osa; - nh_ctrl_api_ops_t ops; - nh_ctrl_api_cb_t cb; - void *user_data; + nh_shared_if_t *shared_if; + nh_osa_t *osa; + nh_ctrl_api_cb_t cb; + void *user_data; /* Private states */ - uint8_t *p_buf_tx; - uint8_t *p_buf_rx; - bool p_flag_request; /* !! Guarded by: p_semaphore_request */ - nh_osa_semaphore_t p_semaphore_event; - nh_osa_semaphore_t p_semaphore_request; - nh_osa_semaphore_t p_semaphore_response; } nh_ctrl_api_t; nh_ret_t nh_ctrl_api_init(nh_ctrl_api_t *api); -void nh_ctrl_api_task(nh_ctrl_api_t *api); -void nh_ctrl_api_inject_data_ready(nh_ctrl_api_t *api); #endif // NH_CTRL_API_H diff --git a/include/nh_shared_if.h b/include/nh_shared_if.h new file mode 100644 index 0000000..2e96639 --- /dev/null +++ b/include/nh_shared_if.h @@ -0,0 +1,41 @@ +#ifndef NH_SHARED_IF_H +#define NH_SHARED_IF_H + +#include "nh_common.h" +/* OPS */ +typedef nh_ret_t (*nh_shared_if_ops_spi_xfer_t)(void *handle, uint8_t *tx_data, uint8_t *rx_data, uint32_t len); +typedef nh_ret_t (*nh_shared_if_ops_reset_t)(void *handle); +typedef nh_ret_t (*nh_shared_if_ops_hs_poll_t)(void *handle); +typedef nh_ret_t (*nh_shared_if_ops_drdy_read_t)(void *handle, bool *rdy); + +typedef struct { + nh_shared_if_ops_spi_xfer_t xfer; + nh_shared_if_ops_reset_t reset; + nh_shared_if_ops_hs_poll_t handshake_poll; + nh_shared_if_ops_drdy_read_t data_ready_read; + + void *user_data; +} nh_shared_if_ops_t; + +typedef struct { + nh_shared_if_ops_t ops; + nh_osa_t *osa; + + /* Private states */ + nh_osa_semaphore_t p_semaphore_xfer_req; + nh_osa_semaphore_t p_semaphore_tx; + uint8_t *p_buf_frame_tx; /* SPI TX frame, guarded */ + uint8_t *p_buf_frame_rx; /* SPI RX frame, guarded */ + uint8_t *p_buf_ctrl_tx; /* Control API TX payload */ + uint8_t *p_buf_ctrl_rx; /* Control API RX payload */ +} nh_shared_if_t; + +nh_ret_t nh_shared_if_init(nh_shared_if_t *shared_if); +void nh_shared_if_task(nh_shared_if_t *shared_if); +void nh_shared_if_inject_data_ready(nh_shared_if_t *shared_if); + +/* Internal APIs */ +nh_ret_t nh_shared_if_ctrl_send(nh_shared_if_t *shared_if, uint8_t *tx_payload, uint32_t len, uint32_t timeout_ms); +nh_ret_t nh_shared_if_ctrl_recv(nh_shared_if_t *shared_if, uint8_t *rx_payload, uint32_t *len, uint32_t timeout_ms); + +#endif // NH_SHARED_IF_H diff --git a/src/nh_ctrl_api.c b/src/nh_ctrl_api.c index 1cfff42..6e358b3 100644 --- a/src/nh_ctrl_api.c +++ b/src/nh_ctrl_api.c @@ -1,5 +1,8 @@ #include "nh_ctrl_api.h" +#include "pb_encode.h" +#include "pb_decode.h" + #include "esp_hosted_config.pb.h" #define NH_XFER_BUF_SIZE 1600 @@ -7,81 +10,31 @@ #define NH_SEMAPHORE_EVENT_TIMEOUT 1000 #define NH_SEMAPHORE_REQ_TIMEOUT 5000 +static nh_ret_t nh_ctrl_api_general_request(nh_ctrl_api_t *api, CtrlMsg *msg); + nh_ret_t nh_ctrl_api_init(nh_ctrl_api_t *api) { nh_ret_t ret = NH_RET_SUCCESS; - /* Create semaphore for input events (Data Ready, Ctrl API call) */ - ret = api->osa.sem_create(api->user_data, &api->p_semaphore_event); - if (ret != NH_RET_SUCCESS) { - return ret; - } - - /* Create semaphore for control API requests, we can support maximum 1 control API request at any given time */ - ret = api->osa.sem_create(api->user_data, &api->p_semaphore_request); - if (ret != NH_RET_SUCCESS) { - return ret; - } - - /* Create semaphore for control API responses */ - ret = api->osa.sem_create(api->user_data, &api->p_semaphore_response); - if (ret != NH_RET_SUCCESS) { - return ret; - } - - /* TODO: Determine the initial value of each semaphores. */ - - /* Allocate TX and RX buffers, we should minimalize the malloc-free counts to reduce memory fragmentation */ - ret = api->osa.buf_allocate(api->user_data, &api->p_buf_tx, NH_XFER_BUF_SIZE); - if (ret != NH_RET_SUCCESS) { - return ret; - } - - ret = api->osa.buf_allocate(api->user_data, &api->p_buf_rx, NH_XFER_BUF_SIZE); - if (ret != NH_RET_SUCCESS) { - return ret; - } - - return NH_RET_SUCCESS; -} - -void nh_ctrl_api_task(nh_ctrl_api_t *api) { - nh_ret_t ret = NH_RET_SUCCESS; - - ret = api->osa.sem_take(api->user_data, api->p_semaphore_event, NH_SEMAPHORE_EVENT_TIMEOUT); - if (ret != NH_RET_SUCCESS) { - /* Failed to acquire semaphore due to error or timed out */ - return; - } - - /* Acquired semaphore, could be an event or a request, or both. */ + return ret; } nh_ret_t nh_ctrl_api_get_mac(nh_ctrl_api_t *api, uint8_t *mac) { nh_ret_t ret; - ret = api->osa.sem_take(api->user_data, api->p_semaphore_request, NH_SEMAPHORE_REQ_TIMEOUT); - if (ret != NH_RET_SUCCESS) { - /* Failed to take the request semaphore in time, maybe due to another request in under way */ - return ret; - } - /* TODO: Encode the request */ - CtrlMsg_Req_GetMacAddress req = CtrlMsg_Req_GetMacAddress_init_zero; - req.mode = Ctrl_WifiMode_STA; + CtrlMsg req_msg = CtrlMsg_init_zero; - api->p_flag_request = true; - ret = api->osa.sem_give(api->user_data, api->p_semaphore_event); - if (ret != NH_RET_SUCCESS) { - goto give_sem_and_exit; - } - - ret = api->osa.sem_take(api->user_data, api->p_semaphore_response, NH_SEMAPHORE_REQ_TIMEOUT); - if(ret != NH_RET_SUCCESS) { - goto give_sem_and_exit; - } - -give_sem_and_exit: - api->osa.sem_give(api->user_data, api->p_semaphore_request); + req_msg.msg_type = CtrlMsgType_Req; + req_msg.msg_id = CtrlMsgId_Req_GetMACAddress; + req_msg.payload.req_get_mac_address.mode = Ctrl_WifiMode_STA; + ret = nh_ctrl_api_general_request(api, &req_msg); return ret; +} + +static nh_ret_t nh_ctrl_api_general_request(nh_ctrl_api_t *api, CtrlMsg *msg) { + nh_ret_t ret; + + pb_ostream_t ostream = pb_ostream_from_buffer(api->p_buf_tx, NH_XFER_BUF_SIZE); + pb_encode(&ostream, CtrlMsg_fields, msg); } \ No newline at end of file diff --git a/src/nh_shared_if.c b/src/nh_shared_if.c new file mode 100644 index 0000000..1d36b69 --- /dev/null +++ b/src/nh_shared_if.c @@ -0,0 +1,75 @@ +#include "nh_shared_if.h" + +#define NH_XFER_REQ_TIMEOUT 5000 + +#define NH_XFER_MAX_SIZE 1600 + +nh_ret_t nh_shared_if_init(nh_shared_if_t *shared_if) { + nh_ret_t ret; + + ret = shared_if->osa->sem_create(shared_if->osa->user_data, &shared_if->p_semaphore_xfer_req); + if (ret != NH_RET_SUCCESS) { + return ret; + } + + ret = shared_if->osa->buf_allocate(shared_if->osa->user_data, &shared_if->p_buf_frame_tx, NH_XFER_MAX_SIZE); + if (ret != NH_RET_SUCCESS) { + goto err_free_sem_xfer; + } + + ret = shared_if->osa->buf_allocate(shared_if->osa->user_data, &shared_if->p_buf_frame_rx, NH_XFER_MAX_SIZE); + if (ret != NH_RET_SUCCESS) { + goto err_free_tx_buf; + } + + return ret; + +err_free_tx_buf: + shared_if->osa->buf_free(shared_if->osa->user_data, shared_if->p_buf_frame_tx); + +err_free_sem_xfer: + shared_if->osa->sem_destroy(shared_if->osa->user_data, shared_if->p_semaphore_xfer_req); + + return ret; +} + +void nh_shared_if_task(nh_shared_if_t *shared_if) { + nh_ret_t ret; + + ret = shared_if->osa->sem_take(shared_if->osa->user_data, shared_if->p_semaphore_xfer_req, NH_XFER_REQ_TIMEOUT); + if (ret != NH_RET_SUCCESS) { + return; + } + + /* Wait for device available */ + ret = shared_if->ops.handshake_poll(shared_if->ops.user_data); + if(ret != NH_RET_SUCCESS) { + return; + } + + /* Taken the xfer request semaphore. The possible reasons are: + * 1: Some data (could from any interfaces) needs to be sent + * 2: Data ready pin has been toggled, some data needs to be received + */ + + bool rx_available = false; + + /* Check if new data is available */ + ret = shared_if->ops.data_ready_read(shared_if->ops.user_data, &rx_available); + if (ret != NH_RET_SUCCESS) { + return; + } + + ret = shared_if->ops.xfer(shared_if->ops.user_data, shared_if->p_buf_frame_tx, shared_if->p_buf_frame_rx, + NH_XFER_MAX_SIZE); + if (ret != NH_RET_SUCCESS) { + return; + } + + /* If we do not have RX data... */ + if (!rx_available) { + return; + } + + /* TODO: Check interface type... */ +} \ No newline at end of file