linux/drivers/net/usb/qmi_wwan.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2012  Bjørn Mork <[email protected]>
 *
 * The probing code is heavily inspired by cdc_ether, which is:
 * Copyright (C) 2003-2005 by David Brownell
 * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync)
 */

#include <linux/module.h>
#include <linux/sched/signal.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
#include <linux/etherdevice.h>
#include <linux/if_arp.h>
#include <linux/kstrtox.h>
#include <linux/mii.h>
#include <linux/rtnetlink.h>
#include <linux/usb.h>
#include <linux/usb/cdc.h>
#include <linux/usb/usbnet.h>
#include <linux/usb/cdc-wdm.h>
#include <linux/u64_stats_sync.h>

/* This driver supports wwan (3G/LTE/?) devices using a vendor
 * specific management protocol called Qualcomm MSM Interface (QMI) -
 * in addition to the more common AT commands over serial interface
 * management
 *
 * QMI is wrapped in CDC, using CDC encapsulated commands on the
 * control ("master") interface of a two-interface CDC Union
 * resembling standard CDC ECM.  The devices do not use the control
 * interface for any other CDC messages.  Most likely because the
 * management protocol is used in place of the standard CDC
 * notifications NOTIFY_NETWORK_CONNECTION and NOTIFY_SPEED_CHANGE
 *
 * Alternatively, control and data functions can be combined in a
 * single USB interface.
 *
 * Handling a protocol like QMI is out of the scope for any driver.
 * It is exported as a character device using the cdc-wdm driver as
 * a subdriver, enabling userspace applications ("modem managers") to
 * handle it.
 *
 * These devices may alternatively/additionally be configured using AT
 * commands on a serial interface
 */

/* driver specific data */
struct qmi_wwan_state {};

enum qmi_wwan_flags {};

enum qmi_wwan_quirks {};

struct qmimux_hdr {};

struct qmimux_priv {};

static int qmimux_open(struct net_device *dev)
{}

static int qmimux_stop(struct net_device *dev)
{}

static netdev_tx_t qmimux_start_xmit(struct sk_buff *skb, struct net_device *dev)
{}

static const struct net_device_ops qmimux_netdev_ops =;

static void qmimux_setup(struct net_device *dev)
{}

static struct net_device *qmimux_find_dev(struct usbnet *dev, u8 mux_id)
{}

static bool qmimux_has_slaves(struct usbnet *dev)
{}

static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{}

static ssize_t mux_id_show(struct device *d, struct device_attribute *attr, char *buf)
{}

static DEVICE_ATTR_RO(mux_id);

static struct attribute *qmi_wwan_sysfs_qmimux_attrs[] =;

static struct attribute_group qmi_wwan_sysfs_qmimux_attr_group =;

static int qmimux_register_device(struct net_device *real_dev, u8 mux_id)
{}

static void qmimux_unregister_device(struct net_device *dev,
				     struct list_head *head)
{}

static void qmi_wwan_netdev_setup(struct net_device *net)
{}

static ssize_t raw_ip_show(struct device *d, struct device_attribute *attr, char *buf)
{}

static ssize_t raw_ip_store(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
{}

static ssize_t add_mux_show(struct device *d, struct device_attribute *attr, char *buf)
{}

static ssize_t add_mux_store(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
{}

static ssize_t del_mux_show(struct device *d, struct device_attribute *attr, char *buf)
{}

static ssize_t del_mux_store(struct device *d,  struct device_attribute *attr, const char *buf, size_t len)
{}

static ssize_t pass_through_show(struct device *d,
				 struct device_attribute *attr, char *buf)
{}

static ssize_t pass_through_store(struct device *d,
				  struct device_attribute *attr,
				  const char *buf, size_t len)
{}

static DEVICE_ATTR_RW(raw_ip);
static DEVICE_ATTR_RW(add_mux);
static DEVICE_ATTR_RW(del_mux);
static DEVICE_ATTR_RW(pass_through);

static struct attribute *qmi_wwan_sysfs_attrs[] =;

static struct attribute_group qmi_wwan_sysfs_attr_group =;

/* default ethernet address used by the modem */
static const u8 default_modem_addr[ETH_ALEN] =;

static const u8 buggy_fw_addr[ETH_ALEN] =;

/* Make up an ethernet header if the packet doesn't have one.
 *
 * A firmware bug common among several devices cause them to send raw
 * IP packets under some circumstances.  There is no way for the
 * driver/host to know when this will happen.  And even when the bug
 * hits, some packets will still arrive with an intact header.
 *
 * The supported devices are only capably of sending IPv4, IPv6 and
 * ARP packets on a point-to-point link. Any packet with an ethernet
 * header will have either our address or a broadcast/multicast
 * address as destination.  ARP packets will always have a header.
 *
 * This means that this function will reliably add the appropriate
 * header iff necessary, provided our hardware address does not start
 * with 4 or 6.
 *
 * Another common firmware bug results in all packets being addressed
 * to 00:a0:c6:00:00:00 despite the host address being different.
 * This function will also fixup such packets.
 */
static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
{}

/* very simplistic detection of IPv4 or IPv6 headers */
static bool possibly_iphdr(const char *data)
{}

/* disallow addresses which may be confused with IP headers */
static int qmi_wwan_mac_addr(struct net_device *dev, void *p)
{}

static const struct net_device_ops qmi_wwan_netdev_ops =;

/* using a counter to merge subdriver requests with our own into a
 * combined state
 */
static int qmi_wwan_manage_power(struct usbnet *dev, int on)
{}

static int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on)
{}

/* collect all three endpoints and register subdriver */
static int qmi_wwan_register_subdriver(struct usbnet *dev)
{}

/* 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)
{}

static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
{}

static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf)
{}

/* suspend/resume wrappers calling both usbnet and the cdc-wdm
 * subdriver if present.
 *
 * NOTE: cdc-wdm also supports pre/post_reset, but we cannot provide
 * wrappers for those without adding usbnet reset support first.
 */
static int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message)
{}

static int qmi_wwan_resume(struct usb_interface *intf)
{}

static const struct driver_info	qmi_wwan_info =;

static const struct driver_info	qmi_wwan_info_quirk_dtr =;

#define HUAWEI_VENDOR_ID

/* map QMI/wwan function by a fixed interface number */
#define QMI_FIXED_INTF(vend, prod, num)

/* devices requiring "set DTR" quirk */
#define QMI_QUIRK_SET_DTR(vend, prod, num)

/* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */
#define QMI_GOBI1K_DEVICE(vend, prod)

/* Gobi 2000/3000 QMI/wwan interface number is 0 according to qcserial */
#define QMI_GOBI_DEVICE(vend, prod)

/* Many devices have QMI and DIAG functions which are distinguishable
 * from other vendor specific functions by class, subclass and
 * protocol all being 0xff. The DIAG function has exactly 2 endpoints
 * and is silently rejected when probed.
 *
 * This makes it possible to match dynamically numbered QMI functions
 * as seen on e.g. many Quectel modems.
 */
#define QMI_MATCH_FF_FF_FF(vend, prod)

static const struct usb_device_id products[] =;
MODULE_DEVICE_TABLE(usb, products);

static bool quectel_ec20_detected(struct usb_interface *intf)
{}

static int qmi_wwan_probe(struct usb_interface *intf,
			  const struct usb_device_id *prod)
{}

static void qmi_wwan_disconnect(struct usb_interface *intf)
{}

static struct usb_driver qmi_wwan_driver =;

module_usb_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();