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