290 lines
8.4 KiB
C
290 lines
8.4 KiB
C
// vim: set et:
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "tusb.h"
|
|
|
|
#include "protocfg.h"
|
|
|
|
#ifdef DBOARD_HAS_SERPROG
|
|
|
|
#include "protos.h"
|
|
#include "thread.h"
|
|
|
|
#include "serprog.h"
|
|
|
|
// TODO: refactor some of this stuff into another header & split off serprog
|
|
// protocol handling from the SPI stuff. one thing we should think about
|
|
// when performing this refactor is, would other boards support
|
|
// parallell, LPC, or FWH, or only SPI? if only SPI, the entire proto
|
|
// handler can just be made reusable verbatim.
|
|
|
|
// kinda refactored this already but it still has a good note for non-SPI stuff,
|
|
// so leaving it here for now
|
|
|
|
static const uint8_t serprog_cmdmap[32] = {
|
|
0x3f, // cmd 00..05 not 0x06 (Q_CHIPSIZE) and 0x07 (Q_OPBUF), as this is a SPI-only device
|
|
0x01, // only cmd 08
|
|
0x1f, // cmd 10..15 supported
|
|
0, // rest is 0
|
|
};
|
|
static const char serprog_pgmname[16] = {
|
|
'D','a','p','p','e','r','M','i','m','e','-','J','T','A','G',0 // TODO
|
|
};
|
|
|
|
static uint8_t rx_buf[CFG_TUD_CDC_RX_BUFSIZE];
|
|
static uint8_t tx_buf[CFG_TUD_CDC_TX_BUFSIZE];
|
|
|
|
static uint32_t rxavail, rxpos;
|
|
|
|
void cdc_serprog_init(void) {
|
|
rxavail = 0;
|
|
rxpos = 0;
|
|
|
|
sp_spi_init();
|
|
}
|
|
|
|
static uint8_t read_byte(void) {
|
|
while (rxavail <= 0) {
|
|
if (!tud_cdc_n_connected(CDC_N_SERPROG) || !tud_cdc_n_available(CDC_N_SERPROG)) {
|
|
thread_yield();
|
|
continue;
|
|
}
|
|
|
|
rxpos = 0;
|
|
rxavail = tud_cdc_n_read(CDC_N_SERPROG, rx_buf, sizeof rx_buf);
|
|
|
|
if (rxavail == 0) thread_yield();
|
|
}
|
|
|
|
uint8_t rv = rx_buf[rxpos];
|
|
++rxpos;
|
|
--rxavail;
|
|
//printf("r %02x\n",rv);
|
|
return rv;
|
|
}
|
|
|
|
static void handle_cmd(void) {
|
|
uint32_t nresp = 0;
|
|
|
|
uint8_t cmd = read_byte();
|
|
switch (cmd) {
|
|
case S_CMD_NOP:
|
|
//printf("nop\n");
|
|
tx_buf[0] = S_ACK;
|
|
nresp = 1;
|
|
break;
|
|
case S_CMD_SYNCNOP:
|
|
//printf("snop\n");
|
|
tx_buf[0] = S_NAK;
|
|
tx_buf[1] = S_ACK;
|
|
nresp = 2;
|
|
break;
|
|
case S_CMD_Q_IFACE:
|
|
//printf("q_if\n");
|
|
tx_buf[0] = S_ACK;
|
|
tx_buf[1] = SERPROG_IFACE_VERSION & 0xff;
|
|
tx_buf[2] = (SERPROG_IFACE_VERSION >> 8) & 0xff;
|
|
nresp = 3;
|
|
break;
|
|
case S_CMD_Q_CMDMAP:
|
|
//printf("q_cmap\n");
|
|
tx_buf[0] = S_ACK;
|
|
memcpy(&tx_buf[1], serprog_cmdmap, sizeof serprog_cmdmap);
|
|
nresp = sizeof(serprog_cmdmap) + 1;
|
|
break;
|
|
case S_CMD_Q_PGMNAME:
|
|
//printf("q_pgm\n");
|
|
tx_buf[0] = S_ACK;
|
|
memcpy(&tx_buf[1], serprog_pgmname, sizeof serprog_pgmname);
|
|
nresp = sizeof(serprog_pgmname) + 1;
|
|
break;
|
|
case S_CMD_Q_SERBUF:
|
|
//printf("q_sbuf\n");
|
|
tx_buf[0] = S_ACK;
|
|
tx_buf[1] = sizeof(rx_buf) & 0xff;
|
|
tx_buf[2] = (sizeof(rx_buf) >> 8) & 0xff;
|
|
nresp = 3;
|
|
break;
|
|
case S_CMD_Q_BUSTYPE:
|
|
//printf("q_btyp\n");
|
|
tx_buf[0] = S_ACK;
|
|
tx_buf[1] = 1<<3; // SPI only
|
|
nresp = 2;
|
|
break;
|
|
case S_CMD_Q_WRNMAXLEN:
|
|
//printf("q_wlen\n");
|
|
tx_buf[0] = S_ACK;
|
|
tx_buf[1] = (sizeof(tx_buf)-1) & 0xff;
|
|
tx_buf[2] = ((sizeof(tx_buf)-1) >> 8) & 0xff;
|
|
tx_buf[3] = ((sizeof(tx_buf)-1) >>16) & 0xff;
|
|
nresp = 4;
|
|
break;
|
|
case S_CMD_Q_RDNMAXLEN:
|
|
//printf("q_rlen\n");
|
|
tx_buf[0] = S_ACK;
|
|
tx_buf[1] = (sizeof(rx_buf)-1) & 0xff;
|
|
tx_buf[2] = ((sizeof(rx_buf)-1) >> 8) & 0xff;
|
|
tx_buf[3] = ((sizeof(rx_buf)-1) >>16) & 0xff;
|
|
nresp = 4;
|
|
break;
|
|
case S_CMD_S_BUSTYPE:
|
|
//printf("s_btyp\n");
|
|
|
|
if (read_byte()/* bus type to set */ == (1<<3)) {
|
|
tx_buf[0] = S_ACK;
|
|
} else {
|
|
tx_buf[0] = S_NAK;
|
|
}
|
|
nresp = 1;
|
|
break;
|
|
|
|
case S_CMD_SPIOP: {
|
|
//printf("spiop\n");
|
|
|
|
uint32_t slen, rlen;
|
|
slen = (uint32_t)read_byte();
|
|
slen |= (uint32_t)read_byte() << 8;
|
|
slen |= (uint32_t)read_byte() << 16;
|
|
rlen = (uint32_t)read_byte();
|
|
rlen |= (uint32_t)read_byte() << 8;
|
|
rlen |= (uint32_t)read_byte() << 16;
|
|
|
|
sp_spi_op_begin();
|
|
size_t this_batch;
|
|
|
|
// 1. write slen data bytes
|
|
// we're going to use the tx buf for all operations here
|
|
while (slen > 0) {
|
|
this_batch = sizeof(tx_buf);
|
|
if (this_batch > slen) this_batch = slen;
|
|
|
|
for (size_t i = 0; i < this_batch; ++i) tx_buf[i] = read_byte();
|
|
sp_spi_op_write(this_batch, tx_buf);
|
|
|
|
slen -= this_batch;
|
|
}
|
|
|
|
// 2. write data
|
|
// first, do a batch of 63, because we also need to send an ACK byte
|
|
this_batch = sizeof(tx_buf)-1;
|
|
if (this_batch > rlen) this_batch = rlen;
|
|
sp_spi_op_read(this_batch, &tx_buf[1]);
|
|
tx_buf[0] = S_ACK;
|
|
tud_cdc_n_write(CDC_N_SERPROG, tx_buf, this_batch+1);
|
|
rlen -= this_batch;
|
|
|
|
// now do in batches of 64
|
|
while (rlen > 0) {
|
|
this_batch = sizeof(tx_buf);
|
|
if (this_batch > rlen) this_batch = rlen;
|
|
|
|
sp_spi_op_read(this_batch, tx_buf);
|
|
tud_cdc_n_write(CDC_N_SERPROG, tx_buf, this_batch);
|
|
|
|
rlen -= this_batch;
|
|
}
|
|
tud_cdc_n_write_flush(CDC_N_SERPROG);
|
|
|
|
// that's it!
|
|
sp_spi_op_end();
|
|
nresp = 0; // we sent our own response manually
|
|
}
|
|
break;
|
|
case S_CMD_S_SPI_FREQ: {
|
|
//printf("s_spi_freq\n");
|
|
uint32_t freq;
|
|
freq = (uint32_t)read_byte();
|
|
freq |= (uint32_t)read_byte() << 8;
|
|
freq |= (uint32_t)read_byte() << 16;
|
|
freq |= (uint32_t)read_byte() << 24;
|
|
|
|
uint32_t nfreq = sp_spi_set_freq(freq);
|
|
tx_buf[0] = S_ACK;
|
|
tx_buf[1] = nfreq & 0xff;
|
|
tx_buf[2] = (nfreq >> 8) & 0xff;
|
|
tx_buf[3] = (nfreq >> 16) & 0xff;
|
|
tx_buf[4] = (nfreq >> 24) & 0xff;
|
|
nresp = 5;
|
|
}
|
|
break;
|
|
case S_CMD_S_PINSTATE: {
|
|
//printf("s_pins\n");
|
|
|
|
if (read_byte() == 0) sp_spi_cs_deselect();
|
|
else sp_spi_cs_select();
|
|
|
|
tx_buf[0] = S_ACK;
|
|
nresp = 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//printf("ill %d\n", cmd);
|
|
tx_buf[0] = S_NAK;
|
|
nresp = 1;
|
|
break;
|
|
}
|
|
|
|
if (nresp > 0) {
|
|
tud_cdc_n_write(CDC_N_SERPROG, tx_buf, nresp);
|
|
tud_cdc_n_write_flush(CDC_N_SERPROG);
|
|
}
|
|
}
|
|
|
|
//extern void cdc_uart_task();
|
|
//void cdc_serprog_task(void) {
|
|
// bool conn = tud_cdc_n_connected(CDC_N_SERPROG),
|
|
// avail = tud_cdc_n_available(CDC_N_SERPROG);
|
|
// //printf("hi conn=%c avail=%c\n", conn?'y':'n', avail?'y':'n');
|
|
// // TODO: this is, apparently, not at all how this works: in practice,
|
|
// // bytes seem to be sent one by one, so its probably better to rework
|
|
// // this, a lot
|
|
// if (conn && avail) {
|
|
// //printf("rbp=%d\n", bufpos);
|
|
// uint32_t nread = tud_cdc_n_read(CDC_N_SERPROG, &rx_buf[bufpos], sizeof(rx_buf) - bufpos);
|
|
// printf("got %d\n", nread);
|
|
// cdc_uart_task();
|
|
//
|
|
// bufpos = 0;
|
|
// do {
|
|
// //printf("hbp=%d\n", /*rx_buf[bufpos],*/ bufpos);
|
|
// cdc_uart_task();
|
|
// uint32_t dec = serprog_handle_cmd(&rx_buf[bufpos], nread);
|
|
// cdc_uart_task();
|
|
// printf("dec=%d\n", dec);
|
|
//
|
|
// cdc_uart_task();
|
|
//
|
|
// // didn't do a decrement => not enough data, wait for the next
|
|
// // task() call to read it in
|
|
// if (dec == 0) {
|
|
// // so we move the leftover data to the start of the buffer,
|
|
// // and make sure the next call will put the new data right
|
|
// // after it
|
|
// //printf("mv %d %d %d ", nread, bufpos, rx_buf[bufpos]);
|
|
// memmove(rx_buf, &rx_buf[bufpos], nread);
|
|
// //printf("%d\n", rx_buf[0]);
|
|
// bufpos = nread;
|
|
// break;
|
|
// }
|
|
//
|
|
// nread -= dec;
|
|
// bufpos += dec;
|
|
// // read everything left in the buffer => we're done here
|
|
// if (nread == 0) {
|
|
// // and we can start using the full rx buffer again
|
|
// bufpos = 0;
|
|
// break;
|
|
// }
|
|
// } while (tud_cdc_n_connected(CDC_N_SERPROG) && tud_cdc_n_available(CDC_N_SERPROG));
|
|
// }
|
|
//}
|
|
void cdc_serprog_task(void) {
|
|
handle_cmd();
|
|
//printf("d\n");
|
|
}
|
|
|
|
#endif /* DBOARD_HAS_SERPROG */
|
|
|