mac80211: generalise work handling

In order to use auth/assoc for different purposes
other than MLME, it needs to be split up. For other
purposes, a generic work handling (potentially on
another channel) will be useful.

To achieve that, this patch moves much of the MLME
work handling out of mlme into a new work API. The
API can currently handle probing a specific AP,
authentication and association. The MLME previously
handled probe/authentication as one step and will
continue to do so, but they are separate in the new
work handling.

Work items are RCU-managed to be able to check for
existence of an item for a specific frame in the RX
path, but they can be re-used which the MLME right
now will do for its combined probe/auth step.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 0339e90..97b6076 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -228,41 +228,63 @@
 };
 
 enum ieee80211_work_type {
-	IEEE80211_WORK_AUTH_PROBE,
+	IEEE80211_WORK_DIRECT_PROBE,
 	IEEE80211_WORK_AUTH,
 	IEEE80211_WORK_ASSOC,
 };
 
+/**
+ * enum work_done_result - indicates what to do after work was done
+ *
+ * @WORK_DONE_DESTROY: This work item is no longer needed, destroy.
+ * @WORK_DONE_REQUEUE: This work item was reset to be reused, and
+ *	should be requeued.
+ */
+enum work_done_result {
+	WORK_DONE_DESTROY,
+	WORK_DONE_REQUEUE,
+};
+
 struct ieee80211_work {
 	struct list_head list;
 
+	struct rcu_head rcu_head;
+
+	struct ieee80211_sub_if_data *sdata;
+
+	enum work_done_result (*done)(struct ieee80211_work *wk,
+				      struct sk_buff *skb);
+
 	struct ieee80211_channel *chan;
 	/* XXX: chan type? -- right now not really needed */
+
 	unsigned long timeout;
 	enum ieee80211_work_type type;
 
+	u8 filter_ta[ETH_ALEN];
+
 	union {
 		struct {
 			int tries;
 			u16 algorithm, transaction;
 			u8 ssid[IEEE80211_MAX_SSID_LEN];
 			u8 ssid_len;
-			u8 bssid[ETH_ALEN];
 			u8 key[WLAN_KEY_LEN_WEP104];
 			u8 key_len, key_idx;
 			bool privacy;
-		} auth;
+		} probe_auth;
 		struct {
 			struct ieee80211_bss *bss;
 			const u8 *supp_rates;
 			const u8 *ht_information_ie;
+			enum ieee80211_smps_mode smps;
 			int tries;
 			u16 capability;
-			u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
+			u8 prev_bssid[ETH_ALEN];
 			u8 ssid[IEEE80211_MAX_SSID_LEN];
 			u8 ssid_len;
 			u8 supp_rates_len;
-			bool wmm_used;
+			bool wmm_used, use_11n;
 		} assoc;
 	};
 
@@ -276,17 +298,11 @@
 	IEEE80211_STA_BEACON_POLL	= BIT(0),
 	IEEE80211_STA_CONNECTION_POLL	= BIT(1),
 	IEEE80211_STA_CONTROL_PORT	= BIT(2),
-	IEEE80211_STA_WMM_ENABLED	= BIT(3),
 	IEEE80211_STA_DISABLE_11N	= BIT(4),
 	IEEE80211_STA_CSA_RECEIVED	= BIT(5),
 	IEEE80211_STA_MFP_ENABLED	= BIT(6),
 };
 
-/* flags for MLME request */
-enum ieee80211_sta_request {
-	IEEE80211_STA_REQ_SCAN,
-};
-
 struct ieee80211_if_managed {
 	struct timer_list timer;
 	struct timer_list conn_mon_timer;
@@ -302,12 +318,10 @@
 
 	struct mutex mtx;
 	struct ieee80211_bss *associated;
-	struct list_head work_list;
 
 	u8 bssid[ETH_ALEN];
 
 	u16 aid;
-	u16 capab;
 
 	struct sk_buff_head skb_queue;
 
@@ -316,8 +330,6 @@
 	enum ieee80211_smps_mode req_smps, /* requested smps mode */
 				 ap_smps; /* smps mode AP thinks we're in */
 
-	unsigned long request;
-
 	unsigned int flags;
 
 	u32 beacon_crc;
@@ -584,6 +596,15 @@
 	const struct ieee80211_ops *ops;
 
 	/*
+	 * work stuff, potentially off-channel (in the future)
+	 */
+	struct mutex work_mtx;
+	struct list_head work_list;
+	struct timer_list work_timer;
+	struct work_struct work_work;
+	struct sk_buff_head work_skb_queue;
+
+	/*
 	 * private workqueue to mac80211. mac80211 makes this accessible
 	 * via ieee80211_queue_work()
 	 */
@@ -1127,6 +1148,14 @@
 void ieee80211_recalc_smps(struct ieee80211_local *local,
 			   struct ieee80211_sub_if_data *forsdata);
 
+/* internal work items */
+void ieee80211_work_init(struct ieee80211_local *local);
+void ieee80211_add_work(struct ieee80211_work *wk);
+void free_work(struct ieee80211_work *wk);
+void ieee80211_work_purge(struct ieee80211_sub_if_data *sdata);
+ieee80211_rx_result ieee80211_work_rx_mgmt(struct ieee80211_sub_if_data *sdata,
+					   struct sk_buff *skb);
+
 #ifdef CONFIG_MAC80211_NOINLINE
 #define debug_noinline noinline
 #else