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>
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_TRANSMITTER_READY 2
26 #define IS_RECEIVER_READY 4
27 #define IS_LINE_STATUS_CHANGE 6
29 /* Line control bits. */
30 #define LC_2STOP_BITS 0x04
31 #define LC_PARITY 0x08
32 #define LC_PAREVEN 0x10
34 #define LC_ADDRESS_DIVISOR 0x80
36 /* Line status bits. */
37 #define LS_OVERRUN_ERR 2
38 #define LS_PARITY_ERR 4
39 #define LS_FRAMING_ERR 8
40 #define LS_BREAK_INTERRUPT 0x10
41 #define LS_TRANSMITTER_READY 0x20
43 /* Modem control bits. */
46 #define MC_OUT2 8 /* required for PC & AT interrupts */
48 /* Modem status bits. */
50 #define MS_RLSD 0x80 /* Received Line Signal Detect */
51 #define MS_DRLSD 0x08 /* RLSD Delta */
53 #define DATA_BITS_SHIFT 8 /* amount data bits shifted in mode */
54 #define DEF_BAUD 1200 /* default baud rate */
56 #define RS_IBUFSIZE 1024 /* RS232 input buffer size */
57 #define RS_OBUFSIZE 1024 /* RS232 output buffer size */
59 /* Input buffer watermarks.
60 * The external device is asked to stop sending when the buffer
61 * exactly reaches high water, or when TTY requests it. Sending restarts
62 * when the input buffer empties below the low watermark.
64 #define RS_ILOWWATER (1 * RS_IBUFSIZE / 4)
65 #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
67 /* Output buffer low watermark.
68 * TTY is notified when the output buffer empties below the low watermark, so
69 * it may continue filling the buffer if doing a large write.
71 #define RS_OLOWWATER (1 * RS_OBUFSIZE / 4)
73 /* Macros to handle flow control.
74 * Interrupts must be off when they are used.
75 * Time is critical - already the function call for outb() is annoying.
76 * If outb() can be done in-line, tests to avoid it can be dropped.
77 * istart() tells external device we are ready by raising RTS.
78 * istop() tells external device we are not ready by dropping RTS.
79 * DTR is kept high all the time (it probably should be raised by open and
80 * dropped by close of the device).
81 * OUT2 is also kept high all the time.
84 (sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_RTS | MC_DTR), \
85 (rs)->idevready = TRUE)
87 (sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_DTR), \
88 (rs)->idevready = FALSE)
90 /* Macro to tell if device is ready. The rs->cts field is set to MS_CTS if
91 * CLOCAL is in effect for a line without a CTS wire.
93 #define devready(rs) ((my_inb(rs->modem_status_port) | rs->cts) & MS_CTS)
95 /* Macro to tell if transmitter is ready. */
96 #define txready(rs) (my_inb(rs->line_status_port) & LS_TRANSMITTER_READY)
98 /* Macro to tell if carrier has dropped.
99 * The RS232 Carrier Detect (CD) line is usually connected to the 8250
100 * Received Line Signal Detect pin, reflected by bit MS_RLSD in the Modem
101 * Status Register. The MS_DRLSD bit tells if MS_RLSD has just changed state.
102 * So if MS_DRLSD is set and MS_RLSD cleared, we know that carrier has just
106 ((my_inb(rs->modem_status_port) & (MS_RLSD|MS_DRLSD)) == MS_DRLSD)
109 typedef unsigned char bool_t
; /* boolean */
111 /* RS232 device structure, one per device. */
112 typedef struct rs232
{
113 tty_t
*tty
; /* associated TTY structure */
115 int icount
; /* number of bytes in the input buffer */
116 char *ihead
; /* next free spot in input buffer */
117 char *itail
; /* first byte to give to TTY */
118 bool_t idevready
; /* nonzero if we are ready to receive (RTS) */
119 char cts
; /* normally 0, but MS_CTS if CLOCAL is set */
121 unsigned char ostate
; /* combination of flags: */
122 #define ODONE 1 /* output completed (< output enable bits) */
123 #define ORAW 2 /* raw mode for xoff disable (< enab. bits) */
124 #define OWAKEUP 4 /* tty_wakeup() pending (asm code only) */
125 #define ODEVREADY MS_CTS /* external device hardware ready (CTS) */
126 #define OQUEUED 0x20 /* output buffer not empty */
127 #define OSWREADY 0x40 /* external device software ready (no xoff) */
128 #define ODEVHUP MS_RLSD /* external device has dropped carrier */
129 #define OSOFTBITS (ODONE | ORAW | OWAKEUP | OQUEUED | OSWREADY)
130 /* user-defined bits */
131 #if (OSOFTBITS | ODEVREADY | ODEVHUP) == OSOFTBITS
132 /* a weak sanity check */
133 #error /* bits are not unique */
135 unsigned char oxoff
; /* char to stop output */
136 bool_t inhibited
; /* output inhibited? (follows tty_inhibited) */
137 bool_t drain
; /* if set drain output and reconfigure line */
138 int ocount
; /* number of bytes in the output buffer */
139 char *ohead
; /* next free spot in output buffer */
140 char *otail
; /* next char to output */
142 #if defined(__i386__)
143 port_t xmit_port
; /* i/o ports */
147 port_t int_enab_port
;
149 port_t line_ctl_port
;
150 port_t modem_ctl_port
;
151 port_t line_status_port
;
152 port_t modem_status_port
;
155 unsigned char lstatus
; /* last line status */
156 unsigned char pad
; /* ensure alignment for 16-bit ints */
157 unsigned framing_errors
; /* error counts (no reporting yet) */
158 unsigned overrun_errors
;
159 unsigned parity_errors
;
160 unsigned break_interrupts
;
162 int irq
; /* irq for this line */
163 int irq_hook_id
; /* interrupt hook */
165 char ibuf
[RS_IBUFSIZE
]; /* input buffer */
166 char obuf
[RS_OBUFSIZE
]; /* output buffer */
169 static rs232_t rs_lines
[NR_RS_LINES
];
171 #if defined(__i386__)
172 /* 8250 base addresses. */
173 static port_t addr_8250
[] = {
181 static void in_int(rs232_t
*rs
);
182 static void line_int(rs232_t
*rs
);
183 static void modem_int(rs232_t
*rs
);
184 static int rs_write(tty_t
*tp
, int try);
185 static void rs_echo(tty_t
*tp
, int c
);
186 static int rs_ioctl(tty_t
*tp
, int try);
187 static void rs_config(rs232_t
*rs
);
188 static int rs_read(tty_t
*tp
, int try);
189 static int rs_icancel(tty_t
*tp
, int try);
190 static int rs_ocancel(tty_t
*tp
, int try);
191 static void rs_ostart(rs232_t
*rs
);
192 static int rs_break(tty_t
*tp
, int try);
193 static int rs_close(tty_t
*tp
, int try);
194 static void out_int(rs232_t
*rs
);
195 static void rs232_handler(rs232_t
*rs
);
198 static void lock(void) {}
199 static void unlock(void) {}
201 static int my_inb(port_t port
)
205 r
= sys_inb(port
, &v
);
207 printf("RS232 warning: failed inb 0x%x\n", port
);
212 /*===========================================================================*
214 *===========================================================================*/
215 static int rs_write(register tty_t
*tp
, int try)
217 /* (*devwrite)() routine for RS232. */
219 rs232_t
*rs
= tp
->tty_priv
;
220 int r
, count
, ocount
;
222 if (rs
->inhibited
!= tp
->tty_inhibited
) {
223 /* Inhibition state has changed. */
225 rs
->ostate
|= OSWREADY
;
226 if (tp
->tty_inhibited
) rs
->ostate
&= ~OSWREADY
;
228 rs
->inhibited
= tp
->tty_inhibited
;
232 /* Wait for the line to drain then reconfigure and continue output. */
233 if (rs
->ocount
> 0) return 0;
238 /* While there is something to do. */
240 ocount
= buflen(rs
->obuf
) - rs
->ocount
;
241 count
= bufend(rs
->obuf
) - rs
->ohead
;
242 if (count
> ocount
) count
= ocount
;
243 if (count
> tp
->tty_outleft
) count
= tp
->tty_outleft
;
244 if (count
== 0 || tp
->tty_inhibited
) {
250 /* Copy from user space to the RS232 output buffer. */
251 if (tp
->tty_outcaller
== KERNEL
) {
252 /* We're trying to print on kernel's behalf */
253 memcpy(rs
->ohead
, (void *) tp
->tty_outgrant
+ tp
->tty_outoffset
, count
);
255 if ((r
= sys_safecopyfrom(tp
->tty_outcaller
, tp
->tty_outgrant
,
256 tp
->tty_outoffset
, (vir_bytes
) rs
->ohead
, count
)) != OK
)
257 printf("TTY: sys_safecopyfrom() failed: %d", r
);
260 /* Perform output processing on the output buffer. */
261 out_process(tp
, rs
->obuf
, rs
->ohead
, bufend(rs
->obuf
), &count
, &ocount
);
262 if (count
== 0) break;
264 /* Assume echoing messed up by output. */
265 tp
->tty_reprint
= TRUE
;
268 lock(); /* protect interrupt sensitive rs->ocount */
269 rs
->ocount
+= ocount
;
272 if ((rs
->ohead
+= ocount
) >= bufend(rs
->obuf
))
273 rs
->ohead
-= buflen(rs
->obuf
);
274 tp
->tty_outoffset
+= count
;
275 tp
->tty_outcum
+= count
;
276 if ((tp
->tty_outleft
-= count
) == 0) {
277 /* Output is finished, reply to the writer. */
278 if(tp
->tty_outrepcode
== TTY_REVIVE
) {
279 notify(tp
->tty_outcaller
);
280 tp
->tty_outrevived
= 1;
282 tty_reply(tp
->tty_outrepcode
, tp
->tty_outcaller
,
283 tp
->tty_outproc
, tp
->tty_outcum
);
288 if (tp
->tty_outleft
> 0 && tp
->tty_termios
.c_ospeed
== B0
) {
289 /* Oops, the line has hung up. */
290 if(tp
->tty_outrepcode
== TTY_REVIVE
) {
291 notify(tp
->tty_outcaller
);
292 tp
->tty_outrevived
= 1;
294 tty_reply(tp
->tty_outrepcode
, tp
->tty_outcaller
,
295 tp
->tty_outproc
, EIO
);
296 tp
->tty_outleft
= tp
->tty_outcum
= 0;
303 /*===========================================================================*
305 *===========================================================================*/
306 static void rs_echo(tp
, c
)
307 tty_t
*tp
; /* which TTY */
308 int c
; /* character to echo */
310 /* Echo one character. (Like rs_write, but only one character, optionally.) */
312 rs232_t
*rs
= tp
->tty_priv
;
315 ocount
= buflen(rs
->obuf
) - rs
->ocount
;
316 if (ocount
== 0) return; /* output buffer full */
318 *rs
->ohead
= c
; /* add one character */
320 out_process(tp
, rs
->obuf
, rs
->ohead
, bufend(rs
->obuf
), &count
, &ocount
);
321 if (count
== 0) return;
324 rs
->ocount
+= ocount
;
327 if ((rs
->ohead
+= ocount
) >= bufend(rs
->obuf
)) rs
->ohead
-= buflen(rs
->obuf
);
330 /*===========================================================================*
332 *===========================================================================*/
333 static int rs_ioctl(tty_t
*tp
, int UNUSED(dummy
))
336 /* Reconfigure the line as soon as the output has drained. */
337 rs232_t
*rs
= tp
->tty_priv
;
340 return 0; /* dummy */
343 /*===========================================================================*
345 *===========================================================================*/
346 static void rs_config(rs232_t
*rs
)
349 /* Set various line control parameters for RS232 I/O.
350 * If DataBits == 5 and StopBits == 2, 8250 will generate 1.5 stop bits.
351 * The 8250 can't handle split speed, so we use the input speed.
357 static struct speed2divisor
{
361 #if defined(__i386__)
362 { B50
, UART_FREQ
/ 50 },
364 { B75
, UART_FREQ
/ 75 },
365 { B110
, UART_FREQ
/ 110 },
366 { B134
, UART_FREQ
* 10 / 1345 },
367 { B150
, UART_FREQ
/ 150 },
368 { B200
, UART_FREQ
/ 200 },
369 { B300
, UART_FREQ
/ 300 },
370 { B600
, UART_FREQ
/ 600 },
371 { B1200
, UART_FREQ
/ 1200 },
372 #if defined(__i386__)
373 { B1800
, UART_FREQ
/ 1800 },
375 { B2400
, UART_FREQ
/ 2400 },
376 { B4800
, UART_FREQ
/ 4800 },
377 { B9600
, UART_FREQ
/ 9600 },
378 { B19200
, UART_FREQ
/ 19200 },
379 #if defined(__i386__)
380 { B38400
, UART_FREQ
/ 38400 },
381 { B57600
, UART_FREQ
/ 57600 },
382 { B115200
, UART_FREQ
/ 115200L },
385 struct speed2divisor
*s2dp
;
387 /* RS232 needs to know the xoff character, and if CTS works. */
388 rs
->oxoff
= tp
->tty_termios
.c_cc
[VSTOP
];
389 rs
->cts
= (tp
->tty_termios
.c_cflag
& CLOCAL
) ? MS_CTS
: 0;
391 /* Look up the 8250 rate divisor from the output speed. */
393 for (s2dp
= s2d
; s2dp
< s2d
+ sizeof(s2d
)/sizeof(s2d
[0]); s2dp
++) {
394 if (s2dp
->speed
== tp
->tty_termios
.c_ospeed
) divisor
= s2dp
->divisor
;
396 if (divisor
== 0) return; /* B0? */
398 /* Compute line control flag bits. */
400 if (tp
->tty_termios
.c_cflag
& PARENB
) {
401 line_controls
|= LC_PARITY
;
402 if (!(tp
->tty_termios
.c_cflag
& PARODD
)) line_controls
|= LC_PAREVEN
;
404 if (divisor
>= (UART_FREQ
/ 110)) line_controls
|= LC_2STOP_BITS
;
405 line_controls
|= (tp
->tty_termios
.c_cflag
& CSIZE
) >> 2;
407 /* Lock out interrupts while setting the speed. The receiver register is
408 * going to be hidden by the div_low register, but the input interrupt
409 * handler relies on reading it to clear the interrupt and avoid looping
414 /* Select the baud rate divisor registers and change the rate. */
415 sys_outb(rs
->line_ctl_port
, LC_ADDRESS_DIVISOR
);
416 sys_outb(rs
->div_low_port
, divisor
);
417 sys_outb(rs
->div_hi_port
, divisor
>> 8);
419 /* Change the line controls and reselect the usual registers. */
420 sys_outb(rs
->line_ctl_port
, line_controls
);
422 rs
->ostate
= devready(rs
) | ORAW
| OSWREADY
; /* reads modem_ctl_port */
423 if ((tp
->tty_termios
.c_lflag
& IXON
) && rs
->oxoff
!= _POSIX_VDISABLE
)
429 /*===========================================================================*
431 *===========================================================================*/
432 void rs_init(tty_t
*tp
)
436 /* Initialize RS232 for one line. */
438 register rs232_t
*rs
;
444 /* Associate RS232 and TTY structures. */
445 line
= tp
- &tty_table
[NR_CONS
];
447 /* See if kernel debugging is enabled; if so, don't initialize this
448 * serial line, making tty not look at the irq and returning ENXIO
449 * for all requests on it from userland. (The kernel will use it.)
451 if(env_get_param(SERVARNAME
, l
, sizeof(l
)-1) == OK
&& atoi(l
) == line
) {
452 printf("TTY: not initializing rs232 line %d (in use by kernel)\n",
457 rs
= tp
->tty_priv
= &rs_lines
[line
];
460 /* Set up input queue. */
461 rs
->ihead
= rs
->itail
= rs
->ibuf
;
463 /* Precalculate port numbers for speed. Magic numbers in the code (once). */
464 this_8250
= addr_8250
[line
];
465 rs
->xmit_port
= this_8250
+ 0;
466 rs
->recv_port
= this_8250
+ 0;
467 rs
->div_low_port
= this_8250
+ 0;
468 rs
->div_hi_port
= this_8250
+ 1;
469 rs
->int_enab_port
= this_8250
+ 1;
470 rs
->int_id_port
= this_8250
+ 2;
471 rs
->line_ctl_port
= this_8250
+ 3;
472 rs
->modem_ctl_port
= this_8250
+ 4;
473 rs
->line_status_port
= this_8250
+ 5;
474 rs
->modem_status_port
= this_8250
+ 6;
476 /* Set up the hardware to a base state, in particular
477 * o turn off DTR (MC_DTR) to try to stop the external device.
478 * o be careful about the divisor latch. Some BIOS's leave it enabled
479 * here and that caused trouble (no interrupts) in version 1.5 by
480 * hiding the interrupt enable port in the next step, and worse trouble
481 * (continual interrupts) in an old version by hiding the receiver
482 * port in the first interrupt. Call rs_ioctl() early to avoid this.
483 * o disable interrupts at the chip level, to force an edge transition
484 * on the 8259 line when interrupts are next enabled and active.
485 * RS232 interrupts are guaranteed to be disabled now by the 8259
486 * mask, but there used to be trouble if the mask was set without
487 * handling a previous interrupt.
489 istop(rs
); /* sets modem_ctl_port */
491 sys_outb(rs
->int_enab_port
, 0);
493 /* Clear any harmful leftover interrupts. An output interrupt is harmless
494 * and will occur when interrupts are enabled anyway. Set up the output
495 * queue using the status from clearing the modem status interrupt.
497 if ((s
= sys_inb(rs
->line_status_port
, &dummy
)) != OK
)
498 printf("TTY: sys_inb() failed: %d", s
);
499 if ((s
= sys_inb(rs
->recv_port
, &dummy
)) != OK
)
500 printf("TTY: sys_inb() failed: %d", s
);
501 rs
->ostate
= devready(rs
) | ORAW
| OSWREADY
; /* reads modem_ctl_port */
502 rs
->ohead
= rs
->otail
= rs
->obuf
;
504 /* Enable interrupts for both interrupt controller and device. */
505 irq
= (line
& 1) == 0 ? RS232_IRQ
: SECONDARY_IRQ
;
508 rs
->irq_hook_id
= rs
->irq
; /* call back with irq line number */
509 if (sys_irqsetpolicy(irq
, IRQ_REENABLE
, &rs
->irq_hook_id
) != OK
) {
510 printf("RS232: Couldn't obtain hook for irq %d\n", irq
);
512 if (sys_irqenable(&rs
->irq_hook_id
) != OK
) {
513 printf("RS232: Couldn't enable irq %d (hooked)\n", irq
);
517 rs_irq_set
|= (1 << irq
);
519 sys_outb(rs
->int_enab_port
, IE_LINE_STATUS_CHANGE
| IE_MODEM_STATUS_CHANGE
520 | IE_RECEIVER_READY
| IE_TRANSMITTER_READY
);
522 /* Fill in TTY function hooks. */
523 tp
->tty_devread
= rs_read
;
524 tp
->tty_devwrite
= rs_write
;
525 tp
->tty_echo
= rs_echo
;
526 tp
->tty_icancel
= rs_icancel
;
527 tp
->tty_ocancel
= rs_ocancel
;
528 tp
->tty_ioctl
= rs_ioctl
;
529 tp
->tty_break
= rs_break
;
530 tp
->tty_close
= rs_close
;
532 /* Tell external device we are ready. */
537 /*===========================================================================*
539 *===========================================================================*/
540 void rs_interrupt(message
*m
)
542 unsigned long irq_set
;
546 irq_set
= m
->NOTIFY_ARG
;
547 for (i
= 0, rs
= rs_lines
; i
<NR_RS_LINES
; i
++, rs
++)
549 if (irq_set
& (1 << rs
->irq
))
554 /*===========================================================================*
556 *===========================================================================*/
557 static int rs_icancel(tty_t
*tp
, int UNUSED(dummy
))
559 /* Cancel waiting input. */
560 rs232_t
*rs
= tp
->tty_priv
;
564 rs
->itail
= rs
->ihead
;
568 return 0; /* dummy */
571 /*===========================================================================*
573 *===========================================================================*/
574 static int rs_ocancel(tty_t
*tp
, int UNUSED(dummy
))
576 /* Cancel pending output. */
577 rs232_t
*rs
= tp
->tty_priv
;
580 rs
->ostate
&= ~(ODONE
| OQUEUED
);
582 rs
->otail
= rs
->ohead
;
585 return 0; /* dummy */
588 /*===========================================================================*
590 *===========================================================================*/
591 static int rs_read(tty_t
*tp
, int try)
593 /* Process characters from the circular input buffer. */
595 rs232_t
*rs
= tp
->tty_priv
;
596 int icount
, count
, ostate
;
598 if (!(tp
->tty_termios
.c_cflag
& CLOCAL
)) {
600 /* Send a SIGHUP if hangup detected. */
603 rs
->ostate
&= ~ODEVHUP
; /* save ostate, clear DEVHUP */
605 if (ostate
& ODEVHUP
) {
606 sigchar(tp
, SIGHUP
, 1);
607 tp
->tty_termios
.c_ospeed
= B0
; /* Disable further I/O. */
618 while ((count
= rs
->icount
) > 0) {
619 icount
= bufend(rs
->ibuf
) - rs
->itail
;
620 if (count
> icount
) count
= icount
;
622 /* Perform input processing on (part of) the input buffer. */
623 if ((count
= in_process(tp
, rs
->itail
, count
, -1)) == 0) break;
625 lock(); /* protect interrupt sensitive variables */
627 if (!rs
->idevready
&& rs
->icount
< RS_ILOWWATER
) istart(rs
);
629 if ((rs
->itail
+= count
) == bufend(rs
->ibuf
)) rs
->itail
= rs
->ibuf
;
635 /*===========================================================================*
637 *===========================================================================*/
638 static void rs_ostart(rs232_t
*rs
)
640 /* Tell RS232 there is something waiting in the output buffer. */
642 rs
->ostate
|= OQUEUED
;
643 if (txready(rs
)) out_int(rs
);
646 /*===========================================================================*
648 *===========================================================================*/
649 static int rs_break(tty_t
*tp
, int UNUSED(dummy
))
651 /* Generate a break condition by setting the BREAK bit for 0.4 sec. */
652 rs232_t
*rs
= tp
->tty_priv
;
656 if ((s
= sys_inb(rs
->line_ctl_port
, &line_controls
)) != OK
)
657 printf("TTY: sys_inb() failed: %d", s
);
658 sys_outb(rs
->line_ctl_port
, line_controls
| LC_BREAK
);
660 /* milli_delay(400); */ /* ouch */
661 printf("RS232 break\n");
662 sys_outb(rs
->line_ctl_port
, line_controls
);
663 return 0; /* dummy */
666 /*===========================================================================*
668 *===========================================================================*/
669 static int rs_close(tty_t
*tp
, int UNUSED(dummy
))
671 /* The line is closed; optionally hang up. */
672 rs232_t
*rs
= tp
->tty_priv
;
674 if (tp
->tty_termios
.c_cflag
& HUPCL
) {
675 sys_outb(rs
->modem_ctl_port
, MC_OUT2
| MC_RTS
);
677 return 0; /* dummy */
680 /* Low level (interrupt) routines. */
682 /*===========================================================================*
684 *===========================================================================*/
685 static void rs232_handler(struct rs232
*rs
)
687 /* Interrupt hander for RS232. */
692 /* Loop to pick up ALL pending interrupts for device.
693 * This usually just wastes time unless the hardware has a buffer
694 * (and then we have to worry about being stuck in the loop too long).
695 * Unfortunately, some serial cards lock up without this.
697 if ((s
= sys_inb(rs
->int_id_port
, &v
)) != OK
)
698 printf("TTY: sys_inb() failed: %d", s
);
700 case IS_RECEIVER_READY
:
703 case IS_TRANSMITTER_READY
:
706 case IS_MODEM_STATUS_CHANGE
:
709 case IS_LINE_STATUS_CHANGE
:
717 /*===========================================================================*
719 *===========================================================================*/
720 static void in_int(register rs232_t
*rs
)
721 /* rs line with input interrupt */
723 /* Read the data which just arrived.
724 * If it is the oxoff char, clear OSWREADY, else if OSWREADY was clear, set
725 * it and restart output (any char does this, not just xon).
726 * Put data in the buffer if room, otherwise discard it.
727 * Set a flag for the clock interrupt handler to eventually notify TTY.
732 #if 0 /* Enable this if you want serial input in the kernel */
736 if ((s
= sys_inb(rs
->recv_port
, &c
)) != OK
)
737 printf("TTY: sys_inb() failed: %d", s
);
739 if (!(rs
->ostate
& ORAW
)) {
740 if (c
== rs
->oxoff
) {
741 rs
->ostate
&= ~OSWREADY
;
743 if (!(rs
->ostate
& OSWREADY
)) {
744 rs
->ostate
|= OSWREADY
;
745 if (txready(rs
)) out_int(rs
);
749 if (rs
->icount
== buflen(rs
->ibuf
))
751 printf("in_int: discarding byte\n");
752 return; /* input buffer full, discard */
755 if (++rs
->icount
== RS_IHIGHWATER
&& rs
->idevready
) istop(rs
);
757 if (++rs
->ihead
== bufend(rs
->ibuf
)) rs
->ihead
= rs
->ibuf
;
758 if (rs
->icount
== 1) {
759 rs
->tty
->tty_events
= 1;
764 /*===========================================================================*
766 *===========================================================================*/
767 static void line_int(register rs232_t
*rs
)
768 /* rs line with line status interrupt */
770 /* Check for and record errors. */
774 if ((r
= sys_inb(rs
->line_status_port
, &s
)) != OK
)
775 printf("TTY: sys_inb() failed: %d", r
);
777 if (rs
->lstatus
& LS_FRAMING_ERR
) ++rs
->framing_errors
;
778 if (rs
->lstatus
& LS_OVERRUN_ERR
) ++rs
->overrun_errors
;
779 if (rs
->lstatus
& LS_PARITY_ERR
) ++rs
->parity_errors
;
780 if (rs
->lstatus
& LS_BREAK_INTERRUPT
) ++rs
->break_interrupts
;
783 /*===========================================================================*
785 *===========================================================================*/
786 static void modem_int(register rs232_t
*rs
)
787 /* rs line with modem interrupt */
789 /* Get possibly new device-ready status, and clear ODEVREADY if necessary.
790 * If the device just became ready, restart output.
794 rs
->ostate
|= ODEVHUP
;
795 rs
->tty
->tty_events
= 1;
800 rs
->ostate
&= ~ODEVREADY
;
801 else if (!(rs
->ostate
& ODEVREADY
)) {
802 rs
->ostate
|= ODEVREADY
;
803 if (txready(rs
)) out_int(rs
);
807 /*===========================================================================*
809 *===========================================================================*/
810 static void out_int(register rs232_t
*rs
)
811 /* rs; line with output interrupt */
813 /* If there is output to do and everything is ready, do it (local device is
815 * Notify TTY when the buffer goes empty.
818 if (rs
->ostate
>= (ODEVREADY
| OQUEUED
| OSWREADY
)) {
819 /* Bit test allows ORAW and requires the others. */
820 sys_outb(rs
->xmit_port
, *rs
->otail
);
821 if (++rs
->otail
== bufend(rs
->obuf
)) rs
->otail
= rs
->obuf
;
822 if (--rs
->ocount
== 0) {
823 rs
->ostate
^= (ODONE
| OQUEUED
); /* ODONE on, OQUEUED off */
824 rs
->tty
->tty_events
= 1;
827 if (rs
->ocount
== RS_OLOWWATER
) { /* running low? */
828 rs
->tty
->tty_events
= 1;
833 #endif /* NR_RS_LINES > 0 */