Browse Source

Add Linux rfkill support

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.
Jouni Malinen 15 years ago
parent
commit
8401a6b028

+ 9 - 0
src/common/defs.h

@@ -136,6 +136,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)
 	 *

+ 17 - 1
src/drivers/driver.h

@@ -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
 };
 
 

+ 60 - 3
src/drivers/driver_nl80211.c

@@ -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);
 

+ 61 - 2
src/drivers/driver_wext.c

@@ -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);

+ 1 - 0
src/drivers/driver_wext.h

@@ -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;

+ 6 - 0
src/drivers/drivers.mak

@@ -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)

+ 194 - 0
src/drivers/rfkill.c

@@ -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;
+}

+ 31 - 0
src/drivers/rfkill.h

@@ -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 */

+ 16 - 6
wpa_supplicant/ctrl_iface.c

@@ -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);

+ 24 - 0
wpa_supplicant/events.c

@@ -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;

+ 5 - 0
wpa_supplicant/scan.c

@@ -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;

+ 4 - 1
wpa_supplicant/wpa_supplicant.c

@@ -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);