changes to a bug on the spit method
[goodfet] / client / GoodFETCCSPI.py
1 #!/usr/bin/env python
2 # GoodFET Chipcon RF Radio Client
3
4 # (C) 2009, 2012 Travis Goodspeed <travis at radiantmachines.com>
5 #
6 # This code is being rewritten and refactored.  You've been warned!
7
8 import sys, time, string, cStringIO, struct, glob, os;
9
10 from GoodFET import GoodFET;
11
12 class GoodFETCCSPI(GoodFET):
13     CCSPIAPP=0x51;
14     CCversions={0x233d: "CC2420",
15                 }
16     def setup(self):
17         """Move the FET into the CCSPI application."""
18         self.writecmd(self.CCSPIAPP,0x10,0,self.data); #CCSPI/SETUP
19         
20         #Set up the radio for ZigBee
21         self.strobe(0x01);       #SXOSCON
22         self.strobe(0x02);       #SCAL
23         self.poke(0x11, 0x0AC2 & (~0x0800)); #MDMCTRL0, promiscuous
24         self.poke(0x12, 0x0500); #MDMCTRL1
25         self.poke(0x1C, 0x007F); #IOCFG0
26         self.poke(0x19, 0x01C4); #SECCTRL0, disabling crypto
27         #self.poke(0x19, 0x0204); #SECCTRL0, as seen elsewhere.
28         #self.RF_setsync();
29         
30     def ident(self):
31         return self.peek(0x1E); #MANFIDL
32     def identstr(self):
33         manfidl=self.peek(0x1E);
34         #manfidh=self.peek(0x1f);
35         try:
36             return "%s" % (self.CCversions[manfidl]); 
37         except:
38             return "Unknown0x%04x" % manfidl;
39     def trans8(self,byte):
40         """Read and write 8 bits by CCSPI."""
41         data=self.CCSPItrans([byte]);
42         return ord(data[0]);
43     
44     def trans(self,data):
45         """Exchange data by CCSPI."""
46         self.data=data;
47         self.writecmd(self.CCSPIAPP,0x00,len(data),data);
48         return self.data;
49     def strobe(self,reg=0x00):
50         """Strobes a strobe register, returning the status."""
51         data=[reg];
52         self.trans(data);
53         return ord(self.data[0]);
54     def CC_RFST_IDLE(self):
55         """Switch the radio to idle mode, clearing overflows and errors."""
56         self.strobe(0x06); #SRXOFF
57     def CC_RFST_TX(self):
58         """Switch the radio to TX mode."""
59         self.strobe(0x04);  #0x05 for CCA
60     def CC_RFST_RX(self):
61         """Switch the radio to RX mode."""
62         self.strobe(0x03); #RX ON
63     def CC_RFST_CAL(self):
64         """Calibrate strobe the radio."""
65         self.strobe(0x02); #RX Calibrate
66     def CC_RFST(self,state=0x00):
67         self.strobe(state);
68         return;
69     def peek(self,reg,bytes=2):
70         """Read a CCSPI Register.  For long regs, result is flipped."""
71         
72         #Reg is ORed with 0x40 by the GoodFET.
73         data=[reg,0,0];
74         
75         #Automatically calibrate the len.
76         bytes=2;
77         
78         self.writecmd(self.CCSPIAPP,0x02,len(data),data);
79         toret=(
80             ord(self.data[2])+
81             (ord(self.data[1])<<8)
82             );
83         return toret;
84     def poke(self,reg,val,bytes=2):
85         """Write a CCSPI Register."""
86         data=[reg,(val>>8)&0xFF,val&0xFF];
87         self.writecmd(self.CCSPIAPP,0x03,len(data),data);
88         if self.peek(reg,bytes)!=val and reg!=0x18:
89             print "Warning, failed to set r%02x=0x%04x, got %02x." %(
90                 reg,
91                 val,
92                 self.peek(reg,bytes));
93             return False;
94         return True;
95     
96     def status(self):
97         """Read the status byte."""
98         statusbits={0x80: "?",
99                     0x40: "XOSC16M_STABLE",
100                     0x20: "TX_UNDERFLOW",
101                     0x10: "ENC_BUSY",
102                     0x08: "TX_ACTIVE",
103                     0x04: "LOCK",
104                     0x02: "RSSI_VALID",
105                     0x01: "?"};
106         status=self.strobe(0x00);
107         i=1;
108         str="";
109         while i<0x100:
110             if status&i:
111                str="%s %s" % (statusbits[i],str);
112             i*=2;
113         return str;
114     
115     #Radio stuff begins here.
116     def RF_setenc(self,code="802.15.4"):
117         """Set the encoding type."""
118         return code;
119     def RF_getenc(self):
120         """Get the encoding type."""
121         return "802.15.4";
122     def RF_getrate(self):
123         return 0;
124     def RF_setrate(self,rate=0):
125         return 0;
126     def RF_getsync(self):
127         return self.peek(0x14);
128     def RF_setsync(self,sync=0xa70F):
129         """Set the SYNC preamble.
130         Use 0xA70F for 0xA7."""
131         self.poke(0x14,sync);
132         return;
133     
134     def RF_setkey(self,key):
135         """Sets the first key for encryption to the given argument."""
136         print "ERROR: Forgot to set the key.";
137         
138         return;
139     def RF_setnonce(self,key):
140         """Sets the first key for encryption to the given argument."""
141         print "ERROR: Forgot to set the nonce.";
142         
143         return;
144     
145     def RF_setfreq(self,frequency):
146         """Set the frequency in Hz."""
147         mhz=frequency/1000000;
148         #fsctrl=0x8000; #
149         fsctrl=self.peek(0x18)&(~0x3FF);
150         fsctrl=fsctrl+int(mhz-2048)
151         self.poke(0x18,fsctrl);
152         #self.CC_RFST_IDLE();
153         self.strobe(0x02);#SCAL
154         time.sleep(0.01);
155         self.strobe(0x03);#SRXON
156     def RF_getfreq(self):
157         """Get the frequency in Hz."""
158         fsctrl=self.peek(0x18);
159         mhz=2048+(fsctrl&0x3ff)
160         return mhz*1000000;
161     def RF_setchan(self,channel):
162         """Set the ZigBee/802.15.4 channel number."""
163         if channel < 11 or channel > 26:
164             print "Only 802.15.4 channels 11 to 26 are currently supported.";
165         else:
166             self.RF_setfreq( ( (channel-11)*5 + 2405 ) * 1000000 );
167     def RF_getsmac(self):
168         """Return the source MAC address."""
169         return 0xdeadbeef;
170     def RF_setsmac(self,mac):
171         """Set the source MAC address."""
172         return 0xdeadbeef;
173     def RF_gettmac(self):
174         """Return the target MAC address."""
175         return 0xdeadbeef;
176     def RF_settmac(self,mac):
177         """Set the target MAC address."""
178         return 0xdeadbeef;
179     def RF_getrssi(self):
180         """Returns the received signal strength, with a weird offset."""
181         rssival=self.peek(0x13)&0xFF; #raw RSSI register
182         return rssival^0x80;
183
184     def peekram(self,adr,count):
185         """Peeks data from CC2420 RAM."""
186         data=[
187             adr&0xFF,adr>>8,     # Address first.
188             count&0xFF,count>>8  # Then length.
189             ];
190         self.writecmd(self.CCSPIAPP,0x84,len(data),data);
191         return self.data;
192     def pokeram(self,adr,data):
193         """Pokes data into CC2420 RAM."""
194         data=[adr&0xFF, adr>>8]+data;
195         self.writecmd(self.CCSPIAPP,0x85,len(data),data);
196         return;
197     
198     lastpacket=range(0,0xff);
199     def RF_rxpacket(self):
200         """Get a packet from the radio.  Returns None if none is
201         waiting."""
202         
203         data="\0";
204         self.data=data;
205         self.writecmd(self.CCSPIAPP,0x80,len(data),data);
206         buffer=self.data;
207         
208         self.lastpacket=buffer;
209         if(len(buffer)==0):
210             return None;
211         
212         return buffer;
213     def RF_rxpacketrepeat(self):
214         """Gets packets from the radio, ignoring all future requests so as
215         not to waste time.  Call RF_rxpacket() after this."""
216         
217         self.writecmd(self.CCSPIAPP,0x91,0,None);
218         return None;
219     
220     def RF_rxpacketdec(self):
221         """Get and decrypt a packet from the radio.  Returns None if
222         none is waiting."""
223         
224         data="\0";
225         self.data=data;
226         self.writecmd(self.CCSPIAPP,0x90,len(data),data);
227         buffer=self.data;
228         
229         self.lastpacket=buffer;
230         if(len(buffer)==0):
231             return None;
232         
233         return buffer;
234
235     def RF_txpacket(self,packet):
236         """Send a packet through the radio."""
237         self.writecmd(self.CCSPIAPP,0x81,len(packet),packet);
238         #time.sleep(1);
239         #self.strobe(0x09);
240         return;
241     
242     def RF_reflexjam(self,duration=0):
243         """Place the device into reflexive jamming mode."""
244         data = [duration&0xff,
245                 (duration>>8)&0xff];
246         self.writecmd(self.CCSPIAPP,0xA0,len(data),data);
247         return;
248
249     def RF_reflexjam_autoack(self):
250         """Place the device into reflexive jamming mode
251            and that also sends a forged ACK if needed."""
252         data = "";
253         self.writecmd(self.CCSPIAPP,0xA1,len(data),data);
254         print "Got:", data, "and", self.data
255         return;
256
257     def RF_modulated_spectrum(self):
258         """Hold a carrier wave on the present frequency."""
259         # print "Don't know how to hold a carrier.";
260         # 33.1 p.55:
261         #  reset chip
262         #  SXOSCON
263         #  set MDMCTRL1.TX_MODE to 3   0x12  3:2 
264         #  STXON                            0x04
265
266         mdmctrl1=self.peek(0x12);
267         #print "mdmctrl1 was %04x" % mdmctrl1;
268         mdmctrl1=mdmctrl1|0x00c0;  #MDMCTRL1.TX_MODE = 3
269         self.poke(0x12, mdmctrl1); #MDMCTRL1
270
271         mdmctrl1=self.peek(0x12);
272         #print "mdmctrl1 is %04x" % mdmctrl1;
273
274         # http://e2e.ti.com/support/low_power_rf/f/155/t/15914.aspx?PageIndex=2
275         #   suggests this
276         self.strobe(0x02);         #STXCAL
277         #print "STXCAL status: %s" % self.status()
278
279         # is this necessary?
280         self.strobe(0x09);         #SFLUSHTX
281         #print "SFLUSHTX status: %s" % self.status()
282
283         self.strobe(0x04);         #STXON
284         #print "STXON status: %s" % self.status()
285
286     def RF_carrier(self):
287         """Hold a carrier wave on the present frequency."""
288         # print "Don't know how to hold a carrier.";
289         # 33.1 p.54:
290         #  reset chip
291         #  SXOSCON
292         #  set MDMCTRL1.TX_MODE to 2 or 3   0x12  3:2 
293         #  set DACTST to 0x1800             0x2E
294         #  STXON                            0x04
295
296         mdmctrl1=self.peek(0x12);
297         #print "mdmctrl1 was %04x" % mdmctrl1;
298         mdmctrl1=mdmctrl1|0x0080; 
299         mdmctrl1=mdmctrl1&0x0080;  #MDMCTRL1.TX_MODE = 2
300         self.poke(0x12, mdmctrl1); #MDMCTRL1
301
302         mdmctrl1=self.peek(0x12);
303         #print "mdmctrl1 is %04x" % mdmctrl1;
304
305         self.poke(0x2E, 0x1800);   #DACTST
306         dactst=self.peek(0x2E);
307         #print "dactst is %04x" % dactst;
308
309         # see above for why this is here
310         self.strobe(0x02);         #STXCAL
311         #print "STXCAL status: %s" % self.status()
312         self.strobe(0x09);         #SFLUSHTX
313         #print "SFLUSHTX status: %s" % self.status()
314
315         self.strobe(0x04);         #STXON
316         #print "STXON status: %s" % self.status()
317
318     def RF_promiscuity(self,promiscuous=1):
319         mdmctrl0=self.peek(0x11);
320         if promiscuous>0:
321             mdmctrl0=mdmctrl0&(~0x800);
322         else:
323             mdmctrl0=mdmctrl0|0x800;
324         self.poke(0x11,mdmctrl0);
325         return;
326     def RF_autocrc(self,autocrc=1):
327         mdmctrl0=self.peek(0x11);
328         if autocrc==0:
329             mdmctrl0=mdmctrl0&(~0x0020);
330         else:
331             mdmctrl0=mdmctrl0|0x0020;
332         self.poke(0x11,mdmctrl0);
333         return;
334     def RF_autoack(self,autoack=1):
335         mdmctrl0=self.peek(0x11);
336         if autoack==0:
337             mdmctrl0=mdmctrl0&(~0x0010);
338         else:
339             mdmctrl0=mdmctrl0|0x0010;
340         self.poke(0x11,mdmctrl0);
341         return;
342     packetlen=16;
343     def RF_setpacketlen(self,len=16):
344         """Set the number of bytes in the expected payload."""
345         #self.poke(0x11,len);
346         self.packetlen=len;
347     def RF_getpacketlen(self):
348         """Set the number of bytes in the expected payload."""
349         #len=self.peek(0x11);
350         self.packetlen=len;
351         return len;
352     maclen=5;
353     def RF_getmaclen(self):
354         """Get the number of bytes in the MAC address."""
355         choices=[0, 3, 4, 5];
356         choice=self.peek(0x03)&3;
357         self.maclen=choices[choice];
358         return self.maclen;
359     def RF_setmaclen(self,len):
360         """Set the number of bytes in the MAC address."""
361         choices=["illegal", "illegal", "illegal", 
362                  1, 2, 3];
363         choice=choices[len];
364         self.poke(0x03,choice);
365         self.maclen=len;
366     def printpacket(self,packet,prefix="#"):
367         print self.packet2str(packet,prefix);
368     def packet2str(self,packet,prefix="#"):
369         s="";
370         i=0;
371         for foo in packet:
372             s="%s %02x" % (s,ord(foo));
373         return "%s%s" % (prefix,s);
374         
375     def printdissect(self,packet):
376         try:
377             from scapy.all import Dot15d4
378         except ImportError:
379             print "To use packet disection, Scapy must be installed and have the Dot15d4 extension present."
380             print "try: hg clone http://hg.secdev.org/scapy-com";
381             print "     sudo ./setup.py install";
382         self.printpacket(packet);
383         try:
384             scapyd = Dot15d4(packet[1:]);
385             scapyd.show();
386         except:
387             pass;