Hi all,

Here's a preliminary patch against 2.6.16 that fixes
rndis_host/cdc_ether to work with Windows Mobile 5 based devices.

I'll submit the patch to the upstream maintainer as soon as it has
been tested a bit more and cleaned up a bit.

Regards,
Ole André
diff -Nur linux-2.6.16-orig/drivers/usb/net/cdc_ether.c linux-2.6.16/drivers/usb/net/cdc_ether.c
--- linux-2.6.16-orig/drivers/usb/net/cdc_ether.c	2006-03-05 20:07:54.000000000 +0100
+++ linux-2.6.16/drivers/usb/net/cdc_ether.c	2006-03-21 04:16:21.000000000 +0100
@@ -17,8 +17,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-// #define	DEBUG			// error path messages, extra info
-// #define	VERBOSE			// more; success messages
+#define	DEBUG			// error path messages, extra info
+#define	VERBOSE			// more; success messages
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -72,7 +72,8 @@
 	/* this assumes that if there's a non-RNDIS vendor variant
 	 * of cdc-acm, it'll fail RNDIS requests cleanly.
 	 */
-	rndis = (intf->cur_altsetting->desc.bInterfaceProtocol == 0xff);
+	rndis = (intf->cur_altsetting->desc.bInterfaceProtocol == 0xff ||
+		 intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_MISC);
 
 	memset(info, 0, sizeof *info);
 	info->control = intf;
@@ -172,6 +173,55 @@
 		buf += buf [0];
 	}
 
+	/* Windows Mobile 5 based RNDIS devices lack the CDC descriptors,
+	 * so to work around this without changing too much of the overall
+	 * logic we fake those headers here. */
+	if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_MISC) {
+		struct usb_cdc_header_desc *h;
+		struct usb_cdc_union_desc *u;
+
+		dev_dbg(&intf->dev, "taking WM5 workaround path\n");
+
+		/* allocate and fill in the missing structures */
+		h = kmalloc(sizeof(struct usb_cdc_header_desc), GFP_KERNEL);
+		if (!h)
+			return -ENOMEM;
+		info->header = h;
+
+		h->bLength = sizeof(struct usb_cdc_header_desc);
+		h->bDescriptorType = USB_DT_CS_INTERFACE;
+		h->bDescriptorSubType = USB_CDC_HEADER_TYPE;
+
+		h->bcdCDC = 0; /* FIXME */
+		
+		u = kmalloc(sizeof(struct usb_cdc_union_desc), GFP_KERNEL);
+		if (!u)
+			return -ENOMEM;
+		info->u = u;
+
+		u->bLength = sizeof(struct usb_cdc_union_desc);
+		u->bDescriptorType = USB_DT_CS_INTERFACE;
+		u->bDescriptorSubType = USB_CDC_UNION_TYPE;
+		
+		u->bMasterInterface0 = 0;
+		u->bSlaveInterface0 = 1;
+
+		/* initialize */
+		info->control = usb_ifnum_to_if(dev->udev,
+					info->u->bMasterInterface0);
+		info->data = usb_ifnum_to_if(dev->udev,
+					info->u->bSlaveInterface0);
+		if (!info->control || !info->data) {
+			dev_dbg(&intf->dev,
+				"master #%u/%p slave #%u/%p\n",
+				info->u->bMasterInterface0,
+				info->control,
+				info->u->bSlaveInterface0,
+				info->data);
+			goto bad_desc;
+		}
+	}
+	
 	if (!info->header || !info->u || (!rndis && !info->ether)) {
 		dev_dbg(&intf->dev, "missing cdc %s%s%sdescriptor\n",
 			info->header ? "" : "header ",
@@ -229,6 +279,15 @@
 	struct cdc_state		*info = (void *) &dev->data;
 	struct usb_driver		*driver = driver_of(intf);
 
+	/* clean up resources allocated by the Windows Mobile 5 RNDIS workaround */
+	if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_MISC) {
+		if (info->header)
+			kfree(info->header);
+
+		if (info->u)
+			kfree(info->u);
+	}
+
 	/* disconnect master --> disconnect slave */
 	if (intf == info->control && info->data) {
 		/* ensure immediate exit from usbnet_disconnect */
diff -Nur linux-2.6.16-orig/drivers/usb/net/rndis_host.c linux-2.6.16/drivers/usb/net/rndis_host.c
--- linux-2.6.16-orig/drivers/usb/net/rndis_host.c	2006-03-05 20:07:54.000000000 +0100
+++ linux-2.6.16/drivers/usb/net/rndis_host.c	2006-03-21 04:18:52.000000000 +0100
@@ -17,8 +17,8 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-// #define	DEBUG			// error path messages, extra info
-// #define	VERBOSE			// more; success messages
+#define	DEBUG			// error path messages, extra info
+#define	VERBOSE			// more; success messages
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -231,6 +231,7 @@
  */
 #define OID_802_3_PERMANENT_ADDRESS	ccpu2(0x01010101)
 #define OID_GEN_CURRENT_PACKET_FILTER	ccpu2(0x0001010e)
+#define OID_GEN_VENDOR_DRIVER_VERSION	ccpu2(0x00010116)
 
 /*
  * RNDIS notifications from device: command completion; "reverse"
@@ -281,8 +282,10 @@
 		0, info->u->bMasterInterface0,
 		buf, le32_to_cpu(buf->msg_len),
 		RNDIS_CONTROL_TIMEOUT_MS);
-	if (unlikely(retval < 0 || xid == 0))
+	if (unlikely(retval < 0 || xid == 0)) {
+		dev_dbg(&info->control->dev, "usb_control_msg failed\n");
 		return retval;
+	}
 
 	// FIXME Seems like some devices discard responses when
 	// we time out and cancel our "get response" requests...
@@ -377,6 +380,7 @@
 		struct rndis_set_c	*set_c;
 	} u;
 	u32			tmp;
+	char			*p;
 
 	/* we can't rely on i/o from stack working, or stack allocation */
 	u.buf = kmalloc(1024, GFP_KERNEL);
@@ -409,11 +413,39 @@
 	dev_dbg(&intf->dev, "hard mtu %u, align %d\n", dev->hard_mtu,
 		1 << le32_to_cpu(u.init_c->packet_alignment));
 
+	/* get vendor driver version */
+	memset(u.get, 0, sizeof *u.get + 4);
+	u.get->msg_type = RNDIS_MSG_QUERY;
+	u.get->msg_len = ccpu2(sizeof *u.get + 4);
+	u.get->oid = OID_GEN_VENDOR_DRIVER_VERSION;
+	u.get->len = ccpu2(4);
+	u.get->offset = ccpu2(20);
+	
+	retval = rndis_command(dev, u.header);
+	if (unlikely(retval < 0)) {
+		dev_err(&intf->dev, "rndis get vendor driver version, %d\n", retval);
+		goto fail;
+	}
+
+	tmp = le32_to_cpu(u.get_c->offset);
+	if (unlikely((tmp + 8) > (1024 - 2)
+			|| u.get_c->len != ccpu2(2))) {
+		dev_err(&intf->dev, "rndis vendor driver version off %d len %d ?\n",
+			tmp, le32_to_cpu(u.get_c->len));
+		retval = -EDOM;
+		goto fail;
+	}
+	
+	p = (char *) &u.get_c->request_id + tmp;
+	dev_dbg(&intf->dev, "got vendor driver version %d.%d\n", p[1], p[0]);
+
 	/* get designated host ethernet address */
-	memset(u.get, 0, sizeof *u.get);
+	memset(u.get, 0, sizeof *u.get + 48);
 	u.get->msg_type = RNDIS_MSG_QUERY;
-	u.get->msg_len = ccpu2(sizeof *u.get);
+	u.get->msg_len = ccpu2(sizeof *u.get + 48);
 	u.get->oid = OID_802_3_PERMANENT_ADDRESS;
+	u.get->len = ccpu2(48);
+	u.get->offset = ccpu2(20);
 
 	retval = rndis_command(dev, u.header);
 	if (unlikely(retval < 0)) {
@@ -575,10 +607,14 @@
 
 /*-------------------------------------------------------------------------*/
 
+#define WM5_SUB_CLASS 0x01
+#define WM5_PROTOCOL  0x01
+
 static const struct usb_device_id	products [] = {
 {
 	/* RNDIS is MSFT's un-official variant of CDC ACM */
 	USB_INTERFACE_INFO(USB_CLASS_COMM, 2 /* ACM */, 0x0ff),
+        USB_INTERFACE_INFO(USB_CLASS_MISC, WM5_SUB_CLASS, WM5_PROTOCOL),
 	.driver_info = (unsigned long) &rndis_info,
 },
 	{ },		// END
diff -Nur linux-2.6.16-orig/drivers/usb/net/usbnet.c linux-2.6.16/drivers/usb/net/usbnet.c
--- linux-2.6.16-orig/drivers/usb/net/usbnet.c	2006-03-05 20:07:54.000000000 +0100
+++ linux-2.6.16/drivers/usb/net/usbnet.c	2006-03-21 02:21:48.000000000 +0100
@@ -30,8 +30,8 @@
  * issues can usefully be addressed by this framework.
  */
 
-// #define	DEBUG			// error path messages, extra info
-// #define	VERBOSE			// more; success messages
+#define	DEBUG			// error path messages, extra info
+#define	VERBOSE			// more; success messages
 
 #include <linux/config.h>
 #include <linux/module.h>
diff -Nur linux-2.6.16-orig/include/linux/usb_ch9.h linux-2.6.16/include/linux/usb_ch9.h
--- linux-2.6.16-orig/include/linux/usb_ch9.h	2006-03-05 20:07:54.000000000 +0100
+++ linux-2.6.16/include/linux/usb_ch9.h	2006-03-21 02:13:39.000000000 +0100
@@ -217,6 +217,7 @@
 #define USB_CLASS_CONTENT_SEC		0x0d	/* content security */
 #define USB_CLASS_VIDEO			0x0e
 #define USB_CLASS_WIRELESS_CONTROLLER	0xe0
+#define USB_CLASS_MISC			0xef
 #define USB_CLASS_APP_SPEC		0xfe
 #define USB_CLASS_VENDOR_SPEC		0xff
 


Reply via email to