diff mbox

[RFC] net: cdc_ncm: workaround NTB input size firmware bug

Message ID 87mwziu21c.fsf_-_@nemi.mork.no
State RFC, archived
Delegated to: David Miller
Headers show

Commit Message

Bjørn Mork Oct. 19, 2012, 11:36 a.m. UTC
Some devices do not support the 8 byte variants of
the NTB input size control messages despite announcing
such support in the functional descriptor. This can be
detected by reading the current input size, looking
at how many bytes the device returns.

Signed-off-by: Bjørn Mork <bjorn@mork.no>
---
Bjørn Mork <bjorn@mork.no> writes:

> I'll cook up an alternative patch implementing this so you can evaluate
> the impact.

So how about something like this?

I couldn't help myself not combining the two paths here while at it.
But I have intentionally not done the other obvious cleanups, like using
the standard timeout constants and using dev_xxx for instead of pr_xxx,
because they must and should be done separately over the whole driver.

This works for me:

Oct 19 13:19:43 nemi kernel: [304987.350138] dwNtbInMaxSize=131072 dwNtbOutMaxSize=32768 wNdpOutPayloadRemainder=0 wNdpOutDivisor=4 wNdpOutAlignment=4 wNtbOutMaxDatagrams=0 flags=0x20
Oct 19 13:19:43 nemi kernel: [304987.350144] Using default maximum receive length=32768
Oct 19 13:19:43 nemi kernel: [304987.350507] firmware bug: flags=0x20, but USB_CDC_GET_NTB_INPUT_SIZE returned 4 bytes


Bjørn

 drivers/net/usb/cdc_ncm.c |   64 +++++++++++++++++++++++----------------------
 1 file changed, 33 insertions(+), 31 deletions(-)
diff mbox

Patch

diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 4cd582a..26d31d7 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -215,44 +215,46 @@  static u8 cdc_ncm_setup(struct cdc_ncm_ctx *ctx)
 
 	/* inform device about NTB input size changes */
 	if (ctx->rx_max != le32_to_cpu(ctx->ncm_parm.dwNtbInMaxSize)) {
+		struct usb_cdc_ncm_ndp_input_size *ndp_in_sz;
+		size_t request_len = 4;
 
+		ndp_in_sz = kzalloc(sizeof(*ndp_in_sz), GFP_KERNEL);
+		if (!ndp_in_sz) {
+			err = -ENOMEM;
+			goto size_err;
+		}
 		if (flags & USB_CDC_NCM_NCAP_NTB_INPUT_SIZE) {
-			struct usb_cdc_ncm_ndp_input_size *ndp_in_sz;
-
-			ndp_in_sz = kzalloc(sizeof(*ndp_in_sz), GFP_KERNEL);
-			if (!ndp_in_sz) {
-				err = -ENOMEM;
-				goto size_err;
-			}
-
 			err = usb_control_msg(ctx->udev,
-					usb_sndctrlpipe(ctx->udev, 0),
-					USB_CDC_SET_NTB_INPUT_SIZE,
-					USB_TYPE_CLASS | USB_DIR_OUT
-					 | USB_RECIP_INTERFACE,
-					0, iface_no, ndp_in_sz, 8, 1000);
-			kfree(ndp_in_sz);
-		} else {
-			__le32 *dwNtbInMaxSize;
-			dwNtbInMaxSize = kzalloc(sizeof(*dwNtbInMaxSize),
-					GFP_KERNEL);
-			if (!dwNtbInMaxSize) {
-				err = -ENOMEM;
-				goto size_err;
+					      usb_rcvctrlpipe(ctx->udev, 0),
+					      USB_CDC_GET_NTB_INPUT_SIZE,
+					      USB_TYPE_CLASS | USB_DIR_OUT
+					      | USB_RECIP_INTERFACE,
+					      0, iface_no, ndp_in_sz, 8, 1000);
+			switch (err) {
+			case 4:
+				pr_debug("firmware bug: flags=0x%02x, but USB_CDC_GET_NTB_INPUT_SIZE returned 4 bytes\n", flags);
+				break;
+			case 8:
+				/* bmNetworkCapabilities is correct */
+				request_len = 8;
+				break;
+			default:
+				goto size_err_free;
 			}
-			*dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
-
-			err = usb_control_msg(ctx->udev,
-					usb_sndctrlpipe(ctx->udev, 0),
-					USB_CDC_SET_NTB_INPUT_SIZE,
-					USB_TYPE_CLASS | USB_DIR_OUT
-					 | USB_RECIP_INTERFACE,
-					0, iface_no, dwNtbInMaxSize, 4, 1000);
-			kfree(dwNtbInMaxSize);
 		}
+		ndp_in_sz->dwNtbInMaxSize = cpu_to_le32(ctx->rx_max);
+		ndp_in_sz->wNtbInMaxDatagrams = 0;	/* unlimited */
+		err = usb_control_msg(ctx->udev,
+				      usb_sndctrlpipe(ctx->udev, 0),
+				      USB_CDC_SET_NTB_INPUT_SIZE,
+				      USB_TYPE_CLASS | USB_DIR_OUT
+				      | USB_RECIP_INTERFACE,
+				      0, iface_no, ndp_in_sz, request_len, 1000);
+size_err_free:
+		kfree(ndp_in_sz);
 size_err:
 		if (err < 0)
-			pr_debug("Setting NTB Input Size failed\n");
+			pr_debug("Setting NTB Input Size failed: %d\n", err);
 	}
 
 	/* verify maximum size of transmitted NTB in bytes */