From 920913d9e10e4addc9d12c834fbd827d2993edd6 Mon Sep 17 00:00:00 2001 From: david Date: Tue, 17 Feb 2009 09:36:51 +0100 Subject: [PATCH] o add usb test --- poc/lpc2148_efsl/conf/config.h | 2 +- poc/lpc2148_efsl/inc/fs.h | 2 +- poc/lpc2148_efsl/test/main.c | 70 ++- poc/lpc2148_usb/client/Makefile | 19 + poc/lpc2148_usb/client/main.c | 193 +++++++ poc/lpc2148_usb/target/Makefile | 52 ++ poc/lpc2148_usb/target/custom/Makefile | 55 ++ poc/lpc2148_usb/target/custom/console.c | 65 +++ poc/lpc2148_usb/target/custom/console.h | 5 + poc/lpc2148_usb/target/custom/crt.s | 110 ++++ poc/lpc2148_usb/target/custom/flash.ocd | 7 + poc/lpc2148_usb/target/custom/lpc2148-ram.ld | 197 +++++++ poc/lpc2148_usb/target/custom/lpc2148-rom.ld | 197 +++++++ poc/lpc2148_usb/target/custom/main_custom.c | 265 +++++++++ poc/lpc2148_usb/target/custom/printf.c | 268 +++++++++ poc/lpc2148_usb/target/custom/startup.c | 126 +++++ poc/lpc2148_usb/target/custom/startup.h | 2 + poc/lpc2148_usb/target/lpcusb.tmproj | 31 ++ poc/lpc2148_usb/target/type.h | 31 ++ poc/lpc2148_usb/target/usbapi.h | 95 ++++ poc/lpc2148_usb/target/usbcontrol.c | 222 ++++++++ poc/lpc2148_usb/target/usbdebug.h | 12 + poc/lpc2148_usb/target/usbhw_lpc.c | 541 +++++++++++++++++++ poc/lpc2148_usb/target/usbhw_lpc.h | 156 ++++++ poc/lpc2148_usb/target/usbinit.c | 69 +++ poc/lpc2148_usb/target/usbstdreq.c | 415 ++++++++++++++ poc/lpc2148_usb/target/usbstruct.h | 94 ++++ 27 files changed, 3285 insertions(+), 16 deletions(-) create mode 100644 poc/lpc2148_usb/client/Makefile create mode 100644 poc/lpc2148_usb/client/main.c create mode 100644 poc/lpc2148_usb/target/Makefile create mode 100644 poc/lpc2148_usb/target/custom/Makefile create mode 100644 poc/lpc2148_usb/target/custom/console.c create mode 100644 poc/lpc2148_usb/target/custom/console.h create mode 100644 poc/lpc2148_usb/target/custom/crt.s create mode 100644 poc/lpc2148_usb/target/custom/flash.ocd create mode 100644 poc/lpc2148_usb/target/custom/lpc2148-ram.ld create mode 100644 poc/lpc2148_usb/target/custom/lpc2148-rom.ld create mode 100644 poc/lpc2148_usb/target/custom/main_custom.c create mode 100644 poc/lpc2148_usb/target/custom/printf.c create mode 100644 poc/lpc2148_usb/target/custom/startup.c create mode 100644 poc/lpc2148_usb/target/custom/startup.h create mode 100644 poc/lpc2148_usb/target/lpcusb.tmproj create mode 100644 poc/lpc2148_usb/target/type.h create mode 100644 poc/lpc2148_usb/target/usbapi.h create mode 100644 poc/lpc2148_usb/target/usbcontrol.c create mode 100644 poc/lpc2148_usb/target/usbdebug.h create mode 100644 poc/lpc2148_usb/target/usbhw_lpc.c create mode 100644 poc/lpc2148_usb/target/usbhw_lpc.h create mode 100644 poc/lpc2148_usb/target/usbinit.c create mode 100644 poc/lpc2148_usb/target/usbstdreq.c create mode 100644 poc/lpc2148_usb/target/usbstruct.h diff --git a/poc/lpc2148_efsl/conf/config.h b/poc/lpc2148_efsl/conf/config.h index 02d0d88..c2f7977 100644 --- a/poc/lpc2148_efsl/conf/config.h +++ b/poc/lpc2148_efsl/conf/config.h @@ -15,7 +15,7 @@ */ /*#define HW_ENDPOINT_LINUX*/ - /*#define HW_ENDPOINT_ATMEGA128_SD*/ + /*#define HW_ENDPOINT_ATMEGA128_SD*/ #define HW_ENDPOINT_LPC2000_SD diff --git a/poc/lpc2148_efsl/inc/fs.h b/poc/lpc2148_efsl/inc/fs.h index 429e9d7..001d27e 100644 --- a/poc/lpc2148_efsl/inc/fs.h +++ b/poc/lpc2148_efsl/inc/fs.h @@ -166,7 +166,7 @@ struct FileRecord{ euint32 FileSize; }; #else -#warning "mt ATTR_PACKED active" +//#warning "mt ATTR_PACKED active" struct FileRecord{ euint8 FileName[11]; euint8 Attribute; diff --git a/poc/lpc2148_efsl/test/main.c b/poc/lpc2148_efsl/test/main.c index 003b74d..08c7ac5 100644 --- a/poc/lpc2148_efsl/test/main.c +++ b/poc/lpc2148_efsl/test/main.c @@ -18,6 +18,8 @@ #include "efs.h" #include "ls.h" #include "mkfs.h" +#include "types.h" +#include "debug.h" #include "interfaces/efsl_dbg_printf_arm.h" #define rprintf efsl_debug_printf_arm @@ -31,7 +33,7 @@ #define LEDDIR IODIR0 #define LEDSET IOSET0 #define LEDCLR IOCLR0 -static char LogFileName[] = "dummy.log"; +static char rom_filename[] = "SPRITE.SMC"; static void gpioInit(void) { @@ -53,6 +55,17 @@ DirList list; unsigned short e; unsigned char buf[513]; +void cleanup_name(filename,sfn){ + + while(*filename != '\0'){ + if(*filename=='.' && !dot){ + dot=1; + c=8; + }else{ + + +} + void list_roms(){ uint8_t cnt = 0; rprintf("Directory of 'root':\n"); @@ -78,13 +91,41 @@ uint8_t * get_filename(uint8_t idx){ return NULL; } +void dump_packet(uint32_t addr,uint32_t len,uint8_t *packet){ + uint16_t i,j; + uint16_t sum =0; + for (i=0;i=33 && packet[i+j]<=126 ) + DBG((TXT("%c"), packet[i+j])); + else + DBG((TXT("."))); + } + DBG((TXT("|\n"))); + + } +} + void dump_filename(uint8_t * filename){ + uint32_t cnt = 0; if (file_fopen(&filer, &efs.myFs, filename, 'r') == 0) { rprintf("File %s open. Content:\n", filename); while ((e = file_read(&filer, 512, buf)) != 0) { - buf[e] = '\0'; - uart0Puts((char *) buf); + dump_packet(cnt,e,buf); + cnt+=e; } + DBG((TXT("Len %08x(%li)\n"), cnt,cnt)); rprintf("\n"); file_fclose(&filer); } else { @@ -100,6 +141,7 @@ int main(void) int ch; int8_t res; uint8_t * filename; + uint8_t fatfilename[12]; Initialize(); gpioInit(); @@ -120,21 +162,21 @@ int main(void) uart0Puts("You pressed : "); uart0Putch(ch); uart0Puts("\r\n"); - if (ch == 'r') { - if (file_fopen(&filer, &efs.myFs, LogFileName, 'r') == 0) { - rprintf("File %s open. Content:\n", LogFileName); - while ((e = file_read(&filer, 512, buf)) != 0) { - buf[e] = '\0'; - uart0Puts((char *) buf); - } rprintf("\n"); - file_fclose(&filer); - } - } if (ch >='1' && ch <='9'){ filename = get_filename(ch - 48); - rprintf("Dump: %s\n",filename); + file_normalToFatName(filename,fatfilename); + rprintf("Filename: '%s'\n",filename); dump_filename(filename); + rprintf("Fatfilename: '%s'\n",fatfilename); + dump_filename(fatfilename); + file_normalToFatName("sprite.smc",fatfilename); + rprintf("Fatfilename: '%s'\n",fatfilename); + dump_filename(fatfilename); + file_normalToFatName("sprite .smc",fatfilename); + rprintf("Fatfilename: '%s'\n",fatfilename); + dump_filename(fatfilename); + //dump_filename(rom_filename); } ledToggle(); diff --git a/poc/lpc2148_usb/client/Makefile b/poc/lpc2148_usb/client/Makefile new file mode 100644 index 0000000..435e1dc --- /dev/null +++ b/poc/lpc2148_usb/client/Makefile @@ -0,0 +1,19 @@ +# app defs +EXE = custom_client +ifndef LIBUSB + LIBUSB = "/opt/local/" +endif + +# tool defs +CFLAGS = -W -Wall -g -I$(LIBUSB)/include +LIBS = -L$(LIBUSB)/lib -lusb + +all: $(EXE) + +$(EXE): main.o + $(CC) -o $(EXE) $< $(LIBS) + +clean: + $(RM) $(EXE) main.o + + diff --git a/poc/lpc2148_usb/client/main.c b/poc/lpc2148_usb/client/main.c new file mode 100644 index 0000000..30c1499 --- /dev/null +++ b/poc/lpc2148_usb/client/main.c @@ -0,0 +1,193 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + This library 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 2.1 of the License, or (at your option) any later version. + + This library 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 library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + Simple benchmarking application. + + It talks with the 'custom' device application on the LPC214x through + libusb. + +*/ + +#include +#include +#include + +#include "usb.h" + +// types +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif +typedef unsigned int U32; +typedef unsigned char U8; + +#define MAX_TIME 3000 + +static unsigned char abData[16384]; + +// USB device specific definitions +#define VENDOR_ID 0xFFFF +#define PRODUCT_ID 0x0004 + +#define BM_REQUEST_TYPE (2<<5) +#define BULK_IN_EP 0x82 +#define BULK_OUT_EP 0x05 + +// this structure should match with the expectations of the 'custom' device! +typedef struct { + U32 dwAddress; + U32 dwLength; +} TMemoryCmd; + + +static struct usb_device * find_device(int iVendor, int iProduct) +{ + struct usb_bus *usb_bus; + struct usb_device *dev; + + for (usb_bus = usb_get_busses(); usb_bus; usb_bus = usb_bus->next) { + for (dev = usb_bus->devices; dev; dev = dev->next) { + if ((dev->descriptor.idVendor == iVendor) && + (dev->descriptor.idProduct == iProduct)) { + return dev; + } + } + } + return NULL; +} + + +static struct timeb start; + +static void starttimer(void) +{ + ftime(&start); +} + +static int stoptimer(void) +{ + struct timeb now; + + ftime(&now); + return 1000 * (now.time - start.time) + now.millitm - start.millitm; +} + + +int main(int argc, char *argv[]) +{ + struct usb_device *dev; + struct usb_dev_handle *hdl; + int i, j; + U32 dwBlockSize, dwChunk, dwBytes; + TMemoryCmd MemCmd; + int iTimer; + + usb_init(); + usb_find_busses(); + usb_find_devices(); + + dev = find_device(VENDOR_ID, PRODUCT_ID); + if (dev == NULL) { + fprintf(stderr, "device not found\n"); + return -1; + } + + hdl = usb_open(dev); + + i = usb_set_configuration(hdl, 1); + if (i < 0) { + fprintf(stderr, "usb_set_configuration failed\n"); + } + + i = usb_claim_interface(hdl, 0); + if (i < 0) { + fprintf(stderr, "usb_claim_interface failed %d\n", i); + return -1; + } + + + // read some data + for (j = 6; j < 15; j++) { + dwBlockSize = (1 << j); + fprintf(stderr, "Testing blocksize %5d\n", dwBlockSize); + + fprintf(stderr, "* read :"); + // send a vendor request for a read + MemCmd.dwAddress = 0; + MemCmd.dwLength = 1024 * 1024; + i = usb_control_msg(hdl, BM_REQUEST_TYPE, 0x01, 0, 0, (char *)&MemCmd, sizeof(MemCmd), 1000); + if (i < 0) { + fprintf(stderr, "usb_control_msg failed %d\n", i); + } + dwBytes = 0; + starttimer(); + while (MemCmd.dwLength > 0) { + dwChunk = MIN(dwBlockSize, MemCmd.dwLength); + i = usb_bulk_read(hdl, 0x82, (char *)abData, dwBlockSize, 2000); + if (i < 0) { + fprintf(stderr, "usb_bulk_read failed %d\n", i); + break; + } + MemCmd.dwLength -= dwChunk; + dwBytes += dwBlockSize; + if (stoptimer() > MAX_TIME) { + break; + } + } + iTimer = stoptimer(); + fprintf(stderr, " %7d bytes in %d ms = %d kB/s\n", dwBytes, iTimer, dwBytes / iTimer); + // stdout + printf("%d,%d,%d,", dwBlockSize, dwBytes, iTimer); + + fprintf(stderr, "* write:"); + // send a vendor request for a write + MemCmd.dwAddress = 0; + MemCmd.dwLength = 1024 * 1024; + i = usb_control_msg(hdl, BM_REQUEST_TYPE, 0x02, 0, 0, (char *)&MemCmd, sizeof(MemCmd), 1000); + if (i < 0) { + fprintf(stderr, "usb_control_msg failed %d\n", i); + } + dwBytes = 0; + starttimer(); + while (MemCmd.dwLength > 0) { + dwChunk = MIN(dwBlockSize, MemCmd.dwLength); + i = usb_bulk_write(hdl, 0x05, (char *)abData, dwBlockSize, 2000); + if (i < 0) { + fprintf(stderr, "usb_bulk_read failed %d\n", i); + break; + } + MemCmd.dwLength -= dwChunk; + dwBytes += dwBlockSize; + if (stoptimer() > MAX_TIME) { + break; + } + } + fprintf(stderr, " %7d bytes in %d ms = %d kB/s\n", dwBytes, iTimer, dwBytes / iTimer); + // stdout + printf("%d,%d,%d\n", dwBlockSize, dwBytes, iTimer); + } + + + usb_release_interface(hdl, 0); + usb_close(hdl); + + return 0; +} + diff --git a/poc/lpc2148_usb/target/Makefile b/poc/lpc2148_usb/target/Makefile new file mode 100644 index 0000000..b88e779 --- /dev/null +++ b/poc/lpc2148_usb/target/Makefile @@ -0,0 +1,52 @@ +LIBNAME = usbstack + +# Package definitions +PKG_NAME = target +DATE = $$(date +%Y%m%d) + +# Tool definitions +CC = arm-elf-gcc +LD = arm-elf-ld -v +AR = arm-elf-ar +AS = arm-elf-as +CP = arm-elf-objcopy +OD = arm-elf-objdump +RM = rm +TAR = tar + +CFLAGS = -I./ -I../ -c -W -Wall -Os -g -DDEBUG -mcpu=arm7tdmi +ARFLAGS = -rcs + +LIBSRCS = usbhw_lpc.c usbcontrol.c usbstdreq.c usbinit.c +LIBOBJS = $(LIBSRCS:.c=.o) + +all: depend lib examples + +clean: + $(RM) -f $(LIBNAME).a $(LIBOBJS) .depend + make -C custom clean + +custom: + make -C custom + +# build lib +lib: $(LIBNAME).a + +$(LIBNAME).a: $(LIBOBJS) + $(AR) $(ARFLAGS) $@ $^ + +# Builds release tar file +dist: clean + cd .. && $(TAR) --exclude={CVS,cvs} -cvzf $(PKG_NAME)-$(DATE).tar.gz $(PKG_NAME) + +# recompile if the Makefile changes +$(LIBOBJS): Makefile + +# dependency checking +depend: $(LIBSRCS) + $(CC) $(CFLAGS) -MM $^ > .depend || rm -f .depend + +# phony targets +.PHONY: all clean examples depend + +-include .depend diff --git a/poc/lpc2148_usb/target/custom/Makefile b/poc/lpc2148_usb/target/custom/Makefile new file mode 100644 index 0000000..2e408e6 --- /dev/null +++ b/poc/lpc2148_usb/target/custom/Makefile @@ -0,0 +1,55 @@ +LIBNAME = ../usbstack +APPNAME = main + +# Tool definitions +CC = arm-elf-gcc +LD = arm-elf-ld -v +AR = arm-elf-ar +AS = arm-elf-as +CP = arm-elf-objcopy +OD = arm-elf-objdump +RM = rm + +# Tool flags +CFLAGS = -I./ -I../ -c -W -Wall -Os -g -DDEBUG -mcpu=arm7tdmi +ASFLAGS = -ahls -mapcs-32 -o crt.o +LFLAGS = -nostartfiles --warn-common +CPFLAGS = -O ihex +ODFLAGS = -x --syms + +LINKFILE = lpc2148-rom.ld + +CSRCS = startup.c printf.c console.c +OBJS = crt.o $(CSRCS:.c=.o) + +EXAMPLES = custom + +all: depend $(EXAMPLES) + +custom: $(OBJS) main_custom.o $(LIBNAME).a + +$(EXAMPLES): + @ echo "Building $@ example..." + $(CC) -T $(LINKFILE) $(LFLAGS) $^ -o $@.elf -Wl,-Map,$@.map + $(CP) $(CPFLAGS) $@.elf $@.hex + $(CP) -O binary $@.elf $@.bin + $(OD) $(ODFLAGS) $@.elf > $@.dmp +crt.o: crt.s + @ echo ".assembling" + $(CC) -c $(AFLAGS) -Wa,-ahlms=crt.lst crt.s -o crt.o + +clean: + rm -f *.hex *.elf *.o *.lst *.dmp *.map .depend + +# recompile if the Makefile changes +$(OBJS): Makefile + +# dependency checking +depend: $(CSRCS) + $(CC) $(CFLAGS) -MM $^ > .depend || rm -f .depend + +# phony targets +.PHONY: clean + +-include .depend + diff --git a/poc/lpc2148_usb/target/custom/console.c b/poc/lpc2148_usb/target/custom/console.c new file mode 100644 index 0000000..22d6e3f --- /dev/null +++ b/poc/lpc2148_usb/target/custom/console.c @@ -0,0 +1,65 @@ +/* + Simple console input/output, over serial port #0 + + Partially copied from Jim Lynch's tutorial +*/ + +#include "console.h" + +#define PINSEL0 *(volatile unsigned int *)0xE002C000 + +#define U0THR *(volatile unsigned int *)0xE000C000 +#define U0RBR *(volatile unsigned int *)0xE000C000 +#define U0DLL *(volatile unsigned int *)0xE000C000 +#define U0DLM *(volatile unsigned int *)0xE000C004 +#define U0FCR *(volatile unsigned int *)0xE000C008 +#define U0LCR *(volatile unsigned int *)0xE000C00C +#define U0LSR *(volatile unsigned int *)0xE000C014 + + +/* Initialize Serial Interface */ +void ConsoleInit(int iDivider) +{ + PINSEL0 = (PINSEL0 & ~0x0000000F) | 0x00000005; /* Enable RxD0 and TxD0 */ + U0LCR = 0x83; /* 8 bits, no Parity, 1 Stop bit */ + U0DLL = iDivider & 0xFF; /* set divider / baud rate */ + U0DLM = iDivider >> 8; + U0LCR = 0x03; /* DLAB = 0 */ + + // enable FIFO + U0FCR = 1; +} + + +/* Write character to Serial Port */ +int putchar(int ch) +{ + if (ch == '\n') { + while (!(U0LSR & 0x20)); + U0THR = '\r'; + } + while (!(U0LSR & 0x20)); + U0THR = ch; + + return ch; +} + + +int getchar (void) { /* Read character from Serial Port */ + + while (!(U0LSR & 0x01)); + + return (U0RBR); +} + + +int puts(char *s) +{ + while (*s) { + putchar(*s++); + } + putchar('\n'); + return 1; +} + + diff --git a/poc/lpc2148_usb/target/custom/console.h b/poc/lpc2148_usb/target/custom/console.h new file mode 100644 index 0000000..62e3b80 --- /dev/null +++ b/poc/lpc2148_usb/target/custom/console.h @@ -0,0 +1,5 @@ +void ConsoleInit(int iDivider); +int putchar(int c); +int puts(char *s); + + diff --git a/poc/lpc2148_usb/target/custom/crt.s b/poc/lpc2148_usb/target/custom/crt.s new file mode 100644 index 0000000..78de943 --- /dev/null +++ b/poc/lpc2148_usb/target/custom/crt.s @@ -0,0 +1,110 @@ +/* *************************************************************************************************************** + + crt.s STARTUP ASSEMBLY CODE + ----------------------- + + + Module includes the interrupt vectors and start-up code. + + *************************************************************************************************************** */ + +/* Stack Sizes */ +.set UND_STACK_SIZE, 0x00000040 /* stack for "undefined instruction" interrupts is 4 bytes */ +.set ABT_STACK_SIZE, 0x00000040 /* stack for "abort" interrupts is 4 bytes */ +.set FIQ_STACK_SIZE, 0x00000040 /* stack for "FIQ" interrupts is 4 bytes */ +.set IRQ_STACK_SIZE, 0X00000040 /* stack for "IRQ" normal interrupts is 4 bytes */ +.set SVC_STACK_SIZE, 0x00000400 /* stack for "SVC" supervisor mode is 4 bytes */ + + + +/* Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs (program status registers) */ +.set MODE_USR, 0x10 /* Normal User Mode */ +.set MODE_FIQ, 0x11 /* FIQ Processing Fast Interrupts Mode */ +.set MODE_IRQ, 0x12 /* IRQ Processing Standard Interrupts Mode */ +.set MODE_SVC, 0x13 /* Supervisor Processing Software Interrupts Mode */ +.set MODE_ABT, 0x17 /* Abort Processing memory Faults Mode */ +.set MODE_UND, 0x1B /* Undefined Processing Undefined Instructions Mode */ +.set MODE_SYS, 0x1F /* System Running Priviledged Operating System Tasks Mode */ + +.set I_BIT, 0x80 /* when I bit is set, IRQ is disabled (program status registers) */ +.set F_BIT, 0x40 /* when F bit is set, FIQ is disabled (program status registers) */ + + +.text +.arm + +.global Reset_Handler +.global _startup +.func _startup + +_startup: + +# Exception Vectors + +_vectors: ldr PC, Reset_Addr + ldr PC, Undef_Addr + ldr PC, SWI_Addr + ldr PC, PAbt_Addr + ldr PC, DAbt_Addr + nop /* Reserved Vector (holds Philips ISP checksum) */ + ldr PC, [PC,#-0xFF0] /* see page 71 of "Insiders Guide to the Philips ARM7-Based Microcontrollers" by Trevor Martin */ + ldr PC, FIQ_Addr + +Reset_Addr: .word Reset_Handler /* defined in this module below */ +Undef_Addr: .word UNDEF_Routine /* defined in main.c */ +SWI_Addr: .word SWI_Routine /* defined in main.c */ +PAbt_Addr: .word UNDEF_Routine /* defined in main.c */ +DAbt_Addr: .word UNDEF_Routine /* defined in main.c */ +IRQ_Addr: .word IRQ_Routine /* defined in main.c */ +FIQ_Addr: .word FIQ_Routine /* defined in main.c */ + .word 0 /* rounds the vectors and ISR addresses to 64 bytes total */ + + +# Reset Handler + +Reset_Handler: + + /* Setup a stack for each mode - note that this only sets up a usable stack + for User mode. Also each mode is setup with interrupts initially disabled. */ + + ldr r0, =_stack_end + msr CPSR_c, #MODE_UND|I_BIT|F_BIT /* Undefined Instruction Mode */ + mov sp, r0 + sub r0, r0, #UND_STACK_SIZE + msr CPSR_c, #MODE_ABT|I_BIT|F_BIT /* Abort Mode */ + mov sp, r0 + sub r0, r0, #ABT_STACK_SIZE + msr CPSR_c, #MODE_FIQ|I_BIT|F_BIT /* FIQ Mode */ + mov sp, r0 + sub r0, r0, #FIQ_STACK_SIZE + msr CPSR_c, #MODE_IRQ|I_BIT|F_BIT /* IRQ Mode */ + mov sp, r0 + sub r0, r0, #IRQ_STACK_SIZE + msr CPSR_c, #MODE_SVC|I_BIT|F_BIT /* Supervisor Mode */ + mov sp, r0 + sub r0, r0, #SVC_STACK_SIZE + msr CPSR_c, #MODE_SYS|I_BIT|F_BIT /* User Mode */ + mov sp, r0 + + /* copy .data section (Copy from ROM to RAM) */ + ldr R1, =_etext + ldr R2, =_data + ldr R3, =_edata +1: cmp R2, R3 + ldrlo R0, [R1], #4 + strlo R0, [R2], #4 + blo 1b + + /* Clear .bss section (Zero init) */ + mov R0, #0 + ldr R1, =_bss_start + ldr R2, =_bss_end +2: cmp R1, R2 + strlo R0, [R1], #4 + blo 2b + + /* Enter the C code */ + b main + +.endfunc +.end diff --git a/poc/lpc2148_usb/target/custom/flash.ocd b/poc/lpc2148_usb/target/custom/flash.ocd new file mode 100644 index 0000000..89af7eb --- /dev/null +++ b/poc/lpc2148_usb/target/custom/flash.ocd @@ -0,0 +1,7 @@ +halt +wait_halt +sleep 10 +poll +flash erase_sector 0 0 14 +flash write_bank 0 /Users/david/Devel/arch/arm/code/lpcusb/examples/custom.bin 0 +reset run diff --git a/poc/lpc2148_usb/target/custom/lpc2148-ram.ld b/poc/lpc2148_usb/target/custom/lpc2148-ram.ld new file mode 100644 index 0000000..4555cce --- /dev/null +++ b/poc/lpc2148_usb/target/custom/lpc2148-ram.ld @@ -0,0 +1,197 @@ +/* ****************************************************************************************************** */ +/* demo2148_blink_flash.cmd LINKER SCRIPT */ +/* */ +/* */ +/* The Linker Script defines how the code and data emitted by the GNU C compiler and assembler are */ +/* to be loaded into memory (code goes into FLASH, variables go into RAM). */ +/* */ +/* Any symbols defined in the Linker Script are automatically global and available to the rest of the */ +/* program. */ +/* */ +/* To force the linker to use this LINKER SCRIPT, just add the -T demo2148_blink_flash.cmd directive */ +/* to the linker flags in the makefile. */ +/* */ +/* LFLAGS = -Map main.map -nostartfiles -T demo2148_blink_flash.cmd */ +/* */ +/* */ +/* The Philips boot loader supports the ISP (In System Programming) via the serial port and the IAP */ +/* (In Application Programming) for flash programming from within your application. */ +/* */ +/* The boot loader uses RAM memory and we MUST NOT load variables or code in these areas. */ +/* */ +/* RAM used by boot loader: 0x40000120 - 0x400001FF (223 bytes) for ISP variables */ +/* 0x40007FE0 - 0x4000FFFF (32 bytes) for ISP and IAP variables */ +/* 0x40007EE0 - 0x40007FE0 (256 bytes) stack for ISP and IAP */ +/* */ +/* */ +/* MEMORY MAP */ +/* | |0x40008000 */ +/* .-------->|---------------------------------| */ +/* . | variables and stack |0x40007FFF */ +/* ram_isp_high | for Philips boot loader | */ +/* . | 32 + 256 = 288 bytes | */ +/* . | | */ +/* . | Do not put anything here |0x40007EE0 */ +/* .-------->|---------------------------------| */ +/* | UDF Stack 4 bytes |0x40007EDC <---------- _stack_end */ +/* .-------->|---------------------------------| */ +/* | ABT Stack 4 bytes |0x40007ED8 */ +/* .-------->|---------------------------------| */ +/* | FIQ Stack 4 bytes |0x40007ED4 */ +/* .-------->|---------------------------------| */ +/* | IRQ Stack 4 bytes |0x40007ED0 */ +/* .-------->|---------------------------------| */ +/* | SVC Stack 4 bytes |0x40007ECC */ +/* .-------->|---------------------------------| */ +/* . | |0x40007EC8 */ +/* . | stack area for user program | */ +/* . | | | */ +/* . | | | */ +/* . | | | */ +/* . | V | */ +/* . | | */ +/* . | | */ +/* . | | */ +/* . | free ram | */ +/* ram | | */ +/* . | | */ +/* . | | */ +/* . |.................................|0x40000234 <---------- _bss_end */ +/* . | | */ +/* . | .bss uninitialized variables | */ +/* . |.................................|0x40000218 <---------- _bss_start, _edata */ +/* . | | */ +/* . | .data initialized variables | */ +/* . | |0x40000200 <---------- _data */ +/* .-------->|---------------------------------| */ +/* . | variables used by |0x400001FF */ +/* ram_isp_low | Philips boot loader | */ +/* . | 223 bytes |0x40000120 */ +/* .-------->|---------------------------------| */ +/* . | |0x4000011F */ +/* ram_vectors | free ram | */ +/* . |---------------------------------|0x40000040 */ +/* . | |0x4000003F */ +/* . | Interrupt Vectors (re-mapped) | */ +/* . | 64 bytes |0x40000000 */ +/* .-------->|---------------------------------| */ +/* | | */ +/* */ +/* */ +/* */ +/* | | */ +/* .--------> |---------------------------------| */ +/* . | |0x0001FFFF */ +/* . | | */ +/* . | | */ +/* . | | */ +/* . | | */ +/* . | | */ +/* . | unused flash eprom | */ +/* . | | */ +/* . |.................................|0x0000032c */ +/* . | | */ +/* . | copy of .data area | */ +/* flash | | */ +/* . |---------------------------------|0x00000314 <----------- _etext */ +/* . | | */ +/* . | |0x00000180 main */ +/* . | |0x00000278 feed */ +/* . | main() |0x000002c4 FIQ_Routine */ +/* . | |0x000002d8 SWI_Routine */ +/* . | |0x000002ec UNDEF_Routine */ +/* . | |0x000002b0 IRQ_routine */ +/* . |---------------------------------|0x000001cc initialize */ +/* . | |0x000000D4 */ +/* . | Startup Code | */ +/* . | (assembler) | */ +/* . | | */ +/* . |---------------------------------|0x00000040 Reset_Handler */ +/* . | |0x0000003F */ +/* . | Interrupt Vector Table (unused) | */ +/* . | 64 bytes | */ +/* .--------->|---------------------------------|0x00000000 _startup * +/* */ +/* */ +/* The easy way to prevent the linker from loading anything into a memory area is to define */ +/* a MEMORY region for it and then avoid assigning any .text, .data or .bss sections into it. */ +/* */ +/* */ +/* MEMORY */ +/* { */ +/* ram_isp_low(A) : ORIGIN = 0x40000120, LENGTH = 223 */ +/* */ +/* } */ +/* */ +/* */ +/* Author: James P. Lynch */ +/* */ +/* ****************************************************************************************************** */ + + +/* identify the Entry Point */ + +ENTRY(_startup) + + + +/* specify the LPC2148 memory areas */ + +MEMORY +{ + flash : ORIGIN = 0, LENGTH = 512K /* FLASH ROM */ + ram_isp_low(A) : ORIGIN = 0x40000120, LENGTH = 223 /* variables used by Philips ISP bootloader */ + ram : ORIGIN = 0x40000200, LENGTH = 32513 /* free RAM area */ + ram_isp_high(A) : ORIGIN = 0x40007FE0, LENGTH = 32 /* variables used by Philips ISP bootloader */ + ram_usb_dma : ORIGIN = 0x7FD00000, LENGTH = 8192 /* on-chip USB DMA RAM area (not used) */ +} + + + +/* define a global symbol _stack_end */ + +_stack_end = 0x40007EDC; + + + +/* now define the output sections */ + +SECTIONS +{ + . = 0; /* set location counter to address zero */ + + startup : { *(.startup)} >ram /* the startup code goes into FLASH */ + + + + .text : /* collect all sections that should go into FLASH after startup */ + { + *(.text) /* all .text sections (code) */ + *(.rodata) /* all .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* all .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* all .glue_7 sections (no idea what these are) */ + *(.glue_7t) /* all .glue_7t sections (no idea what these are) */ + _etext = .; /* define a global symbol _etext just after the last code byte */ + } >ram /* put all the above into FLASH */ + + + + + .data : /* collect all initialized .data sections that go into RAM */ + { + _data = .; /* create a global symbol marking the start of the .data section */ + *(.data) /* all .data sections */ + _edata = .; /* define a global symbol marking the end of the .data section */ + } >ram AT >ram /* put all the above into RAM (but load the LMA copy into FLASH) */ + + .bss : /* collect all uninitialized .bss sections that go into RAM */ + { + _bss_start = .; /* define a global symbol marking the start of the .bss section */ + *(.bss) /* all .bss sections */ + } >ram /* put all the above in RAM (it will be cleared in the startup code */ + + . = ALIGN(4); /* advance location counter to the next 32-bit boundary */ + _bss_end = . ; /* define a global symbol marking the end of the .bss section */ +} + _end = .; /* define a global symbol marking the end of application RAM */ + diff --git a/poc/lpc2148_usb/target/custom/lpc2148-rom.ld b/poc/lpc2148_usb/target/custom/lpc2148-rom.ld new file mode 100644 index 0000000..5be7ede --- /dev/null +++ b/poc/lpc2148_usb/target/custom/lpc2148-rom.ld @@ -0,0 +1,197 @@ +/* ****************************************************************************************************** */ +/* demo2148_blink_flash.cmd LINKER SCRIPT */ +/* */ +/* */ +/* The Linker Script defines how the code and data emitted by the GNU C compiler and assembler are */ +/* to be loaded into memory (code goes into FLASH, variables go into RAM). */ +/* */ +/* Any symbols defined in the Linker Script are automatically global and available to the rest of the */ +/* program. */ +/* */ +/* To force the linker to use this LINKER SCRIPT, just add the -T demo2148_blink_flash.cmd directive */ +/* to the linker flags in the makefile. */ +/* */ +/* LFLAGS = -Map main.map -nostartfiles -T demo2148_blink_flash.cmd */ +/* */ +/* */ +/* The Philips boot loader supports the ISP (In System Programming) via the serial port and the IAP */ +/* (In Application Programming) for flash programming from within your application. */ +/* */ +/* The boot loader uses RAM memory and we MUST NOT load variables or code in these areas. */ +/* */ +/* RAM used by boot loader: 0x40000120 - 0x400001FF (223 bytes) for ISP variables */ +/* 0x40007FE0 - 0x4000FFFF (32 bytes) for ISP and IAP variables */ +/* 0x40007EE0 - 0x40007FE0 (256 bytes) stack for ISP and IAP */ +/* */ +/* */ +/* MEMORY MAP */ +/* | |0x40008000 */ +/* .-------->|---------------------------------| */ +/* . | variables and stack |0x40007FFF */ +/* ram_isp_high | for Philips boot loader | */ +/* . | 32 + 256 = 288 bytes | */ +/* . | | */ +/* . | Do not put anything here |0x40007EE0 */ +/* .-------->|---------------------------------| */ +/* | UDF Stack 4 bytes |0x40007EDC <---------- _stack_end */ +/* .-------->|---------------------------------| */ +/* | ABT Stack 4 bytes |0x40007ED8 */ +/* .-------->|---------------------------------| */ +/* | FIQ Stack 4 bytes |0x40007ED4 */ +/* .-------->|---------------------------------| */ +/* | IRQ Stack 4 bytes |0x40007ED0 */ +/* .-------->|---------------------------------| */ +/* | SVC Stack 4 bytes |0x40007ECC */ +/* .-------->|---------------------------------| */ +/* . | |0x40007EC8 */ +/* . | stack area for user program | */ +/* . | | | */ +/* . | | | */ +/* . | | | */ +/* . | V | */ +/* . | | */ +/* . | | */ +/* . | | */ +/* . | free ram | */ +/* ram | | */ +/* . | | */ +/* . | | */ +/* . |.................................|0x40000234 <---------- _bss_end */ +/* . | | */ +/* . | .bss uninitialized variables | */ +/* . |.................................|0x40000218 <---------- _bss_start, _edata */ +/* . | | */ +/* . | .data initialized variables | */ +/* . | |0x40000200 <---------- _data */ +/* .-------->|---------------------------------| */ +/* . | variables used by |0x400001FF */ +/* ram_isp_low | Philips boot loader | */ +/* . | 223 bytes |0x40000120 */ +/* .-------->|---------------------------------| */ +/* . | |0x4000011F */ +/* ram_vectors | free ram | */ +/* . |---------------------------------|0x40000040 */ +/* . | |0x4000003F */ +/* . | Interrupt Vectors (re-mapped) | */ +/* . | 64 bytes |0x40000000 */ +/* .-------->|---------------------------------| */ +/* | | */ +/* */ +/* */ +/* */ +/* | | */ +/* .--------> |---------------------------------| */ +/* . | |0x0001FFFF */ +/* . | | */ +/* . | | */ +/* . | | */ +/* . | | */ +/* . | | */ +/* . | unused flash eprom | */ +/* . | | */ +/* . |.................................|0x0000032c */ +/* . | | */ +/* . | copy of .data area | */ +/* flash | | */ +/* . |---------------------------------|0x00000314 <----------- _etext */ +/* . | | */ +/* . | |0x00000180 main */ +/* . | |0x00000278 feed */ +/* . | main() |0x000002c4 FIQ_Routine */ +/* . | |0x000002d8 SWI_Routine */ +/* . | |0x000002ec UNDEF_Routine */ +/* . | |0x000002b0 IRQ_routine */ +/* . |---------------------------------|0x000001cc initialize */ +/* . | |0x000000D4 */ +/* . | Startup Code | */ +/* . | (assembler) | */ +/* . | | */ +/* . |---------------------------------|0x00000040 Reset_Handler */ +/* . | |0x0000003F */ +/* . | Interrupt Vector Table (unused) | */ +/* . | 64 bytes | */ +/* .--------->|---------------------------------|0x00000000 _startup * +/* */ +/* */ +/* The easy way to prevent the linker from loading anything into a memory area is to define */ +/* a MEMORY region for it and then avoid assigning any .text, .data or .bss sections into it. */ +/* */ +/* */ +/* MEMORY */ +/* { */ +/* ram_isp_low(A) : ORIGIN = 0x40000120, LENGTH = 223 */ +/* */ +/* } */ +/* */ +/* */ +/* Author: James P. Lynch */ +/* */ +/* ****************************************************************************************************** */ + + +/* identify the Entry Point */ + +ENTRY(_startup) + + + +/* specify the LPC2148 memory areas */ + +MEMORY +{ + flash : ORIGIN = 0, LENGTH = 512K /* FLASH ROM */ + ram_isp_low(A) : ORIGIN = 0x40000120, LENGTH = 223 /* variables used by Philips ISP bootloader */ + ram : ORIGIN = 0x40000200, LENGTH = 32513 /* free RAM area */ + ram_isp_high(A) : ORIGIN = 0x40007FE0, LENGTH = 32 /* variables used by Philips ISP bootloader */ + ram_usb_dma : ORIGIN = 0x7FD00000, LENGTH = 8192 /* on-chip USB DMA RAM area (not used) */ +} + + + +/* define a global symbol _stack_end */ + +_stack_end = 0x40007EDC; + + + +/* now define the output sections */ + +SECTIONS +{ + . = 0; /* set location counter to address zero */ + + startup : { *(.startup)} >flash /* the startup code goes into FLASH */ + + + + .text : /* collect all sections that should go into FLASH after startup */ + { + *(.text) /* all .text sections (code) */ + *(.rodata) /* all .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* all .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* all .glue_7 sections (no idea what these are) */ + *(.glue_7t) /* all .glue_7t sections (no idea what these are) */ + _etext = .; /* define a global symbol _etext just after the last code byte */ + } >flash /* put all the above into FLASH */ + + + + + .data : /* collect all initialized .data sections that go into RAM */ + { + _data = .; /* create a global symbol marking the start of the .data section */ + *(.data) /* all .data sections */ + _edata = .; /* define a global symbol marking the end of the .data section */ + } >ram AT >flash /* put all the above into RAM (but load the LMA copy into FLASH) */ + + .bss : /* collect all uninitialized .bss sections that go into RAM */ + { + _bss_start = .; /* define a global symbol marking the start of the .bss section */ + *(.bss) /* all .bss sections */ + } >ram /* put all the above in RAM (it will be cleared in the startup code */ + + . = ALIGN(4); /* advance location counter to the next 32-bit boundary */ + _bss_end = . ; /* define a global symbol marking the end of the .bss section */ +} + _end = .; /* define a global symbol marking the end of application RAM */ + diff --git a/poc/lpc2148_usb/target/custom/main_custom.c b/poc/lpc2148_usb/target/custom/main_custom.c new file mode 100644 index 0000000..8dc4dd8 --- /dev/null +++ b/poc/lpc2148_usb/target/custom/main_custom.c @@ -0,0 +1,265 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + This library 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 2.1 of the License, or (at your option) any later version. + + This library 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 library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + This is a very simple custom device (not belonging to a specific USB + class). It implements primitive read and write in the ARM memory space. + + Each transfer is initiated by a control transfer to inform the device + about the address and size of the following data transfer. + The data transfer takes place over a bulk endpoint (BULK_IN_EP for + reads and BULK_OUT_EP for writes). + + This example can be used to measure USB transfer speed. +*/ + +#include "type.h" +#include "usbdebug.h" + +#include "console.h" +#include "usbapi.h" +#include "startup.h" + + +#define BULK_IN_EP 0x82 +#define BULK_OUT_EP 0x05 + +#define MAX_PACKET_SIZE 64 + +#define LE_WORD(x) ((x)&0xFF),((x)>>8) + + +static const U8 abDescriptors[] = { + +/* Device descriptor */ + 0x12, + DESC_DEVICE, + LE_WORD(0x0200), // bcdUSB + 0xFF, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + MAX_PACKET_SIZE0, // bMaxPacketSize + LE_WORD(0xFFFF), // idVendor + LE_WORD(0x0004), // idProduct + LE_WORD(0x0100), // bcdDevice + 0x01, // iManufacturer + 0x02, // iProduct + 0x03, // iSerialNumber + 0x01, // bNumConfigurations + +// configuration + 0x09, + DESC_CONFIGURATION, + LE_WORD(0x20), // wTotalLength + 0x01, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0x80, // bmAttributes + 0x32, // bMaxPower + +// interface + 0x09, + DESC_INTERFACE, + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndPoints + 0xFF, // bInterfaceClass + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol + 0x00, // iInterface + +// bulk in + 0x07, + DESC_ENDPOINT, + BULK_IN_EP, // bEndpointAddress + 0x02, // bmAttributes = BULK + LE_WORD(MAX_PACKET_SIZE),// wMaxPacketSize + 0, // bInterval + +// bulk out + 0x07, + DESC_ENDPOINT, + BULK_OUT_EP, // bEndpointAddress + 0x02, // bmAttributes = BULK + LE_WORD(MAX_PACKET_SIZE),// wMaxPacketSize + 0, // bInterval + +// string descriptors + 0x04, + DESC_STRING, + LE_WORD(0x0409), + + // manufacturer string + 0x0E, + DESC_STRING, + 'L', 0, 'P', 0, 'C', 0, 'U', 0, 'S', 0, 'B', 0, + + // product string + 0x1A, + DESC_STRING, + 'M', 0, 'e', 0, 'm', 0, 'o', 0, 'r', 0, 'y', 0, 'A', 0, 'c', 0, 'c', 0, 'e', 0, 's', 0, 's', 0, + + // serial number string + 0x12, + DESC_STRING, + 'D', 0, 'E', 0, 'A', 0, 'D', 0, 'C', 0, '0', 0, 'D', 0, 'E', 0, + + // terminator + 0 +}; + + +typedef struct { + U32 dwAddress; + U32 dwLength; +} TMemoryCmd; + + +static TMemoryCmd MemoryCmd; +static U8 abVendorReqData[sizeof(TMemoryCmd)]; + + +static void _HandleBulkIn(U8 bEP, U8 bEPStatus) +{ + int iChunk; + + iChunk = MIN(MAX_PACKET_SIZE, MemoryCmd.dwLength); + if (iChunk == 0) { + DBG("_HandleBulkIn done\n"); + return; + } + + // send next part + USBHwEPWrite(bEP, (U8 *)MemoryCmd.dwAddress, iChunk); + + MemoryCmd.dwAddress += iChunk; + MemoryCmd.dwLength -= iChunk; + + // limit address range to prevent abort + MemoryCmd.dwAddress &= ~(-512 * 1024); +} + + +static void _HandleBulkOut(U8 bEP, U8 bEPStatus) +{ + int iChunk; + + // get next part + iChunk = USBHwEPRead(bEP, NULL, 0); + + MemoryCmd.dwAddress += iChunk; + MemoryCmd.dwLength -= iChunk; + + if (MemoryCmd.dwLength == 0) { + DBG("_HandleBulkOut done\n"); + } +} + + +/************************************************************************* + HandleVendorRequest + =================== + Handles vendor specific requests + + Control transfer fields: + * request: 0x01 = prepare memory read + 0x02 = prepare memory write + * index: ignored + * value: ignored + * data: U32 dwAddress + U32 dwLength + +**************************************************************************/ +static BOOL HandleVendorRequest(TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + TMemoryCmd *pCmd; + + pCmd = (TMemoryCmd *)*ppbData; + + switch (pSetup->bRequest) { + + // prepare read + case 0x01: + MemoryCmd = *pCmd; + DBG("READ: addr=%X, len=%d\n", MemoryCmd.dwAddress, MemoryCmd.dwLength); + // send initial packet + _HandleBulkIn(BULK_IN_EP, 0); + *piLen = 0; + break; + + // prepare write + case 0x02: + MemoryCmd = *pCmd; + DBG("WRITE: addr=%X, len=%d\n", MemoryCmd.dwAddress, MemoryCmd.dwLength); + *piLen = 0; + break; + + default: + DBG("Unhandled class %X\n", pSetup->bRequest); + return FALSE; + } + return TRUE; +} + + + + +#define BAUD_RATE 57600 + + +/************************************************************************* + main + ==== +**************************************************************************/ +int main(void) +{ + // PLL and MAM + Initialize(); + + // init DBG + ConsoleInit(60000000 / (16 * BAUD_RATE)); + + DBG("Initialising USB stack\n"); + + // initialise stack + USBInit(); + + // register device descriptors + USBRegisterDescriptors(abDescriptors); + + // override standard request handler + USBRegisterRequestHandler(REQTYPE_TYPE_VENDOR, HandleVendorRequest, abVendorReqData); + + // register endpoints + USBHwRegisterEPIntHandler(BULK_IN_EP, _HandleBulkIn); + USBHwRegisterEPIntHandler(BULK_OUT_EP, _HandleBulkOut); + + DBG("Starting USB communication\n"); + + // connect to bus + USBHwConnect(TRUE); + + // call USB interrupt handler continuously + while (1) { + USBHwISR(); + } + + return 0; +} + diff --git a/poc/lpc2148_usb/target/custom/printf.c b/poc/lpc2148_usb/target/custom/printf.c new file mode 100644 index 0000000..768d4c1 --- /dev/null +++ b/poc/lpc2148_usb/target/custom/printf.c @@ -0,0 +1,268 @@ +/* + Copyright 2001, 2002 Georges Menie (www.menie.org) + stdarg version contributed by Christian Ettinger + + 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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* + putchar is the only external dependency for this file, + if you have a working putchar, leave it commented out. + If not, uncomment the define below and + replace outbyte(c) by your own function call. + +#define putchar(c) outbyte(c) +*/ + +#include +#include "console.h" + + +static void printchar(char **str, int c) +{ + if (str) { + **str = c; + ++(*str); + } + else { + putchar(c); + } +} + +#define PAD_RIGHT 1 +#define PAD_ZERO 2 + +static int prints(char **out, const char *string, int width, int pad) +{ + register int pc = 0, padchar = ' '; + + if (width > 0) { + register int len = 0; + register const char *ptr; + for (ptr = string; *ptr; ++ptr) ++len; + if (len >= width) width = 0; + else width -= len; + if (pad & PAD_ZERO) padchar = '0'; + } + if (!(pad & PAD_RIGHT)) { + for ( ; width > 0; --width) { + printchar (out, padchar); + ++pc; + } + } + for ( ; *string ; ++string) { + printchar (out, *string); + ++pc; + } + for ( ; width > 0; --width) { + printchar (out, padchar); + ++pc; + } + + return pc; +} + +/* the following should be enough for 32 bit int */ +#define PRINT_BUF_LEN 12 + +static int printi(char **out, int i, int b, int sg, int width, int pad, int letbase) +{ + char print_buf[PRINT_BUF_LEN]; + register char *s; + register int t, neg = 0, pc = 0; + register unsigned int u = i; + + if (i == 0) { + print_buf[0] = '0'; + print_buf[1] = '\0'; + return prints (out, print_buf, width, pad); + } + + if (sg && b == 10 && i < 0) { + neg = 1; + u = -i; + } + + s = print_buf + PRINT_BUF_LEN-1; + *s = '\0'; + + while (u) { + t = u % b; + if( t >= 10 ) + t += letbase - '0' - 10; + *--s = t + '0'; + u /= b; + } + + if (neg) { + if( width && (pad & PAD_ZERO) ) { + printchar (out, '-'); + ++pc; + --width; + } + else { + *--s = '-'; + } + } + + return pc + prints (out, s, width, pad); +} + +static int print(char **out, const char *format, va_list args ) +{ + register int width, pad; + register int pc = 0; + char scr[2]; + + for (; *format != 0; ++format) { + if (*format == '%') { + ++format; + width = pad = 0; + if (*format == '\0') break; + if (*format == '%') goto out; + if (*format == '-') { + ++format; + pad = PAD_RIGHT; + } + while (*format == '0') { + ++format; + pad |= PAD_ZERO; + } + for ( ; *format >= '0' && *format <= '9'; ++format) { + width *= 10; + width += *format - '0'; + } + if( *format == 's' ) { + register char *s = (char *)va_arg( args, int ); + pc += prints (out, s?s:"(null)", width, pad); + continue; + } + if( *format == 'd' ) { + pc += printi (out, va_arg( args, int ), 10, 1, width, pad, 'a'); + continue; + } + if( *format == 'x' ) { + pc += printi (out, va_arg( args, int ), 16, 0, width, pad, 'a'); + continue; + } + if( *format == 'X' ) { + pc += printi (out, va_arg( args, int ), 16, 0, width, pad, 'A'); + continue; + } + if( *format == 'u' ) { + pc += printi (out, va_arg( args, int ), 10, 0, width, pad, 'a'); + continue; + } + if( *format == 'c' ) { + /* char are converted to int then pushed on the stack */ + scr[0] = (char)va_arg( args, int ); + scr[1] = '\0'; + pc += prints (out, scr, width, pad); + continue; + } + } + else { + out: + printchar (out, *format); + ++pc; + } + } + if (out) **out = '\0'; + va_end( args ); + return pc; +} + +int printf(const char *format, ...) +{ + va_list args; + + va_start( args, format ); + return print( 0, format, args ); +} + +int sprintf(char *out, const char *format, ...) +{ + va_list args; + + va_start( args, format ); + return print( &out, format, args ); +} + +#ifdef TEST_PRINTF +int main(void) +{ + char *ptr = "Hello world!"; + char *np = 0; + int i = 5; + unsigned int bs = sizeof(int)*8; + int mi; + char buf[80]; + + mi = (1 << (bs-1)) + 1; + printf("%s\n", ptr); + printf("printf test\n"); + printf("%s is null pointer\n", np); + printf("%d = 5\n", i); + printf("%d = - max int\n", mi); + printf("char %c = 'a'\n", 'a'); + printf("hex %x = ff\n", 0xff); + printf("hex %02x = 00\n", 0); + printf("signed %d = unsigned %u = hex %x\n", -3, -3, -3); + printf("%d %s(s)%", 0, "message"); + printf("\n"); + printf("%d %s(s) with %%\n", 0, "message"); + sprintf(buf, "justif: \"%-10s\"\n", "left"); printf("%s", buf); + sprintf(buf, "justif: \"%10s\"\n", "right"); printf("%s", buf); + sprintf(buf, " 3: %04d zero padded\n", 3); printf("%s", buf); + sprintf(buf, " 3: %-4d left justif.\n", 3); printf("%s", buf); + sprintf(buf, " 3: %4d right justif.\n", 3); printf("%s", buf); + sprintf(buf, "-3: %04d zero padded\n", -3); printf("%s", buf); + sprintf(buf, "-3: %-4d left justif.\n", -3); printf("%s", buf); + sprintf(buf, "-3: %4d right justif.\n", -3); printf("%s", buf); + + return 0; +} + +/* + * if you compile this file with + * gcc -Wall $(YOUR_C_OPTIONS) -DTEST_PRINTF -c printf.c + * you will get a normal warning: + * printf.c:214: warning: spurious trailing `%' in format + * this line is testing an invalid % at the end of the format string. + * + * this should display (on 32bit int machine) : + * + * Hello world! + * printf test + * (null) is null pointer + * 5 = 5 + * -2147483647 = - max int + * char a = 'a' + * hex ff = ff + * hex 00 = 00 + * signed -3 = unsigned 4294967293 = hex fffffffd + * 0 message(s) + * 0 message(s) with % + * justif: "left " + * justif: " right" + * 3: 0003 zero padded + * 3: 3 left justif. + * 3: 3 right justif. + * -3: -003 zero padded + * -3: -3 left justif. + * -3: -3 right justif. + */ + +#endif diff --git a/poc/lpc2148_usb/target/custom/startup.c b/poc/lpc2148_usb/target/custom/startup.c new file mode 100644 index 0000000..0b949be --- /dev/null +++ b/poc/lpc2148_usb/target/custom/startup.c @@ -0,0 +1,126 @@ +/* + Initialisation functions for exception handlers, PLL and MAM + + Partially copied from Jim Lynch's tutorial +*/ + +/********************************************************** + Header files + **********************************************************/ + +#include "startup.h" + + +#define MAMCR *(volatile unsigned int *)0xE01FC000 +#define MAMTIM *(volatile unsigned int *)0xE01FC004 + +#define PLLCON *(volatile unsigned int *)0xE01FC080 +#define PLLCFG *(volatile unsigned int *)0xE01FC084 +#define PLLSTAT *(volatile unsigned int *)0xE01FC088 +#define PLLFEED *(volatile unsigned int *)0xE01FC08C + +#define VPBDIV *(volatile unsigned int *)0xE01FC100 + +void IRQ_Routine (void) __attribute__ ((interrupt("IRQ"))); +void FIQ_Routine (void) __attribute__ ((interrupt("FIQ"))); +void SWI_Routine (void) __attribute__ ((interrupt("SWI"))); +void UNDEF_Routine (void) __attribute__ ((interrupt("UNDEF"))); + + +/* Stubs for various interrupts (may be replaced later) */ +/* ---------------------------------------------------- */ + +void IRQ_Routine (void) { + while (1) ; +} + +void FIQ_Routine (void) { + while (1) ; +} + + +void SWI_Routine (void) { + while (1) ; +} + + +void UNDEF_Routine (void) { + while (1) ; +} + + +/********************************************************** + Initialize +**********************************************************/ + +#define PLOCK 0x400 + +static void feed(void) +{ + PLLFEED = 0xAA; + PLLFEED = 0x55; +} + + +void Initialize(void) +{ + + + // Setting the Phased Lock Loop (PLL) + // ---------------------------------- + // + // Olimex LPC-P2148 has a 12.0000 mhz crystal + // + // We'd like the LPC2148 to run at 60 mhz (has to be an even multiple of crystal) + // + // According to the Philips LPC2148 manual: M = cclk / Fosc where: M = PLL multiplier (bits 0-4 of PLLCFG) + // cclk = 60000000 hz + // Fosc = 12000000 hz + // + // Solving: M = 60000000 / 12000000 = 5 + // + // Note: M - 1 must be entered into bits 0-4 of PLLCFG (assign 4 to these bits) + // + // + // The Current Controlled Oscilator (CCO) must operate in the range 156 mhz to 320 mhz + // + // According to the Philips LPC2148 manual: Fcco = cclk * 2 * P where: Fcco = CCO frequency + // cclk = 60000000 hz + // P = PLL divisor (bits 5-6 of PLLCFG) + // + // Solving: Fcco = 60000000 * 2 * P + // P = 2 (trial value) + // Fcco = 60000000 * 2 * 2 + // Fcc0 = 240000000 hz (good choice for P since it's within the 156 mhz to 320 mhz range) + // + // From Table 22 (page 34) of Philips LPC2148 manual P = 2, PLLCFG bits 5-6 = 1 (assign 1 to these bits) + // + // Finally: PLLCFG = 0 01 00100 = 0x24 + // + // Final note: to load PLLCFG register, we must use the 0xAA followed 0x55 write sequence to the PLLFEED register + // this is done in the short function feed() below + // + + // Setting Multiplier and Divider values + PLLCFG = 0x24; + feed(); + + // Enabling the PLL */ + PLLCON = 0x1; + feed(); + + // Wait for the PLL to lock to set frequency + while(!(PLLSTAT & PLOCK)) ; + + // Connect the PLL as the clock source + PLLCON = 0x3; + feed(); + + // Enabling MAM and setting number of clocks used for Flash memory fetch + MAMTIM = 0x3; + MAMCR = 0x2; + + // Setting peripheral Clock (pclk) to System Clock (cclk) + VPBDIV = 0x1; +} + diff --git a/poc/lpc2148_usb/target/custom/startup.h b/poc/lpc2148_usb/target/custom/startup.h new file mode 100644 index 0000000..3845642 --- /dev/null +++ b/poc/lpc2148_usb/target/custom/startup.h @@ -0,0 +1,2 @@ +void Initialize(void); + diff --git a/poc/lpc2148_usb/target/lpcusb.tmproj b/poc/lpc2148_usb/target/lpcusb.tmproj new file mode 100644 index 0000000..8d260d2 --- /dev/null +++ b/poc/lpc2148_usb/target/lpcusb.tmproj @@ -0,0 +1,31 @@ + + + + + documents + + + name + lpcusb + regexFolderFilter + !.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$ + selected + + sourceDirectory + + + + fileHierarchyDrawerWidth + 200 + metaData + + showFileHierarchyDrawer + + showFileHierarchyPanel + + treeState + + windowFrame + {{0, 58}, {721, 820}} + + diff --git a/poc/lpc2148_usb/target/type.h b/poc/lpc2148_usb/target/type.h new file mode 100644 index 0000000..77bcc3d --- /dev/null +++ b/poc/lpc2148_usb/target/type.h @@ -0,0 +1,31 @@ +/** + @file + primitive types used in the USB stack + + (c) 2006, Bertrik Sikken, bertrik@sikken.nl + */ + + +#ifndef _TYPE_H_ +#define _TYPE_H_ + +typedef unsigned char U8; /**< unsigned 8-bit */ +typedef unsigned short int U16; /**< unsigned 16-bit */ +typedef unsigned int U32; /**< unsigned 32-bit */ + +typedef int BOOL; /**< #TRUE or #FALSE */ + +#define TRUE 1 /**< TRUE */ +#define FALSE 0 /**< FALSE */ + +#ifndef NULL +#define NULL ((void*)0) /**< NULL pointer */ +#endif + +/* some other useful macros */ +#define MIN(x,y) ((x)<(y)?(x):(y)) /**< MIN */ +#define MAX(x,y) ((x)>(y)?(x):(y)) /**< MAX */ + + +#endif /* _TYPE_H_ */ + diff --git a/poc/lpc2148_usb/target/usbapi.h b/poc/lpc2148_usb/target/usbapi.h new file mode 100644 index 0000000..1952309 --- /dev/null +++ b/poc/lpc2148_usb/target/usbapi.h @@ -0,0 +1,95 @@ +/** + (c) 2006, Bertrik Sikken, bertrik@sikken.nl + + @file +*/ + + +#include "type.h" +#include "usbstruct.h" // for TSetupPacket + +/************************************************************************* + USB configuration +**************************************************************************/ + +#define MAX_PACKET_SIZE0 64 /**< maximum packet size for EP 0 */ + +/************************************************************************* + USB hardware interface +**************************************************************************/ + +// endpoint status sent through callback +#define EP_STATUS_DATA (1<<0) /**< EP has data */ +#define EP_STATUS_STALLED (1<<1) /**< EP is stalled */ +#define EP_STATUS_SETUP (1<<2) /**< EP received setup packet */ +#define EP_STATUS_NACKED (1<<3) /**< EP sent NAK */ +#define EP_STATUS_ERROR (1<<4) /**< EP data was overwritten by setup packet */ + +// device status sent through callback +#define DEV_STATUS_CONNECT (1<<0) /**< device just got connected */ +#define DEV_STATUS_SUSPEND (1<<2) /**< device entered suspend state */ +#define DEV_STATUS_RESET (1<<4) /**< device just got reset */ + +// interrupt bits for NACK events in USBHwNakIntEnable +// (these bits conveniently coincide with the LPC214x USB controller bit) +#define INACK_CI (1<<1) /**< interrupt on NACK for control in */ +#define INACK_CO (1<<2) /**< interrupt on NACK for control out */ +#define INACK_II (1<<3) /**< interrupt on NACK for interrupt in */ +#define INACK_IO (1<<4) /**< interrupt on NACK for interrupt out */ +#define INACK_BI (1<<5) /**< interrupt on NACK for bulk in */ +#define INACK_BO (1<<6) /**< interrupt on NACK for bulk out */ + +BOOL USBHwInit (void); +void USBHwISR (void); + +void USBHwNakIntEnable (U8 bIntBits); + +void USBHwConnect (BOOL fConnect); + +void USBHwSetAddress (U8 bAddr); +void USBHwConfigDevice (BOOL fConfigured); + +// endpoint operations +void USBHwEPConfig (U8 bEP, U16 wMaxPacketSize); +int USBHwEPRead (U8 bEP, U8 *pbBuf, int iMaxLen); +int USBHwEPWrite (U8 bEP, U8 *pbBuf, int iLen); +void USBHwEPStall (U8 bEP, BOOL fStall); +BOOL USBHwEPIsStalled (U8 bEP); + +/** Endpoint interrupt handler callback */ +typedef void (TFnEPIntHandler) (U8 bEP, U8 bEPStatus); +void USBHwRegisterEPIntHandler (U8 bEP, TFnEPIntHandler *pfnHandler); + +/** Device status handler callback */ +typedef void (TFnDevIntHandler) (U8 bDevStatus); +void USBHwRegisterDevIntHandler (TFnDevIntHandler *pfnHandler); + +/** Frame event handler callback */ +typedef void (TFnFrameHandler)(U16 wFrame); +void USBHwRegisterFrameHandler(TFnFrameHandler *pfnHandler); + + +/************************************************************************* + USB application interface +**************************************************************************/ + +// initialise the complete stack, including HW +BOOL USBInit(void); + +/** Request handler callback (standard, vendor, class) */ +typedef BOOL (TFnHandleRequest)(TSetupPacket *pSetup, int *piLen, U8 **ppbData); +void USBRegisterRequestHandler(int iType, TFnHandleRequest *pfnHandler, U8 *pbDataStore); +void USBRegisterCustomReqHandler(TFnHandleRequest *pfnHandler); + +/** Descriptor handler callback */ +typedef BOOL (TFnGetDescriptor)(U16 wTypeIndex, U16 wLangID, int *piLen, U8 **ppbData); + +/** Default standard request handler */ +BOOL USBHandleStandardRequest(TSetupPacket *pSetup, int *piLen, U8 **ppbData); + +/** Default EP0 handler */ +void USBHandleControlTransfer(U8 bEP, U8 bEPStat); + +/** Descriptor handling */ +void USBRegisterDescriptors(const U8 *pabDescriptors); +BOOL USBGetDescriptor(U16 wTypeIndex, U16 wLangID, int *piLen, U8 **ppbData); diff --git a/poc/lpc2148_usb/target/usbcontrol.c b/poc/lpc2148_usb/target/usbcontrol.c new file mode 100644 index 0000000..65e65a4 --- /dev/null +++ b/poc/lpc2148_usb/target/usbcontrol.c @@ -0,0 +1,222 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + This library 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 2.1 of the License, or (at your option) any later version. + + This library 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 library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** @file + Control transfer handler. + + In case of a control-write (host-to-device), this module collects the full + control message in a local buffer, then sends it off to an installed + request handler. + + In case of a control-read (device-to-host), an installed request handler + is asked to handle the request and provide return data. The handler can + either put the data in the control data buffer through the supplied pointer, + or it can supply a new data pointer. In both cases, the handler is required + to update the data length in *piLen; + + + Currently, control transfers are handled in a very simple way, keeping + almost no state about the control transfer progress (setup stage, data + stage, status stage). We simply follow the host: if it sends data, we store + it in the control data buffer and if it asks for data, we just send the next + block. + +*/ + +#include "type.h" +#include "usbdebug.h" + +#include "usbstruct.h" +#include "usbapi.h" + + + +#define MAX_CONTROL_SIZE 128 /**< maximum total size of control transfer data */ +#define MAX_REQ_HANDLERS 4 /**< standard, class, vendor, reserved */ + +static TSetupPacket Setup; /**< setup packet */ + +static U8 *pbData; /**< pointer to data buffer */ +static int iResidue; /**< remaining bytes in buffer */ +static int iLen; /**< total length of control transfer */ + +/** Array of installed request handler callbacks */ +static TFnHandleRequest *apfnReqHandlers[4] = {NULL, NULL, NULL, NULL}; +/** Array of installed request data pointers */ +static U8 *apbDataStore[4] = {NULL, NULL, NULL, NULL}; + +/** + Local function to handle a request by calling one of the installed + request handlers. + + In case of data going from host to device, the data is at *ppbData. + In case of data going from device to host, the handler can either + choose to write its data at *ppbData or update the data pointer. + + @param [in] pSetup The setup packet + @param [in,out] *piLen Pointer to data length + @param [in,out] ppbData Data buffer. + + @return TRUE if the request was handles successfully + */ +static BOOL _HandleRequest(TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + TFnHandleRequest *pfnHandler; + int iType; + + iType = REQTYPE_GET_TYPE(pSetup->bmRequestType); + pfnHandler = apfnReqHandlers[iType]; + if (pfnHandler == NULL) { + DBG("No handler for reqtype %d\n", iType); + return FALSE; + } + + return pfnHandler(pSetup, piLen, ppbData); +} + + +/** + Local function to stall the control endpoint + + @param [in] bEPStat Endpoint status + */ +static void StallControlPipe(U8 bEPStat) +{ + U8 *pb; + int i; + + USBHwEPStall(0x80, TRUE); + +// dump setup packet + DBG("STALL on ["); + pb = (U8 *)&Setup; + for (i = 0; i < 8; i++) { + DBG(" %02x", *pb++); + } + DBG("] stat=%x\n", bEPStat); +} + + +/** + Sends next chunk of data (possibly 0 bytes) to host + */ +static void DataIn(void) +{ + int iChunk; + + iChunk = MIN(MAX_PACKET_SIZE0, iResidue); + USBHwEPWrite(0x80, pbData, iChunk); + pbData += iChunk; + iResidue -= iChunk; +} + + +/** + * Handles IN/OUT transfers on EP0 + * + * @param [in] bEP Endpoint address + * @param [in] bEPStat Endpoint status + */ +void USBHandleControlTransfer(U8 bEP, U8 bEPStat) +{ + int iChunk, iType; + + if (bEP == 0x00) { + // OUT transfer + if (bEPStat & EP_STATUS_SETUP) { + // setup packet, reset request message state machine + USBHwEPRead(0x00, (U8 *)&Setup, sizeof(Setup)); + DBG("S%x", Setup.bRequest); + + // defaults for data pointer and residue + iType = REQTYPE_GET_TYPE(Setup.bmRequestType); + pbData = apbDataStore[iType]; + iResidue = Setup.wLength; + iLen = Setup.wLength; + + if ((Setup.wLength == 0) || + (REQTYPE_GET_DIR(Setup.bmRequestType) == REQTYPE_DIR_TO_HOST)) { + // ask installed handler to process request + if (!_HandleRequest(&Setup, &iLen, &pbData)) { + DBG("_HandleRequest1 failed\n"); + StallControlPipe(bEPStat); + return; + } + // send smallest of requested and offered length + iResidue = MIN(iLen, Setup.wLength); + // send first part (possibly a zero-length status message) + DataIn(); + } + } + else { + if (iResidue > 0) { + // store data + iChunk = USBHwEPRead(0x00, pbData, iResidue); + if (iChunk < 0) { + StallControlPipe(bEPStat); + return; + } + pbData += iChunk; + iResidue -= iChunk; + if (iResidue == 0) { + // received all, send data to handler + iType = REQTYPE_GET_TYPE(Setup.bmRequestType); + pbData = apbDataStore[iType]; + if (!_HandleRequest(&Setup, &iLen, &pbData)) { + DBG("_HandleRequest2 failed\n"); + StallControlPipe(bEPStat); + return; + } + // send status to host + DataIn(); + } + } + else { + // absorb zero-length status message + iChunk = USBHwEPRead(0x00, NULL, 0); + DBG(iChunk > 0 ? "?" : ""); + } + } + } + else if (bEP == 0x80) { + // IN transfer + // send more data if available (possibly a 0-length packet) + DataIn(); + } + else { + ASSERT(FALSE); + } +} + + +/** + Registers a callback for handling requests + + @param [in] iType Type of request, e.g. REQTYPE_TYPE_STANDARD + @param [in] *pfnHandler Callback function pointer + @param [in] *pbDataStore Data storage area for this type of request + */ +void USBRegisterRequestHandler(int iType, TFnHandleRequest *pfnHandler, U8 *pbDataStore) +{ + ASSERT(iType >= 0); + ASSERT(iType < 4); + apfnReqHandlers[iType] = pfnHandler; + apbDataStore[iType] = pbDataStore; +} + diff --git a/poc/lpc2148_usb/target/usbdebug.h b/poc/lpc2148_usb/target/usbdebug.h new file mode 100644 index 0000000..8a22800 --- /dev/null +++ b/poc/lpc2148_usb/target/usbdebug.h @@ -0,0 +1,12 @@ + + +int printf(const char *format, ...); + +#ifdef DEBUG +#define DBG printf +#define ASSERT(x) if(!(x)){DBG("\nAssertion '%s' failed in %s:%s#%d!\n",#x,__FILE__,__FUNCTION__,__LINE__);while(1);} +#else +#define DBG(x ...) +#define ASSERT(x) +#endif + diff --git a/poc/lpc2148_usb/target/usbhw_lpc.c b/poc/lpc2148_usb/target/usbhw_lpc.c new file mode 100644 index 0000000..5c65c4e --- /dev/null +++ b/poc/lpc2148_usb/target/usbhw_lpc.c @@ -0,0 +1,541 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + This library 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 2.1 of the License, or (at your option) any later version. + + This library 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 library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** @file + USB hardware layer + */ + +#include "type.h" +#include "usbdebug.h" +#include "usbhw_lpc.h" +#include "usbapi.h" + + +#ifdef DEBUG +// comment out the following line if you don't want to use debug LEDs +#define DEBUG_LED +#endif + +#ifdef DEBUG_LED +#define DEBUG_LED_ON(x) IOCLR0 = (1 << x); +#define DEBUG_LED_OFF(x) IOSET0 = (1 << x); +#define DEBUG_LED_INIT(x) PINSEL0 &= ~(0x3 << (2*x)); IODIR0 |= (1 << x); DEBUG_LED_OFF(x); +#else +#define DEBUG_LED_INIT(x) /**< LED initialisation macro */ +#define DEBUG_LED_ON(x) /**< turn LED on */ +#define DEBUG_LED_OFF(x) /**< turn LED off */ +#endif + +/** Installed device interrupt handler */ +static TFnDevIntHandler *_pfnDevIntHandler = NULL; +/** Installed endpoint interrupt handlers */ +static TFnEPIntHandler *_apfnEPIntHandlers[16]; +/** Installed frame interrupt handlers */ +static TFnFrameHandler *_pfnFrameHandler = NULL; + +/** convert from endpoint address to endpoint index */ +#define EP2IDX(bEP) ((((bEP)&0xF)<<1)|(((bEP)&0x80)>>7)) +/** convert from endpoint index to endpoint address */ +#define IDX2EP(idx) ((((idx)<<7)&0x80)|(((idx)>>1)&0xF)) + + + +/** + Local function to wait for a device interrupt (and clear it) + + @param [in] dwIntr Interrupts to wait for + */ +static void Wait4DevInt(U32 dwIntr) +{ + while ((USBDevIntSt & dwIntr) != dwIntr); + USBDevIntClr = dwIntr; +} + + +/** + Local function to send a command to the USB protocol engine + + @param [in] bCmd Command to send + */ +static void USBHwCmd(U8 bCmd) +{ + // clear CDFULL/CCEMTY + USBDevIntClr = CDFULL | CCEMTY; + // write command code + USBCmdCode = 0x00000500 | (bCmd << 16); + Wait4DevInt(CCEMTY); +} + + +/** + Local function to send a command + data to the USB protocol engine + + @param [in] bCmd Command to send + @param [in] bData Data to send + */ +static void USBHwCmdWrite(U8 bCmd, U16 bData) +{ + // write command code + USBHwCmd(bCmd); + + // write command data + USBCmdCode = 0x00000100 | (bData << 16); + Wait4DevInt(CCEMTY); +} + + +/** + Local function to send a command to the USB protocol engine and read data + + @param [in] bCmd Command to send + + @return the data + */ +static U8 USBHwCmdRead(U8 bCmd) +{ + // write command code + USBHwCmd(bCmd); + + // get data + USBCmdCode = 0x00000200 | (bCmd << 16); + Wait4DevInt(CDFULL); + return USBCmdData; +} + + +/** + 'Realizes' an endpoint, meaning that buffer space is reserved for + it. An endpoint needs to be realised before it can be used. + + From experiments, it appears that a USB reset causes USBReEP to + re-initialise to 3 (= just the control endpoints). + However, a USB bus reset does not disturb the USBMaxPSize settings. + + @param [in] idx Endpoint index + @param [in] wMaxPSize Maximum packet size for this endpoint + */ +static void USBHwEPRealize(int idx, U16 wMaxPSize) +{ + USBReEP |= (1 << idx); + USBEpInd = idx; + USBMaxPSize = wMaxPSize; + Wait4DevInt(EP_RLZED); +} + + +/** + Enables or disables an endpoint + + @param [in] idx Endpoint index + @param [in] fEnable TRUE to enable, FALSE to disable + */ +static void USBHwEPEnable(int idx, BOOL fEnable) +{ + USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fEnable ? 0 : EP_DA); +} + + +/** + Configures an endpoint and enables it + + @param [in] bEP Endpoint number + @param [in] wMaxPacketSize Maximum packet size for this EP + */ +void USBHwEPConfig(U8 bEP, U16 wMaxPacketSize) +{ + int idx; + + idx = EP2IDX(bEP); + + // realise EP + USBHwEPRealize(idx, wMaxPacketSize); + + // enable EP + USBHwEPEnable(idx, TRUE); +} + + +/** + Registers an endpoint event callback + + @param [in] bEP Endpoint number + @param [in] pfnHandler Callback function + */ +void USBHwRegisterEPIntHandler(U8 bEP, TFnEPIntHandler *pfnHandler) +{ + int idx; + + idx = EP2IDX(bEP); + + ASSERT(idx<32); + + /* add handler to list of EP handlers */ + _apfnEPIntHandlers[idx / 2] = pfnHandler; + + /* enable EP interrupt */ + USBEpIntEn |= (1 << idx); + USBDevIntEn |= EP_SLOW; + + DBG("Registered handler for EP 0x%x\n", bEP); +} + + +/** + Registers an device status callback + + @param [in] pfnHandler Callback function + */ +void USBHwRegisterDevIntHandler(TFnDevIntHandler *pfnHandler) +{ + _pfnDevIntHandler = pfnHandler; + + // enable device interrupt + USBDevIntEn |= DEV_STAT; + + DBG("Registered handler for device status\n"); +} + + +/** + Registers the frame callback + + @param [in] pfnHandler Callback function + */ +void USBHwRegisterFrameHandler(TFnFrameHandler *pfnHandler) +{ + _pfnFrameHandler = pfnHandler; + + // enable device interrupt + USBDevIntEn |= FRAME; + + DBG("Registered handler for frame\n"); +} + + +/** + Sets the USB address. + + @param [in] bAddr Device address to set + */ +void USBHwSetAddress(U8 bAddr) +{ + USBHwCmdWrite(CMD_DEV_SET_ADDRESS, DEV_EN | bAddr); +} + + +/** + Connects or disconnects from the USB bus + + @param [in] fConnect If TRUE, connect, otherwise disconnect + */ +void USBHwConnect(BOOL fConnect) +{ + USBHwCmdWrite(CMD_DEV_STATUS, fConnect ? CON : 0); +} + + +/** + Enables interrupt on NAK condition + + For IN endpoints a NAK is generated when the host wants to read data + from the device, but none is available in the endpoint buffer. + For OUT endpoints a NAK is generated when the host wants to write data + to the device, but the endpoint buffer is still full. + + The endpoint interrupt handlers can distinguish regular (ACK) interrupts + from NAK interrupt by checking the bits in their bEPStatus argument. + + @param [in] bIntBits Bitmap indicating which NAK interrupts to enable + */ +void USBHwNakIntEnable(U8 bIntBits) +{ + USBHwCmdWrite(CMD_DEV_SET_MODE, bIntBits); +} + + +/** + Gets the stalled property of an endpoint + + @param [in] bEP Endpoint number + + @return TRUE if stalled, FALSE if not stalled + */ +BOOL USBHwEPIsStalled(U8 bEP) +{ + int idx = EP2IDX(bEP); + + return (USBHwCmdRead(CMD_EP_SELECT | idx) & 2); +} + + +/** + Sets the stalled property of an endpoint + + @param [in] bEP Endpoint number + @param [in] fStall TRUE to stall, FALSE to unstall + */ +void USBHwEPStall(U8 bEP, BOOL fStall) +{ + int idx = EP2IDX(bEP); + + USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fStall ? EP_ST : 0); +} + + +/** + Writes data to an endpoint buffer + + @param [in] bEP Endpoint number + @param [in] pbBuf Endpoint data + @param [in] iLen Number of bytes to write + + @return TRUE if the data was successfully written or <0 in case of error. +*/ +int USBHwEPWrite(U8 bEP, U8 *pbBuf, int iLen) +{ + int idx; + + idx = EP2IDX(bEP); + +// DBG("<%d", iLen); +// DBG("<"); + + // set write enable for specific endpoint + USBCtrl = WR_EN | ((bEP & 0xF) << 2); + + // set packet length + USBTxPLen = iLen; + + // write data + while (USBCtrl & WR_EN) { + USBTxData = (pbBuf[3] << 24) | (pbBuf[2] << 16) | (pbBuf[1] << 8) | pbBuf[0]; + pbBuf += 4; + } + + // select endpoint and validate buffer + USBHwCmd(CMD_EP_SELECT | idx); + USBHwCmd(CMD_EP_VALIDATE_BUFFER); + + return iLen; +} + + +/** + Reads data from an endpoint buffer + + @param [in] bEP Endpoint number + @param [in] pbBuf Endpoint data + @param [in] iMaxLen Maximum number of bytes to read + + @return the number of bytes available in the EP (possibly more than iMaxLen), + or <0 in case of error. + */ +int USBHwEPRead(U8 bEP, U8 *pbBuf, int iMaxLen) +{ + int i, idx; + U32 dwData, dwLen; + + idx = EP2IDX(bEP); + + // set read enable bit for specific endpoint + USBCtrl = RD_EN | ((bEP & 0xF) << 2); + + // wait for PKT_RDY + do { + dwLen = USBRxPLen; + } while ((dwLen & PKT_RDY) == 0); + + // packet valid? + if ((dwLen & DV) == 0) { + return -1; + } + + // get length + dwLen &= PKT_LNGTH_MASK; + + // get data + while (USBCtrl & RD_EN) { + dwData = USBRxData; + if (pbBuf != NULL) { + for (i = 0; i < 4; i++) { + if (iMaxLen-- != 0) { + *pbBuf++ = dwData & 0xFF; + } + dwData >>= 8; + } + } + } + + // select endpoint and clear buffer + USBHwCmd(CMD_EP_SELECT | idx); + USBHwCmd(CMD_EP_CLEAR_BUFFER); + +// DBG(">%d", dwLen); +// DBG(">"); + + return dwLen; +} + + +/** + Sets the 'configured' state. + + All registered endpoints are 'realised' and enabled, and the + 'configured' bit is set in the device status register. + + @param [in] fConfigured If TRUE, configure device, else unconfigure + */ +void USBHwConfigDevice(BOOL fConfigured) +{ + // set configured bit + USBHwCmdWrite(CMD_DEV_CONFIG, fConfigured ? CONF_DEVICE : 0); +} + + +/** + USB interrupt handler + + Endpoint interrupts are mapped to the slow interrupt + */ +void USBHwISR(void) +{ + U32 dwStatus, dwEPIntStat; + U32 dwIntBit; + U8 bEPStat, bDevStat, bStat; + int i; + + dwStatus = USBDevIntSt; + + // handle device dwStatus interrupts + if (dwStatus & DEV_STAT) { +DEBUG_LED_ON(8); + bDevStat = USBHwCmdRead(CMD_DEV_STATUS); + if (bDevStat & (CON_CH | SUS_CH | RST)) { + // convert device status into something HW independent + bStat = ((bDevStat & CON) ? DEV_STATUS_CONNECT : 0) | + ((bDevStat & SUS) ? DEV_STATUS_SUSPEND : 0) | + ((bDevStat & RST) ? DEV_STATUS_RESET : 0); + // call handler + if (_pfnDevIntHandler != NULL) { + _pfnDevIntHandler(bStat); + } + } + // clear DEV_STAT; + USBDevIntClr = DEV_STAT; +DEBUG_LED_OFF(8); + } + + // check endpoint interrupts + if (dwStatus & EP_SLOW) { +DEBUG_LED_ON(9); + dwEPIntStat = USBEpIntSt; + for (i = 0; i < 32; i++) { + dwIntBit = (1 << i); + if (dwEPIntStat & dwIntBit) { + // clear int (and retrieve status) + USBEpIntClr = dwIntBit; + Wait4DevInt(CDFULL); + bEPStat = USBCmdData; + // convert EP pipe stat into something HW independent + bStat = ((bEPStat & EPSTAT_FE) ? EP_STATUS_DATA : 0) | + ((bEPStat & EPSTAT_ST) ? EP_STATUS_STALLED : 0) | + ((bEPStat & EPSTAT_STP) ? EP_STATUS_SETUP : 0) | + ((bEPStat & EPSTAT_EPN) ? EP_STATUS_NACKED : 0) | + ((bEPStat & EPSTAT_PO) ? EP_STATUS_ERROR : 0); + // call handler + if (_apfnEPIntHandlers[i / 2] != NULL) { + _apfnEPIntHandlers[i / 2](IDX2EP(i), bStat); + } + } + } + // clear EP_SLOW + USBDevIntClr = EP_SLOW; +DEBUG_LED_OFF(9); + } + + // handle frame interrupt + if (dwStatus & FRAME) { +DEBUG_LED_ON(10); + if (_pfnFrameHandler != NULL) { + _pfnFrameHandler(0); // implement counter later + } + // clear int + USBDevIntClr = FRAME; +DEBUG_LED_OFF(10); + } +} + + + +/** + Initialises the USB hardware + + This function assumes that the hardware is connected as shown in + section 10.1 of the LPC2148 data sheet: + * P0.31 controls a switch to connect a 1.5k pull-up to D+ if low. + * P0.23 is connected to USB VCC. + + Embedded artists board: make sure to disconnect P0.23 LED as it + acts as a pull-up and so prevents detection of USB disconnect. + + @return TRUE if the hardware was successfully initialised + */ +BOOL USBHwInit(void) +{ + // configure P0.23 for Vbus sense + PINSEL1 = (PINSEL1 & ~(3 << 14)) | (1 << 14); // P0.23 + IODIR0 &= ~(1 << 23); + // configure P0.31 for CONNECT + PINSEL1 = (PINSEL1 & ~(3 << 30)) | (2 << 30); // P0.31 + + // enable PUSB + PCONP |= (1 << 31); + + // initialise PLL + PLL1CON = 1; // enable PLL + PLL1CFG = (1 << 5) | 3; // P = 2, M = 4 + PLL1FEED = 0xAA; + PLL1FEED = 0x55; + while ((PLL1STAT & (1 << 10)) == 0); + + PLL1CON = 3; // enable and connect + PLL1FEED = 0xAA; + PLL1FEED = 0x55; + + // disable/clear all interrupts for now + USBDevIntEn = 0; + USBEpIntEn = 0; + USBDevIntClr = 0xFFFFFFFF; + USBEpIntClr = 0xFFFFFFFF; + + // setup control endpoints + USBHwEPConfig(0x00, MAX_PACKET_SIZE0); + USBHwEPConfig(0x80, MAX_PACKET_SIZE0); + + // by default, only ACKs generate interrupts + USBHwNakIntEnable(0); + + // init debug leds + DEBUG_LED_INIT(8); + DEBUG_LED_INIT(9); + DEBUG_LED_INIT(10); + + return TRUE; +} + diff --git a/poc/lpc2148_usb/target/usbhw_lpc.h b/poc/lpc2148_usb/target/usbhw_lpc.h new file mode 100644 index 0000000..4719307 --- /dev/null +++ b/poc/lpc2148_usb/target/usbhw_lpc.h @@ -0,0 +1,156 @@ +/** + (c) 2006, Bertrik Sikken, bertrik@sikken.nl + + Hardware definitions for the LPC214x USB controller + + These are private to the usbhw module +*/ + + +/* Common LPC2148 definitions, related to USB */ +#define PCONP *(volatile unsigned int *)0xE01FC0C4 +#define PLL1CON *(volatile unsigned int *)0xE01FC0A0 +#define PLL1CFG *(volatile unsigned int *)0xE01FC0A4 +#define PLL1STAT *(volatile unsigned int *)0xE01FC0A8 +#define PLL1FEED *(volatile unsigned int *)0xE01FC0AC + +#define PINSEL0 *(volatile unsigned int *)0xE002C000 +#define PINSEL1 *(volatile unsigned int *)0xE002C004 +#define IOPIN0 *(volatile unsigned int *)0xE0028000 +#define IOSET0 *(volatile unsigned int *)0xE0028004 +#define IODIR0 *(volatile unsigned int *)0xE0028008 +#define IOCLR0 *(volatile unsigned int *)0xE002800C + +/* USB register definitions */ +#define USBIntSt *(volatile unsigned int *)0xE01FC1C0 + +#define USBDevIntSt *(volatile unsigned int *)0xE0090000 +#define USBDevIntEn *(volatile unsigned int *)0xE0090004 +#define USBDevIntClr *(volatile unsigned int *)0xE0090008 +#define USBDevIntSet *(volatile unsigned int *)0xE009000C +#define USBDevIntPri *(volatile unsigned int *)0xE009002C + +#define USBEpIntSt *(volatile unsigned int *)0xE0090030 +#define USBEpIntEn *(volatile unsigned int *)0xE0090034 +#define USBEpIntClr *(volatile unsigned int *)0xE0090038 +#define USBEpIntSet *(volatile unsigned int *)0xE009003C +#define USBEpIntPri *(volatile unsigned int *)0xE0090040 + +#define USBReEP *(volatile unsigned int *)0xE0090044 +#define USBEpInd *(volatile unsigned int *)0xE0090048 +#define USBMaxPSize *(volatile unsigned int *)0xE009004C + +#define USBRxData *(volatile unsigned int *)0xE0090018 +#define USBRxPLen *(volatile unsigned int *)0xE0090020 +#define USBTxData *(volatile unsigned int *)0xE009001C +#define USBTxPLen *(volatile unsigned int *)0xE0090024 +#define USBCtrl *(volatile unsigned int *)0xE0090028 + +#define USBCmdCode *(volatile unsigned int *)0xE0090010 +#define USBCmdData *(volatile unsigned int *)0xE0090014 + +/* USBIntSt bits */ +#define USB_INT_REQ_LP (1<<0) +#define USB_INT_REQ_HP (1<<1) +#define USB_INT_REQ_DMA (1<<2) +#define USB_need_clock (1<<8) +#define EN_USB_BITS (1<<31) + +/* USBDevInt... bits */ +#define FRAME (1<<0) +#define EP_FAST (1<<1) +#define EP_SLOW (1<<2) +#define DEV_STAT (1<<3) +#define CCEMTY (1<<4) +#define CDFULL (1<<5) +#define RxENDPKT (1<<6) +#define TxENDPKT (1<<7) +#define EP_RLZED (1<<8) +#define ERR_INT (1<<9) + +/* USBRxPLen bits */ +#define PKT_LNGTH (1<<0) +#define PKT_LNGTH_MASK 0x3FF +#define DV (1<<10) +#define PKT_RDY (1<<11) + +/* USBCtrl bits */ +#define RD_EN (1<<0) +#define WR_EN (1<<1) +#define LOG_ENDPOINT (1<<2) + +/* protocol engine command codes */ + /* device commands */ +#define CMD_DEV_SET_ADDRESS 0xD0 +#define CMD_DEV_CONFIG 0xD8 +#define CMD_DEV_SET_MODE 0xF3 +#define CMD_DEV_READ_CUR_FRAME_NR 0xF5 +#define CMD_DEV_READ_TEST_REG 0xFD +#define CMD_DEV_STATUS 0xFE /* read/write */ +#define CMD_DEV_GET_ERROR_CODE 0xFF +#define CMD_DEV_READ_ERROR_STATUS 0xFB + /* endpoint commands */ +#define CMD_EP_SELECT 0x00 +#define CMD_EP_SELECT_CLEAR 0x40 +#define CMD_EP_SET_STATUS 0x40 +#define CMD_EP_CLEAR_BUFFER 0xF2 +#define CMD_EP_VALIDATE_BUFFER 0xFA + +/* set address command */ +#define DEV_ADDR (1<<0) +#define DEV_EN (1<<7) + +/* configure device command */ +#define CONF_DEVICE (1<<0) + +/* set mode command */ +#define AP_CLK (1<<0) +#define INAK_CI (1<<1) +#define INAK_CO (1<<2) +#define INAK_II (1<<3) +#define INAK_IO (1<<4) +#define INAK_BI (1<<5) +#define INAK_BO (1<<6) + +/* set get device status command */ +#define CON (1<<0) +#define CON_CH (1<<1) +#define SUS (1<<2) +#define SUS_CH (1<<3) +#define RST (1<<4) + +/* get error code command */ +// ... + +/* Select Endpoint command read bits */ +#define EPSTAT_FE (1<<0) +#define EPSTAT_ST (1<<1) +#define EPSTAT_STP (1<<2) +#define EPSTAT_PO (1<<3) +#define EPSTAT_EPN (1<<4) +#define EPSTAT_B1FULL (1<<5) +#define EPSTAT_B2FULL (1<<6) + +/* CMD_EP_SET_STATUS command */ +#define EP_ST (1<<0) +#define EP_DA (1<<5) +#define EP_RF_MO (1<<6) +#define EP_CND_ST (1<<7) + +/* read error status command */ +#define PID_ERR (1<<0) +#define UEPKT (1<<1) +#define DCRC (1<<2) +#define TIMEOUT (1<<3) +#define EOP (1<<4) +#define B_OVRN (1<<5) +#define BTSTF (1<<6) +#define TGL_ERR (1<<7) + + + + + + + + diff --git a/poc/lpc2148_usb/target/usbinit.c b/poc/lpc2148_usb/target/usbinit.c new file mode 100644 index 0000000..20760f0 --- /dev/null +++ b/poc/lpc2148_usb/target/usbinit.c @@ -0,0 +1,69 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + This library 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 2.1 of the License, or (at your option) any later version. + + This library 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 library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** @file + USB stack initialisation + */ + +#include "type.h" +#include "usbdebug.h" +#include "usbapi.h" + + +/** data storage area for standard requests */ +static U8 abStdReqData[8]; + + +/** + USB reset handler + + @param [in] bDevStatus Device status + */ +static void HandleUsbReset(U8 bDevStatus) +{ + if (bDevStatus & DEV_STATUS_RESET) { + DBG("\n!"); + } +} + + +/** + Initialises the USB hardware and sets up the USB stack by + installing default callbacks. + + @return TRUE if initialisation was successful + */ +BOOL USBInit(void) +{ + // init hardware + USBHwInit(); + + // register bus reset handler + USBHwRegisterDevIntHandler(HandleUsbReset); + + // register control transfer handler on EP0 + USBHwRegisterEPIntHandler(0x00, USBHandleControlTransfer); + USBHwRegisterEPIntHandler(0x80, USBHandleControlTransfer); + + // register standard request handler + USBRegisterRequestHandler(REQTYPE_TYPE_STANDARD, USBHandleStandardRequest, abStdReqData); + + return TRUE; +} + diff --git a/poc/lpc2148_usb/target/usbstdreq.c b/poc/lpc2148_usb/target/usbstdreq.c new file mode 100644 index 0000000..1dc5f3e --- /dev/null +++ b/poc/lpc2148_usb/target/usbstdreq.c @@ -0,0 +1,415 @@ +/* + LPCUSB, an USB device driver for LPC microcontrollers + Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl) + + This library 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 2.1 of the License, or (at your option) any later version. + + This library 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 library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/** @file + Standard request handler. + + This modules handles the 'chapter 9' processing, specifically the + standard device requests in table 9-3 from the universal serial bus + specification revision 2.0 + + Specific types of devices may specify additional requests (for example + HID devices add a GET_DESCRIPTOR request for interfaces), but they + will not be part of this module. + + @todo some requests have to return a request error if device not configured: + @todo GET_INTERFACE, GET_STATUS, SET_INTERFACE, SYNCH_FRAME + @todo this applies to the following if endpoint != 0: + @todo SET_FEATURE, GET_FEATURE +*/ + +#include "type.h" +#include "usbdebug.h" +#include "usbstruct.h" +#include "usbapi.h" + +#define MAX_DESC_HANDLERS 4 /**< device, interface, endpoint, other */ + + +/* general descriptor field offsets */ +#define DESC_bLength 0 /**< length offset */ +#define DESC_bDescriptorType 1 /**< descriptor type offset */ + +/* config descriptor field offsets */ +#define CONF_DESC_wTotalLength 2 /**< total length offset */ +#define CONF_DESC_bConfigurationValue 5 /**< configuration value offset */ + +/* interface descriptor field offsets */ +#define INTF_DESC_bAlternateSetting 3 /**< alternate setting offset */ + +/* endpoint descriptor field offsets */ +#define ENDP_DESC_bEndpointAddress 2 /**< endpoint address offset */ +#define ENDP_DESC_wMaxPacketSize 4 /**< maximum packet size offset */ + + +/** Currently selected configuration */ +static U8 bConfiguration = 0; +/** Installed custom request handler */ +static TFnHandleRequest *pfnHandleCustomReq = NULL; +/** Pointer to registered descriptors */ +static const U8 *pabDescrip = NULL; + + +/** + Registers a pointer to a descriptor block containing all descriptors + for the device. + + @param [in] pabDescriptors The descriptor byte array + */ +void USBRegisterDescriptors(const U8 *pabDescriptors) +{ + pabDescrip = pabDescriptors; +} + + +/** + Parses the list of installed USB descriptors and attempts to find + the specified USB descriptor. + + @param [in] wTypeIndex Type and index of the descriptor + @param [in] wLangID Language ID of the descriptor (currently unused) + @param [out] *piLen Descriptor length + @param [out] *ppbData Descriptor data + + @return TRUE if the descriptor was found, FALSE otherwise + */ +BOOL USBGetDescriptor(U16 wTypeIndex, U16 wLangID, int *piLen, U8 **ppbData) +{ + U8 bType, bIndex; + U8 *pab; + int iCurIndex; + + ASSERT(pabDescrip != NULL); + + bType = GET_DESC_TYPE(wTypeIndex); + bIndex = GET_DESC_INDEX(wTypeIndex); + + pab = (U8 *)pabDescrip; + iCurIndex = 0; + + while (pab[DESC_bLength] != 0) { + if (pab[DESC_bDescriptorType] == bType) { + if (iCurIndex == bIndex) { + // set data pointer + *ppbData = pab; + // get length from structure + if (bType == DESC_CONFIGURATION) { + // configuration descriptor is an exception, length is at offset 2 and 3 + *piLen = (pab[CONF_DESC_wTotalLength]) | + (pab[CONF_DESC_wTotalLength + 1] << 8); + } + else { + // normally length is at offset 0 + *piLen = pab[0]; + } + return TRUE; + } + iCurIndex++; + } + // skip to next descriptor + pab += pab[DESC_bLength]; + } + // nothing found + DBG("Desc %x not found!\n", wTypeIndex); + return FALSE; +} + + +/** + Configures the device according to the specified configuration index and + alternate setting by parsing the installed USB descriptor list. + A configuration index of 0 unconfigures the device. + + @param [in] bConfigIndex Configuration index + @param [in] bAltSetting Alternate setting number + + @todo function always returns TRUE, add stricter checking? + + @return TRUE if successfully configured, FALSE otherwise + */ +static BOOL USBSetConfiguration(U8 bConfigIndex, U8 bAltSetting) +{ + U8 *pab; + U8 bCurConfig, bCurAltSetting; + U8 bEP; + U16 wMaxPktSize; + + ASSERT(pabDescrip != NULL); + + // parse installed USB descriptors to configure endpoints + pab = (U8 *)pabDescrip; + bCurConfig = 0xFF; + bCurAltSetting = 0xFF; + + while (pab[DESC_bLength] != 0) { + + switch (pab[DESC_bDescriptorType]) { + + case DESC_CONFIGURATION: + // remember current configuration index + bCurConfig = pab[CONF_DESC_bConfigurationValue]; + break; + + case DESC_INTERFACE: + // remember current alternate setting + bCurAltSetting = pab[INTF_DESC_bAlternateSetting]; + break; + + case DESC_ENDPOINT: + if ((bCurConfig == bConfigIndex) && + (bCurAltSetting == bAltSetting)) { + // endpoint found for desired config and alternate setting + bEP = pab[ENDP_DESC_bEndpointAddress]; + wMaxPktSize = (pab[ENDP_DESC_wMaxPacketSize]) | + (pab[ENDP_DESC_wMaxPacketSize + 1] << 8); + // configure it + USBHwEPConfig(bEP, wMaxPktSize); + } + break; + + default: + break; + } + // skip to next descriptor + pab += pab[DESC_bLength]; + } + + // configure device + USBHwConfigDevice(bConfigIndex != 0); + + return TRUE; +} + + +/** + Local function to handle a standard device request + + @param [in] pSetup The setup packet + @param [in,out] *piLen Pointer to data length + @param [in,out] ppbData Data buffer. + + @return TRUE if the request was handled successfully + */ +static BOOL HandleStdDeviceReq(TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + U8 *pbData = *ppbData; + + switch (pSetup->bRequest) { + + case REQ_GET_STATUS: + // bit 0: self-powered + // bit 1: remote wakeup + pbData[0] = 0; // TODO use bmAttributes according to configuration + pbData[1] = 0; + *piLen = 2; + break; + + case REQ_SET_ADDRESS: + USBHwSetAddress(pSetup->wValue); + break; + + case REQ_GET_DESCRIPTOR: + DBG("D%x", pSetup->wValue); + return USBGetDescriptor(pSetup->wValue, pSetup->wIndex, piLen, ppbData); + + case REQ_GET_CONFIGURATION: + // indicate if we are configured + pbData[0] = bConfiguration; + *piLen = 1; + break; + + case REQ_SET_CONFIGURATION: + if (!USBSetConfiguration(pSetup->wValue & 0xFF, 0)) { + DBG("USBSetConfiguration failed!\n"); + return FALSE; + } + // configuration successful, update current configuration + bConfiguration = pSetup->wValue & 0xFF; + break; + + case REQ_CLEAR_FEATURE: + case REQ_SET_FEATURE: + if (pSetup->wValue == FEA_REMOTE_WAKEUP) { + // put DEVICE_REMOTE_WAKEUP code here + } + if (pSetup->wValue == FEA_TEST_MODE) { + // put TEST_MODE code here + } + return FALSE; + + case REQ_SET_DESCRIPTOR: + DBG("Device req %d not implemented\n", pSetup->bRequest); + return FALSE; + + default: + DBG("Illegal device req %d\n", pSetup->bRequest); + return FALSE; + } + + return TRUE; +} + + +/** + Local function to handle a standard interface request + + @param [in] pSetup The setup packet + @param [in,out] *piLen Pointer to data length + @param [in] ppbData Data buffer. + + @return TRUE if the request was handled successfully + */ +static BOOL HandleStdInterfaceReq(TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + U8 *pbData = *ppbData; + + switch (pSetup->bRequest) { + + case REQ_GET_STATUS: + // no bits specified + pbData[0] = 0; + pbData[1] = 0; + *piLen = 2; + break; + + case REQ_CLEAR_FEATURE: + case REQ_SET_FEATURE: + // not defined for interface + return FALSE; + + case REQ_GET_INTERFACE: // TODO use bNumInterfaces + // there is only one interface, return n-1 (= 0) + pbData[0] = 0; + *piLen = 1; + break; + + case REQ_SET_INTERFACE: // TODO use bNumInterfaces + // there is only one interface (= 0) + if (pSetup->wValue != 0) { + return FALSE; + } + *piLen = 0; + break; + + default: + DBG("Illegal interface req %d\n", pSetup->bRequest); + return FALSE; + } + + return TRUE; +} + + +/** + Local function to handle a standard endpoint request + + @param [in] pSetup The setup packet + @param [in,out] *piLen Pointer to data length + @param [in] ppbData Data buffer. + + @return TRUE if the request was handled successfully + */ +static BOOL HandleStdEndPointReq(TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + U8 *pbData = *ppbData; + + switch (pSetup->bRequest) { + case REQ_GET_STATUS: + // bit 0 = endpointed halted or not + pbData[0] = USBHwEPIsStalled(pSetup->wIndex) ? 1 : 0; + pbData[1] = 0; + *piLen = 2; + break; + + case REQ_CLEAR_FEATURE: + if (pSetup->wValue == FEA_ENDPOINT_HALT) { + // clear HALT by unstalling + USBHwEPStall(pSetup->wIndex, FALSE); + break; + } + // only ENDPOINT_HALT defined for endpoints + return FALSE; + + case REQ_SET_FEATURE: + if (pSetup->wValue == FEA_ENDPOINT_HALT) { + // set HALT by stalling + USBHwEPStall(pSetup->wIndex, TRUE); + break; + } + // only ENDPOINT_HALT defined for endpoints + return FALSE; + + case REQ_SYNCH_FRAME: + DBG("EP req %d not implemented\n", pSetup->bRequest); + return FALSE; + + default: + DBG("Illegal EP req %d\n", pSetup->bRequest); + return FALSE; + } + + return TRUE; +} + + +/** + Default handler for standard ('chapter 9') requests + + If a custom request handler was installed, this handler is called first. + + @param [in] pSetup The setup packet + @param [in,out] *piLen Pointer to data length + @param [in] ppbData Data buffer. + + @return TRUE if the request was handled successfully + */ +BOOL USBHandleStandardRequest(TSetupPacket *pSetup, int *piLen, U8 **ppbData) +{ + // try the custom request handler first + if ((pfnHandleCustomReq != NULL) && pfnHandleCustomReq(pSetup, piLen, ppbData)) { + return TRUE; + } + + switch (REQTYPE_GET_RECIP(pSetup->bmRequestType)) { + case REQTYPE_RECIP_DEVICE: return HandleStdDeviceReq(pSetup, piLen, ppbData); + case REQTYPE_RECIP_INTERFACE: return HandleStdInterfaceReq(pSetup, piLen, ppbData); + case REQTYPE_RECIP_ENDPOINT: return HandleStdEndPointReq(pSetup, piLen, ppbData); + default: return FALSE; + } +} + + +/** + Registers a callback for custom device requests + + In USBHandleStandardRequest, the custom request handler gets a first + chance at handling the request before it is handed over to the 'chapter 9' + request handler. + + This can be used for example in HID devices, where a REQ_GET_DESCRIPTOR + request is sent to an interface, which is not covered by the 'chapter 9' + specification. + + @param [in] pfnHandler Callback function pointer + */ +void USBRegisterCustomReqHandler(TFnHandleRequest *pfnHandler) +{ + pfnHandleCustomReq = pfnHandler; +} + diff --git a/poc/lpc2148_usb/target/usbstruct.h b/poc/lpc2148_usb/target/usbstruct.h new file mode 100644 index 0000000..915e490 --- /dev/null +++ b/poc/lpc2148_usb/target/usbstruct.h @@ -0,0 +1,94 @@ +/** + (c) 2006, Bertrik Sikken, bertrik@sikken.nl + + definitions of structure of standard USB packets +*/ + +#ifndef _USBSTRUCT_H_ +#define _USBSTRUCT_H_ + + +#include "type.h" + + +/** setup packet definitions */ +typedef struct { + U8 bmRequestType; /**< characteristics of the specific request */ + U8 bRequest; /**< specific request */ + U16 wValue; /**< request specific parameter */ + U16 wIndex; /**< request specific parameter */ + U16 wLength; /**< length of data transfered in data phase */ +} TSetupPacket; + + +#define REQTYPE_GET_DIR(x) (((x)>>7)&0x01) +#define REQTYPE_GET_TYPE(x) (((x)>>5)&0x03) +#define REQTYPE_GET_RECIP(x) ((x)&0x1F) + +#define REQTYPE_DIR_TO_DEVICE 0 +#define REQTYPE_DIR_TO_HOST 1 + +#define REQTYPE_TYPE_STANDARD 0 +#define REQTYPE_TYPE_CLASS 1 +#define REQTYPE_TYPE_VENDOR 2 +#define REQTYPE_TYPE_RESERVED 3 + +#define REQTYPE_RECIP_DEVICE 0 +#define REQTYPE_RECIP_INTERFACE 1 +#define REQTYPE_RECIP_ENDPOINT 2 +#define REQTYPE_RECIP_OTHER 3 + +/* standard requests */ +#define REQ_GET_STATUS 0x00 +#define REQ_CLEAR_FEATURE 0x01 +#define REQ_SET_FEATURE 0x03 +#define REQ_SET_ADDRESS 0x05 +#define REQ_GET_DESCRIPTOR 0x06 +#define REQ_SET_DESCRIPTOR 0x07 +#define REQ_GET_CONFIGURATION 0x08 +#define REQ_SET_CONFIGURATION 0x09 +#define REQ_GET_INTERFACE 0x0A +#define REQ_SET_INTERFACE 0x0B +#define REQ_SYNCH_FRAME 0x0C + +/* class requests HID */ +#define HID_GET_REPORT 0x01 +#define HID_GET_IDLE 0x02 +#define HID_GET_PROTOCOL 0x03 +#define HID_SET_REPORT 0x09 +#define HID_SET_IDLE 0x0A +#define HID_SET_PROTOCOL 0x0B + +/* feature selectors */ +#define FEA_ENDPOINT_HALT 0x00 +#define FEA_REMOTE_WAKEUP 0x01 +#define FEA_TEST_MODE 0x02 + +/* + USB descriptors +*/ + +/** USB descriptor header */ +typedef struct { + U8 bLength; /**< descriptor length */ + U8 bDescriptorType; /**< descriptor type */ +} TUSBDescHeader; + +#define DESC_DEVICE 1 +#define DESC_CONFIGURATION 2 +#define DESC_STRING 3 +#define DESC_INTERFACE 4 +#define DESC_ENDPOINT 5 +#define DESC_DEVICE_QUALIFIER 6 +#define DESC_OTHER_SPEED 7 +#define DESC_INTERFACE_POWER 8 + +#define DESC_HID_HID 0x21 +#define DESC_HID_REPORT 0x22 +#define DESC_HID_PHYSICAL 0x23 + +#define GET_DESC_TYPE(x) (((x)>>8)&0xFF) +#define GET_DESC_INDEX(x) ((x)&0xFF) + +#endif /* _USBSTRUCT_H_ */ +