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