Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does select return when USB cable is unplugged?

I'm having a problem with some linux c code that reads from ttyUSB ports using select, FD_ISSET, read, etc. My modem uses an FTDI serial to USB cable for input. The problem is that select unblocks when the USB cable is unplugged. Is there a way to prevent it from doing that?

count = 0;
while ( g_running ) {
   FD_ZERO(&readFdSet);
   maxfd = 0;
   numTransPorts = 0;
   logger( DEBUG, "Begin g_running loop - %d", count );
   for ( i = 0; i < MAX_CONFIG_PORTS; i++ ) {
      if ( configPorts[i].commType == 1 && configPorts[i].pttyHost != NULL ) {
         FD_SET( configPorts[i].pttyHost->fd, &readFdSet );
         logger( DEBUG, "FD_SET - fd=%d, index=%d", configPorts[i].pttyHost->fd, i );
         if ( configPorts[i].pttyHost->fd >= maxfd ) {
            maxfd = configPorts[i].pttyHost->fd;
         }
         numTransPorts++;
      }
   }
   maxfd++; // add one because select check a range to n-1 file descriptors
   if ( maxfd != 0 ) { // indicates no ports are available
      logger( DEBUG, "Calling select() with %d ports and maxfd of %d", numTransPorts, maxfd );
      logger( INFO, "Waiting for input ..." );
      select( maxfd, &readFdSet, NULL, NULL, NULL ); // blocking until one available
      if( result == -1 ){
         logger( INFO, "select() error. errno: %d", errno );
      } else if ( result > 0 ){
         for ( i = 0; i < MAX_CONFIG_PORTS; i++ ) {
            if ( FD_ISSET( configPorts[i].pttyHost->fd, &readFdSet ) ) { // input is available
               logger( INFO, "Input on port %s", configPorts[i].pttyHost->serialPath );
               result = serialPortRead( buffer, configPorts[i].pttyHost->fd );
               if ( result <= 0 ) {
                  // there was an error due to the file descriptor. It
                  // probably indicates that the tty ports are no longer available
               }
            }
         }
      } else {
         logger ( INFO, "select() returns 0" );
      }
   }
   count++;
}

serialPortRead:

int serialPortRead( char *buf, int serialHandle ) {
   //char ch;
   char *ptr;
   int res = 0;
   int bytesRead = 0;
   int i;

   logger( TRACE, "TRACE: serialPortRead() with fd = %d", serialHandle );

   ptr = buf;

   // try 3 times
   for ( i = 0; i < 3; i++ ) {
      while ( (res = read( serialHandle, ptr, 1 )) > 0 ) { // read 1 byte at a time
         if ( *ptr == 0x0d ) { //there is 0x0d as a terminate byte from ECR
            break;
         }
         ptr += res;
      }
      if ( res < 0 && errno == EAGAIN ) {
         continue;
      } else {
         break;
      }
   }

   *ptr = 0x00; // set 0x00 as a terminate byte
   //   pthread_mutex_unlock(&g_serial_trans_mutex);
   if ( res < 0 ) {
      // if res is -1, there is an error
      bytesRead = -1;
      logger( DEBUG, "serialPortRead error. errno = %d", errno );
   } else {
      bytesRead = (int) (ptr - buf);
      logger( DEBUG, "serialPortRead %d bytes", bytesRead );
   }

   return bytesRead;
}

When the USB cable is unplugged, select() unblocks, implying input is available, FD_ISSET returns true. read(), in serialPortRead, will return with zero bytes having been read. It then loops back to the select() which unblocks again saying input is available, and so on. Thus, you get a infinite loop of select(), FD_ISSET returning true, the fd never gets cleared, read returns 0 and so on. How can I fix this? The behavior I would expect is that select does not falsely unblock when there is not really anything to read?

NOTE: when select unblocks it returns a positive number

like image 421
Jim Avatar asked Feb 18 '26 06:02

Jim


1 Answers

select() is returning because there is information to read - in this case, the fact that the file descriptor has reached "end of file". This is indicated by read() returning 0.

When read() returns 0, you should be closing the corresponding file descriptor.

like image 171
caf Avatar answered Feb 20 '26 23:02

caf



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!