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