mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-28 01:28:59 +00:00
1111 lines
32 KiB
Diff
1111 lines
32 KiB
Diff
|
From: Felix Fietkau <nbd@nbd.name>
|
||
|
Date: Tue, 8 Sep 2020 12:16:26 +0200
|
||
|
Subject: [PATCH] mac80211: reorganize code to remove a forward
|
||
|
declaration
|
||
|
|
||
|
Remove the newly added ieee80211_set_vif_encap_ops declaration.
|
||
|
No further code changes
|
||
|
|
||
|
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||
|
---
|
||
|
|
||
|
--- a/net/mac80211/iface.c
|
||
|
+++ b/net/mac80211/iface.c
|
||
|
@@ -43,7 +43,6 @@
|
||
|
*/
|
||
|
|
||
|
static void ieee80211_iface_work(struct work_struct *work);
|
||
|
-static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata);
|
||
|
|
||
|
bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata)
|
||
|
{
|
||
|
@@ -349,6 +348,511 @@ static int ieee80211_check_queues(struct
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+static int ieee80211_open(struct net_device *dev)
|
||
|
+{
|
||
|
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||
|
+ int err;
|
||
|
+
|
||
|
+ /* fail early if user set an invalid address */
|
||
|
+ if (!is_valid_ether_addr(dev->dev_addr))
|
||
|
+ return -EADDRNOTAVAIL;
|
||
|
+
|
||
|
+ err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type);
|
||
|
+ if (err)
|
||
|
+ return err;
|
||
|
+
|
||
|
+ return ieee80211_do_open(&sdata->wdev, true);
|
||
|
+}
|
||
|
+
|
||
|
+static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
||
|
+ bool going_down)
|
||
|
+{
|
||
|
+ struct ieee80211_local *local = sdata->local;
|
||
|
+ unsigned long flags;
|
||
|
+ struct sk_buff *skb, *tmp;
|
||
|
+ u32 hw_reconf_flags = 0;
|
||
|
+ int i, flushed;
|
||
|
+ struct ps_data *ps;
|
||
|
+ struct cfg80211_chan_def chandef;
|
||
|
+ bool cancel_scan;
|
||
|
+ struct cfg80211_nan_func *func;
|
||
|
+
|
||
|
+ clear_bit(SDATA_STATE_RUNNING, &sdata->state);
|
||
|
+
|
||
|
+ cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
|
||
|
+ if (cancel_scan)
|
||
|
+ ieee80211_scan_cancel(local);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Stop TX on this interface first.
|
||
|
+ */
|
||
|
+ if (sdata->dev)
|
||
|
+ netif_tx_stop_all_queues(sdata->dev);
|
||
|
+
|
||
|
+ ieee80211_roc_purge(local, sdata);
|
||
|
+
|
||
|
+ switch (sdata->vif.type) {
|
||
|
+ case NL80211_IFTYPE_STATION:
|
||
|
+ ieee80211_mgd_stop(sdata);
|
||
|
+ break;
|
||
|
+ case NL80211_IFTYPE_ADHOC:
|
||
|
+ ieee80211_ibss_stop(sdata);
|
||
|
+ break;
|
||
|
+ case NL80211_IFTYPE_MONITOR:
|
||
|
+ if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES)
|
||
|
+ break;
|
||
|
+ list_del_rcu(&sdata->u.mntr.list);
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Remove all stations associated with this interface.
|
||
|
+ *
|
||
|
+ * This must be done before calling ops->remove_interface()
|
||
|
+ * because otherwise we can later invoke ops->sta_notify()
|
||
|
+ * whenever the STAs are removed, and that invalidates driver
|
||
|
+ * assumptions about always getting a vif pointer that is valid
|
||
|
+ * (because if we remove a STA after ops->remove_interface()
|
||
|
+ * the driver will have removed the vif info already!)
|
||
|
+ *
|
||
|
+ * In WDS mode a station must exist here and be flushed, for
|
||
|
+ * AP_VLANs stations may exist since there's nothing else that
|
||
|
+ * would have removed them, but in other modes there shouldn't
|
||
|
+ * be any stations.
|
||
|
+ */
|
||
|
+ flushed = sta_info_flush(sdata);
|
||
|
+ WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
|
||
|
+ ((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) ||
|
||
|
+ (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)));
|
||
|
+
|
||
|
+ /* don't count this interface for allmulti while it is down */
|
||
|
+ if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
|
||
|
+ atomic_dec(&local->iff_allmultis);
|
||
|
+
|
||
|
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
|
||
|
+ local->fif_pspoll--;
|
||
|
+ local->fif_probe_req--;
|
||
|
+ } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
|
||
|
+ local->fif_probe_req--;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (sdata->dev) {
|
||
|
+ netif_addr_lock_bh(sdata->dev);
|
||
|
+ spin_lock_bh(&local->filter_lock);
|
||
|
+ __hw_addr_unsync(&local->mc_list, &sdata->dev->mc,
|
||
|
+ sdata->dev->addr_len);
|
||
|
+ spin_unlock_bh(&local->filter_lock);
|
||
|
+ netif_addr_unlock_bh(sdata->dev);
|
||
|
+ }
|
||
|
+
|
||
|
+ del_timer_sync(&local->dynamic_ps_timer);
|
||
|
+ cancel_work_sync(&local->dynamic_ps_enable_work);
|
||
|
+
|
||
|
+ cancel_work_sync(&sdata->recalc_smps);
|
||
|
+ sdata_lock(sdata);
|
||
|
+ mutex_lock(&local->mtx);
|
||
|
+ sdata->vif.csa_active = false;
|
||
|
+ if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||
|
+ sdata->u.mgd.csa_waiting_bcn = false;
|
||
|
+ if (sdata->csa_block_tx) {
|
||
|
+ ieee80211_wake_vif_queues(local, sdata,
|
||
|
+ IEEE80211_QUEUE_STOP_REASON_CSA);
|
||
|
+ sdata->csa_block_tx = false;
|
||
|
+ }
|
||
|
+ mutex_unlock(&local->mtx);
|
||
|
+ sdata_unlock(sdata);
|
||
|
+
|
||
|
+ cancel_work_sync(&sdata->csa_finalize_work);
|
||
|
+
|
||
|
+ cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
|
||
|
+
|
||
|
+ if (sdata->wdev.cac_started) {
|
||
|
+ chandef = sdata->vif.bss_conf.chandef;
|
||
|
+ WARN_ON(local->suspended);
|
||
|
+ mutex_lock(&local->mtx);
|
||
|
+ ieee80211_vif_release_channel(sdata);
|
||
|
+ mutex_unlock(&local->mtx);
|
||
|
+ cfg80211_cac_event(sdata->dev, &chandef,
|
||
|
+ NL80211_RADAR_CAC_ABORTED,
|
||
|
+ GFP_KERNEL);
|
||
|
+ }
|
||
|
+
|
||
|
+ /* APs need special treatment */
|
||
|
+ if (sdata->vif.type == NL80211_IFTYPE_AP) {
|
||
|
+ struct ieee80211_sub_if_data *vlan, *tmpsdata;
|
||
|
+
|
||
|
+ /* down all dependent devices, that is VLANs */
|
||
|
+ list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
|
||
|
+ u.vlan.list)
|
||
|
+ dev_close(vlan->dev);
|
||
|
+ WARN_ON(!list_empty(&sdata->u.ap.vlans));
|
||
|
+ } else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
|
||
|
+ /* remove all packets in parent bc_buf pointing to this dev */
|
||
|
+ ps = &sdata->bss->ps;
|
||
|
+
|
||
|
+ spin_lock_irqsave(&ps->bc_buf.lock, flags);
|
||
|
+ skb_queue_walk_safe(&ps->bc_buf, skb, tmp) {
|
||
|
+ if (skb->dev == sdata->dev) {
|
||
|
+ __skb_unlink(skb, &ps->bc_buf);
|
||
|
+ local->total_ps_buffered--;
|
||
|
+ ieee80211_free_txskb(&local->hw, skb);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ spin_unlock_irqrestore(&ps->bc_buf.lock, flags);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (going_down)
|
||
|
+ local->open_count--;
|
||
|
+
|
||
|
+ switch (sdata->vif.type) {
|
||
|
+ case NL80211_IFTYPE_AP_VLAN:
|
||
|
+ mutex_lock(&local->mtx);
|
||
|
+ list_del(&sdata->u.vlan.list);
|
||
|
+ mutex_unlock(&local->mtx);
|
||
|
+ RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
|
||
|
+ /* see comment in the default case below */
|
||
|
+ ieee80211_free_keys(sdata, true);
|
||
|
+ /* no need to tell driver */
|
||
|
+ break;
|
||
|
+ case NL80211_IFTYPE_MONITOR:
|
||
|
+ if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) {
|
||
|
+ local->cooked_mntrs--;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+
|
||
|
+ local->monitors--;
|
||
|
+ if (local->monitors == 0) {
|
||
|
+ local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR;
|
||
|
+ hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
|
||
|
+ }
|
||
|
+
|
||
|
+ ieee80211_adjust_monitor_flags(sdata, -1);
|
||
|
+ break;
|
||
|
+ case NL80211_IFTYPE_NAN:
|
||
|
+ /* clean all the functions */
|
||
|
+ spin_lock_bh(&sdata->u.nan.func_lock);
|
||
|
+
|
||
|
+ idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, i) {
|
||
|
+ idr_remove(&sdata->u.nan.function_inst_ids, i);
|
||
|
+ cfg80211_free_nan_func(func);
|
||
|
+ }
|
||
|
+ idr_destroy(&sdata->u.nan.function_inst_ids);
|
||
|
+
|
||
|
+ spin_unlock_bh(&sdata->u.nan.func_lock);
|
||
|
+ break;
|
||
|
+ case NL80211_IFTYPE_P2P_DEVICE:
|
||
|
+ /* relies on synchronize_rcu() below */
|
||
|
+ RCU_INIT_POINTER(local->p2p_sdata, NULL);
|
||
|
+ /* fall through */
|
||
|
+ default:
|
||
|
+ cancel_work_sync(&sdata->work);
|
||
|
+ /*
|
||
|
+ * When we get here, the interface is marked down.
|
||
|
+ * Free the remaining keys, if there are any
|
||
|
+ * (which can happen in AP mode if userspace sets
|
||
|
+ * keys before the interface is operating, and maybe
|
||
|
+ * also in WDS mode)
|
||
|
+ *
|
||
|
+ * Force the key freeing to always synchronize_net()
|
||
|
+ * to wait for the RX path in case it is using this
|
||
|
+ * interface enqueuing frames at this very time on
|
||
|
+ * another CPU.
|
||
|
+ */
|
||
|
+ ieee80211_free_keys(sdata, true);
|
||
|
+ skb_queue_purge(&sdata->skb_queue);
|
||
|
+ }
|
||
|
+
|
||
|
+ spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||
|
+ for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
|
||
|
+ skb_queue_walk_safe(&local->pending[i], skb, tmp) {
|
||
|
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||
|
+ if (info->control.vif == &sdata->vif) {
|
||
|
+ __skb_unlink(skb, &local->pending[i]);
|
||
|
+ ieee80211_free_txskb(&local->hw, skb);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||
|
+
|
||
|
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
||
|
+ ieee80211_txq_remove_vlan(local, sdata);
|
||
|
+
|
||
|
+ sdata->bss = NULL;
|
||
|
+
|
||
|
+ if (local->open_count == 0)
|
||
|
+ ieee80211_clear_tx_pending(local);
|
||
|
+
|
||
|
+ sdata->vif.bss_conf.beacon_int = 0;
|
||
|
+
|
||
|
+ /*
|
||
|
+ * If the interface goes down while suspended, presumably because
|
||
|
+ * the device was unplugged and that happens before our resume,
|
||
|
+ * then the driver is already unconfigured and the remainder of
|
||
|
+ * this function isn't needed.
|
||
|
+ * XXX: what about WoWLAN? If the device has software state, e.g.
|
||
|
+ * memory allocated, it might expect teardown commands from
|
||
|
+ * mac80211 here?
|
||
|
+ */
|
||
|
+ if (local->suspended) {
|
||
|
+ WARN_ON(local->wowlan);
|
||
|
+ WARN_ON(rtnl_dereference(local->monitor_sdata));
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ switch (sdata->vif.type) {
|
||
|
+ case NL80211_IFTYPE_AP_VLAN:
|
||
|
+ break;
|
||
|
+ case NL80211_IFTYPE_MONITOR:
|
||
|
+ if (local->monitors == 0)
|
||
|
+ ieee80211_del_virtual_monitor(local);
|
||
|
+
|
||
|
+ mutex_lock(&local->mtx);
|
||
|
+ ieee80211_recalc_idle(local);
|
||
|
+ mutex_unlock(&local->mtx);
|
||
|
+
|
||
|
+ if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
|
||
|
+ break;
|
||
|
+
|
||
|
+ /* fall through */
|
||
|
+ default:
|
||
|
+ if (going_down)
|
||
|
+ drv_remove_interface(local, sdata);
|
||
|
+ }
|
||
|
+
|
||
|
+ ieee80211_recalc_ps(local);
|
||
|
+
|
||
|
+ if (cancel_scan)
|
||
|
+ flush_delayed_work(&local->scan_work);
|
||
|
+
|
||
|
+ if (local->open_count == 0) {
|
||
|
+ ieee80211_stop_device(local);
|
||
|
+
|
||
|
+ /* no reconfiguring after stop! */
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* do after stop to avoid reconfiguring when we stop anyway */
|
||
|
+ ieee80211_configure_filter(local);
|
||
|
+ ieee80211_hw_config(local, hw_reconf_flags);
|
||
|
+
|
||
|
+ if (local->monitors == local->open_count)
|
||
|
+ ieee80211_add_virtual_monitor(local);
|
||
|
+}
|
||
|
+
|
||
|
+static int ieee80211_stop(struct net_device *dev)
|
||
|
+{
|
||
|
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||
|
+
|
||
|
+ ieee80211_do_stop(sdata, true);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static void ieee80211_set_multicast_list(struct net_device *dev)
|
||
|
+{
|
||
|
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||
|
+ struct ieee80211_local *local = sdata->local;
|
||
|
+ int allmulti, sdata_allmulti;
|
||
|
+
|
||
|
+ allmulti = !!(dev->flags & IFF_ALLMULTI);
|
||
|
+ sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI);
|
||
|
+
|
||
|
+ if (allmulti != sdata_allmulti) {
|
||
|
+ if (dev->flags & IFF_ALLMULTI)
|
||
|
+ atomic_inc(&local->iff_allmultis);
|
||
|
+ else
|
||
|
+ atomic_dec(&local->iff_allmultis);
|
||
|
+ sdata->flags ^= IEEE80211_SDATA_ALLMULTI;
|
||
|
+ }
|
||
|
+
|
||
|
+ spin_lock_bh(&local->filter_lock);
|
||
|
+ __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len);
|
||
|
+ spin_unlock_bh(&local->filter_lock);
|
||
|
+ ieee80211_queue_work(&local->hw, &local->reconfig_filter);
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+ * Called when the netdev is removed or, by the code below, before
|
||
|
+ * the interface type changes.
|
||
|
+ */
|
||
|
+static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+
|
||
|
+ /* free extra data */
|
||
|
+ ieee80211_free_keys(sdata, false);
|
||
|
+
|
||
|
+ ieee80211_debugfs_remove_netdev(sdata);
|
||
|
+
|
||
|
+ for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
|
||
|
+ __skb_queue_purge(&sdata->fragments[i].skb_list);
|
||
|
+ sdata->fragment_next = 0;
|
||
|
+
|
||
|
+ if (ieee80211_vif_is_mesh(&sdata->vif))
|
||
|
+ ieee80211_mesh_teardown_sdata(sdata);
|
||
|
+}
|
||
|
+
|
||
|
+static void ieee80211_uninit(struct net_device *dev)
|
||
|
+{
|
||
|
+ ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev));
|
||
|
+}
|
||
|
+
|
||
|
+#if LINUX_VERSION_IS_GEQ(5,2,0)
|
||
|
+static u16 ieee80211_netdev_select_queue(struct net_device *dev,
|
||
|
+ struct sk_buff *skb,
|
||
|
+ struct net_device *sb_dev)
|
||
|
+#elif LINUX_VERSION_IS_GEQ(4,19,0)
|
||
|
+static u16 ieee80211_netdev_select_queue(struct net_device *dev,
|
||
|
+ struct sk_buff *skb,
|
||
|
+ struct net_device *sb_dev,
|
||
|
+ select_queue_fallback_t fallback)
|
||
|
+#elif LINUX_VERSION_IS_GEQ(3,14,0) || \
|
||
|
+ (LINUX_VERSION_CODE == KERNEL_VERSION(3,13,11) && UTS_UBUNTU_RELEASE_ABI > 30)
|
||
|
+static u16 ieee80211_netdev_select_queue(struct net_device *dev,
|
||
|
+ struct sk_buff *skb,
|
||
|
+ void *accel_priv,
|
||
|
+ select_queue_fallback_t fallback)
|
||
|
+#elif LINUX_VERSION_IS_GEQ(3,13,0)
|
||
|
+static u16 ieee80211_netdev_select_queue(struct net_device *dev,
|
||
|
+ struct sk_buff *skb,
|
||
|
+ void *accel_priv)
|
||
|
+#else
|
||
|
+static u16 ieee80211_netdev_select_queue(struct net_device *dev,
|
||
|
+ struct sk_buff *skb)
|
||
|
+#endif
|
||
|
+{
|
||
|
+ return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb);
|
||
|
+}
|
||
|
+
|
||
|
+static void
|
||
|
+ieee80211_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+
|
||
|
+ for_each_possible_cpu(i) {
|
||
|
+ const struct pcpu_sw_netstats *tstats;
|
||
|
+ u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
|
||
|
+ unsigned int start;
|
||
|
+
|
||
|
+ tstats = per_cpu_ptr(netdev_tstats(dev), i);
|
||
|
+
|
||
|
+ do {
|
||
|
+ start = u64_stats_fetch_begin_irq(&tstats->syncp);
|
||
|
+ rx_packets = tstats->rx_packets;
|
||
|
+ tx_packets = tstats->tx_packets;
|
||
|
+ rx_bytes = tstats->rx_bytes;
|
||
|
+ tx_bytes = tstats->tx_bytes;
|
||
|
+ } while (u64_stats_fetch_retry_irq(&tstats->syncp, start));
|
||
|
+
|
||
|
+ stats->rx_packets += rx_packets;
|
||
|
+ stats->tx_packets += tx_packets;
|
||
|
+ stats->rx_bytes += rx_bytes;
|
||
|
+ stats->tx_bytes += tx_bytes;
|
||
|
+ }
|
||
|
+}
|
||
|
+#if LINUX_VERSION_IS_LESS(4,11,0)
|
||
|
+/* Just declare it here to keep sparse happy */
|
||
|
+struct rtnl_link_stats64 *bp_ieee80211_get_stats64(struct net_device *dev,
|
||
|
+ struct rtnl_link_stats64 *stats);
|
||
|
+struct rtnl_link_stats64 *
|
||
|
+bp_ieee80211_get_stats64(struct net_device *dev,
|
||
|
+ struct rtnl_link_stats64 *stats){
|
||
|
+ ieee80211_get_stats64(dev, stats);
|
||
|
+ return stats;
|
||
|
+}
|
||
|
+#endif
|
||
|
+
|
||
|
+static const struct net_device_ops ieee80211_dataif_ops = {
|
||
|
+ .ndo_open = ieee80211_open,
|
||
|
+ .ndo_stop = ieee80211_stop,
|
||
|
+ .ndo_uninit = ieee80211_uninit,
|
||
|
+ .ndo_start_xmit = ieee80211_subif_start_xmit,
|
||
|
+ .ndo_set_rx_mode = ieee80211_set_multicast_list,
|
||
|
+ .ndo_set_mac_address = ieee80211_change_mac,
|
||
|
+ .ndo_select_queue = ieee80211_netdev_select_queue,
|
||
|
+#if LINUX_VERSION_IS_GEQ(4,11,0)
|
||
|
+ .ndo_get_stats64 = ieee80211_get_stats64,
|
||
|
+#else
|
||
|
+ .ndo_get_stats64 = bp_ieee80211_get_stats64,
|
||
|
+#endif
|
||
|
+
|
||
|
+};
|
||
|
+
|
||
|
+#if LINUX_VERSION_IS_GEQ(5,2,0)
|
||
|
+static u16 ieee80211_monitor_select_queue(struct net_device *dev,
|
||
|
+ struct sk_buff *skb,
|
||
|
+ struct net_device *sb_dev)
|
||
|
+#elif LINUX_VERSION_IS_GEQ(4,19,0)
|
||
|
+static u16 ieee80211_monitor_select_queue(struct net_device *dev,
|
||
|
+ struct sk_buff *skb,
|
||
|
+ struct net_device *sb_dev,
|
||
|
+ select_queue_fallback_t fallback)
|
||
|
+#elif LINUX_VERSION_IS_GEQ(3,14,0) || \
|
||
|
+ (LINUX_VERSION_CODE == KERNEL_VERSION(3,13,11) && UTS_UBUNTU_RELEASE_ABI > 30)
|
||
|
+static u16 ieee80211_monitor_select_queue(struct net_device *dev,
|
||
|
+ struct sk_buff *skb,
|
||
|
+ void *accel_priv,
|
||
|
+ select_queue_fallback_t fallback)
|
||
|
+#elif LINUX_VERSION_IS_GEQ(3,13,0)
|
||
|
+static u16 ieee80211_monitor_select_queue(struct net_device *dev,
|
||
|
+ struct sk_buff *skb,
|
||
|
+ void *accel_priv)
|
||
|
+#else
|
||
|
+static u16 ieee80211_monitor_select_queue(struct net_device *dev,
|
||
|
+ struct sk_buff *skb)
|
||
|
+#endif
|
||
|
+{
|
||
|
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||
|
+ struct ieee80211_local *local = sdata->local;
|
||
|
+ struct ieee80211_hdr *hdr;
|
||
|
+ struct ieee80211_radiotap_header *rtap = (void *)skb->data;
|
||
|
+
|
||
|
+ if (local->hw.queues < IEEE80211_NUM_ACS)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ if (skb->len < 4 ||
|
||
|
+ skb->len < le16_to_cpu(rtap->it_len) + 2 /* frame control */)
|
||
|
+ return 0; /* doesn't matter, frame will be dropped */
|
||
|
+
|
||
|
+ hdr = (void *)((u8 *)skb->data + le16_to_cpu(rtap->it_len));
|
||
|
+
|
||
|
+ return ieee80211_select_queue_80211(sdata, skb, hdr);
|
||
|
+}
|
||
|
+
|
||
|
+static const struct net_device_ops ieee80211_monitorif_ops = {
|
||
|
+ .ndo_open = ieee80211_open,
|
||
|
+ .ndo_stop = ieee80211_stop,
|
||
|
+ .ndo_uninit = ieee80211_uninit,
|
||
|
+ .ndo_start_xmit = ieee80211_monitor_start_xmit,
|
||
|
+ .ndo_set_rx_mode = ieee80211_set_multicast_list,
|
||
|
+ .ndo_set_mac_address = ieee80211_change_mac,
|
||
|
+ .ndo_select_queue = ieee80211_monitor_select_queue,
|
||
|
+#if LINUX_VERSION_IS_GEQ(4,11,0)
|
||
|
+ .ndo_get_stats64 = ieee80211_get_stats64,
|
||
|
+#else
|
||
|
+ .ndo_get_stats64 = bp_ieee80211_get_stats64,
|
||
|
+#endif
|
||
|
+
|
||
|
+};
|
||
|
+
|
||
|
+static const struct net_device_ops ieee80211_dataif_8023_ops = {
|
||
|
+ .ndo_open = ieee80211_open,
|
||
|
+ .ndo_stop = ieee80211_stop,
|
||
|
+ .ndo_uninit = ieee80211_uninit,
|
||
|
+ .ndo_start_xmit = ieee80211_subif_start_xmit_8023,
|
||
|
+ .ndo_set_rx_mode = ieee80211_set_multicast_list,
|
||
|
+ .ndo_set_mac_address = ieee80211_change_mac,
|
||
|
+ .ndo_select_queue = ieee80211_netdev_select_queue,
|
||
|
+#if LINUX_VERSION_IS_GEQ(4,11,0)
|
||
|
+ .ndo_get_stats64 = ieee80211_get_stats64,
|
||
|
+#else
|
||
|
+ .ndo_get_stats64 = bp_ieee80211_get_stats64,
|
||
|
+#endif
|
||
|
+
|
||
|
+};
|
||
|
+
|
||
|
static bool ieee80211_iftype_supports_encap_offload(enum nl80211_iftype iftype)
|
||
|
{
|
||
|
switch (iftype) {
|
||
|
@@ -389,6 +893,31 @@ static bool ieee80211_set_sdata_offload_
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
+static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata)
|
||
|
+{
|
||
|
+ struct ieee80211_local *local = sdata->local;
|
||
|
+ struct ieee80211_sub_if_data *bss = sdata;
|
||
|
+ bool enabled;
|
||
|
+
|
||
|
+ if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
|
||
|
+ if (!sdata->bss)
|
||
|
+ return;
|
||
|
+
|
||
|
+ bss = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
|
||
|
+ }
|
||
|
+
|
||
|
+ if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) ||
|
||
|
+ !ieee80211_iftype_supports_encap_offload(bss->vif.type))
|
||
|
+ return;
|
||
|
+
|
||
|
+ enabled = bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED;
|
||
|
+ if (sdata->wdev.use_4addr &&
|
||
|
+ !(bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_4ADDR))
|
||
|
+ enabled = false;
|
||
|
+
|
||
|
+ sdata->dev->netdev_ops = enabled ? &ieee80211_dataif_8023_ops :
|
||
|
+ &ieee80211_dataif_ops;
|
||
|
+}
|
||
|
|
||
|
static void ieee80211_recalc_sdata_offload(struct ieee80211_sub_if_data *sdata)
|
||
|
{
|
||
|
@@ -866,511 +1395,6 @@ int ieee80211_do_open(struct wireless_de
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
-static int ieee80211_open(struct net_device *dev)
|
||
|
-{
|
||
|
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||
|
- int err;
|
||
|
-
|
||
|
- /* fail early if user set an invalid address */
|
||
|
- if (!is_valid_ether_addr(dev->dev_addr))
|
||
|
- return -EADDRNOTAVAIL;
|
||
|
-
|
||
|
- err = ieee80211_check_concurrent_iface(sdata, sdata->vif.type);
|
||
|
- if (err)
|
||
|
- return err;
|
||
|
-
|
||
|
- return ieee80211_do_open(&sdata->wdev, true);
|
||
|
-}
|
||
|
-
|
||
|
-static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
|
||
|
- bool going_down)
|
||
|
-{
|
||
|
- struct ieee80211_local *local = sdata->local;
|
||
|
- unsigned long flags;
|
||
|
- struct sk_buff *skb, *tmp;
|
||
|
- u32 hw_reconf_flags = 0;
|
||
|
- int i, flushed;
|
||
|
- struct ps_data *ps;
|
||
|
- struct cfg80211_chan_def chandef;
|
||
|
- bool cancel_scan;
|
||
|
- struct cfg80211_nan_func *func;
|
||
|
-
|
||
|
- clear_bit(SDATA_STATE_RUNNING, &sdata->state);
|
||
|
-
|
||
|
- cancel_scan = rcu_access_pointer(local->scan_sdata) == sdata;
|
||
|
- if (cancel_scan)
|
||
|
- ieee80211_scan_cancel(local);
|
||
|
-
|
||
|
- /*
|
||
|
- * Stop TX on this interface first.
|
||
|
- */
|
||
|
- if (sdata->dev)
|
||
|
- netif_tx_stop_all_queues(sdata->dev);
|
||
|
-
|
||
|
- ieee80211_roc_purge(local, sdata);
|
||
|
-
|
||
|
- switch (sdata->vif.type) {
|
||
|
- case NL80211_IFTYPE_STATION:
|
||
|
- ieee80211_mgd_stop(sdata);
|
||
|
- break;
|
||
|
- case NL80211_IFTYPE_ADHOC:
|
||
|
- ieee80211_ibss_stop(sdata);
|
||
|
- break;
|
||
|
- case NL80211_IFTYPE_MONITOR:
|
||
|
- if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES)
|
||
|
- break;
|
||
|
- list_del_rcu(&sdata->u.mntr.list);
|
||
|
- break;
|
||
|
- default:
|
||
|
- break;
|
||
|
- }
|
||
|
-
|
||
|
- /*
|
||
|
- * Remove all stations associated with this interface.
|
||
|
- *
|
||
|
- * This must be done before calling ops->remove_interface()
|
||
|
- * because otherwise we can later invoke ops->sta_notify()
|
||
|
- * whenever the STAs are removed, and that invalidates driver
|
||
|
- * assumptions about always getting a vif pointer that is valid
|
||
|
- * (because if we remove a STA after ops->remove_interface()
|
||
|
- * the driver will have removed the vif info already!)
|
||
|
- *
|
||
|
- * In WDS mode a station must exist here and be flushed, for
|
||
|
- * AP_VLANs stations may exist since there's nothing else that
|
||
|
- * would have removed them, but in other modes there shouldn't
|
||
|
- * be any stations.
|
||
|
- */
|
||
|
- flushed = sta_info_flush(sdata);
|
||
|
- WARN_ON_ONCE(sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
|
||
|
- ((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) ||
|
||
|
- (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)));
|
||
|
-
|
||
|
- /* don't count this interface for allmulti while it is down */
|
||
|
- if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
|
||
|
- atomic_dec(&local->iff_allmultis);
|
||
|
-
|
||
|
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
|
||
|
- local->fif_pspoll--;
|
||
|
- local->fif_probe_req--;
|
||
|
- } else if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
|
||
|
- local->fif_probe_req--;
|
||
|
- }
|
||
|
-
|
||
|
- if (sdata->dev) {
|
||
|
- netif_addr_lock_bh(sdata->dev);
|
||
|
- spin_lock_bh(&local->filter_lock);
|
||
|
- __hw_addr_unsync(&local->mc_list, &sdata->dev->mc,
|
||
|
- sdata->dev->addr_len);
|
||
|
- spin_unlock_bh(&local->filter_lock);
|
||
|
- netif_addr_unlock_bh(sdata->dev);
|
||
|
- }
|
||
|
-
|
||
|
- del_timer_sync(&local->dynamic_ps_timer);
|
||
|
- cancel_work_sync(&local->dynamic_ps_enable_work);
|
||
|
-
|
||
|
- cancel_work_sync(&sdata->recalc_smps);
|
||
|
- sdata_lock(sdata);
|
||
|
- mutex_lock(&local->mtx);
|
||
|
- sdata->vif.csa_active = false;
|
||
|
- if (sdata->vif.type == NL80211_IFTYPE_STATION)
|
||
|
- sdata->u.mgd.csa_waiting_bcn = false;
|
||
|
- if (sdata->csa_block_tx) {
|
||
|
- ieee80211_wake_vif_queues(local, sdata,
|
||
|
- IEEE80211_QUEUE_STOP_REASON_CSA);
|
||
|
- sdata->csa_block_tx = false;
|
||
|
- }
|
||
|
- mutex_unlock(&local->mtx);
|
||
|
- sdata_unlock(sdata);
|
||
|
-
|
||
|
- cancel_work_sync(&sdata->csa_finalize_work);
|
||
|
-
|
||
|
- cancel_delayed_work_sync(&sdata->dfs_cac_timer_work);
|
||
|
-
|
||
|
- if (sdata->wdev.cac_started) {
|
||
|
- chandef = sdata->vif.bss_conf.chandef;
|
||
|
- WARN_ON(local->suspended);
|
||
|
- mutex_lock(&local->mtx);
|
||
|
- ieee80211_vif_release_channel(sdata);
|
||
|
- mutex_unlock(&local->mtx);
|
||
|
- cfg80211_cac_event(sdata->dev, &chandef,
|
||
|
- NL80211_RADAR_CAC_ABORTED,
|
||
|
- GFP_KERNEL);
|
||
|
- }
|
||
|
-
|
||
|
- /* APs need special treatment */
|
||
|
- if (sdata->vif.type == NL80211_IFTYPE_AP) {
|
||
|
- struct ieee80211_sub_if_data *vlan, *tmpsdata;
|
||
|
-
|
||
|
- /* down all dependent devices, that is VLANs */
|
||
|
- list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans,
|
||
|
- u.vlan.list)
|
||
|
- dev_close(vlan->dev);
|
||
|
- WARN_ON(!list_empty(&sdata->u.ap.vlans));
|
||
|
- } else if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
|
||
|
- /* remove all packets in parent bc_buf pointing to this dev */
|
||
|
- ps = &sdata->bss->ps;
|
||
|
-
|
||
|
- spin_lock_irqsave(&ps->bc_buf.lock, flags);
|
||
|
- skb_queue_walk_safe(&ps->bc_buf, skb, tmp) {
|
||
|
- if (skb->dev == sdata->dev) {
|
||
|
- __skb_unlink(skb, &ps->bc_buf);
|
||
|
- local->total_ps_buffered--;
|
||
|
- ieee80211_free_txskb(&local->hw, skb);
|
||
|
- }
|
||
|
- }
|
||
|
- spin_unlock_irqrestore(&ps->bc_buf.lock, flags);
|
||
|
- }
|
||
|
-
|
||
|
- if (going_down)
|
||
|
- local->open_count--;
|
||
|
-
|
||
|
- switch (sdata->vif.type) {
|
||
|
- case NL80211_IFTYPE_AP_VLAN:
|
||
|
- mutex_lock(&local->mtx);
|
||
|
- list_del(&sdata->u.vlan.list);
|
||
|
- mutex_unlock(&local->mtx);
|
||
|
- RCU_INIT_POINTER(sdata->vif.chanctx_conf, NULL);
|
||
|
- /* see comment in the default case below */
|
||
|
- ieee80211_free_keys(sdata, true);
|
||
|
- /* no need to tell driver */
|
||
|
- break;
|
||
|
- case NL80211_IFTYPE_MONITOR:
|
||
|
- if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) {
|
||
|
- local->cooked_mntrs--;
|
||
|
- break;
|
||
|
- }
|
||
|
-
|
||
|
- local->monitors--;
|
||
|
- if (local->monitors == 0) {
|
||
|
- local->hw.conf.flags &= ~IEEE80211_CONF_MONITOR;
|
||
|
- hw_reconf_flags |= IEEE80211_CONF_CHANGE_MONITOR;
|
||
|
- }
|
||
|
-
|
||
|
- ieee80211_adjust_monitor_flags(sdata, -1);
|
||
|
- break;
|
||
|
- case NL80211_IFTYPE_NAN:
|
||
|
- /* clean all the functions */
|
||
|
- spin_lock_bh(&sdata->u.nan.func_lock);
|
||
|
-
|
||
|
- idr_for_each_entry(&sdata->u.nan.function_inst_ids, func, i) {
|
||
|
- idr_remove(&sdata->u.nan.function_inst_ids, i);
|
||
|
- cfg80211_free_nan_func(func);
|
||
|
- }
|
||
|
- idr_destroy(&sdata->u.nan.function_inst_ids);
|
||
|
-
|
||
|
- spin_unlock_bh(&sdata->u.nan.func_lock);
|
||
|
- break;
|
||
|
- case NL80211_IFTYPE_P2P_DEVICE:
|
||
|
- /* relies on synchronize_rcu() below */
|
||
|
- RCU_INIT_POINTER(local->p2p_sdata, NULL);
|
||
|
- /* fall through */
|
||
|
- default:
|
||
|
- cancel_work_sync(&sdata->work);
|
||
|
- /*
|
||
|
- * When we get here, the interface is marked down.
|
||
|
- * Free the remaining keys, if there are any
|
||
|
- * (which can happen in AP mode if userspace sets
|
||
|
- * keys before the interface is operating, and maybe
|
||
|
- * also in WDS mode)
|
||
|
- *
|
||
|
- * Force the key freeing to always synchronize_net()
|
||
|
- * to wait for the RX path in case it is using this
|
||
|
- * interface enqueuing frames at this very time on
|
||
|
- * another CPU.
|
||
|
- */
|
||
|
- ieee80211_free_keys(sdata, true);
|
||
|
- skb_queue_purge(&sdata->skb_queue);
|
||
|
- }
|
||
|
-
|
||
|
- spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
|
||
|
- for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
|
||
|
- skb_queue_walk_safe(&local->pending[i], skb, tmp) {
|
||
|
- struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||
|
- if (info->control.vif == &sdata->vif) {
|
||
|
- __skb_unlink(skb, &local->pending[i]);
|
||
|
- ieee80211_free_txskb(&local->hw, skb);
|
||
|
- }
|
||
|
- }
|
||
|
- }
|
||
|
- spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags);
|
||
|
-
|
||
|
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
|
||
|
- ieee80211_txq_remove_vlan(local, sdata);
|
||
|
-
|
||
|
- sdata->bss = NULL;
|
||
|
-
|
||
|
- if (local->open_count == 0)
|
||
|
- ieee80211_clear_tx_pending(local);
|
||
|
-
|
||
|
- sdata->vif.bss_conf.beacon_int = 0;
|
||
|
-
|
||
|
- /*
|
||
|
- * If the interface goes down while suspended, presumably because
|
||
|
- * the device was unplugged and that happens before our resume,
|
||
|
- * then the driver is already unconfigured and the remainder of
|
||
|
- * this function isn't needed.
|
||
|
- * XXX: what about WoWLAN? If the device has software state, e.g.
|
||
|
- * memory allocated, it might expect teardown commands from
|
||
|
- * mac80211 here?
|
||
|
- */
|
||
|
- if (local->suspended) {
|
||
|
- WARN_ON(local->wowlan);
|
||
|
- WARN_ON(rtnl_dereference(local->monitor_sdata));
|
||
|
- return;
|
||
|
- }
|
||
|
-
|
||
|
- switch (sdata->vif.type) {
|
||
|
- case NL80211_IFTYPE_AP_VLAN:
|
||
|
- break;
|
||
|
- case NL80211_IFTYPE_MONITOR:
|
||
|
- if (local->monitors == 0)
|
||
|
- ieee80211_del_virtual_monitor(local);
|
||
|
-
|
||
|
- mutex_lock(&local->mtx);
|
||
|
- ieee80211_recalc_idle(local);
|
||
|
- mutex_unlock(&local->mtx);
|
||
|
-
|
||
|
- if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
|
||
|
- break;
|
||
|
-
|
||
|
- /* fall through */
|
||
|
- default:
|
||
|
- if (going_down)
|
||
|
- drv_remove_interface(local, sdata);
|
||
|
- }
|
||
|
-
|
||
|
- ieee80211_recalc_ps(local);
|
||
|
-
|
||
|
- if (cancel_scan)
|
||
|
- flush_delayed_work(&local->scan_work);
|
||
|
-
|
||
|
- if (local->open_count == 0) {
|
||
|
- ieee80211_stop_device(local);
|
||
|
-
|
||
|
- /* no reconfiguring after stop! */
|
||
|
- return;
|
||
|
- }
|
||
|
-
|
||
|
- /* do after stop to avoid reconfiguring when we stop anyway */
|
||
|
- ieee80211_configure_filter(local);
|
||
|
- ieee80211_hw_config(local, hw_reconf_flags);
|
||
|
-
|
||
|
- if (local->monitors == local->open_count)
|
||
|
- ieee80211_add_virtual_monitor(local);
|
||
|
-}
|
||
|
-
|
||
|
-static int ieee80211_stop(struct net_device *dev)
|
||
|
-{
|
||
|
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||
|
-
|
||
|
- ieee80211_do_stop(sdata, true);
|
||
|
-
|
||
|
- return 0;
|
||
|
-}
|
||
|
-
|
||
|
-static void ieee80211_set_multicast_list(struct net_device *dev)
|
||
|
-{
|
||
|
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||
|
- struct ieee80211_local *local = sdata->local;
|
||
|
- int allmulti, sdata_allmulti;
|
||
|
-
|
||
|
- allmulti = !!(dev->flags & IFF_ALLMULTI);
|
||
|
- sdata_allmulti = !!(sdata->flags & IEEE80211_SDATA_ALLMULTI);
|
||
|
-
|
||
|
- if (allmulti != sdata_allmulti) {
|
||
|
- if (dev->flags & IFF_ALLMULTI)
|
||
|
- atomic_inc(&local->iff_allmultis);
|
||
|
- else
|
||
|
- atomic_dec(&local->iff_allmultis);
|
||
|
- sdata->flags ^= IEEE80211_SDATA_ALLMULTI;
|
||
|
- }
|
||
|
-
|
||
|
- spin_lock_bh(&local->filter_lock);
|
||
|
- __hw_addr_sync(&local->mc_list, &dev->mc, dev->addr_len);
|
||
|
- spin_unlock_bh(&local->filter_lock);
|
||
|
- ieee80211_queue_work(&local->hw, &local->reconfig_filter);
|
||
|
-}
|
||
|
-
|
||
|
-/*
|
||
|
- * Called when the netdev is removed or, by the code below, before
|
||
|
- * the interface type changes.
|
||
|
- */
|
||
|
-static void ieee80211_teardown_sdata(struct ieee80211_sub_if_data *sdata)
|
||
|
-{
|
||
|
- int i;
|
||
|
-
|
||
|
- /* free extra data */
|
||
|
- ieee80211_free_keys(sdata, false);
|
||
|
-
|
||
|
- ieee80211_debugfs_remove_netdev(sdata);
|
||
|
-
|
||
|
- for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++)
|
||
|
- __skb_queue_purge(&sdata->fragments[i].skb_list);
|
||
|
- sdata->fragment_next = 0;
|
||
|
-
|
||
|
- if (ieee80211_vif_is_mesh(&sdata->vif))
|
||
|
- ieee80211_mesh_teardown_sdata(sdata);
|
||
|
-}
|
||
|
-
|
||
|
-static void ieee80211_uninit(struct net_device *dev)
|
||
|
-{
|
||
|
- ieee80211_teardown_sdata(IEEE80211_DEV_TO_SUB_IF(dev));
|
||
|
-}
|
||
|
-
|
||
|
-#if LINUX_VERSION_IS_GEQ(5,2,0)
|
||
|
-static u16 ieee80211_netdev_select_queue(struct net_device *dev,
|
||
|
- struct sk_buff *skb,
|
||
|
- struct net_device *sb_dev)
|
||
|
-#elif LINUX_VERSION_IS_GEQ(4,19,0)
|
||
|
-static u16 ieee80211_netdev_select_queue(struct net_device *dev,
|
||
|
- struct sk_buff *skb,
|
||
|
- struct net_device *sb_dev,
|
||
|
- select_queue_fallback_t fallback)
|
||
|
-#elif LINUX_VERSION_IS_GEQ(3,14,0) || \
|
||
|
- (LINUX_VERSION_CODE == KERNEL_VERSION(3,13,11) && UTS_UBUNTU_RELEASE_ABI > 30)
|
||
|
-static u16 ieee80211_netdev_select_queue(struct net_device *dev,
|
||
|
- struct sk_buff *skb,
|
||
|
- void *accel_priv,
|
||
|
- select_queue_fallback_t fallback)
|
||
|
-#elif LINUX_VERSION_IS_GEQ(3,13,0)
|
||
|
-static u16 ieee80211_netdev_select_queue(struct net_device *dev,
|
||
|
- struct sk_buff *skb,
|
||
|
- void *accel_priv)
|
||
|
-#else
|
||
|
-static u16 ieee80211_netdev_select_queue(struct net_device *dev,
|
||
|
- struct sk_buff *skb)
|
||
|
-#endif
|
||
|
-{
|
||
|
- return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb);
|
||
|
-}
|
||
|
-
|
||
|
-static void
|
||
|
-ieee80211_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
|
||
|
-{
|
||
|
- int i;
|
||
|
-
|
||
|
- for_each_possible_cpu(i) {
|
||
|
- const struct pcpu_sw_netstats *tstats;
|
||
|
- u64 rx_packets, rx_bytes, tx_packets, tx_bytes;
|
||
|
- unsigned int start;
|
||
|
-
|
||
|
- tstats = per_cpu_ptr(netdev_tstats(dev), i);
|
||
|
-
|
||
|
- do {
|
||
|
- start = u64_stats_fetch_begin_irq(&tstats->syncp);
|
||
|
- rx_packets = tstats->rx_packets;
|
||
|
- tx_packets = tstats->tx_packets;
|
||
|
- rx_bytes = tstats->rx_bytes;
|
||
|
- tx_bytes = tstats->tx_bytes;
|
||
|
- } while (u64_stats_fetch_retry_irq(&tstats->syncp, start));
|
||
|
-
|
||
|
- stats->rx_packets += rx_packets;
|
||
|
- stats->tx_packets += tx_packets;
|
||
|
- stats->rx_bytes += rx_bytes;
|
||
|
- stats->tx_bytes += tx_bytes;
|
||
|
- }
|
||
|
-}
|
||
|
-#if LINUX_VERSION_IS_LESS(4,11,0)
|
||
|
-/* Just declare it here to keep sparse happy */
|
||
|
-struct rtnl_link_stats64 *bp_ieee80211_get_stats64(struct net_device *dev,
|
||
|
- struct rtnl_link_stats64 *stats);
|
||
|
-struct rtnl_link_stats64 *
|
||
|
-bp_ieee80211_get_stats64(struct net_device *dev,
|
||
|
- struct rtnl_link_stats64 *stats){
|
||
|
- ieee80211_get_stats64(dev, stats);
|
||
|
- return stats;
|
||
|
-}
|
||
|
-#endif
|
||
|
-
|
||
|
-static const struct net_device_ops ieee80211_dataif_ops = {
|
||
|
- .ndo_open = ieee80211_open,
|
||
|
- .ndo_stop = ieee80211_stop,
|
||
|
- .ndo_uninit = ieee80211_uninit,
|
||
|
- .ndo_start_xmit = ieee80211_subif_start_xmit,
|
||
|
- .ndo_set_rx_mode = ieee80211_set_multicast_list,
|
||
|
- .ndo_set_mac_address = ieee80211_change_mac,
|
||
|
- .ndo_select_queue = ieee80211_netdev_select_queue,
|
||
|
-#if LINUX_VERSION_IS_GEQ(4,11,0)
|
||
|
- .ndo_get_stats64 = ieee80211_get_stats64,
|
||
|
-#else
|
||
|
- .ndo_get_stats64 = bp_ieee80211_get_stats64,
|
||
|
-#endif
|
||
|
-
|
||
|
-};
|
||
|
-
|
||
|
-#if LINUX_VERSION_IS_GEQ(5,2,0)
|
||
|
-static u16 ieee80211_monitor_select_queue(struct net_device *dev,
|
||
|
- struct sk_buff *skb,
|
||
|
- struct net_device *sb_dev)
|
||
|
-#elif LINUX_VERSION_IS_GEQ(4,19,0)
|
||
|
-static u16 ieee80211_monitor_select_queue(struct net_device *dev,
|
||
|
- struct sk_buff *skb,
|
||
|
- struct net_device *sb_dev,
|
||
|
- select_queue_fallback_t fallback)
|
||
|
-#elif LINUX_VERSION_IS_GEQ(3,14,0) || \
|
||
|
- (LINUX_VERSION_CODE == KERNEL_VERSION(3,13,11) && UTS_UBUNTU_RELEASE_ABI > 30)
|
||
|
-static u16 ieee80211_monitor_select_queue(struct net_device *dev,
|
||
|
- struct sk_buff *skb,
|
||
|
- void *accel_priv,
|
||
|
- select_queue_fallback_t fallback)
|
||
|
-#elif LINUX_VERSION_IS_GEQ(3,13,0)
|
||
|
-static u16 ieee80211_monitor_select_queue(struct net_device *dev,
|
||
|
- struct sk_buff *skb,
|
||
|
- void *accel_priv)
|
||
|
-#else
|
||
|
-static u16 ieee80211_monitor_select_queue(struct net_device *dev,
|
||
|
- struct sk_buff *skb)
|
||
|
-#endif
|
||
|
-{
|
||
|
- struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
||
|
- struct ieee80211_local *local = sdata->local;
|
||
|
- struct ieee80211_hdr *hdr;
|
||
|
- struct ieee80211_radiotap_header *rtap = (void *)skb->data;
|
||
|
-
|
||
|
- if (local->hw.queues < IEEE80211_NUM_ACS)
|
||
|
- return 0;
|
||
|
-
|
||
|
- if (skb->len < 4 ||
|
||
|
- skb->len < le16_to_cpu(rtap->it_len) + 2 /* frame control */)
|
||
|
- return 0; /* doesn't matter, frame will be dropped */
|
||
|
-
|
||
|
- hdr = (void *)((u8 *)skb->data + le16_to_cpu(rtap->it_len));
|
||
|
-
|
||
|
- return ieee80211_select_queue_80211(sdata, skb, hdr);
|
||
|
-}
|
||
|
-
|
||
|
-static const struct net_device_ops ieee80211_monitorif_ops = {
|
||
|
- .ndo_open = ieee80211_open,
|
||
|
- .ndo_stop = ieee80211_stop,
|
||
|
- .ndo_uninit = ieee80211_uninit,
|
||
|
- .ndo_start_xmit = ieee80211_monitor_start_xmit,
|
||
|
- .ndo_set_rx_mode = ieee80211_set_multicast_list,
|
||
|
- .ndo_set_mac_address = ieee80211_change_mac,
|
||
|
- .ndo_select_queue = ieee80211_monitor_select_queue,
|
||
|
-#if LINUX_VERSION_IS_GEQ(4,11,0)
|
||
|
- .ndo_get_stats64 = ieee80211_get_stats64,
|
||
|
-#else
|
||
|
- .ndo_get_stats64 = bp_ieee80211_get_stats64,
|
||
|
-#endif
|
||
|
-
|
||
|
-};
|
||
|
-
|
||
|
-static const struct net_device_ops ieee80211_dataif_8023_ops = {
|
||
|
- .ndo_open = ieee80211_open,
|
||
|
- .ndo_stop = ieee80211_stop,
|
||
|
- .ndo_uninit = ieee80211_uninit,
|
||
|
- .ndo_start_xmit = ieee80211_subif_start_xmit_8023,
|
||
|
- .ndo_set_rx_mode = ieee80211_set_multicast_list,
|
||
|
- .ndo_set_mac_address = ieee80211_change_mac,
|
||
|
- .ndo_select_queue = ieee80211_netdev_select_queue,
|
||
|
-#if LINUX_VERSION_IS_GEQ(4,11,0)
|
||
|
- .ndo_get_stats64 = ieee80211_get_stats64,
|
||
|
-#else
|
||
|
- .ndo_get_stats64 = bp_ieee80211_get_stats64,
|
||
|
-#endif
|
||
|
-
|
||
|
-};
|
||
|
-
|
||
|
static void ieee80211_if_free(struct net_device *dev)
|
||
|
{
|
||
|
free_percpu(netdev_tstats(dev));
|
||
|
@@ -1401,32 +1425,6 @@ static void ieee80211_if_setup_no_queue(
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
-static void ieee80211_set_vif_encap_ops(struct ieee80211_sub_if_data *sdata)
|
||
|
-{
|
||
|
- struct ieee80211_local *local = sdata->local;
|
||
|
- struct ieee80211_sub_if_data *bss = sdata;
|
||
|
- bool enabled;
|
||
|
-
|
||
|
- if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
|
||
|
- if (!sdata->bss)
|
||
|
- return;
|
||
|
-
|
||
|
- bss = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
|
||
|
- }
|
||
|
-
|
||
|
- if (!ieee80211_hw_check(&local->hw, SUPPORTS_TX_ENCAP_OFFLOAD) ||
|
||
|
- !ieee80211_iftype_supports_encap_offload(bss->vif.type))
|
||
|
- return;
|
||
|
-
|
||
|
- enabled = bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_ENABLED;
|
||
|
- if (sdata->wdev.use_4addr &&
|
||
|
- !(bss->vif.offload_flags & IEEE80211_OFFLOAD_ENCAP_4ADDR))
|
||
|
- enabled = false;
|
||
|
-
|
||
|
- sdata->dev->netdev_ops = enabled ? &ieee80211_dataif_8023_ops :
|
||
|
- &ieee80211_dataif_ops;
|
||
|
-}
|
||
|
-
|
||
|
static void ieee80211_iface_work(struct work_struct *work)
|
||
|
{
|
||
|
struct ieee80211_sub_if_data *sdata =
|