0283d87116ba79c1a24e4ddd3ee9082273a49ec4
[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_modulated_spectrum(self):
196         """Hold a carrier wave on the present frequency."""
197         # print "Don't know how to hold a carrier.";
198         # 33.1 p.55:
199         #  reset chip
200         #  SXOSCON
201         #  set MDMCTRL1.TX_MODE to 3   0x12  3:2 
202         #  STXON                            0x04
203
204         mdmctrl1=self.peek(0x12);
205         #print "mdmctrl1 was %04x" % mdmctrl1;
206         mdmctrl1=mdmctrl1|0x00c0;  #MDMCTRL1.TX_MODE = 3
207         self.poke(0x12, mdmctrl1); #MDMCTRL1
208
209         mdmctrl1=self.peek(0x12);
210         #print "mdmctrl1 is %04x" % mdmctrl1;
211
212         # http://e2e.ti.com/support/low_power_rf/f/155/t/15914.aspx?PageIndex=2
213         #   suggests this
214         self.strobe(0x02);         #STXCAL
215         #print "STXCAL status: %s" % self.status()
216
217         # is this necessary?
218         self.strobe(0x09);         #SFLUSHTX
219         #print "SFLUSHTX status: %s" % self.status()
220
221         self.strobe(0x04);         #STXON
222         #print "STXON status: %s" % self.status()
223
224     def RF_carrier(self):
225         """Hold a carrier wave on the present frequency."""
226         # print "Don't know how to hold a carrier.";
227         # 33.1 p.54:
228         #  reset chip
229         #  SXOSCON
230         #  set MDMCTRL1.TX_MODE to 2 or 3   0x12  3:2 
231         #  set DACTST to 0x1800             0x2E
232         #  STXON                            0x04
233
234         mdmctrl1=self.peek(0x12);
235         #print "mdmctrl1 was %04x" % mdmctrl1;
236         mdmctrl1=mdmctrl1|0x0080; 
237         mdmctrl1=mdmctrl1&0x0080;  #MDMCTRL1.TX_MODE = 2
238         self.poke(0x12, mdmctrl1); #MDMCTRL1
239
240         mdmctrl1=self.peek(0x12);
241         #print "mdmctrl1 is %04x" % mdmctrl1;
242
243         self.poke(0x2E, 0x1800);   #DACTST
244         dactst=self.peek(0x2E);
245         #print "dactst is %04x" % dactst;
246
247         # see above for why this is here
248         self.strobe(0x02);         #STXCAL
249         #print "STXCAL status: %s" % self.status()
250         self.strobe(0x09);         #SFLUSHTX
251         #print "SFLUSHTX status: %s" % self.status()
252
253         self.strobe(0x04);         #STXON
254         #print "STXON status: %s" % self.status()
255
256     def RF_promiscuity(self,promiscuous=1):
257         mdmctrl0=self.peek(0x11);
258         if promiscuous>0:
259             mdmctrl0=mdmctrl0&(~0x800);
260         else:
261             mdmctrl0=mdmctrl0|0x800;
262         self.poke(0x11,mdmctrl0);
263         return;
264     def RF_autocrc(self,autocrc=1):
265         mdmctrl0=self.peek(0x11);
266         if autocrc==0:
267             mdmctrl0=mdmctrl0&(~0x0020);
268         else:
269             mdmctrl0=mdmctrl0|0x0020;
270         self.poke(0x11,mdmctrl0);
271         return;
272     packetlen=16;
273     def RF_setpacketlen(self,len=16):
274         """Set the number of bytes in the expected payload."""
275         #self.poke(0x11,len);
276         self.packetlen=len;
277     def RF_getpacketlen(self):
278         """Set the number of bytes in the expected payload."""
279         #len=self.peek(0x11);
280         self.packetlen=len;
281         return len;
282     maclen=5;
283     def RF_getmaclen(self):
284         """Get the number of bytes in the MAC address."""
285         choices=[0, 3, 4, 5];
286         choice=self.peek(0x03)&3;
287         self.maclen=choices[choice];
288         return self.maclen;
289     def RF_setmaclen(self,len):
290         """Set the number of bytes in the MAC address."""
291         choices=["illegal", "illegal", "illegal", 
292                  1, 2, 3];
293         choice=choices[len];
294         self.poke(0x03,choice);
295         self.maclen=len;
296     def printpacket(self,packet):
297         s="";
298         i=0;
299         for foo in packet:
300             s="%s %02x" % (s,ord(foo));
301         print "#%s" % s;
302         
303     def printdissect(self,packet):
304         try:
305             from scapy.all import Dot15d4
306         except ImportError:
307             print "To use packet disection, Scapy must be installed and have the Dot15d4 extension present."
308             print "try: hg clone http://hg.secdev.org/scapy-com";
309             print "     sudo ./setup.py install";
310         self.printpacket(packet);
311         try:
312             scapyd = Dot15d4(packet[1:]);
313             scapyd.show();
314         except:
315             pass;