Lots of new CC2420 stabilization.
[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;
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_setfreq(self,frequency):
135         """Set the frequency in Hz."""
136         mhz=frequency/1000000;
137         #fsctrl=0x8000; #
138         fsctrl=self.peek(0x18)&(~0x3FF);
139         fsctrl=fsctrl+int(mhz-2048)
140         self.poke(0x18,fsctrl);
141         #self.CC_RFST_IDLE();
142         self.strobe(0x02);#SCAL
143         time.sleep(0.01);
144         self.strobe(0x03);#SRXON
145     def RF_getfreq(self):
146         """Get the frequency in Hz."""
147         fsctrl=self.peek(0x18);
148         mhz=2048+(fsctrl&0x3ff)
149         return mhz*1000000;
150     def RF_setchan(self,channel):
151         """Set the ZigBee/802.15.4 channel number."""
152         if channel < 11 or channel > 26:
153             print "Only 802.15.4 channels 11 to 26 are currently supported.";
154         else:
155             self.RF_setfreq( ( (channel-11)*5 + 2405 ) * 1000000 );
156     def RF_getsmac(self):
157         """Return the source MAC address."""
158         return 0xdeadbeef;
159     def RF_setsmac(self,mac):
160         """Set the source MAC address."""
161         return 0xdeadbeef;
162     def RF_gettmac(self):
163         """Return the target MAC address."""
164         return 0xdeadbeef;
165     def RF_settmac(self,mac):
166         """Set the target MAC address."""
167         return 0xdeadbeef;
168     def RF_getrssi(self):
169         """Returns the received signal strength, with a weird offset."""
170         rssival=self.peek(0x13)&0xFF; #raw RSSI register
171         return rssival^0x80;
172     lastpacket=range(0,0xff);
173     def RF_rxpacket(self):
174         """Get a packet from the radio.  Returns None if none is waiting.  In
175         order to not require the SFD, FIFO, or FIFOP lines, this
176         implementation works by comparing the buffer to the older
177         contents.
178         """
179         
180         data="\0";
181         self.data=data;
182         self.writecmd(self.CCSPIAPP,0x80,len(data),data);
183         buffer=self.data;
184         
185         self.lastpacket=buffer;
186         if(len(buffer)==0):
187             return None;
188         #self.strobe(0x08);         #SFLUSHRX
189         
190         return buffer;
191     def RF_txpacket(self,packet):
192         """Send a packet through the radio."""
193         self.writecmd(self.CCSPIAPP,0x81,len(packet),packet);
194         #time.sleep(1);
195         #self.strobe(0x09);
196         return;
197     
198     def RF_reflexjam(self,duration=0):
199         """Place the device into reflexive jamming mode."""
200         data = [duration&0xff,
201                 (duration>>8)&0xff];
202         self.writecmd(self.CCSPIAPP,0xA0,len(data),data);
203         return;
204
205     def RF_reflexjam_autoack(self):
206         """Place the device into reflexive jamming mode
207            and that also sends a forged ACK if needed."""
208         data = "";
209         self.writecmd(self.CCSPIAPP,0xA1,len(data),data);
210         print "Got:", data, "and", self.data
211         return;
212
213     def RF_modulated_spectrum(self):
214         """Hold a carrier wave on the present frequency."""
215         # print "Don't know how to hold a carrier.";
216         # 33.1 p.55:
217         #  reset chip
218         #  SXOSCON
219         #  set MDMCTRL1.TX_MODE to 3   0x12  3:2 
220         #  STXON                            0x04
221
222         mdmctrl1=self.peek(0x12);
223         #print "mdmctrl1 was %04x" % mdmctrl1;
224         mdmctrl1=mdmctrl1|0x00c0;  #MDMCTRL1.TX_MODE = 3
225         self.poke(0x12, mdmctrl1); #MDMCTRL1
226
227         mdmctrl1=self.peek(0x12);
228         #print "mdmctrl1 is %04x" % mdmctrl1;
229
230         # http://e2e.ti.com/support/low_power_rf/f/155/t/15914.aspx?PageIndex=2
231         #   suggests this
232         self.strobe(0x02);         #STXCAL
233         #print "STXCAL status: %s" % self.status()
234
235         # is this necessary?
236         self.strobe(0x09);         #SFLUSHTX
237         #print "SFLUSHTX status: %s" % self.status()
238
239         self.strobe(0x04);         #STXON
240         #print "STXON status: %s" % self.status()
241
242     def RF_carrier(self):
243         """Hold a carrier wave on the present frequency."""
244         # print "Don't know how to hold a carrier.";
245         # 33.1 p.54:
246         #  reset chip
247         #  SXOSCON
248         #  set MDMCTRL1.TX_MODE to 2 or 3   0x12  3:2 
249         #  set DACTST to 0x1800             0x2E
250         #  STXON                            0x04
251
252         mdmctrl1=self.peek(0x12);
253         #print "mdmctrl1 was %04x" % mdmctrl1;
254         mdmctrl1=mdmctrl1|0x0080; 
255         mdmctrl1=mdmctrl1&0x0080;  #MDMCTRL1.TX_MODE = 2
256         self.poke(0x12, mdmctrl1); #MDMCTRL1
257
258         mdmctrl1=self.peek(0x12);
259         #print "mdmctrl1 is %04x" % mdmctrl1;
260
261         self.poke(0x2E, 0x1800);   #DACTST
262         dactst=self.peek(0x2E);
263         #print "dactst is %04x" % dactst;
264
265         # see above for why this is here
266         self.strobe(0x02);         #STXCAL
267         #print "STXCAL status: %s" % self.status()
268         self.strobe(0x09);         #SFLUSHTX
269         #print "SFLUSHTX status: %s" % self.status()
270
271         self.strobe(0x04);         #STXON
272         #print "STXON status: %s" % self.status()
273
274     def RF_promiscuity(self,promiscuous=1):
275         mdmctrl0=self.peek(0x11);
276         if promiscuous>0:
277             mdmctrl0=mdmctrl0&(~0x800);
278         else:
279             mdmctrl0=mdmctrl0|0x800;
280         self.poke(0x11,mdmctrl0);
281         return;
282     def RF_autocrc(self,autocrc=1):
283         mdmctrl0=self.peek(0x11);
284         if autocrc==0:
285             mdmctrl0=mdmctrl0&(~0x0020);
286         else:
287             mdmctrl0=mdmctrl0|0x0020;
288         self.poke(0x11,mdmctrl0);
289         return;
290     def RF_autoack(self,autoack=1):
291         mdmctrl0=self.peek(0x11);
292         if autoack==0:
293             mdmctrl0=mdmctrl0&(~0x0010);
294         else:
295             mdmctrl0=mdmctrl0|0x0010;
296         self.poke(0x11,mdmctrl0);
297         return;
298     packetlen=16;
299     def RF_setpacketlen(self,len=16):
300         """Set the number of bytes in the expected payload."""
301         #self.poke(0x11,len);
302         self.packetlen=len;
303     def RF_getpacketlen(self):
304         """Set the number of bytes in the expected payload."""
305         #len=self.peek(0x11);
306         self.packetlen=len;
307         return len;
308     maclen=5;
309     def RF_getmaclen(self):
310         """Get the number of bytes in the MAC address."""
311         choices=[0, 3, 4, 5];
312         choice=self.peek(0x03)&3;
313         self.maclen=choices[choice];
314         return self.maclen;
315     def RF_setmaclen(self,len):
316         """Set the number of bytes in the MAC address."""
317         choices=["illegal", "illegal", "illegal", 
318                  1, 2, 3];
319         choice=choices[len];
320         self.poke(0x03,choice);
321         self.maclen=len;
322     def printpacket(self,packet,prefix="#"):
323         s="";
324         i=0;
325         for foo in packet:
326             s="%s %02x" % (s,ord(foo));
327         print "%s%s" % (prefix,s);
328         
329     def printdissect(self,packet):
330         try:
331             from scapy.all import Dot15d4
332         except ImportError:
333             print "To use packet disection, Scapy must be installed and have the Dot15d4 extension present."
334             print "try: hg clone http://hg.secdev.org/scapy-com";
335             print "     sudo ./setup.py install";
336         self.printpacket(packet);
337         try:
338             scapyd = Dot15d4(packet[1:]);
339             scapyd.show();
340         except:
341             pass;