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