rs232.c

Go to the documentation of this file.
00001 #include <minix/config.h>
00002 /*---------------------------------------------------------------------------*
00003  *              rs232.c - serial driver for 8250 and 16450 UARTs             *
00004  *              Added support for Atari ST M68901 and YM-2149   --kub        *
00005  *---------------------------------------------------------------------------*/
00006 
00007 #include "../drivers.h"
00008 #include <termios.h>
00009 #include <signal.h>
00010 #include "tty.h"
00011 
00012 #if NR_RS_LINES > 0
00013 
00014 #if (MACHINE != IBM_PC) && (MACHINE != ATARI)
00015 #error                          /* rs232.c only supports PC and Atari ST */
00016 #endif
00017 
00018 #if (MACHINE == ATARI)
00019 #include "staddr.h"
00020 #include "stsound.h"
00021 #include "stmfp.h"
00022 #if (NR_RS_LINES > 1)
00023 #error                          /* Only one physical RS232 line available */
00024 #endif
00025 #endif
00026 
00027 #if (MACHINE == IBM_PC)         /* PC/AT 8250/16450 chip combination */
00028 
00029 /* 8250 constants. */
00030 #define UART_FREQ         115200L       /* timer frequency */
00031 
00032 /* Interrupt enable bits. */
00033 #define IE_RECEIVER_READY       1
00034 #define IE_TRANSMITTER_READY    2
00035 #define IE_LINE_STATUS_CHANGE   4
00036 #define IE_MODEM_STATUS_CHANGE  8
00037 
00038 /* Interrupt status bits. */
00039 #define IS_MODEM_STATUS_CHANGE  0
00040 #define IS_TRANSMITTER_READY    2
00041 #define IS_RECEIVER_READY       4
00042 #define IS_LINE_STATUS_CHANGE   6
00043 
00044 /* Line control bits. */
00045 #define LC_2STOP_BITS        0x04
00046 #define LC_PARITY            0x08
00047 #define LC_PAREVEN           0x10
00048 #define LC_BREAK             0x40
00049 #define LC_ADDRESS_DIVISOR   0x80
00050 
00051 /* Line status bits. */
00052 #define LS_OVERRUN_ERR          2
00053 #define LS_PARITY_ERR           4
00054 #define LS_FRAMING_ERR          8
00055 #define LS_BREAK_INTERRUPT   0x10
00056 #define LS_TRANSMITTER_READY 0x20
00057 
00058 /* Modem control bits. */
00059 #define MC_DTR                  1
00060 #define MC_RTS                  2
00061 #define MC_OUT2                 8       /* required for PC & AT interrupts */
00062 
00063 /* Modem status bits. */
00064 #define MS_CTS               0x10
00065 #define MS_RLSD              0x80       /* Received Line Signal Detect */
00066 #define MS_DRLSD             0x08       /* RLSD Delta */
00067 
00068 #else /* MACHINE == ATARI */            /* Atari ST 68901 USART */
00069 
00070 /* Most of the USART constants are already defined in stmfp.h . The local
00071  * definitions made here are for keeping C code changes smaller.   --kub
00072  */
00073 
00074 #define UART_FREQ          19200L       /* timer frequency */
00075 
00076 /* Line status bits. */
00077 #define LS_OVERRUN_ERR       R_OE
00078 #define LS_PARITY_ERR        R_PE
00079 #define LS_FRAMING_ERR       R_FE
00080 #define LS_BREAK_INTERRUPT   R_BREAK
00081 
00082 /* Modem status bits. */
00083 #define MS_CTS               IO_SCTS    /* 0x04 */
00084 
00085 #endif /* MACHINE == ATARI */
00086 
00087 #define DATA_BITS_SHIFT         8       /* amount data bits shifted in mode */
00088 #define DEF_BAUD             1200       /* default baud rate */
00089 
00090 #define RS_IBUFSIZE          1024       /* RS232 input buffer size */
00091 #define RS_OBUFSIZE          1024       /* RS232 output buffer size */
00092 
00093 /* Input buffer watermarks.
00094  * The external device is asked to stop sending when the buffer
00095  * exactly reaches high water, or when TTY requests it.  Sending restarts
00096  * when the input buffer empties below the low watermark.
00097  */
00098 #define RS_ILOWWATER    (1 * RS_IBUFSIZE / 4)
00099 #define RS_IHIGHWATER   (3 * RS_IBUFSIZE / 4)
00100 
00101 /* Output buffer low watermark.
00102  * TTY is notified when the output buffer empties below the low watermark, so
00103  * it may continue filling the buffer if doing a large write.
00104  */
00105 #define RS_OLOWWATER    (1 * RS_OBUFSIZE / 4)
00106 
00107 #if (MACHINE == IBM_PC)
00108 
00109 /* Macros to handle flow control.
00110  * Interrupts must be off when they are used.
00111  * Time is critical - already the function call for outb() is annoying.
00112  * If outb() can be done in-line, tests to avoid it can be dropped.
00113  * istart() tells external device we are ready by raising RTS.
00114  * istop() tells external device we are not ready by dropping RTS.
00115  * DTR is kept high all the time (it probably should be raised by open and
00116  * dropped by close of the device).
00117  * OUT2 is also kept high all the time.
00118  */
00119 #define istart(rs) \
00120         (sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_RTS | MC_DTR), \
00121                 (rs)->idevready = TRUE)
00122 #define istop(rs) \
00123         (sys_outb((rs)->modem_ctl_port, MC_OUT2 | MC_DTR), \
00124                 (rs)->idevready = FALSE)
00125 
00126 /* Macro to tell if device is ready.  The rs->cts field is set to MS_CTS if
00127  * CLOCAL is in effect for a line without a CTS wire.
00128  */
00129 #define devready(rs) ((my_inb(rs->modem_status_port) | rs->cts) & MS_CTS)
00130 
00131 /* Macro to tell if transmitter is ready. */
00132 #define txready(rs) (my_inb(rs->line_status_port) & LS_TRANSMITTER_READY)
00133 
00134 /* Macro to tell if carrier has dropped.
00135  * The RS232 Carrier Detect (CD) line is usually connected to the 8250
00136  * Received Line Signal Detect pin, reflected by bit MS_RLSD in the Modem
00137  * Status Register.  The MS_DRLSD bit tells if MS_RLSD has just changed state.
00138  * So if MS_DRLSD is set and MS_RLSD cleared, we know that carrier has just
00139  * dropped.
00140  */
00141 #define devhup(rs)      \
00142         ((my_inb(rs->modem_status_port) & (MS_RLSD|MS_DRLSD)) == MS_DRLSD)
00143 
00144 #else /* MACHINE == ATARI */
00145 
00146 /* Macros to handle flow control.
00147  * Time is critical - already the function call for lock()/restore() is
00148  * annoying.
00149  * istart() tells external device we are ready by raising RTS.
00150  * istop() tells external device we are not ready by dropping RTS.
00151  * DTR is kept high all the time (it probably should be raised by open and
00152  * dropped by close of the device). NOTE: The modem lines are active low.
00153  */
00154 #define set_porta(msk,val) { register int s = lock();           \
00155                              SOUND->sd_selr = YM_IOA;           \
00156                              SOUND->sd_wdat =                   \
00157                                 SOUND->sd_rdat & (msk) | (val); \
00158                              restore(s);        }
00159 #define istart(rs)         { set_porta( ~(PA_SRTS|PA_SDTR),0 ); \
00160                              (rs)->idevready = TRUE;    }
00161 #define istop(rs)          { set_porta( ~PA_SDTR, PA_SRTS );    \
00162                              (rs)->idevready = FALSE;   }
00163 
00164 /* Macro to tell if device is ready.  The rs->cts field is set to MS_CTS if
00165  * CLOCAL is in effect for a line without a CTS wire.
00166  */
00167 #define devready(rs)         ((~MFP->mf_gpip | rs->cts) & MS_CTS)
00168 
00169 /* Transmitter ready test */
00170 #define txready(rs)          (MFP->mf_tsr & (T_EMPTY | T_UE))
00171 
00172 #endif /* MACHINE == ATARI */
00173 
00174 /* Types. */
00175 typedef unsigned char bool_t;   /* boolean */
00176 
00177 /* RS232 device structure, one per device. */
00178 typedef struct rs232 {
00179   tty_t *tty;                   /* associated TTY structure */
00180 
00181   int icount;                   /* number of bytes in the input buffer */
00182   char *ihead;                  /* next free spot in input buffer */
00183   char *itail;                  /* first byte to give to TTY */
00184   bool_t idevready;             /* nonzero if we are ready to receive (RTS) */
00185   char cts;                     /* normally 0, but MS_CTS if CLOCAL is set */
00186 
00187   unsigned char ostate;         /* combination of flags: */
00188 #define ODONE          1        /* output completed (< output enable bits) */
00189 #define ORAW           2        /* raw mode for xoff disable (< enab. bits) */
00190 #define OWAKEUP        4        /* tty_wakeup() pending (asm code only) */
00191 #define ODEVREADY MS_CTS        /* external device hardware ready (CTS) */
00192 #define OQUEUED     0x20        /* output buffer not empty */
00193 #define OSWREADY    0x40        /* external device software ready (no xoff) */
00194 #define ODEVHUP  MS_RLSD        /* external device has dropped carrier */
00195 #define OSOFTBITS  (ODONE | ORAW | OWAKEUP | OQUEUED | OSWREADY)
00196                                 /* user-defined bits */
00197 #if (OSOFTBITS | ODEVREADY | ODEVHUP) == OSOFTBITS
00198                                 /* a weak sanity check */
00199 #error                          /* bits are not unique */
00200 #endif
00201   unsigned char oxoff;          /* char to stop output */
00202   bool_t inhibited;             /* output inhibited? (follows tty_inhibited) */
00203   bool_t drain;                 /* if set drain output and reconfigure line */
00204   int ocount;                   /* number of bytes in the output buffer */
00205   char *ohead;                  /* next free spot in output buffer */
00206   char *otail;                  /* next char to output */
00207 
00208 #if (MACHINE == IBM_PC)
00209   port_t xmit_port;             /* i/o ports */
00210   port_t recv_port;
00211   port_t div_low_port;
00212   port_t div_hi_port;
00213   port_t int_enab_port;
00214   port_t int_id_port;
00215   port_t line_ctl_port;
00216   port_t modem_ctl_port;
00217   port_t line_status_port;
00218   port_t modem_status_port;
00219 #endif
00220 
00221   unsigned char lstatus;        /* last line status */
00222   unsigned char pad;            /* ensure alignment for 16-bit ints */
00223   unsigned framing_errors;      /* error counts (no reporting yet) */
00224   unsigned overrun_errors;
00225   unsigned parity_errors;
00226   unsigned break_interrupts;
00227 
00228   int irq;                      /* irq for this line */
00229   int irq_hook_id;              /* interrupt hook */
00230 
00231   char ibuf[RS_IBUFSIZE];       /* input buffer */
00232   char obuf[RS_OBUFSIZE];       /* output buffer */
00233 } rs232_t;
00234 
00235 PUBLIC rs232_t rs_lines[NR_RS_LINES];
00236 
00237 /* Table and macro to translate an RS232 line number to its rs_lines entry. */
00238 PRIVATE rs232_t *p_rs_addr[NR_RS_LINES];
00239 
00240 #define rs_addr(line)   (p_rs_addr[line])
00241 
00242 #if (MACHINE == IBM_PC)
00243 /* 8250 base addresses. */
00244 PRIVATE port_t addr_8250[] = {
00245   0x3F8,        /* COM1 */
00246   0x2F8,        /* COM2 */
00247   0x3E8,        /* COM3 */
00248   0x2E8,        /* COM4 */
00249 };
00250 #endif
00251 
00252 FORWARD _PROTOTYPE( void in_int, (rs232_t *rs)                          );
00253 FORWARD _PROTOTYPE( void line_int, (rs232_t *rs)                        );
00254 FORWARD _PROTOTYPE( void modem_int, (rs232_t *rs)                       );
00255 FORWARD _PROTOTYPE( int rs_write, (tty_t *tp, int try)                  );
00256 FORWARD _PROTOTYPE( void rs_echo, (tty_t *tp, int c)                    );
00257 FORWARD _PROTOTYPE( int rs_ioctl, (tty_t *tp, int try)                  );
00258 FORWARD _PROTOTYPE( void rs_config, (rs232_t *rs)                       );
00259 FORWARD _PROTOTYPE( int rs_read, (tty_t *tp, int try)                   );
00260 FORWARD _PROTOTYPE( int rs_icancel, (tty_t *tp, int try)                );
00261 FORWARD _PROTOTYPE( int rs_ocancel, (tty_t *tp, int try)                );
00262 FORWARD _PROTOTYPE( void rs_ostart, (rs232_t *rs)                       );
00263 FORWARD _PROTOTYPE( int rs_break, (tty_t *tp, int try)                  );
00264 FORWARD _PROTOTYPE( int rs_close, (tty_t *tp, int try)                  );
00265 FORWARD _PROTOTYPE( void out_int, (rs232_t *rs)                         );
00266 FORWARD _PROTOTYPE( void rs232_handler, (rs232_t *rs)                   );
00267 
00268 /* XXX */
00269 PRIVATE void lock(void) {}
00270 PRIVATE void unlock(void) {}
00271 
00272 PRIVATE int my_inb(port_t port)
00273 {
00274         int r;
00275         unsigned long v = 0;
00276         r = sys_inb(port, &v);
00277         if (r != OK)
00278                 printf("RS232 warning: failed inb 0x%x\n", port);
00279 
00280         return (int) v;
00281 }
00282 
00283 /*===========================================================================*
00284  *                              rs_write                                     *
00285  *===========================================================================*/
00286 PRIVATE int rs_write(tp, try)
00287 register tty_t *tp;
00288 int try;
00289 {
00290 /* (*devwrite)() routine for RS232. */
00291 
00292   rs232_t *rs = tp->tty_priv;
00293   int count, ocount;
00294 
00295   if (rs->inhibited != tp->tty_inhibited) {
00296         /* Inhibition state has changed. */
00297         lock();
00298         rs->ostate |= OSWREADY;
00299         if (tp->tty_inhibited) rs->ostate &= ~OSWREADY;
00300         unlock();
00301         rs->inhibited = tp->tty_inhibited;
00302   }
00303 
00304   if (rs->drain) {
00305         /* Wait for the line to drain then reconfigure and continue output. */
00306         if (rs->ocount > 0) return 0;
00307         rs->drain = FALSE;
00308         rs_config(rs);
00309   }
00310 
00311   /* While there is something to do. */
00312   for (;;) {
00313         ocount = buflen(rs->obuf) - rs->ocount;
00314         count = bufend(rs->obuf) - rs->ohead;
00315         if (count > ocount) count = ocount;
00316         if (count > tp->tty_outleft) count = tp->tty_outleft;
00317         if (count == 0 || tp->tty_inhibited) {
00318                 if (try) return 0;
00319                 break;
00320         }
00321         if (try) return 1;
00322 
00323         /* Copy from user space to the RS232 output buffer. */
00324         sys_vircopy(tp->tty_outproc, D, (vir_bytes) tp->tty_out_vir, 
00325                 SELF, D, (vir_bytes) rs->ohead, (phys_bytes) count);
00326 
00327         /* Perform output processing on the output buffer. */
00328         out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
00329         if (count == 0) break;
00330 
00331         /* Assume echoing messed up by output. */
00332         tp->tty_reprint = TRUE;
00333 
00334         /* Bookkeeping. */
00335         lock();                 /* protect interrupt sensitive rs->ocount */
00336         rs->ocount += ocount;
00337         rs_ostart(rs);
00338         unlock();
00339         if ((rs->ohead += ocount) >= bufend(rs->obuf))
00340                 rs->ohead -= buflen(rs->obuf);
00341         tp->tty_out_vir += count;
00342         tp->tty_outcum += count;
00343         if ((tp->tty_outleft -= count) == 0) {
00344                 /* Output is finished, reply to the writer. */
00345                 tty_reply(tp->tty_outrepcode, tp->tty_outcaller,
00346                                         tp->tty_outproc, tp->tty_outcum);
00347                 tp->tty_outcum = 0;
00348         }
00349   }
00350   if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) {
00351         /* Oops, the line has hung up. */
00352         tty_reply(tp->tty_outrepcode, tp->tty_outcaller, tp->tty_outproc, EIO);
00353         tp->tty_outleft = tp->tty_outcum = 0;
00354   }
00355 
00356   return 1;
00357 }
00358 
00359 /*===========================================================================*
00360  *                              rs_echo                                      *
00361  *===========================================================================*/
00362 PRIVATE void rs_echo(tp, c)
00363 tty_t *tp;                      /* which TTY */
00364 int c;                          /* character to echo */
00365 {
00366 /* Echo one character.  (Like rs_write, but only one character, optionally.) */
00367 
00368   rs232_t *rs = tp->tty_priv;
00369   int count, ocount;
00370 
00371   ocount = buflen(rs->obuf) - rs->ocount;
00372   if (ocount == 0) return;              /* output buffer full */
00373   count = 1;
00374   *rs->ohead = c;                       /* add one character */
00375 
00376   out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount);
00377   if (count == 0) return;
00378 
00379   lock();
00380   rs->ocount += ocount;
00381   rs_ostart(rs);
00382   unlock();
00383   if ((rs->ohead += ocount) >= bufend(rs->obuf)) rs->ohead -= buflen(rs->obuf);
00384 }
00385 
00386 /*===========================================================================*
00387  *                              rs_ioctl                                     *
00388  *===========================================================================*/
00389 PRIVATE int rs_ioctl(tp, dummy)
00390 tty_t *tp;                      /* which TTY */
00391 int dummy;
00392 {
00393 /* Reconfigure the line as soon as the output has drained. */
00394   rs232_t *rs = tp->tty_priv;
00395 
00396   rs->drain = TRUE;
00397   return 0;     /* dummy */
00398 }
00399 
00400 /*===========================================================================*
00401  *                              rs_config                                    *
00402  *===========================================================================*/
00403 PRIVATE void rs_config(rs)
00404 rs232_t *rs;                    /* which line */
00405 {
00406 /* Set various line control parameters for RS232 I/O.
00407  * If DataBits == 5 and StopBits == 2, 8250 will generate 1.5 stop bits.
00408  * The 8250 can't handle split speed, so we use the input speed.
00409  */
00410 
00411   tty_t *tp = rs->tty;
00412   int divisor;
00413   int line_controls;
00414   static struct speed2divisor {
00415         speed_t speed;
00416         int     divisor;
00417   } s2d[] = {
00418 #if (MACHINE == IBM_PC)
00419         { B50,          UART_FREQ / 50          },
00420 #endif
00421         { B75,          UART_FREQ / 75          },
00422         { B110,         UART_FREQ / 110         },
00423         { B134,         UART_FREQ * 10 / 1345   },
00424         { B150,         UART_FREQ / 150         },
00425         { B200,         UART_FREQ / 200         },
00426         { B300,         UART_FREQ / 300         },
00427         { B600,         UART_FREQ / 600         },
00428         { B1200,        UART_FREQ / 1200        },
00429 #if (MACHINE == IBM_PC)
00430         { B1800,        UART_FREQ / 1800        },
00431 #endif
00432         { B2400,        UART_FREQ / 2400        },
00433         { B4800,        UART_FREQ / 4800        },
00434         { B9600,        UART_FREQ / 9600        },
00435         { B19200,       UART_FREQ / 19200       },
00436 #if (MACHINE == IBM_PC)
00437         { B38400,       UART_FREQ / 38400       },
00438         { B57600,       UART_FREQ / 57600       },
00439         { B115200,      UART_FREQ / 115200L     },
00440 #endif
00441   };
00442   struct speed2divisor *s2dp;
00443 
00444   /* RS232 needs to know the xoff character, and if CTS works. */
00445   rs->oxoff = tp->tty_termios.c_cc[VSTOP];
00446   rs->cts = (tp->tty_termios.c_cflag & CLOCAL) ? MS_CTS : 0;
00447 
00448   /* Look up the 8250 rate divisor from the output speed. */
00449   divisor = 0;
00450   for (s2dp = s2d; s2dp < s2d + sizeof(s2d)/sizeof(s2d[0]); s2dp++) {
00451         if (s2dp->speed == tp->tty_termios.c_ospeed) divisor = s2dp->divisor;
00452   }
00453   if (divisor == 0) return;     /* B0? */
00454 
00455 #if (MACHINE == IBM_PC)
00456   /* Compute line control flag bits. */
00457   line_controls = 0;
00458   if (tp->tty_termios.c_cflag & PARENB) {
00459         line_controls |= LC_PARITY;
00460         if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= LC_PAREVEN;
00461   }
00462   if (divisor >= (UART_FREQ / 110)) line_controls |= LC_2STOP_BITS;
00463   line_controls |= (tp->tty_termios.c_cflag & CSIZE) >> 2;
00464 
00465   /* Lock out interrupts while setting the speed. The receiver register is
00466    * going to be hidden by the div_low register, but the input interrupt
00467    * handler relies on reading it to clear the interrupt and avoid looping
00468    * forever.
00469    */
00470   lock();
00471 
00472   /* Select the baud rate divisor registers and change the rate. */
00473   sys_outb(rs->line_ctl_port, LC_ADDRESS_DIVISOR);
00474   sys_outb(rs->div_low_port, divisor);
00475   sys_outb(rs->div_hi_port, divisor >> 8);
00476 
00477   /* Change the line controls and reselect the usual registers. */
00478   sys_outb(rs->line_ctl_port, line_controls);
00479 
00480   rs->ostate = devready(rs) | ORAW | OSWREADY;  /* reads modem_ctl_port */
00481   if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE)
00482         rs->ostate &= ~ORAW;
00483 
00484   unlock();
00485 
00486 #else /* MACHINE == ATARI */
00487 
00488   line_controls = U_Q16;
00489   if (tp->tty_termios.c_cflag & PARENB) {
00490         line_controls |= U_PAR;
00491         if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= U_EVEN;
00492   }
00493   line_controls |= (divisor >= (UART_FREQ / 110)) ? U_ST2 : U_ST1;
00494 
00495   switch (tp->tty_termios.c_cflag & CSIZE) {    /* XXX - are U_Dn like CSn? */
00496         case CS5:       line_controls |= U_D5; break;
00497         case CS5:       line_controls |= U_D6; break;
00498         case CS5:       line_controls |= U_D7; break;
00499         case CS5:       line_controls |= U_D8; break;
00500   }
00501   lock();
00502   MFP->mf_ucr = line_controls;
00503   MFP->mf_tddr = divisor;
00504   unlock();
00505 #endif /* MACHINE == ATARI */
00506 }
00507 
00508 /*===========================================================================*
00509  *                              rs_init                                      *
00510  *===========================================================================*/
00511 PUBLIC void rs_init(tp)
00512 tty_t *tp;                      /* which TTY */
00513 {
00514   unsigned long dummy;
00515 /* Initialize RS232 for one line. */
00516 
00517   register rs232_t *rs;
00518   int line;
00519 #if (MACHINE == IBM_PC)
00520   port_t this_8250;
00521   int irq;
00522   long v;
00523 #endif
00524 
00525   /* Associate RS232 and TTY structures. */
00526   line = tp - &tty_table[NR_CONS];
00527   rs = tp->tty_priv = &rs_lines[line];
00528   rs->tty = tp;
00529 
00530   /* Set up input queue. */
00531   rs->ihead = rs->itail = rs->ibuf;
00532 
00533 #if (MACHINE == IBM_PC)
00534   /* Precalculate port numbers for speed. Magic numbers in the code (once). */
00535   this_8250 = addr_8250[line];
00536   rs->xmit_port = this_8250 + 0;
00537   rs->recv_port = this_8250 + 0;
00538   rs->div_low_port = this_8250 + 0;
00539   rs->div_hi_port = this_8250 + 1;
00540   rs->int_enab_port = this_8250 + 1;
00541   rs->int_id_port = this_8250 + 2;
00542   rs->line_ctl_port = this_8250 + 3;
00543   rs->modem_ctl_port = this_8250 + 4;
00544   rs->line_status_port = this_8250 + 5;
00545   rs->modem_status_port = this_8250 + 6;
00546 #endif
00547 
00548   /* Set up the hardware to a base state, in particular
00549    *    o turn off DTR (MC_DTR) to try to stop the external device.
00550    *    o be careful about the divisor latch.  Some BIOS's leave it enabled
00551    *      here and that caused trouble (no interrupts) in version 1.5 by
00552    *      hiding the interrupt enable port in the next step, and worse trouble
00553    *      (continual interrupts) in an old version by hiding the receiver
00554    *      port in the first interrupt.  Call rs_ioctl() early to avoid this.
00555    *    o disable interrupts at the chip level, to force an edge transition
00556    *      on the 8259 line when interrupts are next enabled and active.
00557    *      RS232 interrupts are guaranteed to be disabled now by the 8259
00558    *      mask, but there used to be trouble if the mask was set without
00559    *      handling a previous interrupt.
00560    */
00561   istop(rs);                    /* sets modem_ctl_port */
00562   rs_config(rs);
00563 #if (MACHINE == IBM_PC)
00564   sys_outb(rs->int_enab_port, 0);
00565 #endif
00566 
00567   /* Clear any harmful leftover interrupts.  An output interrupt is harmless
00568    * and will occur when interrupts are enabled anyway.  Set up the output
00569    * queue using the status from clearing the modem status interrupt.
00570    */
00571 #if (MACHINE == IBM_PC)
00572   sys_inb(rs->line_status_port, &dummy);
00573   sys_inb(rs->recv_port, &dummy);
00574 #endif
00575   rs->ostate = devready(rs) | ORAW | OSWREADY;  /* reads modem_ctl_port */
00576   rs->ohead = rs->otail = rs->obuf;
00577 
00578 #if (MACHINE == IBM_PC)
00579   /* Enable interrupts for both interrupt controller and device. */
00580   irq = (line & 1) == 0 ? RS232_IRQ : SECONDARY_IRQ;
00581 
00582   rs->irq = irq;
00583   rs->irq_hook_id = rs->irq;    /* call back with irq line number */
00584   if (sys_irqsetpolicy(irq, IRQ_REENABLE, &rs->irq_hook_id) != OK) {
00585         printf("RS232: Couldn't obtain hook for irq %d\n", irq);
00586   } else {
00587         if (sys_irqenable(&rs->irq_hook_id) != OK)  {
00588                 printf("RS232: Couldn't enable irq %d (hooked)\n", irq);
00589         }
00590   }
00591 
00592   rs_irq_set |= (1 << irq);
00593 
00594   sys_outb(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_MODEM_STATUS_CHANGE
00595                                 | IE_RECEIVER_READY | IE_TRANSMITTER_READY);
00596 #else /* MACHINE == ATARI */
00597   /* Initialize the 68901 chip, then enable interrupts. */
00598   MFP->mf_scr = 0x00;
00599   MFP->mf_tcdcr |= T_Q004;
00600   MFP->mf_rsr = R_ENA;
00601   MFP->mf_tsr = T_ENA;
00602   MFP->mf_aer = (MFP->mf_aer | (IO_SCTS|IO_SDCD)) ^
00603                  (MFP->mf_gpip & (IO_SCTS|IO_SDCD));
00604   MFP->mf_ddr = (MFP->mf_ddr & ~ (IO_SCTS|IO_SDCD));
00605   MFP->mf_iera |= (IA_RRDY|IA_RERR|IA_TRDY|IA_TERR);
00606   MFP->mf_imra |= (IA_RRDY|IA_RERR|IA_TRDY|IA_TERR);
00607   MFP->mf_ierb |= (IB_SCTS|IB_SDCD);
00608   MFP->mf_imrb |= (IB_SCTS|IB_SDCD);
00609 #endif /* MACHINE == ATARI */
00610 
00611   /* Fill in TTY function hooks. */
00612   tp->tty_devread = rs_read;
00613   tp->tty_devwrite = rs_write;
00614   tp->tty_echo = rs_echo;
00615   tp->tty_icancel = rs_icancel;
00616   tp->tty_ocancel = rs_ocancel;
00617   tp->tty_ioctl = rs_ioctl;
00618   tp->tty_break = rs_break;
00619   tp->tty_close = rs_close;
00620 
00621   /* Tell external device we are ready. */
00622   istart(rs);
00623 
00624 }
00625 
00626 /*===========================================================================*
00627  *                              rs_interrupt                                 *
00628  *===========================================================================*/
00629 PUBLIC void rs_interrupt(m)
00630 message *m;                     /* which TTY */
00631 {
00632         unsigned long irq_set;
00633         int i;
00634         rs232_t *rs;
00635 
00636         irq_set= m->NOTIFY_ARG;
00637         for (i= 0, rs = rs_lines; i<NR_RS_LINES; i++, rs++)
00638         {
00639                 if (irq_set & (1 << rs->irq))
00640                         rs232_handler(rs);
00641         }
00642 }
00643 
00644 /*===========================================================================*
00645  *                              rs_icancel                                   *
00646  *===========================================================================*/
00647 PRIVATE int rs_icancel(tp, dummy)
00648 tty_t *tp;                      /* which TTY */
00649 int dummy;
00650 {
00651 /* Cancel waiting input. */
00652   rs232_t *rs = tp->tty_priv;
00653 
00654   lock();
00655   rs->icount = 0;
00656   rs->itail = rs->ihead;
00657   istart(rs);
00658   unlock();
00659 
00660   return 0;     /* dummy */
00661 }
00662 
00663 /*===========================================================================*
00664  *                              rs_ocancel                                   *
00665  *===========================================================================*/
00666 PRIVATE int rs_ocancel(tp, dummy)
00667 tty_t *tp;                      /* which TTY */
00668 int dummy;
00669 {
00670 /* Cancel pending output. */
00671   rs232_t *rs = tp->tty_priv;
00672 
00673   lock();
00674   rs->ostate &= ~(ODONE | OQUEUED);
00675   rs->ocount = 0;
00676   rs->otail = rs->ohead;
00677   unlock();
00678 
00679   return 0;     /* dummy */
00680 }
00681 
00682 /*===========================================================================*
00683  *                              rs_read                                      *
00684  *===========================================================================*/
00685 PRIVATE int rs_read(tp, try)
00686 tty_t *tp;                      /* which tty */
00687 int try;
00688 {
00689 /* Process characters from the circular input buffer. */
00690 
00691   rs232_t *rs = tp->tty_priv;
00692   int icount, count, ostate;
00693 
00694   if (!(tp->tty_termios.c_cflag & CLOCAL)) {
00695         if (try) return 1;
00696         /* Send a SIGHUP if hangup detected. */
00697         lock();
00698         ostate = rs->ostate;
00699         rs->ostate &= ~ODEVHUP;         /* save ostate, clear DEVHUP */
00700         unlock();
00701         if (ostate & ODEVHUP) {
00702                 sigchar(tp, SIGHUP);
00703                 tp->tty_termios.c_ospeed = B0;  /* Disable further I/O. */
00704                 return;
00705         }
00706   }
00707 
00708   if (try) {
00709         if (rs->icount > 0)
00710                 return 1;
00711         return 0;
00712   }
00713 
00714   while ((count = rs->icount) > 0) {
00715         icount = bufend(rs->ibuf) - rs->itail;
00716         if (count > icount) count = icount;
00717 
00718         /* Perform input processing on (part of) the input buffer. */
00719         if ((count = in_process(tp, rs->itail, count)) == 0) break;
00720 
00721         lock();                 /* protect interrupt sensitive variables */
00722         rs->icount -= count;
00723         if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs);
00724         unlock();
00725         if ((rs->itail += count) == bufend(rs->ibuf)) rs->itail = rs->ibuf;
00726   }
00727 }
00728 
00729 /*===========================================================================*
00730  *                              rs_ostart                                    *
00731  *===========================================================================*/
00732 PRIVATE void rs_ostart(rs)
00733 rs232_t *rs;                    /* which rs line */
00734 {
00735 /* Tell RS232 there is something waiting in the output buffer. */
00736 
00737   rs->ostate |= OQUEUED;
00738   if (txready(rs)) out_int(rs);
00739 }
00740 
00741 /*===========================================================================*
00742  *                              rs_break                                     *
00743  *===========================================================================*/
00744 PRIVATE int rs_break(tp, dummy)
00745 tty_t *tp;                      /* which tty */
00746 int dummy;
00747 {
00748 /* Generate a break condition by setting the BREAK bit for 0.4 sec. */
00749   rs232_t *rs = tp->tty_priv;
00750   unsigned long line_controls;
00751 
00752   sys_inb(rs->line_ctl_port, &line_controls);
00753   sys_outb(rs->line_ctl_port, line_controls | LC_BREAK);
00754   /* XXX */
00755   /* milli_delay(400); */                               /* ouch */
00756   printf("RS232 break\n");
00757   sys_outb(rs->line_ctl_port, line_controls);
00758   return 0;     /* dummy */
00759 }
00760 
00761 /*===========================================================================*
00762  *                              rs_close                                     *
00763  *===========================================================================*/
00764 PRIVATE int rs_close(tp, dummy)
00765 tty_t *tp;                      /* which tty */
00766 int dummy;
00767 {
00768 /* The line is closed; optionally hang up. */
00769   rs232_t *rs = tp->tty_priv;
00770   int r;
00771 
00772   if (tp->tty_termios.c_cflag & HUPCL) {
00773         sys_outb(rs->modem_ctl_port, MC_OUT2 | MC_RTS);
00774   }
00775   return 0;     /* dummy */
00776 }
00777 
00778 /* Low level (interrupt) routines. */
00779 
00780 #if (MACHINE == IBM_PC)
00781 /*===========================================================================*
00782  *                              rs232_handler                                *
00783  *===========================================================================*/
00784 PRIVATE void rs232_handler(rs)
00785 struct rs232 *rs;
00786 {
00787 /* Interrupt hander for RS232. */
00788 
00789   while (TRUE) {
00790         unsigned long v;
00791         /* Loop to pick up ALL pending interrupts for device.
00792          * This usually just wastes time unless the hardware has a buffer
00793          * (and then we have to worry about being stuck in the loop too long).
00794          * Unfortunately, some serial cards lock up without this.
00795          */
00796         sys_inb(rs->int_id_port, &v);
00797         switch (v) {
00798         case IS_RECEIVER_READY:
00799                 in_int(rs);
00800                 continue;
00801         case IS_TRANSMITTER_READY:
00802                 out_int(rs);
00803                 continue;
00804         case IS_MODEM_STATUS_CHANGE:
00805                 modem_int(rs);
00806                 continue;
00807         case IS_LINE_STATUS_CHANGE:
00808                 line_int(rs);
00809                 continue;
00810         }
00811         return;
00812   }
00813 }
00814 #endif /* MACHINE == IBM_PC */
00815 
00816 #if (MACHINE == ATARI)
00817 /*===========================================================================*
00818  *                              siaint                                       *
00819  *===========================================================================*/
00820 PRIVATE void siaint(type)
00821 int    type;           /* interrupt type */
00822 {
00823 /* siaint is the rs232 interrupt procedure for Atari ST's. For ST there are
00824  * as much as 5 interrupt lines used for rs232. The trap type byte left on the
00825  * stack by the assembler interrupt handler identifies the interrupt cause.
00826  */
00827 
00828   register unsigned char  code;
00829   register rs232_t *rs = &rs_lines[0];
00830   int s = lock();
00831 
00832   switch (type & 0x00FF)
00833   {
00834         case 0x00:             /* receive buffer full */
00835                 in_int(rs);
00836                 break;
00837         case 0x01:             /* receive error */
00838                 line_int(rs);
00839                 break;
00840         case 0x02:             /* transmit buffer empty */
00841                 out_int(rs);
00842                 break;
00843         case 0x03:             /* transmit error */
00844                 code = MFP->mf_tsr;
00845                 if (code & ~(T_ENA | T_UE | T_EMPTY))
00846                 {
00847                     printf("sia: transmit error: status=%x\r\n", code);
00848                     /* MFP->mf_udr = lastchar; */ /* retry */
00849                 }
00850                 break;
00851         case 0x04:              /* modem lines change */
00852                 modem_int(rs);
00853                 break;
00854   }
00855   restore(s);
00856 }
00857 #endif /* MACHINE == ATARI */
00858 
00859 /*===========================================================================*
00860  *                              in_int                                       *
00861  *===========================================================================*/
00862 PRIVATE void in_int(rs)
00863 register rs232_t *rs;           /* line with input interrupt */
00864 {
00865 /* Read the data which just arrived.
00866  * If it is the oxoff char, clear OSWREADY, else if OSWREADY was clear, set
00867  * it and restart output (any char does this, not just xon).
00868  * Put data in the buffer if room, otherwise discard it.
00869  * Set a flag for the clock interrupt handler to eventually notify TTY.
00870  */
00871 
00872   unsigned long c;
00873 
00874 #if (MACHINE == IBM_PC)
00875   sys_inb(rs->recv_port, &c);
00876 #else /* MACHINE == ATARI */
00877   c = MFP->mf_udr;
00878 #endif
00879 
00880   if (!(rs->ostate & ORAW)) {
00881         if (c == rs->oxoff) {
00882                 rs->ostate &= ~OSWREADY;
00883         } else
00884         if (!(rs->ostate & OSWREADY)) {
00885                 rs->ostate |= OSWREADY;
00886                 if (txready(rs)) out_int(rs);
00887         }
00888   }
00889 
00890   if (rs->icount == buflen(rs->ibuf))
00891   {
00892         printf("in_int: discarding byte\n");
00893         return; /* input buffer full, discard */
00894   }
00895 
00896   if (++rs->icount == RS_IHIGHWATER && rs->idevready) istop(rs);
00897   *rs->ihead = c;
00898   if (++rs->ihead == bufend(rs->ibuf)) rs->ihead = rs->ibuf;
00899   if (rs->icount == 1) {
00900         rs->tty->tty_events = 1;
00901         force_timeout();
00902   }
00903   else
00904         printf("in_int: icount = %d\n", rs->icount);
00905 }
00906 
00907 /*===========================================================================*
00908  *                              line_int                                     *
00909  *===========================================================================*/
00910 PRIVATE void line_int(rs)
00911 register rs232_t *rs;           /* line with line status interrupt */
00912 {
00913 /* Check for and record errors. */
00914 
00915  unsigned long s;
00916 #if (MACHINE == IBM_PC)
00917   sys_inb(rs->line_status_port, &s);
00918   rs->lstatus = s;
00919 #else /* MACHINE == ATARI */
00920   rs->lstatus = MFP->mf_rsr;
00921   MFP->mf_rsr &= R_ENA;
00922   rs->pad = MFP->mf_udr;        /* discard char in case of LS_OVERRUN_ERR */
00923 #endif /* MACHINE == ATARI */
00924   if (rs->lstatus & LS_FRAMING_ERR) ++rs->framing_errors;
00925   if (rs->lstatus & LS_OVERRUN_ERR) ++rs->overrun_errors;
00926   if (rs->lstatus & LS_PARITY_ERR) ++rs->parity_errors;
00927   if (rs->lstatus & LS_BREAK_INTERRUPT) ++rs->break_interrupts;
00928 }
00929 
00930 /*===========================================================================*
00931  *                              modem_int                                    *
00932  *===========================================================================*/
00933 PRIVATE void modem_int(rs)
00934 register rs232_t *rs;           /* line with modem interrupt */
00935 {
00936 /* Get possibly new device-ready status, and clear ODEVREADY if necessary.
00937  * If the device just became ready, restart output.
00938  */
00939 
00940 #if (MACHINE == ATARI)
00941   /* Set active edge interrupt so that next change causes a new interrupt */
00942   MFP->mf_aer = (MFP->mf_aer | (IO_SCTS|IO_SDCD)) ^
00943                  (MFP->mf_gpip & (IO_SCTS|IO_SDCD));
00944 #endif
00945 
00946   if (devhup(rs)) {
00947         rs->ostate |= ODEVHUP;
00948         rs->tty->tty_events = 1;
00949         force_timeout();
00950   }
00951 
00952   if (!devready(rs))
00953         rs->ostate &= ~ODEVREADY;
00954   else if (!(rs->ostate & ODEVREADY)) {
00955         rs->ostate |= ODEVREADY;
00956         if (txready(rs)) out_int(rs);
00957   }
00958 }
00959 
00960 /*===========================================================================*
00961  *                              out_int                                     *
00962  *===========================================================================*/
00963 PRIVATE void out_int(rs)
00964 register rs232_t *rs;           /* line with output interrupt */
00965 {
00966 /* If there is output to do and everything is ready, do it (local device is
00967  * known ready).
00968  * Notify TTY when the buffer goes empty.
00969  */
00970 
00971   if (rs->ostate >= (ODEVREADY | OQUEUED | OSWREADY)) {
00972         /* Bit test allows ORAW and requires the others. */
00973 #if (MACHINE == IBM_PC)
00974         sys_outb(rs->xmit_port, *rs->otail);
00975 #else /* MACHINE == ATARI */
00976         MFP->mf_udr = *rs->otail;
00977 #endif
00978         if (++rs->otail == bufend(rs->obuf)) rs->otail = rs->obuf;
00979         if (--rs->ocount == 0) {
00980                 rs->ostate ^= (ODONE | OQUEUED);  /* ODONE on, OQUEUED off */
00981                 rs->tty->tty_events = 1;
00982                 force_timeout();
00983         } else
00984         if (rs->ocount == RS_OLOWWATER) {       /* running low? */
00985                 rs->tty->tty_events = 1;
00986                 force_timeout();
00987         }
00988   }
00989 }
00990 #endif /* NR_RS_LINES > 0 */
00991 

Generated on Fri Apr 14 22:57:19 2006 for minix by  doxygen 1.4.6