Merge tag 'mt76-for-kvalo-2021-01-29' of https://github.com/nbd168/wireless
[linux-2.6-microblaze.git] / drivers / net / usb / qmi_wwan.c
index cc48192..c8b2b60 100644 (file)
@@ -57,6 +57,7 @@ struct qmi_wwan_state {
 enum qmi_wwan_flags {
        QMI_WWAN_FLAG_RAWIP = 1 << 0,
        QMI_WWAN_FLAG_MUX = 1 << 1,
+       QMI_WWAN_FLAG_PASS_THROUGH = 1 << 2,
 };
 
 enum qmi_wwan_quirks {
@@ -186,7 +187,7 @@ static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                net = qmimux_find_dev(dev, hdr->mux_id);
                if (!net)
                        goto skip;
-               skbn = netdev_alloc_skb(net, pkt_len);
+               skbn = netdev_alloc_skb(net, pkt_len + LL_MAX_HEADER);
                if (!skbn)
                        return 0;
                skbn->dev = net;
@@ -203,6 +204,7 @@ static int qmimux_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
                        goto skip;
                }
 
+               skb_reserve(skbn, LL_MAX_HEADER);
                skb_put_data(skbn, skb->data + offset + qmimux_hdr_sz, pkt_len);
                if (netif_rx(skbn) != NET_RX_SUCCESS) {
                        net->stats.rx_errors++;
@@ -217,6 +219,28 @@ skip:
        return 1;
 }
 
+static ssize_t mux_id_show(struct device *d, struct device_attribute *attr, char *buf)
+{
+       struct net_device *dev = to_net_dev(d);
+       struct qmimux_priv *priv;
+
+       priv = netdev_priv(dev);
+
+       return sysfs_emit(buf, "0x%02x\n", priv->mux_id);
+}
+
+static DEVICE_ATTR_RO(mux_id);
+
+static struct attribute *qmi_wwan_sysfs_qmimux_attrs[] = {
+       &dev_attr_mux_id.attr,
+       NULL,
+};
+
+static struct attribute_group qmi_wwan_sysfs_qmimux_attr_group = {
+       .name = "qmap",
+       .attrs = qmi_wwan_sysfs_qmimux_attrs,
+};
+
 static int qmimux_register_device(struct net_device *real_dev, u8 mux_id)
 {
        struct net_device *new_dev;
@@ -239,6 +263,8 @@ static int qmimux_register_device(struct net_device *real_dev, u8 mux_id)
                goto out_free_newdev;
        }
 
+       new_dev->sysfs_groups[0] = &qmi_wwan_sysfs_qmimux_attr_group;
+
        err = register_netdevice(new_dev);
        if (err < 0)
                goto out_free_newdev;
@@ -325,6 +351,13 @@ static ssize_t raw_ip_store(struct device *d,  struct device_attribute *attr, co
        if (enable == (info->flags & QMI_WWAN_FLAG_RAWIP))
                return len;
 
+       /* ip mode cannot be cleared when pass through mode is set */
+       if (!enable && (info->flags & QMI_WWAN_FLAG_PASS_THROUGH)) {
+               netdev_err(dev->net,
+                          "Cannot clear ip mode on pass through device\n");
+               return -EINVAL;
+       }
+
        if (!rtnl_trylock())
                return restart_syscall();
 
@@ -455,14 +488,59 @@ err:
        return ret;
 }
 
+static ssize_t pass_through_show(struct device *d,
+                                struct device_attribute *attr, char *buf)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct qmi_wwan_state *info;
+
+       info = (void *)&dev->data;
+       return sprintf(buf, "%c\n",
+                      info->flags & QMI_WWAN_FLAG_PASS_THROUGH ? 'Y' : 'N');
+}
+
+static ssize_t pass_through_store(struct device *d,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t len)
+{
+       struct usbnet *dev = netdev_priv(to_net_dev(d));
+       struct qmi_wwan_state *info;
+       bool enable;
+
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       info = (void *)&dev->data;
+
+       /* no change? */
+       if (enable == (info->flags & QMI_WWAN_FLAG_PASS_THROUGH))
+               return len;
+
+       /* pass through mode can be set for raw ip devices only */
+       if (!(info->flags & QMI_WWAN_FLAG_RAWIP)) {
+               netdev_err(dev->net,
+                          "Cannot set pass through mode on non ip device\n");
+               return -EINVAL;
+       }
+
+       if (enable)
+               info->flags |= QMI_WWAN_FLAG_PASS_THROUGH;
+       else
+               info->flags &= ~QMI_WWAN_FLAG_PASS_THROUGH;
+
+       return len;
+}
+
 static DEVICE_ATTR_RW(raw_ip);
 static DEVICE_ATTR_RW(add_mux);
 static DEVICE_ATTR_RW(del_mux);
+static DEVICE_ATTR_RW(pass_through);
 
 static struct attribute *qmi_wwan_sysfs_attrs[] = {
        &dev_attr_raw_ip.attr,
        &dev_attr_add_mux.attr,
        &dev_attr_del_mux.attr,
+       &dev_attr_pass_through.attr,
        NULL,
 };
 
@@ -509,6 +587,11 @@ static int qmi_wwan_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
        if (info->flags & QMI_WWAN_FLAG_MUX)
                return qmimux_rx_fixup(dev, skb);
 
+       if (info->flags & QMI_WWAN_FLAG_PASS_THROUGH) {
+               skb->protocol = htons(ETH_P_MAP);
+               return (netif_rx(skb) == NET_RX_SUCCESS);
+       }
+
        switch (skb->data[0] & 0xf0) {
        case 0x40:
                proto = htons(ETH_P_IP);