diff options
-rw-r--r-- | drivers/net/usb/cdc_mbim.c | 2 | ||||
-rw-r--r-- | drivers/net/usb/cdc_ncm.c | 61 | ||||
-rw-r--r-- | drivers/net/usb/huawei_cdc_ncm.c | 7 | ||||
-rw-r--r-- | include/linux/usb/cdc_ncm.h | 7 |
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 | ||
690 | int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting) | 692 | int 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 | ||
135 | u8 cdc_ncm_select_altsetting(struct usb_interface *intf); | 140 | u8 cdc_ncm_select_altsetting(struct usb_interface *intf); |
136 | int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting); | 141 | int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting, int drvflags); |
137 | void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf); | 142 | void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf); |
138 | struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign); | 143 | struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign); |
139 | int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in); | 144 | int cdc_ncm_rx_verify_nth16(struct cdc_ncm_ctx *ctx, struct sk_buff *skb_in); |