#pypp 0 // Iris: micro-kernel for a capability-based operating system. // source/usb-mass-storage.ccp: USB mass storage device driver. // Copyright 2009-2010 Bas Wijnen // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "iris.hh" #include "devices.hh" #define ARCH #include "arch.hh" #if 0 States and expected interrupts: IDLE: after reset or csw. IN interrupt: csw received, do nothing. OUT interrupt: cbw; handle -> IDLE (no data; csw sent) -> CSW (data sent in one packet) -> TX (more than one packet to send) -> RX (receive packets) TX: transmitting data. IN interrupt: host received data; send more. -> TX (more to send) -> CSW (last data has now been sent) RX: receiving data. OUT interrupt: host sent data; handle. -> RX (more to receive) -> IDLE (done receiving; send csw) CSW: waiting to transmit csw. IN interrupt: TX is done; send csw -> IDLE #endif extern "C": void *memset (char *s, int c, unsigned long n): Iris::debug ("memset called: %x %x->%x\n", s, n, c) for unsigned i = 0; i < n; ++i: s[i] = c return s class Udc: typedef unsigned char u8 typedef unsigned short u16 typedef unsigned int u32 typedef u8 string // The ugly stuff is because pypp doesn't support __attribute__. /**/struct Setup { u8 request_type; u8 request; u16 value; u16 index; u16 length; } __attribute__ ((packed)) /**/struct Device { static u8 const Type = 1; u8 length; u8 type; u16 usb_version; u8 dev_class; u8 subclass; u8 protocol; u8 max_packet_size0; u16 vendor; u16 product; u16 dev_version; string s_manufacturer; string s_product; string s_serial; u8 num_configurations; } __attribute__ ((packed)) /**/struct Configuration { static u8 const Type = 2; u8 length; u8 type; u16 total_length; u8 num_interfaces; u8 configuration_value; u8 configuration; u8 attributes; u8 max_power; } __attribute__ ((packed)) /**/struct Interface { static u8 const Type = 4; u8 length; u8 type; u8 interface; u8 alternate; u8 num_endpoints; u8 iface_class; u8 subclass; u8 protocol; string name; } __attribute__ ((packed)) /**/struct Endpoint { static u8 const Type = 5; u8 length; u8 type; u8 address; u8 attributes; u16 max_packet_size; u8 interval; } __attribute__ ((packed)) /**/struct Device_Qualifier { static u8 const Type = 6; u8 length; u8 type; u16 version; u8 dev_class; u8 subclass; u8 protocol; u8 max_packet_size0; u8 num_configurations; u8 reserved; } __attribute__ ((packed)) /**/struct Langs { static u8 const Type = 3; u8 length; u8 type; u8 lang; } __attribute__ ((packed)) template struct String { static u8 const Type = 3; u8 length; u8 type; u16 data[size]; } __attribute__ ((packed)) /**/struct CBW { u32 sig; u32 tag; u32 length; u8 flags; u8 lun; u8 size; u8 data[16]; enum Code { INQUIRY = 0x12, TEST_UNIT_READY = 0x00, READ_CAPACITY = 0x25, REQUEST_SENSE = 0x03 }; } __attribute__ ((packed)) union Cbw: unsigned u[8] CBW cbw static unsigned const max_packet_size0 = 64 static unsigned const max_packet_size_bulk = 64 enum Requests: GET_STATUS = 0 CLEAR_FEATURE = 1 SET_FEATURE = 3 SET_ADDRESS = 5 GET_DESCRIPTOR = 6 SET_DESCRIPTOR = 7 GET_CONFIGURATION = 8 SET_CONFIGURATION = 9 GET_INTERFACE = 10 SET_INTERFACE = 11 SYNCH_FRAME = 12 enum Storage_requests: BULK_ONLY_RESET = 0xff GET_MAX_LUN = 0xfe enum Request_types: STANDARD_TO_DEVICE = 0 CLASS_TO_DEVICE = 0x20 VENDOR_TO_DEVICE = 0x40 STANDARD_TO_INTERFACE = 1 CLASS_TO_INTERFACE = 0x21 VENDOR_TO_INTERFACE = 0x41 STANDARD_TO_ENDPOINT = 2 CLASS_TO_ENDPOINT = 0x22 VENDOR_TO_ENDPOINT = 0x42 STANDARD_FROM_DEVICE = 0x80 CLASS_FROM_DEVICE = 0xa0 VENDOR_FROM_DEVICE = 0xc0 STANDARD_FROM_INTERFACE = 0x81 CLASS_FROM_INTERFACE = 0xa1 VENDOR_FROM_INTERFACE = 0xc1 STANDARD_FROM_ENDPOINT = 0x82 CLASS_FROM_ENDPOINT = 0xa2 VENDOR_FROM_ENDPOINT = 0xc2 enum Endpoint_types: CONTROL = 0 ISOCHRONOUS = 1 BULK = 2 INTERRUPT = 3 enum Endpoint_features: ENDPOINT_HALT = 0 /**/struct my_config { Configuration config; Interface interface; Endpoint endpoint[2]; } __attribute__ ((packed)) static Device device_descriptor //static Device_Qualifier device_qualifier_descriptor static my_config config_descriptor; //, other_config_descriptor static String <1> s_langs static String <6> s_manufacturer static String <16> s_product static String <12> s_serial char configuration unsigned get_descriptor (unsigned type, unsigned idx, unsigned len) unsigned handle_setup (Setup *s) void irq_usb () void irq_in0 () void irq_in2 () void irq_out () void send_csw () void parse_cbw () unsigned big_endian (unsigned src) enum State: IDLE IDLE_WAIT RX TX CSW SEND_CSW State state Cbw cbw unsigned residue unsigned status unsigned block_bits Iris::WBlock block public: void init (Iris::WBlock b) void log (unsigned c) void interrupt () unsigned send (unsigned ep, char const *data, unsigned length, unsigned maxlength) unsigned send_padded (unsigned ep, char const *data, unsigned length, unsigned maxlength) Udc::Device Udc::device_descriptor Udc::my_config Udc::config_descriptor Udc::String <1> Udc::s_langs Udc::String <6> Udc::s_manufacturer Udc::String <16> Udc::s_product Udc::String <12> Udc::s_serial void Udc::init (Iris::WBlock b): block = b block_bits = block.get_align_bits () // Initialize the globals. My method of compiling doesn't handle global constructors. device_descriptor = (Device){ sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0xfffe, 0x0002, 0x100, 1, 2, 3, 1 } config_descriptor = (my_config){ (Configuration){ sizeof (Configuration), Configuration::Type, sizeof (my_config), 1, 1, 0, 0xc0, 30 }, (Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0x8, 0x6, 0x50, 0 }, { (Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 }, (Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x82, BULK, max_packet_size_bulk, 0 } } } s_langs = (String <1>){ sizeof (String <1>), String <1>::Type, { 0x0409 } } s_manufacturer = (String <6>){ sizeof (String <6>), String <6>::Type, { 's', 'h', 'e', 'v', 'e', 'k' } } s_product = (String <16>){ sizeof (String <16>), String <16>::Type, { 'I', 'r', 'i', 's', ' ', 'o', 'n', ' ', 'N', 'a', 'n', 'o', 'N', 'o', 't', 'e' } } s_serial = (String <12>){ sizeof (String <12>), String <12>::Type, { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B' } } cpm_start_udc () // Disconnect from the bus and don't try to get high-speed. UDC_POWER = 0 UDC_TESTMODE = 0 UDC_INDEX = 0 configuration = 0 // exit suspend mode by reading the interrupt register. unsigned i = UDC_INTRUSB // reset all pending endpoint interrupts. i = UDC_INTRIN i = UDC_INTROUT // enable interrupt on bus reset. UDC_INTRUSBE = UDC_INTR_RESET // enable interrupt on control endpoint. UDC_INTRINE = 1 << 0 // Wait a while. Iris::sleep (HZ / 10) // Connect to the host. UDC_POWER = UDC_POWER_SOFTCONN // Initialize cbw state state = IDLE status = 0 residue = 0 unsigned Udc::send (unsigned ep, char const *data, unsigned length, unsigned maxlength): if maxlength < length: length = maxlength unsigned i for i = 0; (length - i & ~3) > 0 && i < length; i += 4: UDC_FIFO (ep) = ((unsigned *)data)[i / 4] kdebug_char ('#') for ; i < length; ++i: UDC_FIFO8 (ep) = data[i] kdebug_char ('+') return ep == 0 ? UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND : 0 unsigned Udc::send_padded (unsigned ep, char const *data, unsigned length, unsigned maxlength): unsigned len = length < maxlength ? length : maxlength residue = maxlength - len len = (len + 3) & ~3 send (ep, data, len, maxlength) while len + 3 < maxlength: UDC_FIFO (ep) = 0 len += 4 kdebug_char ('-') while len < maxlength: UDC_FIFO8 (ep) = 0 ++len kdebug_char ('.') unsigned Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): switch type: case Configuration::Type: if idx != 0: return false Iris::debug ("get config descriptor\n") return send (0, reinterpret_cast (&config_descriptor), sizeof (config_descriptor), len) case Device::Type: if idx != 0: return false Iris::debug ("get device descriptor\n") return send (0, reinterpret_cast (&device_descriptor), sizeof (device_descriptor), len) case Device_Qualifier::Type: //if idx != 0: // return false //return send (0, reinterpret_cast (&device_qualifier_descriptor), sizeof (device_qualifier_descriptor), len) //break return ~0 // The 6 is an arbitrary number, except that String <6> is instantiated already. case String <6>::Type: switch idx: case 0: Iris::debug ("get language descriptor\n") return send (0, reinterpret_cast (&s_langs), sizeof (s_langs), len) case 1: Iris::debug ("get manufacturer descriptor\n") return send (0, reinterpret_cast (&s_manufacturer), sizeof (s_manufacturer), len) case 2: Iris::debug ("get product descriptor\n") return send (0, reinterpret_cast (&s_product), sizeof (s_product), len) case 3: Iris::debug ("get serial descriptor\n") return send (0, reinterpret_cast (&s_serial), sizeof (s_serial), len) default: return ~0 default: return ~0 unsigned Udc::handle_setup (Setup *s): switch s->request_type: case STANDARD_TO_DEVICE: switch s->request: case SET_ADDRESS: UDC_FADDR = s->value Iris::debug ("set address %x\n", s->value) return 0 case SET_CONFIGURATION: if s->value >= 2: return ~0 configuration = s->value Iris::debug ("set configuration %x\n", s->value) return 0 case SET_INTERFACE: if s->value != 0: return ~0 Iris::debug ("set interface %x\n", s->value) return 0 default: return ~0 case STANDARD_FROM_DEVICE: switch s->request: case GET_STATUS: Iris::debug ("get status\n") return send (0, "\0\0", 2, s->length) case GET_DESCRIPTOR: return get_descriptor ((s->value >> 8) & 0xff, s->value & 0xff, s->length) case GET_CONFIGURATION: Iris::debug ("get configuration\n") return send (0, &configuration, 1, s->length) case GET_INTERFACE: Iris::debug ("get interface\n") return send (0, "\0", 1, s->length) default: return ~0 case STANDARD_TO_ENDPOINT: switch s->request: case CLEAR_FEATURE: switch s->value: case ENDPOINT_HALT: switch s->index: case 0x82: Iris::debug ("in ep halt reset\n") UDC_INDEX = 2 UDC_INCSR &= ~(UDC_INCSR_SENDSTALL | UDC_INCSR_SENTSTALL) if state == CSW: state = SEND_CSW return 0 case 1: Iris::debug ("out ep halt reset\n") UDC_INDEX = 1 UDC_OUTCSR &= ~(UDC_OUTCSR_SENDSTALL | UDC_OUTCSR_SENTSTALL) if state == CSW: state = SEND_CSW return 0 default: return ~0 default: return ~0 default: return ~0 case CLASS_FROM_INTERFACE: switch s->request: case GET_MAX_LUN: Iris::debug ("get max lun\n") return send (0, "\0", 1, s->length) default: return ~0 case CLASS_TO_INTERFACE: switch s->request: case BULK_ONLY_RESET: Iris::debug ("bulk reset\n") state = IDLE return 0 default: return ~0 default: Iris::debug ("request: %x %x %x %x %x\n", s->request_type, s->request, s->index, s->length, s->value) return ~0 void Udc::irq_usb (): // Reset. // enable interrupts on endpoint 0 and in endpoint 2 UDC_INTRINE = 1 << 0 | 1 << 2 // and on out endpoint 1. UDC_INTROUTE = 1 << 1 UDC_INDEX = 1 UDC_OUTMAXP = max_packet_size_bulk // Do this twice to flush a double-buffered fifo completely. UDC_OUTCSR |= UDC_OUTCSR_CDT | UDC_OUTCSR_FF UDC_OUTCSR |= UDC_OUTCSR_CDT | UDC_OUTCSR_FF UDC_INDEX = 2 UDC_INMAXP = max_packet_size_bulk UDC_INCSR |= UDC_INCSR_CDT UDC_INDEX = 0 Iris::debug ("usb reset\n") void Udc::irq_in0 (): // Interrupt on endpoint 0. unsigned csr = UDC_CSR0 if csr & UDC_CSR0_SENTSTALL: csr &= ~(UDC_CSR0_SENTSTALL | UDC_CSR0_SENDSTALL) if csr & UDC_CSR0_SETUPEND: csr |= UDC_CSR0_SVDSETUPEND if state == SEND_CSW: send_csw () state = IDLE_WAIT if !(csr & UDC_CSR0_OUTPKTRDY): kdebug ("packet sent 0\n") return union { unsigned d[2]; Setup s; } packet packet.d[0] = UDC_FIFO (0) packet.d[1] = UDC_FIFO (0) if !(packet.s.request_type & 0x80) && packet.s.length > 0: // More data will follow; unsupported. Iris::debug ("packet on ep0 too long\n") UDC_CSR0 = csr | UDC_CSR0_SENDSTALL return UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY csr &= ~(UDC_CSR0_SETUPEND | UDC_CSR0_SENDSTALL | UDC_CSR0_SENTSTALL) unsigned ret = handle_setup (&packet.s) UDC_INDEX = 1 UDC_OUTMAXP = max_packet_size_bulk UDC_OUTCSR |= UDC_OUTCSR_CDT UDC_INDEX = 2 UDC_INMAXP = max_packet_size_bulk UDC_INCSR |= UDC_INCSR_CDT UDC_INDEX = 0 if ret == ~0: Iris::debug ("failed setup: %x %x %x %x %x\n", packet.s.request_type, packet.s.request, packet.s.index, packet.s.length, packet.s.value) UDC_CSR0 = csr | UDC_CSR0_SENDSTALL return UDC_CSR0 = csr | ret void Udc::send_csw (): UDC_INDEX = 2 UDC_FIFO (2) = 0x53425355 UDC_FIFO (2) = cbw.cbw.tag UDC_FIFO (2) = residue UDC_FIFO8 (2) = status UDC_INCSR |= UDC_INCSR_INPKTRDY status = 0 residue = 0 kdebug ("sent csw\n") state = IDLE_WAIT unsigned Udc::big_endian (unsigned src): return src >> 24 | src >> 8 & 0xff00 | src << 8 & 0xff0000 | src << 24 void Udc::parse_cbw (): UDC_INDEX = 2 bool to_host = cbw.cbw.flags & 0x80 switch cbw.cbw.data[0]: case CBW::INQUIRY: if !to_host: UDC_INCSR |= UDC_INCSR_SENDSTALL status = 2 state = CSW return Iris::debug ("sending inquiry response\n") send_padded (2, "\x00\x00\x04\x02\x1f\x00\x00\x00shevek iris usb stick 0 ", 36, cbw.cbw.length) UDC_INCSR |= UDC_INCSR_INPKTRDY state = CSW return case CBW::TEST_UNIT_READY: if to_host || cbw.cbw.length != 0: UDC_INCSR |= UDC_INCSR_SENDSTALL status = 2 state = CSW return Iris::debug ("sending ready response\n") send_csw () return case CBW::READ_CAPACITY: if !to_host: UDC_INCSR |= UDC_INCSR_SENDSTALL status = 2 state = CSW return unsigned capacity[2] capacity[0] = big_endian ((block.get_size ().value () >> block_bits) - 1) capacity[1] = big_endian (1 << block_bits) Iris::debug ("sending capacity: %x * %x\n", capacity[0], capacity[1]) send_padded (2, (char *)capacity, 8, cbw.cbw.length) UDC_INCSR |= UDC_INCSR_INPKTRDY state = CSW return case CBW::REQUEST_SENSE: Iris::debug ("sense requested\n") send_padded (2, "\xf0\x00\x05\x00\x00\x00\x00\x00", 8, cbw.cbw.length) UDC_INCSR |= UDC_INCSR_INPKTRDY state = CSW return default: Iris::debug ("cbw:") for unsigned i = 0; i < cbw.cbw.size; ++i: kdebug_char (' ') kdebug_num (cbw.cbw.data[i], 2) Iris::debug ("\n") UDC_INCSR |= UDC_INCSR_SENDSTALL residue = cbw.cbw.length status = 1 state = CSW return // TODO. void Udc::irq_in2 (): UDC_INDEX = 2 unsigned csr = UDC_INCSR if csr & UDC_INCSR_SENDSTALL: // When stalling, do nothing else. kdebug ("stalling\n") UDC_INDEX = 0 return switch state: case IDLE_WAIT: // data has been received. kdebug ("bulk data was sent\n") state = IDLE break case IDLE: // This should not happen. Iris::debug ("in interrupt while idle\n") break case TX: // TODO: transmit more. kdebug ("bulk transmit\n") break case RX: // This should not be possible. Iris::debug ("in interrupt while receiving\n") UDC_INCSR = csr | UDC_INCSR_SENDSTALL status = 2 state = CSW break case CSW: // The host is now ready to receive the csw; send it. kdebug ("bulk send csw\n") send_csw () break UDC_INDEX = 0 void Udc::irq_out (): UDC_INDEX = 1 unsigned csr = UDC_OUTCSR unsigned size = UDC_OUTCOUNT if !(csr & UDC_OUTCSR_OUTPKTRDY): // No packet, just a notification. kdebug ("no packet\n") return if csr & UDC_OUTCSR_SENDSTALL: // When stalling, do nothing else. UDC_INDEX = 0 return switch state: case IDLE_WAIT: Iris::debug ("out interrupt while idle waiting\n") break case IDLE: // expect a new cbw. if size != 31: Iris::debug ("count %d != 31\n", UDC_OUTCOUNT) UDC_OUTCSR = csr | UDC_OUTCSR_SENDSTALL status = 2 state = CSW break for unsigned i = 0; i < 8; ++i: cbw.u[i] = UDC_FIFO (1) if cbw.cbw.sig != 0x43425355 || cbw.cbw.lun != 0 || cbw.cbw.size == 0 || cbw.cbw.size > 16: Iris::debug ("sig %x lun %d size %d\n", cbw.cbw.sig, cbw.cbw.lun, cbw.cbw.size) UDC_OUTCSR = csr | UDC_OUTCSR_SENDSTALL status = 2 state = CSW break UDC_OUTCSR = csr & ~UDC_OUTCSR_OUTPKTRDY kdebug ("bulk cbw\n") parse_cbw () break case TX: // This should be impossible. Iris::debug ("out interrupt while transmitting\n") UDC_OUTCSR = csr | UDC_OUTCSR_SENDSTALL break case RX: // TODO: Handle the data. kdebug ("bulk rx\n") UDC_OUTCSR = csr & ~UDC_OUTCSR_OUTPKTRDY break case CSW: // This should be impossible. Iris::debug ("out interrupt while csw-waiting\n") UDC_OUTCSR = csr | UDC_OUTCSR_SENDSTALL break UDC_INDEX = 0 void Udc::interrupt (): while true: unsigned usb = UDC_INTRUSB unsigned in = UDC_INTRIN unsigned out = UDC_INTROUT if !(usb & 4) && !(in & 5) && !(out & 2): // No more interrupts to handle; this is normal, because we're looping until this happens. Iris::debug ("irq done\n") return if usb & 4: Iris::debug ("usb\t") // reset. irq_usb () if in & 1: Iris::debug ("control\t") // control request irq_in0 () if in & 4: Iris::debug ("in\t") // bulk in done irq_in2 () if out & 2: Iris::debug ("out\t") // bulk out waiting irq_out () Iris::Num start (): map_udc () map_gpio () map_cpm () Udc udc Iris::WBlock nand = Iris::my_parent.get_capability () udc.init (nand) while true: Iris::register_interrupt (IRQ_UDC) Iris::wait () udc.interrupt ()