in2000.c

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

C
2,045
字号
				hostdata->sync_stat[cmd->device->id] = SS_SET;			write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);			hostdata->state = S_CONNECTED;			break;		case EXTENDED_MESSAGE:			DB(DB_INTR, printk("EXT"))			    ucp = hostdata->incoming_msg;#ifdef SYNC_DEBUG			printk("%02x", ucp[hostdata->incoming_ptr]);#endif			/* Is this the last byte of the extended message? */			if ((hostdata->incoming_ptr >= 2) && (hostdata->incoming_ptr == (ucp[1] + 1))) {				switch (ucp[2]) {	/* what's the EXTENDED code? */				case EXTENDED_SDTR:					id = calc_sync_xfer(ucp[3], ucp[4]);					if (hostdata->sync_stat[cmd->device->id] != SS_WAITING) {/* A device has sent an unsolicited SDTR message; rather than go * through the effort of decoding it and then figuring out what * our reply should be, we're just gonna say that we have a * synchronous fifo depth of 0. This will result in asynchronous * transfers - not ideal but so much easier. * Actually, this is OK because it assures us that if we don't * specifically ask for sync transfers, we won't do any. */						write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN);	/* want MESS_OUT */						hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;						hostdata->outgoing_msg[1] = 3;						hostdata->outgoing_msg[2] = EXTENDED_SDTR;						hostdata->outgoing_msg[3] = hostdata->default_sx_per / 4;						hostdata->outgoing_msg[4] = 0;						hostdata->outgoing_len = 5;						hostdata->sync_xfer[cmd->device->id] = calc_sync_xfer(hostdata->default_sx_per / 4, 0);					} else {						hostdata->sync_xfer[cmd->device->id] = id;					}#ifdef SYNC_DEBUG					printk("sync_xfer=%02x", hostdata->sync_xfer[cmd->device->id]);#endif					hostdata->sync_stat[cmd->device->id] = SS_SET;					write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);					hostdata->state = S_CONNECTED;					break;				case EXTENDED_WDTR:					write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN);	/* want MESS_OUT */					printk("sending WDTR ");					hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;					hostdata->outgoing_msg[1] = 2;					hostdata->outgoing_msg[2] = EXTENDED_WDTR;					hostdata->outgoing_msg[3] = 0;	/* 8 bit transfer width */					hostdata->outgoing_len = 4;					write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);					hostdata->state = S_CONNECTED;					break;				default:					write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN);	/* want MESS_OUT */					printk("Rejecting Unknown Extended Message(%02x). ", ucp[2]);					hostdata->outgoing_msg[0] = MESSAGE_REJECT;					hostdata->outgoing_len = 1;					write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);					hostdata->state = S_CONNECTED;					break;				}				hostdata->incoming_ptr = 0;			}			/* We need to read more MESS_IN bytes for the extended message */			else {				hostdata->incoming_ptr++;				write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);				hostdata->state = S_CONNECTED;			}			break;		default:			printk("Rejecting Unknown Message(%02x) ", msg);			write_3393_cmd(hostdata, WD_CMD_ASSERT_ATN);	/* want MESS_OUT */			hostdata->outgoing_msg[0] = MESSAGE_REJECT;			hostdata->outgoing_len = 1;			write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);			hostdata->state = S_CONNECTED;		}		break;/* Note: this interrupt will occur only after a LEVEL2 command */	case CSR_SEL_XFER_DONE:/* Make sure that reselection is enabled at this point - it may * have been turned off for the command that just completed. */		write_3393(hostdata, WD_SOURCE_ID, SRCID_ER);		if (phs == 0x60) {			DB(DB_INTR, printk("SX-DONE-%ld", cmd->serial_number))			    cmd->SCp.Message = COMMAND_COMPLETE;			lun = read_3393(hostdata, WD_TARGET_LUN);			DB(DB_INTR, printk(":%d.%d", cmd->SCp.Status, lun))			    hostdata->connected = NULL;			hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);			hostdata->state = S_UNCONNECTED;			if (cmd->SCp.Status == ILLEGAL_STATUS_BYTE)				cmd->SCp.Status = lun;			if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)				cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);			else				cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);			cmd->scsi_done(cmd);/* We are no longer connected to a target - check to see if * there are commands waiting to be executed. */			in2000_execute(instance);		} else {			printk("%02x:%02x:%02x-%ld: Unknown SEL_XFER_DONE phase!!---", asr, sr, phs, cmd->serial_number);		}		break;/* Note: this interrupt will occur only after a LEVEL2 command */	case CSR_SDP:		DB(DB_INTR, printk("SDP"))		    hostdata->state = S_RUNNING_LEVEL2;		write_3393(hostdata, WD_COMMAND_PHASE, 0x41);		write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);		break;	case CSR_XFER_DONE | PHS_MESS_OUT:	case CSR_UNEXP | PHS_MESS_OUT:	case CSR_SRV_REQ | PHS_MESS_OUT:		DB(DB_INTR, printk("MSG_OUT="))/* To get here, we've probably requested MESSAGE_OUT and have * already put the correct bytes in outgoing_msg[] and filled * in outgoing_len. We simply send them out to the SCSI bus. * Sometimes we get MESSAGE_OUT phase when we're not expecting * it - like when our SDTR message is rejected by a target. Some * targets send the REJECT before receiving all of the extended * message, and then seem to go back to MESSAGE_OUT for a byte * or two. Not sure why, or if I'm doing something wrong to * cause this to happen. Regardless, it seems that sending * NOP messages in these situations results in no harm and * makes everyone happy. */		    if (hostdata->outgoing_len == 0) {			hostdata->outgoing_len = 1;			hostdata->outgoing_msg[0] = NOP;		}		transfer_pio(hostdata->outgoing_msg, hostdata->outgoing_len, DATA_OUT_DIR, hostdata);		DB(DB_INTR, printk("%02x", hostdata->outgoing_msg[0]))		    hostdata->outgoing_len = 0;		hostdata->state = S_CONNECTED;		break;	case CSR_UNEXP_DISC:/* I think I've seen this after a request-sense that was in response * to an error condition, but not sure. We certainly need to do * something when we get this interrupt - the question is 'what?'. * Let's think positively, and assume some command has finished * in a legal manner (like a command that provokes a request-sense), * so we treat it as a normal command-complete-disconnect. *//* Make sure that reselection is enabled at this point - it may * have been turned off for the command that just completed. */		write_3393(hostdata, WD_SOURCE_ID, SRCID_ER);		if (cmd == NULL) {			printk(" - Already disconnected! ");			hostdata->state = S_UNCONNECTED;/* release the SMP spin_lock and restore irq state */			spin_unlock_irqrestore(instance->host_lock, flags);			return IRQ_HANDLED;		}		DB(DB_INTR, printk("UNEXP_DISC-%ld", cmd->serial_number))		    hostdata->connected = NULL;		hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);		hostdata->state = S_UNCONNECTED;		if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)			cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);		else			cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);		cmd->scsi_done(cmd);/* We are no longer connected to a target - check to see if * there are commands waiting to be executed. */		in2000_execute(instance);		break;	case CSR_DISC:/* Make sure that reselection is enabled at this point - it may * have been turned off for the command that just completed. */		write_3393(hostdata, WD_SOURCE_ID, SRCID_ER);		DB(DB_INTR, printk("DISC-%ld", cmd->serial_number))		    if (cmd == NULL) {			printk(" - Already disconnected! ");			hostdata->state = S_UNCONNECTED;		}		switch (hostdata->state) {		case S_PRE_CMP_DISC:			hostdata->connected = NULL;			hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);			hostdata->state = S_UNCONNECTED;			DB(DB_INTR, printk(":%d", cmd->SCp.Status))			    if (cmd->cmnd[0] == REQUEST_SENSE && cmd->SCp.Status != GOOD)				cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);			else				cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);			cmd->scsi_done(cmd);			break;		case S_PRE_TMP_DISC:		case S_RUNNING_LEVEL2:			cmd->host_scribble = (uchar *) hostdata->disconnected_Q;			hostdata->disconnected_Q = cmd;			hostdata->connected = NULL;			hostdata->state = S_UNCONNECTED;#ifdef PROC_STATISTICS			hostdata->disc_done_cnt[cmd->device->id]++;#endif			break;		default:			printk("*** Unexpected DISCONNECT interrupt! ***");			hostdata->state = S_UNCONNECTED;		}/* We are no longer connected to a target - check to see if * there are commands waiting to be executed. */		in2000_execute(instance);		break;	case CSR_RESEL_AM:		DB(DB_INTR, printk("RESEL"))		    /* First we have to make sure this reselection didn't */		    /* happen during Arbitration/Selection of some other device. */		    /* If yes, put losing command back on top of input_Q. */		    if (hostdata->level2 <= L2_NONE) {			if (hostdata->selecting) {				cmd = (Scsi_Cmnd *) hostdata->selecting;				hostdata->selecting = NULL;				hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);				cmd->host_scribble = (uchar *) hostdata->input_Q;				hostdata->input_Q = cmd;			}		}		else {			if (cmd) {				if (phs == 0x00) {					hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);					cmd->host_scribble = (uchar *) hostdata->input_Q;					hostdata->input_Q = cmd;				} else {					printk("---%02x:%02x:%02x-TROUBLE: Intrusive ReSelect!---", asr, sr, phs);					while (1)						printk("\r");				}			}		}		/* OK - find out which device reselected us. */		id = read_3393(hostdata, WD_SOURCE_ID);		id &= SRCID_MASK;		/* and extract the lun from the ID message. (Note that we don't		 * bother to check for a valid message here - I guess this is		 * not the right way to go, but....)		 */		lun = read_3393(hostdata, WD_DATA);		if (hostdata->level2 < L2_RESELECT)			write_3393_cmd(hostdata, WD_CMD_NEGATE_ACK);		lun &= 7;		/* Now we look for the command that's reconnecting. */		cmd = (Scsi_Cmnd *) hostdata->disconnected_Q;		patch = NULL;		while (cmd) {			if (id == cmd->device->id && lun == cmd->device->lun)				break;			patch = cmd;			cmd = (Scsi_Cmnd *) cmd->host_scribble;		}		/* Hmm. Couldn't find a valid command.... What to do? */		if (!cmd) {			printk("---TROUBLE: target %d.%d not in disconnect queue---", id, lun);			break;		}		/* Ok, found the command - now start it up again. */		if (patch)			patch->host_scribble = cmd->host_scribble;		else			hostdata->disconnected_Q = (Scsi_Cmnd *) cmd->host_scribble;		hostdata->connected = cmd;		/* We don't need to worry about 'initialize_SCp()' or 'hostdata->busy[]'		 * because these things are preserved over a disconnect.		 * But we DO need to fix the DPD bit so it's correct for this command.		 */		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);		if (hostdata->level2 >= L2_RESELECT) {			write_3393_count(hostdata, 0);	/* we want a DATA_PHASE interrupt */			write_3393(hostdata, WD_COMMAND_PHASE, 0x45);			write_3393_cmd(hostdata, WD_CMD_SEL_ATN_XFER);			hostdata->state = S_RUNNING_LEVEL2;		} else			hostdata->state = S_CONNECTED;		DB(DB_INTR, printk("-%ld", cmd->serial_number))		    break;	default:		printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--", asr, sr, phs);	}	write1_io(0, IO_LED_OFF);	DB(DB_INTR, printk("} "))/* release the SMP spin_lock and restore irq state */	    spin_unlock_irqrestore(instance->host_lock, flags);	return IRQ_HANDLED;}#define RESET_CARD         0#define RESET_CARD_AND_BUS 1#define B_FLAG 0x80/* *	Caller must hold instance lock! */static int reset_hardware(struct Scsi_Host *instance, int type){	struct IN2000_hostdata *hostdata;	int qt, x;	hostdata = (struct IN2000_hostdata *) instance->hostdata;	write1_io(0, IO_LED_ON);	if (type == RESET_CARD_AND_BUS) {		write1_io(0, IO_CARD_RESET);		x = read1_io(IO_HARDWARE);	}	x = read_3393(hostdata, WD_SCSI_STATUS);	/* clear any WD intrpt */	write_3393(hostdata, WD_OWN_ID, instance->this_id | OWNID_EAF | OWNID_RAF | OWNID_FS_8);	write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);	write_3393(hostdata, WD_SYNCHRONOUS_TRANSFER, calc_sync_xfer(hostdata->default_sx_per / 4, DEFAULT_SX_OFF));	write1_io(0, IO_FIFO_WRITE);	/* clear fifo counter */	write1_io(0, IO_FIFO_READ);	/* start fifo out in read mode */	write_3393(hostdata, WD_COMMAND, WD_CMD_RESET);	/* FIXME: timeout ?? */	while (!(READ_AUX_STAT() & ASR_INT))		cpu_relax();	/* wait for RESET to complete */	x = read_3393(hostdata, WD_SCSI_STATUS);	/* clear interrupt */	write_3393(hostdata, WD_QUEUE_TAG, 0xa5);	/* any random number */	qt = read_3393(hostdata, WD_QUEUE_TAG);	if (qt == 0xa5) {		x |= B_FLAG;		write_3393(hostdata, WD_QUEUE_TAG, 0);	}	write_3393(hostdata, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE);	write_3393(hostdata, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);	write1_io(0, IO_LED_OFF);

⌨️ 快捷键说明

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