|
@@ -4757,3 +4757,49 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
|
|
ieee80211_xmit(sdata, NULL, skb);
|
|
ieee80211_xmit(sdata, NULL, skb);
|
|
local_bh_enable();
|
|
local_bh_enable();
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+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)
|
|
|
|
+{
|
|
|
|
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
|
|
+ struct sk_buff *skb;
|
|
|
|
+ struct ethhdr *ehdr;
|
|
|
|
+ u32 flags;
|
|
|
|
+
|
|
|
|
+ /* 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 (unencrypted)
|
|
|
|
+ flags = IEEE80211_TX_INTFL_DONT_ENCRYPT;
|
|
|
|
+ else
|
|
|
|
+ flags = 0;
|
|
|
|
+
|
|
|
|
+ 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 = htons(ETH_P_802_3);
|
|
|
|
+ skb_reset_network_header(skb);
|
|
|
|
+ skb_reset_mac_header(skb);
|
|
|
|
+
|
|
|
|
+ __ieee80211_subif_start_xmit(skb, skb->dev, flags);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|