From d5a160846a7f8957f06d7f5480a88bcd7f8f01d3 Mon Sep 17 00:00:00 2001 From: sos Date: Mon, 24 Oct 1994 22:14:34 +0000 Subject: [PATCH] Seagate st01/02 driver. Obtained from: FreeBSD-1.1.5.1 --- sys/i386/isa/seagate.c | 1988 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1988 insertions(+) create mode 100644 sys/i386/isa/seagate.c diff --git a/sys/i386/isa/seagate.c b/sys/i386/isa/seagate.c new file mode 100644 index 000000000000..8eeafd589adc --- /dev/null +++ b/sys/i386/isa/seagate.c @@ -0,0 +1,1988 @@ +/* + * (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 + * + * $Id: seagate.c,v 1.1 1994/07/03 20:50:32 sos Exp $ + */ + +/* + * 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 +#ifdef KERNEL /* don't laugh.. look for main() */ +#include "sea.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif /* KERNEL */ +#include +#include + +#ifndef KERNEL +#define NSEA 1 +#endif /* !KERNEL */ + +#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 */ + +/* 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); +void sea_timeout(caddr_t, int); +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 = +{ + sea_probe, + sea_attach, + "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_slotbasemaddr && 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]) && + jid_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; + printf("\n"); + /*****************************************************\ + * 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); + } + timeout((timeout_func_t)sea_timeout, (caddr_t)scb, (xs->timeout * hz)/1000); + 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 + tsleep(&sea->free_scb, PRIBIO, "seascb", 0); + } + } + } + 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 + wakeup((caddr_t)&sea->free_scb); + } + + if (!(flags & SCSI_NOMASK)) + splx(opri); +} + +void +sea_timeout(caddr_t arg1, int arg2) +{ + struct sea_scb *scb = (struct sea_scb *)arg1; + 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 */ + timeout((timeout_func_t)sea_timeout, (caddr_t)scb, 2*hz); + 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 + untimeout((timeout_func_t)sea_timeout, (caddr_t)scb); + } + + 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 + sea_timeout((caddr_t)scb, 0); + + /* because we are polling, take out the timeout entry + * sea_timeout made + */ +#ifdef SEADEBUG2 + printf("?4"); +#endif + untimeout((timeout_func_t)sea_timeout, (caddr_t) scb); + 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 + sea_timeout((caddr_t)scb, 0); + } + } +#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); +} + +