[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20070727150854.GA25935@flint.arm.linux.org.uk>
Date: Fri, 27 Jul 2007 16:08:54 +0100
From: Russell King <rmk+lkml@....linux.org.uk>
To: Linux Kernel List <linux-kernel@...r.kernel.org>
Subject: Fwd: PROBLEM: serial port receive buffers not being flushed properly
Please reply to the original poster.
----- Forwarded message from craigeger@...cast.net -----
From: craigeger@...cast.net
To: rmk+serial@....linux.org.uk
Subject: PROBLEM: serial port receive buffers not being flushed properly
Date: Fri, 27 Jul 2007 14:51:00 +0000
See attached .txt and .c files.
Use that information to fill in all fields of the bug report form, and
post it to the mailing list with a subject of "PROBLEM: <one line
summary from [1.]>" for easy identification by the developers.
[1.] PROBLEM: serial port receive buffers not being flushed properly
[2.] After calling tcflush() or ioctl() with the TCFLSH argument, an ioctl()
with the FIONREAD argument reports 0 bytes available for reading.
However, a subsequent select() and read() of the port returns data.
This problem is seen in 2.6.21.1 and 2.6.20, but is not seen in
version 2.6.17.14. Other versions have not been tested.
[3.] keywords: serial buffer flush
[4.] Kernel version (from /proc/version): 2.6.21.1
[5.] Most recent kernel version which did not have the bug: 2.6.17.14 (others between 2.6.17.14 and 2.6.20 not checked)
[6.] No OOPs message applicable.
[7.] See attached source file com_buffer_test.c
[8.] Environment
[8.1.] Linux Pohaku 2.6.21.1 #1 PREEMPT Thu Jul 12 14:43:22 CDT 2007 i486 GNU/Linux
Gnu C 3.3.5
Gnu make 3.80
binutils 2.15
util-linux 2.12p
mount 2.12p
module-init-tools 3.2-pre1
e2fsprogs 1.37
pcmcia-cs 3.2.5
Linux C Library 2.3.2
Dynamic linker (ldd) 2.3.2
Procps 3.2.1
Net-tools 1.60
Console-tools 0.2.3
Sh-utils 5.2.1
udev 056
Modules Loaded ipv6 e100 mii genrtc ext3 jbd ide_disk ide_generic ns87415 piix siimage sc1200 generic cy82c693 cs5530 sis5513 via82cxxx rz1000 slc90e66 cmd64x pdc202xx_old hpt34x opti621 trm290 amd74xx hpt366 serverworks atiixp aec62xx triflex alim15x3 pdc202xx_new cs5520 ide_core unix
[8.2.] Processor information (from /proc/cpuinfo):
processor : 0
vendor_id : AuthenticAMD
cpu family : 4
model : 9
model name : 486 DX/4-WB
stepping : 4
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 1
wp : yes
flags : fpu
bogomips : 49.40
clflush size : 32
[8.3.] Module information (from /proc/modules):
ipv6 234848 10 - Live 0xc4c44000
e100 31632 0 - Live 0xc4bbb000
mii 5152 1 e100, Live 0xc4b83000
genrtc 9420 0 - Live 0xc4b99000
ext3 120392 1 - Live 0xc4bdf000
jbd 55976 1 ext3, Live 0xc4bac000
ide_disk 15808 2 - Live 0xc4b94000
ide_generic 1152 0 [permanent], Live 0xc4b81000
ns87415 3956 0 [permanent], Live 0xc487d000
piix 9764 0 [permanent], Live 0xc4b8a000
siimage 10912 0 [permanent], Live 0xc4b86000
sc1200 5152 0 [permanent], Live 0xc487a000
generic 4744 0 [permanent], Live 0xc4872000
cy82c693 4292 0 [permanent], Live 0xc486f000
cs5530 5056 0 [permanent], Live 0xc4867000
sis5513 14568 0 [permanent], Live 0xc4875000
via82cxxx 8676 0 [permanent], Live 0xc486b000
rz1000 2560 0 [permanent], Live 0xc482b000
slc90e66 5344 0 [permanent], Live 0xc4864000
cmd64x 10012 0 [permanent], Live 0xc4837000
pdc202xx_old 9408 0 [permanent], Live 0xc4860000
hpt34x 4672 0 [permanent], Live 0xc4834000
opti621 4228 0 [permanent], Live 0xc4831000
trm290 3748 0 [permanent], Live 0xc4829000
amd74xx 13308 0 [permanent], Live 0xc485b000
hpt366 15808 0 [permanent], Live 0xc4856000
serverworks 8264 0 [permanent], Live 0xc482d000
atiixp 6064 0 [permanent], Live 0xc4826000
aec62xx 7200 0 [permanent], Live 0xc4812000
triflex 3584 0 [permanent], Live 0xc481d000
alim15x3 11276 0 [permanent], Live 0xc4822000
pdc202xx_new 7744 0 [permanent], Live 0xc480c000
cs5520 4448 0 [permanent], Live 0xc480f000
ide_core 103204 27 ide_disk,ide_generic,ns87415,piix,siimage,sc1200,generic,cy82c693,cs5530,sis5513,via82cxxx,rz1000,slc90e66,cmd64x,pdc202xx_old,hpt34x,opti621,trm290,amd74xx,hpt366,serverworks,atiixp,aec62xx,triflex,alim15x3,pdc202xx_new,cs5520, Live 0xc483b000
unix 25808 2 - Live 0xc4815000
[8.4.] Loaded driver and hardware information (/proc/ioports, /proc/iomem)
0000-001f : dma1
0020-0021 : pic1
0040-0043 : timer0
0050-0053 : timer1
0060-006f : keyboard
0080-008f : dma page reg
00a0-00a1 : pic2
00c0-00df : dma2
00f0-00ff : fpu
01f0-01f7 : ide0
02e8-02ef : serial
02f8-02ff : serial
03c0-03df : vesafb
03e8-03ef : serial
03f6-03f6 : ide0
03f8-03ff : serial
0cf8-0cff : PCI conf1
f900-f9ff : 0000:00:11.0
fd00-fd3f : 0000:00:10.0
fd00-fd3f : e100
00000000-0009e3ff : System RAM
0009e400-0009ffff : reserved
000a0000-000bffff : Video RAM area
000c0000-000cffff : Video ROM
000f0000-000fffff : System ROM
00100000-03ffffff : System RAM
00100000-0026df65 : Kernel code
0026df66-00321b2f : Kernel data
c0000000-c0000fff : 0000:00:10.0
c0000000-c0000fff : e100
c0020000-c003ffff : 0000:00:10.0
c0020000-c003ffff : e100
c1000000-c1ffffff : 0000:00:11.0
c1000000-c17effff : vesafb
c2000000-c2000fff : 0000:00:11.0
ff000000-ff01ffff : 0000:00:11.0
fff00000-ffffffff : reserved
[8.5.] PCI information ('lspci -vvv' as root)
[8.6.] SCSI information (from /proc/scsi/scsi)
[8.7.] Other information that might be relevant to the problem
(please look in /proc and include all information that you
think to be relevant):
[X.] Other notes, patches, fixes, workarounds:
After sending several kbytes to the com port, an ioctl() with FIONREAD
reports 4095 bytes available under 2.6.20 and 2.6.21.1. However, it
reports 4096 bytes available under 2.6.17.14, and flushing works as
expected with this version. Please see the comments in the attached
source code for more details on the problem.
Thank you
/***************************************************************************
* NAME: com_buffer_test.c
*
* PURPOSE: Used to debug/demonstrate an issue with the flushing of
* of serial port buffers under Linux. It appears that
* tcflush(), or alternatively, an ioctl() with the TCFLSH
* argument only flushes 4095 bytes of buffered data at the
* com port. See comments in the code for further details.
***************************************************************************/
#define COM1 "/dev/ttyS0"
#define COM2 "/dev/ttyS1"
#define COM3 "/dev/ttyS2"
#define COM4 "/dev/ttyS3"
#define COM_PORT COM2
#define BAUD_RATE B115200
#define DATA_BUF_SIZE 10000
#include <stdio.h> // printf, getchar
#include <fcntl.h> // open, fcntl
#include <unistd.h> // close, read
#include <sys/ioctl.h> // ioctl
#include <termios.h> // termios struct
int main( void )
{
int configure_com( int fd );
unsigned char data[DATA_BUF_SIZE] = { 0 };
int ret_val = 0;
int cp_fd = 0;
int byte_count = 0;
int total_bytes = 0;
struct timeval timeout;
fd_set input;
// Build the fd_set and set the timeout value used by select()
FD_ZERO( &input ); // Clear the fd_set
FD_SET( cp_fd, &input ); // The COM fd
timeout.tv_sec = 5;
timeout.tv_usec = 0;
// Open the com port device file.
printf( "Opening device file.\n" );
if( (cp_fd = open( COM_PORT, O_RDWR | O_NOCTTY | O_NONBLOCK )) == -1 )
{
printf( "Unable to open device file %s.\n", COM_PORT );
return( cp_fd );
} // if( (cp_fd = open( COM_PORT, O_RDWR | O_NOCTTY | O_NONBLOCK )) == -1 )
// Setup the COM port.
printf( "Configuring COM port.\n" );
if( (ret_val = configure_com( cp_fd )) != 0 )
{
printf( "Failure configuring COM port.\n" );
close( cp_fd );
return( ret_val );
} // if( (ret_val = configure_com( cp_fd )) != 0 )
// Prompt the user to send data to the port. At this point, use another program
// to send more than 4095 bytes of data to our com port. I typically send 100kb
// or so.
printf( "\n\nSend (a lot of) data now. When the send is complete, hit 'Enter'.\n" );
while( (ret_val = getchar()) != '\n' );
// Now, we're going to "flush" the com port buffer, and then query it for
// the number of bytes available for reading after we flush it. In previous
// tests, the 2nd query has reported 0 bytes available. However, a subsequent
// read (even though we've stopped sending data) will return more data! It
// should also be noted that a delay after the flush and before the 2nd query
// will report more data available. It appears that a maximum of 4095 bytes
// can be flushed per ioctl call.
printf( "About to flush the buffer.\n" );
if( (ret_val = ioctl( cp_fd, FIONREAD, &byte_count )) != 0 ) // Get number of bytes available.
{
printf( "Failure calling ioctl with FIONREAD argument.\n" );
close( cp_fd );
return( ret_val );
} // if( (ret_val = ioctl( cp_fd, FIONREAD, &byte_count )) != 0 )
while( byte_count > 0 )
{
// Note that this is 4095 bytes with versions 2.6.21.1 and 2.6.20,
// but is 4096 bytes with version 2.6.17.14. Not sure if this is
// related to the flush issue, but seemed worth noting.
printf( "Bytes available before flush: %i\n", byte_count );
ioctl( cp_fd, TCFLSH, TCIFLUSH );
if( (ret_val = ioctl( cp_fd, TCFLSH, TCIFLUSH )) != 0 ) // Perform flush.
{
printf( "Failure calling ioctl with TCFLSH argument.\n" );
close( cp_fd );
return( ret_val );
} // if( (ret_val = ioctl( cp_fd, TCFLSH, TCIFLUSH )) != 0 )
// If we sleep for a bit here, the results of the next query will
// be > 0. If we don't sleep, the results will be 0.
//usleep( 100000 );
if( (ret_val = ioctl( cp_fd, FIONREAD, &byte_count )) != 0 ) // Get number of bytes available.
{
printf( "Failure calling ioctl with FIONREAD argument.\n" );
close( cp_fd );
return( ret_val );
} // if( (ret_val = ioctl( cp_fd, FIONREAD, &byte_count )) != 0 )
printf( "Bytes available after flush: %i\n", byte_count );
}
printf( "Calling select(). Since we've already flushed the buffer, there\n" );
printf( "should be NO MORE DATA available at the port!\n\n" );
printf( "If nothing is available, select() will timeout after 5 seconds.\n" );
// Build the fd set.
FD_ZERO( &input ); // Clear the fd_set
FD_SET( cp_fd, &input ); // The COM fd
// Wait for up to 5 seconds for input with select. We would expect this to just
// timeout, because there should be no more data available.
if( (ret_val = select( cp_fd + 1, &input, NULL, NULL, &timeout )) < 0 )
{
printf( "Error on select().\n" );
close( cp_fd );
return( ret_val );
} // if( (ret_val = select( cp_fd + 1, &input, NULL, NULL, NULL )) < 0 )
else if( ret_val == 0 ) // Timeout. This is what should happen.
{
printf( "select() timed out. Nothing available at the port.\n" );
} // else if( ret_val == 0 )
// Data at the com port! Shouldn't be there, as we've "flushed" the buffer!
// Note also that even though we're asking to read up to 10000 bytes per
// read call, we can apparently only read 4095 bytes at a time.
else if( FD_ISSET( cp_fd, &input ) )
{
printf( "select() triggered for COM port.\n" );
printf( "This data should have been flushed already!\n" );
ioctl( cp_fd, FIONREAD, &byte_count );
printf( "Bytes available at port: %i\n", byte_count );
while( (byte_count = read( cp_fd, data, DATA_BUF_SIZE )) > 0 )
{
printf( "Read %i bytes at COM port.\n", byte_count );
total_bytes += byte_count;
}
printf( "Read %i bytes, AFTER flushing the buffer.\n", total_bytes );
} // else if( FD_ISSET( cp_fd, &input ) )
// Close device file.
close( cp_fd );
return( 0 );
} // int main( void )
/////////////////////////////////////////////////////////////////////////////
// configure_com()
// Takes a file descriptor for the COM port and configures it appropriately.
// Flushes the COM port buffer before returning. Returns 0 on
// success. If an error occurs, returns -1.
/////////////////////////////////////////////////////////////////////////////
int configure_com( int fd )
{
struct termios options;
int ret_val = 0;
// Get attributes.
if( (ret_val = tcgetattr( fd, &options )) == -1 )
{
printf( "configure_com: Unable to obtain COM port attributes.\n" );
return( ret_val );
} // if( (ret_val = tcgetattr( fd, &options )) == -1 )
// Set input baud rate.
if( (ret_val = cfsetispeed( &options, BAUD_RATE )) == -1 )
{
printf( "configure_com: Unable to set COM port input baud rate.\n" );
return( ret_val );
} // if( (ret_val = cfsetispeed( &options, BAUD_RATE )) == -1 )
// Set output baud rate.
if( (ret_val = cfsetospeed( &options, BAUD_RATE )) == -1 )
{
printf( "configure_com: Unable to set COM port output baud rate.\n" );
return( ret_val );
} // if( (ret_val = cfsetospeed( &options, BAUD_RATE )) == -1 )
// Set up for 8N1, 8 data bits, no parity, 1 stop bit. Disable hardware flow
// control. Ignore control signals, and enable the port for reading.
options.c_cflag &= ~(PARENB | CSTOPB | CSIZE | CRTSCTS);
options.c_cflag |= (CS8 | CLOCAL | CREAD);
// Set up for raw (non-canonical) input, disable echoing and signalling.
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// Disable software flow control
options.c_iflag &= ~(IXON | IXOFF | IXANY);
// Set up for raw output.
options.c_oflag &= ~OPOST;
// Set the updated attributes, flushing the port first.
if( (ret_val = tcsetattr( fd, TCSAFLUSH, &options )) == -1 )
{
printf( "configure_com: Unable to set COM port attributes.\n" );
return( ret_val );
} // if( (ret_val = tcsetattr( fd, TCSAFLUSH, &options )) == -1 )
// Ensure we're setup for non-blocking operations. If open was used
// with O_NONBLOCK or O_NDELAY, this will be the case anyway. This fcntl
// call will make sure of this.
if( (ret_val = fcntl( fd, F_SETFL, FNDELAY )) == -1 )
{
printf( "configure_com: Unable to configure COM port for non-blocking operations.\n" );
return( ret_val );
} // if( (ret_val = fcntl( fd, F_SETFL, FNDELAY )) == -1 )
return( ret_val );
} // static int configure_com( int cp_fd )
----- End forwarded message -----
--
Russell King
Linux kernel 2.6 ARM Linux - http://www.arm.linux.org.uk/
maintainer of:
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists