0e92fb8fae
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 state. Fix this by using the existing (but unused) ifname argument when the rfkill instance is created to match to a specific rfkill index only. As a P2P Device interface does not have a netdev interface associated with it, use the name of a sibling interface to initialize the rfkill context for the P2P Device interface. For nl80211, as the wiphy index is known only after getting the driver capabilities from the kernel, move the initialization of the rfkill object to wpa_driver_nl80211_finish_drv_init(). Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Ilan Peer <ilan.peer@intel.com>
223 lines
4.6 KiB
C
223 lines
4.6 KiB
C
/*
|
|
* Linux rfkill helper functions for driver wrappers
|
|
* Copyright (c) 2010, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This software may be distributed under the terms of the BSD license.
|
|
* See README for more details.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
#include <fcntl.h>
|
|
#include <limits.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;
|
|
uint32_t idx;
|
|
};
|
|
|
|
|
|
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;
|
|
}
|
|
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.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;
|
|
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) {
|
|
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;
|
|
}
|
|
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 = os_strcmp(phy, rfk_phy) == 0;
|
|
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);
|
|
|
|
rfkill->idx = event.idx;
|
|
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;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!found)
|
|
goto fail2;
|
|
|
|
eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
|
|
|
|
return rfkill;
|
|
|
|
fail2:
|
|
close(rfkill->fd);
|
|
fail:
|
|
os_free(rfkill);
|
|
/* use standard free function to match realpath() */
|
|
free(phy);
|
|
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;
|
|
}
|