ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
				const u8 *peer, u8 action_code, u8 dialog_token,
				u16 status_code, u32 peer_capability,
				bool initiator, const u8 *extra_ies,
				size_t extra_ies_len, u8 oper_class,
				struct cfg80211_chan_def *chandef)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct sk_buff *skb = NULL;
	struct sta_info *sta;
	u32 flags = 0;
	int ret = 0;

	rcu_read_lock();
	sta = sta_info_get(sdata, peer);

	/* infer the initiator if we can, to support old userspace */
	switch (action_code) {
	case WLAN_TDLS_SETUP_REQUEST:
		if (sta) {
			set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
			sta->sta.tdls_initiator = false;
		}
		/* fall-through */
	case WLAN_TDLS_SETUP_CONFIRM:
	case WLAN_TDLS_DISCOVERY_REQUEST:
		initiator = true;
		break;
	case WLAN_TDLS_SETUP_RESPONSE:
		/*
		 * In some testing scenarios, we send a request and response.
		 * Make the last packet sent take effect for the initiator
		 * value.
		 */
		if (sta) {
			clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
			sta->sta.tdls_initiator = true;
		}
		/* fall-through */
	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
		initiator = false;
		break;
	case WLAN_TDLS_TEARDOWN:
	case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
	case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
		/* any value is ok */
		break;
	default:
		ret = -ENOTSUPP;
		break;
	}

	if (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))
		initiator = true;

	rcu_read_unlock();
	if (ret < 0)
		goto fail;

	skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer, action_code,
						    dialog_token, status_code,
						    initiator, extra_ies,
						    extra_ies_len, oper_class,
						    chandef);
	if (!skb) {
		ret = -EINVAL;
		goto fail;
	}

	if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
		ieee80211_tx_skb(sdata, skb);
		return 0;
	}

	/*
	 * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
	 * we should default to AC_VI.
	 */
	switch (action_code) {
	case WLAN_TDLS_SETUP_REQUEST:
	case WLAN_TDLS_SETUP_RESPONSE:
		skb->priority = 256 + 2;
		break;
	default:
		skb->priority = 256 + 5;
		break;
	}
	skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb));

	/*
	 * Set the WLAN_TDLS_TEARDOWN flag to indicate a teardown in progress.
	 * Later, if no ACK is returned from peer, we will re-send the teardown
	 * packet through the AP.
	 */
	if ((action_code == WLAN_TDLS_TEARDOWN) &&
	    ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) {
		bool try_resend; /* Should we keep skb for possible resend */

		/* If not sending directly to peer - no point in keeping skb */
		rcu_read_lock();
		sta = sta_info_get(sdata, peer);
		try_resend = sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
		rcu_read_unlock();

		spin_lock_bh(&sdata->u.mgd.teardown_lock);
		if (try_resend && !sdata->u.mgd.teardown_skb) {
			/* Mark it as requiring TX status callback  */
			flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
				 IEEE80211_TX_INTFL_MLME_CONN_TX;

			/*
			 * skb is copied since mac80211 will later set
			 * properties that might not be the same as the AP,
			 * such as encryption, QoS, addresses, etc.
			 *
			 * No problem if skb_copy() fails, so no need to check.
			 */
			sdata->u.mgd.teardown_skb = skb_copy(skb, GFP_ATOMIC);
			sdata->u.mgd.orig_teardown_skb = skb;
		}
		spin_unlock_bh(&sdata->u.mgd.teardown_lock);
	}

	/* disable bottom halves when entering the Tx path */
	local_bh_disable();
	__ieee80211_subif_start_xmit(skb, dev, flags, 0);
	local_bh_enable();

	return ret;

fail:
	dev_kfree_skb(skb);
	return ret;
}
ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
				const u8 *peer, u8 action_code, u8 dialog_token,
				u16 status_code, u32 peer_capability,
				bool initiator, const u8 *extra_ies,
				size_t extra_ies_len, u8 oper_class,
				struct cfg80211_chan_def *chandef)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct sk_buff *skb = NULL;
	struct sta_info *sta;
	u32 flags = 0;
	int ret = 0;

	rcu_read_lock();
	sta = sta_info_get(sdata, peer);

	 
	switch (action_code) {
	case WLAN_TDLS_SETUP_REQUEST:
		if (sta) {
			set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
			sta->sta.tdls_initiator = false;
		}
		 
	case WLAN_TDLS_SETUP_CONFIRM:
	case WLAN_TDLS_DISCOVERY_REQUEST:
		initiator = true;
		break;
	case WLAN_TDLS_SETUP_RESPONSE:
		 
		if (sta) {
			clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR);
			sta->sta.tdls_initiator = true;
		}
		 
	case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
		initiator = false;
		break;
	case WLAN_TDLS_TEARDOWN:
	case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
	case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
		 
		break;
	default:
		ret = -ENOTSUPP;
		break;
	}

	if (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))
		initiator = true;

	rcu_read_unlock();
	if (ret < 0)
		goto fail;

	skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer, action_code,
						    dialog_token, status_code,
						    initiator, extra_ies,
						    extra_ies_len, oper_class,
						    chandef);
	if (!skb) {
		ret = -EINVAL;
		goto fail;
	}

	if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
		ieee80211_tx_skb(sdata, skb);
		return 0;
	}

	 
	switch (action_code) {
	case WLAN_TDLS_SETUP_REQUEST:
	case WLAN_TDLS_SETUP_RESPONSE:
		skb->priority = 256 + 2;
		break;
	default:
		skb->priority = 256 + 5;
		break;
	}
	skb_set_queue_mapping(skb, ieee80211_select_queue(sdata, skb));

	 
	if ((action_code == WLAN_TDLS_TEARDOWN) &&
	    ieee80211_hw_check(&sdata->local->hw, REPORTS_TX_ACK_STATUS)) {
		bool try_resend;  

		 
		rcu_read_lock();
		sta = sta_info_get(sdata, peer);
		try_resend = sta && test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
		rcu_read_unlock();

		spin_lock_bh(&sdata->u.mgd.teardown_lock);
		if (try_resend && !sdata->u.mgd.teardown_skb) {
			 
			flags |= IEEE80211_TX_CTL_REQ_TX_STATUS |
				 IEEE80211_TX_INTFL_MLME_CONN_TX;

			 
			sdata->u.mgd.teardown_skb = skb_copy(skb, GFP_ATOMIC);
			sdata->u.mgd.orig_teardown_skb = skb;
		}
		spin_unlock_bh(&sdata->u.mgd.teardown_lock);
	}

	 
	local_bh_disable();
	__ieee80211_subif_start_xmit(skb, dev, flags, 0);
	local_bh_enable();

	return ret;

fail:
	dev_kfree_skb(skb);
	return ret;
}