1
0
mirror of git://projects.qi-hardware.com/f32xbase.git synced 2024-06-29 00:00:44 +03:00

fw/common/ - copied from IDBG and generalized a little

This commit is contained in:
Werner Almesberger 2010-08-13 08:47:13 -03:00
parent c55fc4018d
commit bdfe058079
8 changed files with 1163 additions and 0 deletions

100
fw/common/Makefile.common Normal file
View File

@ -0,0 +1,100 @@
#
# common/Makefile.common - Common rules and definitions
#
# Written 2008, 2010 by Werner Almesberger
# Copyright 2008, 2010 Werner Almesberger
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
CC=sdcc
CFLAGS=--std-c99 -I. -I../common \
-DPAYLOAD_START=$(PAYLOAD_START) -DPAYLOAD_SIZE=$(PAYLOAD_SIZE) \
-D`cat ../.target`
LDFLAGS=--xram-size 1024
CPP := $(CPP) # make sure changing CC won't affect CPP
CC_normal := $(CC)
CC_quiet = @echo " CC " $@ && $(CC_normal)
GEN_quiet = @echo " GENERATE " $@ &&
ifeq ($(V),1)
CC = $(CC_normal)
GEN =
else
CC = $(CC_quiet)
GEN = $(GEN_quiet)
endif
.SUFFIXES: .rel .ihx .bin
.PHONY: clean spotless upload version
all: $(MAIN).bin
version version.h:
@if [ -f .version ]; then \
v=`cat .version`; \
expr $$v + 1 >.version; \
else \
echo 0 >.version; \
fi
@[ -s .version ] || echo 0 >.version
@echo '/* MACHINE-GENERATED. DO NOT EDIT ! */' >version.c
@echo '#include "version.h"' >>version.c
@echo "const char *build_date = \"`date`\";" >>version.c
@echo "const uint16_t build_number = `cat .version`;" \
>>version.c
version.rel: version
$(MAIN).ihx: $(OBJS:%=%.rel)
$(GEN) $(CC_normal) $(CFLAGS) $(LDFLAGS) $(OBJS:%=%.rel)
.ihx.bin:
$(GEN) objcopy -I ihex $< -O binary $@
@echo "build #`cat .version`, `ls -l $@`"
.rel.ihx:
$(CC) $(CFLAGS) $<
.c.rel:
$(CC) $(CFLAGS) -c $<
$(OBJS:%=%.rel): ../.target
# below, set dummy UART speed to make dependencies build without error
depend .depend:
>.depend
touch version.h version.c
for n in $(OBJS:%=%.c); do \
$(CPP) $(CFLAGS) -DUART_115200_BPS -MM -MG \
`[ -f $$n ] || echo ../common/`$$n >>.depend || \
{ rm -f .depend; exit 1; }; \
done
.depend: ../.target
ifeq (.depend,$(wildcard .depend))
include .depend
endif
clean:
rm -f $(OBJS:%=%.rel)
rm -f $(OBJS:%=%.asm) $(OBJS:%=%.lst)
rm -f $(OBJS:%=%.rst) $(OBJS:%=%.sym)
rm -f $(MAIN).ihx $(MAIN).lnk $(MAIN).map $(MAIN).mem
spotless: clean
rm -f $(MAIN).bin .depend
upload:
ssh lab neo 'cat \>$(MAIN).bin' <$(MAIN).bin

45
fw/common/Makefile.system Normal file
View File

@ -0,0 +1,45 @@
#
# common/Makefile.system - System-specific definitions
#
# Written 2008-2010 by Werner Almesberger
# Copyright 2008-2010 Werner Almesberger
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
#
# @@@ This is a template for now. Needs more work.
#
#
# For now, we keep an 8k/7.5k division such that there's plenty of space for
# debugging the boot loader. Later, we should move to something like 4k/11.5k.
#
# The last 512 bytes are reserved and the 512 bytes before them cannot be
# erased, so they cannot be used for the payload either :-(
#
PAYLOAD_START=0x1000
PAYLOAD_SIZE=0x2c00
#
# Set the serial speed, depending on target type
#
ifneq (../.target,$(wildcard ../.target))
$(error Please create ../.target first)
endif
ifeq ($(shell cat ../.target),GTA)
DEFINE_UART_SPEED=-DUART_115200_BPS
else ifeq ($(shell cat ../.target),BEN_V1)
DEFINE_UART_SPEED=-DUART_57600_BPS
else ifeq ($(shell cat ../.target),BEN_V2)
DEFINE_UART_SPEED=-DUART_57600_BPS
else
DEFINE_UART_SPEED=
endif

77
fw/common/regs.h Normal file
View File

@ -0,0 +1,77 @@
/*
* common/regs.h - C8051F326 register definitions
*
* Written 2008 by Werner Almesberger
* Copyright 2008 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef REGS_H
#define REGS_H
#include <mcs51/C8051F326.h>
#include "usb-regs.h"
/* REG0CN */
#define REGMOD 0x10 /* Voltage Regulator Mode Select */
#define VBPOL 0x20 /* VBUS Interrupt Polarity Select */
#define VBSTAT 0x40 /* VBUS Signal Status */
#define REGDIS 0x80 /* Voltage Regulator Disable */
/* RSTSRC */
#define PINRSF 0x01 /* HW Pin Reset Flag */
/* PORSF 0x02 -- Power-On/VDD Monitor Reset Flag */
#define MCFRSF 0x04 /* Missing Clock Detector Flag */
/* SWRSF 0x10 -- Software Reset Force and Flag */
#define FERROR 0x40 /* Flash Error Indicator */
#define USBRSF 0x80 /* USB Reset Flag */
/* OSCICN */
#define IFCN0 0x01 /* Internal Oscillator Frequency Control */
#define IFCN1 0x02 /* 00: /8, 01: /4, 10: /2, 11: /1 */
/* CLKMUL */
#define MULSEL 0x01 /* Clock Multiplier Input Select */
#define MULRDY 0x20 /* Clock Multiplier Ready */
#define MULINIT 0x40 /* Clock Multiplier Initialize */
#define MULEN 0x80 /* Clock Multiplier Enable */
/* GPIOCN */
#define SYSCLK 0x01 /* nSYSCLK Enable */
#define INPUTEN 0x40 /* Global Digital Input Enable */
#define WEAKPUD 0x80 /* Port I/O Weak Pullup Disable */
/* VDM0CN */
#define VDMEN 0x80 /* VDD Monitor Enable */
#define VDDSTAT 0x40 /* VDD Status */
/* USB0XCN */
#define Dn 0x01 /* D- Signal Status */
#define Dp 0x02 /* D+ Signal Status */
#define DFREC 0x04 /* Differential Receiver */
#define PHYTST0 0x08 /* Physical Layer Test */
#define PHYTST1 0x10 /* 00: normal, 01: "1", 10: "0", 11: SE0 */
#define SPEED 0x20 /* USB0 Speed Select */
#define PHYEN 0x40 /* Physical Layer Enable */
#define PREN 0x80 /* Internal Pullup Resistor Enable */
/* USB0ADR */
#define BUSY 0x80 /* USB0 Register Read Busy Flag */
/* SMOD0 */
#define S0DL0 0x04 /* Data Length */
#define S0DL1 0x08 /* 00: 5-bit, 01: 6-bit, 10: 7-bit, 11: 8-bit */
/* SBCON0 */
#define SB0PS0 0x01 /* Baud Rate Prescaler Select */
#define SB0PS1 0x02 /* 00: /12, 01: /4, 10: /48, 11: /1 */
#define SB0RUN 0x40 /* Baud Rate Generator Enable */
#define SB0CLK 0x80 /* Baud Rate Clock Source */
#endif /* REGS_H */

125
fw/common/uart.c Normal file
View File

@ -0,0 +1,125 @@
/*
* common/uart.c - UART initialization and debug output
*
* Written 2008, 2009 by Werner Almesberger
* Copyright 2008, 2009 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <stdarg.h>
#include <stdio.h>
#include "regs.h"
#include "uart.h"
/* gpio j4=0 */
#ifndef CONFIG_USB_PUTCHAR
void putchar(char c)
{
if (c == '\n')
putchar('\r');
SBUF0 = c;
while (!TI0);
TI0 = 0;
}
#endif /* !CONFIG_USB_PUTCHAR */
/* for now, we always use printf_tiny, which doesn't have a vprintf* version */
#ifdef noCONFIG_PRINTK
void printk(const char *fmt, ...)
{
va_list ap;
__bit saved;
va_start(ap, fmt);
saved = EA;
EA = 0;
vprintf(fmt, ap);
EA = saved;
va_end(ap);
}
#endif /* CONFIG_PRINTK */
void uart_init(uint8_t brg_mhz)
{
/*
* UART: Enable only transmitter, no interrupts.
*/
SCON0 = 0; /* also clears TI0 */
SMOD0 = S0DL1 | S0DL0;
/*
* Configure the UART to 115200bps, see table 13.1
*
* We can's support 1.5MHz (that is, without using the USB clock, but
* why would one want to run the core at 1.5MHz when USB is around ?)
*
* The closes settings would be:
*
* SBRL0 = 0xfff9; -- 107142.9 bps, error = -7%
* SBRL0 = 0xfffa; -- 125000 bps, error = +8.5%
*
* Depending on signal quality, we would need something like +/-5%.
*/
#if defined(UART_115200_BPS)
switch (brg_mhz) {
case 3:
SBRL0 = 0xfff3;
break;
case 6:
SBRL0 = 0xffe6;
break;
case 12:
SBRL0 = 0xffcc;
break;
case 24:
SBRL0 = 0xff98;
break;
case 48:
SBRL0 = 0xff30;
break;
}
#elif defined(UART_57600_BPS)
switch (brg_mhz) {
case 3:
SBRL0 = 0xffe6;
break;
case 6:
SBRL0 = 0xffcc;
break;
case 12:
SBRL0 = 0xff98;
break;
case 24:
SBRL0 = 0xff30;
break;
case 48:
SBRL0 = 0xfe5f;
break;
}
#else
#error "must set either UART_115200_BPS or UART_57600_BPS"
#endif
SBCON0 = SB0RUN | SB0PS0 | SB0PS1;
/*
* Make TX a push-pull output
*/
P0MDOUT |= 1 << 4;
}

46
fw/common/uart.h Normal file
View File

@ -0,0 +1,46 @@
/*
* common/uart.h - UART initialization and debug output
*
* Written 2008 by Werner Almesberger
* Copyright 2008 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef UART_H
#define UART_H
#include <stdint.h>
#include <stdio.h>
#include "config.h"
#ifdef CONFIG_DEBUG
#define debug printk
#define CONFIG_PRINTK
#else
#define debug(...)
#endif
#ifdef CONFIG_ERROR
#define error printk
#define CONFIG_PRINTK
#else
#define error(...)
#endif
#ifdef CONFIG_PRINTK
#define printk printf_fast
#else
#define printk(...)
#endif
void putchar(char c);
void uart_init(uint8_t brg_mhz);
#endif /* !UART_H */

101
fw/common/usb-regs.h Normal file
View File

@ -0,0 +1,101 @@
/*
* common/usb-regs.h - C8051F326 USB register definitions
*
* Written 2008 by Werner Almesberger
* Copyright 2008 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef USB_REGS_H
#define USB_REGS_H
/* Indirect USB registers */
#define FADDR 0x00 /* Function Address */
#define POWER 0x01 /* Power Management */
#define SUSEN 0x01 /* Suspend Detection Enable */
#define SUSMD 0x02 /* Suspend Mode */
#define RESUME 0x04 /* Force Resume */
#define USBRST 0x08 /* Reset Detect */
#define USBINH 0x10 /* USB0 Inhibit */
#define ISOUD 0x80 /* ISO Update */
#define IN1INT 0x02 /* EP0 and EP1 IN Interrupt Flags */
#define EP0 0x01 /* EP0 Interrupt-pending Flag */
#define IN1 0x02 /* IN EP1 Interrupt-pending Flag */
#define OUT1INT 0x04 /* EP1 OUT Interrupt Flag */
#define CMINT 0x06 /* Common USB Interrupt Flags */
#define SUSINT 0x01 /* Suspend Interrupt-Pending Flag */
#define RSUINT 0x02 /* Resume Interrupt-Pending Flag */
#define RSTINT 0x04 /* Reset Interrupt-Pending Flag */
#define SOF 0x08 /* Start of Frame Interrupt */
#define IN1IE 0x07 /* EP0 and EP1 IN Interrupt Enables */
#define OUT1IE 0x09 /* EP1 out Interrupt Enable */
#define CMIE 0x0b /* Common USB Interrupt Enable */
#define FRAMEL 0x0c /* Frame Number Low Byte */
#define FRAMEH 0x0d /* Frame Number Low Byte */
#define INDEX 0x0e /* USB0 EP Index */
#define CLKREC 0x0f /* Clock Recovery Control */
#define CRLOW 0x20 /* Low Speed Clock Recovery Mode */
#define CRSSEN 0x40 /* Clock Recovery Single Step */
#define CRE 0x80 /* Clock Recovery Enable */
#define E0CSR 0x11 /* EP0 Control/Status */
#define OPRDY_0 0x01 /* OUT Packet Ready */
#define INPRDY_0 0x02 /* IN Packet Ready */
#define STSTL_0 0x04 /* Sent Stall */
#define DATAEND 0x08 /* Data End */
#define SUEND 0x10 /* Setup End */
#define SDSTL_0 0x20 /* Send Stall */
#define SOPRDY 0x40 /* Serviced OPRDY */
#define SSUEND 0x80 /* Serviced Setup End */
#define EINCSRL 0x11 /* EP IN Control/Status Low Byte */
#define INPRDY_IN 0x01 /* IN Packet Ready */
#define FIFONE 0x02 /* FIFO Not Empty */
#define UNDRUN 0x04 /* Data Underrun */
#define FLUSH_IN 0x08 /* FIFO Flush */
#define SDSTL_IN 0x10 /* Send Stall */
#define STSTL_IN 0x20 /* Sent Stall */
#define CLRDT_IN 0x40 /* Clear Data Toggle */
#define EINCSRH 0x12 /* EP OUT Control/Status High Byte */
#define EOUTCSRL 0x14 /* EP OUT Control/Status Low Byte */
#define OPRDY_OUT 0x01 /* OUT Packet Ready */
#define FIFOFUL 0x02 /* OUT FIFO Full */
#define OVRUN 0x04 /* Data Overrun */
#define DATERR 0x08 /* Data Error */
#define FLUSH_OUT 0x10 /* FIFO Flush */
#define SDSTL_OUT 0x20 /* Send Stall */
#define STSTL_OUT 0x40 /* Sent Stall */
#define CLRDT_OUT 0x80 /* Clear Data Toggle */
#define EOUTCSRH 0x15 /* EP OUT Control/Status High Byte */
#define E0CNT 0x16 /* Number of Received Bytes in EP0 FIFO */
#define EOUTCNTL 0x16 /* EP OUT Packet Count Low Byte */
#define EOUTCNTH 0x17 /* EP OUT Packet Count High Byte */
#define FIFO0 0x20 /* EP0 FIFO */
#define FIFO1 0x21 /* EP1 FIFO */
#endif /* !USB_REGS_H */

519
fw/common/usb.c Normal file
View File

@ -0,0 +1,519 @@
/*
* common/usb.c - USB hardware setup and standard device requests
*
* Written 2008, 2009 by Werner Almesberger
* Copyright 2008m 2009 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
/*
* Known issues:
* - no suspend/resume
* - EP0-sized packets cause an (otherwise harmless) SUEND at the end of the
* packet
* - #ifdef hell
*/
/*
* This code follows the register read/write sequences from the examples in
* SiLabs/MCU/Examples/C8051F326_7/USB_Interrupt/Firmware/F326_USB_Main.c and
* SiLabs/MCU/Examples/C8051F326_7/USB_Interrupt/Firmware/F326_USB_ISR.c
*
* More resources:
* http://www.beyondlogic.org/usbnutshell/usb1.htm
*/
#include <stdint.h>
#include "regs.h"
#include "uart.h"
#include "usb.h"
#ifndef NULL
#define NULL 0
#endif
#define BUG_ON(x)
#define NO_ADDRESS 0xff /* null value for function address */
__xdata struct ep_descr ep0;
#ifdef CONFIG_EP1
__xdata struct ep_descr ep1in, ep1out;
#endif
__bit (*user_setup)(struct setup_request *setup) __reentrant;
__bit (*user_get_descriptor)(uint8_t type, uint8_t index,
const uint8_t * const *reply, uint8_t *size) __reentrant;
void (*user_reset)(void) __reentrant;
static uint8_t addr = NO_ADDRESS;
void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf,
uint8_t size, void (*callback)(void *user), void *user)
{
BUG_ON(ep->state);
ep->state = state;
ep->buf = buf;
ep->end = buf+size;
ep->callback = callback;
ep->user = user;
}
static void usb_write(uint8_t reg, uint8_t value)
{
while (USB0ADR & BUSY);
USB0ADR = reg;
USB0DAT = value;
}
static uint8_t usb_read(uint8_t reg)
{
while (USB0ADR & BUSY);
USB0ADR = reg | BUSY;
while (USB0ADR & BUSY);
return USB0DAT;
}
static uint16_t usb_read_word(uint8_t reg)
{
uint8_t low;
low = usb_read(reg);
return low | usb_read(reg) << 8;
}
static __bit get_descriptor(uint8_t type, uint8_t index, uint16_t length)
{
const uint8_t *reply;
uint8_t size;
debug("get_descriptor(0x%02x, 0x%02x, 0x%04x)\n", type, index, length);
switch (type) {
case USB_DT_DEVICE:
reply = device_descriptor;
size = reply[0];
break;
case USB_DT_CONFIG:
if (index)
return 0;
reply = config_descriptor;
size = reply[2];
break;
default:
if (!user_get_descriptor)
return 0;
if (!user_get_descriptor(type, index, &reply, &size))
return 0;
}
if (length < size)
size = length;
usb_send(&ep0, reply, size, NULL, NULL);
return 1;
}
/*
* Process a SETUP packet. Hardware ensures that length is 8 bytes.
*/
static void handle_setup(void)
{
struct setup_request setup;
__bit ok = 0;
BUG_ON(usb_read(E0CNT) < 8);
setup.bmRequestType = usb_read(FIFO0);
setup.bRequest = usb_read(FIFO0);
setup.wValue = usb_read_word(FIFO0);
setup.wIndex = usb_read_word(FIFO0);
setup.wLength = usb_read_word(FIFO0);
switch (setup.bmRequestType | setup.bRequest << 8) {
/*
* Device request
*
* See http://www.beyondlogic.org/usbnutshell/usb6.htm
*/
case FROM_DEVICE(GET_STATUS):
debug("GET_STATUS\n");
if (setup.wLength != 2)
goto stall;
usb_send(&ep0, "\000", 2, NULL, NULL);
ok = 1;
break;
case TO_DEVICE(CLEAR_FEATURE):
debug("CLEAR_FEATURE\n");
ok = 1;
break;
case TO_DEVICE(SET_FEATURE):
debug("SET_FEATURE\n");
break;
case TO_DEVICE(SET_ADDRESS):
debug("SET_ADDRESS (0x%x)\n", setup.wValue);
addr = setup.wValue;
ok = 1;
break;
case FROM_DEVICE(GET_DESCRIPTOR):
ok = get_descriptor(setup.wValue >> 8, setup.wValue,
setup.wLength);
break;
case TO_DEVICE(SET_DESCRIPTOR):
error("SET_DESCRIPTOR\n");
break;
case FROM_DEVICE(GET_CONFIGURATION):
debug("GET_CONFIGURATION\n");
usb_send(&ep0, "", 1, NULL, NULL);
ok = 1;
break;
case TO_DEVICE(SET_CONFIGURATION):
debug("SET_CONFIGURATION\n");
ok = setup.wValue == config_descriptor[5];
break;
/*
* Interface request
*/
case FROM_INTERFACE(GET_STATUS):
printk("GET_STATUS\n");
break;
case TO_INTERFACE(CLEAR_FEATURE):
printk("CLEAR_FEATURE\n");
break;
case TO_INTERFACE(SET_FEATURE):
printk("SET_FEATURE\n");
break;
case FROM_INTERFACE(GET_INTERFACE):
printk("GET_INTERFACE\n");
break;
case TO_INTERFACE(SET_INTERFACE):
debug("SET_INTERFACE\n");
{
uint8_t *interface_descriptor = config_descriptor+9;
ok = setup.wIndex == interface_descriptor[2] &&
setup.wValue == interface_descriptor[3];
}
break;
/*
* Endpoint request
*/
case FROM_ENDPOINT(GET_STATUS):
printk("GET_STATUS\n");
break;
case TO_ENDPOINT(CLEAR_FEATURE):
printk("CLEAR_FEATURE(EP)\n");
break;
case TO_ENDPOINT(SET_FEATURE):
printk("SET_FEATURE(EP)\n");
break;
case FROM_ENDPOINT(SYNCH_FRAME):
printk("SYNCH_FRAME\n");
break;
default:
if (user_setup) {
ok = user_setup(&setup);
/*
* If we "break" here instead of "goto", "EVELYN the
* modified DOG" gets furious, says SDCC.
*/
if (ok)
goto okay;
}
printk("Unrecognized SETUP(%02x %02x ...\n",
setup.bmRequestType, setup.bRequest);
}
if (ok) {
okay:
if ((setup.bmRequestType & 0x80) || ep0.state == EP_RX)
usb_write(E0CSR, SOPRDY);
else
usb_write(E0CSR, SOPRDY | DATAEND);
return;
}
stall:
printk("STALL\n");
usb_write(E0CSR, SDSTL_0);
ep0.state = EP_STALL;
}
static void ep0_data(void)
{
uint8_t fifo;
fifo = usb_read(E0CNT);
if (fifo > ep0.end-ep0.buf) {
usb_write(E0CSR, SDSTL_0);
return;
}
while (fifo--)
*ep0.buf++ = usb_read(FIFO0);
if (ep0.buf == ep0.end) {
ep0.state = EP_IDLE;
if (ep0.callback)
ep0.callback(ep0.user);
}
if (ep0.state == EP_IDLE)
usb_write(E0CSR, SOPRDY | DATAEND);
else
usb_write(E0CSR, SOPRDY);
}
static void handle_ep0(void)
{
uint8_t csr, size, left;
if (addr != NO_ADDRESS) {
usb_write(FADDR, addr);
debug("A");
addr = NO_ADDRESS;
}
csr = usb_read(E0CSR);
/* clear sent stall indication */
if (csr & STSTL_0) {
usb_write(E0CSR, 0);
/*
* @@@ Should return to IDLE, but this causes confusion with
* OPRDY_0. Need to investigate.
* ep0.state = EP_IDLE;
*/
}
/* if transaction was interrupted, clean up */
if (csr & SUEND) {
debug("S");
usb_write(E0CSR, DATAEND | SSUEND);
ep0.state = EP_IDLE;
}
if (csr & OPRDY_0) {
switch (ep0.state) {
case EP_IDLE:
handle_setup();
break;
case EP_RX:
ep0_data();
break;
default:
printk("??? %d\n", ep0.state);
break;
}
}
if (ep0.state != EP_TX)
return;
csr = usb_read(E0CSR);
if (csr & INPRDY_0)
return;
if (csr & (SUEND | OPRDY_0))
return;
size = ep0.end-ep0.buf;
if (size > EP0_SIZE)
size = EP0_SIZE;
for (left = size; left; left--)
usb_write(FIFO0, *ep0.buf++);
csr |= INPRDY_0;
if (size != EP0_SIZE) {
ep0.state = EP_IDLE;
csr |= DATAEND;
}
usb_write(E0CSR, csr);
if (ep0.state == EP_IDLE && ep0.callback)
ep0.callback(ep0.user);
}
#ifdef CONFIG_EP1
static void handle_ep1in(void)
{
uint8_t csrl;
csrl = usb_read(EINCSRL);
debug("handle_ep1in: EINCSRL 0x%02x\n", csrl);
if (csrl & UNDRUN)
csrl &= ~UNDRUN;
if (csrl & STSTL_IN)
csrl &= ~STSTL_IN;
usb_write(EINCSRL, csrl);
}
static void fill_ep1in(void)
{
uint8_t csrl, left;
uint16_t size;
if (ep1in.state != EP_TX)
return;
csrl = usb_read(EINCSRL);
debug("fill_ep1in: EINCSRL 0x%02x\n", csrl);
if (csrl & FIFONE)
return;
size = ep1in.end-ep1in.buf;
if (size > EP1_SIZE)
size = EP1_SIZE;
for (left = size; left; left--)
usb_write(FIFO1, *ep1in.buf++);
if (size != EP1_SIZE)
ep1in.state = EP_IDLE;
csrl |= INPRDY_IN;
usb_write(EINCSRL, csrl);
if (ep1in.state == EP_IDLE && ep1in.callback)
ep1in.callback(ep1in.user);
}
static void handle_ep1out(void)
{
uint8_t csrl, fifo;
csrl = usb_read(EOUTCSRL);
debug("EOUTCSRL 0x%02x\n", csrl);
if (csrl & OVRUN)
csrl &= ~OVRUN;
if (csrl & STSTL_OUT) {
csrl &= ~STSTL_OUT;
csrl |= CLRDT_OUT;
}
usb_write(EINCSRL, csrl);
if (!(csrl & OPRDY_OUT))
return;
if (ep1out.state != EP_RX) {
usb_write(EOUTCSRL, FLUSH_OUT);
return;
}
fifo = usb_read(EOUTCNTL);
if (fifo > ep1out.end-ep1out.buf) {
usb_write(EOUTCSRL, SDSTL_OUT | FLUSH_OUT);
return;
}
while (fifo--)
*ep1out.buf++ = usb_read(FIFO1);
ep1out.state = EP_IDLE;
if (ep1out.callback)
ep1out.callback(ep1out.user);
csrl &= ~OPRDY_OUT;
usb_write(EOUTCSRL, csrl);
}
#endif /* CONFIG_EP1 */
void usb_poll(void)
{
uint8_t flags;
flags = usb_read(CMINT);
if (flags) {
debug("CMINT 0x%02x\n", flags);
if (flags & RSTINT) {
ep0.state = EP_IDLE;
/*
* EP state serves as "buffer is valid" indicator for
* EP1OUT, so don't reset it. We need to call back
* EP1IN to tell it that the URB can be reused.
*
* (@@@ does this make sense ?)
*/
#ifdef CONFIG_EP1
if (ep1in.state == EP_TX && ep1in.callback)
ep1in.callback(ep1in.user);
ep1in.state = EP_IDLE;
#endif
usb_write(POWER, 0);
if (user_reset)
user_reset();
/* @@@ 1 for suspend signaling */
}
}
flags = usb_read(IN1INT);
if (flags) {
debug("IN1INT 0x%02x\n", flags);
if (flags & EP0) {
usb_write(INDEX, 0);
handle_ep0();
}
#ifdef CONFIG_EP1
if (flags & IN1) {
usb_write(INDEX, 1);
handle_ep1in();
}
#endif
}
#ifdef CONFIG_EP1
usb_write(INDEX, 1);
fill_ep1in();
#endif
#ifdef CONFIG_EP1
flags = usb_read(OUT1INT);
if (flags) {
debug("OUT1INT 0x%02x\n", flags);
usb_write(INDEX, 1);
handle_ep1out();
}
#endif
}
void usb_init(void)
{
usb_write(POWER, USBRST);
#ifdef LOW_SPEED
USB0XCN = PHYEN | PREN;
usb_write(CLKREC, CRE | CRLOW);
#else
USB0XCN = PHYEN | PREN | SPEED;
usb_write(CLKREC, CRE);
#endif
//usb_write(POWER, 0x01); /* we don't implement suspend yet */
usb_write(POWER, 0x00);
}

150
fw/common/usb.h Normal file
View File

@ -0,0 +1,150 @@
/*
* common/usb.h - USB hardware setup and standard device requests
*
* Written 2008, 2009 by Werner Almesberger
* Copyright 2008, 2009 Werner Almesberger
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef USB_H
#define USB_H
#include <stdint.h>
#include "config.h"
/*
* Descriptor types
*
* Reuse libusb naming scheme (/usr/include/usb.h)
*/
#define USB_DT_DEVICE 1
#define USB_DT_CONFIG 2
#define USB_DT_STRING 3
#define USB_DT_INTERFACE 4
#define USB_DT_ENDPOINT 5
/*
* Device classes
*
* Reuse libusb naming scheme (/usr/include/usb.h)
*/
#define USB_CLASS_PER_INTERFACE 0xfe
#define USB_CLASS_VENDOR_SPEC 0xff
/*
* Configuration attributes
*/
#define USB_ATTR_BUS_POWERED 0x80
#define USB_ATTR_SELF_POWERED 0x40
#define USB_ATTR_REMOTE_WAKEUP 0x20
/*
* Setup request types
*/
#define TO_DEVICE(req) (0x00 | (req) << 8)
#define FROM_DEVICE(req) (0x80 | (req) << 8)
#define TO_INTERFACE(req) (0x01 | (req) << 8)
#define FROM_INTERFACE(req) (0x81 | (req) << 8)
#define TO_ENDPOINT(req) (0x02 | (req) << 8)
#define FROM_ENDPOINT(req) (0x82 | (req) << 8)
/*
* Setup requests
*/
#define GET_STATUS 0x00
#define CLEAR_FEATURE 0x01
#define SET_FEATURE 0x03
#define SET_ADDRESS 0x05
#define GET_DESCRIPTOR 0x06
#define SET_DESCRIPTOR 0x07
#define GET_CONFIGURATION 0x08
#define SET_CONFIGURATION 0x09
#define GET_INTERFACE 0x0a
#define SET_INTERFACE 0x0b
#define SYNCH_FRAME 0x0c
/*
* Odd. sdcc seems to think "x" assumes the size of the destination, i.e.,
* uint8_t. Hence the cast.
*/
#define LE(x) ((uint16_t) (x) & 0xff), ((uint16_t) (x) >> 8)
#define LO(x) (((uint8_t *) &(x))[0])
#define HI(x) (((uint8_t *) &(x))[1])
#ifdef LOW_SPEED
#define EP0_SIZE 8
#else
#define EP0_SIZE 64
#endif
#define EP1_SIZE 64 /* simplify */
enum ep_state {
EP_IDLE,
EP_RX,
EP_TX,
EP_STALL,
};
struct ep_descr {
enum ep_state state;
uint8_t *buf;
uint8_t *end;
void (*callback)(void *user) __reentrant;
void *user;
};
struct setup_request {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
};
extern const uint8_t device_descriptor[];
extern const uint8_t config_descriptor[];
extern __xdata struct ep_descr ep0;
#ifdef CONFIG_EP1
extern __xdata struct ep_descr ep1in, ep1out;
#endif
extern __bit (*user_setup)(struct setup_request *setup) __reentrant;
extern __bit (*user_get_descriptor)(uint8_t type, uint8_t index,
const uint8_t * const *reply, uint8_t *size) __reentrant;
extern void (*user_reset)(void) __reentrant;
#define usb_left(ep) ((ep)->end-(ep)->buf)
#define usb_send(ep, buf, size, callback, user) \
usb_io(ep, EP_TX, buf, size, callback, user)
#define usb_recv(ep, buf, size, callback, user) \
usb_io(ep, EP_RX, buf, size, callback, user)
void usb_io(struct ep_descr *ep, enum ep_state state, uint8_t *buf,
uint8_t size, void (*callback)(void *user), void *user);
void usb_init(void);
void usb_poll(void);
#endif /* !USB_H */