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