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 *---------------------------------------------------------------------------*/
6
7 #include <minix/drivers.h>
8 #include <sys/termios.h>
9 #include <signal.h>
10 #include "tty.h"
11
12 #if NR_RS_LINES > 0
13
14 /* switch RTS/CTS on/off */
15 #if !defined(UART_RTSCTS_OFF)
16 #define UART_RTSCTS_OFF 0 /* 0: Use RTS/CTS, 1: Don't.*/
17 #endif
18
19 /* 8250 constants. */
20 #define UART_FREQ 115200L /* timer frequency */
21
22 /* Interrupt enable bits. */
23 #define IE_RECEIVER_READY 1
24 #define IE_TRANSMITTER_READY 2
25 #define IE_LINE_STATUS_CHANGE 4
26 #define IE_MODEM_STATUS_CHANGE 8
27
28 /* Interrupt status bits. */
29 #define IS_MODEM_STATUS_CHANGE 0
30 #define IS_NOTPENDING 1
31 #define IS_TRANSMITTER_READY 2
32 #define IS_RECEIVER_READY 4
33 #define IS_LINE_STATUS_CHANGE 6
34 #define IS_IDBITS 6
35
36 /* Line control bits. */
37 #define LC_CS5 0x00 /* LSB0 and LSB1 encoding for CS5 */
38 #define LC_CS6 0x01 /* LSB0 and LSB1 encoding for CS6 */
39 #define LC_CS7 0x02 /* LSB0 and LSB1 encoding for CS7 */
40 #define LC_CS8 0x03 /* LSB0 and LSB1 encoding for CS8 */
41 #define LC_2STOP_BITS 0x04
42 #define LC_PARITY 0x08
43 #define LC_PAREVEN 0x10
44 #define LC_BREAK 0x40
45 #define LC_ADDRESS_DIVISOR 0x80
46
47 /* Line status bits. */
48 #define LS_OVERRUN_ERR 2
49 #define LS_PARITY_ERR 4
50 #define LS_FRAMING_ERR 8
51 #define LS_BREAK_INTERRUPT 0x10
52 #define LS_TRANSMITTER_READY 0x20
53
54 /* Modem control bits. */
55 #define MC_DTR 1
56 #define MC_RTS 2
57 #define MC_OUT2 8 /* required for PC & AT interrupts */
58
59 /* Modem status bits. */
60 #define MS_CTS 0x10
61 #define MS_RLSD 0x80 /* Received Line Signal Detect */
62 #define MS_DRLSD 0x08 /* RLSD Delta */
63
64 #define DATA_BITS_SHIFT 8 /* amount data bits shifted in mode */
65 #define DEF_BAUD 1200 /* default baud rate */
66
67 #define RS_IBUFSIZE 1024 /* RS232 input buffer size */
68 #define RS_OBUFSIZE 1024 /* RS232 output buffer size */
69
70 /* Input buffer watermarks.
71 * The external device is asked to stop sending when the buffer
72 * exactly reaches high water, or when TTY requests it. Sending restarts
73 * when the input buffer empties below the low watermark.
74 */
75 #define RS_ILOWWATER (1 * RS_IBUFSIZE / 4)
76 #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
77
78 /* Output buffer low watermark.
79 * TTY is notified when the output buffer empties below the low watermark, so
80 * it may continue filling the buffer if doing a large write.
81 */
82 #define RS_OLOWWATER (1 * RS_OBUFSIZE / 4)
83
84 /* Macros to handle flow control.
85 * Interrupts must be off when they are used.
86 * Time is critical - already the function call for outb() is annoying.
87 * If outb() can be done in-line, tests to avoid it can be dropped.
88 * istart() tells external device we are ready by raising RTS.
89 * istop() tells external device we are not ready by dropping RTS.
90 * DTR is kept high all the time (it probably should be raised by open and
91 * dropped by close of the device).
92 * OUT2 is also kept high all the time.
93 */
94 #if UART_RTSCTS_OFF
95 #define istart(rs) \
96 (rs)->idevready = TRUE
97
98 #define istop(rs) \
99 (rs)->idevready = FALSE
100
101 /* Macro to tell if device is ready. The rs->cts field is set to MS_CTS if
102 * CLOCAL is in effect for a line without a CTS wire.
103 */
104 #define devready(rs) MS_CTS
105 #else
106 #define istart(rs) \
107 (sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_RTS | MC_DTR), \
108 (rs)->idevready = TRUE)
109 #define istop(rs) \
110 (sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_DTR), \
111 (rs)->idevready = FALSE)
112
113 /* Macro to tell if device is ready. The rs->cts field is set to MS_CTS if
114 * CLOCAL is in effect for a line without a CTS wire.
115 */
116 #define devready(rs) ((my_inb(rs->modem_status_port) | rs->cts) & MS_CTS)
117
118 #endif
119
120 /* Macro to tell if transmitter is ready. */
121 #define txready(rs) (my_inb(rs->line_status_port) & LS_TRANSMITTER_READY)
122
123 /* Macro to tell if carrier has dropped.
124 * The RS232 Carrier Detect (CD) line is usually connected to the 8250
125 * Received Line Signal Detect pin, reflected by bit MS_RLSD in the Modem
126 * Status Register. The MS_DRLSD bit tells if MS_RLSD has just changed state.
127 * So if MS_DRLSD is set and MS_RLSD cleared, we know that carrier has just
128 * dropped.
129 */
130 #define devhup(rs) \
131 ((my_inb(rs->modem_status_port) & (MS_RLSD|MS_DRLSD)) == MS_DRLSD)
132
133 /* Types. */
134 typedef unsigned char bool_t; /* boolean */
135
136 /* RS232 device structure, one per device. */
137 typedef struct rs232 {
138 tty_t *tty; /* associated TTY structure */
139
140 int icount; /* number of bytes in the input buffer */
141 char *ihead; /* next free spot in input buffer */
142 char *itail; /* first byte to give to TTY */
143 bool_t idevready; /* nonzero if we are ready to receive (RTS) */
144 char cts; /* normally 0, but MS_CTS if CLOCAL is set */
145
146 unsigned char ostate; /* combination of flags: */
147 #define ODONE 1 /* output completed (< output enable bits) */
148 #define ORAW 2 /* raw mode for xoff disable (< enab. bits) */
149 #define OWAKEUP 4 /* tty_wakeup() pending (asm code only) */
150 #define ODEVREADY MS_CTS /* external device hardware ready (CTS) */
151 #define OQUEUED 0x20 /* output buffer not empty */
152 #define OSWREADY 0x40 /* external device software ready (no xoff) */
153 #define ODEVHUP MS_RLSD /* external device has dropped carrier */
154 #define OSOFTBITS (ODONE | ORAW | OWAKEUP | OQUEUED | OSWREADY)
155 /* user-defined bits */
156 #if (OSOFTBITS | ODEVREADY | ODEVHUP) == OSOFTBITS
157 /* a weak sanity check */
158 #error /* bits are not unique */
159 #endif
160 unsigned char oxoff; /* char to stop output */
161 bool_t inhibited; /* output inhibited? (follows tty_inhibited) */
162 bool_t drain; /* if set drain output and reconfigure line */
163 int ocount; /* number of bytes in the output buffer */
164 char *ohead; /* next free spot in output buffer */
165 char *otail; /* next char to output */
166
167 #if defined(__i386__)
168 port_t xmit_port; /* i/o ports */
169 port_t recv_port;
170 port_t div_low_port;
171 port_t div_hi_port;
172 port_t int_enab_port;
173 port_t int_id_port;
174 port_t line_ctl_port;
175 port_t modem_ctl_port;
176 port_t line_status_port;
177 port_t modem_status_port;
178 #endif
179
180 unsigned char lstatus; /* last line status */
181 unsigned char pad; /* ensure alignment for 16-bit ints */
182 unsigned framing_errors; /* error counts (no reporting yet) */
183 unsigned overrun_errors;
184 unsigned parity_errors;
185 unsigned break_interrupts;
186
187 int irq; /* irq for this line */
188 int irq_hook_id; /* interrupt hook */
189
190 char ibuf[RS_IBUFSIZE]; /* input buffer */
191 char obuf[RS_OBUFSIZE]; /* output buffer */
192 } rs232_t;
193
194 static rs232_t rs_lines[NR_RS_LINES];
195
196 #if defined(__i386__)
197 /* 8250 base addresses. */
198 static port_t addr_8250[] = {
199 0x3F8, /* COM1 */
200 0x2F8, /* COM2 */
201 0x3E8, /* COM3 */
202 0x2E8, /* COM4 */
203 };
204 #endif
205
206 static void in_int(rs232_t *rs);
207 static void line_int(rs232_t *rs);
208 static void modem_int(rs232_t *rs);
209 static int rs_write(tty_t *tp, int try);
210 static void rs_echo(tty_t *tp, int c);
211 static int rs_ioctl(tty_t *tp, int try);
212 static void rs_config(rs232_t *rs);
213 static int rs_read(tty_t *tp, int try);
214 static int rs_icancel(tty_t *tp, int try);
215 static int rs_ocancel(tty_t *tp, int try);
216 static void rs_ostart(rs232_t *rs);
217 static int rs_break_on(tty_t *tp, int try);
218 static int rs_break_off(tty_t *tp, int try);
219 static int rs_close(tty_t *tp, int try);
220 static void out_int(rs232_t *rs);
221 static void rs232_handler(rs232_t *rs);
222
my_inb(port_t port)223 static int my_inb(port_t port)
224 {
225 int r;
226 u32_t v = 0;
227 r = sys_inb(port, &v);
228 if (r != OK)
229 printf("RS232 warning: failed inb 0x%x\n", port);
230
231 return (int) v;
232 }
233
234 /*===========================================================================*
235 * rs_write *
236 *===========================================================================*/
rs_write(register tty_t * tp,int try)237 static int rs_write(register tty_t *tp, int try)
238 {
239 /* (*devwrite)() routine for RS232. */
240
241 rs232_t *rs = tp->tty_priv;
242 int r, count, ocount;
243
244 if (rs->inhibited != tp->tty_inhibited) {
245 /* Inhibition state has changed. */
246 rs->ostate |= OSWREADY;
247 if (tp->tty_inhibited) rs->ostate &= ~OSWREADY;
248 rs->inhibited = tp->tty_inhibited;
249 }
250
251 if (rs->drain) {
252 /* Wait for the line to drain then reconfigure and continue output. */
253 if (rs->ocount > 0) return 0;
254 rs->drain = FALSE;
255 rs_config(rs);
256 }
257
258 /* While there is something to do. */
259 for (;;) {
260 ocount = buflen(rs->obuf) - rs->ocount;
261 count = bufend(rs->obuf) - rs->ohead;
262 if (count > ocount) count = ocount;
263 if (count > tp->tty_outleft) count = tp->tty_outleft;
264 if (count == 0 || tp->tty_inhibited) {
265 if (try) return 0;
266 break;
267 }
268 if (try) return 1;
269
270 /* Copy from user space to the RS232 output buffer. */
271 if (tp->tty_outcaller == KERNEL) {
272 /* We're trying to print on kernel's behalf */
273 memcpy(rs->ohead, (char *) tp->tty_outgrant + tp->tty_outcum,
274 count);
275 } else {
276 if ((r = sys_safecopyfrom(tp->tty_outcaller, tp->tty_outgrant,
277 tp->tty_outcum, (vir_bytes) rs->ohead, count)) != OK)
278 printf("TTY: sys_safecopyfrom() failed: %d", r);
279 }
280
281 /* Perform output processing on the output buffer. */
282 out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
283 if (count == 0) break;
284
285 /* Assume echoing messed up by output. */
286 tp->tty_reprint = TRUE;
287
288 /* Bookkeeping. */
289 rs->ocount += ocount;
290 rs_ostart(rs);
291 if ((rs->ohead += ocount) >= bufend(rs->obuf))
292 rs->ohead -= buflen(rs->obuf);
293 tp->tty_outcum += count;
294 if ((tp->tty_outleft -= count) == 0) {
295 /* Output is finished, reply to the writer. */
296 if (tp->tty_outcaller != KERNEL)
297 chardriver_reply_task(tp->tty_outcaller, tp->tty_outid,
298 tp->tty_outcum);
299 tp->tty_outcum = 0;
300 tp->tty_outcaller = NONE;
301 }
302 }
303 if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
304 /* Oops, the line has hung up. */
305 if (tp->tty_outcaller != KERNEL)
306 chardriver_reply_task(tp->tty_outcaller, tp->tty_outid, EIO);
307 tp->tty_outleft = tp->tty_outcum = 0;
308 tp->tty_outcaller = NONE;
309 }
310
311 return 1;
312 }
313
314 /*===========================================================================*
315 * rs_echo *
316 *===========================================================================*/
rs_echo(tp,c)317 static void rs_echo(tp, c)
318 tty_t *tp; /* which TTY */
319 int c; /* character to echo */
320 {
321 /* Echo one character. (Like rs_write, but only one character, optionally.) */
322
323 rs232_t *rs = tp->tty_priv;
324 int count, ocount;
325
326 ocount = buflen(rs->obuf) - rs->ocount;
327 if (ocount == 0) return; /* output buffer full */
328 count = 1;
329 *rs->ohead = c; /* add one character */
330
331 out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
332 if (count == 0) return;
333
334 rs->ocount += ocount;
335 rs_ostart(rs);
336 if ((rs->ohead += ocount) >= bufend(rs->obuf)) rs->ohead -= buflen(rs->obuf);
337 }
338
339 /*===========================================================================*
340 * rs_ioctl *
341 *===========================================================================*/
rs_ioctl(tty_t * tp,int UNUSED (dummy))342 static int rs_ioctl(tty_t *tp, int UNUSED(dummy))
343 /* tp; which TTY */
344 {
345 /* Reconfigure the line as soon as the output has drained. */
346 rs232_t *rs = tp->tty_priv;
347
348 rs->drain = TRUE;
349 return 0; /* dummy */
350 }
351
352 /*===========================================================================*
353 * rs_config *
354 *===========================================================================*/
rs_config(rs232_t * rs)355 static void rs_config(rs232_t *rs)
356 /* rs which line */
357 {
358 /* Set various line control parameters for RS232 I/O.
359 * If DataBits == 5 and StopBits == 2, 8250 will generate 1.5 stop bits.
360 * The 8250 can't handle split speed, so we use the input speed.
361 */
362
363 tty_t *tp = rs->tty;
364 int divisor;
365 int line_controls;
366 static struct speed2divisor {
367 speed_t speed;
368 int divisor;
369 } s2d[] = {
370 #if defined(__i386__)
371 { B50, UART_FREQ / 50 },
372 #endif
373 { B75, UART_FREQ / 75 },
374 { B110, UART_FREQ / 110 },
375 { B134, UART_FREQ * 10 / 1345 },
376 { B150, UART_FREQ / 150 },
377 { B200, UART_FREQ / 200 },
378 { B300, UART_FREQ / 300 },
379 { B600, UART_FREQ / 600 },
380 { B1200, UART_FREQ / 1200 },
381 #if defined(__i386__)
382 { B1800, UART_FREQ / 1800 },
383 #endif
384 { B2400, UART_FREQ / 2400 },
385 { B4800, UART_FREQ / 4800 },
386 { B9600, UART_FREQ / 9600 },
387 { B19200, UART_FREQ / 19200 },
388 #if defined(__i386__)
389 { B38400, UART_FREQ / 38400 },
390 { B57600, UART_FREQ / 57600 },
391 { B115200, UART_FREQ / 115200L },
392 #endif
393 };
394 struct speed2divisor *s2dp;
395
396 /* RS232 needs to know the xoff character, and if CTS works. */
397 rs->oxoff = tp->tty_termios.c_cc[VSTOP];
398 rs->cts = (tp->tty_termios.c_cflag & CLOCAL) ? MS_CTS : 0;
399
400 /* Look up the 8250 rate divisor from the output speed. */
401 divisor = 0;
402 for (s2dp = s2d; s2dp < s2d + sizeof(s2d)/sizeof(s2d[0]); s2dp++) {
403 if (s2dp->speed == tp->tty_termios.c_ospeed) divisor = s2dp->divisor;
404 }
405 if (divisor == 0) return; /* B0? */
406
407 /* Compute line control flag bits. */
408 line_controls = 0;
409 if (tp->tty_termios.c_cflag & PARENB) {
410 line_controls |= LC_PARITY;
411 if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= LC_PAREVEN;
412 }
413
414 if (divisor >= (UART_FREQ / 110)) line_controls |= LC_2STOP_BITS;
415
416 /* which word size is configured? set the bits explicitly. */
417 if((tp->tty_termios.c_cflag & CSIZE) == CS5)
418 line_controls |= LC_CS5;
419 else if((tp->tty_termios.c_cflag & CSIZE) == CS6)
420 line_controls |= LC_CS6;
421 else if((tp->tty_termios.c_cflag & CSIZE) == CS7)
422 line_controls |= LC_CS7;
423 else if((tp->tty_termios.c_cflag & CSIZE) == CS8)
424 line_controls |= LC_CS8;
425 else printf("rs232: warning: no known word size set\n");
426
427 /* Select the baud rate divisor registers and change the rate. */
428 sys_outb(rs->line_ctl_port, LC_ADDRESS_DIVISOR);
429 sys_outb(rs->div_low_port, divisor);
430 sys_outb(rs->div_hi_port, divisor >> 8);
431
432 /* Change the line controls and reselect the usual registers. */
433 sys_outb(rs->line_ctl_port, line_controls);
434
435 rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
436 if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE)
437 rs->ostate &= ~ORAW;
438 }
439
440 /*===========================================================================*
441 * rs_init *
442 *===========================================================================*/
rs_init(tty_t * tp)443 void rs_init(tty_t *tp)
444 /* tp which TTY */
445 {
446 u32_t dummy;
447 /* Initialize RS232 for one line. */
448
449 register rs232_t *rs;
450 int line;
451 port_t this_8250;
452 int s, irq;
453 char l[10];
454
455 /* Associate RS232 and TTY structures. */
456 line = tp - &tty_table[NR_CONS];
457
458 /* See if kernel debugging is enabled; if so, don't initialize this
459 * serial line, making tty not look at the irq and returning ENXIO
460 * for all requests on it from userland. (The kernel will use it.)
461 */
462 if(env_get_param(SERVARNAME, l, sizeof(l)-1) == OK && atoi(l) == line) {
463 printf("TTY: not initializing rs232 line %d (in use by kernel)\n",
464 line);
465 return;
466 }
467
468 rs = tp->tty_priv = &rs_lines[line];
469 rs->tty = tp;
470
471 /* Set up input queue. */
472 rs->ihead = rs->itail = rs->ibuf;
473
474 /* Precalculate port numbers for speed. Magic numbers in the code (once). */
475 this_8250 = addr_8250[line];
476 rs->xmit_port = this_8250 + 0;
477 rs->recv_port = this_8250 + 0;
478 rs->div_low_port = this_8250 + 0;
479 rs->div_hi_port = this_8250 + 1;
480 rs->int_enab_port = this_8250 + 1;
481 rs->int_id_port = this_8250 + 2;
482 rs->line_ctl_port = this_8250 + 3;
483 rs->modem_ctl_port = this_8250 + 4;
484 rs->line_status_port = this_8250 + 5;
485 rs->modem_status_port = this_8250 + 6;
486
487 /* Set up the hardware to a base state, in particular
488 * o turn off DTR (MC_DTR) to try to stop the external device.
489 * o be careful about the divisor latch. Some BIOS's leave it enabled
490 * here and that caused trouble (no interrupts) in version 1.5 by
491 * hiding the interrupt enable port in the next step, and worse trouble
492 * (continual interrupts) in an old version by hiding the receiver
493 * port in the first interrupt. Call rs_ioctl() early to avoid this.
494 * o disable interrupts at the chip level, to force an edge transition
495 * on the 8259 line when interrupts are next enabled and active.
496 * RS232 interrupts are guaranteed to be disabled now by the 8259
497 * mask, but there used to be trouble if the mask was set without
498 * handling a previous interrupt.
499 */
500 istop(rs); /* sets modem_ctl_port */
501 rs_config(rs);
502 sys_outb(rs->int_enab_port, 0);
503
504 /* Clear any harmful leftover interrupts. An output interrupt is harmless
505 * and will occur when interrupts are enabled anyway. Set up the output
506 * queue using the status from clearing the modem status interrupt.
507 */
508 if ((s = sys_inb(rs->line_status_port, &dummy)) != OK)
509 printf("TTY: sys_inb() failed: %d", s);
510 if ((s = sys_inb(rs->recv_port, &dummy)) != OK)
511 printf("TTY: sys_inb() failed: %d", s);
512 rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */
513 rs->ohead = rs->otail = rs->obuf;
514
515 /* Enable interrupts for both interrupt controller and device. */
516 irq = (line & 1) == 0 ? RS232_IRQ : SECONDARY_IRQ;
517
518 rs->irq = irq;
519 rs->irq_hook_id = rs->irq; /* call back with irq line number */
520 if (sys_irqsetpolicy(irq, IRQ_REENABLE, &rs->irq_hook_id) != OK) {
521 printf("RS232: Couldn't obtain hook for irq %d\n", irq);
522 } else {
523 if (sys_irqenable(&rs->irq_hook_id) != OK) {
524 printf("RS232: Couldn't enable irq %d (hooked)\n", irq);
525 }
526 }
527
528 rs_irq_set |= (1 << irq);
529 #if UART_RTSCTS_OFF
530 sys_outb(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_RECEIVER_READY | IE_TRANSMITTER_READY);
531 #else
532 sys_outb(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_MODEM_STATUS_CHANGE
533 | IE_RECEIVER_READY | IE_TRANSMITTER_READY);
534 #endif
535
536 /* Fill in TTY function hooks. */
537 tp->tty_devread = rs_read;
538 tp->tty_devwrite = rs_write;
539 tp->tty_echo = rs_echo;
540 tp->tty_icancel = rs_icancel;
541 tp->tty_ocancel = rs_ocancel;
542 tp->tty_ioctl = rs_ioctl;
543 tp->tty_break_on = rs_break_on;
544 tp->tty_break_off = rs_break_off;
545 tp->tty_close = rs_close;
546
547 /* Tell external device we are ready. */
548 istart(rs);
549
550 }
551
552 /*===========================================================================*
553 * rs_interrupt *
554 *===========================================================================*/
rs_interrupt(message * m)555 void rs_interrupt(message *m)
556 {
557 unsigned long irq_set;
558 int i;
559 rs232_t *rs;
560
561 irq_set= m->m_notify.interrupts;
562 for (i= 0, rs = rs_lines; i<NR_RS_LINES; i++, rs++)
563 {
564 if (irq_set & (1 << rs->irq))
565 rs232_handler(rs);
566 }
567 }
568
569 /*===========================================================================*
570 * rs_icancel *
571 *===========================================================================*/
rs_icancel(tty_t * tp,int UNUSED (dummy))572 static int rs_icancel(tty_t *tp, int UNUSED(dummy))
573 {
574 /* Cancel waiting input. */
575 rs232_t *rs = tp->tty_priv;
576
577 rs->icount = 0;
578 rs->itail = rs->ihead;
579 istart(rs);
580
581 return 0; /* dummy */
582 }
583
584 /*===========================================================================*
585 * rs_ocancel *
586 *===========================================================================*/
rs_ocancel(tty_t * tp,int UNUSED (dummy))587 static int rs_ocancel(tty_t *tp, int UNUSED(dummy))
588 {
589 /* Cancel pending output. */
590 rs232_t *rs = tp->tty_priv;
591
592 rs->ostate &= ~(ODONE | OQUEUED);
593 rs->ocount = 0;
594 rs->otail = rs->ohead;
595
596 return 0; /* dummy */
597 }
598
599 /*===========================================================================*
600 * rs_read *
601 *===========================================================================*/
rs_read(tty_t * tp,int try)602 static int rs_read(tty_t *tp, int try)
603 {
604 /* Process characters from the circular input buffer. */
605
606 rs232_t *rs = tp->tty_priv;
607 int icount, count, ostate;
608
609 if (!(tp->tty_termios.c_cflag & CLOCAL)) {
610 if (try) return 1;
611 /* Send a SIGHUP if hangup detected. */
612 ostate = rs->ostate;
613 rs->ostate &= ~ODEVHUP; /* save ostate, clear DEVHUP */
614 if (ostate & ODEVHUP) {
615 sigchar(tp, SIGHUP, 1);
616 tp->tty_termios.c_ospeed = B0; /* Disable further I/O. */
617 return 0;
618 }
619 }
620
621 if (try) {
622 if (rs->icount > 0)
623 return 1;
624 return 0;
625 }
626
627 while ((count = rs->icount) > 0) {
628 icount = bufend(rs->ibuf) - rs->itail;
629 if (count > icount) count = icount;
630
631 /* Perform input processing on (part of) the input buffer. */
632 if ((count = in_process(tp, rs->itail, count)) == 0) break;
633
634 rs->icount -= count;
635 if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs);
636 if ((rs->itail += count) == bufend(rs->ibuf)) rs->itail = rs->ibuf;
637 }
638
639 return 0;
640 }
641
642 /*===========================================================================*
643 * rs_ostart *
644 *===========================================================================*/
rs_ostart(rs232_t * rs)645 static void rs_ostart(rs232_t *rs)
646 {
647 /* Tell RS232 there is something waiting in the output buffer. */
648
649 rs->ostate |= OQUEUED;
650 if (txready(rs)) out_int(rs);
651 }
652
653 /*===========================================================================*
654 * rs_break_on *
655 *===========================================================================*/
rs_break_on(tty_t * tp,int UNUSED (dummy))656 static int rs_break_on(tty_t *tp, int UNUSED(dummy))
657 {
658 /* Raise break condition. */
659 rs232_t *rs = tp->tty_priv;
660 u32_t line_controls;
661 int s;
662
663 if ((s = sys_inb(rs->line_ctl_port, &line_controls)) != OK)
664 printf("TTY: sys_inb() failed: %d", s);
665 sys_outb(rs->line_ctl_port, line_controls | LC_BREAK);
666 return 0; /* dummy */
667 }
668
669 /*===========================================================================*
670 * rs_break_off *
671 *===========================================================================*/
rs_break_off(tty_t * tp,int UNUSED (dummy))672 static int rs_break_off(tty_t *tp, int UNUSED(dummy))
673 {
674 /* Clear break condition. */
675 rs232_t *rs = tp->tty_priv;
676 u32_t line_controls;
677 int s;
678
679 if ((s = sys_inb(rs->line_ctl_port, &line_controls)) != OK)
680 printf("TTY: sys_inb() failed: %d", s);
681 sys_outb(rs->line_ctl_port, line_controls & ~LC_BREAK);
682 return 0; /* dummy */
683 }
684
685 /*===========================================================================*
686 * rs_close *
687 *===========================================================================*/
rs_close(tty_t * tp,int UNUSED (dummy))688 static int rs_close(tty_t *tp, int UNUSED(dummy))
689 {
690 /* The line is closed; optionally hang up. */
691 rs232_t *rs = tp->tty_priv;
692
693 if (tp->tty_termios.c_cflag & HUPCL) {
694 sys_outb(rs->modem_ctl_port, MC_OUT2 | MC_RTS);
695 }
696 return 0; /* dummy */
697 }
698
699 /* Low level (interrupt) routines. */
700
701 /*===========================================================================*
702 * rs232_handler *
703 *===========================================================================*/
rs232_handler(struct rs232 * rs)704 static void rs232_handler(struct rs232 *rs)
705 {
706 /* Interrupt hander for RS232. */
707 int s;
708 int trying = 1000;
709
710 while (trying--) {
711 u32_t v;
712 /* Loop to pick up ALL pending interrupts for device.
713 * This usually just wastes time unless the hardware has a buffer
714 * (and then we have to worry about being stuck in the loop too long).
715 * Unfortunately, some serial cards lock up without this.
716 */
717 if ((s = sys_inb(rs->int_id_port, &v)) != OK)
718 panic("TTY: sys_inb() failed: %d", s);
719
720 /* do we have interrupt info? */
721 if(v & IS_NOTPENDING) return;
722
723 /* what kind of interrupt? */
724 switch (v & IS_IDBITS) {
725 case IS_RECEIVER_READY:
726 in_int(rs);
727 continue;
728 case IS_TRANSMITTER_READY:
729 out_int(rs);
730 continue;
731 case IS_MODEM_STATUS_CHANGE:
732 modem_int(rs);
733 continue;
734 case IS_LINE_STATUS_CHANGE:
735 line_int(rs);
736 continue;
737 }
738 return;
739 }
740
741 printf("tty rs232: enough!\n");
742 }
743
744 /*===========================================================================*
745 * in_int *
746 *===========================================================================*/
in_int(register rs232_t * rs)747 static void in_int(register rs232_t *rs)
748 /* rs line with input interrupt */
749 {
750 /* Read the data which just arrived.
751 * If it is the oxoff char, clear OSWREADY, else if OSWREADY was clear, set
752 * it and restart output (any char does this, not just xon).
753 * Put data in the buffer if room, otherwise discard it.
754 * Set a flag for the clock interrupt handler to eventually notify TTY.
755 */
756 int s;
757 u32_t c;
758
759 #if 0 /* Enable this if you want serial input in the kernel */
760 return;
761 #endif
762
763 if ((s = sys_inb(rs->recv_port, &c)) != OK)
764 printf("TTY: sys_inb() failed: %d", s);
765
766 if (!(rs->ostate & ORAW)) {
767 if (c == rs->oxoff) {
768 rs->ostate &= ~OSWREADY;
769 } else
770 if (!(rs->ostate & OSWREADY)) {
771 rs->ostate |= OSWREADY;
772 if (txready(rs)) out_int(rs);
773 }
774 }
775
776 if (rs->icount == buflen(rs->ibuf))
777 {
778 printf("in_int: discarding byte\n");
779 return; /* input buffer full, discard */
780 }
781
782 if (++rs->icount == RS_IHIGHWATER && rs->idevready) istop(rs);
783 *rs->ihead = c;
784 if (++rs->ihead == bufend(rs->ibuf)) rs->ihead = rs->ibuf;
785 if (rs->icount == 1) {
786 rs->tty->tty_events = 1;
787 force_timeout();
788 }
789 }
790
791 /*===========================================================================*
792 * line_int *
793 *===========================================================================*/
line_int(register rs232_t * rs)794 static void line_int(register rs232_t *rs)
795 /* rs line with line status interrupt */
796 {
797 /* Check for and record errors. */
798 int r;
799 u32_t s;
800
801 if ((r = sys_inb(rs->line_status_port, &s)) != OK)
802 printf("TTY: sys_inb() failed: %d", r);
803 rs->lstatus = s;
804 if (rs->lstatus & LS_FRAMING_ERR) ++rs->framing_errors;
805 if (rs->lstatus & LS_OVERRUN_ERR) ++rs->overrun_errors;
806 if (rs->lstatus & LS_PARITY_ERR) ++rs->parity_errors;
807 if (rs->lstatus & LS_BREAK_INTERRUPT) ++rs->break_interrupts;
808 }
809
810 /*===========================================================================*
811 * modem_int *
812 *===========================================================================*/
modem_int(register rs232_t * rs)813 static void modem_int(register rs232_t *rs)
814 /* rs line with modem interrupt */
815 {
816 /* Get possibly new device-ready status, and clear ODEVREADY if necessary.
817 * If the device just became ready, restart output.
818 */
819
820 if (devhup(rs)) {
821 rs->ostate |= ODEVHUP;
822 rs->tty->tty_events = 1;
823 force_timeout();
824 }
825
826 if (!devready(rs))
827 rs->ostate &= ~ODEVREADY;
828 else if (!(rs->ostate & ODEVREADY)) {
829 rs->ostate |= ODEVREADY;
830 if (txready(rs)) out_int(rs);
831 }
832 }
833
834 /*===========================================================================*
835 * out_int *
836 *===========================================================================*/
out_int(register rs232_t * rs)837 static void out_int(register rs232_t *rs)
838 /* rs; line with output interrupt */
839 {
840 /* If there is output to do and everything is ready, do it (local device is
841 * known ready).
842 * Notify TTY when the buffer goes empty.
843 */
844
845 while (txready(rs) && rs->ostate >= (ODEVREADY | OQUEUED | OSWREADY)) {
846 /* Bit test allows ORAW and requires the others. */
847 sys_outb(rs->xmit_port, *rs->otail);
848 if (++rs->otail == bufend(rs->obuf)) rs->otail = rs->obuf;
849 if (--rs->ocount == 0) {
850 rs->ostate ^= (ODONE | OQUEUED); /* ODONE on, OQUEUED off */
851 rs->tty->tty_events = 1;
852 force_timeout();
853 } else
854 if (rs->ocount == RS_OLOWWATER) { /* running low? */
855 rs->tty->tty_events = 1;
856 force_timeout();
857 }
858 }
859 }
860 #endif /* NR_RS_LINES > 0 */
861
862