/* $OpenBSD: qsc.c,v 1.1 2006/08/27 16:55:41 miod Exp $ */ /* * Copyright (c) 2006 Miodrag Vallat. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice, this permission notice, and the disclaimer below * appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Mach Operating System * Copyright (c) 1993-1991 Carnegie Mellon University * All Rights Reserved. * * Permission to use, copy, modify and distribute this software and its * documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON AND OMRON ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON AND OMRON DISCLAIM ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB #include #include #endif #include "qsckbd.h" #include "qscms.h" struct cfdriver qsc_cd = { NULL, "qsc", DV_TTY }; /* console storage */ struct qsc_sv_reg qsccn_sv; /* prototypes */ cdev_decl(qsc); cons_decl(qsc); int qscintr(void *); int qscparam(struct tty *, struct termios *); void qscrint(struct qscsoftc *, u_int); int qscspeed(int); void qscstart(struct tty *); struct tty *qsctty(dev_t); void qscxint(struct qscsoftc *, u_int); /* * Registers are mapped as the least-significant byte of 32-bit * addresses. The following macros hide this. */ #define qsc_readp(sc, reg) \ bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, 4 * (reg)) #define qsc_read(sc, line, reg) \ bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, \ (sc)->sc_regaddr[line][reg]) #define qsc_writep(sc, reg, val) \ bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, 4 * (reg), (val)) #define qsc_write(sc, line, reg, val) \ bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, \ (sc)->sc_regaddr[line][reg], (val)) #define SC_LINE(dev) (minor(dev)) /* * Attachment glue. */ int qsc_match(struct device *parent, void *self, void *aux); void qsc_attach(struct device *parent, struct device *self, void *aux); int qsc_print(void *, const char *); struct cfattach qsc_ca = { sizeof(struct qscsoftc), qsc_match, qsc_attach }; int qsc_match(struct device *parent, void *cf, void *aux) { struct bp_conf *bp = aux; if (strcmp(bp->type, qsc_cd.cd_name) == 0) return (1); return (0); } void qsc_attach(struct device *parent, struct device *self, void *aux) { extern struct vax_bus_space vax_mem_bus_space; struct qscsoftc *sc = (struct qscsoftc *)self; bus_space_handle_t ioh; u_int line, pair, reg; #if NQSCKBD > 0 || NQSCMS > 0 struct qsc_attach_args qa; #endif sc->sc_iot = &vax_mem_bus_space; if (bus_space_map(sc->sc_iot, QSCADDR, VAX_NBPG, 0, &ioh) != 0) { printf(": can't map registers!\n"); return; } sc->sc_ioh = ioh; if (cn_tab->cn_putc == qsccnputc) { sc->sc_console = 1; printf(": console"); } /* * Initialize line-specific data (register addresses) */ for (line = 0; line < SC_NLINES; line++) { sc->sc_regaddr[line][SC_MR] = line * 8 + SC_MRA; sc->sc_regaddr[line][SC_CSR] = line * 8 + SC_CSRA; sc->sc_regaddr[line][SC_CR] = line * 8 + SC_CRA; sc->sc_regaddr[line][SC_TXFIFO] = line * 8 + SC_TXFIFOA; sc->sc_regaddr[line][SC_IOPCR] = (line < 2 ? 0 : 0x10) + (line & 1) + SC_IOPCRA; sc->sc_regaddr[line][SC_ACR] = (line < 2 ? 0 : 0x10) + SC_ACRAB; sc->sc_regaddr[line][SC_IMR] = (line < 2 ? 0 : 0x10) + SC_IMRAB; sc->sc_regaddr[line][SC_OPR] = (line < 2 ? 0 : 0x10) + SC_OPRAB; } for (line = 0; line < SC_NLINES; line++) for (reg = 0; reg < SC_LOGICAL; reg++) sc->sc_regaddr[line][reg] = 0 + 4 * sc->sc_regaddr[line][reg]; /* * Initialize all lines. */ sc->sc_sv_reg = sc->sc_console ? &qsccn_sv : &sc->sc_sv_reg_storage; for (line = 0; line < SC_NLINES; line++) { /* do not reinitialize the console line... */ if (sc->sc_console && line == QSC_LINE_SERIAL) continue; sc->sc_sv_reg->sv_mr1[line] = (line == 3 ? ODDPAR | PAREN : PARDIS) | RXRTS | CL8; sc->sc_sv_reg->sv_mr2[line] = /* TXCTS | */ SB1; sc->sc_sv_reg->sv_csr[line] = line < 2 ? BD9600 : BD4800; sc->sc_sv_reg->sv_cr[line] = TXEN | RXEN; pair = line >> 1; if (sc->sc_console && pair == (QSC_LINE_SERIAL >> 1)) continue; /* Start out with Tx and RX interrupts disabled */ sc->sc_sv_reg->sv_imr[pair] = 0; } for (line = 0; line < SC_NLINES; line++) { /* do not reset the console line... */ if (sc->sc_console && line == QSC_LINE_SERIAL) continue; qsc_write(sc, line, SC_CR, RXRESET | TXDIS | RXDIS); DELAY(1); qsc_write(sc, line, SC_CR, TXRESET | TXDIS | RXDIS); DELAY(1); qsc_write(sc, line, SC_CR, ERRRESET | TXDIS | RXDIS); DELAY(1); qsc_write(sc, line, SC_CR, BRKINTRESET | TXDIS | RXDIS); DELAY(1); qsc_write(sc, line, SC_CR, MRZERO | TXDIS | RXDIS); DELAY(1); qsc_write(sc, line, SC_MR, 0); qsc_write(sc, line, SC_MR, sc->sc_sv_reg->sv_mr1[line]); qsc_write(sc, line, SC_MR, sc->sc_sv_reg->sv_mr2[line]); qsc_write(sc, line, SC_CSR, sc->sc_sv_reg->sv_csr[line]); qsc_write(sc, line, SC_CR, sc->sc_sv_reg->sv_cr[line]); DELAY(1); } for (pair = 0; pair < SC_NLINES / 2; pair++) qsc_write(sc, pair << 1, SC_IMR, sc->sc_sv_reg->sv_imr[pair]); for (line = 0; line < SC_NLINES; line++) { sc->sc_tty[line] = NULL; sc->sc_swflags[line] = 0; } if (sc->sc_console) sc->sc_swflags[QSC_LINE_SERIAL] |= TIOCFLAG_SOFTCAR; printf("\n"); /* * Configure interrupts. We are bidding in 2681 mode for now. */ qsc_writep(sc, SC_ICR, 0x00); for (line = SC_BIDCRA; line <= SC_BIDCRD; line++) qsc_writep(sc, line, 0x00); qsc_writep(sc, SC_IVR, VXT_INTRVEC >> 2); vxtbus_intr_establish(self->dv_xname, IPL_TTY, qscintr, sc); /* * Attach subdevices, and enable RX and TX interrupts on their lines * if successful. */ #if NQSCKBD > 0 /* keyboard */ qa.qa_line = QSC_LINE_KEYBOARD; qa.qa_console = !sc->sc_console; qa.qa_hook = &sc->sc_hook[QSC_LINE_KEYBOARD]; if (config_found(self, &qa, qsc_print) != NULL) sc->sc_sv_reg->sv_imr[QSC_LINE_KEYBOARD >> 1] |= IRXRDYA; #endif #if NQSCMS > 0 /* mouse */ qa.qa_line = QSC_LINE_MOUSE; qa.qa_console = 0; qa.qa_hook = &sc->sc_hook[QSC_LINE_MOUSE]; if (config_found(self, &qa, qsc_print) != NULL) sc->sc_sv_reg->sv_imr[QSC_LINE_MOUSE >> 1] |= IRXRDYB; #endif for (pair = 0; pair < SC_NLINES / 2; pair++) qsc_write(sc, pair << 1, SC_IMR, sc->sc_sv_reg->sv_imr[pair]); sc->sc_rdy = 1; } /* speed tables */ const struct qsc_s { int kspeed; int dspeed; } qsc_speeds[] = { { B0, 0 }, /* 0 baud, special HUP condition */ { B50, NOBAUD }, /* 50 baud, not implemented */ { B75, BD75 }, /* 75 baud */ { B110, BD110 }, /* 110 baud */ { B134, BD134 }, /* 134.5 baud */ { B150, BD150 }, /* 150 baud */ { B200, NOBAUD }, /* 200 baud, not implemented */ { B300, BD300 }, /* 300 baud */ { B600, BD600 }, /* 600 baud */ { B1200, BD1200 }, /* 1200 baud */ { B1800, BD1800 }, /* 1800 baud */ { B2400, BD2400 }, /* 2400 baud */ { B4800, BD4800 }, /* 4800 baud */ { B9600, BD9600 }, /* 9600 baud */ { B19200, BD19200 }, /* 19200 baud */ { -1, NOBAUD }, /* anything more is uncivilized */ }; int qscspeed(int speed) { const struct qsc_s *ds; for (ds = qsc_speeds; ds->kspeed != -1; ds++) if (ds->kspeed == speed) return ds->dspeed; return NOBAUD; } struct tty * qsctty(dev_t dev) { u_int line; struct qscsoftc *sc; line = SC_LINE(dev); if (qsc_cd.cd_ndevs == 0 || line >= SC_NLINES) return (NULL); sc = (struct qscsoftc *)qsc_cd.cd_devs[0]; if (sc == NULL) return (NULL); return sc->sc_tty[line]; } void qscstart(struct tty *tp) { struct qscsoftc *sc; dev_t dev; int s; u_int line; int c, tries; if ((tp->t_state & TS_ISOPEN) == 0) return; dev = tp->t_dev; line = SC_LINE(dev); sc = (struct qscsoftc *)qsc_cd.cd_devs[0]; s = spltty(); if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) goto bail; if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)&tp->t_outq); } selwakeup(&tp->t_wsel); if (tp->t_outq.c_cc == 0) goto bail; } tp->t_state |= TS_BUSY; while (tp->t_outq.c_cc != 0) { /* load transmitter until it is full */ for (tries = 10000; tries != 0; tries --) if (qsc_read(sc, line, SC_SR) & TXRDY) break; if (tries == 0) { timeout_add(&tp->t_rstrt_to, 1); tp->t_state |= TS_TIMEOUT; break; } else { c = getc(&tp->t_outq); qsc_write(sc, line, SC_TXFIFO, c & 0xff); sc->sc_sv_reg->sv_imr[line >> 1] |= line & 1 ? ITXRDYB : ITXRDYA; qsc_write(sc, line, SC_IMR, sc->sc_sv_reg->sv_imr[line >> 1]); } } tp->t_state &= ~TS_BUSY; bail: splx(s); } int qscstop(struct tty *tp, int flag) { int s; s = spltty(); if (tp->t_state & TS_BUSY) { if ((tp->t_state & TS_TTSTOP) == 0) tp->t_state |= TS_FLUSH; } splx(s); return 0; } int qscioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { int error; u_int line; struct tty *tp; struct qscsoftc *sc; line = SC_LINE(dev); sc = (struct qscsoftc *)qsc_cd.cd_devs[0]; tp = sc->sc_tty[line]; if (tp == NULL) return (ENXIO); error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error >= 0) return(error); error = ttioctl(tp, cmd, data, flag, p); if (error >= 0) return(error); switch (cmd) { case TIOCGFLAGS: *(int *)data = sc->sc_swflags[line]; break; case TIOCSFLAGS: error = suser(p, 0); if (error != 0) return (EPERM); sc->sc_swflags[line] = *(int *)data & /* only allow valid flags */ (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS); break; default: return (ENOTTY); } return (0); } int qscparam(struct tty *tp, struct termios *t) { int flags; u_int line, pair; int speeds; u_int8_t mr1, mr2; struct qscsoftc *sc; dev_t dev; dev = tp->t_dev; line = SC_LINE(dev); pair = line >> 1; sc = (struct qscsoftc *)qsc_cd.cd_devs[0]; tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; flags = tp->t_flags; /* disable Tx and Rx */ if (sc->sc_console == 0 || line != QSC_LINE_SERIAL) { if (line & 1) sc->sc_sv_reg->sv_imr[pair] &= ~(ITXRDYB | IRXRDYB); else sc->sc_sv_reg->sv_imr[pair] &= ~(ITXRDYA | IRXRDYA); qsc_write(sc, line, SC_IMR, sc->sc_sv_reg->sv_imr[pair]); /* set baudrate */ speeds = qscspeed(tp->t_ispeed); if (speeds == NOBAUD) speeds = DEFBAUD; qsc_write(sc, line, SC_CSR, speeds); sc->sc_sv_reg->sv_csr[line] = speeds; /* get saved mode registers and clear set up parameters */ mr1 = sc->sc_sv_reg->sv_mr1[line]; mr1 &= ~(CLMASK | PARTYPEMASK | PARMODEMASK); mr2 = sc->sc_sv_reg->sv_mr2[line]; mr2 &= ~SBMASK; /* set up character size */ switch (t->c_cflag & CSIZE) { case CL8: mr1 |= CL8; break; case CL7: mr1 |= CL7; break; case CL6: mr1 |= CL6; break; case CL5: mr1 |= CL5; break; } /* set up stop bits */ if (tp->t_ospeed == B110) mr2 |= SB2; else mr2 |= SB1; /* set up parity */ if (t->c_cflag & PARENB) { mr1 |= PAREN; if (t->c_cflag & PARODD) mr1 |= ODDPAR; else mr1 |= EVENPAR; } else mr1 |= PARDIS; if (sc->sc_sv_reg->sv_mr1[line] != mr1 || sc->sc_sv_reg->sv_mr2[line] != mr2) { /* write mode registers to duart */ qsc_write(sc, line, SC_CR, MRONE); DELAY(1); qsc_write(sc, line, SC_MR, mr1); qsc_write(sc, line, SC_MR, mr2); /* save changed mode registers */ sc->sc_sv_reg->sv_mr1[line] = mr1; sc->sc_sv_reg->sv_mr2[line] = mr2; } } /* enable transmitter? */ if (tp->t_state & TS_BUSY) { sc->sc_sv_reg->sv_imr[pair] |= line & 1 ? ITXRDYB : ITXRDYA; } /* re-enable the receiver */ sc->sc_sv_reg->sv_imr[pair] |= line & 1 ? IRXRDYB : IRXRDYA; qsc_write(sc, line, SC_IMR, sc->sc_sv_reg->sv_imr[pair]); return (0); } int qscopen(dev_t dev, int flag, int mode, struct proc *p) { int s; u_int line; struct qscsoftc *sc; struct tty *tp; line = SC_LINE(dev); if (qsc_cd.cd_ndevs == 0 || line >= SC_NLINES) return (ENXIO); /* Line B is not wired... */ if (line == QSC_LINE_DEAD) return (ENXIO); sc = (struct qscsoftc *)qsc_cd.cd_devs[0]; if (sc == NULL) return (ENXIO); /* if some other device is using the line, it's not available */ if (sc->sc_hook[line].fn != NULL) return (ENXIO); s = spltty(); if (sc->sc_tty[line] != NULL) tp = sc->sc_tty[line]; else tp = sc->sc_tty[line] = ttymalloc(); tp->t_oproc = qscstart; tp->t_param = qscparam; tp->t_dev = dev; if ((tp->t_state & TS_ISOPEN) == 0) { ttychars(tp); if (tp->t_ispeed == 0) { tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = B9600; if (sc->sc_console && line == QSC_LINE_SERIAL) { /* console is 8N1 */ tp->t_cflag = (CREAD | CS8 | HUPCL); } else { tp->t_cflag = TTYDEF_CFLAG; } } if (sc->sc_swflags[line] & TIOCFLAG_CLOCAL) tp->t_cflag |= CLOCAL; if (sc->sc_swflags[line] & TIOCFLAG_CRTSCTS) tp->t_cflag |= CRTSCTS; if (sc->sc_swflags[line] & TIOCFLAG_MDMBUF) tp->t_cflag |= MDMBUF; qscparam(tp, &tp->t_termios); ttsetwater(tp); tp->t_state |= TS_CARR_ON; } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { splx(s); return (EBUSY); } /* * Reset the tty pointer, as there could have been a dialout * use of the tty with a dialin open waiting. */ tp->t_dev = dev; splx(s); return ((*linesw[tp->t_line].l_open)(dev, tp)); } int qscclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp; struct qscsoftc *sc; u_int line; line = SC_LINE(dev); sc = (struct qscsoftc *)qsc_cd.cd_devs[0]; tp = sc->sc_tty[line]; (*linesw[tp->t_line].l_close)(tp, flag); ttyclose(tp); return (0); } int qscread(dev_t dev, struct uio *uio, int flag) { u_int line; struct tty *tp; struct qscsoftc *sc; line = SC_LINE(dev); sc = (struct qscsoftc *)qsc_cd.cd_devs[0]; tp = sc->sc_tty[line]; if (tp == NULL) return (ENXIO); return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int qscwrite(dev_t dev, struct uio *uio, int flag) { u_int line; struct tty *tp; struct qscsoftc *sc; line = SC_LINE(dev); sc = (struct qscsoftc *)qsc_cd.cd_devs[0]; tp = sc->sc_tty[line]; if (tp == NULL) return (ENXIO); return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } void qscrint(struct qscsoftc *sc, u_int line) { struct tty *tp; int data; unsigned char sr; int overrun = 0; tp = sc->sc_tty[line]; /* read status reg */ while ((sr = qsc_read(sc, line, SC_SR)) & RXRDY) { /* read data and reset receiver */ data = qsc_read(sc, line, SC_RXFIFO); if (sr & RBRK) { /* clear break state */ qsc_write(sc, line, SC_CR, BRKINTRESET); DELAY(1); qsc_write(sc, line, SC_CR, ERRRESET); DELAY(1); continue; } if ((sr & ROVRN) && cold == 0 && overrun == 0) { log(LOG_WARNING, "%s line %d: receiver overrun\n", sc->sc_dev.dv_xname, line); overrun = 1; } if (sr & FRERR) data |= TTY_FE; if (sr & PERR) data |= TTY_PE; /* clear error state */ if (sr & (ROVRN | FRERR | PERR)) { qsc_write(sc, line, SC_CR, ERRRESET); DELAY(1); } if (sc->sc_hook[line].fn != NULL) { if ((data & TTY_ERRORMASK) != 0 || (*sc->sc_hook[line].fn)(sc->sc_hook[line].arg, data)) continue; } if ((tp->t_state & (TS_ISOPEN|TS_WOPEN)) == 0 && (sc->sc_console == 0 || line != QSC_LINE_SERIAL)) { continue; } /* no errors */ #if defined(DDB) if (tp->t_dev == cn_tab->cn_dev) { int j = kdbrint(data); if (j == 1) continue; if (j == 2) (*linesw[tp->t_line].l_rint)(27, tp); } #endif (*linesw[tp->t_line].l_rint)(data, tp); } } void qscxint(struct qscsoftc *sc, u_int line) { struct tty *tp; u_int pair; tp = sc->sc_tty[line]; if ((tp->t_state & (TS_ISOPEN|TS_WOPEN))==0) goto out; if (tp->t_state & TS_BUSY) { tp->t_state &= ~(TS_BUSY | TS_FLUSH); qscstart(tp); if (tp->t_state & TS_BUSY) { /* do not disable transmitter, yet */ return; } } out: /* disable transmitter */ pair = line >> 1; sc->sc_sv_reg->sv_imr[pair] &= line & 1 ? ~ITXRDYB : ~ITXRDYA; qsc_write(sc, line, SC_IMR, sc->sc_sv_reg->sv_imr[pair]); } int qscintr(void *arg) { struct qscsoftc *sc = arg; u_int8_t isr[SC_NLINES >> 1], curisr; u_int pair, line; int rc = 0; for (pair = 0; pair < SC_NLINES >> 1; pair++) { line = pair << 1; /* read interrupt status register and mask with imr */ isr[pair] = curisr = qsc_read(sc, line, SC_ISR); curisr &= sc->sc_sv_reg->sv_imr[pair]; if (curisr == 0) continue; rc = 1; if (curisr & IRXRDYA) qscrint(sc, line); if (curisr & ITXRDYA) qscxint(sc, line); if (curisr & IBRKA) { qsc_write(sc, line, SC_CR, BRKINTRESET); DELAY(1); } if (curisr & IRXRDYB) qscrint(sc, line + 1); if (curisr & ITXRDYB) qscxint(sc, line + 1); if (curisr & IBRKB) { qsc_write(sc, line + 1, SC_CR, BRKINTRESET); DELAY(1); } } return (rc); } /* * Console interface routines. */ vaddr_t qsc_cnregs; #define qsc_cnread(reg) \ *(volatile u_int8_t *)(qsc_cnregs + 4 * (reg)) #define qsc_cnwrite(reg, val) \ *(volatile u_int8_t *)(qsc_cnregs + 4 * (reg)) = (val) void qsccnprobe(struct consdev *cp) { int maj; extern int getmajor(void *); extern vaddr_t iospace; if (vax_boardtype != VAX_BTYP_VXT) return; /* locate the major number */ if ((maj = getmajor(qscopen)) < 0) return; qsc_cnregs = iospace; ioaccess(iospace, QSCADDR, 1); cp->cn_dev = makedev(maj, QSC_LINE_SERIAL); cp->cn_pri = vax_confdata & 2 ? CN_NORMAL : CN_REMOTE; } void qsccninit(cp) struct consdev *cp; { qsccn_sv.sv_mr1[QSC_LINE_SERIAL] = PARDIS | RXRTS | CL8; qsccn_sv.sv_mr2[QSC_LINE_SERIAL] = /* TXCTS | */ SB1; qsccn_sv.sv_csr[QSC_LINE_SERIAL] = BD9600; qsccn_sv.sv_cr[QSC_LINE_SERIAL] = TXEN | RXEN; qsccn_sv.sv_imr[QSC_LINE_SERIAL] = 0; qsc_cnwrite(SC_CRA, RXRESET | TXDIS | RXDIS); DELAY(1); qsc_cnwrite(SC_CRA, TXRESET | TXDIS | RXDIS); DELAY(1); qsc_cnwrite(SC_CRA, ERRRESET | TXDIS | RXDIS); DELAY(1); qsc_cnwrite(SC_CRA, BRKINTRESET | TXDIS | RXDIS); DELAY(1); qsc_cnwrite(SC_CRA, MRZERO | TXDIS | RXDIS); DELAY(1); qsc_cnwrite(SC_MRA, 0); qsc_cnwrite(SC_MRA, qsccn_sv.sv_mr1[QSC_LINE_SERIAL]); qsc_cnwrite(SC_MRA, qsccn_sv.sv_mr2[QSC_LINE_SERIAL]); qsc_cnwrite(SC_CSRA, qsccn_sv.sv_csr[QSC_LINE_SERIAL]); qsc_cnwrite(SC_CRA, qsccn_sv.sv_cr[QSC_LINE_SERIAL]); DELAY(1); qsc_cnwrite(SC_IMRAB, qsccn_sv.sv_imr[QSC_LINE_SERIAL]); qsc_cnwrite(SC_IMRCD, 0); } int qsccngetc(dev_t dev) { unsigned char sr; /* status reg of line a/b */ u_char c; /* received character */ int s; s = spltty(); /* disable interrupts for this line and enable receiver */ qsc_cnwrite(SC_IMRAB, qsccn_sv.sv_imr[QSC_LINE_SERIAL] & ~ITXRDYA); qsc_cnwrite(SC_CRA, RXEN); DELAY(1); for (;;) { /* read status reg */ sr = qsc_cnread(SC_SRA); /* receiver interrupt handler*/ if (sr & RXRDY) { /* read character from line */ c = qsc_cnread(SC_RXFIFOA); /* check break condition */ if (sr & RBRK) { /* clear break state */ qsc_cnwrite(SC_CRA, BRKINTRESET); DELAY(1); qsc_cnwrite(SC_CRA, ERRRESET); DELAY(1); break; } if (sr & (FRERR | PERR | ROVRN)) { /* clear error state */ qsc_cnwrite(SC_CRA, ERRRESET); DELAY(1); } else { break; } } } /* restore the previous state */ qsc_cnwrite(SC_IMRAB, qsccn_sv.sv_imr[QSC_LINE_SERIAL]); qsc_cnwrite(SC_CRA, qsccn_sv.sv_cr[QSC_LINE_SERIAL]); splx(s); return ((int)c); } void qsccnputc(dev_t dev, int c) { int s; if (mfpr(PR_MAPEN) == 0) return; s = spltty(); /* disable interrupts for this line and enable transmitter */ qsc_cnwrite(SC_IMRAB, qsccn_sv.sv_imr[QSC_LINE_SERIAL] & ~ITXRDYA); qsc_cnwrite(SC_CRA, TXEN); DELAY(1); while ((qsc_cnread(SC_SRA) & TXRDY) == 0) ; qsc_cnwrite(SC_TXFIFOA, c); /* wait for transmitter to empty */ while ((qsc_cnread(SC_SRA) & TXEMT) == 0) ; /* restore the previous state */ qsc_cnwrite(SC_IMRAB, qsccn_sv.sv_imr[QSC_LINE_SERIAL]); qsc_cnwrite(SC_CRA, qsccn_sv.sv_cr[QSC_LINE_SERIAL]); DELAY(1); splx(s); } void qsccnpollc(dev, pollflag) dev_t dev; int pollflag; { } /* * Keyboard and mouse helper routines */ #if NQSCKBD > 0 || NQSCMS > 0 int qsc_print(void *aux, const char *name) { struct qsc_attach_args *qa = aux; if (name != NULL) printf(qa->qa_line == QSC_LINE_KEYBOARD ? "lkkbd at %s" : "lkms at %s", name); else printf(" line %d", qa->qa_line); return (UNCONF); } int qscgetc(u_int line) { bus_addr_t craddr; struct qscsoftc *sc = NULL; int s; u_int8_t sr, imr, imrmask, cr, c; s = spltty(); craddr = line == QSC_LINE_KEYBOARD ? SC_CRC : SC_CRD; imrmask = line & 1 ? ~IRXRDYB : ~IRXRDYA; imr = sc != NULL ? sc->sc_sv_reg->sv_imr[line / 2] : 0; cr = sc != NULL ? sc->sc_sv_reg->sv_cr[line] : 0; /* disable interrupts for this line and enable receiver */ qsc_cnwrite(SC_IMRCD, imr & imrmask); qsc_cnwrite(craddr, RXEN); DELAY(1); for (;;) { /* read status reg */ sr = qsc_cnread(line == QSC_LINE_KEYBOARD ? SC_SRC : SC_SRD); /* receiver interrupt handler*/ if (sr & RXRDY) { /* read character from line */ c = qsc_cnread(line == QSC_LINE_KEYBOARD ? SC_RXFIFOC : SC_RXFIFOD); /* check break condition */ if (sr & RBRK) { /* clear break state */ qsc_cnwrite(craddr, BRKINTRESET); DELAY(1); qsc_cnwrite(craddr, ERRRESET); DELAY(1); break; } if (sr & (FRERR | PERR | ROVRN)) { /* clear error state */ qsc_cnwrite(craddr, ERRRESET); DELAY(1); } else { break; } } } /* restore the previous state */ qsc_cnwrite(SC_IMRCD, imr); qsc_cnwrite(craddr, cr); DELAY(1); splx(s); return ((int)c); } void qscputc(u_int line, int c) { bus_addr_t craddr; struct qscsoftc *sc = NULL; int s; u_int8_t imr, imrmask, cr; s = spltty(); if (qsc_cd.cd_ndevs != 0 && (sc = (struct qscsoftc *)qsc_cd.cd_devs[0]) != NULL) if (sc->sc_rdy == 0) sc = NULL; craddr = line == QSC_LINE_KEYBOARD ? SC_CRC : SC_CRD; imrmask = line & 1 ? ~ITXRDYB : ~ITXRDYA; imr = sc != NULL ? sc->sc_sv_reg->sv_imr[line / 2] : 0; cr = sc != NULL ? sc->sc_sv_reg->sv_cr[line] : 0; /* disable interrupts for this line and enable transmitter */ qsc_cnwrite(SC_IMRCD, imr & imrmask); qsc_cnwrite(craddr, TXEN); DELAY(1); while ((qsc_cnread(line == QSC_LINE_KEYBOARD ? SC_SRC : SC_SRD) & TXRDY) == 0) ; qsc_cnwrite(line == QSC_LINE_KEYBOARD ? SC_TXFIFOC : SC_TXFIFOD, c); /* wait for transmitter to empty */ while ((qsc_cnread(line == QSC_LINE_KEYBOARD ? SC_SRC : SC_SRD) & TXEMT) == 0) ; /* restore the previous state */ qsc_cnwrite(SC_IMRCD, imr); qsc_cnwrite(craddr, cr); DELAY(1); splx(s); } #endif