Subversion Repositories freemyipod

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
82 benedikt93 1
#!/usr/bin/env python
64 benedikt93 2
#
3
#
171 farthen 4
#    Copyright 2010 TheSeven, benedikt93, Farthen
64 benedikt93 5
#
6
#
427 farthen 7
#    This file is part of emCORE.
64 benedikt93 8
#
427 farthen 9
#    emCORE is free software: you can redistribute it and/or
64 benedikt93 10
#    modify it under the terms of the GNU General Public License as
11
#    published by the Free Software Foundation, either version 2 of the
12
#    License, or (at your option) any later version.
13
#
427 farthen 14
#    emCORE is distributed in the hope that it will be useful,
64 benedikt93 15
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
16
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
#    See the GNU General Public License for more details.
18
#
82 benedikt93 19
#    You should have received a copy of the GNU General Public License
427 farthen 20
#    along with emCORE.  If not, see <http://www.gnu.org/licenses/>.
64 benedikt93 21
#
787 theseven 22
#
64 benedikt93 23
 
402 farthen 24
"""
427 farthen 25
    Command line interface to communicate with emCORE devices.
402 farthen 26
    Run it without arguments to see usage information.
27
"""
28
 
171 farthen 29
import sys
30
import os
176 farthen 31
import time
215 theseven 32
import struct
346 theseven 33
import locale
64 benedikt93 34
 
176 farthen 35
from functools import wraps
36
 
427 farthen 37
import libemcore
38
import libemcoredata
532 farthen 39
from misc import Error, ArgumentError, ArgumentTypeError, Logger, getfuncdoc, gethwname, to_bool, to_int
64 benedikt93 40
 
236 farthen 41
 
444 farthen 42
def usage(errormsg=None, specific=False, docstring=True):
171 farthen 43
    """
44
        Prints the usage information.
45
        It is auto generated from various places.
46
    """
47
    logger = Logger()
396 farthen 48
    cmddict = Commandline.cmddict
49
    doc = getfuncdoc(cmddict)
171 farthen 50
    if not specific:
501 farthen 51
        logger.write("Please provide a command and (if needed) parameters as command line arguments\n\n")
52
        logger.write("Available commands:\n\n")
82 benedikt93 53
    else:
501 farthen 54
        logger.write("\n")
171 farthen 55
    for function in sorted(doc.items()):
56
        function = function[0]
57
        if specific == False or specific == function:
501 farthen 58
            logger.write(function + " ", 2)
171 farthen 59
            for arg in doc[function]['args']:
501 farthen 60
                logger.write("<" + arg + "> ")
171 farthen 61
            if doc[function]['kwargs']:
579 farthen 62
                for kwarg, kwvalue in doc[function]['kwargs']:
501 farthen 63
                    logger.write("[" + kwarg + " = " + str(kwvalue) + "] ")
171 farthen 64
            if doc[function]['varargs']:
501 farthen 65
                logger.write("<db1> ... <dbN>")
444 farthen 66
            if docstring and doc[function]['documentation']:
501 farthen 67
                logger.write("\n")
68
                logger.write(doc[function]['documentation']+"\n", 4)
69
            logger.write("\n")
70
    logger.write("\n")
517 farthen 71
 
171 farthen 72
    if errormsg:
73
        logger.error(str(errormsg)+"\n")
74
    exit(2)
75
 
76
 
77
def command(func):
78
    """
79
        Decorator for all commands.
80
        The decorated function is called with (self, all, other, arguments, ...)
81
    """
176 farthen 82
    @wraps(func)
172 farthen 83
    def decorator(*args):
171 farthen 84
        return func(args[0], *args[1:])
85
    func._command = True
86
    decorator.func = func
87
    return decorator
88
 
89
 
90
def commandClass(cls):
91
    """
92
        Decorator for the class. Sets the self.cmddict of the class
93
        to all functions decorated with @command
94
    """
95
    cls.cmddict = {}
582 farthen 96
    for attr, value in cls.__dict__.items():
171 farthen 97
        if getattr(value, 'func', False):
98
            if getattr(value.func, '_command', False):
99
                cls.cmddict[value.func.__name__] = value
100
    return cls
101
 
102
 
103
@commandClass
104
class Commandline(object):
105
    """
106
        If you want to create a new commandline function you just need to
107
        create a function with the name of it in this class and decorate
108
        it with the decorator @command. If you don't want to call the desired
109
        function (wrong arguments etc) just raise ArgumentError with or
341 farthen 110
        without an error message.
171 farthen 111
    """
112
    def __init__(self):
113
        self.logger = Logger()
114
        try:
499 farthen 115
            self.emcore = libemcore.Emcore(logger = self.logger)
427 farthen 116
        except libemcore.DeviceNotFoundError:
478 farthen 117
            self.logger.error("No emCORE device found!\n")
176 farthen 118
            exit(1)
119
        self.getinfo("version")
517 farthen 120
 
171 farthen 121
    def _parsecommand(self, func, args):
122
        # adds self to the commandline args.
123
        # this is needed because the functions need access to their class.
124
        args.insert(0, self)
125
        if func in self.cmddict:
126
            try:
172 farthen 127
                self.cmddict[func](*args)
582 farthen 128
            except (ArgumentError, libemcore.ArgumentError) as e:
176 farthen 129
                usage(e, specific=func)
427 farthen 130
            except (ArgumentError, libemcore.ArgumentError):
176 farthen 131
                usage("Syntax Error in function '" + func + "'", specific=func)
582 farthen 132
            except ArgumentTypeError as e:
176 farthen 133
                usage(e, specific=func)
171 farthen 134
            except NotImplementedError:
478 farthen 135
                self.logger.error("This function is not implemented yet!\n")
582 farthen 136
            except libemcore.DeviceError as e:
478 farthen 137
                self.logger.error(str(e) + "\n")
582 farthen 138
            except TypeError as e:
341 farthen 139
                # Only act on TypeErrors for the function we called, not on TypeErrors raised by another function.
171 farthen 140
                if str(e).split(" ", 1)[0] == func + "()":
516 farthen 141
                    self.logger.error(usage("Argument Error in '%s': Wrong argument count" % func, specific=func))
171 farthen 142
                else:
143
                    raise
951 theseven 144
#            except libemcore.usb.core.USBError:
145
#                self.logger.error("There is a problem with the USB connection.\n")
171 farthen 146
        else:
502 farthen 147
            usage("No such command!", docstring = False)
532 farthen 148
 
67 benedikt93 149
 
171 farthen 150
    @command
444 farthen 151
    def help(self):
502 farthen 152
        """ Displays this help """
153
        usage(docstring = True)
444 farthen 154
 
155
    @command
171 farthen 156
    def getinfo(self, infotype):
157
        """
427 farthen 158
            Get info on the running emCORE.
675 farthen 159
            <infotype> may be either of 'version', 'packetsize', 'mallocpoolbounds'.
171 farthen 160
        """
161
        if infotype == "version":
427 farthen 162
            hwtype = gethwname(self.emcore.lib.dev.hwtypeid)
516 farthen 163
            self.logger.info("Connected to %s v%d.%d.%d r%d running on %s\n" % (
164
                             libemcoredata.swtypes[self.emcore.lib.dev.swtypeid],
165
                             self.emcore.lib.dev.version.majorv,
166
                             self.emcore.lib.dev.version.minorv,
167
                             self.emcore.lib.dev.version.patchv,
168
                             self.emcore.lib.dev.version.revision,
169
                             hwtype))
343 farthen 170
 
171 farthen 171
        elif infotype == "packetsize":
516 farthen 172
            self.logger.info("Maximum packet sizes:\n")
173
            self.logger.info("command out: %d\n" % self.emcore.lib.dev.packetsizelimit.cout, 4)
174
            self.logger.info("command in: %d\n" % self.emcore.lib.dev.packetsizelimit.cin, 4)
175
            self.logger.info("data out: %d\n" % self.emcore.lib.dev.packetsizelimit.dout, 4)
176
            self.logger.info("data in: %d\n" % self.emcore.lib.dev.packetsizelimit.din, 4)
343 farthen 177
 
675 farthen 178
        elif infotype == "mallocpoolbounds":
179
            resp = self.emcore.getmallocpoolbounds()
180
            self.logger.info("The malloc pool is 0x%X - 0x%X" % (
181
                             self.emcore.lib.dev.mallocpool.lower,
182
                             self.emcore.lib.dev.mallocpool.upper - 1))
382 farthen 183
 
171 farthen 184
        else:
675 farthen 185
            raise ArgumentTypeError("one out of 'version', 'packetsize', 'mallocpoolbounds'", infotype)
64 benedikt93 186
 
171 farthen 187
    @command
188
    def reset(self, force=False):
189
        """
190
            Resets the device"
402 farthen 191
            If [force] is 1, the reset will be forced, otherwise it will be gracefully,
171 farthen 192
            which may take some time.
193
        """
532 farthen 194
        force = to_bool(force)
171 farthen 195
        if force: self.logger.info("Resetting forcefully...\n")
196
        else: self.logger.info("Resetting...\n")
427 farthen 197
        self.emcore.reset(force)
64 benedikt93 198
 
171 farthen 199
    @command
200
    def poweroff(self, force=False):
201
        """
202
            Powers the device off
402 farthen 203
            If [force] is 1, the poweroff will be forced, otherwise it will be gracefully,
171 farthen 204
            which may take some time.
205
        """
532 farthen 206
        force = to_bool(force)
281 farthen 207
        if force: self.logger.info("Powering off forcefully...\n")
208
        else: self.logger.info("Powering off...\n")
427 farthen 209
        self.emcore.poweroff(force)
64 benedikt93 210
 
171 farthen 211
    @command
442 farthen 212
    def uploadfile(self, filename, addr = None):
171 farthen 213
        """
214
            Uploads a file to the device
442 farthen 215
            <filename>: The path to the file
216
            [addr]: The address to upload the file to. Allocates a chunk of memory if not given.
171 farthen 217
        """
532 farthen 218
        addr = to_int(addr)
171 farthen 219
        try:
220
            f = open(filename, 'rb')
221
        except IOError:
222
            raise ArgumentError("File not readable. Does it exist?")
442 farthen 223
        if addr is not None:
516 farthen 224
            self.logger.info("Writing file '%s' to memory at 0x%X...\n" % (filename, addr))
442 farthen 225
        else:
516 farthen 226
            self.logger.info("Writing file '%s' to an allocated memory region...\n" % filename)
171 farthen 227
        with f:
442 farthen 228
            if addr is not None:
229
                self.emcore.write(addr, f.read())
230
            else:
231
                addr = self.emcore.upload(f.read())
232
            size = f.tell()
178 farthen 233
        f.close()
516 farthen 234
        self.logger.info("Done uploading %d bytes to 0x%X\n" % (size, addr))
442 farthen 235
        return addr, size
176 farthen 236
 
171 farthen 237
    @command
238
    def downloadfile(self, addr, size, filename):
239
        """
574 theseven 240
            Downloads a region of memory from the device to a file
241
            <addr>: the address to download the data from
171 farthen 242
            <size>: the number of bytes to be read
243
            <filename>: the path to the file
244
        """
532 farthen 245
        addr = to_int(addr)
246
        size = to_int(size)
171 farthen 247
        try:
248
            f = open(filename, 'wb')
249
        except IOError:
250
            raise ArgumentError("Can not open file for write!")
516 farthen 251
        self.logger.info("Reading data from address 0x%X with the size 0x%X to '%s'..." %
252
                        (addr, size, filename))
171 farthen 253
        with f:
427 farthen 254
            f.write(self.emcore.read(addr, size))
178 farthen 255
        f.close()
171 farthen 256
        self.logger.info("done\n")
517 farthen 257
 
171 farthen 258
    @command
578 theseven 259
    def hexdump(self, addr, size, width = 16, wordsize = 1, separate = 4, align = False, \
260
                relative = False, ascii = True, asciisep = 8, asciirep = ".", zeropad = True):
575 theseven 261
        """
262
            Downloads a region of memory from the device and hexdumps it
263
            <addr>: the address to download the data from
264
            <size>: the number of bytes to be dumped
265
            [width]: the number of words per output line
266
            [wordsize]: the number of bytes per word (little endian)
267
            [separate]: add an additional space character every [separate] words
268
            [align]: if true, output lines are aligned to the line size
269
            [relative]: if true, the addresses displayed are relative to the <addr>, not zero
270
            [ascii]: add ASCII representations of the bytes to the output
271
            [asciisep]: add an additional space character every [asciisep] ASCII characters
578 theseven 272
            [asciirep]: replacement character for non-printable ASCII characters
273
            [zeropad]: pad hex values with zero instead of space characters
575 theseven 274
        """
275
        addr = to_int(addr)
276
        size = to_int(size)
277
        width = to_int(width)
278
        wordsize = to_int(wordsize)
279
        separate = to_int(separate)
280
        align = to_bool(align)
281
        relative = to_bool(relative)
282
        ascii = to_bool(ascii)
283
        asciisep = to_int(asciisep)
578 theseven 284
        zeropad = to_bool(zeropad)
575 theseven 285
        if addr % wordsize != 0: raise ArgumentError("Address is not word aligned!")
286
        if size % wordsize != 0: raise ArgumentError("Size is not word aligned!")
287
        if align: skip = addr % (wordsize * width)
288
        else: skip = 0
289
        if relative: base = 0
290
        else: base = addr
291
        data = self.emcore.read(addr, size)
292
        for r in range(-skip, size, wordsize * width):
293
            sys.stdout.write("%08X:" % (base + r))
294
            for i in range(r, r + wordsize * width, wordsize):
295
                if i - r > 0 and (i - r) % (separate * wordsize) == 0: sys.stdout.write(" ")
296
                if i >= 0 and i < size:
297
                    w = 0
298
                    for b in range(wordsize):
851 theseven 299
                        w = w | (struct.unpack("B", data[i + b : i + b + 1])[0] << (8 * b))
578 theseven 300
                    sys.stdout.write(((" %%0%dX" if zeropad else " %%%dX") % (wordsize * 2)) % w)
575 theseven 301
                else: sys.stdout.write(" " * (wordsize * 2 + 1))
302
            if ascii:
303
                sys.stdout.write(" |")
304
                for i in range(r, r + wordsize * width):
305
                    if i - r > 0 and (i - r) % asciisep == 0: sys.stdout.write(" ")
306
                    if i >= 0 and i < size:
851 theseven 307
                        if ord(data[i : i + 1]) > 0x1f and ord(data[i : i + 1]) < 0x80:
308
                            sys.stdout.write(data[i : i + 1].decode("latin1"))
578 theseven 309
                        else: sys.stdout.write(asciirep)
575 theseven 310
                    else: sys.stdout.write(" ")
311
                sys.stdout.write("|")
312
            sys.stdout.write("\n")
313
 
314
    @command
774 theseven 315
    def uploadbyte(self, addr, byte):
316
        """
317
            Uploads a single byte to the device
318
            <addr>: the address to upload the byte to
319
            <byte>: the byte to upload
320
        """
321
        addr = to_int(addr)
322
        byte = to_int(byte)
323
        if byte > 0xFF:
324
            raise ArgumentError("Specified integer too long")
325
        data = struct.pack("B", byte)
326
        self.emcore.write(addr, data)
327
        self.logger.info("Byte '0x%X' written successfully to 0x%X\n" % (byte, addr))
328
 
329
    @command
330
    def downloadbyte(self, addr):
331
        """
332
            Downloads a single byte from the device and prints it to the console window
333
            <addr>: the address to download the byte from
334
        """
335
        addr = to_int(addr)
336
        data = self.emcore.read(addr, 1)
337
        byte = struct.unpack("B", data)[0]
338
        self.logger.info("Read '0x%X' from address 0x%X\n" % (byte, addr))
339
 
340
    @command
171 farthen 341
    def uploadint(self, addr, integer):
342
        """
343
            Uploads a single integer to the device
260 theseven 344
            <addr>: the address to upload the integer to
345
            <integer>: the integer to upload
171 farthen 346
        """
532 farthen 347
        addr = to_int(addr)
348
        integer = to_int(integer)
171 farthen 349
        if integer > 0xFFFFFFFF:
350
            raise ArgumentError("Specified integer too long")
548 farthen 351
        data = struct.pack("<I", integer)
427 farthen 352
        self.emcore.write(addr, data)
516 farthen 353
        self.logger.info("Integer '0x%X' written successfully to 0x%X\n" % (integer, addr))
517 farthen 354
 
171 farthen 355
    @command
356
    def downloadint(self, addr):
357
        """
358
            Downloads a single integer from the device and prints it to the console window
236 farthen 359
            <addr>: the address to download the integer from
171 farthen 360
        """
532 farthen 361
        addr = to_int(addr)
427 farthen 362
        data = self.emcore.read(addr, 4)
548 farthen 363
        integer = struct.unpack("<I", data)[0]
516 farthen 364
        self.logger.info("Read '0x%X' from address 0x%X\n" % (integer, addr))
517 farthen 365
 
171 farthen 366
    @command
176 farthen 367
    def i2cread(self, bus, slave, addr, size):
171 farthen 368
        """
369
            Reads data from an I2C device
402 farthen 370
            <bus>: the bus index
371
            <slave>: the slave address
372
            <addr>: the start address on the I2C device
373
            <size>: the number of bytes to read
171 farthen 374
        """
532 farthen 375
        bus = to_int(bus)
376
        slave = to_int(slave)
377
        addr = to_int(addr)
378
        size = to_int(size)
427 farthen 379
        data = self.emcore.i2cread(bus, slave, addr, size)
548 farthen 380
        bytes = struct.unpack("<%dB" % len(data), data)
236 farthen 381
        self.logger.info("Data read from I2C:\n")
382
        for index, byte in enumerate(bytes):
533 theseven 383
            self.logger.info("%02X: %02X\n" % (addr + index, byte))
517 farthen 384
 
171 farthen 385
    @command
176 farthen 386
    def i2cwrite(self, bus, slave, addr, *args):
171 farthen 387
        """
388
            Writes data to an I2C device
402 farthen 389
            <bus>: the bus index
390
            <slave>: the slave address
391
            <addr>: the start address on the I2C device
532 farthen 392
            <db1> ... <dbN>: the data in single bytes,
236 farthen 393
                seperated by whitespaces, eg. 37 5A 4F EB
171 farthen 394
        """
532 farthen 395
        bus = to_int(bus)
396
        slave = to_int(slave)
397
        addr = to_int(addr)
937 theseven 398
        data = b""
171 farthen 399
        for arg in args:
938 theseven 400
            data += struct.pack("B", to_int(arg))
236 farthen 401
        self.logger.info("Writing data to I2C...\n")
427 farthen 402
        self.emcore.i2cwrite(bus, slave, addr, data)
236 farthen 403
        self.logger.info("done\n")
517 farthen 404
 
171 farthen 405
    @command
176 farthen 406
    def console(self):
171 farthen 407
        """
176 farthen 408
            Reads data from the USB console continuously
171 farthen 409
        """
896 theseven 410
        size = 48
176 farthen 411
        while True:
896 theseven 412
            resp = self.emcore.usbcread(size)
599 farthen 413
            self.logger.write(resp.data, target = "stdout")
896 theseven 414
            size = max(48, min(len(resp.data), size) + resp.queuesize)
415
            if size < 0x800:
416
                time.sleep(0.1 / 0x800 * (0x800 - size))
517 farthen 417
 
171 farthen 418
    @command
176 farthen 419
    def writeusbconsole(self, *args):
171 farthen 420
        """
176 farthen 421
            Writes the string <db1> ... <dbN> to the USB console.
171 farthen 422
        """
176 farthen 423
        text = ""
424
        for word in args:
425
            text += word + " "
426
        text = text[:-1]
516 farthen 427
        self.logger.info("Writing '%s' to the usb console\n" % text)
427 farthen 428
        self.emcore.usbcwrite(text)
517 farthen 429
 
171 farthen 430
    @command
176 farthen 431
    def readdevconsole(self, bitmask):
171 farthen 432
        """
176 farthen 433
            Reads data continuously from one or more of the device's consoles.
434
            <bitmask>: the bitmask of the consoles to read from.
171 farthen 435
        """
532 farthen 436
        bitmask = to_int(bitmask)
176 farthen 437
        while True:
427 farthen 438
            resp = self.emcore.cread()
501 farthen 439
            self.logger.write(resp.data)
176 farthen 440
            time.sleep(0.1 / resp.maxsize * (resp.maxsize - len(resp.data)))
441
 
171 farthen 442
    @command
176 farthen 443
    def writedevconsole(self, bitmask, *args):
171 farthen 444
        """
176 farthen 445
            Writes the string <db1> ... <dbN> to one or more of the device's consoles.
446
            <bitmask>: the bitmask of the consoles to write to
171 farthen 447
        """
532 farthen 448
        bitmask = to_int(bitmask)
176 farthen 449
        text = ""
450
        for word in args:
451
            text += word + " "
452
        text = text[:-1]
516 farthen 453
        self.logger.info("Writing '%s' to the device consoles identified with 0x%X\n" % (text, bitmask))
427 farthen 454
        self.emcore.cwrite(text, bitmask)
517 farthen 455
 
171 farthen 456
    @command
457
    def flushconsolebuffers(self, bitmask):
458
        """
459
            flushes one or more of the device consoles' buffers.
460
            <bitmask>: the bitmask of the consoles to be flushed
461
        """
532 farthen 462
        bitmask = to_int(bitmask)
516 farthen 463
        self.logger.info("Flushing consoles identified with the bitmask 0x%X\n" % bitmask)
427 farthen 464
        self.emcore.cflush(bitmask)
517 farthen 465
 
171 farthen 466
    @command
467
    def getprocinfo(self):
468
        """
469
            Fetches data on the currently running processes
470
        """
173 farthen 471
        import datetime
427 farthen 472
        threads = self.emcore.getprocinfo()
382 farthen 473
        threadload = 0
474
        idleload = 0
173 farthen 475
        for thread in threads:
392 theseven 476
            if thread.id != 0:
477
                threadload += thread.cpuload / 255.
382 farthen 478
            else:
392 theseven 479
                idleload += thread.cpuload / 255.
382 farthen 480
        coreload = 1 - (threadload + idleload)
481
        cpuload = threadload + coreload
392 theseven 482
        self.logger.info("Threads: %d, CPU load: %.1f%%, kernel load: %.1f%%, user load: %.1f%%\n\n"
483
                         % (len(threads), cpuload * 100, coreload * 100, threadload * 100))
382 farthen 484
        self.logger.info("Thread dump:\n")
485
        for thread in threads:
516 farthen 486
            self.logger.info("%s:\n" % thread.name, 2)
487
            self.logger.info("Threadstruct address: 0x%X\n" % thread.addr, 4)
488
            self.logger.info("Thread type: %s\n" % thread.thread_type, 4)
489
            self.logger.info("Thread state: %s\n" % thread.state, 4)
605 farthen 490
            if thread.block_type != "THREAD_NOT_BLOCKED":
609 farthen 491
                self.logger.info("Block type: %s\n" % thread.block_type, 4)
492
                if thread.block_type == "THREAD_BLOCK_MUTEX":
493
                    self.logger.info("Blocked by mutex: 0x%X\n" % thread.blocked_by, 6)
494
                    self.logger.info("Owner: %s (0x%X)\n" % (thread.blocked_by.owner.name, thread.blocked_by.owner), 8)
495
                elif thread.block_type == "THREAD_BLOCK_WAKEUP":
496
                    self.logger.info("Blocked by wakeup: 0x%X\n" % thread.blocked_by, 6)
516 farthen 497
            self.logger.info("Priority: %d/255\n" % thread.priority, 4)
396 farthen 498
            self.logger.info("Current CPU load: %.1f%%\n" % ((thread.cpuload * 100) / 255.), 4)
516 farthen 499
            self.logger.info("CPU time (total): %s\n" % datetime.timedelta(microseconds = thread.cputime_total), 4)
500
            self.logger.info("Stack address: 0x%X\n" % thread.stack, 4)
396 farthen 501
            self.logger.info("Registers:\n", 4)
177 farthen 502
            for registerrange in range(4):
503
                for register in range(registerrange, 16, 4):
504
                    registerrepr = "r"+str(register)
606 farthen 505
                    self.logger.info("{0:>3s}: 0x{1:08X}   ".format(registerrepr, thread.regs[register]), 5)
177 farthen 506
                self.logger.info("\n")
506 farthen 507
            self.logger.info("cpsr: 0x{0:08X}".format(thread.cpsr), 6)
173 farthen 508
            self.logger.info("\n")
509
 
171 farthen 510
    @command
511
    def lockscheduler(self):
512
        """
513
            Locks (freezes) the scheduler
514
        """
176 farthen 515
        self.logger.info("Will now lock scheduler\n")
427 farthen 516
        self.emcore.lockscheduler()
178 farthen 517
 
171 farthen 518
    @command
519
    def unlockscheduler(self):
520
        """
521
            Unlocks (unfreezes) the scheduler
522
        """
176 farthen 523
        self.logger.info("Will now unlock scheduler\n")
427 farthen 524
        self.emcore.unlockscheduler()
178 farthen 525
 
171 farthen 526
    @command
516 farthen 527
    def suspendthread(self, threadaddr):
171 farthen 528
        """
516 farthen 529
            Suspends the thread with the thread address <threadaddr>
171 farthen 530
        """
532 farthen 531
        threadaddr = to_int(threadaddr)
516 farthen 532
        self.logger.info("Suspending the thread with the threadaddr 0x%X\n" % threadaddr)
533
        self.emcore.suspendthread(threadaddr)
517 farthen 534
 
171 farthen 535
    @command
516 farthen 536
    def resumethread(self, threadaddr):
171 farthen 537
        """
516 farthen 538
            Resumes the thread with the thread address <threadaddr>
171 farthen 539
        """
532 farthen 540
        threadaddr = to_int(threadaddr)
516 farthen 541
        self.logger.info("Resuming the thread with the threadaddr 0x%X\n" % threadaddr)
542
        self.emcore.resumethread(threadaddr)
517 farthen 543
 
171 farthen 544
    @command
516 farthen 545
    def killthread(self, threadaddr):
171 farthen 546
        """
516 farthen 547
            Kills the thread with the thread address <threadaddr>
171 farthen 548
        """
532 farthen 549
        threadaddr = to_int(threadaddr)
516 farthen 550
        self.logger.info("Killing the thread with the threadaddr 0x%X\n" % threadaddr)
551
        self.emcore.killthread(threadaddr)
517 farthen 552
 
171 farthen 553
    @command
554
    def createthread(self, nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state):
555
        """
516 farthen 556
            Creates a new thread and returns its thread pointer
402 farthen 557
            <namepointer>: a pointer to the thread's name
558
            <entrypoint>: a pointer to the entrypoint of the thread
559
            <stackpointer>: a pointer to the stack of the thread
560
            <stacksize>: the size of the thread's stack
516 farthen 561
            <threadtype>: the thread type, vaild are: 0 => user thread, 1 => system thread
402 farthen 562
            <priority>: the priority of the thread, from 1 to 255
563
            <state>: the thread's initial state, valid are: 1 => ready, 0 => suspended
171 farthen 564
        """
532 farthen 565
        nameptr = to_int(nameptr)
566
        entrypoint = to_int(entrypoint)
567
        stackptr = to_int(stackptr)
568
        stacksize = to_int(stacksize)
569
        priority = to_int(priority)
516 farthen 570
        data = self.emcore.createthread(nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state)
427 farthen 571
        name = self.emcore.readstring(nameptr)
516 farthen 572
        self.logger.info("Created a thread with the thread pointer 0x%X, the name \"%s\", the entrypoint at 0x%X," \
573
                         "the stack at 0x%X with a size of 0x%X and a priority of %d/255\n" %
574
                        (data.threadptr, name, entrypoint, stackptr, stacksize, priority))
172 farthen 575
 
576
    @command
577
    def run(self, filename):
578
        """
427 farthen 579
            Uploads the emCORE application <filename> to
236 farthen 580
            the memory and executes it
172 farthen 581
        """
236 farthen 582
        try:
583
            f = open(filename, 'rb')
584
        except IOError:
585
            raise ArgumentError("File not readable. Does it exist?")
238 farthen 586
        with f:
427 farthen 587
            data = self.emcore.run(f.read())
516 farthen 588
        self.logger.info("Executed emCORE application as thread 0x%X\n" % data.thread)
517 farthen 589
 
171 farthen 590
    @command
173 farthen 591
    def execimage(self, addr):
171 farthen 592
        """
427 farthen 593
            Executes the emCORE application at <addr>.
171 farthen 594
        """
532 farthen 595
        addr = to_int(addr)
516 farthen 596
        self.logger.info("Starting emCORE app at 0x%X\n" % addr)
427 farthen 597
        self.emcore.execimage(addr)
176 farthen 598
 
171 farthen 599
    @command
176 farthen 600
    def flushcaches(self):
171 farthen 601
        """
176 farthen 602
            Flushes the CPUs data and instruction caches.
603
        """
604
        self.logger.info("Flushing CPU data and instruction caches...")
427 farthen 605
        self.emcore.flushcaches()
176 farthen 606
        self.logger.info("done\n")
607
 
608
    @command
609
    def readbootflash(self, addr_flash, addr_mem, size):
610
        """
171 farthen 611
            Reads <size> bytes from bootflash to memory.
612
            <addr_bootflsh>: the address in bootflash to read from
613
            <addr_mem>: the address in memory to copy the data to
614
        """
532 farthen 615
        addr_flash = to_int(addr_flash)
616
        addr_mem = to_int(addr_mem)
617
        size = to_int(size)
516 farthen 618
        self.logger.info("Dumping boot flash from 0x%X - 0x%X to 0x%X - 0x%X\n" %
619
                        (addr_flash, addr_flash + size, addr_mem, addr_mem + size))
427 farthen 620
        self.emcore.bootflashread(addr_mem, addr_flash, size)
176 farthen 621
 
171 farthen 622
    @command
176 farthen 623
    def writebootflash(self, addr_flash, addr_mem, size, force=False):
171 farthen 624
        """
625
            Writes <size> bytes from memory to bootflash.
626
            ATTENTION: Don't call this unless you really know what you're doing!
627
            This may BRICK your device (unless it has a good recovery option)
628
            <addr_mem>: the address in memory to copy the data from
629
            <addr_bootflsh>: the address in bootflash to write to
775 theseven 630
            [force]: Use this flag to suppress the 10 seconds delay
171 farthen 631
        """
532 farthen 632
        addr_flash = to_int(addr_flash)
633
        addr_mem = to_int(addr_mem)
634
        size = to_int(size)
635
        force = to_bool(force)
516 farthen 636
        self.logger.warn("Writing boot flash from the memory in 0x%X - 0x%X to 0x%X - 0x%X\n" %
637
                        (addr_mem, addr_mem + size, addr_flash, addr_flash + size))
174 farthen 638
        if force == False:
382 farthen 639
            self.logger.warn("If this was not what you intended press Ctrl-C NOW")
176 farthen 640
            for i in range(10):
174 farthen 641
                self.logger.info(".")
642
                time.sleep(1)
643
            self.logger.info("\n")
427 farthen 644
        self.emcore.bootflashwrite(addr_mem, addr_flash, size)
176 farthen 645
 
171 farthen 646
    @command
442 farthen 647
    def runfirmware(self, targetaddr, filename):
171 farthen 648
        """
402 farthen 649
            Uploads the firmware in <filename>
442 farthen 650
            to an allocated buffer and executes it at <targetaddr>.
171 farthen 651
        """
532 farthen 652
        targetaddr = to_int(targetaddr)
442 farthen 653
        addr, size = self.uploadfile(filename)
654
        self.execfirmware(targetaddr, addr, size)
64 benedikt93 655
 
171 farthen 656
    @command
442 farthen 657
    def execfirmware(self, targetaddr, addr, size):
176 farthen 658
        """
442 farthen 659
            Moves the firmware at <addr> with <size> to <targetaddr> and executes it
176 farthen 660
        """
532 farthen 661
        targetaddr = to_int(targetaddr)
662
        addr = to_int(addr)
536 farthen 663
        size = to_int(size)
664
        self.logger.info("Running firmware at 0x%X. Bye.\n" % targetaddr)
442 farthen 665
        self.emcore.execfirmware(targetaddr, addr, size)
176 farthen 666
 
667
    @command
171 farthen 668
    def aesencrypt(self, addr, size, keyindex):
669
        """
172 farthen 670
            Encrypts a buffer using a hardware key
402 farthen 671
            <addr>: the starting address of the buffer
672
            <size>: the size of the buffer
673
            <keyindex>: the index of the key in the crypto unit
171 farthen 674
        """
532 farthen 675
        addr = to_int(addr)
676
        size = to_int(size)
677
        keyindex = to_int(keyindex)
427 farthen 678
        self.emcore.aesencrypt(addr, size, keyindex)
82 benedikt93 679
 
171 farthen 680
    @command
681
    def aesdecrypt(self, addr, size, keyindex):
682
        """
172 farthen 683
            Decrypts a buffer using a hardware key
402 farthen 684
            <addr>: the starting address of the buffer
685
            <size>: the size of the buffer
686
            <keyindex>: the index of the key in the crypto unit
171 farthen 687
        """
532 farthen 688
        addr = to_int(addr)
689
        size = to_int(size)
690
        keyindex = to_int(keyindex)
427 farthen 691
        self.emcore.aesdecrypt(addr, size, keyindex)
172 farthen 692
 
693
    @command
694
    def hmac_sha1(self, addr, size, destination):
695
        """
402 farthen 696
            Generates a HMAC-SHA1 hash of the buffer
697
            <addr>: the starting address of the buffer
698
            <size>: the size of the buffer
699
            <destination>: the location where the key will be stored
172 farthen 700
        """
532 farthen 701
        addr = to_int(addr)
702
        size = to_int(size)
703
        destination = to_int(destination)
172 farthen 704
        sha1size = 0x14
516 farthen 705
        self.logger.info("Generating hmac-sha1 hash from the buffer at 0x%X with the size 0x%X and saving it to 0x%X - 0x%X\n" %
706
                        (addr, size, destination, destination + sha1size))
427 farthen 707
        self.emcore.hmac_sha1(addr, size, destination)
172 farthen 708
        self.logger.info("done\n")
427 farthen 709
        data = self.emcore.read(destination, sha1size)
172 farthen 710
        hash = ord(data)
516 farthen 711
        self.logger.info("The generated hash is 0x%X\n" % hash)
517 farthen 712
 
227 theseven 713
    @command
714
    def ipodnano2g_getnandinfo(self):
715
        """
716
            Target-specific function: ipodnano2g
717
            Gathers some information about the NAND chip used
718
        """
427 farthen 719
        data = self.emcore.ipodnano2g_getnandinfo()
516 farthen 720
        self.logger.info("NAND chip type: 0x%X\n" % data["type"])
721
        self.logger.info("Number of banks: %d\n" % data["banks"])
722
        self.logger.info("Number of blocks: %d\n" % data["blocks"])
723
        self.logger.info("Number of user blocks: %d\n" % data["userblocks"])
724
        self.logger.info("Pages per block: %d\n" % data["pagesperblock"])
517 farthen 725
 
227 theseven 726
    @command
404 farthen 727
    def ipodnano2g_nandread(self, addr, start, count, doecc=True, checkempty=True):
227 theseven 728
        """
729
            Target-specific function: ipodnano2g
730
            Reads data from the NAND chip into memory
402 farthen 731
            <addr>: the memory location where the data is written to
732
            <start>: start block
733
            <count>: block count
404 farthen 734
            [doecc]: use ecc error correction data
735
            [checkempty]: set statusflags if pages are empty
227 theseven 736
        """
532 farthen 737
        addr = to_int(addr)
738
        start = to_int(start)
739
        count = to_int(count)
740
        doecc = to_bool(doecc)
741
        checkempty = to_bool(checkempty)
516 farthen 742
        self.logger.info("Reading 0x%X NAND pages starting at 0x%X to memory at 0x%X..." %
743
                        (count, start, addr))
427 farthen 744
        self.emcore.ipodnano2g_nandread(addr, start, count, doecc, checkempty)
227 theseven 745
        self.logger.info("done\n")
517 farthen 746
 
227 theseven 747
    @command
404 farthen 748
    def ipodnano2g_nandwrite(self, addr, start, count, doecc=True):
227 theseven 749
        """
750
            Target-specific function: ipodnano2g
751
            Writes data to the NAND chip
402 farthen 752
            <addr>: the memory location where the data is read from
753
            <start>: start block
754
            <count>: block count
404 farthen 755
            [doecc]: create ecc error correction data
227 theseven 756
        """
532 farthen 757
        addr = to_int(addr)
758
        start = to_int(start)
759
        count = to_int(count)
760
        doecc = to_bool(doecc)
516 farthen 761
        self.logger.info("Writing 0x%X NAND pages starting at 0x%X from memory at 0x%X..." %
762
                        (count, start, addr))
427 farthen 763
        self.emcore.ipodnano2g_nandwrite(addr, start, count, doecc)
227 theseven 764
        self.logger.info("done\n")
517 farthen 765
 
227 theseven 766
    @command
767
    def ipodnano2g_nanderase(self, addr, start, count):
768
        """
769
            Target-specific function: ipodnano2g
770
            Erases blocks on the NAND chip and stores the results to memory
402 farthen 771
            <addr>: the memory location where the results are stored
772
            <start>: start block
773
            <count>: block count
227 theseven 774
        """
532 farthen 775
        addr = to_int(addr)
776
        start = to_int(start)
777
        count = to_int(count)
516 farthen 778
        self.logger.info("Erasing 0x%X NAND pages starting at 0x%X and logging to 0x%X..." %
779
                        (count, start, addr))
427 farthen 780
        self.emcore.ipodnano2g_nanderase(addr, start, count)
227 theseven 781
        self.logger.info("done\n")
517 farthen 782
 
228 theseven 783
    @command
784
    def ipodnano2g_dumpnand(self, filenameprefix):
785
        """
786
            Target-specific function: ipodnano2g
787
            Dumps the whole NAND chip to four files
402 farthen 788
            <filenameprefix>: prefix of the files that will be created
228 theseven 789
        """
427 farthen 790
        info = self.emcore.ipodnano2g_getnandinfo()
228 theseven 791
        try:
775 theseven 792
            buf = self.emcore.memalign(0x10, 0x1088000)
793
            self.logger.info("Dumping NAND contents...")
794
            try:
795
                infofile = open(filenameprefix+"_info.txt", 'wb')
796
                datafile = open(filenameprefix+"_data.bin", 'wb')
797
                sparefile = open(filenameprefix+"_spare.bin", 'wb')
798
                statusfile = open(filenameprefix+"_status.bin", 'wb')
799
            except IOError:
800
                raise ArgumentError("Can not open file for writing!")
801
            infofile.write("NAND chip type: 0x%X\r\n" % info["type"])
802
            infofile.write("Number of banks: %d\r\n" % info["banks"])
803
            infofile.write("Number of blocks: %d\r\n" % info["blocks"])
804
            infofile.write("Number of user blocks: %d\r\n" % info["userblocks"])
805
            infofile.write("Pages per block: %d\r\n" % info["pagesperblock"])
806
            for i in range(info["banks"] * info["blocks"] * info["pagesperblock"] / 8192):
807
                self.logger.info(".")
808
                self.emcore.ipodnano2g_nandread(buf, i * 8192, 8192, 1, 1)
809
                datafile.write(self.emcore.read(buf, 0x01000000))
810
                sparefile.write(self.emcore.read(buf + 0x01000000, 0x00080000))
811
                statusfile.write(self.emcore.read(buf + 0x01080000, 0x00008000))
812
            infofile.close()
813
            datafile.close()
814
            sparefile.close()
815
            statusfile.close()
816
            self.logger.info("done\n")
817
        finally:
818
            self.emcore.free(buf)
819
 
820
    @command
821
    def ipodnano2g_restorenand(self, filenameprefix, force=False):
822
        """
823
            Target-specific function: ipodnano2g
824
            Restores the whole NAND chip from <filenameprefix>_data.bin and <filenameprefix>_spare.bin
825
            [force]: use this flag to suppress the 10 seconds delay
826
        """
827
        self.logger.warn("Flashing NAND image %s!\n" % filenameprefix)
828
        if force == False:
829
            self.logger.warn("If this was not what you intended press Ctrl-C NOW")
830
            for i in range(10):
831
                self.logger.info(".")
832
                time.sleep(1)
833
            self.logger.info("\n\n")
834
        info = self.emcore.ipodnano2g_getnandinfo()
835
        ppb = info["pagesperblock"]
836
        banks = info["banks"]
837
        blocks = info["blocks"]
838
        try:
839
            if (os.path.getsize(filenameprefix+"_data.bin") != blocks * banks * ppb * 2048):
840
                raise ArgumentError("Data file size does not match flash size!")
841
            if (os.path.getsize(filenameprefix+"_spare.bin") != blocks * banks * ppb * 64):
842
                raise ArgumentError("Spare file size does not match flash size!")
843
            datafile = open(filenameprefix+"_data.bin", 'rb')
844
            sparefile = open(filenameprefix+"_spare.bin", 'rb')
228 theseven 845
        except IOError:
775 theseven 846
            raise ArgumentError("Can not open input files!")
847
        try:
776 theseven 848
            buf = self.emcore.memalign(0x10, 0x844)
775 theseven 849
            for block in range(info["blocks"]):
850
                for bank in range(info["banks"]):
776 theseven 851
                    self.logger.info("\r    Erasing block %d bank %d         " % (block, bank))
775 theseven 852
                    self.emcore.ipodnano2g_nanderase(buf, block * banks + bank, 1)
853
                    rc = struct.unpack("<I", self.emcore.read(buf, 4))[0]
854
                    if rc != 0: self.logger.info("\rBlock %d bank %d erase failed with RC %08X\n" % (block, bank, rc))
855
                for page in range(ppb):
776 theseven 856
                    for bank in range(banks):
857
                        data = datafile.read(2048) + sparefile.read(64)
858
                        if data == "\xff" * 2112: continue
859
                        self.emcore.write(buf, data)
860
                        self.logger.info("\rProgramming block %d page %d bank %d" % (block, page, bank))
861
                        self.emcore.ipodnano2g_nandwrite(buf, ((block * ppb) + page) * banks + bank, 1, 0)
862
                        rc = struct.unpack("<I", self.emcore.read(buf + 2112, 4))[0]
863
                        if rc != 0: self.logger.info("\rBlock %d bank %d page %d programming failed with RC %08X\n" % (block, bank, page, rc))
775 theseven 864
        finally:
865
            self.emcore.free(buf)
228 theseven 866
        datafile.close()
867
        sparefile.close()
776 theseven 868
        self.logger.info("\ndone\n")
517 farthen 869
 
228 theseven 870
    @command
871
    def ipodnano2g_wipenand(self, filename, force=False):
872
        """
873
            Target-specific function: ipodnano2g
874
            Wipes the whole NAND chip and logs the result to a file
402 farthen 875
            <filename>: location of the log file
775 theseven 876
            [force]: use this flag to suppress the 10 seconds delay
228 theseven 877
        """
382 farthen 878
        self.logger.warn("Wiping the whole NAND chip!\n")
228 theseven 879
        if force == False:
382 farthen 880
            self.logger.warn("If this was not what you intended press Ctrl-C NOW")
228 theseven 881
            for i in range(10):
882
                self.logger.info(".")
883
                time.sleep(1)
884
            self.logger.info("\n")
885
        try:
775 theseven 886
            buf = self.emcore.malloc(0x100)
887
            info = self.emcore.ipodnano2g_getnandinfo()
888
            self.logger.info("Wiping NAND contents...")
889
            try:
890
                statusfile = open(filename, 'wb')
891
            except IOError:
892
                raise ArgumentError("Can not open file for writing!")
893
            for i in range(info["banks"] * info["blocks"] / 64):
894
                self.logger.info(".")
895
                self.emcore.ipodnano2g_nanderase(buf, i * 64, 64)
896
                statusfile.write(self.emcore.read(buf, 0x00000100))
897
            statusfile.close()
898
            self.logger.info("done\n")
899
        finally:
900
            self.emcore.free(buf)
517 farthen 901
 
346 theseven 902
    @command
787 theseven 903
    def ipodclassic_readbbt(self, filename, tempaddr = None):
904
        """
905
            Target-specific function: ipodclassic
906
            Reads the bad block table from the hard disk to memory at <tempaddr>
907
            (or an allocated block if not given) and writes it to <filename>
908
        """
909
        tempaddr = to_int(tempaddr)
910
        try:
911
            f = open(filename, 'wb')
912
        except IOError:
913
            raise ArgumentError("File not writable.")
914
        self.logger.info("Reading bad block table from disk...")
915
        f.write(self.emcore.ipodclassic_readbbt(tempaddr))
916
        f.close()
917
        self.logger.info(" done\n")
918
 
919
    @command
585 theseven 920
    def ipodclassic_writebbt(self, filename, tempaddr = None):
346 theseven 921
        """
922
            Target-specific function: ipodclassic
585 theseven 923
            Uploads the bad block table <filename> to memory at <tempaddr>
924
            (or an allocated block if not given) and writes it to the hard disk
346 theseven 925
        """
602 theseven 926
        tempaddr = to_int(tempaddr)
346 theseven 927
        try:
928
            f = open(filename, 'rb')
929
        except IOError:
930
            raise ArgumentError("File not readable. Does it exist?")
931
        self.logger.info("Writing bad block table to disk...")
427 farthen 932
        data = self.emcore.ipodclassic_writebbt(f.read(), tempaddr)
346 theseven 933
        f.close()
934
        self.logger.info(" done\n")
517 farthen 935
 
346 theseven 936
    @command
791 theseven 937
    def ipodclassic_disablebbt(self):
938
        """
939
            Target-specific function: ipodclassic
940
            Disables the hard disk bad block table, if present
941
        """
942
        self.logger.info("Disabling hard disk BBT...")
943
        data = self.emcore.ipodclassic_disablebbt()
944
        self.logger.info(" done\n")
945
 
946
    @command
947
    def ipodclassic_reloadbbt(self):
948
        """
949
            Target-specific function: ipodclassic
950
            Reloads the hard disk bad block table, if present
951
        """
952
        self.logger.info("Reloading hard disk BBT...")
953
        data = self.emcore.ipodclassic_reloadbbt()
954
        self.logger.info(" done\n")
955
 
956
    @command
379 theseven 957
    def getvolumeinfo(self, volume):
958
        """
959
            Gathers some information about a storage volume used
404 farthen 960
            <volume>: volume id
379 theseven 961
        """
532 farthen 962
        volume = to_int(volume)
427 farthen 963
        data = self.emcore.storage_get_info(volume)
516 farthen 964
        self.logger.info("Sector size: %d\n" % data["sectorsize"])
965
        self.logger.info("Number of sectors: %d\n" % data["numsectors"])
966
        self.logger.info("Vendor: %s\n" % data["vendor"])
967
        self.logger.info("Product: %s\n" % data["product"])
968
        self.logger.info("Revision: %s\n" % data["revision"])
517 farthen 969
 
379 theseven 970
    @command
971
    def readrawstorage(self, volume, sector, count, addr):
972
        """
973
            Reads <count> sectors starting at <sector> from storage <volume> to memory at <addr>.
974
        """
532 farthen 975
        volume = to_int(volume)
976
        sector = to_int(sector)
977
        count = to_int(count)
978
        addr = to_int(addr)
379 theseven 979
        self.logger.info("Reading volume %s sectors %X - %X to %08X..." % (volume, sector, sector + count - 1, addr))
427 farthen 980
        self.emcore.storage_read_sectors_md(volume, sector, count, addr)
379 theseven 981
        self.logger.info("done\n")
517 farthen 982
 
379 theseven 983
    @command
984
    def writerawstorage(self, volume, sector, count, addr):
985
        """
986
            Writes memory contents at <addr> to <count> sectors starting at <sector> on storage <volume>.
987
        """
532 farthen 988
        volume = to_int(volume)
989
        sector = to_int(sector)
990
        count = to_int(count)
991
        addr = to_int(addr)
379 theseven 992
        self.logger.info("Writing %08X to volume %s sectors %X - %X..." % (addr, volume, sector, sector + count - 1))
427 farthen 993
        self.emcore.storage_write_sectors_md(volume, sector, count, addr)
379 theseven 994
        self.logger.info("done\n")
517 farthen 995
 
379 theseven 996
    @command
475 farthen 997
    def readrawstoragefile(self, volume, sector, count, file, buffsize = 0x100000, buffer = None):
379 theseven 998
        """
999
            Reads <count> sectors starting at <sector> from storage <volume> to file <file>,
402 farthen 1000
            buffering them in memory at [buffer] in chunks of [buffsize] bytes (both optional).
379 theseven 1001
        """
532 farthen 1002
        volume = to_int(volume)
1003
        sector = to_int(sector)
1004
        count = to_int(count)
1005
        buffsize = to_int(buffsize)
379 theseven 1006
        try:
479 theseven 1007
            f = open(file, 'wb')
1008
        except IOError:
1009
            raise ArgumentError("Could not open local file for writing.")
1010
        try:
482 theseven 1011
            storageinfo = self.emcore.storage_get_info(volume)
1012
            buffsize = min(buffsize, storageinfo.sectorsize * count)
479 theseven 1013
            if buffer is None:
644 theseven 1014
                buffer = self.emcore.memalign(0x10, buffsize)
479 theseven 1015
                malloc = True
1016
            else:
532 farthen 1017
                buffer = to_int(buffer)
479 theseven 1018
                malloc = False
472 farthen 1019
            try:
479 theseven 1020
                self.logger.info("Reading volume %s sectors %X - %X to %s..." % (volume, sector, sector + count - 1, file))
1021
                while count > 0:
1022
                    sectors = min(count, int(buffsize / storageinfo.sectorsize))
792 theseven 1023
                    self.emcore.storage_read_sectors_md(volume, sector, sectors, buffer)
479 theseven 1024
                    f.write(self.emcore.read(buffer, storageinfo.sectorsize * sectors))
1025
                    sector = sector + sectors
1026
                    count = count - sectors
1027
            finally:
1028
                if malloc == True:
1029
                    self.emcore.free(buffer)
1030
        finally:
472 farthen 1031
            f.close()
379 theseven 1032
        self.logger.info("done\n")
517 farthen 1033
 
379 theseven 1034
    @command
475 farthen 1035
    def writerawstoragefile(self, volume, sector, count, file, buffsize = 0x100000, buffer = None):
379 theseven 1036
        """
1037
            Writes contents of <file> to <count> sectors starting at <sector> on storage <volume>,
402 farthen 1038
            buffering them in memory at [buffer] in chunks of [buffsize] bytes (both optional).
792 theseven 1039
            If <count> is -1, the number of sectors will be determined from the file size.
379 theseven 1040
        """
532 farthen 1041
        volume = to_int(volume)
1042
        sector = to_int(sector)
1043
        count = to_int(count)
1044
        buffsize = to_int(buffsize)
379 theseven 1045
        try:
479 theseven 1046
            f = open(file, 'rb')
1047
        except IOError:
1048
            raise ArgumentError("Could not open local file for reading.")
1049
        try:
482 theseven 1050
            storageinfo = self.emcore.storage_get_info(volume)
792 theseven 1051
            if count == -1: count = int((os.path.getsize(file) + storageinfo.sectorsize - 1) / storageinfo.sectorsize)
482 theseven 1052
            buffsize = min(buffsize, storageinfo.sectorsize * count)
479 theseven 1053
            if buffer is None:
644 theseven 1054
                buffer = self.emcore.memalign(0x10, buffsize)
479 theseven 1055
                malloc = True
1056
            else:
532 farthen 1057
                buffer = to_int(buffer)
479 theseven 1058
                malloc = False
472 farthen 1059
            try:
479 theseven 1060
                self.logger.info("Writing %s to volume %s sectors %X - %X..." % (file, volume, sector, sector + count - 1))
1061
                while count > 0:
1062
                    sectors = min(count, int(buffsize / storageinfo.sectorsize))
1063
                    bytes = storageinfo.sectorsize * sectors
792 theseven 1064
                    data = b""
1065
                    while len(data) < bytes:
1066
                       new = f.read(bytes - len(data))
1067
                       data = data + new
1068
                       if len(new) == 0: break
479 theseven 1069
                    self.emcore.write(buffer, data)
792 theseven 1070
                    self.emcore.storage_write_sectors_md(volume, sector, sectors, buffer)
479 theseven 1071
                    sector = sector + sectors
1072
                    count = count - sectors
1073
            finally:
1074
                if malloc == True:
1075
                    self.emcore.free(buffer)
1076
        finally:
472 farthen 1077
            f.close()
379 theseven 1078
        self.logger.info("done\n")
517 farthen 1079
 
379 theseven 1080
    @command
346 theseven 1081
    def mkdir(self, dirname):
1082
        """
402 farthen 1083
            Creates a directory with the name <dirname>
346 theseven 1084
        """
516 farthen 1085
        self.logger.info("Creating directory %s..." % dirname)
427 farthen 1086
        self.emcore.dir_create(dirname)
346 theseven 1087
        self.logger.info(" done\n")
517 farthen 1088
 
346 theseven 1089
    @command
1090
    def rmdir(self, dirname):
1091
        """
402 farthen 1092
            Removes an empty directory with the name <dirname>
346 theseven 1093
        """
516 farthen 1094
        self.logger.info("Removing directory %s..." % dirname)
427 farthen 1095
        self.emcore.dir_remove(dirname)
346 theseven 1096
        self.logger.info(" done\n")
517 farthen 1097
 
346 theseven 1098
    @command
349 theseven 1099
    def rm(self, filename):
346 theseven 1100
        """
402 farthen 1101
            Removes a file with the name <filename>
346 theseven 1102
        """
516 farthen 1103
        self.logger.info("Removing file %s..." % filename)
427 farthen 1104
        self.emcore.file_unlink(filename)
346 theseven 1105
        self.logger.info(" done\n")
517 farthen 1106
 
346 theseven 1107
    @command
406 theseven 1108
    def rmtree(self, path):
1109
        """
1110
            Recursively removes a folder
1111
            <path>: the folder to be removed
1112
        """
427 farthen 1113
        handle = self.emcore.dir_open(path)
406 theseven 1114
        while True:
1115
            try:
427 farthen 1116
                entry = self.emcore.dir_read(handle)
406 theseven 1117
                if entry.name == "." or entry.name == "..": continue
1118
                elif entry.attributes & 0x10:
1119
                    self.rmtree(path + "/" + entry.name)
1120
                else: self.rm(path + "/" + entry.name)
1121
            except: break
427 farthen 1122
        self.emcore.dir_close(handle)
406 theseven 1123
        self.rmdir(path)
517 farthen 1124
 
406 theseven 1125
    @command
352 theseven 1126
    def mv(self, oldname, newname):
350 theseven 1127
        """
402 farthen 1128
            Renames or moves file or directory <oldname> to <newname>
350 theseven 1129
        """
516 farthen 1130
        self.logger.info("Renaming %s to %s..." % (oldname, newname))
427 farthen 1131
        self.emcore.file_rename(oldname, newname)
350 theseven 1132
        self.logger.info(" done\n")
517 farthen 1133
 
350 theseven 1134
    @command
475 farthen 1135
    def get(self, remotename, localname, buffsize = 0x10000, buffer = None):
346 theseven 1136
        """
1137
            Downloads a file
402 farthen 1138
            <remotename>: filename on the device
1139
            <localname>: filename on the computer
472 farthen 1140
            [buffsize]: buffer size (optional)
402 farthen 1141
            [buffer]: buffer address (optional)
346 theseven 1142
        """
532 farthen 1143
        buffsize = to_int(buffsize)
346 theseven 1144
        try:
479 theseven 1145
            f = open(localname, 'wb')
1146
        except IOError:
1147
            raise ArgumentError("Could not open local file for writing.")
1148
        try:
482 theseven 1149
            fd = self.emcore.file_open(remotename, 0)
472 farthen 1150
            try:
482 theseven 1151
                size = self.emcore.file_size(fd)
1152
                buffsize = min(buffsize, size)
1153
                if buffer is None:
644 theseven 1154
                    buffer = self.emcore.memalign(0x10, buffsize)
482 theseven 1155
                    malloc = True
1156
                else:
532 farthen 1157
                    buffer = to_int(buffer)
482 theseven 1158
                    malloc = False
479 theseven 1159
                try:
516 farthen 1160
                    self.logger.info("Downloading file %s to %s..." % (remotename, localname))
479 theseven 1161
                    while size > 0:
488 theseven 1162
                        bytes = self.emcore.file_read(fd, buffsize, buffer).rc
479 theseven 1163
                        f.write(self.emcore.read(buffer, bytes))
1164
                        size = size - bytes
1165
                finally:
482 theseven 1166
                    if malloc == True:
1167
                        self.emcore.free(buffer)
479 theseven 1168
            finally:
482 theseven 1169
                self.emcore.file_close(fd)
479 theseven 1170
        finally:
472 farthen 1171
            f.close()
346 theseven 1172
        self.logger.info(" done\n")
517 farthen 1173
 
346 theseven 1174
    @command
475 farthen 1175
    def gettree(self, remotepath, localpath, buffsize = 0x10000, buffer = None):
406 theseven 1176
        """
1177
            Downloads a directory tree
1178
            <remotepath>: path on the device
1179
            <localpath>: path on the computer
472 farthen 1180
            [buffsize]: buffer size (optional)
406 theseven 1181
            [buffer]: buffer address (optional)
1182
        """
532 farthen 1183
        buffsize = to_int(buffsize)
479 theseven 1184
        handle = self.emcore.dir_open(remotepath)
472 farthen 1185
        try:
479 theseven 1186
            if buffer is None:
644 theseven 1187
                buffer = self.emcore.memalign(0x10, buffsize)
479 theseven 1188
                malloc = True
1189
            else:
532 farthen 1190
                buffer = to_int(buffer)
479 theseven 1191
                malloc = False
1192
            try:
1193
                try: os.mkdir(localpath)
1194
                except: pass
1195
                while True:
1196
                    try:
1197
                        entry = self.emcore.dir_read(handle)
1198
                    except: break
486 theseven 1199
                    if entry.name == "." or entry.name == "..": continue
1200
                    elif entry.attributes & 0x10:
1201
                        self.gettree(remotepath + "/" + entry.name, localpath + "/" + entry.name, buffsize, buffer)
1202
                    else: self.get(remotepath + "/" + entry.name, localpath + "/" + entry.name, buffsize, buffer)
479 theseven 1203
            finally:
1204
                if malloc == True:
1205
                    self.emcore.free(buffer)
1206
        finally:
472 farthen 1207
            self.emcore.dir_close(handle)
517 farthen 1208
 
406 theseven 1209
    @command
475 farthen 1210
    def put(self, localname, remotename, buffsize = 0x10000, buffer = None):
346 theseven 1211
        """
1212
            Uploads a file
406 theseven 1213
            <localname>: filename on the computer
402 farthen 1214
            <remotename>: filename on the device
472 farthen 1215
            [buffsize]: buffer size (optional)
402 farthen 1216
            [buffer]: buffer address (optional)
346 theseven 1217
        """
532 farthen 1218
        buffsize = to_int(buffsize)
346 theseven 1219
        try:
479 theseven 1220
            f = open(localname, 'rb')
1221
        except IOError:
1222
            raise ArgumentError("Could not open local file for reading.")
1223
        try:
482 theseven 1224
            buffsize = min(buffsize, os.path.getsize(localname))
479 theseven 1225
            if buffer is None:
644 theseven 1226
                buffer = self.emcore.memalign(0x10, buffsize)
479 theseven 1227
                malloc = True
1228
            else:
532 farthen 1229
                buffer = to_int(buffer)
479 theseven 1230
                malloc = False
472 farthen 1231
            try:
516 farthen 1232
                self.logger.info("Uploading file %s to %s..." % (localname, remotename))
479 theseven 1233
                fd = self.emcore.file_open(remotename, 0x15)
1234
                try:
1235
                    while True:
1236
                        data = f.read(buffsize)
1237
                        if len(data) == 0: break
1238
                        self.emcore.write(buffer, data)
1239
                        bytes = 0
1240
                        while bytes < len(data):
1241
                            bytes = bytes + self.emcore.file_write(fd, len(data) - bytes, buffer + bytes)
1242
                finally:
1243
                    self.emcore.file_close(fd)
1244
            finally:
1245
                if malloc == True:
1246
                    self.emcore.free(buffer)
1247
        finally:
472 farthen 1248
            f.close()
346 theseven 1249
        self.logger.info(" done\n")
517 farthen 1250
 
346 theseven 1251
    @command
475 farthen 1252
    def puttree(self, localpath, remotepath, buffsize = 0x10000, buffer = None):
406 theseven 1253
        """
1254
            Uploads a directory tree
1255
            <localpath>: path on the computer
1256
            <remotepath>: path on the device
472 farthen 1257
            [buffsize]: buffer size (optional)
406 theseven 1258
            [buffer]: buffer address (optional)
1259
        """
532 farthen 1260
        buffsize = to_int(buffsize)
472 farthen 1261
        if buffer is None:
644 theseven 1262
            buffer = self.emcore.memalign(0x10, buffsize)
472 farthen 1263
            malloc = True
1264
        else:
532 farthen 1265
            buffer = to_int(buffer)
472 farthen 1266
            malloc = False
1267
        try:
1268
            try: self.mkdir(remotepath)
1269
            except: self.logger.info(" failed\n")
1270
            pathlen = len(localpath)
1271
            for d in os.walk(localpath):
1272
                prefix = remotepath + "/" + d[0].replace("\\", "/")[pathlen:] + "/"
1273
                for dir in d[1]:
1274
                    if dir != ".svn":
1275
                        try: self.mkdir(prefix + dir)
1276
                        except: self.logger.info(" failed\n")
1277
                for f in d[2]:
479 theseven 1278
                    if prefix.find("/.svn/") == -1:
477 farthen 1279
                        self.put(d[0] + "/" + f, prefix + f, buffsize, buffer)
472 farthen 1280
        finally:
1281
            if malloc == True:
1282
                self.emcore.free(buffer)
517 farthen 1283
 
406 theseven 1284
    @command
351 theseven 1285
    def ls(self, path = "/"):
346 theseven 1286
        """
1287
            Lists all files in the specified path
402 farthen 1288
            [path]: the path which is listed
346 theseven 1289
        """
427 farthen 1290
        handle = self.emcore.dir_open(path)
516 farthen 1291
        self.logger.info("Directory listing of %s:\n" % path)
346 theseven 1292
        while True:
1293
            try:
427 farthen 1294
                entry = self.emcore.dir_read(handle)
346 theseven 1295
            except: break
486 theseven 1296
            if entry.attributes & 0x10: size = "DIR"
1297
            else: size = locale.format("%d", entry.size, True).rjust(13)
1298
            self.logger.info(entry.name.ljust(50) + " - " + size + "\n")
427 farthen 1299
        self.emcore.dir_close(handle)
474 farthen 1300
 
1301
    @command
483 theseven 1302
    def find(self, path = "/"):
1303
        """
1304
            Lists all files in the specified path, recursively
1305
            [path]: the path which is listed
1306
        """
1307
        handle = self.emcore.dir_open(path)
1308
        self.logger.info(path + "/\n")
1309
        while True:
1310
            try:
1311
                entry = self.emcore.dir_read(handle)
1312
            except: break
486 theseven 1313
            if entry.name == "." or entry.name == "..": continue
1314
            elif entry.attributes & 0x10: self.find(path + "/" + entry.name)
1315
            else: self.logger.info(path + "/" + entry.name + "\n")
483 theseven 1316
        self.emcore.dir_close(handle)
1317
 
1318
    @command
474 farthen 1319
    def malloc(self, size):
1320
        """ Allocates <size> bytes and returns a pointer to the allocated memory """
532 farthen 1321
        size = to_int(size)
474 farthen 1322
        self.logger.info("Allocating %d bytes of memory\n" % size)
1323
        addr = self.emcore.malloc(size)
518 farthen 1324
        self.logger.info("Allocated %d bytes of memory at 0x%X\n" % (size, addr))
474 farthen 1325
 
1326
    @command
1327
    def memalign(self, align, size):
1328
        """ Allocates <size> bytes aligned to <align> and returns a pointer to the allocated memory """
532 farthen 1329
        align = to_int(align)
1330
        size = to_int(size)
518 farthen 1331
        self.logger.info("Allocating %d bytes of memory aligned to 0x%X\n" % (size, align))
474 farthen 1332
        addr = self.emcore.memalign(align, size)
518 farthen 1333
        self.logger.info("Allocated %d bytes of memory at 0x%X\n" % (size, addr))
474 farthen 1334
 
1335
    @command
1336
    def realloc(self, ptr, size):
1337
        """ The size of the memory block pointed to by <ptr> is changed to the <size> bytes,
1338
            expanding or reducing the amount of memory available in the block.
1339
            Returns a pointer to the reallocated memory.
1340
        """
532 farthen 1341
        ptr = to_int(ptr)
1342
        size = to_int(size)
518 farthen 1343
        self.logger.info("Reallocating 0x%X to have the new size %d\n" % (ptr, size))
474 farthen 1344
        addr = self.emcore.realloc(ptr, size)
518 farthen 1345
        self.logger.info("Reallocated memory at 0x%X to 0x%X with the new size %d\n" % (ptr, addr, size))
474 farthen 1346
 
1347
    @command
1348
    def reownalloc(self, ptr, owner):
1349
        """ Changes the owner of the memory allocation <ptr> to the thread struct at addr <owner> """
532 farthen 1350
        ptr = to_int(ptr)
1351
        owner = to_int(owner)
518 farthen 1352
        self.logger.info("Changing owner of the memory region 0x%X to 0x%X\n" % (ptr, owner))
474 farthen 1353
        self.emcore.reownalloc(ptr, owner)
518 farthen 1354
        self.logger.info("Successfully changed owner of 0x%X to 0x%X\n" % (ptr, owner))
474 farthen 1355
 
1356
    @command
1357
    def free(self, ptr):
1358
        """ Frees the memory space pointed to by 'ptr' """
532 farthen 1359
        ptr = to_int(ptr)
518 farthen 1360
        self.logger.info("Freeing the memory region at 0x%X\n" % ptr)
474 farthen 1361
        self.emcore.free(ptr)
518 farthen 1362
        self.logger.info("Successfully freed the memory region at 0x%X\n" % ptr)
474 farthen 1363
 
1364
    @command
1365
    def free_all(self):
1366
        """ Frees all memory allocations created by the monitor thread """
1367
        self.logger.info("Freeing all memory allocations created by the monitor thread\n")
1368
        self.emcore.free_all()
1369
        self.logger.info("Successfully freed all memory allocations created by the monitor thread\n")
778 farthen 1370
 
1371
    @command
1372
    def rtcread(self):
1373
        """ Reads the real time clock on the device """
1374
        import datetime
1375
        self.logger.info("Reading the clock\n")
1376
        dt = self.emcore.rtcread()
1377
        self.logger.info("Successfully read the clock: %s\n" % (dt.ctime()))
1378
 
1379
    @command
1380
    def rtcwrite(self):
1381
        """ Sets the real time clock on the device to the current local time """
1382
        import datetime
1383
        dt = datetime.datetime.now()
1384
        self.logger.info("Setting the clock to: %s\n" % (dt.ctime()))
1385
        self.emcore.rtcwrite(dt)
1386
        self.logger.info("Successfully set the clock\n")
474 farthen 1387
 
346 theseven 1388
 
171 farthen 1389
if __name__ == "__main__":
1390
    if len(sys.argv) < 2:
502 farthen 1391
        usage("No command specified", docstring = False)
382 farthen 1392
    try:
1393
        interface = Commandline()
1394
        interface._parsecommand(sys.argv[1], sys.argv[2:])
1395
    except KeyboardInterrupt:
778 farthen 1396
        sys.exit()