Subversion Repositories freemyipod

Rev

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