void si_intr(void *arg) { struct si_softc *sc; struct si_port *pp; volatile struct si_channel *ccbp; struct tty *tp; volatile caddr_t maddr; BYTE op, ip; int x, card, port, n, i, isopen; volatile BYTE *z; BYTE c; sc = arg; mtx_assert(&Giant, MA_OWNED); DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "si_intr\n")); /* * When we get an int we poll all the channels and do ALL pending * work, not just the first one we find. This allows all cards to * share the same vector. * * XXX - But if we're sharing the vector with something that's NOT * a SI/XIO/SX card, we may be making more work for ourselves. */ for (card = 0; card < si_numunits; card++) { sc = devclass_get_softc(si_devclass, card); if (sc == NULL || sc->sc_type == SIEMPTY) continue; /* * First, clear the interrupt */ switch(sc->sc_type) { case SIHOST: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; /* flag nothing pending */ *(maddr+SIINTCL) = 0x00; /* Set IRQ clear */ *(maddr+SIINTCL_CL) = 0x00; /* Clear IRQ clear */ break; case SIHOST2: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIPLIRQCLR) = 0x00; *(maddr+SIPLIRQCLR) = 0x10; break; case SIPCI: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIPCIINTCL) = 0x0; break; case SIJETPCI: /* fall through to JETISA case */ case SIJETISA: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIJETINTCL) = 0x0; break; #ifdef DEV_EISA case SIEISA: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; (void)inb(sc->sc_iobase + 3); break; #endif case SIEMPTY: default: continue; } ((volatile struct si_reg *)maddr)->int_scounter = 0; /* * check each port */ for (pp = sc->sc_ports, port = 0; port < sc->sc_nport; pp++, port++) { ccbp = pp->sp_ccb; tp = pp->sp_tty; tty_lock(tp); /* * See if a command has completed ? */ if (ccbp->hi_stat != pp->sp_pend) { DPRINT((pp, DBG_INTR, "si_intr hi_stat = %s, pend = %s\n", si_cmdname(ccbp->hi_stat), si_cmdname(pp->sp_pend))); switch(pp->sp_pend) { case LOPEN: case MPEND: case MOPEN: case FCLOSE: case CONFIG: case SBREAK: case EBREAK: /* sleeping in si_command */ DPRINT((pp, DBG_INTR, "do wakeup\n")); wakeup(&pp->sp_state); break; } pp->sp_pend = ccbp->hi_stat; } /* * Continue on if it's closed */ if (ccbp->hi_stat == IDLE_CLOSE) { tty_unlock(tp); continue; } /* * Do modem state change if not a local device */ si_modem_state(pp, tp, ccbp->hi_ip); /* * Check to see if we should 'receive' characters. */ isopen = tty_opened(tp); /* * Do input break processing */ if (ccbp->hi_state & ST_BREAK) { if (isopen) ttydisc_rint(tp, 0, TRE_BREAK); ccbp->hi_state &= ~ST_BREAK; /* A Bit iffy this */ DPRINT((pp, DBG_INTR, "si_intr break\n")); } /* * Do RX stuff - if not open then dump any characters. * XXX: This is VERY messy and needs to be cleaned up. * * XXX: can we leave data in the host adapter buffer * when the clists are full? That may be dangerous * if the user cannot get an interrupt signal through. */ more_rx: if (!isopen) { DPRINT((pp, DBG_INTR, "intr1: not open\n")); ccbp->hi_rxopos = ccbp->hi_rxipos; goto end_rx; } #if 0 /* XXXMPSAFETTY */ /* * If the tty input buffers are blocked, stop emptying * the incoming buffers and let the auto flow control * assert.. */ if (tp->t_state & TS_TBLOCK) goto end_rx; #endif /* * Process read characters if not skipped above */ op = ccbp->hi_rxopos; ip = ccbp->hi_rxipos; c = ip - op; if (c == 0) goto end_rx; n = c & 0xff; if (n > 250) n = 250; DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", n, op, ip)); /* * Suck characters out of host card buffer into the * "input staging buffer" - so that we dont leave the * host card in limbo while we're possibly echoing * characters and possibly flushing input inside the * ldisc l_rint() routine. */ if (n <= SI_BUFFERSIZE - op) { z = ccbp->hi_rxbuf + op; si_vbcopy(z, si_rxbuf, n); op += n; } else { x = SI_BUFFERSIZE - op; z = ccbp->hi_rxbuf + op; si_vbcopy(z, si_rxbuf, x); z = ccbp->hi_rxbuf; si_vbcopy(z, si_rxbuf + x, n - x); op += n; } /* clear collected characters from buffer */ ccbp->hi_rxopos = op; /* * at this point... * n = number of chars placed in si_rxbuf */ if (0 && ttydisc_can_bypass(tp)) { i = ttydisc_rint_bypass(tp, (char *)si_rxbuf, n); if (i < n) pp->sp_delta_overflows += (n - i); } else { /* * It'd be nice to not have to go through the * function call overhead for each char here. * It'd be nice to block input it, saving a * loop here and the call/return overhead. */ for(x = 0; x < n; x++) { i = si_rxbuf[x]; if (ttydisc_rint(tp, i, 0) == -1) pp->sp_delta_overflows++; } } goto more_rx; /* try for more until RXbuf is empty */ end_rx: ttydisc_rint_done(tp); /* * Do TX stuff */ si_start(tp); tty_unlock(tp); } /* end of for (all ports on this controller) */ } /* end of for (all controllers) */ DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "end si_intr\n")); }
/* * Handle the Specialix ioctls on the control dev. */ static int si_Sioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { struct si_softc *xsc; struct si_port *xpp; volatile struct si_reg *regp; struct si_tcsi *dp; struct si_pstat *sps; int *ip, error = 0; int oldspl; int card, port; DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%s,0x%lx,0x%x)\n", devtoname(dev), cmd, data)); mtx_assert(&Giant, MA_OWNED); #if 1 DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT)); DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB)); #endif oldspl = spltty(); /* better safe than sorry */ ip = (int *)data; #define SUCHECK if ((error = priv_check(td, PRIV_DRIVER))) goto out switch (cmd) { case TCSIPORTS: *ip = si_Nports; goto out; case TCSIMODULES: *ip = si_Nmodules; goto out; case TCSISDBG_ALL: SUCHECK; si_debug = *ip; goto out; case TCSIGDBG_ALL: *ip = si_debug; goto out; default: /* * Check that a controller for this port exists */ /* may also be a struct si_pstat, a superset of si_tcsi */ dp = (struct si_tcsi *)data; sps = (struct si_pstat *)data; card = dp->tc_card; xsc = devclass_get_softc(si_devclass, card); /* check.. */ if (xsc == NULL || xsc->sc_type == SIEMPTY) { error = ENOENT; goto out; } /* * And check that a port exists */ port = dp->tc_port; if (port < 0 || port >= xsc->sc_nport) { error = ENOENT; goto out; } xpp = xsc->sc_ports + port; regp = (struct si_reg *)xsc->sc_maddr; } switch (cmd) { case TCSIDEBUG: #ifdef SI_DEBUG SUCHECK; if (xpp->sp_debug) xpp->sp_debug = 0; else { xpp->sp_debug = DBG_ALL; DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n", (xpp->sp_debug&DBG_ALL)?"ON":"OFF")); } break; #else error = ENODEV; goto out; #endif case TCSISDBG_LEVEL: case TCSIGDBG_LEVEL: #ifdef SI_DEBUG if (cmd == TCSIGDBG_LEVEL) { dp->tc_dbglvl = xpp->sp_debug; } else { SUCHECK; xpp->sp_debug = dp->tc_dbglvl; } break; #else error = ENODEV; goto out; #endif case TCSIGRXIT: dp->tc_int = regp->rx_int_count; break; case TCSIRXIT: SUCHECK; regp->rx_int_count = dp->tc_int; break; case TCSIGIT: dp->tc_int = regp->int_count; break; case TCSIIT: SUCHECK; regp->int_count = dp->tc_int; break; case TCSISTATE: dp->tc_int = xpp->sp_ccb->hi_ip; break; /* these next three use a different structure */ case TCSI_PORT: SUCHECK; si_bcopy(xpp, &sps->tc_siport, sizeof(sps->tc_siport)); break; case TCSI_CCB: SUCHECK; si_vbcopy(xpp->sp_ccb, &sps->tc_ccb, sizeof(sps->tc_ccb)); break; default: error = EINVAL; goto out; } out: splx(oldspl); return(error); /* success */ }
void si_intr(void *arg) { struct si_softc *sc; struct si_port *pp; volatile struct si_channel *ccbp; struct tty *tp; volatile caddr_t maddr; BYTE op, ip; int x, card, port, n, i, isopen; volatile BYTE *z; BYTE c; sc = arg; DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "si_intr\n")); if (in_intr) return; in_intr = 1; /* * When we get an int we poll all the channels and do ALL pending * work, not just the first one we find. This allows all cards to * share the same vector. * * XXX - But if we're sharing the vector with something that's NOT * a SI/XIO/SX card, we may be making more work for ourselves. */ for (card = 0; card < si_numunits; card++) { sc = devclass_get_softc(si_devclass, card); if (sc == NULL || sc->sc_type == SIEMPTY) continue; /* * First, clear the interrupt */ switch(sc->sc_type) { case SIHOST: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; /* flag nothing pending */ *(maddr+SIINTCL) = 0x00; /* Set IRQ clear */ *(maddr+SIINTCL_CL) = 0x00; /* Clear IRQ clear */ break; case SIHOST2: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIPLIRQCLR) = 0x00; *(maddr+SIPLIRQCLR) = 0x10; break; case SIPCI: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIPCIINTCL) = 0x0; break; case SIJETPCI: /* fall through to JETISA case */ case SIJETISA: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIJETINTCL) = 0x0; break; #ifdef DEV_EISA case SIEISA: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; (void)inb(sc->sc_iobase + 3); break; #endif case SIEMPTY: default: continue; } ((volatile struct si_reg *)maddr)->int_scounter = 0; /* * check each port */ for (pp = sc->sc_ports, port = 0; port < sc->sc_nport; pp++, port++) { ccbp = pp->sp_ccb; tp = pp->sp_tty; /* * See if a command has completed ? */ if (ccbp->hi_stat != pp->sp_pend) { DPRINT((pp, DBG_INTR, "si_intr hi_stat = 0x%x, pend = %d\n", ccbp->hi_stat, pp->sp_pend)); switch(pp->sp_pend) { case LOPEN: case MPEND: case MOPEN: case CONFIG: case SBREAK: case EBREAK: pp->sp_pend = ccbp->hi_stat; /* sleeping in si_command */ wakeup(&pp->sp_state); break; default: pp->sp_pend = ccbp->hi_stat; } } /* * Continue on if it's closed */ if (ccbp->hi_stat == IDLE_CLOSE) { continue; } /* * Do modem state change if not a local device */ si_modem_state(pp, tp, ccbp->hi_ip); /* * Check to see if we should 'receive' characters. */ if (tp->t_state & TS_CONNECTED && tp->t_state & TS_ISOPEN) isopen = 1; else isopen = 0; /* * Do input break processing */ if (ccbp->hi_state & ST_BREAK) { if (isopen) { ttyld_rint(tp, TTY_BI); } ccbp->hi_state &= ~ST_BREAK; /* A Bit iffy this */ DPRINT((pp, DBG_INTR, "si_intr break\n")); } /* * Do RX stuff - if not open then dump any characters. * XXX: This is VERY messy and needs to be cleaned up. * * XXX: can we leave data in the host adapter buffer * when the clists are full? That may be dangerous * if the user cannot get an interrupt signal through. */ more_rx: /* XXX Sorry. the nesting was driving me bats! :-( */ if (!isopen) { ccbp->hi_rxopos = ccbp->hi_rxipos; goto end_rx; } /* * If the tty input buffers are blocked, stop emptying * the incoming buffers and let the auto flow control * assert.. */ if (tp->t_state & TS_TBLOCK) { goto end_rx; } /* * Process read characters if not skipped above */ op = ccbp->hi_rxopos; ip = ccbp->hi_rxipos; c = ip - op; if (c == 0) { goto end_rx; } n = c & 0xff; if (n > 250) n = 250; DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", n, op, ip)); /* * Suck characters out of host card buffer into the * "input staging buffer" - so that we dont leave the * host card in limbo while we're possibly echoing * characters and possibly flushing input inside the * ldisc l_rint() routine. */ if (n <= SI_BUFFERSIZE - op) { DPRINT((pp, DBG_INTR, "\tsingle copy\n")); z = ccbp->hi_rxbuf + op; si_vbcopy(z, si_rxbuf, n); op += n; } else { x = SI_BUFFERSIZE - op; DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x)); z = ccbp->hi_rxbuf + op; si_vbcopy(z, si_rxbuf, x); DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", n - x)); z = ccbp->hi_rxbuf; si_vbcopy(z, si_rxbuf + x, n - x); op += n; } /* clear collected characters from buffer */ ccbp->hi_rxopos = op; DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", n, op, ip)); /* * at this point... * n = number of chars placed in si_rxbuf */ /* * Avoid the grotesquely inefficient lineswitch * routine (ttyinput) in "raw" mode. It usually * takes about 450 instructions (that's without * canonical processing or echo!). slinput is * reasonably fast (usually 40 instructions * plus call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { /* block if the driver supports it */ if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER && (tp->t_cflag & CRTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += n; tk_rawcc += n; tp->t_rawcc += n; pp->sp_delta_overflows += b_to_q((char *)si_rxbuf, n, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; si_start(tp); } } else { /* * It'd be nice to not have to go through the * function call overhead for each char here. * It'd be nice to block input it, saving a * loop here and the call/return overhead. */ for(x = 0; x < n; x++) { i = si_rxbuf[x]; if (ttyld_rint(tp, i) == -1) { pp->sp_delta_overflows++; } } } goto more_rx; /* try for more until RXbuf is empty */ end_rx: /* XXX: Again, sorry about the gotos.. :-) */ /* * Do TX stuff */ ttyld_start(tp); } /* end of for (all ports on this controller) */ } /* end of for (all controllers) */ in_intr = 0; DPRINT((0, arg == NULL ? DBG_POLL:DBG_INTR, "end si_intr\n")); }