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