#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 #include #include #include "devices.hh" 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 POLL = 10 void request (requests num, unsigned data = 0) void send_file (unsigned address, unsigned size, char const *data) void reboot (): char buffer[8] if usb_control_msg (handle, USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, VR_GET_CPU_INFO, 0, 0, buffer, 8, timeout) < 0: std::cerr << "unable to send reboot message to device: " << usb_strerror () << std::endl usb_release_interface (handle, 0) usb_close (handle) handle = NULL void get_device (unsigned vendor, unsigned product, unsigned tries) void poll () struct Name: char name[16] std::string full Name (std::string const &n): full = n memset (name, 0, 16) memcpy (name, n.data (), n.size () > 16 ? 16 : n.size ()) static std::vector dir static std::string files (".") unsigned lock (0) void data::poll (): while true: unsigned buffer[2] int s = usb_control_msg (handle, USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, POLL, 0, 0, (char *)buffer, 8, timeout) if s != 8: std::cerr << "unable to send poll message to device: " << usb_strerror () << std::endl usb_release_interface (handle, 0) usb_close (handle) handle = NULL return switch buffer[0] & 0xffff: case ~0 & 0xffff: // Log character. std::cout << (char)buffer[1] << std::flush continue case ~1 & 0xffff: // No event. break case Directory::GET_SIZE: unsigned long long size = dir.size () if usb_control_msg (handle, USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, Directory::GET_SIZE, 0, 0, (char *)&size, 8, timeout) != 8: std::cerr << "unable to send size to device: " << usb_strerror () << std::endl usb_release_interface (handle, 0) usb_close (handle) handle = NULL return continue case Directory::GET_NAME: if buffer[1] >= dir.size (): std::cerr << "invalid file name requested" << std::endl; usb_release_interface (handle, 0) usb_close (handle) handle = NULL return if usb_control_msg (handle, USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, Directory::GET_NAME, 0, 0, dir[buffer[1]].name, 16, timeout) != 16: std::cerr << "unable to send name to device: " << usb_strerror () << std::endl usb_release_interface (handle, 0) usb_close (handle) handle = NULL return continue case Directory::LOCK_RO: if !lock++: shevek::dir d (files) dir.clear () for shevek::dir::const_iterator i = d.begin (); i == d.end (); ++i: dir.push_back (Name (i->name)) continue case Directory::UNLOCK_RO: if !lock: std::cerr << "unlocking without lock" << std::endl usb_release_interface (handle, 0) usb_close (handle) handle = NULL return if !--lock: dir.clear () continue case Directory::GET_FILE_RO: unsigned f = buffer[0] >> 16 if f >= dir.size (): std::cerr << "reading invalid file" << std::endl usb_release_interface (handle, 0) usb_close (handle) handle = NULL return std::ifstream file (dir[f].full.c_str ()) file.seekg (buffer[1] << 12) char page[1 << 12] memset (page, 0, 1 << 12) file.read (page, 1 << 12) for unsigned i = 0; i < (1 << 12); i += 64: if usb_bulk_write (handle, 1 | USB_ENDPOINT_OUT, &page[i], 64, timeout) != 64: std::cerr << "unable to send file to device: " << usb_strerror () << std::endl usb_release_interface (handle, 0) usb_close (handle) handle = NULL return continue default: std::cerr << "invalid request" << std::endl usb_release_interface (handle, 0) usb_close (handle) handle = NULL return // If the code reaches this point, break out of the loop. The loop continues if a continue statement is reached. break (shevek::absolute_time () + shevek::relative_time (1, 0)).schedule (sigc::mem_fun (*this, &data::poll)) 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 if l ("shutdown%"): std::cerr << "shutting down\n" shevek::end_loop () 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: " << usb_strerror () << "\n" usb_close (handle) handle = NULL continue (shevek::absolute_time () + shevek::relative_time (1, 0)).schedule (sigc::mem_fun (*this, &data::poll)) 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