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