int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
			      const u8 *buf, size_t len,
			      const u8 *dest, __be16 proto, bool unencrypted,
			      u64 *cookie)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_local *local = sdata->local;
	struct sta_info *sta;
	struct sk_buff *skb;
	struct ethhdr *ehdr;
	u32 ctrl_flags = 0;
	u32 flags = 0;

	/* Only accept CONTROL_PORT_PROTOCOL configured in CONNECT/ASSOCIATE
	 * or Pre-Authentication
	 */
	if (proto != sdata->control_port_protocol &&
	    proto != cpu_to_be16(ETH_P_PREAUTH))
		return -EINVAL;

	if (proto == sdata->control_port_protocol)
		ctrl_flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO |
			      IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP;

	if (unencrypted)
		flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;

	if (cookie)
		ctrl_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;

	flags |= IEEE80211_TX_INTFL_NL80211_FRAME_TX;

	skb = dev_alloc_skb(local->hw.extra_tx_headroom +
			    sizeof(struct ethhdr) + len);
	if (!skb)
		return -ENOMEM;

	skb_reserve(skb, local->hw.extra_tx_headroom + sizeof(struct ethhdr));

	skb_put_data(skb, buf, len);

	ehdr = skb_push(skb, sizeof(struct ethhdr));
	memcpy(ehdr->h_dest, dest, ETH_ALEN);
	memcpy(ehdr->h_source, sdata->vif.addr, ETH_ALEN);
	ehdr->h_proto = proto;

	skb->dev = dev;
	skb->protocol = proto;
	skb_reset_network_header(skb);
	skb_reset_mac_header(skb);

	/* update QoS header to prioritize control port frames if possible,
	 * priorization also happens for control port frames send over
	 * AF_PACKET
	 */
	rcu_read_lock();

	if (ieee80211_lookup_ra_sta(sdata, skb, &sta) == 0 && !IS_ERR(sta)) {
		u16 queue = __ieee80211_select_queue(sdata, sta, skb);

		skb_set_queue_mapping(skb, queue);
		skb_get_hash(skb);
	}

	rcu_read_unlock();

	/* mutex lock is only needed for incrementing the cookie counter */
	mutex_lock(&local->mtx);

	local_bh_disable();
	__ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags, cookie);
	local_bh_enable();

	mutex_unlock(&local->mtx);

	return 0;
}
int ieee80211_tx_control_port(struct wiphy *wiphy, struct net_device *dev,
			      const u8 *buf, size_t len,
			      const u8 *dest, __be16 proto, bool unencrypted,
			      u64 *cookie)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_local *local = sdata->local;
	struct sta_info *sta;
	struct sk_buff *skb;
	struct ethhdr *ehdr;
	u32 ctrl_flags = 0;
	u32 flags = 0;

	 
	if (proto != sdata->control_port_protocol &&
	    proto != cpu_to_be16(ETH_P_PREAUTH))
		return -EINVAL;

	if (proto == sdata->control_port_protocol)
		ctrl_flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO |
			      IEEE80211_TX_CTRL_SKIP_MPATH_LOOKUP;

	if (unencrypted)
		flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT;

	if (cookie)
		ctrl_flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;

	flags |= IEEE80211_TX_INTFL_NL80211_FRAME_TX;

	skb = dev_alloc_skb(local->hw.extra_tx_headroom +
			    sizeof(struct ethhdr) + len);
	if (!skb)
		return -ENOMEM;

	skb_reserve(skb, local->hw.extra_tx_headroom + sizeof(struct ethhdr));

	skb_put_data(skb, buf, len);

	ehdr = skb_push(skb, sizeof(struct ethhdr));
	memcpy(ehdr->h_dest, dest, ETH_ALEN);
	memcpy(ehdr->h_source, sdata->vif.addr, ETH_ALEN);
	ehdr->h_proto = proto;

	skb->dev = dev;
	skb->protocol = proto;
	skb_reset_network_header(skb);
	skb_reset_mac_header(skb);

	 
	rcu_read_lock();

	if (ieee80211_lookup_ra_sta(sdata, skb, &sta) == 0 && !IS_ERR(sta)) {
		u16 queue = __ieee80211_select_queue(sdata, sta, skb);

		skb_set_queue_mapping(skb, queue);
		skb_get_hash(skb);
	}

	rcu_read_unlock();

	 
	mutex_lock(&local->mtx);

	local_bh_disable();
	__ieee80211_subif_start_xmit(skb, skb->dev, flags, ctrl_flags, cookie);
	local_bh_enable();

	mutex_unlock(&local->mtx);

	return 0;
}