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