1
0
mirror of git://projects.qi-hardware.com/iris.git synced 2024-11-17 17:15:00 +02:00
iris/mips/nanonote/server/usb-server.ccp

339 lines
12 KiB
Plaintext
Raw Normal View History

2009-12-18 23:27:26 +02:00
#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 <wijnen@debian.org>
//
// 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 <http://www.gnu.org/licenses/>.
#include <unistd.h>
#include <usb.h>
#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
2010-01-14 19:14:37 +02:00
#include <cstring>
2009-12-18 23:27:26 +02:00
#include <shevek/mainloop.hh>
#include <shevek/server.hh>
#include <shevek/args.hh>
2010-01-14 23:04:19 +02:00
#include <shevek/dir.hh>
2010-01-16 17:13:54 +02:00
#include <shevek/error.hh>
2010-01-14 19:14:37 +02:00
#include "devices.hh"
2009-12-18 23:27:26 +02:00
struct client
struct data:
Glib::RefPtr <shevek::server <client, data *> > 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
2010-01-18 06:01:59 +02:00
static unsigned const timeout = 10000
2009-12-18 23:27:26 +02:00
void boot (unsigned entry)
data (std::string const &port):
handle = NULL
server = shevek::server <client, data *>::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
2009-12-26 15:17:06 +02:00
POLL = 10
2009-12-18 23:27:26 +02:00
void request (requests num, unsigned data = 0)
void send_file (unsigned address, unsigned size, char const *data)
void reboot ():
2009-12-26 15:17:06 +02:00
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
2009-12-18 23:27:26 +02:00
usb_release_interface (handle, 0)
usb_close (handle)
handle = NULL
void get_device (unsigned vendor, unsigned product, unsigned tries)
2009-12-26 15:17:06 +02:00
void poll ()
2010-01-16 17:13:54 +02:00
static std::string files ("fs")
2010-01-14 23:04:19 +02:00
struct Name:
char name[16]
std::string full
2010-01-18 06:01:59 +02:00
std::string content
2010-01-14 23:04:19 +02:00
Name (std::string const &n):
2010-01-16 17:13:54 +02:00
full = files + '/' + n
2010-01-14 23:04:19 +02:00
memset (name, 0, 16)
memcpy (name, n.data (), n.size () > 16 ? 16 : n.size ())
2010-01-18 06:01:59 +02:00
std::ifstream f (full.c_str ())
std::ostringstream s
s << f.rdbuf ()
content = s.str ()
2010-01-14 23:04:19 +02:00
static std::vector <Name> dir
unsigned lock (0)
2009-12-26 15:17:06 +02:00
void data::poll ():
while true:
2010-01-14 19:14:37 +02:00
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:
2009-12-26 15:17:06 +02:00
std::cerr << "unable to send poll message to device: " << usb_strerror () << std::endl
usb_release_interface (handle, 0)
usb_close (handle)
handle = NULL
return
2010-01-14 19:14:37 +02:00
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:
2010-01-14 19:14:37 +02:00
unsigned long long size = dir.size ()
2010-01-16 17:13:54 +02:00
std::cerr << "sending dir size\n"
std::cerr << Iris::Directory::GET_SIZE << '\n'
2010-01-16 17:13:54 +02:00
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:
2010-01-14 19:14:37 +02:00
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:
2010-01-14 19:14:37 +02:00
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:
2010-01-14 19:14:37 +02:00
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:
2010-01-16 17:13:54 +02:00
std::cerr << "lock\n"
2010-01-24 22:34:24 +02:00
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))
2010-01-14 19:14:37 +02:00
continue
case Iris::Directory::UNLOCK_RO:
2010-01-16 17:13:54 +02:00
std::cerr << "unlock\n"
2010-01-14 19:14:37 +02:00
if !lock:
std::cerr << "unlocking without lock" << std::endl
usb_release_interface (handle, 0)
usb_close (handle)
handle = NULL
return
2010-01-14 23:04:19 +02:00
if !--lock:
dir.clear ()
2010-01-14 19:14:37 +02:00
continue
2010-06-07 00:03:25 +03:00
case Iris::String::GET_BLOCK:
2010-01-16 17:13:54 +02:00
if buffer[1] >= dir.size ():
2010-01-14 19:14:37 +02:00
std::cerr << "reading invalid file" << std::endl
usb_release_interface (handle, 0)
usb_close (handle)
handle = NULL
return
2010-01-16 17:13:54 +02:00
unsigned pos = buffer[0] >> 16
std::cerr << "getting data from " << pos << '\n'
2010-01-18 06:01:59 +02:00
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)
2010-01-14 19:14:37 +02:00
for unsigned i = 0; i < (1 << 12); i += 64:
2010-01-18 06:01:59 +02:00
if usb_bulk_write (handle, 1 | USB_ENDPOINT_OUT, &page.data ()[i], 64, timeout) != 64:
2010-01-14 19:14:37 +02:00
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::String::GET_SIZE:
2010-01-16 17:13:54 +02:00
if buffer[1] >= dir.size ():
std::cerr << "reading invalid file size " << buffer[1] << " >= " << dir.size () << std::endl
2010-01-16 17:13:54 +02:00
usb_release_interface (handle, 0)
usb_close (handle)
handle = NULL
return
2010-01-18 06:01:59 +02:00
unsigned long long size = dir[buffer[1]].content.size ()
2010-01-16 17:13:54 +02:00
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:
2010-01-16 17:13:54 +02:00
std::cerr << "unable to send size to device: " << usb_strerror () << std::endl
usb_release_interface (handle, 0)
usb_close (handle)
handle = NULL
return
continue
2010-01-14 19:14:37 +02:00
default:
2010-01-16 17:13:54 +02:00
std::cerr << "invalid request " << buffer[0] << std::endl
2010-01-14 19:14:37 +02:00
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
2010-01-16 17:13:54 +02:00
(shevek::absolute_time () + shevek::relative_time (0, 100000000)).schedule (sigc::mem_fun (*this, &data::poll))
2009-12-18 23:27:26 +02:00
struct client : public shevek::server <client, data *>::connection:
static Glib::RefPtr <client> create ():
return Glib::RefPtr <client> (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)
2009-12-26 15:17:06 +02:00
else if l ("shutdown%"):
std::cerr << "shutting down\n"
shevek::end_loop ()
2009-12-18 23:27:26 +02:00
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:
2009-12-26 15:17:06 +02:00
std::cerr << "unable to claim interface: " << usb_strerror () << "\n"
2009-12-18 23:27:26 +02:00
usb_close (handle)
handle = NULL
continue
2010-01-16 17:13:54 +02:00
(shevek::absolute_time () + shevek::relative_time (0, 100000000)).schedule (sigc::mem_fun (*this, &data::poll))
2009-12-18 23:27:26 +02:00
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"
2010-01-16 17:13:54 +02:00
static void dump_devices ():
std::cerr << std::hex << "String: " << Iris::String::ID
std::cerr << "\nWString: " << Iris::WString::ID
std::cerr << "\nDevice: " << Iris::Device::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 << "\nSetting: " << Iris::Setting::ID
std::cerr << "\nDirectory: " << Iris::Directory::ID
std::cerr << "\nWDirectory: " << Iris::WDirectory::ID
std::cerr << "\nStream: " << Iris::Stream::ID
2010-01-16 17:13:54 +02:00
std::cerr << "\n"
2009-12-18 23:27:26 +02:00
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)
2010-01-16 17:13:54 +02:00
dump_devices ()
2009-12-18 23:27:26 +02:00
shevek::loop ()
return 0