Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f09603a2 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville
Browse files

mac80211: add sta_state callback



(based on Eliad's patch)

Add a callback to notify the low-level driver whenever
the state of a station changes. The driver is only
notified when the station is actually in the mac80211
hash table, not for pre-insert state transitions.

To allow the driver to replace sta_add/remove calls
with this, call extra transitions with the NOTEXIST
state.

This callback can fail, so we need to be careful in
handling it when a station is inserted, particularly
in the IBSS case where we still keep the station entry
around for mac80211 purposes.

Signed-off-by: default avatarEliad Peller <eliad@wizery.com>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 71ec375c
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -981,6 +981,25 @@ enum set_key_cmd {
	SET_KEY, DISABLE_KEY,
};

/**
 * enum ieee80211_sta_state - station state
 *
 * @IEEE80211_STA_NOTEXIST: station doesn't exist at all,
 *	this is a special state for add/remove transitions
 * @IEEE80211_STA_NONE: station exists without special state
 * @IEEE80211_STA_AUTH: station is authenticated
 * @IEEE80211_STA_ASSOC: station is associated
 * @IEEE80211_STA_AUTHORIZED: station is authorized (802.1X)
 */
enum ieee80211_sta_state {
	/* NOTE: These need to be ordered correctly! */
	IEEE80211_STA_NOTEXIST,
	IEEE80211_STA_NONE,
	IEEE80211_STA_AUTH,
	IEEE80211_STA_ASSOC,
	IEEE80211_STA_AUTHORIZED,
};

/**
 * struct ieee80211_sta - station table entry
 *
@@ -1974,6 +1993,13 @@ enum ieee80211_frame_release_type {
 *	in AP mode, this callback will not be called when the flag
 *	%IEEE80211_HW_AP_LINK_PS is set. Must be atomic.
 *
 * @sta_state: Notifies low level driver about state transition of a
 *	station (which can be the AP, a client, IBSS/WDS/mesh peer etc.)
 *	This callback is mutually exclusive with @sta_add/@sta_remove.
 *	It must not fail for down transitions but may fail for transitions
 *	up the list of states.
 *	The callback can sleep.
 *
 * @conf_tx: Configure TX queue parameters (EDCF (aifs, cw_min, cw_max),
 *	bursting) for a hardware TX queue.
 *	Returns a negative error code on failure.
@@ -2193,6 +2219,10 @@ struct ieee80211_ops {
			  struct ieee80211_sta *sta);
	void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
			enum sta_notify_cmd, struct ieee80211_sta *sta);
	int (*sta_state)(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
			 struct ieee80211_sta *sta,
			 enum ieee80211_sta_state old_state,
			 enum ieee80211_sta_state new_state);
	int (*conf_tx)(struct ieee80211_hw *hw,
		       struct ieee80211_vif *vif, u16 queue,
		       const struct ieee80211_tx_queue_params *params);
+22 −0
Original line number Diff line number Diff line
@@ -478,6 +478,28 @@ static inline void drv_sta_remove(struct ieee80211_local *local,
	trace_drv_return_void(local);
}

static inline __must_check
int drv_sta_state(struct ieee80211_local *local,
		  struct ieee80211_sub_if_data *sdata,
		  struct sta_info *sta,
		  enum ieee80211_sta_state old_state,
		  enum ieee80211_sta_state new_state)
{
	int ret = 0;

	might_sleep();

	sdata = get_bss_sdata(sdata);
	check_sdata_in_driver(sdata);

	trace_drv_sta_state(local, sdata, &sta->sta, old_state, new_state);
	if (local->ops->sta_state)
		ret = local->ops->sta_state(&local->hw, &sdata->vif, &sta->sta,
					    old_state, new_state);
	trace_drv_return_int(local, ret);
	return ret;
}

static inline int drv_conf_tx(struct ieee80211_local *local,
			      struct ieee80211_sub_if_data *sdata, u16 queue,
			      const struct ieee80211_tx_queue_params *params)
+32 −0
Original line number Diff line number Diff line
@@ -635,6 +635,38 @@ TRACE_EVENT(drv_sta_notify,
	)
);

TRACE_EVENT(drv_sta_state,
	TP_PROTO(struct ieee80211_local *local,
		 struct ieee80211_sub_if_data *sdata,
		 struct ieee80211_sta *sta,
		 enum ieee80211_sta_state old_state,
		 enum ieee80211_sta_state new_state),

	TP_ARGS(local, sdata, sta, old_state, new_state),

	TP_STRUCT__entry(
		LOCAL_ENTRY
		VIF_ENTRY
		STA_ENTRY
		__field(u32, old_state)
		__field(u32, new_state)
	),

	TP_fast_assign(
		LOCAL_ASSIGN;
		VIF_ASSIGN;
		STA_ASSIGN;
		__entry->old_state = old_state;
		__entry->new_state = new_state;
	),

	TP_printk(
		LOCAL_PR_FMT  VIF_PR_FMT  STA_PR_FMT " state: %d->%d",
		LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG,
		__entry->old_state, __entry->new_state
	)
);

TRACE_EVENT(drv_sta_add,
	TP_PROTO(struct ieee80211_local *local,
		 struct ieee80211_sub_if_data *sdata,
+3 −0
Original line number Diff line number Diff line
@@ -535,6 +535,9 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
	int priv_size, i;
	struct wiphy *wiphy;

	if (WARN_ON(ops->sta_state && (ops->sta_add || ops->sta_remove)))
		return NULL;

	/* Ensure 32-byte alignment of our private data and hw private data.
	 * We use the wiphy priv data for both our ieee80211_local and for
	 * the driver's private data
+9 −1
Original line number Diff line number Diff line
@@ -97,9 +97,17 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
	/* tear down aggregation sessions and remove STAs */
	mutex_lock(&local->sta_mtx);
	list_for_each_entry(sta, &local->sta_list, list) {
		if (sta->uploaded)
		if (sta->uploaded) {
			enum ieee80211_sta_state state;

			drv_sta_remove(local, sta->sdata, &sta->sta);

			state = sta->sta_state;
			for (; state > IEEE80211_STA_NOTEXIST; state--)
				WARN_ON(drv_sta_state(local, sdata, sta,
						      state, state - 1));
		}

		mesh_plink_quiesce(sta);
	}
	mutex_unlock(&local->sta_mtx);
Loading