#pypp 0 // Iris: micro-kernel for a capability-based operating system. // boot-programs/udc.ccp: USB device controller driver. // Copyright 2009 Bas Wijnen // // 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 . #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)) /**/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 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 = 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 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)) 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 unsigned rx_request char const *ptr unsigned cmd_code, cmd_arg bool rebooting bool vendor (Setup *s, unsigned cmd) bool get_descriptor (unsigned type, unsigned idx, unsigned len) bool handle_setup (Setup *s, unsigned cmd) void irq_usb (unsigned cmd) void irq_in (unsigned cmd) void irq_out (unsigned cmd) char log_buffer[1000] unsigned log_buffer_size unsigned log_buffer_start Kernel::Cap caller, caller_arg bool have_caller unsigned *page unsigned *p Kernel::Page buffer_page public: void init () void log (unsigned c) void interrupt (unsigned cmd) void send (unsigned code, unsigned narg, Kernel::Cap reply, Kernel::Cap arg) 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 } } } 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 (): // 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' } } have_caller = false log_buffer_size = 0 log_buffer_start = 0 cmd_code = ~0 cmd_arg = 0 // 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 buffer_page = Kernel::my_memory.create_page () buffer_page.set_flags (Kernel::Page::FRAME | Kernel::Page::PAYING, Kernel::Page::FRAME | Kernel::Page::PAYING) Kernel::my_memory.map (buffer_page, (unsigned)page) // Disconnect from the bus and don't try to get high-speed. UDC_POWER = 0 UDC_TESTMODE = 0 UDC_INDEX = 0 state = IDLE configuration = 0 size = 0 // exit suspend mode by reading the interrupt register. cpm_start_udc () // reset all pending endpoint interrupts. unsigned i = UDC_INTRUSB i = UDC_INTRIN i = UDC_INTROUT // enable interrupt on bus reset. UDC_INTRUSBE = UDC_INTR_RESET // enable interrupts on endpoint 0. UDC_INTRINE = 1 << 0 // and on out endpoint 1. UDC_INTROUTE = 1 << 1 // Wait a while. for unsigned w = 0; w < 10000; ++w: Kernel::schedule () // Connect to the host. UDC_POWER = UDC_POWER_SOFTCONN bool Udc::vendor (Setup *s, unsigned cmd): if !(s->request_type & 0x80): kdebug ("data to device without size\n") Kernel::panic (0) return true if s->request == 10: 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 if cmd_code == Directory::LOCK_RO || cmd_code == Directory::UNLOCK_RO: caller.invoke () Kernel::free_cap (caller) Kernel::free_cap (caller_arg) have_caller = false //kdebug ("(un)lock response\n") cmd_code = ~0 else: 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 else: static char const *name = "Reboot" ptr = name size = s->length < 6 ? s->length : 6 rebooting = true state = TX return true void Udc::send (unsigned code, unsigned narg, Kernel::Cap reply, Kernel::Cap arg): if cmd_code != ~0: kdebug ("new code sent while old one wasn't finished.\n") Kernel::panic (0) cmd_code = code cmd_arg = narg caller = reply caller_arg = arg have_caller = true bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): switch type: case Configuration::Type: if idx != 0: return false ptr = reinterpret_cast (&config_descriptor) size = (len < sizeof (config_descriptor) ? len : sizeof (config_descriptor)) break case Device::Type: if idx != 0: return false ptr = reinterpret_cast (&device_descriptor) size = (len < sizeof (device_descriptor) ? len : sizeof (device_descriptor)) break case Device_Qualifier::Type: //if idx != 0: // return false //ptr = reinterpret_cast (&device_qualifier_descriptor) //size = (len < sizeof (device_qualifier_descriptor) ? len : sizeof (device_qualifier_descriptor)) //break return false // The 6 is an arbitrary number, except that String <6> is instantiated already. case String <6>::Type: switch idx: case 0: ptr = reinterpret_cast (&s_langs) size = (len < sizeof (s_langs) ? len : sizeof (s_langs)) break case 1: ptr = reinterpret_cast (&s_manufacturer) size = (len < sizeof (s_manufacturer) ? len : sizeof (s_manufacturer)) break case 2: ptr = reinterpret_cast (&s_product) size = (len < sizeof (s_product) ? len : sizeof (s_product)) break default: return false break default: return false state = TX return true 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: return false configuration = s->value break case SET_INTERFACE: if s->value != 0: return false break default: return false UDC_OUTMAXP = 64 UDC_OUTCSR = UDC_OUTCSR_CDT | UDC_OUTCSR_FF UDC_OUTCSR = UDC_OUTCSR_CDT | UDC_OUTCSR_FF break case STANDARD_FROM_DEVICE: switch s->request: case GET_STATUS: ptr = "\0\0" size = (s->length < 2 ? s->length : 2) state = TX break case GET_DESCRIPTOR: return get_descriptor ((s->value >> 8) & 0xff, s->value & 0xff, s->length) case GET_CONFIGURATION: ptr = &configuration size = (s->length < 1 ? s->length : 1) state = TX break case GET_INTERFACE: ptr = "\0" size = (s->length < 1 ? s->length : 1) state = TX break default: return false break case VENDOR_TO_DEVICE: case VENDOR_FROM_DEVICE: return vendor (s, cmd) default: return false return true void Udc::irq_usb (unsigned cmd): // Reset. 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) state = IDLE if csr & UDC_CSR0_SETUPEND: csr |= UDC_CSR0_SVDSETUPEND state = IDLE switch state: case IDLE: if rebooting: Kernel::reboot () 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 break 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: state = IDLE 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: case Directory::GET_SIZE & 0xff: if !have_caller: kdebug ("received dir size from server without a caller waiting\n") Kernel::panic (0) unsigned size_l = UDC_FIFO (0) unsigned size_h = UDC_FIFO (0) caller.invoke (Kernel::Num (size_l, size_h)) Kernel::free_cap (caller) Kernel::free_cap (caller_arg) have_caller = false //kdebug ("get_size response\n") break case Directory::GET_NAME & 0xff: if !have_caller: kdebug ("received filename from server without a caller waiting\n") Kernel::panic (0) unsigned n[4] for unsigned i = 0; i < 4; ++i: n[i] = UDC_FIFO (0) caller.invoke (Kernel::Num (n[0], n[1]), Kernel::Num (n[2], n[3])) Kernel::free_cap (caller) Kernel::free_cap (caller_arg) //kdebug ("get_name response\n") have_caller = false break case ::String::GET_SIZE & 0xff: if !have_caller: kdebug ("received string size from server without a caller waiting\n") Kernel::panic (0) unsigned size_l = UDC_FIFO (0) unsigned size_h = UDC_FIFO (0) caller.invoke (Kernel::Num (size_l, size_h)) Kernel::free_cap (caller) Kernel::free_cap (caller_arg) have_caller = false //kdebug ("get_filesize response\n") break case ::String::GET_CHARS & 0xff: if !have_caller: kdebug ("received string char data from server without a caller waiting\n") Kernel::panic (0) unsigned n[4] for unsigned i = 0; i < 4; ++i: n[i] = UDC_FIFO (0) caller.invoke (Kernel::Num (n[0], n[1]), Kernel::Num (n[2], n[3])) Kernel::free_cap (caller) Kernel::free_cap (caller_arg) have_caller = false //kdebug ("get_chars response\n") break default: kdebug ("invalid vendor request: ") kdebug_num (rx_request) kdebug ("\n") Kernel::panic (0) 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") Kernel::panic (0) unsigned size = UDC_OUTCOUNT unsigned csr = UDC_OUTCSR //kdebug ("handling bulk interrupt for ") //kdebug_num (csr) //kdebug (" with ") //kdebug_num (size) //kdebug (" bytes.\n") csr &= ~UDC_OUTCSR_OUTPKTRDY for unsigned i = 0; i < size; i += 4: *p++ = UDC_FIFO (1) if p - page == PAGE_SIZE >> 2: buffer_page.share (caller_arg, Kernel::Page::FORGET) buffer_page.set_flags (Kernel::Page::FRAME, Kernel::Page::FRAME) caller.invoke () Kernel::free_cap (caller) Kernel::free_cap (caller_arg) 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 //kdebug ("interrupt: ") //kdebug_num (usb, 1) //kdebug ("/") //kdebug_num (in, 1) //kdebug ("/") //kdebug_num (out, 1) //kdebug ("\n") if usb & 4: irq_usb (cmd) if in & 1: irq_in (cmd) if out & 2: irq_out (cmd) void Udc::log (unsigned c): if log_buffer_size >= sizeof (log_buffer): return log_buffer[log_buffer_size++] = c enum pdata: LOG = 32 FS DATA DIRECTORY FILE NAME Kernel::Num start (): map_udc () map_gpio () map_cpm () Udc udc //Kernel::Cap logcap = Kernel::my_receiver.create_capability (LOG) //__asm__ volatile ("li $a0, 1\nlw $a1, %0\nbreak" :: "m"(logcap.code): "a0", "a1", "memory") udc.init () Kernel::register_interrupt (IRQ_UDC) Device fs_dev = Kernel::my_receiver.create_capability (FS) Device data_dev = Kernel::my_receiver.create_capability (DATA) Kernel::my_parent.provide_device (fs_dev.copy ()) Kernel::my_parent.provide_device (data_dev.copy ()) Kernel::free_cap (fs_dev) Kernel::free_cap (data_dev) unsigned data_current_user = 0, fs_current_user = 0 unsigned next_user unsigned state = 0 while true: Kernel::wait () Kernel::Cap reply = Kernel::get_reply () Kernel::Cap arg = Kernel::get_arg () switch Kernel::recv.protected_data.h: case 0: switch Kernel::recv.protected_data.l: case IRQ_UDC: udc.interrupt (state) Kernel::register_interrupt (IRQ_UDC) break case LOG: udc.log (Kernel::recv.data[0].l) break case FS: Device::host (FS, fs_current_user, reply, arg) continue case DATA: Device::host (DATA, data_current_user, reply, arg) continue 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 break 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) Kernel::free_cap (arg) continue case ::String::GET_PAGE: default: reply.invoke (Kernel::ERR_INVALID_OPERATION) Kernel::free_cap (reply) Kernel::free_cap (arg) continue break case FS: if fs_current_user != Kernel::recv.protected_data.l: break 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) Kernel::free_cap (arg) continue default: reply.invoke (Kernel::ERR_INVALID_OPERATION) Kernel::free_cap (reply) Kernel::free_cap (arg) continue break case DIRECTORY: switch Kernel::recv.data[0].l: case Directory::GET_NAME: Kernel::Cap name = Kernel::my_receiver.create_capability (Kernel::Num (Kernel::recv.data[1].l, NAME)) reply.invoke (0, 0, name.copy ()) Kernel::free_cap (name) Kernel::free_cap (reply) Kernel::free_cap (arg) continue case Directory::GET_SIZE: case Directory::LOCK_RO: case Directory::UNLOCK_RO: 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, reply, arg) continue case Directory::GET_FILE_RO: if Kernel::recv.data[1].h != 0: kdebug ("index out of supported range\n") Kernel::panic (0) //kdebug ("sending file\n") Kernel::Cap file = Kernel::my_receiver.create_capability (Kernel::Num (Kernel::recv.data[1].l, FILE)) reply.invoke (0, 0, file.copy ()) Kernel::free_cap (file) Kernel::free_cap (reply) Kernel::free_cap (arg) continue case Directory::GET_FILE_INFO: default: reply.invoke (Kernel::ERR_INVALID_OPERATION) Kernel::free_cap (reply) Kernel::free_cap (arg) continue break case FILE: switch Kernel::recv.data[0].l: case ::String::GET_SIZE: case ::String::GET_CHARS: case ::String::GET_PAGE: udc.send (Kernel::recv.data[0].l | ((Kernel::recv.data[1].l >> PAGE_BITS) << 16), Kernel::recv.protected_data.l, reply, arg) continue default: reply.invoke (Kernel::ERR_INVALID_OPERATION) Kernel::free_cap (reply) Kernel::free_cap (arg) continue break case NAME: switch Kernel::recv.data[0].l: case ::String::GET_SIZE: reply.invoke (16) Kernel::free_cap (reply) Kernel::free_cap (arg) continue case ::String::GET_CHARS: state = Kernel::recv.data[0].l udc.send (Directory::GET_NAME, Kernel::recv.protected_data.l, reply, arg) continue default: reply.invoke (Kernel::ERR_INVALID_OPERATION) Kernel::free_cap (reply) Kernel::free_cap (arg) continue reply.invoke () Kernel::free_cap (reply) Kernel::free_cap (arg)