memory: use sys_safememset() for /dev/zero
[minix.git] / drivers / tty / rs232.c
blob4a58134391fbc9d29b29bc78549955de9b09a648
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 ((r = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
252 tp->tty_outoffset, (vir_bytes) rs->ohead, count)) != OK)
253 printf("TTY: sys_safecopyfrom() failed: %d", r);
255 /* Perform output processing on the output buffer. */
256 out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
257 if (count == 0) break;
259 /* Assume echoing messed up by output. */
260 tp->tty_reprint = TRUE;
262 /* Bookkeeping. */
263 lock(); /* protect interrupt sensitive rs->ocount */
264 rs->ocount += ocount;
265 rs_ostart(rs);
266 unlock();
267 if ((rs->ohead += ocount) >= bufend(rs->obuf))
268 rs->ohead -= buflen(rs->obuf);
269 tp->tty_outoffset += count;
270 tp->tty_outcum += count;
271 if ((tp->tty_outleft -= count) == 0) {
272 /* Output is finished, reply to the writer. */
273 if(tp->tty_outrepcode == TTY_REVIVE) {
274 notify(tp->tty_outcaller);
275 tp->tty_outrevived = 1;
276 } else {
277 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
278 tp->tty_outproc, tp->tty_outcum);
279 tp->tty_outcum = 0;
283 if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
284 /* Oops, the line has hung up. */
285 if(tp->tty_outrepcode == TTY_REVIVE) {
286 notify(tp->tty_outcaller);
287 tp->tty_outrevived = 1;
288 } else {
289 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
290 tp->tty_outproc, EIO);
291 tp->tty_outleft = tp->tty_outcum = 0;
295 return 1;
298 /*===========================================================================*
299 * rs_echo *
300 *===========================================================================*/
301 static void rs_echo(tp, c)
302 tty_t *tp; /* which TTY */
303 int c; /* character to echo */
305 /* Echo one character. (Like rs_write, but only one character, optionally.) */
307 rs232_t *rs = tp->tty_priv;
308 int count, ocount;
310 ocount = buflen(rs->obuf) - rs->ocount;
311 if (ocount == 0) return; /* output buffer full */
312 count = 1;
313 *rs->ohead = c; /* add one character */
315 out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
316 if (count == 0) return;
318 lock();
319 rs->ocount += ocount;
320 rs_ostart(rs);
321 unlock();
322 if ((rs->ohead += ocount) >= bufend(rs->obuf)) rs->ohead -= buflen(rs->obuf);
325 /*===========================================================================*
326 * rs_ioctl *
327 *===========================================================================*/
328 static int rs_ioctl(tty_t *tp, int UNUSED(dummy))
329 /* tp; which TTY */
331 /* Reconfigure the line as soon as the output has drained. */
332 rs232_t *rs = tp->tty_priv;
334 rs->drain = TRUE;
335 return 0; /* dummy */
338 /*===========================================================================*
339 * rs_config *
340 *===========================================================================*/
341 static void rs_config(rs232_t *rs)
342 /* rs which line */
344 /* Set various line control parameters for RS232 I/O.
345 * If DataBits == 5 and StopBits == 2, 8250 will generate 1.5 stop bits.
346 * The 8250 can't handle split speed, so we use the input speed.
349 tty_t *tp = rs->tty;
350 int divisor;
351 int line_controls;
352 static struct speed2divisor {
353 speed_t speed;
354 int divisor;
355 } s2d[] = {
356 #if defined(__i386__)
357 { B50, UART_FREQ / 50 },
358 #endif
359 { B75, UART_FREQ / 75 },
360 { B110, UART_FREQ / 110 },
361 { B134, UART_FREQ * 10 / 1345 },
362 { B150, UART_FREQ / 150 },
363 { B200, UART_FREQ / 200 },
364 { B300, UART_FREQ / 300 },
365 { B600, UART_FREQ / 600 },
366 { B1200, UART_FREQ / 1200 },
367 #if defined(__i386__)
368 { B1800, UART_FREQ / 1800 },
369 #endif
370 { B2400, UART_FREQ / 2400 },
371 { B4800, UART_FREQ / 4800 },
372 { B9600, UART_FREQ / 9600 },
373 { B19200, UART_FREQ / 19200 },
374 #if defined(__i386__)
375 { B38400, UART_FREQ / 38400 },
376 { B57600, UART_FREQ / 57600 },
377 { B115200, UART_FREQ / 115200L },
378 #endif
380 struct speed2divisor *s2dp;
382 /* RS232 needs to know the xoff character, and if CTS works. */
383 rs->oxoff = tp->tty_termios.c_cc[VSTOP];
384 rs->cts = (tp->tty_termios.c_cflag & CLOCAL) ? MS_CTS : 0;
386 /* Look up the 8250 rate divisor from the output speed. */
387 divisor = 0;
388 for (s2dp = s2d; s2dp < s2d + sizeof(s2d)/sizeof(s2d[0]); s2dp++) {
389 if (s2dp->speed == tp->tty_termios.c_ospeed) divisor = s2dp->divisor;
391 if (divisor == 0) return; /* B0? */
393 /* Compute line control flag bits. */
394 line_controls = 0;
395 if (tp->tty_termios.c_cflag & PARENB) {
396 line_controls |= LC_PARITY;
397 if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= LC_PAREVEN;
399 if (divisor >= (UART_FREQ / 110)) line_controls |= LC_2STOP_BITS;
400 line_controls |= (tp->tty_termios.c_cflag & CSIZE) >> 2;
402 /* Lock out interrupts while setting the speed. The receiver register is
403 * going to be hidden by the div_low register, but the input interrupt
404 * handler relies on reading it to clear the interrupt and avoid looping
405 * forever.
407 lock();
409 /* Select the baud rate divisor registers and change the rate. */
410 sys_outb(rs->line_ctl_port, LC_ADDRESS_DIVISOR);
411 sys_outb(rs->div_low_port, divisor);
412 sys_outb(rs->div_hi_port, divisor >> 8);
414 /* Change the line controls and reselect the usual registers. */
415 sys_outb(rs->line_ctl_port, line_controls);
417 rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
418 if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE)
419 rs->ostate &= ~ORAW;
421 unlock();
424 /*===========================================================================*
425 * rs_init *
426 *===========================================================================*/
427 void rs_init(tty_t *tp)
428 /* tp which TTY */
430 u32_t dummy;
431 /* Initialize RS232 for one line. */
433 register rs232_t *rs;
434 int line;
435 port_t this_8250;
436 int s, irq;
437 char l[10];
439 /* Associate RS232 and TTY structures. */
440 line = tp - &tty_table[NR_CONS];
442 /* See if kernel debugging is enabled; if so, don't initialize this
443 * serial line, making tty not look at the irq and returning ENXIO
444 * for all requests on it from userland. (The kernel will use it.)
446 if(env_get_param(SERVARNAME, l, sizeof(l)-1) == OK && atoi(l) == line) {
447 printf("TTY: not initializing rs232 line %d (in use by kernel)\n",
448 line);
449 return;
452 rs = tp->tty_priv = &rs_lines[line];
453 rs->tty = tp;
455 /* Set up input queue. */
456 rs->ihead = rs->itail = rs->ibuf;
458 /* Precalculate port numbers for speed. Magic numbers in the code (once). */
459 this_8250 = addr_8250[line];
460 rs->xmit_port = this_8250 + 0;
461 rs->recv_port = this_8250 + 0;
462 rs->div_low_port = this_8250 + 0;
463 rs->div_hi_port = this_8250 + 1;
464 rs->int_enab_port = this_8250 + 1;
465 rs->int_id_port = this_8250 + 2;
466 rs->line_ctl_port = this_8250 + 3;
467 rs->modem_ctl_port = this_8250 + 4;
468 rs->line_status_port = this_8250 + 5;
469 rs->modem_status_port = this_8250 + 6;
471 /* Set up the hardware to a base state, in particular
472 * o turn off DTR (MC_DTR) to try to stop the external device.
473 * o be careful about the divisor latch. Some BIOS's leave it enabled
474 * here and that caused trouble (no interrupts) in version 1.5 by
475 * hiding the interrupt enable port in the next step, and worse trouble
476 * (continual interrupts) in an old version by hiding the receiver
477 * port in the first interrupt. Call rs_ioctl() early to avoid this.
478 * o disable interrupts at the chip level, to force an edge transition
479 * on the 8259 line when interrupts are next enabled and active.
480 * RS232 interrupts are guaranteed to be disabled now by the 8259
481 * mask, but there used to be trouble if the mask was set without
482 * handling a previous interrupt.
484 istop(rs); /* sets modem_ctl_port */
485 rs_config(rs);
486 sys_outb(rs->int_enab_port, 0);
488 /* Clear any harmful leftover interrupts. An output interrupt is harmless
489 * and will occur when interrupts are enabled anyway. Set up the output
490 * queue using the status from clearing the modem status interrupt.
492 if ((s = sys_inb(rs->line_status_port, &dummy)) != OK)
493 printf("TTY: sys_inb() failed: %d", s);
494 if ((s = sys_inb(rs->recv_port, &dummy)) != OK)
495 printf("TTY: sys_inb() failed: %d", s);
496 rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
497 rs->ohead = rs->otail = rs->obuf;
499 /* Enable interrupts for both interrupt controller and device. */
500 irq = (line & 1) == 0 ? RS232_IRQ : SECONDARY_IRQ;
502 rs->irq = irq;
503 rs->irq_hook_id = rs->irq; /* call back with irq line number */
504 if (sys_irqsetpolicy(irq, IRQ_REENABLE, &rs->irq_hook_id) != OK) {
505 printf("RS232: Couldn't obtain hook for irq %d\n", irq);
506 } else {
507 if (sys_irqenable(&rs->irq_hook_id) != OK) {
508 printf("RS232: Couldn't enable irq %d (hooked)\n", irq);
512 rs_irq_set |= (1 << irq);
514 sys_outb(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_MODEM_STATUS_CHANGE
515 | IE_RECEIVER_READY | IE_TRANSMITTER_READY);
517 /* Fill in TTY function hooks. */
518 tp->tty_devread = rs_read;
519 tp->tty_devwrite = rs_write;
520 tp->tty_echo = rs_echo;
521 tp->tty_icancel = rs_icancel;
522 tp->tty_ocancel = rs_ocancel;
523 tp->tty_ioctl = rs_ioctl;
524 tp->tty_break = rs_break;
525 tp->tty_close = rs_close;
527 /* Tell external device we are ready. */
528 istart(rs);
532 /*===========================================================================*
533 * rs_interrupt *
534 *===========================================================================*/
535 void rs_interrupt(message *m)
537 unsigned long irq_set;
538 int i;
539 rs232_t *rs;
541 irq_set= m->NOTIFY_ARG;
542 for (i= 0, rs = rs_lines; i<NR_RS_LINES; i++, rs++)
544 if (irq_set & (1 << rs->irq))
545 rs232_handler(rs);
549 /*===========================================================================*
550 * rs_icancel *
551 *===========================================================================*/
552 static int rs_icancel(tty_t *tp, int UNUSED(dummy))
554 /* Cancel waiting input. */
555 rs232_t *rs = tp->tty_priv;
557 lock();
558 rs->icount = 0;
559 rs->itail = rs->ihead;
560 istart(rs);
561 unlock();
563 return 0; /* dummy */
566 /*===========================================================================*
567 * rs_ocancel *
568 *===========================================================================*/
569 static int rs_ocancel(tty_t *tp, int UNUSED(dummy))
571 /* Cancel pending output. */
572 rs232_t *rs = tp->tty_priv;
574 lock();
575 rs->ostate &= ~(ODONE | OQUEUED);
576 rs->ocount = 0;
577 rs->otail = rs->ohead;
578 unlock();
580 return 0; /* dummy */
583 /*===========================================================================*
584 * rs_read *
585 *===========================================================================*/
586 static int rs_read(tty_t *tp, int try)
588 /* Process characters from the circular input buffer. */
590 rs232_t *rs = tp->tty_priv;
591 int icount, count, ostate;
593 if (!(tp->tty_termios.c_cflag & CLOCAL)) {
594 if (try) return 1;
595 /* Send a SIGHUP if hangup detected. */
596 lock();
597 ostate = rs->ostate;
598 rs->ostate &= ~ODEVHUP; /* save ostate, clear DEVHUP */
599 unlock();
600 if (ostate & ODEVHUP) {
601 sigchar(tp, SIGHUP, 1);
602 tp->tty_termios.c_ospeed = B0; /* Disable further I/O. */
603 return 0;
607 if (try) {
608 if (rs->icount > 0)
609 return 1;
610 return 0;
613 while ((count = rs->icount) > 0) {
614 icount = bufend(rs->ibuf) - rs->itail;
615 if (count > icount) count = icount;
617 /* Perform input processing on (part of) the input buffer. */
618 if ((count = in_process(tp, rs->itail, count, -1)) == 0) break;
620 lock(); /* protect interrupt sensitive variables */
621 rs->icount -= count;
622 if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs);
623 unlock();
624 if ((rs->itail += count) == bufend(rs->ibuf)) rs->itail = rs->ibuf;
627 return 0;
630 /*===========================================================================*
631 * rs_ostart *
632 *===========================================================================*/
633 static void rs_ostart(rs232_t *rs)
635 /* Tell RS232 there is something waiting in the output buffer. */
637 rs->ostate |= OQUEUED;
638 if (txready(rs)) out_int(rs);
641 /*===========================================================================*
642 * rs_break *
643 *===========================================================================*/
644 static int rs_break(tty_t *tp, int UNUSED(dummy))
646 /* Generate a break condition by setting the BREAK bit for 0.4 sec. */
647 rs232_t *rs = tp->tty_priv;
648 u32_t line_controls;
649 int s;
651 if ((s = sys_inb(rs->line_ctl_port, &line_controls)) != OK)
652 printf("TTY: sys_inb() failed: %d", s);
653 sys_outb(rs->line_ctl_port, line_controls | LC_BREAK);
654 /* XXX */
655 /* milli_delay(400); */ /* ouch */
656 printf("RS232 break\n");
657 sys_outb(rs->line_ctl_port, line_controls);
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;
685 while (TRUE) {
686 u32_t v;
687 /* Loop to pick up ALL pending interrupts for device.
688 * This usually just wastes time unless the hardware has a buffer
689 * (and then we have to worry about being stuck in the loop too long).
690 * Unfortunately, some serial cards lock up without this.
692 if ((s = sys_inb(rs->int_id_port, &v)) != OK)
693 printf("TTY: sys_inb() failed: %d", s);
694 switch (v) {
695 case IS_RECEIVER_READY:
696 in_int(rs);
697 continue;
698 case IS_TRANSMITTER_READY:
699 out_int(rs);
700 continue;
701 case IS_MODEM_STATUS_CHANGE:
702 modem_int(rs);
703 continue;
704 case IS_LINE_STATUS_CHANGE:
705 line_int(rs);
706 continue;
708 return;
712 /*===========================================================================*
713 * in_int *
714 *===========================================================================*/
715 static void in_int(register rs232_t *rs)
716 /* rs line with input interrupt */
718 /* Read the data which just arrived.
719 * If it is the oxoff char, clear OSWREADY, else if OSWREADY was clear, set
720 * it and restart output (any char does this, not just xon).
721 * Put data in the buffer if room, otherwise discard it.
722 * Set a flag for the clock interrupt handler to eventually notify TTY.
724 int s;
725 u32_t c;
727 #if 0 /* Enable this if you want serial input in the kernel */
728 return;
729 #endif
731 if ((s = sys_inb(rs->recv_port, &c)) != OK)
732 printf("TTY: sys_inb() failed: %d", s);
734 if (!(rs->ostate & ORAW)) {
735 if (c == rs->oxoff) {
736 rs->ostate &= ~OSWREADY;
737 } else
738 if (!(rs->ostate & OSWREADY)) {
739 rs->ostate |= OSWREADY;
740 if (txready(rs)) out_int(rs);
744 if (rs->icount == buflen(rs->ibuf))
746 printf("in_int: discarding byte\n");
747 return; /* input buffer full, discard */
750 if (++rs->icount == RS_IHIGHWATER && rs->idevready) istop(rs);
751 *rs->ihead = c;
752 if (++rs->ihead == bufend(rs->ibuf)) rs->ihead = rs->ibuf;
753 if (rs->icount == 1) {
754 rs->tty->tty_events = 1;
755 force_timeout();
757 else
758 printf("in_int: icount = %d\n", rs->icount);
761 /*===========================================================================*
762 * line_int *
763 *===========================================================================*/
764 static void line_int(register rs232_t *rs)
765 /* rs line with line status interrupt */
767 /* Check for and record errors. */
768 int r;
769 u32_t s;
771 if ((r = sys_inb(rs->line_status_port, &s)) != OK)
772 printf("TTY: sys_inb() failed: %d", r);
773 rs->lstatus = s;
774 if (rs->lstatus & LS_FRAMING_ERR) ++rs->framing_errors;
775 if (rs->lstatus & LS_OVERRUN_ERR) ++rs->overrun_errors;
776 if (rs->lstatus & LS_PARITY_ERR) ++rs->parity_errors;
777 if (rs->lstatus & LS_BREAK_INTERRUPT) ++rs->break_interrupts;
780 /*===========================================================================*
781 * modem_int *
782 *===========================================================================*/
783 static void modem_int(register rs232_t *rs)
784 /* rs line with modem interrupt */
786 /* Get possibly new device-ready status, and clear ODEVREADY if necessary.
787 * If the device just became ready, restart output.
790 if (devhup(rs)) {
791 rs->ostate |= ODEVHUP;
792 rs->tty->tty_events = 1;
793 force_timeout();
796 if (!devready(rs))
797 rs->ostate &= ~ODEVREADY;
798 else if (!(rs->ostate & ODEVREADY)) {
799 rs->ostate |= ODEVREADY;
800 if (txready(rs)) out_int(rs);
804 /*===========================================================================*
805 * out_int *
806 *===========================================================================*/
807 static void out_int(register rs232_t *rs)
808 /* rs; line with output interrupt */
810 /* If there is output to do and everything is ready, do it (local device is
811 * known ready).
812 * Notify TTY when the buffer goes empty.
815 if (rs->ostate >= (ODEVREADY | OQUEUED | OSWREADY)) {
816 /* Bit test allows ORAW and requires the others. */
817 sys_outb(rs->xmit_port, *rs->otail);
818 if (++rs->otail == bufend(rs->obuf)) rs->otail = rs->obuf;
819 if (--rs->ocount == 0) {
820 rs->ostate ^= (ODONE | OQUEUED); /* ODONE on, OQUEUED off */
821 rs->tty->tty_events = 1;
822 force_timeout();
823 } else
824 if (rs->ocount == RS_OLOWWATER) { /* running low? */
825 rs->tty->tty_events = 1;
826 force_timeout();
830 #endif /* NR_RS_LINES > 0 */