void zskbd_softint(struct zs_chanstate *cs) { struct zskbd_softc *sc = cs->cs_private; struct zskbd_devconfig *dc = sc->sc_dc; int rr0; /* handle pending transmissions */ if (dc->txq_head != dc->txq_tail) { int s; s = splzs(); while (dc->txq_head != dc->txq_tail) { rr0 = zs_read_csr(cs); if ((rr0 & ZSRR0_TX_READY) == 0) break; zs_write_data(cs, dc->txq[dc->txq_head]); dc->txq_head = (dc->txq_head + 1) & ~ZSKBD_TXQ_LEN; } splx(s); } /* handle incoming keystrokes/config */ while (dc->rxq_head != dc->rxq_tail) { zskbd_process(cs, dc->rxq[dc->rxq_head]); dc->rxq_head = (dc->rxq_head + 1) & ~ZSKBD_RXQ_LEN; } }
/* * drain on-chip fifo */ void zs_iflush(struct zs_chanstate *cs) { uint8_t c, rr0, rr1; int i; /* * Count how many times we loop. Some systems, such as some * Apple PowerBooks, claim to have SCC's which they really don't. */ for (i = 0; i < 32; i++) { /* Is there input available? */ rr0 = zs_read_csr(cs); if ((rr0 & ZSRR0_RX_READY) == 0) break; /* * First read the status, because reading the data * destroys the status of this char. */ rr1 = zs_read_reg(cs, 1); c = zs_read_data(cs); if (rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) { /* Clear the receive error. */ zs_write_csr(cs, ZSWR0_RESET_ERRORS); } } }
/* * Polled output char. */ static void zs_putc(int c) { int s, rr0; s = splzs(); /* Wait for transmitter to become ready. */ do { rr0 = zs_read_csr(&zscn_cs); } while ((rr0 & ZSRR0_TX_READY) == 0); zs_write_data(&zscn_cs, c); splx(s); }
void zsclock_stint(struct zs_chanstate *cs, int force) { u_char rr0; rr0 = zs_read_csr(cs); cs->cs_rr0 = rr0; /* * Retrigger the interrupt as a soft interrupt, because we need * a trap frame for hardclock(). */ ienab_bis(IE_L10); zs_write_csr(cs, ZSWR0_RESET_STATUS); }
/* * Start or restart transmission. */ void zsstart(struct tty *tp) { struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev)); struct zs_chanstate *cs = zst->zst_cs; u_char *tba; int tbc, rr0; int s; s = spltty(); if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) goto out; if (zst->zst_tx_stopped) goto out; ttwakeupwr(tp); if (tp->t_outq.c_cc == 0) goto out; /* Grab the first contiguous region of buffer space. */ tba = tp->t_outq.c_cf; tbc = ndqb(&tp->t_outq, 0); #if IPL_ZS != IPL_TTY (void)splzs(); #endif zst->zst_tba = tba; zst->zst_tbc = tbc; SET(tp->t_state, TS_BUSY); zst->zst_tx_busy = 1; do { rr0 = zs_read_csr(cs); if ((rr0 & ZSRR0_TX_READY) == 0) break; zs_write_data(cs, *zst->zst_tba); zst->zst_tbc--; zst->zst_tba++; } while (zst->zst_tbc > 0); out: splx(s); }
/* * Status Change interrupt. * Called at splzs(). */ void zstty_stint(struct zs_chanstate *cs, int force) { struct zstty_softc *zst = cs->cs_private; struct tty *tp = zst->zst_tty; uint8_t rr0, delta; rr0 = zs_read_csr(cs); zs_write_csr(cs, ZSWR0_RESET_STATUS); /* * Check here for console break, so that we can abort * even when interrupts are locking up the machine. */ if ((zst->zst_hwflags & ZS_HWFLAG_CONSOLE_INPUT) && ISSET(rr0, ZSRR0_BREAK) && DB_CONSOLE) zs_abort(cs); if (!force) delta = rr0 ^ cs->cs_rr0; else delta = cs->cs_rr0_mask; ttytstamp(tp, cs->cs_rr0 & ZSRR0_CTS, rr0 & ZSRR0_CTS, cs->cs_rr0 & ZSRR0_DCD, rr0 & ZSRR0_DCD); cs->cs_rr0 = rr0; if (ISSET(delta, cs->cs_rr0_mask)) { SET(cs->cs_rr0_delta, delta); /* * Stop output immediately if we lose the output * flow control signal or carrier detect. */ if (ISSET(~rr0, cs->cs_rr0_mask)) { zst->zst_tbc = 0; zst->zst_heldtbc = 0; } zst->zst_st_check = 1; cs->cs_softreq = 1; } }
/* expects to be in splzs() */ int zskbd_send(struct zs_chanstate *cs, uint8_t *c, u_int len) { struct zskbd_softc *sc = cs->cs_private; struct zskbd_devconfig *dc = sc->sc_dc; u_int i; int rr0; while (len != 0) { rr0 = zs_read_csr(cs); if ((rr0 & ZSRR0_TX_READY) == 0) { /* * poll until whole transmission complete during * autoconf */ if (cold) { for (i = 1000; i != 0; i--) { if ((rr0 & ZSRR0_TX_READY) != 0) break; delay(100); } if (i == 0) return EIO; } else break; } zs_write_data(cs, *c++); len--; } /* * Enqueue any remaining bytes. */ while (len != 0) { dc->txq[dc->txq_tail] = *c++; dc->txq_tail = (dc->txq_tail + 1) & ~ZSKBD_TXQ_LEN; len--; cs->cs_softreq = 1; } return 0; }
/* * Polled input char. */ static int zs_getc(void) { int s, c, rr0; s = splzs(); /* Wait for a character to arrive. */ do { rr0 = zs_read_csr(&zscn_cs); } while ((rr0 & ZSRR0_RX_READY) == 0); c = zs_read_data(&zscn_cs); splx(s); /* * This is used by the kd driver to read scan codes, * so don't translate '\r' ==> '\n' here... */ return (c); }
/* * MD functions for setting the baud rate and control modes. */ int zs_set_speed(struct zs_chanstate *cs, int bps) { int tconst, real_bps; #if 0 while (!(zs_read_csr(cs) & ZSRR0_TX_READY)) {/*nop*/} #endif /* Wait for transmit buffer to empty */ if (bps == 0) { return (0); } #ifdef DIAGNOSTIC if (cs->cs_brg_clk == 0) panic("zs_set_speed"); #endif tconst = BPS_TO_TCONST(cs->cs_brg_clk, bps); if (tconst < 0) return (EINVAL); /* Convert back to make sure we can do it. */ real_bps = TCONST_TO_BPS(cs->cs_brg_clk, tconst); /* XXX - Allow some tolerance here? */ #if 0 if (real_bps != bps) return (EINVAL); #endif cs->cs_preg[12] = tconst; cs->cs_preg[13] = tconst >> 8; /* Caller will stuff the pending registers. */ return (0); }
/* * Transmitter Ready interrupt. * Called at splzs(). */ void zstty_txint(struct zs_chanstate *cs) { struct zstty_softc *zst = cs->cs_private; int rr0; zs_write_csr(cs, ZSWR0_RESET_TXINT); /* * If we've delayed a parameter change, do it now, and restart * output. */ if (cs->cs_heldchange) { zs_loadchannelregs(cs); cs->cs_heldchange = 0; zst->zst_tbc = zst->zst_heldtbc; zst->zst_heldtbc = 0; } while (zst->zst_tbc > 0) { rr0 = zs_read_csr(cs); if ((rr0 & ZSRR0_TX_READY) == 0) break; zs_write_data(cs, *zst->zst_tba); zst->zst_tbc--; zst->zst_tba++; } if (zst->zst_tbc == 0) { if (zst->zst_tx_busy) { zst->zst_tx_busy = 0; zst->zst_tx_done = 1; cs->cs_softreq = 1; } } }
/* expects to be in splzs() */ int zskbd_poll(struct zs_chanstate *cs, uint8_t *key) { u_int i; int rr0, rr1, c; for (i = 1000; i != 0; i--) { rr0 = zs_read_csr(cs); if ((rr0 & ZSRR0_RX_READY) != 0) break; delay(100); } if (i == 0) return ENXIO; rr1 = zs_read_reg(cs, 1); c = zs_read_data(cs); if (rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) return EIO; *key = (uint8_t)c; return 0; }
void zskbd_attach(struct device *parent, struct device *self, void *aux) { struct zskbd_softc *sc = (struct zskbd_softc *)self; struct zsc_softc *zsc = (struct zsc_softc *)parent; struct zsc_attach_args *args = aux; struct zs_chanstate *cs; struct wskbddev_attach_args wskaa; int s, channel, rc; uint8_t key; printf(": "); /* Establish ourself with the MD z8530 driver */ channel = args->channel; cs = zsc->zsc_cs[channel]; cs->cs_ops = &zskbd_zsops; cs->cs_private = sc; sc->sc_dc = malloc(sizeof(struct zskbd_devconfig), M_DEVBUF, M_WAITOK | M_ZERO); s = splzs(); zs_write_reg(cs, 9, (channel == 0) ? ZSWR9_A_RESET : ZSWR9_B_RESET); cs->cs_preg[1] = ZSWR1_RIE | ZSWR1_TIE; cs->cs_preg[4] = (cs->cs_preg[4] & ZSWR4_CLK_MASK) | (ZSWR4_ONESB | ZSWR4_PARENB); /* 1 stop, odd parity */ cs->cs_preg[15] &= ~ZSWR15_ENABLE_ENHANCED; zs_set_speed(cs, ZSKBD_BAUD); zs_loadchannelregs(cs); /* * Empty the keyboard input buffer (if the keyboard is the console * input device and the user invoked UKC, the `enter key up' event * will still be pending in the buffer). */ while ((zs_read_csr(cs) & ZSRR0_RX_READY) != 0) (void)zs_read_data(cs); /* * Ask the keyboard for its DIP switch settings. This will also let * us know whether the keyboard is connected. */ sc->sc_dc->expected = 2; zskbd_ctrl(cs, ZSKBD_CTRL_A_RCB, 0, 0, 0); while (sc->sc_dc->expected != 0) { rc = zskbd_poll(cs, &key); if (rc != 0) { if (rc == ENXIO && sc->sc_dc->expected == 2) { printf("no keyboard"); /* * Attach wskbd nevertheless, in case the * keyboard is plugged late. */ sc->sc_dc->expected = 0; goto dip; } else { printf("i/o error\n"); return; } } zskbd_process(cs, key); } printf("dip switches %02x", sc->sc_dc->dip); dip: /* * Disable key click by default. Note that if the keyboard is not * currently connected, the bit will nevertheless stick and will * disable the click as soon as a keyboard led needs to be lit. */ zskbd_ctrl(cs, ZSKBD_CTRL_A_NOCLICK, 0, 0, 0); splx(s); printf("\n"); if (zskbd_is_console) sc->sc_dc->enabled = 1; /* attach wskbd */ wskaa.console = zskbd_is_console; wskaa.keymap = &sgikbd_wskbd_keymapdata; wskaa.accessops = &zskbd_wskbd_accessops; wskaa.accesscookie = cs; sc->sc_dc->wskbddev = config_found(self, &wskaa, wskbddevprint); }
/* * Open a zs serial (tty) port. */ int zsopen(dev_t dev, int flags, int mode, struct proc *p) { struct zstty_softc *zst; struct zs_chanstate *cs; struct tty *tp; int s; #if IPL_ZS != IPL_TTY int s2; #endif int error; zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); if (zst == NULL) return (ENXIO); tp = zst->zst_tty; cs = zst->zst_cs; /* If KGDB took the line, then tp==NULL */ if (tp == NULL) return (EBUSY); if (ISSET(tp->t_state, TS_ISOPEN) && ISSET(tp->t_state, TS_XCLUDE) && suser(p, 0) != 0) return (EBUSY); s = spltty(); /* * Do the following iff this is a first open. */ if (!ISSET(tp->t_state, TS_ISOPEN)) { struct termios t; tp->t_dev = dev; /* Call the power management hook. */ if (cs->enable) { if ((*cs->enable)(cs)) { splx(s); printf("%s: device enable failed\n", zst->zst_dev.dv_xname); return (EIO); } } /* * Initialize the termios status to the defaults. Add in the * sticky bits from TIOCSFLAGS. */ t.c_ispeed = 0; t.c_ospeed = cs->cs_defspeed; t.c_cflag = cs->cs_defcflag; if (ISSET(zst->zst_swflags, TIOCFLAG_CLOCAL)) SET(t.c_cflag, CLOCAL); if (ISSET(zst->zst_swflags, TIOCFLAG_CRTSCTS)) SET(t.c_cflag, CRTSCTS); if (ISSET(zst->zst_swflags, TIOCFLAG_MDMBUF)) SET(t.c_cflag, MDMBUF); #if IPL_ZS != IPL_TTY s2 = splzs(); #endif /* * Turn on receiver and status interrupts. * We defer the actual write of the register to zsparam(), * but we must make sure status interrupts are turned on by * the time zsparam() reads the initial rr0 state. */ SET(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_TIE | ZSWR1_SIE); /* Clear PPS capture state on first open. */ zst->zst_ppsmask = 0; #if IPL_ZS != IPL_TTY splx(s2); #endif /* Make sure zsparam will see changes. */ tp->t_ospeed = 0; (void)zsparam(tp, &t); /* * Note: zsparam has done: cflag, ispeed, ospeed * so we just need to do: iflag, oflag, lflag, cc * For "raw" mode, just leave all zeros. */ if (!ISSET(zst->zst_hwflags, ZS_HWFLAG_RAW)) { tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_lflag = TTYDEF_LFLAG; } else { tp->t_iflag = 0; tp->t_oflag = 0; tp->t_lflag = 0; } ttychars(tp); ttsetwater(tp); if (ZSDIALOUT(dev)) SET(tp->t_state, TS_CARR_ON); else CLR(tp->t_state, TS_CARR_ON); #if IPL_ZS != IPL_TTY s2 = splzs(); #endif /* Clear the input ring, and unblock. */ zst->zst_rbget = zst->zst_rbput = zst->zst_rbuf; zst->zst_rbavail = zstty_rbuf_size; zs_iflush(cs); CLR(zst->zst_rx_flags, RX_ANY_BLOCK); zs_hwiflow(zst); #if IPL_ZS != IPL_TTY splx(s2); #endif } if (ZSDIALOUT(dev)) { if (ISSET(tp->t_state, TS_ISOPEN)) { /* someone already is dialed in... */ splx(s); return EBUSY; } cs->cs_cua = 1; } error = 0; /* wait for carrier if necessary */ if (ISSET(flags, O_NONBLOCK)) { if (!ZSDIALOUT(dev) && cs->cs_cua) { /* Opening TTY non-blocking... but the CUA is busy */ error = EBUSY; } } else while (cs->cs_cua || (!ISSET(tp->t_cflag, CLOCAL) && !ISSET(tp->t_state, TS_CARR_ON))) { int rr0; error = 0; SET(tp->t_state, TS_WOPEN); if (!ZSDIALOUT(dev) && !cs->cs_cua) { /* * Turn on DTR. We must always do this on non-CUA * devices, even if carrier is not present, because * otherwise we'd have to use TIOCSDTR immediately * after setting CLOCAL, which applications do not * expect. We always assert DTR while the device is * open unless explicitly requested to deassert it. */ #if IPL_ZS != IPL_TTY s2 = splzs(); #endif zs_modem(zst, 1); rr0 = zs_read_csr(cs); #if IPL_ZS != IPL_TTY splx(s2); #endif /* loop, turning on the device, until carrier present */ if (ISSET(rr0, ZSRR0_DCD) || ISSET(zst->zst_swflags, TIOCFLAG_SOFTCAR)) SET(tp->t_state, TS_CARR_ON); } if ((ISSET(tp->t_cflag, CLOCAL) || ISSET(tp->t_state, TS_CARR_ON)) && !cs->cs_cua) break; error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH, ttopen, 0); if (!ZSDIALOUT(dev) && cs->cs_cua && error == EINTR) { error = 0; continue; } if (error) { if (!ISSET(tp->t_state, TS_ISOPEN)) { #if IPL_ZS != IPL_TTY s2 = splzs(); #endif zs_modem(zst, 0); #if IPL_ZS != IPL_TTY splx(s2); #endif CLR(tp->t_state, TS_WOPEN); ttwakeup(tp); } if (ZSDIALOUT(dev)) cs->cs_cua = 0; CLR(tp->t_state, TS_WOPEN); break; } if (!ZSDIALOUT(dev) && cs->cs_cua) continue; } splx(s); if (error == 0) error = ((*linesw[tp->t_line].l_open)(dev, tp, p)); if (error) goto bad; return (0); bad: if (!ISSET(tp->t_state, TS_ISOPEN)) { /* * We failed to open the device, and nobody else had it opened. * Clean up the state as appropriate. */ zs_shutdown(zst); } return (error); }
/* * Receiver Ready interrupt. * Called at splzs(). */ void zstty_rxint(struct zs_chanstate *cs) { struct zstty_softc *zst = cs->cs_private; uint8_t *put, *end; u_int cc; uint8_t rr0, rr1, c; end = zst->zst_ebuf; put = zst->zst_rbput; cc = zst->zst_rbavail; while (cc > 0) { /* * First read the status, because reading the received char * destroys the status of this char. */ rr1 = zs_read_reg(cs, 1); c = zs_read_data(cs); if (ISSET(rr1, ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) { /* Clear the receive error. */ zs_write_csr(cs, ZSWR0_RESET_ERRORS); } put[0] = c; put[1] = rr1; put += 2; if (put >= end) put = zst->zst_rbuf; cc--; rr0 = zs_read_csr(cs); if (!ISSET(rr0, ZSRR0_RX_READY)) break; } /* * Current string of incoming characters ended because * no more data was available or we ran out of space. * Schedule a receive event if any data was received. * If we're out of space, turn off receive interrupts. */ zst->zst_rbput = put; zst->zst_rbavail = cc; if (!ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) { zst->zst_rx_ready = 1; cs->cs_softreq = 1; } /* * See if we are in danger of overflowing a buffer. If * so, use hardware flow control to ease the pressure. */ if (!ISSET(zst->zst_rx_flags, RX_IBUF_BLOCKED) && cc < zst->zst_r_hiwat) { SET(zst->zst_rx_flags, RX_IBUF_BLOCKED); zs_hwiflow(zst); } /* * If we're out of space, disable receive interrupts * until the queue has drained a bit. */ if (!cc) { SET(zst->zst_rx_flags, RX_IBUF_OVERFLOWED); CLR(cs->cs_preg[1], ZSWR1_RIE); cs->cs_creg[1] = cs->cs_preg[1]; zs_write_reg(cs, 1, cs->cs_creg[1]); } }