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