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