From ad2f531ab08be6d3f7baaf50da3d6d56123e4d57 Mon Sep 17 00:00:00 2001 From: Bas Wijnen Date: Fri, 18 Dec 2009 22:27:26 +0100 Subject: [PATCH] use usb server for communication --- boot-programs/udc.ccp | 140 +++++++++++++++------- mips/nanonote/Makefile.arch | 7 +- mips/nanonote/server/Makefile.am | 30 +++++ mips/nanonote/server/configure.ac | 9 ++ mips/nanonote/server/usb-server.ccp | 179 ++++++++++++++++++++++++++++ 5 files changed, 321 insertions(+), 44 deletions(-) create mode 100644 mips/nanonote/server/Makefile.am create mode 100644 mips/nanonote/server/configure.ac create mode 100644 mips/nanonote/server/usb-server.ccp diff --git a/boot-programs/udc.ccp b/boot-programs/udc.ccp index 7b46b32..06722b5 100644 --- a/boot-programs/udc.ccp +++ b/boot-programs/udc.ccp @@ -83,6 +83,24 @@ class Udc: 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; @@ -118,10 +136,12 @@ class Udc: 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 + 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 @@ -139,17 +159,43 @@ class Udc: 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 = { +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 <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'} } +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' } } + + // 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 @@ -164,12 +210,15 @@ void Udc::init (): // enable interrupts on endpoint 0. UDC_INTRINE |= 1 << 0 UDC_INDEX = 0 + // Wait a bit and connect to the host. + Kernel::my_receiver.sleep (10) + UDC_POWER |= UDC_POWER_SOFTCONN bool Udc::vendor (Setup *s): if s->request_type & 0x80: - static char const *name = "abcdefgh" + static char const *name = "Reboot" ptr = name - size = 8 + size = s->length < 6 ? s->length : 6 state = TX rebooting = true return true @@ -178,20 +227,34 @@ bool Udc::vendor (Setup *s): bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): switch type: case Configuration::Type: - if idx != 1: + //kdebug ("config\n") + if idx != 0: return false ptr = reinterpret_cast (&config_descriptor) size = (len < sizeof (config_descriptor) ? len : sizeof (config_descriptor)) break case Device::Type: + //kdebug ("device\n") 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: + 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> in instantiated already. + case String <6>::Type: + //kdebug ("string\n") 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)) @@ -205,19 +268,21 @@ bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): break default: return false - state = TX - return true + 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") + //kdebug ("udc: setup: type=") + //kdebug_num (s->request_type, 2) + //kdebug ("; request=") + //kdebug_num (s->request, 2) + //kdebug ("; value=") + //kdebug_num (s->value, 4) + //kdebug ("; index=") + //kdebug_num (s->index, 4) + //kdebug ("; length=") + //kdebug_num (s->length, 4) + //kdebug ("\n") switch s->request_type: case STANDARD_TO_DEVICE: switch s->request: @@ -268,7 +333,7 @@ bool Udc::handle_setup (Setup *s): void Udc::interrupt (): unsigned i = UDC_INTRUSB if i & UDC_INTR_RESET: - kdebug ("udc: reset\n") + //kdebug ("udc: reset\n") state = IDLE return i = UDC_INTRIN @@ -276,7 +341,7 @@ void Udc::interrupt (): // Interrupt on endpoint 0. unsigned csr = UDC_CSR0 if csr & UDC_CSR0_SENTSTALL: - csr &= ~UDC_CSR0_SENTSTALL + csr &= ~(UDC_CSR0_SENTSTALL | UDC_CSR0_SENDSTALL) state = IDLE if csr & UDC_CSR0_SETUPEND: csr |= UDC_CSR0_SVDSETUPEND @@ -292,23 +357,24 @@ void Udc::interrupt (): packet.d[1] = UDC_FIFO (0) UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY if !handle_setup (&packet.s): + //kdebug ("stall 1\n") csr |= UDC_CSR0_SENDSTALL break if size == 0: return // Fall through. case TX: - kdebug ("udc: send next:") + //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) + //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) + //kdebug_num (*ptr, 2) UDC_FIFO8 (0) = *ptr++ - kdebug ("\n") + //kdebug ("\n") if i == max_packet_size0: csr |= UDC_CSR0_INPKTRDY else: @@ -317,6 +383,7 @@ void Udc::interrupt (): break case RX: // Not supported. + //kdebug ("stall 2\n") csr |= UDC_CSR0_SVDOUTPKTRDY | UDC_CSR0_SENDSTALL state = IDLE break @@ -340,20 +407,9 @@ Kernel::Num start (): 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) diff --git a/mips/nanonote/Makefile.arch b/mips/nanonote/Makefile.arch index f060ca0..bcaa77c 100644 --- a/mips/nanonote/Makefile.arch +++ b/mips/nanonote/Makefile.arch @@ -30,10 +30,13 @@ boot_sources = mips/init.cc mips/nanonote/board.cc arch_headers = mips/arch.hh mips/nanonote/jz4740.hh mips/nanonote/board.hh boot_threads = init udc nanonote-gpio buzzer metronome lcd -test: iris.raw nanonote-boot - ./nanonote-boot iris.raw 0xa$(shell /bin/sh -c '$(OBJDUMP) -t iris.elf | grep __start$$ | cut -b2-8') +test: iris.raw mips/nanonote/server/usb-server + echo "reboot 0xa$(shell /bin/sh -c '$(OBJDUMP) -t iris.elf | grep __start$$ | cut -b2-8')" | nc localhost 5050 .PHONY: test +mips/nanonote/server/usb-server: mips/nanonote/server/usb-server.ccp mips/nanonote/server/Makefile.am mips/nanonote/server/configure.ac + $(MAKE) -C mips/nanonote/server + %.elf: %.o $(LD) $(LDFLAGS) -o $@ $< diff --git a/mips/nanonote/server/Makefile.am b/mips/nanonote/server/Makefile.am new file mode 100644 index 0000000..f75cd03 --- /dev/null +++ b/mips/nanonote/server/Makefile.am @@ -0,0 +1,30 @@ +# Iris: micro-kernel for a capability-based operating system. +# mips/nanonote/server/Makefile.am: build rules +# 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 . + +AUTOMAKE_OPTIONS = foreign + +bin_PROGRAMS = usb-server + +usb_server_SOURCES = usb-server.cc +usb_server_CPPFLAGS = $(SHEVEK_CFLAGS) -DSTAGE1_FILE=\"mips/nanonote/sdram-setup.raw\" -DSTAGE2_FILE=\"iris.raw\" +usb_server_LDFLAGS = $(SHEVEK_LIBS) -lusb + +PYPP = /usr/bin/pypp +%.cc: %.ccp + $(PYPP) --name $< < $< > $@ + +BUILT_SOURCES = usb-server.cc diff --git a/mips/nanonote/server/configure.ac b/mips/nanonote/server/configure.ac new file mode 100644 index 0000000..6e66387 --- /dev/null +++ b/mips/nanonote/server/configure.ac @@ -0,0 +1,9 @@ +AC_INIT(usb-server, 1.0, wijnen@debian.org) +AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) + +AC_PROG_CXX + +PKG_CHECK_MODULES(SHEVEK, shevek,, AC_MSG_ERROR(You need libshevek to compile AC_PACKAGE_NAME)) + +AC_CONFIG_FILES(Makefile) +AC_OUTPUT diff --git a/mips/nanonote/server/usb-server.ccp b/mips/nanonote/server/usb-server.ccp new file mode 100644 index 0000000..bf84445 --- /dev/null +++ b/mips/nanonote/server/usb-server.ccp @@ -0,0 +1,179 @@ +#pypp 0 +// Iris: micro-kernel for a capability-based operating system. +// mips/nanonote/server/usb-server.ccp: Host-side helper for USB. +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + +struct client + +struct data: + Glib::RefPtr > server + usb_dev_handle *handle + static int const boot_vendor = 0x601a + static int const boot_product = 0x4740 + static int const run_vendor = 0xfffe + static int const run_product = 0x0002 + static unsigned const timeout = 1000 + void boot (unsigned entry) + data (std::string const &port): + handle = NULL + server = shevek::server ::create () + server->data () = this + server->open (port) + + private: + static unsigned const STAGE1_LOAD = 0x80002000 + static unsigned const STAGE2_LOAD = 0x80000000 + static unsigned const STAGE1_ENTRY = STAGE1_LOAD + enum requests: + VR_GET_CPU_INFO = 0 + VR_SET_DATA_ADDRESS = 1 + VR_SET_DATA_LENGTH = 2 + VR_FLUSH_CACHES = 3 + VR_PROGRAM_START1 = 4 + VR_PROGRAM_START2 = 5 + void request (requests num, unsigned data = 0) + void send_file (unsigned address, unsigned size, char const *data) + void reboot (): + // This always fails, because the device doesn't answer. However, that means there's nothing wrong, so don't complain. + usb_control_msg (handle, USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, VR_GET_CPU_INFO, 0, 0, NULL, 8, timeout) + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + void get_device (unsigned vendor, unsigned product, unsigned tries) + +struct client : public shevek::server ::connection: + static Glib::RefPtr create (): + return Glib::RefPtr (new client ()) + bool keep + void pickup (bool is_stdio): + keep = is_stdio + void read (std::string const &line): + shevek::istring l (line) + unsigned entry + if l ("reboot %x%", entry): + get_server ()->data ()->boot (entry) + else: + out->write ("invalid command\n") + if !keep: + out->write_block () + disconnect () + +void data::request (requests r, unsigned data): + if usb_control_msg (handle, USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, r, (data >> 16) & 0xffff, data & 0xffff, NULL, 0, timeout) < 0: + std::cerr << "unable to send control message to NanoNote: " << usb_strerror () << ".\n" + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + +void data::send_file (unsigned address, unsigned size, char const *data): + request (VR_SET_DATA_ADDRESS, address) + char const *ptr = data + while ptr - data < size: + int ret = usb_bulk_write (handle, 1, ptr, size - (ptr - data), timeout) + if ret <= 0: + std::cerr << "failed to write to NanoNote.\n" + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + return + ptr += ret + +void data::get_device (unsigned vendor, unsigned product, unsigned tries): + for unsigned i = 0; i < tries; ++i: + usb_find_busses () + usb_find_devices () + for struct usb_bus *bus = usb_busses; bus; bus = bus->next: + for struct usb_device *dev = bus->devices; dev; dev = dev->next: + if dev->descriptor.idProduct != product || dev->descriptor.idVendor != vendor: + //std::cerr << shevek::ostring ("Not using %04x:%04x when looking for %04x:%04x\n", dev->descriptor.idVendor, dev->descriptor.idProduct, vendor, product) + continue + handle = usb_open (dev) + if usb_claim_interface (handle, 0) < 0: + std::cerr << "unable to claim interface\n" + usb_close (handle) + handle = NULL + continue + return + if i + 1 < tries: + //std::cerr << "failed to find device, still trying...\n" + sleep (1) + std::cerr << shevek::ostring ("giving up finding device %04x:%04x\n", vendor, product) + +void data::boot (unsigned entry): + std::cerr << "booting " << shevek::ostring ("%x", entry) << "\n" + if handle: + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + get_device (boot_vendor, boot_product, 1) + if !handle: + get_device (run_vendor, run_product, 1) + if !handle: + std::cerr << "unable to find device\n" + return + reboot () + get_device (boot_vendor, boot_product, 5) + if !handle: + std::cerr << "unable to reboot device\n" + return + std::cerr << "sending stage 1\n" + std::ifstream file (STAGE1_FILE) + std::ostringstream stage1 + stage1 << file.rdbuf () + send_file (STAGE1_LOAD, stage1.str ().size (), stage1.str ().data ()) + std::cerr << "running stage 1\n" + request (VR_PROGRAM_START1, STAGE1_ENTRY) + usleep (100) + std::ostringstream stage2 + usb_release_interface (handle, 0) + file.close () + file.open (STAGE2_FILE) + stage2 << file.rdbuf () + std::cerr << "sending Iris\n" + send_file (STAGE2_LOAD, stage2.str ().size (), stage2.str ().data ()) + std::cerr << "flushing caches\n" + request (VR_FLUSH_CACHES) + std::cerr << "running Iris\n" + request (VR_PROGRAM_START2, entry) + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + get_device (run_vendor, run_product, 5) + if !handle: + std::cerr << "unable to open booted device\n" + return + std::cerr << "(re)booted NanoNote\n" + +int main (int argc, char **argv): + usb_init () + std::string port ("5050") + shevek::args::option opts[] = { + shevek::args::option ('p', "port", "port to listen for commands", true, port) + } + shevek::args args (argc, argv, opts, 0, 0, "device server for testing Iris on NanoNote", "2009") + data d (port) + shevek::loop () + return 0