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 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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