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