Commit 66d2dc06 authored by Al Borchers's avatar Al Borchers Committed by Greg Kroah-Hartman

[PATCH] USB: close waits for drain in pl2303

Here is an additional patch for pl2303 in 2.6.9-rc1,
to be applied after my previous circular buffer patch.
This patch waits for the buffer to drain on close and
then clears the buffer.  In addition to the obvious
features, this also fixes a bug where closing a port
with a full buffer would accidentally disable writes
when the port was re-opened.

The original pl2303 could lose data that was still going
out the port on close, and even this patch only solves
the problem completely for data rates of 1200 bps and
above.

Waiting for data to drain from the circular buffer is
easy, but the problem is waiting for data to drain from
the 256 byte hardware buffer on the device.  I don't know
how much data is in the hardware buffer, so I just wait
long enough for a potentially full hardware buffer to
drain, but I don't want to wait that long for low data
rates if the buffer isn't full.  There is probably some
way to query the pl2303 to find out how much data is in
its hardware buffer, maybe snooping would reveal that.

- Added a wait for data to drain from the driver
  write buffer when closing.

- Added a wait for the data to drain from the
  device hardware buffer when closing.

- Cleared the driver write buffer when closing.
Signed-off-by: default avatarAl Borchers <alborchers@steinerpoint.com>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent cf59b853
...@@ -60,6 +60,8 @@ ...@@ -60,6 +60,8 @@
static int debug; static int debug;
#define PL2303_CLOSING_WAIT (30*HZ)
#define PL2303_BUF_SIZE 1024 #define PL2303_BUF_SIZE 1024
#define PL2303_TMP_BUF_SIZE 1024 #define PL2303_TMP_BUF_SIZE 1024
...@@ -158,6 +160,7 @@ static int pl2303_startup (struct usb_serial *serial); ...@@ -158,6 +160,7 @@ static int pl2303_startup (struct usb_serial *serial);
static void pl2303_shutdown (struct usb_serial *serial); static void pl2303_shutdown (struct usb_serial *serial);
static struct pl2303_buf *pl2303_buf_alloc(unsigned int size); static struct pl2303_buf *pl2303_buf_alloc(unsigned int size);
static void pl2303_buf_free(struct pl2303_buf *pb); static void pl2303_buf_free(struct pl2303_buf *pb);
static void pl2303_buf_clear(struct pl2303_buf *pb);
static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb); static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb);
static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb); static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb);
static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf, static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
...@@ -615,13 +618,52 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp) ...@@ -615,13 +618,52 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp)
static void pl2303_close (struct usb_serial_port *port, struct file *filp) static void pl2303_close (struct usb_serial_port *port, struct file *filp)
{ {
struct pl2303_private *priv; struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags; unsigned long flags;
unsigned int c_cflag; unsigned int c_cflag;
int result; int result;
int bps;
long timeout;
wait_queue_t wait; \
dbg("%s - port %d", __FUNCTION__, port->number); dbg("%s - port %d", __FUNCTION__, port->number);
/* wait for data to drain from the buffer */
spin_lock_irqsave(&priv->lock, flags);
timeout = PL2303_CLOSING_WAIT;
init_waitqueue_entry(&wait, current);
add_wait_queue(&port->tty->write_wait, &wait);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (pl2303_buf_data_avail(priv->buf) == 0
|| timeout == 0 || signal_pending(current)
|| !usb_get_intfdata(port->serial->interface)) /* disconnect */
break;
spin_unlock_irqrestore(&priv->lock, flags);
timeout = schedule_timeout(timeout);
spin_lock_irqsave(&priv->lock, flags);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&port->tty->write_wait, &wait);
/* clear out any remaining data in the buffer */
pl2303_buf_clear(priv->buf);
spin_unlock_irqrestore(&priv->lock, flags);
/* wait for characters to drain from the device */
/* (this is long enough for the entire 256 byte */
/* pl2303 hardware buffer to drain with no flow */
/* control for data rates of 1200 bps or more, */
/* for lower rates we should really know how much */
/* data is in the buffer to compute a delay */
/* that is not unnecessarily long) */
bps = tty_get_baud_rate(port->tty);
if (bps > 1200)
timeout = max((HZ*2560)/bps,HZ/10);
else
timeout = 2*HZ;
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(timeout);
/* shutdown our urbs */ /* shutdown our urbs */
dbg("%s - shutting down urbs", __FUNCTION__); dbg("%s - shutting down urbs", __FUNCTION__);
result = usb_unlink_urb (port->write_urb); result = usb_unlink_urb (port->write_urb);
...@@ -646,14 +688,12 @@ static void pl2303_close (struct usb_serial_port *port, struct file *filp) ...@@ -646,14 +688,12 @@ static void pl2303_close (struct usb_serial_port *port, struct file *filp)
c_cflag = port->tty->termios->c_cflag; c_cflag = port->tty->termios->c_cflag;
if (c_cflag & HUPCL) { if (c_cflag & HUPCL) {
/* drop DTR and RTS */ /* drop DTR and RTS */
priv = usb_get_serial_port_data(port);
spin_lock_irqsave(&priv->lock, flags); spin_lock_irqsave(&priv->lock, flags);
priv->line_control = 0; priv->line_control = 0;
spin_unlock_irqrestore (&priv->lock, flags); spin_unlock_irqrestore (&priv->lock, flags);
set_control_lines (port->serial->dev, 0); set_control_lines (port->serial->dev, 0);
} }
} }
} }
static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file, static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
...@@ -1006,7 +1046,7 @@ static struct pl2303_buf *pl2303_buf_alloc(unsigned int size) ...@@ -1006,7 +1046,7 @@ static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
* Free the buffer and all associated memory. * Free the buffer and all associated memory.
*/ */
void pl2303_buf_free(struct pl2303_buf *pb) static void pl2303_buf_free(struct pl2303_buf *pb)
{ {
if (pb != NULL) { if (pb != NULL) {
if (pb->buf_buf != NULL) if (pb->buf_buf != NULL)
...@@ -1016,6 +1056,20 @@ void pl2303_buf_free(struct pl2303_buf *pb) ...@@ -1016,6 +1056,20 @@ void pl2303_buf_free(struct pl2303_buf *pb)
} }
/*
* pl2303_buf_clear
*
* Clear out all data in the circular buffer.
*/
static void pl2303_buf_clear(struct pl2303_buf *pb)
{
if (pb != NULL)
pb->buf_get = pb->buf_put;
/* equivalent to a get of all data available */
}
/* /*
* pl2303_buf_data_avail * pl2303_buf_data_avail
* *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment