Page 1 of 1

IO Redirection and Dual Stdio - FileIO.CPP issue

Posted: Fri Aug 03, 2012 11:41 am
by cfavreau
I have used the dualstdio example I found elsewhere (http://forum.embeddedethernet.com/viewt ... f=5&t=1397). It basically redirects the stdin, stdout, and stderr to a custom File Descriptor and allows the use of stdio over telnet and a serial port at the same time. This is a very nice set of code.

I am running into an issue with the stdioinbyte() and read() functions in the fileio.cpp file. The standard operation (referring to stdioinbyte() function) when the stdio is not redirected is to call the LocalInByte() function which leads back to reading data from the serial ports. When you redirect the IO to your own file descriptor it uses a FD (file descriptor) number >= 3. This causes the code to do this:

Code: Select all

if ( read( fd_map[fd], &c, 1 ) != 1 )
   {
      fd_map[fd] = -1;
   }
Now that is find and dandy... but the read() function does something for you that ends up closing your stdin redirected file descriptor by executing the above if statement which BTW is very irritating.

The dualstdio IO code provides the IO library with its own read/write functions which the dual_read() function returns the # of bytes read. So if it does not read any bytes then it returns 0.

In the fileio.cpp -> read() function there is some code that processes the IOCTL_LAST_IS_RTN flag which is set by default. That is OK. Now this code does a little trick in which it replaces your \r with a \n ... and then attempts to delete or read over the next byte iff it is a \n. It does this by making the loop go round 1 more time. So what ends up happening is that the function calls the stdioinbyte() function again but this time since it is the 2nd byte (we are reading a single character at a time using the getc() function)... we don't have a 2nd byte so dual_read() returns a 0 which causes stdioinbyte() to close input file descriptor..... ugh. You will not see this problem using MTTY or some other terminal programs because they only send a \n. I am testing with telnet and HyperTerminal (configured to send a \r\n).

So my question is... what is the proper way to handle this? I have worked around the problem by having dual_read() return a 1 and setting the buffer[0] = 0... however it still returns a 0. I run into a problem figuring out how the LocalInByte() handles it because I get to that point in the debugger to see what happens and the debugger stops working but my program still works. LocalInByte() has to return something... same as dual_read() because the return from stdioinbyte() is a character however it doesn't actually appear to do that when debugging. What am I missing?

Thanks,
Chris Favreau

Re: IO Redirection and Dual Stdio - FileIO.CPP issue

Posted: Fri Aug 03, 2012 12:00 pm
by cfavreau
It occurred to me that I should probable post the relevant code for read() and stdioinbyte()

From system/fileio.c
Please note that IOCTL_RX_ECHO, IOCTL_RX_CHANGE_CRLF, and IOCTL_RX_PROCESS_EDITS are all set.

Code: Select all

/***********************************************/
/*   read -- read bytes from the proper file descriptior */
/***********************************************/

int read( int fd, char *buf, int nbytes )
{
   if ( fd >= EXTRA_IO_OFFSET )
   {
      if ( pExtraFdFunc )
      {
         return pExtraFdFunc->read( fd, buf, nbytes );
      }
      else
      {
         return 0;
      }
   }

   if ( fd >= TCP_SOCKET_OFFSET )
   {
      return _Socket_Private_read( fd, buf, nbytes );
   }



   if ( fd >= SERIAL_SOCKET_OFFSET )
   {
      return serread( fd, buf, nbytes );
   }

   if ( ( fd < 3 ) && ( fd >= 0 ) )
   {
      int i = 0; 
      int echo = ( ioctl_flags[fd] & IOCTL_RX_ECHO ) != 0;
      int mod_crlf = ( ioctl_flags[fd] & IOCTL_RX_CHANGE_CRLF ) != 0;
      int doedit = ( ioctl_flags[fd] & IOCTL_RX_PROCESS_EDITS ) != 0;

      /* Check for an error on the Stdio mappings */
      if ( fd_map[fd] < 0 )
      {
         return -1;
      }

      for ( i = 0; i < nbytes; i++ )
      {
         /* Check for an error on the Stdio mappings */
         if ( fd_map[fd] < 0 )
         {
            return -1;
         }
         *( buf + i ) = stdioinbyte( fd );

         if ( ( echo ) &&
              !( mod_crlf &&
                 ( ioctl_flags[fd] & IOCTL_LAST_IS_RTN ) &&
                 ( *( buf + i ) == '\n' ) ) )
         {
            rawwrite( fd, ( buf + i ), 1 );
         }

         if ( ( doedit ) && ( *( buf + i ) == 8 ) )
         {
            if ( i > 0 )
            {
               rawwrite( fd, " \10", 2 );
               i -= 2;
            }
         }
         else if ( *( buf + i ) == '\r' )
         {
            if ( mod_crlf )
            {
               *( buf + i ) = '\n';
               ioctl_flags[fd] |= IOCTL_LAST_IS_RTN;
            }
            return ( i + 1 );
         }
         else
         {
            if ( ( *( buf + i ) == '\n' ) || ( *( buf + i ) == 0 ) )
            {
               if ( ioctl_flags[fd] & IOCTL_LAST_IS_RTN )
               {
                  i--;
                  ioctl_flags[fd] &= ~IOCTL_LAST_IS_RTN;
               }
               else
               {
                  return ( i + 1 );
               }
            }
            ioctl_flags[fd] &= ~IOCTL_LAST_IS_RTN;
         }
      }/*For*/
      return ( i );
   } /* if fd is ok */
   return ( 0 );
}
stdioinbyte():

Code: Select all

char stdioinbyte( int fd )
{
   char c = 0;
   if ( fd_map[fd] < 0 )
   {
      return 0;
   }
   if ( fd_map[fd] < 3 )
   {
#ifdef USE_IOTRAP
      return TrapInByte(); 
#else
      return LocalInByte();
#endif
   }
   if ( read( fd_map[fd], &c, 1 ) != 1 )
   {
      fd_map[fd] = -1;
   }
   return c;
}
My main loop has code that looks like this in it:

Code: Select all

if (charavail())
	  {
		  // Get the character and add it to our buffer
		  char c = getc(stdin);//getchar();
		  printf("->%d\r\n", (int)c);
}
Both getc() and getchar() perform the same. Everything works well until you press the enter key and the terminal transmits \r\n. Then you stop receiving anything you type because your stdin has been closed and assigned the fd of -1.

Re: IO Redirection and Dual Stdio - FileIO.CPP issue

Posted: Fri Aug 03, 2012 12:28 pm
by cfavreau
One "duh" solution I have implemented is just to run the feature off in the io handling that is causing trouble:

Code: Select all

ioctl(0, IOCTL_CLR | IOCTL_RX_CHANGE_CRLF);
That appears to work OK.