Skip to content
Advertisement

Serial communication on Linux with flow control enabled – bad behaviour

I wrote common functions in order to manage serial ports, based on the following structure:

  typedef struct
  {
     int  PHandle;
     unsigned int  Port;
     unsigned int  BaudRate;
     unsigned char Parity;
     unsigned char FlowControl;
     char Device[MAX_SIZE];
  } Tst_SPort;

I am calling these functions (see below) in another file in order to test an RS232 serial port. The flow control needs to be enabled.

  int iInit(Tst_SPort *port, const char *device, int baudRate, unsigned char parity, unsigned char flowControl)
  {
     strncpy(port->Device, device, MAX_SIZE);
     port->PHandle     = iOpen(port);
     port->Port        = -1;
     port->BaudRate    = baudRate;
     port->Parity      = parity;
     port->FlowControl = flowControl;
     if(port->PHandle > 0)
     {
        setuart(port, port->BaudRate);
     }
     return port->PHandle;
  }

  int iOpen(Tst_SPort *port)
  {
     port->PHandle = open(port->Device, O_RDWR | O_NOCTTY);
     if(port->PHandle < 0)
     {
        perror("open:");
        return (-1);
     }
     return (port->PHandle);
  }

  void setuart(Tst_SPort *port, int baudRate)
  {
     struct termios opt, optCmp;           
     struct serial_struct info;

     if(port->PHandle > 0)
     {
        bzero(&opt,    sizeof(opt));
        bzero(&optCmp, sizeof(optCmp));

        if(ioctl(port->PHandle, TIOCGSERIAL, &info) == 0) 
           port->Port = info.port;

        fcntl(port->PHandle, F_SETFL, O_NONBLOCK);

        if (tcgetattr(port->PHandle, &opt) < 0) 
           perror("tcgetattr Get:");

        if(baudRate > 0)
        {
           cfsetospeed (&opt, baudRate);
           cfsetispeed (&opt, baudRate);   
        }

        opt.c_iflag = IGNPAR;               
        opt.c_oflag &= ~OPOST
        opt.c_oflag &= ~ONLCR;
        opt.c_lflag = 0;                   
        opt.c_cflag |= (CLOCAL | CREAD); 
        opt.c_cflag &= ~(PARENB | PARODD); 
        opt.c_cflag |= port->Parity;
        opt.c_cflag &= ~CSTOPB;         
        opt.c_cflag &= ~CSIZE;
        opt.c_cflag |=  CS8;        
        if(!port->FlowControl)
           opt.c_cflag &= ~CRTSCTS;         
        else
           opt.c_cflag |= CRTSCTS;         

        opt.c_cc[VMIN] = 0;               
        opt.c_cc[VTIME] = 50; 

        if(tcsetattr(opt->PHandle, TCSANOW, &opt) < 0)
           perror("tcgetattr Update :");

        if (tcgetattr(opt->PHandle, &optCmp) < 0) 
           perror("tcgetattr Read:");

        /* Compare */
        if (memcmp((void *)&opt, (void *)&optCmp, sizeof(opt)) != 0)
           printf("Conf failed");         

        tcflush(port->PHandle, TCIFLUSH);
     }
  }

  int iRead(Tst_SPort *port, char *buffer, unsigned long buffLength)
  {
     struct  timeval tv;   
     fd_set  recv;
     int     s32Read = 0;
     int     s32Offset = 0;
     int     s32SRes = 0;

     tv.tv_sec = 0;
     tv.tv_usec = 100000; /* 100ms */

     if ((port) && (port->PHandle > 0)) 
     {
        while (s32Offset < buffLength)
        {
           FD_ZERO(&recv);
           FD_SET(port->PHandle, &recv);

           s32SRes = select(port->PHandle + 1, &recv, NULL, NULL, &tv);
           if ((s32SRes == -1) && (errno == EINTR)) 
           {
              continue; 
           }
           else if(s32SRes > 0)
           {
              if (FD_ISSET(port->PHandle, &recv))
              {
                 s32Read = read(port->PHandle, buffer + s32Offset, buffLength - s32Offset);
                 if(s32Read  > 0)
                 {
                    tv.tv_sec = 0;
                    tv.tv_usec = 5000;
                    s32Offset += s32Read;
                    continue;
                 }
              }
           }
           break;
        }
     }  
     return s32Offset;
  }

  int iClose(Tst_SPort *port)
  {
     return (close(port->Phandle));
  }

In order to validate the implementation, the pinouts Tx and Rx have been connected together, idem for CTS and RTS. Everything works fine, the message sent can be read correctly. In addition, when Tx is disconnected from the Rx nothing is reading as expected.

But when the CTS is unplugged from the RTS the test blocks after the port closing step (~20 seconds).

However, if the function setuart() is called with flowControl == 0, the test does not block and returns the exepected error code without delay.

I probably understood something wrongly especially in port configuration. Is it the good way to do ?

Advertisement

Answer

The problem you are facing is a correct behaviour.

Leaving CTS unconnected with flow control enabled, means DTE (AKA PC) cannot send data to DCE (slave device).

When you try to write to the UART output buffer it, probably, is full and application temporarily stops running and waits until some buffer space becomes available.

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement