net: usb: qcusbnet: Add support for stingray modem
authorBenoit Goby <benoit@android.com>
Fri, 25 Feb 2011 04:04:17 +0000 (20:04 -0800)
committerBenoit Goby <benoit@android.com>
Thu, 3 Mar 2011 02:24:26 +0000 (18:24 -0800)
1. Implement reset_resume
2. Remove hardcoding of qmi to interface 0
3. Correctly assign bulk in and out endpoints
4. Change ethernet interface name from usbX to qmiX to remove conflicts with other drivers
5. Assign individual MAC address to each ethernet interface
6. Modify the usb device table to include subclass interface ids for motorola products
7. Remove any hardcoded interface, endpoint numbers

Change-Id: I3b08a5b0acb841e38177cdd758a16f32913246bb
Signed-off-by: Srinivas Gowrishetty <srinivas.gowrishetty@motorola.com>
drivers/net/usb/qcusbnet/qcusbnet.c
drivers/net/usb/qcusbnet/qmidevice.c

index b7877f6b1bbda57c44e1626ba95e16d7f28f4a44..ff89e9cd3265d7d2e24990cf464b624df61ae209 100644 (file)
@@ -119,6 +119,11 @@ static int qc_resume(struct usb_interface *iface)
        return ret;
 }
 
+static int qc_reset_resume(struct usb_interface *iface)
+{
+       return qc_resume(iface);
+}
+
 static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface)
 {
        int numends;
@@ -132,12 +137,6 @@ static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface)
                return -EINVAL;
        }
 
-       if (iface->cur_altsetting->desc.bInterfaceNumber != 0) {
-               DBG("invalid interface %d\n",
-                         iface->cur_altsetting->desc.bInterfaceNumber);
-               return -EINVAL;
-       }
-
        numends = iface->cur_altsetting->desc.bNumEndpoints;
        for (i = 0; i < numends; i++) {
                endpoint = iface->cur_altsetting->endpoint + i;
@@ -146,12 +145,10 @@ static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface)
                        return -EINVAL;
                }
 
-               if (usb_endpoint_dir_in(&endpoint->desc)
-               &&  !usb_endpoint_xfer_int(&endpoint->desc)) {
+               if (usb_endpoint_is_bulk_in(&endpoint->desc))
                        in = endpoint;
-               } else if (!usb_endpoint_dir_out(&endpoint->desc)) {
+               else if (usb_endpoint_is_bulk_out(&endpoint->desc))
                        out = endpoint;
-               }
        }
 
        if (!in || !out) {
@@ -172,6 +169,9 @@ static int qcnet_bind(struct usbnet *usbnet, struct usb_interface *iface)
            in->desc.bEndpointAddress,
            out->desc.bEndpointAddress);
 
+       strcpy(usbnet->net->name, "qmi%d");
+       random_ether_addr(&usbnet->net->dev_addr[0]);
+
        return 0;
 }
 
@@ -527,6 +527,10 @@ static const struct usb_device_id qc_vidpids[] = {
        MKVIDPID(0x05c6, 0x9225),       /* Sony Gobi 2000 */
        MKVIDPID(0x05c6, 0x9235),       /* Top Global Gobi 2000 */
        MKVIDPID(0x05c6, 0x9275),       /* iRex Technologies Gobi 2000 */
+       {
+               USB_DEVICE_AND_INTERFACE_INFO(0x22B8, 0x2A70, 0xff, 0xfb, 0xff), /* Motorola Xoom */
+               .driver_info = (unsigned long)&qc_netinfo
+       },
        { }
 };
 
@@ -607,12 +611,13 @@ int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids
 EXPORT_SYMBOL_GPL(qcnet_probe);
 
 static struct usb_driver qcusbnet = {
-       .name       = "QCUSBNet2k",
-       .id_table   = qc_vidpids,
-       .probe      = qcnet_probe,
-       .disconnect = usbnet_disconnect,
-       .suspend    = qc_suspend,
-       .resume     = qc_resume,
+       .name           = "QCUSBNet2k",
+       .id_table       = qc_vidpids,
+       .probe          = qcnet_probe,
+       .disconnect     = usbnet_disconnect,
+       .suspend        = qc_suspend,
+       .resume         = qc_resume,
+       .reset_resume   = qc_reset_resume,
        .supports_autosuspend = true,
 };
 
index 48e44342d81f6632a8a078b18538dda4733165a8..f125697f208e7e5380c102a29f1e769fc789968c 100644 (file)
@@ -84,6 +84,7 @@ static int qmidms_getmeid(struct qcusbnet *dev);
 #define IOCTL_QMI_GET_SERVICE_FILE     (0x8BE0 + 1)
 #define IOCTL_QMI_GET_DEVICE_VIDPID    (0x8BE0 + 2)
 #define IOCTL_QMI_GET_DEVICE_MEID      (0x8BE0 + 3)
+#define CDC_GET_MASK                   0xFFFFll
 #define CDC_GET_ENCAPSULATED_RESPONSE  0x01A1ll
 #define CDC_CONNECTION_SPEED_CHANGE    0x08000000002AA1ll
 
@@ -165,7 +166,8 @@ static void read_callback(struct urb *urb)
        data = urb->transfer_buffer;
        size = urb->actual_length;
 
-       print_hex_dump(KERN_INFO, "QCUSBNet2k: ", DUMP_PREFIX_OFFSET,
+       if (debug)
+               print_hex_dump(KERN_INFO, "QCUSBNet2k: ", DUMP_PREFIX_OFFSET,
                       16, 1, data, size, true);
 
        result = qmux_parse(&cid, data, size);
@@ -228,7 +230,8 @@ static void int_callback(struct urb *urb)
                        return;
        } else {
                if ((urb->actual_length == 8) &&
-                   (*(u64 *)urb->transfer_buffer == CDC_GET_ENCAPSULATED_RESPONSE)) {
+                   (*(u64 *)urb->transfer_buffer & CDC_GET_MASK) ==
+                                       CDC_GET_ENCAPSULATED_RESPONSE) {
                        usb_fill_control_urb(dev->qmi.readurb, dev->usbnet->udev,
                                             usb_rcvctrlpipe(dev->usbnet->udev, 0),
                                             (unsigned char *)dev->qmi.readsetup,
@@ -254,7 +257,8 @@ static void int_callback(struct urb *urb)
                        }
                } else {
                        DBG("ignoring invalid interrupt in packet\n");
-                       print_hex_dump(KERN_INFO, "QCUSBNet2k: ",
+                       if (debug)
+                               print_hex_dump(KERN_INFO, "QCUSBNet2k: ",
                                       DUMP_PREFIX_OFFSET, 16, 1,
                                       urb->transfer_buffer,
                                       urb->actual_length, true);
@@ -275,6 +279,9 @@ static void int_callback(struct urb *urb)
 int qc_startread(struct qcusbnet *dev)
 {
        int interval;
+       int numends;
+       int i;
+       struct usb_host_endpoint *endpoint = NULL;
 
        if (!device_valid(dev)) {
                DBG("Invalid device!\n");
@@ -324,15 +331,31 @@ int qc_startread(struct qcusbnet *dev)
        dev->qmi.readsetup->type = 0xA1;
        dev->qmi.readsetup->code = 1;
        dev->qmi.readsetup->value = 0;
-       dev->qmi.readsetup->index = 0;
+       dev->qmi.readsetup->index = dev->iface->cur_altsetting->desc.bInterfaceNumber;
        dev->qmi.readsetup->len = DEFAULT_READ_URB_LENGTH;
 
        interval = (dev->usbnet->udev->speed == USB_SPEED_HIGH) ? 7 : 3;
 
+       numends = dev->iface->cur_altsetting->desc.bNumEndpoints;
+       for (i = 0; i < numends; i++) {
+               endpoint = dev->iface->cur_altsetting->endpoint + i;
+               if (!endpoint) {
+                       DBG("invalid endpoint %u\n", i);
+                       return -EINVAL;
+               }
+
+               if (usb_endpoint_dir_in(&endpoint->desc)
+                 && usb_endpoint_xfer_int(&endpoint->desc)) {
+                       DBG("Interrupt endpoint is %x\n", endpoint->desc.bEndpointAddress);
+                       break;
+               }
+       }
+
        usb_fill_int_urb(dev->qmi.inturb, dev->usbnet->udev,
-                        usb_rcvintpipe(dev->usbnet->udev, 0x81),
+                        usb_rcvintpipe(dev->usbnet->udev, endpoint->desc.bEndpointAddress),
                         dev->qmi.intbuf, DEFAULT_READ_URB_LENGTH,
                         int_callback, dev, interval);
+
        return usb_submit_urb(dev->qmi.inturb, GFP_KERNEL);
 }
 
@@ -512,7 +535,7 @@ static int write_sync(struct qcusbnet *dev, char *buf, int size, u16 cid)
        setup.type = 0x21;
        setup.code = 0;
        setup.value = 0;
-       setup.index = 0;
+       setup.index = dev->iface->cur_altsetting->desc.bInterfaceNumber;
        setup.len = 0;
        setup.len = size;
 
@@ -522,7 +545,8 @@ static int write_sync(struct qcusbnet *dev, char *buf, int size, u16 cid)
                             NULL, dev);
 
        DBG("Actual Write:\n");
-       print_hex_dump(KERN_INFO,  "QCUSBNet2k: ", DUMP_PREFIX_OFFSET,
+       if (debug)
+               print_hex_dump(KERN_INFO,  "QCUSBNet2k: ", DUMP_PREFIX_OFFSET,
                       16, 1, buf, size, true);
 
        sema_init(&sem, 0);
@@ -1252,12 +1276,12 @@ int qc_register(struct qcusbnet *dev)
                return result;
        }
 
-       name = strstr(dev->usbnet->net->name, "usb");
+       name = strstr(dev->usbnet->net->name, "qmi");
        if (!name) {
                DBG("Bad net name: %s\n", dev->usbnet->net->name);
                return -ENXIO;
        }
-       name += strlen("usb");
+       name += strlen("qmi");
        qmiidx = simple_strtoul(name, NULL, 10);
        if (qmiidx < 0) {
                DBG("Bad minor number\n");
@@ -1526,6 +1550,7 @@ static int setup_wds_callback(struct qcusbnet *dev)
                return result;
        }
 
+/* TODO: Restore this once the ril supports it
        result = usb_control_msg(dev->usbnet->udev,
                                 usb_sndctrlpipe(dev->usbnet->udev, 0),
                                 0x22, 0x21, 1, 0, NULL, 0, 100);
@@ -1533,6 +1558,7 @@ static int setup_wds_callback(struct qcusbnet *dev)
                DBG("Bad SetControlLineState status %d\n", result);
                return result;
        }
+*/
 
        return 0;
 }