Root/
| 1 | #pypp 0 |
| 2 | // Iris: micro-kernel for a capability-based operating system. |
| 3 | // mips/nanonote/server/usb-server.ccp: Host-side helper for USB. |
| 4 | // Copyright 2009 Bas Wijnen <wijnen@debian.org> |
| 5 | // |
| 6 | // This program is free software: you can redistribute it and/or modify |
| 7 | // it under the terms of the GNU General Public License as published by |
| 8 | // the Free Software Foundation, either version 3 of the License, or |
| 9 | // (at your option) any later version. |
| 10 | // |
| 11 | // This program is distributed in the hope that it will be useful, |
| 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | // GNU General Public License for more details. |
| 15 | // |
| 16 | // You should have received a copy of the GNU General Public License |
| 17 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 18 | |
| 19 | #include <unistd.h> |
| 20 | #include <usb.h> |
| 21 | #include <fstream> |
| 22 | #include <sstream> |
| 23 | #include <iostream> |
| 24 | #include <iomanip> |
| 25 | #include <cstring> |
| 26 | #include <shevek/mainloop.hh> |
| 27 | #include <shevek/server.hh> |
| 28 | #include <shevek/args.hh> |
| 29 | #include <shevek/dir.hh> |
| 30 | #include <shevek/bg.hh> |
| 31 | #include <shevek/error.hh> |
| 32 | #include "devices.hh" |
| 33 | |
| 34 | struct client |
| 35 | |
| 36 | struct data: |
| 37 | Glib::RefPtr <shevek::server <client, data *> > server |
| 38 | usb_dev_handle *handle |
| 39 | static int const boot_vendor = 0x601a |
| 40 | static int const boot_product = 0x4740 |
| 41 | static int const run_vendor = 0xfffe |
| 42 | static int const run_product = 0x0002 |
| 43 | static unsigned const timeout = 10000 |
| 44 | void boot (std::string const &filename, unsigned load, unsigned entry) |
| 45 | data (std::string const &port): |
| 46 | handle = NULL |
| 47 | server = shevek::server <client, data *>::create () |
| 48 | server->data () = this |
| 49 | server->open (port) |
| 50 | |
| 51 | private: |
| 52 | static unsigned const STAGE1_LOAD = 0x80003000 |
| 53 | static unsigned const STAGE1_ENTRY = STAGE1_LOAD |
| 54 | enum requests: |
| 55 | VR_GET_CPU_INFO = 0 |
| 56 | VR_SET_DATA_ADDRESS = 1 |
| 57 | VR_SET_DATA_LENGTH = 2 |
| 58 | VR_FLUSH_CACHES = 3 |
| 59 | VR_PROGRAM_START1 = 4 |
| 60 | VR_PROGRAM_START2 = 5 |
| 61 | POLL = 10 |
| 62 | void request (requests num, unsigned data = 0) |
| 63 | void send_file (unsigned address, unsigned size, char const *data) |
| 64 | void reboot (): |
| 65 | char buffer[8] |
| 66 | if usb_control_msg (handle, USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, VR_GET_CPU_INFO, 0, 0, buffer, 8, timeout) < 0: |
| 67 | std::cerr << "unable to send reboot message to device: " << usb_strerror () << std::endl |
| 68 | usb_release_interface (handle, 0) |
| 69 | usb_close (handle) |
| 70 | handle = NULL |
| 71 | void get_device (unsigned vendor, unsigned product, unsigned tries) |
| 72 | void poll () |
| 73 | |
| 74 | static std::string files ("fs") |
| 75 | struct Name: |
| 76 | char name[16] |
| 77 | std::string full |
| 78 | std::string content |
| 79 | Name (std::string const &n): |
| 80 | full = files + '/' + n |
| 81 | memset (name, 0, 16) |
| 82 | memcpy (name, n.data (), n.size () > 16 ? 16 : n.size ()) |
| 83 | std::ifstream f (full.c_str ()) |
| 84 | std::ostringstream s |
| 85 | s << f.rdbuf () |
| 86 | content = s.str () |
| 87 | static std::vector <Name> dir |
| 88 | unsigned lock (0) |
| 89 | |
| 90 | void data::poll (): |
| 91 | while true: |
| 92 | unsigned buffer[2] |
| 93 | int s = usb_control_msg (handle, USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, POLL, 0, 0, (char *)buffer, 8, timeout) |
| 94 | if s != 8: |
| 95 | std::cerr << "unable to send poll message to device: " << usb_strerror () << std::endl |
| 96 | usb_release_interface (handle, 0) |
| 97 | usb_close (handle) |
| 98 | handle = NULL |
| 99 | return |
| 100 | switch buffer[0] & 0xffff: |
| 101 | case ~0 & 0xffff: |
| 102 | // Log character. |
| 103 | std::cout << (char)buffer[1] << std::flush |
| 104 | continue |
| 105 | case ~1 & 0xffff: |
| 106 | // No event. |
| 107 | break |
| 108 | case Iris::Directory::GET_SIZE: |
| 109 | unsigned long long size = dir.size () |
| 110 | std::cerr << "sending dir size\n" |
| 111 | std::cerr << Iris::Directory::GET_SIZE << '\n' |
| 112 | char *str = (char *)&size |
| 113 | for unsigned i = 0; i < 8; ++i: |
| 114 | std::cerr << " " << (unsigned)(str[i] & 0xff) |
| 115 | std::cerr << '\n' |
| 116 | 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: |
| 117 | std::cerr << "unable to send size to device: " << usb_strerror () << std::endl |
| 118 | usb_release_interface (handle, 0) |
| 119 | usb_close (handle) |
| 120 | handle = NULL |
| 121 | return |
| 122 | continue |
| 123 | case Iris::Directory::GET_NAME: |
| 124 | if buffer[1] >= dir.size (): |
| 125 | std::cerr << "invalid file name requested" << std::endl; |
| 126 | usb_release_interface (handle, 0) |
| 127 | usb_close (handle) |
| 128 | handle = NULL |
| 129 | return |
| 130 | std::cerr << "sending filename " << dir[buffer[1]].full << "\n" |
| 131 | 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: |
| 132 | std::cerr << "unable to send name to device: " << usb_strerror () << std::endl |
| 133 | usb_release_interface (handle, 0) |
| 134 | usb_close (handle) |
| 135 | handle = NULL |
| 136 | return |
| 137 | continue |
| 138 | case Iris::Directory::LOCK_RO: |
| 139 | std::cerr << "lock\n" |
| 140 | lock++ |
| 141 | std::cerr << "freezing file list\n" |
| 142 | shevek::dir d (files) |
| 143 | dir.clear () |
| 144 | for shevek::dir::const_iterator i = d.begin (); i != d.end (); ++i: |
| 145 | if !i->name.empty () && i->name[0] != '.': |
| 146 | dir.push_back (Name (i->name)) |
| 147 | continue |
| 148 | case Iris::Directory::UNLOCK_RO: |
| 149 | std::cerr << "unlock\n" |
| 150 | if !lock: |
| 151 | std::cerr << "unlocking without lock" << std::endl |
| 152 | usb_release_interface (handle, 0) |
| 153 | usb_close (handle) |
| 154 | handle = NULL |
| 155 | return |
| 156 | if !--lock: |
| 157 | dir.clear () |
| 158 | continue |
| 159 | case Iris::Block::GET_BLOCK: |
| 160 | if buffer[1] >= dir.size (): |
| 161 | std::cerr << "reading invalid file" << std::endl |
| 162 | usb_release_interface (handle, 0) |
| 163 | usb_close (handle) |
| 164 | handle = NULL |
| 165 | return |
| 166 | unsigned pos = buffer[0] >> 16 |
| 167 | std::cerr << "getting data from " << pos << '\n' |
| 168 | std::string page |
| 169 | if dir[buffer[1]].content.size () <= pos << 12: |
| 170 | page = std::string (1 << 12, 0) |
| 171 | else: |
| 172 | page = (dir[buffer[1]].content.substr (pos << 12, 1 << 12) + std::string (1 << 12, 0)).substr (0, 1 << 12) |
| 173 | for unsigned i = 0; i < (1 << 12); i += 64: |
| 174 | if usb_bulk_write (handle, 1 | USB_ENDPOINT_OUT, &page.data ()[i], 64, timeout) != 64: |
| 175 | std::cerr << "unable to send file to device: " << usb_strerror () << std::endl |
| 176 | usb_release_interface (handle, 0) |
| 177 | usb_close (handle) |
| 178 | handle = NULL |
| 179 | return |
| 180 | continue |
| 181 | case Iris::Block::GET_SIZE: |
| 182 | if buffer[1] >= dir.size (): |
| 183 | std::cerr << "reading invalid file size " << buffer[1] << " >= " << dir.size () << std::endl |
| 184 | usb_release_interface (handle, 0) |
| 185 | usb_close (handle) |
| 186 | handle = NULL |
| 187 | return |
| 188 | unsigned long long size = dir[buffer[1]].content.size () |
| 189 | std::cerr << "sending file size " << size << "\n" |
| 190 | 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: |
| 191 | std::cerr << "unable to send size to device: " << usb_strerror () << std::endl |
| 192 | usb_release_interface (handle, 0) |
| 193 | usb_close (handle) |
| 194 | handle = NULL |
| 195 | return |
| 196 | continue |
| 197 | default: |
| 198 | std::cerr << "invalid request " << buffer[0] << std::endl |
| 199 | usb_release_interface (handle, 0) |
| 200 | usb_close (handle) |
| 201 | handle = NULL |
| 202 | return |
| 203 | // If the code reaches this point, break out of the loop. The loop continues if a continue statement is reached. |
| 204 | break |
| 205 | (shevek::absolute_time () + shevek::relative_time (0, 100000000)).schedule (sigc::mem_fun (*this, &data::poll)) |
| 206 | |
| 207 | struct client : public shevek::server <client, data *>::connection: |
| 208 | static Glib::RefPtr <client> create (): |
| 209 | return Glib::RefPtr <client> (new client ()) |
| 210 | bool keep |
| 211 | void pickup (bool is_stdio): |
| 212 | keep = is_stdio |
| 213 | void read (std::string const &line): |
| 214 | shevek::ristring l (line) |
| 215 | unsigned load, entry |
| 216 | std::string filename |
| 217 | if l ("reboot %x %x %a%", load, entry, filename): |
| 218 | get_server ()->data ()->boot (filename, load, entry) |
| 219 | else if l ("shutdown%"): |
| 220 | std::cerr << "shutting down\n" |
| 221 | shevek::end_loop () |
| 222 | else: |
| 223 | out->write ("invalid command\n") |
| 224 | if !keep: |
| 225 | out->write_block () |
| 226 | disconnect () |
| 227 | |
| 228 | void data::request (requests r, unsigned data): |
| 229 | std::cerr << shevek::ostring ("requesting %08x with data %08x\n", r, data) |
| 230 | if usb_control_msg (handle, USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, r, (data >> 16) & 0xffff, data & 0xffff, NULL, 0, timeout) < 0: |
| 231 | std::cerr << "unable to send control message to NanoNote: " << usb_strerror () << ".\n" |
| 232 | usb_release_interface (handle, 0) |
| 233 | usb_close (handle) |
| 234 | handle = NULL |
| 235 | |
| 236 | void data::send_file (unsigned address, unsigned size, char const *data): |
| 237 | std::cerr << shevek::ostring ("setting data address to 0x%08x\n", address) |
| 238 | request (VR_SET_DATA_ADDRESS, address) |
| 239 | char const *ptr = data |
| 240 | while ptr - data < size: |
| 241 | int ret = usb_bulk_write (handle, 1, ptr, size - (ptr - data), timeout) |
| 242 | if ret <= 0: |
| 243 | std::cerr << "failed to write to NanoNote.\n" |
| 244 | usb_release_interface (handle, 0) |
| 245 | usb_close (handle) |
| 246 | handle = NULL |
| 247 | return |
| 248 | ptr += ret |
| 249 | std::cerr << shevek::ostring ("sent %d bytes\n", size) |
| 250 | |
| 251 | void data::get_device (unsigned vendor, unsigned product, unsigned tries): |
| 252 | for unsigned i = 0; i < tries; ++i: |
| 253 | usb_find_busses () |
| 254 | usb_find_devices () |
| 255 | for struct usb_bus *bus = usb_busses; bus; bus = bus->next: |
| 256 | for struct usb_device *dev = bus->devices; dev; dev = dev->next: |
| 257 | if dev->descriptor.idProduct != product || dev->descriptor.idVendor != vendor: |
| 258 | //std::cerr << shevek::ostring ("Not using %04x:%04x when looking for %04x:%04x\n", dev->descriptor.idVendor, dev->descriptor.idProduct, vendor, product) |
| 259 | continue |
| 260 | handle = usb_open (dev) |
| 261 | if usb_claim_interface (handle, 0) < 0: |
| 262 | std::cerr << "unable to claim interface: " << usb_strerror () << "\n" |
| 263 | usb_close (handle) |
| 264 | handle = NULL |
| 265 | continue |
| 266 | (shevek::absolute_time () + shevek::relative_time (0, 100000000)).schedule (sigc::mem_fun (*this, &data::poll)) |
| 267 | return |
| 268 | if i + 1 < tries: |
| 269 | //std::cerr << "failed to find device, still trying...\n" |
| 270 | sleep (1) |
| 271 | std::cerr << shevek::ostring ("giving up finding device %04x:%04x\n", vendor, product) |
| 272 | |
| 273 | void data::boot (std::string const &filename, unsigned load, unsigned entry): |
| 274 | std::cerr << "booting " << shevek::ostring ("%s from %x@%x", Glib::ustring (filename), load, entry) << "\n" |
| 275 | if handle: |
| 276 | usb_release_interface (handle, 0) |
| 277 | usb_close (handle) |
| 278 | handle = NULL |
| 279 | get_device (boot_vendor, boot_product, 1) |
| 280 | if !handle: |
| 281 | get_device (run_vendor, run_product, 1) |
| 282 | if !handle: |
| 283 | std::cerr << "unable to find device\n" |
| 284 | return |
| 285 | reboot () |
| 286 | get_device (boot_vendor, boot_product, 5) |
| 287 | if !handle: |
| 288 | std::cerr << "unable to reboot device\n" |
| 289 | return |
| 290 | std::cerr << "sending stage 1\n" |
| 291 | std::ifstream file (STAGE1_FILE) |
| 292 | std::ostringstream stage1 |
| 293 | stage1 << file.rdbuf () |
| 294 | send_file (STAGE1_LOAD, stage1.str ().size (), stage1.str ().data ()) |
| 295 | std::cerr << "running stage 1\n" |
| 296 | request (VR_PROGRAM_START1, STAGE1_ENTRY) |
| 297 | usleep (100) |
| 298 | std::ostringstream stage2 |
| 299 | file.close () |
| 300 | file.open (filename.c_str ()) |
| 301 | stage2 << file.rdbuf () |
| 302 | std::cerr << shevek::ostring ("sending Iris (size 0x%x)\n", stage2.str ().size ()) |
| 303 | send_file (load, stage2.str ().size (), stage2.str ().data ()) |
| 304 | std::cerr << "flushing caches\n" |
| 305 | request (VR_FLUSH_CACHES) |
| 306 | std::cerr << "running Iris\n" |
| 307 | request (VR_PROGRAM_START2, entry) |
| 308 | usb_release_interface (handle, 0) |
| 309 | usb_close (handle) |
| 310 | handle = NULL |
| 311 | get_device (run_vendor, run_product, 5) |
| 312 | if !handle: |
| 313 | std::cerr << "unable to open booted device\n" |
| 314 | return |
| 315 | std::cerr << "(re)booted NanoNote\n" |
| 316 | |
| 317 | static void dump_devices (): |
| 318 | std::cerr << std::hex << "String: " << Iris::String::ID |
| 319 | std::cerr << "\nBlock: " << Iris::Block::ID |
| 320 | std::cerr << "\nWString: " << Iris::WString::ID |
| 321 | std::cerr << "\nWBlock: " << Iris::WBlock::ID |
| 322 | std::cerr << "\nBoot: " << Iris::Boot::ID |
| 323 | std::cerr << "\nDevice: " << Iris::Device::ID |
| 324 | std::cerr << "\nEvent: " << Iris::Event::ID |
| 325 | std::cerr << "\nElfrun: " << Iris::Elfrun::ID |
| 326 | std::cerr << "\nParent: " << Iris::Parent::ID |
| 327 | std::cerr << "\nKeyboard: " << Iris::Keyboard::ID |
| 328 | std::cerr << "\nBuzzer: " << Iris::Buzzer::ID |
| 329 | std::cerr << "\nDisplay: " << Iris::Display::ID |
| 330 | std::cerr << "\nFont: " << Iris::Display::ID |
| 331 | std::cerr << "\nSetting: " << Iris::Setting::ID |
| 332 | std::cerr << "\nDirectory: " << Iris::Directory::ID |
| 333 | std::cerr << "\nWDirectory: " << Iris::WDirectory::ID |
| 334 | std::cerr << "\nStream: " << Iris::Stream::ID |
| 335 | std::cerr << "\nUI: " << Iris::UI::ID |
| 336 | std::cerr << "\n" |
| 337 | |
| 338 | int main (int argc, char **argv): |
| 339 | usb_init () |
| 340 | std::string port ("5050") |
| 341 | shevek::args::option opts[] = { |
| 342 | shevek::args::option ('p', "port", "port to listen for commands", true, port) |
| 343 | } |
| 344 | shevek::args args (argc, argv, opts, 0, 0, "device server for testing Iris on NanoNote") |
| 345 | data d (port) |
| 346 | dump_devices () |
| 347 | shevek::bg () |
| 348 | shevek::loop () |
| 349 | return 0 |
| 350 |
Branches:
master
