www.pudn.com > ScriptsSample.zip > SSCSI.C


/*--------------------------------------------------*/ 
/*                                                  */ 
/* Module SSCSI.C                                   */ 
/*                                                  */ 
/* SCSI utility routines for Symbios 53C8XX.        */ 
/*                                                  */ 
/* Adapted from Symbios Logic                       */ 
/*    Software Development Kit                      */ 
/*                                                  */ 
/* Project: A Programmer's Guide to SCSI            */ 
/* Copyright (C) 1997, Brian Sawert.                */ 
/* All rights reserved.                             */ 
/*                                                  */ 
/*--------------------------------------------------*/ 
 
 
#include  
#include  
#include  
#include  
 
#include "gen_tool.h"         // generic tools 
#include "spci.h"             // PCI definitions 
#include "s8xx.h"             // Symbios 8XX definitions 
#include "sscsi.h"            // SCSI utility definitions 
 
#include "genscsi.h"          // compiled SCRIPTS code 
 
 
/*---------- defines and macros ----------*/ 
 
#define TABLE_SIZE 7          // number of table entries 
 
enum table_offsets {          // table offsets 
   SCSI_ID = 0, 
   MSGOUT_BUF, 
   CMD_BUF, 
   STAT_BUF, 
   MSGIN_BUF, 
   EXMSGIN_BUF, 
   DATAIN_BUF 
}; 
 
 
/*---------- global variables ----------*/ 
 
DWORD *pscript;               // pointer to script 
table_entry *ptable;          // pointer to table 
 
DWORD scsi_id;                // encoded SCSI ID 
BYTE msgout_buf[2];           // message out buffer 
BYTE cmd_buf[10];             // command buffer 
BYTE stat_buf;                // status byte 
BYTE msgin_buf[2];            // message in buffer 
BYTE exmsgin_buf[4];          // extended message buffer 
BYTE datain_buf[128];         // data in buffer 
BYTE block_buf[1024];         // block read buffer 
BYTE sense_buf[32];           // sense data buffer 
BYTE sense_key;               // sense data 
BYTE sense_asc; 
BYTE sense_ascq; 
 
 
 
/*---------- external variables ----------*/ 
 
/*---------- local functions ----------*/ 
 
/*---------- external functions ----------*/ 
 
/*---------- function definitions ----------*/ 
 
 
/*--------------------------------------------------*/ 
/*                                                  */ 
/* Routine to allocate and align buffer.            */ 
/*                                                  */ 
/* Usage:   DWORD *alloc_buffer(WORD size);         */ 
/*                                                  */ 
/* Parameters:                                      */ 
/*    size:          size of buffer needed          */ 
/*                                                  */ 
/* Return value:                                    */ 
/*    Returns pointer to aligned buffer on success, */ 
/*    0 otherwise.                                  */ 
/*                                                  */ 
/* Note:                                            */ 
/*    We never free this buffer, so we don't save   */ 
/*    the temporary pointer we use to allocate.     */ 
/*                                                  */ 
/*--------------------------------------------------*/ 
 
DWORD *alloc_buffer(WORD size) 
{ 
   BYTE *pbuf;                // temporary pointer 
   WORD seg, off;             // pointer parts 
   DWORD *newptr = NULL; 
 
 
   // allocate buffer 
   pbuf = malloc(size + 4); 
 
   if (pbuf) { 
   // allocated buffer memory 
   // DWORD align the buffer 
      seg = FP_SEG(pbuf); 
      off = FP_OFF(pbuf); 
      off += (4 - (off & 0x03)); 
      newptr = (DWORD *) MK_FP(seg, off); 
   } 
 
   return newptr; 
} 
 
 
 
/*--------------------------------------------------*/ 
/*                                                  */ 
/* Routine to check for completion by polling ISTAT */ 
/* register.                                        */ 
/*                                                  */ 
/* Usage:   int poll_istat(DWORD io_base,           */ 
/*    DWORD *vector, BYTE *dstat, BYTE *istat,      */ 
/*    WORD *sist);                                  */ 
/*                                                  */ 
/* Parameters:                                      */ 
/*    io_base:       host I/O base address          */ 
/*    vector:        pointer to result vector       */ 
/*    dstat:         pointer to DSTAT buffer        */ 
/*    istat:         pointer to ISTAT buffer        */ 
/*    sist:          pointer to SIEN & SIST buffers */ 
/*                                                  */ 
/* Return value:                                    */ 
/*    Returns 1 on success, 0 otherwise.            */ 
/*                                                  */ 
/*--------------------------------------------------*/ 
 
int poll_istat(DWORD io_base, DWORD *vector, 
   BYTE *dstat, BYTE *istat, WORD *sist) 
{ 
	DWORD starttime; 
   DWORD tempsist; 
 
 
   // get start time 
   starttime = Get_Time(); 
 
   // wait for interrupt or timeout 
	while (!(IORead8(io_base + ISTAT) & 0x3)) { 
   // no interrupt - check timeout 
 
		if (HasTimeElapsed(starttime, DEF_TIMEOUT)) { 
      // time has expired 
         return 0; 
      } 
	} 
 
	// got an interrupt - clear buffers 
	*istat = 0; 
	*dstat = 0; 
	*sist = 0; 
	*vector = 0xFFFFFFFFL; 
 
	while (IORead8(io_base + ISTAT) & 0x03) { 
	// read all interrupts 
 
		*istat |= IORead8(io_base + ISTAT); 
 
		if (*istat & 0x01) { 
      // DMA interrupt occurred 
      // read DMA status register 
			*dstat |= IORead8(io_base + DSTAT); 
      } 
 
		if (*istat & 0x02) { 
      // SCSI interrupt occurred 
      // read SCSI interrupt registers 
			tempsist = (IORead32(io_base + SIEN0)); 
			*sist |= (WORD) (tempsist >> 16); 
      } 
	} 
 
   if (*dstat & C8XX_INT_SIR) { 
   // a script interrupt occurred 
   // get the result vector 
		*vector = IORead32(io_base + DSPS); 
   } 
 
   // get the DMA FIFO full bit 
	*dstat |= IORead8(io_base + DSTAT); 
 
	return(1); 
} 
 
 
 
/*--------------------------------------------------*/ 
/*                                                  */ 
/* Routine to set up script and data buffers.       */ 
/*                                                  */ 
/* Usage:   int init_buffers(void);                 */ 
/*                                                  */ 
/* Parameters:                                      */ 
/*    None                                          */ 
/*                                                  */ 
/* Return value:                                    */ 
/*    Returns 1 on success, 0 otherwise.            */ 
/*                                                  */ 
/*--------------------------------------------------*/ 
 
int init_buffers(void) 
{ 
   int retval = 0; 
 
 
   // allocate script buffer 
   pscript = alloc_buffer(sizeof(GEN_SCRIPT)); 
 
   if (pscript) { 
   // allocated script memory 
      memcpy(pscript, GEN_SCRIPT, sizeof(GEN_SCRIPT)); 
       
      // allocate table memory 
      ptable = (table_entry *) alloc_buffer(TABLE_SIZE * 
         sizeof(table_entry)); 
 
      if (ptable) { 
      // allocated table memory 
 
         // set default select info 
         ptable[SCSI_ID].count = 0x33000000L; 
         ptable[SCSI_ID].address = 0L; 
 
         // set default message out 
         ptable[MSGOUT_BUF].count = 1L;  
         ptable[MSGOUT_BUF].address = 
            getPhysAddr(msgout_buf); 
   
         // set command buffer 
         ptable[CMD_BUF].count = 
            sizeof(cmd_buf); 
         ptable[CMD_BUF].address = 
            getPhysAddr(cmd_buf); 
 
         // set status buffer 
         ptable[STAT_BUF].count = 1L; 
         ptable[STAT_BUF].address = 
            getPhysAddr(&stat_buf); 
 
         // set message in buffer 
         ptable[MSGIN_BUF].count = 1L; 
         ptable[MSGIN_BUF].address = 
            getPhysAddr(msgin_buf); 
 
         // set extended message in buffer 
         ptable[EXMSGIN_BUF].count = 2L; 
         ptable[EXMSGIN_BUF].address = 
            getPhysAddr(exmsgin_buf); 
 
         // set data input buffer 
         ptable[DATAIN_BUF].count = 
            sizeof(datain_buf); 
         ptable[DATAIN_BUF].address = 
            getPhysAddr(datain_buf); 
       
         retval = 1; 
      } 
   } 
 
   return retval; 
} 
 
 
 
/*--------------------------------------------------*/ 
/*                                                  */ 
/* Routine to check status and retrieve sense       */ 
/* data if available.                               */ 
/*                                                  */ 
/* Usage:   int check_status(                       */ 
/*    pci_device *ppcidev,                          */ 
/*    scsi_device *pscsidev);                       */ 
/*                                                  */ 
/* Parameters:                                      */ 
/*    ppcidev:       pointer to PCI device          */ 
/*    pscsidev:      pointer to target SCSI device  */ 
/*                                                  */ 
/* Return value:                                    */ 
/*    Returns error code defined in SSCSI.H         */ 
/*                                                  */ 
/*--------------------------------------------------*/ 
 
int check_status(pci_device *ppcidev, 
   scsi_device *pscsidev) 
{ 
   int retval = ERR_OTHER; 
 
 
   switch (stat_buf) { 
   // handle status codes 
      case STAT_GOOD: 
      // command successful 
         retval = ERR_SUCCESS; 
         break; 
          
      case STAT_CHECK_CONDITION: 
      // sense data available 
          
         if (request_sense(ppcidev, 
            pscsidev) == ERR_SUCCESS) { 
         // got sense data 
 
            sense_key = sense_asc = sense_ascq = 0; 
 
            if ((sense_buf[0] & 0x7E) == 0x70) { 
            // valid sense data 
               sense_key = (sense_buf[2] & 0x0F); 
               sense_asc = sense_buf[12]; 
               sense_ascq = sense_buf[13]; 
            } 
 
            retval = ERR_SENSE; 
         } 
 
         break; 
 
      default: 
         break; 
   } 
 
   return retval; 
} 
 
 
 
/*--------------------------------------------------*/ 
/*                                                  */ 
/* Routine to execute Test Unit Ready command.      */ 
/*                                                  */ 
/* Usage:   int test_unit_ready(                    */ 
/*    pci_device *ppcidev,                          */ 
/*    scsi_device *pscsidev);                       */ 
/*                                                  */ 
/* Parameters:                                      */ 
/*    ppcidev:       pointer to PCI device          */ 
/*    pscsidev:      pointer to target SCSI device  */ 
/*                                                  */ 
/* Return value:                                    */ 
/*    Returns error code defined in SSCSI.H         */ 
/*                                                  */ 
/*--------------------------------------------------*/ 
 
int test_unit_ready(pci_device *ppcidev, 
   scsi_device *pscsidev) 
{ 
   int retval= ERR_OTHER; 
	BYTE dstat, istat; 
	WORD sist; 
	DWORD vector; 
 
 
	// set the DSA register to the table address 
	IOWrite32(ppcidev->io_base + DSA, 
      getPhysAddr(ptable)); 
 
	// set selection info 
   ptable[SCSI_ID].count = 
      (0x0300L | pscsidev->targ_id) << 16; 
 
   // set identify message 
   ptable[MSGOUT_BUF].count = 1L;  
   msgout_buf[0] = 0x80; 
 
   // set command buffer for Test Unit Ready 
   memset(cmd_buf, 0, sizeof(cmd_buf)); 
 
   ptable[CMD_BUF].count = 6L; 
   cmd_buf[0] = CMD_TEST_UNIT_READY; 
 
	// start script execution 
	IOWrite32(ppcidev->io_base + DSP, 
      getPhysAddr(pscript) + Ent_start_scsi); 
	 
   // wait for interrupt to complete 
	if (poll_istat(ppcidev->io_base, 
      &vector, &dstat, &istat, &sist)) { 
   // check interrupt type 
 
		if ((dstat & C8XX_INT_SIR) && 
         vector == A_err_cmd_complete) { 
      // SCRIPTS complete, SCSI complete 
 
			retval = ERR_SUCCESS; 
      } 
 
      // add other error codes here as needed 
 
		else if (sist & C8XX_INT_STO) { 
      // select timeout 
         retval = ERR_SELECT; 
      } 
	} 
	else { 
   // ISTAT poll timed out 
      retval = ERR_TIMEOUT; 
   } 
 
   return retval; 
} 
    
 
 
/*--------------------------------------------------*/ 
/*                                                  */ 
/* Routine to execute Request Sense command.        */ 
/*                                                  */ 
/* Usage:   int request_sense(                      */ 
/*    pci_device *ppcidev,                          */ 
/*    scsi_device *pscsidev);                       */ 
/*                                                  */ 
/* Parameters:                                      */ 
/*    ppcidev:       pointer to PCI device          */ 
/*    pscsidev:      pointer to target SCSI device  */ 
/*                                                  */ 
/* Return value:                                    */ 
/*    Returns error code defined in SSCSI.H         */ 
/*                                                  */ 
/*--------------------------------------------------*/ 
 
int request_sense(pci_device *ppcidev, 
   scsi_device *pscsidev) 
{ 
   int retval = ERR_OTHER; 
	BYTE dstat, istat; 
	WORD sist; 
	DWORD vector; 
 
 
	// set the DSA register to the table address 
	IOWrite32(ppcidev->io_base + DSA, 
      getPhysAddr(ptable)); 
 
	// set selection info 
   ptable[SCSI_ID].count = 
      (0x0300L | pscsidev->targ_id) << 16; 
 
   // set identify message 
   ptable[MSGOUT_BUF].count = 1L;  
   msgout_buf[0] = 0x80; 
 
   // set data input buffer 
   ptable[DATAIN_BUF].count = 14L; 
   ptable[DATAIN_BUF].address = 
      getPhysAddr(sense_buf); 
 
   // set command buffer for Request Sense 
   memset(cmd_buf, 0, sizeof(cmd_buf)); 
 
   ptable[CMD_BUF].count = 6L; 
   cmd_buf[0] = CMD_REQUEST_SENSE; 
	cmd_buf[4] = (BYTE) ptable[DATAIN_BUF].count; 
 
	// start script execution 
	IOWrite32(ppcidev->io_base + DSP, 
      getPhysAddr(pscript) + Ent_start_scsi); 
	 
   // wait for interrupt to complete 
	if (poll_istat(ppcidev->io_base, 
      &vector, &dstat, &istat, &sist)) { 
   // check interrupt type 
 
		if ((dstat & C8XX_INT_SIR) && 
         vector == A_err_cmd_complete) { 
      // SCRIPTS complete, SCSI complete 
 
			retval = check_status(ppcidev, 
            pscsidev); 
      } 
 
      // add other error codes here as needed 
 
		else if (sist & C8XX_INT_STO) { 
      // select timeout 
         retval = ERR_SELECT; 
      } 
	} 
	else { 
   // ISTAT poll timed out 
      retval = ERR_TIMEOUT; 
   } 
 
   return retval; 
} 
 
 
 
/*--------------------------------------------------*/ 
/*                                                  */ 
/* Routine to execute Inquiry command.              */ 
/*                                                  */ 
/* Usage:   int device_inquiry(                     */ 
/*    pci_device *ppcidev,                          */ 
/*    scsi_device *pscsidev);                       */ 
/*                                                  */ 
/* Parameters:                                      */ 
/*    ppcidev:    pointer to PCI device             */ 
/*    pscsidev:   pointer to target SCSI device     */ 
/*                                                  */ 
/* Return value:                                    */ 
/*    Returns error code defined in SSCSI.H         */ 
/*                                                  */ 
/*--------------------------------------------------*/ 
 
int device_inquiry(pci_device *ppcidev, 
   scsi_device *pscsidev) 
{ 
   int retval = ERR_OTHER; 
	BYTE dstat, istat; 
	WORD sist; 
	DWORD vector; 
 
 
	// set the DSA register to the table address 
	IOWrite32(ppcidev->io_base + DSA, 
      getPhysAddr(ptable)); 
 
	// set selection info 
   ptable[SCSI_ID].count = 
      (0x0300L | pscsidev->targ_id) << 16; 
 
   // set identify message 
   ptable[MSGOUT_BUF].count = 1L;  
   msgout_buf[0] = 0x80; 
 
   // set data input buffer 
   ptable[DATAIN_BUF].count = 36L; 
   ptable[DATAIN_BUF].address = 
      getPhysAddr(datain_buf); 
 
   // set command buffer for Inquiry 
   memset(cmd_buf, 0, sizeof(cmd_buf)); 
 
   ptable[CMD_BUF].count = 6L; 
   cmd_buf[0] = CMD_INQUIRY; 
	cmd_buf[4] = (BYTE) ptable[DATAIN_BUF].count; 
 
	// start script execution 
	IOWrite32(ppcidev->io_base + DSP, 
      getPhysAddr(pscript) + Ent_start_scsi); 
	 
   // wait for interrupt to complete 
	if (poll_istat(ppcidev->io_base, 
      &vector, &dstat, &istat, &sist)) { 
   // check interrupt type 
 
		if ((dstat & C8XX_INT_SIR) && 
         vector == A_err_cmd_complete) { 
      // SCRIPTS complete, SCSI complete 
 
			retval = check_status(ppcidev, 
            pscsidev); 
      } 
 
      // add other error codes here as needed 
 
		else if (sist & C8XX_INT_STO) { 
      // select timeout 
         retval = ERR_SELECT; 
      } 
	} 
	else { 
   // ISTAT poll timed out 
      retval = ERR_TIMEOUT; 
   } 
 
   return retval; 
} 
 
 
 
/*--------------------------------------------------*/ 
/*                                                  */ 
/* Routine to execute Read Capacity command.        */ 
/*                                                  */ 
/* Usage:   int read_capacity(                      */ 
/*    pci_device *ppcidev,                          */ 
/*    scsi_device *pscsidev);                       */ 
/*                                                  */ 
/* Parameters:                                      */ 
/*    ppcidev:    pointer to PCI device             */ 
/*    pscsidev:   pointer to target SCSI device     */ 
/*                                                  */ 
/* Return value:                                    */ 
/*    Returns error code defined in SSCSI.H         */ 
/*                                                  */ 
/*--------------------------------------------------*/ 
 
int read_capacity(pci_device *ppcidev, 
   scsi_device *pscsidev) 
{ 
   int retval = ERR_OTHER; 
	BYTE dstat, istat; 
	WORD sist; 
	DWORD vector; 
 
 
	// set the DSA register to the table address 
	IOWrite32(ppcidev->io_base + DSA, 
      getPhysAddr(ptable)); 
 
	// set selection info 
   ptable[SCSI_ID].count = 
      (0x0300L | pscsidev->targ_id) << 16; 
 
   // set identify message 
   ptable[MSGOUT_BUF].count = 1L;  
   msgout_buf[0] = 0x80; 
 
   // set data input buffer 
   ptable[DATAIN_BUF].count = 8L; 
   ptable[DATAIN_BUF].address = 
      getPhysAddr(datain_buf); 
 
   // set command buffer for Read Capacity 
   memset(cmd_buf, 0, sizeof(cmd_buf)); 
 
   ptable[CMD_BUF].count = 10L; 
   cmd_buf[0] = CMD_READ_CAPACITY; 
 
	// start script execution 
	IOWrite32(ppcidev->io_base + DSP, 
      getPhysAddr(pscript) + Ent_start_scsi); 
	 
   // wait for interrupt to complete 
	if (poll_istat(ppcidev->io_base, 
      &vector, &dstat, &istat, &sist)) { 
   // check interrupt type 
 
		if ((dstat & C8XX_INT_SIR) && 
         vector == A_err_cmd_complete) { 
      // SCRIPTS complete, SCSI complete 
 
			retval = check_status(ppcidev, 
            pscsidev); 
      } 
 
      // add other error codes here as needed 
 
		else if (sist & C8XX_INT_STO) { 
      // select timeout 
         retval = ERR_SELECT; 
      } 
	} 
	else { 
   // ISTAT poll timed out 
      retval = ERR_TIMEOUT; 
   } 
 
   return retval; 
} 
 
 
 
/*--------------------------------------------------*/ 
/*                                                  */ 
/* Routine to execute Read command for block        */ 
/* oriented device.                                 */ 
/*                                                  */ 
/* Usage:   int read_block(                         */ 
/*    pci_device *ppcidev,                          */ 
/*    scsi_device *pscsidev,                        */ 
/*    DWORD blk_num, BYTE nblocks);                 */ 
/*                                                  */ 
/* Parameters:                                      */ 
/*    ppcidev:    pointer to PCI device             */ 
/*    pscsidev:   pointer to target SCSI device     */ 
/*    blk_num:    starting block number             */ 
/*    nblocks:    number of blocks to read          */ 
/*                                                  */ 
/* Return value:                                    */ 
/*    Returns error code defined in SSCSI.H         */ 
/*                                                  */ 
/* Note:                                            */ 
/*    This is an experimental routine that uses a   */ 
/*    the statically allocated buffer declared      */ 
/*    above. Watch the read size.                   */ 
/*                                                  */ 
/*--------------------------------------------------*/ 
 
int read_block(pci_device *ppcidev, 
   scsi_device *pscsidev, DWORD blk_num, BYTE nblocks) 
{ 
   int retval = ERR_OTHER; 
	BYTE dstat, istat; 
	WORD sist; 
	DWORD vector; 
   DWORD size; 
 
 
	// set the DSA register to the table address 
	IOWrite32(ppcidev->io_base + DSA, 
      getPhysAddr(ptable)); 
 
	// set selection info 
   ptable[SCSI_ID].count = 
      (0x0300L | pscsidev->targ_id) << 16; 
 
   // set identify message with disconnect privilege 
   ptable[MSGOUT_BUF].count = 1L; 
   msgout_buf[0] = 0xC0; 
 
   // set data input buffer 
   size = nblocks * pscsidev->blk_size; 
 
   if (size > sizeof(block_buf)) { 
   // buffer too small 
      return ERR_OTHER; 
   } 
 
   ptable[DATAIN_BUF].count = size; 
   ptable[DATAIN_BUF].address = 
      getPhysAddr(block_buf); 
 
   // set command buffer for Read 
   memset(cmd_buf, 0, sizeof(cmd_buf)); 
 
   ptable[CMD_BUF].count = 6L; 
   cmd_buf[0] = CMD_READ_6; 
   cmd_buf[1] = (BYTE) (blk_num >> 16) & 0x1F; 
   cmd_buf[2] = (BYTE) (blk_num >> 8) & 0xFF; 
   cmd_buf[3] = (BYTE) blk_num & 0xFF; 
   cmd_buf[4] = nblocks; 
 
	// start script execution 
	IOWrite32(ppcidev->io_base + DSP, 
      getPhysAddr(pscript) + Ent_start_scsi); 
	 
   // wait for interrupt to complete 
	if (poll_istat(ppcidev->io_base, 
      &vector, &dstat, &istat, &sist)) { 
   // check interrupt type 
 
		if ((dstat & C8XX_INT_SIR) && 
         vector == A_err_cmd_complete) { 
      // SCRIPTS complete, SCSI complete 
 
			retval = check_status(ppcidev, 
            pscsidev); 
      } 
 
      // add other error codes here as needed 
 
		else if (sist & C8XX_INT_STO) { 
      // select timeout 
         retval = ERR_SELECT; 
      } 
	} 
	else { 
   // ISTAT poll timed out 
      retval = ERR_TIMEOUT; 
   } 
 
   return retval; 
}