162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2012 Bjørn Mork <bjorn@mork.no> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * The probing code is heavily inspired by cdc_ether, which is: 662306a36Sopenharmony_ci * Copyright (C) 2003-2005 by David Brownell 762306a36Sopenharmony_ci * Copyright (C) 2006 by Ole Andre Vadla Ravnas (ActiveSync) 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/sched/signal.h> 1262306a36Sopenharmony_ci#include <linux/netdevice.h> 1362306a36Sopenharmony_ci#include <linux/ethtool.h> 1462306a36Sopenharmony_ci#include <linux/etherdevice.h> 1562306a36Sopenharmony_ci#include <linux/if_arp.h> 1662306a36Sopenharmony_ci#include <linux/kstrtox.h> 1762306a36Sopenharmony_ci#include <linux/mii.h> 1862306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1962306a36Sopenharmony_ci#include <linux/usb.h> 2062306a36Sopenharmony_ci#include <linux/usb/cdc.h> 2162306a36Sopenharmony_ci#include <linux/usb/usbnet.h> 2262306a36Sopenharmony_ci#include <linux/usb/cdc-wdm.h> 2362306a36Sopenharmony_ci#include <linux/u64_stats_sync.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* This driver supports wwan (3G/LTE/?) devices using a vendor 2662306a36Sopenharmony_ci * specific management protocol called Qualcomm MSM Interface (QMI) - 2762306a36Sopenharmony_ci * in addition to the more common AT commands over serial interface 2862306a36Sopenharmony_ci * management 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * QMI is wrapped in CDC, using CDC encapsulated commands on the 3162306a36Sopenharmony_ci * control ("master") interface of a two-interface CDC Union 3262306a36Sopenharmony_ci * resembling standard CDC ECM. The devices do not use the control 3362306a36Sopenharmony_ci * interface for any other CDC messages. Most likely because the 3462306a36Sopenharmony_ci * management protocol is used in place of the standard CDC 3562306a36Sopenharmony_ci * notifications NOTIFY_NETWORK_CONNECTION and NOTIFY_SPEED_CHANGE 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * Alternatively, control and data functions can be combined in a 3862306a36Sopenharmony_ci * single USB interface. 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Handling a protocol like QMI is out of the scope for any driver. 4162306a36Sopenharmony_ci * It is exported as a character device using the cdc-wdm driver as 4262306a36Sopenharmony_ci * a subdriver, enabling userspace applications ("modem managers") to 4362306a36Sopenharmony_ci * handle it. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * These devices may alternatively/additionally be configured using AT 4662306a36Sopenharmony_ci * commands on a serial interface 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* driver specific data */ 5062306a36Sopenharmony_cistruct qmi_wwan_state { 5162306a36Sopenharmony_ci struct usb_driver *subdriver; 5262306a36Sopenharmony_ci atomic_t pmcount; 5362306a36Sopenharmony_ci unsigned long flags; 5462306a36Sopenharmony_ci struct usb_interface *control; 5562306a36Sopenharmony_ci struct usb_interface *data; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cienum qmi_wwan_flags { 5962306a36Sopenharmony_ci QMI_WWAN_FLAG_RAWIP = 1 << 0, 6062306a36Sopenharmony_ci QMI_WWAN_FLAG_MUX = 1 << 1, 6162306a36Sopenharmony_ci QMI_WWAN_FLAG_PASS_THROUGH = 1 << 2, 6262306a36Sopenharmony_ci}; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cienum qmi_wwan_quirks { 6562306a36Sopenharmony_ci QMI_WWAN_QUIRK_DTR = 1 << 0, /* needs "set DTR" request */ 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistruct qmimux_hdr { 6962306a36Sopenharmony_ci u8 pad; 7062306a36Sopenharmony_ci u8 mux_id; 7162306a36Sopenharmony_ci __be16 pkt_len; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct qmimux_priv { 7562306a36Sopenharmony_ci struct net_device *real_dev; 7662306a36Sopenharmony_ci u8 mux_id; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic int qmimux_open(struct net_device *dev) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct qmimux_priv *priv = netdev_priv(dev); 8262306a36Sopenharmony_ci struct net_device *real_dev = priv->real_dev; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (!(priv->real_dev->flags & IFF_UP)) 8562306a36Sopenharmony_ci return -ENETDOWN; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (netif_carrier_ok(real_dev)) 8862306a36Sopenharmony_ci netif_carrier_on(dev); 8962306a36Sopenharmony_ci return 0; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int qmimux_stop(struct net_device *dev) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci netif_carrier_off(dev); 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic netdev_tx_t qmimux_start_xmit(struct sk_buff *skb, struct net_device *dev) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct qmimux_priv *priv = netdev_priv(dev); 10162306a36Sopenharmony_ci unsigned int len = skb->len; 10262306a36Sopenharmony_ci struct qmimux_hdr *hdr; 10362306a36Sopenharmony_ci netdev_tx_t ret; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci hdr = skb_push(skb, sizeof(struct qmimux_hdr)); 10662306a36Sopenharmony_ci hdr->pad = 0; 10762306a36Sopenharmony_ci hdr->mux_id = priv->mux_id; 10862306a36Sopenharmony_ci hdr->pkt_len = cpu_to_be16(len); 10962306a36Sopenharmony_ci skb->dev = priv->real_dev; 11062306a36Sopenharmony_ci ret = dev_queue_xmit(skb); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) 11362306a36Sopenharmony_ci dev_sw_netstats_tx_add(dev, 1, len); 11462306a36Sopenharmony_ci else 11562306a36Sopenharmony_ci dev->stats.tx_dropped++; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return ret; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_cistatic const struct net_device_ops qmimux_netdev_ops = { 12162306a36Sopenharmony_ci .ndo_open = qmimux_open, 12262306a36Sopenharmony_ci .ndo_stop = qmimux_stop, 12362306a36Sopenharmony_ci .ndo_start_xmit = qmimux_start_xmit, 12462306a36Sopenharmony_ci .ndo_get_stats64 = dev_get_tstats64, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic void qmimux_setup(struct net_device *dev) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci dev->header_ops = NULL; /* No header */ 13062306a36Sopenharmony_ci dev->type = ARPHRD_NONE; 13162306a36Sopenharmony_ci dev->hard_header_len = 0; 13262306a36Sopenharmony_ci dev->addr_len = 0; 13362306a36Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; 13462306a36Sopenharmony_ci dev->netdev_ops = &qmimux_netdev_ops; 13562306a36Sopenharmony_ci dev->mtu = 1500; 13662306a36Sopenharmony_ci dev->needs_free_netdev = true; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic struct net_device *qmimux_find_dev(struct usbnet *dev, u8 mux_id) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct qmimux_priv *priv; 14262306a36Sopenharmony_ci struct list_head *iter; 14362306a36Sopenharmony_ci struct net_device *ldev; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci rcu_read_lock(); 14662306a36Sopenharmony_ci netdev_for_each_upper_dev_rcu(dev->net, ldev, iter) { 14762306a36Sopenharmony_ci priv = netdev_priv(ldev); 14862306a36Sopenharmony_ci if (priv->mux_id == mux_id) { 14962306a36Sopenharmony_ci rcu_read_unlock(); 15062306a36Sopenharmony_ci return ldev; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci rcu_read_unlock(); 15462306a36Sopenharmony_ci return NULL; 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic bool qmimux_has_slaves(struct usbnet *dev) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci return !list_empty(&dev->net->adj_list.upper); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci unsigned int len, offset = 0, pad_len, pkt_len; 16562306a36Sopenharmony_ci struct qmimux_hdr *hdr; 16662306a36Sopenharmony_ci struct net_device *net; 16762306a36Sopenharmony_ci struct sk_buff *skbn; 16862306a36Sopenharmony_ci u8 qmimux_hdr_sz = sizeof(*hdr); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci while (offset + qmimux_hdr_sz < skb->len) { 17162306a36Sopenharmony_ci hdr = (struct qmimux_hdr *)(skb->data + offset); 17262306a36Sopenharmony_ci len = be16_to_cpu(hdr->pkt_len); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* drop the packet, bogus length */ 17562306a36Sopenharmony_ci if (offset + len + qmimux_hdr_sz > skb->len) 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* control packet, we do not know what to do */ 17962306a36Sopenharmony_ci if (hdr->pad & 0x80) 18062306a36Sopenharmony_ci goto skip; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* extract padding length and check for valid length info */ 18362306a36Sopenharmony_ci pad_len = hdr->pad & 0x3f; 18462306a36Sopenharmony_ci if (len == 0 || pad_len >= len) 18562306a36Sopenharmony_ci goto skip; 18662306a36Sopenharmony_ci pkt_len = len - pad_len; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci net = qmimux_find_dev(dev, hdr->mux_id); 18962306a36Sopenharmony_ci if (!net) 19062306a36Sopenharmony_ci goto skip; 19162306a36Sopenharmony_ci skbn = netdev_alloc_skb(net, pkt_len + LL_MAX_HEADER); 19262306a36Sopenharmony_ci if (!skbn) 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci switch (skb->data[offset + qmimux_hdr_sz] & 0xf0) { 19662306a36Sopenharmony_ci case 0x40: 19762306a36Sopenharmony_ci skbn->protocol = htons(ETH_P_IP); 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci case 0x60: 20062306a36Sopenharmony_ci skbn->protocol = htons(ETH_P_IPV6); 20162306a36Sopenharmony_ci break; 20262306a36Sopenharmony_ci default: 20362306a36Sopenharmony_ci /* not ip - do not know what to do */ 20462306a36Sopenharmony_ci goto skip; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci skb_reserve(skbn, LL_MAX_HEADER); 20862306a36Sopenharmony_ci skb_put_data(skbn, skb->data + offset + qmimux_hdr_sz, pkt_len); 20962306a36Sopenharmony_ci if (netif_rx(skbn) != NET_RX_SUCCESS) { 21062306a36Sopenharmony_ci net->stats.rx_errors++; 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci } else { 21362306a36Sopenharmony_ci dev_sw_netstats_rx_add(net, pkt_len); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ciskip: 21762306a36Sopenharmony_ci offset += len + qmimux_hdr_sz; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci return 1; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic ssize_t mux_id_show(struct device *d, struct device_attribute *attr, char *buf) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct net_device *dev = to_net_dev(d); 22562306a36Sopenharmony_ci struct qmimux_priv *priv; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci priv = netdev_priv(dev); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci return sysfs_emit(buf, "0x%02x\n", priv->mux_id); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(mux_id); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic struct attribute *qmi_wwan_sysfs_qmimux_attrs[] = { 23562306a36Sopenharmony_ci &dev_attr_mux_id.attr, 23662306a36Sopenharmony_ci NULL, 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic struct attribute_group qmi_wwan_sysfs_qmimux_attr_group = { 24062306a36Sopenharmony_ci .name = "qmap", 24162306a36Sopenharmony_ci .attrs = qmi_wwan_sysfs_qmimux_attrs, 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic int qmimux_register_device(struct net_device *real_dev, u8 mux_id) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci struct net_device *new_dev; 24762306a36Sopenharmony_ci struct qmimux_priv *priv; 24862306a36Sopenharmony_ci int err; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci new_dev = alloc_netdev(sizeof(struct qmimux_priv), 25162306a36Sopenharmony_ci "qmimux%d", NET_NAME_UNKNOWN, qmimux_setup); 25262306a36Sopenharmony_ci if (!new_dev) 25362306a36Sopenharmony_ci return -ENOBUFS; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci dev_net_set(new_dev, dev_net(real_dev)); 25662306a36Sopenharmony_ci priv = netdev_priv(new_dev); 25762306a36Sopenharmony_ci priv->mux_id = mux_id; 25862306a36Sopenharmony_ci priv->real_dev = real_dev; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci new_dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); 26162306a36Sopenharmony_ci if (!new_dev->tstats) { 26262306a36Sopenharmony_ci err = -ENOBUFS; 26362306a36Sopenharmony_ci goto out_free_newdev; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci new_dev->sysfs_groups[0] = &qmi_wwan_sysfs_qmimux_attr_group; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci err = register_netdevice(new_dev); 26962306a36Sopenharmony_ci if (err < 0) 27062306a36Sopenharmony_ci goto out_free_newdev; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Account for reference in struct qmimux_priv_priv */ 27362306a36Sopenharmony_ci dev_hold(real_dev); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci err = netdev_upper_dev_link(real_dev, new_dev, NULL); 27662306a36Sopenharmony_ci if (err) 27762306a36Sopenharmony_ci goto out_unregister_netdev; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci netif_stacked_transfer_operstate(real_dev, new_dev); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ciout_unregister_netdev: 28462306a36Sopenharmony_ci unregister_netdevice(new_dev); 28562306a36Sopenharmony_ci dev_put(real_dev); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ciout_free_newdev: 28862306a36Sopenharmony_ci free_netdev(new_dev); 28962306a36Sopenharmony_ci return err; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic void qmimux_unregister_device(struct net_device *dev, 29362306a36Sopenharmony_ci struct list_head *head) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct qmimux_priv *priv = netdev_priv(dev); 29662306a36Sopenharmony_ci struct net_device *real_dev = priv->real_dev; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci free_percpu(dev->tstats); 29962306a36Sopenharmony_ci netdev_upper_dev_unlink(real_dev, dev); 30062306a36Sopenharmony_ci unregister_netdevice_queue(dev, head); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Get rid of the reference to real_dev */ 30362306a36Sopenharmony_ci dev_put(real_dev); 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic void qmi_wwan_netdev_setup(struct net_device *net) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(net); 30962306a36Sopenharmony_ci struct qmi_wwan_state *info = (void *)&dev->data; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (info->flags & QMI_WWAN_FLAG_RAWIP) { 31262306a36Sopenharmony_ci net->header_ops = NULL; /* No header */ 31362306a36Sopenharmony_ci net->type = ARPHRD_NONE; 31462306a36Sopenharmony_ci net->hard_header_len = 0; 31562306a36Sopenharmony_ci net->addr_len = 0; 31662306a36Sopenharmony_ci net->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST; 31762306a36Sopenharmony_ci set_bit(EVENT_NO_IP_ALIGN, &dev->flags); 31862306a36Sopenharmony_ci netdev_dbg(net, "mode: raw IP\n"); 31962306a36Sopenharmony_ci } else if (!net->header_ops) { /* don't bother if already set */ 32062306a36Sopenharmony_ci ether_setup(net); 32162306a36Sopenharmony_ci /* Restoring min/max mtu values set originally by usbnet */ 32262306a36Sopenharmony_ci net->min_mtu = 0; 32362306a36Sopenharmony_ci net->max_mtu = ETH_MAX_MTU; 32462306a36Sopenharmony_ci clear_bit(EVENT_NO_IP_ALIGN, &dev->flags); 32562306a36Sopenharmony_ci netdev_dbg(net, "mode: Ethernet\n"); 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* recalculate buffers after changing hard_header_len */ 32962306a36Sopenharmony_ci usbnet_change_mtu(net, net->mtu); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic ssize_t raw_ip_show(struct device *d, struct device_attribute *attr, char *buf) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(to_net_dev(d)); 33562306a36Sopenharmony_ci struct qmi_wwan_state *info = (void *)&dev->data; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci return sprintf(buf, "%c\n", info->flags & QMI_WWAN_FLAG_RAWIP ? 'Y' : 'N'); 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic ssize_t raw_ip_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(to_net_dev(d)); 34362306a36Sopenharmony_ci struct qmi_wwan_state *info = (void *)&dev->data; 34462306a36Sopenharmony_ci bool enable; 34562306a36Sopenharmony_ci int ret; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (kstrtobool(buf, &enable)) 34862306a36Sopenharmony_ci return -EINVAL; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* no change? */ 35162306a36Sopenharmony_ci if (enable == (info->flags & QMI_WWAN_FLAG_RAWIP)) 35262306a36Sopenharmony_ci return len; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* ip mode cannot be cleared when pass through mode is set */ 35562306a36Sopenharmony_ci if (!enable && (info->flags & QMI_WWAN_FLAG_PASS_THROUGH)) { 35662306a36Sopenharmony_ci netdev_err(dev->net, 35762306a36Sopenharmony_ci "Cannot clear ip mode on pass through device\n"); 35862306a36Sopenharmony_ci return -EINVAL; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (!rtnl_trylock()) 36262306a36Sopenharmony_ci return restart_syscall(); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* we don't want to modify a running netdev */ 36562306a36Sopenharmony_ci if (netif_running(dev->net)) { 36662306a36Sopenharmony_ci netdev_err(dev->net, "Cannot change a running device\n"); 36762306a36Sopenharmony_ci ret = -EBUSY; 36862306a36Sopenharmony_ci goto err; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* let other drivers deny the change */ 37262306a36Sopenharmony_ci ret = call_netdevice_notifiers(NETDEV_PRE_TYPE_CHANGE, dev->net); 37362306a36Sopenharmony_ci ret = notifier_to_errno(ret); 37462306a36Sopenharmony_ci if (ret) { 37562306a36Sopenharmony_ci netdev_err(dev->net, "Type change was refused\n"); 37662306a36Sopenharmony_ci goto err; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (enable) 38062306a36Sopenharmony_ci info->flags |= QMI_WWAN_FLAG_RAWIP; 38162306a36Sopenharmony_ci else 38262306a36Sopenharmony_ci info->flags &= ~QMI_WWAN_FLAG_RAWIP; 38362306a36Sopenharmony_ci qmi_wwan_netdev_setup(dev->net); 38462306a36Sopenharmony_ci call_netdevice_notifiers(NETDEV_POST_TYPE_CHANGE, dev->net); 38562306a36Sopenharmony_ci ret = len; 38662306a36Sopenharmony_cierr: 38762306a36Sopenharmony_ci rtnl_unlock(); 38862306a36Sopenharmony_ci return ret; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic ssize_t add_mux_show(struct device *d, struct device_attribute *attr, char *buf) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct net_device *dev = to_net_dev(d); 39462306a36Sopenharmony_ci struct qmimux_priv *priv; 39562306a36Sopenharmony_ci struct list_head *iter; 39662306a36Sopenharmony_ci struct net_device *ldev; 39762306a36Sopenharmony_ci ssize_t count = 0; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci rcu_read_lock(); 40062306a36Sopenharmony_ci netdev_for_each_upper_dev_rcu(dev, ldev, iter) { 40162306a36Sopenharmony_ci priv = netdev_priv(ldev); 40262306a36Sopenharmony_ci count += scnprintf(&buf[count], PAGE_SIZE - count, 40362306a36Sopenharmony_ci "0x%02x\n", priv->mux_id); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci rcu_read_unlock(); 40662306a36Sopenharmony_ci return count; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic ssize_t add_mux_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(to_net_dev(d)); 41262306a36Sopenharmony_ci struct qmi_wwan_state *info = (void *)&dev->data; 41362306a36Sopenharmony_ci u8 mux_id; 41462306a36Sopenharmony_ci int ret; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (kstrtou8(buf, 0, &mux_id)) 41762306a36Sopenharmony_ci return -EINVAL; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* mux_id [1 - 254] for compatibility with ip(8) and the rmnet driver */ 42062306a36Sopenharmony_ci if (mux_id < 1 || mux_id > 254) 42162306a36Sopenharmony_ci return -EINVAL; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (!rtnl_trylock()) 42462306a36Sopenharmony_ci return restart_syscall(); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (qmimux_find_dev(dev, mux_id)) { 42762306a36Sopenharmony_ci netdev_err(dev->net, "mux_id already present\n"); 42862306a36Sopenharmony_ci ret = -EINVAL; 42962306a36Sopenharmony_ci goto err; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci ret = qmimux_register_device(dev->net, mux_id); 43362306a36Sopenharmony_ci if (!ret) { 43462306a36Sopenharmony_ci info->flags |= QMI_WWAN_FLAG_MUX; 43562306a36Sopenharmony_ci ret = len; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_cierr: 43862306a36Sopenharmony_ci rtnl_unlock(); 43962306a36Sopenharmony_ci return ret; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic ssize_t del_mux_show(struct device *d, struct device_attribute *attr, char *buf) 44362306a36Sopenharmony_ci{ 44462306a36Sopenharmony_ci return add_mux_show(d, attr, buf); 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic ssize_t del_mux_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(to_net_dev(d)); 45062306a36Sopenharmony_ci struct qmi_wwan_state *info = (void *)&dev->data; 45162306a36Sopenharmony_ci struct net_device *del_dev; 45262306a36Sopenharmony_ci u8 mux_id; 45362306a36Sopenharmony_ci int ret = 0; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (kstrtou8(buf, 0, &mux_id)) 45662306a36Sopenharmony_ci return -EINVAL; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (!rtnl_trylock()) 45962306a36Sopenharmony_ci return restart_syscall(); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci del_dev = qmimux_find_dev(dev, mux_id); 46262306a36Sopenharmony_ci if (!del_dev) { 46362306a36Sopenharmony_ci netdev_err(dev->net, "mux_id not present\n"); 46462306a36Sopenharmony_ci ret = -EINVAL; 46562306a36Sopenharmony_ci goto err; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci qmimux_unregister_device(del_dev, NULL); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (!qmimux_has_slaves(dev)) 47062306a36Sopenharmony_ci info->flags &= ~QMI_WWAN_FLAG_MUX; 47162306a36Sopenharmony_ci ret = len; 47262306a36Sopenharmony_cierr: 47362306a36Sopenharmony_ci rtnl_unlock(); 47462306a36Sopenharmony_ci return ret; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic ssize_t pass_through_show(struct device *d, 47862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(to_net_dev(d)); 48162306a36Sopenharmony_ci struct qmi_wwan_state *info; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci info = (void *)&dev->data; 48462306a36Sopenharmony_ci return sprintf(buf, "%c\n", 48562306a36Sopenharmony_ci info->flags & QMI_WWAN_FLAG_PASS_THROUGH ? 'Y' : 'N'); 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic ssize_t pass_through_store(struct device *d, 48962306a36Sopenharmony_ci struct device_attribute *attr, 49062306a36Sopenharmony_ci const char *buf, size_t len) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct usbnet *dev = netdev_priv(to_net_dev(d)); 49362306a36Sopenharmony_ci struct qmi_wwan_state *info; 49462306a36Sopenharmony_ci bool enable; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci if (kstrtobool(buf, &enable)) 49762306a36Sopenharmony_ci return -EINVAL; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci info = (void *)&dev->data; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* no change? */ 50262306a36Sopenharmony_ci if (enable == (info->flags & QMI_WWAN_FLAG_PASS_THROUGH)) 50362306a36Sopenharmony_ci return len; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* pass through mode can be set for raw ip devices only */ 50662306a36Sopenharmony_ci if (!(info->flags & QMI_WWAN_FLAG_RAWIP)) { 50762306a36Sopenharmony_ci netdev_err(dev->net, 50862306a36Sopenharmony_ci "Cannot set pass through mode on non ip device\n"); 50962306a36Sopenharmony_ci return -EINVAL; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (enable) 51362306a36Sopenharmony_ci info->flags |= QMI_WWAN_FLAG_PASS_THROUGH; 51462306a36Sopenharmony_ci else 51562306a36Sopenharmony_ci info->flags &= ~QMI_WWAN_FLAG_PASS_THROUGH; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return len; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(raw_ip); 52162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(add_mux); 52262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(del_mux); 52362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(pass_through); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic struct attribute *qmi_wwan_sysfs_attrs[] = { 52662306a36Sopenharmony_ci &dev_attr_raw_ip.attr, 52762306a36Sopenharmony_ci &dev_attr_add_mux.attr, 52862306a36Sopenharmony_ci &dev_attr_del_mux.attr, 52962306a36Sopenharmony_ci &dev_attr_pass_through.attr, 53062306a36Sopenharmony_ci NULL, 53162306a36Sopenharmony_ci}; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic struct attribute_group qmi_wwan_sysfs_attr_group = { 53462306a36Sopenharmony_ci .name = "qmi", 53562306a36Sopenharmony_ci .attrs = qmi_wwan_sysfs_attrs, 53662306a36Sopenharmony_ci}; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci/* default ethernet address used by the modem */ 53962306a36Sopenharmony_cistatic const u8 default_modem_addr[ETH_ALEN] = {0x02, 0x50, 0xf3}; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic const u8 buggy_fw_addr[ETH_ALEN] = {0x00, 0xa0, 0xc6, 0x00, 0x00, 0x00}; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci/* Make up an ethernet header if the packet doesn't have one. 54462306a36Sopenharmony_ci * 54562306a36Sopenharmony_ci * A firmware bug common among several devices cause them to send raw 54662306a36Sopenharmony_ci * IP packets under some circumstances. There is no way for the 54762306a36Sopenharmony_ci * driver/host to know when this will happen. And even when the bug 54862306a36Sopenharmony_ci * hits, some packets will still arrive with an intact header. 54962306a36Sopenharmony_ci * 55062306a36Sopenharmony_ci * The supported devices are only capably of sending IPv4, IPv6 and 55162306a36Sopenharmony_ci * ARP packets on a point-to-point link. Any packet with an ethernet 55262306a36Sopenharmony_ci * header will have either our address or a broadcast/multicast 55362306a36Sopenharmony_ci * address as destination. ARP packets will always have a header. 55462306a36Sopenharmony_ci * 55562306a36Sopenharmony_ci * This means that this function will reliably add the appropriate 55662306a36Sopenharmony_ci * header iff necessary, provided our hardware address does not start 55762306a36Sopenharmony_ci * with 4 or 6. 55862306a36Sopenharmony_ci * 55962306a36Sopenharmony_ci * Another common firmware bug results in all packets being addressed 56062306a36Sopenharmony_ci * to 00:a0:c6:00:00:00 despite the host address being different. 56162306a36Sopenharmony_ci * This function will also fixup such packets. 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_cistatic int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct qmi_wwan_state *info = (void *)&dev->data; 56662306a36Sopenharmony_ci bool rawip = info->flags & QMI_WWAN_FLAG_RAWIP; 56762306a36Sopenharmony_ci __be16 proto; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* This check is no longer done by usbnet */ 57062306a36Sopenharmony_ci if (skb->len < dev->net->hard_header_len) 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (info->flags & QMI_WWAN_FLAG_MUX) 57462306a36Sopenharmony_ci return qmimux_rx_fixup(dev, skb); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (info->flags & QMI_WWAN_FLAG_PASS_THROUGH) { 57762306a36Sopenharmony_ci skb->protocol = htons(ETH_P_MAP); 57862306a36Sopenharmony_ci return 1; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci switch (skb->data[0] & 0xf0) { 58262306a36Sopenharmony_ci case 0x40: 58362306a36Sopenharmony_ci proto = htons(ETH_P_IP); 58462306a36Sopenharmony_ci break; 58562306a36Sopenharmony_ci case 0x60: 58662306a36Sopenharmony_ci proto = htons(ETH_P_IPV6); 58762306a36Sopenharmony_ci break; 58862306a36Sopenharmony_ci case 0x00: 58962306a36Sopenharmony_ci if (rawip) 59062306a36Sopenharmony_ci return 0; 59162306a36Sopenharmony_ci if (is_multicast_ether_addr(skb->data)) 59262306a36Sopenharmony_ci return 1; 59362306a36Sopenharmony_ci /* possibly bogus destination - rewrite just in case */ 59462306a36Sopenharmony_ci skb_reset_mac_header(skb); 59562306a36Sopenharmony_ci goto fix_dest; 59662306a36Sopenharmony_ci default: 59762306a36Sopenharmony_ci if (rawip) 59862306a36Sopenharmony_ci return 0; 59962306a36Sopenharmony_ci /* pass along other packets without modifications */ 60062306a36Sopenharmony_ci return 1; 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci if (rawip) { 60362306a36Sopenharmony_ci skb_reset_mac_header(skb); 60462306a36Sopenharmony_ci skb->dev = dev->net; /* normally set by eth_type_trans */ 60562306a36Sopenharmony_ci skb->protocol = proto; 60662306a36Sopenharmony_ci return 1; 60762306a36Sopenharmony_ci } 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (skb_headroom(skb) < ETH_HLEN) 61062306a36Sopenharmony_ci return 0; 61162306a36Sopenharmony_ci skb_push(skb, ETH_HLEN); 61262306a36Sopenharmony_ci skb_reset_mac_header(skb); 61362306a36Sopenharmony_ci eth_hdr(skb)->h_proto = proto; 61462306a36Sopenharmony_ci eth_zero_addr(eth_hdr(skb)->h_source); 61562306a36Sopenharmony_cifix_dest: 61662306a36Sopenharmony_ci memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN); 61762306a36Sopenharmony_ci return 1; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci/* very simplistic detection of IPv4 or IPv6 headers */ 62162306a36Sopenharmony_cistatic bool possibly_iphdr(const char *data) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci return (data[0] & 0xd0) == 0x40; 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci/* disallow addresses which may be confused with IP headers */ 62762306a36Sopenharmony_cistatic int qmi_wwan_mac_addr(struct net_device *dev, void *p) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci int ret; 63062306a36Sopenharmony_ci struct sockaddr *addr = p; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci ret = eth_prepare_mac_addr_change(dev, p); 63362306a36Sopenharmony_ci if (ret < 0) 63462306a36Sopenharmony_ci return ret; 63562306a36Sopenharmony_ci if (possibly_iphdr(addr->sa_data)) 63662306a36Sopenharmony_ci return -EADDRNOTAVAIL; 63762306a36Sopenharmony_ci eth_commit_mac_addr_change(dev, p); 63862306a36Sopenharmony_ci return 0; 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic const struct net_device_ops qmi_wwan_netdev_ops = { 64262306a36Sopenharmony_ci .ndo_open = usbnet_open, 64362306a36Sopenharmony_ci .ndo_stop = usbnet_stop, 64462306a36Sopenharmony_ci .ndo_start_xmit = usbnet_start_xmit, 64562306a36Sopenharmony_ci .ndo_tx_timeout = usbnet_tx_timeout, 64662306a36Sopenharmony_ci .ndo_change_mtu = usbnet_change_mtu, 64762306a36Sopenharmony_ci .ndo_get_stats64 = dev_get_tstats64, 64862306a36Sopenharmony_ci .ndo_set_mac_address = qmi_wwan_mac_addr, 64962306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 65062306a36Sopenharmony_ci}; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci/* using a counter to merge subdriver requests with our own into a 65362306a36Sopenharmony_ci * combined state 65462306a36Sopenharmony_ci */ 65562306a36Sopenharmony_cistatic int qmi_wwan_manage_power(struct usbnet *dev, int on) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci struct qmi_wwan_state *info = (void *)&dev->data; 65862306a36Sopenharmony_ci int rv; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, 66162306a36Sopenharmony_ci atomic_read(&info->pmcount), on); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if ((on && atomic_add_return(1, &info->pmcount) == 1) || 66462306a36Sopenharmony_ci (!on && atomic_dec_and_test(&info->pmcount))) { 66562306a36Sopenharmony_ci /* need autopm_get/put here to ensure the usbcore sees 66662306a36Sopenharmony_ci * the new value 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ci rv = usb_autopm_get_interface(dev->intf); 66962306a36Sopenharmony_ci dev->intf->needs_remote_wakeup = on; 67062306a36Sopenharmony_ci if (!rv) 67162306a36Sopenharmony_ci usb_autopm_put_interface(dev->intf); 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci return 0; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic int qmi_wwan_cdc_wdm_manage_power(struct usb_interface *intf, int on) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci struct usbnet *dev = usb_get_intfdata(intf); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci /* can be called while disconnecting */ 68162306a36Sopenharmony_ci if (!dev) 68262306a36Sopenharmony_ci return 0; 68362306a36Sopenharmony_ci return qmi_wwan_manage_power(dev, on); 68462306a36Sopenharmony_ci} 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci/* collect all three endpoints and register subdriver */ 68762306a36Sopenharmony_cistatic int qmi_wwan_register_subdriver(struct usbnet *dev) 68862306a36Sopenharmony_ci{ 68962306a36Sopenharmony_ci int rv; 69062306a36Sopenharmony_ci struct usb_driver *subdriver = NULL; 69162306a36Sopenharmony_ci struct qmi_wwan_state *info = (void *)&dev->data; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci /* collect bulk endpoints */ 69462306a36Sopenharmony_ci rv = usbnet_get_endpoints(dev, info->data); 69562306a36Sopenharmony_ci if (rv < 0) 69662306a36Sopenharmony_ci goto err; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* update status endpoint if separate control interface */ 69962306a36Sopenharmony_ci if (info->control != info->data) 70062306a36Sopenharmony_ci dev->status = &info->control->cur_altsetting->endpoint[0]; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* require interrupt endpoint for subdriver */ 70362306a36Sopenharmony_ci if (!dev->status) { 70462306a36Sopenharmony_ci rv = -EINVAL; 70562306a36Sopenharmony_ci goto err; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* for subdriver power management */ 70962306a36Sopenharmony_ci atomic_set(&info->pmcount, 0); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* register subdriver */ 71262306a36Sopenharmony_ci subdriver = usb_cdc_wdm_register(info->control, &dev->status->desc, 71362306a36Sopenharmony_ci 4096, WWAN_PORT_QMI, 71462306a36Sopenharmony_ci &qmi_wwan_cdc_wdm_manage_power); 71562306a36Sopenharmony_ci if (IS_ERR(subdriver)) { 71662306a36Sopenharmony_ci dev_err(&info->control->dev, "subdriver registration failed\n"); 71762306a36Sopenharmony_ci rv = PTR_ERR(subdriver); 71862306a36Sopenharmony_ci goto err; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* prevent usbnet from using status endpoint */ 72262306a36Sopenharmony_ci dev->status = NULL; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* save subdriver struct for suspend/resume wrappers */ 72562306a36Sopenharmony_ci info->subdriver = subdriver; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cierr: 72862306a36Sopenharmony_ci return rv; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci/* Send CDC SetControlLineState request, setting or clearing the DTR. 73262306a36Sopenharmony_ci * "Required for Autoconnect and 9x30 to wake up" according to the 73362306a36Sopenharmony_ci * GobiNet driver. The requirement has been verified on an MDM9230 73462306a36Sopenharmony_ci * based Sierra Wireless MC7455 73562306a36Sopenharmony_ci */ 73662306a36Sopenharmony_cistatic int qmi_wwan_change_dtr(struct usbnet *dev, bool on) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci u8 intf = dev->intf->cur_altsetting->desc.bInterfaceNumber; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci return usbnet_write_cmd(dev, USB_CDC_REQ_SET_CONTROL_LINE_STATE, 74162306a36Sopenharmony_ci USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 74262306a36Sopenharmony_ci on ? 0x01 : 0x00, intf, NULL, 0); 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_cistatic int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) 74662306a36Sopenharmony_ci{ 74762306a36Sopenharmony_ci int status; 74862306a36Sopenharmony_ci u8 *buf = intf->cur_altsetting->extra; 74962306a36Sopenharmony_ci int len = intf->cur_altsetting->extralen; 75062306a36Sopenharmony_ci struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; 75162306a36Sopenharmony_ci struct usb_cdc_union_desc *cdc_union; 75262306a36Sopenharmony_ci struct usb_cdc_ether_desc *cdc_ether; 75362306a36Sopenharmony_ci struct usb_driver *driver = driver_of(intf); 75462306a36Sopenharmony_ci struct qmi_wwan_state *info = (void *)&dev->data; 75562306a36Sopenharmony_ci struct usb_cdc_parsed_header hdr; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < 75862306a36Sopenharmony_ci sizeof(struct qmi_wwan_state))); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci /* set up initial state */ 76162306a36Sopenharmony_ci info->control = intf; 76262306a36Sopenharmony_ci info->data = intf; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci /* and a number of CDC descriptors */ 76562306a36Sopenharmony_ci cdc_parse_cdc_header(&hdr, intf, buf, len); 76662306a36Sopenharmony_ci cdc_union = hdr.usb_cdc_union_desc; 76762306a36Sopenharmony_ci cdc_ether = hdr.usb_cdc_ether_desc; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* Use separate control and data interfaces if we found a CDC Union */ 77062306a36Sopenharmony_ci if (cdc_union) { 77162306a36Sopenharmony_ci info->data = usb_ifnum_to_if(dev->udev, 77262306a36Sopenharmony_ci cdc_union->bSlaveInterface0); 77362306a36Sopenharmony_ci if (desc->bInterfaceNumber != cdc_union->bMasterInterface0 || 77462306a36Sopenharmony_ci !info->data) { 77562306a36Sopenharmony_ci dev_err(&intf->dev, 77662306a36Sopenharmony_ci "bogus CDC Union: master=%u, slave=%u\n", 77762306a36Sopenharmony_ci cdc_union->bMasterInterface0, 77862306a36Sopenharmony_ci cdc_union->bSlaveInterface0); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci /* ignore and continue... */ 78162306a36Sopenharmony_ci cdc_union = NULL; 78262306a36Sopenharmony_ci info->data = intf; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci /* errors aren't fatal - we can live with the dynamic address */ 78762306a36Sopenharmony_ci if (cdc_ether && cdc_ether->wMaxSegmentSize) { 78862306a36Sopenharmony_ci dev->hard_mtu = le16_to_cpu(cdc_ether->wMaxSegmentSize); 78962306a36Sopenharmony_ci usbnet_get_ethernet_addr(dev, cdc_ether->iMACAddress); 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* claim data interface and set it up */ 79362306a36Sopenharmony_ci if (info->control != info->data) { 79462306a36Sopenharmony_ci status = usb_driver_claim_interface(driver, info->data, dev); 79562306a36Sopenharmony_ci if (status < 0) 79662306a36Sopenharmony_ci goto err; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci status = qmi_wwan_register_subdriver(dev); 80062306a36Sopenharmony_ci if (status < 0 && info->control != info->data) { 80162306a36Sopenharmony_ci usb_set_intfdata(info->data, NULL); 80262306a36Sopenharmony_ci usb_driver_release_interface(driver, info->data); 80362306a36Sopenharmony_ci } 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci /* disabling remote wakeup on MDM9x30 devices has the same 80662306a36Sopenharmony_ci * effect as clearing DTR. The device will not respond to QMI 80762306a36Sopenharmony_ci * requests until we set DTR again. This is similar to a 80862306a36Sopenharmony_ci * QMI_CTL SYNC request, clearing a lot of firmware state 80962306a36Sopenharmony_ci * including the client ID allocations. 81062306a36Sopenharmony_ci * 81162306a36Sopenharmony_ci * Our usage model allows a session to span multiple 81262306a36Sopenharmony_ci * open/close events, so we must prevent the firmware from 81362306a36Sopenharmony_ci * clearing out state the clients might need. 81462306a36Sopenharmony_ci * 81562306a36Sopenharmony_ci * MDM9x30 is the first QMI chipset with USB3 support. Abuse 81662306a36Sopenharmony_ci * this fact to enable the quirk for all USB3 devices. 81762306a36Sopenharmony_ci * 81862306a36Sopenharmony_ci * There are also chipsets with the same "set DTR" requirement 81962306a36Sopenharmony_ci * but without USB3 support. Devices based on these chips 82062306a36Sopenharmony_ci * need a quirk flag in the device ID table. 82162306a36Sopenharmony_ci */ 82262306a36Sopenharmony_ci if (dev->driver_info->data & QMI_WWAN_QUIRK_DTR || 82362306a36Sopenharmony_ci le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) { 82462306a36Sopenharmony_ci qmi_wwan_manage_power(dev, 1); 82562306a36Sopenharmony_ci qmi_wwan_change_dtr(dev, true); 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* Never use the same address on both ends of the link, even if the 82962306a36Sopenharmony_ci * buggy firmware told us to. Or, if device is assigned the well-known 83062306a36Sopenharmony_ci * buggy firmware MAC address, replace it with a random address, 83162306a36Sopenharmony_ci */ 83262306a36Sopenharmony_ci if (ether_addr_equal(dev->net->dev_addr, default_modem_addr) || 83362306a36Sopenharmony_ci ether_addr_equal(dev->net->dev_addr, buggy_fw_addr)) 83462306a36Sopenharmony_ci eth_hw_addr_random(dev->net); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* make MAC addr easily distinguishable from an IP header */ 83762306a36Sopenharmony_ci if (possibly_iphdr(dev->net->dev_addr)) { 83862306a36Sopenharmony_ci u8 addr = dev->net->dev_addr[0]; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci addr |= 0x02; /* set local assignment bit */ 84162306a36Sopenharmony_ci addr &= 0xbf; /* clear "IP" bit */ 84262306a36Sopenharmony_ci dev_addr_mod(dev->net, 0, &addr, 1); 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci dev->net->netdev_ops = &qmi_wwan_netdev_ops; 84562306a36Sopenharmony_ci dev->net->sysfs_groups[0] = &qmi_wwan_sysfs_attr_group; 84662306a36Sopenharmony_cierr: 84762306a36Sopenharmony_ci return status; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct qmi_wwan_state *info = (void *)&dev->data; 85362306a36Sopenharmony_ci struct usb_driver *driver = driver_of(intf); 85462306a36Sopenharmony_ci struct usb_interface *other; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (info->subdriver && info->subdriver->disconnect) 85762306a36Sopenharmony_ci info->subdriver->disconnect(info->control); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci /* disable MDM9x30 quirk */ 86062306a36Sopenharmony_ci if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) { 86162306a36Sopenharmony_ci qmi_wwan_change_dtr(dev, false); 86262306a36Sopenharmony_ci qmi_wwan_manage_power(dev, 0); 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci /* allow user to unbind using either control or data */ 86662306a36Sopenharmony_ci if (intf == info->control) 86762306a36Sopenharmony_ci other = info->data; 86862306a36Sopenharmony_ci else 86962306a36Sopenharmony_ci other = info->control; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci /* only if not shared */ 87262306a36Sopenharmony_ci if (other && intf != other) { 87362306a36Sopenharmony_ci usb_set_intfdata(other, NULL); 87462306a36Sopenharmony_ci usb_driver_release_interface(driver, other); 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci info->subdriver = NULL; 87862306a36Sopenharmony_ci info->data = NULL; 87962306a36Sopenharmony_ci info->control = NULL; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci/* suspend/resume wrappers calling both usbnet and the cdc-wdm 88362306a36Sopenharmony_ci * subdriver if present. 88462306a36Sopenharmony_ci * 88562306a36Sopenharmony_ci * NOTE: cdc-wdm also supports pre/post_reset, but we cannot provide 88662306a36Sopenharmony_ci * wrappers for those without adding usbnet reset support first. 88762306a36Sopenharmony_ci */ 88862306a36Sopenharmony_cistatic int qmi_wwan_suspend(struct usb_interface *intf, pm_message_t message) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci struct usbnet *dev = usb_get_intfdata(intf); 89162306a36Sopenharmony_ci struct qmi_wwan_state *info = (void *)&dev->data; 89262306a36Sopenharmony_ci int ret; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci /* Both usbnet_suspend() and subdriver->suspend() MUST return 0 89562306a36Sopenharmony_ci * in system sleep context, otherwise, the resume callback has 89662306a36Sopenharmony_ci * to recover device from previous suspend failure. 89762306a36Sopenharmony_ci */ 89862306a36Sopenharmony_ci ret = usbnet_suspend(intf, message); 89962306a36Sopenharmony_ci if (ret < 0) 90062306a36Sopenharmony_ci goto err; 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci if (intf == info->control && info->subdriver && 90362306a36Sopenharmony_ci info->subdriver->suspend) 90462306a36Sopenharmony_ci ret = info->subdriver->suspend(intf, message); 90562306a36Sopenharmony_ci if (ret < 0) 90662306a36Sopenharmony_ci usbnet_resume(intf); 90762306a36Sopenharmony_cierr: 90862306a36Sopenharmony_ci return ret; 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_cistatic int qmi_wwan_resume(struct usb_interface *intf) 91262306a36Sopenharmony_ci{ 91362306a36Sopenharmony_ci struct usbnet *dev = usb_get_intfdata(intf); 91462306a36Sopenharmony_ci struct qmi_wwan_state *info = (void *)&dev->data; 91562306a36Sopenharmony_ci int ret = 0; 91662306a36Sopenharmony_ci bool callsub = (intf == info->control && info->subdriver && 91762306a36Sopenharmony_ci info->subdriver->resume); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (callsub) 92062306a36Sopenharmony_ci ret = info->subdriver->resume(intf); 92162306a36Sopenharmony_ci if (ret < 0) 92262306a36Sopenharmony_ci goto err; 92362306a36Sopenharmony_ci ret = usbnet_resume(intf); 92462306a36Sopenharmony_ci if (ret < 0 && callsub) 92562306a36Sopenharmony_ci info->subdriver->suspend(intf, PMSG_SUSPEND); 92662306a36Sopenharmony_cierr: 92762306a36Sopenharmony_ci return ret; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic const struct driver_info qmi_wwan_info = { 93162306a36Sopenharmony_ci .description = "WWAN/QMI device", 93262306a36Sopenharmony_ci .flags = FLAG_WWAN | FLAG_SEND_ZLP, 93362306a36Sopenharmony_ci .bind = qmi_wwan_bind, 93462306a36Sopenharmony_ci .unbind = qmi_wwan_unbind, 93562306a36Sopenharmony_ci .manage_power = qmi_wwan_manage_power, 93662306a36Sopenharmony_ci .rx_fixup = qmi_wwan_rx_fixup, 93762306a36Sopenharmony_ci}; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic const struct driver_info qmi_wwan_info_quirk_dtr = { 94062306a36Sopenharmony_ci .description = "WWAN/QMI device", 94162306a36Sopenharmony_ci .flags = FLAG_WWAN | FLAG_SEND_ZLP, 94262306a36Sopenharmony_ci .bind = qmi_wwan_bind, 94362306a36Sopenharmony_ci .unbind = qmi_wwan_unbind, 94462306a36Sopenharmony_ci .manage_power = qmi_wwan_manage_power, 94562306a36Sopenharmony_ci .rx_fixup = qmi_wwan_rx_fixup, 94662306a36Sopenharmony_ci .data = QMI_WWAN_QUIRK_DTR, 94762306a36Sopenharmony_ci}; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci#define HUAWEI_VENDOR_ID 0x12D1 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci/* map QMI/wwan function by a fixed interface number */ 95262306a36Sopenharmony_ci#define QMI_FIXED_INTF(vend, prod, num) \ 95362306a36Sopenharmony_ci USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \ 95462306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci/* devices requiring "set DTR" quirk */ 95762306a36Sopenharmony_ci#define QMI_QUIRK_SET_DTR(vend, prod, num) \ 95862306a36Sopenharmony_ci USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \ 95962306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info_quirk_dtr 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci/* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */ 96262306a36Sopenharmony_ci#define QMI_GOBI1K_DEVICE(vend, prod) \ 96362306a36Sopenharmony_ci QMI_FIXED_INTF(vend, prod, 3) 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci/* Gobi 2000/3000 QMI/wwan interface number is 0 according to qcserial */ 96662306a36Sopenharmony_ci#define QMI_GOBI_DEVICE(vend, prod) \ 96762306a36Sopenharmony_ci QMI_FIXED_INTF(vend, prod, 0) 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci/* Many devices have QMI and DIAG functions which are distinguishable 97062306a36Sopenharmony_ci * from other vendor specific functions by class, subclass and 97162306a36Sopenharmony_ci * protocol all being 0xff. The DIAG function has exactly 2 endpoints 97262306a36Sopenharmony_ci * and is silently rejected when probed. 97362306a36Sopenharmony_ci * 97462306a36Sopenharmony_ci * This makes it possible to match dynamically numbered QMI functions 97562306a36Sopenharmony_ci * as seen on e.g. many Quectel modems. 97662306a36Sopenharmony_ci */ 97762306a36Sopenharmony_ci#define QMI_MATCH_FF_FF_FF(vend, prod) \ 97862306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(vend, prod, USB_CLASS_VENDOR_SPEC, \ 97962306a36Sopenharmony_ci USB_SUBCLASS_VENDOR_SPEC, 0xff), \ 98062306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info_quirk_dtr 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_cistatic const struct usb_device_id products[] = { 98362306a36Sopenharmony_ci /* 1. CDC ECM like devices match on the control interface */ 98462306a36Sopenharmony_ci { /* Huawei E392, E398 and possibly others sharing both device id and more... */ 98562306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 9), 98662306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 98762306a36Sopenharmony_ci }, 98862306a36Sopenharmony_ci { /* Vodafone/Huawei K5005 (12d1:14c8) and similar modems */ 98962306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 57), 99062306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 99162306a36Sopenharmony_ci }, 99262306a36Sopenharmony_ci { /* HUAWEI_INTERFACE_NDIS_CONTROL_QUALCOMM */ 99362306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 0x01, 0x69), 99462306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 99562306a36Sopenharmony_ci }, 99662306a36Sopenharmony_ci { /* Motorola Mapphone devices with MDM6600 */ 99762306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(0x22b8, USB_CLASS_VENDOR_SPEC, 0xfb, 0xff), 99862306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 99962306a36Sopenharmony_ci }, 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci /* 2. Combined interface devices matching on class+protocol */ 100262306a36Sopenharmony_ci { /* Huawei E367 and possibly others in "Windows mode" */ 100362306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 7), 100462306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 100562306a36Sopenharmony_ci }, 100662306a36Sopenharmony_ci { /* Huawei E392, E398 and possibly others in "Windows mode" */ 100762306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 17), 100862306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 100962306a36Sopenharmony_ci }, 101062306a36Sopenharmony_ci { /* HUAWEI_NDIS_SINGLE_INTERFACE_VDF */ 101162306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 0x01, 0x37), 101262306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 101362306a36Sopenharmony_ci }, 101462306a36Sopenharmony_ci { /* HUAWEI_INTERFACE_NDIS_HW_QUALCOMM */ 101562306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 0x01, 0x67), 101662306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 101762306a36Sopenharmony_ci }, 101862306a36Sopenharmony_ci { /* Pantech UML290, P4200 and more */ 101962306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff), 102062306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 102162306a36Sopenharmony_ci }, 102262306a36Sopenharmony_ci { /* Pantech UML290 - newer firmware */ 102362306a36Sopenharmony_ci USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf1, 0xff), 102462306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 102562306a36Sopenharmony_ci }, 102662306a36Sopenharmony_ci { /* Novatel USB551L and MC551 */ 102762306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x1410, 0xb001, 102862306a36Sopenharmony_ci USB_CLASS_COMM, 102962306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 103062306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 103162306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 103262306a36Sopenharmony_ci }, 103362306a36Sopenharmony_ci { /* Novatel E362 */ 103462306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x1410, 0x9010, 103562306a36Sopenharmony_ci USB_CLASS_COMM, 103662306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 103762306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 103862306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 103962306a36Sopenharmony_ci }, 104062306a36Sopenharmony_ci { /* Novatel Expedite E371 */ 104162306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x1410, 0x9011, 104262306a36Sopenharmony_ci USB_CLASS_COMM, 104362306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 104462306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 104562306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 104662306a36Sopenharmony_ci }, 104762306a36Sopenharmony_ci { /* Dell Wireless 5800 (Novatel E362) */ 104862306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x413C, 0x8195, 104962306a36Sopenharmony_ci USB_CLASS_COMM, 105062306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 105162306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 105262306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 105362306a36Sopenharmony_ci }, 105462306a36Sopenharmony_ci { /* Dell Wireless 5800 V2 (Novatel E362) */ 105562306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x413C, 0x8196, 105662306a36Sopenharmony_ci USB_CLASS_COMM, 105762306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 105862306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 105962306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 106062306a36Sopenharmony_ci }, 106162306a36Sopenharmony_ci { /* Dell Wireless 5804 (Novatel E371) */ 106262306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x413C, 0x819b, 106362306a36Sopenharmony_ci USB_CLASS_COMM, 106462306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 106562306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 106662306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 106762306a36Sopenharmony_ci }, 106862306a36Sopenharmony_ci { /* ADU960S */ 106962306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x16d5, 0x650a, 107062306a36Sopenharmony_ci USB_CLASS_COMM, 107162306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 107262306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 107362306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 107462306a36Sopenharmony_ci }, 107562306a36Sopenharmony_ci { /* HP lt2523 (Novatel E371) */ 107662306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x421d, 107762306a36Sopenharmony_ci USB_CLASS_COMM, 107862306a36Sopenharmony_ci USB_CDC_SUBCLASS_ETHERNET, 107962306a36Sopenharmony_ci USB_CDC_PROTO_NONE), 108062306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 108162306a36Sopenharmony_ci }, 108262306a36Sopenharmony_ci { /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */ 108362306a36Sopenharmony_ci USB_DEVICE_AND_INTERFACE_INFO(0x03f0, 0x581d, USB_CLASS_VENDOR_SPEC, 1, 7), 108462306a36Sopenharmony_ci .driver_info = (unsigned long)&qmi_wwan_info, 108562306a36Sopenharmony_ci }, 108662306a36Sopenharmony_ci {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)}, /* Quectel EC25, EC20 R2.0 Mini PCIe */ 108762306a36Sopenharmony_ci {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)}, /* Quectel EP06/EG06/EM06 */ 108862306a36Sopenharmony_ci {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)}, /* Quectel EG12/EM12 */ 108962306a36Sopenharmony_ci {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0620)}, /* Quectel EM160R-GL */ 109062306a36Sopenharmony_ci {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)}, /* Quectel RM500Q-GL */ 109162306a36Sopenharmony_ci {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0801)}, /* Quectel RM520N */ 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* 3. Combined interface devices matching on interface number */ 109462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0408, 0xea42, 4)}, /* Yota / Megafon M100-1 */ 109562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x6001, 3)}, /* 4G LTE usb-modem U901 */ 109662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x7000, 0)}, 109762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x7001, 1)}, 109862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x7002, 1)}, 109962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x7101, 1)}, 110062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x7101, 2)}, 110162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x7101, 3)}, 110262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x7102, 1)}, 110362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x7102, 2)}, 110462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x7102, 3)}, 110562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x8000, 7)}, 110662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x8001, 6)}, 110762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9000, 4)}, 110862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9003, 4)}, 110962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9005, 2)}, 111062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x900a, 4)}, 111162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x900b, 2)}, 111262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x900c, 4)}, 111362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x900c, 5)}, 111462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x900c, 6)}, 111562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x900d, 5)}, 111662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x900f, 3)}, 111762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x900f, 4)}, 111862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x900f, 5)}, 111962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9010, 4)}, 112062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9010, 5)}, 112162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9011, 3)}, 112262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9011, 4)}, 112362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9021, 1)}, 112462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9022, 2)}, 112562306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x05c6, 0x9025, 4)}, /* Alcatel-sbell ASB TL131 TDD LTE (China Mobile) */ 112662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9026, 3)}, 112762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x902e, 5)}, 112862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9031, 5)}, 112962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9032, 4)}, 113062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9033, 3)}, 113162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9033, 4)}, 113262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9033, 5)}, 113362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9033, 6)}, 113462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9034, 3)}, 113562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9034, 4)}, 113662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9034, 5)}, 113762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9034, 6)}, 113862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9034, 7)}, 113962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9035, 4)}, 114062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9036, 3)}, 114162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9037, 5)}, 114262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9038, 4)}, 114362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x903b, 7)}, 114462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x903c, 6)}, 114562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x903d, 6)}, 114662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x903e, 5)}, 114762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9043, 3)}, 114862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9046, 3)}, 114962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9046, 4)}, 115062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9046, 5)}, 115162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9047, 2)}, 115262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9047, 3)}, 115362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9047, 4)}, 115462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9048, 4)}, 115562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9048, 5)}, 115662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9048, 6)}, 115762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9048, 7)}, 115862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9048, 8)}, 115962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x904c, 5)}, 116062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x904c, 6)}, 116162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x904c, 7)}, 116262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x904c, 8)}, 116362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9050, 3)}, 116462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9052, 4)}, 116562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9053, 6)}, 116662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9053, 7)}, 116762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9054, 5)}, 116862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9054, 6)}, 116962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9055, 3)}, 117062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9055, 4)}, 117162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9055, 5)}, 117262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9055, 6)}, 117362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9055, 7)}, 117462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9056, 3)}, 117562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9062, 2)}, 117662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9062, 3)}, 117762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9062, 4)}, 117862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9062, 5)}, 117962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9062, 6)}, 118062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9062, 7)}, 118162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9062, 8)}, 118262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9062, 9)}, 118362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9064, 3)}, 118462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9065, 6)}, 118562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9065, 7)}, 118662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9066, 5)}, 118762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9066, 6)}, 118862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9067, 1)}, 118962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9068, 2)}, 119062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9068, 3)}, 119162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9068, 4)}, 119262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9068, 5)}, 119362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9068, 6)}, 119462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9068, 7)}, 119562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9069, 5)}, 119662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9069, 6)}, 119762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9069, 7)}, 119862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9069, 8)}, 119962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9070, 4)}, 120062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9070, 5)}, 120162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9075, 5)}, 120262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9076, 4)}, 120362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9076, 5)}, 120462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9076, 6)}, 120562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9076, 7)}, 120662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9076, 8)}, 120762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9077, 3)}, 120862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9077, 4)}, 120962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9077, 5)}, 121062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9077, 6)}, 121162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9078, 3)}, 121262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9079, 4)}, 121362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9079, 5)}, 121462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9079, 6)}, 121562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9079, 7)}, 121662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9079, 8)}, 121762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9080, 5)}, 121862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9080, 6)}, 121962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9080, 7)}, 122062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9080, 8)}, 122162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9083, 3)}, 122262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9084, 4)}, 122362306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x05c6, 0x9091, 2)}, /* Compal RXM-G1 */ 122462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x90b2, 3)}, /* ublox R410M */ 122562306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x05c6, 0x90db, 2)}, /* Compal RXM-G1 */ 122662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x920d, 0)}, 122762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x920d, 5)}, 122862306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x05c6, 0x9625, 4)}, /* YUGA CLM920-NC5 */ 122962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0846, 0x68a2, 8)}, 123062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0846, 0x68d3, 8)}, /* Netgear Aircard 779S */ 123162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x12d1, 0x140c, 1)}, /* Huawei E173 */ 123262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x12d1, 0x14ac, 1)}, /* Huawei E1820 */ 123362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1435, 0x0918, 3)}, /* Wistron NeWeb D16Q1 */ 123462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1435, 0x0918, 4)}, /* Wistron NeWeb D16Q1 */ 123562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1435, 0x0918, 5)}, /* Wistron NeWeb D16Q1 */ 123662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1435, 0x3185, 4)}, /* Wistron NeWeb M18Q5 */ 123762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1435, 0xd111, 4)}, /* M9615A DM11-1 D51QC */ 123862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1435, 0xd181, 3)}, /* Wistron NeWeb D18Q1 */ 123962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1435, 0xd181, 4)}, /* Wistron NeWeb D18Q1 */ 124062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1435, 0xd181, 5)}, /* Wistron NeWeb D18Q1 */ 124162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1435, 0xd182, 4)}, /* Wistron NeWeb D18 */ 124262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1435, 0xd182, 5)}, /* Wistron NeWeb D18 */ 124362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1435, 0xd191, 4)}, /* Wistron NeWeb D19Q1 */ 124462306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1508, 0x1001, 4)}, /* Fibocom NL668 series */ 124562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1690, 0x7588, 4)}, /* ASKEY WWHC050 */ 124662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x6003, 0)}, /* CMOTech 6003 */ 124762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x6007, 0)}, /* CMOTech CHE-628S */ 124862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x6008, 0)}, /* CMOTech CMU-301 */ 124962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x6280, 0)}, /* CMOTech CHU-628 */ 125062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x7001, 0)}, /* CMOTech CHU-720S */ 125162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x7002, 0)}, /* CMOTech 7002 */ 125262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x7003, 4)}, /* CMOTech CHU-629K */ 125362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x7004, 3)}, /* CMOTech 7004 */ 125462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x7006, 5)}, /* CMOTech CGU-629 */ 125562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x700a, 4)}, /* CMOTech CHU-629S */ 125662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x7211, 0)}, /* CMOTech CHU-720I */ 125762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x7212, 0)}, /* CMOTech 7212 */ 125862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x7213, 0)}, /* CMOTech 7213 */ 125962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x7251, 1)}, /* CMOTech 7251 */ 126062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x7252, 1)}, /* CMOTech 7252 */ 126162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x16d8, 0x7253, 1)}, /* CMOTech 7253 */ 126262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0002, 1)}, 126362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0012, 1)}, 126462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0017, 3)}, 126562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0019, 3)}, /* ONDA MT689DC */ 126662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0021, 4)}, 126762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0025, 1)}, 126862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0031, 4)}, 126962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0042, 4)}, 127062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0049, 5)}, 127162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0052, 4)}, 127262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0055, 1)}, /* ZTE (Vodafone) K3520-Z */ 127362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0058, 4)}, 127462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0063, 4)}, /* ZTE (Vodafone) K3565-Z */ 127562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0104, 4)}, /* ZTE (Vodafone) K4505-Z */ 127662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0113, 5)}, 127762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0118, 5)}, 127862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0121, 5)}, 127962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0123, 4)}, 128062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0124, 5)}, 128162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0125, 6)}, 128262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0126, 5)}, 128362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0130, 1)}, 128462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0133, 3)}, 128562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0141, 5)}, 128662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0157, 5)}, /* ZTE MF683 */ 128762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0158, 3)}, 128862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0167, 4)}, /* ZTE MF820D */ 128962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0168, 4)}, 129062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0176, 3)}, 129162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0178, 3)}, 129262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0189, 4)}, /* ZTE MF290 */ 129362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0191, 4)}, /* ZTE EuFi890 */ 129462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0199, 1)}, /* ZTE MF820S */ 129562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0200, 1)}, 129662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0257, 3)}, /* ZTE MF821 */ 129762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0265, 4)}, /* ONDA MT8205 4G LTE */ 129862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0284, 4)}, /* ZTE MF880 */ 129962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0326, 4)}, /* ZTE MF821D */ 130062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0396, 3)}, /* ZTE ZM8620 */ 130162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x0412, 4)}, /* Telewell TW-LTE 4G */ 130262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1008, 4)}, /* ZTE (Vodafone) K3570-Z */ 130362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1010, 4)}, /* ZTE (Vodafone) K3571-Z */ 130462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1012, 4)}, 130562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1018, 3)}, /* ZTE (Vodafone) K5006-Z */ 130662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1021, 2)}, 130762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1245, 4)}, 130862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1247, 4)}, 130962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1252, 4)}, 131062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1254, 4)}, 131162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1255, 3)}, 131262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1255, 4)}, 131362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1256, 4)}, 131462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1270, 5)}, /* ZTE MF667 */ 131562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1275, 3)}, /* ZTE P685M */ 131662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1401, 2)}, 131762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1402, 2)}, /* ZTE MF60 */ 131862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1424, 2)}, 131962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1425, 2)}, 132062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1426, 2)}, /* ZTE MF91 */ 132162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1428, 2)}, /* Telewell TW-LTE 4G v2 */ 132262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1432, 3)}, /* ZTE ME3620 */ 132362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x1485, 5)}, /* ZTE MF286D */ 132462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */ 132562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x2001, 0x7e16, 3)}, /* D-Link DWM-221 */ 132662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x2001, 0x7e19, 4)}, /* D-Link DWM-221 B1 */ 132762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x2001, 0x7e35, 4)}, /* D-Link DWM-222 */ 132862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x2001, 0x7e3d, 4)}, /* D-Link DWM-222 A2 */ 132962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x2020, 0x2031, 4)}, /* Olicard 600 */ 133062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x2020, 0x2033, 4)}, /* BroadMobi BM806U */ 133162306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x2020, 0x2060, 4)}, /* BroadMobi BM818 */ 133262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */ 133362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */ 133462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */ 133562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x68a2, 19)}, /* Sierra Wireless MC7710 in QMI mode */ 133662306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1199, 0x68c0, 8)}, /* Sierra Wireless MC7304/MC7354, WP76xx */ 133762306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1199, 0x68c0, 10)},/* Sierra Wireless MC7304/MC7354 */ 133862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x901c, 8)}, /* Sierra Wireless EM7700 */ 133962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x901f, 8)}, /* Sierra Wireless EM7355 */ 134062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x9041, 8)}, /* Sierra Wireless MC7305/MC7355 */ 134162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x9041, 10)}, /* Sierra Wireless MC7305/MC7355 */ 134262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x9051, 8)}, /* Netgear AirCard 340U */ 134362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x9053, 8)}, /* Sierra Wireless Modem */ 134462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x9054, 8)}, /* Sierra Wireless Modem */ 134562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x9055, 8)}, /* Netgear AirCard 341U */ 134662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x9056, 8)}, /* Sierra Wireless Modem */ 134762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x9057, 8)}, 134862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x9061, 8)}, /* Sierra Wireless Modem */ 134962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x9063, 8)}, /* Sierra Wireless EM7305 */ 135062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1199, 0x9063, 10)}, /* Sierra Wireless EM7305 */ 135162306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1199, 0x9071, 8)}, /* Sierra Wireless MC74xx */ 135262306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1199, 0x9071, 10)},/* Sierra Wireless MC74xx */ 135362306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1199, 0x9079, 8)}, /* Sierra Wireless EM74xx */ 135462306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1199, 0x9079, 10)},/* Sierra Wireless EM74xx */ 135562306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1199, 0x907b, 8)}, /* Sierra Wireless EM74xx */ 135662306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1199, 0x907b, 10)},/* Sierra Wireless EM74xx */ 135762306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1199, 0x9091, 8)}, /* Sierra Wireless EM7565 */ 135862306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1199, 0xc081, 8)}, /* Sierra Wireless EM7590 */ 135962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ 136062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1bbb, 0x0203, 2)}, /* Alcatel L800MA */ 136162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ 136262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x2357, 0x9000, 4)}, /* TP-LINK MA260 */ 136362306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1031, 3)}, /* Telit LE910C1-EUX */ 136462306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x103a, 0)}, /* Telit LE910C4-WWX */ 136562306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1040, 2)}, /* Telit LE922A */ 136662306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1050, 2)}, /* Telit FN980 */ 136762306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1057, 2)}, /* Telit FN980 */ 136862306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1060, 2)}, /* Telit LN920 */ 136962306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1070, 2)}, /* Telit FN990 */ 137062306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1080, 2)}, /* Telit FE990 */ 137162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1bc7, 0x1100, 3)}, /* Telit ME910 */ 137262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1bc7, 0x1101, 3)}, /* Telit ME910 dual modem */ 137362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)}, /* Telit LE920 */ 137462306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1201, 2)}, /* Telit LE920, LE920A4 */ 137562306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1230, 2)}, /* Telit LE910Cx */ 137662306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1250, 0)}, /* Telit LE910Cx */ 137762306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1260, 2)}, /* Telit LE910Cx */ 137862306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1261, 2)}, /* Telit LE910Cx */ 137962306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1bc7, 0x1900, 1)}, /* Telit LN940 series */ 138062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1c9e, 0x9801, 3)}, /* Telewell TW-3G HSPA+ */ 138162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1c9e, 0x9803, 4)}, /* Telewell TW-3G HSPA+ */ 138262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1c9e, 0x9b01, 3)}, /* XS Stick W100-2 from 4G Systems */ 138362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0b3c, 0xc000, 4)}, /* Olivetti Olicard 100 */ 138462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0b3c, 0xc001, 4)}, /* Olivetti Olicard 120 */ 138562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0b3c, 0xc002, 4)}, /* Olivetti Olicard 140 */ 138662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0b3c, 0xc004, 6)}, /* Olivetti Olicard 155 */ 138762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0b3c, 0xc005, 6)}, /* Olivetti Olicard 200 */ 138862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0b3c, 0xc00a, 6)}, /* Olivetti Olicard 160 */ 138962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0b3c, 0xc00b, 4)}, /* Olivetti Olicard 500 */ 139062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1e2d, 0x0060, 4)}, /* Cinterion PLxx */ 139162306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1e2d, 0x006f, 8)}, /* Cinterion PLS83/PLS63 */ 139262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1e2d, 0x0053, 4)}, /* Cinterion PHxx,PXxx */ 139362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1e2d, 0x0063, 10)}, /* Cinterion ALASxx (1 RmNet) */ 139462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1e2d, 0x0082, 4)}, /* Cinterion PHxx,PXxx (2 RmNet) */ 139562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1e2d, 0x0082, 5)}, /* Cinterion PHxx,PXxx (2 RmNet) */ 139662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1e2d, 0x0083, 4)}, /* Cinterion PHxx,PXxx (1 RmNet + USB Audio)*/ 139762306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1e2d, 0x00b0, 4)}, /* Cinterion CLS8 */ 139862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1e2d, 0x00b7, 0)}, /* Cinterion MV31 RmNet */ 139962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1e2d, 0x00b9, 0)}, /* Cinterion MV31 RmNet based on new baseline */ 140062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1e2d, 0x00f3, 0)}, /* Cinterion MV32-W-A RmNet */ 140162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x1e2d, 0x00f4, 0)}, /* Cinterion MV32-W-B RmNet */ 140262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81a2, 8)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */ 140362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81a3, 8)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */ 140462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81a4, 8)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ 140562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81a8, 8)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */ 140662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81a9, 8)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */ 140762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81b1, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card */ 140862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81b3, 8)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */ 140962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81b6, 8)}, /* Dell Wireless 5811e */ 141062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81b6, 10)}, /* Dell Wireless 5811e */ 141162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81c2, 8)}, /* Dell Wireless 5811e */ 141262306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81cc, 8)}, /* Dell Wireless 5816e */ 141362306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81d7, 0)}, /* Dell Wireless 5821e */ 141462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81d7, 1)}, /* Dell Wireless 5821e preproduction config */ 141562306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81e0, 0)}, /* Dell Wireless 5821e with eSIM support*/ 141662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81e4, 0)}, /* Dell Wireless 5829e with eSIM support*/ 141762306a36Sopenharmony_ci {QMI_FIXED_INTF(0x413c, 0x81e6, 0)}, /* Dell Wireless 5829e */ 141862306a36Sopenharmony_ci {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */ 141962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x03f0, 0x9d1d, 1)}, /* HP lt4120 Snapdragon X5 LTE */ 142062306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x22de, 0x9051, 2)}, /* Hucom Wireless HM-211S/K */ 142162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x22de, 0x9061, 3)}, /* WeTelecom WPD-600N */ 142262306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1e0e, 0x9001, 5)}, /* SIMCom 7100E, 7230E, 7600E ++ */ 142362306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x2c7c, 0x0121, 4)}, /* Quectel EC21 Mini PCIe */ 142462306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x2c7c, 0x0191, 4)}, /* Quectel EG91 */ 142562306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x2c7c, 0x0195, 4)}, /* Quectel EG95 */ 142662306a36Sopenharmony_ci {QMI_FIXED_INTF(0x2c7c, 0x0296, 4)}, /* Quectel BG96 */ 142762306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x2c7c, 0x030e, 4)}, /* Quectel EM05GV2 */ 142862306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x2cb7, 0x0104, 4)}, /* Fibocom NL678 series */ 142962306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0489, 0xe0b4, 0)}, /* Foxconn T77W968 LTE */ 143062306a36Sopenharmony_ci {QMI_FIXED_INTF(0x0489, 0xe0b5, 0)}, /* Foxconn T77W968 LTE with eSIM support*/ 143162306a36Sopenharmony_ci {QMI_FIXED_INTF(0x2692, 0x9025, 4)}, /* Cellient MPL200 (rebranded Qualcomm 05c6:9025) */ 143262306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1546, 0x1312, 4)}, /* u-blox LARA-R6 01B */ 143362306a36Sopenharmony_ci {QMI_QUIRK_SET_DTR(0x1546, 0x1342, 4)}, /* u-blox LARA-L6 */ 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci /* 4. Gobi 1000 devices */ 143662306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ 143762306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */ 143862306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x04da, 0x250d)}, /* Panasonic Gobi Modem device */ 143962306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x413c, 0x8172)}, /* Dell Gobi Modem device */ 144062306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x1410, 0xa001)}, /* Novatel/Verizon USB-1000 */ 144162306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x1410, 0xa002)}, /* Novatel Gobi Modem device */ 144262306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x1410, 0xa003)}, /* Novatel Gobi Modem device */ 144362306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x1410, 0xa004)}, /* Novatel Gobi Modem device */ 144462306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x1410, 0xa005)}, /* Novatel Gobi Modem device */ 144562306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x1410, 0xa006)}, /* Novatel Gobi Modem device */ 144662306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x1410, 0xa007)}, /* Novatel Gobi Modem device */ 144762306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x0b05, 0x1776)}, /* Asus Gobi Modem device */ 144862306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x19d2, 0xfff3)}, /* ONDA Gobi Modem device */ 144962306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x05c6, 0x9001)}, /* Generic Gobi Modem device */ 145062306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x05c6, 0x9002)}, /* Generic Gobi Modem device */ 145162306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x05c6, 0x9202)}, /* Generic Gobi Modem device */ 145262306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x05c6, 0x9203)}, /* Generic Gobi Modem device */ 145362306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x05c6, 0x9222)}, /* Generic Gobi Modem device */ 145462306a36Sopenharmony_ci {QMI_GOBI1K_DEVICE(0x05c6, 0x9009)}, /* Generic Gobi Modem device */ 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci /* 5. Gobi 2000 and 3000 devices */ 145762306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */ 145862306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x413c, 0x8194)}, /* Dell Gobi 3000 Composite */ 145962306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x05c6, 0x920b)}, /* Generic Gobi 2000 Modem device */ 146062306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */ 146162306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */ 146262306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */ 146362306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */ 146462306a36Sopenharmony_ci {QMI_FIXED_INTF(0x05c6, 0x9215, 4)}, /* Quectel EC20 Mini PCIe */ 146562306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */ 146662306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */ 146762306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */ 146862306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x0af0, 0x8120)}, /* Option GTM681W */ 146962306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */ 147062306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x68a9)}, /* Sierra Wireless Modem */ 147162306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ 147262306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9002)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ 147362306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9003)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ 147462306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9004)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ 147562306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9005)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ 147662306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9006)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ 147762306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9007)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ 147862306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9008)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ 147962306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ 148062306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */ 148162306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9011)}, /* Sierra Wireless Gobi 2000 Modem device (MC8305) */ 148262306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */ 148362306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */ 148462306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */ 148562306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */ 148662306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9015)}, /* Sierra Wireless Gobi 3000 Modem device */ 148762306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x9019)}, /* Sierra Wireless Gobi 3000 Modem device */ 148862306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1199, 0x901b)}, /* Sierra Wireless MC7770 */ 148962306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x12d1, 0x14f1)}, /* Sony Gobi 3000 Composite */ 149062306a36Sopenharmony_ci {QMI_GOBI_DEVICE(0x1410, 0xa021)}, /* Foxconn Gobi 3000 Modem device (Novatel E396) */ 149162306a36Sopenharmony_ci 149262306a36Sopenharmony_ci { } /* END */ 149362306a36Sopenharmony_ci}; 149462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, products); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cistatic bool quectel_ec20_detected(struct usb_interface *intf) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci struct usb_device *dev = interface_to_usbdev(intf); 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci if (dev->actconfig && 150162306a36Sopenharmony_ci le16_to_cpu(dev->descriptor.idVendor) == 0x05c6 && 150262306a36Sopenharmony_ci le16_to_cpu(dev->descriptor.idProduct) == 0x9215 && 150362306a36Sopenharmony_ci dev->actconfig->desc.bNumInterfaces == 5) 150462306a36Sopenharmony_ci return true; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci return false; 150762306a36Sopenharmony_ci} 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_cistatic int qmi_wwan_probe(struct usb_interface *intf, 151062306a36Sopenharmony_ci const struct usb_device_id *prod) 151162306a36Sopenharmony_ci{ 151262306a36Sopenharmony_ci struct usb_device_id *id = (struct usb_device_id *)prod; 151362306a36Sopenharmony_ci struct usb_interface_descriptor *desc = &intf->cur_altsetting->desc; 151462306a36Sopenharmony_ci 151562306a36Sopenharmony_ci /* Workaround to enable dynamic IDs. This disables usbnet 151662306a36Sopenharmony_ci * blacklisting functionality. Which, if required, can be 151762306a36Sopenharmony_ci * reimplemented here by using a magic "blacklist" value 151862306a36Sopenharmony_ci * instead of 0 in the static device id table 151962306a36Sopenharmony_ci */ 152062306a36Sopenharmony_ci if (!id->driver_info) { 152162306a36Sopenharmony_ci dev_dbg(&intf->dev, "setting defaults for dynamic device id\n"); 152262306a36Sopenharmony_ci id->driver_info = (unsigned long)&qmi_wwan_info; 152362306a36Sopenharmony_ci } 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci /* There are devices where the same interface number can be 152662306a36Sopenharmony_ci * configured as different functions. We should only bind to 152762306a36Sopenharmony_ci * vendor specific functions when matching on interface number 152862306a36Sopenharmony_ci */ 152962306a36Sopenharmony_ci if (id->match_flags & USB_DEVICE_ID_MATCH_INT_NUMBER && 153062306a36Sopenharmony_ci desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC) { 153162306a36Sopenharmony_ci dev_dbg(&intf->dev, 153262306a36Sopenharmony_ci "Rejecting interface number match for class %02x\n", 153362306a36Sopenharmony_ci desc->bInterfaceClass); 153462306a36Sopenharmony_ci return -ENODEV; 153562306a36Sopenharmony_ci } 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci /* Quectel EC20 quirk where we've QMI on interface 4 instead of 0 */ 153862306a36Sopenharmony_ci if (quectel_ec20_detected(intf) && desc->bInterfaceNumber == 0) { 153962306a36Sopenharmony_ci dev_dbg(&intf->dev, "Quectel EC20 quirk, skipping interface 0\n"); 154062306a36Sopenharmony_ci return -ENODEV; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci /* Several Quectel modems supports dynamic interface configuration, so 154462306a36Sopenharmony_ci * we need to match on class/subclass/protocol. These values are 154562306a36Sopenharmony_ci * identical for the diagnostic- and QMI-interface, but bNumEndpoints is 154662306a36Sopenharmony_ci * different. Ignore the current interface if the number of endpoints 154762306a36Sopenharmony_ci * equals the number for the diag interface (two). 154862306a36Sopenharmony_ci */ 154962306a36Sopenharmony_ci if (desc->bNumEndpoints == 2) 155062306a36Sopenharmony_ci return -ENODEV; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci return usbnet_probe(intf, id); 155362306a36Sopenharmony_ci} 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_cistatic void qmi_wwan_disconnect(struct usb_interface *intf) 155662306a36Sopenharmony_ci{ 155762306a36Sopenharmony_ci struct usbnet *dev = usb_get_intfdata(intf); 155862306a36Sopenharmony_ci struct qmi_wwan_state *info; 155962306a36Sopenharmony_ci struct list_head *iter; 156062306a36Sopenharmony_ci struct net_device *ldev; 156162306a36Sopenharmony_ci LIST_HEAD(list); 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_ci /* called twice if separate control and data intf */ 156462306a36Sopenharmony_ci if (!dev) 156562306a36Sopenharmony_ci return; 156662306a36Sopenharmony_ci info = (void *)&dev->data; 156762306a36Sopenharmony_ci if (info->flags & QMI_WWAN_FLAG_MUX) { 156862306a36Sopenharmony_ci if (!rtnl_trylock()) { 156962306a36Sopenharmony_ci restart_syscall(); 157062306a36Sopenharmony_ci return; 157162306a36Sopenharmony_ci } 157262306a36Sopenharmony_ci rcu_read_lock(); 157362306a36Sopenharmony_ci netdev_for_each_upper_dev_rcu(dev->net, ldev, iter) 157462306a36Sopenharmony_ci qmimux_unregister_device(ldev, &list); 157562306a36Sopenharmony_ci rcu_read_unlock(); 157662306a36Sopenharmony_ci unregister_netdevice_many(&list); 157762306a36Sopenharmony_ci rtnl_unlock(); 157862306a36Sopenharmony_ci info->flags &= ~QMI_WWAN_FLAG_MUX; 157962306a36Sopenharmony_ci } 158062306a36Sopenharmony_ci usbnet_disconnect(intf); 158162306a36Sopenharmony_ci} 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_cistatic struct usb_driver qmi_wwan_driver = { 158462306a36Sopenharmony_ci .name = "qmi_wwan", 158562306a36Sopenharmony_ci .id_table = products, 158662306a36Sopenharmony_ci .probe = qmi_wwan_probe, 158762306a36Sopenharmony_ci .disconnect = qmi_wwan_disconnect, 158862306a36Sopenharmony_ci .suspend = qmi_wwan_suspend, 158962306a36Sopenharmony_ci .resume = qmi_wwan_resume, 159062306a36Sopenharmony_ci .reset_resume = qmi_wwan_resume, 159162306a36Sopenharmony_ci .supports_autosuspend = 1, 159262306a36Sopenharmony_ci .disable_hub_initiated_lpm = 1, 159362306a36Sopenharmony_ci}; 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_cimodule_usb_driver(qmi_wwan_driver); 159662306a36Sopenharmony_ci 159762306a36Sopenharmony_ciMODULE_AUTHOR("Bjørn Mork <bjorn@mork.no>"); 159862306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm MSM Interface (QMI) WWAN driver"); 159962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1600