diff options
author | jkh <jkh@FreeBSD.org> | 1995-01-10 11:41:28 +0000 |
---|---|---|
committer | jkh <jkh@FreeBSD.org> | 1995-01-10 11:41:28 +0000 |
commit | 7030617825ab920bee9ace1639bfb580621af0a4 (patch) | |
tree | b1a12a43ab4337522683a8a70a9cc4ab90b47366 | |
parent | a2901dd1f4e9676d381b5775d89f3fc7834b1b93 (diff) | |
download | FreeBSD-src-7030617825ab920bee9ace1639bfb580621af0a4.zip FreeBSD-src-7030617825ab920bee9ace1639bfb580621af0a4.tar.gz |
Latest update from Serge Vakulenko <vak@cronyx.ru>
Submitted by: vak
-rw-r--r-- | sys/i386/isa/seagate.c | 1305 |
1 files changed, 635 insertions, 670 deletions
diff --git a/sys/i386/isa/seagate.c b/sys/i386/isa/seagate.c index 1017ce5..0a99049 100644 --- a/sys/i386/isa/seagate.c +++ b/sys/i386/isa/seagate.c @@ -6,7 +6,7 @@ * Copyright 1994, Robert Knier (rknier@qgraph.com) * Copyright 1992, 1994 Drew Eckhardt (drew@colorado.edu) * Copyright 1994, Julian Elischer (julian@tfs.com) - * Copyright 1994, Serge Vakulenko (vak@cronyx.ru) + * Copyright 1994-1995, Serge Vakulenko (vak@cronyx.ru) * * Others that has contributed by example code is * Glen Overby (overby@cray.com) @@ -44,6 +44,7 @@ * 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 * * $Id: seagate.c,v 1.3 1994/06/16 13:26:14 sean Exp $ */ @@ -52,9 +53,8 @@ * What should really be done: * * Restructure interrupt enable/disable code (runs too long with int disabled) - * Find bug? giving problem with tape status * Add code to handle Future Domain 840, 841, 880 and 881 - * add code to use tagged commands in SCSI2 + * Add code to use tagged commands in SCSI2 * Add code to handle slow devices better (sleep if device not disconnecting) * Fix unnecessary interrupts */ @@ -120,16 +120,14 @@ # define PRINT(s) /*void*/ #endif -#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 */ -#define SEA_SCSI_ADDR 7 /* address of the adapter on the SCSI bus */ +#define HOST_SCSI_ADDR 7 /* address of the adapter on the SCSI bus */ /* * Defice config flags */ #define FLAG_NOPARITY 0x01 /* disable SCSI bus parity check */ -#define FLAG_SENSEFIRST 0x02 /* place REQUEST_SENSE ops ahead of queue */ /* * Board CONTROL register @@ -157,19 +155,18 @@ #define STAT_BITS "\20\1bsy\2msg\3i/o\4c/d\5req\6sel\7parity\10arb" /* - * SCSI bus requests + * SCSI bus phases */ -#define REQ_MASK (STAT_MSG | STAT_CD | STAT_IO) -#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 - -static char *sea_phase_name[] = { +#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?", @@ -193,19 +190,21 @@ static char *sea_phase_name[] = { /* * SCSI control block used to keep info about a scsi command */ -struct sea_scb { +typedef struct 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 */ +#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 datalen; /* bytes remaining to transfer */; -}; +} scb_t; typedef enum { CTLR_NONE, @@ -213,40 +212,36 @@ typedef enum { CTLR_FUTURE_DOMAIN, } ctlr_t; -char *sea_name[] = { - "Unknown", - "Seagate ST01/ST02", - "Future Domain TMC-885/TMC-950", -}; - /* * Flags for waiting for REQ deassert during some SCSI bus phases. */ -struct sea_phase { +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. */ -struct sea_target { - struct sea_data *adapter; /* pointer to the adapter structure */ - volatile u_char busy; /* mask of busy luns at device target */ - u_long perrcnt; /* counter of target parity errors */ - struct sea_phase ndelay; /* "don't delay" flags */ - struct sea_phase init; /* "initialized" flags */ -}; +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. */ -struct sea_data { +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 */ @@ -255,19 +250,20 @@ struct sea_data { 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 */ - unsigned sensefirst : 1; /* place REQUEST_SENSE ops ahead */ - unsigned timeout_active : 1; /* sea_timeout active (requested) */ + u_int timeout_active : 1; /* timeout() active (requested) */ 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 */ + scb_t *queue; /* waiting to be issued */ + scb_t *disconnected_queue; /* waiting to reconnect */ int numscb; /* number of scsi control blocks */ - struct sea_scb scbs[SCB_TABLE_SIZE]; - struct sea_scb *free_scb; /* free scb list */ - struct sea_target target[8]; /* target state data */ -} seadata[NSEA]; + scb_t *free_scb; /* free scb list */ + scb_t scbs[SCB_TABLE_SIZE]; + + target_t target[8]; /* target state data */ +} adapter_t; + +adapter_t seadata[NSEA]; #define IS_BUSY(a,b) ((a)->target[(b)->xfer->sc_link->target].busy &\ (1 << (b)->xfer->sc_link->lun)) @@ -278,13 +274,13 @@ struct sea_data { /* * Wait for condition, given as an boolean expression. - * Print the message on timeout (approx 10 msec). + * Print the message on timeout. */ #define WAITFOR(condition,message) {\ - register long cnt = 100000; char *msg = message;\ + register u_long cnt = 100000; char *msg = message;\ while (cnt-- && ! (condition)) continue;\ if (cnt == -1 && msg)\ - printf ("sea: timeout waiting for %s\n", msg); } + printf ("sea: %s timeout\n", msg); } /* * Seagate adapter does not support in hardware @@ -305,48 +301,40 @@ struct sea_data { * 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; } - -int sea_probe (struct isa_device *dev); -int sea_detect (struct sea_data *sea, struct isa_device *dev); -int sea_attach (struct isa_device *dev); -int seaintr (int unit); -int32 sea_scsi_cmd (struct scsi_xfer *xs); -u_int32 sea_adapter_info (int unit); -void sea_timeout (void *scb); -void seaminphys (struct buf *bp); -void sea_done (int unit, struct sea_scb *scb); -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_start (void); -static void sea_information_transfer (struct sea_data *sea); -int sea_poll (struct scsi_xfer *xs); -int sea_init (struct sea_data *sea, struct isa_device *dev); -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_abort (int unit, struct sea_scb *scb); -void sea_msg_output (struct sea_data *sea, u_char msg); -u_char sea_msg_input (struct sea_data *sea); - -struct scsi_adapter sea_switch = { - sea_scsi_cmd, seaminphys, 0, 0, - sea_adapter_info, "sea", {0}, -}; + if (! (t)->ndelay.op &&\ + ! sea_wait_for_req_deassert ((t)->adapter, cnt, #op) &&\ + ! (t)->init.op)\ + (t)->ndelay.op = 1;\ + (t)->init.op = 1; } -/* 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}, +int seaintr (int unit); +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 sea_scsi_cmd (struct scsi_xfer *xs); +static u_int32 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 (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" }; /* @@ -357,74 +345,96 @@ struct isa_driver seadriver = { sea_probe, sea_attach, "sea" }; */ int sea_probe (struct isa_device *dev) { - struct sea_data *sea = &seadata[dev->id_unit]; + adapter_t *z = &seadata[dev->id_unit]; static const 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 (sea, dev)) + if (sea_detect (z, dev)) return (1); } - else if (sea_detect (sea, dev)) + else if (sea_detect (z, dev)) return (1); - sea->type = CTLR_NONE; + + bzero (z, sizeof (*z)); return (0); } -int sea_detect (struct sea_data *sea, struct isa_device *dev) +int sea_detect (adapter_t *z, struct isa_device *dev) { - sea->addr = dev->id_maddr; + z->addr = dev->id_maddr; /* Try Seagate. */ - sea->type = CTLR_SEAGATE; - sea->CONTROL = sea->addr + 0x1a00; /* ST01/ST02 register offsets */ - sea->STATUS = sea->addr + 0x1a00; - sea->DATA = sea->addr + 0x1c00; - if (sea_init (sea, dev) == 0) + 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. */ - sea->type = CTLR_FUTURE_DOMAIN; - sea->CONTROL = sea->addr + 0x1c00; /* TMC-885/TMC-950 reg. offsets */ - sea->STATUS = sea->addr + 0x1c00; - sea->DATA = sea->addr + 0x1e00; - if (sea_init (sea, dev) == 0) + 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; + if (sea_init (z) == 0) return (1); return (0); } /* - * Probe the adapter, and if found, - * reset the board and the scsi bus. + * Probe the adapter, and if found, reset the board and the scsi bus. + * Return 0 if the adapter found. */ -int sea_init (struct sea_data *sea, struct isa_device *dev) +int sea_init (adapter_t *z) { volatile u_char *p; - u_char c; - int i; + int i, c; /* Check that STATUS..STATUS+200h are equal. */ - p = sea->STATUS; + p = z->STATUS; c = *p; if (c == 0xff) return (2); - while (++p < sea->STATUS+0x200) + while (++p < z->STATUS+0x200) if (*p != c) return (3); /* Check that DATA..DATA+200h are equal. */ - for (p=sea->DATA, c= *p++; p<sea->DATA+0x200; ++p) + 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=sea->addr; p<sea->addr+0x1800; ++p) { + for (p=z->addr; p<z->addr+0x1800; ++p) { c = *p; *p = ~c; if (*p == ~c) { @@ -434,7 +444,7 @@ int sea_init (struct sea_data *sea, struct isa_device *dev) } /* Check that addr+1800h..addr+1880h are writable. */ - for (p=sea->addr+0x1800; p<sea->addr+0x1880; ++p) { + for (p=z->addr+0x1800; p<z->addr+0x1880; ++p) { c = *p; *p = 0x55; if (*p != 0x55) { @@ -448,63 +458,40 @@ int sea_init (struct sea_data *sea, struct isa_device *dev) } } - /* Parse device flags. */ - sea->parity = (dev->id_flags & FLAG_NOPARITY) ? 0 : CMD_EN_PARITY; - /* Reset the scsi bus (I don't know if this is needed). */ - *sea->CONTROL = CMD_RST | CMD_DRVR_ENABLE; + *z->CONTROL = CMD_RST | CMD_DRVR_ENABLE; /* Hold reset for at least 25 microseconds. */ DELAY (25); /* Check that status cleared. */ - if (*sea->STATUS != 0) { - *sea->CONTROL = 0; + if (*z->STATUS != 0) { + *z->CONTROL = 0; return (8); } /* Check that DATA register is writable. */ for (i=0; i<256; ++i) { - *sea->DATA = i; - if (*sea->DATA != i) { - *sea->CONTROL = 0; + *z->DATA = i; + if (*z->DATA != i) { + *z->CONTROL = 0; return (9); } } /* Enable the adapter. */ - *sea->CONTROL = CMD_INTR | sea->parity; + *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 = *sea->DATA; + c = *z->DATA; for (i=0; i<256; ++i) { - *sea->DATA = i; - if (*sea->DATA != c) { - *sea->CONTROL = 0; + *z->DATA = i; + if (*z->DATA != c) { + *z->CONTROL = 0; return (10); } } - /* Init fields used by our routines */ - sea->sensefirst = (dev->id_flags & FLAG_SENSEFIRST) ? 1 : 0; - sea->scsi_addr = SEA_SCSI_ADDR; - sea->scsi_id = 1 << sea->scsi_addr; - sea->irq = dev->id_irq ? ffs (dev->id_irq) - 1 : 0; - sea->connected = 0; - sea->issue_queue = 0; - sea->disconnected_queue = 0; - for (i=0; i<8; i++) { - sea->target[i].adapter = sea; - sea->target[i].busy = 0; - } - - /* Link up the free list of scbs */ - sea->numscb = SCB_TABLE_SIZE; - sea->free_scb = sea->scbs; - for (i=1; i<SCB_TABLE_SIZE; i++) - sea->scbs[i-1].next = sea->scbs + i; - sea->scbs[SCB_TABLE_SIZE-1].next = 0; - return (0); } @@ -521,22 +508,20 @@ static struct kern_devconf sea_kdc[NSEA] = {{ int sea_attach (struct isa_device *dev) { int unit = dev->id_unit; - struct sea_data *sea = &seadata[unit]; + adapter_t *z = &seadata[unit]; - sprintf (sea_description, "%s SCSI controller", sea_name[sea->type]); - printf ("\n"); - printf ("sea%d: type %s%s%s\n", unit, sea_name[sea->type], - (dev->id_flags & FLAG_NOPARITY) ? ", no parity" : "", - (dev->id_flags & FLAG_SENSEFIRST) ? ", sense ahead" : ""); + sprintf (sea_description, "%s SCSI controller", z->name); + printf ("\nsea%d: type %s%s\n", unit, z->name, + (dev->id_flags & FLAG_NOPARITY) ? ", no parity" : ""); /* fill in the prototype scsi_link */ - sea->sc_link.adapter_unit = unit; - sea->sc_link.adapter_targ = sea->scsi_addr; - sea->sc_link.adapter = &sea_switch; - sea->sc_link.device = &sea_dev; + z->sc_link.adapter_unit = unit; + z->sc_link.adapter_targ = z->scsi_addr; + z->sc_link.adapter = &sea_switch; + z->sc_link.device = &sea_dev; /* ask the adapter what subunits are present */ - scsi_attachdevs (&(sea->sc_link)); + scsi_attachdevs (&(z->sc_link)); if (dev->id_unit) sea_kdc[dev->id_unit] = sea_kdc[0]; @@ -564,81 +549,29 @@ void seaminphys (struct buf *bp) */ int seaintr (int unit) { - struct sea_data *sea = &seadata[unit]; + adapter_t *z = &seadata[unit]; - PRINT (("sea%d: interrupt status=%b\n", unit, *sea->STATUS, STAT_BITS)); - while ((*sea->STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) { - /* Reselect interrupt */ - sea_reselect (sea); - sea_start (); - } + PRINT (("sea%d: interrupt status=%b\n", unit, *z->STATUS, STAT_BITS)); + sea_start (z); return (1); } /* - * 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. + * 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. */ -struct sea_scb *sea_get_scb (int unit, int flags) +void sea_tick (void *arg) { - struct sea_data *sea = &seadata[unit]; - struct sea_scb *scbp; - int x = 0; - - if (! (flags & SCSI_NOMASK)) - x = splbio (); + adapter_t *z = arg; + int x = splbio (); - /* - * 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)) - if (sea->numscb < SEA_SCB_MAX) { - PRINT (("malloced new scb\n")); - scbp = (struct sea_scb*) malloc (sizeof (struct sea_scb), - M_TEMP, M_NOWAIT); - if (scbp) { - bzero (scbp, sizeof (struct sea_scb)); - sea->numscb++; - scbp->flags = SCB_ACTIVE; - scbp->next = 0; - } else - printf ("sea%d: can't malloc scb\n",unit); - goto ret; - } else if (! (flags & SCSI_NOSLEEP)) - tsleep ((caddr_t)&sea->free_scb, PRIBIO, "seascb", 0); - if (scbp) { - /* Get SCB from free list */ - sea->free_scb = scbp->next; - scbp->next = 0; - scbp->flags = SCB_ACTIVE; + z->timeout_active = 0; + sea_start (z); + if (z->disconnected_queue && ! z->timeout_active) { + timeout (sea_tick, z, 1); + z->timeout_active = 1; } -ret: if (! (flags & SCSI_NOMASK)) - splx (x); - return (scbp); -} - -void sea_free_scb (int unit, struct sea_scb *scb, int flags) -{ - struct sea_data *sea = &seadata[unit]; - int x = 0; - - if (! (flags & SCSI_NOMASK)) - x = 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) - wakeup ((caddr_t) &sea->free_scb); - - if (! (flags & SCSI_NOMASK)) - splx (x); + splx (x); } /* @@ -649,11 +582,11 @@ void sea_free_scb (int unit, struct sea_scb *scb, int flags) int32 sea_scsi_cmd (struct scsi_xfer *xs) { int unit = xs->sc_link->adapter_unit, flags = xs->flags, x = 0; - struct sea_data *sea = &seadata[unit]; - struct sea_scb *scb; + adapter_t *z = &seadata[unit]; + scb_t *scb; - PRINT (("sea%d:%d:%d command 0x%x\n", unit, xs->sc_link->target, - xs->sc_link->lun, xs->cmd->opcode)); + /* 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) { @@ -664,52 +597,51 @@ int32 sea_scsi_cmd (struct scsi_xfer *xs) printf ("sea%d: not in use?", unit); xs->flags |= INUSE; } - scb = sea_get_scb (unit, flags); - if (! scb) { - xs->error = XS_DRIVER_STUFFUP; - return (TRY_AGAIN_LATER); + if (flags & SCSI_RESET) + printf ("sea%d: SCSI_RESET not implemented\n", 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; - 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); - - if (! (flags & SCSI_NOMASK)) - x = splbio (); - /* 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? */ - /* 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 ?? */ - if (! sea->issue_queue || - (sea->sensefirst && xs->cmd->opcode == REQUEST_SENSE)) { - scb->next = sea->issue_queue; - sea->issue_queue = scb; + if (! z->queue) { + scb->next = z->queue; + z->queue = scb; } else { - struct sea_scb *q; + scb_t *q; - for (q=sea->issue_queue; q->next; q=q->next) + 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. Because this board - * does not use any mailboxes, this routine simply adds the command - * to the queue held by the sea_data structure. */ - sea_start (); + /* Try to send this command to the board. */ + sea_start (z); /* Usually return SUCCESSFULLY QUEUED. */ if (! (flags & SCSI_NOMASK)) { @@ -721,13 +653,13 @@ int32 sea_scsi_cmd (struct scsi_xfer *xs) return (SUCCESSFULLY_QUEUED); timeout (sea_timeout, (caddr_t) scb, (xs->timeout * hz) / 1000); scb->flags |= SCB_TIMECHK; - PRINT (("sea%d:%d:%d command queued\n", unit, + PRINT (("sea%d/%d/%d command queued\n", unit, xs->sc_link->target, xs->sc_link->lun)); return (SUCCESSFULLY_QUEUED); } /* If we can't use interrupts, poll on completion. */ - if (! sea_poll (xs)) { + 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. */ @@ -737,98 +669,71 @@ int32 sea_scsi_cmd (struct scsi_xfer *xs) * sea_timeout made. */ untimeout (sea_timeout, (void*) scb); - if (! sea_poll (xs)) + 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", unit, + /* PRINT (("sea%d/%d/%d command %s\n", unit, xs->sc_link->target, xs->sc_link->lun, - xs->error ? "failed" : "done")); + xs->error ? "failed" : "done")); */ return (xs->error ? HAD_ERROR : COMPLETE); } /* * Coroutine that runs as long as more work can be done. - * Both sea_scsi_cmd and sea_intr will try to start it in + * Both scsi_cmd() and intr() will try to start it in * case it is not running. * Always called with interrupts disabled. */ -static void sea_start (void) +void sea_start (adapter_t *z) { - int unit, done; - -again: done = 1; - for (unit=0; unit<NSEA && seadata[unit].type; ++unit) { - struct sea_data *sea = &seadata[unit]; - - if (! sea->connected) { - /* Search through the issue_queue for a command - * destined for a target that's not busy. */ - struct sea_scb *q, *prev = 0; - - for (q=sea->issue_queue; q; prev=q, q=q->next) { - if (IS_BUSY (sea, q)) - continue; - - /* First check that if any device has tried - * a reconnect while we have done other things - * with interrupts disabled. */ - if ((*sea->STATUS & (STAT_SEL | STAT_IO)) == - (STAT_SEL | STAT_IO)) { - sea_reselect (sea); - break; - } + 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; - /* When we find the command, remove it - * from the issue queue. */ - if (prev) - prev->next = q->next; - else - sea->issue_queue = q->next; - q->next = 0; + /* 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; - /* 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. */ - if (sea_select (sea, q)) - break; - q->next = sea->issue_queue; - sea->issue_queue = q; - printf ("sea_start: select failed\n"); - } - } + /* Remove the command from the issue queue. */ + if (prev) + prev->next = q->next; + else + z->queue = q->next; + q->next = 0; - if (sea->connected) { - /* We are connected. Do the task. */ - sea_information_transfer (sea); - done = 0; - } - } - if (! done) + /* We are connected. Do the task. */ + sea_information_transfer (z, q); goto again; + } } void sea_timeout (void *arg) { - struct sea_scb *scb = (struct sea_scb*) arg; + scb_t *scb = (scb_t*) arg; int unit = scb->xfer->sc_link->adapter_unit; + adapter_t *z = &seadata[unit]; int x = splbio (); if (! (scb->xfer->flags & SCSI_NOMASK)) - printf ("sea%d:%d:%d (%s%d) timed out\n", unit, + printf ("sea%d/%d/%d (%s%d) timed out\n", 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 (unit, scb)*/) { - sea_abort (unit, scb); + /* 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 */ timeout (sea_timeout, (caddr_t)scb, 2*hz); scb->flags |= (SCB_ABORTED | SCB_TIMECHK); @@ -836,143 +741,156 @@ void sea_timeout (void *arg) /* abort timed out */ scb->flags |= SCB_ABORTED; scb->xfer->retries = 0; - sea_done (unit, scb); + 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 (" + 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 0 if selection could not execute for some reason, 1 if selection - * succeded or failed because the target did not respond. + * Return 1 if selection succeded. */ -int sea_select (struct sea_data *sea, struct sea_scb *scb) +int sea_select (adapter_t *z, scb_t *scb) { - PRINT (("sea%d:%d:%d select\n", sea->sc_link.adapter_unit, - scb->xfer->sc_link->target, scb->xfer->sc_link->lun)); - *sea->CONTROL = sea->parity; - *sea->DATA = sea->scsi_id; - *sea->CONTROL = CMD_START_ARB | sea->parity; + /* Start arbitration. */ + *z->CONTROL = z->parity; + *z->DATA = z->scsi_id; + *z->CONTROL = CMD_START_ARB | z->parity; /* Wait for arbitration to complete. */ - WAITFOR (*sea->STATUS & STAT_ARB_CMPL, "arbitration"); - - if (! (*sea->STATUS & STAT_ARB_CMPL)) { - if (*sea->STATUS & STAT_SEL) { + 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; } - *sea->CONTROL = CMD_INTR | sea->parity; + *z->CONTROL = CMD_INTR | z->parity; return (0); } DELAY (2); - *sea->DATA = (1 << scb->xfer->sc_link->target) | sea->scsi_id; - *sea->CONTROL = CMD_DRVR_ENABLE | CMD_SEL | CMD_ATTN | sea->parity; + *z->DATA = (1 << scb->xfer->sc_link->target) | z->scsi_id; + *z->CONTROL = CMD_DRVR_ENABLE | CMD_SEL | CMD_ATTN | z->parity; DELAY (1); /* 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 (*sea->STATUS & STAT_BSY, 0); - - if (! (*sea->STATUS & STAT_BSY)) { - /* The target does not respond. Not an error, though. - * Should return some error to the higher level driver? */ - *sea->CONTROL = CMD_INTR | sea->parity; + 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 (1); + return (0); } - /* Try to make the target to take a message from us. */ - *sea->CONTROL = CMD_DRVR_ENABLE | CMD_ATTN | sea->parity; + /* Try to make the target to take a message from us. + * Should start a MSGOUT phase. */ + *z->CONTROL = CMD_DRVR_ENABLE | CMD_ATTN | z->parity; DELAY (1); - - /* Should start a msg_out phase. */ - WAITFOR (*sea->STATUS & STAT_REQ, 0); - - if (! (*sea->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!) - */ - printf ("sea: target does not support messages, canceled\n"); + 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; - *sea->CONTROL = CMD_INTR | sea->parity; + *z->CONTROL = CMD_INTR | z->parity; return (0); } - sea->connected = scb; - SET_BUSY (sea, scb); - *sea->CONTROL = CMD_DRVR_ENABLE | sea->parity; + /* Check for phase mismatch. */ + if ((*z->STATUS & PHASE_MASK) != PHASE_MSGOUT) { + 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. */ - sea_msg_output (sea, MSG_IDENTIFY (scb->xfer->sc_link->lun)); - - if (! (*sea->STATUS & STAT_BSY)) - printf ("sea: target disconnected after successful arbitrate\n"); + *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; - *sea->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | sea->parity; + SET_BUSY (z, scb); return (1); } -int sea_reselect (struct sea_data *sea) +int sea_reselect (adapter_t *z) { - struct sea_scb *q = 0, *prev = 0; - u_char msg, target_mask; - - PRINT (("sea%d reselect: ", sea->sc_link.adapter_unit)); - - if (! (*sea->STATUS & STAT_SEL)) { - printf ("sea: wrong state 0x%x\n", *sea->STATUS); - return (0); - } - + 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. */ - WAITFOR ((*sea->STATUS & (STAT_SEL | STAT_IO | STAT_BSY)) == - (STAT_SEL | STAT_IO), "reselection phase"); + 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 = *sea->DATA; - if (! (target_mask & sea->scsi_id)) { - printf ("sea: polled reselection was not for me: %x\n", - target_mask); - return (0); + 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 &= ~sea->scsi_id; - *sea->CONTROL = CMD_DRVR_ENABLE | CMD_BSY | sea->parity; - WAITFOR (! (*sea->STATUS & STAT_SEL), "reselection acknowledge"); + target_mask &= ~z->scsi_id; + *z->CONTROL = CMD_DRVR_ENABLE | CMD_BSY | z->parity; + WAITFOR (! (*z->STATUS & STAT_SEL), "reselection acknowledge"); /* Remove the busy status. */ - *sea->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | sea->parity; - - /* We are connected. Now we wait for the MSGIN condition. */ - WAITFOR (*sea->STATUS & STAT_REQ, "identify message"); + /* 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 (sea); - if (! MSG_ISIDENT (msg)) - printf ("sea: expecting IDENTIFY message, got 0x%x\n", msg); - else { + 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. */ - unsigned char lun = (msg & 7); - - for (q=sea->disconnected_queue; q; prev=q, q=q->next) { + 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) @@ -980,22 +898,25 @@ int sea_reselect (struct sea_data *sea) if (prev) prev->next = q->next; else - sea->disconnected_queue = q->next; + z->disconnected_queue = q->next; q->next = 0; - sea->connected = q; - PRINT (("lun %d done\n", lun)); + 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); } - /* Since we have an established nexus that we can't - * do anything with, we must abort it. */ - PRINT (("lun %d aborted\n", lun)); - } - - /* Abort the connection. */ - *sea->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | CMD_ATTN | sea->parity; - sea_msg_output (sea, MSG_ABORT); - *sea->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | sea->parity; - return (0); + } 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; } /* @@ -1003,15 +924,14 @@ int sea_reselect (struct sea_data *sea) * Return 1 success, 0 on failure. * Called on splbio level. */ -int sea_abort (int unit, struct sea_scb *scb) +int sea_abort (adapter_t *z, scb_t *scb) { - struct sea_data *sea = &seadata[unit]; - struct sea_scb *q, **prev; + scb_t *q, **prev; /* If the command hasn't been issued yet, we simply remove it * from the issue queue. */ - prev = &sea->issue_queue; - for (q=sea->issue_queue; q; q=q->next) { + prev = &z->queue; + for (q=z->queue; q; q=q->next) { if (scb == q) { (*prev) = q->next; q->next = 0; @@ -1020,29 +940,19 @@ int sea_abort (int unit, struct sea_scb *scb) prev = &q->next; } - /* 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) - 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, + * 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=sea->disconnected_queue; q; q=q->next) { + for (q=z->disconnected_queue; q; q=q->next) { if (scb != q) continue; - if (! sea_select (sea, scb)) + if (! sea_select (z, scb)) return (0); + sea_send_abort (z); - *sea->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | CMD_ATTN | sea->parity; - sea_msg_output (sea, MSG_ABORT); - *sea->CONTROL = CMD_INTR | CMD_DRVR_ENABLE | sea->parity; - - prev = &sea->disconnected_queue; - for (q=sea->disconnected_queue; q; q=q->next) { + prev = &z->disconnected_queue; + for (q=z->disconnected_queue; q; q=q->next) { if (scb == q) { *prev = q->next; q->next = 0; @@ -1054,11 +964,15 @@ int sea_abort (int unit, struct sea_scb *scb) } } - /* Command not found in any queue, race condition in the code? */ - return (1); + /* Command not found in any queue. */ + return (0); } -void sea_done (int unit, struct sea_scb *scb) +/* + * 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; @@ -1073,9 +987,23 @@ void sea_done (int unit, struct sea_scb *scb) 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; - sea_free_scb (unit, scb, xs->flags); + + /* 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); } @@ -1083,82 +1011,125 @@ void sea_done (int unit, struct sea_scb *scb) * Wait for completion of command in polled mode. * Always called with interrupts masked out. */ -int sea_poll (struct scsi_xfer *xs) +int sea_poll (adapter_t *z, scb_t *scb) { int count; - for (count=0; count<1000; ++count) { - if (xs->flags & ITSDONE) - return (1); - /* Try to do something. */ - DELAY (30); - sea_start (); + 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); } /* - * 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. + * Send data to the target. */ -static inline int sea_wait_for_req_deassert (struct sea_data *sea, - int cnt, char *msg) +void sea_data_output (adapter_t *z, u_char **pdata, u_long *plen) { - asm (" - 1: testb $0x10, %2 - jz 2f + u_char *data = *pdata; + u_long len = *plen; + + asm ("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:" - : "=c" (cnt) /* output */ - : "0" (cnt), "m" (*sea->STATUS)); /* input */ - if (! cnt) { - PRINT (("sea%d:%d:%d (%s) timeout waiting for !REQ\n", - sea->sc_link.adapter_unit, - sea->connected->xfer->sc_link->target, - sea->connected->xfer->sc_link->lun, msg)); - return (0); + : "=S" (data), "=c" (len) /* output */ + : "D" (z->DATA), "b" (z->STATUS), /* input */ + "0" (data), "1" (len) + : "eax", "ebx", "edi"); /* clobbered */ + PRINT (("sea (DATAOUT) send %ld bytes\n", *plen - len)); + *plen = len; + *pdata = data; +} + +/* + * Receive data from the target. + */ +void sea_data_input (adapter_t *z, u_char **pdata, u_long *plen) +{ + u_char *data = *pdata; + u_long len = *plen; + + if (len >= 512) { + asm (" 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 (" 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 */ } - /* PRINT (("sea_wait_for_req_deassert %s count=%d\n", msg, cnt)); */ - return (1); + PRINT (("sea (DATAIN) got %ld bytes\n", *plen - len)); + *plen = len; + *pdata = data; } /* * Send the command to the target. */ -void sea_cmd_output (struct sea_data *sea, u_char *cmd, int cmdlen) +void sea_cmd_output (target_t *t, u_char *cmd, int cmdlen) { - struct sea_target *t = &sea->target[sea->connected->xfer->sc_link->target]; - - if (! cmdlen || ! cmd) { - printf ("sea%d:%d:%d no command\n", - sea->sc_link.adapter_unit, - sea->connected->xfer->sc_link->target, - sea->connected->xfer->sc_link->lun); - *sea->DATA = 0; - sea_wait_for_req_deassert (sea, 1000, "ZCMDOUT"); - return; - } - PRINT (("sea%d:%d:%d (CMDOUT) send %d bytes ", - sea->sc_link.adapter_unit, - sea->connected->xfer->sc_link->target, - sea->connected->xfer->sc_link->lun, cmdlen)); + adapter_t *z = t->adapter; + + PRINT (("sea%d send command (%d bytes) ", z->sc_link.adapter_unit, + cmdlen)); PRINT (("%x", *cmd)); - *sea->DATA = *cmd++; + *z->DATA = *cmd++; WAITREQ (t, cmdout1, 10000); --cmdlen; while (cmdlen) { /* Check for target disconnect. */ - u_char sts = *sea->STATUS; + u_char sts = *z->STATUS; if (! (sts & STAT_BSY)) break; /* Check for phase mismatch. */ - if ((sts & REQ_MASK) != REQ_CMDOUT) { + if ((sts & PHASE_MASK) != PHASE_CMDOUT) { printf ("sea: sending command: invalid phase %s\n", - sea_phase_name[sts & REQ_MASK]); + PHASE_NAME (sts & PHASE_MASK)); return; } @@ -1167,7 +1138,7 @@ void sea_cmd_output (struct sea_data *sea, u_char *cmd, int cmdlen) continue; PRINT (("-%x", *cmd)); - *sea->DATA = *cmd++; + *z->DATA = *cmd++; WAITREQ (t, cmdout, 1000); --cmdlen; } @@ -1177,100 +1148,135 @@ void sea_cmd_output (struct sea_data *sea, u_char *cmd, int cmdlen) /* * Send the message to the target. */ -void sea_msg_output (struct sea_data *sea, u_char msg) +void sea_send_abort (adapter_t *z) { - struct sea_target *t = sea->connected ? - &sea->target[sea->connected->xfer->sc_link->target] : 0; - register u_long cnt = 0; - register u_char sts; + 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. */ -again: sts = *sea->STATUS; - if (! (sts & STAT_REQ)) { - if (++cnt > 1000000L) { - printf ("sea: sending message: no REQ\n"); - return; - } - goto again; - } + WAITFOR (*z->STATUS & STAT_REQ, "abort message"); + sts = *z->STATUS; + if (! (sts & STAT_REQ)) + goto ret; /* Check for phase mismatch. */ - if ((sts & REQ_MASK) != REQ_MSGOUT) { - printf ("sea: sending message: invalid phase %s\n", - sea_phase_name[sts & REQ_MASK]); - return; + if ((sts & PHASE_MASK) != PHASE_MSGOUT) { + printf ("sea: sending MSG_ABORT: invalid phase %s\n", + PHASE_NAME (sts & PHASE_MASK)); + goto ret; } - *sea->DATA = msg; - if (! t) - sea_wait_for_req_deassert (sea, 1000, "MSG_OUTPUT"); - else - WAITREQ (t, msgout, 1000); - PRINT (("sea%d:%d:%d (MSGOUT) send 0x%x\n", - sea->sc_link.adapter_unit, - sea->connected ? sea->connected->xfer->sc_link->target : 9, - sea->connected ? sea->connected->xfer->sc_link->lun : 9, - msg)); + *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 (struct sea_data *sea) +u_char sea_msg_input (adapter_t *z) { - register u_long cnt = 0; - register u_char sts, msg; + u_char sts, msg; /* Wait for REQ, after which the phase bits will be valid. */ -again: sts = *sea->STATUS; - if (! (sts & STAT_REQ)) { - if (++cnt > 1000000L) { - printf ("sea: receiving message: no REQ\n"); - return (MSG_ABORT); - } - goto again; - } + 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 & REQ_MASK) != REQ_MSGIN) { + * Reached if the target decides that it has finished the transfer. */ + if ((sts & PHASE_MASK) != PHASE_MSGIN) { printf ("sea: sending message: invalid phase %s\n", - sea_phase_name[sts & REQ_MASK]); + PHASE_NAME (sts & PHASE_MASK)); return (MSG_ABORT); } /* Do actual transfer from SCSI bus to/from memory. */ - msg = *sea->DATA; - sea_wait_for_req_deassert (sea, 1000, "MSG_INPUT"); - PRINT (("sea%d:%d:%d (MSG_INPUT) got 0x%x\n", - sea->sc_link.adapter_unit, - sea->connected ? sea->connected->xfer->sc_link->target : 9, - sea->connected ? sea->connected->xfer->sc_link->lun : 9, msg)); + 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); } /* - * This routine is used in the case when we have no IRQ line (sea->irq == 0). - * It is called every timer tick and polls for reconnect from target. + * Send request-sense op to the target. + * Return 1 success, 0 on failure. + * Called on splbio level. */ -void sea_tick (void *arg) +int sea_sense (adapter_t *z, scb_t *scb) { - struct sea_data *sea = arg; - int x = splbio (); + u_char cmd[6], status, msg, *data; + u_long len; - sea->timeout_active = 0; - while ((*sea->STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) { - /* Reselect interrupt */ - sea_reselect (sea); - sea_start (); - } - if (sea->disconnected_queue && ! sea->timeout_active) { - timeout (sea_tick, sea, 1); - sea->timeout_active = 1; - } - splx (x); + /* 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); } /* @@ -1278,160 +1284,120 @@ void sea_tick (void *arg) * call sea_done when task accomplished. Dialog controlled by the target. * Always called with interrupts disabled. */ -static void sea_information_transfer (register struct sea_data *sea) +void sea_information_transfer (adapter_t *z, scb_t *scb) { - struct sea_scb *scb = sea->connected; /* current control block */ u_char *data = scb->data; /* current data buffer */ u_long datalen = scb->datalen; /* current data transfer size */ - struct sea_target *t = &sea->target[scb->xfer->sc_link->target]; + target_t *t = &z->target[scb->xfer->sc_link->target]; register u_char sts; u_char msg; - while ((sts = *sea->STATUS) & STAT_BSY) { + 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 (++sea->target[target].perrcnt < 8) - printf ("sea%d:%d:%d parity error\n", - sea->sc_link.adapter_unit, 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); - else if (sea->target[target].perrcnt == 8) - printf ("sea%d:%d:%d too many parity errors, not logging any more\n", - sea->sc_link.adapter_unit, target, + 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 & REQ_MASK) { - case REQ_DATAOUT: + switch (sts & PHASE_MASK) { + case PHASE_DATAOUT: if (datalen <= 0) { - printf ("sea%d:%d:%d data length underflow\n", - sea->sc_link.adapter_unit, + printf ("sea%d/%d/%d data length underflow\n", + z->sc_link.adapter_unit, scb->xfer->sc_link->target, scb->xfer->sc_link->lun); - *sea->DATA = 0; + /* send zero byte */ + *z->DATA = 0; break; } - asm ("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" (datalen) /* output */ - : "D" (sea->DATA), "b" (sea->STATUS), /* input */ - "0" (data), "1" (datalen) - : "eax", "ebx", "edi"); /* clobbered */ - PRINT (("sea%d:%d:%d (DATAOUT) send %ld bytes\n", - sea->sc_link.adapter_unit, - scb->xfer->sc_link->target, - scb->xfer->sc_link->lun, - scb->datalen - datalen)); + sea_data_output (z, &data, &datalen); break; - case REQ_DATAIN: + 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 = *sea->DATA; + sts = *z->DATA; break; } - if (datalen >= 512) { - asm (" 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" (datalen) /* output */ - : "b" (sea->DATA), "S" (sea->STATUS), - "0" (data), "1" (datalen) /* input */ - : "eax", "ebx", "esi"); /* clobbered */ - } else { - asm (" 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" (datalen) /* output */ - : "b" (sea->DATA), "S" (sea->STATUS), - "0" (data), "1" (datalen) /* input */ - : "eax", "ebx", "esi"); /* clobbered */ - } - PRINT (("sea%d:%d:%d (DATAIN) got %ld bytes\n", - sea->sc_link.adapter_unit, - scb->xfer->sc_link->target, - scb->xfer->sc_link->lun, - scb->datalen - datalen)); + sea_data_input (z, &data, &datalen); break; - case REQ_CMDOUT: - sea_cmd_output (sea, (u_char*) scb->xfer->cmd, + case PHASE_CMDOUT: + sea_cmd_output (t, (u_char*) scb->xfer->cmd, scb->xfer->cmdlen); break; - case REQ_STATIN: - scb->xfer->status = *sea->DATA; + case PHASE_STATIN: + scb->xfer->status = *z->DATA; WAITREQ (t, statin, 2000); - PRINT (("sea%d:%d:%d (STATIN) got 0x%x\n", - sea->sc_link.adapter_unit, + 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 REQ_MSGOUT: - *sea->DATA = MSG_NOP; - sea_wait_for_req_deassert (sea, 1000, "MSGOUT"); - PRINT (("sea%d:%d:%d (MSGOUT) send NOP\n", - sea->sc_link.adapter_unit, + 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 REQ_MSGIN: + case PHASE_MSGIN: /* Don't handle multi-byte messages here, because they * should not be present here. */ - msg = *sea->DATA; - sea_wait_for_req_deassert (sea, 2000, "MSGIN"); - PRINT (("sea%d:%d:%d (MSGIN) got 0x%x\n", - sea->sc_link.adapter_unit, + 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 = sea->disconnected_queue; - sea->disconnected_queue = scb; - sea->connected = 0; - *sea->CONTROL = CMD_INTR | sea->parity; - if (! sea->irq && ! sea->timeout_active) { - timeout (sea_tick, sea, 1); - sea->timeout_active = 1; + scb->next = z->disconnected_queue; + z->disconnected_queue = scb; + if (! z->irq && ! z->timeout_active) { + timeout (sea_tick, z, 1); + z->timeout_active = 1; } - return; + 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; @@ -1440,28 +1406,27 @@ static void sea_information_transfer (register struct sea_data *sea) data = scb->data; datalen = scb->datalen; break; - case MSG_MESSAGE_REJECT: - PRINT (("sea: message_reject received\n")); - break; default: - printf ("sea%d:%d:%d unknown message: 0x%x\n", - sea->sc_link.adapter_unit, scb->xfer->sc_link->target, + 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 & REQ_MASK, STAT_BITS); + printf ("sea: unknown phase: %b\n", sts, STAT_BITS); + break; } } - printf ("sea%d:%d:%d unexpected target disconnect\n", - sea->sc_link.adapter_unit, scb->xfer->sc_link->target, + 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: sea->connected = 0; - CLEAR_BUSY (sea, scb); - *sea->CONTROL = CMD_INTR | sea->parity; - sea_done (sea->sc_link.adapter_unit, scb); +done: + CLEAR_BUSY (z, scb); + sea_done (z, scb); +ret: + *z->CONTROL = CMD_INTR | z->parity; } #endif /* NSEA */ |