Home | History | Annotate | Line # | Download | only in dev
ser.c revision 1.51
      1 /*	$NetBSD: ser.c,v 1.51 2000/04/07 19:47:18 aymeric Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1982, 1986, 1990 The Regents of the University of California.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by the University of
     18  *	California, Berkeley and its contributors.
     19  * 4. Neither the name of the University nor the names of its contributors
     20  *    may be used to endorse or promote products derived from this software
     21  *    without specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     33  * SUCH DAMAGE.
     34  *
     35  *	@(#)ser.c	7.12 (Berkeley) 6/27/91
     36  */
     37 /*
     38  * XXX This file needs major cleanup it will never service more than one
     39  * XXX unit.
     40  */
     41 
     42 #include <sys/param.h>
     43 #include <sys/systm.h>
     44 #include <sys/ioctl.h>
     45 #include <sys/device.h>
     46 #include <sys/tty.h>
     47 #include <sys/proc.h>
     48 #include <sys/file.h>
     49 #include <sys/malloc.h>
     50 #include <sys/uio.h>
     51 #include <sys/kernel.h>
     52 #include <sys/syslog.h>
     53 #include <sys/queue.h>
     54 #include <machine/cpu.h>
     55 #include <amiga/amiga/device.h>
     56 #include <amiga/dev/serreg.h>
     57 #include <amiga/amiga/custom.h>
     58 #include <amiga/amiga/cia.h>
     59 #include <amiga/amiga/cc.h>
     60 
     61 #include <dev/cons.h>
     62 
     63 #include <sys/conf.h>
     64 #include <machine/conf.h>
     65 
     66 #include "ser.h"
     67 #if NSER > 0
     68 
     69 void serattach __P((struct device *, struct device *, void *));
     70 int sermatch __P((struct device *, struct cfdata *, void *));
     71 
     72 struct ser_softc {
     73 	struct device dev;
     74 	struct tty *ser_tty;
     75 };
     76 
     77 struct cfattach ser_ca = {
     78 	sizeof(struct ser_softc), sermatch, serattach
     79 };
     80 
     81 extern struct cfdriver ser_cd;
     82 
     83 #ifndef SEROBUF_SIZE
     84 #define SEROBUF_SIZE 32
     85 #endif
     86 #ifndef SERIBUF_SIZE
     87 #define SERIBUF_SIZE 512
     88 #endif
     89 
     90 #define splser() spl5()
     91 
     92 void	serstart __P((struct tty *));
     93 void	ser_shutdown __P((struct ser_softc *));
     94 int	serparam __P((struct tty *, struct termios *));
     95 void	serintr __P((void));
     96 int	serhwiflow __P((struct tty *, int));
     97 int	sermctl __P((dev_t dev, int, int));
     98 void	ser_fastint __P((void));
     99 void	sereint __P((int));
    100 static	void ser_putchar __P((struct tty *, u_short));
    101 void	ser_outintr __P((void));
    102 void	sercnprobe __P((struct consdev *));
    103 void	sercninit __P((struct consdev *));
    104 void	serinit __P((int));
    105 int	sercngetc __P((dev_t dev));
    106 void	sercnputc __P((dev_t, int));
    107 void	sercnpollc __P((dev_t, int));
    108 
    109 int	nser = NSER;
    110 #ifdef SERCONSOLE
    111 int	serconsole = SERCONSOLE;
    112 #else
    113 int	serconsole = -1;
    114 #endif
    115 int	serconsinit;
    116 int	serdefaultrate = TTYDEF_SPEED;
    117 int	sermajor;
    118 int	serswflags;
    119 
    120 struct	vbl_node ser_vbl_node;
    121 struct	tty ser_cons;
    122 struct	tty *ser_tty;
    123 
    124 static u_short serbuf[SERIBUF_SIZE];
    125 static u_short *sbrpt = serbuf;
    126 static u_short *sbwpt = serbuf;
    127 static u_short sbcnt;
    128 static u_short sbovfl;
    129 static u_char serdcd;
    130 
    131 /*
    132  * Since this UART is not particularly bright (to put it nicely), we'll
    133  * have to do parity stuff on our own.	This table contains the 8th bit
    134  * in 7bit character mode, for even parity.  If you want odd parity,
    135  * flip the bit.  (for generation of the table, see genpar.c)
    136  */
    137 
    138 u_char	even_parity[] = {
    139 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    140 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    141 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    142 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    143 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    144 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    145 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    146 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    147 };
    148 
    149 /*
    150  * Since we don't get interrupts for changes on the modem control line,
    151  * we'll have to fake them by comparing current settings to the settings
    152  * we remembered on last invocation.
    153  */
    154 
    155 u_char	last_ciab_pra;
    156 
    157 extern struct tty *constty;
    158 
    159 extern int ser_open_speed;	/* current speed of open serial device */
    160 
    161 #ifdef KGDB
    162 #include <machine/remote-sl.h>
    163 
    164 extern dev_t kgdb_dev;
    165 extern int kgdb_rate;
    166 extern int kgdb_debug_init;
    167 #endif
    168 
    169 #ifdef DEBUG
    170 long	fifoin[17];
    171 long	fifoout[17];
    172 long	serintrcount[16];
    173 long	sermintcount[16];
    174 #endif
    175 
    176 void	sermint __P((register int unit));
    177 
    178 int
    179 sermatch(pdp, cfp, auxp)
    180 	struct device *pdp;
    181 	struct cfdata *cfp;
    182 	void *auxp;
    183 {
    184 	static int ser_matched = 0;
    185 
    186 	/* Allow only once instance. */
    187 	if (matchname("ser", (char *)auxp) == 0 || ser_matched)
    188 		return(0);
    189 	if (serconsole != 0 && amiga_realconfig == 0)
    190 		return(0);
    191 
    192 	ser_matched = 1;
    193 	return(1);
    194 }
    195 
    196 
    197 void
    198 serattach(pdp, dp, auxp)
    199 	struct device *pdp, *dp;
    200 	void *auxp;
    201 {
    202 	struct ser_softc *sc;
    203 	struct tty *tp;
    204 	u_short ir;
    205 
    206 	sc = (struct ser_softc *)dp;
    207 
    208 	ir = custom.intenar;
    209 	if (serconsole == 0)
    210 		DELAY(100000);
    211 
    212 	ser_vbl_node.function = (void (*) (void *)) sermint;
    213 	add_vbl_function(&ser_vbl_node, SER_VBL_PRIORITY, (void *) 0);
    214 #ifdef KGDB
    215 	if (kgdb_dev == makedev(sermajor, 0)) {
    216 		if (serconsole == 0)
    217 			kgdb_dev = NODEV; /* can't debug over console port */
    218 		else {
    219 			(void) serinit(kgdb_rate);
    220 			serconsinit = 1;       /* don't re-init in serputc */
    221 			if (kgdb_debug_init == 0)
    222 				printf(" kgdb enabled\n");
    223 			else {
    224 				/*
    225 				 * Print prefix of device name,
    226 				 * let kgdb_connect print the rest.
    227 				 */
    228 				printf("ser0: ");
    229 				kgdb_connect(1);
    230 			}
    231 		}
    232 	}
    233 #endif
    234 	/*
    235 	 * Need to reset baud rate, etc. of next print so reset serconsinit.
    236 	 */
    237 	if (0 == serconsole)
    238 		serconsinit = 0;
    239 
    240 	tp = ttymalloc();
    241 	tp->t_oproc = (void (*) (struct tty *)) serstart;
    242 	tp->t_param = serparam;
    243 	tp->t_hwiflow = serhwiflow;
    244 	tty_attach(tp);
    245 	sc->ser_tty = ser_tty = tp;
    246 
    247 	if (dp)
    248 		printf(": input fifo %d output fifo %d\n", SERIBUF_SIZE,
    249 		    SEROBUF_SIZE);
    250 }
    251 
    252 
    253 /* ARGSUSED */
    254 int
    255 seropen(dev, flag, mode, p)
    256 	dev_t dev;
    257 	int flag, mode;
    258 	struct proc *p;
    259 {
    260 	struct ser_softc *sc;
    261 	struct tty *tp;
    262 	int unit, error, s, s2;
    263 
    264 	error = 0;
    265 	unit = SERUNIT(dev);
    266 
    267 	if (unit >= ser_cd.cd_ndevs)
    268 		return (ENXIO);
    269 
    270 	sc = ser_cd.cd_devs[unit];
    271 	if (sc == 0)
    272 		return (ENXIO);
    273 
    274 	/* XXX com.c: insert KGDB check here */
    275 
    276 	/* XXX ser.c had: s = spltty(); */
    277 
    278 	tp = sc->ser_tty;
    279 
    280 	if ((tp->t_state & TS_ISOPEN) &&
    281 	    (tp->t_state & TS_XCLUDE) &&
    282 	    p->p_ucred->cr_uid != 0)
    283 		return (EBUSY);
    284 
    285 	s = spltty();
    286 
    287 	/*
    288 	 * If this is a first open...
    289 	 */
    290 
    291 	if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) {
    292 		struct termios t;
    293 
    294 		tp->t_dev = dev;
    295 
    296 		s2 = splser();
    297 		/*
    298 		 * XXX here: hw enable,
    299 		 */
    300 		last_ciab_pra = ciab.pra;
    301 
    302 		splx(s2);
    303 		t.c_ispeed = 0;
    304 
    305 		/* XXX serconsolerate? */
    306 		t.c_ospeed = TTYDEF_SPEED;
    307 		t.c_cflag = TTYDEF_CFLAG;
    308 
    309 		if (serswflags & TIOCFLAG_CLOCAL)
    310 			t.c_cflag |= CLOCAL;
    311 		if (serswflags & TIOCFLAG_CRTSCTS)
    312 			t.c_cflag |= CRTSCTS;
    313 		if (serswflags & TIOCFLAG_MDMBUF)
    314 			t.c_cflag |= MDMBUF;
    315 
    316 		/* Make sure serparam() will do something. */
    317 		tp->t_ospeed = 0;
    318 		serparam(tp, &t);
    319 		tp->t_iflag = TTYDEF_IFLAG;
    320 		tp->t_oflag = TTYDEF_OFLAG;
    321 		tp->t_lflag = TTYDEF_LFLAG;
    322 		ttychars(tp);
    323 		ttsetwater(tp);
    324 
    325 		s2 = splser();
    326 		(void)sermctl(dev, TIOCM_DTR, DMSET);
    327 		/* clear input ring */
    328 		sbrpt = sbwpt = serbuf;
    329 		sbcnt = 0;
    330 		splx(s2);
    331 	}
    332 
    333 	splx(s);
    334 
    335 	error = ttyopen(tp, DIALOUT(dev), flag & O_NONBLOCK);
    336 	if (error)
    337 		goto bad;
    338 
    339 	error =  (*linesw[tp->t_line].l_open)(dev, tp);
    340 	if (error)
    341 		goto bad;
    342 
    343 	return (0);
    344 
    345 bad:
    346 	if (!(tp->t_state & TS_ISOPEN) && tp->t_wopen == 0) {
    347 		ser_shutdown(sc);
    348 	}
    349 
    350 	return (error);
    351 }
    352 
    353 /*ARGSUSED*/
    354 int
    355 serclose(dev, flag, mode, p)
    356 	dev_t dev;
    357 	int flag, mode;
    358 	struct proc *p;
    359 {
    360 	struct ser_softc *sc;
    361 	struct tty *tp;
    362 
    363 	sc = ser_cd.cd_devs[0];
    364 	tp = ser_tty;
    365 
    366 	/* XXX This is for cons.c, according to com.c */
    367 	if (!(tp->t_state & TS_ISOPEN))
    368 		return (0);
    369 
    370 	(*linesw[tp->t_line].l_close)(tp, flag);
    371 	ttyclose(tp);
    372 
    373 	if (!(tp->t_state & TS_ISOPEN) && tp->t_wopen == 0) {
    374 		ser_shutdown(sc);
    375 	}
    376 	return (0);
    377 }
    378 
    379 void
    380 ser_shutdown(sc)
    381 	struct ser_softc *sc;
    382 {
    383 	struct tty *tp = sc->ser_tty;
    384 	int s;
    385 
    386 	s = splser();
    387 
    388 	custom.adkcon = ADKCONF_UARTBRK;	/* clear break */
    389 #if 0 /* XXX fix: #ifdef KGDB */
    390 	/*
    391 	 * do not disable interrupts if debugging
    392 	 */
    393 	if (dev != kgdb_dev)
    394 #endif
    395 		custom.intena = INTF_RBF | INTF_TBE;	/* disable interrupts */
    396 	custom.intreq = INTF_RBF | INTF_TBE;		/* clear intr request */
    397 
    398 	/*
    399 	 * If HUPCL is not set, leave DTR unchanged.
    400 	 */
    401 	if (tp->t_cflag & HUPCL) {
    402 		(void)sermctl(tp->t_dev, TIOCM_DTR, DMBIC);
    403 		/*
    404 		 * Idea from dev/ic/com.c:
    405 		 * sleep a bit so that other side will notice, even if we
    406 		 * reopen immediately.
    407 		 */
    408 		(void) tsleep(tp, TTIPRI, ttclos, hz);
    409 	}
    410 
    411 #if not_yet
    412 	if (tp != &ser_cons) {
    413 		remove_vbl_function(&ser_vbl_node);
    414 		ttyfree(tp);
    415 		ser_tty = (struct tty *) NULL;
    416 	}
    417 #endif
    418 	ser_open_speed = tp->t_ispeed;
    419 	return;
    420 }
    421 
    422 int
    423 serread(dev, uio, flag)
    424 	dev_t dev;
    425 	struct uio *uio;
    426 	int flag;
    427 {
    428 	/* ARGSUSED */
    429 
    430 	return((*linesw[ser_tty->t_line].l_read)(ser_tty, uio, flag));
    431 }
    432 
    433 int
    434 serwrite(dev, uio, flag)
    435 	dev_t dev;
    436 	struct uio *uio;
    437 	int flag;
    438 {
    439 	/* ARGSUSED */
    440 
    441 	return((*linesw[ser_tty->t_line].l_write)(ser_tty, uio, flag));
    442 }
    443 
    444 struct tty *
    445 sertty(dev)
    446 	dev_t dev;
    447 {
    448 	/* ARGSUSED */
    449 
    450 	return (ser_tty);
    451 }
    452 
    453 /*
    454  * We don't do any processing of data here, so we store the raw code
    455  * obtained from the uart register.  In theory, 110kBaud gives you
    456  * 11kcps, so 16k buffer should be more than enough, interrupt
    457  * latency of 1s should never happen, or something is seriously
    458  * wrong..
    459  * buffers moved to above seropen()	-is
    460  */
    461 
    462 /*
    463  * This is a replacement for the lack of a hardware fifo.  32k should be
    464  * enough (there's only one unit anyway, so this is not going to
    465  * accumulate).
    466  */
    467 void
    468 ser_fastint()
    469 {
    470 	/*
    471 	 * We're at RBE-level, which is higher than VBL-level which is used
    472 	 * to periodically transmit contents of this buffer up one layer,
    473 	 * so no spl-raising is necessary.
    474 	 */
    475 	u_short code;
    476 
    477 	/*
    478 	 * This register contains both data and status bits!
    479 	 */
    480 	code = custom.serdatr;
    481 
    482 	/*
    483 	 * Use SERDATF_RBF instead of INTF_RBF; they're equivalent, but
    484 	 * we save one (slow) custom chip access.
    485 	 */
    486 	if ((code & SERDATRF_RBF) == 0)
    487 		return;
    488 
    489 	/*
    490 	 * clear interrupt
    491 	 */
    492 	custom.intreq = INTF_RBF;
    493 
    494 	/*
    495 	 * check for buffer overflow.
    496 	 */
    497 	if (sbcnt == SERIBUF_SIZE) {
    498 		++sbovfl;
    499 		return;
    500 	}
    501 	/*
    502 	 * store in buffer
    503 	 */
    504 	*sbwpt++ = code;
    505 	if (sbwpt == serbuf + SERIBUF_SIZE)
    506 		sbwpt = serbuf;
    507 	++sbcnt;
    508 	if (sbcnt > SERIBUF_SIZE - 20)
    509 		CLRRTS(ciab.pra);	/* drop RTS if buffer almost full */
    510 }
    511 
    512 
    513 void
    514 serintr()
    515 {
    516 	int s1, s2, ovfl;
    517 	struct tty *tp = ser_tty;
    518 
    519 	/*
    520 	 * Make sure we're not interrupted by another
    521 	 * vbl, but allow level5 ints
    522 	 */
    523 	s1 = spltty();
    524 
    525 	/*
    526 	 * pass along any acumulated information
    527 	 */
    528 	while (sbcnt > 0 && (tp->t_state & TS_TBLOCK) == 0) {
    529 		/*
    530 		 * no collision with ser_fastint()
    531 		 */
    532 		sereint(*sbrpt++);
    533 
    534 		ovfl = 0;
    535 		/* lock against ser_fastint() */
    536 		s2 = splser();
    537 		sbcnt--;
    538 		if (sbrpt == serbuf + SERIBUF_SIZE)
    539 			sbrpt = serbuf;
    540 		if (sbovfl != 0) {
    541 			ovfl = sbovfl;
    542 			sbovfl = 0;
    543 		}
    544 		splx(s2);
    545 		if (ovfl != 0)
    546 			log(LOG_WARNING, "ser0: %d ring buffer overflows.\n",
    547 			    ovfl);
    548 	}
    549 	s2 = splser();
    550 	if (sbcnt == 0 && (tp->t_state & TS_TBLOCK) == 0)
    551 		SETRTS(ciab.pra);	/* start accepting data again */
    552 	splx(s2);
    553 	splx(s1);
    554 }
    555 
    556 void
    557 sereint(stat)
    558 	int stat;
    559 {
    560 	struct tty *tp;
    561 	u_char ch;
    562 	int c;
    563 
    564 	tp = ser_tty;
    565 	ch = stat & 0xff;
    566 	c = ch;
    567 
    568 	if ((tp->t_state & TS_ISOPEN) == 0) {
    569 #ifdef KGDB
    570 		/* we don't care about parity errors */
    571 		if (kgdb_dev == makedev(sermajor, 0) && c == FRAME_END)
    572 			kgdb_connect(0);	/* trap into kgdb */
    573 #endif
    574 		return;
    575 	}
    576 
    577 	/*
    578 	 * Check for break and (if enabled) parity error.
    579 	 */
    580 	if ((stat & 0x1ff) == 0)
    581 		c |= TTY_FE;
    582 	else if ((tp->t_cflag & PARENB) &&
    583 		    (((ch >> 7) + even_parity[ch & 0x7f]
    584 		    + !!(tp->t_cflag & PARODD)) & 1))
    585 			c |= TTY_PE;
    586 
    587 	if (stat & SERDATRF_OVRUN)
    588 		log(LOG_WARNING, "ser0: silo overflow\n");
    589 
    590 	(*linesw[tp->t_line].l_rint)(c, tp);
    591 }
    592 
    593 /*
    594  * This interrupt is periodically invoked in the vertical blank
    595  * interrupt.  It's used to keep track of the modem control lines
    596  * and (new with the fast_int code) to move accumulated data
    597  * up into the tty layer.
    598  */
    599 void
    600 sermint(unit)
    601 	int unit;
    602 {
    603 	struct tty *tp;
    604 	u_char stat, last, istat;
    605 
    606 	tp = ser_tty;
    607 	if (!tp)
    608 		return;
    609 
    610 	/*
    611 	if ((tp->t_state & TS_ISOPEN) == 0 || tp->t_wopen == 0) {
    612 		sbrpt = sbwpt = serbuf;
    613 		return;
    614 	}
    615 	*/
    616 	/*
    617 	 * empty buffer
    618 	 */
    619 	serintr();
    620 
    621 	stat = ciab.pra;
    622 	last = last_ciab_pra;
    623 	last_ciab_pra = stat;
    624 
    625 	/*
    626 	 * check whether any interesting signal changed state
    627 	 */
    628 	istat = stat ^ last;
    629 
    630 	if (istat & serdcd) {
    631 		(*linesw[tp->t_line].l_modem)(tp, ISDCD(stat));
    632 	}
    633 
    634 	if ((istat & CIAB_PRA_CTS) && (tp->t_state & TS_ISOPEN) &&
    635 	    (tp->t_cflag & CRTSCTS)) {
    636 #if 0
    637 		/* the line is up and we want to do rts/cts flow control */
    638 		if (ISCTS(stat)) {
    639 			tp->t_state &= ~TS_TTSTOP;
    640 			ttstart(tp);
    641 			/* cause tbe-int if we were stuck there */
    642 			custom.intreq = INTF_SETCLR | INTF_TBE;
    643 		} else
    644 			tp->t_state |= TS_TTSTOP;
    645 #else
    646 		/* do this on hardware level, not with tty driver */
    647 		if (ISCTS(stat)) {
    648 			tp->t_state &= ~TS_TTSTOP;
    649 			/* cause TBE interrupt */
    650 			custom.intreq = INTF_SETCLR | INTF_TBE;
    651 		}
    652 #endif
    653 	}
    654 }
    655 
    656 int
    657 serioctl(dev, cmd, data, flag, p)
    658 	dev_t dev;
    659 	u_long cmd;
    660 	caddr_t data;
    661 	int flag;
    662 	struct proc *p;
    663 {
    664 	register struct tty *tp;
    665 	register int error;
    666 
    667 	tp = ser_tty;
    668 	if (!tp)
    669 		return ENXIO;
    670 
    671 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
    672 	if (error >= 0)
    673 		return(error);
    674 
    675 	error = ttioctl(tp, cmd, data, flag, p);
    676 	if (error >= 0)
    677 		return(error);
    678 
    679 	switch (cmd) {
    680 	case TIOCSBRK:
    681 		custom.adkcon = ADKCONF_SETCLR | ADKCONF_UARTBRK;
    682 		break;
    683 
    684 	case TIOCCBRK:
    685 		custom.adkcon = ADKCONF_UARTBRK;
    686 		break;
    687 
    688 	case TIOCSDTR:
    689 		(void) sermctl(dev, TIOCM_DTR, DMBIS);
    690 		break;
    691 
    692 	case TIOCCDTR:
    693 		(void) sermctl(dev, TIOCM_DTR, DMBIC);
    694 		break;
    695 
    696 	case TIOCMSET:
    697 		(void) sermctl(dev, *(int *) data, DMSET);
    698 		break;
    699 
    700 	case TIOCMBIS:
    701 		(void) sermctl(dev, *(int *) data, DMBIS);
    702 		break;
    703 
    704 	case TIOCMBIC:
    705 		(void) sermctl(dev, *(int *) data, DMBIC);
    706 		break;
    707 
    708 	case TIOCMGET:
    709 		*(int *)data = sermctl(dev, 0, DMGET);
    710 		break;
    711 	case TIOCGFLAGS:
    712 		*(int *)data = serswflags;
    713 		break;
    714 	case TIOCSFLAGS:
    715 		error = suser(p->p_ucred, &p->p_acflag);
    716 		if (error != 0)
    717 			return(EPERM);
    718 
    719 		serswflags = *(int *)data;
    720                 serswflags &= /* only allow valid flags */
    721                   (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS);
    722 		break;
    723 	default:
    724 		return(ENOTTY);
    725 	}
    726 
    727 	return(0);
    728 }
    729 
    730 int
    731 serparam(tp, t)
    732 	struct tty *tp;
    733 	struct termios *t;
    734 {
    735 	int cflag, ospeed = 0;
    736 
    737 	if (t->c_ospeed > 0) {
    738 		if (t->c_ospeed < 110)
    739 			return(EINVAL);
    740 		ospeed = SERBRD(t->c_ospeed);
    741 	}
    742 
    743 	if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
    744 		return(EINVAL);
    745 
    746 	/* XXX missing here: console test */
    747 	if (serswflags & TIOCFLAG_SOFTCAR) {
    748 		t->c_cflag = (t->c_cflag & ~HUPCL) | CLOCAL;
    749 	}
    750 
    751 	/* if no changes, dont do anything. com.c explains why. */
    752 	if (tp->t_ospeed == t->c_ospeed &&
    753 	    tp->t_cflag == t->c_cflag)
    754 		return (0);
    755 
    756 	cflag = t->c_cflag;
    757 
    758 	if (cflag & (CLOCAL | MDMBUF))
    759 		serdcd = 0;
    760 	else
    761 		serdcd = CIAB_PRA_CD;
    762 
    763 	/* TODO: support multiple flow control protocols like com.c */
    764 
    765 	/*
    766 	 * copy to tty
    767 	 */
    768 	tp->t_ispeed = t->c_ispeed;
    769 	tp->t_ospeed = t->c_ospeed;
    770 	tp->t_cflag = cflag;
    771 	ser_open_speed = tp->t_ispeed;
    772 
    773 	/*
    774 	 * enable interrupts
    775 	 */
    776 	custom.intena = INTF_SETCLR | INTF_RBF | INTF_TBE;
    777 	last_ciab_pra = ciab.pra;
    778 
    779 	if (t->c_ospeed == 0)
    780 		(void)sermctl(tp->t_dev, 0, DMSET);	/* hang up line */
    781 	else {
    782 		/*
    783 		 * (re)enable DTR
    784 		 * and set baud rate. (8 bit mode)
    785 		 */
    786 		(void)sermctl(tp->t_dev, TIOCM_DTR, DMSET);
    787 		custom.serper = (0 << 15) | ospeed;
    788 	}
    789 	(void)(*linesw[tp->t_line].l_modem)(tp, ISDCD(last_ciab_pra));
    790 
    791 	return(0);
    792 }
    793 
    794 int serhwiflow(tp, flag)
    795         struct tty *tp;
    796         int flag;
    797 {
    798 #if 0
    799 	printf ("serhwiflow %d\n", flag);
    800 #endif
    801         if (flag)
    802 		CLRRTS(ciab.pra);
    803 	else
    804 	        SETRTS(ciab.pra);
    805         return 1;
    806 }
    807 
    808 static void
    809 ser_putchar(tp, c)
    810 	struct tty *tp;
    811 	u_short c;
    812 {
    813 	if ((tp->t_cflag & CSIZE) == CS7 || (tp->t_cflag & PARENB))
    814 		c &= 0x7f;
    815 
    816 	/*
    817 	 * handle parity if necessary
    818 	 */
    819 	if (tp->t_cflag & PARENB) {
    820 		if (even_parity[c])
    821 			c |= 0x80;
    822 		if (tp->t_cflag & PARODD)
    823 			c ^= 0x80;
    824 	}
    825 	/*
    826 	 * add stop bit(s)
    827 	 */
    828 	if (tp->t_cflag & CSTOPB)
    829 		c |= 0x300;
    830 	else
    831 		c |= 0x100;
    832 
    833 	custom.serdat = c;
    834 }
    835 
    836 
    837 static u_char ser_outbuf[SEROBUF_SIZE];
    838 static u_char *sob_ptr = ser_outbuf, *sob_end = ser_outbuf;
    839 
    840 void
    841 ser_outintr()
    842 {
    843 	struct tty *tp;
    844 	int s;
    845 
    846 	tp = ser_tty;
    847 	s = spltty();
    848 
    849 	if (tp == 0)
    850 		goto out;
    851 
    852 	if ((custom.intreqr & INTF_TBE) == 0)
    853 		goto out;
    854 
    855 	/*
    856 	 * clear interrupt
    857 	 */
    858 	custom.intreq = INTF_TBE;
    859 
    860 	if (sob_ptr == sob_end) {
    861 		tp->t_state &= ~(TS_BUSY | TS_FLUSH);
    862 		if (tp->t_line)
    863 			(*linesw[tp->t_line].l_start)(tp);
    864 		else
    865 			serstart(tp);
    866 		goto out;
    867 	}
    868 
    869 	/*
    870 	 * Do hardware flow control here.  if the CTS line goes down, don't
    871 	 * transmit anything.  That way, we'll be restarted by the periodic
    872 	 * interrupt when CTS comes back up.
    873 	 */
    874 	if (ISCTS(ciab.pra))
    875 		ser_putchar(tp, *sob_ptr++);
    876 	else
    877 		CLRCTS(last_ciab_pra);	/* Remember that CTS is off */
    878 out:
    879 	splx(s);
    880 }
    881 
    882 void
    883 serstart(tp)
    884 	struct tty *tp;
    885 {
    886 	int cc, s, hiwat;
    887 #ifdef DIAGNOSTIC
    888 	int unit;
    889 #endif
    890 
    891 	hiwat = 0;
    892 
    893 	if ((tp->t_state & TS_ISOPEN) == 0)
    894 		return;
    895 
    896 #ifdef DIAGNOSTIC
    897 	unit = SERUNIT(tp->t_dev);
    898 	if (unit)
    899 		panic("serstart: unit is %d\n", unit);
    900 #endif
    901 
    902 	s = spltty();
    903 	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
    904 		goto out;
    905 
    906 	cc = tp->t_outq.c_cc;
    907 	if (cc <= tp->t_lowat) {
    908 		if (tp->t_state & TS_ASLEEP) {
    909 			tp->t_state &= ~TS_ASLEEP;
    910 			wakeup((caddr_t) & tp->t_outq);
    911 		}
    912 		selwakeup(&tp->t_wsel);
    913 	}
    914 	if (cc == 0 || (tp->t_state & TS_BUSY))
    915 		goto out;
    916 
    917 	/*
    918 	 * We only do bulk transfers if using CTSRTS flow control, not for
    919 	 * (probably sloooow) ixon/ixoff devices.
    920 	 */
    921 	if ((tp->t_cflag & CRTSCTS) == 0)
    922 		cc = 1;
    923 
    924 	/*
    925 	 * Limit the amount of output we do in one burst
    926 	 * to prevent hogging the CPU.
    927 	 */
    928 	if (cc > SEROBUF_SIZE) {
    929 		hiwat++;
    930 		cc = SEROBUF_SIZE;
    931 	}
    932 	cc = q_to_b(&tp->t_outq, ser_outbuf, cc);
    933 	if (cc > 0) {
    934 		tp->t_state |= TS_BUSY;
    935 
    936 		sob_ptr = ser_outbuf;
    937 		sob_end = ser_outbuf + cc;
    938 
    939 		/*
    940 		 * Get first character out, then have TBE-interrupts blow out
    941 		 * further characters, until buffer is empty, and TS_BUSY gets
    942 		 * cleared.
    943 		 */
    944 		ser_putchar(tp, *sob_ptr++);
    945 	}
    946 out:
    947 	splx(s);
    948 }
    949 
    950 /*
    951  * Stop output on a line.
    952  */
    953 /*ARGSUSED*/
    954 void
    955 serstop(tp, flag)
    956 	struct tty *tp;
    957 	int flag;
    958 {
    959 	int s;
    960 
    961 	s = spltty();
    962 	if (tp->t_state & TS_BUSY) {
    963 		if ((tp->t_state & TS_TTSTOP) == 0)
    964 			tp->t_state |= TS_FLUSH;
    965 	}
    966 	splx(s);
    967 }
    968 
    969 int
    970 sermctl(dev, bits, how)
    971 	dev_t dev;
    972 	int bits, how;
    973 {
    974 	int s;
    975 	u_char ub = 0;
    976 
    977 	/*
    978 	 * convert TIOCM* mask into CIA mask
    979 	 * which is active low
    980 	 */
    981 	if (how != DMGET) {
    982 		ub = 0;
    983 		if (bits & TIOCM_DTR)
    984 			ub |= CIAB_PRA_DTR;
    985 		if (bits & TIOCM_RTS)
    986 			ub |= CIAB_PRA_RTS;
    987 		if (bits & TIOCM_CTS)
    988 			ub |= CIAB_PRA_CTS;
    989 		if (bits & TIOCM_CD)
    990 			ub |= CIAB_PRA_CD;
    991 		if (bits & TIOCM_RI)
    992 			ub |= CIAB_PRA_SEL;	/* collision with /dev/par ! */
    993 		if (bits & TIOCM_DSR)
    994 			ub |= CIAB_PRA_DSR;
    995 	}
    996 	s = spltty();
    997 	switch (how) {
    998 	case DMSET:
    999 		/* invert and set */
   1000 		ciab.pra = ~ub;
   1001 		break;
   1002 
   1003 	case DMBIC:
   1004 		ciab.pra |= ub;
   1005 		ub = ~ciab.pra;
   1006 		break;
   1007 
   1008 	case DMBIS:
   1009 		ciab.pra &= ~ub;
   1010 		ub = ~ciab.pra;
   1011 		break;
   1012 
   1013 	case DMGET:
   1014 		ub = ~ciab.pra;
   1015 		break;
   1016 	}
   1017 	(void)splx(s);
   1018 
   1019 	bits = 0;
   1020 	if (ub & CIAB_PRA_DTR)
   1021 		bits |= TIOCM_DTR;
   1022 	if (ub & CIAB_PRA_RTS)
   1023 		bits |= TIOCM_RTS;
   1024 	if (ub & CIAB_PRA_CTS)
   1025 		bits |= TIOCM_CTS;
   1026 	if (ub & CIAB_PRA_CD)
   1027 		bits |= TIOCM_CD;
   1028 	if (ub & CIAB_PRA_SEL)
   1029 		bits |= TIOCM_RI;
   1030 	if (ub & CIAB_PRA_DSR)
   1031 		bits |= TIOCM_DSR;
   1032 
   1033 	return(bits);
   1034 }
   1035 
   1036 /*
   1037  * Following are all routines needed for SER to act as console
   1038  */
   1039 void
   1040 sercnprobe(cp)
   1041 	struct consdev *cp;
   1042 {
   1043 	int unit;
   1044 
   1045 	/* locate the major number */
   1046 	for (sermajor = 0; sermajor < nchrdev; sermajor++)
   1047 		if (cdevsw[sermajor].d_open == (void *)seropen)
   1048 			break;
   1049 
   1050 
   1051 	unit = CONUNIT;			/* XXX: ick */
   1052 
   1053 	/*
   1054 	 * initialize required fields
   1055 	 */
   1056 	cp->cn_dev = makedev(sermajor, unit);
   1057 	if (serconsole == unit)
   1058 		cp->cn_pri = CN_REMOTE;
   1059 	else
   1060 		cp->cn_pri = CN_NORMAL;
   1061 #ifdef KGDB
   1062 	if (major(kgdb_dev) == 1)	/* XXX */
   1063 		kgdb_dev = makedev(sermajor, minor(kgdb_dev));
   1064 #endif
   1065 }
   1066 
   1067 void
   1068 sercninit(cp)
   1069 	struct consdev *cp;
   1070 {
   1071 	int unit;
   1072 
   1073 	unit = SERUNIT(cp->cn_dev);
   1074 
   1075 	serinit(serdefaultrate);
   1076 	serconsole = unit;
   1077 	serconsinit = 1;
   1078 }
   1079 
   1080 void
   1081 serinit(rate)
   1082 	int rate;
   1083 {
   1084 	int s;
   1085 
   1086 	s = splser();
   1087 	/*
   1088 	 * might want to fiddle with the CIA later ???
   1089 	 */
   1090 	custom.serper = (rate>=110 ? SERBRD(rate) : 0);
   1091 	splx(s);
   1092 }
   1093 
   1094 int
   1095 sercngetc(dev)
   1096 	dev_t dev;
   1097 {
   1098 	u_short stat;
   1099 	int c, s;
   1100 
   1101 	s = splser();
   1102 	/*
   1103 	 * poll
   1104 	 */
   1105 	while (((stat = custom.serdatr & 0xffff) & SERDATRF_RBF) == 0)
   1106 		;
   1107 	c = stat & 0xff;
   1108 	/*
   1109 	 * clear interrupt
   1110 	 */
   1111 	custom.intreq = INTF_RBF;
   1112 	splx(s);
   1113 	return(c);
   1114 }
   1115 
   1116 /*
   1117  * Console kernel output character routine.
   1118  */
   1119 void
   1120 sercnputc(dev, c)
   1121 	dev_t dev;
   1122 	int c;
   1123 {
   1124 	register int timo;
   1125 	int s;
   1126 
   1127 	s = splhigh();
   1128 
   1129 	if (serconsinit == 0) {
   1130 		(void)serinit(serdefaultrate);
   1131 		serconsinit = 1;
   1132 	}
   1133 
   1134 	/*
   1135 	 * wait for any pending transmission to finish
   1136 	 */
   1137 	timo = 50000;
   1138 	while (!(custom.serdatr & SERDATRF_TBE) && --timo);
   1139 
   1140 	/*
   1141 	 * transmit char.
   1142 	 */
   1143 	custom.serdat = (c & 0xff) | 0x100;
   1144 
   1145 	/*
   1146 	 * wait for this transmission to complete
   1147 	 */
   1148 	timo = 1500000;
   1149 	while (!(custom.serdatr & SERDATRF_TBE) && --timo)
   1150 		;
   1151 
   1152 	/*
   1153 	 * Wait for the device (my vt100..) to process the data, since we
   1154 	 * don't do flow-control with cnputc
   1155 	 */
   1156 	for (timo = 0; timo < 30000; timo++)
   1157 		;
   1158 
   1159 	/*
   1160 	 * We set TBE so that ser_outintr() is called right after to check
   1161 	 * whether there still are chars to process.
   1162 	 * We used to clear this, but it hung the tty output if the kernel
   1163 	 * output a char while userland did on the same serial port.
   1164 	 */
   1165 	custom.intreq = INTF_SETCLR | INTF_TBE;
   1166 	splx(s);
   1167 }
   1168 
   1169 void
   1170 sercnpollc(dev, on)
   1171 	dev_t dev;
   1172 	int on;
   1173 {
   1174 }
   1175 #endif
   1176