troubleshooting Question

Hardware Flow Control over RS232 (Serial) in DOS

Avatar of john_ws
john_ws asked on
C
12 Comments1 Solution3955 ViewsLast Modified:
I know the rules state that I'm not allowed to let you answer this question for me, since it's for school. But directing me in the right way would be more than helpful.

Basically I have a lab from last semester that is basic communication over serial using a defeated Null Modem Cable, but now we are given a full handshake cable (with CTS and RTS lines, etc) and must implement hardware flow control (and modify the old lab to allow this). What I know is that we must use CTS (Clear to Send), RTS (Request to Send), MCR (Modem Control Register), and MSR (Modem Status Register) to accomplish this.

It is surprisingly difficult to find examples in C (especially for DOS) of how exactly to do this. We were never actually taught this and it was one of those "research this on your own time" type of things.

If it helps, here is the code from last semester, just to give you an idea of what functions and includes we are using (conio.h, outportb(), inportb(), etc). WARNING: LONG CODE UP AHEAD!

====================================================

 /* Included Libraries */

#include <conio.h>
#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/* Key Defines (ESC, F1, F2, F3, F4, F5) */

#define KEY_ESC       27
#define KEY_F1             59
#define KEY_F2             60
#define KEY_F3             61
#define KEY_F4             62
#define KEY_F5             63
#define KEY_F6            64
#define KEY_F7            65

/* Defines */

// COM1 0x3F8
// COM2 0x2F8
// COM3 0x3E8
// COM4 0x2E8

#define WALK_SIZE       80    /* Length of walking string   */
#define MAX_STRING      32    /* Max lengt of input strings */
#define BUFFER_SIZE       1024  /* Max buffer size for rx in  */

#define S_PORT             0x3f8 /* Serial port  */
#define INTMASK       0x21  /* Interrupt Mask */
#define INTVECT            0x0C  /* Interrupt Vector */

#define IER             ( S_PORT + 1 ) /* Interrupt Enable Register */
#define IIR             ( S_PORT + 2 ) /* Interrupt ID Register     */
#define LCR             ( S_PORT + 3 ) /* Line Control Register     */
#define MCR             ( S_PORT + 4 ) /* Modem Control Register    */
#define LSR             ( S_PORT + 5 ) /* Line Status Register      */
#define MSR             ( S_PORT + 6 ) /* Modem Status Register     */
#define DLAB_LOW       ( S_PORT + 0 ) /* DLAB Low Bit              */
#define DLAB_HIGH       ( S_PORT + 1 ) /* DLAB High Bit             */
#define DLAB             0x80  /* Divisor Latch Access Bit */

#define SBITS1             0x00  /* Stop Bits (1)       */
#define SBITS2             0x04  /* Stop Bits (2)       */

#define NBITS7             0x02  /* Data Bits (7)       */
#define NBITS8             0x03  /* Data Bits (8)       */

#define EVENPARITY       0x18  /* Parity Bits (EVEN)  */
#define NOPARITY       0x00  /* Parity Bits (NONE)  */
#define ODDPARITY       0x08  /* Parity Bits (ODD)   */
#define SPACEPARITY       0x38  /* Parity Bits (SPACE) */
#define MARKPARITY       0x28  /* Parity Bits (MARK)  */

#define BR37      0xC00       /* Baud Rate (37)      */
#define BR75      0x600       /* Baud Rate (75)      */
#define BR150     0x300       /* Baud Rate (150)     */
#define BR300         0x180       /* Baud Rate (300)     */
#define BR600         0xC0        /* Baud Rate (600)     */
#define BR1200         0x60        /* Baud Rate (1200)    */
#define BR2400         0x30        /* Baud Rate (2400)    */
#define BR4800         0x18        /* Baud Rate (4800)    */
#define BR9600         0x0C        /* Baud Rate (9600)    */
#define BR19200   0x06        /* Baud Rate (19200)   */
#define BR38400   0x03        /* Baud Rate (38400)   */
#define BR57600   0x02        /* Baud Rate (57600)   */
#define BR115200  0x01        /* Baud Rate (115200)  */

/* Start of Global Variables */

int             bufferpos,      /* Keep track of current buffer position. */
            currentbuf;

char             receivebuffer   [ BUFFER_SIZE ], /* Receive Buffer.        */
            argument       [ MAX_STRING ],       /* Command line arguments */
            setting        [ MAX_STRING ],  /* Left side of a=b arg.  */
            value              [ MAX_STRING ];  /* Right side of a=b arg. */

unsigned long int baudrate       = BR2400,       /* Default Baud value     */
              numbits        = NBITS8,       /* Default Data bit value */
              numstopbits       = SBITS1,        /* Default Stop bit value */
              parity      = ODDPARITY;       /* Default Parity value   */

/* Function Prototypes */

void interrupt rxchar ( void );
void interrupt ( * old_int ) ( );

void parse_string ( void );
void parse_params ( void );
void walk_pattern ( void );

/* Our Main */
int main ( int argc, char *argv[] )
{
      int i;
      char chartoprint, txchar;

      printf ( "\n" );

      /* If there is at least one arguments from the command line, *
       * and no more than 4, then let's parse them.                */
      if ( argc >= 2 && argc <= 5 )
      {
            for ( i=1; i < argc; i ++ )
            {
                  /* Copy each arg to "argument", and then first     *
                   * parse the string to get the setting and value.  *
                   * Then pass both to parse_params which takes care *
                   * of all the input parameters.                       */
                  strcpy ( argument, argv[i] );
                  parse_string ( );
                  parse_params ( );
            }
      }

      /* Otherwise, there are no parameters, or too many. So just use     *
       * default values.                                        */
      else
      {
            printf ( "\n" );
            printf ( "No parameters were entered.\n" );
            printf ( "Using default.\n\n" );
      }

      /* Print of the Hex values of all the Serial Port values */
      printf ( "Baud Rate:      0x%x\n", baudrate );
      printf ( "Parity:         0x%x\n", parity );
      printf ( "Stop Bits:      0x%x\n", numstopbits );
      printf ( "Number of Bits: 0x%x\n\n", numbits );

      disable ( ); /* Disable interrupts while we prepare the serial port. */

      /* Preparing the serial ports */
      outportb ( IER, 0x00 );

      old_int = getvect ( INTVECT );
        setvect ( INTVECT, rxchar );

      outportb ( LCR, DLAB );
      outportb ( DLAB_LOW, baudrate );
      outportb ( DLAB_HIGH, 0x00 );
      outportb ( LCR, numbits | parity | numstopbits );
      outportb ( IIR, 0xC7 );
      outportb ( MCR, 0x0B );
      outportb ( INTMASK, ( inportb ( INTMASK ) ) &0xEF );
      outportb ( IER, 0x01 );

      enable ( ); /* Renabling interrupts since we're done preparing */

      do
      {
            //rxchar (); /* Polling the serial ports for new characters */


            delay ( 10 );

            /* If the buffer position that has been read, does not    *
             * equal the current buffer position (of the available    *
             * read in buffer), then we have to read it in.           */
            if ( bufferpos != currentbuf )
            {
                  /* Get the character to print from the buffer */
                  chartoprint = receivebuffer [ currentbuf ];

                  /* Increase the buffer position */
                  currentbuf++;

                  /* If the position is over the buffer size, wrap around */
                  if ( currentbuf >= BUFFER_SIZE ) currentbuf = 0;

                  /* If the host has sent the F5 key, they are requesting *
                   * to clear our screen. So let's clear it.              */
                  if ( chartoprint == KEY_F5 ) system ( "cls" );

                  else printf ( "%c", chartoprint );

            }

            /* If there is a character being pressed on the keyboard .. */
            if ( kbhit ( ) )
            {
                  txchar = getch ( );

                  switch ( txchar )
                  {
                        case KEY_F1: walk_pattern (); break;
                        case KEY_F2: disable (); break;
                        case KEY_F3: enable (); break;
                        case KEY_F4: system ("cls"); break;
                        case KEY_ESC: break;

                        default: outportb(S_PORT, txchar);
                  }
            }
      } while ( txchar != KEY_ESC );

      outportb ( IER, 0 );
      outportb ( INTMASK, ( inportb ( INTMASK ) | 0x10 ) );
      setvect ( INTVECT, old_int );

      return 0;
}


/* Send 80 characters of our walking pattern. */
void walk_pattern ( )
{
      int thischar = 'A', i;

      for ( i = 0; i < WALK_SIZE; i ++ )
      {
            outportb ( S_PORT, thischar );
            thischar ++;
      }

      return;
}


/* Parse the args that were passed in from the command line
 * that were in the format setting=value. */
void parse_string ( )
{
      int counter1 = 0, counter2 = 0;

      while ( ( argument[counter1] != '=' ) && ( argument[counter1] != '\0' ) )
      {
            setting [counter1] = argument [counter1];
            counter1++;
      }

      setting [counter1] = '\0';
      counter1 ++;

      while ( argument [counter1] != '\0' )
      {
            value [counter2] = argument [counter1];
            counter1 ++;
            counter2 ++;
      }

      value [counter2] = '\0';
}


/* After we have parsed the paramters from the command line, we can
 * now get these values and place them in our serial port parameter
 * variables */
void parse_params ()
{
      long int baud_in,
             stop_in,
             parity_in,
             bytes_in;

      if ( !strcmp ( setting, "baud" ) )
      {
            baud_in = atoi(value);

            if ( baud_in == 37 ) baudrate = BR37;
            else if ( baud_in == 75    ) baudrate = BR75;
            else if ( baud_in == 150   ) baudrate = BR150;
            else if ( baud_in == 300   ) baudrate = BR300;
            else if ( baud_in == 600   ) baudrate = BR600;
            else if ( baud_in == 1200  ) baudrate = BR1200;
            else if ( baud_in == 2400  ) baudrate = BR2400;
            else if ( baud_in == 4800  ) baudrate = BR4800;
            else if ( baud_in == 9600  ) baudrate = BR9600;
            else if ( baud_in == 19200 ) baudrate = BR19200;
            else if ( baud_in == 38400 ) baudrate = BR38400;
            else baudrate = BR2400;
      }

      if ( !strcmp ( setting, "stop") )
      {
            stop_in = atoi ( value );
            if ( stop_in == 2 ) numstopbits = SBITS2;
            else numstopbits = SBITS1;
      }

      if ( !strcmp (setting, "bits") )
      {
            bytes_in = atoi ( value );
            if ( bytes_in == 7 ) numbits = NBITS7;
            else numbits = NBITS8;

      }
      if ( !strcmp(setting, "parity" ) )
      {
            if ( !strcmp ( value, "even" ) ) parity = EVENPARITY;
            else if ( !strcmp ( value, "none" ) ) parity = NOPARITY;
            else if ( !strcmp ( value, "space" ) ) parity = SPACEPARITY;
            else if ( !strcmp ( value, "mark"  ) ) parity = MARKPARITY;
            else parity = ODDPARITY;
      }
}

/* Polling the serial port for new available data */
void interrupt rxchar()
{
      int checkport = inportb ( LSR );

      while ( checkport & 1 )
      {
            if ( bufferpos >= BUFFER_SIZE ) bufferpos = 0;
            receivebuffer [bufferpos] = inportb ( S_PORT );
            bufferpos ++;
            outportb ( 0x20 , 0x20 );
            checkport = inportb ( LSR );
      }
}

====================================================


I know the parsing and stuff is pretty ugly (using global variables and all). This was actually modified from basic template we were given, so don't blame that on me :P

Basically all this does is allows you to enter certain baudrates and parity bits etc, if you want to. If you don't, it will use a default value. Then it will communicate with the other machine over COM1 using the Null Modem cable.

What I'm asking is what type of code would I have to add to implement hardware flow control? I am terrible with this communication stuff, so any examples or guides you can direct me to would be great.

Thank you.
Join the community to see this answer!
Join our exclusive community to see this answer & millions of others.
Unlock 1 Answer and 12 Comments.
Join the Community
Learn from the best

Network and collaborate with thousands of CTOs, CISOs, and IT Pros rooting for you and your success.

Andrew Hancock - VMware vExpert
See if this solution works for you by signing up for a 7 day free trial.
Unlock 1 Answer and 12 Comments.
Try for 7 days

”The time we save is the biggest benefit of E-E to our team. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange.

-Mike Kapnisakis, Warner Bros