📄 mesh.c
字号:
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); 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 ((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 (mr->bus_status0 & BS0_REQ) { out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg); 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->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; } ms->msgphase = msg_out; /* * We need to wait for REQ before dropping ATN. * We wait for at most 30us, then fall back to * a scheme where we issue a SEQ_COMMAND with ATN, * which will give us a phase mismatch interrupt * when REQ does come, and then we send the message. */ t = 230; /* wait up to 230us */ while ((mr->bus_status0 & BS0_REQ) == 0) { if (--t < 0) { dlog(ms, "impatient for req", ms->n_msgout); ms->msgphase = msg_none; break; } udelay(1); } break; case dataing: if (ms->dma_count != 0) { start_phase(ms); return; } /* * We can get a phase mismatch here if the target * changes to the status phase, even though we have * had a command complete interrupt. Then, if we * issue the SEQ_STATUS command, we'll get a sequence * error interrupt. Which isn't so bad except that * occasionally the mesh actually executes the * SEQ_STATUS *as well as* giving us the sequence * error and phase mismatch exception. */ out_8(&mr->sequence, 0); out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE); halt_dma(ms); break; case statusing: if (cmd) { cmd->SCp.Status = mr->fifo; if (DEBUG_TARGET(cmd)) printk(KERN_DEBUG "mesh: status is %x\n", cmd->SCp.Status); } ms->msgphase = msg_in; break; case busfreeing: mesh_done(ms, 1); return; case disconnecting: ms->current_req = 0; ms->phase = idle; mesh_start(ms); return; default: break; } ++ms->phase; start_phase(ms); break; }}static void phase_mismatch(struct mesh_state *ms){ volatile struct mesh_regs *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 = 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); 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); 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 voidreselected(struct mesh_state *ms){ volatile struct mesh_regs *mr = ms->mesh; Scsi_Cmnd *cmd; struct mesh_target *tp; int b, t, prev; switch (ms->phase) { case idle: break; case arbitrating: if ((cmd = ms->current_req) != NULL) { /* put the command back on the queue */ cmd->host_scribble = (void *) ms->request_q; if (ms->request_q == NULL) ms->request_qtail = cmd; ms->request_q = cmd; tp = &ms->tgts[cmd->target]; tp->current_req = NULL; } break; case busfreeing: ms->phase = reselecting; mesh_done(ms, 0); break; case disconnecting: break; default: printk(KERN_ERR "mesh: reselected in phase %d/%d tgt %d\n", ms->msgphase, ms->phase, ms->conn_tgt); dumplog(ms, ms->conn_tgt); dumpslog(ms); } ms->current_req = NULL; ms->phase = dataing; ms->msgphase = msg_in; ms->dma_started = 0; ms->n_msgout = 0; ms->last_n_msgout = 0; prev = ms->conn_tgt; /* * We seem to get abortive reselections sometimes. */ while ((mr->bus_status1 & BS1_BSY) == 0) { static int mesh_aborted_resels; mesh_aborted_resels++; out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE); udelay(1); out_8(&mr->sequence, SEQ_ENBRESEL); udelay(5); dlog(ms, "extra resel err/exc/fc = %.6x", MKWORD(0, mr->error, mr->exception, mr->fifo_count)); } out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE); udelay(1); out_8(&mr->sequence, SEQ_ENBRESEL); udelay(1); out_8(&mr->sync_params, ASYNC_PARAMS); /* * Find out who reselected us. */ if (mr->fifo_count == 0) { printk(KERN_ERR "mesh: reselection but nothing in fifo?\n"); ms->conn_tgt = ms->host->this_id; goto bogus; } /* get the last byte in the fifo */ do { b = in_8(&mr->fifo); dlog(ms, "reseldata %x", b); } while (in_8(&mr->fifo_count)); for (t = 0; t < 8; ++t) if ((b & (1 << t)) != 0 && t != ms->host->this_id) break; if (b != (1 << t) + (1 << ms->host->this_id)) { printk(KERN_ERR "mesh: bad reselection data %x\n", b); ms->conn_tgt = ms->host->this_id; goto bogus; } /* * Set up to continue with that target's transfer. */ ms->conn_tgt = t; tp = &ms->tgts[t]; out_8(&mr->sync_params, tp->sync_params); if (ALLOW_DEBUG(t)) { printk(KERN_DEBUG "mesh: reselected by target %d\n", t); printk(KERN_DEBUG "mesh: saved_ptr=%x goes_out=%d cmd=%p\n", tp->saved_ptr, tp->data_goes_out, tp->current_req); } ms->current_req = tp->current_req; if (tp->current_req == NULL) { printk(KERN_ERR "mesh: reselected by tgt %d but no cmd!\n", t); goto bogus; } ms->data_ptr = tp->saved_ptr; dlog(ms, "resel prev tgt=%d", prev); dlog(ms, "resel err/exc=%.4x", MKWORD(0, 0, mr->error, mr->exception)); start_phase(ms); return;bogus: dumplog(ms, ms->conn_tgt); dumpslog(ms); ms->data_ptr = 0; ms->aborting = 1; start_phase(ms);}static void do_abort(struct mesh_state *ms){ ms->msgout[0] = ABORT; ms->n_msgout = 1; ms->aborting = 1; ms->stat = DID_ABORT; dlog(ms, "abort", 0);}static voidhandle_reset(struct mesh_state *ms){ int tgt; struct mesh_target *tp; Scsi_Cmnd *cmd; volatile struct mesh_regs *mr = ms->mesh; for (tgt = 0; tgt < 8; ++tgt) { tp = &ms->tgts[tgt]; if ((cmd = tp->current_req) != NULL) { cmd->result = DID_RESET << 16; tp->current_req = NULL; mesh_completed(ms, cmd); } ms->tgts[tgt].sdtr_state = do_sdtr; ms->tgts[tgt].sync_params = ASYNC_PARAMS; } ms->current_req = NULL; while ((cmd = ms->request_q) != NULL) { ms->request_q = (Scsi_Cmnd *) cmd->host_scribble; cmd->result = DID_RESET << 16; mesh_completed(ms, cmd); } ms->phase = idle; ms->msgphase = msg_none; out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE); out_8(&mr->sequence, SEQ_FLUSHFIFO); udelay(1); out_8(&mr->sync_params, ASYNC_PARAMS); out_8(&mr->sequence, SEQ_ENBRESEL);}static voiddo_mesh_interrupt(int irq, void *dev_id, struct pt_regs *ptregs){ unsigned long flags; spin_lock_irqsave(&io_request_lock, flags); mesh_interrupt(irq, dev_id, ptregs); spin_unlock_irqrestore(&io_request_lock, flags);}static void handle_error(struct mesh_state *ms){ int err, exc, count; volatile struct mesh_regs *mr = ms->mesh; err = in_8(&mr->error); exc = in_8(&mr->exception); out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE); 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 ((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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -