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

//-----------------------------------------------------------------------------
//   File:      periph.c
//   Contents:   Hooks required to implement USB peripheral function.
//
//   Copyright (c) 1997-2003 Cypress Semiconductor, Inc. All rights reserved
//
// $Archive: /USB/atapifx2/CY4611B/periph.c $
// $Date: 6/30/05 10:48a $
// $Revision: 9 $
//-----------------------------------------------------------------------------
#include "fx2.h"
#include "fx2regs.h"
#include "gpif.h"
#include "atapi.h"
#include "globals.h"

extern BOOL   Sleep;
extern BOOL   Rwuen;

void clearResetLine();

//-----------------------------------------------------------------------------
// Support functions for specific device
//-----------------------------------------------------------------------------

static void initUSB(void) {
  DBUG &= ~0x01;  // Turn off phase-picker power savings. (DBUG at 0xE6F8)
  PORTACFG = 0;

  OUTATAPI = (BYTE)(ATAPI_IDLE_VALUE); 
  nLOWPWR  = 0;           // active low -- Indicates active (0) vs suspend (tri-state)
  IOEShadow = nPWR500 | ATAPUEN;
  IOE = IOEShadow;         // active low -- Start with power OFF
  OEC = PORTC_OE;
  if (VBUS_POWERED) {
    OEE |= (PORTE_OE_SUSPEND);
    triStateATABus();
  }else OEE |= PORTE_OE;

   // Enable INT4 to latch the GPIOs
  EXIF &= ~bmIE4;	// Clear any pending interrupts
  EIEX4 = 1;	// Enable INT4 interrupts
		// INTSETUP is configured elsewhere.

// if we are trying to operate in bus powered mode, we want to defer setting the 
// CPU clock speed or configuring the GPIF until we have been configured.  This
// is to keep our current under 100mA.
  IFCONFIG = IFCONFIG_DEFAULT;
  CPUCS = 0x14;	// set clock to 48MHz.  clock output disabled. inverted.
//   FIFOPINPOLAR = 0x00;    // ff pin is active low -- This is the default

//   PINFLAGSAB = 0x00;    // FLAGA PF for FIFO selected by FIFOADR[1..0] -- 0 is the default
  PINFLAGSCD = 0x00;    // FLAGB FF for FIFO selected by FIFOADR[1..0]

   // GPIF and CTL configuration
  checkATAEnable();    // Sets GPIFIDLECTL, OEA
  GPIFIDLECS = 0;      // tristate data bus during idle interval
  GPIFREADYCFG = 0x20;    // Turn on TCXpire as a ready source
  GPIFTCB3 = 0;        // Set this ONCE, not every time.  No xfers longer than 24 bits.

  FLOWEQ0CTL = 0x00;      // UDMA init that's the same for both read and write
  FLOWSTBEDGE = 0x03;     // 
  GPIFHOLDAMOUNT = 0x01;  // 

  GPIFWFSELECT = GPIFWFSELECT_DEFAULT;
   // Endpoint initialization
  EP2CFG = 0xA0;           // ep2 is valid BULK OUT 512 quad buffered
  EP2FIFOCFG = 0x05;       // WORDWIDE=1M MANUAL
  EP2FIFOPFH = 0x00;       // PF=0 when BC > PF -> Decis=0 (1 byte in FIFO)
  EP2FIFOPFL = 0x00;       // PF and BC refer to the current pkt -> PKTSTAT=0
//   EP2GPIFPFSTOP = 0;       // Do not stop on PF -- This is the default

  EP6CFG = 0xE0;          // ep6 is valid BULK IN 512 double buffered
  EP6FIFOCFG = 0x05;      // set EP6:  0x05=MANUAL, 0x0D=AUTOIN

  EP1INCFG = 0x80 | 0x30;    // EP1IN is valid INTERRUPT in

   // mark all unused endpoints invalid - setting each reg to 0x22 instead of just clearing
   // the valid bit to save code space.  0x22 basically sets all of these endpoints to 
   // not valid, bulk, double 512 buffered.
  EP1OUTCFG = EP4CFG = EP8CFG = 0x22;

   // disbable Auto Arm
  REVCTL = 0x02;	// bmNOAUTOARM

   // arm the OUT endpoint.  By default OUT endpoints come up unarmed.
  ResetAndArmEp2();

   // reset the EP6 FIFO.  If we are here as the result of a USB reset or MSC
   // Reset Recovery, EP6 may need to be cleaned up.
  FIFORESET = 0x06;

  if (!IN_MASS_STORAGE_CLASS_RESET) AlternateSetting = Configuration = 0;
}

//-----------------------------------------------------------------------------
// Task Dispatcher hooks
//   The following hooks are called by the task dispatcher.
//-----------------------------------------------------------------------------

void TD_Init(void) {// Called once at startup
  bit bError;

   // set our first_time flag.  This is set to the timer start bit.  The timer start
   // bit will only be set if we have already been here before.
  bFirstTime = !TR0;

  phaseErrorState = 0;
  bScsiRegsPreloaded = 0;
  attemptFastScsi = 0;

   // Enable both autoptrs.  ONLY use AUTOPTR2 for writing small amounts of data to EP6buf.  NEVER load
   // AUTOPTR1H again.
  AUTOPTRSETUP = 0x7;
  AUTOPTR2H = MSB(EP6FIFOBUF);

   
   // Only do the copy the first time we run because we modify the descriptor version in HalfKBuffer.
   // We know it's the first time if the timer isn't running yet.
   // There are three possible startup conditions:
   // -- AT2 classic EEPROM present.  Turns off many new features, including EP1
   // -- AT2LP EEPROM present.  As defined in the datasheet
   // -- Bad signature found.  Assumed to be an unprogrammed EEPROM.  Skip drive ID and wait for mfg mode command.
  if (bFirstTime) {
    bError = 1;

    eepromAddr = EEPROM_ADDR;
    bError = EEPROMRead(CONFIG_SPACE_START,sizeof(MX2_CONFIG_DATA), (BYTE xdata *) &mx2_config_data);
         
    miscConfig = mx2_config_data.MiscConfig;
    miscConfig2 = mx2_config_data.MiscConfig2;
      // Secret bit for 400Khz EEPROM read)
    if (bATA_UDMA_ENABLE) {
      bATA_UDMA_ENABLE = 0;
      I2CTL |= 0x01;	// bm400KHZ
    }
    miscConfig |= mx2_config_data.UdmaConfig & 0xc0;
    pinConfig = mx2_config_data.PinConfig;
//      mymemmovexx(halfKBuffer, (char xdata *) &DeviceDscr, (BYTE)&DscrEndOffset);
    sensePtrs[0] = sensePtrs[1] = sensePtr = senseMediaChanged;

    if (bError)
      eepromAddr = 0x52;      // Config for dev board if error is detected.

      // Missing signature:  Read the dscr.a51 file for the VID/PID.  Enter mfg mode.
    if (!(mx2_config_data.Signature == MX2_EEPROM_SIGNATURE || mx2_config_data.Signature == AT2LP_EEPROM_SIGNATURE)) {
         // relocate USB descriptors to RAM
         // load the deecriptors from the rom.
//         mymemmovexx(halfKBuffer, (char xdata *) &DeviceDscr, (WORD)&DscrEndOffset);

      setDefaultConfig();
      enterMfgMode();
    }else{
      mfgMode = 0;
	// relocate USB descriptors to RAM
	// load the deecriptors from the eeprom.
	// the descriptor data starts at offset 16 in the eeprom
      EEPROMRead(CONFIG_SPACE_START+sizeof(MX2_CONFIG_DATA),BUFFER_SIZE,halfKBuffer);

	// Almost all of the AT2LP bits are RESERVED=0 in the AT2 EEPROM.  Set the non-reserved ones here.
      if (mx2_config_data.Signature == MX2_EEPROM_SIGNATURE) {
	bATA_ENABLED  = 1;
	bNewAt2pinout = 0;
      }else{
	bNewAt2pinout = 1;
	IOEShadow |= mx2_config_data.GpioData << 2;
	IOE = IOEShadow;
	OEE |= mx2_config_data.GpioOE << 2;
	checkGPIOonPA3();
      }

	////////////////////////////////////////////////////////////////////////////////
	// Check for RESET shorted to DD7 to signal mfg mode.  This allows devices
	// to be reprogrammed without a drive attached.  If the firsttime flag is set,
	// the GPIF isn't set up yet, so we can treat DD7 like a normal pin.
	//
	// Check procedure:
	// 1)  Check for VBUS -- No VBUS = no check
	// 2)  Float the reset line.  If it floats high, abort.
	// 3)  Reset is now low.  Drive DD7 high.  If RESET is now high, enter mfg mode.
      WAKEUPCS = 0x45;
      if (!(WAKEUPCS & 0x40)) {    // check VBUS and old part's VBUS detector
	OEA &= ~ATAPI_RESET_BIT;
	if (!ATAPI_RESET_) {
	  OEB = 0x80;
	  PB7 = 0x80;
	  if (ATAPI_RESET_) enterMfgMode();
	  OEB = 0x0;
	}
	OEA |= ATAPI_RESET_BIT;
      }
    }
  }

   // init state/reset variables
  currentState = UNCONFIGURED;

   // 0x80 is used as a flag value to tell us that we still need to complete drive ID.
//   if (bFirstTime || !deviceCount)
//      {
//      deviceCount = 0x80;
//      LunBits[0] = LunBits[1] = ActiveLunBits = 0;    // zeroes out bCompactFlash, bScsi, bExtAddrSupport
//      }

  attemptFastRead = attemptFastWrite = 0;
#if 0
   // In order to properly support HID and CSM, we have to parse part of the descriptors.
   // If there is more than one descriptor, some offsets are pulled from the descriptors.
  HIDIntrfcDscrOffset = CSMIntrfcDscrOffset = 0;
  if (bNewAt2pinout) {
   
      #define INTRFC_OFFSET 4
         {
         xdata BYTE *ptr;
         
         for (ptr = FullSpeedConfigDscr; 
//            MSB(ptr) == MSB(halfKBuffer) && *ptr != 0; ptr += *ptr)
            {
            if (ptr[1] == DSCR_INTRFC)
               {
               if (ptr[5] == 3)  // HID class code
                  HIDIntrfcDscrOffset = LSB(ptr);
               else if (ptr[5] == 0xd)
                  CSMIntrfcDscrOffset = LSB(ptr);
               }
            // Existing EEPROMs may contain 0x88 for EP8IN instead of 0x86 for EP6IN.  Fix it on the fly.
            else if (ptr[1] == DSCR_ENDPNT && ptr[2] == 0x88)
               ptr[2] = 0x86;
             }
         }
      }
   else  // for old AT2 pinout, must modify the descriptor from EP8IN to EP6IN
      {
      if (halfKBuffer[FullSpeedConfigDscrOffset+ DSCR_CONFIG_LEN + DSCR_INTRFC_LEN + 2] == 0x88)
          halfKBuffer[FullSpeedConfigDscrOffset+ DSCR_CONFIG_LEN + DSCR_INTRFC_LEN + 2] = 0x86;
      if (halfKBuffer[FullSpeedConfigDscrOffset+ DSCR_CONFIG_LEN + DSCR_INTRFC_LEN + DSCR_ENDPNT_LEN + 2] == 0x88)
          halfKBuffer[FullSpeedConfigDscrOffset+ DSCR_CONFIG_LEN + DSCR_INTRFC_LEN + DSCR_ENDPNT_LEN + 2] = 0x86;
      }
#endif

  initUSB();                 // configure output ports and endpoint params
  mymemmovexx(&GPIF_WAVE_DATA, (BYTE xdata *) WaveDataPioUDMA, 128);          // load wave forms in memory

   // Set up timer 0 to act as the timeout counter.
   // The default setting for the debugger uses timer 1
  TMOD = 0x21;      // Timer 1 set up for debugger (just in case).  Timer 0 is 16 bit counter
  CKCON = 0x10;     // Same setting as debugger for T1, T0 is CPU/12 (4Mhz input)
  ET0 = 1;          // Enable timer interrupt (needed to do software count of 61hz overflows)
  TR0 = 1;
}

void TD_Poll(void) {	// Called repeatedly while the device is idle
  WORD    count = 0;

   // check EP2 EMPTY(busy) bit in EP2468STAT (SFR), core set's this bit when FIFO is empty
  if(!(EP2468STAT & 0x01)) {
      // Check for "USBC"
    if (checkCBW() == USBS_PASSED) processCBW();	// Good packet, forward to the device.
    else{
	// if we receive an invalid CBW, STALL both bulk endpoints and remain that
	// way until reset recovery (MSC BOT 6.6.1)
      EP2CS = EP6CS = 0x01;
      phaseErrorState = 1;
    }
  }
#if DEVICE_TYPE_IS_IDE
  checkForMedia(0);
#endif
}

// Stalls EP2OUT endpoint.
//
// This routine automatically handles several problematic OUT STALL cases.
//
// 1)  The host is still PINGing us on the OUT endpoint, we set the stall bit and he reads it.  (Normal case)
// 2)  We want to STALL the endpoint, but the host is done sending all of the data from the command.
//       In this case, we cannot tell the host about the STALL.
// 3)  The host is in the process of sending an OUT packet when we get here.
// 
void stallEP2OUT() {
  if (!dataTransferLen) return;

  FIFORESET = bmNAKALL;         // NACK all xfers from the host.
   // Wait one packet time -- 50uS at full-speed, 10us at high-speed
  { BYTE count;
    BYTE i;
    if (EZUSB_HIGHSPEED()) count = 10;
    else count = 50;
    for (; count; count--) i = halfKBuffer[0];      
  }

  { WORD x;
      // Check to see if stall is needed.  If it is, STALL the endpoint.
      // If we have already received all of the data for the command, STALLing the endpoint
      // will only cause the NEXT command to fail.
    if (EP2468STAT & 0x01) x = EP2FIFOBCL + (EP2FIFOBCH << 8);
    else x = EP2FIFOBCL + (EP2FIFOBCH << 8) + EP2BC;
    if (dataTransferLen > x) EP2CS |= 0x01;
  }

  FIFORESET = 0;	// Clear the NAKALL bit.
}

// Get the sector count and prevCmd fields set up for the next command.
static void fastSCSIStart()
{
   // Update the sector based on the command group (see spc 4.3.3 and 4.3.4)
   if ((prevCmd[0] & 0xe0) == 0xa0)    // 12 byte command
      *((unsigned long *) &prevCmd[2]) += *((unsigned long *) &prevCmd[6]);
   else if ((prevCmd[0] & 0xe0) == 0x20)    // 10 byte commands (0xa0, 0xb0)
      *((unsigned long *) &prevCmd[2]) += *((unsigned int *) &prevCmd[7]);
   else if ((prevCmd[0] & 0xe0) == 0x40)    // 10 byte commands (0xc0, 0xd0)
      *((unsigned long *) &prevCmd[2]) += *((unsigned int *) &prevCmd[7]);

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

// This function pulls common information out of the CBW
void processCBWHeader() {
  currentState = RECEIVED_CBW;     // This will prevent us from processing CBWs in the ISR.

   // Save the tag for use in the response
  cbwTagLow = *((WORD volatile xdata*)(EP2FIFOBUF+CBW_TAG));
  cbwTagHi =  *((WORD volatile xdata*)(EP2FIFOBUF+CBW_TAG+2));

   // Get the length (convert from little endian)
  *(((BYTE *) &dataTransferLen)+3) = (EP2FIFOBUF+CBW_DATA_TRANSFER_LEN_LSB)[0];  // "Residue"
  *(((BYTE *) &dataTransferLen)+2) = (EP2FIFOBUF+CBW_DATA_TRANSFER_LEN_LSB)[1];  // "Residue"
  *(((BYTE *) &dataTransferLen)+1) = (EP2FIFOBUF+CBW_DATA_TRANSFER_LEN_LSB)[2];  // "Residue"
  *(((BYTE *) &dataTransferLen)+0) = (EP2FIFOBUF+CBW_DATA_TRANSFER_LEN_LSB)[3];  // "Residue"

  directionIn = EP2FIFOBUF[CBW_FLAGS] & 0x80;
}

void processCBW() {
  BYTE CbwLun;

  CbwLun = EP2FIFOBUF[CBW_LUN];

#if DEVICE_TYPE_IS_IDE
  if (attemptFastRead) {
    if (CbwLun == currentLunNum  && !(mymemcmpa(prevCmd,&EP2FIFOBUF[0x0F],10+1))) {
      BYTE status;

      attemptFastRead = 0;
      status = fastReadComplete();
	// Save the tag for use in the response
      cbwTagLow = *((WORD volatile xdata*)(EP2FIFOBUF+CBW_TAG));
      cbwTagHi =  *((WORD volatile xdata*)(EP2FIFOBUF+CBW_TAG+2));

	// relinquish control of the bulk buffer occupied by the CBW
      EP2BCL = 0x80;     
      sendUSBS(status);
      currentState = WAIT_FOR_CBW;
      return;
    }
         // switch the EP back to manual mode and drop through to normal processing
    EP6Manual();
    attemptFastRead = 0;
  }else if (attemptFastWrite) {
    if (CbwLun == currentLunNum  && !(mymemcmpa(prevCmd,&EP2FIFOBUF[0x0F],10+1))) {
      BYTE status;

      attemptFastWrite = 0;

	// Save the tag for use in the response
      cbwTagLow = *((WORD volatile xdata*)(EP2FIFOBUF+CBW_TAG));
      cbwTagHi =  *((WORD volatile xdata*)(EP2FIFOBUF+CBW_TAG+2));

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

      status = fastWriteComplete();
      sendUSBS(status);
      currentState = WAIT_FOR_CBW;
      return;
    }
         // switch the EP back to manual mode and drop through to normal processing
    EP2Manual();
    attemptFastWrite = 0;
  }else 
#endif	// DEVICE_TYPE_IS_IDE
  if (attemptFastScsi) {
    attemptFastScsi = 0;
    if (CbwLun == currentLunNum && !(mymemcmpa(prevCmd,&EP2FIFOBUF[0x0F],12+1))) {
      BYTE driveStatus;
	// Save the tag for use in the response
      cbwTagLow = *((WORD volatile xdata*)(EP2FIFOBUF+CBW_TAG));
      cbwTagHi =  *((WORD volatile xdata*)(EP2FIFOBUF+CBW_TAG+2));

      if (!bScsiRegsPreloaded) preloadSCSIRegs();
   
      EP2BCL = 0x80;         // Release the endpoint buffer

	// Send the "ATAPI packet" command
      writePIO8(ATAPI_COMMAND_REG, ATAPI_COMMAND_ATAPI_PACKET);

	// Wait for the register block to be non-busy and to request data
	// CANNOT call waitForDRQBit because that routine waits for the IRQ pin to be set
      while (     (   (driveStatus = readATAPI_ALT_STATUS_REG()) 
                       & (ATAPI_STATUS_BUSY_BIT | ATAPI_STATUS_DRQ_BIT)  )
                  != ATAPI_STATUS_DRQ_BIT);
      if (driveStatus & ATAPI_STATUS_ERROR_BIT) {
	sendUSBS(USBS_FAILED);
	currentState = WAIT_FOR_CBW;
      }
	// Send our stored command
      while (!gpifIdle());
      GPIFWFSELECT = 0;    // PIO write is waveform 0

	// Write the address/chip selects
      OUTATAPI = (BYTE)(ATAPI_DATA_REG | (~ATAPI_ADDR_MASK & ATAPI_IDLE_VALUE));

      sendprev();
      while (!gpifIdle());
   
      if (directionIn) sendUSBS(scsiReadUdma());
      else sendUSBS(scsiWriteUdma());    // Send USBS and scsiWriteUdma will set us up for the next time....
      return;
    }
  }
  processCBWHeader();

  if (!(CbwLun == currentLunNum)) {
	// Before we change waveforms, tell the ATA interface that we're going to talk to the master.
	// This allows us to use some of the ATA signals for the CF while the slave device ignores them.
	// This works well as long as we don't write to a register that affects both devices, like
	// the device select register, or the SRST bit in the control register.
    mymemmove((BYTE *)&ActiveLunConfigData,(BYTE *)&DeviceConfigData[CbwLun], sizeof(DEVICE_CONFIG_DATA));
    ActiveLunBits = CbwLun&1 ? LunBitsH : LunBitsL;
    sensePtr = sensePtrs[CbwLun];
    currentLunNum = CbwLun;

    EP2FIFOCFG = EP6FIFOCFG = 0x05;      // ATA/ATAPI is word-wide

	// We've switched devices.  Do we need to switch PIO modes too?
	// if (!(DeviceConfigData[0].MaxPIO == DeviceConfigData[1].MaxPIO))
	// ALWAYS switch.  It's easier than special casing startup.
	// switch to PIO4
    if(ActiveLunConfigData.MaxPIO & PIO4) {
      mymemmovexx(&GPIF_WAVE_DATA, (BYTE xdata *) WaveDataPioUDMA+4, 8 + 4);
      mymemmovexx(&(GPIF_WAVE_DATA) + 32, (BYTE xdata *) WaveDataPioUDMA+32+4, 8 + 4);
    }else if(ActiveLunConfigData.MaxPIO & PIO3) { // switch to PIO3
      mymemmovexx(&GPIF_WAVE_DATA, (BYTE xdata *) WaveDataPioUDMA+4, 8 + 4);
      mymemmovexx(&(GPIF_WAVE_DATA) + 32, (BYTE xdata *) WaveDataPioUDMA+32+4, 8 + 4);
      ((BYTE xdata *)&GPIF_WAVE_DATA)[0] = 0x6;
      ((BYTE xdata *)&GPIF_WAVE_DATA)[32] = 0x6;
    }else{ // switch to PIO0
      mymemmovexx(&GPIF_WAVE_DATA, (BYTE xdata *) WaveDataPioUDMA, 8 + 4);
      mymemmovexx(&(GPIF_WAVE_DATA) + 32, (BYTE xdata *) WaveDataPioUDMA+32, 8 + 4);
    }
            // Now switch UDMA / DMA modes
    if (ActiveLunConfigData.udmaMode & TRANSFER_MODE_UDMA0) {
      mymemmovexx(&(GPIF_WAVE_DATA) + 64, (BYTE xdata *) WaveDataPioUDMA+64, 64);
    }else if (ActiveLunConfigData.udmaMode & TRANSFER_MODE_DMA0) {
      mymemmovexx(&(GPIF_WAVE_DATA) + 64, (BYTE xdata *) WaveDataPioUDMA+128, 64);
    }
  }

  driveIsInStandby = 0;
   // Config CB
   // Our personal "firmware update" command
  if (EP2FIFOBUF[0xf] == 0xfb && !(EP2FIFOBUF[CBW_FLAGS] & CBW_FLAGS_DIR_BIT)) {
    BYTE saveAddr = eepromAddr;
      // relinquish control of the bulk buffer occupied by the CBW
    EP2BCL = 0x80;     

    eepromAddr = 0x51;
      // Write the entire EEPROM
    EEPROMWrite(dataTransferLenLSW, 0x00);
    eepromAddr = saveAddr;
    sendUSBS(USBS_PASSED);
  }else if (EP2FIFOBUF[0xf] == mx2_config_data.AtaCommand) { // CONFIG CB
    sendUSBS(processConfigCBCommand());
  }else if (directionIn || (!dataTransferLenLSW && !dataTransferLenMSW)) {
    currentState = RECEIVED_IN_CMD;
    if (bScsi) {
      sendUSBS(generalSCSIInCommand());
      if (deviceCount == 1) {
	preloadSCSIRegs();
	bScsiRegsPreloaded = 1;
      }
    }
#if DEVICE_TYPE_IS_IDE
     else sendUSBS(generalIDEInCommand());
#endif
    while (!gpifIdle());             // wait till GPIF is done before getting real data
    OUTATAPI = (BYTE)(ATAPI_IDLE_VALUE);           // Clear ATA control lines.  Really only needed to turn off the CF LED.
    sensePtrs[currentLunNum] = sensePtr;
  }else{
    currentState = RECEIVED_OUT_CMD;
    if (bScsi) {
      sendUSBS(generalSCSIOutCommand());
      if (deviceCount == 1) {
	preloadSCSIRegs();
	bScsiRegsPreloaded = 1;
      }
    }
#if DEVICE_TYPE_IS_IDE
     else sendUSBS(generalIDEOutCommand());
#endif
    while (!gpifIdle());             // wait till GPIF is done before getting real data
    OUTATAPI = (BYTE)(ATAPI_IDLE_VALUE);           // Clear ATA control lines.  Really only needed to turn off the CF LED.
    sensePtrs[currentLunNum] = sensePtr;
  }

  currentState = WAIT_FOR_CBW;
}   


void sendUSBS(BYTE passOrFail) {
  bit done = 0;
   
   // generalIDEx/generalSCSIx command returns here with passOrFail status bit
   // which is re-cast as the error byte of CSW

  while (!done) {
    if (!(EP6CS & 0x09)) {	// Wait for an available buffer
         // Eliminate any data in the OUT endpoint or FIFO
         // This can happen in the Ho > Dn and Ho > Do cases.
      if(!(EP2468STAT & 0x01) || EP2FIFOBCL || EP2FIFOBCH) ResetAndArmEp2();

         // Fill the buffer & send the data back to the host
      AUTOPTR2L = LSB(EP6FIFOBUF);

      XAUTODAT2 = 'U';        // Believe it or not, this is pretty efficient!
      XAUTODAT2 = 'S';
      XAUTODAT2 = 'B';
      XAUTODAT2 = 'S';

      XAUTODAT2 = MSB(cbwTagLow);
      XAUTODAT2 = LSB(cbwTagLow);
      XAUTODAT2 = MSB(cbwTagHi);
      XAUTODAT2 = LSB(cbwTagHi);

         // have to store LSB first
      XAUTODAT2 = ((BYTE *)&dataTransferLen)[3];    // "Residue"
      XAUTODAT2 = ((BYTE *)&dataTransferLen)[2];    // "Residue"
      XAUTODAT2 = ((BYTE *)&dataTransferLen)[1];    // "Residue"
      XAUTODAT2 = ((BYTE *)&dataTransferLen)[0];    // "Residue"
         
      XAUTODAT2 = passOrFail;                 // Status
      EP6BCH = 0;
      EP6BCL = 13;
      done = 1;

         // If we're in phase error, STALL the OUT endpoint to make sure the host resets us (and the drive)
      if (passOrFail == USBS_PHASE_ERROR) {
        EP2CS = 0x01;	// stall
        while (!(EP6CS & 0x04)) ;  // Wait for an available buffer
        EP6CS = 0x01;	// stall
        phaseErrorState = 1;
      }
    }
  }

   // Stall the IN endpoint if we're in phase error state.
  if (phaseErrorState) {
    while (!(EP6CS & 0x04)) ;
    EP6CS = 0x01;	// stall
  }
#if DEVICE_TYPE_IS_IDE
  if (attemptFastRead) fastReadStart();
  else if (attemptFastWrite) fastWriteStart();
  else
#endif 
       if (attemptFastScsi) fastSCSIStart();
}

void failedIn() {
   // Stall if the host is still expecting data.  Make sure
   // endpoint is empty before doing the stall.
  if (dataTransferLen) {
    if (!bShortPacketSent && SHORT_PACKET_BEFORE_STALL) {
      while (!(EP6CS & 0x04));
      EP6BCH = 0;	// Terminate with NULL packet, then STALL.  This 
      EP6BCL = 0;	// addresses an issue with the current EHCI driver (1/02)
    }
    while (!(EP6CS & 0x04));
    EP6CS = 0x01;	// stall TPM
  }
}

bit waitForBusyBit() {
  BYTE driveStatus;
  do driveStatus = readATAPI_ALT_STATUS_REG();
  while (driveStatus & ATAPI_STATUS_BUSY_BIT);
	// Some drives clear the busy bit asynchronously.  Read the reg one more time to be sure.
  driveStatus = readATAPI_ALT_STATUS_REG();
  if (driveStatus & ATAPI_STATUS_ERROR_BIT) return USBS_FAILED;
  return USBS_PASSED;
}

void mymemmovexx(BYTE xdata*d, BYTE xdata*s, WORD len) {
  if (!len) return;
  AUTOPTR1H = MSB(s);
  AUTOPTR1L = LSB(s);
  do *d++ = XAUTODAT1;
  while (--len);
}

void mymemmove(BYTE idata*d, BYTE idata*s, BYTE len) {
  if (!len) return;
  do *d++ = *s++;
  while (--len);
}


// mdnspd
//BYTE mymemcmp(BYTE idata * s1, BYTE xdata * s2, WORD len)
//{
//   while (len--)
//   {
//      if (!(*s1++ == *s2++))
//      {
//         return(1);
//      }
//   }
//
//   return(0);
//}

// mdnspd
void mymemmoveix(BYTE idata*d, BYTE xdata*s, BYTE len) {
  if (!len) return;
  AUTOPTR1H=MSB(s);
  AUTOPTR1L=LSB(s);
  do *d++ = XAUTODAT1;
  while (--len);
}

//#define FW_STRETCH_VALUE_5 5

void ResetAndArmEp2() {
  BYTE ckcon = CKCON;
   // adjust stretch to allow for synchronization delay.  We are about
   // to do several back to back writes to registers that require a
   // synchroniztion delay.  Increasing stretch allows us to meet
   // the delay requirement.  See "Synchroniztion Delay" in the Technical
   // Reference Manual for more information
   // Set the stretch to 5
  CKCON = ckcon&0xF8 | 5;

  FIFORESET = 2;

   // we're quad-buffered, so we need to arm EP2 four times
  EP2BCL = 0x80;
  EP2BCL = 0x80;
  EP2BCL = 0x80;
  EP2BCL = 0x80;

   // Reset the stretch to 0
  CKCON = ckcon;
}

// check for tri-state signal on WAKEUP pin
// If the tri-state pin is set, disconnect and sleep until we have control of the bus again.
// if the pin is not set, 
void checkATAEnable() {
  WAKEUPCS = 0x45;
  if ((WAKEUPCS & 0x40) || (!bNewAt2pinout && !VBUS_PRESENT)) {     // check VBUS and old part's VBUS detector
#if STANDBY_IMMEDIATE
    WORD i;
#endif
    USBCS = 0x0A;	// Disconnect
  
         // Standby Immediate means that we MUST drive the bus in all modes.
         // Turn it on even though we may have the tristate flag set too.
         // Once we're driving the bus and disconnected, we're done.  Return so that the 
         // background sleep code can send the standby command.
#if STANDBY_IMMEDIATE
    driveATABus();
   
            //////////////////////////////////////////////////
            // Special purpose power-saving code.
            //
            // Expected conditions:  
            //     Self-powered, no bus-sharing, single IDE device
            //
            // This code will wait 500ms before turning off the drive.
            // This is to debounce any short sleep conditions that occur, like the 
            // ~75ms sleep period that many hosts do during startup.
            //////////////////////////////////////////////////
    USBCS |= bmNOSYNSOF;
    USBIRQ |= bmSOF;
    for (i = 0; i < 100; i++) {
      EZUSB_Delay(1);
      if ((USBIRQ & bmSOF) && !VBUS_PRESENT) break;
    }
   
    if (!((USBIRQ & bmSOF) && !VBUS_PRESENT)) standbyImmediate();
   
    while (!VBUS_PRESENT);
      
            // Now that we're done waiting for WAKEUP# exclusively, make sure we wake up on D+ too!
    WAKEUPCS = 0x45;
      
    EZUSB_Delay(30);       // Switch debounce time -- It's okay to use EZUSB_Delay here because we will never return to any version that we may have interrupted.
    softReset();
  
#else
    abortGPIF();
	// Reset the FIFOs
    FIFORESET = 6;
    ResetAndArmEp2();
   
    if (bATA_EN || (!bNewAt2pinout && (WAKEUPCS & 0x40))) triStateATABus();
      
            // Just sit here until we are re-enabled.
    EA = 0;  // In case we have higher priority interrupts
    EZUSB_Delay(30);       // Switch debounce time -- It's okay to use EZUSB_Delay here because we will never return to any version that we may have interrupted.
      
            // New pinout - Now that we're just waiting for WAKEUP# to go high, set polarity to active high, enable ONLY that wakeup source and sleep
            // Old pinout - WAKEUP = ATA_EN, PA6 = VBUS_PRESENT.  If we're here due to WAKEUP, go to sleep.  If not we must stay awake.
    if (bNewAt2pinout || (WAKEUPCS & 0x40)) {
      WAKEUPCS = 0x51;	// bmWU | bmWUPOL | bmWUEN
      EZUSB_Susp();                        // Place processor in idle mode.
    }else while (!VBUS_PRESENT);
      
            // Some designs use this signal to swap drives.  Force drive ID on restart.
    if (bSEARCH_ATA_ON_WAKEUP) deviceCount = 0x80;
   
            // Now that we're done waiting for WAKEUP# exclusively, make sure we wake up on D+ too!
    WAKEUPCS = 0x45;
      
    EZUSB_Delay(30);       // Switch debounce time -- It's okay to use EZUSB_Delay here because we will never return to any version that we may have interrupted.
    softReset();
#endif
  }else if (!VBUS_POWERED) {
         // Turn on the bus for non-bus-powered drives here.  Bus-powered drives will be turned on 
         // in the background after the bus is turned on.
    driveATABus();
    if (deviceCount == 1 || deviceCount == 2) USBCS = 0x02;	// Connect to USB if we've completed drive ID.
  }
}

BYTE checkCBW() {
   // Check for "USBC"
  if (EP2FIFOBUF[0] != 'U' ||
      EP2FIFOBUF[1] != 'S' ||
      EP2FIFOBUF[2] != 'B' ||
      EP2FIFOBUF[3] != 'C')
   return(USBS_FAILED);
  if (EP2BC != 31) return(USBS_FAILED);
  return(USBS_PASSED);
}

BYTE processConfigCBCommand() {
  BYTE addr = EP2FIFOBUF[CONFIG_CB_EEPROM_ADDR_LSB];
  BYTE ConfigCBSubCommand = EP2FIFOBUF[CONFIG_CB_SUBCOMMAND_OFFSET];

  switch (ConfigCBSubCommand) {
    case CONFIG_CB_SUBCOMMAND_EEPROM_RW: {
         WORD len;

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

         if (!directionIn)
            {
            if (EEPROMWrite(dataTransferLenLSW, CONFIG_SPACE_START))
               {
               stallEP2OUT();
               return(USBS_FAILED);
               }
            }
         else
            {
            while (dataTransferLenLSW)
               {
               len = min(dataTransferLenLSW,wPacketSize);

               // wait for an available EP buffer
               while (EP6CS & 0x08)
                  ;
               if(EEPROMRead(addr+CONFIG_SPACE_START,len,EP6FIFOBUF))
                  {
                  failedIn();
                  return(USBS_FAILED);
                  }
               EP6BCH = MSB(len);
               EP6BCL = LSB(len);
               dataTransferLenLSW -= len;
               addr += wPacketSize;
               }
            }
         return(USBS_PASSED);
         }
      case CONFIG_CB_SUBCOMMAND_MFG:
         {
         // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;

         /**************************************************************************
         Byte	Bit(s)	Test / 3-State Control Register Name
         0	    0	       Reserved
         0	    3:1	DA[2:0]
         0	    5:4	CS#[1:0]
         0	    6	VBUS
         0	    7	ARESET#
   
         1	    0	NDIOW
         1	    1	NDIOR
         1	    2	NDMACK
         1	    3 IORDY
         1      4 DMARQ
         1      5 ATA_EN
         1      6 MFG_SEL
         1	    7	DD[15:0] 3-State  Active hi 3-state buffer enable for ATA data bus.
   
         2	    7:0	DD[7:0]
         3	    7:0	DD[15:8]
         4	    7:0	Reserved
         5	    7:0	Reserved
         6	    7:0	Reserved
         **************************************************************************/
         if (directionIn)
            {
            OEA = 0x00;       // Allow us to read the pins, not drive 'em.
            waitForInBuffer();
            AUTOPTR2L = LSB(EP6FIFOBUF);
            XAUTODAT2 = IOA;     // Byte 0
            XAUTODAT2 = (GPIFIDLECTL & 0x7)     // Bits 0-2 
               | ((GPIFREADYSTAT & 0x3) << 3)   // Bits 3-4
               | ((WAKEUPCS & 0x40) >> 1)       // Bit 5
               | ((BYTE)mfgMode << 6)           // Bit 6
               | (OEB & 0x80);                  // Bit 7
            XAUTODAT2 = IOB;
            XAUTODAT2 = IOD;
            EP6BCH = 0;
            EP6BCL = dataTransferLenLSW;
            }
         else
            {
            // Wait for host to send data
            while (EP2CS & 0x04);
      
            IOA = EP2FIFOBUF[0];
            OEA = PORTA_OE;            // PORTA is all output except PA.6(VBUS) and PA.0 (IRQ)
            PORTACFG = 0;          // Turn off alternate function
            GPIFIDLECTL = 0x70 | (EP2FIFOBUF[1] & 0x7);
   
            IFCONFIG &= ~3;   // Turn off GPIF control of ports B and D.
   
            if (EP2FIFOBUF[1] & 0x80)
               {
               IOB = EP2FIFOBUF[2];
               IOD = EP2FIFOBUF[3];
               OEB = 0xff;
               OED = 0xff;
               }
            else
               {
               OEB = 0;
               OED = 0;
               }
   
            // Give up the buffer
            EP2BCL = 0x80; 
            }
         dataTransferLen = 0;
         return(USBS_PASSED);
         }
      // ATACB
      #if ATACB_ENABLE
      case CONFIG_CB_COMMAND:
         {
         return(processATACB());
         }
      #endif
      default:
         // relinquish control of the bulk buffer occupied by the CBW
         EP2BCL = 0x80;

         if (dataTransferLen)
            {
            if (directionIn)
               failedIn();
            else
               stallEP2OUT();
            }

         return (USBS_FAILED);
      }   
}

void enterMfgMode()
{
   // Default hardware setup
   bATA_UDMA_ENABLE             = 1;
   bATAPI_UDMA_ENABLE           = 1;
   bWAIT_FOR_BUSY_BIT           = 0;
   bENABLE_WRITE_CACHE_MODE_PAGE= 0;

   bCOMPLIANCE_MODE             = 0;
   bSHORT_PACKET_BEFORE_STALL   = 1;
   bSRST_ENABLE                 = 1;
   bSKIP_PIN_RESET              = 1;

   bBUTTON_PINOUT               = 0;
   bATA_ENABLED                 = 1;
   bBIG_PACKAGE                 = 1;
   bATA_EN                      = 1;

   bNewAt2pinout                = 1;
   bHS_INDICATOR                = 0;
   bDRVPWRVLD_POLARITY          = 0;
   bDRVPWRVLD_ENABLE            = 0;      // CF!
   setDefaultConfig();

//   mymemmovexx(halfKBuffer, (char xdata *) &DeviceDscr, (WORD)&DscrEndOffset);
   ((BYTE xdata*)FullSpeedConfigDscr)[9+6]=0xff;	// non-USB-class device, vendor-specific
   ((BYTE xdata*)StringDscr2)[2]='M';
   bDRVPWRVLD_ENABLE = 0;     // Turn off CF
   ATA_ENABLED = 0;           // Turn off ATA
   mfgMode = 1;               // We can also go into mfg mode if RESET# is pulled low.  This is checked in initUSB after the ports have been configured.
}

void setDefaultConfig()
{
   // Default hardware setup
//   bATA_UDMA_ENABLE             = 1;
//   bATAPI_UDMA_ENABLE           = 1;
//   bWAIT_FOR_BUSY_BIT           = 0;
//   bENABLE_WRITE_CACHE_MODE_PAGE= 0;
//
//   bCOMPLIANCE_MODE             = 0;
//   bSHORT_PACKET_BEFORE_STALL   = 1;
//   bSRST_ENABLE                 = 1;
//   bSKIP_PIN_RESET              = 1;

//   bBUTTON_PINOUT               = 0;
//   bATA_ENABLED                 = 1;
//   bBIG_PACKAGE                 = 1;
//   bATA_EN                      = 1;

//   bNewAt2pinout                = 1;
//   bHS_INDICATOR                = 0;
//   bDRVPWRVLD_POLARITY          = 0;
//   bDRVPWRVLD_ENABLE            = 0;      // CF!


   // Default config
   mx2_config_data.Signature = AT2LP_EEPROM_SIGNATURE;
   mx2_config_data.ApmValue = 0;
   mx2_config_data.AtaInitTimeout = 0x80;
   mx2_config_data.UdmaConfig = 0xD4;
   mx2_config_data.PioConfig = 0x7;
   mx2_config_data.AtaCommand = CONFIG_CB_COMMAND;
   mx2_config_data.Lun0String = mx2_config_data.Lun1String = 0;
   mx2_config_data.GpioData = 0;
   mx2_config_data.GpioOE = 0;
   mx2_config_data.delayAfterReset = 0;   // 20ms chunks.  0 = 50 for backward compatibility.
   mx2_config_data.MiscConfig2 = 0;        
   miscConfig2 = 0;
}

void triStateATABus()
{
   
   // Data bus is already tristated between read/writes
   // Tri-state port A
   // Tristate GPIF CTL lines 
   GPIFCTLCFG = 0x80;   // 
   GPIFIDLECTL = 0;
   OEA = 0;
   OEC = PORTC_OE_SUSPEND;
   IOEShadow |= nPWR500;    // Turn off the CF power
   IOE = IOEShadow;
   OEE = PORTE_OE_SUSPEND | (mx2_config_data.GpioOE << 2);
}

void driveATABus()
{
   GPIFCTLCFG = 0x0;   // 
   GPIFIDLECTL = 0x77;  // x111x111 - CTL3 not enabled
                        // ||||||||_CTL0 = 1 during idle
                        // |||||||__CTL1 = 1 during idle
                        // ||||||___CTL2 = 1 during idle
                        // ||||_____CTL0 output enable
                        // |||______CTL1 output enable
                        // ||_______CTL2 output enable

   OEA = PORTA_OE;
   OEC = PORTC_OE;
   OEE = PORTE_OE | (mx2_config_data.GpioOE << 2);
   checkGPIOonPA3();
}

// When we are bus-powered, we have to tri-state the ATA bus 
// when we are powered off.  Otherwise power will leak
// through the ATA bus signals an mess up the startup.
void powerOff()
{
   IOEShadow |= nPWR500;    // Turn off the CF power
   IOE = IOEShadow;
   
   // If we're a bus-powered device, the ATA bus mus be floated during suspend.
   if(VBUS_POWERED)
      triStateATABus();
}

void powerOn()
{
   if (Configuration == 1)
      // "PWR500 will only be asserted if VBUSPWRD is asserted (or when we don't have a VBUSPWRD input)"
      if (VBUS_POWERED)
         {
         if (CF_DETECT_ || ejected || !bDRVPWRVLD_ENABLE)
            return;

         IOEShadow &= ~nPWR500;      // Turn on the CF power
         IOE = IOEShadow;
         driveATABus();        // Only manipulate the bus drivers if we're bus-powered.
         EZUSB_Delay(DELAY_AFTER_POWERUP);            // Wait for device to power up
         hardwareReset();
         EZUSB_Delay(90);            // Mitsumi CR-4808TE(CYSD007) is a good test for this number.
         }
}


Vorgefundene Kodierung: UTF-80