📄 ncr.c
字号:
* * ncr_dopoll will return when ncr->n_state == * STATE_FREE, but this can also mean a reselection * preempt occurred. */ PRINTF3("ncr_start: poll_res\n"); if (ncr_dopoll(ncr)) { (void) splx(s); return (FALSE); } } PRINTF3("ncr_start: start next slot= %x\n", NEXTSLOT(slot)); (void) ncr_ustart(ncr, NEXTSLOT(slot)); } (void) splx(s); PRINTF3("ncr_start: done\n"); return (TRUE);}/*ARGSUSED*/static intncr_abort(ap, pkt)struct scsi_address *ap;struct scsi_pkt *pkt;{ PRINTF3("ncr_abort: pkt= %x\n", pkt); if (pkt != (struct scsi_pkt *) 0) { PRINTF3("ncr_abort: call ncr_do_abort(RESET)\n"); ncr_do_abort((struct ncr *)ap->a_cookie, NCR_RESET_ALL, RESET_NOMSG); return (TRUE); } else { return (FALSE); }}static intncr_reset(ap, level) /* clear jobs & reset H/W for external-req */struct scsi_address *ap;int level;{ struct ncr *ncr = (struct ncr *) ap->a_cookie; PRINTF3("ncr_reset: level= %x\n", level); /* * if RESET_ALL requested, call "ncr_do_abort()" to clear all * outstanding jobs in the queue */ if (level == RESET_ALL) { ncr_do_abort(ncr, NCR_RESET_ALL, RESET_NOMSG); return (TRUE); } else return (FALSE);}/*ARGSUSED*/static intncr_getcap(ap, cap) /* return host-adapter's capabilities */struct scsi_address *ap;char *cap;{ PRINTF3("ncr_getcap:\n"); /* * Check requested function (such as "dma_max, msg_out, sync, * disconnect, parity, and Ids) support on the behave of * host_adapter H/W platfrom and S/W driver */ if (cap == (char *) 0) return (UNDEFINED); else if (strncmp("dma_max", cap, 7) == 0) return (1<<16); else if (strncmp("msg_out", cap, 7) == 0) return (TRUE); /* 1 */ else if (strncmp("disconnect", cap, 10) == 0) return (TRUE); /* 1 */ else if (strncmp("synchronous", cap, 11) == 0) return (FALSE); /* 0 */ else if (strncmp("parity", cap, 6) == 0) return (FALSE); else if (strncmp("initiator-id", cap, 12) == 0) { struct ncr *ncr = (struct ncr *) ap->a_cookie; return (ncr->n_id); } else return (UNDEFINED);}/*ARGSUSED*/static intncr_setcap(ap, cap, value) /* set host-adapter actions per request */struct scsi_address *ap;char *cap;int value;{ PRINTF3("ncr_setcap: val= %x\n", value); /* set host_adapter driver's action based on inputted request */ return (UNDEFINED);}/* * Internal start and finish routines *//* * Start the next command on the host adapter. * Search from start_slot for work to do. * * * input: (struct ncr) *ncr= pointer to a ncr software structure; * (short) start_slot= requested slot (target/lun combo); * return: (int) TRUE(1)= command started okay; * (int) FALSE(0)= not started (due to reselection or no work) */static intncr_ustart(ncr, start_slot)register struct ncr *ncr;short start_slot;{ register struct scsi_cmd *sp; register short slot = start_slot; int found = 0; int i; char dkn; PRINTF3("ncr_ustart: start_slot= %x\n", start_slot); /* * Start off a new job in the queue ONLY number of running cmds * is less than disconnected cmds, in hope of NOT floating the bus * and allow the previously disconnected jobs to be finished first, * if more currently disconnected jobs, return ACTION_ABORT * * XXX: mjacob sez that this doesn't make him happy */ if ((ncr->n_ncmds - ncr->n_ndisc) <= 0) { PRINTF3("ncr_ustart: NO new-job\n"); return (FALSE); } /* * search for any ready cmd available (started first with the req * slot, then move to next slot until reaching req_one) */ do { sp = ncr->n_slots[slot]; if (sp && ((sp->cmd_flags & CFLAG_CMDDISC) == 0)) { found++; } else { slot = NEXTSLOT(slot); } } while ((found == 0) && (slot != start_slot)); if (!found) { return (FALSE); } UPDATE_STATE(STATE_STARTING); PRINTF3("ncr_ustart: starting %d.%d\n", Tgt(sp), Lun(sp)); ncr->n_cur_slot = slot; ncr->n_omsgidx = ncr->n_omsglen = 0; /* * Attempt to arbitrate for the bus and select the target. * * ncr_select() can return one of SEL_TRUE (target selected), * SEL_ARBFAIL (unable to get the bus), SEL_FALSE (target did * not respond to selection), or SEL_RESEL (a reselection attempt * is in progress). As a side effect, if SEL_TRUE is the return, * DMA (and interrupts) from the SBC are disabled. * */ switch (ncr_select(ncr)) { case SEL_ARBFAIL: /* * XXX: Should we treat arbitration failures * XXX: differently than selection failures? */ case SEL_FALSE: (void) ncr_finish(ncr); return (FALSE); case SEL_TRUE: if ((sp->cmd_flags & CFLAG_DMAVALID) && (dkn = sp->cmd_pkt.pkt_pmon) >= 0) { dk_busy |= (1<<dkn); dk_xfer[dkn]++; if ((sp->cmd_flags & CFLAG_DMASEND) == 0) dk_read[dkn]++; dk_wds[dkn] += sp->cmd_dmacount >> 6; } UPDATE_STATE(ACTS_UNKNOWN); if (NON_INTR(sp)) ncr_phasemanage(ncr); return (TRUE); case SEL_RESEL: /* * Couldn't select due to a reselection coming in. * Push the state of this command back to what it was. */ ncr_preempt(ncr); return (FALSE); }}/* * Finish routine */static voidncr_finish(ncr)register struct ncr *ncr;{ short last_slot; register int span_states = 0; register struct scsi_cmd *sp = CURRENT_CMD(ncr); char dkn; PRINTF3("ncr_finish:\n"); if ((dkn = sp->cmd_pkt.pkt_pmon) >= 0) { dk_busy &= ~(1<<dkn); } if (ncr->n_last_msgin == MSG_LINK_CMPLT || ncr->n_last_msgin == MSG_LINK_CMPLT_FLAG) { span_states++; } if (sp->cmd_pkt.pkt_state & STATE_XFERRED_DATA) { register struct dataseg *segtmp = sp->cmd_subseg.d_next; register i; /* * XXX : FIX ME NEED TO MERGE TOGETHER OVERLAPPING AND * XXX : ADJACENT SEGMENTS!!! */ if (segtmp != (struct dataseg *) 0 && segtmp->d_count) { panic("ncr_finish: more than one segment with data"); /* NOTREACHED */ } /* * Walk through all data segments and count up transfer counts */ i = 0; for (segtmp = &sp->cmd_subseg; segtmp; segtmp = segtmp->d_next) { i += segtmp->d_count; } sp->cmd_pkt.pkt_resid = sp->cmd_dmacount - i; if (INFORMATIVE && sp->cmd_pkt.pkt_resid) { PRINTF2("ncr_finish: %d.%d finishes with %d resid\n", Tgt(sp), Lun(sp), sp->cmd_pkt.pkt_resid); } } ncr->n_ncmds -= 1; last_slot = ncr->n_last_slot = ncr->n_cur_slot; ncr->n_lastcount = 0; ncr->n_cur_slot = UNDEFINED; ncr->n_slots[last_slot] = (struct scsi_cmd *) 0; ncr->n_omsglen = ncr->n_omsgidx = 0; ncr->n_last_msgin = 0xfe; UPDATE_STATE(STATE_FREE); PRINTF3("ncr_finish: span= %x, flag= %x\n", span_states, sp->cmd_pkt.pkt_flags); if (NON_INTR(sp)) { PRINTF3("ncr_finish: poll= calling upper target to finish\n"); ncr->n_npolling -= 1; (*sp->cmd_pkt.pkt_comp)(sp); } else if (span_states > 0) { ncr->n_state = ACTS_SPANNING; (*sp->cmd_pkt.pkt_comp)(sp); /* * This is we can check upon return that * the target driver did the right thing... * * If the target driver didn't do the right * thing, we have to abort the operation. */ if (ncr->n_slots[last_slot] == 0) { ncr_internal_abort(ncr); } else { PRINTF2("%s%d: linked command start\n", CNAME, CNUM); ncr->n_cur_slot = last_slot; ncr->n_nlinked++; if (ncr_ACKmsg(ncr)) { ncr_do_abort(ncr, NCR_RESET_ALL, RESET_NOMSG); } else { sp = CURRENT_CMD(ncr); if ((sp->cmd_flags & CFLAG_DMAVALID) && (dkn = sp->cmd_pkt.pkt_pmon) >= 0) { dk_busy |= (1<<dkn); dk_xfer[dkn]++; if (!(sp->cmd_flags & CFLAG_DMASEND)) dk_read[dkn]++; dk_wds[dkn] += sp->cmd_dmacount >> 6; } UPDATE_STATE(ACTS_UNKNOWN); ncr_phasemanage(ncr); } } } else { (*sp->cmd_pkt.pkt_comp)(sp); if (ncr->n_state == STATE_FREE) { (void) ncr_ustart(ncr, last_slot); } }}/* * Interrupt Service Routines *//* * polled service routine - called when interrupts are not feasible * * Note that we call ncrpoll() rather than ncrsvc directly. This is * so we can service other ncr controllers while we're polling this * one. */static intncr_dopoll(ncr)register struct ncr *ncr;{ register int i; PRINTF3("ncr_dopoll: state= %x\n", ncr->n_state); while (ncr->n_state != STATE_FREE) { PRINTF3("ncr_dopoll: n_state= %x\n", ncr->n_state); for (i = 0; (i < 3*120000) && (ncr->n_state != STATE_FREE); i++) { if (ncrpoll()) { /* 1 is Okay */ if (ncr->n_state == STATE_FREE) continue; else i = 0; } else { DELAY(100); } if (i >= 3*120000 && ncr->n_state != STATE_FREE) { EPRINTF("ncr_dopoll: poll_cmd timeout, state= %x\n", ncr->n_state); return (FAILURE); } } } return (SUCCESS);}/* * polled (autovector) interrupt entry point */static intncrpoll(){ register struct ncr *ncr; int serviced = 0; for (ncr = ncr_softc; ncr < &ncr_softc[NNCR]; ncr++) { if (ncrctlr[ncr-ncr_softc]) { ncr->n_ints++; while (INTPENDING(ncr)) { ncrsvc(ncr); serviced = 1; } } } return (serviced);}/* * vectored interrupt entry point(s) */voidncrintr(ncr)struct ncr *ncr;{ if (ncrctlr[CNUM]) { ncr->n_ints++; while (INTPENDING(ncr)) { ncrsvc(ncr); } } else { printf("%s%d: spurious vectored interrupt\n", CNAME, CNUM); }}/* * Common interrupt service code- called asynchronously from ncrpoll() or * ncrintr(), or synchronously from varying places in the rest of the * driver to service interrupts. * * What kind of interrupts we'll get: * * * RESELECTION interrupts * * End of DATA PHASE interrupts (PHASE MISMATCH) * * Some specific PHASE MISMATCH interrupts (driven by * enabling DMA mode in the sbc mr register after setting * a bogus phase into the tcr register- when REQ* is asserted * this causes a phase mismatch interrupt. * * XXX: * Monitor LOSS OF BUSY interrupts * XXX: * PARITY Errors */static voidncrsvc(ncr)register struct ncr *ncr;{ register u_char binary_id = NUM_TO_BIT(ncr->n_id); register u_char uctmp; /* * 'Disabling' DMA also allows access to SBC registers */ if (ncr->n_type != IS_3_50) DISABLE_DMA(ncr); uctmp = N_SBC->cbsr; if (ncr->n_type != IS_COBRA && ncr->n_type != IS_3E) { ncr->n_lastbcr = GET_BCR(ncr); } /* * First check for a reselect interrupt coming in */ if (RESELECTING(uctmp, N_SBC->cdr, binary_id)) { if (ncr_reselect(ncr)) { uctmp = 0; ncr_do_abort(ncr, NCR_RESET_ALL, RESET_NOMSG); } else { uctmp = 1; } } else if (IN_DATA_STATE(ncr)) { /* * XXX: should be able to return and await another REQ* * XXX: here? Probably wouldn't help because interrupt * XXX: latency + dma cleanup generally will give the * XXX: targets time to shift to status and/or msg in * XXX: phase. */ if ((*ncr->n_dma_cleanup)(ncr)) { ncr_do_abort(ncr, NCR_RESET_ALL, RESET_NOMSG); uctmp = 0; } else { uctmp = 1; } } else if (ncr->n_state == STATE_FREE) { if (ncr_debug) { printf("%s%d: spurious interrupt\n", CNAME, CNUM); ncr_printstate(ncr); } uctmp = N_SBC->clr; ncr->n_spurint++; uctmp = 0; } else { switch (ncr->n_laststate) { case STATE_SELECTED: ncr->n_pmints[PM_SEL]++; break; case ACTS_MSG_IN: ncr->n_pmints[PM_MSGIN]++; break; case ACTS_MSG_OUT: ncr->n_pmints[PM_MSGOUT]++; break; case ACTS_STATUS: ncr->n_pmints[PM_STATUS]++; break; case ACTS_COMMAND: ncr->n_pmints[PM_CMD]++; break; } /* * dismiss cause of interrupt */ N_SBC->mr &= ~NCR_MR_DMA; uctmp = N_SBC->clr; uctmp = 1; } if (uctmp) { if (ncr->n_state != ACTS_UNKNOWN) { UPDATE_STATE(ACTS_UNKNOWN); } ncr_phasemanage(ncr); } /* * Enabling dma also enables SBC interrupts */ if (ncr->n_type != IS_3_50) ENABLE_DMA(ncr);}/* * Complete reselection */static intncr_reselect(ncr)register struct ncr *ncr;{ struct scsi_cmd *sp; register s, target, lun; register u_char cdr; short slot; u_char msgin, binary_id = NUM_TO_BIT(ncr->n_id); if (ncr->n_ndisc == 0) { printf("%s%d: reselection with no disconnected jobs\n", CNAME, CNUM); return (FAILURE); } else if (ncr->n_state != STATE_FREE) { printf("%s%d: reselection while not in free state\n", CNAME, CNUM); return (FAILURE); } /* * CRITICAL CODE SECTION DON'T TOUCH */ s = splhigh(); cdr = N_SBC->clr; /* clear int */ /* * get reselecting target scsi id */ cdr = N_SBC->cdr & ~binary_id; /* * make sure there are only 2 scsi id's set */ for (target = 0; target < 8; target++) { if (cdr & (1 << target)) break; } N_SBC->ser = 0; /* clear (re)sel int */ cdr &= ~(1 << target); if (cdr != 0) { (void) splx(s); printf("%s%d: reselection w > 2 SCSI ids on the bus\n", CNAME, CNUM); return (FAILURE); } /* * Respond to reselection by asserting BSY* */ N_SBC->icr |= NCR_ICR_BUSY; (void) splx(s); /* * If reselection ok, target should drop select */ if (ncr_sbcwait(&CBSR, NCR_CBSR_SEL, NCR_WAIT_COUNT, 0)) { printf("%s%d: target didn't drop select on reselection\n", CNAME, CNUM);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -