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 8c943661f2df9090c3fab5d9ed1b5c0f4ff78d18..ef8370edace709f8c1fb3437403fe4447f7d7d12 100644 (file)
@@ -776,6 +776,31 @@ out:
        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;
index 6a19f38958f7b9acfcbeb9285d59ba1013b0f6f1..c2ba100f9b1ab3e659301f88328a4274beaf2047 100644 (file)
@@ -399,6 +399,49 @@ struct acx_rts_threshold {
        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;
 
@@ -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_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);
index 712372e50a8794ae550f91709a31589d83e4b90a..dfc4579acb06f6682395e3f2b55dcae3a8b1d081 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;
 }
 
index 012e1a4016fed0a195a2790f5b23c8ab9b884062..0325643b70731b2b87da3e87ab1e47e1dbb90491 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->rssi_thold = 0;
        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 (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);
 
@@ -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 |
-               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;
@@ -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->rssi_thold = 0;
        wl->beacon_int = WL1251_DEFAULT_BEACON_INT;
        wl->dtim_period = WL1251_DEFAULT_DTIM_PERIOD;
        wl->vif = NULL;
index 462ac5b20585b6e0e030494050c3cf2127b58150..bb23cd522b22daa9da053969afa98adfcf0bd960 100644 (file)
@@ -370,6 +370,8 @@ struct wl1251 {
        /* in dBm */
        int power_level;
 
+       int rssi_thold;
+
        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_DEFAULT_LOW_RSSI_WEIGHT          10
+#define WL1251_DEFAULT_LOW_RSSI_DEPTH           10
+
 #endif