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
#
171 farthen 4
#    Copyright 2010 TheSeven, benedikt93, Farthen
56 benedikt93 5
#
6
#
7
#    This file is part of emBIOS.
8
#
9
#    emBIOS is free software: you can redistribute it and/or
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
#
14
#    emBIOS is distributed in the hope that it will be useful,
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
20
#    along with emBIOS.  If not, see <http://www.gnu.org/licenses/>.
21
#
22
#
23
 
24
import sys
25
import struct
171 farthen 26
import usb.core
27
import libembiosdata
56 benedikt93 28
 
171 farthen 29
class Error(Exception):
30
    def __init__(self, value=None):
31
        self.value = value
32
    def __str__(self):
33
        if self.value != None:
34
            return repr(self.value)
56 benedikt93 35
 
171 farthen 36
class ArgumentError(Error):
37
    pass
56 benedikt93 38
 
171 farthen 39
class DeviceNotFoundError(Error):
40
    pass
56 benedikt93 41
 
171 farthen 42
class DeviceError(Error):
43
    pass
56 benedikt93 44
 
171 farthen 45
class SendError(Error):
46
    pass
56 benedikt93 47
 
171 farthen 48
class ReceiveError(Error):
49
    pass
56 benedikt93 50
 
51
 
171 farthen 52
class Bunch(dict):
56 benedikt93 53
    """
171 farthen 54
        This is a dict whose items can also be accessed with
55
        bunchinstance.something.
56 benedikt93 56
    """
171 farthen 57
    def __init__(self, **kw):
58
        dict.__init__(self, kw)
59
        self.__dict__ = self
56 benedikt93 60
 
171 farthen 61
    def __getstate__(self):
62
        return self
56 benedikt93 63
 
171 farthen 64
    def __setstate__(self, state):
65
        self.update(state)
66
        self.__dict__ = self
56 benedikt93 67
 
68
 
171 farthen 69
class Embios(object):
176 farthen 70
    """
71
        Class for all embios functions.
72
    """
171 farthen 73
    def __init__(self):
176 farthen 74
        self.lib = Lib()
343 farthen 75
 
76
        self.getversioninfo()
176 farthen 77
        self.getpacketsizeinfo()
343 farthen 78
        self.getusermemrange()
56 benedikt93 79
 
171 farthen 80
    @staticmethod
81
    def _alignsplit(addr, size, blksize, align):
177 farthen 82
        if size <= blksize: return (size, 0, 0)
171 farthen 83
        end = addr + size
84
        if addr & (align - 1):
85
            bodyaddr = (addr + min(size, blksize)) & ~(align - 1)
86
        else: bodyaddr = addr
87
        headsize = bodyaddr - addr
88
        if (size - headsize) & (align - 1):
89
            tailaddr = (end - min(end - bodyaddr, blksize) + align - 1) & ~(align - 1)
90
        else: tailaddr = end
91
        tailsize = end - tailaddr
92
        return (headsize, tailaddr - bodyaddr, tailsize)
56 benedikt93 93
 
178 farthen 94
    def _readmem(self, addr, size):
95
        """ Reads the memory from location 'addr' with size 'size'
96
            from the device.
97
        """
98
        resp = self.lib.monitorcommand(struct.pack("IIII", 4, addr, size, 0), "III%ds" % size, (None, None, None, "data"))
99
        return resp.data
100
 
101
    def _writemem(self, addr, data):
102
        """ Writes the data in 'data' to the location 'addr'
103
            in the memory of the device.
104
        """
105
        return self.lib.monitorcommand(struct.pack("IIII%ds" % len(data), 5, addr, len(data), 0, data), "III", (None, None, None))
106
 
107
    def _readdma(self, addr, size):
108
        """ Reads the memory from location 'addr' with size 'size'
109
            from the device. This uses DMA and the data in endpoint.
110
        """
111
        self.lib.monitorcommand(struct.pack("IIII", 6, addr, size, 0), "III", (None, None, None))
112
        return struct.unpack("%ds" % size, self.lib.dev.din(size))[0]
113
 
114
    def _writedma(self, addr, data):
115
        """ Writes the data in 'data' to the location 'addr'
116
            in the memory of the device. This uses DMA and the data out endpoint.
117
        """
118
        self.lib.monitorcommand(struct.pack("IIII", 7, addr, len(data), 0), "III", (None, None, None))
119
        return self.lib.dev.dout(data)
120
 
171 farthen 121
    def getversioninfo(self):
122
        """ This returns the emBIOS version and device information. """
342 farthen 123
        resp = self.lib.monitorcommand(struct.pack("IIII", 1, 0, 0, 0), "IBBBBI", ("revision", "majorv", "minorv", "patchv", "swtypeid", "hwtypeid"))
124
        self.lib.dev.version.revision = resp.revision
125
        self.lib.dev.version.majorv = resp.majorv
126
        self.lib.dev.version.minorv = resp.minorv
127
        self.lib.dev.version.patchv = resp.patchv
128
        self.lib.dev.swtypeid = resp.swtypeid
129
        self.lib.dev.hwtypeid = resp.hwtypeid
130
        return resp
56 benedikt93 131
 
171 farthen 132
    def getpacketsizeinfo(self):
133
        """ This returns the emBIOS max packet size information.
134
            It also sets the properties of the device object accordingly.
135
        """
136
        resp = self.lib.monitorcommand(struct.pack("IIII", 1, 1, 0, 0), "HHII", ("coutmax", "cinmax", "doutmax", "dinmax"))
343 farthen 137
        self.lib.dev.packetsizelimit.cout = resp.coutmax
138
        self.lib.dev.packetsizelimit.cin = resp.cinmax
139
        self.lib.dev.packetsizelimit.din = resp.dinmax
140
        self.lib.dev.packetsizelimit.dout = resp.doutmax
171 farthen 141
        return resp
56 benedikt93 142
 
171 farthen 143
    def getusermemrange(self):
144
        """ This returns the memory range the user has access to. """
342 farthen 145
        resp = self.lib.monitorcommand(struct.pack("IIII", 1, 2, 0, 0), "III", ("lower", "upper", None))
146
        self.lib.dev.usermem.lower = resp.lower
147
        self.lib.dev.usermem.upper = resp.upper
148
        return resp
56 benedikt93 149
 
171 farthen 150
    def reset(self, force=False):
151
        """ Reboot the device """
152
        if force:
153
            return self.lib.monitorcommand(struct.pack("IIII", 2, 0, 0, 0))
154
        else:
155
            return self.lib.monitorcommand(struct.pack("IIII", 2, 1, 0, 0), "III", (None, None, None))
56 benedikt93 156
 
171 farthen 157
    def poweroff(self, force=False):
158
        """ Powers the device off. """
159
        if force:
160
            return self.lib.monitorcommand(struct.pack("IIII", 3, 0, 0, 0))
161
        else:
162
            return self.lib.monitorcommand(struct.pack("IIII", 3, 1, 0, 0), "III", (None, None, None))
56 benedikt93 163
 
171 farthen 164
    def read(self, addr, size):
165
        """ Reads the memory from location 'addr' with size 'size'
166
            from the device. This cares about too long packages
167
            and decides whether to use DMA or not.
168
        """
343 farthen 169
        cin_maxsize = self.lib.dev.packetsizelimit.cin - self.lib.headersize
170
        din_maxsize = self.lib.dev.packetsizelimit.din
171 farthen 171
        data = ""
172
        (headsize, bodysize, tailsize) = self._alignsplit(addr, size, cin_maxsize, 16)
173
        if headsize != 0:
178 farthen 174
            data += self._readmem(addr, headsize)
171 farthen 175
            addr += headsize
176
        while bodysize > 0:
177
            if bodysize >= 2 * cin_maxsize:
178
                readsize = min(bodysize, din_maxsize)
178 farthen 179
                data += self._readdma(addr, readsize)
171 farthen 180
            else:
181
                readsize = min(bodysize, cin_maxsize)
178 farthen 182
                data += self._readmem(addr, readsize)
171 farthen 183
            addr += readsize
184
            bodysize -= readsize
185
        if tailsize != 0:
178 farthen 186
            data += self._readmem(addr, tailsize)
171 farthen 187
        return data
56 benedikt93 188
 
171 farthen 189
    def write(self, addr, data):
190
        """ Writes the data in 'data' to the location 'addr'
191
            in the memory of the device. This cares about too long packages
192
            and decides whether to use DMA or not.
193
        """
343 farthen 194
        cout_maxsize = self.lib.dev.packetsizelimit.cout - self.lib.headersize
195
        dout_maxsize = self.lib.dev.packetsizelimit.dout
171 farthen 196
        (headsize, bodysize, tailsize) = self._alignsplit(addr, len(data), cout_maxsize, 16)
197
        offset = 0
198
        if headsize != 0:
178 farthen 199
            self._writemem(addr, data[offset:offset+headsize])
171 farthen 200
            offset += headsize
201
            addr += headsize
202
        while bodysize > 0:
203
            if bodysize >= 2 * cout_maxsize:
204
                writesize = min(bodysize, dout_maxsize)
178 farthen 205
                self._writedma(addr, data[offset:offset+writesize])
171 farthen 206
            else:
207
                writesize = min(bodysize, cout_maxsize)
178 farthen 208
                self._writemem(addr, data[offset:offset+writesize])
171 farthen 209
            offset += writesize
210
            addr += writesize
211
            bodysize -= writesize
212
        if tailsize != 0:
178 farthen 213
            self._writemem(addr, data[offset:offset+tailsize])
171 farthen 214
        return data
56 benedikt93 215
 
173 farthen 216
    def readstring(self, addr, maxlength = 256):
217
        """ Reads a zero terminated string from memory 
218
            Reads only a maximum of 'maxlength' chars.
219
        """
343 farthen 220
        cin_maxsize = self.lib.dev.packetsizelimit.cin - self.lib.headersize
173 farthen 221
        string = ""
222
        while (len(string) < maxlength or maxlength < 0):
178 farthen 223
            data = self._readmem(addr, min(maxlength - len(string), cin_maxsize))
173 farthen 224
            length = data.find("\0")
225
            if length >= 0:
226
                string += data[:length]
227
                break
228
            else:
229
                string += data
230
            addr += cin_maxsize
231
        return string
232
 
171 farthen 233
    def i2cread(self, index, slaveaddr, startaddr, size):
234
        """ Reads data from an i2c slave """
236 farthen 235
        data = ""
236
        for i in range(size):
237
            resp = self.lib.monitorcommand(struct.pack("IBBBBII", 8, index, slaveaddr, startaddr + i, 1, 0, 0), "III1s", (None, None, None, "data"))
238
            data += resp.data
239
        return data
56 benedikt93 240
 
171 farthen 241
    def i2cwrite(self, index, slaveaddr, startaddr, data):
242
        """ Writes data to an i2c slave """
176 farthen 243
        size = len(data)
244
        if size > 256 or size < 1:
341 farthen 245
            raise ArgumentError("Size must be a number between 1 and 256")
176 farthen 246
        if size == 256:
247
            size = 0
215 theseven 248
        return self.lib.monitorcommand(struct.pack("IBBBBII%ds" % size, 9, index, slaveaddr, startaddr, size, 0, 0, data), "III", (None, None, None))
56 benedikt93 249
 
176 farthen 250
    def usbcread(self):
251
        """ Reads one packet with the maximal cin size """
343 farthen 252
        cin_maxsize = self.lib.dev.packetsizelimit.cin - self.lib.headersize
176 farthen 253
        resp = self.lib.monitorcommand(struct.pack("IIII", 10, cin_maxsize, 0, 0), "III%ds" % cin_maxsize, ("validsize", "buffersize", "queuesize", "data"))
254
        resp.data = resp.data[:resp.validsize]
255
        resp.maxsize = cin_maxsize
256
        return resp
56 benedikt93 257
 
171 farthen 258
    def usbcwrite(self, data):
259
        """ Writes data to the USB console """
343 farthen 260
        cin_maxsize = self.lib.dev.packetsizelimit.cin - self.lib.headersize
176 farthen 261
        size = len(data)
262
        while len(data) > 0:
263
            writesize = min(cin_maxsize, len(data))
264
            resp = self.lib.monitorcommand(struct.pack("IIII%ds" % writesize, 11, writesize, 0, 0, data[:writesize]), "III", ("validsize", "buffersize", "freesize"))
265
            data = data[resp.validsize:]
266
        return size
56 benedikt93 267
 
176 farthen 268
    def cread(self, bitmask=0x1):
269
        """ Reads one packet with the maximal cin size from the device consoles
171 farthen 270
            identified with the specified bitmask
271
        """
343 farthen 272
        cin_maxsize = self.lib.dev.packetsizelimit.cin - self.lib.headersize
217 theseven 273
        resp = self.lib.monitorcommand(struct.pack("IIII", 13, bitmask, cin_maxsize, 0), "III%ds" % cin_maxsize, ("size", None, None))
176 farthen 274
        resp.data = resp.data[size:]
275
        resp.maxsize = cin_maxsize
276
        return resp
56 benedikt93 277
 
176 farthen 278
    def cwrite(self, data, bitmask=0x1):
171 farthen 279
        """ Writes data to the device consoles 
280
            identified with the specified bitmask.
281
        """
343 farthen 282
        cin_maxsize = self.lib.dev.packetsizelimit.cin - self.lib.headersize
176 farthen 283
        size = len(data)
284
        while len(data) > 0:
285
            writesize = min(cin_maxsize, len(data))
217 theseven 286
            resp = self.lib.monitorcommand(struct.pack("IIII%ds" % writesize, 12, bitmask, writesize, 0, data[:writesize]), "III", (None, None, None))
176 farthen 287
            data = data[writesize:]
288
        return size
56 benedikt93 289
 
171 farthen 290
    def cflush(self, bitmask):
291
        """ Flushes the consoles specified with 'bitmask' """
292
        return self.lib.monitorcommand(struct.pack("IIII", 14, bitmask, 0, 0), "III", (None, None, None))
56 benedikt93 293
 
173 farthen 294
    def getprocinfo(self):
171 farthen 295
        """ Gets current state of the scheduler """
343 farthen 296
        cin_maxsize = self.lib.dev.packetsizelimit.cin - self.lib.headersize
173 farthen 297
        # Get the size
298
        schedulerstate = self.lockscheduler()
299
        resp = self.lib.monitorcommand(struct.pack("IIII", 15, 0, 0, 0), "III", ("structver", "tablesize", None))
300
        tablesize = resp.tablesize
301
        size = tablesize
302
        structver = resp.structver
303
        offset = 0
304
        data = ""
305
        while size > 0:
306
            if size > cin_maxsize:
307
                readsize = cin_maxsize
308
            else:
309
                readsize = size
310
            resp = self.lib.monitorcommand(struct.pack("IIII", 15, offset, readsize, 0), "III%ds" % readsize, ("structver", "tablesize", None, "data"))
311
            data += resp.data
312
            offset += readsize
313
            size -= readsize
314
        self.lockscheduler(schedulerstate)
315
        threadstructsize = 120
316
        registersize = 32
317
        if len(data) % threadstructsize != 0:
318
            raise DeviceError("The thread struct is not a multiple of "+str(threadsturcsize)+"!")
319
        threadcount = len(data) / threadstructsize
320
        threads = []
321
        id = 0
322
        for thread in range(threadcount):
323
            offset = threadstructsize * thread
324
            threaddata = struct.unpack("<16IIIIIQIIIIIIIBBBB", data[offset:offset+threadstructsize])
325
            info = Bunch()
326
            info.id = id
327
            state = threaddata[17]
328
            info.state = libembiosdata.thread_state[state]
329
            if info.state == "THREAD_FREE":
330
                id += 1
331
                continue
332
            info.regs = Bunch()
333
            for register in range(16):
334
                info.regs["r"+str(register)] = threaddata[register]
335
            info.regs.cpsr = threaddata[16]
336
            info.nameptr = threaddata[18]
337
            if info.nameptr == 0:
338
                info.name = "Thread %d" % info.id
339
            else:
340
                info.name = self.readstring(info.nameptr)
341
            info.cputime_current = threaddata[19]
342
            info.cputime_total = threaddata[20]
343
            info.startusec = threaddata[21]
344
            info.queue_next_ptr = threaddata[22]
345
            info.timeout = threaddata[23]
346
            info.blocked_since = threaddata[24]
347
            info.blocked_by_ptr = threaddata[25]
348
            info.stackaddr = threaddata[26]
349
            info.err_no = threaddata[27]
350
            info.block_type = libembiosdata.thread_block[threaddata[28]]
351
            info.type = libembiosdata.thread_type[threaddata[29]]
352
            info.priority = threaddata[30]
353
            info.cpuload = threaddata[31]
354
            threads.append(info)
355
            id += 1
356
        return threads
56 benedikt93 357
 
173 farthen 358
    def lockscheduler(self, freeze=True):
171 farthen 359
        """ Freezes/Unfreezes the scheduler """
173 farthen 360
        resp = self.lib.monitorcommand(struct.pack("IIII", 16, 1 if freeze else 0, 0, 0), "III", ("before", None, None))
361
        return True if resp.before == 1 else False
67 benedikt93 362
 
173 farthen 363
    def unlockscheduler(self):
171 farthen 364
        """ Unfreezes the scheduler """
365
        return self.lib.monitorcommand(struct.pack("IIII", 16, 0, 0, 0), "III", ("before", None, None))
56 benedikt93 366
 
171 farthen 367
    def suspendthread(self, id, suspend=True):
368
        """ Suspends the thread with the specified id """
173 farthen 369
        resp = self.lib.monitorcommand(struct.pack("IIII", 17, 1 if suspend else 0, id, 0), "III", ("before", None, None))
370
        return True if resp.before == 1 else False
56 benedikt93 371
 
173 farthen 372
    def resumethread(self, id):
373
        """ Resumes the thread with the specified id """
171 farthen 374
        return self.lib.monitorcommand(struct.pack("IIII", 17, 0, id, 0), "III", ("before", None, None))
56 benedikt93 375
 
171 farthen 376
    def killthread(self, id):
377
        """ Kills the thread with the specified id """
378
        return self.lib.monitorcommand(struct.pack("IIII", 18, id, 0, 0), "III", ("before", None, None))
56 benedikt93 379
 
171 farthen 380
    def createthread(self, nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state):
381
        """ Creates a thread with the specified attributes """
382
        if threadtype == "user":
383
            threadtype = 0
384
        elif threadtype == "system":
385
            threadtype = 1
386
        else:
341 farthen 387
            raise ArgumentError("Threadtype must be either 'system' or 'user'")
171 farthen 388
        if priority > 256 or priority < 0:
341 farthen 389
            raise ArgumentError("Priority must be a number between 0 and 256")
171 farthen 390
        if state == "ready":
391
            state = 0
392
        elif state == "suspended":
393
            state = 1
394
        else:
341 farthen 395
            raise ArgumentError("State must be either 'ready' or 'suspended'")
171 farthen 396
        resp = self.lib.monitorcommand(struct.pack("IIIIIIII", 19, nameptr, entrypoint, stackptr, stacksize, threadtype, priority, state), "III", (id, None, None))
397
        if resp.id < 0:
398
            raise DeviceError("The device returned the error code "+str(resp.id))
399
        return resp
56 benedikt93 400
 
172 farthen 401
    def flushcaches(self):
171 farthen 402
        """ Flushes the CPU instruction and data cache """
403
        return self.lib.monitorcommand(struct.pack("IIII", 20, 0, 0, 0), "III", (None, None, None))
56 benedikt93 404
 
172 farthen 405
    def execimage(self, addr):
171 farthen 406
        """ Runs the emBIOS app at 'addr' """
407
        return self.lib.monitorcommand(struct.pack("IIII", 21, addr, 0, 0), "III", ("excecimage", None, None))
56 benedikt93 408
 
238 farthen 409
    def run(self, app):
410
        """ Uploads and runs the emBIOS app in the string 'app' """
411
        try:
412
            appheader = struct.unpack("<8sIIIIIIIIII", app[:48])
413
        except struct.error:
341 farthen 414
            raise ArgumentError("The specified app is not an emBIOS application")
238 farthen 415
        header = appheader[0]
416
        if header != "emBIexec":
341 farthen 417
            raise ArgumentError("The specified app is not an emBIOS application")
238 farthen 418
        baseaddr = appheader[2]
419
        threadnameptr = appheader[8]
420
        nameptr = threadnameptr - baseaddr
421
        name = ""
422
        while True:
423
            char = app[nameptr:nameptr+1]
424
            try:
425
                if ord(char) == 0:
426
                    break
427
            except TypeError:
341 farthen 428
                raise ArgumentError("The specified app is not an emBIOS application")
238 farthen 429
            name += char
430
            nameptr += 1
431
        usermem = self.getusermemrange()
432
        if usermem.lower > baseaddr or usermem.upper < baseaddr + len(app):
433
            raise ArgumentError("The baseaddress of the specified emBIOS application is out of range of the user memory range on the device. Are you sure that this application is compatible with your device?")
434
        self.write(baseaddr, app)
435
        self.execimage(baseaddr)
436
        return Bunch(baseaddr=baseaddr, name=name)
437
 
171 farthen 438
    def bootflashread(self, memaddr, flashaddr, size):
439
        """ Copies the data in the bootflash at 'flashaddr' of the specified size
440
            to the memory at addr 'memaddr'
441
        """
174 farthen 442
        return self.lib.monitorcommand(struct.pack("IIII", 22, memaddr, flashaddr, size), "III", (None, None, None))
82 benedikt93 443
 
171 farthen 444
    def bootflashwrite(self, memaddr, flashaddr, size):
445
        """ Copies the data in the memory at 'memaddr' of the specified size
446
            to the boot flash at addr 'flashaddr'
447
        """
174 farthen 448
        return self.lib.monitorcommand(struct.pack("IIII", 23, memaddr, flashaddr, size), "III", (None, None, None))
56 benedikt93 449
 
171 farthen 450
    def execfirmware(self, addr):
451
        """ Executes the firmware at 'addr' and passes all control to it. """
269 farthen 452
        return self.lib.monitorcommand(struct.pack("IIII", 24, addr, 0, 0))
56 benedikt93 453
 
171 farthen 454
    def aesencrypt(self, addr, size, keyindex):
455
        """ Encrypts the buffer at 'addr' with the specified size
456
            with the hardware AES key index 'keyindex'
457
        """
458
        return self.lib.monitorcommand(struct.pack("IBBHII", 25, 1, 0, keyindex, addr, size), "III", (None, None, None))
82 benedikt93 459
 
171 farthen 460
    def aesdecrypt(self, addr, size, keyindex):
461
        """ Decrypts the buffer at 'addr' with the specified size
462
            with the hardware AES key index 'keyindex'
463
        """
464
        return self.lib.monitorcommand(struct.pack("IBBHII", 25, 0, 0, keyindex, addr, size), "III", (None, None, None))
82 benedikt93 465
 
171 farthen 466
    def hmac_sha1(self, addr, size, destination):
467
        """ Generates a HMAC-SHA1 hash of the buffer and saves it to 'destination' """
468
        return self.lib.monitorcommand(struct.pack("IIII", 26, addr, size, destination), "III", (None, None, None))
56 benedikt93 469
 
227 theseven 470
    def ipodnano2g_getnandinfo(self):
471
        """ Target-specific function: ipodnano2g
472
            Gathers some information about the NAND chip used
473
        """
474
        return self.lib.monitorcommand(struct.pack("IIII", 0xffff0001, 0, 0, 0), "IHHHH", ("type", "pagesperblock", "banks", "userblocks", "blocks"))
475
 
476
    def ipodnano2g_nandread(self, addr, start, count, doecc, checkempty):
477
        """ Target-specific function: ipodnano2g
478
            Reads data from the NAND chip into memory
479
        """
480
        return self.lib.monitorcommand(struct.pack("IIII", 0xffff0002, addr | (0x80000000 if doecc != 0 else 0) | (0x40000000 if checkempty != 0 else 0), start, count), "III", (None, None, None))
481
 
482
    def ipodnano2g_nandwrite(self, addr, start, count, doecc):
483
        """ Target-specific function: ipodnano2g
484
            Writes data to the NAND chip
485
        """
486
        return self.lib.monitorcommand(struct.pack("IIII", 0xffff0003, addr | (0x80000000 if doecc != 0 else 0), start, count), "III", (None, None, None))
487
 
488
    def ipodnano2g_nanderase(self, addr, start, count):
489
        """ Target-specific function: ipodnano2g
490
            Erases blocks on the NAND chip and stores the results to memory
491
        """
492
        return self.lib.monitorcommand(struct.pack("IIII", 0xffff0004, addr, start, count), "III", (None, None, None))
493
 
56 benedikt93 494
 
171 farthen 495
class Lib(object):
176 farthen 496
    def __init__(self):
171 farthen 497
        self.idVendor = 0xFFFF
498
        self.idProduct = 0xE000
176 farthen 499
 
500
        self.headersize = 0x10
501
 
502
        self.connect()
56 benedikt93 503
 
171 farthen 504
    def connect(self):
505
        self.dev = Dev(self.idVendor, self.idProduct)
506
        self.connected = True
56 benedikt93 507
 
171 farthen 508
    def monitorcommand(self, cmd, rcvdatatypes=None, rcvstruct=None):
269 farthen 509
        writelen = self.dev.cout(cmd)
171 farthen 510
        if rcvdatatypes:
511
            rcvdatatypes = "I" + rcvdatatypes # add the response
512
            data = self.dev.cin(struct.calcsize(rcvdatatypes))
513
            data = struct.unpack(rcvdatatypes, data)
514
            response = data[0]
515
            if libembiosdata.responsecodes[response] == "ok":
516
                if rcvstruct:
517
                    datadict = Bunch()
518
                    counter = 1 # start with 1, 0 is the id
519
                    for item in rcvstruct:
520
                        if item != None: # else the data is undefined
521
                            datadict[item] = data[counter]
522
                        counter += 1
523
                    return datadict
524
                else:
525
                    return data
526
            elif libembiosdata.responsecodes[response] == "unsupported":
527
                raise DeviceError("The device does not support this command.")
528
            elif libembiosdata.responsecodes[response] == "invalid":
529
                raise DeviceError("Invalid command! This should NOT happen!")
530
            elif libembiosdata.responsecodes[response] == "busy":
531
                raise DeviceError("Device busy")
269 farthen 532
        else:
533
            return writelen
56 benedikt93 534
 
535
 
171 farthen 536
class Dev(object):
537
    def __init__(self, idVendor, idProduct):
538
        self.idVendor = idVendor
539
        self.idProduct = idProduct
67 benedikt93 540
 
171 farthen 541
        self.interface = 0
542
        self.timeout = 100
176 farthen 543
 
171 farthen 544
        self.connect()
545
        self.findEndpoints()
546
 
342 farthen 547
 
548
        # Device properties
343 farthen 549
        self.packetsizelimit = Bunch()
550
        self.packetsizelimit.cout = None
551
        self.packetsizelimit.cin = None
552
        self.packetsizelimit.dout = None
553
        self.packetsizelimit.din = None
342 farthen 554
 
343 farthen 555
        self.version = Bunch()
342 farthen 556
        self.version.revision = None
557
        self.version.majorv = None
558
        self.version.minorv = None
559
        self.version.patchv = None
560
        self.swtypeid = None
561
        self.hwtypeid = None
562
 
343 farthen 563
        self.usermem = Bunch()
342 farthen 564
        self.usermem.lower = None
565
        self.usermem.upper = None
56 benedikt93 566
 
171 farthen 567
    def __del__(self):
568
        self.disconnect()
56 benedikt93 569
 
171 farthen 570
    def findEndpoints(self):
571
        epcounter = 0
343 farthen 572
        self.endpoint = Bunch()
171 farthen 573
        for cfg in self.dev:
574
            for intf in cfg:
575
                for ep in intf:
576
                    if epcounter == 0:
343 farthen 577
                        self.endpoint.cout = ep.bEndpointAddress
171 farthen 578
                    elif epcounter == 1:
343 farthen 579
                        self.endpoint.cin = ep.bEndpointAddress
171 farthen 580
                    elif epcounter == 2:
343 farthen 581
                        self.endpoint.dout = ep.bEndpointAddress
171 farthen 582
                    elif epcounter == 3:
343 farthen 583
                        self.endpoint.din = ep.bEndpointAddress
171 farthen 584
                    epcounter += 1
585
        if epcounter <= 3:
586
            raise DeviceError("Not all endpoints found in the descriptor. Only "+str(epcounter)+" found, we need 4")
56 benedikt93 587
 
171 farthen 588
    def connect(self):
589
        self.dev = usb.core.find(idVendor=self.idVendor, idProduct=self.idProduct)
590
        if self.dev is None:
591
            raise DeviceNotFoundError()
592
        self.dev.set_configuration()
56 benedikt93 593
 
171 farthen 594
    def disconnect(self):
595
        pass
102 benedikt93 596
 
171 farthen 597
    def send(self, endpoint, data):
598
        size = self.dev.write(endpoint, data, self.interface, self.timeout)
599
        if size != len(data):
176 farthen 600
            raise SendError("Not all data was written!")
171 farthen 601
        return len
102 benedikt93 602
 
171 farthen 603
    def receive(self, endpoint, size):
604
        read = self.dev.read(endpoint, size, self.interface, self.timeout)
605
        if len(read) != size:
176 farthen 606
            raise ReceiveError("Requested size and read size don't match!")
171 farthen 607
        return read
56 benedikt93 608
 
171 farthen 609
    def cout(self, data):
343 farthen 610
        if self.packetsizelimit.cout and len(data) > self.packetsizelimit.cout:
171 farthen 611
            raise SendError("Packet too big")
343 farthen 612
        return self.send(self.endpoint.cout, data)
94 benedikt93 613
 
171 farthen 614
    def cin(self, size):
343 farthen 615
        if self.packetsizelimit.cin and size > self.packetsizelimit.cin:
171 farthen 616
            raise ReceiveError("Packet too big")
343 farthen 617
        return self.receive(self.endpoint.cin, size)
94 benedikt93 618
 
171 farthen 619
    def dout(self, data):
343 farthen 620
        if self.packetsizelimit.dout and len(data) > self.packetsizelimit.dout:
171 farthen 621
            raise SendError("Packet too big")
343 farthen 622
        return self.send(self.endpoint.dout, data)
94 benedikt93 623
 
171 farthen 624
    def din(self, size):
343 farthen 625
        if self.packetsizelimit.din and size > self.packetsizelimit.din:
171 farthen 626
            raise ReceiveError("Packet too big")
343 farthen 627
        return self.receive(self.endpoint.din, size)
56 benedikt93 628
 
96 benedikt93 629
 
171 farthen 630
if __name__ == "__main__":
631
    # Some tests
632
    import sys
633
    embios = Embios()
634
    resp = embios.getversioninfo()
635
    sys.stdout.write("Embios device version information: " + libembiosdata.swtypes[resp.swtypeid] + " v" + str(resp.majorv) + "." + str(resp.minorv) + 
636
                     "." + str(resp.patchv) + " r" + str(resp.revision) + " running on " + libembiosdata.hwtypes[resp.hwtypeid] + "\n")
637
    resp = embios.getusermemrange()
638
    sys.stdout.write("Usermemrange: "+hex(resp.lower)+" - "+hex(resp.upper)+"\n")
639
    memaddr = resp.lower
640
    maxlen = resp.upper - resp.lower
641
    f = open("./embios.py", "rb")
642
    sys.stdout.write("Loading test file (embios.py) to send over USB...\n")
643
    datastr = f.read()[:maxlen]
644
    sys.stdout.write("Sending data...\n")
645
    embios.write(memaddr, datastr)
646
    sys.stdout.write("Encrypting data with the hardware key...\n")
647
    embios.aesencrypt(memaddr, len(datastr), 0)
648
    sys.stdout.write("Reading data back and saving it to 'libembios-test-encrypted.bin'...\n")
649
    f = open("./libembios-test-encrypted.bin", "wb")
650
    f.write(embios.read(memaddr, len(datastr)))
651
    sys.stdout.write("Decrypting the data again...\n")
652
    embios.aesdecrypt(memaddr, len(datastr), 0)
653
    sys.stdout.write("Reading data back from device...\n")
654
    readdata = embios.read(memaddr, len(datastr))
655
    if readdata == datastr:
656
        sys.stdout.write("Data matches!")
657
    else:
658
        sys.stdout.write("Data does NOT match. Something got wrong")