AM53/79C974 and NCR5380 SCSI driver striking similarities

Drew Eckhardt (drew@poohsticks.org)
Wed, 05 Jul 1995 01:19:27 -0600


Sorry if this isn't a bit more eloquent and to the point; I'm
trying to illustrate the code which is common to two moderate
sized SCSI drivers, am a bit flakey after most of a day of
hacking, and felt that this was important enough to take care of
now and not at some later date.

Comparing NCR5380.h and AM53C974.h, I see a hint of resemblance between
your driver and mine -

We all have *_hostdata structures, with the following fields :

Scsi_Cmnd *connected; /* currently connected command */
Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */
Scsi_Cmnd *issue_queue; /* waiting to be issued */
unsigned aborted:1; /* flag, says aborted */
unsigned char busy[8]; /* index = target, bit = lun */

complete with identical comments. I used

#define TAG_NEXT -1 /* Use next free tag */
#define TAG_NONE -2 /*
* Establish I_T_L nexus instead of I_T_L_Q
* even on SCSI-II devices.
*/

you used the same special values and comments.

Thankfully, interfaces aren't copyrightable (inspite of what RMS and Apple
might say), and in and of itself this is harmless. However, the obvious cut
and paste job makes one wonder if something similar was done in the actual code.

Let's start off with how you adopt the same setup I use for supporting
multiple host adapters with the same driver, including the mechanism used
for interrupt sharing - a pointer to the first of the linked list of host
adapters maintained by the middle level SCSI-code, plus a pointer to the
Scsi_Host template which the interrupt handler can traverse to locate the
appropriate per-adapter structure :

NCR5380.c:
static struct Scsi_Host *first_instance = NULL;
static Scsi_Host_Template *the_template = NULL;

53c7,8xx.c:
static struct Scsi_Host *first_host = NULL; /* Head of list of NCR boards */
static Scsi_Host_Template *the_template = NULL;

AM53c7,8xx.c
static struct Scsi_Host *first_instance = NULL;
static Scsi_Host_Template *the_template = NULL;
static struct Scsi_Host *first_host = NULL; /* Head of list of AMD boards */

You use the same code which operates on these lists to try to share interrupts :

53c7,8xx.c:
/*
* Set up an interrupt handler if we aren't allready sharing an IRQ
* with another board.
*/

for (search = first_host; search && (search->hostt == the_template) &&
(search->irq != host->irq); search=search->next);

if (!search) {
if (irqaction (host->irq, &NCR53c7x0_sigaction)) {
printk("scsi%d : IRQ%d not free, detaching\n",
host->host_no, host->irq);
scsi_unregister (host, hostdata->size);
return -1;
}
} else {
printk("scsi%d : using interrupt handler previously installed for scsi%d\n",
host->host_no, search->host_no);
}

AM53c7,8xx.c:
/* Set up an interrupt handler if we aren't already sharing an IRQ with another board */
for (search = first_host; search && (search->hostt == the_template) &&
(search->irq != instance->irq); search = search->next);
if (!search) {
if (request_irq(instance->irq, AM53C974_intr, SA_INTERRUPT, "AM53C974")) {
printk("scsi%d: IRQ%d not free, detaching\n", instance->host_no, instance->irq);
scsi_unregister(instance);
return -1; }
}
else {
printk("scsi%d: using interrupt handler previously installed for scsi%d\n",
instance->host_no, search->host_no); }

NCR5380.c:
for (instance = first_instance; instance && (instance->hostt ==
the_template); instance = instance->next)
if (instance->irq == irq) {

AM53c7,8xx.c
for (instance = first_instance; instance; instance = instance->next)
if ((instance->irq == irq) && (instance->hostt == the_template)) goto FOUND;
sti();
return;

You use the same corroutine design that I do in the NCR5380 driver,
including the same wrapper to prevent rentrancy and essentially the
same actual code

NCR5380.c:

static volatile int main_running = 0;

/*
* Function : run_main(void)
*
* Purpose : insure that the coroutine is running and will process our
* request. main_running is checked/set here (in an inline function)
* rather than in NCR5380_main itself to reduce the chances of stack
* overflow.
*
*/

static __inline__ void run_main(void) {
cli();
if (!main_running) {
main_running = 1;
NCR5380_main();
/*
* main_running is cleared in NCR5380_main once it can't do
* more work, and NCR5380_main exits with interrupts disabled.
*/
sti();
} else
sti();
}

AM53c7,8xx.c:

static volatile int main_running = 0;

/**************************************************************************
* Function : run_main(void) *
* *
* Purpose : insure that the coroutine is running and will process our *
* request. main_running is checked/set here (in an inline *
* function rather than in AM53C974_main itself to reduce the *
* chances of stack overflow. *
* *
* *
* Inputs : none *
* *
* Returns : nothing *
**************************************************************************/
static __inline__ void run_main(void)
{
cli();
if (!main_running) {
/* main_running is cleared in AM53C974_main once it can't do
more work, and AM53C974_main exits with interrupts disabled. */
main_running = 1;
AM53C974_main();
sti(); }
else
sti();
}

NCR5380.c:

/*
* Function : NCR5380_main (void)
*
* Purpose : NCR5380_main is a coroutine that runs as long as more work can
* be done on the NCR5380 host adapters in a system. Both
* NCR5380_queue_command() and NCR5380_intr() will try to start it
* in case it is not running.
*
* NOTE : NCR5380_main exits with interrupts *disabled*, the caller should
* reenable them. This prevents reentrancy and kernel stack overflow.
*/

static void NCR5380_main (void) {
Scsi_Cmnd *tmp, *prev;
struct Scsi_Host *instance;
struct NCR5380_hostdata *hostdata;
int done;

/*
* We run (with interrupts disabled) until we're sure that none of
* the host adapters have anything that can be done, at which point
* we set main_running to 0 and exit.
*
* Interrupts are enabled before doing various other internal
* instructions, after we've decided that we need to run through
* the loop again.
*
* this should prevent any race conditions.
*/

do {
cli(); /* Freeze request queues */
done = 1;
for (instance = first_instance; instance &&
instance->hostt == the_template; instance=instance->next) {
hostdata = (struct NCR5380_hostdata *) instance->hostdata;
cli();
if (!hostdata->connected) {
#if (NDEBUG & NDEBUG_MAIN)
printk("scsi%d : not connected\n", instance->host_no);
#endif
/*
* Search through the issue_queue for a command destined
* for a target that's not busy.
*/
for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *)
tmp->host_scribble)

/* When we find one, remove it from the issue queue. */
if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
if (prev)
prev->host_scribble = tmp->host_scribble;
else
hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
tmp->host_scribble = NULL;

/* reenable interrupts after finding one */
sti();

/*
* Attempt to establish an I_T_L nexus here.
* On success, instance->hostdata->connected is set.
* On failure, we must add the command back to the
* issue queue so we can keep trying.
*/
#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
printk("scsi%d : main() : command for target %d lun %d removed from issue_queue\n",
instance->host_no, tmp->target, tmp->lun);
#endif
/*
* REQUEST SENSE commands are issued without tagged
* queueing, even on SCSI-II devices because the
* contingent allegiance condition exists for the
* entire unit.
*/

if (!NCR5380_select(instance, tmp,
(tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE :
TAG_NEXT)) {
break;
} else {
cli();
tmp->host_scribble = (unsigned char *)
hostdata->issue_queue;
hostdata->issue_queue = tmp;
sti();
#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
printk("scsi%d : main(): select() failed, returned to issue_queue\n",
instance->host_no);
#endif
}
} /* if target/lun is not busy */
} /* if (!hostdata->connected) */

if (hostdata->connected
#ifdef REAL_DMA
&& !hostdata->dmalen
#endif
#ifdef USLEEP
&& (!hostdata->time_expires || hostdata->time_expires >= jiffies)
#endif
) {
sti();
#if (NDEBUG & NDEBUG_MAIN)
printk("scsi%d : main() : performing information transfer\n",
instance->host_no);
#endif
NCR5380_information_transfer(instance);
#if (NDEBUG & NDEBUG_MAIN)
printk("scsi%d : main() : done set false\n", instance->host_no);
#endif
done = 0;
} else
break;
} /* for instance */
} while (!done);
main_running = 0;
}

AM53c7,8xx.c:

/**************************************************************************
* Function : AM53C974_main (void)
*
* Purpose : AM53C974_main is a coroutine that runs as long as more work can
* be done on the AM53C974 host adapters in a system. Both
* AM53C974_queue_command() and AM53C974_intr() will try to start it
* in case it is not running.
*
* NOTE : AM53C974_main exits with interrupts *disabled*, the caller should
* reenable them. This prevents reentrancy and kernel stack overflow.
**************************************************************************/
static void AM53C974_main(void)
{
AM53C974_local_declare();
Scsi_Cmnd *tmp, *prev;
struct Scsi_Host *instance;
struct AM53C974_hostdata *hostdata;
int done;

/* We run (with interrupts disabled) until we're sure that none of
* the host adapters have anything that can be done, at which point
* we set main_running to 0 and exit. */

do {
cli(); /* Freeze request queues */
done = 1;
for (instance = first_instance; instance && instance->hostt == the_template;
instance = instance->next) {
hostdata = (struct AM53C974_hostdata *)instance->hostdata;
AM53C974_setio(instance);
/* start to select target if we are not connected and not in the
selection process */
if (!hostdata->connected && !hostdata->sel_cmd) {
/* Search through the issue_queue for a command destined for a target
that is not busy. */
for (tmp = (Scsi_Cmnd *)hostdata->issue_queue, prev = NULL; tmp;
prev = tmp, tmp = (Scsi_Cmnd *)tmp->host_scribble) {
/* When we find one, remove it from the issue queue. */
if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
if (prev) {
REMOVE(prev, (Scsi_Cmnd *)(prev->host_scribble), tmp,
(Scsi_Cmnd *)(tmp->host_scribble));
prev->host_scribble = tmp->host_scribble; }
else {
REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble);
hostdata->issue_queue = (Scsi_Cmnd *)tmp->host_scribble; }
tmp->host_scribble = NULL;

/* go into selection mode, disable reselection and wait for
SO interrupt which will continue with the selection process */
hostdata->selecting = 1;
hostdata->sel_cmd = tmp;
AM53C974_write_8(CMDREG, CMDREG_DSR);
break;
} /* if target/lun is not busy */

} /* for */
} /* if (!hostdata->connected) */
else {
DEB(printk("main: connected; cmd = 0x%lx, sel_cmd = 0x%lx\n",
(long)hostdata->connected, (long)hostdata->sel_cmd));
}
} /* for instance */
} while (!done);
main_running = 0;
}

Both of us use the Scsi_Pointer structure in the Scsi_Cmnd structure to
keep track of saved and active SCSI pointers; and treat them
interchangably. I have this initialization function

/*
* Function : void initialize_SCp(Scsi_Cmnd *cmd)
*
* Purpose : initialize the saved data pointers for cmd to point to the
* start of the buffer.
*
* Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
*/

static __inline__ void initialize_SCp(Scsi_Cmnd *cmd) {
/*
* Initialize the Scsi Pointer field so that all of the commands in the
* various queues are valid.
*/

if (cmd->use_sg) {
cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
cmd->SCp.buffers_residual = cmd->use_sg - 1;
cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
cmd->SCp.this_residual = cmd->SCp.buffer->length;
} else {
cmd->SCp.buffer = NULL;
cmd->SCp.buffers_residual = 0;
cmd->SCp.ptr = (char *) cmd->request_buffer;
cmd->SCp.this_residual = cmd->request_bufflen;
}
}

and you use the same function.

/**************************************************************************
* Function : void initialize_SCp(Scsi_Cmnd *cmd) *
* *
* Purpose : initialize the saved data pointers for cmd to point to the *
* start of the buffer. *
* *
* Inputs : cmd - Scsi_Cmnd structure to have pointers reset. *
* *
* Returns : nothing *
**************************************************************************/
static __inline__ void initialize_SCp(Scsi_Cmnd *cmd)
{
if (cmd->use_sg) {
cmd->SCp.buffer = (struct scatterlist *)cmd->buffer;
cmd->SCp.buffers_residual = cmd->use_sg - 1;
cmd->SCp.ptr = (char *)cmd->SCp.buffer->address;
cmd->SCp.this_residual = cmd->SCp.buffer->length; }
else {
cmd->SCp.buffer = NULL;
cmd->SCp.buffers_residual = 0;
cmd->SCp.ptr = (char *)cmd->request_buffer;
cmd->SCp.this_residual = cmd->request_bufflen; }
}

Both of us used *_information_transfer functions; IMHO a logical choice. I
move to the next scatter/gather segment like this

/*
* If there is no room left in the current buffer in the
* scatter-gather list, move onto the next one.
*/

if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
++cmd->SCp.buffer;
--cmd->SCp.buffers_residual;
cmd->SCp.this_residual = cmd->SCp.buffer->length;
cmd->SCp.ptr = cmd->SCp.buffer->address;
#if (NDEBUG & NDEBUG_INFORMATION)
printk("scsi%d : %d bytes and %d buffers left\n",

cmd->SCp.buffers_residual);
#endif
}

and you do this

if ((!cmd->SCp.this_residual) && cmd->SCp.buffers_residual) {
cmd->SCp.buffer++;
cmd->SCp.buffers_residual--;
cmd->SCp.ptr = (unsigned char *)cmd->SCp.buffer->address;
cmd->SCp.this_residual = cmd->SCp.buffer->length; }

I've got this message handling code in the NCR5380 driver's
information_transfer function

case PHASE_MSGIN:
len = 1;
data = &tmp;
NCR5380_transfer_pio(instance, &phase, &len, &data);
cmd->SCp.Message = tmp;

switch (tmp) {
/*
* Linking lets us reduce the time required to get the
* next command out to the device, hopefully this will
* mean we don't waste another revolution due to the delays
* required by ARBITRATION and another SELECTION.
*
* In the current implementation proposal, low level drivers
* merely have to start the next command, pointed to by
* next_link, done() is called as with unlinked commands.
*/
#ifdef LINKED
case LINKED_CMD_COMPLETE:
case LINKED_FLG_CMD_COMPLETE:
/* Accept message by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);

#if (NDEBUG & NDEBUG_LINKED)
printk("scsi%d : target %d lun %d linked command complete.\n",
instance->host_no, cmd->target, cmd->lun);
#endif
/*
* Sanity check : A linked command should only terminate with
* one of these messages if there are more linked commands
* available.
*/

if (!cmd->next_link) {
printk("scsi%d : target %d lun %d linked command complete, no next_link\n"
instance->host_no, cmd->target, cmd->lun);
sink = 1;
msgout = ABORT;
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
ICR_ASSERT_ATN);
break;
}

initialize_SCp(cmd->next_link);
/* The next command is still part of this process */
cmd->next_link->tag = cmd->tag;
cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
#if (NDEBUG & NDEBUG_LINKED)
printk("scsi%d : target %d lun %d linked request done, calling scsi_done().\n",
instance->host_no, cmd->target, cmd->lun);
#endif
cmd->scsi_done(cmd);
cmd = hostdata->connected;
break;
#endif /* def LINKED */
case ABORT:
case COMMAND_COMPLETE:
/* Accept message by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
hostdata->connected = NULL;
#if (NDEBUG & NDEBUG_QUEUES)
printk("scsi%d : command for target %d, lun %d completed\n",
instance->host_no, cmd->target, cmd->lun);
#endif
hostdata->busy[cmd->target] &= ~(1 << cmd->lun);

/*
* I'm not sure what the correct thing to do here is :
*
* If the command that just executed is NOT a request
* sense, the obvious thing to do is to set the result
* code to the values of the stored parameters.
*
* If it was a REQUEST SENSE command, we need some way
* to differentiate between the failure code of the original
* and the failure code of the REQUEST sense - the obvious
* case is success, where we fall through and leave the result
* code unchanged.
*
* The non-obvious place is where the REQUEST SENSE failed
*/

if (cmd->cmnd[0] != REQUEST_SENSE)
cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
else if (cmd->SCp.Status != GOOD)
cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);

#ifdef AUTOSENSE
if ((cmd->cmnd[0] != REQUEST_SENSE) &&
(cmd->SCp.Status == CHECK_CONDITION)) {
#if (NDEBUG & NDEBUG_AUTOSENSE)
printk("scsi%d : performing request sense\n",
instance->host_no);
#endif
cmd->cmnd[0] = REQUEST_SENSE;
cmd->cmnd[1] &= 0xe0;
cmd->cmnd[2] = 0;
cmd->cmnd[3] = 0;
cmd->cmnd[4] = sizeof(cmd->sense_buffer);
cmd->cmnd[5] = 0;

cmd->SCp.buffer = NULL;
cmd->SCp.buffers_residual = 0;
cmd->SCp.ptr = (char *) cmd->sense_buffer;
cmd->SCp.this_residual = sizeof(cmd->sense_buffer);

cli();
cmd->host_scribble = (unsigned char *)
hostdata->issue_queue;
hostdata->issue_queue = (Scsi_Cmnd *) cmd;
sti();
#if (NDEBUG & NDEBUG_QUEUES)
printk("scsi%d : REQUEST SENSE added to head of issue queue\n");
#endif
} else
#endif /* def AUTOSENSE */
cmd->scsi_done(cmd);

NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
/*
* Restore phase bits to 0 so an interrupted selection,
* arbitration can resume.
*/
NCR5380_write(TARGET_COMMAND_REG, 0);

while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
barrier();
return;
case MESSAGE_REJECT:
/* Accept message by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
switch (hostdata->last_message) {
case HEAD_OF_QUEUE_TAG:
case ORDERED_QUEUE_TAG:
case SIMPLE_QUEUE_TAG:
cmd->device->tagged_queue = 0;
hostdata->busy[cmd->target] |= (1 << cmd->lun);
break;
default:
break;
}
case DISCONNECT:
/* Accept message by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
cmd->device->disconnect = 1;
cli();
cmd->host_scribble = (unsigned char *)
hostdata->disconnected_queue;
hostdata->connected = NULL;
hostdata->disconnected_queue = cmd;
sti();
#if (NDEBUG & NDEBUG_QUEUES)
printk("scsi%d : command for target %d lun %d was moved from connected to"
" the disconnected_queue\n", instance->host_no,
cmd->target, cmd->lun);
#endif
/*
* Restore phase bits to 0 so an interrupted selection,
* arbitration can resume.
*/
NCR5380_write(TARGET_COMMAND_REG, 0);

/* Enable reselect interrupts */
NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
/* Wait for bus free to avoid nasty timeouts */
while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
barrier();
return;
/*
* The SCSI data pointer is *IMPLICITLY* saved on a disconnect
* operation, in violation of the SCSI spec so we can safely
* ignore SAVE/RESTORE pointers calls.
*
* Unfortunately, some disks violate the SCSI spec and
* don't issue the required SAVE_POINTERS message before
* disconnecting, and we have to break spec to remain
* compatible.
*/
case SAVE_POINTERS:
case RESTORE_POINTERS:
/* Accept message by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
break;
case EXTENDED_MESSAGE:
/*
* Extended messages are sent in the following format :
* Byte
* 0 EXTENDED_MESSAGE == 1
* 1 length (includes one byte for code, doesn't
* include first two bytes)
* 2 code
* 3..length+1 arguments
*
* Start the extended message buffer with the EXTENDED_MESSAGE
* byte, since print_msg() wants the whole thing.
*/
extended_msg[0] = EXTENDED_MESSAGE;
/* Accept first byte by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);

#if (NDEBUG & NDEBUG_EXTENDED)
printk("scsi%d : receiving extended message\n",
instance->host_no);
#endif

len = 2;
data = extended_msg + 1;
phase = PHASE_MSGIN;
NCR5380_transfer_pio(instance, &phase, &len, &data);

#if (NDEBUG & NDEBUG_EXTENDED)
printk("scsi%d : length=%d, code=0x%02x\n",
instance->host_no, (int) extended_msg[1],
(int) extended_msg[2]);
#endif

if (!len && extended_msg[1] <=
(sizeof (extended_msg) - 1)) {
/* Accept third byte by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
len = extended_msg[1] - 1;
data = extended_msg + 3;
phase = PHASE_MSGIN;

NCR5380_transfer_pio(instance, &phase, &len, &data);

#if (NDEBUG & NDEBUG_EXTENDED)
printk("scsi%d : message received, residual %d\n",
instance->host_no, len);
#endif

switch (extended_msg[2]) {
case EXTENDED_SDTR:
case EXTENDED_WDTR:
case EXTENDED_MODIFY_DATA_POINTER:
case EXTENDED_EXTENDED_IDENTIFY:
tmp = 0;
}
} else if (len) {
printk("scsi%d: error receiving extended message\n",
instance->host_no);
tmp = 0;
} else {
printk("scsi%d: extended message code %02x length %d is too long\n",
instance->host_no, extended_msg[2], extended_msg[1]);
tmp = 0;
}
/* Fall through to reject message */

/*
* If we get something weird that we aren't expecting,
* reject it.
*/
default:
if (!tmp) {
printk("scsi%d: rejecting message ", instance->host_no);
print_msg (extended_msg);
printk("\n");
} else if (tmp != EXTENDED_MESSAGE)
printk("scsi%d: rejecting unknown message %02x from target %d, lun %d\n",
instance->host_no, tmp, cmd->target, cmd->lun);
else
printk("scsi%d: rejecting unknown extended message code %02x, length %d from target %d, lun %d\n",
instance->host_no, extended_msg[1], extended_msg[0], cmd->target, cmd->lun);

msgout = MESSAGE_REJECT;
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
ICR_ASSERT_ATN);
break;
} /* switch (tmp) */
break;

you split this code out into the AM53C974_message function :

/******************************************************************************
* Function : int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd,
* unsigned char msg)
*
* Purpose : handle SCSI messages
*
* Inputs : instance -- which AM53C974
* cmd -- SCSI command the message belongs to
* msg -- message id byte
*
* Returns : 1 on success, 0 on failure.
**************************************************************************/
static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd,
unsigned char msg)
{
AM53C974_local_declare();
static unsigned char extended_msg[10];
unsigned char statreg;
int len, ret = 0;
unsigned char *p;
#ifdef AM53C974_DEBUG_MSG
int j;
#endif
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
AM53C974_setio(instance);

DEB_MSG(printk(SEPARATOR_LINE));

/* Linking lets us reduce the time required to get the
* next command out to the device, hopefully this will
* mean we don't waste another revolution due to the delays
* required by ARBITRATION and another SELECTION.
* In the current implementation proposal, low level drivers
* merely have to start the next command, pointed to by
* next_link, done() is called as with unlinked commands. */
switch (msg) {
#ifdef LINKED
case LINKED_CMD_COMPLETE:
case LINKED_FLG_CMD_COMPLETE:
/* Accept message by releasing ACK */
DEB_LINKED(printk("scsi%d : target %d lun %d linked command complete.\n",
instance->host_no, cmd->target, cmd->lun));
/* Sanity check : A linked command should only terminate with
* one of these messages if there are more linked commands available. */
if (!cmd->next_link) {
printk("scsi%d : target %d lun %d linked command complete, no next_link\n"
instance->host_no, cmd->target, cmd->lun);
hostdata->aborted = 1;
AM53C974_write_8(CMDREG, CMDREG_SATN);
AM53C974_write_8(CMDREG, CMDREG_MA);
break; }
if (hostdata->aborted) {
DEB_ABORT(printk("ATN set for cmnd %d upon reception of LINKED_CMD_COMPLETE or"
"LINKED_FLG_CMD_COMPLETE message\n", cmd->cmnd[0]));
AM53C974_write_8(CMDREG, CMDREG_SATN); }
AM53C974_write_8(CMDREG, CMDREG_MA);

initialize_SCp(cmd->next_link);
/* The next command is still part of this process */
cmd->next_link->tag = cmd->tag;
cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
DEB_LINKED(printk("scsi%d : target %d lun %d linked request done, calling scsi_done().\n",
instance->host_no, cmd->target, cmd->lun));
cmd->scsi_done(cmd);
cmd = hostdata->connected;
break;

#endif /* def LINKED */

case ABORT:
case COMMAND_COMPLETE:
DEB_MSG(printk("scsi%d: command complete message received; cmd %d for target %d, lun %d\n",
instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
hostdata->disconnecting = 1;
cmd->device->disconnect = 0;

/* I'm not sure what the correct thing to do here is :
*
* If the command that just executed is NOT a request
* sense, the obvious thing to do is to set the result
* code to the values of the stored parameters.
* If it was a REQUEST SENSE command, we need some way
* to differentiate between the failure code of the original
* and the failure code of the REQUEST sense - the obvious
* case is success, where we fall through and leave the result
* code unchanged.
*
* The non-obvious place is where the REQUEST SENSE failed */
if (cmd->cmnd[0] != REQUEST_SENSE)
cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
else if (cmd->SCp.Status != GOOD)
cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
if (hostdata->aborted) {
AM53C974_write_8(CMDREG, CMDREG_SATN);
AM53C974_write_8(CMDREG, CMDREG_MA);
DEB_ABORT(printk("ATN set for cmnd %d upon reception of ABORT or"
"COMMAND_COMPLETE message\n", cmd->cmnd[0]));
break; }
if ((cmd->cmnd[0] != REQUEST_SENSE) && (cmd->SCp.Status == CHECK_CONDITION)) {
DEB_MSG(printk("scsi%d : performing request sense\n", instance->host_no));
cmd->cmnd[0] = REQUEST_SENSE;
cmd->cmnd[1] &= 0xe0;
cmd->cmnd[2] = 0;
cmd->cmnd[3] = 0;
cmd->cmnd[4] = sizeof(cmd->sense_buffer);
cmd->cmnd[5] = 0;
cmd->SCp.buffer = NULL;
cmd->SCp.buffers_residual = 0;
cmd->SCp.ptr = (char *)cmd->sense_buffer;
cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
LIST(cmd,hostdata->issue_queue);
cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
hostdata->issue_queue = (Scsi_Cmnd *)cmd;
DEB_MSG(printk("scsi%d : REQUEST SENSE added to head of issue queue\n",instance->host_no));
}

/* Accept message by clearing ACK */
AM53C974_write_8(CMDREG, CMDREG_MA);
break;

case MESSAGE_REJECT:
DEB_MSG(printk("scsi%d: reject message received; cmd %d for target %d, lun %d\n",
instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
switch (hostdata->last_message[0]) {
case EXTENDED_MESSAGE:
if (hostdata->last_message[2] == EXTENDED_SDTR) {
/* sync. negotiation was rejected, setup asynchronous transfer with target */
printk("\ntarget %d: rate=%d Mhz, asynchronous (sync. negotiation rejected)\n",
cmd->target, DEF_CLK / DEF_STP);
hostdata->sync_off[cmd->target] = 0;
hostdata->sync_per[cmd->target] = DEF_STP; }
break;
case HEAD_OF_QUEUE_TAG:
case ORDERED_QUEUE_TAG:
case SIMPLE_QUEUE_TAG:
cmd->device->tagged_queue = 0;
hostdata->busy[cmd->target] |= (1 << cmd->lun);
break;
default:
break;
}
if (hostdata->aborted) AM53C974_write_8(CMDREG, CMDREG_SATN);
AM53C974_write_8(CMDREG, CMDREG_MA);
break;

case DISCONNECT:
DEB_MSG(printk("scsi%d: disconnect message received; cmd %d for target %d, lun %d\n",
instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
cmd->device->disconnect = 1;
hostdata->disconnecting = 1;
AM53C974_write_8(CMDREG, CMDREG_MA); /* Accept message by clearing ACK */
break;

case SAVE_POINTERS:
case RESTORE_POINTERS:
DEB_MSG(printk("scsi%d: save/restore pointers message received; cmd %d for target %d, lun %d\n",
instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
/* The SCSI data pointer is *IMPLICITLY* saved on a disconnect
* operation, in violation of the SCSI spec so we can safely
* ignore SAVE/RESTORE pointers calls.
*
* Unfortunately, some disks violate the SCSI spec and
* don't issue the required SAVE_POINTERS message before
* disconnecting, and we have to break spec to remain
* compatible. */
if (hostdata->aborted) {
DEB_ABORT(printk("ATN set for cmnd %d upon reception of SAVE/REST. POINTERS message\n",
cmd->cmnd[0]));
AM53C974_write_8(CMDREG, CMDREG_SATN); }
AM53C974_write_8(CMDREG, CMDREG_MA);
break;

case EXTENDED_MESSAGE:
DEB_MSG(printk("scsi%d: extended message received; cmd %d for target %d, lun %d\n",
instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
/* Extended messages are sent in the following format :
* Byte
* 0 EXTENDED_MESSAGE == 1
* 1 length (includes one byte for code, doesn't include first two bytes)
* 2 code
* 3..length+1 arguments
*/
/* BEWARE!! THIS CODE IS EXTREMELY UGLY */
extended_msg[0] = EXTENDED_MESSAGE;
AM53C974_read_8(INSTREG) ; /* clear int */
AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */
AM53C974_poll_int();
/* get length */
AM53C974_write_8(CMDREG, CMDREG_IT);
AM53C974_poll_int();
AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */
AM53C974_poll_int();
extended_msg[1] = len = AM53C974_read_8(FFREG); /* get length */
p = extended_msg+2;
/* read the remaining (len) bytes */
while (len) {
AM53C974_write_8(CMDREG, CMDREG_IT);
AM53C974_poll_int();
if (len > 1) {
AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */
AM53C974_poll_int(); }
*p = AM53C974_read_8(FFREG);
p++; len--; }

#ifdef AM53C974_DEBUG_MSG
printk("scsi%d: received extended message: ", instance->host_no);
for (j = 0; j < extended_msg[1] + 2; j++) {
printk("0x%02x ", extended_msg[j]);
if (j && !(j % 16)) printk("\n"); }
printk("\n");
#endif

/* check message */
if (extended_msg[2] == EXTENDED_SDTR)
ret = AM53C974_sync_neg(instance, cmd->target, extended_msg);
if (ret || hostdata->aborted) AM53C974_write_8(CMDREG, CMDREG_SATN);

AM53C974_write_8(CMDREG, CMDREG_MA);
break;

default:
printk("scsi%d: unknown message 0x%02x received\n",instance->host_no, msg);
#ifdef AM53C974_DEBUG
deb_stop = 1;
#endif
/* reject message */
hostdata->msgout[0] = MESSAGE_REJECT;
AM53C974_write_8(CMDREG, CMDREG_SATN);
AM53C974_write_8(CMDREG, CMDREG_MA);
return(0);
break;

} /* switch (msg) */
KEYWAIT();
return(1);
}

I handle reselection like this :

len = 1;
data = msg;
phase = PHASE_MSGIN;
NCR5380_transfer_pio(instance, &phase, &len, &data);

if (!msg[0] & 0x80) {
printk("scsi%d : expecting IDENTIFY message, got ",
instance->host_no);
print_msg(msg);
abort = 1;
} else {
/* Accept message by clearing ACK */
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
lun = (msg[0] & 0x07);

/*
* We need to add code for SCSI-II to track which devices have
* I_T_L_Q nexuses established, and which have simple I_T_L
* nexuses so we can chose to do additional data transfer.
*/

#ifdef SCSI2
#error "SCSI-II tagged queueing is not supported yet"
#endif

/*
* Find the command corresponding to the I_T_L or I_T_L_Q nexus we
* just reestablished, and remove it from the disconnected queue.
*/

for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL;
tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble)
if ((target_mask == (1 << tmp->target)) && (lun == tmp->lun)
#ifdef SCSI2
&& (tag == tmp->tag)
#endif
) {
if (prev)
prev->host_scribble = tmp->host_scribble;
else
hostdata->disconnected_queue = (Scsi_Cmnd *) tmp->host_scribble;
tmp->host_scribble = NULL;
break;
}

if (!tmp) {
#ifdef SCSI2
printk("scsi%d : warning : target bitmask %02x lun %d tag %d not in disconnect_queue.\n",
instance->host_no, target_mask, lun, tag);
#else
printk("scsi%d : warning : target bitmask %02x lun %d not in disconnect_queue.\n",
instance->host_no, target_mask, lun);
#endif
/*
* Since we have an established nexus that we can't do anything with,
* we must abort it.
*/
abort = 1;
}
}

if (abort) {
msg[0] = ABORT;
len = 1;
data = msg;
phase = PHASE_MSGOUT;
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
NCR5380_transfer_pio(instance, &phase, &len, &data);
} else {
hostdata->connected = tmp;
#if (NDEBUG & NDEBUG_RESELECTION)
printk"scsi%d : nexus established, target = %d, lun = %d, tag = %d\n",
instance->host_no, tmp->target, tmp->lun, tmp->tag);
#endif
}

And you handle it like this :

msg[0] = AM53C974_read_8(FFREG);
if (!msg[0] & 0x80) {
printk("scsi%d: error: expecting IDENTIFY message, got ", instance->host_no);
print_msg(msg);
hostdata->aborted = 1;
goto EXIT_ABORT; }

lun = (msg[0] & 0x07);

/* We need to add code for SCSI-II to track which devices have
* I_T_L_Q nexuses established, and which have simple I_T_L
* nexuses so we can chose to do additional data transfer. */
#ifdef SCSI2
#error "SCSI-II tagged queueing is not supported yet"
#endif

/* Find the command corresponding to the I_T_L or I_T_L_Q nexus we
* just reestablished, and remove it from the disconnected queue. */
for (tmp = (Scsi_Cmnd *)hostdata->disconnected_queue, prev = NULL;
tmp; prev = tmp, tmp = (Scsi_Cmnd *)tmp->host_scribble)
if ((target == tmp->target) && (lun == tmp->lun)
#ifdef SCSI2
&& (tag == tmp->tag)
#endif
) {
if (prev) {
REMOVE(prev, (Scsi_Cmnd *)(prev->host_scribble), tmp,
(Scsi_Cmnd *)(tmp->host_scribble));
prev->host_scribble = tmp->host_scribble; }
else {
REMOVE(-1, hostdata->disconnected_queue, tmp, tmp->host_scribble);
hostdata->disconnected_queue = (Scsi_Cmnd *)tmp->host_scribble; }
tmp->host_scribble = NULL;
hostdata->connected = tmp;
break; }

if (!tmp) {
#ifdef SCSI2
printk("scsi%d: warning : target %d lun %d tag %d not in disconnect_queue.\n",
instance->host_no, target, lun, tag);
#else
printk("scsi%d: warning : target %d lun %d not in disconnect_queue.\n",
instance->host_no, target, lun);
#endif
/* Since we have an established nexus that we can't do anything with, we must abort it. */
hostdata->aborted = 1;
DEB(AM53C974_keywait());
goto EXIT_ABORT; }
else
goto EXIT_OK;

EXIT_ABORT:
AM53C974_write_8(CMDREG, CMDREG_SATN);
AM53C974_write_8(CMDREG, CMDREG_MA);
return;

EXIT_OK:
DEB_RESEL(printk("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
instance->host_no, target, tmp->lun, tmp->tag));
AM53C974_set_sync(instance, target);
AM53C974_write_8(SDIDREG, SDIREG_MASK & target); /* setup dest. id */
AM53C974_write_8(CMDREG, CMDREG_MA);
hostdata->dma_busy = 0;
hostdata->connected->SCp.phase = PHASE_CMDOUT;

When something goes wrong, I abort the current command like this

/*
* Function : int NCR5380_abort (Scsi_Cmnd *cmd)
*
* Purpose : abort a command
*
* Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
* host byte of the result field to, if zero DID_ABORTED is
* used.
*
* Returns : 0 - success, -1 on failure.
*
* XXX - there is no way to abort the command that is currently
* connected, you have to wait for it to complete. If this is
* a problem, we could implement longjmp() / setjmp(), setjmp()
* called where the loop started in NCR5380_main().
*/

#ifndef NCR5380_abort
static
#endif
int NCR5380_abort (Scsi_Cmnd *cmd) {
NCR5380_local_declare();
struct Scsi_Host *instance = cmd->host;
struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
instance->hostdata;
Scsi_Cmnd *tmp, **prev;
unsigned char msg, phase, *msgptr;
int len;

printk("scsi%d : aborting command\n", instance->host_no);
print_Scsi_Cmnd (cmd);

NCR5380_print_status (instance);

cli();
NCR5380_setup(instance);

#if (NDEBUG & NDEBUG_ABORT)
printk("scsi%d : abort called\n", instance->host_no);
printk(" basr 0x%X, sr 0x%X\n",
NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG));
#endif

#if 0
/*
* Case 1 : If the command is the currently executing command,
* we'll set the aborted flag and return control so that
* information transfer routine can exit cleanly.
*/

if (hostdata->connected == cmd) {
#if (NDEBUG & NDEBUG_ABORT)
printk("scsi%d : aborting connected command\n", instance->host_no);
#endif
hostdata->aborted = 1;
/*
* We should perform BSY checking, and make sure we haven't slipped
* into BUS FREE.
*/

NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN);
/*
* Since we can't change phases until we've completed the current
* handshake, we have to source or sink a byte of data if the current
* phase is not MSGOUT.
*/

/*
* Return control to the executing NCR drive so we can clear the
* aborted flag and get back into our main loop.
*/

return 0;
}
#endif

/*
* Case 2 : If the command hasn't been issued yet, we simply remove it
* from the issue queue.
*/
for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue),
tmp = (Scsi_Cmnd *) hostdata->issue_queue;
tmp; prev = (Scsi_Cmnd **) &(tmp->host_scribble), tmp =
(Scsi_Cmnd *) tmp->host_scribble)
if (cmd == tmp) {
(*prev) = (Scsi_Cmnd *) tmp->host_scribble;
tmp->host_scribble = NULL;
tmp->result = DID_ABORT << 16;
sti();
#if (NDEBUG & NDEBUG_ABORT)
printk("scsi%d : abort removed command from issue queue.\n",
instance->host_no);
#endif
tmp->done(tmp);
return SCSI_ABORT_SUCCESS;
}

/*
* Case 3 : If any commands are connected, we're going to fail the abort
* and let the high level SCSI driver retry at a later time or
* issue a reset.
*
* Timeouts, and therefore aborted commands, will be highly unlikely
* and handling them cleanly in this situation would make the common
* case of noresets less efficient, and would pollute our code. So,
* we fail.
*/

if (hostdata->connected) {
sti();
#if (NDEBUG & NDEBUG_ABORT)
printk("scsi%d : abort failed, command connected.\n", instance->host_no);
#endif
return SCSI_ABORT_NOT_RUNNING;
}

/*
* Case 4: If the command is currently disconnected from the bus, and
* there are no connected commands, we reconnect the I_T_L or
* I_T_L_Q nexus associated with it, go into message out, and send
* an abort message.
*
* This case is especially ugly. In order to reestablish the nexus, we
* need to call NCR5380_select(). The easiest way to implement this
* function was to abort if the bus was busy, and let the interrupt
* handler triggered on the SEL for reselect take care of lost arbitrations
* where necessary, meaning interrupts need to be enabled.
*
* When interrupts are enabled, the queues may change - so we
* can't remove it from the disconnected queue before selecting it
* because that could cause a failure in hashing the nexus if that
* device reselected.
*
* Since the queues may change, we can't use the pointers from when we
* first locate it.
*
* So, we must first locate the command, and if NCR5380_select()
* succeeds, then issue the abort, relocate the command and remove
* it from the disconnected queue.
*/

for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp;
tmp = (Scsi_Cmnd *) tmp->host_scribble)
if (cmd == tmp) {
sti();
#if (NDEBUG & NDEBUG_ABORT)
printk("scsi%d : aborting disconnected command.\n", instance->host_no);
#endif

if (NCR5380_select (instance, cmd, (int) cmd->tag))
return SCSI_ABORT_BUSY;

#if (NDEBUG & NDEBUG_ABORT)
printk("scsi%d : nexus reestablished.\n", instance->host_no);
#endif

msg = ABORT;
msgptr = &msg;
len = 1;
phase = PHASE_MSGOUT;
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
NCR5380_transfer_pio (instance, &phase, &len, &msgptr);

cli();
for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue),
tmp = (Scsi_Cmnd *) hostdata->disconnected_queue;
tmp; prev = (Scsi_Cmnd **) &(tmp->host_scribble), tmp =
(Scsi_Cmnd *) tmp->host_scribble)
if (cmd == tmp) {
*prev = (Scsi_Cmnd *) tmp->host_scribble;
tmp->host_scribble = NULL;
tmp->result = DID_ABORT << 16;
sti();
tmp->done(tmp);
return SCSI_ABORT_SUCCESS;
}
}

/*
* Case 5 : If we reached this point, the command was not found in any of
* the queues.
*
* We probably reached this point because of an unlikely race condition
* between the command completing successfully and the abortion code,
* so we won't panic, but we will notify the user in case something really
* broke.
*/

sti();
printk("scsi%d : warning : SCSI command probably completed successfully\n"
" before abortion\n", instance->host_no);
return SCSI_ABORT_NOT_RUNNING;
}

And you do it like this

/**************************************************************************
* Function : int AM53C974_abort(Scsi_Cmnd *cmd)
*
* Purpose : abort a command
*
* Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
* host byte of the result field to, if zero DID_ABORTED is
* used.
*
* Returns : 0 - success, -1 on failure.
**************************************************************************/
int AM53C974_abort(Scsi_Cmnd *cmd)
{
AM53C974_local_declare();
struct Scsi_Host *instance = cmd->host;
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
Scsi_Cmnd *tmp, **prev;

#ifdef AM53C974_DEBUG
deb_stop = 1;
#endif
cli();
AM53C974_setio(instance);

DEB_ABORT(printk(SEPARATOR_LINE));
DEB_ABORT(printk("scsi%d : AM53C974_abort called -- trouble starts!!\n", instance->host_no));
DEB_ABORT(AM53C974_print(instance));
DEB_ABORT(AM53C974_keywait());

/* Case 1 : If the command is the currently executing command,
we'll set the aborted flag and return control so that the
information transfer routine can exit cleanly. */
if ((hostdata->connected == cmd) || (hostdata->sel_cmd == cmd)) {
DEB_ABORT(printk("scsi%d: aborting connected command\n", instance->host_no));
hostdata->aborted = 1;
hostdata->msgout[0] = ABORT;
sti();
return(SCSI_ABORT_PENDING); }

/* Case 2 : If the command hasn't been issued yet,
we simply remove it from the issue queue. */
for (prev = (Scsi_Cmnd **)&(hostdata->issue_queue),
tmp = (Scsi_Cmnd *)hostdata->issue_queue; tmp;
prev = (Scsi_Cmnd **)&(tmp->host_scribble),
tmp = (Scsi_Cmnd *)tmp->host_scribble) {
if (cmd == tmp) {
DEB_ABORT(printk("scsi%d : abort removed command from issue queue.\n", instance->host_no));
REMOVE(5, *prev, tmp, tmp->host_scribble);
(*prev) = (Scsi_Cmnd *)tmp->host_scribble;
tmp->host_scribble = NULL;
tmp->result = DID_ABORT << 16;
sti();
tmp->done(tmp);
return(SCSI_ABORT_SUCCESS); }
#ifdef AM53C974_DEBUG_ABORT
else {
if (prev == (Scsi_Cmnd **)tmp)
printk("scsi%d : LOOP\n", instance->host_no);
}
#endif
}

/* Case 3 : If any commands are connected, we're going to fail the abort
* and let the high level SCSI driver retry at a later time or
* issue a reset.
*
* Timeouts, and therefore aborted commands, will be highly unlikely
* and handling them cleanly in this situation would make the common
* case of noresets less efficient, and would pollute our code. So,
* we fail. */
if (hostdata->connected || hostdata->sel_cmd) {
DEB_ABORT(printk("scsi%d : abort failed, other command connected.\n", instance->host_no));
sti();
return(SCSI_ABORT_NOT_RUNNING); }

/* Case 4: If the command is currently disconnected from the bus, and
* there are no connected commands, we reconnect the I_T_L or
* I_T_L_Q nexus associated with it, go into message out, and send
* an abort message. */
for (tmp = (Scsi_Cmnd *)hostdata->disconnected_queue; tmp;
tmp = (Scsi_Cmnd *)tmp->host_scribble) {
if (cmd == tmp) {
DEB_ABORT(printk("scsi%d: aborting disconnected command\n", instance->host_no));
hostdata->aborted = 1;
hostdata->msgout[0] = ABORT;
hostdata->selecting = 1;
hostdata->sel_cmd = tmp;
AM53C974_write_8(CMDREG, CMDREG_DSR);
sti();
return(SCSI_ABORT_PENDING); }
}

/* Case 5 : If we reached this point, the command was not found in any of
* the queues.
*
* We probably reached this point because of an unlikely race condition
* between the command completing successfully and the abortion code,
* so we won't panic, but we will notify the user in case something really
* broke. */
DEB_ABORT(printk("scsi%d : abort failed, command not found.\n", instance->host_no));
sti();
return(SCSI_ABORT_NOT_RUNNING);
}

We use the same sort of debugging code for dumping the current SCSI phase

static struct {
unsigned char value;
char *name;
} phases[] = {
{PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
{PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
{PHASE_UNKNOWN, "UNKNOWN"}};

/*
* Function : void NCR5380_print_phase(struct Scsi_Host *instance)
*
* Purpose : print the current SCSI phase for debugging purposes
*
* Input : instance - which NCR5380
*/

static void NCR5380_print_phase(struct Scsi_Host *instance) {
NCR5380_local_declare();
unsigned char status;
int i;
NCR5380_setup(instance);

status = NCR5380_read(STATUS_REG);
if (!(status & SR_REQ))
printk("scsi%d : REQ not asserted, phase unknown.\n",
instance->host_no);
else {
for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
(phases[i].value != (status & PHASE_MASK)); ++i);
printk("scsi%d : phase %s\n", instance->host_no, phases[i].name);
}
static struct {
unsigned char value;
char *name;
} phases[] = {
{PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
{PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
{PHASE_RES_0, "RESERVED 0"}, {PHASE_RES_1, "RESERVED 1"}};

/**************************************************************************
* Function : void AM53C974_print_phase(struct Scsi_Host *instance)
*
* Purpose : print the current SCSI phase for debugging purposes
*
* Input : instance - which AM53C974
**************************************************************************/
static void AM53C974_print_phase(struct Scsi_Host *instance)
{
AM53C974_local_declare();
unsigned char statreg, latched;
int i;
AM53C974_setio(instance);

latched = (AM53C974_read_8(CNTLREG2)) & CNTLREG2_ENF;
statreg = AM53C974_read_8(STATREG);
for (i = 0; (phases[i].value != PHASE_RES_1) &&
(phases[i].value != (statreg & STATREG_PHASE)); ++i);
if (latched)
printk("scsi%d : phase %s, latched at end of last command\n", instance->host_no, phases[i].name);
else
printk("scsi%d : phase %s, real time\n", instance->host_no, phases[i].name);

And almost the same code to dump out our queues

/*
* Function : void NCR5380_print_status (struct Scsi_Host *instance)
*
* Purpose : print commands in the various queues, called from
* NCR5380_abort and NCR5380_debug to aid debugging.
*
* Inputs : instance, pointer to this instance.
*/

static void NCR5380_print_status (struct Scsi_Host *instance) {
struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
instance->hostdata;
Scsi_Cmnd *ptr;

printk("NCR5380 : coroutine is%s running.\n",
main_running ? "" : "n't");

#ifdef NDEBUG
NCR5380_print (instance);
NCR5380_print_phase (instance);
#endif

cli();
if (!hostdata->connected) {
printk ("scsi%d: no currently connected command\n",
instance->host_no);
} else {
print_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected);
}

printk ("scsi%d: issue_queue\n", instance->host_no);

for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr;
ptr = (Scsi_Cmnd *) ptr->host_scribble)
print_Scsi_Cmnd (ptr);

printk ("scsi%d: disconnected_queue\n", instance->host_no);

for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
ptr = (Scsi_Cmnd *) ptr->host_scribble)
print_Scsi_Cmnd (ptr);

sti();
}

/**************************************************************************
* Function : void AM53C974_print_queues(struct Scsi_Host *instance)
*
* Purpose : print commands in the various queues
*
* Inputs : instance - which AM53C974
**************************************************************************/
static void AM53C974_print_queues(struct Scsi_Host *instance)
{
struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
Scsi_Cmnd *ptr;

printk("AM53C974: coroutine is%s running.\n", main_running ? "" : "n't");

cli();

if (!hostdata->connected) {
printk ("scsi%d: no currently connected command\n", instance->host_no); }
else {
print_Scsi_Cmnd ((Scsi_Cmnd *)hostdata->connected); }
if (!hostdata->sel_cmd) {
printk ("scsi%d: no currently arbitrating command\n", instance->host_no); }
else {
print_Scsi_Cmnd ((Scsi_Cmnd *)hostdata->sel_cmd); }

printk ("scsi%d: issue_queue ", instance->host_no);
if (!hostdata->issue_queue)
printk("empty\n");
else {
printk(":\n");
for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *)ptr->host_scribble)
print_Scsi_Cmnd (ptr); }

printk ("scsi%d: disconnected_queue ", instance->host_no);
if (!hostdata->disconnected_queue)
printk("empty\n");
else {
printk(":\n");
for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *)ptr->host_scribble)
print_Scsi_Cmnd (ptr); }

sti();
}

There are other places where the same code shows up as well. Given the
large quantity of code which is functionally identical, I find it highly
likely that one of us copied from the other.

The copyright dates here

NCR5380.c:
* Copyright 1993, Drew Eckhardt
* Visionary Computing
* (Unix and Linux consulting and custom programming)
* drew@colorado.edu

53c7,8xx.c:
* Copyright 1993, 1994 Drew Eckhardt
* Visionary Computing
* (Unix and Linux consulting and custom programming)
* drew@Colorado.EDU
* +1 (303) 786-7975

README.AM53C974:
Copyright
---------
Copyright 1994-1995, D. Frieauff

would indicate that my drivers predate yours, particularly the
NCR5380 driver, which would mean you copying from me (unless I have a
time machine I'm not telling anyone about).

The bison.hairy yacc skeleton is about 300 lines long, and including it
in a program's parser is enough to make that program a derrived work of
bison. There appears to be substantially more code in common in this
case, which would suggest that the AM53C974 driver is a derrived work
of my NCR5380 driver with hints of my NCR53c810 driver thrown in
for good measure.

Code reuse is good. Anyone who wants to use my code in free software
is usually welcome to it provided the copyrights remain intact, weather
they prefer a BSD or GPL'd licensing system. Unfortunately, there's
appears to be more of my code included in your driver than 'fair use'
would allow, and I don't see my copyrights in there anywhere. Although I
don't mind very much if a single individual has gone and reused my code
without credit, I'd be more than a bit upset if a business entity were to
consider this precident for copyright non-enforcement and abuse the spirit
of things. So, I'd like an explanation and mutually agreeable fix ASAP.

PS: the volatiles were there for a reason. Also, if you want multiple
host adapters to work, you want to change the shared-interrupt
detection code to something like this :

for (search = first_host; search && !(search->hostt == the_template &&
search->irq == host->irq && search != host); search=search->next);

if (!search) {
if (request_irq(host->irq, NCR53c7x0_intr, SA_INTERRUPT, "53c7,8xx")) {
printk("scsi%d : IRQ%d not free, detaching\n",
host->host_no, host->irq);
scsi_unregister (host);
return -1;
}
} else {
printk("scsi%d : using interrupt handler previously installed for scsi%d\n",
host->host_no, search->host_no);
}

note that your host is in the list, and you need an additional check for it;
otherwise you don't install an interrupt handler for your second board.
Also, you might want to initialize the_template to something other than
NULL (like host->hostt) before you use it.

PPS: Any other software types interested in comparing the original files
themselves can take a look at /usr/src/linux/drivers/scsi/NCR5380.{c,
h} from any Linux since the Fall '93 Yggdrasil; and
ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/AM53C974-0.3.tar.gz