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