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