Uart Hardware flow control in Dragonboard410c Debian 283

Hello,

I am using the Debain 283 and I am trying to test the RTS/CTS hardware flow control on both uart0 (/dev/ttyMSM1) and uart1 (/dev/ttyMSM0).

In order to enable the RTS/CTS pins for the uart1 I changed the uart1 pins in

arch/arm64/boot/dts/msm8916-pins.dtsi

as follows:

blsp1_uart2_default: blsp1_uart2_default {
	pinmux {
		function = "blsp_uart2";
		pins = "gpio4", "gpio5", "gpio6", "gpio7";
	};
	pinconf {
		pins = "gpio4", "gpio5", "gpio6", "gpio7";
		drive-strength = <16>;
		bias-disable;
	};
};

So I am testing two codes.
The first code I am testing is this one:

#define _XOPEN_SOURCE_EXTENDED

#ifndef _POSIX_SOURCE
#define _POSIX_SOURCE
#endif
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif

#define _BSD_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <termios.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdint.h>

int opentty(const char *ttyName)
{
  printf("In function %s\n", __FUNCTION__);
  /* open file & return file descriptor */
  return open(ttyName, O_RDWR | O_NOCTTY | O_NDELAY);
}

int ttyinit(int *ttyfd, struct termios *t_old, uint32_t baud)
{
  /* struct termios needed to handle the new serial port configuration */
  struct termios t_new;
  int8_t ret;

  printf("In function %s\n", __FUNCTION__);

  /* get the serial port attributes */
  ret = tcgetattr(*ttyfd, t_old);

  /* if fails, exit */
  if(ret == -1)
  {
    printf("tcgetattr failed\n");
    close(*ttyfd); 
    *ttyfd = -1;

    return -1;
  }

  /* copy the new one from the old one */
  t_new = *t_old;

    switch(baud)
    {
        case 38400:
           /* printf("38400...\n"); */
           t_new.c_cflag = B38400;
           break;
        case 9600:
           /* printf("9600...\n"); */
           t_new.c_cflag = B9600;
           break;
        case 19200:
           /* printf("19200...\n"); */
           t_new.c_cflag = B19200;
           break;
        case 115200:
           /* printf("115200...\n"); */
           t_new.c_cflag = B115200;
           break;
        case 57600:
           /* printf("57600...\n"); */
           t_new.c_cflag = B57600;
           break;      
    }

    t_new.c_cflag |= PARODD | CS8 | CREAD | CLOCAL;
    t_new.c_oflag = t_new.c_iflag = t_new.c_lflag = 0;
    t_new.c_cflag |= CRTSCTS;


    /* apply the given changes to the serial port */
    ret = tcsetattr(*ttyfd, TCSANOW, &t_new);

    /* if fails, exit */
    if(ret == -1)
    {
      printf("tcsetattr failed\n");
      /* restore old attributes and clos fd */
      tcsetattr(*ttyfd, TCSANOW, t_old);
      close(*ttyfd);
      *ttyfd = -1;
    }

  return (ret == 0)? 1 : -1; 
}

int main(int argc, char *argv[])
{
    struct termios t_old = {0};
    int fd = -1;
    
    int32_t res = -1;

    char c = 0;
    fd_set rfd;
    fd_set wfd;

    struct timeval tm;
    tm.tv_sec = 1;
    tm.tv_usec = 0;

    fd = opentty(argv[1]);
    if(fd != -1)
    {
        if(ttyinit(&fd, &t_old, atoi(argv[2])) == 1)
        {
            while (1)
            {
              c = 'a';
              write(fd, &c, 1);

            }
        
            close(fd);
        }
    }

    return 0;
}

Whit this code the RTS/CTS pins are not used since the RTS is never set. I am able to communicate from putty with and without hardware flow control set, but I don’t see (from the oscilloscpe) the RTS and CTS pins toggled when hardware flow control is enabled.

However, I am able to toggle manually the RTS pin using ioctl with the following code:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

static struct termios oldterminfo;

void closeserial(int fd)
{
    tcsetattr(fd, TCSANOW, &oldterminfo);
    if (close(fd) < 0)
        perror("closeserial()");
}

int openserial(char *devicename)
{
    int fd;
    struct termios attr;

    if ((fd = open(devicename, O_RDWR)) == -1) {
        perror("openserial(): open()");
        return 0;
    }
    if (tcgetattr(fd, &oldterminfo) == -1) {
        perror("openserial(): tcgetattr()");
        return 0;
    }
    attr = oldterminfo;
    attr.c_cflag |= CRTSCTS | CLOCAL;
    attr.c_oflag = 0;
    if (tcflush(fd, TCIOFLUSH) == -1) {
        perror("openserial(): tcflush()");
        return 0;
    }
    if (tcsetattr(fd, TCSANOW, &attr) == -1) {
        perror("initserial(): tcsetattr()");
        return 0;
    }
    return fd;
}

int setRTS(int fd, int level)
{
    int rts;

    if (ioctl(fd, TIOCMGET, &rts) == -1) {
        perror("setRTS(): TIOCMGET");
        return 0;
    }
    if (level)
        rts |= TIOCM_RTS;
    else
        rts &= ~TIOCM_RTS;

    if (ioctl(fd, TIOCMSET, &rts) == -1) {
        perror("setRTS(): TIOCMSET");
        return 0;
    }
    return 1;
}


int main(int argc, const char *argv[])
{
    int fd;
    char serialdev[20] = {'\0'};

    strcpy(serialdev, argv[1]);

    fd = openserial(serialdev);
    if (!fd) {
        fprintf(stderr, "Error while initializing %s.\n", serialdev);
        return 1;
    }

    while(1)
    {
        setRTS(fd, 0);
        sleep(1);       /* pause 1 second */
        setRTS(fd, 1);
        sleep(1);
    };

    closeserial(fd);
    return 0;
}

This implies to manually set the RTS flow control before writing as manually checking the CTS state before reading. Sounds bad…

I was expecting that the setting and checking of the RTS and CTS pins is performed by hardware and kernel-space, not by the user-space program.

What is the proper way to set the hardware control flow handled by the Dragonboard hardware and kernel ?
Thank you in advance.
Regards,
Simon

Hi,

I think I get totally confused. The RTS/CTS hardware flow seems to work fine.
Simon