|
@@ -713,9 +713,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);
|
|
@@ -726,6 +737,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;
|
|
@@ -1905,6 +1970,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,
|