//-----------------------------------------------------------------------------
// 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.
}
}
| Detected encoding: ASCII (7 bit) | 2
|