⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scsi.c

📁 GNU Mach 微内核源代码, 基于美国卡内基美隆大学的 Mach 研究项目
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * scsi_do_cmd sends all the commands out to the low-level driver.  It * handles the specifics required for each low level driver - ie queued * or non queued.  It also prevents conflicts when different high level * drivers go for the same host at the same time. */void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,		  void *buffer, unsigned bufflen, void (*done)(Scsi_Cmnd *),		  int timeout, int retries){    unsigned long flags;    struct Scsi_Host * host = SCpnt->host;    #ifdef DEBUG    {	int i;	int target = SCpnt->target;	printk ("scsi_do_cmd (host = %d, channel = %d target = %d, "		"buffer =%p, bufflen = %d, done = %p, timeout = %d, "		"retries = %d)\n"		"command : " , host->host_no, SCpnt->channel, target, buffer, 		bufflen, done, timeout, retries);	for (i = 0; i < 10; ++i)	    printk ("%02x  ", ((unsigned char *) cmnd)[i]);	printk("\n");    }#endif        if (!host)    {	panic ("Invalid or not present host.\n");    }            /*     * We must prevent reentrancy to the lowlevel host driver.  This prevents     * it - we enter a loop until the host we want to talk to is not busy.     * Race conditions are prevented, as interrupts are disabled in between the     * time we check for the host being not busy, and the time we mark it busy     * ourselves.     */    save_flags(flags);    cli();    SCpnt->pid = scsi_pid++;        while (SCSI_BLOCK(host)) {	restore_flags(flags);	SCSI_SLEEP(&host->host_wait, SCSI_BLOCK(host));	cli();    }        if (host->block) host_active = host;        host->host_busy++;    restore_flags(flags);        /*     * Our own function scsi_done (which marks the host as not busy, disables     * the timeout counter, etc) will be called by us or by the     * scsi_hosts[host].queuecommand() function needs to also call     * the completion function for the high level driver.     */        memcpy ((void *) SCpnt->data_cmnd , (const void *) cmnd, 12);#if 0    SCpnt->host = host;    SCpnt->channel = channel;    SCpnt->target = target;    SCpnt->lun = (SCpnt->data_cmnd[1] >> 5);#endif    SCpnt->reset_chain = NULL;    SCpnt->serial_number = 0;    SCpnt->bufflen = bufflen;    SCpnt->buffer = buffer;    SCpnt->flags = 0;    SCpnt->retries = 0;    SCpnt->allowed = retries;    SCpnt->done = done;    SCpnt->timeout_per_command = timeout;    memcpy ((void *) SCpnt->cmnd , (const 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;    if (SCpnt->cmd_len == 0)	SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);    SCpnt->old_cmd_len = SCpnt->cmd_len;    /* Start the timer ticking.  */    SCpnt->internal_timeout = NORMAL_TIMEOUT;    SCpnt->abort_reason = 0;    internal_cmnd (SCpnt);#ifdef DEBUG    printk ("Leaving scsi_do_cmd()\n");#endif}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, channel%d : ", SCpnt->host->host_no, SCpnt->channel);    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:	return SUGGEST_IS_OK;	    case ABORTED_COMMAND:	return SUGGEST_RETRY;    case NOT_READY:    case UNIT_ATTENTION:        /*         * If we are expecting a CC/UA because of a bus reset that we         * performed, treat this just as a retry.  Otherwise this is         * information that we should pass up to the upper-level driver         * so that we can deal with it there.         */        if( SCpnt->device->expecting_cc_ua )        {            SCpnt->device->expecting_cc_ua = 0;            return SUGGEST_RETRY;        }	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;    SCpnt->serial_number = 0;    oldto = update_timeout(SCpnt, 0);    #ifdef DEBUG_TIMEOUT    if(result) printk("Non-zero result in scsi_done %x %d:%d\n",		      result, SCpnt->target, SCpnt->lun);#endif        /* If we requested an abort, (and we got it) then fix up the return     *  status to say why      */    if(host_byte(result) == DID_ABORT && SCpnt->abort_reason)	SCpnt->result = result = (result & 0xff00ffff) |	    (SCpnt->abort_reason << 16);#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    if(SCpnt->flags & WAS_SENSE)    {	SCpnt->use_sg = SCpnt->old_use_sg;	SCpnt->cmd_len = SCpnt->old_cmd_len;    }    switch (host_byte(result))    {    case DID_OK:	if (status_byte(result) && (SCpnt->flags & WAS_SENSE))	    /* Failed to obtain sense information */	{	    SCpnt->flags &= ~WAS_SENSE;#if 0	/* This cannot possibly be correct. */	    SCpnt->internal_timeout &= ~SENSE_TIMEOUT;#endif	    	    if (!(SCpnt->flags & WAS_RESET))	    {		printk("scsi%d : channel %d target %d lun %d request sense"		       " failed, performing reset.\n",		       SCpnt->host->host_no, SCpnt->channel, SCpnt->target, 		       SCpnt->lun);		scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);		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;#if 0	/* This cannot possibly be correct. */		    SCpnt->internal_timeout &= ~SENSE_TIMEOUT;#endif		    		    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:#ifdef DEBUG			printk("SENSE SUGGEST REMAP - status = FINISHED\n");#endif			status = FINISHED;			exit = DRIVER_SENSE | SUGGEST_ABORT;			break;		    case SUGGEST_RETRY:#ifdef DEBUG			printk("SENSE 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__);		    }		} /* end WAS_SENSE */		else		{#ifdef DEBUG		    printk("COMMAND COMPLETE message returned, "                           "status = FINISHED. \n");#endif		    exit =  DRIVER_OK;		    status = FINISHED;		}		break;			    case CHECK_CONDITION:	    case COMMAND_TERMINATED:		switch (check_sense(SCpnt))		{		case 0:		    update_timeout(SCpnt, oldto);		    status = REDO;		    break;		case SUGGEST_REMAP:		    status = FINISHED;		    exit =  DRIVER_SENSE | SUGGEST_ABORT;		    break;		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:	    case QUEUE_FULL:		update_timeout(SCpnt, oldto);		status = REDO;		break;			    case RESERVATION_CONFLICT:		printk("scsi%d, channel %d : RESERVATION CONFLICT performing"		       " reset.\n", SCpnt->host->host_no, SCpnt->channel);		scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);		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 received\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	    /*	      Allow TEST_UNIT_READY and INQUIRY commands to timeout early	      without causing resets.  All other commands should be retried.	    */	    if (SCpnt->cmnd[0] != TEST_UNIT_READY &&		SCpnt->cmnd[0] != INQUIRY)		    status = MAYREDO;	    exit = (DRIVER_TIMEOUT | SUGGEST_ABORT);	}	else	{#ifdef DEBUG	    printk ("Retrying.\n");#endif	    SCpnt->flags  |= WAS_TIMEDOUT;	    SCpnt->internal_timeout &= ~IN_ABORT;	    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 (SCpnt->flags & IS_RESETTING)	{	    SCpnt->flags &= ~IS_RESETTING;	    status = REDO;	    break;	}		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;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -