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