diff mbox

[v2] rfkill: match only the correct expected wiphy rfkill

Message ID 1449647728-11995-1-git-send-email-johannes@sipsolutions.net
State Changes Requested
Headers show

Commit Message

Johannes Berg Dec. 9, 2015, 7:55 a.m. UTC
From: Johannes Berg <johannes.berg@intel.com>

On systems that have multiple WLAN rfkill instances, the rfkill code
can become confused into thinking that the device was unblocked when
in fact it wasn't, because it only matches on the WLAN type.

Since it then stores the new (unblocked) state from the wrong rfkill
instance, it will never retry the failing IFF_UP operation and the
user has to toggle rfkill again, or otherwise intervene manually, in
this case to get back to operational.

Fix this by using the existing (but unused) ifname argument when the
rfkill instance is created to match to a specific rfkill index only.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 src/drivers/rfkill.c | 47 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 42 insertions(+), 5 deletions(-)

Comments

Johannes Berg Dec. 10, 2015, 2:39 p.m. UTC | #1
On Wed, 2015-12-09 at 08:55 +0100, Johannes Berg wrote:
> From: Johannes Berg <johannes.berg@intel.com>
> 
> On systems that have multiple WLAN rfkill instances, the rfkill code
> can become confused into thinking that the device was unblocked when
> in fact it wasn't, because it only matches on the WLAN type.
> 
> Since it then stores the new (unblocked) state from the wrong rfkill
> instance, it will never retry the failing IFF_UP operation and the
> user has to toggle rfkill again, or otherwise intervene manually, in
> this case to get back to operational.
> 
> Fix this by using the existing (but unused) ifname argument when the
> rfkill instance is created to match to a specific rfkill index only.
> 
Ilan found that this is broken for p2p-device, for obvious reasons (not
having a netdev, so no sysfs entry for the netdev).

We probably should change the rfkill_init() API to pass the wiphy
instead of the netdev - we'll fix it.

johannes
diff mbox

Patch

diff --git a/src/drivers/rfkill.c b/src/drivers/rfkill.c
index 45b26c46b69c..f3c943a6ae7c 100644
--- a/src/drivers/rfkill.c
+++ b/src/drivers/rfkill.c
@@ -8,6 +8,8 @@ 
 
 #include "includes.h"
 #include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
 
 #include "utils/common.h"
 #include "utils/eloop.h"
@@ -47,6 +49,7 @@  struct rfkill_data {
 	struct rfkill_config *cfg;
 	int fd;
 	int blocked;
+	uint32_t idx;
 };
 
 
@@ -69,12 +72,13 @@  static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
 			   (int) len, RFKILL_EVENT_SIZE_V1);
 		return;
 	}
+	if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx)
+		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");
@@ -102,11 +106,23 @@  struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
 	struct rfkill_data *rfkill;
 	struct rfkill_event event;
 	ssize_t len;
+	char *phy = NULL, *rfk_phy;
+	char buf[24 + IFNAMSIZ + 1];
+	char buf2[31 + 11 + 1];
+	int found = 0;
 
 	rfkill = os_zalloc(sizeof(*rfkill));
 	if (rfkill == NULL)
 		return NULL;
 
+	os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211",
+		    cfg->ifname);
+	phy = realpath(buf, NULL);
+	if (!phy) {
+		wpa_printf(MSG_INFO, "rfkill: Cannot get wiphy information");
+		goto fail;
+	}
+
 	rfkill->cfg = cfg;
 	rfkill->fd = open("/dev/rfkill", O_RDONLY);
 	if (rfkill->fd < 0) {
@@ -136,13 +152,27 @@  struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
 				   (int) len, RFKILL_EVENT_SIZE_V1);
 			continue;
 		}
+		if (event.op != RFKILL_OP_ADD ||
+		    event.type != RFKILL_TYPE_WLAN)
+			continue;
+
+		os_snprintf(buf2, sizeof(buf2),
+			    "/sys/class/rfkill/rfkill%d/device", event.idx);
+		rfk_phy = realpath(buf2, NULL);
+		if (!rfk_phy)
+			goto fail2;
+		found = !strcmp(phy, rfk_phy);
+		free(rfk_phy);
+
+		if (!found)
+			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;
+
+		rfkill->idx = event.idx;
 		if (event.hard) {
 			wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
 			rfkill->blocked = 1;
@@ -150,8 +180,12 @@  struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
 			wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
 			rfkill->blocked = 1;
 		}
+		break;
 	}
 
+	if (!found)
+		goto fail2;
+
 	eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
 
 	return rfkill;
@@ -160,6 +194,9 @@  fail2:
 	close(rfkill->fd);
 fail:
 	os_free(rfkill);
+	/* use standard free function to match realpath() */
+	if (phy)
+		free(phy);
 	return NULL;
 }