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