?? scsi.c
字號:
memcpy ((void *) SCpnt->cmnd , (void *) cmnd, 12);
/* Zero the sense buffer. Some host adapters automatically request
sense on error. 0 is not a valid sense code. */
memset ((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer);
SCpnt->request_buffer = buffer;
SCpnt->request_bufflen = bufflen;
SCpnt->old_use_sg = SCpnt->use_sg;
/* Start the timer ticking. */
SCpnt->internal_timeout = 0;
internal_cmnd (SCpnt);
#ifdef DEBUG
printk ("Leaving scsi_do_cmd()\n");
#endif
}
/*
The scsi_done() function disables the timeout timer for the scsi host,
marks the host as not busy, and calls the user specified completion
function for that host's current command.
*/
static void reset (Scsi_Cmnd * SCpnt)
{
#ifdef DEBUG
printk("scsi: reset(%d)\n", SCpnt->host->host_no);
#endif
SCpnt->flags |= (WAS_RESET | IS_RESETTING);
scsi_reset(SCpnt);
#ifdef DEBUG
printk("performing request sense\n");
#endif
if(SCpnt->flags & NEEDS_JUMPSTART) {
SCpnt->flags &= ~NEEDS_JUMPSTART;
scsi_request_sense (SCpnt);
};
}
static int check_sense (Scsi_Cmnd * SCpnt)
{
/* If there is no sense information, request it. If we have already
requested it, there is no point in asking again - the firmware must be
confused. */
if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) {
if(!(SCpnt->flags & ASKED_FOR_SENSE))
return SUGGEST_SENSE;
else
return SUGGEST_RETRY;
}
SCpnt->flags &= ~ASKED_FOR_SENSE;
#ifdef DEBUG_INIT
printk("scsi%d : ", SCpnt->host->host_no);
print_sense("", SCpnt);
printk("\n");
#endif
if (SCpnt->sense_buffer[2] &0xe0)
return SUGGEST_ABORT;
switch (SCpnt->sense_buffer[2] & 0xf)
{
case NO_SENSE:
return 0;
case RECOVERED_ERROR:
if (scsi_devices[SCpnt->index].type == TYPE_TAPE)
return SUGGEST_IS_OK;
else
return 0;
case ABORTED_COMMAND:
return SUGGEST_RETRY;
case NOT_READY:
case UNIT_ATTENTION:
return SUGGEST_ABORT;
/* these three are not supported */
case COPY_ABORTED:
case VOLUME_OVERFLOW:
case MISCOMPARE:
case MEDIUM_ERROR:
return SUGGEST_REMAP;
case BLANK_CHECK:
case DATA_PROTECT:
case HARDWARE_ERROR:
case ILLEGAL_REQUEST:
default:
return SUGGEST_ABORT;
}
}
/* This function is the mid-level interrupt routine, which decides how
* to handle error conditions. Each invocation of this function must
* do one and *only* one of the following:
*
* (1) Call last_cmnd[host].done. This is done for fatal errors and
* normal completion, and indicates that the handling for this
* request is complete.
* (2) Call internal_cmnd to requeue the command. This will result in
* scsi_done being called again when the retry is complete.
* (3) Call scsi_request_sense. This asks the host adapter/drive for
* more information about the error condition. When the information
* is available, scsi_done will be called again.
* (4) Call reset(). This is sort of a last resort, and the idea is that
* this may kick things loose and get the drive working again. reset()
* automatically calls scsi_request_sense, and thus scsi_done will be
* called again once the reset is complete.
*
* If none of the above actions are taken, the drive in question
* will hang. If more than one of the above actions are taken by
* scsi_done, then unpredictable behavior will result.
*/
static void scsi_done (Scsi_Cmnd * SCpnt)
{
int status=0;
int exit=0;
int checked;
int oldto;
struct Scsi_Host * host = SCpnt->host;
int result = SCpnt->result;
oldto = update_timeout(SCpnt, 0);
#define FINISHED 0
#define MAYREDO 1
#define REDO 3
#define PENDING 4
#ifdef DEBUG
printk("In scsi_done(host = %d, result = %06x)\n", host->host_no, result);
#endif
switch (host_byte(result))
{
case DID_OK:
if (SCpnt->flags & IS_RESETTING)
{
SCpnt->flags &= ~IS_RESETTING;
status = REDO;
break;
}
if (status_byte(result) && (SCpnt->flags & WAS_SENSE))
/* Failed to obtain sense information */
{
SCpnt->flags &= ~WAS_SENSE;
SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
if (!(SCpnt->flags & WAS_RESET))
{
printk("scsi%d : target %d lun %d request sense failed, performing reset.\n",
SCpnt->host->host_no, SCpnt->target, SCpnt->lun);
reset(SCpnt);
return;
}
else
{
exit = (DRIVER_HARD | SUGGEST_ABORT);
status = FINISHED;
}
}
else switch(msg_byte(result))
{
case COMMAND_COMPLETE:
switch (status_byte(result))
{
case GOOD:
if (SCpnt->flags & WAS_SENSE)
{
#ifdef DEBUG
printk ("In scsi_done, GOOD status, COMMAND COMPLETE, parsing sense information.\n");
#endif
SCpnt->flags &= ~WAS_SENSE;
SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
switch (checked = check_sense(SCpnt))
{
case SUGGEST_SENSE:
case 0:
#ifdef DEBUG
printk("NO SENSE. status = REDO\n");
#endif
update_timeout(SCpnt, oldto);
status = REDO;
break;
case SUGGEST_IS_OK:
break;
case SUGGEST_REMAP:
case SUGGEST_RETRY:
#ifdef DEBUG
printk("SENSE SUGGEST REMAP or SUGGEST RETRY - status = MAYREDO\n");
#endif
status = MAYREDO;
exit = DRIVER_SENSE | SUGGEST_RETRY;
break;
case SUGGEST_ABORT:
#ifdef DEBUG
printk("SENSE SUGGEST ABORT - status = FINISHED");
#endif
status = FINISHED;
exit = DRIVER_SENSE | SUGGEST_ABORT;
break;
default:
printk ("Internal error %s %d \n", __FILE__,
__LINE__);
}
}
else
{
#ifdef DEBUG
printk("COMMAND COMPLETE message returned, status = FINISHED. \n");
#endif
exit = DRIVER_OK;
status = FINISHED;
}
break;
case CHECK_CONDITION:
switch (check_sense(SCpnt))
{
case 0:
update_timeout(SCpnt, oldto);
status = REDO;
break;
case SUGGEST_REMAP:
case SUGGEST_RETRY:
status = MAYREDO;
exit = DRIVER_SENSE | SUGGEST_RETRY;
break;
case SUGGEST_ABORT:
status = FINISHED;
exit = DRIVER_SENSE | SUGGEST_ABORT;
break;
case SUGGEST_SENSE:
scsi_request_sense (SCpnt);
status = PENDING;
break;
}
break;
case CONDITION_GOOD:
case INTERMEDIATE_GOOD:
case INTERMEDIATE_C_GOOD:
break;
case BUSY:
update_timeout(SCpnt, oldto);
status = REDO;
break;
case RESERVATION_CONFLICT:
printk("scsi%d : RESERVATION CONFLICT performing reset.\n",
SCpnt->host->host_no);
reset(SCpnt);
return;
#if 0
exit = DRIVER_SOFT | SUGGEST_ABORT;
status = MAYREDO;
break;
#endif
default:
printk ("Internal error %s %d \n"
"status byte = %d \n", __FILE__,
__LINE__, status_byte(result));
}
break;
default:
panic("scsi: unsupported message byte %d recieved\n", msg_byte(result));
}
break;
case DID_TIME_OUT:
#ifdef DEBUG
printk("Host returned DID_TIME_OUT - ");
#endif
if (SCpnt->flags & WAS_TIMEDOUT)
{
#ifdef DEBUG
printk("Aborting\n");
#endif
exit = (DRIVER_TIMEOUT | SUGGEST_ABORT);
}
else
{
#ifdef DEBUG
printk ("Retrying.\n");
#endif
SCpnt->flags |= WAS_TIMEDOUT;
status = REDO;
}
break;
case DID_BUS_BUSY:
case DID_PARITY:
status = REDO;
break;
case DID_NO_CONNECT:
#ifdef DEBUG
printk("Couldn't connect.\n");
#endif
exit = (DRIVER_HARD | SUGGEST_ABORT);
break;
case DID_ERROR:
status = MAYREDO;
exit = (DRIVER_HARD | SUGGEST_ABORT);
break;
case DID_BAD_TARGET:
case DID_ABORT:
exit = (DRIVER_INVALID | SUGGEST_ABORT);
break;
case DID_RESET:
if(msg_byte(result) == GOOD &&
status_byte(result) == CHECK_CONDITION) {
switch (check_sense(SCpnt)) {
case 0:
update_timeout(SCpnt, oldto);
status = REDO;
break;
case SUGGEST_REMAP:
case SUGGEST_RETRY:
status = MAYREDO;
exit = DRIVER_SENSE | SUGGEST_RETRY;
break;
case SUGGEST_ABORT:
status = FINISHED;
exit = DRIVER_SENSE | SUGGEST_ABORT;
break;
case SUGGEST_SENSE:
scsi_request_sense (SCpnt);
status = PENDING;
break;
}
} else {
status=REDO;
exit = SUGGEST_RETRY;
}
break;
default :
exit = (DRIVER_ERROR | SUGGEST_DIE);
}
switch (status)
{
case FINISHED:
case PENDING:
break;
case MAYREDO:
#ifdef DEBUG
printk("In MAYREDO, allowing %d retries, have %d\n",
SCpnt->allowed, SCpnt->retries);
#endif
if ((++SCpnt->retries) < SCpnt->allowed)
{
if ((SCpnt->retries >= (SCpnt->allowed >> 1))
&& !(SCpnt->flags & WAS_RESET))
{
printk("scsi%d : reseting for second half of retries.\n",
SCpnt->host->host_no);
reset(SCpnt);
break;
}
}
else
{
status = FINISHED;
break;
}
/* fall through to REDO */
case REDO:
if (SCpnt->flags & WAS_SENSE)
scsi_request_sense(SCpnt);
else
{
memcpy ((void *) SCpnt->cmnd,
(void*) SCpnt->data_cmnd,
sizeof(SCpnt->data_cmnd));
SCpnt->request_buffer = SCpnt->buffer;
SCpnt->request_bufflen = SCpnt->bufflen;
SCpnt->use_sg = SCpnt->old_use_sg;
internal_cmnd (SCpnt);
};
break;
default:
INTERNAL_ERROR;
}
if (status == FINISHED)
{
#ifdef DEBUG
printk("Calling done function - at address %08x\n", SCpnt->done);
#endif
host->host_busy--; /* Indicate that we are free */
wake_up(&host->host_wait);
SCpnt->result = result | ((exit & 0xff) << 24);
SCpnt->use_sg = SCpnt->old_use_sg;
SCpnt->done (SCpnt);
}
#undef FINISHED
#undef REDO
#undef MAYREDO
#undef PENDING
}
/*
The scsi_abort function interfaces with the abort() function of the host
we are aborting, and causes the current command to not complete. The
caller should deal with any error messages or status returned on the
next call.
This will not be called rentrantly for a given host.
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -