Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1603843

[PATCH v2 1/4] cdc-acm: reassemble fragmented notifications

From Tobias Herzog <t-herzog@gmx.de>
Newsgroups linux.kernel
Subject [PATCH v2 1/4] cdc-acm: reassemble fragmented notifications
Date 2017-03-18 20:10 +0100
Message-ID <tmrTI-2ru-13@gated-at.bofh.it> (permalink)
References <tl15f-5mY-9@gated-at.bofh.it> <tmrK1-27E-3@gated-at.bofh.it>
Organization linux.* mail to news gateway

Show all headers | View raw


USB devices may have very limitited endpoint packet sizes, so that
notifications can not be transferred within one single usb packet.
Reassembling of multiple packages may be necessary.

Signed-off-by: Tobias Herzog <t-herzog@gmx.de>
---
 drivers/usb/class/cdc-acm.c | 106 ++++++++++++++++++++++++++++++++------------
 drivers/usb/class/cdc-acm.h |   3 ++
 2 files changed, 80 insertions(+), 29 deletions(-)

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index e35b150..74acca1 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -282,39 +282,13 @@ static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
  * Interrupt handlers for various ACM device responses
  */
 
-/* control interface reports status changes with "interrupt" transfers */
-static void acm_ctrl_irq(struct urb *urb)
+static void acm_process_notification(struct acm *acm, unsigned char *buf)
 {
-	struct acm *acm = urb->context;
-	struct usb_cdc_notification *dr = urb->transfer_buffer;
-	unsigned char *data;
 	int newctrl;
 	int difference;
-	int retval;
-	int status = urb->status;
-
-	switch (status) {
-	case 0:
-		/* success */
-		break;
-	case -ECONNRESET:
-	case -ENOENT:
-	case -ESHUTDOWN:
-		/* this urb is terminated, clean up */
-		dev_dbg(&acm->control->dev,
-			"%s - urb shutting down with status: %d\n",
-			__func__, status);
-		return;
-	default:
-		dev_dbg(&acm->control->dev,
-			"%s - nonzero urb status received: %d\n",
-			__func__, status);
-		goto exit;
-	}
+	struct usb_cdc_notification *dr = (struct usb_cdc_notification *)buf;
+	unsigned char *data = (unsigned char *)(dr + 1);
 
-	usb_mark_last_busy(acm->dev);
-
-	data = (unsigned char *)(dr + 1);
 	switch (dr->bNotificationType) {
 	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
 		dev_dbg(&acm->control->dev,
@@ -363,8 +337,77 @@ static void acm_ctrl_irq(struct urb *urb)
 			__func__,
 			dr->bNotificationType, dr->wIndex,
 			dr->wLength, data[0], data[1]);
+	}
+}
+
+/* control interface reports status changes with "interrupt" transfers */
+static void acm_ctrl_irq(struct urb *urb)
+{
+	struct acm *acm = urb->context;
+	struct usb_cdc_notification *dr = urb->transfer_buffer;
+	unsigned int current_size = urb->actual_length;
+	unsigned int expected_size, copy_size;
+	int retval;
+	int status = urb->status;
+
+	switch (status) {
+	case 0:
+		/* success */
 		break;
+	case -ECONNRESET:
+	case -ENOENT:
+	case -ESHUTDOWN:
+		/* this urb is terminated, clean up */
+		acm->nb_index = 0;
+		dev_dbg(&acm->control->dev,
+			"%s - urb shutting down with status: %d\n",
+			__func__, status);
+		return;
+	default:
+		dev_dbg(&acm->control->dev,
+			"%s - nonzero urb status received: %d\n",
+			__func__, status);
+		goto exit;
 	}
+
+	usb_mark_last_busy(acm->dev);
+
+	if (acm->nb_index)
+		dr = (struct usb_cdc_notification *)acm->notification_buffer;
+
+	/* size = notification-header + (optional) data */
+	expected_size = sizeof(struct usb_cdc_notification) +
+					le16_to_cpu(dr->wLength);
+
+	if (current_size < expected_size) {
+		/* notification is transmitted fragmented, reassemble */
+		if (acm->nb_size < expected_size) {
+			if (acm->nb_size) {
+				kfree(acm->notification_buffer);
+				acm->nb_size = 0;
+			}
+			acm->notification_buffer =
+					kmalloc(expected_size, GFP_ATOMIC);
+			if (!acm->notification_buffer)
+				goto exit;
+			acm->nb_size = expected_size;
+		}
+
+		copy_size = min(current_size,
+				expected_size - acm->nb_index);
+
+		memcpy(&acm->notification_buffer[acm->nb_index],
+		       urb->transfer_buffer, copy_size);
+		acm->nb_index += copy_size;
+		current_size = acm->nb_index;
+	}
+
+	if (current_size >= expected_size) {
+		/* notification complete */
+		acm_process_notification(acm, (unsigned char *)dr);
+		acm->nb_index = 0;
+	}
+
 exit:
 	retval = usb_submit_urb(urb, GFP_ATOMIC);
 	if (retval && retval != -EPERM)
@@ -1488,6 +1531,9 @@ static int acm_probe(struct usb_interface *intf,
 			 epctrl->bInterval ? epctrl->bInterval : 16);
 	acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 	acm->ctrlurb->transfer_dma = acm->ctrl_dma;
+	acm->notification_buffer = NULL;
+	acm->nb_index = 0;
+	acm->nb_size = 0;
 
 	dev_info(&intf->dev, "ttyACM%d: USB ACM device\n", minor);
 
@@ -1580,6 +1626,8 @@ static void acm_disconnect(struct usb_interface *intf)
 	usb_free_coherent(acm->dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
 	acm_read_buffers_free(acm);
 
+	kfree(acm->notification_buffer);
+
 	if (!acm->combined_interfaces)
 		usb_driver_release_interface(&acm_driver, intf == acm->control ?
 					acm->data : acm->control);
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index c980f11..b519138 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -98,6 +98,9 @@ struct acm {
 	struct acm_wb *putbuffer;			/* for acm_tty_put_char() */
 	int rx_buflimit;
 	spinlock_t read_lock;
+	u8 *notification_buffer;			/* to reassemble fragmented notifications */
+	unsigned int nb_index;
+	unsigned int nb_size;
 	int write_used;					/* number of non-empty write buffers */
 	int transmitting;
 	spinlock_t write_lock;
-- 
2.1.4

Back to linux.kernel | Previous | NextPrevious in thread | Next in thread | Find similar | Unroll thread


Thread

[PATCH v2 0/4] cdc-acm: reassemble fragmented notifications Tobias Herzog <t-herzog@gmx.de> - 2017-03-18 20:00 +0100
  [PATCH v2 4/4] cdc-acm: remove unused element of struct acm Tobias Herzog <t-herzog@gmx.de> - 2017-03-18 20:00 +0100
  [PATCH v2 2/4] cdc-acm: fix possible invalid access when processing notification Tobias Herzog <t-herzog@gmx.de> - 2017-03-18 20:00 +0100
    Re: [PATCH v2 2/4] cdc-acm: fix possible invalid access when  processing notification Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> - 2017-03-19 11:00 +0100
    Re: [PATCH v2 2/4] cdc-acm: fix possible invalid access when  processing notification Oliver Neukum <oneukum@suse.com> - 2017-03-20 16:10 +0100
  [PATCH v2 1/4] cdc-acm: reassemble fragmented notifications Tobias Herzog <t-herzog@gmx.de> - 2017-03-18 20:10 +0100
    Re: [PATCH v2 1/4] cdc-acm: reassemble fragmented notifications Oliver Neukum <oneukum@suse.com> - 2017-03-20 16:10 +0100
      Re: [PATCH v2 1/4] cdc-acm: reassemble fragmented notifications Tobias Herzog <t-herzog@gmx.de> - 2017-03-24 23:00 +0100

csiph-web