Promiscuity can be turned off in CCSPI.
[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         
28     def ident(self):
29         return self.peek(0x1E); #MANFIDL
30     def identstr(self):
31         manfidl=self.peek(0x1E);
32         #manfidh=self.peek(0x1f);
33         try:
34             return "%s" % (self.CCversions[manfidl]); 
35         except:
36             return "Unknown0x%04x" % manfidl;
37     def trans8(self,byte):
38         """Read and write 8 bits by CCSPI."""
39         data=self.CCSPItrans([byte]);
40         return ord(data[0]);
41     
42     def trans(self,data):
43         """Exchange data by CCSPI."""
44         self.data=data;
45         self.writecmd(self.CCSPIAPP,0x00,len(data),data);
46         return self.data;
47     def strobe(self,reg=0x00):
48         """Strobes a strobe register, returning the status."""
49         data=[reg];
50         self.trans(data);
51         return ord(self.data[0]);
52     def CC_RFST_IDLE(self):
53         """Switch the radio to idle mode, clearing overflows and errors."""
54         self.strobe(0x06); #SRXOFF
55     def CC_RFST_TX(self):
56         """Switch the radio to TX mode."""
57         self.strobe(0x04);  #0x05 for CCA
58     def CC_RFST_RX(self):
59         """Switch the radio to RX mode."""
60         self.strobe(0x03);
61     def CC_RFST_CAL(self):
62         """Calibrate strobe the radio."""
63         self.strobe(0x02);
64     def CC_RFST(self,state=0x00):
65         self.strobe(state);
66         return;
67     def peek(self,reg,bytes=2):
68         """Read a CCSPI Register.  For long regs, result is flipped."""
69         
70         #Reg is ORed with 0x40 by the GoodFET.
71         data=[reg,0,0];
72         
73         #Automatically calibrate the len.
74         bytes=2;
75         
76         self.writecmd(self.CCSPIAPP,0x02,len(data),data);
77         toret=(
78             ord(self.data[2])+
79             (ord(self.data[1])<<8)
80             );
81         return toret;
82     def poke(self,reg,val,bytes=2):
83         """Write a CCSPI Register."""
84         data=[reg,(val>>8)&0xFF,val&0xFF];
85         self.writecmd(self.CCSPIAPP,0x03,len(data),data);
86         if self.peek(reg,bytes)!=val:
87             print "Warning, failed to set r%02x=0x%04x, got %02x." %(
88                 reg,
89                 val,
90                 self.peek(reg,bytes));
91         return;
92     
93     def status(self):
94         """Read the status byte."""
95         statusbits={0x80: "?",
96                     0x40: "XOSC16M_STABLE",
97                     0x20: "TX_UNDERFLOW",
98                     0x10: "ENC_BUSY",
99                     0x08: "TX_ACTIVE",
100                     0x04: "LOCK",
101                     0x02: "RSSI_VALID",
102                     0x01: "?"};
103         status=self.strobe(0x00);
104         i=1;
105         str="";
106         while i<0x100:
107             if status&i:
108                str="%s %s" % (statusbits[i],str);
109             i*=2;
110         return str;
111     
112     #Radio stuff begins here.
113     def RF_setenc(self,code="802.15.4"):
114         """Set the encoding type."""
115         return;
116     def RF_getenc(self):
117         """Get the encoding type."""
118         return "802.15.4";
119     def RF_getrate(self):
120         return 0;
121     def RF_setrate(self,rate=0):
122         return 0;
123     def RF_setfreq(self,frequency):
124         """Set the frequency in Hz."""
125         mhz=frequency/1000000;
126         fsctrl=self.peek(0x18)&~0x3FF;
127         fsctrl=fsctrl+int(mhz-2048)
128         self.poke(0x18,fsctrl);
129         self.strobe(0x02);
130     def RF_getfreq(self):
131         """Get the frequency in Hz."""
132         fsctrl=self.peek(0x18);
133         mhz=2048+(fsctrl&0x3ff)
134         return mhz*1000000;
135     def RF_setchan(self,channel):
136         if channel < 11 and channel > 26:
137             print "Only 802.15.4 channels 11 to 26 are currently supported.";
138         else:
139             self.RF_setfreq( ( (channel-11)*5 + 2405 ) * 1000000 );
140     def RF_getsmac(self):
141         """Return the source MAC address."""
142         return 0xdeadbeef;
143     def RF_setsmac(self,mac):
144         """Set the source MAC address."""
145         return 0xdeadbeef;
146     def RF_gettmac(self):
147         """Return the target MAC address."""
148         return 0xdeadbeef;
149     def RF_settmac(self,mac):
150         """Set the target MAC address."""
151         return 0xdeadbeef;
152     def RF_getrssi(self):
153         """Returns the received signal strenght, with a weird offset."""
154         rssival=self.peek(0x13)&0xFF; #raw RSSI register
155         return rssival^0x80;
156     lastpacket=range(0,0xff);
157     def RF_rxpacket(self):
158         """Get a packet from the radio.  Returns None if none is waiting.  In
159         order to not require the SFD, FIFO, or FIFOP lines, this
160         implementation works by comparing the buffer to the older
161         contents.
162         """
163         
164         data="\0";
165         self.data=data;
166         self.writecmd(self.CCSPIAPP,0x80,len(data),data);
167         buffer=self.data;
168         
169         self.lastpacket=buffer;
170         if(len(buffer)==0):
171             return None;
172         return buffer;
173     def RF_txpacket(self,packet):
174         """Send a packet through the radio."""
175         self.writecmd(self.CCSPIAPP,0x81,len(packet),packet);
176         #time.sleep(1);
177         #self.strobe(0x09);
178         return;
179     
180
181     def RF_modulated_spectrum(self):
182         """Hold a carrier wave on the present frequency."""
183         # print "Don't know how to hold a carrier.";
184         # 33.1 p.55:
185         #  reset chip
186         #  SXOSCON
187         #  set MDMCTRL1.TX_MODE to 3   0x12  3:2 
188         #  STXON                            0x04
189
190         mdmctrl1=self.peek(0x12);
191         #print "mdmctrl1 was %04x" % mdmctrl1;
192         mdmctrl1=mdmctrl1|0x00c0;  #MDMCTRL1.TX_MODE = 3
193         self.poke(0x12, mdmctrl1); #MDMCTRL1
194
195         mdmctrl1=self.peek(0x12);
196         #print "mdmctrl1 is %04x" % mdmctrl1;
197
198         # http://e2e.ti.com/support/low_power_rf/f/155/t/15914.aspx?PageIndex=2
199         #   suggests this
200         self.strobe(0x02);         #STXCAL
201         #print "STXCAL status: %s" % self.status()
202
203         # is this necessary?
204         self.strobe(0x09);         #SFLUSHTX
205         #print "SFLUSHTX status: %s" % self.status()
206
207         self.strobe(0x04);         #STXON
208         #print "STXON status: %s" % self.status()
209
210     def RF_carrier(self):
211         """Hold a carrier wave on the present frequency."""
212         # print "Don't know how to hold a carrier.";
213         # 33.1 p.54:
214         #  reset chip
215         #  SXOSCON
216         #  set MDMCTRL1.TX_MODE to 2 or 3   0x12  3:2 
217         #  set DACTST to 0x1800             0x2E
218         #  STXON                            0x04
219
220         mdmctrl1=self.peek(0x12);
221         #print "mdmctrl1 was %04x" % mdmctrl1;
222         mdmctrl1=mdmctrl1|0x0080; 
223         mdmctrl1=mdmctrl1&0x0080;  #MDMCTRL1.TX_MODE = 2
224         self.poke(0x12, mdmctrl1); #MDMCTRL1
225
226         mdmctrl1=self.peek(0x12);
227         #print "mdmctrl1 is %04x" % mdmctrl1;
228
229         self.poke(0x2E, 0x1800);   #DACTST
230         dactst=self.peek(0x2E);
231         #print "dactst is %04x" % dactst;
232
233         # see above for why this is here
234         self.strobe(0x02);         #STXCAL
235         #print "STXCAL status: %s" % self.status()
236         self.strobe(0x09);         #SFLUSHTX
237         #print "SFLUSHTX status: %s" % self.status()
238
239         self.strobe(0x04);         #STXON
240         #print "STXON status: %s" % self.status()
241
242     def RF_promiscuity(self,promiscuous=1):
243         mdmctrl0=self.peek(0x11);
244         if promiscuous>0:
245             mdmctrl0=mdmctrl0&(~0x800);
246         else:
247             mdmctrl0=mdmctrl0|0x800;
248         self.poke(0x11,mdmctrl0);
249         return;
250         
251     packetlen=16;
252     def RF_setpacketlen(self,len=16):
253         """Set the number of bytes in the expected payload."""
254         #self.poke(0x11,len);
255         self.packetlen=len;
256     def RF_getpacketlen(self):
257         """Set the number of bytes in the expected payload."""
258         #len=self.peek(0x11);
259         self.packetlen=len;
260         return len;
261     maclen=5;
262     def RF_getmaclen(self):
263         """Get the number of bytes in the MAC address."""
264         choices=[0, 3, 4, 5];
265         choice=self.peek(0x03)&3;
266         self.maclen=choices[choice];
267         return self.maclen;
268     def RF_setmaclen(self,len):
269         """Set the number of bytes in the MAC address."""
270         choices=["illegal", "illegal", "illegal", 
271                  1, 2, 3];
272         choice=choices[len];
273         self.poke(0x03,choice);
274         self.maclen=len;
275     def printpacket(self,packet):
276         s="";
277         i=0;
278         for foo in packet:
279             s="%s %02x" % (s,ord(foo));
280         print "#%s" % s;
281     def printdissect(self,packet):
282         try:
283             from scapy.all import Dot15d4
284         except ImportError:
285             print "To use packet disection, Scapy must be installed and have the Dot15d4 extension present."
286             print "try: hg clone http://hg.secdev.org/scapy-com";
287             print "     sudo ./setup.py install";
288         self.printpacket(packet);
289         try:
290             scapyd = Dot15d4(packet[1:]);
291             scapyd.show();
292         except:
293             pass;