aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2012-10-09 13:18:10 +0200
committerBjørn Mork <bjorn@mork.no>2012-10-09 16:25:21 +0200
commit2dae0637684044ca37f720454eb9ead393204c94 (patch)
tree909e16b860d3e499472340339295f5a1d4f33aea
parentcd1f26e384453fa3aea5b16789fb481681cc7328 (diff)
net: cdc_ncm: map MBIM IPS SessionID to VLAN ID
MBIM devices can support up to 256 independent IP Streams. The main network device will only handle SessionID 0. Mapping SessionIDs 1 to 255 to VLANs using the SessionID as VLAN ID allow userspace to use these streams with traditional tools like vconfig. Signed-off-by: Bjørn Mork <bjorn@mork.no>
-rw-r--r--drivers/net/usb/cdc_mbim.c51
1 files changed, 43 insertions, 8 deletions
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c
index 93c16678c6c..aaf1f9dfdb2 100644
--- a/drivers/net/usb/cdc_mbim.c
+++ b/drivers/net/usb/cdc_mbim.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
#include <linux/ip.h>
#include <linux/mii.h>
#include <linux/usb.h>
@@ -106,6 +107,9 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
/* MBIM cannot do ARP */
dev->net->flags |= IFF_NOARP;
+
+ /* no need to put the VLAN tci in the packet headers */
+ dev->net->features |= NETIF_F_HW_VLAN_TX;
err:
return ret;
}
@@ -130,6 +134,9 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
struct sk_buff *skb_out;
struct cdc_mbim_state *info = (void *)&dev->data;
struct cdc_ncm_ctx *ctx = info->ctx;
+ __le32 sign = cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN);
+ u16 tci = 0;
+ u8 *c;
if (!ctx)
goto error;
@@ -138,6 +145,24 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
if (skb->len <= sizeof(ETH_HLEN))
goto error;
+ /* mapping VLANs to MBIM sessions:
+ * no tag => IPS session <0>
+ * 1 - 255 => IPS session <vlanid>
+ * 256 - 4095 => unsupported, drop
+ */
+ vlan_get_tag(skb, &tci);
+
+ switch (tci & 0x0f00) {
+ case 0x0000: /* VLAN ID 0 - 255 */
+ c = (u8 *)&sign;
+ c[3] = tci;
+ break;
+ default:
+ netif_err(dev, tx_err, dev->net,
+ "unsupported tci=0x%04x\n", tci);
+ goto error;
+ }
+
skb_reset_mac_header(skb);
switch (eth_hdr(skb)->h_proto) {
case htons(ETH_P_IP):
@@ -150,7 +175,7 @@ static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb
}
spin_lock_bh(&ctx->mtx);
- skb_out = cdc_ncm_fill_tx_frame(ctx, skb, cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN));
+ skb_out = cdc_ncm_fill_tx_frame(ctx, skb, sign);
spin_unlock_bh(&ctx->mtx);
return skb_out;
@@ -161,11 +186,14 @@ error:
return NULL;
}
-static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_t len)
+static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_t len, u16 tci)
{
__be16 proto;
struct sk_buff *skb = NULL;
+ if (len < sizeof(struct iphdr))
+ goto err;
+
switch (*buf & 0xf0) {
case 0x40:
proto = htons(ETH_P_IP);
@@ -190,6 +218,10 @@ static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_
/* add datagram */
memcpy(skb_put(skb, len), buf, len);
+
+ /* map MBIM session to VLAN */
+ if (tci)
+ vlan_put_tag(skb, tci);
err:
return skb;
}
@@ -197,7 +229,8 @@ err:
static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
{
struct sk_buff *skb;
- struct cdc_ncm_ctx *ctx = (struct cdc_ncm_ctx *)dev->data[0];
+ struct cdc_mbim_state *info = (void *)&dev->data;
+ struct cdc_ncm_ctx *ctx = info->ctx;
int len;
int nframes;
int x;
@@ -206,6 +239,8 @@ static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in)
struct usb_cdc_ncm_dpe16 *dpe16;
int ndpoffset;
int loopcount = 50; /* arbitrary max preventing infinite loop */
+ u8 *c;
+ u16 tci;
ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in);
if (ndpoffset < 0)
@@ -218,9 +253,10 @@ next_ndp:
ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset);
- /* only supporting IPS Session #0 for now */
- switch (ndp16->dwSignature) {
+ switch (ndp16->dwSignature & cpu_to_le32(0x00ffffff)) {
case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN):
+ c = (u8 *)&ndp16->dwSignature;
+ tci = c[3];
break;
default:
netif_dbg(dev, rx_err, dev->net,
@@ -246,8 +282,7 @@ next_ndp:
}
/* sanity checking */
- if (((offset + len) > skb_in->len) || (len > ctx->rx_max) ||
- (len < sizeof(struct iphdr))) {
+ if (((offset + len) > skb_in->len) || (len > ctx->rx_max)) {
netif_dbg(dev, rx_err, dev->net,
"invalid frame detected (ignored) offset[%u]=%u, length=%u, skb=%p\n",
x, offset, len, skb_in);
@@ -255,7 +290,7 @@ next_ndp:
goto err_ndp;
break;
} else {
- skb = cdc_mbim_process_dgram(dev, skb_in->data + offset, len);
+ skb = cdc_mbim_process_dgram(dev, skb_in->data + offset, len, tci);
if (!skb)
goto error;
usbnet_skb_return(dev, skb);