xref: /linux/drivers/net/wireless/st/cw1200/sta.c (revision 073350da0aa2aead9df7927a1c1046ebf5cdd816)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a910e4a9SSolomon Peachy /*
3a910e4a9SSolomon Peachy  * Mac80211 STA API for ST-Ericsson CW1200 drivers
4a910e4a9SSolomon Peachy  *
5a910e4a9SSolomon Peachy  * Copyright (c) 2010, ST-Ericsson
6a910e4a9SSolomon Peachy  * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
7a910e4a9SSolomon Peachy  */
8a910e4a9SSolomon Peachy 
9a910e4a9SSolomon Peachy #include <linux/vmalloc.h>
10a910e4a9SSolomon Peachy #include <linux/sched.h>
11a910e4a9SSolomon Peachy #include <linux/firmware.h>
12a910e4a9SSolomon Peachy #include <linux/module.h>
1335df5388Sdingtianhong #include <linux/etherdevice.h>
14a910e4a9SSolomon Peachy 
15a910e4a9SSolomon Peachy #include "cw1200.h"
16a910e4a9SSolomon Peachy #include "sta.h"
17a910e4a9SSolomon Peachy #include "fwio.h"
18a910e4a9SSolomon Peachy #include "bh.h"
19a910e4a9SSolomon Peachy #include "debug.h"
20a910e4a9SSolomon Peachy 
21a910e4a9SSolomon Peachy #ifndef ERP_INFO_BYTE_OFFSET
22a910e4a9SSolomon Peachy #define ERP_INFO_BYTE_OFFSET 2
23a910e4a9SSolomon Peachy #endif
24a910e4a9SSolomon Peachy 
25a910e4a9SSolomon Peachy static void cw1200_do_join(struct cw1200_common *priv);
26a910e4a9SSolomon Peachy static void cw1200_do_unjoin(struct cw1200_common *priv);
27a910e4a9SSolomon Peachy 
28a910e4a9SSolomon Peachy static int cw1200_upload_beacon(struct cw1200_common *priv);
29a910e4a9SSolomon Peachy static int cw1200_upload_pspoll(struct cw1200_common *priv);
30a910e4a9SSolomon Peachy static int cw1200_upload_null(struct cw1200_common *priv);
31a910e4a9SSolomon Peachy static int cw1200_upload_qosnull(struct cw1200_common *priv);
32a910e4a9SSolomon Peachy static int cw1200_start_ap(struct cw1200_common *priv);
33a910e4a9SSolomon Peachy static int cw1200_update_beaconing(struct cw1200_common *priv);
34a910e4a9SSolomon Peachy static int cw1200_enable_beaconing(struct cw1200_common *priv,
35a910e4a9SSolomon Peachy 				   bool enable);
36a910e4a9SSolomon Peachy static void __cw1200_sta_notify(struct ieee80211_hw *dev,
37a910e4a9SSolomon Peachy 				struct ieee80211_vif *vif,
38a910e4a9SSolomon Peachy 				enum sta_notify_cmd notify_cmd,
39a910e4a9SSolomon Peachy 				int link_id);
40a910e4a9SSolomon Peachy static int __cw1200_flush(struct cw1200_common *priv, bool drop);
41a910e4a9SSolomon Peachy 
42a910e4a9SSolomon Peachy static inline void __cw1200_free_event_queue(struct list_head *list)
43a910e4a9SSolomon Peachy {
44a910e4a9SSolomon Peachy 	struct cw1200_wsm_event *event, *tmp;
45a910e4a9SSolomon Peachy 	list_for_each_entry_safe(event, tmp, list, link) {
46a910e4a9SSolomon Peachy 		list_del(&event->link);
47a910e4a9SSolomon Peachy 		kfree(event);
48a910e4a9SSolomon Peachy 	}
49a910e4a9SSolomon Peachy }
50a910e4a9SSolomon Peachy 
51a910e4a9SSolomon Peachy /* ******************************************************************** */
52a910e4a9SSolomon Peachy /* STA API								*/
53a910e4a9SSolomon Peachy 
54a910e4a9SSolomon Peachy int cw1200_start(struct ieee80211_hw *dev)
55a910e4a9SSolomon Peachy {
56a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
57a910e4a9SSolomon Peachy 	int ret = 0;
58a910e4a9SSolomon Peachy 
59a910e4a9SSolomon Peachy 	cw1200_pm_stay_awake(&priv->pm_state, HZ);
60a910e4a9SSolomon Peachy 
61a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
62a910e4a9SSolomon Peachy 
63a910e4a9SSolomon Peachy 	/* default EDCA */
64a910e4a9SSolomon Peachy 	WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, 47, 0xc8, false);
65a910e4a9SSolomon Peachy 	WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, 94, 0xc8, false);
66a910e4a9SSolomon Peachy 	WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, 0, 0xc8, false);
67a910e4a9SSolomon Peachy 	WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, 0, 0xc8, false);
68a910e4a9SSolomon Peachy 	ret = wsm_set_edca_params(priv, &priv->edca);
69a910e4a9SSolomon Peachy 	if (ret)
70a910e4a9SSolomon Peachy 		goto out;
71a910e4a9SSolomon Peachy 
72a910e4a9SSolomon Peachy 	ret = cw1200_set_uapsd_param(priv, &priv->edca);
73a910e4a9SSolomon Peachy 	if (ret)
74a910e4a9SSolomon Peachy 		goto out;
75a910e4a9SSolomon Peachy 
76a910e4a9SSolomon Peachy 	priv->setbssparams_done = false;
77a910e4a9SSolomon Peachy 
78a910e4a9SSolomon Peachy 	memcpy(priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN);
79a910e4a9SSolomon Peachy 	priv->mode = NL80211_IFTYPE_MONITOR;
80a910e4a9SSolomon Peachy 	priv->wep_default_key_id = -1;
81a910e4a9SSolomon Peachy 
82a910e4a9SSolomon Peachy 	priv->cqm_beacon_loss_count = 10;
83a910e4a9SSolomon Peachy 
84a910e4a9SSolomon Peachy 	ret = cw1200_setup_mac(priv);
85a910e4a9SSolomon Peachy 	if (ret)
86a910e4a9SSolomon Peachy 		goto out;
87a910e4a9SSolomon Peachy 
88a910e4a9SSolomon Peachy out:
89a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
90a910e4a9SSolomon Peachy 	return ret;
91a910e4a9SSolomon Peachy }
92a910e4a9SSolomon Peachy 
93a910e4a9SSolomon Peachy void cw1200_stop(struct ieee80211_hw *dev)
94a910e4a9SSolomon Peachy {
95a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
96a910e4a9SSolomon Peachy 	LIST_HEAD(list);
97a910e4a9SSolomon Peachy 	int i;
98a910e4a9SSolomon Peachy 
99a910e4a9SSolomon Peachy 	wsm_lock_tx(priv);
100a910e4a9SSolomon Peachy 
101a910e4a9SSolomon Peachy 	while (down_trylock(&priv->scan.lock)) {
102a910e4a9SSolomon Peachy 		/* Scan is in progress. Force it to stop. */
103a910e4a9SSolomon Peachy 		priv->scan.req = NULL;
104a910e4a9SSolomon Peachy 		schedule();
105a910e4a9SSolomon Peachy 	}
106a910e4a9SSolomon Peachy 	up(&priv->scan.lock);
107a910e4a9SSolomon Peachy 
108a910e4a9SSolomon Peachy 	cancel_delayed_work_sync(&priv->scan.probe_work);
109a910e4a9SSolomon Peachy 	cancel_delayed_work_sync(&priv->scan.timeout);
110a910e4a9SSolomon Peachy 	cancel_delayed_work_sync(&priv->clear_recent_scan_work);
111a910e4a9SSolomon Peachy 	cancel_delayed_work_sync(&priv->join_timeout);
112a910e4a9SSolomon Peachy 	cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
113a910e4a9SSolomon Peachy 	cancel_work_sync(&priv->unjoin_work);
114a910e4a9SSolomon Peachy 	cancel_delayed_work_sync(&priv->link_id_gc_work);
115a910e4a9SSolomon Peachy 	flush_workqueue(priv->workqueue);
116a910e4a9SSolomon Peachy 	del_timer_sync(&priv->mcast_timeout);
117a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
118a910e4a9SSolomon Peachy 	priv->mode = NL80211_IFTYPE_UNSPECIFIED;
119a910e4a9SSolomon Peachy 	priv->listening = false;
120a910e4a9SSolomon Peachy 
121a910e4a9SSolomon Peachy 	spin_lock(&priv->event_queue_lock);
122a910e4a9SSolomon Peachy 	list_splice_init(&priv->event_queue, &list);
123a910e4a9SSolomon Peachy 	spin_unlock(&priv->event_queue_lock);
124a910e4a9SSolomon Peachy 	__cw1200_free_event_queue(&list);
125a910e4a9SSolomon Peachy 
126a910e4a9SSolomon Peachy 
127a910e4a9SSolomon Peachy 	priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
128a910e4a9SSolomon Peachy 	priv->join_pending = false;
129a910e4a9SSolomon Peachy 
130a910e4a9SSolomon Peachy 	for (i = 0; i < 4; i++)
131a910e4a9SSolomon Peachy 		cw1200_queue_clear(&priv->tx_queue[i]);
132a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
133a910e4a9SSolomon Peachy 	tx_policy_clean(priv);
134a910e4a9SSolomon Peachy 
135a910e4a9SSolomon Peachy 	/* HACK! */
136a910e4a9SSolomon Peachy 	if (atomic_xchg(&priv->tx_lock, 1) != 1)
137a910e4a9SSolomon Peachy 		pr_debug("[STA] TX is force-unlocked due to stop request.\n");
138a910e4a9SSolomon Peachy 
139a910e4a9SSolomon Peachy 	wsm_unlock_tx(priv);
140a910e4a9SSolomon Peachy 	atomic_xchg(&priv->tx_lock, 0); /* for recovery to work */
141a910e4a9SSolomon Peachy }
142a910e4a9SSolomon Peachy 
143a910e4a9SSolomon Peachy static int cw1200_bssloss_mitigation = 1;
144a910e4a9SSolomon Peachy module_param(cw1200_bssloss_mitigation, int, 0644);
145a910e4a9SSolomon Peachy MODULE_PARM_DESC(cw1200_bssloss_mitigation, "BSS Loss mitigation. 0 == disabled, 1 == enabled (default)");
146a910e4a9SSolomon Peachy 
147a910e4a9SSolomon Peachy 
148a910e4a9SSolomon Peachy void __cw1200_cqm_bssloss_sm(struct cw1200_common *priv,
149a910e4a9SSolomon Peachy 			     int init, int good, int bad)
150a910e4a9SSolomon Peachy {
151a910e4a9SSolomon Peachy 	int tx = 0;
152a910e4a9SSolomon Peachy 
153a910e4a9SSolomon Peachy 	priv->delayed_link_loss = 0;
154a910e4a9SSolomon Peachy 	cancel_work_sync(&priv->bss_params_work);
155a910e4a9SSolomon Peachy 
156a910e4a9SSolomon Peachy 	pr_debug("[STA] CQM BSSLOSS_SM: state: %d init %d good %d bad: %d txlock: %d uj: %d\n",
157a910e4a9SSolomon Peachy 		 priv->bss_loss_state,
158a910e4a9SSolomon Peachy 		 init, good, bad,
159a910e4a9SSolomon Peachy 		 atomic_read(&priv->tx_lock),
160a910e4a9SSolomon Peachy 		 priv->delayed_unjoin);
161a910e4a9SSolomon Peachy 
162a910e4a9SSolomon Peachy 	/* If we have a pending unjoin */
163a910e4a9SSolomon Peachy 	if (priv->delayed_unjoin)
164a910e4a9SSolomon Peachy 		return;
165a910e4a9SSolomon Peachy 
166a910e4a9SSolomon Peachy 	if (init) {
167a910e4a9SSolomon Peachy 		queue_delayed_work(priv->workqueue,
168a910e4a9SSolomon Peachy 				   &priv->bss_loss_work,
169a910e4a9SSolomon Peachy 				   HZ);
170a910e4a9SSolomon Peachy 		priv->bss_loss_state = 0;
171a910e4a9SSolomon Peachy 
172a910e4a9SSolomon Peachy 		/* Skip the confimration procedure in P2P case */
173a910e4a9SSolomon Peachy 		if (!priv->vif->p2p && !atomic_read(&priv->tx_lock))
174a910e4a9SSolomon Peachy 			tx = 1;
175a910e4a9SSolomon Peachy 	} else if (good) {
176a910e4a9SSolomon Peachy 		cancel_delayed_work_sync(&priv->bss_loss_work);
177a910e4a9SSolomon Peachy 		priv->bss_loss_state = 0;
178a910e4a9SSolomon Peachy 		queue_work(priv->workqueue, &priv->bss_params_work);
179a910e4a9SSolomon Peachy 	} else if (bad) {
180a910e4a9SSolomon Peachy 		/* XXX Should we just keep going until we time out? */
181a910e4a9SSolomon Peachy 		if (priv->bss_loss_state < 3)
182a910e4a9SSolomon Peachy 			tx = 1;
183a910e4a9SSolomon Peachy 	} else {
184a910e4a9SSolomon Peachy 		cancel_delayed_work_sync(&priv->bss_loss_work);
185a910e4a9SSolomon Peachy 		priv->bss_loss_state = 0;
186a910e4a9SSolomon Peachy 	}
187a910e4a9SSolomon Peachy 
188a910e4a9SSolomon Peachy 	/* Bypass mitigation if it's disabled */
189a910e4a9SSolomon Peachy 	if (!cw1200_bssloss_mitigation)
190a910e4a9SSolomon Peachy 		tx = 0;
191a910e4a9SSolomon Peachy 
192a910e4a9SSolomon Peachy 	/* Spit out a NULL packet to our AP if necessary */
193a910e4a9SSolomon Peachy 	if (tx) {
194a910e4a9SSolomon Peachy 		struct sk_buff *skb;
195a910e4a9SSolomon Peachy 
196a910e4a9SSolomon Peachy 		priv->bss_loss_state++;
197a910e4a9SSolomon Peachy 
1987b6ddeafSJohannes Berg 		skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false);
199a910e4a9SSolomon Peachy 		WARN_ON(!skb);
200a910e4a9SSolomon Peachy 		if (skb)
201a910e4a9SSolomon Peachy 			cw1200_tx(priv->hw, NULL, skb);
202a910e4a9SSolomon Peachy 	}
203a910e4a9SSolomon Peachy }
204a910e4a9SSolomon Peachy 
205a910e4a9SSolomon Peachy int cw1200_add_interface(struct ieee80211_hw *dev,
206a910e4a9SSolomon Peachy 			 struct ieee80211_vif *vif)
207a910e4a9SSolomon Peachy {
208a910e4a9SSolomon Peachy 	int ret;
209a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
210a910e4a9SSolomon Peachy 	/* __le32 auto_calibration_mode = __cpu_to_le32(1); */
211a910e4a9SSolomon Peachy 
212a910e4a9SSolomon Peachy 	vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
213848955ccSJohannes Berg 			     IEEE80211_VIF_SUPPORTS_UAPSD |
214a910e4a9SSolomon Peachy 			     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
215a910e4a9SSolomon Peachy 
216a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
217a910e4a9SSolomon Peachy 
218a910e4a9SSolomon Peachy 	if (priv->mode != NL80211_IFTYPE_MONITOR) {
219a910e4a9SSolomon Peachy 		mutex_unlock(&priv->conf_mutex);
220a910e4a9SSolomon Peachy 		return -EOPNOTSUPP;
221a910e4a9SSolomon Peachy 	}
222a910e4a9SSolomon Peachy 
223a910e4a9SSolomon Peachy 	switch (vif->type) {
224a910e4a9SSolomon Peachy 	case NL80211_IFTYPE_STATION:
225a910e4a9SSolomon Peachy 	case NL80211_IFTYPE_ADHOC:
226a910e4a9SSolomon Peachy 	case NL80211_IFTYPE_MESH_POINT:
227a910e4a9SSolomon Peachy 	case NL80211_IFTYPE_AP:
228a910e4a9SSolomon Peachy 		priv->mode = vif->type;
229a910e4a9SSolomon Peachy 		break;
230a910e4a9SSolomon Peachy 	default:
231a910e4a9SSolomon Peachy 		mutex_unlock(&priv->conf_mutex);
232a910e4a9SSolomon Peachy 		return -EOPNOTSUPP;
233a910e4a9SSolomon Peachy 	}
234a910e4a9SSolomon Peachy 
235a910e4a9SSolomon Peachy 	priv->vif = vif;
236a910e4a9SSolomon Peachy 	memcpy(priv->mac_addr, vif->addr, ETH_ALEN);
237a910e4a9SSolomon Peachy 	ret = cw1200_setup_mac(priv);
238a910e4a9SSolomon Peachy 	/* Enable auto-calibration */
239a910e4a9SSolomon Peachy 	/* Exception in subsequent channel switch; disabled.
240a910e4a9SSolomon Peachy 	 *  wsm_write_mib(priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE,
241a910e4a9SSolomon Peachy 	 *      &auto_calibration_mode, sizeof(auto_calibration_mode));
242a910e4a9SSolomon Peachy 	*/
243a910e4a9SSolomon Peachy 
244a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
245a910e4a9SSolomon Peachy 	return ret;
246a910e4a9SSolomon Peachy }
247a910e4a9SSolomon Peachy 
248a910e4a9SSolomon Peachy void cw1200_remove_interface(struct ieee80211_hw *dev,
249a910e4a9SSolomon Peachy 			     struct ieee80211_vif *vif)
250a910e4a9SSolomon Peachy {
251a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
252a910e4a9SSolomon Peachy 	struct wsm_reset reset = {
253a910e4a9SSolomon Peachy 		.reset_statistics = true,
254a910e4a9SSolomon Peachy 	};
255a910e4a9SSolomon Peachy 	int i;
256a910e4a9SSolomon Peachy 
257a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
258a910e4a9SSolomon Peachy 	switch (priv->join_status) {
259a910e4a9SSolomon Peachy 	case CW1200_JOIN_STATUS_JOINING:
260a910e4a9SSolomon Peachy 	case CW1200_JOIN_STATUS_PRE_STA:
261a910e4a9SSolomon Peachy 	case CW1200_JOIN_STATUS_STA:
262a910e4a9SSolomon Peachy 	case CW1200_JOIN_STATUS_IBSS:
263a910e4a9SSolomon Peachy 		wsm_lock_tx(priv);
264a910e4a9SSolomon Peachy 		if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
265a910e4a9SSolomon Peachy 			wsm_unlock_tx(priv);
266a910e4a9SSolomon Peachy 		break;
267a910e4a9SSolomon Peachy 	case CW1200_JOIN_STATUS_AP:
268a910e4a9SSolomon Peachy 		for (i = 0; priv->link_id_map; ++i) {
269a910e4a9SSolomon Peachy 			if (priv->link_id_map & BIT(i)) {
270a910e4a9SSolomon Peachy 				reset.link_id = i;
271a910e4a9SSolomon Peachy 				wsm_reset(priv, &reset);
272a910e4a9SSolomon Peachy 				priv->link_id_map &= ~BIT(i);
273a910e4a9SSolomon Peachy 			}
274a910e4a9SSolomon Peachy 		}
275a910e4a9SSolomon Peachy 		memset(priv->link_id_db, 0, sizeof(priv->link_id_db));
276a910e4a9SSolomon Peachy 		priv->sta_asleep_mask = 0;
277a910e4a9SSolomon Peachy 		priv->enable_beacon = false;
278a910e4a9SSolomon Peachy 		priv->tx_multicast = false;
279a910e4a9SSolomon Peachy 		priv->aid0_bit_set = false;
280a910e4a9SSolomon Peachy 		priv->buffered_multicasts = false;
281a910e4a9SSolomon Peachy 		priv->pspoll_mask = 0;
282a910e4a9SSolomon Peachy 		reset.link_id = 0;
283a910e4a9SSolomon Peachy 		wsm_reset(priv, &reset);
284a910e4a9SSolomon Peachy 		break;
285a910e4a9SSolomon Peachy 	case CW1200_JOIN_STATUS_MONITOR:
286a910e4a9SSolomon Peachy 		cw1200_update_listening(priv, false);
287a910e4a9SSolomon Peachy 		break;
288a910e4a9SSolomon Peachy 	default:
289a910e4a9SSolomon Peachy 		break;
290a910e4a9SSolomon Peachy 	}
291a910e4a9SSolomon Peachy 	priv->vif = NULL;
292a910e4a9SSolomon Peachy 	priv->mode = NL80211_IFTYPE_MONITOR;
29393803b33SJoe Perches 	eth_zero_addr(priv->mac_addr);
294a910e4a9SSolomon Peachy 	memset(&priv->p2p_ps_modeinfo, 0, sizeof(priv->p2p_ps_modeinfo));
295a910e4a9SSolomon Peachy 	cw1200_free_keys(priv);
296a910e4a9SSolomon Peachy 	cw1200_setup_mac(priv);
297a910e4a9SSolomon Peachy 	priv->listening = false;
298a910e4a9SSolomon Peachy 	priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
299a910e4a9SSolomon Peachy 	if (!__cw1200_flush(priv, true))
300a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
301a910e4a9SSolomon Peachy 
302a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
303a910e4a9SSolomon Peachy }
304a910e4a9SSolomon Peachy 
305a910e4a9SSolomon Peachy int cw1200_change_interface(struct ieee80211_hw *dev,
306a910e4a9SSolomon Peachy 			    struct ieee80211_vif *vif,
307a910e4a9SSolomon Peachy 			    enum nl80211_iftype new_type,
308a910e4a9SSolomon Peachy 			    bool p2p)
309a910e4a9SSolomon Peachy {
310a910e4a9SSolomon Peachy 	int ret = 0;
311a910e4a9SSolomon Peachy 	pr_debug("change_interface new: %d (%d), old: %d (%d)\n", new_type,
312a910e4a9SSolomon Peachy 		 p2p, vif->type, vif->p2p);
313a910e4a9SSolomon Peachy 
314a910e4a9SSolomon Peachy 	if (new_type != vif->type || vif->p2p != p2p) {
315a910e4a9SSolomon Peachy 		cw1200_remove_interface(dev, vif);
316a910e4a9SSolomon Peachy 		vif->type = new_type;
317a910e4a9SSolomon Peachy 		vif->p2p = p2p;
318a910e4a9SSolomon Peachy 		ret = cw1200_add_interface(dev, vif);
319a910e4a9SSolomon Peachy 	}
320a910e4a9SSolomon Peachy 
321a910e4a9SSolomon Peachy 	return ret;
322a910e4a9SSolomon Peachy }
323a910e4a9SSolomon Peachy 
324a910e4a9SSolomon Peachy int cw1200_config(struct ieee80211_hw *dev, u32 changed)
325a910e4a9SSolomon Peachy {
326a910e4a9SSolomon Peachy 	int ret = 0;
327a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
328a910e4a9SSolomon Peachy 	struct ieee80211_conf *conf = &dev->conf;
329a910e4a9SSolomon Peachy 
330a910e4a9SSolomon Peachy 	pr_debug("CONFIG CHANGED:  %08x\n", changed);
331a910e4a9SSolomon Peachy 
332a910e4a9SSolomon Peachy 	down(&priv->scan.lock);
333a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
334a910e4a9SSolomon Peachy 	/* TODO: IEEE80211_CONF_CHANGE_QOS */
335a910e4a9SSolomon Peachy 	/* TODO: IEEE80211_CONF_CHANGE_LISTEN_INTERVAL */
336a910e4a9SSolomon Peachy 
337a910e4a9SSolomon Peachy 	if (changed & IEEE80211_CONF_CHANGE_POWER) {
338a910e4a9SSolomon Peachy 		priv->output_power = conf->power_level;
339a910e4a9SSolomon Peachy 		pr_debug("[STA] TX power: %d\n", priv->output_power);
340a910e4a9SSolomon Peachy 		wsm_set_output_power(priv, priv->output_power * 10);
341a910e4a9SSolomon Peachy 	}
342a910e4a9SSolomon Peachy 
343a910e4a9SSolomon Peachy 	if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) &&
344a910e4a9SSolomon Peachy 	    (priv->channel != conf->chandef.chan)) {
345a910e4a9SSolomon Peachy 		struct ieee80211_channel *ch = conf->chandef.chan;
346a910e4a9SSolomon Peachy 		struct wsm_switch_channel channel = {
347a910e4a9SSolomon Peachy 			.channel_number = ch->hw_value,
348a910e4a9SSolomon Peachy 		};
349a910e4a9SSolomon Peachy 		pr_debug("[STA] Freq %d (wsm ch: %d).\n",
350a910e4a9SSolomon Peachy 			 ch->center_freq, ch->hw_value);
351a910e4a9SSolomon Peachy 
352a910e4a9SSolomon Peachy 		/* __cw1200_flush() implicitly locks tx, if successful */
353a910e4a9SSolomon Peachy 		if (!__cw1200_flush(priv, false)) {
354a910e4a9SSolomon Peachy 			if (!wsm_switch_channel(priv, &channel)) {
355a910e4a9SSolomon Peachy 				ret = wait_event_timeout(priv->channel_switch_done,
356a910e4a9SSolomon Peachy 							 !priv->channel_switch_in_progress,
357a910e4a9SSolomon Peachy 							 3 * HZ);
358a910e4a9SSolomon Peachy 				if (ret) {
359a910e4a9SSolomon Peachy 					/* Already unlocks if successful */
360a910e4a9SSolomon Peachy 					priv->channel = ch;
361a910e4a9SSolomon Peachy 					ret = 0;
362a910e4a9SSolomon Peachy 				} else {
363a910e4a9SSolomon Peachy 					ret = -ETIMEDOUT;
364a910e4a9SSolomon Peachy 				}
365a910e4a9SSolomon Peachy 			} else {
366a910e4a9SSolomon Peachy 				/* Unlock if switch channel fails */
367a910e4a9SSolomon Peachy 				wsm_unlock_tx(priv);
368a910e4a9SSolomon Peachy 			}
369a910e4a9SSolomon Peachy 		}
370a910e4a9SSolomon Peachy 	}
371a910e4a9SSolomon Peachy 
372a910e4a9SSolomon Peachy 	if (changed & IEEE80211_CONF_CHANGE_PS) {
373a910e4a9SSolomon Peachy 		if (!(conf->flags & IEEE80211_CONF_PS))
374a910e4a9SSolomon Peachy 			priv->powersave_mode.mode = WSM_PSM_ACTIVE;
375a910e4a9SSolomon Peachy 		else if (conf->dynamic_ps_timeout <= 0)
376a910e4a9SSolomon Peachy 			priv->powersave_mode.mode = WSM_PSM_PS;
377a910e4a9SSolomon Peachy 		else
378a910e4a9SSolomon Peachy 			priv->powersave_mode.mode = WSM_PSM_FAST_PS;
379a910e4a9SSolomon Peachy 
380a910e4a9SSolomon Peachy 		/* Firmware requires that value for this 1-byte field must
381a910e4a9SSolomon Peachy 		 * be specified in units of 500us. Values above the 128ms
382a910e4a9SSolomon Peachy 		 * threshold are not supported.
383a910e4a9SSolomon Peachy 		 */
384a910e4a9SSolomon Peachy 		if (conf->dynamic_ps_timeout >= 0x80)
385a910e4a9SSolomon Peachy 			priv->powersave_mode.fast_psm_idle_period = 0xFF;
386a910e4a9SSolomon Peachy 		else
387a910e4a9SSolomon Peachy 			priv->powersave_mode.fast_psm_idle_period =
388a910e4a9SSolomon Peachy 					conf->dynamic_ps_timeout << 1;
389a910e4a9SSolomon Peachy 
390a910e4a9SSolomon Peachy 		if (priv->join_status == CW1200_JOIN_STATUS_STA &&
391a910e4a9SSolomon Peachy 		    priv->bss_params.aid)
392a910e4a9SSolomon Peachy 			cw1200_set_pm(priv, &priv->powersave_mode);
393a910e4a9SSolomon Peachy 	}
394a910e4a9SSolomon Peachy 
395a910e4a9SSolomon Peachy 	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
396a910e4a9SSolomon Peachy 		/* TBD: It looks like it's transparent
397a910e4a9SSolomon Peachy 		 * there's a monitor interface present -- use this
398a910e4a9SSolomon Peachy 		 * to determine for example whether to calculate
399a910e4a9SSolomon Peachy 		 * timestamps for packets or not, do not use instead
400a910e4a9SSolomon Peachy 		 * of filter flags!
401a910e4a9SSolomon Peachy 		 */
402a910e4a9SSolomon Peachy 	}
403a910e4a9SSolomon Peachy 
404a910e4a9SSolomon Peachy 	if (changed & IEEE80211_CONF_CHANGE_IDLE) {
405a910e4a9SSolomon Peachy 		struct wsm_operational_mode mode = {
406a910e4a9SSolomon Peachy 			.power_mode = cw1200_power_mode,
407a910e4a9SSolomon Peachy 			.disable_more_flag_usage = true,
408a910e4a9SSolomon Peachy 		};
409a910e4a9SSolomon Peachy 
410a910e4a9SSolomon Peachy 		wsm_lock_tx(priv);
411a910e4a9SSolomon Peachy 		/* Disable p2p-dev mode forced by TX request */
412a910e4a9SSolomon Peachy 		if ((priv->join_status == CW1200_JOIN_STATUS_MONITOR) &&
413a910e4a9SSolomon Peachy 		    (conf->flags & IEEE80211_CONF_IDLE) &&
414a910e4a9SSolomon Peachy 		    !priv->listening) {
415a910e4a9SSolomon Peachy 			cw1200_disable_listening(priv);
416a910e4a9SSolomon Peachy 			priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
417a910e4a9SSolomon Peachy 		}
418a910e4a9SSolomon Peachy 		wsm_set_operational_mode(priv, &mode);
419a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
420a910e4a9SSolomon Peachy 	}
421a910e4a9SSolomon Peachy 
422a910e4a9SSolomon Peachy 	if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
423a910e4a9SSolomon Peachy 		pr_debug("[STA] Retry limits: %d (long), %d (short).\n",
424a910e4a9SSolomon Peachy 			 conf->long_frame_max_tx_count,
425a910e4a9SSolomon Peachy 			 conf->short_frame_max_tx_count);
426a910e4a9SSolomon Peachy 		spin_lock_bh(&priv->tx_policy_cache.lock);
427a910e4a9SSolomon Peachy 		priv->long_frame_max_tx_count = conf->long_frame_max_tx_count;
428a910e4a9SSolomon Peachy 		priv->short_frame_max_tx_count =
429a910e4a9SSolomon Peachy 			(conf->short_frame_max_tx_count < 0x0F) ?
430a910e4a9SSolomon Peachy 			conf->short_frame_max_tx_count : 0x0F;
431a910e4a9SSolomon Peachy 		priv->hw->max_rate_tries = priv->short_frame_max_tx_count;
432a910e4a9SSolomon Peachy 		spin_unlock_bh(&priv->tx_policy_cache.lock);
433a910e4a9SSolomon Peachy 	}
434a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
435a910e4a9SSolomon Peachy 	up(&priv->scan.lock);
436a910e4a9SSolomon Peachy 	return ret;
437a910e4a9SSolomon Peachy }
438a910e4a9SSolomon Peachy 
439a910e4a9SSolomon Peachy void cw1200_update_filtering(struct cw1200_common *priv)
440a910e4a9SSolomon Peachy {
441a910e4a9SSolomon Peachy 	int ret;
442a910e4a9SSolomon Peachy 	bool bssid_filtering = !priv->rx_filter.bssid;
443a910e4a9SSolomon Peachy 	bool is_p2p = priv->vif && priv->vif->p2p;
444a910e4a9SSolomon Peachy 	bool is_sta = priv->vif && NL80211_IFTYPE_STATION == priv->vif->type;
445a910e4a9SSolomon Peachy 
446a910e4a9SSolomon Peachy 	static struct wsm_beacon_filter_control bf_ctrl;
447a910e4a9SSolomon Peachy 	static struct wsm_mib_beacon_filter_table bf_tbl = {
448a910e4a9SSolomon Peachy 		.entry[0].ie_id = WLAN_EID_VENDOR_SPECIFIC,
449a910e4a9SSolomon Peachy 		.entry[0].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
450a910e4a9SSolomon Peachy 					WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
451a910e4a9SSolomon Peachy 					WSM_BEACON_FILTER_IE_HAS_APPEARED,
452a910e4a9SSolomon Peachy 		.entry[0].oui[0] = 0x50,
453a910e4a9SSolomon Peachy 		.entry[0].oui[1] = 0x6F,
454a910e4a9SSolomon Peachy 		.entry[0].oui[2] = 0x9A,
455a910e4a9SSolomon Peachy 		.entry[1].ie_id = WLAN_EID_HT_OPERATION,
456a910e4a9SSolomon Peachy 		.entry[1].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
457a910e4a9SSolomon Peachy 					WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
458a910e4a9SSolomon Peachy 					WSM_BEACON_FILTER_IE_HAS_APPEARED,
459a910e4a9SSolomon Peachy 		.entry[2].ie_id = WLAN_EID_ERP_INFO,
460a910e4a9SSolomon Peachy 		.entry[2].flags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
461a910e4a9SSolomon Peachy 					WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
462a910e4a9SSolomon Peachy 					WSM_BEACON_FILTER_IE_HAS_APPEARED,
463a910e4a9SSolomon Peachy 	};
464a910e4a9SSolomon Peachy 
465a910e4a9SSolomon Peachy 	if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE)
466a910e4a9SSolomon Peachy 		return;
467a910e4a9SSolomon Peachy 	else if (priv->join_status == CW1200_JOIN_STATUS_MONITOR)
468a910e4a9SSolomon Peachy 		bssid_filtering = false;
469a910e4a9SSolomon Peachy 
470a910e4a9SSolomon Peachy 	if (priv->disable_beacon_filter) {
471a910e4a9SSolomon Peachy 		bf_ctrl.enabled = 0;
472a910e4a9SSolomon Peachy 		bf_ctrl.bcn_count = 1;
473a910e4a9SSolomon Peachy 		bf_tbl.num = __cpu_to_le32(0);
474a910e4a9SSolomon Peachy 	} else if (is_p2p || !is_sta) {
475a910e4a9SSolomon Peachy 		bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE |
476a910e4a9SSolomon Peachy 			WSM_BEACON_FILTER_AUTO_ERP;
477a910e4a9SSolomon Peachy 		bf_ctrl.bcn_count = 0;
478a910e4a9SSolomon Peachy 		bf_tbl.num = __cpu_to_le32(2);
479a910e4a9SSolomon Peachy 	} else {
480a910e4a9SSolomon Peachy 		bf_ctrl.enabled = WSM_BEACON_FILTER_ENABLE;
481a910e4a9SSolomon Peachy 		bf_ctrl.bcn_count = 0;
482a910e4a9SSolomon Peachy 		bf_tbl.num = __cpu_to_le32(3);
483a910e4a9SSolomon Peachy 	}
484a910e4a9SSolomon Peachy 
4858b3e7be4SSolomon Peachy 	/* When acting as p2p client being connected to p2p GO, in order to
486a910e4a9SSolomon Peachy 	 * receive frames from a different p2p device, turn off bssid filter.
487a910e4a9SSolomon Peachy 	 *
488a910e4a9SSolomon Peachy 	 * WARNING: FW dependency!
489a910e4a9SSolomon Peachy 	 * This can only be used with FW WSM371 and its successors.
490a910e4a9SSolomon Peachy 	 * In that FW version even with bssid filter turned off,
491a910e4a9SSolomon Peachy 	 * device will block most of the unwanted frames.
492a910e4a9SSolomon Peachy 	 */
493a910e4a9SSolomon Peachy 	if (is_p2p)
494a910e4a9SSolomon Peachy 		bssid_filtering = false;
495a910e4a9SSolomon Peachy 
496a910e4a9SSolomon Peachy 	ret = wsm_set_rx_filter(priv, &priv->rx_filter);
497a910e4a9SSolomon Peachy 	if (!ret)
498a910e4a9SSolomon Peachy 		ret = wsm_set_beacon_filter_table(priv, &bf_tbl);
499a910e4a9SSolomon Peachy 	if (!ret)
500a910e4a9SSolomon Peachy 		ret = wsm_beacon_filter_control(priv, &bf_ctrl);
501a910e4a9SSolomon Peachy 	if (!ret)
502a910e4a9SSolomon Peachy 		ret = wsm_set_bssid_filtering(priv, bssid_filtering);
503a910e4a9SSolomon Peachy 	if (!ret)
504a910e4a9SSolomon Peachy 		ret = wsm_set_multicast_filter(priv, &priv->multicast_filter);
505a910e4a9SSolomon Peachy 	if (ret)
506a910e4a9SSolomon Peachy 		wiphy_err(priv->hw->wiphy,
507a910e4a9SSolomon Peachy 			  "Update filtering failed: %d.\n", ret);
508a910e4a9SSolomon Peachy 	return;
509a910e4a9SSolomon Peachy }
510a910e4a9SSolomon Peachy 
511a910e4a9SSolomon Peachy void cw1200_update_filtering_work(struct work_struct *work)
512a910e4a9SSolomon Peachy {
513a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
514a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common,
515a910e4a9SSolomon Peachy 			     update_filtering_work);
516a910e4a9SSolomon Peachy 
517a910e4a9SSolomon Peachy 	cw1200_update_filtering(priv);
518a910e4a9SSolomon Peachy }
519a910e4a9SSolomon Peachy 
520a910e4a9SSolomon Peachy void cw1200_set_beacon_wakeup_period_work(struct work_struct *work)
521a910e4a9SSolomon Peachy {
522a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
523a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common,
524a910e4a9SSolomon Peachy 			     set_beacon_wakeup_period_work);
525a910e4a9SSolomon Peachy 
526a910e4a9SSolomon Peachy 	wsm_set_beacon_wakeup_period(priv,
527a910e4a9SSolomon Peachy 				     priv->beacon_int * priv->join_dtim_period >
528a910e4a9SSolomon Peachy 				     MAX_BEACON_SKIP_TIME_MS ? 1 :
529a910e4a9SSolomon Peachy 				     priv->join_dtim_period, 0);
530a910e4a9SSolomon Peachy }
531a910e4a9SSolomon Peachy 
532a910e4a9SSolomon Peachy u64 cw1200_prepare_multicast(struct ieee80211_hw *hw,
533a910e4a9SSolomon Peachy 			     struct netdev_hw_addr_list *mc_list)
534a910e4a9SSolomon Peachy {
535a910e4a9SSolomon Peachy 	static u8 broadcast_ipv6[ETH_ALEN] = {
536a910e4a9SSolomon Peachy 		0x33, 0x33, 0x00, 0x00, 0x00, 0x01
537a910e4a9SSolomon Peachy 	};
538a910e4a9SSolomon Peachy 	static u8 broadcast_ipv4[ETH_ALEN] = {
539a910e4a9SSolomon Peachy 		0x01, 0x00, 0x5e, 0x00, 0x00, 0x01
540a910e4a9SSolomon Peachy 	};
541a910e4a9SSolomon Peachy 	struct cw1200_common *priv = hw->priv;
542a910e4a9SSolomon Peachy 	struct netdev_hw_addr *ha;
543a910e4a9SSolomon Peachy 	int count = 0;
544a910e4a9SSolomon Peachy 
545a910e4a9SSolomon Peachy 	/* Disable multicast filtering */
546a910e4a9SSolomon Peachy 	priv->has_multicast_subscription = false;
547a910e4a9SSolomon Peachy 	memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter));
548a910e4a9SSolomon Peachy 
549a910e4a9SSolomon Peachy 	if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES)
550a910e4a9SSolomon Peachy 		return 0;
551a910e4a9SSolomon Peachy 
552a910e4a9SSolomon Peachy 	/* Enable if requested */
553a910e4a9SSolomon Peachy 	netdev_hw_addr_list_for_each(ha, mc_list) {
554a910e4a9SSolomon Peachy 		pr_debug("[STA] multicast: %pM\n", ha->addr);
555a910e4a9SSolomon Peachy 		memcpy(&priv->multicast_filter.macaddrs[count],
556a910e4a9SSolomon Peachy 		       ha->addr, ETH_ALEN);
55735df5388Sdingtianhong 		if (!ether_addr_equal(ha->addr, broadcast_ipv4) &&
55835df5388Sdingtianhong 		    !ether_addr_equal(ha->addr, broadcast_ipv6))
559a910e4a9SSolomon Peachy 			priv->has_multicast_subscription = true;
560a910e4a9SSolomon Peachy 		count++;
561a910e4a9SSolomon Peachy 	}
562a910e4a9SSolomon Peachy 
563a910e4a9SSolomon Peachy 	if (count) {
564a910e4a9SSolomon Peachy 		priv->multicast_filter.enable = __cpu_to_le32(1);
565a910e4a9SSolomon Peachy 		priv->multicast_filter.num_addrs = __cpu_to_le32(count);
566a910e4a9SSolomon Peachy 	}
567a910e4a9SSolomon Peachy 
568a910e4a9SSolomon Peachy 	return netdev_hw_addr_list_count(mc_list);
569a910e4a9SSolomon Peachy }
570a910e4a9SSolomon Peachy 
571a910e4a9SSolomon Peachy void cw1200_configure_filter(struct ieee80211_hw *dev,
572a910e4a9SSolomon Peachy 			     unsigned int changed_flags,
573a910e4a9SSolomon Peachy 			     unsigned int *total_flags,
574a910e4a9SSolomon Peachy 			     u64 multicast)
575a910e4a9SSolomon Peachy {
576a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
577a910e4a9SSolomon Peachy 	bool listening = !!(*total_flags &
578df140465SJohannes Berg 			    (FIF_OTHER_BSS |
579a910e4a9SSolomon Peachy 			     FIF_BCN_PRBRESP_PROMISC |
580a910e4a9SSolomon Peachy 			     FIF_PROBE_REQ));
581a910e4a9SSolomon Peachy 
582df140465SJohannes Berg 	*total_flags &= FIF_OTHER_BSS |
583a910e4a9SSolomon Peachy 			FIF_FCSFAIL |
584a910e4a9SSolomon Peachy 			FIF_BCN_PRBRESP_PROMISC |
585a910e4a9SSolomon Peachy 			FIF_PROBE_REQ;
586a910e4a9SSolomon Peachy 
587a910e4a9SSolomon Peachy 	down(&priv->scan.lock);
588a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
589a910e4a9SSolomon Peachy 
590df140465SJohannes Berg 	priv->rx_filter.promiscuous = 0;
591a910e4a9SSolomon Peachy 	priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS |
592a910e4a9SSolomon Peachy 			FIF_PROBE_REQ)) ? 1 : 0;
593a910e4a9SSolomon Peachy 	priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0;
594a910e4a9SSolomon Peachy 	priv->disable_beacon_filter = !(*total_flags &
595a910e4a9SSolomon Peachy 					(FIF_BCN_PRBRESP_PROMISC |
596a910e4a9SSolomon Peachy 					 FIF_PROBE_REQ));
597a910e4a9SSolomon Peachy 	if (priv->listening != listening) {
598a910e4a9SSolomon Peachy 		priv->listening = listening;
599a910e4a9SSolomon Peachy 		wsm_lock_tx(priv);
600a910e4a9SSolomon Peachy 		cw1200_update_listening(priv, listening);
601a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
602a910e4a9SSolomon Peachy 	}
603a910e4a9SSolomon Peachy 	cw1200_update_filtering(priv);
604a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
605a910e4a9SSolomon Peachy 	up(&priv->scan.lock);
606a910e4a9SSolomon Peachy }
607a910e4a9SSolomon Peachy 
608a910e4a9SSolomon Peachy int cw1200_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
609a910e4a9SSolomon Peachy 		   u16 queue, const struct ieee80211_tx_queue_params *params)
610a910e4a9SSolomon Peachy {
611a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
612a910e4a9SSolomon Peachy 	int ret = 0;
613a910e4a9SSolomon Peachy 	/* To prevent re-applying PM request OID again and again*/
614a910e4a9SSolomon Peachy 	bool old_uapsd_flags;
615a910e4a9SSolomon Peachy 
616a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
617a910e4a9SSolomon Peachy 
618a910e4a9SSolomon Peachy 	if (queue < dev->queues) {
6197258416cSSolomon Peachy 		old_uapsd_flags = le16_to_cpu(priv->uapsd_info.uapsd_flags);
620a910e4a9SSolomon Peachy 
621a910e4a9SSolomon Peachy 		WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0);
622a910e4a9SSolomon Peachy 		ret = wsm_set_tx_queue_params(priv,
623a910e4a9SSolomon Peachy 					      &priv->tx_queue_params.params[queue], queue);
624a910e4a9SSolomon Peachy 		if (ret) {
625a910e4a9SSolomon Peachy 			ret = -EINVAL;
626a910e4a9SSolomon Peachy 			goto out;
627a910e4a9SSolomon Peachy 		}
628a910e4a9SSolomon Peachy 
629a910e4a9SSolomon Peachy 		WSM_EDCA_SET(&priv->edca, queue, params->aifs,
630a910e4a9SSolomon Peachy 			     params->cw_min, params->cw_max,
631a910e4a9SSolomon Peachy 			     params->txop, 0xc8,
632a910e4a9SSolomon Peachy 			     params->uapsd);
633a910e4a9SSolomon Peachy 		ret = wsm_set_edca_params(priv, &priv->edca);
634a910e4a9SSolomon Peachy 		if (ret) {
635a910e4a9SSolomon Peachy 			ret = -EINVAL;
636a910e4a9SSolomon Peachy 			goto out;
637a910e4a9SSolomon Peachy 		}
638a910e4a9SSolomon Peachy 
639a910e4a9SSolomon Peachy 		if (priv->mode == NL80211_IFTYPE_STATION) {
640a910e4a9SSolomon Peachy 			ret = cw1200_set_uapsd_param(priv, &priv->edca);
641a910e4a9SSolomon Peachy 			if (!ret && priv->setbssparams_done &&
642a910e4a9SSolomon Peachy 			    (priv->join_status == CW1200_JOIN_STATUS_STA) &&
6437258416cSSolomon Peachy 			    (old_uapsd_flags != le16_to_cpu(priv->uapsd_info.uapsd_flags)))
644a910e4a9SSolomon Peachy 				ret = cw1200_set_pm(priv, &priv->powersave_mode);
645a910e4a9SSolomon Peachy 		}
646a910e4a9SSolomon Peachy 	} else {
647a910e4a9SSolomon Peachy 		ret = -EINVAL;
648a910e4a9SSolomon Peachy 	}
649a910e4a9SSolomon Peachy 
650a910e4a9SSolomon Peachy out:
651a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
652a910e4a9SSolomon Peachy 	return ret;
653a910e4a9SSolomon Peachy }
654a910e4a9SSolomon Peachy 
655a910e4a9SSolomon Peachy int cw1200_get_stats(struct ieee80211_hw *dev,
656a910e4a9SSolomon Peachy 		     struct ieee80211_low_level_stats *stats)
657a910e4a9SSolomon Peachy {
658a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
659a910e4a9SSolomon Peachy 
660a910e4a9SSolomon Peachy 	memcpy(stats, &priv->stats, sizeof(*stats));
661a910e4a9SSolomon Peachy 	return 0;
662a910e4a9SSolomon Peachy }
663a910e4a9SSolomon Peachy 
664a910e4a9SSolomon Peachy int cw1200_set_pm(struct cw1200_common *priv, const struct wsm_set_pm *arg)
665a910e4a9SSolomon Peachy {
666a910e4a9SSolomon Peachy 	struct wsm_set_pm pm = *arg;
667a910e4a9SSolomon Peachy 
668a910e4a9SSolomon Peachy 	if (priv->uapsd_info.uapsd_flags != 0)
669a910e4a9SSolomon Peachy 		pm.mode &= ~WSM_PSM_FAST_PS_FLAG;
670a910e4a9SSolomon Peachy 
671a910e4a9SSolomon Peachy 	if (memcmp(&pm, &priv->firmware_ps_mode,
672a910e4a9SSolomon Peachy 		   sizeof(struct wsm_set_pm))) {
673a910e4a9SSolomon Peachy 		priv->firmware_ps_mode = pm;
674a910e4a9SSolomon Peachy 		return wsm_set_pm(priv, &pm);
675a910e4a9SSolomon Peachy 	} else {
676a910e4a9SSolomon Peachy 		return 0;
677a910e4a9SSolomon Peachy 	}
678a910e4a9SSolomon Peachy }
679a910e4a9SSolomon Peachy 
680a910e4a9SSolomon Peachy int cw1200_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
681a910e4a9SSolomon Peachy 		   struct ieee80211_vif *vif, struct ieee80211_sta *sta,
682a910e4a9SSolomon Peachy 		   struct ieee80211_key_conf *key)
683a910e4a9SSolomon Peachy {
684a910e4a9SSolomon Peachy 	int ret = -EOPNOTSUPP;
685a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
686a910e4a9SSolomon Peachy 	struct ieee80211_key_seq seq;
687a910e4a9SSolomon Peachy 
688a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
689a910e4a9SSolomon Peachy 
690a910e4a9SSolomon Peachy 	if (cmd == SET_KEY) {
691a910e4a9SSolomon Peachy 		u8 *peer_addr = NULL;
692a910e4a9SSolomon Peachy 		int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
693a910e4a9SSolomon Peachy 			1 : 0;
694a910e4a9SSolomon Peachy 		int idx = cw1200_alloc_key(priv);
695a910e4a9SSolomon Peachy 		struct wsm_add_key *wsm_key = &priv->keys[idx];
696a910e4a9SSolomon Peachy 
697a910e4a9SSolomon Peachy 		if (idx < 0) {
698a910e4a9SSolomon Peachy 			ret = -EINVAL;
699a910e4a9SSolomon Peachy 			goto finally;
700a910e4a9SSolomon Peachy 		}
701a910e4a9SSolomon Peachy 
702a910e4a9SSolomon Peachy 		if (sta)
703a910e4a9SSolomon Peachy 			peer_addr = sta->addr;
704a910e4a9SSolomon Peachy 
705db12847cSIdo Yariv 		key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE |
706db12847cSIdo Yariv 			      IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
707a910e4a9SSolomon Peachy 
708a910e4a9SSolomon Peachy 		switch (key->cipher) {
709a910e4a9SSolomon Peachy 		case WLAN_CIPHER_SUITE_WEP40:
710a910e4a9SSolomon Peachy 		case WLAN_CIPHER_SUITE_WEP104:
711a910e4a9SSolomon Peachy 			if (key->keylen > 16) {
712a910e4a9SSolomon Peachy 				cw1200_free_key(priv, idx);
713a910e4a9SSolomon Peachy 				ret = -EINVAL;
714a910e4a9SSolomon Peachy 				goto finally;
715a910e4a9SSolomon Peachy 			}
716a910e4a9SSolomon Peachy 
717a910e4a9SSolomon Peachy 			if (pairwise) {
718a910e4a9SSolomon Peachy 				wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE;
719a910e4a9SSolomon Peachy 				memcpy(wsm_key->wep_pairwise.peer,
720a910e4a9SSolomon Peachy 				       peer_addr, ETH_ALEN);
721a910e4a9SSolomon Peachy 				memcpy(wsm_key->wep_pairwise.keydata,
722a910e4a9SSolomon Peachy 				       &key->key[0], key->keylen);
723a910e4a9SSolomon Peachy 				wsm_key->wep_pairwise.keylen = key->keylen;
724a910e4a9SSolomon Peachy 			} else {
725a910e4a9SSolomon Peachy 				wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT;
726a910e4a9SSolomon Peachy 				memcpy(wsm_key->wep_group.keydata,
727a910e4a9SSolomon Peachy 				       &key->key[0], key->keylen);
728a910e4a9SSolomon Peachy 				wsm_key->wep_group.keylen = key->keylen;
729a910e4a9SSolomon Peachy 				wsm_key->wep_group.keyid = key->keyidx;
730a910e4a9SSolomon Peachy 			}
731a910e4a9SSolomon Peachy 			break;
732a910e4a9SSolomon Peachy 		case WLAN_CIPHER_SUITE_TKIP:
733a910e4a9SSolomon Peachy 			ieee80211_get_key_rx_seq(key, 0, &seq);
734a910e4a9SSolomon Peachy 			if (pairwise) {
735a910e4a9SSolomon Peachy 				wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE;
736a910e4a9SSolomon Peachy 				memcpy(wsm_key->tkip_pairwise.peer,
737a910e4a9SSolomon Peachy 				       peer_addr, ETH_ALEN);
738a910e4a9SSolomon Peachy 				memcpy(wsm_key->tkip_pairwise.keydata,
739a910e4a9SSolomon Peachy 				       &key->key[0], 16);
740a910e4a9SSolomon Peachy 				memcpy(wsm_key->tkip_pairwise.tx_mic_key,
741a910e4a9SSolomon Peachy 				       &key->key[16], 8);
742a910e4a9SSolomon Peachy 				memcpy(wsm_key->tkip_pairwise.rx_mic_key,
743a910e4a9SSolomon Peachy 				       &key->key[24], 8);
744a910e4a9SSolomon Peachy 			} else {
745a910e4a9SSolomon Peachy 				size_t mic_offset =
746a910e4a9SSolomon Peachy 					(priv->mode == NL80211_IFTYPE_AP) ?
747a910e4a9SSolomon Peachy 					16 : 24;
748a910e4a9SSolomon Peachy 				wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP;
749a910e4a9SSolomon Peachy 				memcpy(wsm_key->tkip_group.keydata,
750a910e4a9SSolomon Peachy 				       &key->key[0], 16);
751a910e4a9SSolomon Peachy 				memcpy(wsm_key->tkip_group.rx_mic_key,
752a910e4a9SSolomon Peachy 				       &key->key[mic_offset], 8);
753a910e4a9SSolomon Peachy 
754a910e4a9SSolomon Peachy 				wsm_key->tkip_group.rx_seqnum[0] = seq.tkip.iv16 & 0xff;
755a910e4a9SSolomon Peachy 				wsm_key->tkip_group.rx_seqnum[1] = (seq.tkip.iv16 >> 8) & 0xff;
756a910e4a9SSolomon Peachy 				wsm_key->tkip_group.rx_seqnum[2] = seq.tkip.iv32 & 0xff;
757a910e4a9SSolomon Peachy 				wsm_key->tkip_group.rx_seqnum[3] = (seq.tkip.iv32 >> 8) & 0xff;
758a910e4a9SSolomon Peachy 				wsm_key->tkip_group.rx_seqnum[4] = (seq.tkip.iv32 >> 16) & 0xff;
759a910e4a9SSolomon Peachy 				wsm_key->tkip_group.rx_seqnum[5] = (seq.tkip.iv32 >> 24) & 0xff;
760a910e4a9SSolomon Peachy 				wsm_key->tkip_group.rx_seqnum[6] = 0;
761a910e4a9SSolomon Peachy 				wsm_key->tkip_group.rx_seqnum[7] = 0;
762a910e4a9SSolomon Peachy 
763a910e4a9SSolomon Peachy 				wsm_key->tkip_group.keyid = key->keyidx;
764a910e4a9SSolomon Peachy 			}
765a910e4a9SSolomon Peachy 			break;
766a910e4a9SSolomon Peachy 		case WLAN_CIPHER_SUITE_CCMP:
767a910e4a9SSolomon Peachy 			ieee80211_get_key_rx_seq(key, 0, &seq);
768a910e4a9SSolomon Peachy 			if (pairwise) {
769a910e4a9SSolomon Peachy 				wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE;
770a910e4a9SSolomon Peachy 				memcpy(wsm_key->aes_pairwise.peer,
771a910e4a9SSolomon Peachy 				       peer_addr, ETH_ALEN);
772a910e4a9SSolomon Peachy 				memcpy(wsm_key->aes_pairwise.keydata,
773a910e4a9SSolomon Peachy 				       &key->key[0], 16);
774a910e4a9SSolomon Peachy 			} else {
775a910e4a9SSolomon Peachy 				wsm_key->type = WSM_KEY_TYPE_AES_GROUP;
776a910e4a9SSolomon Peachy 				memcpy(wsm_key->aes_group.keydata,
777a910e4a9SSolomon Peachy 				       &key->key[0], 16);
778a910e4a9SSolomon Peachy 
779a910e4a9SSolomon Peachy 				wsm_key->aes_group.rx_seqnum[0] = seq.ccmp.pn[5];
780a910e4a9SSolomon Peachy 				wsm_key->aes_group.rx_seqnum[1] = seq.ccmp.pn[4];
781a910e4a9SSolomon Peachy 				wsm_key->aes_group.rx_seqnum[2] = seq.ccmp.pn[3];
782a910e4a9SSolomon Peachy 				wsm_key->aes_group.rx_seqnum[3] = seq.ccmp.pn[2];
783a910e4a9SSolomon Peachy 				wsm_key->aes_group.rx_seqnum[4] = seq.ccmp.pn[1];
784a910e4a9SSolomon Peachy 				wsm_key->aes_group.rx_seqnum[5] = seq.ccmp.pn[0];
785a910e4a9SSolomon Peachy 				wsm_key->aes_group.rx_seqnum[6] = 0;
786a910e4a9SSolomon Peachy 				wsm_key->aes_group.rx_seqnum[7] = 0;
787a910e4a9SSolomon Peachy 				wsm_key->aes_group.keyid = key->keyidx;
788a910e4a9SSolomon Peachy 			}
789a910e4a9SSolomon Peachy 			break;
790a910e4a9SSolomon Peachy 		case WLAN_CIPHER_SUITE_SMS4:
791a910e4a9SSolomon Peachy 			if (pairwise) {
792a910e4a9SSolomon Peachy 				wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE;
793a910e4a9SSolomon Peachy 				memcpy(wsm_key->wapi_pairwise.peer,
794a910e4a9SSolomon Peachy 				       peer_addr, ETH_ALEN);
795a910e4a9SSolomon Peachy 				memcpy(wsm_key->wapi_pairwise.keydata,
796a910e4a9SSolomon Peachy 				       &key->key[0], 16);
797a910e4a9SSolomon Peachy 				memcpy(wsm_key->wapi_pairwise.mic_key,
798a910e4a9SSolomon Peachy 				       &key->key[16], 16);
799a910e4a9SSolomon Peachy 				wsm_key->wapi_pairwise.keyid = key->keyidx;
800a910e4a9SSolomon Peachy 			} else {
801a910e4a9SSolomon Peachy 				wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP;
802a910e4a9SSolomon Peachy 				memcpy(wsm_key->wapi_group.keydata,
803a910e4a9SSolomon Peachy 				       &key->key[0],  16);
804a910e4a9SSolomon Peachy 				memcpy(wsm_key->wapi_group.mic_key,
805a910e4a9SSolomon Peachy 				       &key->key[16], 16);
806a910e4a9SSolomon Peachy 				wsm_key->wapi_group.keyid = key->keyidx;
807a910e4a9SSolomon Peachy 			}
808a910e4a9SSolomon Peachy 			break;
809a910e4a9SSolomon Peachy 		default:
810a910e4a9SSolomon Peachy 			pr_warn("Unhandled key type %d\n", key->cipher);
811a910e4a9SSolomon Peachy 			cw1200_free_key(priv, idx);
812a910e4a9SSolomon Peachy 			ret = -EOPNOTSUPP;
813a910e4a9SSolomon Peachy 			goto finally;
814a910e4a9SSolomon Peachy 		}
815a910e4a9SSolomon Peachy 		ret = wsm_add_key(priv, wsm_key);
816a910e4a9SSolomon Peachy 		if (!ret)
817a910e4a9SSolomon Peachy 			key->hw_key_idx = idx;
818a910e4a9SSolomon Peachy 		else
819a910e4a9SSolomon Peachy 			cw1200_free_key(priv, idx);
820a910e4a9SSolomon Peachy 	} else if (cmd == DISABLE_KEY) {
821a910e4a9SSolomon Peachy 		struct wsm_remove_key wsm_key = {
822a910e4a9SSolomon Peachy 			.index = key->hw_key_idx,
823a910e4a9SSolomon Peachy 		};
824a910e4a9SSolomon Peachy 
825a910e4a9SSolomon Peachy 		if (wsm_key.index > WSM_KEY_MAX_INDEX) {
826a910e4a9SSolomon Peachy 			ret = -EINVAL;
827a910e4a9SSolomon Peachy 			goto finally;
828a910e4a9SSolomon Peachy 		}
829a910e4a9SSolomon Peachy 
830a910e4a9SSolomon Peachy 		cw1200_free_key(priv, wsm_key.index);
831a910e4a9SSolomon Peachy 		ret = wsm_remove_key(priv, &wsm_key);
832a910e4a9SSolomon Peachy 	} else {
833a910e4a9SSolomon Peachy 		pr_warn("Unhandled key command %d\n", cmd);
834a910e4a9SSolomon Peachy 	}
835a910e4a9SSolomon Peachy 
836a910e4a9SSolomon Peachy finally:
837a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
838a910e4a9SSolomon Peachy 	return ret;
839a910e4a9SSolomon Peachy }
840a910e4a9SSolomon Peachy 
841a910e4a9SSolomon Peachy void cw1200_wep_key_work(struct work_struct *work)
842a910e4a9SSolomon Peachy {
843a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
844a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common, wep_key_work);
845a910e4a9SSolomon Peachy 	u8 queue_id = cw1200_queue_get_queue_id(priv->pending_frame_id);
846a910e4a9SSolomon Peachy 	struct cw1200_queue *queue = &priv->tx_queue[queue_id];
847a910e4a9SSolomon Peachy 	__le32 wep_default_key_id = __cpu_to_le32(
848a910e4a9SSolomon Peachy 		priv->wep_default_key_id);
849a910e4a9SSolomon Peachy 
850a910e4a9SSolomon Peachy 	pr_debug("[STA] Setting default WEP key: %d\n",
851a910e4a9SSolomon Peachy 		 priv->wep_default_key_id);
852a910e4a9SSolomon Peachy 	wsm_flush_tx(priv);
853a910e4a9SSolomon Peachy 	wsm_write_mib(priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID,
854a910e4a9SSolomon Peachy 		      &wep_default_key_id, sizeof(wep_default_key_id));
855a910e4a9SSolomon Peachy 	cw1200_queue_requeue(queue, priv->pending_frame_id);
856a910e4a9SSolomon Peachy 	wsm_unlock_tx(priv);
857a910e4a9SSolomon Peachy }
858a910e4a9SSolomon Peachy 
859a910e4a9SSolomon Peachy int cw1200_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
860a910e4a9SSolomon Peachy {
861a910e4a9SSolomon Peachy 	int ret = 0;
862a910e4a9SSolomon Peachy 	__le32 val32;
863a910e4a9SSolomon Peachy 	struct cw1200_common *priv = hw->priv;
864a910e4a9SSolomon Peachy 
865a910e4a9SSolomon Peachy 	if (priv->mode == NL80211_IFTYPE_UNSPECIFIED)
866a910e4a9SSolomon Peachy 		return 0;
867a910e4a9SSolomon Peachy 
868a910e4a9SSolomon Peachy 	if (value != (u32) -1)
869a910e4a9SSolomon Peachy 		val32 = __cpu_to_le32(value);
870a910e4a9SSolomon Peachy 	else
871a910e4a9SSolomon Peachy 		val32 = 0; /* disabled */
872a910e4a9SSolomon Peachy 
873a910e4a9SSolomon Peachy 	if (priv->rts_threshold == value)
874a910e4a9SSolomon Peachy 		goto out;
875a910e4a9SSolomon Peachy 
876a910e4a9SSolomon Peachy 	pr_debug("[STA] Setting RTS threshold: %d\n",
877a910e4a9SSolomon Peachy 		 priv->rts_threshold);
878a910e4a9SSolomon Peachy 
879a910e4a9SSolomon Peachy 	/* mutex_lock(&priv->conf_mutex); */
880a910e4a9SSolomon Peachy 	ret = wsm_write_mib(priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD,
881a910e4a9SSolomon Peachy 			    &val32, sizeof(val32));
882a910e4a9SSolomon Peachy 	if (!ret)
883a910e4a9SSolomon Peachy 		priv->rts_threshold = value;
884a910e4a9SSolomon Peachy 	/* mutex_unlock(&priv->conf_mutex); */
885a910e4a9SSolomon Peachy 
886a910e4a9SSolomon Peachy out:
887a910e4a9SSolomon Peachy 	return ret;
888a910e4a9SSolomon Peachy }
889a910e4a9SSolomon Peachy 
890a910e4a9SSolomon Peachy /* If successful, LOCKS the TX queue! */
891a910e4a9SSolomon Peachy static int __cw1200_flush(struct cw1200_common *priv, bool drop)
892a910e4a9SSolomon Peachy {
893a910e4a9SSolomon Peachy 	int i, ret;
894a910e4a9SSolomon Peachy 
895a910e4a9SSolomon Peachy 	for (;;) {
896a910e4a9SSolomon Peachy 		/* TODO: correct flush handling is required when dev_stop.
897a910e4a9SSolomon Peachy 		 * Temporary workaround: 2s
898a910e4a9SSolomon Peachy 		 */
899a910e4a9SSolomon Peachy 		if (drop) {
900a910e4a9SSolomon Peachy 			for (i = 0; i < 4; ++i)
901a910e4a9SSolomon Peachy 				cw1200_queue_clear(&priv->tx_queue[i]);
902a910e4a9SSolomon Peachy 		} else {
903a910e4a9SSolomon Peachy 			ret = wait_event_timeout(
904a910e4a9SSolomon Peachy 				priv->tx_queue_stats.wait_link_id_empty,
905a910e4a9SSolomon Peachy 				cw1200_queue_stats_is_empty(
906a910e4a9SSolomon Peachy 					&priv->tx_queue_stats, -1),
907a910e4a9SSolomon Peachy 				2 * HZ);
908a910e4a9SSolomon Peachy 		}
909a910e4a9SSolomon Peachy 
910a910e4a9SSolomon Peachy 		if (!drop && ret <= 0) {
911a910e4a9SSolomon Peachy 			ret = -ETIMEDOUT;
912a910e4a9SSolomon Peachy 			break;
913a910e4a9SSolomon Peachy 		} else {
914a910e4a9SSolomon Peachy 			ret = 0;
915a910e4a9SSolomon Peachy 		}
916a910e4a9SSolomon Peachy 
917a910e4a9SSolomon Peachy 		wsm_lock_tx(priv);
918a910e4a9SSolomon Peachy 		if (!cw1200_queue_stats_is_empty(&priv->tx_queue_stats, -1)) {
919a910e4a9SSolomon Peachy 			/* Highly unlikely: WSM requeued frames. */
920a910e4a9SSolomon Peachy 			wsm_unlock_tx(priv);
921a910e4a9SSolomon Peachy 			continue;
922a910e4a9SSolomon Peachy 		}
923a910e4a9SSolomon Peachy 		break;
924a910e4a9SSolomon Peachy 	}
925a910e4a9SSolomon Peachy 	return ret;
926a910e4a9SSolomon Peachy }
927a910e4a9SSolomon Peachy 
92877be2c54SEmmanuel Grumbach void cw1200_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
92977be2c54SEmmanuel Grumbach 		  u32 queues, bool drop)
930a910e4a9SSolomon Peachy {
931a910e4a9SSolomon Peachy 	struct cw1200_common *priv = hw->priv;
932a910e4a9SSolomon Peachy 
933a910e4a9SSolomon Peachy 	switch (priv->mode) {
934a910e4a9SSolomon Peachy 	case NL80211_IFTYPE_MONITOR:
935a910e4a9SSolomon Peachy 		drop = true;
936a910e4a9SSolomon Peachy 		break;
937a910e4a9SSolomon Peachy 	case NL80211_IFTYPE_AP:
938a910e4a9SSolomon Peachy 		if (!priv->enable_beacon)
939a910e4a9SSolomon Peachy 			drop = true;
940a910e4a9SSolomon Peachy 		break;
941a910e4a9SSolomon Peachy 	}
942a910e4a9SSolomon Peachy 
943a910e4a9SSolomon Peachy 	if (!__cw1200_flush(priv, drop))
944a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
945a910e4a9SSolomon Peachy 
946a910e4a9SSolomon Peachy 	return;
947a910e4a9SSolomon Peachy }
948a910e4a9SSolomon Peachy 
949a910e4a9SSolomon Peachy /* ******************************************************************** */
950a910e4a9SSolomon Peachy /* WSM callbacks							*/
951a910e4a9SSolomon Peachy 
952a910e4a9SSolomon Peachy void cw1200_free_event_queue(struct cw1200_common *priv)
953a910e4a9SSolomon Peachy {
954a910e4a9SSolomon Peachy 	LIST_HEAD(list);
955a910e4a9SSolomon Peachy 
956a910e4a9SSolomon Peachy 	spin_lock(&priv->event_queue_lock);
957a910e4a9SSolomon Peachy 	list_splice_init(&priv->event_queue, &list);
958a910e4a9SSolomon Peachy 	spin_unlock(&priv->event_queue_lock);
959a910e4a9SSolomon Peachy 
960a910e4a9SSolomon Peachy 	__cw1200_free_event_queue(&list);
961a910e4a9SSolomon Peachy }
962a910e4a9SSolomon Peachy 
963a910e4a9SSolomon Peachy void cw1200_event_handler(struct work_struct *work)
964a910e4a9SSolomon Peachy {
965a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
966a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common, event_handler);
967a910e4a9SSolomon Peachy 	struct cw1200_wsm_event *event;
968a910e4a9SSolomon Peachy 	LIST_HEAD(list);
969a910e4a9SSolomon Peachy 
970a910e4a9SSolomon Peachy 	spin_lock(&priv->event_queue_lock);
971a910e4a9SSolomon Peachy 	list_splice_init(&priv->event_queue, &list);
972a910e4a9SSolomon Peachy 	spin_unlock(&priv->event_queue_lock);
973a910e4a9SSolomon Peachy 
974a910e4a9SSolomon Peachy 	list_for_each_entry(event, &list, link) {
975a910e4a9SSolomon Peachy 		switch (event->evt.id) {
976a910e4a9SSolomon Peachy 		case WSM_EVENT_ERROR:
977a910e4a9SSolomon Peachy 			pr_err("Unhandled WSM Error from LMAC\n");
978a910e4a9SSolomon Peachy 			break;
979a910e4a9SSolomon Peachy 		case WSM_EVENT_BSS_LOST:
980a910e4a9SSolomon Peachy 			pr_debug("[CQM] BSS lost.\n");
981a910e4a9SSolomon Peachy 			cancel_work_sync(&priv->unjoin_work);
982a910e4a9SSolomon Peachy 			if (!down_trylock(&priv->scan.lock)) {
983a910e4a9SSolomon Peachy 				cw1200_cqm_bssloss_sm(priv, 1, 0, 0);
984a910e4a9SSolomon Peachy 				up(&priv->scan.lock);
985a910e4a9SSolomon Peachy 			} else {
986a910e4a9SSolomon Peachy 				/* Scan is in progress. Delay reporting.
987a910e4a9SSolomon Peachy 				 * Scan complete will trigger bss_loss_work
988a910e4a9SSolomon Peachy 				 */
989a910e4a9SSolomon Peachy 				priv->delayed_link_loss = 1;
990a910e4a9SSolomon Peachy 				/* Also start a watchdog. */
991a910e4a9SSolomon Peachy 				queue_delayed_work(priv->workqueue,
992a910e4a9SSolomon Peachy 						   &priv->bss_loss_work, 5*HZ);
993a910e4a9SSolomon Peachy 			}
994a910e4a9SSolomon Peachy 			break;
995a910e4a9SSolomon Peachy 		case WSM_EVENT_BSS_REGAINED:
996a910e4a9SSolomon Peachy 			pr_debug("[CQM] BSS regained.\n");
997a910e4a9SSolomon Peachy 			cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
998a910e4a9SSolomon Peachy 			cancel_work_sync(&priv->unjoin_work);
999a910e4a9SSolomon Peachy 			break;
1000a910e4a9SSolomon Peachy 		case WSM_EVENT_RADAR_DETECTED:
1001a910e4a9SSolomon Peachy 			wiphy_info(priv->hw->wiphy, "radar pulse detected\n");
1002a910e4a9SSolomon Peachy 			break;
1003a910e4a9SSolomon Peachy 		case WSM_EVENT_RCPI_RSSI:
1004a910e4a9SSolomon Peachy 		{
1005a910e4a9SSolomon Peachy 			/* RSSI: signed Q8.0, RCPI: unsigned Q7.1
1006a910e4a9SSolomon Peachy 			 * RSSI = RCPI / 2 - 110
1007a910e4a9SSolomon Peachy 			 */
10088b3e7be4SSolomon Peachy 			int rcpi_rssi = (int)(event->evt.data & 0xFF);
1009a910e4a9SSolomon Peachy 			int cqm_evt;
1010a910e4a9SSolomon Peachy 			if (priv->cqm_use_rssi)
10118b3e7be4SSolomon Peachy 				rcpi_rssi = (s8)rcpi_rssi;
1012a910e4a9SSolomon Peachy 			else
10138b3e7be4SSolomon Peachy 				rcpi_rssi =  rcpi_rssi / 2 - 110;
1014a910e4a9SSolomon Peachy 
10158b3e7be4SSolomon Peachy 			cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ?
1016a910e4a9SSolomon Peachy 				NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
1017a910e4a9SSolomon Peachy 				NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
10188b3e7be4SSolomon Peachy 			pr_debug("[CQM] RSSI event: %d.\n", rcpi_rssi);
1019769f07d8SAndrzej Zaborowski 			ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, rcpi_rssi,
1020a910e4a9SSolomon Peachy 						  GFP_KERNEL);
1021a910e4a9SSolomon Peachy 			break;
1022a910e4a9SSolomon Peachy 		}
1023a910e4a9SSolomon Peachy 		case WSM_EVENT_BT_INACTIVE:
1024a910e4a9SSolomon Peachy 			pr_warn("Unhandled BT INACTIVE from LMAC\n");
1025a910e4a9SSolomon Peachy 			break;
1026a910e4a9SSolomon Peachy 		case WSM_EVENT_BT_ACTIVE:
1027a910e4a9SSolomon Peachy 			pr_warn("Unhandled BT ACTIVE from LMAC\n");
1028a910e4a9SSolomon Peachy 			break;
1029a910e4a9SSolomon Peachy 		}
1030a910e4a9SSolomon Peachy 	}
1031a910e4a9SSolomon Peachy 	__cw1200_free_event_queue(&list);
1032a910e4a9SSolomon Peachy }
1033a910e4a9SSolomon Peachy 
1034a910e4a9SSolomon Peachy void cw1200_bss_loss_work(struct work_struct *work)
1035a910e4a9SSolomon Peachy {
1036a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
1037a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common, bss_loss_work.work);
1038a910e4a9SSolomon Peachy 
1039a910e4a9SSolomon Peachy 	pr_debug("[CQM] Reporting connection loss.\n");
1040a910e4a9SSolomon Peachy 	wsm_lock_tx(priv);
1041a910e4a9SSolomon Peachy 	if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
1042a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
1043a910e4a9SSolomon Peachy }
1044a910e4a9SSolomon Peachy 
1045a910e4a9SSolomon Peachy void cw1200_bss_params_work(struct work_struct *work)
1046a910e4a9SSolomon Peachy {
1047a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
1048a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common, bss_params_work);
1049a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
1050a910e4a9SSolomon Peachy 
1051a910e4a9SSolomon Peachy 	priv->bss_params.reset_beacon_loss = 1;
1052a910e4a9SSolomon Peachy 	wsm_set_bss_params(priv, &priv->bss_params);
1053a910e4a9SSolomon Peachy 	priv->bss_params.reset_beacon_loss = 0;
1054a910e4a9SSolomon Peachy 
1055a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
1056a910e4a9SSolomon Peachy }
1057a910e4a9SSolomon Peachy 
1058a910e4a9SSolomon Peachy /* ******************************************************************** */
1059a910e4a9SSolomon Peachy /* Internal API								*/
1060a910e4a9SSolomon Peachy 
10618b3e7be4SSolomon Peachy /* This function is called to Parse the SDD file
1062a910e4a9SSolomon Peachy  * to extract listen_interval and PTA related information
1063a910e4a9SSolomon Peachy  * sdd is a TLV: u8 id, u8 len, u8 data[]
1064a910e4a9SSolomon Peachy  */
1065a910e4a9SSolomon Peachy static int cw1200_parse_sdd_file(struct cw1200_common *priv)
1066a910e4a9SSolomon Peachy {
1067a910e4a9SSolomon Peachy 	const u8 *p = priv->sdd->data;
1068a910e4a9SSolomon Peachy 	int ret = 0;
1069a910e4a9SSolomon Peachy 
1070a910e4a9SSolomon Peachy 	while (p + 2 <= priv->sdd->data + priv->sdd->size) {
1071a910e4a9SSolomon Peachy 		if (p + p[1] + 2 > priv->sdd->data + priv->sdd->size) {
1072a910e4a9SSolomon Peachy 			pr_warn("Malformed sdd structure\n");
1073a910e4a9SSolomon Peachy 			return -1;
1074a910e4a9SSolomon Peachy 		}
1075a910e4a9SSolomon Peachy 		switch (p[0]) {
1076a910e4a9SSolomon Peachy 		case SDD_PTA_CFG_ELT_ID: {
1077a910e4a9SSolomon Peachy 			u16 v;
1078a910e4a9SSolomon Peachy 			if (p[1] < 4) {
1079a910e4a9SSolomon Peachy 				pr_warn("SDD_PTA_CFG_ELT_ID malformed\n");
1080a910e4a9SSolomon Peachy 				ret = -1;
1081a910e4a9SSolomon Peachy 				break;
1082a910e4a9SSolomon Peachy 			}
10837258416cSSolomon Peachy 			v = le16_to_cpu(*((__le16 *)(p + 2)));
1084a910e4a9SSolomon Peachy 			if (!v)  /* non-zero means this is enabled */
1085a910e4a9SSolomon Peachy 				break;
1086a910e4a9SSolomon Peachy 
10877258416cSSolomon Peachy 			v = le16_to_cpu(*((__le16 *)(p + 4)));
1088a910e4a9SSolomon Peachy 			priv->conf_listen_interval = (v >> 7) & 0x1F;
1089a910e4a9SSolomon Peachy 			pr_debug("PTA found; Listen Interval %d\n",
1090a910e4a9SSolomon Peachy 				 priv->conf_listen_interval);
1091a910e4a9SSolomon Peachy 			break;
1092a910e4a9SSolomon Peachy 		}
1093a910e4a9SSolomon Peachy 		case SDD_REFERENCE_FREQUENCY_ELT_ID: {
10947258416cSSolomon Peachy 			u16 clk = le16_to_cpu(*((__le16 *)(p + 2)));
1095a910e4a9SSolomon Peachy 			if (clk != priv->hw_refclk)
1096a910e4a9SSolomon Peachy 				pr_warn("SDD file doesn't match configured refclk (%d vs %d)\n",
1097a910e4a9SSolomon Peachy 					clk, priv->hw_refclk);
1098a910e4a9SSolomon Peachy 			break;
1099a910e4a9SSolomon Peachy 		}
1100a910e4a9SSolomon Peachy 		default:
1101a910e4a9SSolomon Peachy 			break;
1102a910e4a9SSolomon Peachy 		}
1103a910e4a9SSolomon Peachy 		p += p[1] + 2;
1104a910e4a9SSolomon Peachy 	}
1105a910e4a9SSolomon Peachy 
1106a910e4a9SSolomon Peachy 	if (!priv->bt_present) {
1107a910e4a9SSolomon Peachy 		pr_debug("PTA element NOT found.\n");
1108a910e4a9SSolomon Peachy 		priv->conf_listen_interval = 0;
1109a910e4a9SSolomon Peachy 	}
1110a910e4a9SSolomon Peachy 	return ret;
1111a910e4a9SSolomon Peachy }
1112a910e4a9SSolomon Peachy 
1113a910e4a9SSolomon Peachy int cw1200_setup_mac(struct cw1200_common *priv)
1114a910e4a9SSolomon Peachy {
1115a910e4a9SSolomon Peachy 	int ret = 0;
1116a910e4a9SSolomon Peachy 
1117a910e4a9SSolomon Peachy 	/* NOTE: There is a bug in FW: it reports signal
1118a910e4a9SSolomon Peachy 	 * as RSSI if RSSI subscription is enabled.
1119a910e4a9SSolomon Peachy 	 * It's not enough to set WSM_RCPI_RSSI_USE_RSSI.
1120a910e4a9SSolomon Peachy 	 *
1121a910e4a9SSolomon Peachy 	 * NOTE2: RSSI based reports have been switched to RCPI, since
1122a910e4a9SSolomon Peachy 	 * FW has a bug and RSSI reported values are not stable,
1123f4bd758fSYangtao Li 	 * what can lead to signal level oscilations in user-end applications
1124a910e4a9SSolomon Peachy 	 */
1125a910e4a9SSolomon Peachy 	struct wsm_rcpi_rssi_threshold threshold = {
1126a910e4a9SSolomon Peachy 		.rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
1127a910e4a9SSolomon Peachy 		WSM_RCPI_RSSI_DONT_USE_UPPER |
1128a910e4a9SSolomon Peachy 		WSM_RCPI_RSSI_DONT_USE_LOWER,
1129a910e4a9SSolomon Peachy 		.rollingAverageCount = 16,
1130a910e4a9SSolomon Peachy 	};
1131a910e4a9SSolomon Peachy 
1132a910e4a9SSolomon Peachy 	struct wsm_configuration cfg = {
1133a910e4a9SSolomon Peachy 		.dot11StationId = &priv->mac_addr[0],
1134a910e4a9SSolomon Peachy 	};
1135a910e4a9SSolomon Peachy 
1136a910e4a9SSolomon Peachy 	/* Remember the decission here to make sure, we will handle
1137a910e4a9SSolomon Peachy 	 * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS
1138a910e4a9SSolomon Peachy 	 */
1139a910e4a9SSolomon Peachy 	if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI)
1140a910e4a9SSolomon Peachy 		priv->cqm_use_rssi = true;
1141a910e4a9SSolomon Peachy 
1142a910e4a9SSolomon Peachy 	if (!priv->sdd) {
1143a910e4a9SSolomon Peachy 		ret = request_firmware(&priv->sdd, priv->sdd_path, priv->pdev);
1144a910e4a9SSolomon Peachy 		if (ret) {
1145a910e4a9SSolomon Peachy 			pr_err("Can't load sdd file %s.\n", priv->sdd_path);
1146a910e4a9SSolomon Peachy 			return ret;
1147a910e4a9SSolomon Peachy 		}
1148a910e4a9SSolomon Peachy 		cw1200_parse_sdd_file(priv);
1149a910e4a9SSolomon Peachy 	}
1150a910e4a9SSolomon Peachy 
1151a910e4a9SSolomon Peachy 	cfg.dpdData = priv->sdd->data;
1152a910e4a9SSolomon Peachy 	cfg.dpdData_size = priv->sdd->size;
1153a910e4a9SSolomon Peachy 	ret = wsm_configuration(priv, &cfg);
1154a910e4a9SSolomon Peachy 	if (ret)
1155a910e4a9SSolomon Peachy 		return ret;
1156a910e4a9SSolomon Peachy 
1157a910e4a9SSolomon Peachy 	/* Configure RSSI/SCPI reporting as RSSI. */
1158a910e4a9SSolomon Peachy 	wsm_set_rcpi_rssi_threshold(priv, &threshold);
1159a910e4a9SSolomon Peachy 
1160a910e4a9SSolomon Peachy 	return 0;
1161a910e4a9SSolomon Peachy }
1162a910e4a9SSolomon Peachy 
1163a910e4a9SSolomon Peachy static void cw1200_join_complete(struct cw1200_common *priv)
1164a910e4a9SSolomon Peachy {
1165a910e4a9SSolomon Peachy 	pr_debug("[STA] Join complete (%d)\n", priv->join_complete_status);
1166a910e4a9SSolomon Peachy 
1167a910e4a9SSolomon Peachy 	priv->join_pending = false;
1168a910e4a9SSolomon Peachy 	if (priv->join_complete_status) {
1169a910e4a9SSolomon Peachy 		priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
1170a910e4a9SSolomon Peachy 		cw1200_update_listening(priv, priv->listening);
1171a910e4a9SSolomon Peachy 		cw1200_do_unjoin(priv);
1172a910e4a9SSolomon Peachy 		ieee80211_connection_loss(priv->vif);
1173a910e4a9SSolomon Peachy 	} else {
1174a910e4a9SSolomon Peachy 		if (priv->mode == NL80211_IFTYPE_ADHOC)
1175a910e4a9SSolomon Peachy 			priv->join_status = CW1200_JOIN_STATUS_IBSS;
1176a910e4a9SSolomon Peachy 		else
1177a910e4a9SSolomon Peachy 			priv->join_status = CW1200_JOIN_STATUS_PRE_STA;
1178a910e4a9SSolomon Peachy 	}
1179a910e4a9SSolomon Peachy 	wsm_unlock_tx(priv); /* Clearing the lock held before do_join() */
1180a910e4a9SSolomon Peachy }
1181a910e4a9SSolomon Peachy 
1182a910e4a9SSolomon Peachy void cw1200_join_complete_work(struct work_struct *work)
1183a910e4a9SSolomon Peachy {
1184a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
1185a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common, join_complete_work);
1186a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
1187a910e4a9SSolomon Peachy 	cw1200_join_complete(priv);
1188a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
1189a910e4a9SSolomon Peachy }
1190a910e4a9SSolomon Peachy 
1191a910e4a9SSolomon Peachy void cw1200_join_complete_cb(struct cw1200_common *priv,
1192a910e4a9SSolomon Peachy 			     struct wsm_join_complete *arg)
1193a910e4a9SSolomon Peachy {
1194a910e4a9SSolomon Peachy 	pr_debug("[STA] cw1200_join_complete_cb called, status=%d.\n",
1195a910e4a9SSolomon Peachy 		 arg->status);
1196a910e4a9SSolomon Peachy 
1197a910e4a9SSolomon Peachy 	if (cancel_delayed_work(&priv->join_timeout)) {
1198a910e4a9SSolomon Peachy 		priv->join_complete_status = arg->status;
1199a910e4a9SSolomon Peachy 		queue_work(priv->workqueue, &priv->join_complete_work);
1200a910e4a9SSolomon Peachy 	}
1201a910e4a9SSolomon Peachy }
1202a910e4a9SSolomon Peachy 
1203a910e4a9SSolomon Peachy /* MUST be called with tx_lock held!  It will be unlocked for us. */
1204a910e4a9SSolomon Peachy static void cw1200_do_join(struct cw1200_common *priv)
1205a910e4a9SSolomon Peachy {
1206a910e4a9SSolomon Peachy 	const u8 *bssid;
1207a910e4a9SSolomon Peachy 	struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
1208a910e4a9SSolomon Peachy 	struct cfg80211_bss *bss = NULL;
1209a910e4a9SSolomon Peachy 	struct wsm_protected_mgmt_policy mgmt_policy;
1210a910e4a9SSolomon Peachy 	struct wsm_join join = {
1211a910e4a9SSolomon Peachy 		.mode = conf->ibss_joined ?
1212a910e4a9SSolomon Peachy 				WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS,
1213a910e4a9SSolomon Peachy 		.preamble_type = WSM_JOIN_PREAMBLE_LONG,
1214a910e4a9SSolomon Peachy 		.probe_for_join = 1,
1215a910e4a9SSolomon Peachy 		.atim_window = 0,
1216a910e4a9SSolomon Peachy 		.basic_rate_set = cw1200_rate_mask_to_wsm(priv,
1217a910e4a9SSolomon Peachy 							  conf->basic_rates),
1218a910e4a9SSolomon Peachy 	};
1219a910e4a9SSolomon Peachy 	if (delayed_work_pending(&priv->join_timeout)) {
1220a910e4a9SSolomon Peachy 		pr_warn("[STA] - Join request already pending, skipping..\n");
1221a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
1222a910e4a9SSolomon Peachy 		return;
1223a910e4a9SSolomon Peachy 	}
1224a910e4a9SSolomon Peachy 
1225a910e4a9SSolomon Peachy 	if (priv->join_status)
1226a910e4a9SSolomon Peachy 		cw1200_do_unjoin(priv);
1227a910e4a9SSolomon Peachy 
1228a910e4a9SSolomon Peachy 	bssid = priv->vif->bss_conf.bssid;
1229a910e4a9SSolomon Peachy 
12306eb18137SDedy Lansky 	bss = cfg80211_get_bss(priv->hw->wiphy, priv->channel, bssid, NULL, 0,
12316eb18137SDedy Lansky 			       IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
1232a910e4a9SSolomon Peachy 
1233a910e4a9SSolomon Peachy 	if (!bss && !conf->ibss_joined) {
1234a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
1235a910e4a9SSolomon Peachy 		return;
1236a910e4a9SSolomon Peachy 	}
1237a910e4a9SSolomon Peachy 
1238a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
1239a910e4a9SSolomon Peachy 
1240a910e4a9SSolomon Peachy 	/* Under the conf lock: check scan status and
1241a910e4a9SSolomon Peachy 	 * bail out if it is in progress.
1242a910e4a9SSolomon Peachy 	 */
1243a910e4a9SSolomon Peachy 	if (atomic_read(&priv->scan.in_progress)) {
1244a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
1245a910e4a9SSolomon Peachy 		goto done_put;
1246a910e4a9SSolomon Peachy 	}
1247a910e4a9SSolomon Peachy 
1248a910e4a9SSolomon Peachy 	priv->join_pending = true;
1249a910e4a9SSolomon Peachy 
1250a910e4a9SSolomon Peachy 	/* Sanity check basic rates */
1251a910e4a9SSolomon Peachy 	if (!join.basic_rate_set)
1252a910e4a9SSolomon Peachy 		join.basic_rate_set = 7;
1253a910e4a9SSolomon Peachy 
1254a910e4a9SSolomon Peachy 	/* Sanity check beacon interval */
1255a910e4a9SSolomon Peachy 	if (!priv->beacon_int)
1256a910e4a9SSolomon Peachy 		priv->beacon_int = 1;
1257a910e4a9SSolomon Peachy 
1258a910e4a9SSolomon Peachy 	join.beacon_interval = priv->beacon_int;
1259a910e4a9SSolomon Peachy 
1260a910e4a9SSolomon Peachy 	/* BT Coex related changes */
1261a910e4a9SSolomon Peachy 	if (priv->bt_present) {
1262a910e4a9SSolomon Peachy 		if (((priv->conf_listen_interval * 100) %
1263a910e4a9SSolomon Peachy 		     priv->beacon_int) == 0)
1264a910e4a9SSolomon Peachy 			priv->listen_interval =
1265a910e4a9SSolomon Peachy 				((priv->conf_listen_interval * 100) /
1266a910e4a9SSolomon Peachy 				 priv->beacon_int);
1267a910e4a9SSolomon Peachy 		else
1268a910e4a9SSolomon Peachy 			priv->listen_interval =
1269a910e4a9SSolomon Peachy 				((priv->conf_listen_interval * 100) /
1270a910e4a9SSolomon Peachy 				 priv->beacon_int + 1);
1271a910e4a9SSolomon Peachy 	}
1272a910e4a9SSolomon Peachy 
1273a910e4a9SSolomon Peachy 	if (priv->hw->conf.ps_dtim_period)
1274a910e4a9SSolomon Peachy 		priv->join_dtim_period = priv->hw->conf.ps_dtim_period;
1275a910e4a9SSolomon Peachy 	join.dtim_period = priv->join_dtim_period;
1276a910e4a9SSolomon Peachy 
1277a910e4a9SSolomon Peachy 	join.channel_number = priv->channel->hw_value;
127857fbcce3SJohannes Berg 	join.band = (priv->channel->band == NL80211_BAND_5GHZ) ?
1279a910e4a9SSolomon Peachy 		WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
1280a910e4a9SSolomon Peachy 
1281a910e4a9SSolomon Peachy 	memcpy(join.bssid, bssid, sizeof(join.bssid));
1282a910e4a9SSolomon Peachy 
1283a910e4a9SSolomon Peachy 	pr_debug("[STA] Join BSSID: %pM DTIM: %d, interval: %d\n",
1284a910e4a9SSolomon Peachy 		 join.bssid,
1285a910e4a9SSolomon Peachy 		 join.dtim_period, priv->beacon_int);
1286a910e4a9SSolomon Peachy 
1287a910e4a9SSolomon Peachy 	if (!conf->ibss_joined) {
1288a910e4a9SSolomon Peachy 		const u8 *ssidie;
1289a910e4a9SSolomon Peachy 		rcu_read_lock();
1290a910e4a9SSolomon Peachy 		ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
1291a910e4a9SSolomon Peachy 		if (ssidie) {
1292a910e4a9SSolomon Peachy 			join.ssid_len = ssidie[1];
1293a910e4a9SSolomon Peachy 			memcpy(join.ssid, &ssidie[2], join.ssid_len);
1294a910e4a9SSolomon Peachy 		}
1295a910e4a9SSolomon Peachy 		rcu_read_unlock();
1296a910e4a9SSolomon Peachy 	}
1297a910e4a9SSolomon Peachy 
1298a910e4a9SSolomon Peachy 	if (priv->vif->p2p) {
1299a910e4a9SSolomon Peachy 		join.flags |= WSM_JOIN_FLAGS_P2P_GO;
1300a910e4a9SSolomon Peachy 		join.basic_rate_set =
1301a910e4a9SSolomon Peachy 			cw1200_rate_mask_to_wsm(priv, 0xFF0);
1302a910e4a9SSolomon Peachy 	}
1303a910e4a9SSolomon Peachy 
1304a910e4a9SSolomon Peachy 	/* Enable asynchronous join calls */
1305a910e4a9SSolomon Peachy 	if (!conf->ibss_joined) {
1306a910e4a9SSolomon Peachy 		join.flags |= WSM_JOIN_FLAGS_FORCE;
1307a910e4a9SSolomon Peachy 		join.flags |= WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND;
1308a910e4a9SSolomon Peachy 	}
1309a910e4a9SSolomon Peachy 
1310a910e4a9SSolomon Peachy 	wsm_flush_tx(priv);
1311a910e4a9SSolomon Peachy 
1312a910e4a9SSolomon Peachy 	/* Stay Awake for Join and Auth Timeouts and a bit more */
1313a910e4a9SSolomon Peachy 	cw1200_pm_stay_awake(&priv->pm_state,
1314a910e4a9SSolomon Peachy 			     CW1200_JOIN_TIMEOUT + CW1200_AUTH_TIMEOUT);
1315a910e4a9SSolomon Peachy 
1316a910e4a9SSolomon Peachy 	cw1200_update_listening(priv, false);
1317a910e4a9SSolomon Peachy 
1318a910e4a9SSolomon Peachy 	/* Turn on Block ACKs */
1319a910e4a9SSolomon Peachy 	wsm_set_block_ack_policy(priv, priv->ba_tx_tid_mask,
1320a910e4a9SSolomon Peachy 				 priv->ba_rx_tid_mask);
1321a910e4a9SSolomon Peachy 
1322a910e4a9SSolomon Peachy 	/* Set up timeout */
1323a910e4a9SSolomon Peachy 	if (join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND) {
1324a910e4a9SSolomon Peachy 		priv->join_status = CW1200_JOIN_STATUS_JOINING;
1325a910e4a9SSolomon Peachy 		queue_delayed_work(priv->workqueue,
1326a910e4a9SSolomon Peachy 				   &priv->join_timeout,
1327a910e4a9SSolomon Peachy 				   CW1200_JOIN_TIMEOUT);
1328a910e4a9SSolomon Peachy 	}
1329a910e4a9SSolomon Peachy 
1330a910e4a9SSolomon Peachy 	/* 802.11w protected mgmt frames */
1331a910e4a9SSolomon Peachy 	mgmt_policy.protectedMgmtEnable = 0;
1332a910e4a9SSolomon Peachy 	mgmt_policy.unprotectedMgmtFramesAllowed = 1;
1333a910e4a9SSolomon Peachy 	mgmt_policy.encryptionForAuthFrame = 1;
1334a910e4a9SSolomon Peachy 	wsm_set_protected_mgmt_policy(priv, &mgmt_policy);
1335a910e4a9SSolomon Peachy 
1336a910e4a9SSolomon Peachy 	/* Perform actual join */
1337a910e4a9SSolomon Peachy 	if (wsm_join(priv, &join)) {
1338a910e4a9SSolomon Peachy 		pr_err("[STA] cw1200_join_work: wsm_join failed!\n");
1339a910e4a9SSolomon Peachy 		cancel_delayed_work_sync(&priv->join_timeout);
1340a910e4a9SSolomon Peachy 		cw1200_update_listening(priv, priv->listening);
1341a910e4a9SSolomon Peachy 		/* Tx lock still held, unjoin will clear it. */
1342a910e4a9SSolomon Peachy 		if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
1343a910e4a9SSolomon Peachy 			wsm_unlock_tx(priv);
1344a910e4a9SSolomon Peachy 	} else {
1345a910e4a9SSolomon Peachy 		if (!(join.flags & WSM_JOIN_FLAGS_FORCE_WITH_COMPLETE_IND))
1346a910e4a9SSolomon Peachy 			cw1200_join_complete(priv); /* Will clear tx_lock */
1347a910e4a9SSolomon Peachy 
1348a910e4a9SSolomon Peachy 		/* Upload keys */
1349a910e4a9SSolomon Peachy 		cw1200_upload_keys(priv);
1350a910e4a9SSolomon Peachy 
1351a910e4a9SSolomon Peachy 		/* Due to beacon filtering it is possible that the
1352a910e4a9SSolomon Peachy 		 * AP's beacon is not known for the mac80211 stack.
1353a910e4a9SSolomon Peachy 		 * Disable filtering temporary to make sure the stack
1354a910e4a9SSolomon Peachy 		 * receives at least one
1355a910e4a9SSolomon Peachy 		 */
1356a910e4a9SSolomon Peachy 		priv->disable_beacon_filter = true;
1357a910e4a9SSolomon Peachy 	}
1358a910e4a9SSolomon Peachy 	cw1200_update_filtering(priv);
1359a910e4a9SSolomon Peachy 
1360a910e4a9SSolomon Peachy done_put:
1361a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
1362a910e4a9SSolomon Peachy 	if (bss)
1363a910e4a9SSolomon Peachy 		cfg80211_put_bss(priv->hw->wiphy, bss);
1364a910e4a9SSolomon Peachy }
1365a910e4a9SSolomon Peachy 
1366a910e4a9SSolomon Peachy void cw1200_join_timeout(struct work_struct *work)
1367a910e4a9SSolomon Peachy {
1368a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
1369a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common, join_timeout.work);
1370a910e4a9SSolomon Peachy 	pr_debug("[WSM] Join timed out.\n");
1371a910e4a9SSolomon Peachy 	wsm_lock_tx(priv);
1372a910e4a9SSolomon Peachy 	if (queue_work(priv->workqueue, &priv->unjoin_work) <= 0)
1373a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
1374a910e4a9SSolomon Peachy }
1375a910e4a9SSolomon Peachy 
1376a910e4a9SSolomon Peachy static void cw1200_do_unjoin(struct cw1200_common *priv)
1377a910e4a9SSolomon Peachy {
1378a910e4a9SSolomon Peachy 	struct wsm_reset reset = {
1379a910e4a9SSolomon Peachy 		.reset_statistics = true,
1380a910e4a9SSolomon Peachy 	};
1381a910e4a9SSolomon Peachy 
1382a910e4a9SSolomon Peachy 	cancel_delayed_work_sync(&priv->join_timeout);
1383a910e4a9SSolomon Peachy 
1384a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
1385a910e4a9SSolomon Peachy 	priv->join_pending = false;
1386a910e4a9SSolomon Peachy 
1387a910e4a9SSolomon Peachy 	if (atomic_read(&priv->scan.in_progress)) {
1388a910e4a9SSolomon Peachy 		if (priv->delayed_unjoin)
1389a910e4a9SSolomon Peachy 			wiphy_dbg(priv->hw->wiphy, "Delayed unjoin is already scheduled.\n");
1390a910e4a9SSolomon Peachy 		else
1391a910e4a9SSolomon Peachy 			priv->delayed_unjoin = true;
1392a910e4a9SSolomon Peachy 		goto done;
1393a910e4a9SSolomon Peachy 	}
1394a910e4a9SSolomon Peachy 
1395a910e4a9SSolomon Peachy 	priv->delayed_link_loss = false;
1396a910e4a9SSolomon Peachy 
1397a910e4a9SSolomon Peachy 	if (!priv->join_status)
1398a910e4a9SSolomon Peachy 		goto done;
1399a910e4a9SSolomon Peachy 
14005a6e0cf7SSolomon Peachy 	if (priv->join_status == CW1200_JOIN_STATUS_AP)
14015a6e0cf7SSolomon Peachy 		goto done;
1402a910e4a9SSolomon Peachy 
1403a910e4a9SSolomon Peachy 	cancel_work_sync(&priv->update_filtering_work);
1404a910e4a9SSolomon Peachy 	cancel_work_sync(&priv->set_beacon_wakeup_period_work);
1405a910e4a9SSolomon Peachy 	priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
1406a910e4a9SSolomon Peachy 
1407a910e4a9SSolomon Peachy 	/* Unjoin is a reset. */
1408a910e4a9SSolomon Peachy 	wsm_flush_tx(priv);
1409a910e4a9SSolomon Peachy 	wsm_keep_alive_period(priv, 0);
1410a910e4a9SSolomon Peachy 	wsm_reset(priv, &reset);
1411a910e4a9SSolomon Peachy 	wsm_set_output_power(priv, priv->output_power * 10);
1412a910e4a9SSolomon Peachy 	priv->join_dtim_period = 0;
1413a910e4a9SSolomon Peachy 	cw1200_setup_mac(priv);
1414a910e4a9SSolomon Peachy 	cw1200_free_event_queue(priv);
1415a910e4a9SSolomon Peachy 	cancel_work_sync(&priv->event_handler);
1416a910e4a9SSolomon Peachy 	cw1200_update_listening(priv, priv->listening);
1417a910e4a9SSolomon Peachy 	cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
1418a910e4a9SSolomon Peachy 
1419a910e4a9SSolomon Peachy 	/* Disable Block ACKs */
1420a910e4a9SSolomon Peachy 	wsm_set_block_ack_policy(priv, 0, 0);
1421a910e4a9SSolomon Peachy 
1422a910e4a9SSolomon Peachy 	priv->disable_beacon_filter = false;
1423a910e4a9SSolomon Peachy 	cw1200_update_filtering(priv);
1424a910e4a9SSolomon Peachy 	memset(&priv->association_mode, 0,
1425a910e4a9SSolomon Peachy 	       sizeof(priv->association_mode));
1426a910e4a9SSolomon Peachy 	memset(&priv->bss_params, 0, sizeof(priv->bss_params));
1427a910e4a9SSolomon Peachy 	priv->setbssparams_done = false;
1428a910e4a9SSolomon Peachy 	memset(&priv->firmware_ps_mode, 0,
1429a910e4a9SSolomon Peachy 	       sizeof(priv->firmware_ps_mode));
1430a910e4a9SSolomon Peachy 
1431a910e4a9SSolomon Peachy 	pr_debug("[STA] Unjoin completed.\n");
1432a910e4a9SSolomon Peachy 
1433a910e4a9SSolomon Peachy done:
1434a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
1435a910e4a9SSolomon Peachy }
1436a910e4a9SSolomon Peachy 
1437a910e4a9SSolomon Peachy void cw1200_unjoin_work(struct work_struct *work)
1438a910e4a9SSolomon Peachy {
1439a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
1440a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common, unjoin_work);
1441a910e4a9SSolomon Peachy 
1442a910e4a9SSolomon Peachy 	cw1200_do_unjoin(priv);
1443a910e4a9SSolomon Peachy 
1444a910e4a9SSolomon Peachy 	/* Tell the stack we're dead */
1445a910e4a9SSolomon Peachy 	ieee80211_connection_loss(priv->vif);
1446a910e4a9SSolomon Peachy 
1447a910e4a9SSolomon Peachy 	wsm_unlock_tx(priv);
1448a910e4a9SSolomon Peachy }
1449a910e4a9SSolomon Peachy 
1450a910e4a9SSolomon Peachy int cw1200_enable_listening(struct cw1200_common *priv)
1451a910e4a9SSolomon Peachy {
1452a910e4a9SSolomon Peachy 	struct wsm_start start = {
1453a910e4a9SSolomon Peachy 		.mode = WSM_START_MODE_P2P_DEV,
1454a910e4a9SSolomon Peachy 		.band = WSM_PHY_BAND_2_4G,
1455a910e4a9SSolomon Peachy 		.beacon_interval = 100,
1456a910e4a9SSolomon Peachy 		.dtim_period = 1,
1457a910e4a9SSolomon Peachy 		.probe_delay = 0,
1458a910e4a9SSolomon Peachy 		.basic_rate_set = 0x0F,
1459a910e4a9SSolomon Peachy 	};
1460a910e4a9SSolomon Peachy 
1461a910e4a9SSolomon Peachy 	if (priv->channel) {
146257fbcce3SJohannes Berg 		start.band = priv->channel->band == NL80211_BAND_5GHZ ?
1463a910e4a9SSolomon Peachy 			     WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
1464a910e4a9SSolomon Peachy 		start.channel_number = priv->channel->hw_value;
1465a910e4a9SSolomon Peachy 	} else {
1466a910e4a9SSolomon Peachy 		start.band = WSM_PHY_BAND_2_4G;
1467a910e4a9SSolomon Peachy 		start.channel_number = 1;
1468a910e4a9SSolomon Peachy 	}
1469a910e4a9SSolomon Peachy 
1470a910e4a9SSolomon Peachy 	return wsm_start(priv, &start);
1471a910e4a9SSolomon Peachy }
1472a910e4a9SSolomon Peachy 
1473a910e4a9SSolomon Peachy int cw1200_disable_listening(struct cw1200_common *priv)
1474a910e4a9SSolomon Peachy {
1475a910e4a9SSolomon Peachy 	int ret;
1476a910e4a9SSolomon Peachy 	struct wsm_reset reset = {
1477a910e4a9SSolomon Peachy 		.reset_statistics = true,
1478a910e4a9SSolomon Peachy 	};
1479a910e4a9SSolomon Peachy 	ret = wsm_reset(priv, &reset);
1480a910e4a9SSolomon Peachy 	return ret;
1481a910e4a9SSolomon Peachy }
1482a910e4a9SSolomon Peachy 
1483a910e4a9SSolomon Peachy void cw1200_update_listening(struct cw1200_common *priv, bool enabled)
1484a910e4a9SSolomon Peachy {
1485a910e4a9SSolomon Peachy 	if (enabled) {
1486a910e4a9SSolomon Peachy 		if (priv->join_status == CW1200_JOIN_STATUS_PASSIVE) {
1487a910e4a9SSolomon Peachy 			if (!cw1200_enable_listening(priv))
1488a910e4a9SSolomon Peachy 				priv->join_status = CW1200_JOIN_STATUS_MONITOR;
1489a910e4a9SSolomon Peachy 			wsm_set_probe_responder(priv, true);
1490a910e4a9SSolomon Peachy 		}
1491a910e4a9SSolomon Peachy 	} else {
1492a910e4a9SSolomon Peachy 		if (priv->join_status == CW1200_JOIN_STATUS_MONITOR) {
1493a910e4a9SSolomon Peachy 			if (!cw1200_disable_listening(priv))
1494a910e4a9SSolomon Peachy 				priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
1495a910e4a9SSolomon Peachy 			wsm_set_probe_responder(priv, false);
1496a910e4a9SSolomon Peachy 		}
1497a910e4a9SSolomon Peachy 	}
1498a910e4a9SSolomon Peachy }
1499a910e4a9SSolomon Peachy 
1500a910e4a9SSolomon Peachy int cw1200_set_uapsd_param(struct cw1200_common *priv,
1501a910e4a9SSolomon Peachy 			   const struct wsm_edca_params *arg)
1502a910e4a9SSolomon Peachy {
1503a910e4a9SSolomon Peachy 	int ret;
1504a910e4a9SSolomon Peachy 	u16 uapsd_flags = 0;
1505a910e4a9SSolomon Peachy 
1506a910e4a9SSolomon Peachy 	/* Here's the mapping AC [queue, bit]
1507a910e4a9SSolomon Peachy 	 *  VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]
1508a910e4a9SSolomon Peachy 	 */
1509a910e4a9SSolomon Peachy 
1510a910e4a9SSolomon Peachy 	if (arg->uapsd_enable[0])
1511a910e4a9SSolomon Peachy 		uapsd_flags |= 1 << 3;
1512a910e4a9SSolomon Peachy 
1513a910e4a9SSolomon Peachy 	if (arg->uapsd_enable[1])
1514a910e4a9SSolomon Peachy 		uapsd_flags |= 1 << 2;
1515a910e4a9SSolomon Peachy 
1516a910e4a9SSolomon Peachy 	if (arg->uapsd_enable[2])
1517a910e4a9SSolomon Peachy 		uapsd_flags |= 1 << 1;
1518a910e4a9SSolomon Peachy 
1519a910e4a9SSolomon Peachy 	if (arg->uapsd_enable[3])
1520a910e4a9SSolomon Peachy 		uapsd_flags |= 1;
1521a910e4a9SSolomon Peachy 
1522a910e4a9SSolomon Peachy 	/* Currently pseudo U-APSD operation is not supported, so setting
1523a910e4a9SSolomon Peachy 	 * MinAutoTriggerInterval, MaxAutoTriggerInterval and
1524a910e4a9SSolomon Peachy 	 * AutoTriggerStep to 0
1525a910e4a9SSolomon Peachy 	 */
1526a910e4a9SSolomon Peachy 
1527a910e4a9SSolomon Peachy 	priv->uapsd_info.uapsd_flags = cpu_to_le16(uapsd_flags);
1528a910e4a9SSolomon Peachy 	priv->uapsd_info.min_auto_trigger_interval = 0;
1529a910e4a9SSolomon Peachy 	priv->uapsd_info.max_auto_trigger_interval = 0;
1530a910e4a9SSolomon Peachy 	priv->uapsd_info.auto_trigger_step = 0;
1531a910e4a9SSolomon Peachy 
1532a910e4a9SSolomon Peachy 	ret = wsm_set_uapsd_info(priv, &priv->uapsd_info);
1533a910e4a9SSolomon Peachy 	return ret;
1534a910e4a9SSolomon Peachy }
1535a910e4a9SSolomon Peachy 
1536a910e4a9SSolomon Peachy /* ******************************************************************** */
1537a910e4a9SSolomon Peachy /* AP API								*/
1538a910e4a9SSolomon Peachy 
1539a910e4a9SSolomon Peachy int cw1200_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1540a910e4a9SSolomon Peachy 		   struct ieee80211_sta *sta)
1541a910e4a9SSolomon Peachy {
1542a910e4a9SSolomon Peachy 	struct cw1200_common *priv = hw->priv;
1543a910e4a9SSolomon Peachy 	struct cw1200_sta_priv *sta_priv =
1544a910e4a9SSolomon Peachy 			(struct cw1200_sta_priv *)&sta->drv_priv;
1545a910e4a9SSolomon Peachy 	struct cw1200_link_entry *entry;
1546a910e4a9SSolomon Peachy 	struct sk_buff *skb;
1547a910e4a9SSolomon Peachy 
1548a910e4a9SSolomon Peachy 	if (priv->mode != NL80211_IFTYPE_AP)
1549a910e4a9SSolomon Peachy 		return 0;
1550a910e4a9SSolomon Peachy 
1551a910e4a9SSolomon Peachy 	sta_priv->link_id = cw1200_find_link_id(priv, sta->addr);
1552a910e4a9SSolomon Peachy 	if (WARN_ON(!sta_priv->link_id)) {
1553a910e4a9SSolomon Peachy 		wiphy_info(priv->hw->wiphy,
1554a910e4a9SSolomon Peachy 			   "[AP] No more link IDs available.\n");
1555a910e4a9SSolomon Peachy 		return -ENOENT;
1556a910e4a9SSolomon Peachy 	}
1557a910e4a9SSolomon Peachy 
1558a910e4a9SSolomon Peachy 	entry = &priv->link_id_db[sta_priv->link_id - 1];
1559a910e4a9SSolomon Peachy 	spin_lock_bh(&priv->ps_state_lock);
1560a910e4a9SSolomon Peachy 	if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) ==
1561a910e4a9SSolomon Peachy 					IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
1562a910e4a9SSolomon Peachy 		priv->sta_asleep_mask |= BIT(sta_priv->link_id);
1563a910e4a9SSolomon Peachy 	entry->status = CW1200_LINK_HARD;
1564a910e4a9SSolomon Peachy 	while ((skb = skb_dequeue(&entry->rx_queue)))
1565a910e4a9SSolomon Peachy 		ieee80211_rx_irqsafe(priv->hw, skb);
1566a910e4a9SSolomon Peachy 	spin_unlock_bh(&priv->ps_state_lock);
1567a910e4a9SSolomon Peachy 	return 0;
1568a910e4a9SSolomon Peachy }
1569a910e4a9SSolomon Peachy 
1570a910e4a9SSolomon Peachy int cw1200_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1571a910e4a9SSolomon Peachy 		      struct ieee80211_sta *sta)
1572a910e4a9SSolomon Peachy {
1573a910e4a9SSolomon Peachy 	struct cw1200_common *priv = hw->priv;
1574a910e4a9SSolomon Peachy 	struct cw1200_sta_priv *sta_priv =
1575a910e4a9SSolomon Peachy 			(struct cw1200_sta_priv *)&sta->drv_priv;
1576a910e4a9SSolomon Peachy 	struct cw1200_link_entry *entry;
1577a910e4a9SSolomon Peachy 
1578a910e4a9SSolomon Peachy 	if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id)
1579a910e4a9SSolomon Peachy 		return 0;
1580a910e4a9SSolomon Peachy 
1581a910e4a9SSolomon Peachy 	entry = &priv->link_id_db[sta_priv->link_id - 1];
1582a910e4a9SSolomon Peachy 	spin_lock_bh(&priv->ps_state_lock);
1583a910e4a9SSolomon Peachy 	entry->status = CW1200_LINK_RESERVE;
1584a910e4a9SSolomon Peachy 	entry->timestamp = jiffies;
1585a910e4a9SSolomon Peachy 	wsm_lock_tx_async(priv);
1586a910e4a9SSolomon Peachy 	if (queue_work(priv->workqueue, &priv->link_id_work) <= 0)
1587a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
1588a910e4a9SSolomon Peachy 	spin_unlock_bh(&priv->ps_state_lock);
1589a910e4a9SSolomon Peachy 	flush_workqueue(priv->workqueue);
1590a910e4a9SSolomon Peachy 	return 0;
1591a910e4a9SSolomon Peachy }
1592a910e4a9SSolomon Peachy 
1593a910e4a9SSolomon Peachy static void __cw1200_sta_notify(struct ieee80211_hw *dev,
1594a910e4a9SSolomon Peachy 				struct ieee80211_vif *vif,
1595a910e4a9SSolomon Peachy 				enum sta_notify_cmd notify_cmd,
1596a910e4a9SSolomon Peachy 				int link_id)
1597a910e4a9SSolomon Peachy {
1598a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
1599a910e4a9SSolomon Peachy 	u32 bit, prev;
1600a910e4a9SSolomon Peachy 
1601a910e4a9SSolomon Peachy 	/* Zero link id means "for all link IDs" */
1602a910e4a9SSolomon Peachy 	if (link_id)
1603a910e4a9SSolomon Peachy 		bit = BIT(link_id);
1604a910e4a9SSolomon Peachy 	else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE))
1605a910e4a9SSolomon Peachy 		bit = 0;
1606a910e4a9SSolomon Peachy 	else
1607a910e4a9SSolomon Peachy 		bit = priv->link_id_map;
1608a910e4a9SSolomon Peachy 	prev = priv->sta_asleep_mask & bit;
1609a910e4a9SSolomon Peachy 
1610a910e4a9SSolomon Peachy 	switch (notify_cmd) {
1611a910e4a9SSolomon Peachy 	case STA_NOTIFY_SLEEP:
1612a910e4a9SSolomon Peachy 		if (!prev) {
1613a910e4a9SSolomon Peachy 			if (priv->buffered_multicasts &&
1614a910e4a9SSolomon Peachy 			    !priv->sta_asleep_mask)
1615a910e4a9SSolomon Peachy 				queue_work(priv->workqueue,
1616a910e4a9SSolomon Peachy 					   &priv->multicast_start_work);
1617a910e4a9SSolomon Peachy 			priv->sta_asleep_mask |= bit;
1618a910e4a9SSolomon Peachy 		}
1619a910e4a9SSolomon Peachy 		break;
1620a910e4a9SSolomon Peachy 	case STA_NOTIFY_AWAKE:
1621a910e4a9SSolomon Peachy 		if (prev) {
1622a910e4a9SSolomon Peachy 			priv->sta_asleep_mask &= ~bit;
1623a910e4a9SSolomon Peachy 			priv->pspoll_mask &= ~bit;
1624a910e4a9SSolomon Peachy 			if (priv->tx_multicast && link_id &&
1625a910e4a9SSolomon Peachy 			    !priv->sta_asleep_mask)
1626a910e4a9SSolomon Peachy 				queue_work(priv->workqueue,
1627a910e4a9SSolomon Peachy 					   &priv->multicast_stop_work);
1628a910e4a9SSolomon Peachy 			cw1200_bh_wakeup(priv);
1629a910e4a9SSolomon Peachy 		}
1630a910e4a9SSolomon Peachy 		break;
1631a910e4a9SSolomon Peachy 	}
1632a910e4a9SSolomon Peachy }
1633a910e4a9SSolomon Peachy 
1634a910e4a9SSolomon Peachy void cw1200_sta_notify(struct ieee80211_hw *dev,
1635a910e4a9SSolomon Peachy 		       struct ieee80211_vif *vif,
1636a910e4a9SSolomon Peachy 		       enum sta_notify_cmd notify_cmd,
1637a910e4a9SSolomon Peachy 		       struct ieee80211_sta *sta)
1638a910e4a9SSolomon Peachy {
1639a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
1640a910e4a9SSolomon Peachy 	struct cw1200_sta_priv *sta_priv =
1641a910e4a9SSolomon Peachy 		(struct cw1200_sta_priv *)&sta->drv_priv;
1642a910e4a9SSolomon Peachy 
1643a910e4a9SSolomon Peachy 	spin_lock_bh(&priv->ps_state_lock);
1644a910e4a9SSolomon Peachy 	__cw1200_sta_notify(dev, vif, notify_cmd, sta_priv->link_id);
1645a910e4a9SSolomon Peachy 	spin_unlock_bh(&priv->ps_state_lock);
1646a910e4a9SSolomon Peachy }
1647a910e4a9SSolomon Peachy 
1648a910e4a9SSolomon Peachy static void cw1200_ps_notify(struct cw1200_common *priv,
1649a910e4a9SSolomon Peachy 		      int link_id, bool ps)
1650a910e4a9SSolomon Peachy {
1651a910e4a9SSolomon Peachy 	if (link_id > CW1200_MAX_STA_IN_AP_MODE)
1652a910e4a9SSolomon Peachy 		return;
1653a910e4a9SSolomon Peachy 
1654a910e4a9SSolomon Peachy 	pr_debug("%s for LinkId: %d. STAs asleep: %.8X\n",
1655a910e4a9SSolomon Peachy 		 ps ? "Stop" : "Start",
1656a910e4a9SSolomon Peachy 		 link_id, priv->sta_asleep_mask);
1657a910e4a9SSolomon Peachy 
1658a910e4a9SSolomon Peachy 	__cw1200_sta_notify(priv->hw, priv->vif,
1659a910e4a9SSolomon Peachy 			    ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id);
1660a910e4a9SSolomon Peachy }
1661a910e4a9SSolomon Peachy 
1662a910e4a9SSolomon Peachy static int cw1200_set_tim_impl(struct cw1200_common *priv, bool aid0_bit_set)
1663a910e4a9SSolomon Peachy {
1664a910e4a9SSolomon Peachy 	struct sk_buff *skb;
1665a910e4a9SSolomon Peachy 	struct wsm_update_ie update_ie = {
1666a910e4a9SSolomon Peachy 		.what = WSM_UPDATE_IE_BEACON,
1667a910e4a9SSolomon Peachy 		.count = 1,
1668a910e4a9SSolomon Peachy 	};
1669a910e4a9SSolomon Peachy 	u16 tim_offset, tim_length;
1670a910e4a9SSolomon Peachy 
1671a910e4a9SSolomon Peachy 	pr_debug("[AP] mcast: %s.\n", aid0_bit_set ? "ena" : "dis");
1672a910e4a9SSolomon Peachy 
1673a910e4a9SSolomon Peachy 	skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
1674a910e4a9SSolomon Peachy 			&tim_offset, &tim_length);
1675a910e4a9SSolomon Peachy 	if (!skb) {
1676a910e4a9SSolomon Peachy 		if (!__cw1200_flush(priv, true))
1677a910e4a9SSolomon Peachy 			wsm_unlock_tx(priv);
1678a910e4a9SSolomon Peachy 		return -ENOENT;
1679a910e4a9SSolomon Peachy 	}
1680a910e4a9SSolomon Peachy 
1681a910e4a9SSolomon Peachy 	if (tim_offset && tim_length >= 6) {
1682a910e4a9SSolomon Peachy 		/* Ignore DTIM count from mac80211:
1683a910e4a9SSolomon Peachy 		 * firmware handles DTIM internally.
1684a910e4a9SSolomon Peachy 		 */
1685a910e4a9SSolomon Peachy 		skb->data[tim_offset + 2] = 0;
1686a910e4a9SSolomon Peachy 
1687a910e4a9SSolomon Peachy 		/* Set/reset aid0 bit */
1688a910e4a9SSolomon Peachy 		if (aid0_bit_set)
1689a910e4a9SSolomon Peachy 			skb->data[tim_offset + 4] |= 1;
1690a910e4a9SSolomon Peachy 		else
1691a910e4a9SSolomon Peachy 			skb->data[tim_offset + 4] &= ~1;
1692a910e4a9SSolomon Peachy 	}
1693a910e4a9SSolomon Peachy 
1694a910e4a9SSolomon Peachy 	update_ie.ies = &skb->data[tim_offset];
1695a910e4a9SSolomon Peachy 	update_ie.length = tim_length;
1696a910e4a9SSolomon Peachy 	wsm_update_ie(priv, &update_ie);
1697a910e4a9SSolomon Peachy 
1698a910e4a9SSolomon Peachy 	dev_kfree_skb(skb);
1699a910e4a9SSolomon Peachy 
1700a910e4a9SSolomon Peachy 	return 0;
1701a910e4a9SSolomon Peachy }
1702a910e4a9SSolomon Peachy 
1703a910e4a9SSolomon Peachy void cw1200_set_tim_work(struct work_struct *work)
1704a910e4a9SSolomon Peachy {
1705a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
1706a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common, set_tim_work);
1707a910e4a9SSolomon Peachy 	(void)cw1200_set_tim_impl(priv, priv->aid0_bit_set);
1708a910e4a9SSolomon Peachy }
1709a910e4a9SSolomon Peachy 
1710a910e4a9SSolomon Peachy int cw1200_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
1711a910e4a9SSolomon Peachy 		   bool set)
1712a910e4a9SSolomon Peachy {
1713a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
1714a910e4a9SSolomon Peachy 	queue_work(priv->workqueue, &priv->set_tim_work);
1715a910e4a9SSolomon Peachy 	return 0;
1716a910e4a9SSolomon Peachy }
1717a910e4a9SSolomon Peachy 
1718a910e4a9SSolomon Peachy void cw1200_set_cts_work(struct work_struct *work)
1719a910e4a9SSolomon Peachy {
1720a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
1721a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common, set_cts_work);
1722a910e4a9SSolomon Peachy 
1723a910e4a9SSolomon Peachy 	u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0};
1724a910e4a9SSolomon Peachy 	struct wsm_update_ie update_ie = {
1725a910e4a9SSolomon Peachy 		.what = WSM_UPDATE_IE_BEACON,
1726a910e4a9SSolomon Peachy 		.count = 1,
1727a910e4a9SSolomon Peachy 		.ies = erp_ie,
1728a910e4a9SSolomon Peachy 		.length = 3,
1729a910e4a9SSolomon Peachy 	};
1730a910e4a9SSolomon Peachy 	u32 erp_info;
1731a910e4a9SSolomon Peachy 	__le32 use_cts_prot;
1732a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
1733a910e4a9SSolomon Peachy 	erp_info = priv->erp_info;
1734a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
1735a910e4a9SSolomon Peachy 	use_cts_prot =
1736a910e4a9SSolomon Peachy 		erp_info & WLAN_ERP_USE_PROTECTION ?
1737a910e4a9SSolomon Peachy 		__cpu_to_le32(1) : 0;
1738a910e4a9SSolomon Peachy 
1739a910e4a9SSolomon Peachy 	erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info;
1740a910e4a9SSolomon Peachy 
1741a910e4a9SSolomon Peachy 	pr_debug("[STA] ERP information 0x%x\n", erp_info);
1742a910e4a9SSolomon Peachy 
1743a910e4a9SSolomon Peachy 	wsm_write_mib(priv, WSM_MIB_ID_NON_ERP_PROTECTION,
1744a910e4a9SSolomon Peachy 		      &use_cts_prot, sizeof(use_cts_prot));
1745a910e4a9SSolomon Peachy 	wsm_update_ie(priv, &update_ie);
1746a910e4a9SSolomon Peachy 
1747a910e4a9SSolomon Peachy 	return;
1748a910e4a9SSolomon Peachy }
1749a910e4a9SSolomon Peachy 
1750a910e4a9SSolomon Peachy static int cw1200_set_btcoexinfo(struct cw1200_common *priv)
1751a910e4a9SSolomon Peachy {
1752a910e4a9SSolomon Peachy 	struct wsm_override_internal_txrate arg;
1753a910e4a9SSolomon Peachy 	int ret = 0;
1754a910e4a9SSolomon Peachy 
1755a910e4a9SSolomon Peachy 	if (priv->mode == NL80211_IFTYPE_STATION) {
1756a910e4a9SSolomon Peachy 		/* Plumb PSPOLL and NULL template */
1757a910e4a9SSolomon Peachy 		cw1200_upload_pspoll(priv);
1758a910e4a9SSolomon Peachy 		cw1200_upload_null(priv);
1759a910e4a9SSolomon Peachy 		cw1200_upload_qosnull(priv);
1760a910e4a9SSolomon Peachy 	} else {
1761a910e4a9SSolomon Peachy 		return 0;
1762a910e4a9SSolomon Peachy 	}
1763a910e4a9SSolomon Peachy 
1764a910e4a9SSolomon Peachy 	memset(&arg, 0, sizeof(struct wsm_override_internal_txrate));
1765a910e4a9SSolomon Peachy 
1766a910e4a9SSolomon Peachy 	if (!priv->vif->p2p) {
1767a910e4a9SSolomon Peachy 		/* STATION mode */
1768a910e4a9SSolomon Peachy 		if (priv->bss_params.operational_rate_set & ~0xF) {
1769a910e4a9SSolomon Peachy 			pr_debug("[STA] STA has ERP rates\n");
1770a910e4a9SSolomon Peachy 			/* G or BG mode */
1771a910e4a9SSolomon Peachy 			arg.internalTxRate = (__ffs(
1772a910e4a9SSolomon Peachy 			priv->bss_params.operational_rate_set & ~0xF));
1773a910e4a9SSolomon Peachy 		} else {
1774a910e4a9SSolomon Peachy 			pr_debug("[STA] STA has non ERP rates\n");
1775a910e4a9SSolomon Peachy 			/* B only mode */
17767258416cSSolomon Peachy 			arg.internalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set)));
1777a910e4a9SSolomon Peachy 		}
17787258416cSSolomon Peachy 		arg.nonErpInternalTxRate = (__ffs(le32_to_cpu(priv->association_mode.basic_rate_set)));
1779a910e4a9SSolomon Peachy 	} else {
1780a910e4a9SSolomon Peachy 		/* P2P mode */
1781a910e4a9SSolomon Peachy 		arg.internalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
1782a910e4a9SSolomon Peachy 		arg.nonErpInternalTxRate = (__ffs(priv->bss_params.operational_rate_set & ~0xF));
1783a910e4a9SSolomon Peachy 	}
1784a910e4a9SSolomon Peachy 
1785a910e4a9SSolomon Peachy 	pr_debug("[STA] BTCOEX_INFO MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n",
1786a910e4a9SSolomon Peachy 		 priv->mode,
1787a910e4a9SSolomon Peachy 		 arg.internalTxRate,
1788a910e4a9SSolomon Peachy 		 arg.nonErpInternalTxRate);
1789a910e4a9SSolomon Peachy 
1790a910e4a9SSolomon Peachy 	ret = wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
1791a910e4a9SSolomon Peachy 			    &arg, sizeof(arg));
1792a910e4a9SSolomon Peachy 
1793a910e4a9SSolomon Peachy 	return ret;
1794a910e4a9SSolomon Peachy }
1795a910e4a9SSolomon Peachy 
1796a910e4a9SSolomon Peachy void cw1200_bss_info_changed(struct ieee80211_hw *dev,
1797a910e4a9SSolomon Peachy 			     struct ieee80211_vif *vif,
1798a910e4a9SSolomon Peachy 			     struct ieee80211_bss_conf *info,
1799a910e4a9SSolomon Peachy 			     u32 changed)
1800a910e4a9SSolomon Peachy {
1801a910e4a9SSolomon Peachy 	struct cw1200_common *priv = dev->priv;
1802a910e4a9SSolomon Peachy 	bool do_join = false;
1803a910e4a9SSolomon Peachy 
1804a910e4a9SSolomon Peachy 	mutex_lock(&priv->conf_mutex);
1805a910e4a9SSolomon Peachy 
1806a910e4a9SSolomon Peachy 	pr_debug("BSS CHANGED:  %08x\n", changed);
1807a910e4a9SSolomon Peachy 
1808a910e4a9SSolomon Peachy 	/* TODO: BSS_CHANGED_QOS */
1809a910e4a9SSolomon Peachy 	/* TODO: BSS_CHANGED_TXPOWER */
1810a910e4a9SSolomon Peachy 
1811a910e4a9SSolomon Peachy 	if (changed & BSS_CHANGED_ARP_FILTER) {
1812a910e4a9SSolomon Peachy 		struct wsm_mib_arp_ipv4_filter filter = {0};
1813a910e4a9SSolomon Peachy 		int i;
1814a910e4a9SSolomon Peachy 
1815a910e4a9SSolomon Peachy 		pr_debug("[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n",
1816a910e4a9SSolomon Peachy 			 info->arp_addr_cnt);
1817a910e4a9SSolomon Peachy 
1818a910e4a9SSolomon Peachy 		/* Currently only one IP address is supported by firmware.
1819a910e4a9SSolomon Peachy 		 * In case of more IPs arp filtering will be disabled.
1820a910e4a9SSolomon Peachy 		 */
1821a910e4a9SSolomon Peachy 		if (info->arp_addr_cnt > 0 &&
1822a910e4a9SSolomon Peachy 		    info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) {
1823a910e4a9SSolomon Peachy 			for (i = 0; i < info->arp_addr_cnt; i++) {
1824a910e4a9SSolomon Peachy 				filter.ipv4addrs[i] = info->arp_addr_list[i];
1825a910e4a9SSolomon Peachy 				pr_debug("[STA] addr[%d]: 0x%X\n",
1826a910e4a9SSolomon Peachy 					 i, filter.ipv4addrs[i]);
1827a910e4a9SSolomon Peachy 			}
1828a910e4a9SSolomon Peachy 			filter.enable = __cpu_to_le32(1);
1829a910e4a9SSolomon Peachy 		}
1830a910e4a9SSolomon Peachy 
1831a910e4a9SSolomon Peachy 		pr_debug("[STA] arp ip filter enable: %d\n",
1832a910e4a9SSolomon Peachy 			 __le32_to_cpu(filter.enable));
1833a910e4a9SSolomon Peachy 
1834a910e4a9SSolomon Peachy 		wsm_set_arp_ipv4_filter(priv, &filter);
1835a910e4a9SSolomon Peachy 	}
1836a910e4a9SSolomon Peachy 
1837a910e4a9SSolomon Peachy 	if (changed &
1838a910e4a9SSolomon Peachy 	    (BSS_CHANGED_BEACON |
1839a910e4a9SSolomon Peachy 	     BSS_CHANGED_AP_PROBE_RESP |
1840a910e4a9SSolomon Peachy 	     BSS_CHANGED_BSSID |
1841a910e4a9SSolomon Peachy 	     BSS_CHANGED_SSID |
1842a910e4a9SSolomon Peachy 	     BSS_CHANGED_IBSS)) {
1843a910e4a9SSolomon Peachy 		pr_debug("BSS_CHANGED_BEACON\n");
1844a910e4a9SSolomon Peachy 		priv->beacon_int = info->beacon_int;
1845a910e4a9SSolomon Peachy 		cw1200_update_beaconing(priv);
1846a910e4a9SSolomon Peachy 		cw1200_upload_beacon(priv);
1847a910e4a9SSolomon Peachy 	}
1848a910e4a9SSolomon Peachy 
1849a910e4a9SSolomon Peachy 	if (changed & BSS_CHANGED_BEACON_ENABLED) {
1850a910e4a9SSolomon Peachy 		pr_debug("BSS_CHANGED_BEACON_ENABLED (%d)\n", info->enable_beacon);
1851a910e4a9SSolomon Peachy 
1852a910e4a9SSolomon Peachy 		if (priv->enable_beacon != info->enable_beacon) {
1853a910e4a9SSolomon Peachy 			cw1200_enable_beaconing(priv, info->enable_beacon);
1854a910e4a9SSolomon Peachy 			priv->enable_beacon = info->enable_beacon;
1855a910e4a9SSolomon Peachy 		}
1856a910e4a9SSolomon Peachy 	}
1857a910e4a9SSolomon Peachy 
1858a910e4a9SSolomon Peachy 	if (changed & BSS_CHANGED_BEACON_INT) {
1859a910e4a9SSolomon Peachy 		pr_debug("CHANGED_BEACON_INT\n");
1860a910e4a9SSolomon Peachy 		if (info->ibss_joined)
1861a910e4a9SSolomon Peachy 			do_join = true;
1862a910e4a9SSolomon Peachy 		else if (priv->join_status == CW1200_JOIN_STATUS_AP)
1863a910e4a9SSolomon Peachy 			cw1200_update_beaconing(priv);
1864a910e4a9SSolomon Peachy 	}
1865a910e4a9SSolomon Peachy 
1866a910e4a9SSolomon Peachy 	/* assoc/disassoc, or maybe AID changed */
1867a910e4a9SSolomon Peachy 	if (changed & BSS_CHANGED_ASSOC) {
1868a910e4a9SSolomon Peachy 		wsm_lock_tx(priv);
1869a910e4a9SSolomon Peachy 		priv->wep_default_key_id = -1;
1870a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
1871a910e4a9SSolomon Peachy 	}
1872a910e4a9SSolomon Peachy 
1873a910e4a9SSolomon Peachy 	if (changed & BSS_CHANGED_BSSID) {
1874a910e4a9SSolomon Peachy 		pr_debug("BSS_CHANGED_BSSID\n");
1875a910e4a9SSolomon Peachy 		do_join = true;
1876a910e4a9SSolomon Peachy 	}
1877a910e4a9SSolomon Peachy 
1878a910e4a9SSolomon Peachy 	if (changed &
1879a910e4a9SSolomon Peachy 	    (BSS_CHANGED_ASSOC |
1880a910e4a9SSolomon Peachy 	     BSS_CHANGED_BSSID |
1881a910e4a9SSolomon Peachy 	     BSS_CHANGED_IBSS |
1882a910e4a9SSolomon Peachy 	     BSS_CHANGED_BASIC_RATES |
1883a910e4a9SSolomon Peachy 	     BSS_CHANGED_HT)) {
1884a910e4a9SSolomon Peachy 		pr_debug("BSS_CHANGED_ASSOC\n");
1885a910e4a9SSolomon Peachy 		if (info->assoc) {
1886a910e4a9SSolomon Peachy 			if (priv->join_status < CW1200_JOIN_STATUS_PRE_STA) {
1887a910e4a9SSolomon Peachy 				ieee80211_connection_loss(vif);
1888a910e4a9SSolomon Peachy 				mutex_unlock(&priv->conf_mutex);
1889a910e4a9SSolomon Peachy 				return;
1890a910e4a9SSolomon Peachy 			} else if (priv->join_status == CW1200_JOIN_STATUS_PRE_STA) {
1891a910e4a9SSolomon Peachy 				priv->join_status = CW1200_JOIN_STATUS_STA;
1892a910e4a9SSolomon Peachy 			}
1893a910e4a9SSolomon Peachy 		} else {
1894a910e4a9SSolomon Peachy 			do_join = true;
1895a910e4a9SSolomon Peachy 		}
1896a910e4a9SSolomon Peachy 
1897a910e4a9SSolomon Peachy 		if (info->assoc || info->ibss_joined) {
1898a910e4a9SSolomon Peachy 			struct ieee80211_sta *sta = NULL;
18997258416cSSolomon Peachy 			__le32 htprot = 0;
1900a910e4a9SSolomon Peachy 
1901a910e4a9SSolomon Peachy 			if (info->dtim_period)
1902a910e4a9SSolomon Peachy 				priv->join_dtim_period = info->dtim_period;
1903a910e4a9SSolomon Peachy 			priv->beacon_int = info->beacon_int;
1904a910e4a9SSolomon Peachy 
1905a910e4a9SSolomon Peachy 			rcu_read_lock();
1906a910e4a9SSolomon Peachy 
1907a910e4a9SSolomon Peachy 			if (info->bssid && !info->ibss_joined)
1908a910e4a9SSolomon Peachy 				sta = ieee80211_find_sta(vif, info->bssid);
1909a910e4a9SSolomon Peachy 			if (sta) {
1910*046d2e7cSSriram R 				priv->ht_info.ht_cap = sta->deflink.ht_cap;
1911a910e4a9SSolomon Peachy 				priv->bss_params.operational_rate_set =
1912a910e4a9SSolomon Peachy 					cw1200_rate_mask_to_wsm(priv,
1913*046d2e7cSSriram R 						sta->deflink.supp_rates[priv->channel->band]);
1914a910e4a9SSolomon Peachy 				priv->ht_info.channel_type = cfg80211_get_chandef_type(&dev->conf.chandef);
1915a910e4a9SSolomon Peachy 				priv->ht_info.operation_mode = info->ht_operation_mode;
1916a910e4a9SSolomon Peachy 			} else {
1917a910e4a9SSolomon Peachy 				memset(&priv->ht_info, 0,
1918a910e4a9SSolomon Peachy 				       sizeof(priv->ht_info));
1919a910e4a9SSolomon Peachy 				priv->bss_params.operational_rate_set = -1;
1920a910e4a9SSolomon Peachy 			}
1921a910e4a9SSolomon Peachy 			rcu_read_unlock();
1922a910e4a9SSolomon Peachy 
1923a910e4a9SSolomon Peachy 			/* Non Greenfield stations present */
1924a910e4a9SSolomon Peachy 			if (priv->ht_info.operation_mode &
1925a910e4a9SSolomon Peachy 			    IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)
19267258416cSSolomon Peachy 				htprot |= cpu_to_le32(WSM_NON_GREENFIELD_STA_PRESENT);
1927a910e4a9SSolomon Peachy 
1928a910e4a9SSolomon Peachy 			/* Set HT protection method */
19297258416cSSolomon Peachy 			htprot |= cpu_to_le32((priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_PROTECTION) << 2);
1930a910e4a9SSolomon Peachy 
1931a910e4a9SSolomon Peachy 			/* TODO:
1932a910e4a9SSolomon Peachy 			 * STBC_param.dual_cts
1933a910e4a9SSolomon Peachy 			 *  STBC_param.LSIG_TXOP_FILL
1934a910e4a9SSolomon Peachy 			 */
1935a910e4a9SSolomon Peachy 
1936a910e4a9SSolomon Peachy 			wsm_write_mib(priv, WSM_MIB_ID_SET_HT_PROTECTION,
19377258416cSSolomon Peachy 				      &htprot, sizeof(htprot));
1938a910e4a9SSolomon Peachy 
1939a910e4a9SSolomon Peachy 			priv->association_mode.greenfield =
1940a910e4a9SSolomon Peachy 				cw1200_ht_greenfield(&priv->ht_info);
1941a910e4a9SSolomon Peachy 			priv->association_mode.flags =
1942a910e4a9SSolomon Peachy 				WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES |
1943a910e4a9SSolomon Peachy 				WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE |
1944a910e4a9SSolomon Peachy 				WSM_ASSOCIATION_MODE_USE_HT_MODE |
1945a910e4a9SSolomon Peachy 				WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET |
1946a910e4a9SSolomon Peachy 				WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING;
1947a910e4a9SSolomon Peachy 			priv->association_mode.preamble =
1948a910e4a9SSolomon Peachy 				info->use_short_preamble ?
1949a910e4a9SSolomon Peachy 				WSM_JOIN_PREAMBLE_SHORT :
1950a910e4a9SSolomon Peachy 				WSM_JOIN_PREAMBLE_LONG;
1951a910e4a9SSolomon Peachy 			priv->association_mode.basic_rate_set = __cpu_to_le32(
1952a910e4a9SSolomon Peachy 				cw1200_rate_mask_to_wsm(priv,
1953a910e4a9SSolomon Peachy 							info->basic_rates));
1954a910e4a9SSolomon Peachy 			priv->association_mode.mpdu_start_spacing =
1955a910e4a9SSolomon Peachy 				cw1200_ht_ampdu_density(&priv->ht_info);
1956a910e4a9SSolomon Peachy 
1957a910e4a9SSolomon Peachy 			cw1200_cqm_bssloss_sm(priv, 0, 0, 0);
1958a910e4a9SSolomon Peachy 			cancel_work_sync(&priv->unjoin_work);
1959a910e4a9SSolomon Peachy 
1960a910e4a9SSolomon Peachy 			priv->bss_params.beacon_lost_count = priv->cqm_beacon_loss_count;
1961a910e4a9SSolomon Peachy 			priv->bss_params.aid = info->aid;
1962a910e4a9SSolomon Peachy 
1963a910e4a9SSolomon Peachy 			if (priv->join_dtim_period < 1)
1964a910e4a9SSolomon Peachy 				priv->join_dtim_period = 1;
1965a910e4a9SSolomon Peachy 
1966a910e4a9SSolomon Peachy 			pr_debug("[STA] DTIM %d, interval: %d\n",
1967a910e4a9SSolomon Peachy 				 priv->join_dtim_period, priv->beacon_int);
1968a910e4a9SSolomon Peachy 			pr_debug("[STA] Preamble: %d, Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n",
1969a910e4a9SSolomon Peachy 				 priv->association_mode.preamble,
1970a910e4a9SSolomon Peachy 				 priv->association_mode.greenfield,
1971a910e4a9SSolomon Peachy 				 priv->bss_params.aid,
1972a910e4a9SSolomon Peachy 				 priv->bss_params.operational_rate_set,
1973a910e4a9SSolomon Peachy 				 priv->association_mode.basic_rate_set);
1974a910e4a9SSolomon Peachy 			wsm_set_association_mode(priv, &priv->association_mode);
1975a910e4a9SSolomon Peachy 
1976a910e4a9SSolomon Peachy 			if (!info->ibss_joined) {
1977a910e4a9SSolomon Peachy 				wsm_keep_alive_period(priv, 30 /* sec */);
1978a910e4a9SSolomon Peachy 				wsm_set_bss_params(priv, &priv->bss_params);
1979a910e4a9SSolomon Peachy 				priv->setbssparams_done = true;
1980a910e4a9SSolomon Peachy 				cw1200_set_beacon_wakeup_period_work(&priv->set_beacon_wakeup_period_work);
1981a910e4a9SSolomon Peachy 				cw1200_set_pm(priv, &priv->powersave_mode);
1982a910e4a9SSolomon Peachy 			}
1983a910e4a9SSolomon Peachy 			if (priv->vif->p2p) {
1984a910e4a9SSolomon Peachy 				pr_debug("[STA] Setting p2p powersave configuration.\n");
1985a910e4a9SSolomon Peachy 				wsm_set_p2p_ps_modeinfo(priv,
1986a910e4a9SSolomon Peachy 							&priv->p2p_ps_modeinfo);
1987a910e4a9SSolomon Peachy 			}
1988a910e4a9SSolomon Peachy 			if (priv->bt_present)
1989a910e4a9SSolomon Peachy 				cw1200_set_btcoexinfo(priv);
1990a910e4a9SSolomon Peachy 		} else {
1991a910e4a9SSolomon Peachy 			memset(&priv->association_mode, 0,
1992a910e4a9SSolomon Peachy 			       sizeof(priv->association_mode));
1993a910e4a9SSolomon Peachy 			memset(&priv->bss_params, 0, sizeof(priv->bss_params));
1994a910e4a9SSolomon Peachy 		}
1995a910e4a9SSolomon Peachy 	}
1996a910e4a9SSolomon Peachy 
1997a910e4a9SSolomon Peachy 	/* ERP Protection */
1998a910e4a9SSolomon Peachy 	if (changed & (BSS_CHANGED_ASSOC |
1999a910e4a9SSolomon Peachy 		       BSS_CHANGED_ERP_CTS_PROT |
2000a910e4a9SSolomon Peachy 		       BSS_CHANGED_ERP_PREAMBLE)) {
2001a910e4a9SSolomon Peachy 		u32 prev_erp_info = priv->erp_info;
2002a910e4a9SSolomon Peachy 		if (info->use_cts_prot)
2003a910e4a9SSolomon Peachy 			priv->erp_info |= WLAN_ERP_USE_PROTECTION;
2004a910e4a9SSolomon Peachy 		else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT))
2005a910e4a9SSolomon Peachy 			priv->erp_info &= ~WLAN_ERP_USE_PROTECTION;
2006a910e4a9SSolomon Peachy 
2007a910e4a9SSolomon Peachy 		if (info->use_short_preamble)
2008a910e4a9SSolomon Peachy 			priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE;
2009a910e4a9SSolomon Peachy 		else
2010a910e4a9SSolomon Peachy 			priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE;
2011a910e4a9SSolomon Peachy 
2012a910e4a9SSolomon Peachy 		pr_debug("[STA] ERP Protection: %x\n", priv->erp_info);
2013a910e4a9SSolomon Peachy 
2014a910e4a9SSolomon Peachy 		if (prev_erp_info != priv->erp_info)
2015a910e4a9SSolomon Peachy 			queue_work(priv->workqueue, &priv->set_cts_work);
2016a910e4a9SSolomon Peachy 	}
2017a910e4a9SSolomon Peachy 
2018a910e4a9SSolomon Peachy 	/* ERP Slottime */
2019a910e4a9SSolomon Peachy 	if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) {
2020a910e4a9SSolomon Peachy 		__le32 slot_time = info->use_short_slot ?
2021a910e4a9SSolomon Peachy 			__cpu_to_le32(9) : __cpu_to_le32(20);
2022a910e4a9SSolomon Peachy 		pr_debug("[STA] Slot time: %d us.\n",
2023a910e4a9SSolomon Peachy 			 __le32_to_cpu(slot_time));
2024a910e4a9SSolomon Peachy 		wsm_write_mib(priv, WSM_MIB_ID_DOT11_SLOT_TIME,
2025a910e4a9SSolomon Peachy 			      &slot_time, sizeof(slot_time));
2026a910e4a9SSolomon Peachy 	}
2027a910e4a9SSolomon Peachy 
2028a910e4a9SSolomon Peachy 	if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) {
2029a910e4a9SSolomon Peachy 		struct wsm_rcpi_rssi_threshold threshold = {
2030a910e4a9SSolomon Peachy 			.rollingAverageCount = 8,
2031a910e4a9SSolomon Peachy 		};
2032a910e4a9SSolomon Peachy 		pr_debug("[CQM] RSSI threshold subscribe: %d +- %d\n",
2033a910e4a9SSolomon Peachy 			 info->cqm_rssi_thold, info->cqm_rssi_hyst);
2034a910e4a9SSolomon Peachy 		priv->cqm_rssi_thold = info->cqm_rssi_thold;
2035a910e4a9SSolomon Peachy 		priv->cqm_rssi_hyst = info->cqm_rssi_hyst;
2036a910e4a9SSolomon Peachy 
2037a910e4a9SSolomon Peachy 		if (info->cqm_rssi_thold || info->cqm_rssi_hyst) {
2038a910e4a9SSolomon Peachy 			/* RSSI subscription enabled */
2039a910e4a9SSolomon Peachy 			/* TODO: It's not a correct way of setting threshold.
2040a910e4a9SSolomon Peachy 			 * Upper and lower must be set equal here and adjusted
2041a910e4a9SSolomon Peachy 			 * in callback. However current implementation is much
2042a910e4a9SSolomon Peachy 			 * more relaible and stable.
2043a910e4a9SSolomon Peachy 			 */
2044a910e4a9SSolomon Peachy 
2045a910e4a9SSolomon Peachy 			/* RSSI: signed Q8.0, RCPI: unsigned Q7.1
2046a910e4a9SSolomon Peachy 			 * RSSI = RCPI / 2 - 110
2047a910e4a9SSolomon Peachy 			 */
2048a910e4a9SSolomon Peachy 			if (priv->cqm_use_rssi) {
2049a910e4a9SSolomon Peachy 				threshold.upperThreshold =
2050a910e4a9SSolomon Peachy 					info->cqm_rssi_thold + info->cqm_rssi_hyst;
2051a910e4a9SSolomon Peachy 				threshold.lowerThreshold =
2052a910e4a9SSolomon Peachy 					info->cqm_rssi_thold;
2053a910e4a9SSolomon Peachy 				threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
2054a910e4a9SSolomon Peachy 			} else {
2055a910e4a9SSolomon Peachy 				threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110) * 2;
2056a910e4a9SSolomon Peachy 				threshold.lowerThreshold = (info->cqm_rssi_thold + 110) * 2;
2057a910e4a9SSolomon Peachy 			}
2058a910e4a9SSolomon Peachy 			threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE;
2059a910e4a9SSolomon Peachy 		} else {
2060a910e4a9SSolomon Peachy 			/* There is a bug in FW, see sta.c. We have to enable
2061a910e4a9SSolomon Peachy 			 * dummy subscription to get correct RSSI values.
2062a910e4a9SSolomon Peachy 			 */
2063a910e4a9SSolomon Peachy 			threshold.rssiRcpiMode |=
2064a910e4a9SSolomon Peachy 				WSM_RCPI_RSSI_THRESHOLD_ENABLE |
2065a910e4a9SSolomon Peachy 				WSM_RCPI_RSSI_DONT_USE_UPPER |
2066a910e4a9SSolomon Peachy 				WSM_RCPI_RSSI_DONT_USE_LOWER;
2067a910e4a9SSolomon Peachy 			if (priv->cqm_use_rssi)
2068a910e4a9SSolomon Peachy 				threshold.rssiRcpiMode |= WSM_RCPI_RSSI_USE_RSSI;
2069a910e4a9SSolomon Peachy 		}
2070a910e4a9SSolomon Peachy 		wsm_set_rcpi_rssi_threshold(priv, &threshold);
2071a910e4a9SSolomon Peachy 	}
2072a910e4a9SSolomon Peachy 	mutex_unlock(&priv->conf_mutex);
2073a910e4a9SSolomon Peachy 
2074a910e4a9SSolomon Peachy 	if (do_join) {
2075a910e4a9SSolomon Peachy 		wsm_lock_tx(priv);
2076a910e4a9SSolomon Peachy 		cw1200_do_join(priv); /* Will unlock it for us */
2077a910e4a9SSolomon Peachy 	}
2078a910e4a9SSolomon Peachy }
2079a910e4a9SSolomon Peachy 
2080a910e4a9SSolomon Peachy void cw1200_multicast_start_work(struct work_struct *work)
2081a910e4a9SSolomon Peachy {
2082a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
2083a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common, multicast_start_work);
2084a910e4a9SSolomon Peachy 	long tmo = priv->join_dtim_period *
2085a910e4a9SSolomon Peachy 			(priv->beacon_int + 20) * HZ / 1024;
2086a910e4a9SSolomon Peachy 
2087a910e4a9SSolomon Peachy 	cancel_work_sync(&priv->multicast_stop_work);
2088a910e4a9SSolomon Peachy 
2089a910e4a9SSolomon Peachy 	if (!priv->aid0_bit_set) {
2090a910e4a9SSolomon Peachy 		wsm_lock_tx(priv);
2091a910e4a9SSolomon Peachy 		cw1200_set_tim_impl(priv, true);
2092a910e4a9SSolomon Peachy 		priv->aid0_bit_set = true;
2093a910e4a9SSolomon Peachy 		mod_timer(&priv->mcast_timeout, jiffies + tmo);
2094a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
2095a910e4a9SSolomon Peachy 	}
2096a910e4a9SSolomon Peachy }
2097a910e4a9SSolomon Peachy 
2098a910e4a9SSolomon Peachy void cw1200_multicast_stop_work(struct work_struct *work)
2099a910e4a9SSolomon Peachy {
2100a910e4a9SSolomon Peachy 	struct cw1200_common *priv =
2101a910e4a9SSolomon Peachy 		container_of(work, struct cw1200_common, multicast_stop_work);
2102a910e4a9SSolomon Peachy 
2103a910e4a9SSolomon Peachy 	if (priv->aid0_bit_set) {
2104a910e4a9SSolomon Peachy 		del_timer_sync(&priv->mcast_timeout);
2105a910e4a9SSolomon Peachy 		wsm_lock_tx(priv);
2106a910e4a9SSolomon Peachy 		priv->aid0_bit_set = false;
2107a910e4a9SSolomon Peachy 		cw1200_set_tim_impl(priv, false);
2108a910e4a9SSolomon Peachy 		wsm_unlock_tx(priv);
2109a910e4a9SSolomon Peachy 	}
2110a910e4a9SSolomon Peachy }
2111a910e4a9SSolomon Peachy 
2112e3dcf8bbSKees Cook void cw1200_mcast_timeout(struct timer_list *t)
2113a910e4a9SSolomon Peachy {
2114e3dcf8bbSKees Cook 	struct cw1200_common *priv = from_timer(priv, t, mcast_timeout);
2115a910e4a9SSolomon Peachy 
2116a910e4a9SSolomon Peachy 	wiphy_warn(priv->hw->wiphy,
2117a910e4a9SSolomon Peachy 		   "Multicast delivery timeout.\n");
2118a910e4a9SSolomon Peachy 	spin_lock_bh(&priv->ps_state_lock);
2119a910e4a9SSolomon Peachy 	priv->tx_multicast = priv->aid0_bit_set &&
2120a910e4a9SSolomon Peachy 			priv->buffered_multicasts;
2121a910e4a9SSolomon Peachy 	if (priv->tx_multicast)
2122a910e4a9SSolomon Peachy 		cw1200_bh_wakeup(priv);
2123a910e4a9SSolomon Peachy 	spin_unlock_bh(&priv->ps_state_lock);
2124a910e4a9SSolomon Peachy }
2125a910e4a9SSolomon Peachy 
2126a910e4a9SSolomon Peachy int cw1200_ampdu_action(struct ieee80211_hw *hw,
2127a910e4a9SSolomon Peachy 			struct ieee80211_vif *vif,
212850ea05efSSara Sharon 			struct ieee80211_ampdu_params *params)
2129a910e4a9SSolomon Peachy {
2130a910e4a9SSolomon Peachy 	/* Aggregation is implemented fully in firmware,
2131a910e4a9SSolomon Peachy 	 * including block ack negotiation. Do not allow
2132a910e4a9SSolomon Peachy 	 * mac80211 stack to do anything: it interferes with
2133a910e4a9SSolomon Peachy 	 * the firmware.
2134a910e4a9SSolomon Peachy 	 */
2135a910e4a9SSolomon Peachy 
2136a910e4a9SSolomon Peachy 	/* Note that we still need this function stubbed. */
2137a910e4a9SSolomon Peachy 	return -ENOTSUPP;
2138a910e4a9SSolomon Peachy }
2139a910e4a9SSolomon Peachy 
2140a910e4a9SSolomon Peachy /* ******************************************************************** */
2141a910e4a9SSolomon Peachy /* WSM callback								*/
2142a910e4a9SSolomon Peachy void cw1200_suspend_resume(struct cw1200_common *priv,
2143a910e4a9SSolomon Peachy 			  struct wsm_suspend_resume *arg)
2144a910e4a9SSolomon Peachy {
2145a910e4a9SSolomon Peachy 	pr_debug("[AP] %s: %s\n",
2146a910e4a9SSolomon Peachy 		 arg->stop ? "stop" : "start",
2147a910e4a9SSolomon Peachy 		 arg->multicast ? "broadcast" : "unicast");
2148a910e4a9SSolomon Peachy 
2149a910e4a9SSolomon Peachy 	if (arg->multicast) {
2150a910e4a9SSolomon Peachy 		bool cancel_tmo = false;
2151a910e4a9SSolomon Peachy 		spin_lock_bh(&priv->ps_state_lock);
2152a910e4a9SSolomon Peachy 		if (arg->stop) {
2153a910e4a9SSolomon Peachy 			priv->tx_multicast = false;
2154a910e4a9SSolomon Peachy 		} else {
2155a910e4a9SSolomon Peachy 			/* Firmware sends this indication every DTIM if there
2156a910e4a9SSolomon Peachy 			 * is a STA in powersave connected. There is no reason
2157a910e4a9SSolomon Peachy 			 * to suspend, following wakeup will consume much more
2158a910e4a9SSolomon Peachy 			 * power than it could be saved.
2159a910e4a9SSolomon Peachy 			 */
2160a910e4a9SSolomon Peachy 			cw1200_pm_stay_awake(&priv->pm_state,
2161a910e4a9SSolomon Peachy 					     priv->join_dtim_period *
2162a910e4a9SSolomon Peachy 					     (priv->beacon_int + 20) * HZ / 1024);
2163a910e4a9SSolomon Peachy 			priv->tx_multicast = (priv->aid0_bit_set &&
2164a910e4a9SSolomon Peachy 					      priv->buffered_multicasts);
2165a910e4a9SSolomon Peachy 			if (priv->tx_multicast) {
2166a910e4a9SSolomon Peachy 				cancel_tmo = true;
2167a910e4a9SSolomon Peachy 				cw1200_bh_wakeup(priv);
2168a910e4a9SSolomon Peachy 			}
2169a910e4a9SSolomon Peachy 		}
2170a910e4a9SSolomon Peachy 		spin_unlock_bh(&priv->ps_state_lock);
2171a910e4a9SSolomon Peachy 		if (cancel_tmo)
2172a910e4a9SSolomon Peachy 			del_timer_sync(&priv->mcast_timeout);
2173a910e4a9SSolomon Peachy 	} else {
2174a910e4a9SSolomon Peachy 		spin_lock_bh(&priv->ps_state_lock);
2175a910e4a9SSolomon Peachy 		cw1200_ps_notify(priv, arg->link_id, arg->stop);
2176a910e4a9SSolomon Peachy 		spin_unlock_bh(&priv->ps_state_lock);
2177a910e4a9SSolomon Peachy 		if (!arg->stop)
2178a910e4a9SSolomon Peachy 			cw1200_bh_wakeup(priv);
2179a910e4a9SSolomon Peachy 	}
2180a910e4a9SSolomon Peachy 	return;
2181a910e4a9SSolomon Peachy }
2182a910e4a9SSolomon Peachy 
2183a910e4a9SSolomon Peachy /* ******************************************************************** */
2184a910e4a9SSolomon Peachy /* AP privates								*/
2185a910e4a9SSolomon Peachy 
2186a910e4a9SSolomon Peachy static int cw1200_upload_beacon(struct cw1200_common *priv)
2187a910e4a9SSolomon Peachy {
2188a910e4a9SSolomon Peachy 	int ret = 0;
2189a910e4a9SSolomon Peachy 	struct ieee80211_mgmt *mgmt;
2190a910e4a9SSolomon Peachy 	struct wsm_template_frame frame = {
2191a910e4a9SSolomon Peachy 		.frame_type = WSM_FRAME_TYPE_BEACON,
2192a910e4a9SSolomon Peachy 	};
2193a910e4a9SSolomon Peachy 
2194a910e4a9SSolomon Peachy 	u16 tim_offset;
2195a910e4a9SSolomon Peachy 	u16 tim_len;
2196a910e4a9SSolomon Peachy 
2197a910e4a9SSolomon Peachy 	if (priv->mode == NL80211_IFTYPE_STATION ||
2198a910e4a9SSolomon Peachy 	    priv->mode == NL80211_IFTYPE_MONITOR ||
2199a910e4a9SSolomon Peachy 	    priv->mode == NL80211_IFTYPE_UNSPECIFIED)
2200a910e4a9SSolomon Peachy 		goto done;
2201a910e4a9SSolomon Peachy 
2202a910e4a9SSolomon Peachy 	if (priv->vif->p2p)
2203a910e4a9SSolomon Peachy 		frame.rate = WSM_TRANSMIT_RATE_6;
2204a910e4a9SSolomon Peachy 
2205a910e4a9SSolomon Peachy 	frame.skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
2206a910e4a9SSolomon Peachy 					     &tim_offset, &tim_len);
2207a910e4a9SSolomon Peachy 	if (!frame.skb)
2208a910e4a9SSolomon Peachy 		return -ENOMEM;
2209a910e4a9SSolomon Peachy 
2210a910e4a9SSolomon Peachy 	ret = wsm_set_template_frame(priv, &frame);
2211a910e4a9SSolomon Peachy 
2212a910e4a9SSolomon Peachy 	if (ret)
2213a910e4a9SSolomon Peachy 		goto done;
2214a910e4a9SSolomon Peachy 
2215a910e4a9SSolomon Peachy 	/* TODO: Distill probe resp; remove TIM
2216a910e4a9SSolomon Peachy 	 * and any other beacon-specific IEs
2217a910e4a9SSolomon Peachy 	 */
2218a910e4a9SSolomon Peachy 	mgmt = (void *)frame.skb->data;
2219a910e4a9SSolomon Peachy 	mgmt->frame_control =
2220a910e4a9SSolomon Peachy 		__cpu_to_le16(IEEE80211_FTYPE_MGMT |
2221a910e4a9SSolomon Peachy 			      IEEE80211_STYPE_PROBE_RESP);
2222a910e4a9SSolomon Peachy 
2223a910e4a9SSolomon Peachy 	frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE;
2224a910e4a9SSolomon Peachy 	if (priv->vif->p2p) {
2225a910e4a9SSolomon Peachy 		ret = wsm_set_probe_responder(priv, true);
2226a910e4a9SSolomon Peachy 	} else {
2227a910e4a9SSolomon Peachy 		ret = wsm_set_template_frame(priv, &frame);
2228a910e4a9SSolomon Peachy 		wsm_set_probe_responder(priv, false);
2229a910e4a9SSolomon Peachy 	}
2230a910e4a9SSolomon Peachy 
2231a910e4a9SSolomon Peachy done:
2232a910e4a9SSolomon Peachy 	dev_kfree_skb(frame.skb);
2233a910e4a9SSolomon Peachy 
2234a910e4a9SSolomon Peachy 	return ret;
2235a910e4a9SSolomon Peachy }
2236a910e4a9SSolomon Peachy 
2237a910e4a9SSolomon Peachy static int cw1200_upload_pspoll(struct cw1200_common *priv)
2238a910e4a9SSolomon Peachy {
2239a910e4a9SSolomon Peachy 	int ret = 0;
2240a910e4a9SSolomon Peachy 	struct wsm_template_frame frame = {
2241a910e4a9SSolomon Peachy 		.frame_type = WSM_FRAME_TYPE_PS_POLL,
2242a910e4a9SSolomon Peachy 		.rate = 0xFF,
2243a910e4a9SSolomon Peachy 	};
2244a910e4a9SSolomon Peachy 
2245a910e4a9SSolomon Peachy 
2246a910e4a9SSolomon Peachy 	frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif);
2247a910e4a9SSolomon Peachy 	if (!frame.skb)
2248a910e4a9SSolomon Peachy 		return -ENOMEM;
2249a910e4a9SSolomon Peachy 
2250a910e4a9SSolomon Peachy 	ret = wsm_set_template_frame(priv, &frame);
2251a910e4a9SSolomon Peachy 
2252a910e4a9SSolomon Peachy 	dev_kfree_skb(frame.skb);
2253a910e4a9SSolomon Peachy 
2254a910e4a9SSolomon Peachy 	return ret;
2255a910e4a9SSolomon Peachy }
2256a910e4a9SSolomon Peachy 
2257a910e4a9SSolomon Peachy static int cw1200_upload_null(struct cw1200_common *priv)
2258a910e4a9SSolomon Peachy {
2259a910e4a9SSolomon Peachy 	int ret = 0;
2260a910e4a9SSolomon Peachy 	struct wsm_template_frame frame = {
2261a910e4a9SSolomon Peachy 		.frame_type = WSM_FRAME_TYPE_NULL,
2262a910e4a9SSolomon Peachy 		.rate = 0xFF,
2263a910e4a9SSolomon Peachy 	};
2264a910e4a9SSolomon Peachy 
22657b6ddeafSJohannes Berg 	frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif, false);
2266a910e4a9SSolomon Peachy 	if (!frame.skb)
2267a910e4a9SSolomon Peachy 		return -ENOMEM;
2268a910e4a9SSolomon Peachy 
2269a910e4a9SSolomon Peachy 	ret = wsm_set_template_frame(priv, &frame);
2270a910e4a9SSolomon Peachy 
2271a910e4a9SSolomon Peachy 	dev_kfree_skb(frame.skb);
2272a910e4a9SSolomon Peachy 
2273a910e4a9SSolomon Peachy 	return ret;
2274a910e4a9SSolomon Peachy }
2275a910e4a9SSolomon Peachy 
2276a910e4a9SSolomon Peachy static int cw1200_upload_qosnull(struct cw1200_common *priv)
2277a910e4a9SSolomon Peachy {
2278a910e4a9SSolomon Peachy 	/* TODO:  This needs to be implemented
2279a910e4a9SSolomon Peachy 
2280a910e4a9SSolomon Peachy 	struct wsm_template_frame frame = {
2281a910e4a9SSolomon Peachy 		.frame_type = WSM_FRAME_TYPE_QOS_NULL,
2282a910e4a9SSolomon Peachy 		.rate = 0xFF,
2283a910e4a9SSolomon Peachy 	};
2284a910e4a9SSolomon Peachy 
2285a910e4a9SSolomon Peachy 	frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif);
2286a910e4a9SSolomon Peachy 	if (!frame.skb)
2287a910e4a9SSolomon Peachy 		return -ENOMEM;
2288a910e4a9SSolomon Peachy 
2289a910e4a9SSolomon Peachy 	ret = wsm_set_template_frame(priv, &frame);
2290a910e4a9SSolomon Peachy 
2291a910e4a9SSolomon Peachy 	dev_kfree_skb(frame.skb);
2292a910e4a9SSolomon Peachy 
2293a910e4a9SSolomon Peachy 	*/
229469253b61SPeter Senna Tschudin 	return 0;
2295a910e4a9SSolomon Peachy }
2296a910e4a9SSolomon Peachy 
2297a910e4a9SSolomon Peachy static int cw1200_enable_beaconing(struct cw1200_common *priv,
2298a910e4a9SSolomon Peachy 				   bool enable)
2299a910e4a9SSolomon Peachy {
2300a910e4a9SSolomon Peachy 	struct wsm_beacon_transmit transmit = {
2301a910e4a9SSolomon Peachy 		.enable_beaconing = enable,
2302a910e4a9SSolomon Peachy 	};
2303a910e4a9SSolomon Peachy 
2304a910e4a9SSolomon Peachy 	return wsm_beacon_transmit(priv, &transmit);
2305a910e4a9SSolomon Peachy }
2306a910e4a9SSolomon Peachy 
2307a910e4a9SSolomon Peachy static int cw1200_start_ap(struct cw1200_common *priv)
2308a910e4a9SSolomon Peachy {
2309a910e4a9SSolomon Peachy 	int ret;
2310a910e4a9SSolomon Peachy 	struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
2311a910e4a9SSolomon Peachy 	struct wsm_start start = {
2312a910e4a9SSolomon Peachy 		.mode = priv->vif->p2p ?
2313a910e4a9SSolomon Peachy 				WSM_START_MODE_P2P_GO : WSM_START_MODE_AP,
231457fbcce3SJohannes Berg 		.band = (priv->channel->band == NL80211_BAND_5GHZ) ?
2315a910e4a9SSolomon Peachy 				WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
2316a910e4a9SSolomon Peachy 		.channel_number = priv->channel->hw_value,
2317a910e4a9SSolomon Peachy 		.beacon_interval = conf->beacon_int,
2318a910e4a9SSolomon Peachy 		.dtim_period = conf->dtim_period,
2319a910e4a9SSolomon Peachy 		.preamble = conf->use_short_preamble ?
2320a910e4a9SSolomon Peachy 				WSM_JOIN_PREAMBLE_SHORT :
2321a910e4a9SSolomon Peachy 				WSM_JOIN_PREAMBLE_LONG,
2322a910e4a9SSolomon Peachy 		.probe_delay = 100,
2323a910e4a9SSolomon Peachy 		.basic_rate_set = cw1200_rate_mask_to_wsm(priv,
2324a910e4a9SSolomon Peachy 				conf->basic_rates),
2325a910e4a9SSolomon Peachy 	};
2326a910e4a9SSolomon Peachy 	struct wsm_operational_mode mode = {
2327a910e4a9SSolomon Peachy 		.power_mode = cw1200_power_mode,
2328a910e4a9SSolomon Peachy 		.disable_more_flag_usage = true,
2329a910e4a9SSolomon Peachy 	};
2330a910e4a9SSolomon Peachy 
2331a910e4a9SSolomon Peachy 	memset(start.ssid, 0, sizeof(start.ssid));
2332a910e4a9SSolomon Peachy 	if (!conf->hidden_ssid) {
2333a910e4a9SSolomon Peachy 		start.ssid_len = conf->ssid_len;
2334a910e4a9SSolomon Peachy 		memcpy(start.ssid, conf->ssid, start.ssid_len);
2335a910e4a9SSolomon Peachy 	}
2336a910e4a9SSolomon Peachy 
2337a910e4a9SSolomon Peachy 	priv->beacon_int = conf->beacon_int;
2338a910e4a9SSolomon Peachy 	priv->join_dtim_period = conf->dtim_period;
2339a910e4a9SSolomon Peachy 
2340a910e4a9SSolomon Peachy 	memset(&priv->link_id_db, 0, sizeof(priv->link_id_db));
2341a910e4a9SSolomon Peachy 
2342a910e4a9SSolomon Peachy 	pr_debug("[AP] ch: %d(%d), bcn: %d(%d), brt: 0x%.8X, ssid: %.*s.\n",
2343a910e4a9SSolomon Peachy 		 start.channel_number, start.band,
2344a910e4a9SSolomon Peachy 		 start.beacon_interval, start.dtim_period,
2345a910e4a9SSolomon Peachy 		 start.basic_rate_set,
2346a910e4a9SSolomon Peachy 		 start.ssid_len, start.ssid);
2347a910e4a9SSolomon Peachy 	ret = wsm_start(priv, &start);
2348a910e4a9SSolomon Peachy 	if (!ret)
2349a910e4a9SSolomon Peachy 		ret = cw1200_upload_keys(priv);
2350a910e4a9SSolomon Peachy 	if (!ret && priv->vif->p2p) {
2351a910e4a9SSolomon Peachy 		pr_debug("[AP] Setting p2p powersave configuration.\n");
2352a910e4a9SSolomon Peachy 		wsm_set_p2p_ps_modeinfo(priv, &priv->p2p_ps_modeinfo);
2353a910e4a9SSolomon Peachy 	}
2354a910e4a9SSolomon Peachy 	if (!ret) {
2355a910e4a9SSolomon Peachy 		wsm_set_block_ack_policy(priv, 0, 0);
2356a910e4a9SSolomon Peachy 		priv->join_status = CW1200_JOIN_STATUS_AP;
2357a910e4a9SSolomon Peachy 		cw1200_update_filtering(priv);
2358a910e4a9SSolomon Peachy 	}
2359a910e4a9SSolomon Peachy 	wsm_set_operational_mode(priv, &mode);
2360a910e4a9SSolomon Peachy 	return ret;
2361a910e4a9SSolomon Peachy }
2362a910e4a9SSolomon Peachy 
2363a910e4a9SSolomon Peachy static int cw1200_update_beaconing(struct cw1200_common *priv)
2364a910e4a9SSolomon Peachy {
2365a910e4a9SSolomon Peachy 	struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
2366a910e4a9SSolomon Peachy 	struct wsm_reset reset = {
2367a910e4a9SSolomon Peachy 		.link_id = 0,
2368a910e4a9SSolomon Peachy 		.reset_statistics = true,
2369a910e4a9SSolomon Peachy 	};
2370a910e4a9SSolomon Peachy 
2371a910e4a9SSolomon Peachy 	if (priv->mode == NL80211_IFTYPE_AP) {
2372a910e4a9SSolomon Peachy 		/* TODO: check if changed channel, band */
2373a910e4a9SSolomon Peachy 		if (priv->join_status != CW1200_JOIN_STATUS_AP ||
2374a910e4a9SSolomon Peachy 		    priv->beacon_int != conf->beacon_int) {
2375a910e4a9SSolomon Peachy 			pr_debug("ap restarting\n");
2376a910e4a9SSolomon Peachy 			wsm_lock_tx(priv);
2377a910e4a9SSolomon Peachy 			if (priv->join_status != CW1200_JOIN_STATUS_PASSIVE)
2378a910e4a9SSolomon Peachy 				wsm_reset(priv, &reset);
2379a910e4a9SSolomon Peachy 			priv->join_status = CW1200_JOIN_STATUS_PASSIVE;
2380a910e4a9SSolomon Peachy 			cw1200_start_ap(priv);
2381a910e4a9SSolomon Peachy 			wsm_unlock_tx(priv);
2382a910e4a9SSolomon Peachy 		} else
2383a910e4a9SSolomon Peachy 			pr_debug("ap started join_status: %d\n",
2384a910e4a9SSolomon Peachy 				 priv->join_status);
2385a910e4a9SSolomon Peachy 	}
2386a910e4a9SSolomon Peachy 	return 0;
2387a910e4a9SSolomon Peachy }
2388