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