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