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