18d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt/*
28d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Linux rfkill helper functions for driver wrappers
38d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
48d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt *
5c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * This software may be distributed under the terms of the BSD license.
6c5ec7f57ead87efa365800228aa0b09a12d9e6c4Dmitry Shmidt * See README for more details.
78d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt */
88d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
98d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "includes.h"
108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include <fcntl.h>
118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "utils/common.h"
138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "utils/eloop.h"
148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#include "rfkill.h"
158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt#define RFKILL_EVENT_SIZE_V1 8
178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct rfkill_event {
198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u32 idx;
208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 type;
218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 op;
228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 soft;
238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	u8 hard;
248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt} STRUCT_PACKED;
258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtenum rfkill_operation {
278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	RFKILL_OP_ADD = 0,
288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	RFKILL_OP_DEL,
298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	RFKILL_OP_CHANGE,
308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	RFKILL_OP_CHANGE_ALL,
318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtenum rfkill_type {
348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	RFKILL_TYPE_ALL = 0,
358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	RFKILL_TYPE_WLAN,
368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	RFKILL_TYPE_BLUETOOTH,
378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	RFKILL_TYPE_UWB,
388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	RFKILL_TYPE_WIMAX,
398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	RFKILL_TYPE_WWAN,
408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	RFKILL_TYPE_GPS,
418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	RFKILL_TYPE_FM,
428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	NUM_RFKILL_TYPES,
438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct rfkill_data {
478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct rfkill_config *cfg;
488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int fd;
498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int blocked;
508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt};
518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstatic void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct rfkill_data *rfkill = eloop_ctx;
568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct rfkill_event event;
578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	ssize_t len;
588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	int new_blocked;
598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	len = read(rfkill->fd, &event, sizeof(event));
618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (len < 0) {
628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   strerror(errno));
648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return;
658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (len != RFKILL_EVENT_SIZE_V1) {
678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "%d (expected %d)",
698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   (int) len, RFKILL_EVENT_SIZE_V1);
708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return;
718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		   "op=%u soft=%u hard=%u",
748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		   event.idx, event.type, event.op, event.soft,
758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		   event.hard);
768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN)
778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return;
788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (event.hard) {
808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		new_blocked = 1;
828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	} else if (event.soft) {
838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		new_blocked = 1;
858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	} else {
868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		new_blocked = 0;
888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
898d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
908d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (new_blocked != rfkill->blocked) {
918d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		rfkill->blocked = new_blocked;
928d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (new_blocked)
938d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
948d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		else
958d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
968d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
978d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
988d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
998d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1008d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtstruct rfkill_data * rfkill_init(struct rfkill_config *cfg)
1018d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1028d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct rfkill_data *rfkill;
1038d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	struct rfkill_event event;
1048d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	ssize_t len;
1058d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1068d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	rfkill = os_zalloc(sizeof(*rfkill));
1078d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (rfkill == NULL)
1088d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return NULL;
1098d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1108d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	rfkill->cfg = cfg;
1118d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	rfkill->fd = open("/dev/rfkill", O_RDONLY);
1128d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (rfkill->fd < 0) {
1138d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
1148d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "device");
1158d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto fail;
1168d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1178d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1188d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
1198d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
1208d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "%s", strerror(errno));
1218d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		goto fail2;
1228d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1238d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1248d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	for (;;) {
1258d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		len = read(rfkill->fd, &event, sizeof(event));
1268d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (len < 0) {
1278d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			if (errno == EAGAIN)
1288d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				break; /* No more entries */
1298d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
1308d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				   strerror(errno));
1318d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			break;
1328d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
1338d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (len != RFKILL_EVENT_SIZE_V1) {
1348d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
1358d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				   "%d (expected %d)",
1368d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt				   (int) len, RFKILL_EVENT_SIZE_V1);
1378d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			continue;
1388d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
1398d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
1408d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   "op=%u soft=%u hard=%u",
1418d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   event.idx, event.type, event.op, event.soft,
1428d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			   event.hard);
1438d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (event.op != RFKILL_OP_ADD ||
1448d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		    event.type != RFKILL_TYPE_WLAN)
1458d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			continue;
1468d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		if (event.hard) {
1478d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
1488d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			rfkill->blocked = 1;
1498d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		} else if (event.soft) {
1508d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
1518d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt			rfkill->blocked = 1;
1528d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		}
1538d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1548d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1558d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
1568d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1578d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return rfkill;
1588d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1598d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtfail2:
1608d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	close(rfkill->fd);
1618d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtfail:
1628d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_free(rfkill);
1638d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return NULL;
1648d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1658d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1668d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1678d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtvoid rfkill_deinit(struct rfkill_data *rfkill)
1688d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1698d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (rfkill == NULL)
1708d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return;
1718d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1728d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (rfkill->fd >= 0) {
1738d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		eloop_unregister_read_sock(rfkill->fd);
1748d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		close(rfkill->fd);
1758d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	}
1768d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1778d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_free(rfkill->cfg);
1788d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	os_free(rfkill);
1798d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
1808d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1818d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1828d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidtint rfkill_is_blocked(struct rfkill_data *rfkill)
1838d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt{
1848d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	if (rfkill == NULL)
1858d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt		return 0;
1868d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt
1878d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt	return rfkill->blocked;
1888d520ff1dc2da35cdca849e982051b86468016d8Dmitry Shmidt}
189