#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: seagate.c,v 1.77 2022/09/25 17:11:48 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/device.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/queue.h>
#include <sys/kmem.h>
#include <sys/intr.h>
#include <machine/pio.h>
#include <dev/scsipi/scsi_all.h>
#include <dev/scsipi/scsipi_all.h>
#include <dev/scsipi/scsi_message.h>
#include <dev/scsipi/scsiconf.h>
#include <dev/isa/isareg.h>
#include <dev/isa/isavar.h>
#define SEA_SCB_MAX 32
#define SCB_TABLE_SIZE 8
#define BLOCK_SIZE 512
#define SEA_BLINDTRANSFER
#define SEA_ASSEMBLER
#undef SEA_NOMSGS
#undef SEA_NODATAOUT
#undef SEA_DEBUGQUEUE
#define CMD_RST 0x01
#define CMD_SEL 0x02
#define CMD_BSY 0x04
#define CMD_ATTN 0x08
#define CMD_START_ARB 0x10
#define CMD_EN_PARITY 0x20
#define CMD_INTR 0x40
#define CMD_DRVR_ENABLE 0x80
#define STAT_BSY 0x01
#define STAT_MSG 0x02
#define STAT_IO 0x04
#define STAT_CD 0x08
#define STAT_REQ 0x10
#define STAT_SEL 0x20
#define STAT_PARITY 0x40
#define STAT_ARB_CMPL 0x80
#define PH_DATAOUT (0)
#define PH_DATAIN (STAT_IO)
#define PH_CMD (STAT_CD)
#define PH_STAT (STAT_CD | STAT_IO)
#define PH_MSGOUT (STAT_MSG | STAT_CD)
#define PH_MSGIN (STAT_MSG | STAT_CD | STAT_IO)
#define PH_MASK (STAT_MSG | STAT_CD | STAT_IO)
#define PH_INVALID 0xff
#define SEA_RAMOFFSET 0x00001800
#define BASE_CMD (CMD_INTR | CMD_EN_PARITY)
#define SEAGATE 1
#define FDOMAIN 2
#define FDOMAIN840 3
struct sea_scb {
u_char *data;
int datalen;
TAILQ_ENTRY(sea_scb) chain;
struct scsipi_xfer *xs;
int flags;
#define SCB_FREE 0
#define SCB_ACTIVE 1
#define SCB_ABORTED 2
#define SCB_TIMEOUT 4
#define SCB_ERROR 8
};
struct sea_softc {
device_t sc_dev;
void *sc_ih;
int type;
void * maddr;
void * maddr_cr_sr;
void * maddr_dr;
struct scsipi_adapter sc_adapter;
struct scsipi_channel sc_channel;
TAILQ_HEAD(, sea_scb) free_list, ready_list, nexus_list;
struct sea_scb *nexus;
int numscbs;
struct sea_scb scb[SCB_TABLE_SIZE];
int our_id;
u_char our_id_mask;
volatile u_char busy[8];
};
static volatile int main_running = 0;
#define STATUS (*(volatile u_char *)sea->maddr_cr_sr)
#define CONTROL STATUS
#define DATA (*(volatile u_char *)sea->maddr_dr)
#define TAG_NEXT -1
#define TAG_NONE -2
typedef struct {
const char *signature;
int offset, length;
int type;
} BiosSignature;
static const BiosSignature signatures[] = {
{"ST01 v1.7 (C) Copyright 1987 Seagate", 15, 37, SEAGATE},
{"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40, SEAGATE},
{"SEAGATE SCSI BIOS ", 16, 17, SEAGATE},
{"SEAGATE SCSI BIOS ", 17, 17, SEAGATE},
{"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 45, FDOMAIN},
{"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FDOMAIN},
{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90",5, 47, FDOMAIN},
{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90",5, 47, FDOMAIN},
{"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FDOMAIN},
{"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FDOMAIN},
{"FUTURE DOMAIN TMC-950", 5, 21, FDOMAIN},
};
#define nsignatures (sizeof(signatures) / sizeof(signatures[0]))
#ifdef notdef
static const char *bases[] = {
(char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000,
(char *) 0xce000, (char *) 0xdc000, (char *) 0xde000
};
#define nbases (sizeof(bases) / sizeof(bases[0]))
#endif
int seaintr(void *);
void sea_scsipi_request(struct scsipi_channel *, scsipi_adapter_req_t, void *);
void sea_timeout(void *);
void sea_done(struct sea_softc *, struct sea_scb *);
struct sea_scb *sea_get_scb(struct sea_softc *, int);
void sea_free_scb(struct sea_softc *, struct sea_scb *, int);
static void sea_main(void);
static void sea_information_transfer(struct sea_softc *);
int sea_poll(struct sea_softc *, struct scsipi_xfer *, int);
void sea_init(struct sea_softc *);
void sea_send_scb(struct sea_softc *sea, struct sea_scb *scb);
void sea_reselect(struct sea_softc *sea);
int sea_select(struct sea_softc *sea, struct sea_scb *scb);
int sea_transfer_pio(struct sea_softc *sea, u_char *phase,
int *count, u_char **data);
int sea_abort(struct sea_softc *, struct sea_scb *scb);
void sea_grow_scb(struct sea_softc *);
int seaprobe(device_t, cfdata_t, void *);
void seaattach(device_t, device_t, void *);
CFATTACH_DECL_NEW(sea, sizeof(struct sea_softc),
seaprobe, seaattach, NULL, NULL);
extern struct cfdriver sea_cd;
#ifdef SEA_DEBUGQUEUE
void
sea_queue_length(struct sea_softc *sea)
{
struct sea_scb *scb;
int connected, issued, disconnected;
connected = sea->nexus ? 1 : 0;
for (scb = sea->ready_list.tqh_first, issued = 0; scb;
scb = scb->chain.tqe_next, issued++);
for (scb = sea->nexus_list.tqh_first, disconnected = 0; scb;
scb = scb->chain.tqe_next, disconnected++);
printf("%s: length: %d/%d/%d\n", device_xname(sea->sc_dev), connected,
issued, disconnected);
}
#endif
int
seaprobe(device_t parent, cfdata_t match, void *aux)
{
struct isa_attach_args *ia = aux;
int i, type = 0;
void *maddr;
if (ia->ia_niomem < 1)
return (0);
if (ia->ia_nirq < 1)
return (0);
if (ISA_DIRECT_CONFIG(ia))
return (0);
if (ia->ia_iomem[0].ir_addr == ISA_UNKNOWN_IOMEM)
return (0);
if (ia->ia_irq[0].ir_irq == ISA_UNKNOWN_IRQ)
return (0);
maddr = ISA_HOLE_VADDR(ia->ia_iomem[0].ir_addr);
for (i = 0; i < nsignatures; i++)
if (!memcmp((char *)maddr + signatures[i].offset,
signatures[i].signature, signatures[i].length)) {
type = signatures[i].type;
break;
}
switch (type) {
case SEAGATE:
case FDOMAIN840:
case FDOMAIN:
break;
default:
#ifdef SEA_DEBUG
printf("seaprobe: board type unknown at address %p\n", maddr);
#endif
return 0;
}
ia->ia_niomem = 1;
ia->ia_iomem[0].ir_size = 0x2000;
ia->ia_nirq = 1;
ia->ia_nio = 0;
ia->ia_ndrq = 0;
return 1;
}
void
seaattach(device_t parent, device_t self, void *aux)
{
struct isa_attach_args *ia = aux;
struct sea_softc *sea = device_private(self);
struct scsipi_adapter *adapt = &sea->sc_adapter;
struct scsipi_channel *chan = &sea->sc_channel;
int i;
aprint_naive("\n");
sea->sc_dev = self;
sea->maddr = ISA_HOLE_VADDR(ia->ia_iomem[0].ir_addr);
for (i = 0; i < nsignatures; i++)
if (!memcmp((char *)sea->maddr + signatures[i].offset,
signatures[i].signature, signatures[i].length)) {
sea->type = signatures[i].type;
break;
}
switch (sea->type) {
case SEAGATE:
case FDOMAIN840:
sea->maddr_cr_sr =
(void *) (((u_char *)sea->maddr) + 0x1a00);
sea->maddr_dr =
(void *) (((u_char *)sea->maddr) + 0x1c00);
break;
case FDOMAIN:
sea->maddr_cr_sr =
(void *) (((u_char *)sea->maddr) + 0x1c00);
sea->maddr_dr =
(void *) (((u_char *)sea->maddr) + 0x1e00);
break;
default:
#ifdef DEBUG
printf("%s: board type unknown at address %p\n",
device_xname(sea->sc_dev), sea->maddr);
#endif
return;
}
*((u_char *)sea->maddr + SEA_RAMOFFSET) = 0xa5;
*((u_char *)sea->maddr + SEA_RAMOFFSET + 1) = 0x5a;
if ((*((u_char *)sea->maddr + SEA_RAMOFFSET) != 0xa5) ||
(*((u_char *)sea->maddr + SEA_RAMOFFSET + 1) != 0x5a)) {
aprint_error_dev(sea->sc_dev, "board RAM failure\n");
return;
}
sea_init(sea);
memset(adapt, 0, sizeof(*adapt));
adapt->adapt_dev = sea->sc_dev;
adapt->adapt_nchannels = 1;
adapt->adapt_openings = sea->numscbs;
adapt->adapt_max_periph = 1;
adapt->adapt_request = sea_scsipi_request;
adapt->adapt_minphys = minphys;
memset(chan, 0, sizeof(*chan));
chan->chan_adapter = adapt;
chan->chan_bustype = &scsi_bustype;
chan->chan_channel = 0;
chan->chan_ntargets = 8;
chan->chan_nluns = 8;
chan->chan_id = sea->our_id;
chan->chan_flags = SCSIPI_CHAN_CANGROW;
aprint_normal("\n");
sea->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq[0].ir_irq,
IST_EDGE, IPL_BIO, seaintr, sea);
config_found(self, &sea->sc_channel, scsiprint, CFARGS_NONE);
}
int
seaintr(void *arg)
{
struct sea_softc *sea = arg;
#ifdef DEBUG
if ((STATUS & STAT_PARITY) == 0 &&
(STATUS & (STAT_SEL | STAT_IO)) != (STAT_SEL | STAT_IO))
return 0;
#endif
loop:
if (STATUS & STAT_PARITY) {
aprint_error_dev(sea->sc_dev, "parity error\n");
return 1;
}
if ((STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) {
sea_reselect(sea);
if (!main_running)
sea_main();
goto loop;
}
return 1;
}
void
sea_init(struct sea_softc *sea)
{
int i;
CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_RST;
delay(25);
CONTROL = BASE_CMD;
delay(10);
switch (sea->type) {
case SEAGATE:
sea->our_id = 7;
break;
case FDOMAIN:
case FDOMAIN840:
sea->our_id = 6;
break;
}
sea->our_id_mask = 1 << sea->our_id;
sea->nexus = 0;
TAILQ_INIT(&sea->ready_list);
TAILQ_INIT(&sea->nexus_list);
TAILQ_INIT(&sea->free_list);
for (i = 0; i < 8; i++)
sea->busy[i] = 0x00;
sea->numscbs = SCB_TABLE_SIZE;
for (i = 0; i < SCB_TABLE_SIZE; i++) {
TAILQ_INSERT_TAIL(&sea->free_list, &sea->scb[i], chain);
}
}
void
sea_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
void *arg)
{
struct scsipi_xfer *xs;
struct scsipi_periph *periph __diagused;
struct sea_softc *sea = device_private(chan->chan_adapter->adapt_dev);
struct sea_scb *scb;
int flags;
int s;
switch (req) {
case ADAPTER_REQ_RUN_XFER:
xs = arg;
periph = xs->xs_periph;
flags = xs->xs_control;
SC_DEBUG(periph, SCSIPI_DB2, ("sea_scsipi_requeset\n"));
if (flags & XS_CTL_RESET) {
printf("%s: resetting\n", device_xname(sea->sc_dev));
xs->error = XS_DRIVER_STUFFUP;
scsipi_done(xs);
return;
}
scb = sea_get_scb(sea, flags);
#ifdef DIAGNOSTIC
if (scb == NULL) {
scsipi_printaddr(periph);
printf("unable to allocate scb\n");
panic("sea_scsipi_request");
}
#endif
scb->flags = SCB_ACTIVE;
scb->xs = xs;
scb->datalen = xs->datalen;
scb->data = xs->data;
#ifdef SEA_DEBUGQUEUE
sea_queue_length(sea);
#endif
s = splbio();
sea_send_scb(sea, scb);
if ((flags & XS_CTL_POLL) == 0) {
callout_reset(&scb->xs->xs_callout,
mstohz(xs->timeout), sea_timeout, scb);
splx(s);
return;
}
splx(s);
if (sea_poll(sea, xs, xs->timeout)) {
sea_timeout(scb);
if (sea_poll(sea, xs, 2000))
sea_timeout(scb);
}
return;
case ADAPTER_REQ_GROW_RESOURCES:
sea_grow_scb(sea);
return;
case ADAPTER_REQ_SET_XFER_MODE:
{
struct scsipi_xfer_mode *xm = arg;
xm->xm_mode = 0;
xm->xm_period = 0;
xm->xm_offset = 0;
scsipi_async_event(chan, ASYNC_EVENT_XFER_MODE, xm);
return;
}
}
}
struct sea_scb *
sea_get_scb(struct sea_softc *sea, int flags)
{
int s;
struct sea_scb *scb;
s = splbio();
if ((scb = TAILQ_FIRST(&sea->free_list)) != NULL)
TAILQ_REMOVE(&sea->free_list, scb, chain);
splx(s);
return (scb);
}
void
sea_send_scb(struct sea_softc *sea, struct sea_scb *scb)
{
TAILQ_INSERT_TAIL(&sea->ready_list, scb, chain);
if (!main_running)
sea_main();
}
void
sea_main(void)
{
struct sea_softc *sea;
struct sea_scb *scb;
int done;
int unit;
int s;
main_running = 1;
loop:
done = 1;
for (unit = 0; unit < sea_cd.cd_ndevs; unit++) {
sea = device_lookup_private(&sea_cd, unit);
if (!sea)
continue;
s = splbio();
if (!sea->nexus) {
for (scb = sea->ready_list.tqh_first; scb;
scb = scb->chain.tqe_next) {
if ((sea->busy[scb->xs->xs_periph->periph_target] &
(1 << scb->xs->xs_periph->periph_lun)))
continue;
TAILQ_REMOVE(&sea->ready_list, scb, chain);
splx(s);
if ((STATUS & (STAT_SEL | STAT_IO)) ==
(STAT_SEL | STAT_IO)) {
sea_reselect(sea);
s = splbio();
break;
}
if (sea_select(sea, scb)) {
s = splbio();
TAILQ_INSERT_HEAD(&sea->ready_list,
scb, chain);
} else {
s = splbio();
break;
}
}
if (!sea->nexus) {
if ((STATUS & (STAT_SEL | STAT_IO)) ==
(STAT_SEL | STAT_IO)) {
sea_reselect(sea);
}
}
}
splx(s);
if (sea->nexus) {
sea_information_transfer(sea);
done = 0;
} else
break;
}
if (!done)
goto loop;
main_running = 0;
}
void
sea_grow_scb(struct sea_softc *sea)
{
struct sea_scb *scb;
if (sea->numscbs == SEA_SCB_MAX) {
sea->sc_channel.chan_flags &= ~SCSIPI_CHAN_CANGROW;
return;
}
scb = kmem_zalloc(sizeof(*scb), KM_NOSLEEP);
if (scb == NULL)
return;
TAILQ_INSERT_TAIL(&sea->free_list, scb, chain);
sea->numscbs++;
sea->sc_adapter.adapt_openings++;
}
void
sea_free_scb(struct sea_softc *sea, struct sea_scb *scb, int flags)
{
int s;
s = splbio();
scb->flags = SCB_FREE;
TAILQ_INSERT_HEAD(&sea->free_list, scb, chain);
splx(s);
}
void
sea_timeout(void *arg)
{
struct sea_scb *scb = arg;
struct scsipi_xfer *xs = scb->xs;
struct scsipi_periph *periph = xs->xs_periph;
struct sea_softc *sea =
device_private(periph->periph_channel->chan_adapter->adapt_dev);
int s;
scsipi_printaddr(periph);
printf("timed out");
s = splbio();
if (scb->flags & SCB_ABORTED) {
printf(" AGAIN\n");
scb->xs->xs_retries = 0;
scb->flags |= SCB_ABORTED;
sea_done(sea, scb);
} else {
printf("\n");
scb->flags |= SCB_ABORTED;
sea_abort(sea, scb);
if ((xs->xs_control & XS_CTL_POLL) == 0)
callout_reset(&scb->xs->xs_callout, 2 * hz,
sea_timeout, scb);
}
splx(s);
}
void
sea_reselect(struct sea_softc *sea)
{
u_char target_mask;
int i;
u_char lun, phase;
u_char msg[3];
int len;
u_char *data;
struct sea_scb *scb;
int abort = 0;
if (!((target_mask = STATUS) & STAT_SEL)) {
printf("%s: wrong state 0x%x\n", device_xname(sea->sc_dev),
target_mask);
return;
}
for (i = 10; i && (STATUS & (STAT_SEL | STAT_IO | STAT_BSY)) !=
(STAT_SEL | STAT_IO | 0); i--);
target_mask = DATA;
if (!(target_mask & sea->our_id_mask)) {
printf("%s: polled reselection was not for me: 0x%x\n",
device_xname(sea->sc_dev), target_mask);
return;
}
target_mask &= ~sea->our_id_mask;
CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY;
for (i = 50000; i && (STATUS & STAT_SEL); i++);
CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
for (i = 50000; i && !(STATUS & STAT_REQ); i--);
len = 3;
data = msg;
phase = PH_MSGIN;
sea_transfer_pio(sea, &phase, &len, &data);
if (!MSG_ISIDENTIFY(msg[0])) {
printf("%s: expecting IDENTIFY message, got 0x%x\n",
device_xname(sea->sc_dev), msg[0]);
abort = 1;
scb = NULL;
} else {
lun = msg[0] & 0x07;
for (scb = sea->nexus_list.tqh_first; scb;
scb = scb->chain.tqe_next)
if (target_mask == (1 << scb->xs->xs_periph->periph_target) &&
lun == scb->xs->xs_periph->periph_lun) {
TAILQ_REMOVE(&sea->nexus_list, scb,
chain);
break;
}
if (!scb) {
printf("%s: target %02x lun %d not disconnected\n",
device_xname(sea->sc_dev), target_mask, lun);
abort = 1;
}
}
if (abort) {
msg[0] = MSG_ABORT;
len = 1;
data = msg;
phase = PH_MSGOUT;
CONTROL = BASE_CMD | CMD_ATTN;
sea_transfer_pio(sea, &phase, &len, &data);
} else
sea->nexus = scb;
return;
}
int
sea_transfer_pio(struct sea_softc *sea, u_char *phase, int *count, u_char **data)
{
u_char p = *phase, tmp;
int c = *count;
u_char *d = *data;
int timeout;
do {
for (timeout = 0; timeout < 50000; timeout++)
if ((tmp = STATUS) & STAT_REQ)
break;
if (!(tmp & STAT_REQ)) {
printf("%s: timeout waiting for STAT_REQ\n",
device_xname(sea->sc_dev));
break;
}
if (sea->type == FDOMAIN840)
tmp = ((tmp & 0x08) >> 2) |
((tmp & 0x02) << 2) |
(tmp & 0xf5);
if ((tmp & PH_MASK) != p)
break;
if (!(p & STAT_IO))
DATA = *d;
else
*d = DATA;
++d;
#if 0
for (timeout = 0; timeout < 200000L; timeout++)
if (!(STATUS & STAT_REQ))
break;
if (STATUS & STAT_REQ)
printf("%s: timeout on wait for !STAT_REQ",
device_xname(sea->sc_dev));
#endif
} while (--c);
*count = c;
*data = d;
tmp = STATUS;
if (tmp & STAT_REQ)
*phase = tmp & PH_MASK;
else
*phase = PH_INVALID;
if (c && (*phase != p))
return -1;
return 0;
}
int
sea_select(struct sea_softc *sea, struct sea_scb *scb)
{
u_char msg[3], phase;
u_char *data;
int len;
int timeout;
CONTROL = BASE_CMD;
DATA = sea->our_id_mask;
CONTROL = (BASE_CMD & ~CMD_INTR) | CMD_START_ARB;
for (timeout = 0; timeout < 3000000L; timeout++)
if (STATUS & STAT_ARB_CMPL)
break;
if (!(STATUS & STAT_ARB_CMPL)) {
if (STATUS & STAT_SEL) {
printf("%s: arbitration lost\n", device_xname(sea->sc_dev));
scb->flags |= SCB_ERROR;
} else {
printf("%s: arbitration timeout\n",
device_xname(sea->sc_dev));
scb->flags |= SCB_TIMEOUT;
}
CONTROL = BASE_CMD;
return -1;
}
delay(2);
DATA = (u_char)((1 << scb->xs->xs_periph->periph_target) |
sea->our_id_mask);
CONTROL =
#ifdef SEA_NOMSGS
(BASE_CMD & ~CMD_INTR) | CMD_DRVR_ENABLE | CMD_SEL;
#else
(BASE_CMD & ~CMD_INTR) | CMD_DRVR_ENABLE | CMD_SEL | CMD_ATTN;
#endif
delay(1);
for (timeout = 0; timeout < 2000000L; timeout++)
if (STATUS & STAT_BSY)
break;
if (!(STATUS & STAT_BSY)) {
CONTROL = BASE_CMD;
scb->flags |= SCB_TIMEOUT;
return 0;
}
#ifdef SEA_NOMSGS
CONTROL = (BASE_CMD & ~CMD_INTR) | CMD_DRVR_ENABLE;
#else
CONTROL = (BASE_CMD & ~CMD_INTR) | CMD_DRVR_ENABLE | CMD_ATTN;
#endif
delay(1);
for (timeout = 0; timeout < 2000000L; timeout++)
if (STATUS & STAT_REQ)
break;
CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
if (!(STATUS & STAT_REQ)) {
} else {
msg[0] = MSG_IDENTIFY(scb->xs->xs_periph->periph_lun, 1);
len = 1;
data = msg;
phase = PH_MSGOUT;
sea_transfer_pio(sea, &phase, &len, &data);
}
if (!(STATUS & STAT_BSY))
printf("%s: after successful arbitrate: no STAT_BSY!\n",
device_xname(sea->sc_dev));
sea->nexus = scb;
sea->busy[scb->xs->xs_periph->periph_target] |=
1 << scb->xs->xs_periph->periph_lun;
CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
return 0;
}
int
sea_abort(struct sea_softc *sea, struct sea_scb *scb)
{
struct sea_scb *tmp;
u_char msg, phase, *msgptr;
int len;
for (tmp = sea->ready_list.tqh_first; tmp; tmp = tmp->chain.tqe_next)
if (scb == tmp) {
TAILQ_REMOVE(&sea->ready_list, scb, chain);
return 1;
}
if (sea->nexus)
return 0;
for (tmp = sea->nexus_list.tqh_first; tmp;
tmp = tmp->chain.tqe_next)
if (scb == tmp) {
if (sea_select(sea, scb))
return 0;
msg = MSG_ABORT;
msgptr = &msg;
len = 1;
phase = PH_MSGOUT;
CONTROL = BASE_CMD | CMD_ATTN;
sea_transfer_pio(sea, &phase, &len, &msgptr);
for (tmp = sea->nexus_list.tqh_first; tmp;
tmp = tmp->chain.tqe_next)
if (scb == tmp) {
TAILQ_REMOVE(&sea->nexus_list,
scb, chain);
return 1;
}
}
return 1;
}
void
sea_done(struct sea_softc *sea, struct sea_scb *scb)
{
struct scsipi_xfer *xs = scb->xs;
callout_stop(&scb->xs->xs_callout);
xs->resid = scb->datalen;
if (scb->flags == SCB_ACTIVE) {
xs->resid = 0;
} else {
if (scb->flags & (SCB_TIMEOUT | SCB_ABORTED))
xs->error = XS_TIMEOUT;
if (scb->flags & SCB_ERROR)
xs->error = XS_DRIVER_STUFFUP;
}
sea_free_scb(sea, scb, xs->xs_control);
scsipi_done(xs);
}
int
sea_poll(struct sea_softc *sea, struct scsipi_xfer *xs, int count)
{
int s;
while (count) {
s = splbio();
if (!main_running)
sea_main();
splx(s);
if (xs->xs_status & XS_STS_DONE)
return 0;
delay(1000);
count--;
}
return 1;
}
void
sea_information_transfer(struct sea_softc *sea)
{
int timeout;
u_char msgout = MSG_NOOP;
int len;
int s;
u_char *data;
u_char phase, tmp, old_phase = PH_INVALID;
struct sea_scb *scb = sea->nexus;
int loop;
for (timeout = 0; timeout < 10000000L; timeout++) {
tmp = STATUS;
if (tmp & STAT_PARITY)
printf("%s: parity error detected\n",
device_xname(sea->sc_dev));
if (!(tmp & STAT_BSY)) {
for (loop = 0; loop < 20; loop++)
if ((tmp = STATUS) & STAT_BSY)
break;
if (!(tmp & STAT_BSY)) {
printf("%s: !STAT_BSY unit in data transfer!\n",
device_xname(sea->sc_dev));
s = splbio();
sea->nexus = NULL;
scb->flags = SCB_ERROR;
splx(s);
sea_done(sea, scb);
return;
}
}
if (!(tmp & STAT_REQ))
continue;
if (sea->type == FDOMAIN840)
tmp = ((tmp & 0x08) >> 2) |
((tmp & 0x02) << 2) |
(tmp & 0xf5);
phase = tmp & PH_MASK;
if (phase != old_phase)
old_phase = phase;
switch (phase) {
case PH_DATAOUT:
#ifdef SEA_NODATAOUT
printf("%s: SEA_NODATAOUT set, attempted DATAOUT aborted\n",
device_xname(sea->sc_dev));
msgout = MSG_ABORT;
CONTROL = BASE_CMD | CMD_ATTN;
break;
#endif
case PH_DATAIN:
if (!scb->data)
printf("no data address!\n");
#ifdef SEA_BLINDTRANSFER
if (scb->datalen && !(scb->datalen % BLOCK_SIZE)) {
while (scb->datalen) {
for (loop = 0; loop < 50000; loop++)
if ((tmp = STATUS) & STAT_REQ)
break;
if (!(tmp & STAT_REQ)) {
printf("%s: timeout waiting for STAT_REQ\n",
device_xname(sea->sc_dev));
}
if (sea->type == FDOMAIN840)
tmp = ((tmp & 0x08) >> 2) |
((tmp & 0x02) << 2) |
(tmp & 0xf5);
if ((tmp & PH_MASK) != phase)
break;
if (!(phase & STAT_IO)) {
#ifdef SEA_ASSEMBLER
void *junk;
__asm("cld\n\t\
rep\n\t\
movsl" :
"=S" (scb->data),
"=c" (len),
"=D" (junk) :
"0" (scb->data),
"1" (BLOCK_SIZE >> 2),
"2" (sea->maddr_dr));
#else
for (len = BLOCK_SIZE;
len; len--)
DATA = *(scb->data++);
#endif
} else {
#ifdef SEA_ASSEMBLER
void *junk;
__asm("cld\n\t\
rep\n\t\
movsl" :
"=D" (scb->data),
"=c" (len),
"=S" (junk) :
"0" (scb->data),
"1" (BLOCK_SIZE >> 2),
"2" (sea->maddr_dr));
#else
for (len = BLOCK_SIZE;
len; len--)
*(scb->data++) = DATA;
#endif
}
scb->datalen -= BLOCK_SIZE;
}
}
#endif
if (scb->datalen)
sea_transfer_pio(sea, &phase, &scb->datalen,
&scb->data);
break;
case PH_MSGIN:
len = 1;
data = &tmp;
sea_transfer_pio(sea, &phase, &len, &data);
switch (tmp) {
case MSG_ABORT:
scb->flags = SCB_ABORTED;
printf("sea: command aborted by target\n");
CONTROL = BASE_CMD;
sea_done(sea, scb);
return;
case MSG_CMDCOMPLETE:
s = splbio();
sea->nexus = NULL;
splx(s);
sea->busy[scb->xs->xs_periph->periph_target] &=
~(1 << scb->xs->xs_periph->periph_lun);
CONTROL = BASE_CMD;
sea_done(sea, scb);
return;
case MSG_MESSAGE_REJECT:
printf("%s: message_reject received\n",
device_xname(sea->sc_dev));
break;
case MSG_DISCONNECT:
s = splbio();
TAILQ_INSERT_TAIL(&sea->nexus_list,
scb, chain);
sea->nexus = NULL;
CONTROL = BASE_CMD;
splx(s);
return;
case MSG_SAVEDATAPOINTER:
case MSG_RESTOREPOINTERS:
break;
default:
printf("%s: unknown message in: %x\n",
device_xname(sea->sc_dev), tmp);
break;
}
break;
case PH_MSGOUT:
len = 1;
data = &msgout;
sea_transfer_pio(sea, &phase, &len, &data);
if (msgout == MSG_ABORT) {
printf("%s: sent message abort to target\n",
device_xname(sea->sc_dev));
s = splbio();
sea->busy[scb->xs->xs_periph->periph_target] &=
~(1 << scb->xs->xs_periph->periph_lun);
sea->nexus = NULL;
scb->flags = SCB_ABORTED;
splx(s);
sea_done(sea, scb);
return;
}
msgout = MSG_NOOP;
break;
case PH_CMD:
len = scb->xs->cmdlen;
data = (char *) scb->xs->cmd;
sea_transfer_pio(sea, &phase, &len, &data);
break;
case PH_STAT:
len = 1;
data = &tmp;
sea_transfer_pio(sea, &phase, &len, &data);
scb->xs->status = tmp;
break;
default:
printf("sea: unknown phase\n");
}
}
printf("%s: timeout in data transfer\n", device_xname(sea->sc_dev));
scb->flags = SCB_TIMEOUT;
sea_done(sea, scb);
}