2009-09-27 11:23:33 +03:00
|
|
|
#pypp 0
|
2013-05-12 16:46:11 +03:00
|
|
|
// vim: set filetype=cpp : //
|
2009-09-27 11:23:33 +03:00
|
|
|
// Iris: micro-kernel for a capability-based operating system.
|
|
|
|
// boot-programs/udc.ccp: USB device controller driver.
|
|
|
|
// Copyright 2009 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/>.
|
|
|
|
|
2009-12-11 10:43:42 +02:00
|
|
|
#include "iris.hh"
|
2009-09-27 11:23:33 +03:00
|
|
|
#include "devices.hh"
|
|
|
|
#define ARCH
|
|
|
|
#include "arch.hh"
|
|
|
|
|
|
|
|
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))
|
2009-12-18 23:27:26 +02:00
|
|
|
/**/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))
|
2009-09-27 11:23:33 +03:00
|
|
|
template <unsigned size> struct String {
|
|
|
|
static u8 const Type = 3;
|
|
|
|
u8 length;
|
|
|
|
u8 type;
|
|
|
|
u16 data[size];
|
|
|
|
} __attribute__ ((packed))
|
|
|
|
static unsigned const max_packet_size0 = 64
|
2010-01-16 17:13:54 +02:00
|
|
|
static unsigned const max_packet_size_bulk = 64
|
2009-09-27 11:23:33 +03:00
|
|
|
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 Request_types:
|
|
|
|
STANDARD_TO_DEVICE = 0
|
|
|
|
VENDOR_TO_DEVICE = 0x40
|
|
|
|
STANDARD_FROM_DEVICE = 0x80
|
|
|
|
VENDOR_FROM_DEVICE = 0xc0
|
|
|
|
enum Endpoint_types:
|
|
|
|
CONTROL = 0
|
|
|
|
ISOCHRONOUS = 1
|
|
|
|
BULK = 2
|
|
|
|
INTERRUPT = 3
|
|
|
|
/**/struct my_config {
|
|
|
|
Configuration config;
|
|
|
|
Interface interface;
|
|
|
|
Endpoint endpoint[2];
|
|
|
|
} __attribute__ ((packed))
|
2009-12-18 23:27:26 +02:00
|
|
|
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
|
2009-09-27 11:23:33 +03:00
|
|
|
enum State:
|
|
|
|
IDLE
|
|
|
|
TX
|
|
|
|
RX
|
|
|
|
State state
|
|
|
|
char configuration
|
|
|
|
unsigned size
|
2010-01-16 17:13:54 +02:00
|
|
|
unsigned rx_request
|
2009-09-27 11:23:33 +03:00
|
|
|
char const *ptr
|
2010-01-14 19:14:37 +02:00
|
|
|
unsigned cmd_code, cmd_arg
|
2009-10-04 20:47:20 +03:00
|
|
|
bool rebooting
|
2010-01-14 19:14:37 +02:00
|
|
|
bool vendor (Setup *s, unsigned cmd)
|
2009-09-30 00:48:33 +03:00
|
|
|
bool get_descriptor (unsigned type, unsigned idx, unsigned len)
|
2010-01-14 19:14:37 +02:00
|
|
|
bool handle_setup (Setup *s, unsigned cmd)
|
2010-01-16 17:13:54 +02:00
|
|
|
void irq_usb (unsigned cmd)
|
|
|
|
void irq_in (unsigned cmd)
|
|
|
|
void irq_out (unsigned cmd)
|
2009-12-26 15:17:06 +02:00
|
|
|
char log_buffer[1000]
|
|
|
|
unsigned log_buffer_size
|
|
|
|
unsigned log_buffer_start
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::Cap caller, caller_arg
|
2010-01-14 23:04:19 +02:00
|
|
|
bool have_caller
|
2010-01-16 17:13:54 +02:00
|
|
|
unsigned *page
|
|
|
|
unsigned *p
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::Page buffer_page
|
2009-09-27 11:23:33 +03:00
|
|
|
public:
|
|
|
|
void init ()
|
|
|
|
void log (unsigned c)
|
2010-01-14 19:14:37 +02:00
|
|
|
void interrupt (unsigned cmd)
|
2010-05-01 00:13:49 +03:00
|
|
|
void send (unsigned code, unsigned narg, Iris::Cap reply, Iris::Cap arg)
|
2009-09-27 11:23:33 +03:00
|
|
|
|
2009-12-18 23:27:26 +02:00
|
|
|
Udc::Device Udc::device_descriptor = { sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0xfffe, 0x0002, 0x100, 1, 2, 0, 1 }
|
|
|
|
Udc::my_config Udc::config_descriptor = {
|
2009-09-27 11:23:33 +03:00
|
|
|
(Configuration){ sizeof (Configuration), Configuration::Type, sizeof (my_config), 1, 1, 0, 0xc0, 1 },
|
|
|
|
(Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0xff, 0, 0x80, 0 }, {
|
|
|
|
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 },
|
|
|
|
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x81, BULK, max_packet_size_bulk, 0 }
|
|
|
|
}
|
|
|
|
}
|
2009-12-18 23:27:26 +02:00
|
|
|
Udc::String <1> Udc::s_langs = { sizeof (String <1>), String <1>::Type, { 0x0409 } }
|
|
|
|
Udc::String <6> Udc::s_manufacturer = { sizeof (String <6>), String <6>::Type, { 's', 'h', 'e', 'v', 'e', 'k' } }
|
|
|
|
Udc::String <16> Udc::s_product = { sizeof (String <16>), String <16>::Type, { 'I', 'r', 'i', 's', ' ', 'o', 'n', ' ', 'N', 'a', 'n', 'o', 'N', 'o', 't', 'e' } }
|
|
|
|
|
|
|
|
//Udc::Device_Qualifier const Udc::device_qualifier_descriptor = { sizeof (Device_Qualifier), Device_Qualifier::Type, 0x200, 0, 0, 0, max_packet_size0, 1, 0 }
|
|
|
|
//Udc::my_config const Udc::other_config_descriptor = {
|
|
|
|
// (Configuration){ sizeof (Configuration), 7, sizeof (my_config), 1, 1, 0, 0xc0, 1 },
|
|
|
|
// (Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0xff, 0, 0x80, 0 }, {
|
|
|
|
// (Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 },
|
|
|
|
// (Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x81, BULK, max_packet_size_bulk, 0 }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
2009-09-27 11:23:33 +03:00
|
|
|
void Udc::init ():
|
2009-12-18 23:27:26 +02:00
|
|
|
// Initialize the globals. My method of compiling doesn't do that properly.
|
|
|
|
device_descriptor = (Device){ sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0xfffe, 0x0002, 0x100, 1, 2, 0, 1 }
|
|
|
|
config_descriptor = (my_config){
|
|
|
|
(Configuration){ sizeof (Configuration), Configuration::Type, sizeof (my_config), 1, 1, 0, 0xc0, 1 },
|
|
|
|
(Interface){ sizeof (Interface), Interface::Type, 0, 0, 2, 0xff, 0, 0x80, 0 }, {
|
|
|
|
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 1, BULK, max_packet_size_bulk, 0 },
|
|
|
|
(Endpoint){ sizeof (Endpoint), Endpoint::Type, 0x81, 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' } }
|
2010-01-14 23:04:19 +02:00
|
|
|
have_caller = false
|
2009-12-26 15:17:06 +02:00
|
|
|
log_buffer_size = 0
|
|
|
|
log_buffer_start = 0
|
2010-01-14 19:14:37 +02:00
|
|
|
cmd_code = ~0
|
|
|
|
cmd_arg = 0
|
2009-12-18 23:27:26 +02:00
|
|
|
|
2010-01-16 17:13:54 +02:00
|
|
|
// Use the space which is reserved for the framebuffer, because it will not be in use.
|
|
|
|
// Normally a normal new page should be allocated here, but new isn't implemented at this point.
|
|
|
|
page = (unsigned *)LCD_FRAMEBUFFER_BASE
|
|
|
|
p = page
|
2010-05-01 00:13:49 +03:00
|
|
|
buffer_page = Iris::my_memory.create_page ()
|
2010-06-22 21:55:02 +03:00
|
|
|
buffer_page.set_flags (Iris::Page::FRAME | Iris::Page::PAYING)
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::my_memory.map (buffer_page, (unsigned)page)
|
2010-01-16 17:13:54 +02:00
|
|
|
|
2009-12-18 23:27:26 +02:00
|
|
|
// Disconnect from the bus and don't try to get high-speed.
|
2010-01-16 17:13:54 +02:00
|
|
|
UDC_POWER = 0
|
|
|
|
UDC_TESTMODE = 0
|
|
|
|
UDC_INDEX = 0
|
2009-09-27 11:23:33 +03:00
|
|
|
state = IDLE
|
|
|
|
configuration = 0
|
|
|
|
size = 0
|
|
|
|
// exit suspend mode by reading the interrupt register.
|
2009-12-11 10:43:42 +02:00
|
|
|
cpm_start_udc ()
|
2009-09-27 11:23:33 +03:00
|
|
|
// reset all pending endpoint interrupts.
|
2010-01-16 17:13:54 +02:00
|
|
|
unsigned i = UDC_INTRUSB
|
2009-09-27 11:23:33 +03:00
|
|
|
i = UDC_INTRIN
|
|
|
|
i = UDC_INTROUT
|
|
|
|
// enable interrupt on bus reset.
|
2010-01-16 17:13:54 +02:00
|
|
|
UDC_INTRUSBE = UDC_INTR_RESET
|
2009-09-27 11:23:33 +03:00
|
|
|
// enable interrupts on endpoint 0.
|
2010-01-16 17:13:54 +02:00
|
|
|
UDC_INTRINE = 1 << 0
|
|
|
|
// and on out endpoint 1.
|
|
|
|
UDC_INTROUTE = 1 << 1
|
2009-12-27 01:12:35 +02:00
|
|
|
// Wait a while.
|
|
|
|
for unsigned w = 0; w < 10000; ++w:
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::schedule ()
|
2009-12-26 15:17:06 +02:00
|
|
|
// Connect to the host.
|
2010-01-16 17:13:54 +02:00
|
|
|
UDC_POWER = UDC_POWER_SOFTCONN
|
2009-09-27 11:23:33 +03:00
|
|
|
|
2010-01-14 19:14:37 +02:00
|
|
|
bool Udc::vendor (Setup *s, unsigned cmd):
|
2009-12-26 15:17:06 +02:00
|
|
|
if !(s->request_type & 0x80):
|
2010-01-16 17:13:54 +02:00
|
|
|
kdebug ("data to device without size\n")
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::panic (0)
|
2009-12-26 15:17:06 +02:00
|
|
|
return true
|
|
|
|
if s->request == 10:
|
2010-01-14 19:14:37 +02:00
|
|
|
static unsigned b[2]
|
|
|
|
ptr = (char *)b
|
|
|
|
size = s->length < 8 ? s->length : 8
|
|
|
|
if cmd_code != ~0:
|
|
|
|
b[0] = cmd_code
|
|
|
|
b[1] = cmd_arg
|
2010-05-01 00:13:49 +03:00
|
|
|
if cmd_code == Iris::Directory::LOCK_RO || cmd_code == Iris::Directory::UNLOCK_RO:
|
2010-01-16 17:13:54 +02:00
|
|
|
caller.invoke ()
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::free_cap (caller)
|
|
|
|
Iris::free_cap (caller_arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
have_caller = false
|
|
|
|
//kdebug ("(un)lock response\n")
|
2010-01-14 19:14:37 +02:00
|
|
|
cmd_code = ~0
|
2009-12-26 15:17:06 +02:00
|
|
|
else:
|
2010-01-14 19:14:37 +02:00
|
|
|
if log_buffer_start == log_buffer_size:
|
|
|
|
b[0] = ~1
|
|
|
|
b[1] = 0
|
|
|
|
else:
|
|
|
|
b[0] = ~0
|
|
|
|
b[1] = (log_buffer[log_buffer_start++] & 0xff) * 0x01010101
|
|
|
|
if log_buffer_start == log_buffer_size:
|
|
|
|
log_buffer_start = 0
|
|
|
|
log_buffer_size = 0
|
2009-12-26 15:17:06 +02:00
|
|
|
else:
|
2009-12-18 23:27:26 +02:00
|
|
|
static char const *name = "Reboot"
|
2009-09-30 00:48:33 +03:00
|
|
|
ptr = name
|
2009-12-18 23:27:26 +02:00
|
|
|
size = s->length < 6 ? s->length : 6
|
2009-10-04 20:47:20 +03:00
|
|
|
rebooting = true
|
2009-12-26 15:17:06 +02:00
|
|
|
state = TX
|
2009-09-30 00:48:33 +03:00
|
|
|
return true
|
2009-09-27 11:23:33 +03:00
|
|
|
|
2010-05-01 00:13:49 +03:00
|
|
|
void Udc::send (unsigned code, unsigned narg, Iris::Cap reply, Iris::Cap arg):
|
2010-01-16 17:13:54 +02:00
|
|
|
if cmd_code != ~0:
|
2010-01-14 19:14:37 +02:00
|
|
|
kdebug ("new code sent while old one wasn't finished.\n")
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::panic (0)
|
2010-01-14 19:14:37 +02:00
|
|
|
cmd_code = code
|
2010-01-16 17:13:54 +02:00
|
|
|
cmd_arg = narg
|
|
|
|
caller = reply
|
|
|
|
caller_arg = arg
|
|
|
|
have_caller = true
|
2010-01-14 19:14:37 +02:00
|
|
|
|
2009-09-30 00:48:33 +03:00
|
|
|
bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len):
|
2009-09-27 11:23:33 +03:00
|
|
|
switch type:
|
|
|
|
case Configuration::Type:
|
2009-12-18 23:27:26 +02:00
|
|
|
if idx != 0:
|
2009-09-30 00:48:33 +03:00
|
|
|
return false
|
2009-09-27 11:23:33 +03:00
|
|
|
ptr = reinterpret_cast <char const *> (&config_descriptor)
|
|
|
|
size = (len < sizeof (config_descriptor) ? len : sizeof (config_descriptor))
|
|
|
|
break
|
|
|
|
case Device::Type:
|
|
|
|
if idx != 0:
|
2009-09-30 00:48:33 +03:00
|
|
|
return false
|
2009-09-27 11:23:33 +03:00
|
|
|
ptr = reinterpret_cast <char const *> (&device_descriptor)
|
|
|
|
size = (len < sizeof (device_descriptor) ? len : sizeof (device_descriptor))
|
|
|
|
break
|
2009-12-18 23:27:26 +02:00
|
|
|
case Device_Qualifier::Type:
|
|
|
|
//if idx != 0:
|
|
|
|
// return false
|
|
|
|
//ptr = reinterpret_cast <char const *> (&device_qualifier_descriptor)
|
|
|
|
//size = (len < sizeof (device_qualifier_descriptor) ? len : sizeof (device_qualifier_descriptor))
|
|
|
|
//break
|
|
|
|
return false
|
2010-01-14 19:14:37 +02:00
|
|
|
// The 6 is an arbitrary number, except that String <6> is instantiated already.
|
2009-12-18 23:27:26 +02:00
|
|
|
case String <6>::Type:
|
2009-09-27 11:23:33 +03:00
|
|
|
switch idx:
|
2009-12-18 23:27:26 +02:00
|
|
|
case 0:
|
|
|
|
ptr = reinterpret_cast <char const *> (&s_langs)
|
|
|
|
size = (len < sizeof (s_langs) ? len : sizeof (s_langs))
|
|
|
|
break
|
2009-09-27 11:23:33 +03:00
|
|
|
case 1:
|
|
|
|
ptr = reinterpret_cast <char const *> (&s_manufacturer)
|
|
|
|
size = (len < sizeof (s_manufacturer) ? len : sizeof (s_manufacturer))
|
|
|
|
break
|
|
|
|
case 2:
|
|
|
|
ptr = reinterpret_cast <char const *> (&s_product)
|
|
|
|
size = (len < sizeof (s_product) ? len : sizeof (s_product))
|
|
|
|
break
|
|
|
|
default:
|
2009-09-30 00:48:33 +03:00
|
|
|
return false
|
2009-09-27 11:23:33 +03:00
|
|
|
break
|
|
|
|
default:
|
2009-09-30 00:48:33 +03:00
|
|
|
return false
|
2009-12-18 23:27:26 +02:00
|
|
|
state = TX
|
|
|
|
return true
|
2009-09-27 11:23:33 +03:00
|
|
|
|
2010-01-14 19:14:37 +02:00
|
|
|
bool Udc::handle_setup (Setup *s, unsigned cmd):
|
2009-09-27 11:23:33 +03:00
|
|
|
switch s->request_type:
|
|
|
|
case STANDARD_TO_DEVICE:
|
|
|
|
switch s->request:
|
|
|
|
case SET_ADDRESS:
|
|
|
|
UDC_FADDR = s->value
|
|
|
|
break
|
|
|
|
case SET_CONFIGURATION:
|
|
|
|
if s->value >= 2:
|
2009-09-30 00:48:33 +03:00
|
|
|
return false
|
2009-09-27 11:23:33 +03:00
|
|
|
configuration = s->value
|
|
|
|
break
|
|
|
|
case SET_INTERFACE:
|
|
|
|
if s->value != 0:
|
2009-09-30 00:48:33 +03:00
|
|
|
return false
|
2009-09-27 11:23:33 +03:00
|
|
|
break
|
|
|
|
default:
|
2009-09-30 00:48:33 +03:00
|
|
|
return false
|
2010-01-16 17:13:54 +02:00
|
|
|
UDC_OUTMAXP = 64
|
|
|
|
UDC_OUTCSR = UDC_OUTCSR_CDT | UDC_OUTCSR_FF
|
|
|
|
UDC_OUTCSR = UDC_OUTCSR_CDT | UDC_OUTCSR_FF
|
2009-09-27 11:23:33 +03:00
|
|
|
break
|
|
|
|
case STANDARD_FROM_DEVICE:
|
|
|
|
switch s->request:
|
|
|
|
case GET_STATUS:
|
|
|
|
ptr = "\0\0"
|
|
|
|
size = (s->length < 2 ? s->length : 2)
|
2009-09-30 00:48:33 +03:00
|
|
|
state = TX
|
|
|
|
break
|
2009-09-27 11:23:33 +03:00
|
|
|
case GET_DESCRIPTOR:
|
2009-09-30 00:48:33 +03:00
|
|
|
return get_descriptor ((s->value >> 8) & 0xff, s->value & 0xff, s->length)
|
2009-09-27 11:23:33 +03:00
|
|
|
case GET_CONFIGURATION:
|
|
|
|
ptr = &configuration
|
|
|
|
size = (s->length < 1 ? s->length : 1)
|
2009-09-30 00:48:33 +03:00
|
|
|
state = TX
|
|
|
|
break
|
2009-09-27 11:23:33 +03:00
|
|
|
case GET_INTERFACE:
|
|
|
|
ptr = "\0"
|
|
|
|
size = (s->length < 1 ? s->length : 1)
|
2009-09-30 00:48:33 +03:00
|
|
|
state = TX
|
|
|
|
break
|
2009-09-27 11:23:33 +03:00
|
|
|
default:
|
2009-09-30 00:48:33 +03:00
|
|
|
return false
|
2009-09-27 11:23:33 +03:00
|
|
|
break
|
|
|
|
case VENDOR_TO_DEVICE:
|
|
|
|
case VENDOR_FROM_DEVICE:
|
2010-01-14 19:14:37 +02:00
|
|
|
return vendor (s, cmd)
|
2009-09-27 11:23:33 +03:00
|
|
|
default:
|
2009-09-30 00:48:33 +03:00
|
|
|
return false
|
|
|
|
return true
|
2009-09-27 11:23:33 +03:00
|
|
|
|
2010-01-16 17:13:54 +02:00
|
|
|
void Udc::irq_usb (unsigned cmd):
|
|
|
|
// Reset.
|
2012-09-26 20:03:36 +03:00
|
|
|
//Iris::debug ("usb reset\n")
|
2010-01-16 17:13:54 +02:00
|
|
|
state = IDLE
|
|
|
|
|
|
|
|
void Udc::irq_in (unsigned cmd):
|
|
|
|
// Interrupt on endpoint 0.
|
|
|
|
unsigned csr = UDC_CSR0
|
|
|
|
if csr & UDC_CSR0_SENTSTALL:
|
|
|
|
csr &= ~(UDC_CSR0_SENTSTALL | UDC_CSR0_SENDSTALL)
|
2009-09-27 11:23:33 +03:00
|
|
|
state = IDLE
|
2010-01-16 17:13:54 +02:00
|
|
|
if csr & UDC_CSR0_SETUPEND:
|
|
|
|
csr |= UDC_CSR0_SVDSETUPEND
|
|
|
|
state = IDLE
|
|
|
|
switch state:
|
|
|
|
case IDLE:
|
|
|
|
if rebooting:
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::reboot ()
|
2010-01-16 17:13:54 +02:00
|
|
|
if !(csr & UDC_CSR0_OUTPKTRDY):
|
|
|
|
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; delay handling of packet.
|
|
|
|
state = RX
|
|
|
|
UDC_CSR0 = csr | UDC_CSR0_SVDOUTPKTRDY
|
|
|
|
rx_request = packet.s.request
|
|
|
|
return
|
|
|
|
UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY
|
|
|
|
if !handle_setup (&packet.s, cmd):
|
|
|
|
csr |= UDC_CSR0_SENDSTALL
|
2009-09-27 11:23:33 +03:00
|
|
|
break
|
2010-01-16 17:13:54 +02:00
|
|
|
if size == 0:
|
|
|
|
return
|
|
|
|
// Fall through.
|
|
|
|
case TX:
|
|
|
|
unsigned i
|
|
|
|
for i = 0; (size & ~3) > 0 && i < max_packet_size0; i += 4, size -= 4:
|
|
|
|
UDC_FIFO (0) = *(unsigned *)ptr
|
|
|
|
ptr += 4
|
|
|
|
for ; size > 0 && i < max_packet_size0; ++i, --size:
|
|
|
|
UDC_FIFO8 (0) = *ptr++
|
|
|
|
if i == max_packet_size0:
|
|
|
|
csr |= UDC_CSR0_INPKTRDY
|
|
|
|
else:
|
2009-09-27 11:23:33 +03:00
|
|
|
state = IDLE
|
2010-01-16 17:13:54 +02:00
|
|
|
csr |= UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND
|
|
|
|
break
|
|
|
|
case RX:
|
|
|
|
// The protocol that is used doesn't allow large packets, so being here always means the entire packet is received.
|
|
|
|
switch rx_request & 0xff:
|
2010-05-01 00:13:49 +03:00
|
|
|
case Iris::Directory::GET_SIZE & 0xff:
|
2010-01-16 17:13:54 +02:00
|
|
|
if !have_caller:
|
|
|
|
kdebug ("received dir size from server without a caller waiting\n")
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::panic (0)
|
2010-01-16 17:13:54 +02:00
|
|
|
unsigned size_l = UDC_FIFO (0)
|
|
|
|
unsigned size_h = UDC_FIFO (0)
|
2010-05-01 00:13:49 +03:00
|
|
|
caller.invoke (Iris::Num (size_l, size_h))
|
|
|
|
Iris::free_cap (caller)
|
|
|
|
Iris::free_cap (caller_arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
have_caller = false
|
|
|
|
//kdebug ("get_size response\n")
|
|
|
|
break
|
2010-05-01 00:13:49 +03:00
|
|
|
case Iris::Directory::GET_NAME & 0xff:
|
2010-01-16 17:13:54 +02:00
|
|
|
if !have_caller:
|
|
|
|
kdebug ("received filename from server without a caller waiting\n")
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::panic (0)
|
2010-01-16 17:13:54 +02:00
|
|
|
unsigned n[4]
|
|
|
|
for unsigned i = 0; i < 4; ++i:
|
|
|
|
n[i] = UDC_FIFO (0)
|
2010-05-01 00:13:49 +03:00
|
|
|
caller.invoke (Iris::Num (n[0], n[1]), Iris::Num (n[2], n[3]))
|
|
|
|
Iris::free_cap (caller)
|
|
|
|
Iris::free_cap (caller_arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
//kdebug ("get_name response\n")
|
|
|
|
have_caller = false
|
|
|
|
break
|
2010-05-01 00:13:49 +03:00
|
|
|
case Iris::String::GET_SIZE & 0xff:
|
2010-01-16 17:13:54 +02:00
|
|
|
if !have_caller:
|
|
|
|
kdebug ("received string size from server without a caller waiting\n")
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::panic (0)
|
2010-01-16 17:13:54 +02:00
|
|
|
unsigned size_l = UDC_FIFO (0)
|
|
|
|
unsigned size_h = UDC_FIFO (0)
|
2010-05-01 00:13:49 +03:00
|
|
|
caller.invoke (Iris::Num (size_l, size_h))
|
|
|
|
Iris::free_cap (caller)
|
|
|
|
Iris::free_cap (caller_arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
have_caller = false
|
|
|
|
//kdebug ("get_filesize response\n")
|
|
|
|
break
|
2010-05-01 00:13:49 +03:00
|
|
|
case Iris::String::GET_CHARS & 0xff:
|
2010-01-16 17:13:54 +02:00
|
|
|
if !have_caller:
|
|
|
|
kdebug ("received string char data from server without a caller waiting\n")
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::panic (0)
|
2010-01-16 17:13:54 +02:00
|
|
|
unsigned n[4]
|
|
|
|
for unsigned i = 0; i < 4; ++i:
|
|
|
|
n[i] = UDC_FIFO (0)
|
2010-05-01 00:13:49 +03:00
|
|
|
caller.invoke (Iris::Num (n[0], n[1]), Iris::Num (n[2], n[3]))
|
|
|
|
Iris::free_cap (caller)
|
|
|
|
Iris::free_cap (caller_arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
have_caller = false
|
|
|
|
//kdebug ("get_chars response\n")
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
kdebug ("invalid vendor request: ")
|
|
|
|
kdebug_num (rx_request)
|
|
|
|
kdebug ("\n")
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::panic (0)
|
2010-01-16 17:13:54 +02:00
|
|
|
UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY
|
|
|
|
state = IDLE
|
|
|
|
break
|
|
|
|
UDC_CSR0 = csr
|
|
|
|
|
|
|
|
void Udc::irq_out (unsigned cmd):
|
|
|
|
// Interrupt on OUT endpoint 1.
|
|
|
|
UDC_INDEX = 1
|
|
|
|
if !have_caller:
|
|
|
|
kdebug ("received bulk data from server without a caller waiting\n")
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::panic (0)
|
2010-01-16 17:13:54 +02:00
|
|
|
unsigned size = UDC_OUTCOUNT
|
|
|
|
unsigned csr = UDC_OUTCSR
|
2012-09-26 20:03:36 +03:00
|
|
|
//Iris::debug ("handling bulk interrupt for %x with %d bytes.\n", csr, size)
|
2010-01-16 17:13:54 +02:00
|
|
|
csr &= ~UDC_OUTCSR_OUTPKTRDY
|
|
|
|
for unsigned i = 0; i < size; i += 4:
|
|
|
|
*p++ = UDC_FIFO (1)
|
|
|
|
if p - page == PAGE_SIZE >> 2:
|
2010-05-01 00:13:49 +03:00
|
|
|
buffer_page.share (caller_arg, Iris::Page::FORGET)
|
2010-06-22 21:55:02 +03:00
|
|
|
buffer_page.set_flags (Iris::Page::FRAME)
|
2010-01-16 17:13:54 +02:00
|
|
|
caller.invoke ()
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::free_cap (caller)
|
|
|
|
Iris::free_cap (caller_arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
have_caller = false
|
|
|
|
//kdebug ("bulk response\n")
|
|
|
|
p = page
|
|
|
|
UDC_OUTCSR = csr
|
|
|
|
UDC_INDEX = 0
|
|
|
|
|
|
|
|
void Udc::interrupt (unsigned cmd):
|
|
|
|
while true:
|
|
|
|
unsigned usb = UDC_INTRUSB
|
|
|
|
unsigned in = UDC_INTRIN
|
|
|
|
unsigned out = UDC_INTROUT
|
|
|
|
if !(usb & 4) && !(in & 1) && !(out & 2):
|
|
|
|
break
|
2012-09-26 20:03:36 +03:00
|
|
|
//Iris::debug ("interrupt: %d/%d/%d\n", usb, in, out)
|
2010-01-16 17:13:54 +02:00
|
|
|
if usb & 4:
|
|
|
|
irq_usb (cmd)
|
|
|
|
if in & 1:
|
|
|
|
irq_in (cmd)
|
|
|
|
if out & 2:
|
|
|
|
irq_out (cmd)
|
2009-09-27 11:23:33 +03:00
|
|
|
|
|
|
|
void Udc::log (unsigned c):
|
2009-12-26 15:17:06 +02:00
|
|
|
if log_buffer_size >= sizeof (log_buffer):
|
|
|
|
return
|
|
|
|
log_buffer[log_buffer_size++] = c
|
2009-10-04 20:47:20 +03:00
|
|
|
|
|
|
|
enum pdata:
|
2012-09-26 20:03:36 +03:00
|
|
|
LOG = 1
|
2010-01-14 19:14:37 +02:00
|
|
|
DIRECTORY
|
2010-01-16 17:13:54 +02:00
|
|
|
FILE
|
|
|
|
NAME
|
2009-09-27 11:23:33 +03:00
|
|
|
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::Num start ():
|
2013-05-12 16:46:11 +03:00
|
|
|
Iris::debug ("udc started")
|
2009-09-27 11:23:33 +03:00
|
|
|
map_udc ()
|
2009-10-04 20:47:20 +03:00
|
|
|
map_gpio ()
|
2009-12-11 10:43:42 +02:00
|
|
|
map_cpm ()
|
2009-09-27 11:23:33 +03:00
|
|
|
Udc udc
|
|
|
|
|
2012-09-26 20:03:36 +03:00
|
|
|
//Iris::Cap logcap = Iris::my_receiver.create_capability (LOG)
|
2013-05-12 16:46:11 +03:00
|
|
|
//__asm__ volatile ("li $a0, 1\nlw $a1, %0\nbreak" :: "m"(logcap.code): "a0", "a1")
|
2010-06-22 21:55:02 +03:00
|
|
|
Iris::Directory dir = Iris::my_receiver.create_capability (DIRECTORY)
|
|
|
|
Iris::my_parent.provide_capability <Iris::Directory> (dir.copy ())
|
|
|
|
Iris::free_cap (dir)
|
2012-09-26 20:03:36 +03:00
|
|
|
udc.init ()
|
|
|
|
Iris::register_interrupt (IRQ_UDC)
|
|
|
|
// Don't call init_done, because this can be used as bootthread and without a parent this call will not be answered.
|
|
|
|
//Iris::my_parent.init_done ()
|
2010-01-14 19:14:37 +02:00
|
|
|
unsigned state = 0
|
2009-09-27 11:23:33 +03:00
|
|
|
while true:
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::wait ()
|
|
|
|
Iris::Cap reply = Iris::get_reply ()
|
|
|
|
Iris::Cap arg = Iris::get_arg ()
|
2012-09-26 20:03:36 +03:00
|
|
|
//Iris::debug ("udc event, protected: %x\n", Iris::recv.protected_data.l)
|
2010-05-05 02:09:32 +03:00
|
|
|
switch Iris::recv.protected_data.l:
|
2012-09-26 20:03:36 +03:00
|
|
|
case 0:
|
2010-05-05 02:09:32 +03:00
|
|
|
udc.interrupt (state)
|
|
|
|
Iris::register_interrupt (IRQ_UDC)
|
|
|
|
break
|
|
|
|
case LOG:
|
|
|
|
udc.log (Iris::recv.data[0].l)
|
2010-01-16 17:13:54 +02:00
|
|
|
break
|
2010-01-14 19:14:37 +02:00
|
|
|
case DIRECTORY:
|
2012-09-26 20:03:36 +03:00
|
|
|
//Iris::debug ("dir request %d\n", Iris::recv.data[0].l)
|
2010-05-01 00:13:49 +03:00
|
|
|
switch Iris::recv.data[0].l:
|
|
|
|
case Iris::Directory::GET_NAME:
|
2010-05-05 02:09:32 +03:00
|
|
|
Iris::Cap name = Iris::my_receiver.create_capability (Iris::Num (NAME, Iris::recv.data[1].l))
|
2010-01-16 17:13:54 +02:00
|
|
|
reply.invoke (0, 0, name.copy ())
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::free_cap (name)
|
|
|
|
Iris::free_cap (reply)
|
|
|
|
Iris::free_cap (arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
continue
|
2010-05-01 00:13:49 +03:00
|
|
|
case Iris::Directory::GET_SIZE:
|
|
|
|
case Iris::Directory::LOCK_RO:
|
|
|
|
case Iris::Directory::UNLOCK_RO:
|
|
|
|
state = Iris::recv.data[0].l
|
|
|
|
if Iris::recv.data[1].h != 0:
|
2012-09-26 20:03:36 +03:00
|
|
|
Iris::panic (0, "index out of supported range")
|
2010-05-01 00:13:49 +03:00
|
|
|
udc.send (Iris::recv.data[0].l, Iris::recv.data[1].l, reply, arg)
|
2010-01-14 19:14:37 +02:00
|
|
|
continue
|
2010-05-01 00:13:49 +03:00
|
|
|
case Iris::Directory::GET_FILE_RO:
|
|
|
|
if Iris::recv.data[1].h != 0:
|
2010-01-16 17:13:54 +02:00
|
|
|
kdebug ("index out of supported range\n")
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::panic (0)
|
2010-01-16 17:13:54 +02:00
|
|
|
//kdebug ("sending file\n")
|
2010-05-05 02:09:32 +03:00
|
|
|
Iris::Cap file = Iris::my_receiver.create_capability (Iris::Num (FILE, Iris::recv.data[1].l))
|
2010-01-16 17:13:54 +02:00
|
|
|
reply.invoke (0, 0, file.copy ())
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::free_cap (file)
|
|
|
|
Iris::free_cap (reply)
|
|
|
|
Iris::free_cap (arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
continue
|
2010-05-01 00:13:49 +03:00
|
|
|
case Iris::Directory::GET_FILE_INFO:
|
2009-12-26 15:17:06 +02:00
|
|
|
default:
|
2010-05-01 00:13:49 +03:00
|
|
|
reply.invoke (Iris::ERR_INVALID_OPERATION)
|
|
|
|
Iris::free_cap (reply)
|
|
|
|
Iris::free_cap (arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
continue
|
|
|
|
break
|
|
|
|
case FILE:
|
2012-09-26 20:03:36 +03:00
|
|
|
//Iris::debug ("file request %d\n", Iris::recv.data[0].l)
|
2010-05-01 00:13:49 +03:00
|
|
|
switch Iris::recv.data[0].l:
|
2010-09-02 00:27:14 +03:00
|
|
|
case Iris::Block::GET_BLOCK:
|
2010-06-07 00:03:25 +03:00
|
|
|
if Iris::recv.data[0].h != PAGE_SIZE << 16:
|
|
|
|
Iris::panic (0, "unsupported get_block arguments for boot usb device driver")
|
|
|
|
// Fall through.
|
2010-09-02 00:27:14 +03:00
|
|
|
case Iris::Block::GET_SIZE:
|
2010-05-05 02:09:32 +03:00
|
|
|
udc.send (Iris::recv.data[0].l | ((Iris::recv.data[1].l >> PAGE_BITS) << 16), Iris::recv.protected_data.h, reply, arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
continue
|
|
|
|
default:
|
2010-05-01 00:13:49 +03:00
|
|
|
reply.invoke (Iris::ERR_INVALID_OPERATION)
|
|
|
|
Iris::free_cap (reply)
|
|
|
|
Iris::free_cap (arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
continue
|
|
|
|
break
|
|
|
|
case NAME:
|
2012-09-26 20:03:36 +03:00
|
|
|
//Iris::debug ("name request %d\n", Iris::recv.data[0].l)
|
2010-05-01 00:13:49 +03:00
|
|
|
switch Iris::recv.data[0].l:
|
|
|
|
case Iris::String::GET_SIZE:
|
2010-01-16 17:13:54 +02:00
|
|
|
reply.invoke (16)
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::free_cap (reply)
|
|
|
|
Iris::free_cap (arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
continue
|
2010-05-01 00:13:49 +03:00
|
|
|
case Iris::String::GET_CHARS:
|
|
|
|
state = Iris::recv.data[0].l
|
2010-05-05 02:09:32 +03:00
|
|
|
udc.send (Iris::Directory::GET_NAME, Iris::recv.protected_data.h, reply, arg)
|
2010-01-16 17:13:54 +02:00
|
|
|
continue
|
|
|
|
default:
|
2010-05-01 00:13:49 +03:00
|
|
|
reply.invoke (Iris::ERR_INVALID_OPERATION)
|
|
|
|
Iris::free_cap (reply)
|
|
|
|
Iris::free_cap (arg)
|
2010-01-14 19:14:37 +02:00
|
|
|
continue
|
2010-05-05 02:09:32 +03:00
|
|
|
default:
|
|
|
|
kdebug ("other request:")
|
|
|
|
kdebug_num (Iris::recv.protected_data.l)
|
|
|
|
kdebug ("\n")
|
|
|
|
udc.log ('~')
|
|
|
|
char digit[] = "0123456789abcdef"
|
|
|
|
for unsigned i = 0; i < 8; ++i:
|
|
|
|
udc.log (digit[(Iris::recv.protected_data.l >> (4 * (7 - i))) & 0xf])
|
|
|
|
udc.log ('\n')
|
|
|
|
break
|
2010-01-14 19:14:37 +02:00
|
|
|
reply.invoke ()
|
2010-05-01 00:13:49 +03:00
|
|
|
Iris::free_cap (reply)
|
|
|
|
Iris::free_cap (arg)
|