rfkill.c revision c5ec7f57ead87efa365800228aa0b09a12d9e6c4
15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/*
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Linux rfkill helper functions for driver wrappers
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * This software may be distributed under the terms of the BSD license.
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * See README for more details.
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "includes.h"
105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include <fcntl.h>
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "utils/common.h"
135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "utils/eloop.h"
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "rfkill.h"
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#define RFKILL_EVENT_SIZE_V1 8
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)struct rfkill_event {
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	u32 idx;
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	u8 type;
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	u8 op;
225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	u8 soft;
235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	u8 hard;
245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} STRUCT_PACKED;
25197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch
2653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)enum rfkill_operation {
277242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci	RFKILL_OP_ADD = 0,
2806f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles)	RFKILL_OP_DEL,
2953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)	RFKILL_OP_CHANGE,
309e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)	RFKILL_OP_CHANGE_ALL,
31e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)};
3253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)
33f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liuenum rfkill_type {
34bfe3590b1806e3ff18f46ee3af5d4b83078f305aTorne (Richard Coles)	RFKILL_TYPE_ALL = 0,
35bfe3590b1806e3ff18f46ee3af5d4b83078f305aTorne (Richard Coles)	RFKILL_TYPE_WLAN,
3651b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)	RFKILL_TYPE_BLUETOOTH,
37521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)	RFKILL_TYPE_UWB,
38521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)	RFKILL_TYPE_WIMAX,
39521d96ec04ace82590870fb04353ec4f82bb150fTorne (Richard Coles)	RFKILL_TYPE_WWAN,
405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	RFKILL_TYPE_GPS,
41c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)	RFKILL_TYPE_FM,
425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	NUM_RFKILL_TYPES,
435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)};
445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
45df95704c49daea886ddad70775bda23618d6274dBen Murdoch
469e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)struct rfkill_data {
47df95704c49daea886ddad70775bda23618d6274dBen Murdoch	struct rfkill_config *cfg;
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	int fd;
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	int blocked;
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)};
518abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
549e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles){
555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	struct rfkill_data *rfkill = eloop_ctx;
56df95704c49daea886ddad70775bda23618d6274dBen Murdoch	struct rfkill_event event;
57df95704c49daea886ddad70775bda23618d6274dBen Murdoch	ssize_t len;
5876c265b59aa821ccbf8c75ab2bb0d036e97d2956Torne (Richard Coles)	int new_blocked;
599e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
6076c265b59aa821ccbf8c75ab2bb0d036e97d2956Torne (Richard Coles)	len = read(rfkill->fd, &event, sizeof(event));
6176c265b59aa821ccbf8c75ab2bb0d036e97d2956Torne (Richard Coles)	if (len < 0) {
627242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci		wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
6376c265b59aa821ccbf8c75ab2bb0d036e97d2956Torne (Richard Coles)			   strerror(errno));
647242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci		return;
65d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)	}
66d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)	if (len != RFKILL_EVENT_SIZE_V1) {
677242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci		wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)			   "%d (expected %d)",
69d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)			   (int) len, RFKILL_EVENT_SIZE_V1);
7009380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)		return;
715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	}
725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		   "op=%u soft=%u hard=%u",
745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		   event.idx, event.type, event.op, event.soft,
755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		   event.hard);
765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN)
775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		return;
785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	if (event.hard) {
8002772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch		wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		new_blocked = 1;
825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	} else if (event.soft) {
8302772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch		wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
84e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)		new_blocked = 1;
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	} else {
865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
878abfc5808a4e34d6e03867af8bc440dee641886fTorne (Richard Coles)		new_blocked = 0;
889e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)	}
899e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	if (new_blocked != rfkill->blocked) {
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		rfkill->blocked = new_blocked;
9209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)		if (new_blocked)
9309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)			rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
9409380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)		else
9509380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)			rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
96197021e6b966cfb06891637935ef33fff06433d1Ben Murdoch	}
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
98e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)
991e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)
1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
102a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)	struct rfkill_data *rfkill;
103a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)	struct rfkill_event event;
1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	ssize_t len;
1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
106df95704c49daea886ddad70775bda23618d6274dBen Murdoch	rfkill = os_zalloc(sizeof(*rfkill));
107a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)	if (rfkill == NULL)
108a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)		return NULL;
109a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)
110a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)	rfkill->cfg = cfg;
111df95704c49daea886ddad70775bda23618d6274dBen Murdoch	rfkill->fd = open("/dev/rfkill", O_RDONLY);
112df95704c49daea886ddad70775bda23618d6274dBen Murdoch	if (rfkill->fd < 0) {
113df95704c49daea886ddad70775bda23618d6274dBen Murdoch		wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
114df95704c49daea886ddad70775bda23618d6274dBen Murdoch			   "device");
115df95704c49daea886ddad70775bda23618d6274dBen Murdoch		goto fail;
116df95704c49daea886ddad70775bda23618d6274dBen Murdoch	}
117df95704c49daea886ddad70775bda23618d6274dBen Murdoch
1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
119a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)		wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
1209e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)			   "%s", strerror(errno));
121a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)		goto fail2;
122a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)	}
123df95704c49daea886ddad70775bda23618d6274dBen Murdoch
124c0e19a689c8ac22cdc96b291a8d33a5d3b0b34a4Torne (Richard Coles)	for (;;) {
125df95704c49daea886ddad70775bda23618d6274dBen Murdoch		len = read(rfkill->fd, &event, sizeof(event));
126e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)		if (len < 0) {
1279e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)			if (errno == EAGAIN)
1289e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)				break; /* No more entries */
1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)			wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
130df95704c49daea886ddad70775bda23618d6274dBen Murdoch				   strerror(errno));
1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)			break;
132df95704c49daea886ddad70775bda23618d6274dBen Murdoch		}
1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		if (len != RFKILL_EVENT_SIZE_V1) {
1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)			wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
135a854de003a23bf3c7f95ec0f8154ada64092ff5cTorne (Richard Coles)				   "%d (expected %d)",
1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)				   (int) len, RFKILL_EVENT_SIZE_V1);
137df95704c49daea886ddad70775bda23618d6274dBen Murdoch			continue;
1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		}
1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
14002772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch			   "op=%u soft=%u hard=%u",
141f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)			   event.idx, event.type, event.op, event.soft,
142f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)			   event.hard);
1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		if (event.op != RFKILL_OP_ADD ||
144e1f1df5f01594c0e62e751e4b46e779b85c2faa5Torne (Richard Coles)		    event.type != RFKILL_TYPE_WLAN)
1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)			continue;
1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		if (event.hard) {
1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)			wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)			rfkill->blocked = 1;
1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		} else if (event.soft) {
1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)			wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)			rfkill->blocked = 1;
1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		}
153d6cdb82654e8f3343a693ca752d5c4cee0324e17Torne (Richard Coles)	}
154d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
1561e202183a5dc46166763171984b285173f8585e5Torne (Richard Coles)
1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	return rfkill;
1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)fail2:
1605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)	close(rfkill->fd);
16109380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)fail:
16209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)	os_free(rfkill);
16309380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)	return NULL;
164f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)}
165f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
166f6b7aed3f7ce69aca0d7a032d144cbd088b04393Torne (Richard Coles)
16709380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)void rfkill_deinit(struct rfkill_data *rfkill)
16809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles){
16909380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)	if (rfkill == NULL)
1705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)		return;
1717242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci
1727242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci	if (rfkill->fd >= 0) {
1737242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci		eloop_unregister_read_sock(rfkill->fd);
1747242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci		close(rfkill->fd);
1757242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci	}
176e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
177e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)	os_free(rfkill->cfg);
178e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)	os_free(rfkill);
179e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)}
180e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
181e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
182e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)int rfkill_is_blocked(struct rfkill_data *rfkill)
183e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles){
184e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)	if (rfkill == NULL)
185e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)		return 0;
186e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)
187e38fbeeb576b5094e34e038ab88d9d6a5c5c2214Torne (Richard Coles)	return rfkill->blocked;
1885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1899e12abdf8c3a23d52091ea54ebb6a04d327f9300Torne (Richard Coles)