-static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
- struct ieee80211_tx_info *info,
- struct ieee80211_sta *sta,
- struct sk_buff_head *mpdus_skb)
-{
- struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
- struct ieee80211_hdr *hdr = (void *)skb->data;
- unsigned int mss = skb_shinfo(skb)->gso_size;
- unsigned int num_subframes, tcp_payload_len, subf_len, max_amsdu_len;
- u16 snap_ip_tcp, pad;
- unsigned int dbg_max_amsdu_len;
- netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG;
- u8 tid;
-
- snap_ip_tcp = 8 + skb_transport_header(skb) - skb_network_header(skb) +
- tcp_hdrlen(skb);
-
- dbg_max_amsdu_len = READ_ONCE(mvm->max_amsdu_len);
-
- if (!mvmsta->max_amsdu_len ||
- !ieee80211_is_data_qos(hdr->frame_control) ||
- (!mvmsta->amsdu_enabled && !dbg_max_amsdu_len))
- return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb);
-
- /*
- * Do not build AMSDU for IPv6 with extension headers.
- * ask stack to segment and checkum the generated MPDUs for us.
- */
- if (skb->protocol == htons(ETH_P_IPV6) &&
- ((struct ipv6hdr *)skb_network_header(skb))->nexthdr !=
- IPPROTO_TCP) {
- netdev_flags &= ~NETIF_F_CSUM_MASK;
- return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb);
- }
-
- tid = ieee80211_get_tid(hdr);
- if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT))
- return -EINVAL;
-
- /*
- * No need to lock amsdu_in_ampdu_allowed since it can't be modified
- * during an BA session.
- */
- if (info->flags & IEEE80211_TX_CTL_AMPDU &&
- !mvmsta->tid_data[tid].amsdu_in_ampdu_allowed)
- return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb);
-
- if (iwl_mvm_vif_low_latency(iwl_mvm_vif_from_mac80211(mvmsta->vif)) ||
- !(mvmsta->amsdu_enabled & BIT(tid)))
- return iwl_mvm_tx_tso_segment(skb, 1, netdev_flags, mpdus_skb);
-
- max_amsdu_len = iwl_mvm_max_amsdu_size(mvm, sta, tid);
-
- if (unlikely(dbg_max_amsdu_len))
- max_amsdu_len = min_t(unsigned int, max_amsdu_len,
- dbg_max_amsdu_len);
-
- /*
- * Limit A-MSDU in A-MPDU to 4095 bytes when VHT is not
- * supported. This is a spec requirement (IEEE 802.11-2015
- * section 8.7.3 NOTE 3).
- */
- if (info->flags & IEEE80211_TX_CTL_AMPDU &&
- !sta->vht_cap.vht_supported)
- max_amsdu_len = min_t(unsigned int, max_amsdu_len, 4095);
-
- /* Sub frame header + SNAP + IP header + TCP header + MSS */
- subf_len = sizeof(struct ethhdr) + snap_ip_tcp + mss;
- pad = (4 - subf_len) & 0x3;
-
- /*
- * If we have N subframes in the A-MSDU, then the A-MSDU's size is
- * N * subf_len + (N - 1) * pad.
- */
- num_subframes = (max_amsdu_len + pad) / (subf_len + pad);
-
- if (sta->max_amsdu_subframes &&
- num_subframes > sta->max_amsdu_subframes)
- num_subframes = sta->max_amsdu_subframes;
-
- tcp_payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) -
- tcp_hdrlen(skb) + skb->data_len;
-
- /*
- * Make sure we have enough TBs for the A-MSDU:
- * 2 for each subframe
- * 1 more for each fragment
- * 1 more for the potential data in the header
- */
- if ((num_subframes * 2 + skb_shinfo(skb)->nr_frags + 1) >
- mvm->trans->max_skb_frags)
- num_subframes = 1;
-
- if (num_subframes > 1)
- *ieee80211_get_qos_ctl(hdr) |= IEEE80211_QOS_CTL_A_MSDU_PRESENT;
-
- /* This skb fits in one single A-MSDU */
- if (num_subframes * mss >= tcp_payload_len) {
- __skb_queue_tail(mpdus_skb, skb);
- return 0;
- }
-
- /*
- * Trick the segmentation function to make it
- * create SKBs that can fit into one A-MSDU.
- */
- return iwl_mvm_tx_tso_segment(skb, num_subframes, netdev_flags,
- mpdus_skb);
-}
-#else /* CONFIG_INET */
-static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb,
- struct ieee80211_tx_info *info,
- struct ieee80211_sta *sta,
- struct sk_buff_head *mpdus_skb)
-{
- /* Impossible to get TSO with CONFIG_INET */
- WARN_ON(1);
-
- return -1;
-}
-#endif
-