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