+/*
+ * (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)
+ *
+ * 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
+ *
+ */
+
+/*
+ * What should really be done:
+ *
+ * Add missing tests for timeouts
+ * Restructure interrupt enable/disable code (runs to long with int disabled)
+ * Find bug? giving problem with tape status
+ * Add code to handle Future Domain 840, 841, 880 and 881
+ * adjust timeouts (startup is very slow)
+ * 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
+ */
+
+#include <sys/types.h>
+
+#ifdef KERNEL /* don't laugh.. look for main() */
+#include <sea.h>
+
+#include <sys/param.h>
+#include <sys/systm.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 <i386/isa/isa_device.h>
+#endif /* KERNEL */
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#ifdef KERNEL
+#include "ddb.h"
+#include "kernel.h"
+#else /* KERNEL */
+#define NSEA 1
+#endif /* KERNEL */
+
+extern int hz;
+
+#define SEA_SCB_MAX 8 /* allow maximally 8 scsi control blocks */
+#define SCB_TABLE_SIZE 8 /* start with 8 scb entries in table */
+#define BLOCK_SIZE 512 /* size of READ/WRITE areas on SCSI card */
+
+/*
+ * defining PARITY causes parity data to be checked
+ */
+#define PARITY 1
+
+/*
+ * defining SEA_BLINDTRANSFER will make DATA IN and DATA OUT to be done with
+ * blind transfers, i.e. no check is done for scsi phase changes. This will
+ * result in data loss if the scsi device does not send its data using
+ * BLOCK_SIZE bytes at a time.
+ * If SEA_BLINDTRANSFER defined and SEA_ASSEMBLER also defined will result in
+ * the use of blind transfers coded in assembler. SEA_ASSEMBLER is no good
+ * without SEA_BLINDTRANSFER defined.
+ */
+#define SEA_BLINDTRANSFER 1 /* do blind transfers */
+#define SEA_ASSEMBLER 1 /* Use assembly code for fast transfers */
+
+/*
+ * defining SEANOMSGS causes messages not to be used (thereby disabling
+ * disconnects)
+ */
+/* #define SEANOMSGS 1 */
+
+/*
+ * defining SEA_NODATAOUT makes dataout phase being aborted
+ */
+/* #define SEA_NODATAOUT 1 */
+
+/*
+ * defining SEA_SENSEFIRST make REQUEST_SENSE opcode to be placed first
+ */
+/* #define SEA_SENSEFIRST 1 */
+
+#define SEA_FREEBSD11 1 /* intermediate def. for FreeBSD 1.1 BETA */
+ /* timeout function has changed */
+
+/* Debugging definitions. Should not be used unless you want a lot of
+ printouts even under normal conditions */
+
+/* #define SEADEBUG 1 */ /* General info about errors */
+/* #define SEADEBUG1 1 */ /* Info about internal results and errors */
+/* #define SEADEBUG2 1 */ /* Display a lot about timeouts etc */
+/* #define SEADEBUG3 1 */
+/* #define SEADEBUG4 1 */
+/* #define SEADEBUG5 1 */
+/* #define SEADEBUG6 1 */ /* Display info about queue-lengths */
+/* #define SEADEBUG7 1 */ /* Extra check on STATUS before phase check */
+/* #define SEADEBUG8 1 */ /* Disregard non-BSY state in
+ sea_information_transfer */
+/* #define SEADEBUG9 1 */ /* Enable printouts */
+/* #define SEADEBUG11 1 */ /* stop everything except access to scsi id 1 */
+/* #define SEADEBUG15 1 */ /* Display every byte sent/received */
+
+#define NUM_CONCURRENT 1 /* number of concurrent ops per board */
+
+/******************************* board definitions **************************/
+/*
+ * CONTROL defines
+ */
+
+#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 */
+
+/*
+ * STATUS
+ */
+
+#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 */
+
+/*
+ * REQUESTS
+ */
+
+#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG)
+#define REQ_DATAOUT 0
+#define REQ_DATAIN STAT_IO
+#define REQ_CMDOUT STAT_CD
+#define REQ_STATIN (STAT_CD | STAT_IO)
+#define REQ_MSGOUT (STAT_MSG | STAT_CD)
+#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO)
+
+#define REQ_UNKNOWN 0xff
+
+#define SEAGATERAMOFFSET 0x00001800
+
+#ifdef PARITY
+ #define BASE_CMD (CMD_EN_PARITY | CMD_INTR)
+#else
+ #define BASE_CMD (CMD_INTR)
+#endif
+
+#define SEAGATE 1
+#define FD 2
+
+/******************************************************************************
+ * This should be placed in a more generic file (presume in /sys/scsi)
+ * Message codes:
+ */
+#define MSG_ABORT 0x06
+#define MSG_NOP 0x08
+#define MSG_COMMAND_COMPLETE 0x00
+#define MSG_DISCONNECT 0x04
+#define MSG_IDENTIFY 0x80
+#define MSG_BUS_DEV_RESET 0x0c
+#define MSG_MESSAGE_REJECT 0x07
+#define MSG_SAVE_POINTERS 0x02
+#define MSG_RESTORE_POINTERS 0x03
+/******************************************************************************/
+
+#define IDENTIFY(can_disconnect,lun) (MSG_IDENTIFY | ((can_disconnect) ? \
+ 0x40 : 0) | ((lun) & 0x07))
+
+/* scsi control block used to keep info about a scsi command */
+struct sea_scb
+{
+ int flags; /* status of the instruction */
+#define SCB_FREE 0
+#define SCB_ACTIVE 1
+#define SCB_ABORTED 2
+#define SCB_TIMEOUT 4
+#define SCB_ERROR 8
+#define SCB_TIMECHK 16 /* We have set a timeout on this one */
+ struct sea_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 */;
+};
+
+/*
+ * data structure describing current status of the scsi bus. One for each
+ * controller card.
+ */
+struct sea_data
+{
+ caddr_t basemaddr; /* Base address for card */
+ char ctrl_type; /* FD or SEAGATE */
+ caddr_t st0x_cr_sr; /* Address of control and status register */
+ caddr_t st0x_dr; /* Address of data register */
+ u_short vect; /* interrupt vector for this card */
+ int our_id; /* our scsi id */
+ int numscb; /* number of scsi control blocks */
+ struct scsi_link sc_link; /* struct connecting different data */
+ struct sea_scb *connected; /* currently connected command */
+ struct sea_scb *issue_queue; /* waiting to be issued */
+ struct sea_scb *disconnected_queue; /* waiting to reconnect */
+ struct sea_scb scbs[SCB_TABLE_SIZE];
+ struct sea_scb *free_scb; /* free scb list */
+ volatile unsigned char busy[8]; /* index=target, bit=lun, Keep track of
+ busy luns at device target */
+} *seadata[NSEA];
+
+/* flag showing if main routine is running. */
+static volatile int main_running = 0;
+
+#define STATUS (*(volatile unsigned char *) sea->st0x_cr_sr)
+#define CONTROL STATUS
+#define DATA (*(volatile unsigned char *) sea->st0x_dr)
+
+/*
+ * These are "special" values for the tag parameter passed to sea_select
+ * Not implemented right now.
+ */
+
+#define TAG_NEXT -1 /* Use next free tag */
+#define TAG_NONE -2 /*
+ * Establish I_T_L nexus instead of I_T_L_Q
+ * even on SCSI-II devices.
+ */
+
+typedef struct {
+ char *signature ;
+ unsigned offset;
+ unsigned length;
+ unsigned char type;
+} BiosSignature;
+
+/*
+ * Signatures for automatic recognition of board type
+ */
+
+static const BiosSignature signatures[] = {
+{"ST01 v1.7 (C) Copyright 1987 Seagate", 15, 37, SEAGATE},
+{"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40, SEAGATE},
+
+/*
+ * The following two lines are NOT mistakes. One detects ROM revision
+ * 3.0.0, the other 3.2. Since seagate has only one type of SCSI adapter,
+ * and this is not going to change, the "SEAGATE" and "SCSI" together
+ * are probably "good enough"
+ */
+
+{"SEAGATE SCSI BIOS ", 16, 17, SEAGATE},
+{"SEAGATE SCSI BIOS ", 17, 17, SEAGATE},
+
+ /*
+ * However, future domain makes several incompatible SCSI boards, so specific
+ * signatures must be used.
+ */
+
+ {"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 45, FD},
+ {"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FD},
+ {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90",5, 47, FD},
+ {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90",5, 47, FD},
+ {"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD},
+ {"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD},
+ {"FUTURE DOMAIN TMC-950", 5, 21, FD},
+ };
+
+#define NUM_SIGNATURES (sizeof(signatures) / sizeof(BiosSignature))
+
+static const char * seagate_bases[] = {
+ (char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000,
+ (char *) 0xce000, (char *) 0xdc000, (char *) 0xde000
+};
+
+#define NUM_BASES (sizeof(seagate_bases)/sizeof(char *))
+
+int sea_probe(struct isa_device *dev);
+int sea_attach(struct isa_device *dev);
+int seaintr(int unit);
+int32 sea_scsi_cmd(struct scsi_xfer *xs);
+#ifdef SEA_FREEBSD11
+void sea_timeout(caddr_t, int);
+#else
+void sea_timeout(struct sea_scb *scb);
+#endif
+void seaminphys(struct buf *bp);
+void sea_done(int unit, struct sea_scb *scb);
+u_int32 sea_adapter_info(int unit);
+struct sea_scb *sea_get_scb(int unit, int flags);
+void sea_free_scb(int unit, struct sea_scb *scb, int flags);
+static void sea_main(void);
+static void sea_information_transfer(struct sea_data *sea);
+int sea_poll(int unit, struct scsi_xfer *xs, struct sea_scb *scb);
+int sea_init(int unit);
+int sea_send_scb(struct sea_data *sea, struct sea_scb *scb);
+int sea_reselect(struct sea_data *sea);
+int sea_select(struct sea_data *sea, struct sea_scb *scb);
+int sea_transfer_pio(struct sea_data *sea, u_char *phase, int32 *count,
+ u_char **data);
+int sea_abort(int unit, struct sea_scb *scb);
+
+static sea_unit = 0;
+static sea_slot = -1; /* last found board seagate_bases address index */
+#define FAIL 1
+#define SUCCESS 0
+
+#ifdef KERNEL
+struct scsi_adapter sea_switch =
+{
+ sea_scsi_cmd,
+ seaminphys,
+ 0,
+ 0,
+ sea_adapter_info,
+ "sea",
+ 0,0
+};
+
+/* the below structure is so we have a default dev struct for our link struct */
+struct scsi_device sea_dev =
+{
+ NULL, /* use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
+ "sea",
+ 0,
+ 0,0
+};
+
+struct isa_driver seadriver =
+{
+ seaprobe,
+ seaattach,
+ "sea"
+};
+
+#endif /* KERNEL */
+
+#ifdef SEADEBUG6
+void sea_queue_length()
+{
+ struct sea_scb *tmp;
+ int length = 0;
+
+ if(seadata[0]->connected)
+ length = 1;
+ for(tmp = seadata[0]->issue_queue; tmp != NULL; tmp = tmp->next, length++);
+ for(tmp = seadata[0]->disconnected_queue ; tmp != NULL; tmp->next, length++);
+ printf("length:%d ",length);
+}
+#endif
+
+/***********************************************************************\
+* 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(dev)
+struct isa_device *dev;
+{
+ int j;
+ int unit = sea_unit;
+ struct sea_data *sea;
+ dev->id_unit = unit;
+
+#ifdef SEADEBUG2
+ printf("sea_probe ");
+#endif
+
+ /* find unit and check we have that many defined */
+ if(unit >= NSEA) {
+ printf("sea%d: unit number too high\n",unit);
+ return(0);
+ }
+ dev->id_unit = unit;
+#ifdef SEADEBUG2
+ printf("unit: %d\n",unit);
+ printf("dev_addr: 0x%lx\n",dev->id_maddr);
+#endif
+ /* allocate a storage area for us */
+
+ if (seadata[unit]) {
+ printf("sea%d: memory already allocated\n", unit);
+ return(0);
+ }
+#ifdef SEADEBUG2
+ printf("Before malloc\n");
+#endif
+ sea = malloc(sizeof(struct sea_data), M_TEMP, M_NOWAIT);
+ if (!sea) {
+ printf("sea%d: cannot malloc!\n", unit);
+ return(0);
+ }
+
+#ifdef SEADEBUG2
+ printf("after malloc\n");
+ for(j=0;j<32767;j++);
+#endif
+ bzero(sea,sizeof(struct sea_data));
+ seadata[unit] = sea;
+
+ /* check for address if no one specified */
+ sea->basemaddr = NULL;
+
+ /* Could try to find a board by looking through all possible addresses */
+ /* This is not done the right way now, because I have not found a way */
+ /* to get a boards virtual memory address given its physical. There is */
+ /* a function that returns the physical address for a given virtual */
+ /* address, but not the other way around */
+
+ if(dev->id_maddr == 0) {
+/*
+ for(sea_slot++;sea_slot<NUM_BASES;sea_slot++)
+ for(j = 0; !sea->basemaddr && j < NUM_SIGNATURES; ++j)
+ if(!memcmp((void *)(seagate_bases[sea_slot]+signatures[j].offset),
+ (void *) signatures[j].signature, signatures[j].length)) {
+ sea->basemaddr = (void *)seagate_bases[sea_slot];
+ break;
+ }
+*/
+ } else {
+
+#ifdef SEADEBUG2
+ printf("id_maddr != 0\n");
+ for(j = 0; j < 32767 ; j++);
+ for(j = 0; j < 32767 ; j++);
+#endif
+ /* find sea_slot position for overridden memory address */
+ for(j = 0; ((char *)vtophys(dev->id_maddr) != seagate_bases[j]) &&
+ j<NUM_BASES; ++j);
+ if(j == NUM_BASES) {
+ printf("sea: board not expected at address 0x%lx\n",dev->id_maddr);
+ seadata[unit]=NULL;
+ free(sea, M_TEMP);
+ return(0);
+ } else if(sea_slot > j) {
+ printf("sea: board address 0x%lx already probed!\n", dev->id_maddr);
+ seadata[unit]=NULL;
+ free(sea, M_TEMP);
+ return(0);
+ } else {
+ sea->basemaddr = dev->id_maddr;
+ }
+
+ }
+#ifdef SEADEBUG2
+ printf("sea->basemaddr = %lx\n", sea->basemaddr);
+#endif
+
+ /* check board type */ /* No way to define this through config */
+ for(j = 0; j < NUM_SIGNATURES; j++)
+ if(!memcmp((void *) (sea->basemaddr + signatures[j].offset),
+ (void *) signatures[j].signature, signatures[j].length)) {
+ sea->ctrl_type = signatures[j].type;
+ break;
+ }
+ if(j == NUM_SIGNATURES) {
+ printf("sea: Board type unknown at address 0x%lx\n",
+ sea->basemaddr);
+ seadata[unit]=NULL;
+ free(sea, M_TEMP);
+ return(0);
+ }
+
+ /* Find controller and data memory addresses */
+ sea->st0x_cr_sr = (void *) (((unsigned char *) sea->basemaddr) +
+ ((sea->ctrl_type == SEAGATE) ? 0x1a00 : 0x1c00));
+ sea->st0x_dr = (void *) (((unsigned char *) sea->basemaddr) +
+ ((sea->ctrl_type == SEAGATE) ? 0x1c00 : 0x1e00));
+
+ /* Test controller RAM (works the same way on future domain cards?) */
+ *(sea->basemaddr + SEAGATERAMOFFSET) = 0xa5;
+ *(sea->basemaddr + SEAGATERAMOFFSET + 1) = 0x5a;
+
+ if((*(sea->basemaddr + SEAGATERAMOFFSET) != (char) 0xa5) ||
+ (*(sea->basemaddr + SEAGATERAMOFFSET + 1) != (char) 0x5a)) {
+ printf("sea%d: Board RAM failure\n",unit);
+ }
+
+ if(sea_init(unit) != 0) {
+ seadata[unit] = NULL;
+ free(sea,M_TEMP);
+ return(0);
+ }
+
+ /* if its there put in it's interrupt vector */
+ /* (Doesn't use dma, so no drq is set) */
+ sea->vect = dev->id_irq;
+
+ sea_unit++;
+ return(1);
+}
+
+/***********************************************\
+* Attach all sub-devices we can find *
+\***********************************************/
+int
+sea_attach(dev)
+ struct isa_device *dev;
+{
+ int unit = dev->id_unit;
+ struct sea_data *sea = seadata[unit];
+
+#ifdef SEADEBUG2
+ printf("sea_attach called\n");
+#endif
+
+ /* fill in the prototype scsi_link */
+ sea->sc_link.adapter_unit = unit;
+ sea->sc_link.adapter_targ = sea->our_id;
+ sea->sc_link.adapter = &sea_switch;
+ sea->sc_link.device = &sea_dev;
+
+ /*****************************************************\
+ * ask the adapter what subunits are present *
+ \*****************************************************/
+ scsi_attachdevs(&(sea->sc_link));
+ return 1;
+}
+
+/***********************************************\
+* Return some information to the caller about *
+* the adapter and its capabilities *
+\***********************************************/
+u_int32
+sea_adapter_info(unit)
+ int unit;
+{
+#ifdef SEADEBUG2
+ printf("sea_adapter_info called\n");
+#endif
+ return 1;
+}
+
+/***********************************************\
+* Catch an interrupt from the adaptor *
+\***********************************************/
+int
+seaintr(unit)
+ int unit;
+{
+ int done;
+ struct sea_data *sea = seadata[unit];
+ int oldpri;
+
+#if SEADEBUG2
+ printf(";");
+#endif
+
+ do {
+ done = 1;
+ /* dispatch to appropriate routine if found and done=0 */
+ /* should check to see that this card really caused the interrupt */
+ if ((STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) {
+ /* Reselect interrupt */
+#ifdef SEADEBUG2
+ printf(";2");
+#endif
+ done = 0;
+/* enable_intr(); */ /* ?? How should this be done ?? */
+ sea_reselect(sea);
+ } else if (STATUS & STAT_PARITY) {
+ /* Parity error interrupt */
+#ifdef SEADEBUG2
+ printf(";3");
+#endif
+ printf("sea%d: PARITY interrupt\n", unit);
+ } else {
+#ifdef SEADEBUG2
+/* printf("sea%d: unknown interrupt\n",unit); */
+ printf(";4%x", STATUS);
+#endif
+ }
+ if (!done) {
+ oldpri = splbio(); /* disable_intr(); */
+ if (!main_running) {
+#ifdef SEADEBUG2
+ printf(";5");
+#endif
+ main_running = 1;
+ sea_main();
+ /* main_running is cleared in sea_main once it can't
+ * do more work, and sea_main exits with interrupts
+ * disabled
+ */
+ splx(oldpri); /* enable_intr(); */
+ } else {
+ splx(oldpri); /* enable_intr(); */
+ }
+ }
+ } while (!done);
+ return 1;
+}
+
+/***********************************************\
+* Setup data structures, and reset the board *
+* and the scsi bus *
+\***********************************************/
+int
+sea_init(unit)
+ int unit;
+{
+ long l;
+ int i;
+ struct sea_data *sea = seadata[unit];
+
+#ifdef SEADEBUG2
+ printf("sea_init called\n");
+#endif
+/* Reset the scsi bus (I don't know if this is needed */
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_RST;
+ DELAY(25); /* hold reset for at least 25 microseconds */
+ CONTROL = BASE_CMD;
+ DELAY(10); /* wait a Bus Clear Delay (800 ns + bus free delay (800 ns) */
+ /* Set our id (don't know anything about this) */
+ if(sea->ctrl_type == SEAGATE)
+ sea->our_id = 7;
+ else
+ sea->our_id = 6;
+ /* init fields used by our routines */
+ sea->connected = NULL;
+ sea->issue_queue = NULL;
+ sea->disconnected_queue = NULL;
+ for (i=0; i<8 ; i++)
+ sea->busy[i] = 0;
+
+ /* link up the free list of scbs */
+ sea->numscb = SCB_TABLE_SIZE;
+ sea->free_scb = (struct sea_scb *) & (sea->scbs[0]);
+ for(i=1;i< SCB_TABLE_SIZE ; i++) {
+ sea->scbs[i-1].next = &(sea->scbs[i]);
+ }
+ sea->scbs[SCB_TABLE_SIZE - 1].next = NULL;
+
+ return(0);
+}
+
+/***********************************************\
+* *
+\***********************************************/
+void seaminphys(bp)
+ struct buf *bp;
+{
+#ifdef SEADEBUG2
+/* printf("seaminphys called\n"); */
+ printf(",");
+#endif
+}
+
+/***********************************************\
+* 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 sea_scsi_cmd(xs)
+struct scsi_xfer *xs;
+{
+ struct scsi_sense_data *s1, *s2;
+ struct sea_scb *scb;
+ int i = 0;
+ int flags;
+ int unit = xs->sc_link->adapter_unit;
+ struct sea_data *sea = seadata[unit];
+ int s;
+ unsigned int stat;
+ int32 result;
+
+#ifdef SEADEBUG2
+ /* printf("scsi_cmd\n"); */
+ printf("=");
+#endif
+
+#ifdef SEADEBUG11
+ if(xs->sc_link->target != 1) {
+ xs->flags |= ITSDONE;
+ xs->error = XS_TIMEOUT;
+ return(HAD_ERROR);
+ }
+#endif
+
+ flags = xs->flags;
+ if(xs->bp) flags |= (SCSI_NOSLEEP);
+ if(flags & ITSDONE) {
+ printf("sea%d: Already done?", unit);
+ xs->flags &= ~ITSDONE;
+ }
+ if(!(flags & INUSE)) {
+ printf("sea%d: Not in use?", unit);
+ xs->flags |= INUSE;
+ }
+ if (!(scb = sea_get_scb(unit, flags))) {
+#ifdef SEADEBUG2
+ printf("=2");
+#endif
+ xs->error = XS_DRIVER_STUFFUP;
+ return(TRY_AGAIN_LATER);
+ }
+
+ /*
+ * Put all the arguments for the xfer in the scb
+ */
+ scb->xfer = xs;
+ scb->datalen = xs->datalen;
+ scb->data = xs->data;
+
+ if(flags & SCSI_RESET) {
+ /* Try to send a reset command to the card. This is done by calling the
+ * Reset function. Should then return COMPLETE. Need to take care of the
+ * possible current connected command.
+ * Not implemented right now.
+ */
+ printf("sea%d: Got a SCSI_RESET!\n",unit);
+ }
+
+ /* setup the scb to contain necessary values */
+ /* The interresting 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 ? */
+#ifdef SEADEBUG6
+ sea_queue_length();
+#endif
+ if (sea_send_scb(sea, scb) == 0) {
+#ifdef SEADEBUG2
+ printf("=3");
+#endif
+ xs->error = XS_DRIVER_STUFFUP;
+ sea_free_scb(unit, scb, flags);
+ return (TRY_AGAIN_LATER);
+ }
+
+ /*
+ * Usually return SUCCESSFULLY QUEUED
+ */
+ if (!(flags & SCSI_NOMASK)) {
+ if(xs->flags & ITSDONE) { /* timout timer not started, already finished */
+ /* Tried to return COMPLETE but the machine hanged with this */
+#ifdef SEADEBUG2
+ printf("=6");
+#endif
+ return(SUCCESSFULLY_QUEUED);
+ }
+#ifdef SEA_FREEBSD11
+ timeout(sea_timeout, (caddr_t)scb, (xs->timeout * hz) / 1000);
+#else
+ timeout(sea_timeout, scb, (xs->timeout * hz) / 1000);
+#endif
+ scb->flags |= SCB_TIMECHK;
+#ifdef SEADEBUG2
+ printf("=4");
+#endif
+ return(SUCCESSFULLY_QUEUED);
+ }
+
+ /*
+ * If we can't use interrupts, poll on completion
+ */
+
+ result = sea_poll(unit, xs, scb);
+#ifdef SEADEBUG2
+ printf("=5 %lx", result);
+#endif
+ return result;
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new one. If so,
+ * put it in the hash table too, otherwise return an error or sleep.
+ */
+
+struct sea_scb *
+sea_get_scb(unit, flags)
+ int unit;
+ int flags;
+{
+ struct sea_data *sea = seadata[unit];
+ unsigned opri = 0;
+ struct sea_scb * scbp;
+ int hashnum;
+
+#ifdef SEADEBUG2
+/* printf("get_scb\n"); */
+ printf("(");
+#endif
+
+ if (!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+#ifdef SEADEBUG3
+ printf("(2 %lx ", sea->free_scb);
+#endif
+
+ /*
+ * If we can and have to, sleep waiting for one to come free
+ * but only if we can´t allocate a new one.
+ */
+ while (!(scbp = sea->free_scb)) {
+#ifdef SEADEBUG12
+ printf("(3");
+#endif
+ if (sea->numscb < SEA_SCB_MAX) {
+ printf("malloced new scbs\n");
+ if (scbp = (struct sea_scb *) malloc(sizeof(struct sea_scb),
+ M_TEMP, M_NOWAIT)) {
+ bzero(scbp, sizeof(struct sea_scb));
+ sea->numscb++;
+ scbp->flags = SCB_ACTIVE;
+ scbp->next = NULL;
+ } else {
+ printf("sea%d: Can't malloc SCB\n",unit);
+ }
+ goto gottit;
+ } else {
+#ifdef SEADEBUG12
+ printf("(4");
+#endif
+ if(!(flags & SCSI_NOSLEEP)) {
+#ifdef SEADEBUG2
+ printf("(5");
+#endif
+ sleep(&sea->free_scb, PRIBIO);
+ }
+ }
+ }
+ if (scbp) {
+#ifdef SEADEBUG2
+ printf("(6");
+#endif
+ /* Get SCB from free list */
+ sea->free_scb = scbp->next;
+ scbp->next = NULL;
+ scbp->flags = SCB_ACTIVE;
+ }
+ gottit:
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+
+ return(scbp);
+}
+
+/*
+ * sea_send_scb
+ *
+ * Try to send this command to the board. Because this board does not use any
+ * mailboxes, this routine simply adds the command to the queue held by the
+ * sea_data structure.
+ * A check is done to see if the command contains a REQUEST_SENSE command, and
+ * if so the command is put first in the queue, otherwise the command is added
+ * to the end of the queue. ?? Not correct ??
+ */
+int
+sea_send_scb(struct sea_data *sea, struct sea_scb *scb)
+{
+ struct sea_scb *tmp;
+ int oldpri = 0;
+
+#ifdef SEADEBUG2
+ printf("+");
+#endif
+
+ if(!(scb->xfer->flags & SCSI_NOSLEEP)) {
+ oldpri = splbio();
+ }
+
+ /* add to head of queue if queue empty or command is REQUEST_SENSE */
+
+ if (!(sea->issue_queue)
+#ifdef SEA_SENSEFIRST
+ || (scb->xfer->cmd->opcode == (u_char) REQUEST_SENSE)
+#endif
+ ) {
+#ifdef SEADEBUG2
+ printf("+2");
+#endif
+ scb->next = sea->issue_queue;
+ sea->issue_queue = scb;
+ } else {
+#ifdef SEADEBUG2
+ printf("+3");
+#endif
+ for (tmp = sea->issue_queue; tmp->next; tmp = tmp->next);
+ tmp->next = scb;
+ scb->next = NULL; /* placed at the end of the queue */
+ }
+ /* Try to do some work on the card */
+ if (!main_running) {
+ main_running = 1;
+ sea_main();
+ /* main running is cleared in sea_main once it can't
+ * do more work, and sea_main exits with interrupts
+ * disabled
+ */
+ }
+ if(!(scb->xfer->flags & SCSI_NOSLEEP)) {
+ splx(oldpri);
+ }
+ return (1); /* No possible errors right now */
+}
+
+/*
+ * sea_main(void)
+ *
+ * corroutine that runs as long as more work can be done on the seagate host
+ * adapter in a system. Both sea_scsi_cmd and sea_intr will try to start it in
+ * case it is not running.
+ */
+
+static void sea_main(void)
+{
+ struct sea_data *sea; /* This time we look at all cards */
+ struct sea_scb *tmp, *prev;
+ int done;
+ int unit;
+ int oldpri;
+
+#ifdef SEADEBUG2
+ printf(".");
+#endif
+
+ /*
+ * This should not be run with interrupts disabled, but use the splx code
+ * instead
+ */
+ do {
+ done = 1;
+ for (sea=seadata[unit=0]; (unit < NSEA) && seadata[unit] ;
+ sea=seadata[++unit]) {
+ oldpri = splbio();
+ if (!sea->connected) {
+#ifdef SEADEBUG2
+ printf(".2");
+#endif
+ /*
+ * Search through the issue_queue for a command destined for a
+ * target that's not busy.
+ */
+ for (tmp = sea->issue_queue, prev = NULL; tmp ;
+ prev = tmp, tmp = tmp->next)
+ /* When we find one, remove it from the issue queue. */
+ if (!(sea->busy[tmp->xfer->sc_link->target] &
+ (1 << tmp->xfer->sc_link->lun))) {
+ if (prev)
+ prev->next = tmp->next;
+ else
+ sea->issue_queue = tmp->next;
+ tmp->next = NULL;
+
+ /* re-enable interrupts after finding one */
+ splx(oldpri);
+
+ /*
+ * Attempt to establish an I_T_L nexus here.
+ * On success, sea->connected is set.
+ * On failure, we must add the command back to
+ * the issue queue so we can keep trying.
+ */
+#ifdef SEADEBUG2
+ printf(".3");
+#endif
+
+ /* REQUEST_SENSE commands are issued without tagged
+ * queueing, even on SCSI-II devices because the
+ * contingent alligence condition exists for the
+ * entire unit.
+ */
+
+ /* First check that if any device has tried a reconnect while
+ * we have done other things with interrupts disabled
+ */
+
+ if ((STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) {
+#ifdef SEADEBUG2
+ printf(".7");
+#endif
+ sea_reselect(sea);
+ break;
+ }
+ if (!sea_select(sea, tmp)) {
+#ifdef SEADEBUG2
+ /* printf("Select returned ok\n"); */
+ printf(".4");
+#endif
+ break;
+ } else {
+ oldpri = splbio();
+ tmp->next = sea->issue_queue;
+ sea->issue_queue = tmp;
+ splx(oldpri);
+ printf("sea_main: select failed\n");
+ }
+ } /* if target/lun is not busy */
+ } /* if (!sea->connected) */
+
+ if (sea->connected) { /* we are connected. Do the task */
+ splx(oldpri);
+#ifdef SEADEBUG2
+/* printf("sea_main: starting information transfer!\n"); */
+ printf(".5");
+#endif
+ sea_information_transfer(sea);
+#ifdef SEADEBUG2
+/* printf("sea_main: sea->connected:%lx\n", sea->connected); */
+ printf(".6%lx ", sea->connected);
+#endif
+ done = 0;
+ } else
+ break;
+ } /* for instance */
+ } while (!done);
+ main_running = 0;
+}
+
+void
+sea_free_scb(unit, scb, flags)
+ int unit;
+ struct sea_scb *scb;
+ int flags;
+{
+ struct sea_data *sea = seadata[unit];
+ unsigned int opri = 0;
+
+#ifdef SEADEBUG2
+/* printf("free_scb\n"); */
+ printf(")");
+#endif
+
+ if(!(flags & SCSI_NOMASK))
+ opri = splbio();
+
+ scb->next = sea->free_scb;
+ sea->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) {
+#ifdef SEADEBUG2
+/* printf("free_scb waking up sleep\n"); */
+ printf(")2");
+#endif
+#ifdef SEA_FREEBSD11
+ wakeup((caddr_t)&sea->free_scb);
+#else
+ wakeup(&sea->free_scb);
+#endif
+ }
+
+ if (!(flags & SCSI_NOMASK))
+ splx(opri);
+}
+
+#ifdef SEA_FREEBSD11
+void
+sea_timeout(caddr_t arg1, int arg2)
+#else
+void
+sea_timeout(struct sea_scb *scb)
+#endif
+{
+#ifdef SEA_FREEBSD11
+ struct sea_scb *scb = (struct sea_scb *)arg1;
+#endif
+ int unit;
+ struct sea_data *sea;
+ int s=splbio();
+
+#ifdef SEADEBUG2
+/* printf("sea_timeout called\n"); */
+ printf(":");
+#endif
+
+ unit = scb->xfer->sc_link->adapter_unit;
+ sea = seadata[unit];
+#ifndef SEADEBUG /* print message only if not waiting unless debug */
+ if(!(scb->xfer->flags & SCSI_NOMASK))
+#endif
+ printf("sea%d:%d:%d (%s%d) timed out ", 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 (/* (sea_abort(unit, scb) != 1) ||*/ (scb->flags & SCB_ABORTED)) {
+ /*
+ * abort timed out
+ */
+#ifdef SEADEBUG2
+/* printf("sea%d: Abort Operation has timed out\n", unit); */
+ printf(":2");
+#endif
+ scb->xfer->retries = 0;
+ scb->flags |= SCB_ABORTED;
+ sea_done(unit, scb);
+ } else {
+ #ifdef SEADEBUG2
+ /* printf("sea%d: Try to abort\n", unit); */
+ printf(":3");
+ #endif
+ sea_abort(unit, scb);
+ /* sea_send_scb(sea, ~SCSI_NOMASK, SEA_SCB_ABORT, scb); */
+ /* 2 seconds for the abort */
+ #ifdef SEA_FREEBSD11
+ timeout(sea_timeout, (caddr_t)scb, 2*hz);
+ #else
+ timeout(sea_timeout, scb, 2*hz);
+ #endif
+ scb->flags |= (SCB_ABORTED | SCB_TIMECHK);
+ }
+ splx(s);
+}
+
+int
+sea_reselect(sea)
+ struct sea_data *sea;
+{
+ unsigned char target_mask;
+ long l;
+ unsigned char lun, phase;
+ unsigned char msg[3];
+ int32 len;
+ u_char *data;
+ struct sea_scb *tmp = 0, *prev = 0;
+ int abort = 0;
+
+#if SEADEBUG2
+/* printf("sea_reselect called\n"); */
+ printf("}");
+#endif
+
+ if (!((target_mask = STATUS) & STAT_SEL)) {
+ printf("sea: wrong state 0x%x\n", target_mask);
+ return(0);
+ }
+ /* wait for a device to win the reselection phase */
+ /* signals this by asserting the I/O signal */
+ for(l=10; l && (STATUS & (STAT_SEL | STAT_IO | STAT_BSY))
+ != (STAT_SEL | STAT_IO | 0);l--);
+ /* !! Check for timeout here */
+ /* the data bus contains original initiator id ORed with target id */
+ target_mask = DATA;
+ /* see that we really are the initiator */
+ if (!(target_mask & ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40))) {
+ printf("sea: polled reselection was not for me: %x\n",target_mask);
+ return(0);
+ }
+ /* find target who won */
+ target_mask &= ((sea->ctrl_type == SEAGATE) ? ~0x80 : ~0x40);
+ /* host responds by asserting the BSY signal */
+ CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY);
+ /* target should respond by deasserting the SEL signal */
+ for(l=50000;l && (STATUS & STAT_SEL);l++);
+ /* remove the busy status */
+ CONTROL = (BASE_CMD | CMD_DRVR_ENABLE);
+ /* we are connected. Now we wait for the MSGIN condition */
+ for(l=50000; l && !(STATUS & STAT_REQ);l--);
+ /* !! Add timeout check here */
+ /* hope we get an IDENTIFY message */
+ len = 3;
+ data = msg;
+ phase = REQ_MSGIN;
+ sea_transfer_pio(sea, &phase, &len, &data);
+
+ if (!(msg[0] & 0x80)) {
+ printf("sea: Expecting IDENTIFY message, got 0x%x\n", msg[0]);
+ abort = 1;
+ } else {
+ lun = (msg[0] & 0x07);
+
+ /*
+ * 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.
+ */
+
+ for(tmp = sea->disconnected_queue, prev = NULL;
+ tmp; prev=tmp, tmp = tmp->next)
+ if((target_mask == (1 << tmp->xfer->sc_link->target)) &&
+ (lun == tmp->xfer->sc_link->lun)) {
+ if(prev) {
+#ifdef SEADEBUG2
+ printf("}2");
+#endif
+ prev->next = tmp->next;
+ } else {
+#ifdef SEADEBUG2
+ printf("}3");
+#endif
+ sea->disconnected_queue = tmp->next;
+ }
+ tmp->next = NULL;
+ break;
+ }
+ if (!tmp) {
+ printf("sea: warning : target %02x lun %d not in disconnect_queue\n",
+ target_mask, lun);
+ /*
+ * Since we have an established nexus that we can't do anything with,
+ * we must abort it.
+ */
+ abort = 1;
+ }
+ }
+
+ if(abort) {
+#ifdef SEADEBUG2
+ printf("}4");
+#endif
+ msg[0] = MSG_ABORT;
+ len = 1;
+ data = msg;
+ phase = REQ_MSGOUT;
+ CONTROL = (BASE_CMD | CMD_ATTN);
+ sea_transfer_pio(sea, &phase, &len, &data);
+ } else {
+#ifdef SEADEBUG2
+ printf("}5");
+#endif
+ sea->connected = tmp;
+ }
+ /* return value not used yet */
+ return 0;
+}
+
+/* Transfer data in given phase using polled I/O
+*/
+
+int sea_transfer_pio(struct sea_data *sea, u_char *phase, int32 *count,
+ u_char **data)
+{
+ register unsigned char p = *phase, tmp;
+ register int c = *count;
+ register unsigned char *d = *data;
+ unsigned long int timeout;
+
+#if SEADEBUG2
+/* printf("sea_transfer_pio called: len:%x\n",c); */
+ printf("-1 %x %x", c, p);
+#endif
+
+ do {
+ /* wait for assertion of REQ, after which the phase bits will be valid */
+ for(timeout = 0; timeout < 5000000L ; timeout++)
+ if ((tmp = STATUS) & STAT_REQ)
+ break;
+ if (!(tmp & STAT_REQ)) {
+ printf("sea_transfer_pio: timeout waiting for STAT_REQ\n");
+ break;
+ }
+
+ /* check for phase mismatch */
+ /* Reached if the target decides that it has finished the transfer */
+ if ((tmp & REQ_MASK) != p) {
+#ifdef SEADEBUG1
+/* printf("-2 %x", tmp); */
+ printf("sea:pio phase mismatch:%x, want:%x, len:%x\n",tmp,p,c);
+#endif
+ break;
+ }
+
+ /* Do actual transfer from SCSI bus to/from memory */
+ if (!(p & STAT_IO))
+ DATA = *d;
+ else
+ *d = DATA;
+#ifdef SEADEBUG15
+ printf("-7%x", *d);
+#endif
+ ++d;
+
+ /* The SCSI standard suggests that in MSGOUT phase, the initiator
+ * should drop ATN on the last byte of the message phase
+ * after REQ has been asserted for the handshake but before
+ * the initiator raises ACK.
+ * Don't know how to accomplish this on the ST01/02
+ */
+ /* We don't mind right now. */
+
+ /* The st01 code doesn't wait for STAT_REQ to be deasserted. Is this ok? */
+/* for(timeout=0;timeout<200000L;timeout++)
+ if(!(STATUS & STAT_REQ))
+ break;
+ if(STATUS & STAT_REQ)
+ printf("timeout on wait for !STAT_REQ"); */
+/* printf("*"); */
+ } while (--c);
+
+ *count = c;
+ *data = d;
+ tmp = STATUS;
+ if (tmp & STAT_REQ) {
+#if SEADEBUG2
+ printf("-3%x", tmp);
+#endif
+ *phase = tmp & REQ_MASK;
+ } else {
+#if SEADEBUG2
+ printf("-4%x", tmp);
+#endif
+ *phase = REQ_UNKNOWN;
+ }
+ if (!c || (*phase == p)) {
+#if SEADEBUG2
+ printf("-5%x %x", c, *phase);
+#endif
+ return 0;
+ } else {
+#if SEADEBUG2
+ printf("-6");
+#endif
+ return -1;
+ }
+}
+
+/* sea_select
+ * 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 could not execute for some reason, 0 if selection
+ * succeded or failed because the taget did not respond
+ */
+int sea_select(struct sea_data *sea, struct sea_scb *scb)
+{
+ unsigned char tmp[3], phase;
+ u_char *data;
+ int32 len;
+ unsigned long timeout;
+
+#ifdef SEADEBUG2
+/* printf("sea_select called\n"); */
+ printf("{");
+#endif
+
+ CONTROL = BASE_CMD;
+ DATA = ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40);
+ CONTROL = (BASE_CMD & ~CMD_INTR) | CMD_START_ARB;
+ /* wait for arbitration to complete */
+ for (timeout = 0; timeout < 3000000L ; timeout++) {
+ if (STATUS & STAT_ARB_CMPL)
+ break;
+ }
+ if (!(STATUS & STAT_ARB_CMPL)) {
+ if (STATUS & STAT_SEL) {
+ printf("sea: arbitration lost\n");
+ scb->flags |= SCB_ERROR;
+ } else {
+ printf("sea: arbitration timeout.\n");
+ scb->flags |=SCB_TIMEOUT;
+ }
+ CONTROL = BASE_CMD;
+ return(-1);
+ }
+ DELAY(2);
+
+#if SEADEBUG2
+/* printf("after arbitration: STATUS=%x\n", STATUS); */
+ printf("{2 %x", STATUS);
+#endif
+
+ DATA = (unsigned char)((1 << scb->xfer->sc_link->target) |
+ ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40));
+#ifdef SEANOMSGS
+ CONTROL = (BASE_CMD & (~CMD_INTR))| CMD_DRVR_ENABLE | CMD_SEL;
+#else
+ CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE | CMD_SEL | CMD_ATTN;
+#endif
+ DELAY(1);
+ /* wait for a bsy from target */
+ for (timeout = 0; timeout < 2000000L; timeout++) {
+ if (STATUS & STAT_BSY)
+ break;
+ }
+
+#if SEADEBUG2
+/* printf("after wait for BSY: STATUS=%x,count=%lx\n", STATUS, timeout); */
+ printf("{3 %x %x", STATUS, timeout);
+#endif
+
+ if (!(STATUS & STAT_BSY)) {
+ /* should return some error to the higher level driver */
+ CONTROL = BASE_CMD;
+#if SEADEBUG2
+/* printf("sea: target did not respond\n"); */
+ printf("{4");
+#endif
+ scb->flags |= SCB_TIMEOUT;
+ return 0;
+ }
+
+ /* Try to make the target to take a message from us */
+#ifdef SEANOMSGS
+ CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE;
+#else
+ CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE | CMD_ATTN;
+#endif
+
+ DELAY(1);
+
+ /* should start a msg_out phase */
+ for (timeout = 0; timeout < 2000000L ; timeout++) {
+ if (STATUS & STAT_REQ)
+ break;
+ }
+
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
+
+#if SEADEBUG2 || SEADEBUG9
+/* printf("after wait for STAT_REQ: STATUS=%x,count=%lx\n", STATUS, timeout);
+ printf("2:nd try after wait for STAT_REQ: STATUS=%x\n", STATUS); */
+ printf("{5%x", timeout);
+#endif
+
+ if (!(STATUS & STAT_REQ)) {
+ /* 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!)
+ */
+#if SEADEBUG
+/* printf("{6"); */
+ printf("sea: WARNING: target %x don't support messages?\n",
+ scb->xfer->sc_link->target);
+#endif
+ } else {
+ tmp[0] = IDENTIFY(1, scb->xfer->sc_link->lun); /* allow disconnects */
+ len = 1;
+ data = tmp;
+ phase = REQ_MSGOUT;
+ /* Should do test on result of sea_transfer_pio */
+#if SEADEBUG2
+/* printf("Trying a msg out phase\n"); */
+ printf("{7");
+#endif
+ sea_transfer_pio(sea, &phase, &len, &data);
+ }
+ if (!(STATUS & STAT_BSY)) {
+ printf("sea: after successful arbitrate: No STAT_BSY!\n");
+ }
+
+#if SEADEBUG2
+ printf("{8");
+#endif
+ sea->connected = scb;
+ sea->busy[scb->xfer->sc_link->target] |= (1 << scb->xfer->sc_link->lun);
+ /* this assignment should depend on possibility to send a message to target */
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
+ /* reset pointer in command ??? */
+ return 0;
+}
+
+/* sea_abort
+ send an abort to the target
+ return 1 success, 0 on failure
+ */
+int sea_abort(int unit, struct sea_scb *scb)
+{
+ struct sea_data *sea = seadata[unit];
+ struct sea_scb *tmp, **prev;
+ unsigned char msg, phase, *msgptr;
+ int32 len;
+ int oldpri;
+
+#ifdef SEADEBUG2
+/* printf("sea_abort called\n"); */
+ printf("\\");
+#endif
+
+ oldpri = splbio();
+
+ /* If the command hasn't been issued yet, we simply remove it from the
+ * issue queue
+ */
+ for (prev = (struct sea_scb **) &(sea->issue_queue),
+ tmp = sea->issue_queue; tmp;
+ prev = (struct sea_scb **) &(tmp->next), tmp = tmp->next)
+ if (scb == tmp) {
+ (*prev) = tmp->next;
+ tmp->next = NULL;
+ /* set some type of error result for this operation */
+ splx(oldpri);
+#ifdef SEADEBUG2
+ printf("\\2");
+#endif
+ return 1;
+ }
+
+ /* If any commands are connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or issue a
+ * reset
+ */
+
+ if(sea->connected) {
+ splx(oldpri);
+#ifdef SEADEBUG2
+/* printf("sea:abort error connected\n"); */
+ printf("\\3");
+#endif
+ return 0;
+ }
+
+ /* If the command is currently disconnected from the bus, and there are
+ * no connected commands, 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 (tmp = sea->disconnected_queue; tmp; tmp = tmp->next)
+ if (scb == tmp) {
+ splx(oldpri);
+#ifdef SEADEBUG2
+ printf("\\4");
+#endif
+ if (sea_select(sea,scb)) {
+#ifdef SEADEBUG2
+ printf("\\5");
+#endif
+ return 0;
+ }
+ msg = MSG_ABORT;
+ msgptr = &msg;
+ len = 1;
+ phase = REQ_MSGOUT;
+ CONTROL = BASE_CMD | CMD_ATTN;
+ sea_transfer_pio(sea, &phase, &len, &msgptr);
+
+ oldpri = splbio();
+ for (prev = (struct sea_scb **) &(sea->disconnected_queue),
+ tmp = sea->disconnected_queue; tmp ;
+ prev = (struct sea_scb **) &(tmp->next), tmp = tmp->next)
+ if (scb == tmp) {
+ *prev = tmp->next;
+ tmp->next = NULL;
+ /* set some type of error result for the operation */
+#ifdef SEADEBUG2
+ printf("\\6");
+#endif
+ splx(oldpri);
+ return 1;
+ }
+ }
+
+ /* command not found in any queue, race condition in the code ? */
+
+ splx(oldpri);
+#ifdef SEADEBUG2
+/* printf("sea: WARNING: SCSI command probably completed successfully\n"
+ " before abortion\n"); */
+ printf("\\7");
+#endif
+ return 1;
+
+}
+
+void sea_done(int unit, struct sea_scb *scb)
+{
+ struct sea_data *sea = seadata[unit];
+ struct scsi_xfer *xs = scb->xfer;
+
+
+#ifdef SEADEBUG2
+/* printf("sea_done called\n"); */
+ printf("&");
+#endif
+
+ if (scb->flags & SCB_TIMECHK) {
+#ifdef SEADEBUG2
+ printf("&2");
+#endif
+#ifdef SEA_FREEBSD11
+ untimeout(sea_timeout, (caddr_t)scb);
+#else
+ untimeout(sea_timeout, scb);
+#endif
+ }
+
+ xs->resid = scb->datalen; /* How much of the buffer was not touched */
+
+ if ((scb->flags == SCB_ACTIVE) || (xs->flags & SCSI_ERR_OK)) {
+#ifdef SEADEBUG2
+/* printf("sea_done:Report no err in xs\n"); */
+ printf("&3");
+#endif
+/* xs->resid = 0; */
+/* xs->error = 0; */
+ } else {
+
+ if (!(scb->flags == SCB_ACTIVE)) {
+ if ((scb->flags & SCB_TIMEOUT) || (scb->flags & SCB_ABORTED)) {
+#ifdef SEADEBUG2
+ printf("&6");
+#endif
+ xs->error = XS_TIMEOUT;
+ }
+ if (scb->flags & SCB_ERROR) {
+#ifdef SEADEBUG2
+ printf("&7");
+#endif
+ xs->error = XS_DRIVER_STUFFUP;
+ }
+ } else {
+
+ /* !!! Add code to check for target status */
+ /* say all error now */
+ xs->error = XS_DRIVER_STUFFUP;
+#ifdef SEADEBUG2
+ printf("&4");
+#endif
+ }
+ }
+ xs->flags |= ITSDONE;
+ sea_free_scb(unit, scb, xs->flags);
+ scsi_done(xs);
+#ifdef SEADEBUG2
+/* printf("Leaving sea_done\n"); */
+ printf("&5");
+#endif
+}
+
+/* wait for completion of command in polled mode */
+
+int sea_poll(int unit, struct scsi_xfer *xs, struct sea_scb *scb)
+{
+ int count = 500; /* xs->timeout; */
+ int oldpri;
+
+#ifdef SEADEBUG2
+/* printf("sea_poll called\n"); */
+ printf("?");
+#endif
+
+ while (count) {
+ /* try to do something */
+ oldpri = splbio();
+ if (!main_running) {
+ main_running = 1;
+ sea_main();
+ /* main_running is cleared in sea_main once it can't
+ * do more work, and sea_main exits with interrupts
+ * disabled
+ */
+ splx(oldpri);
+ } else {
+ splx(oldpri);
+ }
+ if (xs->flags & ITSDONE) {
+ break;
+ }
+ DELAY(10);
+ count--;
+ }
+#ifdef SEADEBUG2
+ printf("?2 %x ", count);
+/* printf("sea_poll: count:%x\n",count); */
+#endif
+ if (count == 0) {
+ /* 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.
+ */
+#ifdef SEADEBUG2
+ printf("?3");
+#endif
+#ifdef SEA_FREEBSD11
+ sea_timeout((caddr_t)scb, 0);
+#else
+ sea_timeout(scb);
+#endif
+
+ /* because we are polling, take out the timeout entry
+ * sea_timeout made
+ */
+#ifdef SEADEBUG2
+ printf("?4");
+#endif
+#ifdef SEA_FREEBSD11
+ untimeout(sea_timeout, (caddr_t) scb);
+#else
+ untimeout(sea_timeout, scb);
+#endif
+ count = 50;
+ while (count) {
+ /* once again, wait for the int bit */
+ oldpri = splbio();
+ if (!main_running) {
+ main_running = 1;
+ sea_main();
+ /* main_running is cleared by sea_main once it can't
+ * do more work, and sea_main exits with interrupts
+ * disabled
+ */
+ splx(oldpri);
+ } else {
+ splx(oldpri);
+ }
+ if (xs->flags & ITSDONE) {
+ break;
+ }
+ DELAY(10);
+ count--;
+ }
+ if (count == 0) {
+ /* we timed out again... This is bad. Notice that
+ * this time there is no clock queue entry to remove
+ */
+#ifdef SEADEBUG2
+ printf("?5");
+#endif
+#ifdef SEA_FREEBSD11
+ sea_timeout((caddr_t)scb, 0);
+#else
+ sea_timeout(scb);
+#endif
+ }
+ }
+#ifdef SEADEBUG2
+/* printf("sea_poll: xs->error:%x\n",xs->error); */
+ printf("?6%x",xs->error);
+#endif
+ if (xs->error) {
+#ifdef SEADEBUG2
+/* printf("done return error\n"); */
+ printf("?7");
+#endif
+ return (HAD_ERROR);
+ }
+#ifdef SEADEBUG2
+/* printf("done return complete\n"); */
+ printf("?8");
+#endif
+ return (COMPLETE);
+}
+
+/*
+ * sea_information_transfer
+ * Do the transfer. We know we are connected. Update the flags,
+ * call sea_done when task accomplished. Dialog controlled by the
+ * target
+ */
+static void sea_information_transfer (struct sea_data *sea)
+{
+ long int timeout;
+ int unit = sea->sc_link.adapter_unit;
+ unsigned char msgout = MSG_NOP;
+ int32 len;
+ int oldpri;
+ u_char *data;
+ unsigned char phase, tmp, old_phase=REQ_UNKNOWN;
+ struct sea_scb *scb = sea->connected;
+ int loop;
+
+#if SEADEBUG2
+/* printf("sea_information_transfer called\n"); */
+ printf("!");
+#endif
+
+ for(timeout = 0; timeout < 10000000L ; timeout++) {
+ tmp = STATUS;
+ if (!(tmp & STAT_BSY)) {
+/* for(loop=0;loop < 20 ; loop++) {
+ if((tmp=STATUS) & STAT_BSY)
+ break;
+ } */
+#ifndef SEADEBUG8
+ if(!(tmp & STAT_BSY)) {
+ printf("sea: !STAT_BSY unit in data transfer!\n");
+ oldpri = splbio();
+ sea->connected = NULL;
+ scb->flags = SCB_ERROR;
+ splx(oldpri);
+ sea_done(unit, scb);
+ return;
+ }
+#endif
+ }
+
+ /* we only have a valid SCSI phase when REQ is asserted */
+ if (tmp & STAT_REQ) {
+ phase = (tmp & REQ_MASK);
+ if (phase != old_phase) {
+ old_phase = phase;
+ }
+
+#ifdef SEADEBUG7
+ printf("!2%x", phase);
+ for(loop=0;loop < 20; loop++) {
+ phase = STATUS;
+ printf("!6%x",phase);
+ phase = phase & REQ_MASK;
+ }
+#endif
+
+ switch (phase) {
+ case REQ_DATAOUT:
+#ifdef SEA_NODATAOUT
+ printf("sea: SEA_NODATAOUT set, attempted DATAOUT aborted\n");
+ msgout = MSG_ABORT;
+ CONTROL = BASE_CMD | CMD_ATTN;
+ break;
+#endif
+ case REQ_DATAIN:
+/* data = scb->xfer->data;
+ len = scb->xfer->datalen;
+*/ if(!(scb->data)) {
+ printf("no data address!\n");
+ }
+#ifdef SEA_BLINDTRANSFER
+ if (scb->datalen && !(scb->datalen % BLOCK_SIZE)) {
+ while (scb->datalen) {
+ for(timeout = 0; timeout < 5000000L ; timeout++)
+ if((tmp = STATUS) & STAT_REQ)
+ break;
+ if(!(tmp & STAT_REQ)) {
+ printf("sea_transfer_pio: timeout waiting for STAT_REQ\n");
+ /* getchar(); */
+ }
+ if((tmp & REQ_MASK) != phase) {
+#ifdef SEADEBUG1
+ printf("sea:infotransfer phase mismatch:%x, want:%x, len:%x\n",
+ tmp,phase,scb->datalen);
+ /* getchar(); */
+#endif
+ break;
+ }
+ if(!(phase & STAT_IO)) {
+#ifdef SEA_ASSEMBLER
+ asm("
+ shr $2, %%ecx;
+ cld;
+ rep;
+ movsl; " : :
+ "D" (sea->st0x_dr), "S" (scb->data), "c" (BLOCK_SIZE) :
+ "cx", "si", "di" );
+ scb->data += BLOCK_SIZE;
+#else
+ for(count=0; count < BLOCK_SIZE; count++) {
+ DATA = *(scb->data);
+ scb->data++;
+ }
+#endif
+ } else {
+#ifdef SEA_ASSEMBLER
+ asm("
+ shr $2, %%ecx;
+ cld;
+ rep;
+ movsl; " : :
+ "S" (sea->st0x_dr), "D" (scb->data), "c" (BLOCK_SIZE) :
+ "cx", "si", "di" );
+ scb->data += BLOCK_SIZE;
+#else
+ for(count=0; count < BLOCK_SIZE; count++) {
+ *scb->data = DATA;
+ scb->data++;
+ }
+#endif
+ }
+ scb->datalen -= BLOCK_SIZE;
+ }
+ }
+
+ /* save current position into the command structure */
+/* scb->xfer->data = data;
+ scb->xfer->datalen = len; */
+#endif
+
+ sea_transfer_pio(sea, &phase, &(scb->datalen), &(scb->data));
+/* scb->xfer->data = data;
+ scb->xfer->datalen = len;
+*/ break;
+
+ case REQ_MSGIN:
+ /* don't handle multi-byte messages here, because they
+ * should not be present here
+ */
+ len = 1;
+ data = &tmp;
+ sea_transfer_pio(sea, &phase, &len, &data);
+ /* scb->MessageIn = tmp; */
+
+ switch (tmp) {
+
+ case MSG_ABORT:
+ scb->flags = SCB_ABORTED;
+ printf("sea:Command aborted by target\n");
+ CONTROL = BASE_CMD;
+ sea_done(unit, scb);
+ return;
+
+ case MSG_COMMAND_COMPLETE:
+ oldpri = splbio();
+ sea->connected = NULL;
+ splx(oldpri);
+#ifdef SEADEBUG2
+ printf("!3");
+#endif
+ sea->busy[scb->xfer->sc_link->target] &=
+ ~(1 << scb->xfer->sc_link->lun);
+
+ CONTROL = BASE_CMD;
+ sea_done(unit, scb);
+ return;
+ case MSG_MESSAGE_REJECT:
+ /* printf("sea: message_reject recieved\n"); */
+ printf("!4");
+ break;
+ case MSG_DISCONNECT:
+ oldpri = splbio();
+ scb->next = sea->disconnected_queue;
+ sea->disconnected_queue = scb;
+ sea->connected = NULL;
+ CONTROL = BASE_CMD;
+ splx(oldpri);
+#ifdef SEADEBUG2
+/* printf("msg_disconnect\n"); */
+ printf("!5");
+#endif
+ return;
+ /* save/restore of pointers are ignored */
+ case MSG_SAVE_POINTERS:
+ case MSG_RESTORE_POINTERS:
+#if SEADEBUG2
+ printf("sea: rec save/restore ptrs\n");
+#endif
+ break;
+ default:
+ /* this should be handled in the pio data transfer phase, as the
+ * ATN should be raised before ACK goes false when rejecting a message
+ */
+#ifdef SEADEBUG
+ printf("sea: Unknown message in:%x\n", tmp);
+#endif
+ break;
+ } /* switch (tmp) */
+ break;
+ case REQ_MSGOUT:
+ len = 1;
+ data = &msgout;
+ /* sea->last_message = msgout; */
+ sea_transfer_pio(sea, &phase, &len, &data);
+ if (msgout == MSG_ABORT) {
+ printf("sea: sent message abort to target\n");
+ oldpri = splbio();
+ sea->busy[scb->xfer->sc_link->target] &=
+ ~(1 << scb->xfer->sc_link->lun);
+ sea->connected = NULL;
+ scb->flags = SCB_ABORTED;
+ splx(oldpri);
+ /* enable interrupt from scsi */
+ sea_done(unit, scb);
+ return;
+ }
+ msgout = MSG_NOP;
+ break;
+ case REQ_CMDOUT:
+ len = scb->xfer->cmdlen;
+ data = (char *) scb->xfer->cmd;
+ sea_transfer_pio(sea, &phase, &len, &data);
+ break;
+ case REQ_STATIN:
+ len = 1;
+ data = &tmp;
+ sea_transfer_pio(sea, &phase, &len, &data);
+ scb->xfer->status = tmp;
+ break;
+ default:
+ printf("sea: unknown phase\n");
+ } /* switch (phase) */
+ } /* if (tmp & STAT_REQ) */
+ } /* for (...) */
+ /* if we get here we have got a timeout! */
+ printf("sea: Timeout in data transfer\n");
+ scb->flags = SCB_TIMEOUT;
+ /* should I clear scsi-bus state? */
+ sea_done(unit, scb);
+}
+
+