summaryrefslogtreecommitdiff
path: root/sys/i386/isa/seagate.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/i386/isa/seagate.c')
-rw-r--r--sys/i386/isa/seagate.c1506
1 files changed, 1506 insertions, 0 deletions
diff --git a/sys/i386/isa/seagate.c b/sys/i386/isa/seagate.c
new file mode 100644
index 0000000000000..8b2be3ff63d57
--- /dev/null
+++ b/sys/i386/isa/seagate.c
@@ -0,0 +1,1506 @@
+/*
+ * (Free/Net/386)BSD ST01/02, Future Domain TMC-885, TMC-950 SCSI driver for
+ * Julians SCSI-code
+ *
+ * Copyright 1994, Kent Palmkvist (kentp@isy.liu.se)
+ * Copyright 1994, Robert Knier (rknier@qgraph.com)
+ * Copyright 1992, 1994 Drew Eckhardt (drew@colorado.edu)
+ * Copyright 1994, Julian Elischer (julian@tfs.com)
+ * Copyright 1994-1995, Serge Vakulenko (vak@cronyx.ru)
+ * Copyright 1995 Stephen Hocking (sysseh@devetir.qld.gov.au)
+ *
+ * Others that has contributed by example code is
+ * Glen Overby (overby@cray.com)
+ * Tatu Yllnen
+ * Brian E Litzinger
+ *
+ * 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.
+ */
+
+/*
+ * kentp 940307 alpha version based on newscsi-03 version of Julians SCSI-code
+ * kentp 940314 Added possibility to not use messages
+ * rknier 940331 Added fast transfer code
+ * rknier 940407 Added assembler coded data transfers
+ * vak 941226 New probe algorithm, based on expected behaviour
+ * instead of BIOS signatures analysis, better timeout handling,
+ * new asm fragments for data input/output, target-dependent
+ * delays, device flags, polling mode, generic cleanup
+ * vak 950115 Added request-sense ops
+ * seh 950701 Fixed up Future Domain TMC-885 problems with disconnects,
+ * weird phases and the like. (we could probably investigate
+ * what the board's idea of the phases are, but that requires
+ * doco that I don't have). Note that it is slower than the
+ * 2.0R driver with both SEA_BLINDTRANSFER & SEA_ASSEMBLER
+ * defined by a factor of more than 2. I'll look at that later!
+ * seh 950712 The performance release 8^). Put in the blind transfer code
+ * from the 2.0R source. Don't use it by commenting out the
+ * SEA_BLINDTRANSFER below. Note that it only kicks in during
+ * DATAOUT or DATAIN and then only when the transfer is a
+ * multiple of BLOCK_SIZE bytes (512). Most devices fit into
+ * that category, with the possible exception of scanners and
+ * some of the older MO drives.
+ *
+ * $Id: seagate.c,v 1.30 1998/07/15 09:38:10 bde Exp $
+ */
+
+/*
+ * What should really be done:
+ *
+ * Restructure interrupt enable/disable code (runs too long with int disabled)
+ * Add code to handle Future Domain 840, 841, 880 and 881
+ * Add code to use tagged commands in SCSI2
+ * Add code to handle slow devices better (sleep if device not disconnecting)
+ * Fix unnecessary interrupts
+ */
+
+/* Note to users trying to share a disk between DOS and unix:
+ * The ST01/02 is a translating host-adapter. It is not giving DOS
+ * the same number of heads/tracks/sectors as specified by the disk.
+ * It is therefore important to look at what numbers DOS thinks the
+ * disk has. Use these to disklabel your disk in an appropriate manner
+ *
+ * About ST02+IDE coexistence: the original Seagate ST02
+ * BIOS cannot coexist with IDE or any other disk controller
+ * because it does not share BIOS disk drive numbers (80h, 81h)
+ * with others. New probing code allows using ST02 controller
+ * without BIOS: just unplug the ST02 BIOS chip from the board.
+ *
+ * Another problem is the floppy adapter on ST02 which could not be
+ * disabled by jumpers. I commonly use ST02 adapter as a cheap solution
+ * for atttaching the tape and CD-ROM drives, and an extra floppy controller
+ * is just a headache. I found a simple workaround: cutting off
+ * the AEN signal (A11 contact on ISA connector). AEN then goes high and
+ * disables the floppy adapter port address decoder.
+ *
+ * I also had a problem with ST02 conflicting with IDE during
+ * IDE data write phase. It seems than ST02 makes some noise
+ * on /IOW line. The /IOW line is used only for floppy controller
+ * part of ST02, and because I don't need it, I cut off the /IOW (contact B13)
+ * and it helped. (vak)
+ *
+ * Tested on the following hardware:
+ * Adapter: Seagate ST02
+ * Disk: HP D1686
+ * Streamers: Archive Viper 150, Wangtek 5525
+ * CD-ROMs: Toshiba XM-3401, NEC CDR-25
+ *
+ * Maximum data rate is about 270-280 kbytes/sec (on 386DX/40).
+ * (vak)
+ */
+#undef DEBUG
+
+#include "sea.h"
+#if NSEA > 0
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <machine/clock.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+#include <vm/pmap.h>
+
+#include <i386/isa/isa_device.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 */
+#define SEA_BLINDTRANSFER 1 /* for quicker than quick xfers */
+/*
+ * Define config flags
+ */
+#define FLAG_NOPARITY 0x01 /* disable SCSI bus parity check */
+
+/*
+ * Board CONTROL register
+ */
+#define CMD_RST 0x01 /* scsi reset */
+#define CMD_SEL 0x02 /* scsi select */
+#define CMD_BSY 0x04 /* scsi busy */
+#define CMD_ATTN 0x08 /* scsi attention */
+#define CMD_START_ARB 0x10 /* start arbitration bit */
+#define CMD_EN_PARITY 0x20 /* enable scsi parity generation */
+#define CMD_INTR 0x40 /* enable scsi interrupts */
+#define CMD_DRVR_ENABLE 0x80 /* scsi enable */
+
+/*
+ * Board STATUS register
+ */
+#define STAT_BSY 0x01 /* scsi busy */
+#define STAT_MSG 0x02 /* scsi msg */
+#define STAT_IO 0x04 /* scsi I/O */
+#define STAT_CD 0x08 /* scsi C/D */
+#define STAT_REQ 0x10 /* scsi req */
+#define STAT_SEL 0x20 /* scsi select */
+#define STAT_PARITY 0x40 /* parity error bit */
+#define STAT_ARB_CMPL 0x80 /* arbitration complete bit */
+#define STAT_BITS "\20\1bsy\2msg\3i/o\4c/d\5req\6sel\7parity\10arb"
+
+/*
+ * SCSI bus phases
+ */
+#define PHASE_MASK (STAT_MSG | STAT_CD | STAT_IO)
+#define PHASE_DATAOUT 0
+#define PHASE_DATAIN STAT_IO
+#define PHASE_CMDOUT STAT_CD
+#define PHASE_STATIN (STAT_CD | STAT_IO)
+#define PHASE_MSGOUT (STAT_MSG | STAT_CD)
+#define PHASE_MSGIN (STAT_MSG | STAT_CD | STAT_IO)
+#define PHASE_NAME(ph) phase_name[(ph)>>2]
+
+static char *phase_name[] = {
+ "DATAOUT", "Phase1?", "Phase2?", "Phase3?",
+ "DATAIN", "Phase5?", "Phase6?", "Phase7?",
+ "CMDOUT", "Phase9?", "MSGOUT", "Phase11?",
+ "STATIN", "Phase13?", "MSGIN", "Phase15?",
+};
+
+/*
+ * 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_t datalen; /* bytes remaining to transfer */
+} scb_t;
+
+typedef enum {
+ CTLR_NONE,
+ CTLR_SEAGATE,
+ CTLR_FUTURE_DOMAIN
+} ctlr_t;
+
+/*
+ * Flags for waiting for REQ deassert during some SCSI bus phases.
+ */
+typedef struct {
+ unsigned cmdout1 : 1; /* after CMDOUT[0] byte */
+ unsigned cmdout : 1; /* after CMDOUT[1..N] bytes */
+ unsigned msgout : 1; /* after MSGOUT byte */
+ unsigned statin : 1; /* after STATIN byte */
+} phase_t;
+
+/*
+ * Data structure describing the target state.
+ */
+typedef struct {
+ struct adapter *adapter; /* pointer to the adapter structure */
+ u_char busy; /* mask of busy luns at device target */
+ u_long perrcnt; /* counter of target parity errors */
+ phase_t ndelay; /* "don't delay" flags */
+ phase_t init; /* "initialized" flags */
+} target_t;
+
+/*
+ * Data structure describing current status of the scsi bus. One for each
+ * controller card.
+ */
+typedef struct adapter {
+ ctlr_t type; /* Seagate or Future Domain */
+ char *name; /* adapter name */
+ volatile u_char *addr; /* base address for card */
+
+ volatile u_char *CONTROL; /* address of control register */
+ volatile u_char *STATUS; /* address of status register */
+ volatile u_char *DATA; /* address of data 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;
+
+static adapter_t seadata[NSEA];
+
+#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,message) {\
+ register u_long cnt = 100000; char *_msg = message;\
+ while (cnt-- && ! (condition)) continue;\
+ if (cnt == -1 && _msg)\
+ printf ("sea: %s timeout\n", _msg); }
+
+#define WAITFOR10(condition,message) {\
+ register u_long cnt = 1000000; char *_msg = message;\
+ while (cnt-- && ! (condition)) continue;\
+ if (cnt == -1 && _msg)\
+ printf ("sea: %s timeout\n", _msg); }
+
+/*
+ * Seagate adapter does not support in hardware
+ * waiting for REQ deassert after transferring each data byte.
+ * We must do it in software.
+ * The problem is that some SCSI devices deassert REQ so fast that
+ * we can miss it. We the flag for each target sayind if we should (not)
+ * wait for REQ deassert. This flag is initialized when the first
+ * operation on the target is done.
+ * 1) Test if we don't need to wait for REQ deassert (`nodelay' flag).
+ * Initially the flag is off, i.e. wait. If the flag is set,
+ * go to the step 4.
+ * 2) Wait for REQ deassert (call sea_wait_for_req_deassert function).
+ * If REQ deassert got, go to the step 4. If REQ did not cleared
+ * during timeout period, go to the next step.
+ * 3) If `nodelay' flag did not initialized yet (`init' flag),
+ * then set `ndelay' flag.
+ * 4) Set `init' flag. Done.
+ */
+#define WAITREQ(t,op,cnt) {\
+ if (! (t)->ndelay.op &&\
+ ! sea_wait_for_req_deassert ((t)->adapter, cnt, #op) &&\
+ ! (t)->init.op)\
+ (t)->ndelay.op = 1;\
+ (t)->init.op = 1; }
+
+static int sea_probe (struct isa_device *dev);
+static int sea_detect (adapter_t *z, struct isa_device *dev);
+static int sea_attach (struct isa_device *dev);
+static int32_t sea_scsi_cmd (struct scsi_xfer *xs);
+static u_int32_t sea_adapter_info (int unit);
+static void sea_timeout (void *scb);
+static void seaminphys (struct buf *bp);
+static void sea_done (adapter_t *z, scb_t *scb);
+static void sea_start (adapter_t *z);
+static void sea_information_transfer (adapter_t *z, scb_t *scb);
+static int sea_poll (adapter_t *z, scb_t *scb);
+static int sea_init (adapter_t *z);
+static int sea_reselect (adapter_t *z);
+static int sea_select (volatile adapter_t *z, scb_t *scb);
+static int sea_abort (adapter_t *z, scb_t *scb);
+static void sea_send_abort (adapter_t *z);
+static u_char sea_msg_input (adapter_t *z);
+static void sea_tick (void *arg);
+static int sea_sense (adapter_t *z, scb_t *scb);
+static void sea_data_output (adapter_t *z, u_char **pdata, u_long *plen);
+static void sea_data_input (adapter_t *z, u_char **pdata, u_long *plen);
+static void sea_cmd_output (target_t *z, u_char *cmd, int cmdlen);
+
+static struct scsi_adapter sea_switch = {
+ sea_scsi_cmd, seaminphys, 0, 0, sea_adapter_info, "sea", {0},
+};
+static struct scsi_device sea_dev = { NULL, NULL, NULL, NULL, "sea", 0, {0} };
+struct isa_driver seadriver = { sea_probe, sea_attach, "sea" };
+
+/* FD TMC885's can't handle detach & re-attach */
+static int sea_select_cmd = CMD_DRVR_ENABLE | CMD_ATTN;
+
+/*
+ * 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 sea_probe (struct isa_device *dev)
+{
+ adapter_t *z = &seadata[dev->id_unit];
+ static const int addrtab[] = {
+ 0xc8000, 0xca000, 0xcc000, 0xce000, 0xdc000, 0xde000, 0,
+ };
+ int i;
+
+ /* Init fields used by our routines */
+ z->parity = (dev->id_flags & FLAG_NOPARITY) ? 0 : CMD_EN_PARITY;
+ 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].adapter = z;
+ 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;
+
+ /* Detect the adapter. */
+ dev->id_msize = 0x4000;
+ if (! dev->id_maddr)
+ for (i=0; addrtab[i]; ++i) {
+ dev->id_maddr = (u_char*) KERNBASE + addrtab[i];
+ if (sea_detect (z, dev))
+ return (1);
+ }
+ else if (sea_detect (z, dev))
+ return (1);
+
+ bzero (z, sizeof (*z));
+ return (0);
+}
+
+int sea_detect (adapter_t *z, struct isa_device *dev)
+{
+ z->addr = dev->id_maddr;
+
+ /* Try Seagate. */
+ z->type = CTLR_SEAGATE;
+ z->name = "Seagate ST01/ST02";
+ z->CONTROL = z->addr + 0x1a00; /* ST01/ST02 register offsets */
+ z->STATUS = z->addr + 0x1a00;
+ z->DATA = z->addr + 0x1c00;
+ if (sea_init (z) == 0)
+ return (1);
+
+ /* Try Future Domain. */
+ z->type = CTLR_FUTURE_DOMAIN;
+ z->name = "Future Domain TMC-885/TMC-950";
+ z->CONTROL = z->addr + 0x1c00; /* TMC-885/TMC-950 reg. offsets */
+ z->STATUS = z->addr + 0x1c00;
+ z->DATA = z->addr + 0x1e00;
+ /* FD TMC885's can't handle detach & re-attach */
+ sea_select_cmd = CMD_DRVR_ENABLE;
+ /* FD TMC-885 is supposed to be at id 6. How strange. */
+ z->scsi_addr = HOST_SCSI_ADDR - 1;
+ z->scsi_id = 1 << z->scsi_addr;
+ if (sea_init (z) == 0)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * Probe the adapter, and if found, reset the board and the scsi bus.
+ * Return 0 if the adapter found.
+ */
+int sea_init (adapter_t *z)
+{
+ volatile u_char *p;
+ int i, c;
+
+ /* Check that STATUS..STATUS+200h are equal. */
+ p = z->STATUS;
+ c = *p;
+ if (c == 0xff)
+ return (2);
+ while (++p < z->STATUS+0x200)
+ if (*p != c)
+ return (3);
+
+ /* Check that DATA..DATA+200h are equal. */
+ for (p=z->DATA, c= *p++; p<z->DATA+0x200; ++p)
+ if (*p != c)
+ return (4);
+
+ /* Check that addr..addr+1800h are not writable. */
+ for (p=z->addr; p<z->addr+0x1800; ++p) {
+ c = *p;
+ *p = ~c;
+ if (*p == ~c) {
+ *p = c;
+ return (5);
+ }
+ }
+
+ /* Check that addr+1800h..addr+1880h are writable. */
+ for (p=z->addr+0x1800; p<z->addr+0x1880; ++p) {
+ c = *p;
+ *p = 0x55;
+ if (*p != 0x55) {
+ *p = c;
+ return (6);
+ }
+ *p = 0xaa;
+ if (*p != 0xaa) {
+ *p = c;
+ return (7);
+ }
+ }
+
+ /* Reset the scsi bus (I don't know if this is needed). */
+ *z->CONTROL = CMD_RST | CMD_DRVR_ENABLE | z->parity | CMD_INTR;
+ /* Hold reset for at least 25 microseconds. */
+ DELAY (25);
+ /* Check that status cleared. */
+ if (*z->STATUS != 0) {
+ *z->CONTROL = 0;
+ return (8);
+ }
+
+ /* Check that DATA register is writable. */
+ for (i=0; i<256; ++i) {
+ *z->DATA = i;
+ if (*z->DATA != i) {
+ *z->CONTROL = 0;
+ return (9);
+ }
+ }
+
+ /* Enable the adapter. */
+ *z->CONTROL = CMD_INTR | z->parity;
+ /* Wait a Bus Clear Delay (800 ns + bus free delay 800 ns). */
+ DELAY (10);
+
+ /* Check that DATA register is NOT writable. */
+ c = *z->DATA;
+ for (i=0; i<256; ++i) {
+ *z->DATA = i;
+ if (*z->DATA != c) {
+ *z->CONTROL = 0;
+ return (10);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Attach all sub-devices we can find.
+ */
+int sea_attach (struct isa_device *dev)
+{
+ int unit = dev->id_unit;
+ adapter_t *z = &seadata[unit];
+ struct scsibus_data *scbus;
+
+ printf ("\nsea%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_softc = z;
+ z->sc_link.adapter = &sea_switch;
+ z->sc_link.device = &sea_dev;
+
+ /*
+ * Prepare the scsibus_data area for the upperlevel
+ * scsi code.
+ */
+ scbus = scsi_alloc_bus();
+ if(!scbus)
+ return 0;
+ scbus->adapter_link = &z->sc_link;
+
+ /* ask the adapter what subunits are present */
+ scsi_attachdevs (scbus);
+
+ return (1);
+}
+
+/*
+ * Return some information to the caller about
+ * the adapter and its capabilities.
+ */
+u_int32_t sea_adapter_info (int unit)
+{
+ return (1);
+}
+
+void seaminphys (struct buf *bp)
+{
+}
+
+/*
+ * Catch an interrupt from the adaptor.
+ */
+void seaintr (int unit)
+{
+ adapter_t *z = &seadata[unit];
+
+ PRINT (("sea%d: interrupt status=%b\n", unit, *z->STATUS, STAT_BITS));
+ sea_start (z);
+}
+
+/*
+ * 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 sea_tick (void *arg)
+{
+ adapter_t *z = arg;
+ int x = splbio ();
+
+ z->timeout_active = 0;
+ sea_start (z);
+ if (z->disconnected_queue && ! z->timeout_active) {
+ timeout (sea_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_t sea_scsi_cmd (struct scsi_xfer *xs)
+{
+ int flags = xs->flags, x = 0;
+ adapter_t *z = (adapter_t *)xs->sc_link->adapter_softc;
+ scb_t *scb;
+
+ PRINT (("sea%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 ("sea%d: already done?", xs->sc_link->adapter_unit);
+ xs->flags &= ~ITSDONE;
+ }
+ if (! (flags & INUSE)) {
+ printf ("sea%d: not in use?", xs->sc_link->adapter_unit);
+ xs->flags |= INUSE;
+ }
+ if (flags & SCSI_RESET)
+ printf ("sea%d: SCSI_RESET not implemented\n",
+ xs->sc_link->adapter_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, "seascb", 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. */
+ sea_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);
+ xs->timeout_ch = timeout (sea_timeout, (caddr_t) scb,
+ (xs->timeout * hz) / 1000);
+ scb->flags |= SCB_TIMECHK;
+ PRINT (("sea%d/%d/%d command queued\n",
+ xs->sc_link->adapter_unit,
+ xs->sc_link->target, xs->sc_link->lun));
+ return (SUCCESSFULLY_QUEUED);
+ }
+
+ /* If we can't use interrupts, poll on completion. */
+ if (! sea_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. */
+ sea_timeout ((void*) scb);
+
+ /* Because we are polling, take out the timeout entry
+ * sea_timeout made. */
+ untimeout (sea_timeout, (void*) scb, xs->timeout_ch);
+
+ if (! sea_poll (z, scb))
+ /* We timed out again... This is bad. Notice that
+ * this time there is no clock queue entry to remove. */
+ sea_timeout ((void*) scb);
+ }
+ PRINT (("sea%d/%d/%d command %s\n", xs->sc_link->adapter_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 sea_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 (sea_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) || ! sea_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. */
+ sea_information_transfer (z, q);
+ goto again;
+ }
+}
+
+void sea_timeout (void *arg)
+{
+ scb_t *scb = (scb_t*) arg;
+ adapter_t *z = (adapter_t *)scb->xfer->sc_link->adapter_softc;
+ int x = splbio ();
+
+ if (! (scb->xfer->flags & SCSI_NOMASK))
+ printf ("sea%d/%d/%d (%s%d) timed out\n",
+ scb->xfer->sc_link->adapter_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)) {
+ sea_abort (z, scb);
+ /* 2 seconds for the abort */
+ scb->xfer->timeout_ch = timeout (sea_timeout,
+ (caddr_t)scb, 2*hz);
+ scb->flags |= (SCB_ABORTED | SCB_TIMECHK);
+ } else {
+ /* abort timed out */
+ scb->flags |= SCB_ABORTED;
+ scb->xfer->retries = 0;
+ sea_done (z, scb);
+ }
+ splx (x);
+}
+
+/*
+ * Wait until REQ goes down. This is needed for some devices (CDROMs)
+ * after every MSGOUT, MSGIN, CMDOUT, STATIN request.
+ * Return true if REQ deassert found.
+ */
+static __inline int sea_wait_for_req_deassert (adapter_t *z, int cnt, char *msg)
+{
+ __asm __volatile ("
+ 1: testb $0x10, %2
+ jz 2f
+ loop 1b
+ 2:"
+ : "=c" (cnt) /* output */
+ : "0" (cnt), "m" (*z->STATUS)); /* input */
+ if (! cnt) {
+ PRINT (("sea%d (%s) timeout waiting for !REQ\n",
+ z->sc_link.adapter_unit, msg));
+ return (0);
+ }
+ /* PRINT (("sea_wait_for_req_deassert %s count=%d\n", msg, cnt)); */
+ return (1);
+}
+
+/*
+ * 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 sea_select (volatile adapter_t *z, scb_t *scb)
+{
+ /* Start arbitration. */
+ *z->CONTROL = z->parity | CMD_INTR;
+ *z->DATA = z->scsi_id;
+ *z->CONTROL = CMD_START_ARB | z->parity;
+
+ /* Wait for arbitration to complete. */
+ WAITFOR (*z->STATUS & STAT_ARB_CMPL, "arbitration");
+ if (! (*z->STATUS & STAT_ARB_CMPL)) {
+ if (*z->STATUS & STAT_SEL) {
+ printf ("sea: arbitration lost\n");
+ scb->flags |= SCB_ERROR;
+ } else {
+ printf ("sea: arbitration timeout\n");
+ scb->flags |= SCB_TIMEOUT;
+ }
+ *z->CONTROL = CMD_INTR | z->parity;
+ return (0);
+ }
+ DELAY (1);
+
+ *z->DATA = (1 << scb->xfer->sc_link->target) | z->scsi_id;
+ *z->CONTROL = sea_select_cmd | CMD_SEL | z->parity;
+ DELAY (2);
+
+ /* Wait for a bsy from target.
+ * If the target is not present on the bus, we get
+ * the timeout. Don't PRINT any message -- it's not an error. */
+ WAITFOR (*z->STATUS & STAT_BSY, 0);
+ if (! (*z->STATUS & STAT_BSY)) {
+ /* The target does not respond. Not an error, though. */
+ PRINT (("sea%d/%d/%d target does not respond\n",
+ z->sc_link.adapter_unit, scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun));
+ *z->CONTROL = CMD_INTR | z->parity;
+ scb->flags |= SCB_TIMEOUT;
+ return (0);
+ }
+
+ /* Try to make the target to take a message from us.
+ * Should start a MSGOUT phase. */
+ *z->CONTROL = sea_select_cmd | z->parity;
+ DELAY (15);
+ WAITFOR (*z->STATUS & STAT_REQ, 0);
+
+ if (z->type == CTLR_FUTURE_DOMAIN)
+ *z->CONTROL = CMD_INTR | z->parity | CMD_DRVR_ENABLE;
+
+ WAITFOR (*z->STATUS & STAT_REQ, 0);
+ if (! (*z->STATUS & STAT_REQ)) {
+ PRINT (("sea%d/%d/%d timeout waiting for REQ\n",
+ z->sc_link.adapter_unit, scb->xfer->sc_link->target,
+ scb->xfer->sc_link->lun));
+ scb->flags |= SCB_ERROR;
+ *z->CONTROL = CMD_INTR | z->parity;
+ return (0);
+ }
+
+ /* Check for phase mismatch. FD 885 always seems to get this wrong! */
+ if ((*z->STATUS & PHASE_MASK) != PHASE_MSGOUT && z->type != CTLR_FUTURE_DOMAIN) {
+ PRINT (("sea%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 (*z->STATUS & PHASE_MASK)));
+ scb->flags |= SCB_ERROR;
+ *z->CONTROL = CMD_INTR | z->parity;
+ return (0);
+ }
+
+ /* Allow disconnects. (except for FD controllers) */
+ if (z->type == CTLR_SEAGATE) {
+ *z->CONTROL = CMD_DRVR_ENABLE | z->parity;
+ *z->DATA = MSG_IDENTIFY (scb->xfer->sc_link->lun);
+ WAITREQ (&z->target[scb->xfer->sc_link->target], msgout, 1000);
+ }
+ *z->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | z->parity;
+
+ SET_BUSY (z, scb);
+ return (1);
+}
+
+int sea_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 ((*z->STATUS & (STAT_SEL | STAT_IO | STAT_BSY)) !=
+ (STAT_SEL | STAT_IO))
+ return (0);
+
+ /* The data bus contains original initiator id ORed with target id. */
+ /* See that we really are the initiator. */
+ target_mask = *z->DATA;
+ if (! (target_mask & z->scsi_id)) {
+ PRINT (("sea%d reselect not for me: mask=0x%x, status=%b\n",
+ z->sc_link.adapter_unit, target_mask,
+ *z->STATUS, STAT_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;
+ *z->CONTROL = CMD_DRVR_ENABLE | CMD_BSY | z->parity | CMD_INTR;
+ WAITFOR (! (*z->STATUS & STAT_SEL), "reselection acknowledge");
+
+ /* Remove the busy status. */
+ /* Target should set the MSGIN phase. */
+ *z->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | z->parity;
+ WAITFOR (*z->STATUS & STAT_REQ, "identify message");
+
+ /* Hope we get an IDENTIFY message. */
+ msg = sea_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 (("sea%d/%d/%d reselect done\n",
+ z->sc_link.adapter_unit,
+ ffs (target_mask) - 1, lun));
+ sea_information_transfer (z, q);
+ WAITFOR (! (*z->STATUS & STAT_BSY), "reselect !busy");
+ return (1);
+ }
+ } else
+ printf ("sea%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. */
+ sea_send_abort (z);
+ PRINT (("sea%d reselect aborted\n", z->sc_link.adapter_unit));
+ WAITFOR (! (*z->STATUS & STAT_BSY), "bus free after reselect abort");
+ goto again;
+}
+
+/*
+ * Send an abort to the target.
+ * Return 1 success, 0 on failure.
+ * Called on splbio level.
+ */
+int sea_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 (! sea_select (z, scb))
+ return (0);
+ sea_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 sea_done (adapter_t *z, scb_t *scb)
+{
+ struct scsi_xfer *xs = scb->xfer;
+
+ if (scb->flags & SCB_TIMECHK)
+ untimeout (sea_timeout, (caddr_t) scb, xs->timeout_ch);
+
+ /* 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 sea_poll (adapter_t *z, scb_t *scb)
+{
+ int count;
+
+ for (count=0; count<30; ++count) {
+ DELAY (1000); /* delay for a while */
+ sea_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);
+}
+
+/*
+ * Send data to the target.
+ */
+void sea_data_output (adapter_t *z, u_char **pdata, u_long *plen)
+{
+ volatile u_char *data = *pdata;
+ volatile u_int len = *plen;
+
+#ifdef SEA_BLINDTRANSFER
+ if (len && !(len % BLOCK_SIZE)) {
+ while (len) {
+ WAITFOR10 (*z->STATUS & STAT_REQ, "blind block read");
+ __asm __volatile ("
+ shr $2, %%ecx;
+ cld;
+ rep;
+ movsl; " : :
+ "D" (z->DATA), "S" (data), "c" (BLOCK_SIZE) :
+ "cx", "si", "di" );
+ data += BLOCK_SIZE;
+ len -= BLOCK_SIZE;
+ }
+ } else {
+#endif
+ __asm __volatile ("cld
+ 1: movb (%%ebx), %%al
+ xorb $1, %%al
+ testb $0xf, %%al
+ jnz 2f
+ testb $0x10, %%al
+ jz 1b
+ lodsb
+ movb %%al, (%%edi)
+ loop 1b
+ 2:"
+ : "=S" (data), "=c" (len) /* output */
+ : "D" (z->DATA), "b" (z->STATUS), /* input */
+ "0" (data), "1" (len)
+ : "eax", "ebx", "edi"); /* clobbered */
+#ifdef SEA_BLINDTRANSFER
+ }
+#endif
+ PRINT (("sea (DATAOUT) send %ld bytes\n", *plen - len));
+ *plen = len;
+ *pdata = (u_char *)data;
+}
+
+/*
+ * Receive data from the target.
+ */
+void sea_data_input (adapter_t *z, u_char **pdata, u_long *plen)
+{
+ volatile u_char *data = *pdata;
+ volatile u_int len = *plen;
+
+#ifdef SEA_BLINDTRANSFER
+ if (len && !(len % BLOCK_SIZE)) {
+ while (len) {
+ WAITFOR10 (*z->STATUS & STAT_REQ, "blind block read");
+ __asm __volatile ("
+ shr $2, %%ecx;
+ cld;
+ rep;
+ movsl; " : :
+ "S" (z->DATA), "D" (data), "c" (BLOCK_SIZE) :
+ "cx", "si", "di" );
+ data += BLOCK_SIZE;
+ len -= BLOCK_SIZE;
+ }
+ } else {
+#endif
+ if (len >= 512) {
+ __asm __volatile (" cld
+ 1: movb (%%esi), %%al
+ xorb $5, %%al
+ testb $0xf, %%al
+ jnz 2f
+ testb $0x10, %%al
+ jz 1b
+ movb (%%ebx), %%al
+ stosb
+ loop 1b
+ 2:"
+ : "=D" (data), "=c" (len) /* output */
+ : "b" (z->DATA), "S" (z->STATUS),
+ "0" (data), "1" (len) /* input */
+ : "eax", "ebx", "esi"); /* clobbered */
+ } else {
+ __asm __volatile (" cld
+ 1: movb (%%esi), %%al
+ xorb $5, %%al
+ testb $0xf, %%al
+ jnz 2f
+ testb $0x10, %%al
+ jz 1b
+ movb (%%ebx), %%al
+ stosb
+ movb $1000, %%al
+ 3: testb $0x10, (%%esi)
+ jz 4f
+ dec %%al
+ jnz 3b
+ 4: loop 1b
+ 2:"
+ : "=D" (data), "=c" (len) /* output */
+ : "b" (z->DATA), "S" (z->STATUS),
+ "0" (data), "1" (len) /* input */
+ : "eax", "ebx", "esi"); /* clobbered */
+ }
+#ifdef SEA_BLINDTRANSFER
+ }
+#endif
+ PRINT (("sea (DATAIN) got %ld bytes\n", *plen - len));
+ *plen = len;
+ *pdata = (u_char *)data;
+}
+
+/*
+ * Send the command to the target.
+ */
+void sea_cmd_output (target_t *t, u_char *cmd, int cmdlen)
+{
+ adapter_t *z = t->adapter;
+
+ PRINT (("sea%d send command (%d bytes) ", z->sc_link.adapter_unit,
+ cmdlen));
+
+ PRINT (("%x", *cmd));
+ *z->DATA = *cmd++;
+ if (z->type == CTLR_SEAGATE)
+ WAITREQ (t, cmdout1, 10000);
+ --cmdlen;
+
+ while (cmdlen) {
+ /* Check for target disconnect. */
+ u_char sts = *z->STATUS;
+ if (! (sts & STAT_BSY))
+ break;
+
+ /* Check for phase mismatch. FD 885 seems to get this wrong! */
+ if ((sts & PHASE_MASK) != PHASE_CMDOUT && z->type != CTLR_FUTURE_DOMAIN) {
+ printf ("sea: sea_cmd_output: invalid phase %s\n",
+ PHASE_NAME (sts & PHASE_MASK));
+ return;
+ }
+
+ /* Wait for REQ. */
+ if (! (sts & STAT_REQ))
+ continue;
+
+ PRINT (("-%x", *cmd));
+ *z->DATA = *cmd++;
+ if (z->type == CTLR_SEAGATE)
+ WAITREQ (t, cmdout, 1000);
+ --cmdlen;
+ }
+ PRINT (("\n"));
+}
+
+/*
+ * Send the message to the target.
+ */
+void sea_send_abort (adapter_t *z)
+{
+ u_char sts;
+
+ *z->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | CMD_ATTN | z->parity;
+
+ /* Wait for REQ, after which the phase bits will be valid. */
+ WAITFOR (*z->STATUS & STAT_REQ, "abort message");
+ sts = *z->STATUS;
+ if (! (sts & STAT_REQ))
+ goto ret;
+
+ /* Check for phase mismatch. */
+ if ((sts & PHASE_MASK) != PHASE_MSGOUT) {
+ printf ("sea: sending MSG_ABORT: invalid phase %s\n",
+ PHASE_NAME (sts & PHASE_MASK));
+ goto ret;
+ }
+
+ *z->DATA = MSG_ABORT;
+ sea_wait_for_req_deassert (z, 1000, "MSG_OUTPUT");
+ PRINT (("sea%d send abort message\n", z->sc_link.adapter_unit));
+ret:
+ *z->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | z->parity;
+}
+
+/*
+ * Get the message from the target.
+ * Return the length of the received message.
+ */
+u_char sea_msg_input (adapter_t *z)
+{
+ u_char sts, msg;
+
+ /* Wait for REQ, after which the phase bits will be valid. */
+ WAITFOR (*z->STATUS & STAT_REQ, "message input");
+ sts = *z->STATUS;
+ if (! (sts & STAT_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 ("sea: sea_msg_input: invalid phase %s\n",
+ PHASE_NAME (sts & PHASE_MASK));
+ return (MSG_ABORT);
+ }
+
+ /* Do actual transfer from SCSI bus to/from memory. */
+ msg = *z->DATA;
+ sea_wait_for_req_deassert (z, 1000, "MSG_INPUT");
+ PRINT (("sea%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 sea_sense (adapter_t *z, scb_t *scb)
+{
+ u_char cmd[6], status, msg, *data;
+ u_long len;
+
+ /* Wait for target to disconnect. */
+ WAITFOR (! (*z->STATUS & STAT_BSY), "sense bus free");
+ if (*z->STATUS & STAT_BSY)
+ return (0);
+
+ /* Select the target again. */
+ if (! sea_select (z, scb))
+ return (0);
+
+ /* Wait for CMDOUT phase. */
+ WAITFOR (*z->STATUS & STAT_REQ, "sense CMDOUT");
+ if (! (*z->STATUS & STAT_REQ) ||
+ (*z->STATUS & PHASE_MASK) != PHASE_CMDOUT)
+ return (0);
+
+ /* 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;
+ sea_cmd_output (&z->target[scb->xfer->sc_link->target],
+ cmd, sizeof (cmd));
+
+ /* Wait for DATAIN phase. */
+ WAITFOR (*z->STATUS & STAT_REQ, "sense DATAIN");
+ if (! (*z->STATUS & STAT_REQ) ||
+ (*z->STATUS & PHASE_MASK) != PHASE_DATAIN)
+ return (0);
+
+ data = (u_char*) &scb->xfer->sense;
+ sea_data_input (z, &data, &len);
+ PRINT (("sea%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 (*z->STATUS & STAT_REQ, "sense STATIN");
+ if (! (*z->STATUS & STAT_REQ) ||
+ (*z->STATUS & PHASE_MASK) != PHASE_STATIN)
+ return (0);
+
+ status = *z->DATA;
+
+ /* Wait for MSGIN phase. */
+ WAITFOR (*z->STATUS & STAT_REQ, "sense MSGIN");
+ if (! (*z->STATUS & STAT_REQ) ||
+ (*z->STATUS & PHASE_MASK) != PHASE_MSGIN)
+ return (0);
+
+ msg = *z->DATA;
+
+ if (status != 0 || msg != 0)
+ printf ("sea%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 sea_done when task accomplished. Dialog controlled by the target.
+ * Always called with interrupts disabled.
+ */
+void sea_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 */
+ target_t *t = &z->target[scb->xfer->sc_link->target];
+ register u_char sts;
+ u_char msg;
+
+ while ((sts = *z->STATUS) & STAT_BSY) {
+ /* We only have a valid SCSI phase when REQ is asserted. */
+ if (! (sts & STAT_REQ))
+ continue;
+ if (sts & STAT_PARITY) {
+ int target = scb->xfer->sc_link->target;
+ if (++z->target[target].perrcnt <= 8)
+ printf ("sea%d/%d/%d parity error\n",
+ z->sc_link.adapter_unit, target,
+ scb->xfer->sc_link->lun);
+ if (z->target[target].perrcnt == 8)
+ printf ("sea%d/%d/%d too many parity errors, not logging any more\n",
+ z->sc_link.adapter_unit, target,
+ scb->xfer->sc_link->lun);
+ }
+ switch (sts & PHASE_MASK) {
+ case PHASE_DATAOUT:
+ if (datalen <= 0) {
+ printf ("sea%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 */
+ *z->DATA = 0;
+ break;
+ }
+ sea_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 (("@"));
+ sts = *z->DATA;
+ break;
+ }
+ sea_data_input (z, &data, &datalen);
+ break;
+ case PHASE_CMDOUT:
+ sea_cmd_output (t, (u_char*) scb->xfer->cmd,
+ scb->xfer->cmdlen);
+ break;
+ case PHASE_STATIN:
+ scb->xfer->status = *z->DATA;
+ if (z->type == CTLR_SEAGATE)
+ WAITREQ (t, statin, 2000);
+ PRINT (("sea%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. */
+ *z->DATA = MSG_NOP;
+ sea_wait_for_req_deassert (z, 1000, "MSGOUT");
+ PRINT (("sea%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 = *z->DATA;
+ sea_wait_for_req_deassert (z, 2000, "MSGIN");
+ PRINT (("sea%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 (sea_sense (z, scb))
+ scb->flags = SCB_SENSE;
+ break;
+ case SCSI_BUSY:
+ scb->flags = SCB_TBUSY;
+ break;
+ }
+ goto done;
+ case MSG_ABORT:
+ printf ("sea: command aborted by target\n");
+ scb->flags = SCB_ABORTED;
+ goto done;
+ case MSG_MESSAGE_REJECT:
+ printf ("sea: 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 (sea_tick, z, 1);
+ z->timeout_active = 1;
+ }
+ PRINT (("sea%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 ("sea%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 ("sea: unknown phase: %b\n", sts, STAT_BITS);
+ break;
+ }
+ }
+ printf ("sea%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);
+ sea_done (z, scb);
+ret:
+ *z->CONTROL = CMD_INTR | z->parity;
+}
+#endif /* NSEA */