_Simulation and Device Driver Development_
by Eddy Quicksall and Ken Gibson 

Listing One
//       ScsiPortWritePortUchar and ScsiPortReadPortUchar
// These functions are used by the driver to read and write to I/O mapped 
// registers. Routines are used to simulate the Adaptec 1540. Writing a 
// control register initiates some action in the hardware. Reading returns 
// appropriate status bits.
//
VOID ScsiPortWritePortUchar( IN PUCHAR Port, IN UCHAR Value)
{
    switch( (UINT)Port )  {
        case CTRL_STAT_REG:
            if( Value & IOP_INTERRUPT_RESET ) {
                // Clear any pending interrupt
                interruptReg = 0;
            }
            if( Value & IOP_HARD_RESET ) {
                statusReg = (IOP_SCSI_HBA_IDLE
                             +IOP_MAILBOX_INIT_REQUIRED);
            }
            break;
        case CMD_DATA_REG:
            if( statusReg & IOP_COMMAND_DATA_OUT_FULL ) {
                // Already have a command, ignore like 1540 does
                break;
            }
            else  {
                if( inInitMbox ) {
                    switch( dataCount-- ) {
                        case 4:
                            // First byte tells number of mailboxes
                            mBoxCount = Value;
                            break;
                        case 3:
                            //Third byte of the mailbox address
                            *(ULONG *)&pMailboxOut |= (Value << 16);
                            break;
                        case 2:
                            *(ULONG *)&pMailboxOut |= (Value << 8);
                            break;
                        case 1:
                            *(ULONG *)&pMailboxOut |= Value;
                            pMailboxIn = (MBI*)(pMailboxOut
                                                + mBoxCount);
                            interruptReg |= IOP_COMMAND_COMPLETE
                                          | IOP_ANY_INTERRUPT;
                            inInitMbox = FALSE;
                            break;
                        default:
                            break;
                    }
                } else if (readSetupData) {
                    // Setup to return the number of bytes requested
                    // by the driver.
                    readSetupData = FALSE;
                    dataCount = Value;
                    intOnDataComplete = TRUE;
                    dataRegPtr = adapterSetupData;
                    statusReg |= IOP_DATA_IN_PORT_FULL;
                    adapterSetupData[4] = mBoxCount;
                    adapterSetupData[5] = (UCHAR)(((ULONG)pMailboxOut
                                          >> 16) & 0xFF);
                    adapterSetupData[6] = (UCHAR)(((ULONG)pMailboxOut
                                          >>  8) & 0xFF);
                    adapterSetupData[7] = (UCHAR)(((ULONG)pMailboxOut) & 0xFF);
                }
                else if ( dataCount > 0 ) {
                    // Just Throw away initialization bytes
                    if (--dataCount==0) {
                        interruptReg |= IOP_COMMAND_COMPLETE
                                      | IOP_ANY_INTERRUPT;
                    }
                } else {
                  statusReg &= ~IOP_INVALID_COMMAND;
                  switch( Value ) {
                    case AC_ADAPTER_INQUIRY:
                        // Set up to return 4 bytes of Adapter Data
                        // through the Command/Data register
                        dataRegPtr = adapterInquiryData;
                        dataCount = 4;
                        intOnDataComplete = TRUE;
                        statusReg |= IOP_DATA_IN_PORT_FULL;
                        break;
                    case AC_RET_CONFIGURATION_DATA:
                        // Set up to return 3 bytes of Config Data
                        // through the Command/Data register
                        dataRegPtr = adapterConfigData;
                        dataCount = 3; // 3 bytes of config data
                        intOnDataComplete = TRUE;
                        statusReg |= IOP_DATA_IN_PORT_FULL;
                        break;
                    case AC_RETURN_SETUP_DATA:
                        readSetupData = TRUE;
                        break;
                    case AC_SET_HA_OPTION:
                        dataCount = 2;
                        intOnDataComplete = TRUE;
                        break;
                    case AC_SET_MAILBOX_INTERFACE:
                        dataCount = 2;
                        intOnDataComplete = TRUE;
                        break;
                    case AC_GET_BIOS_INFO:
                        // 1540 returns 2 bytes of BIOS info
                        dataRegPtr = adapterBiosData;
                        dataCount = 2;
                        intOnDataComplete = TRUE;
                        statusReg |= IOP_DATA_IN_PORT_FULL;
                        break;
                    case AC_MAILBOX_INITIALIZATION:
                        // Driver provides number of mailboxes and
                        // the 3-byte physical address
                        inInitMbox = TRUE;
                        dataCount = 4;
                        statusReg &= ~IOP_DATA_IN_PORT_FULL;
                        break;
                    case AC_SET_TRANSFER_SPEED:
                    case AC_SET_BUS_ON_TIME:
                    case AC_SET_BUS_OFF_TIME:
                        // Driver is going to give 1540 one byte of
                        // SCSI initialization parameters.
                        dataCount = 1;
                        break;
                    case AC_SET_SELECTION_TIMEOUT:
                        // Driver will give 4 bytes of SCSI bus
                        // initialization parameters
                        dataCount = 4;
                        break;
                    case AC_START_SCSI_COMMAND:
                        // Simulate executing a SCSI Command
                        {
                            int i, j;
                            CCB *pCmdBlock;
                            MBO *pMbo;  // Mailbox Out
                            MBI *pMbi;  // Mailbox In

                            // Find the new SCSI Command in the Mailbox
                            pMbo = pMailboxOut;
                            for( i=0; i<mBoxCount; ++i, ++pMbo ) {
                                if( pMbo->Command == MBO_START ) {
                                    pCmdBlock = (CCB*)
                                        (pMbo->Address.Lsb +
                                         (pMbo->Address.Mid<<8) +
                                         (pMbo->Address.Msb<<16));
                                    // For now just report good status
                                    // and return to the driver
                                    pCmdBlock->HostStatus =CCB_COMPLETE;
                                    pCmdBlock->TargetStatus = 0;
                                    pCmdBlock->DataLength.Lsb
                                        = pCmdBlock->DataLength.Mid
                                        = pCmdBlock->DataLength.Msb=0;
                                    pMbo->Command = MBO_FREE;

                                    // Post the mailbox back to the driver
                                    pMbi = pMailboxIn + mbiIndex;
                                    for( j=0; j<mBoxCount; ++j ) {
                                        if( pMbi->Status == MBI_FREE )
                                        {
                                            pMbi->Status =MBI_SUCCESS;
                                            pMbi->Address.Lsb
                                                = (UCHAR)((ULONG)
                                                pCmdBlock & 0xFF);
                                            pMbi->Address.Mid
                                                = (UCHAR)((ULONG)
                                                pCmdBlock>>8 & 0xFF);
                                            pMbi->Address.Msb
                                                = (UCHAR)((ULONG)
                                                pCmdBlock>>16 & 0xFF);
                                            mbiIndex
                                             = (mbiIndex+1)%mBoxCount;
                                            interruptReg
                                                  = IOP_MBI_FULL
                                                  | IOP_ANY_INTERRUPT;
                                            break;
                                        }
                                        mbiIndex = (mbiIndex+1)%mBoxCount;
                                        pMbi = pMailboxIn + mbiIndex;
                                    }
                                }
                            }
                        }
                        break;
                    default:
                        // This is an invalid command
                        interruptReg |= IOP_COMMAND_COMPLETE
                                      | IOP_ANY_INTERRUPT;
                        statusReg |= IOP_INVALID_COMMAND;
                        break;
                  } // end switch
                } // end else
            }
            break;
        default:
            break;      // Interrupt register not written
    }
}
UCHAR ScsiPortReadPortUchar( IN PUCHAR Port)
{
    UCHAR   retVal;
    // Just return the current contents of the appropriate register
    switch( (UINT)Port ) {
        case CTRL_STAT_REG:
            return statusReg;
        case INT_REG:
            return interruptReg;
        case CMD_DATA_REG:
            if( dataCount > 0 ) {
                retVal = *(dataRegPtr++);
                if( --dataCount == 0 ) {
                    // No more data, clear DATA IN PORT FULL
                    statusReg &= ~IOP_DATA_IN_PORT_FULL;
                    if( intOnDataComplete )  {
                        // Command is complete, post an interrupt
                        interruptReg |= IOP_COMMAND_COMPLETE
                                      | IOP_ANY_INTERRUPT;
                    }
                } else {
                    // Immediately indicate next byte is ready
                    statusReg |= IOP_DATA_IN_PORT_FULL;
                }
            } else {
                retVal = dataReg;
            }
            return retVal;
        default:
            // Driver is accessing some register that is not part of
            // the adapter.  May want to flag an error since this
            // could be a bug in the driver.  We will just return -1
             return 0xFF;
    }
}
// Read a memory location (the BIOS for our driver)
UCHAR ScsiPortReadRegisterUchar( IN PUCHAR Register )
{
    return 0;   // Give 0 since there is no BIOS in simulation
}
5



