wd33c93.c

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

C
2,145
字号
 * While such a command can then be "resumed" (ie restarted to * finish up as a LEVEL2 command), the LUN register will NOT be * a valid status byte at the command's conclusion, and we must * use the byte obtained during the earlier interrupt. Here, we * preset SCp.Status to an illegal value (0xff) so that when * this command finally completes, we can tell where the actual * status byte is stored. */	cmd->SCp.Status = ILLEGAL_STATUS_BYTE;	/*	 * 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.	 */	spin_lock_irq(&hostdata->lock);	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 = (struct scsi_cmnd *) hostdata->input_Q;		     tmp->host_scribble;		     tmp = (struct 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! */	wd33c93_execute(cmd->device->host);	DB(DB_QUEUE_COMMAND, printk(")Q-%ld ", cmd->serial_number))	spin_unlock_irq(&hostdata->lock);	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. * * wd33c93_execute() is always called with interrupts disabled or from * the wd33c93_intr itself, which means that a wd33c93 interrupt * cannot occur while we are in here. */static voidwd33c93_execute(struct Scsi_Host *instance){	struct WD33C93_hostdata *hostdata =	    (struct WD33C93_hostdata *) instance->hostdata;	const wd33c93_regs regs = hostdata->regs;	struct scsi_cmnd *cmd, *prev;	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 = (struct scsi_cmnd *) hostdata->input_Q;	prev = NULL;	while (cmd) {		if (!(hostdata->busy[cmd->device->id] & (1 << cmd->device->lun)))			break;		prev = cmd;		cmd = (struct 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 = (struct scsi_cmnd *) cmd->host_scribble;#ifdef PROC_STATISTICS	hostdata->cmd_cnt[cmd->device->id]++;#endif	/*	 * Start the selection process	 */	if (cmd->sc_data_direction == DMA_TO_DEVICE)		write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id);	else		write_wd33c93(regs, 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 = (struct scsi_cmnd *) hostdata->input_Q; prev;	     prev = (struct scsi_cmnd *) prev->host_scribble) {		if ((prev->device->id != cmd->device->id) ||		    (prev->device->lun != cmd->device->lun)) {			for (prev = (struct scsi_cmnd *) hostdata->input_Q; prev;			     prev = (struct 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_wd33c93(regs, WD_SOURCE_ID, ((cmd->SCp.phase) ? SRCID_ER : 0));	write_wd33c93(regs, WD_TARGET_LUN, cmd->device->lun);	write_wd33c93(regs, 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 SX_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. * If we've been asked not to try synchronous transfers on this * target (and _all_ luns within it), we'll still send the SDTR message * later, but at that time we'll negotiate for async by specifying a * sync fifo depth of 0. */		if (hostdata->sync_stat[cmd->device->id] == SS_UNSET)			hostdata->sync_stat[cmd->device->id] = SS_FIRST;		hostdata->state = S_SELECTING;		write_wd33c93_count(regs, 0);	/* guarantee a DATA_PHASE interrupt */		write_wd33c93_cmd(regs, 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_wd33c93(regs, WD_COMMAND_PHASE, 0);		/* copy command_descriptor_block into WD chip		 * (take advantage of auto-incrementing)		 */		write_wd33c93_cdb(regs, cmd->cmd_len, cmd->cmnd);		/* 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_wd33c93(regs, WD_OWN_ID, cmd->cmd_len);		/* When doing a non-disconnect command with DMA, we can save		 * ourselves a DATA phase interrupt later by setting everything		 * up ahead of time.		 */		if ((cmd->SCp.phase == 0) && (hostdata->no_dma == 0)) {			if (hostdata->dma_setup(cmd,			    (cmd->sc_data_direction == DMA_TO_DEVICE) ?			     DATA_OUT_DIR : DATA_IN_DIR))				write_wd33c93_count(regs, 0);	/* guarantee a DATA_PHASE interrupt */			else {				write_wd33c93_count(regs,						    cmd->SCp.this_residual);				write_wd33c93(regs, WD_CONTROL,					      CTRL_IDI | CTRL_EDI | hostdata->dma_mode);				hostdata->dma = D_DMA_RUNNING;			}		} else			write_wd33c93_count(regs, 0);	/* guarantee a DATA_PHASE interrupt */		hostdata->state = S_RUNNING_LEVEL2;		write_wd33c93_cmd(regs, 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 voidtransfer_pio(const wd33c93_regs regs, uchar * buf, int cnt,	     int data_in_dir, struct WD33C93_hostdata *hostdata){	uchar asr;	DB(DB_TRANSFER,	   printk("(%p,%d,%s:", buf, cnt, data_in_dir ? "in" : "out"))	write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);	write_wd33c93_count(regs, cnt);	write_wd33c93_cmd(regs, WD_CMD_TRANS_INFO);	if (data_in_dir) {		do {			asr = read_aux_stat(regs);			if (asr & ASR_DBR)				*buf++ = read_wd33c93(regs, WD_DATA);		} while (!(asr & ASR_INT));	} else {		do {			asr = read_aux_stat(regs);			if (asr & ASR_DBR)				write_wd33c93(regs, 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 voidtransfer_bytes(const wd33c93_regs regs, struct scsi_cmnd *cmd,		int data_in_dir){	struct WD33C93_hostdata *hostdata;	unsigned long length;	hostdata = (struct WD33C93_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);	}	if (!cmd->SCp.this_residual) /* avoid bogus setups */		return;	write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,		      hostdata->sync_xfer[cmd->device->id]);/* 'hostdata->no_dma' is TRUE if we don't even want to try DMA. * Update 'this_residual' and 'ptr' after 'transfer_pio()' returns. */	if (hostdata->no_dma || hostdata->dma_setup(cmd, data_in_dir)) {#ifdef PROC_STATISTICS		hostdata->pio_cnt++;#endif		transfer_pio(regs, (uchar *) cmd->SCp.ptr,			     cmd->SCp.this_residual, data_in_dir, hostdata);		length = cmd->SCp.this_residual;		cmd->SCp.this_residual = read_wd33c93_count(regs);		cmd->SCp.ptr += (length - cmd->SCp.this_residual);	}/* We are able to do DMA (in fact, the Amiga hardware is * already going!), so start up the wd33c93 in DMA mode. * We set 'hostdata->dma' = D_DMA_RUNNING so that when the * transfer completes and causes an interrupt, we're * reminded to tell the Amiga to shut down its end. We'll * postpone the updating of 'this_residual' and 'ptr' * until then. */	else {#ifdef PROC_STATISTICS		hostdata->dma_cnt++;#endif		write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | hostdata->dma_mode);		write_wd33c93_count(regs, cmd->SCp.this_residual);		if ((hostdata->level2 >= L2_DATA) ||		    (hostdata->level2 == L2_BASIC && cmd->SCp.phase == 0)) {			write_wd33c93(regs, WD_COMMAND_PHASE, 0x45);			write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);			hostdata->state = S_RUNNING_LEVEL2;		} else			write_wd33c93_cmd(regs, WD_CMD_TRANS_INFO);		hostdata->dma = D_DMA_RUNNING;	}}voidwd33c93_intr(struct Scsi_Host *instance){	struct WD33C93_hostdata *hostdata =	    (struct WD33C93_hostdata *) instance->hostdata;	const wd33c93_regs regs = hostdata->regs;	struct scsi_cmnd *patch, *cmd;	uchar asr, sr, phs, id, lun, *ucp, msg;	unsigned long length, flags;	asr = read_aux_stat(regs);	if (!(asr & ASR_INT) || (asr & ASR_BSY))		return;	spin_lock_irqsave(&hostdata->lock, flags);#ifdef PROC_STATISTICS	hostdata->int_cnt++;#endif	cmd = (struct scsi_cmnd *) hostdata->connected;	/* assume we're connected */	sr = read_wd33c93(regs, WD_SCSI_STATUS);	/* clear the interrupt */	phs = read_wd33c93(regs, WD_COMMAND_PHASE);	DB(DB_INTR, printk("{%02x:%02x-", asr, sr))/* After starting a DMA transfer, the next interrupt * is guaranteed to be in response to completion of * the transfer. Since the Amiga DMA hardware runs in * in an open-ended fashion, it needs to be told when * to stop; do that here if D_DMA_RUNNING is true. * Also, we have to update 'this_residual' and 'ptr' * based on the contents of the TRANSFER_COUNT register, * in case the device decided to do an intermediate * disconnect (a device may do this if it has to do a * seek, or just to be nice and let other devices have * some bus time during long transfers). After doing * whatever is needed, we go on and service the WD3393 * interrupt normally. */	    if (hostdata->dma == D_DMA_RUNNING) {		DB(DB_TRANSFER,		   printk("[%p/%d:", cmd->SCp.ptr, cmd->SCp.this_residual))		    hostdata->dma_stop(cmd->device->host, cmd, 1);		hostdata->dma = D_DMA_OFF;		length = cmd->SCp.this_residual;		cmd->SCp.this_residual = read_wd33c93_count(regs);		cmd->SCp.ptr += (length - cmd->SCp.this_residual);		DB(DB_TRANSFER,		   printk("%p/%d]", cmd->SCp.ptr, cmd->SCp.this_residual))	}/* Respond to the specific WD3393 interrupt - there are quite a few! */	switch (sr) {	case CSR_TIMEOUT:		DB(DB_INTR, printk("TIMEOUT"))		    if (hostdata->state == S_RUNNING_LEVEL2)			hostdata->connected = NULL;		else {			cmd = (struct scsi_cmnd *) hostdata->selecting;	/* get a valid cmd */

⌨️ 快捷键说明

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