The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/drivers/tty/rs232.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: f55072f196e4104f8958a23745edac5b


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.