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