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
64 benedikt93 28
 
29
import libembios
171 farthen 30
from libembios import Error
31
import libembiosdata
64 benedikt93 32
 
171 farthen 33
class NotImplementedError(Error):
34
    pass
64 benedikt93 35
 
171 farthen 36
class ArgumentError(Error):
37
    pass
64 benedikt93 38
 
171 farthen 39
class ArgumentTypeError(Error):
40
    def __init__(self, expected, seen=False):
41
        self.expected = expected
42
        self.seen = seen
43
    def __str__(self):
44
        if self.seen:
45
            return "Expected " + str(self.expected) + " but saw " + str(self.seen)
46
        else:
47
            return "Expected " + str(self.expected) + ", but saw something else"
64 benedikt93 48
 
49
 
171 farthen 50
def usage(errormsg=None, specific=False):
51
    """
52
        Prints the usage information.
53
        It is auto generated from various places.
54
    """
55
    logger = Logger()
56
    cmddict= Commandline.cmddict
57
    doc = {}
58
    # This sorts the output of various internal functions
59
    # and puts everything in easy readable form
60
    for function in cmddict:
61
        function = cmddict[function].func
62
        docinfo = {}
63
        name = function.__name__
64
        args = inspect.getargspec(function)[0]
65
        docinfo['varargs'] = False
66
        if inspect.getargspec(function)[1]:
67
            docinfo['varargs'] = True
68
        kwargvalues = inspect.getargspec(function)[3]
69
        kwargs = {}
70
        if args:
71
            if kwargvalues:
72
                argnum = len(args) - len(kwargvalues)
73
                kwargnum = len(kwargvalues)
74
                kwargs = dict(zip(args[argnum:], kwargvalues))
75
            else:
76
                argnum = len(args)
77
        else:
78
            argnum = 0
79
        docinfo['args'] = args[1:argnum]
80
        docinfo['kwargs'] = kwargs
81
        if function.__doc__:
82
            # strip unneccessary whitespace
83
            docinfo['documentation'] = re.sub(r'\n        ', '\n', function.__doc__)
84
        else:
85
            docinfo['documentation'] = None
86
        doc[name] = docinfo
64 benedikt93 87
 
171 farthen 88
    if not specific:
89
        logger.log("Please provide a command and (if needed) parameters as command line arguments\n\n")
90
        logger.log("Available commands:\n\n")
82 benedikt93 91
    else:
171 farthen 92
        logger.log("\n")
93
    for function in sorted(doc.items()):
94
        function = function[0]
95
        if specific == False or specific == function:
96
            logger.log("  " + function + " ")
97
            for arg in doc[function]['args']:
98
                logger.log("<" + arg + "> ")
99
            if doc[function]['kwargs']:
100
                for kwarg in doc[function]['kwargs']:
101
                    logger.log("[" + kwarg + "] ")
102
            if doc[function]['varargs']:
103
                logger.log("<db1> ... <dbN>")
104
            if doc[function]['documentation']:
105
                logger.log(doc[function]['documentation']+"\n")
64 benedikt93 106
 
171 farthen 107
    logger.log("\n")
108
 
109
    if errormsg:
110
        logger.error(str(errormsg)+"\n")
111
    exit(2)
112
 
113
 
114
class Logger(object):
115
    """
116
        Simple stdout logger.
117
        Loglevel 4 is most verbose, Loglevel 0 only say something if there is an error.
118
    """
119
    def __init__(self):
120
        # Possible values: 0 (only errors), 1 (warnings), 2 (info, recommended for production use), 3 and more (debug)
121
        self.loglevel = 3
122
 
123
    def log(self, text):
124
        sys.stdout.write(text)
67 benedikt93 125
 
171 farthen 126
    def debug(self, text):
127
        if self.loglevel >= 3:
128
            self.log(text)
119 benedikt93 129
 
171 farthen 130
    def info(self, text):
131
        if self.loglevel >= 2:
132
            self.log(text)
82 benedikt93 133
 
171 farthen 134
    def warning(self, text):
135
        if self.loglevel >= 1:
136
            self.log("WARNING: " + text)
119 benedikt93 137
 
171 farthen 138
    def error(self, text):
139
        self.log("ERROR: " + text)
140
 
141
 
142
def command(func):
143
    """
144
        Decorator for all commands.
145
        The decorated function is called with (self, all, other, arguments, ...)
146
    """
147
    def decorator(args):
148
        return func(args[0], *args[1:])
149
    func._command = True
150
    decorator.func = func
151
    return decorator
152
 
153
 
154
def commandClass(cls):
155
    """
156
        Decorator for the class. Sets the self.cmddict of the class
157
        to all functions decorated with @command
158
    """
159
    cls.cmddict = {}
160
    for attr, value in cls.__dict__.iteritems():
161
        if getattr(value, 'func', False):
162
            if getattr(value.func, '_command', False):
163
                cls.cmddict[value.func.__name__] = value
164
    return cls
165
 
166
 
167
@commandClass
168
class Commandline(object):
169
    """
170
        If you want to create a new commandline function you just need to
171
        create a function with the name of it in this class and decorate
172
        it with the decorator @command. If you don't want to call the desired
173
        function (wrong arguments etc) just raise ArgumentError with or
174
        without an error message or raise ArgumentCountError
175
    """
176
    def __init__(self):
177
        self.logger = Logger()
178
        try:
179
            self.embios = libembios.Embios()
180
        except libembios.DeviceNotFoundError:
181
            self.logger.error("No emBIOS device found!")
182
            end(1)
183
 
184
    def _parsecommand(self, func, args):
185
        # adds self to the commandline args.
186
        # this is needed because the functions need access to their class.
187
        args.insert(0, self)
188
        if func in self.cmddict:
189
            try:
190
                self.cmddict[func](args)
191
            except ArgumentError, e:
192
                usage(e)
193
            except ArgumentError:
194
                usage("Syntax Error in function '" + func + "'")
195
            except ArgumentTypeError, e:
196
                usage(e)
197
            except NotImplementedError:
198
                self.logger.error("This function is not implemented yet!")
199
            except libembios.DeviceError, e:
200
                self.logger.error(str(e))
201
            except TypeError, e:
202
                if str(e).split(" ", 1)[0] == func + "()":
203
                    self.logger.error(usage("Argument Error in '" + func + "': Wrong argument count", specific=func))
204
                else:
205
                    raise
206
        else:
207
            usage("No such command")
67 benedikt93 208
 
171 farthen 209
    @staticmethod
210
    def _bool(something):
211
        """
212
            Converts quite everything into bool.
213
        """
214
        if type(something) == bool:
215
            return something
216
        elif type(something) == int or type(something) == long:
217
            return bool(something)
218
        elif type(something == str):
219
            truelist = ['true', '1', 't', 'y', 'yes']
220
            falselist = ['false', '0', 'f', 'n', 'no']
221
            if something.lower() in truelist:
222
                return True
223
            elif something.lower() in falselist:
224
                return False
225
        raise ArgumentTypeError("bool", "'"+str(something)+"'")
226
 
227
    @staticmethod
228
    def _hexint(something):
229
        """
230
            Converts quite everything to a hexadecimal represented integer.
231
            This works for default arguments too, because it returns
232
            None when it found that it got a NoneType object.
233
        """
234
        if type(something) == int or type(something) == long:
235
            return something
236
        elif type(something) == str:
237
            try:
238
                return int(something, 16)
239
            except ValueError:
240
                raise ArgumentTypeError("hexadecimal coded integer", "'"+str(something)+"'")
241
        elif type(something) == NoneType:
242
            return None
243
        else:
244
            raise ArgumentTypeError("hexadecimal coded integer", "'"+str(something)+"'")
245
 
246
    @staticmethod
247
    def _strcheck(string, values):
248
        if string in values:
249
            return string
250
        else:
251
            expected = ""
252
            for item in values:
253
                expected += "'" + item + "', "
254
            expected = expected[:-2]
255
            raise ArgumentTypeError("one out of " + expected, "'" + string + "'")
64 benedikt93 256
 
257
 
171 farthen 258
    @command
259
    def getinfo(self, infotype):
260
        """
261
            Get info on the running emBIOS.
262
            <infotype> may be either of 'version', 'packetsize', 'usermemrange'.
263
        """
264
        if infotype == "version":
265
            resp = self.embios.getversioninfo()
266
            self.logger.info(libembiosdata.swtypes[resp.swtypeid] + " v" + str(resp.majorv) + "." + str(resp.minorv) +
267
                             "." + str(resp.patchv) + " r" + str(resp.revision) + " running on " + libembiosdata.hwtypes[resp.hwtypeid] + "\n")
268
        elif infotype == "packetsize":
269
            resp = self.embios.getpacketsizeinfo()
270
            self.logger.info("Maximum packet sizes: "+str(resp))
271
        elif infotype == "usermemrange":
272
            resp = self.embios.getusermemrange()
273
            self.logger.info("The user memory range is "+hex(resp.lower)+" - "+hex(resp.upper-1))
274
        else:
275
            raise ArgumentTypeError("one out of 'version', 'packetsize', 'usermemrange'", infotype)
64 benedikt93 276
 
171 farthen 277
    @command
278
    def reset(self, force=False):
279
        """
280
            Resets the device"
281
            If <force> is 1, the reset will be forced, otherwise it will be gracefully,
282
            which may take some time.
283
        """
284
        force = self._bool(force)
285
        if force: self.logger.info("Resetting forcefully...\n")
286
        else: self.logger.info("Resetting...\n")
287
        self.embios.reset(force)
64 benedikt93 288
 
171 farthen 289
    @command
290
    def poweroff(self, force=False):
291
        """
292
            Powers the device off
293
            If <force> is 1, the poweroff will be forced, otherwise it will be gracefully,
294
            which may take some time.
295
        """
296
        force = self._bool(force)
297
        if force: self.logger.info("Resetting forcefully...\n")
298
        else: self.logger.info("Resetting...\n")
299
        self.embios.reset(force)
64 benedikt93 300
 
171 farthen 301
    @command
302
    def uploadfile(self, addr, filename):
303
        """
304
            Uploads a file to the device
305
            <offset>: the address to upload the file to
306
            <filename>: the path to the file
307
        """
308
        addr = self._hexint(addr)
309
        try:
310
            f = open(filename, 'rb')
311
        except IOError:
312
            raise ArgumentError("File not readable. Does it exist?")
313
        self.logger.info("Writing file '"+filename+"' to memory at "+hex(addr)+"...")
314
        with f:
315
            self.embios.write(addr, f.read())
316
        self.logger.info("done\n")
317
 
318
 
319
 
320
    @command
321
    def downloadfile(self, addr, size, filename):
322
        """
323
            Uploads a file to the device
324
            <offset>: the address to upload the file to
325
            <size>: the number of bytes to be read
326
            <filename>: the path to the file
327
        """
328
        addr = self._hexint(addr)
329
        size = self._hexint(size)
330
        try:
331
            f = open(filename, 'wb')
332
        except IOError:
333
            raise ArgumentError("Can not open file for write!")
334
        self.logger.info("Reading data from address "+hex(addr)+" with the size "+hex(size)+" to '"+filename+"'...")
335
        with f:
336
            f.write(self.embios.read(addr, size))
337
        self.logger.info("done\n")
338
 
339
    @command
340
    def uploadint(self, addr, integer):
341
        """
342
            Uploads a single integer to the device
343
            <offset>: the address to upload the integer to
344
            <data>: the integer to upload
345
        """
346
        addr = self._hexint(addr)
347
        integer = self._hexint(integer)
348
        if integer > 0xFFFFFFFF:
349
            raise ArgumentError("Specified integer too long")
350
        data = chr(integer)
351
        self.embios.writemem(addr, data)
352
        self.logger.info("Integer '"+hex(integer)+"' written successfully to "+hex(addr))
353
 
354
    @command
355
    def downloadint(self, addr):
356
        """
357
            Downloads a single integer from the device and prints it to the console window
358
            <offset>: the address to download the integer from
359
        """
360
        addr = self._hexint(addr)
361
        data = self.embios.readmem(addr, 1)
362
        integer = ord(data)
363
        self.logger.info("Integer '"+hex(integer)+"' read from address "+hex(addr))
364
 
365
    @command
366
    def i2crecv(self, bus, slave, addr, size):
367
        """
368
            Reads data from an I2C device
369
            <bus> the bus index
370
            <slave> the slave address
371
            <addr> the start address on the I2C device
372
            <size> the number of bytes to read
373
        """
374
        bus = self._hexint(bus)
375
        slave = self._hexint(slave)
376
        addr = self._hexint(addr)
377
        size = self._hexint(size)
378
        raise NotImplementedError
379
 
380
    @command
381
    def i2csend(self, bus, slave, addr, *args):
382
        """
383
            Writes data to an I2C device
384
            <bus> the bus index
385
            <slave> the slave address
386
            <addr> the start address on the I2C device
387
            <db1> ... <dbN> the data in single bytes, seperated by whitespaces,
388
                eg. 0x37 0x56 0x45 0x12
389
        """
390
        bus = self._hexint(bus)
391
        slave = self._hexint(slave)
392
        addr = self._hexint(addr)
393
        data = []
394
        for arg in args:
395
            data.append(self._hexint(arg))
396
        raise NotImplementedError
397
 
398
    @command
399
    def readusbconsole(self, size, outtype):
400
        """
401
            Reads data from the USB console.
402
            <size>: the number of bytes to read
403
            <outtype>: defines how to output the result:
404
                'file': writes the result to file <file>
405
                'printstring': writes the result as string to the console window
406
                'printhex': writes the result in hexedit notation to the console window
407
            <file>: the file to write the result to, can be omitted
408
                if <outtype> is not 'file'
409
        """
410
        size = self._hexint(size)
411
        raise NotImplementedError
412
 
413
 
414
    @command
415
    def writeusbconsole_file(self, file, offset=0, length=None):
416
        """
417
            Writes the file <file> to the USB console.
418
            Optional params <offset> <length>: specify the range in <file> to write
419
        """
420
        # We don't care about file here, this is done when opening it
421
        offset = self._hexint(offset)
422
        length = self._hexint(length)
423
        raise NotImplementedError
424
 
425
    @command
426
    def writeusbconsole_direct(self, *args):
427
        """
428
            Writes the strings <db1> ... <dbN> to the USB console."
429
        """
430
        raise NotImplementedError
431
 
432
    @command
433
    def readdevconsole(self, bitmask, size, outtype, file=None):
434
        """
435
            Reads data from one or more of the device's consoles.
436
            <bitmask>: the bitmask of the consoles to read from
437
            <size>: the number of bytes to read
438
            <outtype>: defines how to output the result:
439
                'file': writes the result to file <file>
440
                'printstring': writes the result as string to the console window
441
                'printhex': writes the result in hexedit notation to the console window
442
            <file>: the file to write the result to, can be omitted
443
                if <outtype> is not 'file'
444
        """
445
        bitmask = self._hexint(bitmask)
446
        size = self._hexint(size)
447
        outtype = self._strcheck(['file', 'printstring', 'printhex'])
448
        raise NotImplementedError
449
 
450
    @command
451
    def writedevconsole_file(self, bitmask, file, offset=0, length=None):
452
        """
453
            Writes the file <file> to the device consoles specified by <bitmask>
454
            Optional params <offset> <length>: specify the range in <file> to write
455
        """
456
        bitmask = self._hexint(bitmask)
457
        # We don't care about file here, this is done when opening it
458
        offset = self._hexint(offset)
459
        length = self._hexint(length)
460
        raise NotImplementedError
461
 
462
    @command
463
    def writedevconsole_direct(self, bitmask, *args):
464
        """
465
            Writes the integers <db1> ... <dbN> to the device consoles specified
466
            by <bitmask>
467
        """
468
        bitmask = self._hexint(bitmask)
469
        data = []
470
        for arg in args:
471
            data.append(self._hexint(arg))
472
        raise NotImplementedError
473
 
474
    @command
475
    def flushconsolebuffers(self, bitmask):
476
        """
477
            flushes one or more of the device consoles' buffers.
478
            <bitmask>: the bitmask of the consoles to be flushed
479
        """
480
        bitmask = self._hexint(bitmask)
481
        raise NotImplementedError
482
 
483
    @command
484
    def getprocinfo(self):
485
        """
486
            Fetches data on the currently running processes
487
            ATTENTION: this function will be print the information to the console window.
488
                If several threads are running this might overflow the window,
489
                causing not everything to be shown.
490
        """
491
        raise NotImplementedError
492
 
493
    @command
494
    def lockscheduler(self):
495
        """
496
            Locks (freezes) the scheduler
497
        """
498
        raise NotImplementedError
499
 
500
    @command
501
    def unlockscheduler(self):
502
        """
503
            Unlocks (unfreezes) the scheduler
504
        """
505
        raise NotImplementedError
506
 
507
    @command
508
    def suspendthread(self, threadid):
509
        """
510
            Suspends/resumes the thread with thread ID <threadid>
511
        """
512
        threadid = self._hexint(threadid)
513
        raise NotImplementedError
514
 
515
    @command
516
    def resumethread(self, threadid):
517
        """
518
            Resumes the thread with thread ID <threadid>
519
        """
520
        threadid = self._hexint(threadid)
521
        raise NotImplementedError
522
 
523
    @command
524
    def killthread(self, threadid):
525
        """
526
            Kills the thread with thread ID <threadid>
527
        """
528
        threadid = self._hexint(threadid)
529
        raise NotImplementedError
530
 
531
    @command
532
    def createthread(self, nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state):
533
        """
534
            Creates a new thread and returns its thread ID
535
            <namepointer> a pointer to the thread's name
536
            <entrypoint> a pointer to the entrypoint of the thread
537
            <stackpointer> a pointer to the stack of the thread
538
            <stacksize> the size of the thread's stack
539
            <type> the thread type, vaild are: 0 => user thread, 1 => system thread
540
            <priority> the priority of the thread, from 1 to 255
541
            <state> the thread's initial state, valid are: 1 => ready, 0 => suspended
542
        """
543
        nameptr = self._hexint(nameptr)
544
        entrypoint = self._hexint(entrypoint)
545
        stackpointer = self._hexint(stackpointer)
546
        stacksize = self._hexint(stacksize)
547
        priority = self._hexint(priority)
548
        self.embios.createthread(nameptr, entrypoint, stackptr, stacksize, type, priority, state)
549
 
550
    @command
551
    def run(self, address):
552
        """
553
            Executes the emBIOS application at <address>.
554
        """
555
        address = self._hexint(address)
556
        raise NotImplementedError
557
 
558
    @command
559
    def readrawbootflash(self, addr_flash, addr_mem, size):
560
        """
561
            Reads <size> bytes from bootflash to memory.
562
            <addr_bootflsh>: the address in bootflash to read from
563
            <addr_mem>: the address in memory to copy the data to
564
        """
565
        addr_flash = self._hexint(addr_flash)
566
        addr_mem = self._hexint(addr_mem)
567
        size = self._hexint(size)
568
        raise NotImplementedError
569
 
570
    @command
571
    def writerawbootflash(self, addr_flash, addr_mem, size):
572
        """
573
            Writes <size> bytes from memory to bootflash.
574
            ATTENTION: Don't call this unless you really know what you're doing!
575
            This may BRICK your device (unless it has a good recovery option)
576
            <addr_mem>: the address in memory to copy the data from
577
            <addr_bootflsh>: the address in bootflash to write to
578
        """
579
        addr_flash = self._hexint(addr_flash)
580
        addr_mem = self._hexint(addr_mem)
581
        size = self._hexint(size)
582
        raise NotImplementedError
583
 
584
    @command
585
    def flushcaches(self):
586
        """
587
            Flushes the CPUs data and instruction caches.
588
        """
589
        raise NotImplementedError
64 benedikt93 590
 
171 farthen 591
    @command
592
    def aesencrypt(self, addr, size, keyindex):
593
        """
594
            Encrypt a buffer using a hardware key
595
        """
596
        addr = self._hexint(addr)
597
        size = self._hexint(size)
598
        keyindex = self._hexint(keyindex)
599
        self.embios.aesencrypt(addr, size, keyindex)
82 benedikt93 600
 
171 farthen 601
    @command
602
    def aesdecrypt(self, addr, size, keyindex):
603
        """
604
            Decrypt a buffer using a hardware key
605
        """
606
        addr = self._hexint(addr)
607
        size = self._hexint(size)
608
        keyindex = self._hexint(keyindex)
609
        self.embios.aesdecrypt(addr, size, keyindex)
64 benedikt93 610
 
171 farthen 611
if __name__ == "__main__":
612
    if len(sys.argv) < 2:
613
        usage("No command specified")
614
    interface = Commandline()
615
    interface._parsecommand(sys.argv[1], sys.argv[2:])