new maxusb library
[goodfet] / client / GoodFETMAXUSB.py
index d38bfa7..b918dd0 100644 (file)
@@ -7,9 +7,14 @@
 
 
 import sys, time, string, cStringIO, struct, glob, os;
+import warnings
 
 from GoodFET import GoodFET;
 
+warnings.warn(
+"""This library will soon be deprecated in favor of the USB*.py libraries."""
+)
+
 #Handy registers.
 rEP0FIFO=0
 rEP1OUTFIFO=1
@@ -203,6 +208,7 @@ bmHXFRDNIRQ     =0x80
 
 class GoodFETMAXUSB(GoodFET):
     MAXUSBAPP=0x40;
+    usbverbose=False;
     
     def service_irqs(self):
         """Handle USB interrupt events."""
@@ -219,10 +225,10 @@ class GoodFETMAXUSB(GoodFET):
             self.wreg(rEPIRQ,bmOUT1DAVIRQ); #Clear the bit *AFTER* servicing.
         if(epirq&bmIN3BAVIRQ): #IN3-IN packet
             self.do_IN3();
-            self.wreg(rEPIRQ,bmIN3BAVIRQ); #Clear the bit
+            #self.wreg(rEPIRQ,bmIN3BAVIRQ); #Clear the bit
         if(epirq&bmIN2BAVIRQ): #IN2 packet
             self.do_IN2();
-            self.wreg(rEPIRQ,bmIN2BAVIRQ); #Clear the bit
+            #self.wreg(rEPIRQ,bmIN2BAVIRQ); #Clear the bit
         #else:
         #    print "No idea how to service this IRQ: %02x" % epirq;
     def do_IN2(self):
@@ -231,7 +237,7 @@ class GoodFETMAXUSB(GoodFET):
         """Overload this."""
     def do_OUT1(self):
         """Overload this."""
-        print "Ignoring an OUT1 interrupt.";
+        if self.usbverbose: print "Ignoring an OUT1 interrupt.";
     def setup2str(self,SUD):
         """Converts the header of a setup packet to a string."""
         return "bmRequestType=0x%02x, bRequest=0x%02x, wValue=0x%04x, wIndex=0x%04x, wLength=0x%04x" % (
@@ -244,6 +250,8 @@ class GoodFETMAXUSB(GoodFET):
     def MAXUSBsetup(self):
         """Move the FET into the MAXUSB application."""
         self.writecmd(self.MAXUSBAPP,0x10,0,self.data); #MAXUSB/SETUP
+        self.writecmd(self.MAXUSBAPP,0x10,0,self.data); #MAXUSB/SETUP
+        self.writecmd(self.MAXUSBAPP,0x10,0,self.data); #MAXUSB/SETUP
         print "Connected to MAX342x Rev. %x" % (self.rreg(rREVISION));
         self.wreg(rPINCTL,0x18); #Set duplex and negative INT level.
         
@@ -286,7 +294,7 @@ class GoodFETMAXUSB(GoodFET):
         ashex="";
         for foo in toret:
             ashex=ashex+(" %02x"%ord(foo));
-        print "GET   %02x==%s" % (reg,ashex);
+        if self.usbverbose: print "GET   %02x==%s" % (reg,ashex);
         return toret;
     def readbytesAS(self,reg,length):
         """Peek some bytes from a register, acking prior transfer."""
@@ -296,8 +304,29 @@ class GoodFETMAXUSB(GoodFET):
         ashex="";
         for foo in toret:
             ashex=ashex+(" %02x"%ord(foo));
-        print "GETAS %02x==%s" % (reg,ashex);
+        if self.usbverbose: print "GETAS %02x==%s" % (reg,ashex);
         return toret;
+    def fifo_ep3in_tx(self,data):
+        """Sends the data out of EP3 in 64-byte chunks."""
+        #Wait for the buffer to be free before starting.
+        while not(self.rreg(rEPIRQ)&bmIN3BAVIRQ): pass;
+        
+        count=len(data);
+        pos=0;
+        while count>0:
+            #Send 64-byte chunks or the remainder.
+            c=min(count,64);
+            self.writebytes(rEP3INFIFO,
+                            data[pos:pos+c]);
+            self.wregAS(rEP3INBC,c);
+            count=count-c;
+            pos=pos+c;
+            
+            #Wait for the buffer to be free before continuing.
+            while not(self.rreg(rEPIRQ)&bmIN3BAVIRQ): pass;
+            
+        return;
+        
     def ctl_write_nd(self,request):
         """Control Write with no data stage.  Assumes PERADDR is set
         and the SUDFIFO contains the 8 setup bytes.  Returns with
@@ -370,7 +399,7 @@ class GoodFETMAXUSB(GoodFET):
             self.wreg(rHIRQ,bmRCVDAVIRQ); #Clear IRQ
             xfrlen=xfrlen+pktsize; #Add byte count to total transfer length.
             
-            print "%i / %i" % (xfrlen,xfrsize)
+            #print "%i / %i" % (xfrlen,xfrsize)
             
             #Packet is complete if:
             # 1. The device sent a short packet, <maxPacketSize
@@ -380,7 +409,7 @@ class GoodFETMAXUSB(GoodFET):
                 ashex="";
                 for foo in self.xfrdata:
                     ashex=ashex+(" %02x"%foo);
-                print "INPACKET EP%i==%s (0x%02x bytes remain)" % (endpoint,ashex,xfrsize);
+                #print "INPACKET EP%i==%s (0x%02x bytes remain)" % (endpoint,ashex,xfrsize);
                 return resultcode;
 
     RETRY_LIMIT=3;
@@ -413,13 +442,13 @@ class GoodFETMAXUSB(GoodFET):
         data="";
         if type(tosend)==str:
             data=chr((reg<<3)|3)+tosend;
-            print "PUT %02x:=%s (0x%02x bytes)" % (reg,tosend,len(data))
+            if self.usbverbose: print "PUT %02x:=%s (0x%02x bytes)" % (reg,tosend,len(data))
         else:
             data=[(reg<<3)|3]+tosend;
             ashex="";
             for foo in tosend:
                 ashex=ashex+(" %02x"%foo);
-            print "PUT %02x:=%s (0x%02x bytes)" % (reg,ashex,len(data))
+            if self.usbverbose: print "PUT %02x:=%s (0x%02x bytes)" % (reg,ashex,len(data))
         self.writecmd(self.MAXUSBAPP,0x00,len(data),data);
     def usb_connect(self):
         """Connect the USB port."""
@@ -449,9 +478,15 @@ class GoodFETMAXUSB(GoodFET):
         """Resets the chip into host mode."""
         self.wreg(rUSBCTL,bmCHIPRES); #Stop the oscillator.
         self.wreg(rUSBCTL,0x00);      #restart it.
-        while self.rreg(rUSBIRQ)&bmOSCOKIRQ:
-            #Hang until the PLL stabilizes.
-            pass;
+        
+        #FIXME: Why does the OSC line never settle?
+        #Code works without it.
+        
+        #print "Waiting for PLL to sabilize.";
+        #while self.rreg(rUSBIRQ)&bmOSCOKIRQ:
+        #    #Hang until the PLL stabilizes.
+        #    pass;
+        #print "Stable.";
 
 class GoodFETMAXUSBHost(GoodFETMAXUSB):
     """This is a class for implemented a minimal USB host.
@@ -459,10 +494,13 @@ class GoodFETMAXUSBHost(GoodFETMAXUSB):
     def hostinit(self):
         """Initialize the MAX3421 as a USB Host."""
         self.usb_connect();
+        print "Enabling host mode.";
         self.wreg(rPINCTL,(bmFDUPSPI|bmPOSINT));
+        print "Resetting host.";
         self.reset_host();
         self.vbus_off();
         time.sleep(0.2);
+        print "Powering host.";
         self.vbus_on();
         
         #self.hostrun();
@@ -659,6 +697,8 @@ class GoodFETMAXUSBHID(GoodFETMAXUSBDevice):
         #Parse the SETUP packet
         print "Handling a setup packet of %s" % self.setup2str(SUD);
         
+       self.OsLastConfigType=ord(SUD[bmRequestType]);
+       self.typepos=0;
         setuptype=(ord(SUD[bmRequestType])&0x60);
         if setuptype==0x00:
             self.std_request(SUD);
@@ -699,6 +739,7 @@ class GoodFETMAXUSBHID(GoodFETMAXUSBDevice):
         else:
             self.STALL_EP0(SUD);
     
+    OsLastConfigType=-1;
 #Device Descriptor
     DD=[0x12,                  # bLength = 18d
         0x01,                  # bDescriptorType = Device (1)
@@ -831,45 +872,86 @@ class GoodFETMAXUSBHID(GoodFETMAXUSBDevice):
         else:
             self.STALL_EP0(SUD);
 
-    
-    
-    typephase=0;
-    typestring="                      Python does USB HID!";
     typepos=0;
-    
+    typestrings={
+       -1   : "Python does USB HID!\n",                # Unidentified OS.  This is the default typestring.
+       0x00 : "OSX Hosts don't recognize Maxim keyboards.\n",  # We have to identify as an Apple keyboard to get arround the unknown keyboard error.
+       0xA1 : "Python does USB HID on Linux!\n",
+       0x81 : "                                                                                             Python does USB HID on Windows!\n",        # Windows requires a bit of a delay.  Maybe we can watch for a keyboard reset command?
+    }
+    def typestring(self):
+       if self.typestrings.has_key(self.OsLastConfigType):
+           return self.typestrings[self.OsLastConfigType];
+       else:
+           return self.typestrings[-1];
+    # http://www.win.tue.nl/~aeb/linux/kbd/scancodes-14.html
+    # Escape=0x29 Backsp=0x2A Space=0x2C CapsLock=0x39 Menu=0x65
+    keymaps={
+       'en_US'  :[ '    abcdefghijklmnopqrstuvwxyz1234567890\n\e\7f\t -=[]\\\\;\'`,./',
+                   '''    \ 1\ 2\ 3\ 4\ 5\ 6\a\b  \v\f\r\ e\ f\10 \12 \14\15\16\17\18\19\1a''',        # LeftCtrl
+                   '    ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()     {}?+||:"~<>?', # LeftShift
+                   '', # LeftCtrl & LeftShift
+                   '    \ea\eb\ec'], # LeftAlt
+       'Dvorak' :[ '    axje.uidchtnmbrl\'poygk,qf;1234567890\n\e\7f\t []/=\\\\s-`wvz',
+                   '''    \ 1\18 \ 5 \15 \ 4\ 3\b\14\ e\r\ 2\12\f \10\ f\19\a\v  \ 6                         \17\16\1a''',     # LeftCtrl
+                   '    AXJE UIDCHTNMBRL"POYGK<QF:!@#$%^&*()     {}?+||S_~WVZ', # LeftShift
+                   '', # LeftCtrl & LeftShift
+                   '    \ea\ex\ej'], # LeftAlt
+    }
+    layout='en_US';
+    def keymap(self):
+       return self.keymaps[self.layout];
+    modifiers={
+       'None':         0b00000000,
+       'LeftCtrl':     0b00000001,
+       'LeftShift':    0b00000010,
+       'LeftAlt':      0b00000100,
+       'LeftGUI':      0b00001000,
+       'RightCtrl':    0b00010000,
+       'RightShift':   0b00100000,
+       'RightAlt':     0b01000000,
+       'RightGUI':     0b10000000
+    }
+
     def asc2hid(self,ascii):
         """Translate ASCII to an USB keycode."""
-        a=ascii;
-        if a>='a' and a<='z':
-            return ord(a)-ord('a')+4;
-        elif a>='A' and a<='Z':
-            return ord(a)-ord('A')+4;
-        elif a==' ':
-            return 0x2C; #space
-        else:
-            return 0; #key-up
+       if type(ascii)!=str:
+           return (0,0);               # Send NoEvent if not passed a character
+        if ascii==' ':
+            return (0,0x2C);           # space
+       for modset in self.keymap():
+           keycode=modset.find(ascii);
+           if keycode != -1:
+               modifier = self.keymap().index(modset)
+               return (modifier, keycode);
+       return (0,0);
     def type_IN3(self):
-        """Type next letter in buffer."""
-        if self.typepos>=len(self.typestring):
-            self.typeletter(0);
-        elif self.typephase==0:
-            self.typephase=1;
-            self.typeletter(0);
-        else:
-            typephase=0;
-            self.typeletter(self.typestring[self.typepos]);
-            self.typepos=self.typepos+1;
-        return;
+       """Type next letter in buffer."""
+       string=self.typestring();
+       if self.typepos>=len(string):
+           self.typeletter(0);         # Send NoEvent to indicate key-up
+           exit(0);
+           self.typepos=0;             # Repeat typestring forever!
+           # This would be a great place to enable a typethrough mode so the host operator can control the target
+       else:
+           if self.usbverbose:
+               sys.stdout.write(string[self.typepos]);
+               sys.stdout.flush();
+           self.typeletter(string[self.typepos]);
+           self.typepos+=1;
+       return;
     def typeletter(self,key):
         """Type a letter on IN3.  Zero for keyup."""
-        #if type(key)==str: key=ord(key);
-        #Send a key-up.
-        self.wreg(rEP3INFIFO,0);
+       mod=0;
+       if type(key)==str:
+           (mod, key) = self.asc2hid(key);
+       self.wreg(rEP3INFIFO,mod);
         self.wreg(rEP3INFIFO,0);
-        self.wreg(rEP3INFIFO,self.asc2hid(key));
+        self.wreg(rEP3INFIFO,key);
         self.wreg(rEP3INBC,3);
     def do_IN3(self):
         """Handle IN3 event."""
         #Don't bother clearing interrupt flag, that's done by sending the reply.
-        self.type_IN3();
+       if self.OsLastConfigType != -1: # Wait for some configuration before stuffing keycodes down the pipe
+           self.type_IN3();