freebsd-skq/sys/i386/isa/ncr5380.c

1542 lines
43 KiB
C

/*
* FreeBSD generic NCR-5380/NCR-53C400 SCSI driver
*
* Copyright (C) 1994 Serge Vakulenko (vak@cronyx.ru)
*
* 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.
*/
/*
* Tested on the following hardware:
* Adapter: Trantor T130
* Streamer: Archive Viper 150,
* CD-ROM: NEC CDR-25
*/
#undef DEBUG
#include "nca.h"
#if NNCA > 0
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <machine/clock.h>
#include <i386/isa/isa_device.h>
#include <i386/isa/ic/ncr5380.h>
#include <i386/isa/ic/ncr53400.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#ifdef DEBUG
# define PRINT(s) printf s
#else
# define PRINT(s) /*void*/
#endif
#define SCB_TABLE_SIZE 8 /* start with 8 scb entries in table */
#define BLOCK_SIZE 512 /* size of READ/WRITE areas on SCSI card */
#define HOST_SCSI_ADDR 7 /* address of the adapter on the SCSI bus */
/*
* Defice config flags
*/
#define FLAG_NOPARITY 0x01 /* disable SCSI bus parity check */
/*
* ProAudioSpectrum registers
*/
#define PAS16_DATA 8 /* Data Register */
#define PAS16_STAT 9 /* Status Register */
#define PAS16_STAT_DREQ 0x80 /* Pseudo-DMA ready bit */
#define PAS16_REG(r) (((r) & 0xc) << 11 | ((r) & 3))
static u_char pas16_irq_magic[] =
{ 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 };
/*
* SCSI bus phases
*/
#define PHASE_MASK (CSBR_MSG | CSBR_CD | CSBR_IO)
#define PHASE_DATAOUT 0
#define PHASE_DATAIN CSBR_IO
#define PHASE_CMDOUT CSBR_CD
#define PHASE_STATIN (CSBR_CD | CSBR_IO)
#define PHASE_MSGOUT (CSBR_MSG | CSBR_CD)
#define PHASE_MSGIN (CSBR_MSG | CSBR_CD | CSBR_IO)
#define PHASE_NAME(ph) phase_name[(ph)>>2]
#define PHASE_TO_TCR(ph) ((ph) >> 2)
static char *phase_name[] = {
"DATAOUT", "DATAIN", "CMDOUT", "STATIN",
"Phase4?", "Phase5?", "MSGOUT", "MSGIN",
};
/*
* 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_NCR_5380,
CTLR_NCR_53C400,
CTLR_PAS_16,
} ctlr_t;
/*
* Data structure describing the target state.
*/
typedef struct {
u_char busy; /* mask of busy luns at device target */
u_long perrcnt; /* counter of target parity errors */
} target_t;
/*
* Data structure describing current status of the scsi bus. One for each
* controller card.
*/
typedef struct {
ctlr_t type; /* Seagate or Future Domain */
char *name; /* adapter name */
/* NCR-5380 controller registers */
u_short ODR; /* (wo-0) Output Data Register */
u_short CSDR; /* (ro-0) Current SCSI Data Register */
u_short ICR; /* (rw-1) Initiator Command Register */
u_short MR; /* (rw-2) Mode Register */
u_short TCR; /* (rw-3) Target Command Register */
u_short SER; /* (wo-4) Select Enable Register */
u_short CSBR; /* (ro-4) Current SCSI Bus Status Register */
u_short BSR; /* (ro-5) Bus and Status Register */
u_short SDSR; /* (wo-5) Start DMA Send Register */
u_short SDIR; /* (wo-7) Start DMA Initiator Receive Register */
u_short RPIR; /* (ro-7) Reset Parity/Interrupt Register */
/* NCR-53C400 controller registers */
u_short CSR; /* (rw-0) Control and Status Register */
u_short CCR; /* (rw-1) Clock Counter Register */
u_short HBR; /* (rw-4) Host Buffer Register */
/* ProAudioSpectrum controller registers */
u_short PDATA; /* (rw) Pseudo-DMA Data Register */
u_short PSTAT; /* (rw) Pseudo-DMA Status 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 ncadata[NNCA];
#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,count,message) {\
register u_long cnt = count; char *msg = message;\
while (cnt-- && ! (condition)) continue;\
if (cnt == -1 && msg)\
printf ("nca: %s timeout\n", msg); }
static int nca_probe (struct isa_device *dev);
static int nca_attach (struct isa_device *dev);
static int32_t nca_scsi_cmd (struct scsi_xfer *xs);
static u_int32_t nca_adapter_info (int unit);
static void nca_timeout (void *scb);
static void ncaminphys (struct buf *bp);
static void nca_done (adapter_t *z, scb_t *scb);
static void nca_start (adapter_t *z);
static void nca_information_transfer (adapter_t *z, scb_t *scb);
static int nca_poll (adapter_t *z, scb_t *scb);
static int nca_init (adapter_t *z);
static int nca_reselect (adapter_t *z);
static int nca_select (adapter_t *z, scb_t *scb);
static int nca_abort (adapter_t *z, scb_t *scb);
static void nca_send_abort (adapter_t *z);
static u_char nca_msg_input (adapter_t *z);
static void nca_tick (void *arg);
static int nca_sense (adapter_t *z, scb_t *scb);
static void nca_data_output (adapter_t *z, u_char **pdata, u_long *plen);
static void nca_data_input (adapter_t *z, u_char **pdata, u_long *plen);
static void nca_cmd_output (adapter_t *z, u_char *cmd, int cmdlen);
static void nca_53400_dma_xfer (adapter_t *z, int r, u_char **dat, u_long *len);
static void nca_pas_dma_xfer (adapter_t *z, int r, u_char **dat, u_long *len);
static struct scsi_adapter nca_switch = {
nca_scsi_cmd, ncaminphys, 0, 0, nca_adapter_info, "nca", {0},
};
static struct scsi_device nca_dev = { NULL, NULL, NULL, NULL, "nca", 0, {0} };
struct isa_driver ncadriver = { nca_probe, nca_attach, "nca" };
/*
* 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 nca_probe (struct isa_device *dev)
{
adapter_t *z = &ncadata[dev->id_unit];
int i;
/* Init fields used by our routines */
z->parity = (dev->id_flags & FLAG_NOPARITY) ? 0 :
MR_ENABLE_PARITY_CHECKING;
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].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;
/* Try NCR 5380. */
z->type = CTLR_NCR_5380;
z->name = "NCR-5380";
z->ODR = dev->id_iobase + C80_ODR;
z->CSDR = dev->id_iobase + C80_CSDR;
z->ICR = dev->id_iobase + C80_ICR;
z->MR = dev->id_iobase + C80_MR;
z->TCR = dev->id_iobase + C80_TCR;
z->SER = dev->id_iobase + C80_SER;
z->CSBR = dev->id_iobase + C80_CSBR;
z->BSR = dev->id_iobase + C80_BSR;
z->SDSR = dev->id_iobase + C80_SDSR;
z->SDIR = dev->id_iobase + C80_SDIR;
z->RPIR = dev->id_iobase + C80_RPIR;
z->CSR = 0;
z->CCR = 0;
z->HBR = 0;
z->PDATA = 0;
z->PSTAT = 0;
if (nca_init (z) == 0)
return (8);
/* Try NCR 53C400. */
z->type = CTLR_NCR_53C400;
z->name = "NCR-53C400";
z->ODR = dev->id_iobase + C400_5380_REG_OFFSET + C80_ODR;
z->CSDR = dev->id_iobase + C400_5380_REG_OFFSET + C80_CSDR;
z->ICR = dev->id_iobase + C400_5380_REG_OFFSET + C80_ICR;
z->MR = dev->id_iobase + C400_5380_REG_OFFSET + C80_MR;
z->TCR = dev->id_iobase + C400_5380_REG_OFFSET + C80_TCR;
z->SER = dev->id_iobase + C400_5380_REG_OFFSET + C80_SER;
z->CSBR = dev->id_iobase + C400_5380_REG_OFFSET + C80_CSBR;
z->BSR = dev->id_iobase + C400_5380_REG_OFFSET + C80_BSR;
z->SDSR = dev->id_iobase + C400_5380_REG_OFFSET + C80_SDSR;
z->SDIR = dev->id_iobase + C400_5380_REG_OFFSET + C80_SDIR;
z->RPIR = dev->id_iobase + C400_5380_REG_OFFSET + C80_RPIR;
z->CSR = dev->id_iobase + C400_CSR;
z->CCR = dev->id_iobase + C400_CCR;
z->HBR = dev->id_iobase + C400_HBR;
z->PDATA = 0;
z->PSTAT = 0;
if (nca_init (z) == 0)
return (16);
/* Try ProAudioSpectrum-16. */
z->type = CTLR_PAS_16;
z->name = "ProAudioSpectrum"; /* changed later */
z->ODR = dev->id_iobase ^ PAS16_REG (C80_ODR);
z->CSDR = dev->id_iobase ^ PAS16_REG (C80_CSDR);
z->ICR = dev->id_iobase ^ PAS16_REG (C80_ICR);
z->MR = dev->id_iobase ^ PAS16_REG (C80_MR);
z->TCR = dev->id_iobase ^ PAS16_REG (C80_TCR);
z->SER = dev->id_iobase ^ PAS16_REG (C80_SER);
z->CSBR = dev->id_iobase ^ PAS16_REG (C80_CSBR);
z->BSR = dev->id_iobase ^ PAS16_REG (C80_BSR);
z->SDSR = dev->id_iobase ^ PAS16_REG (C80_SDSR);
z->SDIR = dev->id_iobase ^ PAS16_REG (C80_SDIR);
z->RPIR = dev->id_iobase ^ PAS16_REG (C80_RPIR);
z->CSR = 0;
z->CCR = 0;
z->HBR = 0;
z->PDATA = dev->id_iobase ^ PAS16_REG (PAS16_DATA);
z->PSTAT = dev->id_iobase ^ PAS16_REG (PAS16_STAT);
if (nca_init (z) == 0)
return (4);
bzero (z, sizeof (*z));
return (0);
}
/*
* Probe the adapter, and if found, reset the board and the scsi bus.
* Return 0 if the adapter found.
*/
int nca_init (adapter_t *z)
{
int i, c;
if (z->type == CTLR_NCR_53C400) {
if (inb (z->CSR) == 0xFF)
return (100);
/* Reset 53C400. */
outb (z->CSR, CSR_5380_ENABLE);
/* Enable interrupts. */
outb (z->CSR, z->irq ? CSR_5380_INTR : 0);
}
if (z->type == CTLR_PAS_16) {
u_short base = z->PDATA & 0x3FF;
outb (0x9a01, 0xbc + (z-ncadata)); /* unit number */
outb (0x9a01, base >> 2);
if (inb (base^0x803) == 0xFF)
return (200);
if (inb (z->CSDR) == 0xFF && inb (z->CSDR^0x2000) == 0xFF &&
inb (z->CSDR) == 0xFF && inb (z->CSDR^0x2000) == 0xFF &&
inb (z->CSDR) == 0xFF && inb (z->CSDR^0x2000) == 0xFF &&
inb (z->CSDR) == 0xFF && inb (z->CSDR^0x2000) == 0xFF)
return (201);
i = inb (base^0x803);
outb (base^0x803, i ^ 0xE0);
c = inb (base^0x803);
outb (base^0x803, 1);
if (i != c)
return (202);
/* Various magic. */
outb (base^0x4000, 0x30); /* Timeout counter */
outb (base^0x4001, 0x01); /* Reset TC */
outb (base^0xbc00, 0x01); /* 1 Wait state */
outb (base^0x8003, 0x4d); /* sysconfig_4 */
i = pas16_irq_magic[z->irq];
if (!i) {
z->irq = 0;
} else {
outb (base^0xf002, i << 4);
outb (base^0x8003, 0x6d); /* sysconfig_4 */
}
switch (inb (base^0xEC03) & 0xF) {
case 6: z->name = "ProAudioSpectrum-Plus"; break;
case 12: z->name = "ProAudioSpectrum-16D"; break;
case 14: z->name = "ProAudioSpectrum-CDPC"; break;
case 15: z->name = "ProAudioSpectrum-16"; break;
default: return (203);
}
}
/* Read RPI port, resetting parity/interrupt state. */
inb (z->RPIR);
/* Test BSR: parity error, interrupt request and busy loss state
* should be cleared. */
if (inb (z->BSR) & (BSR_PARITY_ERROR |
BSR_INTERRUPT_REQUEST_ACTIVE | BSR_BUSY_ERROR)) {
PRINT (("nca: invalid bsr[0x%x]=%b\n", z->BSR,
inb (z->BSR), BSR_BITS));
return (1);
}
/* Reset the SCSI bus. */
outb (z->ICR, ICR_ASSERT_RST);
outb (z->ODR, 0);
/* Hold reset for at least 25 microseconds. */
DELAY (25);
/* Check that status cleared. */
if (inb (z->CSBR) != CSBR_RST) {
PRINT (("nca: invalid csbr[0x%x]=%b\n", z->CSBR,
inb (z->CSBR), CSBR_BITS));
outb (z->ICR, 0);
return (2);
}
/* Clear reset. */
outb (z->ICR, 0);
/* Wait a Bus Clear Delay (800 ns + bus free delay 800 ns). */
DELAY (2);
/* Enable data drivers. */
outb (z->ICR, ICR_ASSERT_DATA_BUS);
/* Check that data register is writable. */
for (i=0; i<256; ++i) {
outb (z->ODR, i);
DELAY (1);
if (inb (z->CSDR) != i) {
PRINT (("nca: ODR[0x%x] not writable: 0x%x should be 0x%x\n",
z->ODR, inb (z->CSDR), i));
outb (z->ICR, 0);
return (3);
}
}
/* Disable data drivers. */
outb (z->ICR, 0);
/* Check that data register is NOT writable. */
c = inb (z->CSDR);
for (i=0; i<256; ++i) {
outb (z->ODR, i);
DELAY (1);
if (inb (z->CSDR) != c) {
PRINT (("nca: ODR[0x%x] writable: 0x%x should be 0x%x\n",
z->ODR, inb (z->CSDR), c));
return (4);
}
}
/* Initialize the controller. */
outb (z->MR, z->parity);
outb (z->TCR, 0);
outb (z->SER, z->scsi_id);
return (0);
}
/*
* Attach all sub-devices we can find.
*/
int nca_attach (struct isa_device *dev)
{
int unit = dev->id_unit;
adapter_t *z = &ncadata[unit];
struct scsibus_data *scbus;
printf ("nca%d: type %s%s\n", unit, z->name,
(dev->id_flags & FLAG_NOPARITY) ? ", no parity" : "");
/* fill in the prototype scsi_link */
z->sc_link.adapter_unit = unit;
z->sc_link.adapter_targ = z->scsi_addr;
z->sc_link.adapter_softc = z;
z->sc_link.adapter = &nca_switch;
z->sc_link.device = &nca_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 nca_adapter_info (int unit)
{
return (1);
}
void ncaminphys (struct buf *bp)
{
}
/*
* Catch an interrupt from the adaptor.
*/
void ncaintr (int unit)
{
adapter_t *z = &ncadata[unit];
PRINT (("nca%d: interrupt bsr=%b csbr=%b\n", unit,
inb (z->BSR), BSR_BITS, inb (z->CSBR), CSBR_BITS));
nca_start (z);
/* Reset interrupt state. */
inb (z->RPIR);
}
/*
* 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 nca_tick (void *arg)
{
adapter_t *z = arg;
int x = splbio ();
z->timeout_active = 0;
nca_start (z);
/* Reset interrupt state. */
inb (z->RPIR);
if (z->disconnected_queue && ! z->timeout_active) {
timeout (nca_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 nca_scsi_cmd (struct scsi_xfer *xs)
{
int unit = xs->sc_link->adapter_unit, flags = xs->flags, x = 0;
adapter_t *z = (adapter_t *)xs->sc_link->adapter_softc;
scb_t *scb;
/* PRINT (("nca%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 ("nca%d: already done?", unit);
xs->flags &= ~ITSDONE;
}
if (! (flags & INUSE)) {
printf ("nca%d: not in use?", unit);
xs->flags |= INUSE;
}
if (flags & SCSI_RESET)
printf ("nca%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, "ncascb", 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. */
nca_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);
timeout (nca_timeout, (caddr_t) scb, (xs->timeout * hz) / 1000);
scb->flags |= SCB_TIMECHK;
PRINT (("nca%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 (! nca_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. */
nca_timeout ((void*) scb);
/* Because we are polling, take out the timeout entry
* nca_timeout made. */
untimeout (nca_timeout, (void*) scb);
if (! nca_poll (z, scb))
/* We timed out again... This is bad. Notice that
* this time there is no clock queue entry to remove. */
nca_timeout ((void*) scb);
}
/* PRINT (("nca%d/%d/%d command %s\n", 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 nca_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 (nca_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) || ! nca_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. */
nca_information_transfer (z, q);
goto again;
}
}
void nca_timeout (void *arg)
{
scb_t *scb = (scb_t*) arg;
int unit = scb->xfer->sc_link->adapter_unit;
adapter_t *z = (adapter_t *)scb->xfer->sc_link->adapter_softc;
int x = splbio ();
if (! (scb->xfer->flags & SCSI_NOMASK))
printf ("nca%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)) {
nca_abort (z, scb);
/* 2 seconds for the abort */
timeout (nca_timeout, (caddr_t)scb, 2*hz);
scb->flags |= (SCB_ABORTED | SCB_TIMECHK);
} else {
/* abort timed out */
scb->flags |= SCB_ABORTED;
scb->xfer->retries = 0;
nca_done (z, scb);
}
splx (x);
}
static inline void nca_sendbyte (adapter_t *z, u_char data)
{
outb (z->ODR, data);
outb (z->ICR, ICR_ASSERT_DATA_BUS | ICR_ASSERT_ACK);
WAITFOR (! (inb (z->CSBR) & CSBR_REQ), 10000, "sendbyte");
outb (z->ICR, ICR_ASSERT_DATA_BUS);
}
static inline u_char nca_recvbyte (adapter_t *z)
{
u_char data;
data = inb (z->CSDR);
outb (z->ICR, ICR_ASSERT_ACK);
WAITFOR (! (inb (z->CSBR) & CSBR_REQ), 10000, "recvbyte");
outb (z->ICR, 0);
return (data);
}
/*
* 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 nca_select (adapter_t *z, scb_t *scb)
{
/* Set the phase bits to 0, otherwise the NCR5380 won't drive the
* data bus during SELECTION. */
outb (z->TCR, 0);
/* Start arbitration. */
outb (z->ODR, z->scsi_id);
outb (z->MR, MR_ARBITRATE);
/* Wait for arbitration logic to complete (20 usec) */
WAITFOR (inb (z->ICR) & ICR_ARBITRATION_IN_PROGRESS, 200, 0);
if (! (inb (z->ICR) & ICR_ARBITRATION_IN_PROGRESS)) {
PRINT (("nca%d/%d/%d no arbitration progress, bsr=%b csbr=%b\n",
z->sc_link.adapter_unit, scb->xfer->sc_link->target,
scb->xfer->sc_link->lun, inb (z->BSR), BSR_BITS,
inb (z->CSBR), CSBR_BITS));
outb (z->MR, z->parity);
return (0);
}
DELAY (3);
/* Check for lost arbitration. */
if ((inb (z->ICR) & ICR_LOST_ARBITRATION) ||
(inb (z->CSDR) >> 1 >> z->scsi_addr) ||
(inb (z->ICR) & ICR_LOST_ARBITRATION)) {
PRINT (("nca%d/%d/%d arbitration lost\n",
z->sc_link.adapter_unit, scb->xfer->sc_link->target,
scb->xfer->sc_link->lun));
outb (z->MR, z->parity);
return (0);
}
outb (z->ICR, ICR_ASSERT_SEL);
if (inb (z->ICR) & ICR_LOST_ARBITRATION) {
PRINT (("nca%d/%d/%d arbitration lost after SEL\n",
z->sc_link.adapter_unit, scb->xfer->sc_link->target,
scb->xfer->sc_link->lun));
outb (z->ICR, 0);
outb (z->MR, z->parity);
return (0);
}
DELAY (2);
/* Start selection, asserting the host and target ID's on the bus. */
outb (z->SER, 0);
outb (z->ODR, z->scsi_id | (1 << scb->xfer->sc_link->target));
outb (z->ICR, ICR_ASSERT_DATA_BUS | ICR_ASSERT_BSY |
ICR_ASSERT_SEL);
/* Finish arbitration, drop BSY. */
outb (z->MR, 0);
outb (z->ICR, ICR_ASSERT_DATA_BUS | ICR_ASSERT_SEL |
ICR_ASSERT_ATN);
DELAY (1);
/* The SCSI specification calls for a 250 ms timeout for the actual
* selection. */
WAITFOR (inb (z->CSBR) & CSBR_BSY, 100000, 0);
if (! (inb (z->CSBR) & CSBR_BSY)) {
/* The target does not respond. Not an error, though. */
PRINT (("nca%d/%d/%d target does not respond\n",
z->sc_link.adapter_unit, scb->xfer->sc_link->target,
scb->xfer->sc_link->lun));
outb (z->ICR, 0);
outb (z->SER, z->scsi_id);
outb (z->MR, z->parity);
scb->flags |= SCB_TIMEOUT;
return (0);
}
/* Clear SEL and SCSI id.
* Wait for start of REQ/ACK handshake. */
outb (z->ICR, ICR_ASSERT_DATA_BUS | ICR_ASSERT_ATN);
WAITFOR (inb (z->CSBR) & CSBR_REQ, 100000, 0);
if (! (inb (z->CSBR) & CSBR_REQ)) {
PRINT (("nca%d/%d/%d timeout waiting for REQ\n",
z->sc_link.adapter_unit, scb->xfer->sc_link->target,
scb->xfer->sc_link->lun));
outb (z->ICR, 0);
outb (z->SER, z->scsi_id);
outb (z->MR, z->parity);
scb->flags |= SCB_ERROR;
return (0);
}
/* Check for phase mismatch. */
if ((inb (z->CSBR) & PHASE_MASK) != PHASE_MSGOUT) {
/* 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!) */
PRINT (("nca%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 (inb (z->CSBR) & PHASE_MASK)));
outb (z->ICR, 0);
outb (z->SER, z->scsi_id);
outb (z->MR, z->parity);
scb->flags |= SCB_ERROR;
return (0);
}
/* Allow disconnects. */
outb (z->TCR, PHASE_TO_TCR (PHASE_MSGOUT));
outb (z->ICR, ICR_ASSERT_DATA_BUS);
nca_sendbyte (z, MSG_IDENTIFY (scb->xfer->sc_link->lun));
outb (z->ICR, 0);
outb (z->SER, z->scsi_id);
outb (z->MR, z->parity);
SET_BUSY (z, scb);
return (1);
}
int nca_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 ((inb (z->CSBR) & (CSBR_SEL | CSBR_IO | CSBR_BSY)) !=
(CSBR_SEL | CSBR_IO))
return (0);
/* The data bus contains original initiator id ORed with target id. */
/* See that we really are the initiator. */
target_mask = inb (z->CSDR);
if (! (target_mask & z->scsi_id)) {
PRINT (("nca%d reselect not for me: mask=0x%x, csbr=%b\n",
z->sc_link.adapter_unit, target_mask,
inb (z->CSBR), CSBR_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;
outb (z->ICR, ICR_ASSERT_BSY);
WAITFOR (! (inb (z->CSBR) & CSBR_SEL), 10000, "SEL deassert");
/* Remove the busy status. */
/* Target should set the MSGIN phase. */
outb (z->ICR, 0);
WAITFOR (inb (z->CSBR) & CSBR_REQ, 10000, "MSGIN");
/* Hope we get an IDENTIFY message. */
msg = nca_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 (("nca%d/%d/%d reselect done\n",
z->sc_link.adapter_unit,
ffs (target_mask) - 1, lun));
nca_information_transfer (z, q);
WAITFOR (! (inb (z->CSBR) & CSBR_BSY), 100000, "reselect !busy");
return (1);
}
} else
printf ("nca%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. */
nca_send_abort (z);
PRINT (("nca%d reselect aborted\n", z->sc_link.adapter_unit));
WAITFOR (! (inb (z->CSBR) & CSBR_BSY), 100000, "reselect abort !busy");
goto again;
}
/*
* Send an abort to the target.
* Return 1 success, 0 on failure.
* Called on splbio level.
*/
int nca_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 (! nca_select (z, scb))
return (0);
nca_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 nca_done (adapter_t *z, scb_t *scb)
{
struct scsi_xfer *xs = scb->xfer;
if (scb->flags & SCB_TIMECHK)
untimeout (nca_timeout, (caddr_t) scb);
/* 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 nca_poll (adapter_t *z, scb_t *scb)
{
int count;
for (count=0; count<30; ++count) {
DELAY (1000); /* delay for a while */
nca_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);
}
/*
* Perform NCR-53C400 pseudo-dma data transfer.
*/
void nca_53400_dma_xfer (adapter_t *z, int read, u_char **pdata, u_long *plen)
{
/* Set dma direction. */
outb (z->CSR, read ? CSR_TRANSFER_DIRECTION : 0);
/* Enable dma mode. */
outb (z->MR, MR_DMA_MODE | (read ? z->parity : 0));
/* Start dma transfer. */
outb (read ? z->SDIR : z->SDSR, 0);
/* Set up clock counter. */
outb (z->CCR, *plen/128);
for (; *plen>=128; *plen-=128, *pdata+=128) {
/* Wait for 53C400 host buffer ready. */
WAITFOR (! (inb (z->CSR) & CSR_HOST_BUF_NOT_READY), 100000, 0);
if (inb (z->CSR) & CSR_HOST_BUF_NOT_READY)
break;
/* Transfer 128 bytes of data. */
if (read)
insw (z->HBR, *pdata, 64);
else
outsw (z->HBR, *pdata, 64);
}
/* Wait for 5380 registers ready. */
WAITFOR (inb (z->CSR) & CSR_5380_ENABLE, 10000, 0);
if (! (inb (z->CSR) & CSR_5380_ENABLE)) {
/* Reset 53C400. */
PRINT (("nca%d: reset: pseudo-dma incomplete, csr=%b\n",
z->sc_link.adapter_unit, inb (z->CSR), CSR_BITS));
outb (z->CSR, CSR_5380_ENABLE);
outb (z->CSR, 0);
}
/* Wait for FIFO flush on write. */
if (! read)
WAITFOR (inb (z->TCR) & TCR_LAST_BYTE_SENT, 10000, "last byte");
/* Clear dma mode. */
outb (z->MR, z->parity);
/* Re-enable interrupts. */
outb (z->CSR, z->irq ? CSR_5380_INTR : 0);
}
/*
* Perform PAS-16 pseudo-dma data transfer.
*/
void nca_pas_dma_xfer (adapter_t *z, int read, u_char **pdata, u_long *plen)
{
/* Enable dma mode. */
outb (z->MR, MR_DMA_MODE | (read ? z->parity : 0));
/* Start dma transfer. */
outb (read ? z->SDIR : z->SDSR, 0);
for (; *plen>=512; *plen-=512, *pdata+=512) {
/* Wait for pseudo-DMA request. */
WAITFOR (inb (z->PSTAT) & PAS16_STAT_DREQ, 10000, "pseudo-dma");
if (! (inb (z->PSTAT) & PAS16_STAT_DREQ))
break;
/* Transfer 512 bytes of data. */
if (read)
insb (z->PDATA, *pdata, 512);
else
outsb (z->PDATA, *pdata, 512);
}
/* Clear dma mode. */
outb (z->MR, z->parity);
}
/*
* Send data to the target.
*/
void nca_data_output (adapter_t *z, u_char **pdata, u_long *plen)
{
u_char *data = *pdata;
u_long len = *plen;
outb (z->ICR, ICR_ASSERT_DATA_BUS);
if (z->type == CTLR_NCR_53C400 && len%128 == 0)
/* Use NCR-53C400 pseudo-dma for data transfer. */
nca_53400_dma_xfer (z, 0, &data, &len);
else if (z->type == CTLR_PAS_16 && len%512 == 0)
/* Use PAS-16 pseudo-dma for data transfer. */
nca_pas_dma_xfer (z, 0, &data, &len);
else
for (;;) {
/* Check SCSI bus phase. */
u_char s = inb (z->CSBR) ^ (CSBR_BSY | PHASE_DATAOUT);
if (s & (CSBR_BSY | PHASE_MASK))
break;
/* Wait for REQ. */
if (! (s & CSBR_REQ))
continue;
/* Output data. */
outb (z->ODR, *data++);
/* Assert ACK and wait for REQ deassert,
* with irqs disabled. */
disable_intr ();
outb (z->ICR, ICR_ASSERT_ACK | ICR_ASSERT_DATA_BUS);
WAITFOR (! (inb (z->CSBR) & CSBR_REQ), 1000, 0);
enable_intr ();
/* Deassert ACK. */
outb (z->ICR, ICR_ASSERT_DATA_BUS);
--len;
}
outb (z->ICR, 0);
PRINT (("nca (DATAOUT) send %ld bytes\n", *plen - len));
*plen = len;
*pdata = data;
}
/*
* Receive data from the target.
*/
void nca_data_input (adapter_t *z, u_char **pdata, u_long *plen)
{
u_char *data = *pdata;
u_long len = *plen;
if (z->type == CTLR_NCR_53C400 && len%128 == 0)
/* Use NCR-53C400 pseudo-dma for data transfer. */
nca_53400_dma_xfer (z, 1, &data, &len);
else if (z->type == CTLR_PAS_16 && len%512 == 0)
/* Use PAS-16 pseudo-dma for data transfer. */
nca_pas_dma_xfer (z, 1, &data, &len);
else
for (;;) {
/* Check SCSI bus phase. */
u_char s = inb (z->CSBR) ^ (CSBR_BSY | PHASE_DATAIN);
if (s & (CSBR_BSY | PHASE_MASK))
break;
/* Wait for REQ. */
if (! (s & CSBR_REQ))
continue;
/* Input data. */
*data++ = inb (z->CSDR);
/* Assert ACK and wait for REQ deassert,
* with irqs disabled. */
disable_intr ();
outb (z->ICR, ICR_ASSERT_ACK);
WAITFOR (! (inb (z->CSBR) & CSBR_REQ), 1000, 0);
enable_intr ();
/* Deassert ACK. */
outb (z->ICR, 0);
--len;
}
PRINT (("nca (DATAIN) got %ld bytes\n", *plen - len));
*plen = len;
*pdata = data;
}
/*
* Send the command to the target.
*/
void nca_cmd_output (adapter_t *z, u_char *cmd, int cmdlen)
{
PRINT (("nca%d send command (%d bytes) ", z->sc_link.adapter_unit,
cmdlen));
outb (z->ICR, ICR_ASSERT_DATA_BUS);
while (cmdlen) {
/* Check for target disconnect. */
u_char sts = inb (z->CSBR);
if (! (sts & CSBR_BSY))
break;
/* Check for phase mismatch. */
if ((sts & PHASE_MASK) != PHASE_CMDOUT) {
printf ("nca: sending command: invalid phase %s\n",
PHASE_NAME (sts & PHASE_MASK));
break;
}
/* Wait for REQ. */
if (! (sts & CSBR_REQ))
continue;
PRINT (("-%x", *cmd));
nca_sendbyte (z, *cmd++);
--cmdlen;
}
outb (z->ICR, 0);
PRINT (("\n"));
}
/*
* Send the message to the target.
*/
void nca_send_abort (adapter_t *z)
{
u_char sts;
outb (z->ICR, ICR_ASSERT_ATN);
/* Wait for REQ, after which the phase bits will be valid. */
WAITFOR (inb (z->CSBR) & CSBR_REQ, 1000000, "abort message");
sts = inb (z->CSBR);
if (! (sts & CSBR_REQ))
goto ret;
/* Check for phase mismatch. */
if ((sts & PHASE_MASK) != PHASE_MSGOUT) {
printf ("nca: sending MSG_ABORT: invalid phase %s\n",
PHASE_NAME (sts & PHASE_MASK));
goto ret;
}
outb (z->ICR, ICR_ASSERT_DATA_BUS);
outb (z->TCR, PHASE_TO_TCR (PHASE_MSGOUT));
nca_sendbyte (z, MSG_ABORT);
PRINT (("nca%d send MSG_ABORT\n", z->sc_link.adapter_unit));
ret: outb (z->ICR, 0);
}
/*
* Get the message from the target.
* Return the length of the received message.
*/
u_char nca_msg_input (adapter_t *z)
{
u_char sts, msg;
/* Wait for REQ, after which the phase bits will be valid. */
WAITFOR (inb (z->CSBR) & CSBR_REQ, 1000000, "message input");
sts = inb (z->CSBR);
if (! (sts & CSBR_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 ("nca: sending message: invalid phase %s\n",
PHASE_NAME (sts & PHASE_MASK));
return (MSG_ABORT);
}
/* Do actual transfer from SCSI bus to memory. */
outb (z->TCR, PHASE_TO_TCR (PHASE_MSGIN));
msg = nca_recvbyte (z);
PRINT (("nca%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 nca_sense (adapter_t *z, scb_t *scb)
{
u_char cmd[6], status, msg, *data;
u_long len;
/* Wait for target to disconnect. */
WAITFOR (! (inb (z->CSBR) & CSBR_BSY), 100000, "sense bus free");
if (inb (z->CSBR) & CSBR_BSY)
return (0);
/* Select the target again. */
if (! nca_select (z, scb))
return (0);
/* Wait for CMDOUT phase. */
WAITFOR (inb (z->CSBR) & CSBR_REQ, 100000, "sense CMDOUT");
if (! (inb (z->CSBR) & CSBR_REQ) ||
(inb (z->CSBR) & PHASE_MASK) != PHASE_CMDOUT)
return (0);
outb (z->TCR, PHASE_TO_TCR (PHASE_CMDOUT));
/* 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;
nca_cmd_output (z, cmd, sizeof (cmd));
/* Wait for DATAIN phase. */
WAITFOR (inb (z->CSBR) & CSBR_REQ, 100000, "sense DATAIN");
if (! (inb (z->CSBR) & CSBR_REQ) ||
(inb (z->CSBR) & PHASE_MASK) != PHASE_DATAIN)
return (0);
outb (z->TCR, PHASE_TO_TCR (PHASE_DATAIN));
data = (u_char*) &scb->xfer->sense;
nca_data_input (z, &data, &len);
PRINT (("nca%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 (inb (z->CSBR) & CSBR_REQ, 100000, "sense STATIN");
if (! (inb (z->CSBR) & CSBR_REQ) ||
(inb (z->CSBR) & PHASE_MASK) != PHASE_STATIN)
return (0);
outb (z->TCR, PHASE_TO_TCR (PHASE_STATIN));
status = nca_recvbyte (z);
/* Wait for MSGIN phase. */
WAITFOR (inb (z->CSBR) & CSBR_REQ, 100000, "sense MSGIN");
if (! (inb (z->CSBR) & CSBR_REQ) ||
(inb (z->CSBR) & PHASE_MASK) != PHASE_MSGIN)
return (0);
outb (z->TCR, PHASE_TO_TCR (PHASE_MSGIN));
msg = nca_recvbyte (z);
if (status != 0 || msg != 0)
printf ("nca%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 nca_done when task accomplished. Dialog controlled by the target.
* Always called with interrupts disabled.
*/
void nca_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 */
register u_char sts;
u_char msg;
while ((sts = inb (z->CSBR)) & CSBR_BSY) {
/* We only have a valid SCSI phase when REQ is asserted. */
if (! (sts & CSBR_REQ))
continue;
if (inb (z->BSR) & BSR_PARITY_ERROR) {
int target = scb->xfer->sc_link->target;
if (++z->target[target].perrcnt <= 8)
printf ("nca%d/%d/%d parity error\n",
z->sc_link.adapter_unit, target,
scb->xfer->sc_link->lun);
if (z->target[target].perrcnt == 8)
printf ("nca%d/%d/%d too many parity errors, not logging any more\n",
z->sc_link.adapter_unit, target,
scb->xfer->sc_link->lun);
/* Clear parity error. */
inb (z->RPIR);
}
outb (z->TCR, PHASE_TO_TCR (sts & PHASE_MASK));
switch (sts & PHASE_MASK) {
case PHASE_DATAOUT:
if (datalen <= 0) {
printf ("nca%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 */
outb (z->ICR, ICR_ASSERT_DATA_BUS);
nca_sendbyte (z, 0);
outb (z->ICR, 0);
break;
}
nca_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 (("@"));
nca_recvbyte (z);
break;
}
nca_data_input (z, &data, &datalen);
break;
case PHASE_CMDOUT:
nca_cmd_output (z, (u_char*) scb->xfer->cmd,
scb->xfer->cmdlen);
break;
case PHASE_STATIN:
scb->xfer->status = nca_recvbyte (z);
PRINT (("nca%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. */
outb (z->ICR, ICR_ASSERT_DATA_BUS);
nca_sendbyte (z, MSG_NOP);
outb (z->ICR, 0);
PRINT (("nca%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 = nca_recvbyte (z);
PRINT (("nca%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 (nca_sense (z, scb))
scb->flags = SCB_SENSE;
break;
case SCSI_BUSY:
scb->flags = SCB_TBUSY;
break;
}
goto done;
case MSG_ABORT:
printf ("nca: command aborted by target\n");
scb->flags = SCB_ABORTED;
goto done;
case MSG_MESSAGE_REJECT:
printf ("nca: 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 (nca_tick, z, 1);
z->timeout_active = 1;
}
PRINT (("nca%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 ("nca%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 ("nca: unknown phase: %b\n", sts, CSBR_BITS);
break;
}
}
printf ("nca%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);
nca_done (z, scb);
ret:
outb (z->ICR, 0);
outb (z->TCR, 0);
outb (z->SER, z->scsi_id);
WAITFOR (! (inb (z->CSBR) & CSBR_BSY), 100000, "xfer bus free");
}
#endif /* NNCA */