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