Subversion Repositories freemyipod

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
56 benedikt93 1
#!/usr/bin/env python
2
#
3
#
891 theseven 4
#    Copyright 2013 TheSeven, benedikt93, Farthen
56 benedikt93 5
#
6
#
427 farthen 7
#    This file is part of emCORE.
56 benedikt93 8
#
427 farthen 9
#    emCORE is free software: you can redistribute it and/or
56 benedikt93 10
#    modify it under the terms of the GNU General Public License as
11
#    published by the Free Software Foundation, either version 2 of the
12
#    License, or (at your option) any later version.
13
#
427 farthen 14
#    emCORE is distributed in the hope that it will be useful,
56 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
#
19
#    You should have received a copy of the GNU General Public License
427 farthen 20
#    along with emCORE.  If not, see <http://www.gnu.org/licenses/>.
56 benedikt93 21
#
22
#
23
 
402 farthen 24
"""
427 farthen 25
    emCORE client library.
26
    Provides functions to communicate with emCORE devices via the USB bus.
402 farthen 27
"""
28
 
56 benedikt93 29
import sys
30
import struct
506 farthen 31
import ctypes
171 farthen 32
import usb.core
582 farthen 33
import base64
778 farthen 34
import datetime
56 benedikt93 35
 
506 farthen 36
from libemcoredata import *
609 farthen 37
from misc import Logger, Bunch, remote_pointer, Error, ArgumentError, getthread, gethwname
394 farthen 38
from functools import wraps
39
 
171 farthen 40
class DeviceNotFoundError(Error):
41
    pass
56 benedikt93 42
 
171 farthen 43
class DeviceError(Error):
44
    pass
56 benedikt93 45
 
171 farthen 46
class SendError(Error):
47
    pass
56 benedikt93 48
 
171 farthen 49
class ReceiveError(Error):
50
    pass
56 benedikt93 51
 
52
 
398 farthen 53
def command(timeout = None, target = None):
394 farthen 54
    """
55
        Decorator for all commands.
56
        It adds the "timeout" variable to all commands.
57
        It also provides the possibility to set the timeout directly in the decorator.
58
        It also includes some dirty hacks to not learn from.
59
    """
60
    time = timeout # dirty hack because otherwise it would raise a scoping problem.
61
                   # The reason is probably because I suck but I can't find any good explanation of this.
62
    def decorator(func):
63
        @wraps(func)
64
        def wrapper(*args, **kwargs):
398 farthen 65
            self = args[0] # little cheat as it expects self being always the first argument
394 farthen 66
            # precommand stuff
398 farthen 67
            if target is not None:
68
                if self.lib.dev.hwtypeid != target:
518 farthen 69
                    raise DeviceError("Wrong device for target-specific command. Expected \'%s\' but got \'%s\'" %
70
                                     (gethwname(target), gethwname(self.lib.dev.hwtypeid)))
394 farthen 71
            timeout = None
72
            if "timeout" in kwargs.keys():
73
                timeout = kwargs['timeout']
74
            elif time is not None:
75
                timeout = time
76
            if timeout is not None:
77
                oldtimeout = self.lib.dev.timeout
78
                self.lib.dev.timeout = timeout
79
            # function call
80
            ret = func(*args)
81
            # postcommand stuff
82
            if timeout is not None:
83
                self.lib.dev.timeout = oldtimeout
84
            return ret
396 farthen 85
        func._command = True
86
        wrapper.func = func
394 farthen 87
        return wrapper
88
    return decorator
89
 
90
 
427 farthen 91
class Emcore(object):
176 farthen 92
    """
427 farthen 93
        Class for all emcore functions.
394 farthen 94
        They all get the "@command()" decorator.
95
        This decorator has a timeout variable that can be set to change the
96
        device timeout for the duration of the function.
97
        It also adds a "timeout" argument to every function to access this
98
        feature from external. So DON'T EVER use a parameter called 'timeout'
99
        in your commands. Variables are ok.
499 farthen 100
 
101
        If you want to enable logging please pass a misc.Logger object to the
102
        constructor.
176 farthen 103
    """
499 farthen 104
    def __init__(self, logger = Logger(loglevel = -1)):
105
        self.logger = logger
427 farthen 106
        self.logger.debug("Initializing Emcore object\n")
401 farthen 107
        self.lib = Lib(self.logger)
343 farthen 108
 
109
        self.getversioninfo()
949 theseven 110
        if self.lib.dev.swtypeid not in swtypes:
891 theseven 111
            raise DeviceError("Connected to unknown software type. Exiting")
440 farthen 112
 
675 farthen 113
        self.getmallocpoolbounds()
56 benedikt93 114
 
394 farthen 115
    @command()
178 farthen 116
    def _readmem(self, addr, size):
117
        """ Reads the memory from location 'addr' with size 'size'
891 theseven 118
            from the device. Can handle up to 0xff0 bytes.
178 farthen 119
        """
548 farthen 120
        resp = self.lib.monitorcommand(struct.pack("<IIII", 4, addr, size, 0), "III%ds" % size, (None, None, None, "data"))
178 farthen 121
        return resp.data
394 farthen 122
 
123
    @command()
178 farthen 124
    def _writemem(self, addr, data):
125
        """ Writes the data in 'data' to the location 'addr'
891 theseven 126
            in the memory of the device. Can handle up to 0xff0 bytes.
178 farthen 127
        """
548 farthen 128
        return self.lib.monitorcommand(struct.pack("<IIII%ds" % len(data), 5, addr, len(data), 0, data), "III", (None, None, None))
178 farthen 129
 
394 farthen 130
    @command()
171 farthen 131
    def getversioninfo(self):
427 farthen 132
        """ This returns the emCORE version and device information. """
548 farthen 133
        resp = self.lib.monitorcommand(struct.pack("<IIII", 1, 0, 0, 0), "IBBBBI", ("revision", "majorv", "minorv", "patchv", "swtypeid", "hwtypeid"))
342 farthen 134
        self.lib.dev.version.revision = resp.revision
135
        self.lib.dev.version.majorv = resp.majorv
136
        self.lib.dev.version.minorv = resp.minorv
137
        self.lib.dev.version.patchv = resp.patchv
518 farthen 138
        self.logger.debug("Device Software Type ID = 0x%X\n" % resp.swtypeid)
342 farthen 139
        self.lib.dev.swtypeid = resp.swtypeid
518 farthen 140
        self.logger.debug("Device Hardware Type ID = 0x%X\n" % resp.hwtypeid)
342 farthen 141
        self.lib.dev.hwtypeid = resp.hwtypeid
142
        return resp
56 benedikt93 143
 
394 farthen 144
    @command()
675 farthen 145
    def getmallocpoolbounds(self):
146
        """ This returns the memory range of the malloc pool """
949 theseven 147
        try:
148
            resp = self.lib.monitorcommand(struct.pack("<IIII", 1, 1, 0, 0), "III", ("lower", "upper", None))
149
            self.logger.debug("Malloc pool bounds = 0x%X - 0x%X\n" % (resp.lower, resp.upper))
150
            self.lib.dev.mallocpool.lower = resp.lower
151
            self.lib.dev.mallocpool.upper = resp.upper
152
            self.lib.dev.havemalloc = True
153
            return resp
154
        except:
155
            self.logger.debug("Device doesn't have a memory allocator\n")
156
            self.lib.dev.havemalloc = True
157
            return None
56 benedikt93 158
 
394 farthen 159
    @command()
171 farthen 160
    def reset(self, force=False):
161
        """ Reboot the device """
891 theseven 162
        return self.lib.monitorcommand(struct.pack("<IIII", 2, 0 if force else 1, 0, 0))
56 benedikt93 163
 
394 farthen 164
    @command()
171 farthen 165
    def poweroff(self, force=False):
166
        """ Powers the device off. """
891 theseven 167
        return self.lib.monitorcommand(struct.pack("<IIII", 3, 0 if force else 1, 0, 0))
56 benedikt93 168
 
394 farthen 169
    @command()
171 farthen 170
    def read(self, addr, size):
171
        """ Reads the memory from location 'addr' with size 'size'
891 theseven 172
            from the device. This takes care of splitting long requests.
171 farthen 173
        """
582 farthen 174
        data = b""
891 theseven 175
        self.logger.debug("Downloading %d bytes from 0x%X\n" % (size, addr))
176
        while size > 0:
177
            readsize = min(size, 0xf00)
178
            data += self._readmem(addr, readsize)
171 farthen 179
            addr += readsize
891 theseven 180
            size -= readsize
171 farthen 181
        return data
56 benedikt93 182
 
394 farthen 183
    @command()
171 farthen 184
    def write(self, addr, data):
185
        """ Writes the data in 'data' to the location 'addr'
891 theseven 186
            in the memory of the device. This takes care of splitting long requests.
171 farthen 187
        """
892 theseven 188
        size = len(data)
189
        self.logger.debug("Uploading %d bytes to 0x%X\n" % (size, addr))
171 farthen 190
        offset = 0
891 theseven 191
        while size > 0:
192
            writesize = min(size, 0xf00)
193
            self._writemem(addr, data[offset:offset+writesize])
171 farthen 194
            offset += writesize
195
            addr += writesize
891 theseven 196
            size -= writesize
171 farthen 197
        return data
56 benedikt93 198
 
394 farthen 199
    @command()
442 farthen 200
    def upload(self, data):
201
        """ Allocates memory of the size of 'data' and uploads 'data' to that memory region.
202
            Returns the address where 'data' is stored
203
        """
204
        addr = self.malloc(len(data))
205
        self.write(addr, data)
206
        return addr
207
 
208
    @command()
588 theseven 209
    def readstring(self, addr, maxlength = 256, replacement = "."):
173 farthen 210
        """ Reads a zero terminated string from memory 
211
            Reads only a maximum of 'maxlength' chars.
212
        """
592 farthen 213
        if addr == 0: return "<NULL>"
891 theseven 214
        cin_maxsize = 1024
173 farthen 215
        string = ""
592 farthen 216
        done = False
588 theseven 217
        while not done and (len(string) < maxlength or maxlength < 0):
178 farthen 218
            data = self._readmem(addr, min(maxlength - len(string), cin_maxsize))
582 farthen 219
            length = data.find(b"\0")
173 farthen 220
            if length >= 0:
592 farthen 221
                data = data[:length]
222
                done = True
223
            for i in range(len(data)):
224
                byte = ord(data[i : i + 1])
225
                if byte < 0x20: string = string + replacement
588 theseven 226
                else: string = string + chr(byte)
173 farthen 227
            addr += cin_maxsize
228
        return string
229
 
394 farthen 230
    @command()
171 farthen 231
    def i2cread(self, index, slaveaddr, startaddr, size):
232
        """ Reads data from an i2c slave """
582 farthen 233
        data = b""
236 farthen 234
        for i in range(size):
548 farthen 235
            resp = self.lib.monitorcommand(struct.pack("<IBBBBII", 8, index, slaveaddr, startaddr + i, 1, 0, 0), "III1s", (None, None, None, "data"))
236 farthen 236
            data += resp.data
237
        return data
56 benedikt93 238
 
394 farthen 239
    @command()
171 farthen 240
    def i2cwrite(self, index, slaveaddr, startaddr, data):
241
        """ Writes data to an i2c slave """
176 farthen 242
        size = len(data)
891 theseven 243
        if size > 48 or size < 1:
244
            raise ArgumentError("Size must be a number between 1 and 48")
548 farthen 245
        return self.lib.monitorcommand(struct.pack("<IBBBBII%ds" % size, 9, index, slaveaddr, startaddr, size, 0, 0, data), "III", (None, None, None))
56 benedikt93 246
 
394 farthen 247
    @command()
896 theseven 248
    def usbcread(self, maxsize = 48):
582 farthen 249
        """ Reads one packet with the maximal cin size from the console """
896 theseven 250
        maxsize = min(0xff0, maxsize)
251
        resp = self.lib.monitorcommand(struct.pack("<IIII", 10, maxsize, 0, 0), "III%ds" % maxsize, ("validsize", "buffersize", "queuesize", "data"))
582 farthen 252
        resp.data = resp.data[:resp.validsize].decode("latin_1")
896 theseven 253
        resp.maxsize = maxsize
176 farthen 254
        return resp
56 benedikt93 255
 
394 farthen 256
    @command()
171 farthen 257
    def usbcwrite(self, data):
258
        """ Writes data to the USB console """
891 theseven 259
        cin_maxsize = 48
176 farthen 260
        size = len(data)
261
        while len(data) > 0:
262
            writesize = min(cin_maxsize, len(data))
548 farthen 263
            resp = self.lib.monitorcommand(struct.pack("<IIII%ds" % writesize, 11, writesize, 0, 0, data[:writesize]), "III", ("validsize", "buffersize", "freesize"))
176 farthen 264
            data = data[resp.validsize:]
265
        return size
56 benedikt93 266
 
394 farthen 267
    @command()
176 farthen 268
    def cread(self, bitmask=0x1):
269
        """ Reads one packet with the maximal cin size from the device consoles
171 farthen 270
            identified with the specified bitmask
271
        """
891 theseven 272
        cin_maxsize = 48
548 farthen 273
        resp = self.lib.monitorcommand(struct.pack("<IIII", 13, bitmask, cin_maxsize, 0), "III%ds" % cin_maxsize, ("size", None, None))
176 farthen 274
        resp.data = resp.data[size:]
275
        resp.maxsize = cin_maxsize
276
        return resp
394 farthen 277
 
278
    @command()
176 farthen 279
    def cwrite(self, data, bitmask=0x1):
171 farthen 280
        """ Writes data to the device consoles 
281
            identified with the specified bitmask.
282
        """
891 theseven 283
        cin_maxsize = 48
176 farthen 284
        size = len(data)
285
        while len(data) > 0:
286
            writesize = min(cin_maxsize, len(data))
548 farthen 287
            resp = self.lib.monitorcommand(struct.pack("<IIII%ds" % writesize, 12, bitmask, writesize, 0, data[:writesize]), "III", (None, None, None))
176 farthen 288
            data = data[writesize:]
289
        return size
56 benedikt93 290
 
394 farthen 291
    @command()
171 farthen 292
    def cflush(self, bitmask):
293
        """ Flushes the consoles specified with 'bitmask' """
548 farthen 294
        return self.lib.monitorcommand(struct.pack("<IIII", 14, bitmask, 0, 0), "III", (None, None, None))
56 benedikt93 295
 
394 farthen 296
    @command()
173 farthen 297
    def getprocinfo(self):
171 farthen 298
        """ Gets current state of the scheduler """
173 farthen 299
        schedulerstate = self.lockscheduler()
548 farthen 300
        resp = self.lib.monitorcommand(struct.pack("<IIII", 15, 0, 0, 0), "III", ("structver", "structptr", None))
704 theseven 301
        if resp.structver != 3:
506 farthen 302
            raise DeviceError("Unsupported thread struct version!")
303
 
173 farthen 304
        threads = []
506 farthen 305
        structptr = resp.structptr
173 farthen 306
        id = 0
506 farthen 307
        while structptr != 0:
308
            threadstruct = scheduler_thread()
518 farthen 309
            self.logger.debug("Reading thread struct of thread at 0x%X\n" % structptr)
891 theseven 310
            threaddata = self._readmem(structptr, ctypes.sizeof(scheduler_thread))
506 farthen 311
            threadstruct._from_string(threaddata)
312
            threadstruct = threadstruct._to_bunch()
313
            threadstruct.id = id # only for the purpose of detecting the idle thread as it is always the first one
314
            threadstruct.addr = structptr
592 farthen 315
            if threadstruct.name != 0:
316
                threadstruct.name = self.readstring(threadstruct.name)
689 farthen 317
            else: threadstruct.name = "[Thread 0x%08X]" % structptr
506 farthen 318
            threadstruct.state = thread_state(threadstruct.state)
609 farthen 319
            if threadstruct.block_type == "THREAD_BLOCK_MUTEX":
320
                blocked_by_struct = mutex
321
            elif threadstruct.block_type == "THREAD_BLOCK_WAKEUP":
322
                blocked_by_struct = wakeup
323
            else:
324
                blocked_by_struct = None
325
            if blocked_by_struct != None:
326
                blocked_by_data = self.read(threadstruct.blocked_by, ctypes.sizeof(blocked_by_struct))
327
                blocked_by = blocked_by_struct()
328
                blocked_by._from_string(blocked_by_data)
329
                threadstruct.blocked_by = remote_pointer(threadstruct.blocked_by, blocked_by._to_bunch())
330
            else:
331
                threadstruct.blocked_by = 0
506 farthen 332
            threads.append(threadstruct)
173 farthen 333
            id += 1
506 farthen 334
            structptr = threadstruct.thread_next
508 farthen 335
        self.lockscheduler(schedulerstate)
609 farthen 336
 
337
        for thread in threads:
338
            if thread.block_type == "THREAD_BLOCK_MUTEX":
339
                thread.blocked_by.owner = remote_pointer(thread.blocked_by.owner, getthread(thread.blocked_by.owner, threads))
340
            if thread.block_type == "THREAD_BLOCK_WAKEUP":
341
                thread.blocked_by.waiter = remote_pointer(thread.blocked_by.waiter, getthread(thread.blocked_by.waiter, threads))
173 farthen 342
        return threads
56 benedikt93 343
 
394 farthen 344
    @command()
173 farthen 345
    def lockscheduler(self, freeze=True):
171 farthen 346
        """ Freezes/Unfreezes the scheduler """
548 farthen 347
        resp = self.lib.monitorcommand(struct.pack("<IIII", 16, 1 if freeze else 0, 0, 0), "III", ("before", None, None))
173 farthen 348
        return True if resp.before == 1 else False
67 benedikt93 349
 
394 farthen 350
    @command()
173 farthen 351
    def unlockscheduler(self):
171 farthen 352
        """ Unfreezes the scheduler """
548 farthen 353
        return self.lib.monitorcommand(struct.pack("<IIII", 16, 0, 0, 0), "III", ("before", None, None))
56 benedikt93 354
 
394 farthen 355
    @command()
171 farthen 356
    def suspendthread(self, id, suspend=True):
357
        """ Suspends the thread with the specified id """
548 farthen 358
        resp = self.lib.monitorcommand(struct.pack("<IIII", 17, 1 if suspend else 0, id, 0), "III", ("before", None, None))
173 farthen 359
        return True if resp.before == 1 else False
56 benedikt93 360
 
394 farthen 361
    @command()
173 farthen 362
    def resumethread(self, id):
363
        """ Resumes the thread with the specified id """
548 farthen 364
        return self.lib.monitorcommand(struct.pack("<IIII", 17, 0, id, 0), "III", ("before", None, None))
56 benedikt93 365
 
394 farthen 366
    @command()
171 farthen 367
    def killthread(self, id):
368
        """ Kills the thread with the specified id """
548 farthen 369
        return self.lib.monitorcommand(struct.pack("<IIII", 18, id, 0, 0), "III", ("before", None, None))
56 benedikt93 370
 
394 farthen 371
    @command()
171 farthen 372
    def createthread(self, nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state):
373
        """ Creates a thread with the specified attributes """
374
        if threadtype == "user":
375
            threadtype = 0
376
        elif threadtype == "system":
377
            threadtype = 1
378
        else:
341 farthen 379
            raise ArgumentError("Threadtype must be either 'system' or 'user'")
171 farthen 380
        if priority > 256 or priority < 0:
341 farthen 381
            raise ArgumentError("Priority must be a number between 0 and 256")
171 farthen 382
        if state == "ready":
383
            state = 0
384
        elif state == "suspended":
385
            state = 1
386
        else:
341 farthen 387
            raise ArgumentError("State must be either 'ready' or 'suspended'")
548 farthen 388
        resp = self.lib.monitorcommand(struct.pack("<IIIIIIII", 19, nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state), "III", ("threadptr", None, None))
516 farthen 389
        if resp.threadptr < 0:
518 farthen 390
            raise DeviceError("The device returned the error code %d" % resp.threadptr)
171 farthen 391
        return resp
56 benedikt93 392
 
394 farthen 393
    @command()
172 farthen 394
    def flushcaches(self):
171 farthen 395
        """ Flushes the CPU instruction and data cache """
548 farthen 396
        return self.lib.monitorcommand(struct.pack("<IIII", 20, 0, 0, 0), "III", (None, None, None))
56 benedikt93 397
 
394 farthen 398
    @command()
891 theseven 399
    def execimage(self, addr, args = [], copy = False):
400
        """ Runs the emCORE app at 'addr' with command line arguments 'args' """
401
        flags = 1 if copy else 0
402
        argptr = b""
403
        argdata = b""
404
        ptr = len(args) * 4
405
        for arg in args:
406
           arg = arg.encode("utf_8") + "\0"
407
           argptr += struct.pack("<I", ptr);
408
           argdata += arg
409
           ptr += len(arg)
410
        if ptr > 48:
411
            buf = self.malloc(ptr)
412
            try:
413
                self._writemem(buf, argptr + argdata)
414
                result = self.lib.monitorcommand(struct.pack("<IIBBBBI", 21, addr, 0, 0, flags, len(args), buf), "III", ("thread", None, None))
415
            finally: self.free(buf)
416
        else: return self.lib.monitorcommand(struct.pack("<IIBBBBI", 21, addr, 0, 0, flags, len(args), 0) + argptr + argdata, "III", ("thread", None, None))
56 benedikt93 417
 
394 farthen 418
    @command()
891 theseven 419
    def run(self, app, args = []):
420
        """ Uploads and runs the emCORE app in the string 'app'
421
            with command line arguments 'args'
422
        """
589 farthen 423
        if app[:8].decode("latin_1") != "emCOexec":
427 farthen 424
            raise ArgumentError("The specified app is not an emCORE application")
452 theseven 425
        baseaddr = self.malloc(len(app))
238 farthen 426
        self.write(baseaddr, app)
891 theseven 427
        result = self.execimage(baseaddr, args)
452 theseven 428
        return Bunch(thread=result.thread)
238 farthen 429
 
395 farthen 430
    @command(timeout = 5000)
171 farthen 431
    def bootflashread(self, memaddr, flashaddr, size):
432
        """ Copies the data in the bootflash at 'flashaddr' of the specified size
433
            to the memory at addr 'memaddr'
434
        """
548 farthen 435
        return self.lib.monitorcommand(struct.pack("<IIII", 22, memaddr, flashaddr, size), "III", (None, None, None))
82 benedikt93 436
 
395 farthen 437
    @command(timeout = 30000)
171 farthen 438
    def bootflashwrite(self, memaddr, flashaddr, size):
439
        """ Copies the data in the memory at 'memaddr' of the specified size
440
            to the boot flash at addr 'flashaddr'
441
        """
548 farthen 442
        return self.lib.monitorcommand(struct.pack("<IIII", 23, memaddr, flashaddr, size), "III", (None, None, None))
56 benedikt93 443
 
394 farthen 444
    @command()
442 farthen 445
    def execfirmware(self, targetaddr, addr, size):
446
        """ Moves the firmware at 'addr' with size 'size' to 'targetaddr' and passes all control to it. """
518 farthen 447
        self.logger.debug("Moving firmware at 0x%X with the size %d to 0x%X and executing it\n" % (addr, size, targetaddr))
548 farthen 448
        return self.lib.monitorcommand(struct.pack("<IIII", 24, targetaddr, addr, size))
56 benedikt93 449
 
395 farthen 450
    @command(timeout = 30000)
171 farthen 451
    def aesencrypt(self, addr, size, keyindex):
452
        """ Encrypts the buffer at 'addr' with the specified size
453
            with the hardware AES key index 'keyindex'
454
        """
548 farthen 455
        return self.lib.monitorcommand(struct.pack("<IBBHII", 25, 1, 0, keyindex, addr, size), "III", (None, None, None))
82 benedikt93 456
 
395 farthen 457
    @command(timeout = 30000)
171 farthen 458
    def aesdecrypt(self, addr, size, keyindex):
459
        """ Decrypts the buffer at 'addr' with the specified size
460
            with the hardware AES key index 'keyindex'
461
        """
548 farthen 462
        return self.lib.monitorcommand(struct.pack("<IBBHII", 25, 0, 0, keyindex, addr, size), "III", (None, None, None))
82 benedikt93 463
 
395 farthen 464
    @command(timeout = 30000)
171 farthen 465
    def hmac_sha1(self, addr, size, destination):
466
        """ Generates a HMAC-SHA1 hash of the buffer and saves it to 'destination' """
548 farthen 467
        return self.lib.monitorcommand(struct.pack("<IIII", 26, addr, size, destination), "III", (None, None, None))
56 benedikt93 468
 
398 farthen 469
    @command(target = 0x47324e49)
227 theseven 470
    def ipodnano2g_getnandinfo(self):
471
        """ Target-specific function: ipodnano2g
472
            Gathers some information about the NAND chip used
473
        """
548 farthen 474
        return self.lib.monitorcommand(struct.pack("<IIII", 0xffff0001, 0, 0, 0), "IHHHH", ("type", "pagesperblock", "banks", "userblocks", "blocks"))
227 theseven 475
 
398 farthen 476
    @command(timeout = 30000, target = 0x47324e49)
227 theseven 477
    def ipodnano2g_nandread(self, addr, start, count, doecc, checkempty):
478
        """ Target-specific function: ipodnano2g
479
            Reads data from the NAND chip into memory
480
        """
548 farthen 481
        return self.lib.monitorcommand(struct.pack("<IIII", 0xffff0002, addr | (0x80000000 if doecc else 0) | (0x40000000 if checkempty else 0), start, count), "III", (None, None, None))
227 theseven 482
 
398 farthen 483
    @command(timeout = 30000, target = 0x47324e49)
227 theseven 484
    def ipodnano2g_nandwrite(self, addr, start, count, doecc):
485
        """ Target-specific function: ipodnano2g
486
            Writes data to the NAND chip
487
        """
548 farthen 488
        return self.lib.monitorcommand(struct.pack("<IIII", 0xffff0003, addr | (0x80000000 if doecc else 0), start, count), "III", (None, None, None))
227 theseven 489
 
398 farthen 490
    @command(timeout = 30000, target = 0x47324e49)
227 theseven 491
    def ipodnano2g_nanderase(self, addr, start, count):
492
        """ Target-specific function: ipodnano2g
493
            Erases blocks on the NAND chip and stores the results to memory
494
        """
548 farthen 495
        return self.lib.monitorcommand(struct.pack("<IIII", 0xffff0004, addr, start, count), "III", (None, None, None))
227 theseven 496
 
398 farthen 497
    @command(target = 0x4c435049)
346 theseven 498
    def ipodclassic_gethddinfo(self):
499
        """ Target-specific function: ipodclassic
500
            Gather information about the hard disk drive
501
        """
548 farthen 502
        return self.lib.monitorcommand(struct.pack("<IIII", 0xffff0001, 0, 0, 0), "IQQII", ("identifyptr", "totalsectors", "virtualsectors", "bbtptr", "bbtsize"))
346 theseven 503
 
398 farthen 504
    @command(timeout = 30000, target = 0x4c435049)
346 theseven 505
    def ipodclassic_hddaccess(self, type, sector, count, addr):
506
        """ Target-specific function: ipodclassic
507
            Access the hard disk, type = 0 (read) / 1 (write)
508
        """
548 farthen 509
        rc = self.lib.monitorcommand(struct.pack("<IIQIIII", 0xffff0002, type, sector, count, addr, 0, 0), "III", ("rc", None, None))
585 theseven 510
        if (rc.rc > 0x80000000):
511
            raise DeviceError("HDD access (type=%d, sector=%d, count=%d, addr=0x%08X) failed with RC 0x%08X" % (type, sector, count, addr, rc.rc))
346 theseven 512
 
615 theseven 513
    @command(timeout = 30000, target = 0x4c435049)
514
    def ipodclassic_reloadbbt(self):
515
        """ Target-specific function: ipodclassic
516
            Reload the ATA bbt
517
        """
518
        rc = self.lib.monitorcommand(struct.pack("<IIII", 0xffff0003, 0, 0, 0), "III", (None, None, None))
519
 
520
    @command(timeout = 30000, target = 0x4c435049)
521
    def ipodclassic_disablebbt(self):
522
        """ Target-specific function: ipodclassic
523
            Disable the ATA bbt
524
        """
525
        rc = self.lib.monitorcommand(struct.pack("<IIII", 0xffff0004, 0, 0, 0), "III", (None, None, None))
526
 
398 farthen 527
    @command(target = 0x4c435049)
787 theseven 528
    def ipodclassic_readbbt(self, tempaddr = None):
529
        """ Target-specific function: ipodclassic
530
            Read hard drive bad block table
531
        """
532
        if tempaddr is None:
533
            tempaddr = self.memalign(0x10, 4096)
534
            malloc = True
535
        else:
536
            malloc = False
537
        try:
538
            self.ipodclassic_hddaccess(0, 0, 1, tempaddr)
539
            bbt = self.read(tempaddr, 4096)
540
        finally:
541
            if malloc == True:
542
                self.free(tempaddr)
543
        try:
544
            bbtheader = struct.unpack("<8s2024sQII512I", bbt)
545
        except struct.error:
546
            raise ArgumentError("There is no emCORE hard disk BBT present on this device")
547
        if bbtheader[0] != b"emBIbbth":
548
            raise ArgumentError("There is no emCORE hard disk BBT present on this device")
549
        bbtsectors = bbtheader[4]
550
        if malloc:
551
            tempaddr = self.memalign(0x10, 4096 * bbtsectors)
552
        try:
553
            sector = 1
554
            count = 0
555
            offset = 0
556
            for i in range(0, bbtsectors):
557
                if bbtheader[5 + i] == sector + count:
558
                    count = count + 1
559
                else:
560
                    self.ipodclassic_hddaccess(0, sector, count, tempaddr + offset)
561
                    offset = offset + count * 4096
562
                    sector = bbtheader[5 + i]
563
                    count = 1
564
            self.ipodclassic_hddaccess(0, sector, count, tempaddr + offset)
565
            bbt += self.read(tempaddr, 4096 * bbtsectors)
566
        finally:
567
            if malloc == True:
568
                self.free(tempaddr)
569
        return bbt
570
 
571
    @command(target = 0x4c435049)
585 theseven 572
    def ipodclassic_writebbt(self, bbt, tempaddr = None):
346 theseven 573
        """ Target-specific function: ipodclassic
574
            Write hard drive bad block table
575
        """
576
        try:
577
            bbtheader = struct.unpack("<8s2024sQII512I", bbt[:4096])
578
        except struct.error:
427 farthen 579
            raise ArgumentError("The specified file is not an emCORE hard disk BBT")
787 theseven 580
        if bbtheader[0] != b"emBIbbth":
427 farthen 581
            raise ArgumentError("The specified file is not an emCORE hard disk BBT")
602 theseven 582
        bbtsectors = bbtheader[4]
787 theseven 583
        if (bbtsectors + 1) * 4096 != len(bbt):
584
            raise ArgumentError("Size of BBT is not consistent: Expected %d bytes, got %d" % ((bbtsectors + 1) * 4096, len(bbt)))
585 theseven 585
        if tempaddr is None:
787 theseven 586
            tempaddr = self.memalign(0x10, len(bbt))
585 theseven 587
            malloc = True
588
        else:
589
            malloc = False
590
        try:
591
            self.write(tempaddr, bbt)
780 user890104 592
            self.disk_unmount(0)
585 theseven 593
            sector = 0
594
            count = 1
595
            offset = 0
596
            for i in range(bbtsectors):
602 theseven 597
                if bbtheader[5 + i] == sector + count:
585 theseven 598
                    count = count + 1
599
                else:
600
                    self.ipodclassic_hddaccess(1, sector, count, tempaddr + offset)
601
                    offset = offset + count * 4096
787 theseven 602
                    sector = bbtheader[5 + i]
585 theseven 603
                    count = 1
604
            self.ipodclassic_hddaccess(1, sector, count, tempaddr + offset)
780 user890104 605
            self.ipodclassic_reloadbbt()
606
            self.disk_mount(0)
602 theseven 607
        finally:
585 theseven 608
            if malloc == True:
609
                self.free(tempaddr)
346 theseven 610
 
394 farthen 611
    @command()
379 theseven 612
    def storage_get_info(self, volume):
346 theseven 613
        """ Get information about a storage device """
484 theseven 614
        self.logger.debug("Getting storage information\n")
779 user890104 615
        result = self.lib.monitorcommand(struct.pack("<IIII", 27, volume, 0, 0), "IIIIIIIII", ("version", None, None, "sectorsize", "numsectors", "vendorptr", "productptr", "revisionptr", "driverinfoptr"))
346 theseven 616
        if result.version != 1:
617
            raise ValueError("Unknown version of storage_info struct: %d" % result.version)
379 theseven 618
        result.vendor = self.readstring(result.vendorptr)
619
        result.product = self.readstring(result.productptr)
620
        result.revision = self.readstring(result.revisionptr)
484 theseven 621
        self.logger.debug("Got storage information:\n")
622
        self.logger.debug("Vendor: %s\n" % result.vendor)
623
        self.logger.debug("Product: %s\n" % result.product)
624
        self.logger.debug("Revision: %s\n" % result.revision)
625
        self.logger.debug("Sector size: %d\n" % result.sectorsize)
626
        self.logger.debug("Number of sectors: %d\n" % result.numsectors)
346 theseven 627
        return result
628
 
395 farthen 629
    @command(timeout = 50000)
479 theseven 630
    def storage_read_sectors_md(self, volume, sector, count, addr):
631
        """ Read sectors from as storage device """
518 farthen 632
        self.logger.debug("Reading %d sectors from disk at volume %d, sector %d to memory at 0x%X\n" % (count, volume, sector, addr))
548 farthen 633
        result = self.lib.monitorcommand(struct.pack("<IIQIIII", 28, volume, sector, count, addr, 0, 0), "III", ("rc", None, None))
518 farthen 634
        self.logger.debug("Read sectors, result: 0x%X\n" % result.rc)
802 theseven 635
        if result.rc > 0x80000000:
636
            raise DeviceError("storage_read_sectors_md(volume=%d, sector=%d, count=%d, addr=0x%08X) failed with RC 0x%08X" % (volume, sector, count, addr, result.rc))
479 theseven 637
 
638
    @command(timeout = 50000)
639
    def storage_write_sectors_md(self, volume, sector, count, addr):
640
        """ Read sectors from as storage device """
518 farthen 641
        self.logger.debug("Writing %d sectors from memory at 0x%X to disk at volume %d, sector %d\n" % (count, addr, volume, sector))
548 farthen 642
        result = self.lib.monitorcommand(struct.pack("<IIQIIII", 29, volume, sector, count, addr, 0, 0), "III", ("rc", None, None))
518 farthen 643
        self.logger.debug("Wrote sectors, result: 0x%X\n" % result.rc)
802 theseven 644
        if result.rc > 0x80000000:
645
            raise DeviceError("storage_write_sectors_md(volume=%d, sector=%d, count=%d, addr=0x%08X) failed with RC 0x%08X" % (volume, sector, count, addr, result.rc))
394 farthen 646
 
395 farthen 647
    @command(timeout = 30000)
479 theseven 648
    def fat_enable_flushing(self, state):
649
        """ Enables/disables flushing the FAT cache after every transaction """
484 theseven 650
        if state != 0: self.logger.debug("Enabling FAT flushing\n")
651
        else: self.logger.debug("Disabling FAT flushing\n")
548 farthen 652
        self.lib.monitorcommand(struct.pack("<IIII", 58, state, 0, 0), "III", (None, None, None))
484 theseven 653
        if state != 0: self.logger.debug("Enabled FAT flushing\n")
654
        else: self.logger.debug("Disabled FAT flushing\n")
517 farthen 655
 
479 theseven 656
    @command(timeout = 30000)
346 theseven 657
    def file_open(self, filename, mode):
658
        """ Opens a file and returns the handle """
801 theseven 659
        fn = filename.encode("utf_8")
487 theseven 660
        self.logger.debug("Opening remote file %s with mode %d\n" % (filename, mode))
891 theseven 661
        bytes = len(fn) + 1
662
        if bytes > 48:
663
            buf = self.malloc(bytes)
664
            try:
665
                self._writemem(buf, fn + b"\0")
666
                result = self.lib.monitorcommand(struct.pack("<IIII", 30, mode, 0, buf), "III", ("fd", None, None))
667
            finally: self.free(buf)
668
        else: result = self.lib.monitorcommand(struct.pack("<IIII%dsB" % len(fn), 30, mode, 0, 0, fn, 0), "III", ("fd", None, None))
346 theseven 669
        if result.fd > 0x80000000:
670
            raise DeviceError("file_open(filename=\"%s\", mode=0x%X) failed with RC=0x%08X, errno=%d" % (filename, mode, result.fd, self.errno()))
518 farthen 671
        self.logger.debug("Opened file as handle 0x%X\n" % result.fd)
346 theseven 672
        return result.fd
673
 
395 farthen 674
    @command(timeout = 30000)
346 theseven 675
    def file_size(self, fd):
676
        """ Gets the size of a file referenced by a handle """
518 farthen 677
        self.logger.debug("Getting file size of handle 0x%X\n" % fd)
548 farthen 678
        result = self.lib.monitorcommand(struct.pack("<IIII", 31, fd, 0, 0), "III", ("size", None, None))
346 theseven 679
        if result.size > 0x80000000:
680
            raise DeviceError("file_size(fd=%d) failed with RC=0x%08X, errno=%d" % (fd, result.size, self.errno()))
484 theseven 681
        self.logger.debug("Got file size: %d bytes\n" % result.size)
346 theseven 682
        return result.size
394 farthen 683
 
395 farthen 684
    @command(timeout = 30000)
480 theseven 685
    def file_read(self, fd, size, addr = None):
472 farthen 686
        """ Reads data from a file referenced by a handle. If addr is not given it allocates a buffer itself. """
687
        if addr is None:
688
            addr = self.malloc(size)
689
            malloc = True
690
        else:
691
            malloc = False
518 farthen 692
        self.logger.debug("Reading %d bytes from file handle 0x%X to 0x%X\n" % (size, fd, addr))
472 farthen 693
        try:
548 farthen 694
            result = self.lib.monitorcommand(struct.pack("<IIII", 32, fd, addr, size), "III", ("rc", None, None))
479 theseven 695
            if result.rc > 0x80000000:
696
                raise DeviceError("file_read(fd=%d, addr=0x%08X, size=0x%08X) failed with RC=0x%08X, errno=%d" % (fd, addr, size, result.rc, self.errno()))
697
        except:
472 farthen 698
            if malloc == True:
699
                self.free(addr)
479 theseven 700
            raise
518 farthen 701
        self.logger.debug("File read result: 0x%X\n" % result.rc)
479 theseven 702
        return Bunch(rc = result.rc, addr = addr)
394 farthen 703
 
395 farthen 704
    @command(timeout = 30000)
479 theseven 705
    def file_write(self, fd, size, addr):
706
        """ Writes data from a file referenced by a handle. """
518 farthen 707
        self.logger.debug("Writing %d bytes from 0x%X to file handle 0x%X\n" % (size, addr, fd))
548 farthen 708
        result = self.lib.monitorcommand(struct.pack("<IIII", 33, fd, addr, size), "III", ("rc", None, None))
346 theseven 709
        if result.rc > 0x80000000:
710
            raise DeviceError("file_write(fd=%d, addr=0x%08X, size=0x%08X) failed with RC=0x%08X, errno=%d" % (fd, addr, size, result.rc, self.errno()))
518 farthen 711
        self.logger.debug("File write result: 0x%X\n" % result.rc)
346 theseven 712
        return result.rc
713
 
395 farthen 714
    @command(timeout = 30000)
346 theseven 715
    def file_seek(self, fd, offset, whence):
716
        """ Seeks the file handle to the specified position in the file """
518 farthen 717
        self.logger.debug("Seeking file handle 0x%X to whence=%d, offset=0x%X\n" % (fd, whence, offset))
548 farthen 718
        result = self.lib.monitorcommand(struct.pack("<IIII", 34, fd, offset, whence), "III", ("rc", None, None))
346 theseven 719
        if result.rc > 0x80000000:
720
            raise DeviceError("file_seek(fd=%d, offset=0x%08X, whence=%d) failed with RC=0x%08X, errno=%d" % (fd, offset, whence, result.rc, self.errno()))
518 farthen 721
        self.logger.debug("File seek result: 0x%X\n" % (result.rc))
346 theseven 722
        return result.rc
723
 
395 farthen 724
    @command(timeout = 30000)
346 theseven 725
    def file_truncate(self, fd, length):
726
        """ Truncates a file referenced by a handle to a specified length """
518 farthen 727
        self.logger.debug("Truncating file with handle 0x%X to 0x%X bytes\n" % (fd, length))
769 user890104 728
        result = self.lib.monitorcommand(struct.pack("<IIII", 35, fd, length, 0), "III", ("rc", None, None))
346 theseven 729
        if result.rc > 0x80000000:
730
            raise DeviceError("file_truncate(fd=%d, length=0x%08X) failed with RC=0x%08X, errno=%d" % (fd, length, result.rc, self.errno()))
518 farthen 731
        self.logger.debug("File truncate result: 0x%X\n" % (result.rc))
346 theseven 732
        return result.rc
733
 
395 farthen 734
    @command(timeout = 30000)
346 theseven 735
    def file_sync(self, fd):
736
        """ Flushes a file handles' buffers """
518 farthen 737
        self.logger.debug("Flushing buffers of file with handle 0x%X\n" % (fd))
548 farthen 738
        result = self.lib.monitorcommand(struct.pack("<IIII", 36, fd, 0, 0), "III", ("rc", None, None))
346 theseven 739
        if result.rc > 0x80000000:
740
            raise DeviceError("file_sync(fd=%d) failed with RC=0x%08X, errno=%d" % (fd, result.rc, self.errno()))
518 farthen 741
        self.logger.debug("File flush result: 0x%X\n" % (result.rc))
346 theseven 742
        return result.rc
743
 
395 farthen 744
    @command(timeout = 30000)
346 theseven 745
    def file_close(self, fd):
746
        """ Closes a file handle """
518 farthen 747
        self.logger.debug("Closing file handle 0x%X\n" % (fd))
548 farthen 748
        result = self.lib.monitorcommand(struct.pack("<IIII", 37, fd, 0, 0), "III", ("rc", None, None))
346 theseven 749
        if result.rc > 0x80000000:
750
            raise DeviceError("file_close(fd=%d) failed with RC=0x%08X, errno=%d" % (fd, result.rc, self.errno()))
518 farthen 751
        self.logger.debug("File close result: 0x%X\n" % (result.rc))
346 theseven 752
        return result.rc
753
 
395 farthen 754
    @command(timeout = 30000)
346 theseven 755
    def file_close_all(self):
756
        """ Closes all file handles opened through the debugger """
484 theseven 757
        self.logger.debug("Closing all files that were opened via USB\n")
548 farthen 758
        result = self.lib.monitorcommand(struct.pack("<IIII", 38, 0, 0, 0), "III", ("rc", None, None))
346 theseven 759
        if result.rc > 0x80000000:
760
            raise DeviceError("file_close_all() failed with RC=0x%08X, errno=%d" % (result.rc, self.errno()))
484 theseven 761
        self.logger.debug("Closed %d files\n" % (result.rc))
346 theseven 762
        return result.rc
763
 
395 farthen 764
    @command(timeout = 30000)
484 theseven 765
    def file_kill_all(self, volume):
766
        """ Kills all file handles of a volume (in the whole system) """
767
        self.logger.debug("Killing all file handles of volume %d\n" % (volume))
548 farthen 768
        result = self.lib.monitorcommand(struct.pack("<IIII", 39, volume, 0, 0), "III", ("rc", None, None))
346 theseven 769
        if result.rc > 0x80000000:
770
            raise DeviceError("file_kill_all() failed with RC=0x%08X, errno=%d" % (result.rc, self.errno()))
484 theseven 771
        self.logger.debug("Closed %d files\n" % (result.rc))
346 theseven 772
        return result.rc
773
 
395 farthen 774
    @command(timeout = 30000)
346 theseven 775
    def file_unlink(self, filename):
776
        """ Removes a file """
801 theseven 777
        fn = filename.encode("utf_8")
484 theseven 778
        self.logger.debug("Deleting file %s\n" % (filename))
891 theseven 779
        bytes = len(fn)
780
        if bytes > 48:
781
            buf = self.malloc(bytes)
782
            try:
783
                self._writemem(buf, fn + b"\0")
784
                result = self.lib.monitorcommand(struct.pack("<IIII", 40, 0, 0, buf), "III", ("rc", None, None))
785
            finally: self.free(buf)
786
        else: result = self.lib.monitorcommand(struct.pack("<IIII%dsB" % len(fn), 40, 0, 0, 0, fn, 0), "III", ("rc", None, None))
346 theseven 787
        if result.rc > 0x80000000:
788
            raise DeviceError("file_unlink(filename=\"%s\") failed with RC=0x%08X, errno=%d" % (filename, result.rc, self.errno()))
518 farthen 789
        self.logger.debug("Delete file result: 0x%X\n" % (result.rc))
346 theseven 790
        return result.rc
791
 
395 farthen 792
    @command(timeout = 30000)
346 theseven 793
    def file_rename(self, oldname, newname):
794
        """ Renames a file """
801 theseven 795
        on = oldname.encode("utf_8")
796
        nn = newname.encode("utf_8")
797
        self.logger.debug("Renaming file %s to %s\n" % (on, nn))
891 theseven 798
        obytes = len(on) + 1
799
        nbytes = len(nn) + 1
800
        buf = self.malloc(obytes + nbytes)
801
        try:
802
            self._writemem(buf, on + b"\0" + nn + b"\0")
803
            result = self.lib.monitorcommand(struct.pack("<IIII", 41, 0, buf, buf + obytes), "III", ("rc", None, None))
804
        finally: self.free(buf)
346 theseven 805
        if result.rc > 0x80000000:
806
            raise DeviceError("file_rename(oldname=\"%s\", newname=\"%s\") failed with RC=0x%08X, errno=%d" % (oldname, newname, result.rc, self.errno()))
518 farthen 807
        self.logger.debug("Rename file result: 0x%X\n" % (result.rc))
346 theseven 808
        return result.rc
809
 
395 farthen 810
    @command(timeout = 30000)
346 theseven 811
    def dir_open(self, dirname):
812
        """ Opens a directory and returns the handle """
801 theseven 813
        dn = dirname.encode("utf_8")
484 theseven 814
        self.logger.debug("Opening directory %s\n" % (dirname))
891 theseven 815
        bytes = len(dn)
816
        if bytes > 48:
817
            buf = self.malloc(bytes)
818
            try:
819
                self._writemem(buf, dn + b"\0")
820
                result = self.lib.monitorcommand(struct.pack("<IIII", 42, 0, 0, buf), "III", ("handle", None, None))
821
            finally: self.free(buf)
822
        else: result = self.lib.monitorcommand(struct.pack("<IIII%dsB" % len(dn), 42, 0, 0, 0, dn, 0), "III", ("handle", None, None))
346 theseven 823
        if result.handle == 0:
824
            raise DeviceError("dir_open(dirname=\"%s\") failed with RC=0x%08X, errno=%d" % (dirname, result.handle, self.errno()))
518 farthen 825
        self.logger.debug("Opened directory as handle 0x%X\n" % (result.handle))
346 theseven 826
        return result.handle
827
 
395 farthen 828
    @command(timeout = 30000)
346 theseven 829
    def dir_read(self, handle):
830
        """ Reads the next entry from a directory """
518 farthen 831
        self.logger.debug("Reading next entry of directory handle 0x%X\n" % (handle))
548 farthen 832
        result = self.lib.monitorcommand(struct.pack("<IIII", 43, handle, 0, 0), "III", ("version", "maxpath", "ptr"))
346 theseven 833
        if result.ptr == 0:
834
            raise DeviceError("dir_read(handle=0x%08X) failed with RC=0x%08X, errno=%d" % (handle, result.ptr, self.errno()))
835
        if result.version != 1:
836
            raise ValueError("Unknown version of dirent struct: %d" % result.version)
837
        dirent = self.read(result.ptr, result.maxpath + 16)
838
        ret = Bunch()
548 farthen 839
        (ret.name, ret.attributes, ret.size, ret.startcluster, ret.wrtdate, ret.wrttime) = struct.unpack("<%dsIIIHH" % result.maxpath, dirent)
807 theseven 840
        ret.name = ret.name[:ret.name.index(b"\0")].decode("utf_8")
484 theseven 841
        self.logger.debug("Read directory entry:\n")
842
        self.logger.debug("Name: %s\n" % ret.name)
518 farthen 843
        self.logger.debug("Attributes: 0x%X\n" % ret.attributes)
484 theseven 844
        self.logger.debug("Size: %d\n" % ret.size)
845
        self.logger.debug("Start cluster: %d\n" % ret.startcluster)
518 farthen 846
        self.logger.debug("Last written date: 0x%X\n" % ret.wrtdate)
847
        self.logger.debug("Last written time: 0x%X\n" % ret.wrttime)
346 theseven 848
        return ret
849
 
395 farthen 850
    @command(timeout = 30000)
346 theseven 851
    def dir_close(self, handle):
852
        """ Closes a directory handle """
518 farthen 853
        self.logger.debug("Closing directory handle 0x%X\n" % (handle))
548 farthen 854
        result = self.lib.monitorcommand(struct.pack("<IIII", 44, handle, 0, 0), "III", ("rc", None, None))
346 theseven 855
        if result.rc > 0x80000000:
856
            raise DeviceError("dir_close(handle=0x%08X) failed with RC=0x%08X, errno=%d" % (handle, result.rc, self.errno()))
518 farthen 857
        self.logger.debug("Close directory result: 0x%X\n" % (result.rc))
346 theseven 858
        return result.rc
859
 
395 farthen 860
    @command(timeout = 30000)
346 theseven 861
    def dir_close_all(self):
862
        """ Closes all directory handles opened through the debugger """
484 theseven 863
        self.logger.debug("Closing all directories that were opened via USB\n")
548 farthen 864
        result = self.lib.monitorcommand(struct.pack("<IIII", 45, 0, 0, 0), "III", ("rc", None, None))
346 theseven 865
        if result.rc > 0x80000000:
866
            raise DeviceError("dir_close_all() failed with RC=0x%08X, errno=%d" % (result.rc, self.errno()))
484 theseven 867
        self.logger.debug("Closed %d directories\n" % (result.rc))
346 theseven 868
        return result.rc
869
 
395 farthen 870
    @command(timeout = 30000)
484 theseven 871
    def dir_kill_all(self, volume):
872
        """ Kills all directory handles of a volume (in the whole system) """
873
        self.logger.debug("Closing all directories of volume %d\n" % (volume))
548 farthen 874
        result = self.lib.monitorcommand(struct.pack("<IIII", 46, volume, 0, 0), "III", ("rc", None, None))
346 theseven 875
        if result.rc > 0x80000000:
876
            raise DeviceError("dir_kill_all() failed with RC=0x%08X, errno=%d" % (result.rc, self.errno()))
484 theseven 877
        self.logger.debug("Closed %d directories\n" % (result.rc))
346 theseven 878
        return result.rc
879
 
395 farthen 880
    @command(timeout = 30000)
346 theseven 881
    def dir_create(self, dirname):
882
        """ Creates a directory """
801 theseven 883
        dn = dirname.encode("utf_8")
484 theseven 884
        self.logger.debug("Creating directory %s\n" % (dirname))
891 theseven 885
        bytes = len(dn)
886
        if bytes > 48:
887
            buf = self.malloc(bytes)
888
            try:
889
                self._writemem(buf, dn + b"\0")
890
                result = self.lib.monitorcommand(struct.pack("<IIII", 47, 0, 0, buf), "III", ("rc", None, None))
891
            finally: self.free(buf)
892
        else: result = self.lib.monitorcommand(struct.pack("<IIII%dsB" % len(dn), 47, 0, 0, 0, dn, 0), "III", ("rc", None, None))
346 theseven 893
        if result.rc > 0x80000000:
894
            raise DeviceError("dir_create(dirname=\"%s\") failed with RC=0x%08X, errno=%d" % (dirname, result.rc, self.errno()))
518 farthen 895
        self.logger.debug("Create directory result: 0x%X\n" % (result.rc))
346 theseven 896
        return result.rc
897
 
395 farthen 898
    @command(timeout = 30000)
346 theseven 899
    def dir_remove(self, dirname):
900
        """ Removes an (empty) directory """
801 theseven 901
        dn = dirname.encode("utf_8")
484 theseven 902
        self.logger.debug("Removing directory %s\n" % (dirname))
891 theseven 903
        bytes = len(dn)
904
        if bytes > 48:
905
            buf = self.malloc(bytes)
906
            try:
907
                self._writemem(buf, dn + b"\0")
908
                result = self.lib.monitorcommand(struct.pack("<IIII", 48, 0, 0, buf), "III", ("rc", None, None))
909
            finally: self.free(buf)
910
        else: result = self.lib.monitorcommand(struct.pack("<IIII%dsB" % len(dn), 48, 0, 0, 0, dn, 0), "III", ("rc", None, None))
346 theseven 911
        if result.rc > 0x80000000:
912
            raise DeviceError("dir_remove(dirname=\"%s\") failed with RC=0x%08X, errno=%d" % (dirname, result.rc, self.errno()))
518 farthen 913
        self.logger.debug("Remove directory result: 0x%X\n" % (result.rc))
346 theseven 914
        return result.rc
915
 
394 farthen 916
    @command()
346 theseven 917
    def errno(self):
918
        """ Returns the number of the last error that happened """
484 theseven 919
        self.logger.debug("Getting last error number\n")
548 farthen 920
        result = self.lib.monitorcommand(struct.pack("<IIII", 49, 0, 0, 0), "III", ("errno", None, None))
518 farthen 921
        self.logger.debug("Last error: 0x%X\n" % (result.errno))
346 theseven 922
        return result.errno
923
 
394 farthen 924
    @command()
346 theseven 925
    def disk_mount(self, volume):
926
        """ Mounts a volume """
484 theseven 927
        self.logger.debug("Mounting volume %d\n" % (volume))
548 farthen 928
        result = self.lib.monitorcommand(struct.pack("<IIII", 50, volume, 0, 0), "III", ("rc", None, None))
346 theseven 929
        if result.rc > 0x80000000:
930
            raise DeviceError("disk_mount(volume=%d) failed with RC=0x%08X, errno=%d" % (volume, result.rc, self.errno()))
518 farthen 931
        self.logger.debug("Mount volume result: 0x%X\n" % (result.rc))
346 theseven 932
        return result.rc
933
 
394 farthen 934
    @command()
346 theseven 935
    def disk_unmount(self, volume):
936
        """ Unmounts a volume """
484 theseven 937
        self.logger.debug("Unmounting volume %d\n" % (volume))
548 farthen 938
        result = self.lib.monitorcommand(struct.pack("<IIII", 51, volume, 0, 0), "III", ("rc", None, None))
346 theseven 939
        if result.rc > 0x80000000:
940
            raise DeviceError("disk_unmount(volume=%d) failed with RC=0x%08X, errno=%d" % (volume, result.rc, self.errno()))
518 farthen 941
        self.logger.debug("Unmount volume result: 0x%X\n" % (result.rc))
346 theseven 942
        return result.rc
943
 
441 farthen 944
    @command()
945
    def malloc(self, size):
946
        """ Allocates 'size' bytes and returns a pointer to the allocated memory """
442 farthen 947
        self.logger.debug("Allocating %d bytes of memory\n" % size)
548 farthen 948
        result = self.lib.monitorcommand(struct.pack("<IIII", 52, size, 0, 0), "III", ("ptr", None, None))
518 farthen 949
        self.logger.debug("Allocated %d bytes of memory at 0x%X\n" % (size, result.ptr))
441 farthen 950
        return result.ptr
951
 
952
    @command()
953
    def memalign(self, align, size):
954
        """ Allocates 'size' bytes aligned to 'align' and returns a pointer to the allocated memory """
518 farthen 955
        self.logger.debug("Allocating %d bytes of memory aligned to 0x%X\n" % (size, align))
548 farthen 956
        result = self.lib.monitorcommand(struct.pack("<IIII", 53, align, size, 0), "III", ("ptr", None, None))
518 farthen 957
        self.logger.debug("Allocated %d bytes of memory at 0x%X\n" % (size, result.ptr))
441 farthen 958
        return result.ptr
959
 
960
    @command()
961
    def realloc(self, ptr, size):
962
        """ The size of the memory block pointed to by 'ptr' is changed to the 'size' bytes,
963
            expanding or reducing the amount of memory available in the block.
964
            Returns a pointer to the reallocated memory.
965
        """
518 farthen 966
        self.logger.debug("Reallocating 0x%X to have the new size %d\n" % (ptr, size))
548 farthen 967
        result = self.lib.monitorcommand(struct.pack("<IIII", 54, ptr, size, 0), "III", ("ptr", None, None))
518 farthen 968
        self.logger.debug("Reallocated memory at 0x%X to 0x%X with the new size %d\n" % (ptr, result.ptr, size))
441 farthen 969
        return result.ptr
970
 
971
    @command()
972
    def reownalloc(self, ptr, owner):
973
        """ Changes the owner of the memory allocation 'ptr' to the thread struct at addr 'owner' """
518 farthen 974
        self.logger.debug("Changing owner of the memory region 0x%X to 0x%X\n" % (ptr, owner))
548 farthen 975
        return self.lib.monitorcommand(struct.pack("<IIII", 55, ptr, owner, 0), "III", (None, None, None))
441 farthen 976
 
977
    @command()
978
    def free(self, ptr):
979
        """ Frees the memory space pointed to by 'ptr' """
518 farthen 980
        self.logger.debug("Freeing the memory region at 0x%X\n" % ptr)
548 farthen 981
        return self.lib.monitorcommand(struct.pack("<IIII", 56, ptr, 0, 0), "III", (None, None, None))
441 farthen 982
 
473 farthen 983
    @command()
984
    def free_all(self):
985
        """ Frees all memory allocations created by the monitor thread """
986
        self.logger.debug("Freeing all memory allocations created by the monitor thread\n")
548 farthen 987
        return self.lib.monitorcommand(struct.pack("<IIII", 57, 0, 0, 0), "III", (None, None, None))
778 farthen 988
 
989
    @command()
990
    def rtcread(self):
991
        """ Reads the real time clock on the device """
992
        self.logger.debug("Reading the clock\n")
993
        date = self.lib.monitorcommand(struct.pack("<IIII", 60, 0, 0, 0), "BBBBBBBBI", ("second", "minute", "hour", "weekday", "day", "month", "year", None, None))
994
        dt = datetime.datetime(date.year + 2000, date.month, date.day, date.hour, date.minute, date.second)
995
        self.logger.debug("Read date '%s' from device", (dt.ctime()))
996
        return dt
997
 
998
    @command()
999
    def rtcwrite(self, dt):
1000
        """ Sets the real time clock on the device to the datetime object 'dt' """
1001
        self.logger.debug("Setting the clock to: %s\n" % (dt.ctime()))
1002
        if dt.year < 2000 or dt.year > 2255:
1003
            raise ArgumentError("The Year must be between 2000 and 2255")
1004
        return self.lib.monitorcommand(struct.pack("<IBBBBBBBBI", 61, dt.second, dt.minute, dt.hour, dt.weekday(), dt.day, dt.month, dt.year - 2000, 0, 0), "III", (None, None, None))
346 theseven 1005
 
517 farthen 1006
 
171 farthen 1007
class Lib(object):
401 farthen 1008
    def __init__(self, logger):
1009
        self.logger = logger
1010
        self.logger.debug("Initializing Lib object\n")
171 farthen 1011
        self.idVendor = 0xFFFF
1012
        self.idProduct = 0xE000
912 user890104 1013
        self.idProductMask = 0xFFFE
176 farthen 1014
 
1015
        self.headersize = 0x10
1016
 
1017
        self.connect()
56 benedikt93 1018
 
171 farthen 1019
    def connect(self):
912 user890104 1020
        self.dev = Dev(self.idVendor, self.idProduct, self.idProductMask, self.logger)
171 farthen 1021
        self.connected = True
56 benedikt93 1022
 
171 farthen 1023
    def monitorcommand(self, cmd, rcvdatatypes=None, rcvstruct=None):
582 farthen 1024
        self.logger.debug("Sending monitorcommand [0x%s]\n" % base64.b16encode(cmd[3::-1]).decode("ascii"))
891 theseven 1025
        writelen = self.dev.send(cmd)
171 farthen 1026
        if rcvdatatypes:
1027
            rcvdatatypes = "I" + rcvdatatypes # add the response
891 theseven 1028
            data = self.dev.receive(struct.calcsize(rcvdatatypes))
171 farthen 1029
            data = struct.unpack(rcvdatatypes, data)
506 farthen 1030
            try:
1031
                response = responsecode(data[0])
1032
            except IndexError:
1033
                self.logger.debug("Response: UNKOWN\n")
1034
                raise DeviceError("Invalid response! This should NOT happen!")
1035
            if response == "OK":
401 farthen 1036
                self.logger.debug("Response: OK\n")
171 farthen 1037
                if rcvstruct:
1038
                    datadict = Bunch()
1039
                    counter = 1 # start with 1, 0 is the id
1040
                    for item in rcvstruct:
1041
                        if item != None: # else the data is undefined
1042
                            datadict[item] = data[counter]
1043
                        counter += 1
1044
                    return datadict
1045
                else:
1046
                    return data
506 farthen 1047
            elif response == "UNSUPPORTED":
401 farthen 1048
                self.logger.debug("Response: UNSUPPORTED\n")
171 farthen 1049
                raise DeviceError("The device does not support this command.")
506 farthen 1050
            elif response == "INVALID":
401 farthen 1051
                self.logger.debug("Response: INVALID\n")
171 farthen 1052
                raise DeviceError("Invalid command! This should NOT happen!")
506 farthen 1053
            elif response == "BUSY":
401 farthen 1054
                self.logger.debug("Response: BUSY\n")
171 farthen 1055
                raise DeviceError("Device busy")
269 farthen 1056
        else:
1057
            return writelen
56 benedikt93 1058
 
1059
 
171 farthen 1060
class Dev(object):
912 user890104 1061
    def __init__(self, idVendor, idProduct, idProductMask, logger):
171 farthen 1062
        self.idVendor = idVendor
1063
        self.idProduct = idProduct
912 user890104 1064
        self.idProductMask = idProductMask
67 benedikt93 1065
 
401 farthen 1066
        self.logger = logger
1067
        self.logger.debug("Initializing Dev object\n")
1068
 
912 user890104 1069
        self.dev = None
891 theseven 1070
        self.interface = None
1071
        self.claimed = False
774 theseven 1072
        self.timeout = 1000
517 farthen 1073
 
171 farthen 1074
        self.connect()
1075
 
401 farthen 1076
        self.logger.debug("Successfully connected to device\n")
342 farthen 1077
 
343 farthen 1078
        self.version = Bunch()
342 farthen 1079
        self.version.revision = None
1080
        self.version.majorv = None
1081
        self.version.minorv = None
1082
        self.version.patchv = None
1083
        self.swtypeid = None
1084
        self.hwtypeid = None
1085
 
949 theseven 1086
        self.havemalloc = False
675 farthen 1087
        self.mallocpool = Bunch()
1088
        self.mallocpool.lower = None
1089
        self.mallocpool.upper = None
56 benedikt93 1090
 
171 farthen 1091
    def __del__(self):
891 theseven 1092
        if self.claimed: self.disconnect()
56 benedikt93 1093
 
171 farthen 1094
    def connect(self):
427 farthen 1095
        self.logger.debug("Looking for emCORE device\n")
912 user890104 1096
        devs = usb.core.find(find_all=True, idVendor=self.idVendor)
1097
        for dev in devs:
922 theseven 1098
            self.logger.debug("%04x:%04x\n" % (dev.idVendor, dev.idProduct))
912 user890104 1099
            if dev.idProduct & self.idProductMask == self.idProduct:
1100
                self.dev = dev
1101
                break
171 farthen 1102
        if self.dev is None:
1103
            raise DeviceNotFoundError()
891 theseven 1104
        self.logger.debug("Device found!\n")
1105
        self.logger.debug("Searching for device interface:\n")
1106
        for cfg in self.dev:
1107
            for intf in cfg:
922 theseven 1108
                self.logger.debug("%02x:%02x:%02x\n" % (intf.bInterfaceClass, intf.bInterfaceSubClass, intf.bInterfaceProtocol))
891 theseven 1109
                if intf.bInterfaceClass == 0xff and intf.bInterfaceSubClass == 0 and intf.bInterfaceProtocol == 0:
1110
                    self.interface = intf.bInterfaceNumber
1111
                    break
1112
        if self.interface is None:
1113
            raise DeviceNotFoundError()
1114
        self.logger.debug("Debugger interface found!\n")
1115
        self.logger.debug("Claiming interface...\n")
1116
        usb.util.claim_interface(self.dev, self.interface)
1117
        self.claimed = True
56 benedikt93 1118
 
171 farthen 1119
    def disconnect(self):
891 theseven 1120
        usb.util.release_interface(self.dev, self.interface)
1121
        self.claimed = False
102 benedikt93 1122
 
891 theseven 1123
    def send(self, data):
1124
        if len(data) > 0x1000: raise DeviceError("Attempting to send a message that is too big!")
1125
        size = self.dev.ctrl_transfer(0x41, 0x00, 0, self.interface, data, self.timeout)
171 farthen 1126
        if size != len(data):
176 farthen 1127
            raise SendError("Not all data was written!")
171 farthen 1128
        return len
102 benedikt93 1129
 
891 theseven 1130
    def receive(self, size):
1131
        if size > 0x1000: raise DeviceError("Attempting to receive a message that is too big!")
1132
        read = self.dev.ctrl_transfer(0xc1, 0x00, 0, self.interface, size, self.timeout)
171 farthen 1133
        if len(read) != size:
176 farthen 1134
            raise ReceiveError("Requested size and read size don't match!")
171 farthen 1135
        return read
56 benedikt93 1136
 
1137
 
171 farthen 1138
if __name__ == "__main__":
396 farthen 1139
    from misc import Logger
1140
    logger = Logger()
1141
 
500 farthen 1142
    if sys.argv[1] == "gendoc":
396 farthen 1143
        # Generates Documentation
1144
        from misc import gendoc
501 farthen 1145
        logger.write("Generating documentation\n")
396 farthen 1146
        cmddict = {}
582 farthen 1147
        for attr, value in Emcore.__dict__.items():
396 farthen 1148
            if getattr(value, 'func', False):
1149
                if getattr(value.func, '_command', False):
1150
                    cmddict[value.func.__name__] = value
501 farthen 1151
        logger.write(gendoc(cmddict))
441 farthen 1152
 
1153
    elif sys.argv[1] == "malloctest":
1154
        emcore = Emcore()
501 farthen 1155
        logger.write("Allocating 200 bytes of memory: ")
441 farthen 1156
        addr = emcore.malloc(200)
518 farthen 1157
        logger.write("0x%X\n" % addr)
501 farthen 1158
        logger.write("Reallocating to 2000 bytes: ")
441 farthen 1159
        addr = emcore.realloc(addr, 2000)
518 farthen 1160
        logger.write("0x%X\n" % addr)
1161
        logger.write("Freeing 0x%X\n" % addr)
441 farthen 1162
        emcore.free(addr)
501 farthen 1163
        logger.write("Allocating 1000 bytes of memory aligned to 100 bytes: ")
441 farthen 1164
        addr = emcore.memalign(100, 1000)
518 farthen 1165
        logger.write("0x%X\n" % addr)
1166
        logger.write("Freeing 0x%X\n" % addr)
704 theseven 1167
        emcore.free(addr)