1
0
mirror of git://projects.qi-hardware.com/iris.git synced 2024-07-02 21:17:38 +03:00
iris/source/usb-mass-storage.ccp
2010-09-01 23:27:14 +02:00

690 lines
19 KiB
COBOL

#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 ()