i386 cpu.h
[minix3.git] / drivers / tty / arch / i386 / rs232.c
blobd98979d3dca3a38e0783c23666ef3ab06a6dc1df
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 *---------------------------------------------------------------------------*/
7 #include <minix/drivers.h>
8 #include <sys/termios.h>
9 #include <signal.h>
10 #include "tty.h"
12 #if NR_RS_LINES > 0
14 /* 8250 constants. */
15 #define UART_FREQ 115200L /* timer frequency */
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
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
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
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
49 /* Modem control bits. */
50 #define MC_DTR 1
51 #define MC_RTS 2
52 #define MC_OUT2 8 /* required for PC & AT interrupts */
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 */
59 #define DATA_BITS_SHIFT 8 /* amount data bits shifted in mode */
60 #define DEF_BAUD 1200 /* default baud rate */
62 #define RS_IBUFSIZE 1024 /* RS232 input buffer size */
63 #define RS_OBUFSIZE 1024 /* RS232 output buffer size */
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.
70 #define RS_ILOWWATER (1 * RS_IBUFSIZE / 4)
71 #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
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.
77 #define RS_OLOWWATER (1 * RS_OBUFSIZE / 4)
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.
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)
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.
99 #define devready(rs) ((my_inb(rs->modem_status_port) | rs->cts) & MS_CTS)
101 /* Macro to tell if transmitter is ready. */
102 #define txready(rs) (my_inb(rs->line_status_port) & LS_TRANSMITTER_READY)
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.
111 #define devhup(rs) \
112 ((my_inb(rs->modem_status_port) & (MS_RLSD|MS_DRLSD)) == MS_DRLSD)
114 /* Types. */
115 typedef unsigned char bool_t; /* boolean */
117 /* RS232 device structure, one per device. */
118 typedef struct rs232 {
119 tty_t *tty; /* associated TTY structure */
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 */
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 */
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
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;
168 int irq; /* irq for this line */
169 int irq_hook_id; /* interrupt hook */
171 char ibuf[RS_IBUFSIZE]; /* input buffer */
172 char obuf[RS_OBUFSIZE]; /* output buffer */
173 } rs232_t;
175 static rs232_t rs_lines[NR_RS_LINES];
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 */
185 #endif
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);
204 static int my_inb(port_t port)
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);
212 return (int) v;
215 /*===========================================================================*
216 * rs_write *
217 *===========================================================================*/
218 static int rs_write(register tty_t *tp, int try)
220 /* (*devwrite)() routine for RS232. */
222 rs232_t *rs = tp->tty_priv;
223 int r, count, ocount;
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;
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);
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;
249 if (try) return 1;
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);
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;
266 /* Assume echoing messed up by output. */
267 tp->tty_reprint = TRUE;
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 chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
278 tp->tty_outcum);
279 tp->tty_outcum = 0;
280 tp->tty_outcaller = NONE;
283 if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
284 /* Oops, the line has hung up. */
285 chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
286 tp->tty_outleft = tp->tty_outcum = 0;
287 tp->tty_outcaller = NONE;
290 return 1;
293 /*===========================================================================*
294 * rs_echo *
295 *===========================================================================*/
296 static void rs_echo(tp, c)
297 tty_t *tp; /* which TTY */
298 int c; /* character to echo */
300 /* Echo one character. (Like rs_write, but only one character, optionally.) */
302 rs232_t *rs = tp->tty_priv;
303 int count, ocount;
305 ocount = buflen(rs->obuf) - rs->ocount;
306 if (ocount == 0) return; /* output buffer full */
307 count = 1;
308 *rs->ohead = c; /* add one character */
310 out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
311 if (count == 0) return;
313 rs->ocount += ocount;
314 rs_ostart(rs);
315 if ((rs->ohead += ocount) >= bufend(rs->obuf)) rs->ohead -= buflen(rs->obuf);
318 /*===========================================================================*
319 * rs_ioctl *
320 *===========================================================================*/
321 static int rs_ioctl(tty_t *tp, int UNUSED(dummy))
322 /* tp; which TTY */
324 /* Reconfigure the line as soon as the output has drained. */
325 rs232_t *rs = tp->tty_priv;
327 rs->drain = TRUE;
328 return 0; /* dummy */
331 /*===========================================================================*
332 * rs_config *
333 *===========================================================================*/
334 static void rs_config(rs232_t *rs)
335 /* rs which line */
337 /* Set various line control parameters for RS232 I/O.
338 * If DataBits == 5 and StopBits == 2, 8250 will generate 1.5 stop bits.
339 * The 8250 can't handle split speed, so we use the input speed.
342 tty_t *tp = rs->tty;
343 int divisor;
344 int line_controls;
345 static struct speed2divisor {
346 speed_t speed;
347 int divisor;
348 } s2d[] = {
349 #if defined(__i386__)
350 { B50, UART_FREQ / 50 },
351 #endif
352 { B75, UART_FREQ / 75 },
353 { B110, UART_FREQ / 110 },
354 { B134, UART_FREQ * 10 / 1345 },
355 { B150, UART_FREQ / 150 },
356 { B200, UART_FREQ / 200 },
357 { B300, UART_FREQ / 300 },
358 { B600, UART_FREQ / 600 },
359 { B1200, UART_FREQ / 1200 },
360 #if defined(__i386__)
361 { B1800, UART_FREQ / 1800 },
362 #endif
363 { B2400, UART_FREQ / 2400 },
364 { B4800, UART_FREQ / 4800 },
365 { B9600, UART_FREQ / 9600 },
366 { B19200, UART_FREQ / 19200 },
367 #if defined(__i386__)
368 { B38400, UART_FREQ / 38400 },
369 { B57600, UART_FREQ / 57600 },
370 { B115200, UART_FREQ / 115200L },
371 #endif
373 struct speed2divisor *s2dp;
375 /* RS232 needs to know the xoff character, and if CTS works. */
376 rs->oxoff = tp->tty_termios.c_cc[VSTOP];
377 rs->cts = (tp->tty_termios.c_cflag & CLOCAL) ? MS_CTS : 0;
379 /* Look up the 8250 rate divisor from the output speed. */
380 divisor = 0;
381 for (s2dp = s2d; s2dp < s2d + sizeof(s2d)/sizeof(s2d[0]); s2dp++) {
382 if (s2dp->speed == tp->tty_termios.c_ospeed) divisor = s2dp->divisor;
384 if (divisor == 0) return; /* B0? */
386 /* Compute line control flag bits. */
387 line_controls = 0;
388 if (tp->tty_termios.c_cflag & PARENB) {
389 line_controls |= LC_PARITY;
390 if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= LC_PAREVEN;
393 if (divisor >= (UART_FREQ / 110)) line_controls |= LC_2STOP_BITS;
395 /* which word size is configured? set the bits explicitly. */
396 if((tp->tty_termios.c_cflag & CSIZE) == CS5)
397 line_controls |= LC_CS5;
398 else if((tp->tty_termios.c_cflag & CSIZE) == CS6)
399 line_controls |= LC_CS6;
400 else if((tp->tty_termios.c_cflag & CSIZE) == CS7)
401 line_controls |= LC_CS7;
402 else if((tp->tty_termios.c_cflag & CSIZE) == CS8)
403 line_controls |= LC_CS8;
404 else printf("rs232: warning: no known word size set\n");
406 /* Select the baud rate divisor registers and change the rate. */
407 sys_outb(rs->line_ctl_port, LC_ADDRESS_DIVISOR);
408 sys_outb(rs->div_low_port, divisor);
409 sys_outb(rs->div_hi_port, divisor >> 8);
411 /* Change the line controls and reselect the usual registers. */
412 sys_outb(rs->line_ctl_port, line_controls);
414 rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
415 if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE)
416 rs->ostate &= ~ORAW;
419 /*===========================================================================*
420 * rs_init *
421 *===========================================================================*/
422 void rs_init(tty_t *tp)
423 /* tp which TTY */
425 u32_t dummy;
426 /* Initialize RS232 for one line. */
428 register rs232_t *rs;
429 int line;
430 port_t this_8250;
431 int s, irq;
432 char l[10];
434 /* Associate RS232 and TTY structures. */
435 line = tp - &tty_table[NR_CONS];
437 /* See if kernel debugging is enabled; if so, don't initialize this
438 * serial line, making tty not look at the irq and returning ENXIO
439 * for all requests on it from userland. (The kernel will use it.)
441 if(env_get_param(SERVARNAME, l, sizeof(l)-1) == OK && atoi(l) == line) {
442 printf("TTY: not initializing rs232 line %d (in use by kernel)\n",
443 line);
444 return;
447 rs = tp->tty_priv = &rs_lines[line];
448 rs->tty = tp;
450 /* Set up input queue. */
451 rs->ihead = rs->itail = rs->ibuf;
453 /* Precalculate port numbers for speed. Magic numbers in the code (once). */
454 this_8250 = addr_8250[line];
455 rs->xmit_port = this_8250 + 0;
456 rs->recv_port = this_8250 + 0;
457 rs->div_low_port = this_8250 + 0;
458 rs->div_hi_port = this_8250 + 1;
459 rs->int_enab_port = this_8250 + 1;
460 rs->int_id_port = this_8250 + 2;
461 rs->line_ctl_port = this_8250 + 3;
462 rs->modem_ctl_port = this_8250 + 4;
463 rs->line_status_port = this_8250 + 5;
464 rs->modem_status_port = this_8250 + 6;
466 /* Set up the hardware to a base state, in particular
467 * o turn off DTR (MC_DTR) to try to stop the external device.
468 * o be careful about the divisor latch. Some BIOS's leave it enabled
469 * here and that caused trouble (no interrupts) in version 1.5 by
470 * hiding the interrupt enable port in the next step, and worse trouble
471 * (continual interrupts) in an old version by hiding the receiver
472 * port in the first interrupt. Call rs_ioctl() early to avoid this.
473 * o disable interrupts at the chip level, to force an edge transition
474 * on the 8259 line when interrupts are next enabled and active.
475 * RS232 interrupts are guaranteed to be disabled now by the 8259
476 * mask, but there used to be trouble if the mask was set without
477 * handling a previous interrupt.
479 istop(rs); /* sets modem_ctl_port */
480 rs_config(rs);
481 sys_outb(rs->int_enab_port, 0);
483 /* Clear any harmful leftover interrupts. An output interrupt is harmless
484 * and will occur when interrupts are enabled anyway. Set up the output
485 * queue using the status from clearing the modem status interrupt.
487 if ((s = sys_inb(rs->line_status_port, &dummy)) != OK)
488 printf("TTY: sys_inb() failed: %d", s);
489 if ((s = sys_inb(rs->recv_port, &dummy)) != OK)
490 printf("TTY: sys_inb() failed: %d", s);
491 rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
492 rs->ohead = rs->otail = rs->obuf;
494 /* Enable interrupts for both interrupt controller and device. */
495 irq = (line & 1) == 0 ? RS232_IRQ : SECONDARY_IRQ;
497 rs->irq = irq;
498 rs->irq_hook_id = rs->irq; /* call back with irq line number */
499 if (sys_irqsetpolicy(irq, IRQ_REENABLE, &rs->irq_hook_id) != OK) {
500 printf("RS232: Couldn't obtain hook for irq %d\n", irq);
501 } else {
502 if (sys_irqenable(&rs->irq_hook_id) != OK) {
503 printf("RS232: Couldn't enable irq %d (hooked)\n", irq);
507 rs_irq_set |= (1 << irq);
509 sys_outb(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_MODEM_STATUS_CHANGE
510 | IE_RECEIVER_READY | IE_TRANSMITTER_READY);
512 /* Fill in TTY function hooks. */
513 tp->tty_devread = rs_read;
514 tp->tty_devwrite = rs_write;
515 tp->tty_echo = rs_echo;
516 tp->tty_icancel = rs_icancel;
517 tp->tty_ocancel = rs_ocancel;
518 tp->tty_ioctl = rs_ioctl;
519 tp->tty_break_on = rs_break_on;
520 tp->tty_break_off = rs_break_off;
521 tp->tty_close = rs_close;
523 /* Tell external device we are ready. */
524 istart(rs);
528 /*===========================================================================*
529 * rs_interrupt *
530 *===========================================================================*/
531 void rs_interrupt(message *m)
533 unsigned long irq_set;
534 int i;
535 rs232_t *rs;
537 irq_set= m->m_notify.interrupts;
538 for (i= 0, rs = rs_lines; i<NR_RS_LINES; i++, rs++)
540 if (irq_set & (1 << rs->irq))
541 rs232_handler(rs);
545 /*===========================================================================*
546 * rs_icancel *
547 *===========================================================================*/
548 static int rs_icancel(tty_t *tp, int UNUSED(dummy))
550 /* Cancel waiting input. */
551 rs232_t *rs = tp->tty_priv;
553 rs->icount = 0;
554 rs->itail = rs->ihead;
555 istart(rs);
557 return 0; /* dummy */
560 /*===========================================================================*
561 * rs_ocancel *
562 *===========================================================================*/
563 static int rs_ocancel(tty_t *tp, int UNUSED(dummy))
565 /* Cancel pending output. */
566 rs232_t *rs = tp->tty_priv;
568 rs->ostate &= ~(ODONE | OQUEUED);
569 rs->ocount = 0;
570 rs->otail = rs->ohead;
572 return 0; /* dummy */
575 /*===========================================================================*
576 * rs_read *
577 *===========================================================================*/
578 static int rs_read(tty_t *tp, int try)
580 /* Process characters from the circular input buffer. */
582 rs232_t *rs = tp->tty_priv;
583 int icount, count, ostate;
585 if (!(tp->tty_termios.c_cflag & CLOCAL)) {
586 if (try) return 1;
587 /* Send a SIGHUP if hangup detected. */
588 ostate = rs->ostate;
589 rs->ostate &= ~ODEVHUP; /* save ostate, clear DEVHUP */
590 if (ostate & ODEVHUP) {
591 sigchar(tp, SIGHUP, 1);
592 tp->tty_termios.c_ospeed = B0; /* Disable further I/O. */
593 return 0;
597 if (try) {
598 if (rs->icount > 0)
599 return 1;
600 return 0;
603 while ((count = rs->icount) > 0) {
604 icount = bufend(rs->ibuf) - rs->itail;
605 if (count > icount) count = icount;
607 /* Perform input processing on (part of) the input buffer. */
608 if ((count = in_process(tp, rs->itail, count)) == 0) break;
610 rs->icount -= count;
611 if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs);
612 if ((rs->itail += count) == bufend(rs->ibuf)) rs->itail = rs->ibuf;
615 return 0;
618 /*===========================================================================*
619 * rs_ostart *
620 *===========================================================================*/
621 static void rs_ostart(rs232_t *rs)
623 /* Tell RS232 there is something waiting in the output buffer. */
625 rs->ostate |= OQUEUED;
626 if (txready(rs)) out_int(rs);
629 /*===========================================================================*
630 * rs_break_on *
631 *===========================================================================*/
632 static int rs_break_on(tty_t *tp, int UNUSED(dummy))
634 /* Raise break condition. */
635 rs232_t *rs = tp->tty_priv;
636 u32_t line_controls;
637 int s;
639 if ((s = sys_inb(rs->line_ctl_port, &line_controls)) != OK)
640 printf("TTY: sys_inb() failed: %d", s);
641 sys_outb(rs->line_ctl_port, line_controls | LC_BREAK);
642 return 0; /* dummy */
645 /*===========================================================================*
646 * rs_break_off *
647 *===========================================================================*/
648 static int rs_break_off(tty_t *tp, int UNUSED(dummy))
650 /* Clear break condition. */
651 rs232_t *rs = tp->tty_priv;
652 u32_t line_controls;
653 int s;
655 if ((s = sys_inb(rs->line_ctl_port, &line_controls)) != OK)
656 printf("TTY: sys_inb() failed: %d", s);
657 sys_outb(rs->line_ctl_port, line_controls & ~LC_BREAK);
658 return 0; /* dummy */
661 /*===========================================================================*
662 * rs_close *
663 *===========================================================================*/
664 static int rs_close(tty_t *tp, int UNUSED(dummy))
666 /* The line is closed; optionally hang up. */
667 rs232_t *rs = tp->tty_priv;
669 if (tp->tty_termios.c_cflag & HUPCL) {
670 sys_outb(rs->modem_ctl_port, MC_OUT2 | MC_RTS);
672 return 0; /* dummy */
675 /* Low level (interrupt) routines. */
677 /*===========================================================================*
678 * rs232_handler *
679 *===========================================================================*/
680 static void rs232_handler(struct rs232 *rs)
682 /* Interrupt hander for RS232. */
683 int s;
684 int trying = 1000;
686 while (trying--) {
687 u32_t v;
688 /* Loop to pick up ALL pending interrupts for device.
689 * This usually just wastes time unless the hardware has a buffer
690 * (and then we have to worry about being stuck in the loop too long).
691 * Unfortunately, some serial cards lock up without this.
693 if ((s = sys_inb(rs->int_id_port, &v)) != OK)
694 panic("TTY: sys_inb() failed: %d", s);
696 /* do we have interrupt info? */
697 if(v & IS_NOTPENDING) return;
699 /* what kind of interrupt? */
700 switch (v & IS_IDBITS) {
701 case IS_RECEIVER_READY:
702 in_int(rs);
703 continue;
704 case IS_TRANSMITTER_READY:
705 out_int(rs);
706 continue;
707 case IS_MODEM_STATUS_CHANGE:
708 modem_int(rs);
709 continue;
710 case IS_LINE_STATUS_CHANGE:
711 line_int(rs);
712 continue;
714 return;
717 printf("tty rs232: enough!\n");
720 /*===========================================================================*
721 * in_int *
722 *===========================================================================*/
723 static void in_int(register rs232_t *rs)
724 /* rs line with input interrupt */
726 /* Read the data which just arrived.
727 * If it is the oxoff char, clear OSWREADY, else if OSWREADY was clear, set
728 * it and restart output (any char does this, not just xon).
729 * Put data in the buffer if room, otherwise discard it.
730 * Set a flag for the clock interrupt handler to eventually notify TTY.
732 int s;
733 u32_t c;
735 #if 0 /* Enable this if you want serial input in the kernel */
736 return;
737 #endif
739 if ((s = sys_inb(rs->recv_port, &c)) != OK)
740 printf("TTY: sys_inb() failed: %d", s);
742 if (!(rs->ostate & ORAW)) {
743 if (c == rs->oxoff) {
744 rs->ostate &= ~OSWREADY;
745 } else
746 if (!(rs->ostate & OSWREADY)) {
747 rs->ostate |= OSWREADY;
748 if (txready(rs)) out_int(rs);
752 if (rs->icount == buflen(rs->ibuf))
754 printf("in_int: discarding byte\n");
755 return; /* input buffer full, discard */
758 if (++rs->icount == RS_IHIGHWATER && rs->idevready) istop(rs);
759 *rs->ihead = c;
760 if (++rs->ihead == bufend(rs->ibuf)) rs->ihead = rs->ibuf;
761 if (rs->icount == 1) {
762 rs->tty->tty_events = 1;
763 force_timeout();
767 /*===========================================================================*
768 * line_int *
769 *===========================================================================*/
770 static void line_int(register rs232_t *rs)
771 /* rs line with line status interrupt */
773 /* Check for and record errors. */
774 int r;
775 u32_t s;
777 if ((r = sys_inb(rs->line_status_port, &s)) != OK)
778 printf("TTY: sys_inb() failed: %d", r);
779 rs->lstatus = s;
780 if (rs->lstatus & LS_FRAMING_ERR) ++rs->framing_errors;
781 if (rs->lstatus & LS_OVERRUN_ERR) ++rs->overrun_errors;
782 if (rs->lstatus & LS_PARITY_ERR) ++rs->parity_errors;
783 if (rs->lstatus & LS_BREAK_INTERRUPT) ++rs->break_interrupts;
786 /*===========================================================================*
787 * modem_int *
788 *===========================================================================*/
789 static void modem_int(register rs232_t *rs)
790 /* rs line with modem interrupt */
792 /* Get possibly new device-ready status, and clear ODEVREADY if necessary.
793 * If the device just became ready, restart output.
796 if (devhup(rs)) {
797 rs->ostate |= ODEVHUP;
798 rs->tty->tty_events = 1;
799 force_timeout();
802 if (!devready(rs))
803 rs->ostate &= ~ODEVREADY;
804 else if (!(rs->ostate & ODEVREADY)) {
805 rs->ostate |= ODEVREADY;
806 if (txready(rs)) out_int(rs);
810 /*===========================================================================*
811 * out_int *
812 *===========================================================================*/
813 static void out_int(register rs232_t *rs)
814 /* rs; line with output interrupt */
816 /* If there is output to do and everything is ready, do it (local device is
817 * known ready).
818 * Notify TTY when the buffer goes empty.
821 if (rs->ostate >= (ODEVREADY | OQUEUED | OSWREADY)) {
822 /* Bit test allows ORAW and requires the others. */
823 sys_outb(rs->xmit_port, *rs->otail);
824 if (++rs->otail == bufend(rs->obuf)) rs->otail = rs->obuf;
825 if (--rs->ocount == 0) {
826 rs->ostate ^= (ODONE | OQUEUED); /* ODONE on, OQUEUED off */
827 rs->tty->tty_events = 1;
828 force_timeout();
829 } else
830 if (rs->ocount == RS_OLOWWATER) { /* running low? */
831 rs->tty->tty_events = 1;
832 force_timeout();
836 #endif /* NR_RS_LINES > 0 */