1
0
mirror of git://projects.qi-hardware.com/iris.git synced 2024-11-05 06:04:59 +02:00
iris/boot-programs/udc.ccp

519 lines
15 KiB
Plaintext
Raw Normal View History

#pypp 0
// 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"
#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))
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
static unsigned const max_packet_size_bulk = 512
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
enum State:
IDLE
TX
RX
State state
char configuration
unsigned size
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)
2009-12-26 15:17:06 +02:00
char log_buffer[1000]
unsigned log_buffer_size
unsigned log_buffer_start
public:
void init ()
void log (unsigned c)
2010-01-14 19:14:37 +02:00
void interrupt (unsigned cmd)
void send (unsigned code, unsigned arg)
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 = {
(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 }
// }
// }
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' } }
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
// Disconnect from the bus and don't try to get high-speed.
UDC_POWER &= ~(UDC_POWER_SOFTCONN | UDC_POWER_HSENAB)
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 ()
unsigned i = UDC_INTRUSB
// reset all pending endpoint interrupts.
i = UDC_INTRIN
i = UDC_INTROUT
// enable interrupt on bus reset.
UDC_INTRUSB = UDC_INTR_RESET
// enable interrupts on endpoint 0.
UDC_INTRINE |= 1 << 0
2009-09-30 00:48:33 +03:00
UDC_INDEX = 0
2009-12-27 01:12:35 +02:00
// Wait a while.
for unsigned w = 0; w < 10000; ++w:
Kernel::schedule ()
2009-12-26 15:17:06 +02:00
// Connect to the host.
2009-12-18 23:27:26 +02:00
UDC_POWER |= UDC_POWER_SOFTCONN
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-14 19:14:37 +02:00
switch s->request:
case Directory::GET_SIZE:
//TODO
case Directory::GET_NAME:
//TODO
default:
kdebug ("invalid vendor request\n")
Kernel::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
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
2010-01-14 19:14:37 +02:00
void Udc::send (unsigned code, unsigned arg):
if code != ~0:
kdebug ("new code sent while old one wasn't finished.\n")
Kernel::panic (0)
cmd_code = code
cmd_arg = arg
2009-09-30 00:48:33 +03:00
bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len):
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
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
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:
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
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
break
default:
2009-09-30 00:48:33 +03:00
return false
2009-12-18 23:27:26 +02:00
state = TX
return true
2010-01-14 19:14:37 +02:00
bool Udc::handle_setup (Setup *s, unsigned cmd):
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
configuration = s->value
break
case SET_INTERFACE:
if s->value != 0:
2009-09-30 00:48:33 +03:00
return false
break
default:
2009-09-30 00:48:33 +03:00
return false
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
case GET_DESCRIPTOR:
2009-09-30 00:48:33 +03:00
return get_descriptor ((s->value >> 8) & 0xff, s->value & 0xff, s->length)
case GET_CONFIGURATION:
ptr = &configuration
size = (s->length < 1 ? s->length : 1)
2009-09-30 00:48:33 +03:00
state = TX
break
case GET_INTERFACE:
ptr = "\0"
size = (s->length < 1 ? s->length : 1)
2009-09-30 00:48:33 +03:00
state = TX
break
default:
2009-09-30 00:48:33 +03:00
return false
break
case VENDOR_TO_DEVICE:
case VENDOR_FROM_DEVICE:
2010-01-14 19:14:37 +02:00
return vendor (s, cmd)
default:
2009-09-30 00:48:33 +03:00
return false
return true
2010-01-14 19:14:37 +02:00
void Udc::interrupt (unsigned cmd):
unsigned i = UDC_INTRUSB
if i & UDC_INTR_RESET:
state = IDLE
return
i = UDC_INTRIN
if i & (1 << 0):
// Interrupt on endpoint 0.
unsigned csr = UDC_CSR0
if csr & UDC_CSR0_SENTSTALL:
2009-12-18 23:27:26 +02:00
csr &= ~(UDC_CSR0_SENTSTALL | UDC_CSR0_SENDSTALL)
state = IDLE
if csr & UDC_CSR0_SETUPEND:
csr |= UDC_CSR0_SVDSETUPEND
state = IDLE
switch state:
case IDLE:
2009-10-04 20:47:20 +03:00
if rebooting:
Kernel::reboot ()
if !(csr & UDC_CSR0_OUTPKTRDY):
2009-09-30 00:48:33 +03:00
return
union { unsigned d[2]; Setup s; } packet
2009-09-30 00:48:33 +03:00
packet.d[0] = UDC_FIFO (0)
packet.d[1] = UDC_FIFO (0)
UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY
2010-01-14 19:14:37 +02:00
if !handle_setup (&packet.s, cmd):
2009-09-30 00:48:33 +03:00
csr |= UDC_CSR0_SENDSTALL
break
if size == 0:
return
// Fall through.
case TX:
2009-09-30 00:48:33 +03:00
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:
state = IDLE
csr |= UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND
break
case RX:
// Not supported.
2009-09-30 00:48:33 +03:00
csr |= UDC_CSR0_SVDOUTPKTRDY | UDC_CSR0_SENDSTALL
state = IDLE
break
2009-09-30 00:48:33 +03:00
UDC_CSR0 = csr
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:
LOG = 32
2009-12-26 15:17:06 +02:00
FS
2010-01-14 19:14:37 +02:00
DATA
DIRECTORY
Kernel::Num start ():
map_udc ()
2009-10-04 20:47:20 +03:00
map_gpio ()
2009-12-11 10:43:42 +02:00
map_cpm ()
Udc udc
2009-10-04 20:47:20 +03:00
Kernel::Cap logcap = Kernel::my_receiver.create_capability (LOG)
__asm__ volatile ("li $a0, 1\nlw $a1, %0\nbreak" :: "m"(logcap.code): "a0", "a1", "memory")
2009-12-26 15:17:06 +02:00
udc.init ()
Kernel::register_interrupt (IRQ_UDC)
2009-12-26 15:17:06 +02:00
Device fs_dev = Kernel::my_receiver.create_capability (FS)
Kernel::my_parent.provide_device <Directory> (fs_dev)
2010-01-14 19:14:37 +02:00
unsigned data_current_user = 0, fs_current_user = 0
2009-12-26 15:17:06 +02:00
unsigned next_user
2010-01-14 19:14:37 +02:00
unsigned state = 0
while true:
Kernel::wait ()
2010-01-14 19:14:37 +02:00
Kernel::Cap reply = Kernel::get_reply ()
2009-12-26 15:17:06 +02:00
switch Kernel::recv.protected_data.h:
case 0:
switch Kernel::recv.protected_data.l:
case IRQ_UDC:
2010-01-14 19:14:37 +02:00
udc.interrupt (state)
2009-12-26 15:17:06 +02:00
Kernel::register_interrupt (IRQ_UDC)
break
case LOG:
udc.log (Kernel::recv.data[0].l)
break
case FS:
2010-01-14 19:14:37 +02:00
Device::host (FS, fs_current_user, reply)
continue
case DATA:
Device::host (DATA, data_current_user, reply)
continue
2009-12-26 15:17:06 +02:00
default:
udc.log ('~')
char digit[] = "0123456789abcdef"
for unsigned i = 0; i < 8; ++i:
udc.log (digit[(Kernel::recv.protected_data.l >> (4 * (7 - i))) & 0xf])
udc.log ('\n')
break
2010-01-14 19:14:37 +02:00
case DATA:
if data_current_user != Kernel::recv.protected_data.l:
break
switch Kernel::recv.data[0].l:
case String::GET_SIZE:
case String::GET_CHARS:
reply.invoke (0)
Kernel::free_cap (reply)
continue
case String::GET_PAGE:
default:
reply.invoke (Kernel::ERR_INVALID_OPERATION)
Kernel::free_cap (reply)
continue
break
2009-12-26 15:17:06 +02:00
case FS:
2010-01-14 19:14:37 +02:00
if fs_current_user != Kernel::recv.protected_data.l:
2009-12-26 15:17:06 +02:00
break
2010-01-14 19:14:37 +02:00
switch Kernel::recv.data[0].l:
case Filesystem::USE_DEVICE:
case Filesystem::USE_DEVICE_RO:
Directory dir = Kernel::my_receiver.create_capability (Kernel::Num (0, DIRECTORY))
reply.invoke (0, 0, dir.copy ())
Kernel::free_cap (dir)
Kernel::free_cap (reply)
continue
default:
reply.invoke (Kernel::ERR_INVALID_OPERATION)
Kernel::free_cap (reply)
continue
case DIRECTORY:
2009-12-26 15:17:06 +02:00
switch Kernel::recv.data[0].l:
case Directory::GET_SIZE:
case Directory::GET_NAME:
case Directory::LOCK_RO:
case Directory::UNLOCK_RO:
2010-01-14 19:14:37 +02:00
state = Kernel::recv.data[0].l
if Kernel::recv.data[1].h != 0:
kdebug ("index out of supported range\n")
Kernel::panic (0)
udc.send (Kernel::recv.data[0].l, Kernel::recv.data[1].l)
Kernel::free_cap (reply)
continue
case Directory::GET_FILE_RO:
//TODO
2009-12-26 15:17:06 +02:00
case Directory::GET_FILE_INFO:
default:
2010-01-14 19:14:37 +02:00
reply.invoke (Kernel::ERR_INVALID_OPERATION)
Kernel::free_cap (reply)
continue
reply.invoke ()
Kernel::free_cap (reply)