8dd79199f8
The bes2600 driver is a fork of the upstream cw1200 driver
(drivers/net/wireless/st/cw1200/, ST-Ericsson, Dmitry Tarnyagin
2010-2011). The fork's file headers have three GPL-compliance issues:
1. NO SPDX-License-Identifier on any of 48 source files (cw1200
mainline has them on all 25). kernel.org-mandated since 2017.
2. Original "Copyright (c) 2010, ST-Ericsson" lines stripped from
all files inherited from cw1200, replaced with
"Copyright (c) 2010, Bestechnic" — factually impossible
(Bestechnic did not author the 2010 work) and a GPL-2.0 §1
attribution-preservation violation.
3. The "GPL version 2 as published by the Free Software Foundation"
boilerplate paragraph is redundant alongside SPDX and is the
legacy form modern kernel sources have replaced.
This patch corrects all three for the 48 .c/.h files in bes2600/:
- Adds `// SPDX-License-Identifier: GPL-2.0-only` (or `/* ... */`
for headers) as line 1 of every file.
- Restores `Copyright (c) 2010, ST-Ericsson` + `Author: Dmitry
Tarnyagin <dmitry.tarnyagin@lockless.no>` as the FIRST copyright
chain entry on all 22 files derived from cw1200 (bh.{c,h},
debug.{c,h}, fwio.{c,h}, hwio.{c,h}, main.c, pm.{c,h},
queue.{c,h}, scan.{c,h}, sta.{c,h}, txrx.{c,h}, wsm.{c,h}).
- Keeps `Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.` as
the SECOND chain entry where Bestechnic genuinely contributed.
- Notes "Derived from cw1200_sdio.c" + ST-Ericsson copyright on
bes2600_sdio.c (heavy derivation, not a literal rename).
- Notes "Replaces hwbus.h from cw1200/" + ST-Ericsson copyright
on sbus.h.
- Preserves the prism54/islsm authorship chain on main.c and
bes2600.h (Michael Wu 2006 + Jean-Baptiste Note 2004-2006).
- Drops the GPL-2.0 boilerplate paragraph in favour of SPDX.
No code changes — only file-header comment blocks. Module build is
unaffected (verified by header-only diff scope).
This is a prerequisite for any kernel.org submission attempt. The
existing MODULE_LICENSE("GPL") + MODULE_AUTHOR(Tarnyagin@stericsson.com)
declarations were already present and are unchanged here; the
mismatch between MODULE_AUTHOR and the (since-corrected) per-file
copyrights is now resolved.
604 lines
15 KiB
C
604 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Power management for BES2600 mac80211 driver
|
|
*
|
|
* Copyright (c) 2010, ST-Ericsson
|
|
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
|
|
*
|
|
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
|
|
*
|
|
*/
|
|
|
|
#include <linux/platform_device.h>
|
|
#include <linux/if_ether.h>
|
|
#include <linux/suspend.h>
|
|
#include "bes2600.h"
|
|
#include "pm.h"
|
|
#include "sta.h"
|
|
#include "bh.h"
|
|
#include "sbus.h"
|
|
#include "bes_chardev.h"
|
|
#include "bes_log.h"
|
|
|
|
#define BES2600_BEACON_SKIPPING_MULTIPLIER 3
|
|
|
|
struct bes2600_udp_port_filter {
|
|
struct wsm_udp_port_filter_hdr hdr;
|
|
struct wsm_udp_port_filter dhcp;
|
|
struct wsm_udp_port_filter upnp;
|
|
struct wsm_udp_port_filter mdns;
|
|
} __packed;
|
|
|
|
struct bes2600_ether_type_filter {
|
|
struct wsm_ether_type_filter_hdr hdr;
|
|
struct wsm_ether_type_filter pae;
|
|
struct wsm_ether_type_filter wapi;
|
|
struct wsm_ether_type_filter append;
|
|
} __packed;
|
|
|
|
static struct bes2600_udp_port_filter bes2600_udp_port_filter_on = {
|
|
.hdr.nrFilters = 3,
|
|
.dhcp = {
|
|
.filterAction = WSM_FILTER_ACTION_FILTER_OUT,
|
|
.portType = WSM_FILTER_PORT_TYPE_DST,
|
|
.udpPort = __cpu_to_le16(67),
|
|
},
|
|
.upnp = {
|
|
.filterAction = WSM_FILTER_ACTION_FILTER_OUT,
|
|
.portType = WSM_FILTER_PORT_TYPE_DST,
|
|
.udpPort = __cpu_to_le16(1900),
|
|
},
|
|
.mdns = {
|
|
.filterAction = WSM_FILTER_ACTION_FILTER_OUT,
|
|
.portType = WSM_FILTER_PORT_TYPE_DST,
|
|
.udpPort = __cpu_to_le16(5353),
|
|
},
|
|
/* Please add other known ports to be filtered out here and
|
|
* update nrFilters field in the header.
|
|
* Up to 4 filters are allowed. */
|
|
};
|
|
|
|
static struct wsm_udp_port_filter_hdr bes2600_udp_port_filter_off = {
|
|
.nrFilters = 0,
|
|
};
|
|
|
|
#ifndef ETH_P_WAPI
|
|
#define ETH_P_WAPI 0x88B4
|
|
#endif
|
|
|
|
#define ETH_P_UNKNOWN 0xFFFF
|
|
static struct bes2600_ether_type_filter bes2600_ether_type_filter_on = {
|
|
.hdr.nrFilters = 3,
|
|
.hdr.extFlags = WSM_ETH_FILTER_EXT_DISABLE_IPV6_MATCH, // patch for disable lmac SUSPEND_MODE_IPV6_FIX
|
|
.pae = {
|
|
.filterAction = WSM_FILTER_ACTION_FILTER_IN,
|
|
.etherType = __cpu_to_le16(ETH_P_PAE),
|
|
},
|
|
.wapi = {
|
|
.filterAction = WSM_FILTER_ACTION_FILTER_IN,
|
|
.etherType = __cpu_to_le16(ETH_P_WAPI),
|
|
},
|
|
// add for lmac ether filter strategy: If every filtermode is FilterIN, discard all the frame which is mismatched
|
|
.append = {
|
|
.filterAction = WSM_FILTER_ACTION_FILTER_OUT,
|
|
.etherType = __cpu_to_le16(ETH_P_UNKNOWN),
|
|
},
|
|
/* Please add other known ether types to be filtered out here and
|
|
* update nrFilters field in the header.
|
|
* Up to 4 filters are allowed. */
|
|
};
|
|
|
|
static struct wsm_ether_type_filter_hdr bes2600_ether_type_filter_off = {
|
|
.nrFilters = 0,
|
|
};
|
|
|
|
#ifdef IPV6_FILTERING
|
|
static struct wsm_ipv6_filter bes2600_ipv6_filter_on = {
|
|
.hdr.numfilter = 1,
|
|
.hdr.action_mode = WSM_FILTER_ACTION_FILTER_IN,
|
|
.ipv6filter[0] = {
|
|
.filter_mode = WSM_FILTER_ACTION_FILTER_IN,
|
|
.address_mode = WSM_IP_DATA_FRAME_ADDRMODE_DEST,
|
|
/* a random ipvd addr, in order to filter all ipv6 packet */
|
|
.ipv6 = {0x01, 0x28, 0x35, 0xde, 0xbf, 0x34, 0x9d, 0x8a,
|
|
0x47, 0x62, 0x85, 0x69, 0x7e, 0x8c, 0x29, 0x38},
|
|
}
|
|
};
|
|
|
|
static struct wsm_ipv6_filter bes2600_ipv6_filter_off = {
|
|
.hdr.numfilter = 0,
|
|
.hdr.action_mode = WSM_FILTER_ACTION_IGNORE,
|
|
};
|
|
#endif
|
|
|
|
static int __bes2600_wow_suspend(struct bes2600_vif *priv,
|
|
struct cfg80211_wowlan *wowlan);
|
|
static int __bes2600_wow_resume(struct bes2600_vif *priv);
|
|
|
|
|
|
/* private */
|
|
struct bes2600_suspend_state {
|
|
unsigned long bss_loss_tmo;
|
|
unsigned long connection_loss_tmo;
|
|
unsigned long join_tmo;
|
|
unsigned long direct_probe;
|
|
unsigned long link_id_gc;
|
|
};
|
|
|
|
void bes2600_suspend_status_set(struct bes2600_common *hw_priv, bool status)
|
|
{
|
|
hw_priv->suspend_in_progress = status;
|
|
}
|
|
|
|
bool bes2600_suspend_status_get(struct bes2600_common *hw_priv)
|
|
{
|
|
return hw_priv->suspend_in_progress;
|
|
}
|
|
|
|
void bes2600_pending_unjoin_reset(struct bes2600_common *hw_priv)
|
|
{
|
|
hw_priv->unjoin_if_id_slots = 0x00;
|
|
}
|
|
|
|
void bes2600_pending_unjoin_set(struct bes2600_common *hw_priv, int if_id)
|
|
{
|
|
if(if_id > 1)
|
|
bes_err("unexpected if_id: %d\n", if_id);
|
|
else
|
|
hw_priv->unjoin_if_id_slots |= (1 << if_id);
|
|
}
|
|
|
|
bool bes2600_pending_unjoin_get(struct bes2600_common *hw_priv, int if_id)
|
|
{
|
|
if(if_id > 1) {
|
|
bes_err("unexpected if_id: %d\n", if_id);
|
|
return false;
|
|
} else
|
|
return hw_priv->unjoin_if_id_slots & (1 << if_id);
|
|
}
|
|
|
|
static int bes2600_pm_notifier(struct notifier_block *notifier,
|
|
unsigned long pm_event,
|
|
void *unused)
|
|
{
|
|
int if_id;
|
|
struct bes2600_vif *priv;
|
|
struct bes2600_common *hw_priv = container_of(notifier,
|
|
struct bes2600_common,
|
|
pm_notify);
|
|
|
|
switch (pm_event) {
|
|
case PM_HIBERNATION_PREPARE:
|
|
case PM_SUSPEND_PREPARE:
|
|
bes2600_suspend_status_set(hw_priv, true);
|
|
break;
|
|
|
|
case PM_POST_RESTORE:
|
|
case PM_POST_HIBERNATION:
|
|
case PM_POST_SUSPEND:
|
|
bes2600_suspend_status_set(hw_priv, false);
|
|
if(hw_priv->unjoin_if_id_slots) {
|
|
for(if_id = 0; if_id < 2; if_id++) {
|
|
if(bes2600_pending_unjoin_get(hw_priv, if_id)) {
|
|
priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, if_id);
|
|
ieee80211_connection_loss(priv->vif);
|
|
}
|
|
}
|
|
bes2600_pending_unjoin_reset(hw_priv);
|
|
}
|
|
break;
|
|
|
|
case PM_RESTORE_PREPARE:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return NOTIFY_DONE;
|
|
}
|
|
|
|
void bes2600_register_pm_notifier(struct bes2600_common *hw_priv)
|
|
{
|
|
hw_priv->pm_notify.notifier_call = bes2600_pm_notifier;
|
|
register_pm_notifier(&hw_priv->pm_notify);
|
|
}
|
|
|
|
void bes2600_unregister_pm_notifier(struct bes2600_common *hw_priv)
|
|
{
|
|
unregister_pm_notifier(&hw_priv->pm_notify);
|
|
}
|
|
|
|
static long bes2600_suspend_work(struct delayed_work *work)
|
|
{
|
|
int ret = cancel_delayed_work(work);
|
|
long tmo;
|
|
if (ret > 0) {
|
|
/* Timer is pending */
|
|
tmo = work->timer.expires - jiffies;
|
|
if (tmo < 0)
|
|
tmo = 0;
|
|
} else {
|
|
tmo = -1;
|
|
}
|
|
return tmo;
|
|
}
|
|
|
|
static int bes2600_resume_work(struct bes2600_common *hw_priv,
|
|
struct delayed_work *work,
|
|
unsigned long tmo)
|
|
{
|
|
if ((long)tmo < 0)
|
|
return 1;
|
|
|
|
return queue_delayed_work(hw_priv->workqueue, work, tmo);
|
|
}
|
|
|
|
int bes2600_can_suspend(struct bes2600_common *priv)
|
|
{
|
|
if (atomic_read(&priv->bh_rx)) {
|
|
wiphy_dbg(priv->hw->wiphy, "Suspend interrupted.\n");
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
EXPORT_SYMBOL_GPL(bes2600_can_suspend);
|
|
|
|
int bes2600_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
|
|
{
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
struct bes2600_vif *priv;
|
|
int i, ret = 0;
|
|
unsigned long begin, end, diff;
|
|
char *busy_event_buffer = NULL;
|
|
|
|
bes_devel("bes2600_wow_suspend enter\n");
|
|
|
|
WARN_ON(!atomic_read(&hw_priv->num_vifs));
|
|
|
|
/* reset wakeup reason to default */
|
|
bes2600_chrdev_wifi_update_wakeup_reason(0, 0);
|
|
|
|
#ifdef ROAM_OFFLOAD
|
|
bes2600_for_each_vif(hw_priv, priv, i) {
|
|
#ifdef P2P_MULTIVIF
|
|
if ((i == (CW12XX_MAX_VIFS - 1)) || !priv)
|
|
#else
|
|
if (!priv)
|
|
#endif
|
|
continue;
|
|
if((priv->vif->type == NL80211_IFTYPE_STATION)
|
|
&& (priv->join_status == BES2600_JOIN_STATUS_STA)) {
|
|
down(&hw_priv->scan.lock);
|
|
hw_priv->scan.if_id = priv->if_id;
|
|
bes2600_sched_scan_work(&hw_priv->scan.swork);
|
|
}
|
|
}
|
|
#endif /*ROAM_OFFLOAD*/
|
|
|
|
/* Do not suspend when datapath is not idle */
|
|
if (hw_priv->tx_queue_stats.num_queued[0]
|
|
+ hw_priv->tx_queue_stats.num_queued[1])
|
|
return -EBUSY;
|
|
|
|
|
|
/* Make sure there is no configuration requests in progress. */
|
|
if (down_trylock(&hw_priv->conf_lock))
|
|
return -EBUSY;
|
|
|
|
/* Do not suspend when scanning or ROC*/
|
|
if (down_trylock(&hw_priv->scan.lock))
|
|
goto revert1;
|
|
|
|
/* Do not suspend when probe is doing */
|
|
if (delayed_work_pending(&hw_priv->scan.probe_work))
|
|
goto revert2;
|
|
|
|
/* record suspend start time */
|
|
begin = jiffies;
|
|
|
|
/* wait uitil bes2600 finish current pending operation */
|
|
if (!bes2600_pwr_device_is_idle(hw_priv)) {
|
|
/* clear power busy event */
|
|
bes2600_pwr_set_busy_event_with_timeout(hw_priv, BES_PWR_LOCK_ON_TX, 10);
|
|
|
|
/* wait device enter lp mode */
|
|
if (wait_event_timeout(hw_priv->bes_power.dev_lp_wq,
|
|
bes2600_pwr_device_is_idle(hw_priv), HZ * 5) <= 0) {
|
|
bes_err("wait device idle timeout\n");
|
|
busy_event_buffer = kmalloc(4096, GFP_KERNEL);
|
|
|
|
if(!busy_event_buffer)
|
|
goto revert2;
|
|
|
|
if(bes2600_pwr_busy_event_record(hw_priv, busy_event_buffer, 4096) == 0) {
|
|
bes_devel("%s\n", busy_event_buffer);
|
|
} else {
|
|
bes_err("busy event show failed\n");
|
|
}
|
|
|
|
kfree(busy_event_buffer);
|
|
goto revert2;
|
|
}
|
|
}
|
|
|
|
/* Lock TX. */
|
|
wsm_lock_tx_async(hw_priv);
|
|
|
|
/* mark suspend start to avoid device to exit ps mode when setting device */
|
|
bes2600_pwr_suspend_start(hw_priv);
|
|
|
|
/* set filters and offload based on interface */
|
|
bes2600_for_each_vif(hw_priv, priv, i) {
|
|
#ifdef P2P_MULTIVIF
|
|
if ((i == (CW12XX_MAX_VIFS - 1)) || !priv)
|
|
#else
|
|
if (!priv)
|
|
#endif
|
|
continue;
|
|
|
|
ret = __bes2600_wow_suspend(priv,
|
|
wowlan);
|
|
if (ret) {
|
|
for (; i >= 0; i--) {
|
|
if (!hw_priv->vif_list[i])
|
|
continue;
|
|
priv = (struct bes2600_vif *)
|
|
hw_priv->vif_list[i]->drv_priv;
|
|
__bes2600_wow_resume(priv);
|
|
}
|
|
goto revert3;
|
|
}
|
|
}
|
|
|
|
/* mark suspend end */
|
|
bes2600_pwr_suspend_end(hw_priv);
|
|
|
|
/* Stop serving thread */
|
|
if (bes2600_bh_suspend(hw_priv)) {
|
|
bes_err("%s: bes2600_bh_suspend failed\n",
|
|
__func__);
|
|
bes2600_wow_resume(hw);
|
|
return -EBUSY;
|
|
}
|
|
|
|
/* Force resume if event is coming from the device. */
|
|
if (atomic_read(&hw_priv->bh_rx)) {
|
|
bes_devel("%s: incoming event present - resume\n",
|
|
__func__);
|
|
bes2600_wow_resume(hw);
|
|
return -EAGAIN;
|
|
}
|
|
|
|
/* calculate the time consumed by bes2600 suspend flow */
|
|
end = jiffies;
|
|
diff = end - begin;
|
|
bes_devel("%s consume %d ms\n", __func__, jiffies_to_msecs(diff));
|
|
|
|
return 0;
|
|
|
|
revert3:
|
|
bes2600_pwr_suspend_end(hw_priv);
|
|
wsm_unlock_tx(hw_priv);
|
|
revert2:
|
|
up(&hw_priv->scan.lock);
|
|
revert1:
|
|
up(&hw_priv->conf_lock);
|
|
return -EBUSY;
|
|
}
|
|
|
|
static void bes2600_set_ehter_and_udp_filter(struct bes2600_common *hw_priv,
|
|
struct wsm_ether_type_filter_hdr *ether_type, struct wsm_udp_port_filter_hdr *udp_type,
|
|
int if_id)
|
|
{
|
|
/* Set UDP filter */
|
|
wsm_set_udp_port_filter(hw_priv, udp_type, if_id);
|
|
|
|
/* Set ethernet frame type filter */
|
|
wsm_set_ether_type_filter(hw_priv, ether_type, if_id);
|
|
}
|
|
|
|
static int __bes2600_wow_suspend(struct bes2600_vif *priv,
|
|
struct cfg80211_wowlan *wowlan)
|
|
{
|
|
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
|
struct bes2600_pm_state_vif *pm_state_vif = &priv->pm_state_vif;
|
|
struct bes2600_suspend_state *state;
|
|
int ret;
|
|
|
|
#ifdef MCAST_FWDING
|
|
struct wsm_forwarding_offload fwdoffload = {
|
|
.fwenable = 0x1,
|
|
.flags = 0x1,
|
|
};
|
|
#endif
|
|
|
|
/* Do not suspend when join work is scheduled */
|
|
if (work_pending(&priv->join_work))
|
|
goto revert1;
|
|
|
|
bes2600_set_ehter_and_udp_filter(hw_priv, &bes2600_ether_type_filter_on.hdr,
|
|
&bes2600_udp_port_filter_on.hdr, priv->if_id);
|
|
|
|
/* Set ipv6 filer */
|
|
#ifdef IPV6_FILTERING
|
|
wsm_set_ipv6_filter(hw_priv, &bes2600_ipv6_filter_on.hdr, priv->if_id);
|
|
#endif
|
|
|
|
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
|
WARN_ON(wsm_set_keepalive_filter(priv, true));
|
|
|
|
/* Set Multicast Address Filter */
|
|
if (priv->multicast_filter.numOfAddresses) {
|
|
priv->multicast_filter.enable = __cpu_to_le32(2);
|
|
wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
|
|
}
|
|
|
|
#ifdef MCAST_FWDING
|
|
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
|
WARN_ON(wsm_set_forwarding_offlad(hw_priv,
|
|
&fwdoffload,priv->if_id));
|
|
#endif
|
|
|
|
/* Allocate state */
|
|
state = kzalloc(sizeof(struct bes2600_suspend_state), GFP_KERNEL);
|
|
if (!state)
|
|
goto revert2;
|
|
|
|
/* Store delayed work states. */
|
|
state->bss_loss_tmo =
|
|
bes2600_suspend_work(&priv->bss_loss_work);
|
|
state->connection_loss_tmo =
|
|
bes2600_suspend_work(&priv->connection_loss_work);
|
|
state->join_tmo =
|
|
bes2600_suspend_work(&priv->join_timeout);
|
|
state->link_id_gc =
|
|
bes2600_suspend_work(&priv->link_id_gc_work);
|
|
|
|
ret = timer_pending(&priv->mcast_timeout);
|
|
if (ret)
|
|
goto revert3;
|
|
|
|
/* Store suspend state */
|
|
pm_state_vif->suspend_state = state;
|
|
|
|
return 0;
|
|
|
|
revert3:
|
|
bes2600_resume_work(hw_priv, &priv->bss_loss_work,
|
|
state->bss_loss_tmo);
|
|
bes2600_resume_work(hw_priv, &priv->connection_loss_work,
|
|
state->connection_loss_tmo);
|
|
bes2600_resume_work(hw_priv, &priv->join_timeout,
|
|
state->join_tmo);
|
|
bes2600_resume_work(hw_priv, &priv->link_id_gc_work,
|
|
state->link_id_gc);
|
|
kfree(state);
|
|
revert2:
|
|
wsm_set_udp_port_filter(hw_priv, &bes2600_udp_port_filter_off,
|
|
priv->if_id);
|
|
wsm_set_ether_type_filter(hw_priv, &bes2600_ether_type_filter_off,
|
|
priv->if_id);
|
|
|
|
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
|
WARN_ON(wsm_set_keepalive_filter(priv, false));
|
|
|
|
/* Set Multicast Address Filter */
|
|
if (priv->multicast_filter.numOfAddresses) {
|
|
priv->multicast_filter.enable = __cpu_to_le32(1);
|
|
wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
|
|
}
|
|
|
|
|
|
#ifdef MCAST_FWDING
|
|
fwdoffload.flags = 0x0;
|
|
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
|
WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id));
|
|
#endif
|
|
revert1:
|
|
up(&hw_priv->conf_lock);
|
|
return -EBUSY;
|
|
}
|
|
|
|
int bes2600_wow_resume(struct ieee80211_hw *hw)
|
|
{
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
struct bes2600_vif *priv;
|
|
int i, ret = 0;
|
|
|
|
bes_devel("bes2600_wow_resume enter\n");
|
|
WARN_ON(!atomic_read(&hw_priv->num_vifs));
|
|
|
|
up(&hw_priv->scan.lock);
|
|
|
|
/* Resume BH thread */
|
|
WARN_ON(bes2600_bh_resume(hw_priv));
|
|
|
|
/* mark resume start to avoid device to exit ps mode when setting device */
|
|
bes2600_pwr_resume_start(hw_priv);
|
|
|
|
/* set filters and offload based on interface */
|
|
bes2600_for_each_vif(hw_priv, priv, i) {
|
|
#ifdef P2P_MULTIVIF
|
|
if ((i == (CW12XX_MAX_VIFS - 1)) || !priv)
|
|
#else
|
|
if (!priv)
|
|
#endif
|
|
continue;
|
|
ret = __bes2600_wow_resume(priv);
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
/* mark resume end */
|
|
bes2600_pwr_resume_end(hw_priv);
|
|
|
|
wsm_unlock_tx(hw_priv);
|
|
/* Unlock configuration mutex */
|
|
up(&hw_priv->conf_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int __bes2600_wow_resume(struct bes2600_vif *priv)
|
|
{
|
|
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
|
struct bes2600_pm_state_vif *pm_state_vif = &priv->pm_state_vif;
|
|
struct bes2600_suspend_state *state;
|
|
|
|
#ifdef MCAST_FWDING
|
|
struct wsm_forwarding_offload fwdoffload = {
|
|
.fwenable = 0x1,
|
|
.flags = 0x0,
|
|
};
|
|
#endif
|
|
state = pm_state_vif->suspend_state;
|
|
pm_state_vif->suspend_state = NULL;
|
|
|
|
#ifdef ROAM_OFFLOAD
|
|
if((priv->vif->type == NL80211_IFTYPE_STATION)
|
|
&& (priv->join_status == BES2600_JOIN_STATUS_STA))
|
|
bes2600_hw_sched_scan_stop(hw_priv);
|
|
#endif /*ROAM_OFFLOAD*/
|
|
|
|
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
|
WARN_ON(wsm_set_keepalive_filter(priv, false));
|
|
|
|
/* Set Multicast Address Filter */
|
|
if (priv->multicast_filter.numOfAddresses) {
|
|
priv->multicast_filter.enable = __cpu_to_le32(1);
|
|
wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
|
|
}
|
|
|
|
#ifdef MCAST_FWDING
|
|
if (priv->join_status == BES2600_JOIN_STATUS_AP)
|
|
WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id));
|
|
#endif
|
|
|
|
/* Resume delayed work */
|
|
bes2600_resume_work(hw_priv, &priv->bss_loss_work,
|
|
state->bss_loss_tmo);
|
|
bes2600_resume_work(hw_priv, &priv->connection_loss_work,
|
|
state->connection_loss_tmo);
|
|
bes2600_resume_work(hw_priv, &priv->join_timeout,
|
|
state->join_tmo);
|
|
bes2600_resume_work(hw_priv, &priv->link_id_gc_work,
|
|
state->link_id_gc);
|
|
|
|
/* Remove UDP port filter */
|
|
wsm_set_udp_port_filter(hw_priv, &bes2600_udp_port_filter_off,
|
|
priv->if_id);
|
|
|
|
/* Remove ethernet frame type filter */
|
|
wsm_set_ether_type_filter(hw_priv, &bes2600_ether_type_filter_off,
|
|
priv->if_id);
|
|
|
|
/* Remove ipv6 filer */
|
|
#ifdef IPV6_FILTERING
|
|
wsm_set_ipv6_filter(hw_priv, &bes2600_ipv6_filter_off.hdr, priv->if_id);
|
|
#endif
|
|
|
|
/* Free memory */
|
|
kfree(state);
|
|
|
|
return 0;
|
|
}
|