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