From 1e4dcd012423b6a28f968a55886d2b27896a1586 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Fri, 19 Mar 2010 07:14:53 +0200 Subject: 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 Signed-off-by: Juuso Oikarinen Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/ieee80211_i.h | 4 +-- net/mac80211/iface.c | 2 +- net/mac80211/mlme.c | 64 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 60 insertions(+), 10 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b84126491ab..ab369e2a528 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -327,7 +327,7 @@ struct ieee80211_if_managed { struct work_struct work; struct work_struct monitor_work; struct work_struct chswitch_work; - struct work_struct beacon_loss_work; + struct work_struct beacon_connection_loss_work; unsigned long probe_timeout; int probe_send_count; @@ -1156,7 +1156,7 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, int powersave); void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, 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, enum queue_stop_reason reason); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index d5571b9420c..b4ec59a8dc0 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -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.chswitch_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. diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 0ab284c3213..865ea1cfb7b 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -851,6 +851,9 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, if (is_multicast_ether_addr(hdr->addr1)) return; + if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) + return; + mod_timer(&sdata->u.mgd.conn_mon_timer, round_jiffies_up(jiffies + IEEE80211_CONNECTION_IDLE_TIME)); } @@ -928,23 +931,68 @@ static void ieee80211_mgd_probe_ap(struct ieee80211_sub_if_data *sdata, 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 = 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) { 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); +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 ieee80211_rx_mgmt_deauth(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len) @@ -1634,7 +1682,8 @@ static void ieee80211_sta_bcn_mon_timer(unsigned long data) if (local->quiescing) 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) @@ -1686,7 +1735,7 @@ void ieee80211_sta_quiesce(struct ieee80211_sub_if_data *sdata) */ 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)) set_bit(TMR_RUNNING_TIMER, &ifmgd->timers_running); @@ -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->monitor_work, ieee80211_sta_monitor_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, (unsigned long) sdata); setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, -- cgit v1.2.3-70-g09d2 From a97c13c34509be460dea23c86f31c02daa2428b5 Mon Sep 17 00:00:00 2001 From: Juuso Oikarinen Date: Tue, 23 Mar 2010 09:02:34 +0200 Subject: mac80211: Add support for connection quality monitoring Add support for the set_cqm_config op. This op function configures the requested connection quality monitor rssi threshold and rssi hysteresis values to the hardware if the hardware supports IEEE80211_HW_SUPPORTS_CQM. For unsupported hardware, currently -EOPNOTSUPP is returned, so the mac80211 is currently not doing connection quality monitoring on the host. This could be added later, if needed. Signed-off-by: Juuso Oikarinen Reviewed-by: Johannes Berg Signed-off-by: John W. Linville --- include/net/mac80211.h | 30 ++++++++++++++++++++++++++++++ net/mac80211/cfg.c | 27 +++++++++++++++++++++++++++ net/mac80211/mlme.c | 15 +++++++++++++++ 3 files changed, 72 insertions(+) (limited to 'net/mac80211') diff --git a/include/net/mac80211.h b/include/net/mac80211.h index d14226f29ff..1a8f50af49a 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -144,6 +144,7 @@ struct ieee80211_low_level_stats { * new beacon (beaconing modes) * @BSS_CHANGED_BEACON_ENABLED: Beaconing should be * enabled/disabled (beaconing modes) + * @BSS_CHANGED_CQM: Connection quality monitor config changed */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -156,6 +157,7 @@ enum ieee80211_bss_change { BSS_CHANGED_BSSID = 1<<7, BSS_CHANGED_BEACON = 1<<8, BSS_CHANGED_BEACON_ENABLED = 1<<9, + BSS_CHANGED_CQM = 1<<10, }; /** @@ -185,6 +187,9 @@ enum ieee80211_bss_change { * @enable_beacon: whether beaconing should be enabled or not * @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info). * This field is only valid when the channel type is one of the HT types. + * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value + * implies disabled + * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis */ struct ieee80211_bss_conf { const u8 *bssid; @@ -202,6 +207,8 @@ struct ieee80211_bss_conf { u64 timestamp; u32 basic_rates; u16 ht_operation_mode; + s32 cqm_rssi_thold; + u32 cqm_rssi_hyst; }; /** @@ -959,6 +966,12 @@ enum ieee80211_tkip_key_type { * 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. + * + * @IEEE80211_HW_SUPPORTS_CQM_RSSI: + * Hardware can do connection quality monitoring - i.e. it can monitor + * connection quality related parameters, such as the RSSI level and + * provide notifications if configured trigger levels are reached. + * */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -981,6 +994,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18, IEEE80211_HW_CONNECTION_MONITOR = 1<<19, + IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<20, }; /** @@ -2390,6 +2404,22 @@ void ieee80211_beacon_loss(struct ieee80211_vif *vif); */ void ieee80211_connection_loss(struct ieee80211_vif *vif); +/** + * ieee80211_cqm_rssi_notify - inform a configured connection quality monitoring + * rssi threshold triggered + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @rssi_event: the RSSI trigger event type + * @gfp: context flags + * + * When the %IEEE80211_HW_SUPPORTS_CQM_RSSI is set, and a connection quality + * monitoring is configured with an rssi threshold, the driver will inform + * whenever the rssi level reaches the threshold. + */ +void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp); + /* Rate control API */ /** diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index b7116ef84a3..c8f520529ee 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1402,6 +1402,32 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, return 0; } +static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); + struct ieee80211_vif *vif = &sdata->vif; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + + if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) + return -EOPNOTSUPP; + + if (rssi_thold == bss_conf->cqm_rssi_thold && + rssi_hyst == bss_conf->cqm_rssi_hyst) + return 0; + + bss_conf->cqm_rssi_thold = rssi_thold; + bss_conf->cqm_rssi_hyst = rssi_hyst; + + /* tell the driver upon association, unless already associated */ + if (sdata->u.mgd.associated) + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM); + + return 0; +} + static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, const u8 *addr, @@ -1506,4 +1532,5 @@ struct cfg80211_ops mac80211_config_ops = { .remain_on_channel = ieee80211_remain_on_channel, .cancel_remain_on_channel = ieee80211_cancel_remain_on_channel, .action = ieee80211_action, + .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config, }; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 865ea1cfb7b..65eafda5738 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -750,6 +750,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, /* And the BSSID changed - we're associated now */ bss_info_changed |= BSS_CHANGED_BSSID; + /* Tell the driver to monitor connection quality (if supported) */ + if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) && + sdata->vif.bss_conf.cqm_rssi_thold) + bss_info_changed |= BSS_CHANGED_CQM; + ieee80211_bss_info_change_notify(sdata, bss_info_changed); mutex_lock(&local->iflist_mtx); @@ -2182,3 +2187,13 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata, *cookie = (unsigned long) skb; return 0; } + +void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif, + enum nl80211_cqm_rssi_threshold_event rssi_event, + gfp_t gfp) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + + cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp); +} +EXPORT_SYMBOL(ieee80211_cqm_rssi_notify); -- cgit v1.2.3-70-g09d2 From 14b44974d5a3c1ca59f6809b7313d7229eb55fd8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 24 Mar 2010 14:56:41 +0300 Subject: mac80211: remove unneed variable from ieee80211_tx_pending() We don't need "sdata" any more after: d84f323477260e773d5317ad7cbe50f76115cb47 mac80211: remove dev_hold/put calls Signed-off-by: Dan Carpenter Signed-off-by: John W. Linville --- net/mac80211/tx.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index cbe53ed4fb0..08e1f17a422 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -2010,14 +2010,12 @@ void ieee80211_tx_pending(unsigned long data) while (!skb_queue_empty(&local->pending[i])) { struct sk_buff *skb = __skb_dequeue(&local->pending[i]); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct ieee80211_sub_if_data *sdata; if (WARN_ON(!info->control.vif)) { kfree_skb(skb); continue; } - sdata = vif_to_sdata(info->control.vif); spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); -- cgit v1.2.3-70-g09d2