nsp32.c

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

C
2,411
字号
	/*	 * message out	 *	 * Note: If the range of msgout_len is 1 - 3, fill scsi_msgout.	 *       over 3 messages needs another routine.	 */	if (data->msgout_len == 0) {		nsp32_msg(KERN_ERR, "SCSI MsgOut without any message!");		SCpnt->result = DID_ERROR << 16;		return FALSE;	} else if (data->msgout_len > 0 && data->msgout_len <= 3) {		msgout = 0;		for (i = 0; i < data->msgout_len; i++) {			/*			 * the sending order of the message is:			 *  MCNT 3: MSG#0 -> MSG#1 -> MSG#2			 *  MCNT 2:          MSG#1 -> MSG#2			 *  MCNT 1:                   MSG#2    			 */			msgout >>= 8;			msgout |= ((unsigned int)(data->msgoutbuf[i]) << 24);		}		msgout |= MV_VALID;	/* MV valid */		msgout |= (unsigned int)data->msgout_len; /* len */	} else {		/* data->msgout_len > 3 */		msgout = 0;	}	// nsp_dbg(NSP32_DEBUG_AUTOSCSI, "sel time out=0x%x\n", nsp32_read2(base, SEL_TIME_OUT));	// nsp32_write2(base, SEL_TIME_OUT,   SEL_TIMEOUT_TIME);	/*	 * setup asic parameter	 */	memset(param, 0, sizeof(nsp32_autoparam));	/* cdb */	for (i = 0; i < SCpnt->cmd_len; i++) {		param->cdb[4 * i] = SCpnt->cmnd[i];	}	/* outgoing messages */	param->msgout = cpu_to_le32(msgout);	/* syncreg, ackwidth, target id, SREQ sampling rate */	param->syncreg    = data->cur_target->syncreg;	param->ackwidth   = data->cur_target->ackwidth;	param->target_id  = BIT(host_id) | BIT(target);	param->sample_reg = data->cur_target->sample_reg;	// nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "sample rate=0x%x\n", data->cur_target->sample_reg);	/* command control */	param->command_control = cpu_to_le16(CLEAR_CDB_FIFO_POINTER |					     AUTOSCSI_START         |					     AUTO_MSGIN_00_OR_04    |					     AUTO_MSGIN_02          |					     AUTO_ATN               );	/* transfer control */	s = 0;	switch (data->trans_method) {	case NSP32_TRANSFER_BUSMASTER:		s |= BM_START;		break;	case NSP32_TRANSFER_MMIO:		s |= CB_MMIO_MODE;		break;	case NSP32_TRANSFER_PIO:		s |= CB_IO_MODE;		break;	default:		nsp32_msg(KERN_ERR, "unknown trans_method");		break;	}	/*	 * OR-ed BLIEND_MODE, FIFO intr is decreased, instead of PCI bus waits.	 * For bus master transfer, it's taken off.	 */	s |= (TRANSFER_GO | ALL_COUNTER_CLR);	param->transfer_control = cpu_to_le16(s);	/* sg table addr */	param->sgt_pointer = cpu_to_le32(data->cur_lunt->sglun_paddr);	/*	 * transfer parameter to ASIC	 */	nsp32_write4(base, SGT_ADR,         data->auto_paddr);	nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER |		                            AUTO_PARAMETER         );	/*	 * Check arbitration	 */	ret = nsp32_arbitration(SCpnt, base);	return ret;}/* * Selection with AUTO SCSI (without AUTO PARAMETER) */static int nsp32_selection_autoscsi(struct scsi_cmnd *SCpnt){	nsp32_hw_data  *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;	unsigned int	base    = SCpnt->device->host->io_port;	unsigned int	host_id = SCpnt->device->host->this_id;	unsigned char	target  = scmd_id(SCpnt);	unsigned char	phase;	int		status;	unsigned short	command	= 0;	unsigned int	msgout  = 0;	unsigned short	execph;	int		i;	nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "in");	/*	 * IRQ disable	 */	nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);	/*	 * check bus line	 */	phase = nsp32_read1(base, SCSI_BUS_MONITOR);	if(((phase & BUSMON_BSY) == 1) || (phase & BUSMON_SEL) == 1) {		nsp32_msg(KERN_WARNING, "bus busy");		SCpnt->result = DID_BUS_BUSY << 16;		status = 1;		goto out;        }	/*	 * clear execph	 */	execph = nsp32_read2(base, SCSI_EXECUTE_PHASE);	/*	 * clear FIFO counter to set CDBs	 */	nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER);	/*	 * set CDB0 - CDB15	 */	for (i = 0; i < SCpnt->cmd_len; i++) {		nsp32_write1(base, COMMAND_DATA, SCpnt->cmnd[i]);        }	nsp32_dbg(NSP32_DEBUG_CDB_CONTENTS, "CDB[0]=[0x%x]", SCpnt->cmnd[0]);	/*	 * set SCSIOUT LATCH(initiator)/TARGET(target) (OR-ed) ID	 */	nsp32_write1(base, SCSI_OUT_LATCH_TARGET_ID, BIT(host_id) | BIT(target));	/*	 * set SCSI MSGOUT REG	 *	 * Note: If the range of msgout_len is 1 - 3, fill scsi_msgout.	 *       over 3 messages needs another routine.	 */	if (data->msgout_len == 0) {		nsp32_msg(KERN_ERR, "SCSI MsgOut without any message!");		SCpnt->result = DID_ERROR << 16;		status = 1;		goto out;	} else if (data->msgout_len > 0 && data->msgout_len <= 3) {		msgout = 0;		for (i = 0; i < data->msgout_len; i++) {			/*			 * the sending order of the message is:			 *  MCNT 3: MSG#0 -> MSG#1 -> MSG#2			 *  MCNT 2:          MSG#1 -> MSG#2			 *  MCNT 1:                   MSG#2    			 */			msgout >>= 8;			msgout |= ((unsigned int)(data->msgoutbuf[i]) << 24);		}		msgout |= MV_VALID;	/* MV valid */		msgout |= (unsigned int)data->msgout_len; /* len */		nsp32_write4(base, SCSI_MSG_OUT, msgout);	} else {		/* data->msgout_len > 3 */		nsp32_write4(base, SCSI_MSG_OUT, 0);	}	/*	 * set selection timeout(= 250ms)	 */	nsp32_write2(base, SEL_TIME_OUT,   SEL_TIMEOUT_TIME);	/*	 * set SREQ hazard killer sampling rate	 * 	 * TODO: sample_rate (BASE+0F) is 0 when internal clock = 40MHz.	 *      check other internal clock!	 */	nsp32_write1(base, SREQ_SMPL_RATE, data->cur_target->sample_reg);	/*	 * clear Arbit	 */	nsp32_write1(base, SET_ARBIT,      ARBIT_CLEAR);	/*	 * set SYNCREG	 * Don't set BM_START_ADR before setting this register.	 */	nsp32_write1(base, SYNC_REG,  data->cur_target->syncreg);	/*	 * set ACKWIDTH	 */	nsp32_write1(base, ACK_WIDTH, data->cur_target->ackwidth);	nsp32_dbg(NSP32_DEBUG_AUTOSCSI,		  "syncreg=0x%x, ackwidth=0x%x, sgtpaddr=0x%x, id=0x%x",		  nsp32_read1(base, SYNC_REG), nsp32_read1(base, ACK_WIDTH),		  nsp32_read4(base, SGT_ADR), nsp32_read1(base, SCSI_OUT_LATCH_TARGET_ID));	nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "msgout_len=%d, msgout=0x%x",		  data->msgout_len, msgout);	/*	 * set SGT ADDR (physical address)	 */	nsp32_write4(base, SGT_ADR, data->cur_lunt->sglun_paddr);	/*	 * set TRANSFER CONTROL REG	 */	command = 0;	command |= (TRANSFER_GO | ALL_COUNTER_CLR);	if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {		if (scsi_bufflen(SCpnt) > 0) {			command |= BM_START;		}	} else if (data->trans_method & NSP32_TRANSFER_MMIO) {		command |= CB_MMIO_MODE;	} else if (data->trans_method & NSP32_TRANSFER_PIO) {		command |= CB_IO_MODE;	}	nsp32_write2(base, TRANSFER_CONTROL, command);	/*	 * start AUTO SCSI, kick off arbitration	 */	command = (CLEAR_CDB_FIFO_POINTER |		   AUTOSCSI_START         |		   AUTO_MSGIN_00_OR_04    |		   AUTO_MSGIN_02          |		   AUTO_ATN                );	nsp32_write2(base, COMMAND_CONTROL, command);	/*	 * Check arbitration	 */	status = nsp32_arbitration(SCpnt, base); out:	/*	 * IRQ enable	 */	nsp32_write2(base, IRQ_CONTROL, 0);	return status;}/* * Arbitration Status Check *	 * Note: Arbitration counter is waited during ARBIT_GO is not lifting. *	 Using udelay(1) consumes CPU time and system time, but  *	 arbitration delay time is defined minimal 2.4us in SCSI *	 specification, thus udelay works as coarse grained wait timer. */static int nsp32_arbitration(struct scsi_cmnd *SCpnt, unsigned int base){	unsigned char arbit;	int	      status = TRUE;	int	      time   = 0;	do {		arbit = nsp32_read1(base, ARBIT_STATUS);		time++;	} while ((arbit & (ARBIT_WIN | ARBIT_FAIL)) == 0 &&		 (time <= ARBIT_TIMEOUT_TIME));	nsp32_dbg(NSP32_DEBUG_AUTOSCSI,		  "arbit: 0x%x, delay time: %d", arbit, time);	if (arbit & ARBIT_WIN) {		/* Arbitration succeeded */		SCpnt->result = DID_OK << 16;		nsp32_index_write1(base, EXT_PORT, LED_ON); /* PCI LED on */	} else if (arbit & ARBIT_FAIL) {		/* Arbitration failed */		SCpnt->result = DID_BUS_BUSY << 16;		status = FALSE;	} else {		/*		 * unknown error or ARBIT_GO timeout,		 * something lock up! guess no connection.		 */		nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "arbit timeout");		SCpnt->result = DID_NO_CONNECT << 16;		status = FALSE;        }	/*	 * clear Arbit	 */	nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);	return status;}/* * reselection * * Note: This reselection routine is called from msgin_occur, *	 reselection target id&lun must be already set. *	 SCSI-2 says IDENTIFY implies RESTORE_POINTER operation. */static int nsp32_reselection(struct scsi_cmnd *SCpnt, unsigned char newlun){	nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;	unsigned int   host_id = SCpnt->device->host->this_id;	unsigned int   base    = SCpnt->device->host->io_port;	unsigned char  tmpid, newid;	nsp32_dbg(NSP32_DEBUG_RESELECTION, "enter");	/*	 * calculate reselected SCSI ID	 */	tmpid = nsp32_read1(base, RESELECT_ID);	tmpid &= (~BIT(host_id));	newid = 0;	while (tmpid) {		if (tmpid & 1) {			break;		}		tmpid >>= 1;		newid++;	}	/*	 * If reselected New ID:LUN is not existed	 * or current nexus is not existed, unexpected	 * reselection is occurred. Send reject message.	 */	if (newid >= ARRAY_SIZE(data->lunt) || newlun >= ARRAY_SIZE(data->lunt[0])) {		nsp32_msg(KERN_WARNING, "unknown id/lun");		return FALSE;	} else if(data->lunt[newid][newlun].SCpnt == NULL) {		nsp32_msg(KERN_WARNING, "no SCSI command is processing");		return FALSE;	}	data->cur_id    = newid;	data->cur_lun   = newlun;	data->cur_target = &(data->target[newid]);	data->cur_lunt   = &(data->lunt[newid][newlun]);	/* reset SACK/SavedACK counter (or ALL clear?) */	nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);	return TRUE;}/* * nsp32_setup_sg_table - build scatter gather list for transfer data *			    with bus master. * * Note: NinjaSCSI-32Bi/UDE bus master can not transfer over 64KB at a time. */static int nsp32_setup_sg_table(struct scsi_cmnd *SCpnt){	nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;	struct scatterlist *sg;	nsp32_sgtable *sgt = data->cur_lunt->sglun->sgt;	int num, i;	u32_le l;	if (sgt == NULL) {		nsp32_dbg(NSP32_DEBUG_SGLIST, "SGT == null");		return FALSE;	}	num = scsi_dma_map(SCpnt);	if (!num)		return TRUE;	else if (num < 0)		return FALSE;	else {		scsi_for_each_sg(SCpnt, sg, num, i) {			/*			 * Build nsp32_sglist, substitute sg dma addresses.			 */			sgt[i].addr = cpu_to_le32(sg_dma_address(sg));			sgt[i].len  = cpu_to_le32(sg_dma_len(sg));			if (le32_to_cpu(sgt[i].len) > 0x10000) {				nsp32_msg(KERN_ERR,					"can't transfer over 64KB at a time, size=0x%lx", le32_to_cpu(sgt[i].len));				return FALSE;			}			nsp32_dbg(NSP32_DEBUG_SGLIST,				  "num 0x%x : addr 0x%lx len 0x%lx",				  i,				  le32_to_cpu(sgt[i].addr),				  le32_to_cpu(sgt[i].len ));		}		/* set end mark */		l = le32_to_cpu(sgt[num-1].len);		sgt[num-1].len = cpu_to_le32(l | SGTEND);	}	return TRUE;}static int nsp32_queuecommand(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)){	nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;	nsp32_target *target;	nsp32_lunt   *cur_lunt;	int ret;	nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,		  "enter. target: 0x%x LUN: 0x%x cmnd: 0x%x cmndlen: 0x%x "		  "use_sg: 0x%x reqbuf: 0x%lx reqlen: 0x%x",		  SCpnt->device->id, SCpnt->device->lun, SCpnt->cmnd[0], SCpnt->cmd_len,		  scsi_sg_count(SCpnt), scsi_sglist(SCpnt), scsi_bufflen(SCpnt));	if (data->CurrentSC != NULL) {		nsp32_msg(KERN_ERR, "Currentsc != NULL. Cancel this command request");		data->CurrentSC = NULL;		SCpnt->result   = DID_NO_CONNECT << 16;		done(SCpnt);		return 0;	}	/* check target ID is not same as this initiator ID */	if (scmd_id(SCpnt) == SCpnt->device->host->this_id) {		nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND, "terget==host???");		SCpnt->result = DID_BAD_TARGET << 16;		done(SCpnt);		return 0;	}	/* check target LUN is allowable value */	if (SCpnt->device->lun >= MAX_LUN) {		nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND, "no more lun");		SCpnt->result = DID_BAD_TARGET << 16;		done(SCpnt);		return 0;	}	show_command(SCpnt);	SCpnt->scsi_done     = done;	data->CurrentSC      = SCpnt;	SCpnt->SCp.Status    = CHECK_CONDITION;	SCpnt->SCp.Message   = 0;	scsi_set_resid(SCpnt, scsi_bufflen(SCpnt));	SCpnt->SCp.ptr		    = (char *)scsi_sglist(SCpnt);	SCpnt->SCp.this_residual    = scsi_bufflen(SCpnt);	SCpnt->SCp.buffer	    = NULL;	SCpnt->SCp.buffers_residual = 0;	/* initialize data */	data->msgout_len	= 0;

⌨️ 快捷键说明

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