Commit a81cf979 authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman
Browse files

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: default avatarOliver Neukum <&gt;oneukum@suse.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent ca1c3e6f
Loading
Loading
Loading
Loading
+67 −0
Original line number Diff line number Diff line
@@ -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,
+1 −0
Original line number Diff line number Diff line
@@ -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;