/*
** *************************************************************************
**
**
**     R C L A N M T L . C             $Revision: 6 $
**
**
**  RedCreek I2O LAN Message Transport Layer program module.
**
**  ---------------------------------------------------------------------
**  ---     Copyright (c) 1997-1999, RedCreek Communications Inc.     ---
**  ---                   All rights reserved.                        ---
**  ---------------------------------------------------------------------
**
**  File Description:
**
**  Host side I2O (Intelligent I/O) LAN message transport layer.
**
**  This program is free software; you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation; either version 2 of the License, or
**  (at your option) any later version.

**  This program is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.

**  You should have received a copy of the GNU General Public License
**  along with this program; if not, write to the Free Software
**  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
** 1998-1999, LAN API was modified and enhanced by Alice Hennessy.
**
** Sometime in 1997, LAN API was written from scratch by Wendell Nichols.
** *************************************************************************
*/

#undef DEBUG

#define RC_LINUX_MODULE
#include "rclanmtl.h"

#define dprintf kprintf

extern int printk(const char * fmt, ...);

 /* RedCreek LAN device Target ID */
#define RC_LAN_TARGET_ID  0x10 
 /* RedCreek's OSM default LAN receive Initiator */
#define DEFAULT_RECV_INIT_CONTEXT  0xA17  


/*
** I2O message structures
*/

#define    I2O_TID_SZ                                  12
#define    I2O_FUNCTION_SZ                             8

/* Transaction Reply Lists (TRL) Control Word structure */

#define    I2O_TRL_FLAGS_SINGLE_FIXED_LENGTH           0x00
#define    I2O_TRL_FLAGS_SINGLE_VARIABLE_LENGTH        0x40
#define    I2O_TRL_FLAGS_MULTIPLE_FIXED_LENGTH         0x80

/* LAN Class specific functions */

#define    I2O_LAN_PACKET_SEND                         0x3B
#define    I2O_LAN_SDU_SEND                            0x3D
#define    I2O_LAN_RECEIVE_POST                        0x3E
#define    I2O_LAN_RESET                               0x35
#define    I2O_LAN_SHUTDOWN                            0x37

/* Private Class specfic function */
#define    I2O_PRIVATE                                 0xFF

/*  I2O Executive Function Codes.  */

#define    I2O_EXEC_ADAPTER_ASSIGN                     0xB3
#define    I2O_EXEC_ADAPTER_READ                       0xB2
#define    I2O_EXEC_ADAPTER_RELEASE                    0xB5
#define    I2O_EXEC_BIOS_INFO_SET                      0xA5
#define    I2O_EXEC_BOOT_DEVICE_SET                    0xA7
#define    I2O_EXEC_CONFIG_VALIDATE                    0xBB
#define    I2O_EXEC_CONN_SETUP                         0xCA
#define    I2O_EXEC_DEVICE_ASSIGN                      0xB7
#define    I2O_EXEC_DEVICE_RELEASE                     0xB9
#define    I2O_EXEC_HRT_GET                            0xA8
#define    I2O_EXEC_IOP_CLEAR                          0xBE
#define    I2O_EXEC_IOP_CONNECT                        0xC9
#define    I2O_EXEC_IOP_RESET                          0xBD
#define    I2O_EXEC_LCT_NOTIFY                         0xA2
#define    I2O_EXEC_OUTBOUND_INIT                      0xA1
#define    I2O_EXEC_PATH_ENABLE                        0xD3
#define    I2O_EXEC_PATH_QUIESCE                       0xC5
#define    I2O_EXEC_PATH_RESET                         0xD7
#define    I2O_EXEC_STATIC_MF_CREATE                   0xDD
#define    I2O_EXEC_STATIC_MF_RELEASE                  0xDF
#define    I2O_EXEC_STATUS_GET                         0xA0
#define    I2O_EXEC_SW_DOWNLOAD                        0xA9
#define    I2O_EXEC_SW_UPLOAD                          0xAB
#define    I2O_EXEC_SW_REMOVE                          0xAD
#define    I2O_EXEC_SYS_ENABLE                         0xD1
#define    I2O_EXEC_SYS_MODIFY                         0xC1
#define    I2O_EXEC_SYS_QUIESCE                        0xC3
#define    I2O_EXEC_SYS_TAB_SET                        0xA3


 /* Init Outbound Q status */
#define    I2O_EXEC_OUTBOUND_INIT_IN_PROGRESS          0x01
#define    I2O_EXEC_OUTBOUND_INIT_REJECTED             0x02
#define    I2O_EXEC_OUTBOUND_INIT_FAILED               0x03
#define    I2O_EXEC_OUTBOUND_INIT_COMPLETE             0x04


#define    I2O_UTIL_NOP                                0x00


/* I2O Get Status State values */

#define    I2O_IOP_STATE_INITIALIZING                  0x01
#define    I2O_IOP_STATE_RESET                         0x02
#define    I2O_IOP_STATE_HOLD                          0x04
#define    I2O_IOP_STATE_READY                         0x05
#define    I2O_IOP_STATE_OPERATIONAL                   0x08
#define    I2O_IOP_STATE_FAILED                        0x10
#define    I2O_IOP_STATE_FAULTED                       0x11


/* Defines for Request Status Codes:  Table 3-1 Reply Status Codes.  */

#define    I2O_REPLY_STATUS_SUCCESS                    0x00
#define    I2O_REPLY_STATUS_ABORT_DIRTY                0x01
#define    I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER     0x02
#define    I2O_REPLY_STATUS_ABORT_PARTIAL_TRANSFER     0x03
#define    I2O_REPLY_STATUS_ERROR_DIRTY                0x04
#define    I2O_REPLY_STATUS_ERROR_NO_DATA_TRANSFER     0x05
#define    I2O_REPLY_STATUS_ERROR_PARTIAL_TRANSFER     0x06
#define    I2O_REPLY_STATUS_PROCESS_ABORT_DIRTY        0x07
#define    I2O_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER   0x08
#define    I2O_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER   0x09
#define    I2O_REPLY_STATUS_TRANSACTION_ERROR          0x0A
#define    I2O_REPLY_STATUS_PROGRESS_REPORT            0x80


/* DetailedStatusCode defines for ALL messages: Table 3-2 Detailed Status Codes.*/

#define    I2O_DETAIL_STATUS_SUCCESS                        0x0000
#define    I2O_DETAIL_STATUS_BAD_KEY                        0x0001
#define    I2O_DETAIL_STATUS_CHAIN_BUFFER_TOO_LARGE         0x0002
#define    I2O_DETAIL_STATUS_DEVICE_BUSY                    0x0003
#define    I2O_DETAIL_STATUS_DEVICE_LOCKED                  0x0004
#define    I2O_DETAIL_STATUS_DEVICE_NOT_AVAILABLE           0x0005
#define    I2O_DETAIL_STATUS_DEVICE_RESET                   0x0006
#define    I2O_DETAIL_STATUS_INAPPROPRIATE_FUNCTION         0x0007
#define    I2O_DETAIL_STATUS_INSUFFICIENT_RESOURCE_HARD     0x0008
#define    I2O_DETAIL_STATUS_INSUFFICIENT_RESOURCE_SOFT     0x0009
#define    I2O_DETAIL_STATUS_INVALID_INITIATOR_ADDRESS      0x000A
#define    I2O_DETAIL_STATUS_INVALID_MESSAGE_FLAGS          0x000B
#define    I2O_DETAIL_STATUS_INVALID_OFFSET                 0x000C
#define    I2O_DETAIL_STATUS_INVALID_PARAMETER              0x000D
#define    I2O_DETAIL_STATUS_INVALID_REQUEST                0x000E
#define    I2O_DETAIL_STATUS_INVALID_TARGET_ADDRESS         0x000F
#define    I2O_DETAIL_STATUS_MESSAGE_TOO_LARGE              0x0010
#define    I2O_DETAIL_STATUS_MESSAGE_TOO_SMALL              0x0011
#define    I2O_DETAIL_STATUS_MISSING_PARAMETER              0x0012
#define    I2O_DETAIL_STATUS_NO_SUCH_PAGE                   0x0013
#define    I2O_DETAIL_STATUS_REPLY_BUFFER_FULL              0x0014
#define    I2O_DETAIL_STATUS_TCL_ERROR                      0x0015
#define    I2O_DETAIL_STATUS_TIMEOUT                        0x0016
#define    I2O_DETAIL_STATUS_UNKNOWN_ERROR                  0x0017
#define    I2O_DETAIL_STATUS_UNKNOWN_FUNCTION               0x0018
#define    I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION           0x0019
#define    I2O_DETAIL_STATUS_UNSUPPORTED_VERSION            0x001A

 /* I2O msg header defines for VersionOffset */
#define I2OMSGVER_1_5   0x0001
#define SGL_OFFSET_0    I2OMSGVER_1_5
#define SGL_OFFSET_4    (0x0040 | I2OMSGVER_1_5)
#define TRL_OFFSET_5    (0x0050 | I2OMSGVER_1_5)
#define TRL_OFFSET_6    (0x0060 | I2OMSGVER_1_5)

 /* I2O msg header defines for MsgFlags */
#define MSG_STATIC      0x0100
#define MSG_64BIT_CNTXT 0x0200
#define MSG_MULTI_TRANS 0x1000
#define MSG_FAIL        0x2000
#define MSG_LAST        0x4000
#define MSG_REPLY       0x8000

  /* normal LAN request message MsgFlags and VersionOffset (0x1041) */
#define LAN_MSG_REQST  (MSG_MULTI_TRANS | SGL_OFFSET_4)

 /* minimum size msg */
#define THREE_WORD_MSG_SIZE 0x00030000
#define FOUR_WORD_MSG_SIZE  0x00040000
#define FIVE_WORD_MSG_SIZE  0x00050000
#define SIX_WORD_MSG_SIZE   0x00060000
#define SEVEN_WORD_MSG_SIZE 0x00070000
#define EIGHT_WORD_MSG_SIZE 0x00080000
#define NINE_WORD_MSG_SIZE  0x00090000

/* Special TID Assignments */

#define I2O_IOP_TID   0
#define I2O_HOST_TID  0xB91

 /* RedCreek I2O private message codes */
#define RC_PRIVATE_GET_MAC_ADDR     0x0001/**/ /* OBSOLETE */
#define RC_PRIVATE_SET_MAC_ADDR     0x0002
#define RC_PRIVATE_GET_NIC_STATS    0x0003
#define RC_PRIVATE_GET_LINK_STATUS  0x0004
#define RC_PRIVATE_SET_LINK_SPEED   0x0005
#define RC_PRIVATE_SET_IP_AND_MASK  0x0006
/* #define RC_PRIVATE_GET_IP_AND_MASK  0x0007 */ /* OBSOLETE */
#define RC_PRIVATE_GET_LINK_SPEED   0x0008
#define RC_PRIVATE_GET_FIRMWARE_REV 0x0009
/* #define RC_PRIVATE_GET_MAC_ADDR     0x000A *//**/
#define RC_PRIVATE_GET_IP_AND_MASK  0x000B /**/
#define RC_PRIVATE_DEBUG_MSG        0x000C
#define RC_PRIVATE_REPORT_DRIVER_CAPABILITY  0x000D
#define RC_PRIVATE_SET_PROMISCUOUS_MODE  0x000e
#define RC_PRIVATE_GET_PROMISCUOUS_MODE  0x000f
#define RC_PRIVATE_SET_BROADCAST_MODE    0x0010
#define RC_PRIVATE_GET_BROADCAST_MODE    0x0011

#define RC_PRIVATE_REBOOT           0x00FF


/* I2O message header */
typedef struct _I2O_MESSAGE_FRAME 
{
    U8                          VersionOffset;
    U8                          MsgFlags;
    U16                         MessageSize;
    BF                          TargetAddress:I2O_TID_SZ;
    BF                          InitiatorAddress:I2O_TID_SZ;
    BF                          Function:I2O_FUNCTION_SZ;
    U32                         InitiatorContext;
    /* SGL[] */ 
}
I2O_MESSAGE_FRAME, *PI2O_MESSAGE_FRAME;


 /* assumed a 16K minus 256 byte space for outbound queue message frames */
#define MSG_FRAME_SIZE  512
#define NMBR_MSG_FRAMES 30

/*
**  Message Unit CSR definitions for RedCreek PCI45 board
*/
typedef struct tag_rcatu 
{
    volatile unsigned long APICRegSel;  /* APIC Register Select */
    volatile unsigned long reserved0;
    volatile unsigned long APICWinReg;  /* APIC Window Register */
    volatile unsigned long reserved1;
    volatile unsigned long InMsgReg0;   /* inbound message register 0 */
    volatile unsigned long InMsgReg1;   /* inbound message register 1 */
    volatile unsigned long OutMsgReg0;  /* outbound message register 0 */
    volatile unsigned long OutMsgReg1;  /* outbound message register 1 */
    volatile unsigned long InDoorReg;   /* inbound doorbell register */
    volatile unsigned long InIntStat;   /* inbound interrupt status register */
    volatile unsigned long InIntMask;   /* inbound interrupt mask register */
    volatile unsigned long OutDoorReg;  /* outbound doorbell register */
    volatile unsigned long OutIntStat;  /* outbound interrupt status register */
    volatile unsigned long OutIntMask;  /* outbound interrupt mask register */
    volatile unsigned long reserved2;
    volatile unsigned long reserved3;
    volatile unsigned long InQueue;     /* inbound queue port */
    volatile unsigned long OutQueue;    /* outbound queue port */
    volatile unsigned long reserved4;
    volatile unsigned long reserver5;
    /* RedCreek extension */
    volatile unsigned long EtherMacLow;
    volatile unsigned long EtherMacHi;
    volatile unsigned long IPaddr;
    volatile unsigned long IPmask;
}
ATU, *PATU;

 /* 
 ** typedef PAB
 **
 ** PCI Adapter Block - holds instance specific information and is located
 ** in a reserved space at the start of the message buffer allocated by user.
 */
typedef struct
{
    PATU             p_atu;                /* ptr to  ATU register block */
    PU8              pPci45LinBaseAddr;
    PU8              pLinOutMsgBlock;
    U32              outMsgBlockPhyAddr; 
    PFNTXCALLBACK    pTransCallbackFunc;
    PFNRXCALLBACK    pRecvCallbackFunc;
    PFNCALLBACK      pRebootCallbackFunc;
    PFNCALLBACK      pCallbackFunc;
    U16              IOPState;
    U16              InboundMFrameSize;
}
PAB, *PPAB;

 /* 
 ** in reserved space right after PAB in host memory is area for returning
 ** values from card 
 */

 /* 
 ** Array of pointers to PCI Adapter Blocks.
 ** Indexed by a zero based (0-31) interface number.
 */ 
#define MAX_ADAPTERS 32
static PPAB  PCIAdapterBlock[MAX_ADAPTERS];

/*
** typedef NICSTAT
**
** Data structure for NIC statistics retruned from PCI card.  Data copied from
** here to user allocated RCLINKSTATS (see rclanmtl.h) structure.
*/
typedef struct tag_NicStat 
{
    unsigned long   TX_good; 
    unsigned long   TX_maxcol;
    unsigned long   TX_latecol;
    unsigned long   TX_urun;
    unsigned long   TX_crs;         /* lost carrier sense */
    unsigned long   TX_def;         /* transmit deferred */
    unsigned long   TX_singlecol;   /* single collisions */
    unsigned long   TX_multcol;
    unsigned long   TX_totcol;
    unsigned long   Rcv_good;
    unsigned long   Rcv_CRCerr;
    unsigned long   Rcv_alignerr;
    unsigned long   Rcv_reserr;     /* rnr'd pkts */
    unsigned long   Rcv_orun;
    unsigned long   Rcv_cdt;
    unsigned long   Rcv_runt;
    unsigned long   dump_status;    /* last field directly from the chip */
} 
NICSTAT, *P_NICSTAT;
 

#define DUMP_DONE   0x0000A005      /* completed statistical dump */
#define DUMP_CLEAR  0x0000A007      /* completed stat dump and clear counters */


static volatile int msgFlag;


/* local function prototypes */
static void ProcessOutboundI2OMsg(PPAB pPab, U32 phyMsgAddr);
static int FillI2OMsgSGLFromTCB(PU32 pMsg, PRCTCB pXmitCntrlBlock);
static int GetI2OStatus(PPAB pPab);
static int SendI2OOutboundQInitMsg(PPAB pPab);
static int SendEnableSysMsg(PPAB pPab);


/* 1st 100h bytes of message block is reserved for messenger instance */
#define ADAPTER_BLOCK_RESERVED_SPACE 0x100

/*
** =========================================================================
** RCInitI2OMsgLayer()
**
** Initialize the RedCreek I2O Module and adapter.
**
** Inputs:  AdapterID - interface number from 0 to 15
**          pciBaseAddr - virual base address of PCI (set by BIOS)
**          p_msgbuf - virual address to private message block (min. 16K)
**          p_phymsgbuf - physical address of private message block
**          TransmitCallbackFunction - address of transmit callback function
**          ReceiveCallbackFunction  - address of receive  callback function
**
** private message block is allocated by user.  It must be in locked pages.
** p_msgbuf and p_phymsgbuf point to the same location.  Must be contigous
** memory block of a minimum of 16K byte and long word aligned.
** =========================================================================
*/
RC_RETURN
RCInitI2OMsgLayer(U16 AdapterID, U32 pciBaseAddr, 
                  PU8 p_msgbuf,  PU8 p_phymsgbuf,
                  PFNTXCALLBACK  TransmitCallbackFunction,
                  PFNRXCALLBACK  ReceiveCallbackFunction,
                  PFNCALLBACK    RebootCallbackFunction)
{
    int result;
    PPAB pPab; 
    
#ifdef DEBUG
    kprintf("InitI2O: Adapter:0x%04.4ux ATU:0x%08.8ulx msgbuf:0x%08.8ulx phymsgbuf:0x%08.8ulx\n"
            "TransmitCallbackFunction:0x%08.8ulx  ReceiveCallbackFunction:0x%08.8ulx\n",
            AdapterID, pciBaseAddr, p_msgbuf, p_phymsgbuf, TransmitCallbackFunction, ReceiveCallbackFunction);
#endif /* DEBUG */

    
    /* Check if this interface already initialized - if so, shut it down */
    if (PCIAdapterBlock[AdapterID] != NULL)
    {
        printk("PCIAdapterBlock[%d]!=NULL\n", AdapterID);
//        RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
        PCIAdapterBlock[AdapterID] = NULL;
    }

    /* 
    ** store adapter instance values in adapter block.
    ** Adapter block is at beginning of message buffer
    */  
    pPab = (PPAB)p_msgbuf;
    
    pPab->p_atu = (PATU)pciBaseAddr;
    pPab->pPci45LinBaseAddr =  (PU8)pciBaseAddr;
    
    /* Set outbound message frame addr - skip over Adapter Block */
    pPab->outMsgBlockPhyAddr = (U32)(p_phymsgbuf + ADAPTER_BLOCK_RESERVED_SPACE);
    pPab->pLinOutMsgBlock    = (PU8)(p_msgbuf + ADAPTER_BLOCK_RESERVED_SPACE);

    /* store callback function addresses */
    pPab->pTransCallbackFunc = TransmitCallbackFunction;
    pPab->pRecvCallbackFunc  = ReceiveCallbackFunction;
    pPab->pRebootCallbackFunc  = RebootCallbackFunction;
    pPab->pCallbackFunc  = (PFNCALLBACK)NULL;

    /*
    ** Initialize I2O IOP
    */
    result = GetI2OStatus(pPab);
    
    if (result != RC_RTN_NO_ERROR)
        return result;

    if (pPab->IOPState == I2O_IOP_STATE_OPERATIONAL)
    {
        printk("pPab->IOPState == op: resetting adapter\n");
        RCResetLANCard(AdapterID, 0, (PU32)NULL, (PFNCALLBACK)NULL);
    }
        
    result = SendI2OOutboundQInitMsg(pPab);
    
    if (result != RC_RTN_NO_ERROR)
        return result;

    result = SendEnableSysMsg(pPab);
   
    if (result != RC_RTN_NO_ERROR)
        return result;
   
    PCIAdapterBlock[AdapterID] = pPab;
    return RC_RTN_NO_ERROR;
}

/*
** =========================================================================
** Disable and Enable I2O interrupts.  I2O interrupts are enabled at Init time
** but can be disabled and re-enabled through these two function calls.
** Packets will still be put into any posted received buffers and packets will
** be sent through RCI2OSendPacket() functions.  Disabling I2O interrupts
** will prevent hardware interrupt to host even though the outbound I2O msg
** queue is not emtpy.
** =========================================================================
*/
#define i960_OUT_POST_Q_INT_BIT        0x0008 /* bit set masks interrupts */

RC_RETURN RCDisableI2OInterrupts(U16 AdapterID)
{
    PPAB pPab;


    pPab = PCIAdapterBlock[AdapterID];
    
    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
        
    pPab->p_atu->OutIntMask |= i960_OUT_POST_Q_INT_BIT;

    return RC_RTN_NO_ERROR;
}

RC_RETURN RCEnableI2OInterrupts(U16 AdapterID)
{
    PPAB pPab;

    pPab = PCIAdapterBlock[AdapterID];
    
    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
    
    pPab->p_atu->OutIntMask &= ~i960_OUT_POST_Q_INT_BIT;
    
    return RC_RTN_NO_ERROR;

}


/*
** =========================================================================
** RCI2OSendPacket()
** =========================================================================
*/
RC_RETURN
RCI2OSendPacket(U16 AdapterID, U32 InitiatorContext, PRCTCB pTransCtrlBlock)
{
    U32 msgOffset;
    PU32 pMsg;
    int size;
    PPAB pPab;

#ifdef DEBUG
    kprintf("RCI2OSendPacket()...\n");
#endif /* DEBUG */
    
    pPab = PCIAdapterBlock[AdapterID];

    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
    
    /* get Inbound free Q entry - reading from In Q gets free Q entry */
    /* offset to Msg Frame in PCI msg block */

    msgOffset = pPab->p_atu->InQueue; 

    if (msgOffset == 0xFFFFFFFF)
    {
#ifdef DEBUG
        kprintf("RCI2OSendPacket(): Inbound Free Q empty!\n");
#endif /* DEBUG */
        return RC_RTN_FREE_Q_EMPTY;
    }
        
    /* calc virual address of msg - virual already mapped to physical */    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);

    size = FillI2OMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);

    if (size == -1) /* error processing TCB - send NOP msg */
    {
#ifdef DEBUG
        kprintf("RCI2OSendPacket(): Error Rrocess TCB!\n");
#endif /* DEBUG */
        pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
        pMsg[1] = I2O_UTIL_NOP << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
        return RC_RTN_TCB_ERROR;
    }
    else /* send over msg header */
    {    
        pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
        pMsg[1] = I2O_LAN_PACKET_SEND << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
        pMsg[2] = InitiatorContext;
        pMsg[3] = 0;  /* batch reply */
        /* post to Inbound Post Q */   
        pPab->p_atu->InQueue = msgOffset;
        return RC_RTN_NO_ERROR;
    }
}
 

/*
** =========================================================================
** RCI2OPostRecvBuffer()
**
** inputs:  pBufrCntrlBlock - pointer to buffer control block
**
** returns TRUE if successful in sending message, else FALSE.
** =========================================================================
*/
RC_RETURN
RCPostRecvBuffers(U16 AdapterID, PRCTCB pTransCtrlBlock)
{
    U32 msgOffset;
    PU32 pMsg;
    int size;
    PPAB pPab;

#ifdef DEBUG
    kprintf("RCPostRecvBuffers()...\n");
#endif /* DEBUG */
    
    /* search for DeviceHandle */
    pPab = PCIAdapterBlock[AdapterID];

    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
    

    /* get Inbound free Q entry - reading from In Q gets free Q entry */
    /* offset to Msg Frame in PCI msg block */
    msgOffset = pPab->p_atu->InQueue; 

    if (msgOffset == 0xFFFFFFFF)
    {
#ifdef DEBUG
        kprintf("RCPostRecvBuffers(): Inbound Free Q empty!\n");
#endif /* DEBUG */
        return RC_RTN_FREE_Q_EMPTY;
   
    }
    /* calc virual address of msg - virual already mapped to physical */    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);

    size = FillI2OMsgSGLFromTCB(pMsg + 4, pTransCtrlBlock);

    if (size == -1) /* error prcessing TCB - send 3 DWORD private msg == NOP */
    {
#ifdef DEBUG
        kprintf("RCPostRecvBuffers(): Error Processing TCB! size = %d\n", size);
#endif /* DEBUG */
        pMsg[0] = THREE_WORD_MSG_SIZE | SGL_OFFSET_0;
        pMsg[1] = I2O_UTIL_NOP << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
        /* post to Post Q */   
        pPab->p_atu->InQueue = msgOffset;
        return RC_RTN_TCB_ERROR;
    }
    else /* send over size msg header */
    {    
        pMsg[0] = (size + 4) << 16 | LAN_MSG_REQST; /* send over message size and flags */
        pMsg[1] = I2O_LAN_RECEIVE_POST << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
        pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
        pMsg[3] = *(PU32)pTransCtrlBlock; /* number of packet buffers */
        /* post to Post Q */   
        pPab->p_atu->InQueue = msgOffset;
        return RC_RTN_NO_ERROR;
    }
}


/*
** =========================================================================
** RCProcI2OMsgQ()
**
** Process I2O outbound message queue until empty.
** =========================================================================
*/
void 
RCProcI2OMsgQ(U16 AdapterID)
{
    U32 phyAddrMsg;
    PU8 p8Msg;
    PU32 p32;
    U16 count;
    PPAB pPab;
    unsigned char debug_msg[20];
    
    pPab = PCIAdapterBlock[AdapterID];
    
    if (pPab == NULL)
        return;
    
    phyAddrMsg = pPab->p_atu->OutQueue;

    while (phyAddrMsg != 0xFFFFFFFF)
    {
        p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
        p32 = (PU32)p8Msg;
        
        //printk(" msg: 0x%x  0x%x \n", p8Msg[7], p32[5]);

        /* 
        ** Send Packet Reply Msg
        */
        if (I2O_LAN_PACKET_SEND == p8Msg[7])  /* function code byte */
        {
            count = *(PU16)(p8Msg+2);
            count -= p8Msg[0] >> 4;
            /* status, count, context[], adapter */
            (*pPab->pTransCallbackFunc)(p8Msg[19], count, p32+5, AdapterID);
        }             
        /* 
        ** Receive Packet Reply Msg */
        else if (I2O_LAN_RECEIVE_POST == p8Msg[7])
        {
#ifdef DEBUG    
            kprintf("I2O_RECV_REPLY pPab:0x%08.8ulx p8Msg:0x%08.8ulx p32:0x%08.8ulx\n", pPab, p8Msg, p32);
            kprintf("msg: 0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
                    p32[0], p32[1], p32[2], p32[3]);
            kprintf("     0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
                    p32[4], p32[5], p32[6], p32[7]);
            kprintf("     0x%08.8ulx:0X%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
                    p32[8], p32[9], p32[10], p32[11]);
#endif
            /*  status, count, buckets remaining, packetParmBlock, adapter */
            (*pPab->pRecvCallbackFunc)(p8Msg[19], p8Msg[12], p32[5], p32+6, AdapterID);
      
      
        }
        else if (I2O_LAN_RESET == p8Msg[7] || I2O_LAN_SHUTDOWN == p8Msg[7])
        {
            if (pPab->pCallbackFunc)
            {
                (*pPab->pCallbackFunc)(p8Msg[19],0,0,AdapterID);
            }
            else
            {
                pPab->pCallbackFunc = (PFNCALLBACK) 1;
            }
            //PCIAdapterBlock[AdapterID] = 0;
        }
        else if (I2O_PRIVATE == p8Msg[7])
        {
            //printk("i2o private 0x%x, 0x%x \n", p8Msg[7], p32[5]);
            switch (p32[5])
            {
            case RC_PRIVATE_DEBUG_MSG:
                msgFlag = 1;
                /*printk("Received I2O_PRIVATE msg\n");*/
                debug_msg[15]  = (p32[6]&0xff000000) >> 24;
                debug_msg[14]  = (p32[6]&0x00ff0000) >> 16;
                debug_msg[13]  = (p32[6]&0x0000ff00) >> 8;
                debug_msg[12]  = (p32[6]&0x000000ff);

                debug_msg[11]  = (p32[7]&0xff000000) >> 24;
                debug_msg[10]  = (p32[7]&0x00ff0000) >> 16;
                debug_msg[ 9]  = (p32[7]&0x0000ff00) >> 8;
                debug_msg[ 8]  = (p32[7]&0x000000ff);

                debug_msg[ 7]  = (p32[8]&0xff000000) >> 24;
                debug_msg[ 6]  = (p32[8]&0x00ff0000) >> 16;
                debug_msg[ 5] = (p32[8]&0x0000ff00) >> 8;
                debug_msg[ 4] = (p32[8]&0x000000ff);

                debug_msg[ 3] = (p32[9]&0xff000000) >> 24;
                debug_msg[ 2] = (p32[9]&0x00ff0000) >> 16;
                debug_msg[ 1] = (p32[9]&0x0000ff00) >> 8;
                debug_msg[ 0] = (p32[9]&0x000000ff);

                debug_msg[16] = '\0';
                printk (debug_msg);
                break;
            case RC_PRIVATE_REBOOT:
                printk("Adapter reboot initiated...\n");
                if (pPab->pRebootCallbackFunc)
                {
                    (*pPab->pRebootCallbackFunc)(0,0,0,AdapterID);
                }
                break;
            default:
                printk("Unknown private I2O msg received: 0x%lx\n",
                       p32[5]);
                break;
            }
        }

        /* 
        ** Process other Msg's
        */
        else
        {
            ProcessOutboundI2OMsg(pPab, phyAddrMsg);
        }
        
        /* return MFA to outbound free Q*/
        pPab->p_atu->OutQueue = phyAddrMsg;
    
        /* any more msgs? */
        phyAddrMsg = pPab->p_atu->OutQueue;
    }
}


/*
** =========================================================================
**  Returns LAN interface statistical counters to space provided by caller at
**  StatsReturnAddr.  Returns 0 if success, else RC_RETURN code.
**  This function will call the WaitCallback function provided by
**  user while waiting for card to respond.
** =========================================================================
*/
RC_RETURN
RCGetLinkStatistics(U16 AdapterID, 
                    P_RCLINKSTATS StatsReturnAddr,
                    PFNWAITCALLBACK WaitCallback)
{
    U32 msgOffset;
    volatile U32 timeout;
    volatile PU32 pMsg;
    volatile PU32 p32, pReturnAddr;
    P_NICSTAT pStats;
    int i;
    PPAB pPab;

/*kprintf("Get82558Stats() StatsReturnAddr:0x%08.8ulx\n", StatsReturnAddr);*/

    pPab = PCIAdapterBlock[AdapterID];

    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
    
    msgOffset = pPab->p_atu->InQueue;

    if (msgOffset == 0xFFFFFFFF)
    {
#ifdef DEBUG
        kprintf("Get8255XStats(): Inbound Free Q empty!\n");
#endif
        return RC_RTN_FREE_Q_EMPTY;
    }

    /* calc virual address of msg - virual already mapped to physical */
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);

/*dprintf("Get82558Stats - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
/*dprintf("Get82558Stats - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/

    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
    pMsg[3] = 0x112; /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_NIC_STATS;
    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 

    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));

    pStats = (P_NICSTAT)p32;
    pStats->dump_status = 0xFFFFFFFF;

    /* post to Inbound Post Q */
    pPab->p_atu->InQueue = msgOffset;

    timeout = 100000;
    while (1)
    {
        if (WaitCallback)
            (*WaitCallback)();
        
        for (i = 0; i < 1000; i++)
            ;

        if (pStats->dump_status != 0xFFFFFFFF)
            break;

        if (!timeout--)
        {
#ifdef DEBUG
            kprintf("RCGet82558Stats() Timeout waiting for NIC statistics\n");
#endif
            return RC_RTN_MSG_REPLY_TIMEOUT;
        }
    }
    
    pReturnAddr = (PU32)StatsReturnAddr;
    
    /* copy Nic stats to user's structure */
    for (i = 0; i < (int) sizeof(RCLINKSTATS) / 4; i++)
        pReturnAddr[i] = p32[i];
   
    return RC_RTN_NO_ERROR;     
}


/*
** =========================================================================
** Get82558LinkStatus()
** =========================================================================
*/
RC_RETURN
RCGetLinkStatus(U16 AdapterID, PU32 ReturnAddr, PFNWAITCALLBACK WaitCallback)
{
    U32 msgOffset;
    volatile U32 timeout;
    volatile PU32 pMsg;
    volatile PU32 p32;
    PPAB pPab;

/*kprintf("Get82558LinkStatus() ReturnPhysAddr:0x%08.8ulx\n", ReturnAddr);*/

    pPab = PCIAdapterBlock[AdapterID];

    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
    
    msgOffset = pPab->p_atu->InQueue;

    if (msgOffset == 0xFFFFFFFF)
    {
#ifdef DEBUG
        dprintf("Get82558LinkStatus(): Inbound Free Q empty!\n");
#endif
        return RC_RTN_FREE_Q_EMPTY;
    }

    /* calc virual address of msg - virual already mapped to physical */
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);
/*dprintf("Get82558LinkStatus - pMsg = 0x%08ulx, InQ msgOffset = 0x%08ulx\n", pMsg, msgOffset);*/
/*dprintf("Get82558LinkStatus - pMsg = 0x%08X, InQ msgOffset = 0x%08X\n", pMsg, msgOffset);*/

    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
    pMsg[3] = 0x112; /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_STATUS;
    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 

    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
    *p32 = 0xFFFFFFFF;
    
    /* post to Inbound Post Q */
    pPab->p_atu->InQueue = msgOffset;

    timeout = 100000;
    while (1)
    {
        U32 i;

        if (WaitCallback)
            (*WaitCallback)();
        
        for (i = 0; i < 1000; i++)
            ;

        if (*p32 != 0xFFFFFFFF)
            break;

        if (!timeout--)
        {
#ifdef DEBUG
            kprintf("Timeout waiting for link status\n");
#endif    
            return RC_RTN_MSG_REPLY_TIMEOUT;
        }
    }
    
    *ReturnAddr = *p32; /* 1 = up 0 = down */
    
    return RC_RTN_NO_ERROR;
    
}

/*
** =========================================================================
** RCGetMAC()
**
** get the MAC address the adapter is listening for in non-promiscous mode.
** MAC address is in media format.
** =========================================================================
*/
RC_RETURN
RCGetMAC(U16 AdapterID, PU8 mac, PFNWAITCALLBACK WaitCallback)
{
    unsigned i, timeout;
    U32      off;
    PU32     p;
    U32      temp[2];
    PPAB     pPab;
    PATU     p_atu;
    
    pPab = PCIAdapterBlock[AdapterID];
    
    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
    
    p_atu = pPab->p_atu;

    p_atu->EtherMacLow = 0;     /* first zero return data */
    p_atu->EtherMacHi = 0;
    
    off = p_atu->InQueue;   /* get addresss of message */
 
    if (0xFFFFFFFF == off)
        return RC_RTN_FREE_Q_EMPTY;

    p = (PU32)(pPab->pPci45LinBaseAddr + off);

#ifdef RCDEBUG
    printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
           (uint)p_atu, (uint)off, (uint)p);
#endif /* RCDEBUG */
    /* setup private message */
    p[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
    p[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    p[2] = 0;               /* initiator context */
    p[3] = 0x218;           /* transaction context */
    p[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_MAC_ADDR;


    p_atu->InQueue = off;   /* send it to the I2O device */
#ifdef RCDEBUG
    printk("RCGetMAC: p_atu 0x%08x, off 0x%08x, p 0x%08x\n", 
           (uint)p_atu, (uint)off, (uint)p);
#endif /* RCDEBUG */

    /* wait for the rcpci45 board to update the info */
    timeout = 1000000;
    while (0 == p_atu->EtherMacLow) 
    {
        if (WaitCallback)
            (*WaitCallback)();
    
        for (i = 0; i < 1000; i++)
            ;

        if (!timeout--)
        {
            printk("rc_getmac: Timeout\n");
            return RC_RTN_MSG_REPLY_TIMEOUT;
        }
    }
    
    /* read the mac address  */
    temp[0] = p_atu->EtherMacLow;
    temp[1] = p_atu->EtherMacHi;
    memcpy((char *)mac, (char *)temp, 6);


#ifdef RCDEBUG
//    printk("rc_getmac: 0x%X\n", ptr);
#endif /* RCDEBUG */

    return RC_RTN_NO_ERROR;
}

 
/*
** =========================================================================
** RCSetMAC()
**
** set MAC address the adapter is listening for in non-promiscous mode.
** MAC address is in media format.
** =========================================================================
*/
RC_RETURN
RCSetMAC(U16 AdapterID, PU8 mac)
{
    U32  off;
    PU32 pMsg;
    PPAB pPab;


    pPab = PCIAdapterBlock[AdapterID];
    
    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
    
    off = pPab->p_atu->InQueue; /* get addresss of message */
    
    if (0xFFFFFFFF == off)
        return RC_RTN_FREE_Q_EMPTY;
    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);

    /* setup private message */
    pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = 0;                 /* initiator context */
    pMsg[3] = 0x219;             /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_MAC_ADDR;
    pMsg[5] = *(unsigned *)mac;  /* first four bytes */
    pMsg[6] = *(unsigned *)(mac + 4); /* last two bytes */
    
    pPab->p_atu->InQueue = off;   /* send it to the I2O device */

    return RC_RTN_NO_ERROR ;
}


/*
** =========================================================================
** RCSetLinkSpeed()
**
** set ethernet link speed. 
** input: speedControl - determines action to take as follows
**          0 = reset and auto-negotiate (NWay)
**          1 = Full Duplex 100BaseT
**          2 = Half duplex 100BaseT
**          3 = Full Duplex  10BaseT
**          4 = Half duplex  10BaseT
**          all other values are ignore (do nothing)
** =========================================================================
*/
RC_RETURN
RCSetLinkSpeed(U16 AdapterID, U16 LinkSpeedCode)
{
    U32  off;
    PU32 pMsg;
    PPAB pPab;

    
    pPab =PCIAdapterBlock[AdapterID];
     
    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
    
    off = pPab->p_atu->InQueue; /* get addresss of message */
    
    if (0xFFFFFFFF == off)
        return RC_RTN_FREE_Q_EMPTY;
    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);

    /* setup private message */
    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = 0;                 /* initiator context */
    pMsg[3] = 0x219;             /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_LINK_SPEED;
    pMsg[5] = LinkSpeedCode;     /* link speed code */

    pPab->p_atu->InQueue = off;   /* send it to the I2O device */

    return RC_RTN_NO_ERROR ;
}
/*
** =========================================================================
** RCSetPromiscuousMode()
**
** Defined values for Mode:
**  0 - turn off promiscuous mode
**  1 - turn on  promiscuous mode
**
** =========================================================================
*/
RC_RETURN
RCSetPromiscuousMode(U16 AdapterID, U16 Mode)
{
    U32  off;
    PU32 pMsg;
    PPAB pPab;
    
    pPab =PCIAdapterBlock[AdapterID];
     
    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
    
    off = pPab->p_atu->InQueue; /* get addresss of message */
    
    if (0xFFFFFFFF == off)
        return RC_RTN_FREE_Q_EMPTY;
    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);

    /* setup private message */
    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = 0;                 /* initiator context */
    pMsg[3] = 0x219;             /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_PROMISCUOUS_MODE;
    pMsg[5] = Mode;     /* promiscuous mode setting */

    pPab->p_atu->InQueue = off;   /* send it to the device */

    return RC_RTN_NO_ERROR ;
}
/*
** =========================================================================
** RCGetPromiscuousMode()
**
** get promiscuous mode setting
**
** Possible return values placed in pMode:
**  0 = promisuous mode not set
**  1 = promisuous mode is set
**
** =========================================================================
*/
RC_RETURN
RCGetPromiscuousMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
{
    U32 msgOffset, timeout;
    PU32 pMsg;
    volatile PU32 p32;
    PPAB pPab;

    pPab =PCIAdapterBlock[AdapterID];


    msgOffset = pPab->p_atu->InQueue;


    if (msgOffset == 0xFFFFFFFF)
    {
        kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
        return RC_RTN_FREE_Q_EMPTY;
    }

    /* calc virtual address of msg - virtual already mapped to physical */    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);

    /* virtual pointer to return buffer - clear first two dwords */
    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
    p32[0] = 0xff;

    /* setup private message */
    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = 0;                 /* initiator context */
    pMsg[3] = 0x219;             /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_PROMISCUOUS_MODE;
    /* phys address to return status - area right after PAB */
    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
   
    /* post to Inbound Post Q */   

    pPab->p_atu->InQueue = msgOffset;

    /* wait for response */
    timeout = 1000000;
    while(1)
    {
        int i;
        
        if (WaitCallback)
            (*WaitCallback)();

        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
            ;
            
        if (p32[0] != 0xff)
            break;
            
        if (!timeout--)
        {
            kprintf("Timeout waiting for promiscuous mode from adapter\n");
            kprintf("0x%8.8lx\n", p32[0]);
            return RC_RTN_NO_LINK_SPEED;
        }
    }

    /* get mode */
    *pMode = (U8)((volatile PU8)p32)[0] & 0x0f;

    return RC_RTN_NO_ERROR;
}
/*
** =========================================================================
** RCSetBroadcastMode()
**
** Defined values for Mode:
**  0 - turn off promiscuous mode
**  1 - turn on  promiscuous mode
**
** =========================================================================
*/
RC_RETURN
RCSetBroadcastMode(U16 AdapterID, U16 Mode)
{
    U32  off;
    PU32 pMsg;
    PPAB pPab;
    
    pPab =PCIAdapterBlock[AdapterID];
     
    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
    
    off = pPab->p_atu->InQueue; /* get addresss of message */
    
    if (0xFFFFFFFF == off)
        return RC_RTN_FREE_Q_EMPTY;
    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);

    /* setup private message */
    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = 0;                 /* initiator context */
    pMsg[3] = 0x219;             /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_BROADCAST_MODE;
    pMsg[5] = Mode;     /* promiscuous mode setting */

    pPab->p_atu->InQueue = off;   /* send it to the device */

    return RC_RTN_NO_ERROR ;
}
/*
** =========================================================================
** RCGetBroadcastMode()
**
** get promiscuous mode setting
**
** Possible return values placed in pMode:
**  0 = promisuous mode not set
**  1 = promisuous mode is set
**
** =========================================================================
*/
RC_RETURN
RCGetBroadcastMode(U16 AdapterID, PU32 pMode, PFNWAITCALLBACK WaitCallback)
{
    U32 msgOffset, timeout;
    PU32 pMsg;
    volatile PU32 p32;
    PPAB pPab;

    pPab =PCIAdapterBlock[AdapterID];


    msgOffset = pPab->p_atu->InQueue;


    if (msgOffset == 0xFFFFFFFF)
    {
        kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
        return RC_RTN_FREE_Q_EMPTY;
    }

    /* calc virtual address of msg - virtual already mapped to physical */    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);

    /* virtual pointer to return buffer - clear first two dwords */
    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
    p32[0] = 0xff;

    /* setup private message */
    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = 0;                 /* initiator context */
    pMsg[3] = 0x219;             /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_BROADCAST_MODE;
    /* phys address to return status - area right after PAB */
    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
   
    /* post to Inbound Post Q */   

    pPab->p_atu->InQueue = msgOffset;

    /* wait for response */
    timeout = 1000000;
    while(1)
    {
        int i;
        
        if (WaitCallback)
            (*WaitCallback)();

        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
            ;
            
        if (p32[0] != 0xff)
            break;
            
        if (!timeout--)
        {
            kprintf("Timeout waiting for promiscuous mode from adapter\n");
            kprintf("0x%8.8lx\n", p32[0]);
            return RC_RTN_NO_LINK_SPEED;
        }
    }

    /* get mode */
    *pMode = (U8)((volatile PU8)p32)[0] & 0x0f;

    return RC_RTN_NO_ERROR;
}

/*
** =========================================================================
** RCGetLinkSpeed()
**
** get ethernet link speed. 
**
** 0 = Unknown
** 1 = Full Duplex 100BaseT
** 2 = Half duplex 100BaseT
** 3 = Full Duplex  10BaseT
** 4 = Half duplex  10BaseT
**
** =========================================================================
*/
RC_RETURN
RCGetLinkSpeed(U16 AdapterID, PU32 pLinkSpeedCode, PFNWAITCALLBACK WaitCallback)
{
    U32 msgOffset, timeout;
    PU32 pMsg;
    volatile PU32 p32;
    U8 IOPLinkSpeed;
    PPAB pPab;

    pPab =PCIAdapterBlock[AdapterID];


    msgOffset = pPab->p_atu->InQueue;


    if (msgOffset == 0xFFFFFFFF)
    {
        kprintf("RCGetLinkSpeed(): Inbound Free Q empty!\n");
        return RC_RTN_FREE_Q_EMPTY;
    }

    /* calc virtual address of msg - virtual already mapped to physical */    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);

    /* virtual pointer to return buffer - clear first two dwords */
    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
    p32[0] = 0xff;

    /* setup private message */
    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = 0;                 /* initiator context */
    pMsg[3] = 0x219;             /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_LINK_SPEED;
    /* phys address to return status - area right after PAB */
    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
   
    /* post to Inbound Post Q */   

    pPab->p_atu->InQueue = msgOffset;

    /* wait for response */
    timeout = 1000000;
    while(1)
    {
        int i;
        
        if (WaitCallback)
            (*WaitCallback)();

        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
            ;
            
        if (p32[0] != 0xff)
            break;
            
        if (!timeout--)
        {
            kprintf("Timeout waiting for link speed from IOP\n");
            kprintf("0x%8.8lx\n", p32[0]);
            return RC_RTN_NO_LINK_SPEED;
        }
    }

    /* get Link speed */
    IOPLinkSpeed = (U8)((volatile PU8)p32)[0] & 0x0f;

    *pLinkSpeedCode= IOPLinkSpeed;

    return RC_RTN_NO_ERROR;
}

/*
** =========================================================================
** RCReportDriverCapability(U16 AdapterID, U32 capability)
**
** Currently defined bits:
** WARM_REBOOT_CAPABLE   0x01
**
** =========================================================================
*/
RC_RETURN
RCReportDriverCapability(U16 AdapterID, U32 capability)
{
    U32  off;
    PU32 pMsg;
    PPAB pPab;

    pPab =PCIAdapterBlock[AdapterID];
     
    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
    
    off = pPab->p_atu->InQueue; /* get addresss of message */
    
    if (0xFFFFFFFF == off)
        return RC_RTN_FREE_Q_EMPTY;
    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);

    /* setup private message */
    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = 0;                 /* initiator context */
    pMsg[3] = 0x219;             /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_REPORT_DRIVER_CAPABILITY;
    pMsg[5] = capability;

    pPab->p_atu->InQueue = off;   /* send it to the I2O device */

    return RC_RTN_NO_ERROR ;
}

/*
** =========================================================================
** RCGetFirmwareVer()
**
** Return firmware version in the form "SoftwareVersion : Bt BootVersion"
**
** =========================================================================
*/
RC_RETURN
RCGetFirmwareVer(U16 AdapterID, PU8 pFirmString, PFNWAITCALLBACK WaitCallback)
{
    U32 msgOffset, timeout;
    PU32 pMsg;
    volatile PU32 p32;
    PPAB pPab;

    pPab =PCIAdapterBlock[AdapterID];

    msgOffset = pPab->p_atu->InQueue;


    if (msgOffset == 0xFFFFFFFF)
    {
        kprintf("RCGetFirmwareVer(): Inbound Free Q empty!\n");
        return RC_RTN_FREE_Q_EMPTY;
    }

    /* calc virtual address of msg - virtual already mapped to physical */    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);

    /* virtual pointer to return buffer - clear first two dwords */
    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
    p32[0] = 0xff;

    /* setup private message */
    pMsg[0] = SIX_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = 0;                 /* initiator context */
    pMsg[3] = 0x219;             /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_FIRMWARE_REV;
    /* phys address to return status - area right after PAB */
    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 


   
    /* post to Inbound Post Q */   

    pPab->p_atu->InQueue = msgOffset;

    
    /* wait for response */
    timeout = 1000000;
    while(1)
    {
        int i;
        
        if (WaitCallback)
            (*WaitCallback)();
            
        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
            ;

        if (p32[0] != 0xff)
            break;
            
        if (!timeout--)
        {
            kprintf("Timeout waiting for link speed from IOP\n");
            return RC_RTN_NO_FIRM_VER;
        }
    }

    strcpy(pFirmString, (PU8)p32);
    return RC_RTN_NO_ERROR;
}

/*
** =========================================================================
** RCResetLANCard()
**
** ResourceFlags indicates whether to return buffer resource explicitly
** to host or keep and reuse.
** CallbackFunction (if not NULL) is the function to be called when 
** reset is complete.
** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
** reset is done (if not NULL).
**
** =========================================================================
*/
RC_RETURN 
RCResetLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
{
    unsigned long off;
    unsigned long *pMsg;
    PPAB pPab;
    int i;
    long timeout = 0;

    
    pPab =PCIAdapterBlock[AdapterID];
     
    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;

    off = pPab->p_atu->InQueue; /* get addresss of message */
    
    if (0xFFFFFFFF == off)
        return RC_RTN_FREE_Q_EMPTY;
    
    pPab->pCallbackFunc = CallbackFunction;

    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);

    /* setup message */
    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_LAN_RESET << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
    pMsg[3] = ResourceFlags << 16;   /* resource flags */

    pPab->p_atu->InQueue = off;   /* send it to the I2O device */

    if (CallbackFunction == (PFNCALLBACK)NULL)
    {
        /* call RCProcI2OMsgQ() until something in pPab->pCallbackFunc
           or until timer goes off */
        while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
        {
            RCProcI2OMsgQ(AdapterID);
            for (i = 0; i < 100000; i++)     /* please don't hog the bus!!! */
                ;
            timeout++;
            if (timeout > 10000)
            {
                break;
            }
        }
        if (ReturnAddr != (PU32)NULL)
            *ReturnAddr = (U32)pPab->pCallbackFunc;
    }

    return RC_RTN_NO_ERROR ;
}
/*
** =========================================================================
** RCResetIOP()
**
** Send StatusGet Msg, wait for results return directly to buffer.
**
** =========================================================================
*/
RC_RETURN 
RCResetIOP(U16 AdapterID)
{
    U32 msgOffset, timeout;
    PU32 pMsg;
    PPAB pPab;
    volatile PU32 p32;
    
    pPab = PCIAdapterBlock[AdapterID];
    msgOffset = pPab->p_atu->InQueue;

    if (msgOffset == 0xFFFFFFFF)
    {
        return RC_RTN_FREE_Q_EMPTY;
    }

    /* calc virtual address of msg - virtual already mapped to physical */    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);

    pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_EXEC_IOP_RESET << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
    pMsg[2] = 0; /* universal context */
    pMsg[3] = 0; /* universal context */
    pMsg[4] = 0; /* universal context */
    pMsg[5] = 0; /* universal context */
    /* phys address to return status - area right after PAB */
    pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
    pMsg[7] = 0;
    pMsg[8] = 1;  /*  return 1 byte */

    /* virual pointer to return buffer - clear first two dwords */
    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
    p32[0] = 0;
    p32[1] = 0;

    /* post to Inbound Post Q */   

    pPab->p_atu->InQueue = msgOffset;

    /* wait for response */
    timeout = 1000000;
    while(1)
    {
        int i;
        
        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
            ;
            
        if (p32[0] || p32[1])
            break;
            
        if (!timeout--)
        {
            printk("RCResetIOP timeout\n");
            return RC_RTN_MSG_REPLY_TIMEOUT;
        }
    }
    return RC_RTN_NO_ERROR;
}

/*
** =========================================================================
** RCShutdownLANCard()
**
** ResourceFlags indicates whether to return buffer resource explicitly
** to host or keep and reuse.
** CallbackFunction (if not NULL) is the function to be called when 
** shutdown is complete.
** If CallbackFunction is NULL, ReturnAddr will have a 1 placed in it when
** shutdown is done (if not NULL).
**
** =========================================================================
*/
RC_RETURN 
RCShutdownLANCard(U16 AdapterID, U16 ResourceFlags, PU32 ReturnAddr, PFNCALLBACK CallbackFunction)
{
    volatile PU32 pMsg;
    U32 off;
    PPAB pPab;
    int i;
    long timeout = 0;

    pPab = PCIAdapterBlock[AdapterID];

    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;

    off = pPab->p_atu->InQueue; /* get addresss of message */
    
    if (0xFFFFFFFF == off)
        return RC_RTN_FREE_Q_EMPTY;

    pPab->pCallbackFunc = CallbackFunction;
    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);

    /* setup message */
    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_LAN_SHUTDOWN << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
    pMsg[3] = ResourceFlags << 16;   /* resource flags */

    pPab->p_atu->InQueue = off;   /* send it to the I2O device */

    if (CallbackFunction == (PFNCALLBACK)NULL)
    {
        /* call RCProcI2OMsgQ() until something in pPab->pCallbackFunc
           or until timer goes off */
        while (pPab->pCallbackFunc == (PFNCALLBACK)NULL)
        {
            RCProcI2OMsgQ(AdapterID);
            for (i = 0; i < 100000; i++)     /* please don't hog the bus!!! */
                ;
            timeout++;
            if (timeout > 10000)
            {
				printk("RCShutdownLANCard(): timeout\n");
                break;
            }
        }
        if (ReturnAddr != (PU32)NULL)
            *ReturnAddr = (U32)pPab->pCallbackFunc;
    }
    return RC_RTN_NO_ERROR ;
}


/*
** =========================================================================
** RCSetRavlinIPandMask()
**
** Set the Ravlin 45/PCI cards IP address and network mask.
**
** IP address and mask must be in network byte order.
** For example, IP address 1.2.3.4 and mask 255.255.255.0 would be
** 0x04030201 and 0x00FFFFFF on a little endian machine.
**
** =========================================================================
*/
RC_RETURN
RCSetRavlinIPandMask(U16 AdapterID, U32 ipAddr, U32 netMask)
{
    volatile PU32 pMsg;
    U32 off;
    PPAB pPab;

    pPab = PCIAdapterBlock[AdapterID];

    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;

    off = pPab->p_atu->InQueue; /* get addresss of message */
    
    if (0xFFFFFFFF == off)
        return RC_RTN_FREE_Q_EMPTY;
    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);

    /* setup private message */
    pMsg[0] = SEVEN_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = 0;                 /* initiator context */
    pMsg[3] = 0x219;             /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_SET_IP_AND_MASK;
    pMsg[5] = ipAddr; 
    pMsg[6] = netMask;


    pPab->p_atu->InQueue = off;   /* send it to the I2O device */
    return RC_RTN_NO_ERROR ;

}

/*
** =========================================================================
** RCGetRavlinIPandMask()
**
** get the IP address and MASK from the card
** 
** =========================================================================
*/
RC_RETURN
RCGetRavlinIPandMask(U16 AdapterID, PU32 pIpAddr, PU32 pNetMask, 
                     PFNWAITCALLBACK WaitCallback)
{
    unsigned i, timeout;
    U32      off;
    PU32     pMsg, p32;
    PPAB     pPab;
    PATU     p_atu;
    
#ifdef DEBUG
    kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
#endif /* DEBUG */

    pPab = PCIAdapterBlock[AdapterID];
    
    if (pPab == NULL)
        return RC_RTN_ADPTR_NOT_REGISTERED;
    
    p_atu = pPab->p_atu;
    off = p_atu->InQueue;   /* get addresss of message */
 
    if (0xFFFFFFFF == off)
        return RC_RTN_FREE_Q_EMPTY;

    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
    *p32 = 0xFFFFFFFF;

    pMsg = (PU32)(pPab->pPci45LinBaseAddr + off);

#ifdef DEBUG
    kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
#endif /* DEBUG */
    /* setup private message */
    pMsg[0] = FIVE_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_PRIVATE << 24 | I2O_HOST_TID << 12 | RC_LAN_TARGET_ID;
    pMsg[2] = 0;               /* initiator context */
    pMsg[3] = 0x218;           /* transaction context */
    pMsg[4] = RC_PCI45_VENDOR_ID << 16 | RC_PRIVATE_GET_IP_AND_MASK;
    pMsg[5] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 

    p_atu->InQueue = off;   /* send it to the I2O device */
#ifdef DEBUG
    kprintf("RCGetRavlinIPandMask: p_atu 0x%08.8ulx, off 0x%08.8ulx, p32 0x%08.8ulx\n", p_atu, off, p32);
#endif /* DEBUG */

    /* wait for the rcpci45 board to update the info */
    timeout = 100000;
    while (0xffffffff == *p32)
    {
        if (WaitCallback)
            (*WaitCallback)();
    
        for (i = 0; i < 1000; i++)
            ;

        if (!timeout--)
        {
#ifdef DEBUG
            kprintf("RCGetRavlinIPandMask: Timeout\n");
#endif /* DEBUG */
            return RC_RTN_MSG_REPLY_TIMEOUT;
        }
    }

#ifdef DEBUG
    kprintf("RCGetRavlinIPandMask: after time out\n", \
            "p32[0] (IpAddr) 0x%08.8ulx, p32[1] (IPmask) 0x%08.8ulx\n", p32[0], p32[1]);
#endif /* DEBUG */
    
    /* send IP and mask to user's space  */
    *pIpAddr  = p32[0];
    *pNetMask = p32[1];


#ifdef DEBUG
    kprintf("RCGetRavlinIPandMask: pIpAddr is 0x%08.8ulx, *IpAddr is 0x%08.8ulx\n", pIpAddr, *pIpAddr);
#endif /* DEBUG */

    return RC_RTN_NO_ERROR;
}

/* 
** /////////////////////////////////////////////////////////////////////////
** /////////////////////////////////////////////////////////////////////////
**
**                        local functions
**
** /////////////////////////////////////////////////////////////////////////
** /////////////////////////////////////////////////////////////////////////
*/

/*
** =========================================================================
** SendI2OOutboundQInitMsg()
**
** =========================================================================
*/
static int 
SendI2OOutboundQInitMsg(PPAB pPab)
{
    U32 msgOffset, timeout, phyOutQFrames, i;
    volatile PU32 pMsg;
    volatile PU32 p32;
    
    
    
    msgOffset = pPab->p_atu->InQueue;

    
    if (msgOffset == 0xFFFFFFFF)
    {
#ifdef DEBUG
        kprintf("SendI2OOutboundQInitMsg(): Inbound Free Q empty!\n");
#endif /* DEBUG */
        return RC_RTN_FREE_Q_EMPTY;
    }
    
    
    /* calc virual address of msg - virual already mapped to physical */    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);

#ifdef DEBUG
    kprintf("SendI2OOutboundQInitMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
#endif /* DEBUG */

    pMsg[0] = EIGHT_WORD_MSG_SIZE | TRL_OFFSET_6;
    pMsg[1] = I2O_EXEC_OUTBOUND_INIT << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
    pMsg[3] = 0x106; /* transaction context */
    pMsg[4] = 4096; /* Host page frame size */
    pMsg[5] = MSG_FRAME_SIZE  << 16 | 0x80; /* outbound msg frame size and Initcode */
    pMsg[6] = 0xD0000004;       /* simple sgl element LE, EOB */
    /* phys address to return status - area right after PAB */
    pMsg[7] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 

    /* virual pointer to return buffer - clear first two dwords */
    p32 = (PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
    p32[0] = 0;
    
    /* post to Inbound Post Q */   
    pPab->p_atu->InQueue = msgOffset;
    
    /* wait for response */
    timeout = 100000;
    while(1)
    {
        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
            ;
            
        if (p32[0])
            break;
            
        if (!timeout--)
        {
#ifdef DEBUG
            kprintf("Timeout wait for InitOutQ InPrgress status from IOP\n");
#endif /* DEBUG */
            return RC_RTN_NO_I2O_STATUS;
        }
    }

    timeout = 100000;
    while(1)
    {
        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
            ;
            
        if (p32[0] == I2O_EXEC_OUTBOUND_INIT_COMPLETE)
            break;

        if (!timeout--)
        {
#ifdef DEBUG
            kprintf("Timeout wait for InitOutQ Complete status from IOP\n");
#endif /* DEBUG */
            return RC_RTN_NO_I2O_STATUS;
        }
    }

    /* load PCI outbound free Q with MF physical addresses */
    phyOutQFrames = pPab->outMsgBlockPhyAddr;

    for (i = 0; i < NMBR_MSG_FRAMES; i++)
    {
        pPab->p_atu->OutQueue = phyOutQFrames;
        phyOutQFrames += MSG_FRAME_SIZE;
    }
    return RC_RTN_NO_ERROR;
}


/*
** =========================================================================
** GetI2OStatus()
**
** Send StatusGet Msg, wait for results return directly to buffer.
**
** =========================================================================
*/
static int 
GetI2OStatus(PPAB pPab)
{
    U32 msgOffset, timeout;
    PU32 pMsg;
    volatile PU32 p32;
    
    
    msgOffset = pPab->p_atu->InQueue; 
#ifdef DEBUG
    printk("GetI2OStatus: msg offset = 0x%x\n", msgOffset);
#endif /* DEBUG */
    if (msgOffset == 0xFFFFFFFF)
    {
#ifdef DEBUG
        kprintf("GetI2OStatus(): Inbound Free Q empty!\n");
#endif /* DEBUG */
        return RC_RTN_FREE_Q_EMPTY;
    }

    /* calc virual address of msg - virual already mapped to physical */    
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);

    pMsg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_EXEC_STATUS_GET << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
    pMsg[2] = 0; /* universal context */
    pMsg[3] = 0; /* universal context */
    pMsg[4] = 0; /* universal context */
    pMsg[5] = 0; /* universal context */
    /* phys address to return status - area right after PAB */
    pMsg[6] = pPab->outMsgBlockPhyAddr - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB); 
    pMsg[7] = 0;
    pMsg[8] = 88;  /*  return 88 bytes */

    /* virual pointer to return buffer - clear first two dwords */
    p32 = (volatile PU32)(pPab->pLinOutMsgBlock - ADAPTER_BLOCK_RESERVED_SPACE + sizeof(PAB));
    p32[0] = 0;
    p32[1] = 0;

#ifdef DEBUG
    kprintf("GetI2OStatus - pMsg:0x%08.8ulx, msgOffset:0x%08.8ulx, [1]:0x%08.8ulx, [6]:0x%08.8ulx\n",
            pMsg, msgOffset, pMsg[1], pMsg[6]);
#endif /* DEBUG */
   
    /* post to Inbound Post Q */   
    pPab->p_atu->InQueue = msgOffset;
    
#ifdef DEBUG
    kprintf("Return status to p32 = 0x%08.8ulx\n", p32);
#endif /* DEBUG */
    
    /* wait for response */
    timeout = 1000000;
    while(1)
    {
        int i;
        
        for (i = 0; i < 1000; i++)      /* please don't hog the bus!!! */
            ;
            
        if (p32[0] && p32[1])
            break;
            
        if (!timeout--)
        {
#ifdef DEBUG
            kprintf("Timeout waiting for status from IOP\n");
            kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
            kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
            kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
#endif /* DEBUG */
            return RC_RTN_NO_I2O_STATUS;
        }
    }
            
#ifdef DEBUG
    kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
    kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
    kprintf("0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[8], p32[9], p32[10], p32[11]);
#endif /* DEBUG */
    /* get IOP state */
    pPab->IOPState = ((volatile PU8)p32)[10];
    pPab->InboundMFrameSize  = ((volatile PU16)p32)[6];
    
#ifdef DEBUG
    kprintf("IOP state 0x%02.2x InFrameSize = 0x%04.4x\n", 
            pPab->IOPState, pPab->InboundMFrameSize);
#endif /* DEBUG */
    return RC_RTN_NO_ERROR;
}


/*
** =========================================================================
** SendEnableSysMsg()
**
**
** =========================================================================
*/
static int 
SendEnableSysMsg(PPAB pPab)
{
    U32 msgOffset; // timeout;
    volatile PU32 pMsg;

    msgOffset = pPab->p_atu->InQueue;

    if (msgOffset == 0xFFFFFFFF)
    {
#ifdef DEBUG
        kprintf("SendEnableSysMsg(): Inbound Free Q empty!\n");
#endif /* DEBUG */
        return RC_RTN_FREE_Q_EMPTY;
    }

    /* calc virual address of msg - virual already mapped to physical */
    pMsg = (PU32)(pPab->pPci45LinBaseAddr + msgOffset);

#ifdef DEBUG
    kprintf("SendEnableSysMsg - pMsg = 0x%08.8ulx, InQ msgOffset = 0x%08.8ulx\n", pMsg, msgOffset);
#endif /* DEBUG */

    pMsg[0] = FOUR_WORD_MSG_SIZE | SGL_OFFSET_0;
    pMsg[1] = I2O_EXEC_SYS_ENABLE << 24 | I2O_HOST_TID << 12 | I2O_IOP_TID;
    pMsg[2] = DEFAULT_RECV_INIT_CONTEXT;
    pMsg[3] = 0x110; /* transaction context */
    pMsg[4] = 0x50657465; /*  RedCreek Private */

    /* post to Inbound Post Q */
    pPab->p_atu->InQueue = msgOffset;

    return RC_RTN_NO_ERROR;
}


/*
** =========================================================================
** FillI2OMsgFromTCB()
**
** inputs   pMsgU32 - virual pointer (mapped to physical) of message frame
**          pXmitCntrlBlock - pointer to caller buffer control block.
**
** fills in LAN SGL after Transaction Control Word or Bucket Count.
** =========================================================================
*/
static int 
FillI2OMsgSGLFromTCB(PU32 pMsgFrame, PRCTCB pTransCtrlBlock)
{
    unsigned int nmbrBuffers, nmbrSeg, nmbrDwords, context, flags;
    PU32 pTCB, pMsg;

    /* SGL element flags */   
#define EOB        0x40000000
#define LE         0x80000000
#define SIMPLE_SGL 0x10000000
#define BC_PRESENT 0x01000000

    pTCB = (PU32)pTransCtrlBlock;
    pMsg = pMsgFrame;
    nmbrDwords = 0;

#ifdef DEBUG
    kprintf("FillI2OMsgSGLFromTCBX\n");
    kprintf("TCB  0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n",
            pTCB[0], pTCB[1], pTCB[2], pTCB[3], pTCB[4]);
    kprintf("pTCB 0x%08.8ulx, pMsg 0x%08.8ulx\n", pTCB, pMsg);
#endif /* DEBUG */
    
    nmbrBuffers = *pTCB++;
    
    if (!nmbrBuffers)
    {
        return -1;
    }

    do
    {
        context = *pTCB++; /* buffer tag (context) */
        nmbrSeg = *pTCB++; /* number of segments */

        if (!nmbrSeg)
        {
            return -1;
        }
        
        flags = SIMPLE_SGL | BC_PRESENT;

        if (1 == nmbrSeg)
        {
            flags |= EOB;
        
            if (1 == nmbrBuffers)
                flags |= LE;
        }    

        /* 1st SGL buffer element has context */
        pMsg[0] = pTCB[0] | flags ; /* send over count (segment size) */
        pMsg[1] = context;
        pMsg[2] = pTCB[1]; /* send buffer segment physical address */
        nmbrDwords += 3;
        pMsg += 3;
        pTCB += 2;

        
        if (--nmbrSeg)
        {
            do
            {
                flags = SIMPLE_SGL;
                
                if (1 == nmbrSeg)
                {
                    flags |= EOB;
                
                    if (1 == nmbrBuffers)
                        flags |= LE;
                }    
                
                pMsg[0] = pTCB[0] | flags;  /* send over count */
                pMsg[1] = pTCB[1];   /* send buffer segment physical address */
                nmbrDwords += 2;
                pTCB += 2;
                pMsg += 2;
        
            } while (--nmbrSeg);
        }
        
    } while (--nmbrBuffers);
    
    return nmbrDwords;
}


/*
** =========================================================================
** ProcessOutboundI2OMsg()
**
** process I2O reply message
** * change to msg structure *
** =========================================================================
*/
static void 
ProcessOutboundI2OMsg(PPAB pPab, U32 phyAddrMsg)
{
    PU8 p8Msg;
    PU32 p32;
    //  U16 count;
    
    
    p8Msg = pPab->pLinOutMsgBlock + (phyAddrMsg - pPab->outMsgBlockPhyAddr);
    p32 = (PU32)p8Msg;
    
#ifdef DEBUG
    kprintf("VXD: ProcessOutboundI2OMsg - pPab 0x%08.8ulx, phyAdr 0x%08.8ulx, linAdr 0x%08.8ulx\n", pPab, phyAddrMsg, p8Msg);
    kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[0], p32[1], p32[2], p32[3]);
    kprintf("msg :0x%08.8ulx:0x%08.8ulx:0x%08.8ulx:0x%08.8ulx\n", p32[4], p32[5], p32[6], p32[7]);
#endif /* DEBUG */

    if (p32[4] >> 24 != I2O_REPLY_STATUS_SUCCESS)
    {
#ifdef DEBUG
        kprintf("Message reply status not success\n");
#endif /* DEBUG */
        return;
    }
    
    switch (p8Msg[7] )  /* function code byte */
    {
    case I2O_EXEC_SYS_TAB_SET:
        msgFlag = 1;
#ifdef DEBUG
        kprintf("Received I2O_EXEC_SYS_TAB_SET reply\n");
#endif /* DEBUG */
        break;

    case I2O_EXEC_HRT_GET:
        msgFlag = 1;
#ifdef DEBUG
        kprintf("Received I2O_EXEC_HRT_GET reply\n");
#endif /* DEBUG */
        break;
        
    case I2O_EXEC_LCT_NOTIFY:
        msgFlag = 1;
#ifdef DEBUG
        kprintf("Received I2O_EXEC_LCT_NOTIFY reply\n");
#endif /* DEBUG */
        break;
        
    case I2O_EXEC_SYS_ENABLE:
        msgFlag = 1;
#ifdef DEBUG
        kprintf("Received I2O_EXEC_SYS_ENABLE reply\n");
#endif /* DEBUG */
        break;

    default:    
#ifdef DEBUG
        kprintf("Received UNKNOWN reply\n");
#endif /* DEBUG */
        break;
    }
}
