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