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
828
    def mkdir(self, dirname):
829
        """
830
            Creates a directory
831
        """
832
        self.embios.lib.dev.timeout = 30000
833
        self.logger.info("Creating directory " + dirname + "...")
834
        self.embios.dir_create(dirname)
835
        self.logger.info(" done\n")
836
 
837
    @command
838
    def rmdir(self, dirname):
839
        """
840
            Removes an empty directory
841
        """
842
        self.embios.lib.dev.timeout = 30000
843
        self.logger.info("Removing directory " + dirname + "...")
844
        self.embios.dir_remove(dirname)
845
        self.logger.info(" done\n")
846
 
847
    @command
349 theseven 848
    def rm(self, filename):
346 theseven 849
        """
850
            Removes a file
851
        """
852
        self.embios.lib.dev.timeout = 30000
853
        self.logger.info("Removing file " + filename + "...")
854
        self.embios.file_unlink(filename)
855
        self.logger.info(" done\n")
856
 
857
    @command
352 theseven 858
    def mv(self, oldname, newname):
350 theseven 859
        """
352 theseven 860
            Renames or moves a file or directory
350 theseven 861
        """
862
        self.embios.lib.dev.timeout = 30000
863
        self.logger.info("Renaming " + oldname + " to " + newname + "...")
864
        self.embios.file_rename(oldname, newname)
865
        self.logger.info(" done\n")
866
 
867
    @command
351 theseven 868
    def get(self, remotename, localname, buffer = False, buffsize = "10000"):
346 theseven 869
        """
870
            Downloads a file
871
        """
351 theseven 872
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
873
        else: buffer = self._hexint(buffer)
346 theseven 874
        buffsize = self._hexint(buffsize)
875
        try:
876
            f = open(localname, 'wb')
877
        except IOError:
878
            raise ArgumentError("Could not open local file for writing.")
879
        self.embios.lib.dev.timeout = 30000
880
        self.logger.info("Downloading file " + remotename + " to " + localname + "...")
881
        fd = self.embios.file_open(remotename, 0)
882
        size = self.embios.file_size(fd)
883
        while size > 0:
351 theseven 884
            bytes = self.embios.file_read(fd, buffer, buffsize = 0x10000)
346 theseven 885
            f.write(self.embios.read(buffer, bytes))
886
            size = size - bytes
887
        self.embios.file_close(fd)
888
        f.close()
889
        self.logger.info(" done\n")
890
 
891
    @command
351 theseven 892
    def put(self, localname, remotename, buffer = False, buffsize = "10000"):
346 theseven 893
        """
894
            Uploads a file
895
        """
351 theseven 896
        if buffer == False: buffer = self.embios.lib.dev.usermem.lower
897
        else: buffer = self._hexint(buffer)
346 theseven 898
        buffsize = self._hexint(buffsize)
899
        try:
900
            f = open(localname, 'rb')
901
        except IOError:
902
            raise ArgumentError("Could not open local file for reading.")
903
        self.embios.lib.dev.timeout = 30000
904
        self.logger.info("Uploading file " + localname + " to " + remotename + "...")
905
        fd = self.embios.file_open(remotename, 0x15)
906
        while True:
907
            data = f.read(buffsize)
908
            if len(data) == 0: break
909
            self.embios.write(buffer, data)
910
            bytes = 0
911
            while bytes < len(data):
912
                bytes = bytes + self.embios.file_write(fd, buffer + bytes, len(data) - bytes)
913
        self.embios.file_close(fd)
914
        f.close()
915
        self.logger.info(" done\n")
916
 
917
    @command
351 theseven 918
    def ls(self, path = "/"):
346 theseven 919
        """
920
            Lists all files in the specified path
921
        """
922
        self.embios.lib.dev.timeout = 30000
923
        handle = self.embios.dir_open(path)
924
        self.logger.info("Directory listing of " + path + ":\n")
925
        while True:
926
            try:
927
                entry = self.embios.dir_read(handle)
928
                if entry.attributes & 0x10: size = "DIR"
929
                else: size = locale.format("%d", entry.size, True).rjust(13)
930
                self.logger.info(entry.name.ljust(50) + " - " + size + "\n")
931
            except: break
932
        self.embios.dir_close(handle)
933
 
171 farthen 934
if __name__ == "__main__":
935
    if len(sys.argv) < 2:
936
        usage("No command specified")
937
    interface = Commandline()
938
    interface._parsecommand(sys.argv[1], sys.argv[2:])