home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS 1992 December / simtel1292_SIMTEL_1292_Walnut_Creek.iso / msdos / ddjmag / ddj8910.arc / SMITH2.ARC / COMMINT.C next >
Text File  |  1989-04-20  |  13KB  |  330 lines

  1. /* COMMINT.C:  Interrupt driven comm port handler, written 100% in C.
  2.  *   Contains:  All PC Hardware specific routines, all routines that
  3.  *     access the infifo data structure directly.
  4.  *   Goal:   Provide high speed, publishable comm routines for state
  5.  *     machine driven Comm protocol series of articles.  Should be usable
  6.  *     on either com1 or com2, but not both simultaneously.
  7.  *   Warning:  This code deals with PC hardware directly, and should
  8.  *     only be used on 100% hardware compatibles.  Also, if your program
  9.  *     aborts abnormally without removing the IH, I'm not responsible.
  10.  * Externally callable routines include:
  11.  *   int Inst_IH( void interrupt (*faddr)(), int comnum) - Installs IH.
  12.  *   int Remove_IH()       - Removes previously installed Int. handler
  13.  *   void Config_Comm( int port, S_INIT conf )   - Configure comm port
  14.  *   int incomm()   - tests if a char. is available in the comm buffer
  15.  *   unsigned int getcomch()   - returns oldest char. from comm buffer
  16.  *   int writecomm(unsigned char *buf, int len) - send chars via UART.
  17.  */
  18.  
  19. #include <dos.h>
  20. #include "commint.h"        /* Hardware specific (low level) defines */
  21. #include "commn.h"          /* common to commint and cterm files */
  22.  
  23. /* -------  Storage area to be able to put things back when we leave */
  24. static void interrupt (*oldvect)();
  25. static unsigned char  oldimr;          /* 8259 interrupt mask reg.  */
  26. static unsigned char  old_Int_Enable;  /* 8250 interrupt enable reg */
  27. static S_INIT         old_bps_lctrl;   /* Saved speed, par, etc.    */
  28.  
  29. /* ------- Globally visible variables and tables ----------- */
  30. int  CurPortAddr;         /* Save the currently active comm port here */
  31. unsigned char RxErr = 0;  /* Note flags of comm read error */
  32. unsigned int  RxErrLoc;   /* Save location of the first error */
  33.  
  34. /* ------- Preloaded arrays for Comm port configuration speed variables */
  35. unsigned divisors[] = { 0x900, 0x600, 0x417, 0x359, 0x300, 0x180, 0x0C0, 0x060,
  36.      0x040, 0x03A, 0x030, 0x020, 0x018, 0x010, 0x00C, 0x006,
  37.      0x004, 0x003, 0x002, 0 };
  38. unsigned speedi[] = {      50 ,   75 ,  110 ,  135,   150 ,  300 ,  600 , 1200,
  39.      1800 , 2000 , 2400 , 3600 , 4800 , 7200 , 9600 ,19200,
  40.      28800, 38400, 57600, 0 };
  41.  
  42. /* ------- The input fifo (initialized) -------- */
  43. static FIFO infifo = { 0,             /* empty */
  44.                 &infifo.data[0],      /* init to start */
  45.                 &infifo.data[0] };
  46.  
  47. static int inum = 0;        /* used to tell if IH already installed */
  48.  
  49. /* External variables affected by this module */
  50. extern S_INIT cur_config;           /* from module CTERMx.C */
  51.  
  52. /* ---------- RXRDY interrupt handler -----------------------
  53.  * Does:  Inputs serial data into receive FIFO.  Overwrites if need be.
  54.  * Pass:  Nothing.
  55.  * Sets:  Global flag word as follows:  Bits 0 :3   Line Status bits.
  56.  *      Bit 0: FIFO oflow, 1: UART Orun, 2: Parity Err, 3: Framing Err
  57.  *                                      Bits 4 :15  buf loc. of error.
  58.  *  NOTE:  Only sets location area upon first error.  User should clear.
  59.  */
  60. void interrupt rxrdy_IH()
  61. {
  62.   unsigned char lstat;         /* Used to save Line status locally */
  63.  
  64.   lstat = inportb( CurPortAddr + LineStatus );
  65.  
  66.   /* Insure there is data to receive.. */
  67.   if ( (lstat & DataRdyBit) != 0 ) {              /* Bit zero on = data. */
  68.     *(infifo.hdr.inptr++) = inportb(CurPortAddr); /* read it and bump ptr. */
  69.     if (infifo.hdr.inptr == &infifo.data[FIFOSIZE])
  70.       infifo.hdr.inptr = &infifo.data[0];         /* wrap the circular buffer */
  71.     if ( ++infifo.hdr.count > FIFOSIZE ) {        /* If data overwritten. */
  72.       /* Note that LSB is already set (DataRdyBit) and means buf overwrite */
  73.       if (RxErr == 0) {                           /* No Previous error? */
  74.         RxErr = lstat;
  75.         RxErrLoc = infifo.hdr.inptr - &infifo.data[0];
  76.       }
  77.       else
  78.         RxErr |= lstat;                           /* OR Rcv errors as we go */
  79.     }
  80.     else
  81.       if ( (lstat &= 0x0E) != 0 )                 /* Other Rcv errors? */
  82.         RxErr |= lstat;                           /* OR errors as we go */
  83.   }
  84.   outportb(Int_Cmd_Reg, End_Int_Cmd);             /* Tell 8259 we're done */
  85. }
  86.  
  87.  
  88. /*  **************  High level interface to hardware ********** */
  89.  
  90. /* ---------- Get_Conf ----------------------------------
  91.  * Does: Reads the  speed, parity, stops of async UART.
  92.  *       Our interrupt handler need not be installed to do so.
  93.  * Pass: The comm port # (1 or 2) of interest.
  94.  * Returns: A struct containing S_INIT config info.
  95.  */
  96. S_INIT Get_Conf( int port )
  97. {
  98.   S_INIT conf;                   /* to be returned after filling in */
  99.   unsigned int divword;          /* two halves of baud rate divisor */
  100.   int portaddr = (port == 1) ? COM1 : COM2;
  101.   unsigned char line_ctrl, temp;
  102.   int i;
  103.  
  104.   line_ctrl = inportb(portaddr + LineControl);    /* Read current value */
  105.   /* Enable access to baud rate divisor regs */
  106.   outportb( (portaddr + LineControl), (line_ctrl | DivBit) );
  107.   divword  = inportb(portaddr + DLL);    /* Try the LSByte first */
  108.   temp     = inportb(portaddr + 1);
  109.   divword += (unsigned int) ( temp << 8 );
  110.   for ( i = 0; divisors[i] != 0; i++)   /* look up divisor inlist */
  111.     if (divword == divisors[i])
  112.       break;
  113.   conf.speed = speedi[i];
  114.  
  115.   outportb( (portaddr + LineControl), line_ctrl );  /* See Data regs again */
  116.  
  117.   conf.ubits.lctrl = line_ctrl;
  118.   return(conf);
  119. }
  120.  
  121. /* ---------- Config_Comm ----------------------------------
  122.  * Does: Configures speed, parity, stops of async port.
  123.  *       Our interrupt handler need not be installed to do so.
  124.  * Pass: The comm port # (1 or 2) of interest.
  125.  *       A struct containing the desired config info.
  126.  * Returns: NOTHING.
  127.  * Use:  Should be used once during init and every time config changes.
  128.  * NOTE: Sets global cur_config to new value.
  129.  */
  130. void Config_Comm( int port, S_INIT conf )
  131. {
  132.   unsigned char divhi, divlo;    /* two halves of baud rate divisor */
  133.   int portaddr = (port == 1) ? COM1 : COM2;
  134.   int i;
  135.  
  136.   for ( i = 0; speedi[i] != 0; i++)   /* look up the chosen speed */
  137.     if (conf.speed == speedi[i])
  138.       break;
  139.  
  140.   divhi = (unsigned char) (divisors[i] >> 8);
  141.   divlo = (unsigned char) (divisors[i] & 0x00FF);
  142.  
  143.   outportb( (portaddr + LineControl), 0x80);   /* To config baud divisors */
  144.   outportb( (portaddr + DLM), divhi);
  145.   outportb( (portaddr + DLL), divlo);
  146.  
  147.   outportb( (portaddr + LineControl), conf.ubits.lctrl );
  148.   cur_config = conf;
  149.   delay(100);
  150. }
  151.  
  152. /* ---------- xmit_break  ----------------------------------
  153.  * Does: Sends a 12 char time break signal to current comm port via UART.
  154.  *       Our interrupt handler need not be installed to do so.
  155.  * Returns:  nothing of value.
  156.  */
  157. int xmit_break( )
  158. {
  159.   unsigned char line_ctrl;
  160.   long break_bits = ( 12 * 10 * 1000L);   /* Bits per 12 char * 1000 */
  161.   int msecs;                              /* Number of msecs to send break */
  162.  
  163.   msecs = (int)( break_bits / (long)cur_config.speed );
  164.  
  165.   line_ctrl = inportb(CurPortAddr + LineControl);    /* Read current value */
  166.   outportb( (CurPortAddr + LineControl), (line_ctrl | BrkBit) );
  167.   delay( msecs + 10 );         /* Add some for the timer interrupt */
  168.   outportb( (CurPortAddr + LineControl), line_ctrl );
  169.   return(0);
  170. }
  171.  
  172.  
  173. /* ---------- Install a comm interrupt handler ---------
  174.  * Does:  Installs the rxrdy comm interrupt handler requested
  175.  * Pass:  The address of the interrupt function.
  176.  *        Which comm port (1 or 2) you would like installed.
  177.  * Uses:  Global variable oldvect to save previous owner
  178.  *        Global CurPortAddr so that interrupt handler can use it.
  179.  * Returns: 0 if alls well, or interrupt # already installed.
  180.  */
  181.  
  182. int Inst_IH( void interrupt (*faddr)(), int comnum)
  183. {
  184.   unsigned char temp;
  185.   int i;
  186.   unsigned long myvect;
  187.  
  188.   if (inum != 0)      /* Do NOT install over ourselves */
  189.     return(inum);
  190.  
  191.   /* Both the below set globals for later use to disable */
  192.   inum        = (comnum == 1) ? 0x0C : 0x0B;
  193.   CurPortAddr = (comnum == 1) ? COM1 : COM2;
  194.  
  195.   disable();                        /* Disable interrupts to change vectors */
  196.   oldvect = getvect(inum);          /* Save old vector to clean up later */
  197.   old_Int_Enable = inportb(CurPortAddr + Int_Enable_Reg);  /* Save this too */
  198.  
  199.   /* Clear the 8250 UART of any garbage by reading all registers */
  200.   for (i = 0; i < 6; i++)
  201.     temp = inportb(CurPortAddr + i);
  202.  
  203.   old_bps_lctrl = Get_Conf(comnum);       /* Save UART speed, etc. */
  204.  
  205.   /* Read the 8259 int mask reg and set IRQ3 or 4 low to enable interrupts.
  206.    * Write result back to 8259 when finished */
  207.   oldimr = temp = inportb(Int_Mask_Reg);
  208.   temp &= ( (CurPortAddr == COM1) ? 0xEF : 0xF7);
  209.   outportb(Int_Mask_Reg, temp);
  210.  
  211.   /* Set Out2, DTR and RTS high for the 8250 */
  212.   temp = (Out2Bit + DTRBit + RTSBit);
  213.   outportb( (CurPortAddr + ModemControl), temp);
  214.  
  215.   /* Set only rxrdy interrupt bit on (bit 0) */
  216.   outportb( (CurPortAddr + Int_Enable_Reg), 0x01 );
  217.  
  218.   /* Finally set up the vector and wait and enable interupts */
  219.   setvect(inum, faddr);
  220.   enable();
  221.   return(0);
  222. }
  223.  
  224. /* ---------- Remove Interrupt Handler. ------------------
  225.  * Does:  Removes an interrupt handler installed by Inst_IH.
  226.  *        Re-installs the saved 8259 and 8250 interrupt control regs.
  227.  * Uses:  Global variables oldvect, CurPortAddr, oldimr and old_Int_Enable.
  228.  * Sets:  Global inum to 0 if successfully removed.
  229.  * Returns: 0 if alls well, or interrupt # already installed.
  230.  */
  231. int Remove_IH()
  232. {
  233.   if (inum == 0)
  234.     return(inum);
  235.  
  236.   disable();                 /* Turn interrupts off to change vectors */
  237.  
  238.   Config_Comm( (inum == 0x0C ? 1 : 2), old_bps_lctrl );  /* restore speed.. */
  239.  
  240.   /* Restore 8259 and 8250 interrupt registers as they were */
  241.   outportb(Int_Mask_Reg, oldimr);
  242.   outportb( (CurPortAddr + Int_Enable_Reg), old_Int_Enable);
  243.  
  244.   setvect(inum, oldvect);    /* Put the other guys interrupt vector back */
  245.  
  246.   inum = 0;                  /* reset to enable re-install */
  247.   enable();
  248.   return(0);
  249. }
  250.  
  251. /* ---------- writecomm -------------------------
  252.  * Does: Writes a list of characters to the UART directly.
  253.  *       Tests UART xmit status.  If full, waits until empty.
  254.  *       If full for too long, returns error value.
  255.  * Pass: unsigned char pointer to start of buffer to send.
  256.  *       length of buffer to send.
  257.  * Returns: 1 if write timeout or error, else returns 0.
  258.  *  NOTE:  Not interrupt driven at this time.
  259.  */
  260. writecomm(unsigned char *buf, int len)
  261. {
  262.   int i = len;
  263.   int ctimes = 0;
  264.   unsigned char *cptr = buf;
  265.  
  266.   while (i > 0) {
  267.     if ( inportb(CurPortAddr + LineStatus) & TxRdyBit ) {
  268.       outportb( CurPortAddr, *(cptr++) );
  269.       i--;
  270.       ctimes = 0;
  271.     }
  272.     else {
  273.       if (++ctimes > 1000)  /* give it a good second to clear */
  274.         return(1);
  275.       delay(1);    /* should take 83 ms per ch at 1200, 333 at 300 */
  276.     }
  277.   }
  278.   return(0);
  279. }
  280.  
  281. /* ********* Routines that access the infifo data structure ********** */
  282.  
  283. /* ---------- incomm ----------------------------------
  284.  * Does: Tests if a character is ready in the comm buffer.
  285.  * Returns: The number of characters stored in the input fifo.
  286.  * Use:  Similar to keyboard inkey() call.
  287.  */
  288. int incomm()
  289. {
  290.    return(infifo.hdr.count);
  291. }
  292.  
  293. /* ---------- getcomch  ----------------------------------
  294.  * Does: Returns the oldest character from the comm buffer.
  295.  *  Similar to keyboard getch() call.
  296.  * WARNING:  Don't call unless incomm() says it's O.K.
  297.  * New 12/23: Now returns unsigned int.  If RxErr is set, encodes
  298.  *     error information in the high byte from RxErr and zeroes RxErr.
  299.  */
  300. unsigned char getcomch()
  301. {
  302.   unsigned char outch;
  303.  
  304.   outch = *(infifo.hdr.outptr++);
  305.  
  306. /*   if (RxErr != 0)
  307.  *     Pass up error and reset RxErr
  308.  */
  309.  
  310.   if (infifo.hdr.outptr == &infifo.data[FIFOSIZE])  /* wrap time? */
  311.     infifo.hdr.outptr = &infifo.data[0];
  312.   infifo.hdr.count--;
  313.   return(outch);
  314. }
  315.  
  316. /* ---------- eat_noise ------------------------
  317.  * Does:  Digests any junk that has arrived in the buffer before it
  318.  *        is allowed to be interpreted by zeroing fifo counters and ptrs.
  319.  * Use:   Before initial NAK and just after send completes.
  320.  * Returns:  nothing
  321.  * NOTE:  Belongs in this module since this is the only place the infifo
  322.  *        structure is visible.
  323.  */
  324. void eat_noise()
  325. {
  326.   infifo.hdr.count = 0;
  327.   infifo.hdr.inptr = &infifo.data[0];
  328.   infifo.hdr.outptr = infifo.hdr.inptr;
  329. }
  330.