Hello!
I have ZyXEL UNO modem which has usb acm interface. Starting with kernel
version 2.6.8 the driver cdc-acm began to hand when the modem connection
was dropped due to being idle. After the hang, no data could be read from
the device /dev/usb/ttyACM0, but writting was ok. The led "data" was lit
on the modem all the time after the disconnect (which indicates that modem
has data to be read).
I tracked down the problem to this: acm_read_bulk was called from
usb_submit_urb(acm->readurb), _inside_ acm_tty_open, and that time acm->used
was 0 which led to acm_rx_tasklet being not called, and acm_read_bulk was
never called again.
The problem was caused by patch in 2.6.8, which moved incrementing of
acm->used to bottom of acm_tty_open. My patch to fix it is attached.
When debugging the problem I noticed that debug messages sometimes have
double \n and the second one is not KERN_DEBUG level, which causes it to
be emitted to display. A second patch to normalize newlines in debug
output is attached.
--
Alexander.
--- cdc-acm.c.1 2005-02-19 00:46:04.498452192 +0300
+++ cdc-acm.c 2005-02-19 00:48:09.313477424 +0300
@@ -272,7 +272,7 @@
down(&open_sem);
- if (acm->used) {
+ if (acm->used++) {
goto done;
}
@@ -296,7 +296,6 @@
tty->low_latency = 1;
done:
- acm->used++;
up(&open_sem);
return 0;
@@ -305,6 +304,7 @@
bail_out_and_unlink:
usb_kill_urb(acm->ctrlurb);
bail_out:
+ acm->used--;
up(&open_sem);
return -EIO;
}
--- cdc-acm.c.2 2005-02-19 00:48:48.222562344 +0300
+++ cdc-acm.c 2005-02-19 00:53:42.616807616 +0300
@@ -175,7 +175,7 @@
static void acm_read_bulk(struct urb *urb, struct pt_regs *regs)
{
struct acm *acm = urb->context;
- dbg("Entering acm_read_bulk with status %d\n", urb->status);
+ dbg("Entering acm_read_bulk with status %d", urb->status);
if (!ACM_READY(acm))
return;
@@ -232,7 +232,7 @@
static void acm_write_bulk(struct urb *urb, struct pt_regs *regs)
{
struct acm *acm = (struct acm *)urb->context;
- dbg("Entering acm_write_bulk with status %d\n", urb->status);
+ dbg("Entering acm_write_bulk with status %d", urb->status);
if (!ACM_READY(acm))
goto out;
@@ -248,7 +248,7 @@
static void acm_softint(void *private)
{
struct acm *acm = private;
- dbg("Entering acm_softint.\n");
+ dbg("Entering acm_softint.");
if (!ACM_READY(acm))
return;
@@ -262,7 +262,7 @@
static int acm_tty_open(struct tty_struct *tty, struct file *filp)
{
struct acm *acm = acm_table[tty->index];
- dbg("Entering acm_tty_open.\n");
+ dbg("Entering acm_tty_open.");
if (!acm || !acm->dev)
return -EINVAL;
@@ -339,7 +339,7 @@
{
struct acm *acm = tty->driver_data;
int stat;
- dbg("Entering acm_tty_write to write %d bytes,\n", count);
+ dbg("Entering acm_tty_write to write %d bytes,", count);
if (!ACM_READY(acm))
return -EINVAL;
@@ -352,7 +352,7 @@
dbg("Get %d bytes...", count);
memcpy(acm->write_buffer, buf, count);
- dbg(" Successfully copied.\n");
+ dbg(" Successfully copied.");
acm->writeurb->transfer_buffer_length = count;
acm->writeurb->dev = acm->dev;
@@ -540,7 +540,7 @@
if (!buflen) {
if (intf->cur_altsetting->endpoint->extralen &&
intf->cur_altsetting->endpoint->extra) {
- dev_dbg(&intf->dev,"Seeking extra descriptors on
endpoint");
+ dev_dbg(&intf->dev,"Seeking extra descriptors on
endpoint\n");
buflen = intf->cur_altsetting->endpoint->extralen;
buffer = intf->cur_altsetting->endpoint->extra;
} else {