Re: Handshaking on USB serial devices

From: David Newall
Date: Thu Feb 14 2008 - 13:05:09 EST


Alan Cox wrote:
>> To make it clear: Even aside from the buffer in 2.6's pl2303.c, there's
>> a race: An in-flight write URB can fill all hardware buffers, making
>> unsafe what previously appeared to be a safe write. I think it's
>> essential to delay submission of the URB on a stop-transmit condition.
>>
>
> Hardware flow control *is* a race, and always will be. The remote end has
> a delay in signalling 'stop' there is a propogation delay and a response
> delay. This is why most implementations assert stop a bit *before* they
> run out.
>

That's a very good point. Even so: on the 2.4 driver, write_room isn't
implemented (refer to a previous message by Alan); and in 2.6, a 1k
buffer is built into the driver, with nothing to prevent it being sent
when the hardware buffer fills. These could be bugs in the two versions
of pl2303, if you like; in fact I suppose they are; but there's a wider
problem: The same weakness can be found in aircable.c, airprime.c,
cyberjack.c, cypress_m8.c (I think), empeg.c, ftdi_sio (I think),
io_ti.c, and that's where I stop checking, and declare it's widespread.

> Given the size of transfers and the internal buffering one would hope the
> USB devices do their own flow control if told to properly.
>

RS232 is (normally) so much slower than USB that, on an extended
transmission, the buffer internal to the local hardware can fill well
before the remote device has demanded that transmission stop. In fact,
now that you've mentioned it, I can't see that anything to stop the
driver from overflowing the internal buffer, which is very perplexing.
Would that be right? That seems a pretty dramatic weakness; how do you
write a large report to a slow printer without losing data?


I'm beginning to realise that overflowing the internal buffer is the
greatest risk of data loss. Hardware flow control almost seems
serendipitous. It gives us a reason to stop submitting bulk writes.

This is the essence of the change that I looking at for pl2303.c.
(Patch relative to kernel 2.6.23.14.) Does it make sense?

--- pl2303.c 2008-01-15 07:19:56.000000000 +1030
+++ pl2303.c.new 2008-02-15 04:22:12.000000000 +1030
@@ -370,6 +370,18 @@

spin_lock_irqsave(&priv->lock, flags);

+ /*
+ * there's a limit to how much we can buffer, so don't accept data while
+ * CTS or DSR are low. XXX I don't know what to do about XON/XOFF.
+ */
+ if ((priv->line_control | CONTROL_RTS && !(priv->line_status & UART_CTS)) || \
+ (priv->line_control | CONTROL_DTR && !(priv->line_status & UART_DSR))) {
+ /*
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dbg("%s: send paused by remote flow control", __FUNCTION__);
+ return;
+ }
+
if (priv->write_urb_in_use) {
spin_unlock_irqrestore(&priv->lock, flags);
return;
@@ -957,6 +969,9 @@

pl2303_update_line_status(port, data, actual_length);

+ /* send any buffered data */
+ pl2303_send(port);
+
exit:
retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/