in2000.c

来自「linux 内核源代码」· C语言 代码 · 共 2,045 行 · 第 1/5 页

C
2,045
字号
	/*	 * Add the cmd to the end of 'input_Q'. Note that REQUEST_SENSE	 * commands are added to the head of the queue so that the desired	 * sense data is not lost before REQUEST_SENSE executes.	 */	if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) {		cmd->host_scribble = (uchar *) hostdata->input_Q;		hostdata->input_Q = cmd;	} else {		/* find the end of the queue */		for (tmp = (Scsi_Cmnd *) hostdata->input_Q; tmp->host_scribble; tmp = (Scsi_Cmnd *) tmp->host_scribble);		tmp->host_scribble = (uchar *) cmd;	}/* We know that there's at least one command in 'input_Q' now. * Go see if any of them are runnable! */	in2000_execute(cmd->device->host);	DB(DB_QUEUE_COMMAND, printk(")Q-%ld ", cmd->serial_number))	    return 0;}/* * This routine attempts to start a scsi command. If the host_card is * already connected, we give up immediately. Otherwise, look through * the input_Q, using the first command we find that's intended * for a currently non-busy target/lun. * Note that this function is always called with interrupts already * disabled (either from in2000_queuecommand() or in2000_intr()). */static void in2000_execute(struct Scsi_Host *instance){	struct IN2000_hostdata *hostdata;	Scsi_Cmnd *cmd, *prev;	int i;	unsigned short *sp;	unsigned short f;	unsigned short flushbuf[16];	hostdata = (struct IN2000_hostdata *) instance->hostdata;	DB(DB_EXECUTE, printk("EX("))	    if (hostdata->selecting || hostdata->connected) {		DB(DB_EXECUTE, printk(")EX-0 "))		    return;	}	/*	 * Search through the input_Q for a command destined	 * for an idle target/lun.	 */	cmd = (Scsi_Cmnd *) hostdata->input_Q;	prev = NULL;	while (cmd) {		if (!(hostdata->busy[cmd->device->id] & (1 << cmd->device->lun)))			break;		prev = cmd;		cmd = (Scsi_Cmnd *) cmd->host_scribble;	}	/* quit if queue empty or all possible targets are busy */	if (!cmd) {		DB(DB_EXECUTE, printk(")EX-1 "))		    return;	}	/*  remove command from queue */	if (prev)		prev->host_scribble = cmd->host_scribble;	else		hostdata->input_Q = (Scsi_Cmnd *) cmd->host_scribble;#ifdef PROC_STATISTICS	hostdata->cmd_cnt[cmd->device->id]++;#endif/* * Start the selection process */	if (is_dir_out(cmd))		write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id);	else		write_3393(hostdata, WD_DESTINATION_ID, cmd->device->id | DSTID_DPD);/* Now we need to figure out whether or not this command is a good * candidate for disconnect/reselect. We guess to the best of our * ability, based on a set of hierarchical rules. When several * devices are operating simultaneously, disconnects are usually * an advantage. In a single device system, or if only 1 device * is being accessed, transfers usually go faster if disconnects * are not allowed: * * + Commands should NEVER disconnect if hostdata->disconnect = *   DIS_NEVER (this holds for tape drives also), and ALWAYS *   disconnect if hostdata->disconnect = DIS_ALWAYS. * + Tape drive commands should always be allowed to disconnect. * + Disconnect should be allowed if disconnected_Q isn't empty. * + Commands should NOT disconnect if input_Q is empty. * + Disconnect should be allowed if there are commands in input_Q *   for a different target/lun. In this case, the other commands *   should be made disconnect-able, if not already. * * I know, I know - this code would flunk me out of any * "C Programming 101" class ever offered. But it's easy * to change around and experiment with for now. */	cmd->SCp.phase = 0;	/* assume no disconnect */	if (hostdata->disconnect == DIS_NEVER)		goto no;	if (hostdata->disconnect == DIS_ALWAYS)		goto yes;	if (cmd->device->type == 1)	/* tape drive? */		goto yes;	if (hostdata->disconnected_Q)	/* other commands disconnected? */		goto yes;	if (!(hostdata->input_Q))	/* input_Q empty? */		goto no;	for (prev = (Scsi_Cmnd *) hostdata->input_Q; prev; prev = (Scsi_Cmnd *) prev->host_scribble) {		if ((prev->device->id != cmd->device->id) || (prev->device->lun != cmd->device->lun)) {			for (prev = (Scsi_Cmnd *) hostdata->input_Q; prev; prev = (Scsi_Cmnd *) prev->host_scribble)				prev->SCp.phase = 1;			goto yes;		}	}	goto no;      yes:	cmd->SCp.phase = 1;#ifdef PROC_STATISTICS	hostdata->disc_allowed_cnt[cmd->device->id]++;#endif      no:	write_3393(hostdata, WD_SOURCE_ID, ((cmd->SCp.phase) ? SRCID_ER : 0));	write_3393(hostdata, WD_TARGET_LUN, cmd->device->lun);	write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]);	hostdata->busy[cmd->device->id] |= (1 << cmd->device->lun);	if ((hostdata->level2 <= L2_NONE) || (hostdata->sync_stat[cmd->device->id] == SS_UNSET)) {		/*		 * Do a 'Select-With-ATN' command. This will end with		 * one of the following interrupts:		 *    CSR_RESEL_AM:  failure - can try again later.		 *    CSR_TIMEOUT:   failure - give up.		 *    CSR_SELECT:    success - proceed.		 */		hostdata->selecting = cmd;/* Every target has its own synchronous transfer setting, kept in * the sync_xfer array, and a corresponding status byte in sync_stat[]. * Each target's sync_stat[] entry is initialized to SS_UNSET, and its * sync_xfer[] entry is initialized to the default/safe value. SS_UNSET * means that the parameters are undetermined as yet, and that we * need to send an SDTR message to this device after selection is * complete. We set SS_FIRST to tell the interrupt routine to do so, * unless we don't want to even _try_ synchronous transfers: In this * case we set SS_SET to make the defaults final. */		if (hostdata->sync_stat[cmd->device->id] == SS_UNSET) {			if (hostdata->sync_off & (1 << cmd->device->id))				hostdata->sync_stat[cmd->device->id] = SS_SET;			else				hostdata->sync_stat[cmd->device->id] = SS_FIRST;		}		hostdata->state = S_SELECTING;		write_3393_count(hostdata, 0);	/* this guarantees a DATA_PHASE interrupt */		write_3393_cmd(hostdata, WD_CMD_SEL_ATN);	}	else {		/*		 * Do a 'Select-With-ATN-Xfer' command. This will end with		 * one of the following interrupts:		 *    CSR_RESEL_AM:  failure - can try again later.		 *    CSR_TIMEOUT:   failure - give up.		 *    anything else: success - proceed.		 */		hostdata->connected = cmd;		write_3393(hostdata, WD_COMMAND_PHASE, 0);		/* copy command_descriptor_block into WD chip		 * (take advantage of auto-incrementing)		 */		write1_io(WD_CDB_1, IO_WD_ADDR);		for (i = 0; i < cmd->cmd_len; i++)			write1_io(cmd->cmnd[i], IO_WD_DATA);		/* The wd33c93 only knows about Group 0, 1, and 5 commands when		 * it's doing a 'select-and-transfer'. To be safe, we write the		 * size of the CDB into the OWN_ID register for every case. This		 * way there won't be problems with vendor-unique, audio, etc.		 */		write_3393(hostdata, WD_OWN_ID, cmd->cmd_len);		/* When doing a non-disconnect command, we can save ourselves a DATA		 * phase interrupt later by setting everything up now. With writes we		 * need to pre-fill the fifo; if there's room for the 32 flush bytes,		 * put them in there too - that'll avoid a fifo interrupt. Reads are		 * somewhat simpler.		 * KLUDGE NOTE: It seems that you can't completely fill the fifo here:		 * This results in the IO_FIFO_COUNT register rolling over to zero,		 * and apparently the gate array logic sees this as empty, not full,		 * so the 3393 chip is never signalled to start reading from the		 * fifo. Or maybe it's seen as a permanent fifo interrupt condition.		 * Regardless, we fix this by temporarily pretending that the fifo		 * is 16 bytes smaller. (I see now that the old driver has a comment		 * about "don't fill completely" in an analogous place - must be the		 * same deal.) This results in CDROM, swap partitions, and tape drives		 * needing an extra interrupt per write command - I think we can live		 * with that!		 */		if (!(cmd->SCp.phase)) {			write_3393_count(hostdata, cmd->SCp.this_residual);			write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS);			write1_io(0, IO_FIFO_WRITE);	/* clear fifo counter, write mode */			if (is_dir_out(cmd)) {				hostdata->fifo = FI_FIFO_WRITING;				if ((i = cmd->SCp.this_residual) > (IN2000_FIFO_SIZE - 16))					i = IN2000_FIFO_SIZE - 16;				cmd->SCp.have_data_in = i;	/* this much data in fifo */				i >>= 1;	/* Gulp. Assuming modulo 2. */				sp = (unsigned short *) cmd->SCp.ptr;				f = hostdata->io_base + IO_FIFO;#ifdef FAST_WRITE_IO				FAST_WRITE2_IO();#else				while (i--)					write2_io(*sp++, IO_FIFO);#endif				/* Is there room for the flush bytes? */				if (cmd->SCp.have_data_in <= ((IN2000_FIFO_SIZE - 16) - 32)) {					sp = flushbuf;					i = 16;#ifdef FAST_WRITE_IO					FAST_WRITE2_IO();#else					while (i--)						write2_io(0, IO_FIFO);#endif				}			}			else {				write1_io(0, IO_FIFO_READ);	/* put fifo in read mode */				hostdata->fifo = FI_FIFO_READING;				cmd->SCp.have_data_in = 0;	/* nothing transferred yet */			}		} else {			write_3393_count(hostdata, 0);	/* this guarantees a DATA_PHASE interrupt */		}		hostdata->state = S_RUNNING_LEVEL2;		write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);	}	/*	 * Since the SCSI bus can handle only 1 connection at a time,	 * we get out of here now. If the selection fails, or when	 * the command disconnects, we'll come back to this routine	 * to search the input_Q again...	 */	DB(DB_EXECUTE, printk("%s%ld)EX-2 ", (cmd->SCp.phase) ? "d:" : "", cmd->serial_number))}static void transfer_pio(uchar * buf, int cnt, int data_in_dir, struct IN2000_hostdata *hostdata){	uchar asr;	DB(DB_TRANSFER, printk("(%p,%d,%s)", buf, cnt, data_in_dir ? "in" : "out"))	    write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);	write_3393_count(hostdata, cnt);	write_3393_cmd(hostdata, WD_CMD_TRANS_INFO);	if (data_in_dir) {		do {			asr = READ_AUX_STAT();			if (asr & ASR_DBR)				*buf++ = read_3393(hostdata, WD_DATA);		} while (!(asr & ASR_INT));	} else {		do {			asr = READ_AUX_STAT();			if (asr & ASR_DBR)				write_3393(hostdata, WD_DATA, *buf++);		} while (!(asr & ASR_INT));	}	/* Note: we are returning with the interrupt UN-cleared.	 * Since (presumably) an entire I/O operation has	 * completed, the bus phase is probably different, and	 * the interrupt routine will discover this when it	 * responds to the uncleared int.	 */}static void transfer_bytes(Scsi_Cmnd * cmd, int data_in_dir){	struct IN2000_hostdata *hostdata;	unsigned short *sp;	unsigned short f;	int i;	hostdata = (struct IN2000_hostdata *) cmd->device->host->hostdata;/* Normally, you'd expect 'this_residual' to be non-zero here. * In a series of scatter-gather transfers, however, this * routine will usually be called with 'this_residual' equal * to 0 and 'buffers_residual' non-zero. This means that a * previous transfer completed, clearing 'this_residual', and * now we need to setup the next scatter-gather buffer as the * source or destination for THIS transfer. */	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 = sg_virt(cmd->SCp.buffer);	}/* Set up hardware registers */	write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]);	write_3393_count(hostdata, cmd->SCp.this_residual);	write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_BUS);	write1_io(0, IO_FIFO_WRITE);	/* zero counter, assume write *//* Reading is easy. Just issue the command and return - we'll * get an interrupt later when we have actual data to worry about. */	if (data_in_dir) {		write1_io(0, IO_FIFO_READ);		if ((hostdata->level2 >= L2_DATA) || (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {			write_3393(hostdata, WD_COMMAND_PHASE, 0x45);			write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);			hostdata->state = S_RUNNING_LEVEL2;		} else			write_3393_cmd(hostdata, WD_CMD_TRANS_INFO);		hostdata->fifo = FI_FIFO_READING;		cmd->SCp.have_data_in = 0;		return;	}/* Writing is more involved - we'll start the WD chip and write as * much data to the fifo as we can right now. Later interrupts will * write any bytes that don't make it at this stage. */	if ((hostdata->level2 >= L2_DATA) || (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {		write_3393(hostdata, WD_COMMAND_PHASE, 0x45);		write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);		hostdata->state = S_RUNNING_LEVEL2;	} else		write_3393_cmd(hostdata, WD_CMD_TRANS_INFO);	hostdata->fifo = FI_FIFO_WRITING;	sp = (unsigned short *) cmd->SCp.ptr;	if ((i = cmd->SCp.this_residual) > IN2000_FIFO_SIZE)		i = IN2000_FIFO_SIZE;	cmd->SCp.have_data_in = i;	i >>= 1;		/* Gulp. We assume this_residual is modulo 2 */	f = hostdata->io_base + IO_FIFO;#ifdef FAST_WRITE_IO	FAST_WRITE2_IO();#else	while (i--)

⌨️ 快捷键说明

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