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