diff --git a/CMakeLists.txt b/CMakeLists.txt index 9dd32b2..78f9813 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_event.c" "src/nh_shared_if.c" ) diff --git a/include/nh_common.h b/include/nh_common.h index 7afccaf..7e8e0fa 100644 --- a/include/nh_common.h +++ b/include/nh_common.h @@ -17,17 +17,31 @@ typedef nh_ret_t (*nh_osa_semaphore_take_t)(void *handle, nh_osa_semaphore_t sem 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 void *nh_osa_queue_t; +typedef nh_ret_t (*nh_osa_queue_create_t)(void *handle, nh_osa_queue_t *queue, uint32_t item_size, uint32_t max_length); +typedef nh_ret_t (*nh_osa_queue_enqueue_t)(void *handle, nh_osa_queue_t queue, void *item, uint32_t timeout_msec); +typedef nh_ret_t (*nh_osa_queue_dequeue_t)(void *handle, nh_osa_queue_t queue, void *item, uint32_t timeout_msec); +typedef nh_ret_t (*nh_osa_queue_destroy_t)(void *handle, nh_osa_queue_t queue); + 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_buf_allocate_t buf_allocate; + nh_osa_buf_free_t buf_free; + + /* Semaphore operations */ 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; + /* Queue operations */ + nh_osa_queue_create_t queue_create; + nh_osa_queue_enqueue_t queue_enqueue; + nh_osa_queue_dequeue_t queue_dequeue; + nh_osa_queue_destroy_t queue_destroy; + void *user_data; } nh_osa_t; diff --git a/include/nh_ctrl_api.h b/include/nh_ctrl_api.h index 500af65..5361e66 100644 --- a/include/nh_ctrl_api.h +++ b/include/nh_ctrl_api.h @@ -3,28 +3,15 @@ #include "nh_shared_if.h" -/* Event callbacks */ -typedef void (*nh_cb_init_t)(void *handle); -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_cb_init_t init; - nh_cb_heartbeat_t heartbeat; - nh_cb_sta_disconn_from_ap_t sta_disconn_from_ap; - nh_cb_sta_disconn_from_soft_ap_t sta_disconn_from_soft_ap; -} nh_ctrl_api_cb_t; - typedef struct { nh_shared_if_t *shared_if; nh_osa_t *osa; - nh_ctrl_api_cb_t cb; void *user_data; /* Private states */ } 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); #endif // NH_CTRL_API_H diff --git a/include/nh_event.h b/include/nh_event.h new file mode 100644 index 0000000..e921e03 --- /dev/null +++ b/include/nh_event.h @@ -0,0 +1,36 @@ +#ifndef NH_EVENT_H +#define NH_EVENT_H + +#include "nh_common.h" + +typedef enum { + NH_EVENT_TYPE_INIT, +} nh_event_type_t; + +typedef enum { + NH_EVENT_INIT_CAP_WLAN_SDIO = (1U << 0U), + NH_EVENT_INIT_CAP_BT_UART = (1U << 1U), + NH_EVENT_INIT_CAP_BT_SDIO = (1U << 2U), + NH_EVENT_INIT_CAP_BLE_ONLY = (1U << 3U), + NH_EVENT_INIT_CAP_BR_EDR_ONLY = (1U << 4U), + NH_EVENT_INIT_CAP_WLAN_SPI = (1U << 5U), + NH_EVENT_INIT_CAP_BT_SPI = (1U << 6U), + NH_EVENT_INIT_CAP_CHECKSUM = (1U << 7U), +} nh_event_init_capability_t; + +typedef enum { + NH_ESP_INIT_CHIP_UNRECOGNIZED = 0xFFU, + NH_ESP_INIT_CHIP_ESP32 = 0x00U, + NH_ESP_INIT_CHIP_ESP32S2 = 0x02U, + NH_ESP_INIT_CHIP_ESP32C3 = 0x05U, + NH_ESP_INIT_CHIP_ESP32S3 = 0x09U, + NH_ESP_INIT_CHIP_ESP32C2 = 0x0CU, +} nh_event_init_chip_id_t; + +typedef struct { + nh_event_init_capability_t capabilities; + nh_event_init_chip_id_t chip_id; + uint8_t spi_freq; +} nh_event_init_t; + +#endif // NH_EVENT_H diff --git a/include/nh_shared_if.h b/include/nh_shared_if.h index 2e96639..92654c6 100644 --- a/include/nh_shared_if.h +++ b/include/nh_shared_if.h @@ -2,32 +2,41 @@ #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); +/* Event callbacks */ +typedef void (*nh_shared_if_cb_event_t)(void *handle, uint8_t *data); + 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_drdy_read_t drdy_read; } nh_shared_if_ops_t; +typedef struct { + nh_shared_if_cb_event_t event; +} nh_shared_if_cb_t; + typedef struct { nh_shared_if_ops_t ops; - nh_osa_t *osa; + nh_shared_if_cb_t cb; + void *user_data; + + 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_osa_queue_t p_queue_tx; + nh_osa_queue_t p_queue_rx_data; + nh_osa_queue_t p_queue_rx_ctrl; + + uint8_t *p_buf_frame_tx; /* SPI TX frame */ + uint8_t *p_buf_frame_rx; /* SPI RX frame */ } nh_shared_if_t; nh_ret_t nh_shared_if_init(nh_shared_if_t *shared_if); @@ -36,6 +45,7 @@ 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); +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); +nh_ret_t nh_shared_if_ctrl_free(nh_shared_if_t *shared_if, uint8_t *rx_payload); #endif // NH_SHARED_IF_H diff --git a/src/nh_ctrl_api.c b/src/nh_ctrl_api.c index 6e358b3..fc38929 100644 --- a/src/nh_ctrl_api.c +++ b/src/nh_ctrl_api.c @@ -1,16 +1,15 @@ +/* Private */ #include "nh_ctrl_api.h" -#include "pb_encode.h" +/* Nano-PB */ #include "pb_decode.h" +#include "pb_encode.h" +/* PB config */ #include "esp_hosted_config.pb.h" -#define NH_XFER_BUF_SIZE 1600 - -#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); +#define NH_RECEIVE_TIMEOUT_MSEC 5000 +#define NH_COMMAND_TIMEOUT_MSEC 30000 nh_ret_t nh_ctrl_api_init(nh_ctrl_api_t *api) { nh_ret_t ret = NH_RET_SUCCESS; @@ -18,23 +17,17 @@ nh_ret_t nh_ctrl_api_init(nh_ctrl_api_t *api) { return ret; } -nh_ret_t nh_ctrl_api_get_mac(nh_ctrl_api_t *api, uint8_t *mac) { - nh_ret_t ret; +void nh_ctrl_api_task(nh_ctrl_api_t *api) { + nh_ret_t ret = NH_RET_SUCCESS; - /* TODO: Encode the request */ - CtrlMsg req_msg = CtrlMsg_init_zero; + uint8_t *rx_payload; + uint32_t rx_payload_size; - 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_shared_if_ctrl_recv(api->shared_if, &rx_payload, &rx_payload_size, NH_RECEIVE_TIMEOUT_MSEC); + if (ret != NH_RET_SUCCESS) { + return; + } - 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); + /* TODO: process SERIAL data */ + nh_shared_if_ctrl_free(api->shared_if, rx_payload); } \ No newline at end of file diff --git a/src/nh_event.c b/src/nh_event.c new file mode 100644 index 0000000..5770887 --- /dev/null +++ b/src/nh_event.c @@ -0,0 +1 @@ +#include "nh_event.h" \ No newline at end of file diff --git a/src/nh_shared_if.c b/src/nh_shared_if.c index 1d36b69..3884f6d 100644 --- a/src/nh_shared_if.c +++ b/src/nh_shared_if.c @@ -1,8 +1,53 @@ #include "nh_shared_if.h" -#define NH_XFER_REQ_TIMEOUT 5000 +#include "string.h" +#define NH_XFER_REQ_TIMEOUT 5000 #define NH_XFER_MAX_SIZE 1600 +#define NH_XFER_QUEUE_SIZE 1 + +#ifdef __GNUC__ +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define H_TO_LE16(x) (x) +#define LE16_TO_H(x) (x) +#else +#define H_TO_LE16(x) (((x & 0xFFU) << 8U) | (x >> 8U)) +#endif +#else +#warning "This code only supports GCC for now." +#endif + +typedef enum { + NH_SHARED_IF_TYPE_STA = 0U, + NH_SHARED_IF_TYPE_AP = 1U, + NH_SHARED_IF_TYPE_SERIAL = 2U, + NH_SHARED_IF_TYPE_PRIV = 4U, +} nh_xfer_type_t; + +typedef struct { + nh_xfer_type_t type; + uint8_t *buf; + uint32_t buf_len; +} nh_xfer_queue_item_t; + +typedef struct __attribute__((packed)) { + uint8_t if_type : 4; /* Offset: 0x00 */ + uint8_t if_num : 4; /* Offset: 0x00 */ + uint8_t flags; /* Offset: 0x01 */ + uint16_t len; /* Offset: 0x02 */ + uint16_t offset; /* Offset: 0x04 */ + uint16_t checksum; /* Offset: 0x06 */ + uint16_t seq_num; /* Offset: 0x08 */ + uint8_t reserved2; /* Offset: 0x0A */ + union { + uint8_t reserved3; + uint8_t hci_pkt_type; + uint8_t priv_pkt_type; + }; /* Offset: 0x0B */ +} nh_xfer_pkt_hdr_t; /* Size: 0x0C (12 bytes) */ + +static uint16_t nh_shared_if_checksum_calculate(nh_xfer_pkt_hdr_t *hdr); +static bool nh_shared_if_checksum_verify(nh_xfer_pkt_hdr_t *hdr); nh_ret_t nh_shared_if_init(nh_shared_if_t *shared_if) { nh_ret_t ret; @@ -12,11 +57,29 @@ nh_ret_t nh_shared_if_init(nh_shared_if_t *shared_if) { return ret; } - ret = shared_if->osa->buf_allocate(shared_if->osa->user_data, &shared_if->p_buf_frame_tx, NH_XFER_MAX_SIZE); + ret = shared_if->osa->queue_create(shared_if->osa->user_data, &shared_if->p_queue_tx, sizeof(nh_xfer_queue_item_t), + NH_XFER_QUEUE_SIZE); if (ret != NH_RET_SUCCESS) { goto err_free_sem_xfer; } + ret = shared_if->osa->queue_create(shared_if->osa->user_data, &shared_if->p_queue_rx_data, + sizeof(nh_xfer_queue_item_t), NH_XFER_QUEUE_SIZE); + if (ret != NH_RET_SUCCESS) { + goto err_free_queue_tx; + } + + ret = shared_if->osa->queue_create(shared_if->osa->user_data, &shared_if->p_queue_rx_ctrl, + sizeof(nh_xfer_queue_item_t), NH_XFER_QUEUE_SIZE); + if (ret != NH_RET_SUCCESS) { + goto err_free_queue_rx_data; + } + + 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_queue_rx_ctrl; + } + 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; @@ -27,12 +90,68 @@ nh_ret_t nh_shared_if_init(nh_shared_if_t *shared_if) { err_free_tx_buf: shared_if->osa->buf_free(shared_if->osa->user_data, shared_if->p_buf_frame_tx); +err_free_queue_rx_ctrl: + shared_if->osa->queue_destroy(shared_if->osa->user_data, shared_if->p_queue_rx_ctrl); + +err_free_queue_rx_data: + shared_if->osa->queue_destroy(shared_if->osa->user_data, shared_if->p_queue_rx_data); + +err_free_queue_tx: + shared_if->osa->queue_destroy(shared_if->osa->user_data, shared_if->p_queue_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_inject_data_ready(nh_shared_if_t *shared_if) { + shared_if->osa->sem_give(shared_if->osa->user_data, shared_if->p_semaphore_xfer_req); +} + +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 ret = NH_RET_SUCCESS; + + nh_xfer_queue_item_t item = { + .type = NH_SHARED_IF_TYPE_SERIAL, + .buf_len = len, + }; + + ret = shared_if->osa->buf_allocate(shared_if->osa->user_data, &item.buf, len); + if (ret != NH_RET_SUCCESS) { + return ret; + } + + ret = shared_if->osa->queue_enqueue(shared_if->osa->user_data, shared_if->p_queue_tx, &item, timeout_ms); + if (ret != NH_RET_SUCCESS) { + shared_if->osa->buf_free(shared_if->osa->user_data, item.buf); + + return ret; + } + + return ret; +} + +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) { + nh_ret_t ret = NH_RET_SUCCESS; + + nh_xfer_queue_item_t item; + + ret = shared_if->osa->queue_dequeue(shared_if->osa->user_data, shared_if->p_queue_rx_ctrl, &item, timeout_ms); + if (ret != NH_RET_SUCCESS) { + return ret; + } + + *rx_payload = item.buf; + *len = item.buf_len; + + return ret; +} + +nh_ret_t nh_shared_if_ctrl_free(nh_shared_if_t *shared_if, uint8_t *rx_payload) { + return shared_if->osa->buf_free(shared_if->osa->user_data, rx_payload); +} + void nh_shared_if_task(nh_shared_if_t *shared_if) { nh_ret_t ret; @@ -41,35 +160,113 @@ void nh_shared_if_task(nh_shared_if_t *shared_if) { 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 + * 2: Data ready pin is high when HS at rising, 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); + ret = shared_if->ops.drdy_read(shared_if->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_queue_item_t item; + + /* Clear TX header */ + memset(shared_if->p_buf_frame_tx, 0U, sizeof(nh_xfer_pkt_hdr_t)); + + /* Check if we have data to send */ + ret = shared_if->osa->queue_dequeue(shared_if->user_data, shared_if->p_queue_tx, &item, 0U); + if (ret == NH_RET_SUCCESS) { + /* Write header and payload since we have data to send */ + nh_xfer_pkt_hdr_t *hdr = (nh_xfer_pkt_hdr_t *)shared_if->p_buf_frame_tx; + + hdr->if_type = item.type; + hdr->offset = H_TO_LE16(sizeof(nh_xfer_pkt_hdr_t)); /* Payload follows header... */ + hdr->len = H_TO_LE16(item.buf_len); + hdr->checksum = H_TO_LE16(nh_shared_if_checksum_calculate(hdr)); + + /* Copy payload */ + memcpy(&shared_if->p_buf_frame_tx[sizeof(nh_xfer_pkt_hdr_t)], item.buf, item.buf_len); + + /* Queue consumer owns the buffer. */ + shared_if->osa->buf_free(shared_if->osa->user_data, item.buf); + } + + /* Exchange buffers */ + ret = shared_if->ops.xfer(shared_if->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; + /* If we have RX data... */ + if (rx_available) { + nh_xfer_pkt_hdr_t *hdr = (nh_xfer_pkt_hdr_t *)shared_if->p_buf_frame_rx; + if (!nh_shared_if_checksum_verify(hdr)) { + return; + } + + item.type = LE16_TO_H(hdr->if_type); + item.buf_len = LE16_TO_H(hdr->len); + + uint16_t payload_offset = LE16_TO_H(hdr->offset); + + if (item.type == NH_SHARED_IF_TYPE_SERIAL) { + if (item.buf_len) { + /* Allocate payload */ + ret = shared_if->osa->buf_allocate(shared_if->osa->user_data, &item.buf, item.buf_len); + if (ret != NH_RET_SUCCESS) { + return; + } + + memcpy(item.buf, &shared_if->p_buf_frame_rx[payload_offset], item.buf_len); + } + + /* Enqueue response */ + ret = shared_if->osa->queue_enqueue(shared_if->osa->user_data, shared_if->p_queue_rx_ctrl, &item, 0); + if (ret != NH_RET_SUCCESS) { + /* Queue is full, maybe the ctrl interface is not active. */ + shared_if->osa->buf_free(shared_if->osa->user_data, item.buf); + } + } else if (item.type == NH_SHARED_IF_TYPE_PRIV) { + /* Event is using PRIV interface. */ + if (item.buf_len) { + if (shared_if->cb.event) { + /* Note: This is a blocking callback !! */ + shared_if->cb.event(shared_if->user_data, &shared_if->p_buf_frame_rx[payload_offset]); + } + } + } + } +} + +static uint16_t nh_shared_if_checksum_calculate(nh_xfer_pkt_hdr_t *hdr) { + uint16_t checksum = 0U; + + uint16_t checksum_length = sizeof(nh_xfer_pkt_hdr_t) + LE16_TO_H(hdr->len); + + for (size_t i = 0; i < checksum_length; i++) { + checksum += ((uint8_t *)hdr)[i]; } - /* TODO: Check interface type... */ + return checksum; +} + +static bool nh_shared_if_checksum_verify(nh_xfer_pkt_hdr_t *hdr) { + bool ret = true; + + uint16_t checksum = LE16_TO_H(hdr->checksum); + hdr->checksum = 0U; + + if (checksum != nh_shared_if_checksum_calculate(hdr)) { + return false; + } + + hdr->checksum = H_TO_LE16(checksum); + + return ret; } \ No newline at end of file