Linux Audio

Check our new training course

Linux kernel drivers training

May 6-19, 2025
Register
Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Mac80211 power management API for ST-Ericsson CW1200 drivers
  3 *
  4 * Copyright (c) 2011, ST-Ericsson
  5 * Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
  6 *
  7 * This program is free software; you can redistribute it and/or modify
  8 * it under the terms of the GNU General Public License version 2 as
  9 * published by the Free Software Foundation.
 10 */
 11
 12#include <linux/module.h>
 13#include <linux/if_ether.h>
 14#include "cw1200.h"
 15#include "pm.h"
 16#include "sta.h"
 17#include "bh.h"
 18#include "hwbus.h"
 19
 20#define CW1200_BEACON_SKIPPING_MULTIPLIER 3
 21
 22struct cw1200_udp_port_filter {
 23	struct wsm_udp_port_filter_hdr hdr;
 24	/* Up to 4 filters are allowed. */
 25	struct wsm_udp_port_filter filters[WSM_MAX_FILTER_ELEMENTS];
 26} __packed;
 27
 28struct cw1200_ether_type_filter {
 29	struct wsm_ether_type_filter_hdr hdr;
 30	/* Up to 4 filters are allowed. */
 31	struct wsm_ether_type_filter filters[WSM_MAX_FILTER_ELEMENTS];
 32} __packed;
 33
 34static struct cw1200_udp_port_filter cw1200_udp_port_filter_on = {
 35	.hdr.num = 2,
 36	.filters = {
 37		[0] = {
 38			.action = WSM_FILTER_ACTION_FILTER_OUT,
 39			.type = WSM_FILTER_PORT_TYPE_DST,
 40			.port = __cpu_to_le16(67), /* DHCP Bootps */
 41		},
 42		[1] = {
 43			.action = WSM_FILTER_ACTION_FILTER_OUT,
 44			.type = WSM_FILTER_PORT_TYPE_DST,
 45			.port = __cpu_to_le16(68), /* DHCP Bootpc */
 46		},
 47	}
 48};
 49
 50static struct wsm_udp_port_filter_hdr cw1200_udp_port_filter_off = {
 51	.num = 0,
 52};
 53
 54#ifndef ETH_P_WAPI
 55#define ETH_P_WAPI     0x88B4
 56#endif
 57
 58static struct cw1200_ether_type_filter cw1200_ether_type_filter_on = {
 59	.hdr.num = 4,
 60	.filters = {
 61		[0] = {
 62			.action = WSM_FILTER_ACTION_FILTER_IN,
 63			.type = __cpu_to_le16(ETH_P_IP),
 64		},
 65		[1] = {
 66			.action = WSM_FILTER_ACTION_FILTER_IN,
 67			.type = __cpu_to_le16(ETH_P_PAE),
 68		},
 69		[2] = {
 70			.action = WSM_FILTER_ACTION_FILTER_IN,
 71			.type = __cpu_to_le16(ETH_P_WAPI),
 72		},
 73		[3] = {
 74			.action = WSM_FILTER_ACTION_FILTER_IN,
 75			.type = __cpu_to_le16(ETH_P_ARP),
 76		},
 77	},
 78};
 79
 80static struct wsm_ether_type_filter_hdr cw1200_ether_type_filter_off = {
 81	.num = 0,
 82};
 83
 84/* private */
 85struct cw1200_suspend_state {
 86	unsigned long bss_loss_tmo;
 87	unsigned long join_tmo;
 88	unsigned long direct_probe;
 89	unsigned long link_id_gc;
 90	bool beacon_skipping;
 91	u8 prev_ps_mode;
 92};
 93
 94static void cw1200_pm_stay_awake_tmo(struct timer_list *unused)
 95{
 96	/* XXX what's the point of this ? */
 97}
 98
 99int cw1200_pm_init(struct cw1200_pm_state *pm,
100		   struct cw1200_common *priv)
101{
102	spin_lock_init(&pm->lock);
103
104	timer_setup(&pm->stay_awake, cw1200_pm_stay_awake_tmo, 0);
105
106	return 0;
107}
108
109void cw1200_pm_deinit(struct cw1200_pm_state *pm)
110{
111	del_timer_sync(&pm->stay_awake);
112}
113
114void cw1200_pm_stay_awake(struct cw1200_pm_state *pm,
115			  unsigned long tmo)
116{
117	long cur_tmo;
118	spin_lock_bh(&pm->lock);
119	cur_tmo = pm->stay_awake.expires - jiffies;
120	if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo)
121		mod_timer(&pm->stay_awake, jiffies + tmo);
122	spin_unlock_bh(&pm->lock);
123}
124
125static long cw1200_suspend_work(struct delayed_work *work)
126{
127	int ret = cancel_delayed_work(work);
128	long tmo;
129	if (ret > 0) {
130		/* Timer is pending */
131		tmo = work->timer.expires - jiffies;
132		if (tmo < 0)
133			tmo = 0;
134	} else {
135		tmo = -1;
136	}
137	return tmo;
138}
139
140static int cw1200_resume_work(struct cw1200_common *priv,
141			       struct delayed_work *work,
142			       unsigned long tmo)
143{
144	if ((long)tmo < 0)
145		return 1;
146
147	return queue_delayed_work(priv->workqueue, work, tmo);
148}
149
150int cw1200_can_suspend(struct cw1200_common *priv)
151{
152	if (atomic_read(&priv->bh_rx)) {
153		wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n");
154		return 0;
155	}
156	return 1;
157}
158EXPORT_SYMBOL_GPL(cw1200_can_suspend);
159
160int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
161{
162	struct cw1200_common *priv = hw->priv;
163	struct cw1200_pm_state *pm_state = &priv->pm_state;
164	struct cw1200_suspend_state *state;
165	int ret;
166
167	spin_lock_bh(&pm_state->lock);
168	ret = timer_pending(&pm_state->stay_awake);
169	spin_unlock_bh(&pm_state->lock);
170	if (ret)
171		return -EAGAIN;
172
173	/* Do not suspend when datapath is not idle */
174	if (priv->tx_queue_stats.num_queued)
175		return -EBUSY;
176
177	/* Make sure there is no configuration requests in progress. */
178	if (!mutex_trylock(&priv->conf_mutex))
179		return -EBUSY;
180
181	/* Ensure pending operations are done.
182	 * Note also that wow_suspend must return in ~2.5sec, before
183	 * watchdog is triggered.
184	 */
185	if (priv->channel_switch_in_progress)
186		goto revert1;
187
188	/* Do not suspend when join is pending */
189	if (priv->join_pending)
190		goto revert1;
191
192	/* Do not suspend when scanning */
193	if (down_trylock(&priv->scan.lock))
194		goto revert1;
195
196	/* Lock TX. */
197	wsm_lock_tx_async(priv);
198
199	/* Wait to avoid possible race with bh code.
200	 * But do not wait too long...
201	 */
202	if (wait_event_timeout(priv->bh_evt_wq,
203			       !priv->hw_bufs_used, HZ / 10) <= 0)
204		goto revert2;
205
206	/* Set UDP filter */
207	wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_on.hdr);
208
209	/* Set ethernet frame type filter */
210	wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_on.hdr);
211
212	/* Allocate state */
213	state = kzalloc(sizeof(struct cw1200_suspend_state), GFP_KERNEL);
214	if (!state)
215		goto revert3;
216
217	/* Change to legacy PS while going to suspend */
218	if (!priv->vif->p2p &&
219	    priv->join_status == CW1200_JOIN_STATUS_STA &&
220	    priv->powersave_mode.mode != WSM_PSM_PS) {
221		state->prev_ps_mode = priv->powersave_mode.mode;
222		priv->powersave_mode.mode = WSM_PSM_PS;
223		cw1200_set_pm(priv, &priv->powersave_mode);
224		if (wait_event_interruptible_timeout(priv->ps_mode_switch_done,
225						     !priv->ps_mode_switch_in_progress, 1*HZ) <= 0) {
226			goto revert4;
227		}
228	}
229
230	/* Store delayed work states. */
231	state->bss_loss_tmo =
232		cw1200_suspend_work(&priv->bss_loss_work);
233	state->join_tmo =
234		cw1200_suspend_work(&priv->join_timeout);
235	state->direct_probe =
236		cw1200_suspend_work(&priv->scan.probe_work);
237	state->link_id_gc =
238		cw1200_suspend_work(&priv->link_id_gc_work);
239
240	cancel_delayed_work_sync(&priv->clear_recent_scan_work);
241	atomic_set(&priv->recent_scan, 0);
242
243	/* Enable beacon skipping */
244	if (priv->join_status == CW1200_JOIN_STATUS_STA &&
245	    priv->join_dtim_period &&
246	    !priv->has_multicast_subscription) {
247		state->beacon_skipping = true;
248		wsm_set_beacon_wakeup_period(priv,
249					     priv->join_dtim_period,
250					     CW1200_BEACON_SKIPPING_MULTIPLIER * priv->join_dtim_period);
251	}
252
253	/* Stop serving thread */
254	if (cw1200_bh_suspend(priv))
255		goto revert5;
256
257	ret = timer_pending(&priv->mcast_timeout);
258	if (ret)
259		goto revert6;
260
261	/* Store suspend state */
262	pm_state->suspend_state = state;
263
264	/* Enable IRQ wake */
265	ret = priv->hwbus_ops->power_mgmt(priv->hwbus_priv, true);
266	if (ret) {
267		wiphy_err(priv->hw->wiphy,
268			  "PM request failed: %d. WoW is disabled.\n", ret);
269		cw1200_wow_resume(hw);
270		return -EBUSY;
271	}
272
273	/* Force resume if event is coming from the device. */
274	if (atomic_read(&priv->bh_rx)) {
275		cw1200_wow_resume(hw);
276		return -EAGAIN;
277	}
278
279	return 0;
280
281revert6:
282	WARN_ON(cw1200_bh_resume(priv));
283revert5:
284	cw1200_resume_work(priv, &priv->bss_loss_work,
285			   state->bss_loss_tmo);
286	cw1200_resume_work(priv, &priv->join_timeout,
287			   state->join_tmo);
288	cw1200_resume_work(priv, &priv->scan.probe_work,
289			   state->direct_probe);
290	cw1200_resume_work(priv, &priv->link_id_gc_work,
291			   state->link_id_gc);
292revert4:
293	kfree(state);
294revert3:
295	wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
296	wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
297revert2:
298	wsm_unlock_tx(priv);
299	up(&priv->scan.lock);
300revert1:
301	mutex_unlock(&priv->conf_mutex);
302	return -EBUSY;
303}
304
305int cw1200_wow_resume(struct ieee80211_hw *hw)
306{
307	struct cw1200_common *priv = hw->priv;
308	struct cw1200_pm_state *pm_state = &priv->pm_state;
309	struct cw1200_suspend_state *state;
310
311	state = pm_state->suspend_state;
312	pm_state->suspend_state = NULL;
313
314	/* Disable IRQ wake */
315	priv->hwbus_ops->power_mgmt(priv->hwbus_priv, false);
316
317	/* Scan.lock must be released before BH is resumed other way
318	 * in case when BSS_LOST command arrived the processing of the
319	 * command will be delayed.
320	 */
321	up(&priv->scan.lock);
322
323	/* Resume BH thread */
324	WARN_ON(cw1200_bh_resume(priv));
325
326	/* Restores previous PS mode */
327	if (!priv->vif->p2p && priv->join_status == CW1200_JOIN_STATUS_STA) {
328		priv->powersave_mode.mode = state->prev_ps_mode;
329		cw1200_set_pm(priv, &priv->powersave_mode);
330	}
331
332	if (state->beacon_skipping) {
333		wsm_set_beacon_wakeup_period(priv, priv->beacon_int *
334					     priv->join_dtim_period >
335					     MAX_BEACON_SKIP_TIME_MS ? 1 :
336					     priv->join_dtim_period, 0);
337		state->beacon_skipping = false;
338	}
339
340	/* Resume delayed work */
341	cw1200_resume_work(priv, &priv->bss_loss_work,
342			   state->bss_loss_tmo);
343	cw1200_resume_work(priv, &priv->join_timeout,
344			   state->join_tmo);
345	cw1200_resume_work(priv, &priv->scan.probe_work,
346			   state->direct_probe);
347	cw1200_resume_work(priv, &priv->link_id_gc_work,
348			   state->link_id_gc);
349
350	/* Remove UDP port filter */
351	wsm_set_udp_port_filter(priv, &cw1200_udp_port_filter_off);
352
353	/* Remove ethernet frame type filter */
354	wsm_set_ether_type_filter(priv, &cw1200_ether_type_filter_off);
355
356	/* Unlock datapath */
357	wsm_unlock_tx(priv);
358
359	/* Unlock configuration mutex */
360	mutex_unlock(&priv->conf_mutex);
361
362	/* Free memory */
363	kfree(state);
364
365	return 0;
366}