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]

Re: read() blocking and TIOCINQ


Hello Igor,
Sorry for my incomplete question.
I attach to you the following file that could be compiled with gcc in
CYGWIN.
Accepted commands start by '?' character, and when the device is not
connected on the serial line the function read(...) blocks:


DEVICE test program -------------------

Type your commands ('.' to quit):
>> Enter command: ?ver


Then, blocked....:-((



Note that in our computers the serial line name is COMn not /dev/ttySn.


I tried before with /dev/ttySn and I receive file not found as answer.

Thank you for your help,

Ernesto.

PS.: more details about CYGWIN

PAISER@tenerife ~
$ uname -a
CYGWIN_NT-5.1 tenerife 1.5.21(0.156/4/2) 2006-07-30 14:21 i686 Cygwin


Igor Peshansky wrote:
On Tue, 22 Aug 2006, Ernesto Paiser wrote:

Hello Corinna,

FYI, this is a mailing list, and unless you're replying to a specific message, you're actually talking to many people.

I have problems with read() function blocking and
waiting for characters on serial line with cygwin:

Here are some code fragments:
========================================================
fd = open(sl, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY)

newpio.c_cflag = brate | CS8 | CLOCAL | CREAD;
newpio.c_iflag = IGNPAR | ICRNL;
newpio.c_oflag = 0;
newpio.c_lflag = 0;
newpio.c_cc[VTIME]    = 1;
newpio.c_cc[VMIN]     = 0;
...

n = ioctl(fd, TIOCINQ, &n); //It gives me an error (return -1) why??!!!

and

n = read(fd, buffer, 1);  <<< HERE IS BLOCKING!!!
========================================================

TIOCINQ is working on CYGWIN,

Is there another way to solve this problem???

Please post a complete compilable test case that reproduces the problem. For example, in the above code, it's unclear what the variable sl contains (and I suspect it contains "COM1", which is a no-no -- you should be using "/dev/ttyS0"). Igor


--
Ernesto PAISER ____________________ paiser@esrf.fr
E.S.R.F. - European Synchrotron Radiation Facility
6 rue Jules Horowitz  BP 220 Grenoble CEDEX France
phone +33 4 76 88 23 48      fax +33 4 76 88 23 25
#include <stdio.h>			/* Standard input/output definitions */
#include <string.h>			/* String function definitions */
#include <unistd.h>			/* UNIX standard function definitions */
#include <fcntl.h>			/* File control definitions */
#include <errno.h>			/* Error number definitions */
#include <termios.h>		/* POSIX terminal control definitions */
#include <ctype.h>
#include <stdlib.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>



// ............................................................................
// function declarations
int  serial_init (int portn, long baudrate);
int  serial_getchar(int fd, char *buffer);
int  serial_putnchar(int fd, char *data, int n);
void serial_flush(int fd);
void serial_close(int fd);
char *device_get(int fd);
char *device_comm(int fd, char *comm);
int device_getchar(int fd, char *buffer);
char *device_getline(int fd);
size_t check_rxbuffer(void);
int is_a_query(char *comm);

// ............................................................................
# ifndef TEMP_FAILURE_RETRY

#  define TEMP_FAILURE_RETRY(expression) \
(__extension__ \
      ({ long int __result; \
            do __result = (long int) (expression); \
              while (__result == -1L && errno == EINTR); \
              __result; }))

# endif /* TEMP_FAILURE_RETRY */

# define TFR TEMP_FAILURE_RETRY


//#define SLNAME_FORMAT "/dev/ttyS%d" // It doesn't work on Windows XP
#define SLNAME_FORMAT "COM%d"		// I'm using CYGWIN on Windows XP

#define INBUFF_SZ 1024
#define NO_ANSWER       ((char *)-1)
#define COMM_PREFIX   "COM"
#define BAUDRATE_CHAR '@'

#define DEF_BAUDRATE 9600
#define CR_STR         "\r"
#define CR_CHAR        '\r'
#define NL_CHAR        '\n'
#define ACKN_CHAR      '#'
#define QUERY_CHAR     '?'
#define MIN_RXBUFSIZE   1024
#define MAX_RXBUFSIZE   (128 * 1024)
#define COM1	1
#define COM2	2
#define COM3	3
#define COM4	4

struct termios oldpio;    // placeholder for original port settings

size_t   rxbuffsize;
char    *rxbuff;
char    *rxbuffend;
int      rxlines;

// ............................................................................

int main(void) {
   char    inbuff[INBUFF_SZ];
   char   *comm;
   char   *answ;
   int    fd;

	 fd = serial_init(COM1, 9600L);
   if (fd < 0) {
		printf("ERROR: %s\n", "serial_init()");
		exit(EXIT_FAILURE);
	 }


   printf("   DEVICE test program\n");
   printf("   -------------------\n\n");
   printf("Type your commands ('.' to quit):\n");
   while(1) {
      printf(">> Enter command: ");
      comm = fgets(inbuff, INBUFF_SZ, stdin);
      while(isspace(*comm))
         comm++;
      if (!*comm)
         continue;
      if (*comm == '.')
         break;
			
      answ = device_comm(fd, comm);

      if (answ == NO_ANSWER)
         ;
      else if (answ)
         printf("Answer: [%s]\n", answ);
      else
         printf("ERROR: %s\n", "device_comm()");
   }
   return(EXIT_SUCCESS);
}



int serial_init(int portn, long baudrate) {
   struct termios newpio;    // placeholder for new port settings
   long   brate;             // baudrate value
   int    fd;
   char   sl[sizeof(SLNAME_FORMAT)];

   if (portn < 0  || portn > 99) {
      fprintf(stderr, "serial_init(): Bad port number\n");
      return (-1);
   }
   sprintf(sl, SLNAME_FORMAT, portn);

   if ((fd = open(sl, O_RDWR | O_NOCTTY | O_NONBLOCK | O_NDELAY)) < 0) {
      perror("serial_init()");
      return (-1);
   } 
   tcgetattr(fd, &oldpio);         // save current port settings        
   bzero(&newpio, sizeof(newpio)); // clear struct for new port settings

   switch (baudrate) {
      case 38400:
    	 brate = B38400;
         break;
      case 19200:
         brate = B19200;
         break;
      case 9600:
         brate = B9600;
         break;
      case 4800:
         brate = B4800;
         break;
      case 2400:
         brate = B2400;
         break;
   }

   // in a more general case one could select 
   // newpio.c_cflag = brate | DATABITS | STOPBITS | PARITY | CLOCAL | CREAD;
   newpio.c_cflag = brate | CS8 | CLOCAL | CREAD;   
   newpio.c_iflag = IGNPAR | ICRNL;
   newpio.c_oflag = 0;
   newpio.c_lflag = 0;//ICANON;    
   newpio.c_cc[VTIME]    = 1;     // inter-character timer unused
   newpio.c_cc[VMIN]     = 0;     // blocking read until 1 character arrives 
     
   tcflush(fd, TCIOFLUSH);  // flush data in internal buffers
   tcsetattr(fd, TCSANOW, &newpio);
   rxbuff     = NULL;
   rxbuffsize = 0;


   return fd;     	
}

int input_timeout (int fd)
{
  fd_set set;
  struct timeval timeout;

  /* Initialize the file descriptor set. */
  FD_ZERO (&set);
  FD_SET (fd, &set);

  /* Initialize the timeout data structure. */
  timeout.tv_sec = 0;
  timeout.tv_usec = 0;

	// select should return 0 if timeout, 1 if input available, -1 if error.
  return TFR(select(fd + 1, &set, NULL, NULL, &timeout));  
}


int serial_getchar(int fd, char *buffer) {
   ssize_t n;
   
   n = input_timeout (fd);   						// it doesn't work...

//	 n = ioctl(fd, FIONBIO , &optval);		// 
//	 n = ioctl(fd, FIONREAD, &optval);	  // FIONREAD not defined on CYGWIN!!!! 
   n = ioctl(fd, TIOCINQ, &n);					// It gives me an error (return -1)	 

      
   if (n)
   	n = read(fd, buffer, 1);
   	
   if (n == EINTR || n == EAGAIN || n == 0) { // read interrupted or not data available
      n = 0;
   } else if (n != 1) {
      perror("serial_getchar()");
      return(-1);
   }
   return(n);
}

int serial_putnchar(int fd, char *data, int n) {

   n = write (fd, data, (ssize_t)n);	
   if (n < 0) {
      perror("serial_putnchar()");
      return(-1);
   }
   return(n);
}

void serial_flush(int fd) {
   tcflush(fd, TCIOFLUSH);  // flush data in internal buffers
}

void serial_close(int fd) {
   close(fd);
   tcsetattr(fd, TCSANOW, &oldpio);
}

char *device_comm(int fd, char *comm)
{
   char *answer;

	 serial_flush(fd);

   if (serial_putnchar(fd, comm, strlen(comm)) < 0) {
      return(NULL);
   } else if (serial_putnchar(fd, CR_STR, sizeof(CR_STR) - 1) < 0) {
      return(NULL);
   }

	 if (is_a_query(comm))
      answer = device_get(fd);
   else 
      answer = NO_ANSWER;

   return(answer);
}

char *device_get(int fd)
{
   char    *line;
   int      nlines;

   rxbuffend = rxbuff;
   line = device_getline(fd);
   if (!line) {
      nlines = -1;
   } else
      nlines = 1;

	 rxlines = nlines;
   return (nlines < 0 ? NULL : rxbuff);
}

char *device_getline(int fd)
{
   int     remsize;
   time_t  tout;
   int     nloffset;

   if (!(remsize = check_rxbuffer())) {
      return(NULL);
   }
   nloffset = rxbuffend - rxbuff;
   tout = time(NULL) + 2; // crappy 2 sec timeout
   while(time(NULL) < tout) {
   	  int c;
   	  
      c = device_getchar(fd, rxbuffend);
      if (c) {
         if (c < 0) {
            return(NULL);
         } else {
            rxbuffend++;
            remsize--;
            if (c == NL_CHAR)
               return(rxbuff + nloffset);             
            else if (!remsize) {
               if (!(remsize = check_rxbuffer()))
                  return(NULL);
            }
         }
      }
   }
   printf("ERROR: %s\n", "device_getline()");
   return(NULL);
}

size_t check_rxbuffer(void) 
{
   size_t  buffsize;
   size_t  newsize;
   char   *newbuff;

   buffsize = rxbuffend - rxbuff;
   if (!rxbuffsize) {
      newsize = MIN_RXBUFSIZE;
   } else if (rxbuffsize == buffsize) {
      if (rxbuffsize < MAX_RXBUFSIZE)
         newsize = 2 * rxbuffsize;
      else
         return(0);
   } else {
      return(rxbuffsize - buffsize);
   }
   if (!(newbuff = realloc(rxbuff, newsize)))
      return(0);

   rxbuffsize = newsize;
   rxbuff = newbuff;
   rxbuffend = newbuff + buffsize;
   return(newsize - buffsize);
}

int device_getchar(int fd, char *buffer)
{
   int  nchar;
   int  c;

   nchar = serial_getchar(fd, buffer);

	 if (nchar == 0) {
      return(0);
   } else if (nchar < 0) {
      printf("ERROR: %s\n", "device_getchar()");
      return(-1);
   }
   c = *buffer;
   if (c == NL_CHAR) {
      *buffer = 0;
      return(NL_CHAR);
   } else if (c < ' ')
      return(0);
   else
      return(c);
}

int is_a_query(char *comm)
{
   char  firstc;

   // skip whitespaces
   while((firstc = *comm) != 0 && isspace(firstc))
      comm++;
   if (isdigit(firstc)) {
      comm++;
      while((firstc = *comm) != ':')
         comm++;
      return(is_a_query(++comm));
   } else if (firstc == '>')
      return(is_a_query(++comm));
   else if (firstc == ACKN_CHAR || firstc == QUERY_CHAR)
      return(1);
   else
      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]