⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mesh.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	dlog(ms, "error err/exc/fc/cl=%.8x",	     MKWORD(err, exc, mr->fifo_count, mr->count_lo));	if (err & ERR_SCSIRESET) {		/* SCSI bus was reset */		printk(KERN_INFO "mesh: SCSI bus reset detected: "		       "waiting for end...");		while ((in_8(&mr->bus_status1) & BS1_RST) != 0)			udelay(1);		printk("done\n");		handle_reset(ms);		/* request_q is empty, no point in mesh_start() */		return;	}	if (err & ERR_UNEXPDISC) {		/* Unexpected disconnect */		if (exc & EXC_RESELECTED) {			reselected(ms);			return;		}		if (!ms->aborting) {			printk(KERN_WARNING "mesh: target %d aborted\n",			       ms->conn_tgt);			dumplog(ms, ms->conn_tgt);			dumpslog(ms);		}		out_8(&mr->interrupt, INT_CMDDONE);		ms->stat = DID_ABORT;		mesh_done(ms, 1);		return;	}	if (err & ERR_PARITY) {		if (ms->msgphase == msg_in) {			printk(KERN_ERR "mesh: msg parity error, target %d\n",			       ms->conn_tgt);			ms->msgout[0] = MSG_PARITY_ERROR;			ms->n_msgout = 1;			ms->msgphase = msg_in_bad;			cmd_complete(ms);			return;		}		if (ms->stat == DID_OK) {			printk(KERN_ERR "mesh: parity error, target %d\n",			       ms->conn_tgt);			ms->stat = DID_PARITY;		}		count = (mr->count_hi << 8) + mr->count_lo;		if (count == 0) {			cmd_complete(ms);		} else {			/* reissue the data transfer command */			out_8(&mr->sequence, mr->sequence);		}		return;	}	if (err & ERR_SEQERR) {		if (exc & EXC_RESELECTED) {			/* This can happen if we issue a command to			   get the bus just after the target reselects us. */			static int mesh_resel_seqerr;			mesh_resel_seqerr++;			reselected(ms);			return;		}		if (exc == EXC_PHASEMM) {			static int mesh_phasemm_seqerr;			mesh_phasemm_seqerr++;			phase_mismatch(ms);			return;		}		printk(KERN_ERR "mesh: sequence error (err=%x exc=%x)\n",		       err, exc);	} else {		printk(KERN_ERR "mesh: unknown error %x (exc=%x)\n", err, exc);	}	mesh_dump_regs(ms);	dumplog(ms, ms->conn_tgt);	if (ms->phase > selecting && (in_8(&mr->bus_status1) & BS1_BSY)) {		/* try to do what the target wants */		do_abort(ms);		phase_mismatch(ms);		return;	}	ms->stat = DID_ERROR;	mesh_done(ms, 1);}static void handle_exception(struct mesh_state *ms){	int exc;	volatile struct mesh_regs __iomem *mr = ms->mesh;	exc = in_8(&mr->exception);	out_8(&mr->interrupt, INT_EXCEPTION | INT_CMDDONE);	if (exc & EXC_RESELECTED) {		static int mesh_resel_exc;		mesh_resel_exc++;		reselected(ms);	} else if (exc == EXC_ARBLOST) {		printk(KERN_DEBUG "mesh: lost arbitration\n");		ms->stat = DID_BUS_BUSY;		mesh_done(ms, 1);	} else if (exc == EXC_SELTO) {		/* selection timed out */		ms->stat = DID_BAD_TARGET;		mesh_done(ms, 1);	} else if (exc == EXC_PHASEMM) {		/* target wants to do something different:		   find out what it wants and do it. */		phase_mismatch(ms);	} else {		printk(KERN_ERR "mesh: can't cope with exception %x\n", exc);		mesh_dump_regs(ms);		dumplog(ms, ms->conn_tgt);		do_abort(ms);		phase_mismatch(ms);	}}static void handle_msgin(struct mesh_state *ms){	int i, code;	struct scsi_cmnd *cmd = ms->current_req;	struct mesh_target *tp = &ms->tgts[ms->conn_tgt];	if (ms->n_msgin == 0)		return;	code = ms->msgin[0];	if (ALLOW_DEBUG(ms->conn_tgt)) {		printk(KERN_DEBUG "got %d message bytes:", ms->n_msgin);		for (i = 0; i < ms->n_msgin; ++i)			printk(" %x", ms->msgin[i]);		printk("\n");	}	dlog(ms, "msgin msg=%.8x",	     MKWORD(ms->n_msgin, code, ms->msgin[1], ms->msgin[2]));	ms->expect_reply = 0;	ms->n_msgout = 0;	if (ms->n_msgin < msgin_length(ms))		goto reject;	if (cmd)		cmd->SCp.Message = code;	switch (code) {	case COMMAND_COMPLETE:		break;	case EXTENDED_MESSAGE:		switch (ms->msgin[2]) {		case EXTENDED_MODIFY_DATA_POINTER:			ms->data_ptr += (ms->msgin[3] << 24) + ms->msgin[6]				+ (ms->msgin[4] << 16) + (ms->msgin[5] << 8);			break;		case EXTENDED_SDTR:			if (tp->sdtr_state != sdtr_sent) {				/* reply with an SDTR */				add_sdtr_msg(ms);				/* limit period to at least his value,				   offset to no more than his */				if (ms->msgout[3] < ms->msgin[3])					ms->msgout[3] = ms->msgin[3];				if (ms->msgout[4] > ms->msgin[4])					ms->msgout[4] = ms->msgin[4];				set_sdtr(ms, ms->msgout[3], ms->msgout[4]);				ms->msgphase = msg_out;			} else {				set_sdtr(ms, ms->msgin[3], ms->msgin[4]);			}			break;		default:			goto reject;		}		break;	case SAVE_POINTERS:		tp->saved_ptr = ms->data_ptr;		break;	case RESTORE_POINTERS:		ms->data_ptr = tp->saved_ptr;		break;	case DISCONNECT:		ms->phase = disconnecting;		break;	case ABORT:		break;	case MESSAGE_REJECT:		if (tp->sdtr_state == sdtr_sent)			set_sdtr(ms, 0, 0);		break;	case NOP:		break;	default:		if (IDENTIFY_BASE <= code && code <= IDENTIFY_BASE + 7) {			if (cmd == NULL) {				do_abort(ms);				ms->msgphase = msg_out;			} else if (code != cmd->device->lun + IDENTIFY_BASE) {				printk(KERN_WARNING "mesh: lun mismatch "				       "(%d != %d) on reselection from "				       "target %d\n", code - IDENTIFY_BASE,				       cmd->device->lun, ms->conn_tgt);			}			break;		}		goto reject;	}	return; reject:	printk(KERN_WARNING "mesh: rejecting message from target %d:",	       ms->conn_tgt);	for (i = 0; i < ms->n_msgin; ++i)		printk(" %x", ms->msgin[i]);	printk("\n");	ms->msgout[0] = MESSAGE_REJECT;	ms->n_msgout = 1;	ms->msgphase = msg_out;}/* * Set up DMA commands for transferring data. */static void set_dma_cmds(struct mesh_state *ms, struct scsi_cmnd *cmd){	int i, dma_cmd, total, off, dtot;	struct scatterlist *scl;	struct dbdma_cmd *dcmds;	dma_cmd = ms->tgts[ms->conn_tgt].data_goes_out?		OUTPUT_MORE: INPUT_MORE;	dcmds = ms->dma_cmds;	dtot = 0;	if (cmd) {		int nseg;		cmd->SCp.this_residual = scsi_bufflen(cmd);		nseg = scsi_dma_map(cmd);		BUG_ON(nseg < 0);		if (nseg) {			total = 0;			off = ms->data_ptr;			scsi_for_each_sg(cmd, scl, nseg, i) {				u32 dma_addr = sg_dma_address(scl);				u32 dma_len = sg_dma_len(scl);								total += scl->length;				if (off >= dma_len) {					off -= dma_len;					continue;				}				if (dma_len > 0xffff)					panic("mesh: scatterlist element >= 64k");				st_le16(&dcmds->req_count, dma_len - off);				st_le16(&dcmds->command, dma_cmd);				st_le32(&dcmds->phy_addr, dma_addr + off);				dcmds->xfer_status = 0;				++dcmds;				dtot += dma_len - off;				off = 0;			}		}	}	if (dtot == 0) {		/* Either the target has overrun our buffer,		   or the caller didn't provide a buffer. */		static char mesh_extra_buf[64];		dtot = sizeof(mesh_extra_buf);		st_le16(&dcmds->req_count, dtot);		st_le32(&dcmds->phy_addr, virt_to_phys(mesh_extra_buf));		dcmds->xfer_status = 0;		++dcmds;	}	dma_cmd += OUTPUT_LAST - OUTPUT_MORE;	st_le16(&dcmds[-1].command, dma_cmd);	memset(dcmds, 0, sizeof(*dcmds));	st_le16(&dcmds->command, DBDMA_STOP);	ms->dma_count = dtot;}static void halt_dma(struct mesh_state *ms){	volatile struct dbdma_regs __iomem *md = ms->dma;	volatile struct mesh_regs __iomem *mr = ms->mesh;	struct scsi_cmnd *cmd = ms->current_req;	int t, nb;	if (!ms->tgts[ms->conn_tgt].data_goes_out) {		/* wait a little while until the fifo drains */		t = 50;		while (t > 0 && in_8(&mr->fifo_count) != 0		       && (in_le32(&md->status) & ACTIVE) != 0) {			--t;			udelay(1);		}	}	out_le32(&md->control, RUN << 16);	/* turn off RUN bit */	nb = (mr->count_hi << 8) + mr->count_lo;	dlog(ms, "halt_dma fc/count=%.6x",	     MKWORD(0, mr->fifo_count, 0, nb));	if (ms->tgts[ms->conn_tgt].data_goes_out)		nb += mr->fifo_count;	/* nb is the number of bytes not yet transferred	   to/from the target. */	ms->data_ptr -= nb;	dlog(ms, "data_ptr %x", ms->data_ptr);	if (ms->data_ptr < 0) {		printk(KERN_ERR "mesh: halt_dma: data_ptr=%d (nb=%d, ms=%p)\n",		       ms->data_ptr, nb, ms);		ms->data_ptr = 0;#ifdef MESH_DBG		dumplog(ms, ms->conn_tgt);		dumpslog(ms);#endif /* MESH_DBG */	} else if (cmd && scsi_bufflen(cmd) &&		   ms->data_ptr > scsi_bufflen(cmd)) {		printk(KERN_DEBUG "mesh: target %d overrun, "		       "data_ptr=%x total=%x goes_out=%d\n",		       ms->conn_tgt, ms->data_ptr, scsi_bufflen(cmd),		       ms->tgts[ms->conn_tgt].data_goes_out);	}	scsi_dma_unmap(cmd);	ms->dma_started = 0;}static void phase_mismatch(struct mesh_state *ms){	volatile struct mesh_regs __iomem *mr = ms->mesh;	int phase;	dlog(ms, "phasemm ch/cl/seq/fc=%.8x",	     MKWORD(mr->count_hi, mr->count_lo, mr->sequence, mr->fifo_count));	phase = in_8(&mr->bus_status0) & BS0_PHASE;	if (ms->msgphase == msg_out_xxx && phase == BP_MSGOUT) {		/* output the last byte of the message, without ATN */		out_8(&mr->count_lo, 1);		out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);		mesh_flush_io(mr);		udelay(1);		out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);		ms->msgphase = msg_out_last;		return;	}	if (ms->msgphase == msg_in) {		get_msgin(ms);		if (ms->n_msgin)			handle_msgin(ms);	}	if (ms->dma_started)		halt_dma(ms);	if (mr->fifo_count) {		out_8(&mr->sequence, SEQ_FLUSHFIFO);		mesh_flush_io(mr);		udelay(1);	}	ms->msgphase = msg_none;	switch (phase) {	case BP_DATAIN:		ms->tgts[ms->conn_tgt].data_goes_out = 0;		ms->phase = dataing;		break;	case BP_DATAOUT:		ms->tgts[ms->conn_tgt].data_goes_out = 1;		ms->phase = dataing;		break;	case BP_COMMAND:		ms->phase = commanding;		break;	case BP_STATUS:		ms->phase = statusing;		break;	case BP_MSGIN:		ms->msgphase = msg_in;		ms->n_msgin = 0;		break;	case BP_MSGOUT:		ms->msgphase = msg_out;		if (ms->n_msgout == 0) {			if (ms->aborting) {				do_abort(ms);			} else {				if (ms->last_n_msgout == 0) {					printk(KERN_DEBUG					       "mesh: no msg to repeat\n");					ms->msgout[0] = NOP;					ms->last_n_msgout = 1;				}				ms->n_msgout = ms->last_n_msgout;			}		}		break;	default:		printk(KERN_DEBUG "mesh: unknown scsi phase %x\n", phase);		ms->stat = DID_ERROR;		mesh_done(ms, 1);		return;	}	start_phase(ms);}static void cmd_complete(struct mesh_state *ms){	volatile struct mesh_regs __iomem *mr = ms->mesh;	struct scsi_cmnd *cmd = ms->current_req;	struct mesh_target *tp = &ms->tgts[ms->conn_tgt];	int seq, n, t;	dlog(ms, "cmd_complete fc=%x", mr->fifo_count);	seq = use_active_neg + (ms->n_msgout? SEQ_ATN: 0);	switch (ms->msgphase) {	case msg_out_xxx:		/* huh?  we expected a phase mismatch */		ms->n_msgin = 0;		ms->msgphase = msg_in;		/* fall through */	case msg_in:		/* should have some message bytes in fifo */		get_msgin(ms);		n = msgin_length(ms);		if (ms->n_msgin < n) {			out_8(&mr->count_lo, n - ms->n_msgin);			out_8(&mr->sequence, SEQ_MSGIN + seq);		} else {			ms->msgphase = msg_none;			handle_msgin(ms);			start_phase(ms);		}		break;	case msg_in_bad:		out_8(&mr->sequence, SEQ_FLUSHFIFO);		mesh_flush_io(mr);		udelay(1);		out_8(&mr->count_lo, 1);		out_8(&mr->sequence, SEQ_MSGIN + SEQ_ATN + use_active_neg);		break;	case msg_out:		/*		 * To get the right timing on ATN wrt ACK, we have		 * to get the MESH to drop ACK, wait until REQ gets		 * asserted, then drop ATN.  To do this we first		 * issue a SEQ_MSGOUT with ATN and wait for REQ,		 * then change the command to a SEQ_MSGOUT w/o ATN.		 * If we don't see REQ in a reasonable time, we		 * change the command to SEQ_MSGIN with ATN,		 * wait for the phase mismatch interrupt, then		 * issue the SEQ_MSGOUT without ATN.		 */		out_8(&mr->count_lo, 1);		out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg + SEQ_ATN);		t = 30;		/* wait up to 30us */		while ((in_8(&mr->bus_status0) & BS0_REQ) == 0 && --t >= 0)			udelay(1);		dlog(ms, "last_mbyte err/exc/fc/cl=%.8x",		     MKWORD(mr->error, mr->exception,			    mr->fifo_count, mr->count_lo));		if (in_8(&mr->interrupt) & (INT_ERROR | INT_EXCEPTION)) {			/* whoops, target didn't do what we expected */			ms->last_n_msgout = ms->n_msgout;			ms->n_msgout = 0;			if (in_8(&mr->interrupt) & INT_ERROR) {				printk(KERN_ERR "mesh: error %x in msg_out\n",				       in_8(&mr->error));				handle_error(ms);				return;			}			if (in_8(&mr->exception) != EXC_PHASEMM)				printk(KERN_ERR "mesh: exc %x in msg_out\n",				       in_8(&mr->exception));			else				printk(KERN_DEBUG "mesh: bs0=%x in msg_out\n",				       in_8(&mr->bus_status0));			handle_exception(ms);			return;		}		if (in_8(&mr->bus_status0) & BS0_REQ) {			out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);			mesh_flush_io(mr);			udelay(1);			out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);			ms->msgphase = msg_out_last;		} else {			out_8(&mr->sequence, SEQ_MSGIN + use_active_neg + SEQ_ATN);			ms->msgphase = msg_out_xxx;		}		break;	case msg_out_last:		ms->last_n_msgout = ms->n_msgout;		ms->n_msgout = 0;		ms->msgphase = ms->expect_reply? msg_in: msg_none;		start_phase(ms);		break;	case msg_none:		switch (ms->phase) {		case idle:			printk(KERN_ERR "mesh: interrupt in idle phase?\n");			dumpslog(ms);			return;		case selecting:			dlog(ms, "Selecting phase at command completion",0);			ms->msgout[0] = IDENTIFY(ALLOW_RESEL(ms->conn_tgt),						 (cmd? cmd->device->lun: 0));			ms->n_msgout = 1;			ms->expect_reply = 0;			if (ms->aborting) {				ms->msgout[0] = ABORT;				ms->n_msgout++;			} else if (tp->sdtr_state == do_sdtr) {				/* add SDTR message */				add_sdtr_msg(ms);				ms->expect_reply = 1;				tp->sdtr_state = sdtr_sent;

⌨️ 快捷键说明

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