set/get SYNC on CC2420.
[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         
260     packetlen=16;
261     def RF_setpacketlen(self,len=16):
262         """Set the number of bytes in the expected payload."""
263         #self.poke(0x11,len);
264         self.packetlen=len;
265     def RF_getpacketlen(self):
266         """Set the number of bytes in the expected payload."""
267         #len=self.peek(0x11);
268         self.packetlen=len;
269         return len;
270     maclen=5;
271     def RF_getmaclen(self):
272         """Get the number of bytes in the MAC address."""
273         choices=[0, 3, 4, 5];
274         choice=self.peek(0x03)&3;
275         self.maclen=choices[choice];
276         return self.maclen;
277     def RF_setmaclen(self,len):
278         """Set the number of bytes in the MAC address."""
279         choices=["illegal", "illegal", "illegal", 
280                  1, 2, 3];
281         choice=choices[len];
282         self.poke(0x03,choice);
283         self.maclen=len;
284     def printpacket(self,packet):
285         s="";
286         i=0;
287         for foo in packet:
288             s="%s %02x" % (s,ord(foo));
289         print "#%s" % s;
290     def printdissect(self,packet):
291         try:
292             from scapy.all import Dot15d4
293         except ImportError:
294             print "To use packet disection, Scapy must be installed and have the Dot15d4 extension present."
295             print "try: hg clone http://hg.secdev.org/scapy-com";
296             print "     sudo ./setup.py install";
297         self.printpacket(packet);
298         try:
299             scapyd = Dot15d4(packet[1:]);
300             scapyd.show();
301         except:
302             pass;