diff options
Diffstat (limited to 'contrib/wpa/src/drivers/rfkill.c')
-rw-r--r-- | contrib/wpa/src/drivers/rfkill.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/contrib/wpa/src/drivers/rfkill.c b/contrib/wpa/src/drivers/rfkill.c new file mode 100644 index 0000000..45b26c4 --- /dev/null +++ b/contrib/wpa/src/drivers/rfkill.c @@ -0,0 +1,188 @@ +/* + * 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 "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; +} |