07b064e02e
Some of the stuff (e.g., rarpd, bootpd, dhcpd etc., libsa) still will only support Ethernet. Tcpdump itself should be ok, but libpcap needs lot of work. For the detailed change history, look at the commit log entries for the is-newarp branch.
1948 lines
45 KiB
C
1948 lines
45 KiB
C
/* $NetBSD: ncr5380.c,v 1.11 1997/03/15 18:10:18 is Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1995 Leo Weppelman.
|
|
* All rights reserved.
|
|
*
|
|
* 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.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Leo Weppelman.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
|
*/
|
|
|
|
|
|
#ifdef DBG_NOSTATIC
|
|
# define static
|
|
#endif
|
|
#ifdef DBG_SEL
|
|
# define DBG_SELPRINT(a,b) printf(a,b)
|
|
#else
|
|
# define DBG_SELPRINT(a,b)
|
|
#endif
|
|
#ifdef DBG_PIO
|
|
# define DBG_PIOPRINT(a,b,c) printf(a,b,c)
|
|
#else
|
|
# define DBG_PIOPRINT(a,b,c)
|
|
#endif
|
|
#ifdef DBG_INF
|
|
# define DBG_INFPRINT(a,b,c) a(b,c)
|
|
#else
|
|
# define DBG_INFPRINT(a,b,c)
|
|
#endif
|
|
#ifdef DBG_PID
|
|
static char *last_hit = NULL, *olast_hit = NULL;
|
|
# define PID(a) olast_hit = last_hit; last_hit = a;
|
|
#else
|
|
# define PID(a)
|
|
#endif
|
|
|
|
/*
|
|
* Bit mask of targets you want debugging to be shown
|
|
*/
|
|
u_char dbg_target_mask = 0x7f;
|
|
|
|
/*
|
|
* Set bit for target when parity checking must be disabled.
|
|
* My (LWP) Maxtor 7245S seems to generate parity errors on about 50%
|
|
* of all transfers while the data is correct!?
|
|
*/
|
|
u_char ncr5380_no_parchk = 0xff;
|
|
|
|
/*
|
|
* This is the default sense-command we send.
|
|
*/
|
|
static u_char sense_cmd[] = {
|
|
REQUEST_SENSE, 0, 0, 0, sizeof(struct scsi_sense_data), 0
|
|
};
|
|
|
|
/*
|
|
* True if the main co-routine is running
|
|
*/
|
|
static volatile int main_running = 0;
|
|
|
|
/*
|
|
* Mask of targets selected
|
|
*/
|
|
static u_char busy;
|
|
|
|
static void ncr5380_minphys(struct buf *bp);
|
|
static int ncr5380_scsi_cmd(struct scsi_xfer *xs);
|
|
static int ncr5380_show_scsi_cmd(struct scsi_xfer *xs);
|
|
|
|
struct scsi_adapter ncr5380_switch = {
|
|
ncr5380_scsi_cmd, /* scsi_cmd() */
|
|
ncr5380_minphys, /* scsi_minphys() */
|
|
0, /* open_target_lu() */
|
|
0 /* close_target_lu() */
|
|
};
|
|
|
|
struct scsi_device ncr5380_dev = {
|
|
NULL, /* use default error handler */
|
|
NULL, /* do not have a start functio */
|
|
NULL, /* have no async handler */
|
|
NULL /* Use default done routine */
|
|
};
|
|
|
|
|
|
static SC_REQ req_queue[NREQ];
|
|
static SC_REQ *free_head = NULL; /* Free request structures */
|
|
|
|
|
|
/*
|
|
* Inline functions:
|
|
*/
|
|
|
|
/*
|
|
* Determine the size of a SCSI command.
|
|
*/
|
|
extern __inline__ int command_size(opcode)
|
|
u_char opcode;
|
|
{
|
|
switch ((opcode >> 4) & 0xf) {
|
|
case 0:
|
|
case 1:
|
|
return (6);
|
|
case 2:
|
|
case 3:
|
|
return (10);
|
|
}
|
|
return (12);
|
|
}
|
|
|
|
|
|
/*
|
|
* Wait for request-line to become active. When it doesn't return 0.
|
|
* Otherwise return != 0.
|
|
* The timeouts in the 'wait_req_*' functions are arbitrary and rather
|
|
* large. In 99% of the invocations nearly no timeout is needed but in
|
|
* some cases (especially when using my tapedrive, a Tandberg 3600) the
|
|
* device is busy internally and the first SCSI-phase will be delayed.
|
|
*/
|
|
extern __inline__ int wait_req_true(void)
|
|
{
|
|
int timeout = 25000;
|
|
|
|
while (!(GET_5380_REG(NCR5380_IDSTAT) & SC_S_REQ) && --timeout)
|
|
delay(1);
|
|
return (GET_5380_REG(NCR5380_IDSTAT) & SC_S_REQ);
|
|
}
|
|
|
|
/*
|
|
* Wait for request-line to become inactive. When it doesn't return 0.
|
|
* Otherwise return != 0.
|
|
*/
|
|
extern __inline__ int wait_req_false(void)
|
|
{
|
|
int timeout = 25000;
|
|
|
|
while ((GET_5380_REG(NCR5380_IDSTAT) & SC_S_REQ) && --timeout)
|
|
delay(1);
|
|
return (!(GET_5380_REG(NCR5380_IDSTAT) & SC_S_REQ));
|
|
}
|
|
|
|
extern __inline__ void ack_message()
|
|
{
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
}
|
|
|
|
extern __inline__ void nack_message(SC_REQ *reqp, u_char msg)
|
|
{
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_ATN);
|
|
reqp->msgout = msg;
|
|
}
|
|
|
|
extern __inline__ void finish_req(SC_REQ *reqp)
|
|
{
|
|
int sps;
|
|
struct scsi_xfer *xs = reqp->xs;
|
|
|
|
#ifdef REAL_DMA
|
|
/*
|
|
* If we bounced, free the bounce buffer
|
|
*/
|
|
if (reqp->dr_flag & DRIVER_BOUNCING)
|
|
free_bounceb(reqp->bounceb);
|
|
#endif /* REAL_DMA */
|
|
#ifdef DBG_REQ
|
|
if (dbg_target_mask & (1 << reqp->targ_id))
|
|
show_request(reqp, "DONE");
|
|
#endif
|
|
#ifdef DBG_ERR_RET
|
|
if (reqp->xs->error != 0)
|
|
show_request(reqp, "ERR_RET");
|
|
#endif
|
|
|
|
/*
|
|
* Return request to free-q
|
|
*/
|
|
sps = splbio();
|
|
reqp->next = free_head;
|
|
free_head = reqp;
|
|
splx(sps);
|
|
|
|
xs->flags |= ITSDONE;
|
|
scsi_done(xs);
|
|
}
|
|
|
|
/*
|
|
* Auto config stuff....
|
|
*/
|
|
int ncr_cprint __P((void *auxp, const char *));
|
|
void ncr_attach __P((struct device *, struct device *, void *));
|
|
int ncr_match __P((struct device *, struct cfdata *, void *));
|
|
|
|
/*
|
|
* Tricks to make driver-name configurable
|
|
*/
|
|
#define CFNAME(n) __CONCAT(n,cd)
|
|
#define CFSTRING(n) __STRING(n)
|
|
|
|
struct cfdriver CFNAME(DRNAME) = {
|
|
NULL, CFSTRING(DRNAME), (cfmatch_t)ncr_match, ncr_attach,
|
|
DV_DULL, sizeof(struct ncr_softc), NULL, 0 };
|
|
|
|
int
|
|
ncr_match(pdp, cdp, auxp)
|
|
struct device *pdp;
|
|
struct cfdata *cdp;
|
|
void *auxp;
|
|
{
|
|
return (machine_match(pdp, cdp, auxp, &CFNAME(DRNAME)));
|
|
}
|
|
|
|
void
|
|
ncr_attach(pdp, dp, auxp)
|
|
struct device *pdp, *dp;
|
|
void *auxp;
|
|
{
|
|
struct ncr_softc *sc;
|
|
int i;
|
|
|
|
sc = (struct ncr_softc *)dp;
|
|
|
|
sc->sc_link.adapter_softc = sc;
|
|
sc->sc_link.adapter_target = 7;
|
|
sc->sc_link.adapter = &ncr5380_switch;
|
|
sc->sc_link.device = &ncr5380_dev;
|
|
sc->sc_link.openings = NREQ - 1;
|
|
|
|
/*
|
|
* bitmasks
|
|
*/
|
|
sc->sc_noselatn = 0;
|
|
sc->sc_selected = 0;
|
|
|
|
/*
|
|
* Initialize machine-type specific things...
|
|
*/
|
|
scsi_mach_init(sc);
|
|
printf("\n");
|
|
|
|
/*
|
|
* Initialize request queue freelist.
|
|
*/
|
|
for (i = 0; i < NREQ; i++) {
|
|
req_queue[i].next = free_head;
|
|
free_head = &req_queue[i];
|
|
}
|
|
|
|
/*
|
|
* Initialize the host adapter
|
|
*/
|
|
scsi_idisable();
|
|
ENABLE_NCR5380(sc);
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
SET_5380_REG(NCR5380_MODE, IMODE_BASE);
|
|
SET_5380_REG(NCR5380_TCOM, 0);
|
|
SET_5380_REG(NCR5380_IDSTAT, 0);
|
|
scsi_ienable();
|
|
|
|
/*
|
|
* attach all scsi units on us
|
|
*/
|
|
config_found(dp, &sc->sc_link, ncr_cprint);
|
|
}
|
|
|
|
/*
|
|
* print diag if name is NULL else just extra
|
|
*/
|
|
int
|
|
ncr_cprint(auxp, name)
|
|
void *auxp;
|
|
const char *name;
|
|
{
|
|
if (name == NULL)
|
|
return (UNCONF);
|
|
return (QUIET);
|
|
}
|
|
/*
|
|
* End of auto config stuff....
|
|
*/
|
|
|
|
/*
|
|
* Carry out a request from the high level driver.
|
|
*/
|
|
static int
|
|
ncr5380_scsi_cmd(struct scsi_xfer *xs)
|
|
{
|
|
int sps;
|
|
SC_REQ *reqp;
|
|
int flags = xs->flags;
|
|
|
|
/*
|
|
* We do not queue RESET commands
|
|
*/
|
|
if (flags & SCSI_RESET) {
|
|
scsi_reset(xs->sc_link->adapter_softc);
|
|
return (COMPLETE);
|
|
}
|
|
|
|
/*
|
|
* Get a request block
|
|
*/
|
|
sps = splbio();
|
|
if ((reqp = free_head) == 0) {
|
|
splx(sps);
|
|
return (TRY_AGAIN_LATER);
|
|
}
|
|
free_head = reqp->next;
|
|
reqp->next = NULL;
|
|
splx(sps);
|
|
|
|
/*
|
|
* Initialize our private fields
|
|
*/
|
|
reqp->dr_flag = (flags & SCSI_POLL) ? DRIVER_NOINT : 0;
|
|
reqp->phase = NR_PHASE;
|
|
reqp->msgout = MSG_NOOP;
|
|
reqp->status = SCSGOOD;
|
|
reqp->link = NULL;
|
|
reqp->xs = xs;
|
|
reqp->targ_id = xs->sc_link->target;
|
|
reqp->targ_lun = xs->sc_link->lun;
|
|
reqp->xdata_ptr = (u_char*)xs->data;
|
|
reqp->xdata_len = xs->datalen;
|
|
bcopy(xs->cmd, &reqp->xcmd, sizeof(struct scsi_generic));
|
|
reqp->xcmd.bytes[0] |= reqp->targ_lun << 5;
|
|
|
|
/*
|
|
* Sanity check on flags...
|
|
*/
|
|
if (flags & ITSDONE) {
|
|
ncr_tprint(reqp, "scsi_cmd: command already done.....\n");
|
|
xs->flags &= ~ITSDONE;
|
|
}
|
|
if (!(flags & INUSE)) {
|
|
ncr_tprint(reqp, "scsi_cmd: command not in use.....\n");
|
|
xs->flags |= ~INUSE;
|
|
}
|
|
|
|
#ifdef REAL_DMA
|
|
/*
|
|
* Check if DMA can be used on this request
|
|
*/
|
|
if (scsi_dmaok(reqp))
|
|
reqp->dr_flag |= DRIVER_DMAOK;
|
|
#endif /* REAL_DMA */
|
|
|
|
/*
|
|
* Insert the command into the issue queue. Note that 'REQUEST SENSE'
|
|
* commands are inserted at the head of the queue since any command
|
|
* will clear the existing contingent allegience condition and the sense
|
|
* data is only valid while the condition exists.
|
|
* When possible, link the command to a previous command to the same
|
|
* target. This is not very sensible when AUTO_SENSE is not defined!
|
|
* Interrupts are disabled while we are fiddling with the issue-queue.
|
|
*/
|
|
sps = splbio();
|
|
if ((issue_q == NULL) || (reqp->xcmd.opcode == REQUEST_SENSE)) {
|
|
reqp->next = issue_q;
|
|
issue_q = reqp;
|
|
}
|
|
else {
|
|
SC_REQ *tmp, *link;
|
|
|
|
tmp = issue_q;
|
|
link = NULL;
|
|
do {
|
|
if (!link && (tmp->targ_id == reqp->targ_id) && !tmp->link)
|
|
link = tmp;
|
|
} while (tmp->next && (tmp = tmp->next));
|
|
tmp->next = reqp;
|
|
#ifdef AUTO_SENSE
|
|
if (link) {
|
|
link->link = reqp;
|
|
link->xcmd.bytes[link->xs->cmdlen - 2] |= 1;
|
|
}
|
|
#endif
|
|
}
|
|
splx(sps);
|
|
|
|
#ifdef DBG_REQ
|
|
if (dbg_target_mask & (1 << reqp->targ_id))
|
|
show_request(reqp, (reqp->xcmd.opcode == REQUEST_SENSE) ?
|
|
"HEAD":"TAIL");
|
|
#endif
|
|
|
|
run_main(xs->sc_link->adapter_softc);
|
|
|
|
if (xs->flags & (SCSI_POLL|ITSDONE))
|
|
return (COMPLETE); /* We're booting or run_main has completed */
|
|
return (SUCCESSFULLY_QUEUED);
|
|
}
|
|
|
|
static void
|
|
ncr5380_minphys(struct buf *bp)
|
|
{
|
|
if (bp->b_bcount > MIN_PHYS)
|
|
bp->b_bcount = MIN_PHYS;
|
|
minphys(bp);
|
|
}
|
|
#undef MIN_PHYS
|
|
|
|
static int
|
|
ncr5380_show_scsi_cmd(struct scsi_xfer *xs)
|
|
{
|
|
u_char *b = (u_char *) xs->cmd;
|
|
int i = 0;
|
|
|
|
if (!(xs->flags & SCSI_RESET)) {
|
|
printf("(%d:%d:%d,0x%x)-", xs->sc_link->scsibus,
|
|
xs->sc_link->target, xs->sc_link->lun, xs->sc_link->flags);
|
|
while (i < xs->cmdlen) {
|
|
if (i)
|
|
printf(",");
|
|
printf("%x",b[i++]);
|
|
}
|
|
printf("-\n");
|
|
}
|
|
else {
|
|
printf("(%d:%d:%d)-RESET-\n",
|
|
xs->sc_link->scsibus,xs->sc_link->target, xs->sc_link->lun);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* The body of the driver.
|
|
*/
|
|
static void
|
|
scsi_main(sc)
|
|
struct ncr_softc *sc;
|
|
{
|
|
SC_REQ *req, *prev;
|
|
int itype;
|
|
int sps;
|
|
|
|
/*
|
|
* While running in the driver SCSI-interrupts are disabled.
|
|
*/
|
|
scsi_idisable();
|
|
ENABLE_NCR5380(sc);
|
|
|
|
PID("scsi_main1");
|
|
for (;;) {
|
|
sps = splbio();
|
|
if (!connected) {
|
|
/*
|
|
* Check if it is fair keep any exclusive access to DMA
|
|
* claimed. If not, stop queueing new jobs so the discon_q
|
|
* will be eventually drained and DMA can be given up.
|
|
*/
|
|
if (!fair_to_keep_dma())
|
|
goto main_exit;
|
|
|
|
/*
|
|
* Search through the issue-queue for a command
|
|
* destined for a target that isn't busy.
|
|
*/
|
|
prev = NULL;
|
|
for (req=issue_q; req != NULL; prev = req, req = req->next) {
|
|
if (!(busy & (1 << req->targ_id))) {
|
|
/*
|
|
* Found one, remove it from the issue queue
|
|
*/
|
|
if (prev == NULL)
|
|
issue_q = req->next;
|
|
else prev->next = req->next;
|
|
req->next = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When a request has just ended, we get here before an other
|
|
* device detects that the bus is free and that it can
|
|
* reconnect. The problem is that when this happens, we always
|
|
* baffle the device because our (initiator) id is higher. This
|
|
* can cause a sort of starvation on slow devices. So we check
|
|
* for a pending reselection here.
|
|
* Note that 'connected' will be non-null if the reselection
|
|
* succeeds.
|
|
*/
|
|
if ((GET_5380_REG(NCR5380_IDSTAT) & (SC_S_SEL|SC_S_IO))
|
|
== (SC_S_SEL|SC_S_IO)){
|
|
if (req != NULL) {
|
|
req->next = issue_q;
|
|
issue_q = req;
|
|
}
|
|
splx(sps);
|
|
|
|
reselect(sc);
|
|
scsi_clr_ipend();
|
|
goto connected;
|
|
}
|
|
|
|
/*
|
|
* The host is not connected and there is no request
|
|
* pending, exit.
|
|
*/
|
|
if (req == NULL) {
|
|
PID("scsi_main2");
|
|
goto main_exit;
|
|
}
|
|
|
|
/*
|
|
* Re-enable interrupts before handling the request.
|
|
*/
|
|
splx(sps);
|
|
|
|
#ifdef DBG_REQ
|
|
if (dbg_target_mask & (1 << req->targ_id))
|
|
show_request(req, "TARGET");
|
|
#endif
|
|
/*
|
|
* We found a request. Try to connect to the target. If the
|
|
* initiator fails arbitration, the command is put back in the
|
|
* issue queue.
|
|
*/
|
|
if (scsi_select(req, 0)) {
|
|
sps = splbio();
|
|
req->next = issue_q;
|
|
issue_q = req;
|
|
splx(sps);
|
|
#ifdef DBG_REQ
|
|
if (dbg_target_mask & (1 << req->targ_id))
|
|
ncr_tprint(req, "Select failed\n");
|
|
#endif
|
|
}
|
|
}
|
|
else splx(sps);
|
|
connected:
|
|
if (connected) {
|
|
#ifdef REAL_DMA
|
|
/*
|
|
* If the host is currently connected but a 'real-dma' transfer
|
|
* is in progress, the 'end-of-dma' interrupt restarts main.
|
|
* So quit.
|
|
*/
|
|
sps = splbio();
|
|
if (connected && (connected->dr_flag & DRIVER_IN_DMA)) {
|
|
PID("scsi_main3");
|
|
goto main_exit;
|
|
}
|
|
splx(sps);
|
|
#endif /* REAL_DMA */
|
|
|
|
/*
|
|
* Let the target guide us through the bus-phases
|
|
*/
|
|
while (information_transfer() == -1)
|
|
;
|
|
}
|
|
}
|
|
/* NEVER TO REACH HERE */
|
|
panic("ncr5380-SCSI: not designed to come here");
|
|
|
|
main_exit:
|
|
/*
|
|
* We enter here with interrupts disabled. We are about to exit main
|
|
* so interrupts should be re-enabled. Because interrupts are edge
|
|
* triggered, we could already have missed the interrupt. Therefore
|
|
* we check the IRQ-line here and re-enter when we really missed a
|
|
* valid interrupt.
|
|
*/
|
|
PID("scsi_main4");
|
|
scsi_ienable();
|
|
SET_5380_REG(NCR5380_IDSTAT, SC_HOST_ID);
|
|
if (GET_5380_REG(NCR5380_DMSTAT) & SC_IRQ_SET) {
|
|
if ((itype = check_intr(sc)) != INTR_SPURIOUS) {
|
|
scsi_idisable();
|
|
splx(sps);
|
|
|
|
if (itype == INTR_RESEL)
|
|
reselect(sc);
|
|
#ifdef REAL_DMA
|
|
else dma_ready();
|
|
#else
|
|
else {
|
|
if (pdma_ready())
|
|
goto connected;
|
|
panic("Got DMA interrupt without DMA");
|
|
}
|
|
#endif
|
|
scsi_clr_ipend();
|
|
goto connected;
|
|
}
|
|
}
|
|
reconsider_dma();
|
|
|
|
main_running = 0;
|
|
splx(sps);
|
|
PID("scsi_main5");
|
|
}
|
|
|
|
#ifdef REAL_DMA
|
|
/*
|
|
* The SCSI-DMA interrupt.
|
|
* This interrupt can only be triggered when running in non-polled DMA
|
|
* mode. When DMA is not active, it will be silently ignored, it is usually
|
|
* to late because the EOP interrupt of the controller happens just a tiny
|
|
* bit earlier. It might become usefull when scatter/gather is implemented,
|
|
* because in that case only part of the DATAIN/DATAOUT transfer is taken
|
|
* out of a single buffer.
|
|
*/
|
|
static void
|
|
ncr_dma_intr(sc)
|
|
struct ncr_softc *sc;
|
|
{
|
|
SC_REQ *reqp;
|
|
int dma_done;
|
|
|
|
PID("ncr_dma_intr");
|
|
if ((reqp = connected) && (reqp->dr_flag & DRIVER_IN_DMA)) {
|
|
scsi_idisable();
|
|
if (!(dma_done = dma_ready())) {
|
|
transfer_dma(reqp, reqp->phase, 0);
|
|
return;
|
|
}
|
|
run_main(sc);
|
|
}
|
|
}
|
|
#endif /* REAL_DMA */
|
|
|
|
/*
|
|
* The SCSI-controller interrupt. This interrupt occurs on reselections and
|
|
* at the end of non-polled DMA-interrupts. It is assumed to be called from
|
|
* the machine-dependent hardware interrupt.
|
|
*/
|
|
static void
|
|
ncr_ctrl_intr(sc)
|
|
struct ncr_softc *sc;
|
|
{
|
|
int itype;
|
|
int dma_done;
|
|
|
|
while (GET_5380_REG(NCR5380_DMSTAT) & SC_IRQ_SET) {
|
|
scsi_idisable();
|
|
if ((itype = check_intr(sc)) != INTR_SPURIOUS) {
|
|
if (itype == INTR_RESEL)
|
|
reselect(sc);
|
|
else {
|
|
#ifdef REAL_DMA
|
|
if (!(dma_done = dma_ready())) {
|
|
transfer_dma(connected, connected->phase, 0);
|
|
return;
|
|
}
|
|
#else
|
|
if (pdma_ready())
|
|
return;
|
|
panic("Got DMA interrupt without DMA\n");
|
|
#endif
|
|
}
|
|
scsi_clr_ipend();
|
|
}
|
|
run_main(sc);
|
|
return;
|
|
}
|
|
PID("ncr_ctrl_intr1");
|
|
}
|
|
|
|
/*
|
|
* Initiate a connection path between the host and the target. The function
|
|
* first goes into arbitration for the SCSI-bus. When this succeeds, the target
|
|
* is selected and an 'IDENTIFY' message is send.
|
|
* Returns -1 when the arbitration failed. Otherwise 0 is returned. When
|
|
* the target does not respond (to either selection or 'MESSAGE OUT') the
|
|
* 'done' function is executed.
|
|
* The result code given by the driver can be influenced by setting 'code'
|
|
* to a non-zero value. This is the case when 'select' is called by abort.
|
|
*/
|
|
static int
|
|
scsi_select(reqp, code)
|
|
SC_REQ *reqp;
|
|
{
|
|
u_long timeout;
|
|
u_char tmp[1];
|
|
u_char phase;
|
|
u_long cnt;
|
|
int sps;
|
|
u_int8_t atn_flag;
|
|
u_int8_t targ_bit;
|
|
struct ncr_softc *sc;
|
|
|
|
sc = reqp->xs->sc_link->adapter_softc;
|
|
DBG_SELPRINT ("Starting arbitration\n", 0);
|
|
PID("scsi_select1");
|
|
|
|
sps = splbio();
|
|
|
|
/*
|
|
* Prevent a race condition here. If a reslection interrupt occurred
|
|
* between the decision to pick a new request and the call to select,
|
|
* we abort the selection.
|
|
* Interrupts are lowered when the 5380 is setup to arbitrate for the
|
|
* bus.
|
|
*/
|
|
if (connected) {
|
|
splx(sps);
|
|
PID("scsi_select2");
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Set phase bits to 0, otherwise the 5380 won't drive the bus during
|
|
* selection.
|
|
*/
|
|
SET_5380_REG(NCR5380_TCOM, 0);
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
|
|
/*
|
|
* Arbitrate for the bus.
|
|
*/
|
|
SET_5380_REG(NCR5380_DATA, SC_HOST_ID);
|
|
SET_5380_REG(NCR5380_MODE, SC_ARBIT);
|
|
|
|
splx(sps);
|
|
|
|
cnt = 10;
|
|
while (!(GET_5380_REG(NCR5380_ICOM) & SC_AIP) && --cnt)
|
|
delay(1);
|
|
|
|
if (!(GET_5380_REG(NCR5380_ICOM) & SC_AIP)) {
|
|
SET_5380_REG(NCR5380_MODE, IMODE_BASE);
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
DBG_SELPRINT ("Arbitration lost, bus not free\n",0);
|
|
PID("scsi_select3");
|
|
return (-1);
|
|
}
|
|
|
|
/* The arbitration delay is 2.2 usecs */
|
|
delay(3);
|
|
|
|
/*
|
|
* Check the result of the arbitration. If we failed, return -1.
|
|
*/
|
|
if (GET_5380_REG(NCR5380_ICOM) & SC_LA) {
|
|
SET_5380_REG(NCR5380_MODE, IMODE_BASE);
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
PID("scsi_select4");
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* The spec requires that we should read the data register to
|
|
* check for higher id's and check the SC_LA again.
|
|
*/
|
|
tmp[0] = GET_5380_REG(NCR5380_DATA);
|
|
if (tmp[0] & ~((SC_HOST_ID << 1) - 1)) {
|
|
SET_5380_REG(NCR5380_MODE, IMODE_BASE);
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
DBG_SELPRINT ("Arbitration lost, higher id present\n",0);
|
|
PID("scsi_select5");
|
|
return (-1);
|
|
}
|
|
if (GET_5380_REG(NCR5380_ICOM) & SC_LA) {
|
|
SET_5380_REG(NCR5380_MODE, IMODE_BASE);
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
DBG_SELPRINT ("Arbitration lost, deassert SC_ARBIT\n",0);
|
|
PID("scsi_select6");
|
|
return (-1);
|
|
}
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_SEL | SC_A_BSY);
|
|
if (GET_5380_REG(NCR5380_ICOM) & SC_LA) {
|
|
SET_5380_REG(NCR5380_MODE, IMODE_BASE);
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
DBG_SELPRINT ("Arbitration lost, deassert SC_A_SEL\n", 0);
|
|
PID("scsi_select7");
|
|
return (-1);
|
|
}
|
|
/* Bus settle delay + Bus clear delay = 1.2 usecs */
|
|
delay(2);
|
|
DBG_SELPRINT ("Arbitration complete\n", 0);
|
|
|
|
/*
|
|
* Now that we won the arbitration, start the selection.
|
|
*/
|
|
targ_bit = 1 << reqp->targ_id;
|
|
SET_5380_REG(NCR5380_DATA, SC_HOST_ID | targ_bit);
|
|
|
|
if (sc->sc_noselatn & targ_bit)
|
|
atn_flag = 0;
|
|
else
|
|
atn_flag = SC_A_ATN;
|
|
|
|
/*
|
|
* Raise ATN while SEL is true before BSY goes false from arbitration,
|
|
* since this is the only way to guarantee that we'll get a MESSAGE OUT
|
|
* phase immediately after the selection.
|
|
*/
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_BSY | SC_A_SEL | atn_flag | SC_ADTB);
|
|
SET_5380_REG(NCR5380_MODE, IMODE_BASE);
|
|
|
|
/*
|
|
* Turn off reselection interrupts
|
|
*/
|
|
SET_5380_REG(NCR5380_IDSTAT, 0);
|
|
|
|
/*
|
|
* Reset BSY. The delay following it, surpresses a glitch in the
|
|
* 5380 which causes us to see our own BSY signal instead of that of
|
|
* the target.
|
|
*/
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_SEL | atn_flag | SC_ADTB);
|
|
delay(1);
|
|
|
|
/*
|
|
* Wait for the target to react, the specs call for a timeout of
|
|
* 250 ms.
|
|
*/
|
|
cnt = 25000;
|
|
while (!(GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY) && --cnt)
|
|
delay(10);
|
|
|
|
if (!(GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY)) {
|
|
/*
|
|
* There is no reaction from the target, start the selection
|
|
* timeout procedure. We release the databus but keep SEL
|
|
* asserted. After that we wait a 'selection abort time' (200
|
|
* usecs) and 2 deskew delays (90 ns) and check BSY again.
|
|
* When BSY is asserted, we assume the selection succeeded,
|
|
* otherwise we release the bus.
|
|
*/
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_SEL | atn_flag);
|
|
delay(201);
|
|
if (!(GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY)) {
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
reqp->xs->error = code ? code : XS_SELTIMEOUT;
|
|
DBG_SELPRINT ("Target %d not responding to sel\n",
|
|
reqp->targ_id);
|
|
finish_req(reqp);
|
|
PID("scsi_select8");
|
|
return (0);
|
|
}
|
|
}
|
|
SET_5380_REG(NCR5380_ICOM, atn_flag);
|
|
|
|
DBG_SELPRINT ("Target %d responding to select.\n", reqp->targ_id);
|
|
|
|
/*
|
|
* The SCSI-interrupts are disabled while a request is being handled.
|
|
*/
|
|
scsi_idisable();
|
|
|
|
/*
|
|
* If we did not request ATN, then don't try to send IDENTIFY.
|
|
*/
|
|
if (atn_flag == 0) {
|
|
reqp->phase = PH_CMD;
|
|
goto identify_failed;
|
|
}
|
|
|
|
/*
|
|
* Here we prepare to send an 'IDENTIFY' message.
|
|
* Allow disconnect only when interrups are allowed.
|
|
*/
|
|
#if 1
|
|
/* def YES_DISCONNECT_RECONNECT_IS_WORKING */
|
|
tmp[0] = MSG_IDENTIFY(reqp->targ_lun,
|
|
(reqp->dr_flag & DRIVER_NOINT) ? 0 : 1);
|
|
#else
|
|
tmp[0] = MSG_IDENTIFY(reqp->targ_lun,
|
|
(reqp->dr_flag & DRIVER_NOINT) ? 0 : 0);
|
|
#endif
|
|
cnt = 1;
|
|
phase = PH_MSGOUT;
|
|
|
|
/*
|
|
* Since we followed the SCSI-spec and raised ATN while SEL was true
|
|
* but before BSY was false during the selection, a 'MESSAGE OUT'
|
|
* phase should follow. Unfortunately, this does not happen on
|
|
* all targets (Asante ethernet devices, for example), so we must
|
|
* check the actual mode if the message transfer fails--if the
|
|
* new phase is PH_CMD and has never been successfully selected
|
|
* w/ATN in the past, then we assume that it is an old device
|
|
* that doesn't support select w/ATN.
|
|
*/
|
|
if (transfer_pio(&phase, tmp, &cnt, 0) || cnt) {
|
|
|
|
if ((phase == PH_CMD) && !(sc->sc_selected & targ_bit)) {
|
|
DBG_SELPRINT ("Target %d: not responding to ATN.\n",
|
|
reqp->targ_id);
|
|
sc->sc_noselatn |= targ_bit;
|
|
reqp->phase = PH_CMD;
|
|
goto identify_failed;
|
|
}
|
|
|
|
DBG_SELPRINT ("Target %d: failed to send identify\n",
|
|
reqp->targ_id);
|
|
/*
|
|
* Try to disconnect from the target. We cannot leave
|
|
* it just hanging here.
|
|
*/
|
|
if (!reach_msg_out(sc, sizeof(struct scsi_generic))) {
|
|
u_long len = 1;
|
|
u_char phase = PH_MSGOUT;
|
|
u_char msg = MSG_ABORT;
|
|
|
|
transfer_pio(&phase, &msg, &len, 0);
|
|
}
|
|
else scsi_reset(sc);
|
|
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
reqp->xs->error = code ? code : XS_DRIVER_STUFFUP;
|
|
finish_req(reqp);
|
|
PID("scsi_select9");
|
|
return (0);
|
|
}
|
|
reqp->phase = PH_MSGOUT;
|
|
|
|
identify_failed:
|
|
sc->sc_selected |= targ_bit;
|
|
|
|
#ifdef notyet /* LWP: Do we need timeouts in the driver? */
|
|
/*
|
|
* Command is connected, start timer ticking.
|
|
*/
|
|
ccb_p->xtimeout = ccb_p->timeout + Lbolt;
|
|
#endif
|
|
|
|
connected = reqp;
|
|
busy |= targ_bit;
|
|
PID("scsi_select10");
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Return codes:
|
|
* -1: quit main, trigger on interrupt
|
|
* 0: keep on running main.
|
|
*/
|
|
static int
|
|
information_transfer()
|
|
{
|
|
SC_REQ *reqp = connected;
|
|
u_char tmp, phase;
|
|
u_long len;
|
|
|
|
PID("info_transf1");
|
|
/*
|
|
* Clear pending interrupts from 5380-chip.
|
|
*/
|
|
scsi_clr_ipend();
|
|
|
|
/*
|
|
* We only have a valid SCSI-phase when REQ is asserted. Something
|
|
* is deadly wrong when BSY has dropped.
|
|
*/
|
|
tmp = GET_5380_REG(NCR5380_IDSTAT);
|
|
|
|
if (!(tmp & SC_S_BSY)) {
|
|
busy &= ~(1 << reqp->targ_id);
|
|
connected = NULL;
|
|
reqp->xs->error = XS_BUSY;
|
|
finish_req(reqp);
|
|
PID("info_transf2");
|
|
return (0);
|
|
}
|
|
|
|
if (tmp & SC_S_REQ) {
|
|
phase = (tmp >> 2) & 7;
|
|
if (phase != reqp->phase) {
|
|
reqp->phase = phase;
|
|
DBG_INFPRINT(show_phase, reqp, phase);
|
|
}
|
|
}
|
|
else return (-1);
|
|
|
|
switch (phase) {
|
|
case PH_DATAOUT:
|
|
#ifdef DBG_NOWRITE
|
|
ncr_tprint(reqp, "NOWRITE set -- write attempt aborted.");
|
|
reqp->msgout = MSG_ABORT;
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_ATN);
|
|
return (-1);
|
|
#endif /* DBG_NOWRITE */
|
|
/*
|
|
* If this is the first write using DMA, fill
|
|
* the bounce buffer.
|
|
*/
|
|
if (reqp->xdata_ptr == reqp->xs->data) { /* XXX */
|
|
if (reqp->dr_flag & DRIVER_BOUNCING)
|
|
bcopy(reqp->xdata_ptr, reqp->bounceb, reqp->xdata_len);
|
|
}
|
|
|
|
case PH_DATAIN:
|
|
#ifdef REAL_DMA
|
|
if (reqp->dr_flag & DRIVER_DMAOK) {
|
|
int poll = REAL_DMA_POLL|(reqp->dr_flag & DRIVER_NOINT);
|
|
transfer_dma(reqp, phase, poll);
|
|
if (!poll)
|
|
return (0);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
PID("info_transf3");
|
|
len = reqp->xdata_len;
|
|
#ifdef USE_PDMA
|
|
if (transfer_pdma(&phase, reqp->xdata_ptr, &len) == 0)
|
|
return (0);
|
|
#else
|
|
transfer_pio(&phase, reqp->xdata_ptr, &len, 0);
|
|
#endif
|
|
reqp->xdata_ptr += reqp->xdata_len - len;
|
|
reqp->xdata_len = len;
|
|
}
|
|
return (-1);
|
|
case PH_MSGIN:
|
|
/*
|
|
* We only expect single byte messages here.
|
|
*/
|
|
len = 1;
|
|
transfer_pio(&phase, &tmp, &len, 1);
|
|
reqp->message = tmp;
|
|
return (handle_message(reqp, tmp));
|
|
case PH_MSGOUT:
|
|
len = 1;
|
|
transfer_pio(&phase, &reqp->msgout, &len, 0);
|
|
if (reqp->msgout == MSG_ABORT) {
|
|
busy &= ~(1 << reqp->targ_id);
|
|
connected = NULL;
|
|
reqp->xs->error = XS_DRIVER_STUFFUP;
|
|
finish_req(reqp);
|
|
PID("info_transf4");
|
|
return (0);
|
|
}
|
|
reqp->msgout = MSG_NOOP;
|
|
return (-1);
|
|
case PH_CMD :
|
|
len = command_size(reqp->xcmd.opcode);
|
|
transfer_pio(&phase, (u_char *)&reqp->xcmd, &len, 0);
|
|
PID("info_transf5");
|
|
return (-1);
|
|
case PH_STATUS:
|
|
len = 1;
|
|
transfer_pio(&phase, &tmp, &len, 0);
|
|
reqp->status = tmp;
|
|
PID("info_transf6");
|
|
return (-1);
|
|
default :
|
|
ncr_tprint(reqp, "Unknown phase\n");
|
|
}
|
|
PID("info_transf7");
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Handle the message 'msg' send to us by the target.
|
|
* Return values:
|
|
* 0 : The current command has completed.
|
|
* -1 : Get on to the next phase.
|
|
*/
|
|
static int
|
|
handle_message(reqp, msg)
|
|
SC_REQ *reqp;
|
|
u_int msg;
|
|
{
|
|
SC_REQ *prev, *req;
|
|
int sps;
|
|
|
|
PID("hmessage1");
|
|
switch (msg) {
|
|
/*
|
|
* Linking lets us reduce the time required to get
|
|
* the next command to the device, skipping the arbitration
|
|
* and selection time. In the current implementation,
|
|
* we merely have to start the next command pointed
|
|
* to by 'next_link'.
|
|
*/
|
|
case MSG_LINK_CMD_COMPLETE:
|
|
case MSG_LINK_CMD_COMPLETEF:
|
|
if (reqp->link == NULL) {
|
|
ncr_tprint(reqp, "No link for linked command");
|
|
nack_message(reqp, MSG_ABORT);
|
|
PID("hmessage2");
|
|
return (-1);
|
|
}
|
|
ack_message();
|
|
|
|
if (!(reqp->dr_flag & DRIVER_AUTOSEN)) {
|
|
reqp->xs->resid = reqp->xdata_len;
|
|
reqp->xs->error = 0;
|
|
}
|
|
|
|
#ifdef AUTO_SENSE
|
|
if (check_autosense(reqp, 1) == -1)
|
|
return (-1);
|
|
#endif /* AUTO_SENSE */
|
|
|
|
#ifdef DBG_REQ
|
|
if (dbg_target_mask & (1 << reqp->targ_id))
|
|
show_request(reqp->link, "LINK");
|
|
#endif
|
|
/*
|
|
* Unlink the 'linked' request from the issue_q
|
|
*/
|
|
sps = splbio();
|
|
prev = NULL;
|
|
req = issue_q;
|
|
for (; req != NULL; prev = req, req = req->next) {
|
|
if (req == reqp->link)
|
|
break;
|
|
}
|
|
if (req == NULL)
|
|
panic("Inconsistent issue_q");
|
|
if (prev == NULL)
|
|
issue_q = req->next;
|
|
else prev->next = req->next;
|
|
req->next = NULL;
|
|
connected = reqp->link;
|
|
splx(sps);
|
|
|
|
finish_req(reqp);
|
|
PID("hmessage3");
|
|
return (-1);
|
|
case MSG_ABORT:
|
|
case MSG_CMDCOMPLETE:
|
|
ack_message();
|
|
connected = NULL;
|
|
busy &= ~(1 << reqp->targ_id);
|
|
if (!(reqp->dr_flag & DRIVER_AUTOSEN)) {
|
|
reqp->xs->resid = reqp->xdata_len;
|
|
reqp->xs->error = 0;
|
|
}
|
|
|
|
#ifdef AUTO_SENSE
|
|
if (check_autosense(reqp, 0) == -1) {
|
|
PID("hmessage4");
|
|
return (0);
|
|
}
|
|
#endif /* AUTO_SENSE */
|
|
|
|
finish_req(reqp);
|
|
PID("hmessage5");
|
|
return (0);
|
|
case MSG_MESSAGE_REJECT:
|
|
ack_message();
|
|
PID("hmessage6");
|
|
return (-1);
|
|
case MSG_DISCONNECT:
|
|
ack_message();
|
|
#ifdef DBG_REQ
|
|
if (dbg_target_mask & (1 << reqp->targ_id))
|
|
show_request(reqp, "DISCON");
|
|
#endif
|
|
sps = splbio();
|
|
connected = NULL;
|
|
reqp->next = discon_q;
|
|
discon_q = reqp;
|
|
splx(sps);
|
|
PID("hmessage7");
|
|
return (0);
|
|
case MSG_SAVEDATAPOINTER:
|
|
case MSG_RESTOREPOINTERS:
|
|
/*
|
|
* We save pointers implicitely at disconnect.
|
|
* So we can ignore these messages.
|
|
*/
|
|
ack_message();
|
|
PID("hmessage8");
|
|
return (-1);
|
|
case MSG_EXTENDED:
|
|
nack_message(reqp, MSG_MESSAGE_REJECT);
|
|
PID("hmessage9");
|
|
return (-1);
|
|
default:
|
|
ncr_tprint(reqp, "Unknown message %x\n", msg);
|
|
return (-1);
|
|
}
|
|
PID("hmessage10");
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* Handle reselection. If a valid reconnection occurs, connected
|
|
* points at the reconnected command. The command is removed from the
|
|
* disconnected queue.
|
|
*/
|
|
static void
|
|
reselect(sc)
|
|
struct ncr_softc *sc;
|
|
{
|
|
u_char phase;
|
|
u_long len;
|
|
u_char msg;
|
|
u_char target_mask;
|
|
int abort = 0;
|
|
SC_REQ *tmp, *prev;
|
|
|
|
PID("reselect1");
|
|
target_mask = GET_5380_REG(NCR5380_DATA) & ~SC_HOST_ID;
|
|
|
|
/*
|
|
* At this point, we have detected that our SCSI-id is on the bus,
|
|
* SEL is true and BSY was false for at least one bus settle
|
|
* delay (400 ns.).
|
|
* We must assert BSY ourselves, until the target drops the SEL signal.
|
|
* The SCSI-spec specifies no maximum time for this, so we have to
|
|
* choose something long enough to suit all targets.
|
|
*/
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_BSY);
|
|
len = 250000;
|
|
while ((GET_5380_REG(NCR5380_IDSTAT) & SC_S_SEL) && (len > 0)) {
|
|
delay(1);
|
|
len--;
|
|
}
|
|
if (GET_5380_REG(NCR5380_IDSTAT) & SC_S_SEL) {
|
|
/* Damn SEL isn't dropping */
|
|
scsi_reset(sc);
|
|
return;
|
|
}
|
|
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
|
|
/*
|
|
* Check if the reselection is still valid. Check twice because
|
|
* of possible line glitches - cheaper than delay(1) and we need
|
|
* only a few nanoseconds.
|
|
*/
|
|
if (!(GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY)) {
|
|
if (!(GET_5380_REG(NCR5380_IDSTAT) & SC_S_BSY)) {
|
|
ncr_aprint(sc, "Stepped into the reselection timeout\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the expected identify message.
|
|
*/
|
|
phase = PH_MSGIN;
|
|
len = 1;
|
|
transfer_pio(&phase, &msg, &len, 0);
|
|
if (len || !MSG_ISIDENTIFY(msg)) {
|
|
ncr_aprint(sc, "Expecting IDENTIFY, got 0x%x\n", msg);
|
|
abort = 1;
|
|
}
|
|
else {
|
|
/*
|
|
* Find the command reconnecting
|
|
*/
|
|
for (tmp = discon_q, prev = NULL; tmp; prev = tmp, tmp = tmp->next){
|
|
if (target_mask == (1 << tmp->targ_id)) {
|
|
if (prev)
|
|
prev->next = tmp->next;
|
|
else discon_q = tmp->next;
|
|
tmp->next = NULL;
|
|
break;
|
|
}
|
|
}
|
|
if (tmp == NULL) {
|
|
ncr_aprint(sc, "No disconnected job for targetmask %x\n",
|
|
target_mask);
|
|
abort = 1;
|
|
}
|
|
}
|
|
if (abort) {
|
|
msg = MSG_ABORT;
|
|
len = 1;
|
|
phase = PH_MSGOUT;
|
|
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_ATN);
|
|
transfer_pio(&phase, &msg, &len, 0);
|
|
}
|
|
else {
|
|
connected = tmp;
|
|
#ifdef DBG_REQ
|
|
if (dbg_target_mask & (1 << tmp->targ_id))
|
|
show_request(tmp, "RECON");
|
|
#endif
|
|
}
|
|
PID("reselect2");
|
|
}
|
|
|
|
/*
|
|
* Transfer data in a given phase using programmed I/O.
|
|
* Returns -1 when a different phase is entered without transferring the
|
|
* maximum number of bytes, 0 if all bytes transferred or exit is in the same
|
|
* phase.
|
|
*/
|
|
static int
|
|
transfer_pio(phase, data, len, dont_drop_ack)
|
|
u_char *phase;
|
|
u_char *data;
|
|
u_long *len;
|
|
int dont_drop_ack;
|
|
{
|
|
u_int cnt = *len;
|
|
u_char ph = *phase;
|
|
u_char tmp, new_icom;
|
|
|
|
DBG_PIOPRINT ("SCSI: transfer_pio start: phase: %d, len: %d\n", ph,cnt);
|
|
PID("tpio1");
|
|
SET_5380_REG(NCR5380_TCOM, ph);
|
|
do {
|
|
if (!wait_req_true()) {
|
|
DBG_PIOPRINT ("SCSI: transfer_pio: missing REQ\n", 0, 0);
|
|
break;
|
|
}
|
|
if (((GET_5380_REG(NCR5380_IDSTAT) >> 2) & 7) != ph) {
|
|
DBG_PIOPRINT ("SCSI: transfer_pio: phase mismatch\n", 0, 0);
|
|
break;
|
|
}
|
|
if (PH_IN(ph)) {
|
|
*data++ = GET_5380_REG(NCR5380_DATA);
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_ACK);
|
|
if ((cnt == 1) && dont_drop_ack)
|
|
new_icom = SC_A_ACK;
|
|
else new_icom = 0;
|
|
}
|
|
else {
|
|
SET_5380_REG(NCR5380_DATA, *data++);
|
|
|
|
/*
|
|
* The SCSI-standard suggests that in the 'MESSAGE OUT' phase,
|
|
* the initiator should drop ATN on the last byte of the
|
|
* message phase after REQ has been asserted for the handshake
|
|
* but before the initiator raises ACK.
|
|
*/
|
|
if (!( (ph == PH_MSGOUT) && (cnt > 1) )) {
|
|
SET_5380_REG(NCR5380_ICOM, SC_ADTB);
|
|
SET_5380_REG(NCR5380_ICOM, SC_ADTB | SC_A_ACK);
|
|
new_icom = 0;
|
|
}
|
|
else {
|
|
SET_5380_REG(NCR5380_ICOM, SC_ADTB | SC_A_ATN);
|
|
SET_5380_REG(NCR5380_ICOM, SC_ADTB|SC_A_ATN|SC_A_ACK);
|
|
new_icom = SC_A_ATN;
|
|
}
|
|
}
|
|
if (!wait_req_false()) {
|
|
DBG_PIOPRINT ("SCSI: transfer_pio - REQ not dropping\n", 0, 0);
|
|
break;
|
|
}
|
|
SET_5380_REG(NCR5380_ICOM, new_icom);
|
|
|
|
} while (--cnt);
|
|
|
|
if ((tmp = GET_5380_REG(NCR5380_IDSTAT)) & SC_S_REQ)
|
|
*phase = (tmp >> 2) & 7;
|
|
else *phase = NR_PHASE;
|
|
*len = cnt;
|
|
DBG_PIOPRINT ("SCSI: transfer_pio done: phase: %d, len: %d\n",
|
|
*phase, cnt);
|
|
PID("tpio2");
|
|
if (!cnt || (*phase == ph))
|
|
return (0);
|
|
return (-1);
|
|
}
|
|
|
|
#ifdef REAL_DMA
|
|
/*
|
|
* Start a DMA-transfer on the device using the current pointers.
|
|
* If 'poll' is true, the function busy-waits until DMA has completed.
|
|
*/
|
|
static void
|
|
transfer_dma(reqp, phase, poll)
|
|
SC_REQ *reqp;
|
|
u_int phase;
|
|
int poll;
|
|
{
|
|
int dma_done;
|
|
u_char mbase = 0;
|
|
int sps;
|
|
|
|
again:
|
|
PID("tdma1");
|
|
|
|
/*
|
|
* We should be in phase, otherwise we are not allowed to
|
|
* drive the bus.
|
|
*/
|
|
SET_5380_REG(NCR5380_TCOM, phase);
|
|
|
|
/*
|
|
* Defer interrupts until DMA is fully running.
|
|
*/
|
|
sps = splbio();
|
|
|
|
/*
|
|
* Clear pending interrupts and parity errors.
|
|
*/
|
|
scsi_clr_ipend();
|
|
|
|
if (!poll) {
|
|
/*
|
|
* Enable SCSI interrupts and set IN_DMA flag, set 'mbase'
|
|
* to the interrupts we want enabled.
|
|
*/
|
|
scsi_ienable();
|
|
reqp->dr_flag |= DRIVER_IN_DMA;
|
|
mbase = SC_E_EOPI | SC_MON_BSY;
|
|
}
|
|
else scsi_idisable();
|
|
mbase |= IMODE_BASE | SC_M_DMA;
|
|
scsi_dma_setup(reqp, phase, mbase);
|
|
|
|
splx(sps);
|
|
|
|
if (poll) {
|
|
/*
|
|
* On polled-dma transfers, we wait here until the
|
|
* 'end-of-dma' condition occurs.
|
|
*/
|
|
poll_edma(reqp);
|
|
if (!(dma_done = dma_ready()))
|
|
goto again;
|
|
}
|
|
PID("tdma2");
|
|
}
|
|
|
|
/*
|
|
* Check results of a DMA data-transfer.
|
|
*/
|
|
static int
|
|
dma_ready()
|
|
{
|
|
SC_REQ *reqp = connected;
|
|
int dmstat, is_edma;
|
|
long bytes_left, bytes_done;
|
|
|
|
is_edma = get_dma_result(reqp, &bytes_left);
|
|
dmstat = GET_5380_REG(NCR5380_DMSTAT);
|
|
|
|
/*
|
|
* Check if the call is sensible and not caused by any spurious
|
|
* interrupt.
|
|
*/
|
|
if (!is_edma && !(dmstat & (SC_END_DMA|SC_BSY_ERR))
|
|
&& (dmstat & SC_PHS_MTCH) ) {
|
|
ncr_tprint(reqp, "dma_ready: spurious call"
|
|
"(dm:%x,last_hit: %s)\n",
|
|
#ifdef DBG_PID
|
|
dmstat, last_hit);
|
|
#else
|
|
dmstat, "unknown");
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Clear all (pending) interrupts.
|
|
*/
|
|
scsi_clr_ipend();
|
|
|
|
/*
|
|
* Update various transfer-pointers/lengths
|
|
*/
|
|
bytes_done = reqp->dm_cur->dm_count - bytes_left;
|
|
|
|
if ((reqp->dr_flag & DRIVER_BOUNCING) && (PH_IN(reqp->phase))) {
|
|
/*
|
|
* Copy the bytes read until now from the bounce buffer
|
|
* to the 'real' destination. Flush the data-cache
|
|
* before copying.
|
|
*/
|
|
PCIA();
|
|
bcopy(reqp->bouncerp, reqp->xdata_ptr, bytes_done);
|
|
reqp->bouncerp += bytes_done;
|
|
}
|
|
|
|
reqp->xdata_ptr = &reqp->xdata_ptr[bytes_done]; /* XXX */
|
|
reqp->xdata_len -= bytes_done; /* XXX */
|
|
if ((reqp->dm_cur->dm_count -= bytes_done) == 0)
|
|
reqp->dm_cur++;
|
|
else reqp->dm_cur->dm_addr += bytes_done;
|
|
|
|
if (PH_IN(reqp->phase) && (dmstat & SC_PAR_ERR)) {
|
|
if (!(ncr5380_no_parchk & (1 << reqp->targ_id)))
|
|
/* XXX: Should be parity error ???? */
|
|
reqp->xs->error = XS_DRIVER_STUFFUP;
|
|
}
|
|
|
|
/*
|
|
* DMA mode should always be reset even when we will continue with the
|
|
* next chain. It is also essential to clear the MON_BUSY because
|
|
* when LOST_BUSY is unexpectedly set, we will not be able to drive
|
|
* the bus....
|
|
*/
|
|
SET_5380_REG(NCR5380_MODE, IMODE_BASE);
|
|
|
|
|
|
if ((dmstat & SC_BSY_ERR) || !(dmstat & SC_PHS_MTCH)
|
|
|| (reqp->dm_cur > reqp->dm_last) || (reqp->xs->error)) {
|
|
|
|
/*
|
|
* Tell interrupt functions DMA mode has ended.
|
|
*/
|
|
reqp->dr_flag &= ~DRIVER_IN_DMA;
|
|
|
|
/*
|
|
* Clear mode and icom
|
|
*/
|
|
SET_5380_REG(NCR5380_MODE, IMODE_BASE);
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
|
|
if (dmstat & SC_BSY_ERR) {
|
|
if (!reqp->xs->error)
|
|
reqp->xs->error = XS_BUSY;
|
|
finish_req(reqp);
|
|
PID("dma_ready1");
|
|
return (1);
|
|
}
|
|
|
|
if (reqp->xs->error != 0) {
|
|
ncr_tprint(reqp, "dma-ready: code = %d\n", reqp->xs->error); /* LWP */
|
|
reqp->msgout = MSG_ABORT;
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_ATN);
|
|
}
|
|
PID("dma_ready2");
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
#endif /* REAL_DMA */
|
|
|
|
static int
|
|
check_autosense(reqp, linked)
|
|
SC_REQ *reqp;
|
|
int linked;
|
|
{
|
|
int sps;
|
|
|
|
/*
|
|
* If we not executing an auto-sense and the status code
|
|
* is request-sense, we automatically issue a request
|
|
* sense command.
|
|
*/
|
|
PID("cautos1");
|
|
if (!(reqp->dr_flag & DRIVER_AUTOSEN)) {
|
|
switch (reqp->status & SCSMASK) {
|
|
case SCSCHKC:
|
|
bcopy(sense_cmd, &reqp->xcmd, sizeof(sense_cmd));
|
|
reqp->xdata_ptr = (u_char *)&reqp->xs->sense;
|
|
reqp->xdata_len = sizeof(reqp->xs->sense);
|
|
reqp->dr_flag |= DRIVER_AUTOSEN;
|
|
reqp->dr_flag &= ~DRIVER_DMAOK;
|
|
if (!linked) {
|
|
sps = splbio();
|
|
reqp->next = issue_q;
|
|
issue_q = reqp;
|
|
splx(sps);
|
|
}
|
|
else reqp->xcmd.bytes[4] |= 1;
|
|
|
|
#ifdef DBG_REQ
|
|
bzero(reqp->xdata_ptr, reqp->xdata_len);
|
|
if (dbg_target_mask & (1 << reqp->targ_id))
|
|
show_request(reqp, "AUTO-SENSE");
|
|
#endif
|
|
PID("cautos2");
|
|
return (-1);
|
|
|
|
case SCSBUSY:
|
|
reqp->xs->error = XS_BUSY;
|
|
return (0);
|
|
}
|
|
}
|
|
else {
|
|
/*
|
|
* An auto-sense has finished
|
|
*/
|
|
if ((reqp->status & SCSMASK) != SCSGOOD)
|
|
reqp->xs->error = XS_DRIVER_STUFFUP; /* SC_E_AUTOSEN; */
|
|
else reqp->xs->error = XS_SENSE;
|
|
reqp->status = SCSCHKC;
|
|
}
|
|
PID("cautos3");
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
reach_msg_out(sc, len)
|
|
struct ncr_softc *sc;
|
|
u_long len;
|
|
{
|
|
u_char phase;
|
|
u_char data;
|
|
|
|
ncr_aprint(sc, "Trying to reach Message-out phase\n");
|
|
if ((phase = GET_5380_REG(NCR5380_IDSTAT)) & SC_S_REQ)
|
|
phase = (phase >> 2) & 7;
|
|
else return (-1);
|
|
ncr_aprint(sc, "Trying to reach Message-out phase, now: %d\n", phase);
|
|
if (phase == PH_MSGOUT)
|
|
return (0);
|
|
|
|
SET_5380_REG(NCR5380_TCOM, phase);
|
|
|
|
do {
|
|
if (!wait_req_true())
|
|
break;
|
|
if (((GET_5380_REG(NCR5380_IDSTAT) >> 2) & 7) != phase)
|
|
break;
|
|
if (PH_IN(phase)) {
|
|
data = GET_5380_REG(NCR5380_DATA);
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_ACK | SC_A_ATN);
|
|
}
|
|
else {
|
|
SET_5380_REG(NCR5380_DATA, 0);
|
|
SET_5380_REG(NCR5380_ICOM, SC_ADTB|SC_A_ACK|SC_A_ATN);
|
|
}
|
|
if (!wait_req_false())
|
|
break;
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_ATN);
|
|
} while (--len);
|
|
|
|
if ((phase = GET_5380_REG(NCR5380_IDSTAT)) & SC_S_REQ) {
|
|
phase = (phase >> 2) & 7;
|
|
if (phase == PH_MSGOUT) {
|
|
ncr_aprint(sc, "Message-out phase reached.\n");
|
|
return (0);
|
|
}
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
static void
|
|
scsi_reset(sc)
|
|
struct ncr_softc *sc;
|
|
{
|
|
SC_REQ *tmp, *next;
|
|
int sps;
|
|
|
|
ncr_aprint(sc, "Resetting SCSI-bus\n");
|
|
|
|
PID("scsi_reset1");
|
|
sps = splbio();
|
|
SET_5380_REG(NCR5380_ICOM, SC_A_RST);
|
|
delay(1);
|
|
SET_5380_REG(NCR5380_ICOM, 0);
|
|
|
|
/*
|
|
* None of the jobs in the discon_q will ever be reconnected,
|
|
* notify this to the higher level code.
|
|
*/
|
|
for (tmp = discon_q; tmp ;) {
|
|
next = tmp->next;
|
|
tmp->next = NULL;
|
|
tmp->xs->error = XS_TIMEOUT;
|
|
busy &= ~(1 << tmp->targ_id);
|
|
finish_req(tmp);
|
|
tmp = next;
|
|
}
|
|
discon_q = NULL;
|
|
|
|
/*
|
|
* The current job will never finish either.
|
|
* The problem is that we can't finish the job because an instance
|
|
* of main is running on it. Our best guess is that the job is currently
|
|
* doing REAL-DMA. In that case 'dma_ready()' should correctly finish
|
|
* the job because it detects BSY-loss.
|
|
*/
|
|
if (tmp = connected) {
|
|
if (tmp->dr_flag & DRIVER_IN_DMA) {
|
|
tmp->xs->error = XS_DRIVER_STUFFUP;
|
|
#ifdef REAL_DMA
|
|
dma_ready();
|
|
#endif
|
|
}
|
|
}
|
|
splx(sps);
|
|
PID("scsi_reset2");
|
|
}
|
|
|
|
/*
|
|
* Check validity of the IRQ set by the 5380. If the interrupt is valid,
|
|
* the appropriate action is carried out (reselection or DMA ready) and
|
|
* INTR_RESEL or INTR_DMA is returned. Otherwise a console notice is written
|
|
* and INTR_SPURIOUS is returned.
|
|
*/
|
|
static int
|
|
check_intr(sc)
|
|
struct ncr_softc *sc;
|
|
{
|
|
SC_REQ *reqp;
|
|
|
|
if ((GET_5380_REG(NCR5380_IDSTAT) & (SC_S_SEL|SC_S_IO))
|
|
==(SC_S_SEL|SC_S_IO))
|
|
return (INTR_RESEL);
|
|
else {
|
|
if ((reqp = connected) && (reqp->dr_flag & DRIVER_IN_DMA)){
|
|
reqp->dr_flag &= ~DRIVER_IN_DMA;
|
|
return (INTR_DMA);
|
|
}
|
|
}
|
|
scsi_clr_ipend();
|
|
printf("-->");
|
|
scsi_show();
|
|
ncr_aprint(sc, "Spurious interrupt.\n");
|
|
return (INTR_SPURIOUS);
|
|
}
|
|
|
|
#ifdef REAL_DMA
|
|
/*
|
|
* Check if DMA can be used for this request. This function also builds
|
|
* the dma-chain.
|
|
*/
|
|
static int
|
|
scsi_dmaok(reqp)
|
|
SC_REQ *reqp;
|
|
{
|
|
u_long phy_buf;
|
|
u_long phy_len;
|
|
void *req_addr;
|
|
u_long req_len;
|
|
struct dma_chain *dm;
|
|
|
|
/*
|
|
* Initialize locals and requests' DMA-chain.
|
|
*/
|
|
req_len = reqp->xdata_len;
|
|
req_addr = (void*)reqp->xdata_ptr;
|
|
dm = reqp->dm_cur = reqp->dm_last = reqp->dm_chain;
|
|
dm->dm_count = dm->dm_addr = 0;
|
|
reqp->dr_flag &= ~DRIVER_BOUNCING;
|
|
|
|
/*
|
|
* Do not accept zero length DMA.
|
|
*/
|
|
if (req_len == 0)
|
|
return (0);
|
|
|
|
/*
|
|
* LWP: I think that this restriction is not strictly nessecary.
|
|
*/
|
|
if ((req_len & 0x1) || ((u_int)req_addr & 0x3))
|
|
return (0);
|
|
|
|
/*
|
|
* Build the DMA-chain.
|
|
*/
|
|
dm->dm_addr = phy_buf = kvtop(req_addr);
|
|
while (req_len) {
|
|
if (req_len < (phy_len = NBPG - ((u_long)req_addr & PGOFSET)))
|
|
phy_len = req_len;
|
|
|
|
req_addr += phy_len;
|
|
req_len -= phy_len;
|
|
dm->dm_count += phy_len;
|
|
|
|
if (req_len) {
|
|
u_long tmp = kvtop(req_addr);
|
|
|
|
if ((phy_buf + phy_len) != tmp) {
|
|
if (wrong_dma_range(reqp, dm)) {
|
|
if (reqp->dr_flag & DRIVER_BOUNCING)
|
|
goto bounceit;
|
|
return (0);
|
|
}
|
|
|
|
if (++dm >= &reqp->dm_chain[MAXDMAIO]) {
|
|
ncr_tprint(reqp,"dmaok: DMA chain too long!\n");
|
|
return (0);
|
|
}
|
|
dm->dm_count = 0;
|
|
dm->dm_addr = tmp;
|
|
}
|
|
phy_buf = tmp;
|
|
}
|
|
}
|
|
if (wrong_dma_range(reqp, dm)) {
|
|
if (reqp->dr_flag & DRIVER_BOUNCING)
|
|
goto bounceit;
|
|
return (0);
|
|
}
|
|
reqp->dm_last = dm;
|
|
return (1);
|
|
|
|
bounceit:
|
|
if ((reqp->bounceb = alloc_bounceb(reqp->xdata_len)) == NULL) {
|
|
/*
|
|
* If we can't get a bounce buffer, forget DMA
|
|
*/
|
|
reqp->dr_flag &= ~DRIVER_BOUNCING;
|
|
return(0);
|
|
}
|
|
/*
|
|
* Initialize a single DMA-range containing the bounced request
|
|
*/
|
|
dm = reqp->dm_cur = reqp->dm_last = reqp->dm_chain;
|
|
dm->dm_addr = kvtop(reqp->bounceb);
|
|
dm->dm_count = reqp->xdata_len;
|
|
reqp->bouncerp = reqp->bounceb;
|
|
|
|
return (1);
|
|
}
|
|
#endif /* REAL_DMA */
|
|
|
|
static void
|
|
run_main(sc)
|
|
struct ncr_softc *sc;
|
|
{
|
|
int sps = splbio();
|
|
|
|
if (!main_running) {
|
|
/*
|
|
* If shared resources are required, claim them
|
|
* before entering 'scsi_main'. If we can't get them
|
|
* now, assume 'run_main' will be called when the resource
|
|
* becomes available.
|
|
*/
|
|
if (!claimed_dma()) {
|
|
splx(sps);
|
|
return;
|
|
}
|
|
main_running = 1;
|
|
splx(sps);
|
|
scsi_main(sc);
|
|
}
|
|
else splx(sps);
|
|
}
|
|
|
|
/*
|
|
* Prefix message with full target info.
|
|
*/
|
|
static void
|
|
ncr_tprint(SC_REQ *reqp, char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
sc_print_addr(reqp->xs->sc_link);
|
|
printf("%:", fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
|
|
/*
|
|
* Prefix message with adapter info.
|
|
*/
|
|
static void
|
|
ncr_aprint(struct ncr_softc *sc, char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
printf("%s : %:", sc->sc_dev.dv_xname, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
/****************************************************************************
|
|
* Start Debugging Functions *
|
|
****************************************************************************/
|
|
static void
|
|
show_data_sense(xs)
|
|
struct scsi_xfer *xs;
|
|
{
|
|
u_char *p1, *p2;
|
|
int i;
|
|
int sz;
|
|
|
|
p1 = (u_char *) xs->cmd;
|
|
p2 = (u_char *)&xs->sense;
|
|
if (*p2 == 0)
|
|
return; /* No(n)sense */
|
|
printf("cmd[%d,%d]: ", xs->cmdlen, sz = command_size(*p1));
|
|
for (i = 0; i < sz; i++)
|
|
printf("%x ", p1[i]);
|
|
printf("\nsense: ");
|
|
for (i = 0; i < sizeof(xs->sense); i++)
|
|
printf("%x ", p2[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
static void
|
|
show_request(reqp, qtxt)
|
|
SC_REQ *reqp;
|
|
char *qtxt;
|
|
{
|
|
printf("REQ-%s: %d %x[%d] cmd[0]=%x S=%x M=%x R=%x resid=%d %s\n",
|
|
qtxt, reqp->targ_id, reqp->xdata_ptr, reqp->xdata_len,
|
|
reqp->xcmd.opcode, reqp->status, reqp->message,
|
|
reqp->xs->error, reqp->xs->resid, reqp->link ? "L":"");
|
|
if ((reqp->status & SCSMASK) == SCSCHKC)
|
|
show_data_sense(reqp->xs);
|
|
}
|
|
|
|
static char *sig_names[] = {
|
|
"PAR", "SEL", "I/O", "C/D", "MSG", "REQ", "BSY", "RST",
|
|
"ACK", "ATN", "LBSY", "PMATCH", "IRQ", "EPAR", "DREQ", "EDMA"
|
|
};
|
|
|
|
static void
|
|
show_signals(dmstat, idstat)
|
|
u_char dmstat, idstat;
|
|
{
|
|
u_short tmp, mask;
|
|
int i, j, need_pipe;
|
|
|
|
tmp = idstat | ((dmstat & 3) << 8);
|
|
printf("Bus signals (%02x/%02x): ", idstat, dmstat & 3);
|
|
for (mask = 1, j = need_pipe = 0; mask <= tmp; mask <<= 1, j++) {
|
|
if (tmp & mask)
|
|
printf("%s%s", need_pipe++ ? "|" : "", sig_names[j]);
|
|
}
|
|
printf("\nDma status (%02x): ", dmstat);
|
|
for (mask = 4, j = 10, need_pipe = 0; mask <= dmstat; mask <<= 1, j++) {
|
|
if (dmstat & mask)
|
|
printf("%s%s", need_pipe++ ? "|" : "", sig_names[j]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
scsi_show()
|
|
{
|
|
SC_REQ *tmp;
|
|
int sps = splhigh();
|
|
u_char idstat, dmstat;
|
|
|
|
#ifndef DBG_PID
|
|
#define last_hit ""
|
|
#define olast_hit ""
|
|
#endif
|
|
|
|
for (tmp = issue_q; tmp; tmp = tmp->next)
|
|
show_request(tmp, "ISSUED");
|
|
for (tmp = discon_q; tmp; tmp = tmp->next)
|
|
show_request(tmp, "DISCONNECTED");
|
|
if (connected)
|
|
show_request(connected, "CONNECTED");
|
|
idstat = GET_5380_REG(NCR5380_IDSTAT);
|
|
dmstat = GET_5380_REG(NCR5380_DMSTAT);
|
|
show_signals(dmstat, idstat);
|
|
if (connected)
|
|
printf("phase = %d, ", connected->phase);
|
|
printf("busy:%x, last_hit:%s, olast_hit:%s spl:%04x\n", busy,
|
|
last_hit, olast_hit, sps);
|
|
|
|
splx(sps);
|
|
}
|