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
#
82 benedikt93 7
#    This file is part of emBIOS.
64 benedikt93 8
#
82 benedikt93 9
#    emBIOS 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
#
82 benedikt93 14
#    emBIOS 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
20
#    along with emBIOS.  If not, see <http://www.gnu.org/licenses/>.
64 benedikt93 21
#
22
#
23
 
171 farthen 24
import sys
25
import os
26
import inspect
27
import re
176 farthen 28
import time
215 theseven 29
import struct
346 theseven 30
import locale
64 benedikt93 31
 
176 farthen 32
from functools import wraps
33
 
64 benedikt93 34
import libembios
171 farthen 35
from libembios import Error
36
import libembiosdata
64 benedikt93 37
 
236 farthen 38
 
171 farthen 39
class NotImplementedError(Error):
40
    pass
64 benedikt93 41
 
171 farthen 42
class ArgumentError(Error):
43
    pass
64 benedikt93 44
 
171 farthen 45
class ArgumentTypeError(Error):
46
    def __init__(self, expected, seen=False):
47
        self.expected = expected
48
        self.seen = seen
49
    def __str__(self):
50
        if self.seen:
51
            return "Expected " + str(self.expected) + " but saw " + str(self.seen)
52
        else:
53
            return "Expected " + str(self.expected) + ", but saw something else"
64 benedikt93 54
 
55
 
171 farthen 56
def usage(errormsg=None, specific=False):
57
    """
58
        Prints the usage information.
59
        It is auto generated from various places.
60
    """
61
    logger = Logger()
62
    cmddict= Commandline.cmddict
63
    doc = {}
64
    # This sorts the output of various internal functions
65
    # and puts everything in easy readable form
66
    for function in cmddict:
67
        function = cmddict[function].func
68
        docinfo = {}
69
        name = function.__name__
70
        args = inspect.getargspec(function)[0]
71
        docinfo['varargs'] = False
72
        if inspect.getargspec(function)[1]:
73
            docinfo['varargs'] = True
74
        kwargvalues = inspect.getargspec(function)[3]
75
        kwargs = {}
76
        if args:
77
            if kwargvalues:
78
                argnum = len(args) - len(kwargvalues)
79
                kwargnum = len(kwargvalues)
80
                kwargs = dict(zip(args[argnum:], kwargvalues))
81
            else:
82
                argnum = len(args)
83
        else:
84
            argnum = 0
85
        docinfo['args'] = args[1:argnum]
86
        docinfo['kwargs'] = kwargs
87
        if function.__doc__:
88
            # strip unneccessary whitespace
89
            docinfo['documentation'] = re.sub(r'\n        ', '\n', function.__doc__)
90
        else:
91
            docinfo['documentation'] = None
92
        doc[name] = docinfo
64 benedikt93 93
 
171 farthen 94
    if not specific:
95
        logger.log("Please provide a command and (if needed) parameters as command line arguments\n\n")
96
        logger.log("Available commands:\n\n")
82 benedikt93 97
    else:
171 farthen 98
        logger.log("\n")
99
    for function in sorted(doc.items()):
100
        function = function[0]
101
        if specific == False or specific == function:
102
            logger.log("  " + function + " ")
103
            for arg in doc[function]['args']:
104
                logger.log("<" + arg + "> ")
105
            if doc[function]['kwargs']:
106
                for kwarg in doc[function]['kwargs']:
107
                    logger.log("[" + kwarg + "] ")
108
            if doc[function]['varargs']:
109
                logger.log("<db1> ... <dbN>")
110
            if doc[function]['documentation']:
111
                logger.log(doc[function]['documentation']+"\n")
64 benedikt93 112
 
171 farthen 113
    logger.log("\n")
114
 
115
    if errormsg:
116
        logger.error(str(errormsg)+"\n")
117
    exit(2)
118
 
119
 
120
class Logger(object):
121
    """
122
        Simple stdout logger.
123
        Loglevel 4 is most verbose, Loglevel 0 only say something if there is an error.
124
    """
125
    def __init__(self):
126
        # Possible values: 0 (only errors), 1 (warnings), 2 (info, recommended for production use), 3 and more (debug)
127
        self.loglevel = 3
128
 
129
    def log(self, text):
130
        sys.stdout.write(text)
67 benedikt93 131
 
171 farthen 132
    def debug(self, text):
133
        if self.loglevel >= 3:
134
            self.log(text)
119 benedikt93 135
 
171 farthen 136
    def info(self, text):
137
        if self.loglevel >= 2:
138
            self.log(text)
82 benedikt93 139
 
171 farthen 140
    def warning(self, text):
141
        if self.loglevel >= 1:
142
            self.log("WARNING: " + text)
119 benedikt93 143
 
171 farthen 144
    def error(self, text):
145
        self.log("ERROR: " + text)
146
 
147
 
148
def command(func):
149
    """
150
        Decorator for all commands.
151
        The decorated function is called with (self, all, other, arguments, ...)
152
    """
176 farthen 153
    @wraps(func)
172 farthen 154
    def decorator(*args):
171 farthen 155
        return func(args[0], *args[1:])
156
    func._command = True
157
    decorator.func = func
158
    return decorator
159
 
160
 
161
def commandClass(cls):
162
    """
163
        Decorator for the class. Sets the self.cmddict of the class
164
        to all functions decorated with @command
165
    """
166
    cls.cmddict = {}
167
    for attr, value in cls.__dict__.iteritems():
168
        if getattr(value, 'func', False):
169
            if getattr(value.func, '_command', False):
170
                cls.cmddict[value.func.__name__] = value
171
    return cls
172
 
173
 
174
@commandClass
175
class Commandline(object):
176
    """
177
        If you want to create a new commandline function you just need to
178
        create a function with the name of it in this class and decorate
179
        it with the decorator @command. If you don't want to call the desired
180
        function (wrong arguments etc) just raise ArgumentError with or
341 farthen 181
        without an error message.
171 farthen 182
    """
183
    def __init__(self):
184
        self.logger = Logger()
185
        try:
186
            self.embios = libembios.Embios()
187
        except libembios.DeviceNotFoundError:
188
            self.logger.error("No emBIOS device found!")
176 farthen 189
            exit(1)
190
        self.getinfo("version")
171 farthen 191
 
192
    def _parsecommand(self, func, args):
193
        # adds self to the commandline args.
194
        # this is needed because the functions need access to their class.
195
        args.insert(0, self)
196
        if func in self.cmddict:
197
            try:
172 farthen 198
                self.cmddict[func](*args)
341 farthen 199
            except (ArgumentError, libembios.ArgumentError), e:
176 farthen 200
                usage(e, specific=func)
341 farthen 201
            except (ArgumentError, libembios.ArgumentError):
176 farthen 202
                usage("Syntax Error in function '" + func + "'", specific=func)
171 farthen 203
            except ArgumentTypeError, e:
176 farthen 204
                usage(e, specific=func)
171 farthen 205
            except NotImplementedError:
206
                self.logger.error("This function is not implemented yet!")
207
            except libembios.DeviceError, e:
208
                self.logger.error(str(e))
209
            except TypeError, e:
341 farthen 210
                # Only act on TypeErrors for the function we called, not on TypeErrors raised by another function.
171 farthen 211
                if str(e).split(" ", 1)[0] == func + "()":
212
                    self.logger.error(usage("Argument Error in '" + func + "': Wrong argument count", specific=func))
213
                else:
214
                    raise
176 farthen 215
            except libembios.usb.core.USBError:
341 farthen 216
                self.logger.error("There is a problem with the USB connection.")
171 farthen 217
        else:
218
            usage("No such command")
67 benedikt93 219
 
171 farthen 220
    @staticmethod
221
    def _bool(something):
222
        """
223
            Converts quite everything into bool.
224
        """
225
        if type(something) == bool:
226
            return something
227
        elif type(something) == int or type(something) == long:
228
            return bool(something)
229
        elif type(something == str):
230
            truelist = ['true', '1', 't', 'y', 'yes']
231
            falselist = ['false', '0', 'f', 'n', 'no']
232
            if something.lower() in truelist:
233
                return True
234
            elif something.lower() in falselist:
235
                return False
236
        raise ArgumentTypeError("bool", "'"+str(something)+"'")
237
 
238
    @staticmethod
239
    def _hexint(something):
240
        """
241
            Converts quite everything to a hexadecimal represented integer.
242
            This works for default arguments too, because it returns
243
            None when it found that it got a NoneType object.
244
        """
245
        if type(something) == int or type(something) == long:
246
            return something
247
        elif type(something) == str:
248
            try:
249
                return int(something, 16)
250
            except ValueError:
251
                raise ArgumentTypeError("hexadecimal coded integer", "'"+str(something)+"'")
252
        elif type(something) == NoneType:
253
            return None
254
        else:
255
            raise ArgumentTypeError("hexadecimal coded integer", "'"+str(something)+"'")
64 benedikt93 256
 
173 farthen 257
    @staticmethod
258
    def _hex(integer):
259
        return "0x%x" % integer
64 benedikt93 260
 
171 farthen 261
    @command
262
    def getinfo(self, infotype):
263
        """
264
            Get info on the running emBIOS.
265
            <infotype> may be either of 'version', 'packetsize', 'usermemrange'.
266
        """
267
        if infotype == "version":
271 farthen 268
            try:
343 farthen 269
                hwtype = libembiosdata.hwtypes[self.embios.lib.dev.hwtypeid]
271 farthen 270
            except KeyError:
343 farthen 271
                hwtype = "UNKNOWN (ID = " + self._hex(self.embios.lib.dev.hwtypeid) + ")"
272
            self.logger.info("Connected to "+libembiosdata.swtypes[self.embios.lib.dev.swtypeid] + " v" + str(self.embios.lib.dev.version.majorv) + "." + str(self.embios.lib.dev.version.minorv) +
273
                             "." + str(self.embios.lib.dev.version.patchv) + " r" + str(self.embios.lib.dev.version.revision) + " running on " + hwtype + "\n")
274
 
171 farthen 275
        elif infotype == "packetsize":
343 farthen 276
            self.logger.info("Maximum packet sizes: \n command out: " + str(self.embios.lib.dev.packetsizelimit.cout) + "\n command in: " + str(self.embios.lib.dev.packetsizelimit.cin) + "\n data in: " + str(self.embios.lib.dev.packetsizelimit.din) + "\n data out: " + str(self.embios.lib.dev.packetsizelimit.dout))
277
 
171 farthen 278
        elif infotype == "usermemrange":
279
            resp = self.embios.getusermemrange()
343 farthen 280
            self.logger.info("The user memory range is "+self._hex(self.embios.lib.dev.usermem.lower)+" - "+self._hex(self.embios.lib.dev.usermem.upper - 1))
171 farthen 281
        else:
282
            raise ArgumentTypeError("one out of 'version', 'packetsize', 'usermemrange'", infotype)
64 benedikt93 283
 
171 farthen 284
    @command
285
    def reset(self, force=False):
286
        """
287
            Resets the device"
288
            If <force> is 1, the reset will be forced, otherwise it will be gracefully,
289
            which may take some time.
290
        """
291
        force = self._bool(force)
292
        if force: self.logger.info("Resetting forcefully...\n")
293
        else: self.logger.info("Resetting...\n")
294
        self.embios.reset(force)
64 benedikt93 295
 
171 farthen 296
    @command
297
    def poweroff(self, force=False):
298
        """
299
            Powers the device off
300
            If <force> is 1, the poweroff will be forced, otherwise it will be gracefully,
301
            which may take some time.
302
        """
303
        force = self._bool(force)
281 farthen 304
        if force: self.logger.info("Powering off forcefully...\n")
305
        else: self.logger.info("Powering off...\n")
306
        self.embios.poweroff(force)
64 benedikt93 307
 
171 farthen 308
    @command
309
    def uploadfile(self, addr, filename):
310
        """
311
            Uploads a file to the device
312
            <offset>: the address to upload the file to
313
            <filename>: the path to the file
314
        """
315
        addr = self._hexint(addr)
316
        try:
317
            f = open(filename, 'rb')
318
        except IOError:
319
            raise ArgumentError("File not readable. Does it exist?")
173 farthen 320
        self.logger.info("Writing file '"+filename+"' to memory at "+self._hex(addr)+"...")
171 farthen 321
        with f:
322
            self.embios.write(addr, f.read())
178 farthen 323
        f.close()
171 farthen 324
        self.logger.info("done\n")
176 farthen 325
 
171 farthen 326
    @command
327
    def downloadfile(self, addr, size, filename):
328
        """
329
            Uploads a file to the device
330
            <offset>: the address to upload the file to
331
            <size>: the number of bytes to be read
332
            <filename>: the path to the file
333
        """
334
        addr = self._hexint(addr)
335
        size = self._hexint(size)
336
        try:
337
            f = open(filename, 'wb')
338
        except IOError:
339
            raise ArgumentError("Can not open file for write!")
173 farthen 340
        self.logger.info("Reading data from address "+self._hex(addr)+" with the size "+self._hex(size)+" to '"+filename+"'...")
171 farthen 341
        with f:
342
            f.write(self.embios.read(addr, size))
178 farthen 343
        f.close()
171 farthen 344
        self.logger.info("done\n")
345
 
346
    @command
347
    def uploadint(self, addr, integer):
348
        """
349
            Uploads a single integer to the device
260 theseven 350
            <addr>: the address to upload the integer to
351
            <integer>: the integer to upload
171 farthen 352
        """
353
        addr = self._hexint(addr)
354
        integer = self._hexint(integer)
355
        if integer > 0xFFFFFFFF:
356
            raise ArgumentError("Specified integer too long")
260 theseven 357
        data = struct.pack("I", integer)
178 farthen 358
        self.embios.write(addr, data)
236 farthen 359
        self.logger.info("Integer '"+self._hex(integer)+"' written successfully to "+self._hex(addr)+"\n")
171 farthen 360
 
361
    @command
362
    def downloadint(self, addr):
363
        """
364
            Downloads a single integer from the device and prints it to the console window
236 farthen 365
            <addr>: the address to download the integer from
171 farthen 366
        """
367
        addr = self._hexint(addr)
260 theseven 368
        data = self.embios.read(addr, 4)
369
        integer = struct.unpack("I", data)[0]
236 farthen 370
        self.logger.info("Integer '"+self._hex(integer)+"' read from address "+self._hex(addr)+"\n")
171 farthen 371
 
372
    @command
176 farthen 373
    def i2cread(self, bus, slave, addr, size):
171 farthen 374
        """
375
            Reads data from an I2C device
376
            <bus> the bus index
377
            <slave> the slave address
378
            <addr> the start address on the I2C device
379
            <size> the number of bytes to read
380
        """
381
        bus = self._hexint(bus)
382
        slave = self._hexint(slave)
383
        addr = self._hexint(addr)
384
        size = self._hexint(size)
236 farthen 385
        data = self.embios.i2cread(bus, slave, addr, size)
386
        bytes = struct.unpack("%dB" % len(data), data)
387
        self.logger.info("Data read from I2C:\n")
388
        for index, byte in enumerate(bytes):
389
            self.logger.info("%02X: %02X\n" % (index, byte))
171 farthen 390
 
391
    @command
176 farthen 392
    def i2cwrite(self, bus, slave, addr, *args):
171 farthen 393
        """
394
            Writes data to an I2C device
395
            <bus> the bus index
396
            <slave> the slave address
397
            <addr> the start address on the I2C device
176 farthen 398
            <db1> ... <dbN> the data in single bytes, encoded in hex,
236 farthen 399
                seperated by whitespaces, eg. 37 5A 4F EB
171 farthen 400
        """
401
        bus = self._hexint(bus)
402
        slave = self._hexint(slave)
403
        addr = self._hexint(addr)
176 farthen 404
        data = ""
171 farthen 405
        for arg in args:
176 farthen 406
            data += chr(self._hexint(arg))
236 farthen 407
        self.logger.info("Writing data to I2C...\n")
176 farthen 408
        self.embios.i2cwrite(bus, slave, addr, data)
236 farthen 409
        self.logger.info("done\n")
171 farthen 410
 
411
    @command
176 farthen 412
    def console(self):
171 farthen 413
        """
176 farthen 414
            Reads data from the USB console continuously
171 farthen 415
        """
176 farthen 416
        while True:
417
            resp = self.embios.usbcread()
418
            self.logger.log(resp.data)
419
            time.sleep(0.1 / resp.maxsize * (resp.maxsize - len(resp.data)))
171 farthen 420
 
421
    @command
176 farthen 422
    def writeusbconsole(self, *args):
171 farthen 423
        """
176 farthen 424
            Writes the string <db1> ... <dbN> to the USB console.
171 farthen 425
        """
176 farthen 426
        text = ""
427
        for word in args:
428
            text += word + " "
429
        text = text[:-1]
236 farthen 430
        self.logger.info("Writing '"+ text +"' to the usb console\n")
176 farthen 431
        self.embios.usbcwrite(text)
171 farthen 432
 
433
    @command
176 farthen 434
    def readdevconsole(self, bitmask):
171 farthen 435
        """
176 farthen 436
            Reads data continuously from one or more of the device's consoles.
437
            <bitmask>: the bitmask of the consoles to read from.
171 farthen 438
        """
439
        bitmask = self._hexint(bitmask)
176 farthen 440
        while True:
441
            resp = self.embios.cread()
442
            self.logger.log(resp.data)
443
            time.sleep(0.1 / resp.maxsize * (resp.maxsize - len(resp.data)))
444
 
171 farthen 445
    @command
176 farthen 446
    def writedevconsole(self, bitmask, *args):
171 farthen 447
        """
176 farthen 448
            Writes the string <db1> ... <dbN> to one or more of the device's consoles.
449
            <bitmask>: the bitmask of the consoles to write to
171 farthen 450
        """
451
        bitmask = self._hexint(bitmask)
176 farthen 452
        text = ""
453
        for word in args:
454
            text += word + " "
455
        text = text[:-1]
456
        self.logger.info("Writing '"+text+"' to the device consoles identified with "+self._hex(bitmask)+"\n")
457
        self.embios.cwrite(text, bitmask)
171 farthen 458
 
459
    @command
460
    def flushconsolebuffers(self, bitmask):
461
        """
462
            flushes one or more of the device consoles' buffers.
463
            <bitmask>: the bitmask of the consoles to be flushed
464
        """
465
        bitmask = self._hexint(bitmask)
236 farthen 466
        self.logger.info("Flushing consoles identified with the bitmask "+self._hex(bitmask)+"\n")
467
        self.embios.cflush(bitmask)
171 farthen 468
 
469
    @command
470
    def getprocinfo(self):
471
        """
472
            Fetches data on the currently running processes
473
        """
173 farthen 474
        import datetime
475
        threads = self.embios.getprocinfo()
476
        self.logger.info("The device has "+str(len(threads))+" running threads:\n\n")
477
        for thread in threads:
478
            self.logger.info("  "+thread.name+":\n")
479
            self.logger.info("    Thread id: "+str(thread.id)+"\n")
480
            self.logger.info("    Thread type: "+thread.type+"\n")
481
            self.logger.info("    Thread state: "+thread.state+"\n")
346 theseven 482
            self.logger.info("    Block type: "+thread.block_type+"\n")
483
            self.logger.info("    Blocked by: "+self._hex(thread.blocked_by_ptr)+"\n")
353 theseven 484
            self.logger.info("    Priority: "+str(thread.priority)+"/255\n")
324 theseven 485
            self.logger.info("    Current CPU load: "+str((thread.cpuload*100)/255)+"%\n")
173 farthen 486
            self.logger.info("    CPU time (total): "+str(datetime.timedelta(microseconds=thread.cputime_total))+"\n")
487
            self.logger.info("    Stack address: "+self._hex(thread.stackaddr)+"\n")
488
            self.logger.info("    Registers:\n")
177 farthen 489
            for registerrange in range(4):
490
                self.logger.info("      ")
491
                for register in range(registerrange, 16, 4):
492
                    registerrepr = "r"+str(register)
318 theseven 493
                    self.logger.info("{0:3s}: 0x{1:08X}   ".format(registerrepr, thread.regs["r"+str(register)]))
177 farthen 494
                self.logger.info("\n")
318 theseven 495
            self.logger.info("     cpsr: 0x{0:08X}".format(thread.regs.cpsr))
173 farthen 496
            self.logger.info("\n")
497
 
171 farthen 498
    @command
499
    def lockscheduler(self):
500
        """
501
            Locks (freezes) the scheduler
502
        """
176 farthen 503
        self.logger.info("Will now lock scheduler\n")
173 farthen 504
        self.embios.lockscheduler()
178 farthen 505
 
171 farthen 506
    @command
507
    def unlockscheduler(self):
508
        """
509
            Unlocks (unfreezes) the scheduler
510
        """
176 farthen 511
        self.logger.info("Will now unlock scheduler\n")
173 farthen 512
        self.embios.unlockscheduler()
178 farthen 513
 
171 farthen 514
    @command
515
    def suspendthread(self, threadid):
516
        """
517
            Suspends/resumes the thread with thread ID <threadid>
518
        """
519
        threadid = self._hexint(threadid)
236 farthen 520
        self.logger.info("Suspending the thread with the threadid "+self._hex(threadid)+"\n")
521
        self.embios.suspendthread(threadid)
171 farthen 522
 
523
    @command
524
    def resumethread(self, threadid):
525
        """
526
            Resumes the thread with thread ID <threadid>
527
        """
528
        threadid = self._hexint(threadid)
236 farthen 529
        self.logger.info("Resuming the thread with the threadid "+self._hex(threadid)+"\n")
173 farthen 530
        self.embios.resumethread(threadid)
171 farthen 531
 
532
    @command
533
    def killthread(self, threadid):
534
        """
535
            Kills the thread with thread ID <threadid>
536
        """
537
        threadid = self._hexint(threadid)
236 farthen 538
        self.logger.info("Killing the thread with the threadid " + self._hex(threadid) + "\n")
173 farthen 539
        self.embios.killthread(threadid)
171 farthen 540
 
541
    @command
542
    def createthread(self, nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state):
543
        """
544
            Creates a new thread and returns its thread ID
545
            <namepointer> a pointer to the thread's name
546
            <entrypoint> a pointer to the entrypoint of the thread
547
            <stackpointer> a pointer to the stack of the thread
548
            <stacksize> the size of the thread's stack
549
            <type> the thread type, vaild are: 0 => user thread, 1 => system thread
550
            <priority> the priority of the thread, from 1 to 255
551
            <state> the thread's initial state, valid are: 1 => ready, 0 => suspended
552
        """
553
        nameptr = self._hexint(nameptr)
554
        entrypoint = self._hexint(entrypoint)
555
        stackpointer = self._hexint(stackpointer)
556
        stacksize = self._hexint(stacksize)
557
        priority = self._hexint(priority)
236 farthen 558
        data = self.embios.createthread(nameptr, entrypoint, stackptr, stacksize, type, priority, state)
559
        name = self.embios.readstring(nameptr)
560
        self.logger.info("Created a thread with the threadid " + data.id + ", the name \"" + name + "\", the entrypoint at " + self._hex(entrypoint) + ", the stack at " + self._hex(stackpointer) + " with a size of " + self._hex(stacksize) + " and a priority of " + self._hex(priority) + "\n")
172 farthen 561
 
562
    @command
563
    def run(self, filename):
564
        """
173 farthen 565
            Uploads the emBIOS application <filename> to
236 farthen 566
            the memory and executes it
172 farthen 567
        """
236 farthen 568
        try:
569
            f = open(filename, 'rb')
570
        except IOError:
571
            raise ArgumentError("File not readable. Does it exist?")
238 farthen 572
        with f:
573
            data = self.embios.run(f.read())
574
        self.logger.info("Executed emBIOS application \"" + data.name + "\" at address " + self._hex(data.baseaddr))
171 farthen 575
 
576
    @command
173 farthen 577
    def execimage(self, addr):
171 farthen 578
        """
173 farthen 579
            Executes the emBIOS application at <addr>.
171 farthen 580
        """
172 farthen 581
        addr = self._hexint(addr)
173 farthen 582
        self.logger.info("Starting emBIOS app at "+self._hex(addr)+"\n")
172 farthen 583
        self.embios.execimage(addr)
176 farthen 584
 
171 farthen 585
    @command
176 farthen 586
    def flushcaches(self):
171 farthen 587
        """
176 farthen 588
            Flushes the CPUs data and instruction caches.
589
        """
590
        self.logger.info("Flushing CPU data and instruction caches...")
591
        self.embios.flushcaches()
592
        self.logger.info("done\n")
593
 
594
    @command
595
    def readbootflash(self, addr_flash, addr_mem, size):
596
        """
171 farthen 597
            Reads <size> bytes from bootflash to memory.
598
            <addr_bootflsh>: the address in bootflash to read from
599
            <addr_mem>: the address in memory to copy the data to
600
        """
601
        addr_flash = self._hexint(addr_flash)
602
        addr_mem = self._hexint(addr_mem)
603
        size = self._hexint(size)
174 farthen 604
        self.logger.info("Dumping boot flash addresses "+self._hex(addr_flash)+" - "+
605
                         hex(addr_flash+size)+" to "+self._hex(addr_mem)+" - "+self._hex(addr_mem+size)+"\n")
276 theseven 606
        self.embios.lib.dev.timeout = 5000
607
        self.embios.bootflashread(addr_mem, addr_flash, size)
176 farthen 608
 
171 farthen 609
    @command
176 farthen 610
    def writebootflash(self, addr_flash, addr_mem, size, force=False):
171 farthen 611
        """
612
            Writes <size> bytes from memory to bootflash.
613
            ATTENTION: Don't call this unless you really know what you're doing!
614
            This may BRICK your device (unless it has a good recovery option)
615
            <addr_mem>: the address in memory to copy the data from
616
            <addr_bootflsh>: the address in bootflash to write to
174 farthen 617
            <force>: Use this flag to suppress the 5 seconds delay
171 farthen 618
        """
619
        addr_flash = self._hexint(addr_flash)
620
        addr_mem = self._hexint(addr_mem)
621
        size = self._hexint(size)
174 farthen 622
        force = self._bool(force)
623
        self.logger.info("Writing boot flash from the memory in "+self._hex(addr_mem)+" - "+
624
                         hex(addr_mem+size)+" to "+self._hex(addr_flash)+" - "+self._hex(addr_flash+size)+"\n")
625
        if force == False:
626
            self.logger.info("If this was not what you intended press Ctrl-C NOW")
176 farthen 627
            for i in range(10):
174 farthen 628
                self.logger.info(".")
629
                time.sleep(1)
630
            self.logger.info("\n")
276 theseven 631
        self.embios.lib.dev.timeout = 30000
632
        self.embios.bootflashwrite(addr_mem, addr_flash, size)
176 farthen 633
 
171 farthen 634
    @command
176 farthen 635
    def runfirmware(self, addr, filename):
171 farthen 636
        """
176 farthen 637
            Uploads the firmware in 'filename' to the beginning of the
638
            user memory and executes it
171 farthen 639
        """
176 farthen 640
        addr = self._hexint(addr)
641
        self.uploadfile(addr, filename)
642
        self.execfirmware(addr)
64 benedikt93 643
 
171 farthen 644
    @command
176 farthen 645
    def execfirmware(self, addr):
646
        """
647
            Executes the firmware at addr
648
        """
649
        addr = self._hexint(addr)
650
        self.logger.info("Running firmware at "+self._hex(addr)+". Bye.")
651
        self.embios.execfirmware(addr)
652
 
653
    @command
171 farthen 654
    def aesencrypt(self, addr, size, keyindex):
655
        """
172 farthen 656
            Encrypts a buffer using a hardware key
171 farthen 657
        """
658
        addr = self._hexint(addr)
659
        size = self._hexint(size)
660
        keyindex = self._hexint(keyindex)
661
        self.embios.aesencrypt(addr, size, keyindex)
82 benedikt93 662
 
171 farthen 663
    @command
664
    def aesdecrypt(self, addr, size, keyindex):
665
        """
172 farthen 666
            Decrypts a buffer using a hardware key
171 farthen 667
        """
668
        addr = self._hexint(addr)
669
        size = self._hexint(size)
670
        keyindex = self._hexint(keyindex)
671
        self.embios.aesdecrypt(addr, size, keyindex)
172 farthen 672
 
673
    @command
674
    def hmac_sha1(self, addr, size, destination):
675
        """
676
            Generates a HMAC-SHA1 hash of the buffer and saves it to 'destination'
677
        """
678
        addr = self._hexint(addr)
679
        size = self._hexint(size)
680
        destination = self._hexint(destination)
681
        sha1size = 0x14
173 farthen 682
        self.logger.info("Generating hmac-sha1 hash from the buffer at "+self._hex(addr)+" with the size "+self._hex(size)+
683
                         " and saving it to "+self._hex(destination)+" - "+self._hex(destination+sha1size)+"...")
172 farthen 684
        self.embios.hmac_sha1(addr, size, destination)
685
        self.logger.info("done\n")
178 farthen 686
        data = self.embios.read(destination, sha1size)
172 farthen 687
        hash = ord(data)
173 farthen 688
        self.logger.info("The generated hash is "+self._hex(hash))
64 benedikt93 689
 
227 theseven 690
    @command
691
    def ipodnano2g_getnandinfo(self):
692
        """
693
            Target-specific function: ipodnano2g
694
            Gathers some information about the NAND chip used
695
        """
696
        data = self.embios.ipodnano2g_getnandinfo()
697
        self.logger.info("NAND chip type: "+self._hex(data["type"])+"\n")
698
        self.logger.info("Number of banks: "+str(data["banks"])+"\n")
699
        self.logger.info("Number of blocks: "+str(data["blocks"])+"\n")
700
        self.logger.info("Number of user blocks: "+str(data["userblocks"])+"\n")
701
        self.logger.info("Pages per block: "+str(data["pagesperblock"]))
702
 
703
    @command
704
    def ipodnano2g_nandread(self, addr, start, count, doecc, checkempty):
705
        """
706
            Target-specific function: ipodnano2g
707
            Reads data from the NAND chip into memory
708
        """
709
        addr = self._hexint(addr)
710
        start = self._hexint(start)
711
        count = self._hexint(count)
712
        doecc = int(doecc)
713
        checkempty = int(checkempty)
714
        self.logger.info("Reading "+self._hex(count)+" NAND pages starting at "+self._hex(start)+" to "+self._hex(addr)+"...")
228 theseven 715
        self.embios.lib.dev.timeout = 30000
227 theseven 716
        self.embios.ipodnano2g_nandread(addr, start, count, doecc, checkempty)
717
        self.logger.info("done\n")
718
 
719
    @command
720
    def ipodnano2g_nandwrite(self, addr, start, count, doecc):
721
        """
722
            Target-specific function: ipodnano2g
723
            Writes data to the NAND chip
724
        """
725
        addr = self._hexint(addr)
726
        start = self._hexint(start)
727
        count = self._hexint(count)
728
        doecc = int(doecc)
729
        self.logger.info("Writing "+self._hex(count)+" NAND pages starting at "+self._hex(start)+" from "+self._hex(addr)+"...")
228 theseven 730
        self.embios.lib.dev.timeout = 30000
227 theseven 731
        self.embios.ipodnano2g_nandwrite(addr, start, count, doecc)
732
        self.logger.info("done\n")
733
 
734
    @command
735
    def ipodnano2g_nanderase(self, addr, start, count):
736
        """
737
            Target-specific function: ipodnano2g
738
            Erases blocks on the NAND chip and stores the results to memory
739
        """
740
        addr = self._hexint(addr)
741
        start = self._hexint(start)
742
        count = self._hexint(count)
743
        self.logger.info("Erasing "+self._hex(count)+" NAND blocks starting at "+self._hex(start)+" and logging to "+self._hex(addr)+"...")
228 theseven 744
        self.embios.lib.dev.timeout = 30000
227 theseven 745
        self.embios.ipodnano2g_nanderase(addr, start, count)
746
        self.logger.info("done\n")
747
 
228 theseven 748
    @command
749
    def ipodnano2g_dumpnand(self, filenameprefix):
750
        """
751
            Target-specific function: ipodnano2g
752
            Dumps the whole NAND chip to four files
753
        """
754
        info = self.embios.ipodnano2g_getnandinfo()
755
        self.logger.info("Dumping NAND contents...")
756
        try:
757
            infofile = open(filenameprefix+"_info.txt", 'wb')
758
            datafile = open(filenameprefix+"_data.bin", 'wb')
759
            sparefile = open(filenameprefix+"_spare.bin", 'wb')
760
            statusfile = open(filenameprefix+"_status.bin", 'wb')
761
        except IOError:
762
            raise ArgumentError("Can not open file for writing!")
763
        infofile.write("NAND chip type: "+self._hex(info["type"])+"\r\n")
764
        infofile.write("Number of banks: "+str(info["banks"])+"\r\n")
765
        infofile.write("Number of blocks: "+str(info["blocks"])+"\r\n")
766
        infofile.write("Number of user blocks: "+str(info["userblocks"])+"\r\n")
767
        infofile.write("Pages per block: "+str(info["pagesperblock"])+"\r\n")
768
        self.embios.lib.dev.timeout = 30000
769
        for i in range(info["banks"] * info["blocks"] * info["pagesperblock"] / 8192):
770
            self.logger.info(".")
771
            self.embios.ipodnano2g_nandread(0x08000000, i * 8192, 8192, 1, 1)
772
            datafile.write(self.embios.read(0x08000000, 0x01000000))
773
            sparefile.write(self.embios.read(0x09000000, 0x00080000))
774
            statusfile.write(self.embios.read(0x09080000, 0x00008000))
775
        infofile.close()
776
        datafile.close()
777
        sparefile.close()
778
        statusfile.close()
779
        self.logger.info("done\n")
780
 
781
    @command
782
    def ipodnano2g_wipenand(self, filename, force=False):
783
        """
784
            Target-specific function: ipodnano2g
785
            Wipes the whole NAND chip and logs the result to a file
786
            <force>: Use this flag to suppress the 5 seconds delay
787
        """
788
        self.logger.info("Wiping the whole NAND chip!\n")
789
        if force == False:
790
            self.logger.info("If this was not what you intended press Ctrl-C NOW")
791
            for i in range(10):
792
                self.logger.info(".")
793
                time.sleep(1)
794
            self.logger.info("\n")
795
        info = self.embios.ipodnano2g_getnandinfo()
796
        self.logger.info("Wiping NAND contents...")
797
        try:
798
            statusfile = open(filename, 'wb')
799
        except IOError:
800
            raise ArgumentError("Can not open file for writing!")
801
        self.embios.lib.dev.timeout = 30000
802
        for i in range(info["banks"] * info["blocks"] / 64):
803
            self.logger.info(".")
230 theseven 804
            self.embios.ipodnano2g_nanderase(0x08000000, i * 64, 64)
228 theseven 805
            statusfile.write(self.embios.read(0x08000000, 0x00000100))
806
        statusfile.close()
807
        self.logger.info("done\n")
808
 
346 theseven 809
    @command
810
    def ipodclassic_writebbt(self, tempaddr, filename):
811
        """
812
            Target-specific function: ipodclassic
813
            Uploads the bad block table <filename> to
814
            memory at <tempaddr> and writes it to the hard disk
815
        """
816
        tempaddr = self._hexint(tempaddr)
817
        try:
818
            f = open(filename, 'rb')
819
        except IOError:
820
            raise ArgumentError("File not readable. Does it exist?")
821
        self.embios.lib.dev.timeout = 30000
822
        self.logger.info("Writing bad block table to disk...")
823
        data = self.embios.ipodclassic_writebbt(f.read(), tempaddr)
824
        f.close()
825
        self.logger.info(" done\n")
826
 
827
    @command
379 theseven 828
    def getvolumeinfo(self, volume):
829
        """
830
            Gathers some information about a storage volume used
831
        """
832
        volume = self._hexint(volume)
833
        data = self.embios.storage_get_info(volume)
834
        self.logger.info("Sector size: "+str(data["sectorsize"])+"\n")
835
        self.logger.info("Number of sectors: "+str(data["numsectors"])+"\n")
836
        self.logger.info("Vendor: "+data["vendor"]+"\n")
837
        self.logger.info("Product: "+data["product"]+"\n")
838
        self.logger.info("Revision: "+data["revision"])
839
 
840
    @command
841
    def readrawstorage(self, volume, sector, count, addr):
842
        """
843
            Reads <count> sectors starting at <sector> from storage <volume> to memory at <addr>.
844
        """
845
        volume = self._hexint(volume)
846
        sector = self._hexint(sector)
847
        count = self._hexint(count)
848
        addr = self._hexint(addr)
849
        self.logger.info("Reading volume %s sectors %X - %X to %08X..." % (volume, sector, sector + count - 1, addr))
850
        self.embios.lib.dev.timeout = 50000
851
        self.embios.storage_read_sectors_md(volume, sector, count, addr)
852
        self.logger.info("done\n")
853
 
854
    @command
855
    def writerawstorage(self, volume, sector, count, addr):
856
        """
857
            Writes memory contents at <addr> to <count> sectors starting at <sector> on storage <volume>.
858
        """
859
        volume = self._hexint(volume)
860
        sector = self._hexint(sector)
861
        count = self._hexint(count)
862
        addr = self._hexint(addr)
863
        self.logger.info("Writing %08X to volume %s sectors %X - %X..." % (addr, volume, sector, sector + count - 1))
864
        self.embios.lib.dev.timeout = 50000
865
        self.embios.storage_write_sectors_md(volume, sector, count, addr)
866
        self.logger.info("done\n")
867
 
868
    @command
869
    def readrawstoragefile(self, volume, sector, count, file, buffer = False, buffsize = "100000"):
870
        """
871
            Reads <count> sectors starting at <sector> from storage <volume> to file <file>,
872
            buffering them in memory at <buffer> in chunks of <buffsize> bytes (both optional).
873
        """
874
        volume = self._hexint(volume)
875
        sector = self._hexint(sector)
876
        count = self._hexint(count)
877
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
878
        else: buffer = self._hexint(buffer)
879
        buffsize = self._hexint(buffsize)
880
        try:
881
            f = open(file, 'wb')
882
        except IOError:
883
            raise ArgumentError("Could not open local file for writing.")
884
        self.logger.info("Reading volume %s sectors %X - %X to %s..." % (volume, sector, sector + count - 1, file))
885
        self.embios.lib.dev.timeout = 50000
886
        storageinfo = self.embios.storage_get_info(volume)
887
        while count > 0:
888
            sectors = min(count, int(buffsize / storageinfo.sectorsize))
889
            self.embios.storage_read_sectors_md(volume, sector, sectors, buffer)
890
            f.write(self.embios.read(buffer, storageinfo.sectorsize * sectors))
891
            sector = sector + sectors
892
            count = count - sectors
893
        f.close()
894
        self.logger.info("done\n")
895
 
896
    @command
897
    def writerawstoragefile(self, volume, sector, count, file, buffer = False, buffsize = "100000"):
898
        """
899
            Writes contents of <file> to <count> sectors starting at <sector> on storage <volume>,
900
            buffering them in memory at <buffer> in chunks of <buffsize> bytes (both optional).
901
        """
902
        volume = self._hexint(volume)
903
        sector = self._hexint(sector)
904
        count = self._hexint(count)
905
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
906
        else: buffer = self._hexint(buffer)
907
        buffsize = self._hexint(buffsize)
908
        try:
909
            f = open(file, 'rb')
910
        except IOError:
911
            raise ArgumentError("Could not open local file for reading.")
912
        self.logger.info("Writing %s to volume %s sectors %X - %X..." % (file, volume, sector, sector + count - 1))
913
        self.embios.lib.dev.timeout = 50000
914
        storageinfo = self.embios.storage_get_info(volume)
915
        while count > 0:
916
            sectors = min(count, int(buffsize / storageinfo.sectorsize))
917
            bytes = storageinfo.sectorsize * sectors
918
            data = f.read(bytes)
919
            if len(data) == 0: break
920
            while len(data) < bytes: data = data + f.read(bytes - len(data))
921
            self.embios.write(buffer, data)
922
            self.embios.storage_write_sectors_md(volume, sector, sectors, buffer)
923
            sector = sector + sectors
924
            count = count - sectors
925
        f.close()
926
        self.logger.info("done\n")
927
 
928
    @command
346 theseven 929
    def mkdir(self, dirname):
930
        """
931
            Creates a directory
932
        """
933
        self.embios.lib.dev.timeout = 30000
934
        self.logger.info("Creating directory " + dirname + "...")
935
        self.embios.dir_create(dirname)
936
        self.logger.info(" done\n")
937
 
938
    @command
939
    def rmdir(self, dirname):
940
        """
941
            Removes an empty directory
942
        """
943
        self.embios.lib.dev.timeout = 30000
944
        self.logger.info("Removing directory " + dirname + "...")
945
        self.embios.dir_remove(dirname)
946
        self.logger.info(" done\n")
947
 
948
    @command
349 theseven 949
    def rm(self, filename):
346 theseven 950
        """
951
            Removes a file
952
        """
953
        self.embios.lib.dev.timeout = 30000
954
        self.logger.info("Removing file " + filename + "...")
955
        self.embios.file_unlink(filename)
956
        self.logger.info(" done\n")
957
 
958
    @command
352 theseven 959
    def mv(self, oldname, newname):
350 theseven 960
        """
352 theseven 961
            Renames or moves a file or directory
350 theseven 962
        """
963
        self.embios.lib.dev.timeout = 30000
964
        self.logger.info("Renaming " + oldname + " to " + newname + "...")
965
        self.embios.file_rename(oldname, newname)
966
        self.logger.info(" done\n")
967
 
968
    @command
351 theseven 969
    def get(self, remotename, localname, buffer = False, buffsize = "10000"):
346 theseven 970
        """
971
            Downloads a file
972
        """
351 theseven 973
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
974
        else: buffer = self._hexint(buffer)
346 theseven 975
        buffsize = self._hexint(buffsize)
976
        try:
977
            f = open(localname, 'wb')
978
        except IOError:
979
            raise ArgumentError("Could not open local file for writing.")
980
        self.embios.lib.dev.timeout = 30000
981
        self.logger.info("Downloading file " + remotename + " to " + localname + "...")
982
        fd = self.embios.file_open(remotename, 0)
983
        size = self.embios.file_size(fd)
984
        while size > 0:
370 theseven 985
            bytes = self.embios.file_read(fd, buffer, buffsize)
346 theseven 986
            f.write(self.embios.read(buffer, bytes))
987
            size = size - bytes
988
        self.embios.file_close(fd)
989
        f.close()
990
        self.logger.info(" done\n")
991
 
992
    @command
351 theseven 993
    def put(self, localname, remotename, buffer = False, buffsize = "10000"):
346 theseven 994
        """
995
            Uploads a file
996
        """
351 theseven 997
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
998
        else: buffer = self._hexint(buffer)
346 theseven 999
        buffsize = self._hexint(buffsize)
1000
        try:
1001
            f = open(localname, 'rb')
1002
        except IOError:
1003
            raise ArgumentError("Could not open local file for reading.")
1004
        self.embios.lib.dev.timeout = 30000
1005
        self.logger.info("Uploading file " + localname + " to " + remotename + "...")
1006
        fd = self.embios.file_open(remotename, 0x15)
1007
        while True:
1008
            data = f.read(buffsize)
1009
            if len(data) == 0: break
1010
            self.embios.write(buffer, data)
1011
            bytes = 0
1012
            while bytes < len(data):
1013
                bytes = bytes + self.embios.file_write(fd, buffer + bytes, len(data) - bytes)
1014
        self.embios.file_close(fd)
1015
        f.close()
1016
        self.logger.info(" done\n")
1017
 
1018
    @command
351 theseven 1019
    def ls(self, path = "/"):
346 theseven 1020
        """
1021
            Lists all files in the specified path
1022
        """
1023
        self.embios.lib.dev.timeout = 30000
1024
        handle = self.embios.dir_open(path)
1025
        self.logger.info("Directory listing of " + path + ":\n")
1026
        while True:
1027
            try:
1028
                entry = self.embios.dir_read(handle)
1029
                if entry.attributes & 0x10: size = "DIR"
1030
                else: size = locale.format("%d", entry.size, True).rjust(13)
1031
                self.logger.info(entry.name.ljust(50) + " - " + size + "\n")
1032
            except: break
1033
        self.embios.dir_close(handle)
1034
 
171 farthen 1035
if __name__ == "__main__":
1036
    if len(sys.argv) < 2:
1037
        usage("No command specified")
1038
    interface = Commandline()
1039
    interface._parsecommand(sys.argv[1], sys.argv[2:])