diff options
author | Jim Baxter <jim_baxter@mentor.com> | 2014-07-07 13:33:17 -0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2014-07-10 09:49:35 -0400 |
commit | 370af734dfaf8336b496b386e194648e097e248a (patch) | |
tree | ac8ec25e4fae41ddc9c06ce33bd4afde35c74ed0 | |
parent | d82aa8aeb0eaa06bad80860a95ca5fdff84e8775 (diff) |
usb: gadget: NCM: RX function support multiple NDPs
The NDP was ignoring the wNextNdpIndex in the NDP which
means that NTBs containing multiple NDPs would have missed
frames.
Signed-off-by: Jim Baxter <jim_baxter@mentor.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
-rw-r--r-- | drivers/usb/gadget/f_ncm.c | 146 |
1 files changed, 78 insertions, 68 deletions
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index a9499fd30792..d0ebbac8845f 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c | |||
@@ -963,6 +963,7 @@ static int ncm_unwrap_ntb(struct gether *port, | |||
963 | struct f_ncm *ncm = func_to_ncm(&port->func); | 963 | struct f_ncm *ncm = func_to_ncm(&port->func); |
964 | __le16 *tmp = (void *) skb->data; | 964 | __le16 *tmp = (void *) skb->data; |
965 | unsigned index, index2; | 965 | unsigned index, index2; |
966 | int ndp_index; | ||
966 | unsigned dg_len, dg_len2; | 967 | unsigned dg_len, dg_len2; |
967 | unsigned ndp_len; | 968 | unsigned ndp_len; |
968 | struct sk_buff *skb2; | 969 | struct sk_buff *skb2; |
@@ -995,91 +996,100 @@ static int ncm_unwrap_ntb(struct gether *port, | |||
995 | goto err; | 996 | goto err; |
996 | } | 997 | } |
997 | 998 | ||
998 | index = get_ncm(&tmp, opts->fp_index); | 999 | ndp_index = get_ncm(&tmp, opts->fp_index); |
999 | /* NCM 3.2 */ | ||
1000 | if (((index % 4) != 0) && (index < opts->nth_size)) { | ||
1001 | INFO(port->func.config->cdev, "Bad index: %x\n", | ||
1002 | index); | ||
1003 | goto err; | ||
1004 | } | ||
1005 | |||
1006 | /* walk through NDP */ | ||
1007 | tmp = ((void *)skb->data) + index; | ||
1008 | if (get_unaligned_le32(tmp) != ncm->ndp_sign) { | ||
1009 | INFO(port->func.config->cdev, "Wrong NDP SIGN\n"); | ||
1010 | goto err; | ||
1011 | } | ||
1012 | tmp += 2; | ||
1013 | |||
1014 | ndp_len = get_unaligned_le16(tmp++); | ||
1015 | /* | ||
1016 | * NCM 3.3.1 | ||
1017 | * entry is 2 items | ||
1018 | * item size is 16/32 bits, opts->dgram_item_len * 2 bytes | ||
1019 | * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry | ||
1020 | */ | ||
1021 | if ((ndp_len < opts->ndp_size + 2 * 2 * (opts->dgram_item_len * 2)) | ||
1022 | || (ndp_len % opts->ndplen_align != 0)) { | ||
1023 | INFO(port->func.config->cdev, "Bad NDP length: %x\n", ndp_len); | ||
1024 | goto err; | ||
1025 | } | ||
1026 | tmp += opts->reserved1; | ||
1027 | tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */ | ||
1028 | tmp += opts->reserved2; | ||
1029 | |||
1030 | ndp_len -= opts->ndp_size; | ||
1031 | index2 = get_ncm(&tmp, opts->dgram_item_len); | ||
1032 | dg_len2 = get_ncm(&tmp, opts->dgram_item_len); | ||
1033 | dgram_counter = 0; | ||
1034 | 1000 | ||
1001 | /* Run through all the NDP's in the NTB */ | ||
1035 | do { | 1002 | do { |
1036 | index = index2; | 1003 | /* NCM 3.2 */ |
1037 | dg_len = dg_len2; | 1004 | if (((ndp_index % 4) != 0) && |
1038 | if (dg_len < 14 + crc_len) { /* ethernet header + crc */ | 1005 | (ndp_index < opts->nth_size)) { |
1039 | INFO(port->func.config->cdev, "Bad dgram length: %x\n", | 1006 | INFO(port->func.config->cdev, "Bad index: %#X\n", |
1040 | dg_len); | 1007 | ndp_index); |
1041 | goto err; | 1008 | goto err; |
1042 | } | 1009 | } |
1043 | if (ncm->is_crc) { | 1010 | |
1044 | uint32_t crc, crc2; | 1011 | /* walk through NDP */ |
1045 | 1012 | tmp = (void *)(skb->data + ndp_index); | |
1046 | crc = get_unaligned_le32(skb->data + | 1013 | if (get_unaligned_le32(tmp) != ncm->ndp_sign) { |
1047 | index + dg_len - crc_len); | 1014 | INFO(port->func.config->cdev, "Wrong NDP SIGN\n"); |
1048 | crc2 = ~crc32_le(~0, | 1015 | goto err; |
1049 | skb->data + index, | ||
1050 | dg_len - crc_len); | ||
1051 | if (crc != crc2) { | ||
1052 | INFO(port->func.config->cdev, "Bad CRC\n"); | ||
1053 | goto err; | ||
1054 | } | ||
1055 | } | 1016 | } |
1017 | tmp += 2; | ||
1056 | 1018 | ||
1019 | ndp_len = get_unaligned_le16(tmp++); | ||
1020 | /* | ||
1021 | * NCM 3.3.1 | ||
1022 | * entry is 2 items | ||
1023 | * item size is 16/32 bits, opts->dgram_item_len * 2 bytes | ||
1024 | * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry | ||
1025 | * Each entry is a dgram index and a dgram length. | ||
1026 | */ | ||
1027 | if ((ndp_len < opts->ndp_size | ||
1028 | + 2 * 2 * (opts->dgram_item_len * 2)) | ||
1029 | || (ndp_len % opts->ndplen_align != 0)) { | ||
1030 | INFO(port->func.config->cdev, "Bad NDP length: %#X\n", | ||
1031 | ndp_len); | ||
1032 | goto err; | ||
1033 | } | ||
1034 | tmp += opts->reserved1; | ||
1035 | /* Check for another NDP (d)wNextNdpIndex */ | ||
1036 | ndp_index = get_ncm(&tmp, opts->next_fp_index); | ||
1037 | tmp += opts->reserved2; | ||
1038 | |||
1039 | ndp_len -= opts->ndp_size; | ||
1057 | index2 = get_ncm(&tmp, opts->dgram_item_len); | 1040 | index2 = get_ncm(&tmp, opts->dgram_item_len); |
1058 | dg_len2 = get_ncm(&tmp, opts->dgram_item_len); | 1041 | dg_len2 = get_ncm(&tmp, opts->dgram_item_len); |
1042 | dgram_counter = 0; | ||
1043 | |||
1044 | do { | ||
1045 | index = index2; | ||
1046 | dg_len = dg_len2; | ||
1047 | if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */ | ||
1048 | INFO(port->func.config->cdev, | ||
1049 | "Bad dgram length: %#X\n", dg_len); | ||
1050 | goto err; | ||
1051 | } | ||
1052 | if (ncm->is_crc) { | ||
1053 | uint32_t crc, crc2; | ||
1054 | |||
1055 | crc = get_unaligned_le32(skb->data + | ||
1056 | index + dg_len - | ||
1057 | crc_len); | ||
1058 | crc2 = ~crc32_le(~0, | ||
1059 | skb->data + index, | ||
1060 | dg_len - crc_len); | ||
1061 | if (crc != crc2) { | ||
1062 | INFO(port->func.config->cdev, | ||
1063 | "Bad CRC\n"); | ||
1064 | goto err; | ||
1065 | } | ||
1066 | } | ||
1067 | |||
1068 | index2 = get_ncm(&tmp, opts->dgram_item_len); | ||
1069 | dg_len2 = get_ncm(&tmp, opts->dgram_item_len); | ||
1059 | 1070 | ||
1060 | if (index2 == 0 || dg_len2 == 0) { | ||
1061 | skb2 = skb; | ||
1062 | } else { | ||
1063 | skb2 = skb_clone(skb, GFP_ATOMIC); | 1071 | skb2 = skb_clone(skb, GFP_ATOMIC); |
1064 | if (skb2 == NULL) | 1072 | if (skb2 == NULL) |
1065 | goto err; | 1073 | goto err; |
1066 | } | ||
1067 | 1074 | ||
1068 | if (!skb_pull(skb2, index)) { | 1075 | if (!skb_pull(skb2, index)) { |
1069 | ret = -EOVERFLOW; | 1076 | ret = -EOVERFLOW; |
1070 | goto err; | 1077 | goto err; |
1071 | } | 1078 | } |
1072 | 1079 | ||
1073 | skb_trim(skb2, dg_len - crc_len); | 1080 | skb_trim(skb2, dg_len - crc_len); |
1074 | skb_queue_tail(list, skb2); | 1081 | skb_queue_tail(list, skb2); |
1075 | 1082 | ||
1076 | ndp_len -= 2 * (opts->dgram_item_len * 2); | 1083 | ndp_len -= 2 * (opts->dgram_item_len * 2); |
1077 | 1084 | ||
1078 | dgram_counter++; | 1085 | dgram_counter++; |
1079 | 1086 | ||
1080 | if (index2 == 0 || dg_len2 == 0) | 1087 | if (index2 == 0 || dg_len2 == 0) |
1081 | break; | 1088 | break; |
1082 | } while (ndp_len > 2 * (opts->dgram_item_len * 2)); /* zero entry */ | 1089 | } while (ndp_len > 2 * (opts->dgram_item_len * 2)); |
1090 | } while (ndp_index); | ||
1091 | |||
1092 | dev_kfree_skb_any(skb); | ||
1083 | 1093 | ||
1084 | VDBG(port->func.config->cdev, | 1094 | VDBG(port->func.config->cdev, |
1085 | "Parsed NTB with %d frames\n", dgram_counter); | 1095 | "Parsed NTB with %d frames\n", dgram_counter); |