mirror of
git://projects.qi-hardware.com/iris.git
synced 2024-12-29 02:39:53 +02:00
763 lines
21 KiB
COBOL
763 lines
21 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)
|
|
-> 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 <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 {
|
|
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 <char const *> (&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 <char const *> (&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 <char const *> (&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 <char const *> (&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 <char const *> (&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 <char const *> (&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 <char const *> (&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 <Iris::WBlock> ()
|
|
udc.init (nand)
|
|
while true:
|
|
Iris::register_interrupt (IRQ_UDC)
|
|
Iris::wait ()
|
|
udc.interrupt ()
|