Add Qualcomm Gobi 2000/3000 driver.
Subsystems (3)
PATCH 1/1 Add Qualcomm Gobi 2000/3000 driver.
From: Elizabeth Jones <[email protected]>
This is a rewrite and unification of the Qualcomm Gobi 2000 and Gobi 3000 drivers, available at:
https://www.codeaurora.org/patches/quic/gobi/VT773.Gobi2000Drivers_03022011.tar.gz
https://www.codeaurora.org/patches/quic/gobi/Gobi3000Drivers1040_03022011.tar.gz
Both devices need firmware loaded in order to be useful.
BUG=chromium-os:5521
TEST=suite_Cellular
Review URL: http://codereview.chromium.org/6539018
--- drivers/net/usb/Kconfig | 6 + drivers/net/usb/Makefile | 1 + drivers/net/usb/gobi/Makefile | 2 + drivers/net/usb/gobi/README | 30 + drivers/net/usb/gobi/qcusbnet.c | 717 +++++++++++++++++ drivers/net/usb/gobi/qcusbnet.h | 28 + drivers/net/usb/gobi/qmi.c | 358 +++++++++ drivers/net/usb/gobi/qmi.h | 67 ++ drivers/net/usb/gobi/qmidevice.c | 1562 ++++++++++++++++++++++++++++++++++++++ drivers/net/usb/gobi/qmidevice.h | 35 + drivers/net/usb/gobi/structs.h | 96 +++ 11 files changed, 2902 insertions(+), 0 deletions(-) create mode 100644 drivers/net/usb/gobi/Makefile create mode 100644 drivers/net/usb/gobi/README create mode 100644 drivers/net/usb/gobi/qcusbnet.c create mode 100644 drivers/net/usb/gobi/qcusbnet.h create mode 100644 drivers/net/usb/gobi/qmi.c create mode 100644 drivers/net/usb/gobi/qmi.h create mode 100644 drivers/net/usb/gobi/qmidevice.c create mode 100644 drivers/net/usb/gobi/qmidevice.h create mode 100644 drivers/net/usb/gobi/structs.h
drivers/net/usb/Kconfig +6 -0
@@ -448,4 +448,10 @@ config USB_VL600 http://ubuntuforums.org/showpost.php?p=10589647&postcount=17 +config USB_NET_GOBI + tristate "Qualcomm Gobi" + depends on USB_USBNET + help + Qualcomm Gobi 2k/3k support. + endmenu
drivers/net/usb/Makefile +1 -0
@@ -28,4 +28,5 @@ obj-$(CONFIG_USB_SIERRA_NET) += sierra_net.o obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o obj-$(CONFIG_USB_VL600) += lg-vl600.o +obj-$(CONFIG_USB_NET_GOBI) += gobi/
drivers/net/usb/gobi/Makefile new file +2 -0
@@ -0,0 +1,2 @@ +obj-$(CONFIG_USB_NET_GOBI) += gobi.o +gobi-objs += qcusbnet.o qmidevice.o qmi.o
drivers/net/usb/gobi/README new file +30 -0
@@ -0,0 +1,30 @@ +Qualcomm Gobi 2000 driver +------------------------- + +This directory contains a driver for the Qualcomm Gobi 2000 CDMA/GSM modem. The +device speaks a protocol called QMI with its host; this driver's responsibility +is to multiplex transactions over QMI connections. Note that the relatively +simple encoding defined in qmi.h and qmi.c is only the encapsulating protocol; +the device speaks a more complex set of protocols embodied in the closed-source +Gobi SDK which are necessary to use advanced features of the device. +Furthermore, firmware (also proprietary) is needed to make the device useful. An +open-source firmware loader exists: http://www.codon.org.uk/~mjg59/gobi_loader/. + +Interfaces: +This driver presents two separate interfaces to userspace: +- usbN, an ordinary usb network interface +- /dev/qcqmiN, a channel to speak to the device using QMI messages +The latter is for use by the Gobi SDK. + +History: +This driver's original incarnation (of which this is a rewrite) was released +under GPLv2 by Qualcomm on their Code Aurora site: +https://www.codeaurora.org/gitweb/quic/la/?p=kernel/msm.git;a=commit;h=b5135a880f8942f990e8c2e383f7f876beacc55b + +Issues: +Help and commentary on any of the issues below would be appreciated. +- The locking in devqmi_close() is not threadsafe: we're walking a list of tasks + without holding a lock on the list. The original driver did this and I have + left it intact because I do not fully understand what's going on here. +- The driver seems to deadlock very occasionally (one in ~6000 reboots during + stress testing). I've had no luck tracking this problem down.
drivers/net/usb/gobi/qcusbnet.c new file +717 -0
717 lines not on this page · raw message
drivers/net/usb/gobi/qcusbnet.h new file +28 -0
@@ -0,0 +1,28 @@ +/* qcusbnet.h - gobi network device header +* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 and +* only version 2 as published by the Free Software Foundation. + +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +* 02110-1301, USA. +*/ + +#ifndef QCUSBNET_QCUSBNET_H +#define QCUSBNET_QCUSBNET_H + +#include "structs.h" + +extern int qc_suspend (struct usb_interface *iface,pm_message_t event); +extern void qcusbnet_put (struct qcusbnet *dev); +extern struct qcusbnet *qcusbnet_get (struct qcusbnet *dev); + +#endif /* !QCUSBNET_QCUSBNET_H */
drivers/net/usb/gobi/qmi.c new file +358 -0
358 lines not on this page · raw message
drivers/net/usb/gobi/qmi.h new file +67 -0
67 lines not on this page · raw message
drivers/net/usb/gobi/qmidevice.c new file +1562 -0
1,562 lines not on this page · raw message
drivers/net/usb/gobi/qmidevice.h new file +35 -0
@@ -0,0 +1,35 @@ +/* qmidevice.h - gobi QMI device header +* Copyright (c) 2010, Code Aurora Forum. All rights reserved. + +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 and +* only version 2 as published by the Free Software Foundation. + +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. + +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +* 02110-1301, USA. +*/ + +#ifndef QCUSBNET_QMIDEVICE_H +#define QCUSBNET_QMIDEVICE_H + +#include "structs.h" +#include "qmi.h" + +void qc_setdown (struct qcusbnet *dev,u8 reason); +void qc_cleardown (struct qcusbnet *dev,u8 reason); +bool qc_isdown (struct qcusbnet *dev,u8 reason); + +int qc_startread (struct qcusbnet *dev); +void qc_stopread (struct qcusbnet *dev); + +int qc_register (struct qcusbnet *dev); +void qc_deregister (struct qcusbnet *dev); + +#endif /* !QCUSBNET_QMIDEVICE_H */
drivers/net/usb/gobi/structs.h new file +96 -0
96 lines not on this page · raw message
Signature · 1 line
1.7.3.1
David Miller
maintainerFrom: Elly Jones <[email protected]>
Date: Wed, 13 Apr 2011 15:00:24 -0400
drivers/net/usb/gobi/qcusbnet.c line 48
Quoted 6 lines
drivers/net/usb/gobi/qcusbnet.c new file +6
-0+void qcusbnet_put (struct qcusbnet *dev) +{ +mutex_lock (&qcusbnet_lock); +kref_put (&dev->refcount, free_dev); +mutex_unlock (&qcusbnet_lock); +}
This locking looks excessive, and shouldn't be needed simply to release a reference to an object.
drivers/net/usb/gobi/qcusbnet.c line 77
Quoted 7 lines
drivers/net/usb/gobi/qcusbnet.c new file +7
-0+int qc_suspend (struct usb_interface *iface,pm_message_t event) +{ +struct usbnet *usbnet; +struct qcusbnet *dev; + +if (!iface) +return -ENOMEM ;
When is qc_suspend() called with a NULL iface arguemnt?
drivers/net/usb/gobi/qcusbnet.c line 119
Quoted 9 lines
drivers/net/usb/gobi/qcusbnet.c new file +9
-0+static int qc_resume (struct usb_interface *iface) +{ +struct usbnet *usbnet; +struct qcusbnet *dev; +int ret; +int oldstate; + +if (iface ==0 ) +return -ENOMEM ;
Likewise, and if it is needed use consistent tests for NULL. Testing against the integer "0" is definitely the wrong way.
drivers/net/usb/gobi/qcusbnet.c line 191
Quoted 2 lines
drivers/net/usb/gobi/qcusbnet.c new file +2
-0+if (usb_endpoint_dir_in (&endpoint->desc) + && !usb_endpoint_xfer_int (&endpoint->desc)) {
Please do it like this:
if (A &&
B) {Not like:
if (A && B
the latter looks awful at best.
drivers/net/usb/gobi/qcusbnet.c line 271
Quoted 10 lines
drivers/net/usb/gobi/qcusbnet.c new file +10
-0+if (!usbnet || !usbnet->net) { +DBG ("failed to get usbnet device\n" ); +return ; + } + + dev = (struct qcusbnet *)usbnet->data[0 ]; +if (!dev) { +DBG ("failed to get QMIDevice\n" ); +return ; + }
These NULL checks are everywhere! Do we really _ever_ create a full registered netdev with any of these things being NULL? I severely doubt it.
drivers/net/usb/gobi/qcusbnet.c line 304
Quoted 12 lines
drivers/net/usb/gobi/qcusbnet.c new file +12
-0+static int qcnet_worker (void *arg) +{ +struct list_head *node, *tmp; +unsigned long activeflags, listflags; +struct urbreq *req; +int status; +struct usb_device *usbdev; +struct worker *worker = arg; +if (!worker) { +DBG ("passed null pointer\n" ); +return -EINVAL ; + }
This NULL check is impossible, you register the worker function with an explicit &dev->worker argument, so seeing NULL here is impossible.
Quoted 8 lines
drivers/net/usb/gobi/qcusbnet.c new file +8
-0+static int qcnet_startxmit (struct sk_buff *skb,struct net_device *netdev) +{ +unsigned long listflags; +struct qcusbnet *dev; +struct worker *worker; +struct urbreq *req; +void *data; +struct usbnet *usbnet =netdev_priv (netdev);
drivers/net/usb/gobi/qcusbnet.c line 413
Quoted 10 lines
drivers/net/usb/gobi/qcusbnet.c new file +10
-0+ +DBG ("\n" ); + +if (!usbnet || !usbnet->net) { +DBG ("failed to get usbnet device\n" ); +return NETDEV_TX_BUSY ; + } + + dev = (struct qcusbnet *)usbnet->data[0 ]; +if (!dev) {
Again, kill this NULL check noise, all of it can't be necessary.
drivers/net/usb/gobi/qcusbnet.c line 457
Quoted 1 line
drivers/net/usb/gobi/qcusbnet.c new file +1
-0+ netdev->trans_start = jiffies;
Setting netdev->trans_start in drivers is expensive and deprecated, please set netdev_queue->trans_start instead.
Quoted 6 lines
drivers/net/usb/gobi/qcusbnet.c new file +6
-0+static int qcnet_open (struct net_device *netdev) +{ +int status =0 ; +struct qcusbnet *dev; +struct usbnet *usbnet =netdev_priv (netdev); +
drivers/net/usb/gobi/qcusbnet.c line 478
Quoted 10 lines
drivers/net/usb/gobi/qcusbnet.c new file +10
-0+if (!usbnet) { +DBG ("failed to get usbnet device\n" ); +return -ENXIO ; + } + + dev = (struct qcusbnet *)usbnet->data[0 ]; +if (!dev) { +DBG ("failed to get QMIDevice\n" ); +return -ENXIO ; + }
Again, excessive NULL checks.
Quoted 5 lines
drivers/net/usb/gobi/qcusbnet.c new file +5
-0+int qcnet_stop (struct net_device *netdev) +{ +struct qcusbnet *dev; +struct usbnet *usbnet =netdev_priv (netdev); +
drivers/net/usb/gobi/qcusbnet.c line 522
Quoted 10 lines
drivers/net/usb/gobi/qcusbnet.c new file +10
-0+if (!usbnet || !usbnet->net) { +DBG ("failed to get netdevice\n" ); +return -ENXIO ; + } + + dev = (struct qcusbnet *)usbnet->data[0 ]; +if (!dev) { +DBG ("failed to get QMIDevice\n" ); +return -ENXIO ; + }
Here too.
drivers/net/usb/gobi/qcusbnet.c line 591
Quoted 9 lines
drivers/net/usb/gobi/qcusbnet.c new file +9
-0+static u8 nibble (unsigned char c) +{ +if (likely (isdigit (c))) +return c -'0' ; + c =toupper (c); +if (likely (isxdigit (c))) +return 10 + c -'A' ; +return 0 ; +}
Remove this function and use hex_to_bin() instead.