#include #include #include "encoding.h" #include "platform.h" #include "board.h" #include "eos.h" #include "log.h" #include "event.h" #include "soc/interrupt.h" #include "soc/timer.h" #include "soc/pwr.h" #include "soc/spi.h" #include "soc/spi_priv.h" #include "spi.h" #include "aon.h" #include "net.h" #define NET_DETECT_TIMEOUT 1000 #define NET_SIZE_HDR 3 #define NET_SIZE_BUFQ 2 #define NET_STATE_FLAG_RUN 0x0001 #define NET_STATE_FLAG_RTS 0x0002 #define NET_STATE_FLAG_CTS 0x0004 #define NET_STATE_FLAG_INIT 0x0010 #define NET_STATE_FLAG_XCHG 0x0020 #define NET_STATE_FLAG_ONEW 0x0040 #define NET_STATE_FLAG_SYNC 0x0080 #define NET_STATE_FLAG_SLEEP 0x0100 #define NET_STATE_FLAG_SLEEP_REQ 0x0200 #define NET_STATE_FLAG_ABSENT 0x0400 #define NET_STATE_FLAG_ERR_SIZE 0x0800 #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y)) #define MAX(X, Y) (((X) > (Y)) ? (X) : (Y)) static EOSBufQ net_buf_q; static unsigned char *net_bufq_array[NET_SIZE_BUFQ]; static unsigned char net_bufq_buffer[NET_SIZE_BUFQ][EOS_NET_MTU] __attribute__((section (".itim2"))); static EOSMsgQ net_send_q; static EOSMsgItem net_sndq_array[NET_SIZE_BUFQ]; static volatile uint16_t net_state_flags = 0; static volatile unsigned char net_state_type = 0; static uint32_t net_state_len_tx = 0; static volatile uint32_t net_state_len_rx = 0; static unsigned char *net_state_buf = NULL; static uint16_t net_state_buf_sz = 0; static volatile uint8_t net_state_next_cnt = 0; static unsigned char * volatile net_state_next_buf = NULL; static eos_evt_handler_t net_handler[EOS_NET_MAX_MTYPE]; static uint16_t net_wrapper_acq; static uint16_t net_flags_acq[EOS_EVT_MAX]; static void net_xchg_reset(void) { volatile uint32_t x = 0; net_state_flags &= ~NET_STATE_FLAG_CTS; SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD; SPI1_REG(SPI_REG_TXFIFO) = 0; while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY); SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO; } static void net_xchg_sleep_req(void) { volatile uint32_t x = 0; int i; net_state_flags &= ~NET_STATE_FLAG_CTS; SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD; SPI1_REG(SPI_REG_TXFIFO) = EOS_NET_MTYPE_SLEEP | EOS_NET_MTYPE_FLAG_ONEW; while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY); /* minimum 8 bytes for esp32 */ for (i=0; i<7; i++) { while (SPI1_REG(SPI_REG_TXFIFO) & SPI_TXFIFO_FULL); SPI1_REG(SPI_REG_TXFIFO) = 0; while ((x = SPI1_REG(SPI_REG_RXFIFO)) & SPI_RXFIFO_EMPTY); } SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO; } static void net_xchg_start(unsigned char type, EOSMessage *msg, uint16_t len) { net_state_flags &= ~(NET_STATE_FLAG_CTS | NET_STATE_FLAG_ERR_SIZE); net_state_flags |= (NET_STATE_FLAG_INIT | NET_STATE_FLAG_XCHG); if (net_state_next_cnt && (net_state_next_buf == NULL)) type |= EOS_NET_MTYPE_FLAG_ONEW; if (type & EOS_NET_MTYPE_FLAG_ONEW) net_state_flags |= NET_STATE_FLAG_ONEW; net_state_type = type; net_state_len_tx = len; net_state_len_rx = 0; net_state_buf = msg->buffer; net_state_buf_sz = msg->size; SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_HOLD; SPI1_REG(SPI_REG_TXFIFO) = type; SPI1_REG(SPI_REG_TXFIFO) = (len >> 8) & 0xFF; SPI1_REG(SPI_REG_TXFIFO) = (len & 0xFF); SPI1_REG(SPI_REG_RXCTRL) = SPI_RXWM(2); SPI1_REG(SPI_REG_IE) = SPI_IP_RXWM; } static int net_xchg_next(EOSMessage *_msg) { unsigned char type; EOSMessage msg; uint16_t len; int do_release = _msg ? 1 : 0; eos_msgq_pop(&net_send_q, &type, &msg, &len); if (type) { net_xchg_start(type, &msg, len); } else if (net_state_flags & NET_STATE_FLAG_RTS) { if (_msg) { msg = *_msg; do_release = 0; } else { msg.buffer = eos_bufq_pop(&net_buf_q); msg.size = EOS_NET_MTU; } if (msg.buffer) net_xchg_start(0, &msg, 0); } return do_release; } static void net_handle_xchg(void) { if (net_state_flags & NET_STATE_FLAG_INIT) { volatile uint32_t r1, r2, r3; uint32_t len; net_state_flags &= ~NET_STATE_FLAG_INIT; r1 = SPI1_REG(SPI_REG_RXFIFO); r2 = SPI1_REG(SPI_REG_RXFIFO); r3 = SPI1_REG(SPI_REG_RXFIFO); if (net_state_flags & NET_STATE_FLAG_ONEW) { r1 = 0; r2 = 0; r3 = 0; } net_state_type = r1; net_state_len_rx = (r2 & 0xFF) << 8; net_state_len_rx |= (r3 & 0xFF); len = MAX(net_state_len_tx, net_state_len_rx); /* esp32 dma workaraund */ if (len < 8 - NET_SIZE_HDR) { len = 8 - NET_SIZE_HDR; } else if ((len + NET_SIZE_HDR) % 4 != 0) { len = ((len + NET_SIZE_HDR)/4 + 1) * 4 - NET_SIZE_HDR; } if (len > net_state_buf_sz) { net_state_type = 0; net_state_len_tx = 0; net_state_len_rx = 0; net_state_flags |= NET_STATE_FLAG_ERR_SIZE; net_state_flags &= ~NET_STATE_FLAG_XCHG; SPI1_REG(SPI_REG_CSMODE) = SPI_CSMODE_AUTO; SPI1_REG(SPI_REG_IE) = 0x0; return; } _eos_spi_xchg_init(net_state_buf, net_state_buf_sz, len, 0); SPI1_REG(SPI_REG_TXCTRL) = SPI_TXWM(SPI_SIZE_WM); SPI1_REG(SPI_REG_IE) = SPI_IP_TXWM; return; } eos_spi_handle_xchg(); if (SPI1_REG(SPI_REG_CSMODE) == SPI_CSMODE_AUTO) { /* exchange done */ if (!(net_state_flags & NET_STATE_FLAG_SYNC)) { if (net_state_type) { if (net_state_type == EOS_NET_MTYPE_SLEEP) { net_state_flags |= NET_STATE_FLAG_SLEEP; eos_bufq_push(&net_buf_q, net_state_buf); } else { EOSMessage msg; int rv; msg.buffer = net_state_buf; msg.size = net_state_buf_sz; rv = eos_evtq_push_isr(EOS_EVT_NET | (net_state_type & EOS_NET_MTYPE_MASK), &msg, net_state_len_rx); if (rv) { EOS_LOG(EOS_LOG_ERR, "NET XCHG EVTQ PUSH ERR:%d\n", rv); eos_bufq_push(&net_buf_q, net_state_buf); } } } else if (((net_state_flags & NET_STATE_FLAG_ONEW) || net_state_next_cnt) && (net_state_next_buf == NULL)) { net_state_next_buf = net_state_buf; } else { eos_bufq_push(&net_buf_q, net_state_buf); } } net_state_flags &= ~(NET_STATE_FLAG_XCHG | NET_STATE_FLAG_ONEW); } } static void net_handle_cts(void) { GPIO_REG(GPIO_FALL_IP) = (1 << NET_PIN_CTS); net_state_flags |= NET_STATE_FLAG_CTS; net_state_flags &= ~NET_STATE_FLAG_SLEEP; if (net_state_flags & NET_STATE_FLAG_RUN) { net_xchg_next(NULL); } } static void net_handle_rts(void) { uint32_t rts_offset = (1 << NET_PIN_RTS); if (GPIO_REG(GPIO_FALL_IP) & rts_offset) { GPIO_REG(GPIO_FALL_IP) = rts_offset; net_state_flags |= NET_STATE_FLAG_RTS; if ((net_state_flags & NET_STATE_FLAG_RUN) && (net_state_flags & NET_STATE_FLAG_CTS)) { net_xchg_reset(); } } if (GPIO_REG(GPIO_RISE_IP) & rts_offset) { GPIO_REG(GPIO_RISE_IP) = rts_offset; net_state_flags &= ~NET_STATE_FLAG_RTS; } } static void net_handle_evt(unsigned char type, EOSMessage *msg, uint16_t len) { unsigned char idx; idx = type & EOS_NET_MTYPE_FLAG_BRIDGE ? EOS_NET_MTYPE_BRIDGE : (type & ~EOS_EVT_MASK); type &= ~EOS_EVT_MASK; if (msg && (idx < EOS_NET_MAX_MTYPE)) { net_handler[idx](type, msg, len); } else { eos_net_bad_handler(type, msg, len); } if (msg && msg->buffer) { eos_net_free(msg, 0); } else { eos_net_release(); } } static int net_acquire(unsigned char reserved) { int ret = 0; if (reserved) { while (!ret) { clear_csr(mstatus, MSTATUS_MIE); if (net_state_next_buf) { ret = 1; net_state_next_cnt--; } else { asm volatile ("wfi"); } set_csr(mstatus, MSTATUS_MIE); } } else { clear_csr(mstatus, MSTATUS_MIE); if (net_state_next_buf == NULL) { net_state_next_buf = eos_bufq_pop(&net_buf_q); } ret = (net_state_next_buf != NULL); if (!ret) net_state_next_cnt++; set_csr(mstatus, MSTATUS_MIE); } return ret; } static void evt_handler_wrapper(unsigned char type, EOSMessage *msg, uint16_t len, uint8_t idx) { uint16_t flag = (uint16_t)1 << idx; int rv; rv = net_acquire(net_wrapper_acq & flag); if (rv) { eos_evtq_get_handler(type)(type, msg, len); eos_net_release(); net_wrapper_acq &= ~flag; } else { rv = eos_evtq_push_widx(type, msg, len, &idx); if (rv) { EOS_LOG(EOS_LOG_ERR, "NET WRAPPER EVTQ PUSH ERR:%d\n", rv); return; } flag = (uint16_t)1 << idx; net_wrapper_acq |= flag; } } static void evt_handler(unsigned char type, EOSMessage *msg, uint16_t len, uint8_t _idx) { unsigned char idx = (type & EOS_EVT_MASK) >> 4; if (idx && (idx <= EOS_EVT_MAX)) { uint16_t flag = (uint16_t)1 << (type & ~EOS_EVT_MASK); idx--; if (flag & net_flags_acq[idx]) { evt_handler_wrapper(type, msg, len, _idx); } else { eos_evtq_get_handler(type)(type, msg, len); } } else { eos_evtq_bad_handler(type, msg, len); } } static void net_flushq(void) { while (eos_msgq_len(&net_send_q)) { asm volatile ("wfi"); set_csr(mstatus, MSTATUS_MIE); clear_csr(mstatus, MSTATUS_MIE); } } static void net_wait4xchg(void) { while (net_state_flags & NET_STATE_FLAG_XCHG) { asm volatile ("wfi"); set_csr(mstatus, MSTATUS_MIE); clear_csr(mstatus, MSTATUS_MIE); } } static void net_wait4cts(void) { while (!(net_state_flags & NET_STATE_FLAG_CTS)) { asm volatile ("wfi"); set_csr(mstatus, MSTATUS_MIE); clear_csr(mstatus, MSTATUS_MIE); } } static int net_wake(void) { uint32_t start, timeout; start = eos_get_tick(); timeout = NET_DETECT_TIMEOUT; while (net_state_flags & NET_STATE_FLAG_SLEEP) { if (timeout && (eos_tdelta_ms(start) > timeout)) return EOS_ERR_NOTFOUND; net_xchg_reset(); eos_sleep(10); set_csr(mstatus, MSTATUS_MIE); clear_csr(mstatus, MSTATUS_MIE); } return EOS_OK; } static int net_select(int *dsel) { int rv; *dsel = 0; if (net_state_flags & NET_STATE_FLAG_ABSENT) return EOS_ERR_NOTFOUND; if (net_state_flags & NET_STATE_FLAG_SLEEP_REQ) return EOS_ERR_BUSY; rv = EOS_OK; if (!(net_state_flags & NET_STATE_FLAG_RUN)) { set_csr(mstatus, MSTATUS_MIE); rv = eos_spi_select(EOS_SPI_DEV_NET); clear_csr(mstatus, MSTATUS_MIE); if (rv) return rv; *dsel = 1; } /* wake up remote if sleeping */ if (net_state_flags & NET_STATE_FLAG_SLEEP) rv = net_wake(); return rv; } static void net_deselect(void) { eos_spi_deselect(); } static void net_pause(void) { net_state_flags &= ~NET_STATE_FLAG_RUN; net_wait4xchg(); } static void net_resume(void) { if (net_state_flags & NET_STATE_FLAG_ABSENT) return; net_state_flags |= NET_STATE_FLAG_RUN; if (net_state_flags & NET_STATE_FLAG_CTS) { net_xchg_next(NULL); } } static void net_start(void) { eos_spi_dev_configure(EOS_SPI_DEV_NET); eos_intr_set_handler(INT_SPI1_BASE, net_handle_xchg); } static void net_stop(void) { eos_intr_set_handler(INT_SPI1_BASE, NULL); } int eos_net_init(void) { int i; eos_msgq_init(&net_send_q, net_sndq_array, NET_SIZE_BUFQ); eos_bufq_init(&net_buf_q, net_bufq_array, NET_SIZE_BUFQ); for (i=0; i timeout)) { rv = EOS_ERR_NOTFOUND; break; } } } else { if (eos_aon_load4net()) { /* device previously declared as absent */ rv = EOS_ERR_NOTFOUND; } else { net_state_flags |= NET_STATE_FLAG_SLEEP; } } if (!rv) { /* set initial state */ if (!(GPIO_REG(GPIO_INPUT_VAL) & (1 << NET_PIN_CTS))) net_state_flags |= NET_STATE_FLAG_CTS; if (!(GPIO_REG(GPIO_INPUT_VAL) & (1 << NET_PIN_RTS))) net_state_flags |= NET_STATE_FLAG_RTS; GPIO_REG(GPIO_PULLUP_EN) &= ~(1 << NET_PIN_CTS); /* wake up remote */ if (!rst) rv = net_wake(); if (!rv) net_resume(); } else { net_state_flags |= NET_STATE_FLAG_ABSENT; EOS_LOG(EOS_LOG_ERR, "NET DEVICE ABSENT\n"); } set_csr(mstatus, MSTATUS_MIE); /* save absent state */ if (rst) eos_aon_save4net(!!rv); if (rv) net_stop(); return rv; } void eos_net_start(void) { if (net_state_flags & NET_STATE_FLAG_ABSENT) return; net_start(); clear_csr(mstatus, MSTATUS_MIE); net_resume(); set_csr(mstatus, MSTATUS_MIE); } void eos_net_stop(void) { if (net_state_flags & NET_STATE_FLAG_ABSENT) return; clear_csr(mstatus, MSTATUS_MIE); net_pause(); set_csr(mstatus, MSTATUS_MIE); net_stop(); } int eos_net_sleep(void) { int dsel = 0; int rv; clear_csr(mstatus, MSTATUS_MIE); rv = net_select(&dsel); if (rv) { set_csr(mstatus, MSTATUS_MIE); return rv; } net_state_flags |= NET_STATE_FLAG_SLEEP_REQ; net_flushq(); net_pause(); net_wait4cts(); net_xchg_sleep_req(); net_resume(); set_csr(mstatus, MSTATUS_MIE); if (dsel) net_deselect(); return EOS_OK; } int eos_net_sleep_rdy(void) { int rv; clear_csr(mstatus, MSTATUS_MIE); rv = !!(net_state_flags & NET_STATE_FLAG_SLEEP); set_csr(mstatus, MSTATUS_MIE); return rv; } void eos_net_wake(void) { clear_csr(mstatus, MSTATUS_MIE); net_state_flags &= ~NET_STATE_FLAG_SLEEP_REQ; set_csr(mstatus, MSTATUS_MIE); } void eos_net_bad_handler(unsigned char type, EOSMessage *msg, uint16_t len) { eos_evtq_bad_handler(type, msg, len); } void eos_net_set_handler(unsigned char mtype, eos_evt_handler_t handler) { if (handler == NULL) handler = eos_net_bad_handler; if (mtype < EOS_NET_MAX_MTYPE) net_handler[mtype] = handler; } void eos_net_acquire_for_evt(unsigned char type, int acq) { unsigned char idx = (type & EOS_EVT_MASK) >> 4; uint16_t flag = type & ~EOS_EVT_MASK ? (uint16_t)1 << (type & ~EOS_EVT_MASK) : 0xFFFF; if (idx && (idx <= EOS_EVT_MAX)) { idx--; net_flags_acq[idx] &= ~flag; if (acq) net_flags_acq[idx] |= flag; } } void eos_net_acquire(void) { int acq; acq = net_acquire(0); if (!acq) net_acquire(1); } void eos_net_release(void) { clear_csr(mstatus, MSTATUS_MIE); if (!net_state_next_cnt && net_state_next_buf) { eos_bufq_push(&net_buf_q, net_state_next_buf); net_state_next_buf = NULL; } set_csr(mstatus, MSTATUS_MIE); } void eos_net_alloc(EOSMessage *msg) { msg->buffer = NULL; msg->size = 0; while (msg->buffer == NULL) { clear_csr(mstatus, MSTATUS_MIE); if (net_state_next_buf) { msg->buffer = net_state_next_buf; msg->size = EOS_NET_MTU; net_state_next_buf = NULL; } else { asm volatile ("wfi"); } set_csr(mstatus, MSTATUS_MIE); } } void eos_net_free(EOSMessage *msg, int more) { clear_csr(mstatus, MSTATUS_MIE); if ((more || net_state_next_cnt) && (net_state_next_buf == NULL)) { net_state_next_buf = msg->buffer; } else { int do_release = 1; if ((net_state_flags & NET_STATE_FLAG_RUN) && (net_state_flags & NET_STATE_FLAG_CTS)) { do_release = net_xchg_next(msg); } if (do_release) { eos_bufq_push(&net_buf_q, msg->buffer); } } msg->buffer = NULL; msg->size = 0; set_csr(mstatus, MSTATUS_MIE); } static int net_xchg(unsigned char *type, EOSMessage *msg, uint16_t *len, unsigned char flags) { int rv = EOS_OK; int sync = 0, dsel = 0; unsigned char _type = *type & EOS_NET_MTYPE_MASK; uint16_t _len = *len; if ((flags & EOS_NET_FLAG_REPL) && (_type & EOS_NET_MTYPE_FLAG_BRIDGE)) return EOS_ERR; if (flags & EOS_NET_FLAG_REPL) flags |= EOS_NET_FLAG_SYNC; if (flags & EOS_NET_FLAG_MORE) _type |= EOS_NET_MTYPE_FLAG_ONEW; if (flags & EOS_NET_FLAG_REPL) _type |= EOS_NET_MTYPE_FLAG_REPL; if (flags & EOS_NET_FLAG_SYNC) sync = 1; clear_csr(mstatus, MSTATUS_MIE); rv = net_select(&dsel); if (rv) goto net_xchg_fin; if (dsel) sync = 1; if (sync) { _type |= EOS_NET_MTYPE_FLAG_ONEW; net_pause(); net_wait4cts(); net_state_flags |= NET_STATE_FLAG_SYNC; net_xchg_start(_type, msg, _len); if (flags & EOS_NET_FLAG_REPL) { net_wait4cts(); net_xchg_start(0, msg, 0); } net_wait4xchg(); net_state_flags &= ~NET_STATE_FLAG_SYNC; if (flags & EOS_NET_FLAG_REPL) { *type = 0; *len = 0; rv = (net_state_flags & NET_STATE_FLAG_ERR_SIZE) ? EOS_ERR_SIZE : EOS_OK; if (!rv) { *type = net_state_type & EOS_NET_MTYPE_MASK; *len = net_state_len_rx; } } net_resume(); } else { if ((net_state_flags & NET_STATE_FLAG_RUN) && (net_state_flags & NET_STATE_FLAG_CTS)) { net_xchg_start(_type, msg, _len); } else { rv = eos_msgq_push(&net_send_q, _type, msg, _len); } } net_xchg_fin: set_csr(mstatus, MSTATUS_MIE); if (!(flags & EOS_NET_FLAG_SYNC)) { if (sync || rv) { eos_net_free(msg, !!(flags & EOS_NET_FLAG_MORE)); } else { msg->buffer = NULL; msg->size = 0; } } if (dsel) net_deselect(); return rv; } int eos_net_send(unsigned char type, EOSMessage *msg, uint16_t len, int more) { return net_xchg(&type, msg, &len, more ? EOS_NET_FLAG_MORE : 0); } int eos_net_send_sync(unsigned char type, EOSMessage *msg, uint16_t len) { return net_xchg(&type, msg, &len, EOS_NET_FLAG_SYNC); } int eos_net_xchg(unsigned char *type, EOSMessage *msg, uint16_t *len) { return net_xchg(type, msg, len, EOS_NET_FLAG_REPL); } int _eos_net_send(unsigned char type, EOSMessage *msg, uint16_t len, unsigned char flags) { return net_xchg(&type, msg, &len, flags); }