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 d166c32..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);
@@ -1013,6 +1096,7 @@ static const struct usb_device_id products[] = {
        {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0125)},   /* Quectel EC25, EC20 R2.0  Mini PCIe */
        {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0306)},   /* Quectel EP06/EG06/EM06 */
        {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0512)},   /* Quectel EG12/EM12 */
+       {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0620)},   /* Quectel EM160R-GL */
        {QMI_MATCH_FF_FF_FF(0x2c7c, 0x0800)},   /* Quectel RM500Q-GL */
 
        /* 3. Combined interface devices matching on interface number */
@@ -1301,6 +1385,7 @@ static const struct usb_device_id products[] = {
        {QMI_FIXED_INTF(0x0b3c, 0xc00a, 6)},    /* Olivetti Olicard 160 */
        {QMI_FIXED_INTF(0x0b3c, 0xc00b, 4)},    /* Olivetti Olicard 500 */
        {QMI_FIXED_INTF(0x1e2d, 0x0060, 4)},    /* Cinterion PLxx */
+       {QMI_QUIRK_SET_DTR(0x1e2d, 0x006f, 8)}, /* Cinterion PLS83/PLS63 */
        {QMI_FIXED_INTF(0x1e2d, 0x0053, 4)},    /* Cinterion PHxx,PXxx */
        {QMI_FIXED_INTF(0x1e2d, 0x0063, 10)},   /* Cinterion ALASxx (1 RmNet) */
        {QMI_FIXED_INTF(0x1e2d, 0x0082, 4)},    /* Cinterion PHxx,PXxx (2 RmNet) */