CHROMIUM: usb: add VID/PID of Sierra Wireless Gobi 3000 MC8355 modem.
[cascardo/linux.git] / drivers / net / usb / gobi / qcusbnet.c
index 77716fe..c48fd5d 100644 (file)
@@ -34,10 +34,12 @@ static DEFINE_MUTEX(qcusbnet_lock);
 int gobi_debug;
 static struct class *devclass;
 
-static void free_dev(struct kref *ref)
+static void free_dev(struct kobject *ref)
 {
-       struct qcusbnet *dev = container_of(ref, struct qcusbnet, refcount);
+       struct qcusbnet *dev = container_of(ref, struct qcusbnet, kobj);
+       mutex_lock(&qcusbnet_lock);
        list_del(&dev->node);
+       mutex_unlock(&qcusbnet_lock);
        kfree(dev);
 }
 
@@ -48,11 +50,13 @@ static void free_urb_with_skb(struct urb *urb)
        usb_free_urb(urb);
 }
 
+static struct kobj_type ktype_qcusbnet = {
+               .release        = free_dev,
+};
+
 void qcusbnet_put(struct qcusbnet *dev)
 {
-       mutex_lock(&qcusbnet_lock);
-       kref_put(&dev->refcount, free_dev);
-       mutex_unlock(&qcusbnet_lock);
+       kobject_put(&dev->kobj);
 }
 
 struct qcusbnet *qcusbnet_get(struct qcusbnet *key)
@@ -67,7 +71,7 @@ struct qcusbnet *qcusbnet_get(struct qcusbnet *key)
        mutex_lock(&qcusbnet_lock);
        list_for_each_entry(entry, &qcusbnet_list, node) {
                if (entry == key) {
-                       kref_get(&entry->refcount);
+                       kobject_get(&entry->kobj);
                        mutex_unlock(&qcusbnet_lock);
                        return entry;
                }
@@ -462,12 +466,16 @@ static const struct usb_device_id qc_vidpids[] = {
        MKVIDPID(0x1199, 0x9008),       /* Sierra Wireless Gobi 2000 */
        MKVIDPID(0x1199, 0x9009),       /* Sierra Wireless Gobi 2000 */
        MKVIDPID(0x1199, 0x900a),       /* Sierra Wireless Gobi 2000 */
+       MKVIDPID(0x1199, 0x9012),       /* Sierra Wireless Gobi 3000 */
+       MKVIDPID(0x1199, 0x9013),       /* Sierra Wireless Gobi 3000 */
        MKVIDPID(0x05c6, 0x9225),       /* Sony Gobi 2000 */
        MKVIDPID(0x05c6, 0x9235),       /* Top Global Gobi 2000 */
        MKVIDPID(0x05c6, 0x9275),       /* iRex Technologies Gobi 2000 */
 
        MKVIDPID(0x05c6, 0x920d),       /* Qualcomm Gobi 3000 */
-       MKVIDPID(0x1410, 0xa021),       /* Novatel Gobi 3000 */
+       MKVIDPID(0x0af0, 0x8120),       /* Option Gobi 3000 */
+       MKVIDPID(0x1410, 0xa021),       /* Novatel Gobi 3000 (E396) */
+       MKVIDPID(0x1410, 0xa023),       /* Novatel Gobi 3000 (E396U) */
        MKVIDPID(0x413c, 0x8194),       /* Dell Gobi 3000 */
        MKVIDPID(0x12D1, 0x14F1),       /* Sony Gobi 3000 */
        { }
@@ -577,7 +585,9 @@ int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids
        struct qcusbnet *dev;
        struct net_device_ops *netdevops;
        int i;
+       int addr_len;
        u8 *addr;
+       const char *id;
 
        status = usbnet_probe(iface, vidpids);
        if (status < 0) {
@@ -639,14 +649,16 @@ int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids
 
        memset(&(dev->usbnet->net->stats), 0, sizeof(struct net_device_stats));
 
-       memset(&(dev->meid), '0', 14);
+       memset(dev->meid, '0', sizeof(dev->meid));
+       memset(dev->imei, '0', sizeof(dev->imei));
 
        dev->valid = false;
        memset(&dev->qmi, 0, sizeof(dev->qmi));
 
        dev->qmi.devclass = devclass;
 
-       kref_init(&dev->refcount);
+       memset(&dev->kobj, 0, sizeof(dev->kobj));
+       kobject_init(&dev->kobj, &ktype_qcusbnet);
        INIT_LIST_HEAD(&dev->node);
        INIT_LIST_HEAD(&dev->qmi.clients);
        dev->workqueue = alloc_ordered_workqueue("gobi", 0);
@@ -677,11 +689,16 @@ int qcnet_probe(struct usb_interface *iface, const struct usb_device_id *vidpids
        list_add(&dev->node, &qcusbnet_list);
        mutex_unlock(&qcusbnet_lock);
 
-       /* After calling qc_register, MEID is valid */
-       addr = &usbnet->net->dev_addr[0];
-       for (i = 0; i < 6; i++)
-               addr[i] = (nibble(dev->meid[i*2+2]) << 4)+
-                       nibble(dev->meid[i*2+3]);
+       /* After calling qc_register, either MEID or IMEI is valid */
+       id = memchr_inv(dev->meid, '0', sizeof(dev->meid)) ?
+                       dev->meid : dev->imei;
+       addr = usbnet->net->dev_addr;
+       addr_len = usbnet->net->addr_len;
+       if (addr_len > ETH_ALEN)
+               addr_len = ETH_ALEN;
+
+       for (i = 0; i < addr_len; i++)
+               addr[i] = (nibble(id[i*2+2]) << 4) + nibble(id[i*2+3]);
        addr[0] &= 0xfe;                /* clear multicast bit */
        addr[0] |= 0x02;                /* set local assignment bit (IEEE802) */
 
@@ -707,7 +724,6 @@ static void qcnet_disconnect(struct usb_interface *intf)
 
        intf->needs_remote_wakeup = 0;
        netif_carrier_off(usbnet->net);
-       usbnet_disconnect(intf);
 
        qc_deregister(dev);
 
@@ -717,6 +733,7 @@ static void qcnet_disconnect(struct usb_interface *intf)
                list_del(&urb->urb_list);
                free_urb_with_skb(urb);
        }
+       usbnet_disconnect(intf);
        qcusbnet_put(dev);
 }