kernel: backport raw-ip mode for newer QMI LTE modems
authorKoen Vandeputte <koen.vandeputte@ncentric.com>
Thu, 15 Feb 2018 14:15:56 +0000 (15:15 +0100)
committerJohn Crispin <john@phrozen.org>
Sun, 18 Feb 2018 08:34:32 +0000 (09:34 +0100)
Backport support for raw-ip mode including all known fixes afterwards.

Newer LTE modems only tend to support this mode, which was only
introduced in kernel 4.5.

Also backport support for the Quectel EC2x LTE modem series which is
a very popular device.

No custom changes were needed in order to apply these patches.

Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com>
target/linux/generic/patches-4.4/099-0001-usbnet-allow-mini-drivers-to-consume-L2-headers.patch [new file with mode: 0644]
target/linux/generic/patches-4.4/099-0002-net-qmi_wwan-support-raw-IP-mode.patch [new file with mode: 0644]
target/linux/generic/patches-4.4/099-0003-net-qmi_wwan-should-hold-RTNL-while-changing-netdev-.patch [new file with mode: 0644]
target/linux/generic/patches-4.4/099-0004-usbnet-fix-alignment-for-frames-with-no-ethernet-hea.patch [new file with mode: 0644]
target/linux/generic/patches-4.4/099-0005-qmi_wwan-Add-missing-skb_reset_mac_header-call.patch [new file with mode: 0644]
target/linux/generic/patches-4.4/099-0006-net-qmi_wwan-MDM9x30-specific-power-management.patch [new file with mode: 0644]
target/linux/generic/patches-4.4/099-0007-qmi_wwan-add-support-for-Quectel-EC21-and-EC25.patch [new file with mode: 0644]

diff --git a/target/linux/generic/patches-4.4/099-0001-usbnet-allow-mini-drivers-to-consume-L2-headers.patch b/target/linux/generic/patches-4.4/099-0001-usbnet-allow-mini-drivers-to-consume-L2-headers.patch
new file mode 100644 (file)
index 0000000..a1f19ea
--- /dev/null
@@ -0,0 +1,39 @@
+From 81e0ce79f2919dbd5f025894d29aa806af8695c7 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bjorn@mork.no>
+Date: Thu, 3 Dec 2015 19:24:20 +0100
+Subject: [PATCH] usbnet: allow mini-drivers to consume L2 headers
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Assume the minidriver has taken care of all L2 header parsing
+if it sets skb->protocol.  This allows the minidriver to
+support non-ethernet L2 headers, and even operate without
+any L2 header at all.
+
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Acked-by: Oliver Neukum <oneukum@suse.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/usbnet.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
+index 0744bf2ef2d6..0b0ba7ef14e4 100644
+--- a/drivers/net/usb/usbnet.c
++++ b/drivers/net/usb/usbnet.c
+@@ -324,7 +324,10 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb)
+               return;
+       }
+-      skb->protocol = eth_type_trans (skb, dev->net);
++      /* only update if unset to allow minidriver rx_fixup override */
++      if (skb->protocol == 0)
++              skb->protocol = eth_type_trans (skb, dev->net);
++
+       dev->net->stats.rx_packets++;
+       dev->net->stats.rx_bytes += skb->len;
+-- 
+2.7.4
+
diff --git a/target/linux/generic/patches-4.4/099-0002-net-qmi_wwan-support-raw-IP-mode.patch b/target/linux/generic/patches-4.4/099-0002-net-qmi_wwan-support-raw-IP-mode.patch
new file mode 100644 (file)
index 0000000..8b5fc37
--- /dev/null
@@ -0,0 +1,206 @@
+From 32f7adf633b9f99ad5089901bc7ebff57704aaa9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bjorn@mork.no>
+Date: Thu, 3 Dec 2015 19:24:21 +0100
+Subject: [PATCH] net: qmi_wwan: support "raw IP" mode
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+QMI wwan devices have traditionally emulated ethernet devices
+by default. But they have always had the capability of operating
+without any L2 header at all, transmitting and receiving "raw"
+IP packets over the USB link.  This firmware feature used to be
+configurable through the QMI management protocol.
+
+Traditionally there was no way to verify the firmware mode
+without attempting to change it.  And the firmware would often
+disallow changes anyway, i.e. due to a session already being
+established.  In some cases, this could be a hidden firmware
+internal session, completely outside host control.  For these
+reasons, sticking with the "well known" default mode was safest.
+
+But newer generations of QMI hardware and firmware have moved
+towards defaulting to "raw IP" mode instead, followed by an
+increasing number of bugs in the already buggy "802.3" firmware
+implementation. At the same time, the QMI management protocol
+gained the ability to detect the current mode.  This has enabled
+the userspace QMI management application to verify the current
+firmware mode without trying to modify it.
+
+Following this development, the latest QMI hardware and firmware
+(the MDM9x30 generation) has dropped support for "802.3" mode
+entirely. Support for "raw IP" framing in the driver is therefore
+necessary for these devices, and to a certain degree to work
+around problems with the previous generation,
+
+This patch adds support for "raw IP" framing for QMI devices,
+changing the netdev from an ethernet device to an ARPHRD_NONE
+p-t-p device when "raw IP" framing is enabled.
+
+The firmware setup is fully delegated to the QMI userspace
+management application, through simple tunneling of the QMI
+protocol. The driver will therefore not know which mode has been
+"negotiated" between firmware and userspace. Allowing userspace
+to inform the driver of the result through a sysfs switch is
+considered a better alternative than to change the well established
+clean delegation of firmware management to userspace.
+
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/qmi_wwan.c | 98 +++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 97 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
+index e3727b66d850..98add3bf8821 100644
+--- a/drivers/net/usb/qmi_wwan.c
++++ b/drivers/net/usb/qmi_wwan.c
+@@ -14,6 +14,7 @@
+ #include <linux/netdevice.h>
+ #include <linux/ethtool.h>
+ #include <linux/etherdevice.h>
++#include <linux/if_arp.h>
+ #include <linux/mii.h>
+ #include <linux/usb.h>
+ #include <linux/usb/cdc.h>
+@@ -48,11 +49,93 @@
+ struct qmi_wwan_state {
+       struct usb_driver *subdriver;
+       atomic_t pmcount;
+-      unsigned long unused;
++      unsigned long flags;
+       struct usb_interface *control;
+       struct usb_interface *data;
+ };
++enum qmi_wwan_flags {
++      QMI_WWAN_FLAG_RAWIP = 1 << 0,
++};
++
++static void qmi_wwan_netdev_setup(struct net_device *net)
++{
++      struct usbnet *dev = netdev_priv(net);
++      struct qmi_wwan_state *info = (void *)&dev->data;
++
++      if (info->flags & QMI_WWAN_FLAG_RAWIP) {
++              net->header_ops      = NULL;  /* No header */
++              net->type            = ARPHRD_NONE;
++              net->hard_header_len = 0;
++              net->addr_len        = 0;
++              net->flags           = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
++              netdev_dbg(net, "mode: raw IP\n");
++      } else if (!net->header_ops) { /* don't bother if already set */
++              ether_setup(net);
++              netdev_dbg(net, "mode: Ethernet\n");
++      }
++
++      /* recalculate buffers after changing hard_header_len */
++      usbnet_change_mtu(net, net->mtu);
++}
++
++static ssize_t raw_ip_show(struct device *d, struct device_attribute *attr, char *buf)
++{
++      struct usbnet *dev = netdev_priv(to_net_dev(d));
++      struct qmi_wwan_state *info = (void *)&dev->data;
++
++      return sprintf(buf, "%c\n", info->flags & QMI_WWAN_FLAG_RAWIP ? 'Y' : 'N');
++}
++
++static ssize_t raw_ip_store(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
++{
++      struct usbnet *dev = netdev_priv(to_net_dev(d));
++      struct qmi_wwan_state *info = (void *)&dev->data;
++      bool enable;
++      int err;
++
++      if (strtobool(buf, &enable))
++              return -EINVAL;
++
++      /* no change? */
++      if (enable == (info->flags & QMI_WWAN_FLAG_RAWIP))
++              return len;
++
++      /* we don't want to modify a running netdev */
++      if (netif_running(dev->net)) {
++              netdev_err(dev->net, "Cannot change a running device\n");
++              return -EBUSY;
++      }
++
++      /* let other drivers deny the change */
++      err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev->net);
++      err = notifier_to_errno(err);
++      if (err) {
++              netdev_err(dev->net, "Type change was refused\n");
++              return err;
++      }
++
++      if (enable)
++              info->flags |= QMI_WWAN_FLAG_RAWIP;
++      else
++              info->flags &= ~QMI_WWAN_FLAG_RAWIP;
++      qmi_wwan_netdev_setup(dev->net);
++      call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev->net);
++      return len;
++}
++
++static DEVICE_ATTR_RW(raw_ip);
++
++static struct attribute *qmi_wwan_sysfs_attrs[] = {
++      &dev_attr_raw_ip.attr,
++      NULL,
++};
++
++static struct attribute_group qmi_wwan_sysfs_attr_group = {
++      .name = "qmi",
++      .attrs = qmi_wwan_sysfs_attrs,
++};
++
+ /* default ethernet address used by the modem */
+ static const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3};
+@@ -80,6 +163,8 @@ static const u8 buggy_fw_addr[ETH_ALEN] = {0x00, 0xa0, 0xc6, 0x00, 0x00, 0x00};
+  */
+ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+ {
++      struct qmi_wwan_state *info = (void *)&dev->data;
++      bool rawip = info->flags & QMI_WWAN_FLAG_RAWIP;
+       __be16 proto;
+       /* This check is no longer done by usbnet */
+@@ -94,15 +179,25 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+               proto = htons(ETH_P_IPV6);
+               break;
+       case 0x00:
++              if (rawip)
++                      return 0;
+               if (is_multicast_ether_addr(skb->data))
+                       return 1;
+               /* possibly bogus destination - rewrite just in case */
+               skb_reset_mac_header(skb);
+               goto fix_dest;
+       default:
++              if (rawip)
++                      return 0;
+               /* pass along other packets without modifications */
+               return 1;
+       }
++      if (rawip) {
++              skb->dev = dev->net; /* normally set by eth_type_trans */
++              skb->protocol = proto;
++              return 1;
++      }
++
+       if (skb_headroom(skb) < ETH_HLEN)
+               return 0;
+       skb_push(skb, ETH_HLEN);
+@@ -326,6 +421,7 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
+               dev->net->dev_addr[0] &= 0xbf;  /* clear "IP" bit */
+       }
+       dev->net->netdev_ops = &qmi_wwan_netdev_ops;
++      dev->net->sysfs_groups[0] = &qmi_wwan_sysfs_attr_group;
+ err:
+       return status;
+ }
+-- 
+2.7.4
+
diff --git a/target/linux/generic/patches-4.4/099-0003-net-qmi_wwan-should-hold-RTNL-while-changing-netdev-.patch b/target/linux/generic/patches-4.4/099-0003-net-qmi_wwan-should-hold-RTNL-while-changing-netdev-.patch
new file mode 100644 (file)
index 0000000..48f7141
--- /dev/null
@@ -0,0 +1,107 @@
+From 6c730080e663b1d629f8aa89348291fbcdc46cd9 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bjorn@mork.no>
+Date: Sun, 6 Dec 2015 21:25:50 +0100
+Subject: [PATCH] net: qmi_wwan: should hold RTNL while changing netdev type
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The notifier calls were thrown in as a last-minute fix for an
+imagined "this device could be part of a bridge" problem. That
+revealed a certain lack of locking.  Not to mention testing...
+
+Avoid this splat:
+
+RTNL: assertion failed at net/core/dev.c (1639)
+CPU: 0 PID: 4293 Comm: bash Not tainted 4.4.0-rc3+ #358
+Hardware name: LENOVO 2776LEG/2776LEG, BIOS 6EET55WW (3.15 ) 12/19/2011
+ 0000000000000000 ffff8800ad253d60 ffffffff8122f7cf ffff8800ad253d98
+ ffff8800ad253d88 ffffffff813833ab 0000000000000002 ffff880230f48560
+ ffff880230a12900 ffff8800ad253da0 ffffffff813833da 0000000000000002
+Call Trace:
+ [<ffffffff8122f7cf>] dump_stack+0x4b/0x63
+ [<ffffffff813833ab>] call_netdevice_notifiers_info+0x3d/0x59
+ [<ffffffff813833da>] call_netdevice_notifiers+0x13/0x15
+ [<ffffffffa09be227>] raw_ip_store+0x81/0x193 [qmi_wwan]
+ [<ffffffff8131e149>] dev_attr_store+0x20/0x22
+ [<ffffffff811d858b>] sysfs_kf_write+0x49/0x50
+ [<ffffffff811d8027>] kernfs_fop_write+0x10a/0x151
+ [<ffffffff8117249a>] __vfs_write+0x26/0xa5
+ [<ffffffff81085ed4>] ? percpu_down_read+0x53/0x7f
+ [<ffffffff81174c9e>] ? __sb_start_write+0x5f/0xb0
+ [<ffffffff81174c9e>] ? __sb_start_write+0x5f/0xb0
+ [<ffffffff81172c37>] vfs_write+0xa3/0xe7
+ [<ffffffff811734ad>] SyS_write+0x50/0x7e
+ [<ffffffff8145c517>] entry_SYSCALL_64_fastpath+0x12/0x6f
+
+Fixes: 32f7adf633b9 ("net: qmi_wwan: support "raw IP" mode")
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/qmi_wwan.c | 22 +++++++++++++++-------
+ 1 file changed, 15 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
+index 98add3bf8821..babc84a3946c 100644
+--- a/drivers/net/usb/qmi_wwan.c
++++ b/drivers/net/usb/qmi_wwan.c
+@@ -16,6 +16,7 @@
+ #include <linux/etherdevice.h>
+ #include <linux/if_arp.h>
+ #include <linux/mii.h>
++#include <linux/rtnetlink.h>
+ #include <linux/usb.h>
+ #include <linux/usb/cdc.h>
+ #include <linux/usb/usbnet.h>
+@@ -92,7 +93,7 @@ static ssize_t raw_ip_store(struct device *d,  struct device_attribute *attr, co
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct qmi_wwan_state *info = (void *)&dev->data;
+       bool enable;
+-      int err;
++      int ret;
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+@@ -101,18 +102,22 @@ static ssize_t raw_ip_store(struct device *d,  struct device_attribute *attr, co
+       if (enable == (info->flags & QMI_WWAN_FLAG_RAWIP))
+               return len;
++      if (!rtnl_trylock())
++              return restart_syscall();
++
+       /* we don't want to modify a running netdev */
+       if (netif_running(dev->net)) {
+               netdev_err(dev->net, "Cannot change a running device\n");
+-              return -EBUSY;
++              ret = -EBUSY;
++              goto err;
+       }
+       /* let other drivers deny the change */
+-      err = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev->net);
+-      err = notifier_to_errno(err);
+-      if (err) {
++      ret = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev->net);
++      ret = notifier_to_errno(ret);
++      if (ret) {
+               netdev_err(dev->net, "Type change was refused\n");
+-              return err;
++              goto err;
+       }
+       if (enable)
+@@ -121,7 +126,10 @@ static ssize_t raw_ip_store(struct device *d,  struct device_attribute *attr, co
+               info->flags &= ~QMI_WWAN_FLAG_RAWIP;
+       qmi_wwan_netdev_setup(dev->net);
+       call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev->net);
+-      return len;
++      ret = len;
++err:
++      rtnl_unlock();
++      return ret;
+ }
+ static DEVICE_ATTR_RW(raw_ip);
+-- 
+2.7.4
+
diff --git a/target/linux/generic/patches-4.4/099-0004-usbnet-fix-alignment-for-frames-with-no-ethernet-hea.patch b/target/linux/generic/patches-4.4/099-0004-usbnet-fix-alignment-for-frames-with-no-ethernet-hea.patch
new file mode 100644 (file)
index 0000000..2511e0a
--- /dev/null
@@ -0,0 +1,73 @@
+From a4abd7a80addb4a9547f7dfc7812566b60ec505c Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bjorn@mork.no>
+Date: Wed, 6 Dec 2017 20:21:24 +0100
+Subject: [PATCH] usbnet: fix alignment for frames with no ethernet header
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The qmi_wwan minidriver support a 'raw-ip' mode where frames are
+received without any ethernet header. This causes alignment issues
+because the skbs allocated by usbnet are "IP aligned".
+
+Fix by allowing minidrivers to disable the additional alignment
+offset. This is implemented using a per-device flag, since the same
+minidriver also supports 'ethernet' mode.
+
+Fixes: 32f7adf633b9 ("net: qmi_wwan: support "raw IP" mode")
+Reported-and-tested-by: Jay Foster <jay@systech.com>
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/qmi_wwan.c | 2 ++
+ drivers/net/usb/usbnet.c   | 5 ++++-
+ include/linux/usb/usbnet.h | 1 +
+ 3 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
+index c750cf7c042b..304ec6555cd8 100644
+--- a/drivers/net/usb/qmi_wwan.c
++++ b/drivers/net/usb/qmi_wwan.c
+@@ -261,9 +261,11 @@ static void qmi_wwan_netdev_setup(struct net_device *net)
+               net->hard_header_len = 0;
+               net->addr_len        = 0;
+               net->flags           = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
++              set_bit(EVENT_NO_IP_ALIGN, &dev->flags);
+               netdev_dbg(net, "mode: raw IP\n");
+       } else if (!net->header_ops) { /* don't bother if already set */
+               ether_setup(net);
++              clear_bit(EVENT_NO_IP_ALIGN, &dev->flags);
+               netdev_dbg(net, "mode: Ethernet\n");
+       }
+diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
+index 80348b6a8646..d56fe32bf48d 100644
+--- a/drivers/net/usb/usbnet.c
++++ b/drivers/net/usb/usbnet.c
+@@ -484,7 +484,10 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
+               return -ENOLINK;
+       }
+-      skb = __netdev_alloc_skb_ip_align(dev->net, size, flags);
++      if (test_bit(EVENT_NO_IP_ALIGN, &dev->flags))
++              skb = __netdev_alloc_skb(dev->net, size, flags);
++      else
++              skb = __netdev_alloc_skb_ip_align(dev->net, size, flags);
+       if (!skb) {
+               netif_dbg(dev, rx_err, dev->net, "no rx skb\n");
+               usbnet_defer_kevent (dev, EVENT_RX_MEMORY);
+diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
+index a69877734c4e..e2ec3582e549 100644
+--- a/include/linux/usb/usbnet.h
++++ b/include/linux/usb/usbnet.h
+@@ -82,6 +82,7 @@ struct usbnet {
+ #             define EVENT_RX_KILL    10
+ #             define EVENT_LINK_CHANGE        11
+ #             define EVENT_SET_RX_MODE        12
++#             define EVENT_NO_IP_ALIGN        13
+ };
+ static inline struct usb_driver *driver_of(struct usb_interface *intf)
+-- 
+2.7.4
+
diff --git a/target/linux/generic/patches-4.4/099-0005-qmi_wwan-Add-missing-skb_reset_mac_header-call.patch b/target/linux/generic/patches-4.4/099-0005-qmi_wwan-Add-missing-skb_reset_mac_header-call.patch
new file mode 100644 (file)
index 0000000..08408e2
--- /dev/null
@@ -0,0 +1,81 @@
+From 0de0add10e587effa880c741c9413c874f16be91 Mon Sep 17 00:00:00 2001
+From: Kristian Evensen <kristian.evensen@gmail.com>
+Date: Tue, 7 Nov 2017 13:47:56 +0100
+Subject: [PATCH] qmi_wwan: Add missing skb_reset_mac_header-call
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+When we receive a packet on a QMI device in raw IP mode, we should call
+skb_reset_mac_header() to ensure that skb->mac_header contains a valid
+offset in the packet. While it shouldn't really matter, the packets have
+no MAC header and the interface is configured as-such, it seems certain
+parts of the network stack expects a "good" value in skb->mac_header.
+
+Without the skb_reset_mac_header() call added in this patch, for example
+shaping traffic (using tc) triggers the following oops on the first
+received packet:
+
+[  303.642957] skbuff: skb_under_panic: text:8f137918 len:177 put:67 head:8e4b0f00 data:8e4b0eff tail:0x8e4b0fb0 end:0x8e4b1520 dev:wwan0
+[  303.655045] Kernel bug detected[#1]:
+[  303.658622] CPU: 1 PID: 1002 Comm: logd Not tainted 4.9.58 #0
+[  303.664339] task: 8fdf05e0 task.stack: 8f15c000
+[  303.668844] $ 0   : 00000000 00000001 0000007a 00000000
+[  303.674062] $ 4   : 8149a2fc 8149a2fc 8149ce20 00000000
+[  303.679284] $ 8   : 00000030 3878303a 31623465 20303235
+[  303.684510] $12   : ded731e3 2626a277 00000000 03bd0000
+[  303.689747] $16   : 8ef62b40 00000043 8f137918 804db5fc
+[  303.694978] $20   : 00000001 00000004 8fc13800 00000003
+[  303.700215] $24   : 00000001 8024ab10
+[  303.705442] $28   : 8f15c000 8fc19cf0 00000043 802cc920
+[  303.710664] Hi    : 00000000
+[  303.713533] Lo    : 74e58000
+[  303.716436] epc   : 802cc920 skb_panic+0x58/0x5c
+[  303.721046] ra    : 802cc920 skb_panic+0x58/0x5c
+[  303.725639] Status: 11007c03 KERNEL EXL IE
+[  303.729823] Cause : 50800024 (ExcCode 09)
+[  303.733817] PrId  : 0001992f (MIPS 1004Kc)
+[  303.737892] Modules linked in: rt2800pci rt2800mmio rt2800lib qcserial ppp_async option usb_wwan rt2x00pci rt2x00mmio rt2x00lib rndis_host qmi_wwan ppp_generic nf_nat_pptp nf_conntrack_pptp nf_conntrack_ipv6 mt76x2i
+Process logd (pid: 1002, threadinfo=8f15c000, task=8fdf05e0, tls=77b3eee4)
+[  303.962509] Stack : 00000000 80408990 8f137918 000000b1 00000043 8e4b0f00 8e4b0eff 8e4b0fb0
+[  303.970871]         8e4b1520 8fec1800 00000043 802cd2a4 6e000045 00000043 00000000 8ef62000
+[  303.979219]         8eef5d00 8ef62b40 8fea7300 8f137918 00000000 00000000 0002bb01 793e5664
+[  303.987568]         8ef08884 00000001 8fea7300 00000002 8fc19e80 8eef5d00 00000006 00000003
+[  303.995934]         00000000 8030ba90 00000003 77ab3fd0 8149dc80 8004d1bc 8f15c000 8f383700
+[  304.004324]         ...
+[  304.006767] Call Trace:
+[  304.009241] [<802cc920>] skb_panic+0x58/0x5c
+[  304.013504] [<802cd2a4>] skb_push+0x78/0x90
+[  304.017783] [<8f137918>] 0x8f137918
+[  304.021269] Code: 00602825  0c02a3b4  24842888 <000c000d> 8c870060  8c8200a0  0007382b  00070336  8c88005c
+[  304.031034]
+[  304.032805] ---[ end trace b778c482b3f0bda9 ]---
+[  304.041384] Kernel panic - not syncing: Fatal exception in interrupt
+[  304.051975] Rebooting in 3 seconds..
+
+While the oops is for a 4.9-kernel, I was able to trigger the same oops with
+net-next as of yesterday.
+
+Fixes: 32f7adf633b9 ("net: qmi_wwan: support "raw IP" mode")
+Signed-off-by: Kristian Evensen <kristian.evensen@gmail.com>
+Acked-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/qmi_wwan.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
+index a4f229edcceb..8d4a6f7cba61 100644
+--- a/drivers/net/usb/qmi_wwan.c
++++ b/drivers/net/usb/qmi_wwan.c
+@@ -499,6 +499,7 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+               return 1;
+       }
+       if (rawip) {
++              skb_reset_mac_header(skb);
+               skb->dev = dev->net; /* normally set by eth_type_trans */
+               skb->protocol = proto;
+               return 1;
+-- 
+2.7.4
+
diff --git a/target/linux/generic/patches-4.4/099-0006-net-qmi_wwan-MDM9x30-specific-power-management.patch b/target/linux/generic/patches-4.4/099-0006-net-qmi_wwan-MDM9x30-specific-power-management.patch
new file mode 100644 (file)
index 0000000..c26a86b
--- /dev/null
@@ -0,0 +1,97 @@
+From 93725149794d3d418cf1eddcae60c7b536c5faa1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bjorn@mork.no>
+Date: Thu, 3 Dec 2015 19:24:18 +0100
+Subject: [PATCH] net: qmi_wwan: MDM9x30 specific power management
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+MDM9x30 based modems appear to go into a deeper sleep when
+suspended without "Remote Wakeup" enabled.  The QMI interface
+will not respond unless a "set DTR" control request is sent
+on resume. The effect is similar to a QMI_CTL SYNC request,
+resetting (some of) the firmware state.
+
+We allow userspace sessions to span multiple character device
+open/close sequences.  This means that userspace can depend
+on firmware state while both the netdev and the character
+device are closed.  We have disabled "needs_remote_wakeup" at
+this point to allow devices without remote wakeup support to
+be auto-suspended.
+
+To make sure the MDM9x30 keeps firmware state, we need to
+keep "needs_remote_wakeup" always set. We also need to
+issue a "set DTR" request to enable the QMI interface.
+
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/qmi_wwan.c | 38 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 38 insertions(+)
+
+diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
+index 9a5be8b85186..fc9dd452a3b5 100644
+--- a/drivers/net/usb/qmi_wwan.c
++++ b/drivers/net/usb/qmi_wwan.c
+@@ -223,6 +223,20 @@ static int qmi_wwan_register_subdriver(struct usbnet *dev)
+       return rv;
+ }
++/* Send CDC SetControlLineState request, setting or clearing the DTR.
++ * "Required for Autoconnect and 9x30 to wake up" according to the
++ * GobiNet driver. The requirement has been verified on an MDM9230
++ * based Sierra Wireless MC7455
++ */
++static int qmi_wwan_change_dtr(struct usbnet *dev, bool on)
++{
++      u8 intf = dev->intf->cur_altsetting->desc.bInterfaceNumber;
++
++      return usbnet_write_cmd(dev, USB_CDC_REQ_SET_CONTROL_LINE_STATE,
++                              USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
++                              on ? 0x01 : 0x00, intf, NULL, 0);
++}
++
+ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
+ {
+       int status = -1;
+@@ -280,6 +294,24 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
+               usb_driver_release_interface(driver, info->data);
+       }
++      /* disabling remote wakeup on MDM9x30 devices has the same
++       * effect as clearing DTR. The device will not respond to QMI
++       * requests until we set DTR again.  This is similar to a
++       * QMI_CTL SYNC request, clearing a lot of firmware state
++       * including the client ID allocations.
++       *
++       * Our usage model allows a session to span multiple
++       * open/close events, so we must prevent the firmware from
++       * clearing out state the clients might need.
++       *
++       * MDM9x30 is the first QMI chipset with USB3 support. Abuse
++       * this fact to enable the quirk.
++       */
++      if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
++              qmi_wwan_manage_power(dev, 1);
++              qmi_wwan_change_dtr(dev, true);
++      }
++
+       /* Never use the same address on both ends of the link, even if the
+        * buggy firmware told us to. Or, if device is assigned the well-known
+        * buggy firmware MAC address, replace it with a random address,
+@@ -307,6 +339,12 @@ static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf)
+       if (info->subdriver && info->subdriver->disconnect)
+               info->subdriver->disconnect(info->control);
++      /* disable MDM9x30 quirk */
++      if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
++              qmi_wwan_change_dtr(dev, false);
++              qmi_wwan_manage_power(dev, 0);
++      }
++
+       /* allow user to unbind using either control or data */
+       if (intf == info->control)
+               other = info->data;
+-- 
+2.7.4
+
diff --git a/target/linux/generic/patches-4.4/099-0007-qmi_wwan-add-support-for-Quectel-EC21-and-EC25.patch b/target/linux/generic/patches-4.4/099-0007-qmi_wwan-add-support-for-Quectel-EC21-and-EC25.patch
new file mode 100644 (file)
index 0000000..3b80327
--- /dev/null
@@ -0,0 +1,98 @@
+From 9a765881bf3dcd32847d7108cf48cb04a4ed993f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bjorn@mork.no>
+Date: Mon, 10 Oct 2016 21:12:49 +0200
+Subject: [PATCH] qmi_wwan: add support for Quectel EC21 and EC25
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The Quectel EC21 and EC25 need the same "set DTR" request as devices
+based on the MDM9230 chipset, but has no USB3 support. Our best guess
+is that the "set DTR" functionality depends on chipset and/or
+baseband firmware generation. But USB3 is still an optional feature.
+
+Since we cannot enable this unconditionally for all older devices, and
+there doesn't appear to be anything we can use in the USB descriptors
+to identify these chips, we are forced to use a device specific quirk
+flag.
+
+Reported-and-tested-by: Sebastian Sjoholm <sebastian.sjoholm@gmail.com>
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+ drivers/net/usb/qmi_wwan.c | 30 ++++++++++++++++++++++++++++--
+ 1 file changed, 28 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
+index 9d1fce8a6e84..3ff76c6db4f6 100644
+--- a/drivers/net/usb/qmi_wwan.c
++++ b/drivers/net/usb/qmi_wwan.c
+@@ -59,6 +59,10 @@ enum qmi_wwan_flags {
+       QMI_WWAN_FLAG_RAWIP = 1 << 0,
+ };
++enum qmi_wwan_quirks {
++      QMI_WWAN_QUIRK_DTR = 1 << 0,    /* needs "set DTR" request */
++};
++
+ static void qmi_wwan_netdev_setup(struct net_device *net)
+ {
+       struct usbnet *dev = netdev_priv(net);
+@@ -411,9 +415,14 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
+        * clearing out state the clients might need.
+        *
+        * MDM9x30 is the first QMI chipset with USB3 support. Abuse
+-       * this fact to enable the quirk.
++       * this fact to enable the quirk for all USB3 devices.
++       *
++       * There are also chipsets with the same "set DTR" requirement
++       * but without USB3 support.  Devices based on these chips
++       * need a quirk flag in the device ID table.
+        */
+-      if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
++      if (dev->driver_info->data & QMI_WWAN_QUIRK_DTR ||
++          le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
+               qmi_wwan_manage_power(dev, 1);
+               qmi_wwan_change_dtr(dev, true);
+       }
+@@ -526,6 +535,16 @@ static const struct driver_info   qmi_wwan_info = {
+       .rx_fixup       = qmi_wwan_rx_fixup,
+ };
++static const struct driver_info       qmi_wwan_info_quirk_dtr = {
++      .description    = "WWAN/QMI device",
++      .flags          = FLAG_WWAN,
++      .bind           = qmi_wwan_bind,
++      .unbind         = qmi_wwan_unbind,
++      .manage_power   = qmi_wwan_manage_power,
++      .rx_fixup       = qmi_wwan_rx_fixup,
++      .data           = QMI_WWAN_QUIRK_DTR,
++};
++
+ #define HUAWEI_VENDOR_ID      0x12D1
+ /* map QMI/wwan function by a fixed interface number */
+@@ -533,6 +552,11 @@ static const struct driver_info   qmi_wwan_info = {
+       USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \
+       .driver_info = (unsigned long)&qmi_wwan_info
++/* devices requiring "set DTR" quirk */
++#define QMI_QUIRK_SET_DTR(vend, prod, num) \
++      USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \
++      .driver_info = (unsigned long)&qmi_wwan_info_quirk_dtr
++
+ /* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */
+ #define QMI_GOBI1K_DEVICE(vend, prod) \
+       QMI_FIXED_INTF(vend, prod, 3)
+@@ -895,6 +919,8 @@ static const struct usb_device_id products[] = {
+       {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)},    /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */
+       {QMI_FIXED_INTF(0x22de, 0x9061, 3)},    /* WeTelecom WPD-600N */
+       {QMI_FIXED_INTF(0x1e0e, 0x9001, 5)},    /* SIMCom 7230E */
++      {QMI_QUIRK_SET_DTR(0x2c7c, 0x0125, 4)}, /* Quectel EC25, EC20 R2.0  Mini PCIe */
++      {QMI_QUIRK_SET_DTR(0x2c7c, 0x0121, 4)}, /* Quectel EC21 Mini PCIe */
+       /* 4. Gobi 1000 devices */
+       {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)},    /* Acer Gobi Modem Device */
+-- 
+2.7.4
+