xref: /netbsd/sys/arch/amiga/dev/ser.c (revision bf9ec67e)
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