diff --git a/source/usb-mass-storage.ccp b/source/usb-mass-storage.ccp index e8e4cfb..cd1cd32 100644 --- a/source/usb-mass-storage.ccp +++ b/source/usb-mass-storage.ccp @@ -152,9 +152,6 @@ class Udc: 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: @@ -215,30 +212,23 @@ class Udc: 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 + 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 () - unsigned send (unsigned ep, char const *data, unsigned length, unsigned maxlength) - unsigned send_padded (unsigned ep, char const *data, unsigned length, unsigned maxlength) + 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 @@ -268,7 +258,6 @@ void Udc::init (Iris::WBlock b): // 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 @@ -279,41 +268,64 @@ void Udc::init (Iris::WBlock b): 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 - state = IDLE status = 0 residue = 0 -unsigned Udc::send (unsigned ep, char const *data, unsigned length, unsigned maxlength): +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_char ('#') + kdebug_num (((unsigned *)data)[i / 4], 8) + kdebug (" ") for ; i < length; ++i: UDC_FIFO8 (ep) = data[i] - kdebug_char ('+') - return ep == 0 ? UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND : 0 + kdebug_num (data[i], 2) + kdebug (" ") -unsigned Udc::send_padded (unsigned ep, char const *data, unsigned length, unsigned maxlength): +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 (ep, data, len, maxlength) + send (2, data, len, maxlength) while len + 3 < maxlength: - UDC_FIFO (ep) = 0 + UDC_FIFO (2) = 0 len += 4 kdebug_char ('-') while len < maxlength: - UDC_FIFO8 (ep) = 0 + 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: @@ -321,16 +333,19 @@ unsigned Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): if idx != 0: return false Iris::debug ("get config descriptor\n") - return send (0, reinterpret_cast (&config_descriptor), sizeof (config_descriptor), len) + 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") - return send (0, reinterpret_cast (&device_descriptor), sizeof (device_descriptor), len) + 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 - //return send (0, reinterpret_cast (&device_qualifier_descriptor), sizeof (device_qualifier_descriptor), len) + //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. @@ -338,16 +353,20 @@ unsigned Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): switch idx: case 0: Iris::debug ("get language descriptor\n") - return send (0, reinterpret_cast (&s_langs), sizeof (s_langs), len) + 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") - return send (0, reinterpret_cast (&s_manufacturer), sizeof (s_manufacturer), len) + 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") - return send (0, reinterpret_cast (&s_product), sizeof (s_product), len) + 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") - return send (0, reinterpret_cast (&s_serial), sizeof (s_serial), len) + send (0, reinterpret_cast (&s_serial), sizeof (s_serial), len) + return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND default: return ~0 default: @@ -378,15 +397,18 @@ unsigned Udc::handle_setup (Setup *s): switch s->request: case GET_STATUS: Iris::debug ("get status\n") - return send (0, "\0\0", 2, s->length) + 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") - return send (0, &configuration, 1, s->length) + send (0, &configuration, 1, s->length) + return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND case GET_INTERFACE: Iris::debug ("get interface\n") - return send (0, "\0", 1, s->length) + send (0, "\0", 1, s->length) + return UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND default: return ~0 case STANDARD_TO_ENDPOINT: @@ -399,15 +421,13 @@ unsigned Udc::handle_setup (Setup *s): Iris::debug ("in ep halt reset\n") UDC_INDEX = 2 UDC_INCSR &= ~(UDC_INCSR_SENDSTALL | UDC_INCSR_SENTSTALL) - if state == CSW: - state = SEND_CSW + 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) - if state == CSW: - state = SEND_CSW + stalling[1] = false return 0 default: return ~0 @@ -419,14 +439,14 @@ unsigned Udc::handle_setup (Setup *s): switch s->request: case GET_MAX_LUN: Iris::debug ("get max lun\n") - return send (0, "\0", 1, s->length) + 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") - state = IDLE return 0 default: return ~0 @@ -441,26 +461,21 @@ void Udc::irq_usb (): // 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. + 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 state == SEND_CSW: - send_csw () - state = IDLE_WAIT if !(csr & UDC_CSR0_OUTPKTRDY): kdebug ("packet sent 0\n") return @@ -476,10 +491,8 @@ void Udc::irq_in0 (): 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: @@ -487,115 +500,64 @@ void Udc::irq_in0 (): 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) = cbw.cbw.tag + 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") - state = IDLE_WAIT + +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::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 @@ -606,48 +568,83 @@ void Udc::irq_out (): return if csr & UDC_OUTCSR_SENDSTALL: // When stalling, do nothing else. - UDC_INDEX = 0 + kdebug ("not responding to out during stall\n") + UDC_OUTCSR &= ~UDC_OUTCSR_SENTSTALL 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 + // 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 - 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 + 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 - UDC_OUTCSR = csr & ~UDC_OUTCSR_OUTPKTRDY - kdebug ("bulk cbw\n") - parse_cbw () + 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 TX: - // This should be impossible. - Iris::debug ("out interrupt while transmitting\n") - UDC_OUTCSR = csr | UDC_OUTCSR_SENDSTALL + 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 - case RX: - // TODO: Handle the data. - kdebug ("bulk rx\n") - UDC_OUTCSR = csr & ~UDC_OUTCSR_OUTPKTRDY + 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 - case CSW: - // This should be impossible. - Iris::debug ("out interrupt while csw-waiting\n") - UDC_OUTCSR = csr | UDC_OUTCSR_SENDSTALL - break - UDC_INDEX = 0 + // 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: @@ -658,21 +655,10 @@ void Udc::interrupt (): // 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 () + Iris::debug ("ignoring data request during idle\n") + handle_interrupt (usb & 4, in & 1) if out & 2: - Iris::debug ("out\t") - // bulk out waiting irq_out () Iris::Num start ():