nsp32.c

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

C
2,411
字号
	/*	 * set TRANSFER CONTROL REG	 */	transfer = 0;	transfer |= (TRANSFER_GO | ALL_COUNTER_CLR);	if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {		if (scsi_bufflen(SCpnt) > 0) {			transfer |= BM_START;		}	} else if (data->trans_method & NSP32_TRANSFER_MMIO) {		transfer |= CB_MMIO_MODE;	} else if (data->trans_method & NSP32_TRANSFER_PIO) {		transfer |= CB_IO_MODE;	}	nsp32_write2(base, TRANSFER_CONTROL, transfer);	/*	 * restart AutoSCSI	 *	 * TODO: COMMANDCONTROL_AUTO_COMMAND_PHASE is needed ?	 */	command |= (CLEAR_CDB_FIFO_POINTER |		    AUTO_COMMAND_PHASE     |		    AUTOSCSI_RESTART       );	nsp32_write2(base, COMMAND_CONTROL, command);	nsp32_dbg(NSP32_DEBUG_RESTART, "exit");}/* * cannot run automatically message in occur */static void nsp32_msgin_occur(struct scsi_cmnd     *SCpnt,			      unsigned long  irq_status,			      unsigned short execph){	nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;	unsigned int   base = SCpnt->device->host->io_port;	unsigned char  msg;	unsigned char  msgtype;	unsigned char  newlun;	unsigned short command  = 0;	int            msgclear = TRUE;	long           new_sgtp;	int            ret;	/*	 * read first message	 *    Use SCSIDATA_W_ACK instead of SCSIDATAIN, because the procedure	 *    of Message-In have to be processed before sending back SCSI ACK.	 */	msg = nsp32_read1(base, SCSI_DATA_IN);	data->msginbuf[(unsigned char)data->msgin_len] = msg;	msgtype = data->msginbuf[0];	nsp32_dbg(NSP32_DEBUG_MSGINOCCUR,		  "enter: msglen: 0x%x msgin: 0x%x msgtype: 0x%x",		  data->msgin_len, msg, msgtype);	/*	 * TODO: We need checking whether bus phase is message in?	 */	/*	 * assert SCSI ACK	 */	nsp32_sack_assert(data);	/*	 * processing IDENTIFY	 */	if (msgtype & 0x80) {		if (!(irq_status & IRQSTATUS_RESELECT_OCCUER)) {			/* Invalid (non reselect) phase */			goto reject;		}		newlun = msgtype & 0x1f; /* TODO: SPI-3 compliant? */		ret = nsp32_reselection(SCpnt, newlun);		if (ret == TRUE) {			goto restart;		} else {			goto reject;		}	}		/*	 * processing messages except for IDENTIFY	 *	 * TODO: Messages are all SCSI-2 terminology. SCSI-3 compliance is TODO.	 */	switch (msgtype) {	/*	 * 1-byte message	 */	case COMMAND_COMPLETE:	case DISCONNECT:		/*		 * These messages should not be occurred.		 * They should be processed on AutoSCSI sequencer.		 */		nsp32_msg(KERN_WARNING, 			   "unexpected message of AutoSCSI MsgIn: 0x%x", msg);		break;			case RESTORE_POINTERS:		/*		 * AutoMsgIn03 is disabled, and HBA gets this message.		 */		if ((execph & DATA_IN_PHASE) || (execph & DATA_OUT_PHASE)) {			unsigned int s_sacklen;			s_sacklen = nsp32_read4(base, SAVED_SACK_CNT);			if ((execph & MSGIN_02_VALID) && (s_sacklen > 0)) {				nsp32_adjust_busfree(SCpnt, s_sacklen);			} else {				/* No need to rewrite SGT */			}		}		data->cur_lunt->msgin03 = FALSE;		/* Update with the new value */		/* reset SACK/SavedACK counter (or ALL clear?) */		nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);		/*		 * set new sg pointer		 */		new_sgtp = data->cur_lunt->sglun_paddr + 			(data->cur_lunt->cur_entry * sizeof(nsp32_sgtable));		nsp32_write4(base, SGT_ADR, new_sgtp);		break;	case SAVE_POINTERS:		/*		 * These messages should not be occurred.		 * They should be processed on AutoSCSI sequencer.		 */		nsp32_msg (KERN_WARNING, 			   "unexpected message of AutoSCSI MsgIn: SAVE_POINTERS");				break;			case MESSAGE_REJECT:		/* If previous message_out is sending SDTR, and get 		   message_reject from target, SDTR negotiation is failed */		if (data->cur_target->sync_flag &				(SDTR_INITIATOR | SDTR_TARGET)) {			/*			 * Current target is negotiating SDTR, but it's			 * failed.  Fall back to async transfer mode, and set			 * SDTR_DONE.			 */			nsp32_set_async(data, data->cur_target);			data->cur_target->sync_flag &= ~SDTR_INITIATOR;			data->cur_target->sync_flag |= SDTR_DONE;		}		break;	case LINKED_CMD_COMPLETE:	case LINKED_FLG_CMD_COMPLETE:		/* queue tag is not supported currently */		nsp32_msg (KERN_WARNING, 			   "unsupported message: 0x%x", msgtype);		break;	case INITIATE_RECOVERY:		/* staring ECA (Extended Contingent Allegiance) state. */		/* This message is declined in SPI2 or later. */		goto reject;	/*	 * 2-byte message	 */	case SIMPLE_QUEUE_TAG:	case 0x23:		/*		 * 0x23: Ignore_Wide_Residue is not declared in scsi.h.		 * No support is needed.		 */		if (data->msgin_len >= 1) {			goto reject;		}		/* current position is 1-byte of 2 byte */		msgclear = FALSE;		break;	/*	 * extended message	 */	case EXTENDED_MESSAGE:		if (data->msgin_len < 1) {			/*			 * Current position does not reach 2-byte			 * (2-byte is extended message length).			 */			msgclear = FALSE;			break;		}		if ((data->msginbuf[1] + 1) > data->msgin_len) {			/*			 * Current extended message has msginbuf[1] + 2			 * (msgin_len starts counting from 0, so buf[1] + 1).			 * If current message position is not finished,			 * continue receiving message.			 */			msgclear = FALSE;			break;		}		/*		 * Reach here means regular length of each type of 		 * extended messages.		 */		switch (data->msginbuf[2]) {		case EXTENDED_MODIFY_DATA_POINTER:			/* TODO */			goto reject; /* not implemented yet */			break;		case EXTENDED_SDTR:			/*			 * Exchange this message between initiator and target.			 */			if (data->msgin_len != EXTENDED_SDTR_LEN + 1) {				/*				 * received inappropriate message.				 */				goto reject;				break;			}			nsp32_analyze_sdtr(SCpnt);			break;		case EXTENDED_EXTENDED_IDENTIFY:			/* SCSI-I only, not supported. */			goto reject; /* not implemented yet */			break;		case EXTENDED_WDTR:			goto reject; /* not implemented yet */			break;					default:			goto reject;		}		break;			default:		goto reject;	} restart:	if (msgclear == TRUE) {		data->msgin_len = 0;		/*		 * If restarting AutoSCSI, but there are some message to out		 * (msgout_len > 0), set AutoATN, and set SCSIMSGOUT as 0		 * (MV_VALID = 0). When commandcontrol is written with		 * AutoSCSI restart, at the same time MsgOutOccur should be		 * happened (however, such situation is really possible...?).		 */		if (data->msgout_len > 0) {				nsp32_write4(base, SCSI_MSG_OUT, 0);			command |= AUTO_ATN;		}		/*		 * restart AutoSCSI		 * If it's failed, COMMANDCONTROL_AUTO_COMMAND_PHASE is needed.		 */		command |= (AUTO_MSGIN_00_OR_04 | AUTO_MSGIN_02);		/*		 * If current msgin03 is TRUE, then flag on.		 */		if (data->cur_lunt->msgin03 == TRUE) {			command |= AUTO_MSGIN_03;		}		data->cur_lunt->msgin03 = FALSE;	} else {		data->msgin_len++;	}	/*	 * restart AutoSCSI	 */	nsp32_restart_autoscsi(SCpnt, command);	/*	 * wait SCSI REQ negate for REQ-ACK handshake	 */	nsp32_wait_req(data, NEGATE);	/*	 * negate SCSI ACK	 */	nsp32_sack_negate(data);	nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "exit");	return; reject:	nsp32_msg(KERN_WARNING, 		  "invalid or unsupported MessageIn, rejected. "		  "current msg: 0x%x (len: 0x%x), processing msg: 0x%x",		  msg, data->msgin_len, msgtype);	nsp32_build_reject(SCpnt);	data->msgin_len = 0;	goto restart;}/* *  */static void nsp32_analyze_sdtr(struct scsi_cmnd *SCpnt){	nsp32_hw_data   *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;	nsp32_target     *target     = data->cur_target;	nsp32_sync_table *synct;	unsigned char     get_period = data->msginbuf[3];	unsigned char     get_offset = data->msginbuf[4];	int               entry;	int               syncnum;	nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "enter");	synct   = data->synct;	syncnum = data->syncnum;	/*	 * If this inititor sent the SDTR message, then target responds SDTR,	 * initiator SYNCREG, ACKWIDTH from SDTR parameter.	 * Messages are not appropriate, then send back reject message.	 * If initiator did not send the SDTR, but target sends SDTR, 	 * initiator calculator the appropriate parameter and send back SDTR.	 */		if (target->sync_flag & SDTR_INITIATOR) {		/*		 * Initiator sent SDTR, the target responds and		 * send back negotiation SDTR.		 */		nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "target responds SDTR");			target->sync_flag &= ~SDTR_INITIATOR;		target->sync_flag |= SDTR_DONE;		/*		 * offset:		 */		if (get_offset > SYNC_OFFSET) {			/*			 * Negotiation is failed, the target send back			 * unexpected offset value.			 */			goto reject;		}				if (get_offset == ASYNC_OFFSET) {			/*			 * Negotiation is succeeded, the target want			 * to fall back into asynchronous transfer mode.			 */			goto async;		}		/*		 * period:		 *    Check whether sync period is too short. If too short,		 *    fall back to async mode. If it's ok, then investigate		 *    the received sync period. If sync period is acceptable		 *    between sync table start_period and end_period, then		 *    set this I_T nexus as sent offset and period.		 *    If it's not acceptable, send back reject and fall back		 *    to async mode.		 */		if (get_period < data->synct[0].period_num) {			/*			 * Negotiation is failed, the target send back			 * unexpected period value.			 */			goto reject;		}		entry = nsp32_search_period_entry(data, target, get_period);		if (entry < 0) {			/*			 * Target want to use long period which is not 			 * acceptable NinjaSCSI-32Bi/UDE.			 */			goto reject;		}		/*		 * Set new sync table and offset in this I_T nexus.		 */		nsp32_set_sync_entry(data, target, entry, get_offset);	} else {		/* Target send SDTR to initiator. */		nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "target send SDTR");			target->sync_flag |= SDTR_INITIATOR;		/* offset: */		if (get_offset > SYNC_OFFSET) {			/* send back as SYNC_OFFSET */			get_offset = SYNC_OFFSET;		}		/* period: */		if (get_period < data->synct[0].period_num) {			get_period = data->synct[0].period_num;		}		entry = nsp32_search_period_entry(data, target, get_period);		if (get_offset == ASYNC_OFFSET || entry < 0) {			nsp32_set_async(data, target);			nsp32_build_sdtr(SCpnt, 0, ASYNC_OFFSET);		} else {			nsp32_set_sync_entry(data, target, entry, get_offset);			nsp32_build_sdtr(SCpnt, get_period, get_offset);		}	}	target->period = get_period;	nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "exit");	return; reject:	/*	 * If the current message is unacceptable, send back to the target	 * with reject message.	 */	nsp32_build_reject(SCpnt); async:	nsp32_set_async(data, target);	/* set as ASYNC transfer mode */	target->period = 0;	nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "exit: set async");	return;}/* * Search config entry number matched in sync_table from given * target and speed period value. If failed to search, return negative value. */static int nsp32_search_period_entry(nsp32_hw_data *data,				     nsp32_target  *target,				     unsigned char  period){	int i;	if (target->limit_entry >= data->syncnum) {		nsp32_msg(KERN_ERR, "limit_entry exceeds syncnum!");		target->limit_entry = 0;	}	for (i = target->limit_entry; i < data->syncnum; i++) {		if (period >

⌨️ 快捷键说明

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