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