diff options
author | Jim Baxter <jim_baxter@mentor.com> | 2014-07-07 13:33:18 -0400 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2014-07-10 09:49:38 -0400 |
commit | 6d3865f9d41f15ddbcecaa6722871fc0db21d7ab (patch) | |
tree | 2f4ed7cce199a98fadc042403d6f0088fff3f0a1 /drivers/usb | |
parent | 370af734dfaf8336b496b386e194648e097e248a (diff) |
usb: gadget: NCM: Add transmit multi-frame.
This adds multi-frame support to the NCM NTB's for
the gadget driver. This allows multiple network
packets to be put inside a single USB NTB with a
maximum size of 16kB.
It has a time out of 300ms to ensure that smaller
number of packets still maintain a normal latency.
Also the .fp_index and .next_fp_index have been
changed to .ndp_index and .next_ndp_index to
match the latest CDC-NCM specification and
help with maintenance.
Results transmitting from gadget to host.
Before the change:
TCP_STREAM Throughput (10^6bits/sec): 22.72
UDP_STREAM Throughput (10^6bits/sec): 25.94
Latency:
netperf -H 192.168.1.101 -v2 -l 50 -t TCP_RR -- -r 16384,16384
Trans. RoundTrip Throughput
Rate Latency 10^6bits/s
per sec usec/Tran Outbound
100.83 9918.116 13.215
After the change:
TCP_STREAM Throughput (10^6bits/sec): 124.26
UDP_STREAM Throughput (10^6bits/sec): 227.48
Latency:
netperf -H 192.168.1.101 -v2 -l 50 -t TCP_RR -- -r 16384,16384
Trans. RoundTrip Throughput
Rate Latency 10^6bits/s
per sec usec/Tran Outbound
156.80 6377.730 20.552
Signed-off-by: Jim Baxter <jim_baxter@mentor.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/gadget/f_ncm.c | 335 | ||||
-rw-r--r-- | drivers/usb/gadget/u_ether.c | 19 | ||||
-rw-r--r-- | drivers/usb/gadget/u_ether.h | 2 |
3 files changed, 269 insertions, 87 deletions
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c index d0ebbac8845f..5452fb663762 100644 --- a/drivers/usb/gadget/f_ncm.c +++ b/drivers/usb/gadget/f_ncm.c | |||
@@ -68,6 +68,18 @@ struct f_ncm { | |||
68 | * callback and ethernet open/close | 68 | * callback and ethernet open/close |
69 | */ | 69 | */ |
70 | spinlock_t lock; | 70 | spinlock_t lock; |
71 | |||
72 | struct net_device *netdev; | ||
73 | |||
74 | /* For multi-frame NDP TX */ | ||
75 | struct sk_buff *skb_tx_data; | ||
76 | struct sk_buff *skb_tx_ndp; | ||
77 | u16 ndp_dgram_count; | ||
78 | bool timer_force_tx; | ||
79 | struct tasklet_struct tx_tasklet; | ||
80 | struct hrtimer task_timer; | ||
81 | |||
82 | bool timer_stopping; | ||
71 | }; | 83 | }; |
72 | 84 | ||
73 | static inline struct f_ncm *func_to_ncm(struct usb_function *f) | 85 | static inline struct f_ncm *func_to_ncm(struct usb_function *f) |
@@ -92,15 +104,20 @@ static inline unsigned ncm_bitrate(struct usb_gadget *g) | |||
92 | * If the host can group frames, allow it to do that, 16K is selected, | 104 | * If the host can group frames, allow it to do that, 16K is selected, |
93 | * because it's used by default by the current linux host driver | 105 | * because it's used by default by the current linux host driver |
94 | */ | 106 | */ |
95 | #define NTB_DEFAULT_IN_SIZE USB_CDC_NCM_NTB_MIN_IN_SIZE | 107 | #define NTB_DEFAULT_IN_SIZE 16384 |
96 | #define NTB_OUT_SIZE 16384 | 108 | #define NTB_OUT_SIZE 16384 |
97 | 109 | ||
98 | /* | 110 | /* Allocation for storing the NDP, 32 should suffice for a |
99 | * skbs of size less than that will not be aligned | 111 | * 16k packet. This allows a maximum of 32 * 507 Byte packets to |
100 | * to NCM's dwNtbInMaxSize to save bus bandwidth | 112 | * be transmitted in a single 16kB skb, though when sending full size |
113 | * packets this limit will be plenty. | ||
114 | * Smaller packets are not likely to be trying to maximize the | ||
115 | * throughput and will be mstly sending smaller infrequent frames. | ||
101 | */ | 116 | */ |
117 | #define TX_MAX_NUM_DPE 32 | ||
102 | 118 | ||
103 | #define MAX_TX_NONFIXED (512 * 3) | 119 | /* Delay for the transmit to wait before sending an unfilled NTB frame. */ |
120 | #define TX_TIMEOUT_NSECS 300000 | ||
104 | 121 | ||
105 | #define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \ | 122 | #define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \ |
106 | USB_CDC_NCM_NTB32_SUPPORTED) | 123 | USB_CDC_NCM_NTB32_SUPPORTED) |
@@ -355,14 +372,15 @@ struct ndp_parser_opts { | |||
355 | u32 ndp_sign; | 372 | u32 ndp_sign; |
356 | unsigned nth_size; | 373 | unsigned nth_size; |
357 | unsigned ndp_size; | 374 | unsigned ndp_size; |
375 | unsigned dpe_size; | ||
358 | unsigned ndplen_align; | 376 | unsigned ndplen_align; |
359 | /* sizes in u16 units */ | 377 | /* sizes in u16 units */ |
360 | unsigned dgram_item_len; /* index or length */ | 378 | unsigned dgram_item_len; /* index or length */ |
361 | unsigned block_length; | 379 | unsigned block_length; |
362 | unsigned fp_index; | 380 | unsigned ndp_index; |
363 | unsigned reserved1; | 381 | unsigned reserved1; |
364 | unsigned reserved2; | 382 | unsigned reserved2; |
365 | unsigned next_fp_index; | 383 | unsigned next_ndp_index; |
366 | }; | 384 | }; |
367 | 385 | ||
368 | #define INIT_NDP16_OPTS { \ | 386 | #define INIT_NDP16_OPTS { \ |
@@ -370,13 +388,14 @@ struct ndp_parser_opts { | |||
370 | .ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \ | 388 | .ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \ |
371 | .nth_size = sizeof(struct usb_cdc_ncm_nth16), \ | 389 | .nth_size = sizeof(struct usb_cdc_ncm_nth16), \ |
372 | .ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \ | 390 | .ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \ |
391 | .dpe_size = sizeof(struct usb_cdc_ncm_dpe16), \ | ||
373 | .ndplen_align = 4, \ | 392 | .ndplen_align = 4, \ |
374 | .dgram_item_len = 1, \ | 393 | .dgram_item_len = 1, \ |
375 | .block_length = 1, \ | 394 | .block_length = 1, \ |
376 | .fp_index = 1, \ | 395 | .ndp_index = 1, \ |
377 | .reserved1 = 0, \ | 396 | .reserved1 = 0, \ |
378 | .reserved2 = 0, \ | 397 | .reserved2 = 0, \ |
379 | .next_fp_index = 1, \ | 398 | .next_ndp_index = 1, \ |
380 | } | 399 | } |
381 | 400 | ||
382 | 401 | ||
@@ -385,13 +404,14 @@ struct ndp_parser_opts { | |||
385 | .ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \ | 404 | .ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \ |
386 | .nth_size = sizeof(struct usb_cdc_ncm_nth32), \ | 405 | .nth_size = sizeof(struct usb_cdc_ncm_nth32), \ |
387 | .ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \ | 406 | .ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \ |
407 | .dpe_size = sizeof(struct usb_cdc_ncm_dpe32), \ | ||
388 | .ndplen_align = 8, \ | 408 | .ndplen_align = 8, \ |
389 | .dgram_item_len = 2, \ | 409 | .dgram_item_len = 2, \ |
390 | .block_length = 2, \ | 410 | .block_length = 2, \ |
391 | .fp_index = 2, \ | 411 | .ndp_index = 2, \ |
392 | .reserved1 = 1, \ | 412 | .reserved1 = 1, \ |
393 | .reserved2 = 2, \ | 413 | .reserved2 = 2, \ |
394 | .next_fp_index = 2, \ | 414 | .next_ndp_index = 2, \ |
395 | } | 415 | } |
396 | 416 | ||
397 | static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS; | 417 | static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS; |
@@ -803,6 +823,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) | |||
803 | 823 | ||
804 | if (ncm->port.in_ep->driver_data) { | 824 | if (ncm->port.in_ep->driver_data) { |
805 | DBG(cdev, "reset ncm\n"); | 825 | DBG(cdev, "reset ncm\n"); |
826 | ncm->timer_stopping = true; | ||
827 | ncm->netdev = NULL; | ||
806 | gether_disconnect(&ncm->port); | 828 | gether_disconnect(&ncm->port); |
807 | ncm_reset_values(ncm); | 829 | ncm_reset_values(ncm); |
808 | } | 830 | } |
@@ -839,6 +861,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) | |||
839 | net = gether_connect(&ncm->port); | 861 | net = gether_connect(&ncm->port); |
840 | if (IS_ERR(net)) | 862 | if (IS_ERR(net)) |
841 | return PTR_ERR(net); | 863 | return PTR_ERR(net); |
864 | ncm->netdev = net; | ||
865 | ncm->timer_stopping = false; | ||
842 | } | 866 | } |
843 | 867 | ||
844 | spin_lock(&ncm->lock); | 868 | spin_lock(&ncm->lock); |
@@ -865,95 +889,232 @@ static int ncm_get_alt(struct usb_function *f, unsigned intf) | |||
865 | return ncm->port.in_ep->driver_data ? 1 : 0; | 889 | return ncm->port.in_ep->driver_data ? 1 : 0; |
866 | } | 890 | } |
867 | 891 | ||
892 | static struct sk_buff *package_for_tx(struct f_ncm *ncm) | ||
893 | { | ||
894 | __le16 *ntb_iter; | ||
895 | struct sk_buff *skb2 = NULL; | ||
896 | unsigned ndp_pad; | ||
897 | unsigned ndp_index; | ||
898 | unsigned new_len; | ||
899 | |||
900 | const struct ndp_parser_opts *opts = ncm->parser_opts; | ||
901 | const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); | ||
902 | const int dgram_idx_len = 2 * 2 * opts->dgram_item_len; | ||
903 | |||
904 | /* Stop the timer */ | ||
905 | hrtimer_try_to_cancel(&ncm->task_timer); | ||
906 | |||
907 | ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) - | ||
908 | ncm->skb_tx_data->len; | ||
909 | ndp_index = ncm->skb_tx_data->len + ndp_pad; | ||
910 | new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len; | ||
911 | |||
912 | /* Set the final BlockLength and wNdpIndex */ | ||
913 | ntb_iter = (void *) ncm->skb_tx_data->data; | ||
914 | /* Increment pointer to BlockLength */ | ||
915 | ntb_iter += 2 + 1 + 1; | ||
916 | put_ncm(&ntb_iter, opts->block_length, new_len); | ||
917 | put_ncm(&ntb_iter, opts->ndp_index, ndp_index); | ||
918 | |||
919 | /* Set the final NDP wLength */ | ||
920 | new_len = opts->ndp_size + | ||
921 | (ncm->ndp_dgram_count * dgram_idx_len); | ||
922 | ncm->ndp_dgram_count = 0; | ||
923 | /* Increment from start to wLength */ | ||
924 | ntb_iter = (void *) ncm->skb_tx_ndp->data; | ||
925 | ntb_iter += 2; | ||
926 | put_unaligned_le16(new_len, ntb_iter); | ||
927 | |||
928 | /* Merge the skbs */ | ||
929 | swap(skb2, ncm->skb_tx_data); | ||
930 | if (ncm->skb_tx_data) { | ||
931 | dev_kfree_skb_any(ncm->skb_tx_data); | ||
932 | ncm->skb_tx_data = NULL; | ||
933 | } | ||
934 | |||
935 | /* Insert NDP alignment. */ | ||
936 | ntb_iter = (void *) skb_put(skb2, ndp_pad); | ||
937 | memset(ntb_iter, 0, ndp_pad); | ||
938 | |||
939 | /* Copy NTB across. */ | ||
940 | ntb_iter = (void *) skb_put(skb2, ncm->skb_tx_ndp->len); | ||
941 | memcpy(ntb_iter, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len); | ||
942 | dev_kfree_skb_any(ncm->skb_tx_ndp); | ||
943 | ncm->skb_tx_ndp = NULL; | ||
944 | |||
945 | /* Insert zero'd datagram. */ | ||
946 | ntb_iter = (void *) skb_put(skb2, dgram_idx_len); | ||
947 | memset(ntb_iter, 0, dgram_idx_len); | ||
948 | |||
949 | return skb2; | ||
950 | } | ||
951 | |||
868 | static struct sk_buff *ncm_wrap_ntb(struct gether *port, | 952 | static struct sk_buff *ncm_wrap_ntb(struct gether *port, |
869 | struct sk_buff *skb) | 953 | struct sk_buff *skb) |
870 | { | 954 | { |
871 | struct f_ncm *ncm = func_to_ncm(&port->func); | 955 | struct f_ncm *ncm = func_to_ncm(&port->func); |
872 | struct sk_buff *skb2; | 956 | struct sk_buff *skb2 = NULL; |
873 | int ncb_len = 0; | 957 | int ncb_len = 0; |
874 | __le16 *tmp; | 958 | __le16 *ntb_data; |
875 | int div; | 959 | __le16 *ntb_ndp; |
876 | int rem; | 960 | int dgram_pad; |
877 | int pad; | 961 | |
878 | int ndp_align; | ||
879 | int ndp_pad; | ||
880 | unsigned max_size = ncm->port.fixed_in_len; | 962 | unsigned max_size = ncm->port.fixed_in_len; |
881 | const struct ndp_parser_opts *opts = ncm->parser_opts; | 963 | const struct ndp_parser_opts *opts = ncm->parser_opts; |
882 | unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0; | 964 | const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); |
883 | 965 | const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor); | |
884 | div = le16_to_cpu(ntb_parameters.wNdpInDivisor); | 966 | const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder); |
885 | rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder); | 967 | const int dgram_idx_len = 2 * 2 * opts->dgram_item_len; |
886 | ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment); | ||
887 | |||
888 | ncb_len += opts->nth_size; | ||
889 | ndp_pad = ALIGN(ncb_len, ndp_align) - ncb_len; | ||
890 | ncb_len += ndp_pad; | ||
891 | ncb_len += opts->ndp_size; | ||
892 | ncb_len += 2 * 2 * opts->dgram_item_len; /* Datagram entry */ | ||
893 | ncb_len += 2 * 2 * opts->dgram_item_len; /* Zero datagram entry */ | ||
894 | pad = ALIGN(ncb_len, div) + rem - ncb_len; | ||
895 | ncb_len += pad; | ||
896 | 968 | ||
897 | if (ncb_len + skb->len + crc_len > max_size) { | 969 | if (!skb && !ncm->skb_tx_data) |
898 | dev_kfree_skb_any(skb); | ||
899 | return NULL; | 970 | return NULL; |
900 | } | ||
901 | 971 | ||
902 | skb2 = skb_copy_expand(skb, ncb_len, | 972 | if (skb) { |
903 | max_size - skb->len - ncb_len - crc_len, | 973 | /* Add the CRC if required up front */ |
904 | GFP_ATOMIC); | 974 | if (ncm->is_crc) { |
905 | dev_kfree_skb_any(skb); | 975 | uint32_t crc; |
906 | if (!skb2) | 976 | __le16 *crc_pos; |
907 | return NULL; | 977 | |
978 | crc = ~crc32_le(~0, | ||
979 | skb->data, | ||
980 | skb->len); | ||
981 | crc_pos = (void *) skb_put(skb, sizeof(uint32_t)); | ||
982 | put_unaligned_le32(crc, crc_pos); | ||
983 | } | ||
908 | 984 | ||
909 | skb = skb2; | 985 | /* If the new skb is too big for the current NCM NTB then |
986 | * set the current stored skb to be sent now and clear it | ||
987 | * ready for new data. | ||
988 | * NOTE: Assume maximum align for speed of calculation. | ||
989 | */ | ||
990 | if (ncm->skb_tx_data | ||
991 | && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE | ||
992 | || (ncm->skb_tx_data->len + | ||
993 | div + rem + skb->len + | ||
994 | ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len)) | ||
995 | > max_size)) { | ||
996 | skb2 = package_for_tx(ncm); | ||
997 | if (!skb2) | ||
998 | goto err; | ||
999 | } | ||
910 | 1000 | ||
911 | tmp = (void *) skb_push(skb, ncb_len); | 1001 | if (!ncm->skb_tx_data) { |
912 | memset(tmp, 0, ncb_len); | 1002 | ncb_len = opts->nth_size; |
1003 | dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len; | ||
1004 | ncb_len += dgram_pad; | ||
913 | 1005 | ||
914 | put_unaligned_le32(opts->nth_sign, tmp); /* dwSignature */ | 1006 | /* Create a new skb for the NTH and datagrams. */ |
915 | tmp += 2; | 1007 | ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC); |
916 | /* wHeaderLength */ | 1008 | if (!ncm->skb_tx_data) |
917 | put_unaligned_le16(opts->nth_size, tmp++); | 1009 | goto err; |
918 | tmp++; /* skip wSequence */ | ||
919 | put_ncm(&tmp, opts->block_length, skb->len); /* (d)wBlockLength */ | ||
920 | /* (d)wFpIndex */ | ||
921 | /* the first pointer is right after the NTH + align */ | ||
922 | put_ncm(&tmp, opts->fp_index, opts->nth_size + ndp_pad); | ||
923 | 1010 | ||
924 | tmp = (void *)tmp + ndp_pad; | 1011 | ntb_data = (void *) skb_put(ncm->skb_tx_data, ncb_len); |
1012 | memset(ntb_data, 0, ncb_len); | ||
1013 | /* dwSignature */ | ||
1014 | put_unaligned_le32(opts->nth_sign, ntb_data); | ||
1015 | ntb_data += 2; | ||
1016 | /* wHeaderLength */ | ||
1017 | put_unaligned_le16(opts->nth_size, ntb_data++); | ||
1018 | |||
1019 | /* Allocate an skb for storing the NDP, | ||
1020 | * TX_MAX_NUM_DPE should easily suffice for a | ||
1021 | * 16k packet. | ||
1022 | */ | ||
1023 | ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size | ||
1024 | + opts->dpe_size | ||
1025 | * TX_MAX_NUM_DPE), | ||
1026 | GFP_ATOMIC); | ||
1027 | if (!ncm->skb_tx_ndp) | ||
1028 | goto err; | ||
1029 | ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, | ||
1030 | opts->ndp_size); | ||
1031 | memset(ntb_ndp, 0, ncb_len); | ||
1032 | /* dwSignature */ | ||
1033 | put_unaligned_le32(ncm->ndp_sign, ntb_ndp); | ||
1034 | ntb_ndp += 2; | ||
925 | 1035 | ||
926 | /* NDP */ | 1036 | /* There is always a zeroed entry */ |
927 | put_unaligned_le32(ncm->ndp_sign, tmp); /* dwSignature */ | 1037 | ncm->ndp_dgram_count = 1; |
928 | tmp += 2; | ||
929 | /* wLength */ | ||
930 | put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++); | ||
931 | 1038 | ||
932 | tmp += opts->reserved1; | 1039 | /* Note: we skip opts->next_ndp_index */ |
933 | tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */ | 1040 | } |
934 | tmp += opts->reserved2; | ||
935 | 1041 | ||
936 | if (ncm->is_crc) { | 1042 | /* Delay the timer. */ |
937 | uint32_t crc; | 1043 | hrtimer_start(&ncm->task_timer, |
1044 | ktime_set(0, TX_TIMEOUT_NSECS), | ||
1045 | HRTIMER_MODE_REL); | ||
1046 | |||
1047 | /* Add the datagram position entries */ | ||
1048 | ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, dgram_idx_len); | ||
1049 | memset(ntb_ndp, 0, dgram_idx_len); | ||
1050 | |||
1051 | ncb_len = ncm->skb_tx_data->len; | ||
1052 | dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len; | ||
1053 | ncb_len += dgram_pad; | ||
1054 | |||
1055 | /* (d)wDatagramIndex */ | ||
1056 | put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len); | ||
1057 | /* (d)wDatagramLength */ | ||
1058 | put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len); | ||
1059 | ncm->ndp_dgram_count++; | ||
1060 | |||
1061 | /* Add the new data to the skb */ | ||
1062 | ntb_data = (void *) skb_put(ncm->skb_tx_data, dgram_pad); | ||
1063 | memset(ntb_data, 0, dgram_pad); | ||
1064 | ntb_data = (void *) skb_put(ncm->skb_tx_data, skb->len); | ||
1065 | memcpy(ntb_data, skb->data, skb->len); | ||
1066 | dev_kfree_skb_any(skb); | ||
1067 | skb = NULL; | ||
938 | 1068 | ||
939 | crc = ~crc32_le(~0, | 1069 | } else if (ncm->skb_tx_data && ncm->timer_force_tx) { |
940 | skb->data + ncb_len, | 1070 | /* If the tx was requested because of a timeout then send */ |
941 | skb->len - ncb_len); | 1071 | skb2 = package_for_tx(ncm); |
942 | put_unaligned_le32(crc, skb->data + skb->len); | 1072 | if (!skb2) |
943 | skb_put(skb, crc_len); | 1073 | goto err; |
944 | } | 1074 | } |
945 | 1075 | ||
946 | /* (d)wDatagramIndex[0] */ | 1076 | return skb2; |
947 | put_ncm(&tmp, opts->dgram_item_len, ncb_len); | 1077 | |
948 | /* (d)wDatagramLength[0] */ | 1078 | err: |
949 | put_ncm(&tmp, opts->dgram_item_len, skb->len - ncb_len); | 1079 | ncm->netdev->stats.tx_dropped++; |
950 | /* (d)wDatagramIndex[1] and (d)wDatagramLength[1] already zeroed */ | 1080 | |
1081 | if (skb) | ||
1082 | dev_kfree_skb_any(skb); | ||
1083 | if (ncm->skb_tx_data) | ||
1084 | dev_kfree_skb_any(ncm->skb_tx_data); | ||
1085 | if (ncm->skb_tx_ndp) | ||
1086 | dev_kfree_skb_any(ncm->skb_tx_ndp); | ||
1087 | |||
1088 | return NULL; | ||
1089 | } | ||
1090 | |||
1091 | /* | ||
1092 | * This transmits the NTB if there are frames waiting. | ||
1093 | */ | ||
1094 | static void ncm_tx_tasklet(unsigned long data) | ||
1095 | { | ||
1096 | struct f_ncm *ncm = (void *)data; | ||
951 | 1097 | ||
952 | if (skb->len > MAX_TX_NONFIXED) | 1098 | if (ncm->timer_stopping) |
953 | memset(skb_put(skb, max_size - skb->len), | 1099 | return; |
954 | 0, max_size - skb->len); | 1100 | |
1101 | /* Only send if data is available. */ | ||
1102 | if (ncm->skb_tx_data) { | ||
1103 | ncm->timer_force_tx = true; | ||
1104 | ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev); | ||
1105 | ncm->timer_force_tx = false; | ||
1106 | } | ||
1107 | } | ||
955 | 1108 | ||
956 | return skb; | 1109 | /* |
1110 | * The transmit should only be run if no skb data has been sent | ||
1111 | * for a certain duration. | ||
1112 | */ | ||
1113 | static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data) | ||
1114 | { | ||
1115 | struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer); | ||
1116 | tasklet_schedule(&ncm->tx_tasklet); | ||
1117 | return HRTIMER_NORESTART; | ||
957 | } | 1118 | } |
958 | 1119 | ||
959 | static int ncm_unwrap_ntb(struct gether *port, | 1120 | static int ncm_unwrap_ntb(struct gether *port, |
@@ -996,7 +1157,7 @@ static int ncm_unwrap_ntb(struct gether *port, | |||
996 | goto err; | 1157 | goto err; |
997 | } | 1158 | } |
998 | 1159 | ||
999 | ndp_index = get_ncm(&tmp, opts->fp_index); | 1160 | ndp_index = get_ncm(&tmp, opts->ndp_index); |
1000 | 1161 | ||
1001 | /* Run through all the NDP's in the NTB */ | 1162 | /* Run through all the NDP's in the NTB */ |
1002 | do { | 1163 | do { |
@@ -1033,7 +1194,7 @@ static int ncm_unwrap_ntb(struct gether *port, | |||
1033 | } | 1194 | } |
1034 | tmp += opts->reserved1; | 1195 | tmp += opts->reserved1; |
1035 | /* Check for another NDP (d)wNextNdpIndex */ | 1196 | /* Check for another NDP (d)wNextNdpIndex */ |
1036 | ndp_index = get_ncm(&tmp, opts->next_fp_index); | 1197 | ndp_index = get_ncm(&tmp, opts->next_ndp_index); |
1037 | tmp += opts->reserved2; | 1198 | tmp += opts->reserved2; |
1038 | 1199 | ||
1039 | ndp_len -= opts->ndp_size; | 1200 | ndp_len -= opts->ndp_size; |
@@ -1107,8 +1268,11 @@ static void ncm_disable(struct usb_function *f) | |||
1107 | 1268 | ||
1108 | DBG(cdev, "ncm deactivated\n"); | 1269 | DBG(cdev, "ncm deactivated\n"); |
1109 | 1270 | ||
1110 | if (ncm->port.in_ep->driver_data) | 1271 | if (ncm->port.in_ep->driver_data) { |
1272 | ncm->timer_stopping = true; | ||
1273 | ncm->netdev = NULL; | ||
1111 | gether_disconnect(&ncm->port); | 1274 | gether_disconnect(&ncm->port); |
1275 | } | ||
1112 | 1276 | ||
1113 | if (ncm->notify->driver_data) { | 1277 | if (ncm->notify->driver_data) { |
1114 | usb_ep_disable(ncm->notify); | 1278 | usb_ep_disable(ncm->notify); |
@@ -1277,6 +1441,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) | |||
1277 | ncm->port.open = ncm_open; | 1441 | ncm->port.open = ncm_open; |
1278 | ncm->port.close = ncm_close; | 1442 | ncm->port.close = ncm_close; |
1279 | 1443 | ||
1444 | tasklet_init(&ncm->tx_tasklet, ncm_tx_tasklet, (unsigned long) ncm); | ||
1445 | hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||
1446 | ncm->task_timer.function = ncm_tx_timeout; | ||
1447 | |||
1280 | DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n", | 1448 | DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n", |
1281 | gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", | 1449 | gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full", |
1282 | ncm->port.in_ep->name, ncm->port.out_ep->name, | 1450 | ncm->port.in_ep->name, ncm->port.out_ep->name, |
@@ -1390,6 +1558,10 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f) | |||
1390 | 1558 | ||
1391 | DBG(c->cdev, "ncm unbind\n"); | 1559 | DBG(c->cdev, "ncm unbind\n"); |
1392 | 1560 | ||
1561 | hrtimer_cancel(&ncm->task_timer); | ||
1562 | tasklet_kill(&ncm->tx_tasklet); | ||
1563 | |||
1564 | ncm_string_defs[0].id = 0; | ||
1393 | usb_free_all_descriptors(f); | 1565 | usb_free_all_descriptors(f); |
1394 | 1566 | ||
1395 | kfree(ncm->notify_req->buf); | 1567 | kfree(ncm->notify_req->buf); |
@@ -1426,6 +1598,7 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi) | |||
1426 | ncm->port.ioport = netdev_priv(opts->net); | 1598 | ncm->port.ioport = netdev_priv(opts->net); |
1427 | mutex_unlock(&opts->lock); | 1599 | mutex_unlock(&opts->lock); |
1428 | ncm->port.is_fixed = true; | 1600 | ncm->port.is_fixed = true; |
1601 | ncm->port.supports_multi_frame = true; | ||
1429 | 1602 | ||
1430 | ncm->port.func.name = "cdc_network"; | 1603 | ncm->port.func.name = "cdc_network"; |
1431 | /* descriptors are per-instance copies */ | 1604 | /* descriptors are per-instance copies */ |
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index 3d78a8844e43..6e6f87656e7b 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c | |||
@@ -483,7 +483,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, | |||
483 | struct net_device *net) | 483 | struct net_device *net) |
484 | { | 484 | { |
485 | struct eth_dev *dev = netdev_priv(net); | 485 | struct eth_dev *dev = netdev_priv(net); |
486 | int length = skb->len; | 486 | int length = 0; |
487 | int retval; | 487 | int retval; |
488 | struct usb_request *req = NULL; | 488 | struct usb_request *req = NULL; |
489 | unsigned long flags; | 489 | unsigned long flags; |
@@ -500,13 +500,13 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, | |||
500 | } | 500 | } |
501 | spin_unlock_irqrestore(&dev->lock, flags); | 501 | spin_unlock_irqrestore(&dev->lock, flags); |
502 | 502 | ||
503 | if (!in) { | 503 | if (skb && !in) { |
504 | dev_kfree_skb_any(skb); | 504 | dev_kfree_skb_any(skb); |
505 | return NETDEV_TX_OK; | 505 | return NETDEV_TX_OK; |
506 | } | 506 | } |
507 | 507 | ||
508 | /* apply outgoing CDC or RNDIS filters */ | 508 | /* apply outgoing CDC or RNDIS filters */ |
509 | if (!is_promisc(cdc_filter)) { | 509 | if (skb && !is_promisc(cdc_filter)) { |
510 | u8 *dest = skb->data; | 510 | u8 *dest = skb->data; |
511 | 511 | ||
512 | if (is_multicast_ether_addr(dest)) { | 512 | if (is_multicast_ether_addr(dest)) { |
@@ -557,11 +557,17 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, | |||
557 | if (dev->port_usb) | 557 | if (dev->port_usb) |
558 | skb = dev->wrap(dev->port_usb, skb); | 558 | skb = dev->wrap(dev->port_usb, skb); |
559 | spin_unlock_irqrestore(&dev->lock, flags); | 559 | spin_unlock_irqrestore(&dev->lock, flags); |
560 | if (!skb) | 560 | if (!skb) { |
561 | /* Multi frame CDC protocols may store the frame for | ||
562 | * later which is not a dropped frame. | ||
563 | */ | ||
564 | if (dev->port_usb->supports_multi_frame) | ||
565 | goto multiframe; | ||
561 | goto drop; | 566 | goto drop; |
562 | 567 | } | |
563 | length = skb->len; | ||
564 | } | 568 | } |
569 | |||
570 | length = skb->len; | ||
565 | req->buf = skb->data; | 571 | req->buf = skb->data; |
566 | req->context = skb; | 572 | req->context = skb; |
567 | req->complete = tx_complete; | 573 | req->complete = tx_complete; |
@@ -604,6 +610,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, | |||
604 | dev_kfree_skb_any(skb); | 610 | dev_kfree_skb_any(skb); |
605 | drop: | 611 | drop: |
606 | dev->net->stats.tx_dropped++; | 612 | dev->net->stats.tx_dropped++; |
613 | multiframe: | ||
607 | spin_lock_irqsave(&dev->req_lock, flags); | 614 | spin_lock_irqsave(&dev->req_lock, flags); |
608 | if (list_empty(&dev->tx_reqs)) | 615 | if (list_empty(&dev->tx_reqs)) |
609 | netif_start_queue(net); | 616 | netif_start_queue(net); |
diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h index 0f0290acea7e..334b38947916 100644 --- a/drivers/usb/gadget/u_ether.h +++ b/drivers/usb/gadget/u_ether.h | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <linux/if_ether.h> | 18 | #include <linux/if_ether.h> |
19 | #include <linux/usb/composite.h> | 19 | #include <linux/usb/composite.h> |
20 | #include <linux/usb/cdc.h> | 20 | #include <linux/usb/cdc.h> |
21 | #include <linux/netdevice.h> | ||
21 | 22 | ||
22 | #include "gadget_chips.h" | 23 | #include "gadget_chips.h" |
23 | 24 | ||
@@ -74,6 +75,7 @@ struct gether { | |||
74 | bool is_fixed; | 75 | bool is_fixed; |
75 | u32 fixed_out_len; | 76 | u32 fixed_out_len; |
76 | u32 fixed_in_len; | 77 | u32 fixed_in_len; |
78 | bool supports_multi_frame; | ||
77 | struct sk_buff *(*wrap)(struct gether *port, | 79 | struct sk_buff *(*wrap)(struct gether *port, |
78 | struct sk_buff *skb); | 80 | struct sk_buff *skb); |
79 | int (*unwrap)(struct gether *port, | 81 | int (*unwrap)(struct gether *port, |