#include "compiler.h" #include "common.h" #include "io.h" #include "console.h" #include "esp.h" #include "boardinfo_fpga.h" #include "config.h" struct esplink_head __esplink_head esplink_head; static volatile __esplink struct esplink_timesync tsync; static volatile __esplink struct esplink_ota ota; volatile __esplink char board_info_esp[BOARDINFO_SIZE]; IRQHANDLER(esp,0) { uint32_t irqstatus = ESP_CPU_IRQ; ESP_CPU_IRQ_CLR = irqstatus; if (irqstatus & (1 << EL_DIRQ_UNDERRUN)) { con_printf("esp: ESP link memory underrun!!\n"); ESP_SPI_IRQ = (1 << EL_UIRQ_READY); /* Block writes, reinitialize! */ return; } if (irqstatus & (1 << EL_DIRQ_TIME)) { if (tsync.set.update) { SYSCLOCK_TICK_HOLD = tsync.set.tick; SYSCLOCK_DATETIME = tsync.set.td; tsync.set.update = 0; do_write_rtc = true; } else { tsync.get.td = SYSCLOCK_DATETIME; tsync.get.tick = SYSCLOCK_TICK_HOLD; ESP_SPI_IRQ_SET = 1 << EL_UIRQ_TIME; } } if (irqstatus & (1 << EL_DIRQ_BOARDINFO)) do_update_boardinfo = true; if (irqstatus & (1 << EL_DIRQ_HELLO)) { con_printf("esp: Got hello, sending ready...\n"); /* Hello, are you there? Yes, I'm here, and you can write data now */ ESP_SPI_IRQ_SET = (1 << EL_UIRQ_READY)|(1 << EL_UIRQ_WREN); } if (irqstatus & (1 << EL_DIRQ_CONFIG)) do_update_config = true; /* * Check to see if we got hello after an OTA process was completed * or aborted; ESP will send EL_DIRQ_DONE to wake us up. */ if (!(ESP_SPI_IRQ & (1 << EL_UIRQ_OTA))) { ota.data = NULL; ota.len = 0; } } void esp_ota(const void *data, size_t len) { mask_irq(ESP_IRQ); ota.data = data; ota.len = len; ESP_SPI_IRQ_SET = 1 << EL_UIRQ_OTA; unmask_irq(ESP_IRQ); while (ota.data) waitfor(ESP_IRQ); } #define RINGBUF_BUF_SIZE 4096 /* Must be a power of 2 */ static __esplink uint8_t rb_buf[EL_RB_COUNT][2][RINGBUF_BUF_SIZE]; static __esplink struct esplink_ringbuf_desc rb_desc[EL_RB_COUNT]; static volatile __esplink struct esplink_ptrs_dstr rb_dstr[EL_RB_COUNT]; static volatile __esplink struct esplink_ptrs_ustr rb_ustr[EL_RB_COUNT]; static void esplink_rb_init(void) { unsigned int i; for (i = 0; i < EL_RB_COUNT; i++) { rb_desc[i].dstr.start = rb_buf[i][0]; rb_desc[i].dstr.size = RINGBUF_BUF_SIZE; rb_desc[i].ustr.start = rb_buf[i][1]; rb_desc[i].ustr.size = RINGBUF_BUF_SIZE; } esplink_head.rb.desc = rb_desc; esplink_head.rb.dstr = (void *)rb_dstr; esplink_head.rb.ustr = (void *)rb_ustr; esplink_head.rb.count = EL_RB_COUNT; } /* * Read and write from ring buffers. These are atomic only; on failure * returns the amount of data/space available, but does NOT advance * any pointers nor copy any data. */ size_t esp_rb_read(enum esplink_ringbuf_user ring, void *data, size_t len) { const size_t size = rb_desc[ring].dstr.size; const size_t sizemask = size - 1; const uint8_t * const base = rb_desc[ring].dstr.start; size_t head = rb_ustr[ring].head; size_t tail = rb_dstr[ring].tail; size_t avail = (head - tail) & sizemask; uint8_t *p = data; size_t xlen = len; if (!len) return 0; if (avail < xlen) return avail; if (tail + xlen > size) { size_t left = size - tail; memcpy(p, base + tail, left); p += left; xlen -= left; tail = 0; } memcpy(p, base + tail, xlen); tail = (tail + xlen) & sizemask; rb_dstr[ring].tail = tail; ESP_SPI_IRQ_SET = 1 << EL_UIRQ_RINGBUF; return len; } size_t esp_rb_write(enum esplink_ringbuf_user ring, const void *data, size_t len) { const size_t size = RINGBUF_BUF_SIZE; const size_t sizemask = size - 1; uint8_t * const base = rb_desc[ring].ustr.start; size_t tail = rb_ustr[ring].tail; size_t head = rb_dstr[ring].head; size_t avail = (tail - head - 1) & sizemask; const uint8_t *p = data; size_t xlen = len; if (!len) return 0; if (avail < xlen) return avail; if (head + xlen > size) { size_t left = size - head; memcpy(base + head, p, left); p += left; xlen -= left; head = 0; } memcpy(base + head, p, xlen); head = (head + xlen) & sizemask; rb_dstr[ring].head = head; ESP_SPI_IRQ_SET = 1 << EL_UIRQ_RINGBUF; return len; } /* * Faster/simpler versions for reading/writing a single byte at a time; * esp_rb_getc() returns -1 if the ring is empty. * * esp_rb_putc() returns -1 on failure, otherwise the number of * bytes still free in the ring (a nonnegative number.) */ __hot int esp_rb_getc(enum esplink_ringbuf_user ring) { const size_t size = RINGBUF_BUF_SIZE; const size_t sizemask = size - 1; const uint8_t * const base = rb_desc[ring].dstr.start; size_t head = rb_ustr[ring].head; size_t tail = rb_dstr[ring].tail; int data; if (tail == head) return -1; /* Buffer empty */ data = base[tail]; tail = (tail + 1) & sizemask; rb_dstr[ring].tail = tail; ESP_SPI_IRQ_SET = 1 << EL_UIRQ_RINGBUF; return data; } __hot int esp_rb_putc(enum esplink_ringbuf_user ring, uint8_t data) { const size_t size = RINGBUF_BUF_SIZE; const size_t sizemask = size - 1; uint8_t * const base = rb_desc[ring].ustr.start; size_t tail = rb_ustr[ring].tail; size_t head = rb_dstr[ring].head; int avail = (tail - head - 1) & sizemask; if (avail--) { base[head] = data; head = (head + 1) & sizemask; rb_dstr[ring].head = head; ESP_SPI_IRQ_SET = 1 << EL_UIRQ_RINGBUF; } return avail; } __hot enum ringbuf_status esp_rb_status(enum esplink_ringbuf_user ring) { const size_t size = RINGBUF_BUF_SIZE; const size_t sizemask = size - 1; const size_t utail = rb_ustr[ring].tail; const size_t dhead = rb_ustr[ring].head; const size_t uhead = rb_dstr[ring].head; const size_t dtail = rb_dstr[ring].tail; enum ringbuf_status status = RB_CONNECTED; if (dhead != dtail) status |= RB_RXDATA; if ((utail - uhead - 1) & sizemask) status |= RB_TXFREE; return status; } void esp_init(void) { static char __dram_data esp_signature[] = "Hej tomtebuggar slÄ i glasen!"; ESP_CPU_IRQ = 0; ESP_SPI_IRQ = 0; memset(&esplink_head, 0, sizeof esplink_head); esplink_head.hlen = sizeof esplink_head; esplink_head.board.cfg = SYS_BOARDCFG; memcpy(esplink_head.signature, esp_signature, sizeof esp_signature); esplink_head.board_info = &board_info_raw; esplink_head.tsync = &tsync; esplink_head.ota = &ota; esplink_head.cfg.buf = config_buf; esplink_head.cfg.buflen = sizeof config_buf; esplink_rb_init(); esplink_head.magic = ESPLINK_HEAD_MAGIC; ESP_SPI_IRQ = (1 << EL_UIRQ_READY); unmask_irq(ESP_IRQ); }