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