Commit 1e4dcd01 authored by Juuso Oikarinen's avatar Juuso Oikarinen Committed by John W. Linville

mac80211: Add support for connection monitor in hardware

This patch is based on a RFC patch by Kalle Valo.

The wl1271 has a feature which handles the connection monitor logic
in hardware, basically sending periodically nullfunc frames and reporting
to the host if AP is lost, after attempting to recover by sending
probe-requests to the AP.

Add support to mac80211 by adding a new flag IEEE80211_HW_CONNECTION_MONITOR
which prevents conn_mon_timer from triggering during idle periods, and
prevents sending probe-requests to the AP if beacon-loss is indicated by the
hardware.

Cc: Kalle Valo <kalle.valo@nokia.com>
Signed-off-by: default avatarJuuso Oikarinen <juuso.oikarinen@nokia.com>
Reviewed-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 43ead78a
...@@ -954,6 +954,11 @@ enum ieee80211_tkip_key_type { ...@@ -954,6 +954,11 @@ enum ieee80211_tkip_key_type {
* Hardware can provide ack status reports of Tx frames to * Hardware can provide ack status reports of Tx frames to
* the stack. * the stack.
* *
* @IEEE80211_HW_CONNECTION_MONITOR:
* The hardware performs its own connection monitoring, including
* periodic keep-alives to the AP and probing the AP on beacon loss.
* When this flag is set, signaling beacon-loss will cause an immediate
* change to disassociated state.
*/ */
enum ieee80211_hw_flags { enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
...@@ -975,6 +980,7 @@ enum ieee80211_hw_flags { ...@@ -975,6 +980,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
IEEE80211_HW_CONNECTION_MONITOR = 1<<19,
}; };
/** /**
...@@ -2364,12 +2370,26 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw, ...@@ -2364,12 +2370,26 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
* *
* @vif: &struct ieee80211_vif pointer from the add_interface callback. * @vif: &struct ieee80211_vif pointer from the add_interface callback.
* *
* When beacon filtering is enabled with IEEE80211_HW_BEACON_FILTERING and * When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING and
* IEEE80211_CONF_PS is set, the driver needs to inform whenever the * %IEEE80211_CONF_PS is set, the driver needs to inform whenever the
* hardware is not receiving beacons with this function. * hardware is not receiving beacons with this function.
*/ */
void ieee80211_beacon_loss(struct ieee80211_vif *vif); void ieee80211_beacon_loss(struct ieee80211_vif *vif);
/**
* ieee80211_connection_loss - inform hardware has lost connection to the AP
*
* @vif: &struct ieee80211_vif pointer from the add_interface callback.
*
* When beacon filtering is enabled with %IEEE80211_HW_BEACON_FILTERING, and
* %IEEE80211_CONF_PS and %IEEE80211_HW_CONNECTION_MONITOR are set, the driver
* needs to inform if the connection to the AP has been lost.
*
* This function will cause immediate change to disassociated state,
* without connection recovery attempts.
*/
void ieee80211_connection_loss(struct ieee80211_vif *vif);
/* Rate control API */ /* Rate control API */
/** /**
......
...@@ -327,7 +327,7 @@ struct ieee80211_if_managed { ...@@ -327,7 +327,7 @@ struct ieee80211_if_managed {
struct work_struct work; struct work_struct work;
struct work_struct monitor_work; struct work_struct monitor_work;
struct work_struct chswitch_work; struct work_struct chswitch_work;
struct work_struct beacon_loss_work; struct work_struct beacon_connection_loss_work;
unsigned long probe_timeout; unsigned long probe_timeout;
int probe_send_count; int probe_send_count;
...@@ -1156,7 +1156,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, ...@@ -1156,7 +1156,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local,
int powersave); int powersave);
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
struct ieee80211_hdr *hdr); struct ieee80211_hdr *hdr);
void ieee80211_beacon_loss_work(struct work_struct *work); void ieee80211_beacon_connection_loss_work(struct work_struct *work);
void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw, void ieee80211_wake_queues_by_reason(struct ieee80211_hw *hw,
enum queue_stop_reason reason); enum queue_stop_reason reason);
......
...@@ -486,7 +486,7 @@ static int ieee80211_stop(struct net_device *dev) ...@@ -486,7 +486,7 @@ static int ieee80211_stop(struct net_device *dev)
cancel_work_sync(&sdata->u.mgd.work); cancel_work_sync(&sdata->u.mgd.work);
cancel_work_sync(&sdata->u.mgd.chswitch_work); cancel_work_sync(&sdata->u.mgd.chswitch_work);
cancel_work_sync(&sdata->u.mgd.monitor_work); cancel_work_sync(&sdata->u.mgd.monitor_work);
cancel_work_sync(&sdata->u.mgd.beacon_loss_work); cancel_work_sync(&sdata->u.mgd.beacon_connection_loss_work);
/* /*
* When we get here, the interface is marked down. * When we get here, the interface is marked down.
......
...@@ -851,6 +851,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, ...@@ -851,6 +851,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
if (is_multicast_ether_addr(hdr->addr1)) if (is_multicast_ether_addr(hdr->addr1))
return; return;
if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
return;
mod_timer(&sdata->u.mgd.conn_mon_timer, mod_timer(&sdata->u.mgd.conn_mon_timer,
round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME));
} }
...@@ -928,23 +931,68 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, ...@@ -928,23 +931,68 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata,
mutex_unlock(&ifmgd->mtx); mutex_unlock(&ifmgd->mtx);
} }
void ieee80211_beacon_loss_work(struct work_struct *work) static void __ieee80211_connection_loss(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
struct ieee80211_local *local = sdata->local;
u8 bssid[ETH_ALEN];
mutex_lock(&ifmgd->mtx);
if (!ifmgd->associated) {
mutex_unlock(&ifmgd->mtx);
return;
}
memcpy(bssid, ifmgd->associated->bssid, ETH_ALEN);
printk(KERN_DEBUG "Connection to AP %pM lost.\n", bssid);
ieee80211_set_disassoc(sdata);
ieee80211_recalc_idle(local);
mutex_unlock(&ifmgd->mtx);
/*
* must be outside lock due to cfg80211,
* but that's not a problem.
*/
ieee80211_send_deauth_disassoc(sdata, bssid,
IEEE80211_STYPE_DEAUTH,
WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY,
NULL);
}
void ieee80211_beacon_connection_loss_work(struct work_struct *work)
{ {
struct ieee80211_sub_if_data *sdata = struct ieee80211_sub_if_data *sdata =
container_of(work, struct ieee80211_sub_if_data, container_of(work, struct ieee80211_sub_if_data,
u.mgd.beacon_loss_work); u.mgd.beacon_connection_loss_work);
ieee80211_mgd_probe_ap(sdata, true); if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR)
__ieee80211_connection_loss(sdata);
else
ieee80211_mgd_probe_ap(sdata, true);
} }
void ieee80211_beacon_loss(struct ieee80211_vif *vif) void ieee80211_beacon_loss(struct ieee80211_vif *vif)
{ {
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
struct ieee80211_hw *hw = &sdata->local->hw;
ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); WARN_ON(hw->flags & IEEE80211_HW_CONNECTION_MONITOR);
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
} }
EXPORT_SYMBOL(ieee80211_beacon_loss); EXPORT_SYMBOL(ieee80211_beacon_loss);
void ieee80211_connection_loss(struct ieee80211_vif *vif)
{
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
struct ieee80211_hw *hw = &sdata->local->hw;
WARN_ON(!(hw->flags & IEEE80211_HW_CONNECTION_MONITOR));
ieee80211_queue_work(hw, &sdata->u.mgd.beacon_connection_loss_work);
}
EXPORT_SYMBOL(ieee80211_connection_loss);
static enum rx_mgmt_action __must_check static enum rx_mgmt_action __must_check
ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len) struct ieee80211_mgmt *mgmt, size_t len)
...@@ -1634,7 +1682,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) ...@@ -1634,7 +1682,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data)
if (local->quiescing) if (local->quiescing)
return; return;
ieee80211_queue_work(&sdata->local->hw, &sdata->u.mgd.beacon_loss_work); ieee80211_queue_work(&sdata->local->hw,
&sdata->u.mgd.beacon_connection_loss_work);
} }
static void ieee80211_sta_conn_mon_timer(unsigned long data) static void ieee80211_sta_conn_mon_timer(unsigned long data)
...@@ -1686,7 +1735,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) ...@@ -1686,7 +1735,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata)
*/ */
cancel_work_sync(&ifmgd->work); cancel_work_sync(&ifmgd->work);
cancel_work_sync(&ifmgd->beacon_loss_work); cancel_work_sync(&ifmgd->beacon_connection_loss_work);
if (del_timer_sync(&ifmgd->timer)) if (del_timer_sync(&ifmgd->timer))
set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running);
...@@ -1720,7 +1769,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) ...@@ -1720,7 +1769,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata)
INIT_WORK(&ifmgd->work, ieee80211_sta_work); INIT_WORK(&ifmgd->work, ieee80211_sta_work);
INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work); INIT_WORK(&ifmgd->monitor_work, ieee80211_sta_monitor_work);
INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work); INIT_WORK(&ifmgd->chswitch_work, ieee80211_chswitch_work);
INIT_WORK(&ifmgd->beacon_loss_work, ieee80211_beacon_loss_work); INIT_WORK(&ifmgd->beacon_connection_loss_work,
ieee80211_beacon_connection_loss_work);
setup_timer(&ifmgd->timer, ieee80211_sta_timer, setup_timer(&ifmgd->timer, ieee80211_sta_timer,
(unsigned long) sdata); (unsigned long) sdata);
setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment