📄 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/software/scsi.c $
// $Date: 1/21/02 2:38p $
// $Revision: 41 $
//
//-----------------------------------------------------------------------------
// Copyright (c) 1999 Cypress Semiconductor, Inc. All rights reserved
//-----------------------------------------------------------------------------
#include "fx2.h"
#include "fx2regs.h"
#include "scsi.h"
#include "gpif.h"
static BYTE scsiWrite(void);
static BYTE scsiWriteUdma();
static void prepareForATAPICommand();
static bit inDataFromDriveUdma();
// this bit determines if the current transfer is to be carried out using UDMA (1) or
// PIO (0)
bit useUdma;
bit bShortPacketSent;
BYTE udmaErrorCount;
#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 = 0;
// Clear the bit telling us if we need a STALL to terminat the IRP on the host side
bShortPacketSent = 0;
// if the drive is configured for udma then use udma depending on the
// scsi command
if (udmaMode)
{
switch(EP2FIFOBUF[0xf])
{
case 0x28:
case 0xA8:
useUdma = 1;
break;
default:
useUdma = 0;
break;
}
}
result = sendSCSICommand(EP2FIFOBUF + CBW_DATA_START);
// relinquish control of the bulk buffer occupied by the CBW
EP2BCL = 0x80;
// 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 EP8
return(USBS_FAILED);
}
// no need to do the data xfer phase if the host isn't expecting any data.
if (!dataTransferLenLSW && !dataTransferLenMSW)
{
// Make sure the status is correct
while (readATAPI_STATUS_REG() & ATAPI_STATUS_BUSY_BIT)
;
if (readATAPI_STATUS_REG() & ATAPI_STATUS_DRQ_BIT)
return(USBS_PHASE_ERROR); // USBS_PHASE_ERROR -- Hn < Di (case 2)
else
return(USBS_PASSED);
}
//////////////////////////////////////////////////////////////////
// Start of data xfer phase
//////////////////////////////////////////////////////////////////
if (useUdma)
{
result = inDataFromDriveUdma();
}
else
{
result = inDataFromDrive();
}
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 EP8, 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;
useUdma = 0;
// if the drive is configured for udma then use udma depending on the
// scsi command
if (udmaMode)
{
switch(EP2FIFOBUF[0xf])
{
case 0x2A:
case 0xAA:
useUdma = 1; //syk
break;
default:
useUdma = 0;
break;
}
}
result = sendSCSICommand(EP2FIFOBUF + CBW_DATA_START);
// relinquish control of the bulk buffer occupied by the CBW
EP2BCL = 0x80;
// If the command failed, stall the endpoint and get out
if (result != USBS_PASSED)
{
// If the transfer still contains data, and the xfer has not been terminated by a short packet
// then we must stop the transfer with a STALL.
if (dataTransferLen > 0)
{
// We may want to stall the endpoint here, but we must be careful to make sure that
// we don't set the stall bit when the xfer has completed!
stallEP2OUT();
}
return(USBS_FAILED);
}
if (!dataTransferLen)
return(result);
if (useUdma)
{
result = scsiWriteUdma();
}
else
{
result = scsiWrite();
}
return(result);
#endif
#if DEVICE_TYPE_IS_IDE
return(0);
#endif
}
#define SENSE_LEN 18
const char code testUnitReady[12] = { 0x00, 0x00, 0x00, 0x00, 00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const char code requestSense[12] = { 0x03, 0x00, 0x00, 0x00, SENSE_LEN, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// Send a TestUnitReady command to the device.
// If the device fails the testUnitReady command, return the sense code, else return 0
BYTE SCSITestUnitReady()
{
bit result = 0;
//char count;
if (!scsi)
return(result);
#if DEVICE_TYPE_IS_SCSI
useUdma = 0;
result = sendSCSICommand((char xdata *) testUnitReady);
if (result != USBS_PASSED)
{
result = sendSCSICommand((char xdata *) requestSense);
if (result != USBS_PASSED)
{
}
//result = waitForIntrq();
readPIO16toXdata(ATAPI_DATA_REG, halfKBuffer, SENSE_LEN, LISTEN_TO_DRIVE_LEN);
return(halfKBuffer[12]);
}
#endif
}
// Read the Inquiry info into our internal data structures.
// NOT prompted by the host.
#define INQUIRY_LEN 0x2c
const char code inquiryCommand[12] = { 0x12, 0x00, 0x00, 0x00, INQUIRY_LEN, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
void SCSIInquiryToATAPI()
{
#if DEVICE_TYPE_IS_SCSI
bit result;
useUdma = 0;
result = sendSCSICommand((char xdata *) inquiryCommand);
if (result != USBS_PASSED)
{
failedIn(); //This is an internal command, just leave if it fails.
return;
}
result = waitForIntrq();
readPIO16toXdata(ATAPI_DATA_REG, halfKBuffer, INQUIRY_LEN, LISTEN_TO_DRIVE_LEN);
if (halfKBuffer[SCSI_INQUIRY_DEVICE_CLASS] == 5)
{
intrfcSubClass = USB_MS_CD_ROM_SUBCLASS;
}
#endif
}
WORD getDriveDataLen()
{
WORD driveDataLen;
driveDataLen = readPIO8(ATAPI_BYTE_COUNT_MSB) << 8;
driveDataLen += readPIO8(ATAPI_BYTE_COUNT_LSB);
return(driveDataLen);
}
///////////////////////////////////////////////////////////////////////////////
#if DEVICE_TYPE_IS_SCSI
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
// 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.
//
//-----------------------------------------------------------------------------
static 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;
// See if drive is finished processing command and
// is ready for data.
while(readATAPI_STATUS_REG() & ATAPI_STATUS_BUSY_BIT)
;
driveStatus = readATAPI_STATUS_REG();
if (driveStatus & ATAPI_STATUS_DRQ_BIT)
{
while( (!(driveStatus & ATAPI_STATUS_ERROR_BIT)) && (!bDone) && dataTransferLen)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -