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