Home | History | Annotate | Line # | Download | only in dev
      1 /*	$NetBSD: ser.c,v 1.86 2022/10/26 23:38:06 riastradh 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.86 2022/10/26 23:38:06 riastradh 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 <ddb/db_active.h>
     68 
     69 #include "ser.h"
     70 #if NSER > 0
     71 
     72 void serattach(device_t, device_t, void *);
     73 int sermatch(device_t, cfdata_t, void *);
     74 
     75 struct ser_softc {
     76 	struct tty *ser_tty;
     77 };
     78 
     79 CFATTACH_DECL_NEW(ser, sizeof(struct ser_softc),
     80     sermatch, serattach, NULL, NULL);
     81 
     82 extern struct cfdriver ser_cd;
     83 
     84 dev_type_open(seropen);
     85 dev_type_close(serclose);
     86 dev_type_read(serread);
     87 dev_type_write(serwrite);
     88 dev_type_ioctl(serioctl);
     89 dev_type_stop(serstop);
     90 dev_type_tty(sertty);
     91 dev_type_poll(serpoll);
     92 
     93 const struct cdevsw ser_cdevsw = {
     94 	.d_open = seropen,
     95 	.d_close = serclose,
     96 	.d_read = serread,
     97 	.d_write = serwrite,
     98 	.d_ioctl = serioctl,
     99 	.d_stop = serstop,
    100 	.d_tty = sertty,
    101 	.d_poll = serpoll,
    102 	.d_mmap = nommap,
    103 	.d_kqfilter = ttykqfilter,
    104 	.d_discard = nodiscard,
    105 	.d_flag = D_TTY
    106 };
    107 
    108 #ifndef SEROBUF_SIZE
    109 #define SEROBUF_SIZE 32
    110 #endif
    111 #ifndef SERIBUF_SIZE
    112 #define SERIBUF_SIZE 512
    113 #endif
    114 
    115 #define splser() spl5()
    116 
    117 void	serstart(struct tty *);
    118 void	ser_shutdown(struct ser_softc *);
    119 int	serparam(struct tty *, struct termios *);
    120 void	serintr(void);
    121 int	serhwiflow(struct tty *, int);
    122 int	sermctl(dev_t dev, int, int);
    123 void	ser_fastint(void);
    124 void	sereint(int);
    125 static	void ser_putchar(struct tty *, u_short);
    126 void	ser_outintr(void);
    127 void	sercnprobe(struct consdev *);
    128 void	sercninit(struct consdev *);
    129 void	serinit(int);
    130 int	sercngetc(dev_t dev);
    131 void	sercnputc(dev_t, int);
    132 void	sercnpollc(dev_t, int);
    133 
    134 int	nser = NSER;
    135 #ifdef SERCONSOLE
    136 int	serconsole = 0;
    137 #else
    138 int	serconsole = -1;
    139 #endif
    140 int	serconsinit;
    141 int	serdefaultrate = TTYDEF_SPEED;
    142 int	serswflags;
    143 
    144 struct	vbl_node ser_vbl_node;
    145 struct	tty ser_cons;
    146 struct	tty *ser_tty;
    147 
    148 static u_short serbuf[SERIBUF_SIZE];
    149 static u_short *sbrpt = serbuf;
    150 static u_short *sbwpt = serbuf;
    151 static u_short sbcnt;
    152 static u_short sbovfl;
    153 static u_char serdcd;
    154 
    155 /*
    156  * Since this UART is not particularly bright (to put it nicely), we'll
    157  * have to do parity stuff on our own.	This table contains the 8th bit
    158  * in 7bit character mode, for even parity.  If you want odd parity,
    159  * flip the bit.  (for generation of the table, see genpar.c)
    160  */
    161 
    162 u_char	even_parity[] = {
    163 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    164 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    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 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    168 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    169 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
    170 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
    171 };
    172 
    173 /*
    174  * Since we don't get interrupts for changes on the modem control line,
    175  * we'll have to fake them by comparing current settings to the settings
    176  * we remembered on last invocation.
    177  */
    178 
    179 u_char	last_ciab_pra;
    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 accumulated 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 			if (!db_active) {
    611 				console_debugger();
    612 				return;
    613 			}
    614 		}
    615 #endif
    616 	} else {
    617 		break_in_progress = 0;
    618 		if ((tp->t_cflag & PARENB) &&
    619 			(((ch >> 7) + even_parity[ch & 0x7f]
    620 				+ !!(tp->t_cflag & PARODD)) & 1))
    621 			c |= TTY_PE;
    622 	}
    623 
    624 	if (stat & SERDATRF_OVRUN)
    625 		log(LOG_WARNING, "ser0: silo overflow\n");
    626 
    627 	tp->t_linesw->l_rint(c, tp);
    628 }
    629 
    630 /*
    631  * This interrupt is periodically invoked in the vertical blank
    632  * interrupt.  It's used to keep track of the modem control lines
    633  * and (new with the fast_int code) to move accumulated data
    634  * up into the tty layer.
    635  */
    636 void
    637 sermint(int unit)
    638 {
    639 	struct tty *tp;
    640 	u_char stat, last, istat;
    641 
    642 	tp = ser_tty;
    643 	if (!tp)
    644 		return;
    645 
    646 	/*
    647 	if ((tp->t_state & TS_ISOPEN) == 0 || tp->t_wopen == 0) {
    648 		sbrpt = sbwpt = serbuf;
    649 		return;
    650 	}
    651 	*/
    652 	/*
    653 	 * empty buffer
    654 	 */
    655 	serintr();
    656 
    657 	stat = ciab.pra;
    658 	last = last_ciab_pra;
    659 	last_ciab_pra = stat;
    660 
    661 	/*
    662 	 * check whether any interesting signal changed state
    663 	 */
    664 	istat = stat ^ last;
    665 
    666 	if (istat & serdcd) {
    667 		tp->t_linesw->l_modem(tp, ISDCD(stat));
    668 	}
    669 
    670 	if ((istat & CIAB_PRA_CTS) && (tp->t_state & TS_ISOPEN) &&
    671 	    (tp->t_cflag & CRTSCTS)) {
    672 #if 0
    673 		/* the line is up and we want to do rts/cts flow control */
    674 		if (ISCTS(stat)) {
    675 			tp->t_state &= ~TS_TTSTOP;
    676 			ttstart(tp);
    677 			/* cause tbe-int if we were stuck there */
    678 			custom.intreq = INTF_SETCLR | INTF_TBE;
    679 		} else
    680 			tp->t_state |= TS_TTSTOP;
    681 #else
    682 		/* do this on hardware level, not with tty driver */
    683 		if (ISCTS(stat)) {
    684 			tp->t_state &= ~TS_TTSTOP;
    685 			/* cause TBE interrupt */
    686 			custom.intreq = INTF_SETCLR | INTF_TBE;
    687 		}
    688 #endif
    689 	}
    690 }
    691 
    692 int
    693 serioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
    694 {
    695 	register struct tty *tp;
    696 	register int error;
    697 
    698 	tp = ser_tty;
    699 	if (!tp)
    700 		return ENXIO;
    701 
    702 	error = tp->t_linesw->l_ioctl(tp, cmd, data, flag, l);
    703 	if (error != EPASSTHROUGH)
    704 		return(error);
    705 
    706 	error = ttioctl(tp, cmd, data, flag, l);
    707 	if (error != EPASSTHROUGH)
    708 		return(error);
    709 
    710 	switch (cmd) {
    711 	case TIOCSBRK:
    712 		custom.adkcon = ADKCONF_SETCLR | ADKCONF_UARTBRK;
    713 		break;
    714 
    715 	case TIOCCBRK:
    716 		custom.adkcon = ADKCONF_UARTBRK;
    717 		break;
    718 
    719 	case TIOCSDTR:
    720 		(void) sermctl(dev, TIOCM_DTR, DMBIS);
    721 		break;
    722 
    723 	case TIOCCDTR:
    724 		(void) sermctl(dev, TIOCM_DTR, DMBIC);
    725 		break;
    726 
    727 	case TIOCMSET:
    728 		(void) sermctl(dev, *(int *) data, DMSET);
    729 		break;
    730 
    731 	case TIOCMBIS:
    732 		(void) sermctl(dev, *(int *) data, DMBIS);
    733 		break;
    734 
    735 	case TIOCMBIC:
    736 		(void) sermctl(dev, *(int *) data, DMBIC);
    737 		break;
    738 
    739 	case TIOCMGET:
    740 		*(int *)data = sermctl(dev, 0, DMGET);
    741 		break;
    742 	case TIOCGFLAGS:
    743 		*(int *)data = serswflags;
    744 		break;
    745 	case TIOCSFLAGS:
    746 		error = kauth_authorize_device_tty(l->l_cred,
    747 		    KAUTH_DEVICE_TTY_PRIVSET, tp);
    748 		if (error != 0)
    749 			return(EPERM);
    750 
    751 		serswflags = *(int *)data;
    752                 serswflags &= /* only allow valid flags */
    753                   (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS);
    754 		break;
    755 	default:
    756 		return(EPASSTHROUGH);
    757 	}
    758 
    759 	return(0);
    760 }
    761 
    762 int
    763 serparam(struct tty *tp, struct termios *t)
    764 {
    765 	int cflag, ospeed = 0;
    766 
    767 	if (t->c_ospeed > 0) {
    768 		if (t->c_ospeed < 110)
    769 			return(EINVAL);
    770 		ospeed = SERBRD(t->c_ospeed);
    771 	}
    772 
    773 	if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
    774 		return(EINVAL);
    775 
    776 	if (serswflags & TIOCFLAG_SOFTCAR || serconsole == 0) {
    777 		t->c_cflag = (t->c_cflag & ~HUPCL) | CLOCAL;
    778 	}
    779 
    780 	/* if no changes, dont do anything. com.c explains why. */
    781 	if (tp->t_ospeed == t->c_ospeed &&
    782 	    tp->t_cflag == t->c_cflag)
    783 		return (0);
    784 
    785 	cflag = t->c_cflag;
    786 
    787 	if (cflag & (CLOCAL | MDMBUF))
    788 		serdcd = 0;
    789 	else
    790 		serdcd = CIAB_PRA_CD;
    791 
    792 	/* TODO: support multiple flow control protocols like com.c */
    793 
    794 	/*
    795 	 * copy to tty
    796 	 */
    797 	tp->t_ispeed = t->c_ispeed;
    798 	tp->t_ospeed = t->c_ospeed;
    799 	tp->t_cflag = cflag;
    800 	ser_open_speed = tp->t_ispeed;
    801 
    802 	/*
    803 	 * enable interrupts
    804 	 */
    805 	custom.intena = INTF_SETCLR | INTF_RBF | INTF_TBE;
    806 	last_ciab_pra = ciab.pra;
    807 
    808 	if (t->c_ospeed == 0)
    809 		(void)sermctl(tp->t_dev, 0, DMSET);	/* hang up line */
    810 	else {
    811 		/*
    812 		 * (re)enable DTR
    813 		 * and set baud rate. (8 bit mode)
    814 		 */
    815 		(void)sermctl(tp->t_dev, TIOCM_DTR, DMSET);
    816 		custom.serper = (0 << 15) | ospeed;
    817 	}
    818 	(void)tp->t_linesw->l_modem(tp, ISDCD(last_ciab_pra));
    819 
    820 	return(0);
    821 }
    822 
    823 int serhwiflow(struct tty *tp, int flag)
    824 {
    825 #if 0
    826 	printf ("serhwiflow %d\n", flag);
    827 #endif
    828         if (flag)
    829 		CLRRTS(ciab.pra);
    830 	else
    831 	        SETRTS(ciab.pra);
    832         return 1;
    833 }
    834 
    835 static void
    836 ser_putchar(struct tty *tp, u_short c)
    837 {
    838 	if ((tp->t_cflag & CSIZE) == CS7 || (tp->t_cflag & PARENB))
    839 		c &= 0x7f;
    840 
    841 	/*
    842 	 * handle parity if necessary
    843 	 */
    844 	if (tp->t_cflag & PARENB) {
    845 		if (even_parity[c])
    846 			c |= 0x80;
    847 		if (tp->t_cflag & PARODD)
    848 			c ^= 0x80;
    849 	}
    850 	/*
    851 	 * add stop bit(s)
    852 	 */
    853 	if (tp->t_cflag & CSTOPB)
    854 		c |= 0x300;
    855 	else
    856 		c |= 0x100;
    857 
    858 	custom.serdat = c;
    859 }
    860 
    861 
    862 static u_char ser_outbuf[SEROBUF_SIZE];
    863 static u_char *sob_ptr = ser_outbuf, *sob_end = ser_outbuf;
    864 
    865 void
    866 ser_outintr(void)
    867 {
    868 	struct tty *tp;
    869 	int s;
    870 
    871 	tp = ser_tty;
    872 	s = spltty();
    873 
    874 	if (tp == 0)
    875 		goto out;
    876 
    877 	if ((custom.intreqr & INTF_TBE) == 0)
    878 		goto out;
    879 
    880 	/*
    881 	 * clear interrupt
    882 	 */
    883 	custom.intreq = INTF_TBE;
    884 
    885 	if (sob_ptr == sob_end) {
    886 		tp->t_state &= ~(TS_BUSY | TS_FLUSH);
    887 		if (tp->t_linesw)
    888 			tp->t_linesw->l_start(tp);
    889 		else
    890 			serstart(tp);
    891 		goto out;
    892 	}
    893 
    894 	/*
    895 	 * Do hardware flow control here.  if the CTS line goes down, don't
    896 	 * transmit anything.  That way, we'll be restarted by the periodic
    897 	 * interrupt when CTS comes back up.
    898 	 */
    899 	if (ISCTS(ciab.pra))
    900 		ser_putchar(tp, *sob_ptr++);
    901 	else
    902 		CLRCTS(last_ciab_pra);	/* Remember that CTS is off */
    903 out:
    904 	splx(s);
    905 }
    906 
    907 void
    908 serstart(struct tty *tp)
    909 {
    910 	int cc, s, hiwat;
    911 #ifdef DIAGNOSTIC
    912 	int unit;
    913 #endif
    914 
    915 	hiwat = 0;
    916 
    917 	if ((tp->t_state & TS_ISOPEN) == 0)
    918 		return;
    919 
    920 #ifdef DIAGNOSTIC
    921 	unit = SERUNIT(tp->t_dev);
    922 	if (unit)
    923 		panic("serstart: unit is %d", unit);
    924 #endif
    925 
    926 	s = spltty();
    927 	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
    928 		goto out;
    929 
    930 	cc = tp->t_outq.c_cc;
    931 	if (!ttypull(tp) || (tp->t_state & TS_BUSY))
    932 		goto out;
    933 
    934 	/*
    935 	 * We only do bulk transfers if using CTSRTS flow control, not for
    936 	 * (probably sloooow) ixon/ixoff devices.
    937 	 */
    938 	if ((tp->t_cflag & CRTSCTS) == 0)
    939 		cc = 1;
    940 
    941 	/*
    942 	 * Limit the amount of output we do in one burst
    943 	 * to prevent hogging the CPU.
    944 	 */
    945 	if (cc > SEROBUF_SIZE) {
    946 		hiwat++;
    947 		cc = SEROBUF_SIZE;
    948 	}
    949 	cc = q_to_b(&tp->t_outq, ser_outbuf, cc);
    950 	if (cc > 0) {
    951 		tp->t_state |= TS_BUSY;
    952 
    953 		sob_ptr = ser_outbuf;
    954 		sob_end = ser_outbuf + cc;
    955 
    956 		/*
    957 		 * Get first character out, then have TBE-interrupts blow out
    958 		 * further characters, until buffer is empty, and TS_BUSY gets
    959 		 * cleared.
    960 		 */
    961 		ser_putchar(tp, *sob_ptr++);
    962 	}
    963 out:
    964 	splx(s);
    965 }
    966 
    967 /*
    968  * Stop output on a line.
    969  */
    970 /*ARGSUSED*/
    971 void
    972 serstop(struct tty *tp, int flag)
    973 {
    974 	int s;
    975 
    976 	s = spltty();
    977 	if (tp->t_state & TS_BUSY) {
    978 		if ((tp->t_state & TS_TTSTOP) == 0)
    979 			tp->t_state |= TS_FLUSH;
    980 	}
    981 	splx(s);
    982 }
    983 
    984 int
    985 sermctl(dev_t dev, int bits, int how)
    986 {
    987 	int s;
    988 	u_char ub = 0;
    989 
    990 	/*
    991 	 * convert TIOCM* mask into CIA mask
    992 	 * which is active low
    993 	 */
    994 	if (how != DMGET) {
    995 		ub = 0;
    996 		if (bits & TIOCM_DTR)
    997 			ub |= CIAB_PRA_DTR;
    998 		if (bits & TIOCM_RTS)
    999 			ub |= CIAB_PRA_RTS;
   1000 		if (bits & TIOCM_CTS)
   1001 			ub |= CIAB_PRA_CTS;
   1002 		if (bits & TIOCM_CD)
   1003 			ub |= CIAB_PRA_CD;
   1004 		if (bits & TIOCM_RI)
   1005 			ub |= CIAB_PRA_SEL;	/* collision with /dev/par ! */
   1006 		if (bits & TIOCM_DSR)
   1007 			ub |= CIAB_PRA_DSR;
   1008 	}
   1009 	s = spltty();
   1010 	switch (how) {
   1011 	case DMSET:
   1012 		/* invert and set */
   1013 		ciab.pra = ~ub;
   1014 		break;
   1015 
   1016 	case DMBIC:
   1017 		ciab.pra |= ub;
   1018 		ub = ~ciab.pra;
   1019 		break;
   1020 
   1021 	case DMBIS:
   1022 		ciab.pra &= ~ub;
   1023 		ub = ~ciab.pra;
   1024 		break;
   1025 
   1026 	case DMGET:
   1027 		ub = ~ciab.pra;
   1028 		break;
   1029 	}
   1030 	(void)splx(s);
   1031 
   1032 	bits = 0;
   1033 	if (ub & CIAB_PRA_DTR)
   1034 		bits |= TIOCM_DTR;
   1035 	if (ub & CIAB_PRA_RTS)
   1036 		bits |= TIOCM_RTS;
   1037 	if (ub & CIAB_PRA_CTS)
   1038 		bits |= TIOCM_CTS;
   1039 	if (ub & CIAB_PRA_CD)
   1040 		bits |= TIOCM_CD;
   1041 	if (ub & CIAB_PRA_SEL)
   1042 		bits |= TIOCM_RI;
   1043 	if (ub & CIAB_PRA_DSR)
   1044 		bits |= TIOCM_DSR;
   1045 
   1046 	return(bits);
   1047 }
   1048 
   1049 /*
   1050  * Following are all routines needed for SER to act as console
   1051  */
   1052 void
   1053 sercnprobe(struct consdev *cp)
   1054 {
   1055 	int maj, unit;
   1056 #ifdef KGDB
   1057 	extern const struct cdevsw ctty_cdevsw;
   1058 #endif
   1059 
   1060 	/* locate the major number */
   1061 	maj = cdevsw_lookup_major(&ser_cdevsw);
   1062 
   1063 
   1064 	unit = CONUNIT;			/* XXX: ick */
   1065 
   1066 	/*
   1067 	 * initialize required fields
   1068 	 */
   1069 	cp->cn_dev = makedev(maj, unit);
   1070 	if (serconsole == unit)
   1071 		cp->cn_pri = CN_REMOTE;
   1072 	else
   1073 		cp->cn_pri = CN_NORMAL;
   1074 #ifdef KGDB
   1075 	/* XXX */
   1076 	if (cdevsw_lookup(kgdb_dev) == &ctty_cdevsw)
   1077 		kgdb_dev = makedev(maj, minor(kgdb_dev));
   1078 #endif
   1079 }
   1080 
   1081 void
   1082 sercninit(struct consdev *cp)
   1083 {
   1084 	int unit;
   1085 
   1086 	unit = SERUNIT(cp->cn_dev);
   1087 
   1088 	serinit(serdefaultrate);
   1089 	serconsole = unit;
   1090 	serconsinit = 1;
   1091 }
   1092 
   1093 void
   1094 serinit(int rate)
   1095 {
   1096 	int s;
   1097 
   1098 	s = splser();
   1099 	/*
   1100 	 * might want to fiddle with the CIA later ???
   1101 	 */
   1102 	custom.serper = (rate>=110 ? SERBRD(rate) : 0);
   1103 	splx(s);
   1104 }
   1105 
   1106 int
   1107 sercngetc(dev_t dev)
   1108 {
   1109 	u_short stat;
   1110 	int c, s;
   1111 
   1112 	s = splser();
   1113 	/*
   1114 	 * poll
   1115 	 */
   1116 	while (((stat = custom.serdatr & 0xffff) & SERDATRF_RBF) == 0)
   1117 		;
   1118 
   1119 	c = stat & 0xff;
   1120 	/*
   1121 	 * clear interrupt
   1122 	 */
   1123 	custom.intreq = INTF_RBF;
   1124 	splx(s);
   1125 
   1126 	return(c);
   1127 }
   1128 
   1129 /*
   1130  * Console kernel output character routine.
   1131  */
   1132 void
   1133 sercnputc(dev_t dev, int c)
   1134 {
   1135 	register int timo;
   1136 	int s;
   1137 
   1138 	s = splhigh();
   1139 
   1140 	if (serconsinit == 0) {
   1141 		(void)serinit(serdefaultrate);
   1142 		serconsinit = 1;
   1143 	}
   1144 
   1145 	/*
   1146 	 * wait for any pending transmission to finish
   1147 	 */
   1148 	timo = 50000;
   1149 	while (!(custom.serdatr & SERDATRF_TBE) && --timo);
   1150 
   1151 	/*
   1152 	 * transmit char.
   1153 	 */
   1154 	custom.serdat = (c & 0xff) | 0x100;
   1155 
   1156 	/*
   1157 	 * wait for this transmission to complete
   1158 	 */
   1159 	timo = 1500000;
   1160 	while (!(custom.serdatr & SERDATRF_TBE) && --timo)
   1161 		;
   1162 
   1163 	/*
   1164 	 * Wait for the device (my vt100..) to process the data, since we
   1165 	 * don't do flow-control with cnputc
   1166 	 */
   1167 	for (timo = 0; timo < 30000; timo++)
   1168 		;
   1169 
   1170 	/*
   1171 	 * We set TBE so that ser_outintr() is called right after to check
   1172 	 * whether there still are chars to process.
   1173 	 * We used to clear this, but it hung the tty output if the kernel
   1174 	 * output a char while userland did on the same serial port.
   1175 	 */
   1176 	custom.intreq = INTF_SETCLR | INTF_TBE;
   1177 	splx(s);
   1178 }
   1179 
   1180 void
   1181 sercnpollc(dev_t dev, int on)
   1182 {
   1183 }
   1184 #endif
   1185