diff --git a/.gitignore b/.gitignore index dbbd4db..97ba5a0 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ mips/nanonote/server/missing mips/nanonote/server/usb-server fs/ iris-sd.tar +unbrick diff --git a/mips/nand.hhp b/mips/nand.hhp index eed9c17..dc05740 100644 --- a/mips/nand.hhp +++ b/mips/nand.hhp @@ -320,4 +320,4 @@ static void erase (unsigned a): addr (row >> 8) addr (row >> 16) cmd (CMD_ERASE2) - debug ("nand erase %d done\n", a) + //debug ("nand erase %d done\n", a) diff --git a/mips/nanonote/Makefile.arch b/mips/nanonote/Makefile.arch index dba5af0..6fe6a7f 100644 --- a/mips/nanonote/Makefile.arch +++ b/mips/nanonote/Makefile.arch @@ -17,16 +17,20 @@ start_load = 0x80600000 load = 0x80000000 -UDC_BOOT = comment this out for sd boot +# Uncomment one of these to select the boot method for the image. +#UDC_BOOT = yes +#SD_BOOT = yes +UNBRICK = yes arch_iris_sources = mips/interrupts.cc mips/arch.cc boot_sources = mips/init.cc mips/nanonote/board.cc arch_headers = mips/arch.hh mips/nanonote/jz4740.hh mips/nanonote/board.hh mips/nand.hh udc_boot_programs = udc sd_boot_programs = sd+mmc partition fat +unbrick_boot_programs = nand usb-mass-storage standard_boot_programs = bootinit -programs = init usb-mass-storage gpio lcd bsquare ball buzzer metronome elfrun alarm rtc gui nand test boot booter $(udc_boot_programs) $(sd_boot_programs) $(standard_boot_programs) +programs = init gpio lcd bsquare ball buzzer metronome elfrun alarm rtc gui test boot booter $(udc_boot_programs) $(sd_boot_programs) $(unbrick_boot_programs) $(standard_boot_programs) ARCH_CPPFLAGS = -I. -Imips -Imips/nanonote -Wa,-mips32 -DNANONOTE -DUSE_SERIAL CROSS = mipsel-linux-gnu- @@ -34,21 +38,35 @@ OBJDUMP = $(CROSS)objdump junk = mdebug.abi32 reginfo comment pdr OBJCOPYFLAGS = $(addprefix --remove-section=.,$(junk)) -ifdef UDC_BOOT +ifneq ($(UDC_BOOT),) boot_threads = $(standard_boot_programs) $(udc_boot_programs) -threadlist = mips/nanonote/threadlist-udc ARCH_CXXFLAGS = -DNUM_THREADS=2 +BOOT_CPPFLAGS = -DUDCBOOT all: mips/nanonote/nand-boot.raw test mips/start.o: TARGET = else +ifneq ($(SD_BOOT),) boot_threads = $(standard_boot_programs) $(sd_boot_programs) -threadlist = mips/nanonote/threadlist-sd ARCH_CXXFLAGS = -DNUM_THREADS=4 +BOOT_CPPFLAGS = -DSDBOOT all: mips/nanonote/nand-boot.raw iris-sd.tar mips/start.o: TARGET = -DWRAPPED iris-sd.tar: $(addprefix fs/,$(addsuffix .elf,$(programs))) mips/start.raw.gz fs/init.config mkimage -A mips -T kernel -a $(start_load) -e $(shell /bin/sh -c '$(OBJDUMP) -t mips/start.elf | grep __start$$ | cut -b1-8') -n Iris -d mips/start.raw.gz fs/uimage | sed -e 's/:/;/g' cd fs && tar cvf ../$@ uimage init.config $(addsuffix .elf,$(programs)) --dereference +else +ifneq ($(UNBRICK),) +boot_threads = $(standard_boot_programs) $(unbrick_boot_programs) +ARCH_CXXFLAGS = -DNUM_THREADS=3 +BOOT_CPPFLAGS = -DUNBRICK +all: mips/nanonote/nand-boot.raw iris.raw unbrick +mips/start.o: TARGET = +unbrick: mips/nanonote/unbrick.cc + g++ -Wall -Wextra -Werror `pkg-config --cflags --libs shevek` -lusb $< -o $@ +else +error Please define your boot method. +endif +endif endif iris.elf: LDFLAGS = --omagic -Ttext $(load) @@ -83,7 +101,7 @@ nanonote-boot: mips/nanonote/nanonote-boot.cc mips/nanonote/sdram-setup.raw mips/nanonote/sdram-setup.elf: mips/nanonote/sdram-setup.ld mips/nanonote/sdram-setup.elf: LDFLAGS = --omagic -T mips/nanonote/sdram-setup.ld -$(threadlist).o: $(addprefix fs/,$(addsuffix .elf,$(boot_threads))) +mips/nanonote/threadlist.o: $(addprefix fs/,$(addsuffix .elf,$(boot_threads))) mips/boot.o: TARGET_FLAGS = -DMEMORY_SIZE="32 << 20" mips/init.o: TARGET_FLAGS = -I/usr/include source/bootinit.o: TARGET_FLAGS = -I/usr/include @@ -99,10 +117,10 @@ source/charset.data: source/charset $< > $@ %.o:%.S Makefile Makefile.arch mips/arch.hh - $(CC) $(CPPFLAGS) $(TARGET_FLAGS) -DKERNEL_STACK_SIZE=0x1000 -c $< -o $@ + $(CC) $(CPPFLAGS) $(BOOT_CPPFLAGS) $(TARGET_FLAGS) -DKERNEL_STACK_SIZE=0x1000 -c $< -o $@ # entry.o must be the first file. threadlist.o must be the first of the init objects (which can be freed after loading). -iris.elf: mips/entry.o $(subst .cc,.o,$(iris_sources)) $(threadlist).o mips/boot.o $(subst .cc,.o,$(boot_sources)) +iris.elf: mips/entry.o $(subst .cc,.o,$(iris_sources)) mips/nanonote/threadlist.o mips/boot.o $(subst .cc,.o,$(boot_sources)) $(LD) $(LDFLAGS) $^ -o $@ mips/start.elf: mips/start.o diff --git a/mips/nanonote/threadlist-udc.S b/mips/nanonote/threadlist-udc.S deleted file mode 100644 index 461193a..0000000 --- a/mips/nanonote/threadlist-udc.S +++ /dev/null @@ -1,39 +0,0 @@ -// Iris: micro-kernel for a capability-based operating system. -// mips/nanonote/threadlist.S: List of initial threads. -// Copyright 2009 Bas Wijnen -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - - .globl init_start - .globl thread_start - .set noreorder - - .balign 0x1000 -thread0: - .incbin "fs/bootinit.elf" - - .balign 0x1000 -thread1: - .incbin "fs/udc.elf" - - .balign 0x1000 -thread2: - -// Everything from here may be freed after kernel initialization. -init_start: - -thread_start: - .word thread0 - .word thread1 - .word thread2 diff --git a/mips/nanonote/threadlist-sd.S b/mips/nanonote/threadlist.S similarity index 63% rename from mips/nanonote/threadlist-sd.S rename to mips/nanonote/threadlist.S index 04c343a..3414824 100644 --- a/mips/nanonote/threadlist-sd.S +++ b/mips/nanonote/threadlist.S @@ -19,6 +19,27 @@ .globl thread_start .set noreorder +#if defined (UDCBOOT) + .balign 0x1000 +thread0: + .incbin "fs/bootinit.elf" + + .balign 0x1000 +thread1: + .incbin "fs/udc.elf" + + .balign 0x1000 +thread2: + +// Everything from here may be freed after kernel initialization. +init_start: + +thread_start: + .word thread0 + .word thread1 + .word thread2 + +#elif defined (SDBOOT) .balign 0x1000 thread0: .incbin "fs/bootinit.elf" @@ -46,4 +67,31 @@ thread_start: .word thread1 .word thread2 .word thread3 - .word thread4 +#elif defined (UNBRICK) + .balign 0x1000 +thread0: + .incbin "fs/bootinit.elf" + + .balign 0x1000 +thread1: + .incbin "fs/nand.elf" + + .balign 0x1000 +thread2: + .incbin "fs/usb-mass-storage.elf" + + .balign 0x1000 +thread3: + +// Everything from here may be freed after kernel initialization. +init_start: + +thread_start: + .word thread0 + .word thread1 + .word thread2 + .word thread3 + +#else + #error "boot method not defined" +#endif diff --git a/mips/nanonote/unbrick.ccp b/mips/nanonote/unbrick.ccp new file mode 100644 index 0000000..1b7e45f --- /dev/null +++ b/mips/nanonote/unbrick.ccp @@ -0,0 +1,135 @@ +#pypp 0 +// Iris: micro-kernel for a capability-based operating system. +// mips/nanonote/unbrick.ccp: Host-side helper for USB boot. +// Copyright 2009-2010 Bas Wijnen +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STAGE1_FILE "stage1.raw" + +static usb_dev_handle *handle +static int const boot_vendor = 0x601a +static int const boot_product = 0x4740 +static int const run_vendor = 0xfffe +static int const run_product = 0x0002 +static unsigned const timeout = 10000 +void boot (std::string const &filename, unsigned load, unsigned entry) +static unsigned const STAGE1_LOAD = 0x80002000 +static unsigned const STAGE1_ENTRY = STAGE1_LOAD +enum requests: + VR_GET_CPU_INFO = 0 + VR_SET_DATA_ADDRESS = 1 + VR_SET_DATA_LENGTH = 2 + VR_FLUSH_CACHES = 3 + VR_PROGRAM_START1 = 4 + VR_PROGRAM_START2 = 5 +void request (requests num, unsigned data = 0) +void get_device (unsigned vendor, unsigned product, unsigned tries) + +void request (requests r, unsigned data): + if usb_control_msg (handle, USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, r, (data >> 16) & 0xffff, data & 0xffff, NULL, 0, timeout) < 0: + std::cerr << "unable to send control message to NanoNote: " << usb_strerror () << ".\n" + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + +void send_file (unsigned address, int size, char const *data): + request (VR_SET_DATA_ADDRESS, address) + char const *ptr = data + while ptr - data < size: + int ret = usb_bulk_write (handle, 1, ptr, size - (ptr - data), timeout) + if ret <= 0: + std::cerr << "failed to write to NanoNote.\n" + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + return + ptr += ret + +void get_device (unsigned vendor, unsigned product, unsigned tries): + for unsigned i = 0; i < tries; ++i: + usb_find_busses () + usb_find_devices () + for struct usb_bus *bus = usb_busses; bus; bus = bus->next: + for struct usb_device *dev = bus->devices; dev; dev = dev->next: + if dev->descriptor.idProduct != product || dev->descriptor.idVendor != vendor: + //std::cerr << shevek::ostring ("Not using %04x:%04x when looking for %04x:%04x\n", dev->descriptor.idVendor, dev->descriptor.idProduct, vendor, product) + continue + handle = usb_open (dev) + if usb_claim_interface (handle, 0) < 0: + std::cerr << "unable to claim interface: " << usb_strerror () << "\n" + usb_close (handle) + handle = NULL + continue + return + if i + 1 < tries: + //std::cerr << "failed to find device, still trying...\n" + sleep (1) + std::cerr << shevek::ostring ("giving up finding device %04x:%04x\n", vendor, product) + +void boot (std::string const &filename, unsigned load, unsigned entry): + std::cerr << "booting " << shevek::ostring ("%s from %x@%x", Glib::ustring (filename), load, entry) << "\n" + get_device (boot_vendor, boot_product, 1) + if !handle: + std::cerr << "unable to find device\n" + return + std::cerr << "sending stage 1\n" + std::ifstream file (STAGE1_FILE) + std::ostringstream stage1 + stage1 << file.rdbuf () + send_file (STAGE1_LOAD, stage1.str ().size (), stage1.str ().data ()) + std::cerr << "running stage 1\n" + request (VR_PROGRAM_START1, STAGE1_ENTRY) + usleep (100) + std::ostringstream stage2 + usb_release_interface (handle, 0) + file.close () + file.open (filename.c_str ()) + stage2 << file.rdbuf () + std::cerr << shevek::ostring ("sending Iris (size 0x%x)\n", stage2.str ().size ()) + send_file (load, stage2.str ().size (), stage2.str ().data ()) + std::cerr << "flushing caches\n" + request (VR_FLUSH_CACHES) + std::cerr << "running Iris\n" + request (VR_PROGRAM_START2, entry) + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + std::cerr << "done\n" + +int main (int argc, char **argv): + std::string filename ("iris.raw") + unsigned load (0x80000000), entry (0) + handle = NULL + usb_init () + shevek::args::option opts[] = { + shevek::args::option ('f', "file", "image file", true, filename), + shevek::args::option ('l', "load", "load address", true, load), + shevek::args::option ('e', "entry", "entry point address", false, entry) + } + shevek::args args (argc, argv, opts, 0, 0, "unbrick program for NanoNote", "2009-2010") + if !entry: + shevek_error ("You must specify the entry point") + boot (filename, load, entry) + return 0 diff --git a/source/nand.ccp b/source/nand.ccp index 511e874..627f64e 100644 --- a/source/nand.ccp +++ b/source/nand.ccp @@ -30,7 +30,7 @@ static bool dirty static unsigned current_block static void sync (): - Iris::debug ("erasing %x\n", current_block << block_bits) + //Iris::debug ("erasing %x\n", current_block << block_bits) erase (current_block << block_bits) for unsigned p = 0; p < 1 << (block_bits - page_bits); ++p: write ((current_block << block_bits) + (p << page_bits), (char *)&cache[p << (page_bits - 2)]) diff --git a/source/usb-mass-storage.ccp b/source/usb-mass-storage.ccp index 49ae4b1..66fa5f0 100644 --- a/source/usb-mass-storage.ccp +++ b/source/usb-mass-storage.ccp @@ -28,20 +28,15 @@ IDLE: after reset or csw. IN interrupt: csw received, do nothing. OUT interrupt: cbw; handle -> IDLE (no data; csw sent) - -> CSW (data sent in one packet) - -> TX (more than one packet to send) + -> TX (send) -> RX (receive packets) TX: transmitting data. IN interrupt: host received data; send more. -> TX (more to send) - -> CSW (last data has now been sent) RX: receiving data. OUT interrupt: host sent data; handle. -> RX (more to receive) -> IDLE (done receiving; send csw) -CSW: waiting to transmit csw. - IN interrupt: TX is done; send csw - -> IDLE #endif extern "C": @@ -218,17 +213,27 @@ class Udc: char configuration unsigned get_descriptor (unsigned type, unsigned idx, unsigned len) unsigned handle_setup (Setup *s) - void irq_usb () + void reset () void irq_in0 () - void irq_out () + void handle_rx () + void handle_tx () + void handle_cbw () void send_csw () unsigned big_endian (unsigned src) bool handle_interrupt (bool usb, bool in) void stall (unsigned error) - bool stalling[3] + bool stalling + enum State: + IDLE + TX + RX + SENT_CSW + STALL + State state unsigned residue unsigned status unsigned tag + unsigned data_done, lba, blocks unsigned block_bits Iris::WBlock block Iris::Page buffer_page @@ -248,6 +253,35 @@ Udc::String <6> Udc::s_manufacturer Udc::String <16> Udc::s_product Udc::String <12> Udc::s_serial +void Udc::reset (): + // Reset. + UDC_TESTMODE = 0 + configuration = 0 + state = IDLE + status = 0 + residue = 0 + // enable interrupt on bus reset. + UDC_INTRUSBE = UDC_INTR_RESET + // enable interrupts on endpoint 0 and in endpoint 2 + UDC_INTRINE = 1 << 0 | 1 << 2 + // and on out endpoint 1. + UDC_INTROUTE = 1 << 1 + // exit suspend mode by reading the interrupt register. + unsigned i = UDC_INTRUSB + // reset all pending endpoint interrupts. + i = UDC_INTRIN + i = UDC_INTROUT + UDC_INDEX = 1 + UDC_OUTMAXP = max_packet_size_bulk + // Do this twice to flush a double-buffered fifo completely. + UDC_OUTCSR |= UDC_OUTCSR_CDT | UDC_OUTCSR_FF + UDC_OUTCSR |= UDC_OUTCSR_CDT | UDC_OUTCSR_FF + UDC_INDEX = 2 + UDC_INMAXP = max_packet_size_bulk + UDC_INCSR = (UDC_INCSRH_MODE << 8) | UDC_INCSR_CDT | UDC_INCSR_FF + UDC_INCSR = (UDC_INCSRH_MODE << 8) | UDC_INCSR_CDT | UDC_INCSR_FF + //Iris::debug ("usb reset\n") + void Udc::init (Iris::WBlock b): block = b block_bits = block.get_align_bits () @@ -272,37 +306,12 @@ void Udc::init (Iris::WBlock b): cpm_start_udc () // Disconnect from the bus and don't try to get high-speed. UDC_POWER = 0 - UDC_TESTMODE = 0 - configuration = 0 - // Set max packet sizes. - UDC_INDEX = 1 - UDC_OUTMAXP = max_packet_size_bulk - UDC_OUTCSR = UDC_OUTCSR_CDT | UDC_OUTCSR_FF - UDC_OUTCSR = UDC_OUTCSR_CDT | UDC_OUTCSR_FF - UDC_INDEX = 2 - UDC_INMAXP = max_packet_size_bulk - UDC_INCSR = (UDC_INCSRH_MODE << 8) | UDC_INCSR_CDT | UDC_INCSR_FF - UDC_INCSR = (UDC_INCSRH_MODE << 8) | UDC_INCSR_CDT | UDC_INCSR_FF - // exit suspend mode by reading the interrupt register. - unsigned i = UDC_INTRUSB - // reset all pending endpoint interrupts. - i = UDC_INTRIN - i = UDC_INTROUT - // enable interrupt on bus reset. - UDC_INTRUSBE = UDC_INTR_RESET - // enable interrupts on endpoint 0 and in endpoint 2 - UDC_INTRINE = 1 << 0 | 1 << 2 - // and on out endpoint 1. - UDC_INTROUTE = 1 << 1 + reset () // Wait a while. Iris::sleep (HZ / 10) // Connect to the host. UDC_POWER = UDC_POWER_SOFTCONN - // Initialize cbw state - status = 0 - residue = 0 - void Udc::send (unsigned ep, char const *data, unsigned length, unsigned maxlength): if maxlength < length: length = maxlength @@ -318,8 +327,6 @@ void Udc::send (unsigned ep, char const *data, unsigned length, unsigned maxleng void Udc::send_padded (char const *data, unsigned length, unsigned maxlength): UDC_INDEX = 2 - if UDC_INCSR & UDC_INCSR_INPKTRDY: - Iris::panic (0, "sending padded not possible because a packet is already waiting.\n") unsigned len = length < maxlength ? length : maxlength residue = maxlength - len len = (len + 3) & ~3 @@ -328,48 +335,14 @@ void Udc::send_padded (char const *data, unsigned length, unsigned maxlength): while len + 3 < maxlength: UDC_FIFO (2) = 0 len += 4 - if len % max_packet_size_bulk == 0: - // This doesn't ever happen, because the largest packet we send is smaller than max_packet_size_bulk. - Iris::debug ("sending at len %x\n", len) - UDC_INCSR |= UDC_INCSR_INPKTRDY - while true: - Iris::register_interrupt (IRQ_UDC) - Iris::wait_for_interrupt (IRQ_UDC) - kdebug ("interrupt pad0\n") - unsigned usb = UDC_INTRUSB - unsigned in = UDC_INTRIN - if usb & 4 || in & 1: - //kdebug ("general interrupt pad0\t") - if !handle_interrupt (usb & 4, in & 1): - return - unsigned out = UDC_INTROUT - if out & 2: - Iris::panic (0, "out interrupt while waiting for in") - if in & 4: - break //kdebug_char ('-') - if len % max_packet_size_bulk != 0 || len < maxlength: - while len < maxlength: - UDC_FIFO8 (2) = 0 - ++len - //kdebug_char ('.') - UDC_INCSR |= UDC_INCSR_INPKTRDY - while true: - Iris::register_interrupt (IRQ_UDC) - Iris::wait_for_interrupt (IRQ_UDC) - kdebug ("interrupt pad\t") - unsigned usb = UDC_INTRUSB - unsigned in = UDC_INTRIN - if usb & 4 || in & 1: - //kdebug ("general interrupt pad\t") - if !handle_interrupt (usb & 4, in & 1): - return - unsigned out = UDC_INTROUT - if out & 2: - Iris::panic (0, "out interrupt while waiting for in") - if in & 4: - break - //kdebug ("done interrupt pad\n") + while len < maxlength: + UDC_FIFO8 (2) = 0 + ++len + //kdebug_char ('.') + UDC_INCSR |= UDC_INCSR_INPKTRDY + blocks = 0 + state = TX unsigned Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): switch type: @@ -424,18 +397,18 @@ unsigned Udc::handle_setup (Setup *s): switch s->request: case SET_ADDRESS: UDC_FADDR = s->value - //Iris::debug ("set address %x\n", s->value) + Iris::debug ("set address %x\n", s->value) return 0 case SET_CONFIGURATION: if s->value >= 2: return ~0 configuration = s->value - //Iris::debug ("set configuration %x\n", s->value) + Iris::debug ("set configuration %x\n", s->value) return 0 case SET_INTERFACE: if s->value != 0: return ~0 - //Iris::debug ("set interface %x\n", s->value) + Iris::debug ("set interface %x\n", s->value) return 0 default: return ~0 @@ -444,17 +417,17 @@ unsigned Udc::handle_setup (Setup *s): UDC_CSR0 = UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY switch s->request: case GET_STATUS: - //Iris::debug ("get status\t") + Iris::debug ("get status\t") send (0, "\0\0", 2, s->length) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND case GET_DESCRIPTOR: return get_descriptor ((s->value >> 8) & 0xff, s->value & 0xff, s->length) case GET_CONFIGURATION: - //Iris::debug ("get configuration\t") + Iris::debug ("get configuration\t") send (0, &configuration, 1, s->length) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND case GET_INTERFACE: - //Iris::debug ("get interface\t") + Iris::debug ("get interface\t") send (0, "\0", 1, s->length) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND default: @@ -466,16 +439,16 @@ unsigned Udc::handle_setup (Setup *s): case ENDPOINT_HALT: switch s->index: case 0x82: - Iris::debug ("in ep halt reset\n") + //Iris::debug ("in ep halt reset\n") UDC_INDEX = 2 - UDC_INCSR &= ~UDC_INCSR_SENDSTALL - stalling[2] = false + UDC_INCSR = (UDC_INCSR & ~UDC_INCSR_SENDSTALL) | UDC_INCSR_CDT + stalling = false + send_csw () break case 1: - Iris::debug ("out ep halt reset\n") + //Iris::panic (0, "halt reset on out endpoint") UDC_INDEX = 1 - UDC_OUTCSR &= ~UDC_OUTCSR_SENDSTALL - stalling[1] = false + UDC_OUTCSR |= UDC_OUTCSR_CDT break default: return ~0 @@ -499,7 +472,8 @@ unsigned Udc::handle_setup (Setup *s): UDC_CSR0 = UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY switch s->request: case BULK_ONLY_RESET: - //Iris::debug ("bulk reset\n") + Iris::debug ("bulk reset\n") + state = IDLE return 0 default: return ~0 @@ -507,52 +481,32 @@ unsigned Udc::handle_setup (Setup *s): Iris::debug ("request: %x %x %x %x %x\n", s->request_type, s->request, s->index, s->length, s->value) return ~0 -void Udc::irq_usb (): - // Reset. - // enable interrupts on endpoint 0 and in endpoint 2 - UDC_INTRINE = 1 << 0 | 1 << 2 - // and on out endpoint 1. - UDC_INTROUTE = 1 << 1 - UDC_INDEX = 1 - // Do this twice to flush a double-buffered fifo completely. - UDC_OUTMAXP = max_packet_size_bulk - UDC_OUTCSR |= UDC_OUTCSR_CDT | UDC_OUTCSR_FF - UDC_OUTCSR |= UDC_OUTCSR_CDT | UDC_OUTCSR_FF - UDC_INDEX = 2 - UDC_INMAXP = max_packet_size_bulk - UDC_INCSR |= UDC_INCSR_CDT - //Iris::debug ("usb reset\n") - void Udc::irq_in0 (): // Interrupt on endpoint 0. UDC_INDEX = 0 unsigned csr = UDC_CSR0 if csr & UDC_CSR0_SENTSTALL: UDC_CSR0 = 0 - //Iris::debug ("stall done\t") + //Iris::debug ("stall 0 done\t") if csr & UDC_CSR0_SETUPEND: UDC_CSR0 = UDC_CSR0_SVDSETUPEND - //Iris::debug ("setup aborted\t") + Iris::debug ("setup aborted\t") if !(csr & UDC_CSR0_OUTPKTRDY): //Iris::debug ("no packet 0: %x\n", csr) return - UDC_INDEX = 1 - UDC_OUTCSR |= UDC_OUTCSR_CDT - UDC_INDEX = 2 - UDC_INCSR |= UDC_INCSR_CDT UDC_INDEX = 0 union { unsigned d[2]; Setup s; } packet packet.d[0] = UDC_FIFO (0) packet.d[1] = UDC_FIFO (0) if !(packet.s.request_type & 0x80) && packet.s.length > 0: // More data will follow; unsupported. - //Iris::debug ("packet on ep0 too long\n") + Iris::debug ("packet on ep0 too long\n") UDC_CSR0 = UDC_CSR0_SENDSTALL return unsigned ret = handle_setup (&packet.s) UDC_INDEX = 0 if ret == ~0: - Iris::debug ("failed setup: %x %x %x %x %x\n", packet.s.request_type, packet.s.request, packet.s.index, packet.s.length, packet.s.value) + //Iris::debug ("failed setup: %x %x %x %x %x\n", packet.s.request_type, packet.s.request, packet.s.index, packet.s.length, packet.s.value) UDC_CSR0 = UDC_CSR0_SENDSTALL return if ret: @@ -566,79 +520,73 @@ void Udc::send_csw (): UDC_FIFO (2) = residue UDC_FIFO8 (2) = status UDC_INCSR |= UDC_INCSR_INPKTRDY + state = SENT_CSW status = 0 residue = 0 - while true: - Iris::register_interrupt (IRQ_UDC) - Iris::wait_for_interrupt (IRQ_UDC) - kdebug ("interrupt csw\n") - unsigned usb = UDC_INTRUSB - unsigned in = UDC_INTRIN - if usb & 4 || in & 1: - if !handle_interrupt (usb & 4, in & 1): - return - if in & 4: - break - unsigned out = UDC_INTROUT - if out & 2: - Iris::panic (0, "out interrupt while waiting for in after csw") - kdebug ("sent csw\n") + //kdebug ("sent csw\n") void Udc::stall (unsigned error): - unsigned index = UDC_INDEX - if stalling[index]: + if stalling: Iris::debug ("already stalling!\n") - if index == 1: - UDC_OUTCSR |= UDC_OUTCSR_SENDSTALL - else: - UDC_INCSR |= UDC_INCSR_SENDSTALL - stalling[index] = true - while stalling[index]: - //Iris::debug ("stalling %d\n", index) - Iris::register_interrupt (IRQ_UDC) - Iris::wait_for_interrupt (IRQ_UDC) - kdebug ("stalling interrupt\n") - unsigned usb = UDC_INTRUSB - unsigned in = UDC_INTRIN - if in & 4: - if index != 2: - Iris::panic (0, "stalling on out, but in responds") - kdebug ("stall has been sent to in endpoint\n") - UDC_INDEX = 2 - UDC_INCSR &= ~UDC_INCSR_SENTSTALL - //Iris::debug ("csr: %x\n", UDC_INCSR) - if usb & 4 || in & 1: - //kdebug ("stuff\n") - if !handle_interrupt (usb & 4, in & 1): - return - unsigned out = UDC_INTROUT - if out & 2: - if index != 1: - Iris::panic (0, "stalling on in, but out responds") - kdebug ("stall has been sent to out endpoint\n") - UDC_INDEX = 1 - UDC_OUTCSR &= ~UDC_OUTCSR_SENTSTALL - //kdebug ("done stalling\n") - if index == 2: - status = error - send_csw () + UDC_INCSR |= UDC_INCSR_SENDSTALL + stalling = true + state = STALL unsigned Udc::big_endian (unsigned src): return src >> 24 | src >> 8 & 0xff00 | src << 8 & 0xff0000 | src << 24 -void Udc::irq_out (): +void Udc::handle_rx (): + buffer_page.set_flags (Iris::Page::FRAME) + UDC_INDEX = 1 + if !(UDC_OUTCSR & UDC_OUTCSR_OUTPKTRDY): + Iris::panic (0, "no packet ready after out interrupt during rx") + if UDC_OUTCOUNT != max_packet_size_bulk: + Iris::panic (UDC_OUTCOUNT, "invalid packet size during rx") + for unsigned t = 0; t < max_packet_size_bulk; t += 4: + ((unsigned *)buffer)[(t + data_done) >> 2] = UDC_FIFO (1) + UDC_OUTCSR &= ~UDC_OUTCSR_OUTPKTRDY + data_done += max_packet_size_bulk + if data_done == 1 << block_bits: + //Iris::debug ("writing block %x\n", lba) + block.set_block (lba << block_bits, buffer_page, 1 << block_bits) + data_done = 0 + --blocks + ++lba + if blocks == 0: + send_csw () + return + +void Udc::handle_tx (): + if blocks == 0: + send_csw () + return + if data_done == 0: + // read block lba. + buffer_page.set_flags (Iris::Page::FRAME) + block.get_block (lba << block_bits, 1 << block_bits, 0, buffer_page) + UDC_INDEX = 2 + for unsigned t = 0; t < max_packet_size_bulk; t += 4: + UDC_FIFO (2) = ((unsigned *)buffer)[(data_done + t) >> 2] + data_done += max_packet_size_bulk + if data_done == 1 << block_bits: + data_done = 0 + ++lba + --blocks + UDC_INCSR |= UDC_INCSR_INPKTRDY + +void Udc::handle_cbw (): UDC_INDEX = 1 unsigned csr = UDC_OUTCSR unsigned size = UDC_OUTCOUNT - if !(csr & UDC_OUTCSR_OUTPKTRDY): - // No packet, just a notification. - kdebug ("no packet\n") - return if csr & UDC_OUTCSR_SENDSTALL: // When stalling, do nothing else. //kdebug ("not responding to out during stall\n") UDC_OUTCSR = csr & ~UDC_OUTCSR_SENTSTALL return + if !(csr & UDC_OUTCSR_OUTPKTRDY): + // No packet; this shouldn't happen. + Iris::panic (0, "no packet") + return // expect a new cbw. if size != 31: Iris::debug ("count %d != 31\n", size) @@ -656,10 +604,17 @@ void Udc::irq_out (): UDC_OUTCSR = csr & ~UDC_OUTCSR_OUTPKTRDY tag = cbw.cbw.tag if cbw.cbw.sig != 0x43425355 || cbw.cbw.lun != 0 || cbw.cbw.size == 0 || cbw.cbw.size > 16: - Iris::debug ("sig %x lun %d size %d\n", cbw.cbw.sig, cbw.cbw.lun, cbw.cbw.size) + Iris::debug ("wrong cbw: sig %x lun %d size %d\n", cbw.cbw.sig, cbw.cbw.lun, cbw.cbw.size) stall (2) return //kdebug ("bulk cbw\t") + #if 0 + Iris::debug ("cbw:") + for unsigned i = 0; i < cbw.cbw.size; ++i: + kdebug_char (' ') + kdebug_num (cbw.cbw.data[i], 2) + Iris::debug ("\n") + #endif UDC_INDEX = 2 bool to_host = cbw.cbw.flags & 0x80 switch cbw.cbw.data[0]: @@ -673,7 +628,6 @@ void Udc::irq_out (): case CBW::REQUEST_SENSE: //Iris::debug ("sense requested\n") send_padded ("\xf0\x00\x05\x00\x00\x00\x00\x00", 8, cbw.cbw.length) - send_csw () break case CBW::FORMAT_UNIT: Iris::panic (0, "FORMAT_UNIT isn't implemented") @@ -682,8 +636,8 @@ void Udc::irq_out (): stall (2) return //Iris::debug ("sending inquiry response\t") + // TODO: find out why these bytes are messed up. send_padded ("\x00\x00\x04\x02\x1f\x00\x00\x00shevek iris usb stick \x00\x00\x04\x02", 36, cbw.cbw.length) - send_csw () break case CBW::RESERVE6: Iris::panic (0, "RESERVE6 isn't implemented") @@ -700,118 +654,94 @@ void Udc::irq_out (): capacity[1] = big_endian (1 << block_bits) //Iris::debug ("sending capacity: %x * %x\t", capacity[0], capacity[1]) send_padded ((char *)capacity, 8, cbw.cbw.length) - send_csw () break case CBW::READ10: - unsigned lba = cbw.cbw.data[2] << 24 | cbw.cbw.data[3] << 16 | cbw.cbw.data[4] << 8 | cbw.cbw.data[5] - unsigned blocks = cbw.cbw.data[7] << 8 | cbw.cbw.data[8] - for unsigned i = 0; i < blocks; ++i: - //Iris::debug ("reading block %d\n", lba + i) - // read block lba + i. - buffer_page.set_flags (Iris::Page::FRAME) - block.get_block ((lba + i) << block_bits, 1 << block_bits, 0, buffer_page) - for unsigned p = 0; p < 1 << block_bits; p += max_packet_size_bulk: - //Iris::debug (" %d", p) - UDC_INDEX = 2 - for unsigned t = 0; t < max_packet_size_bulk; t += 4: - UDC_FIFO (2) = ((unsigned *)buffer)[(p + t) >> 2] - UDC_INCSR |= UDC_INCSR_INPKTRDY - //Iris::debug ("\n") - while true: - Iris::register_interrupt (IRQ_UDC) - Iris::wait_for_interrupt (IRQ_UDC) - kdebug ("interrupt read10\n") - unsigned usb = UDC_INTRUSB - unsigned in = UDC_INTRIN - unsigned out = UDC_INTROUT - if usb & 4 || in & 1: - //kdebug ("general interrupt read10\t") - if !handle_interrupt (usb & 4, in & 1): - return - if out & 2: - Iris::panic (0, "out interrupt while waiting for in") - if in & 4: - break - send_csw () + if !to_host: + stall (2) + return + lba = cbw.cbw.data[2] << 24 | cbw.cbw.data[3] << 16 | cbw.cbw.data[4] << 8 | cbw.cbw.data[5] + blocks = cbw.cbw.data[7] << 8 | cbw.cbw.data[8] + data_done = 0 + state = TX + handle_tx () break case CBW::WRITE10: - unsigned lba = cbw.cbw.data[2] << 24 | cbw.cbw.data[3] << 16 | cbw.cbw.data[4] << 8 | cbw.cbw.data[5] - unsigned blocks = cbw.cbw.data[7] << 8 | cbw.cbw.data[8] - for unsigned i = 0; i < blocks; ++i: - //Iris::debug ("writing block %d\n", lba + i) - // write block lba + i. - buffer_page.set_flags (Iris::Page::FRAME) - //Iris::debug ("@%x:", (lba + i) << block_bits) - for unsigned p = 0; p < 1 << block_bits; p += max_packet_size_bulk: - while true: - Iris::register_interrupt (IRQ_UDC) - Iris::wait_for_interrupt (IRQ_UDC) - Iris::debug (".") - unsigned usb = UDC_INTRUSB - unsigned in = UDC_INTRIN - unsigned out = UDC_INTROUT - if usb & 4 || in & 1: - if !handle_interrupt (usb & 4, in & 1): - return - if out & 2: - break - if in & 4: - Iris::panic (0, "in interrupt while waiting for out") - UDC_INDEX = 1 - if !(UDC_OUTCSR & UDC_OUTCSR_OUTPKTRDY): - Iris::panic (0, "no packet ready after out interrupt in write10") - if UDC_OUTCOUNT != max_packet_size_bulk: - Iris::panic (UDC_OUTCOUNT, "invalid packet size in write10") - for unsigned t = 0; t < max_packet_size_bulk; t += 4: - ((unsigned *)buffer)[(p + t) >> 2] = UDC_FIFO (1) - //kdebug (" ") - //kdebug_num (((unsigned *)buffer)[(p + t) >> 2], 8) - UDC_OUTCSR &= ~UDC_OUTCSR_OUTPKTRDY - //kdebug ("\n") - //Iris::debug ("setting block %x@%x+%x\n", lba + i << block_bits, 0, 1 << block_bits) - block.set_block ((lba + i) << block_bits, buffer_page, 1 << block_bits) - send_csw () + if to_host: + stall (2) + return + lba = cbw.cbw.data[2] << 24 | cbw.cbw.data[3] << 16 | cbw.cbw.data[4] << 8 | cbw.cbw.data[5] + blocks = cbw.cbw.data[7] << 8 | cbw.cbw.data[8] + if blocks == 0: + send_csw () + break + state = RX + data_done = 0 + buffer_page.set_flags (Iris::Page::FRAME) break case CBW::RESERVE10: Iris::panic (0, "RESERVE10 isn't implemented") case CBW::RELEASE10: Iris::panic (0, "RELEASE10 isn't implemented") default: - Iris::debug ("cbw:") + #if 0 + Iris::debug ("unknown cbw:") for unsigned i = 0; i < cbw.cbw.size; ++i: kdebug_char (' ') kdebug_num (cbw.cbw.data[i], 2) Iris::debug ("\n") + #endif residue = cbw.cbw.length stall (1) return -bool Udc::handle_interrupt (bool usb, bool in): - if usb: - //Iris::debug ("usb\t") - // reset. - irq_usb () - return false - if in: - //Iris::debug ("control\t") - // control request - irq_in0 () - return true - void Udc::interrupt (): - Iris::debug ("interrupt\n") + //Iris::debug ("interrupt, state = %d\n", state) while true: + bool action = false unsigned usb = UDC_INTRUSB unsigned in = UDC_INTRIN - bool action = false - if in & 4: - Iris::panic (0, "data request during idle\n") - if usb & 4 || in & 1: - handle_interrupt (usb & 4, in & 1) - action = true unsigned out = UDC_INTROUT + if usb & 4: + //Iris::debug ("reset\n") + reset () + action = true + if state == STALL && in & 4: + // This must be handled here, because the state can be changed by the control request. + //Iris::debug ("stalling\n") + in &= ~4 + if in & 1: + //Iris::debug ("control request\n") + irq_in0 () + action = true + if in & 4: + //Iris::debug ("in request\n") + // Notification of sent packet (or stall, but we don't do that on the in endpoint). + switch state: + case SENT_CSW: + // csw received. + state = IDLE + break + case TX: + handle_tx () + break + default: + Iris::panic (state, "invalid state for data send") + stall (2) + break + action = true if out & 2: - irq_out () + //Iris::debug ("out request\n") + switch state: + case IDLE: + handle_cbw () + break + case RX: + handle_rx () + break + default: + stall (2) + Iris::panic (0, "invalid state for data receive") + break action = true if !action: // No more interrupts to handle; this is normal, because we're looping until this happens.