This is the mail archive of the cygwin mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

1.5.18: Possible bug with select and serial ports


Hello everyone,
I have what I suspect is a bug in cygwin's serial port handling which only seems to manifest itself when using select, then read.

I have distilled the code into one simple piece of C which reproduces the fault. The general idea of the code (and 
how I want to handle serial ports in my applicaton) is to have one thread waiting for incoming data on any number of serial ports (in the test code 2). 

This reader thread will then send data to a registered user (in the application a C++ object, in the test code the data is just printed out). Data will only be sent to the user when an inter character timer has expired as this will be considered an end of message (in the test code =1sec). Therefore when the port is opened the options.c_cc[VTIME] is set to 10 and options.c_cc[VMIN] is set to 100. 

The test code sends a long string of data from one port (/dev/ttyS0) to another (/dev/ttyS3) which are physically looped back to back on the PC. The code is sent and received as expected however the reader thread just gets a few bytes of data at a time and does not seem to wait for the inter character timer to expire as expected. It appears as though after select, read just grabs whatever data is in the buffer at the time and does not take into consideration VTIME and VMIN. It therefore takes several reads to get the data and the "message" structure I want is lost.

I have also included a 2nd file which does what I want without the select (i.e. it only reads data coming in on one port) and in that case it waits for the timeout and therefore delivery a complete "message" to the user.

This fault occured with CygWin 1.5.18 on Windows XP. For a trial I compiled exactly the same code on Linux and I got the desired behaviour for both cases. So, I may be doing something wrong, but even so, Linux and Cygwin behave differently and Linux seems to be correct to my way of thinking.

Hopefully someone can have a look at this issue and confirm it is a bug. Alternatively, if someone can see a flaw in what I'm doing, or a workaround I would be very grateful.
Best regards,
Tim Hatton
tim --- at --- coherent ---- dash --- tech ----- dot ---- co ---- uk

file1: with select

#include <pthread.h>
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#include <sys/select.h>
#define BUFFER_SIZE 100
char* Message="Hello1 Hello2 Hello3 Hello4 Hello5 Hello6 Hello7 Hello8 Hello9 Hello10";
char incomingBuffer[BUFFER_SIZE];
int fd1;
int fd2;
void* ReaderThread(void* object) {
	int total=0;
	int expected=strlen(Message);
	struct timeval timeOut;
	fd_set fdSet;
	int maxFdp=0;

	printf("Waiting for data on serial ports\r\n");			
	while(total<expected) {
		timeOut.tv_sec=0;
		timeOut.tv_usec=100000; // Every 100 ms 
		FD_ZERO(&fdSet);
		FD_SET(fd1, &fdSet);
		FD_SET(fd2, &fdSet);	
		maxFdp=fd2+1;		
		int result=select(maxFdp, &fdSet, (fd_set*)0, (fd_set*)0, &timeOut);
		if(result<0) {
			printf("A select error occured.. exiting\r\n");
			exit(-1);
		}
		else if(result>0) {
			printf("Data is available on a port\r\n");
			int n=0;
			if(FD_ISSET(fd1, &fdSet)) {
				n=read(fd1, incomingBuffer, BUFFER_SIZE);		
			}
			else if(FD_ISSET(fd2, &fdSet)) {
				n=read(fd2, incomingBuffer, BUFFER_SIZE);						
			}
			else {
				printf("Unexpected fd from select... exiting\r\n");
				exit(-1);				
			}
			incomingBuffer[n]='\0';
			printf("Received: %s\r\n", incomingBuffer);
			total+=n;		
		}
		// else (result==0) just a time out so go round again
	}
}
int OpenPort(const char* port) {
		printf("Opening serial %s\r\n", port);
		int fd=open(port, O_RDWR | O_NOCTTY | O_NDELAY);
		if(fd==-1) {
			printf("Error opening serial port %s... exiting", port);
			exit(-1);
		}			
		fcntl(fd, F_SETFL, 0); /* Reads will be blocking */
		struct termios options;		
		tcgetattr(fd, &options);
		(void)cfsetispeed(&options, B2400); /* (void) is to stop warning in cygwin */
		(void)cfsetospeed(&options, B2400); 
		options.c_cflag &= ~CSIZE;
		options.c_cflag |= CS7;  /* 7 bits */
		options.c_cflag &= ~CSTOPB; /* 1 stop bit */
		options.c_cflag |= PARENB; /* Even parity */
		options.c_cflag &= ~PARODD;
		options.c_cflag &= ~CRTSCTS; /* HW flow control off */
		options.c_lflag =0; /* RAW input */
		options.c_iflag = 0;		/* SW flow control off, no parity checks etc */		
		options.c_oflag &= ~OPOST; /* RAW output */
		options.c_cc[VTIME]=10; /* 1 sec */
		options.c_cc[VMIN]=BUFFER_SIZE;
		options.c_cflag |= (CLOCAL | CREAD); 		
		tcsetattr(fd, TCSAFLUSH, &options);					
		return fd;	
}
int main(int argc, char** argv) {
	/* Open port /dev/ttyS0 and /dev/ttyS1
	 * Both will be set to 2400 7E1
	 */		 

	fd1=OpenPort("/dev/ttyS0");
	fd2=OpenPort("/dev/ttyS3"); /* assuming fd2>fd1 in thread */
	/* Start thread to wait for incoming message on ttyS1 */
	pthread_t tid;
	pthread_create(&tid, (pthread_attr_t*)(0), &ReaderThread, (void*)(0));
	sleep(1);
	printf("Sending message...\r\n");
	write(fd1, Message, strlen(Message));
	printf("Data sent... \r\n");
	pthread_join(tid, (void*)(0));
	close(fd1);
	close(fd2);

	return 0;
}


file2: without select
#include <pthread.h>
#include <stdio.h>
#include <termios.h>
#include <fcntl.h>
#include <string.h>
#define BUFFER_SIZE 100
char* Message="Hello1 Hello2 Hello3 Hello4 Hello5 Hello6 Hello7 Hello8 Hello9 Hello10";
char incomingBuffer[BUFFER_SIZE];
int fd1;
int fd2;
void* ReaderThread(void* object) {
	int total=0;
	int expected=strlen(Message);
	printf("Waiting for data on 2nd serial port\r\n");			
	while(total<expected) {
		int n=read(fd2, incomingBuffer, BUFFER_SIZE);						
		incomingBuffer[n]='\0';
		printf("Received: %s\r\n", incomingBuffer);
		total+=n;		
	}
}
int OpenPort(const char* port) {
		printf("Opening serial %s\r\n", port);
		int fd=open(port, O_RDWR | O_NOCTTY | O_NDELAY);
		if(fd==-1) {
			printf("Error opening serial port %s... exiting", port);
			exit(-1);
		}			
		fcntl(fd, F_SETFL, 0); /* Reads will be blocking */
		struct termios options;		
		tcgetattr(fd, &options);
		(void)cfsetispeed(&options, B2400); /* (void) is to stop warning in cygwin */
		(void)cfsetospeed(&options, B2400); 
		options.c_cflag &= ~CSIZE;
		options.c_cflag |= CS7;  /* 7 bits */
		options.c_cflag &= ~CSTOPB; /* 1 stop bit */
		options.c_cflag |= PARENB; /* Even parity */
		options.c_cflag &= ~PARODD;
		options.c_cflag &= ~CRTSCTS; /* HW flow control off */
		options.c_lflag =0; /* RAW input */
		options.c_iflag = 0;		/* SW flow control off, no parity checks etc */		
		options.c_oflag &= ~OPOST; /* RAW output */
		options.c_cc[VTIME]=10; /* 1 sec */
		options.c_cc[VMIN]=BUFFER_SIZE;
		options.c_cflag |= (CLOCAL | CREAD); 		
		tcsetattr(fd, TCSAFLUSH, &options);					
		return fd;	
}
int main(int argc, char** argv) {
	/* Open port /dev/ttyS0 and /dev/ttyS1
	 * Both will be set to 2400 7E1
	 */		 

	fd1=OpenPort("/dev/ttyS0");
	fd2=OpenPort("/dev/ttyS3"); /* assuming fd2>fd1 in thread */
	/* Start thread to wait for incoming message on ttyS1 */
	pthread_t tid;
	pthread_create(&tid, (pthread_attr_t*)(0), &ReaderThread, (void*)(0));
	sleep(1);
	printf("Sending message...\r\n");
	write(fd1, Message, strlen(Message));
	printf("Data sent... \r\n");
	pthread_join(tid, (void*)(0));
	close(fd1);
	close(fd2);

	return 0;
}




--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]