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