aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEnrico Mioso <mrkiko.rs@gmail.com>2015-07-08 07:05:57 -0400
committerDavid S. Miller <davem@davemloft.net>2015-07-09 17:58:31 -0400
commit4a0e3e989d66bb7204b163d9cfaa7fa96d0f2023 (patch)
tree7a2f829757f3ece636b821328639c0bad6dbaae6
parent5a0266af10246c84a6c712f365788800c7e23fd4 (diff)
cdc_ncm: Add support for moving NDP to end of NCM frame
NCM specs are not actually mandating a specific position in the frame for the NDP (Network Datagram Pointer). However, some Huawei devices will ignore our aggregates if it is not placed after the datagrams it points to. Add support for doing just this, in a per-device configurable way. While at it, update NCM subdrivers, disabling this functionality in all of them, except in huawei_cdc_ncm where it is enabled instead. We aren't making any distinction between different Huawei NCM devices, based on what the vendor driver does. Standard NCM devices are left unaffected: if they are compliant, they should be always usable, still stay on the safe side. This change has been tested and working with a Huawei E3131 device (which works regardless of NDP position), a Huawei E3531 (also working both ways) and an E3372 (which mandates NDP to be after indexed datagrams). V1->V2: - corrected wrong NDP acronym definition - fixed possible NULL pointer dereference - patch cleanup V2->V3: - Properly account for the NDP size when writing new packets to SKB Signed-off-by: Enrico Mioso <mrkiko.rs@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/usb/cdc_mbim.c2
-rw-r--r--drivers/net/usb/cdc_ncm.c61
-rw-r--r--drivers/net/usb/huawei_cdc_ncm.c7
-rw-r--r--include/linux/usb/cdc_ncm.h7
4 files changed, 67 insertions, 10 deletions
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c
index e4b7a47a825c..efc18e05af0a 100644
--- a/drivers/net/usb/cdc_mbim.c
+++ b/drivers/net/usb/cdc_mbim.c
@@ -158,7 +158,7 @@ static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
158 if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) 158 if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
159 goto err; 159 goto err;
160 160
161 ret = cdc_ncm_bind_common(dev, intf, data_altsetting); 161 ret = cdc_ncm_bind_common(dev, intf, data_altsetting, 0);
162 if (ret) 162 if (ret)
163 goto err; 163 goto err;
164 164
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 8067b8fbb0ee..1991e4a24657 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -684,10 +684,12 @@ static void cdc_ncm_free(struct cdc_ncm_ctx *ctx)
684 ctx->tx_curr_skb = NULL; 684 ctx->tx_curr_skb = NULL;
685 } 685 }
686 686
687 kfree(ctx->delayed_ndp16);
688
687 kfree(ctx); 689 kfree(ctx);
688} 690}
689 691
690int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting) 692int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags)
691{ 693{
692 const struct usb_cdc_union_desc *union_desc = NULL; 694 const struct usb_cdc_union_desc *union_desc = NULL;
693 struct cdc_ncm_ctx *ctx; 695 struct cdc_ncm_ctx *ctx;
@@ -855,6 +857,17 @@ advance:
855 /* finish setting up the device specific data */ 857 /* finish setting up the device specific data */
856 cdc_ncm_setup(dev); 858 cdc_ncm_setup(dev);
857 859
860 /* Device-specific flags */
861 ctx->drvflags = drvflags;
862
863 /* Allocate the delayed NDP if needed. */
864 if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
865 ctx->delayed_ndp16 = kzalloc(ctx->max_ndp_size, GFP_KERNEL);
866 if (!ctx->delayed_ndp16)
867 goto error2;
868 dev_info(&intf->dev, "NDP will be placed at end of frame for this device.");
869 }
870
858 /* override ethtool_ops */ 871 /* override ethtool_ops */
859 dev->net->ethtool_ops = &cdc_ncm_ethtool_ops; 872 dev->net->ethtool_ops = &cdc_ncm_ethtool_ops;
860 873
@@ -954,8 +967,11 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
954 if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM) 967 if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM)
955 return -ENODEV; 968 return -ENODEV;
956 969
957 /* The NCM data altsetting is fixed */ 970 /* The NCM data altsetting is fixed, so we hard-coded it.
958 ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM); 971 * Additionally, generic NCM devices are assumed to accept arbitrarily
972 * placed NDP.
973 */
974 ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM, 0);
959 975
960 /* 976 /*
961 * We should get an event when network connection is "connected" or 977 * We should get an event when network connection is "connected" or
@@ -986,6 +1002,14 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_
986 struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data; 1002 struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data;
987 size_t ndpoffset = le16_to_cpu(nth16->wNdpIndex); 1003 size_t ndpoffset = le16_to_cpu(nth16->wNdpIndex);
988 1004
1005 /* If NDP should be moved to the end of the NCM package, we can't follow the
1006 * NTH16 header as we would normally do. NDP isn't written to the SKB yet, and
1007 * the wNdpIndex field in the header is actually not consistent with reality. It will be later.
1008 */
1009 if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)
1010 if (ctx->delayed_ndp16->dwSignature == sign)
1011 return ctx->delayed_ndp16;
1012
989 /* follow the chain of NDPs, looking for a match */ 1013 /* follow the chain of NDPs, looking for a match */
990 while (ndpoffset) { 1014 while (ndpoffset) {
991 ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset); 1015 ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset);
@@ -995,7 +1019,8 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_
995 } 1019 }
996 1020
997 /* align new NDP */ 1021 /* align new NDP */
998 cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max); 1022 if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
1023 cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max);
999 1024
1000 /* verify that there is room for the NDP and the datagram (reserve) */ 1025 /* verify that there is room for the NDP and the datagram (reserve) */
1001 if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size) 1026 if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size)
@@ -1008,7 +1033,11 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_
1008 nth16->wNdpIndex = cpu_to_le16(skb->len); 1033 nth16->wNdpIndex = cpu_to_le16(skb->len);
1009 1034
1010 /* push a new empty NDP */ 1035 /* push a new empty NDP */
1011 ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, ctx->max_ndp_size), 0, ctx->max_ndp_size); 1036 if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END))
1037 ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, ctx->max_ndp_size), 0, ctx->max_ndp_size);
1038 else
1039 ndp16 = ctx->delayed_ndp16;
1040
1012 ndp16->dwSignature = sign; 1041 ndp16->dwSignature = sign;
1013 ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16)); 1042 ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16));
1014 return ndp16; 1043 return ndp16;
@@ -1023,6 +1052,15 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
1023 struct sk_buff *skb_out; 1052 struct sk_buff *skb_out;
1024 u16 n = 0, index, ndplen; 1053 u16 n = 0, index, ndplen;
1025 u8 ready2send = 0; 1054 u8 ready2send = 0;
1055 u32 delayed_ndp_size;
1056
1057 /* When our NDP gets written in cdc_ncm_ndp(), then skb_out->len gets updated
1058 * accordingly. Otherwise, we should check here.
1059 */
1060 if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)
1061 delayed_ndp_size = ctx->max_ndp_size;
1062 else
1063 delayed_ndp_size = 0;
1026 1064
1027 /* if there is a remaining skb, it gets priority */ 1065 /* if there is a remaining skb, it gets priority */
1028 if (skb != NULL) { 1066 if (skb != NULL) {
@@ -1077,7 +1115,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
1077 cdc_ncm_align_tail(skb_out, ctx->tx_modulus, ctx->tx_remainder, ctx->tx_max); 1115 cdc_ncm_align_tail(skb_out, ctx->tx_modulus, ctx->tx_remainder, ctx->tx_max);
1078 1116
1079 /* check if we had enough room left for both NDP and frame */ 1117 /* check if we had enough room left for both NDP and frame */
1080 if (!ndp16 || skb_out->len + skb->len > ctx->tx_max) { 1118 if (!ndp16 || skb_out->len + skb->len + delayed_ndp_size > ctx->tx_max) {
1081 if (n == 0) { 1119 if (n == 0) {
1082 /* won't fit, MTU problem? */ 1120 /* won't fit, MTU problem? */
1083 dev_kfree_skb_any(skb); 1121 dev_kfree_skb_any(skb);
@@ -1150,6 +1188,17 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign)
1150 /* variables will be reset at next call */ 1188 /* variables will be reset at next call */
1151 } 1189 }
1152 1190
1191 /* If requested, put NDP at end of frame. */
1192 if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) {
1193 nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
1194 cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_max);
1195 nth16->wNdpIndex = cpu_to_le16(skb_out->len);
1196 memcpy(skb_put(skb_out, ctx->max_ndp_size), ctx->delayed_ndp16, ctx->max_ndp_size);
1197
1198 /* Zero out delayed NDP - signature checking will naturally fail. */
1199 ndp16 = memset(ctx->delayed_ndp16, 0, ctx->max_ndp_size);
1200 }
1201
1153 /* If collected data size is less or equal ctx->min_tx_pkt 1202 /* If collected data size is less or equal ctx->min_tx_pkt
1154 * bytes, we send buffers as it is. If we get more data, it 1203 * bytes, we send buffers as it is. If we get more data, it
1155 * would be more efficient for USB HS mobile device with DMA 1204 * would be more efficient for USB HS mobile device with DMA
diff --git a/drivers/net/usb/huawei_cdc_ncm.c b/drivers/net/usb/huawei_cdc_ncm.c
index 735f7dadb9a0..2680a65cd5e4 100644
--- a/drivers/net/usb/huawei_cdc_ncm.c
+++ b/drivers/net/usb/huawei_cdc_ncm.c
@@ -73,11 +73,14 @@ static int huawei_cdc_ncm_bind(struct usbnet *usbnet_dev,
73 struct usb_driver *subdriver = ERR_PTR(-ENODEV); 73 struct usb_driver *subdriver = ERR_PTR(-ENODEV);
74 int ret = -ENODEV; 74 int ret = -ENODEV;
75 struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data; 75 struct huawei_cdc_ncm_state *drvstate = (void *)&usbnet_dev->data;
76 int drvflags = 0;
76 77
77 /* altsetting should always be 1 for NCM devices - so we hard-coded 78 /* altsetting should always be 1 for NCM devices - so we hard-coded
78 * it here 79 * it here. Some huawei devices will need the NDP part of the NCM package to
80 * be at the end of the frame.
79 */ 81 */
80 ret = cdc_ncm_bind_common(usbnet_dev, intf, 1); 82 drvflags |= CDC_NCM_FLAG_NDP_TO_END;
83 ret = cdc_ncm_bind_common(usbnet_dev, intf, 1, drvflags);
81 if (ret) 84 if (ret)
82 goto err; 85 goto err;
83 86
diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h
index 7c9b484735c5..1f6526c76ee8 100644
--- a/include/linux/usb/cdc_ncm.h
+++ b/include/linux/usb/cdc_ncm.h
@@ -80,6 +80,9 @@
80#define CDC_NCM_TIMER_INTERVAL_MIN 5UL 80#define CDC_NCM_TIMER_INTERVAL_MIN 5UL
81#define CDC_NCM_TIMER_INTERVAL_MAX (U32_MAX / NSEC_PER_USEC) 81#define CDC_NCM_TIMER_INTERVAL_MAX (U32_MAX / NSEC_PER_USEC)
82 82
83/* Driver flags */
84#define CDC_NCM_FLAG_NDP_TO_END 0x02 /* NDP is placed at end of frame */
85
83#define cdc_ncm_comm_intf_is_mbim(x) ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \ 86#define cdc_ncm_comm_intf_is_mbim(x) ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \
84 (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE) 87 (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE)
85#define cdc_ncm_data_intf_is_mbim(x) ((x)->desc.bInterfaceProtocol == USB_CDC_MBIM_PROTO_NTB) 88#define cdc_ncm_data_intf_is_mbim(x) ((x)->desc.bInterfaceProtocol == USB_CDC_MBIM_PROTO_NTB)
@@ -103,9 +106,11 @@ struct cdc_ncm_ctx {
103 106
104 spinlock_t mtx; 107 spinlock_t mtx;
105 atomic_t stop; 108 atomic_t stop;
109 int drvflags;
106 110
107 u32 timer_interval; 111 u32 timer_interval;
108 u32 max_ndp_size; 112 u32 max_ndp_size;
113 struct usb_cdc_ncm_ndp16 *delayed_ndp16;
109 114
110 u32 tx_timer_pending; 115 u32 tx_timer_pending;
111 u32 tx_curr_frame_num; 116 u32 tx_curr_frame_num;
@@ -133,7 +138,7 @@ struct cdc_ncm_ctx {
133}; 138};
134 139
135u8 cdc_ncm_select_altsetting(struct usb_interface *intf); 140u8 cdc_ncm_select_altsetting(struct usb_interface *intf);
136int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting); 141int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags);
137void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf); 142void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
138struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign); 143struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign);
139int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in); 144int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in);