Initial stab at bslv2 client
authorthequux <thequux@12e2690d-a6be-4b82-a7b7-67c4a43b65c8>
Sat, 6 Aug 2011 20:02:43 +0000 (20:02 +0000)
committerthequux <thequux@12e2690d-a6be-4b82-a7b7-67c4a43b65c8>
Sat, 6 Aug 2011 20:02:43 +0000 (20:02 +0000)
git-svn-id: https://svn.code.sf.net/p/goodfet/code/trunk@1030 12e2690d-a6be-4b82-a7b7-67c4a43b65c8

client/goodfet.bslv2 [new file with mode: 0755]
client/hidapi.py [new file with mode: 0644]
client/lib/Makefile [new file with mode: 0644]
client/lib/hidapi-linux2-x86_64.so [new file with mode: 0755]

diff --git a/client/goodfet.bslv2 b/client/goodfet.bslv2
new file mode 100755 (executable)
index 0000000..3b1d200
--- /dev/null
@@ -0,0 +1,321 @@
+#!/usr/bin/env python
+
+import hidapi
+import struct
+import time
+
+BSL_VID = 0x2047
+BSL_PID = 0x0200
+
+def _fmt_addr(addr, count=3):
+    return "".join([chr((addr >> (n*8)) & 0xff) for n in range(count)])
+
+RAM_BSL = """
+@2400
+E2 43 04 02 E2 43 02 02 80 00 04 25
+@2500
+00 05 04 34 31 40 90 33 B0 13 5E 2E 0C 93 00 24
+B0 13 F4 2D FF 3F 12 01 00 02 00 00 00 08 47 20
+00 02 04 01 00 00 00 01 06 00 FF 09 01 A1 01 85
+3F 95 3F 75 08 25 01 15 01 09 01 81 02 85 3F 95
+3F 75 08 25 01 15 01 09 01 91 02 C0 09 02 29 00
+01 01 00 80 32 09 04 00 00 02 03 00 00 00 09 21
+01 01 00 01 22 24 00 07 05 81 03 40 00 01 07 05
+01 03 40 00 01
+@2576
+F2 D2 20 09 F2 D2 22 09 10 01 5E 42 02 24 7E 93
+25 24 7E 90 09 00 04 28 7D 42 7E 82 5F 43 0C 3C
+7E 92 02 2C 4D 4E 06 3C 7D 42 D2 93 0E 24 02 20
+4E 43 F4 3F 7E 43 4F 43 C2 4F 10 24 C2 4E 02 24
+4F 43 07 3C 1E 42 06 24 EF 4E 78 23 92 53 06 24
+5F 53 4F 9D F7 2B C2 4D 21 09 10 01 C2 43 10 24
+10 01 82 4C 06 24 5E 42 86 23 C2 9E 02 24 04 28
+C2 4E 02 24 4E 43 01 3C 5E 43 C2 4E 0E 24 80 00
+80 25 F2 B0 0F 00 84 23 14 20 C2 93 84 23 03 34
+5E 42 20 09 02 3C 5E 42 22 09 7E F2 C2 4E 60 24
+5E 42 60 24 42 19 4E 10 C2 4E 60 24 B0 13 C4 27
+09 3C C2 93 84 23 03 34 5E 42 C8 23 EE 3F 5E 42
+88 23 EB 3F 3C 40 60 24 80 00 D8 25 F2 43 02 24
+C2 43 10 24 C2 43 21 09 10 01 C2 93 82 23 12 20
+5E 42 84 23 7E F0 0F 00 02 20 80 00 42 26 5E 93
+0B 20 C2 93 84 23 03 34 F2 D2 C8 23 F6 3F F2 D2
+88 23 F3 3F B0 13 76 25 10 01 C2 93 80 23 04 34
+1F 43 D2 D3 3C 09 03 3C 0F 43 D2 C3 3C 09 5E 42
+80 23 7E B0 60 00 90 20 5D 42 81 23 4D 83 81 24
+5D 83 6B 24 6D 83 67 24 6D 83 45 24 5D 83 09 24
+6D 83 52 24 5D 83 46 24 5D 83 33 24 5D 83 54 24
+7B 3C 0F 93 79 24 5E 42 83 23 5E 83 08 24 5E 83
+0F 24 7E 80 1F 00 1C 24 5E 83 13 24 6D 3C C2 43
+23 09 F2 40 12 00 02 24 3C 40 16 25 80 00 D8 25
+C2 43 23 09 F2 40 29 00 02 24 3C 40 4C 25 80 00
+D8 25 F2 40 24 00 02 24 3C 40 28 25 80 00 D8 25
+C2 43 23 09 F2 40 09 00 02 24 3C 40 5E 25 80 00
+D8 25 0F 93 49 24 B0 13 C4 27 C2 43 60 24 D2 42
+01 24 61 24 3B 3C B0 13 CE 27 D2 42 82 23 3F 09
+80 00 42 26 B0 13 CE 27 D2 42 82 23 00 24 B0 13
+42 26 D2 43 12 24 10 01 C2 43 23 09 D2 43 02 24
+3C 40 00 24 80 00 D8 25 B0 13 CE 27 D2 42 84 23
+01 24 80 00 42 26 80 00 50 26 5E 42 84 23 7E F0
+0F 00 02 20 80 00 42 26 5E 93 18 20 C2 93 84 23
+04 34 F2 F0 D7 00 C8 23 F5 3F F2 F0 D7 00 88 23
+F1 3F 7E 90 80 00 03 20 B0 13 C4 27 43 3F 7E 90
+82 00 02 20 80 00 F8 25 B0 13 76 25 10 01 C2 43
+23 09 E2 43 02 24 10 01 D5 3E 1B 15 1F 42 5A 24
+5B 4F 03 00 5E 4F 01 00 5C 4F 02 00 8C 10 0C DE
+0D 4B 0E 4F 2E 52 6A 4F 7A 80 10 00 29 24 5A 83
+14 24 5A 83 2A 24 5A 83 2E 24 6A 83 23 24 5A 83
+3A 24 5A 83 15 24 5A 83 3B 24 5A 83 3E 24 6A 83
+41 20 5F 43 B0 13 E2 2B 41 3C 1F 53 0C 4F B0 13
+38 2C 4C 93 02 20 4C 43 37 3C 7C 40 05 00 34 3C
+B0 13 66 2E 03 20 B0 13 6E 2E F5 3F 6C 42 2C 3C
+4F 43 E8 3F B0 13 D4 2D 27 3C 0E 4C 0F 4B 4C 43
+B0 13 D4 2C 21 3C B0 13 66 2E F0 23 4C 43 1F 42
+58 24 3F 50 40 00 1B 42 44 01 3B F0 10 00 0F 5B
+82 4F 44 01 11 3C B0 13 3C 2E B0 13 12 2B 0E 3C
+B0 13 3C 2E B0 13 66 29 09 3C 2E 42 3C 40 00 25
+0D 43 F8 3F 7C 40 07 00 B0 13 12 2E 1A 17 10 01
+E2 B2 3E 09 14 28 F2 40 80 00 23 09 03 3C F2 F0
+FA 00 3E 09 C2 43 10 24 C2 43 60 24 C2 43 61 24
+B0 13 80 26 D2 B3 3E 09 F2 2F E2 C2 3E 09 1F 42
+32 09 7F 90 0A 00 0C 20 B0 13 4A 2E B0 13 86 2C
+B0 13 08 2A B2 F0 F9 FF 08 09 A2 D3 02 09 10 01
+7F 90 0C 00 06 20 B0 13 4A 2E B2 40 04 A5 20 01
+10 01 7F 90 12 00 0A 20 C2 43 23 09 D2 93 10 24
+02 20 80 00 80 25 F2 D2 20 09 10 01 7F 90 16 00
+02 20 80 00 08 2A 7F 90 18 00 0C 20 D2 43 11 24
+F2 C0 40 00 3E 09 B2 40 80 00 10 09 F2 40 20 00
+3D 09 10 01 7F 90 1A 00 0A 20 B0 13 86 2C F2 F0
+9F 00 3E 09 F2 40 C0 00 3D 09 C2 43 11 24 10 01
+7B 15 0A 4C 0B 4D 0F 4E 3F E3 0F 5F 0F 7F 08 4C
+09 4D 08 5E 09 6F 47 43 0B 3C 1F 42 5C 24 FF 40
+3A 00 00 00 0C 46 1C 53 B0 13 2A 2E 0A 56 0B 63
+0B 99 03 28 34 20 0A 98 32 2C 47 93 30 20 0E 48
+0F 49 0E 8A 0F 7B 03 20 3E 90 3E 00 03 28 36 40
+3D 00 02 3C 06 48 06 8A 14 42 5C 24 14 53 0E 46
+0F 46 3F E3 0F 5F 0F 7F 0E 5A 0F 6B 09 3C 1F 15
+0D 16 6C 4D 0D 4E 0D 8A 05 44 05 5D C5 4C 00 00
+3E 53 3F 63 0F 9B C9 2B 02 20 0E 9A C6 2B B0 13
+66 2E ED 27 67 42 6C 42 B0 13 12 2E C7 3F 74 17
+10 01 F2 40 10 00 3C 09 C2 43 12 24 C2 43 11 24
+C2 43 00 24 C2 43 01 24 C2 43 3C 09 F2 43 02 24
+F2 43 04 24 C2 43 10 24 7E 40 80 00 C2 4E 21 09
+C2 4E 23 09 F2 40 8C 00 20 09 F2 40 8C 00 22 09
+F2 40 03 00 2F 09 F2 40 03 00 2E 09 C2 4E C8 23
+F2 40 10 00 C9 23 C2 4E CA 23 C2 4E CE 23 F2 40
+40 00 CF 23 C2 4E 88 23 C2 43 89 23 C2 43 8A 23
+F2 40 40 00 8F 23 F2 40 40 00 3C 09 C2 43 3E 09
+F2 40 C0 00 3D 09 10 01 7B 15 08 4C 07 4D 04 4F
+4C 43 0A 48 0B 4D 0F 4E 3F E3 0F 5F 0F 7F 06 48
+06 5E 07 6F 02 3C 1A 53 0B 63 0B 97 03 28 2C 20
+0A 96 2A 2C 18 B3 08 2C 0E 46 0F 47 3E 53 3F 63
+0A 9E 19 20 0B 9F 17 20 6E 44 B0 13 66 2E 10 20
+4C 43 B0 13 56 2E 1B 15 0F 16 CF 4E 00 00 B0 13
+56 2E 1B 15 0F 16 6D 4F 4E 9D 03 24 5C 43 01 3C
+6C 42 14 53 07 3C 3E 44 0C 4A 0D 4B B0 13 64 2D
+1A 53 0B 63 4C 93 CF 27 74 17 10 01 3B 15 0A 4E
+B2 43 54 01 08 4C 09 4D 07 3C 19 15 0E 16 6F 4E
+C2 4F 52 01 18 53 09 63 0E 4C 0F 4D 0E 5A 0F 63
+09 9F 03 28 09 20 08 9E 07 2C B0 13 66 2E ED 27
+6C 42 B0 13 12 2E 15 3C 1E 42 54 01 1F 42 5C 24
+FF 40 3A 00 00 00 1B 42 5C 24 CB 4E 01 00 47 18
+0E 11 1F 42 5C 24 CF 4E 02 00 3C 40 03 00 B0 13
+2A 2E 38 17 10 01 32 C2 03 43 B2 40 02 1C 5A 24
+B2 40 17 24 5C 24 B2 40 28 96 00 09 82 43 02 09
+82 43 60 01 B2 40 F3 10 64 01 B2 40 40 00 62 01
+B2 40 44 02 68 01 C2 43 0E 24 C2 43 11 24 B2 40
+28 96 00 09 B2 40 40 1E 08 09 B2 40 80 00 04 09
+B0 13 4A 2E C2 43 12 24 B2 B2 08 09 06 28 B0 13
+86 2C B0 13 08 2A A2 D3 02 09 10 01 3B 15 4A 4F
+6F 42 3B 40 58 24 B0 13 66 2E 08 20 4F 43 A2 4B
+44 01 28 4B 38 50 40 00 82 48 40 01 4F 93 0B 20
+B2 90 05 00 5E 24 07 38 0F 4E 1E 42 5E 24 2E 82
+B0 13 8E 2A 4F 4C 4A 93 03 20 4C 4F B0 13 12 2E
+A2 4B 40 01 2F 4B 3F 50 10 00 82 4F 44 01 38 17
+10 01 1B 15 21 83 0D 43 3A 40 E0 FF 0B 43 7E 4A
+0F 4C 0F 5B 6F 4F 0E EF 0D DE 1B 53 3B 90 20 00
+F6 2B 0D 93 0E 20 B1 40 FF 7F 00 00 02 3C B1 53
+00 00 91 93 00 00 FB 37 B2 40 A5 A5 56 24 4C 43
+04 3C B0 13 D4 2D 7C 40 05 00 21 53 1A 17 10 01
+21 82 81 43 02 00 B2 40 28 96 00 09 92 D3 02 09
+92 42 14 24 12 09 B2 40 00 13 10 09 82 43 14 09
+81 43 00 00 02 3C 91 53 00 00 B1 90 64 00 00 00
+FA 2B 1F 41 02 00 0E 4F 1E 53 81 4E 02 00 3F 90
+E9 03 03 2C 82 93 14 09 E9 23 21 52 10 01 B0 13
+66 2E 0E 20 4C 43 B0 13 FA 2C 1D 42 58 24 2D 53
+82 4D 40 01 1F 15 0D 16 CD 43 00 00 80 00 08 2D
+6C 42 10 01 92 B3 44 01 FD 2F 92 42 58 24 44 01
+10 01 92 B3 44 01 FD 2F 1F 42 58 24 3F 50 10 00
+82 4F 44 01 10 01 82 43 5E 24 C2 43 8A 23 B0 13
+A6 28 D2 93 12 24 0D 20 C2 93 11 24 0A 20 4F 43
+C2 93 8A 23 04 34 5F 42 8A 23 7F F0 7F 00 82 4F
+5E 24 82 93 5E 24 EB 27 92 93 5E 24 06 38 5F 42
+01 1C 82 4F 5E 24 5C 43 10 01 4C 43 10 01 1B 15
+B0 13 66 2E 15 20 4F 43 B0 13 56 2E 1D 15 0A 16
+8A 4E 00 00 B0 13 56 2E 1D 15 0A 16 2B 4A 0E 9B
+01 24 5F 43 92 B3 46 01 04 28 7F 40 03 00 01 3C
+6F 42 4C 4F 1A 17 10 01 0A 12 7E 40 3F 00 C2 93
+CA 23 11 34 C2 4E 80 1C 3D 40 81 1C 4F 43 0A 4C
+0A 5F ED 4A 00 00 1D 53 5F 53 4F 9E F8 2B F2 40
+40 00 CA 23 01 3C 4E 43 4C 4E 3A 41 10 01 B0 13
+FA 2C B0 13 56 2E 1F 42 58 24 3F 50 06 00 82 4F
+40 01 C2 43 E0 FF B0 13 08 2D 4C 43 10 01 B2 40
+A5 A5 56 24 B2 40 00 A5 58 24 B0 13 7C 2B B0 13
+1C 2D 5C B3 FC 2B B0 13 D0 27 F9 3F 1F 42 5C 24
+FF 40 3B 00 00 00 1F 42 5C 24 CF 4C 01 00 2C 43
+80 00 2A 2E C2 4C 16 24 3C 40 16 24 B0 13 9E 2D
+4C 93 FA 27 10 01 6E 4E 5F 4F 05 00 47 18 0F 5F
+0E DF 10 01 03 43 3F 40 DE 2E 3F 53 FE 2F 10 01
+92 B3 44 01 FD 2F 10 01 B2 40 80 5A 5C 01 10 01
+B2 90 A5 A5 56 24 10 01 1D 15 10 01
+@FFFE
+04 25
+q"""
+
+BLINK_BSL = """@8000
+31 40 00 34 B0 13 0C 80 B0 13 32 80 21 83 D2 43
+04 02 D2 43 02 02 B2 40 80 5A 5C 01 07 3C 91 53
+00 00 B1 93 00 00 FB 23 D2 E3 02 02 81 43 00 00
+F8 3F 80 00 36 80 80 00 3A 80 FF 3F
+@FFE0
+12 34 56 78 99 10 11 12 13 14 15 16 17 18 19 20
+12 34 56 78 99 10 11 12 13 14 15 16 17 18 00 80
+q"""
+
+BLINK_BSL2 = """@2000
+31 40 00 34 B0 13 0C 20 B0 13 32 20 21 83 D2 43
+04 02 D2 43 02 02 B2 40 80 5A 5C 01 07 3C 91 53
+00 00 B1 93 00 00 FB 23 D2 E3 02 02 81 43 00 00
+F8 3F 80 00 36 20 80 00 3A 20 FF 3F
+@FFE0
+12 34 56 78 99 10 11 12 13 14 15 16 17 18 19 20
+12 34 56 78 99 10 11 12 13 14 15 16 17 18 00 20
+q"""
+def hexline(pkt):
+    return " ".join(c.encode("hex") for c in pkt)
+
+class TiHex(object):
+    def __init__(self, string):
+        self.content = content = []
+        addr = 0
+        for line in string.split("\n"):
+            if line.startswith("@"):
+                addr = int(line[1:],16)
+            elif line.startswith("q"):
+                return
+            elif len(line) == 0:
+                continue
+            else:
+                data = line.replace(" ", "").decode("hex")
+                if len(data) > 0:
+                    content.append((addr, data))
+                    addr += len(data)
+    def __iter__(self):
+        for x in self.content:
+            yield x
+
+
+
+class BSL(object):
+    MSGS = ["SUCCESS",
+            "Flash write check failed",
+            "Flash Fail Bit Set",
+            "Voltage Change During Program",
+            "BSL Locked",
+            "BSL Password Error",
+            "Byte Write Forbidden",
+            "Unknown Command",
+            "Packet Length Exceeds Buffer Size"]
+
+    def __init__(self, vid = BSL_VID, pid = BSL_PID):
+        self.vid = vid
+        self.pid = pid
+        self.device = hidapi.HidDevice(vid, pid)
+
+    def _send_command(self, num, data, expect_response = True):
+        if len(data) >= 255:
+            raise Exception("Data too long")
+        packet = '\x3f' + chr(len(data) + 1) + chr(num) + data
+        #print " ".join(c.encode("hex") for c in packet)
+        r = self.device.write(packet)
+        #print r
+        if expect_response:
+            rdata = self.device.read(64)
+            if len(rdata) < 2:
+                raise Exception("Short response")
+            if rdata[0] != '\x3f':
+                raise Exception("Malformed packet")
+            print repr(rdata)
+            resp = rdata[2: 2+ord(rdata[1])]
+            if resp[0] == '\x3a':
+                return resp[1:]
+            elif resp[0] == '\x3b' and len(resp) == 2:
+                if resp[1] == '\0':
+                    return True
+                else:
+                    raise Exception(self.MSGS[ord(resp[1])])
+            else:
+                raise Exception("Slightly malformed response")
+
+    def RxPassword(self, passwd):
+        return self._send_command(0x11, passwd)
+
+
+    def RxDataBlock(self, addr, data):
+        return self._send_command(0x10, _fmt_addr(addr) + data)
+
+    def RxDataBlockFast(self, addr, data):
+        return self._send_command(0x1b, _fmt_addr(addr) + data, False)
+
+    def EraseSegment(self, addr):
+        return self._send_command(0x12, _fmt_addr(addr))
+
+    def ToggleInfoLock(self):
+        return self._send_command(0x13, "")
+    def MassErase(self):
+        return self._send_command(0x15, "")
+    def CrcCheck(self, addr, length):
+        return self._send_command(0x16, _fmt_addr(addr) + _fmt_addr(length, 2))
+    def LoadPc(self, addr):
+        return self._send_command(0x17, _fmt_addr(addr), False)
+    def TxDataBlock(self, addr, length):
+        return self._send_command(0x18, _fmt_addr(addr) + _fmt_addr(length, 2))
+    def TxBslVersion(self):
+        return self._send_command(0x19, "")
+    def TxBufferSize(self):
+        return self._send_command(0x1a, "")
+
+    # Helpers
+    def RxTIHexFast(self, hexstring):
+        tihex = TiHex(hexstring)
+        for addr, value in tihex:
+            self.RxDataBlockFast(addr, value)
+
+    def bounce_hid(self):
+        "Reconnect to the target"
+        self.device.close()
+        time.sleep(0.250)
+        self.device = hidapi.HidDevice(self.vid, self.pid)
+        print self.device._device
+
+def main():
+    bsl = BSL()
+    #print BLINK_BSL
+    #return
+    try:
+        print repr(bsl.RxPassword('\xff' * 32))
+        print repr(bsl.RxTIHexFast(RAM_BSL))
+        print repr(bsl.LoadPc(0x2400))
+        #print repr(bsl.RxTIHexFast(file("blinky.tihex","r").read()))
+        #print repr(bsl.LoadPc(0x2000))
+        #bsl.device.close()
+        print repr("Bouncing...")
+        time.sleep(3)
+        print repr(bsl.bounce_hid())
+        print repr("waiting")
+        print repr("Next")
+        print repr(bsl.TxBufferSize())
+        print repr(bsl.TxBslVersion())
+    finally:
+        bsl.device.close()
+
+main()
diff --git a/client/hidapi.py b/client/hidapi.py
new file mode 100644 (file)
index 0000000..6cf204c
--- /dev/null
@@ -0,0 +1,221 @@
+from ctypes import *
+
+# Python doesn't define any sort of ABI string, so we need to manually probe for it.
+# If your OS/arch isn't supported yet, see lib/README for help.
+
+import platform
+import sys
+
+def _get_shared_lib_name():
+    if sys.platform.startswith('linux'):
+        # This may fail for a x86_32 userland on a x86_64 kernel. platform.architecture can be used to work around it, if necessary
+        arch = platform.machine()
+        if arch == 'amd64':
+            # old 64-bit linux.
+            arch = 'x86_64'
+        if arch == 'x86_64' and platform.architecture()[0] == '32bit':
+            # Whee! 32-bit python on a 64-bit kernel. The horror!
+            arch = 'x86_32'
+        if arch[0] == 'i' and arch[2:] == '86':
+            arch = 'x86_32'
+        # Any other variations need to be dealt with here.
+
+        return "lib/hidapi-%(platform)s-%(arch)s.so" % {"platform": sys.platform, "arch": arch}
+    elif sys.platform == 'darwin':
+        # This is completely backwards... both 32 and 64 report an i386.
+        # However, it may not matter; darwin supports fat binaries.
+        # If necessary, here's what I've found (very incomplete):
+        #
+        # arch*  machine    desired build
+        # 64bit  i386       x86_64
+        return "lib/hidapi-darwin.dylib"
+    elif sys.platform == 'win32':
+        # not yet fully supported. For now, assuming 32-bit x86.
+        return "lib/hidapi-win32-x86_32.dll"
+    else:
+        print >>sys.stderr, "Your platform is not yet supported. Please let the devs know, or read lib/README for help fixing it."
+        raise Exception("Unsupported platform")
+
+if __name__ == '__main__':
+    print "Would try to load %s" % _get_shared_lib_name()
+
+_hidapi = CDLL(_get_shared_lib_name())
+
+class _HidDeviceInfo(Structure):
+    pass
+
+_HidDeviceInfo._fields_ = [
+    ("path", c_char_p),
+    ("vendor_id", c_ushort),
+    ("product_id", c_ushort),
+    ("serial_number", c_wchar_p),
+    ("release_number", c_ushort),
+    ("manufacturer_string", c_wchar_p),
+    ("product_string", c_wchar_p),
+    ("usage_page", c_ushort),
+    ("usage", c_ushort),
+    ("interface_number", c_int),
+    ("next", POINTER(_HidDeviceInfo))]
+
+class HidDeviceInfo(object):
+    """User-facing version of the _HidDeviceInfo structure."""
+
+    def __init__(self, raw):
+        for attr in ("path", "vendor_id", "product_id", "serial_number", "release_number", "manufacturer_string", "product_string", "usage_page", "usage", "interface_number"):
+            setattr(self, attr, getattr(raw, attr))
+
+    def open(self):
+        return HidDevice(self.vendor_id, self.product_id, self.serial_number)
+
+class _HidDevice_p(c_void_p):
+    pass
+
+class HidDevice(object):
+    def __init__(self, vid, pid=None, serial=None):
+        if type(vid) is str:
+            assert pid is None and serial is None
+            self._device = _hidapi.hid_open_path(vid)
+        else:
+            self._device = _hidapi.hid_open(vid, pid, serial)
+        if not self._device:
+            raise IOError("Failed to open device")
+
+    def write(self, string):
+        return _hidapi.hid_write(self._device, create_string_buffer(string), len(string))
+    def read(self, length):
+        buf = create_string_buffer(length)
+        olen = _hidapi.hid_read(self._device, buf, length)
+        return buf.raw[:length]
+    def set_nonblocking(self, nonblocking = True):
+        _hidapi.hid_set_nonblocking(self._device, nonblocking)
+    def send_feature_report(self, report_id, data):
+        buf = create_string_buffer(chr(report_id) + data)
+
+        return _hidapi.hid_send_feature_report(self._device, buf, len(data) + 1)
+    def get_feature_report(self, report_id, length):
+        # length does not include report id
+        buf = create_string_buffer(length + 1)
+        buf[0] = report_id
+        olen = _hidapi.hid_get_feature_report(self._device, buf, length + 1)
+        # BUG(thequux): Possible off-by-one error
+        return buf.raw[1:olen]
+
+    def close(self):
+        _hidapi.hid_close(self._device)
+        self._device = None
+
+    def get_manufacturer_string(_device):
+        buf = create_unicode_buffer(257)
+        _hidapi.hid_get_manufacturer_string(self._device, buf, 257)
+        return buf.value
+
+    def get_product_string(_device):
+        buf = create_unicode_buffer(257)
+        _hidapi.hid_get_product_string(self._device, buf, 257)
+        return buf.value
+
+    def get_serial_number_string(_device):
+        buf = create_unicode_buffer(257)
+        _hidapi.hid_get_serial_number_string(self._device, buf, 257)
+        return buf.value
+
+    def get_indexed_string(_device, index):
+        buf = create_unicode_buffer(257)
+        _hidapi.hid_get_indexed_string(self._device, index, buf, 257)
+        return buf.value
+
+
+
+
+class HidApiError(IOError):
+    pass
+
+def _check_hid_error(result, func, args):
+    if result == -1:
+        print args, _hidapi.hid_error(args[0])
+        raise HidApiError(_hidapi.hid_error(args[0]))
+    return result
+
+# signatures
+_hidapi.hid_enumerate.argtypes = [c_ushort, c_ushort]
+_hidapi.hid_enumerate.restype = POINTER(_HidDeviceInfo)
+
+_hidapi.hid_free_enumeration.argtypes = [POINTER(_HidDeviceInfo)]
+
+_hidapi.hid_open.argtypes = [c_ushort, c_ushort, c_wchar_p]
+_hidapi.hid_open.restype = _HidDevice_p
+
+_hidapi.hid_open_path.argtypes = [c_char_p]
+_hidapi.hid_open_path.restype = _HidDevice_p
+
+_hidapi.hid_write.argtypes = [_HidDevice_p, POINTER(c_char), c_size_t]
+_hidapi.hid_write.restype = c_int
+_hidapi.hid_write.errcheck = _check_hid_error
+
+_hidapi.hid_read.argtypes = [_HidDevice_p, POINTER(c_char), c_size_t]
+_hidapi.hid_read.restype = c_int
+_hidapi.hid_read.errcheck = _check_hid_error
+
+_hidapi.hid_set_nonblocking.argtypes = [_HidDevice_p, c_int]
+_hidapi.hid_set_nonblocking.restype = c_int
+_hidapi.hid_set_nonblocking.errcheck = _check_hid_error
+
+_hidapi.hid_send_feature_report.argtypes = [_HidDevice_p, POINTER(c_char), c_size_t]
+_hidapi.hid_send_feature_report.restype = c_int
+_hidapi.hid_send_feature_report.errcheck = _check_hid_error
+
+_hidapi.hid_get_feature_report.argtypes = [_HidDevice_p, POINTER(c_char), c_size_t]
+_hidapi.hid_get_feature_report.restype = c_int
+_hidapi.hid_get_feature_report.errcheck = _check_hid_error
+
+_hidapi.hid_close.argtypes = [_HidDevice_p]
+
+_hidapi.hid_get_manufacturer_string.argtypes = [_HidDevice_p, POINTER(c_wchar), c_size_t]
+_hidapi.hid_get_manufacturer_string.restype = c_int
+_hidapi.hid_get_manufacturer_string.errcheck = _check_hid_error
+
+_hidapi.hid_get_product_string.argtypes = [_HidDevice_p, POINTER(c_wchar), c_size_t]
+_hidapi.hid_get_product_string.restype = c_int
+_hidapi.hid_get_product_string.errcheck = _check_hid_error
+
+_hidapi.hid_get_serial_number_string.argtypes = [_HidDevice_p, POINTER(c_wchar), c_size_t]
+_hidapi.hid_get_serial_number_string.restype = c_int
+_hidapi.hid_get_serial_number_string.errcheck = _check_hid_error
+
+_hidapi.hid_get_indexed_string.argtypes = [_HidDevice_p, c_int, POINTER(c_wchar), c_size_t]
+_hidapi.hid_get_indexed_string.restype = c_int
+_hidapi.hid_get_indexed_string.errcheck = _check_hid_error
+
+_hidapi.hid_error.argtypes = [_HidDevice_p]
+_hidapi.hid_error.restype = c_wchar_p
+
+def hid_enumerate(vid=0, pid=0):
+    """Enumerate the HID devices.
+
+    If vid == pid == 0, will enumerate all hid devices. Otherwise, just the ones with the given vid/pid.
+
+    Returns:
+       List of HidDeviceInfo structures.
+    """
+
+    devs = _hidapi.hid_enumerate(vid,pid)
+
+    raw_list = devs
+    ret = []
+    while raw_list:
+        raw = raw_list.contents
+        raw_list = raw.next
+        ret.append(HidDeviceInfo(raw))
+
+    _hidapi.hid_free_enumeration(devs)
+    return ret
+
+def hid_open(vid, pid, serial_number = None):
+    return HidDevice(vid, pid, serial_number)
+
+def hid_open_path(path):
+    return HidDevice(path)
+
+if __name__ == '__main__':
+    for dev in hid_enumerate():
+        print "%04x:%04x %30s <%s> <%s> %d" % (dev.vendor_id, dev.product_id, dev.serial_number, dev.manufacturer_string, dev.product_string, dev.interface_number)
diff --git a/client/lib/Makefile b/client/lib/Makefile
new file mode 100644 (file)
index 0000000..8f305be
--- /dev/null
@@ -0,0 +1,9 @@
+
+hidapi:
+       git clone -b release git://github.com/thequux/hidapi.git
+
+hidapi-linux2-x86_64.so:
+       make -C hidapi/linux EXTRA_CFLAGS=-m64 clean hidapi.so && mv hidapi/linux/hidapi.so $@
+
+hidapi-linux2-x86_32.so:
+       make -C hidapi/linux EXTRA_CFLAGS=-m32 clean hidapi.so && mv hidapi/linux/hidapi.so $@
diff --git a/client/lib/hidapi-linux2-x86_64.so b/client/lib/hidapi-linux2-x86_64.so
new file mode 100755 (executable)
index 0000000..0d99edb
Binary files /dev/null and b/client/lib/hidapi-linux2-x86_64.so differ