more about serial ports: do they even work?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



After some debugging and debugging, with a help
Hollis Blanchard on #kvm@freenode, I discovered
that kvm (or, rather, qemu) does not work correctly
with serial ports, at least on linux.  One problem
report has already here, author Cc'd -- see e.g.
http://marc.info/?l=kvm&m=122995568009533&w=2 .

Here's what's going on.

When opening a host's port, kvm resets the status
lines, doing this:

ioctl(13, TIOCMGET, [TIOCM_DTR|TIOCM_RTS|TIOCM_CTS|TIOCM_DSR|0x4000])
ioctl(13, TIOCMSET, [TIOCM_DTR|TIOCM_RTS])

which results in the following set

ioctl(13, TIOCMGET, [TIOCM_DTR|TIOCM_RTS|TIOCM_CTS|TIOCM_DSR])

Note the difference between the default set and new one: the
missing bit, 0x4000, which is unknown to strace(1) but is defined
as TIOCM_OUT2 in linux headers.

After that change (resetting the TIOCM_OUT2 bit), no writes
to the serial port works anymore, they're all gets accepted
by host kernel and are buffered in the kernel.

After some time, when the kernel buffer fills up, and since
the port (on host side) is opened in non-blocking mode, the
writes starts returning EAGAIN, and kvm process starts
endless loop, which were seen by David.

Here's the trivial program to demonstrate the idea:

---- cut ---
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>

int main(int argc, char **argv) {
  fd = open("/dev/ttyS0", O_RDWR|O_NONBLOCK);
  fcntl(fd, F_SETFL, O_RDWR);
  x = TIOCM_DTR|TIOCM_RTS|TIOCM_CTS|TIOCM_DSR // |0x4000
  ;
  ioctl(fd, TIOCMSET, &x);
  ioctl(fd, TIOCMGET, &x);
  write(fd, "at\r", 3);
  read(fd, buf, 20);
  close(fd);

  return 0;
}
--- cut ---


Run it under strace while a dialup modem is connected to /dev/ttyS0
(i used this way for testing).  It will stuck at read, and nothing
will be written, even when write() will happily return 3.  Un-comment
the |0x4000 thing, and it will work.

I'm not sure what should be done with this, and how much this is
linux-specific.  But it is obvious that bit (TIOCM_OUT2) should
be left in-place (after which the thing works), at least on linux.

Note that this bit is NOT shown in /proc/tty/driver/serial file
(which shows other bits).

Note also that this file (/proc/tty/driver/serial) helps to see
if any write were performed: compare the counters.  In 'tx'
there's number of bytes actually sent to device, as opposed to
accepted by the kernel.  When you write something to /dev/ttyS0,
that number increases, IF that something actually reached the
device.

Thanks.

/mjt
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux