//-----------------------------------------------------------------------------
// 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: ASCII (7 bit) | 2
|