Home | History | Annotate | Line # | Download | only in dev
ser.c revision 1.81
      1 /*	$NetBSD: ser.c,v 1.81 2014/01/22 00:25:16 christos 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.81 2014/01/22 00:25:16 christos 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 	seropen, serclose, serread, serwrite, serioctl,
     93 	serstop, sertty, serpoll, nommap, ttykqfilter, D_TTY
     94 };
     95 
     96 #ifndef SEROBUF_SIZE
     97 #define SEROBUF_SIZE 32
     98 #endif
     99 #ifndef SERIBUF_SIZE
    100 #define SERIBUF_SIZE 512
    101 #endif
    102 
    103 #define splser() spl5()
    104 
    105 void	serstart(struct tty *);
    106 void	ser_shutdown(struct ser_softc *);
    107 int	serparam(struct tty *, struct termios *);
    108 void	serintr(void);
    109 int	serhwiflow(struct tty *, int);
    110 int	sermctl(dev_t dev, int, int);
    111 void	ser_fastint(void);
    112 void	sereint(int);
    113 static	void ser_putchar(struct tty *, u_short);
    114 void	ser_outintr(void);
    115 void	sercnprobe(struct consdev *);
    116 void	sercninit(struct consdev *);
    117 void	serinit(int);
    118 int	sercngetc(dev_t dev);
    119 void	sercnputc(dev_t, int);
    120 void	sercnpollc(dev_t, int);
    121 
    122 int	nser = NSER;
    123 #ifdef SERCONSOLE
    124 int	serconsole = 0;
    125 #else
    126 int	serconsole = -1;
    127 #endif
    128 int	serconsinit;
    129 int	serdefaultrate = TTYDEF_SPEED;
    130 int	serswflags;
    131 
    132 struct	vbl_node ser_vbl_node;
    133 struct	tty ser_cons;
    134 struct	tty *ser_tty;
    135 
    136 static u_short serbuf[SERIBUF_SIZE];
    137 static u_short *sbrpt = serbuf;
    138 static u_short *sbwpt = serbuf;
    139 static u_short sbcnt;
    140 static u_short sbovfl;
    141 static u_char serdcd;
    142 
    143 /*
    144  * Since this UART is not particularly bright (to put it nicely), we'll
    145  * have to do parity stuff on our own.	This table contains the 8th bit
    146  * in 7bit character mode, for even parity.  If you want odd parity,
    147  * flip the bit.  (for generation of the table, see genpar.c)
    148  */
    149 
    150 u_char	even_parity[] = {
    151 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    152 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    153 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    154 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    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 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    158 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    159 };
    160 
    161 /*
    162  * Since we don't get interrupts for changes on the modem control line,
    163  * we'll have to fake them by comparing current settings to the settings
    164  * we remembered on last invocation.
    165  */
    166 
    167 u_char	last_ciab_pra;
    168 
    169 extern struct tty *constty;
    170 
    171 extern int ser_open_speed;	/* current speed of open serial device */
    172 
    173 #ifdef KGDB
    174 #include <machine/remote-sl.h>
    175 
    176 extern dev_t kgdb_dev;
    177 extern int kgdb_rate;
    178 extern int kgdb_debug_init;
    179 #endif
    180 
    181 #ifdef DEBUG
    182 long	fifoin[17];
    183 long	fifoout[17];
    184 long	serintrcount[16];
    185 long	sermintcount[16];
    186 #endif
    187 
    188 void	sermint(register int unit);
    189 
    190 int
    191 sermatch(device_t parent, cfdata_t cf, void *aux)
    192 {
    193 	static int ser_matched = 0;
    194 	static int ser_matched_real = 0;
    195 
    196 	/* Allow only once instance. */
    197 	if (matchname("ser", (char *)aux) == 0)
    198 		return(0);
    199 
    200 	if (amiga_realconfig) {
    201 		if (ser_matched_real)
    202 			return(0);
    203 		ser_matched_real = 1;
    204 	} else {
    205 		if (serconsole != 0)
    206 			return(0);
    207 
    208 		if (ser_matched != 0)
    209 			return(0);
    210 
    211 		ser_matched = 1;
    212 	}
    213 	return(1);
    214 }
    215 
    216 
    217 void
    218 serattach(device_t parent, device_t self, void *aux)
    219 {
    220 	struct ser_softc *sc;
    221 	struct tty *tp;
    222 	u_short ir;
    223 
    224 	sc = device_private(self);
    225 
    226 	ir = custom.intenar;
    227 	__USE(ir);
    228 	if (serconsole == 0)
    229 		DELAY(100000);
    230 
    231 	ser_vbl_node.function = (void (*) (void *)) sermint;
    232 	add_vbl_function(&ser_vbl_node, SER_VBL_PRIORITY, (void *) 0);
    233 #ifdef KGDB
    234 	if (kgdb_dev == makedev(cdevsw_lookup_major(&ser_cdevsw), 0)) {
    235 		if (serconsole == 0)
    236 			kgdb_dev = NODEV; /* can't debug over console port */
    237 		else {
    238 			(void) serinit(kgdb_rate);
    239 			serconsinit = 1;       /* don't re-init in serputc */
    240 			if (kgdb_debug_init == 0)
    241 				printf(" kgdb enabled\n");
    242 			else {
    243 				/*
    244 				 * Print prefix of device name,
    245 				 * let kgdb_connect print the rest.
    246 				 */
    247 				printf("ser0: ");
    248 				kgdb_connect(1);
    249 			}
    250 		}
    251 	}
    252 #endif
    253 	/*
    254 	 * Need to reset baud rate, etc. of next print so reset serconsinit.
    255 	 */
    256 	if (0 == serconsole)
    257 		serconsinit = 0;
    258 
    259 	tp = tty_alloc();
    260 	tp->t_oproc = (void (*) (struct tty *)) serstart;
    261 	tp->t_param = serparam;
    262 	tp->t_hwiflow = serhwiflow;
    263 	tty_attach(tp);
    264 	sc->ser_tty = ser_tty = tp;
    265 
    266 	if (self)
    267 		printf(": input fifo %d output fifo %d\n", SERIBUF_SIZE,
    268 		    SEROBUF_SIZE);
    269 }
    270 
    271 
    272 /* ARGSUSED */
    273 int
    274 seropen(dev_t dev, int flag, int mode, struct lwp *l)
    275 {
    276 	struct ser_softc *sc;
    277 	struct tty *tp;
    278 	int unit, error, s, s2;
    279 
    280 	error = 0;
    281 	unit = SERUNIT(dev);
    282 
    283 	sc = device_lookup_private(&ser_cd, unit);
    284 	if (sc == NULL)
    285 		return (ENXIO);
    286 
    287 	/* XXX com.c: insert KGDB check here */
    288 
    289 	/* XXX ser.c had: s = spltty(); */
    290 
    291 	tp = sc->ser_tty;
    292 
    293 	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
    294 		return (EBUSY);
    295 
    296 	s = spltty();
    297 
    298 	/*
    299 	 * If this is a first open...
    300 	 */
    301 
    302 	if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) {
    303 		struct termios t;
    304 
    305 		tp->t_dev = dev;
    306 
    307 		s2 = splser();
    308 		/*
    309 		 * XXX here: hw enable,
    310 		 */
    311 		last_ciab_pra = ciab.pra;
    312 
    313 		splx(s2);
    314 		t.c_ispeed = 0;
    315 
    316 		/* XXX serconsolerate? */
    317 		t.c_ospeed = TTYDEF_SPEED;
    318 		t.c_cflag = TTYDEF_CFLAG;
    319 
    320 		if (serswflags & TIOCFLAG_CLOCAL)
    321 			t.c_cflag |= CLOCAL;
    322 		if (serswflags & TIOCFLAG_CRTSCTS)
    323 			t.c_cflag |= CRTSCTS;
    324 		if (serswflags & TIOCFLAG_MDMBUF)
    325 			t.c_cflag |= MDMBUF;
    326 
    327 		/* Make sure serparam() will do something. */
    328 		tp->t_ospeed = 0;
    329 		serparam(tp, &t);
    330 		tp->t_iflag = TTYDEF_IFLAG;
    331 		tp->t_oflag = TTYDEF_OFLAG;
    332 		tp->t_lflag = TTYDEF_LFLAG;
    333 		ttychars(tp);
    334 		ttsetwater(tp);
    335 
    336 		s2 = splser();
    337 		(void)sermctl(dev, TIOCM_DTR, DMSET);
    338 		/* clear input ring */
    339 		sbrpt = sbwpt = serbuf;
    340 		sbcnt = 0;
    341 		splx(s2);
    342 	}
    343 
    344 	splx(s);
    345 
    346 	error = ttyopen(tp, DIALOUT(dev), flag & O_NONBLOCK);
    347 	if (error)
    348 		goto bad;
    349 
    350 	error =  tp->t_linesw->l_open(dev, tp);
    351 	if (error)
    352 		goto bad;
    353 
    354 	return (0);
    355 
    356 bad:
    357 	if (!(tp->t_state & TS_ISOPEN) && tp->t_wopen == 0) {
    358 		ser_shutdown(sc);
    359 	}
    360 
    361 	return (error);
    362 }
    363 
    364 /*ARGSUSED*/
    365 int
    366 serclose(dev_t dev, int flag, int mode, struct lwp *l)
    367 {
    368 	struct ser_softc *sc;
    369 	struct tty *tp;
    370 
    371 	sc = device_lookup_private(&ser_cd, SERUNIT(dev));
    372 	tp = ser_tty;
    373 
    374 	/* XXX This is for cons.c, according to com.c */
    375 	if (!(tp->t_state & TS_ISOPEN))
    376 		return (0);
    377 
    378 	tp->t_linesw->l_close(tp, flag);
    379 	ttyclose(tp);
    380 
    381 	if (!(tp->t_state & TS_ISOPEN) && tp->t_wopen == 0) {
    382 		ser_shutdown(sc);
    383 	}
    384 	return (0);
    385 }
    386 
    387 void
    388 ser_shutdown(struct ser_softc *sc)
    389 {
    390 	struct tty *tp = sc->ser_tty;
    391 	int s;
    392 
    393 	s = splser();
    394 
    395 	custom.adkcon = ADKCONF_UARTBRK;	/* clear break */
    396 #if 0 /* XXX fix: #ifdef KGDB */
    397 	/*
    398 	 * do not disable interrupts if debugging
    399 	 */
    400 	if (dev != kgdb_dev)
    401 #endif
    402 		custom.intena = INTF_RBF | INTF_TBE;	/* disable interrupts */
    403 	custom.intreq = INTF_RBF | INTF_TBE;		/* clear intr request */
    404 
    405 	/*
    406 	 * If HUPCL is not set, leave DTR unchanged.
    407 	 */
    408 	if (tp->t_cflag & HUPCL) {
    409 		(void)sermctl(tp->t_dev, TIOCM_DTR, DMBIC);
    410 		/*
    411 		 * Idea from dev/ic/com.c:
    412 		 * sleep a bit so that other side will notice, even if we
    413 		 * reopen immediately.
    414 		 */
    415 		(void) tsleep(tp, TTIPRI, ttclos, hz);
    416 	}
    417 
    418 #if not_yet
    419 	if (tp != &ser_cons) {
    420 		remove_vbl_function(&ser_vbl_node);
    421 		tty_free(tp);
    422 		ser_tty = (struct tty *) NULL;
    423 	}
    424 #endif
    425 	ser_open_speed = tp->t_ispeed;
    426 	splx(s);
    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