jtagarm: a few cleanup things...
[goodfet] / client / goodfet.maxusbmass
index c644827..0bda107 100755 (executable)
@@ -11,8 +11,78 @@ import time;
 
 from GoodFETMAXUSB import *;
 
+
+# This constant is kinda complicated and very ugly.  The idea is that
+# if we take too long in any given transaction, the host will abort.
+# How many blocks we can send depends upon timeouts on both sides,
+# with (at least in Linux) the behavior that aborting early causes the
+# disk to reset with only warning and no real errors.  Somewhere
+# there's a way to provide this constant to the host, in which case
+# stalling and waiting for a reset will no longer be necessary.
+
+MAXBLOCKSPERTRANSFER=128
+
+def zeros(length):
+    """Returns a list of zeroes of the specified length."""
+    l=range(0,length);
+    for foo in l:
+        l[foo]=0;
+    return l;
+
 class GoodFETMAXUSBMass(GoodFETMAXUSBDevice):
     """This emulates a USB Mass Storage device."""
+    
+    #Too much data to watch everything.
+    usbverbose=False;
+
+    def getSectorData(self,lba):
+        """Overload this to return data from a given 512-byte sector."""
+        print "You forgot to overload getSectorData().  Returning something neighborly.";
+        sector=[
+            0xE9, 0x86, 0x00, 0x0A, 0x47, 0x6F, 0x6F, 0x64, 0x44, 0x69, 0x73, 0x6B, 0x20, 0x30, 0x2E, 0x30,
+            0x31, 0x0A, 0x0D, 0x62, 0x79, 0x20, 0x54, 0x72, 0x61, 0x76, 0x69, 0x73, 0x20, 0x47, 0x6F, 0x6F,
+            0x64, 0x73, 0x70, 0x65, 0x65, 0x64, 0x0A, 0x0A, 0x0D, 0x00, 0x59, 0x6F, 0x75, 0x20, 0x68, 0x61,
+            0x76, 0x65, 0x20, 0x62, 0x65, 0x65, 0x6E, 0x20, 0x65, 0x61, 0x74, 0x65, 0x6E, 0x20, 0x62, 0x79,
+            0x20, 0x61, 0x20, 0x67, 0x72, 0x75, 0x65, 0x2E, 0x20, 0x20, 0x53, 0x6F, 0x72, 0x72, 0x79, 0x2E,
+            0x0A, 0x0D, 0x00, 0x31, 0x29, 0x20, 0x52, 0x65, 0x61, 0x64, 0x69, 0x6E, 0x67, 0x20, 0x6B, 0x65,
+            0x72, 0x6E, 0x65, 0x6C, 0x20, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x2E, 0x0A,
+            0x0D, 0x00, 0x32, 0x29, 0x20, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6E, 0x67, 0x20, 0x6B,
+            0x65, 0x72, 0x6E, 0x65, 0x6C, 0x2E, 0x0A, 0x0D, 0x00, 0xBE, 0x03, 0x7C, 0xE8, 0x41, 0x00, 0xE8,
+            0x7B, 0x00, 0x31, 0xC0, 0x30, 0xD2, 0xCD, 0x13, 0x0F, 0x82, 0xE8, 0x00, 0xBE, 0x53, 0x7C, 0xE8,
+            0x2E, 0x00, 0xB8, 0xE0, 0x07, 0x8E, 0xC0, 0x31, 0xDB, 0xB8, 0x10, 0x02, 0xB5, 0x00, 0xB1, 0x02,
+            0xB6, 0x00, 0xB2, 0x00, 0xCD, 0x13, 0x0F, 0x82, 0xCA, 0x00, 0xB8, 0x00, 0x7E, 0x89, 0xC6, 0xE8,
+            0x7C, 0x00, 0xBE, 0x72, 0x7C, 0xE8, 0x08, 0x00, 0xEA, 0x00, 0x00, 0xE0, 0x07, 0xE8, 0xB4, 0x00,
+            0xAC, 0x3C, 0x00, 0x74, 0x06, 0xB4, 0x0E, 0xCD, 0x10, 0xEB, 0xF5, 0xC3, 0x30, 0x78, 0x00, 0x20,
+            0x62, 0x79, 0x74, 0x65, 0x73, 0x20, 0x6F, 0x66, 0x20, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x20,
+            0x64, 0x65, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x2E, 0x0A, 0x0D, 0x00, 0x53, 0x65, 0x67, 0x6D,
+            0x65, 0x6E, 0x74, 0x73, 0x3A, 0x20, 0x00, 0x2C, 0x20, 0x00, 0x0A, 0x0D, 0x00, 0xBE, 0xDC, 0x7C,
+            0xE8, 0xBD, 0xFF, 0xE8, 0x63, 0x00, 0xE8, 0x07, 0x00, 0xBE, 0xDF, 0x7C, 0xE8, 0xB1, 0xFF, 0xC3,
+            0x89, 0xC3, 0xC1, 0xE8, 0x0C, 0xE8, 0x39, 0x00, 0x89, 0xD8, 0xC1, 0xE8, 0x08, 0xE8, 0x31, 0x00,
+            0x89, 0xD8, 0xC1, 0xE8, 0x04, 0xE8, 0x29, 0x00, 0x89, 0xD8, 0xE8, 0x24, 0x00, 0xC3, 0x31, 0xC9,
+            0xAD, 0xE8, 0xDC, 0xFF, 0xE8, 0x2C, 0x00, 0x83, 0xC1, 0x02, 0x81, 0xF9, 0x00, 0x02, 0x75, 0xF0,
+            0xC3, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45,
+            0x46, 0x50, 0x56, 0x83, 0xE0, 0x0F, 0x05, 0x51, 0x7D, 0x89, 0xC6, 0xAC, 0xB4, 0x0E, 0xCD, 0x10,
+            0x5E, 0x58, 0xC3, 0xB8, 0x20, 0x0E, 0xCD, 0x10, 0xC3, 0x31, 0xC0, 0xCD, 0x12, 0x72, 0x05, 0x85,
+            0xC0, 0x74, 0x01, 0xC3, 0xBE, 0x2A, 0x7C, 0xE8, 0x46, 0xFF, 0xEB, 0xFE, 0xEA, 0x00, 0x00, 0xFF,
+            0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
+            ];
+        return sector;
+    def putSectorData(self,lba,block):
+        """Overload this to write data to a given 512-byte sector."""
+        print "You forgot to overload putSectorData().  Ignoring sector write.";
+        return;
+    
+    def getSectorCount(self):
+        """Returns the number of viable Logical Block Addresses."""
+        print "You forgot to overload getSectorCount().  Guessing 0x200.";
+        return 0x200;
+    
     def massinit(self):
         """Initialize a USB Mass Storage device."""
         self.usb_disconnect();
@@ -22,8 +92,9 @@ class GoodFETMAXUSBMass(GoodFETMAXUSBDevice):
         
     def massrun(self):
         """Main loop of the USB Mass Storage emulator."""
-        print "Starting a Mass Storage device.  This doesn't work yet.";
+        print "Starting a Mass Storage device.";
         while 1:
+            sys.stdout.flush();
             self.service_irqs();
     def do_SETUP(self):
         """Handle USB Enumeration"""
@@ -168,21 +239,16 @@ class GoodFETMAXUSBMass(GoodFETMAXUSBDevice):
     
     def do_IN3(self):
         """Handle IN3 input event."""
-        #Don't bother clearing interrupt flag, that's done by sending the reply.
-        print "Got an input event, no idea what to do about it.";
-        #This would be for FTDI emulation.  Not for Mass Storage.
-        #self.wreg(rEP3INFIFO,0x01);      #Modem 
-        #self.wreg(rEP3INFIFO,0x00);      #Line 
-        #self.wreg(rEP3INFIFO,0x00);
-        #self.wregAS(rEP3INBC,0);
-        
+        # Do nothing here, as it'll be taken care of elsewhere.  The
+        # interrupt just means that the buffer is empty, not that we
+        # are expected to fill it.
         
     def do_OUT1(self):
         """Handle an OUT1 output event."""
-        print "Got an output event, printing the result.";
         l=self.rreg(rEP1OUTBC);
         frame=self.readbytes(rEP1OUTFIFO,l);
         self.handleCBW(frame);
+        
     lastCBW="";
     def handleCBW(self,cbw):
         """Handles an incoming Command Block Wrapper.  See USB Mass
@@ -207,38 +273,29 @@ class GoodFETMAXUSBMass(GoodFETMAXUSBDevice):
         
     def handleCB(self,cb,cblen,dtlen,dtdir):
         """Handles a command block, then replies with a CSW."""
-        print "\nGot command block 0x%02x, requesting 0x%02x bytes" % (
-            ord(cb[0]), dtlen);
+        if self.usbverbose:
+            print "Got command block, type 0x%02x requesting 0x%02x bytes" % (
+                ord(cb[0]), dtlen);
         verb=ord(cb[0]);
         status=00; #good, set to 1 for bad.
         if verb==0x00: # Test Unit Ready
-            response=[0,0,0,0,0,0];
-            self.writebytes(rEP3INFIFO,
-                       response);
-            self.wreg(rEP3INBC,len(response));
-            while not(self.rreg(rEPIRQ)&bmIN3BAVIRQ):
-                #Wait for the packet to complete before sending the next.
-                print "Waiting to complete inquiry."
-                pass;
+            # Send nothing, just the success code.
+            status=0;
         elif verb==0x03: # Request Sense
             print "Responding to Request Sense.  Needed for Macs.";
             response=[0x70, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0A,
                       0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00,
                       0x00, 0x00,
                       0,0,0,0,0];
-            status=1;
+            #status=1;
             self.writebytes(rEP3INFIFO,
                             response);
             self.wreg(rEP3INBC,len(response));
-            #while not(self.rreg(rEPIRQ)&bmIN3BAVIRQ):
-            #    #Wait for the packet to complete before sending the next.
-            #    print "Waiting to complete inquiry."
-            #    pass;
         elif verb==0x12: #Inquiry
             #print "Responding to CB inquiry.";
             response=[
                 0x00, # 00 for Direct, 1F for "no floppy"
-                0x80, # make 0x80 for removable media
+                0x80, # make 0x80 for removable media, 0x00 for fixed
                 0x00, # Version
                 0x01, # Response Data Format
                 0x1f, #Additional length.
@@ -251,6 +308,8 @@ class GoodFETMAXUSBMass(GoodFETMAXUSBDevice):
                 ord('0'),ord('.'),ord('0'),ord('1')]
             #print "Sending %i byte reply to %i byte query." % (
             #        len(response),dtlen);
+            while len(response)<dtlen:
+                response=response+[0];
             #while not(self.rreg(rEPIRQ)&bmIN3BAVIRQ):
             #    #Wait for the packet to complete before sending the next.
             #    print "Waiting to complete inquiry."
@@ -265,14 +324,90 @@ class GoodFETMAXUSBMass(GoodFETMAXUSBDevice):
             #    #Wait for the packet to complete before sending the next.
             #    print "Waiting to complete inquiry."
             #    pass;
+        elif verb==0x1e: #Prevent/Allow Removal
+            # Give a good status to pretend we understand.
+            status=0x00;
+        elif verb==0x1A or verb==0x5A: #Mode Sense (6 or 10)
+            # I should probably send six bytes here.
+            page=ord(cb[2])&0x3F;
+            print "Mode Sense (6) requesting %i byte Page Code %02x" % (
+                dtlen,page);
+            #This is completely wrong.
+            response=[0x07,0,0,0, 0,0,0,0x1C];
+            # response=[0x37,0x00,0x00,0x08,0x00,0x00,0x00,0x00, 0x00,0x00,0x02,0x00,0x01,0x0a,0x80,0x00,
+            #           0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x08,0x12,0x04,0x00,0x00,0x00,0x00,0x00,
+            #           0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x0a,0x0a,0x02,0x00,
+            #           0x00,0x00,0x00,0x00,0xff,0xff,0x00,0x1e];
+            if page!=0x3f:
+                print "Unknown page, returning empty page.";
+                response=[0x07,0,0,0, 0,0,0,0];
+            self.fifo_ep3in_tx(response);
+        elif verb==0x23: #Read Format Capacity
+            response=[
+                0x00, 0,0x00,0x08,    # Capacity list length.
+                0,0x00,0x10,0x00,     # Number of sectors, implying 10MB.  Should come from image.
+                0x01,0x00,            # reserved/desciptor code.
+                0x02,0x00             # 512 bytes/sector.  Why is this twice?
+                ];
+            self.writebytes(rEP3INFIFO,
+                            response);
+            self.wregAS(rEP3INBC,
+                        len(response));
+        elif verb==0x25: #Read Capacity
+            lastlba=self.getSectorCount();
+            response=[
+                #0x00, 0, 0x0f, 0xFF, # Last LBA
+                (lastlba>>24)&0xFF, (lastlba>>16)&0xFF, (lastlba>>8)&0xFF, lastlba&0xFF, # Last LBA
+                0x00,0x00,0x02,0x00   # Block length of 512 bytes.
+                ];
+            self.writebytes(rEP3INFIFO,
+                            response);
+            self.wregAS(rEP3INBC,
+                        len(response));
+        elif verb==0x28: #READ SECTOR
+            cbw=self.lastCBW;
+            baselba=(
+                ord(cbw[20]) |
+                (ord(cbw[19])<<8) |
+                (ord(cbw[18])<<16) |
+                (ord(cbw[17])<<24)
+                );
+            count=dtlen/512;
+            print "Fetching %i blocks starting at  LBA %i." % (count,baselba);
+            if count>MAXBLOCKSPERTRANSFER:
+                count=0;
+                #status=1; #Fail if we're asked to read more than 32 blocks.
+                #Now we need to stall EP3.  It's not acceptable to just forget to transmit.
+                self.wreg(rEPSTALLS,0x10);
+                return;
+            for i in range(0,count):
+                data=self.getSectorData(baselba+i);
+                self.fifo_ep3in_tx(data);
+                
+                # for j in range(0,8):
+                #     #print "Sending block fragment %i,%i" % (i,j);
+                #     #Transmit each 64-byte block fragment, then wait for next.
+                #     while not(self.rreg(rEPIRQ)&bmIN3BAVIRQ): pass;
+                #     response=data[j*64:j*64+64];
+                #     self.writebytes(rEP3INFIFO,
+                #                     response);
+                #     self.wregAS(rEP3INBC,
+                #                 64);
+            #sys.exit();
+        elif verb==0x2A: #WRITE SECTOR
+            print "Haven't implemented WRITE SECTOR.";
+            #sys.exit();
+        elif verb==0x1B: #EJECT/RETRACT DISK
+            print "Haven't implemented SCSI Start Stop Unit Command (1B)";
+            print "https://en.wikipedia.org/wiki/SCSI_Start_Stop_Unit_Command";
+            #sys.exit();
         else:
-            print "ERROR: Unknown command block verb %02x." % verb;
-            status=1; #Command Failed
+            print "ERROR: Unknown SCSI command block verb %02x." % verb;
+            status=0x02; #Command Failed
             if dtlen>0:
-                print "Exiting, as %i bytes are expected to be transfered toward %i." % (
-                    dtlen,dtdir);
-                    
-                sys.exit();
+                print "Sending %i bytes of dummy data here." % dtlen;
+                self.fifo_ep3in_tx(zeros(dtlen));
+            #sys.exit(1);
         cbw=self.lastCBW;
         
         #Now we need to send the CSW.
@@ -282,7 +417,7 @@ class GoodFETMAXUSBMass(GoodFETMAXUSBDevice):
             #CBW key; must be the same as the one we're replying to.
             ord(cbw[4]),ord(cbw[5]),ord(cbw[6]),ord(cbw[7]),
             #CSW Data Residue, probably oughtn't be zeroed.
-            0,0,0,
+            0,0,0,0,
             #Status byte: 00 for good, 01 for bad.
             status];
         self.writebytes(rEP3INFIFO,
@@ -290,12 +425,75 @@ class GoodFETMAXUSBMass(GoodFETMAXUSBDevice):
         self.wregAS(rEP3INBC,len(csw));
         
         
-        self.wreg(rEPIRQ,bmIN3BAVIRQ); #Clear the IRQ bit.
         return;
+
+class GoodFETMAXUSBMassFile(GoodFETMAXUSBMass):
+    """This emulates a USB Mass Storage Device, providing a file as
+    its image.  Writes are not yet supported, and this is very slow.
+    Performance hacks will come after the code stabilizes."""
+    
+    datafile=None;
+    datafilelen=None;
+    def openImage(self,filename):
+        """Opens an image for use.  Call this *before* massinit()."""
+        self.datafile=open(filename,"rb");
+        print "Opened an image with %i blocks." % self.getSectorCount();
+    def putSectorData(self,lba,block):
+        """Writes a 512-byte sector to the lba address."""
+        print "Writes aren't yet supported.";
+        return;
+    
+    def getSectorData(self,lba):
+        """Returns data from a 512-byte sector."""
+        toret="";
+        
+        #Seek to the appropraite block.
+        #print "Seeking to position %i"% (lba*512)
+        self.datafile.seek(lba*512,0);
+        pos=self.datafile.tell();
+        if pos!=lba*512:
+            print "SEEK ERROR: Seeked to %i (lba=%i), but now I'm at %i (lba=%i)" % (
+                lba*512,lba,
+                pos,pos/512);
+            #sys.exit();
+        
+        #Dump the data out, assuming no blocking and filling with nonsense.
+        toret=self.datafile.read(512);
+        if len(toret)<512:
+            print "Holy hell, I only have %i bytes of 512." % len(toret);
+        
+        toretbytes=range(0,len(toret));
+        for b in range(0,len(toret)): toretbytes[b]=ord(toret[b]);
+        return toretbytes;
+        
+    def getSectorCount(self):
+        """Returns the number of viable Logical Block Addresses."""
+        
+        # Python must have a better way to read a file length, but for
+        # now we just read the whole damned thing and then throw it
+        # away.  With present performance, we can't read anything
+        # large enough for this to be a problem.
+        if self.datafilelen==None:
+            self.datafile.seek(0);
+            self.datafilelen=len(self.datafile.read());
+        if self.datafilelen%512!=0:
+            print "ERROR: Image does not have an integer number of blocks!"
+            print "%i \% 512 == %i" % (self.datafilelen,
+                                       self.datafilelen%512);
+            sys.exit();
+        return self.datafilelen/512-1;
+
+if(len(sys.argv)==1):
+    print "Usage: %s disk.img\n" % sys.argv[0];
+    sys.exit();
+
+
 #Initialize FET and set baud rate
-client=GoodFETMAXUSBMass();
+client=GoodFETMAXUSBMassFile();
 client.serInit()
 
+client.openImage(sys.argv[1]);
+
 client.MAXUSBsetup();
 client.massinit();