Browse Source

projects/riscv_usb: Complete overhaul of the USB stack

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Sylvain Munaut 5 years ago
parent
commit
8cc8acfb36

+ 36 - 14
projects/riscv_usb/fw/Makefile

@@ -1,5 +1,5 @@
 BOARD ?= icebreaker
-CROSS = riscv-none-embed-
+CROSS ?= riscv-none-embed-
 CC = $(CROSS)gcc
 OBJCOPY = $(CROSS)objcopy
 ICEPROG = iceprog
@@ -7,31 +7,46 @@ ICEPROG = iceprog
 BOARD_DEFINE=BOARD_$(shell echo $(BOARD) | tr a-z\- A-Z_)
 CFLAGS=-Wall -Os -march=rv32i -mabi=ilp32 -ffreestanding -flto -nostartfiles -fomit-frame-pointer -Wl,--gc-section --specs=nano.specs -D$(BOARD_DEFINE)
 
-HEADERS=\
+
+HEADERS_common=\
 	config.h \
 	console.h \
 	led.h \
 	mini-printf.h \
 	spi.h \
+	usb_hw.h \
 	usb_priv.h \
-	usb_desc_data.h
+	usb_proto.h \
+	utils.h
 
-SOURCES=\
+SOURCES_common=\
 	start.S \
 	console.c \
-	firmware.c \
 	led.c \
 	mini-printf.c  \
 	spi.c \
 	usb.c \
-	usb_ep0.c \
-	usb_desc.c
+	usb_ctrl_ep0.c \
+	usb_ctrl_std.c \
+	utils.c
+
+HEADERS_app=\
+	usb_str_app.gen.h
+
+SOURCES_app=\
+	fw_app.c \
+	usb_desc_app.c
+
+
+all: boot_app.hex fw_app.bin
+
 
-firmware.elf: lnk-app.lds $(HEADERS) $(SOURCES)
-	$(CC) $(CFLAGS) -Wl,-Bstatic,-T,lnk-app.lds,--strip-debug -o $@ $(SOURCES)
+boot_app.elf: lnk-boot.lds boot.S
+	$(CC) $(CFLAGS) -Wl,-Bstatic,-T,lnk-boot.lds,--strip-debug -DFLASH_APP_ADDR=0x000a0000 -o $@ boot.S
+
+fw_app.elf: lnk-app.lds $(HEADERS_app) $(SOURCES_app) $(HEADERS_common) $(SOURCES_common)
+	$(CC) $(CFLAGS) -Wl,-Bstatic,-T,lnk-app.lds,--strip-debug -o $@ $(SOURCES_common) $(SOURCES_app)
 
-boot.elf: lnk-boot.lds boot.S
-	$(CC) $(CFLAGS) -Wl,-Bstatic,-T,lnk-boot.lds,--strip-debug -o $@ boot.S
 
 %.hex: %.bin
 	./bin2hex.py $< $@
@@ -39,8 +54,15 @@ boot.elf: lnk-boot.lds boot.S
 %.bin: %.elf
 	$(OBJCOPY) -O binary $< $@
 
-prog_fw: firmware.bin
-	$(ICEPROG) -o 1M $<
+usb_str_%.gen.h: usb_str_%.txt
+	./usb_gen_strings.py $< $@ $(BOARD)
+
+
+prog_app: fw_app.bin
+	$(ICEPROG) -o 640k $<
+
 
 clean:
-	rm -f *.bin *.hex *.elf
+	rm -f *.bin *.hex *.elf *.o *.gen.h
+
+.PHONY: prog_app clean

+ 36 - 33
projects/riscv_usb/fw/firmware.c

@@ -1,5 +1,5 @@
 /*
- * firmware.c
+ * fw_app.c
  *
  * Copyright (C) 2019 Sylvain Munaut
  * All rights reserved.
@@ -30,38 +30,39 @@
 #include "mini-printf.h"
 #include "spi.h"
 #include "usb.h"
+#include "utils.h"
 
 
-static char *
-hexstr(void *d, int n)
+extern const struct usb_stack_descriptors app_stack_desc;
+
+static void
+serial_no_init()
 {
-	static const char * const hex = "0123456789abcdef";
-	static char buf[96];
-	uint8_t *p = d;
-	char *s = buf;
-	char c;
-
-	while (n--) {
-		c = *p++;
-		*s++ = hex[c >> 4];
-		*s++ = hex[c & 0xf];
-		*s++ = ' ';
-	}
+	uint8_t buf[8];
+	char *id, *desc;
+	int i;
 
-	s[-1] = '\0';
+	flash_manuf_id(buf);
+	printf("Flash Manufacturer : %s\n", hexstr(buf, 3, true));
 
-	return buf;
+	flash_unique_id(buf);
+	printf("Flash Unique ID    : %s\n", hexstr(buf, 8, true));
+
+	/* Overwrite descriptor string */
+		/* In theory in rodata ... but nothing is ro here */
+	id = hexstr(buf, 8, false);
+	desc = (char*)app_stack_desc.str[1];
+	for (i=0; i<16; i++)
+		desc[2 + (i << 1)] = id[i];
 }
 
 void main()
 {
-	bool usb_active = false;
-	uint8_t buf[8];
 	int cmd = 0;
 
 	/* Init console IO */
 	console_init();
-	puts("Booting..\n");
+	puts("Booting App image..\n");
 
 	/* LED */
 	led_init();
@@ -73,34 +74,37 @@ void main()
 	/* SPI */
 	spi_init();
 
-	flash_manuf_id(buf);
-	puts("Flash Manuf ID  : "); puts(hexstr(buf, 3)); puts("\n");
-
-	flash_unique_id(buf);
-	puts("Flash Unique ID : "); puts(hexstr(buf, 8)); puts("\n");
+	/* Enable USB directly */
+	serial_no_init();
+	usb_init(&app_stack_desc);
 
 	/* Main loop */
 	while (1)
 	{
 		/* Prompt ? */
 		if (cmd >= 0)
-			puts("\nCommand> ");
+			printf("Command> ");
 
 		/* Poll for command */
 		cmd = getchar_nowait();
 
 		if (cmd >= 0) {
-			if (cmd > 32 && cmd < 127)
+			if (cmd > 32 && cmd < 127) {
 				putchar(cmd);
+				putchar('\r');
+				putchar('\n');
+			}
 
 			switch (cmd)
 			{
-			case 'd':
+			case 'p':
 				usb_debug_print();
 				break;
-			case 'u':
-				usb_active = true;
-				usb_init();
+			case 'c':
+				usb_connect();
+				break;
+			case 'd':
+				usb_disconnect();
 				break;
 			default:
 				break;
@@ -108,7 +112,6 @@ void main()
 		}
 
 		/* USB poll */
-		if (usb_active)
-			usb_poll();
+		usb_poll();
 	}
 }

+ 431 - 47
projects/riscv_usb/fw/usb.c

@@ -26,19 +26,26 @@
 #include <string.h>
 
 #include "console.h"
+#include "usb_hw.h"
 #include "usb_priv.h"
+#include "usb.h"
 
 
 /* Main stack state */
 struct usb_stack g_usb;
 
 
-/* Helpers for data access */
+/* Helpers */
+/* ------- */
+
+	/* Data buffer access */
+
 void
-usb_data_write(int dst_ofs, const void *src, int len)
+usb_data_write(unsigned int dst_ofs, const void *src, int len)
 {
+	/* FIXME unaligned ofs */
 	const uint32_t *src_u32 = src;
-	volatile uint32_t *dst_u32 = (volatile uint32_t *)((USB_DATA_BASE) + (dst_ofs << 2));
+	volatile uint32_t *dst_u32 = (volatile uint32_t *)((USB_DATA_BASE) + dst_ofs);
 
 	len = (len + 3) >> 2;
 	while (len--)
@@ -46,16 +53,17 @@ usb_data_write(int dst_ofs, const void *src, int len)
 }
 
 void
-usb_data_read (void *dst, int src_ofs, int len)
+usb_data_read (void *dst, unsigned int src_ofs, int len)
 {
-	volatile uint32_t *src_u32 = (volatile uint32_t *)((USB_DATA_BASE) + (src_ofs << 2));
+	/* FIXME unaligned ofs */
+	volatile uint32_t *src_u32 = (volatile uint32_t *)((USB_DATA_BASE) + src_ofs);
 	uint32_t *dst_u32 = dst;
 
 	int i = len >> 2;
 
 	while (i--)
 		*dst_u32++ = *src_u32++;
-	
+
 	if ((len &= 3) != 0) {
 		uint32_t x = *src_u32;
 		uint8_t  *dst_u8 = (uint8_t *)dst_u32;
@@ -67,36 +75,198 @@ usb_data_read (void *dst, int src_ofs, int len)
 }
 
 
-/* Debug */
-static const char *_hex = "0123456789abcdef";
+	/* Descriptors */
 
-static void
-_fast_print_04x(uint32_t v)
+const void *
+usb_desc_find(const void *sod, const void *eod, uint8_t dt)
 {
-	int i;
-	char str[5];
-	for (i=3; i>=0; i--) {
-		str[i] = _hex[v & 0xf];
-		v >>= 4;
+	const uint8_t *sod_p = sod, *eod_p = eod;
+	while ((eod_p - sod_p) >= 2) {
+		if (sod_p[1] == dt)
+			return sod_p;
+		sod_p += sod_p[0];
+	}
+	return NULL;
+}
+
+const void *
+usb_desc_next(const void *sod)
+{
+	const uint8_t *sod_p = sod;
+	return sod_p + sod_p[0];
+}
+
+const struct usb_conf_desc *
+usb_desc_find_conf(uint8_t cfg_value)
+{
+	for (int i=0; i<g_usb.stack_desc->n_conf; i++)
+		if (g_usb.stack_desc->conf[i]->bConfigurationValue == cfg_value)
+			return g_usb.stack_desc->conf[i];
+	return NULL;
+}
+
+const struct usb_intf_desc *
+usb_desc_find_intf(const struct usb_conf_desc *conf, uint8_t idx, uint8_t alt,
+                   const struct usb_intf_desc **alt0)
+{
+	const struct usb_intf_desc *intf = NULL;
+	const void *sod, *eod;
+
+	/* Config select */
+	if (!conf)
+		conf = g_usb.conf;
+	if (!conf)
+		return NULL;
+
+	/* Bound the search */
+	sod = conf;
+	eod = sod + conf->wTotalLength;
+
+	while (1) {
+		sod = usb_desc_find(sod, eod, USB_DT_INTF);
+		if (!sod)
+			break;
+
+		intf = (void*)sod;
+		if (intf->bInterfaceNumber == idx) {
+			if (alt0 && !intf->bAlternateSetting)
+				*alt0 = intf;
+			if (intf->bAlternateSetting == alt)
+				return intf;
+		}
+
+		sod = usb_desc_next(sod);
+	}
+
+	return NULL;
+}
+
+
+	/* Callback dispatching */
+
+void
+usb_dispatch_sof(void)
+{
+	struct usb_fn_drv *p = g_usb.fnd;
+
+	while (p) {
+		if (p->sof)
+			p->sof();
+		p = p->next;
+	}
+}
+
+void
+usb_dipatch_bus_reset(void)
+{
+	struct usb_fn_drv *p = g_usb.fnd;
+
+	while (p) {
+		if (p->bus_reset)
+			p->bus_reset();
+		p = p->next;
+	}
+}
+
+void
+usb_dispatch_state_chg(enum usb_dev_state state)
+{
+	struct usb_fn_drv *p = g_usb.fnd;
+
+	while (p) {
+		if (p->state_chg)
+			p->state_chg(state);
+		p = p->next;
 	}
-	str[4] = 0;
-	puts(str);
 }
 
+enum usb_fnd_resp
+usb_dispatch_ctrl_req(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	struct usb_fn_drv *p = g_usb.fnd;
+	enum usb_fnd_resp rv = USB_FND_CONTINUE;
+
+	while (p) {
+		if (p->ctrl_req) {
+			rv = p->ctrl_req(req, xfer);
+			if (rv != USB_FND_CONTINUE)
+				return rv;
+		}
+		p = p->next;
+	}
+
+	return rv;
+}
+
+enum usb_fnd_resp
+usb_dispatch_set_conf(const struct usb_conf_desc *desc)
+{
+	struct usb_fn_drv *p = g_usb.fnd;
+	enum usb_fnd_resp rv = USB_FND_SUCCESS;
+
+	while (p) {
+		if (p->set_conf) {
+			if (p->set_conf(desc) == USB_FND_ERROR)
+				rv = USB_FND_ERROR;
+		}
+		p = p->next;
+	}
+
+	return rv;
+}
+
+enum usb_fnd_resp
+usb_dispatch_set_intf(const struct usb_intf_desc *base, const struct usb_intf_desc *sel)
+{
+	struct usb_fn_drv *p = g_usb.fnd;
+	enum usb_fnd_resp rv = USB_FND_CONTINUE;
+
+	while (p) {
+		if (p->set_intf) {
+			rv = p->set_intf(base, sel);
+			if (rv != USB_FND_CONTINUE)
+				return rv;
+		}
+		p = p->next;
+	}
+
+	return rv;
+}
+
+enum usb_fnd_resp
+usb_dispatch_get_intf(const struct usb_intf_desc *base, uint8_t *sel)
+{
+	struct usb_fn_drv *p = g_usb.fnd;
+	enum usb_fnd_resp rv = USB_FND_CONTINUE;
+
+	while (p) {
+		if (p->get_intf) {
+			rv = p->get_intf(base, sel);
+			if (rv != USB_FND_CONTINUE)
+				return rv;
+		}
+		p = p->next;
+	}
+
+	return rv;
+}
+
+
+/* Debug */
+/* ----- */
+
 static void
 _fast_print_hex(uint32_t v)
 {
-	char str[12], *p = str;
+	const char _hex[] = "0123456789abcdef";
 	int i;
 
 	for (i=0; i<4; i++) {
-		*p++ = _hex[(v & 0xf0) >> 4];
-		*p++ = _hex[ v & 0x0f      ];
-		*p++ = ' ';
+		putchar(_hex[(v & 0xf0) >> 4]);
+		putchar(_hex[ v & 0x0f      ]);
+		putchar(' ');
 		v >>= 8;
 	}
-	str[11] = 0;
-	puts(str);
 }
 
 void
@@ -105,12 +275,12 @@ usb_debug_print_ep(int ep, int dir)
 	volatile struct usb_ep *ep_regs = dir ? &usb_ep_regs[ep].in : &usb_ep_regs[ep].out;
 
 	printf("EP%d %s", ep, dir ? "IN" : "OUT");
-	puts("\n\tS     "); _fast_print_04x(ep_regs->status);
-	puts("\n\tBD0.0 "); _fast_print_04x(ep_regs->bd[0].csr);
-	puts("\n\tBD0.1 "); _fast_print_04x(ep_regs->bd[0].ptr);
-	puts("\n\tBD1.0 "); _fast_print_04x(ep_regs->bd[1].csr);
-	puts("\n\tBD1.1 "); _fast_print_04x(ep_regs->bd[1].ptr);
-	puts("\n\n");
+	printf("\tS     %04x\n", ep_regs->status);
+	printf("\tBD0.0 %04x\n", ep_regs->bd[0].csr);
+	printf("\tBD0.1 %04x\n", ep_regs->bd[0].ptr);
+	printf("\tBD1.0 %04x\n", ep_regs->bd[1].csr);
+	printf("\tBD1.1 %04x\n", ep_regs->bd[1].ptr);
+	printf("\n");
 }
 
 void
@@ -129,47 +299,261 @@ usb_debug_print_data(int ofs, int len)
 void
 usb_debug_print(void)
 {
-	puts("\nCSR:");
-	puts("\n\tSR: "); _fast_print_04x(usb_regs->csr); 
-	puts("\n\n");
+	printf("Stack:\n");
+	printf("\tState: %d\n", g_usb.state);
+	printf("HW:\n");
+	printf("\tSR   : %04x\n", usb_regs->csr);
+	printf("\tTick : %04x\n", g_usb.tick);
+	printf("\n");
 
 	usb_debug_print_ep(0, 0);
 	usb_debug_print_ep(0, 1);
-	usb_debug_print_ep(1, 0);
-	usb_debug_print_ep(1, 1);
 
-	puts("\nData:\n");
+	printf("Data:\n");
 	usb_debug_print_data(0, 4);
 }
 
 
+/* Internal API */
+/* ------------ */
+
+static void
+_usb_hw_reset_ep(volatile struct usb_ep *ep)
+{
+	ep->status = 0;
+	ep->bd[0].csr = 0;
+	ep->bd[0].ptr = 0;
+	ep->bd[1].csr = 0;
+	ep->bd[1].ptr = 0;
+}
+
+static void
+_usb_hw_reset(bool pu)
+{
+	/* Clear all descriptors */
+	for (int i=0; i<16; i++) {
+		_usb_hw_reset_ep(&usb_ep_regs[i].out);
+		_usb_hw_reset_ep(&usb_ep_regs[i].in);
+	}
+
+	/* Main control */
+	usb_regs->csr = (pu ? USB_CSR_PU_ENA : 0) | USB_CSR_CEL_ENA | USB_CSR_ADDR_MATCH | USB_CSR_ADDR(0);
+	usb_regs->ar  = USB_AR_BUS_RST_CLEAR | USB_AR_SOF_CLEAR | USB_AR_CEL_RELEASE;
+}
+
+static void
+usb_bus_reset(void)
+{
+	/* Reset hw */
+	_usb_hw_reset(true);
+
+	/* Reset EP0 */
+	usb_ep0_reset();
+
+	/* Dispatch event */
+	usb_dipatch_bus_reset();
+
+	/* Set state */
+	usb_set_state(USB_DS_DEFAULT);
+}
+
+
 /* Exposed API */
+/* ----------- */
 
 void
-usb_init(void)
+usb_init(const struct usb_stack_descriptors *stack_desc)
 {
-	/* Main state init */
+	/* Main state reset */
 	memset(&g_usb, 0x00, sizeof(g_usb));
 
-	g_usb.ctrl.state = IDLE;
+	/* Stack setup */
+	g_usb.state = USB_DS_DISCONNECTED;
+	g_usb.stack_desc = stack_desc;
 
-	/* Initialize EP0 */
-	usb_ep0_init();
+	usb_register_function_driver(&usb_ctrl_std_drv);
 
-	/* Enable the core */
-	usb_regs->csr = USB_CSR_PU_ENA | USB_CSR_CEL_ENA;
+	/* Reset and enable the core */
+	_usb_hw_reset(false);
 }
 
 void
 usb_poll(void)
 {
-	uint32_t evt;
+	uint32_t csr;
+
+	/* Active ? */
+	if (g_usb.state < USB_DS_CONNECTED)
+		return;
+
+	/* Read CSR */
+	csr = usb_regs->csr;
+
+	/* Check for pending bus reset */
+	if (csr & USB_CSR_BUS_RST_PENDING) {
+		if (csr & USB_CSR_BUS_RST)
+			return;
+		usb_bus_reset();
+	}
+
+	/* If we've not been reset, only reset is of interest */
+	if (g_usb.state < USB_DS_DEFAULT)
+		return;
+
+	/* Supspend handling */
+	if (csr & USB_CSR_BUS_SUSPEND) {
+		if (!(g_usb.state & USB_DS_SUSPENDED)) {
+			usb_set_state(USB_DS_SUSPENDED);
+		}
+		return;
+	} else if (g_usb.state & USB_DS_SUSPENDED) {
+		usb_set_state(USB_DS_RESUME);
+	}
+
+	/* SOF Tick */
+	if (csr & USB_CSR_SOF_PENDING) {
+		g_usb.tick++;
+		usb_regs->ar = USB_AR_SOF_CLEAR;
+		usb_dispatch_sof();
+	}
 
 	/* Check for activity */
-	evt = usb_regs->evt;
-	if (!(evt & 0xf000))
+	if (!(csr & USB_CSR_EVT_PENDING))
+		return;
+	csr = usb_regs->evt;
+
+	/* Poll EP0 (control) */
+	usb_ep0_poll();
+}
+
+void
+usb_set_state(enum usb_dev_state new_state)
+{
+	/* Handle resume/suspend 'markers' */
+	if (new_state == USB_DS_RESUME)
+		new_state = g_usb.state & ~USB_DS_SUSPENDED;
+	else if (new_state == USB_DS_SUSPENDED)
+		new_state = g_usb.state | USB_DS_SUSPENDED;
+
+	/* If state is new, update */
+	if (g_usb.state != new_state) {
+		g_usb.state = new_state;
+		usb_dispatch_state_chg(usb_get_state());
+	}
+}
+
+enum usb_dev_state
+usb_get_state(void)
+{
+	return (g_usb.state & USB_DS_SUSPENDED) ? USB_DS_SUSPENDED : g_usb.state;
+}
+
+uint32_t
+usb_get_tick(void)
+{
+	return g_usb.tick;
+}
+
+void
+usb_connect(void)
+{
+	/* Sanity check */
+	if (g_usb.state != USB_DS_DISCONNECTED)
 		return;
 
-	/* Run EP0 (control) */
-	usb_ep0_run();
+	/* Turn-off pull-up */
+	usb_regs->csr |= USB_CSR_PU_ENA;
+
+	/* Stack update */
+	usb_set_state(USB_DS_CONNECTED);
+}
+
+void
+usb_disconnect(void)
+{
+	/* Sanity check */
+	if (g_usb.state < USB_DS_CONNECTED)
+		return;
+
+	/* Turn-off pull-up */
+	usb_regs->csr &= ~USB_CSR_PU_ENA;
+
+	/* Stack state */
+	usb_set_state(USB_DS_DISCONNECTED);
+}
+
+
+void
+usb_set_address(uint8_t addr)
+{
+	usb_regs->csr = USB_CSR_PU_ENA | USB_CSR_CEL_ENA | USB_CSR_ADDR_MATCH | USB_CSR_ADDR(addr);
+}
+
+
+void
+usb_register_function_driver(struct usb_fn_drv *drv)
+{
+	drv->next = g_usb.fnd;
+	g_usb.fnd = drv;
+}
+
+void
+usb_unregister_function_driver(struct usb_fn_drv *drv)
+{
+	struct usb_fn_drv **p = &g_usb.fnd;
+	while (*p) {
+		if (*p == drv) {
+			*p = drv->next;
+			drv->next = NULL;
+			break;
+		}
+		p = &(*p)->next->next;
+	}
+}
+
+
+static volatile struct usb_ep *
+_get_ep_regs(uint8_t ep)
+{
+	return (ep & 0x80) ?
+		&usb_ep_regs[ep & 0xf].in :
+		&usb_ep_regs[ep & 0xf].out;
+}
+
+bool
+usb_ep_is_configured(uint8_t ep)
+{
+	volatile struct usb_ep *epr = _get_ep_regs(ep);
+	uint32_t s = epr->status;
+	return USB_EP_TYPE(s) != USB_EP_TYPE_NONE;
+}
+
+bool
+usb_ep_is_halted(uint8_t ep)
+{
+	volatile struct usb_ep *epr = _get_ep_regs(ep);
+	uint32_t s = epr->status;
+	return USB_EP_TYPE_IS_BCI(s) && (s & USB_EP_TYPE_HALTED);
+}
+
+bool
+usb_ep_halt(uint8_t ep)
+{
+	volatile struct usb_ep *epr = _get_ep_regs(ep);
+	uint32_t s = epr->status;
+	if (!USB_EP_TYPE_IS_BCI(s))
+		return false;
+	epr->status = s | USB_EP_TYPE_HALTED;
+	return true;
+}
+
+bool
+usb_ep_resume(uint8_t ep)
+{
+	volatile struct usb_ep *epr = _get_ep_regs(ep);
+	uint32_t s = epr->status;
+	if (!USB_EP_TYPE_IS_BCI(s))
+		return false;
+	epr->status = s & ~(USB_EP_TYPE_HALTED | USB_EP_DT_BIT); /* DT bit clear needed by CLEAR_FEATURE */
+	return true;
 }

+ 103 - 1
projects/riscv_usb/fw/usb.h

@@ -23,9 +23,111 @@
 
 #pragma once
 
-void usb_init(void);
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "usb_proto.h"
+
+
+/* Types */
+/* ----- */
+
+struct usb_xfer;
+
+struct usb_stack_descriptors {
+	const struct usb_dev_desc *dev;
+	const struct usb_conf_desc * const *conf;
+	int n_conf;
+	const struct usb_str_desc * const *str;
+	int n_str;
+};
+
+enum usb_dev_state {
+	USB_DS_OFF		= 0,	/* Core is not initialize */
+	USB_DS_DISCONNECTED	= 1,	/* Core is not connected  */
+	USB_DS_CONNECTED	= 2,	/* Core is connected awaiting reset */
+	USB_DS_DEFAULT		= 3,
+	USB_DS_ADDRESS		= 4,
+	USB_DS_CONFIGURED	= 5,
+	USB_DS_SUSPENDED	= 0x80,	/* Bit marking suspend */
+	USB_DS_RESUME		= 0x81,	/* Special value for set_state */
+};
+
+
+enum usb_fnd_resp {
+        USB_FND_CONTINUE = 0,	/* Not handled, continue to next */
+        USB_FND_SUCCESS,	/* Handled: Success */
+        USB_FND_ERROR,		/* Handled: Error   */
+};
+
+typedef void (*usb_fnd_sof_cb)(void);
+typedef void (*usb_fnd_bus_reset_cb)(void);
+typedef void (*usb_fnd_state_chg_cb)(enum usb_dev_state state);
+typedef enum usb_fnd_resp (*usb_fnd_ctrl_req_cb)(struct usb_ctrl_req *req, struct usb_xfer *xfer);
+typedef enum usb_fnd_resp (*usb_fnd_set_conf_cb)(const struct usb_conf_desc *desc);
+typedef enum usb_fnd_resp (*usb_fnd_set_intf_cb)(const struct usb_intf_desc *base, const struct usb_intf_desc *sel);
+typedef enum usb_fnd_resp (*usb_fnd_get_intf_cb)(const struct usb_intf_desc *base, uint8_t *alt);
+
+struct usb_fn_drv {
+	struct usb_fn_drv *next;
+        usb_fnd_sof_cb		sof;
+        usb_fnd_bus_reset_cb	bus_reset;
+        usb_fnd_state_chg_cb	state_chg;
+        usb_fnd_ctrl_req_cb	ctrl_req;
+        usb_fnd_set_conf_cb	set_conf;
+        usb_fnd_set_intf_cb	set_intf;
+        usb_fnd_get_intf_cb	get_intf;
+};
+
+
+typedef bool (*usb_xfer_cb)(struct usb_xfer *xfer);
+
+struct usb_xfer {
+	/* Data buffer */
+	uint8_t *data;
+	int ofs;
+	int len;
+
+	/* Call backs */
+	usb_xfer_cb cb_data;	/* Data call back */
+	usb_xfer_cb cb_done;	/* Completion call back */
+	void *cb_ctx;
+};
+
+
+/* API */
+void usb_init(const struct usb_stack_descriptors *stack_desc);
 void usb_poll(void);
 
+void usb_set_state(enum usb_dev_state new_state);
+enum usb_dev_state usb_get_state(void);
+
+uint32_t usb_get_tick(void);
+
+void usb_connect(void);
+void usb_disconnect(void);
+
+void usb_set_address(uint8_t addr);
+
+void usb_register_function_driver(struct usb_fn_drv *drv);
+void usb_unregister_function_driver(struct usb_fn_drv *drv);
+
+
+	/* EP */
+bool usb_ep_is_configured(uint8_t ep);
+bool usb_ep_is_halted(uint8_t ep);
+bool usb_ep_halt(uint8_t ep);
+bool usb_ep_resume(uint8_t ep);
+
+	/* Descriptors */
+const void *usb_desc_find(const void *sod, const void *eod, uint8_t dt);
+const void *usb_desc_next(const void *sod);
+
+const struct usb_conf_desc *usb_desc_find_conf(uint8_t cfg_value);
+const struct usb_intf_desc *usb_desc_find_intf(const struct usb_conf_desc *conf, uint8_t idx, uint8_t alt,
+                                               const struct usb_intf_desc **base);
+
+
 /* Debug */
 void usb_debug_print_ep(int ep, int dir);
 void usb_debug_print_data(int ofs, int len);

+ 388 - 0
projects/riscv_usb/fw/usb_ctrl_ep0.c

@@ -0,0 +1,388 @@
+/*
+ * usb_ctrl_ep0.c
+ *
+ * Copyright (C) 2019 Sylvain Munaut
+ * All rights reserved.
+ *
+ * LGPL v3+, see LICENSE.lgpl3
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "console.h"
+#include "usb_hw.h"
+#include "usb_priv.h"
+
+#define EP0_PKT_LEN	64
+
+/* Helpers to manipulate BDs */
+
+	/* IN */
+static inline uint32_t
+usb_ep0_in_peek(void)
+{
+	return usb_ep_regs[0].in.bd[0].csr;
+}
+
+static inline void
+usb_ep0_in_clear(void)
+{
+	usb_ep_regs[0].in.bd[0].csr = 0;
+}
+
+static inline void
+usb_ep0_in_queue_data(unsigned int len)
+{
+	usb_ep_regs[0].in.bd[0].csr = USB_BD_STATE_RDY_DATA | USB_BD_LEN(len);
+}
+
+static inline void
+usb_ep0_in_queue_stall(void)
+{
+	usb_ep_regs[0].in.bd[0].csr = USB_BD_STATE_RDY_STALL;
+}
+
+	/* OUT */
+static inline uint32_t
+usb_ep0_out_peek(void)
+{
+	return usb_ep_regs[0].out.bd[0].csr;
+}
+
+static inline void
+usb_ep0_out_clear(void)
+{
+	usb_ep_regs[0].out.bd[0].csr = 0;
+}
+
+static inline void
+usb_ep0_out_queue_data(void)
+{
+	usb_ep_regs[0].out.bd[0].csr = USB_BD_STATE_RDY_DATA | USB_BD_LEN(EP0_PKT_LEN);
+}
+
+static inline void
+usb_ep0_out_queue_stall(void)
+{
+	usb_ep_regs[0].out.bd[0].csr = USB_BD_STATE_RDY_STALL;
+}
+
+	/* SETUP */
+static inline uint32_t
+usb_ep0_setup_peek(void)
+{
+	return usb_ep_regs[0].out.bd[1].csr;
+}
+
+static inline void
+usb_ep0_setup_clear(void)
+{
+	usb_ep_regs[0].out.bd[1].csr = 0;
+}
+
+static inline void
+usb_ep0_setup_queue_data(void)
+{
+	usb_ep_regs[0].out.bd[1].csr = USB_BD_STATE_RDY_DATA | USB_BD_LEN(EP0_PKT_LEN);
+}
+
+
+/* Handle control transfers */
+
+static void
+usb_handle_control_data()
+{
+	/* Handle read requests */
+	if (g_usb.ctrl.state == DATA_IN) {
+		/* How much left to do ? */
+		int xflen = g_usb.ctrl.xfer.len - g_usb.ctrl.xfer.ofs;
+		if (xflen > EP0_PKT_LEN)
+			xflen = EP0_PKT_LEN;
+
+		/* Setup descriptor for output */
+		if (xflen)
+			usb_data_write(0, &g_usb.ctrl.xfer.data[g_usb.ctrl.xfer.ofs], xflen);
+		usb_ep0_in_queue_data(xflen);
+
+		/* Move on */
+		g_usb.ctrl.xfer.ofs += xflen;
+
+		/* If we're done, setup the OUT ack */
+		if (xflen < EP0_PKT_LEN) {
+			usb_ep0_out_queue_data();
+			g_usb.ctrl.state = STATUS_DONE_OUT;
+		}
+	}
+
+	/* Handle write requests */
+	if (g_usb.ctrl.state == DATA_OUT) {
+		/* Read off any data we got */
+		uint32_t bds_out = usb_ep0_out_peek();
+
+		if ((bds_out & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_OK)
+		{
+			/* Read data from USB buffer */
+			int xflen = (bds_out & USB_BD_LEN_MSK) - 2;
+			usb_data_read(&g_usb.ctrl.xfer.data[g_usb.ctrl.xfer.ofs], 0, xflen);
+
+			/* Move on */
+			g_usb.ctrl.xfer.ofs += xflen;
+
+			/* Done with that buffer */
+			usb_ep0_out_clear();
+		}
+
+		/* Next ? */
+		if (g_usb.ctrl.xfer.ofs == g_usb.ctrl.xfer.len)
+		{
+			/* Done, ACK with a ZLP */
+			usb_ep0_in_queue_data(0);
+			g_usb.ctrl.state = STATUS_DONE_IN;
+		}
+		else if ((bds_out & USB_BD_STATE_MSK) != USB_BD_STATE_RDY_DATA)
+		{
+			/* Submit next BD to fill */
+			usb_ep0_out_queue_data();
+		}
+	}
+}
+
+static void
+usb_handle_control_request(struct usb_ctrl_req *req)
+{
+	enum usb_fnd_resp rv = USB_FND_CONTINUE;
+
+	/* Defaults */
+	g_usb.ctrl.xfer.data = g_usb.ctrl.buf;
+	g_usb.ctrl.xfer.len  = sizeof(g_usb.ctrl.buf);
+	g_usb.ctrl.xfer.ofs     = 0;
+	g_usb.ctrl.xfer.cb_data = NULL;
+	g_usb.ctrl.xfer.cb_done = NULL;
+	g_usb.ctrl.xfer.cb_ctx  = NULL;
+
+	/* Dipatch to all handlers */
+	rv = usb_dispatch_ctrl_req(req, &g_usb.ctrl.xfer);
+
+	/* If the request isn't handled, answer with STALL */
+	if (rv != USB_FND_SUCCESS)
+		goto error;
+
+	/* Buffer size vs request size checks */
+	if (req->wLength > g_usb.ctrl.xfer.len) {
+		if (!USB_REQ_IS_READ(req)) {
+			/* If this is a OUT treansaction and no suitable buffer was
+			 * provided, there isn't much we can do ... */
+			USB_LOG_ERR("[!] Control request handler failed to provide enough buffer space");
+			goto error;
+		}
+	} else {
+		g_usb.ctrl.xfer.len = req->wLength;
+	}
+
+	/* Handle the 'data' stage now */
+	g_usb.ctrl.state = USB_REQ_IS_READ(req) ? DATA_IN : DATA_OUT;
+	usb_handle_control_data();
+
+	return;
+
+	/* Error path */
+error:
+	g_usb.ctrl.state = STALL;
+	usb_ep0_in_queue_stall();
+	usb_ep0_out_queue_stall();
+	return;
+}
+
+
+/* Internally exposed "API" */
+
+void
+usb_ep0_reset(void)
+{
+	/* Reset internal state */
+	g_usb.ctrl.state = IDLE;
+
+	/* Configure EP0 */
+	usb_ep_regs[0].out.status = USB_EP_TYPE_CTRL | USB_EP_BD_CTRL; /* Type=Control, control mode buffered */
+	usb_ep_regs[0].in.status  = USB_EP_TYPE_CTRL | USB_EP_DT_BIT;  /* Type=Control, single buffered, DT=1 */
+
+	/* Setup the BD pointers */
+	usb_ep_regs[0].in.bd[0].ptr  = 0;
+	usb_ep_regs[0].out.bd[0].ptr = 0;
+	usb_ep_regs[0].out.bd[1].ptr = EP0_PKT_LEN;
+
+	/* Clear BD for IN/OUT */
+	usb_ep0_in_clear();
+	usb_ep0_out_clear();
+
+	/* Queue one buffer for SETUP */
+	usb_ep0_setup_queue_data();
+}
+
+void
+usb_ep0_poll(void)
+{
+	uint32_t bds_setup, bds_out, bds_in;
+	bool acted;
+
+	do {
+		/* Not done anything yet */
+		acted = false;
+
+		/* Grab current EP status */
+		bds_setup = usb_ep0_setup_peek();
+		bds_out   = usb_ep0_out_peek();
+		bds_in    = usb_ep0_in_peek();
+
+		/* Check for status IN stage finishing */
+		if (g_usb.ctrl.state == STATUS_DONE_IN) {
+			if ((bds_in & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_OK) {
+				/* Return to IDLE */
+				g_usb.ctrl.state = IDLE;
+				usb_ep0_in_clear();
+
+				/* Completion Callback */
+				if (g_usb.ctrl.xfer.cb_done)
+					g_usb.ctrl.xfer.cb_done(&g_usb.ctrl.xfer);
+
+				/* Next event */
+				acted = true;
+			}
+		}
+
+		/* Check for status OUT stage finishing */
+		else if (g_usb.ctrl.state == STATUS_DONE_OUT) {
+			if ((bds_in & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_OK) {
+				/* Done with the last IN BD of this transfer */
+				usb_ep0_in_clear();
+
+				/* Next event */
+				acted = true;
+			}
+			if ((bds_out & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_OK) {
+				/* Sanity check */
+				if ((bds_out & USB_BD_LEN_MSK) != 2)
+					USB_LOG_ERR("[!] Got a non ZLP as a status stage packet ?!?\n");
+
+				/* Return to IDLE */
+				g_usb.ctrl.state = IDLE;
+				usb_ep0_out_clear();
+
+				/* Completion Callback */
+				if (g_usb.ctrl.xfer.cb_done)
+					g_usb.ctrl.xfer.cb_done(&g_usb.ctrl.xfer);
+
+				/* Next event */
+				acted = true;
+			}
+		}
+
+		/* Check for STALL needing a refresh */
+		else if (g_usb.ctrl.state == STALL) {
+			if ((bds_in & USB_BD_STATE_MSK) != USB_BD_STATE_RDY_STALL) {
+				usb_ep0_in_queue_stall();
+				acted = true;
+			}
+			if ((bds_out & USB_BD_STATE_MSK) != USB_BD_STATE_RDY_STALL) {
+				usb_ep0_out_queue_stall();
+				acted = true;
+			}
+		}
+
+		/* If any of the above was acted upon, we need a refresh */
+		if (acted)
+			continue;
+
+		/* Retry any RX error on both setup and data buffers */
+		if ((bds_setup & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_ERR) {
+			USB_LOG_ERR("[!] Retry SETUP error\n");
+			usb_ep0_setup_queue_data();
+			acted = true;
+			continue;
+		}
+
+		if ((bds_out & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_ERR) {
+			USB_LOG_ERR("[!] Retry OUT error\n");
+			usb_ep0_out_queue_data();
+			acted = true;
+			continue;
+		}
+
+		/* Check for SETUP */
+		if ((bds_setup & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_OK) {
+			/* Really setup ? */
+			if (!(bds_setup & USB_BD_IS_SETUP)) {
+				USB_LOG_ERR("[!] Got non-SETUP in the SETUP BD !?!\n");
+			}
+
+			/* Were we waiting for this ? */
+			if ((g_usb.ctrl.state != IDLE) && (g_usb.ctrl.state != STALL)) {
+				USB_LOG_ERR("[!] Got SETUP while busy !??\n");
+			}
+
+			/* Clear descriptors */
+			usb_ep0_out_clear();
+			usb_ep0_in_clear();
+
+			/* Make sure DT=1 for IN endpoint after a SETUP */
+			usb_ep_regs[0].in.status = USB_EP_TYPE_CTRL | USB_EP_DT_BIT;  /* Type=Control, single buffered, DT=1 */
+
+			/* We acked it, need to handle it */
+			usb_data_read(&g_usb.ctrl.req, EP0_PKT_LEN, sizeof(struct usb_ctrl_req));
+			usb_handle_control_request(&g_usb.ctrl.req);
+
+			/* Release the lockout and allow new SETUP */
+			usb_regs->ar = USB_AR_CEL_RELEASE;
+			usb_ep0_setup_queue_data();
+
+			return;
+		}
+
+		/* Process data stage */
+		if (((bds_out & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_OK)) {
+			/* Sanity check */
+			if (g_usb.ctrl.state != DATA_OUT) {
+				USB_LOG_ERR("[!] Got unexpected DATA !?!\n");
+				usb_ep0_out_clear();
+			} else {
+				/* Process data */
+				usb_handle_control_data();
+			}
+
+			/* Next event */
+			acted = true;
+			continue;
+		}
+
+		if ((bds_in & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_OK) {
+			/* Sanity check */
+			if (g_usb.ctrl.state != DATA_IN) {
+				USB_LOG_ERR("[!] Got ack for DATA we didn't send !?!\n");
+				usb_ep0_in_clear();
+			} else {
+				/* Process data */
+				usb_handle_control_data();
+			}
+
+			/* Next event */
+			acted = true;
+			continue;
+		}
+	} while (acted);
+}

+ 360 - 0
projects/riscv_usb/fw/usb_ctrl_std.c

@@ -0,0 +1,360 @@
+/*
+ * usb_ctrl_std.c
+ *
+ * Copyright (C) 2019 Sylvain Munaut
+ * All rights reserved.
+ *
+ * LGPL v3+, see LICENSE.lgpl3
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include "console.h"
+#include "usb_hw.h"
+#include "usb_priv.h"
+
+
+	/* Control Request implementation */
+
+static bool
+_get_status_dev(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	xfer->data[0] = 0x00;	/* No remote wakeup, bus-powered */
+	xfer->data[1] = 0x00;
+	xfer->len = 2;
+	return true;
+}
+
+static bool
+_get_status_intf(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	/* Check interface exits */
+	if (usb_desc_find_intf(NULL, req->wIndex, 0, NULL))
+		return false;
+
+	/* Nothing to return really */
+	xfer->data[0] = 0x00;
+	xfer->data[1] = 0x00;
+	xfer->len = 2;
+	return true;
+}
+
+static bool
+_get_status_ep(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	uint8_t ep = req->wIndex;
+
+	if (!usb_ep_is_configured(ep))
+		return false;
+
+	xfer->data[0] = usb_ep_is_halted(ep) ? 0x01 : 0x00;
+	xfer->data[1] = 0x00;
+	xfer->len = 2;
+	return true;
+}
+
+static bool
+_clear_feature_dev(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	/* No support for any device feature */
+	return false;
+}
+
+static bool
+_clear_feature_intf(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	/* No support for any interface feature */
+	return false;
+}
+
+static bool
+_clear_feature_ep(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	uint8_t ep = req->wIndex;
+
+	/* Only support ENDPOINT_HALT feature on non-zero EP that exist
+	 * and only when in CONFIGURED state */
+	if ((usb_get_state() < USB_DS_CONFIGURED) ||
+	    (req->wValue != 0) ||	/* ENDPOINT_HALT */
+	    (ep == 0) ||
+	    (!usb_ep_is_configured(ep)))
+		return false;
+
+	/* Resume the EP */
+	return usb_ep_resume(ep);
+}
+
+static bool
+_set_feature_dev(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	/* No support for any device feature */
+	return false;
+}
+
+static bool
+_set_feature_intf(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	/* No support for any interface feature */
+	return false;
+}
+
+static bool
+_set_feature_ep(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	uint8_t ep = req->wIndex;
+
+	/* Only support ENDPOINT_HALT feature on non-zero EP that exist
+	 * and only when in CONFIGURED state */
+	if ((usb_get_state() < USB_DS_CONFIGURED) ||
+	    (req->wValue != 0) ||	/* ENDPOINT_HALT */
+	    (ep == 0) ||
+	    (!usb_ep_is_configured(ep)))
+		return false;
+
+	/* Halt the EP */
+	return usb_ep_halt(ep);
+}
+
+static bool
+_set_addr_done(struct usb_xfer *xfer)
+{
+	struct usb_ctrl_req *req = xfer->cb_ctx;
+	usb_set_address(req->wValue);
+	return true;
+}
+
+static bool
+_set_address(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	xfer->len = 0;
+	xfer->cb_done = _set_addr_done;
+	xfer->cb_ctx = req;
+	return true;
+}
+
+static bool
+_get_descriptor(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	int idx = req->wValue & 0xff;
+
+	xfer->data = NULL;
+
+	switch (req->wValue & 0xff00)
+	{
+	case 0x0100:	/* Device */
+		xfer->data = (void*)g_usb.stack_desc->dev;
+		xfer->len  = g_usb.stack_desc->dev->bLength;
+		break;
+
+	case 0x0200:	/* Configuration */
+		if (idx < g_usb.stack_desc->n_conf) {
+			xfer->data = (void*)g_usb.stack_desc->conf[idx];
+			xfer->len  = g_usb.stack_desc->conf[idx]->wTotalLength;
+		}
+		break;
+
+	case 0x0300:	/* String */
+		if (idx < g_usb.stack_desc->n_str) {
+			xfer->data = (void*)g_usb.stack_desc->str[idx];
+			xfer->len  = g_usb.stack_desc->str[idx]->bLength;
+		}
+		break;
+	}
+
+	return xfer->data != NULL;
+}
+
+static bool
+_get_configuration(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	xfer->data[0] = g_usb.conf ? g_usb.conf->bConfigurationValue : 0;
+	xfer->len = 1;
+	return true;
+}
+
+static bool
+_set_configuration(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	const struct usb_conf_desc *conf = NULL;
+	enum usb_dev_state new_state;
+
+	/* Handle the 'zero' case first */
+	if (req->wValue == 0) {
+		new_state = USB_DS_DEFAULT;
+	} else {
+		/* Find the requested config */
+		for (int i=0; i<g_usb.stack_desc->n_conf; i++)
+			if (g_usb.stack_desc->conf[i]->bConfigurationValue == req->wValue) {
+				conf = g_usb.stack_desc->conf[i];
+				break;
+			}
+
+		if (!conf)
+			return false;
+
+		new_state = USB_DS_CONFIGURED;
+	}
+
+	/* Update state */
+		/* FIXME: configure all endpoint */
+	g_usb.conf = conf;
+	g_usb.intf_alt = 0;
+	usb_set_state(new_state);
+	usb_dispatch_set_conf(g_usb.conf);
+
+	return true;
+}
+
+static bool
+_get_interface(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	const struct usb_intf_desc *intf;
+	uint8_t idx = req->wIndex;
+	uint8_t alt = req->wValue;
+	enum usb_fnd_resp rv;
+
+	/* Check interface exits */
+	if (usb_desc_find_intf(NULL, idx, 0, NULL))
+	if (intf == NULL)
+		return false;
+
+	/* Fast path */
+	if (!(g_usb.intf_alt & (1 << idx))) {
+		xfer->data[0] = 0x00;
+		xfer->len = 1;
+		return true;
+	}
+
+	/* Dispatch for an answer */
+	rv = usb_dispatch_get_intf(intf, &alt);
+	if (rv != USB_FND_SUCCESS)
+		return false;
+
+	/* Setup response */
+	xfer->data[0] = alt;
+	xfer->len = 1;
+
+	return true;
+}
+
+static bool
+_set_interface(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	const struct usb_intf_desc *intf_base, *intf_alt;
+	uint8_t idx = req->wIndex;
+	uint8_t alt = req->wValue;
+	enum usb_fnd_resp rv;
+
+	/* Check interface exits and its altsettings */
+	intf_alt = usb_desc_find_intf(NULL, req->wIndex, alt, &intf_base);
+	if (intf_alt == NULL)
+		return false;
+
+	/* Disable fast path */
+	g_usb.intf_alt |= (1 << idx);
+
+	/* Dispatch enable */
+	rv = usb_dispatch_set_intf(intf_base, intf_alt);
+	if (rv != USB_FND_SUCCESS)
+		return false;
+
+	return true;
+}
+
+
+	/* Control Request dispatch */
+
+static enum usb_fnd_resp
+usb_ctrl_std_handle(struct usb_ctrl_req *req, struct usb_xfer *xfer)
+{
+	bool rv = false;
+
+	/* Main dispatch */
+	switch (req->wRequestAndType)
+	{
+	case USB_RT_GET_STATUS_DEV:
+		rv = _get_status_dev(req, xfer);
+		break;
+
+	case USB_RT_GET_STATUS_INTF:
+		rv = _get_status_intf(req, xfer);
+		break;
+
+	case USB_RT_GET_STATUS_EP:
+		rv = _get_status_ep(req, xfer);
+		break;
+
+	case USB_RT_CLEAR_FEATURE_DEV:
+		rv = _clear_feature_dev(req, xfer);
+		break;
+
+	case USB_RT_CLEAR_FEATURE_INTF:
+		rv = _clear_feature_intf(req, xfer);
+		break;
+
+	case USB_RT_CLEAR_FEATURE_EP:
+		rv = _clear_feature_ep(req, xfer);
+		break;
+
+	case USB_RT_SET_FEATURE_DEV:
+		rv = _set_feature_dev(req, xfer);
+		break;
+
+	case USB_RT_SET_FEATURE_INTF:
+		rv = _set_feature_intf(req, xfer);
+		break;
+
+	case USB_RT_SET_FEATURE_EP:
+		rv = _set_feature_ep(req, xfer);
+		break;
+
+	case USB_RT_SET_ADDRESS:
+		rv = _set_address(req, xfer);
+		break;
+
+	case USB_RT_GET_DESCRIPTOR:
+		rv = _get_descriptor(req, xfer);
+		break;
+
+	case USB_RT_GET_CONFIGURATION:
+		rv = _get_configuration(req, xfer);
+		break;
+
+	case USB_RT_SET_CONFIGURATION:
+		rv = _set_configuration(req, xfer);
+		break;
+
+	case USB_RT_GET_INTERFACE:
+		rv = _get_interface(req, xfer);
+		break;
+
+	case USB_RT_SET_INTERFACE:
+		rv = _set_interface(req, xfer);
+		break;
+
+	default:
+		return USB_FND_CONTINUE;
+	}
+
+	return rv ? USB_FND_SUCCESS : USB_FND_ERROR;
+}
+
+struct usb_fn_drv usb_ctrl_std_drv = {
+	.ctrl_req = usb_ctrl_std_handle,
+};

+ 183 - 0
projects/riscv_usb/fw/usb_desc_app.c

@@ -0,0 +1,183 @@
+/*
+ * usb_desc_app.c
+ *
+ * Copyright (C) 2019 Sylvain Munaut
+ * All rights reserved.
+ *
+ * LGPL v3+, see LICENSE.lgpl3
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "usb_proto.h"
+#include "usb.h"
+
+#define NULL ((void*)0)
+#define num_elem(a) (sizeof(a) / sizeof(a[0]))
+
+
+static const struct {
+	/* Configuration */
+	struct usb_conf_desc conf;
+
+	/* CDC */
+	struct {
+		struct usb_intf_desc intf_ctl;
+		struct usb_cs_intf_hdr_desc cs_intf_hdr;
+		struct usb_cs_intf_acm_desc cs_intf_acm;
+		struct usb_cs_intf_union_desc cs_intf_union;
+		uint8_t cs_intf_union_slave;
+		struct usb_ep_desc ep_ctl;
+		struct usb_intf_desc intf_data;
+		struct usb_ep_desc ep_data_out;
+		struct usb_ep_desc ep_data_in;
+	} __attribute__ ((packed)) cdc;
+
+	/* DFU Runtime */
+	struct {
+		struct usb_intf_desc intf;
+		struct usb_dfu_desc func;
+	} __attribute__ ((packed)) dfu;
+} __attribute__ ((packed)) _app_conf_desc = {
+	.conf = {
+		.bLength                = sizeof(struct usb_conf_desc),
+		.bDescriptorType        = USB_DT_CONF,
+		.wTotalLength           = sizeof(_app_conf_desc),
+		.bNumInterfaces         = 3,
+		.bConfigurationValue    = 1,
+		.iConfiguration         = 4,
+		.bmAttributes           = 0x80,
+		.bMaxPower              = 0x32, /* 100 mA */
+	},
+	.cdc = {
+		.intf_ctl = {
+			.bLength		= sizeof(struct usb_intf_desc),
+			.bDescriptorType	= USB_DT_INTF,
+			.bInterfaceNumber	= 0,
+			.bAlternateSetting	= 0,
+			.bNumEndpoints		= 1,
+			.bInterfaceClass	= 0x02,
+			.bInterfaceSubClass	= 0x02,
+			.bInterfaceProtocol	= 0x00,
+			.iInterface		= 5,
+		},
+		.cs_intf_hdr = {
+			.bLength		= sizeof(struct usb_cs_intf_hdr_desc),
+			.bDescriptorType	= USB_DT_CS_INTF,
+			.bDescriptorsubtype	= 0x00,
+			.bcdCDC			= 0x0110,
+		},
+		.cs_intf_acm = {
+			.bLength		= sizeof(struct usb_cs_intf_acm_desc),
+			.bDescriptorType	= USB_DT_CS_INTF,
+			.bDescriptorsubtype	= 0x02,
+			.bmCapabilities		= 0x02,
+		},
+		.cs_intf_union = {
+			.bLength		= sizeof(struct usb_cs_intf_union_desc) + 1,
+			.bDescriptorType	= USB_DT_CS_INTF,
+			.bDescriptorsubtype	= 0x06,
+			.bMasterInterface	= 0,
+		},
+		.cs_intf_union_slave = 1,
+		.ep_ctl = {
+			.bLength		= sizeof(struct usb_ep_desc),
+			.bDescriptorType	= USB_DT_EP,
+			.bEndpointAddress	= 0x84,
+			.bmAttributes		= 0x03,
+			.wMaxPacketSize		= 64,
+			.bInterval		= 0x40,
+		},
+		.intf_data = {
+			.bLength		= sizeof(struct usb_intf_desc),
+			.bDescriptorType	= USB_DT_INTF,
+			.bInterfaceNumber	= 1,
+			.bAlternateSetting	= 0,
+			.bNumEndpoints		= 2,
+			.bInterfaceClass	= 0x0a,
+			.bInterfaceSubClass	= 0x00,
+			.bInterfaceProtocol	= 0x00,
+			.iInterface		= 6,
+		},
+		.ep_data_out = {
+			.bLength		= sizeof(struct usb_ep_desc),
+			.bDescriptorType	= USB_DT_EP,
+			.bEndpointAddress	= 0x05,
+			.bmAttributes		= 0x02,
+			.wMaxPacketSize		= 64,
+			.bInterval		= 0x00,
+		},
+		.ep_data_in = {
+			.bLength		= sizeof(struct usb_ep_desc),
+			.bDescriptorType	= USB_DT_EP,
+			.bEndpointAddress	= 0x85,
+			.bmAttributes		= 0x02,
+			.wMaxPacketSize		= 64,
+			.bInterval		= 0x00,
+		},
+	},
+	.dfu = {
+		.intf = {
+			.bLength		= sizeof(struct usb_intf_desc),
+			.bDescriptorType	= USB_DT_INTF,
+			.bInterfaceNumber	= 2,
+			.bAlternateSetting	= 0,
+			.bNumEndpoints		= 0,
+			.bInterfaceClass	= 0xfe,
+			.bInterfaceSubClass	= 0x01,
+			.bInterfaceProtocol	= 0x01,
+			.iInterface		= 7,
+		},
+		.func = {
+			.bLength		= sizeof(struct usb_dfu_desc),
+			.bDescriptorType	= USB_DT_DFU,
+			.bmAttributes		= 0x0d,
+			.wDetachTimeOut		= 1000,
+			.wTransferSize		= 4096,
+			.bcdDFUVersion		= 0x0101,
+		},
+	},
+};
+
+static const struct usb_conf_desc * const _conf_desc_array[] = {
+	&_app_conf_desc.conf,
+};
+
+static const struct usb_dev_desc _dev_desc = {
+	.bLength		= sizeof(struct usb_dev_desc),
+	.bDescriptorType	= USB_DT_DEV,
+	.bcdUSB			= 0x0200,
+	.bDeviceClass		= 0,
+	.bDeviceSubClass	= 0,
+	.bDeviceProtocol	= 0,
+	.bMaxPacketSize0	= 64,
+	.idVendor		= 0x1d50,
+	.idProduct		= 0x6147,
+	.bcdDevice		= 0x0001,	/* v0.1 */
+	.iManufacturer		= 2,
+	.iProduct		= 3,
+	.iSerialNumber		= 1,
+	.bNumConfigurations	= num_elem(_conf_desc_array),
+};
+
+#include "usb_str_app.gen.h"
+
+const struct usb_stack_descriptors app_stack_desc = {
+	.dev = &_dev_desc,
+	.conf = _conf_desc_array,
+	.n_conf = num_elem(_conf_desc_array),
+	.str = _str_desc_array,
+	.n_str = num_elem(_str_desc_array),
+};

+ 0 - 126
projects/riscv_usb/fw/usb_desc_data.h

@@ -1,126 +0,0 @@
-
-/***************************************************************************
-   AUTOMATICALLY GENERATED by USBDescriptorKitchen (0.3)
-   When editing, make sure you keep the indentation right
-   and use an editor that doesn't mess around with the linefeeds.
-
-   See http://github.com/zonque/USBDescriptorKitchen/
-   for more information about the tool that was used to generate this file.
- ***************************************************************************/
-static const char Device_1[] = {
-  /* Device*/
-  0x12,                       /* bLength (Size of this descriptor in bytes) */
-  0x01,                       /* bDescriptorType (DEVICE Descriptor Type) */
-  0x00, 0x02,                 /* bcdUSB (USB Specification Release Number in Binary-Coded Decimal) ("2.0") (512) */
-  0x00,                       /* bDeviceClass (Class code (assigned by the USB-IF).) ("Defined at interface level") */
-  0x00,                       /* bDeviceSubClass (Subclass code (assigned by the USB-IF).) */
-  0x00,                       /* bDeviceProtocol (Protocol code (assigned by the USB-IF).) */
-  0x40,                       /* bMaxPacketSize0 (Maximum packet size for endpoint zero) ("64") */
-  0x50, 0x1d,                 /* idVendor (Vendor ID (assigned by the USB-IF)) (7504) */
-  0xe1, 0xe1,                 /* idProduct (Product ID (assigned by the manufacturer)) (57825) */
-  0x00, 0x01,                 /* bcdDevice (Device release number in binary-coded decimal) (256) */
-  0x01,                       /* iManufacturer */
-  0x02,                       /* iProduct */
-  0x03,                       /* iSerialNumber */
-  0x01,                       /* bNumConfigurations (Number of possible configurations) */
-};
-
-static const char Configuration_1[] = {
-  /* Configuration*/
-  0x09,                       /* bLength (Size of this descriptor in bytes) */
-  0x02,                       /* bDescriptorType (CONFIGURATION Descriptor Type) */
-  0x27, 0x00,                 /* wTotalLength (Total length of data returned for this configuration) (39) */
-  0x01,                       /* bNumInterfaces (Number of interfaces supported by this configuration) */
-  0x01,                       /* bConfigurationValue (Value to use as an argument to the SetConfiguration() request to select this configuration) */
-  0x04,                       /* iConfiguration (Index of string descriptor describing this configuration) */
-  0x80,                       /* bmAttributes (Configuration characteristics) ('Remote Wakeup' = 0, 'Self-powered' = 0, 'Reserved (set to one)' = 1) */
-  0x32,                       /* bMaxPower (Maximum power consumption of the USB, device. Expressed in 2 mA units) */
-    /* Interface*/
-    0x09,                     /* bLength (Size of this descriptor in bytes) */
-    0x04,                     /* bDescriptorType (INTERFACE Descriptor Type) */
-    0x00,                     /* bInterfaceNumber (Number of this interface. Zero-based value.) */
-    0x00,                     /* bAlternateSetting (Value used to select this alternate setting) */
-    0x03,                     /* bNumEndpoints (Number of endpoints used by this interface) */
-    0xff,                     /* bInterfaceClass (Class code (assigned by the USB-IF).) ("Vendor specific") */
-    0x00,                     /* bInterfaceSubClass (Subclass code (assigned by the USB-IF).) ("0") */
-    0x00,                     /* bInterfaceProtocol (Protocol code (assigned by the USB).) */
-    0x02,                     /* iInterface (Index of string descriptor describing this interface) */
-      /* Endpoint*/
-      0x07,                   /* bLength (Size of this descriptor in bytes) */
-      0x05,                   /* bDescriptorType (ENDPOINT descriptor) */
-      0x01,                   /* bEndpointAddress ('Endpoint Number' = 1, 'Direction' = 0) */
-      0x05,                   /* bEndpointAttributes ('Transfer Type' = 1, 'Synchronization Type' = 1, 'Usage Type' = 0) */
-      0xb0, 0x01,             /* wMaxPacketSize (Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected.) (432) */
-      0x01,                   /* bInterval (Interval for polling endpoint for data transfers. Expressed in frames or microframes depending on the device operating speed (i.e., either 1 millisecond or 125 us units)) */
-      /* Endpoint*/
-      0x07,                   /* bLength (Size of this descriptor in bytes) */
-      0x05,                   /* bDescriptorType (ENDPOINT descriptor) */
-      0x81,                   /* bEndpointAddress ('Endpoint Number' = 1, 'Direction' = 1) */
-      0x11,                   /* bEndpointAttributes ('Transfer Type' = 1, 'Synchronization Type' = 0, 'Usage Type' = 1) */
-      0x08, 0x00,             /* wMaxPacketSize (Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected.) (8) */
-      0x03,                   /* bInterval (Interval for polling endpoint for data transfers. Expressed in frames or microframes depending on the device operating speed (i.e., either 1 millisecond or 125 us units)) */
-      /* Endpoint*/
-      0x07,                   /* bLength (Size of this descriptor in bytes) */
-      0x05,                   /* bDescriptorType (ENDPOINT descriptor) */
-      0x82,                   /* bEndpointAddress ('Endpoint Number' = 2, 'Direction' = 1) */
-      0x05,                   /* bEndpointAttributes ('Transfer Type' = 1, 'Synchronization Type' = 1, 'Usage Type' = 0) */
-      0xb0, 0x01,             /* wMaxPacketSize (Maximum packet size this endpoint is capable of sending or receiving when this configuration is selected.) (432) */
-      0x01,                   /* bInterval (Interval for polling endpoint for data transfers. Expressed in frames or microframes depending on the device operating speed (i.e., either 1 millisecond or 125 us units)) */
-};
-
-static const char StringZero_1[] = {
-  /* StringZero*/
-  0x04,                       /* bLength (Size of this descriptor in bytes) */
-  0x03,                       /* bDescriptorType (STRING Descriptor Type) */
-  0x09, 0x04,                 /* wLANGID(0) ("English (United States)") (1033) */
-};
-
-static const char String_1[] = {
-  /* String*/
-  0x10,                       /* bLength (Size of this descriptor in bytes) */
-  0x03,                       /* bDescriptorType (STRING Descriptor Type) */
-  0x6f, 0x00, 0x73, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, /* bString (0) */
-};
-
-static const char String_2[] = {
-  /* String*/
-  0x22,                       /* bLength (Size of this descriptor in bytes) */
-  0x03,                       /* bDescriptorType (STRING Descriptor Type) */
-  0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x45, 0x00, 0x31, 0x00, 0x20, 0x00, 0x69, 0x00, 
-  0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x66, 0x00, 0x61, 0x00, 0x63, 0x00, 0x65, 0x00, 
-   /* bString (0) */
-};
-
-static const char String_3[] = {
-  /* String*/
-  0x12,                       /* bLength (Size of this descriptor in bytes) */
-  0x03,                       /* bDescriptorType (STRING Descriptor Type) */
-  0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 
-   /* bString (0) */
-};
-
-static const char String_4[] = {
-  /* String*/
-  0x0a,                       /* bLength (Size of this descriptor in bytes) */
-  0x03,                       /* bDescriptorType (STRING Descriptor Type) */
-  0x4d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, /* bString ("Main") (0) */
-};
-
-static const char *Devices[] = {
-	Device_1,
-};
-
-static const char *Configurations[] = {
-	Configuration_1,
-};
-
-static const char *Strings[] = {
-	String_1,
-	String_2,
-	String_3,
-	String_4,
-};
-
-static const char *StringZeros[] = {
-	StringZero_1,
-};

+ 0 - 333
projects/riscv_usb/fw/usb_ep0.c

@@ -1,333 +0,0 @@
-/*
- * usb_ep0.c
- *
- * Copyright (C) 2019 Sylvain Munaut
- * All rights reserved.
- *
- * LGPL v3+, see LICENSE.lgpl3
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-
-#include "console.h"
-#include "usb_priv.h"
-
-
-/* Helpers to manipulate BDs */
-
-static inline void
-usb_ep0_out_queue_bd(bool setup, int ofs, int len, bool stall)
-{
-	int bdi = setup ? 1 : 0;
-	usb_ep_regs[0].out.bd[bdi].ptr = ofs;
-	usb_ep_regs[0].out.bd[bdi].csr = stall ? USB_BD_STATE_RDY_STALL : (USB_BD_STATE_RDY_DATA | USB_BD_LEN(len));
-}
-
-static inline void
-usb_ep0_in_queue_bd(int ofs, int len, bool stall)
-{
-	usb_ep_regs[0].in.bd[0].ptr = ofs;
-	usb_ep_regs[0].in.bd[0].csr = stall ? USB_BD_STATE_RDY_STALL : (USB_BD_STATE_RDY_DATA | USB_BD_LEN(len));
-}
-
-static inline uint32_t
-usb_ep0_out_peek_bd(bool setup)
-{
-	int bdi = setup ? 1 : 0;
-	return usb_ep_regs[0].out.bd[bdi].csr;
-}
-
-static inline uint32_t
-usb_ep0_in_peek_bd(void)
-{
-	return usb_ep_regs[0].in.bd[0].csr;
-}
-
-static inline void
-usb_ep0_out_done_bd(bool setup)
-{
-	int bdi = setup ? 1 : 0;
-	usb_ep_regs[0].out.bd[bdi].csr = 0;
-}
-
-static inline void
-usb_ep0_in_done_bd(void)
-{
-	usb_ep_regs[0].in.bd[0].csr = 0;
-}
-
-
-/* Standard control request handling */
-
-
-
-/* Handle control transfers */
-
-static void
-usb_handle_control_data()
-{
-	/* Handle read requests */
-	if (g_usb.ctrl.state == DATA_IN) {
-		/* How much left to do ? */
-		int xflen = g_usb.ctrl.len - g_usb.ctrl.ofs;
-		if (xflen > 64)
-			xflen = 64;
-
-		/* Setup descriptor for output */
-		if (xflen)
-			usb_data_write(0, &g_usb.ctrl.data.in[g_usb.ctrl.ofs], xflen);
-		usb_ep0_in_queue_bd(0, xflen, false);
-
-		/* Move on */
-		g_usb.ctrl.ofs += xflen;
-
-		/* If we're done, setup the OUT ack */
-		if (xflen < 64) {
-			usb_ep0_out_queue_bd(false, 0, 0, false);
-			g_usb.ctrl.state = STATUS_DONE_OUT;
-		}
-	}
-
-	/* Handle write requests */
-	if (g_usb.ctrl.state == DATA_OUT) {
-		if (g_usb.ctrl.ofs == g_usb.ctrl.len)
-		{
-			/* Done, ACK with a ZLP */
-			usb_ep0_in_queue_bd(0, 0, false);
-			g_usb.ctrl.state = STATUS_DONE_IN;
-		}
-		else
-		{
-			/* Fill a BD with as much as we can */
-
-		}
-	}
-}
-
-static void
-usb_handle_control_request(struct usb_ctrl_req_hdr *req)
-{
-	bool handled = false;
-
-	/* Defaults */
-	g_usb.ctrl.data.in  = NULL;
-	g_usb.ctrl.data.out = NULL;
-	g_usb.ctrl.len  = req->wLength;
-	g_usb.ctrl.ofs  = 0;
-
-	/* Process request */
-	switch (req->bRequest)
-	{
-	case USB_REQ_GET_STATUS:
-	case USB_REQ_CLEAR_FEATURE:
-	case USB_REQ_SET_FEATURE:
-		break;
-
-	case USB_REQ_SET_ADDRESS:
-		handled = true;
-		break;
-
-	case USB_REQ_GET_DESCRIPTOR:
-	{
-		int idx = req->wValue & 0xff;
-
-		switch (req->wValue & 0xff00)
-		{
-		case 0x0100:	/* Device */
-			g_usb.ctrl.data.out = usb_get_device_desc(&g_usb.ctrl.len);
-			break;
-
-		case 0x0200:	/* Configuration */
-			g_usb.ctrl.data.out = usb_get_config_desc(&g_usb.ctrl.len, idx);
-			break;
-
-		case 0x0300:	/* String */
-			g_usb.ctrl.data.out = usb_get_string_desc(&g_usb.ctrl.len, idx);
-			break;
-		}
-
-		handled = g_usb.ctrl.data.out != NULL;
-		break;
-	}
-
-	case USB_REQ_SET_DESCRIPTOR:
-	case USB_REQ_GET_CONFIGURATION:
-		break;
-
-	case USB_REQ_SET_CONFIGURATION:
-		handled = true;
-		break;
-
-	case USB_REQ_GET_INTERFACE:
-	case USB_REQ_SET_INTERFACE:
-	case USB_REQ_SYNCHFRAME:
-	default:
-		break;
-	}
-
-	/* If the request isn't handled, answer with STALL */
-	if (!handled) {
-		if (USB_REQ_IS_READ(req)) {
-			/* Read request, send a STALL for the DATA IN stage */
-			g_usb.ctrl.state = STATUS_DONE_IN;
-			usb_ep0_in_queue_bd(0, 0, true);
-		} else if (req->wLength) {
-			/* Write request with some incoming data, send a STALL to next OUT */
-			g_usb.ctrl.state = STATUS_DONE_OUT;
-			usb_ep0_out_queue_bd(false, 0,  0, true);
-		} else {
-			/* Write request with no data, send a STALL in the STATUS IN stage */
-			g_usb.ctrl.state = STATUS_DONE_IN;
-			usb_ep0_in_queue_bd(0, 0, true);
-		}
-
-		return;
-	}
-
-	/* Handle the 'data' stage now */
-	g_usb.ctrl.state = USB_REQ_IS_READ(req) ? DATA_IN : DATA_OUT;
-
-	if (g_usb.ctrl.len > req->wLength)
-		g_usb.ctrl.len = req->wLength;
-
-	usb_handle_control_data();
-}
-
-
-/* Internally exposed "API" */
-
-void
-usb_ep0_run(void)
-{
-	uint32_t bds_setup, bds_out, bds_in;
-	bool acted;
-
-	do {
-		/* Not done anything yet */
-		acted = false;
-
-		/* Grab current EP status */
-		bds_out   = usb_ep0_out_peek_bd(false);
-		bds_setup = usb_ep0_out_peek_bd(true);
-		bds_in    = usb_ep0_in_peek_bd();
-
-		/* Check for status IN stage finishing */
-		if (g_usb.ctrl.state == STATUS_DONE_IN) {
-			if ((bds_in & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_OK)
-			{
-				g_usb.ctrl.state = IDLE;
-				usb_ep0_in_done_bd();
-				acted = true;
-				continue;
-			}
-		}
-
-		/* Check for status OUT stage finishing */
-		if (g_usb.ctrl.state == STATUS_DONE_OUT) {
-			if ((bds_out & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_OK)
-			{
-				if ((bds_out & USB_BD_LEN_MSK) == 2) {
-					g_usb.ctrl.state = IDLE;
-					usb_ep0_out_done_bd(false);
-					acted = true;
-					continue;
-				} else {
-					puts("[!] Got a non ZLP as a status stage packet ?!?\n");
-				}
-			}
-		}
-
-		/* Retry any RX error on both setup and data buffers */
-		if ((bds_setup & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_ERR)
-		{
-			usb_ep0_out_queue_bd(true, 0, 64, false);
-			acted = true;
-			continue;
-		}
-
-		if ((bds_out & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_ERR)
-		{
-			usb_ep0_out_queue_bd(false, 64, 64, false);
-			acted = true;
-			continue;
-		}
-
-		/* Check for SETUP */
-		if ((bds_setup & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_OK)
-		{
-			/* Really setup ? */
-			if (!(bds_setup & USB_BD_IS_SETUP)) {
-				puts("[!] Got non-SETUP in the SETUP BD !?!\n");
-			}
-
-			/* Were we waiting for this ? */
-			if (g_usb.ctrl.state != IDLE) {
-				puts("[!] Got SETUP while busy !??\n");
-			}
-
-			/* Clear descriptors */
-			usb_ep0_out_done_bd(false);
-			usb_ep0_in_done_bd();
-
-			/* Make sure DT=1 for IN endpoint after a SETUP */
-			usb_ep_regs[0].in.status = USB_EP_TYPE_CTRL | USB_EP_DT_BIT;  /* Type=Control, single buffered, DT=1 */
-
-			/* We acked it, need to handle it */
-			usb_data_read(&g_usb.ctrl.req, 0, sizeof(struct usb_ctrl_req_hdr));
-			usb_handle_control_request(&g_usb.ctrl.req);
-
-			/* Release the lockout and allow new SETUP */
-			usb_regs->ar = USB_AR_CEL_RELEASE;
-			usb_ep0_out_queue_bd(true, 0, 64, false);
-
-			return;
-		}
-
-		/* Process data stage */
-		if (((bds_out & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_OK)) {
-			usb_ep0_out_done_bd(false);
-			if (g_usb.ctrl.state != DATA_OUT) {
-				puts("[!] Got unexpected DATA !?!\n");
-				continue;
-			}
-			usb_handle_control_data();
-			acted = true;
-		}
-
-		if ((bds_in & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_OK) {
-			usb_ep0_in_done_bd();
-			if (g_usb.ctrl.state == DATA_IN) {
-				usb_handle_control_data();
-				acted = true;
-			}
-		}
-
-	} while (acted);
-}
-
-void
-usb_ep0_init(void)
-{
-	/* Configure EP0 */
-	usb_ep_regs[0].out.status = USB_EP_TYPE_CTRL | USB_EP_BD_CTRL; /* Type=Control, control mode buffered */
-	usb_ep_regs[0].in.status  = USB_EP_TYPE_CTRL | USB_EP_DT_BIT;  /* Type=Control, single buffered, DT=1 */
-
-	/* Queue one buffer for SETUP */
-	usb_ep0_out_queue_bd(true, 0, 64, false);
-}

+ 55 - 0
projects/riscv_usb/fw/usb_gen_strings.py

@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+
+import json
+import sys
+
+def main(argv0, fn_in, fn_out, board=''):
+
+	with open(fn_in, 'r') as fh_in, open(fn_out, 'w') as fh_out:
+		# Arrays
+		str_d = []
+
+		# String 0
+		str_d.append("""static const struct usb_str_desc _str0_desc = {
+	.bLength		= 4,
+	.bDescriptorType	= USB_DT_STR,
+	.wString		= { 0x0409 },
+};
+""")
+
+		# String 1..n
+		def sep(i, l):
+			if i == l-1:
+				return ''
+			elif ((i & 7) == 7):
+				return '\n\t\t'
+			else:
+				return ' '
+
+		for i, ld in enumerate(fh_in.readlines()):
+			ld = ld.strip()
+			if ld.startswith('!{'):
+				ld = json.loads(ld[1:])
+				ld = ld[board] if board in ld else ld['']
+			ll = len(ld)
+			d = ''.join(['0x%04x,%s' % (ord(c), sep(j, ll)) for j,c in enumerate(ld)])
+			str_d.append("""static const struct usb_str_desc _str%d_desc = {
+	.bLength		= %d,
+	.bDescriptorType	= USB_DT_STR,
+	.wString		= {
+		%s
+	},
+};
+""" % (i+1, ll*2+2, d))
+
+		fh_out.write('\n'.join(str_d))
+
+		# Array
+		fh_out.write("\n")
+		fh_out.write("static const struct usb_str_desc * const _str_desc_array[] = {\n")
+		for i in range(len(str_d)):
+			fh_out.write("\t& _str%d_desc,\n" % i)
+		fh_out.write("};\n")
+
+if __name__ == '__main__':
+	main(*sys.argv)

+ 94 - 0
projects/riscv_usb/fw/usb_hw.h

@@ -0,0 +1,94 @@
+/*
+ * usb_hw.h
+ *
+ * Copyright (C) 2019 Sylvain Munaut
+ * All rights reserved.
+ *
+ * LGPL v3+, see LICENSE.lgpl3
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include "config.h"
+
+
+struct usb_core {
+	uint32_t csr;
+	uint32_t ar;
+	uint32_t evt;
+} __attribute__((packed,aligned(4)));
+
+#define USB_CSR_PU_ENA		(1 << 15)
+#define USB_CSR_EVT_PENDING	(1 << 14)
+#define USB_CSR_CEL_ACTIVE	(1 << 13)
+#define USB_CSR_CEL_ENA		(1 << 12)
+#define USB_CSR_BUS_SUSPEND	(1 << 11)
+#define USB_CSR_BUS_RST		(1 << 10)
+#define USB_CSR_BUS_RST_PENDING	(1 <<  9)
+#define USB_CSR_SOF_PENDING	(1 <<  8)
+#define USB_CSR_ADDR_MATCH	(1 <<  7)
+#define USB_CSR_ADDR(x)		((x) & 0x7f)
+
+#define USB_AR_CEL_RELEASE	(1 << 13)
+#define USB_AR_BUS_RST_CLEAR	(1 <<  9)
+#define USB_AR_SOF_CLEAR	(1 <<  8)
+
+
+struct usb_ep {
+	uint32_t status;
+	uint32_t _rsvd[3];
+	struct {
+		uint32_t csr;
+		uint32_t ptr;
+	} bd[2];
+} __attribute__((packed,aligned(4)));
+
+struct usb_ep_pair {
+	struct usb_ep out;
+	struct usb_ep in;
+} __attribute__((packed,aligned(4)));
+
+#define USB_EP_TYPE_NONE	0x0000
+#define USB_EP_TYPE_ISOC	0x0001
+#define USB_EP_TYPE_INT		0x0002
+#define USB_EP_TYPE_BULK	0x0004
+#define USB_EP_TYPE_CTRL	0x0006
+#define USB_EP_TYPE_HALTED	0x0001
+#define USB_EP_TYPE_IS_BCI(x)	(((x) & 6) != 0)
+#define USB_EP_TYPE(x)		((x) & 6)
+
+#define USB_EP_DT_BIT		0x0080
+#define USB_EP_BD_IDX		0x0040
+#define USB_EP_BD_CTRL		0x0020
+#define USB_EP_BD_DUAL		0x0010
+
+#define USB_BD_STATE_MSK	0xe000
+#define USB_BD_STATE_NONE	0x0000
+#define USB_BD_STATE_RDY_DATA	0x4000
+#define USB_BD_STATE_RDY_STALL	0x6000
+#define USB_BD_STATE_DONE_OK	0x8000
+#define USB_BD_STATE_DONE_ERR	0xa000
+#define USB_BD_IS_SETUP		0x1000
+
+#define USB_BD_LEN(l)		((l) & 0x3ff)
+#define USB_BD_LEN_MSK		0x03ff
+
+
+static volatile struct usb_core *    const usb_regs    = (void*) (USB_CORE_BASE);
+static volatile struct usb_ep_pair * const usb_ep_regs = (void*)((USB_CORE_BASE) + (1 << 13));

+ 50 - 104
projects/riscv_usb/fw/usb_priv.h

@@ -25,133 +25,79 @@
 
 #include <stdint.h>
 
-#include "config.h"
+#include "usb.h"
+#include "usb_proto.h"
 
 
-/* Hardware registers */
-/* ------------------ */
-
-struct usb_core {
-	uint32_t csr;
-	uint32_t ar;
-	uint32_t evt;
-} __attribute__((packed,aligned(4)));
-
-#define USB_CSR_PU_ENA		(1 << 15)
-#define USB_CSR_CEL_ENA		(1 << 12)
-#define USB_CSR_CEL_ACTIVE	(1 << 13)
-#define USB_AR_CEL_RELEASE	(1 << 13)
-
-
-struct usb_ep {
-	uint32_t status;
-	uint32_t _rsvd[3];
-	struct {
-		uint32_t csr;
-		uint32_t ptr;
-	} bd[2];
-} __attribute__((packed,aligned(4)));
-
-struct usb_ep_pair {
-	struct usb_ep out;
-	struct usb_ep in;
-} __attribute__((packed,aligned(4)));
-
-#define USB_EP_TYPE_NONE	0x0000
-#define USB_EP_TYPE_ISOC	0x0001
-#define USB_EP_TYPE_INT		0x0002
-#define USB_EP_TYPE_BULK	0x0004
-#define USB_EP_TYPE_CTRL	0x0006
-#define USB_EP_TYPE_HALTED	0x0001
-
-#define USB_EP_DT_BIT		0x0080
-#define USB_EP_BD_IDX		0x0040
-#define USB_EP_BD_CTRL		0x0020
-#define USB_EP_BD_DUAL		0x0010
-
-#define USB_BD_STATE_MSK	0xe000
-#define USB_BD_STATE_NONE	0x0000
-#define USB_BD_STATE_RDY_DATA	0x4000
-#define USB_BD_STATE_RDY_STALL	0x6000
-#define USB_BD_STATE_DONE_OK	0x8000
-#define USB_BD_STATE_DONE_ERR	0xa000
-#define USB_BD_IS_SETUP		0x1000
-
-#define USB_BD_LEN(l)		((l) & 0x3ff)
-#define USB_BD_LEN_MSK		0x03ff
-
+/* Debug logging */
+/* ------------- */
 
-static volatile struct usb_core *    const usb_regs    = (void*) (USB_CORE_BASE);
-static volatile struct usb_ep_pair * const usb_ep_regs = (void*)((USB_CORE_BASE) + (1 << 13));
+#define USB_LOG_LEVEL 1
 
+#ifdef USB_LOG_LEVEL
+# define USB_LOG(lvl,...)	if ((lvl) >= USB_LOG_LEVEL) printf(__VA_ARGS__)
+#else
+# define USB_LOG(lvl,...)	do {} while(0)
+#endif
 
+#define USB_LOG_ERR(...)	USB_LOG(1, __VA_ARGS__)
 
-/* USB protocol */
-/* ------------ */
 
-struct usb_ctrl_req_hdr {
-	uint8_t  bmRequestType;
-	uint8_t  bRequest;
-	uint16_t wValue;
-	uint16_t wIndex;
-	uint16_t wLength;
-} __attribute__((packed));
-
-#define USB_REQ_IS_READ(req)	(  req->bmRequestType & 0x80 )
-#define USB_REQ_IS_WRITE(req)	(!(req->bmRequestType & 0x80))
+/* Internal functions */
+/* ------------------ */
 
-#define USB_REQ_GET_STATUS		0
-#define USB_REQ_CLEAR_FEATURE		1
-#define USB_REQ_SET_FEATURE		3
-#define USB_REQ_SET_ADDRESS		5
-#define USB_REQ_GET_DESCRIPTOR		6
-#define USB_REQ_SET_DESCRIPTOR		7
-#define USB_REQ_GET_CONFIGURATION	8
-#define USB_REQ_SET_CONFIGURATION	9
-#define USB_REQ_GET_INTERFACE		10
-#define USB_REQ_SET_INTERFACE		11
-#define USB_REQ_SYNCHFRAME		12
+/* Stack */
+struct usb_stack {
+	/* Driver config */
+	const struct usb_stack_descriptors *stack_desc;
 
+	/* Device state */
+	enum usb_dev_state state;
 
+	const struct usb_conf_desc *conf;
+	uint32_t intf_alt;
 
-/* Internal functions */
-/* ------------------ */
+	/* Timebase */
+	uint32_t tick;
 
-/* Internal state */
-struct usb_stack {
+	/* EP0 control state */
 	struct {
 		enum {
 			IDLE,
-			DATA_IN,		/* Data stage via 'IN'  */
-			DATA_OUT,		/* Data stage via 'OUT' */
-			STATUS_DONE_OUT,	/* Status sent via 'OUT' EP */
-			STATUS_DONE_IN,		/* Status sent via 'IN' EP */
+			DATA_IN,		/* Data stage via 'IN'        */
+			DATA_OUT,		/* Data stage via 'OUT'       */
+			STATUS_DONE_OUT,	/* Status sent via 'OUT' EP   */
+			STATUS_DONE_IN,		/* Status sent via 'IN' EP    */
+			STALL,			/* Stalled until next `SETUP` */
 		} state;
 
-		struct usb_ctrl_req_hdr req;
-
-		union {
-			const uint8_t *out;
-			uint8_t *in;
-		} data;
+		uint8_t buf[64];
 
-		int len;
-		int ofs;
+		struct usb_xfer xfer;
+		struct usb_ctrl_req req;
 	} ctrl;
+
+	/* Function drivers */
+	struct usb_fn_drv *fnd;
 };
 
 extern struct usb_stack g_usb;
 
-
 /* Helpers for data access */
-void usb_data_write(int dst_ofs, const void *src, int len);
-void usb_data_read(void *dst, int src_ofs, int len);
+void usb_data_write(unsigned int dst_ofs, const void *src, int len);
+void usb_data_read(void *dst, unsigned int src_ofs, int len);
+
+
+void usb_dispatch_sof(void);
+void usb_dipatch_bus_reset(void);
+void usb_dispatch_state_chg(enum usb_dev_state state);
+enum usb_fnd_resp usb_dispatch_ctrl_req(struct usb_ctrl_req *req, struct usb_xfer *xfer);
+enum usb_fnd_resp usb_dispatch_set_conf(const struct usb_conf_desc *desc);
+enum usb_fnd_resp usb_dispatch_set_intf(const struct usb_intf_desc *base, const struct usb_intf_desc *sel);
+enum usb_fnd_resp usb_dispatch_get_intf(const struct usb_intf_desc *base, uint8_t *sel);
 
-/* Descriptors retrieval */
-const void *usb_get_device_desc(int *len);
-const void *usb_get_config_desc(int *len, int idx);
-const void *usb_get_string_desc(int *len, int idx);
+/* Control */
+void usb_ep0_reset(void);
+void usb_ep0_poll(void);
 
-/* EndPoint 0 */
-void usb_ep0_run(void);
-void usb_ep0_init(void);
+extern struct usb_fn_drv usb_ctrl_std_drv;

+ 213 - 0
projects/riscv_usb/fw/usb_proto.h

@@ -0,0 +1,213 @@
+/*
+ * usb_proto.h
+ *
+ * Copyright (C) 2019 Sylvain Munaut
+ * All rights reserved.
+ *
+ * LGPL v3+, see LICENSE.lgpl3
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+
+// Descriptors
+// -----------
+
+enum usb_desc_type {
+	USB_DT_DEV		= 1,
+	USB_DT_CONF		= 2,
+	USB_DT_STR		= 3,
+	USB_DT_INTF		= 4,
+	USB_DT_EP		= 5,
+	USB_DT_DEV_QUAL		= 6,
+	USB_DT_OTHER_SPEED_CONF	= 7,
+	USB_DT_INTF_PWR		= 8,
+	USB_DT_OTG		= 9,
+	USB_DT_DEBUG		= 10,
+	USB_DT_INTF_ASSOC	= 11,
+	USB_DT_DFU		= 33,
+	USB_DT_CS_INTF		= 36,
+	USB_DT_CS_EP		= 37,
+};
+
+struct usb_desc_hdr {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+} __attribute__((packed));
+
+struct usb_dev_desc {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint16_t bcdUSB;
+	uint8_t  bDeviceClass;
+	uint8_t  bDeviceSubClass;
+	uint8_t  bDeviceProtocol;
+	uint8_t  bMaxPacketSize0;
+	uint16_t idVendor;
+	uint16_t idProduct;
+	uint16_t bcdDevice;
+	uint8_t  iManufacturer;
+	uint8_t  iProduct;
+	uint8_t  iSerialNumber;
+	uint8_t  bNumConfigurations;
+} __attribute__((packed));
+
+struct usb_conf_desc {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint16_t wTotalLength;
+	uint8_t  bNumInterfaces;
+	uint8_t  bConfigurationValue;
+	uint8_t  iConfiguration;
+	uint8_t  bmAttributes;
+	uint8_t  bMaxPower;
+} __attribute__((packed));
+
+struct usb_intf_desc {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint8_t  bInterfaceNumber;
+	uint8_t  bAlternateSetting;
+	uint8_t  bNumEndpoints;
+	uint8_t  bInterfaceClass;
+	uint8_t  bInterfaceSubClass;
+	uint8_t  bInterfaceProtocol;
+	uint8_t  iInterface;
+} __attribute__((packed));
+
+struct usb_ep_desc {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint8_t  bEndpointAddress;
+	uint8_t  bmAttributes;
+	uint16_t wMaxPacketSize;
+	uint8_t  bInterval;
+} __attribute__((packed));
+
+struct usb_intf_assoc_desc {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint8_t  bFirstInterface;
+	uint8_t  bInterfaceCount;
+	uint8_t  bFunctionClass;
+	uint8_t  bFunctionSubClass;
+	uint8_t  bFunctionProtocol;
+	uint8_t  iFunction;
+} __attribute__((packed));
+
+struct usb_str_desc {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint16_t wString[];
+} __attribute__((packed));
+
+struct usb_dfu_desc {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint8_t  bmAttributes;
+	uint16_t wDetachTimeOut;
+	uint16_t wTransferSize;
+	uint16_t bcdDFUVersion;
+} __attribute__((packed));
+
+struct usb_cs_intf_hdr_desc {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint8_t  bDescriptorsubtype;
+	uint16_t bcdCDC;
+} __attribute__((packed));
+
+struct usb_cs_intf_acm_desc {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint8_t  bDescriptorsubtype;
+	uint8_t  bmCapabilities;
+} __attribute__((packed));
+
+struct usb_cs_intf_union_desc {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint8_t  bDescriptorsubtype;
+	uint8_t  bMasterInterface;
+	/* uint8_t  bSlaveInterface[]; */
+} __attribute__((packed));
+
+struct usb_cs_intf_call_mgmt_desc {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint8_t  bDescriptorsubtype;
+	uint8_t  bmCapabilities;
+	uint8_t  bDataInterface;
+} __attribute__((packed));
+
+
+// Control requests
+// ----------------
+
+struct usb_ctrl_req {
+	union {
+		struct {
+			uint8_t  bmRequestType;
+			uint8_t  bRequest;
+		};
+		uint16_t wRequestAndType;
+	};
+	uint16_t wValue;
+	uint16_t wIndex;
+	uint16_t wLength;
+} __attribute__((packed,aligned(4)));
+
+#define USB_REQ_RCPT_MSK	(0x1f)
+#define USB_REQ_RCPT(req)	((req)->bmRequestType & USB_REQ_RCPT_MSK)
+#define USB_REQ_RCPT_DEV	(0 << 0)
+#define USB_REQ_RCPT_INTF	(1 << 0)
+#define USB_REQ_RCPT_EP		(2 << 0)
+#define USB_REQ_RCPT_OTHER	(3 << 0)
+
+#define USB_REQ_TYPE_MSK	(0x60)
+#define USB_REQ_TYPE(req)	((req)->bmRequestType & USB_REQ_TYPE_MSK)
+#define USB_REQ_TYPE_STD	(0 << 5)
+#define USB_REQ_TYPE_CLASS	(1 << 5)
+#define USB_REQ_TYPE_VENDOR	(2 << 5)
+#define USB_REQ_TYPE_RSVD	(3 << 5)
+
+#define USB_REQ_TYPE_RCPT(req)	((req)->bmRequestType & (USB_REQ_RCPT_MSK | USB_REQ_TYPE_MSK))
+
+#define USB_REQ_READ		(1 << 7)
+#define USB_REQ_IS_READ(req)	(  (req)->bmRequestType & USB_REQ_READ )
+#define USB_REQ_IS_WRITE(req)	(!((req)->bmRequestType & USB_REQ_READ))
+
+	/* wRequestAndType */
+#define USB_RT_GET_STATUS_DEV		(( 0 << 8) | 0x80)
+#define USB_RT_GET_STATUS_INTF		(( 0 << 8) | 0x81)
+#define USB_RT_GET_STATUS_EP		(( 0 << 8) | 0x82)
+#define USB_RT_CLEAR_FEATURE_DEV	(( 1 << 8) | 0x00)
+#define USB_RT_CLEAR_FEATURE_INTF	(( 1 << 8) | 0x01)
+#define USB_RT_CLEAR_FEATURE_EP		(( 1 << 8) | 0x02)
+#define USB_RT_SET_FEATURE_DEV		(( 3 << 8) | 0x00)
+#define USB_RT_SET_FEATURE_INTF		(( 3 << 8) | 0x01)
+#define USB_RT_SET_FEATURE_EP		(( 3 << 8) | 0x02)
+#define USB_RT_SET_ADDRESS		(( 5 << 8) | 0x00)
+#define USB_RT_GET_DESCRIPTOR		(( 6 << 8) | 0x80)
+#define USB_RT_SET_DESCRIPTOR		(( 7 << 8) | 0x00)
+#define USB_RT_GET_CONFIGURATION	(( 8 << 8) | 0x80)
+#define USB_RT_SET_CONFIGURATION	(( 9 << 8) | 0x00)
+#define USB_RT_GET_INTERFACE		((10 << 8) | 0x81)
+#define USB_RT_SET_INTERFACE		((11 << 8) | 0x01)
+#define USB_RT_SYNCHFRAME		((12 << 8) | 0x82)

+ 7 - 0
projects/riscv_usb/fw/usb_str_app.txt

@@ -0,0 +1,7 @@
+0000000000000000
+osmocom
+iCE40 USB Demo
+Main
+Console (control)
+Console (data)
+DFU runtime

+ 18 - 34
projects/riscv_usb/fw/usb_desc.c

@@ -1,5 +1,5 @@
 /*
- * usb_desc.c
+ * utils.c
  *
  * Copyright (C) 2019 Sylvain Munaut
  * All rights reserved.
@@ -22,42 +22,26 @@
  */
 
 #include <stdint.h>
+#include <stdbool.h>
 
-#include "usb_desc_data.h"
-
-#define NULL ((void*)0)
-#define num_elem(a) (sizeof(a) / sizeof(a[0]))
-
-const void *
-usb_get_device_desc(int *len)
+char *
+hexstr(void *d, int n, bool space)
 {
-	*len = Devices[0][0];
-	return Devices[0];
-}
+	static const char * const hex = "0123456789abcdef";
+	static char buf[96];
+	uint8_t *p = d;
+	char *s = buf;
+	char c;
 
-const void *
-usb_get_config_desc(int *len, int idx)
-{
-	if (idx < num_elem(Configurations)) {
-		*len = Configurations[idx][2] + (Configurations[idx][3] << 8);
-		return Configurations[idx];
-	} else {
-		*len = 0;
-		return NULL;
+	while (n--) {
+		c = *p++;
+		*s++ = hex[c >> 4];
+		*s++ = hex[c & 0xf];
+		if (space)
+			*s++ = ' ';
 	}
-}
 
-const void *
-usb_get_string_desc(int *len, int idx)
-{
-	if (idx <= 0) {
-		*len = StringZeros[0][0];
-		return StringZeros[0];
-	} else if ((idx-1) < num_elem(Strings)) {
-		*len = Strings[idx-1][0];
-		return Strings[idx-1];
-	} else {
-		*len = 0;
-		return NULL;
-	}
+	s[space?-1:0] = '\0';
+
+	return buf;
 }

+ 28 - 0
projects/riscv_usb/fw/utils.h

@@ -0,0 +1,28 @@
+/*
+ * utils.h
+ *
+ * Copyright (C) 2019 Sylvain Munaut
+ * All rights reserved.
+ *
+ * LGPL v3+, see LICENSE.lgpl3
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <stdbool.h>
+
+char *hexstr(void *d, int n, bool space);