Add driver command and event for signal strength monitoring
authorJouni Malinen <j@w1.fi>
Sun, 28 Mar 2010 22:31:04 +0000 (15:31 -0700)
committerJouni Malinen <j@w1.fi>
Sun, 28 Mar 2010 22:31:04 +0000 (15:31 -0700)
src/drivers/driver.h
src/drivers/driver_ndis.c
src/drivers/driver_nl80211.c
wpa_supplicant/driver_i.h

index 00ca97f..1d51a49 100644 (file)
@@ -1758,6 +1758,22 @@ struct wpa_driver_ops {
         * @priv: Private driver interface data
         */
        void (*resume)(void *priv);
+
+       /**
+        * signal_monitor - Set signal monitoring parameters
+        * @priv: Private driver interface data
+        * @threshold: Threshold value for signal change events; 0 = disabled
+        * @hysteresis: Minimum change in signal strength before indicating a
+        *      new event
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        *
+        * This function can be used to configure monitoring of signal strength
+        * with the current AP. Whenever signal strength drops below the
+        * %threshold value or increases above it, EVENT_SIGNAL_CHANGE event
+        * should be generated assuming the signal strength has changed at
+        * least %hysteresis from the previously indicated signal change event.
+        */
+       int (*signal_monitor)(void *priv, int threshold, int hysteresis);
 };
 
 
@@ -2030,7 +2046,16 @@ enum wpa_event_type {
         * %wpa_supplicant, this event is used only if the send_eapol() handler
         * is used to override the use of l2_packet for EAPOL frame TX.
         */
-       EVENT_EAPOL_RX
+       EVENT_EAPOL_RX,
+
+       /**
+        * EVENT_SIGNAL_CHANGE - Indicate change in signal strength
+        *
+        * This event is used to indicate changes in the signal strength
+        * observed in frames received from the current AP if signal strength
+        * monitoring has been enabled with signal_monitor().
+        */
+       EVENT_SIGNAL_CHANGE
 };
 
 
@@ -2402,6 +2427,13 @@ union wpa_event_data {
                const u8 *data;
                size_t data_len;
        } eapol_rx;
+
+       /**
+        * struct signal_change - Data for EVENT_SIGNAL_CHANGE events
+        */
+       struct signal_change {
+               int above_threshold;
+       } signal_change;
 };
 
 /**
index 6f28452..662b1a1 100644 (file)
@@ -3274,5 +3274,6 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = {
        NULL /* disable_11b_rates */,
        NULL /* deinit_ap */,
        NULL /* suspend */,
-       NULL /* resume */
+       NULL /* resume */,
+       NULL /* signal_monitor */
 };
index 3fe62a7..b1007fe 100644 (file)
@@ -859,6 +859,7 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
        };
        struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
        enum nl80211_cqm_rssi_threshold_event event;
+       union wpa_event_data ed;
 
        if (tb[NL80211_ATTR_CQM] == NULL ||
            nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
@@ -870,13 +871,21 @@ static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
        if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
                return;
        event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+
+       os_memset(&ed, 0, sizeof(ed));
+
        if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
                wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
                           "event: RSSI high");
+               ed.signal_change.above_threshold = 1;
        } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
                wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
                           "event: RSSI low");
-       }
+               ed.signal_change.above_threshold = 0;
+       } else
+               return;
+
+       wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
 }
 
 
@@ -5190,6 +5199,44 @@ static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
 }
 
 
+static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg, *cqm = NULL;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
+                  "hysteresis=%d", threshold, hysteresis);
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_SET_CQM, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+       cqm = nlmsg_alloc();
+       if (cqm == NULL)
+               return -1;
+
+       NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold);
+       NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis);
+       nla_put_nested(msg, NL80211_ATTR_CQM, cqm);
+
+       if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+               return 0;
+       msg = NULL;
+
+nla_put_failure:
+       if (cqm)
+               nlmsg_free(cqm);
+       nlmsg_free(msg);
+       return -1;
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -5249,4 +5296,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .deinit_ap = wpa_driver_nl80211_deinit_ap,
        .resume = wpa_driver_nl80211_resume,
        .send_ft_action = nl80211_send_ft_action,
+       .signal_monitor = nl80211_signal_monitor,
 };
index 8b312a4..71240c3 100644 (file)
@@ -466,4 +466,13 @@ static inline void wpa_drv_resume(struct wpa_supplicant *wpa_s)
                wpa_s->driver->resume(wpa_s->drv_priv);
 }
 
+static inline int wpa_drv_signal_monitor(struct wpa_supplicant *wpa_s,
+                                        int threshold, int hysteresis)
+{
+       if (wpa_s->driver->signal_monitor)
+               return wpa_s->driver->signal_monitor(wpa_s->drv_priv,
+                                                    threshold, hysteresis);
+       return -1;
+}
+
 #endif /* DRIVER_I_H */