fw/e1-tracer: Import firmware for the E1 tracer board

Some more code could probably be unified with the "normal" usb E1 adapter
to both reduce code duplication but also offer 'sniff' function to the
E1 adapter.

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
This commit is contained in:
Sylvain Munaut 2020-09-14 10:23:50 +02:00
parent bc9f5c4219
commit f5d7bf2480
11 changed files with 1403 additions and 0 deletions

View File

@ -0,0 +1,5 @@
*.elf
*.hex
*.bin
*.o
*.gen.h

View File

@ -0,0 +1,82 @@
#ice1usb
#ice1usb-proto-icebreaker
#ice1usb-proto-bitsty
#e1-tracer
BOARD ?= e1-tracer
CROSS ?= riscv-none-embed-
CC = $(CROSS)gcc
OBJCOPY = $(CROSS)objcopy
ICEPROG = iceprog
DFU_UTIL = dfu-util
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) -I. -I../common
NO2USB_FW_VERSION=0
include ../../../gateware/cores/no2usb/fw/fw.mk
CFLAGS += $(INC_no2usb)
LNK=../common/lnk-app.lds
HEADERS_common := $(addprefix ../common/, \
console.h \
dma.h \
led.h \
mini-printf.h \
spi.h \
utils.h \
)
SOURCES_common := $(addprefix ../common/, \
../common/start.S \
console.c \
dma.c \
led.c \
mini-printf.c \
spi.c \
utils.c \
)
HEADERS_common += $(HEADERS_no2usb)
SOURCES_common += $(SOURCES_no2usb)
HEADERS_app=\
config.h \
e1.h \
misc.h \
usb_str_app.gen.h \
$(NULL)
SOURCES_app=\
e1.c \
fw_app.c \
misc.c \
usb_desc_app.c \
usb_e1.c \
$(NULL)
all: fw_app.bin
fw_app.elf: $(LNK) $(HEADERS_app) $(SOURCES_app) $(HEADERS_common) $(SOURCES_common)
$(CC) $(CFLAGS) -Wl,-Bstatic,-T,$(LNK),--strip-debug -o $@ $(SOURCES_common) $(SOURCES_app)
%.hex: %.bin
../common/bin2hex.py $< $@
%.bin: %.elf
$(OBJCOPY) -O binary $< $@
prog: fw_app.bin
$(ICEPROG) -o 640k $<
dfuprog: fw_app.bin
$(DFU_UTIL) -R -a 1 -D $<
clean:
rm -f *.bin *.hex *.elf *.o *.gen.h
.PHONY: prog dfuprog clean

View File

@ -0,0 +1,19 @@
/*
* config.h
*
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#define SPI_FLASH_BASE 0x80000000
#define UART_BASE 0x81000000
#define LED_BASE 0x82000000
#define USB_CORE_BASE 0x83000000
#define USB_DATA_BASE 0x84000000
#define E1_DATA_BASE 0x85000000
#define DMA_BASE 0x86000000
#define E1_CORE_BASE 0x87000000
#define MISC_BASE 0x88000000
#define SPI_LIU_BASE 0x89000000

View File

@ -0,0 +1,513 @@
/*
* e1.c
*
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <no2usb/usb.h>
#include "config.h"
#include "console.h"
#include "e1.h"
#include "dma.h"
#include "led.h" // FIXME
// Hardware
// --------
struct e1_chan {
uint32_t csr;
uint32_t bd;
uint32_t _rsvd[2];
} __attribute__((packed,aligned(4)));
struct e1_core {
struct e1_chan rx[2];
} __attribute__((packed,aligned(4)));
#define E1_RX_CR_ENABLE (1 << 0)
#define E1_RX_CR_MODE_TRSP (0 << 1)
#define E1_RX_CR_MODE_BYTE (1 << 1)
#define E1_RX_CR_MODE_BFA (2 << 1)
#define E1_RX_CR_MODE_MFA (3 << 1)
#define E1_RX_CR_OVFL_CLR (1 << 12)
#define E1_RX_SR_ENABLED (1 << 0)
#define E1_RX_SR_ALIGNED (1 << 1)
#define E1_RX_SR_BD_IN_EMPTY (1 << 8)
#define E1_RX_SR_BD_IN_FULL (1 << 9)
#define E1_RX_SR_BD_OUT_EMPTY (1 << 10)
#define E1_RX_SR_BD_OUT_FULL (1 << 11)
#define E1_RX_SR_OVFL (1 << 12)
#define E1_BD_VALID (1 << 15)
#define E1_BD_CRC1 (1 << 14)
#define E1_BD_CRC0 (1 << 13)
#define E1_BD_ADDR(x) ((x) & 0x7f)
#define E1_BD_ADDR_MSK 0x7f
#define E1_BD_ADDR_SHFT 0
static volatile struct e1_core * const e1_regs = (void *)(E1_CORE_BASE);
static volatile uint8_t * const e1_data = (void *)(E1_DATA_BASE);
volatile uint8_t *
e1_data_ptr(int mf, int frame, int ts)
{
return &e1_data[(mf << 9) | (frame << 5) | ts];
}
unsigned int
e1_data_ofs(int mf, int frame, int ts)
{
return (mf << 9) | (frame << 5) | ts;
}
// FIFOs
// -----
/* Note: FIFO works at 'frame' level (i.e. 32 bytes) */
struct e1_fifo {
/* Buffer zone associated with the FIFO */
unsigned int base;
unsigned int mask;
/* Pointers / Levels */
unsigned int wptr[2]; /* 0=committed 1=allocated */
unsigned int rptr[2]; /* 0=discared 1=peeked */
};
/* Utils */
static void
e1f_reset(struct e1_fifo *fifo, unsigned int base, unsigned int len)
{
memset(fifo, 0x00, sizeof(struct e1_fifo));
fifo->base = base;
fifo->mask = len - 1;
}
static unsigned int
e1f_allocd_frames(struct e1_fifo *fifo)
{
/* Number of frames that are allocated (i.e. where we can't write to) */
return (fifo->wptr[1] - fifo->rptr[0]) & fifo->mask;
}
static unsigned int
e1f_valid_frames(struct e1_fifo *fifo)
{
/* Number of valid frames */
return (fifo->wptr[0] - fifo->rptr[0]) & fifo->mask;
}
static unsigned int
e1f_unseen_frames(struct e1_fifo *fifo)
{
/* Number of valid frames that haven't been peeked yet */
return (fifo->wptr[0] - fifo->rptr[1]) & fifo->mask;
}
static unsigned int
e1f_free_frames(struct e1_fifo *fifo)
{
/* Number of frames that aren't allocated */
return (fifo->rptr[0] - fifo->wptr[1] - 1) & fifo->mask;
}
static unsigned int
e1f_ofs_to_dma(unsigned int ofs)
{
/* DMA address are 32-bits word address. Offsets are 32 byte address */
return (ofs << 3);
}
static unsigned int
e1f_ofs_to_mf(unsigned int ofs)
{
/* E1 Buffer Descriptors are always multiframe aligned */
return (ofs >> 4);
}
/* Debug */
static void
e1f_debug(struct e1_fifo *fifo, const char *name)
{
unsigned int la, lv, lu, lf;
la = e1f_allocd_frames(fifo);
lv = e1f_valid_frames(fifo);
lu = e1f_unseen_frames(fifo);
lf = e1f_free_frames(fifo);
printf("%s: R: %u / %u | W: %u / %u | A:%u V:%u U:%u F:%u\n",
name,
fifo->rptr[0], fifo->rptr[1], fifo->wptr[0], fifo->wptr[1],
la, lv, lu, lf
);
}
/* Frame level read/write */
static unsigned int
e1f_frame_write(struct e1_fifo *fifo, unsigned int *ofs, unsigned int max_frames)
{
unsigned int lf, le;
lf = e1f_free_frames(fifo);
le = fifo->mask - fifo->wptr[0] + 1;
if (max_frames > le)
max_frames = le;
if (max_frames > lf)
max_frames = lf;
*ofs = fifo->base + fifo->wptr[0];
fifo->wptr[1] = fifo->wptr[0] = (fifo->wptr[0] + max_frames) & fifo->mask;
return max_frames;
}
static unsigned int
e1f_frame_read(struct e1_fifo *fifo, unsigned int *ofs, int max_frames)
{
unsigned int lu, le;
lu = e1f_unseen_frames(fifo);
le = fifo->mask - fifo->rptr[1] + 1;
if (max_frames > le)
max_frames = le;
if (max_frames > lu)
max_frames = lu;
*ofs = fifo->base + fifo->rptr[1];
fifo->rptr[0] = fifo->rptr[1] = (fifo->rptr[1] + max_frames) & fifo->mask;
return max_frames;
}
/* MultiFrame level split read/write */
static bool
e1f_multiframe_write_prepare(struct e1_fifo *fifo, unsigned int *ofs)
{
unsigned int lf;
lf = e1f_free_frames(fifo);
if (lf < 16)
return false;
*ofs = fifo->base + fifo->wptr[1];
fifo->wptr[1] = (fifo->wptr[1] + 16) & fifo->mask;
return true;
}
static void
e1f_multiframe_write_commit(struct e1_fifo *fifo)
{
fifo->wptr[0] = (fifo->wptr[0] + 16) & fifo->mask;
}
static bool
e1f_multiframe_read_peek(struct e1_fifo *fifo, unsigned int *ofs)
{
unsigned int lu;
lu = e1f_unseen_frames(fifo);
if (lu < 16)
return false;
*ofs = fifo->base + fifo->rptr[1];
fifo->rptr[1] = (fifo->rptr[1] + 16) & fifo->mask;
return true;
}
static void
e1f_multiframe_read_discard(struct e1_fifo *fifo)
{
fifo->rptr[0] = (fifo->rptr[0] + 16) & fifo->mask;
}
static void
e1f_multiframe_empty(struct e1_fifo *fifo)
{
fifo->rptr[0] = fifo->rptr[1] = (fifo->wptr[0] & ~15);
}
// Main logic
// ----------
enum e1_pipe_state {
IDLE = 0,
BOOT = 1,
RUN = 2,
RECOVER = 3,
};
static struct {
struct {
uint32_t cr;
struct e1_fifo fifo;
short in_flight;
enum e1_pipe_state state;
uint8_t flags;
} rx[2];
uint32_t error;
} g_e1;
void
e1_init(void)
{
/* Global state init */
memset(&g_e1, 0x00, sizeof(g_e1));
}
void
e1_start(void)
{
/* Reset FIFOs */
#ifdef BIGBUF
e1f_reset(&g_e1.rx[0].fifo, 0, 1024);
e1f_reset(&g_e1.rx[1].fifo, 1024, 1024);
#else
e1f_reset(&g_e1.rx[0].fifo, 0, 128);
e1f_reset(&g_e1.rx[1].fifo, 128, 128);
#endif
/* Enable Rx0 */
g_e1.rx[0].cr = E1_RX_CR_OVFL_CLR |
E1_RX_CR_MODE_MFA |
E1_RX_CR_ENABLE;
e1_regs->rx[0].csr = g_e1.rx[0].cr;
/* Enable Rx1 */
g_e1.rx[1].cr = E1_RX_CR_OVFL_CLR |
E1_RX_CR_MODE_MFA |
E1_RX_CR_ENABLE;
e1_regs->rx[1].csr = g_e1.rx[1].cr;
/* State */
g_e1.rx[0].state = BOOT;
g_e1.rx[0].in_flight = 0;
g_e1.rx[0].flags = 0;
g_e1.rx[1].state = BOOT;
g_e1.rx[1].in_flight = 0;
g_e1.rx[1].flags = 0;
}
void
e1_stop()
{
/* Disable RX0 */
g_e1.rx[0].cr = 0;
e1_regs->rx[0].csr = g_e1.rx[0].cr;
/* Disable RX1 */
g_e1.rx[1].cr = 0;
e1_regs->rx[1].csr = g_e1.rx[1].cr;
/* State */
g_e1.rx[0].state = IDLE;
g_e1.rx[1].state = IDLE;
}
#include "dma.h"
unsigned int
e1_rx_need_data(int chan, unsigned int usb_addr, unsigned int max_frames, unsigned int *pos)
{
unsigned int ofs;
int tot_frames = 0;
int n_frames;
while (max_frames) {
/* Get some data from the FIFO */
n_frames = e1f_frame_read(&g_e1.rx[chan].fifo, &ofs, max_frames);
if (!n_frames)
break;
/* Give pos */
if (pos) {
*pos = ofs & g_e1.rx[chan].fifo.mask;
pos = NULL;
}
/* Copy from FIFO to USB */
dma_exec(e1f_ofs_to_dma(ofs), usb_addr, n_frames * (32 / 4), false, NULL, NULL);
/* Prepare Next */
usb_addr += n_frames * (32 / 4);
max_frames -= n_frames;
tot_frames += n_frames;
/* Wait for DMA completion */
while (dma_poll());
}
return tot_frames;
}
unsigned int
e1_rx_level(int chan)
{
return e1f_valid_frames(&g_e1.rx[chan].fifo);
}
uint8_t
e1_get_pending_flags(int chan)
{
uint8_t f = g_e1.rx[chan].flags;
g_e1.rx[chan].flags = 0;
return f;
}
#define ERR_TIME 1000
void
e1_poll(void)
{
uint32_t bd;
unsigned int ofs;
int chan;
bool error = false;
/* HACK: LED link status */
if ((g_e1.rx[0].state == IDLE) && (g_e1.rx[1].state == IDLE))
{
/* Static dim red */
led_color(32, 0, 0);
led_blink(false, 0, 0);
} else {
uint32_t csr[2];
csr[0] = e1_regs->rx[0].csr;
csr[1] = e1_regs->rx[1].csr;
if (!((csr[0] & csr[1]) & E1_RX_SR_ALIGNED))
error = true;
/* Color is current SYNC status */
led_color(
error ? 1 : 0,
csr[0] & E1_RX_SR_ALIGNED ? 48 : 0,
csr[1] & E1_RX_SR_ALIGNED ? 112 : 0
);
}
/* Active ? */
if ((g_e1.rx[0].state == IDLE) && (g_e1.rx[1].state == IDLE))
return;
/* Recover any done RX BD */
for (chan=0; chan<2; chan++)
{
while ( (bd = e1_regs->rx[chan].bd) & E1_BD_VALID ) {
/* FIXME: CRC status ? */
e1f_multiframe_write_commit(&g_e1.rx[chan].fifo);
if ((bd & (E1_BD_CRC0 | E1_BD_CRC1)) != (E1_BD_CRC0 | E1_BD_CRC1)) {
printf("b: %03x\n", bd);
g_e1.rx[chan].flags |= 4;
error = true;
}
g_e1.rx[chan].in_flight--;
}
}
/* Handle RX */
for (chan=0; chan<2; chan++)
{
/* Misalign ? */
if (g_e1.rx[chan].state == RUN) {
if (!(e1_regs->rx[chan].csr & E1_RX_SR_ALIGNED)) {
printf("[!] E1 rx misalign\n");
g_e1.rx[chan].state = RECOVER;
g_e1.rx[chan].flags |= 1;
error = true;
}
}
/* Overflow ? */
if (g_e1.rx[chan].state == RUN) {
if (e1_regs->rx[chan].csr & E1_RX_SR_OVFL) {
printf("[!] E1 overflow %d\n", g_e1.rx[chan].in_flight);
g_e1.rx[chan].state = RECOVER;
g_e1.rx[chan].flags |= 2;
}
}
/* Recover ready ? */
if (g_e1.rx[chan].state == RECOVER) {
if (g_e1.rx[chan].in_flight != 0)
continue;
e1f_multiframe_empty(&g_e1.rx[chan].fifo);
}
/* Fill new RX BD */
while (g_e1.rx[chan].in_flight < 4) {
if (!e1f_multiframe_write_prepare(&g_e1.rx[chan].fifo, &ofs))
break;
e1_regs->rx[chan].bd = e1f_ofs_to_mf(ofs);
g_e1.rx[chan].in_flight++;
}
/* Clear overflow if needed */
if (g_e1.rx[chan].state != RUN) {
e1_regs->rx[chan].csr = g_e1.rx[chan].cr | E1_RX_CR_OVFL_CLR;
g_e1.rx[chan].state = RUN;
}
}
/* Error tracking */
if (error) {
if (!g_e1.error) {
printf("Error LED\n");
led_blink(true, 150, 150);
}
g_e1.error = usb_get_tick() + ERR_TIME;
} else if (g_e1.error && (g_e1.error < usb_get_tick())) {
g_e1.error = 0;
led_blink(false, 0, 0);
printf("No error\n");
}
}
void
e1_debug_print(bool data)
{
volatile uint8_t *p;
puts("E1\n");
printf("CSR: Rx0 %04x / Rx1 %04x\n", e1_regs->rx[0].csr, e1_regs->rx[1].csr);
printf("InF: Rx0 %d / Rx1 %d\n", g_e1.rx[0].in_flight, g_e1.rx[1].in_flight);
printf("Sta: Rx0 %d / Rx1 %d\n", g_e1.rx[0].state, g_e1.rx[1].state);
e1f_debug(&g_e1.rx[0].fifo, "Rx0 FIFO");
e1f_debug(&g_e1.rx[1].fifo, "Rx1 FIFO");
if (data) {
puts("\nE1 Data\n");
for (int f=0; f<16; f++) {
p = e1_data_ptr(0, f, 0);
for (int ts=0; ts<32; ts++)
printf(" %02x", p[ts]);
printf("\n");
}
}
}

View File

@ -0,0 +1,19 @@
/*
* e1.h
*
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
void e1_init();
void e1_start();
void e1_stop();
void e1_poll(void);
void e1_debug_print(bool data);
volatile uint8_t *e1_data_ptr(int mf, int frame, int ts);
unsigned int e1_data_ofs(int mf, int frame, int ts);

View File

@ -0,0 +1,224 @@
/*
* fw_app.c
*
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <no2usb/usb.h>
#include <no2usb/usb_dfu_rt.h>
#include "console.h"
#include "e1.h"
#include "led.h"
#include "misc.h"
#include "mini-printf.h"
#include "spi.h"
#include "utils.h"
extern const struct usb_stack_descriptors app_stack_desc;
void usb_e1_init(void);
void usb_e1_run(void);
static void
serial_no_init()
{
uint8_t buf[8];
char *id, *desc;
int i;
flash_manuf_id(buf);
printf("Flash Manufacturer : %s\n", hexstr(buf, 3, true));
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];
}
static void
boot_dfu(void)
{
/* Force re-enumeration */
usb_disconnect();
/* Boot firmware */
volatile uint32_t *boot = (void*)0x80000000;
*boot = (1 << 2) | (1 << 0);
}
void
usb_dfu_rt_cb_reboot(void)
{
boot_dfu();
}
static uint8_t
liu_read_reg(unsigned chan, uint8_t reg)
{
uint8_t cmd = reg | 0x20;
uint8_t rv;
struct spi_xfer_chunk xfer[2] = {
{ .data = (void*)&cmd, .len = 1, .read = false, .write = true, },
{ .data = (void*)&rv, .len = 1, .read = true, .write = false, },
};
spi_xfer(SPI_CS_LIU(chan), xfer, 2);
return rv;
}
static void
liu_write_reg(unsigned chan, uint8_t reg, uint8_t val)
{
uint8_t cmd[2] = { reg, val };
struct spi_xfer_chunk xfer[2] = {
{ .data = (void*)cmd, .len = 2, .read = false, .write = true, },
};
spi_xfer(SPI_CS_LIU(chan), xfer, 1);
}
#define USB_RT_LIU_REG_WRITE ((1 << 8) | 0x42)
#define USB_RT_LIU_REG_READ ((2 << 8) | 0xc2)
static bool
_liu_reg_write_done_cb(struct usb_xfer *xfer)
{
struct usb_ctrl_req *req = xfer->cb_ctx;
unsigned chan = req->wIndex - 0x81;
liu_write_reg(chan, req->wValue, xfer->data[0]);
return true;
}
static enum usb_fnd_resp
_liu_ctrl_req(struct usb_ctrl_req *req, struct usb_xfer *xfer)
{
unsigned chan;
/* If this a vendor request for the E1 endpoints */
if (USB_REQ_RCPT(req) != USB_REQ_RCPT_EP)
return USB_FND_CONTINUE;
if (req->wIndex < 0x81 || req->wIndex > 0x82)
return USB_FND_CONTINUE;
if (USB_REQ_TYPE(req) != USB_REQ_TYPE_VENDOR)
return USB_FND_CONTINUE;
chan = req->wIndex - 0x81;
/* */
switch (req->wRequestAndType)
{
case USB_RT_LIU_REG_WRITE:
xfer->len = 1;
xfer->cb_done = _liu_reg_write_done_cb;
xfer->cb_ctx = req;
break;
case USB_RT_LIU_REG_READ:
xfer->len = 1;
xfer->data[0] = liu_read_reg(chan, req->wValue);
break;
default:
goto error;
}
return USB_FND_SUCCESS;
error:
return USB_FND_ERROR;
}
static struct usb_fn_drv _liu_drv = {
.ctrl_req = _liu_ctrl_req,
};
void main()
{
int cmd = 0;
/* Init console IO */
console_init();
puts("Booting App image..\n");
/* LED */
led_init();
led_state(true);
/* SPI */
spi_init();
puts("XXX\n");
/* Enable USB directly */
serial_no_init();
usb_init(&app_stack_desc);
usb_dfu_rt_init();
usb_register_function_driver(&_liu_drv);
usb_e1_init();
usb_connect();
e1_init();
/* Main loop */
while (1)
{
/* Prompt ? */
if (cmd >= 0)
printf("Command> ");
/* Poll for command */
cmd = getchar_nowait();
if (cmd >= 0) {
if (cmd > 32 && cmd < 127) {
putchar(cmd);
putchar('\r');
putchar('\n');
}
switch (cmd)
{
case 'u':
usb_debug_print();
break;
case 'e':
e1_debug_print(false);
break;
case 'E':
e1_debug_print(true);
break;
default:
break;
}
}
/* USB poll */
usb_poll();
/* E1 poll */
e1_poll();
usb_e1_run();
}
}

View File

@ -0,0 +1,35 @@
/*
* misc.c
*
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <stdbool.h>
#include <stdint.h>
#include "config.h"
#include "misc.h"
struct misc {
uint32_t warmboot;
uint32_t _rsvd0[3];;
struct {
uint16_t tx;
uint16_t rx;
} e1_tick[2];
uint32_t _rsvd1;
uint32_t time;
} __attribute__((packed,aligned(4)));
static volatile struct misc * const misc_regs = (void*)(MISC_BASE);
void
e1_tick_read(uint16_t *ticks)
{
uint32_t v = misc_regs->e1_tick;
ticks[0] = (v ) & 0xffff;
ticks[1] = (v >> 16) & 0xffff;
}

View File

@ -0,0 +1,15 @@
/*
* misc.h
*
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
void vio_set(unsigned value);
void e1_tick_read(uint16_t *ticks);

View File

@ -0,0 +1,253 @@
/*
* usb_desc_app.c
*
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <no2usb/usb_proto.h>
#include <no2usb/usb.h>
#define NULL ((void*)0)
#define num_elem(a) (sizeof(a) / sizeof(a[0]))
static const struct {
/* Configuration */
struct usb_conf_desc conf;
/* E1 */
struct {
struct {
struct usb_intf_desc intf;
struct usb_ep_desc ep_data_in0;
struct usb_ep_desc ep_data_in1;
} __attribute__ ((packed)) off;
struct {
struct usb_intf_desc intf;
struct usb_ep_desc ep_data_in0;
struct usb_ep_desc ep_data_in1;
} __attribute__ ((packed)) on;
} __attribute__ ((packed)) e1;
/* CDC */
#if 0
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;
#endif
/* 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),
#if 0
.bNumInterfaces = 4,
#else
.bNumInterfaces = 2,
#endif
.bConfigurationValue = 1,
.iConfiguration = 4,
.bmAttributes = 0x80,
.bMaxPower = 0x32, /* 100 mA */
},
.e1 = {
.off = {
.intf = {
.bLength = sizeof(struct usb_intf_desc),
.bDescriptorType = USB_DT_INTF,
.bInterfaceNumber = 0,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0xe1,
.bInterfaceProtocol = 0x00,
.iInterface = 5,
},
.ep_data_in0 = {
.bLength = sizeof(struct usb_ep_desc),
.bDescriptorType = USB_DT_EP,
.bEndpointAddress = 0x81,
.bmAttributes = 0x05,
.wMaxPacketSize = 0,
.bInterval = 1,
},
.ep_data_in1 = {
.bLength = sizeof(struct usb_ep_desc),
.bDescriptorType = USB_DT_EP,
.bEndpointAddress = 0x82,
.bmAttributes = 0x05,
.wMaxPacketSize = 0,
.bInterval = 1,
},
},
.on = {
.intf = {
.bLength = sizeof(struct usb_intf_desc),
.bDescriptorType = USB_DT_INTF,
.bInterfaceNumber = 0,
.bAlternateSetting = 1,
.bNumEndpoints = 2,
.bInterfaceClass = 0xff,
.bInterfaceSubClass = 0xe1,
.bInterfaceProtocol = 0x00,
.iInterface = 5,
},
.ep_data_in0 = {
.bLength = sizeof(struct usb_ep_desc),
.bDescriptorType = USB_DT_EP,
.bEndpointAddress = 0x81,
.bmAttributes = 0x05,
.wMaxPacketSize = 388,
.bInterval = 1,
},
.ep_data_in1 = {
.bLength = sizeof(struct usb_ep_desc),
.bDescriptorType = USB_DT_EP,
.bEndpointAddress = 0x82,
.bmAttributes = 0x05,
.wMaxPacketSize = 388,
.bInterval = 1,
},
},
},
#if 0
.cdc = {
.intf_ctl = {
.bLength = sizeof(struct usb_intf_desc),
.bDescriptorType = USB_DT_INTF,
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 1,
.bInterfaceClass = 0x02,
.bInterfaceSubClass = 0x02,
.bInterfaceProtocol = 0x00,
.iInterface = 6,
},
.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 = 0x00,
},
.cs_intf_union = {
.bLength = sizeof(struct usb_cs_intf_union_desc) + 1,
.bDescriptorType = USB_DT_CS_INTF,
.bDescriptorsubtype = 0x06,
.bMasterInterface = 1,
},
.cs_intf_union_slave = 2,
.ep_ctl = {
.bLength = sizeof(struct usb_ep_desc),
.bDescriptorType = USB_DT_EP,
.bEndpointAddress = 0x84,
.bmAttributes = 0x03,
.wMaxPacketSize = 64,
.bInterval = 0xff,
},
.intf_data = {
.bLength = sizeof(struct usb_intf_desc),
.bDescriptorType = USB_DT_INTF,
.bInterfaceNumber = 2,
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = 0x0a,
.bInterfaceSubClass = 0x00,
.bInterfaceProtocol = 0x00,
.iInterface = 7,
},
.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 = 0x01,
},
},
#endif
.dfu = {
.intf = {
.bLength = sizeof(struct usb_intf_desc),
.bDescriptorType = USB_DT_INTF,
#if 0
.bInterfaceNumber = 3,
#else
.bInterfaceNumber = 1,
#endif
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = 0xfe,
.bInterfaceSubClass = 0x01,
.bInterfaceProtocol = 0x01,
.iInterface = 8,
},
.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 = 0x6151,
.bcdDevice = 2,
.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),
};

View File

@ -0,0 +1,230 @@
/*
* usb_e1.c
*
* Copyright (C) 2019-2020 Sylvain Munaut <tnt@246tNt.com>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <no2usb/usb_hw.h>
#include <no2usb/usb_priv.h>
#include "console.h"
#include "e1.h"
#include "misc.h"
struct {
bool running;
int in_bdi[2];
} g_usb_e1;
/* Hack */
unsigned int e1_rx_need_data(int chan, unsigned int usb_addr, unsigned int max_len, unsigned int *pos);
unsigned int e1_rx_level(int chan);
uint8_t e1_get_pending_flags(int chan);
/* ---- */
bool
usb_ep_boot(const struct usb_intf_desc *intf, uint8_t ep_addr, bool dual_bd);
void
usb_e1_run(void)
{
int chan;
int bdi;
if (!g_usb_e1.running)
return;
/* EP[1-2] IN */
for (chan=0; chan<2; chan++)
{
bdi = g_usb_e1.in_bdi[chan];
while ((usb_ep_regs[1+chan].in.bd[bdi].csr & USB_BD_STATE_MSK) != USB_BD_STATE_RDY_DATA)
{
uint32_t ptr = usb_ep_regs[1+chan].in.bd[bdi].ptr;
uint32_t hdr;
unsigned int pos;
/* Error check */
if ((usb_ep_regs[1+chan].in.bd[bdi].csr & USB_BD_STATE_MSK) == USB_BD_STATE_DONE_ERR)
printf("Err EP%d IN\n", 1+chan);
/* Get some data from E1 */
int n = e1_rx_level(chan);
// if (n > 64)
// n = 12;
// else if (n > 32)
// n = 10;
// else if (n > 8)
// n = 8;
if (n > 12)
n = 12;
else if (!n)
break;
n = e1_rx_need_data(chan, (ptr >> 2) + 1, n, &pos);
/* Write header */
/* [31:12] (reserved) */
/* [11:10] CRC results (first new multiframe present in packet) */
/* [ 9: 8] CRC results (second new multiframe present in packet) */
/* [ 7: 5] Multiframe sequence number (first frame of packet) */
/* [ 4: 0] Position in multi-frame (first frame of packet) */
hdr = (pos & 0xff) | (e1_get_pending_flags(chan) << 24);
usb_data_write(ptr, &hdr, 4);
usb_ep_regs[1+chan].in.bd[bdi].csr = USB_BD_STATE_RDY_DATA | USB_BD_LEN((n * 32) + 4);
/* Next BDI */
bdi ^= 1;
g_usb_e1.in_bdi[chan] = bdi;
}
}
}
static const struct usb_intf_desc *
_find_intf(const struct usb_conf_desc *conf, uint8_t idx)
{
const struct usb_intf_desc *intf = NULL;
const void *sod, *eod;
if (!conf)
return NULL;
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)
return intf;
sod = usb_desc_next(sod);
}
return NULL;
}
enum usb_fnd_resp
_e1_set_conf(const struct usb_conf_desc *conf)
{
const struct usb_intf_desc *intf;
printf("e1 set_conf %08x\n", conf);
if (!conf)
return USB_FND_SUCCESS;
intf = _find_intf(conf, 0);
if (!intf)
return USB_FND_ERROR;
printf("e1 set_conf %08x\n", intf);
usb_ep_boot(intf, 0x81, true);
usb_ep_boot(intf, 0x82, true);
return USB_FND_SUCCESS;
}
enum usb_fnd_resp
_e1_set_intf(const struct usb_intf_desc *base, const struct usb_intf_desc *sel)
{
if (base->bInterfaceNumber != 0)
return USB_FND_CONTINUE;
if (sel->bAlternateSetting == 0)
{
/* Already stopped ? */
if (!g_usb_e1.running)
return USB_FND_SUCCESS;
/* Update state */
g_usb_e1.running = false;
/* Stop E1 */
e1_stop();
/* Disable end-points */
usb_ep_regs[1].in.status = 0;
usb_ep_regs[2].in.status = 0;
}
else if (sel->bAlternateSetting == 1)
{
/* Already running ? */
if (g_usb_e1.running)
return USB_FND_SUCCESS;
/* Update state */
g_usb_e1.running = true;
/* Reset buffer pointers */
g_usb_e1.in_bdi[0] = 0;
g_usb_e1.in_bdi[1] = 0;
/* Configure EP1 IN / EP2 IN */
usb_ep_regs[1].in.status = USB_EP_TYPE_ISOC | USB_EP_BD_DUAL; /* Type=Isochronous, dual buffered */
usb_ep_regs[2].in.status = USB_EP_TYPE_ISOC | USB_EP_BD_DUAL; /* Type=Isochronous, dual buffered */
/* EP1 IN: Prepare two buffers */
usb_ep_regs[1].in.bd[0].ptr = 256 + 0 * 388;
usb_ep_regs[1].in.bd[0].csr = 0;
usb_ep_regs[1].in.bd[1].ptr = 256 + 1 * 388;
usb_ep_regs[1].in.bd[1].csr = 0;
/* EP2 IN: Prepare two buffers */
usb_ep_regs[2].in.bd[0].ptr = 256 + 2 * 388;
usb_ep_regs[2].in.bd[0].csr = 0;
usb_ep_regs[2].in.bd[1].ptr = 256 + 3 * 388;
usb_ep_regs[2].in.bd[1].csr = 0;
/* Start E1 */
e1_start();
}
else
{
/* Unknown */
return USB_FND_ERROR;
}
return USB_FND_SUCCESS;
}
enum usb_fnd_resp
_e1_get_intf(const struct usb_intf_desc *base, uint8_t *alt)
{
if (base->bInterfaceNumber != 0)
return USB_FND_CONTINUE;
*alt = g_usb_e1.running ? 1 : 0;
return USB_FND_SUCCESS;
}
static struct usb_fn_drv _e1_drv = {
.set_conf = _e1_set_conf,
.set_intf = _e1_set_intf,
.get_intf = _e1_get_intf,
};
void
usb_e1_init(void)
{
/* Clear state */
memset(&g_usb_e1, 0x00, sizeof(g_usb_e1));
/* Install driver */
usb_register_function_driver(&_e1_drv);
}

View File

@ -0,0 +1,8 @@
0000000000000000
osmocom
e1-tracer
Main
E1
Console (control)
Console (data)
DFU runtime