⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scsi.c

📁 Cy68013的应用——USB2.0接口转IDE、CF卡接口
💻 C
📖 第 1 页 / 共 2 页
字号:
//-----------------------------------------------------------------------------
//   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.
//
//-----------------------------------------------------------------------------
// $Workfile: scsi.c $
// $Date: 6/17/02 2:00p $
// $Revision: 8 $
//
//-----------------------------------------------------------------------------
//  Copyright (c) 1999 Cypress Semiconductor, Inc. All rights reserved
//-----------------------------------------------------------------------------
#include "fx2.h"
#include "fx2regs.h"
#include "scsi.h"
#include "gpif.h"
#include "globals.h"

static BYTE scsiWrite(void);
static BYTE scsiWriteUdma();
static void prepareForATAPICommand();
static bit inDataFromDriveUdma();
static BYTE waitForDRQBit(bit expectedValue);


#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;

   useUdma = 0;

   // if the drive is configured for udma then use udma depending on the
   // scsi command
   if (ActiveLunConfigData.udmaMode)
   {
      switch(EP2FIFOBUF[0xf])
      {
         case 0x28:
         case 0xA8:
            useUdma = 1;     
            break;
         default:
            break;
      }
   }

   result = sendSCSICommand(EP2FIFOBUF + CBW_DATA_START, EP2FIFOBUF[CBW_CBW_LEN] & CBW_CBW_LEN_MASK);

   // 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 (ActiveLunConfigData.udmaMode)
   {
      switch(EP2FIFOBUF[0xf])
      {
         case 0x2A:
         case 0xAA:
            useUdma = 1;   //syk
            break;
         default:
            useUdma = 0;
            break;
      }
   }

   result = sendSCSICommand(EP2FIFOBUF + CBW_DATA_START, EP2FIFOBUF[CBW_CBW_LEN] & CBW_CBW_LEN_MASK);

   // 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
}


#if 0
#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;
   bit oldEA;
   BYTE retval;
   //char count;
    
   if (!bScsi)
      return(result);

#if DEVICE_TYPE_IS_SCSI
   useUdma = 0;
   result = sendSCSICommand((char xdata *) testUnitReady, 12);
   if (result != USBS_PASSED)
   {
      result = sendSCSICommand((char xdata *) requestSense, 12);
      if (result != USBS_PASSED)
         {
         }

      // We need to disable interrupts while using EP8FIFOBUF.  Here's why.  It is possible
      // (even probable) that we could get a USB reset while doing drive identification.
      // One of the things that occurs in the USB reset ISR is a reset of the EP8 FIFO.
      // It would be bad if that happened just as we were reading drive data into EP8FIFOBUF.
      // The following code segment will complete in a deterministic amount of time.
      oldEA = EA;
      EA = 0;

      //result = waitForIntrq();
      readPIO16(ATAPI_DATA_REG, min(getDriveDataLen(),SENSE_LEN));
      while (!gpifIdle());    // wait for the read to complete before continuing

      retval = EP8FIFOBUF[12];

      // We are done with EP8FIFOBUF.  reset the EP8FIFO so the internal pointers are pointing
      // back at the start of it and then re-enable interrupts.
      FIFORESET = 0x08;
      EA = oldEA;

      return(retval);
    }
#endif
}   
#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;
   

   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);
   
               if(driveStatus & ATAPI_STATUS_ERROR_BIT)
                  {
                  cReturnStatus = USBS_FAILED;  
                  bDone = 1;                    
                  break;
                  }
               // Check if drive is finished with transaction.
               else if(!(driveStatus & ATAPI_STATUS_DRQ_BIT))
                  {
                  bDone=1;
                  cReturnStatus = USBS_PASSED;
                  }
               else if ((readPIO8(ATAPI_INT_CAUSE_REG) & 2))  // On the first DRQ, check to see if the drive is trying to do IN our OUT (Ho <> Di)
                  {
                  bDone=1;      // No more data
                  cReturnStatus = USBS_PHASE_ERROR;
                  break;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -