📄 ncr5380.c
字号:
} else {
printk("No overrun??\n");
cnt = toPIO = 2;
}
#if (NDEBUG & NDEBUG_DMA)
printk("Doing %d-byte PIO to 0x%X\n", cnt, *data);
#endif
NCR5380_transfer_pio(instance, phase, &cnt, data);
*count -= toPIO - cnt;
}
#endif
#if (NDEBUG & NDEBUG_DMA)
printk("Return with data ptr = 0x%X, count %d, last 0x%X, next 0x%X\n",
*data, *count, *(*data+*count-1), *(*data+*count));
#endif
return 0;
#elif defined(REAL_DMA)
return 0;
#else /* defined(REAL_DMA_POLL) */
if (p & SR_IO) {
if (!(foo = NCR5380_pread(instance, d, c - 1))) {
/*
* We can't disable DMA mode after successfully transfering
* what we plan to be the last byte, since that would open up
* a race condition where if the target asserted REQ before
* we got the DMA mode reset, the NCR5380 would have latched
* an additional byte into the INPUT DATA register and we'd
* have dropped it.
*
* The workarround was to transfer one fewer bytes than we
* intended to with the pseudo-DMA read function, wait for
* the chip to latch the last byte, read it, and then disable
* pseudo-DMA mode.
*
* After REQ is asserted, the NCR5380 asserts DRQ and ACK.
* REQ is deasserted when ACK is asserted, and not reasserted
* until ACK goes false. Since the NCR5380 won't lower ACK
* until DACK is asserted, which won't happen unless we twiddle
* the DMA port or we take the NCR5380 out of DMA mode, we
* can gurantee that we won't handshake another extra
* byte.
*/
while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
/* Wait for clean handshake */
while (NCR5380_read(STATUS_REG) & SR_REQ);
d[c - 1] = NCR5380_read(INPUT_DATA_REG);
}
} else {
int timeout;
if (!(foo = NCR5380_pwrite(instance, d, c))) {
/*
* Wait for the last byte to be sent. If REQ is being asserted for
* the byte we're interested, we'll ACK it and it will go false.
*/
if (!(hostdata->flags & FLAG_HAS_LAST_BYTE_SENT)) {
timeout = 20000;
#if 1
#if 1
while (!(NCR5380_read(BUS_AND_STATUS_REG) &
BASR_DRQ) && (NCR5380_read(BUS_AND_STATUS_REG) &
BASR_PHASE_MATCH));
#else
if (NCR5380_read(STATUS_REG) & SR_REQ) {
for (; timeout &&
!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
--timeout);
for (; timeout && (NCR5380_read(STATUS_REG) & SR_REQ);
--timeout);
}
#endif
#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
if (!timeout)
printk("scsi%d : timed out on last byte\n",
instance->host_no);
#endif
if (hostdata->flags & FLAG_CHECK_LAST_BYTE_SENT) {
hostdata->flags &= ~FLAG_CHECK_LAST_BYTE_SENT;
if (NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT) {
hostdata->flags |= FLAG_HAS_LAST_BYTE_SENT;
#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
printk("scsi%d : last bit sent works\n",
instance->host_no);
#endif
}
}
} else
while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT));
#else
udelay (5);
#endif
}
}
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
NCR5380_write(MODE_REG, MR_BASE);
*data = d + c;
*count = 0;
*phase = (NCR5380_read(STATUS_REG & PHASE_MASK));
#if defined(PSEUDO_DMA) && !defined(UNSAFE)
sti();
#endif /* defined(REAL_DMA_POLL) */
return foo;
#endif /* def REAL_DMA */
}
#endif /* defined(REAL_DMA) | defined(PSEUDO_DMA) */
/*
* Function : NCR5380_information_transfer (struct Scsi_Host *instance)
*
* Purpose : run through the various SCSI phases and do as the target
* directs us to. Operates on the currently connected command,
* instance->connected.
*
* Inputs : instance, instance for which we are doing commands
*
* Side effects : SCSI things happen, the disconnected queue will be
* modified if a command disconnects, *instance->connected will
* change.
*
* XXX Note : we need to watch for bus free or a reset condition here
* to recover from an unexpected bus free condition.
*/
static void NCR5380_information_transfer (struct Scsi_Host *instance) {
NCR5380_local_declare();
struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
instance->hostdata;
unsigned char msgout = NOP;
int len, transfersize;
unsigned char *data;
unsigned char phase, tmp, old_phase=0xff;
Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
NCR5380_setup(instance);
while (1) {
tmp = NCR5380_read(STATUS_REG);
/* We only have a valid SCSI phase when REQ is asserted */
if (tmp & SR_REQ) {
phase = (tmp & PHASE_MASK);
if (phase != old_phase) {
old_phase = phase;
#if (NDEBUG & NDEBUG_INFORMATION)
NCR5380_print_phase(instance);
#endif
}
switch (phase) {
case PHASE_DATAIN:
case PHASE_DATAOUT:
#if (NDEBUG & NDEBUG_NO_DATAOUT)
printk("scsi%d : NDEBUG_NO_DATAOUT set, attempted DATAOUT aborted\n",
instance->host_no);
msgout = ABORT;
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
break;
#endif
/*
* 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",
instance->host_no, cmd->SCp.this_residual,
cmd->SCp.buffers_residual);
#endif
}
/*
* The preffered transfer method is going to be
* PSEUDO-DMA for systems that are strictly PIO,
* since we can let the hardware do the handshaking.
*
* For this to work, we need to know the transfersize
* ahead of time, since the pseudo-DMA code will sit
* in an unconditional loop.
*/
#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
#ifdef NCR5380_dma_xfer_len
if (!scsi_devices[cmd->index].borken &&
(transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) {
#else
if (!scsi_devices[cmd->index].borken &&
(transfersize = cmd->transfersize) &&
cmd->SCp.this_residual && !(cmd->SCp.this_residual %
transfersize)) {
#endif
len = transfersize;
if (NCR5380_transfer_dma(instance, &phase,
&len, (unsigned char **) &cmd->SCp.ptr)) {
/*
* If the watchdog timer fires, all future accesses to this
* device will use the polled-IO.
*/
printk("scsi%d : switching target %d lun %d to slow handshake\n",
instance->host_no, cmd->target, cmd->lun);
scsi_devices[cmd->index].borken = 1;
NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
ICR_ASSERT_ATN);
msgout = ABORT;
} else
cmd->SCp.this_residual -= transfersize - len;
} else
#endif /* defined(REAL_DMA) || defined(REAL_DMA_POLL) */
NCR5380_transfer_pio(instance, &phase,
(int *) &cmd->SCp.this_residual, (unsigned char **)
&cmd->SCp.ptr);
break;
case PHASE_MSGIN:
/*
* XXX - we don't handle multi-byte messages here, since we
* shouldn't get them after the I_T_L_Q nexus is established
* for tagged queuing, and the host should initiate any
* negotiations for sync. SCSI, etc.
*/
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:
#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);
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:
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);
while ((NCR5380_read(STATUS_REG) & SR_BSY) &&
!hostdata->connected);
return;
case MESSAGE_REJECT:
switch (hostdata->last_message) {
case HEAD_OF_QUEUE_TAG:
case ORDERED_QUEUE_TAG:
case SIMPLE_QUEUE_TAG:
scsi_devices[cmd->index].tagged_queue = 0;
hostdata->busy[cmd->target] |= (1 << cmd->lun);
break;
default:
break;
}
case DISCONNECT:
scsi_devices[cmd->index].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
/* Enable reselect interupts */
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);
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
* compatable.
*/
case SAVE_POINTERS:
case RESTORE_POINTERS:
break;
default:
/*
* XXX rejected messages should be handled in the pio data transfer phase,
* since ATN should be raised before ACK goes false when we reject a message
*/
printk("Unknown message!\n");
#ifdef notyet
/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -