wl1251: implement connection quality monitoring
authorDavid Gnedt <david.gnedt@davizone.at>
Sun, 30 Jan 2011 19:11:00 +0000 (20:11 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Thu, 3 Feb 2011 21:44:42 +0000 (16:44 -0500)
Implement connection quality monitoring similar to the wl1271 driver.
It triggers ieee80211_cqm_rssi_notify with the corresponding event when
RSSI drops blow RSSI threshold or rises again above the RSSI threshold.
It should be noted that wl1251 doesn't support RSSI hysteresis, instead it
uses RSSI averageing and delays events until a certain count of frames
proved RSSI change.

Signed-off-by: David Gnedt <david.gnedt@davizone.at>
Acked-by: Kalle Valo <kvalo@adurom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/wl1251/acx.c
drivers/net/wireless/wl1251/acx.h
drivers/net/wireless/wl1251/event.c
drivers/net/wireless/wl1251/main.c
drivers/net/wireless/wl1251/wl1251.h

index 8c94366..ef8370e 100644 (file)
@@ -776,6 +776,31 @@ out:
        return ret;
 }
 
        return ret;
 }
 
+int wl1251_acx_low_rssi(struct wl1251 *wl, s8 threshold, u8 weight,
+                       u8 depth, enum wl1251_acx_low_rssi_type type)
+{
+       struct acx_low_rssi *rssi;
+       int ret;
+
+       wl1251_debug(DEBUG_ACX, "acx low rssi");
+
+       rssi = kzalloc(sizeof(*rssi), GFP_KERNEL);
+       if (!rssi)
+               return -ENOMEM;
+
+       rssi->threshold = threshold;
+       rssi->weight = weight;
+       rssi->depth = depth;
+       rssi->type = type;
+
+       ret = wl1251_cmd_configure(wl, ACX_LOW_RSSI, rssi, sizeof(*rssi));
+       if (ret < 0)
+               wl1251_warning("failed to set low rssi threshold: %d", ret);
+
+       kfree(rssi);
+       return ret;
+}
+
 int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble)
 {
        struct acx_preamble *acx;
 int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble)
 {
        struct acx_preamble *acx;
index 6a19f38..c2ba100 100644 (file)
@@ -399,6 +399,49 @@ struct acx_rts_threshold {
        u8 pad[2];
 } __packed;
 
        u8 pad[2];
 } __packed;
 
+enum wl1251_acx_low_rssi_type {
+       /*
+        * The event is a "Level" indication which keeps triggering
+        * as long as the average RSSI is below the threshold.
+        */
+       WL1251_ACX_LOW_RSSI_TYPE_LEVEL = 0,
+
+       /*
+        * The event is an "Edge" indication which triggers
+        * only when the RSSI threshold is crossed from above.
+        */
+       WL1251_ACX_LOW_RSSI_TYPE_EDGE = 1,
+};
+
+struct acx_low_rssi {
+       struct acx_header header;
+
+       /*
+        * The threshold (in dBm) below (or above after low rssi
+        * indication) which the firmware generates an interrupt to the
+        * host. This parameter is signed.
+        */
+       s8 threshold;
+
+       /*
+        * The weight of the current RSSI sample, before adding the new
+        * sample, that is used to calculate the average RSSI.
+        */
+       u8 weight;
+
+       /*
+        * The number of Beacons/Probe response frames that will be
+        * received before issuing the Low or Regained RSSI event.
+        */
+       u8 depth;
+
+       /*
+        * Configures how the Low RSSI Event is triggered. Refer to
+        * enum wl1251_acx_low_rssi_type for more.
+        */
+       u8 type;
+} __packed;
+
 struct acx_beacon_filter_option {
        struct acx_header header;
 
 struct acx_beacon_filter_option {
        struct acx_header header;
 
@@ -1418,6 +1461,8 @@ int wl1251_acx_cca_threshold(struct wl1251 *wl);
 int wl1251_acx_bcn_dtim_options(struct wl1251 *wl);
 int wl1251_acx_aid(struct wl1251 *wl, u16 aid);
 int wl1251_acx_event_mbox_mask(struct wl1251 *wl, u32 event_mask);
 int wl1251_acx_bcn_dtim_options(struct wl1251 *wl);
 int wl1251_acx_aid(struct wl1251 *wl, u16 aid);
 int wl1251_acx_event_mbox_mask(struct wl1251 *wl, u32 event_mask);
+int wl1251_acx_low_rssi(struct wl1251 *wl, s8 threshold, u8 weight,
+                       u8 depth, enum wl1251_acx_low_rssi_type type);
 int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble);
 int wl1251_acx_cts_protect(struct wl1251 *wl,
                            enum acx_ctsprotect_type ctsprotect);
 int wl1251_acx_set_preamble(struct wl1251 *wl, enum acx_preamble_type preamble);
 int wl1251_acx_cts_protect(struct wl1251 *wl,
                            enum acx_ctsprotect_type ctsprotect);
index 712372e..dfc4579 100644 (file)
@@ -90,6 +90,24 @@ static int wl1251_event_process(struct wl1251 *wl, struct event_mailbox *mbox)
                }
        }
 
                }
        }
 
+       if (wl->vif && wl->rssi_thold) {
+               if (vector & ROAMING_TRIGGER_LOW_RSSI_EVENT_ID) {
+                       wl1251_debug(DEBUG_EVENT,
+                                    "ROAMING_TRIGGER_LOW_RSSI_EVENT");
+                       ieee80211_cqm_rssi_notify(wl->vif,
+                               NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+                               GFP_KERNEL);
+               }
+
+               if (vector & ROAMING_TRIGGER_REGAINED_RSSI_EVENT_ID) {
+                       wl1251_debug(DEBUG_EVENT,
+                                    "ROAMING_TRIGGER_REGAINED_RSSI_EVENT");
+                       ieee80211_cqm_rssi_notify(wl->vif,
+                               NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+                               GFP_KERNEL);
+               }
+       }
+
        return 0;
 }
 
        return 0;
 }
 
index 012e1a4..0325643 100644 (file)
@@ -502,6 +502,7 @@ static void wl1251_op_stop(struct ieee80211_hw *hw)
        wl->psm = 0;
        wl->tx_queue_stopped = false;
        wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
        wl->psm = 0;
        wl->tx_queue_stopped = false;
        wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
+       wl->rssi_thold = 0;
        wl->channel = WL1251_DEFAULT_CHANNEL;
 
        wl1251_debugfs_reset(wl);
        wl->channel = WL1251_DEFAULT_CHANNEL;
 
        wl1251_debugfs_reset(wl);
@@ -959,6 +960,16 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw,
        if (ret < 0)
                goto out;
 
        if (ret < 0)
                goto out;
 
+       if (changed & BSS_CHANGED_CQM) {
+               ret = wl1251_acx_low_rssi(wl, bss_conf->cqm_rssi_thold,
+                                         WL1251_DEFAULT_LOW_RSSI_WEIGHT,
+                                         WL1251_DEFAULT_LOW_RSSI_DEPTH,
+                                         WL1251_ACX_LOW_RSSI_TYPE_EDGE);
+               if (ret < 0)
+                       goto out;
+               wl->rssi_thold = bss_conf->cqm_rssi_thold;
+       }
+
        if (changed & BSS_CHANGED_BSSID) {
                memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
 
        if (changed & BSS_CHANGED_BSSID) {
                memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN);
 
@@ -1310,7 +1321,8 @@ int wl1251_init_ieee80211(struct wl1251 *wl)
        wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
                IEEE80211_HW_SUPPORTS_PS |
                IEEE80211_HW_BEACON_FILTER |
        wl->hw->flags = IEEE80211_HW_SIGNAL_DBM |
                IEEE80211_HW_SUPPORTS_PS |
                IEEE80211_HW_BEACON_FILTER |
-               IEEE80211_HW_SUPPORTS_UAPSD;
+               IEEE80211_HW_SUPPORTS_UAPSD |
+               IEEE80211_HW_SUPPORTS_CQM_RSSI;
 
        wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
        wl->hw->wiphy->max_scan_ssids = 1;
 
        wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
        wl->hw->wiphy->max_scan_ssids = 1;
@@ -1374,6 +1386,7 @@ struct ieee80211_hw *wl1251_alloc_hw(void)
        wl->psm_requested = false;
        wl->tx_queue_stopped = false;
        wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
        wl->psm_requested = false;
        wl->tx_queue_stopped = false;
        wl->power_level = WL1251_DEFAULT_POWER_LEVEL;
+       wl->rssi_thold = 0;
        wl->beacon_int = WL1251_DEFAULT_BEACON_INT;
        wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD;
        wl->vif = NULL;
        wl->beacon_int = WL1251_DEFAULT_BEACON_INT;
        wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD;
        wl->vif = NULL;
index 462ac5b..bb23cd5 100644 (file)
@@ -370,6 +370,8 @@ struct wl1251 {
        /* in dBm */
        int power_level;
 
        /* in dBm */
        int power_level;
 
+       int rssi_thold;
+
        struct wl1251_stats stats;
        struct wl1251_debugfs debugfs;
 
        struct wl1251_stats stats;
        struct wl1251_debugfs debugfs;
 
@@ -433,4 +435,7 @@ void wl1251_disable_interrupts(struct wl1251 *wl);
 #define WL1251_PART_WORK_REG_START     REGISTERS_BASE
 #define WL1251_PART_WORK_REG_SIZE      REGISTERS_WORK_SIZE
 
 #define WL1251_PART_WORK_REG_START     REGISTERS_BASE
 #define WL1251_PART_WORK_REG_SIZE      REGISTERS_WORK_SIZE
 
+#define WL1251_DEFAULT_LOW_RSSI_WEIGHT          10
+#define WL1251_DEFAULT_LOW_RSSI_DEPTH           10
+
 #endif
 #endif