📄 periph.c
字号:
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 = 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 = LunBits[CbwLun];
sensePtr = sensePtrs[CbwLun];
currentLunNum = CbwLun;
{
EP2FIFOCFG = EP6FIFOCFG = bmZEROLENIN | bmWORDWIDE; // 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);
}
// switch to PIO3
else if(ActiveLunConfigData.MaxPIO & 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;
}
// switch to PIO0
else
{
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);
}
// CONFIG CB
else if (EP2FIFOBUF[0xf] == mx2_config_data.AtaCommand)
{
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 = 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 = 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 & bmEPFULL)) && (!(EP6CS & bmEPSTALL))) // 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 & bmEP2EMPTY) || EP2FIFOBCL || EP2FIFOBCH)
ResetAndArmEp2();
// Fill the buffer & send the data back to the host
AUTOPTRL2 = 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 = bmEPSTALL;
while( !(EP6CS & bmEPEMPTY)) // Wait for an available buffer
;
EP6CS = bmEPSTALL;
phaseErrorState = 1;
}
}
}
// Stall the IN endpoint if we're in phase error state.
if (phaseErrorState)
{
while( !(EP6CS & bmEPEMPTY) )
;
EP6CS = bmEPSTALL;
}
#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 & bmEPEMPTY) )
;
EP6BCH = 0; // Terminate with NULL packet, then STALL. This
EP6BCL = 0; // addresses an issue with the current EHCI driver (1/02)
}
while( !(EP6CS & bmEPEMPTY) )
;
EP6CS = bmEPSTALL; // TPM
}
}
bit waitForBusyBit()
{
BYTE driveStatus;
do
{
driveStatus = readATAPI_ALT_STATUS_REG();
}
while((driveStatus & (ATAPI_STATUS_BUSY_BIT))); // Do-while
// 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);
else
return(USBS_PASSED);
}
#pragma DISABLE
void mymemmovexx(BYTE xdata * dest, BYTE xdata * src, WORD len)
{
AUTOPTR1H = MSB(dest);
AUTOPTR1L = LSB(dest);
while (len--)
{
XAUTODAT1 = *src++;
}
}
// Used in ISRs to avoid conflicts with mymemmovexx()
void mymemmovexxISR(BYTE xdata * dest, BYTE xdata * src, WORD len)
{
while (len--)
{
*dest++ = *src++;
}
}
void mymemmove(BYTE data * dest, BYTE data * src, BYTE len)
{
while (len--)
{
*dest++ = *src++;
}
}
// 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 * dest, BYTE xdata * src, BYTE len)
{
while (len--)
{
*dest++ = *src++;
}
}
//-----------------------------------------------------------------------------
// USB Interrupt Handlers
// The following functions are called by the USB interrupt jump table.
//-----------------------------------------------------------------------------
// Setup Data Available Interrupt Handler
void ISR_Sudav(void) interrupt 0
{
EZUSB_IRQ_CLEAR();
INT2CLR = bmSUDAV; // Clear SUDAV IRQ
SetupCommand();
}
void ISR_Ures(void) interrupt 0
{
// whenever we get a USB reset, we should revert to full speed mode
wPacketSize = FS_BULK_PACKET_SIZE;
EP6FIFOPFH = 0x80;
EP6FIFOPFL = 0x60;
EP6AUTOINLENH = MSB(wPacketSize);
EP6AUTOINLENL = LSB(wPacketSize);
abortGPIF();
FIFORESET = 6;
ResetAndArmEp2();
// clear the stall and busy bits that may be set
EP2CS = 0; // set EP2OUT to empty and clear stall
EP6CS = 0; // set EP6OUT to empty and clear stall
// Initialize USB variables to make chapter 9 happy
AlternateSetting = Configuration = 0;
EZUSB_IRQ_CLEAR();
INT2CLR = bmURES; // Clear URES IRQ
if (currentState != UNCONFIGURED)
{
EA = 0;
// force a soft reset after the iret.
softReset();
}
}
void ISR_Susp(void) interrupt 0
{
Sleep = TRUE;
EZUSB_IRQ_CLEAR();
INT2CLR = bmSUSP;
}
void ISR_Highspeed(void) interrupt 0
{
if (EZUSB_HIGHSPEED())
{
WORD CT4Count = 272;
// Look for CT4.3 (JK activity) inactive for 500us
// This loop was hand-counted to be 22/12 = 1.8us long, so 272 iterations are 500us
// With the CT2 check, this loop is 31/12 = 193 iterations are 500us
while(CT2 != 0xd && --CT4Count)
{
if (CT4 & 8)
CT4Count = 193;
}
if (CT2 == 0xc) // If we timed out in state C, bump the state machine to state D
{
CT1 = 2;
WRITEDELAY();
CT2 = 0xd;
WRITEDELAY();
CT1 = 0;
}
wPacketSize = HS_BULK_PACKET_SIZE;
EP6FIFOPFH = 0x99; // Allow the FIFO to hold 3 full packets + 0x190 bytes.
EP6FIFOPFL = 0x90;
}
EP6AUTOINLENH = MSB(wPacketSize);
EP6AUTOINLENL = LSB(wPacketSize);
EZUSB_IRQ_CLEAR();
INT2CLR = bmHSGRANT;
}
#define FW_STRETCH_VALUE_5 5
void ResetAndArmEp2()
{
// 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -