Add Linux rfkill support
authorJouni Malinen <j@w1.fi>
Sun, 23 May 2010 07:27:32 +0000 (10:27 +0300)
committerJouni Malinen <j@w1.fi>
Sun, 23 May 2010 07:27:32 +0000 (10:27 +0300)
Add a new wpa_supplicant state: interface disabled. This can be used
to allow wpa_supplicant to be running with the network interface even
when the driver does not actually allow any radio operations (e.g.,
due to rfkill).

Allow driver_nl80211.c and driver_wext.c to start while rfkill is in
blocked state (i.e., when ifconfig up fails) and process rfkill
events to block/unblock WLAN.

12 files changed:
src/common/defs.h
src/drivers/driver.h
src/drivers/driver_nl80211.c
src/drivers/driver_wext.c
src/drivers/driver_wext.h
src/drivers/drivers.mak
src/drivers/rfkill.c [new file with mode: 0644]
src/drivers/rfkill.h [new file with mode: 0644]
wpa_supplicant/ctrl_iface.c
wpa_supplicant/events.c
wpa_supplicant/scan.c
wpa_supplicant/wpa_supplicant.c

index 173bbd1..f3f80a6 100644 (file)
@@ -137,6 +137,15 @@ enum wpa_states {
        WPA_DISCONNECTED,
 
        /**
+        * WPA_INTERFACE_DISABLED - Interface disabled
+        *
+        * This stat eis entered if the network interface is disabled, e.g.,
+        * due to rfkill. wpa_supplicant refuses any new operations that would
+        * use the radio until the interface has been enabled.
+        */
+       WPA_INTERFACE_DISABLED,
+
+       /**
         * WPA_INACTIVE - Inactive state (wpa_supplicant disabled)
         *
         * This state is entered if there are no enabled networks in the
index fa49da4..85ca0a5 100644 (file)
@@ -2046,7 +2046,23 @@ enum wpa_event_type {
         * observed in frames received from the current AP if signal strength
         * monitoring has been enabled with signal_monitor().
         */
-       EVENT_SIGNAL_CHANGE
+       EVENT_SIGNAL_CHANGE,
+
+       /**
+        * EVENT_INTERFACE_ENABLED - Notify that interface was enabled
+        *
+        * This event is used to indicate that the interface was enabled after
+        * having been previously disabled, e.g., due to rfkill.
+        */
+       EVENT_INTERFACE_ENABLED,
+
+       /**
+        * EVENT_INTERFACE_DISABLED - Notify that interface was disabled
+        *
+        * This event is used to indicate that the interface was disabled,
+        * e.g., due to rfkill.
+        */
+       EVENT_INTERFACE_DISABLED
 };
 
 
index a123327..136ca11 100644 (file)
@@ -33,6 +33,7 @@
 #include "linux_ioctl.h"
 #include "radiotap.h"
 #include "radiotap_iter.h"
+#include "rfkill.h"
 #include "driver.h"
 
 #ifdef CONFIG_LIBNL20
@@ -72,6 +73,7 @@ struct wpa_driver_nl80211_data {
        char brname[IFNAMSIZ];
        int ifindex;
        int if_removed;
+       struct rfkill_data *rfkill;
        struct wpa_driver_capa capa;
        int has_capability;
 
@@ -1347,6 +1349,27 @@ err1:
 }
 
 
+static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
+{
+       struct wpa_driver_nl80211_data *drv = ctx;
+       wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
+static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
+{
+       struct wpa_driver_nl80211_data *drv = ctx;
+       wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->first_bss.ifname, 1)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
+                          "after rfkill unblock");
+               return;
+       }
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
+}
+
+
 /**
  * wpa_driver_nl80211_init - Initialize nl80211 driver interface
  * @ctx: context to be used when calling wpa_supplicant functions,
@@ -1358,6 +1381,7 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
 {
        struct wpa_driver_nl80211_data *drv;
        struct netlink_config *cfg;
+       struct rfkill_config *rcfg;
        struct i802_bss *bss;
 
        drv = os_zalloc(sizeof(*drv));
@@ -1393,12 +1417,25 @@ static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
                os_free(cfg);
                goto failed;
        }
+
+       rcfg = os_zalloc(sizeof(*rcfg));
+       if (rcfg == NULL)
+               goto failed;
+       rcfg->ctx = drv;
+       os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+       rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
+       rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
+       drv->rfkill = rfkill_init(rcfg);
+       if (drv->rfkill == NULL)
+               wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
+
        if (wpa_driver_nl80211_finish_drv_init(drv))
                goto failed;
 
        return bss;
 
 failed:
+       rfkill_deinit(drv->rfkill);
        netlink_deinit(drv->netlink);
        if (drv->ioctl_sock >= 0)
                close(drv->ioctl_sock);
@@ -1459,10 +1496,17 @@ static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv)
 }
 
 
+static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+       wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
 static int
 wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
 {
        struct i802_bss *bss = &drv->first_bss;
+       int send_rfkill_event = 0;
 
        drv->ifindex = if_nametoindex(bss->ifname);
        drv->first_bss.ifindex = drv->ifindex;
@@ -1474,9 +1518,16 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
        }
 
        if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
-               wpa_printf(MSG_ERROR, "Could not set interface '%s' UP",
-                          bss->ifname);
-               return -1;
+               if (rfkill_is_blocked(drv->rfkill)) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
+                                  "interface '%s' due to rfkill",
+                                  bss->ifname);
+                       send_rfkill_event = 1;
+               } else {
+                       wpa_printf(MSG_ERROR, "nl80211: Could not set "
+                                  "interface '%s' UP", bss->ifname);
+                       return -1;
+               }
        }
 
        if (wpa_driver_nl80211_capa(drv))
@@ -1496,6 +1547,11 @@ wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
                 */
        }
 
+       if (send_rfkill_event) {
+               eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
+                                      drv, drv->ctx);
+       }
+
        return 0;
 }
 
@@ -1572,6 +1628,7 @@ static void wpa_driver_nl80211_deinit(void *priv)
 
        netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
        netlink_deinit(drv->netlink);
+       rfkill_deinit(drv->rfkill);
 
        eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
 
index 2614f23..ef3a30c 100644 (file)
@@ -31,6 +31,7 @@
 #include "priv_netlink.h"
 #include "netlink.h"
 #include "linux_ioctl.h"
+#include "rfkill.h"
 #include "driver.h"
 #include "driver_wext.h"
 
@@ -687,6 +688,27 @@ static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi,
 }
 
 
+static void wpa_driver_wext_rfkill_blocked(void *ctx)
+{
+       struct wpa_driver_wext_data *drv = ctx;
+       wpa_printf(MSG_DEBUG, "WEXT: RFKILL blocked");
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
+static void wpa_driver_wext_rfkill_unblocked(void *ctx)
+{
+       struct wpa_driver_wext_data *drv = ctx;
+       wpa_printf(MSG_DEBUG, "WEXT: RFKILL unblocked");
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1)) {
+               wpa_printf(MSG_DEBUG, "WEXT: Could not set interface UP "
+                          "after rfkill unblock");
+               return;
+       }
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED, NULL);
+}
+
+
 /**
  * wpa_driver_wext_init - Initialize WE driver interface
  * @ctx: context to be used when calling wpa_supplicant functions,
@@ -698,6 +720,7 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
 {
        struct wpa_driver_wext_data *drv;
        struct netlink_config *cfg;
+       struct rfkill_config *rcfg;
        char path[128];
        struct stat buf;
 
@@ -731,6 +754,17 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
                goto err2;
        }
 
+       rcfg = os_zalloc(sizeof(*rcfg));
+       if (rcfg == NULL)
+               goto err3;
+       rcfg->ctx = drv;
+       os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+       rcfg->blocked_cb = wpa_driver_wext_rfkill_blocked;
+       rcfg->unblocked_cb = wpa_driver_wext_rfkill_unblocked;
+       drv->rfkill = rfkill_init(rcfg);
+       if (drv->rfkill == NULL)
+               wpa_printf(MSG_DEBUG, "WEXT: RFKILL status not available");
+
        drv->mlme_sock = -1;
 
        if (wpa_driver_wext_finish_drv_init(drv) < 0)
@@ -741,6 +775,7 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
        return drv;
 
 err3:
+       rfkill_deinit(drv->rfkill);
        netlink_deinit(drv->netlink);
 err2:
        close(drv->ioctl_sock);
@@ -750,10 +785,28 @@ err1:
 }
 
 
+static void wpa_driver_wext_send_rfkill(void *eloop_ctx, void *timeout_ctx)
+{
+       wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
+
+
 static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
 {
-       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0)
-               return -1;
+       int send_rfkill_event = 0;
+
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0) {
+               if (rfkill_is_blocked(drv->rfkill)) {
+                       wpa_printf(MSG_DEBUG, "WEXT: Could not yet enable "
+                                  "interface '%s' due to rfkill",
+                                  drv->ifname);
+                       send_rfkill_event = 1;
+               } else {
+                       wpa_printf(MSG_ERROR, "WEXT: Could not set "
+                                  "interface '%s' UP", drv->ifname);
+                       return -1;
+               }
+       }
 
        /*
         * Make sure that the driver does not have any obsolete PMKID entries.
@@ -795,6 +848,11 @@ static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
        netlink_send_oper_ifla(drv->netlink, drv->ifindex,
                               1, IF_OPER_DORMANT);
 
+       if (send_rfkill_event) {
+               eloop_register_timeout(0, 0, wpa_driver_wext_send_rfkill,
+                                      drv, drv->ctx);
+       }
+
        return 0;
 }
 
@@ -822,6 +880,7 @@ void wpa_driver_wext_deinit(void *priv)
 
        netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
        netlink_deinit(drv->netlink);
+       rfkill_deinit(drv->rfkill);
 
        if (drv->mlme_sock >= 0)
                eloop_unregister_read_sock(drv->mlme_sock);
index 602c7e1..22d26a6 100644 (file)
@@ -26,6 +26,7 @@ struct wpa_driver_wext_data {
        int ifindex;
        int ifindex2;
        int if_removed;
+       struct rfkill_data *rfkill;
        u8 *assoc_req_ies;
        size_t assoc_req_ies_len;
        u8 *assoc_resp_ies;
index b76b229..b1b05cc 100644 (file)
@@ -31,6 +31,7 @@ NEED_SME=y
 NEED_AP_MLME=y
 NEED_NETLINK=y
 NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
 DRV_LIBS += -lnl
 
 ifdef CONFIG_LIBNL20
@@ -77,6 +78,7 @@ DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
 CONFIG_WIRELESS_EXTENSION=y
 NEED_NETLINK=y
 NEED_LINUX_IOCTL=y
+NEED_RFKILL=y
 endif
 
 ifdef CONFIG_DRIVER_HERMES
@@ -162,6 +164,10 @@ ifdef NEED_LINUX_IOCTL
 DRV_OBJS += ../src/drivers/linux_ioctl.o
 endif
 
+ifdef NEED_RFKILL
+DRV_OBJS += ../src/drivers/rfkill.o
+endif
+
 
 ##### COMMON VARS
 DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
diff --git a/src/drivers/rfkill.c b/src/drivers/rfkill.c
new file mode 100644 (file)
index 0000000..8818311
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 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
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <fcntl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "rfkill.h"
+
+#define RFKILL_EVENT_SIZE_V1 8
+
+struct rfkill_event {
+       u32 idx;
+       u8 type;
+       u8 op;
+       u8 soft;
+       u8 hard;
+} STRUCT_PACKED;
+
+enum rfkill_operation {
+       RFKILL_OP_ADD = 0,
+       RFKILL_OP_DEL,
+       RFKILL_OP_CHANGE,
+       RFKILL_OP_CHANGE_ALL,
+};
+
+enum rfkill_type {
+       RFKILL_TYPE_ALL = 0,
+       RFKILL_TYPE_WLAN,
+       RFKILL_TYPE_BLUETOOTH,
+       RFKILL_TYPE_UWB,
+       RFKILL_TYPE_WIMAX,
+       RFKILL_TYPE_WWAN,
+       RFKILL_TYPE_GPS,
+       RFKILL_TYPE_FM,
+       NUM_RFKILL_TYPES,
+};
+
+
+struct rfkill_data {
+       struct rfkill_config *cfg;
+       int fd;
+       int blocked;
+};
+
+
+static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct rfkill_data *rfkill = eloop_ctx;
+       struct rfkill_event event;
+       ssize_t len;
+       int new_blocked;
+
+       len = read(rfkill->fd, &event, sizeof(event));
+       if (len < 0) {
+               wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
+                          strerror(errno));
+               return;
+       }
+       if (len != RFKILL_EVENT_SIZE_V1) {
+               wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
+                          "%d (expected %d)",
+                          (int) len, RFKILL_EVENT_SIZE_V1);
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
+                  "op=%u soft=%u hard=%u",
+                  event.idx, event.type, event.op, event.soft,
+                  event.hard);
+       if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN)
+               return;
+
+       if (event.hard) {
+               wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
+               new_blocked = 1;
+       } else if (event.soft) {
+               wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
+               new_blocked = 1;
+       } else {
+               wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
+               new_blocked = 0;
+       }
+
+       if (new_blocked != rfkill->blocked) {
+               rfkill->blocked = new_blocked;
+               if (new_blocked)
+                       rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
+               else
+                       rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
+       }
+}
+
+
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
+{
+       struct rfkill_data *rfkill;
+       struct rfkill_event event;
+       ssize_t len;
+
+       rfkill = os_zalloc(sizeof(*rfkill));
+       if (rfkill == NULL)
+               return NULL;
+
+       rfkill->cfg = cfg;
+       rfkill->fd = open("/dev/rfkill", O_RDONLY);
+       if (rfkill->fd < 0) {
+               wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
+                          "device");
+               goto fail;
+       }
+
+       if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
+               wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
+                          "%s", strerror(errno));
+               goto fail2;
+       }
+
+       for (;;) {
+               len = read(rfkill->fd, &event, sizeof(event));
+               if (len < 0) {
+                       if (errno == EAGAIN)
+                               break; /* No more entries */
+                       wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
+                                  strerror(errno));
+                       break;
+               }
+               if (len != RFKILL_EVENT_SIZE_V1) {
+                       wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
+                                  "%d (expected %d)",
+                                  (int) len, RFKILL_EVENT_SIZE_V1);
+                       continue;
+               }
+               wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
+                          "op=%u soft=%u hard=%u",
+                          event.idx, event.type, event.op, event.soft,
+                          event.hard);
+               if (event.op != RFKILL_OP_ADD ||
+                   event.type != RFKILL_TYPE_WLAN)
+                       continue;
+               if (event.hard) {
+                       wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
+                       rfkill->blocked = 1;
+               } else if (event.soft) {
+                       wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
+                       rfkill->blocked = 1;
+               }
+       }
+
+       eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
+
+       return rfkill;
+
+fail2:
+       close(rfkill->fd);
+fail:
+       os_free(rfkill);
+       return NULL;
+}
+
+
+void rfkill_deinit(struct rfkill_data *rfkill)
+{
+       if (rfkill == NULL)
+               return;
+
+       if (rfkill->fd >= 0) {
+               eloop_unregister_read_sock(rfkill->fd);
+               close(rfkill->fd);
+       }
+
+       os_free(rfkill->cfg);
+       os_free(rfkill);
+}
+
+
+int rfkill_is_blocked(struct rfkill_data *rfkill)
+{
+       if (rfkill == NULL)
+               return 0;
+
+       return rfkill->blocked;
+}
diff --git a/src/drivers/rfkill.h b/src/drivers/rfkill.h
new file mode 100644 (file)
index 0000000..7a984a6
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Linux rfkill helper functions for driver wrappers
+ * Copyright (c) 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
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef RFKILL_H
+#define RFKILL_H
+
+struct rfkill_data;
+
+struct rfkill_config {
+       void *ctx;
+       char ifname[IFNAMSIZ];
+       void (*blocked_cb)(void *ctx);
+       void (*unblocked_cb)(void *ctx);
+};
+
+struct rfkill_data * rfkill_init(struct rfkill_config *cfg);
+void rfkill_deinit(struct rfkill_data *rfkill);
+int rfkill_is_blocked(struct rfkill_data *rfkill);
+
+#endif /* RFKILL_H */
index 19fea29..d4383e7 100644 (file)
@@ -1742,11 +1742,17 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strcmp(buf, "LOGOFF") == 0) {
                eapol_sm_notify_logoff(wpa_s->eapol, TRUE);
        } else if (os_strcmp(buf, "REASSOCIATE") == 0) {
-               wpa_s->disconnected = 0;
-               wpa_s->reassociate = 1;
-               wpa_supplicant_req_scan(wpa_s, 0, 0);
+               if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+                       reply_len = -1;
+               else {
+                       wpa_s->disconnected = 0;
+                       wpa_s->reassociate = 1;
+                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+               }
        } else if (os_strcmp(buf, "RECONNECT") == 0) {
-               if (wpa_s->disconnected) {
+               if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+                       reply_len = -1;
+               else if (wpa_s->disconnected) {
                        wpa_s->disconnected = 0;
                        wpa_s->reassociate = 1;
                        wpa_supplicant_req_scan(wpa_s, 0, 0);
@@ -1832,8 +1838,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
        } else if (os_strcmp(buf, "SCAN") == 0) {
-               wpa_s->scan_req = 2;
-               wpa_supplicant_req_scan(wpa_s, 0, 0);
+               if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+                       reply_len = -1;
+               else {
+                       wpa_s->scan_req = 2;
+                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+               }
        } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
                reply_len = wpa_supplicant_ctrl_iface_scan_results(
                        wpa_s, reply, reply_size);
index 2f3a303..d9801ac 100644 (file)
@@ -109,6 +109,9 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
 {
        int bssid_changed;
 
+       if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
+               return;
+
        wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
        bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
        os_memset(wpa_s->bssid, 0, ETH_ALEN);
@@ -1583,6 +1586,14 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
        struct wpa_supplicant *wpa_s = ctx;
        u16 reason_code = 0;
 
+       if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
+           event != EVENT_INTERFACE_ENABLED &&
+           event != EVENT_INTERFACE_STATUS) {
+               wpa_printf(MSG_DEBUG, "Ignore event %d while interface is "
+                          "disabled", event);
+               return;
+       }
+
        switch (event) {
        case EVENT_AUTH:
                sme_event_auth(wpa_s, data);
@@ -1726,6 +1737,19 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                bgscan_notify_signal_change(
                        wpa_s, data->signal_change.above_threshold);
                break;
+       case EVENT_INTERFACE_ENABLED:
+               wpa_printf(MSG_DEBUG, "Interface was enabled");
+               if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+                       wpa_supplicant_set_state(wpa_s,
+                                                WPA_DISCONNECTED);
+                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+               }
+               break;
+       case EVENT_INTERFACE_DISABLED:
+               wpa_printf(MSG_DEBUG, "Interface was disabled");
+               wpa_supplicant_mark_disassoc(wpa_s);
+               wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
+               break;
        default:
                wpa_printf(MSG_INFO, "Unknown event %d", event);
                break;
index 458b981..efbc347 100644 (file)
@@ -251,6 +251,11 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
        size_t max_ssids;
        enum wpa_states prev_state;
 
+       if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+               wpa_printf(MSG_DEBUG, "Skip scan - interface disabled");
+               return;
+       }
+
        if (wpa_s->disconnected && !wpa_s->scan_req) {
                wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
                return;
index 5563f0b..6c01444 100644 (file)
@@ -489,6 +489,8 @@ const char * wpa_supplicant_state_txt(enum wpa_states state)
                return "DISCONNECTED";
        case WPA_INACTIVE:
                return "INACTIVE";
+       case WPA_INTERFACE_DISABLED:
+               return "INTERFACE_DISABLED";
        case WPA_SCANNING:
                return "SCANNING";
        case WPA_AUTHENTICATING:
@@ -592,7 +594,8 @@ static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
        wpa_s->group_cipher = 0;
        wpa_s->mgmt_group_cipher = 0;
        wpa_s->key_mgmt = 0;
-       wpa_s->wpa_state = WPA_DISCONNECTED;
+       if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED)
+               wpa_s->wpa_state = WPA_DISCONNECTED;
 
        if (wpa_s->wpa_state != old_state)
                wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);