I'm implementing a multi-drop serial protocol over a tty serial port on an embedded Linux platform running a 2.6.18 kernel. The protocol is half duplex - the host sends ID polling packets periodically (about 100 per second) and if a device has data to send it waits for its ID before sending it. Pretty standard simple stuff which I've done many times on microcontrollers.
The problem is that the Linux host is caching my transmit data (the polling packets) and spitting them out back to back at full data rate in bursts. So, instead of one packet every 10ms, I'm getting 20 packets back to back, then a 200ms ish delay then another 20 and so on. When I send a packet I need Linux to flush it to the serial port immediately, not cache it for a later burst. In any case, 200ms seems like an infeasibly huge delay.
I looked into the ASYNC_LOW_LATENCY flag but when set the port stops working altogether. From grepping the kernel source it doesn't look like this is implemented for davinci.
Here's an extract of my code (with error handling removed for brevity):
fd_target=open(TARGET_TTY, O_RDWR | O_NDELAY | O_NOCTTY);
tcgetattr(fd_target, &config
config.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON | IXOFF);
config.c_oflag &= ~(OPOST | OLCUC | ONLCR | OCRNL | ONOCR | ONLRET | OFILL | OFDEL | NLDLY | CRDLY | TABDLY | XTABS | BSDLY | VTDLY | FFDLY);
config.c_cflag &= ~(CSIZE | PARENB | CSTOPB | HUPCL);
config.c_cflag |= CS8 | CLOCAL | CREAD;
config.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON | IEXTEN | ISIG | NOFLSH | TOSTOP);
config.c_cc[VMIN] = 1;
config.c_cc[VTIME] = 0;
cfsetispeed(&config, TARGET_BPS);
cfsetospeed(&config, TARGET_BPS);
tcsetattr(fd_target, TCSAFLUSH, &config);
FD_ZERO(&target_fds);
FD_SET(fd_target, &target_fds);
fdmax = fd_target;
while(1)
{
/* Poll for incoming data, unblock anyway after 10ms */
read_fds = target_fds;
tv.tv_sec = 0;
tv.tv_usec = 10000;
select(fdmax+1, &read_fds, NULL, NULL, &tv);
/* Check for serial data */
if(FD_ISSET(fd_target, &read_fds))
{
/* Empty the device into our state machine */
do
{
n = read(fd_target, buf, 1);
if(n>0) target_fsm(*buf);
}
while(n>0);
}
else
{
/* Poll targets if there's nothing incoming - pkt is setup with the appropriate data*/
write(fd_target, &pkt, 3);
}
}
I tried the same thing in blocking mode (without the inner loop around the read) - same behaviour. I need something to force that write through to the port. I tried tcdrain() but that just causes my thread to block until the FIFO empties after 200ms or so.
I checked that the scheduler isn't causing the latency - the thread is waking 100 times per second as expected and the CPU load is very low, so I'm pretty sure this behaviour is somewhere in the tty.
Any advice appreciated! Hardware is the DM355 EVM, software is the RidgeRun SDK with 2.6.18 montavista kernel.
Cheers
Rob