Quelltext /~heha/basteln/PC/fx2/fx2ata-200917.zip/ide.c

//-----------------------------------------------------------------------------
//  Copyright (c) 1999 Cypress Semiconductor, Inc. All rights reserved
//-----------------------------------------------------------------------------
//
// This file contains the IDE specific portions of the code.  In ATAPI
// or SCSI applications, this file should not be needed.
//
// $Archive: /USB/atapifx2/CY4611B/ide.c $
// $Date: 6/26/05 1:57p $
// $Revision: 7 $
//-----------------------------------------------------------------------------
#include "Fx2.h"
#include "Fx2regs.h"
#include "gpif.h"
#include "scsi.h"
#include "globals.h"

static DWORD dwLBA;     // This is global to help the optimizer
static WORD wCmdSectorCount;     // Sector count from the read or write command
WORD stuffLBAandSector();      // Stuff the LBA registers, returns sectorCount
static void IDEPrepareForXfer();
static BYTE loadSensePtrFromErrorRegister(bit readWrite);

char sensePtr;

/////////////////////////////////////////////////////////////////////////////////
#if DEVICE_TYPE_IS_IDE
/////////////////////////////////////////////////////////////////////////////////

// From SCSI spec SPC (SCSI primary commands)
// Byte 0 -- 70 = Current error
// Byte 1 -- Segment number
// Byte 2 -- Sense key (SPC table 107)
//             5 = ILLEGAL REQUEST. Indicates that there was an illegal parameter in the CDB or in the additional parameters supplied as data for some commands
//             B = ABORTED COMMAND. Indicates that the device server aborted the command. The application client may be able to recover by trying the command again.
//             E = MISCOMPARE. Indicates that the source data did not match the data read from the medium.
// Byte 3-6 -- Information (not used)
// Byte 7 -- add'l sense length 
// byte 8-11 -- Command specific information
// byte 12 -- ASC (Add'l sense code)
// byte 13 -- ASQ (Add'l sense qualifier)
//                                                         Key                                                         ASC   ASQ
//                                              0     1     2     3     4     5     6     7     8     9    10    11    12    13    14    15
//const char code senseTemplate[] =          {0x70,  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};   

//                                          SK    ASC   ASQ

const char code senseArray[senseWriteProtected+1][3] = 
{{0x0b, 0x08, 0x03},   // senseCRCError               0 // Set on CRC error.  Causes host to retry  
{0x05, 0x24, 0x00},    // senseInvalidFieldInCDB      1// 300 calls this InvalidCommandField   
{0x00, 0x00, 0x00},    // senseOk                     2
{0x02, 0x3a, 0x00},    // senseNoMedia                3
{0x03, 0x03, 0x00},    // senseWriteFault             4
{0x03, 0x11, 0x00},    // senseReadError              5
{0x03, 0x12, 0x00},    // senseAddrNotFound           6
{0x05, 0x20, 0x00},    // senseInvalidOpcode          7
{0x05, 0x21, 0x00},    // senseInvalidLBA             8
{0x05, 0x26, 0x00},    // senseInvalidParameter       9
{0x05, 0x53, 0x02},    // senseCantEject              0xa
{0x06, 0x28, 0x00},    // senseMediaChanged           0xb
{0x06, 0x29, 0x00},    // senseDeviceReset            0xc// Initial value.  Set in ATARESET.c
{0x07, 0x27, 0x00}};   // senseWriteProtected         0xd


  
static BYTE ideReadCommand(BYTE command);

BYTE generalIDEInCommand()
{
   BYTE cmd;

   bShortPacketSent = 0;
   
   cmd = EP2FIFOBUF[0xf];  

   switch (cmd)
   {
      // Minimum processing for a case in this switch statement:
      case INQUIRY:
      {
         BYTE i;
         
         // clear out the bottom of EP6FIFOBUF.  This is where we are going to build
         // the response to the IDENTIFY command.
         AUTOPTR2L = LSB(EP6FIFOBUF);
         for (i = SCSI_IDENTIFY_LEN; i; i--)
            XAUTODAT2 = 0;

         // Standard INQUIRY command
         if ((EP2FIFOBUF[CBW_DATA_START+1] & 0x3) == 0x0)
            {   
            waitForInBuffer();
            // CompactFlash uses a lot of local stuff
            if (bCompactFlash)
               {
               AUTOPTR2L = LSB(EP6FIFOBUF);
   
               // Clear out the EP buffer
               for (i = SCSI_IDENTIFY_LEN; i; i--)
                  XAUTODAT2 = 0;
   
               // Load fields we use
               EP6FIFOBUF[SCSI_INQUIRY_REMOVABLE_BYTE] |= SCSI_INQUIRY_REMOVABLE_BIT;
   
               mymemmovexx(EP6FIFOBUF+SCSI_INQUIRY_MANUFACTURER, (BYTE xdata*)"Cypress Semi", sizeof("Cypress Semi"));
               }
            else
               {
               SendDeviceIdentifyCommand(1);
   
               // No need to disable interrupts here because we are expected to own EP6 at this point.
               FetchDeviceIdentifyIntoEp6();
      
               // copy the IDENTIFY data into the top of EP6FIFOBUF
               mymemmovexx(EP6FIFOBUF+256, EP6FIFOBUF, 256);
      
               // clear out the bottom of EP6FIFOBUF.  This is where we are going to build
               // the response to the IDENTIFY command.
               AUTOPTR2L = LSB(EP6FIFOBUF);
               for (i = SCSI_IDENTIFY_LEN; i; i--)
                  XAUTODAT2 = 0;
      
               // Place the fields that we need into the SCSI block
               AUTOPTR2L = LSB(EP6FIFOBUF+SCSI_INQUIRY_MANUFACTURER);
               for (i = 0; i < SCSI_INQUIRY_MANUFACTURER_LEN; i++)
                  {
                  // swap bytes within words.  This is stored backwards!
                  XAUTODAT2 = EP6FIFOBUF[(ATAPI_INQUIRY_MANUFACTURER * 2 + (i ^ 1)) + 256];
                  }
               
               XAUTODAT2 = '0';
               XAUTODAT2 = EP6FIFOBUF[(ATAPI_INQUIRY_REVISION * 2) + 256]+'0';
               XAUTODAT2 = '0';
               XAUTODAT2 = EP6FIFOBUF[(ATAPI_INQUIRY_REVISION * 2 +2) + 256]+'0';
               EP6FIFOBUF[SCSI_INQUIRY_REMOVABLE_BYTE] |= SCSI_INQUIRY_REMOVABLE_BIT & EP6FIFOBUF[(ATAPI_INQUIRY_REMOVABLE_BYTE) + 256];
               }
   
            if (currentLunNum == 0 && mx2_config_data.Lun0String)
               mymemmovexx(EP6FIFOBUF+SCSI_INQUIRY_MANUFACTURER, (BYTE xdata *)DeviceDscr + (mx2_config_data.Lun0String) - 16, SCSI_IDENTIFY_LEN); 
            else if (currentLunNum == 1 && mx2_config_data.Lun1String)
               mymemmovexx(EP6FIFOBUF+SCSI_INQUIRY_MANUFACTURER, (BYTE xdata *)DeviceDscr + (mx2_config_data.Lun1String) - 16, SCSI_IDENTIFY_LEN); 
   
            EP6FIFOBUF[SCSI_INQUIRY_REVISION_LEN] = 0x1F;
            loadEP6BC(SCSI_IDENTIFY_LEN);
            }
         // Respond to serial number page -- EVPD = 1 and page 0x80
         //
         // BUG - What should we do in the CF case?  The CF isn't there to give us the serial number.  Use our USB #?  Use the other devices's number?
         else if ((EP2FIFOBUF[CBW_DATA_START+1] & 0x3) == 1 && EP2FIFOBUF[CBW_DATA_START+2] == 0x80)
            {
            sensePtr = senseOk;
            checkForMedia(1);
   
            if (sensePtr != senseOk)
               {
               // relinquish control of the bulk buffer occupied by the CBW
               EP2BCL = 0x80;     
               
               failedIn();         
               return(USBS_FAILED);
               }

            SendDeviceIdentifyCommand(1);
   
            FetchDeviceIdentifyIntoEp6();
   
            // copy the IDENTIFY data into the top of EP6FIFOBUF
            mymemmovexx(EP6FIFOBUF+256, EP6FIFOBUF, 256);
   
            // clear out the bottom of EP6FIFOBUF.  This is where we are going to build
            // the response to the IDENTIFY command.
            AUTOPTR2L = LSB(EP6FIFOBUF);
            for (i = SCSI_IDENTIFY_LEN; i; i--)
               XAUTODAT2 = 0;
            
            // First byte is the PDT (peripheral device type).  It's already set to 0 above
            AUTOPTR2L = LSB(EP6FIFOBUF+1);
            XAUTODAT2 = 0x80;              // Page code = 0x80
            XAUTODAT2 = 0;                 // Reserved
            XAUTODAT2 = 8;                 // Thomson requested 8 bytes of data.  The ATA device will return 20 bytes.
            for (i = 0; i < 8; i++)
               XAUTODAT2 = EP6FIFOBUF[256+ATAPI_INQUIRY_SERIAL+i];
            loadEP6BC(12);
            }
         else
            {
            failedIn();
            sensePtr = senseInvalidFieldInCDB;
            EP2BCL = 0x80;     
            return(USBS_FAILED);
            }
         // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;     
         sensePtr = senseOk;
         return(USBS_PASSED);
      }
      
      case READ_06:
      case READ_10:
      case VERIFY_10:
      {
         sensePtr = senseOk;
         checkForMedia(1);

         if (sensePtr == senseOk)
         {
            return(ideReadCommand(cmd));   
         }
         else
         {
            // relinquish control of the bulk buffer occupied by the CBW
            EP2BCL = 0x80;     
            
            failedIn();         
            return(USBS_FAILED);
         }
      }
                
      case SEEK_10:
      {
         sensePtr = senseOk;
         checkForMedia(1);

         if (sensePtr == senseOk)
            {
            // Cannot just call readcommand because it aborts on no dataTransferLen.
            IDEPrepareForXfer();
            stuffLBAandSector();
            writePIO8(ATA_COMMAND_REG, ATA_COMMAND_SEEK);

            if(waitForBusyBit() == USBS_FAILED)
               {
               loadSensePtrFromErrorRegister(1);
               return(USBS_FAILED);
               }
            else
               return(USBS_PASSED);
            }
         else
            {
	         // relinquish control of the bulk buffer occupied by the CBW
	         EP2BCL = 0x80;     

            failedIn();         
            return(USBS_FAILED);
            }
      }                

      case READ_FORMAT_CAPACITIES:
      case READ_CAPACITY:
      {
         BYTE num_bytes = 8;

         // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;     
         
         sensePtr = senseOk;
         checkForMedia(1);
                 
         waitForInBuffer();
         if (sensePtr == senseOk)
         {
            AUTOPTR2L = LSB(EP6FIFOBUF);
            if(cmd == READ_FORMAT_CAPACITIES) // add 4 byte capacity list header
            {
               XAUTODAT2 = 0x0;
               XAUTODAT2 = 0x0;
               XAUTODAT2 = 0x0;
               XAUTODAT2 = 0x08;
               num_bytes = 12;
            }
            XAUTODAT2 = ((BYTE *) &ActiveLunConfigData.driveCapacity)[0];
            XAUTODAT2 = ((BYTE *) &ActiveLunConfigData.driveCapacity)[1];
            XAUTODAT2 = ((BYTE *) &ActiveLunConfigData.driveCapacity)[2];
            XAUTODAT2 = ((BYTE *) &ActiveLunConfigData.driveCapacity)[3];
            if(cmd == READ_FORMAT_CAPACITIES) 
               XAUTODAT2 = ((ATA_SECTOR_SIZE >> 24) & 0xff) | 2;     // Report media type -- Formatted
            else
               XAUTODAT2 = (ATA_SECTOR_SIZE >> 24) & 0xff;
            XAUTODAT2 = (ATA_SECTOR_SIZE >> 16) & 0xff;
            XAUTODAT2 = (ATA_SECTOR_SIZE >>  8) & 0xff;
            XAUTODAT2 = (ATA_SECTOR_SIZE >>  0) & 0xff;

            loadEP6BC(num_bytes);
            return(USBS_PASSED);
         }
         else
         {
            failedIn();
            return(USBS_FAILED);
         }
      }

      ///////////////////////////////////////////////////////////
      // Spoofed commands
      case MODE_SELECT_06:    // Note that these are in BOTH the read and write areas in case they are sent with no data     
      case MODE_SELECT_10:
      {
      // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;     
         
      // This command is allowed to have data.         
         sensePtr = senseOk;
         checkForMedia(1);
         
         if (sensePtr == senseOk)
            return(USBS_PASSED);
         else
            return(USBS_FAILED);
      }                

      case TEST_UNIT_READY:
      case FORMAT_UNIT:
      {
      // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;     
         
         if (dataTransferLen)    // This command shouldn't have any data!
            failedIn();         
         
         sensePtr = senseOk;
         checkForMedia(1);
         
         if (sensePtr == senseOk)
            return(USBS_PASSED);
         else
            return(USBS_FAILED);
      }                
      case REQUEST_SENSE:
      {
         // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;     
   
         waitForInBuffer();

         AUTOPTR2L = LSB(EP6FIFOBUF);

         // First two bytes are 0x70 and 0x00
         XAUTODAT2 = 0x70;
         XAUTODAT2 = 0x00;
         // Clear the rest of the buffer
         mymemmovexx(EP6FIFOBUF+2, EP6FIFOBUF+1, 18-2);
         XAUTODAT2 = senseArray[sensePtr][0];

         AUTOPTR2L = 7;
         XAUTODAT2 = 18-8;    // Length of data following this byte

         AUTOPTR2L = 12;
         XAUTODAT2 = senseArray[sensePtr][1];
         XAUTODAT2 = senseArray[sensePtr][2];

         loadEP6BC(18);
         sensePtr = senseOk;
         return(USBS_PASSED);
      }
      case STOP_START_UNIT:
      case PREVENT_ALLOW_MEDIUM_REMOVAL:
         {
         BYTE options;

         options = EP2FIFOBUF[CBW_DATA_START+4];
         // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;     
         
         if (dataTransferLen)    // This command shouldn't have any data!
            failedIn();         

         writeATA_DRIVESEL_REG();
         sensePtr = senseOk;
         checkForMedia(1);
         if (sensePtr != senseOk)
            return(USBS_FAILED);

         waitForBusyBit();
         if (cmd == PREVENT_ALLOW_MEDIUM_REMOVAL)
            {
            if (options & 1)
               writePIO8(ATA_COMMAND_REG, ATA_COMMAND_MEDIA_LOCK);
            else
               {
               flushCache();
               writePIO8(ATA_COMMAND_REG, ATA_COMMAND_MEDIA_UNLOCK);
               }

            // Cannot really lock the CF into the drive.  Forces Windows to poll us.
            if (bCompactFlash)
               return(USBS_FAILED);
            }
         else      // STOP_START_UNIT:
            {
            if (options & 0xf0)
               {
               if ((options & 0xf0) > 0x10 &&
                   (options & 0xf0) < 0x70)
                     writePIO8(ATA_COMMAND_REG, ATA_COMMAND_STANDBY_IMMEDIATE);
               }
            else if ((options & 3) == 2)
               {
               if (bCompactFlash)
                  ejected = 1;
               else
                  writePIO8(ATA_COMMAND_REG, ATA_COMMAND_MEDIA_EJECT);
               }
            }
         waitForBusyBit();

         if (readATAPI_STATUS_REG() & ATAPI_STATUS_ERROR_BIT)
            {
            BYTE error = loadSensePtrFromErrorRegister(1);
            if (error & 4)  // command not supported -- Non removable media, return PASS
               {
               sensePtr = senseOk;
               return(USBS_PASSED);
               }
            else if ((error & 2) && (options & 3) == 2)
               sensePtr = senseCantEject;
            return(USBS_FAILED);
            }
         else
            return(USBS_PASSED);
         break;
         }

      case MODE_SENSE_06:
      case MODE_SENSE_10:
      {               
         BYTE pagenum;
         
         pagenum = EP2FIFOBUF[CBW_DATA_START+2] & 0x3F; // identify page (see p.141 SCSI 2ed.)
   
         EP2BCL = 0x80; // relinquish control of the bulk buffer occupied by the CBW
         
         waitForInBuffer();
         
         if((pagenum != 0x05) && (pagenum != 0x3F)
             && (pagenum != 0x01) && (pagenum != 0x08) && (pagenum != 0x1B))
         { // only respond to requests for certain pages (the mandatory ones plus page 5)
            sensePtr = senseInvalidFieldInCDB;
            failedIn();
            return(USBS_FAILED);
         }
   
         // If one of the supported pages is requested, return the 8 byte Mode Parameter Header
         // plus a single 12 byte page. Only the Mode Data length (LSB) is significant in the
         // Mode Parameter Header. It has a Vendor Specific Medium Type Code in byte 2, and
         // a 1 bit WP Write Protect bit in byte 3 that are not initialized here.
         // Pages 0x01, and 0x08 do not have significant data - they are spoofed.
         // Page 0x1B has a TLUN field in byte 3 that is initialized to 1 (as per ISD).
         // Page 0x05 does contain information that is needed to boot to HDD and CDROM.
         // Page 0x3F, All Pages, is also responded to.
         // The supported pages are (see INF-8070_1_3.pdf p37 Table 25):
         //   case 0x01:                          // Read-Write Error Recovery Page
         //   case 0x08:                          // Caching Page
         //   case 0x05:                          // Flexible Disk Page: needed to boot from USB
         //   case 0x1B:                          // Removable Block Access Capabilities Page
         //   case 0x3F:                          // All (4) Pages
         
         // The format used is:
         //    Mode parameter header (4 or 8 bytes)
         //    Block descriptors (not supported)
         //    Mode page
         EP6FIFOBUF[0] = 0x00;
         mymemmovexx(EP6FIFOBUF+1, EP6FIFOBUF, 200-1); // clear buffer - <200 bytes in all responses

         if (cmd == MODE_SENSE_10)
            {
            AUTOPTR2L = LSB(EP6FIFOBUF) + 8;     // The rest of the header is zeroes.  Start the pages at byte 8.     
            }
         else
            {
            AUTOPTR2L = LSB(EP6FIFOBUF) + 4;          
            }

         if((pagenum == 0x05) || (pagenum == 0x3F))
            {  
            XAUTODAT2 = 5;               // fill out the page num - fields are all 0x0
            XAUTODAT2 = 12-2;            // set individual Page Length -- return 12 for all commands

            if(EZUSB_HIGHSPEED())
               {
               XAUTODAT2 = 0xFF;         // HS Transfer Rate (MSB) (field limited to 65Mb/Sec)
               XAUTODAT2 = 0xFF;         // HS Transfer Rate (LSB)
               }
            else
               {
               XAUTODAT2 = 0x2E;         // FS Transfer Rate (MSB) (12Mb/Sec) 
               XAUTODAT2 = 0xE0;         // FS Transfer Rate (LSB)
               }
            XAUTODAT2 = ActiveLunConfigData.NumHeads;        // #Heads
            XAUTODAT2 = ActiveLunConfigData.NumSectPerTrack; // #SectorsPerTrack
            XAUTODAT2 = (ATA_SECTOR_SIZE >>  8) & 0xff; // Data Bytes per sector (truncated)
            XAUTODAT2 = (ATA_SECTOR_SIZE >>  0) & 0xff; // Data Bytes per sector
            XAUTODAT2 = ActiveLunConfigData.NumCylindersMSB; // #Cyl MSB
            XAUTODAT2 = ActiveLunConfigData.NumCylindersLSB; // #Cyl LSB
            XAUTODAT2 = 0;
            XAUTODAT2 = 0;
            }
         if(pagenum == 0x1B || pagenum == 0x3f)
            {
            XAUTODAT2 = 0x1b;            // fill out the page num
            XAUTODAT2 = 12-2;            // set individual Page Length -- return 12 for all commands
            XAUTODAT2 = 0;
            XAUTODAT2 = 0x01;            // set TLUN = 1 for page 0x1B
            AUTOPTR2L += 8;              // 8 zeroes too.
            }
         if(pagenum == 0x1 || pagenum == 0x3f)
            {
            XAUTODAT2 = 0x1;             // fill out the page num
            XAUTODAT2 = 12-2;            // set individual Page Length -- return 12 for all commands
            AUTOPTR2L += 10;              // 10 zeroes too.
            }

         if (bENABLE_WRITE_CACHE_MODE_PAGE)
            if(pagenum == 0x8 || pagenum == 0x3f)
               {
               XAUTODAT2 = 0x8;             // fill out the page num
               XAUTODAT2 = 0x14-2;          // set individual Page Length -- 0x14
               XAUTODAT2 = bWriteCacheDisabled ? 0 : 4;           // Report write cache enabled or disabled.
               AUTOPTR2L += 13-3;           // Skip to the # of cache segments area (byte 13, we've done 3 so far)
               XAUTODAT2 = 0x10;            // 10 Cache segments
               XAUTODAT2 = 0x10;            // 4K (this is fake data!)
               XAUTODAT2 = 0x00;
               AUTOPTR2L += 4;              // Skip the last 4 bytes
               }

         if (cmd == MODE_SENSE_10)
            EP6FIFOBUF[1] = AUTOPTR2L-2;
         else
            EP6FIFOBUF[0] = AUTOPTR2L-1;

         loadEP6BC(AUTOPTR2L);
         sensePtr = senseOk;
         return(USBS_PASSED);
      } // end case

      ////////////////////////////////////////////////////////////////
      // Check for phase error -- Write in a read wrapper
      case WRITE_10:
      case WRITE_AND_VERIFY_10:
         // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;     
         
         sensePtr = senseInvalidOpcode;
         failedIn();
         return(USBS_PHASE_ERROR);

      default:
      {
         // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;     
         
         sensePtr = senseInvalidOpcode;
         failedIn();
         return(USBS_FAILED);
      }
   }
}   

static BYTE ideWriteCommand(BYTE command)
{
   BYTE driveStatus = 0;
   WORD sectorcount; 
   BYTE i; 
   
   if (command != WRITE_06)
      IDEPrepareForXfer();
   else
      {
      ((BYTE *) &dwLBA)[0] = 0;
      ((BYTE *) &dwLBA)[1] = EP2FIFOBUF[CBW_DATA_START+1] & 0x1f;
      ((BYTE *) &dwLBA)[2] = EP2FIFOBUF[CBW_DATA_START+2];
      ((BYTE *) &dwLBA)[3] = EP2FIFOBUF[CBW_DATA_START+3];

      wCmdSectorCount = EP2FIFOBUF[CBW_DATA_START+4];
      // relinquish control of the bulk buffer occupied by the CBW
      EP2BCL = 0x80;     
      }

   gSectorcount = wCmdSectorCount;     // Save the cmdSectorCount for use in fast writes

   // If there's no valid data length, just exit once we've freed the CBW buffer
   if (dataTransferLen < ATA_SECTOR_SIZE)
      return USBS_PASSED;

   // Check for Ho < Do phase error (case 13)
   if (wCmdSectorCount > dataTransferLen23W/2)
      return (USBS_PHASE_ERROR);

   // If we're going to do DMA, set up the FIFO ONE time before we start
   // It's safe to init while a transfer is taking place, but risky to switch back to manual mode
   // during a transfer.

   if (ActiveLunConfigData.udmaMode && !(dataTransferLenLSW & 0x1ff)
      )
      {
      initUdmaWrite();
      prevDataTransferLen = dataTransferLen;
      mymemmoveix(prevCmd,&EP2FIFOBUF[0x0F],10);  // prepare command buffer for fast write attempt
      }


   // Send the command to the drive
   // This loop breaks up the 32 bit length into 8 bits * sectors.
   // For example, a read of 0x10000 turns into 0x80 sectors.
   while (dataTransferLen >= ATA_SECTOR_SIZE && wCmdSectorCount)
   {
      sectorcount = stuffLBAandSector();
        
      if (ActiveLunConfigData.udmaMode && !(dataTransferLenLSW & 0x1ff)
         )
      {
         // Wait for drive non-busy before starting xfer
         while (readATAPI_ALT_STATUS_REG() & ATAPI_STATUS_BUSY_BIT)
            ;

         // Execute the write command
         if (bExtAddrSupport)
            writePIO8(ATA_COMMAND_REG, ATA_COMMAND_DMAWRITE_RETRY_EXT);
         else
            writePIO8(ATA_COMMAND_REG, ATA_COMMAND_DMAWRITE_RETRY);

         attemptFastWrite = 1;
         attemptFastRead = 0;

         if (sectorcount == 0x100)
            prepUDMA(0x1,00,00);     // 64k Transfers = 128K
         else
            prepUDMA(MSB(sectorcount),LSB(sectorcount),0);
         writeUDMA();

         // If the host is still trying to send us data, stall EP2.
         // We must check for data remaining in the current xfer and data remaining in subsequent xfers.
         // 
         if (GPIFTCMSW || GPIFTCLSW)
         {
            stallEP2OUT();
            attemptFastWrite = 0;
            driveStatus = readATAPI_STATUS_REG();
            goto stopOnShortPacketOrError;
         }
         
         // Check status to clear interrupt.
         if ((driveStatus = readATAPI_STATUS_REG()) & ATAPI_STATUS_ERROR_BIT) 
         {
            if (wCmdSectorCount > 0x100)
               stallEP2OUT();
            attemptFastWrite = 0;
            goto stopOnShortPacketOrError;
         }
         else
         {
            if (sectorcount == 0x100)
               {
               dataTransferLenMSW -= 2;
               wCmdSectorCount -= 0x100;
               }
            else
               {
               dataTransferLen23W -= LSB(sectorcount) << 1;
               wCmdSectorCount -= sectorcount;
               }
         }
      }
      else
      {
         attemptFastWrite = 0;
         // Are we finishing an odd-length UDMA?
         if (ActiveLunConfigData.udmaMode)
         {
            // cancel AUTO OUT mode.  
            EP2FIFOCFG = 0x01;
            SYNCDELAY;
            IFCONFIG = IFCONFIG_DEFAULT;
         }
         // Execute the write command
         if (bExtAddrSupport)
            writePIO8(ATA_COMMAND_REG, ATA_COMMAND_WRITE_10_EXT);
         else
            writePIO8(ATA_COMMAND_REG, ATA_COMMAND_WRITE_10);
         
         while (sectorcount--)
         {
            BYTE limit;
   
            // Wait for the drive to be non-busy and have either data or an error
            while (1)
            {
               driveStatus = readATAPI_STATUS_REG();                             
               if ((driveStatus & (ATAPI_STATUS_BUSY_BIT | ATAPI_STATUS_DRQ_BIT)) == ATAPI_STATUS_DRQ_BIT)
                  break;
               if ((driveStatus & (ATAPI_STATUS_BUSY_BIT | ATAPI_STATUS_ERROR_BIT)) == ATAPI_STATUS_ERROR_BIT)
                  goto stopOnShortPacketOrError;
            }
               
            // Normal case -- Got a sector.  Send it to the drive (in 8 chunks)
            if (wPacketSize == 0x40)
               limit = 8;
            else
               limit = 1;
   
            for (i = 0; i < limit; i++)
            {
               WORD savebc;

               while(EP2CS & 0x04);	// Wait for host to send data
               savebc = (EP2BCH << 8) | EP2BCL;
                   
               // Terminate xfer on receipt of short packet, otherwise drop
               // into streamlined case
               if (savebc < wPacketSize)
               {
                  EP2BCL = 0;
                  writePIO16(savebc+1);     // Add 1 to allow odd xfers.
                  
                  // This looks wrong, but i is always 0 when wPacketSize is 0x200.
                  dataTransferLen -= savebc + i * 0x40; /*wPacketSize*/
                  goto stopOnShortPacketOrError;
               }
               else
               {
                  EP2BCL = 0;
                  writePIO16(wPacketSize);
               }
            }
            dataTransferLen -= ATA_SECTOR_SIZE; // Returned a full sector.
            wCmdSectorCount -= 0x1;
         }  // while (sectorcount) 
      } // else (if udma)
   } // While (dataTransferLen)
    
   // This check is active whenever Ho > Do or Ho > Dn.
   if (dataTransferLen)
      {
      stallEP2OUT();
      attemptFastWrite = 0;
      }

stopOnShortPacketOrError:
   // cancel AUTO OUT mode, put IFCLK back to normal
   IFCONFIG = IFCONFIG_DEFAULT;
   EP2FIFOCFG = 0x05;

   if (driveStatus & ATAPI_STATUS_ERROR_BIT)
      {
      loadSensePtrFromErrorRegister(0);
      return(USBS_FAILED);
      }

   return(USBS_PASSED);
}


BYTE generalIDEOutCommand()
{
   BYTE cmd;
   
   cmd = EP2FIFOBUF[0xf];  
   
   switch (cmd)
   {
      case WRITE_06:
      case WRITE_10:
      case WRITE_AND_VERIFY_10:
      {
         sensePtr = senseOk;
         checkForMedia(1);
         if (sensePtr == senseOk)
            {
            return ideWriteCommand(cmd);
            }
         else
         {
            // relinquish control of the bulk buffer occupied by the CBW
            EP2BCL = 0x80;
            stallEP2OUT();

            return(USBS_FAILED);
         }
      }
      case SYNCHRONIZE_CACHE:
      {
         return(flushCache());
      }
      ///////////////////////////////////////////////////////////
      // Spoofed commands
      case MODE_SELECT_06:    // Note that these are in BOTH the read and write areas in case they are sent with no data
         // Add kludge for Mac Jaguar OS.  Wait for extra OUT packets to show up so we process them properly.
         EZUSB_Delay(5);
      case MODE_SELECT_10:
      {
         bit bShortPacketReceived = 0;

         // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;   
  
         // Only process one mode select -- Write caching
         // When we're done, discard the data just like any other mode select.
         if (dataTransferLen)
            {
            BYTE offset;

            while (EP2CS & 0x04);	// spin while empty

            // Skip past block descriptor to get to the page.
            if (cmd == MODE_SELECT_06)
               offset = 4+EP2FIFOBUF[3];
            else
               offset = 8+EP2FIFOBUF[7];

            // If the page is 8, use it.
            if (EP2FIFOBUF[offset] == 8)
               {
               bWriteCacheDisabled = (~EP2FIFOBUF[offset+2]) & 4;
               setFeatures(bWriteCacheDisabled ? 
                  SET_FEATURES_WRITE_CACHE_DISABLE : SET_FEATURES_WRITE_CACHE_ENABLE, 0);
               }
            }

      // Toss away all of the data received with the command
         while (dataTransferLen && !bShortPacketReceived)
            {
            if(!(EP2CS & 0x04))		// if not empty
               {
               dataTransferLen -= min(EP2BC, dataTransferLen);
               if (EP2BC != wPacketSize)
                  bShortPacketReceived = 1;   
               EP2BCL = 0x80;
               }
            }
         
      // This command is allowed to have data.         
         sensePtr = senseOk;
         checkForMedia(1);
         
         if (sensePtr == senseOk)
            return(USBS_PASSED);
         else
            return(USBS_FAILED);
      }

      //////////////////////////////////////////////////////////////////////
      // Check for phase error -- Read command in a write wrapper
      case INQUIRY:
      case READ_06:
      case READ_10:
      case READ_FORMAT_CAPACITIES:
      case READ_CAPACITY:
      case REQUEST_SENSE:
      case MODE_SENSE_06:
      case MODE_SENSE_10:
         // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;     
         
         stallEP2OUT();
         sensePtr = senseInvalidOpcode;         
         return(USBS_PHASE_ERROR);
    
      default:
         // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;     
         
         stallEP2OUT();
         sensePtr = senseInvalidOpcode;         
         return(USBS_FAILED);
         break;
   }
}   





static BYTE ideReadCommand(BYTE command)
{   
   BYTE driveStatus;
   WORD sectorcount; 
   WORD wordCtr;

   if (command == VERIFY_10)
      {
      // Stuff dataTranferLen with 00 (sector count << 1) 00
      dataTransferLenLSB = 0;
      dataTransferLen23W = (EP2FIFOBUF[CBW_DATA_START+7] << (8+1)) + (EP2FIFOBUF[CBW_DATA_START+8] << 1);
      dataTransferLenMSB = 0;
      }

   // Prepare for fast read 
   mymemmoveix(prevCmd,&EP2FIFOBUF[0x0F],10);   
   prevDataTransferLen = dataTransferLen;

   switch (command)
      {
      case READ_06:   
         {
         ((BYTE *) &dwLBA)[0] = 0;
         ((BYTE *) &dwLBA)[1] = EP2FIFOBUF[CBW_DATA_START+1] & 0x1f;
         ((BYTE *) &dwLBA)[2] = EP2FIFOBUF[CBW_DATA_START+2];
         ((BYTE *) &dwLBA)[3] = EP2FIFOBUF[CBW_DATA_START+3];

         wCmdSectorCount = EP2FIFOBUF[CBW_DATA_START+4];
         // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;     
         break;
         }
      default:
         IDEPrepareForXfer();
      }

   if (wCmdSectorCount > dataTransferLen23W/2)
      {
      failedIn();
      return (USBS_PHASE_ERROR);
      }

   gSectorcount = wCmdSectorCount;     // Save the cmdSectorCount for use in fast reads

   // This loop breaks up the 32 bit length into 8 bits * sectors.
   // For example, a read of 0x10000 turns into 0x80 sectors.
   while (dataTransferLen && wCmdSectorCount)
   {
      sectorcount = stuffLBAandSector();
        
      if (!ActiveLunConfigData.udmaMode || (command == VERIFY_10) || (dataTransferLenLSW & 0x1ff)    // UDMA cannot handle sub-sector sized reads
         )
      {
         // Execute the read command
         if (bExtAddrSupport)
            {
            if ((command == VERIFY_10))
               writePIO8(ATA_COMMAND_REG, ATA_COMMAND_VERIFY_10_EXT);
            else
               writePIO8(ATA_COMMAND_REG, ATA_COMMAND_READ_10_EXT);
            }
         else
            {
            if ((command == VERIFY_10))
               writePIO8(ATA_COMMAND_REG, ATA_COMMAND_VERIFY_10);
            else
               writePIO8(ATA_COMMAND_REG, ATA_COMMAND_READ_10);
            }

           
         // The verify command reads from the drive, but doesn't transfer data
         // to us.  The verify code path doesn't get any further than this IF statement.
         if ((command == VERIFY_10))     
         {
            if(waitForBusyBit() == USBS_FAILED)
               {
               loadSensePtrFromErrorRegister(1);
               dataTransferLen = 0;    // This command has no data but this loop uses dataTransferLen as a counter.
               return(USBS_FAILED);
               }
            else
               {
               if (sectorcount == 0x100)
                  {
                  dataTransferLenMSW -= 2;
                  wCmdSectorCount -= 0x100;
                  }
               else
                  dataTransferLen = 0;
               continue;
               }
         }

         // Wait for drive non-busy before starting xfer
         while (readATAPI_ALT_STATUS_REG() & ATAPI_STATUS_BUSY_BIT)
            ;
   
         // set up for GPIF transfer - wordwide
         //EP6GPIFTCH = MSB(wPacketSize >> 1);     
         //EP6GPIFTCL = LSB(wPacketSize >> 1);
         
         while (sectorcount--)
         {
            // Wait for the drive to be non-busy and have either data or an error
            while (1)
            {
               driveStatus = readATAPI_STATUS_REG();                             
               if ((driveStatus & (ATAPI_STATUS_BUSY_BIT | ATAPI_STATUS_DRQ_BIT)) == ATAPI_STATUS_DRQ_BIT)
                  break;
               if ((driveStatus & (ATAPI_STATUS_BUSY_BIT | ATAPI_STATUS_ERROR_BIT)) == ATAPI_STATUS_ERROR_BIT)
                  break;
            }

            // If there's an error, the drive may still want to send us the data.
            if (driveStatus & ATAPI_STATUS_ERROR_BIT)
               {
               loadSensePtrFromErrorRegister(1);
               if (driveStatus & ATAPI_STATUS_DRQ_BIT)
                  for (wordCtr = 0; wordCtr < 0x100; wordCtr++)
                     readPIO8(ATAPI_DATA_REG);
               failedIn();
               return(USBS_FAILED);  
               }
            else
            {
               BYTE bLimit;
               WORD wThisPacketSize=0;
               BYTE i;

               if (wPacketSize == 0x40)
                  bLimit = 8;
               else 
                  bLimit = 1;
               for (i = 0; i < bLimit && dataTransferLen; i++)
               {
                  waitForInBuffer();

                  wThisPacketSize = min(wPacketSize, dataTransferLen);
                  readPIO16(wThisPacketSize);
                  while (!gpifIdle())     // Wait for xfer to complete
                     ;
                  EP6BCH = MSB(wThisPacketSize);
                  EP6BCL = LSB(wThisPacketSize);
                  dataTransferLen -= wThisPacketSize; 
               }
               // Check for residue at the drive.  If it's there, read it all.
               if (i < bLimit || wThisPacketSize != wPacketSize)
               {
                  while (readATAPI_STATUS_REG() & ATAPI_STATUS_DRQ_BIT)
                      readPIO8(ATAPI_DATA_REG);
                  // Now we have up to 511 bytes of crap in the FIFO.
                  // Wait for the IN endpoint to be empty, then reset the FIFO
                  while (!(EP6CS & 0x04))	// bmEPEMPTY
                     ;
                  FIFORESET = 6;
               }
            }
            wCmdSectorCount -= 0x1;
         }//while (sectorcount--)
      }
      else  // transfer is udma mode
      {
         initUdmaRead();
         if (bExtAddrSupport)
            writePIO8(ATA_COMMAND_REG, ATA_COMMAND_DMAREAD_RETRY_EXT);
         else
            writePIO8(ATA_COMMAND_REG, ATA_COMMAND_DMAREAD_RETRY);
         prepUDMA(MSB(sectorcount),LSB(sectorcount),0);     // (DWORD) sectorcount << 8
         readUDMA();     // Words = sectors * 256

         // switch the EP back to manual mode
         EP6Manual();
         attemptFastRead = 1;
         attemptFastWrite = 0;

         // If there's anything left in the count regs, we've got a problem.  
         // We will have to STALL in the IN endpoint to stop the host.
         // This is the Hi > Di case.
         if (GPIFTCMSW || GPIFTCLSW)
            {
            if ((GPIFTCLSW & 0xff))      // Check for short packet already sent
               bShortPacketSent = 1;
            failedIn();
            attemptFastRead = 0;
            }

         // Clear the interrupt by reading the status reg.
         while ((driveStatus = readATAPI_STATUS_REG()) & ATAPI_STATUS_BUSY_BIT)
            ;
         if (driveStatus & ATAPI_STATUS_ERROR_BIT) 
            {
            loadSensePtrFromErrorRegister(1);
            if (sensePtr == senseCRCError)
               slowDownOnUDMAErrors();
               
            attemptFastRead = 0;
            // No need to do failedIn() -- Already checked above to see that all data has been xferred.
            return(USBS_FAILED);
            }

         // In the Hi > Di case, we could still not have an error.
         if (attemptFastRead == 0)
            return(USBS_PASSED);

         if (udmaErrorCount)
            udmaErrorCount--;

         if (sectorcount == 0x100)
            {
            dataTransferLenMSW -= 2;
            wCmdSectorCount -= 0x100;
            }
         else
            {
            dataTransferLen23W -= LSB(sectorcount) << 1;
            wCmdSectorCount -= sectorcount;
            }
      }
   }//while (dataTransferLen)

   // This check is active whenever Hi > Dn or Hi > Di.
   if (dataTransferLen)
      {
      failedIn();
      attemptFastRead = 0;
      return (USBS_PASSED);
      }
      
   return(USBS_PASSED);
}   

/* This command is only used for removable DRIVES, not removable media.  An example of removable drive is Compact Flash */
// Check to see if a new CF is inserted.  Called before executing every command to make sure
// we have media.  Called from TD_Poll to detect status changes between host activity.
// If we're not looking at a CF device, still check for CF but return 1 -- Media here.  
//
// Input -- commandProcessing:  When set to 0, we will not modify the sensePtr, we will just look for CF changes.
bit checkForMedia(bit commandProcessing)
{
   // Check for new media every time, not just when the last command addressed a CF
   // If there's no media, just set the noFlashMedia flag.  If the host tries to execute
   // a command w/o media, we report "no media".
   // On the transition from no media to media, we must wait until the host tries to 
   // send a command, then report "media changed".
   if (CF_DETECT_ || ejected || !bDRVPWRVLD_ENABLE)
      {
      noFlashMedia = 1;
      
      if (!(IOEShadow & nPWR500))    // Turn off the CF power if it's on
         powerOff();            
      }

   if (bCompactFlash && commandProcessing)  // This check assures that the CF waves get loaded.
      {

      if (CF_DETECT_)      // If the media is gone, there is no reason to keep the ejected flag set.
         ejected = 0;

      // If there's no media, the noMedia flag will be set above.  Just load sensePtr and return.
      // We cannot do this above because we the CF only owns the sensePtr when its LUN is selected.
      if (CF_DETECT_ || ejected || !bDRVPWRVLD_ENABLE || !Configuration)
         {
         sensePtr = senseNoMedia;
         if (!(IOEShadow & nPWR500))    // Turn off the CF power if it's on
            powerOff();             // Turn off the power
         return (0);   
         }
      else  // CF detected case
         {
         // Check for ALL of the following to be true to see if we're resuming from suspend:
         // -- The noFlashMedia flag is cleared (indicating media present)
         // -- We're bus powered
         // -- Power is off, we are resuming from suspend.
         // -- Configuration is set (we have authorization for power)
         if (!noFlashMedia && VBUS_POWERED && (IOEShadow & nPWR500) && Configuration)
            powerOn();

         // If the noFlashMedia flag is set, we're transitioning from no media to media inserted.
         if (noFlashMedia && Configuration)
            {
            // Turn on the power
            // "PWR500 will only be asserted if VBUSPWRD is asserted (or when we don't have a VBUSPWRD input)"
            EZUSB_Delay(10);
            if (VBUS_POWERED)
               powerOn();             // Turn on the power
            else
               {
               // If we don't control the power, we may want to reset the new device.
               // If there are other devices on the bus, this will reset them too, but we have no choice.
               if (!bATA_ENABLED)
                  {
                  hardwareReset();
                  EZUSB_Delay(90);
                  }
               }
            EZUSB_Delay(30);

            ATAPIIdDevice();        // Identify the device
            initDriveAfterReset();  // Set up the device

            // move the drive identification stuff to LUN0.  Compact flash is always LUN0.
            LunBitsL = ActiveLunBits;
            mymemmove((BYTE *)&DeviceConfigData[0],(BYTE *)&ActiveLunConfigData, sizeof(DEVICE_CONFIG_DATA));
            sensePtr = senseMediaChanged;
            noFlashMedia = 0;
            return (0);       // 0 will force the code through the error path.
            }
         return (1);
         }
      }
   else
      return(1);
}

WORD stuffLBAandSector()      // Stuff the LBA registers
{
   WORD sectorcount;

   writeATA_DRIVESEL_REG();
   waitForBusyBit();

   // First stuff the length register (number of sectors to read)
   if (wCmdSectorCount & 0xff00)
   {
      if (bExtAddrSupport)
         writePIO8(ATA_SECTOR_COUNT_REG, 1);     // if (bExtAddrSupport) we need to stuff the MSB
      writePIO8(ATA_SECTOR_COUNT_REG, 0);     // 0 means 256 blocks of 512 bytes -- Max drive xfer, max TC
      sectorcount = 0x100;
   }
   else
   {
      sectorcount = wCmdSectorCount;
      if (bExtAddrSupport)
         writePIO8(ATA_SECTOR_COUNT_REG, 0);      // for extended addressing
      writePIO8(ATA_SECTOR_COUNT_REG, sectorcount);       // divide len into blocks
   }

   if (bExtAddrSupport)
      {
      writePIO8(ATA_LBA_LSB_REG,  ((BYTE *) &dwLBA)[0]);    // LBA (31:24)
      writePIO8(ATA_LBA_2SB_REG,  0);                       // LBA (39:32)
      writePIO8(ATA_LBA_MSB_REG,  0);                       // LBA (47:40)
      }

   writePIO8(ATA_LBA_LSB_REG,  ((BYTE *) &dwLBA)[3]);    // LBA (7:0)
   writePIO8(ATA_LBA_2SB_REG,  ((BYTE *) &dwLBA)[2]);    // LBA (15:8)
   writePIO8(ATA_LBA_MSB_REG,  ((BYTE *) &dwLBA)[1]);    // LBA (23:16)

   if (!bExtAddrSupport)
      {      
      writePIO8(ATA_DRIVESEL_REG, ((BYTE *) &dwLBA)[0] | 0xe0 | ((BYTE)bMasterSlave << 4));
      }

   dwLBA += sectorcount;
   return sectorcount;
}


static void IDEPrepareForXfer()
{
   writeATA_DRIVESEL_REG();
   waitForBusyBit();
      // Oddly enough, an error bit is okay here.  It means that the LAST command failed, not this one.
      // A new command is required to clear many error conditions.
    
   ((BYTE *) &dwLBA)[0] = EP2FIFOBUF[CBW_DATA_START+2];
   ((BYTE *) &dwLBA)[1] = EP2FIFOBUF[CBW_DATA_START+3];
   ((BYTE *) &dwLBA)[2] = EP2FIFOBUF[CBW_DATA_START+4];
   ((BYTE *) &dwLBA)[3] = EP2FIFOBUF[CBW_DATA_START+5];

   ((BYTE *) &wCmdSectorCount)[0] = EP2FIFOBUF[CBW_DATA_START+7];
   ((BYTE *) &wCmdSectorCount)[1] = EP2FIFOBUF[CBW_DATA_START+8];

   // relinquish control of the bulk buffer occupied by the CBW
   EP2BCL = 0x80;     
}

static BYTE loadSensePtrFromErrorRegister(bit readWrite)
{  
   BYTE error;

   error = readPIO8(ATAPI_ERROR_REG);
   if (!error)
      sensePtr = senseOk;
   else if (error & 0x80)       // CRC
      sensePtr = senseCRCError;
   else if (error & 0x02)  // No media present
      sensePtr = senseNoMedia;
   else if (error & 0x20)  // Media changed
      sensePtr = senseMediaChanged;
   else if (error & 0x10)  // Addr out of range 
      sensePtr = senseAddrNotFound;
   else if (error & 0x40)  // Uncorrectable read error / Write Protect
      {
      if (readWrite)
         sensePtr = senseReadError;
      else 
         sensePtr = senseWriteProtected;
      }
   else if (error & 0x08)  // Media change requested -- No good translation for this one.
      sensePtr = senseInvalidOpcode;
   else if (error & 0x04)  // Command not supported or CRC error or request out of range
      {
      if (readWrite)
         sensePtr = senseReadError;
      else 
         sensePtr = senseWriteFault;
      }
   else
      sensePtr = senseInvalidOpcode;

   return(error);
}

void EP2Manual()
{
   // cancel AUTO OUT mode, put IFCLK back to normal
   IFCONFIG = IFCONFIG_DEFAULT;
   EP2FIFOCFG = 0x05;
}
void fastReadStart()
{
   // bump the LBA to what we think it will be
//   dwLBA += *((WORD *) &prevCmd[7]);

   // now put the bumped LBA in our previous CBW buffer for later comparison
   *((unsigned long *) &prevCmd[2]) = dwLBA;

   // get our saved transfer length from the previous transfer
   dataTransferLen = prevDataTransferLen;

   wCmdSectorCount = gSectorcount;        // Retrieve stored value from last command
   gSectorcount = stuffLBAandSector();

   initUdmaRead();

}

bit fastReadComplete()
{
   bit error;

   // this is basically from idereadcommand()
   while (dataTransferLen)
   {
//      initUdmaRead();
      error = 0;
      if (bExtAddrSupport)
         writePIO8(ATA_COMMAND_REG, ATA_COMMAND_DMAREAD_RETRY_EXT);
      else
         writePIO8(ATA_COMMAND_REG, ATA_COMMAND_DMAREAD_RETRY);
      prepUDMA(MSB(gSectorcount),LSB(gSectorcount),0);
      readUDMA();     // Words = sectors * 256
   
 
      // If there's anything left in the count regs, we've got a problem.  
      // We will have to STALL in the IN endpoint to stop the host.
      if (GPIFTCMSW || GPIFTCLSW)
      {
         if ((GPIFTCLSW & 0xff))      // Check for short packet already sent
            bShortPacketSent = 1;
         EP6Manual();
         failedIn();
         error = 1;        // Set an error flag here, but don't return yet.  Still have to load an error code.
      }

      if (readATAPI_STATUS_REG() & ATAPI_STATUS_ERROR_BIT) 
      {
         loadSensePtrFromErrorRegister(1);
         // No need to do failedIn() -- Already checked above to see that all data has been xferred.
         EP6Manual();
         return(USBS_FAILED);
      }
      // If the xfer failed but the drive didn't detect an error, ask the host to retry by setting a CRC error
      else if (error)
      {
         sensePtr = senseCRCError;
         EP6Manual();
         return(USBS_FAILED);
      }
      // Two choices -- Either we're limited to 0x100 sectors, or limited to dataTransferLen.
      // BUGBUG -- No capability here to report Hi > Di.
      if (gSectorcount == 0x100)
      {
         dataTransferLenMSW -= 2;
         gSectorcount = stuffLBAandSector();
      }
      else
         dataTransferLen = 0;
   }//while (dataTransferLen)

   attemptFastRead = 1;
   EP6Manual();
   return(USBS_PASSED);
}

void fastWriteStart()
{
   // now put the bumped LBA in our previous CBW buffer for later comparison
   *((unsigned long *) &prevCmd[2]) = dwLBA;

   // get our saved transfer length and sector count from the previous transfer
   dataTransferLen = prevDataTransferLen;
   wCmdSectorCount = gSectorcount; 

   gSectorcount = stuffLBAandSector();
}

bit fastWriteComplete()
{
   // We cannot do this until we process the CBW!  Don't move it into fastReadStart
   initUdmaWrite();

   // Send the command to the drive
   // This loop breaks up the 32 bit length into 8 bits * sectors.
   // For example, a read of 0x10000 turns into 0x80 sectors.
   while (dataTransferLen)
   {
      // Wait for drive non-busy before starting xfer
      while (readATAPI_ALT_STATUS_REG() & ATAPI_STATUS_BUSY_BIT)
         ;

      // Execute the write command
      if (bExtAddrSupport)
         writePIO8(ATA_COMMAND_REG, ATA_COMMAND_DMAWRITE_RETRY_EXT);
      else
         writePIO8(ATA_COMMAND_REG, ATA_COMMAND_DMAWRITE_RETRY);

      attemptFastWrite = 1;

      if (gSectorcount == 0x100)
         prepUDMA(0x1,00,00);     // 64k Transfers = 128K
      else
         prepUDMA(MSB(gSectorcount),LSB(gSectorcount),0);
      writeUDMA();

      if ((GPIFTCMSW || GPIFTCLSW))
      {
         stallEP2OUT();
         attemptFastWrite = 0;
         goto stopOnShortPacket;
      }
      
      // Check status to clear interrupt.
      if (readATAPI_STATUS_REG() & ATAPI_STATUS_ERROR_BIT) 
      {
         goto stopOnShortPacket;
      }
      else
      {
         // Two choices -- Either we're limited to 0x100 sectors, or limited to dataTransferLen.
         if (gSectorcount == 0x100)
         {
            dataTransferLenMSW -= 2;
            gSectorcount = stuffLBAandSector();
         }
         else
            {
            dataTransferLen = 0;    // This will terminate the loop anyway.  The break; just makes it faster
            break;
            }
      }
   } // While (dataTransferLen)
    
stopOnShortPacket:
   EP2Manual();
   if (readATAPI_STATUS_REG() & ATAPI_STATUS_ERROR_BIT)
      {
      loadSensePtrFromErrorRegister(0);
      attemptFastWrite = 0;
      return(USBS_FAILED);
      }
   else
      return(USBS_PASSED);
}


/////////////////////////////////////////////////////////////////////////////////
#endif      // DEVICE_TYPE_IS_IDE
/////////////////////////////////////////////////////////////////////////////////


void waitForInBuffer()
{
   while(EP2468STAT & 0x20);   // Wait for an available buffer from the host

   return;
}   

void writeATA_DRIVESEL_REG()
{
   writePIO8(ATA_DRIVESEL_REG, 0xe0 | ((BYTE)bMasterSlave << 4));
}

#if DEVICE_TYPE_IS_IDE || ATACB_ENABLE
void EP6Manual()
{
   // switch the EP back to manual mode
   EP6FIFOCFG = 0x05;
   SYNCDELAY;
   IFCONFIG = IFCONFIG_DEFAULT;
}

////////////////////////////////////////////////////////////////
//
// loadEP6BC()
// 
// Sends a short (less than one packet) response back to the host.  If the host asked for 0
// length data, don't send anything.  
//
/////////////////////////////////////////////////////////////////
void loadEP6BC(WORD dataLen)
{
   WORD packetLen;
   bit   bPaddedPacket = 0;

   // If the host asked for 0 data, give him 0 data!
   if (!dataTransferLenLSW)
      return;

   packetLen = min(dataTransferLen, dataLen);
   if (packetLen || (SHORT_PACKET_BEFORE_STALL))
   {
      // pad the really short responses
      if (dataTransferLen <= wPacketSize)
         {
         bPaddedPacket = 1;
         EP6BCH = MSB(dataTransferLenLSW);
         EP6BCL = LSB(dataTransferLenLSW);
         }
      else         
         {
         EP6BCH = MSB(packetLen);
         EP6BCL = LSB(packetLen);
         }
      dataTransferLen -= packetLen;
   }
   if (packetLen & (wPacketSize - 1))
      bShortPacketSent = 1;

   if (!bPaddedPacket)
      failedIn();    // This doesn't set status, just STALLs the IN endpoint if needed.      
}
#endif

BYTE flushCache()
{
   writeATA_DRIVESEL_REG();
   sensePtr = senseOk;
   checkForMedia(1);
   if (sensePtr != senseOk)
      return(USBS_FAILED);
   
   waitForBusyBit();
   writePIO8(ATA_COMMAND_REG, ATA_COMMAND_FLUSH_CACHE);  
   waitForBusyBit();
   return (USBS_PASSED);
}

#if STANDBY_IMMEDIATE
// Added for power saving on HDD devices in systems WITHOUT CF
// Only writes to the current LUN.  Multiple LUNs not supported.
void standbyImmediate()
{
   if (driveIsInStandby)
      return;
   driveIsInStandby = 1;
   writeATA_DRIVESEL_REG();
   
   waitForBusyBit();
   writePIO8(ATA_COMMAND_REG, ATA_COMMAND_STANDBY_IMMEDIATE);  
   waitForBusyBit();
}
#endif

Vorgefundene Kodierung: UTF-80