fix endianness in device descriptor
[goodfet] / client / Facedancer.py
1 # Facedancer.py
2 #
3 # Contains class definitions for Facedancer, FacedancerCommand, FacedancerApp,
4 # and GoodFETMonitorApp.
5
6 from util import *
7
8 class Facedancer:
9     def __init__(self, serialport, verbose=0):
10         self.serialport = serialport
11         self.verbose = verbose
12
13         self.reset()
14         self.monitor_app = GoodFETMonitorApp(self, verbose=self.verbose)
15         self.monitor_app.announce_connected()
16
17     def halt(self):
18         self.serialport.setRTS(1)
19         self.serialport.setDTR(1)
20
21     def reset(self):
22         if self.verbose > 1:
23             print("Facedancer resetting...")
24
25         self.halt()
26         self.serialport.setDTR(0)
27
28         c = self.readcmd()
29
30         if self.verbose > 0:
31             print("Facedancer reset")
32
33     def read(self, n):
34         """Read raw bytes."""
35
36         b = self.serialport.read(n)
37
38         if self.verbose > 3:
39             print("Facedancer received", len(b), "bytes;",
40                     self.serialport.inWaiting(), "bytes remaining")
41
42         if self.verbose > 2:
43             print("Facedancer Rx:", bytes_as_hex(b))
44
45         return b
46
47     def readcmd(self):
48         """Read a single command."""
49
50         b = self.read(4)
51
52         app = b[0]
53         verb = b[1]
54         n = b[2] + (b[3] << 8)
55
56         if n > 0:
57             data = self.read(n)
58         else:
59             data = b''
60
61         if len(data) != n:
62             raise ValueError('Facedancer expected ' + str(n) \
63                     + ' bytes but received only ' + str(len(data)))
64
65         cmd = FacedancerCommand(app, verb, data)
66
67         if self.verbose > 1:
68             print("Facedancer Rx command:", cmd)
69
70         return cmd
71
72     def write(self, b):
73         """Write raw bytes."""
74
75         if self.verbose > 2:
76             print("Facedancer Tx:", bytes_as_hex(b))
77
78         self.serialport.write(b)
79
80     def writecmd(self, c):
81         """Write a single command."""
82         self.write(c.as_bytestring())
83
84         if self.verbose > 1:
85             print("Facedancer Tx command:", c)
86
87
88 class FacedancerCommand:
89     def __init__(self, app=None, verb=None, data=None):
90         self.app = app
91         self.verb = verb
92         self.data = data
93
94     def __str__(self):
95         s = "app 0x%02x, verb 0x%02x, len %d" % (self.app, self.verb,
96                 len(self.data))
97
98         if len(self.data) > 0:
99             s += ", data " + bytes_as_hex(self.data)
100
101         return s
102
103     def long_string(self):
104         s = "app: " + str(self.app) + "\n" \
105           + "verb: " + str(self.verb) + "\n" \
106           + "len: " + str(len(self.data))
107
108         if len(self.data) > 0:
109             try:
110                 s += "\n" + self.data.decode("utf-8")
111             except UnicodeDecodeError:
112                 s += "\n" + bytes_as_hex(self.data)
113
114         return s
115
116     def as_bytestring(self):
117         n = len(self.data)
118
119         b = bytearray(n + 4)
120         b[0] = self.app
121         b[1] = self.verb
122         b[2] = n & 0xff
123         b[3] = n >> 8
124         b[4:] = self.data
125
126         return b
127
128
129 class FacedancerApp:
130     app_name = "override this"
131     app_num = 0x00
132
133     def __init__(self, device, verbose=0):
134         self.device = device
135         self.verbose = verbose
136
137         self.init_commands()
138
139         if self.verbose > 0:
140             print(self.app_name, "initialized")
141
142     def init_commands(self):
143         pass
144
145     def enable(self):
146         for i in range(3):
147             self.device.writecmd(self.enable_app_cmd)
148             self.device.readcmd()
149
150         if self.verbose > 0:
151             print(self.app_name, "enabled")
152
153
154 class GoodFETMonitorApp(FacedancerApp):
155     app_name = "GoodFET monitor"
156     app_num = 0x00
157
158     def read_byte(self, addr):
159         d = [ addr & 0xff, addr >> 8 ]
160         cmd = FacedancerCommand(0, 2, d)
161
162         self.device.writecmd(cmd)
163         resp = self.device.readcmd()
164
165         return resp.data[0]
166
167     def get_infostring(self):
168         return bytes([ self.read_byte(0xff0), self.read_byte(0xff1) ])
169
170     def get_clocking(self):
171         return bytes([ self.read_byte(0x57), self.read_byte(0x56) ])
172
173     def print_info(self):
174         infostring = self.get_infostring()
175         clocking = self.get_clocking()
176
177         print("MCU", bytes_as_hex(infostring, delim=""))
178         print("clocked at", bytes_as_hex(clocking, delim=""))
179
180     def list_apps(self):
181         cmd = FacedancerCommand(self.app_num, 0x82, b'0x0')
182         self.device.writecmd(cmd)
183
184         resp = self.device.readcmd()
185         print("build date:", resp.data.decode("utf-8"))
186
187         print("firmware apps:")
188         while True:
189             resp = self.device.readcmd()
190             if len(resp.data) == 0:
191                 break
192             print(resp.data.decode("utf-8"))
193
194     def echo(self, s):
195         b = bytes(s, encoding="utf-8")
196
197         cmd = FacedancerCommand(self.app_num, 0x81, b)
198         self.device.writecmd(cmd)
199
200         resp = self.device.readcmd()
201
202         return resp.data == b
203
204     def announce_connected(self):
205         cmd = FacedancerCommand(self.app_num, 0xb1, b'')
206         self.device.writecmd(cmd)
207         resp = self.device.readcmd()
208