Search Linux Wireless

[RFC 4/6] wifi: mac80211: support per-radio driver start/stop calls

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Radios are started/stopped based on the vif allowed radios. This allows
drivers to keep unused radios inactive.

Signed-off-by: Felix Fietkau <nbd@xxxxxxxx>
---
 include/net/mac80211.h     | 13 +++++++++++++
 net/mac80211/driver-ops.h  | 32 ++++++++++++++++++++++++++++++++
 net/mac80211/ieee80211_i.h |  2 ++
 net/mac80211/iface.c       | 37 +++++++++++++++++++++++++++++++++++--
 net/mac80211/trace.h       | 10 ++++++++++
 5 files changed, 92 insertions(+), 2 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 7bee2d912efe..d0dcd66e565e 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -3827,6 +3827,17 @@ struct ieee80211_prep_tx_info {
  *	you should ensure to cancel it on this callback.
  *	Must be implemented and can sleep.
  *
+ * @start_radio: Called before the first netdevice attached to the radio
+ *	is enabled. This should turn on the per-radio hardware and must turn
+ *	on frame reception (for possibly enabled monitor interfaces.)
+ *	Returns negative error codes, these may be seen in userspace,
+ *	or zero. It can sleep.
+ *
+ * @stop_radio: Called after last netdevice attached to the radio
+ *	is disabled. This should turn off the per-radio hardware.
+ *	May be called right after add_interface if that rejects
+ *	an interface. It can sleep.
+ *
  * @suspend: Suspend the device; mac80211 itself will quiesce before and
  *	stop transmitting and doing any other configuration, and then
  *	ask the device to suspend. This is only invoked when WoWLAN is
@@ -4440,6 +4451,8 @@ struct ieee80211_ops {
 		   struct sk_buff *skb);
 	int (*start)(struct ieee80211_hw *hw);
 	void (*stop)(struct ieee80211_hw *hw, bool suspend);
+	int (*start_radio)(struct ieee80211_hw *hw, int idx);
+	void (*stop_radio)(struct ieee80211_hw *hw, int idx);
 #ifdef CONFIG_PM
 	int (*suspend)(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
 	int (*resume)(struct ieee80211_hw *hw);
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h
index d382d9729e85..cedc12b98bbb 100644
--- a/net/mac80211/driver-ops.h
+++ b/net/mac80211/driver-ops.h
@@ -90,6 +90,38 @@ static inline int drv_get_et_sset_count(struct ieee80211_sub_if_data *sdata,
 int drv_start(struct ieee80211_local *local);
 void drv_stop(struct ieee80211_local *local, bool suspend);
 
+static inline int drv_start_radio(struct ieee80211_local *local, u32 idx)
+{
+	int ret;
+
+	might_sleep();
+	lockdep_assert_wiphy(local->hw.wiphy);
+
+	if (!local->ops->start_radio || local->radio_data[idx].started)
+	    return 0;
+
+	trace_drv_start_radio(local, idx);
+	ret = local->ops->start_radio(&local->hw, idx);
+	if (!ret)
+		local->radio_data[idx].started = true;
+	trace_drv_return_int(local, ret);
+	return ret;
+}
+
+static inline void drv_stop_radio(struct ieee80211_local *local, u32 idx)
+{
+	might_sleep();
+	lockdep_assert_wiphy(local->hw.wiphy);
+
+	if (!local->ops->stop_radio || !local->radio_data[idx].started)
+	    return;
+
+	trace_drv_stop_radio(local, idx);
+	local->ops->stop_radio(&local->hw, idx);
+	local->radio_data[idx].started = false;
+	trace_drv_return_void(local);
+}
+
 #ifdef CONFIG_PM
 static inline int drv_suspend(struct ieee80211_local *local,
 			      struct cfg80211_wowlan *wowlan)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 3be9f8149117..acc1a2d0f30f 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -1332,6 +1332,8 @@ DECLARE_STATIC_KEY_FALSE(aql_disable);
 
 struct ieee80211_radio_data {
 	u32 monitors;
+	u32 open_count;
+	bool started;
 };
 
 struct ieee80211_local {
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 4db867ae97f0..20a4a19c8ba1 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -578,8 +578,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
 		spin_unlock_irqrestore(&ps->bc_buf.lock, flags);
 	}
 
-	if (going_down)
+	if (going_down) {
+		for (i = 0; i < local->hw.wiphy->n_radio; i++)
+			if (sdata->wdev.radio_mask & BIT(i))
+				local->radio_data[i].open_count--;
 		local->open_count--;
+	}
 
 	switch (sdata->vif.type) {
 	case NL80211_IFTYPE_AP_VLAN:
@@ -714,6 +718,14 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
 	if (cancel_scan)
 		wiphy_delayed_work_flush(local->hw.wiphy, &local->scan_work);
 
+	for (i = 0; i < local->hw.wiphy->n_radio; i++) {
+		if (!(sdata->wdev.radio_mask & BIT(i)) ||
+		    local->radio_data[i].open_count)
+			continue;
+
+		drv_stop_radio(local, i);
+	}
+
 	if (local->open_count == 0) {
 		ieee80211_stop_device(local, false);
 
@@ -1294,6 +1306,16 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
 					   IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
 	}
 
+	for (i = 0; i < local->hw.wiphy->n_radio; i++) {
+		if (!(sdata->wdev.radio_mask & BIT(i)) ||
+		    local->radio_data[i].open_count)
+			continue;
+
+		res = drv_start_radio(local, i);
+		if (res)
+			goto err_stop;
+	}
+
 	/*
 	 * Copy the hopefully now-present MAC address to
 	 * this interface, if it has the special null one.
@@ -1444,8 +1466,15 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
 	if (sdata->flags & IEEE80211_SDATA_ALLMULTI)
 		atomic_inc(&local->iff_allmultis);
 
-	if (coming_up)
+	if (coming_up) {
 		local->open_count++;
+		for (i = 0; i < local->hw.wiphy->n_radio; i++) {
+			if (!(sdata->wdev.radio_mask & BIT(i)))
+				continue;
+
+			local->radio_data[i].open_count++;
+		}
+	}
 
 	if (local->open_count == 1)
 		ieee80211_hw_conf_init(local);
@@ -1462,6 +1491,10 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
  err_stop:
 	if (!local->open_count)
 		drv_stop(local, false);
+	for (i = 0; i < local->hw.wiphy->n_radio; i++)
+		if ((sdata->wdev.radio_mask & BIT(i)) &&
+		    !local->radio_data[i].open_count)
+			drv_stop_radio(local, i);
  err_del_bss:
 	sdata->bss = NULL;
 	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h
index dc498cd8cd91..0b6b4ebc681a 100644
--- a/net/mac80211/trace.h
+++ b/net/mac80211/trace.h
@@ -342,6 +342,16 @@ TRACE_EVENT(drv_stop,
 	TP_printk(LOCAL_PR_FMT " suspend:%d", LOCAL_PR_ARG, __entry->suspend)
 );
 
+DEFINE_EVENT(local_u32_evt, drv_start_radio,
+	     TP_PROTO(struct ieee80211_local *local, u32 idx),
+	     TP_ARGS(local, idx)
+);
+
+DEFINE_EVENT(local_u32_evt, drv_stop_radio,
+	     TP_PROTO(struct ieee80211_local *local, u32 idx),
+	     TP_ARGS(local, idx)
+);
+
 DEFINE_EVENT(local_sdata_addr_evt, drv_add_interface,
 	TP_PROTO(struct ieee80211_local *local,
 		 struct ieee80211_sub_if_data *sdata),
-- 
git-series 0.9.1




[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Wireless Personal Area Network]     [Linux Bluetooth]     [Wireless Regulations]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite Hiking]     [MIPS Linux]     [ARM Linux]     [Linux RAID]

  Powered by Linux