Unix Programming - serial: flushing device buffer - am I close?

This is Interesting: Free IT Magazines  
Home > Archive > Unix Programming > June 2004 > serial: flushing device buffer - am I close?





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author serial: flushing device buffer - am I close?
TJ

2004-06-26, 10:11 am

I'm writing a program in C that communicates with a serial device. I have a
conceptual question to clarify my interpretation of the HOWTOs...

The specs of my device, are that it responds with a fixed number of bytes
per issued command (typically 6 bytes). Sometimes, after an abend and
restart, the device's response will only be two bytes, and then my program
hangs while waiting for the remaining 4 bytes. My thought is that those
two bytes in the device's buffer are residual from the last run of the
program, assuming abnormal termination.

I changed my approach of "always wait for 6 bytes" to "read bytes until the
buffer is empty" - certainly a better approach. I also decided to flush
the buffer on the device by performing a read before any commands are
issued.

But now that I am reading the buffer until empty, it always appears to be
empty... perhaps because data has not arrived yet?? I'm sure there is a
simple technique, used millions of times, and I'm not quite finding it. My
loop will wait until data arrives, but the read() always returns zero, so
there never appears to be data. If I ignore the loop, and just ask for 6
bytes, sometimes I get all of them, sometimes I dont. Tricky.

From the manpage on read():
On success, the number of bytes read is returned (zero indicates end of
file), and the file position is advanced by this number. It is not an error
if this number is smaller than the number of bytes requested; this may
happen for example because fewer bytes are actually available right now
(maybe because we were close to end-of-file, or because we are reading from
a pipe, or from a terminal), or because read() was interrupted by a signal.
On error, -1 is returned, and errno is set appropriately. In this case it
is left unspecified whether the file position (if any) changes.

So possibly its my configuration/code:
Linux 2.6 , GCC 3.3.3 (SuSE 9.1)

Notes:
1. I set a fixed size for 128 since my device never exceeds 72 bytes in or
out.
2. The result bytes do not always come at once, they have to be
concatenated as they arrive.

dpDevWIDGET = open(strDevWIDGET, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (dpDevWIDGET == -1){
intUTILS_PrintMsg(7, "WIDGET", "Port not opened");
}else{
intLRC = tcgetattr(dpDevWIDGET, &tattr);
intLRC = cfsetispeed(&tattr, B1200);
intLRC = cfsetospeed(&tattr, B1200);
intLRC = tcsetattr(dpDevWIDGET, TCSAFLUSH, &tattr);
tattr.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ISIG);
/* reset the control characters */
for (i=0; i<NCCS; i++) {
tattr.c_cc[i] = _POSIX_VDISABLE;
}
}

// expect 6 bytes, but they dont always arrive at once, so concatenate them
as they arrive from multiple reads. Use the same routine to flush the
device buffer before any commands are issued to the device that get a
response...

while(blnEOF == FALSE){
intSize = read(dpDevWIDGET, inbuffer, 128);
intTmpBufferTotalSize += intSize;
if (intSize < 0){
perror("WIDGET");
blnEOF = TRUE;
return(FAILURE);
break;
}else if (intSize == 0){
printf("WIDGET:\tBuffer empty\n");
blnEOF = TRUE;
break;
}else if (intSize > 0){
for (intInBufferPos = 0; intInBufferPos < intSize; intInBufferPos++){
tmpbuffer[intTmpBufferPos++] = inbuffer[intInBufferPos];
}
}
}

Robert Harris

2004-06-26, 10:11 am

TJ wrote:
> I'm writing a program in C that communicates with a serial device. I have a
> conceptual question to clarify my interpretation of the HOWTOs...
>
> The specs of my device, are that it responds with a fixed number of bytes
> per issued command (typically 6 bytes). Sometimes, after an abend and
> restart, the device's response will only be two bytes, and then my program
> hangs while waiting for the remaining 4 bytes. My thought is that those
> two bytes in the device's buffer are residual from the last run of the
> program, assuming abnormal termination.
>
> I changed my approach of "always wait for 6 bytes" to "read bytes until the
> buffer is empty" - certainly a better approach. I also decided to flush
> the buffer on the device by performing a read before any commands are
> issued.
>
> But now that I am reading the buffer until empty, it always appears to be
> empty... perhaps because data has not arrived yet?? I'm sure there is a
> simple technique, used millions of times, and I'm not quite finding it. My
> loop will wait until data arrives, but the read() always returns zero, so
> there never appears to be data. If I ignore the loop, and just ask for 6
> bytes, sometimes I get all of them, sometimes I dont. Tricky.
>
> From the manpage on read():
> On success, the number of bytes read is returned (zero indicates end of
> file), and the file position is advanced by this number. It is not an error
> if this number is smaller than the number of bytes requested; this may
> happen for example because fewer bytes are actually available right now
> (maybe because we were close to end-of-file, or because we are reading from
> a pipe, or from a terminal), or because read() was interrupted by a signal.
> On error, -1 is returned, and errno is set appropriately. In this case it
> is left unspecified whether the file position (if any) changes.
>
> So possibly its my configuration/code:
> Linux 2.6 , GCC 3.3.3 (SuSE 9.1)
>
> Notes:
> 1. I set a fixed size for 128 since my device never exceeds 72 bytes in or
> out.
> 2. The result bytes do not always come at once, they have to be
> concatenated as they arrive.
>
> dpDevWIDGET = open(strDevWIDGET, O_RDWR | O_NOCTTY | O_NONBLOCK);

You probably don't want O_NONBLOCK, otherwise you'll be doing a lot of
polling.
> if (dpDevWIDGET == -1){
> intUTILS_PrintMsg(7, "WIDGET", "Port not opened");
> }else{
> intLRC = tcgetattr(dpDevWIDGET, &tattr);
> intLRC = cfsetispeed(&tattr, B1200);
> intLRC = cfsetospeed(&tattr, B1200);
> intLRC = tcsetattr(dpDevWIDGET, TCSAFLUSH, &tattr);

Call this after you've finished with tattr
> tattr.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ISIG);


> /* reset the control characters */
> for (i=0; i<NCCS; i++) {
> tattr.c_cc[i] = _POSIX_VDISABLE;
> }

Those don't apply if ICANON is not set. Instead you need to set:

tattr.c_cc[VMIN] and tattr.c_cc[VTIME]

You probably want your read()s to return after, say, half a second
whether or not you have read in any characters. In which case set:

tattr.c_cc[VMIN] = 0;
tattr.c_cc[VTIME] = 5;

man termios should tell you the whole story.

Robert
Joerg Schmitz-Linneweber

2004-06-26, 10:11 am

Hi TJ,

I don't really see the point...
You get what you asked (the library) for. But let's have a look:

TJ wrote:
> I'm writing a program in C that communicates with a serial device. I have
> a conceptual question to clarify my interpretation of the HOWTOs...
>
> The specs of my device, are that it responds with a fixed number of bytes
> per issued command (typically 6 bytes). Sometimes, after an abend and
> restart, the device's response will only be two bytes, and then my program
> hangs while waiting for the remaining 4 bytes. My thought is that those
> two bytes in the device's buffer are residual from the last run of the
> program, assuming abnormal termination.

OK. If you don't have fixed input record length', you'll need some kind of
"frame syncing" however this will be done (start character, timeout, ...)

> I changed my approach of "always wait for 6 bytes" to "read bytes until
> the
> buffer is empty" - certainly a better approach. I also decided to flush
> the buffer on the device by performing a read before any commands are
> issued.

Which buffer do you flush? (input or output)
But you know that the buffer might be empty although vou don't have an
actual record yet? (Your reads may be faster then the device delivers
data...)

> But now that I am reading the buffer until empty, it always appears to be
> empty... perhaps because data has not arrived yet?? I'm sure there is a
> simple technique, used millions of times, and I'm not quite finding it.
> My loop will wait until data arrives, but the read() always returns zero,
> so
> there never appears to be data. If I ignore the loop, and just ask for 6
> bytes, sometimes I get all of them, sometimes I dont. Tricky.

Not so tricky... Of course, most of the time the buffer will be empty since
your CPU is much faster then the device delivers data

> From the manpage on read():
> On success, the number of bytes read is returned (zero indicates end of
> file), and the file position is advanced by this number. It is not an
> error if this number is smaller than the number of bytes requested; this
> may happen for example because fewer bytes are actually available right
> now (maybe because we were close to end-of-file, or because we are reading
> from a pipe, or from a terminal), or because read() was interrupted by a
> signal. On error, -1 is returned, and errno is set appropriately. In this
> case it is left unspecified whether the file position (if any) changes.

Right.

> So possibly its my configuration/code:
> Linux 2.6 , GCC 3.3.3 (SuSE 9.1)
>
> Notes:
> 1. I set a fixed size for 128 since my device never exceeds 72 bytes in
> or out.
> 2. The result bytes do not always come at once, they have to be
> concatenated as they arrive.
>
> dpDevWIDGET = open(strDevWIDGET, O_RDWR | O_NOCTTY | O_NONBLOCK);

Why do you use NON_BLOCKING operation? Is this only pseudocode or are you
perfomring other things while your reads return zero?
Why not try to read one byte in blocking mode? The read will return you one
byte if it is available (or it may return 0 if there was a signal
(interrupt) or even bail out with < 0)

> if (dpDevWIDGET == -1){
> intUTILS_PrintMsg(7, "WIDGET", "Port not opened");
> }else{
> intLRC = tcgetattr(dpDevWIDGET, &tattr);
> intLRC = cfsetispeed(&tattr, B1200);
> intLRC = cfsetospeed(&tattr, B1200);
> intLRC = tcsetattr(dpDevWIDGET, TCSAFLUSH, &tattr);
> tattr.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ISIG);
> /* reset the control characters */
> for (i=0; i<NCCS; i++) {
> tattr.c_cc[i] = _POSIX_VDISABLE;
> }
> }

Can't say much about the settings of the line discipline... (anyone?)

> // expect 6 bytes, but they dont always arrive at once, so concatenate
> them
> as they arrive from multiple reads. Use the same routine to flush the
> device buffer before any commands are issued to the device that get a
> response...
>
> while(blnEOF == FALSE){
> intSize = read(dpDevWIDGET, inbuffer, 128);
> intTmpBufferTotalSize += intSize;
> if (intSize < 0){
> perror("WIDGET");
> blnEOF = TRUE;
> return(FAILURE);
> break;
> }else if (intSize == 0){
> printf("WIDGET:\tBuffer empty\n");
> blnEOF = TRUE;
> break;
> }else if (intSize > 0){
> for (intInBufferPos = 0; intInBufferPos < intSize;
> intInBufferPos++){
> tmpbuffer[intTmpBufferPos++] =
> inbuffer[intInBufferPos];
> }
> }
> }

OK. See above for using blocking reads eventually. But be aware that a
possible scenario for your reads may be: (You are expecting to get 6 bytes)
[Assuming that at time 'step00' you are in-sync (i.e. at start-of-frame)]
step00: read () returns 0
step01: read () returns 0
step02: read () returns 0
step03: read () returns 2
step04: read () returns 0
step05: read () returns 1
step06: read () returns 0
step07: read () returns 0
step08: read () returns 4
step09: read () returns 6
step10: read () returns 0
step11: read () returns 0
step12: read () returns 0
step13: read () returns 0
Let' see: after step03 you've got data but not enough. Don't discard it!
After step05 the same. After step08 there is _too much_ data! So now you've
to deal with "one and a half" frame... And so on. But most of the time
there is no data waiting!

HTH. Salut, Jörg

--
gpg/pgp key # 0xe40a9d7a
fingerprint d4f8 b448 835b 7bcf 4161 ce35 7e8b ab47 e40a 9d7a
tj

2004-06-26, 10:11 am

Robert Harris wrote:

> You probably don't want O_NONBLOCK, otherwise you'll be doing a lot of
> polling.


Yes, I agree with that... without O_NONBLOCK I experience the problem: While
trying to obtain a frame of 6 bytes, the read() hangs - since not all 6
bytes arrived in the first frame. The vendor says that they dont experience
that behavior, regardless of synch or asynch reads on the device. Hmmmm...
so I think I'm missing something - perhaps I should be building a frame
from 6 successive synchronous reads, including the time delay you suggest
below?

> Call this after you've finished with tattr

I think you mean flush it after all settings are made, before I perform
device I/O? Or do mean set those flags at another point in the sequence?

[vbcol=seagreen]
> You probably want your read()s to return after, say, half a second
> whether or not you have read in any characters. In which case set:
>
> tattr.c_cc[VMIN] = 0;
> tattr.c_cc[VTIME] = 5;
>
> man termios should tell you the whole story.


Yes, I've read it... the haze is beginning to burn off... I think if I had a
recommend approach, then I could figure out how to adjust my settings for
termios, read, and open.

TIA,
--
TJ
Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com