ba20341e70
Source: https://github.com/cringeops/bes2600 Source: https://github.com/cringeops/bes2600/pull/14 Source: https://github.com/cringeops/bes2600/pull/17 Source: https://github.com/cringeops/bes2600/pull/20
604 lines
15 KiB
C
604 lines
15 KiB
C
/*
|
|
* Mac80211 power management API for BES2600 drivers
|
|
*
|
|
* Copyright (c) 2011, Bestechnic
|
|
* Author:
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#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;
|
|
}
|