/* * (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.31 1998/08/11 17:22:42 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 #include #include #include #include #include #include #include #include #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 ointhand2_t seaintr; 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; iscbs[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++; pDATA+0x200; ++p) if (*p != c) return (4); /* Check that addr..addr+1800h are not writable. */ for (p=z->addr; paddr+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; paddr+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" : ""); dev->id_ointr = seaintr; /* 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. */ static 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 */