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