xref: /netbsd/sys/arch/amiga/dev/ser.c (revision c4a72b64)
1 /*	$NetBSD: ser.c,v 1.68 2002/10/23 09:10:36 jdolecek 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. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  *	@(#)ser.c	7.12 (Berkeley) 6/27/91
36  */
37 /*
38  * XXX This file needs major cleanup it will never service more than one
39  * XXX unit.
40  */
41 
42 #include "opt_amigacons.h"
43 #include "opt_ddb.h"
44 #include "opt_kgdb.h"
45 
46 #include <sys/cdefs.h>
47 __KERNEL_RCSID(0, "$NetBSD: ser.c,v 1.68 2002/10/23 09:10:36 jdolecek Exp $");
48 
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/ioctl.h>
52 #include <sys/device.h>
53 #include <sys/tty.h>
54 #include <sys/proc.h>
55 #include <sys/file.h>
56 #include <sys/malloc.h>
57 #include <sys/uio.h>
58 #include <sys/kernel.h>
59 #include <sys/syslog.h>
60 #include <sys/queue.h>
61 #include <sys/conf.h>
62 #include <machine/cpu.h>
63 #include <amiga/amiga/device.h>
64 #include <amiga/dev/serreg.h>
65 #include <amiga/amiga/custom.h>
66 #include <amiga/amiga/cia.h>
67 #include <amiga/amiga/cc.h>
68 
69 #include <dev/cons.h>
70 
71 #include "ser.h"
72 #if NSER > 0
73 
74 void serattach(struct device *, struct device *, void *);
75 int sermatch(struct device *, struct cfdata *, void *);
76 
77 struct ser_softc {
78 	struct device dev;
79 	struct tty *ser_tty;
80 };
81 
82 CFATTACH_DECL(ser, sizeof(struct ser_softc),
83     sermatch, serattach, NULL, NULL);
84 
85 extern struct cfdriver ser_cd;
86 
87 dev_type_open(seropen);
88 dev_type_close(serclose);
89 dev_type_read(serread);
90 dev_type_write(serwrite);
91 dev_type_ioctl(serioctl);
92 dev_type_stop(serstop);
93 dev_type_tty(sertty);
94 dev_type_poll(serpoll);
95 
96 const struct cdevsw ser_cdevsw = {
97 	seropen, serclose, serread, serwrite, serioctl,
98 	serstop, sertty, serpoll, nommap, ttykqfilter, D_TTY
99 };
100 
101 #ifndef SEROBUF_SIZE
102 #define SEROBUF_SIZE 32
103 #endif
104 #ifndef SERIBUF_SIZE
105 #define SERIBUF_SIZE 512
106 #endif
107 
108 #define splser() spl5()
109 
110 void	serstart(struct tty *);
111 void	ser_shutdown(struct ser_softc *);
112 int	serparam(struct tty *, struct termios *);
113 void	serintr(void);
114 int	serhwiflow(struct tty *, int);
115 int	sermctl(dev_t dev, int, int);
116 void	ser_fastint(void);
117 void	sereint(int);
118 static	void ser_putchar(struct tty *, u_short);
119 void	ser_outintr(void);
120 void	sercnprobe(struct consdev *);
121 void	sercninit(struct consdev *);
122 void	serinit(int);
123 int	sercngetc(dev_t dev);
124 void	sercnputc(dev_t, int);
125 void	sercnpollc(dev_t, int);
126 
127 int	nser = NSER;
128 #ifdef SERCONSOLE
129 int	serconsole = 0;
130 #else
131 int	serconsole = -1;
132 #endif
133 int	serconsinit;
134 int	serdefaultrate = TTYDEF_SPEED;
135 int	serswflags;
136 
137 struct	vbl_node ser_vbl_node;
138 struct	tty ser_cons;
139 struct	tty *ser_tty;
140 
141 static u_short serbuf[SERIBUF_SIZE];
142 static u_short *sbrpt = serbuf;
143 static u_short *sbwpt = serbuf;
144 static u_short sbcnt;
145 static u_short sbovfl;
146 static u_char serdcd;
147 
148 /*
149  * Since this UART is not particularly bright (to put it nicely), we'll
150  * have to do parity stuff on our own.	This table contains the 8th bit
151  * in 7bit character mode, for even parity.  If you want odd parity,
152  * flip the bit.  (for generation of the table, see genpar.c)
153  */
154 
155 u_char	even_parity[] = {
156 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
157 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
158 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
159 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
160 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
161 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
162 	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
163 	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
164 };
165 
166 /*
167  * Since we don't get interrupts for changes on the modem control line,
168  * we'll have to fake them by comparing current settings to the settings
169  * we remembered on last invocation.
170  */
171 
172 u_char	last_ciab_pra;
173 
174 extern struct tty *constty;
175 
176 extern int ser_open_speed;	/* current speed of open serial device */
177 
178 #ifdef KGDB
179 #include <machine/remote-sl.h>
180 
181 extern dev_t kgdb_dev;
182 extern int kgdb_rate;
183 extern int kgdb_debug_init;
184 #endif
185 
186 #ifdef DEBUG
187 long	fifoin[17];
188 long	fifoout[17];
189 long	serintrcount[16];
190 long	sermintcount[16];
191 #endif
192 
193 void	sermint(register int unit);
194 
195 int
196 sermatch(struct device *pdp, struct cfdata *cfp, void *auxp)
197 {
198 	static int ser_matched = 0;
199 	static int ser_matched_real = 0;
200 
201 	/* Allow only once instance. */
202 	if (matchname("ser", (char *)auxp) == 0)
203 		return(0);
204 
205 	if (amiga_realconfig) {
206 		if (ser_matched_real)
207 			return(0);
208 		ser_matched_real = 1;
209 	} else {
210 		if (serconsole != 0)
211 			return(0);
212 
213 		if (ser_matched != 0)
214 			return(0);
215 
216 		ser_matched = 1;
217 	}
218 	return(1);
219 }
220 
221 
222 void
223 serattach(struct device *pdp, struct device *dp, void *auxp)
224 {
225 	struct ser_softc *sc;
226 	struct tty *tp;
227 	u_short ir;
228 
229 	sc = (struct ser_softc *)dp;
230 
231 	ir = custom.intenar;
232 	if (serconsole == 0)
233 		DELAY(100000);
234 
235 	ser_vbl_node.function = (void (*) (void *)) sermint;
236 	add_vbl_function(&ser_vbl_node, SER_VBL_PRIORITY, (void *) 0);
237 #ifdef KGDB
238 	if (kgdb_dev == makedev(cdevsw_lookup_major(&ser_cdevsw), 0)) {
239 		if (serconsole == 0)
240 			kgdb_dev = NODEV; /* can't debug over console port */
241 		else {
242 			(void) serinit(kgdb_rate);
243 			serconsinit = 1;       /* don't re-init in serputc */
244 			if (kgdb_debug_init == 0)
245 				printf(" kgdb enabled\n");
246 			else {
247 				/*
248 				 * Print prefix of device name,
249 				 * let kgdb_connect print the rest.
250 				 */
251 				printf("ser0: ");
252 				kgdb_connect(1);
253 			}
254 		}
255 	}
256 #endif
257 	/*
258 	 * Need to reset baud rate, etc. of next print so reset serconsinit.
259 	 */
260 	if (0 == serconsole)
261 		serconsinit = 0;
262 
263 	tp = ttymalloc();
264 	tp->t_oproc = (void (*) (struct tty *)) serstart;
265 	tp->t_param = serparam;
266 	tp->t_hwiflow = serhwiflow;
267 	tty_attach(tp);
268 	sc->ser_tty = ser_tty = tp;
269 
270 	if (dp)
271 		printf(": input fifo %d output fifo %d\n", SERIBUF_SIZE,
272 		    SEROBUF_SIZE);
273 }
274 
275 
276 /* ARGSUSED */
277 int
278 seropen(dev_t dev, int flag, int mode, struct proc *p)
279 {
280 	struct ser_softc *sc;
281 	struct tty *tp;
282 	int unit, error, s, s2;
283 
284 	error = 0;
285 	unit = SERUNIT(dev);
286 
287 	if (unit >= ser_cd.cd_ndevs)
288 		return (ENXIO);
289 
290 	sc = ser_cd.cd_devs[unit];
291 	if (sc == 0)
292 		return (ENXIO);
293 
294 	/* XXX com.c: insert KGDB check here */
295 
296 	/* XXX ser.c had: s = spltty(); */
297 
298 	tp = sc->ser_tty;
299 
300 	if ((tp->t_state & TS_ISOPEN) &&
301 	    (tp->t_state & TS_XCLUDE) &&
302 	    p->p_ucred->cr_uid != 0)
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 proc *p)
376 {
377 	struct ser_softc *sc;
378 	struct tty *tp;
379 
380 	sc = ser_cd.cd_devs[0];
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 		ttyfree(tp);
431 		ser_tty = (struct tty *) NULL;
432 	}
433 #endif
434 	ser_open_speed = tp->t_ispeed;
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 proc *p)
456 {
457 	/* ARGSUSED */
458 
459 	return ser_tty->t_linesw->l_poll(ser_tty, events, p);
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 acumulated 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, caddr_t data, int flag, struct proc *p)
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, p);
703 	if (error != EPASSTHROUGH)
704 		return(error);
705 
706 	error = ttioctl(tp, cmd, data, flag, p);
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 = suser(p->p_ucred, &p->p_acflag);
747 		if (error != 0)
748 			return(EPERM);
749 
750 		serswflags = *(int *)data;
751                 serswflags &= /* only allow valid flags */
752                   (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS);
753 		break;
754 	default:
755 		return(EPASSTHROUGH);
756 	}
757 
758 	return(0);
759 }
760 
761 int
762 serparam(struct tty *tp, struct termios *t)
763 {
764 	int cflag, ospeed = 0;
765 
766 	if (t->c_ospeed > 0) {
767 		if (t->c_ospeed < 110)
768 			return(EINVAL);
769 		ospeed = SERBRD(t->c_ospeed);
770 	}
771 
772 	if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
773 		return(EINVAL);
774 
775 	if (serswflags & TIOCFLAG_SOFTCAR || serconsole == 0) {
776 		t->c_cflag = (t->c_cflag & ~HUPCL) | CLOCAL;
777 	}
778 
779 	/* if no changes, dont do anything. com.c explains why. */
780 	if (tp->t_ospeed == t->c_ospeed &&
781 	    tp->t_cflag == t->c_cflag)
782 		return (0);
783 
784 	cflag = t->c_cflag;
785 
786 	if (cflag & (CLOCAL | MDMBUF))
787 		serdcd = 0;
788 	else
789 		serdcd = CIAB_PRA_CD;
790 
791 	/* TODO: support multiple flow control protocols like com.c */
792 
793 	/*
794 	 * copy to tty
795 	 */
796 	tp->t_ispeed = t->c_ispeed;
797 	tp->t_ospeed = t->c_ospeed;
798 	tp->t_cflag = cflag;
799 	ser_open_speed = tp->t_ispeed;
800 
801 	/*
802 	 * enable interrupts
803 	 */
804 	custom.intena = INTF_SETCLR | INTF_RBF | INTF_TBE;
805 	last_ciab_pra = ciab.pra;
806 
807 	if (t->c_ospeed == 0)
808 		(void)sermctl(tp->t_dev, 0, DMSET);	/* hang up line */
809 	else {
810 		/*
811 		 * (re)enable DTR
812 		 * and set baud rate. (8 bit mode)
813 		 */
814 		(void)sermctl(tp->t_dev, TIOCM_DTR, DMSET);
815 		custom.serper = (0 << 15) | ospeed;
816 	}
817 	(void)tp->t_linesw->l_modem(tp, ISDCD(last_ciab_pra));
818 
819 	return(0);
820 }
821 
822 int serhwiflow(struct tty *tp, int flag)
823 {
824 #if 0
825 	printf ("serhwiflow %d\n", flag);
826 #endif
827         if (flag)
828 		CLRRTS(ciab.pra);
829 	else
830 	        SETRTS(ciab.pra);
831         return 1;
832 }
833 
834 static void
835 ser_putchar(struct tty *tp, u_short c)
836 {
837 	if ((tp->t_cflag & CSIZE) == CS7 || (tp->t_cflag & PARENB))
838 		c &= 0x7f;
839 
840 	/*
841 	 * handle parity if necessary
842 	 */
843 	if (tp->t_cflag & PARENB) {
844 		if (even_parity[c])
845 			c |= 0x80;
846 		if (tp->t_cflag & PARODD)
847 			c ^= 0x80;
848 	}
849 	/*
850 	 * add stop bit(s)
851 	 */
852 	if (tp->t_cflag & CSTOPB)
853 		c |= 0x300;
854 	else
855 		c |= 0x100;
856 
857 	custom.serdat = c;
858 }
859 
860 
861 static u_char ser_outbuf[SEROBUF_SIZE];
862 static u_char *sob_ptr = ser_outbuf, *sob_end = ser_outbuf;
863 
864 void
865 ser_outintr(void)
866 {
867 	struct tty *tp;
868 	int s;
869 
870 	tp = ser_tty;
871 	s = spltty();
872 
873 	if (tp == 0)
874 		goto out;
875 
876 	if ((custom.intreqr & INTF_TBE) == 0)
877 		goto out;
878 
879 	/*
880 	 * clear interrupt
881 	 */
882 	custom.intreq = INTF_TBE;
883 
884 	if (sob_ptr == sob_end) {
885 		tp->t_state &= ~(TS_BUSY | TS_FLUSH);
886 		if (tp->t_linesw)
887 			tp->t_linesw->l_start(tp);
888 		else
889 			serstart(tp);
890 		goto out;
891 	}
892 
893 	/*
894 	 * Do hardware flow control here.  if the CTS line goes down, don't
895 	 * transmit anything.  That way, we'll be restarted by the periodic
896 	 * interrupt when CTS comes back up.
897 	 */
898 	if (ISCTS(ciab.pra))
899 		ser_putchar(tp, *sob_ptr++);
900 	else
901 		CLRCTS(last_ciab_pra);	/* Remember that CTS is off */
902 out:
903 	splx(s);
904 }
905 
906 void
907 serstart(struct tty *tp)
908 {
909 	int cc, s, hiwat;
910 #ifdef DIAGNOSTIC
911 	int unit;
912 #endif
913 
914 	hiwat = 0;
915 
916 	if ((tp->t_state & TS_ISOPEN) == 0)
917 		return;
918 
919 #ifdef DIAGNOSTIC
920 	unit = SERUNIT(tp->t_dev);
921 	if (unit)
922 		panic("serstart: unit is %d", unit);
923 #endif
924 
925 	s = spltty();
926 	if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
927 		goto out;
928 
929 	cc = tp->t_outq.c_cc;
930 	if (cc <= tp->t_lowat) {
931 		if (tp->t_state & TS_ASLEEP) {
932 			tp->t_state &= ~TS_ASLEEP;
933 			wakeup((caddr_t) & tp->t_outq);
934 		}
935 		selwakeup(&tp->t_wsel);
936 	}
937 	if (cc == 0 || (tp->t_state & TS_BUSY))
938 		goto out;
939 
940 	/*
941 	 * We only do bulk transfers if using CTSRTS flow control, not for
942 	 * (probably sloooow) ixon/ixoff devices.
943 	 */
944 	if ((tp->t_cflag & CRTSCTS) == 0)
945 		cc = 1;
946 
947 	/*
948 	 * Limit the amount of output we do in one burst
949 	 * to prevent hogging the CPU.
950 	 */
951 	if (cc > SEROBUF_SIZE) {
952 		hiwat++;
953 		cc = SEROBUF_SIZE;
954 	}
955 	cc = q_to_b(&tp->t_outq, ser_outbuf, cc);
956 	if (cc > 0) {
957 		tp->t_state |= TS_BUSY;
958 
959 		sob_ptr = ser_outbuf;
960 		sob_end = ser_outbuf + cc;
961 
962 		/*
963 		 * Get first character out, then have TBE-interrupts blow out
964 		 * further characters, until buffer is empty, and TS_BUSY gets
965 		 * cleared.
966 		 */
967 		ser_putchar(tp, *sob_ptr++);
968 	}
969 out:
970 	splx(s);
971 }
972 
973 /*
974  * Stop output on a line.
975  */
976 /*ARGSUSED*/
977 void
978 serstop(struct tty *tp, int flag)
979 {
980 	int s;
981 
982 	s = spltty();
983 	if (tp->t_state & TS_BUSY) {
984 		if ((tp->t_state & TS_TTSTOP) == 0)
985 			tp->t_state |= TS_FLUSH;
986 	}
987 	splx(s);
988 }
989 
990 int
991 sermctl(dev_t dev, int bits, int how)
992 {
993 	int s;
994 	u_char ub = 0;
995 
996 	/*
997 	 * convert TIOCM* mask into CIA mask
998 	 * which is active low
999 	 */
1000 	if (how != DMGET) {
1001 		ub = 0;
1002 		if (bits & TIOCM_DTR)
1003 			ub |= CIAB_PRA_DTR;
1004 		if (bits & TIOCM_RTS)
1005 			ub |= CIAB_PRA_RTS;
1006 		if (bits & TIOCM_CTS)
1007 			ub |= CIAB_PRA_CTS;
1008 		if (bits & TIOCM_CD)
1009 			ub |= CIAB_PRA_CD;
1010 		if (bits & TIOCM_RI)
1011 			ub |= CIAB_PRA_SEL;	/* collision with /dev/par ! */
1012 		if (bits & TIOCM_DSR)
1013 			ub |= CIAB_PRA_DSR;
1014 	}
1015 	s = spltty();
1016 	switch (how) {
1017 	case DMSET:
1018 		/* invert and set */
1019 		ciab.pra = ~ub;
1020 		break;
1021 
1022 	case DMBIC:
1023 		ciab.pra |= ub;
1024 		ub = ~ciab.pra;
1025 		break;
1026 
1027 	case DMBIS:
1028 		ciab.pra &= ~ub;
1029 		ub = ~ciab.pra;
1030 		break;
1031 
1032 	case DMGET:
1033 		ub = ~ciab.pra;
1034 		break;
1035 	}
1036 	(void)splx(s);
1037 
1038 	bits = 0;
1039 	if (ub & CIAB_PRA_DTR)
1040 		bits |= TIOCM_DTR;
1041 	if (ub & CIAB_PRA_RTS)
1042 		bits |= TIOCM_RTS;
1043 	if (ub & CIAB_PRA_CTS)
1044 		bits |= TIOCM_CTS;
1045 	if (ub & CIAB_PRA_CD)
1046 		bits |= TIOCM_CD;
1047 	if (ub & CIAB_PRA_SEL)
1048 		bits |= TIOCM_RI;
1049 	if (ub & CIAB_PRA_DSR)
1050 		bits |= TIOCM_DSR;
1051 
1052 	return(bits);
1053 }
1054 
1055 /*
1056  * Following are all routines needed for SER to act as console
1057  */
1058 void
1059 sercnprobe(struct consdev *cp)
1060 {
1061 	int maj, unit;
1062 #ifdef KGDB
1063 	extern const struct cdevsw ctty_cdevsw;
1064 #endif
1065 
1066 	/* locate the major number */
1067 	maj = cdevsw_lookup_major(&ser_cdevsw);
1068 
1069 
1070 	unit = CONUNIT;			/* XXX: ick */
1071 
1072 	/*
1073 	 * initialize required fields
1074 	 */
1075 	cp->cn_dev = makedev(maj, unit);
1076 	if (serconsole == unit)
1077 		cp->cn_pri = CN_REMOTE;
1078 	else
1079 		cp->cn_pri = CN_NORMAL;
1080 #ifdef KGDB
1081 	/* XXX */
1082 	if (cdevsw_lookup(kgdb_dev) == &ctty_cdevsw)
1083 		kgdb_dev = makedev(maj, minor(kgdb_dev));
1084 #endif
1085 }
1086 
1087 void
1088 sercninit(struct consdev *cp)
1089 {
1090 	int unit;
1091 
1092 	unit = SERUNIT(cp->cn_dev);
1093 
1094 	serinit(serdefaultrate);
1095 	serconsole = unit;
1096 	serconsinit = 1;
1097 }
1098 
1099 void
1100 serinit(int rate)
1101 {
1102 	int s;
1103 
1104 	s = splser();
1105 	/*
1106 	 * might want to fiddle with the CIA later ???
1107 	 */
1108 	custom.serper = (rate>=110 ? SERBRD(rate) : 0);
1109 	splx(s);
1110 }
1111 
1112 int
1113 sercngetc(dev_t dev)
1114 {
1115 	u_short stat;
1116 	int c, s;
1117 
1118 	s = splser();
1119 	/*
1120 	 * poll
1121 	 */
1122 	while (((stat = custom.serdatr & 0xffff) & SERDATRF_RBF) == 0)
1123 		;
1124 
1125 	c = stat & 0xff;
1126 	/*
1127 	 * clear interrupt
1128 	 */
1129 	custom.intreq = INTF_RBF;
1130 	splx(s);
1131 
1132 	return(c);
1133 }
1134 
1135 /*
1136  * Console kernel output character routine.
1137  */
1138 void
1139 sercnputc(dev_t dev, int c)
1140 {
1141 	register int timo;
1142 	int s;
1143 
1144 	s = splhigh();
1145 
1146 	if (serconsinit == 0) {
1147 		(void)serinit(serdefaultrate);
1148 		serconsinit = 1;
1149 	}
1150 
1151 	/*
1152 	 * wait for any pending transmission to finish
1153 	 */
1154 	timo = 50000;
1155 	while (!(custom.serdatr & SERDATRF_TBE) && --timo);
1156 
1157 	/*
1158 	 * transmit char.
1159 	 */
1160 	custom.serdat = (c & 0xff) | 0x100;
1161 
1162 	/*
1163 	 * wait for this transmission to complete
1164 	 */
1165 	timo = 1500000;
1166 	while (!(custom.serdatr & SERDATRF_TBE) && --timo)
1167 		;
1168 
1169 	/*
1170 	 * Wait for the device (my vt100..) to process the data, since we
1171 	 * don't do flow-control with cnputc
1172 	 */
1173 	for (timo = 0; timo < 30000; timo++)
1174 		;
1175 
1176 	/*
1177 	 * We set TBE so that ser_outintr() is called right after to check
1178 	 * whether there still are chars to process.
1179 	 * We used to clear this, but it hung the tty output if the kernel
1180 	 * output a char while userland did on the same serial port.
1181 	 */
1182 	custom.intreq = INTF_SETCLR | INTF_TBE;
1183 	splx(s);
1184 }
1185 
1186 void
1187 sercnpollc(dev_t dev, int on)
1188 {
1189 }
1190 #endif
1191