Discussion:
possible patch for rndis_host
Mark Ellis
2010-02-07 19:39:05 UTC
Permalink
Below is a patch we've had at the synce project for some time now, I was
hoping for some guidance. None of us are really kernel level developers,
but we flounder around where we can.

We'd observed a few rndis Windows Mobile devices giving -100 errors on
connection, and one of our sometime contributors, John Carr, came up
with the attached patch by comparing the kernel module with a userspace
test driver that was developed by someone that left the project some
time ago.

This patch seems to be very effective, but as I mentioned, none of us
are really device side developers, and John was never completely
convinced this was correct. Quoting him from a while back....

"The reason I consider it dirty is that it needs the
rndis_get_in_endpoint function which is called every time something
calls rndis_command.

Ideally that should run once when the driver loads for a given device,
but i felt like i was being too invasive when I started doing that and
whimped out.

The patch exists through careful comparison of the deprecated user
mode and the current kernel mode implementations of usb-rndis, the
poking of an INT IN endpoint being the only difference I found."


So we think we are on the right lines, but perhaps not fixing the
problem in the correct way. It would be great if someone could take a
look at this, and either say it's fine for submission, or point us in a
more appropriate direction.

Many thanks
Mark

---

diff -Nurp a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
--- a/drivers/net/usb/rndis_host.c 2009-09-09 23:13:59.000000000 +0100
+++ b/drivers/net/usb/rndis_host.c 2010-02-07 18:59:21.341929763 +0000
@@ -64,6 +64,25 @@ void rndis_status(struct usbnet *dev, st
}
EXPORT_SYMBOL_GPL(rndis_status);

+/* Function ripped from keyspan_remote.c */
+static struct usb_endpoint_descriptor *rndis_get_in_endpoint(struct
usb_host_interface *iface)
+{
+
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
+ endpoint = &iface->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(endpoint)) {
+ /* we found our interrupt in endpoint */
+ return endpoint;
+ }
+ }
+
+ return NULL;
+}
+
/*
* RPC done RNDIS-style. Caller guarantees:
* - message is properly byteswapped
@@ -77,11 +96,14 @@ EXPORT_SYMBOL_GPL(rndis_status);
int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int
buflen)
{
struct cdc_state *info = (void *) &dev->data;
+ struct usb_endpoint_descriptor *endpoint;
int master_ifnum;
int retval;
unsigned count;
__le32 rsp;
u32 xid = 0, msg_len, request_id;
+ char int_buf[128];
+ int maxp, pipe, partial;

/* REVISIT when this gets called from contexts other than probe() or
* disconnect(): either serialize, or dispatch responses on xid
@@ -110,6 +132,29 @@ int rndis_command(struct usbnet *dev, st
// we time out and cancel our "get response" requests...
// so, this is fragile. Probably need to poll for status.

+ /* FIXME This feels rancid */
+ endpoint =
rndis_get_in_endpoint(info->control->cur_altsetting);
+ pipe = usb_rcvintpipe(dev->udev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
+
+ retval = usb_interrupt_msg(dev->udev,
+ pipe,
+ int_buf,
+ (maxp > 8 ? 8 : maxp),
+ &partial,
+ RNDIS_CONTROL_TIMEOUT_MS);
+
+ dev_dbg(&info->control->dev,
+ "pipe: %d, maxp: %d, partial: %d, retval: %d\n",
+ pipe,
+ maxp,
+ partial,
+ retval);
+
+ /* I /think/ usb_interrupt_msg blocks and returns < 0 for error
*/
+ if (unlikely(retval < 0))
+ return retval;
+
/* ignore status endpoint, just poll the control channel;
* the request probably completed immediately
*/
Mark Ellis
2010-02-07 19:51:18 UTC
Permalink
synce-devel...

Didn't hear back from Ole André, so I thought I'd see what the kernel
usb guys said.
Post by Mark Ellis
Below is a patch we've had at the synce project for some time now, I was
hoping for some guidance. None of us are really kernel level developers,
but we flounder around where we can.
We'd observed a few rndis Windows Mobile devices giving -100 errors on
connection, and one of our sometime contributors, John Carr, came up
with the attached patch by comparing the kernel module with a userspace
test driver that was developed by someone that left the project some
time ago.
This patch seems to be very effective, but as I mentioned, none of us
are really device side developers, and John was never completely
convinced this was correct. Quoting him from a while back....
"The reason I consider it dirty is that it needs the
rndis_get_in_endpoint function which is called every time something
calls rndis_command.
Ideally that should run once when the driver loads for a given device,
but i felt like i was being too invasive when I started doing that and
whimped out.
The patch exists through careful comparison of the deprecated user
mode and the current kernel mode implementations of usb-rndis, the
poking of an INT IN endpoint being the only difference I found."
So we think we are on the right lines, but perhaps not fixing the
problem in the correct way. It would be great if someone could take a
look at this, and either say it's fine for submission, or point us in a
more appropriate direction.
Many thanks
Mark
---
diff -Nurp a/drivers/net/usb/rndis_host.c b/drivers/net/usb/rndis_host.c
--- a/drivers/net/usb/rndis_host.c 2009-09-09 23:13:59.000000000 +0100
+++ b/drivers/net/usb/rndis_host.c 2010-02-07 18:59:21.341929763 +0000
@@ -64,6 +64,25 @@ void rndis_status(struct usbnet *dev, st
}
EXPORT_SYMBOL_GPL(rndis_status);
+/* Function ripped from keyspan_remote.c */
+static struct usb_endpoint_descriptor *rndis_get_in_endpoint(struct
usb_host_interface *iface)
+{
+
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
+ endpoint = &iface->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(endpoint)) {
+ /* we found our interrupt in endpoint */
+ return endpoint;
+ }
+ }
+
+ return NULL;
+}
+
/*
* - message is properly byteswapped
@@ -77,11 +96,14 @@ EXPORT_SYMBOL_GPL(rndis_status);
int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int
buflen)
{
struct cdc_state *info = (void *) &dev->data;
+ struct usb_endpoint_descriptor *endpoint;
int master_ifnum;
int retval;
unsigned count;
__le32 rsp;
u32 xid = 0, msg_len, request_id;
+ char int_buf[128];
+ int maxp, pipe, partial;
/* REVISIT when this gets called from contexts other than probe() or
* disconnect(): either serialize, or dispatch responses on xid
@@ -110,6 +132,29 @@ int rndis_command(struct usbnet *dev, st
// we time out and cancel our "get response" requests...
// so, this is fragile. Probably need to poll for status.
+ /* FIXME This feels rancid */
+ endpoint =
rndis_get_in_endpoint(info->control->cur_altsetting);
+ pipe = usb_rcvintpipe(dev->udev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
+
+ retval = usb_interrupt_msg(dev->udev,
+ pipe,
+ int_buf,
+ (maxp > 8 ? 8 : maxp),
+ &partial,
+ RNDIS_CONTROL_TIMEOUT_MS);
+
+ dev_dbg(&info->control->dev,
+ "pipe: %d, maxp: %d, partial: %d, retval: %d\n",
+ pipe,
+ maxp,
+ partial,
+ retval);
+
+ /* I /think/ usb_interrupt_msg blocks and returns < 0 for error
*/
+ if (unlikely(retval < 0))
+ return retval;
+
/* ignore status endpoint, just poll the control channel;
* the request probably completed immediately
*/
Oliver Neukum
2010-02-08 09:43:45 UTC
Permalink
Post by Mark Ellis
This patch seems to be very effective, but as I mentioned, none of us
are really device side developers, and John was never completely
convinced this was correct. Quoting him from a while back....
"The reason I consider it dirty is that it needs the
rndis_get_in_endpoint function which is called every time something
calls rndis_command.
Ideally that should run once when the driver loads for a given device,
but i felt like i was being too invasive when I started doing that and
whimped out.
No problem, we can do that.
Post by Mark Ellis
The patch exists through careful comparison of the deprecated user
mode and the current kernel mode implementations of usb-rndis, the
poking of an INT IN endpoint being the only difference I found."
It would be nice if we knew why it works. If not, we need a comment
to that effect.
Post by Mark Ellis
So we think we are on the right lines, but perhaps not fixing the
problem in the correct way. It would be great if someone could take a
look at this, and either say it's fine for submission, or point us in a
more appropriate direction.
[..]
Post by Mark Ellis
+ char int_buf[128];
[..]
Post by Mark Ellis
+ retval = usb_interrupt_msg(dev->udev,
+ pipe,
+ int_buf,
+ (maxp > 8 ? 8 : maxp),
+ &partial,
+ RNDIS_CONTROL_TIMEOUT_MS);
This means doing DMA on the stack. int_buf needs to be allocated with kmalloc()
preferably during probe().

Can you comment on why this patch is needed? If that is clear, either you can
clear up the issues I mentioned, or I can do so if you prefer.

Regards
Oliver
Mark Ellis
2010-02-10 22:04:28 UTC
Permalink
Post by Oliver Neukum
Post by Mark Ellis
This patch seems to be very effective, but as I mentioned, none of us
are really device side developers, and John was never completely
convinced this was correct. Quoting him from a while back....
"The reason I consider it dirty is that it needs the
rndis_get_in_endpoint function which is called every time something
calls rndis_command.
Ideally that should run once when the driver loads for a given device,
but i felt like i was being too invasive when I started doing that and
whimped out.
No problem, we can do that.
Post by Mark Ellis
The patch exists through careful comparison of the deprecated user
mode and the current kernel mode implementations of usb-rndis, the
poking of an INT IN endpoint being the only difference I found."
It would be nice if we knew why it works. If not, we need a comment
to that effect.
Post by Mark Ellis
So we think we are on the right lines, but perhaps not fixing the
problem in the correct way. It would be great if someone could take a
look at this, and either say it's fine for submission, or point us in a
more appropriate direction.
[..]
Post by Mark Ellis
+ char int_buf[128];
[..]
Post by Mark Ellis
+ retval = usb_interrupt_msg(dev->udev,
+ pipe,
+ int_buf,
+ (maxp > 8 ? 8 : maxp),
+ &partial,
+ RNDIS_CONTROL_TIMEOUT_MS);
This means doing DMA on the stack. int_buf needs to be allocated with kmalloc()
preferably during probe().
Ok, that makes sense.
Post by Oliver Neukum
Can you comment on why this patch is needed? If that is clear, either you can
clear up the issues I mentioned, or I can do so if you prefer.
Specifically, I can't say, why it works. We first tried it for devices
with Samsung processors, but since then it has become our general fix if
rndis_host gives something like the following.

[ 246.753730] usb 1-1.3: new full speed USB device using uhci_hcd and
address 4
[ 246.887677] usb 1-1.3: configuration #1 chosen from 1 choice
[ 247.518396] usbcore: registered new interface driver cdc_ether
[ 269.730612] rndis_host 1-1.3:1.0: RNDIS init failed, -110
[ 271.310247] rndis_host: probe of 1-1.3:1.0 failed with error -110
[ 271.310310] usbcore: registered new interface driver rndis_host

Mark
Oliver Neukum
2010-02-11 13:26:19 UTC
Permalink
Post by Mark Ellis
Post by Oliver Neukum
This means doing DMA on the stack. int_buf needs to be allocated with kmalloc()
preferably during probe().
Ok, that makes sense.
Can you make a version of your patch addressing this?
Post by Mark Ellis
Post by Oliver Neukum
Can you comment on why this patch is needed? If that is clear, either you can
clear up the issues I mentioned, or I can do so if you prefer.
Specifically, I can't say, why it works. We first tried it for devices
with Samsung processors, but since then it has become our general fix if
rndis_host gives something like the following.
Very well, that needs to be mentioned in a comment to the code.
Could you put what you've just written into a comment and respin the
patch?

Regards
Oliver
Mark Ellis
2010-02-22 18:37:54 UTC
Permalink
From: Mark Ellis <***@mpellis.org.uk>

Certain devices, particularly those with Samsung processors, show
difficulties connecting via RNDIS, specifically resulting in -110
errors in driver initialisation.

This patch, resulting from comparisons with an obsolete user mode
driver, overcomes this in all tested cases.

Signed-off-by: Mark Ellis <***@mpellis.org.uk>
---
diff -Nurp linux-source-2.6.31.orig/drivers/net/usb/rndis_host.c linux-source-2.6.31/drivers/net/usb/rndis_host.c
--- linux-source-2.6.31.orig/drivers/net/usb/rndis_host.c 2009-09-09 23:13:59.000000000 +0100
+++ linux-source-2.6.31/drivers/net/usb/rndis_host.c 2010-02-16 19:03:12.497106220 +0000
@@ -64,6 +64,25 @@ void rndis_status(struct usbnet *dev, st
}
EXPORT_SYMBOL_GPL(rndis_status);

+/* Function copied from keyspan_remote.c */
+static struct usb_endpoint_descriptor *rndis_get_in_endpoint(struct usb_host_interface *iface)
+{
+
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
+ endpoint = &iface->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(endpoint)) {
+ /* we found our interrupt in endpoint */
+ return endpoint;
+ }
+ }
+
+ return NULL;
+}
+
/*
* RPC done RNDIS-style. Caller guarantees:
* - message is properly byteswapped
@@ -77,11 +96,14 @@ EXPORT_SYMBOL_GPL(rndis_status);
int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen)
{
struct cdc_state *info = (void *) &dev->data;
+ struct usb_endpoint_descriptor *endpoint;
int master_ifnum;
int retval;
unsigned count;
__le32 rsp;
u32 xid = 0, msg_len, request_id;
+ char *int_buf = NULL;
+ int maxp, pipe, partial;

/* REVISIT when this gets called from contexts other than probe() or
* disconnect(): either serialize, or dispatch responses on xid
@@ -110,6 +132,38 @@ int rndis_command(struct usbnet *dev, st
// we time out and cancel our "get response" requests...
// so, this is fragile. Probably need to poll for status.

+ /* Certain rndis devices, particularly with Samsung processors,
+ * are problematic. An interrupt message appears to fix this.
+ */
+
+ int_buf = kmalloc(128, GFP_KERNEL);
+ if (!int_buf)
+ return -ENOMEM;
+
+ endpoint = rndis_get_in_endpoint(info->control->cur_altsetting);
+ pipe = usb_rcvintpipe(dev->udev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
+
+ retval = usb_interrupt_msg(dev->udev,
+ pipe,
+ int_buf,
+ (maxp > 8 ? 8 : maxp),
+ &partial,
+ RNDIS_CONTROL_TIMEOUT_MS);
+ kfree(int_buf);
+
+ dev_dbg(&info->control->dev,
+ "pipe: %d, maxp: %d, partial: %d, retval: %d\n",
+ pipe,
+ maxp,
+ partial,
+ retval);
+
+ /* I /think/ usb_interrupt_msg blocks and returns < 0 for error */
+ if (unlikely(retval < 0))
+ return retval;
+
+
/* ignore status endpoint, just poll the control channel;
* the request probably completed immediately
*/

Loading...