0

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!

Gordon
  • 317
  • 1
  • 17

2 Answers2

4

...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.

sawdust
  • 16,103
  • 3
  • 40
  • 50
-1

I should be reading 19 bytes in every read. [...]

"/dev/ttyS1"

On a serial port with only 16 byte fifo or less? I don't think that will work.

Serial ports are character devices for a good reason - you want to code in a way that reads work anywhere from zero bytes (if a timeout is enabled), one byte and up to the size of the internal fifo. Don't expect them to stay in neat packets even if you (try to) send them that way.

Turbo J
  • 7,563
  • 1
  • 23
  • 43
  • 2
    The size of the UART FIFO is irrelevant to the possible number of bytes a **read()** syscall can return. – sawdust Jan 11 '18 at 01:59
  • 1
    Code that uses `O_NONBLOCK` should not expect that read() fills a buffer, because the system call will not wait for data to arrive. But you are correct, there could be many more bytes than UART internal fifo - the kernel itself buffers data. But that also means the last `read()` call happend a some time ago. – Turbo J Jan 11 '18 at 21:48