kernel: scheduling fix for ARM
[minix.git] / drivers / tty / arch / i386 / rs232.c
blob7525c2784de92a4dd067056d8e7d558606384044
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 <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_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
33 #define LC_BREAK 0x40
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. */
44 #define MC_DTR 1
45 #define MC_RTS 2
46 #define MC_OUT2 8 /* required for PC & AT interrupts */
48 /* Modem status bits. */
49 #define MS_CTS 0x10
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.
83 #define istart(rs) \
84 (sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_RTS | MC_DTR), \
85 (rs)->idevready = TRUE)
86 #define istop(rs) \
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
103 * dropped.
105 #define devhup(rs) \
106 ((my_inb(rs->modem_status_port) & (MS_RLSD|MS_DRLSD)) == MS_DRLSD)
108 /* Types. */
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 */
134 #endif
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 */
144 port_t recv_port;
145 port_t div_low_port;
146 port_t div_hi_port;
147 port_t int_enab_port;
148 port_t int_id_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;
153 #endif
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 */
167 } rs232_t;
169 static rs232_t rs_lines[NR_RS_LINES];
171 #if defined(__i386__)
172 /* 8250 base addresses. */
173 static port_t addr_8250[] = {
174 0x3F8, /* COM1 */
175 0x2F8, /* COM2 */
176 0x3E8, /* COM3 */
177 0x2E8, /* COM4 */
179 #endif
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);
197 /* XXX */
198 static void lock(void) {}
199 static void unlock(void) {}
201 static int my_inb(port_t port)
203 int r;
204 u32_t v = 0;
205 r = sys_inb(port, &v);
206 if (r != OK)
207 printf("RS232 warning: failed inb 0x%x\n", port);
209 return (int) v;
212 /*===========================================================================*
213 * rs_write *
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. */
224 lock();
225 rs->ostate |= OSWREADY;
226 if (tp->tty_inhibited) rs->ostate &= ~OSWREADY;
227 unlock();
228 rs->inhibited = tp->tty_inhibited;
231 if (rs->drain) {
232 /* Wait for the line to drain then reconfigure and continue output. */
233 if (rs->ocount > 0) return 0;
234 rs->drain = FALSE;
235 rs_config(rs);
238 /* While there is something to do. */
239 for (;;) {
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) {
245 if (try) return 0;
246 break;
248 if (try) return 1;
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);
254 } else {
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;
267 /* Bookkeeping. */
268 lock(); /* protect interrupt sensitive rs->ocount */
269 rs->ocount += ocount;
270 rs_ostart(rs);
271 unlock();
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;
281 } else {
282 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
283 tp->tty_outproc, tp->tty_outcum);
284 tp->tty_outcum = 0;
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;
293 } else {
294 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
295 tp->tty_outproc, EIO);
296 tp->tty_outleft = tp->tty_outcum = 0;
300 return 1;
303 /*===========================================================================*
304 * rs_echo *
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;
313 int count, ocount;
315 ocount = buflen(rs->obuf) - rs->ocount;
316 if (ocount == 0) return; /* output buffer full */
317 count = 1;
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;
323 lock();
324 rs->ocount += ocount;
325 rs_ostart(rs);
326 unlock();
327 if ((rs->ohead += ocount) >= bufend(rs->obuf)) rs->ohead -= buflen(rs->obuf);
330 /*===========================================================================*
331 * rs_ioctl *
332 *===========================================================================*/
333 static int rs_ioctl(tty_t *tp, int UNUSED(dummy))
334 /* tp; which TTY */
336 /* Reconfigure the line as soon as the output has drained. */
337 rs232_t *rs = tp->tty_priv;
339 rs->drain = TRUE;
340 return 0; /* dummy */
343 /*===========================================================================*
344 * rs_config *
345 *===========================================================================*/
346 static void rs_config(rs232_t *rs)
347 /* rs which line */
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.
354 tty_t *tp = rs->tty;
355 int divisor;
356 int line_controls;
357 static struct speed2divisor {
358 speed_t speed;
359 int divisor;
360 } s2d[] = {
361 #if defined(__i386__)
362 { B50, UART_FREQ / 50 },
363 #endif
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 },
374 #endif
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 },
383 #endif
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. */
392 divisor = 0;
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. */
399 line_controls = 0;
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
410 * forever.
412 lock();
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)
424 rs->ostate &= ~ORAW;
426 unlock();
429 /*===========================================================================*
430 * rs_init *
431 *===========================================================================*/
432 void rs_init(tty_t *tp)
433 /* tp which TTY */
435 u32_t dummy;
436 /* Initialize RS232 for one line. */
438 register rs232_t *rs;
439 int line;
440 port_t this_8250;
441 int s, irq;
442 char l[10];
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",
453 line);
454 return;
457 rs = tp->tty_priv = &rs_lines[line];
458 rs->tty = tp;
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 */
490 rs_config(rs);
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;
507 rs->irq = 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);
511 } else {
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. */
533 istart(rs);
537 /*===========================================================================*
538 * rs_interrupt *
539 *===========================================================================*/
540 void rs_interrupt(message *m)
542 unsigned long irq_set;
543 int i;
544 rs232_t *rs;
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))
550 rs232_handler(rs);
554 /*===========================================================================*
555 * rs_icancel *
556 *===========================================================================*/
557 static int rs_icancel(tty_t *tp, int UNUSED(dummy))
559 /* Cancel waiting input. */
560 rs232_t *rs = tp->tty_priv;
562 lock();
563 rs->icount = 0;
564 rs->itail = rs->ihead;
565 istart(rs);
566 unlock();
568 return 0; /* dummy */
571 /*===========================================================================*
572 * rs_ocancel *
573 *===========================================================================*/
574 static int rs_ocancel(tty_t *tp, int UNUSED(dummy))
576 /* Cancel pending output. */
577 rs232_t *rs = tp->tty_priv;
579 lock();
580 rs->ostate &= ~(ODONE | OQUEUED);
581 rs->ocount = 0;
582 rs->otail = rs->ohead;
583 unlock();
585 return 0; /* dummy */
588 /*===========================================================================*
589 * rs_read *
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)) {
599 if (try) return 1;
600 /* Send a SIGHUP if hangup detected. */
601 lock();
602 ostate = rs->ostate;
603 rs->ostate &= ~ODEVHUP; /* save ostate, clear DEVHUP */
604 unlock();
605 if (ostate & ODEVHUP) {
606 sigchar(tp, SIGHUP, 1);
607 tp->tty_termios.c_ospeed = B0; /* Disable further I/O. */
608 return 0;
612 if (try) {
613 if (rs->icount > 0)
614 return 1;
615 return 0;
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 */
626 rs->icount -= count;
627 if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs);
628 unlock();
629 if ((rs->itail += count) == bufend(rs->ibuf)) rs->itail = rs->ibuf;
632 return 0;
635 /*===========================================================================*
636 * rs_ostart *
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 /*===========================================================================*
647 * rs_break *
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;
653 u32_t line_controls;
654 int s;
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);
659 /* XXX */
660 /* milli_delay(400); */ /* ouch */
661 printf("RS232 break\n");
662 sys_outb(rs->line_ctl_port, line_controls);
663 return 0; /* dummy */
666 /*===========================================================================*
667 * rs_close *
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 /*===========================================================================*
683 * rs232_handler *
684 *===========================================================================*/
685 static void rs232_handler(struct rs232 *rs)
687 /* Interrupt hander for RS232. */
688 int s;
690 while (TRUE) {
691 u32_t v;
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);
699 switch (v) {
700 case IS_RECEIVER_READY:
701 in_int(rs);
702 continue;
703 case IS_TRANSMITTER_READY:
704 out_int(rs);
705 continue;
706 case IS_MODEM_STATUS_CHANGE:
707 modem_int(rs);
708 continue;
709 case IS_LINE_STATUS_CHANGE:
710 line_int(rs);
711 continue;
713 return;
717 /*===========================================================================*
718 * in_int *
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.
729 int s;
730 u32_t c;
732 #if 0 /* Enable this if you want serial input in the kernel */
733 return;
734 #endif
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;
742 } else
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);
756 *rs->ihead = c;
757 if (++rs->ihead == bufend(rs->ibuf)) rs->ihead = rs->ibuf;
758 if (rs->icount == 1) {
759 rs->tty->tty_events = 1;
760 force_timeout();
764 /*===========================================================================*
765 * line_int *
766 *===========================================================================*/
767 static void line_int(register rs232_t *rs)
768 /* rs line with line status interrupt */
770 /* Check for and record errors. */
771 int r;
772 u32_t s;
774 if ((r = sys_inb(rs->line_status_port, &s)) != OK)
775 printf("TTY: sys_inb() failed: %d", r);
776 rs->lstatus = s;
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 /*===========================================================================*
784 * modem_int *
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.
793 if (devhup(rs)) {
794 rs->ostate |= ODEVHUP;
795 rs->tty->tty_events = 1;
796 force_timeout();
799 if (!devready(rs))
800 rs->ostate &= ~ODEVREADY;
801 else if (!(rs->ostate & ODEVREADY)) {
802 rs->ostate |= ODEVREADY;
803 if (txready(rs)) out_int(rs);
807 /*===========================================================================*
808 * out_int *
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
814 * known ready).
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;
825 force_timeout();
826 } else
827 if (rs->ocount == RS_OLOWWATER) { /* running low? */
828 rs->tty->tty_events = 1;
829 force_timeout();
833 #endif /* NR_RS_LINES > 0 */