root/sys/dev/disk/trm/trm.c
/*
 *      O.S   : FreeBSD CAM
 *      FILE NAME  : trm.c                                            
 *           BY    : C.L. Huang         (ching@tekram.com.tw)
 *                   Erich Chen     (erich@tekram.com.tw)
 *      Description: Device Driver for Tekram SCSI adapters
 *                   DC395U/UW/F ,DC315/U(TRM-S1040)
 *                   DC395U2D/U2W(TRM-S2080)
 *                   PCI SCSI Bus Master Host Adapter
 *                   (SCSI chip set used Tekram ASIC TRM-S1040,TRM-S2080)
 */

/*
 *      HISTORY:                                        
 *                                              
 *      REV#    DATE    NAME            DESCRIPTION     
 *  1.05   05/01/1999  ERICH CHEN  First released for 3.x.x (CAM)
 *  1.06   07/29/1999  ERICH CHEN  Modify for NEW PCI
 *  1.07   12/12/1999  ERICH CHEN  Modify for 3.3.x ,DCB no free
 *  1.08   06/12/2000  ERICH CHEN  Modify for 4.x.x 
 *  1.09   11/03/2000  ERICH CHEN  Modify for 4.1.R ,new sim
 *  1.10   10/10/2001  Oscar Feng  Fixed CAM rescan hang up bug.
 *  1.11   10/13/2001  Oscar Feng  Fixed wrong Async speed display bug.
 */

/*-
 * (C)Copyright 1995-2001 Tekram Technology Co.,Ltd.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * $FreeBSD: src/sys/dev/trm/trm.c,v 1.2.2.2 2002/12/19 20:34:45 cognet Exp $
 */

/*
 * Imported into FreeBSD source repository, and updated to compile under  
 * FreeBSD-3.0-DEVELOPMENT, by Stefan Esser <se@FreeBSD.Org>, 1996-12-17  
 */

/*
 * Updated to compile under FreeBSD 5.0-CURRENT by Olivier Houchard
 * <doginou@ci0.org>, 2002-03-04
 */

#include <sys/param.h>

#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/queue.h>
#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
#include <sys/bio.h>
#endif
#include <sys/buf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/thread2.h>

#include <vm/vm.h>
#include <vm/pmap.h>

#include <bus/pci/pcivar.h>
#include <bus/pci/pcireg.h>
#include <sys/rman.h>

#include <bus/cam/cam.h>
#include <bus/cam/cam_ccb.h>
#include <bus/cam/cam_sim.h>
#include <bus/cam/cam_xpt.h>
#include <bus/cam/cam_xpt_sim.h>
#include <bus/cam/cam_xpt_periph.h>
#include <bus/cam/cam_debug.h>

#include <bus/cam/scsi/scsi_all.h>
#include <bus/cam/scsi/scsi_message.h>

#include "trm.h"

#define trm_reg_read8(reg)      bus_space_read_1(pACB->tag, pACB->bsh, reg)
#define trm_reg_read16(reg)     bus_space_read_2(pACB->tag, pACB->bsh, reg)
#define trm_reg_read32(reg)     bus_space_read_4(pACB->tag, pACB->bsh, reg)
#define trm_reg_write8(value,reg)       bus_space_write_1(pACB->tag, pACB->bsh,\
                reg, value)
#define trm_reg_write16(value,reg)      bus_space_write_2(pACB->tag, pACB->bsh,\
                reg, value)
#define trm_reg_write32(value,reg)      bus_space_write_4(pACB->tag, pACB->bsh,\
                reg, value)

#define PCI_Vendor_ID_TEKRAM    0x1DE1
#define PCI_Device_ID_TRM_S1040 0x0391
#define PCI_DEVICEID_TRMS1040   0x03911DE1
#define PCI_DEVICEID_TRMS2080   0x03921DE1

#ifdef trm_DEBUG1
#define TRM_DPRINTF(fmt, arg...) kprintf("trm: " fmt, ##arg)
#else
#define TRM_DPRINTF(fmt, arg...) {}
#endif /* TRM_DEBUG */

static void     trm_check_eeprom(PNVRAMTYPE pEEpromBuf,PACB pACB);
static void     NVRAM_trm_read_all(PNVRAMTYPE pEEpromBuf,PACB pACB);
static u_int8_t NVRAM_trm_get_data(PACB pACB, u_int8_t bAddr);
static void     NVRAM_trm_write_all(PNVRAMTYPE pEEpromBuf,PACB pACB);
static void     NVRAM_trm_set_data(PACB pACB, u_int8_t bAddr, u_int8_t bData);
static void     NVRAM_trm_write_cmd(PACB pACB, u_int8_t bCmd, u_int8_t bAddr);
static void     NVRAM_trm_wait_30us(PACB pACB);

static void     trm_Interrupt(void *vpACB);
static void     trm_DataOutPhase0(PACB pACB, PSRB pSRB, 
                                         u_int16_t * pscsi_status);
static void     trm_DataInPhase0(PACB pACB, PSRB pSRB, 
                                        u_int16_t * pscsi_status);
static void     trm_CommandPhase0(PACB pACB, PSRB pSRB, 
                                         u_int16_t * pscsi_status);
static void     trm_StatusPhase0(PACB pACB, PSRB pSRB, 
                                        u_int16_t * pscsi_status);
static void     trm_MsgOutPhase0(PACB pACB, PSRB pSRB, 
                                        u_int16_t * pscsi_status);
static void     trm_MsgInPhase0(PACB pACB, PSRB pSRB, 
                                        u_int16_t * pscsi_status);
static void     trm_DataOutPhase1(PACB pACB, PSRB pSRB, 
                                         u_int16_t * pscsi_status);
static void     trm_DataInPhase1(PACB pACB, PSRB pSRB, 
                                        u_int16_t * pscsi_status);
static void     trm_CommandPhase1(PACB pACB, PSRB pSRB, 
                                         u_int16_t * pscsi_status);
static void     trm_StatusPhase1(PACB pACB, PSRB pSRB, 
                                        u_int16_t * pscsi_status);
static void     trm_MsgOutPhase1(PACB pACB, PSRB pSRB, 
                                        u_int16_t * pscsi_status);
static void     trm_MsgInPhase1(PACB pACB, PSRB pSRB, 
                                        u_int16_t * pscsi_status);
static void     trm_Nop0(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status);
static void     trm_Nop1(PACB pACB, PSRB pSRB, u_int16_t * pscsi_status);
static void     trm_SetXferRate(PACB pACB, PSRB pSRB,PDCB pDCB);
static void     trm_DataIO_transfer(PACB pACB, PSRB pSRB, u_int16_t ioDir);
static void     trm_Disconnect(PACB pACB);
static void     trm_Reselect(PACB pACB);
static void     trm_SRBdone(PACB pACB, PDCB pDCB, PSRB pSRB);
static void     trm_DoingSRB_Done(PACB pACB);
static void     trm_ScsiRstDetect(PACB pACB);
static void     trm_ResetSCSIBus(PACB pACB);
static void     trm_RequestSense(PACB pACB, PDCB pDCB, PSRB pSRB);
static void     trm_EnableMsgOutAbort2(PACB pACB, PSRB pSRB);
static void     trm_EnableMsgOutAbort1(PACB pACB, PSRB pSRB);
static void     trm_SendSRB(PACB pACB, PSRB pSRB);
static int      trm_probe(device_t tag);
static int      trm_attach(device_t tag);
static void     trm_reset(PACB pACB);

static u_int16_t        trm_StartSCSI(PACB pACB, PDCB pDCB, PSRB pSRB);

static int      trm_initAdapter(PACB pACB, u_int16_t unit);
static void     trm_initDCB(PACB pACB, PDCB pDCB, u_int16_t unit, 
                                        u_int32_t i, u_int32_t j);
static int      trm_initSRB(PACB pACB);
static void     trm_initACB(PACB pACB, u_int8_t adaptType, u_int16_t unit);
/* CAM SIM entry points */
#define ccb_trmsrb_ptr spriv_ptr0
#define ccb_trmacb_ptr spriv_ptr1
static void     trm_action(struct cam_sim *psim, union ccb *pccb);
static void     trm_poll(struct cam_sim *psim);


static void * trm_SCSI_phase0[] = {
        trm_DataOutPhase0,    /* phase:0 */
        trm_DataInPhase0,     /* phase:1 */
        trm_CommandPhase0,    /* phase:2 */
        trm_StatusPhase0,     /* phase:3 */
        trm_Nop0,             /* phase:4 */
        trm_Nop1,             /* phase:5 */
        trm_MsgOutPhase0,     /* phase:6 */
        trm_MsgInPhase0,      /* phase:7 */
};

/*
 *
 *          stateV = (void *) trm_SCSI_phase1[phase]
 *
 */
static void * trm_SCSI_phase1[] = {
        trm_DataOutPhase1,    /* phase:0 */
        trm_DataInPhase1,     /* phase:1 */
        trm_CommandPhase1,    /* phase:2 */
        trm_StatusPhase1,     /* phase:3 */
        trm_Nop0,             /* phase:4 */
        trm_Nop1,             /* phase:5 */
        trm_MsgOutPhase1,     /* phase:6 */
        trm_MsgInPhase1,      /* phase:7 */
};


NVRAMTYPE trm_eepromBuf[TRM_MAX_ADAPTER_NUM];
/*
 *Fast20:  000   50ns, 20.0 Mbytes/s
 *             001       75ns, 13.3 Mbytes/s
 *             010      100ns, 10.0 Mbytes/s
 *             011      125ns,  8.0 Mbytes/s
 *             100      150ns,  6.6 Mbytes/s
 *             101      175ns,  5.7 Mbytes/s
 *             110      200ns,  5.0 Mbytes/s
 *             111      250ns,  4.0 Mbytes/s
 *
 *Fast40:  000   25ns, 40.0 Mbytes/s
 *             001       50ns, 20.0 Mbytes/s
 *             010       75ns, 13.3 Mbytes/s
 *             011      100ns, 10.0 Mbytes/s
 *             100      125ns,  8.0 Mbytes/s
 *             101      150ns,  6.6 Mbytes/s
 *             110      175ns,  5.7 Mbytes/s
 *             111      200ns,  5.0 Mbytes/s
 */
                                             /* real period: */
u_int8_t dc395x_clock_period[] = {
        12,/*  48  ns 20   MB/sec */
        18,/*  72  ns 13.3 MB/sec */
        25,/* 100  ns 10.0 MB/sec */
        31,/* 124  ns  8.0 MB/sec */
        37,/* 148  ns  6.6 MB/sec */
        43,/* 172  ns  5.7 MB/sec */
        50,/* 200  ns  5.0 MB/sec */
        62 /* 248  ns  4.0 MB/sec */
};

u_int8_t dc395u2x_clock_period[]={
        10,/*  25  ns 40.0 MB/sec */
        12,/*  48  ns 20.0 MB/sec */
        18,/*  72  ns 13.3 MB/sec */
        25,/* 100  ns 10.0 MB/sec */
        31,/* 124  ns  8.0 MB/sec */
        37,/* 148  ns  6.6 MB/sec */
        43,/* 172  ns  5.7 MB/sec */
        50,/* 200  ns  5.0 MB/sec */
};

#define  dc395x_tinfo_period           dc395x_clock_period
#define  dc395u2x_tinfo_period         dc395u2x_clock_period

static PSRB
trm_GetSRB(PACB pACB)
{
        PSRB    pSRB;

        crit_enter();
        pSRB = pACB->pFreeSRB;
        if (pSRB) {
                pACB->pFreeSRB = pSRB->pNextSRB;
                pSRB->pNextSRB = NULL;
        }
        crit_exit();
        return (pSRB);
}

static void
trm_RewaitSRB0(PDCB pDCB, PSRB pSRB)
{
        PSRB    psrb1;

        crit_enter();
        if ((psrb1 = pDCB->pWaitingSRB)) {
                pSRB->pNextSRB = psrb1;
                pDCB->pWaitingSRB = pSRB;
        } else {
                pSRB->pNextSRB = NULL;
                pDCB->pWaitingSRB = pSRB;
                pDCB->pWaitingLastSRB = pSRB;
        }
        crit_exit();
}

static void
trm_RewaitSRB(PDCB pDCB, PSRB pSRB)
{
        PSRB    psrb1;

        crit_enter();
        pDCB->GoingSRBCnt--;
        psrb1 = pDCB->pGoingSRB;
        if (pSRB == psrb1)
                /*
                 * if this SRB is GoingSRB
                 * remove this SRB from GoingSRB Q
                 */
                pDCB->pGoingSRB = psrb1->pNextSRB;
        else {
                /*
                 * if this SRB is not current GoingSRB
                 * remove this SRB from GoingSRB Q
                 */
                while (pSRB != psrb1->pNextSRB)
                        psrb1 = psrb1->pNextSRB;
                psrb1->pNextSRB = pSRB->pNextSRB;
                if (pSRB == pDCB->pGoingLastSRB)
                        pDCB->pGoingLastSRB = psrb1;
        }
        if ((psrb1 = pDCB->pWaitingSRB)) {
                /*
                 * if WaitingSRB Q is not NULL
                 * Q back this SRB into WaitingSRB
                 */

                pSRB->pNextSRB = psrb1;
                pDCB->pWaitingSRB = pSRB;
        } else {
                pSRB->pNextSRB = NULL;
                pDCB->pWaitingSRB = pSRB;
                pDCB->pWaitingLastSRB = pSRB;
        }
        crit_exit();
}

static void
trm_DoWaitingSRB(PACB pACB)
{
        PDCB    ptr, ptr1;
        PSRB    pSRB;

        crit_enter();
        if (!(pACB->pActiveDCB) && 
            !(pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV))) {
                ptr = pACB->pDCBRunRobin;
                if (!ptr) {
                        ptr = pACB->pLinkDCB;
                        pACB->pDCBRunRobin = ptr;
                }
                ptr1 = ptr;
                for (;ptr1 ;) {
                        pACB->pDCBRunRobin = ptr1->pNextDCB;
                        if (!(ptr1->MaxActiveCommandCnt > ptr1->GoingSRBCnt)
                            || !(pSRB = ptr1->pWaitingSRB)) {
                                if (pACB->pDCBRunRobin == ptr)
                                        break;
                                ptr1 = ptr1->pNextDCB;
                        } else {
                                if (!trm_StartSCSI(pACB, ptr1, pSRB)) {
                                /* 
                                 * If trm_StartSCSI return 0 :
                                 * current interrupt status is interrupt enable 
                                 * It's said that SCSI processor is unoccupied 
                                 */
                                        ptr1->GoingSRBCnt++;
                                        if (ptr1->pWaitingLastSRB == pSRB) {
                                                ptr1->pWaitingSRB = NULL;
                                                ptr1->pWaitingLastSRB = NULL;
                                        } else
                                                ptr1->pWaitingSRB = pSRB->pNextSRB;
                                        pSRB->pNextSRB = NULL;
                                        if (ptr1->pGoingSRB) 
                                                ptr1->pGoingLastSRB->pNextSRB = pSRB;
                                        else
                                                ptr1->pGoingSRB = pSRB;
                                        ptr1->pGoingLastSRB = pSRB;
                                }
                                break;
                        }
                }
        }
        crit_exit();
        return;
}

static void
trm_SRBwaiting(PDCB pDCB, PSRB pSRB)
{
  
        if (pDCB->pWaitingSRB) {
                pDCB->pWaitingLastSRB->pNextSRB = pSRB;
                pDCB->pWaitingLastSRB = pSRB;
                pSRB->pNextSRB = NULL;
        } else {
                pDCB->pWaitingSRB = pSRB;
                pDCB->pWaitingLastSRB = pSRB;
        }
}

static u_int32_t
trm_get_sense_bufaddr(PACB pACB, PSRB pSRB)
{
        int offset;

        offset = pSRB->TagNumber;
        return (pACB->sense_busaddr +
            (offset * sizeof(struct scsi_sense_data)));
}

static struct scsi_sense_data *
trm_get_sense_buf(PACB pACB, PSRB pSRB)
{
        int offset;

        offset = pSRB->TagNumber;
        return (&pACB->sense_buffers[offset]);
}
static void
trm_ExecuteSRB(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
{
        PACB            pACB;
        PSRB            pSRB;
        union ccb       *ccb;
        u_long          totalxferlen=0;

        crit_enter();
        pSRB = (PSRB)arg;
        ccb = pSRB->pccb;
        pACB = (PACB)ccb->ccb_h.ccb_trmacb_ptr;
        TRM_DPRINTF("trm_ExecuteSRB..........\n");        
        if (nseg != 0) {
                PSEG                    psg;
                bus_dma_segment_t       *end_seg;
                bus_dmasync_op_t        op;

                /* Copy the segments into our SG list */
                end_seg = dm_segs + nseg;
                psg = pSRB->pSRBSGL;
                while (dm_segs < end_seg) {
                        psg->address = dm_segs->ds_addr;
                        psg->length = (u_long)dm_segs->ds_len;
                        totalxferlen += dm_segs->ds_len;
                        psg++;
                        dm_segs++;
                }
                if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) {
                        op = BUS_DMASYNC_PREREAD;
                } else {
                        op = BUS_DMASYNC_PREWRITE;
                }
                bus_dmamap_sync(pACB->buffer_dmat, pSRB->dmamap, op);
        }
        pSRB->RetryCnt = 0;
        pSRB->SRBTotalXferLength = totalxferlen;
        pSRB->SRBSGCount = nseg;
        pSRB->SRBSGIndex = 0;
        pSRB->AdaptStatus = 0;
        pSRB->TargetStatus = 0;
        pSRB->MsgCnt = 0;
        pSRB->SRBStatus = 0;
        pSRB->SRBFlag = 0;
        pSRB->SRBState = 0;
        pSRB->ScsiPhase = PH_BUS_FREE; /* SCSI bus free Phase */

        if (ccb->ccb_h.status != CAM_REQ_INPROG) {
                if (nseg != 0)
                        bus_dmamap_unload(pACB->buffer_dmat, pSRB->dmamap);
                pSRB->pNextSRB = pACB->pFreeSRB;
                pACB->pFreeSRB = pSRB;
                xpt_done(ccb);
                crit_exit();
                return;
        }
        ccb->ccb_h.status |= CAM_SIM_QUEUED;
#if 0
        /* XXX Need a timeout handler */
        callout_reset(&ccb->ccb_h.timeout_ch, (ccb->ccb_h.timeout * hz) / 1000,
                      trmtimeout, srb);
#endif
        trm_SendSRB(pACB, pSRB);
        crit_exit();
        return;
}

static void
trm_SendSRB(PACB pACB, PSRB pSRB)
{
        PDCB    pDCB;

        pDCB = pSRB->pSRBDCB;
        if (!(pDCB->MaxActiveCommandCnt > pDCB->GoingSRBCnt) || (pACB->pActiveDCB)
            || (pACB->ACBFlag & (RESET_DETECT+RESET_DONE+RESET_DEV))) {
                TRM_DPRINTF("pDCB->MaxCommand=%d \n",pDCB->MaxActiveCommandCnt);
                TRM_DPRINTF("pDCB->GoingSRBCnt=%d \n",pDCB->GoingSRBCnt);
                TRM_DPRINTF("pACB->pActiveDCB=%8x \n",(u_int)pACB->pActiveDCB);
                TRM_DPRINTF("pACB->ACBFlag=%x \n",pACB->ACBFlag);
                trm_SRBwaiting(pDCB, pSRB);
                goto SND_EXIT;
        }

        if (pDCB->pWaitingSRB) {
                trm_SRBwaiting(pDCB, pSRB);
                pSRB = pDCB->pWaitingSRB;
                pDCB->pWaitingSRB = pSRB->pNextSRB;
                pSRB->pNextSRB = NULL;
        }

        if (!trm_StartSCSI(pACB, pDCB, pSRB)) { 
        /* 
         * If trm_StartSCSI return 0 :
         * current interrupt status is interrupt enable 
         * It's said that SCSI processor is unoccupied 
         */
                pDCB->GoingSRBCnt++; /* stack waiting SRB*/
                if (pDCB->pGoingSRB) {
                        pDCB->pGoingLastSRB->pNextSRB = pSRB;
                        pDCB->pGoingLastSRB = pSRB;
                } else {
                        pDCB->pGoingSRB = pSRB;
                        pDCB->pGoingLastSRB = pSRB;
                }
        } else {
        /* 
         * If trm_StartSCSI return 1 :
         * current interrupt status is interrupt disreenable 
         * It's said that SCSI processor has more one SRB need to do
         */
                trm_RewaitSRB0(pDCB, pSRB);
        }
SND_EXIT:
        return;
}


static void
trm_action(struct cam_sim *psim, union ccb *pccb) 
{
        PACB    pACB;
        u_int   target_id,target_lun;

        CAM_DEBUG(pccb->ccb_h.path, CAM_DEBUG_TRACE, ("trm_action\n"));

        crit_enter();
        pACB = (PACB) cam_sim_softc(psim);
        target_id  = pccb->ccb_h.target_id;
        target_lun = pccb->ccb_h.target_lun;

        switch (pccb->ccb_h.func_code) {
                case XPT_NOOP:                  
                        TRM_DPRINTF(" XPT_NOOP \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Execute the requested I/O operation 
                 */
                case XPT_SCSI_IO: {
                        PDCB                    pDCB = NULL;
                        PSRB                    pSRB;
                        struct ccb_scsiio       *pcsio;
     
                        pcsio = &pccb->csio;
                        TRM_DPRINTF(" XPT_SCSI_IO \n");
                        TRM_DPRINTF("trm: target_id= %d target_lun= %d \n"
                             ,target_id, target_lun);
                        TRM_DPRINTF(
                            "pACB->scan_devices[target_id][target_lun]= %d \n"
                            ,pACB->scan_devices[target_id][target_lun]);
                        if ((pccb->ccb_h.status & CAM_STATUS_MASK) !=
                            CAM_REQ_INPROG) {
                                xpt_done(pccb);
                                crit_exit();
                                return;
                        }
                        pDCB = &pACB->DCBarray[target_id][target_lun];
                        if (!(pDCB->DCBstatus & DS_IN_QUEUE)) {
                                pACB->scan_devices[target_id][target_lun] = 1;
                                trm_initDCB(pACB, pDCB, pACB->AdapterUnit,
                                    target_id, target_lun);
                        }
                        /*
                         * Assign an SRB and connect it with this ccb.
                         */
                        pSRB = trm_GetSRB(pACB);
                        if (!pSRB) {
                                /* Freeze SIMQ */
                                pccb->ccb_h.status = CAM_RESRC_UNAVAIL;
                                xpt_done(pccb);
                                crit_exit();
                                return;
                        }
                        pSRB->pSRBDCB = pDCB;
                        pccb->ccb_h.ccb_trmsrb_ptr = pSRB;
                        pccb->ccb_h.ccb_trmacb_ptr = pACB;
                        pSRB->pccb = pccb;
                        pSRB->ScsiCmdLen = pcsio->cdb_len;
                        /* 
                         * move layer of CAM command block to layer of SCSI
                         * Request Block for SCSI processor command doing
                         */
                        if ((pccb->ccb_h.flags & CAM_CDB_POINTER) != 0) {
                                if ((pccb->ccb_h.flags & CAM_CDB_PHYS) == 0) {
                                        bcopy(pcsio->cdb_io.cdb_ptr,pSRB->CmdBlock
                                            ,pcsio->cdb_len);
                                } else {
                                        pccb->ccb_h.status = CAM_REQ_INVALID;
                                        pSRB->pNextSRB = pACB->pFreeSRB;
                                        pACB->pFreeSRB=  pSRB;
                                        xpt_done(pccb);
                                        crit_exit();
                                        return;
                                }
                        } else
                                bcopy(pcsio->cdb_io.cdb_bytes,
                                    pSRB->CmdBlock, pcsio->cdb_len);
                        if ((pccb->ccb_h.flags & CAM_DIR_MASK)
                            != CAM_DIR_NONE) {
                                if ((pccb->ccb_h.flags &
                                      CAM_SCATTER_VALID) == 0) {
                                        if ((pccb->ccb_h.flags 
                                              & CAM_DATA_PHYS) == 0) {
                                                int error;

                                                crit_enter();
                                                error = bus_dmamap_load(
                                                    pACB->buffer_dmat,
                                                    pSRB->dmamap,
                                                    pcsio->data_ptr,
                                                    pcsio->dxfer_len,
                                                    trm_ExecuteSRB,
                                                    pSRB,
                                                    0);
                                                if (error == EINPROGRESS) {
                                                        xpt_freeze_simq(
                                                            pACB->psim,
                                                            1);
                                                        pccb->ccb_h.status |=
                                                          CAM_RELEASE_SIMQ;
                                                }
                                                crit_exit();
                                        } else {   
                                                struct bus_dma_segment seg;

                                                /* Pointer to physical buffer */
                                                seg.ds_addr = 
                                                  (bus_addr_t)pcsio->data_ptr;
                                                seg.ds_len = pcsio->dxfer_len;
                                                trm_ExecuteSRB(pSRB, &seg, 1,
                                                    0);
                                        }
                                } else { 
                                        /*  CAM_SCATTER_VALID */
                                        struct bus_dma_segment *segs;

                                        if ((pccb->ccb_h.flags &
                                             CAM_SG_LIST_PHYS) == 0 ||
                                             (pccb->ccb_h.flags 
                                             & CAM_DATA_PHYS) != 0) {
                                                pSRB->pNextSRB = pACB->pFreeSRB;
                                                pACB->pFreeSRB = pSRB;
                                                pccb->ccb_h.status = 
                                                  CAM_PROVIDE_FAIL;
                                                xpt_done(pccb);
                                                crit_exit();
                                                return;
                                        }

                                        /* cam SG list is physical,
                                         *  cam data is virtual 
                                         */
                                        segs = (struct bus_dma_segment *)
                                            pcsio->data_ptr;
                                        trm_ExecuteSRB(pSRB, segs,
                                            pcsio->sglist_cnt, 1);
                                }   /*  CAM_SCATTER_VALID */
                        } else
                                trm_ExecuteSRB(pSRB, NULL, 0, 0);
                                  }
                        break;
                case XPT_GDEV_TYPE:                 
                        TRM_DPRINTF(" XPT_GDEV_TYPE \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                case XPT_GDEVLIST:                  
                        TRM_DPRINTF(" XPT_GDEVLIST \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Path routing inquiry 
                 * Path Inquiry CCB 
                 */
                case XPT_PATH_INQ: {
                        struct ccb_pathinq *cpi = &pccb->cpi;

                        TRM_DPRINTF(" XPT_PATH_INQ \n");
                        cpi->version_num = 1; 
                        cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16;
                        cpi->target_sprt = 0;
                        cpi->hba_misc = 0;
                        cpi->hba_eng_cnt = 0;
                        cpi->max_target = 15 ; 
                        cpi->max_lun = pACB->max_lun;        /* 7 or 0 */
                        cpi->initiator_id = pACB->AdaptSCSIID;
                        cpi->bus_id = cam_sim_bus(psim);
                        cpi->base_transfer_speed = 3300;
                        strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
                        strncpy(cpi->hba_vid, "Tekram_TRM", HBA_IDLEN);
                        strncpy(cpi->dev_name, cam_sim_name(psim), DEV_IDLEN);
                        cpi->unit_number = cam_sim_unit(psim);
                        cpi->transport = XPORT_SPI;
                        cpi->transport_version = 2;
                        cpi->protocol = PROTO_SCSI;
                        cpi->protocol_version = SCSI_REV_2;
                        cpi->ccb_h.status = CAM_REQ_CMP;
                        xpt_done(pccb);
                                   }
                        break;
                /*
                 * Release a frozen SIM queue 
                 * Release SIM Queue 
                 */
                case XPT_REL_SIMQ:                  
                        TRM_DPRINTF(" XPT_REL_SIMQ \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Set Asynchronous Callback Parameters 
                 * Set Asynchronous Callback CCB
                 */
                case XPT_SASYNC_CB:                 
                        TRM_DPRINTF(" XPT_SASYNC_CB \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Set device type information 
                 * Set Device Type CCB 
                 */
                case XPT_SDEV_TYPE:                 
                        TRM_DPRINTF(" XPT_SDEV_TYPE \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * (Re)Scan the SCSI Bus 
                 * Rescan the given bus, or bus/target/lun
                 */
                case XPT_SCAN_BUS:                  
                        TRM_DPRINTF(" XPT_SCAN_BUS \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Get EDT entries matching the given pattern 
                 */
                case XPT_DEV_MATCH:             
                        TRM_DPRINTF(" XPT_DEV_MATCH \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Turn on debugging for a bus, target or lun 
                 */
                case XPT_DEBUG:             
                        TRM_DPRINTF(" XPT_DEBUG \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                        /*
                         * XPT_ABORT = 0x10, Abort the specified CCB 
                         * Abort XPT request CCB 
                         */
                case XPT_ABORT:             
                        TRM_DPRINTF(" XPT_ABORT \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Reset the specified SCSI bus 
                 * Reset SCSI Bus CCB 
                 */
                case XPT_RESET_BUS: {           
                        int i;

                        TRM_DPRINTF(" XPT_RESET_BUS \n");
                        trm_reset(pACB);
                        pACB->ACBFlag=0;
                        for (i=0; i<500; i++)
                                DELAY(1000);
                        pccb->ccb_h.status = CAM_REQ_CMP;
                        xpt_done(pccb);
                                    }
                        break;
                /*
                 * Bus Device Reset the specified SCSI device 
                 * Reset SCSI Device CCB 
                 */
                case XPT_RESET_DEV:             
                /*
                 * Don't (yet?) support vendor
                 * specific commands.
                 */
                        TRM_DPRINTF(" XPT_RESET_DEV \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Terminate the I/O process 
                 * Terminate I/O Process Request CCB 
                 */
                case XPT_TERM_IO:               
                        TRM_DPRINTF(" XPT_TERM_IO \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Scan Logical Unit 
                 */
                case XPT_SCAN_LUN:                 
                        TRM_DPRINTF(" XPT_SCAN_LUN \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;

                /*
                 * Get/Set transfer rate/width/disconnection/tag queueing 
                 * settings 
                 * (GET) default/user transfer settings for the target 
                 */
                case XPT_GET_TRAN_SETTINGS: {
                        struct  ccb_trans_settings *cts = &pccb->cts;
                        struct  trm_transinfo *tinfo;
                        PDCB    pDCB;   
                        struct ccb_trans_settings_scsi *scsi =
                            &cts->proto_specific.scsi;
                        struct ccb_trans_settings_spi *spi =
                            &cts->xport_specific.spi;

                        cts->protocol = PROTO_SCSI;
                        cts->protocol_version = SCSI_REV_2;
                        cts->transport = XPORT_SPI;
                        cts->transport_version = 2;

                        TRM_DPRINTF(" XPT_GET_TRAN_SETTINGS \n");
                        pDCB = &pACB->DCBarray[target_id][target_lun];
                        crit_enter();
                        /*
                         * disable interrupt
                         */
                        if (cts->type == CTS_TYPE_CURRENT_SETTINGS) {
                                /* current transfer settings */
                                if (pDCB->tinfo.disc_tag & TRM_CUR_DISCENB)
                                        spi->flags = CTS_SPI_FLAGS_DISC_ENB;
                                else
                                        spi->flags = 0;/* no tag & disconnect */
                                if (pDCB->tinfo.disc_tag & TRM_CUR_TAGENB)
                                        scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB;
                                tinfo = &pDCB->tinfo.current;
                                TRM_DPRINTF("CURRENT:  cts->flags= %2x \n",
                                    cts->flags);
                        } else {
                          /* default(user) transfer settings */
                                if (pDCB->tinfo.disc_tag & TRM_USR_DISCENB)
                                        spi->flags = CTS_SPI_FLAGS_DISC_ENB;
                                else
                                        spi->flags = 0;
                                if (pDCB->tinfo.disc_tag & TRM_USR_TAGENB)
                                        scsi->flags |= CTS_SCSI_FLAGS_TAG_ENB;
                                tinfo = &pDCB->tinfo.user;
                                TRM_DPRINTF("USER: cts->flags= %2x \n",
                                        cts->flags);
                        }
                        spi->sync_period = tinfo->period;
                        spi->sync_offset = tinfo->offset;
                        spi->bus_width   = tinfo->width;
                        TRM_DPRINTF("pDCB->SyncPeriod: %d  \n",
                                pDCB->SyncPeriod);
                        TRM_DPRINTF("period: %d  \n", tinfo->period);
                        TRM_DPRINTF("offset: %d  \n", tinfo->offset);
                        TRM_DPRINTF("width: %d  \n", tinfo->width);

                        crit_exit();
                        spi->valid = CTS_SPI_VALID_SYNC_RATE |
                            CTS_SPI_VALID_SYNC_OFFSET |
                            CTS_SPI_VALID_BUS_WIDTH |
                            CTS_SPI_VALID_DISC;
                        scsi->valid = CTS_SCSI_VALID_TQ;
                        pccb->ccb_h.status = CAM_REQ_CMP;
                        xpt_done(pccb);
                                            }
                        break;
                /* 
                 * Get/Set transfer rate/width/disconnection/tag queueing 
                 * settings
                 * (Set) transfer rate/width negotiation settings 
                 */
                case XPT_SET_TRAN_SETTINGS: {
                        struct  ccb_trans_settings *cts =  &pccb->cts;
                        u_int   update_type;
                        PDCB    pDCB;
                        struct ccb_trans_settings_scsi *scsi =
                            &cts->proto_specific.scsi;
                        struct ccb_trans_settings_spi *spi =
                            &cts->xport_specific.spi;

                        TRM_DPRINTF(" XPT_SET_TRAN_SETTINGS \n");
                        update_type = 0;
                        if (cts->type == CTS_TYPE_CURRENT_SETTINGS)
                                update_type |= TRM_TRANS_GOAL;
                        if (cts->type == CTS_TYPE_USER_SETTINGS)
                                update_type |= TRM_TRANS_USER;
                        crit_enter();
                        pDCB = &pACB->DCBarray[target_id][target_lun];

                        if ((spi->valid & CTS_SPI_VALID_DISC) != 0) {
                          /*ccb disc enables */
                                if (update_type & TRM_TRANS_GOAL) {
                                        if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB)
                                            != 0)
                                                pDCB->tinfo.disc_tag
                                                    |= TRM_CUR_DISCENB;
                                        else
                                                pDCB->tinfo.disc_tag &=
                                                    ~TRM_CUR_DISCENB;
                                }
                                if (update_type & TRM_TRANS_USER) {
                                        if ((spi->flags & CTS_SPI_FLAGS_DISC_ENB)
                                            != 0)
                                                pDCB->tinfo.disc_tag
                                                    |= TRM_USR_DISCENB;
                                        else
                                                pDCB->tinfo.disc_tag &=
                                                    ~TRM_USR_DISCENB;
                                }
                        }
                        if ((scsi->valid & CTS_SCSI_VALID_TQ) != 0) {
                          /* if ccb tag q active */
                                if (update_type & TRM_TRANS_GOAL) {
                                        if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB)
                                            != 0)
                                                pDCB->tinfo.disc_tag |=
                                                    TRM_CUR_TAGENB;
                                        else
                                                pDCB->tinfo.disc_tag &=
                                                    ~TRM_CUR_TAGENB;
                                }
                                if (update_type & TRM_TRANS_USER) {
                                        if ((scsi->flags & CTS_SCSI_FLAGS_TAG_ENB)
                                            != 0)
                                                pDCB->tinfo.disc_tag |=
                                                    TRM_USR_TAGENB;
                                        else
                                                pDCB->tinfo.disc_tag &=
                                                    ~TRM_USR_TAGENB;
                                }
                        }
                        /* Minimum sync period factor   */

                        if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) != 0) {
                                /* if ccb sync active */
                                /* TRM-S1040 MinSyncPeriod = 4 clocks/byte */
                                if ((spi->sync_period != 0) &&
                                    (spi->sync_period < 125))
                                        spi->sync_period = 125;
                                /* 1/(125*4) minsync 2 MByte/sec */
                                if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET)
                                    != 0) {
                                        if (spi->sync_offset == 0)
                                                spi->sync_period = 0;
                                        /* TRM-S1040 MaxSyncOffset = 15 bytes*/
                                        if (spi->sync_offset > 15)
                                                spi->sync_offset = 15;
                                }
                        }
                        if ((update_type & TRM_TRANS_USER) != 0) {
                                pDCB->tinfo.user.period = spi->sync_period;
                                pDCB->tinfo.user.offset = spi->sync_offset;
                                pDCB->tinfo.user.width  = spi->bus_width;
                        }
                        if ((update_type & TRM_TRANS_GOAL) != 0) {
                                pDCB->tinfo.goal.period = spi->sync_period;
                                pDCB->tinfo.goal.offset = spi->sync_offset;
                                pDCB->tinfo.goal.width  = spi->bus_width;
                        }
                        crit_exit();
                        pccb->ccb_h.status = CAM_REQ_CMP;
                        xpt_done(pccb);
                        break;
                                            }
                /*
                 * Calculate the geometry parameters for a device give
                 * the sector size and volume size. 
                 */
                case XPT_CALC_GEOMETRY:
                        TRM_DPRINTF(" XPT_CALC_GEOMETRY \n");
                        cam_calc_geometry(&pccb->ccg, /*extended*/1);
                        xpt_done(pccb);
                        break;
                case XPT_ENG_INQ:           
                        TRM_DPRINTF(" XPT_ENG_INQ \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * HBA execute engine request 
                 * This structure must match SCSIIO size 
                 */
                case XPT_ENG_EXEC:                  
                        TRM_DPRINTF(" XPT_ENG_EXEC \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * XPT_EN_LUN = 0x30, Enable LUN as a target 
                 * Target mode structures. 
                 */
                case XPT_EN_LUN:            
                /*
                 * Don't (yet?) support vendor
                 * specific commands.
                 */
                        TRM_DPRINTF(" XPT_EN_LUN \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                * Execute target I/O request 
                */
                case XPT_TARGET_IO:                 
                /*
                 * Don't (yet?) support vendor
                 * specific commands.
                 */
                        TRM_DPRINTF(" XPT_TARGET_IO \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Accept Host Target Mode CDB 
                 */
                case XPT_ACCEPT_TARGET_IO:      
                /*
                 * Don't (yet?) support vendor
                 * specific commands.
                 */
                        TRM_DPRINTF(" XPT_ACCEPT_TARGET_IO \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Continue Host Target I/O Connection 
                 */
                case XPT_CONT_TARGET_IO:        
                /*
                 * Don't (yet?) support vendor
                 * specific commands.
                 */
                        TRM_DPRINTF(" XPT_CONT_TARGET_IO \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Notify Host Target driver of event 
                 */
                case XPT_IMMED_NOTIFY:      
                        TRM_DPRINTF(" XPT_IMMED_NOTIFY \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * Acknowledgement of event
                 */
                case XPT_NOTIFY_ACK:        
                        TRM_DPRINTF(" XPT_NOTIFY_ACK \n");
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                /*
                 * XPT_VUNIQUE = 0x80
                 */
                case XPT_VUNIQUE:   
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
                default:
                        pccb->ccb_h.status = CAM_REQ_INVALID;
                        xpt_done(pccb);
                        break;
        }
        crit_exit();
}

static void 
trm_poll(struct cam_sim *psim)
{       
        trm_Interrupt(cam_sim_softc(psim));     
}

static void
trm_ResetDevParam(PACB pACB)
{
        PDCB            pDCB, pdcb;
        PNVRAMTYPE      pEEpromBuf;
        u_int8_t        PeriodIndex;

        pDCB = pACB->pLinkDCB;
        if (pDCB == NULL)
                return;
        pdcb = pDCB;
        do {
                pDCB->SyncMode  &= ~(SYNC_NEGO_DONE+ WIDE_NEGO_DONE);
                pDCB->SyncPeriod = 0;
                pDCB->SyncOffset = 0;
                pEEpromBuf = &trm_eepromBuf[pACB->AdapterUnit];
                pDCB->DevMode = 
                  pEEpromBuf->NvramTarget[pDCB->TargetID].NvmTarCfg0;
                pDCB->AdpMode = pEEpromBuf->NvramChannelCfg;
                PeriodIndex =
                   pEEpromBuf->NvramTarget[pDCB->TargetID].NvmTarPeriod & 0x07;
                if (pACB->AdaptType == 1) /* is U2? */
                        pDCB->MaxNegoPeriod = dc395u2x_clock_period[PeriodIndex];
                else
                        pDCB->MaxNegoPeriod = dc395x_clock_period[PeriodIndex];
                if ((pDCB->DevMode & NTC_DO_WIDE_NEGO) && 
                    (pACB->Config & HCC_WIDE_CARD))
                        pDCB->SyncMode |= WIDE_NEGO_ENABLE;
                pDCB = pDCB->pNextDCB;
        }
        while (pdcb != pDCB);
}

static void
trm_RecoverSRB(PACB pACB)
{
        PDCB            pDCB, pdcb;
        PSRB            psrb, psrb2;
        u_int16_t       cnt, i;

        pDCB = pACB->pLinkDCB;
        if (pDCB == NULL)
                return;
        pdcb = pDCB;
        do {
                cnt = pdcb->GoingSRBCnt;
                psrb = pdcb->pGoingSRB;
                for (i = 0; i < cnt; i++) {
                        psrb2 = psrb;
                        psrb = psrb->pNextSRB;
                        if (pdcb->pWaitingSRB) {
                                psrb2->pNextSRB = pdcb->pWaitingSRB;
                                pdcb->pWaitingSRB = psrb2;
                        } else {
                                pdcb->pWaitingSRB = psrb2;
                                pdcb->pWaitingLastSRB = psrb2;
                                psrb2->pNextSRB = NULL;
                        }
                }
                pdcb->GoingSRBCnt = 0;
                pdcb->pGoingSRB = NULL;
                pdcb = pdcb->pNextDCB;
        }
        while (pdcb != pDCB);
}

static void
trm_reset(PACB pACB)
{
        u_int16_t       i;

        TRM_DPRINTF("trm: RESET");
        crit_enter();
        trm_reg_write8(0x00, TRMREG_DMA_INTEN);
        trm_reg_write8(0x00, TRMREG_SCSI_INTEN);

        trm_ResetSCSIBus(pACB);
        for (i = 0; i < 500; i++)
                DELAY(1000);
        trm_reg_write8(0x7F, TRMREG_SCSI_INTEN); 
        /* Enable DMA interrupt */
        trm_reg_write8(EN_SCSIINTR, TRMREG_DMA_INTEN);
        /* Clear DMA FIFO */
        trm_reg_write8(CLRXFIFO, TRMREG_DMA_CONTROL);
        /* Clear SCSI FIFO */
        trm_reg_write16(DO_CLRFIFO,TRMREG_SCSI_CONTROL);
        trm_ResetDevParam(pACB);
        trm_DoingSRB_Done(pACB);
        pACB->pActiveDCB = NULL;
        pACB->ACBFlag = 0;/* RESET_DETECT, RESET_DONE ,RESET_DEV */
        trm_DoWaitingSRB(pACB);
        /* Tell the XPT layer that a bus reset occured    */
        if (pACB->ppath != NULL)
                xpt_async(AC_BUS_RESET, pACB->ppath, NULL);
        crit_exit();
        return;
}

static u_int16_t
trm_StartSCSI(PACB pACB, PDCB pDCB, PSRB pSRB)
{
        u_int16_t       return_code;
        u_int8_t        scsicommand, i,command,identify_message;
        u_int8_t *      ptr;
        union  ccb      *pccb;
        struct ccb_scsiio *pcsio;

        pccb  = pSRB->pccb;
        pcsio = &pccb->csio;

        trm_reg_write8(pACB->AdaptSCSIID, TRMREG_SCSI_HOSTID);
        trm_reg_write8(pDCB->TargetID, TRMREG_SCSI_TARGETID);
        trm_reg_write8(pDCB->SyncPeriod, TRMREG_SCSI_SYNC);
        trm_reg_write8(pDCB->SyncOffset, TRMREG_SCSI_OFFSET);
        pSRB->ScsiPhase = PH_BUS_FREE;/* initial phase */
        /* Flush FIFO */
        trm_reg_write16(DO_CLRFIFO, TRMREG_SCSI_CONTROL);

        identify_message = pDCB->IdentifyMsg;

        if ((pSRB->CmdBlock[0] == INQUIRY) ||
            (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
            (pSRB->SRBFlag & AUTO_REQSENSE)) {
                if (((pDCB->SyncMode & WIDE_NEGO_ENABLE) &&
                      !(pDCB->SyncMode & WIDE_NEGO_DONE))
                || ((pDCB->SyncMode & SYNC_NEGO_ENABLE) &&
                  !(pDCB->SyncMode & SYNC_NEGO_DONE))) {
                        if (!(pDCB->IdentifyMsg & 7) ||
                            (pSRB->CmdBlock[0] != INQUIRY)) {
                                scsicommand = SCMD_SEL_ATNSTOP;
                                pSRB->SRBState = SRB_MSGOUT;
                                goto polling;
                        }
                }
        /* 
        * Send identify message 
        */
                trm_reg_write8((identify_message & 0xBF) ,TRMREG_SCSI_FIFO); 
                scsicommand = SCMD_SEL_ATN;
                pSRB->SRBState = SRB_START_;
        } else {
                /* not inquiry,request sense,auto request sense */
                /* 
                 * Send identify message        
                 */
                trm_reg_write8(identify_message,TRMREG_SCSI_FIFO);
                scsicommand = SCMD_SEL_ATN;
                pSRB->SRBState = SRB_START_;
                if (pDCB->SyncMode & EN_TAG_QUEUING) {
                  /* Send Tag message */
                        trm_reg_write8(MSG_SIMPLE_QTAG, TRMREG_SCSI_FIFO);
                        trm_reg_write8(pSRB->TagNumber, TRMREG_SCSI_FIFO);
                        scsicommand = SCMD_SEL_ATN3;
                }
        }
polling:
        /*
         *       Send CDB ..command block .........                     
         */
        if (pSRB->SRBFlag & AUTO_REQSENSE) {
                trm_reg_write8(REQUEST_SENSE, TRMREG_SCSI_FIFO);
                trm_reg_write8((pDCB->IdentifyMsg << 5), TRMREG_SCSI_FIFO);
                trm_reg_write8(0, TRMREG_SCSI_FIFO);
                trm_reg_write8(0, TRMREG_SCSI_FIFO);
                trm_reg_write8(pcsio->sense_len, TRMREG_SCSI_FIFO);
                trm_reg_write8(0, TRMREG_SCSI_FIFO);
        } else {
                ptr = (u_int8_t *) pSRB->CmdBlock;
                for (i = 0; i < pSRB->ScsiCmdLen ; i++) {
                        command = *ptr++;
                        trm_reg_write8(command,TRMREG_SCSI_FIFO);
                }
        }
        if (trm_reg_read16(TRMREG_SCSI_STATUS) & SCSIINTERRUPT) { 
            /* 
             * If trm_StartSCSI return 1 :
             * current interrupt status is interrupt disreenable 
             * It's said that SCSI processor has more one SRB need to do,
             * SCSI processor has been occupied by one SRB.
             */
                pSRB->SRBState = SRB_READY;
                return_code = 1;
        } else { 
          /* 
           * If trm_StartSCSI return 0 :
           * current interrupt status is interrupt enable 
           * It's said that SCSI processor is unoccupied 
           */
                pSRB->ScsiPhase  = SCSI_NOP1; /* SCSI bus free Phase */
                pACB->pActiveDCB = pDCB;
                pDCB->pActiveSRB = pSRB;
                return_code = 0;
                trm_reg_write16(DO_DATALATCH | DO_HWRESELECT, 
                    TRMREG_SCSI_CONTROL);/* it's important for atn stop*/
                /*
                 * SCSI cammand 
                 */
                trm_reg_write8(scsicommand,TRMREG_SCSI_COMMAND);
        }
        return (return_code);   
}

static void 
trm_Interrupt(void *vpACB)
{
        PACB            pACB;
        PDCB            pDCB;
        PSRB            pSRB;
        u_int16_t       phase;
        void            (*stateV)(PACB, PSRB, u_int16_t *);
        u_int16_t       scsi_status=0;
        u_int8_t        scsi_intstatus;

        pACB = vpACB;

        scsi_status = trm_reg_read16(TRMREG_SCSI_STATUS);
        if (!(scsi_status & SCSIINTERRUPT)) {
                TRM_DPRINTF("trm_Interrupt: TRMREG_SCSI_STATUS scsi_status = NULL ,return......");
                return;
        }
        TRM_DPRINTF("scsi_status=%2x,",scsi_status);

        scsi_intstatus = trm_reg_read8(TRMREG_SCSI_INTSTATUS);

        TRM_DPRINTF("scsi_intstatus=%2x,",scsi_intstatus);

        if (scsi_intstatus & (INT_SELTIMEOUT | INT_DISCONNECT)) {
                trm_Disconnect(pACB);
                return;
        }

        if (scsi_intstatus & INT_RESELECTED) {
                trm_Reselect(pACB);
                return;
        }
        if (scsi_intstatus & INT_SCSIRESET) {
                trm_ScsiRstDetect(pACB);
                return;
        }

        if (scsi_intstatus & (INT_BUSSERVICE | INT_CMDDONE)) {
                pDCB = pACB->pActiveDCB;
                KASSERT(pDCB != NULL, ("no active DCB"));
                pSRB = pDCB->pActiveSRB;
                if (pDCB->DCBFlag & ABORT_DEV_)
                                trm_EnableMsgOutAbort1(pACB, pSRB);
                phase = (u_int16_t) pSRB->ScsiPhase;  /* phase: */
                stateV = trm_SCSI_phase0[phase];
                stateV(pACB, pSRB, &scsi_status);
                pSRB->ScsiPhase = scsi_status & PHASEMASK; 
                /* phase:0,1,2,3,4,5,6,7 */
                phase = (u_int16_t) scsi_status & PHASEMASK;       
                stateV = trm_SCSI_phase1[phase];
                stateV(pACB, pSRB, &scsi_status);  
        }
}

static void
trm_MsgOutPhase0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{

        if (pSRB->SRBState & (SRB_UNEXPECT_RESEL+SRB_ABORT_SENT))
                *pscsi_status = PH_BUS_FREE;
        /*.. initial phase*/
}

static void
trm_MsgOutPhase1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{
        u_int8_t        bval;
        u_int16_t       i, cnt;
        u_int8_t *      ptr;
        PDCB            pDCB;

        trm_reg_write16(DO_CLRFIFO, TRMREG_SCSI_CONTROL);
        pDCB = pACB->pActiveDCB;
        if (!(pSRB->SRBState & SRB_MSGOUT)) {
                cnt = pSRB->MsgCnt;
                if (cnt) {
                        ptr = (u_int8_t *) pSRB->MsgOutBuf;
                        for (i = 0; i < cnt; i++) {
                                trm_reg_write8(*ptr, TRMREG_SCSI_FIFO);
                                ptr++;
                        }
                        pSRB->MsgCnt = 0;
                        if ((pDCB->DCBFlag & ABORT_DEV_) &&
                            (pSRB->MsgOutBuf[0] == MSG_ABORT)) {
                                pSRB->SRBState = SRB_ABORT_SENT;
                        }
                } else {
                        bval = MSG_ABORT;       
                        if ((pSRB->CmdBlock[0] == INQUIRY) ||
                                        (pSRB->CmdBlock[0] == REQUEST_SENSE) ||
                                        (pSRB->SRBFlag & AUTO_REQSENSE)) {
                                if (pDCB->SyncMode & SYNC_NEGO_ENABLE) {
                                        goto  mop1;
                                }
                        }
                        trm_reg_write8(bval, TRMREG_SCSI_FIFO);
                }
        } else {
mop1:   /* message out phase */
                if (!(pSRB->SRBState & SRB_DO_WIDE_NEGO)
                    && (pDCB->SyncMode & WIDE_NEGO_ENABLE)) {
                  /*
                   * WIDE DATA TRANSFER REQUEST code (03h)
                   */
                        pDCB->SyncMode &= ~(SYNC_NEGO_DONE | EN_ATN_STOP);
                        trm_reg_write8((pDCB->IdentifyMsg & 0xBF),
                            TRMREG_SCSI_FIFO); 
                        trm_reg_write8(MSG_EXTENDED,TRMREG_SCSI_FIFO);
                        /* (01h) */
                        trm_reg_write8(2,TRMREG_SCSI_FIFO);     
                        /* Message length (02h) */
                        trm_reg_write8(3,TRMREG_SCSI_FIFO);
                        /* wide data xfer (03h) */
                        trm_reg_write8(1,TRMREG_SCSI_FIFO);
                        /* width:0(8bit),1(16bit),2(32bit) */
                        pSRB->SRBState |= SRB_DO_WIDE_NEGO; 
                } else if (!(pSRB->SRBState & SRB_DO_SYNC_NEGO) 
                    && (pDCB->SyncMode & SYNC_NEGO_ENABLE)) {
                  /*
                   * SYNCHRONOUS DATA TRANSFER REQUEST code (01h)
                   */
                        if (!(pDCB->SyncMode & WIDE_NEGO_DONE))
                                trm_reg_write8((pDCB->IdentifyMsg & 0xBF),
                                                TRMREG_SCSI_FIFO);
                        trm_reg_write8(MSG_EXTENDED,TRMREG_SCSI_FIFO);
                  /* (01h) */
                        trm_reg_write8(3,TRMREG_SCSI_FIFO); 
                  /* Message length (03h) */
                        trm_reg_write8(1,TRMREG_SCSI_FIFO);
                  /* SYNCHRONOUS DATA TRANSFER REQUEST code (01h) */
                        trm_reg_write8(pDCB->MaxNegoPeriod,TRMREG_SCSI_FIFO);
                  /* Transfer peeriod factor */
                        trm_reg_write8((pACB->AdaptType == 1) ? 31 : 15,
                            TRMREG_SCSI_FIFO);
                  /* REQ/ACK offset */
                        pSRB->SRBState |= SRB_DO_SYNC_NEGO;
                }
        }
        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
        /* it's important for atn stop */
        /*
         * SCSI cammand 
         */
        trm_reg_write8(SCMD_FIFO_OUT, TRMREG_SCSI_COMMAND);
}

static void 
trm_CommandPhase0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{

}

static void 
trm_CommandPhase1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{
        PDCB                    pDCB;
        u_int8_t *              ptr;
        u_int16_t               i, cnt;
        union  ccb              *pccb;
        struct ccb_scsiio       *pcsio;

        pccb  = pSRB->pccb;
        pcsio = &pccb->csio;

        trm_reg_write16(DO_CLRATN | DO_CLRFIFO , TRMREG_SCSI_CONTROL);
        if (!(pSRB->SRBFlag & AUTO_REQSENSE)) {
                cnt = (u_int16_t) pSRB->ScsiCmdLen;
                ptr = (u_int8_t *) pSRB->CmdBlock;
                for (i = 0; i < cnt; i++) {
                        trm_reg_write8(*ptr, TRMREG_SCSI_FIFO);
                        ptr++;
                }
        } else {
                trm_reg_write8(REQUEST_SENSE, TRMREG_SCSI_FIFO);
                pDCB = pACB->pActiveDCB;
                /* target id */
                trm_reg_write8((pDCB->IdentifyMsg << 5), TRMREG_SCSI_FIFO);
                trm_reg_write8(0, TRMREG_SCSI_FIFO);
                trm_reg_write8(0, TRMREG_SCSI_FIFO);
                /* sizeof(struct scsi_sense_data) */
                trm_reg_write8(pcsio->sense_len, TRMREG_SCSI_FIFO);
                trm_reg_write8(0, TRMREG_SCSI_FIFO);
        }
        pSRB->SRBState = SRB_COMMAND;
        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
        /* it's important for atn stop*/
        /*
         * SCSI cammand 
         */
        trm_reg_write8(SCMD_FIFO_OUT, TRMREG_SCSI_COMMAND);
}

static void
trm_DataOutPhase0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{
        PDCB            pDCB;
        u_int8_t        TempDMAstatus,SGIndexTemp;
        u_int16_t       scsi_status;
        PSEG            pseg;
        u_long          TempSRBXferredLength,dLeftCounter=0;

        pDCB = pSRB->pSRBDCB;
        scsi_status = *pscsi_status;

        if (!(pSRB->SRBState & SRB_XFERPAD)) {
                if (scsi_status & PARITYERROR)
                        pSRB->SRBStatus |= PARITY_ERROR;
                if (!(scsi_status & SCSIXFERDONE)) {
                  /*
                   * when data transfer from DMA FIFO to SCSI FIFO
                   * if there was some data left in SCSI FIFO
                   */
                        dLeftCounter = (u_long) 
                          (trm_reg_read8(TRMREG_SCSI_FIFOCNT) & 0x3F);
                        if (pDCB->SyncPeriod & WIDE_SYNC) {
                          /*
                           * if WIDE scsi SCSI FIFOCNT unit is word
                           * so need to * 2
                           */
                                dLeftCounter <<= 1;
                        }
                }
                /*
                 * caculate all the residue data that not yet tranfered
                 * SCSI transfer counter + left in SCSI FIFO data
                 *
                 * .....TRM_SCSI_COUNTER (24bits)
                 * The counter always decrement by one for every SCSI byte 
                 *transfer.
                 * .....TRM_SCSI_FIFOCNT (5bits)
                 * The counter is SCSI FIFO offset counter
                 */
                dLeftCounter += trm_reg_read32(TRMREG_SCSI_COUNTER);
                if (dLeftCounter == 1) {
                        dLeftCounter = 0;
                        trm_reg_write16(DO_CLRFIFO,TRMREG_SCSI_CONTROL);
                }
                if ((dLeftCounter == 0) || 
                    (scsi_status & SCSIXFERCNT_2_ZERO)) {   
                        TempDMAstatus = trm_reg_read8(TRMREG_DMA_STATUS);
                        while (!(TempDMAstatus & DMAXFERCOMP)) {
                                TempDMAstatus = 
                                  trm_reg_read8(TRMREG_DMA_STATUS);
                        }
                        pSRB->SRBTotalXferLength = 0;
                } else {
                  /* Update SG list             */
                  /*
                   * if transfer not yet complete
                   * there were some data residue in SCSI FIFO or
                   * SCSI transfer counter not empty
                   */
                        if (pSRB->SRBTotalXferLength != dLeftCounter) {
                          /*
                           * data that had transferred length
                           */
                                TempSRBXferredLength = 
                                  pSRB->SRBTotalXferLength - dLeftCounter;
                                /*
                                 * next time to be transferred length
                                 */
                                pSRB->SRBTotalXferLength = dLeftCounter;
                                /*
                                 * parsing from last time disconnect SRBSGIndex
                                 */
                                pseg = 
                                  pSRB->pSRBSGL + pSRB->SRBSGIndex;
                                for (SGIndexTemp = pSRB->SRBSGIndex;
                                    SGIndexTemp < pSRB->SRBSGCount; 
                                    SGIndexTemp++) {
                                        /* 
                                         * find last time which SG transfer be 
                                         * disconnect 
                                         */
                                        if (TempSRBXferredLength >= 
                                            pseg->length) 
                                                TempSRBXferredLength -= 
                                                  pseg->length;
                                        else {
                                          /*
                                           * update last time disconnected SG 
                                           * list
                                           */
                                                pseg->length -= 
                                                  TempSRBXferredLength; 
                                                /* residue data length  */
                                                pseg->address += 
                                                  TempSRBXferredLength;
                                                /* residue data pointer */
                                                pSRB->SRBSGIndex = SGIndexTemp;
                                                break;
                                        }
                                        pseg++;
                                }
                        }
                }
        }
        trm_reg_write8(STOPDMAXFER ,TRMREG_DMA_CONTROL);
}


static void
trm_DataOutPhase1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{
        u_int16_t       ioDir;
        /*
         * do prepare befor transfer when data out phase
         */

        ioDir = XFERDATAOUT;
        trm_DataIO_transfer(pACB, pSRB, ioDir);
}

static void 
trm_DataInPhase0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{
        u_int8_t        TempDMAstatus, SGIndexTemp;
        u_int16_t       scsi_status;
        PSEG            pseg;
        u_long          TempSRBXferredLength,dLeftCounter = 0;

        scsi_status = *pscsi_status;
        if (!(pSRB->SRBState & SRB_XFERPAD)) {
                if (scsi_status & PARITYERROR)
                        pSRB->SRBStatus |= PARITY_ERROR;
                dLeftCounter += trm_reg_read32(TRMREG_SCSI_COUNTER);
                if ((dLeftCounter == 0) || (scsi_status & SCSIXFERCNT_2_ZERO)) {
                        TempDMAstatus = trm_reg_read8(TRMREG_DMA_STATUS);
                        while (!(TempDMAstatus & DMAXFERCOMP))
                                TempDMAstatus = trm_reg_read8(TRMREG_DMA_STATUS);
                        pSRB->SRBTotalXferLength = 0;
                } else {  
                  /*
                   * parsing the case:
                   * when a transfer not yet complete 
                   * but be disconnected by uper layer
                   * if transfer not yet complete
                   * there were some data residue in SCSI FIFO or
                   * SCSI transfer counter not empty
                   */
                  if (pSRB->SRBTotalXferLength != dLeftCounter) {
                                /*
                                 * data that had transferred length
                                 */
                        TempSRBXferredLength = 
                          pSRB->SRBTotalXferLength - dLeftCounter;
                                /*
                                 * next time to be transferred length
                                 */
                        pSRB->SRBTotalXferLength = dLeftCounter;
                                /*
                                 * parsing from last time disconnect SRBSGIndex
                                 */
                        pseg = pSRB->pSRBSGL + pSRB->SRBSGIndex;
                        for (SGIndexTemp = pSRB->SRBSGIndex; 
                            SGIndexTemp < pSRB->SRBSGCount;
                            SGIndexTemp++) {
                          /* 
                           * find last time which SG transfer be disconnect 
                           */
                                if (TempSRBXferredLength >= pseg->length)
                                        TempSRBXferredLength -= pseg->length;
                                else {
                                  /*
                                   * update last time disconnected SG list
                                   */
                                        pseg->length -= TempSRBXferredLength;
                                        /* residue data length  */
                                        pseg->address += TempSRBXferredLength;
                                        /* residue data pointer */
                                        pSRB->SRBSGIndex = SGIndexTemp;
                                        break;
                                } 
                                pseg++;
                        }
                  }
                }
        }
}

static void
trm_DataInPhase1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{
        u_int16_t       ioDir;
        /*
         * do prepare befor transfer when data in phase
         */
        
        ioDir = XFERDATAIN;
        trm_DataIO_transfer(pACB, pSRB, ioDir);
}

static void
trm_DataIO_transfer(PACB pACB, PSRB pSRB, u_int16_t ioDir)
{
        u_int8_t        bval;
        PDCB            pDCB;

        pDCB = pSRB->pSRBDCB;
        if (pSRB->SRBSGIndex < pSRB->SRBSGCount) {
                if (pSRB->SRBTotalXferLength != 0) {
                        /* 
                         * load what physical address of Scatter/Gather list 
                         table want to be transfer
                         */
                        TRM_DPRINTF(" SG->address=%8x \n",pSRB->pSRBSGL->address);
                        TRM_DPRINTF(" SG->length=%8x \n",pSRB->pSRBSGL->length);
                        TRM_DPRINTF(" pDCB->SyncPeriod=%x \n",pDCB->SyncPeriod);
                        TRM_DPRINTF(" pSRB->pSRBSGL=%8x \n",(unsigned int)pSRB->pSRBSGL);
                        TRM_DPRINTF(" pSRB->SRBSGPhyAddr=%8x \n",pSRB->SRBSGPhyAddr);
                        TRM_DPRINTF(" pSRB->SRBSGIndex=%d \n",pSRB->SRBSGIndex);
                        TRM_DPRINTF(" pSRB->SRBSGCount=%d \n",pSRB->SRBSGCount);
                        TRM_DPRINTF(" pSRB->SRBTotalXferLength=%d \n",pSRB->SRBTotalXferLength);

                        pSRB->SRBState = SRB_DATA_XFER;
                        trm_reg_write32(0, TRMREG_DMA_XHIGHADDR);
                        trm_reg_write32(
                            (pSRB->SRBSGPhyAddr + 
                             ((u_long)pSRB->SRBSGIndex << 3)),
                            TRMREG_DMA_XLOWADDR);
                        /*
                         * load how many bytes in the Scatter/Gather 
                         * list table 
                         */
                        trm_reg_write32(
                            ((u_long)(pSRB->SRBSGCount - pSRB->SRBSGIndex) << 3),
                            TRMREG_DMA_XCNT);                   
                        /*
                         * load total transfer length (24bits) max value
                         * 16Mbyte 
                         */
                        trm_reg_write32(pSRB->SRBTotalXferLength,
                            TRMREG_SCSI_COUNTER);
                        /* Start DMA transfer */
                        trm_reg_write16(ioDir, TRMREG_DMA_COMMAND);
                        /* Start SCSI transfer */
                        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                        /* it's important for atn stop */
                        /*
                         * SCSI cammand 
                         */
                        bval = (ioDir == XFERDATAOUT) ?
                          SCMD_DMA_OUT : SCMD_DMA_IN;
                        trm_reg_write8(bval, TRMREG_SCSI_COMMAND);
                } else {
                  /* xfer pad */
                        if (pSRB->SRBSGCount) {
                                pSRB->AdaptStatus = H_OVER_UNDER_RUN;
                                pSRB->SRBStatus |= OVER_RUN;
                        }
                        if (pDCB->SyncPeriod & WIDE_SYNC)
                                trm_reg_write32(2,TRMREG_SCSI_COUNTER);
                        else
                                trm_reg_write32(1,TRMREG_SCSI_COUNTER);
                        if (ioDir == XFERDATAOUT)
                                trm_reg_write16(0, TRMREG_SCSI_FIFO);
                        else
                                trm_reg_read16(TRMREG_SCSI_FIFO);
                        pSRB->SRBState |= SRB_XFERPAD;
                        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                        /* it's important for atn stop */
                        /*
                         * SCSI cammand 
                         */
                        bval = (ioDir == XFERDATAOUT) ? 
                          SCMD_FIFO_OUT : SCMD_FIFO_IN;
                        trm_reg_write8(bval, TRMREG_SCSI_COMMAND);
                }
        }
}

static void
trm_StatusPhase0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{

        pSRB->TargetStatus = trm_reg_read8(TRMREG_SCSI_FIFO);
        pSRB->SRBState = SRB_COMPLETED;
        *pscsi_status = PH_BUS_FREE;  
        /*.. initial phase*/
        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
        /* it's important for atn stop */
        /*
         * SCSI cammand 
         */
        trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
}



static void
trm_StatusPhase1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{

        if (trm_reg_read16(TRMREG_DMA_COMMAND) & 0x0001) {
                if (!(trm_reg_read8(TRMREG_SCSI_FIFOCNT) & 0x40))
                        trm_reg_write16(DO_CLRFIFO, TRMREG_SCSI_CONTROL);
                if (!(trm_reg_read16(TRMREG_DMA_FIFOCNT) & 0x8000))
                        trm_reg_write8(CLRXFIFO, TRMREG_DMA_CONTROL);
        } else {
                if (!(trm_reg_read16(TRMREG_DMA_FIFOCNT) & 0x8000))
                        trm_reg_write8(CLRXFIFO, TRMREG_DMA_CONTROL);
                if (!(trm_reg_read8(TRMREG_SCSI_FIFOCNT) & 0x40))
                        trm_reg_write16(DO_CLRFIFO, TRMREG_SCSI_CONTROL);
        }
        pSRB->SRBState = SRB_STATUS;
        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
        /* it's important for atn stop */
        /*
         * SCSI cammand 
         */
        trm_reg_write8(SCMD_COMP, TRMREG_SCSI_COMMAND);
}

/*
 *scsiiom                
 *       trm_MsgInPhase0: one of trm_SCSI_phase0[] vectors
 *            stateV = (void *) trm_SCSI_phase0[phase]
 *                         if phase =7    
 * extended message codes:
 *
 *   code        description
 *
 *    02h        Reserved
 *    00h        MODIFY DATA  POINTER
 *    01h        SYNCHRONOUS DATA TRANSFER REQUEST
 *    03h        WIDE DATA TRANSFER REQUEST
 * 04h - 7Fh     Reserved
 * 80h - FFh     Vendor specific  
 *                      
 */

static void
trm_MsgInPhase0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{
        u_int8_t        message_in_code,bIndex,message_in_tag_id;
        PDCB            pDCB;
        PSRB            pSRBTemp;

        pDCB = pACB->pActiveDCB;

        message_in_code = trm_reg_read8(TRMREG_SCSI_FIFO);
        if (!(pSRB->SRBState & SRB_EXTEND_MSGIN)) {
                if (message_in_code == MSG_DISCONNECT) {
                        pSRB->SRBState = SRB_DISCONNECT;
                        *pscsi_status = PH_BUS_FREE; /* .. initial phase */
                        /* it's important for atn stop */
                        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                        /*
                         * SCSI command
                         */
                        trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
                        return;
                } else if (message_in_code == MSG_SAVE_PTR) {
                        *pscsi_status = PH_BUS_FREE; /* .. initial phase */
                        /* it's important for atn stop */
                        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                        /*
                         * SCSI command
                         */
                        trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
                        return;
                } else if ((message_in_code == MSG_EXTENDED) ||
                    ((message_in_code >= MSG_SIMPLE_QTAG) &&
                     (message_in_code <= MSG_ORDER_QTAG))) {
                        pSRB->SRBState |= SRB_EXTEND_MSGIN;
                        pSRB->MsgInBuf[0] = message_in_code;
                        /* extended message      (01h) */
                        pSRB->MsgCnt = 1;
                        pSRB->pMsgPtr = &pSRB->MsgInBuf[1];
                        /* extended message length (n) */
                        *pscsi_status = PH_BUS_FREE; /* .. initial phase */
                        /* it's important for atn stop */
                        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                        /*
                         * SCSI command
                         */
                        trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
                        return;
                } else if (message_in_code == MSG_REJECT_) {
                        /* Reject message */
                        if (pDCB->SyncMode & WIDE_NEGO_ENABLE) {
                          /* do wide nego reject */
                                pDCB = pSRB->pSRBDCB;
                                pDCB->SyncMode |= WIDE_NEGO_DONE;
                                pDCB->SyncMode &= ~(SYNC_NEGO_DONE | 
                                    EN_ATN_STOP | WIDE_NEGO_ENABLE);
                                pSRB->SRBState &= ~(SRB_DO_WIDE_NEGO+SRB_MSGIN);
                                if ((pDCB->SyncMode & SYNC_NEGO_ENABLE) 
                                    && !(pDCB->SyncMode & SYNC_NEGO_DONE)) {   
                                  /* Set ATN, in case ATN was clear */
                                        pSRB->SRBState |= SRB_MSGOUT;
                                        trm_reg_write16(
                                            DO_SETATN,
                                            TRMREG_SCSI_CONTROL);
                                } else {   
                                  /* Clear ATN */
                                        trm_reg_write16(
                                            DO_CLRATN,
                                            TRMREG_SCSI_CONTROL);
                                }
                        } else if (pDCB->SyncMode & SYNC_NEGO_ENABLE) { 
                          /* do sync nego reject */
                                trm_reg_write16(DO_CLRATN,TRMREG_SCSI_CONTROL);
                                if (pSRB->SRBState & SRB_DO_SYNC_NEGO) {
                                        pDCB = pSRB->pSRBDCB;
                                        pDCB->SyncMode &= 
                                          ~(SYNC_NEGO_ENABLE+SYNC_NEGO_DONE); 
                                        pDCB->SyncPeriod = 0;
                                        pDCB->SyncOffset = 0;
                                        /*
                                         *
                                         *   program SCSI control register
                                         *
                                         */
                                        trm_reg_write8(pDCB->SyncPeriod,
                                            TRMREG_SCSI_SYNC);
                                        trm_reg_write8(pDCB->SyncOffset,
                                            TRMREG_SCSI_OFFSET);
                                        trm_SetXferRate(pACB,pSRB,pDCB);
                                }
                        }
                        *pscsi_status = PH_BUS_FREE; /* .. initial phase */
                        /* it's important for atn stop */
                        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                        /*
                         * SCSI command
                         */
                        trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
                        return;
                } else if (message_in_code == MSG_IGNOREWIDE) {
                        trm_reg_write32(1, TRMREG_SCSI_COUNTER);
                        trm_reg_read8(TRMREG_SCSI_FIFO);
                        *pscsi_status = PH_BUS_FREE; /* .. initial phase */
                        /* it's important for atn stop */
                        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                        /*
                         * SCSI command
                         */
                        trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
                        return;
                } else {
                  /* Restore data pointer message */
                  /* Save data pointer message    */
                  /* Completion message           */
                  /* NOP message                  */
                        *pscsi_status = PH_BUS_FREE; /* .. initial phase */
                        /* it's important for atn stop */
                        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                        /*
                         * SCSI command
                         */
                        trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
                        return;
                }
        } else {        
          /* 
           * Parsing incomming extented messages 
           */
                *pSRB->pMsgPtr = message_in_code;
                pSRB->MsgCnt++;
                pSRB->pMsgPtr++;
                TRM_DPRINTF("pSRB->MsgInBuf[0]=%2x \n ",pSRB->MsgInBuf[0]);
                TRM_DPRINTF("pSRB->MsgInBuf[1]=%2x \n ",pSRB->MsgInBuf[1]);
                TRM_DPRINTF("pSRB->MsgInBuf[2]=%2x \n ",pSRB->MsgInBuf[2]);
                TRM_DPRINTF("pSRB->MsgInBuf[3]=%2x \n ",pSRB->MsgInBuf[3]);
                TRM_DPRINTF("pSRB->MsgInBuf[4]=%2x \n ",pSRB->MsgInBuf[4]);
                if ((pSRB->MsgInBuf[0] >= MSG_SIMPLE_QTAG)
                    && (pSRB->MsgInBuf[0] <= MSG_ORDER_QTAG)) {
                  /*
                   * is QUEUE tag message :
                   *
                   * byte 0:
                   * HEAD    QUEUE TAG (20h)
                   * ORDERED QUEUE TAG (21h)
                   * SIMPLE  QUEUE TAG (22h)
                   * byte 1:
                   * Queue tag (00h - FFh)
                   */
                        if (pSRB->MsgCnt == 2) {
                                pSRB->SRBState = 0;
                                message_in_tag_id = pSRB->MsgInBuf[1];
                                pSRB = pDCB->pGoingSRB;
                                pSRBTemp = pDCB->pGoingLastSRB;
                                if (pSRB) {
                                        for (;;) {
                                                if (pSRB->TagNumber != 
                                                    message_in_tag_id) {
                                                        if (pSRB == pSRBTemp) {
                                                                goto  mingx0;
                                                        }
                                                        pSRB = pSRB->pNextSRB;
                                                } else
                                                        break;
                                        }
                                        if (pDCB->DCBFlag & ABORT_DEV_) {
                                                pSRB->SRBState = SRB_ABORT_SENT;
                                                trm_EnableMsgOutAbort1(
                                                    pACB, pSRB);
                                        }
                                        if (!(pSRB->SRBState & SRB_DISCONNECT)) {
                                                TRM_DPRINTF("SRB not yet disconnect........ \n ");
                                                goto  mingx0;
                                        }
                                        pDCB->pActiveSRB = pSRB;
                                        pSRB->SRBState = SRB_DATA_XFER;
                                } else {
mingx0:
                                        pSRB = &pACB->TmpSRB;
                                        pSRB->SRBState = SRB_UNEXPECT_RESEL;
                                        pDCB->pActiveSRB = pSRB;
                                        pSRB->MsgOutBuf[0] = MSG_ABORT_TAG;
                                        trm_EnableMsgOutAbort2(
                                            pACB,
                                            pSRB);
                                }
                        }
                        *pscsi_status = PH_BUS_FREE;
                        /* .. initial phase */
                        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                        /* it's important for atn stop */
                        /*
                         * SCSI command
                         */
                        trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
                        return;
                } else if ((pSRB->MsgInBuf[0] == MSG_EXTENDED) &&
                    (pSRB->MsgInBuf[2] == 3) && (pSRB->MsgCnt == 4)) {
                  /*
                   * is Wide data xfer Extended message :
                   * ======================================
                   * WIDE DATA TRANSFER REQUEST
                   * ======================================
                   * byte 0 :  Extended message (01h)
                   * byte 1 :  Extended message length (02h)
                   * byte 2 :  WIDE DATA TRANSFER code (03h)
                   * byte 3 :  Transfer width exponent 
                   */
                        pDCB = pSRB->pSRBDCB;
                        pSRB->SRBState &= ~(SRB_EXTEND_MSGIN+SRB_DO_WIDE_NEGO);
                        if ((pSRB->MsgInBuf[1] != 2)) {
                          /* Length is wrong, reject it  */
                                pDCB->SyncMode &=
                                  ~(WIDE_NEGO_ENABLE+WIDE_NEGO_DONE); 
                                pSRB->MsgCnt = 1;
                                pSRB->MsgInBuf[0] = MSG_REJECT_;
                                trm_reg_write16(DO_SETATN, TRMREG_SCSI_CONTROL);
                                *pscsi_status = PH_BUS_FREE; /* .. initial phase */
                                /* it's important for atn stop */
                                trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                                /*
                                 * SCSI command
                                 */
                                trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
                                return;
                        }
                        if (pDCB->SyncMode & WIDE_NEGO_ENABLE) {                
                          /* Do wide negoniation */
                                if (pSRB->MsgInBuf[3] > 2) {
                                  /* > 32 bit   */
                                  /* reject_msg: */
                                        pDCB->SyncMode &= 
                                          ~(WIDE_NEGO_ENABLE+WIDE_NEGO_DONE); 
                                        pSRB->MsgCnt = 1;
                                        pSRB->MsgInBuf[0] = MSG_REJECT_;
                                        trm_reg_write16(DO_SETATN,
                                            TRMREG_SCSI_CONTROL);
                                        *pscsi_status = PH_BUS_FREE; /* .. initial phase */
                                        /* it's important for atn stop */
                                        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                                        /*
                                         * SCSI command
                                         */
                                        trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
                                        return;
                                }
                                if (pSRB->MsgInBuf[3] == 2) {
                                        pSRB->MsgInBuf[3] = 1;
                                        /* do 16 bits   */
                                } else {
                                        if (!(pDCB->SyncMode 
                                              & WIDE_NEGO_DONE)) {
                                                pSRB->SRBState &=
                                                  ~(SRB_DO_WIDE_NEGO+SRB_MSGIN);
                                                pDCB->SyncMode |= 
                                                  WIDE_NEGO_DONE;
                                                pDCB->SyncMode &=
                                                  ~(SYNC_NEGO_DONE |
                                                      EN_ATN_STOP |
                                                      WIDE_NEGO_ENABLE);
                                                if (pSRB->MsgInBuf[3] != 0) {
                                                  /* is Wide data xfer */
                                                        pDCB->SyncPeriod |=
                                                          WIDE_SYNC;
                                                        pDCB->tinfo.current.width 
                                                          = MSG_EXT_WDTR_BUS_16_BIT;
                                                        pDCB->tinfo.goal.width
                                                          = MSG_EXT_WDTR_BUS_16_BIT;
                                                }
                                        }
                                }
                        } else
                                pSRB->MsgInBuf[3] = 0;
                        pSRB->SRBState |= SRB_MSGOUT;
                        trm_reg_write16(DO_SETATN,TRMREG_SCSI_CONTROL);
                        *pscsi_status = PH_BUS_FREE; /* .. initial phase */
                        /* it's important for atn stop */
                        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                        /*
                         * SCSI command
                         */
                        trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
                        return;
                } else if ((pSRB->MsgInBuf[0] == MSG_EXTENDED) &&
                    (pSRB->MsgInBuf[2] == 1) && (pSRB->MsgCnt == 5)) {
                        /*
                         * is 8bit transfer Extended message :
                         * =================================
                         * SYNCHRONOUS DATA TRANSFER REQUEST
                         * =================================
                         * byte 0 :  Extended message (01h)
                         * byte 1 :  Extended message length (03)
                         * byte 2 :  SYNCHRONOUS DATA TRANSFER code (01h)
                         * byte 3 :  Transfer period factor 
                         * byte 4 :  REQ/ACK offset  
                         */
                        pSRB->SRBState &= ~(SRB_EXTEND_MSGIN+SRB_DO_SYNC_NEGO);
                        if ((pSRB->MsgInBuf[1] != 3) ||
                            (pSRB->MsgInBuf[2] != 1)) {
                          /* reject_msg: */
                                pSRB->MsgCnt = 1;
                                pSRB->MsgInBuf[0] = MSG_REJECT_;
                                trm_reg_write16(DO_SETATN, TRMREG_SCSI_CONTROL);
                                *pscsi_status = PH_BUS_FREE;
                                /* .. initial phase */
                                trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                                /* it's important for atn stop */
                                /*
                                 * SCSI cammand
                                 */
                                trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
                                return;
                        } else if (!(pSRB->MsgInBuf[3]) || !(pSRB->MsgInBuf[4])) {
                                /* set async */
                                pDCB = pSRB->pSRBDCB;
                                /* disable sync & sync nego */
                                pDCB->SyncMode &= 
                                  ~(SYNC_NEGO_ENABLE+SYNC_NEGO_DONE);
                                pDCB->SyncPeriod = 0;
                                pDCB->SyncOffset = 0;
                                pDCB->tinfo.goal.period = 0;
                                pDCB->tinfo.goal.offset = 0;
                                pDCB->tinfo.current.period = 0;
                                pDCB->tinfo.current.offset = 0;
                                pDCB->tinfo.current.width = 
                                  MSG_EXT_WDTR_BUS_8_BIT;
                                /*
                                 *
                                 *   program SCSI control register
                                 *
                                 */
                                trm_reg_write8(pDCB->SyncPeriod,TRMREG_SCSI_SYNC);
                                trm_reg_write8(pDCB->SyncOffset,TRMREG_SCSI_OFFSET);
                                trm_SetXferRate(pACB,pSRB,pDCB);
                                *pscsi_status = PH_BUS_FREE;
                                /* .. initial phase */
                                trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
                                /* it's important for atn stop */
                                /*
                                 * SCSI cammand
                                 */
                                trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
                                return;
                        } else {
                                /* set sync */
                                pDCB = pSRB->pSRBDCB;
                                pDCB->SyncMode |= 
                                  SYNC_NEGO_ENABLE+SYNC_NEGO_DONE;
                                pDCB->MaxNegoPeriod = pSRB->MsgInBuf[3];
                                /* Transfer period factor */
                                pDCB->SyncOffset = pSRB->MsgInBuf[4]; 
                                /* REQ/ACK offset */
                                if (pACB->AdaptType == 1) {
                                        for(bIndex = 0; bIndex < 7; bIndex++) {
                                                if (pSRB->MsgInBuf[3] <=
                                           dc395u2x_clock_period[bIndex]) {
                                            pDCB->tinfo.goal.period =
                                                dc395u2x_tinfo_period[bIndex];
                                            pDCB->tinfo.current.period =
                                                dc395u2x_tinfo_period[bIndex];
                                        pDCB->tinfo.goal.offset =
                                            pDCB->SyncOffset;
                                        pDCB->tinfo.current.offset =
                                            pDCB->SyncOffset;
                                        pDCB->SyncPeriod |= (bIndex|LVDS_SYNC);
                                        break;
                                                }
                                        }
                                } else {
                                        for(bIndex = 0; bIndex < 7; bIndex++) {
                                                if (pSRB->MsgInBuf[3] <=
                                                 dc395x_clock_period[bIndex]) {
                                                   pDCB->tinfo.goal.period =
                                                dc395x_tinfo_period[bIndex];
                                                   pDCB->tinfo.current.period =
                                                dc395x_tinfo_period[bIndex];
                                                   pDCB->tinfo.goal.offset =
                                                pDCB->SyncOffset;
                                                   pDCB->tinfo.current.offset =
                                                       pDCB->SyncOffset;
                                                   pDCB->SyncPeriod |=
                                                       (bIndex|ALT_SYNC);
                                                   break;
                                                }
                                        }
                                }
                                /*               
                                 *
                                 *   program SCSI control register
                                 *
                                 */
                                trm_reg_write8(pDCB->SyncPeriod,
                                    TRMREG_SCSI_SYNC);
                                trm_reg_write8(pDCB->SyncOffset,
                                    TRMREG_SCSI_OFFSET);
                                trm_SetXferRate(pACB,pSRB,pDCB);
                                *pscsi_status=PH_BUS_FREE;/*.. initial phase*/
                                trm_reg_write16(DO_DATALATCH,TRMREG_SCSI_CONTROL);/* it's important for atn stop*/
                    /*
                     * SCSI command
                     */
                                trm_reg_write8(SCMD_MSGACCEPT,TRMREG_SCSI_COMMAND);
                                return;
                        }
                }
        *pscsi_status = PH_BUS_FREE;
        /* .. initial phase */
        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
        /* it's important for atn stop */
        /*
         * SCSI cammand 
         */
        trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
        }
}

static void
trm_MsgInPhase1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{

        trm_reg_write16(DO_CLRFIFO, TRMREG_SCSI_CONTROL);
        trm_reg_write32(1,TRMREG_SCSI_COUNTER);
        if (!(pSRB->SRBState & SRB_MSGIN)) {
                pSRB->SRBState &= SRB_DISCONNECT;
                pSRB->SRBState |= SRB_MSGIN;
        }
        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
        /* it's important for atn stop*/
        /*
         * SCSI cammand 
         */
        trm_reg_write8(SCMD_FIFO_IN, TRMREG_SCSI_COMMAND);
}

static void
trm_Nop0(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{

}

static void
trm_Nop1(PACB pACB, PSRB pSRB, u_int16_t *pscsi_status)
{

}

static void
trm_SetXferRate(PACB pACB,PSRB pSRB, PDCB pDCB)
{
        union ccb       *pccb;
        struct ccb_trans_settings *neg;
        u_int16_t       cnt, i;
        u_int8_t        bval;
        PDCB            pDCBTemp;

        /*
         * set all lun device's  period , offset
         */
        TRM_DPRINTF("trm_SetXferRate\n");
        pccb = pSRB->pccb;

        neg = &xpt_alloc_ccb()->cts;

        neg->xport_specific.spi.sync_period = pDCB->tinfo.goal.period;
        neg->xport_specific.spi.sync_offset = pDCB->tinfo.goal.offset;
        neg->xport_specific.spi.valid =
            CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET;
        xpt_setup_ccb(&neg->ccb_h, pccb->ccb_h.path, /* priority */1);
        xpt_async(AC_TRANSFER_NEG, pccb->ccb_h.path, neg);
        xpt_free_ccb(&neg->ccb_h);

        if (!(pDCB->IdentifyMsg & 0x07)) {
                pDCBTemp = pACB->pLinkDCB;
                cnt = pACB->DeviceCnt;
                bval = pDCB->TargetID;
                for (i = 0; i < cnt; i++) {
                        if (pDCBTemp->TargetID == bval) {
                                pDCBTemp->SyncPeriod = pDCB->SyncPeriod;
                                pDCBTemp->SyncOffset = pDCB->SyncOffset;
                                pDCBTemp->SyncMode = pDCB->SyncMode;
                        }
                        pDCBTemp = pDCBTemp->pNextDCB;
                }
        }
}

/*
 * scsiiom              
 *            trm_Interrupt       
 *                      
 *
 *    ---SCSI bus phase
 *
 *      PH_DATA_OUT             0x00     Data out phase               
 *      PH_DATA_IN              0x01     Data in phase              
 *      PH_COMMAND              0x02     Command phase   
 *      PH_STATUS               0x03     Status phase
 *      PH_BUS_FREE             0x04     Invalid phase used as bus free 
 *      PH_BUS_FREE             0x05     Invalid phase used as bus free 
 *      PH_MSG_OUT              0x06     Message out phase
 *      PH_MSG_IN               0x07     Message in phase
 *
 */
static void 
trm_Disconnect(PACB pACB)
{
        PDCB            pDCB;
        PSRB            pSRB, psrb;
        u_int16_t       i,j, cnt;
        u_int           target_id,target_lun;
        
        TRM_DPRINTF("trm_Disconnect...............\n ");
        
        pDCB = pACB->pActiveDCB;
        if (!pDCB) {
                TRM_DPRINTF(" Exception Disconnect DCB=NULL..............\n ");
                j = 400;
                while (--j) 
                        DELAY(1);
                /* 1 msec */
                trm_reg_write16((DO_CLRFIFO | DO_HWRESELECT),
                    TRMREG_SCSI_CONTROL);
                crit_exit();
                return;
        }
        pSRB = pDCB->pActiveSRB; 
        /* bug pSRB=0 */
        target_id  = pSRB->pccb->ccb_h.target_id;
        target_lun = pSRB->pccb->ccb_h.target_lun;
        TRM_DPRINTF(":pDCB->pActiveSRB= %8x \n ",(u_int) pDCB->pActiveSRB);
        pACB->pActiveDCB = NULL;
        pSRB->ScsiPhase = PH_BUS_FREE; 
        /* SCSI bus free Phase */
        trm_reg_write16((DO_CLRFIFO | DO_HWRESELECT), TRMREG_SCSI_CONTROL);
        if (pSRB->SRBState & SRB_UNEXPECT_RESEL) {
                pSRB->SRBState = 0;
                trm_DoWaitingSRB(pACB);
        } else if (pSRB->SRBState & SRB_ABORT_SENT) {
                pDCB->DCBFlag = 0;
                cnt = pDCB->GoingSRBCnt;
                pDCB->GoingSRBCnt = 0;
                pSRB = pDCB->pGoingSRB;
                for (i = 0; i < cnt; i++) {
                        psrb = pSRB->pNextSRB;
                        pSRB->pNextSRB = pACB->pFreeSRB;
                        pACB->pFreeSRB = pSRB;
                        pSRB = psrb;
                }
                pDCB->pGoingSRB = NULL;
                trm_DoWaitingSRB(pACB);
        } else {
                if ((pSRB->SRBState & (SRB_START_+SRB_MSGOUT)) || 
                    !(pSRB->SRBState & (SRB_DISCONNECT+SRB_COMPLETED))) {
                  /* Selection time out */
                        if (!(pACB->scan_devices[target_id][target_lun]) &&
                            pSRB->CmdBlock[0] != 0x00 && /* TEST UNIT READY */
                            pSRB->CmdBlock[0] != INQUIRY) {
                                pSRB->SRBState = SRB_READY;
                                trm_RewaitSRB(pDCB, pSRB);
                        } else {
                                pSRB->TargetStatus = SCSI_STAT_SEL_TIMEOUT;
                                goto  disc1;
                        }
                } else if (pSRB->SRBState & SRB_DISCONNECT) {
                        /*
                         * SRB_DISCONNECT
                         */
                        trm_DoWaitingSRB(pACB);
                } else if (pSRB->SRBState & SRB_COMPLETED) {
disc1:
                  /*
                   * SRB_COMPLETED
                   */
                        pDCB->pActiveSRB = NULL;
                        pSRB->SRBState = SRB_FREE;
                        trm_SRBdone(pACB, pDCB, pSRB);
                }
        }
        return;
}

static void
trm_Reselect(PACB pACB)
{
        PDCB            pDCB;
        PSRB            pSRB;
        u_int16_t       RselTarLunId;

        TRM_DPRINTF("trm_Reselect................. \n");
        pDCB = pACB->pActiveDCB;
        if (pDCB) {
          /* Arbitration lost but Reselection win */
                pSRB = pDCB->pActiveSRB;
                pSRB->SRBState = SRB_READY;
                trm_RewaitSRB(pDCB, pSRB);
        }
        /* Read Reselected Target Id and LUN */
        RselTarLunId = trm_reg_read16(TRMREG_SCSI_TARGETID) & 0x1FFF;
        pDCB = pACB->pLinkDCB;
        while (RselTarLunId != *((u_int16_t *) &pDCB->TargetID)) {
          /* get pDCB of the reselect id */
                pDCB = pDCB->pNextDCB;
        }

        pACB->pActiveDCB = pDCB;
        if (pDCB->SyncMode & EN_TAG_QUEUING) {
                pSRB = &pACB->TmpSRB;
                pDCB->pActiveSRB = pSRB;
        } else {
                pSRB = pDCB->pActiveSRB;
                if (!pSRB || !(pSRB->SRBState & SRB_DISCONNECT)) {
                  /*
                   * abort command
                   */
                        pSRB = &pACB->TmpSRB;
                        pSRB->SRBState = SRB_UNEXPECT_RESEL;
                        pDCB->pActiveSRB = pSRB;
                        trm_EnableMsgOutAbort1(pACB, pSRB);
                } else {
                        if (pDCB->DCBFlag & ABORT_DEV_) {
                                pSRB->SRBState = SRB_ABORT_SENT;
                                trm_EnableMsgOutAbort1(pACB, pSRB);
                        } else 
                                pSRB->SRBState = SRB_DATA_XFER;
                }
        }
        pSRB->ScsiPhase = PH_BUS_FREE;
        /* SCSI bus free Phase */
        /* 
         * Program HA ID, target ID, period and offset
         */
        trm_reg_write8((u_int8_t) RselTarLunId,TRMREG_SCSI_TARGETID);
        /* target ID */
        trm_reg_write8(pACB->AdaptSCSIID,TRMREG_SCSI_HOSTID);
        /* host   ID */
        trm_reg_write8(pDCB->SyncPeriod,TRMREG_SCSI_SYNC);
        /* period    */
        trm_reg_write8(pDCB->SyncOffset,TRMREG_SCSI_OFFSET); 
        /* offset    */
        trm_reg_write16(DO_DATALATCH, TRMREG_SCSI_CONTROL);
        /* it's important for atn stop*/
        /*
         * SCSI cammand 
         */
        trm_reg_write8(SCMD_MSGACCEPT, TRMREG_SCSI_COMMAND);
        /* to rls the /ACK signal */
}

static void
trm_SRBdone(PACB pACB, PDCB pDCB, PSRB pSRB)
{
        PSRB                    psrb;
        u_int8_t                bval, bval1,status;
        union ccb               *pccb;
        struct ccb_scsiio       *pcsio;
        PSCSI_INQDATA           ptr;
        u_int                   target_id,target_lun;
        PDCB                    pTempDCB;

        pccb  = pSRB->pccb;
        if (pccb == NULL)
                return;
        pcsio = &pccb->csio;
        target_id  = pSRB->pccb->ccb_h.target_id;
        target_lun = pSRB->pccb->ccb_h.target_lun;
        if ((pccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
                bus_dmasync_op_t op;
                if ((pccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
                        op = BUS_DMASYNC_POSTREAD;
                else
                        op = BUS_DMASYNC_POSTWRITE;
                bus_dmamap_sync(pACB->buffer_dmat, pSRB->dmamap, op);
                bus_dmamap_unload(pACB->buffer_dmat, pSRB->dmamap);
        }
        /*
         *
         * target status
         *
         */
        status = pSRB->TargetStatus;
        pcsio->scsi_status=SCSI_STAT_GOOD;
        pccb->ccb_h.status = CAM_REQ_CMP;
        if (pSRB->SRBFlag & AUTO_REQSENSE) {
          /* 
           * status of auto request sense 
           */
                pSRB->SRBFlag &= ~AUTO_REQSENSE;
                pSRB->AdaptStatus = 0;
                pSRB->TargetStatus = SCSI_STATUS_CHECK_COND;
                
                if (status == SCSI_STATUS_CHECK_COND) {
                        pccb->ccb_h.status = CAM_SEL_TIMEOUT;
                        goto ckc_e;
                }
                *((u_long *) &(pSRB->CmdBlock[0])) = pSRB->Segment0[0];
                *((u_long *) &(pSRB->CmdBlock[4])) = pSRB->Segment0[1];
                pSRB->SRBTotalXferLength = pSRB->Segment1[1];
                pSRB->pSRBSGL->address = pSRB->SgSenseTemp.address;
                pSRB->pSRBSGL->length = pSRB->SgSenseTemp.length;
                pcsio->scsi_status = SCSI_STATUS_CHECK_COND;
                bcopy(trm_get_sense_buf(pACB, pSRB), &pcsio->sense_data,
                    pcsio->sense_len);
                pcsio->ccb_h.status = CAM_SCSI_STATUS_ERROR
                    | CAM_AUTOSNS_VALID;
                goto ckc_e;
        }
        /*
         * target status
         */
        if (status) {
                if (status == SCSI_STATUS_CHECK_COND) {
                        if ((pcsio->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) {
                          TRM_DPRINTF("trm_RequestSense..................\n");
                          trm_RequestSense(pACB, pDCB, pSRB);
                          return;
                        }
                        pcsio->scsi_status = SCSI_STATUS_CHECK_COND;
                        pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
                        goto ckc_e;
                } else if (status == SCSI_STAT_QUEUEFULL) {
                        bval = (u_int8_t) pDCB->GoingSRBCnt;
                        bval--;
                        pDCB->MaxActiveCommandCnt = bval;
                        trm_RewaitSRB(pDCB, pSRB);
                        pSRB->AdaptStatus = 0;
                        pSRB->TargetStatus = 0;
                        return;
                } else if (status == SCSI_STAT_SEL_TIMEOUT) {
                        pSRB->AdaptStatus  = H_SEL_TIMEOUT;
                        pSRB->TargetStatus = 0;
                        pcsio->scsi_status = SCSI_STAT_SEL_TIMEOUT;
                        pccb->ccb_h.status = CAM_SEL_TIMEOUT;
                } else if (status == SCSI_STAT_BUSY) {
                        TRM_DPRINTF("trm: target busy at %s %d\n",
                                __FILE__, __LINE__);
                        pcsio->scsi_status = SCSI_STAT_BUSY;
                        pccb->ccb_h.status = CAM_SCSI_BUSY;
                        return;
                  /* The device busy, try again later?    */
                } else if (status == SCSI_STAT_RESCONFLICT) {
                        TRM_DPRINTF("trm: target reserved at %s %d\n",
                                __FILE__, __LINE__);
                        pcsio->scsi_status = SCSI_STAT_RESCONFLICT;
                        pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;     /*XXX*/
                        return;
                } else {
                        pSRB->AdaptStatus = 0;
                        if (pSRB->RetryCnt) {
                                pSRB->RetryCnt--;
                                pSRB->TargetStatus = 0;
                                pSRB->SRBSGIndex = 0;
                                if (trm_StartSCSI(pACB, pDCB, pSRB)) {
                                  /* 
                                   * If trm_StartSCSI return 1 :
                                   * current interrupt status is interrupt 
                                   * disreenable 
                                   * It's said that SCSI processor has more 
                                   * one SRB need to do
                                   */
                                        trm_RewaitSRB(pDCB, pSRB);
                                }
                                return;
                        } else {
                                TRM_DPRINTF("trm: driver stuffup at %s %d\n",
                                        __FILE__, __LINE__);
                                pccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
                        }
                }
        } else {
        /* 
         * process initiator status..........................
         * Adapter (initiator) status
         */
                status = pSRB->AdaptStatus;
                if (status & H_OVER_UNDER_RUN) {
                        pSRB->TargetStatus = 0;
                        pccb->ccb_h.status = CAM_DATA_RUN_ERR;
                        /* Illegal length (over/under run) */
                } else if (pSRB->SRBStatus & PARITY_ERROR) {
                        TRM_DPRINTF("trm: driver stuffup %s %d\n",
                                __FILE__, __LINE__);
                        pDCB->tinfo.goal.period = 0;
                        pDCB->tinfo.goal.offset = 0;
                        /* Driver failed to perform operation */
                        pccb->ccb_h.status = CAM_UNCOR_PARITY;
                } else {
                  /* no error */
                        pSRB->AdaptStatus = 0;
                        pSRB->TargetStatus = 0;
                        pccb->ccb_h.status = CAM_REQ_CMP;
                        /* there is no error, (sense is invalid) */
                }
        }
ckc_e:
        if (pACB->scan_devices[target_id][target_lun]) {
          /*
           *   if SCSI command in "scan devices" duty
           */
                if (pSRB->CmdBlock[0] == TEST_UNIT_READY) 
                        pACB->scan_devices[target_id][target_lun] = 0;
                /* SCSI command phase :test unit ready */
                else if (pSRB->CmdBlock[0] == INQUIRY) {
                  /* 
                   * SCSI command phase :inquiry scsi device data 
                   * (type,capacity,manufacture.... 
                   */
                        if (pccb->ccb_h.status == CAM_SEL_TIMEOUT)
                                goto NO_DEV;
                        ptr = (PSCSI_INQDATA) pcsio->data_ptr;
                        /* page fault */
                        TRM_DPRINTF("trm_SRBdone..PSCSI_INQDATA:%2x \n",
                            ptr->DevType);
                        bval1 = ptr->DevType & SCSI_DEVTYPE; 
                        if (bval1 == SCSI_NODEV) {
NO_DEV:
                                TRM_DPRINTF("trm_SRBdone NO Device:target_id= %d ,target_lun= %d \n",
                                    target_id,
                                    target_lun);
                                crit_enter();
                                pACB->scan_devices[target_id][target_lun] = 0;
                                /* no device set scan device flag =0*/
                                /* pDCB Q link */
                                /* move the head of DCB to tempDCB*/
                                pTempDCB=pACB->pLinkDCB;  
                                /* search current DCB for pass link */
                                while (pTempDCB->pNextDCB != pDCB) {
                                        pTempDCB = pTempDCB->pNextDCB;
                                }
                                /*
                                 * when the current DCB found than connect 
                                 * current DCB tail 
                                 */
                                /* to the DCB tail that before current DCB */
                                pTempDCB->pNextDCB = pDCB->pNextDCB;
                                /*
                                 * if there was only one DCB ,connect his tail
                                 * to his head 
                                 */
                                if (pACB->pLinkDCB == pDCB)
                                        pACB->pLinkDCB = pTempDCB->pNextDCB;
                                if (pACB->pDCBRunRobin == pDCB)
                                        pACB->pDCBRunRobin = pTempDCB->pNextDCB;
                                pDCB->DCBstatus &= ~DS_IN_QUEUE;
                                pACB->DeviceCnt--;
                                if (pACB->DeviceCnt == 0) {
                                        pACB->pLinkDCB = NULL;
                                        pACB->pDCBRunRobin = NULL;
                                }
                                crit_exit();
                        } else { 
#ifdef trm_DEBUG1
                                int j;
                                for (j = 0; j < 28; j++) {
                                        TRM_DPRINTF("ptr=%2x ", 
                                                ((u_int8_t *)ptr)[j]);
                                }
#endif
                                pDCB->DevType = bval1;
                                if (bval1 == SCSI_DASD ||
                                    bval1 == SCSI_OPTICAL) {
                                        if ((((ptr->Vers & 0x07) >= 2) ||
                                              ((ptr->RDF & 0x0F) == 2)) && 
                                            (ptr->Flags & SCSI_INQ_CMDQUEUE) &&
                                            (pDCB->DevMode & TAG_QUEUING_) &&
                                            (pDCB->DevMode & EN_DISCONNECT_)) {
                                                if (pDCB->DevMode &
                                                    TAG_QUEUING_) {
                                                        pDCB->
                                                            MaxActiveCommandCnt =
                                                          pACB->TagMaxNum;
                                                        pDCB->SyncMode |= 
                                                          EN_TAG_QUEUING;
                                                        pDCB->tinfo.disc_tag |=
                                                          TRM_CUR_TAGENB;
                                                } else {
                                                        pDCB->SyncMode |= 
                                                          EN_ATN_STOP;
                                                        pDCB->tinfo.disc_tag &=
                                                          ~TRM_CUR_TAGENB;
                                                }
                                        }
                                }
                        }
                        /* pSRB->CmdBlock[0] == INQUIRY */
                }
                /* pACB->scan_devices[target_id][target_lun] */
        }
        crit_enter();
        /*  ReleaseSRB(pDCB, pSRB); */
        if (pSRB == pDCB->pGoingSRB)
                pDCB->pGoingSRB = pSRB->pNextSRB;
        else {
                psrb = pDCB->pGoingSRB;
                while (psrb->pNextSRB != pSRB) {
                        psrb = psrb->pNextSRB;
                }
                psrb->pNextSRB = pSRB->pNextSRB;
                if (pSRB == pDCB->pGoingLastSRB) {
                        pDCB->pGoingLastSRB = psrb;
                }
        }
        pSRB->pNextSRB = pACB->pFreeSRB;
        pACB->pFreeSRB = pSRB;
        pDCB->GoingSRBCnt--;
        trm_DoWaitingSRB(pACB);

        crit_exit();
        /*  Notify cmd done */
        xpt_done (pccb);
}

static void
trm_DoingSRB_Done(PACB pACB)
{
        PDCB            pDCB, pdcb;
        PSRB            psrb, psrb2;
        u_int16_t       cnt, i;
        union ccb       *pccb;

        pDCB = pACB->pLinkDCB;
        if (pDCB == NULL) 
                return;
        pdcb = pDCB;
        do {
                cnt = pdcb->GoingSRBCnt;
                psrb = pdcb->pGoingSRB;
                for (i = 0; i < cnt; i++) {
                        psrb2 = psrb->pNextSRB;
                        pccb = psrb->pccb;
                        pccb->ccb_h.status = CAM_SEL_TIMEOUT;
                        /*  ReleaseSRB(pDCB, pSRB); */
                        psrb->pNextSRB = pACB->pFreeSRB;
                        pACB->pFreeSRB = psrb;
                        xpt_done(pccb);
                        psrb  = psrb2;
                }
                pdcb->GoingSRBCnt = 0;
                pdcb->pGoingSRB = NULL;
                pdcb = pdcb->pNextDCB;
        }
        while (pdcb != pDCB);
}

static void 
trm_ResetSCSIBus(PACB pACB)
{
        crit_enter();
        pACB->ACBFlag |= RESET_DEV;

        trm_reg_write16(DO_RSTSCSI,TRMREG_SCSI_CONTROL);
        while (!(trm_reg_read16(TRMREG_SCSI_INTSTATUS) & INT_SCSIRESET));
        crit_exit();
        return;
}

static void 
trm_ScsiRstDetect(PACB pACB)
{
        u_long  wlval;

        TRM_DPRINTF("trm_ScsiRstDetect \n");
        wlval = 1000;
        while (--wlval)
                DELAY(1000);
        crit_enter();
        trm_reg_write8(STOPDMAXFER,TRMREG_DMA_CONTROL);

        trm_reg_write16(DO_CLRFIFO,TRMREG_SCSI_CONTROL);

        if (pACB->ACBFlag & RESET_DEV)
                pACB->ACBFlag |= RESET_DONE;
        else {
                pACB->ACBFlag |= RESET_DETECT;
                trm_ResetDevParam(pACB);
                /*      trm_DoingSRB_Done(pACB); ???? */
                trm_RecoverSRB(pACB);
                pACB->pActiveDCB = NULL;
                pACB->ACBFlag = 0;
                trm_DoWaitingSRB(pACB);
        }
        crit_exit();
        return;
}

static void
trm_RequestSense(PACB pACB, PDCB pDCB, PSRB pSRB)       
{
        union ccb               *pccb;
        struct ccb_scsiio       *pcsio;

        pccb  = pSRB->pccb;
        pcsio = &pccb->csio;

        pSRB->SRBFlag |= AUTO_REQSENSE;
        pSRB->Segment0[0] = *((u_long *) &(pSRB->CmdBlock[0]));
        pSRB->Segment0[1] = *((u_long *) &(pSRB->CmdBlock[4]));
        pSRB->Segment1[0] = (u_long) ((pSRB->ScsiCmdLen << 8) + 
            pSRB->SRBSGCount);
        pSRB->Segment1[1] = pSRB->SRBTotalXferLength; /* ?????????? */

        /* $$$$$$ Status of initiator/target $$$$$$$$ */
        pSRB->AdaptStatus = 0;
        pSRB->TargetStatus = 0;
        /* $$$$$$ Status of initiator/target $$$$$$$$ */
        
        pSRB->SRBTotalXferLength = sizeof(pcsio->sense_data);
        pSRB->SgSenseTemp.address = pSRB->pSRBSGL->address;
        pSRB->SgSenseTemp.length  = pSRB->pSRBSGL->length;
        pSRB->pSRBSGL->address = trm_get_sense_bufaddr(pACB, pSRB);
        pSRB->pSRBSGL->length = (u_long) sizeof(struct scsi_sense_data);
        pSRB->SRBSGCount = 1;
        pSRB->SRBSGIndex = 0;
        
        *((u_long *) &(pSRB->CmdBlock[0])) = 0x00000003;
        pSRB->CmdBlock[1] = pDCB->IdentifyMsg << 5;
        *((u_int16_t *) &(pSRB->CmdBlock[4])) = pcsio->sense_len;
        pSRB->ScsiCmdLen = 6;
        
        if (trm_StartSCSI(pACB, pDCB, pSRB))
           /* 
            * If trm_StartSCSI return 1 :
            * current interrupt status is interrupt disreenable 
            * It's said that SCSI processor has more one SRB need to do
            */
                trm_RewaitSRB(pDCB, pSRB);
}

static void 
trm_EnableMsgOutAbort2(PACB pACB, PSRB pSRB)
{

        pSRB->MsgCnt = 1;
        trm_reg_write16(DO_SETATN, TRMREG_SCSI_CONTROL);
}

static void
trm_EnableMsgOutAbort1(PACB pACB, PSRB pSRB)
{
  
        pSRB->MsgOutBuf[0] = MSG_ABORT;
        trm_EnableMsgOutAbort2(pACB, pSRB);
}

static void
trm_initDCB(PACB pACB, PDCB pDCB, u_int16_t unit,u_int32_t i,u_int32_t j)
{
        PNVRAMTYPE      pEEpromBuf;
        u_int8_t        bval,PeriodIndex;
        u_int           target_id,target_lun;
        PDCB            pTempDCB;
    
        target_id  = i;
        target_lun = j;

        /*
         *  Using the lun 0 device to init other DCB first, if the device
         *  has been initialized.
         *  I don't want init sync arguments one by one, it is the same.
         */
        if (target_lun != 0 &&
            (pACB->DCBarray[target_id][0].DCBstatus & DS_IN_QUEUE))
                bcopy(&pACB->DCBarray[target_id][0], pDCB,
                    sizeof(TRM_DCB));
        crit_enter();
        if (pACB->pLinkDCB == NULL) {
                pACB->pLinkDCB = pDCB;
                /* 
                 * RunRobin impersonate the role 
                 * that let each device had good proportion 
                 * about SCSI command proceeding 
                 */
                pACB->pDCBRunRobin = pDCB;
                pDCB->pNextDCB = pDCB;
        } else {
                pTempDCB=pACB->pLinkDCB;
                /* search the last nod of DCB link */
                while (pTempDCB->pNextDCB != pACB->pLinkDCB)
                        pTempDCB = pTempDCB->pNextDCB;
                /* connect current DCB with last DCB tail */
                pTempDCB->pNextDCB = pDCB;
                /* connect current DCB tail to this DCB Q head */
                pDCB->pNextDCB=pACB->pLinkDCB;
        }
        crit_exit();

        pACB->DeviceCnt++;
        pDCB->TargetID = target_id;
        pDCB->TargetLUN =  target_lun;
        pDCB->pWaitingSRB = NULL;
        pDCB->pGoingSRB = NULL;
        pDCB->GoingSRBCnt = 0;
        pDCB->pActiveSRB = NULL;
        pDCB->MaxActiveCommandCnt = 1;
        pDCB->DCBFlag = 0;
        pDCB->DCBstatus |= DS_IN_QUEUE;
        /* $$$$$$$ */
        pEEpromBuf = &trm_eepromBuf[unit];
        pDCB->DevMode = pEEpromBuf->NvramTarget[target_id].NvmTarCfg0;
        pDCB->AdpMode = pEEpromBuf->NvramChannelCfg;
        /* $$$$$$$ */
        /* 
         * disconnect enable ?
         */
        if (pDCB->DevMode & NTC_DO_DISCONNECT) {
                bval = 0xC0;
                pDCB->tinfo.disc_tag |= TRM_USR_DISCENB ;
        } else {
                bval = 0x80;
                pDCB->tinfo.disc_tag &= ~(TRM_USR_DISCENB);
        }
        bval |= target_lun;
        pDCB->IdentifyMsg = bval;
        if (target_lun != 0 &&
            (pACB->DCBarray[target_id][0].DCBstatus & DS_IN_QUEUE))
                return;
        /* $$$$$$$ */
        /*
         * tag Qing enable ?
         */
        if (pDCB->DevMode & TAG_QUEUING_) {
                pDCB->tinfo.disc_tag |= TRM_USR_TAGENB ;
        } else
                pDCB->tinfo.disc_tag &= ~(TRM_USR_TAGENB);
        /* $$$$$$$ */
        /*
         * wide nego ,sync nego enable ?
         */
        pDCB->SyncPeriod = 0;
        pDCB->SyncOffset = 0;
        PeriodIndex = pEEpromBuf->NvramTarget[target_id].NvmTarPeriod & 0x07;
        if (pACB->AdaptType==1) {/* is U2? */
            pDCB->MaxNegoPeriod=dc395u2x_clock_period[ PeriodIndex ];
            pDCB->tinfo.user.period=pDCB->MaxNegoPeriod;
            pDCB->tinfo.user.offset=(pDCB->SyncMode & SYNC_NEGO_ENABLE) ? 31 : 0;
        } else {
        pDCB->MaxNegoPeriod=dc395x_clock_period[ PeriodIndex ];
            pDCB->tinfo.user.period=pDCB->MaxNegoPeriod;
            pDCB->tinfo.user.offset=(pDCB->SyncMode & SYNC_NEGO_ENABLE) ? 15 : 0;
        }
        pDCB->SyncMode = 0;
        if ((pDCB->DevMode & NTC_DO_WIDE_NEGO) && 
            (pACB->Config & HCC_WIDE_CARD))
                pDCB->SyncMode |= WIDE_NEGO_ENABLE;
        /* enable wide nego */
        if (pDCB->DevMode & NTC_DO_SYNC_NEGO)
                pDCB->SyncMode |= SYNC_NEGO_ENABLE;
        /* enable sync nego */
        /* $$$$$$$ */
        /*
         *      Fill in tinfo structure.
         */
        pDCB->tinfo.user.width  = (pDCB->SyncMode & WIDE_NEGO_ENABLE) ? 
          MSG_EXT_WDTR_BUS_16_BIT : MSG_EXT_WDTR_BUS_8_BIT;

        pDCB->tinfo.current.period = 0;
        pDCB->tinfo.current.offset = 0;
        pDCB->tinfo.current.width = MSG_EXT_WDTR_BUS_8_BIT;
}

static void
trm_srbmapSG(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
        PSRB pSRB;

        pSRB=(PSRB) arg;
        pSRB->SRBSGPhyAddr=segs->ds_addr;
        return;
}

static void
trm_destroySRB(PACB pACB)
{
        PSRB pSRB;

        pSRB = pACB->pFreeSRB;
        while (pSRB) {
                if (pSRB->sg_dmamap) {
                        bus_dmamap_unload(pACB->sg_dmat, pSRB->sg_dmamap);
                        bus_dmamem_free(pACB->sg_dmat, pSRB->pSRBSGL,
                            pSRB->sg_dmamap);
                        bus_dmamap_destroy(pACB->sg_dmat, pSRB->sg_dmamap);
                }
                if (pSRB->dmamap)
                        bus_dmamap_destroy(pACB->buffer_dmat, pSRB->dmamap);
                pSRB = pSRB->pNextSRB;
        }
}

static int
trm_initSRB(PACB pACB)
{
        u_int16_t    i;
        PSRB    pSRB;
        int error;

        for (i = 0; i < TRM_MAX_SRB_CNT; i++) {
                pSRB = (PSRB)&pACB->pFreeSRB[i];

                if (bus_dmamem_alloc(pACB->sg_dmat, (void **)&pSRB->pSRBSGL,
                    BUS_DMA_NOWAIT, &pSRB->sg_dmamap) !=0 ) {
                        return ENXIO;
                }
                bus_dmamap_load(pACB->sg_dmat, pSRB->sg_dmamap, pSRB->pSRBSGL,
                    TRM_MAX_SG_LISTENTRY * sizeof(SGentry),
                    trm_srbmapSG, pSRB, /*flags*/0);
                if (i != TRM_MAX_SRB_CNT - 1) {
                        /*
                         * link all SRB 
                         */
                        pSRB->pNextSRB = &pACB->pFreeSRB[i+1];
                } else {
                        /*
                         * load NULL to NextSRB of the last SRB
                         */
                        pSRB->pNextSRB = NULL;
                }
                pSRB->TagNumber = i;

                /*
                 * Create the dmamap.  This is no longer optional!
                 */
                if ((error = bus_dmamap_create(pACB->buffer_dmat, 0,
                                               &pSRB->dmamap)) != 0)
                        return (error);

        }
        return (0);
}




static void
trm_initACB(PACB pACB, u_int8_t adaptType, u_int16_t unit)
{
        PNVRAMTYPE      pEEpromBuf;
    
        pEEpromBuf = &trm_eepromBuf[unit];
        pACB->max_id = 15;
        
        if (pEEpromBuf->NvramChannelCfg & NAC_SCANLUN)
                pACB->max_lun = 7;
        else
                pACB->max_lun = 0;

        TRM_DPRINTF("trm: pACB->max_id= %d pACB->max_lun= %d \n",
            pACB->max_id, pACB->max_lun);
        pACB->pLinkDCB = NULL;
        pACB->pDCBRunRobin = NULL;
        pACB->pActiveDCB = NULL;
        pACB->AdapterUnit = (u_int8_t)unit;
        pACB->AdaptSCSIID = pEEpromBuf->NvramScsiId;
        pACB->AdaptSCSILUN = 0;
        pACB->DeviceCnt = 0;
        pACB->AdaptType = adaptType;
        pACB->TagMaxNum = 2 << pEEpromBuf->NvramMaxTag;
        pACB->ACBFlag = 0;
        return;
}

static void
NVRAM_trm_write_all(PNVRAMTYPE pEEpromBuf,PACB pACB)
{
        u_int8_t        *bpEeprom = (u_int8_t *) pEEpromBuf;
        u_int8_t        bAddr;

        /* Enable SEEPROM */
        trm_reg_write8((trm_reg_read8(TRMREG_GEN_CONTROL) | EN_EEPROM),
            TRMREG_GEN_CONTROL);
        /*
         * Write enable
         */
        NVRAM_trm_write_cmd(pACB, 0x04, 0xFF);
        trm_reg_write8(0, TRMREG_GEN_NVRAM);
        NVRAM_trm_wait_30us(pACB);
        for (bAddr = 0; bAddr < 128; bAddr++, bpEeprom++) { 
                NVRAM_trm_set_data(pACB, bAddr, *bpEeprom);
        }
        /* 
         * Write disable
         */
        NVRAM_trm_write_cmd(pACB, 0x04, 0x00);
        trm_reg_write8(0 , TRMREG_GEN_NVRAM);
        NVRAM_trm_wait_30us(pACB);
        /* Disable SEEPROM */
        trm_reg_write8((trm_reg_read8(TRMREG_GEN_CONTROL) & ~EN_EEPROM),
            TRMREG_GEN_CONTROL);
        return;
}

static void
NVRAM_trm_set_data(PACB pACB, u_int8_t bAddr, u_int8_t bData)
{
        int             i;
        u_int8_t        bSendData;
        /* 
         * Send write command & address 
         */
        
        NVRAM_trm_write_cmd(pACB, 0x05, bAddr);
        /* 
         * Write data 
         */
        for (i = 0; i < 8; i++, bData <<= 1) {
                bSendData = NVR_SELECT;
                if (bData & 0x80)
                  /* Start from bit 7   */
                        bSendData |= NVR_BITOUT;
                trm_reg_write8(bSendData , TRMREG_GEN_NVRAM);
                NVRAM_trm_wait_30us(pACB);
                trm_reg_write8((bSendData | NVR_CLOCK), TRMREG_GEN_NVRAM);
                NVRAM_trm_wait_30us(pACB);
        }
        trm_reg_write8(NVR_SELECT , TRMREG_GEN_NVRAM);
        NVRAM_trm_wait_30us(pACB);
        /*
         * Disable chip select 
         */
        trm_reg_write8(0 , TRMREG_GEN_NVRAM);
        NVRAM_trm_wait_30us(pACB);
        trm_reg_write8(NVR_SELECT ,TRMREG_GEN_NVRAM);
        NVRAM_trm_wait_30us(pACB);
        /* 
         * Wait for write ready 
         */
        while (1) {
                trm_reg_write8((NVR_SELECT | NVR_CLOCK), TRMREG_GEN_NVRAM);
                NVRAM_trm_wait_30us(pACB);
                trm_reg_write8(NVR_SELECT, TRMREG_GEN_NVRAM);
                NVRAM_trm_wait_30us(pACB);
                if (trm_reg_read8(TRMREG_GEN_NVRAM) & NVR_BITIN) {
                        break;
                }
        }
        /* 
         * Disable chip select 
         */
        trm_reg_write8(0, TRMREG_GEN_NVRAM);
        return;
}

static void 
NVRAM_trm_read_all(PNVRAMTYPE pEEpromBuf, PACB pACB)
{
        u_int8_t        *bpEeprom = (u_int8_t*) pEEpromBuf;
        u_int8_t        bAddr;
    
        /*
         * Enable SEEPROM 
         */
        trm_reg_write8((trm_reg_read8(TRMREG_GEN_CONTROL) | EN_EEPROM),
            TRMREG_GEN_CONTROL);
        for (bAddr = 0; bAddr < 128; bAddr++, bpEeprom++)
                *bpEeprom = NVRAM_trm_get_data(pACB, bAddr);
        /* 
         * Disable SEEPROM 
         */
        trm_reg_write8((trm_reg_read8(TRMREG_GEN_CONTROL) & ~EN_EEPROM),
            TRMREG_GEN_CONTROL);
        return;
}

static u_int8_t
NVRAM_trm_get_data(PACB pACB, u_int8_t bAddr)
{
        int             i;
        u_int8_t        bReadData, bData = 0;
        /* 
        * Send read command & address
        */
        
        NVRAM_trm_write_cmd(pACB, 0x06, bAddr);
                                
        for (i = 0; i < 8; i++) {
          /* 
           * Read data
           */
                trm_reg_write8((NVR_SELECT | NVR_CLOCK) , TRMREG_GEN_NVRAM);
                NVRAM_trm_wait_30us(pACB);
                trm_reg_write8(NVR_SELECT , TRMREG_GEN_NVRAM);
                /* 
                 * Get data bit while falling edge 
                 */
                bReadData = trm_reg_read8(TRMREG_GEN_NVRAM);
                bData <<= 1;
                if (bReadData & NVR_BITIN) {
                        bData |= 1;
                }
                NVRAM_trm_wait_30us(pACB);
        }
        /* 
         * Disable chip select 
         */
        trm_reg_write8(0, TRMREG_GEN_NVRAM);
        return (bData);
}

static void
NVRAM_trm_wait_30us(PACB pACB)
{
  
        /*    ScsiPortStallExecution(30);        wait 30 us     */
        trm_reg_write8(5, TRMREG_GEN_TIMER);
        while (!(trm_reg_read8(TRMREG_GEN_STATUS) & GTIMEOUT));
        return;
}

static void
NVRAM_trm_write_cmd(PACB pACB, u_int8_t bCmd, u_int8_t bAddr)
{
        int             i;
        u_int8_t        bSendData;
                                        
        for (i = 0; i < 3; i++, bCmd <<= 1) {
          /* 
           * Program SB+OP code         
           */
                bSendData = NVR_SELECT;
                if (bCmd & 0x04)        
                        bSendData |= NVR_BITOUT;
                /* start from bit 2 */
                trm_reg_write8(bSendData, TRMREG_GEN_NVRAM);
                NVRAM_trm_wait_30us(pACB);
                trm_reg_write8((bSendData | NVR_CLOCK), TRMREG_GEN_NVRAM);
                NVRAM_trm_wait_30us(pACB);
        }       
        for (i = 0; i < 7; i++, bAddr <<= 1) {
          /* 
           * Program address            
           */
                bSendData = NVR_SELECT;
                if (bAddr & 0x40)       
                  /* Start from bit 6   */
                        bSendData |= NVR_BITOUT;
                trm_reg_write8(bSendData , TRMREG_GEN_NVRAM);
                NVRAM_trm_wait_30us(pACB);
                trm_reg_write8((bSendData | NVR_CLOCK), TRMREG_GEN_NVRAM);
                NVRAM_trm_wait_30us(pACB);
        }
        trm_reg_write8(NVR_SELECT, TRMREG_GEN_NVRAM);
        NVRAM_trm_wait_30us(pACB);
}

static void
trm_check_eeprom(PNVRAMTYPE pEEpromBuf, PACB pACB)
{
        u_int16_t       *wpEeprom;
        u_int16_t       wAddr, wCheckSum;
        u_long  dAddr, *dpEeprom;

        NVRAM_trm_read_all(pEEpromBuf,pACB);
        wCheckSum = 0;
        for (wAddr = 0, wpEeprom = (u_int16_t *) pEEpromBuf;
            wAddr < 64; wAddr++, wpEeprom++) {
                wCheckSum += *wpEeprom;
        }
        if (wCheckSum != 0x1234) {
          /* 
           * Checksum error, load default       
           */
                pEEpromBuf->NvramSubVendorID[0] = (u_int8_t) PCI_Vendor_ID_TEKRAM;
                pEEpromBuf->NvramSubVendorID[1] =
                  (u_int8_t) (PCI_Vendor_ID_TEKRAM >> 8);
                pEEpromBuf->NvramSubSysID[0] = (u_int8_t) PCI_Device_ID_TRM_S1040;
                pEEpromBuf->NvramSubSysID[1] = 
                  (u_int8_t) (PCI_Device_ID_TRM_S1040 >> 8);
                pEEpromBuf->NvramSubClass = 0x00;
                pEEpromBuf->NvramVendorID[0] = (u_int8_t) PCI_Vendor_ID_TEKRAM;
                pEEpromBuf->NvramVendorID[1] =
                  (u_int8_t) (PCI_Vendor_ID_TEKRAM >> 8);
                pEEpromBuf->NvramDeviceID[0] = (u_int8_t) PCI_Device_ID_TRM_S1040;
                pEEpromBuf->NvramDeviceID[1] = 
                  (u_int8_t) (PCI_Device_ID_TRM_S1040 >> 8);
                pEEpromBuf->NvramReserved = 0x00;

                for (dAddr = 0, dpEeprom = (u_long *) pEEpromBuf->NvramTarget;
                    dAddr < 16; dAddr++, dpEeprom++) {
                        *dpEeprom = 0x00000077;
                        /* NvmTarCfg3,NvmTarCfg2,NvmTarPeriod,NvmTarCfg0 */
                }

                *dpEeprom++ = 0x04000F07;
                /* NvramMaxTag,NvramDelayTime,NvramChannelCfg,NvramScsiId */
                *dpEeprom++ = 0x00000015;
                /* NvramReserved1,NvramBootLun,NvramBootTarget,NvramReserved0 */
                for (dAddr = 0; dAddr < 12; dAddr++, dpEeprom++)
                        *dpEeprom = 0x00;
                pEEpromBuf->NvramCheckSum = 0x00;
                for (wAddr = 0, wCheckSum = 0, wpEeprom = (u_int16_t *) pEEpromBuf;
                    wAddr < 63; wAddr++, wpEeprom++)
                        wCheckSum += *wpEeprom;
                *wpEeprom = 0x1234 - wCheckSum;
                NVRAM_trm_write_all(pEEpromBuf,pACB);
        }
        return;
}
static int
trm_initAdapter(PACB pACB, u_int16_t unit)
{
        PNVRAMTYPE      pEEpromBuf;
        u_int16_t       wval;
        u_int8_t        bval;

        pEEpromBuf = &trm_eepromBuf[unit];

        /* 250ms selection timeout */
        trm_reg_write8(SEL_TIMEOUT, TRMREG_SCSI_TIMEOUT);
        /* Mask all the interrupt */
        trm_reg_write8(0x00, TRMREG_DMA_INTEN);    
        trm_reg_write8(0x00, TRMREG_SCSI_INTEN);     
        /* Reset SCSI module */
        trm_reg_write16(DO_RSTMODULE, TRMREG_SCSI_CONTROL); 
        /* program configuration 0 */
        pACB->Config = HCC_AUTOTERM | HCC_PARITY;
        if (trm_reg_read8(TRMREG_GEN_STATUS) & WIDESCSI)
                pACB->Config |= HCC_WIDE_CARD;
        if (pEEpromBuf->NvramChannelCfg & NAC_POWERON_SCSI_RESET)
                pACB->Config |= HCC_SCSI_RESET;
        if (pACB->Config & HCC_PARITY)
                bval = PHASELATCH | INITIATOR | BLOCKRST | PARITYCHECK;
        else
                bval = PHASELATCH | INITIATOR | BLOCKRST ;
        trm_reg_write8(bval,TRMREG_SCSI_CONFIG0); 
        /* program configuration 1 */
        trm_reg_write8(0x13, TRMREG_SCSI_CONFIG1); 
        /* program Host ID */
        bval = pEEpromBuf->NvramScsiId;
        trm_reg_write8(bval, TRMREG_SCSI_HOSTID); 
        /* set ansynchronous transfer */
        trm_reg_write8(0x00, TRMREG_SCSI_OFFSET); 
        /* Trun LED control off*/
        wval = trm_reg_read16(TRMREG_GEN_CONTROL) & 0x7F;
        trm_reg_write16(wval, TRMREG_GEN_CONTROL); 
        /* DMA config */
        wval = trm_reg_read16(TRMREG_DMA_CONFIG) | DMA_ENHANCE;
        trm_reg_write16(wval, TRMREG_DMA_CONFIG); 
        /* Clear pending interrupt status */
        trm_reg_read8(TRMREG_SCSI_INTSTATUS);
        /* Enable SCSI interrupt */
        trm_reg_write8(0x7F, TRMREG_SCSI_INTEN); 
        trm_reg_write8(EN_SCSIINTR, TRMREG_DMA_INTEN); 
        return (0);
}

static void
trm_mapSRB(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
        PACB pACB;

        pACB = (PACB)arg;
        pACB->srb_physbase = segs->ds_addr;
}

static void
trm_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
        bus_addr_t *baddr;

        baddr = (bus_addr_t *)arg;
        *baddr = segs->ds_addr;
}

static PACB
trm_init(u_int16_t unit, device_t dev)
{
        PACB            pACB;
        int             rid = PCIR_BAR(0), i = 0, j = 0;
        u_int16_t       adaptType = 0;
    
        pACB = (PACB) device_get_softc(dev);
        if (!pACB) {
                kprintf("trm%d: cannot allocate ACB !\n", unit);
                return (NULL);
        }
        pACB->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 
            &rid, RF_ACTIVE);
        if (pACB->iores == NULL) {
                kprintf("trm_init: bus_alloc_resource failed!\n");
                return (NULL);
        }
        switch (pci_get_devid(dev)) {
        case PCI_DEVICEID_TRMS1040:
                adaptType = 0;
                break;
        case PCI_DEVICEID_TRMS2080:
                adaptType = 1;
                break;
        default:
                kprintf("trm_init %d: unknown adapter type!\n", unit);
                goto bad;
        }
        pACB->dev = dev;
        pACB->tag = rman_get_bustag(pACB->iores);
        pACB->bsh = rman_get_bushandle(pACB->iores);
        if (bus_dma_tag_create(/*parent_dmat*/ pACB->parent_dmat,
              /*alignment*/                      1,
              /*boundary*/                       0,
              /*lowaddr*/  BUS_SPACE_MAXADDR,
              /*highaddr*/       BUS_SPACE_MAXADDR,
              /*maxsize*/                 MAXBSIZE,
              /*nsegments*/               TRM_NSEG,
              /*maxsegsz*/    TRM_MAXTRANSFER_SIZE,
              /*flags*/           BUS_DMA_ALLOCNOW,
              &pACB->buffer_dmat) != 0) 
                goto bad;
        /* DMA tag for our ccb structures */
        if (bus_dma_tag_create(                                 
        /*parent_dmat*/pACB->parent_dmat,
        /*alignment*/  1,
        /*boundary*/   0,
        /*lowaddr*/    BUS_SPACE_MAXADDR,
        /*highaddr*/   BUS_SPACE_MAXADDR,
        /*maxsize*/    TRM_MAX_SRB_CNT * sizeof(TRM_SRB),
        /*nsegments*/  1,
        /*maxsegsz*/   TRM_MAXTRANSFER_SIZE,
        /*flags*/      0,
        /*dmat*/       &pACB->srb_dmat) != 0) {
                kprintf("trm_init %d: bus_dma_tag_create SRB failure\n", unit);
                goto bad;
        }
        if (bus_dmamem_alloc(pACB->srb_dmat, (void **)&pACB->pFreeSRB,
            BUS_DMA_NOWAIT, &pACB->srb_dmamap) != 0) {
                kprintf("trm_init %d: bus_dmamem_alloc SRB failure\n", unit);
                goto bad;
        }
        bus_dmamap_load(pACB->srb_dmat, pACB->srb_dmamap, pACB->pFreeSRB,
            TRM_MAX_SRB_CNT * sizeof(TRM_SRB), trm_mapSRB, pACB,
            /* flags */0);
        /* Create, allocate, and map DMA buffers for autosense data */
        if (bus_dma_tag_create(/*parent_dmat*/NULL, /*alignment*/1,
            /*boundary*/0,
            /*lowaddr*/BUS_SPACE_MAXADDR_32BIT,
            /*highaddr*/BUS_SPACE_MAXADDR,
            sizeof(struct scsi_sense_data) * TRM_MAX_SRB_CNT,
            /*nsegments*/1,
            /*maxsegsz*/TRM_MAXTRANSFER_SIZE,
            /*flags*/0, &pACB->sense_dmat) != 0) {
          if (bootverbose)
            device_printf(dev, "cannot create sense buffer dmat\n");
          goto bad;
        }

        if (bus_dmamem_alloc(pACB->sense_dmat, (void **)&pACB->sense_buffers,
                             BUS_DMA_NOWAIT, &pACB->sense_dmamap) != 0)
                goto bad;

        bus_dmamap_load(pACB->sense_dmat, pACB->sense_dmamap,
                       pACB->sense_buffers,
                       sizeof(struct scsi_sense_data) * TRM_MAX_SRB_CNT,
                       trm_dmamap_cb, &pACB->sense_busaddr, /*flags*/0);

        trm_check_eeprom(&trm_eepromBuf[unit],pACB);
        trm_initACB(pACB, adaptType, unit);
        for (i = 0; i < (pACB->max_id + 1); i++) {
                if (pACB->AdaptSCSIID == i)
                        continue;
                for(j = 0; j < (pACB->max_lun + 1); j++) {
                        pACB->scan_devices[i][j] = 1;
                        /* we assume we need to scan all devices */
                        trm_initDCB(pACB, &pACB->DCBarray[i][j], unit, i, j);
                }
        }
        bzero(pACB->pFreeSRB, TRM_MAX_SRB_CNT * sizeof(TRM_SRB));
        if (bus_dma_tag_create(                    
                    /*parent_dmat*/NULL, 
                    /*alignment*/  1,
                    /*boundary*/   0,
                    /*lowaddr*/    BUS_SPACE_MAXADDR,
                    /*highaddr*/   BUS_SPACE_MAXADDR,
                    /*maxsize*/    TRM_MAX_SG_LISTENTRY * sizeof(SGentry), 
                    /*nsegments*/  1,
                    /*maxsegsz*/   TRM_MAXTRANSFER_SIZE,
                    /*flags*/      0, 
                    /*dmat*/       &pACB->sg_dmat) != 0)
                goto bad;

        if (trm_initSRB(pACB)) {
                kprintf("trm_initSRB: error\n");
                goto bad;
        }
        if (trm_initAdapter(pACB, unit)) {
                kprintf("trm_initAdapter: initial ERROR\n");
                goto bad;
        }
        return (pACB);
bad:
        if (pACB->iores)
                bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0),
                    pACB->iores);
        if (pACB->sense_dmamap) {
                bus_dmamap_unload(pACB->sense_dmat, pACB->sense_dmamap);
                bus_dmamem_free(pACB->sense_dmat, pACB->sense_buffers,
                    pACB->sense_dmamap);
                bus_dmamap_destroy(pACB->sense_dmat, pACB->sense_dmamap);
        }
        if (pACB->sense_dmat)
                bus_dma_tag_destroy(pACB->sense_dmat);
        if (pACB->sg_dmat) {
                trm_destroySRB(pACB);
                bus_dma_tag_destroy(pACB->sg_dmat);
        }
        if (pACB->srb_dmamap) {
                bus_dmamap_unload(pACB->srb_dmat, pACB->srb_dmamap);
                bus_dmamem_free(pACB->srb_dmat, pACB->pFreeSRB,
                    pACB->srb_dmamap);
                bus_dmamap_destroy(pACB->srb_dmat, pACB->srb_dmamap);
        }
        if (pACB->srb_dmat)
                bus_dma_tag_destroy(pACB->srb_dmat);
        if (pACB->buffer_dmat)
                bus_dma_tag_destroy(pACB->buffer_dmat);
        return (NULL);
}

static int
trm_attach(device_t dev)
{
        struct  cam_devq *device_Q;
        PACB    pACB = NULL;
        int     rid = 0;
        int unit = device_get_unit(dev);
        
        if ((pACB = trm_init((u_int16_t) unit,
            dev)) == NULL) {
                kprintf("trm%d: trm_init error!\n",unit);
                return (ENXIO);
        }
        /* After setting up the adapter, map our interrupt */
        /*  
         * Now let the CAM generic SCSI layer find the SCSI devices on the bus
         * start queue to reset to the idle loop.
         * Create device queue of SIM(s)
         * (MAX_START_JOB - 1) : max_sim_transactions
         */
        pACB->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
            RF_SHAREABLE | RF_ACTIVE);
        if (pACB->irq == NULL ||
            bus_setup_intr(dev, pACB->irq, 
                           0, trm_Interrupt, pACB,
                           &pACB->ih, NULL)) {
                kprintf("trm%d: register Interrupt handler error!\n", unit);
                goto bad;
        }
        device_Q = cam_simq_alloc(TRM_MAX_START_JOB);
        if (device_Q == NULL){ 
                kprintf("trm%d: device_Q == NULL !\n",unit);
                goto bad;
        }
        /*
         * Now tell the generic SCSI layer
         * about our bus.
         * If this is the xpt layer creating a sim, then it's OK
         * to wait for an allocation.
         * XXX Should we pass in a flag to indicate that wait is OK?
         *
         *                    SIM allocation
         *
         *                 SCSI Interface Modules
         * The sim driver creates a sim for each controller.  The sim device
         * queue is separately created in order to allow resource sharing betwee
         * sims.  For instance, a driver may create one sim for each channel of
         * a multi-channel controller and use the same queue for each channel.
         * In this way, the queue resources are shared across all the channels
         * of the multi-channel controller.
         * trm_action     : sim_action_func
         * trm_poll       : sim_poll_func
         * "trm"        : sim_name ,if sim_name =  "xpt" ..M_DEVBUF,M_WAITOK
         * pACB         : *softc    if sim_name <> "xpt" ..M_DEVBUF,M_NOWAIT
         * pACB->unit   : unit
         * 1            : max_dev_transactions
         * MAX_TAGS     : max_tagged_dev_transactions
         *
         *  *******Construct our first channel SIM entry
         */
        pACB->psim = cam_sim_alloc(trm_action,
            trm_poll,
            "trm",
            pACB,
            unit,
            &sim_mplock,
            1,
            TRM_MAX_TAGS_CMD_QUEUE,
            device_Q);
        cam_simq_release(device_Q);  /* device queues are refcounted */
        if (pACB->psim == NULL) {
                kprintf("trm%d: SIM allocate fault !\n",unit);
                goto bad;
        }
        if (xpt_bus_register(pACB->psim, 0) != CAM_SUCCESS)  {
                kprintf("trm%d: xpt_bus_register fault !\n",unit);
                goto bad;
        }
        if (xpt_create_path(&pACB->ppath,
              NULL,
              cam_sim_path(pACB->psim),
              CAM_TARGET_WILDCARD,
              CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
                kprintf("trm%d: xpt_create_path fault !\n",unit);
                xpt_bus_deregister(cam_sim_path(pACB->psim));
                goto bad;
        }
        return (0);
bad:
        if (pACB->iores)
                bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0),
                    pACB->iores);
        if (pACB->sg_dmat) {            
                trm_destroySRB(pACB);
                bus_dma_tag_destroy(pACB->sg_dmat);
        }
        
        if (pACB->srb_dmamap) {
                bus_dmamap_unload(pACB->srb_dmat, pACB->srb_dmamap);
                bus_dmamem_free(pACB->srb_dmat, pACB->pFreeSRB,
                    pACB->srb_dmamap);
                bus_dmamap_destroy(pACB->srb_dmat, pACB->srb_dmamap);
        }
        if (pACB->srb_dmat)
                bus_dma_tag_destroy(pACB->srb_dmat);
        if (pACB->sense_dmamap) {
                  bus_dmamap_unload(pACB->sense_dmat, pACB->sense_dmamap);
                  bus_dmamem_free(pACB->sense_dmat, pACB->sense_buffers,
                      pACB->sense_dmamap);
                  bus_dmamap_destroy(pACB->sense_dmat, pACB->sense_dmamap);
        }
        if (pACB->sense_dmat)
                bus_dma_tag_destroy(pACB->sense_dmat);
        if (pACB->buffer_dmat)
                bus_dma_tag_destroy(pACB->buffer_dmat);
        if (pACB->ih)
                bus_teardown_intr(dev, pACB->irq, pACB->ih);
        if (pACB->irq)
                bus_release_resource(dev, SYS_RES_IRQ, 0, pACB->irq);
        if (pACB->psim)
                cam_sim_free(pACB->psim);
        
        return (ENXIO);
        
}

/*
*                  pci_device
*         trm_probe (device_t tag, pcidi_t type)
*
*/
static int
trm_probe(device_t dev)
{
        switch (pci_get_devid(dev)) {
        case PCI_DEVICEID_TRMS1040:
                device_set_desc(dev,
                    "Tekram DC395U/UW/F DC315/U Fast20 Wide SCSI Adapter");
                return (BUS_PROBE_DEFAULT);
        case PCI_DEVICEID_TRMS2080:
                device_set_desc(dev,
                    "Tekram DC395U2D/U2W Fast40 Wide SCSI Adapter");
                return (BUS_PROBE_DEFAULT);
        default:
                return (ENXIO);
        }
}

static int
trm_detach(device_t dev)
{
        PACB pACB = device_get_softc(dev);

        bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), pACB->iores);
        trm_destroySRB(pACB);
        bus_dma_tag_destroy(pACB->sg_dmat);
        bus_dmamap_unload(pACB->srb_dmat, pACB->srb_dmamap);
        bus_dmamem_free(pACB->srb_dmat, pACB->pFreeSRB,
            pACB->srb_dmamap);
        bus_dmamap_destroy(pACB->srb_dmat, pACB->srb_dmamap);
        bus_dma_tag_destroy(pACB->srb_dmat);
        bus_dmamap_unload(pACB->sense_dmat, pACB->sense_dmamap);
        bus_dmamem_free(pACB->sense_dmat, pACB->sense_buffers,
            pACB->sense_dmamap);
        bus_dmamap_destroy(pACB->sense_dmat, pACB->sense_dmamap);
        bus_dma_tag_destroy(pACB->sense_dmat);
        bus_dma_tag_destroy(pACB->buffer_dmat);
        bus_teardown_intr(dev, pACB->irq, pACB->ih);
        bus_release_resource(dev, SYS_RES_IRQ, 0, pACB->irq);
        xpt_async(AC_LOST_DEVICE, pACB->ppath, NULL);
        xpt_free_path(pACB->ppath);
        xpt_bus_deregister(cam_sim_path(pACB->psim));
        cam_sim_free(pACB->psim);
        return (0);
}
static device_method_t trm_methods[] = {
        /* Device interface */
        DEVMETHOD(device_probe,         trm_probe),
        DEVMETHOD(device_attach,        trm_attach),
        DEVMETHOD(device_detach,        trm_detach),
        DEVMETHOD_END
};

static driver_t trm_driver = {
        "trm", trm_methods, sizeof(struct _ACB)
};

static devclass_t trm_devclass;
DRIVER_MODULE(trm, pci, trm_driver, trm_devclass, NULL, NULL);
MODULE_DEPEND(trm, cam, 1, 1, 1);