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>
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
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
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. */
52 #define MC_OUT2 8 /* required for PC & AT interrupts */
54 /* Modem status bits. */
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.
90 (sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_RTS | MC_DTR), \
91 (rs)->idevready = TRUE)
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
112 ((my_inb(rs->modem_status_port) & (MS_RLSD|MS_DRLSD)) == MS_DRLSD)
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 */
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 */
153 port_t int_enab_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
;
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 */
175 static rs232_t rs_lines
[NR_RS_LINES
];
177 #if defined(__i386__)
178 /* 8250 base addresses. */
179 static port_t addr_8250
[] = {
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
)
208 r
= sys_inb(port
, &v
);
210 printf("RS232 warning: failed inb 0x%x\n", port
);
215 /*===========================================================================*
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
;
233 /* Wait for the line to drain then reconfigure and continue output. */
234 if (rs
->ocount
> 0) return 0;
239 /* While there is something to do. */
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
) {
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
,
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
;
270 rs
->ocount
+= ocount
;
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
,
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
;
293 /*===========================================================================*
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
;
305 ocount
= buflen(rs
->obuf
) - rs
->ocount
;
306 if (ocount
== 0) return; /* output buffer full */
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
;
315 if ((rs
->ohead
+= ocount
) >= bufend(rs
->obuf
)) rs
->ohead
-= buflen(rs
->obuf
);
318 /*===========================================================================*
320 *===========================================================================*/
321 static int rs_ioctl(tty_t
*tp
, int UNUSED(dummy
))
324 /* Reconfigure the line as soon as the output has drained. */
325 rs232_t
*rs
= tp
->tty_priv
;
328 return 0; /* dummy */
331 /*===========================================================================*
333 *===========================================================================*/
334 static void rs_config(rs232_t
*rs
)
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.
345 static struct speed2divisor
{
349 #if defined(__i386__)
350 { B50
, UART_FREQ
/ 50 },
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 },
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 },
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. */
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. */
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
)
419 /*===========================================================================*
421 *===========================================================================*/
422 void rs_init(tty_t
*tp
)
426 /* Initialize RS232 for one line. */
428 register rs232_t
*rs
;
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",
447 rs
= tp
->tty_priv
= &rs_lines
[line
];
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 */
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
;
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
);
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. */
528 /*===========================================================================*
530 *===========================================================================*/
531 void rs_interrupt(message
*m
)
533 unsigned long irq_set
;
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
))
545 /*===========================================================================*
547 *===========================================================================*/
548 static int rs_icancel(tty_t
*tp
, int UNUSED(dummy
))
550 /* Cancel waiting input. */
551 rs232_t
*rs
= tp
->tty_priv
;
554 rs
->itail
= rs
->ihead
;
557 return 0; /* dummy */
560 /*===========================================================================*
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
);
570 rs
->otail
= rs
->ohead
;
572 return 0; /* dummy */
575 /*===========================================================================*
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
)) {
587 /* Send a SIGHUP if hangup detected. */
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. */
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;
611 if (!rs
->idevready
&& rs
->icount
< RS_ILOWWATER
) istart(rs
);
612 if ((rs
->itail
+= count
) == bufend(rs
->ibuf
)) rs
->itail
= rs
->ibuf
;
618 /*===========================================================================*
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 /*===========================================================================*
631 *===========================================================================*/
632 static int rs_break_on(tty_t
*tp
, int UNUSED(dummy
))
634 /* Raise break condition. */
635 rs232_t
*rs
= tp
->tty_priv
;
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 /*===========================================================================*
647 *===========================================================================*/
648 static int rs_break_off(tty_t
*tp
, int UNUSED(dummy
))
650 /* Clear break condition. */
651 rs232_t
*rs
= tp
->tty_priv
;
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 /*===========================================================================*
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 /*===========================================================================*
679 *===========================================================================*/
680 static void rs232_handler(struct rs232
*rs
)
682 /* Interrupt hander for RS232. */
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
:
704 case IS_TRANSMITTER_READY
:
707 case IS_MODEM_STATUS_CHANGE
:
710 case IS_LINE_STATUS_CHANGE
:
717 printf("tty rs232: enough!\n");
720 /*===========================================================================*
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.
735 #if 0 /* Enable this if you want serial input in the kernel */
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
;
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
);
760 if (++rs
->ihead
== bufend(rs
->ibuf
)) rs
->ihead
= rs
->ibuf
;
761 if (rs
->icount
== 1) {
762 rs
->tty
->tty_events
= 1;
767 /*===========================================================================*
769 *===========================================================================*/
770 static void line_int(register rs232_t
*rs
)
771 /* rs line with line status interrupt */
773 /* Check for and record errors. */
777 if ((r
= sys_inb(rs
->line_status_port
, &s
)) != OK
)
778 printf("TTY: sys_inb() failed: %d", r
);
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 /*===========================================================================*
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.
797 rs
->ostate
|= ODEVHUP
;
798 rs
->tty
->tty_events
= 1;
803 rs
->ostate
&= ~ODEVREADY
;
804 else if (!(rs
->ostate
& ODEVREADY
)) {
805 rs
->ostate
|= ODEVREADY
;
806 if (txready(rs
)) out_int(rs
);
810 /*===========================================================================*
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
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;
830 if (rs
->ocount
== RS_OLOWWATER
) { /* running low? */
831 rs
->tty
->tty_events
= 1;
836 #endif /* NR_RS_LINES > 0 */