mirror of
https://github.com/Polprzewodnikowy/SummerCart64.git
synced 2025-01-23 09:51:11 +01:00
214 lines
5.6 KiB
C
214 lines
5.6 KiB
C
|
#include <stdbool.h>
|
||
|
#include <stdint.h>
|
||
|
#include "cfg.h"
|
||
|
#include "fpga.h"
|
||
|
#include "rtc.h"
|
||
|
#include "usb.h"
|
||
|
#include "flash.h"
|
||
|
|
||
|
|
||
|
enum state {
|
||
|
STATE_IDLE,
|
||
|
STATE_ARGS,
|
||
|
STATE_DATA,
|
||
|
STATE_RESPONSE
|
||
|
};
|
||
|
|
||
|
struct process {
|
||
|
enum state state;
|
||
|
uint32_t counter;
|
||
|
uint8_t cmd;
|
||
|
uint32_t args[2];
|
||
|
bool error;
|
||
|
bool dma_in_progress;
|
||
|
};
|
||
|
|
||
|
static struct process p;
|
||
|
|
||
|
|
||
|
static const char CMD_TOKEN[3] = { 'C', 'M', 'D' };
|
||
|
static const uint32_t CMP_TOKEN = (0x434D5000UL);
|
||
|
static const uint32_t ERR_TOKEN = (0x45525200UL);
|
||
|
|
||
|
static bool usb_rx_byte (uint8_t *data) {
|
||
|
if (fpga_usb_status_get() & USB_STATUS_RXNE) {
|
||
|
*data = fpga_usb_pop();
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static bool usb_tx_byte (uint8_t data) {
|
||
|
if (fpga_usb_status_get() & USB_STATUS_TXE) {
|
||
|
fpga_usb_push(data);
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static uint8_t usb_rx_word_counter = 0;
|
||
|
static uint32_t usb_rx_word_buffer = 0;
|
||
|
|
||
|
static bool usb_rx_word (uint32_t *data) {
|
||
|
uint8_t tmp;
|
||
|
while (usb_rx_byte(&tmp)) {
|
||
|
usb_rx_word_buffer = (usb_rx_word_buffer << 8) | tmp;
|
||
|
usb_rx_word_counter += 1;
|
||
|
if (usb_rx_word_counter == 4) {
|
||
|
usb_rx_word_counter = 0;
|
||
|
*data = usb_rx_word_buffer;
|
||
|
usb_rx_word_buffer = 0;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static uint8_t usb_tx_word_counter = 0;
|
||
|
|
||
|
static bool usb_tx_word (uint32_t data) {
|
||
|
while (usb_tx_byte(data >> ((3 - usb_tx_word_counter) * 8))) {
|
||
|
usb_tx_word_counter += 1;
|
||
|
if (usb_tx_word_counter == 4) {
|
||
|
usb_tx_word_counter = 0;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static uint8_t usb_rx_cmd_counter = 0;
|
||
|
|
||
|
static bool usb_rx_cmd (uint8_t *cmd) {
|
||
|
uint8_t data;
|
||
|
while (usb_rx_byte(&data)) {
|
||
|
if (usb_rx_cmd_counter == 3) {
|
||
|
*cmd = data;
|
||
|
usb_rx_cmd_counter = 0;
|
||
|
return true;
|
||
|
}
|
||
|
if (data != CMD_TOKEN[usb_rx_cmd_counter++]) {
|
||
|
usb_rx_cmd_counter = 0;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
void usb_init (void) {
|
||
|
fpga_reg_set(REG_USB_DMA_SCR, DMA_SCR_STOP);
|
||
|
fpga_reg_set(REG_USB_SCR, USB_SCR_FIFO_FLUSH);
|
||
|
|
||
|
p.state = STATE_IDLE;
|
||
|
|
||
|
usb_rx_word_counter = 0;
|
||
|
usb_rx_word_buffer = 0;
|
||
|
usb_tx_word_counter = 0;
|
||
|
usb_rx_cmd_counter = 0;
|
||
|
}
|
||
|
|
||
|
void usb_process (void) {
|
||
|
uint32_t scr = fpga_reg_get(REG_USB_SCR);
|
||
|
|
||
|
if (scr & USB_SCR_RESET_PENDING) {
|
||
|
usb_init();
|
||
|
fpga_reg_set(REG_USB_SCR, USB_SCR_RESET_ACK);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (p.state) {
|
||
|
case STATE_IDLE: {
|
||
|
if (usb_rx_cmd(&p.cmd)) {
|
||
|
p.counter = 0;
|
||
|
p.error = false;
|
||
|
p.dma_in_progress = false;
|
||
|
p.state = STATE_ARGS;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_ARGS: {
|
||
|
if (usb_rx_word(&p.args[p.counter])) {
|
||
|
p.counter += 1;
|
||
|
if (p.counter == 2) {
|
||
|
p.counter = 0;
|
||
|
p.state = STATE_DATA;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_DATA: {
|
||
|
switch (p.cmd) {
|
||
|
case 'v':
|
||
|
if (usb_tx_word(cfg_get_version())) {
|
||
|
p.state = STATE_RESPONSE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'c':
|
||
|
if (p.counter == 0) {
|
||
|
cfg_query(p.args);
|
||
|
p.counter += 1;
|
||
|
}
|
||
|
if (usb_tx_word(p.args[1])) {
|
||
|
p.state = STATE_RESPONSE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'C':
|
||
|
cfg_update(p.args);
|
||
|
p.state = STATE_RESPONSE;
|
||
|
break;
|
||
|
|
||
|
case 't':
|
||
|
if (p.counter == 0) {
|
||
|
cfg_get_time(p.args);
|
||
|
p.counter += 1;
|
||
|
}
|
||
|
if ((p.counter == 1) && usb_tx_word(p.args[0])) {
|
||
|
p.counter += 1;
|
||
|
}
|
||
|
if ((p.counter == 2) && usb_tx_word(p.args[1])) {
|
||
|
p.state = STATE_RESPONSE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'T':
|
||
|
cfg_set_time(p.args);
|
||
|
p.state = STATE_RESPONSE;
|
||
|
break;
|
||
|
|
||
|
case 'm':
|
||
|
case 'M':
|
||
|
if (!((fpga_reg_get(REG_USB_DMA_SCR) & DMA_SCR_BUSY))) {
|
||
|
if (!p.dma_in_progress) {
|
||
|
fpga_reg_set(REG_USB_DMA_ADDRESS, p.args[0]);
|
||
|
fpga_reg_set(REG_USB_DMA_LENGTH, p.args[1]);
|
||
|
fpga_reg_set(REG_USB_DMA_SCR, (p.cmd == 'M' ? DMA_SCR_DIRECTION : 0) | DMA_SCR_START);
|
||
|
p.dma_in_progress = true;
|
||
|
} else {
|
||
|
p.state = STATE_RESPONSE;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
p.error = true;
|
||
|
p.state = STATE_RESPONSE;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_RESPONSE: {
|
||
|
if (usb_tx_word((p.error ? ERR_TOKEN : CMP_TOKEN) | p.cmd)) {
|
||
|
p.state = STATE_IDLE;
|
||
|
fpga_reg_set(REG_USB_SCR, USB_SCR_WRITE_FLUSH);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|