Re: Huawei/Qualcomm "NDIS" driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Dan Williams <dcbw@xxxxxxxxxx> writes:

> Right; though at some point here we do want to start enabling the pseudo
> ethernet interfaces on these devices too. 

I think I have something going now.  But let me just add a little
disclaimer:  There are things going on here which I do *not*
understand.  

I don't know how it happened, but all of a sudden I started receiving
packets which looked OK except that they didn't have an ethernet header.
So I hacked adding a fake one into cdc_ether.  But while I was at this,
I started receiving frames _with_ an ethernet header, which made
cdc_ether work nearly without any changes at all.  Anyway, I am
completely unable to recreate that now, so I'm attaching a hacky driver
which basically just adds an ethernet IP type header to every received
frame. It also modifies cdc_ether to allow a vendor specific data
interface.

Be nice to me.  I haven't the faintest clue how to really do this.  I
just want to demostrate something that Works For Me(tm).

To connect:

1) open a terminal session with either the modem or pcui serial interface

2) enter pin code as usual: AT+CPIN=xxxx

3) connect using: AT^NDISDUP=1,1,"yourapn"
     (I believe the first number is the CGDCONT - this may require an
     existing entry, or overwrite anything already there)

4) Retrieve IP addresses using: AT^DHCP?
     Note that this has side effects: Packets will not flow until this
     command is sent

5) configure the interface manuall with the output of the previous command:
    ^DHCP:4d92124d,fcffffff,4e92124d,4e92124d,470d5c1,c60f4382,100000000,50000000

     $ perl -e 'print join(",",map { join(".", unpack("C4", pack("L", hex))) } split /,/, shift),"\n"' 4d92124d,fcffffff,4e92124d,4e92124d,470d5c1,c60f4382
     77.18.146.77,255.255.255.252,77.18.146.78,77.18.146.78,193.213.112.4,130.67.15.198
   
     # ifconfig wwan0 77.18.146.77 netmask 255.255.255.252
     # ip route add default via 77.18.146.78

Things should now work.

To the speed discussion:  I just got 55 Mbits/s down and 25 Mbits/s up
using this driver and configuration.  That's nice :-)  And BTW:  I got
the last two numbers in the ^DHCP output wrong yesterday.  They are
probably the connected downstream/upstream speed.  

This driver is definitely not ready for submission yet.  Need to be
understood first.  But I would appreciate it if others could try and see
if the ethernet interface on other Huawei-modems can be enabled.  Be
aware though:  There are a number of different protocols in use.  This
will only support one of them.  



Bjørn

>From f3430a06225b7921ac7ebfbb58e19419ba0e0c84 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= <bjorn@xxxxxxx>
Date: Wed, 30 Nov 2011 13:45:52 +0100
Subject: [PATCH] huawei_wwan: support Huawei vendor specific "NDIS" interface
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Some Huawei LTE/3G modems hides a CDC Ethernet conforming
interface behind a vendor specific descriptor.  But they
include the necessary CDC class specific descriptors, so
we can reuse most of the cdc_ether driver.

This changes cdc_ether to allow the CDC Union to point to
a vendor specific data interface as long as the control
interface is vendor specific.

The control of the Huawei interface is not fully understood.
Most of the time, the received frames are missing an ethernet
header.  Add a new driver to fixup this.

Signed-off-by: Bjørn Mork <bjorn@xxxxxxx>
---
 drivers/net/usb/Kconfig       |    7 ++
 drivers/net/usb/Makefile      |    1 +
 drivers/net/usb/cdc_ether.c   |    8 ++-
 drivers/net/usb/huawei_wwan.c |  140 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 154 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/usb/huawei_wwan.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 84d4608..097ad13 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -458,4 +458,11 @@ config USB_VL600
 
 	  http://ubuntuforums.org/showpost.php?p=10589647&postcount=17
 
+config USB_NET_HUAWEI_WWAN
+	tristate "Huawei 3G and LTE modems"
+	depends on USB_NET_CDCETHER
+	help
+	  Huawei 3G and LTE modems with an Ethernet like interface
+
+
 endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index c203fa2..faab9ce 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -29,4 +29,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_HUAWEI_WWAN)	+= huawei_wwan.o
 
diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c
index c924ea2..5375549 100644
--- a/drivers/net/usb/cdc_ether.c
+++ b/drivers/net/usb/cdc_ether.c
@@ -211,8 +211,12 @@ int usbnet_generic_cdc_bind(struct usbnet *dev, struct usb_interface *intf)
 
 			/* a data interface altsetting does the real i/o */
 			d = &info->data->cur_altsetting->desc;
-			if (d->bInterfaceClass != USB_CLASS_CDC_DATA) {
-				dev_dbg(&intf->dev, "slave class %u\n",
+			if ((d->bInterfaceClass != USB_CLASS_CDC_DATA) &&
+				/* Allow vendor specific data interface iff 
+				   control interface is vendor specific */
+				!(info->control->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
+					d->bInterfaceClass == USB_CLASS_VENDOR_SPEC)) {
+				dev_dbg(&intf->dev, "xslave class %u\n",
 					d->bInterfaceClass);
 				goto bad_desc;
 			}
diff --git a/drivers/net/usb/huawei_wwan.c b/drivers/net/usb/huawei_wwan.c
new file mode 100644
index 0000000..8a9ba28
--- /dev/null
+++ b/drivers/net/usb/huawei_wwan.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) Bjørn Mork <bjorn@xxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// #define	DEBUG			// error path messages, extra info
+// #define	VERBOSE			// more; success messages
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/workqueue.h>
+#include <linux/mii.h>
+#include <linux/crc32.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb/usbnet.h>
+
+static int huawei_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct ethhdr *hdr  = (void *)skb->data;
+	static unsigned char peer[ETH_ALEN];
+
+	if (hdr->h_proto == htons(ETH_P_IP))
+		return 1;
+
+	if (hdr->h_proto == htons(ETH_P_ARP)) {
+		memcpy(peer, hdr->h_source, ETH_ALEN);
+		netdev_dbg(dev->net, "updating peer address to %pM\n", peer);
+		return 1;
+	}
+
+//	netdev_dbg(dev->net, "hdr->h_proto=%04x\n", ntohs(hdr->h_proto));
+
+	/* add room for ethernet header */
+	if (pskb_expand_head(skb, sizeof(*hdr), 0, GFP_ATOMIC) < 0)
+		return 0;
+
+	hdr = (void *)skb_push(skb, sizeof(*hdr));
+	memcpy(hdr->h_dest, dev->net->dev_addr, ETH_ALEN);
+	memcpy(hdr->h_source, peer, ETH_ALEN);
+	hdr->h_proto = htons(ETH_P_IP);
+
+	return 1;
+}
+
+
+void huawei_cdc_status(struct usbnet *dev, struct urb *urb)
+{
+	struct usb_cdc_notification	*event;
+
+	if (urb->actual_length < sizeof *event)
+		return;
+
+	event = urb->transfer_buffer;
+	switch (event->bNotificationType) {
+	case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
+		netdev_dbg(dev->net, "received USB_CDC_NOTIFY_RESPONSE_AVAILABLE\n");
+		break;
+	default:
+		usbnet_cdc_status(dev, urb);
+	}
+}
+
+static const struct driver_info	huawei_info = {
+	.description =	"Vendor specific Ethernet Device",
+	.flags =	FLAG_WWAN,
+	.bind =		usbnet_cdc_bind,
+	.unbind =	usbnet_cdc_unbind,
+	.status =	huawei_cdc_status,
+	.rx_fixup =	huawei_rx_fixup,
+};
+
+#define HUAWEI_VENDOR_ID	0x12D1
+
+
+static const struct usb_device_id products [] = {
+{
+	/* Huawei E392, E398, ++? */
+	.match_flags    =   USB_DEVICE_ID_MATCH_VENDOR
+		 | USB_DEVICE_ID_MATCH_INT_INFO,
+	.idVendor               = HUAWEI_VENDOR_ID,
+	.bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass	= 1,
+	.bInterfaceProtocol	= 9,
+	.driver_info = (unsigned long)&huawei_info,
+}, {
+	/* Huawei device id 1413 ++? */
+	.match_flags    =   USB_DEVICE_ID_MATCH_VENDOR
+		 | USB_DEVICE_ID_MATCH_INT_INFO,
+	.idVendor               = HUAWEI_VENDOR_ID,
+	.bInterfaceClass	= USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass	= 6,
+	.bInterfaceProtocol	= 255,
+	.driver_info = (unsigned long)&huawei_info,
+},
+	{ },		// END
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver huawei_driver = {
+	.name =		"huawei_wwan",
+	.id_table =	products,
+	.probe =	usbnet_probe,
+	.disconnect =	usbnet_disconnect,
+	.suspend =	usbnet_suspend,
+	.resume =	usbnet_resume,
+	.reset_resume =	usbnet_resume,
+	.supports_autosuspend = 1,
+};
+
+static int __init huawei_init(void)
+{
+	return usb_register(&huawei_driver);
+}
+module_init(huawei_init);
+
+static void __exit huawei_exit(void)
+{
+	usb_deregister(&huawei_driver);
+}
+module_exit(huawei_exit);
+
+MODULE_AUTHOR("Bjørn Mork");
+MODULE_DESCRIPTION("Huawei WWAN driver");
+MODULE_LICENSE("GPL");
-- 
1.7.7.3


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux