#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 #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 = 10000 void boot (std::string const &filename, unsigned load, 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 = 0x80003000 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 () static std::string files ("fs") struct Name: char name[16] std::string full std::string content Name (std::string const &n): full = files + '/' + n memset (name, 0, 16) memcpy (name, n.data (), n.size () > 16 ? 16 : n.size ()) std::ifstream f (full.c_str ()) std::ostringstream s s << f.rdbuf () content = s.str () static std::vector dir 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 Iris::Directory::GET_SIZE: unsigned long long size = dir.size () std::cerr << "sending dir size\n" std::cerr << Iris::Directory::GET_SIZE << '\n' char *str = (char *)&size for unsigned i = 0; i < 8; ++i: std::cerr << " " << (unsigned)(str[i] & 0xff) std::cerr << '\n' if usb_control_msg (handle, USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, Iris::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 Iris::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 std::cerr << "sending filename " << dir[buffer[1]].full << "\n" if usb_control_msg (handle, USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, Iris::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 Iris::Directory::LOCK_RO: std::cerr << "lock\n" lock++ std::cerr << "freezing file list\n" shevek::dir d (files) dir.clear () for shevek::dir::const_iterator i = d.begin (); i != d.end (); ++i: if !i->name.empty () && i->name[0] != '.': dir.push_back (Name (i->name)) continue case Iris::Directory::UNLOCK_RO: std::cerr << "unlock\n" 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 Iris::Block::GET_BLOCK: if buffer[1] >= dir.size (): std::cerr << "reading invalid file" << std::endl usb_release_interface (handle, 0) usb_close (handle) handle = NULL return unsigned pos = buffer[0] >> 16 std::cerr << "getting data from " << pos << '\n' std::string page if dir[buffer[1]].content.size () <= pos << 12: page = std::string (1 << 12, 0) else: page = (dir[buffer[1]].content.substr (pos << 12, 1 << 12) + std::string (1 << 12, 0)).substr (0, 1 << 12) for unsigned i = 0; i < (1 << 12); i += 64: if usb_bulk_write (handle, 1 | USB_ENDPOINT_OUT, &page.data ()[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 case Iris::Block::GET_SIZE: if buffer[1] >= dir.size (): std::cerr << "reading invalid file size " << buffer[1] << " >= " << dir.size () << std::endl usb_release_interface (handle, 0) usb_close (handle) handle = NULL return unsigned long long size = dir[buffer[1]].content.size () std::cerr << "sending file size " << size << "\n" if usb_control_msg (handle, USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, Iris::String::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 default: std::cerr << "invalid request " << buffer[0] << 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 (0, 100000000)).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::ristring l (line) unsigned load, entry std::string filename if l ("reboot %x %x %a%", load, entry, filename): get_server ()->data ()->boot (filename, load, 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): std::cerr << shevek::ostring ("requesting %08x with data %08x\n", r, 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): std::cerr << shevek::ostring ("setting data address to 0x%08x\n", address) 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 std::cerr << shevek::ostring ("sent %d bytes\n", size) 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 (0, 100000000)).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 (std::string const &filename, unsigned load, unsigned entry): std::cerr << "booting " << shevek::ostring ("%s from %x@%x", Glib::ustring (filename), load, 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 file.close () file.open (filename.c_str ()) stage2 << file.rdbuf () std::cerr << shevek::ostring ("sending Iris (size 0x%x)\n", stage2.str ().size ()) send_file (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" static void dump_devices (): std::cerr << std::hex << "String: " << Iris::String::ID std::cerr << "\nBlock: " << Iris::Block::ID std::cerr << "\nWString: " << Iris::WString::ID std::cerr << "\nWBlock: " << Iris::WBlock::ID std::cerr << "\nBoot: " << Iris::Boot::ID std::cerr << "\nDevice: " << Iris::Device::ID std::cerr << "\nEvent: " << Iris::Event::ID std::cerr << "\nElfrun: " << Iris::Elfrun::ID std::cerr << "\nParent: " << Iris::Parent::ID std::cerr << "\nKeyboard: " << Iris::Keyboard::ID std::cerr << "\nBuzzer: " << Iris::Buzzer::ID std::cerr << "\nDisplay: " << Iris::Display::ID std::cerr << "\nFont: " << Iris::Display::ID std::cerr << "\nSetting: " << Iris::Setting::ID std::cerr << "\nDirectory: " << Iris::Directory::ID std::cerr << "\nWDirectory: " << Iris::WDirectory::ID std::cerr << "\nStream: " << Iris::Stream::ID std::cerr << "\nUI: " << Iris::UI::ID std::cerr << "\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") data d (port) dump_devices () shevek::bg () shevek::loop () return 0