#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) -> TX (send) -> RX (receive packets) TX: transmitting data. IN interrupt: host received data; send more. -> TX (more to send) RX: receiving data. OUT interrupt: host sent data; handle. -> RX (more to receive) -> IDLE (done receiving; send csw) #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 { TEST_UNIT_READY = 0x00, REQUEST_SENSE = 0x03, FORMAT_UNIT = 0x04, INQUIRY = 0x12, RESERVE6 = 0x16, RELEASE6 = 0x17, SEND_DIAGNOSTIC = 0x1d, READ_CAPACITY = 0x25, READ10 = 0x28, WRITE10 = 0x2a, RESERVE10 = 0x56, RELEASE10 = 0x57 }; } __attribute__ ((packed)) 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 reset () void irq_in0 () void handle_rx () void handle_tx () void handle_cbw () void send_csw () unsigned big_endian (unsigned src) bool handle_interrupt (bool usb, bool in) void stall (unsigned error) bool stalling enum State: IDLE TX RX SENT_CSW STALL State state unsigned residue unsigned status unsigned tag unsigned data_done, lba, blocks unsigned block_bits Iris::WBlock block Iris::Page buffer_page // A random address to map the buffer. static unsigned const buffer = 0x15000 public: void init (Iris::WBlock b) void log (unsigned c) void interrupt () void send (unsigned ep, char const *data, unsigned length, unsigned maxlength) void send_padded (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::reset (): // Reset. UDC_TESTMODE = 0 configuration = 0 state = IDLE status = 0 residue = 0 // enable interrupt on bus reset. UDC_INTRUSBE = UDC_INTR_RESET // enable interrupts on endpoint 0 and in endpoint 2 UDC_INTRINE = 1 << 0 | 1 << 2 // and on out endpoint 1. UDC_INTROUTE = 1 << 1 // exit suspend mode by reading the interrupt register. unsigned i = UDC_INTRUSB // reset all pending endpoint interrupts. i = UDC_INTRIN i = UDC_INTROUT UDC_INDEX = 1 UDC_OUTMAXP = max_packet_size_bulk // Do this twice to flush a double-buffered fifo completely. UDC_OUTCSR |= UDC_OUTCSR_CDT | UDC_OUTCSR_FF UDC_OUTCSR |= UDC_OUTCSR_CDT | UDC_OUTCSR_FF UDC_INDEX = 2 UDC_INMAXP = max_packet_size_bulk UDC_INCSR = (UDC_INCSRH_MODE << 8) | UDC_INCSR_CDT | UDC_INCSR_FF UDC_INCSR = (UDC_INCSRH_MODE << 8) | UDC_INCSR_CDT | UDC_INCSR_FF //Iris::debug ("usb reset\n") void Udc::init (Iris::WBlock b): block = b block_bits = block.get_align_bits () // Set up the buffer page. buffer_page = Iris::my_memory.create_page () buffer_page.set_flags (Iris::Page::PAYING) Iris::my_memory.map (buffer_page, buffer) // 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 reset () // Wait a while. Iris::sleep (HZ / 10) // Connect to the host. UDC_POWER = UDC_POWER_SOFTCONN void 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_num (((unsigned *)data)[i / 4], 8) //kdebug (" ") for ; i < length; ++i: UDC_FIFO8 (ep) = data[i] //kdebug_num (data[i], 2) //kdebug (" ") void Udc::send_padded (char const *data, unsigned length, unsigned maxlength): UDC_INDEX = 2 unsigned len = length < maxlength ? length : maxlength residue = maxlength - len len = (len + 3) & ~3 send (2, data, len, maxlength) //Iris::debug ("sending %x, valid %x\n", maxlength, len) while len + 3 < maxlength: UDC_FIFO (2) = 0 len += 4 //kdebug_char ('-') while len < maxlength: UDC_FIFO8 (2) = 0 ++len //kdebug_char ('.') UDC_INCSR |= UDC_INCSR_INPKTRDY blocks = 0 state = TX unsigned Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): switch type: case Configuration::Type: if idx != 0: return false //Iris::debug ("get config descriptor\n") send (0, reinterpret_cast (&config_descriptor), sizeof (config_descriptor), len) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND case Device::Type: if idx != 0: return false //Iris::debug ("get device descriptor\n") send (0, reinterpret_cast (&device_descriptor), sizeof (device_descriptor), len) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND case Device_Qualifier::Type: //if idx != 0: // return false //send (0, reinterpret_cast (&device_qualifier_descriptor), sizeof (device_qualifier_descriptor), len) //return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND //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") send (0, reinterpret_cast (&s_langs), sizeof (s_langs), len) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND case 1: //Iris::debug ("get manufacturer descriptor\n") send (0, reinterpret_cast (&s_manufacturer), sizeof (s_manufacturer), len) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND case 2: //Iris::debug ("get product descriptor\n") send (0, reinterpret_cast (&s_product), sizeof (s_product), len) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND case 3: //Iris::debug ("get serial descriptor\n") send (0, reinterpret_cast (&s_serial), sizeof (s_serial), len) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND default: return ~0 default: return ~0 unsigned Udc::handle_setup (Setup *s): switch s->request_type: case STANDARD_TO_DEVICE: UDC_INDEX = 0 UDC_CSR0 = UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY 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: UDC_INDEX = 0 UDC_CSR0 = UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY switch s->request: case GET_STATUS: Iris::debug ("get status\t") send (0, "\0\0", 2, s->length) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND case GET_DESCRIPTOR: return get_descriptor ((s->value >> 8) & 0xff, s->value & 0xff, s->length) case GET_CONFIGURATION: Iris::debug ("get configuration\t") send (0, &configuration, 1, s->length) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND case GET_INTERFACE: Iris::debug ("get interface\t") send (0, "\0", 1, s->length) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND 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 & ~UDC_INCSR_SENDSTALL) | UDC_INCSR_CDT stalling = false send_csw () break case 1: //Iris::panic (0, "halt reset on out endpoint") UDC_INDEX = 1 UDC_OUTCSR |= UDC_OUTCSR_CDT break default: return ~0 return UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY default: return ~0 default: return ~0 case CLASS_FROM_INTERFACE: UDC_INDEX = 0 UDC_CSR0 = UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY switch s->request: case GET_MAX_LUN: //Iris::debug ("get max lun\t") send (0, "\0", 1, s->length) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND default: return ~0 case CLASS_TO_INTERFACE: UDC_INDEX = 0 UDC_CSR0 = UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY 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_in0 (): // Interrupt on endpoint 0. UDC_INDEX = 0 unsigned csr = UDC_CSR0 if csr & UDC_CSR0_SENTSTALL: UDC_CSR0 = 0 //Iris::debug ("stall 0 done\t") if csr & UDC_CSR0_SETUPEND: UDC_CSR0 = UDC_CSR0_SVDSETUPEND Iris::debug ("setup aborted\t") if !(csr & UDC_CSR0_OUTPKTRDY): //Iris::debug ("no packet 0: %x\n", csr) return UDC_INDEX = 0 union { unsigned d[2]; Setup s; } packet packet.d[0] = UDC_FIFO (0) packet.d[1] = UDC_FIFO (0) if !(packet.s.request_type & 0x80) && packet.s.length > 0: // More data will follow; unsupported. Iris::debug ("packet on ep0 too long\n") UDC_CSR0 = UDC_CSR0_SENDSTALL return unsigned ret = handle_setup (&packet.s) UDC_INDEX = 0 if ret == ~0: //Iris::debug ("failed setup: %x %x %x %x %x\n", packet.s.request_type, packet.s.request, packet.s.index, packet.s.length, packet.s.value) UDC_CSR0 = UDC_CSR0_SENDSTALL return if ret: UDC_CSR0 = ret //kdebug ("done in0\n") void Udc::send_csw (): UDC_INDEX = 2 UDC_FIFO (2) = 0x53425355 UDC_FIFO (2) = tag UDC_FIFO (2) = residue UDC_FIFO8 (2) = status UDC_INCSR |= UDC_INCSR_INPKTRDY state = SENT_CSW status = 0 residue = 0 //kdebug ("sent csw\n") void Udc::stall (unsigned error): if stalling: Iris::debug ("already stalling!\n") UDC_INCSR |= UDC_INCSR_SENDSTALL stalling = true state = STALL unsigned Udc::big_endian (unsigned src): return src >> 24 | src >> 8 & 0xff00 | src << 8 & 0xff0000 | src << 24 void Udc::handle_rx (): buffer_page.set_flags (Iris::Page::FRAME) UDC_INDEX = 1 if !(UDC_OUTCSR & UDC_OUTCSR_OUTPKTRDY): Iris::panic (0, "no packet ready after out interrupt during rx") if UDC_OUTCOUNT != max_packet_size_bulk: Iris::panic (UDC_OUTCOUNT, "invalid packet size during rx") for unsigned t = 0; t < max_packet_size_bulk; t += 4: ((unsigned *)buffer)[(t + data_done) >> 2] = UDC_FIFO (1) UDC_OUTCSR &= ~UDC_OUTCSR_OUTPKTRDY data_done += max_packet_size_bulk if data_done == 1 << block_bits: //Iris::debug ("writing block %x\n", lba) block.set_block (lba << block_bits, buffer_page, 1 << block_bits) data_done = 0 --blocks ++lba if blocks == 0: send_csw () return void Udc::handle_tx (): if blocks == 0: send_csw () return if data_done == 0: // read block lba. buffer_page.set_flags (Iris::Page::FRAME) block.get_block (lba << block_bits, 1 << block_bits, 0, buffer_page) UDC_INDEX = 2 for unsigned t = 0; t < max_packet_size_bulk; t += 4: UDC_FIFO (2) = ((unsigned *)buffer)[(data_done + t) >> 2] data_done += max_packet_size_bulk if data_done == 1 << block_bits: data_done = 0 ++lba --blocks UDC_INCSR |= UDC_INCSR_INPKTRDY void Udc::handle_cbw (): UDC_INDEX = 1 unsigned csr = UDC_OUTCSR unsigned size = UDC_OUTCOUNT if csr & UDC_OUTCSR_SENDSTALL: // When stalling, do nothing else. //kdebug ("not responding to out during stall\n") UDC_OUTCSR = csr & ~UDC_OUTCSR_SENTSTALL return if !(csr & UDC_OUTCSR_OUTPKTRDY): // No packet; this shouldn't happen. Iris::panic (0, "no packet") return // expect a new cbw. if size != 31: Iris::debug ("count %d != 31\n", size) stall (2) return union Cbw: unsigned u[8] char b[32] CBW cbw Cbw cbw for unsigned i = 0; i < 7; ++i: cbw.u[i] = UDC_FIFO (1) for unsigned i = 28; i < 31; ++i: cbw.b[i] = UDC_FIFO8 (1) UDC_OUTCSR = csr & ~UDC_OUTCSR_OUTPKTRDY tag = cbw.cbw.tag if cbw.cbw.sig != 0x43425355 || cbw.cbw.lun != 0 || cbw.cbw.size == 0 || cbw.cbw.size > 16: Iris::debug ("wrong cbw: sig %x lun %d size %d\n", cbw.cbw.sig, cbw.cbw.lun, cbw.cbw.size) stall (2) return //kdebug ("bulk cbw\t") #if 0 Iris::debug ("cbw:") for unsigned i = 0; i < cbw.cbw.size; ++i: kdebug_char (' ') kdebug_num (cbw.cbw.data[i], 2) Iris::debug ("\n") #endif UDC_INDEX = 2 bool to_host = cbw.cbw.flags & 0x80 switch cbw.cbw.data[0]: case CBW::TEST_UNIT_READY: if to_host || cbw.cbw.length != 0: stall (2) return //Iris::debug ("sending ready response\t") send_csw () break case CBW::REQUEST_SENSE: //Iris::debug ("sense requested\n") send_padded ("\xf0\x00\x05\x00\x00\x00\x00\x00", 8, cbw.cbw.length) break case CBW::FORMAT_UNIT: Iris::panic (0, "FORMAT_UNIT isn't implemented") case CBW::INQUIRY: if !to_host: stall (2) return //Iris::debug ("sending inquiry response\t") // TODO: find out why these bytes are messed up. send_padded ("\x00\x00\x04\x02\x1f\x00\x00\x00shevek iris usb stick \x00\x00\x04\x02", 36, cbw.cbw.length) break case CBW::RESERVE6: Iris::panic (0, "RESERVE6 isn't implemented") case CBW::RELEASE6: Iris::panic (0, "RELEASE6 isn't implemented") case CBW::SEND_DIAGNOSTIC: Iris::panic (0, "SEND_DIAGNOSTIC isn't implemented") case CBW::READ_CAPACITY: if !to_host: stall (2) 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\t", capacity[0], capacity[1]) send_padded ((char *)capacity, 8, cbw.cbw.length) break case CBW::READ10: if !to_host: stall (2) return lba = cbw.cbw.data[2] << 24 | cbw.cbw.data[3] << 16 | cbw.cbw.data[4] << 8 | cbw.cbw.data[5] blocks = cbw.cbw.data[7] << 8 | cbw.cbw.data[8] data_done = 0 state = TX handle_tx () break case CBW::WRITE10: if to_host: stall (2) return lba = cbw.cbw.data[2] << 24 | cbw.cbw.data[3] << 16 | cbw.cbw.data[4] << 8 | cbw.cbw.data[5] blocks = cbw.cbw.data[7] << 8 | cbw.cbw.data[8] if blocks == 0: send_csw () break state = RX data_done = 0 buffer_page.set_flags (Iris::Page::FRAME) break case CBW::RESERVE10: Iris::panic (0, "RESERVE10 isn't implemented") case CBW::RELEASE10: Iris::panic (0, "RELEASE10 isn't implemented") default: #if 0 Iris::debug ("unknown cbw:") for unsigned i = 0; i < cbw.cbw.size; ++i: kdebug_char (' ') kdebug_num (cbw.cbw.data[i], 2) Iris::debug ("\n") #endif residue = cbw.cbw.length stall (1) return void Udc::interrupt (): //Iris::debug ("interrupt, state = %d\n", state) while true: bool action = false unsigned usb = UDC_INTRUSB unsigned in = UDC_INTRIN unsigned out = UDC_INTROUT if usb & 4: //Iris::debug ("reset\n") reset () action = true if state == STALL && in & 4: // This must be handled here, because the state can be changed by the control request. //Iris::debug ("stalling\n") in &= ~4 if in & 1: //Iris::debug ("control request\n") irq_in0 () action = true if in & 4: //Iris::debug ("in request\n") // Notification of sent packet (or stall, but we don't do that on the in endpoint). switch state: case SENT_CSW: // csw received. state = IDLE break case TX: handle_tx () break default: Iris::panic (state, "invalid state for data send") stall (2) break action = true if out & 2: //Iris::debug ("out request\n") switch state: case IDLE: handle_cbw () break case RX: handle_rx () break default: stall (2) Iris::panic (0, "invalid state for data receive") break action = true if !action: // No more interrupts to handle; this is normal, because we're looping until this happens. //Iris::debug ("irq done\n") return 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 ()