2

I am trying to write and read data from an USB port in a cygwin command central. I have managed to write and read data when the device is connected but I want a way to detect if the other device isn't present or is unable ta send back any data. My current test code is shown below(I have tried a bunch of different things but nothing seemed to work).

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <stdio.h>
#include "USB_com.h"
#include "unistd.h"
void main()
{
  int fd, value, bytes_read, bytes_written, nbytes, i, j;
  char buffR[20];
  char buffS[20];
  fd = USB_init("/dev/com1");
  printf("enter a message (write exit to terminate the session): ");
  fgets(buffS, 19, stdin);
  while (strncmp("exit", buffS, 4) != 0)
  {
    bytes_written = write(fd, buffS, 19);
    sleep(1);
    bytes_read = read(fd, buffR, 19);
    printf("string recieved : %s\n", buffR);
    memset(buffS, '\0', 19);
    printf("enter a message (write exit to terminate the session): ");
    fgets(buffS, 19, stdin);
  }
  USB_cleanup(fd);
}

And my USB_init.c for writing and reading from the USB device is shown below.

#include "USB_init.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <stdlib.h>
#include <strings.h>
#include <stdio.h>


/* baudrate settings are defined in <asm/termbits.h>, which is
 * included by <termios.h> */
#ifndef BAUDRATE
#define BAUDRATE B9600
#endif

#define _POSIX_SOURCE 1     /* POSIX compliant source */

static int fd, c, res;
static struct termios oldtio, newtio;
static char *device;

int USB_init(char *modemdevice)
{
    /* 
     * Open modem device for reading and writing and not as controlling tty
     * because we don't want to get killed if linenoise sends CTRL-C.
     **/
    device = modemdevice;
    //fd = open (device, O_RDWR | O_NOCTTY | O_NDELAY);
    fd = open (device, O_RDWR | O_NOCTTY );
    if (fd < 0)
      {
      perror (device);
      exit(-1);
      }

    tcgetattr (fd, &oldtio);    /* save current serial port settings */
    bzero (&newtio, sizeof (newtio));   /* clear struct for new port settings */

    /* 
     *BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
     *CRTSCTS : output hardware flow control (only used if the cable has
     *all necessary lines. )
     *CS8     : 8n1 (8bit,no parity,1 stopbit)
     *CLOCAL  : local connection, no modem contol
     *CREAD   : enable receiving characters
     **/
    newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;

    /*
     *IGNPAR  : ignore bytes with parity errors
     *ICRNL   : map CR to NL (otherwise a CR input on the other computer
     *          will not terminate input)
     *          otherwise make device raw (no other input processing)
     **/
    newtio.c_iflag = IGNPAR | ICRNL;

    /*
     * Map NL to CR NL in output.
     *                  */
#if 0
    newtio.c_oflag = ONLCR;
#else
    newtio.c_oflag = 0;
#endif


    /*
     * ICANON  : enable canonical input
     *           disable all echo functionality, and don't send signals to calling program
     **/
#if 1
    newtio.c_lflag = ICANON;
#else
    newtio.c_lflag = 0;
#endif

    /* 
     * initialize all control characters 
     * default values can be found in /usr/include/termios.h, and are given
     * in the comments, but we don't need them here
     *                                       */
    newtio.c_cc[VINTR] = 0; /* Ctrl-c */
    newtio.c_cc[VQUIT] = 0; /* Ctrl-\ */
    newtio.c_cc[VERASE] = 0;    /* del */
    newtio.c_cc[VKILL] = 0; /* @ */
    newtio.c_cc[VEOF] = 4;  /* Ctrl-d */
    newtio.c_cc[VTIME] = 0; /* inter-character timer unused*/
    newtio.c_cc[VMIN] = 1;  /* blocking read until 1 character arrives*/
    newtio.c_cc[VSWTC] = 0; /* '\0' */
    newtio.c_cc[VSTART] = 0;    /* Ctrl-q */
    newtio.c_cc[VSTOP] = 0; /* Ctrl-s */
    newtio.c_cc[VSUSP] = 0; /* Ctrl-z */
    newtio.c_cc[VEOL] = 0;  /* '\0' */
    newtio.c_cc[VREPRINT] = 0;  /* Ctrl-r */
    newtio.c_cc[VDISCARD] = 0;  /* Ctrl-u */
    newtio.c_cc[VWERASE] = 0;   /* Ctrl-w */
    newtio.c_cc[VLNEXT] = 0;    /* Ctrl-v */
    newtio.c_cc[VEOL2] = 0; /* '\0' */

    /* 
     * now clean the modem line and activate the settings for the port
     **/
    tcflush (fd, TCIFLUSH);
    tcsetattr (fd, TCSANOW, &newtio);

    /*
     * terminal settings done, return file descriptor
     **/

    return fd;
}

void USB_cleanup(int ifd){
    if(ifd != fd) {
        fprintf(stderr, "WARNING! file descriptor != the one returned by serial_init()\n");
    }
    /* restore the old port settings */
    tcsetattr (ifd, TCSANOW, &oldtio);
}

Can someone tell me how I can do something like read(fd,buffR,19) but abort it after some time if I haven't recieved any data and print something like printf("no contact with the device")?

I am extremely thankful for any suggestions on how to solve this!

2
  • "I am trying to write and read data from an USB port" -- If you're using a USB-to-RS232 adapter (or a USB gadget that emulates a serial port device), then the fact that the USBus is involved is totally transparent to the application program. So naming your functions USB_init() and USB_cleanup() when they do nothing with respect to USB is misleading. bzero (&newtio, sizeof (newtio)) is a common but wrong method of initializing a termios structure. Study Setting Terminal Modes Properly Commented May 26, 2014 at 1:44
  • @sawdust - you are of course generally right about the misuse of the USB label with what is a serial API, but it is worth remembering that USB serial converters are not, in fact totally transparent - they are packetized transport devices with substantial latency, a difference which can have practical impact on application programs and protocols. Commented May 27, 2014 at 12:55

2 Answers 2

4

When working with serials, you have two options: either use select/poll with timeout, or configure the port using termios.

For select and poll, the approach is first wait for event on descriptor and read after successful result. Pro of this method is that it works also with network sockets and some other descriptor types:

int fd = ...
fd_set fds;
stuct timeval timeout;

timeout.tv_sec = 10; /* timeout in secs */
timeout.tv_usec = 0;
FD_ZERO(&fds)
FD_SET(fd, &fds)
if (select(fd, fds, NULL, NULL, &timeout) > 0)
  read(...)
else ... timeout

The poll example is very similar to above.

But, when working with any kind of serial devices, there is another way of making read to time out correctly. You need to use VMIN and VTIME attributes:

newtio.c_cc[VTIME] = timeout; /* timeout in 1/10 of second  1==100ms, 10==1sec*/
newtio.c_cc[VMIN] = 0;

That is it.

Sign up to request clarification or add additional context in comments.

4 Comments

Second part of answer is incomplete and misleading. VMIN and VTIME are only salient when non-canonical input mode is used.
@sawdust What do you mean by non-canonical input mode?
Anyone using this in a loop should be wary to reset timeout.tv_sec and timeout.tv_usec each iteration. The last paragraph of select(2) The timeout details how Linux alters these values.
2

You might like to take a look at select() to monitor the file descriptor for being ready to be read from. It could be called with a time-out.

Or make the file describtor non-blocking when open()ing it or later using fcntl(), so it will return from read() with EAGAIN or EWOULDBLOCK if no data is available, which would allow you to implement your on logic to decide when to retry or to cancel reading.

2 Comments

It says ` Note that this flag has no effect for regular files and block devices;` man7.org/linux/man-pages/man2/open.2.html
@shaoyihe: Linking the Linux man-pages in a Cygwin context probably was not a good idea. The POSIX documentation is here: pubs.opengroup.org/onlinepubs/9699919799/functions/open.html It specifies the case of non-/blocking differently.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.