Add suspend/resume notifications
authorJouni Malinen <j@w1.fi>
Sat, 27 Feb 2010 16:46:02 +0000 (18:46 +0200)
committerJouni Malinen <j@w1.fi>
Sat, 27 Feb 2010 16:46:02 +0000 (18:46 +0200)
wpa_supplicant can now be notified of suspend/resume events, e.g.,
from pm-action scripts. This allows wpa_supplicant to clear information
that may become invalid during a suspend operation.

src/drivers/driver.h
src/drivers/driver_ndis.c
src/drivers/driver_nl80211.c
wpa_supplicant/ctrl_iface.c
wpa_supplicant/driver_i.h
wpa_supplicant/examples/60_wpa_supplicant [new file with mode: 0755]
wpa_supplicant/notify.c
wpa_supplicant/notify.h
wpa_supplicant/scan.c
wpa_supplicant/wpa_cli.c
wpa_supplicant/wpa_supplicant_i.h

index 51e1898..0412586 100644 (file)
@@ -1731,6 +1731,18 @@ struct wpa_driver_ops {
         * normal station operations like scanning to be completed.
         */
        int (*deinit_ap)(void *priv);
+
+       /**
+        * suspend - Notification on system suspend/hibernate event
+        * @priv: Private driver interface data
+        */
+       void (*suspend)(void *priv);
+
+       /**
+        * resume - Notification on system resume/thaw event
+        * @priv: Private driver interface data
+        */
+       void (*resume)(void *priv);
 };
 
 
index 29597a6..6f28452 100644 (file)
@@ -3272,5 +3272,7 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = {
        NULL /* cancel_remain_on_channel */,
        NULL /* probe_req_report */,
        NULL /* disable_11b_rates */,
-       NULL /* deinit_ap */
+       NULL /* deinit_ap */,
+       NULL /* suspend */,
+       NULL /* resume */
 };
index aff6b81..6c2949d 100644 (file)
@@ -4996,6 +4996,16 @@ static int wpa_driver_nl80211_deinit_ap(void *priv)
 }
 
 
+static void wpa_driver_nl80211_resume(void *priv)
+{
+       struct wpa_driver_nl80211_data *drv = priv;
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on "
+                          "resume event");
+       }
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .name = "nl80211",
        .desc = "Linux nl80211/cfg80211",
@@ -5053,4 +5063,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .release_interface_addr = wpa_driver_nl80211_release_interface_addr,
        .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates,
        .deinit_ap = wpa_driver_nl80211_deinit_ap,
+       .resume = wpa_driver_nl80211_resume,
 };
index 7242e5c..33a1d94 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Control interface (shared code for all backends)
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -1812,6 +1812,10 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
                                                   reply_size);
 #endif /* CONFIG_AP */
+       } else if (os_strcmp(buf, "SUSPEND") == 0) {
+               wpas_notify_suspend(wpa_s->global);
+       } else if (os_strcmp(buf, "RESUME") == 0) {
+               wpas_notify_resume(wpa_s->global);
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
@@ -2038,6 +2042,10 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
                        global, reply, reply_size);
        } else if (os_strcmp(buf, "TERMINATE") == 0) {
                wpa_supplicant_terminate_proc(global);
+       } else if (os_strcmp(buf, "SUSPEND") == 0) {
+               wpas_notify_suspend(global);
+       } else if (os_strcmp(buf, "RESUME") == 0) {
+               wpas_notify_resume(global);
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
index bd31253..7f64c4c 100644 (file)
@@ -456,4 +456,16 @@ static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s)
        return 0;
 }
 
+static inline void wpa_drv_suspend(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->suspend)
+               wpa_s->driver->suspend(wpa_s->drv_priv);
+}
+
+static inline void wpa_drv_resume(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->resume)
+               wpa_s->driver->resume(wpa_s->drv_priv);
+}
+
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/examples/60_wpa_supplicant b/wpa_supplicant/examples/60_wpa_supplicant
new file mode 100755 (executable)
index 0000000..f570ad3
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# /etc/pm/sleep.d/60_wpa_supplicant
+# Action script to notify wpa_supplicant of pm-action events.
+
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+
+WPACLI=wpa_cli
+
+case "$1" in
+       suspend|hibernate)
+               $WPACLI suspend
+               ;;
+       resume|thaw)
+               $WPACLI resume
+               ;;
+esac
index 6a55cdc..ac65b4f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - Event notifications
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -22,6 +22,8 @@
 #include "dbus/dbus_common.h"
 #include "dbus/dbus_old.h"
 #include "dbus/dbus_new.h"
+#include "driver_i.h"
+#include "scan.h"
 #include "notify.h"
 
 int wpas_notify_supplicant_initialized(struct wpa_global *global)
@@ -301,3 +303,37 @@ void wpas_notify_debug_show_keys_changed(struct wpa_global *global)
 {
        wpas_dbus_signal_debug_show_keys_changed(global);
 }
+
+
+void wpas_notify_suspend(struct wpa_global *global)
+{
+       struct wpa_supplicant *wpa_s;
+
+       os_get_time(&global->suspend_time);
+       wpa_printf(MSG_DEBUG, "System suspend notification");
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
+               wpa_drv_suspend(wpa_s);
+}
+
+
+void wpas_notify_resume(struct wpa_global *global)
+{
+       struct os_time now;
+       int slept;
+       struct wpa_supplicant *wpa_s;
+
+       if (global->suspend_time.sec == 0)
+               slept = -1;
+       else {
+               os_get_time(&now);
+               slept = now.sec - global->suspend_time.sec;
+       }
+       wpa_printf(MSG_DEBUG, "System resume notification (slept %d seconds)",
+                  slept);
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               wpa_drv_resume(wpa_s);
+               if (wpa_s->wpa_state == WPA_DISCONNECTED)
+                       wpa_supplicant_req_scan(wpa_s, 0, 100000);
+       }
+}
index 7b1febe..2e70bdb 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - Event notifications
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -75,5 +75,7 @@ void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name);
 void wpas_notify_debug_level_changed(struct wpa_global *global);
 void wpas_notify_debug_timestamp_changed(struct wpa_global *global);
 void wpas_notify_debug_show_keys_changed(struct wpa_global *global);
+void wpas_notify_suspend(struct wpa_global *global);
+void wpas_notify_resume(struct wpa_global *global);
 
 #endif /* NOTIFY_H */
index 56f062f..9c61690 100644 (file)
@@ -216,6 +216,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 #endif /* CONFIG_WPS */
        struct wpa_driver_scan_params params;
        size_t max_ssids;
+       enum wpa_states prev_state;
 
        if (wpa_s->disconnected && !wpa_s->scan_req) {
                wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
@@ -271,6 +272,7 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 
        os_memset(&params, 0, sizeof(params));
 
+       prev_state = wpa_s->wpa_state;
        if (wpa_s->wpa_state == WPA_DISCONNECTED ||
            wpa_s->wpa_state == WPA_INACTIVE)
                wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
@@ -368,6 +370,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 
        if (ret) {
                wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
+               if (prev_state != wpa_s->wpa_state)
+                       wpa_supplicant_set_state(wpa_s, prev_state);
                wpa_supplicant_req_scan(wpa_s, 10, 0);
        } else
                wpa_s->scan_runs++;
index d3b9e1f..518afa0 100644 (file)
@@ -1403,6 +1403,18 @@ static int wpa_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
 #endif /* CONFIG_AP */
 
 
+static int wpa_cli_cmd_suspend(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "SUSPEND");
+}
+
+
+static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "RESUME");
+}
+
+
 enum wpa_cli_cmd_flags {
        cli_cmd_flag_none               = 0x00,
        cli_cmd_flag_sensitive          = 0x01
@@ -1597,6 +1609,10 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
          cli_cmd_flag_none,
          "= get information about all associated stations (AP)" },
 #endif /* CONFIG_AP */
+       { "suspend", wpa_cli_cmd_suspend, cli_cmd_flag_none,
+         "= notification of suspend/hibernate" },
+       { "resume", wpa_cli_cmd_resume, cli_cmd_flag_none,
+         "= notification of resume/thaw" },
        { NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
index c96fd3b..bc56517 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - Internal definitions
- * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -194,6 +194,7 @@ struct wpa_global {
        struct wpas_dbus_priv *dbus;
        void **drv_priv;
        size_t drv_count;
+       struct os_time suspend_time;
 };