Hi, the theory that the lack of support for put_char() is a major contributor to character loss in cdc-acm can be tested. Sven, could you test the attached patch? It implements the support. Regards Oliver
From f3871a76d7e2876b0e6ad66aee91085ecf9b2785 Mon Sep 17 00:00:00 2001 From: Oliver Neukum <oneukum@xxxxxxxx> Date: Wed, 21 Oct 2015 12:10:07 +0200 Subject: [PATCH] cdc-acm: implement put_char() and flush_chars() This should cut down latencies and waste if the tty layer writes single bytes. Signed-off-by: Oliver Neukum >oneukum@xxxxxxxx> --- drivers/usb/class/cdc-acm.c | 67 +++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/class/cdc-acm.h | 1 + 2 files changed, 68 insertions(+) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index b30e742..1cb3124 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -712,9 +712,20 @@ static int acm_tty_write(struct tty_struct *tty, } if (acm->susp_count) { + if (acm->putbuffer) { + /* now to preserve order */ + usb_anchor_urb(acm->putbuffer->urb, &acm->delayed); + acm->putbuffer = NULL; + } usb_anchor_urb(wb->urb, &acm->delayed); spin_unlock_irqrestore(&acm->write_lock, flags); return count; + } else { + if (acm->putbuffer) { + /* at this point there is no good way to handle errors */ + acm_start_wb(acm, acm->putbuffer); + acm->putbuffer = NULL; + } } stat = acm_start_wb(acm, wb); @@ -725,6 +736,60 @@ static int acm_tty_write(struct tty_struct *tty, return count; } +static void acm_tty_flush_chars(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + struct acm_wb *cur = acm->putbuffer; + int err; + unsigned long flags; + + acm->putbuffer = NULL; + err = usb_autopm_get_interface_async(acm->control); + spin_lock_irqsave(&acm->write_lock, flags); + if (err < 0) { + cur->use = 0; + goto out; + } + + if (acm->susp_count) + usb_anchor_urb(cur->urb, &acm->delayed); + else + acm_start_wb(acm, cur); +out: + spin_unlock_irqrestore(&acm->write_lock, flags); + return; +} + +static int acm_tty_put_char(struct tty_struct *tty, unsigned char ch) +{ + struct acm *acm = tty->driver_data; + struct acm_wb *cur; + int wbn; + unsigned long flags; + +overflow: + cur = acm->putbuffer; + if (!cur) { + spin_lock_irqsave(&acm->write_lock, flags); + wbn = acm_wb_alloc(acm); + if (wbn >= 0) { + cur = &acm->wb[wbn]; + acm->putbuffer = cur; + } + spin_unlock_irqrestore(&acm->write_lock, flags); + if (!cur) + return 0; + } + + if (cur->len == acm->writesize) { + acm_tty_flush_chars(tty); + goto overflow; + } + + cur->buf[cur->len++] = ch; + return 1; +} + static int acm_tty_write_room(struct tty_struct *tty) { struct acm *acm = tty->driver_data; @@ -1888,6 +1953,8 @@ static const struct tty_operations acm_ops = { .cleanup = acm_tty_cleanup, .hangup = acm_tty_hangup, .write = acm_tty_write, + .put_char = acm_tty_put_char, + .flush_chars = acm_tty_flush_chars, .write_room = acm_tty_write_room, .ioctl = acm_tty_ioctl, .throttle = acm_tty_throttle, diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index dd9af38..648a6f7 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -94,6 +94,7 @@ struct acm { unsigned long read_urbs_free; struct urb *read_urbs[ACM_NR]; struct acm_rb read_buffers[ACM_NR]; + struct acm_wb *putbuffer; /* for acm_tty_put_char() */ int rx_buflimit; int rx_endpoint; spinlock_t read_lock; -- 2.1.4