OLD | NEW |
---|---|
1 /* qcusbnet.c - gobi network device | 1 /* qcusbnet.c - gobi network device |
2 * Copyright (c) 2010, Code Aurora Forum. All rights reserved. | 2 * Copyright (c) 2010, Code Aurora Forum. All rights reserved. |
3 | 3 |
4 * This program is free software; you can redistribute it and/or modify | 4 * This program is free software; you can redistribute it and/or modify |
5 * it under the terms of the GNU General Public License version 2 and | 5 * it under the terms of the GNU General Public License version 2 and |
6 * only version 2 as published by the Free Software Foundation. | 6 * only version 2 as published by the Free Software Foundation. |
7 | 7 |
8 * This program is distributed in the hope that it will be useful, | 8 * This program is distributed in the hope that it will be useful, |
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
11 * GNU General Public License for more details. | 11 * GNU General Public License for more details. |
12 | 12 |
13 * You should have received a copy of the GNU General Public License | 13 * You should have received a copy of the GNU General Public License |
14 * along with this program; if not, write to the Free Software | 14 * along with this program; if not, write to the Free Software |
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | 15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
16 * 02110-1301, USA. | 16 * 02110-1301, USA. |
17 */ | 17 */ |
18 | 18 |
19 #include "structs.h" | 19 #include "structs.h" |
20 #include "qmidevice.h" | 20 #include "qmidevice.h" |
21 #include "qmi.h" | 21 #include "qmi.h" |
22 #include "qcusbnet.h" | 22 #include "qcusbnet.h" |
23 | 23 |
24 #define DRIVER_VERSION "1.0.110" | 24 #define DRIVER_VERSION "1.0.110" |
25 #define DRIVER_AUTHOR "Qualcomm Innovation Center" | 25 #define DRIVER_AUTHOR "Qualcomm Innovation Center" |
26 #define DRIVER_DESC "QCUSBNet2k" | 26 #define DRIVER_DESC "QCUSBNet2k" |
27 | 27 |
28 static LIST_HEAD(qcusbnet_list); | |
29 static DEFINE_MUTEX(qcusbnet_lock); | |
30 | |
28 int qcusbnet_debug; | 31 int qcusbnet_debug; |
29 static struct class *devclass; | 32 static struct class *devclass; |
30 | 33 |
34 static void free_dev(struct kref *ref) | |
35 { | |
36 struct qcusbnet *dev = container_of(ref, struct qcusbnet, refcount); | |
37 list_del(&dev->node); | |
38 kfree(dev); | |
39 } | |
40 | |
41 void qcusbnet_put(struct qcusbnet *dev) | |
42 { | |
43 mutex_lock(&qcusbnet_lock); | |
44 kref_put(&dev->refcount, free_dev); | |
45 mutex_unlock(&qcusbnet_lock); | |
46 } | |
47 | |
48 struct qcusbnet *qcusbnet_get(struct qcusbnet *key) | |
49 { | |
50 /* Given a putative qcusbnet struct, return either the struct itself | |
51 * (with a ref taken) if the struct is still visible, or NULL if it's | |
52 * not. This prevents object-visibility races where someone is looking | |
53 * up an object as the last ref gets dropped; dropping the last ref and | |
54 * removing the object from the list are atomic with respect to getting | |
55 * a new ref. */ | |
56 struct qcusbnet *entry = NULL; | |
57 mutex_lock(&qcusbnet_lock); | |
58 list_for_each_entry(entry, &qcusbnet_list, node) { | |
59 if (entry == key) { | |
60 kref_get(&entry->refcount); | |
61 mutex_unlock(&qcusbnet_lock); | |
62 return entry; | |
63 } | |
64 } | |
65 mutex_unlock(&qcusbnet_lock); | |
66 return NULL; | |
67 } | |
68 | |
31 int qc_suspend(struct usb_interface *iface, pm_message_t event) | 69 int qc_suspend(struct usb_interface *iface, pm_message_t event) |
32 { | 70 { |
33 struct usbnet *usbnet; | 71 struct usbnet *usbnet; |
34 struct qcusbnet *dev; | 72 struct qcusbnet *dev; |
35 | 73 |
36 if (!iface) | 74 if (!iface) |
37 return -ENOMEM; | 75 return -ENOMEM; |
38 | 76 |
39 usbnet = usb_get_intfdata(iface); | 77 usbnet = usb_get_intfdata(iface); |
40 | 78 |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
173 out->desc.bEndpointAddress); | 211 out->desc.bEndpointAddress); |
174 | 212 |
175 return 0; | 213 return 0; |
176 } | 214 } |
177 | 215 |
178 static void qcnet_unbind(struct usbnet *usbnet, struct usb_interface *iface) | 216 static void qcnet_unbind(struct usbnet *usbnet, struct usb_interface *iface) |
179 { | 217 { |
180 struct qcusbnet *dev = (struct qcusbnet *)usbnet->data[0]; | 218 struct qcusbnet *dev = (struct qcusbnet *)usbnet->data[0]; |
181 | 219 |
182 netif_carrier_off(usbnet->net); | 220 netif_carrier_off(usbnet->net); |
221 /* See the comments in qcusbnet_get() for an explanation of why we need | |
Jason Glasgow
2011/03/10 18:36:28
comment should disappear.
Elly Fong-Jones
2011/03/10 19:15:46
Done.
| |
222 * this lock. */ | |
183 qc_deregister(dev); | 223 qc_deregister(dev); |
184 | 224 |
225 usbnet->net->netdev_ops = NULL; | |
Jason Glasgow
2011/03/10 18:36:28
still wrong.
Elly Fong-Jones
2011/03/10 19:15:46
Done.
| |
185 kfree(usbnet->net->netdev_ops); | 226 kfree(usbnet->net->netdev_ops); |
186 » usbnet->net->netdev_ops = NULL; | 227 » /* drop the list's ref */ |
187 | 228 » qcusbnet_put(dev); |
188 » kfree(dev); | |
189 } | 229 } |
190 | 230 |
191 static void qcnet_urbhook(struct urb *urb) | 231 static void qcnet_urbhook(struct urb *urb) |
192 { | 232 { |
193 unsigned long flags; | 233 unsigned long flags; |
194 struct worker *worker = urb->context; | 234 struct worker *worker = urb->context; |
195 if (!worker) { | 235 if (!worker) { |
196 DBG("bad context\n"); | 236 DBG("bad context\n"); |
197 return; | 237 return; |
198 } | 238 } |
(...skipping 384 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
583 usbnet->net->netdev_ops = netdevops; | 623 usbnet->net->netdev_ops = netdevops; |
584 | 624 |
585 memset(&(dev->usbnet->net->stats), 0, sizeof(struct net_device_stats)); | 625 memset(&(dev->usbnet->net->stats), 0, sizeof(struct net_device_stats)); |
586 | 626 |
587 dev->iface = iface; | 627 dev->iface = iface; |
588 memset(&(dev->meid), '0', 14); | 628 memset(&(dev->meid), '0', 14); |
589 | 629 |
590 DBG("Mac Address: %pM\n", dev->usbnet->net->dev_addr); | 630 DBG("Mac Address: %pM\n", dev->usbnet->net->dev_addr); |
591 | 631 |
592 dev->valid = false; | 632 dev->valid = false; |
593 » memset(&dev->qmi, 0, sizeof(struct qmidev)); | 633 » memset(&dev->qmi, 0, sizeof(dev->qmi)); |
594 | 634 |
595 dev->qmi.devclass = devclass; | 635 dev->qmi.devclass = devclass; |
596 | 636 |
637 kref_init(&dev->refcount); | |
638 INIT_LIST_HEAD(&dev->node); | |
597 INIT_LIST_HEAD(&dev->qmi.clients); | 639 INIT_LIST_HEAD(&dev->qmi.clients); |
598 init_completion(&dev->worker.work); | 640 init_completion(&dev->worker.work); |
599 spin_lock_init(&dev->qmi.clients_lock); | 641 spin_lock_init(&dev->qmi.clients_lock); |
600 | 642 |
601 dev->down = 0; | 643 dev->down = 0; |
602 qc_setdown(dev, DOWN_NO_NDIS_CONNECTION); | 644 qc_setdown(dev, DOWN_NO_NDIS_CONNECTION); |
603 qc_setdown(dev, DOWN_NET_IFACE_STOPPED); | 645 qc_setdown(dev, DOWN_NET_IFACE_STOPPED); |
604 | 646 |
605 status = qc_register(dev); | 647 status = qc_register(dev); |
606 if (status) { | 648 if (status) { |
607 qc_deregister(dev); | 649 qc_deregister(dev); |
650 } else { | |
651 mutex_lock(&qcusbnet_lock); | |
652 /* Give our initial ref to the list */ | |
653 list_add(&dev->node, &qcusbnet_list); | |
654 mutex_unlock(&qcusbnet_lock); | |
608 } | 655 } |
609 | 656 |
610 return status; | 657 return status; |
611 } | 658 } |
612 EXPORT_SYMBOL_GPL(qcnet_probe); | 659 EXPORT_SYMBOL_GPL(qcnet_probe); |
613 | 660 |
614 static struct usb_driver qcusbnet = { | 661 static struct usb_driver qcusbnet = { |
615 .name = "QCUSBNet2k", | 662 .name = "QCUSBNet2k", |
616 .id_table = qc_vidpids, | 663 .id_table = qc_vidpids, |
617 .probe = qcnet_probe, | 664 .probe = qcnet_probe, |
(...skipping 22 matching lines...) Expand all Loading... | |
640 } | 687 } |
641 module_exit(modexit); | 688 module_exit(modexit); |
642 | 689 |
643 MODULE_VERSION(DRIVER_VERSION); | 690 MODULE_VERSION(DRIVER_VERSION); |
644 MODULE_AUTHOR(DRIVER_AUTHOR); | 691 MODULE_AUTHOR(DRIVER_AUTHOR); |
645 MODULE_DESCRIPTION(DRIVER_DESC); | 692 MODULE_DESCRIPTION(DRIVER_DESC); |
646 MODULE_LICENSE("Dual BSD/GPL"); | 693 MODULE_LICENSE("Dual BSD/GPL"); |
647 | 694 |
648 module_param(qcusbnet_debug, bool, S_IRUGO | S_IWUSR); | 695 module_param(qcusbnet_debug, bool, S_IRUGO | S_IWUSR); |
649 MODULE_PARM_DESC(qcusbnet_debug, "Debugging enabled or not"); | 696 MODULE_PARM_DESC(qcusbnet_debug, "Debugging enabled or not"); |
OLD | NEW |