Under linux, I'm using this code for writing:
int serial_write (serial_t fd, const void* buffer, unsigned int count)
{
int nbytes = 0;
while (nbytes < count) {
int n = write (fd, buffer + nbytes, count - nbytes);
if (n < 0)
return -1; // Error during write call.
nbytes += n;
}
return nbytes;
}
While testing (with a virtual device provided by ttypatch [1] and a real
serial port without a device attached), I noticed the write *always*
succeeds (immediately after the first write call). Is this the expected
behavior when there is nothing receiving the data at the other end? I
suppose the data is buffered somewhere (by the OS or the serial driver),
but not actually send. Because sometimes ioctl (fd, TIOCOUTQ, &bytes)
shows there is data waiting in the output queue and close (fd) takes a
long time to finish in that case. If I add tcdrain (fd) before returning
in serial_write, it never returns. I would expect write to return an
error instead of the behavior I described. Or am I doing something wrong?
[1] http://ttypatch.sourceforge.net/
If I compare with the windows version (see code below), the function
blocks until everything is written (optionally a timeout can be
configured). But if the function returns successfully, the output queue
is always empty, just as I expected.
BTW, is it possible to implement a timeout when writing data, similar to
windows? Orherwise my application would become unresponsive (when using
tcdrain) if there is no device present.
int serial_write (serial_t fd, const void* buffer, unsigned int count)
{
DWORD dwWritten = 0;
if (!WriteFile (serial, buffer, count, &dwWritten, NULL))
return -1;
return dwWritten;
}
> For one of my applications, I'm writing a small library for the
> communication with a device attached to a serial port. The library is
> very simple (open, close, configure, read, write) and is only a small
> wrapper around the platform specific (termios, win32) API. Almost
> everything works, but I'm having some problems with writing data.
>
> Under linux, I'm using this code for writing:
>
> int serial_write (serial_t fd, const void* buffer, unsigned int count)
> {
> int nbytes = 0;
> while (nbytes < count) {
> int n = write (fd, buffer + nbytes, count - nbytes);
> if (n < 0)
> return -1; // Error during write call.
>
> nbytes += n;
> }
>
> return nbytes;
> }
>
> While testing (with a virtual device provided by ttypatch [1] and a real
> serial port without a device attached), I noticed the write *always*
> succeeds (immediately after the first write call). Is this the expected
> behavior when there is nothing receiving the data at the other end? I
Yes.
> suppose the data is buffered somewhere (by the OS or the serial driver),
> but not actually send. Because sometimes ioctl (fd, TIOCOUTQ, &bytes)
That's the reason. The kernel buffers writes to most types of devices.
For example, if you try to write to a network file system, it may
succeed even when the server is down, because it simply writes to the
buffer cache, which is flushed to the server asynchronously.
> shows there is data waiting in the output queue and close (fd) takes a
> long time to finish in that case. If I add tcdrain (fd) before returning
> in serial_write, it never returns. I would expect write to return an
> error instead of the behavior I described. Or am I doing something wrong?
>
> [1] http://ttypatch.sourceforge.net/
>
> If I compare with the windows version (see code below), the function
> blocks until everything is written (optionally a timeout can be
> configured). But if the function returns successfully, the output queue
> is always empty, just as I expected.
>
> BTW, is it possible to implement a timeout when writing data, similar to
> windows? Orherwise my application would become unresponsive (when using
> tcdrain) if there is no device present.
You could put the descriptor in non-blocking mode. Then if you try to
write while the buffer is full, you'll get an error (errno ==
EWOULDBLOCK). You can then use select() to wait for the buffer to
drain, with a timeout.
>
> int serial_write (serial_t fd, const void* buffer, unsigned int count)
> {
> DWORD dwWritten = 0;
> if (!WriteFile (serial, buffer, count, &dwWritten, NULL))
> return -1;
>
> return dwWritten;
> }
--
Barry Margolin, bar...@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***
*** PLEASE don't copy me on replies, I'll read them in the group ***
> > While testing (with a virtual device provided by ttypatch [1] and a real
> > serial port without a device attached), I noticed the write *always*
> > succeeds (immediately after the first write call). Is this the expected
> > behavior when there is nothing receiving the data at the other end? I
> Yes.
> > suppose the data is buffered somewhere (by the OS or the serial driver),
> > but not actually send. Because sometimes ioctl (fd, TIOCOUTQ, &bytes)
> That's the reason. The kernel buffers writes to most types of devices.
> For example, if you try to write to a network file system, it may
> succeed even when the server is down, because it simply writes to the
> buffer cache, which is flushed to the server asynchronously.
If there's no hardware flow control, you can keep writing forever even
if there is no device connected. The data will continue to be output
on the serial port's output lines and there will be no way to tell
that nothing is listening.
DS
> Barry Margolin wrote:
> > In article <fcbmqu$hkq$1...@ikaria.belnet.be>,
> > Jef Driesen <jefdr...@hotmail.com.invalid> wrote:
> >
> >> For one of my applications, I'm writing a small library for the
> >> communication with a device attached to a serial port. The library is
> >> very simple (open, close, configure, read, write) and is only a small
> >> wrapper around the platform specific (termios, win32) API. Almost
> >> everything works, but I'm having some problems with writing data.
> >>
> >> Under linux, I'm using this code for writing:
> >>
> >> int serial_write (serial_t fd, const void* buffer, unsigned int count)
> >> {
> >> int nbytes = 0;
> >> while (nbytes < count) {
> >> int n = write (fd, buffer + nbytes, count - nbytes);
> >> if (n < 0)
> >> return -1; // Error during write call.
> >>
> >> nbytes += n;
> >> }
> >>
> >> return nbytes;
> >> }
> >>
> >> While testing (with a virtual device provided by ttypatch [1] and a real
> >> serial port without a device attached), I noticed the write *always*
> >> succeeds (immediately after the first write call). Is this the expected
> >> behavior when there is nothing receiving the data at the other end?
> >
> > Yes.
>
> Does that also mean, the while loop is not necessary? I have a similar
> loop for reading, because I use select and a non-blocking read (by
> setting both termios cc[VMIN] and cc[VTIME] to zero, but not using
> O_NONBLOCK) to implement a timeout [1]. But if I understand it
> correctly, this loop is not required for a normal blocking write?
>
> [1] See my topic "Recalculate timeout after select() when reading from
> serial port?" from 7 Sep 2007.
> http://groups.google.be/group/comp.unix.programmer/browse_thread/thread/4bf551
> 5bf7ba577c/ac12dc4f9f6827ae
>
>
> >> I suppose the data is buffered somewhere (by the OS or the serial driver),
> >> but not actually send.
> >
> > That's the reason. The kernel buffers writes to most types of devices.
> > For example, if you try to write to a network file system, it may
> > succeed even when the server is down, because it simply writes to the
> > buffer cache, which is flushed to the server asynchronously.
>
> But how do I detect errors if the data is always send asynchronously and
> write does not return an error? Or is this something I don't need to
> worry about?
What kind of error can occur on a serial port?
If it's a dialup line and the modem hangs up, you'll get an error when
you try to write something after that. But if it was connected when you
called write(), the call will succeed and put the data in the buffer.
While it's possible that the line could hang up between the time your
data is buffered and when it actually gets sent, this is usually such a
short time that no one worries about it. You'll get an error the next
time you write().
>
> >> Because sometimes ioctl (fd, TIOCOUTQ, &bytes)
> >> shows there is data waiting in the output queue and close (fd) takes a
> >> long time to finish in that case. If I add tcdrain (fd) before returning
> >> in serial_write, it never returns. I would expect write to return an
> >> error instead of the behavior I described. Or am I doing something wrong?
> >>
> >> [1] http://ttypatch.sourceforge.net/
> >>
> >> If I compare with the windows version (see code below), the function
> >> blocks until everything is written (optionally a timeout can be
> >> configured). But if the function returns successfully, the output queue
> >> is always empty, just as I expected.
> >>
> >> BTW, is it possible to implement a timeout when writing data, similar to
> >> windows? Orherwise my application would become unresponsive (when using
> >> tcdrain) if there is no device present.
> >
> > You could put the descriptor in non-blocking mode. Then if you try to
> > write while the buffer is full, you'll get an error (errno ==
> > EWOULDBLOCK). You can then use select() to wait for the buffer to
> > drain, with a timeout.
>
> The non-blocking mode would only make a difference when the buffer is
> full, right? Because when there is enough space left, even a blocking
> write does not block. How large is this buffer?
Implementation dependent.
>
> I suppose the implementation of the timeouts with select is the same as
> I did for reading (see simplified code below), or do I have to try
> writing first before the select call?
No, you don't. select() tells you whether or not a call to write would
have blocked. Your pseudo-code is right.
>
> int serial_write (int fd, const void* buffer, unsigned int count)
> int nbytes = 0;
> while (nbytes < count) {
> int rc = select (fd + 1, NULL, &fds, NULL, &tv);
> if (rc < 0) {
> return -1; // Error during select call.
> } else if (rc == 0)
> break; // Timeout.
>
> int n = write (fd, buffer + nbytes, count - nbytes);
> if (n < 0) {
> return -1; // Error during write call.
>
> nbytes += n;
> }
> return nbytes;
> }
--
If the data is send asynchronously from the kernel buffer, does a read
(at a later time) waits until all the data is transmitted? I'm asking
because the communication protocol consist of sending commands and
reading the reply. But if the command is not entirely send to device at
the time I try to read the reply, the device doesn't know yet it has to
send its reply. Does the kernel take care of this or is this why there
is the tcdrain () function?
I certainly hope not. Doing that on a busy serial line doing PPP
networking would kill throughput and probably result in buffer
overflows. It might also play havoc with someone trying to type
quickly using a screen-oriented editor like vi or emacs.
>because the communication protocol consist of sending commands and
>reading the reply. But if the command is not entirely send to device at
>the time I try to read the reply, the device doesn't know yet it has to
>send its reply.
So? The device can send its reply after it DOES know it has to
send one. If this is a blocking read, the program will wait. If
it's a non-blocking read, the program have to try again, which will
likely happen anyway because the device does not reply instantaneously
(and even if it did, serial ports aren't instantaneous).
>Does the kernel take care of this or is this why there
>is the tcdrain () function?
I don't understand why you have to wait for the command to be sent
before waiting for the reply, instead of just waiting for the reply?
If you are worried about ignoring the echo of the command you sent,
(does the device echo commands you send it?) you still have to worry
about it anyway. The last couple of characters of the echoed command
will be received after the command has been sent anyway.
A read() will wait until there's something to read. If the data hasn't
been transmitted, the device won't reply, so there won't be anything to
read, and read() will block. When the kernel finishes transmitting
everything the device will reply, and read() will then return the reply.
The main problem is I don't have much experience with serial
communication, so I'm probably seeing problems where there are none. But
I'm starting to understand the differences with normal i/o now.
> If you are worried about ignoring the echo of the command you sent,
> (does the device echo commands you send it?) you still have to worry
> about it anyway. The last couple of characters of the echoed command
> will be received after the command has been sent anyway.
Some of the devices send an echo, but not all of them.