len = 32;
struct tty *sabttytty __P((dev_t));void sabtty_cnputc __P((struct sabtty_softc *, int));int sabtty_cngetc __P((struct sabtty_softc *));void sabtty_abort __P((struct sabtty_softc *));struct cfattach sab_ca = {	sizeof(struct sab_softc), sab_match, sab_attach};struct cfdriver sab_cd = {	NULL, "sab", DV_DULL};struct cfattach sabtty_ca = {	sizeof(struct sabtty_softc), sabtty_match, sabtty_attach};struct cfdriver sabtty_cd = {	NULL, "sabtty", DV_TTY};struct sabtty_rate {	int baud;	int n, m;};struct sabtty_rate sabtty_baudtable[] = {	{      50,	35,     10 },	{      75,	47,	9 },	{     110,	32,	9 },	{     134,	53,	8 },	{     150,	47,	8 },	{     200,	35,	8 },	{     300,	47,	7 },	{     600,	47,	6 },	{    1200,	47,	5 },	{    1800,	31,	5 },	{    2400,	47,	4 },	{    4800,	47,	3 },	{    9600,	47,	2 },	{   19200,	47,	1 },	{   38400,	23,	1 },	{   57600,	15,	1 },	{  115200,	 7,	1 },	{  230400,	 3,	1 },	{  460800,	 1,	1 },	{   76800,	11,	1 },	{  153600,	 5,	1 },	{  307200,	 3,	1 },	{  614400,	 3,	0 },	{  921600,	 0,	1 },};intsab_match(parent, match, aux)	struct device *parent;	void *match, *aux;{	struct ebus_attach_args *ea = aux;	if (strcmp(ea->ea_name, "se") == 0)		return (1);	return (0);}voidsab_attach(parent, self, aux)	struct device *parent;	struct device *self;	void *aux;{	struct sab_softc *sc = (struct sab_softc *)self;	struct ebus_attach_args *ea = aux;	u_int8_t r;	u_int i;	sc->sc_bt = ea->ea_bustag;	sc->sc_node = ea->ea_node;	/* Use prom mapping, if available. */	if (ea->ea_nvaddrs)		sc->sc_bh = (bus_space_handle_t)ea->ea_vaddrs[0];	else if (ebus_bus_map(sc->sc_bt, 0,	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]), ea->ea_regs[0].size,	    BUS_SPACE_MAP_LINEAR, 0, &sc->sc_bh) != 0) {		printf(": can't map register space\n");		return;	}	sc->sc_ih = bus_intr_establish(ea->ea_bustag, ea->ea_intrs[0],	    IPL_TTY, 0, sab_intr, sc);	if (sc->sc_ih == NULL) {		printf(": can't map interrupt\n");		return;	}	sc->sc_softintr = softintr_establish(IPL_TTY, sab_softintr, sc);	if (sc->sc_softintr == NULL) {		printf(": can't get soft intr\n");		return;	}	printf(": rev ");	r = SAB_READ(sc, SAB_VSTR) & SAB_VSTR_VMASK;	switch (r) {	case SAB_VSTR_V_1:		printf("1");		break;	case SAB_VSTR_V_2:		printf("2");		break;	case SAB_VSTR_V_32:		printf("3.2");		break;	default:		printf("unknown(0x%x)", r);		break;	}	printf("\n");	/* Set all pins, except DTR pins to be inputs */	SAB_WRITE(sc, SAB_PCR, ~(SAB_PVR_DTR_A | SAB_PVR_DTR_B));	/* Disable port interrupts */	SAB_WRITE(sc, SAB_PIM, 0xff);	SAB_WRITE(sc, SAB_PVR, SAB_PVR_DTR_A | SAB_PVR_DTR_B | SAB_PVR_MAGIC);	SAB_WRITE(sc, SAB_IPC, SAB_IPC_ICPL);	for (i = 0; i < SAB_NCHAN; i++) {		struct sabtty_attach_args sta;		sta.sbt_portno = i;		sc->sc_child[i] = (struct sabtty_softc *)config_found_sm(self,		    &sta, sab_print, sabtty_match);		if (sc->sc_child[i] != NULL)			sc->sc_nchild++;	}}intsab_print(args, name)	void *args;	const char *name;{	struct sabtty_attach_args *sa = args;	if (name)		printf("sabtty at %s", name);	printf(" port %d", sa->sbt_portno);	return (UNCONF);}intsab_intr(vsc)	void *vsc;{	struct sab_softc *sc = vsc;	int r = 0, needsoft = 0;	u_int8_t gis;	gis = SAB_READ(sc, SAB_GIS);	/* channel A */	if ((gis & (SAB_GIS_ISA1 | SAB_GIS_ISA0)) && sc->sc_child[0] &&	    sc->sc_child[0]->sc_tty)		r |= sabtty_intr(sc->sc_child[0], &needsoft);	/* channel B */	if ((gis & (SAB_GIS_ISB1 | SAB_GIS_ISB0)) && sc->sc_child[1] &&	    sc->sc_child[1]->sc_tty)		r |= sabtty_intr(sc->sc_child[1], &needsoft);	if (needsoft)		softintr_schedule(sc->sc_softintr);	return (r);}voidsab_softintr(vsc)	void *vsc;{	struct sab_softc *sc = vsc;	if (sc->sc_child[0] && sc->sc_child[0]->sc_tty)		sabtty_softintr(sc->sc_child[0]);	if (sc->sc_child[1] && sc->sc_child[1]->sc_tty)		sabtty_softintr(sc->sc_child[1]);}intsabtty_match(parent, match, aux)	struct device *parent;	void *match, *aux;{	struct sabtty_attach_args *sa = aux;	if (sa->sbt_portno < SAB_NCHAN)		return (1);	return (0);}voidsabtty_attach(parent, self, aux)	struct device *parent;	struct device *self;	void *aux;{	struct sabtty_softc *sc = (struct sabtty_softc *)self;	struct sabtty_attach_args *sa = aux;	int r;	sc->sc_tty = ttymalloc();	if (sc->sc_tty == NULL) {		printf(": failed to allocate tty\n");		return;	}	tty_attach(sc->sc_tty);	sc->sc_tty->t_oproc = sabtty_start;	sc->sc_tty->t_param = sabtty_param;	sc->sc_parent = (struct sab_softc *)parent;	sc->sc_bt = sc->sc_parent->sc_bt;	sc->sc_portno = sa->sbt_portno;	sc->sc_rend = sc->sc_rbuf + SABTTY_RBUF_SIZE;	switch (sa->sbt_portno) {	case 0:	/* port A */		sc->sc_pvr_dtr = SAB_PVR_DTR_A;		sc->sc_pvr_dsr = SAB_PVR_DSR_A;		r = bus_space_subregion(sc->sc_bt, sc->sc_parent->sc_bh,		    SAB_CHAN_A, SAB_CHANLEN, &sc->sc_bh);		break;	case 1:	/* port B */		sc->sc_pvr_dtr = SAB_PVR_DTR_B;		sc->sc_pvr_dsr = SAB_PVR_DSR_B;		r = bus_space_subregion(sc->sc_bt, sc->sc_parent->sc_bh,		    SAB_CHAN_B, SAB_CHANLEN, &sc->sc_bh);		break;	default:		printf(": invalid channel: %u\n", sa->sbt_portno);		return;	}	if (r != 0) {		printf(": failed to allocate register subregion\n");		return;	}	sabtty_console_flags(sc);	if (sc->sc_flags & (SABTTYF_CONS_IN | SABTTYF_CONS_OUT)) {		char *acc;		switch (sc->sc_flags & (SABTTYF_CONS_IN | SABTTYF_CONS_OUT)) {		case SABTTYF_CONS_IN:			acc = "input";			break;		case SABTTYF_CONS_OUT:			acc = "output";			break;		case SABTTYF_CONS_IN|SABTTYF_CONS_OUT:		default:			acc = "i/o";			break;		}		/* Let current output drain */		DELAY(100000);		if (sc->sc_flags & SABTTYF_CONS_IN) {			sabtty_cons_input = sc;			cn_tab->cn_pollc = sab_cnpollc;			cn_tab->cn_getc = sab_cngetc;			cn_tab->cn_dev = makedev(77/*XXX*/, self->dv_unit);			shutdownhook_establish(sabtty_shutdown, sc);		}		if (sc->sc_flags & SABTTYF_CONS_OUT) {			sabtty_cons_output = sc;			cn_tab->cn_putc = sab_cnputc;			cn_tab->cn_dev = makedev(77/*XXX*/, self->dv_unit);		}		printf(": console %s", acc);	} else {		/* Not a console... */		sabtty_reset(sc);	}	printf("\n");}intsabtty_intr(sc, needsoftp)	struct sabtty_softc *sc;	int *needsoftp;{	u_int8_t isr0, isr1;	int i, len = 0, needsoft = 0, r = 0, clearfifo = 0;	isr0 = SAB_READ(sc, SAB_ISR0);	isr1 = SAB_READ(sc, SAB_ISR1);	if (isr0 || isr1)		r = 1;	if (isr0 & SAB_ISR0_RPF) {		len = 32;		clearfifo = 1;	}	if (isr0 & SAB_ISR0_TCD) {		len = (32 - 1) & SAB_READ(sc, SAB_RBCL);		clearfifo = 1;	}	if (isr0 & SAB_ISR0_TIME) {		sabtty_cec_wait(sc);		SAB_WRITE(sc, SAB_CMDR, SAB_CMDR_RFRD);	}	if (isr0 & SAB_ISR0_RFO) {		sc->sc_flags |= SABTTYF_RINGOVERFLOW;		clearfifo = 1;	}	if (len != 0) {		u_int8_t *ptr;		ptr = sc->sc_rput;		for (i = 0; i < len; i++) {			*ptr++ = SAB_READ(sc, SAB_RFIFO);			if (ptr == sc->sc_rend)				ptr = sc->sc_rbuf;			if (ptr == sc->sc_rget) {				if (ptr == sc->sc_rbuf)					ptr = sc->sc_rend;				ptr--;				sc->sc_flags |= SABTTYF_RINGOVERFLOW;			}		}		sc->sc_rput = ptr;		needsoft = 1;	}	if (clearfifo) {		sabtty_cec_wait(sc);		SAB_WRITE(sc, SAB_CMDR, SAB_CMDR_RMC);	}	if (isr0 & SAB_ISR0_CDSC) {		sc->sc_flags |= SABTTYF_CDCHG;		needsoft = 1;	}	if (isr1 & SAB_ISR1_BRK)		sabtty_abort(sc);	if (isr1 & SAB_ISR1_XPR) {		r = 1;		if ((sc->sc_flags & SABTTYF_STOP) == 0) {			len = 32;			if (sc->sc_txc < 32)				len = sc->sc_txc;			for (i = 0; i < len; i++) {				SAB_WRITE(sc, SAB_XFIFO + i, *sc->sc_txp);				sc->sc_txp++;				sc->sc_txc--;			}			if (i != 0) {				sabtty_cec_wait(sc);				SAB_WRITE(sc, SAB_CMDR, SAB_CMDR_XF);			}		}		if ((sc->sc_txc == 0) || (sc->sc_flags & SABTTYF_STOP)) {			sc->sc_imr1 |= SAB_IMR1_XPR;			SAB_WRITE(sc, SAB_IMR1, sc->sc_imr1);			sc->sc_flags &= ~SABTTYF_STOP;			sc->sc_flags |= SABTTYF_DONE;			needsoft = 1;		}	}	if (needsoft)		*needsoftp = needsoft;	return (r);}voidsabtty_softintr(sc)	struct sabtty_softc *sc;{	struct tty *tp = sc->sc_tty;	int s, flags;	u_int8_t r;	if (tp == NULL)		return;	if ((tp->t_state & TS_ISOPEN) == 0)		return;	while (sc->sc_rget != sc->sc_rput) {		int data;		u_int8_t stat;		data = sc->sc_rget[0];		stat = sc->sc_rget[1];		sc->sc_rget += 2;		if (stat & SAB_RSTAT_PE)			data |= TTY_PE;		if (stat & SAB_RSTAT_FE)			data |= TTY_FE;		if (sc->sc_rget == sc->sc_rend)			sc->sc_rget = sc->sc_rbuf;		(*linesw[tp->t_line].l_rint)(data, tp);	}	s = splhigh();	flags = sc->sc_flags;	sc->sc_flags &= ~(SABTTYF_DONE|SABTTYF_CDCHG|SABTTYF_RINGOVERFLOW);	splx(s);	if (flags & SABTTYF_CDCHG) {		s = spltty();		r = SAB_READ(sc, SAB_VSTR) & SAB_VSTR_CD;		splx(s);		(*linesw[tp->t_line].l_modem)(tp, r);	}	if (flags & SABTTYF_RINGOVERFLOW)		log(LOG_WARNING, "%s: ring overflow\n", sc->sc_dv.dv_xname);	if (flags & SABTTYF_DONE) {		ndflush(&tp->t_outq, sc->sc_txp - tp->t_outq.c_cf);		tp->t_state &= ~TS_BUSY;		(*linesw[tp->t_line].l_start)(tp);	}}intsabttyopen(dev, flags, mode, p)	dev_t dev;	int flags, mode;	struct proc *p;{	struct sab_softc *bc;	struct sabtty_softc *sc;	struct tty *tp;	int card = SAB_CARD(dev), port = SAB_PORT(dev), s;	if (card >= sab_cd.cd_ndevs)		return (ENXIO);	bc = sab_cd.cd_devs[card];	if (bc == NULL)		return (ENXIO);	if (port >= bc->sc_nchild)		return (ENXIO);	sc = bc->sc_child[port];	if (sc == NULL)		return (ENXIO);	tp = sc->sc_tty;	tp->t_dev = dev;	if ((tp->t_state & TS_ISOPEN) == 0) {		tp->t_state |= TS_WOPEN;		ttychars(tp);		tp->t_iflag = TTYDEF_IFLAG;		tp->t_oflag = TTYDEF_OFLAG;		tp->t_cflag = TTYDEF_CFLAG;		if (sc->sc_openflags & TIOCFLAG_CLOCAL)			tp->t_cflag |= CLOCAL;		if (sc->sc_openflags & TIOCFLAG_CRTSCTS)			tp->t_cflag |= CRTSCTS;		if (sc->sc_openflags & TIOCFLAG_MDMBUF)			tp->t_cflag |= MDMBUF;		tp->t_lflag = TTYDEF_LFLAG;		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;		sc->sc_rput = sc->sc_rget = sc->sc_rbuf;		s = spltty();		sabtty_reset(sc);		sabtty_param(tp, &tp->t_termios);		ttsetwater(tp);		sc->sc_imr0 = SAB_IMR0_PERR | SAB_IMR0_FERR | SAB_IMR0_PLLA;		SAB_WRITE(sc, SAB_IMR0, sc->sc_imr0);		sc->sc_imr1 = SAB_IMR1_BRKT | SAB_IMR1_ALLS | SAB_IMR1_XDU |		    SAB_IMR1_TIN | SAB_IMR1_CSC | SAB_IMR1_XMR | SAB_IMR1_XPR;		SAB_WRITE(sc, SAB_IMR1, sc->sc_imr1);		SAB_WRITE(sc, SAB_CCR0, SAB_READ(sc, SAB_CCR0) | SAB_CCR0_PU);		sabtty_flush(sc);		if ((sc->sc_openflags & TIOCFLAG_SOFTCAR) ||		    (SAB_READ(sc, SAB_VSTR) & SAB_VSTR_CD))			tp->t_state |= TS_CARR_ON;		else			tp->t_state &= ~TS_CARR_ON;	} else if ((tp->t_state & TS_XCLUDE) &&	    (!suser(p->p_ucred, &p->p_acflag))) {		return (EBUSY);	} else {		s = spltty();	}	if ((flags & O_NONBLOCK) == 0) {		while ((tp->t_cflag & CLOCAL) == 0 &&		    (tp->t_state & TS_CARR_ON) == 0) {			int error;			tp->t_state |= TS_WOPEN;			error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH,			    "sabttycd", 0);			if (error != 0) {				splx(s);				tp->t_state &= ~TS_WOPEN;				return (error);			}		}	}	splx(s);	return ((*linesw[tp->t_line].l_open)(dev, tp));}intsabttyclose(dev, flags, mode, p)	dev_t dev;	int flags, mode;	struct proc *p;{	struct sab_softc *bc = sab_cd.cd_devs[SAB_CARD(dev)];	struct sabtty_softc *sc = bc->sc_child[SAB_PORT(dev)];	struct tty *tp = sc->sc_tty;	int s;	(*linesw[tp->t_line].l_close)(tp, flags);	s = spltty();	if ((tp->t_cflag & HUPCL) || ((tp->t_state & TS_ISOPEN) == 0)) {		sabtty_mdmctrl(sc, 0, DMSET);		sabtty_flush(sc);		sabtty_reset(sc);	}	splx(s);	ttyclose(tp);	return (0);}intsabttyread(dev, uio, flags)	dev_t dev;	struct uio *uio;	int flags;{	struct sab_softc *bc = sab_cd.cd_devs[SAB_CARD(dev)];	struct sabtty_softc *sc = bc->sc_child[SAB_PORT(dev)];	struct tty *tp = sc->sc_tty;	return ((*linesw[tp->t_line].l_read)(tp, uio, flags));}intsabttywrite(dev, uio, flags)	dev_t dev;	struct uio *uio;	int flags;{	struct sab_softc *bc = sab_cd.cd_devs[SAB_CARD(dev)];	struct sabtty_softc *sc = bc->sc_child[SAB_PORT(dev)];	struct tty *tp = sc->sc_tty;	return ((*linesw[tp->t_line].l_write)(tp, uio, flags));}intsabttyioctl(dev, cmd, data, flags, p)	dev_t dev;	u_long cmd;	caddr_t data;	int flags;	struct proc *p;{	struct sab_softc *bc = sab_cd.cd_devs[SAB_CARD(dev)];	struct sabtty_softc *sc = bc->sc_child[SAB_PORT(dev)];	struct tty *tp = sc->sc_tty;	int error;	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flags, p);	if (error >= 0)		return (error);	error = ttioctl(tp, cmd, data, flags, p);	if (error >= 0)		return (error);	error = 0;	switch (cmd) {	case TIOCSBRK:		SAB_WRITE(sc, SAB_DAFO,		    SAB_READ(sc, SAB_DAFO) | SAB_DAFO_XBRK);		break;	case TIOCCBRK:		SAB_WRITE(sc, SAB_DAFO,		    SAB_READ(sc, SAB_DAFO) & ~SAB_DAFO_XBRK);		break;	case TIOCSDTR:		sabtty_mdmctrl(sc, TIOCM_DTR, DMBIS);		break;	case TIOCCDTR:		sabtty_mdmctrl(sc, TIOCM_DTR, DMBIC);		break;	case TIOCMBIS:		sabtty_mdmctrl(sc, *((int *)data), DMBIS);		break;	case TIOCMBIC:		sabtty_mdmctrl(sc, *((int *)data), DMBIC);		break;	case TIOCMGET:		*((int *)data) = sabtty_mdmctrl(sc, 0, DMGET);		break;	case TIOCMSET:		sabtty_mdmctrl(sc, *((int *)data), DMSET);		break;	case TIOCGFLAGS:		*((int *)data) = sc->sc_openflags;		break;	case TIOCSFLAGS:		if (suser(p->p_ucred, &p->p_acflag))			error = EPERM;		else			sc->sc_openflags = *((int *)data) &			    (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL |			     TIOCFLAG_CRTSCTS | TIOCFLAG_MDMBUF);		break;	default:		error = ENOTTY;	}	return (error);}struct tty *sabttytty(dev)	dev_t dev;{	struct sab_softc *bc = sab_cd.cd_devs[SAB_CARD(dev)];	struct sabtty_softc *sc = bc->sc_child[SAB_PORT(dev)];	return (sc->sc_tty);}