📄 scsi.c
字号:
//-----------------------------------------------------------------------------
// File: scsi.c
// Contents: Functions to handle a scsi device.
//
// indent 3. NO TABS!
//
// Description:
// SCSI devices differ from IDE device as follows:
// - SCSI devices may accept or return data according to the value of
// its byte count registers which may differ from the host request
// indicated in the CBW variable 'dataTransferlen'.
//
// If the host requests to write a greater amount than the byte count
// registers indicate, the extra data is processed by throwing it on
// the floor and reporting it as a residue.
//
// If the host requests a read greater than what is indicated in the
// byte count registers, and the last packet is a full packet, a short
// packet is sent to terminate the transfer.
//
// - generalSCSIInCommand() and generalSCSIOutCommand() are called from
// periph.c. Their conditional compile flags are kept inside the function
// to maintain original code for periph.c. All other functions in this
// module are specific for SCSI and are static to this file. They are
// under one conditional compile flag.
//
//-----------------------------------------------------------------------------
// $Archive: /USB/atapifx2/CY4611B/scsi.c $
// $Date: 5/27/05 4:11p $
// $Revision: 4 $
//
//-----------------------------------------------------------------------------
// Copyright (c) 1999 Cypress Semiconductor, Inc. All rights reserved
//-----------------------------------------------------------------------------
#include "fx2.h"
#include "fx2regs.h"
#include "scsi.h"
#include "gpif.h"
#include "globals.h"
// this bit determines if the current transfer is to be carried out using UDMA (1) or
// PIO (0)
#define SENSE_LEN 18
//-----------------------------------------------------------------------------
// Function: generalSCSIInCommand()
//
// Input: none
// Output: bit flag
// 0 = success, 1 = failure
//
// Global data:
// CBW from EP2FIFOBUF.
//
// Description:
// Top level handler for scsi read. The scsi command packet is
// a 12-byte packet extracted from the CBW contained in EP2FIFOBUF
// starting from byte 15. If the command fails, the IN endpoint buffer
// is stalled and the transaction is failed.
//
// If the command was processed successfully, data is extracted from the
// drive till the byte count from the drive is exhausted. If the byte count
// indicated by the drive is less than what is requested by the host, the IN
// endpoint is stalled, but the transaction is passed. The remainder of bytes
// the host still expects is reported as a residue.
//-----------------------------------------------------------------------------
BYTE generalSCSIInCommand()
{
#if DEVICE_TYPE_IS_SCSI
BYTE result;
// Clear the bit telling us if we need a STALL to terminat the IRP on the host side
bShortPacketSent = 0;
result = sendSCSICommand();
// Need to modify this code so that we know if we sent a short packet to terminate the xfer.
// Although the STALL is required, the ScanLogic driver and Mac driver will not properly handle it.
if(result != USBS_PASSED)
{
failedIn(); // stalls EP6
return(USBS_FAILED);
}
// no need to do the data xfer phase if the host isn't expecting any data.
if (!dataTransferLen)
{
if (readATAPI_STATUS_REG() & ATAPI_STATUS_DRQ_BIT)
{
return(USBS_PHASE_ERROR); // USBS_PHASE_ERROR -- Hn < Di (case 2) or Hn < Do (case 3)
}
else
return(USBS_PASSED);
}
//////////////////////////////////////////////////////////////////
// Start of data xfer phase
//////////////////////////////////////////////////////////////////
if (bUseUdma)
{
result = scsiReadUdma();
}
else
{
result = scsiReadPio();
}
if (dataTransferLen)
{
// Case H(i) > D(i) or H(i) > D(n)
// "terminate the transfer with a short packet, then STALL the IN endpoint"
failedIn(); // only stalls EP6, does not return error
// Pass the result to the next layer up.
return(result);
}
else
return(result); // No residue, just return status
#else
return(0);
#endif
}
//-----------------------------------------------------------------------------
// Function: generalSCSIOutCommand()
//
// Input: none
// Output: bit flag
// 0 = success, 1 = failure
//
// Global data:
// CBW from EP2FIFOBUF.
//
// Local data:
// cmd - command op code from CBW
// result - return status
//
// Description:
// The scsi command packet is a 12-byte packet extracted from the CBW
// contained in EP2FIFOBUF starting from byte 15. If the command fails,
// the OUT endpoint buffer is stalled and the transaction is failed.
//
// If the command is successful, data is sent to the drive.
// When the write encounters an error, the endpoint is stalled and the
// transaction is failed.
//
//-----------------------------------------------------------------------------
BYTE generalSCSIOutCommand()
{
#if DEVICE_TYPE_IS_SCSI
// Init local vars.
BYTE result = USBS_FAILED;
bUseUdma = 0;
// if the drive is configured for udma then use udma depending on the
// scsi command
if (ActiveLunConfigData.udmaMode)
{
bUseUdma = 1;
}
result = sendSCSICommand();
// If the command failed, stall the endpoint and get out
if (result != USBS_PASSED)
{
stallEP2OUT();
return(USBS_FAILED);
}
if (!dataTransferLen)
return(result);
if (bUseUdma)
{
return(scsiWriteUdma());
}
else
{
return(scsiWrite());
}
#endif
#if DEVICE_TYPE_IS_IDE
return(0);
#endif
}
#define SENSE_LEN 18
///////////////////////////////////////////////////////////////////////////////
#if DEVICE_TYPE_IS_SCSI
///////////////////////////////////////////////////////////////////////////////
WORD getDriveDataLen()
{
WORD driveDataLen;
driveDataLen = readPIO8(ATAPI_BYTE_COUNT_MSB) << 8;
driveDataLen |= readPIO8(ATAPI_BYTE_COUNT_LSB);
return(driveDataLen);
}
//-----------------------------------------------------------------------------
// Function: scsiWrite()
//
// Input: none
// Output: bit flag
// 0 = success, 1 = failure
//
// Global data:
// dataTransferLen - Amount of data requested by the host. Counts down.
// EP2CS - Endpoint buffer status byte.
// EP2BCL - Endpoint LSB byte count register.
//
// Local data:
// wDriveDataLen - Amount of data drive will accept.
// wAmountToWrite - Amount of data to write to the drive. This is typically
// a portion wDriveDataLen, and is qualified by the packet lenght.
// wAmountSent - Amount of data sent to the drive. Counts up to ensure we don't
// exceed the packet size.
// driveStatus - Drive status from last action.
// bDone - loop control flag
//
// Description:
// This function handles the special case of scsi MODE PAGE write (MODE SELECT command).
// The drive byte count and the DRQ bit are used to determine how much data to send.
// Typically, the host requests to send data that is much larger than what the
// drive wants to accept.
//
// The drive may want to receive data in chunks less than a packet size, and the
// total number of bytes to satisfy the command may be less or greater than
// a packet length. Drive data request is processed as follows:
//
// (1) Total number of bytes is less than a packet, and drive wants to receive it in
// increments. The rest of the packet must be processed out of the buffer in
// the size requested by the drive. This transaction is governed by the
// DRQ bit in the status register.
//
// (2) Total number of bytes is greater than a packet, but drive wants it
// in increments less than a packet lenght. Full packet(s) are processed out
// of the buffer using logic from (1) until the drive is satisfied. This
// transaction is governed by the DRQ bit in the status register as well as the
// byte count from the drive. Any data residue in the endpoint buffer is sent
// to ATAPI_NULL_REGISTER.
//
// If the host is determined to queue up packets to the buffer after the drive
// byte count has been satisfied (DRQ=0), the data is processed out of the endpoint
// buffer and discarded by setting the skip bit in the endpoint's byte count
// register.
//
// The DRQ bit is valid.
//
// If a write encounters an error, we return an error and let caller
// stall the endpoint.
//
//-----------------------------------------------------------------------------
BYTE scsiWrite(void)
{
WORD wDriveDataLen = 0;
BYTE driveStatus = 0;
WORD wAmountToWrite = 0;
WORD wAmountSent = 0;
bit bDone = 0;
bit bShortPacketReceived = 0;
BYTE cReturnStatus = USBS_PASSED;
while( (!bDone) && dataTransferLen)
{
// Make sure the data is already in the endpoint buffer
if(!(EP2CS & (bmEPEMPTY | bmEPSTALL)))
{
if (EP2BC & (wPacketSize - 1))
bShortPacketReceived = 1;
EP2BCL = 00; // Release the endpoint buffer to FIFO.
wAmountSent = 0;
while( (wPacketSize > wAmountSent) && (!bDone) && dataTransferLen)
{
// Make sure the write is successful, otherwise get out. -- First wait for the busy bit to clear.
if (!wDriveDataLen)
{
driveStatus = waitForDRQBit(1);
readATAPI_STATUS_REG(); // Clear interrupt for next round.
if(driveStatus & ATAPI_STATUS_ERROR_BIT)
{
cReturnStatus = USBS_FAILED;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -