Root/native/usb-server.ccp

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
34struct client
35
36struct 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
74static std::string files ("fs")
75struct 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 ()
87static std::vector <Name> dir
88unsigned lock (0)
89
90void 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
207struct 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
228void 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
236void 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
251void 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
273void 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
317static 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
338int 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

Archive Download this file

Branches:
master



interactive