mirror of
git://projects.qi-hardware.com/f32xbase.git
synced 2024-11-27 18:10:38 +02:00
fw/common/ - copied from IDBG and generalized a little
This commit is contained in:
parent
c55fc4018d
commit
bdfe058079
100
fw/common/Makefile.common
Normal file
100
fw/common/Makefile.common
Normal 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
45
fw/common/Makefile.system
Normal 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
77
fw/common/regs.h
Normal 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
125
fw/common/uart.c
Normal 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
46
fw/common/uart.h
Normal 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
101
fw/common/usb-regs.h
Normal 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
519
fw/common/usb.c
Normal 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
150
fw/common/usb.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user