updated features for two different types of sniffing. fixed and scrolling. This can...
[goodfet] / client / GoodFETCCSPI.py
index 48b219e..518add3 100644 (file)
 #!/usr/bin/env python
 # GoodFET Chipcon RF Radio Client
 # 
-# (C) 2009 Travis Goodspeed <travis at radiantmachines.com>
+# (C) 2009, 2012 Travis Goodspeed <travis at radiantmachines.com>
 #
 # This code is being rewritten and refactored.  You've been warned!
 
-import sys, time, string, cStringIO, struct, glob, serial, os;
+import sys, time, string, cStringIO, struct, glob, os;
 
 from GoodFET import GoodFET;
 
 class GoodFETCCSPI(GoodFET):
-    CCSPIAPP=0x50;
-    def CCSPIsetup(self):
+    CCSPIAPP=0x51;
+    CCversions={0x233d: "CC2420",
+                }
+    def setup(self):
         """Move the FET into the CCSPI application."""
         self.writecmd(self.CCSPIAPP,0x10,0,self.data); #CCSPI/SETUP
         
-    def CCSPItrans8(self,byte):
+        #Set up the radio for ZigBee
+        self.strobe(0x01);       #SXOSCON
+        self.strobe(0x02);       #SCAL
+        self.poke(0x11, 0x0AC2 & (~0x0800)); #MDMCTRL0, promiscuous
+        self.poke(0x12, 0x0500); #MDMCTRL1
+        self.poke(0x1C, 0x007F); #IOCFG0
+        self.poke(0x19, 0x01C4); #SECCTRL0, disabling crypto
+        #self.poke(0x19, 0x0204); #SECCTRL0, as seen elsewhere.
+        #self.RF_setsync();
+        
+    def ident(self):
+        return self.peek(0x1E); #MANFIDL
+    def identstr(self):
+        manfidl=self.peek(0x1E);
+        #manfidh=self.peek(0x1f);
+        try:
+            return "%s" % (self.CCversions[manfidl]); 
+        except:
+            return "Unknown0x%04x" % manfidl;
+    def trans8(self,byte):
         """Read and write 8 bits by CCSPI."""
         data=self.CCSPItrans([byte]);
         return ord(data[0]);
     
-    def CCSPItrans(self,data):
+    def trans(self,data):
         """Exchange data by CCSPI."""
         self.data=data;
         self.writecmd(self.CCSPIAPP,0x00,len(data),data);
         return self.data;
-    
-    def peek(self,reg,bytes=-1):
+    def strobe(self,reg=0x00):
+        """Strobes a strobe register, returning the status."""
+        data=[reg];
+        self.trans(data);
+        return ord(self.data[0]);
+    def CC_RFST_IDLE(self):
+        """Switch the radio to idle mode, clearing overflows and errors."""
+        self.strobe(0x06); #SRXOFF
+    def CC_RFST_TX(self):
+        """Switch the radio to TX mode."""
+        self.strobe(0x04);  #0x05 for CCA
+    def CC_RFST_RX(self):
+        """Switch the radio to RX mode."""
+        self.strobe(0x03); #RX ON
+    def CC_RFST_CAL(self):
+        """Calibrate strobe the radio."""
+        self.strobe(0x02); #RX Calibrate
+    def CC_RFST(self,state=0x00):
+        self.strobe(state);
+        return;
+    def peek(self,reg,bytes=2):
         """Read a CCSPI Register.  For long regs, result is flipped."""
-        data=[reg,0,0,0,0,0];
+        
+        #Reg is ORed with 0x40 by the GoodFET.
+        data=[reg,0,0];
         
         #Automatically calibrate the len.
-        if bytes==-1:
-            bytes=1;
-            if reg==0x0a or reg==0x0b or reg==0x10: bytes=5;
+        bytes=2;
         
         self.writecmd(self.CCSPIAPP,0x02,len(data),data);
-        toret=0;
-        for i in range(0,bytes):
-            toret=toret|(ord(self.data[i+1])<<(8*i));
+        toret=(
+            ord(self.data[2])+
+            (ord(self.data[1])<<8)
+            );
         return toret;
-    def poke(self,reg,val,bytes=-1):
+    def poke(self,reg,val,bytes=2):
         """Write a CCSPI Register."""
-        data=[reg];
-        
-        #Automatically calibrate the len.
-        if bytes==-1:
-            bytes=1;
-            if reg==0x0a or reg==0x0b or reg==0x10: bytes=5;
-        
-        for i in range(0,bytes):
-            data=data+[(val>>(8*i))&0xFF];
+        data=[reg,(val>>8)&0xFF,val&0xFF];
         self.writecmd(self.CCSPIAPP,0x03,len(data),data);
-        if self.peek(reg,bytes)!=val and reg!=0x07:
-            print "Warning, failed to set r%02x=%02x, got %02x." %(reg,
-                                                                 val,
-                                                                 self.peek(reg,bytes));
-        return;
+        if self.peek(reg,bytes)!=val and reg!=0x18:
+            print "Warning, failed to set r%02x=0x%04x, got %02x." %(
+                reg,
+                val,
+                self.peek(reg,bytes));
+            return False;
+        return True;
     
     def status(self):
         """Read the status byte."""
-        status=self.peek(0x07);
-        print "Status=%02x" % status;
+        statusbits={0x80: "?",
+                    0x40: "XOSC16M_STABLE",
+                    0x20: "TX_UNDERFLOW",
+                    0x10: "ENC_BUSY",
+                    0x08: "TX_ACTIVE",
+                    0x04: "LOCK",
+                    0x02: "RSSI_VALID",
+                    0x01: "?"};
+        status=self.strobe(0x00);
+        i=1;
+        str="";
+        while i<0x100:
+            if status&i:
+               str="%s %s" % (statusbits[i],str);
+            i*=2;
+        return str;
     
     #Radio stuff begins here.
-    def RF_setenc(self,code="GFSK"):
+    def RF_setenc(self,code="802.15.4"):
         """Set the encoding type."""
-        if code!=GFSK:
-            return "%s not supported by the CCSPI24L01.  Try GFSK."
-        return;
+        return code;
     def RF_getenc(self):
         """Get the encoding type."""
-        return "GFSK";
+        return "802.15.4";
     def RF_getrate(self):
-        rate=self.peek(0x06)&0x28;
-        if rate==0x28:
-            rate=250*10**3; #256kbps
-        elif rate==0x08:
-            rate=2*10**6;  #2Mbps
-        elif rate==0x00: 
-            rate=1*10**6;  #1Mbps
-        return rate;
-    def RF_setrate(self,rate=2*10**6):
-        r6=self.peek(0x06); #RF_SETUP register
-        r6=r6&(~0x28);   #Clear rate fields.
-        if rate==2*10**6:
-            r6=r6|0x08;
-        elif rate==1*10**6:
-            r6=r6;
-        elif rate==250*10**3:
-            r6=r6|0x20;
-        print "Setting r6=%02x." % r6;
-        self.poke(0x06,r6); #Write new setting.
-    def RF_setfreq(self,frequency):
-        """Set the frequency in Hz."""
+        return 0;
+    def RF_setrate(self,rate=0):
+        return 0;
+    def RF_getsync(self):
+        return self.peek(0x14);
+    def RF_setsync(self,sync=0xa70F):
+        """Set the SYNC preamble.
+        Use 0xA70F for 0xA7."""
+        self.poke(0x14,sync);
+        return;
+    
+    def RF_setkey(self,key):
+        """Sets the first key for encryption to the given argument."""
+        print "ERROR: Forgot to set the key.";
         
-        #On the CCSPI24L01+, register 0x05 is the offset in
-        #MHz above 2400.
+        return;
+    def RF_setnonce(self,key):
+        """Sets the first key for encryption to the given argument."""
+        print "ERROR: Forgot to set the nonce.";
         
-        chan=frequency/1000000-2400;
-        self.poke(0x05,chan);
+        return;
+    
+    def RF_setfreq(self,frequency):
+        """Set the frequency in Hz."""
+        mhz=frequency/1000000;
+        #fsctrl=0x8000; #
+        fsctrl=self.peek(0x18)&(~0x3FF);
+        fsctrl=fsctrl+int(mhz-2048)
+        self.poke(0x18,fsctrl);
+        #self.CC_RFST_IDLE();
+        self.strobe(0x02);#SCAL
+        time.sleep(0.01);
+        self.strobe(0x03);#SRXON
     def RF_getfreq(self):
         """Get the frequency in Hz."""
-        
-        #On the CCSPI24L01+, register 0x05 is the offset in
-        #MHz above 2400.
-        
-        return (2400+self.peek(0x05))*10**6
-        self.poke(0x05,chan);
+        fsctrl=self.peek(0x18);
+        mhz=2048+(fsctrl&0x3ff)
+        return mhz*1000000;
+    def RF_setchan(self,channel):
+        """Set the ZigBee/802.15.4 channel number."""
+        if channel < 11 or channel > 26:
+            print "Only 802.15.4 channels 11 to 26 are currently supported.";
+        else:
+            self.RF_setfreq( ( (channel-11)*5 + 2405 ) * 1000000 );
     def RF_getsmac(self):
         """Return the source MAC address."""
-        
-        #Register 0A is RX_ADDR_P0, five bytes.
-        mac=self.peek(0x0A, 5);
-        return mac;
+        return 0xdeadbeef;
     def RF_setsmac(self,mac):
         """Set the source MAC address."""
-        
-        #Register 0A is RX_ADDR_P0, five bytes.
-        self.poke(0x0A, mac, 5);
-        return mac;
+        return 0xdeadbeef;
     def RF_gettmac(self):
         """Return the target MAC address."""
-        
-        #Register 0x10 is TX_ADDR, five bytes.
-        mac=self.peek(0x10, 5);
-        return mac;
+        return 0xdeadbeef;
     def RF_settmac(self,mac):
         """Set the target MAC address."""
-        
-        #Register 0x10 is TX_ADDR, five bytes.
-        self.poke(0x10, mac, 5);
-        return mac;
+        return 0xdeadbeef;
+    def RF_getrssi(self):
+        """Returns the received signal strength, with a weird offset."""
+        rssival=self.peek(0x13)&0xFF; #raw RSSI register
+        return rssival^0x80;
 
+    def peekram(self,adr,count):
+        """Peeks data from CC2420 RAM."""
+        data=[
+            adr&0xFF,adr>>8,     # Address first.
+            count&0xFF,count>>8  # Then length.
+            ];
+        self.writecmd(self.CCSPIAPP,0x84,len(data),data);
+        return self.data;
+    def pokeram(self,adr,data):
+        """Pokes data into CC2420 RAM."""
+        data=[adr&0xFF, adr>>8]+data;
+        self.writecmd(self.CCSPIAPP,0x85,len(data),data);
+        return;
+    
+    lastpacket=range(0,0xff);
     def RF_rxpacket(self):
-        """Get a packet from the radio.  Returns None if none is waiting."""
-        if self.peek(0x07) & 0x40:
-            #Packet has arrived.
-            self.writecmd(self.CCSPIAPP,0x80,0,None); #RX Packet
-            data=self.data;
-            self.poke(0x07,0x40);#clear bit.
-            return data;
-        elif self.peek(0x07)==0:
-            self.writecmd(self.CCSPIAPP,0x82,0,None); #Flush
-            self.poke(0x07,0x40);#clear bit.
+        """Get a packet from the radio.  Returns None if none is
+        waiting."""
+        
+        data="\0";
+        self.data=data;
+        self.writecmd(self.CCSPIAPP,0x80,len(data),data);
+        buffer=self.data;
+        
+        self.lastpacket=buffer;
+        if(len(buffer)==0):
+            return None;
+        
+        return buffer;
+    def RF_rxpacketrepeat(self):
+        """Gets packets from the radio, ignoring all future requests so as
+        not to waste time.  Call RF_rxpacket() after this."""
+        
+        self.writecmd(self.CCSPIAPP,0x91,0,None);
         return None;
+    
+    def RF_rxpacketdec(self):
+        """Get and decrypt a packet from the radio.  Returns None if
+        none is waiting."""
+        
+        data="\0";
+        self.data=data;
+        self.writecmd(self.CCSPIAPP,0x90,len(data),data);
+        buffer=self.data;
+        
+        self.lastpacket=buffer;
+        if(len(buffer)==0):
+            return None;
+        
+        return buffer;
+
+    def RF_txpacket(self,packet):
+        """Send a packet through the radio."""
+        self.writecmd(self.CCSPIAPP,0x81,len(packet),packet);
+        #time.sleep(1);
+        #self.strobe(0x09);
+        return;
+    
+    def RF_reflexjam(self,duration=0):
+        """Place the device into reflexive jamming mode."""
+        data = [duration&0xff,
+                (duration>>8)&0xff];
+        self.writecmd(self.CCSPIAPP,0xA0,len(data),data);
+        return;
+
+    def RF_reflexjam_autoack(self):
+        """Place the device into reflexive jamming mode
+           and that also sends a forged ACK if needed."""
+        data = "";
+        self.writecmd(self.CCSPIAPP,0xA1,len(data),data);
+        print "Got:", data, "and", self.data
+        return;
+
+    def RF_modulated_spectrum(self):
+        """Hold a carrier wave on the present frequency."""
+        # print "Don't know how to hold a carrier.";
+        # 33.1 p.55:
+        #  reset chip
+        #  SXOSCON
+        #  set MDMCTRL1.TX_MODE to 3   0x12  3:2 
+        #  STXON                            0x04
+
+        mdmctrl1=self.peek(0x12);
+        #print "mdmctrl1 was %04x" % mdmctrl1;
+        mdmctrl1=mdmctrl1|0x00c0;  #MDMCTRL1.TX_MODE = 3
+        self.poke(0x12, mdmctrl1); #MDMCTRL1
+
+        mdmctrl1=self.peek(0x12);
+        #print "mdmctrl1 is %04x" % mdmctrl1;
+
+        # http://e2e.ti.com/support/low_power_rf/f/155/t/15914.aspx?PageIndex=2
+        #   suggests this
+        self.strobe(0x02);         #STXCAL
+        #print "STXCAL status: %s" % self.status()
+
+        # is this necessary?
+        self.strobe(0x09);         #SFLUSHTX
+        #print "SFLUSHTX status: %s" % self.status()
+
+        self.strobe(0x04);         #STXON
+        #print "STXON status: %s" % self.status()
+
     def RF_carrier(self):
         """Hold a carrier wave on the present frequency."""
-        # Set CONT_WAVE, PLL_LOCK, and 0dBm in RF_SETUP            
-        self.poke(0x06,8+10+4+2); 
-        
+        # print "Don't know how to hold a carrier.";
+        # 33.1 p.54:
+        #  reset chip
+        #  SXOSCON
+        #  set MDMCTRL1.TX_MODE to 2 or 3   0x12  3:2 
+        #  set DACTST to 0x1800             0x2E
+        #  STXON                            0x04
+
+        mdmctrl1=self.peek(0x12);
+        #print "mdmctrl1 was %04x" % mdmctrl1;
+        mdmctrl1=mdmctrl1|0x0080; 
+        mdmctrl1=mdmctrl1&0x0080;  #MDMCTRL1.TX_MODE = 2
+        self.poke(0x12, mdmctrl1); #MDMCTRL1
+
+        mdmctrl1=self.peek(0x12);
+        #print "mdmctrl1 is %04x" % mdmctrl1;
+
+        self.poke(0x2E, 0x1800);   #DACTST
+        dactst=self.peek(0x2E);
+        #print "dactst is %04x" % dactst;
+
+        # see above for why this is here
+        self.strobe(0x02);         #STXCAL
+        #print "STXCAL status: %s" % self.status()
+        self.strobe(0x09);         #SFLUSHTX
+        #print "SFLUSHTX status: %s" % self.status()
+
+        self.strobe(0x04);         #STXON
+        #print "STXON status: %s" % self.status()
+
+    def RF_promiscuity(self,promiscuous=1):
+        mdmctrl0=self.peek(0x11);
+        if promiscuous>0:
+            mdmctrl0=mdmctrl0&(~0x800);
+        else:
+            mdmctrl0=mdmctrl0|0x800;
+        self.poke(0x11,mdmctrl0);
+        return;
+    def RF_autocrc(self,autocrc=1):
+        mdmctrl0=self.peek(0x11);
+        if autocrc==0:
+            mdmctrl0=mdmctrl0&(~0x0020);
+        else:
+            mdmctrl0=mdmctrl0|0x0020;
+        self.poke(0x11,mdmctrl0);
+        return;
+    def RF_autoack(self,autoack=1):
+        mdmctrl0=self.peek(0x11);
+        if autoack==0:
+            mdmctrl0=mdmctrl0&(~0x0010);
+        else:
+            mdmctrl0=mdmctrl0|0x0010;
+        self.poke(0x11,mdmctrl0);
+        return;
     packetlen=16;
     def RF_setpacketlen(self,len=16):
         """Set the number of bytes in the expected payload."""
-        self.poke(0x11,len);
+        #self.poke(0x11,len);
         self.packetlen=len;
     def RF_getpacketlen(self):
         """Set the number of bytes in the expected payload."""
-        len=self.peek(0x11);
+        #len=self.peek(0x11);
         self.packetlen=len;
         return len;
     maclen=5;
@@ -174,3 +363,25 @@ class GoodFETCCSPI(GoodFET):
         choice=choices[len];
         self.poke(0x03,choice);
         self.maclen=len;
+    def printpacket(self,packet,prefix="#"):
+        print self.packet2str(packet,prefix);
+    def packet2str(self,packet,prefix="#"):
+        s="";
+        i=0;
+        for foo in packet:
+            s="%s %02x" % (s,ord(foo));
+        return "%s%s" % (prefix,s);
+        
+    def printdissect(self,packet):
+        try:
+            from scapy.all import Dot15d4
+        except ImportError:
+            print "To use packet disection, Scapy must be installed and have the Dot15d4 extension present."
+            print "try: hg clone http://hg.secdev.org/scapy-com";
+            print "     sudo ./setup.py install";
+        self.printpacket(packet);
+        try:
+            scapyd = Dot15d4(packet[1:]);
+            scapyd.show();
+        except:
+            pass;