Embedded linux serial port

I used a simple driver for serial port in Linux with success in many projects, but recently I found an issue with it.

The scenario is an embedded Linux (running on iMX6) that runs a QT application (that creates a GUI on a touch display) and a C application that communicates over a serial port.

When QT application starts some complex graphics (I see its CPU load reaching 70-80%), the serial port application stops working correctly. After some debugging, I noticed that the bytes really transmitted on the wire aren't correct. It seems one or two bytes are re-transmitted and one-two bytes aren't completely transmitted.

I suspect my serial port driver has some errors that are triggered only when the CPU is loaded by other processes, but I don't know how to fix them.

I use O_NONBLOCK flag when opening serial port, but write() always returns a positive number, so I think the byte is correctly moved to the low-level serial port driver and really transmitted... but it isn't always the case.

If I enable debugging messagin (with DEBUG_SERIAL macro), I always see correct data on stdout, but wrong data on the wire.

Any hint?

#include #include #include #include #include #include #include #include #include #include "serial.h"

//#define DEBUG_SERIAL

#ifdef DEBUG_SERIAL static bool debug_tx; #endif

SERIAL_HANDLE serial_open(const char *serial_name, int baudrate) { if (serial_name == NULL) return INVALID_SERIAL_HANDLE_VALUE;

speed_t speed; if (baudrate == 57600) { speed = B57600; } else if (baudrate == 115200) { speed = B115200; } else { return INVALID_SERIAL_HANDLE_VALUE; }

int hSerial;

hSerial = open(serial_name, O_RDWR | O_NOCTTY | O_NONBLOCK); if (hSerial != -1) { int oldflags = fcntl (hSerial, F_GETFL, 0); if (oldflags == -1) return -1; oldflags &= ~O_NONBLOCK; if (fcntl(hSerial, F_SETFL, oldflags) == -1) { return INVALID_SERIAL_HANDLE_VALUE; }

struct termios options; tcgetattr(hSerial, &options);

cfsetispeed(&options, speed); cfsetospeed(&options, speed);

/* Input mode flags */ options.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON | INPCK ); options.c_iflag |= IGNBRK;

/* Output mode flags */ options.c_oflag &= ~OPOST;

/* Control mode flags */ options.c_cflag |= (CLOCAL | CREAD); options.c_cflag &= ~( CSTOPB | PARENB); options.c_cflag &= ~CSIZE; options.c_cflag |= CS8;

/* Local mode flags */ options.c_lflag &= ~(ICANON | ECHO | ISIG); options.c_lflag = NOFLSH; options.c_cc[VMIN] = 0; options.c_cc[VTIME] = 1;

tcsetattr(hSerial, TCSANOW, &options);

#ifdef DEBUG_SERIAL debug_tx = 1; printf("-> "); #endif }

return (SERIAL_HANDLE)hSerial; }

void serial_close(SERIAL_HANDLE hSerial) { close(hSerial); }

int serial_getdata(SERIAL_HANDLE hSerial, void *data, size_t data_len, unsigned int timeout_sec) { size_t bytes_read = 0; uint8_t *d = data; time_t t_begin = time(NULL);

while(bytes_read != data_len) { if ((timeout_sec > 0) && (time(NULL) - t_begin > timeout_sec)) { break; } int ret; ret = read(hSerial, d, data_len - bytes_read); if (ret < 0) return -1; // Error if (ret > 0) { #ifdef DEBUG_SERIAL if (debug_tx) { printf("\n "); debug_tx = 1; } printf("%02X", c); #endif int bytes_written;

bytes_written = write(hSerial, &c, 1); if (bytes_written != 1) return -1;

return 0; }

int serial_putdata(SERIAL_HANDLE hSerial, const void *data, size_t size) { const unsigned char *d = data; while(size--) { int ret; ret = serial_putchar(hSerial, *d++); if (ret < 0) return -1; } return 0; }

int serial_putstr(SERIAL_HANDLE hSerial, const char *s) { if (s == NULL) return -1; return serial_putdata(hSerial, s, strlen(s)); }

#endif

Reply to
pozz
Loading thread data ...

[I didn't read your code] But here's what I used for transmitting.

static int send_char (ser * d, char c) { if (d->fd == -1) longjmp (d->env, 2); ssize_t nbytes = TEMP_FAILURE_RETRY (write (d->fd, &c, 1)); return !(nbytes == 1); }

I think the TEMP_FAILURE_RETRY may be important. but there's about a zillion other flags and iocls wot could be at fault. So I dont' really know if it helps.

Reply to
Johann Klammer

I don't think TEMP_FAILURE_RETRY could help in my case. It simply retry forever the write() while it returns -1 and errno==EINTR.

My function for writing is:

int serial_putchar(SERIAL_HANDLE hSerial, unsigned char c) { int bytes_written;

bytes_written = write(hSerial, &c, 1); if (bytes_written != 1) return -1;

return 0; }

If write() returned -1, serial_putchar() would have returned -1 and the caller would have detected this situation.

However in my case, when the problem arises, write() and read() never return a negative number.

Reply to
pozz

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.