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