From 61d76aaefbbd7233959f935acdfbaf15cd392075 Mon Sep 17 00:00:00 2001 From: Bas Wijnen Date: Wed, 1 Sep 2010 23:27:14 +0200 Subject: [PATCH] working on mass storage --- devices.hhp | 129 ++++-- init.config | 32 +- mips/nand.hhp | 6 +- mips/nanonote/Makefile.arch | 2 +- mips/nanonote/server/usb-server.ccp | 4 +- source/boot.ccp | 2 +- source/bootinit.ccp | 30 +- source/elfrun.ccp | 6 +- source/fat.ccp | 28 +- source/init.ccp | 14 +- source/nand.ccp | 143 ++++-- source/partition.ccp | 28 +- source/sd+mmc.ccp | 13 +- source/udc.ccp | 5 +- source/usb-mass-storage.ccp | 689 ++++++++++++++++++++++++++++ 15 files changed, 942 insertions(+), 189 deletions(-) create mode 100644 source/usb-mass-storage.ccp diff --git a/devices.hhp b/devices.hhp index 3a82ad7..1081536 100644 --- a/devices.hhp +++ b/devices.hhp @@ -33,19 +33,49 @@ namespace Iris: _T get (unsigned idx): return _T (Caps::get (idx)) - /// A block of data with a size and content. Any character can be stored in it (including '\0'). - struct String : public Cap: - String (Cap c = Cap ()) : Cap (c): + struct _Locker_base: enum request: - GET_SIZE = 0x2001 + LOCK_RO = 0x2001 + UNLOCK_RO + NUM + template // + struct Locker : public _Locker_base, public _T: + // Lock the object for reading. Multiple read locks can exist simultaneously, but no write lock can be present. + void lock_ro (): + _T::call (CAP_MASTER_DIRECT | LOCK_RO) + // Release a read-only lock. + void unlock_ro (): + _T::call (CAP_MASTER_DIRECT | UNLOCK_RO) + Locker (): + Locker (Cap c) : _T (c): + + struct _WLocker_base: + enum request: + LOCK = _Locker_base::NUM + UNLOCK + NUM + template // + struct WLocker : public _WLocker_base, public _T: + // Lock the object. Write operations can only be done when the object is locked. + void lock (): + _T::call (CAP_MASTER_DIRECT | LOCK) + // Unlock the object. Write operations can only be done when the object is locked. + void unlock (): + _T::call (CAP_MASTER_DIRECT | UNLOCK) + WLocker (): + WLocker (Cap c) : _T (c): + + /// A short block of data with a size and content. Any character can be stored in it (including '\0'). + struct _String : public Cap: + _String (Cap c = Cap ()) : Cap (c): + enum request: + GET_SIZE = _WLocker_base::NUM GET_CHARS - GET_ALIGN_BITS - GET_BLOCK ID /// Get the size of the string. Num get_size (): return call (CAP_MASTER_DIRECT | GET_SIZE) - /// Get exactly 16 characters. The index must be aligned to 16 bytes or align_bits, whichever is smaller. + /// Get exactly 16 characters. There is no alignment requirement. char *get_chars (Num idx, char buffer[16]): call (CAP_MASTER_DIRECT | GET_CHARS, idx) unsigned *b = (unsigned *)buffer @@ -54,6 +84,19 @@ namespace Iris: b[2] = recv.data[1].l b[3] = recv.data[1].h return buffer + typedef Locker <_String> String + + /// A block of data with a size and content. Any character can be stored in it (including '\0'). + struct _Block : public Cap: + _Block (Cap c = Cap ()) : Cap (c): + enum request: + GET_SIZE = _String::ID + GET_ALIGN_BITS + GET_BLOCK + ID + /// Get the size of the block. + Num get_size (): + return call (CAP_MASTER_DIRECT | GET_SIZE) /// Get the number of bits that page accesses must be aligned to. Cannot be more than PAGE_BITS. unsigned get_align_bits (): return call (CAP_MASTER_DIRECT | GET_ALIGN_BITS).l @@ -62,34 +105,47 @@ namespace Iris: Page ret = my_memory.create_page () ret.set_flags (Page::PAYING) return ret - /// Get a block from the string; place it at offset on page. This need not be implemented for strings smaller than PAGE_SIZE. All arguments must be aligned. + /// Get a block from the block; place it at offset on page. This need not be implemented for blocks smaller than PAGE_SIZE. All arguments must be aligned. If size is PAGE_SIZE, the caller may lose the frame in the transaction. Cap get_block (Num idx, unsigned size = PAGE_SIZE, unsigned offset = 0, Page ret = _create_paying_page ()): ocall (ret, Iris::Num (CAP_MASTER_DIRECT | GET_BLOCK, size << 16 | offset), idx) return ret + typedef Locker <_Block> Block /// A writable String. - struct WString : public String: - WString (Cap c = Cap ()) : String (c): + struct _WString : public String: + _WString (Cap c = Cap ()) : String (c): enum request: - TRUNCATE = String::ID + TRUNCATE = _Block::ID SET_CHARS - SET_BLOCK ID /// Set the size of the string. Strings may have a limit to this setting. void truncate (Num size): call (CAP_MASTER_DIRECT | TRUNCATE, size) - /// Set exactly 4 characters. The index must be aligned. This need not be supported for strings with align larger than 4 bytes. + /// Set exactly 4 characters. If the size is shorter than idx + 4, the extra characters are ignored. void set_chars (Num idx, char buffer[4]): call (Num (CAP_MASTER_DIRECT | SET_CHARS, *(unsigned *)buffer), idx) - /// Overwrite a block from the string with data at offset on the page. This need not be implemented for strings smaller than PAGE_SIZE. The all arguments must be aligned. The caller may lose the frame in the transaction. Only the specified part of the page is used for overwriting data. + typedef WLocker <_WString> WString + + /// A writable Block. + struct _WBlock : public Block: + _WBlock (Cap c = Cap ()) : Block (c): + enum request: + TRUNCATE = _Block::ID + SET_BLOCK + ID + /// Set the size of the block. This setting may have a limited range, or not be supported at all. + void truncate (Num size): + call (CAP_MASTER_DIRECT | TRUNCATE, size) + /// Overwrite a block from the block with data at offset on the page. All arguments must be aligned. If size is PAGE_SIZE, the caller may lose the frame in the transaction. Only the specified part of the page is used for overwriting data. void set_block (Num idx, Page page, unsigned size = PAGE_SIZE, unsigned offset = 0): ocall (page, Iris::Num (CAP_MASTER_DIRECT | SET_BLOCK, size << 16 | offset), idx) + typedef WLocker <_WBlock> WBlock // This interface allows another kernel to be booted by Iris. struct Boot : public Cap: Boot (Cap c = Cap ()) : Cap (c): enum request: - BOOT = WString::ID + BOOT = _WBlock::ID ID // Boot a new kernel. void boot (String code, unsigned load, unsigned entry): @@ -117,19 +173,19 @@ namespace Iris: struct Elfrun : public Cap: Elfrun (Cap c = Cap ()) : Cap (c): enum request: - RUN_STRING = Event::ID + RUN_BLOCK = Event::ID RUN_CAPS ID enum arg_pos: PARENT_MEMORY DATA PARENT - Caps run_string (Memory parent_memory, String data, Cap parent, unsigned num_slots = 8, unsigned num_caps = 32): + Caps run_block (Memory parent_memory, Block data, Cap parent, unsigned num_slots = 8, unsigned num_caps = 32): Caps caps = my_memory.create_caps (3) caps.set (PARENT_MEMORY, parent_memory) caps.set (DATA, data) caps.set (PARENT, parent) - iocall (caps.copy (), CAP_MASTER_DIRECT | RUN_STRING, Num (num_slots, num_caps)) + iocall (caps.copy (), CAP_MASTER_DIRECT | RUN_BLOCK, Num (num_slots, num_caps)) Caps ret = get_arg () my_memory.destroy (caps) free_cap (caps) @@ -257,15 +313,13 @@ namespace Iris: // Seekable is not a class, it is identical to [W]String. // Directory interface. - struct Directory : public Cap: - Directory (Cap c = Cap ()) : Cap (c): + struct _Directory : public Cap: + _Directory (Cap c = Cap ()) : Cap (c): enum request: GET_SIZE = Setting::ID GET_NAME GET_FILE_RO GET_FILE_INFO - LOCK_RO - UNLOCK_RO ID // Get the number of entries in this directory. Num get_size (): @@ -281,20 +335,14 @@ namespace Iris: // Get file info. TODO: define possible types. Num get_file_info (Num idx, unsigned type): return icall (Num (CAP_MASTER_DIRECT | GET_FILE_INFO, type), idx) - // Lock the directory for reading. Multiple read locks can exist simultaneously, but no write lock can be present. - void lock_ro (): - call (CAP_MASTER_DIRECT | LOCK_RO) - // Release a read-only lock. Write operations can only be done when the directory is locked. - void unlock_ro (): - call (CAP_MASTER_DIRECT | UNLOCK_RO) - struct WDirectory : public Directory: - WDirectory (Cap c = Cap ()) : Directory (c): + typedef Locker <_Directory> Directory + + struct _WDirectory : public Directory: + _WDirectory (Cap c = Cap ()) : Directory (c): enum request: - GET_FILE = Directory::ID + GET_FILE = _Directory::ID CREATE_FILE DELETE_FILE - LOCK - UNLOCK ID // Get the file. Cap get_file (Num idx): @@ -307,18 +355,13 @@ namespace Iris: // Delete a file. After this, any index may map to a different file. void delete_file (Num idx): call (CAP_MASTER_DIRECT | DELETE_FILE, idx) - // Lock the directory. Write operations can only be done when the directory is locked. - void lock (): - call (CAP_MASTER_DIRECT | LOCK) - // Unlock the directory. Write operations can only be done when the directory is locked. - void unlock (): - call (CAP_MASTER_DIRECT | UNLOCK) + typedef WLocker <_WDirectory> WDirectory // Stream interface. struct Stream : public Cap: Stream (Cap c = Cap ()) : Cap (c): enum request: - READ = Directory::ID + READ = _WDirectory::ID WRITE ID // Try to read size bytes. Returns the number of bytes successfully read. @@ -344,12 +387,6 @@ namespace Iris: void exit (): call (CAP_MASTER_DIRECT | EXIT) - // Block device interface. - struct Block_device : public WString: - Block_device (Cap c = Cap ()) : WString (c): - // TODO: to be designed. - - // TODO. // Sound interface. // Usb interfaces (port, device). diff --git a/init.config b/init.config index d7e6a5a..9b0528c 100644 --- a/init.config +++ b/init.config @@ -13,7 +13,11 @@ receive driver_gpio / Event = sdmmc_gpio sysreq sysreq - #driver nand = "nand.elf" + driver driver_nand = "nand.elf" + receive driver_nand / WBlock = nand + + driver driver_ums = "usb-mass-storage.elf" + give driver_ums / WBlock = nand #driver driver_boot = "boot.elf" #receive driver_boot / Boot = boot @@ -23,22 +27,22 @@ #give booter / String = kernel #give booter / Boot = boot - driver driver_lcd = "lcd.elf" - receive driver_lcd / Display = display - receive driver_lcd / Setting = display_bright + #driver driver_lcd = "lcd.elf" + #receive driver_lcd / Display = display + #receive driver_lcd / Setting = display_bright - driver driver_buzzer = "buzzer.elf" - receive driver_buzzer / Buzzer = buzzer + #driver driver_buzzer = "buzzer.elf" + #receive driver_buzzer / Buzzer = buzzer - program alarm = "alarm.elf" - receive alarm / UI = ui + #program alarm = "alarm.elf" + #receive alarm / UI = ui - program gui = "gui.elf" - give gui / UI = ui - give gui / Display = display - give gui / Setting = display_bright - give gui / Buzzer = buzzer - give gui / Keyboard = keyboard + #program gui = "gui.elf" + #give gui / UI = ui + #give gui / Display = display + #give gui / Setting = display_bright + #give gui / Buzzer = buzzer + #give gui / Keyboard = keyboard #driver sdmmc = "sd+mmc.elf" #receive sdmmc / WString = sdmmc diff --git a/mips/nand.hhp b/mips/nand.hhp index 35f1ff6..84c4542 100644 --- a/mips/nand.hhp +++ b/mips/nand.hhp @@ -53,6 +53,7 @@ static volatile char *data static unsigned page_bits static unsigned redundant_bits static unsigned block_bits +static unsigned size_bits static unsigned word_size static void unbusy (): @@ -107,8 +108,9 @@ static void reset (): debug ("word size: %d\n", word_size) //unsigned serial_access_minimum = (d & 0x80 ? 25 : 50) d = rdata () - //unsigned num_planes = 1 << ((d >> 2) & 3) - //unsigned plane_bits = 26 + ((d >> 4) & 7) + unsigned num_planes_bits = d >> 2 & 3 + unsigned plane_bits = 26 + (d >> 4 & 7) + size_bits = plane_bits + num_planes_bits - 3 static bool read (unsigned a, char *buffer): unsigned column = a & ((1 << page_bits) - 1) diff --git a/mips/nanonote/Makefile.arch b/mips/nanonote/Makefile.arch index 3baccea..dba5af0 100644 --- a/mips/nanonote/Makefile.arch +++ b/mips/nanonote/Makefile.arch @@ -26,7 +26,7 @@ udc_boot_programs = udc sd_boot_programs = sd+mmc partition fat standard_boot_programs = bootinit -programs = init 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 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) ARCH_CPPFLAGS = -I. -Imips -Imips/nanonote -Wa,-mips32 -DNANONOTE -DUSE_SERIAL CROSS = mipsel-linux-gnu- diff --git a/mips/nanonote/server/usb-server.ccp b/mips/nanonote/server/usb-server.ccp index f045307..c04a793 100644 --- a/mips/nanonote/server/usb-server.ccp +++ b/mips/nanonote/server/usb-server.ccp @@ -155,7 +155,7 @@ void data::poll (): if !--lock: dir.clear () continue - case Iris::String::GET_BLOCK: + case Iris::Block::GET_BLOCK: if buffer[1] >= dir.size (): std::cerr << "reading invalid file" << std::endl usb_release_interface (handle, 0) @@ -177,7 +177,7 @@ void data::poll (): handle = NULL return continue - case Iris::String::GET_SIZE: + case Iris::Block::GET_SIZE: if buffer[1] >= dir.size (): std::cerr << "reading invalid file size " << buffer[1] << " >= " << dir.size () << std::endl usb_release_interface (handle, 0) diff --git a/source/boot.ccp b/source/boot.ccp index 2fdbdcb..483f35f 100644 --- a/source/boot.ccp +++ b/source/boot.ccp @@ -41,7 +41,7 @@ Iris::Num start (): Iris::wait () switch Iris::recv.data[0].l: case Iris::Boot::BOOT: - Iris::String code = Iris::get_arg () + Iris::Block code = Iris::get_arg () unsigned load = Iris::recv.data[1].l unsigned entry = Iris::recv.data[1].h Iris::Num lsize = code.get_size () diff --git a/source/bootinit.ccp b/source/bootinit.ccp index a087c24..6805d25 100644 --- a/source/bootinit.ccp +++ b/source/bootinit.ccp @@ -65,7 +65,7 @@ static Iris::Page bss_page // Get the initial block device and filesystem. static Iris::Directory receive_devices (): - Iris::String device + Iris::Block device bool have_device = false Iris::Cap reply[2] bool have_reply[2] @@ -85,8 +85,8 @@ static Iris::Directory receive_devices (): switch Iris::recv.data[0].l: case Iris::Parent::PROVIDE_CAPABILITY: switch Iris::recv.data[1].l: - case Iris::String::ID: - case Iris::WString::ID: + case Iris::Block::ID: + case Iris::WBlock::ID: // Ignore other partitions. Iris::Cap r = Iris::get_reply () if Iris::recv.data[0].h != 0: @@ -96,11 +96,11 @@ static Iris::Directory receive_devices (): Iris::panic (0, "double device provided") device = Iris::get_arg () if have_reply[next - 2]: - kdebug ("string provided (used)\n") + kdebug ("block provided (used)\n") reply[next++ - 2].invoke (0, 0, device.copy ()) Iris::free_cap (device) else: - kdebug ("string provided (stored)\n") + kdebug ("block provided (stored)\n") have_device = true r.invoke () Iris::free_cap (r) @@ -123,16 +123,16 @@ static Iris::Directory receive_devices (): Iris::free_cap (event) Iris::free_cap (reply) break - if Iris::recv.data[1].l != Iris::String::ID && Iris::recv.data[1].l != Iris::WString::ID: + if Iris::recv.data[1].l != Iris::Block::ID && Iris::recv.data[1].l != Iris::WBlock::ID: Iris::panic (Iris::recv.data[1].l, "invalid capability type requested by boot thread") if next == Iris::recv.protected_data.l && have_device: - kdebug ("string requested (sent)\n") + kdebug ("block requested (sent)\n") Iris::recv.reply.invoke (0, 0, device.copy ()) Iris::free_cap (device) have_device = false ++next else: - kdebug ("string requested (not sent)\n") + kdebug ("block requested (not sent)\n") reply[Iris::recv.protected_data.l - 2] = Iris::get_reply () have_reply[Iris::recv.protected_data.l - 2] = true break @@ -152,7 +152,7 @@ static bool stringcmp (char const *s1, char const *s2, unsigned size): return false return true -static Iris::String find (Iris::Directory root, char const *name): +static Iris::Block find (Iris::Directory root, char const *name): unsigned size = 0 while name[size]: ++size @@ -165,11 +165,11 @@ static Iris::String find (Iris::Directory root, char const *name): if !stringcmp (current_name, name, size): continue // Found elfrun. - Iris::String ret = root.get_file_ro (i) + Iris::Block ret = root.get_file_ro (i) return ret Iris::panic (0, "bootfile not found") -static void run (Iris::String data, Iris::Memory parent_memory, Iris::Cap parent): +static void run (Iris::Block data, Iris::Memory parent_memory, Iris::Cap parent): // Get the size. Iris::Num size = data.get_size () if size.value () == 0: @@ -320,9 +320,9 @@ Iris::Num start (): Iris::Memory top_memory = Iris::get_top_memory () Iris::Directory root = receive_devices () root.lock_ro () - Iris::String run_string = find (root, ELFRUN_NAME) + Iris::Block run_block = find (root, ELFRUN_NAME) Iris::Cap parent_cap = Iris::my_receiver.create_capability (0) - run (run_string, top_memory, parent_cap) + run (run_block, top_memory, parent_cap) Iris::wait () if Iris::recv.data[0].l != Iris::Parent::PROVIDE_CAPABILITY || Iris::recv.data[1].l != Iris::Elfrun::ID: Iris::panic (0, "elfrun doesn't provide correct capability") @@ -334,8 +334,8 @@ Iris::Num start (): Iris::free_cap (reply) parent_cap = Iris::my_receiver.create_capability (0) - Iris::String init_string = find (root, INIT_NAME) - Iris::Caps init_caps = elfrun.run_string (top_memory.copy (), init_string.copy (), parent_cap.copy (), 8, 63) + Iris::Block init_block = find (root, INIT_NAME) + Iris::Caps init_caps = elfrun.run_block (top_memory.copy (), init_block.copy (), parent_cap.copy (), 8, 63) Iris::Thread init = init_caps.get (__thread_num) init.make_priv () diff --git a/source/elfrun.ccp b/source/elfrun.ccp index 7378b9b..31516e6 100644 --- a/source/elfrun.ccp +++ b/source/elfrun.ccp @@ -63,7 +63,7 @@ static Iris::Memory mem static unsigned *bss_mapping static Iris::Page bss_page -static Iris::Caps map_string (Iris::String data): +static Iris::Caps map_string (Iris::Block data): // Get the size. Iris::Num size = data.get_size () if size.value () == 0: @@ -267,12 +267,12 @@ Iris::Num start (): Iris::Cap reply = Iris::get_reply () Iris::Cap arg = Iris::get_arg () switch Iris::recv.data[0].l: - case Iris::Elfrun::RUN_STRING: + case Iris::Elfrun::RUN_BLOCK: unsigned num_slots = Iris::recv.data[1].l unsigned num_caps = Iris::recv.data[1].h parent_memory = Iris::Caps (arg).get (Iris::Elfrun::PARENT_MEMORY) parent = Iris::Caps (arg).get (Iris::Elfrun::PARENT) - Iris::String data = Iris::Caps (arg).get (Iris::Elfrun::DATA) + Iris::Block data = Iris::Caps (arg).get (Iris::Elfrun::DATA) map_string (data) Iris::Caps ret = run (data, parent_memory, parent, num_slots, num_caps) reply.invoke (0, 0, ret.copy ()) diff --git a/source/fat.ccp b/source/fat.ccp index c071e34..2e917f9 100644 --- a/source/fat.ccp +++ b/source/fat.ccp @@ -39,7 +39,7 @@ void *operator new[] (unsigned size): void *operator new (unsigned size): return new char[size] -static Iris::WString dev +static Iris::WBlock dev static Iris::Num device_size static Iris::Page page static char *data @@ -491,7 +491,7 @@ struct Fat: Iris::Num start (): init_alloc () current_block = ~0 - dev = Iris::my_parent.get_capability () + dev = Iris::my_parent.get_capability () if dev.get_align_bits () > SECTOR_BITS: kdebug ("fat device doesn't support 512 byte access") return 1 @@ -574,11 +574,6 @@ Iris::Num start (): //kdebug ("\n") reply.invoke (Iris::Num (u.u[0], u.u[1]), Iris::Num (u.u[2], u.u[3])) break - case Iris::String::GET_ALIGN_BITS: - //kdebug ("filename align requested\n") - reply.invoke (0) - break - case Iris::String::GET_BLOCK: default: Iris::panic (Iris::recv.data[0].l, "invalid request for fat filename") Iris::free_cap (reply) @@ -594,23 +589,15 @@ Iris::Num start (): Fat::File f fat.get_dir_entry (dir, idx, &f) switch cmd: - case Iris::String::GET_SIZE: + case Iris::Block::GET_SIZE: //kdebug ("file size requested\n") reply.invoke (f.size) break - case Iris::String::GET_CHARS: - //kdebug ("file chars requested\n") - unsigned mask = (1 << fat.cluster_size_bits) - 1 - f.load_cluster (num.l & ~mask, num.l & mask) - unsigned n = num.l & mask & ~0xf - unsigned *dat = (unsigned *)(data + n) - reply.invoke (Iris::Num (dat[0], dat[1]), Iris::Num (dat[2], dat[3])) - break - case Iris::String::GET_ALIGN_BITS: + case Iris::Block::GET_ALIGN_BITS: //kdebug ("file align requested\n") reply.invoke (fat.cluster_size_bits <= PAGE_BITS ? fat.cluster_size_bits : PAGE_BITS) break - case Iris::String::GET_BLOCK: + case Iris::Block::GET_BLOCK: //kdebug ("file block requested\n") unsigned mask = (1 << fat.cluster_size_bits) - 1 //kdebug ("mask = ") @@ -626,9 +613,8 @@ Iris::Num start (): f.load_cluster ((num.l & ~mask) + i, num.l & mask, arg, i + offset) reply.invoke () break - case Iris::WString::TRUNCATE: - case Iris::WString::SET_CHARS: - case Iris::WString::SET_BLOCK: + case Iris::WBlock::TRUNCATE: + case Iris::WBlock::SET_BLOCK: Iris::panic (Iris::recv.data[0].l, "writing to files not supported yet") default: Iris::panic (Iris::recv.data[0].l, "invalid request for fat file") diff --git a/source/init.ccp b/source/init.ccp index 52abda1..0505eb6 100644 --- a/source/init.ccp +++ b/source/init.ccp @@ -185,7 +185,7 @@ static Iris::Caps load (char const *name, unsigned name_len, unsigned &size, Iri Iris::free_cap (n) continue Iris::free_cap (n) - Iris::String file = root.get_file_ro (i) + Iris::Block file = root.get_file_ro (i) Iris::Num s = file.get_size () if s.h: Iris::panic (s.h, "file is too large to load") @@ -286,6 +286,8 @@ static unsigned read_num (char *&line, unsigned &len): static Type types[] = { { "String", 6, Iris::String::ID }, { "WString", 7, Iris::WString::ID }, + { "Block", 5, Iris::Block::ID }, + { "WBlock", 6, Iris::WBlock::ID }, { "Boot", 4, Iris::Boot::ID }, { "Device", 6, Iris::Device::ID }, { "Event", 5, Iris::Event::ID }, @@ -405,7 +407,7 @@ static void parse_line (char *&line, unsigned maxlen): else if match (start, maxlen, "file"): // file = "" File *f = &**files.insert () - f->type = Iris::String::ID + f->type = Iris::Block::ID if !get_name (start, maxlen, f->name, f->name_len) || !match (start, maxlen, "=") || !maxlen: Iris::panic (0, "syntax error in init.config (file name)") unsigned l @@ -519,15 +521,13 @@ Iris::Num start (): case FILE: File *file = (File *)Iris::recv.protected_data.h switch Iris::recv.data[0].l: - case Iris::String::GET_SIZE: + case Iris::Block::GET_SIZE: Iris::recv.reply.invoke (file->size) break - case Iris::String::GET_CHARS: - Iris::panic (0, "get_chars is not defined for init strings") - case Iris::String::GET_ALIGN_BITS: + case Iris::Block::GET_ALIGN_BITS: Iris::recv.reply.invoke (PAGE_BITS) break - case Iris::String::GET_BLOCK: + case Iris::Block::GET_BLOCK: Iris::Cap reply = Iris::get_reply () Iris::Page target = Iris::get_arg () Iris::Page source = file->pages.get (Iris::recv.data[1].l >> PAGE_BITS) diff --git a/source/nand.ccp b/source/nand.ccp index e2c6d7f..df9bb82 100644 --- a/source/nand.ccp +++ b/source/nand.ccp @@ -16,21 +16,31 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#define DELAY Iris::schedule - #include "devices.hh" #define ARCH #include "arch.hh" + #define debug Iris::debug +#define DELAY Iris::schedule #include "nand.hh" -extern "C": - extern char file_start, file_mid, file_end +static unsigned *cache +static bool dirty +static unsigned current_block + +static void sync (): + debug ("erasing %x\n", current_block << block_bits) + //erase (current_block << block_bits) + for unsigned p = 0; p < 1 << (block_bits - 9); ++p: + debug ("writing %x\n", (current_block << block_bits) + (p << 9)) + //write ((current_block << block_bits) + (p << 9), &cache[p << (9 - 2)]) + +static void read_block (unsigned b): + for unsigned p = 0; p < 1 << (block_bits - 9); ++p: + read ((b << block_bits) + (p << 9), (char *)&cache[p << (9 - 2)]) + dirty = false Iris::Num start (): - //kdebug ("starting nand operation in 10 seconds\n") - //Iris::sleep (10 * HZ) - map_emc () map_gpio () @@ -58,43 +68,86 @@ Iris::Num start (): reset () - #if 1 - erase (0) - char *source = &file_start - unsigned a = 0 - while source < &file_mid: - write (a, source) - a += 0x800 - source += 0x800 - source = &file_mid - a = 0x4000 - while source < &file_end: - write (a, source) - a += 0x800 - source += 0x800 - #endif + current_block = ~0 + dirty = false + cache = (unsigned *)0x40000000 + for unsigned p = 0; p < 1 << (block_bits - PAGE_BITS); ++p: + Iris::Page page = Iris::my_memory.create_page () + page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME) + Iris::my_memory.map (page, (unsigned)cache + p << PAGE_BITS) + Iris::free_cap (page) + Iris::Page tmp = Iris::my_memory.create_page () + tmp.set_flags (Iris::Page::PAYING) + unsigned *tmp_addr = (unsigned *)0x3ffff000 + Iris::my_memory.map (tmp, (unsigned)tmp_addr) - char buffer[0x200] - // Send nand contents to serial port. - for unsigned a = 0; a < 0x400; a += 0x200: - read (a, buffer) - for unsigned s = 0; s < 0x8; ++s: - for unsigned t = 0; t < 0x40; ++t: - kdebug (" ") - kdebug_num (buffer[s * 0x40 + t], 2) - kdebug ("\n") - kdebug ("\n") - // Exit. - return 0 + Iris::Cap cap = Iris::my_receiver.create_capability (0) + Iris::my_parent.provide_capability (cap.copy ()) + Iris::free_cap (cap) -asm volatile ("\t.set noreorder\n" - "\t.globl file_start\n" - "\t.globl file_mid\n" - "\t.globl file_end\n" - "\t.text\n" - "file_start:\n" - "\t.incbin \"mips/nanonote/nand-boot.raw\"\n" - "file_mid:\n" - "\t.incbin \"iris-sd.raw\"\n" - "file_end:\n" - ".set reorder") + Iris::my_parent.init_done () + + while true: + Iris::wait () + switch Iris::recv.data[0].l: + case Iris::Block::GET_SIZE: + if size_bits > 32: + Iris::recv.reply.invoke (Iris::Num (0, 1 << (size_bits - 32))) + else: + Iris::recv.reply.invoke (1 << size_bits) + break + case Iris::Block::GET_ALIGN_BITS: + Iris::recv.reply.invoke (9) + break + case Iris::Block::GET_BLOCK: + unsigned row = Iris::recv.data[1].value () >> 9 + unsigned block = row >> (block_bits - 9) + row &= (1 << (block_bits - 9)) - 1 + if block != current_block: + if current_block != ~0 && dirty: + sync () + current_block = block + read_block (block) + unsigned offset = Iris::recv.data[0].h & 0xe00 + unsigned size = Iris::recv.data[0].h >> 16 + if size + offset >= 0x1000: + size = 0x1000 - offset + Iris::Cap reply = Iris::get_reply () + Iris::Page page = Iris::get_arg () + page.share (tmp) + tmp.set_flags (Iris::Page::FRAME) + for unsigned t = 0; t < size >> 2; ++t: + tmp_addr[(offset >> 2) + t] = cache[(offset >> 2) + t + (row << 9)] + tmp.set_flags (0, Iris::Page::FRAME) + reply.invoke () + Iris::free_cap (reply) + Iris::free_cap (page) + break + case Iris::WBlock::SET_BLOCK: + unsigned row = Iris::recv.data[1].value () >> 9 + unsigned block = row >> (block_bits - 9) + if block != current_block: + if current_block != ~0 && dirty: + sync () + current_block = block + read_block (block) + unsigned offset = Iris::recv.data[0].h & 0xe00 + unsigned size = Iris::recv.data[0].h >> 16 + if size + offset >= 0x1000: + size = 0x1000 - offset + Iris::Cap reply = Iris::get_reply () + Iris::Page page = Iris::get_arg () + page.share (tmp) + tmp.set_flags (Iris::Page::FRAME) + for unsigned t = 0; t < size >> 2; ++t: + cache[(offset >> 2) + t + (row << 9)] = tmp_addr[(offset >> 2) + t] + tmp.set_flags (0, Iris::Page::FRAME) + reply.invoke () + Iris::free_cap (reply) + Iris::free_cap (page) + dirty = true + break + case Iris::WBlock::TRUNCATE: + default: + Iris::recv.reply.invoke (Iris::ERR_INVALID_OPERATION) + break diff --git a/source/partition.ccp b/source/partition.ccp index 734a091..000ff9e 100644 --- a/source/partition.ccp +++ b/source/partition.ccp @@ -38,7 +38,7 @@ struct Partition: Iris::Num Partition::device_size -static Iris::WString dev +static Iris::WBlock dev static void read_sector (Iris::Num idx, Iris::Page page, unsigned size = 1 << SECTOR_BITS, unsigned offset = 0): offset &= ~PAGE_MASK if size + offset > PAGE_SIZE: @@ -50,7 +50,7 @@ static void read_sector (Iris::Num idx, Iris::Page page, unsigned size = 1 << SE Iris::Num start (): Partition::device_size = 0 - dev = Iris::my_parent.get_capability () + dev = Iris::my_parent.get_capability () bits = dev.get_align_bits () if bits > SECTOR_BITS: Iris::panic (bits, "partitioned device doesn't support 512 byte access\n") @@ -71,7 +71,7 @@ Iris::Num start (): for unsigned i = 0; i < NUM_PARTITIONS; ++i: partition[i].read (buffer + 0x1be + 0x10 * i) cap = Iris::my_receiver.create_capability (i) - Iris::my_parent.provide_capability (cap.copy (), i) + Iris::my_parent.provide_capability (cap.copy (), i) Iris::free_cap (cap) page.set_flags (0, Iris::Page::PAYING | Iris::Page::FRAME) @@ -84,24 +84,13 @@ Iris::Num start (): //kdebug_num (Iris::recv.data[0].l) //kdebug ("\n") switch Iris::recv.data[0].l: - case Iris::String::GET_SIZE: + case Iris::Block::GET_SIZE: Iris::recv.reply.invoke (partition[Iris::recv.protected_data.l].size) break - case Iris::String::GET_CHARS: - Iris::Cap reply = Iris::get_reply () - Iris::Num request = Iris::recv.data[1] - Iris::Num offset = (partition[Iris::recv.protected_data.l].start.value () + (request.value () & BLOCK_MASK)) & 0xf - unsigned page_offset = request.l & ~BLOCK_MASK - page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME) - read_sector (offset, page) - reply.invoke (Iris::Num (ubuffer[page_offset >> 2 + 0], ubuffer[page_offset >> 2 + 1]), Iris::Num (ubuffer[page_offset >> 2 + 2], ubuffer[page_offset >> 2 + 3])) - page.set_flags (0, Iris::Page::PAYING | Iris::Page::FRAME) - Iris::free_cap (reply) - break - case Iris::String::GET_ALIGN_BITS: + case Iris::Block::GET_ALIGN_BITS: Iris::recv.reply.invoke (SECTOR_BITS) break - case Iris::String::GET_BLOCK: + case Iris::Block::GET_BLOCK: Iris::Cap reply = Iris::get_reply () Iris::Cap arg = Iris::get_arg () Iris::Num p = Iris::recv.data[1].value () & BLOCK_MASK @@ -126,9 +115,8 @@ Iris::Num start (): Iris::free_cap (reply) Iris::free_cap (arg) break - case Iris::WString::SET_CHARS: - case Iris::WString::SET_BLOCK: + case Iris::WBlock::SET_BLOCK: Iris::panic (Iris::recv.data[0].l, "writing to partitions not supported yet") - case Iris::WString::TRUNCATE: + case Iris::WBlock::TRUNCATE: default: Iris::panic (Iris::recv.data[0].l, "invalid request for partition handler") diff --git a/source/sd+mmc.ccp b/source/sd+mmc.ccp index 06c19c6..993eabc 100644 --- a/source/sd+mmc.ccp +++ b/source/sd+mmc.ccp @@ -495,17 +495,14 @@ Iris::Num start (): //kdebug_num (Iris::recv.data[0].l) //kdebug ("\n") switch Iris::recv.data[0].l: - case Iris::String::GET_SIZE: + case Iris::Block::GET_SIZE: unsigned long long size = mmc.get_num_blocks () * mmc.get_read_block_size () Iris::recv.reply.invoke (size) break - case Iris::String::GET_CHARS: - Iris::panic (0, "get chars from mmc not supported yet") - break - case Iris::String::GET_ALIGN_BITS: + case Iris::Block::GET_ALIGN_BITS: Iris::recv.reply.invoke (9) break - case Iris::String::GET_BLOCK: + case Iris::Block::GET_BLOCK: Iris::Cap reply = Iris::get_reply () Iris::Page page = Iris::get_arg () mmc.fill_page (page, Iris::recv.data[1], Iris::recv.data[0].h >> 16, Iris::recv.data[0].h & 0xffff) @@ -513,7 +510,7 @@ Iris::Num start (): Iris::free_cap (page) Iris::free_cap (reply) break - case Iris::WString::SET_BLOCK: + case Iris::WBlock::SET_BLOCK: Iris::Cap reply = Iris::get_reply () Iris::Page page = Iris::get_arg () mmc.write_page (page, Iris::recv.data[1], Iris::recv.data[0].h >> 16, Iris::recv.data[0].h & 0xffff) @@ -522,8 +519,6 @@ Iris::Num start (): Iris::free_cap (reply) mmc.wait_write () break - case Iris::WString::SET_CHARS: - // Fall through: don't support writing single characters. case Iris::WString::TRUNCATE: // Fall through: don't support resizing. default: diff --git a/source/udc.ccp b/source/udc.ccp index af375b6..e531fa2 100644 --- a/source/udc.ccp +++ b/source/udc.ccp @@ -625,12 +625,11 @@ Iris::Num start (): case FILE: //kdebug ("file request\n") switch Iris::recv.data[0].l: - case Iris::String::GET_BLOCK: + case Iris::Block::GET_BLOCK: if Iris::recv.data[0].h != PAGE_SIZE << 16: Iris::panic (0, "unsupported get_block arguments for boot usb device driver") // Fall through. - case Iris::String::GET_SIZE: - case Iris::String::GET_CHARS: + case Iris::Block::GET_SIZE: udc.send (Iris::recv.data[0].l | ((Iris::recv.data[1].l >> PAGE_BITS) << 16), Iris::recv.protected_data.h, reply, arg) continue default: diff --git a/source/usb-mass-storage.ccp b/source/usb-mass-storage.ccp new file mode 100644 index 0000000..e8e4cfb --- /dev/null +++ b/source/usb-mass-storage.ccp @@ -0,0 +1,689 @@ +#pypp 0 +// Iris: micro-kernel for a capability-based operating system. +// source/usb-mass-storage.ccp: USB mass storage device driver. +// 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 "iris.hh" +#include "devices.hh" +#define ARCH +#include "arch.hh" + +#if 0 +States and expected interrupts: + +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) + -> 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": + void *memset (char *s, int c, unsigned long n): + Iris::debug ("memset called: %x %x->%x\n", s, n, c) + for unsigned i = 0; i < n; ++i: + s[i] = c + return s + +class Udc: + typedef unsigned char u8 + typedef unsigned short u16 + typedef unsigned int u32 + typedef u8 string + // The ugly stuff is because pypp doesn't support __attribute__. + /**/struct Setup { + u8 request_type; + u8 request; + u16 value; + u16 index; + u16 length; + } __attribute__ ((packed)) + /**/struct Device { + static u8 const Type = 1; + u8 length; + u8 type; + u16 usb_version; + u8 dev_class; + u8 subclass; + u8 protocol; + u8 max_packet_size0; + u16 vendor; + u16 product; + u16 dev_version; + string s_manufacturer; + string s_product; + string s_serial; + u8 num_configurations; + } __attribute__ ((packed)) + /**/struct Configuration { + static u8 const Type = 2; + u8 length; + u8 type; + u16 total_length; + u8 num_interfaces; + u8 configuration_value; + u8 configuration; + u8 attributes; + u8 max_power; + } __attribute__ ((packed)) + /**/struct Interface { + static u8 const Type = 4; + u8 length; + u8 type; + u8 interface; + u8 alternate; + u8 num_endpoints; + u8 iface_class; + u8 subclass; + u8 protocol; + string name; + } __attribute__ ((packed)) + /**/struct Endpoint { + static u8 const Type = 5; + u8 length; + u8 type; + u8 address; + u8 attributes; + u16 max_packet_size; + u8 interval; + } __attribute__ ((packed)) + /**/struct Device_Qualifier { + static u8 const Type = 6; + u8 length; + u8 type; + u16 version; + u8 dev_class; + u8 subclass; + u8 protocol; + u8 max_packet_size0; + u8 num_configurations; + u8 reserved; + } __attribute__ ((packed)) + /**/struct Langs { + static u8 const Type = 3; + u8 length; + u8 type; + u8 lang; + } __attribute__ ((packed)) + template struct String { + static u8 const Type = 3; + u8 length; + u8 type; + u16 data[size]; + } __attribute__ ((packed)) + /**/struct CBW { + u32 sig; + u32 tag; + u32 length; + u8 flags; + u8 lun; + u8 size; + u8 data[16]; + enum Code { + INQUIRY = 0x12, + TEST_UNIT_READY = 0x00, + READ_CAPACITY = 0x25, + REQUEST_SENSE = 0x03 + }; + } __attribute__ ((packed)) + union Cbw: + unsigned u[8] + CBW cbw + static unsigned const max_packet_size0 = 64 + static unsigned const max_packet_size_bulk = 64 + enum Requests: + GET_STATUS = 0 + CLEAR_FEATURE = 1 + SET_FEATURE = 3 + SET_ADDRESS = 5 + GET_DESCRIPTOR = 6 + SET_DESCRIPTOR = 7 + GET_CONFIGURATION = 8 + SET_CONFIGURATION = 9 + GET_INTERFACE = 10 + SET_INTERFACE = 11 + SYNCH_FRAME = 12 + enum Storage_requests: + BULK_ONLY_RESET = 0xff + GET_MAX_LUN = 0xfe + enum Request_types: + STANDARD_TO_DEVICE = 0 + CLASS_TO_DEVICE = 0x20 + VENDOR_TO_DEVICE = 0x40 + STANDARD_TO_INTERFACE = 1 + CLASS_TO_INTERFACE = 0x21 + VENDOR_TO_INTERFACE = 0x41 + STANDARD_TO_ENDPOINT = 2 + CLASS_TO_ENDPOINT = 0x22 + VENDOR_TO_ENDPOINT = 0x42 + STANDARD_FROM_DEVICE = 0x80 + CLASS_FROM_DEVICE = 0xa0 + VENDOR_FROM_DEVICE = 0xc0 + STANDARD_FROM_INTERFACE = 0x81 + CLASS_FROM_INTERFACE = 0xa1 + VENDOR_FROM_INTERFACE = 0xc1 + STANDARD_FROM_ENDPOINT = 0x82 + CLASS_FROM_ENDPOINT = 0xa2 + VENDOR_FROM_ENDPOINT = 0xc2 + enum Endpoint_types: + CONTROL = 0 + ISOCHRONOUS = 1 + BULK = 2 + INTERRUPT = 3 + enum Endpoint_features: + ENDPOINT_HALT = 0 + /**/struct my_config { + Configuration config; + Interface interface; + Endpoint endpoint[2]; + } __attribute__ ((packed)) + static Device device_descriptor + //static Device_Qualifier device_qualifier_descriptor + static my_config config_descriptor; //, other_config_descriptor + static String <1> s_langs + static String <6> s_manufacturer + static String <16> s_product + static String <12> s_serial + char configuration + unsigned get_descriptor (unsigned type, unsigned idx, unsigned len) + unsigned handle_setup (Setup *s) + void irq_usb () + void irq_in0 () + void irq_in2 () + void irq_out () + void send_csw () + void parse_cbw () + unsigned big_endian (unsigned src) + enum State: + IDLE + IDLE_WAIT + RX + TX + CSW + SEND_CSW + State state + Cbw cbw + unsigned residue + unsigned status + unsigned block_bits + Iris::WBlock block + public: + void init (Iris::WBlock b) + void log (unsigned c) + void interrupt () + unsigned send (unsigned ep, char const *data, unsigned length, unsigned maxlength) + unsigned send_padded (unsigned ep, char const *data, unsigned length, unsigned maxlength) + +Udc::Device Udc::device_descriptor +Udc::my_config Udc::config_descriptor +Udc::String <1> Udc::s_langs +Udc::String <6> Udc::s_manufacturer +Udc::String <16> Udc::s_product +Udc::String <12> Udc::s_serial + +void Udc::init (Iris::WBlock b): + block = b + block_bits = block.get_align_bits () + // Initialize the globals. My method of compiling doesn't handle global constructors. + device_descriptor = (Device){ sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0xfffe, 0x0002, 0x100, 1, 2, 3, 1 } + config_descriptor = (my_config){ + (Configuration){ sizeof (Configuration), Configuration::Type, sizeof (my_config), 1, 1, 0, 0xc0, 30 }, + (Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0x8, 0x6, 0x50, 0 }, { + (Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 }, + (Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x82, BULK, max_packet_size_bulk, 0 } + } + } + s_langs = (String <1>){ sizeof (String <1>), String <1>::Type, { 0x0409 } } + s_manufacturer = (String <6>){ sizeof (String <6>), String <6>::Type, { 's', 'h', 'e', 'v', 'e', 'k' } } + s_product = (String <16>){ sizeof (String <16>), String <16>::Type, { 'I', 'r', 'i', 's', ' ', 'o', 'n', ' ', 'N', 'a', 'n', 'o', 'N', 'o', 't', 'e' } } + s_serial = (String <12>){ sizeof (String <12>), String <12>::Type, { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B' } } + + cpm_start_udc () + // Disconnect from the bus and don't try to get high-speed. + UDC_POWER = 0 + UDC_TESTMODE = 0 + UDC_INDEX = 0 + configuration = 0 + // 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 interrupt on control endpoint. + UDC_INTRINE = 1 << 0 + // Wait a while. + Iris::sleep (HZ / 10) + // Connect to the host. + UDC_POWER = UDC_POWER_SOFTCONN + + // Initialize cbw state + state = IDLE + status = 0 + residue = 0 + +unsigned Udc::send (unsigned ep, char const *data, unsigned length, unsigned maxlength): + if maxlength < length: + length = maxlength + unsigned i + for i = 0; (length - i & ~3) > 0 && i < length; i += 4: + UDC_FIFO (ep) = ((unsigned *)data)[i / 4] + kdebug_char ('#') + for ; i < length; ++i: + UDC_FIFO8 (ep) = data[i] + kdebug_char ('+') + return ep == 0 ? UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND : 0 + +unsigned Udc::send_padded (unsigned ep, char const *data, unsigned length, unsigned maxlength): + unsigned len = length < maxlength ? length : maxlength + residue = maxlength - len + len = (len + 3) & ~3 + send (ep, data, len, maxlength) + while len + 3 < maxlength: + UDC_FIFO (ep) = 0 + len += 4 + kdebug_char ('-') + while len < maxlength: + UDC_FIFO8 (ep) = 0 + ++len + kdebug_char ('.') + +unsigned Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): + switch type: + case Configuration::Type: + if idx != 0: + return false + Iris::debug ("get config descriptor\n") + return send (0, reinterpret_cast (&config_descriptor), sizeof (config_descriptor), len) + case Device::Type: + if idx != 0: + return false + Iris::debug ("get device descriptor\n") + return send (0, reinterpret_cast (&device_descriptor), sizeof (device_descriptor), len) + case Device_Qualifier::Type: + //if idx != 0: + // return false + //return send (0, reinterpret_cast (&device_qualifier_descriptor), sizeof (device_qualifier_descriptor), len) + //break + return ~0 + // The 6 is an arbitrary number, except that String <6> is instantiated already. + case String <6>::Type: + switch idx: + case 0: + Iris::debug ("get language descriptor\n") + return send (0, reinterpret_cast (&s_langs), sizeof (s_langs), len) + case 1: + Iris::debug ("get manufacturer descriptor\n") + return send (0, reinterpret_cast (&s_manufacturer), sizeof (s_manufacturer), len) + case 2: + Iris::debug ("get product descriptor\n") + return send (0, reinterpret_cast (&s_product), sizeof (s_product), len) + case 3: + Iris::debug ("get serial descriptor\n") + return send (0, reinterpret_cast (&s_serial), sizeof (s_serial), len) + default: + return ~0 + default: + return ~0 + +unsigned Udc::handle_setup (Setup *s): + switch s->request_type: + case STANDARD_TO_DEVICE: + switch s->request: + case SET_ADDRESS: + UDC_FADDR = 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) + return 0 + case SET_INTERFACE: + if s->value != 0: + return ~0 + Iris::debug ("set interface %x\n", s->value) + return 0 + default: + return ~0 + case STANDARD_FROM_DEVICE: + switch s->request: + case GET_STATUS: + Iris::debug ("get status\n") + return send (0, "\0\0", 2, s->length) + case GET_DESCRIPTOR: + return get_descriptor ((s->value >> 8) & 0xff, s->value & 0xff, s->length) + case GET_CONFIGURATION: + Iris::debug ("get configuration\n") + return send (0, &configuration, 1, s->length) + case GET_INTERFACE: + Iris::debug ("get interface\n") + return send (0, "\0", 1, s->length) + default: + return ~0 + case STANDARD_TO_ENDPOINT: + switch s->request: + case CLEAR_FEATURE: + switch s->value: + case ENDPOINT_HALT: + switch s->index: + case 0x82: + Iris::debug ("in ep halt reset\n") + UDC_INDEX = 2 + UDC_INCSR &= ~(UDC_INCSR_SENDSTALL | UDC_INCSR_SENTSTALL) + if state == CSW: + state = SEND_CSW + return 0 + case 1: + Iris::debug ("out ep halt reset\n") + UDC_INDEX = 1 + UDC_OUTCSR &= ~(UDC_OUTCSR_SENDSTALL | UDC_OUTCSR_SENTSTALL) + if state == CSW: + state = SEND_CSW + return 0 + default: + return ~0 + default: + return ~0 + default: + return ~0 + case CLASS_FROM_INTERFACE: + switch s->request: + case GET_MAX_LUN: + Iris::debug ("get max lun\n") + return send (0, "\0", 1, s->length) + default: + return ~0 + case CLASS_TO_INTERFACE: + switch s->request: + case BULK_ONLY_RESET: + Iris::debug ("bulk reset\n") + state = IDLE + return 0 + default: + return ~0 + default: + 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 + 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_INCSR_CDT + UDC_INDEX = 0 + Iris::debug ("usb reset\n") + +void Udc::irq_in0 (): + // Interrupt on endpoint 0. + unsigned csr = UDC_CSR0 + if csr & UDC_CSR0_SENTSTALL: + csr &= ~(UDC_CSR0_SENTSTALL | UDC_CSR0_SENDSTALL) + if csr & UDC_CSR0_SETUPEND: + csr |= UDC_CSR0_SVDSETUPEND + if state == SEND_CSW: + send_csw () + state = IDLE_WAIT + if !(csr & UDC_CSR0_OUTPKTRDY): + kdebug ("packet sent 0\n") + return + 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") + UDC_CSR0 = csr | UDC_CSR0_SENDSTALL + return + UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY + csr &= ~(UDC_CSR0_SETUPEND | UDC_CSR0_SENDSTALL | UDC_CSR0_SENTSTALL) + unsigned ret = handle_setup (&packet.s) + UDC_INDEX = 1 + UDC_OUTMAXP = max_packet_size_bulk + UDC_OUTCSR |= UDC_OUTCSR_CDT + UDC_INDEX = 2 + UDC_INMAXP = max_packet_size_bulk + UDC_INCSR |= UDC_INCSR_CDT + 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) + UDC_CSR0 = csr | UDC_CSR0_SENDSTALL + return + UDC_CSR0 = csr | ret + +void Udc::send_csw (): + UDC_INDEX = 2 + UDC_FIFO (2) = 0x53425355 + UDC_FIFO (2) = cbw.cbw.tag + UDC_FIFO (2) = residue + UDC_FIFO8 (2) = status + UDC_INCSR |= UDC_INCSR_INPKTRDY + status = 0 + residue = 0 + kdebug ("sent csw\n") + state = IDLE_WAIT + +unsigned Udc::big_endian (unsigned src): + return src >> 24 | src >> 8 & 0xff00 | src << 8 & 0xff0000 | src << 24 + +void Udc::parse_cbw (): + UDC_INDEX = 2 + bool to_host = cbw.cbw.flags & 0x80 + switch cbw.cbw.data[0]: + case CBW::INQUIRY: + if !to_host: + UDC_INCSR |= UDC_INCSR_SENDSTALL + status = 2 + state = CSW + return + Iris::debug ("sending inquiry response\n") + send_padded (2, "\x00\x00\x04\x02\x1f\x00\x00\x00shevek iris usb stick 0 ", 36, cbw.cbw.length) + UDC_INCSR |= UDC_INCSR_INPKTRDY + state = CSW + return + case CBW::TEST_UNIT_READY: + if to_host || cbw.cbw.length != 0: + UDC_INCSR |= UDC_INCSR_SENDSTALL + status = 2 + state = CSW + return + Iris::debug ("sending ready response\n") + send_csw () + return + case CBW::READ_CAPACITY: + if !to_host: + UDC_INCSR |= UDC_INCSR_SENDSTALL + status = 2 + state = CSW + return + unsigned capacity[2] + capacity[0] = big_endian ((block.get_size ().value () >> block_bits) - 1) + capacity[1] = big_endian (1 << block_bits) + Iris::debug ("sending capacity: %x * %x\n", capacity[0], capacity[1]) + send_padded (2, (char *)capacity, 8, cbw.cbw.length) + UDC_INCSR |= UDC_INCSR_INPKTRDY + state = CSW + return + case CBW::REQUEST_SENSE: + Iris::debug ("sense requested\n") + send_padded (2, "\xf0\x00\x05\x00\x00\x00\x00\x00", 8, cbw.cbw.length) + UDC_INCSR |= UDC_INCSR_INPKTRDY + state = CSW + return + default: + Iris::debug ("cbw:") + for unsigned i = 0; i < cbw.cbw.size; ++i: + kdebug_char (' ') + kdebug_num (cbw.cbw.data[i], 2) + Iris::debug ("\n") + UDC_INCSR |= UDC_INCSR_SENDSTALL + residue = cbw.cbw.length + status = 1 + state = CSW + return + // TODO. + +void Udc::irq_in2 (): + UDC_INDEX = 2 + unsigned csr = UDC_INCSR + if csr & UDC_INCSR_SENDSTALL: + // When stalling, do nothing else. + kdebug ("stalling\n") + UDC_INDEX = 0 + return + switch state: + case IDLE_WAIT: + // data has been received. + kdebug ("bulk data was sent\n") + state = IDLE + break + case IDLE: + // This should not happen. + Iris::debug ("in interrupt while idle\n") + break + case TX: + // TODO: transmit more. + kdebug ("bulk transmit\n") + break + case RX: + // This should not be possible. + Iris::debug ("in interrupt while receiving\n") + UDC_INCSR = csr | UDC_INCSR_SENDSTALL + status = 2 + state = CSW + break + case CSW: + // The host is now ready to receive the csw; send it. + kdebug ("bulk send csw\n") + send_csw () + break + UDC_INDEX = 0 + +void Udc::irq_out (): + 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. + UDC_INDEX = 0 + return + switch state: + case IDLE_WAIT: + Iris::debug ("out interrupt while idle waiting\n") + break + case IDLE: + // expect a new cbw. + if size != 31: + Iris::debug ("count %d != 31\n", UDC_OUTCOUNT) + UDC_OUTCSR = csr | UDC_OUTCSR_SENDSTALL + status = 2 + state = CSW + break + for unsigned i = 0; i < 8; ++i: + cbw.u[i] = UDC_FIFO (1) + 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) + UDC_OUTCSR = csr | UDC_OUTCSR_SENDSTALL + status = 2 + state = CSW + break + UDC_OUTCSR = csr & ~UDC_OUTCSR_OUTPKTRDY + kdebug ("bulk cbw\n") + parse_cbw () + break + case TX: + // This should be impossible. + Iris::debug ("out interrupt while transmitting\n") + UDC_OUTCSR = csr | UDC_OUTCSR_SENDSTALL + break + case RX: + // TODO: Handle the data. + kdebug ("bulk rx\n") + UDC_OUTCSR = csr & ~UDC_OUTCSR_OUTPKTRDY + break + case CSW: + // This should be impossible. + Iris::debug ("out interrupt while csw-waiting\n") + UDC_OUTCSR = csr | UDC_OUTCSR_SENDSTALL + break + UDC_INDEX = 0 + +void Udc::interrupt (): + while true: + unsigned usb = UDC_INTRUSB + unsigned in = UDC_INTRIN + unsigned out = UDC_INTROUT + if !(usb & 4) && !(in & 5) && !(out & 2): + // No more interrupts to handle; this is normal, because we're looping until this happens. + Iris::debug ("irq done\n") + return + if usb & 4: + Iris::debug ("usb\t") + // reset. + irq_usb () + if in & 1: + Iris::debug ("control\t") + // control request + irq_in0 () + if in & 4: + Iris::debug ("in\t") + // bulk in done + irq_in2 () + if out & 2: + Iris::debug ("out\t") + // bulk out waiting + irq_out () + +Iris::Num start (): + map_udc () + map_gpio () + map_cpm () + Udc udc + + Iris::WBlock nand = Iris::my_parent.get_capability () + udc.init (nand) + while true: + Iris::register_interrupt (IRQ_UDC) + Iris::wait () + udc.interrupt ()