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