83c5d981ac
- Unify bus reset/probe sequence. Whenever bus attached at boot or later, CAM will automatically reset and scan it. It allows to remove duplicate code from many drivers. - Any bus, attached before CAM completed it's boot-time initialization, will equally join to the process, delaying boot if needed. - New kern.cam.boot_delay loader tunable should help controllers that are still unable to register their buses in time (such as slow USB/ PCCard/ CardBus devices), by adding one more event to wait on boot. - To allow synchronization between different CAM levels, concept of requests priorities was extended. Priorities now split between several "run levels". Device can be freezed at specified level, allowing higher priority requests to pass. For example, no payload requests allowed, until PMP driver enable port. ATA XPT negotiate transfer parameters, periph driver configure caching and so on. - Frozen requests are no more counted by request allocation scheduler. It fixes deadlocks, when frozen low priority payload requests occupying slots, required by higher levels to manage theit execution. - Two last changes were holding proper ATA reinitialization and error recovery implementation. Now it is done: SATA controllers and Port Multipliers now implement automatic hot-plug and should correctly recover from timeouts and bus resets. - Improve SCSI error recovery for devices on buses without automatic sense reporting, such as ATAPI or USB. For example, it allows CAM to wait, while CD drive loads disk, instead of immediately return error status. - Decapitalize diagnostic messages and make them more readable and sensible. - Teach PMP driver to limit maximum speed on fan-out ports. - Make boot wait for PMP scan completes, and make rescan more reliable. - Fix pass driver, to return CCB to user level in case of error. - Increase number of retries in cd driver, as device may return several UAs.
3747 lines
100 KiB
C
3747 lines
100 KiB
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)
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
/*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* 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 __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 <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcireg.h>
|
|
#include <machine/resource.h>
|
|
#include <machine/bus.h>
|
|
#include <sys/rman.h>
|
|
|
|
#include <cam/cam.h>
|
|
#include <cam/cam_ccb.h>
|
|
#include <cam/cam_sim.h>
|
|
#include <cam/cam_xpt_sim.h>
|
|
#include <cam/cam_debug.h>
|
|
|
|
#include <cam/scsi/scsi_all.h>
|
|
#include <cam/scsi/scsi_message.h>
|
|
|
|
#include <dev/trm/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...) printf("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)
|
|
{
|
|
int intflag;
|
|
PSRB pSRB;
|
|
|
|
intflag = splcam();
|
|
pSRB = pACB->pFreeSRB;
|
|
if (pSRB) {
|
|
pACB->pFreeSRB = pSRB->pNextSRB;
|
|
pSRB->pNextSRB = NULL;
|
|
}
|
|
splx(intflag);
|
|
return (pSRB);
|
|
}
|
|
|
|
static void
|
|
trm_RewaitSRB0(PDCB pDCB, PSRB pSRB)
|
|
{
|
|
PSRB psrb1;
|
|
int intflag;
|
|
|
|
intflag = splcam();
|
|
if ((psrb1 = pDCB->pWaitingSRB)) {
|
|
pSRB->pNextSRB = psrb1;
|
|
pDCB->pWaitingSRB = pSRB;
|
|
} else {
|
|
pSRB->pNextSRB = NULL;
|
|
pDCB->pWaitingSRB = pSRB;
|
|
pDCB->pWaitingLastSRB = pSRB;
|
|
}
|
|
splx(intflag);
|
|
}
|
|
|
|
static void
|
|
trm_RewaitSRB(PDCB pDCB, PSRB pSRB)
|
|
{
|
|
PSRB psrb1;
|
|
int intflag;
|
|
|
|
intflag = splcam();
|
|
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;
|
|
}
|
|
splx(intflag);
|
|
}
|
|
|
|
static void
|
|
trm_DoWaitingSRB(PACB pACB)
|
|
{
|
|
int intflag;
|
|
PDCB ptr, ptr1;
|
|
PSRB pSRB;
|
|
|
|
intflag = splcam();
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
splx(intflag);
|
|
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)
|
|
{
|
|
int flags;
|
|
PACB pACB;
|
|
PSRB pSRB;
|
|
union ccb *ccb;
|
|
u_long totalxferlen=0;
|
|
|
|
flags = splcam();
|
|
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;
|
|
int 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);
|
|
splx(flags);
|
|
return;
|
|
}
|
|
ccb->ccb_h.status |= CAM_SIM_QUEUED;
|
|
#if 0
|
|
/* XXX Need a timeout handler */
|
|
ccb->ccb_h.timeout_ch = timeout(trmtimeout, (caddr_t)srb, (ccb->ccb_h.timeout * hz) / 1000);
|
|
#endif
|
|
trm_SendSRB(pACB, pSRB);
|
|
splx(flags);
|
|
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;
|
|
int actionflags;
|
|
u_int target_id,target_lun;
|
|
|
|
CAM_DEBUG(pccb->ccb_h.path, CAM_DEBUG_TRACE, ("trm_action\n"));
|
|
|
|
actionflags = splcam();
|
|
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);
|
|
splx(actionflags);
|
|
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);
|
|
splx(actionflags);
|
|
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);
|
|
splx(actionflags);
|
|
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 vmflags;
|
|
int error;
|
|
|
|
vmflags = splsoftvm();
|
|
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;
|
|
}
|
|
splx(vmflags);
|
|
} 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);
|
|
splx(actionflags);
|
|
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;
|
|
/*
|
|
* 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;
|
|
/*
|
|
* 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;
|
|
int intflag;
|
|
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];
|
|
intflag = splcam();
|
|
/*
|
|
* 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);
|
|
|
|
splx(intflag);
|
|
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;
|
|
int intflag;
|
|
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;
|
|
intflag = splcam();
|
|
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;
|
|
}
|
|
splx(intflag);
|
|
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;
|
|
}
|
|
splx(actionflags);
|
|
}
|
|
|
|
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)
|
|
{
|
|
int intflag;
|
|
u_int16_t i;
|
|
|
|
TRM_DPRINTF("trm: RESET");
|
|
intflag = splcam();
|
|
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);
|
|
splx(intflag);
|
|
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(vpACB)
|
|
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 = (void *) 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 = (void *) 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;
|
|
memset(&neg, 0, sizeof (neg));
|
|
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);
|
|
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;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
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 = 0;
|
|
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 = 0;
|
|
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 = 0;
|
|
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;
|
|
int intflag;
|
|
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);
|
|
intflag = splcam();
|
|
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;
|
|
}
|
|
splx(intflag);
|
|
} 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] */
|
|
}
|
|
intflag = splcam();
|
|
/* 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);
|
|
|
|
splx(intflag);
|
|
/* 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)
|
|
{
|
|
int intflag;
|
|
|
|
intflag = splcam();
|
|
pACB->ACBFlag |= RESET_DEV;
|
|
|
|
trm_reg_write16(DO_RSTSCSI,TRMREG_SCSI_CONTROL);
|
|
while (!(trm_reg_read16(TRMREG_SCSI_INTSTATUS) & INT_SCSIRESET));
|
|
splx(intflag);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
trm_ScsiRstDetect(PACB pACB)
|
|
{
|
|
int intflag;
|
|
u_long wlval;
|
|
|
|
TRM_DPRINTF("trm_ScsiRstDetect \n");
|
|
wlval = 1000;
|
|
while (--wlval)
|
|
DELAY(1000);
|
|
intflag = splcam();
|
|
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);
|
|
}
|
|
splx(intflag);
|
|
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;
|
|
int intflag;
|
|
|
|
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));
|
|
intflag = splcam();
|
|
if (pACB->pLinkDCB == 0) {
|
|
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;
|
|
}
|
|
splx(intflag);
|
|
|
|
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 *) pEEpromBuf;
|
|
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) {
|
|
printf("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) {
|
|
printf("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:
|
|
printf("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,
|
|
/*filter*/ NULL,
|
|
/*filterarg*/ NULL,
|
|
/*maxsize*/ MAXBSIZE,
|
|
/*nsegments*/ TRM_NSEG,
|
|
/*maxsegsz*/ TRM_MAXTRANSFER_SIZE,
|
|
/*flags*/ BUS_DMA_ALLOCNOW,
|
|
/*lockfunc*/ busdma_lock_mutex,
|
|
/*lockarg*/ &Giant,
|
|
/* dmat */ &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,
|
|
/*filter*/ NULL,
|
|
/*filterarg*/ NULL,
|
|
/*maxsize*/ TRM_MAX_SRB_CNT * sizeof(TRM_SRB),
|
|
/*nsegments*/ 1,
|
|
/*maxsegsz*/ TRM_MAXTRANSFER_SIZE,
|
|
/*flags*/ 0,
|
|
/*lockfunc*/ busdma_lock_mutex,
|
|
/*lockarg*/ &Giant,
|
|
/*dmat*/ &pACB->srb_dmat) != 0) {
|
|
printf("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) {
|
|
printf("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,
|
|
/*filter*/NULL, /*filterarg*/NULL,
|
|
sizeof(struct scsi_sense_data) * TRM_MAX_SRB_CNT,
|
|
/*nsegments*/1,
|
|
/*maxsegsz*/TRM_MAXTRANSFER_SIZE,
|
|
/*flags*/0, /*lockfunc*/busdma_lock_mutex,
|
|
/*lockarg*/&Giant, &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,
|
|
/*filter*/ NULL,
|
|
/*filterarg*/ NULL,
|
|
/*maxsize*/ TRM_MAX_SG_LISTENTRY * sizeof(SGentry),
|
|
/*nsegments*/ 1,
|
|
/*maxsegsz*/ TRM_MAXTRANSFER_SIZE,
|
|
/*flags*/ 0,
|
|
/*lockfunc*/ busdma_lock_mutex,
|
|
/*lockarg*/ &Giant,
|
|
/*dmat*/ &pACB->sg_dmat) != 0)
|
|
goto bad;
|
|
|
|
if (trm_initSRB(pACB)) {
|
|
printf("trm_initSRB: error\n");
|
|
goto bad;
|
|
}
|
|
if (trm_initAdapter(pACB, unit)) {
|
|
printf("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;
|
|
u_long device_id;
|
|
PACB pACB = 0;
|
|
int rid = 0;
|
|
int unit = device_get_unit(dev);
|
|
|
|
device_id = pci_get_devid(dev);
|
|
/*
|
|
* These cards do not allow memory mapped accesses
|
|
*/
|
|
if ((pACB = trm_init((u_int16_t) unit,
|
|
dev)) == NULL) {
|
|
printf("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,
|
|
INTR_TYPE_CAM, NULL, trm_Interrupt, pACB, &pACB->ih)) {
|
|
printf("trm%d: register Interrupt handler error!\n", unit);
|
|
goto bad;
|
|
}
|
|
device_Q = cam_simq_alloc(TRM_MAX_START_JOB);
|
|
if (device_Q == NULL){
|
|
printf("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,
|
|
&Giant,
|
|
1,
|
|
TRM_MAX_TAGS_CMD_QUEUE,
|
|
device_Q);
|
|
if (pACB->psim == NULL) {
|
|
printf("trm%d: SIM allocate fault !\n",unit);
|
|
cam_simq_free(device_Q); /* SIM allocate fault*/
|
|
goto bad;
|
|
}
|
|
if (xpt_bus_register(pACB->psim, dev, 0) != CAM_SUCCESS) {
|
|
printf("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) {
|
|
printf("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, TRUE);
|
|
|
|
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, TRUE);
|
|
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),
|
|
{ 0, 0 }
|
|
};
|
|
|
|
static driver_t trm_driver = {
|
|
"trm", trm_methods, sizeof(struct _ACB)
|
|
};
|
|
|
|
static devclass_t trm_devclass;
|
|
DRIVER_MODULE(trm, pci, trm_driver, trm_devclass, 0, 0);
|
|
MODULE_DEPEND(trm, pci, 1, 1, 1);
|
|
MODULE_DEPEND(trm, cam, 1, 1, 1);
|