mirror of
git://projects.qi-hardware.com/iris.git
synced 2024-12-29 20:23:00 +02:00
working on mass storage
This commit is contained in:
parent
aef83317c9
commit
61d76aaefb
129
devices.hhp
129
devices.hhp
@ -33,19 +33,49 @@ namespace Iris:
|
|||||||
_T get (unsigned idx):
|
_T get (unsigned idx):
|
||||||
return _T (Caps::get (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 _Locker_base:
|
||||||
struct String : public Cap:
|
|
||||||
String (Cap c = Cap ()) : Cap (c):
|
|
||||||
enum request:
|
enum request:
|
||||||
GET_SIZE = 0x2001
|
LOCK_RO = 0x2001
|
||||||
|
UNLOCK_RO
|
||||||
|
NUM
|
||||||
|
template <typename _T> //
|
||||||
|
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 <typename _T> //
|
||||||
|
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_CHARS
|
||||||
GET_ALIGN_BITS
|
|
||||||
GET_BLOCK
|
|
||||||
ID
|
ID
|
||||||
/// Get the size of the string.
|
/// Get the size of the string.
|
||||||
Num get_size ():
|
Num get_size ():
|
||||||
return call (CAP_MASTER_DIRECT | 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]):
|
char *get_chars (Num idx, char buffer[16]):
|
||||||
call (CAP_MASTER_DIRECT | GET_CHARS, idx)
|
call (CAP_MASTER_DIRECT | GET_CHARS, idx)
|
||||||
unsigned *b = (unsigned *)buffer
|
unsigned *b = (unsigned *)buffer
|
||||||
@ -54,6 +84,19 @@ namespace Iris:
|
|||||||
b[2] = recv.data[1].l
|
b[2] = recv.data[1].l
|
||||||
b[3] = recv.data[1].h
|
b[3] = recv.data[1].h
|
||||||
return buffer
|
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.
|
/// Get the number of bits that page accesses must be aligned to. Cannot be more than PAGE_BITS.
|
||||||
unsigned get_align_bits ():
|
unsigned get_align_bits ():
|
||||||
return call (CAP_MASTER_DIRECT | GET_ALIGN_BITS).l
|
return call (CAP_MASTER_DIRECT | GET_ALIGN_BITS).l
|
||||||
@ -62,34 +105,47 @@ namespace Iris:
|
|||||||
Page ret = my_memory.create_page ()
|
Page ret = my_memory.create_page ()
|
||||||
ret.set_flags (Page::PAYING)
|
ret.set_flags (Page::PAYING)
|
||||||
return ret
|
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 ()):
|
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)
|
ocall (ret, Iris::Num (CAP_MASTER_DIRECT | GET_BLOCK, size << 16 | offset), idx)
|
||||||
return ret
|
return ret
|
||||||
|
typedef Locker <_Block> Block
|
||||||
|
|
||||||
/// A writable String.
|
/// A writable String.
|
||||||
struct WString : public String:
|
struct _WString : public String:
|
||||||
WString (Cap c = Cap ()) : String (c):
|
_WString (Cap c = Cap ()) : String (c):
|
||||||
enum request:
|
enum request:
|
||||||
TRUNCATE = String::ID
|
TRUNCATE = _Block::ID
|
||||||
SET_CHARS
|
SET_CHARS
|
||||||
SET_BLOCK
|
|
||||||
ID
|
ID
|
||||||
/// Set the size of the string. Strings may have a limit to this setting.
|
/// Set the size of the string. Strings may have a limit to this setting.
|
||||||
void truncate (Num size):
|
void truncate (Num size):
|
||||||
call (CAP_MASTER_DIRECT | TRUNCATE, 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]):
|
void set_chars (Num idx, char buffer[4]):
|
||||||
call (Num (CAP_MASTER_DIRECT | SET_CHARS, *(unsigned *)buffer), idx)
|
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):
|
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)
|
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.
|
// This interface allows another kernel to be booted by Iris.
|
||||||
struct Boot : public Cap:
|
struct Boot : public Cap:
|
||||||
Boot (Cap c = Cap ()) : Cap (c):
|
Boot (Cap c = Cap ()) : Cap (c):
|
||||||
enum request:
|
enum request:
|
||||||
BOOT = WString::ID
|
BOOT = _WBlock::ID
|
||||||
ID
|
ID
|
||||||
// Boot a new kernel.
|
// Boot a new kernel.
|
||||||
void boot (String code, unsigned load, unsigned entry):
|
void boot (String code, unsigned load, unsigned entry):
|
||||||
@ -117,19 +173,19 @@ namespace Iris:
|
|||||||
struct Elfrun : public Cap:
|
struct Elfrun : public Cap:
|
||||||
Elfrun (Cap c = Cap ()) : Cap (c):
|
Elfrun (Cap c = Cap ()) : Cap (c):
|
||||||
enum request:
|
enum request:
|
||||||
RUN_STRING = Event::ID
|
RUN_BLOCK = Event::ID
|
||||||
RUN_CAPS
|
RUN_CAPS
|
||||||
ID
|
ID
|
||||||
enum arg_pos:
|
enum arg_pos:
|
||||||
PARENT_MEMORY
|
PARENT_MEMORY
|
||||||
DATA
|
DATA
|
||||||
PARENT
|
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 caps = my_memory.create_caps (3)
|
||||||
caps.set (PARENT_MEMORY, parent_memory)
|
caps.set (PARENT_MEMORY, parent_memory)
|
||||||
caps.set (DATA, data)
|
caps.set (DATA, data)
|
||||||
caps.set (PARENT, parent)
|
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 ()
|
Caps ret = get_arg ()
|
||||||
my_memory.destroy (caps)
|
my_memory.destroy (caps)
|
||||||
free_cap (caps)
|
free_cap (caps)
|
||||||
@ -257,15 +313,13 @@ namespace Iris:
|
|||||||
// Seekable is not a class, it is identical to [W]String.
|
// Seekable is not a class, it is identical to [W]String.
|
||||||
|
|
||||||
// Directory interface.
|
// Directory interface.
|
||||||
struct Directory : public Cap:
|
struct _Directory : public Cap:
|
||||||
Directory (Cap c = Cap ()) : Cap (c):
|
_Directory (Cap c = Cap ()) : Cap (c):
|
||||||
enum request:
|
enum request:
|
||||||
GET_SIZE = Setting::ID
|
GET_SIZE = Setting::ID
|
||||||
GET_NAME
|
GET_NAME
|
||||||
GET_FILE_RO
|
GET_FILE_RO
|
||||||
GET_FILE_INFO
|
GET_FILE_INFO
|
||||||
LOCK_RO
|
|
||||||
UNLOCK_RO
|
|
||||||
ID
|
ID
|
||||||
// Get the number of entries in this directory.
|
// Get the number of entries in this directory.
|
||||||
Num get_size ():
|
Num get_size ():
|
||||||
@ -281,20 +335,14 @@ namespace Iris:
|
|||||||
// Get file info. TODO: define possible types.
|
// Get file info. TODO: define possible types.
|
||||||
Num get_file_info (Num idx, unsigned type):
|
Num get_file_info (Num idx, unsigned type):
|
||||||
return icall (Num (CAP_MASTER_DIRECT | GET_FILE_INFO, type), idx)
|
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.
|
typedef Locker <_Directory> Directory
|
||||||
void lock_ro ():
|
|
||||||
call (CAP_MASTER_DIRECT | LOCK_RO)
|
struct _WDirectory : public Directory:
|
||||||
// Release a read-only lock. Write operations can only be done when the directory is locked.
|
_WDirectory (Cap c = Cap ()) : Directory (c):
|
||||||
void unlock_ro ():
|
|
||||||
call (CAP_MASTER_DIRECT | UNLOCK_RO)
|
|
||||||
struct WDirectory : public Directory:
|
|
||||||
WDirectory (Cap c = Cap ()) : Directory (c):
|
|
||||||
enum request:
|
enum request:
|
||||||
GET_FILE = Directory::ID
|
GET_FILE = _Directory::ID
|
||||||
CREATE_FILE
|
CREATE_FILE
|
||||||
DELETE_FILE
|
DELETE_FILE
|
||||||
LOCK
|
|
||||||
UNLOCK
|
|
||||||
ID
|
ID
|
||||||
// Get the file.
|
// Get the file.
|
||||||
Cap get_file (Num idx):
|
Cap get_file (Num idx):
|
||||||
@ -307,18 +355,13 @@ namespace Iris:
|
|||||||
// Delete a file. After this, any index may map to a different file.
|
// Delete a file. After this, any index may map to a different file.
|
||||||
void delete_file (Num idx):
|
void delete_file (Num idx):
|
||||||
call (CAP_MASTER_DIRECT | DELETE_FILE, idx)
|
call (CAP_MASTER_DIRECT | DELETE_FILE, idx)
|
||||||
// Lock the directory. Write operations can only be done when the directory is locked.
|
typedef WLocker <_WDirectory> WDirectory
|
||||||
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)
|
|
||||||
|
|
||||||
// Stream interface.
|
// Stream interface.
|
||||||
struct Stream : public Cap:
|
struct Stream : public Cap:
|
||||||
Stream (Cap c = Cap ()) : Cap (c):
|
Stream (Cap c = Cap ()) : Cap (c):
|
||||||
enum request:
|
enum request:
|
||||||
READ = Directory::ID
|
READ = _WDirectory::ID
|
||||||
WRITE
|
WRITE
|
||||||
ID
|
ID
|
||||||
// Try to read size bytes. Returns the number of bytes successfully read.
|
// Try to read size bytes. Returns the number of bytes successfully read.
|
||||||
@ -344,12 +387,6 @@ namespace Iris:
|
|||||||
void exit ():
|
void exit ():
|
||||||
call (CAP_MASTER_DIRECT | 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.
|
// TODO.
|
||||||
// Sound interface.
|
// Sound interface.
|
||||||
// Usb interfaces (port, device).
|
// Usb interfaces (port, device).
|
||||||
|
32
init.config
32
init.config
@ -13,7 +13,11 @@
|
|||||||
receive driver_gpio / Event = sdmmc_gpio
|
receive driver_gpio / Event = sdmmc_gpio
|
||||||
sysreq sysreq
|
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"
|
#driver driver_boot = "boot.elf"
|
||||||
#receive driver_boot / Boot = boot
|
#receive driver_boot / Boot = boot
|
||||||
@ -23,22 +27,22 @@
|
|||||||
#give booter / String = kernel
|
#give booter / String = kernel
|
||||||
#give booter / Boot = boot
|
#give booter / Boot = boot
|
||||||
|
|
||||||
driver driver_lcd = "lcd.elf"
|
#driver driver_lcd = "lcd.elf"
|
||||||
receive driver_lcd / Display = display
|
#receive driver_lcd / Display = display
|
||||||
receive driver_lcd / Setting = display_bright
|
#receive driver_lcd / Setting = display_bright
|
||||||
|
|
||||||
driver driver_buzzer = "buzzer.elf"
|
#driver driver_buzzer = "buzzer.elf"
|
||||||
receive driver_buzzer / Buzzer = buzzer
|
#receive driver_buzzer / Buzzer = buzzer
|
||||||
|
|
||||||
program alarm = "alarm.elf"
|
#program alarm = "alarm.elf"
|
||||||
receive alarm / UI = ui
|
#receive alarm / UI = ui
|
||||||
|
|
||||||
program gui = "gui.elf"
|
#program gui = "gui.elf"
|
||||||
give gui / UI = ui
|
#give gui / UI = ui
|
||||||
give gui / Display = display
|
#give gui / Display = display
|
||||||
give gui / Setting = display_bright
|
#give gui / Setting = display_bright
|
||||||
give gui / Buzzer = buzzer
|
#give gui / Buzzer = buzzer
|
||||||
give gui / Keyboard = keyboard
|
#give gui / Keyboard = keyboard
|
||||||
|
|
||||||
#driver sdmmc = "sd+mmc.elf"
|
#driver sdmmc = "sd+mmc.elf"
|
||||||
#receive sdmmc / WString = sdmmc
|
#receive sdmmc / WString = sdmmc
|
||||||
|
@ -53,6 +53,7 @@ static volatile char *data
|
|||||||
static unsigned page_bits
|
static unsigned page_bits
|
||||||
static unsigned redundant_bits
|
static unsigned redundant_bits
|
||||||
static unsigned block_bits
|
static unsigned block_bits
|
||||||
|
static unsigned size_bits
|
||||||
static unsigned word_size
|
static unsigned word_size
|
||||||
|
|
||||||
static void unbusy ():
|
static void unbusy ():
|
||||||
@ -107,8 +108,9 @@ static void reset ():
|
|||||||
debug ("word size: %d\n", word_size)
|
debug ("word size: %d\n", word_size)
|
||||||
//unsigned serial_access_minimum = (d & 0x80 ? 25 : 50)
|
//unsigned serial_access_minimum = (d & 0x80 ? 25 : 50)
|
||||||
d = rdata ()
|
d = rdata ()
|
||||||
//unsigned num_planes = 1 << ((d >> 2) & 3)
|
unsigned num_planes_bits = d >> 2 & 3
|
||||||
//unsigned plane_bits = 26 + ((d >> 4) & 7)
|
unsigned plane_bits = 26 + (d >> 4 & 7)
|
||||||
|
size_bits = plane_bits + num_planes_bits - 3
|
||||||
|
|
||||||
static bool read (unsigned a, char *buffer):
|
static bool read (unsigned a, char *buffer):
|
||||||
unsigned column = a & ((1 << page_bits) - 1)
|
unsigned column = a & ((1 << page_bits) - 1)
|
||||||
|
@ -26,7 +26,7 @@ udc_boot_programs = udc
|
|||||||
sd_boot_programs = sd+mmc partition fat
|
sd_boot_programs = sd+mmc partition fat
|
||||||
standard_boot_programs = bootinit
|
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
|
ARCH_CPPFLAGS = -I. -Imips -Imips/nanonote -Wa,-mips32 -DNANONOTE -DUSE_SERIAL
|
||||||
CROSS = mipsel-linux-gnu-
|
CROSS = mipsel-linux-gnu-
|
||||||
|
@ -155,7 +155,7 @@ void data::poll ():
|
|||||||
if !--lock:
|
if !--lock:
|
||||||
dir.clear ()
|
dir.clear ()
|
||||||
continue
|
continue
|
||||||
case Iris::String::GET_BLOCK:
|
case Iris::Block::GET_BLOCK:
|
||||||
if buffer[1] >= dir.size ():
|
if buffer[1] >= dir.size ():
|
||||||
std::cerr << "reading invalid file" << std::endl
|
std::cerr << "reading invalid file" << std::endl
|
||||||
usb_release_interface (handle, 0)
|
usb_release_interface (handle, 0)
|
||||||
@ -177,7 +177,7 @@ void data::poll ():
|
|||||||
handle = NULL
|
handle = NULL
|
||||||
return
|
return
|
||||||
continue
|
continue
|
||||||
case Iris::String::GET_SIZE:
|
case Iris::Block::GET_SIZE:
|
||||||
if buffer[1] >= dir.size ():
|
if buffer[1] >= dir.size ():
|
||||||
std::cerr << "reading invalid file size " << buffer[1] << " >= " << dir.size () << std::endl
|
std::cerr << "reading invalid file size " << buffer[1] << " >= " << dir.size () << std::endl
|
||||||
usb_release_interface (handle, 0)
|
usb_release_interface (handle, 0)
|
||||||
|
@ -41,7 +41,7 @@ Iris::Num start ():
|
|||||||
Iris::wait ()
|
Iris::wait ()
|
||||||
switch Iris::recv.data[0].l:
|
switch Iris::recv.data[0].l:
|
||||||
case Iris::Boot::BOOT:
|
case Iris::Boot::BOOT:
|
||||||
Iris::String code = Iris::get_arg ()
|
Iris::Block code = Iris::get_arg ()
|
||||||
unsigned load = Iris::recv.data[1].l
|
unsigned load = Iris::recv.data[1].l
|
||||||
unsigned entry = Iris::recv.data[1].h
|
unsigned entry = Iris::recv.data[1].h
|
||||||
Iris::Num lsize = code.get_size ()
|
Iris::Num lsize = code.get_size ()
|
||||||
|
@ -65,7 +65,7 @@ static Iris::Page bss_page
|
|||||||
|
|
||||||
// Get the initial block device and filesystem.
|
// Get the initial block device and filesystem.
|
||||||
static Iris::Directory receive_devices ():
|
static Iris::Directory receive_devices ():
|
||||||
Iris::String device
|
Iris::Block device
|
||||||
bool have_device = false
|
bool have_device = false
|
||||||
Iris::Cap reply[2]
|
Iris::Cap reply[2]
|
||||||
bool have_reply[2]
|
bool have_reply[2]
|
||||||
@ -85,8 +85,8 @@ static Iris::Directory receive_devices ():
|
|||||||
switch Iris::recv.data[0].l:
|
switch Iris::recv.data[0].l:
|
||||||
case Iris::Parent::PROVIDE_CAPABILITY:
|
case Iris::Parent::PROVIDE_CAPABILITY:
|
||||||
switch Iris::recv.data[1].l:
|
switch Iris::recv.data[1].l:
|
||||||
case Iris::String::ID:
|
case Iris::Block::ID:
|
||||||
case Iris::WString::ID:
|
case Iris::WBlock::ID:
|
||||||
// Ignore other partitions.
|
// Ignore other partitions.
|
||||||
Iris::Cap r = Iris::get_reply ()
|
Iris::Cap r = Iris::get_reply ()
|
||||||
if Iris::recv.data[0].h != 0:
|
if Iris::recv.data[0].h != 0:
|
||||||
@ -96,11 +96,11 @@ static Iris::Directory receive_devices ():
|
|||||||
Iris::panic (0, "double device provided")
|
Iris::panic (0, "double device provided")
|
||||||
device = Iris::get_arg ()
|
device = Iris::get_arg ()
|
||||||
if have_reply[next - 2]:
|
if have_reply[next - 2]:
|
||||||
kdebug ("string provided (used)\n")
|
kdebug ("block provided (used)\n")
|
||||||
reply[next++ - 2].invoke (0, 0, device.copy ())
|
reply[next++ - 2].invoke (0, 0, device.copy ())
|
||||||
Iris::free_cap (device)
|
Iris::free_cap (device)
|
||||||
else:
|
else:
|
||||||
kdebug ("string provided (stored)\n")
|
kdebug ("block provided (stored)\n")
|
||||||
have_device = true
|
have_device = true
|
||||||
r.invoke ()
|
r.invoke ()
|
||||||
Iris::free_cap (r)
|
Iris::free_cap (r)
|
||||||
@ -123,16 +123,16 @@ static Iris::Directory receive_devices ():
|
|||||||
Iris::free_cap (event)
|
Iris::free_cap (event)
|
||||||
Iris::free_cap (reply)
|
Iris::free_cap (reply)
|
||||||
break
|
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")
|
Iris::panic (Iris::recv.data[1].l, "invalid capability type requested by boot thread")
|
||||||
if next == Iris::recv.protected_data.l && have_device:
|
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::recv.reply.invoke (0, 0, device.copy ())
|
||||||
Iris::free_cap (device)
|
Iris::free_cap (device)
|
||||||
have_device = false
|
have_device = false
|
||||||
++next
|
++next
|
||||||
else:
|
else:
|
||||||
kdebug ("string requested (not sent)\n")
|
kdebug ("block requested (not sent)\n")
|
||||||
reply[Iris::recv.protected_data.l - 2] = Iris::get_reply ()
|
reply[Iris::recv.protected_data.l - 2] = Iris::get_reply ()
|
||||||
have_reply[Iris::recv.protected_data.l - 2] = true
|
have_reply[Iris::recv.protected_data.l - 2] = true
|
||||||
break
|
break
|
||||||
@ -152,7 +152,7 @@ static bool stringcmp (char const *s1, char const *s2, unsigned size):
|
|||||||
return false
|
return false
|
||||||
return true
|
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
|
unsigned size = 0
|
||||||
while name[size]:
|
while name[size]:
|
||||||
++size
|
++size
|
||||||
@ -165,11 +165,11 @@ static Iris::String find (Iris::Directory root, char const *name):
|
|||||||
if !stringcmp (current_name, name, size):
|
if !stringcmp (current_name, name, size):
|
||||||
continue
|
continue
|
||||||
// Found elfrun.
|
// Found elfrun.
|
||||||
Iris::String ret = root.get_file_ro (i)
|
Iris::Block ret = root.get_file_ro (i)
|
||||||
return ret
|
return ret
|
||||||
Iris::panic (0, "bootfile not found")
|
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.
|
// Get the size.
|
||||||
Iris::Num size = data.get_size ()
|
Iris::Num size = data.get_size ()
|
||||||
if size.value () == 0:
|
if size.value () == 0:
|
||||||
@ -320,9 +320,9 @@ Iris::Num start ():
|
|||||||
Iris::Memory top_memory = Iris::get_top_memory ()
|
Iris::Memory top_memory = Iris::get_top_memory ()
|
||||||
Iris::Directory root = receive_devices ()
|
Iris::Directory root = receive_devices ()
|
||||||
root.lock_ro ()
|
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)
|
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 ()
|
Iris::wait ()
|
||||||
if Iris::recv.data[0].l != Iris::Parent::PROVIDE_CAPABILITY || Iris::recv.data[1].l != Iris::Elfrun::ID:
|
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")
|
Iris::panic (0, "elfrun doesn't provide correct capability")
|
||||||
@ -334,8 +334,8 @@ Iris::Num start ():
|
|||||||
Iris::free_cap (reply)
|
Iris::free_cap (reply)
|
||||||
|
|
||||||
parent_cap = Iris::my_receiver.create_capability (0)
|
parent_cap = Iris::my_receiver.create_capability (0)
|
||||||
Iris::String init_string = find (root, INIT_NAME)
|
Iris::Block init_block = find (root, INIT_NAME)
|
||||||
Iris::Caps init_caps = elfrun.run_string (top_memory.copy (), init_string.copy (), parent_cap.copy (), 8, 63)
|
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)
|
Iris::Thread init = init_caps.get (__thread_num)
|
||||||
init.make_priv ()
|
init.make_priv ()
|
||||||
|
@ -63,7 +63,7 @@ static Iris::Memory mem
|
|||||||
static unsigned *bss_mapping
|
static unsigned *bss_mapping
|
||||||
static Iris::Page bss_page
|
static Iris::Page bss_page
|
||||||
|
|
||||||
static Iris::Caps map_string (Iris::String data):
|
static Iris::Caps map_string (Iris::Block data):
|
||||||
// Get the size.
|
// Get the size.
|
||||||
Iris::Num size = data.get_size ()
|
Iris::Num size = data.get_size ()
|
||||||
if size.value () == 0:
|
if size.value () == 0:
|
||||||
@ -267,12 +267,12 @@ Iris::Num start ():
|
|||||||
Iris::Cap reply = Iris::get_reply ()
|
Iris::Cap reply = Iris::get_reply ()
|
||||||
Iris::Cap arg = Iris::get_arg ()
|
Iris::Cap arg = Iris::get_arg ()
|
||||||
switch Iris::recv.data[0].l:
|
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_slots = Iris::recv.data[1].l
|
||||||
unsigned num_caps = Iris::recv.data[1].h
|
unsigned num_caps = Iris::recv.data[1].h
|
||||||
parent_memory = Iris::Caps (arg).get (Iris::Elfrun::PARENT_MEMORY)
|
parent_memory = Iris::Caps (arg).get (Iris::Elfrun::PARENT_MEMORY)
|
||||||
parent = Iris::Caps (arg).get (Iris::Elfrun::PARENT)
|
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)
|
map_string (data)
|
||||||
Iris::Caps ret = run (data, parent_memory, parent, num_slots, num_caps)
|
Iris::Caps ret = run (data, parent_memory, parent, num_slots, num_caps)
|
||||||
reply.invoke (0, 0, ret.copy ())
|
reply.invoke (0, 0, ret.copy ())
|
||||||
|
@ -39,7 +39,7 @@ void *operator new[] (unsigned size):
|
|||||||
void *operator new (unsigned size):
|
void *operator new (unsigned size):
|
||||||
return new char[size]
|
return new char[size]
|
||||||
|
|
||||||
static Iris::WString dev
|
static Iris::WBlock dev
|
||||||
static Iris::Num device_size
|
static Iris::Num device_size
|
||||||
static Iris::Page page
|
static Iris::Page page
|
||||||
static char *data
|
static char *data
|
||||||
@ -491,7 +491,7 @@ struct Fat:
|
|||||||
Iris::Num start ():
|
Iris::Num start ():
|
||||||
init_alloc ()
|
init_alloc ()
|
||||||
current_block = ~0
|
current_block = ~0
|
||||||
dev = Iris::my_parent.get_capability <Iris::WString> ()
|
dev = Iris::my_parent.get_capability <Iris::WBlock> ()
|
||||||
if dev.get_align_bits () > SECTOR_BITS:
|
if dev.get_align_bits () > SECTOR_BITS:
|
||||||
kdebug ("fat device doesn't support 512 byte access")
|
kdebug ("fat device doesn't support 512 byte access")
|
||||||
return 1
|
return 1
|
||||||
@ -574,11 +574,6 @@ Iris::Num start ():
|
|||||||
//kdebug ("\n")
|
//kdebug ("\n")
|
||||||
reply.invoke (Iris::Num (u.u[0], u.u[1]), Iris::Num (u.u[2], u.u[3]))
|
reply.invoke (Iris::Num (u.u[0], u.u[1]), Iris::Num (u.u[2], u.u[3]))
|
||||||
break
|
break
|
||||||
case Iris::String::GET_ALIGN_BITS:
|
|
||||||
//kdebug ("filename align requested\n")
|
|
||||||
reply.invoke (0)
|
|
||||||
break
|
|
||||||
case Iris::String::GET_BLOCK:
|
|
||||||
default:
|
default:
|
||||||
Iris::panic (Iris::recv.data[0].l, "invalid request for fat filename")
|
Iris::panic (Iris::recv.data[0].l, "invalid request for fat filename")
|
||||||
Iris::free_cap (reply)
|
Iris::free_cap (reply)
|
||||||
@ -594,23 +589,15 @@ Iris::Num start ():
|
|||||||
Fat::File f
|
Fat::File f
|
||||||
fat.get_dir_entry (dir, idx, &f)
|
fat.get_dir_entry (dir, idx, &f)
|
||||||
switch cmd:
|
switch cmd:
|
||||||
case Iris::String::GET_SIZE:
|
case Iris::Block::GET_SIZE:
|
||||||
//kdebug ("file size requested\n")
|
//kdebug ("file size requested\n")
|
||||||
reply.invoke (f.size)
|
reply.invoke (f.size)
|
||||||
break
|
break
|
||||||
case Iris::String::GET_CHARS:
|
case Iris::Block::GET_ALIGN_BITS:
|
||||||
//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:
|
|
||||||
//kdebug ("file align requested\n")
|
//kdebug ("file align requested\n")
|
||||||
reply.invoke (fat.cluster_size_bits <= PAGE_BITS ? fat.cluster_size_bits : PAGE_BITS)
|
reply.invoke (fat.cluster_size_bits <= PAGE_BITS ? fat.cluster_size_bits : PAGE_BITS)
|
||||||
break
|
break
|
||||||
case Iris::String::GET_BLOCK:
|
case Iris::Block::GET_BLOCK:
|
||||||
//kdebug ("file block requested\n")
|
//kdebug ("file block requested\n")
|
||||||
unsigned mask = (1 << fat.cluster_size_bits) - 1
|
unsigned mask = (1 << fat.cluster_size_bits) - 1
|
||||||
//kdebug ("mask = ")
|
//kdebug ("mask = ")
|
||||||
@ -626,9 +613,8 @@ Iris::Num start ():
|
|||||||
f.load_cluster ((num.l & ~mask) + i, num.l & mask, arg, i + offset)
|
f.load_cluster ((num.l & ~mask) + i, num.l & mask, arg, i + offset)
|
||||||
reply.invoke ()
|
reply.invoke ()
|
||||||
break
|
break
|
||||||
case Iris::WString::TRUNCATE:
|
case Iris::WBlock::TRUNCATE:
|
||||||
case Iris::WString::SET_CHARS:
|
case Iris::WBlock::SET_BLOCK:
|
||||||
case Iris::WString::SET_BLOCK:
|
|
||||||
Iris::panic (Iris::recv.data[0].l, "writing to files not supported yet")
|
Iris::panic (Iris::recv.data[0].l, "writing to files not supported yet")
|
||||||
default:
|
default:
|
||||||
Iris::panic (Iris::recv.data[0].l, "invalid request for fat file")
|
Iris::panic (Iris::recv.data[0].l, "invalid request for fat file")
|
||||||
|
@ -185,7 +185,7 @@ static Iris::Caps load (char const *name, unsigned name_len, unsigned &size, Iri
|
|||||||
Iris::free_cap (n)
|
Iris::free_cap (n)
|
||||||
continue
|
continue
|
||||||
Iris::free_cap (n)
|
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 ()
|
Iris::Num s = file.get_size ()
|
||||||
if s.h:
|
if s.h:
|
||||||
Iris::panic (s.h, "file is too large to load")
|
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[] = {
|
static Type types[] = {
|
||||||
{ "String", 6, Iris::String::ID },
|
{ "String", 6, Iris::String::ID },
|
||||||
{ "WString", 7, Iris::WString::ID },
|
{ "WString", 7, Iris::WString::ID },
|
||||||
|
{ "Block", 5, Iris::Block::ID },
|
||||||
|
{ "WBlock", 6, Iris::WBlock::ID },
|
||||||
{ "Boot", 4, Iris::Boot::ID },
|
{ "Boot", 4, Iris::Boot::ID },
|
||||||
{ "Device", 6, Iris::Device::ID },
|
{ "Device", 6, Iris::Device::ID },
|
||||||
{ "Event", 5, Iris::Event::ID },
|
{ "Event", 5, Iris::Event::ID },
|
||||||
@ -405,7 +407,7 @@ static void parse_line (char *&line, unsigned maxlen):
|
|||||||
else if match (start, maxlen, "file"):
|
else if match (start, maxlen, "file"):
|
||||||
// file <name> = "<filename>"
|
// file <name> = "<filename>"
|
||||||
File *f = &**files.insert ()
|
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:
|
if !get_name (start, maxlen, f->name, f->name_len) || !match (start, maxlen, "=") || !maxlen:
|
||||||
Iris::panic (0, "syntax error in init.config (file name)")
|
Iris::panic (0, "syntax error in init.config (file name)")
|
||||||
unsigned l
|
unsigned l
|
||||||
@ -519,15 +521,13 @@ Iris::Num start ():
|
|||||||
case FILE:
|
case FILE:
|
||||||
File *file = (File *)Iris::recv.protected_data.h
|
File *file = (File *)Iris::recv.protected_data.h
|
||||||
switch Iris::recv.data[0].l:
|
switch Iris::recv.data[0].l:
|
||||||
case Iris::String::GET_SIZE:
|
case Iris::Block::GET_SIZE:
|
||||||
Iris::recv.reply.invoke (file->size)
|
Iris::recv.reply.invoke (file->size)
|
||||||
break
|
break
|
||||||
case Iris::String::GET_CHARS:
|
case Iris::Block::GET_ALIGN_BITS:
|
||||||
Iris::panic (0, "get_chars is not defined for init strings")
|
|
||||||
case Iris::String::GET_ALIGN_BITS:
|
|
||||||
Iris::recv.reply.invoke (PAGE_BITS)
|
Iris::recv.reply.invoke (PAGE_BITS)
|
||||||
break
|
break
|
||||||
case Iris::String::GET_BLOCK:
|
case Iris::Block::GET_BLOCK:
|
||||||
Iris::Cap reply = Iris::get_reply ()
|
Iris::Cap reply = Iris::get_reply ()
|
||||||
Iris::Page target = Iris::get_arg ()
|
Iris::Page target = Iris::get_arg ()
|
||||||
Iris::Page source = file->pages.get (Iris::recv.data[1].l >> PAGE_BITS)
|
Iris::Page source = file->pages.get (Iris::recv.data[1].l >> PAGE_BITS)
|
||||||
|
143
source/nand.ccp
143
source/nand.ccp
@ -16,21 +16,31 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
#define DELAY Iris::schedule
|
|
||||||
|
|
||||||
#include "devices.hh"
|
#include "devices.hh"
|
||||||
#define ARCH
|
#define ARCH
|
||||||
#include "arch.hh"
|
#include "arch.hh"
|
||||||
|
|
||||||
#define debug Iris::debug
|
#define debug Iris::debug
|
||||||
|
#define DELAY Iris::schedule
|
||||||
#include "nand.hh"
|
#include "nand.hh"
|
||||||
|
|
||||||
extern "C":
|
static unsigned *cache
|
||||||
extern char file_start, file_mid, file_end
|
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 ():
|
Iris::Num start ():
|
||||||
//kdebug ("starting nand operation in 10 seconds\n")
|
|
||||||
//Iris::sleep (10 * HZ)
|
|
||||||
|
|
||||||
map_emc ()
|
map_emc ()
|
||||||
map_gpio ()
|
map_gpio ()
|
||||||
|
|
||||||
@ -58,43 +68,86 @@ Iris::Num start ():
|
|||||||
|
|
||||||
reset ()
|
reset ()
|
||||||
|
|
||||||
#if 1
|
current_block = ~0
|
||||||
erase (0)
|
dirty = false
|
||||||
char *source = &file_start
|
cache = (unsigned *)0x40000000
|
||||||
unsigned a = 0
|
for unsigned p = 0; p < 1 << (block_bits - PAGE_BITS); ++p:
|
||||||
while source < &file_mid:
|
Iris::Page page = Iris::my_memory.create_page ()
|
||||||
write (a, source)
|
page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME)
|
||||||
a += 0x800
|
Iris::my_memory.map (page, (unsigned)cache + p << PAGE_BITS)
|
||||||
source += 0x800
|
Iris::free_cap (page)
|
||||||
source = &file_mid
|
Iris::Page tmp = Iris::my_memory.create_page ()
|
||||||
a = 0x4000
|
tmp.set_flags (Iris::Page::PAYING)
|
||||||
while source < &file_end:
|
unsigned *tmp_addr = (unsigned *)0x3ffff000
|
||||||
write (a, source)
|
Iris::my_memory.map (tmp, (unsigned)tmp_addr)
|
||||||
a += 0x800
|
|
||||||
source += 0x800
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char buffer[0x200]
|
Iris::Cap cap = Iris::my_receiver.create_capability (0)
|
||||||
// Send nand contents to serial port.
|
Iris::my_parent.provide_capability <Iris::WString> (cap.copy ())
|
||||||
for unsigned a = 0; a < 0x400; a += 0x200:
|
Iris::free_cap (cap)
|
||||||
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
|
|
||||||
|
|
||||||
asm volatile ("\t.set noreorder\n"
|
Iris::my_parent.init_done ()
|
||||||
"\t.globl file_start\n"
|
|
||||||
"\t.globl file_mid\n"
|
while true:
|
||||||
"\t.globl file_end\n"
|
Iris::wait ()
|
||||||
"\t.text\n"
|
switch Iris::recv.data[0].l:
|
||||||
"file_start:\n"
|
case Iris::Block::GET_SIZE:
|
||||||
"\t.incbin \"mips/nanonote/nand-boot.raw\"\n"
|
if size_bits > 32:
|
||||||
"file_mid:\n"
|
Iris::recv.reply.invoke (Iris::Num (0, 1 << (size_bits - 32)))
|
||||||
"\t.incbin \"iris-sd.raw\"\n"
|
else:
|
||||||
"file_end:\n"
|
Iris::recv.reply.invoke (1 << size_bits)
|
||||||
".set reorder")
|
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
|
||||||
|
@ -38,7 +38,7 @@ struct Partition:
|
|||||||
|
|
||||||
Iris::Num Partition::device_size
|
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):
|
static void read_sector (Iris::Num idx, Iris::Page page, unsigned size = 1 << SECTOR_BITS, unsigned offset = 0):
|
||||||
offset &= ~PAGE_MASK
|
offset &= ~PAGE_MASK
|
||||||
if size + offset > PAGE_SIZE:
|
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 ():
|
Iris::Num start ():
|
||||||
Partition::device_size = 0
|
Partition::device_size = 0
|
||||||
dev = Iris::my_parent.get_capability <Iris::WString> ()
|
dev = Iris::my_parent.get_capability <Iris::WBlock> ()
|
||||||
bits = dev.get_align_bits ()
|
bits = dev.get_align_bits ()
|
||||||
if bits > SECTOR_BITS:
|
if bits > SECTOR_BITS:
|
||||||
Iris::panic (bits, "partitioned device doesn't support 512 byte access\n")
|
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:
|
for unsigned i = 0; i < NUM_PARTITIONS; ++i:
|
||||||
partition[i].read (buffer + 0x1be + 0x10 * i)
|
partition[i].read (buffer + 0x1be + 0x10 * i)
|
||||||
cap = Iris::my_receiver.create_capability (i)
|
cap = Iris::my_receiver.create_capability (i)
|
||||||
Iris::my_parent.provide_capability <Iris::WString> (cap.copy (), i)
|
Iris::my_parent.provide_capability <Iris::WBlock> (cap.copy (), i)
|
||||||
Iris::free_cap (cap)
|
Iris::free_cap (cap)
|
||||||
|
|
||||||
page.set_flags (0, Iris::Page::PAYING | Iris::Page::FRAME)
|
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_num (Iris::recv.data[0].l)
|
||||||
//kdebug ("\n")
|
//kdebug ("\n")
|
||||||
switch Iris::recv.data[0].l:
|
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)
|
Iris::recv.reply.invoke (partition[Iris::recv.protected_data.l].size)
|
||||||
break
|
break
|
||||||
case Iris::String::GET_CHARS:
|
case Iris::Block::GET_ALIGN_BITS:
|
||||||
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:
|
|
||||||
Iris::recv.reply.invoke (SECTOR_BITS)
|
Iris::recv.reply.invoke (SECTOR_BITS)
|
||||||
break
|
break
|
||||||
case Iris::String::GET_BLOCK:
|
case Iris::Block::GET_BLOCK:
|
||||||
Iris::Cap reply = Iris::get_reply ()
|
Iris::Cap reply = Iris::get_reply ()
|
||||||
Iris::Cap arg = Iris::get_arg ()
|
Iris::Cap arg = Iris::get_arg ()
|
||||||
Iris::Num p = Iris::recv.data[1].value () & BLOCK_MASK
|
Iris::Num p = Iris::recv.data[1].value () & BLOCK_MASK
|
||||||
@ -126,9 +115,8 @@ Iris::Num start ():
|
|||||||
Iris::free_cap (reply)
|
Iris::free_cap (reply)
|
||||||
Iris::free_cap (arg)
|
Iris::free_cap (arg)
|
||||||
break
|
break
|
||||||
case Iris::WString::SET_CHARS:
|
case Iris::WBlock::SET_BLOCK:
|
||||||
case Iris::WString::SET_BLOCK:
|
|
||||||
Iris::panic (Iris::recv.data[0].l, "writing to partitions not supported yet")
|
Iris::panic (Iris::recv.data[0].l, "writing to partitions not supported yet")
|
||||||
case Iris::WString::TRUNCATE:
|
case Iris::WBlock::TRUNCATE:
|
||||||
default:
|
default:
|
||||||
Iris::panic (Iris::recv.data[0].l, "invalid request for partition handler")
|
Iris::panic (Iris::recv.data[0].l, "invalid request for partition handler")
|
||||||
|
@ -495,17 +495,14 @@ Iris::Num start ():
|
|||||||
//kdebug_num (Iris::recv.data[0].l)
|
//kdebug_num (Iris::recv.data[0].l)
|
||||||
//kdebug ("\n")
|
//kdebug ("\n")
|
||||||
switch Iris::recv.data[0].l:
|
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 ()
|
unsigned long long size = mmc.get_num_blocks () * mmc.get_read_block_size ()
|
||||||
Iris::recv.reply.invoke (size)
|
Iris::recv.reply.invoke (size)
|
||||||
break
|
break
|
||||||
case Iris::String::GET_CHARS:
|
case Iris::Block::GET_ALIGN_BITS:
|
||||||
Iris::panic (0, "get chars from mmc not supported yet")
|
|
||||||
break
|
|
||||||
case Iris::String::GET_ALIGN_BITS:
|
|
||||||
Iris::recv.reply.invoke (9)
|
Iris::recv.reply.invoke (9)
|
||||||
break
|
break
|
||||||
case Iris::String::GET_BLOCK:
|
case Iris::Block::GET_BLOCK:
|
||||||
Iris::Cap reply = Iris::get_reply ()
|
Iris::Cap reply = Iris::get_reply ()
|
||||||
Iris::Page page = Iris::get_arg ()
|
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)
|
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 (page)
|
||||||
Iris::free_cap (reply)
|
Iris::free_cap (reply)
|
||||||
break
|
break
|
||||||
case Iris::WString::SET_BLOCK:
|
case Iris::WBlock::SET_BLOCK:
|
||||||
Iris::Cap reply = Iris::get_reply ()
|
Iris::Cap reply = Iris::get_reply ()
|
||||||
Iris::Page page = Iris::get_arg ()
|
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)
|
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)
|
Iris::free_cap (reply)
|
||||||
mmc.wait_write ()
|
mmc.wait_write ()
|
||||||
break
|
break
|
||||||
case Iris::WString::SET_CHARS:
|
|
||||||
// Fall through: don't support writing single characters.
|
|
||||||
case Iris::WString::TRUNCATE:
|
case Iris::WString::TRUNCATE:
|
||||||
// Fall through: don't support resizing.
|
// Fall through: don't support resizing.
|
||||||
default:
|
default:
|
||||||
|
@ -625,12 +625,11 @@ Iris::Num start ():
|
|||||||
case FILE:
|
case FILE:
|
||||||
//kdebug ("file request\n")
|
//kdebug ("file request\n")
|
||||||
switch Iris::recv.data[0].l:
|
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:
|
if Iris::recv.data[0].h != PAGE_SIZE << 16:
|
||||||
Iris::panic (0, "unsupported get_block arguments for boot usb device driver")
|
Iris::panic (0, "unsupported get_block arguments for boot usb device driver")
|
||||||
// Fall through.
|
// Fall through.
|
||||||
case Iris::String::GET_SIZE:
|
case Iris::Block::GET_SIZE:
|
||||||
case Iris::String::GET_CHARS:
|
|
||||||
udc.send (Iris::recv.data[0].l | ((Iris::recv.data[1].l >> PAGE_BITS) << 16), Iris::recv.protected_data.h, reply, arg)
|
udc.send (Iris::recv.data[0].l | ((Iris::recv.data[1].l >> PAGE_BITS) << 16), Iris::recv.protected_data.h, reply, arg)
|
||||||
continue
|
continue
|
||||||
default:
|
default:
|
||||||
|
689
source/usb-mass-storage.ccp
Normal file
689
source/usb-mass-storage.ccp
Normal file
@ -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 <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 "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 <unsigned size> 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 <char const *> (&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 <char const *> (&device_descriptor), sizeof (device_descriptor), len)
|
||||||
|
case Device_Qualifier::Type:
|
||||||
|
//if idx != 0:
|
||||||
|
// return false
|
||||||
|
//return send (0, reinterpret_cast <char const *> (&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 <char const *> (&s_langs), sizeof (s_langs), len)
|
||||||
|
case 1:
|
||||||
|
Iris::debug ("get manufacturer descriptor\n")
|
||||||
|
return send (0, reinterpret_cast <char const *> (&s_manufacturer), sizeof (s_manufacturer), len)
|
||||||
|
case 2:
|
||||||
|
Iris::debug ("get product descriptor\n")
|
||||||
|
return send (0, reinterpret_cast <char const *> (&s_product), sizeof (s_product), len)
|
||||||
|
case 3:
|
||||||
|
Iris::debug ("get serial descriptor\n")
|
||||||
|
return send (0, reinterpret_cast <char const *> (&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 <Iris::WBlock> ()
|
||||||
|
udc.init (nand)
|
||||||
|
while true:
|
||||||
|
Iris::register_interrupt (IRQ_UDC)
|
||||||
|
Iris::wait ()
|
||||||
|
udc.interrupt ()
|
Loading…
Reference in New Issue
Block a user