static bool ieee80211_amsdu_aggregate(struct ieee80211_sub_if_data *sdata,
				      struct sta_info *sta,
				      struct ieee80211_fast_tx *fast_tx,
				      struct sk_buff *skb)
{
	struct ieee80211_local *local = sdata->local;
	struct fq *fq = &local->fq;
	struct fq_tin *tin;
	struct fq_flow *flow;
	u8 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
	struct ieee80211_txq *txq = sta->sta.txq[tid];
	struct txq_info *txqi;
	struct sk_buff **frag_tail, *head;
	int subframe_len = skb->len - ETH_ALEN;
	u8 max_subframes = sta->sta.max_amsdu_subframes;
	int max_frags = local->hw.max_tx_fragments;
	int max_amsdu_len = sta->sta.max_amsdu_len;
	int orig_truesize;
	u32 flow_idx;
	__be16 len;
	void *data;
	bool ret = false;
	unsigned int orig_len;
	int n = 2, nfrags, pad = 0;
	u16 hdrlen;

	if (!ieee80211_hw_check(&local->hw, TX_AMSDU))
		return false;

	if (skb_is_gso(skb))
		return false;

	if (!txq)
		return false;

	txqi = to_txq_info(txq);
	if (test_bit(IEEE80211_TXQ_NO_AMSDU, &txqi->flags))
		return false;

	if (sta->sta.max_rc_amsdu_len)
		max_amsdu_len = min_t(int, max_amsdu_len,
				      sta->sta.max_rc_amsdu_len);

	if (sta->sta.max_tid_amsdu_len[tid])
		max_amsdu_len = min_t(int, max_amsdu_len,
				      sta->sta.max_tid_amsdu_len[tid]);

	flow_idx = fq_flow_idx(fq, skb);

	spin_lock_bh(&fq->lock);

	/* TODO: Ideally aggregation should be done on dequeue to remain
	 * responsive to environment changes.
	 */

	tin = &txqi->tin;
	flow = fq_flow_classify(fq, tin, flow_idx, skb);
	head = skb_peek_tail(&flow->queue);
	if (!head || skb_is_gso(head))
		goto out;

	orig_truesize = head->truesize;
	orig_len = head->len;

	if (skb->len + head->len > max_amsdu_len)
		goto out;

	nfrags = 1 + skb_shinfo(skb)->nr_frags;
	nfrags += 1 + skb_shinfo(head)->nr_frags;
	frag_tail = &skb_shinfo(head)->frag_list;
	while (*frag_tail) {
		nfrags += 1 + skb_shinfo(*frag_tail)->nr_frags;
		frag_tail = &(*frag_tail)->next;
		n++;
	}

	if (max_subframes && n > max_subframes)
		goto out;

	if (max_frags && nfrags > max_frags)
		goto out;

	if (!drv_can_aggregate_in_amsdu(local, head, skb))
		goto out;

	if (!ieee80211_amsdu_prepare_head(sdata, fast_tx, head))
		goto out;

	/*
	 * Pad out the previous subframe to a multiple of 4 by adding the
	 * padding to the next one, that's being added. Note that head->len
	 * is the length of the full A-MSDU, but that works since each time
	 * we add a new subframe we pad out the previous one to a multiple
	 * of 4 and thus it no longer matters in the next round.
	 */
	hdrlen = fast_tx->hdr_len - sizeof(rfc1042_header);
	if ((head->len - hdrlen) & 3)
		pad = 4 - ((head->len - hdrlen) & 3);

	if (!ieee80211_amsdu_realloc_pad(local, skb, sizeof(rfc1042_header) +
						     2 + pad))
		goto out_recalc;

	ret = true;
	data = skb_push(skb, ETH_ALEN + 2);
	memmove(data, data + ETH_ALEN + 2, 2 * ETH_ALEN);

	data += 2 * ETH_ALEN;
	len = cpu_to_be16(subframe_len);
	memcpy(data, &len, 2);
	memcpy(data + 2, rfc1042_header, sizeof(rfc1042_header));

	memset(skb_push(skb, pad), 0, pad);

	head->len += skb->len;
	head->data_len += skb->len;
	*frag_tail = skb;

out_recalc:
	fq->memory_usage += head->truesize - orig_truesize;
	if (head->len != orig_len) {
		flow->backlog += head->len - orig_len;
		tin->backlog_bytes += head->len - orig_len;
	}
out:
	spin_unlock_bh(&fq->lock);

	return ret;
}