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

📄 ncr53c9x.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
				/* see how much we got ... */				fifocnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES);				if (!fifocnt)					fifo_stuck++;				else					fifo_stuck = 0;				ESPDATA(("\rgot %d st %x ph %x", fifocnt, esp->sreg, newphase));				/* read fifo */				for(j=0;j<fifocnt;j++)					SCptr->SCp.ptr[i++] = esp_read(eregs->esp_fdata);				ESPDATA(("(%d) ", i));				/* how many to go ?? */				hmuch -= fifocnt;				/* break if status phase !! */				if(newphase == ESP_STATP) {					/* clear int. */					esp->ireg = esp_read(eregs->esp_intrpt);					break;				}			} else {#define MAX_FIFO 8				/* how much will fit ? */				int this_count = MAX_FIFO - fifocnt;				if (this_count > hmuch)					this_count = hmuch;				/* fill fifo */				for(j=0;j<this_count;j++)					esp_write(eregs->esp_fdata, SCptr->SCp.ptr[i++]);				/* how many left if this goes out ?? */				hmuch -= this_count;				/* 'go' ... */ 				esp_cmd(esp, eregs, ESP_CMD_TI);				/* wait for 'got it' */				timeout = 1000000;				while (!((esp->sreg=esp_read(eregs->esp_status)) & ESP_STAT_INTR) && --timeout)					udelay(2);				if (timeout == 0)					printk("DRQ dataout timeout!  \n");				newphase = esp->sreg & ESP_STAT_PMASK;				/* need to check how much was sent ?? */				fifocnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES);				ESPDATA(("\rsent %d st %x ph %x", this_count - fifocnt, esp->sreg, newphase));				ESPDATA(("(%d) ", i));				/* break if status phase !! */				if(newphase == ESP_STATP) {					/* clear int. */					esp->ireg = esp_read(eregs->esp_intrpt);					break;				}			}			/* clear int. */			esp->ireg = esp_read(eregs->esp_intrpt);			ESPDATA(("ir %x ... ", esp->ireg));			if (hmuch == 0)				ESPDATA(("done! \n"));			restore_flags(flags);			/* check new bus phase */			if (newphase != oldphase && i < esp->current_transfer_size) {				/* something happened; disconnect ?? */				ESPDATA(("phase change, dropped out with %d done ... ", i));				break;			}			/* check int. status */			if (esp->ireg & ESP_INTR_DC) {				/* disconnect */				ESPDATA(("disconnect; %d transfered ... ", i));				break;			} else if (esp->ireg & ESP_INTR_FDONE) {				/* function done */				ESPDATA(("function done; %d transfered ... ", i));				break;			}			/* XXX fixme: bail out on stall */			if (fifo_stuck > 10) {				/* we're stuck */				ESPDATA(("fifo stall; %d transfered ... ", i));				break;			}		}		ESPDATA(("\n"));		/* check successful completion ?? */		if (thisphase == in_dataout)			hmuch += fifocnt; /* stuck?? adjust data pointer ...*/		/* tell do_data_finale how much was transfered */		esp->current_transfer_size -= hmuch;		/* still not completely sure on this one ... */				return /*do_intr_end*/ do_work_bus /*do_phase_determine*/ ;		/*		 * end PIO		 */	}	return do_intr_end;}/* See how successful the data transfer was. */static int esp_do_data_finale(struct NCR_ESP *esp,			      struct ESP_regs *eregs){	Scsi_Cmnd *SCptr = esp->current_SC;	int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0;	if(esp->dma_led_off)		esp->dma_led_off(esp);	ESPDATA(("esp_do_data_finale: "));	if(SCptr->SCp.phase == in_datain) {		if(esp->sreg & ESP_STAT_PERR) {			/* Yuck, parity error.  The ESP asserts ATN			 * so that we can go to message out phase			 * immediately and inform the target that			 * something bad happened.			 */			ESPLOG(("esp%d: data bad parity detected.\n",				esp->esp_id));			esp->cur_msgout[0] = INITIATOR_ERROR;			esp->msgout_len = 1;		}		if(esp->dma_drain)			esp->dma_drain(esp);	}	if(esp->dma_invalidate)		esp->dma_invalidate(esp);	/* This could happen for the above parity error case. */	if(!(esp->ireg == ESP_INTR_BSERV)) {		/* Please go to msgout phase, please please please... */		ESPLOG(("esp%d: !BSERV after data, probably to msgout\n",			esp->esp_id));		return esp_do_phase_determine(esp, eregs);	}		/* Check for partial transfers and other horrible events. */	fifocnt = (esp_read(eregs->esp_fflags) & ESP_FF_FBYTES);	ecount = esp_getcount(eregs);	if(esp->fas_premature_intr_workaround)		ecount -= 0x40;	bytes_sent = esp->current_transfer_size;	ESPDATA(("trans_sz=%d, ", bytes_sent));	if(!(esp->sreg & ESP_STAT_TCNT))		bytes_sent -= ecount;	if(SCptr->SCp.phase == in_dataout)		bytes_sent -= fifocnt;	ESPDATA(("bytes_sent=%d (ecount=%d, fifocnt=%d), ", bytes_sent,		 ecount, fifocnt));	/* If we were in synchronous mode, check for peculiarities. */	if(SCptr->device->sync_max_offset)		bogus_data = esp100_sync_hwbug(esp, eregs, SCptr, fifocnt);	else		esp_cmd(esp, eregs, ESP_CMD_FLUSH);	/* Until we are sure of what has happened, we are certainly	 * in the dark.	 */	esp_advance_phase(SCptr, in_the_dark);	/* Check for premature interrupt condition. Can happen on FAS2x6	 * chips. QLogic recommends a workaround by overprogramming the	 * transfer counters, but this makes doing scatter-gather impossible.	 * Until there is a way to disable scatter-gather for a single target,	 * and not only for the entire host adapter as it is now, the workaround	 * is way to expensive performance wise.	 * Instead, it turns out that when this happens the target has disconnected	 * allready but it doesn't show in the interrupt register. Compensate for	 * that here to try and avoid a SCSI bus reset.	 */	if(!esp->fas_premature_intr_workaround && (fifocnt == 1) &&	   sreg_dataoutp(esp->sreg)) {		ESPLOG(("esp%d: Premature interrupt, enabling workaround\n",			esp->esp_id));#if 0		/* Disable scatter-gather operations, they are not possible		 * when using this workaround.		 */		esp->ehost->sg_tablesize = 0;		esp->ehost->use_clustering = ENABLE_CLUSTERING;		esp->fas_premature_intr_workaround = 1;		bytes_sent = 0;		if(SCptr->use_sg) {			ESPLOG(("esp%d: Aborting scatter-gather operation\n",				esp->esp_id));			esp->cur_msgout[0] = ABORT;			esp->msgout_len = 1;			esp->msgout_ctr = 0;			esp_cmd(esp, eregs, ESP_CMD_SATN);			esp_setcount(eregs, 0xffff);			esp_cmd(esp, eregs, ESP_CMD_NULL);			esp_cmd(esp, eregs, ESP_CMD_TPAD | ESP_CMD_DMA);			return do_intr_end;		}#else		/* Just set the disconnected bit. That's what appears to		 * happen anyway. The state machine will pick it up when		 * we return.		 */		esp->ireg |= ESP_INTR_DC;#endif        }	if(bytes_sent < 0) {		/* I've seen this happen due to lost state in this		 * driver.  No idea why it happened, but allowing		 * this value to be negative caused things to		 * lock up.  This allows greater chance of recovery.		 * In fact every time I've seen this, it has been		 * a driver bug without question.		 */		ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id));		ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n",			esp->esp_id,			esp->current_transfer_size, fifocnt, ecount));		ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n",			esp->esp_id,			SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual));		ESPLOG(("esp%d: Forcing async for target %d\n", esp->esp_id, 			SCptr->target));		SCptr->device->borken = 1;		SCptr->device->sync = 0;		bytes_sent = 0;	}	/* Update the state of our transfer. */	SCptr->SCp.ptr += bytes_sent;	SCptr->SCp.this_residual -= bytes_sent;	if(SCptr->SCp.this_residual < 0) {		/* shit */		ESPLOG(("esp%d: Data transfer overrun.\n", esp->esp_id));		SCptr->SCp.this_residual = 0;	}	/* Maybe continue. */	if(!bogus_data) {		ESPDATA(("!bogus_data, "));		/* NO MATTER WHAT, we advance the scatterlist,		 * if the target should decide to disconnect		 * in between scatter chunks (which is common)		 * we could die horribly!  I used to have the sg		 * advance occur only if we are going back into		 * (or are staying in) a data phase, you can		 * imagine the hell I went through trying to		 * figure this out.		 */		if(!SCptr->SCp.this_residual && SCptr->SCp.buffers_residual)			advance_sg(esp, SCptr);#ifdef DEBUG_ESP_DATA		if(sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) {			ESPDATA(("to more data\n"));		} else {			ESPDATA(("to new phase\n"));		}#endif		return esp_do_phase_determine(esp, eregs);	}	/* Bogus data, just wait for next interrupt. */	ESPLOG(("esp%d: bogus_data during end of data phase\n",		esp->esp_id));	return do_intr_end;}/* We received a non-good status return at the end of * running a SCSI command.  This is used to decide if * we should clear our synchronous transfer state for * such a device when that happens. * * The idea is that when spinning up a disk or rewinding * a tape, we don't want to go into a loop re-negotiating * synchronous capabilities over and over. */static int esp_should_clear_sync(Scsi_Cmnd *sp){	unchar cmd1 = sp->cmnd[0];	unchar cmd2 = sp->data_cmnd[0];	/* These cases are for spinning up a disk and	 * waiting for that spinup to complete.	 */	if(cmd1 == START_STOP ||	   cmd2 == START_STOP)		return 0;	if(cmd1 == TEST_UNIT_READY ||	   cmd2 == TEST_UNIT_READY)		return 0;	/* One more special case for SCSI tape drives,	 * this is what is used to probe the device for	 * completion of a rewind or tape load operation.	 */	if(sp->device->type == TYPE_TAPE) {		if(cmd1 == MODE_SENSE ||		   cmd2 == MODE_SENSE)			return 0;	}	return 1;}/* Either a command is completing or a target is dropping off the bus * to continue the command in the background so we can do other work. */static int esp_do_freebus(struct NCR_ESP *esp, struct ESP_regs *eregs){	Scsi_Cmnd *SCptr = esp->current_SC;	int rval;	rval = skipahead2(esp, eregs, SCptr, in_status, in_msgindone, in_freeing);	if(rval)		return rval;	if(esp->ireg != ESP_INTR_DC) {		ESPLOG(("esp%d: Target will not disconnect\n", esp->esp_id));		return do_reset_bus; /* target will not drop BSY... */	}	esp->msgout_len = 0;	esp->prevmsgout = NOP;	if(esp->prevmsgin == COMMAND_COMPLETE) {		/* Normal end of nexus. */		if(esp->disconnected_SC)			esp_cmd(esp, eregs, ESP_CMD_ESEL);		if(SCptr->SCp.Status != GOOD &&		   SCptr->SCp.Status != CONDITION_GOOD &&		   ((1<<SCptr->target) & esp->targets_present) &&		   SCptr->device->sync &&		   SCptr->device->sync_max_offset) {			/* SCSI standard says that the synchronous capabilities			 * should be renegotiated at this point.  Most likely			 * we are about to request sense from this target			 * in which case we want to avoid using sync			 * transfers until we are sure of the current target			 * state.			 */			ESPMISC(("esp: Status <%d> for target %d lun %d\n",				 SCptr->SCp.Status, SCptr->target, SCptr->lun));			/* But don't do this when spinning up a disk at			 * boot time while we poll for completion as it			 * fills up the console with messages.  Also, tapes			 * can report not ready many times right after			 * loading up a tape.			 */			if(esp_should_clear_sync(SCptr) != 0)				SCptr->device->sync = 0;		}		ESPDISC(("F<%02x,%02x>", SCptr->target, SCptr->lun));		esp_done(esp, ((SCptr->SCp.Status & 0xff) |			       ((SCptr->SCp.Message & 0xff)<<8) |			       (DID_OK << 16)));	} else if(esp->prevmsgin == DISCONNECT) {		/* Normal disconnect. */		esp_cmd(esp, eregs, ESP_CMD_ESEL);		ESPDISC(("D<%02x,%02x>", SCptr->target, SCptr->lun));		append_SC(&esp->disconnected_SC, SCptr);		esp->current_SC = NULL;		if(esp->issue_SC)			esp_exec_cmd(esp);	} else {		/* Driver bug, we do not expect a disconnect here		 * and should not have advanced the state engine		 * to in_freeing.		 */		ESPLOG(("esp%d: last msg not disc and not cmd cmplt.\n",			esp->esp_id));		return do_reset_bus;	}	return do_intr_end;}/* When a reselect occurs, and we cannot find the command to * reconnect to in our queues, we do this. */static int esp_bad_reconnect(struct NCR_ESP *esp){	Scsi_Cmnd *sp;	ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n",		esp->esp_id));	ESPLOG(("QUEUE DUMP\n"));	sp = esp->issue_SC;	ESPLOG(("esp%d: issue_SC[", esp->esp_id));	while(sp) {		ESPLOG(("<%02x,%02x>", sp->target, sp->lun));		sp = (Scsi_Cmnd *) sp->host_scribble;	}	ESPLOG(("]\n"));	sp = esp->current_SC;	ESPLOG(("esp%d: current_SC[", esp->esp_id));	while(sp) {		ESPLOG(("<%02x,%02x>", sp->target, sp->lun));		sp = (Scsi_Cmnd *) sp->host_scribble;	}	ESPLOG(("]\n"));	sp = esp->disconnected_SC;	ESPLOG(("esp%d: disconnected_SC[", esp->esp_id));	while(sp) {		ESPLOG(("<%02x,%02x>", sp->target, sp->lun));		sp = (Scsi_Cmnd *) sp->host_scribble;	}	ESPLOG(("]\n"));	return do_reset_bus;}/* Do the needy when a target tries to reconnect to us. */static int esp_do_reconnect(struct NCR_ESP *esp, 			    struct ESP_regs *eregs){	int lun, target;	Scsi_Cmnd *SCptr;	/* Check for all bogus conditions first. */	target = reconnect_target(esp, eregs);	if(target < 0) {		ESPDISC(("bad bus bits\n"));		return do_reset_bus;	}	lun = reconnect_lun(esp, eregs);	if(lun < 0) {		ESPDISC(("target=%2x, bad identify msg\n", target));		return do_reset_bus;	}	/* Things look ok... */	ESPDISC(("R<%02x,%02x>", target, lun));	esp_cmd(esp, eregs, ESP_CMD_FLUSH);	if(esp100_reconnect_hwbug(esp, eregs))		return do_reset_bus;	esp_cmd(esp, eregs

⌨️ 快捷键说明

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