sw: first software commit

The software is untested, but it builds.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
2019-05-22 15:02:00 +08:00
parent dd14bb4d29
commit e8aaac5cfe
57 changed files with 36778 additions and 43 deletions

91
sw/src/crt0-vexriscv.S Normal file
View File

@ -0,0 +1,91 @@
.global main
.global isr
.section .text.start
.global _start
_start:
j crt_init
nop
nop
nop
nop
nop
nop
nop
.section .text
.global trap_entry
trap_entry:
sw x1, - 1*4(sp)
sw x5, - 2*4(sp)
sw x6, - 3*4(sp)
sw x7, - 4*4(sp)
sw x10, - 5*4(sp)
sw x11, - 6*4(sp)
sw x12, - 7*4(sp)
sw x13, - 8*4(sp)
sw x14, - 9*4(sp)
sw x15, -10*4(sp)
sw x16, -11*4(sp)
sw x17, -12*4(sp)
sw x28, -13*4(sp)
sw x29, -14*4(sp)
sw x30, -15*4(sp)
sw x31, -16*4(sp)
addi sp,sp,-16*4
call isr
lw x1 , 15*4(sp)
lw x5, 14*4(sp)
lw x6, 13*4(sp)
lw x7, 12*4(sp)
lw x10, 11*4(sp)
lw x11, 10*4(sp)
lw x12, 9*4(sp)
lw x13, 8*4(sp)
lw x14, 7*4(sp)
lw x15, 6*4(sp)
lw x16, 5*4(sp)
lw x17, 4*4(sp)
lw x28, 3*4(sp)
lw x29, 2*4(sp)
lw x30, 1*4(sp)
lw x31, 0*4(sp)
addi sp,sp,16*4
mret
.text
crt_init:
la sp, _fstack + 4
la a0, trap_entry
csrw mtvec, a0
bss_init:
la a0, _fbss
la a1, _ebss
bss_loop:
beq a0,a1,bss_done
sw zero,0(a0)
add a0,a0,4
j bss_loop
bss_done:
/* Load DATA */
la t0, _erodata
la t1, _fdata
la t2, _edata
3:
lw t3, 0(t0)
sw t3, 0(t1)
/* _edata is aligned to 16 bytes. Use word-xfers. */
addi t0, t0, 4
addi t1, t1, 4
bltu t1, t2, 3b
li a0, 0x880 //880 enable timer + external interrupt sources (until mstatus.MIE is set, they will never trigger an interrupt)
csrw mie,a0
call main
infinit_loop:
j infinit_loop

46
sw/src/main.c Normal file
View File

@ -0,0 +1,46 @@
#include <stdio.h>
#include <irq.h>
#include <usb.h>
#include <time.h>
#include <rgb.h>
#include <spi.h>
#include <fomu/csr.h>
struct ff_spi *spi;
void isr(void)
{
unsigned int irqs;
irqs = irq_pending() & irq_getmask();
if (irqs & (1 << USB_INTERRUPT))
usb_isr();
}
static void init(void)
{
rgb_init();
spi = spiAlloc();
spiInit(spi);
irq_setmask(0);
irq_setie(1);
usb_init();
time_init();
}
int main(int argc, char **argv)
{
(void)argc;
(void)argv;
init();
usb_connect();
while (1)
{
usb_poll();
}
return 0;
}

89
sw/src/rgb.c Normal file
View File

@ -0,0 +1,89 @@
#include <rgb.h>
#include <fomu/csr.h>
enum led_registers {
LEDDCR0 = 8,
LEDDBR = 9,
LEDDONR = 10,
LEDDOFR = 11,
LEDDBCRR = 5,
LEDDBCFR = 6,
LEDDPWRR = 1,
LEDDPWRG = 2,
LEDDPWRB = 3,
};
#define BREATHE_ENABLE (1 << 7)
#define BREATHE_EDGE_ON (0 << 6)
#define BREATHE_EDGE_BOTH (1 << 6)
#define BREATHE_MODE_MODULATE (1 << 5)
#define BREATHE_RATE(x) ((x & 7) << 0)
#define RGB_SWITCH_MODE(x) do { \
if (rgb_mode == x) \
return; \
rgb_mode = x; \
/* Toggle LEDD_EXE to force the mode to switch */ \
rgb_ctrl_write( (1 << 1) | (1 << 2)); \
rgb_ctrl_write((1 << 0) | (1 << 1) | (1 << 2)); \
} while(0)
static enum {
INVALID = 0,
IDLE,
WRITING,
ERROR,
DONE,
} rgb_mode;
static void rgb_write(uint8_t value, uint8_t addr) {
rgb_addr_write(addr);
rgb_dat_write(value);
}
void rgb_init(void) {
// Turn on the RGB block and current enable, as well as enabling led control
rgb_ctrl_write((1 << 0) | (1 << 1) | (1 << 2));
// Enable the LED driver, and set 250 Hz mode.
// Also set quick stop, which we'll use to switch patterns quickly.
rgb_write((1 << 7) | (1 << 6) | (1 << 3), LEDDCR0);
// Set clock register to 12 MHz / 64 kHz - 1
rgb_write((12000000/64000)-1, LEDDBR);
rgb_mode_idle();
}
static void rgb_switch_mode(uint8_t mode,
uint8_t onr, uint8_t ofr,
uint8_t onrate, uint8_t offrate,
uint8_t r, uint8_t g, uint8_t b) {
RGB_SWITCH_MODE(mode);
rgb_write(onr, LEDDONR);
rgb_write(ofr, LEDDOFR);
rgb_write(BREATHE_ENABLE | BREATHE_EDGE_BOTH
| BREATHE_MODE_MODULATE | BREATHE_RATE(onrate), LEDDBCRR);
rgb_write(BREATHE_ENABLE | BREATHE_MODE_MODULATE | BREATHE_RATE(offrate), LEDDBCFR);
rgb_write(r, LEDDPWRG); // Red
rgb_write(g, LEDDPWRB); // Green
rgb_write(b, LEDDPWRR); // Blue
}
void rgb_mode_idle(void) {
rgb_switch_mode(IDLE, 12, 14, 2, 3, 0x00/4, 0x4a/4, 0xe1/4);
}
void rgb_mode_writing(void) {
rgb_switch_mode(WRITING, 1, 2, 1, 3, 0x00/4, 0x7a/4, 0x51/4);
}
void rgb_mode_error(void) {
rgb_switch_mode(ERROR, 3, 3, 2, 3, 0xf0/4, 0x0a/4, 0x01/4);
}
void rgb_mode_done(void) {
rgb_switch_mode(DONE, 8, 8, 2, 3, 0x14/4, 0xff/4, 0x44/4);
}

824
sw/src/spi.c Normal file
View File

@ -0,0 +1,824 @@
#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <fomu/csr.h>
#include "spi.h"
#define fprintf(...) do {} while(0)
static uint8_t do_mirror;
static uint8_t oe_mirror;
#define PI_OUTPUT 1
#define PI_INPUT 0
#define PI_ALT0 PI_INPUT
static void gpioSetMode(int pin, int mode) {
if (mode)
oe_mirror |= 1 << pin;
else
oe_mirror &= ~(1 << pin);
picorvspi_cfg2_write(oe_mirror);
}
static void gpioWrite(int pin, int val) {
if (val)
do_mirror |= 1 << pin;
else
do_mirror &= ~(1 << pin);
picorvspi_cfg1_write(do_mirror);
}
static int gpioRead(int pin) {
return !!(picorvspi_stat1_read() & (1 << pin));
}
static void gpioSync(void) {
// bbspi_do_write(do_mirror);
}
#define SPI_ONLY_SINGLE
enum ff_spi_quirks {
// There is no separate "Write SR 2" command. Instead,
// you must write SR2 after writing SR1
SQ_SR2_FROM_SR1 = (1 << 0),
// Don't issue a "Write Enable" command prior to writing
// a status register
SQ_SKIP_SR_WEL = (1 << 1),
// Security registers are shifted up by 4 bits
SQ_SECURITY_NYBBLE_SHIFT = (1 << 2),
};
struct ff_spi {
enum spi_state state;
enum spi_type type;
enum spi_type desired_type;
struct spi_id id;
enum ff_spi_quirks quirks;
int size_override;
struct {
int clk;
int d0;
int d1;
int d2;
int d3;
int wp;
int hold;
int cs;
int miso;
int mosi;
} pins;
};
static void spi_get_id(struct ff_spi *spi);
static void spi_set_state(struct ff_spi *spi, enum spi_state state) {
return;
if (spi->state == state)
return;
#ifndef SPI_ONLY_SINGLE
switch (state) {
case SS_SINGLE:
#endif
gpioSetMode(spi->pins.clk, PI_OUTPUT); // CLK
gpioSetMode(spi->pins.cs, PI_OUTPUT); // CE0#
gpioSetMode(spi->pins.mosi, PI_OUTPUT); // MOSI
gpioSetMode(spi->pins.miso, PI_INPUT); // MISO
gpioSetMode(spi->pins.hold, PI_OUTPUT);
gpioSetMode(spi->pins.wp, PI_OUTPUT);
#ifndef SPI_ONLY_SINGLE
break;
case SS_DUAL_RX:
gpioSetMode(spi->pins.clk, PI_OUTPUT); // CLK
gpioSetMode(spi->pins.cs, PI_OUTPUT); // CE0#
gpioSetMode(spi->pins.mosi, PI_INPUT); // MOSI
gpioSetMode(spi->pins.miso, PI_INPUT); // MISO
gpioSetMode(spi->pins.hold, PI_OUTPUT);
gpioSetMode(spi->pins.wp, PI_OUTPUT);
break;
case SS_DUAL_TX:
gpioSetMode(spi->pins.clk, PI_OUTPUT); // CLK
gpioSetMode(spi->pins.cs, PI_OUTPUT); // CE0#
gpioSetMode(spi->pins.mosi, PI_OUTPUT); // MOSI
gpioSetMode(spi->pins.miso, PI_OUTPUT); // MISO
gpioSetMode(spi->pins.hold, PI_OUTPUT);
gpioSetMode(spi->pins.wp, PI_OUTPUT);
break;
case SS_QUAD_RX:
gpioSetMode(spi->pins.clk, PI_OUTPUT); // CLK
gpioSetMode(spi->pins.cs, PI_OUTPUT); // CE0#
gpioSetMode(spi->pins.mosi, PI_INPUT); // MOSI
gpioSetMode(spi->pins.miso, PI_INPUT); // MISO
gpioSetMode(spi->pins.hold, PI_INPUT);
gpioSetMode(spi->pins.wp, PI_INPUT);
break;
case SS_QUAD_TX:
gpioSetMode(spi->pins.clk, PI_OUTPUT); // CLK
gpioSetMode(spi->pins.cs, PI_OUTPUT); // CE0#
gpioSetMode(spi->pins.mosi, PI_OUTPUT); // MOSI
gpioSetMode(spi->pins.miso, PI_OUTPUT); // MISO
gpioSetMode(spi->pins.hold, PI_OUTPUT);
gpioSetMode(spi->pins.wp, PI_OUTPUT);
break;
case SS_HARDWARE:
gpioSetMode(spi->pins.clk, PI_ALT0); // CLK
gpioSetMode(spi->pins.cs, PI_ALT0); // CE0#
gpioSetMode(spi->pins.mosi, PI_ALT0); // MOSI
gpioSetMode(spi->pins.miso, PI_ALT0); // MISO
gpioSetMode(spi->pins.hold, PI_OUTPUT);
gpioSetMode(spi->pins.wp, PI_OUTPUT);
break;
default:
fprintf(stderr, "Unrecognized spi state\n");
return;
}
#endif
spi->state = state;
}
void spiPause(struct ff_spi *spi) {
(void)spi;
gpioSync();
// usleep(1);
return;
}
void spiBegin(struct ff_spi *spi) {
spi_set_state(spi, SS_SINGLE);
if ((spi->type == ST_SINGLE) || (spi->type == ST_DUAL)) {
gpioWrite(spi->pins.wp, 1);
gpioWrite(spi->pins.hold, 1);
}
gpioWrite(spi->pins.cs, 0);
}
void spiEnd(struct ff_spi *spi) {
(void)spi;
gpioWrite(spi->pins.cs, 1);
}
static uint8_t spiXfer(struct ff_spi *spi, uint8_t out) {
int bit;
uint8_t in = 0;
for (bit = 7; bit >= 0; bit--) {
if (out & (1 << bit)) {
gpioWrite(spi->pins.mosi, 1);
}
else {
gpioWrite(spi->pins.mosi, 0);
}
gpioWrite(spi->pins.clk, 1);
spiPause(spi);
in |= ((!!gpioRead(spi->pins.miso)) << bit);
gpioWrite(spi->pins.clk, 0);
spiPause(spi);
}
return in;
}
static void spiSingleTx(struct ff_spi *spi, uint8_t out) {
spi_set_state(spi, SS_SINGLE);
spiXfer(spi, out);
}
static uint8_t spiSingleRx(struct ff_spi *spi) {
spi_set_state(spi, SS_SINGLE);
return spiXfer(spi, 0xff);
}
static void spiDualTx(struct ff_spi *spi, uint8_t out) {
int bit;
spi_set_state(spi, SS_DUAL_TX);
for (bit = 7; bit >= 0; bit -= 2) {
if (out & (1 << (bit - 1))) {
gpioWrite(spi->pins.d0, 1);
}
else {
gpioWrite(spi->pins.d0, 0);
}
if (out & (1 << (bit - 0))) {
gpioWrite(spi->pins.d1, 1);
}
else {
gpioWrite(spi->pins.d1, 0);
}
gpioWrite(spi->pins.clk, 1);
spiPause(spi);
gpioWrite(spi->pins.clk, 0);
spiPause(spi);
}
}
static void spiQuadTx(struct ff_spi *spi, uint8_t out) {
int bit;
spi_set_state(spi, SS_QUAD_TX);
for (bit = 7; bit >= 0; bit -= 4) {
if (out & (1 << (bit - 3))) {
gpioWrite(spi->pins.d0, 1);
}
else {
gpioWrite(spi->pins.d0, 0);
}
if (out & (1 << (bit - 2))) {
gpioWrite(spi->pins.d1, 1);
}
else {
gpioWrite(spi->pins.d1, 0);
}
if (out & (1 << (bit - 1))) {
gpioWrite(spi->pins.d2, 1);
}
else {
gpioWrite(spi->pins.d2, 0);
}
if (out & (1 << (bit - 0))) {
gpioWrite(spi->pins.d3, 1);
}
else {
gpioWrite(spi->pins.d3, 0);
}
gpioWrite(spi->pins.clk, 1);
spiPause(spi);
gpioWrite(spi->pins.clk, 0);
spiPause(spi);
}
}
static uint8_t spiDualRx(struct ff_spi *spi) {
int bit;
uint8_t in = 0;
spi_set_state(spi, SS_QUAD_RX);
for (bit = 7; bit >= 0; bit -= 2) {
gpioWrite(spi->pins.clk, 1);
spiPause(spi);
in |= ((!!gpioRead(spi->pins.d0)) << (bit - 1));
in |= ((!!gpioRead(spi->pins.d1)) << (bit - 0));
gpioWrite(spi->pins.clk, 0);
spiPause(spi);
}
return in;
}
static uint8_t spiQuadRx(struct ff_spi *spi) {
int bit;
uint8_t in = 0;
spi_set_state(spi, SS_QUAD_RX);
for (bit = 7; bit >= 0; bit -= 4) {
gpioWrite(spi->pins.clk, 1);
spiPause(spi);
in |= ((!!gpioRead(spi->pins.d0)) << (bit - 3));
in |= ((!!gpioRead(spi->pins.d1)) << (bit - 2));
in |= ((!!gpioRead(spi->pins.d2)) << (bit - 1));
in |= ((!!gpioRead(spi->pins.d3)) << (bit - 0));
gpioWrite(spi->pins.clk, 0);
spiPause(spi);
}
return in;
}
int spiTx(struct ff_spi *spi, uint8_t word) {
switch (spi->type) {
case ST_SINGLE:
spiSingleTx(spi, word);
break;
case ST_DUAL:
spiDualTx(spi, word);
break;
case ST_QUAD:
case ST_QPI:
spiQuadTx(spi, word);
break;
default:
return -1;
}
return 0;
}
uint8_t spiRx(struct ff_spi *spi) {
switch (spi->type) {
case ST_SINGLE:
return spiSingleRx(spi);
case ST_DUAL:
return spiDualRx(spi);
case ST_QUAD:
case ST_QPI:
return spiQuadRx(spi);
default:
return 0xff;
}
}
void spiCommand(struct ff_spi *spi, uint8_t cmd) {
if (spi->type == ST_QPI)
spiQuadTx(spi, cmd);
else
spiSingleTx(spi, cmd);
}
uint8_t spiCommandRx(struct ff_spi *spi) {
if (spi->type == ST_QPI)
return spiQuadRx(spi);
else
return spiSingleRx(spi);
}
uint8_t spiReadStatus(struct ff_spi *spi, uint8_t sr) {
uint8_t val = 0xff;
(void)sr;
#if 0
switch (sr) {
case 1:
#endif
spiBegin(spi);
spiCommand(spi, 0x05);
val = spiCommandRx(spi);
spiEnd(spi);
#if 0
break;
case 2:
spiBegin(spi);
spiCommand(spi, 0x35);
val = spiCommandRx(spi);
spiEnd(spi);
break;
case 3:
spiBegin(spi);
spiCommand(spi, 0x15);
val = spiCommandRx(spi);
spiEnd(spi);
break;
default:
fprintf(stderr, "unrecognized status register: %d\n", sr);
break;
}
#endif
return val;
}
void spiWriteSecurity(struct ff_spi *spi, uint8_t sr, uint8_t security[256]) {
if (spi->quirks & SQ_SECURITY_NYBBLE_SHIFT)
sr <<= 4;
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
// erase the register
spiBegin(spi);
spiCommand(spi, 0x44);
spiCommand(spi, 0x00); // A23-16
spiCommand(spi, sr); // A15-8
spiCommand(spi, 0x00); // A0-7
spiEnd(spi);
spi_get_id(spi);
sleep(1);
// write enable
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
spiBegin(spi);
spiCommand(spi, 0x42);
spiCommand(spi, 0x00); // A23-16
spiCommand(spi, sr); // A15-8
spiCommand(spi, 0x00); // A0-7
int i;
for (i = 0; i < 256; i++)
spiCommand(spi, security[i]);
spiEnd(spi);
spi_get_id(spi);
}
void spiReadSecurity(struct ff_spi *spi, uint8_t sr, uint8_t security[256]) {
if (spi->quirks & SQ_SECURITY_NYBBLE_SHIFT)
sr <<= 4;
spiBegin(spi);
spiCommand(spi, 0x48); // Read security registers
spiCommand(spi, 0x00); // A23-16
spiCommand(spi, sr); // A15-8
spiCommand(spi, 0x00); // A0-7
int i;
for (i = 0; i < 256; i++)
security[i] = spiCommandRx(spi);
spiEnd(spi);
}
void spiWriteStatus(struct ff_spi *spi, uint8_t sr, uint8_t val) {
switch (sr) {
case 1:
if (!(spi->quirks & SQ_SKIP_SR_WEL)) {
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
}
spiBegin(spi);
spiCommand(spi, 0x50);
spiEnd(spi);
spiBegin(spi);
spiCommand(spi, 0x01);
spiCommand(spi, val);
spiEnd(spi);
break;
case 2: {
uint8_t sr1 = 0x00;
if (spi->quirks & SQ_SR2_FROM_SR1)
sr1 = spiReadStatus(spi, 1);
if (!(spi->quirks & SQ_SKIP_SR_WEL)) {
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
}
spiBegin(spi);
spiCommand(spi, 0x50);
spiEnd(spi);
spiBegin(spi);
if (spi->quirks & SQ_SR2_FROM_SR1) {
spiCommand(spi, 0x01);
spiCommand(spi, sr1);
spiCommand(spi, val);
}
else {
spiCommand(spi, 0x31);
spiCommand(spi, val);
}
spiEnd(spi);
break;
}
case 3:
if (!(spi->quirks & SQ_SKIP_SR_WEL)) {
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
}
spiBegin(spi);
spiCommand(spi, 0x50);
spiEnd(spi);
spiBegin(spi);
spiCommand(spi, 0x11);
spiCommand(spi, val);
spiEnd(spi);
break;
default:
fprintf(stderr, "unrecognized status register: %d\n", sr);
break;
}
}
#if 0
struct spi_id spiId(struct ff_spi *spi) {
return spi->id;
}
static void spi_decode_id(struct ff_spi *spi) {
spi->id.bytes = -1; // unknown
if (spi->id.manufacturer_id == 0xef) {
// spi->id.manufacturer = "Winbond";
if ((spi->id.memory_type == 0x70)
&& (spi->id.memory_size == 0x18)) {
// spi->id.model = "W25Q128JV";
// spi->id.capacity = "128 Mbit";
spi->id.bytes = 16 * 1024 * 1024;
}
}
if (spi->id.manufacturer_id == 0x1f) {
// spi->id.manufacturer = "Adesto";
if ((spi->id.memory_type == 0x86)
&& (spi->id.memory_size == 0x01)) {
// spi->id.model = "AT25SF161";
// spi->id.capacity = "16 Mbit";
spi->id.bytes = 1 * 1024 * 1024;
}
}
return;
}
#endif
static void spi_get_id(struct ff_spi *spi) {
spiBegin(spi);
spiCommand(spi, 0x90); // Read manufacturer ID
spiCommand(spi, 0x00); // Dummy byte 1
spiCommand(spi, 0x00); // Dummy byte 2
spiCommand(spi, 0x00); // Dummy byte 3
spi->id.manufacturer_id = spiCommandRx(spi);
spi->id.device_id = spiCommandRx(spi);
spiEnd(spi);
return;
#if 0
spiBegin(spi);
spiCommand(spi, 0x9f); // Read device id
spi->id._manufacturer_id = spiCommandRx(spi);
spi->id.memory_type = spiCommandRx(spi);
spi->id.memory_size = spiCommandRx(spi);
spiEnd(spi);
spiBegin(spi);
spiCommand(spi, 0xab); // Read electronic signature
spiCommand(spi, 0x00); // Dummy byte 1
spiCommand(spi, 0x00); // Dummy byte 2
spiCommand(spi, 0x00); // Dummy byte 3
spi->id.signature = spiCommandRx(spi);
spiEnd(spi);
spiBegin(spi);
spiCommand(spi, 0x4b); // Read unique ID
spiCommand(spi, 0x00); // Dummy byte 1
spiCommand(spi, 0x00); // Dummy byte 2
spiCommand(spi, 0x00); // Dummy byte 3
spiCommand(spi, 0x00); // Dummy byte 4
spi->id.serial[0] = spiCommandRx(spi);
spi->id.serial[1] = spiCommandRx(spi);
spi->id.serial[2] = spiCommandRx(spi);
spi->id.serial[3] = spiCommandRx(spi);
spiEnd(spi);
spi_decode_id(spi);
return;
#endif
}
#if 0
void spiOverrideSize(struct ff_spi *spi, uint32_t size) {
spi->size_override = size;
// If size is 0, re-read the capacity
if (!size)
spi_decode_id(spi);
else
spi->id.bytes = size;
}
#endif
int spiSetType(struct ff_spi *spi, enum spi_type type) {
if (spi->type == type)
return 0;
#ifndef SPI_ONLY_SINGLE
switch (type) {
case ST_SINGLE:
#endif
if (spi->type == ST_QPI) {
spiBegin(spi);
spiCommand(spi, 0xff); // Exit QPI Mode
spiEnd(spi);
}
spi->type = type;
spi_set_state(spi, SS_SINGLE);
#ifndef SPI_ONLY_SINGLE
break;
case ST_DUAL:
if (spi->type == ST_QPI) {
spiBegin(spi);
spiCommand(spi, 0xff); // Exit QPI Mode
spiEnd(spi);
}
spi->type = type;
spi_set_state(spi, SS_DUAL_TX);
break;
case ST_QUAD:
if (spi->type == ST_QPI) {
spiBegin(spi);
spiCommand(spi, 0xff); // Exit QPI Mode
spiEnd(spi);
}
// Enable QE bit
spiWriteStatus(spi, 2, spiReadStatus(spi, 2) | (1 << 1));
spi->type = type;
spi_set_state(spi, SS_QUAD_TX);
break;
case ST_QPI:
// Enable QE bit
spiWriteStatus(spi, 2, spiReadStatus(spi, 2) | (1 << 1));
spiBegin(spi);
spiCommand(spi, 0x38); // Enter QPI Mode
spiEnd(spi);
spi->type = type;
spi_set_state(spi, SS_QUAD_TX);
break;
default:
fprintf(stderr, "Unrecognized SPI type: %d\n", type);
return 1;
}
#endif
return 0;
}
int spiIsBusy(struct ff_spi *spi) {
return spiReadStatus(spi, 1) & (1 << 0);
}
int spiBeginErase32(struct ff_spi *spi, uint32_t erase_addr) {
// Enable Write-Enable Latch (WEL)
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
spiBegin(spi);
spiCommand(spi, 0x52);
spiCommand(spi, erase_addr >> 16);
spiCommand(spi, erase_addr >> 8);
spiCommand(spi, erase_addr >> 0);
spiEnd(spi);
return 0;
}
int spiBeginErase64(struct ff_spi *spi, uint32_t erase_addr) {
// Enable Write-Enable Latch (WEL)
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
spiBegin(spi);
spiCommand(spi, 0xD8);
spiCommand(spi, erase_addr >> 16);
spiCommand(spi, erase_addr >> 8);
spiCommand(spi, erase_addr >> 0);
spiEnd(spi);
return 0;
}
int spiBeginWrite(struct ff_spi *spi, uint32_t addr, const void *v_data, unsigned int count) {
const uint8_t write_cmd = 0x02;
const uint8_t *data = v_data;
unsigned int i;
// Enable Write-Enable Latch (WEL)
spiBegin(spi);
spiCommand(spi, 0x06);
spiEnd(spi);
// uint8_t sr1 = spiReadStatus(spi, 1);
// if (!(sr1 & (1 << 1)))
// fprintf(stderr, "error: write-enable latch (WEL) not set, write will probably fail\n");
spiBegin(spi);
spiCommand(spi, write_cmd);
spiCommand(spi, addr >> 16);
spiCommand(spi, addr >> 8);
spiCommand(spi, addr >> 0);
for (i = 0; (i < count) && (i < 256); i++)
spiTx(spi, *data++);
spiEnd(spi);
return 0;
}
uint8_t spiReset(struct ff_spi *spi) {
// XXX You should check the "Ready" bit before doing this!
// Shift to QPI mode, then back to Single mode, to ensure
// we're actually in Single mode.
spiSetType(spi, ST_QPI);
spiSetType(spi, ST_SINGLE);
spiBegin(spi);
spiCommand(spi, 0x66); // "Enable Reset" command
spiEnd(spi);
spiBegin(spi);
spiCommand(spi, 0x99); // "Reset Device" command
spiEnd(spi);
// msleep(30);
spiBegin(spi);
spiCommand(spi, 0xab); // "Resume from Deep Power-Down" command
spiEnd(spi);
return 0;
}
int spiInit(struct ff_spi *spi) {
spi->state = SS_UNCONFIGURED;
spi->type = ST_UNCONFIGURED;
// Disable memory-mapped mode and enable bit-bang mode
picorvspi_cfg4_write(0);
// Reset the SPI flash, which will return it to SPI mode even
// if it's in QPI mode.
spiReset(spi);
spiSetType(spi, ST_SINGLE);
// Have the SPI flash pay attention to us
gpioWrite(spi->pins.hold, 1);
// Disable WP
gpioWrite(spi->pins.wp, 1);
gpioSetMode(spi->pins.clk, PI_OUTPUT); // CLK
gpioSetMode(spi->pins.cs, PI_OUTPUT); // CE0#
gpioSetMode(spi->pins.mosi, PI_OUTPUT); // MOSI
gpioSetMode(spi->pins.miso, PI_INPUT); // MISO
gpioSetMode(spi->pins.hold, PI_OUTPUT);
gpioSetMode(spi->pins.wp, PI_OUTPUT);
spi_get_id(spi);
spi->quirks |= SQ_SR2_FROM_SR1;
if (spi->id.manufacturer_id == 0xef)
spi->quirks |= SQ_SKIP_SR_WEL | SQ_SECURITY_NYBBLE_SHIFT;
return 0;
}
void spiEnableQuad(void) {
struct ff_spi *spi = spiAlloc();
spiInit(spi);
spiWriteStatus(spi, 2, spiReadStatus(spi, 2) | (1 << 1));
spiFree();
}
struct ff_spi *spiAlloc(void) {
static struct ff_spi spi;
return &spi;
}
void spiSetPin(struct ff_spi *spi, enum spi_pin pin, int val) {
switch (pin) {
case SP_MOSI: spi->pins.mosi = val; break;
case SP_MISO: spi->pins.miso = val; break;
case SP_HOLD: spi->pins.hold = val; break;
case SP_WP: spi->pins.wp = val; break;
case SP_CS: spi->pins.cs = val; break;
case SP_CLK: spi->pins.clk = val; break;
case SP_D0: spi->pins.d0 = val; break;
case SP_D1: spi->pins.d1 = val; break;
case SP_D2: spi->pins.d2 = val; break;
case SP_D3: spi->pins.d3 = val; break;
default: fprintf(stderr, "unrecognized pin: %d\n", pin); break;
}
}
void spiHold(struct ff_spi *spi) {
spiBegin(spi);
spiCommand(spi, 0xb9);
spiEnd(spi);
}
void spiUnhold(struct ff_spi *spi) {
spiBegin(spi);
spiCommand(spi, 0xab);
spiEnd(spi);
}
void spiFree(void) {
// Re-enable memory-mapped mode
picorvspi_cfg4_write(0x80);
}

43
sw/src/time.c Normal file
View File

@ -0,0 +1,43 @@
#include <fomu/csr.h>
#include <time.h>
void time_init(void)
{
int t;
timer0_en_write(0);
t = 2*SYSTEM_CLOCK_FREQUENCY;
timer0_reload_write(t);
timer0_load_write(t);
timer0_en_write(1);
}
int elapsed(int *last_event, int period)
{
int t, dt;
timer0_update_value_write(1);
t = timer0_reload_read() - timer0_value_read();
if(period < 0) {
*last_event = t;
return 1;
}
dt = t - *last_event;
if(dt < 0)
dt += timer0_reload_read();
if((dt > period) || (dt < 0)) {
*last_event = t;
return 1;
} else
return 0;
}
void msleep(int ms)
{
timer0_en_write(0);
timer0_reload_write(0);
timer0_load_write(SYSTEM_CLOCK_FREQUENCY/1000*ms);
timer0_en_write(1);
timer0_update_value_write(1);
while(timer0_value_read()) timer0_update_value_write(1);
}

220
sw/src/usb-desc.c Normal file
View File

@ -0,0 +1,220 @@
/* Teensyduino Core Library
* http://www.pjrc.com/teensy/
* Copyright (c) 2013 PJRC.COM, LLC.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* 1. The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* 2. If the Software is incorporated into a build system that allows
* selection among a list of target devices, then similar target
* devices manufactured by PJRC.COM must be included in the list of
* target devices and selectable in the same manner.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <usb-desc.h>
#include <usb-cdc.h>
// USB Descriptors are binary data which the USB host reads to
// automatically detect a USB device's capabilities. The format
// and meaning of every field is documented in numerous USB
// standards. When working with USB descriptors, despite the
// complexity of the standards and poor writing quality in many
// of those documents, remember descriptors are nothing more
// than constant binary data that tells the USB host what the
// device can do. Computers will load drivers based on this data.
// Those drivers then communicate on the endpoints specified by
// the descriptors.
// To configure a new combination of interfaces or make minor
// changes to existing configuration (eg, change the name or ID
// numbers), usually you would edit "usb_desc.h". This file
// is meant to be configured by the header, so generally it is
// only edited to add completely new USB interfaces or features.
// **************************************************************
// USB Device
// **************************************************************
#define LSB(n) ((n) & 255)
#define MSB(n) (((n) >> 8) & 255)
// USB Device Descriptor. The USB host reads this first, to learn
// what type of device is connected.
static const uint8_t device_descriptor[] = {
18, // bLength
1, // bDescriptorType
0x01, 0x02, // bcdUSB
USB_CLASS_CDC, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
EP0_SIZE, // bMaxPacketSize0
LSB(VENDOR_ID), MSB(VENDOR_ID), // idVendor
LSB(PRODUCT_ID), MSB(PRODUCT_ID), // idProduct
LSB(DEVICE_VER), MSB(DEVICE_VER), // bcdDevice
1, // iManufacturer
2, // iProduct
0, // iSerialNumber
1 // bNumConfigurations
};
// These descriptors must NOT be "const", because the USB DMA
// has trouble accessing flash memory with enough bandwidth
// while the processor is executing from flash.
// **************************************************************
// USB Configuration
// **************************************************************
// USB Configuration Descriptor. This huge descriptor tells all
// of the devices capbilities.
static const uint8_t config_descriptor[CONFIG_DESC_SIZE] = {
// configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10
9, // bLength;
2, // bDescriptorType;
LSB(CONFIG_DESC_SIZE), // wTotalLength
MSB(CONFIG_DESC_SIZE),
NUM_INTERFACE, // bNumInterfaces
1, // bConfigurationValue
1, // iConfiguration
0x80, // bmAttributes
50, // bMaxPower
// interface descriptor, CDC
USB_DT_INTERFACE_SIZE, // bLength
USB_DT_INTERFACE, // bDescriptorType
0, // bInterfaceNumber
0, // bAlternateSetting
1, // bNumEndpoints
USB_CLASS_CDC, // bInterfaceClass
USB_CDC_SUBCLASS_ACM, // bInterfaceSubClass
USB_CDC_PROTOCOL_AT, // bInterfaceProtocol
0, // iInterface
// Header Functional Descriptor
0x05, // bLength: Endpoint Descriptor size
CS_INTERFACE, // bDescriptorType: CS_INTERFACE
USB_CDC_TYPE_HEADER, // bDescriptorSubtype: Header Func Desc
0x10, // bcdCDC: spec release number
0x01,
// Call Managment Functional Descriptor
0x05, // bFunctionLength
CS_INTERFACE, // bDescriptorType: CS_INTERFACE
USB_CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype: Call Management Func Desc
0, // bmCapabilities: D0+D1
1, // bDataInterface: 1
// ACM Functional Descriptor
0x04, // bFunctionLength
CS_INTERFACE, // bDescriptorType: CS_INTERFACE
USB_CDC_TYPE_ACM, // bDescriptorSubtype: Abstract Control Management desc
6, // bmCapabilities
// Union Functional Descriptor
0x05, // bFunctionLength
CS_INTERFACE, // bDescriptorType: CS_INTERFACE
USB_CDC_TYPE_UNION, // bDescriptorSubtype: Union func desc
0, // bMasterInterface: Communication class interface
1, // bSlaveInterface0: Data Class Interface
// Endpoint descriptor
USB_DT_ENDPOINT_SIZE, // bLength
USB_DT_ENDPOINT, // bDescriptorType
0x81, // bEndpointAddress
USB_ENDPOINT_ATTR_INTERRUPT, // bmAttributes
LSB(16), // wMaxPacketSize
MSB(16), // wMaxPacketSize
255, // bInterval
// interface descriptor, CDC
USB_DT_INTERFACE_SIZE, // bLength
USB_DT_INTERFACE, // bDescriptorType
1, // bInterfaceNumber
0, // bAlternateSetting
2, // bNumEndpoints
USB_CLASS_DATA, // bInterfaceClass
0, // bInterfaceSubClass
0, // bInterfaceProtocol
0, // iInterface
// Endpoint descriptor
USB_DT_ENDPOINT_SIZE, // bLength
USB_DT_ENDPOINT, // bDescriptorType
0x82, // bEndpointAddress
USB_ENDPOINT_ATTR_BULK, // bmAttributes
LSB(64), // wMaxPacketSize
MSB(64), // wMaxPacketSize
1, // bInterval
// Endpoint descriptor
USB_DT_ENDPOINT_SIZE, // bLength
USB_DT_ENDPOINT, // bDescriptorType
0x02, // bEndpointAddress
USB_ENDPOINT_ATTR_BULK, // bmAttributes
LSB(64), // wMaxPacketSize
MSB(64), // wMaxPacketSize
1, // bInterval
};
// **************************************************************
// String Descriptors
// **************************************************************
// The descriptors above can provide human readable strings,
// referenced by index numbers. These descriptors are the
// actual string data
static const struct usb_string_descriptor_struct string0 = {
4,
3,
{0x0409}
};
__attribute__((aligned(4)))
static const struct usb_string_descriptor_struct usb_string_manufacturer_name = {
2 + MANUFACTURER_NAME_LEN,
3,
MANUFACTURER_NAME
};
__attribute__((aligned(4)))
static const struct usb_string_descriptor_struct usb_string_product_name = {
2 + PRODUCT_NAME_LEN,
3,
PRODUCT_NAME
};
// **************************************************************
// Descriptors List
// **************************************************************
// This table provides access to all the descriptor data above.
const usb_descriptor_list_t usb_descriptor_list[] = {
{0x0100, sizeof(device_descriptor), device_descriptor},
{0x0200, sizeof(config_descriptor), config_descriptor},
{0x0300, 0, (const uint8_t *)&string0},
{0x0301, 0, (const uint8_t *)&usb_string_manufacturer_name},
{0x0302, 0, (const uint8_t *)&usb_string_product_name},
{0, 0, NULL}
};

254
sw/src/usb-epfifo.c Normal file
View File

@ -0,0 +1,254 @@
#include <irq.h>
#include <riscv.h>
#include <string.h>
#include <usb.h>
#ifdef CSR_USB_EP_0_OUT_EV_PENDING_ADDR
#define EP0OUT_BUFFERS 4
__attribute__((aligned(4)))
static uint8_t volatile usb_ep0out_buffer_len[EP0OUT_BUFFERS];
static uint8_t volatile usb_ep0out_buffer[EP0OUT_BUFFERS][256];
static uint8_t volatile usb_ep0out_last_tok[EP0OUT_BUFFERS];
static volatile uint8_t usb_ep0out_wr_ptr;
static volatile uint8_t usb_ep0out_rd_ptr;
static const int max_byte_length = 64;
static const uint8_t * volatile current_data;
static volatile int current_length;
static volatile int data_offset;
static volatile int data_to_send;
static int next_packet_is_empty;
// Note that our PIDs are only bits 2 and 3 of the token,
// since all other bits are effectively redundant at this point.
enum USB_PID {
USB_PID_OUT = 0,
USB_PID_SOF = 1,
USB_PID_IN = 2,
USB_PID_SETUP = 3,
};
enum epfifo_response {
EPF_ACK = 0,
EPF_NAK = 1,
EPF_NONE = 2,
EPF_STALL = 3,
};
#define USB_EV_ERROR 1
#define USB_EV_PACKET 2
void usb_idle(void) {
usb_ep_0_out_ev_enable_write(0);
usb_ep_0_in_ev_enable_write(0);
// Reject all incoming data, since there is no handler anymore
usb_ep_0_out_respond_write(EPF_NAK);
// Reject outgoing data, since we don't have any to give.
usb_ep_0_in_respond_write(EPF_NAK);
irq_setmask(irq_getmask() & ~(1 << USB_INTERRUPT));
}
void usb_disconnect(void) {
usb_ep_0_out_ev_enable_write(0);
usb_ep_0_in_ev_enable_write(0);
irq_setmask(irq_getmask() & ~(1 << USB_INTERRUPT));
usb_pullup_out_write(0);
}
void usb_connect(void) {
usb_ep_0_out_ev_pending_write(usb_ep_0_out_ev_enable_read());
usb_ep_0_in_ev_pending_write(usb_ep_0_in_ev_pending_read());
usb_ep_0_out_ev_enable_write(USB_EV_PACKET | USB_EV_ERROR);
usb_ep_0_in_ev_enable_write(USB_EV_PACKET | USB_EV_ERROR);
// Accept incoming data by default.
usb_ep_0_out_respond_write(EPF_ACK);
// Reject outgoing data, since we have none to give yet.
usb_ep_0_in_respond_write(EPF_NAK);
usb_pullup_out_write(1);
irq_setmask(irq_getmask() | (1 << USB_INTERRUPT));
}
void usb_init(void) {
usb_ep0out_wr_ptr = 0;
usb_ep0out_rd_ptr = 0;
usb_pullup_out_write(0);
return;
}
static void process_tx(void) {
// Don't allow requeueing -- only queue more data if we're
// currently set up to respond NAK.
if (usb_ep_0_in_respond_read() != EPF_NAK) {
return;
}
// Prevent us from double-filling the buffer.
if (!usb_ep_0_in_ibuf_empty_read()) {
return;
}
if (!current_data || !current_length) {
return;
}
data_offset += data_to_send;
data_to_send = current_length - data_offset;
// Clamp the data to the maximum packet length
if (data_to_send > max_byte_length) {
data_to_send = max_byte_length;
next_packet_is_empty = 0;
}
else if (data_to_send == max_byte_length) {
next_packet_is_empty = 1;
}
else if (next_packet_is_empty) {
next_packet_is_empty = 0;
data_to_send = 0;
}
else if (current_data == NULL || data_to_send <= 0) {
next_packet_is_empty = 0;
current_data = NULL;
current_length = 0;
data_offset = 0;
data_to_send = 0;
return;
}
int this_offset;
for (this_offset = data_offset; this_offset < (data_offset + data_to_send); this_offset++) {
usb_ep_0_in_ibuf_head_write(current_data[this_offset]);
}
usb_ep_0_in_respond_write(EPF_ACK);
return;
}
void usb_send(const void *data, int total_count) {
while ((current_length || current_data))// && usb_ep_0_in_respond_read() != EPF_NAK)
;
current_data = (uint8_t *)data;
current_length = total_count;
data_offset = 0;
data_to_send = 0;
process_tx();
}
void usb_wait_for_send_done(void) {
while (current_data && current_length)
usb_poll();
while ((usb_ep_0_in_dtb_read() & 1) == 1)
usb_poll();
}
void usb_isr(void) {
uint8_t ep0o_pending = usb_ep_0_out_ev_pending_read();
uint8_t ep0i_pending = usb_ep_0_in_ev_pending_read();
// We got an OUT or a SETUP packet. Copy it to usb_ep0out_buffer
// and clear the "pending" bit.
if (ep0o_pending) {
uint8_t last_tok = usb_ep_0_out_last_tok_read();
int byte_count = 0;
usb_ep0out_last_tok[usb_ep0out_wr_ptr] = last_tok;
volatile uint8_t * obuf = usb_ep0out_buffer[usb_ep0out_wr_ptr];
if (!usb_ep_0_out_obuf_empty_read()) {
while (!usb_ep_0_out_obuf_empty_read()) {
obuf[byte_count++] = usb_ep_0_out_obuf_head_read();
usb_ep_0_out_obuf_head_write(0);
}
}
if (byte_count >= 2)
usb_ep0out_buffer_len[usb_ep0out_wr_ptr] = byte_count - 2 /* Strip off CRC16 */;
usb_ep0out_wr_ptr = (usb_ep0out_wr_ptr + 1) & (EP0OUT_BUFFERS-1);
if (last_tok == USB_PID_SETUP) {
usb_ep_0_in_dtb_write(1);
data_offset = 0;
current_length = 0;
current_data = NULL;
}
usb_ep_0_out_ev_pending_write(ep0o_pending);
usb_ep_0_out_respond_write(EPF_ACK);
}
// We just got an "IN" token. Send data if we have it.
if (ep0i_pending) {
usb_ep_0_in_respond_write(EPF_NAK);
usb_ep_0_in_ev_pending_write(ep0i_pending);
}
return;
}
void usb_ack_in(void) {
while (usb_ep_0_in_respond_read() == EPF_ACK)
;
usb_ep_0_in_respond_write(EPF_ACK);
}
void usb_ack_out(void) {
while (usb_ep_0_out_respond_read() == EPF_ACK)
;
usb_ep_0_out_respond_write(EPF_ACK);
}
void usb_err(void) {
usb_ep_0_out_respond_write(EPF_STALL);
usb_ep_0_in_respond_write(EPF_STALL);
}
int usb_recv(void *buffer, unsigned int buffer_len) {
// Set the OUT response to ACK, since we are in a position to receive data now.
usb_ep_0_out_respond_write(EPF_ACK);
while (1) {
if (usb_ep0out_rd_ptr != usb_ep0out_wr_ptr) {
if (usb_ep0out_last_tok[usb_ep0out_rd_ptr] == USB_PID_OUT) {
unsigned int ep0_buffer_len = usb_ep0out_buffer_len[usb_ep0out_rd_ptr];
if (ep0_buffer_len < buffer_len)
buffer_len = ep0_buffer_len;
// usb_ep0out_buffer_len[usb_ep0out_rd_ptr] = 0;
memcpy(buffer, (void *)&usb_ep0out_buffer[usb_ep0out_rd_ptr], buffer_len);
usb_ep0out_rd_ptr = (usb_ep0out_rd_ptr + 1) & (EP0OUT_BUFFERS-1);
return buffer_len;
}
usb_ep0out_rd_ptr = (usb_ep0out_rd_ptr + 1) & (EP0OUT_BUFFERS-1);
}
}
return 0;
}
void usb_poll(void) {
// If some data was received, then process it.
while (usb_ep0out_rd_ptr != usb_ep0out_wr_ptr) {
const struct usb_setup_request *request = (const struct usb_setup_request *)(usb_ep0out_buffer[usb_ep0out_rd_ptr]);
// uint8_t len = usb_ep0out_buffer_len[usb_ep0out_rd_ptr];
uint8_t last_tok = usb_ep0out_last_tok[usb_ep0out_rd_ptr];
// usb_ep0out_buffer_len[usb_ep0out_rd_ptr] = 0;
usb_ep0out_rd_ptr = (usb_ep0out_rd_ptr + 1) & (EP0OUT_BUFFERS-1);
if (last_tok == USB_PID_SETUP) {
usb_setup(request);
}
}
process_tx();
}
#endif /* CSR_USB_EP_0_OUT_EV_PENDING_ADDR */

114
sw/src/usb-setup.c Normal file
View File

@ -0,0 +1,114 @@
#include <stdint.h>
#include <unistd.h>
#include <usb.h>
#include <usb-desc.h>
static uint8_t reply_buffer[8];
static uint8_t usb_configuration = 0;
void usb_setup(const struct usb_setup_request *setup)
{
const uint8_t *data = NULL;
uint32_t datalen = 0;
const usb_descriptor_list_t *list;
switch (setup->wRequestAndType)
{
case 0x0500: // SET_ADDRESS
case 0x0b01: // SET_INTERFACE
break;
case 0x0900: // SET_CONFIGURATION
usb_configuration = setup->wValue;
break;
case 0x0880: // GET_CONFIGURATION
reply_buffer[0] = usb_configuration;
datalen = 1;
data = reply_buffer;
break;
case 0x0080: // GET_STATUS (device)
reply_buffer[0] = 0;
reply_buffer[1] = 0;
datalen = 2;
data = reply_buffer;
break;
case 0x0082: // GET_STATUS (endpoint)
if (setup->wIndex > 0)
{
usb_err();
return;
}
reply_buffer[0] = 0;
reply_buffer[1] = 0;
// XXX handle endpoint stall here
data = reply_buffer;
datalen = 2;
break;
case 0x0102: // CLEAR_FEATURE (endpoint)
if (setup->wIndex > 0 || setup->wValue != 0)
{
// TODO: do we need to handle IN vs OUT here?
usb_err();
return;
}
break;
case 0x0302: // SET_FEATURE (endpoint)
if (setup->wIndex > 0 || setup->wValue != 0)
{
// TODO: do we need to handle IN vs OUT here?
usb_err();
return;
}
// XXX: Should we set the stall bit?
// USB->DIEP0CTL |= USB_DIEP_CTL_STALL;
// TODO: do we need to clear the data toggle here?
break;
case 0x0680: // GET_DESCRIPTOR
case 0x0681:
for (list = usb_descriptor_list; 1; list++)
{
if (list->addr == NULL)
break;
if (setup->wValue == list->wValue)
{
data = list->addr;
if ((setup->wValue >> 8) == 3)
{
// for string descriptors, use the descriptor's
// length field, allowing runtime configured
// length.
datalen = *(list->addr);
}
else
{
datalen = list->length;
}
goto send;
}
}
usb_err();
return;
default:
usb_err();
return;
}
send:
if (data && datalen) {
if (datalen > setup->wLength)
datalen = setup->wLength;
usb_send(data, datalen);
}
else
usb_ack_in();
return;
}