Search code examples
clinuxserial-portfile-descriptorread-write

Problems reading and writing to the same file descriptor in Linux


I am getting successful reads of a fd in Linux, but with 0 bytes read which means EOF has been reached. I should be reading 19 bytes in every read.

The project is a motor driver that sends out packets of 19 bytes to drive 2 DC motors, and it also needs to read the same size packet coming from the motor with updated position, command, and status info.

I open the fd like this:

mc_fd = InitPort("/dev/ttyS1", "COM2", O_NONBLOCK | O_RDWR | O_SYNC, B115200); 

Here is the function to initialize the port:

int InitPort( char *port, char *name, int oflags, speed_t baudRate ) {

int fd;                             // File descriptor
fd = open(port, oflags);            // Open the port like a file
assert(fd > 0);                     // Open returns -1 on error

struct termios options;             // Initialize a termios struct
tcgetattr(fd, &options);            // Populate with current attributes
cfsetospeed (&options, baudRate);   // Set baud rate out
cfsetispeed (&options, baudRate);   // Set baud rate in (same as baud rate out)
options.c_cflag &= ~CSIZE;          // Clear bit-length flag so it can be set
    //8N1 Serial Mode
    options.c_cflag |=  CS8;        // Set bit-length:  8
    options.c_cflag &= ~PARENB;     // Set parity:      none
    options.c_cflag &= ~CSTOPB;     // Set stop bit:        1
    options.c_cflag &= ~CRTSCTS;    // Set flow control:    none

options.c_iflag &= ~ICANON;         // Enable canonical input
options.c_oflag &= ~OPOST;          // Disables all output processing (prevents CR in output)
options.c_cflag |= (CLOCAL | CREAD);// Enable receiver, and set local mode
tcsetattr(fd, TCSANOW, &options);   // Set new attributes to hardware
return fd;
}

Originally, I only used the O_RDWR flag and reads of the fd would fail with EAGAIN (or EWOULDBLOCK). I have been trying synchronization and non-blocking settings to see if I could receive packets. At least now I am reading successfully (I think).

I am able to write packets out at 120Hz, and reads of the fd return "success" at the same rate, although with 0 bytes read.

How do I get read() to read the incoming packets? Here is the read code along with the output from the terminal:

bytesRead = read( mc_fd, readPacket, MC_PACKET_SIZE );
printf("\npacket: %019X\n", &readPacket);
perror("error type ");
printf("bytes read = %d\n", bytesRead);

packet: 00000000000B63B4140
error type : Success
bytes read = 0

The 8-digit hex number in the least significant part of the packet is always similar to that shown, and is not what is expected in the packet.

This is Debian running on an embedded linux SBC (single board computer). I am able to read other file descriptors in the program with no problems. I am still fairly new to Linux and may be missing something obvious. Thanks!


Solution

  • ...but with 0 bytes read which means EOF has been reached.

    Incorrect.
    You're reading a serial terminal in non-blocking mode.
    A return code of zero simply means that no data was available from the terminal at that time.
    That's what your program (which you should have posted) has to deal with when you use non-blocking mode.

    How do I get read() to read the incoming packets?

    Use blocking mode (i.e. remove the O_NONBLOCK option from the open()) if you do not want to see a return code of zero (or errno set to EAGAIN).
    But don't expect the read() syscall to message-align the packets for you unless you have text in canonical mode.

    Study this answer.

    The 8-digit hex number in the least significant part of the packet is always similar to that shown, and is not what is expected in the packet.

    You've posted so little of your code (which is grounds for closing the question), but you try to read the data into readPacket, which seems to be a (byte?) array.
    But then you treat readPacket as if it was an integer in the printf().

    Printing the address of the array address (or the address of an integer variable) accomplishes nothing (i.e. "8-digit hex number ... is always similar to that shown"). You have not displayed anything that might have been received.

    If you're using a little-endian, 32-bit processor, accessing a byte array as a long interger will reverse the order of the bytes of each word (i.e. "not what is expected"), and only access the first four bytes, which could be represented by eight hexadecimal digits .

    I am still fairly new to Linux ad may be missing something obvious.

    Although Linux is one of those OSes where (almost) "everything is a file", those "files" may not be equal. In particular the device file that your program accesses, i.e. /dev/ttyS1, is a serial terminal device. Serial terminals require additional device configuration, which is performed with the termios structure.
    Since you have posted only a few lines of your program, and make no mention of any termios concepts other than baudrate, your program cannot be evaluated.


    Addendum

    Now that you've posted some initialization code, a few more mistakes are evident.

    Regardless of your programming experience, the following inconsistency between what the code does and the comment is a flaw that can prolong debugging.

    options.c_iflag &= ~ICANON;         // Enable canonical input
    

    Clearing the ICANON flag enables non-canonical input, the opposite of what the comment states.
    You have not described the 19 bytes of data, so whether canonical mode is appropriate cannot be determined.

    Your termios initialization is well written (i.e. you use the proper Boolean operators instead of direct assignments), but is incomplete (based on the existing code as executed for non-canonical mode).
    All of the necessary flags for non-canonical mode can be configured by simply using the cfmakeraw() routine.
    Your code does not initialize the VMIN and VTIME parameters, but since the combination of non-canonical and non-blocking modes disables that feature, it doesn't matter.

    Since you have done a poor job of describing what you are trying to do, the appropriate corrections cannot be suggested.