summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/amd64/conf/GENERIC5
-rw-r--r--sys/conf/NOTES15
-rw-r--r--sys/conf/files.i3864
-rw-r--r--sys/i386/conf/GENERIC5
-rw-r--r--sys/i386/conf/LINT15
-rw-r--r--sys/i386/conf/NOTES15
-rw-r--r--sys/i386/conf/files.i3864
-rw-r--r--sys/i386/isa/ic/ncr53400.h49
-rw-r--r--sys/i386/isa/ic/ncr5380.h90
-rw-r--r--sys/i386/isa/ncr5380.c1544
10 files changed, 1717 insertions, 29 deletions
diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC
index 3e531e5..37a1c73 100644
--- a/sys/amd64/conf/GENERIC
+++ b/sys/amd64/conf/GENERIC
@@ -1,7 +1,7 @@
#
# GENERIC -- Generic machine with WD/AHx/NCR/BTx family disks
#
-# $Id: GENERIC,v 1.27 1995/01/24 20:38:27 paul Exp $
+# $Id: GENERIC,v 1.28 1995/01/26 05:04:34 ache Exp $
#
machine "i386"
@@ -50,7 +50,8 @@ controller ahc1 at isa? bio irq ? vector ahcintr
controller ahb0 at isa? bio irq ? vector ahbintr
controller aha0 at isa? port "IO_AHA0" bio irq ? drq 5 vector ahaintr
controller aic0 at isa? port 0x340 bio irq 11 vector aicintr
-controller pas0 at isa? port 0x1f88 bio
+controller nca0 at isa? port 0x1f88 bio irq 10 vector ncaintr
+controller nca1 at isa? port 0x350 bio irq 5 vector ncaintr
controller sea0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector seaintr
controller scbus0
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index 7c15437..f1bcf62 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -2,7 +2,7 @@
# LINT -- config file for checking all the sources, tries to pull in
# as much of the source tree as it can.
#
-# $Id: LINT,v 1.130 1995/01/26 05:04:37 ache Exp $
+# $Id: LINT,v 1.131 1995/01/26 11:00:12 ache Exp $
#
# NB: You probably don't want to try running a kernel built from this
# file. Instead, you should start from GENERIC, and add options from
@@ -370,14 +370,14 @@ device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
#
#
-# SCSI host adapters: `aha', `ahb', `aic', `bt', `pas'
+# SCSI host adapters: `aha', `ahb', `aic', `bt', `nca'
#
# aha: Adaptec 154x
# ahb: Adaptec 174x
# ahc: Adaptec 274x
# aic: Adaptec 152x and sound cards using the Adaptec AIC-6360 (slow!)
# bt: Most Buslogic controllers
-# pas: ProAudioSpectrum cards using the NCR 5380 (slow!)
+# nca: ProAudioSpectrum cards using the NCR 5380 or Trantor T130
# uha: UltraStore 14F and 34F
# sea: Seagate ST01/02 8 bit controller (slow!)
# wds: Western Digital WD7000 controller (no scatter/gather!).
@@ -393,10 +393,11 @@ controller aha0 at isa? port "IO_AHA0" bio irq ? drq 5 vector ahaintr
controller uha0 at isa? port "IO_UHA0" bio irq ? drq 5 vector uhaintr
controller aic0 at isa? port 0x340 bio irq 11 vector aicintr
-controller pas0 at isa? port 0x1f88
-controller pas1 at isa? port 0x1f84
-controller pas2 at isa? port 0x1f8c
-controller pas3 at isa? port 0x1e88
+controller nca0 at isa? port 0x1f88 bio irq 10 vector ncaintr
+controller nca1 at isa? port 0x1f84
+controller nca2 at isa? port 0x1f8c
+controller nca3 at isa? port 0x1e88
+controller nca4 at isa? port 0x350 bio irq 5 vector ncaintr
controller sea0 at isa? bio irq 5 iomem 0xdc000 iosiz 0x2000 vector seaintr
controller wds0 at isa? port 0x350 bio irq 15 drq 6 vector wdsintr
diff --git a/sys/conf/files.i386 b/sys/conf/files.i386
index 854f289..7fd6cba 100644
--- a/sys/conf/files.i386
+++ b/sys/conf/files.i386
@@ -1,7 +1,7 @@
# This file tells config what files go into building a kernel,
# files marked standard are always included.
#
-# $Id: files.i386,v 1.73 1995/01/23 00:25:03 jkh Exp $
+# $Id: files.i386,v 1.74 1995/01/25 21:04:15 jmz Exp $
#
i386/apm/apm.c optional apm device-driver
i386/apm/apm_setup.s optional apm
@@ -87,8 +87,8 @@ i386/isa/joy.c optional joy device-driver
i386/isa/lpt.c optional lpt device-driver
i386/isa/mcd.c optional mcd device-driver
i386/isa/mse.c optional mse device-driver
+i386/isa/ncr5380.c optional nca device-driver
i386/isa/npx.c optional npx device-driver
-i386/isa/pas.c optional pas device-driver
i386/isa/readMBR.c standard
i386/isa/syscons.c optional sc device-driver
i386/isa/pcaudio.c optional pca device-driver
diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC
index 3e531e5..37a1c73 100644
--- a/sys/i386/conf/GENERIC
+++ b/sys/i386/conf/GENERIC
@@ -1,7 +1,7 @@
#
# GENERIC -- Generic machine with WD/AHx/NCR/BTx family disks
#
-# $Id: GENERIC,v 1.27 1995/01/24 20:38:27 paul Exp $
+# $Id: GENERIC,v 1.28 1995/01/26 05:04:34 ache Exp $
#
machine "i386"
@@ -50,7 +50,8 @@ controller ahc1 at isa? bio irq ? vector ahcintr
controller ahb0 at isa? bio irq ? vector ahbintr
controller aha0 at isa? port "IO_AHA0" bio irq ? drq 5 vector ahaintr
controller aic0 at isa? port 0x340 bio irq 11 vector aicintr
-controller pas0 at isa? port 0x1f88 bio
+controller nca0 at isa? port 0x1f88 bio irq 10 vector ncaintr
+controller nca1 at isa? port 0x350 bio irq 5 vector ncaintr
controller sea0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector seaintr
controller scbus0
diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT
index 7c15437..f1bcf62 100644
--- a/sys/i386/conf/LINT
+++ b/sys/i386/conf/LINT
@@ -2,7 +2,7 @@
# LINT -- config file for checking all the sources, tries to pull in
# as much of the source tree as it can.
#
-# $Id: LINT,v 1.130 1995/01/26 05:04:37 ache Exp $
+# $Id: LINT,v 1.131 1995/01/26 11:00:12 ache Exp $
#
# NB: You probably don't want to try running a kernel built from this
# file. Instead, you should start from GENERIC, and add options from
@@ -370,14 +370,14 @@ device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
#
#
-# SCSI host adapters: `aha', `ahb', `aic', `bt', `pas'
+# SCSI host adapters: `aha', `ahb', `aic', `bt', `nca'
#
# aha: Adaptec 154x
# ahb: Adaptec 174x
# ahc: Adaptec 274x
# aic: Adaptec 152x and sound cards using the Adaptec AIC-6360 (slow!)
# bt: Most Buslogic controllers
-# pas: ProAudioSpectrum cards using the NCR 5380 (slow!)
+# nca: ProAudioSpectrum cards using the NCR 5380 or Trantor T130
# uha: UltraStore 14F and 34F
# sea: Seagate ST01/02 8 bit controller (slow!)
# wds: Western Digital WD7000 controller (no scatter/gather!).
@@ -393,10 +393,11 @@ controller aha0 at isa? port "IO_AHA0" bio irq ? drq 5 vector ahaintr
controller uha0 at isa? port "IO_UHA0" bio irq ? drq 5 vector uhaintr
controller aic0 at isa? port 0x340 bio irq 11 vector aicintr
-controller pas0 at isa? port 0x1f88
-controller pas1 at isa? port 0x1f84
-controller pas2 at isa? port 0x1f8c
-controller pas3 at isa? port 0x1e88
+controller nca0 at isa? port 0x1f88 bio irq 10 vector ncaintr
+controller nca1 at isa? port 0x1f84
+controller nca2 at isa? port 0x1f8c
+controller nca3 at isa? port 0x1e88
+controller nca4 at isa? port 0x350 bio irq 5 vector ncaintr
controller sea0 at isa? bio irq 5 iomem 0xdc000 iosiz 0x2000 vector seaintr
controller wds0 at isa? port 0x350 bio irq 15 drq 6 vector wdsintr
diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES
index 7c15437..f1bcf62 100644
--- a/sys/i386/conf/NOTES
+++ b/sys/i386/conf/NOTES
@@ -2,7 +2,7 @@
# LINT -- config file for checking all the sources, tries to pull in
# as much of the source tree as it can.
#
-# $Id: LINT,v 1.130 1995/01/26 05:04:37 ache Exp $
+# $Id: LINT,v 1.131 1995/01/26 11:00:12 ache Exp $
#
# NB: You probably don't want to try running a kernel built from this
# file. Instead, you should start from GENERIC, and add options from
@@ -370,14 +370,14 @@ device npx0 at isa? port "IO_NPX" irq 13 vector npxintr
#
#
-# SCSI host adapters: `aha', `ahb', `aic', `bt', `pas'
+# SCSI host adapters: `aha', `ahb', `aic', `bt', `nca'
#
# aha: Adaptec 154x
# ahb: Adaptec 174x
# ahc: Adaptec 274x
# aic: Adaptec 152x and sound cards using the Adaptec AIC-6360 (slow!)
# bt: Most Buslogic controllers
-# pas: ProAudioSpectrum cards using the NCR 5380 (slow!)
+# nca: ProAudioSpectrum cards using the NCR 5380 or Trantor T130
# uha: UltraStore 14F and 34F
# sea: Seagate ST01/02 8 bit controller (slow!)
# wds: Western Digital WD7000 controller (no scatter/gather!).
@@ -393,10 +393,11 @@ controller aha0 at isa? port "IO_AHA0" bio irq ? drq 5 vector ahaintr
controller uha0 at isa? port "IO_UHA0" bio irq ? drq 5 vector uhaintr
controller aic0 at isa? port 0x340 bio irq 11 vector aicintr
-controller pas0 at isa? port 0x1f88
-controller pas1 at isa? port 0x1f84
-controller pas2 at isa? port 0x1f8c
-controller pas3 at isa? port 0x1e88
+controller nca0 at isa? port 0x1f88 bio irq 10 vector ncaintr
+controller nca1 at isa? port 0x1f84
+controller nca2 at isa? port 0x1f8c
+controller nca3 at isa? port 0x1e88
+controller nca4 at isa? port 0x350 bio irq 5 vector ncaintr
controller sea0 at isa? bio irq 5 iomem 0xdc000 iosiz 0x2000 vector seaintr
controller wds0 at isa? port 0x350 bio irq 15 drq 6 vector wdsintr
diff --git a/sys/i386/conf/files.i386 b/sys/i386/conf/files.i386
index 854f289..7fd6cba 100644
--- a/sys/i386/conf/files.i386
+++ b/sys/i386/conf/files.i386
@@ -1,7 +1,7 @@
# This file tells config what files go into building a kernel,
# files marked standard are always included.
#
-# $Id: files.i386,v 1.73 1995/01/23 00:25:03 jkh Exp $
+# $Id: files.i386,v 1.74 1995/01/25 21:04:15 jmz Exp $
#
i386/apm/apm.c optional apm device-driver
i386/apm/apm_setup.s optional apm
@@ -87,8 +87,8 @@ i386/isa/joy.c optional joy device-driver
i386/isa/lpt.c optional lpt device-driver
i386/isa/mcd.c optional mcd device-driver
i386/isa/mse.c optional mse device-driver
+i386/isa/ncr5380.c optional nca device-driver
i386/isa/npx.c optional npx device-driver
-i386/isa/pas.c optional pas device-driver
i386/isa/readMBR.c standard
i386/isa/syscons.c optional sc device-driver
i386/isa/pcaudio.c optional pca device-driver
diff --git a/sys/i386/isa/ic/ncr53400.h b/sys/i386/isa/ic/ncr53400.h
new file mode 100644
index 0000000..4a60f01
--- /dev/null
+++ b/sys/i386/isa/ic/ncr53400.h
@@ -0,0 +1,49 @@
+/*
+ * Definitions for 53C400 SCSI-controller chip.
+ *
+ * Derived from Linux NCR-5380 generic driver sources (by Drew Eckhardt).
+ *
+ * Copyright (C) 1994 Serge Vakulenko (vak@cronyx.ru)
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``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 DEVELOPERS 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.
+ */
+
+#ifndef _IC_NCR_53C400_H_
+#define _IC_NCR_53C400_H_
+
+#define C400_CSR 0 /* rw - Control and Status Reg. */
+#define CSR_5380_ENABLE 0x80
+#define CSR_TRANSFER_DIRECTION 0x40
+#define CSR_TRANSFER_READY_INTR 0x20
+#define CSR_5380_INTR 0x10
+#define CSR_SHARED_INTR 0x08
+#define CSR_HOST_BUF_NOT_READY 0x04 /* read only */
+#define CSR_SCSI_BUF_READY 0x02 /* read only */
+#define CSR_5380_GATED_IRQ 0x01 /* read only */
+#define CSR_BITS "\20\1irq\2sbrdy\3hbrdy\4shintr\5intr\6tintr\7tdir\10enable"
+
+#define C400_CCR 1 /* rw - Clock Counter Reg. */
+#define C400_HBR 4 /* rw - Host Buffer Reg. */
+
+#define C400_5380_REG_OFFSET 8 /* Offset of 5380 registers. */
+
+#endif /* _IC_NCR_53C400_H_ */
diff --git a/sys/i386/isa/ic/ncr5380.h b/sys/i386/isa/ic/ncr5380.h
new file mode 100644
index 0000000..f4eec05
--- /dev/null
+++ b/sys/i386/isa/ic/ncr5380.h
@@ -0,0 +1,90 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dkuug.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ * Modified by Serge Vakulenko (vak@cronyx.ru)
+ *
+ * ncr_5380.h,v 1.2 1994/09/11 20:29:18 phk Exp
+ *
+ * Definitions for 5380 SCSI-controller chip.
+ *
+ * Derived from "NCR 53C80 Family SCSI Protocol Controller Data Manual"
+ */
+
+#ifndef _IC_NCR_5380_H_
+#define _IC_NCR_5380_H_
+
+#define C80_CSDR 0 /* ro - Current SCSI Data Reg. */
+#define C80_ODR 0 /* wo - Output Data Reg. */
+
+#define C80_ICR 1 /* rw - Initiator Command Reg. */
+#define ICR_ASSERT_RST 0x80
+#define ICR_ARBITRATION_IN_PROGRESS 0x40 /* read only */
+#define ICR_TRI_STATE_MODE 0x40 /* write only */
+#define ICR_LOST_ARBITRATION 0x20 /* read only */
+#define ICR_DIFF_ENABLE 0x20 /* write only */
+#define ICR_ASSERT_ACK 0x10
+#define ICR_ASSERT_BSY 0x08
+#define ICR_ASSERT_SEL 0x04
+#define ICR_ASSERT_ATN 0x02
+#define ICR_ASSERT_DATA_BUS 0x01
+#define ICR_BITS "\20\1dbus\2atn\3sel\4bsy\5ack\6arblost\7arb\10rst"
+
+/*
+ * The mask to use when doing read_modify_write on ICR.
+ */
+#define ICR_MASK (~(ICR_DIFF_ENABLE | ICR_TRI_STATE_MODE))
+
+#define C80_MR 2 /* rw - Mode Reg. */
+#define MR_BLOCK_MODE_DMA 0x80
+#define MR_TARGET_MODE 0x40
+#define MR_ENABLE_PARITY_CHECKING 0x20
+#define MR_ENABLE_PARITY_INTERRUPT 0x10
+#define MR_ENABLE_EOP_INTERRUPT 0x08
+#define MR_MONITOR_BUSY 0x04
+#define MR_DMA_MODE 0x02
+#define MR_ARBITRATE 0x01
+#define MR_BITS "\20\1arb\2dma\3mbusy\4eopintr\5parintr\6pcheck\7targ\10blk"
+
+#define C80_TCR 3 /* rw - Target Command Reg. */
+#define TCR_LAST_BYTE_SENT 0x80 /* read only */
+#define TCR_ASSERT_REQ 0x08
+#define TCR_ASSERT_MSG 0x04
+#define TCR_ASSERT_CD 0x02
+#define TCR_ASSERT_IO 0x01
+#define TCR_BITS "\20\1i/o\2c/d\3msg\4req\10lastbyte"
+
+#define C80_CSBR 4 /* ro - Current SCSI Bus Status Reg. */
+#define CSBR_RST 0x80
+#define CSBR_BSY 0x40
+#define CSBR_REQ 0x20
+#define CSBR_MSG 0x10
+#define CSBR_CD 0x08
+#define CSBR_IO 0x04
+#define CSBR_SEL 0x02
+#define CSBR_ACK 0x01
+#define CSBR_BITS "\20\1ack\2sel\3i/o\4c/d\5msg\6req\7bsy\10rst"
+
+#define C80_SER 4 /* wo - Select Enable Reg. */
+
+#define C80_BSR 5 /* ro - Bus and Status Reg. */
+#define BSR_END_OF_DMA_XFER 0x80
+#define BSR_DMA_REQUEST 0x40
+#define BSR_PARITY_ERROR 0x20
+#define BSR_INTERRUPT_REQUEST_ACTIVE 0x10
+#define BSR_PHASE_MISMATCH 0x08
+#define BSR_BUSY_ERROR 0x04
+#define BSR_ATN 0x02
+#define BSR_ACK 0x01
+#define BSR_BITS "\20\1ack\2atn\3busyerr\4pherr\5irq\6parerr\7drq\10dend"
+
+#define C80_SDSR 5 /* wo - Start DMA Send Reg. */
+#define C80_IDR 6 /* ro - Input Data Reg. */
+#define C80_SDTR 6 /* wo - Start DMA Target Receive Reg. */
+#define C80_RPIR 7 /* ro - Reset Parity/Interrupt Reg. */
+#define C80_SDIR 7 /* wo - Start DMA Initiator Receive Reg. */
+
+#endif /* _IC_NCR_5380_H_ */
diff --git a/sys/i386/isa/ncr5380.c b/sys/i386/isa/ncr5380.c
new file mode 100644
index 0000000..cba446b
--- /dev/null
+++ b/sys/i386/isa/ncr5380.c
@@ -0,0 +1,1544 @@
+/*
+ * FreeBSD generic NCR-5380/NCR-53C400 SCSI driver
+ *
+ * Copyright (C) 1994 Serge Vakulenko (vak@cronyx.ru)
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``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 DEVELOPERS 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.
+ */
+
+/*
+ * Tested on the following hardware:
+ * Adapter: Trantor T130
+ * Streamer: Archive Viper 150,
+ * CD-ROM: NEC CDR-25
+ */
+#undef DEBUG
+
+#include "nca.h"
+#if NNCA > 0
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/devconf.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/ic/ncr5380.h>
+#include <i386/isa/ic/ncr53400.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#ifdef DEBUG
+# define PRINT(s) printf s
+#else
+# define PRINT(s) /*void*/
+#endif
+
+#define SCB_TABLE_SIZE 8 /* start with 8 scb entries in table */
+#define BLOCK_SIZE 512 /* size of READ/WRITE areas on SCSI card */
+#define HOST_SCSI_ADDR 7 /* address of the adapter on the SCSI bus */
+
+/*
+ * Defice config flags
+ */
+#define FLAG_NOPARITY 0x01 /* disable SCSI bus parity check */
+
+/*
+ * ProAudioSpectrum registers
+ */
+#define PAS16_DATA 8 /* Data Register */
+#define PAS16_STAT 9 /* Status Register */
+#define PAS16_STAT_DREQ 0x80 /* Pseudo-DMA ready bit */
+#define PAS16_REG(r) (((r) & 0xc) << 11 | ((r) & 3))
+
+static u_char pas16_irq_magic[] =
+ { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 };
+
+/*
+ * SCSI bus phases
+ */
+#define PHASE_MASK (CSBR_MSG | CSBR_CD | CSBR_IO)
+#define PHASE_DATAOUT 0
+#define PHASE_DATAIN CSBR_IO
+#define PHASE_CMDOUT CSBR_CD
+#define PHASE_STATIN (CSBR_CD | CSBR_IO)
+#define PHASE_MSGOUT (CSBR_MSG | CSBR_CD)
+#define PHASE_MSGIN (CSBR_MSG | CSBR_CD | CSBR_IO)
+#define PHASE_NAME(ph) phase_name[(ph)>>2]
+#define PHASE_TO_TCR(ph) ((ph) >> 2)
+
+static char *phase_name[] = {
+ "DATAOUT", "DATAIN", "CMDOUT", "STATIN",
+ "Phase4?", "Phase5?", "MSGOUT", "MSGIN",
+};
+
+/*
+ * SCSI message codes
+ */
+#define MSG_COMMAND_COMPLETE 0x00
+#define MSG_SAVE_POINTERS 0x02
+#define MSG_RESTORE_POINTERS 0x03
+#define MSG_DISCONNECT 0x04
+#define MSG_ABORT 0x06
+#define MSG_MESSAGE_REJECT 0x07
+#define MSG_NOP 0x08
+#define MSG_BUS_DEV_RESET 0x0c
+#define MSG_IDENTIFY(lun) (0xc0 | ((lun) & 0x7))
+#define MSG_ISIDENT(m) ((m) & 0x80)
+
+/*
+ * SCSI control block used to keep info about a scsi command
+ */
+typedef struct scb {
+ int flags; /* status of the instruction */
+#define SCB_FREE 0x00
+#define SCB_ACTIVE 0x01
+#define SCB_ABORTED 0x02
+#define SCB_TIMEOUT 0x04
+#define SCB_ERROR 0x08
+#define SCB_TIMECHK 0x10 /* we have set a timeout on this one */
+#define SCB_SENSE 0x20 /* sensed data available */
+#define SCB_TBUSY 0x40 /* target busy */
+ struct scb *next; /* in free list */
+ struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */
+ u_char *data; /* position in data buffer so far */
+ int32 datalen; /* bytes remaining to transfer */;
+} scb_t;
+
+typedef enum {
+ CTLR_NONE,
+ CTLR_NCR_5380,
+ CTLR_NCR_53C400,
+ CTLR_PAS_16,
+} ctlr_t;
+
+/*
+ * Data structure describing the target state.
+ */
+typedef struct {
+ u_char busy; /* mask of busy luns at device target */
+ u_long perrcnt; /* counter of target parity errors */
+} target_t;
+
+/*
+ * Data structure describing current status of the scsi bus. One for each
+ * controller card.
+ */
+typedef struct {
+ ctlr_t type; /* Seagate or Future Domain */
+ char *name; /* adapter name */
+
+ /* NCR-5380 controller registers */
+ u_short ODR; /* (wo-0) Output Data Register */
+ u_short CSDR; /* (ro-0) Current SCSI Data Register */
+ u_short ICR; /* (rw-1) Initiator Command Register */
+ u_short MR; /* (rw-2) Mode Register */
+ u_short TCR; /* (rw-3) Target Command Register */
+ u_short SER; /* (wo-4) Select Enable Register */
+ u_short CSBR; /* (ro-4) Current SCSI Bus Status Register */
+ u_short BSR; /* (ro-5) Bus and Status Register */
+ u_short SDSR; /* (wo-5) Start DMA Send Register */
+ u_short SDIR; /* (wo-7) Start DMA Initiator Receive Register */
+ u_short RPIR; /* (ro-7) Reset Parity/Interrupt Register */
+
+ /* NCR-53C400 controller registers */
+ u_short CSR; /* (rw-0) Control and Status Register */
+ u_short CCR; /* (rw-1) Clock Counter Register */
+ u_short HBR; /* (rw-4) Host Buffer Register */
+
+ /* ProAudioSpectrum controller registers */
+ u_short PDATA; /* (rw) Pseudo-DMA Data Register */
+ u_short PSTAT; /* (rw) Pseudo-DMA Status Register */
+
+ u_char scsi_addr; /* our scsi address, 0..7 */
+ u_char scsi_id; /* our scsi id mask */
+ u_char parity; /* parity flag: CMD_EN_PARITY or 0 */
+ u_char irq; /* IRQ number used or 0 if no IRQ */
+ u_int timeout_active : 1; /* timeout() active (requested) */
+
+ struct scsi_link sc_link; /* struct connecting different data */
+ scb_t *queue; /* waiting to be issued */
+ scb_t *disconnected_queue; /* waiting to reconnect */
+
+ int numscb; /* number of scsi control blocks */
+ scb_t *free_scb; /* free scb list */
+ scb_t scbs[SCB_TABLE_SIZE];
+
+ target_t target[8]; /* target state data */
+} adapter_t;
+
+adapter_t ncadata[NNCA];
+
+#define IS_BUSY(a,b) ((a)->target[(b)->xfer->sc_link->target].busy &\
+ (1 << (b)->xfer->sc_link->lun))
+#define SET_BUSY(a,b) ((a)->target[(b)->xfer->sc_link->target].busy |=\
+ (1 << (b)->xfer->sc_link->lun))
+#define CLEAR_BUSY(a,b) ((a)->target[(b)->xfer->sc_link->target].busy &=\
+ ~(1 << (b)->xfer->sc_link->lun))
+
+/*
+ * Wait for condition, given as an boolean expression.
+ * Print the message on timeout.
+ */
+#define WAITFOR(condition,count,message) {\
+ register u_long cnt = count; char *msg = message;\
+ while (cnt-- && ! (condition)) continue;\
+ if (cnt == -1 && msg)\
+ printf ("nca: %s timeout\n", msg); }
+
+int ncaintr (int unit);
+static int nca_probe (struct isa_device *dev);
+static int nca_attach (struct isa_device *dev);
+static int32 nca_scsi_cmd (struct scsi_xfer *xs);
+static u_int32 nca_adapter_info (int unit);
+static void nca_timeout (void *scb);
+static void ncaminphys (struct buf *bp);
+static void nca_done (adapter_t *z, scb_t *scb);
+static void nca_start (adapter_t *z);
+static void nca_information_transfer (adapter_t *z, scb_t *scb);
+static int nca_poll (adapter_t *z, scb_t *scb);
+static int nca_init (adapter_t *z);
+static int nca_reselect (adapter_t *z);
+static int nca_select (adapter_t *z, scb_t *scb);
+static int nca_abort (adapter_t *z, scb_t *scb);
+static void nca_send_abort (adapter_t *z);
+static u_char nca_msg_input (adapter_t *z);
+static void nca_tick (void *arg);
+static int nca_sense (adapter_t *z, scb_t *scb);
+static void nca_data_output (adapter_t *z, u_char **pdata, u_long *plen);
+static void nca_data_input (adapter_t *z, u_char **pdata, u_long *plen);
+static void nca_cmd_output (adapter_t *z, u_char *cmd, int cmdlen);
+static void nca_53400_dma_xfer (adapter_t *z, int r, u_char **dat, u_long *len);
+static void nca_pas_dma_xfer (adapter_t *z, int r, u_char **dat, u_long *len);
+
+static struct scsi_adapter nca_switch = {
+ nca_scsi_cmd, ncaminphys, 0, 0, nca_adapter_info, "nca", {0},
+};
+static struct scsi_device nca_dev = { NULL, NULL, NULL, NULL, "nca", 0, {0} };
+struct isa_driver ncadriver = { nca_probe, nca_attach, "nca" };
+
+/*
+ * Check if the device can be found at the port given and if so,
+ * detect the type of board. Set it up ready for further work.
+ * Takes the isa_dev structure from autoconf as an argument.
+ * Returns 1 if card recognized, 0 if errors.
+ */
+int nca_probe (struct isa_device *dev)
+{
+ adapter_t *z = &ncadata[dev->id_unit];
+ int i;
+
+ /* Init fields used by our routines */
+ z->parity = (dev->id_flags & FLAG_NOPARITY) ? 0 :
+ MR_ENABLE_PARITY_CHECKING;
+ z->scsi_addr = HOST_SCSI_ADDR;
+ z->scsi_id = 1 << z->scsi_addr;
+ z->irq = dev->id_irq ? ffs (dev->id_irq) - 1 : 0;
+ z->queue = 0;
+ z->disconnected_queue = 0;
+ for (i=0; i<8; i++)
+ z->target[i].busy = 0;
+
+ /* Link up the free list of scbs */
+ z->numscb = SCB_TABLE_SIZE;
+ z->free_scb = z->scbs;
+ for (i=1; i<SCB_TABLE_SIZE; i++)
+ z->scbs[i-1].next = z->scbs + i;
+ z->scbs[SCB_TABLE_SIZE-1].next = 0;
+
+ /* Try NCR 5380. */
+ z->type = CTLR_NCR_5380;
+ z->name = "NCR-5380";
+ z->ODR = dev->id_iobase + C80_ODR;
+ z->CSDR = dev->id_iobase + C80_CSDR;
+ z->ICR = dev->id_iobase + C80_ICR;
+ z->MR = dev->id_iobase + C80_MR;
+ z->TCR = dev->id_iobase + C80_TCR;
+ z->SER = dev->id_iobase + C80_SER;
+ z->CSBR = dev->id_iobase + C80_CSBR;
+ z->BSR = dev->id_iobase + C80_BSR;
+ z->SDSR = dev->id_iobase + C80_SDSR;
+ z->SDIR = dev->id_iobase + C80_SDIR;
+ z->RPIR = dev->id_iobase + C80_RPIR;
+ z->CSR = 0;
+ z->CCR = 0;
+ z->HBR = 0;
+ z->PDATA = 0;
+ z->PSTAT = 0;
+ if (nca_init (z) == 0)
+ return (8);
+
+ /* Try NCR 53C400. */
+ z->type = CTLR_NCR_53C400;
+ z->name = "NCR-53C400";
+ z->ODR = dev->id_iobase + C400_5380_REG_OFFSET + C80_ODR;
+ z->CSDR = dev->id_iobase + C400_5380_REG_OFFSET + C80_CSDR;
+ z->ICR = dev->id_iobase + C400_5380_REG_OFFSET + C80_ICR;
+ z->MR = dev->id_iobase + C400_5380_REG_OFFSET + C80_MR;
+ z->TCR = dev->id_iobase + C400_5380_REG_OFFSET + C80_TCR;
+ z->SER = dev->id_iobase + C400_5380_REG_OFFSET + C80_SER;
+ z->CSBR = dev->id_iobase + C400_5380_REG_OFFSET + C80_CSBR;
+ z->BSR = dev->id_iobase + C400_5380_REG_OFFSET + C80_BSR;
+ z->SDSR = dev->id_iobase + C400_5380_REG_OFFSET + C80_SDSR;
+ z->SDIR = dev->id_iobase + C400_5380_REG_OFFSET + C80_SDIR;
+ z->RPIR = dev->id_iobase + C400_5380_REG_OFFSET + C80_RPIR;
+ z->CSR = dev->id_iobase + C400_CSR;
+ z->CCR = dev->id_iobase + C400_CCR;
+ z->HBR = dev->id_iobase + C400_HBR;
+ z->PDATA = 0;
+ z->PSTAT = 0;
+ if (nca_init (z) == 0)
+ return (16);
+
+ /* Try ProAudioSpectrum-16. */
+ z->type = CTLR_PAS_16;
+ z->name = "ProAudioSpectrum"; /* changed later */
+ z->ODR = dev->id_iobase ^ PAS16_REG (C80_ODR);
+ z->CSDR = dev->id_iobase ^ PAS16_REG (C80_CSDR);
+ z->ICR = dev->id_iobase ^ PAS16_REG (C80_ICR);
+ z->MR = dev->id_iobase ^ PAS16_REG (C80_MR);
+ z->TCR = dev->id_iobase ^ PAS16_REG (C80_TCR);
+ z->SER = dev->id_iobase ^ PAS16_REG (C80_SER);
+ z->CSBR = dev->id_iobase ^ PAS16_REG (C80_CSBR);
+ z->BSR = dev->id_iobase ^ PAS16_REG (C80_BSR);
+ z->SDSR = dev->id_iobase ^ PAS16_REG (C80_SDSR);
+ z->SDIR = dev->id_iobase ^ PAS16_REG (C80_SDIR);
+ z->RPIR = dev->id_iobase ^ PAS16_REG (C80_RPIR);
+ z->CSR = 0;
+ z->CCR = 0;
+ z->HBR = 0;
+ z->PDATA = dev->id_iobase ^ PAS16_REG (PAS16_DATA);
+ z->PSTAT = dev->id_iobase ^ PAS16_REG (PAS16_STAT);
+ if (nca_init (z) == 0)
+ return (4);
+
+ bzero (z, sizeof (*z));
+ return (0);
+}
+
+/*
+ * Probe the adapter, and if found, reset the board and the scsi bus.
+ * Return 0 if the adapter found.
+ */
+int nca_init (adapter_t *z)
+{
+ int i, c;
+
+ if (z->type == CTLR_NCR_53C400) {
+ if (inb (z->CSR) == 0xFF)
+ return (100);
+
+ /* Reset 53C400. */
+ outb (z->CSR, CSR_5380_ENABLE);
+
+ /* Enable interrupts. */
+ outb (z->CSR, z->irq ? CSR_5380_INTR : 0);
+ }
+
+ if (z->type == CTLR_PAS_16) {
+ u_short base = z->PDATA & 0x3FF;
+
+ outb (0x9a01, 0xbc + (z-ncadata)); /* unit number */
+ outb (0x9a01, base >> 2);
+
+ if (inb (base^0x803) == 0xFF)
+ return (200);
+
+ if (inb (z->CSDR) == 0xFF && inb (z->CSDR^0x2000) == 0xFF &&
+ inb (z->CSDR) == 0xFF && inb (z->CSDR^0x2000) == 0xFF &&
+ inb (z->CSDR) == 0xFF && inb (z->CSDR^0x2000) == 0xFF &&
+ inb (z->CSDR) == 0xFF && inb (z->CSDR^0x2000) == 0xFF)
+ return (201);
+
+ i = inb (base^0x803);
+ outb (base^0x803, i ^ 0xE0);
+ c = inb (base^0x803);
+ outb (base^0x803, 1);
+ if (i != c)
+ return (202);
+
+ /* Various magic. */
+ outb (base^0x4000, 0x30); /* Timeout counter */
+ outb (base^0x4001, 0x01); /* Reset TC */
+ outb (base^0xbc00, 0x01); /* 1 Wait state */
+ outb (base^0x8003, 0x4d); /* sysconfig_4 */
+ i = pas16_irq_magic[z->irq];
+ if (!i) {
+ z->irq = 0;
+ } else {
+ outb (base^0xf002, i << 4);
+ outb (base^0x8003, 0x6d); /* sysconfig_4 */
+ }
+
+ switch (inb (base^0xEC03) & 0xF) {
+ case 6: z->name = "ProAudioSpectrum-Plus"; break;
+ case 12: z->name = "ProAudioSpectrum-16D"; break;
+ case 14: z->name = "ProAudioSpectrum-CDPC"; break;
+ case 15: z->name = "ProAudioSpectrum-16"; break;
+ default: return (203);
+ }
+ }
+
+ /* Read RPI port, resetting parity/interrupt state. */
+ inb (z->RPIR);
+
+ /* Test BSR: parity error, interrupt request and busy loss state
+ * should be cleared. */
+ if (inb (z->BSR) & (BSR_PARITY_ERROR |
+ BSR_INTERRUPT_REQUEST_ACTIVE | BSR_BUSY_ERROR)) {
+ PRINT (("nca: invalid bsr[0x%x]=%b\n", z->BSR,
+ inb (z->BSR), BSR_BITS));
+ return (1);
+ }
+
+ /* Reset the SCSI bus. */
+ outb (z->ICR, ICR_ASSERT_RST);
+ outb (z->ODR, 0);
+ /* Hold reset for at least 25 microseconds. */
+ DELAY (25);
+ /* Check that status cleared. */
+ if (inb (z->CSBR) != CSBR_RST) {
+ PRINT (("nca: invalid csbr[0x%x]=%b\n", z->CSBR,
+ inb (z->CSBR), CSBR_BITS));
+ outb (z->ICR, 0);
+ return (2);
+ }
+ /* Clear reset. */
+ outb (z->ICR, 0);
+ /* Wait a Bus Clear Delay (800 ns + bus free delay 800 ns). */
+ DELAY (2);
+
+ /* Enable data drivers. */
+ outb (z->ICR, ICR_ASSERT_DATA_BUS);
+
+ /* Check that data register is writable. */
+ for (i=0; i<256; ++i) {
+ outb (z->ODR, i);
+ DELAY (1);
+ if (inb (z->CSDR) != i) {
+ PRINT (("nca: ODR[0x%x] not writable: 0x%x should be 0x%x\n",
+ z->ODR, inb (z->CSDR), i));
+ outb (z->ICR, 0);
+ return (3);
+ }
+ }
+
+ /* Disable data drivers. */
+ outb (z->ICR, 0);
+
+ /* Check that data register is NOT writable. */
+ c = inb (z->CSDR);
+ for (i=0; i<256; ++i) {
+ outb (z->ODR, i);
+ DELAY (1);
+ if (inb (z->CSDR) != c) {
+ PRINT (("nca: ODR[0x%x] writable: 0x%x should be 0x%x\n",
+ z->ODR, inb (z->CSDR), c));
+ return (4);
+ }
+ }
+
+ /* Initialize the controller. */
+ outb (z->MR, z->parity);
+ outb (z->TCR, 0);
+ outb (z->SER, z->scsi_id);
+ return (0);
+}
+
+static char nca_description [80];
+static struct kern_devconf nca_kdc[NNCA] = {{
+ 0, 0, 0, "nca", 0, { MDDT_ISA, 0, "bio" },
+ isa_generic_externalize, 0, 0, ISA_EXTERNALLEN, &kdc_isa0, 0,
+ DC_BUSY, nca_description,
+}};
+
+/*
+ * Attach all sub-devices we can find.
+ */
+int nca_attach (struct isa_device *dev)
+{
+ int unit = dev->id_unit;
+ adapter_t *z = &ncadata[unit];
+
+ sprintf (nca_description, "%s SCSI controller", z->name);
+ printf ("nca%d: type %s%s\n", unit, z->name,
+ (dev->id_flags & FLAG_NOPARITY) ? ", no parity" : "");
+
+ /* fill in the prototype scsi_link */
+ z->sc_link.adapter_unit = unit;
+ z->sc_link.adapter_targ = z->scsi_addr;
+ z->sc_link.adapter = &nca_switch;
+ z->sc_link.device = &nca_dev;
+
+ /* ask the adapter what subunits are present */
+ scsi_attachdevs (&(z->sc_link));
+
+ if (dev->id_unit)
+ nca_kdc[dev->id_unit] = nca_kdc[0];
+ nca_kdc[dev->id_unit].kdc_unit = dev->id_unit;
+ nca_kdc[dev->id_unit].kdc_isa = dev;
+ dev_attach (&nca_kdc[dev->id_unit]);
+ return (1);
+}
+
+/*
+ * Return some information to the caller about
+ * the adapter and its capabilities.
+ */
+u_int32 nca_adapter_info (int unit)
+{
+ return (1);
+}
+
+void ncaminphys (struct buf *bp)
+{
+}
+
+/*
+ * Catch an interrupt from the adaptor.
+ */
+int ncaintr (int unit)
+{
+ adapter_t *z = &ncadata[unit];
+
+ PRINT (("nca%d: interrupt bsr=%b csbr=%b\n", unit,
+ inb (z->BSR), BSR_BITS, inb (z->CSBR), CSBR_BITS));
+ nca_start (z);
+ /* Reset interrupt state. */
+ inb (z->RPIR);
+ return (1);
+}
+
+/*
+ * This routine is used in the case when we have no IRQ line (z->irq == 0).
+ * It is called every timer tick and polls for reconnect from target.
+ */
+void nca_tick (void *arg)
+{
+ adapter_t *z = arg;
+ int x = splbio ();
+
+ z->timeout_active = 0;
+ nca_start (z);
+ /* Reset interrupt state. */
+ inb (z->RPIR);
+ if (z->disconnected_queue && ! z->timeout_active) {
+ timeout (nca_tick, z, 1);
+ z->timeout_active = 1;
+ }
+ splx (x);
+}
+
+/*
+ * Start a scsi operation given the command and the data address.
+ * Also needs the unit, target and lu. Get a free scb and set it up.
+ * Call send_scb. Either start timer or wait until done.
+ */
+int32 nca_scsi_cmd (struct scsi_xfer *xs)
+{
+ int unit = xs->sc_link->adapter_unit, flags = xs->flags, x = 0;
+ adapter_t *z = &ncadata[unit];
+ scb_t *scb;
+
+ /* PRINT (("nca%d/%d/%d command 0x%x\n", unit, xs->sc_link->target,
+ xs->sc_link->lun, xs->cmd->opcode)); */
+ if (xs->bp)
+ flags |= SCSI_NOSLEEP;
+ if (flags & ITSDONE) {
+ printf ("nca%d: already done?", unit);
+ xs->flags &= ~ITSDONE;
+ }
+ if (! (flags & INUSE)) {
+ printf ("nca%d: not in use?", unit);
+ xs->flags |= INUSE;
+ }
+ if (flags & SCSI_RESET)
+ printf ("nca%d: SCSI_RESET not implemented\n", unit);
+
+ if (! (flags & SCSI_NOMASK))
+ x = splbio ();
+
+ /* Get a free scb.
+ * If we can and have to, sleep waiting for one to come free. */
+ while (! (scb = z->free_scb)) {
+ if (flags & SCSI_NOSLEEP) {
+ xs->error = XS_DRIVER_STUFFUP;
+ if (! (flags & SCSI_NOMASK))
+ splx (x);
+ return (TRY_AGAIN_LATER);
+ }
+ tsleep ((caddr_t)&z->free_scb, PRIBIO, "ncascb", 0);
+ }
+ /* Get scb from free list. */
+ z->free_scb = scb->next;
+ scb->next = 0;
+ scb->flags = SCB_ACTIVE;
+
+ /* Put all the arguments for the xfer in the scb */
+ scb->xfer = xs;
+ scb->datalen = xs->datalen;
+ scb->data = xs->data;
+
+ /* Setup the scb to contain necessary values.
+ * The interesting values can be read from the xs that is saved.
+ * I therefore think that the structure can be kept very small.
+ * The driver doesn't use DMA so the scatter/gather is not needed? */
+ if (! z->queue) {
+ scb->next = z->queue;
+ z->queue = scb;
+ } else {
+ scb_t *q;
+
+ for (q=z->queue; q->next; q=q->next)
+ continue;
+ q->next = scb;
+ scb->next = 0; /* placed at the end of the queue */
+ }
+
+ /* Try to send this command to the board. */
+ nca_start (z);
+
+ /* Usually return SUCCESSFULLY QUEUED. */
+ if (! (flags & SCSI_NOMASK)) {
+ splx (x);
+ if (xs->flags & ITSDONE)
+ /* Timeout timer not started, already finished.
+ * Tried to return COMPLETE but the machine hanged
+ * with this. */
+ return (SUCCESSFULLY_QUEUED);
+ timeout (nca_timeout, (caddr_t) scb, (xs->timeout * hz) / 1000);
+ scb->flags |= SCB_TIMECHK;
+ PRINT (("nca%d/%d/%d command queued\n", unit,
+ xs->sc_link->target, xs->sc_link->lun));
+ return (SUCCESSFULLY_QUEUED);
+ }
+
+ /* If we can't use interrupts, poll on completion. */
+ if (! nca_poll (z, scb)) {
+ /* We timed out, so call the timeout handler manually,
+ * accounting for the fact that the clock is not running yet
+ * by taking out the clock queue entry it makes. */
+ nca_timeout ((void*) scb);
+
+ /* Because we are polling, take out the timeout entry
+ * nca_timeout made. */
+ untimeout (nca_timeout, (void*) scb);
+
+ if (! nca_poll (z, scb))
+ /* We timed out again... This is bad. Notice that
+ * this time there is no clock queue entry to remove. */
+ nca_timeout ((void*) scb);
+ }
+ /* PRINT (("nca%d/%d/%d command %s\n", unit,
+ xs->sc_link->target, xs->sc_link->lun,
+ xs->error ? "failed" : "done")); */
+ return (xs->error ? HAD_ERROR : COMPLETE);
+}
+
+/*
+ * Coroutine that runs as long as more work can be done.
+ * Both scsi_cmd() and intr() will try to start it in
+ * case it is not running.
+ * Always called with interrupts disabled.
+ */
+void nca_start (adapter_t *z)
+{
+ scb_t *q, *prev;
+again:
+ /* First check that if any device has tried
+ * a reconnect while we have done other things
+ * with interrupts disabled. */
+ if (nca_reselect (z))
+ goto again;
+
+ /* Search through the queue for a command
+ * destined for a target that's not busy. */
+ for (q=z->queue, prev=0; q; prev=q, q=q->next) {
+ /* Attempt to establish an I_T_L nexus here. */
+ if (IS_BUSY (z, q) || ! nca_select (z, q))
+ continue;
+
+ /* Remove the command from the issue queue. */
+ if (prev)
+ prev->next = q->next;
+ else
+ z->queue = q->next;
+ q->next = 0;
+
+ /* We are connected. Do the task. */
+ nca_information_transfer (z, q);
+ goto again;
+ }
+}
+
+void nca_timeout (void *arg)
+{
+ scb_t *scb = (scb_t*) arg;
+ int unit = scb->xfer->sc_link->adapter_unit;
+ adapter_t *z = &ncadata[unit];
+ int x = splbio ();
+
+ if (! (scb->xfer->flags & SCSI_NOMASK))
+ printf ("nca%d/%d/%d (%s%d) timed out\n", unit,
+ scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun,
+ scb->xfer->sc_link->device->name,
+ scb->xfer->sc_link->dev_unit);
+
+ /* If it has been through before, then a previous abort has failed,
+ * don't try abort again. */
+ if (! (scb->flags & SCB_ABORTED)) {
+ nca_abort (z, scb);
+ /* 2 seconds for the abort */
+ timeout (nca_timeout, (caddr_t)scb, 2*hz);
+ scb->flags |= (SCB_ABORTED | SCB_TIMECHK);
+ } else {
+ /* abort timed out */
+ scb->flags |= SCB_ABORTED;
+ scb->xfer->retries = 0;
+ nca_done (z, scb);
+ }
+ splx (x);
+}
+
+static inline void nca_sendbyte (adapter_t *z, u_char data)
+{
+ outb (z->ODR, data);
+ outb (z->ICR, ICR_ASSERT_DATA_BUS | ICR_ASSERT_ACK);
+ WAITFOR (! (inb (z->CSBR) & CSBR_REQ), 10000, "sendbyte");
+ outb (z->ICR, ICR_ASSERT_DATA_BUS);
+}
+
+static inline u_char nca_recvbyte (adapter_t *z)
+{
+ u_char data;
+
+ data = inb (z->CSDR);
+ outb (z->ICR, ICR_ASSERT_ACK);
+ WAITFOR (! (inb (z->CSBR) & CSBR_REQ), 10000, "recvbyte");
+ outb (z->ICR, 0);
+ return (data);
+}
+
+/*
+ * Establish I_T_L or I_T_L_Q nexus for new or existing command
+ * including ARBITRATION, SELECTION, and initial message out
+ * for IDENTIFY and queue messages.
+ * Return 1 if selection succeded.
+ */
+int nca_select (adapter_t *z, scb_t *scb)
+{
+ /* Set the phase bits to 0, otherwise the NCR5380 won't drive the
+ * data bus during SELECTION. */
+ outb (z->TCR, 0);
+
+ /* Start arbitration. */
+ outb (z->ODR, z->scsi_id);
+ outb (z->MR, MR_ARBITRATE);
+
+ /* Wait for arbitration logic to complete (20 usec) */
+ WAITFOR (inb (z->ICR) & ICR_ARBITRATION_IN_PROGRESS, 200, 0);
+ if (! (inb (z->ICR) & ICR_ARBITRATION_IN_PROGRESS)) {
+ PRINT (("nca%d/%d/%d no arbitration progress, bsr=%b csbr=%b\n",
+ z->sc_link.adapter_unit, scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun, inb (z->BSR), BSR_BITS,
+ inb (z->CSBR), CSBR_BITS));
+ outb (z->MR, z->parity);
+ return (0);
+ }
+ DELAY (3);
+
+ /* Check for lost arbitration. */
+ if ((inb (z->ICR) & ICR_LOST_ARBITRATION) ||
+ (inb (z->CSDR) >> 1 >> z->scsi_addr) ||
+ (inb (z->ICR) & ICR_LOST_ARBITRATION)) {
+ PRINT (("nca%d/%d/%d arbitration lost\n",
+ z->sc_link.adapter_unit, scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun));
+ outb (z->MR, z->parity);
+ return (0);
+ }
+
+ outb (z->ICR, ICR_ASSERT_SEL);
+ if (inb (z->ICR) & ICR_LOST_ARBITRATION) {
+ PRINT (("nca%d/%d/%d arbitration lost after SEL\n",
+ z->sc_link.adapter_unit, scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun));
+ outb (z->ICR, 0);
+ outb (z->MR, z->parity);
+ return (0);
+ }
+ DELAY (2);
+
+ /* Start selection, asserting the host and target ID's on the bus. */
+ outb (z->SER, 0);
+ outb (z->ODR, z->scsi_id | (1 << scb->xfer->sc_link->target));
+ outb (z->ICR, ICR_ASSERT_DATA_BUS | ICR_ASSERT_BSY |
+ ICR_ASSERT_SEL);
+
+ /* Finish arbitration, drop BSY. */
+ outb (z->MR, 0);
+ outb (z->ICR, ICR_ASSERT_DATA_BUS | ICR_ASSERT_SEL |
+ ICR_ASSERT_ATN);
+ DELAY (1);
+
+ /* The SCSI specification calls for a 250 ms timeout for the actual
+ * selection. */
+ WAITFOR (inb (z->CSBR) & CSBR_BSY, 100000, 0);
+ if (! (inb (z->CSBR) & CSBR_BSY)) {
+ /* The target does not respond. Not an error, though. */
+ PRINT (("nca%d/%d/%d target does not respond\n",
+ z->sc_link.adapter_unit, scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun));
+ outb (z->ICR, 0);
+ outb (z->SER, z->scsi_id);
+ outb (z->MR, z->parity);
+ scb->flags |= SCB_TIMEOUT;
+ return (0);
+ }
+
+ /* Clear SEL and SCSI id.
+ * Wait for start of REQ/ACK handshake. */
+ outb (z->ICR, ICR_ASSERT_DATA_BUS | ICR_ASSERT_ATN);
+ WAITFOR (inb (z->CSBR) & CSBR_REQ, 100000, 0);
+ if (! (inb (z->CSBR) & CSBR_REQ)) {
+ PRINT (("nca%d/%d/%d timeout waiting for REQ\n",
+ z->sc_link.adapter_unit, scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun));
+ outb (z->ICR, 0);
+ outb (z->SER, z->scsi_id);
+ outb (z->MR, z->parity);
+ scb->flags |= SCB_ERROR;
+ return (0);
+ }
+
+ /* Check for phase mismatch. */
+ if ((inb (z->CSBR) & PHASE_MASK) != PHASE_MSGOUT) {
+ /* This should not be taken as an error, but more like
+ * an unsupported feature!
+ * Should set a flag indicating that the target don't support
+ * messages, and continue without failure.
+ * (THIS IS NOT AN ERROR!) */
+ PRINT (("nca%d/%d/%d waiting for MSGOUT: invalid phase %s\n",
+ z->sc_link.adapter_unit, scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun,
+ PHASE_NAME (inb (z->CSBR) & PHASE_MASK)));
+ outb (z->ICR, 0);
+ outb (z->SER, z->scsi_id);
+ outb (z->MR, z->parity);
+ scb->flags |= SCB_ERROR;
+ return (0);
+ }
+
+ /* Allow disconnects. */
+ outb (z->TCR, PHASE_TO_TCR (PHASE_MSGOUT));
+ outb (z->ICR, ICR_ASSERT_DATA_BUS);
+ nca_sendbyte (z, MSG_IDENTIFY (scb->xfer->sc_link->lun));
+ outb (z->ICR, 0);
+ outb (z->SER, z->scsi_id);
+ outb (z->MR, z->parity);
+
+ SET_BUSY (z, scb);
+ return (1);
+}
+
+int nca_reselect (adapter_t *z)
+{
+ scb_t *q = 0, *prev = 0;
+ u_char msg, target_mask, lun;
+again:
+ /* Wait for a device to win the reselection phase. */
+ /* Signals this by asserting the I/O signal. */
+ if ((inb (z->CSBR) & (CSBR_SEL | CSBR_IO | CSBR_BSY)) !=
+ (CSBR_SEL | CSBR_IO))
+ return (0);
+
+ /* The data bus contains original initiator id ORed with target id. */
+ /* See that we really are the initiator. */
+ target_mask = inb (z->CSDR);
+ if (! (target_mask & z->scsi_id)) {
+ PRINT (("nca%d reselect not for me: mask=0x%x, csbr=%b\n",
+ z->sc_link.adapter_unit, target_mask,
+ inb (z->CSBR), CSBR_BITS));
+ goto again;
+ }
+
+ /* Find target who won. */
+ /* Host responds by asserting the BSY signal. */
+ /* Target should respond by deasserting the SEL signal. */
+ target_mask &= ~z->scsi_id;
+ outb (z->ICR, ICR_ASSERT_BSY);
+ WAITFOR (! (inb (z->CSBR) & CSBR_SEL), 10000, "SEL deassert");
+
+ /* Remove the busy status. */
+ /* Target should set the MSGIN phase. */
+ outb (z->ICR, 0);
+ WAITFOR (inb (z->CSBR) & CSBR_REQ, 10000, "MSGIN");
+
+ /* Hope we get an IDENTIFY message. */
+ msg = nca_msg_input (z);
+ if (MSG_ISIDENT (msg)) {
+ /* Find the command corresponding to the I_T_L or I_T_L_Q
+ * nexus we just restablished, and remove it from
+ * the disconnected queue. */
+ lun = (msg & 7);
+ for (q=z->disconnected_queue; q; prev=q, q=q->next) {
+ if (target_mask != (1 << q->xfer->sc_link->target))
+ continue;
+ if (lun != q->xfer->sc_link->lun)
+ continue;
+ if (prev)
+ prev->next = q->next;
+ else
+ z->disconnected_queue = q->next;
+ q->next = 0;
+ PRINT (("nca%d/%d/%d reselect done\n",
+ z->sc_link.adapter_unit,
+ ffs (target_mask) - 1, lun));
+ nca_information_transfer (z, q);
+ WAITFOR (! (inb (z->CSBR) & CSBR_BSY), 100000, "reselect !busy");
+ return (1);
+ }
+ } else
+ printf ("nca%d reselect: expecting IDENTIFY, got 0x%x\n",
+ z->sc_link.adapter_unit, msg);
+
+ /* Since we have an established nexus that we can't
+ * do anything with, we must abort it. */
+ nca_send_abort (z);
+ PRINT (("nca%d reselect aborted\n", z->sc_link.adapter_unit));
+ WAITFOR (! (inb (z->CSBR) & CSBR_BSY), 100000, "reselect abort !busy");
+ goto again;
+}
+
+/*
+ * Send an abort to the target.
+ * Return 1 success, 0 on failure.
+ * Called on splbio level.
+ */
+int nca_abort (adapter_t *z, scb_t *scb)
+{
+ scb_t *q, **prev;
+
+ /* If the command hasn't been issued yet, we simply remove it
+ * from the issue queue. */
+ prev = &z->queue;
+ for (q=z->queue; q; q=q->next) {
+ if (scb == q) {
+ (*prev) = q->next;
+ q->next = 0;
+ return (1);
+ }
+ prev = &q->next;
+ }
+
+ /* If the command is currently disconnected from the bus,
+ * we reconnect the I_T_L or I_T_L_Q nexus associated with it,
+ * go into message out, and send an abort message. */
+ for (q=z->disconnected_queue; q; q=q->next) {
+ if (scb != q)
+ continue;
+
+ if (! nca_select (z, scb))
+ return (0);
+ nca_send_abort (z);
+
+ prev = &z->disconnected_queue;
+ for (q=z->disconnected_queue; q; q=q->next) {
+ if (scb == q) {
+ *prev = q->next;
+ q->next = 0;
+ /* Set some type of error result
+ * for the operation. */
+ return (1);
+ }
+ prev = &q->next;
+ }
+ }
+
+ /* Command not found in any queue. */
+ return (0);
+}
+
+/*
+ * The task accomplished, mark the i/o control block as done.
+ * Always called with interrupts disabled.
+ */
+void nca_done (adapter_t *z, scb_t *scb)
+{
+ struct scsi_xfer *xs = scb->xfer;
+
+ if (scb->flags & SCB_TIMECHK)
+ untimeout (nca_timeout, (caddr_t) scb);
+
+ /* How much of the buffer was not touched. */
+ xs->resid = scb->datalen;
+
+ if (scb->flags != SCB_ACTIVE && ! (xs->flags & SCSI_ERR_OK))
+ if (scb->flags & (SCB_TIMEOUT | SCB_ABORTED))
+ xs->error = XS_TIMEOUT;
+ else if (scb->flags & SCB_ERROR)
+ xs->error = XS_DRIVER_STUFFUP;
+ else if (scb->flags & SCB_TBUSY)
+ xs->error = XS_BUSY;
+ else if (scb->flags & SCB_SENSE)
+ xs->error = XS_SENSE;
+
+ xs->flags |= ITSDONE;
+
+ /* Free the control block. */
+ scb->next = z->free_scb;
+ z->free_scb = scb;
+ scb->flags = SCB_FREE;
+
+ /* If there were none, wake anybody waiting for one to come free,
+ * starting with queued entries. */
+ if (! scb->next)
+ wakeup ((caddr_t) &z->free_scb);
+
+ scsi_done (xs);
+}
+
+/*
+ * Wait for completion of command in polled mode.
+ * Always called with interrupts masked out.
+ */
+int nca_poll (adapter_t *z, scb_t *scb)
+{
+ int count;
+
+ for (count=0; count<30; ++count) {
+ DELAY (1000); /* delay for a while */
+ nca_start (z); /* retry operation */
+ if (scb->xfer->flags & ITSDONE)
+ return (1); /* all is done */
+ if (scb->flags & SCB_TIMEOUT)
+ return (0); /* no target present */
+ }
+ return (0);
+}
+
+/*
+ * Perform NCR-53C400 pseudo-dma data transfer.
+ */
+void nca_53400_dma_xfer (adapter_t *z, int read, u_char **pdata, u_long *plen)
+{
+ /* Set dma direction. */
+ outb (z->CSR, read ? CSR_TRANSFER_DIRECTION : 0);
+
+ /* Enable dma mode. */
+ outb (z->MR, MR_DMA_MODE | (read ? z->parity : 0));
+
+ /* Start dma transfer. */
+ outb (read ? z->SDIR : z->SDSR, 0);
+
+ /* Set up clock counter. */
+ outb (z->CCR, *plen/128);
+
+ for (; *plen>=128; *plen-=128, *pdata+=128) {
+ /* Wait for 53C400 host buffer ready. */
+ WAITFOR (! (inb (z->CSR) & CSR_HOST_BUF_NOT_READY), 100000, 0);
+ if (inb (z->CSR) & CSR_HOST_BUF_NOT_READY)
+ break;
+
+ /* Transfer 128 bytes of data. */
+ if (read)
+ insw (z->HBR, *pdata, 64);
+ else
+ outsw (z->HBR, *pdata, 64);
+ }
+
+ /* Wait for 5380 registers ready. */
+ WAITFOR (inb (z->CSR) & CSR_5380_ENABLE, 10000, 0);
+ if (! (inb (z->CSR) & CSR_5380_ENABLE)) {
+ /* Reset 53C400. */
+ PRINT (("nca%d: reset: pseudo-dma incomplete, csr=%b\n",
+ z->sc_link.adapter_unit, inb (z->CSR), CSR_BITS));
+ outb (z->CSR, CSR_5380_ENABLE);
+ outb (z->CSR, 0);
+ }
+
+ /* Wait for FIFO flush on write. */
+ if (! read)
+ WAITFOR (inb (z->TCR) & TCR_LAST_BYTE_SENT, 10000, "last byte");
+
+ /* Clear dma mode. */
+ outb (z->MR, z->parity);
+
+ /* Re-enable interrupts. */
+ outb (z->CSR, z->irq ? CSR_5380_INTR : 0);
+}
+
+/*
+ * Perform PAS-16 pseudo-dma data transfer.
+ */
+void nca_pas_dma_xfer (adapter_t *z, int read, u_char **pdata, u_long *plen)
+{
+ /* Enable dma mode. */
+ outb (z->MR, MR_DMA_MODE | (read ? z->parity : 0));
+
+ /* Start dma transfer. */
+ outb (read ? z->SDIR : z->SDSR, 0);
+
+ for (; *plen>=512; *plen-=512, *pdata+=512) {
+ /* Wait for pseudo-DMA request. */
+ WAITFOR (inb (z->PSTAT) & PAS16_STAT_DREQ, 10000, "pseudo-dma");
+ if (! (inb (z->PSTAT) & PAS16_STAT_DREQ))
+ break;
+
+ /* Transfer 512 bytes of data. */
+ if (read)
+ insb (z->PDATA, *pdata, 512);
+ else
+ outsb (z->PDATA, *pdata, 512);
+ }
+
+ /* Clear dma mode. */
+ outb (z->MR, z->parity);
+}
+
+/*
+ * Send data to the target.
+ */
+void nca_data_output (adapter_t *z, u_char **pdata, u_long *plen)
+{
+ u_char *data = *pdata;
+ u_long len = *plen;
+
+ outb (z->ICR, ICR_ASSERT_DATA_BUS);
+ if (z->type == CTLR_NCR_53C400 && len%128 == 0)
+ /* Use NCR-53C400 pseudo-dma for data transfer. */
+ nca_53400_dma_xfer (z, 0, &data, &len);
+ else if (z->type == CTLR_PAS_16 && len%512 == 0)
+ /* Use PAS-16 pseudo-dma for data transfer. */
+ nca_pas_dma_xfer (z, 0, &data, &len);
+ else
+ for (;;) {
+ /* Check SCSI bus phase. */
+ u_char s = inb (z->CSBR) ^ (CSBR_BSY | PHASE_DATAOUT);
+ if (s & (CSBR_BSY | PHASE_MASK))
+ break;
+
+ /* Wait for REQ. */
+ if (! (s & CSBR_REQ))
+ continue;
+
+ /* Output data. */
+ outb (z->ODR, *data++);
+
+ /* Assert ACK and wait for REQ deassert,
+ * with irqs disabled. */
+ disable_intr ();
+ outb (z->ICR, ICR_ASSERT_ACK | ICR_ASSERT_DATA_BUS);
+ WAITFOR (! (inb (z->CSBR) & CSBR_REQ), 1000, 0);
+ enable_intr ();
+
+ /* Deassert ACK. */
+ outb (z->ICR, ICR_ASSERT_DATA_BUS);
+ --len;
+ }
+ outb (z->ICR, 0);
+ PRINT (("nca (DATAOUT) send %ld bytes\n", *plen - len));
+ *plen = len;
+ *pdata = data;
+}
+
+/*
+ * Receive data from the target.
+ */
+void nca_data_input (adapter_t *z, u_char **pdata, u_long *plen)
+{
+ u_char *data = *pdata;
+ u_long len = *plen;
+
+ if (z->type == CTLR_NCR_53C400 && len%128 == 0)
+ /* Use NCR-53C400 pseudo-dma for data transfer. */
+ nca_53400_dma_xfer (z, 1, &data, &len);
+ else if (z->type == CTLR_PAS_16 && len%512 == 0)
+ /* Use PAS-16 pseudo-dma for data transfer. */
+ nca_pas_dma_xfer (z, 1, &data, &len);
+ else
+ for (;;) {
+ /* Check SCSI bus phase. */
+ u_char s = inb (z->CSBR) ^ (CSBR_BSY | PHASE_DATAIN);
+ if (s & (CSBR_BSY | PHASE_MASK))
+ break;
+
+ /* Wait for REQ. */
+ if (! (s & CSBR_REQ))
+ continue;
+
+ /* Input data. */
+ *data++ = inb (z->CSDR);
+
+ /* Assert ACK and wait for REQ deassert,
+ * with irqs disabled. */
+ disable_intr ();
+ outb (z->ICR, ICR_ASSERT_ACK);
+ WAITFOR (! (inb (z->CSBR) & CSBR_REQ), 1000, 0);
+ enable_intr ();
+
+ /* Deassert ACK. */
+ outb (z->ICR, 0);
+ --len;
+ }
+ PRINT (("nca (DATAIN) got %ld bytes\n", *plen - len));
+ *plen = len;
+ *pdata = data;
+}
+
+/*
+ * Send the command to the target.
+ */
+void nca_cmd_output (adapter_t *z, u_char *cmd, int cmdlen)
+{
+ PRINT (("nca%d send command (%d bytes) ", z->sc_link.adapter_unit,
+ cmdlen));
+
+ outb (z->ICR, ICR_ASSERT_DATA_BUS);
+ while (cmdlen) {
+ /* Check for target disconnect. */
+ u_char sts = inb (z->CSBR);
+ if (! (sts & CSBR_BSY))
+ break;
+
+ /* Check for phase mismatch. */
+ if ((sts & PHASE_MASK) != PHASE_CMDOUT) {
+ printf ("nca: sending command: invalid phase %s\n",
+ PHASE_NAME (sts & PHASE_MASK));
+ break;
+ }
+
+ /* Wait for REQ. */
+ if (! (sts & CSBR_REQ))
+ continue;
+
+ PRINT (("-%x", *cmd));
+ nca_sendbyte (z, *cmd++);
+ --cmdlen;
+ }
+ outb (z->ICR, 0);
+ PRINT (("\n"));
+}
+
+/*
+ * Send the message to the target.
+ */
+void nca_send_abort (adapter_t *z)
+{
+ u_char sts;
+
+ outb (z->ICR, ICR_ASSERT_ATN);
+
+ /* Wait for REQ, after which the phase bits will be valid. */
+ WAITFOR (inb (z->CSBR) & CSBR_REQ, 1000000, "abort message");
+ sts = inb (z->CSBR);
+ if (! (sts & CSBR_REQ))
+ goto ret;
+
+ /* Check for phase mismatch. */
+ if ((sts & PHASE_MASK) != PHASE_MSGOUT) {
+ printf ("nca: sending MSG_ABORT: invalid phase %s\n",
+ PHASE_NAME (sts & PHASE_MASK));
+ goto ret;
+ }
+
+ outb (z->ICR, ICR_ASSERT_DATA_BUS);
+ outb (z->TCR, PHASE_TO_TCR (PHASE_MSGOUT));
+ nca_sendbyte (z, MSG_ABORT);
+
+ PRINT (("nca%d send MSG_ABORT\n", z->sc_link.adapter_unit));
+ret: outb (z->ICR, 0);
+}
+
+/*
+ * Get the message from the target.
+ * Return the length of the received message.
+ */
+u_char nca_msg_input (adapter_t *z)
+{
+ u_char sts, msg;
+
+ /* Wait for REQ, after which the phase bits will be valid. */
+ WAITFOR (inb (z->CSBR) & CSBR_REQ, 1000000, "message input");
+ sts = inb (z->CSBR);
+ if (! (sts & CSBR_REQ))
+ return (MSG_ABORT);
+
+ /* Check for phase mismatch.
+ * Reached if the target decides that it has finished the transfer. */
+ if ((sts & PHASE_MASK) != PHASE_MSGIN) {
+ printf ("nca: sending message: invalid phase %s\n",
+ PHASE_NAME (sts & PHASE_MASK));
+ return (MSG_ABORT);
+ }
+
+ /* Do actual transfer from SCSI bus to memory. */
+ outb (z->TCR, PHASE_TO_TCR (PHASE_MSGIN));
+ msg = nca_recvbyte (z);
+ PRINT (("nca%d (MSG_INPUT) got 0x%x\n", z->sc_link.adapter_unit, msg));
+ return (msg);
+}
+
+/*
+ * Send request-sense op to the target.
+ * Return 1 success, 0 on failure.
+ * Called on splbio level.
+ */
+int nca_sense (adapter_t *z, scb_t *scb)
+{
+ u_char cmd[6], status, msg, *data;
+ u_long len;
+
+ /* Wait for target to disconnect. */
+ WAITFOR (! (inb (z->CSBR) & CSBR_BSY), 100000, "sense bus free");
+ if (inb (z->CSBR) & CSBR_BSY)
+ return (0);
+
+ /* Select the target again. */
+ if (! nca_select (z, scb))
+ return (0);
+
+ /* Wait for CMDOUT phase. */
+ WAITFOR (inb (z->CSBR) & CSBR_REQ, 100000, "sense CMDOUT");
+ if (! (inb (z->CSBR) & CSBR_REQ) ||
+ (inb (z->CSBR) & PHASE_MASK) != PHASE_CMDOUT)
+ return (0);
+ outb (z->TCR, PHASE_TO_TCR (PHASE_CMDOUT));
+
+ /* Send command. */
+ len = sizeof (scb->xfer->sense);
+ cmd[0] = REQUEST_SENSE;
+ cmd[1] = scb->xfer->sc_link->lun << 5;
+ cmd[2] = 0;
+ cmd[3] = 0;
+ cmd[4] = len;
+ cmd[5] = 0;
+ nca_cmd_output (z, cmd, sizeof (cmd));
+
+ /* Wait for DATAIN phase. */
+ WAITFOR (inb (z->CSBR) & CSBR_REQ, 100000, "sense DATAIN");
+ if (! (inb (z->CSBR) & CSBR_REQ) ||
+ (inb (z->CSBR) & PHASE_MASK) != PHASE_DATAIN)
+ return (0);
+ outb (z->TCR, PHASE_TO_TCR (PHASE_DATAIN));
+
+ data = (u_char*) &scb->xfer->sense;
+ nca_data_input (z, &data, &len);
+ PRINT (("nca%d sense %x-%x-%x-%x-%x-%x-%x-%x\n",
+ z->sc_link.adapter_unit, scb->xfer->sense.error_code,
+ scb->xfer->sense.ext.extended.segment,
+ scb->xfer->sense.ext.extended.flags,
+ scb->xfer->sense.ext.extended.info[0],
+ scb->xfer->sense.ext.extended.info[1],
+ scb->xfer->sense.ext.extended.info[2],
+ scb->xfer->sense.ext.extended.info[3],
+ scb->xfer->sense.ext.extended.extra_len));
+
+ /* Wait for STATIN phase. */
+ WAITFOR (inb (z->CSBR) & CSBR_REQ, 100000, "sense STATIN");
+ if (! (inb (z->CSBR) & CSBR_REQ) ||
+ (inb (z->CSBR) & PHASE_MASK) != PHASE_STATIN)
+ return (0);
+ outb (z->TCR, PHASE_TO_TCR (PHASE_STATIN));
+
+ status = nca_recvbyte (z);
+
+ /* Wait for MSGIN phase. */
+ WAITFOR (inb (z->CSBR) & CSBR_REQ, 100000, "sense MSGIN");
+ if (! (inb (z->CSBR) & CSBR_REQ) ||
+ (inb (z->CSBR) & PHASE_MASK) != PHASE_MSGIN)
+ return (0);
+ outb (z->TCR, PHASE_TO_TCR (PHASE_MSGIN));
+
+ msg = nca_recvbyte (z);
+
+ if (status != 0 || msg != 0)
+ printf ("nca%d: bad sense status=0x%x, msg=0x%x\n",
+ z->sc_link.adapter_unit, status, msg);
+ return (1);
+}
+
+/*
+ * Do the transfer. We know we are connected. Update the flags,
+ * call nca_done when task accomplished. Dialog controlled by the target.
+ * Always called with interrupts disabled.
+ */
+void nca_information_transfer (adapter_t *z, scb_t *scb)
+{
+ u_char *data = scb->data; /* current data buffer */
+ u_long datalen = scb->datalen; /* current data transfer size */
+ register u_char sts;
+ u_char msg;
+
+ while ((sts = inb (z->CSBR)) & CSBR_BSY) {
+ /* We only have a valid SCSI phase when REQ is asserted. */
+ if (! (sts & CSBR_REQ))
+ continue;
+ if (inb (z->BSR) & BSR_PARITY_ERROR) {
+ int target = scb->xfer->sc_link->target;
+ if (++z->target[target].perrcnt <= 8)
+ printf ("nca%d/%d/%d parity error\n",
+ z->sc_link.adapter_unit, target,
+ scb->xfer->sc_link->lun);
+ if (z->target[target].perrcnt == 8)
+ printf ("nca%d/%d/%d too many parity errors, not logging any more\n",
+ z->sc_link.adapter_unit, target,
+ scb->xfer->sc_link->lun);
+ /* Clear parity error. */
+ inb (z->RPIR);
+ }
+ outb (z->TCR, PHASE_TO_TCR (sts & PHASE_MASK));
+ switch (sts & PHASE_MASK) {
+ case PHASE_DATAOUT:
+ if (datalen <= 0) {
+ printf ("nca%d/%d/%d data length underflow\n",
+ z->sc_link.adapter_unit,
+ scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun);
+ /* send zero byte */
+ outb (z->ICR, ICR_ASSERT_DATA_BUS);
+ nca_sendbyte (z, 0);
+ outb (z->ICR, 0);
+ break;
+ }
+ nca_data_output (z, &data, &datalen);
+ break;
+ case PHASE_DATAIN:
+ if (datalen <= 0) {
+ /* Get extra data. Some devices (e.g. CDROMs)
+ * use fixed-length blocks (e.g. 2k),
+ * even if we need less. */
+ PRINT (("@"));
+ nca_recvbyte (z);
+ break;
+ }
+ nca_data_input (z, &data, &datalen);
+ break;
+ case PHASE_CMDOUT:
+ nca_cmd_output (z, (u_char*) scb->xfer->cmd,
+ scb->xfer->cmdlen);
+ break;
+ case PHASE_STATIN:
+ scb->xfer->status = nca_recvbyte (z);
+ PRINT (("nca%d/%d/%d (STATIN) got 0x%x\n",
+ z->sc_link.adapter_unit,
+ scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun,
+ (u_char) scb->xfer->status));
+ break;
+ case PHASE_MSGOUT:
+ /* Send no-op message. */
+ outb (z->ICR, ICR_ASSERT_DATA_BUS);
+ nca_sendbyte (z, MSG_NOP);
+ outb (z->ICR, 0);
+ PRINT (("nca%d/%d/%d (MSGOUT) send NOP\n",
+ z->sc_link.adapter_unit,
+ scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun));
+ break;
+ case PHASE_MSGIN:
+ /* Don't handle multi-byte messages here, because they
+ * should not be present here. */
+ msg = nca_recvbyte (z);
+ PRINT (("nca%d/%d/%d (MSGIN) got 0x%x\n",
+ z->sc_link.adapter_unit,
+ scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun, msg));
+ switch (msg) {
+ case MSG_COMMAND_COMPLETE:
+ scb->data = data;
+ scb->datalen = datalen;
+ /* In the case of check-condition status,
+ * perform the request-sense op. */
+ switch (scb->xfer->status & 0x1e) {
+ case SCSI_CHECK:
+ if (nca_sense (z, scb))
+ scb->flags = SCB_SENSE;
+ break;
+ case SCSI_BUSY:
+ scb->flags = SCB_TBUSY;
+ break;
+ }
+ goto done;
+ case MSG_ABORT:
+ printf ("nca: command aborted by target\n");
+ scb->flags = SCB_ABORTED;
+ goto done;
+ case MSG_MESSAGE_REJECT:
+ printf ("nca: message rejected\n");
+ scb->flags = SCB_ABORTED;
+ goto done;
+ case MSG_DISCONNECT:
+ scb->next = z->disconnected_queue;
+ z->disconnected_queue = scb;
+ if (! z->irq && ! z->timeout_active) {
+ timeout (nca_tick, z, 1);
+ z->timeout_active = 1;
+ }
+ PRINT (("nca%d/%d/%d disconnected\n",
+ z->sc_link.adapter_unit,
+ scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun));
+ goto ret;
+ case MSG_SAVE_POINTERS:
+ scb->data = data;
+ scb->datalen = datalen;
+ break;
+ case MSG_RESTORE_POINTERS:
+ data = scb->data;
+ datalen = scb->datalen;
+ break;
+ default:
+ printf ("nca%d/%d/%d unknown message: 0x%x\n",
+ z->sc_link.adapter_unit,
+ scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun, msg);
+ break;
+ }
+ break;
+ default:
+ printf ("nca: unknown phase: %b\n", sts, CSBR_BITS);
+ break;
+ }
+ }
+ printf ("nca%d/%d/%d unexpected target disconnect\n",
+ z->sc_link.adapter_unit, scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun);
+ scb->flags = SCB_ERROR;
+done:
+ CLEAR_BUSY (z, scb);
+ nca_done (z, scb);
+ret:
+ outb (z->ICR, 0);
+ outb (z->TCR, 0);
+ outb (z->SER, z->scsi_id);
+ WAITFOR (! (inb (z->CSBR) & CSBR_BSY), 100000, "xfer bus free");
+}
+#endif /* NNCA */
OpenPOWER on IntegriCloud