diff options
Diffstat (limited to 'sys/i386/isa/seagate.c')
-rw-r--r-- | sys/i386/isa/seagate.c | 1506 |
1 files changed, 1506 insertions, 0 deletions
diff --git a/sys/i386/isa/seagate.c b/sys/i386/isa/seagate.c new file mode 100644 index 0000000000000..8b2be3ff63d57 --- /dev/null +++ b/sys/i386/isa/seagate.c @@ -0,0 +1,1506 @@ +/* + * (Free/Net/386)BSD ST01/02, Future Domain TMC-885, TMC-950 SCSI driver for + * Julians SCSI-code + * + * Copyright 1994, Kent Palmkvist (kentp@isy.liu.se) + * Copyright 1994, Robert Knier (rknier@qgraph.com) + * Copyright 1992, 1994 Drew Eckhardt (drew@colorado.edu) + * Copyright 1994, Julian Elischer (julian@tfs.com) + * Copyright 1994-1995, Serge Vakulenko (vak@cronyx.ru) + * Copyright 1995 Stephen Hocking (sysseh@devetir.qld.gov.au) + * + * Others that has contributed by example code is + * Glen Overby (overby@cray.com) + * Tatu Yllnen + * Brian E Litzinger + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE DEVELOPERS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * kentp 940307 alpha version based on newscsi-03 version of Julians SCSI-code + * kentp 940314 Added possibility to not use messages + * rknier 940331 Added fast transfer code + * rknier 940407 Added assembler coded data transfers + * vak 941226 New probe algorithm, based on expected behaviour + * instead of BIOS signatures analysis, better timeout handling, + * new asm fragments for data input/output, target-dependent + * delays, device flags, polling mode, generic cleanup + * vak 950115 Added request-sense ops + * seh 950701 Fixed up Future Domain TMC-885 problems with disconnects, + * weird phases and the like. (we could probably investigate + * what the board's idea of the phases are, but that requires + * doco that I don't have). Note that it is slower than the + * 2.0R driver with both SEA_BLINDTRANSFER & SEA_ASSEMBLER + * defined by a factor of more than 2. I'll look at that later! + * seh 950712 The performance release 8^). Put in the blind transfer code + * from the 2.0R source. Don't use it by commenting out the + * SEA_BLINDTRANSFER below. Note that it only kicks in during + * DATAOUT or DATAIN and then only when the transfer is a + * multiple of BLOCK_SIZE bytes (512). Most devices fit into + * that category, with the possible exception of scanners and + * some of the older MO drives. + * + * $Id: seagate.c,v 1.30 1998/07/15 09:38:10 bde Exp $ + */ + +/* + * What should really be done: + * + * Restructure interrupt enable/disable code (runs too long with int disabled) + * Add code to handle Future Domain 840, 841, 880 and 881 + * Add code to use tagged commands in SCSI2 + * Add code to handle slow devices better (sleep if device not disconnecting) + * Fix unnecessary interrupts + */ + +/* Note to users trying to share a disk between DOS and unix: + * The ST01/02 is a translating host-adapter. It is not giving DOS + * the same number of heads/tracks/sectors as specified by the disk. + * It is therefore important to look at what numbers DOS thinks the + * disk has. Use these to disklabel your disk in an appropriate manner + * + * About ST02+IDE coexistence: the original Seagate ST02 + * BIOS cannot coexist with IDE or any other disk controller + * because it does not share BIOS disk drive numbers (80h, 81h) + * with others. New probing code allows using ST02 controller + * without BIOS: just unplug the ST02 BIOS chip from the board. + * + * Another problem is the floppy adapter on ST02 which could not be + * disabled by jumpers. I commonly use ST02 adapter as a cheap solution + * for atttaching the tape and CD-ROM drives, and an extra floppy controller + * is just a headache. I found a simple workaround: cutting off + * the AEN signal (A11 contact on ISA connector). AEN then goes high and + * disables the floppy adapter port address decoder. + * + * I also had a problem with ST02 conflicting with IDE during + * IDE data write phase. It seems than ST02 makes some noise + * on /IOW line. The /IOW line is used only for floppy controller + * part of ST02, and because I don't need it, I cut off the /IOW (contact B13) + * and it helped. (vak) + * + * Tested on the following hardware: + * Adapter: Seagate ST02 + * Disk: HP D1686 + * Streamers: Archive Viper 150, Wangtek 5525 + * CD-ROMs: Toshiba XM-3401, NEC CDR-25 + * + * Maximum data rate is about 270-280 kbytes/sec (on 386DX/40). + * (vak) + */ +#undef DEBUG + +#include "sea.h" +#if NSEA > 0 + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> + +#include <machine/clock.h> + +#include <vm/vm.h> +#include <vm/vm_param.h> +#include <vm/pmap.h> + +#include <i386/isa/isa_device.h> + +#include <scsi/scsiconf.h> + +#ifdef DEBUG +# define PRINT(s) printf s +#else +# define PRINT(s) /*void*/ +#endif + +#define SCB_TABLE_SIZE 8 /* start with 8 scb entries in table */ +#define BLOCK_SIZE 512 /* size of READ/WRITE areas on SCSI card */ +#define HOST_SCSI_ADDR 7 /* address of the adapter on the SCSI bus */ +#define SEA_BLINDTRANSFER 1 /* for quicker than quick xfers */ +/* + * Define config flags + */ +#define FLAG_NOPARITY 0x01 /* disable SCSI bus parity check */ + +/* + * Board CONTROL register + */ +#define CMD_RST 0x01 /* scsi reset */ +#define CMD_SEL 0x02 /* scsi select */ +#define CMD_BSY 0x04 /* scsi busy */ +#define CMD_ATTN 0x08 /* scsi attention */ +#define CMD_START_ARB 0x10 /* start arbitration bit */ +#define CMD_EN_PARITY 0x20 /* enable scsi parity generation */ +#define CMD_INTR 0x40 /* enable scsi interrupts */ +#define CMD_DRVR_ENABLE 0x80 /* scsi enable */ + +/* + * Board STATUS register + */ +#define STAT_BSY 0x01 /* scsi busy */ +#define STAT_MSG 0x02 /* scsi msg */ +#define STAT_IO 0x04 /* scsi I/O */ +#define STAT_CD 0x08 /* scsi C/D */ +#define STAT_REQ 0x10 /* scsi req */ +#define STAT_SEL 0x20 /* scsi select */ +#define STAT_PARITY 0x40 /* parity error bit */ +#define STAT_ARB_CMPL 0x80 /* arbitration complete bit */ +#define STAT_BITS "\20\1bsy\2msg\3i/o\4c/d\5req\6sel\7parity\10arb" + +/* + * SCSI bus phases + */ +#define PHASE_MASK (STAT_MSG | STAT_CD | STAT_IO) +#define PHASE_DATAOUT 0 +#define PHASE_DATAIN STAT_IO +#define PHASE_CMDOUT STAT_CD +#define PHASE_STATIN (STAT_CD | STAT_IO) +#define PHASE_MSGOUT (STAT_MSG | STAT_CD) +#define PHASE_MSGIN (STAT_MSG | STAT_CD | STAT_IO) +#define PHASE_NAME(ph) phase_name[(ph)>>2] + +static char *phase_name[] = { + "DATAOUT", "Phase1?", "Phase2?", "Phase3?", + "DATAIN", "Phase5?", "Phase6?", "Phase7?", + "CMDOUT", "Phase9?", "MSGOUT", "Phase11?", + "STATIN", "Phase13?", "MSGIN", "Phase15?", +}; + +/* + * SCSI message codes + */ +#define MSG_COMMAND_COMPLETE 0x00 +#define MSG_SAVE_POINTERS 0x02 +#define MSG_RESTORE_POINTERS 0x03 +#define MSG_DISCONNECT 0x04 +#define MSG_ABORT 0x06 +#define MSG_MESSAGE_REJECT 0x07 +#define MSG_NOP 0x08 +#define MSG_BUS_DEV_RESET 0x0c +#define MSG_IDENTIFY(lun) (0xc0 | ((lun) & 0x7)) +#define MSG_ISIDENT(m) ((m) & 0x80) + +/* + * SCSI control block used to keep info about a scsi command + */ +typedef struct scb { + int flags; /* status of the instruction */ +#define SCB_FREE 0x00 +#define SCB_ACTIVE 0x01 +#define SCB_ABORTED 0x02 +#define SCB_TIMEOUT 0x04 +#define SCB_ERROR 0x08 +#define SCB_TIMECHK 0x10 /* we have set a timeout on this one */ +#define SCB_SENSE 0x20 /* sensed data available */ +#define SCB_TBUSY 0x40 /* target busy */ + struct scb *next; /* in free list */ + struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */ + u_char *data; /* position in data buffer so far */ + int32_t datalen; /* bytes remaining to transfer */ +} scb_t; + +typedef enum { + CTLR_NONE, + CTLR_SEAGATE, + CTLR_FUTURE_DOMAIN +} ctlr_t; + +/* + * Flags for waiting for REQ deassert during some SCSI bus phases. + */ +typedef struct { + unsigned cmdout1 : 1; /* after CMDOUT[0] byte */ + unsigned cmdout : 1; /* after CMDOUT[1..N] bytes */ + unsigned msgout : 1; /* after MSGOUT byte */ + unsigned statin : 1; /* after STATIN byte */ +} phase_t; + +/* + * Data structure describing the target state. + */ +typedef struct { + struct adapter *adapter; /* pointer to the adapter structure */ + u_char busy; /* mask of busy luns at device target */ + u_long perrcnt; /* counter of target parity errors */ + phase_t ndelay; /* "don't delay" flags */ + phase_t init; /* "initialized" flags */ +} target_t; + +/* + * Data structure describing current status of the scsi bus. One for each + * controller card. + */ +typedef struct adapter { + ctlr_t type; /* Seagate or Future Domain */ + char *name; /* adapter name */ + volatile u_char *addr; /* base address for card */ + + volatile u_char *CONTROL; /* address of control register */ + volatile u_char *STATUS; /* address of status register */ + volatile u_char *DATA; /* address of data register */ + + u_char scsi_addr; /* our scsi address, 0..7 */ + u_char scsi_id; /* our scsi id mask */ + u_char parity; /* parity flag: CMD_EN_PARITY or 0 */ + u_char irq; /* IRQ number used or 0 if no IRQ */ + u_int timeout_active : 1; /* timeout() active (requested) */ + + struct scsi_link sc_link; /* struct connecting different data */ + scb_t *queue; /* waiting to be issued */ + scb_t *disconnected_queue; /* waiting to reconnect */ + + int numscb; /* number of scsi control blocks */ + scb_t *free_scb; /* free scb list */ + scb_t scbs[SCB_TABLE_SIZE]; + + target_t target[8]; /* target state data */ +} adapter_t; + +static adapter_t seadata[NSEA]; + +#define IS_BUSY(a,b) ((a)->target[(b)->xfer->sc_link->target].busy &\ + (1 << (b)->xfer->sc_link->lun)) +#define SET_BUSY(a,b) ((a)->target[(b)->xfer->sc_link->target].busy |=\ + (1 << (b)->xfer->sc_link->lun)) +#define CLEAR_BUSY(a,b) ((a)->target[(b)->xfer->sc_link->target].busy &=\ + ~(1 << (b)->xfer->sc_link->lun)) + +/* + * Wait for condition, given as an boolean expression. + * Print the message on timeout. + */ +#define WAITFOR(condition,message) {\ + register u_long cnt = 100000; char *_msg = message;\ + while (cnt-- && ! (condition)) continue;\ + if (cnt == -1 && _msg)\ + printf ("sea: %s timeout\n", _msg); } + +#define WAITFOR10(condition,message) {\ + register u_long cnt = 1000000; char *_msg = message;\ + while (cnt-- && ! (condition)) continue;\ + if (cnt == -1 && _msg)\ + printf ("sea: %s timeout\n", _msg); } + +/* + * Seagate adapter does not support in hardware + * waiting for REQ deassert after transferring each data byte. + * We must do it in software. + * The problem is that some SCSI devices deassert REQ so fast that + * we can miss it. We the flag for each target sayind if we should (not) + * wait for REQ deassert. This flag is initialized when the first + * operation on the target is done. + * 1) Test if we don't need to wait for REQ deassert (`nodelay' flag). + * Initially the flag is off, i.e. wait. If the flag is set, + * go to the step 4. + * 2) Wait for REQ deassert (call sea_wait_for_req_deassert function). + * If REQ deassert got, go to the step 4. If REQ did not cleared + * during timeout period, go to the next step. + * 3) If `nodelay' flag did not initialized yet (`init' flag), + * then set `ndelay' flag. + * 4) Set `init' flag. Done. + */ +#define WAITREQ(t,op,cnt) {\ + if (! (t)->ndelay.op &&\ + ! sea_wait_for_req_deassert ((t)->adapter, cnt, #op) &&\ + ! (t)->init.op)\ + (t)->ndelay.op = 1;\ + (t)->init.op = 1; } + +static int sea_probe (struct isa_device *dev); +static int sea_detect (adapter_t *z, struct isa_device *dev); +static int sea_attach (struct isa_device *dev); +static int32_t sea_scsi_cmd (struct scsi_xfer *xs); +static u_int32_t sea_adapter_info (int unit); +static void sea_timeout (void *scb); +static void seaminphys (struct buf *bp); +static void sea_done (adapter_t *z, scb_t *scb); +static void sea_start (adapter_t *z); +static void sea_information_transfer (adapter_t *z, scb_t *scb); +static int sea_poll (adapter_t *z, scb_t *scb); +static int sea_init (adapter_t *z); +static int sea_reselect (adapter_t *z); +static int sea_select (volatile adapter_t *z, scb_t *scb); +static int sea_abort (adapter_t *z, scb_t *scb); +static void sea_send_abort (adapter_t *z); +static u_char sea_msg_input (adapter_t *z); +static void sea_tick (void *arg); +static int sea_sense (adapter_t *z, scb_t *scb); +static void sea_data_output (adapter_t *z, u_char **pdata, u_long *plen); +static void sea_data_input (adapter_t *z, u_char **pdata, u_long *plen); +static void sea_cmd_output (target_t *z, u_char *cmd, int cmdlen); + +static struct scsi_adapter sea_switch = { + sea_scsi_cmd, seaminphys, 0, 0, sea_adapter_info, "sea", {0}, +}; +static struct scsi_device sea_dev = { NULL, NULL, NULL, NULL, "sea", 0, {0} }; +struct isa_driver seadriver = { sea_probe, sea_attach, "sea" }; + +/* FD TMC885's can't handle detach & re-attach */ +static int sea_select_cmd = CMD_DRVR_ENABLE | CMD_ATTN; + +/* + * Check if the device can be found at the port given and if so, + * detect the type of board. Set it up ready for further work. + * Takes the isa_dev structure from autoconf as an argument. + * Returns 1 if card recognized, 0 if errors. + */ +int sea_probe (struct isa_device *dev) +{ + adapter_t *z = &seadata[dev->id_unit]; + static const int addrtab[] = { + 0xc8000, 0xca000, 0xcc000, 0xce000, 0xdc000, 0xde000, 0, + }; + int i; + + /* Init fields used by our routines */ + z->parity = (dev->id_flags & FLAG_NOPARITY) ? 0 : CMD_EN_PARITY; + z->scsi_addr = HOST_SCSI_ADDR; + z->scsi_id = 1 << z->scsi_addr; + z->irq = dev->id_irq ? ffs (dev->id_irq) - 1 : 0; + z->queue = 0; + z->disconnected_queue = 0; + for (i=0; i<8; i++) { + z->target[i].adapter = z; + z->target[i].busy = 0; + } + + /* Link up the free list of scbs */ + z->numscb = SCB_TABLE_SIZE; + z->free_scb = z->scbs; + for (i=1; i<SCB_TABLE_SIZE; i++) + z->scbs[i-1].next = z->scbs + i; + z->scbs[SCB_TABLE_SIZE-1].next = 0; + + /* Detect the adapter. */ + dev->id_msize = 0x4000; + if (! dev->id_maddr) + for (i=0; addrtab[i]; ++i) { + dev->id_maddr = (u_char*) KERNBASE + addrtab[i]; + if (sea_detect (z, dev)) + return (1); + } + else if (sea_detect (z, dev)) + return (1); + + bzero (z, sizeof (*z)); + return (0); +} + +int sea_detect (adapter_t *z, struct isa_device *dev) +{ + z->addr = dev->id_maddr; + + /* Try Seagate. */ + z->type = CTLR_SEAGATE; + z->name = "Seagate ST01/ST02"; + z->CONTROL = z->addr + 0x1a00; /* ST01/ST02 register offsets */ + z->STATUS = z->addr + 0x1a00; + z->DATA = z->addr + 0x1c00; + if (sea_init (z) == 0) + return (1); + + /* Try Future Domain. */ + z->type = CTLR_FUTURE_DOMAIN; + z->name = "Future Domain TMC-885/TMC-950"; + z->CONTROL = z->addr + 0x1c00; /* TMC-885/TMC-950 reg. offsets */ + z->STATUS = z->addr + 0x1c00; + z->DATA = z->addr + 0x1e00; + /* FD TMC885's can't handle detach & re-attach */ + sea_select_cmd = CMD_DRVR_ENABLE; + /* FD TMC-885 is supposed to be at id 6. How strange. */ + z->scsi_addr = HOST_SCSI_ADDR - 1; + z->scsi_id = 1 << z->scsi_addr; + if (sea_init (z) == 0) + return (1); + + return (0); +} + +/* + * Probe the adapter, and if found, reset the board and the scsi bus. + * Return 0 if the adapter found. + */ +int sea_init (adapter_t *z) +{ + volatile u_char *p; + int i, c; + + /* Check that STATUS..STATUS+200h are equal. */ + p = z->STATUS; + c = *p; + if (c == 0xff) + return (2); + while (++p < z->STATUS+0x200) + if (*p != c) + return (3); + + /* Check that DATA..DATA+200h are equal. */ + for (p=z->DATA, c= *p++; p<z->DATA+0x200; ++p) + if (*p != c) + return (4); + + /* Check that addr..addr+1800h are not writable. */ + for (p=z->addr; p<z->addr+0x1800; ++p) { + c = *p; + *p = ~c; + if (*p == ~c) { + *p = c; + return (5); + } + } + + /* Check that addr+1800h..addr+1880h are writable. */ + for (p=z->addr+0x1800; p<z->addr+0x1880; ++p) { + c = *p; + *p = 0x55; + if (*p != 0x55) { + *p = c; + return (6); + } + *p = 0xaa; + if (*p != 0xaa) { + *p = c; + return (7); + } + } + + /* Reset the scsi bus (I don't know if this is needed). */ + *z->CONTROL = CMD_RST | CMD_DRVR_ENABLE | z->parity | CMD_INTR; + /* Hold reset for at least 25 microseconds. */ + DELAY (25); + /* Check that status cleared. */ + if (*z->STATUS != 0) { + *z->CONTROL = 0; + return (8); + } + + /* Check that DATA register is writable. */ + for (i=0; i<256; ++i) { + *z->DATA = i; + if (*z->DATA != i) { + *z->CONTROL = 0; + return (9); + } + } + + /* Enable the adapter. */ + *z->CONTROL = CMD_INTR | z->parity; + /* Wait a Bus Clear Delay (800 ns + bus free delay 800 ns). */ + DELAY (10); + + /* Check that DATA register is NOT writable. */ + c = *z->DATA; + for (i=0; i<256; ++i) { + *z->DATA = i; + if (*z->DATA != c) { + *z->CONTROL = 0; + return (10); + } + } + + return (0); +} + +/* + * Attach all sub-devices we can find. + */ +int sea_attach (struct isa_device *dev) +{ + int unit = dev->id_unit; + adapter_t *z = &seadata[unit]; + struct scsibus_data *scbus; + + printf ("\nsea%d: type %s%s\n", unit, z->name, + (dev->id_flags & FLAG_NOPARITY) ? ", no parity" : ""); + + /* fill in the prototype scsi_link */ + z->sc_link.adapter_unit = unit; + z->sc_link.adapter_targ = z->scsi_addr; + z->sc_link.adapter_softc = z; + z->sc_link.adapter = &sea_switch; + z->sc_link.device = &sea_dev; + + /* + * Prepare the scsibus_data area for the upperlevel + * scsi code. + */ + scbus = scsi_alloc_bus(); + if(!scbus) + return 0; + scbus->adapter_link = &z->sc_link; + + /* ask the adapter what subunits are present */ + scsi_attachdevs (scbus); + + return (1); +} + +/* + * Return some information to the caller about + * the adapter and its capabilities. + */ +u_int32_t sea_adapter_info (int unit) +{ + return (1); +} + +void seaminphys (struct buf *bp) +{ +} + +/* + * Catch an interrupt from the adaptor. + */ +void seaintr (int unit) +{ + adapter_t *z = &seadata[unit]; + + PRINT (("sea%d: interrupt status=%b\n", unit, *z->STATUS, STAT_BITS)); + sea_start (z); +} + +/* + * This routine is used in the case when we have no IRQ line (z->irq == 0). + * It is called every timer tick and polls for reconnect from target. + */ +void sea_tick (void *arg) +{ + adapter_t *z = arg; + int x = splbio (); + + z->timeout_active = 0; + sea_start (z); + if (z->disconnected_queue && ! z->timeout_active) { + timeout (sea_tick, z, 1); + z->timeout_active = 1; + } + splx (x); +} + +/* + * Start a scsi operation given the command and the data address. + * Also needs the unit, target and lu. Get a free scb and set it up. + * Call send_scb. Either start timer or wait until done. + */ +int32_t sea_scsi_cmd (struct scsi_xfer *xs) +{ + int flags = xs->flags, x = 0; + adapter_t *z = (adapter_t *)xs->sc_link->adapter_softc; + scb_t *scb; + + PRINT (("sea%d/%d/%d command 0x%x\n", unit, xs->sc_link->target, + xs->sc_link->lun, xs->cmd->opcode)); + if (xs->bp) + flags |= SCSI_NOSLEEP; + if (flags & ITSDONE) { + printf ("sea%d: already done?", xs->sc_link->adapter_unit); + xs->flags &= ~ITSDONE; + } + if (! (flags & INUSE)) { + printf ("sea%d: not in use?", xs->sc_link->adapter_unit); + xs->flags |= INUSE; + } + if (flags & SCSI_RESET) + printf ("sea%d: SCSI_RESET not implemented\n", + xs->sc_link->adapter_unit); + + if (! (flags & SCSI_NOMASK)) + x = splbio (); + + /* Get a free scb. + * If we can and have to, sleep waiting for one to come free. */ + while (! (scb = z->free_scb)) { + if (flags & SCSI_NOSLEEP) { + xs->error = XS_DRIVER_STUFFUP; + if (! (flags & SCSI_NOMASK)) + splx (x); + return (TRY_AGAIN_LATER); + } + tsleep ((caddr_t)&z->free_scb, PRIBIO, "seascb", 0); + } + /* Get scb from free list. */ + z->free_scb = scb->next; + scb->next = 0; + scb->flags = SCB_ACTIVE; + + /* Put all the arguments for the xfer in the scb */ + scb->xfer = xs; + scb->datalen = xs->datalen; + scb->data = xs->data; + + /* Setup the scb to contain necessary values. + * The interesting values can be read from the xs that is saved. + * I therefore think that the structure can be kept very small. + * The driver doesn't use DMA so the scatter/gather is not needed? */ + if (! z->queue) { + scb->next = z->queue; + z->queue = scb; + } else { + scb_t *q; + + for (q=z->queue; q->next; q=q->next) + continue; + q->next = scb; + scb->next = 0; /* placed at the end of the queue */ + } + + /* Try to send this command to the board. */ + sea_start (z); + + /* Usually return SUCCESSFULLY QUEUED. */ + if (! (flags & SCSI_NOMASK)) { + splx (x); + if (xs->flags & ITSDONE) + /* Timeout timer not started, already finished. + * Tried to return COMPLETE but the machine hanged + * with this. */ + return (SUCCESSFULLY_QUEUED); + xs->timeout_ch = timeout (sea_timeout, (caddr_t) scb, + (xs->timeout * hz) / 1000); + scb->flags |= SCB_TIMECHK; + PRINT (("sea%d/%d/%d command queued\n", + xs->sc_link->adapter_unit, + xs->sc_link->target, xs->sc_link->lun)); + return (SUCCESSFULLY_QUEUED); + } + + /* If we can't use interrupts, poll on completion. */ + if (! sea_poll (z, scb)) { + /* We timed out, so call the timeout handler manually, + * accounting for the fact that the clock is not running yet + * by taking out the clock queue entry it makes. */ + sea_timeout ((void*) scb); + + /* Because we are polling, take out the timeout entry + * sea_timeout made. */ + untimeout (sea_timeout, (void*) scb, xs->timeout_ch); + + if (! sea_poll (z, scb)) + /* We timed out again... This is bad. Notice that + * this time there is no clock queue entry to remove. */ + sea_timeout ((void*) scb); + } + PRINT (("sea%d/%d/%d command %s\n", xs->sc_link->adapter_unit, + xs->sc_link->target, xs->sc_link->lun, + xs->error ? "failed" : "done")); + return (xs->error ? HAD_ERROR : COMPLETE); +} + +/* + * Coroutine that runs as long as more work can be done. + * Both scsi_cmd() and intr() will try to start it in + * case it is not running. + * Always called with interrupts disabled. + */ +void sea_start (adapter_t *z) +{ + scb_t *q, *prev; +again: + /* First check that if any device has tried + * a reconnect while we have done other things + * with interrupts disabled. */ + if (sea_reselect (z)) + goto again; + + /* Search through the queue for a command + * destined for a target that's not busy. */ + for (q=z->queue, prev=0; q; prev=q, q=q->next) { + /* Attempt to establish an I_T_L nexus here. */ + if (IS_BUSY (z, q) || ! sea_select (z, q)) + continue; + + /* Remove the command from the issue queue. */ + if (prev) + prev->next = q->next; + else + z->queue = q->next; + q->next = 0; + + /* We are connected. Do the task. */ + sea_information_transfer (z, q); + goto again; + } +} + +void sea_timeout (void *arg) +{ + scb_t *scb = (scb_t*) arg; + adapter_t *z = (adapter_t *)scb->xfer->sc_link->adapter_softc; + int x = splbio (); + + if (! (scb->xfer->flags & SCSI_NOMASK)) + printf ("sea%d/%d/%d (%s%d) timed out\n", + scb->xfer->sc_link->adapter_unit, + scb->xfer->sc_link->target, + scb->xfer->sc_link->lun, + scb->xfer->sc_link->device->name, + scb->xfer->sc_link->dev_unit); + + /* If it has been through before, then a previous abort has failed, + * don't try abort again. */ + if (! (scb->flags & SCB_ABORTED)) { + sea_abort (z, scb); + /* 2 seconds for the abort */ + scb->xfer->timeout_ch = timeout (sea_timeout, + (caddr_t)scb, 2*hz); + scb->flags |= (SCB_ABORTED | SCB_TIMECHK); + } else { + /* abort timed out */ + scb->flags |= SCB_ABORTED; + scb->xfer->retries = 0; + sea_done (z, scb); + } + splx (x); +} + +/* + * Wait until REQ goes down. This is needed for some devices (CDROMs) + * after every MSGOUT, MSGIN, CMDOUT, STATIN request. + * Return true if REQ deassert found. + */ +static __inline int sea_wait_for_req_deassert (adapter_t *z, int cnt, char *msg) +{ + __asm __volatile (" + 1: testb $0x10, %2 + jz 2f + loop 1b + 2:" + : "=c" (cnt) /* output */ + : "0" (cnt), "m" (*z->STATUS)); /* input */ + if (! cnt) { + PRINT (("sea%d (%s) timeout waiting for !REQ\n", + z->sc_link.adapter_unit, msg)); + return (0); + } + /* PRINT (("sea_wait_for_req_deassert %s count=%d\n", msg, cnt)); */ + return (1); +} + +/* + * Establish I_T_L or I_T_L_Q nexus for new or existing command + * including ARBITRATION, SELECTION, and initial message out + * for IDENTIFY and queue messages. + * Return 1 if selection succeded. + */ +int sea_select (volatile adapter_t *z, scb_t *scb) +{ + /* Start arbitration. */ + *z->CONTROL = z->parity | CMD_INTR; + *z->DATA = z->scsi_id; + *z->CONTROL = CMD_START_ARB | z->parity; + + /* Wait for arbitration to complete. */ + WAITFOR (*z->STATUS & STAT_ARB_CMPL, "arbitration"); + if (! (*z->STATUS & STAT_ARB_CMPL)) { + if (*z->STATUS & STAT_SEL) { + printf ("sea: arbitration lost\n"); + scb->flags |= SCB_ERROR; + } else { + printf ("sea: arbitration timeout\n"); + scb->flags |= SCB_TIMEOUT; + } + *z->CONTROL = CMD_INTR | z->parity; + return (0); + } + DELAY (1); + + *z->DATA = (1 << scb->xfer->sc_link->target) | z->scsi_id; + *z->CONTROL = sea_select_cmd | CMD_SEL | z->parity; + DELAY (2); + + /* Wait for a bsy from target. + * If the target is not present on the bus, we get + * the timeout. Don't PRINT any message -- it's not an error. */ + WAITFOR (*z->STATUS & STAT_BSY, 0); + if (! (*z->STATUS & STAT_BSY)) { + /* The target does not respond. Not an error, though. */ + PRINT (("sea%d/%d/%d target does not respond\n", + z->sc_link.adapter_unit, scb->xfer->sc_link->target, + scb->xfer->sc_link->lun)); + *z->CONTROL = CMD_INTR | z->parity; + scb->flags |= SCB_TIMEOUT; + return (0); + } + + /* Try to make the target to take a message from us. + * Should start a MSGOUT phase. */ + *z->CONTROL = sea_select_cmd | z->parity; + DELAY (15); + WAITFOR (*z->STATUS & STAT_REQ, 0); + + if (z->type == CTLR_FUTURE_DOMAIN) + *z->CONTROL = CMD_INTR | z->parity | CMD_DRVR_ENABLE; + + WAITFOR (*z->STATUS & STAT_REQ, 0); + if (! (*z->STATUS & STAT_REQ)) { + PRINT (("sea%d/%d/%d timeout waiting for REQ\n", + z->sc_link.adapter_unit, scb->xfer->sc_link->target, + scb->xfer->sc_link->lun)); + scb->flags |= SCB_ERROR; + *z->CONTROL = CMD_INTR | z->parity; + return (0); + } + + /* Check for phase mismatch. FD 885 always seems to get this wrong! */ + if ((*z->STATUS & PHASE_MASK) != PHASE_MSGOUT && z->type != CTLR_FUTURE_DOMAIN) { + PRINT (("sea%d/%d/%d waiting for MSGOUT: invalid phase %s\n", + z->sc_link.adapter_unit, scb->xfer->sc_link->target, + scb->xfer->sc_link->lun, + PHASE_NAME (*z->STATUS & PHASE_MASK))); + scb->flags |= SCB_ERROR; + *z->CONTROL = CMD_INTR | z->parity; + return (0); + } + + /* Allow disconnects. (except for FD controllers) */ + if (z->type == CTLR_SEAGATE) { + *z->CONTROL = CMD_DRVR_ENABLE | z->parity; + *z->DATA = MSG_IDENTIFY (scb->xfer->sc_link->lun); + WAITREQ (&z->target[scb->xfer->sc_link->target], msgout, 1000); + } + *z->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | z->parity; + + SET_BUSY (z, scb); + return (1); +} + +int sea_reselect (adapter_t *z) +{ + scb_t *q = 0, *prev = 0; + u_char msg, target_mask, lun; +again: + /* Wait for a device to win the reselection phase. */ + /* Signals this by asserting the I/O signal. */ + if ((*z->STATUS & (STAT_SEL | STAT_IO | STAT_BSY)) != + (STAT_SEL | STAT_IO)) + return (0); + + /* The data bus contains original initiator id ORed with target id. */ + /* See that we really are the initiator. */ + target_mask = *z->DATA; + if (! (target_mask & z->scsi_id)) { + PRINT (("sea%d reselect not for me: mask=0x%x, status=%b\n", + z->sc_link.adapter_unit, target_mask, + *z->STATUS, STAT_BITS)); + goto again; + } + + /* Find target who won. */ + /* Host responds by asserting the BSY signal. */ + /* Target should respond by deasserting the SEL signal. */ + target_mask &= ~z->scsi_id; + *z->CONTROL = CMD_DRVR_ENABLE | CMD_BSY | z->parity | CMD_INTR; + WAITFOR (! (*z->STATUS & STAT_SEL), "reselection acknowledge"); + + /* Remove the busy status. */ + /* Target should set the MSGIN phase. */ + *z->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | z->parity; + WAITFOR (*z->STATUS & STAT_REQ, "identify message"); + + /* Hope we get an IDENTIFY message. */ + msg = sea_msg_input (z); + if (MSG_ISIDENT (msg)) { + /* Find the command corresponding to the I_T_L or I_T_L_Q + * nexus we just restablished, and remove it from + * the disconnected queue. */ + lun = (msg & 7); + for (q=z->disconnected_queue; q; prev=q, q=q->next) { + if (target_mask != (1 << q->xfer->sc_link->target)) + continue; + if (lun != q->xfer->sc_link->lun) + continue; + if (prev) + prev->next = q->next; + else + z->disconnected_queue = q->next; + q->next = 0; + PRINT (("sea%d/%d/%d reselect done\n", + z->sc_link.adapter_unit, + ffs (target_mask) - 1, lun)); + sea_information_transfer (z, q); + WAITFOR (! (*z->STATUS & STAT_BSY), "reselect !busy"); + return (1); + } + } else + printf ("sea%d reselect: expecting IDENTIFY, got 0x%x\n", + z->sc_link.adapter_unit, msg); + + /* Since we have an established nexus that we can't + * do anything with, we must abort it. */ + sea_send_abort (z); + PRINT (("sea%d reselect aborted\n", z->sc_link.adapter_unit)); + WAITFOR (! (*z->STATUS & STAT_BSY), "bus free after reselect abort"); + goto again; +} + +/* + * Send an abort to the target. + * Return 1 success, 0 on failure. + * Called on splbio level. + */ +int sea_abort (adapter_t *z, scb_t *scb) +{ + scb_t *q, **prev; + + /* If the command hasn't been issued yet, we simply remove it + * from the issue queue. */ + prev = &z->queue; + for (q=z->queue; q; q=q->next) { + if (scb == q) { + (*prev) = q->next; + q->next = 0; + return (1); + } + prev = &q->next; + } + + /* If the command is currently disconnected from the bus, + * we reconnect the I_T_L or I_T_L_Q nexus associated with it, + * go into message out, and send an abort message. */ + for (q=z->disconnected_queue; q; q=q->next) { + if (scb != q) + continue; + + if (! sea_select (z, scb)) + return (0); + sea_send_abort (z); + + prev = &z->disconnected_queue; + for (q=z->disconnected_queue; q; q=q->next) { + if (scb == q) { + *prev = q->next; + q->next = 0; + /* Set some type of error result + * for the operation. */ + return (1); + } + prev = &q->next; + } + } + + /* Command not found in any queue. */ + return (0); +} + +/* + * The task accomplished, mark the i/o control block as done. + * Always called with interrupts disabled. + */ +void sea_done (adapter_t *z, scb_t *scb) +{ + struct scsi_xfer *xs = scb->xfer; + + if (scb->flags & SCB_TIMECHK) + untimeout (sea_timeout, (caddr_t) scb, xs->timeout_ch); + + /* How much of the buffer was not touched. */ + xs->resid = scb->datalen; + + if (scb->flags != SCB_ACTIVE && ! (xs->flags & SCSI_ERR_OK)) + if (scb->flags & (SCB_TIMEOUT | SCB_ABORTED)) + xs->error = XS_TIMEOUT; + else if (scb->flags & SCB_ERROR) + xs->error = XS_DRIVER_STUFFUP; + else if (scb->flags & SCB_TBUSY) + xs->error = XS_BUSY; + else if (scb->flags & SCB_SENSE) + xs->error = XS_SENSE; + + xs->flags |= ITSDONE; + + /* Free the control block. */ + scb->next = z->free_scb; + z->free_scb = scb; + scb->flags = SCB_FREE; + + /* If there were none, wake anybody waiting for one to come free, + * starting with queued entries. */ + if (! scb->next) + wakeup ((caddr_t) &z->free_scb); + + scsi_done (xs); +} + +/* + * Wait for completion of command in polled mode. + * Always called with interrupts masked out. + */ +int sea_poll (adapter_t *z, scb_t *scb) +{ + int count; + + for (count=0; count<30; ++count) { + DELAY (1000); /* delay for a while */ + sea_start (z); /* retry operation */ + if (scb->xfer->flags & ITSDONE) + return (1); /* all is done */ + if (scb->flags & SCB_TIMEOUT) + return (0); /* no target present */ + } + return (0); +} + +/* + * Send data to the target. + */ +void sea_data_output (adapter_t *z, u_char **pdata, u_long *plen) +{ + volatile u_char *data = *pdata; + volatile u_int len = *plen; + +#ifdef SEA_BLINDTRANSFER + if (len && !(len % BLOCK_SIZE)) { + while (len) { + WAITFOR10 (*z->STATUS & STAT_REQ, "blind block read"); + __asm __volatile (" + shr $2, %%ecx; + cld; + rep; + movsl; " : : + "D" (z->DATA), "S" (data), "c" (BLOCK_SIZE) : + "cx", "si", "di" ); + data += BLOCK_SIZE; + len -= BLOCK_SIZE; + } + } else { +#endif + __asm __volatile ("cld + 1: movb (%%ebx), %%al + xorb $1, %%al + testb $0xf, %%al + jnz 2f + testb $0x10, %%al + jz 1b + lodsb + movb %%al, (%%edi) + loop 1b + 2:" + : "=S" (data), "=c" (len) /* output */ + : "D" (z->DATA), "b" (z->STATUS), /* input */ + "0" (data), "1" (len) + : "eax", "ebx", "edi"); /* clobbered */ +#ifdef SEA_BLINDTRANSFER + } +#endif + PRINT (("sea (DATAOUT) send %ld bytes\n", *plen - len)); + *plen = len; + *pdata = (u_char *)data; +} + +/* + * Receive data from the target. + */ +void sea_data_input (adapter_t *z, u_char **pdata, u_long *plen) +{ + volatile u_char *data = *pdata; + volatile u_int len = *plen; + +#ifdef SEA_BLINDTRANSFER + if (len && !(len % BLOCK_SIZE)) { + while (len) { + WAITFOR10 (*z->STATUS & STAT_REQ, "blind block read"); + __asm __volatile (" + shr $2, %%ecx; + cld; + rep; + movsl; " : : + "S" (z->DATA), "D" (data), "c" (BLOCK_SIZE) : + "cx", "si", "di" ); + data += BLOCK_SIZE; + len -= BLOCK_SIZE; + } + } else { +#endif + if (len >= 512) { + __asm __volatile (" cld + 1: movb (%%esi), %%al + xorb $5, %%al + testb $0xf, %%al + jnz 2f + testb $0x10, %%al + jz 1b + movb (%%ebx), %%al + stosb + loop 1b + 2:" + : "=D" (data), "=c" (len) /* output */ + : "b" (z->DATA), "S" (z->STATUS), + "0" (data), "1" (len) /* input */ + : "eax", "ebx", "esi"); /* clobbered */ + } else { + __asm __volatile (" cld + 1: movb (%%esi), %%al + xorb $5, %%al + testb $0xf, %%al + jnz 2f + testb $0x10, %%al + jz 1b + movb (%%ebx), %%al + stosb + movb $1000, %%al + 3: testb $0x10, (%%esi) + jz 4f + dec %%al + jnz 3b + 4: loop 1b + 2:" + : "=D" (data), "=c" (len) /* output */ + : "b" (z->DATA), "S" (z->STATUS), + "0" (data), "1" (len) /* input */ + : "eax", "ebx", "esi"); /* clobbered */ + } +#ifdef SEA_BLINDTRANSFER + } +#endif + PRINT (("sea (DATAIN) got %ld bytes\n", *plen - len)); + *plen = len; + *pdata = (u_char *)data; +} + +/* + * Send the command to the target. + */ +void sea_cmd_output (target_t *t, u_char *cmd, int cmdlen) +{ + adapter_t *z = t->adapter; + + PRINT (("sea%d send command (%d bytes) ", z->sc_link.adapter_unit, + cmdlen)); + + PRINT (("%x", *cmd)); + *z->DATA = *cmd++; + if (z->type == CTLR_SEAGATE) + WAITREQ (t, cmdout1, 10000); + --cmdlen; + + while (cmdlen) { + /* Check for target disconnect. */ + u_char sts = *z->STATUS; + if (! (sts & STAT_BSY)) + break; + + /* Check for phase mismatch. FD 885 seems to get this wrong! */ + if ((sts & PHASE_MASK) != PHASE_CMDOUT && z->type != CTLR_FUTURE_DOMAIN) { + printf ("sea: sea_cmd_output: invalid phase %s\n", + PHASE_NAME (sts & PHASE_MASK)); + return; + } + + /* Wait for REQ. */ + if (! (sts & STAT_REQ)) + continue; + + PRINT (("-%x", *cmd)); + *z->DATA = *cmd++; + if (z->type == CTLR_SEAGATE) + WAITREQ (t, cmdout, 1000); + --cmdlen; + } + PRINT (("\n")); +} + +/* + * Send the message to the target. + */ +void sea_send_abort (adapter_t *z) +{ + u_char sts; + + *z->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | CMD_ATTN | z->parity; + + /* Wait for REQ, after which the phase bits will be valid. */ + WAITFOR (*z->STATUS & STAT_REQ, "abort message"); + sts = *z->STATUS; + if (! (sts & STAT_REQ)) + goto ret; + + /* Check for phase mismatch. */ + if ((sts & PHASE_MASK) != PHASE_MSGOUT) { + printf ("sea: sending MSG_ABORT: invalid phase %s\n", + PHASE_NAME (sts & PHASE_MASK)); + goto ret; + } + + *z->DATA = MSG_ABORT; + sea_wait_for_req_deassert (z, 1000, "MSG_OUTPUT"); + PRINT (("sea%d send abort message\n", z->sc_link.adapter_unit)); +ret: + *z->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | z->parity; +} + +/* + * Get the message from the target. + * Return the length of the received message. + */ +u_char sea_msg_input (adapter_t *z) +{ + u_char sts, msg; + + /* Wait for REQ, after which the phase bits will be valid. */ + WAITFOR (*z->STATUS & STAT_REQ, "message input"); + sts = *z->STATUS; + if (! (sts & STAT_REQ)) + return (MSG_ABORT); + + /* Check for phase mismatch. + * Reached if the target decides that it has finished the transfer. */ + if ((sts & PHASE_MASK) != PHASE_MSGIN) { + printf ("sea: sea_msg_input: invalid phase %s\n", + PHASE_NAME (sts & PHASE_MASK)); + return (MSG_ABORT); + } + + /* Do actual transfer from SCSI bus to/from memory. */ + msg = *z->DATA; + sea_wait_for_req_deassert (z, 1000, "MSG_INPUT"); + PRINT (("sea%d (MSG_INPUT) got 0x%x\n", z->sc_link.adapter_unit, msg)); + return (msg); +} + +/* + * Send request-sense op to the target. + * Return 1 success, 0 on failure. + * Called on splbio level. + */ +int sea_sense (adapter_t *z, scb_t *scb) +{ + u_char cmd[6], status, msg, *data; + u_long len; + + /* Wait for target to disconnect. */ + WAITFOR (! (*z->STATUS & STAT_BSY), "sense bus free"); + if (*z->STATUS & STAT_BSY) + return (0); + + /* Select the target again. */ + if (! sea_select (z, scb)) + return (0); + + /* Wait for CMDOUT phase. */ + WAITFOR (*z->STATUS & STAT_REQ, "sense CMDOUT"); + if (! (*z->STATUS & STAT_REQ) || + (*z->STATUS & PHASE_MASK) != PHASE_CMDOUT) + return (0); + + /* Send command. */ + len = sizeof (scb->xfer->sense); + cmd[0] = REQUEST_SENSE; + cmd[1] = scb->xfer->sc_link->lun << 5; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = len; + cmd[5] = 0; + sea_cmd_output (&z->target[scb->xfer->sc_link->target], + cmd, sizeof (cmd)); + + /* Wait for DATAIN phase. */ + WAITFOR (*z->STATUS & STAT_REQ, "sense DATAIN"); + if (! (*z->STATUS & STAT_REQ) || + (*z->STATUS & PHASE_MASK) != PHASE_DATAIN) + return (0); + + data = (u_char*) &scb->xfer->sense; + sea_data_input (z, &data, &len); + PRINT (("sea%d sense %x-%x-%x-%x-%x-%x-%x-%x\n", + z->sc_link.adapter_unit, scb->xfer->sense.error_code, + scb->xfer->sense.ext.extended.segment, + scb->xfer->sense.ext.extended.flags, + scb->xfer->sense.ext.extended.info[0], + scb->xfer->sense.ext.extended.info[1], + scb->xfer->sense.ext.extended.info[2], + scb->xfer->sense.ext.extended.info[3], + scb->xfer->sense.ext.extended.extra_len)); + + /* Wait for STATIN phase. */ + WAITFOR (*z->STATUS & STAT_REQ, "sense STATIN"); + if (! (*z->STATUS & STAT_REQ) || + (*z->STATUS & PHASE_MASK) != PHASE_STATIN) + return (0); + + status = *z->DATA; + + /* Wait for MSGIN phase. */ + WAITFOR (*z->STATUS & STAT_REQ, "sense MSGIN"); + if (! (*z->STATUS & STAT_REQ) || + (*z->STATUS & PHASE_MASK) != PHASE_MSGIN) + return (0); + + msg = *z->DATA; + + if (status != 0 || msg != 0) + printf ("sea%d: bad sense status=0x%x, msg=0x%x\n", + z->sc_link.adapter_unit, status, msg); + return (1); +} + +/* + * Do the transfer. We know we are connected. Update the flags, + * call sea_done when task accomplished. Dialog controlled by the target. + * Always called with interrupts disabled. + */ +void sea_information_transfer (adapter_t *z, scb_t *scb) +{ + u_char *data = scb->data; /* current data buffer */ + u_long datalen = scb->datalen; /* current data transfer size */ + target_t *t = &z->target[scb->xfer->sc_link->target]; + register u_char sts; + u_char msg; + + while ((sts = *z->STATUS) & STAT_BSY) { + /* We only have a valid SCSI phase when REQ is asserted. */ + if (! (sts & STAT_REQ)) + continue; + if (sts & STAT_PARITY) { + int target = scb->xfer->sc_link->target; + if (++z->target[target].perrcnt <= 8) + printf ("sea%d/%d/%d parity error\n", + z->sc_link.adapter_unit, target, + scb->xfer->sc_link->lun); + if (z->target[target].perrcnt == 8) + printf ("sea%d/%d/%d too many parity errors, not logging any more\n", + z->sc_link.adapter_unit, target, + scb->xfer->sc_link->lun); + } + switch (sts & PHASE_MASK) { + case PHASE_DATAOUT: + if (datalen <= 0) { + printf ("sea%d/%d/%d data length underflow\n", + z->sc_link.adapter_unit, + scb->xfer->sc_link->target, + scb->xfer->sc_link->lun); + /* send zero byte */ + *z->DATA = 0; + break; + } + sea_data_output (z, &data, &datalen); + break; + case PHASE_DATAIN: + if (datalen <= 0) { + /* Get extra data. Some devices (e.g. CDROMs) + * use fixed-length blocks (e.g. 2k), + * even if we need less. */ + PRINT (("@")); + sts = *z->DATA; + break; + } + sea_data_input (z, &data, &datalen); + break; + case PHASE_CMDOUT: + sea_cmd_output (t, (u_char*) scb->xfer->cmd, + scb->xfer->cmdlen); + break; + case PHASE_STATIN: + scb->xfer->status = *z->DATA; + if (z->type == CTLR_SEAGATE) + WAITREQ (t, statin, 2000); + PRINT (("sea%d/%d/%d (STATIN) got 0x%x\n", + z->sc_link.adapter_unit, + scb->xfer->sc_link->target, + scb->xfer->sc_link->lun, + (u_char) scb->xfer->status)); + break; + case PHASE_MSGOUT: + /* Send no-op message. */ + *z->DATA = MSG_NOP; + sea_wait_for_req_deassert (z, 1000, "MSGOUT"); + PRINT (("sea%d/%d/%d (MSGOUT) send NOP\n", + z->sc_link.adapter_unit, + scb->xfer->sc_link->target, + scb->xfer->sc_link->lun)); + break; + case PHASE_MSGIN: + /* Don't handle multi-byte messages here, because they + * should not be present here. */ + msg = *z->DATA; + sea_wait_for_req_deassert (z, 2000, "MSGIN"); + PRINT (("sea%d/%d/%d (MSGIN) got 0x%x\n", + z->sc_link.adapter_unit, + scb->xfer->sc_link->target, + scb->xfer->sc_link->lun, msg)); + switch (msg) { + case MSG_COMMAND_COMPLETE: + scb->data = data; + scb->datalen = datalen; + /* In the case of check-condition status, + * perform the request-sense op. */ + switch (scb->xfer->status & 0x1e) { + case SCSI_CHECK: + if (sea_sense (z, scb)) + scb->flags = SCB_SENSE; + break; + case SCSI_BUSY: + scb->flags = SCB_TBUSY; + break; + } + goto done; + case MSG_ABORT: + printf ("sea: command aborted by target\n"); + scb->flags = SCB_ABORTED; + goto done; + case MSG_MESSAGE_REJECT: + printf ("sea: message rejected\n"); + scb->flags = SCB_ABORTED; + goto done; + case MSG_DISCONNECT: + scb->next = z->disconnected_queue; + z->disconnected_queue = scb; + if (! z->irq && ! z->timeout_active) { + timeout (sea_tick, z, 1); + z->timeout_active = 1; + } + PRINT (("sea%d/%d/%d disconnected\n", + z->sc_link.adapter_unit, + scb->xfer->sc_link->target, + scb->xfer->sc_link->lun)); + goto ret; + case MSG_SAVE_POINTERS: + scb->data = data; + scb->datalen = datalen; + break; + case MSG_RESTORE_POINTERS: + data = scb->data; + datalen = scb->datalen; + break; + default: + printf ("sea%d/%d/%d unknown message: 0x%x\n", + z->sc_link.adapter_unit, + scb->xfer->sc_link->target, + scb->xfer->sc_link->lun, msg); + break; + } + break; + default: + printf ("sea: unknown phase: %b\n", sts, STAT_BITS); + break; + } + } + printf ("sea%d/%d/%d unexpected target disconnect\n", + z->sc_link.adapter_unit, scb->xfer->sc_link->target, + scb->xfer->sc_link->lun); + scb->flags = SCB_ERROR; +done: + CLEAR_BUSY (z, scb); + sea_done (z, scb); +ret: + *z->CONTROL = CMD_INTR | z->parity; +} +#endif /* NSEA */ |