xref: /netbsd-src/sys/arch/amiga/dev/ser.c (revision aef5eb5f59cdfe8314f1b5f78ac04eb144e44010)
1 /*	$NetBSD: ser.c,v 1.85 2022/10/04 07:24:32 rin 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.85 2022/10/04 07:24:32 rin 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 int ser_open_speed;	/* current speed of open serial device */
180 
181 #ifdef KGDB
182 #include <machine/remote-sl.h>
183 
184 extern dev_t kgdb_dev;
185 extern int kgdb_rate;
186 extern int kgdb_debug_init;
187 #endif
188 
189 #ifdef DEBUG
190 long	fifoin[17];
191 long	fifoout[17];
192 long	serintrcount[16];
193 long	sermintcount[16];
194 #endif
195 
196 void	sermint(register int unit);
197 
198 int
199 sermatch(device_t parent, cfdata_t cf, void *aux)
200 {
201 	static int ser_matched = 0;
202 	static int ser_matched_real = 0;
203 
204 	/* Allow only once instance. */
205 	if (matchname("ser", (char *)aux) == 0)
206 		return(0);
207 
208 	if (amiga_realconfig) {
209 		if (ser_matched_real)
210 			return(0);
211 		ser_matched_real = 1;
212 	} else {
213 		if (serconsole != 0)
214 			return(0);
215 
216 		if (ser_matched != 0)
217 			return(0);
218 
219 		ser_matched = 1;
220 	}
221 	return(1);
222 }
223 
224 
225 void
226 serattach(device_t parent, device_t self, void *aux)
227 {
228 	struct ser_softc *sc;
229 	struct tty *tp;
230 	u_short ir;
231 
232 	sc = device_private(self);
233 
234 	ir = custom.intenar;
235 	__USE(ir);
236 	if (serconsole == 0)
237 		DELAY(100000);
238 
239 	ser_vbl_node.function = (void (*) (void *)) sermint;
240 	add_vbl_function(&ser_vbl_node, SER_VBL_PRIORITY, (void *) 0);
241 #ifdef KGDB
242 	if (kgdb_dev == makedev(cdevsw_lookup_major(&ser_cdevsw), 0)) {
243 		if (serconsole == 0)
244 			kgdb_dev = NODEV; /* can't debug over console port */
245 		else {
246 			(void) serinit(kgdb_rate);
247 			serconsinit = 1;       /* don't re-init in serputc */
248 			if (kgdb_debug_init == 0)
249 				printf(" kgdb enabled\n");
250 			else {
251 				/*
252 				 * Print prefix of device name,
253 				 * let kgdb_connect print the rest.
254 				 */
255 				printf("ser0: ");
256 				kgdb_connect(1);
257 			}
258 		}
259 	}
260 #endif
261 	/*
262 	 * Need to reset baud rate, etc. of next print so reset serconsinit.
263 	 */
264 	if (0 == serconsole)
265 		serconsinit = 0;
266 
267 	tp = tty_alloc();
268 	tp->t_oproc = (void (*) (struct tty *)) serstart;
269 	tp->t_param = serparam;
270 	tp->t_hwiflow = serhwiflow;
271 	tty_attach(tp);
272 	sc->ser_tty = ser_tty = tp;
273 
274 	if (self)
275 		printf(": input fifo %d output fifo %d\n", SERIBUF_SIZE,
276 		    SEROBUF_SIZE);
277 }
278 
279 
280 /* ARGSUSED */
281 int
282 seropen(dev_t dev, int flag, int mode, struct lwp *l)
283 {
284 	struct ser_softc *sc;
285 	struct tty *tp;
286 	int unit, error, s, s2;
287 
288 	error = 0;
289 	unit = SERUNIT(dev);
290 
291 	sc = device_lookup_private(&ser_cd, unit);
292 	if (sc == NULL)
293 		return (ENXIO);
294 
295 	/* XXX com.c: insert KGDB check here */
296 
297 	/* XXX ser.c had: s = spltty(); */
298 
299 	tp = sc->ser_tty;
300 
301 	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
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 = device_lookup_private(&ser_cd, SERUNIT(dev));
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 		tty_free(tp);
430 		ser_tty = (struct tty *) NULL;
431 	}
432 #endif
433 	ser_open_speed = tp->t_ispeed;
434 	splx(s);
435 	return;
436 }
437 
438 int
439 serread(dev_t dev, struct uio *uio, int flag)
440 {
441 	/* ARGSUSED */
442 
443 	return ser_tty->t_linesw->l_read(ser_tty, uio, flag);
444 }
445 
446 int
447 serwrite(dev_t dev, struct uio *uio, int flag)
448 {
449 	/* ARGSUSED */
450 
451 	return ser_tty->t_linesw->l_write(ser_tty, uio, flag);
452 }
453 
454 int
455 serpoll(dev_t dev, int events, struct lwp *l)
456 {
457 	/* ARGSUSED */
458 
459 	return ser_tty->t_linesw->l_poll(ser_tty, events, l);
460 }
461 
462 struct tty *
463 sertty(dev_t dev)
464 {
465 	/* ARGSUSED */
466 
467 	return (ser_tty);
468 }
469 
470 /*
471  * We don't do any processing of data here, so we store the raw code
472  * obtained from the uart register.  In theory, 110kBaud gives you
473  * 11kcps, so 16k buffer should be more than enough, interrupt
474  * latency of 1s should never happen, or something is seriously
475  * wrong..
476  * buffers moved to above seropen()	-is
477  */
478 
479 /*
480  * This is a replacement for the lack of a hardware fifo.  32k should be
481  * enough (there's only one unit anyway, so this is not going to
482  * accumulate).
483  */
484 void
485 ser_fastint(void)
486 {
487 	/*
488 	 * We're at RBE-level, which is higher than VBL-level which is used
489 	 * to periodically transmit contents of this buffer up one layer,
490 	 * so no spl-raising is necessary.
491 	 */
492 	u_short code;
493 
494 	/*
495 	 * This register contains both data and status bits!
496 	 */
497 	code = custom.serdatr;
498 
499 	/*
500 	 * Use SERDATF_RBF instead of INTF_RBF; they're equivalent, but
501 	 * we save one (slow) custom chip access.
502 	 */
503 	if ((code & SERDATRF_RBF) == 0)
504 		return;
505 
506 	/*
507 	 * clear interrupt
508 	 */
509 	custom.intreq = INTF_RBF;
510 
511 	/*
512 	 * check for buffer overflow.
513 	 */
514 	if (sbcnt == SERIBUF_SIZE) {
515 		++sbovfl;
516 		return;
517 	}
518 	/*
519 	 * store in buffer
520 	 */
521 	*sbwpt++ = code;
522 	if (sbwpt == serbuf + SERIBUF_SIZE)
523 		sbwpt = serbuf;
524 	++sbcnt;
525 	if (sbcnt > SERIBUF_SIZE - 20)
526 		CLRRTS(ciab.pra);	/* drop RTS if buffer almost full */
527 }
528 
529 
530 void
531 serintr(void)
532 {
533 	int s1, s2, ovfl;
534 	struct tty *tp = ser_tty;
535 
536 	/*
537 	 * Make sure we're not interrupted by another
538 	 * vbl, but allow level5 ints
539 	 */
540 	s1 = spltty();
541 
542 	/*
543 	 * pass along any accumulated information
544 	 */
545 	while (sbcnt > 0 && (tp->t_state & TS_TBLOCK) == 0) {
546 		/*
547 		 * no collision with ser_fastint()
548 		 */
549 		sereint(*sbrpt++);
550 
551 		ovfl = 0;
552 		/* lock against ser_fastint() */
553 		s2 = splser();
554 		sbcnt--;
555 		if (sbrpt == serbuf + SERIBUF_SIZE)
556 			sbrpt = serbuf;
557 		if (sbovfl != 0) {
558 			ovfl = sbovfl;
559 			sbovfl = 0;
560 		}
561 		splx(s2);
562 		if (ovfl != 0)
563 			log(LOG_WARNING, "ser0: %d ring buffer overflows.\n",
564 			    ovfl);
565 	}
566 	s2 = splser();
567 	if (sbcnt == 0 && (tp->t_state & TS_TBLOCK) == 0)
568 		SETRTS(ciab.pra);	/* start accepting data again */
569 	splx(s2);
570 	splx(s1);
571 }
572 
573 void
574 sereint(int stat)
575 {
576 	static int break_in_progress = 0;
577 	struct tty *tp;
578 	u_char ch;
579 	int c;
580 
581 	tp = ser_tty;
582 	ch = stat & 0xff;
583 	c = ch;
584 
585 	if ((tp->t_state & TS_ISOPEN) == 0) {
586 #ifdef KGDB
587 		int maj;
588 
589 		/* we don't care about parity errors */
590 		maj = cdevsw_lookup_major(&ser_cdevsw);
591 		if (kgdb_dev == makedev(maj, 0) && c == FRAME_END)
592 			kgdb_connect(0);	/* trap into kgdb */
593 #endif
594 		return;
595 	}
596 
597 	/*
598 	 * Check for break and (if enabled) parity error.
599 	 */
600 	if ((stat & 0x1ff) == 0) {
601 		if (break_in_progress)
602 			return;
603 
604 		c = TTY_FE;
605 		break_in_progress = 1;
606 #ifdef DDB
607 		if (serconsole == 0) {
608 			extern int db_active;
609 
610 			if (!db_active) {
611 				console_debugger();
612 				return;
613 			}
614 		}
615 #endif
616 	} else {
617 		break_in_progress = 0;
618 		if ((tp->t_cflag & PARENB) &&
619 			(((ch >> 7) + even_parity[ch & 0x7f]
620 				+ !!(tp->t_cflag & PARODD)) & 1))
621 			c |= TTY_PE;
622 	}
623 
624 	if (stat & SERDATRF_OVRUN)
625 		log(LOG_WARNING, "ser0: silo overflow\n");
626 
627 	tp->t_linesw->l_rint(c, tp);
628 }
629 
630 /*
631  * This interrupt is periodically invoked in the vertical blank
632  * interrupt.  It's used to keep track of the modem control lines
633  * and (new with the fast_int code) to move accumulated data
634  * up into the tty layer.
635  */
636 void
637 sermint(int unit)
638 {
639 	struct tty *tp;
640 	u_char stat, last, istat;
641 
642 	tp = ser_tty;
643 	if (!tp)
644 		return;
645 
646 	/*
647 	if ((tp->t_state & TS_ISOPEN) == 0 || tp->t_wopen == 0) {
648 		sbrpt = sbwpt = serbuf;
649 		return;
650 	}
651 	*/
652 	/*
653 	 * empty buffer
654 	 */
655 	serintr();
656 
657 	stat = ciab.pra;
658 	last = last_ciab_pra;
659 	last_ciab_pra = stat;
660 
661 	/*
662 	 * check whether any interesting signal changed state
663 	 */
664 	istat = stat ^ last;
665 
666 	if (istat & serdcd) {
667 		tp->t_linesw->l_modem(tp, ISDCD(stat));
668 	}
669 
670 	if ((istat & CIAB_PRA_CTS) && (tp->t_state & TS_ISOPEN) &&
671 	    (tp->t_cflag & CRTSCTS)) {
672 #if 0
673 		/* the line is up and we want to do rts/cts flow control */
674 		if (ISCTS(stat)) {
675 			tp->t_state &= ~TS_TTSTOP;
676 			ttstart(tp);
677 			/* cause tbe-int if we were stuck there */
678 			custom.intreq = INTF_SETCLR | INTF_TBE;
679 		} else
680 			tp->t_state |= TS_TTSTOP;
681 #else
682 		/* do this on hardware level, not with tty driver */
683 		if (ISCTS(stat)) {
684 			tp->t_state &= ~TS_TTSTOP;
685 			/* cause TBE interrupt */
686 			custom.intreq = INTF_SETCLR | INTF_TBE;
687 		}
688 #endif
689 	}
690 }
691 
692 int
693 serioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
694 {
695 	register struct tty *tp;
696 	register int error;
697 
698 	tp = ser_tty;
699 	if (!tp)
700 		return ENXIO;
701 
702 	error = tp->t_linesw->l_ioctl(tp, cmd, data, flag, l);
703 	if (error != EPASSTHROUGH)
704 		return(error);
705 
706 	error = ttioctl(tp, cmd, data, flag, l);
707 	if (error != EPASSTHROUGH)
708 		return(error);
709 
710 	switch (cmd) {
711 	case TIOCSBRK:
712 		custom.adkcon = ADKCONF_SETCLR | ADKCONF_UARTBRK;
713 		break;
714 
715 	case TIOCCBRK:
716 		custom.adkcon = ADKCONF_UARTBRK;
717 		break;
718 
719 	case TIOCSDTR:
720 		(void) sermctl(dev, TIOCM_DTR, DMBIS);
721 		break;
722 
723 	case TIOCCDTR:
724 		(void) sermctl(dev, TIOCM_DTR, DMBIC);
725 		break;
726 
727 	case TIOCMSET:
728 		(void) sermctl(dev, *(int *) data, DMSET);
729 		break;
730 
731 	case TIOCMBIS:
732 		(void) sermctl(dev, *(int *) data, DMBIS);
733 		break;
734 
735 	case TIOCMBIC:
736 		(void) sermctl(dev, *(int *) data, DMBIC);
737 		break;
738 
739 	case TIOCMGET:
740 		*(int *)data = sermctl(dev, 0, DMGET);
741 		break;
742 	case TIOCGFLAGS:
743 		*(int *)data = serswflags;
744 		break;
745 	case TIOCSFLAGS:
746 		error = kauth_authorize_device_tty(l->l_cred,
747 		    KAUTH_DEVICE_TTY_PRIVSET, tp);
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 (!ttypull(tp) || (tp->t_state & TS_BUSY))
932 		goto out;
933 
934 	/*
935 	 * We only do bulk transfers if using CTSRTS flow control, not for
936 	 * (probably sloooow) ixon/ixoff devices.
937 	 */
938 	if ((tp->t_cflag & CRTSCTS) == 0)
939 		cc = 1;
940 
941 	/*
942 	 * Limit the amount of output we do in one burst
943 	 * to prevent hogging the CPU.
944 	 */
945 	if (cc > SEROBUF_SIZE) {
946 		hiwat++;
947 		cc = SEROBUF_SIZE;
948 	}
949 	cc = q_to_b(&tp->t_outq, ser_outbuf, cc);
950 	if (cc > 0) {
951 		tp->t_state |= TS_BUSY;
952 
953 		sob_ptr = ser_outbuf;
954 		sob_end = ser_outbuf + cc;
955 
956 		/*
957 		 * Get first character out, then have TBE-interrupts blow out
958 		 * further characters, until buffer is empty, and TS_BUSY gets
959 		 * cleared.
960 		 */
961 		ser_putchar(tp, *sob_ptr++);
962 	}
963 out:
964 	splx(s);
965 }
966 
967 /*
968  * Stop output on a line.
969  */
970 /*ARGSUSED*/
971 void
972 serstop(struct tty *tp, int flag)
973 {
974 	int s;
975 
976 	s = spltty();
977 	if (tp->t_state & TS_BUSY) {
978 		if ((tp->t_state & TS_TTSTOP) == 0)
979 			tp->t_state |= TS_FLUSH;
980 	}
981 	splx(s);
982 }
983 
984 int
985 sermctl(dev_t dev, int bits, int how)
986 {
987 	int s;
988 	u_char ub = 0;
989 
990 	/*
991 	 * convert TIOCM* mask into CIA mask
992 	 * which is active low
993 	 */
994 	if (how != DMGET) {
995 		ub = 0;
996 		if (bits & TIOCM_DTR)
997 			ub |= CIAB_PRA_DTR;
998 		if (bits & TIOCM_RTS)
999 			ub |= CIAB_PRA_RTS;
1000 		if (bits & TIOCM_CTS)
1001 			ub |= CIAB_PRA_CTS;
1002 		if (bits & TIOCM_CD)
1003 			ub |= CIAB_PRA_CD;
1004 		if (bits & TIOCM_RI)
1005 			ub |= CIAB_PRA_SEL;	/* collision with /dev/par ! */
1006 		if (bits & TIOCM_DSR)
1007 			ub |= CIAB_PRA_DSR;
1008 	}
1009 	s = spltty();
1010 	switch (how) {
1011 	case DMSET:
1012 		/* invert and set */
1013 		ciab.pra = ~ub;
1014 		break;
1015 
1016 	case DMBIC:
1017 		ciab.pra |= ub;
1018 		ub = ~ciab.pra;
1019 		break;
1020 
1021 	case DMBIS:
1022 		ciab.pra &= ~ub;
1023 		ub = ~ciab.pra;
1024 		break;
1025 
1026 	case DMGET:
1027 		ub = ~ciab.pra;
1028 		break;
1029 	}
1030 	(void)splx(s);
1031 
1032 	bits = 0;
1033 	if (ub & CIAB_PRA_DTR)
1034 		bits |= TIOCM_DTR;
1035 	if (ub & CIAB_PRA_RTS)
1036 		bits |= TIOCM_RTS;
1037 	if (ub & CIAB_PRA_CTS)
1038 		bits |= TIOCM_CTS;
1039 	if (ub & CIAB_PRA_CD)
1040 		bits |= TIOCM_CD;
1041 	if (ub & CIAB_PRA_SEL)
1042 		bits |= TIOCM_RI;
1043 	if (ub & CIAB_PRA_DSR)
1044 		bits |= TIOCM_DSR;
1045 
1046 	return(bits);
1047 }
1048 
1049 /*
1050  * Following are all routines needed for SER to act as console
1051  */
1052 void
1053 sercnprobe(struct consdev *cp)
1054 {
1055 	int maj, unit;
1056 #ifdef KGDB
1057 	extern const struct cdevsw ctty_cdevsw;
1058 #endif
1059 
1060 	/* locate the major number */
1061 	maj = cdevsw_lookup_major(&ser_cdevsw);
1062 
1063 
1064 	unit = CONUNIT;			/* XXX: ick */
1065 
1066 	/*
1067 	 * initialize required fields
1068 	 */
1069 	cp->cn_dev = makedev(maj, unit);
1070 	if (serconsole == unit)
1071 		cp->cn_pri = CN_REMOTE;
1072 	else
1073 		cp->cn_pri = CN_NORMAL;
1074 #ifdef KGDB
1075 	/* XXX */
1076 	if (cdevsw_lookup(kgdb_dev) == &ctty_cdevsw)
1077 		kgdb_dev = makedev(maj, minor(kgdb_dev));
1078 #endif
1079 }
1080 
1081 void
1082 sercninit(struct consdev *cp)
1083 {
1084 	int unit;
1085 
1086 	unit = SERUNIT(cp->cn_dev);
1087 
1088 	serinit(serdefaultrate);
1089 	serconsole = unit;
1090 	serconsinit = 1;
1091 }
1092 
1093 void
1094 serinit(int rate)
1095 {
1096 	int s;
1097 
1098 	s = splser();
1099 	/*
1100 	 * might want to fiddle with the CIA later ???
1101 	 */
1102 	custom.serper = (rate>=110 ? SERBRD(rate) : 0);
1103 	splx(s);
1104 }
1105 
1106 int
1107 sercngetc(dev_t dev)
1108 {
1109 	u_short stat;
1110 	int c, s;
1111 
1112 	s = splser();
1113 	/*
1114 	 * poll
1115 	 */
1116 	while (((stat = custom.serdatr & 0xffff) & SERDATRF_RBF) == 0)
1117 		;
1118 
1119 	c = stat & 0xff;
1120 	/*
1121 	 * clear interrupt
1122 	 */
1123 	custom.intreq = INTF_RBF;
1124 	splx(s);
1125 
1126 	return(c);
1127 }
1128 
1129 /*
1130  * Console kernel output character routine.
1131  */
1132 void
1133 sercnputc(dev_t dev, int c)
1134 {
1135 	register int timo;
1136 	int s;
1137 
1138 	s = splhigh();
1139 
1140 	if (serconsinit == 0) {
1141 		(void)serinit(serdefaultrate);
1142 		serconsinit = 1;
1143 	}
1144 
1145 	/*
1146 	 * wait for any pending transmission to finish
1147 	 */
1148 	timo = 50000;
1149 	while (!(custom.serdatr & SERDATRF_TBE) && --timo);
1150 
1151 	/*
1152 	 * transmit char.
1153 	 */
1154 	custom.serdat = (c & 0xff) | 0x100;
1155 
1156 	/*
1157 	 * wait for this transmission to complete
1158 	 */
1159 	timo = 1500000;
1160 	while (!(custom.serdatr & SERDATRF_TBE) && --timo)
1161 		;
1162 
1163 	/*
1164 	 * Wait for the device (my vt100..) to process the data, since we
1165 	 * don't do flow-control with cnputc
1166 	 */
1167 	for (timo = 0; timo < 30000; timo++)
1168 		;
1169 
1170 	/*
1171 	 * We set TBE so that ser_outintr() is called right after to check
1172 	 * whether there still are chars to process.
1173 	 * We used to clear this, but it hung the tty output if the kernel
1174 	 * output a char while userland did on the same serial port.
1175 	 */
1176 	custom.intreq = INTF_SETCLR | INTF_TBE;
1177 	splx(s);
1178 }
1179 
1180 void
1181 sercnpollc(dev_t dev, int on)
1182 {
1183 }
1184 #endif
1185