#pypp 0 // Iris: micro-kernel for a capability-based operating system. // source/usb-mass-storage.ccp: USB mass storage device driver. // Copyright 2009-2010 Bas Wijnen // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "iris.hh" #include "devices.hh" #define ARCH #include "arch.hh" #if 0 States and expected interrupts: IDLE: after reset or csw. IN interrupt: csw received, do nothing. OUT interrupt: cbw; handle -> IDLE (no data; csw sent) -> CSW (data sent in one packet) -> TX (more than one packet to send) -> RX (receive packets) TX: transmitting data. IN interrupt: host received data; send more. -> TX (more to send) -> CSW (last data has now been sent) RX: receiving data. OUT interrupt: host sent data; handle. -> RX (more to receive) -> IDLE (done receiving; send csw) CSW: waiting to transmit csw. IN interrupt: TX is done; send csw -> IDLE #endif extern "C": void *memset (char *s, int c, unsigned long n): Iris::debug ("memset called: %x %x->%x\n", s, n, c) for unsigned i = 0; i < n; ++i: s[i] = c return s class Udc: typedef unsigned char u8 typedef unsigned short u16 typedef unsigned int u32 typedef u8 string // The ugly stuff is because pypp doesn't support __attribute__. /**/struct Setup { u8 request_type; u8 request; u16 value; u16 index; u16 length; } __attribute__ ((packed)) /**/struct Device { static u8 const Type = 1; u8 length; u8 type; u16 usb_version; u8 dev_class; u8 subclass; u8 protocol; u8 max_packet_size0; u16 vendor; u16 product; u16 dev_version; string s_manufacturer; string s_product; string s_serial; u8 num_configurations; } __attribute__ ((packed)) /**/struct Configuration { static u8 const Type = 2; u8 length; u8 type; u16 total_length; u8 num_interfaces; u8 configuration_value; u8 configuration; u8 attributes; u8 max_power; } __attribute__ ((packed)) /**/struct Interface { static u8 const Type = 4; u8 length; u8 type; u8 interface; u8 alternate; u8 num_endpoints; u8 iface_class; u8 subclass; u8 protocol; string name; } __attribute__ ((packed)) /**/struct Endpoint { static u8 const Type = 5; u8 length; u8 type; u8 address; u8 attributes; u16 max_packet_size; u8 interval; } __attribute__ ((packed)) /**/struct Device_Qualifier { static u8 const Type = 6; u8 length; u8 type; u16 version; u8 dev_class; u8 subclass; u8 protocol; u8 max_packet_size0; u8 num_configurations; u8 reserved; } __attribute__ ((packed)) /**/struct Langs { static u8 const Type = 3; u8 length; u8 type; u8 lang; } __attribute__ ((packed)) template struct String { static u8 const Type = 3; u8 length; u8 type; u16 data[size]; } __attribute__ ((packed)) /**/struct CBW { u32 sig; u32 tag; u32 length; u8 flags; u8 lun; u8 size; u8 data[16]; enum Code { INQUIRY = 0x12, TEST_UNIT_READY = 0x00, READ_CAPACITY = 0x25, REQUEST_SENSE = 0x03 }; } __attribute__ ((packed)) 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_out () void send_csw () unsigned big_endian (unsigned src) bool handle_interrupt (bool usb, bool in) void stall (unsigned error) bool stalling[3] unsigned residue unsigned status unsigned tag unsigned block_bits Iris::WBlock block 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::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 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 // Set max packet sizes. UDC_INDEX = 1 UDC_OUTMAXP = max_packet_size_bulk UDC_INDEX = 2 UDC_INMAXP = max_packet_size_bulk // Wait a while. Iris::sleep (HZ / 10) // Connect to the host. UDC_POWER = UDC_POWER_SOFTCONN // Initialize cbw state status = 0 residue = 0 void Udc::send (unsigned ep, char const *data, unsigned length, unsigned maxlength): if maxlength < length: length = maxlength 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): unsigned len = length < maxlength ? length : maxlength residue = maxlength - len len = (len + 3) & ~3 send (2, data, len, maxlength) while len + 3 < maxlength: UDC_FIFO (2) = 0 len += 4 kdebug_char ('-') while len < maxlength: UDC_FIFO8 (2) = 0 ++len kdebug_char ('.') UDC_INDEX = 2 UDC_INCSR |= UDC_INCSR_INPKTRDY while true: Iris::register_interrupt (IRQ_UDC) Iris::wait_for_interrupt (IRQ_UDC) kdebug ("interrupt pad\n") unsigned usb = UDC_INTRUSB unsigned in = UDC_INTRIN unsigned out = UDC_INTROUT if usb & 4 || in & 1: kdebug ("general interrupt pad\n") if !handle_interrupt (usb & 4, in & 1): return if out & 2: Iris::panic (0, "out interrupt while waiting for in") if in & 4: break kdebug ("done interrupt pad\n") unsigned Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): 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: 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") 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\n") send (0, &configuration, 1, s->length) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND case GET_INTERFACE: Iris::debug ("get interface\n") 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_SENDSTALL | UDC_INCSR_SENTSTALL) stalling[2] = false return 0 case 1: Iris::debug ("out ep halt reset\n") UDC_INDEX = 1 UDC_OUTCSR &= ~(UDC_OUTCSR_SENDSTALL | UDC_OUTCSR_SENTSTALL) stalling[1] = false 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") send (0, "\0", 1, s->length) return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND default: return ~0 case CLASS_TO_INTERFACE: switch s->request: case BULK_ONLY_RESET: Iris::debug ("bulk reset\n") 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 // 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_INCSR |= UDC_INCSR_CDT Iris::debug ("usb reset\n") void Udc::irq_in0 (): // Interrupt on endpoint 0. UDC_INDEX = 0 unsigned csr = UDC_CSR0 if csr & UDC_CSR0_SENTSTALL: csr &= ~(UDC_CSR0_SENTSTALL | UDC_CSR0_SENDSTALL) if csr & UDC_CSR0_SETUPEND: csr |= UDC_CSR0_SVDSETUPEND 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_OUTCSR |= UDC_OUTCSR_CDT UDC_INDEX = 2 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 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 status = 0 residue = 0 while true: Iris::register_interrupt (IRQ_UDC) Iris::wait_for_interrupt (IRQ_UDC) unsigned usb = UDC_INTRUSB unsigned in = UDC_INTRIN if usb & 4 || in & 1: if !handle_interrupt (usb & 4, in & 1): return if in & 4: break unsigned out = UDC_INTROUT if out & 2: Iris::panic (0, "out interrupt while waiting for in after csw") kdebug ("sent csw\n") void Udc::stall (unsigned error): unsigned index = UDC_INDEX if index == 1: UDC_OUTCSR |= UDC_OUTCSR_SENDSTALL else: UDC_INCSR |= UDC_INCSR_SENDSTALL stalling[index] = true kdebug ("stalling\n") while stalling[index]: Iris::register_interrupt (IRQ_UDC) Iris::wait_for_interrupt (IRQ_UDC) kdebug ("stalling interrupt\n") unsigned usb = UDC_INTRUSB unsigned in = UDC_INTRIN if usb & 4 || in & 1: kdebug ("stuff\n") if !handle_interrupt (usb & 4, in & 1): return if in & 4: kdebug ("in interrupt ignored while waiting for stall reset\n") unsigned out = UDC_INTROUT if out & 2: Iris::panic (0, "out interrupt while waiting for stall reset") kdebug ("no stuff\n") UDC_INDEX = index if index == 2: status = error send_csw () unsigned Udc::big_endian (unsigned src): return src >> 24 | src >> 8 & 0xff00 | src << 8 & 0xff0000 | src << 24 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. kdebug ("not responding to out during stall\n") UDC_OUTCSR &= ~UDC_OUTCSR_SENTSTALL return // expect a new cbw. if size != 31: Iris::debug ("count %d != 31\n", size) stall (2) return union Cbw: unsigned u[8] CBW cbw Cbw cbw for unsigned i = 0; i < 8; ++i: cbw.u[i] = UDC_FIFO (1) tag = cbw.cbw.tag if cbw.cbw.sig != 0x43425355 || cbw.cbw.lun != 0 || cbw.cbw.size == 0 || cbw.cbw.size > 16: Iris::debug ("sig %x lun %d size %d\n", cbw.cbw.sig, cbw.cbw.lun, cbw.cbw.size) stall (2) return UDC_OUTCSR = csr & ~UDC_OUTCSR_OUTPKTRDY kdebug ("bulk cbw\n") UDC_INDEX = 2 bool to_host = cbw.cbw.flags & 0x80 switch cbw.cbw.data[0]: case CBW::INQUIRY: if !to_host: stall (2) break Iris::debug ("sending inquiry response\n") send_padded ("\x00\x00\x04\x02\x1f\x00\x00\x00shevek iris usb stick 0 ", 36, cbw.cbw.length) send_csw () break case CBW::TEST_UNIT_READY: if to_host || cbw.cbw.length != 0: stall (2) return Iris::debug ("sending ready response\n") send_csw () break case CBW::READ_CAPACITY: if !to_host: stall (2) break 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 ((char *)capacity, 8, cbw.cbw.length) 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) send_csw () break 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") residue = cbw.cbw.length stall (1) break // TODO. bool Udc::handle_interrupt (bool usb, bool in): if usb: Iris::debug ("usb\t") // reset. irq_usb () return false if in: Iris::debug ("control\t") // control request irq_in0 () return true void Udc::interrupt (): 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 in & 4: Iris::debug ("ignoring data request during idle\n") handle_interrupt (usb & 4, in & 1) if out & 2: irq_out () Iris::Num start (): map_udc () map_gpio () map_cpm () Udc udc Iris::WBlock nand = Iris::my_parent.get_capability () udc.init (nand) while true: Iris::register_interrupt (IRQ_UDC) Iris::wait () udc.interrupt ()