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