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