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