mirror of
git://projects.qi-hardware.com/iris.git
synced 2024-12-29 03:59:53 +02:00
Add unbricking method
This commit is contained in:
parent
5f9fd7cc0f
commit
9aff93c300
1
.gitignore
vendored
1
.gitignore
vendored
@ -23,3 +23,4 @@ mips/nanonote/server/missing
|
|||||||
mips/nanonote/server/usb-server
|
mips/nanonote/server/usb-server
|
||||||
fs/
|
fs/
|
||||||
iris-sd.tar
|
iris-sd.tar
|
||||||
|
unbrick
|
||||||
|
@ -320,4 +320,4 @@ static void erase (unsigned a):
|
|||||||
addr (row >> 8)
|
addr (row >> 8)
|
||||||
addr (row >> 16)
|
addr (row >> 16)
|
||||||
cmd (CMD_ERASE2)
|
cmd (CMD_ERASE2)
|
||||||
debug ("nand erase %d done\n", a)
|
//debug ("nand erase %d done\n", a)
|
||||||
|
@ -17,16 +17,20 @@
|
|||||||
|
|
||||||
start_load = 0x80600000
|
start_load = 0x80600000
|
||||||
load = 0x80000000
|
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
|
arch_iris_sources = mips/interrupts.cc mips/arch.cc
|
||||||
boot_sources = mips/init.cc mips/nanonote/board.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
|
arch_headers = mips/arch.hh mips/nanonote/jz4740.hh mips/nanonote/board.hh mips/nand.hh
|
||||||
udc_boot_programs = udc
|
udc_boot_programs = udc
|
||||||
sd_boot_programs = sd+mmc partition fat
|
sd_boot_programs = sd+mmc partition fat
|
||||||
|
unbrick_boot_programs = nand usb-mass-storage
|
||||||
standard_boot_programs = bootinit
|
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
|
ARCH_CPPFLAGS = -I. -Imips -Imips/nanonote -Wa,-mips32 -DNANONOTE -DUSE_SERIAL
|
||||||
CROSS = mipsel-linux-gnu-
|
CROSS = mipsel-linux-gnu-
|
||||||
@ -34,21 +38,35 @@ OBJDUMP = $(CROSS)objdump
|
|||||||
junk = mdebug.abi32 reginfo comment pdr
|
junk = mdebug.abi32 reginfo comment pdr
|
||||||
OBJCOPYFLAGS = $(addprefix --remove-section=.,$(junk))
|
OBJCOPYFLAGS = $(addprefix --remove-section=.,$(junk))
|
||||||
|
|
||||||
ifdef UDC_BOOT
|
ifneq ($(UDC_BOOT),)
|
||||||
boot_threads = $(standard_boot_programs) $(udc_boot_programs)
|
boot_threads = $(standard_boot_programs) $(udc_boot_programs)
|
||||||
threadlist = mips/nanonote/threadlist-udc
|
|
||||||
ARCH_CXXFLAGS = -DNUM_THREADS=2
|
ARCH_CXXFLAGS = -DNUM_THREADS=2
|
||||||
|
BOOT_CPPFLAGS = -DUDCBOOT
|
||||||
all: mips/nanonote/nand-boot.raw test
|
all: mips/nanonote/nand-boot.raw test
|
||||||
mips/start.o: TARGET =
|
mips/start.o: TARGET =
|
||||||
else
|
else
|
||||||
|
ifneq ($(SD_BOOT),)
|
||||||
boot_threads = $(standard_boot_programs) $(sd_boot_programs)
|
boot_threads = $(standard_boot_programs) $(sd_boot_programs)
|
||||||
threadlist = mips/nanonote/threadlist-sd
|
|
||||||
ARCH_CXXFLAGS = -DNUM_THREADS=4
|
ARCH_CXXFLAGS = -DNUM_THREADS=4
|
||||||
|
BOOT_CPPFLAGS = -DSDBOOT
|
||||||
all: mips/nanonote/nand-boot.raw iris-sd.tar
|
all: mips/nanonote/nand-boot.raw iris-sd.tar
|
||||||
mips/start.o: TARGET = -DWRAPPED
|
mips/start.o: TARGET = -DWRAPPED
|
||||||
iris-sd.tar: $(addprefix fs/,$(addsuffix .elf,$(programs))) mips/start.raw.gz fs/init.config
|
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'
|
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
|
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
|
endif
|
||||||
|
|
||||||
iris.elf: LDFLAGS = --omagic -Ttext $(load)
|
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: mips/nanonote/sdram-setup.ld
|
||||||
mips/nanonote/sdram-setup.elf: LDFLAGS = --omagic -T 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/boot.o: TARGET_FLAGS = -DMEMORY_SIZE="32 << 20"
|
||||||
mips/init.o: TARGET_FLAGS = -I/usr/include
|
mips/init.o: TARGET_FLAGS = -I/usr/include
|
||||||
source/bootinit.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
|
%.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).
|
# 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 $@
|
$(LD) $(LDFLAGS) $^ -o $@
|
||||||
|
|
||||||
mips/start.elf: mips/start.o
|
mips/start.elf: mips/start.o
|
||||||
|
@ -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 <wijnen@debian.org>
|
|
||||||
//
|
|
||||||
// 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
.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
|
|
@ -19,6 +19,27 @@
|
|||||||
.globl thread_start
|
.globl thread_start
|
||||||
.set noreorder
|
.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
|
.balign 0x1000
|
||||||
thread0:
|
thread0:
|
||||||
.incbin "fs/bootinit.elf"
|
.incbin "fs/bootinit.elf"
|
||||||
@ -46,4 +67,31 @@ thread_start:
|
|||||||
.word thread1
|
.word thread1
|
||||||
.word thread2
|
.word thread2
|
||||||
.word thread3
|
.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
|
135
mips/nanonote/unbrick.ccp
Normal file
135
mips/nanonote/unbrick.ccp
Normal file
@ -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 <wijnen@debian.org>
|
||||||
|
//
|
||||||
|
// 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <usb.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <cstring>
|
||||||
|
#include <shevek/args.hh>
|
||||||
|
#include <shevek/error.hh>
|
||||||
|
|
||||||
|
#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
|
@ -30,7 +30,7 @@ static bool dirty
|
|||||||
static unsigned current_block
|
static unsigned current_block
|
||||||
|
|
||||||
static void sync ():
|
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)
|
erase (current_block << block_bits)
|
||||||
for unsigned p = 0; p < 1 << (block_bits - page_bits); ++p:
|
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)])
|
write ((current_block << block_bits) + (p << page_bits), (char *)&cache[p << (page_bits - 2)])
|
||||||
|
@ -28,20 +28,15 @@ IDLE: after reset or csw.
|
|||||||
IN interrupt: csw received, do nothing.
|
IN interrupt: csw received, do nothing.
|
||||||
OUT interrupt: cbw; handle
|
OUT interrupt: cbw; handle
|
||||||
-> IDLE (no data; csw sent)
|
-> IDLE (no data; csw sent)
|
||||||
-> CSW (data sent in one packet)
|
-> TX (send)
|
||||||
-> TX (more than one packet to send)
|
|
||||||
-> RX (receive packets)
|
-> RX (receive packets)
|
||||||
TX: transmitting data.
|
TX: transmitting data.
|
||||||
IN interrupt: host received data; send more.
|
IN interrupt: host received data; send more.
|
||||||
-> TX (more to send)
|
-> TX (more to send)
|
||||||
-> CSW (last data has now been sent)
|
|
||||||
RX: receiving data.
|
RX: receiving data.
|
||||||
OUT interrupt: host sent data; handle.
|
OUT interrupt: host sent data; handle.
|
||||||
-> RX (more to receive)
|
-> RX (more to receive)
|
||||||
-> IDLE (done receiving; send csw)
|
-> IDLE (done receiving; send csw)
|
||||||
CSW: waiting to transmit csw.
|
|
||||||
IN interrupt: TX is done; send csw
|
|
||||||
-> IDLE
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern "C":
|
extern "C":
|
||||||
@ -218,17 +213,27 @@ class Udc:
|
|||||||
char configuration
|
char configuration
|
||||||
unsigned get_descriptor (unsigned type, unsigned idx, unsigned len)
|
unsigned get_descriptor (unsigned type, unsigned idx, unsigned len)
|
||||||
unsigned handle_setup (Setup *s)
|
unsigned handle_setup (Setup *s)
|
||||||
void irq_usb ()
|
void reset ()
|
||||||
void irq_in0 ()
|
void irq_in0 ()
|
||||||
void irq_out ()
|
void handle_rx ()
|
||||||
|
void handle_tx ()
|
||||||
|
void handle_cbw ()
|
||||||
void send_csw ()
|
void send_csw ()
|
||||||
unsigned big_endian (unsigned src)
|
unsigned big_endian (unsigned src)
|
||||||
bool handle_interrupt (bool usb, bool in)
|
bool handle_interrupt (bool usb, bool in)
|
||||||
void stall (unsigned error)
|
void stall (unsigned error)
|
||||||
bool stalling[3]
|
bool stalling
|
||||||
|
enum State:
|
||||||
|
IDLE
|
||||||
|
TX
|
||||||
|
RX
|
||||||
|
SENT_CSW
|
||||||
|
STALL
|
||||||
|
State state
|
||||||
unsigned residue
|
unsigned residue
|
||||||
unsigned status
|
unsigned status
|
||||||
unsigned tag
|
unsigned tag
|
||||||
|
unsigned data_done, lba, blocks
|
||||||
unsigned block_bits
|
unsigned block_bits
|
||||||
Iris::WBlock block
|
Iris::WBlock block
|
||||||
Iris::Page buffer_page
|
Iris::Page buffer_page
|
||||||
@ -248,6 +253,35 @@ Udc::String <6> Udc::s_manufacturer
|
|||||||
Udc::String <16> Udc::s_product
|
Udc::String <16> Udc::s_product
|
||||||
Udc::String <12> Udc::s_serial
|
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):
|
void Udc::init (Iris::WBlock b):
|
||||||
block = b
|
block = b
|
||||||
block_bits = block.get_align_bits ()
|
block_bits = block.get_align_bits ()
|
||||||
@ -272,37 +306,12 @@ void Udc::init (Iris::WBlock b):
|
|||||||
cpm_start_udc ()
|
cpm_start_udc ()
|
||||||
// Disconnect from the bus and don't try to get high-speed.
|
// Disconnect from the bus and don't try to get high-speed.
|
||||||
UDC_POWER = 0
|
UDC_POWER = 0
|
||||||
UDC_TESTMODE = 0
|
reset ()
|
||||||
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
|
|
||||||
// Wait a while.
|
// Wait a while.
|
||||||
Iris::sleep (HZ / 10)
|
Iris::sleep (HZ / 10)
|
||||||
// Connect to the host.
|
// Connect to the host.
|
||||||
UDC_POWER = UDC_POWER_SOFTCONN
|
UDC_POWER = UDC_POWER_SOFTCONN
|
||||||
|
|
||||||
// Initialize cbw state
|
|
||||||
status = 0
|
|
||||||
residue = 0
|
|
||||||
|
|
||||||
void Udc::send (unsigned ep, char const *data, unsigned length, unsigned maxlength):
|
void Udc::send (unsigned ep, char const *data, unsigned length, unsigned maxlength):
|
||||||
if maxlength < length:
|
if maxlength < length:
|
||||||
length = maxlength
|
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):
|
void Udc::send_padded (char const *data, unsigned length, unsigned maxlength):
|
||||||
UDC_INDEX = 2
|
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
|
unsigned len = length < maxlength ? length : maxlength
|
||||||
residue = maxlength - len
|
residue = maxlength - len
|
||||||
len = (len + 3) & ~3
|
len = (len + 3) & ~3
|
||||||
@ -328,48 +335,14 @@ void Udc::send_padded (char const *data, unsigned length, unsigned maxlength):
|
|||||||
while len + 3 < maxlength:
|
while len + 3 < maxlength:
|
||||||
UDC_FIFO (2) = 0
|
UDC_FIFO (2) = 0
|
||||||
len += 4
|
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 ('-')
|
//kdebug_char ('-')
|
||||||
if len % max_packet_size_bulk != 0 || len < maxlength:
|
while len < maxlength:
|
||||||
while len < maxlength:
|
UDC_FIFO8 (2) = 0
|
||||||
UDC_FIFO8 (2) = 0
|
++len
|
||||||
++len
|
//kdebug_char ('.')
|
||||||
//kdebug_char ('.')
|
UDC_INCSR |= UDC_INCSR_INPKTRDY
|
||||||
UDC_INCSR |= UDC_INCSR_INPKTRDY
|
blocks = 0
|
||||||
while true:
|
state = TX
|
||||||
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")
|
|
||||||
|
|
||||||
unsigned Udc::get_descriptor (unsigned type, unsigned idx, unsigned len):
|
unsigned Udc::get_descriptor (unsigned type, unsigned idx, unsigned len):
|
||||||
switch type:
|
switch type:
|
||||||
@ -424,18 +397,18 @@ unsigned Udc::handle_setup (Setup *s):
|
|||||||
switch s->request:
|
switch s->request:
|
||||||
case SET_ADDRESS:
|
case SET_ADDRESS:
|
||||||
UDC_FADDR = s->value
|
UDC_FADDR = s->value
|
||||||
//Iris::debug ("set address %x\n", s->value)
|
Iris::debug ("set address %x\n", s->value)
|
||||||
return 0
|
return 0
|
||||||
case SET_CONFIGURATION:
|
case SET_CONFIGURATION:
|
||||||
if s->value >= 2:
|
if s->value >= 2:
|
||||||
return ~0
|
return ~0
|
||||||
configuration = s->value
|
configuration = s->value
|
||||||
//Iris::debug ("set configuration %x\n", s->value)
|
Iris::debug ("set configuration %x\n", s->value)
|
||||||
return 0
|
return 0
|
||||||
case SET_INTERFACE:
|
case SET_INTERFACE:
|
||||||
if s->value != 0:
|
if s->value != 0:
|
||||||
return ~0
|
return ~0
|
||||||
//Iris::debug ("set interface %x\n", s->value)
|
Iris::debug ("set interface %x\n", s->value)
|
||||||
return 0
|
return 0
|
||||||
default:
|
default:
|
||||||
return ~0
|
return ~0
|
||||||
@ -444,17 +417,17 @@ unsigned Udc::handle_setup (Setup *s):
|
|||||||
UDC_CSR0 = UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY
|
UDC_CSR0 = UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY
|
||||||
switch s->request:
|
switch s->request:
|
||||||
case GET_STATUS:
|
case GET_STATUS:
|
||||||
//Iris::debug ("get status\t")
|
Iris::debug ("get status\t")
|
||||||
send (0, "\0\0", 2, s->length)
|
send (0, "\0\0", 2, s->length)
|
||||||
return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND
|
return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND
|
||||||
case GET_DESCRIPTOR:
|
case GET_DESCRIPTOR:
|
||||||
return get_descriptor ((s->value >> 8) & 0xff, s->value & 0xff, s->length)
|
return get_descriptor ((s->value >> 8) & 0xff, s->value & 0xff, s->length)
|
||||||
case GET_CONFIGURATION:
|
case GET_CONFIGURATION:
|
||||||
//Iris::debug ("get configuration\t")
|
Iris::debug ("get configuration\t")
|
||||||
send (0, &configuration, 1, s->length)
|
send (0, &configuration, 1, s->length)
|
||||||
return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND
|
return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND
|
||||||
case GET_INTERFACE:
|
case GET_INTERFACE:
|
||||||
//Iris::debug ("get interface\t")
|
Iris::debug ("get interface\t")
|
||||||
send (0, "\0", 1, s->length)
|
send (0, "\0", 1, s->length)
|
||||||
return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND
|
return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND
|
||||||
default:
|
default:
|
||||||
@ -466,16 +439,16 @@ unsigned Udc::handle_setup (Setup *s):
|
|||||||
case ENDPOINT_HALT:
|
case ENDPOINT_HALT:
|
||||||
switch s->index:
|
switch s->index:
|
||||||
case 0x82:
|
case 0x82:
|
||||||
Iris::debug ("in ep halt reset\n")
|
//Iris::debug ("in ep halt reset\n")
|
||||||
UDC_INDEX = 2
|
UDC_INDEX = 2
|
||||||
UDC_INCSR &= ~UDC_INCSR_SENDSTALL
|
UDC_INCSR = (UDC_INCSR & ~UDC_INCSR_SENDSTALL) | UDC_INCSR_CDT
|
||||||
stalling[2] = false
|
stalling = false
|
||||||
|
send_csw ()
|
||||||
break
|
break
|
||||||
case 1:
|
case 1:
|
||||||
Iris::debug ("out ep halt reset\n")
|
//Iris::panic (0, "halt reset on out endpoint")
|
||||||
UDC_INDEX = 1
|
UDC_INDEX = 1
|
||||||
UDC_OUTCSR &= ~UDC_OUTCSR_SENDSTALL
|
UDC_OUTCSR |= UDC_OUTCSR_CDT
|
||||||
stalling[1] = false
|
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
return ~0
|
return ~0
|
||||||
@ -499,7 +472,8 @@ unsigned Udc::handle_setup (Setup *s):
|
|||||||
UDC_CSR0 = UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY
|
UDC_CSR0 = UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY
|
||||||
switch s->request:
|
switch s->request:
|
||||||
case BULK_ONLY_RESET:
|
case BULK_ONLY_RESET:
|
||||||
//Iris::debug ("bulk reset\n")
|
Iris::debug ("bulk reset\n")
|
||||||
|
state = IDLE
|
||||||
return 0
|
return 0
|
||||||
default:
|
default:
|
||||||
return ~0
|
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)
|
Iris::debug ("request: %x %x %x %x %x\n", s->request_type, s->request, s->index, s->length, s->value)
|
||||||
return ~0
|
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 ():
|
void Udc::irq_in0 ():
|
||||||
// Interrupt on endpoint 0.
|
// Interrupt on endpoint 0.
|
||||||
UDC_INDEX = 0
|
UDC_INDEX = 0
|
||||||
unsigned csr = UDC_CSR0
|
unsigned csr = UDC_CSR0
|
||||||
if csr & UDC_CSR0_SENTSTALL:
|
if csr & UDC_CSR0_SENTSTALL:
|
||||||
UDC_CSR0 = 0
|
UDC_CSR0 = 0
|
||||||
//Iris::debug ("stall done\t")
|
//Iris::debug ("stall 0 done\t")
|
||||||
if csr & UDC_CSR0_SETUPEND:
|
if csr & UDC_CSR0_SETUPEND:
|
||||||
UDC_CSR0 = UDC_CSR0_SVDSETUPEND
|
UDC_CSR0 = UDC_CSR0_SVDSETUPEND
|
||||||
//Iris::debug ("setup aborted\t")
|
Iris::debug ("setup aborted\t")
|
||||||
if !(csr & UDC_CSR0_OUTPKTRDY):
|
if !(csr & UDC_CSR0_OUTPKTRDY):
|
||||||
//Iris::debug ("no packet 0: %x\n", csr)
|
//Iris::debug ("no packet 0: %x\n", csr)
|
||||||
return
|
return
|
||||||
UDC_INDEX = 1
|
|
||||||
UDC_OUTCSR |= UDC_OUTCSR_CDT
|
|
||||||
UDC_INDEX = 2
|
|
||||||
UDC_INCSR |= UDC_INCSR_CDT
|
|
||||||
UDC_INDEX = 0
|
UDC_INDEX = 0
|
||||||
union { unsigned d[2]; Setup s; } packet
|
union { unsigned d[2]; Setup s; } packet
|
||||||
packet.d[0] = UDC_FIFO (0)
|
packet.d[0] = UDC_FIFO (0)
|
||||||
packet.d[1] = UDC_FIFO (0)
|
packet.d[1] = UDC_FIFO (0)
|
||||||
if !(packet.s.request_type & 0x80) && packet.s.length > 0:
|
if !(packet.s.request_type & 0x80) && packet.s.length > 0:
|
||||||
// More data will follow; unsupported.
|
// 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
|
UDC_CSR0 = UDC_CSR0_SENDSTALL
|
||||||
return
|
return
|
||||||
unsigned ret = handle_setup (&packet.s)
|
unsigned ret = handle_setup (&packet.s)
|
||||||
UDC_INDEX = 0
|
UDC_INDEX = 0
|
||||||
if ret == ~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
|
UDC_CSR0 = UDC_CSR0_SENDSTALL
|
||||||
return
|
return
|
||||||
if ret:
|
if ret:
|
||||||
@ -566,79 +520,73 @@ void Udc::send_csw ():
|
|||||||
UDC_FIFO (2) = residue
|
UDC_FIFO (2) = residue
|
||||||
UDC_FIFO8 (2) = status
|
UDC_FIFO8 (2) = status
|
||||||
UDC_INCSR |= UDC_INCSR_INPKTRDY
|
UDC_INCSR |= UDC_INCSR_INPKTRDY
|
||||||
|
state = SENT_CSW
|
||||||
status = 0
|
status = 0
|
||||||
residue = 0
|
residue = 0
|
||||||
while true:
|
//kdebug ("sent csw\n")
|
||||||
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")
|
|
||||||
|
|
||||||
void Udc::stall (unsigned error):
|
void Udc::stall (unsigned error):
|
||||||
unsigned index = UDC_INDEX
|
if stalling:
|
||||||
if stalling[index]:
|
|
||||||
Iris::debug ("already stalling!\n")
|
Iris::debug ("already stalling!\n")
|
||||||
if index == 1:
|
UDC_INCSR |= UDC_INCSR_SENDSTALL
|
||||||
UDC_OUTCSR |= UDC_OUTCSR_SENDSTALL
|
stalling = true
|
||||||
else:
|
state = STALL
|
||||||
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 ()
|
|
||||||
|
|
||||||
unsigned Udc::big_endian (unsigned src):
|
unsigned Udc::big_endian (unsigned src):
|
||||||
return src >> 24 | src >> 8 & 0xff00 | src << 8 & 0xff0000 | src << 24
|
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
|
UDC_INDEX = 1
|
||||||
unsigned csr = UDC_OUTCSR
|
unsigned csr = UDC_OUTCSR
|
||||||
unsigned size = UDC_OUTCOUNT
|
unsigned size = UDC_OUTCOUNT
|
||||||
if !(csr & UDC_OUTCSR_OUTPKTRDY):
|
|
||||||
// No packet, just a notification.
|
|
||||||
kdebug ("no packet\n")
|
|
||||||
return
|
|
||||||
if csr & UDC_OUTCSR_SENDSTALL:
|
if csr & UDC_OUTCSR_SENDSTALL:
|
||||||
// When stalling, do nothing else.
|
// When stalling, do nothing else.
|
||||||
//kdebug ("not responding to out during stall\n")
|
//kdebug ("not responding to out during stall\n")
|
||||||
UDC_OUTCSR = csr & ~UDC_OUTCSR_SENTSTALL
|
UDC_OUTCSR = csr & ~UDC_OUTCSR_SENTSTALL
|
||||||
return
|
return
|
||||||
|
if !(csr & UDC_OUTCSR_OUTPKTRDY):
|
||||||
|
// No packet; this shouldn't happen.
|
||||||
|
Iris::panic (0, "no packet")
|
||||||
|
return
|
||||||
// expect a new cbw.
|
// expect a new cbw.
|
||||||
if size != 31:
|
if size != 31:
|
||||||
Iris::debug ("count %d != 31\n", size)
|
Iris::debug ("count %d != 31\n", size)
|
||||||
@ -656,10 +604,17 @@ void Udc::irq_out ():
|
|||||||
UDC_OUTCSR = csr & ~UDC_OUTCSR_OUTPKTRDY
|
UDC_OUTCSR = csr & ~UDC_OUTCSR_OUTPKTRDY
|
||||||
tag = cbw.cbw.tag
|
tag = cbw.cbw.tag
|
||||||
if cbw.cbw.sig != 0x43425355 || cbw.cbw.lun != 0 || cbw.cbw.size == 0 || cbw.cbw.size > 16:
|
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)
|
stall (2)
|
||||||
return
|
return
|
||||||
//kdebug ("bulk cbw\t")
|
//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
|
UDC_INDEX = 2
|
||||||
bool to_host = cbw.cbw.flags & 0x80
|
bool to_host = cbw.cbw.flags & 0x80
|
||||||
switch cbw.cbw.data[0]:
|
switch cbw.cbw.data[0]:
|
||||||
@ -673,7 +628,6 @@ void Udc::irq_out ():
|
|||||||
case CBW::REQUEST_SENSE:
|
case CBW::REQUEST_SENSE:
|
||||||
//Iris::debug ("sense requested\n")
|
//Iris::debug ("sense requested\n")
|
||||||
send_padded ("\xf0\x00\x05\x00\x00\x00\x00\x00", 8, cbw.cbw.length)
|
send_padded ("\xf0\x00\x05\x00\x00\x00\x00\x00", 8, cbw.cbw.length)
|
||||||
send_csw ()
|
|
||||||
break
|
break
|
||||||
case CBW::FORMAT_UNIT:
|
case CBW::FORMAT_UNIT:
|
||||||
Iris::panic (0, "FORMAT_UNIT isn't implemented")
|
Iris::panic (0, "FORMAT_UNIT isn't implemented")
|
||||||
@ -682,8 +636,8 @@ void Udc::irq_out ():
|
|||||||
stall (2)
|
stall (2)
|
||||||
return
|
return
|
||||||
//Iris::debug ("sending inquiry response\t")
|
//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_padded ("\x00\x00\x04\x02\x1f\x00\x00\x00shevek iris usb stick \x00\x00\x04\x02", 36, cbw.cbw.length)
|
||||||
send_csw ()
|
|
||||||
break
|
break
|
||||||
case CBW::RESERVE6:
|
case CBW::RESERVE6:
|
||||||
Iris::panic (0, "RESERVE6 isn't implemented")
|
Iris::panic (0, "RESERVE6 isn't implemented")
|
||||||
@ -700,118 +654,94 @@ void Udc::irq_out ():
|
|||||||
capacity[1] = big_endian (1 << block_bits)
|
capacity[1] = big_endian (1 << block_bits)
|
||||||
//Iris::debug ("sending capacity: %x * %x\t", capacity[0], capacity[1])
|
//Iris::debug ("sending capacity: %x * %x\t", capacity[0], capacity[1])
|
||||||
send_padded ((char *)capacity, 8, cbw.cbw.length)
|
send_padded ((char *)capacity, 8, cbw.cbw.length)
|
||||||
send_csw ()
|
|
||||||
break
|
break
|
||||||
case CBW::READ10:
|
case CBW::READ10:
|
||||||
unsigned lba = cbw.cbw.data[2] << 24 | cbw.cbw.data[3] << 16 | cbw.cbw.data[4] << 8 | cbw.cbw.data[5]
|
if !to_host:
|
||||||
unsigned blocks = cbw.cbw.data[7] << 8 | cbw.cbw.data[8]
|
stall (2)
|
||||||
for unsigned i = 0; i < blocks; ++i:
|
return
|
||||||
//Iris::debug ("reading block %d\n", lba + i)
|
lba = cbw.cbw.data[2] << 24 | cbw.cbw.data[3] << 16 | cbw.cbw.data[4] << 8 | cbw.cbw.data[5]
|
||||||
// read block lba + i.
|
blocks = cbw.cbw.data[7] << 8 | cbw.cbw.data[8]
|
||||||
buffer_page.set_flags (Iris::Page::FRAME)
|
data_done = 0
|
||||||
block.get_block ((lba + i) << block_bits, 1 << block_bits, 0, buffer_page)
|
state = TX
|
||||||
for unsigned p = 0; p < 1 << block_bits; p += max_packet_size_bulk:
|
handle_tx ()
|
||||||
//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 ()
|
|
||||||
break
|
break
|
||||||
case CBW::WRITE10:
|
case CBW::WRITE10:
|
||||||
unsigned lba = cbw.cbw.data[2] << 24 | cbw.cbw.data[3] << 16 | cbw.cbw.data[4] << 8 | cbw.cbw.data[5]
|
if to_host:
|
||||||
unsigned blocks = cbw.cbw.data[7] << 8 | cbw.cbw.data[8]
|
stall (2)
|
||||||
for unsigned i = 0; i < blocks; ++i:
|
return
|
||||||
//Iris::debug ("writing block %d\n", lba + i)
|
lba = cbw.cbw.data[2] << 24 | cbw.cbw.data[3] << 16 | cbw.cbw.data[4] << 8 | cbw.cbw.data[5]
|
||||||
// write block lba + i.
|
blocks = cbw.cbw.data[7] << 8 | cbw.cbw.data[8]
|
||||||
buffer_page.set_flags (Iris::Page::FRAME)
|
if blocks == 0:
|
||||||
//Iris::debug ("@%x:", (lba + i) << block_bits)
|
send_csw ()
|
||||||
for unsigned p = 0; p < 1 << block_bits; p += max_packet_size_bulk:
|
break
|
||||||
while true:
|
state = RX
|
||||||
Iris::register_interrupt (IRQ_UDC)
|
data_done = 0
|
||||||
Iris::wait_for_interrupt (IRQ_UDC)
|
buffer_page.set_flags (Iris::Page::FRAME)
|
||||||
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 ()
|
|
||||||
break
|
break
|
||||||
case CBW::RESERVE10:
|
case CBW::RESERVE10:
|
||||||
Iris::panic (0, "RESERVE10 isn't implemented")
|
Iris::panic (0, "RESERVE10 isn't implemented")
|
||||||
case CBW::RELEASE10:
|
case CBW::RELEASE10:
|
||||||
Iris::panic (0, "RELEASE10 isn't implemented")
|
Iris::panic (0, "RELEASE10 isn't implemented")
|
||||||
default:
|
default:
|
||||||
Iris::debug ("cbw:")
|
#if 0
|
||||||
|
Iris::debug ("unknown cbw:")
|
||||||
for unsigned i = 0; i < cbw.cbw.size; ++i:
|
for unsigned i = 0; i < cbw.cbw.size; ++i:
|
||||||
kdebug_char (' ')
|
kdebug_char (' ')
|
||||||
kdebug_num (cbw.cbw.data[i], 2)
|
kdebug_num (cbw.cbw.data[i], 2)
|
||||||
Iris::debug ("\n")
|
Iris::debug ("\n")
|
||||||
|
#endif
|
||||||
residue = cbw.cbw.length
|
residue = cbw.cbw.length
|
||||||
stall (1)
|
stall (1)
|
||||||
return
|
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 ():
|
void Udc::interrupt ():
|
||||||
Iris::debug ("interrupt\n")
|
//Iris::debug ("interrupt, state = %d\n", state)
|
||||||
while true:
|
while true:
|
||||||
|
bool action = false
|
||||||
unsigned usb = UDC_INTRUSB
|
unsigned usb = UDC_INTRUSB
|
||||||
unsigned in = UDC_INTRIN
|
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
|
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:
|
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
|
action = true
|
||||||
if !action:
|
if !action:
|
||||||
// No more interrupts to handle; this is normal, because we're looping until this happens.
|
// No more interrupts to handle; this is normal, because we're looping until this happens.
|
||||||
|
Loading…
Reference in New Issue
Block a user