xref: /minix/minix/drivers/tty/tty/arch/i386/rs232.c (revision 045e0ed3)
1 #include <minix/config.h>
2 /*---------------------------------------------------------------------------*
3  *		rs232.c - serial driver for 8250 and 16450 UARTs	     *
4  *		Added support for Atari ST M68901 and YM-2149	--kub	     *
5  *---------------------------------------------------------------------------*/
6 
7 #include <minix/drivers.h>
8 #include <sys/termios.h>
9 #include <signal.h>
10 #include "tty.h"
11 
12 #if NR_RS_LINES > 0
13 
14 /* switch RTS/CTS on/off */
15 #if !defined(UART_RTSCTS_OFF)
16 #define UART_RTSCTS_OFF 0 /* 0: Use RTS/CTS, 1: Don't.*/
17 #endif
18 
19 /* 8250 constants. */
20 #define UART_FREQ         115200L	/* timer frequency */
21 
22 /* Interrupt enable bits. */
23 #define IE_RECEIVER_READY       1
24 #define IE_TRANSMITTER_READY    2
25 #define IE_LINE_STATUS_CHANGE   4
26 #define IE_MODEM_STATUS_CHANGE  8
27 
28 /* Interrupt status bits. */
29 #define IS_MODEM_STATUS_CHANGE  0
30 #define IS_NOTPENDING		1
31 #define IS_TRANSMITTER_READY    2
32 #define IS_RECEIVER_READY       4
33 #define IS_LINE_STATUS_CHANGE   6
34 #define IS_IDBITS		6
35 
36 /* Line control bits. */
37 #define LC_CS5               0x00	/* LSB0 and LSB1 encoding for CS5 */
38 #define LC_CS6               0x01	/* LSB0 and LSB1 encoding for CS6 */
39 #define LC_CS7               0x02	/* LSB0 and LSB1 encoding for CS7 */
40 #define LC_CS8               0x03	/* LSB0 and LSB1 encoding for CS8 */
41 #define LC_2STOP_BITS        0x04
42 #define LC_PARITY            0x08
43 #define LC_PAREVEN           0x10
44 #define LC_BREAK             0x40
45 #define LC_ADDRESS_DIVISOR   0x80
46 
47 /* Line status bits. */
48 #define LS_OVERRUN_ERR          2
49 #define LS_PARITY_ERR           4
50 #define LS_FRAMING_ERR          8
51 #define LS_BREAK_INTERRUPT   0x10
52 #define LS_TRANSMITTER_READY 0x20
53 
54 /* Modem control bits. */
55 #define MC_DTR                  1
56 #define MC_RTS                  2
57 #define MC_OUT2                 8	/* required for PC & AT interrupts */
58 
59 /* Modem status bits. */
60 #define MS_CTS               0x10
61 #define MS_RLSD              0x80       /* Received Line Signal Detect */
62 #define MS_DRLSD             0x08       /* RLSD Delta */
63 
64 #define DATA_BITS_SHIFT         8      /* amount data bits shifted in mode */
65 #define DEF_BAUD             1200      /* default baud rate */
66 
67 #define RS_IBUFSIZE          1024      /* RS232 input buffer size */
68 #define RS_OBUFSIZE          1024      /* RS232 output buffer size */
69 
70 /* Input buffer watermarks.
71  * The external device is asked to stop sending when the buffer
72  * exactly reaches high water, or when TTY requests it.  Sending restarts
73  * when the input buffer empties below the low watermark.
74  */
75 #define RS_ILOWWATER   (1 * RS_IBUFSIZE / 4)
76 #define RS_IHIGHWATER  (3 * RS_IBUFSIZE / 4)
77 
78 /* Output buffer low watermark.
79  * TTY is notified when the output buffer empties below the low watermark, so
80  * it may continue filling the buffer if doing a large write.
81  */
82 #define RS_OLOWWATER   (1 * RS_OBUFSIZE / 4)
83 
84 /* Macros to handle flow control.
85  * Interrupts must be off when they are used.
86  * Time is critical - already the function call for outb() is annoying.
87  * If outb() can be done in-line, tests to avoid it can be dropped.
88  * istart() tells external device we are ready by raising RTS.
89  * istop() tells external device we are not ready by dropping RTS.
90  * DTR is kept high all the time (it probably should be raised by open and
91  * dropped by close of the device).
92  * OUT2 is also kept high all the time.
93  */
94 #if UART_RTSCTS_OFF
95 #define istart(rs) \
96 	(rs)->idevready = TRUE
97 
98 #define istop(rs) \
99 		(rs)->idevready = FALSE
100 
101 /* Macro to tell if device is ready.  The rs->cts field is set to MS_CTS if
102  * CLOCAL is in effect for a line without a CTS wire.
103  */
104 #define devready(rs) MS_CTS
105 #else
106 #define istart(rs) \
107 	(sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_RTS | MC_DTR), \
108 		(rs)->idevready = TRUE)
109 #define istop(rs) \
110 	(sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_DTR), \
111 		(rs)->idevready = FALSE)
112 
113 /* Macro to tell if device is ready.  The rs->cts field is set to MS_CTS if
114  * CLOCAL is in effect for a line without a CTS wire.
115  */
116 #define devready(rs) ((my_inb(rs->modem_status_port) | rs->cts) & MS_CTS)
117 
118 #endif
119 
120 /* Macro to tell if transmitter is ready. */
121 #define txready(rs) (my_inb(rs->line_status_port) & LS_TRANSMITTER_READY)
122 
123 /* Macro to tell if carrier has dropped.
124  * The RS232 Carrier Detect (CD) line is usually connected to the 8250
125  * Received Line Signal Detect pin, reflected by bit MS_RLSD in the Modem
126  * Status Register.  The MS_DRLSD bit tells if MS_RLSD has just changed state.
127  * So if MS_DRLSD is set and MS_RLSD cleared, we know that carrier has just
128  * dropped.
129  */
130 #define devhup(rs)	\
131 	((my_inb(rs->modem_status_port) & (MS_RLSD|MS_DRLSD)) == MS_DRLSD)
132 
133 /* Types. */
134 typedef unsigned char bool_t;	/* boolean */
135 
136 /* RS232 device structure, one per device. */
137 typedef struct rs232 {
138   tty_t *tty;			/* associated TTY structure */
139 
140   int icount;			/* number of bytes in the input buffer */
141   char *ihead;			/* next free spot in input buffer */
142   char *itail;			/* first byte to give to TTY */
143   bool_t idevready;		/* nonzero if we are ready to receive (RTS) */
144   char cts;			/* normally 0, but MS_CTS if CLOCAL is set */
145 
146   unsigned char ostate;		/* combination of flags: */
147 #define ODONE          1	/* output completed (< output enable bits) */
148 #define ORAW           2	/* raw mode for xoff disable (< enab. bits) */
149 #define OWAKEUP        4	/* tty_wakeup() pending (asm code only) */
150 #define ODEVREADY MS_CTS	/* external device hardware ready (CTS) */
151 #define OQUEUED     0x20	/* output buffer not empty */
152 #define OSWREADY    0x40	/* external device software ready (no xoff) */
153 #define ODEVHUP  MS_RLSD	/* external device has dropped carrier */
154 #define OSOFTBITS  (ODONE | ORAW | OWAKEUP | OQUEUED | OSWREADY)
155 				/* user-defined bits */
156 #if (OSOFTBITS | ODEVREADY | ODEVHUP) == OSOFTBITS
157 				/* a weak sanity check */
158 #error				/* bits are not unique */
159 #endif
160   unsigned char oxoff;		/* char to stop output */
161   bool_t inhibited;		/* output inhibited? (follows tty_inhibited) */
162   bool_t drain;			/* if set drain output and reconfigure line */
163   int ocount;			/* number of bytes in the output buffer */
164   char *ohead;			/* next free spot in output buffer */
165   char *otail;			/* next char to output */
166 
167 #if defined(__i386__)
168   port_t xmit_port;		/* i/o ports */
169   port_t recv_port;
170   port_t div_low_port;
171   port_t div_hi_port;
172   port_t int_enab_port;
173   port_t int_id_port;
174   port_t line_ctl_port;
175   port_t modem_ctl_port;
176   port_t line_status_port;
177   port_t modem_status_port;
178 #endif
179 
180   unsigned char lstatus;	/* last line status */
181   unsigned char pad;		/* ensure alignment for 16-bit ints */
182   unsigned framing_errors;	/* error counts (no reporting yet) */
183   unsigned overrun_errors;
184   unsigned parity_errors;
185   unsigned break_interrupts;
186 
187   int irq;			/* irq for this line */
188   int irq_hook_id;		/* interrupt hook */
189 
190   char ibuf[RS_IBUFSIZE];	/* input buffer */
191   char obuf[RS_OBUFSIZE];	/* output buffer */
192 } rs232_t;
193 
194 static rs232_t rs_lines[NR_RS_LINES];
195 
196 #if defined(__i386__)
197 /* 8250 base addresses. */
198 static port_t addr_8250[] = {
199   0x3F8,	/* COM1 */
200   0x2F8,	/* COM2 */
201   0x3E8,	/* COM3 */
202   0x2E8,	/* COM4 */
203 };
204 #endif
205 
206 static void in_int(rs232_t *rs);
207 static void line_int(rs232_t *rs);
208 static void modem_int(rs232_t *rs);
209 static int rs_write(tty_t *tp, int try);
210 static void rs_echo(tty_t *tp, int c);
211 static int rs_ioctl(tty_t *tp, int try);
212 static void rs_config(rs232_t *rs);
213 static int rs_read(tty_t *tp, int try);
214 static int rs_icancel(tty_t *tp, int try);
215 static int rs_ocancel(tty_t *tp, int try);
216 static void rs_ostart(rs232_t *rs);
217 static int rs_break_on(tty_t *tp, int try);
218 static int rs_break_off(tty_t *tp, int try);
219 static int rs_close(tty_t *tp, int try);
220 static void out_int(rs232_t *rs);
221 static void rs232_handler(rs232_t *rs);
222 
223 static int my_inb(port_t port)
224 {
225 	int r;
226 	u32_t v = 0;
227 	r = sys_inb(port, &v);
228 	if (r != OK)
229 		printf("RS232 warning: failed inb 0x%x\n", port);
230 
231 	return (int) v;
232 }
233 
234 /*===========================================================================*
235  *				rs_write				     *
236  *===========================================================================*/
237 static int rs_write(register tty_t *tp, int try)
238 {
239 /* (*devwrite)() routine for RS232. */
240 
241   rs232_t *rs = tp->tty_priv;
242   int r, count, ocount;
243 
244   if (rs->inhibited != tp->tty_inhibited) {
245 	/* Inhibition state has changed. */
246 	rs->ostate |= OSWREADY;
247 	if (tp->tty_inhibited) rs->ostate &= ~OSWREADY;
248 	rs->inhibited = tp->tty_inhibited;
249   }
250 
251   if (rs->drain) {
252 	/* Wait for the line to drain then reconfigure and continue output. */
253 	if (rs->ocount > 0) return 0;
254 	rs->drain = FALSE;
255 	rs_config(rs);
256   }
257 
258   /* While there is something to do. */
259   for (;;) {
260 	ocount = buflen(rs->obuf) - rs->ocount;
261 	count = bufend(rs->obuf) - rs->ohead;
262 	if (count > ocount) count = ocount;
263 	if (count > tp->tty_outleft) count = tp->tty_outleft;
264 	if (count == 0 || tp->tty_inhibited) {
265 		if (try) return 0;
266 		break;
267 	}
268 	if (try) return 1;
269 
270 	/* Copy from user space to the RS232 output buffer. */
271 	if (tp->tty_outcaller == KERNEL) {
272 		/* We're trying to print on kernel's behalf */
273 		memcpy(rs->ohead, (char *) tp->tty_outgrant + tp->tty_outcum,
274 			count);
275 	} else {
276 		if ((r = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
277 			tp->tty_outcum, (vir_bytes) rs->ohead, count)) != OK)
278 				printf("TTY: sys_safecopyfrom() failed: %d", r);
279 	}
280 
281 	/* Perform output processing on the output buffer. */
282 	out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
283 	if (count == 0) break;
284 
285 	/* Assume echoing messed up by output. */
286 	tp->tty_reprint = TRUE;
287 
288 	/* Bookkeeping. */
289 	rs->ocount += ocount;
290 	rs_ostart(rs);
291 	if ((rs->ohead += ocount) >= bufend(rs->obuf))
292 		rs->ohead -= buflen(rs->obuf);
293 	tp->tty_outcum += count;
294 	if ((tp->tty_outleft -= count) == 0) {
295 		/* Output is finished, reply to the writer. */
296 		if (tp->tty_outcaller != KERNEL)
297 			chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
298 				tp->tty_outcum);
299 		tp->tty_outcum = 0;
300 		tp->tty_outcaller = NONE;
301 	}
302   }
303   if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
304 	/* Oops, the line has hung up. */
305 	if (tp->tty_outcaller != KERNEL)
306 		chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
307 	tp->tty_outleft = tp->tty_outcum = 0;
308 	tp->tty_outcaller = NONE;
309   }
310 
311   return 1;
312 }
313 
314 /*===========================================================================*
315  *				rs_echo					     *
316  *===========================================================================*/
317 static void rs_echo(tp, c)
318 tty_t *tp;			/* which TTY */
319 int c;				/* character to echo */
320 {
321 /* Echo one character.  (Like rs_write, but only one character, optionally.) */
322 
323   rs232_t *rs = tp->tty_priv;
324   int count, ocount;
325 
326   ocount = buflen(rs->obuf) - rs->ocount;
327   if (ocount == 0) return;		/* output buffer full */
328   count = 1;
329   *rs->ohead = c;			/* add one character */
330 
331   out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
332   if (count == 0) return;
333 
334   rs->ocount += ocount;
335   rs_ostart(rs);
336   if ((rs->ohead += ocount) >= bufend(rs->obuf)) rs->ohead -= buflen(rs->obuf);
337 }
338 
339 /*===========================================================================*
340  *				rs_ioctl				     *
341  *===========================================================================*/
342 static int rs_ioctl(tty_t *tp, int UNUSED(dummy))
343 /* tp;			which TTY */
344 {
345 /* Reconfigure the line as soon as the output has drained. */
346   rs232_t *rs = tp->tty_priv;
347 
348   rs->drain = TRUE;
349   return 0;	/* dummy */
350 }
351 
352 /*===========================================================================*
353  *				rs_config				     *
354  *===========================================================================*/
355 static void rs_config(rs232_t *rs)
356 /* rs			which line */
357 {
358 /* Set various line control parameters for RS232 I/O.
359  * If DataBits == 5 and StopBits == 2, 8250 will generate 1.5 stop bits.
360  * The 8250 can't handle split speed, so we use the input speed.
361  */
362 
363   tty_t *tp = rs->tty;
364   int divisor;
365   int line_controls;
366   static struct speed2divisor {
367 	speed_t	speed;
368 	int	divisor;
369   } s2d[] = {
370 #if defined(__i386__)
371 	{ B50,		UART_FREQ / 50		},
372 #endif
373 	{ B75,		UART_FREQ / 75		},
374 	{ B110,		UART_FREQ / 110		},
375 	{ B134,		UART_FREQ * 10 / 1345	},
376 	{ B150,		UART_FREQ / 150		},
377 	{ B200,		UART_FREQ / 200		},
378 	{ B300,		UART_FREQ / 300		},
379 	{ B600,		UART_FREQ / 600		},
380 	{ B1200,	UART_FREQ / 1200	},
381 #if defined(__i386__)
382 	{ B1800,	UART_FREQ / 1800	},
383 #endif
384 	{ B2400,	UART_FREQ / 2400	},
385 	{ B4800,	UART_FREQ / 4800	},
386 	{ B9600,	UART_FREQ / 9600	},
387 	{ B19200,	UART_FREQ / 19200	},
388 #if defined(__i386__)
389 	{ B38400,	UART_FREQ / 38400	},
390 	{ B57600,	UART_FREQ / 57600	},
391 	{ B115200,	UART_FREQ / 115200L	},
392 #endif
393   };
394   struct speed2divisor *s2dp;
395 
396   /* RS232 needs to know the xoff character, and if CTS works. */
397   rs->oxoff = tp->tty_termios.c_cc[VSTOP];
398   rs->cts = (tp->tty_termios.c_cflag & CLOCAL) ? MS_CTS : 0;
399 
400   /* Look up the 8250 rate divisor from the output speed. */
401   divisor = 0;
402   for (s2dp = s2d; s2dp < s2d + sizeof(s2d)/sizeof(s2d[0]); s2dp++) {
403 	if (s2dp->speed == tp->tty_termios.c_ospeed) divisor = s2dp->divisor;
404   }
405   if (divisor == 0) return;	/* B0? */
406 
407   /* Compute line control flag bits. */
408   line_controls = 0;
409   if (tp->tty_termios.c_cflag & PARENB) {
410 	line_controls |= LC_PARITY;
411 	if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= LC_PAREVEN;
412   }
413 
414   if (divisor >= (UART_FREQ / 110)) line_controls |= LC_2STOP_BITS;
415 
416   /* which word size is configured? set the bits explicitly. */
417   if((tp->tty_termios.c_cflag & CSIZE) == CS5)
418 	line_controls |= LC_CS5;
419   else if((tp->tty_termios.c_cflag & CSIZE) == CS6)
420 	line_controls |= LC_CS6;
421   else if((tp->tty_termios.c_cflag & CSIZE) == CS7)
422 	line_controls |= LC_CS7;
423   else if((tp->tty_termios.c_cflag & CSIZE) == CS8)
424 	line_controls |= LC_CS8;
425   else printf("rs232: warning: no known word size set\n");
426 
427   /* Select the baud rate divisor registers and change the rate. */
428   sys_outb(rs->line_ctl_port, LC_ADDRESS_DIVISOR);
429   sys_outb(rs->div_low_port, divisor);
430   sys_outb(rs->div_hi_port, divisor >> 8);
431 
432   /* Change the line controls and reselect the usual registers. */
433   sys_outb(rs->line_ctl_port, line_controls);
434 
435   rs->ostate = devready(rs) | ORAW | OSWREADY;	/* reads modem_ctl_port */
436   if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE)
437 	rs->ostate &= ~ORAW;
438 }
439 
440 /*===========================================================================*
441  *				rs_init					     *
442  *===========================================================================*/
443 void rs_init(tty_t *tp)
444 /* tp			which TTY */
445 {
446   u32_t dummy;
447 /* Initialize RS232 for one line. */
448 
449   register rs232_t *rs;
450   int line;
451   port_t this_8250;
452   int s, irq;
453   char l[10];
454 
455   /* Associate RS232 and TTY structures. */
456   line = tp - &tty_table[NR_CONS];
457 
458   /* See if kernel debugging is enabled; if so, don't initialize this
459    * serial line, making tty not look at the irq and returning ENXIO
460    * for all requests on it from userland. (The kernel will use it.)
461    */
462   if(env_get_param(SERVARNAME, l, sizeof(l)-1) == OK && atoi(l) == line) {
463 	printf("TTY: not initializing rs232 line %d (in use by kernel)\n",
464 		line);
465 	return;
466   }
467 
468   rs = tp->tty_priv = &rs_lines[line];
469   rs->tty = tp;
470 
471   /* Set up input queue. */
472   rs->ihead = rs->itail = rs->ibuf;
473 
474   /* Precalculate port numbers for speed. Magic numbers in the code (once). */
475   this_8250 = addr_8250[line];
476   rs->xmit_port = this_8250 + 0;
477   rs->recv_port = this_8250 + 0;
478   rs->div_low_port = this_8250 + 0;
479   rs->div_hi_port = this_8250 + 1;
480   rs->int_enab_port = this_8250 + 1;
481   rs->int_id_port = this_8250 + 2;
482   rs->line_ctl_port = this_8250 + 3;
483   rs->modem_ctl_port = this_8250 + 4;
484   rs->line_status_port = this_8250 + 5;
485   rs->modem_status_port = this_8250 + 6;
486 
487   /* Set up the hardware to a base state, in particular
488    *	o turn off DTR (MC_DTR) to try to stop the external device.
489    *	o be careful about the divisor latch.  Some BIOS's leave it enabled
490    *	  here and that caused trouble (no interrupts) in version 1.5 by
491    *	  hiding the interrupt enable port in the next step, and worse trouble
492    *	  (continual interrupts) in an old version by hiding the receiver
493    *	  port in the first interrupt.  Call rs_ioctl() early to avoid this.
494    *	o disable interrupts at the chip level, to force an edge transition
495    *	  on the 8259 line when interrupts are next enabled and active.
496    *	  RS232 interrupts are guaranteed to be disabled now by the 8259
497    *	  mask, but there used to be trouble if the mask was set without
498    *	  handling a previous interrupt.
499    */
500   istop(rs);			/* sets modem_ctl_port */
501   rs_config(rs);
502   sys_outb(rs->int_enab_port, 0);
503 
504   /* Clear any harmful leftover interrupts.  An output interrupt is harmless
505    * and will occur when interrupts are enabled anyway.  Set up the output
506    * queue using the status from clearing the modem status interrupt.
507    */
508   if ((s = sys_inb(rs->line_status_port, &dummy)) != OK)
509 	printf("TTY: sys_inb() failed: %d", s);
510   if ((s = sys_inb(rs->recv_port, &dummy)) != OK)
511 	printf("TTY: sys_inb() failed: %d", s);
512   rs->ostate = devready(rs) | ORAW | OSWREADY;	/* reads modem_ctl_port */
513   rs->ohead = rs->otail = rs->obuf;
514 
515   /* Enable interrupts for both interrupt controller and device. */
516   irq = (line & 1) == 0 ? RS232_IRQ : SECONDARY_IRQ;
517 
518   rs->irq = irq;
519   rs->irq_hook_id = rs->irq;	/* call back with irq line number */
520   if (sys_irqsetpolicy(irq, IRQ_REENABLE, &rs->irq_hook_id) != OK) {
521   	printf("RS232: Couldn't obtain hook for irq %d\n", irq);
522   } else {
523   	if (sys_irqenable(&rs->irq_hook_id) != OK)  {
524   		printf("RS232: Couldn't enable irq %d (hooked)\n", irq);
525   	}
526   }
527 
528   rs_irq_set |= (1 << irq);
529 #if UART_RTSCTS_OFF
530   sys_outb(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_RECEIVER_READY | IE_TRANSMITTER_READY);
531 #else
532   sys_outb(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_MODEM_STATUS_CHANGE
533 				| IE_RECEIVER_READY | IE_TRANSMITTER_READY);
534 #endif
535 
536   /* Fill in TTY function hooks. */
537   tp->tty_devread = rs_read;
538   tp->tty_devwrite = rs_write;
539   tp->tty_echo = rs_echo;
540   tp->tty_icancel = rs_icancel;
541   tp->tty_ocancel = rs_ocancel;
542   tp->tty_ioctl = rs_ioctl;
543   tp->tty_break_on = rs_break_on;
544   tp->tty_break_off = rs_break_off;
545   tp->tty_close = rs_close;
546 
547   /* Tell external device we are ready. */
548   istart(rs);
549 
550 }
551 
552 /*===========================================================================*
553  *				rs_interrupt				     *
554  *===========================================================================*/
555 void rs_interrupt(message *m)
556 {
557 	unsigned long irq_set;
558 	int i;
559 	rs232_t *rs;
560 
561 	irq_set= m->m_notify.interrupts;
562 	for (i= 0, rs = rs_lines; i<NR_RS_LINES; i++, rs++)
563 	{
564 		if (irq_set & (1 << rs->irq))
565 			rs232_handler(rs);
566 	}
567 }
568 
569 /*===========================================================================*
570  *				rs_icancel				     *
571  *===========================================================================*/
572 static int rs_icancel(tty_t *tp, int UNUSED(dummy))
573 {
574 /* Cancel waiting input. */
575   rs232_t *rs = tp->tty_priv;
576 
577   rs->icount = 0;
578   rs->itail = rs->ihead;
579   istart(rs);
580 
581   return 0;	/* dummy */
582 }
583 
584 /*===========================================================================*
585  *				rs_ocancel				     *
586  *===========================================================================*/
587 static int rs_ocancel(tty_t *tp, int UNUSED(dummy))
588 {
589 /* Cancel pending output. */
590   rs232_t *rs = tp->tty_priv;
591 
592   rs->ostate &= ~(ODONE | OQUEUED);
593   rs->ocount = 0;
594   rs->otail = rs->ohead;
595 
596   return 0;	/* dummy */
597 }
598 
599 /*===========================================================================*
600  *				rs_read					     *
601  *===========================================================================*/
602 static int rs_read(tty_t *tp, int try)
603 {
604 /* Process characters from the circular input buffer. */
605 
606   rs232_t *rs = tp->tty_priv;
607   int icount, count, ostate;
608 
609   if (!(tp->tty_termios.c_cflag & CLOCAL)) {
610   	if (try) return 1;
611 	/* Send a SIGHUP if hangup detected. */
612 	ostate = rs->ostate;
613 	rs->ostate &= ~ODEVHUP;		/* save ostate, clear DEVHUP */
614 	if (ostate & ODEVHUP) {
615 		sigchar(tp, SIGHUP, 1);
616 		tp->tty_termios.c_ospeed = B0;	/* Disable further I/O. */
617 		return 0;
618 	}
619   }
620 
621   if (try) {
622   	if (rs->icount > 0)
623 	  	return 1;
624 	return 0;
625   }
626 
627   while ((count = rs->icount) > 0) {
628 	icount = bufend(rs->ibuf) - rs->itail;
629 	if (count > icount) count = icount;
630 
631 	/* Perform input processing on (part of) the input buffer. */
632 	if ((count = in_process(tp, rs->itail, count)) == 0) break;
633 
634 	rs->icount -= count;
635 	if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs);
636 	if ((rs->itail += count) == bufend(rs->ibuf)) rs->itail = rs->ibuf;
637   }
638 
639   return 0;
640 }
641 
642 /*===========================================================================*
643  *				rs_ostart				     *
644  *===========================================================================*/
645 static void rs_ostart(rs232_t *rs)
646 {
647 /* Tell RS232 there is something waiting in the output buffer. */
648 
649   rs->ostate |= OQUEUED;
650   if (txready(rs)) out_int(rs);
651 }
652 
653 /*===========================================================================*
654  *				rs_break_on				     *
655  *===========================================================================*/
656 static int rs_break_on(tty_t *tp, int UNUSED(dummy))
657 {
658 /* Raise break condition. */
659   rs232_t *rs = tp->tty_priv;
660   u32_t line_controls;
661   int s;
662 
663   if ((s = sys_inb(rs->line_ctl_port, &line_controls)) != OK)
664 	printf("TTY: sys_inb() failed: %d", s);
665   sys_outb(rs->line_ctl_port, line_controls | LC_BREAK);
666   return 0;	/* dummy */
667 }
668 
669 /*===========================================================================*
670  *				rs_break_off				     *
671  *===========================================================================*/
672 static int rs_break_off(tty_t *tp, int UNUSED(dummy))
673 {
674 /* Clear break condition. */
675   rs232_t *rs = tp->tty_priv;
676   u32_t line_controls;
677   int s;
678 
679   if ((s = sys_inb(rs->line_ctl_port, &line_controls)) != OK)
680 	printf("TTY: sys_inb() failed: %d", s);
681   sys_outb(rs->line_ctl_port, line_controls & ~LC_BREAK);
682   return 0;	/* dummy */
683 }
684 
685 /*===========================================================================*
686  *				rs_close				     *
687  *===========================================================================*/
688 static int rs_close(tty_t *tp, int UNUSED(dummy))
689 {
690 /* The line is closed; optionally hang up. */
691   rs232_t *rs = tp->tty_priv;
692 
693   if (tp->tty_termios.c_cflag & HUPCL) {
694 	sys_outb(rs->modem_ctl_port, MC_OUT2 | MC_RTS);
695   }
696   return 0;	/* dummy */
697 }
698 
699 /* Low level (interrupt) routines. */
700 
701 /*===========================================================================*
702  *				rs232_handler				     *
703  *===========================================================================*/
704 static void rs232_handler(struct rs232 *rs)
705 {
706 /* Interrupt hander for RS232. */
707   int s;
708   int trying = 1000;
709 
710   while (trying--) {
711 	u32_t v;
712 	/* Loop to pick up ALL pending interrupts for device.
713 	 * This usually just wastes time unless the hardware has a buffer
714 	 * (and then we have to worry about being stuck in the loop too long).
715 	 * Unfortunately, some serial cards lock up without this.
716 	 */
717 	if ((s = sys_inb(rs->int_id_port, &v)) != OK)
718 		panic("TTY: sys_inb() failed: %d", s);
719 
720 	/* do we have interrupt info? */
721 	if(v & IS_NOTPENDING) return;
722 
723 	/* what kind of interrupt? */
724 	switch (v & IS_IDBITS) {
725 	case IS_RECEIVER_READY:
726 		in_int(rs);
727 		continue;
728 	case IS_TRANSMITTER_READY:
729 		out_int(rs);
730 		continue;
731 	case IS_MODEM_STATUS_CHANGE:
732 		modem_int(rs);
733 		continue;
734 	case IS_LINE_STATUS_CHANGE:
735 		line_int(rs);
736 		continue;
737 	}
738 	return;
739   }
740 
741   printf("tty rs232: enough!\n");
742 }
743 
744 /*===========================================================================*
745  *				in_int					     *
746  *===========================================================================*/
747 static void in_int(register rs232_t *rs)
748 /* rs	line with input interrupt */
749 {
750 /* Read the data which just arrived.
751  * If it is the oxoff char, clear OSWREADY, else if OSWREADY was clear, set
752  * it and restart output (any char does this, not just xon).
753  * Put data in the buffer if room, otherwise discard it.
754  * Set a flag for the clock interrupt handler to eventually notify TTY.
755  */
756   int s;
757   u32_t c;
758 
759 #if 0	/* Enable this if you want serial input in the kernel */
760   return;
761 #endif
762 
763   if ((s = sys_inb(rs->recv_port, &c)) != OK)
764 	printf("TTY: sys_inb() failed: %d", s);
765 
766   if (!(rs->ostate & ORAW)) {
767 	if (c == rs->oxoff) {
768 		rs->ostate &= ~OSWREADY;
769 	} else
770 	if (!(rs->ostate & OSWREADY)) {
771 		rs->ostate |= OSWREADY;
772 		if (txready(rs)) out_int(rs);
773 	}
774   }
775 
776   if (rs->icount == buflen(rs->ibuf))
777   {
778 	printf("in_int: discarding byte\n");
779 	return;	/* input buffer full, discard */
780   }
781 
782   if (++rs->icount == RS_IHIGHWATER && rs->idevready) istop(rs);
783   *rs->ihead = c;
784   if (++rs->ihead == bufend(rs->ibuf)) rs->ihead = rs->ibuf;
785   if (rs->icount == 1) {
786 	rs->tty->tty_events = 1;
787 	force_timeout();
788   }
789 }
790 
791 /*===========================================================================*
792  *				line_int				     *
793  *===========================================================================*/
794 static void line_int(register rs232_t *rs)
795 /* rs		line with line status interrupt */
796 {
797 /* Check for and record errors. */
798   int r;
799   u32_t s;
800 
801   if ((r = sys_inb(rs->line_status_port, &s)) != OK)
802 	printf("TTY: sys_inb() failed: %d", r);
803   rs->lstatus = s;
804   if (rs->lstatus & LS_FRAMING_ERR) ++rs->framing_errors;
805   if (rs->lstatus & LS_OVERRUN_ERR) ++rs->overrun_errors;
806   if (rs->lstatus & LS_PARITY_ERR) ++rs->parity_errors;
807   if (rs->lstatus & LS_BREAK_INTERRUPT) ++rs->break_interrupts;
808 }
809 
810 /*===========================================================================*
811  *				modem_int				     *
812  *===========================================================================*/
813 static void modem_int(register rs232_t *rs)
814 /* rs	line with modem interrupt */
815 {
816 /* Get possibly new device-ready status, and clear ODEVREADY if necessary.
817  * If the device just became ready, restart output.
818  */
819 
820   if (devhup(rs)) {
821 	rs->ostate |= ODEVHUP;
822 	rs->tty->tty_events = 1;
823 	force_timeout();
824   }
825 
826   if (!devready(rs))
827 	rs->ostate &= ~ODEVREADY;
828   else if (!(rs->ostate & ODEVREADY)) {
829 	rs->ostate |= ODEVREADY;
830 	if (txready(rs)) out_int(rs);
831   }
832 }
833 
834 /*===========================================================================*
835  *				out_int					    *
836  *===========================================================================*/
837 static void out_int(register rs232_t *rs)
838 /* rs;	line with output interrupt */
839 {
840 /* If there is output to do and everything is ready, do it (local device is
841  * known ready).
842  * Notify TTY when the buffer goes empty.
843  */
844 
845   while (txready(rs) && rs->ostate >= (ODEVREADY | OQUEUED | OSWREADY)) {
846 	/* Bit test allows ORAW and requires the others. */
847 	sys_outb(rs->xmit_port, *rs->otail);
848 	if (++rs->otail == bufend(rs->obuf)) rs->otail = rs->obuf;
849 	if (--rs->ocount == 0) {
850 		rs->ostate ^= (ODONE | OQUEUED);  /* ODONE on, OQUEUED off */
851 		rs->tty->tty_events = 1;
852 		force_timeout();
853 	} else
854 	if (rs->ocount == RS_OLOWWATER) {	/* running low? */
855 		rs->tty->tty_events = 1;
856 		force_timeout();
857 	}
858   }
859 }
860 #endif /* NR_RS_LINES > 0 */
861 
862