#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)) 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 = 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)) static Device const device_descriptor static my_config const config_descriptor static String <7> const s_manufacturer static String <22> const s_product enum State: IDLE TX RX State state char configuration unsigned size char const *ptr bool rebooting bool vendor (Setup *s) bool get_descriptor (unsigned type, unsigned idx, unsigned len) bool handle_setup (Setup *s) public: void init () void log (unsigned c) void interrupt () Udc::Device const Udc::device_descriptor = { sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0x601a, 0x4740, 0x100, 1, 2, 0, 1 } Udc::my_config const 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 <7> const Udc::s_manufacturer = { sizeof (String <7>), String <7>::Type, { 'I', 'n', 'g', 'e', 'n', 'i', 'c' } } Udc::String <22> const Udc::s_product = { sizeof (String <22>), String <22>::Type, { 'J', 'Z', '4', '7', '4', '0', ' ', 'U', 'S', 'B', ' ', 'B', 'o', 'o', 't', ' ', 'D', 'e', 'v', 'i', 'c', 'e'} } void Udc::init (): state = IDLE configuration = 0 size = 0 // exit suspend mode by reading the interrupt register. 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 UDC_INDEX = 0 bool Udc::vendor (Setup *s): if s->request_type & 0x80: static char const *name = "abcdefgh" ptr = name size = 8 state = TX rebooting = true return true return true bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): switch type: case Configuration::Type: if idx != 1: 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 // The 7 is an arbitrary number. case String <7>::Type: switch idx: 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): kdebug ("udc: setup: type=") kdebug_num (s->request_type) kdebug ("; request=") kdebug_num (s->request) kdebug ("; value=") kdebug_num (s->value) kdebug ("; index=") kdebug_num (s->index) kdebug ("\n") 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 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) default: return false return true void Udc::interrupt (): unsigned i = UDC_INTRUSB if i & UDC_INTR_RESET: kdebug ("udc: reset\n") state = IDLE return i = UDC_INTRIN if i & (1 << 0): // Interrupt on endpoint 0. unsigned csr = UDC_CSR0 if csr & UDC_CSR0_SENTSTALL: csr &= ~UDC_CSR0_SENTSTALL 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) UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY if !handle_setup (&packet.s): csr |= UDC_CSR0_SENDSTALL break if size == 0: return // Fall through. case TX: kdebug ("udc: send next:") unsigned i for i = 0; (size & ~3) > 0 && i < max_packet_size0; i += 4, size -= 4: for unsigned i = 0; i < 4; ++i: kdebug_num (ptr[i], 2) UDC_FIFO (0) = *(unsigned *)ptr ptr += 4 for ; size > 0 && i < max_packet_size0; ++i, --size: kdebug_num (*ptr, 2) UDC_FIFO8 (0) = *ptr++ kdebug ("\n") if i == max_packet_size0: csr |= UDC_CSR0_INPKTRDY else: state = IDLE csr |= UDC_CSR0_INPKTRDY | UDC_CSR0_DATAEND break case RX: // Not supported. csr |= UDC_CSR0_SVDOUTPKTRDY | UDC_CSR0_SENDSTALL state = IDLE break UDC_CSR0 = csr void Udc::log (unsigned c): kdebug ("udc: log ") kdebug_char (c) kdebug ("\n") enum pdata: LOG = 32 Kernel::Num start (): map_udc () map_gpio () map_cpm () Udc udc udc.init () Kernel::Cap logcap = Kernel::my_receiver.create_capability (LOG) __asm__ volatile ("li $a0, 1\nlw $a1, %0\nbreak" :: "m"(logcap.code): "a0", "a1", "memory") Kernel::register_interrupt (IRQ_UDC) //Kernel::my_receiver.set_alarm (HZ) while true: Kernel::wait () switch Kernel::recv.protected_data.l: case ~0: // alarm. Kernel::my_receiver.set_alarm (HZ) kdebug ("alarm; d =") unsigned mask[4] = { 0x00000000, 0xfe000000, 0x09c00000, 0x3dfc8055 } for unsigned i = 0; i < 4; ++i: kdebug (" ") kdebug_num (gpio_get_port (i) & mask[i]) kdebug ("\n") break case IRQ_UDC: udc.interrupt () Kernel::register_interrupt (IRQ_UDC) break case LOG: udc.log (Kernel::recv.data[0].l) break default: udc.log ('~') break