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

📄 scsi.c

📁 <Linux1.0核心游记>电子书+书后源码+Linux1.0源码
💻 C
📖 第 1 页 / 共 3 页
字号:
      /* Now reset things so that req looks OK */      SCpnt->request.nr_sectors -= req->nr_sectors;      req->current_nr_sectors = bh->b_size >> 9;      req->buffer = bh->b_data;      SCpnt->request.waiting = NULL; /* Wait until whole thing done */    } else      req->dev = -1;        } else {    SCpnt->request.dev = 0xffff; /* Busy, but no request */    SCpnt->request.waiting = NULL;  /* And no one is waiting for the device either */  };  SCpnt->use_sg = 0;  /* Reset the scatter-gather flag */  SCpnt->old_use_sg  = 0;  SCpnt->transfersize = 0;  SCpnt->underflow = 0;  return SCpnt;}/* This function returns a structure pointer that will be valid forthe device.  The wait parameter tells us whether we should wait forthe unit to become free or not.  We are also able to tell this routinenot to return a descriptor if the host is unable to accept any morecommands for the time being.  We need to keep in mind that there is noguarantee that the host remain not busy.  Keep in mind therequest_queueable function also knows the internal allocation schemeof the packets for each device */Scsi_Cmnd * allocate_device (struct request ** reqp, int index, int wait){  int dev = -1;  struct request * req = NULL;  int tablesize;  struct buffer_head * bh;  struct Scsi_Host * host;  Scsi_Cmnd * SCpnt = NULL;  Scsi_Cmnd * SCwait = NULL;  if ((index < 0) ||  (index > NR_SCSI_DEVICES))    panic ("Index number in allocate_device() is out of range.\n");    if (reqp) req = *reqp;    /* See if this request has already been queued by an interrupt routine */  if (req && (dev = req->dev) <= 0) return NULL;    host = scsi_devices[index].host;    while (1==1){    SCpnt = host->host_queue;    while(SCpnt){      if(SCpnt->target == scsi_devices[index].id &&	 SCpnt->lun == scsi_devices[index].lun) {	SCwait = SCpnt;	if(SCpnt->request.dev < 0) break;      };      SCpnt = SCpnt->next;    };    cli();    /* See if this request has already been queued by an interrupt routine */    if (req && ((req->dev < 0) || (req->dev != dev))) {      sti();      return NULL;    };    if (!SCpnt || SCpnt->request.dev >= 0)  /* Might have changed */      {	sti();	if(!wait) return NULL;	if (!SCwait) {	  printk("Attempt to allocate device index %d, target %d, lun %d\n",		 index, scsi_devices[index].id ,scsi_devices[index].lun);	  panic("No device found in allocate_device\n");	};	SCSI_SLEEP(&scsi_devices[SCwait->index].device_wait, 		   (SCwait->request.dev > 0));      } else {	if (req) {	  memcpy(&SCpnt->request, req, sizeof(struct request));	  tablesize = scsi_devices[index].host->sg_tablesize;	  bh = req->bh;	  if(!tablesize) bh = NULL;	  /* Take a quick look through the table to see how big it is.  We already	     have our copy of req, so we can mess with that if we want to.  */	  while(req->nr_sectors && bh){	    tablesize--;	    req->nr_sectors -= bh->b_size >> 9;	    req->sector += bh->b_size >> 9;	    if(!tablesize) break;	    bh = bh->b_reqnext;	  };	  if(req->nr_sectors && bh && bh->b_reqnext){  /* Any leftovers? */	    SCpnt->request.bhtail = bh;	    req->bh = bh->b_reqnext; /* Divide request */	    bh->b_reqnext = NULL;	    bh = req->bh;	    /* Now reset things so that req looks OK */	    SCpnt->request.nr_sectors -= req->nr_sectors;	    req->current_nr_sectors = bh->b_size >> 9;	    req->buffer = bh->b_data;	    SCpnt->request.waiting = NULL; /* Wait until whole thing done */	  }	  else 	    {	      req->dev = -1;	      *reqp = req->next;	    };	} else {	  SCpnt->request.dev = 0xffff; /* Busy */	  SCpnt->request.waiting = NULL;  /* And no one is waiting for this to complete */	};	sti();	break;      };  };  SCpnt->use_sg = 0;  /* Reset the scatter-gather flag */  SCpnt->old_use_sg  = 0;  SCpnt->transfersize = 0;      /* No default transfer size */  SCpnt->underflow = 0;         /* Do not flag underflow conditions */  return SCpnt;}/*	This is inline because we have stack problemes if we recurse to deeply.*/			 inline void internal_cmnd (Scsi_Cmnd * SCpnt)	{	int temp;	struct Scsi_Host * host;#ifdef DEBUG_DELAY		int clock;#endif	if ((unsigned long) &SCpnt < current->kernel_stack_page)	  panic("Kernel stack overflow.");	host = SCpnt->host;/*	We will wait MIN_RESET_DELAY clock ticks after the last reset so 	we can avoid the drive not being ready.*/ temp = host->last_reset;while (jiffies < temp);update_timeout(SCpnt, SCpnt->timeout_per_command);/*	We will use a queued command if possible, otherwise we will emulate the	queing and calling of completion function ourselves. */#ifdef DEBUG	printk("internal_cmnd (host = %d, target = %d, command = %08x, buffer =  %08x, \n"		"bufflen = %d, done = %08x)\n", SCpnt->host->host_no, SCpnt->target, SCpnt->cmnd, SCpnt->buffer, SCpnt->bufflen, SCpnt->done);#endif        if (host->hostt->can_queue)		{#ifdef DEBUG	printk("queuecommand : routine at %08x\n", 		host->hostt->queuecommand);#endif                host->hostt->queuecommand (SCpnt, scsi_done);		}	else		{#ifdef DEBUG	printk("command() :  routine at %08x\n", host->hostt->command);#endif		temp=host->hostt->command (SCpnt);	        SCpnt->result = temp;#ifdef DEBUG_DELAY	clock = jiffies + 400;	while (jiffies < clock);	printk("done(host = %d, result = %04x) : routine at %08x\n", host->host_no, temp, done);#endif	        scsi_done(SCpnt);		}	#ifdef DEBUG	printk("leaving internal_cmnd()\n");#endif	}	static void scsi_request_sense (Scsi_Cmnd * SCpnt)	{	cli();	SCpnt->flags |= WAS_SENSE | ASKED_FOR_SENSE;	update_timeout(SCpnt, SENSE_TIMEOUT);	sti();		memcpy ((void *) SCpnt->cmnd , (void *) generic_sense,		sizeof(generic_sense));	SCpnt->cmnd[1] = SCpnt->lun << 5;		SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer);	SCpnt->request_buffer = &SCpnt->sense_buffer;	SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer);	SCpnt->use_sg = 0;	internal_cmnd (SCpnt);	SCpnt->use_sg = SCpnt->old_use_sg;	}/*	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 queud.  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 		   )        {	struct Scsi_Host * host = SCpnt->host;#ifdef DEBUG	{	int i;		int target = SCpnt->target;	printk ("scsi_do_cmd (host = %d, target = %d, buffer =%08x, "		"bufflen = %d, done = %08x, timeout = %d, retries = %d)\n"		"command : " , host->host_no, 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. %d\n", host->host_no);		}	/*	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 inbetween the	time we check for the host being not busy, and the time we mark it busy	ourselves.*/	while (1==1){	  cli();	  if (host->hostt->can_queue	      && host->host_busy >= host->hostt->can_queue)	    {	      sti();	      SCSI_SLEEP(&host->host_wait, 			 (host->host_busy >= host->hostt->can_queue));	    } else {	      host->host_busy++;	      sti();	      break;	    };      };/*	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 , (void *) cmnd, 12);#if 0	SCpnt->host = host;	SCpnt->target = target;	SCpnt->lun = (SCpnt->data_cmnd[1] >> 5);#endif	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 , (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 - ");

⌨️ 快捷键说明

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