xref: /netbsd-src/external/bsd/wpa/dist/src/drivers/rfkill.c (revision 36ebd06e5ab61115eab7acca17a2350fc8222071)
1111b9fd8Schristos /*
2111b9fd8Schristos  * Linux rfkill helper functions for driver wrappers
3111b9fd8Schristos  * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
4111b9fd8Schristos  *
5e604d861Schristos  * This software may be distributed under the terms of the BSD license.
6e604d861Schristos  * See README for more details.
7111b9fd8Schristos  */
8111b9fd8Schristos 
9111b9fd8Schristos #include "includes.h"
10111b9fd8Schristos #include <fcntl.h>
11*36ebd06eSchristos #include <limits.h>
12111b9fd8Schristos 
13111b9fd8Schristos #include "utils/common.h"
14111b9fd8Schristos #include "utils/eloop.h"
15111b9fd8Schristos #include "rfkill.h"
16111b9fd8Schristos 
17111b9fd8Schristos #define RFKILL_EVENT_SIZE_V1 8
18111b9fd8Schristos 
19111b9fd8Schristos struct rfkill_event {
20111b9fd8Schristos 	u32 idx;
21111b9fd8Schristos 	u8 type;
22111b9fd8Schristos 	u8 op;
23111b9fd8Schristos 	u8 soft;
24111b9fd8Schristos 	u8 hard;
25111b9fd8Schristos } STRUCT_PACKED;
26111b9fd8Schristos 
27111b9fd8Schristos enum rfkill_operation {
28111b9fd8Schristos 	RFKILL_OP_ADD = 0,
29111b9fd8Schristos 	RFKILL_OP_DEL,
30111b9fd8Schristos 	RFKILL_OP_CHANGE,
31111b9fd8Schristos 	RFKILL_OP_CHANGE_ALL,
32111b9fd8Schristos };
33111b9fd8Schristos 
34111b9fd8Schristos enum rfkill_type {
35111b9fd8Schristos 	RFKILL_TYPE_ALL = 0,
36111b9fd8Schristos 	RFKILL_TYPE_WLAN,
37111b9fd8Schristos 	RFKILL_TYPE_BLUETOOTH,
38111b9fd8Schristos 	RFKILL_TYPE_UWB,
39111b9fd8Schristos 	RFKILL_TYPE_WIMAX,
40111b9fd8Schristos 	RFKILL_TYPE_WWAN,
41111b9fd8Schristos 	RFKILL_TYPE_GPS,
42111b9fd8Schristos 	RFKILL_TYPE_FM,
43111b9fd8Schristos 	NUM_RFKILL_TYPES,
44111b9fd8Schristos };
45111b9fd8Schristos 
46111b9fd8Schristos 
47111b9fd8Schristos struct rfkill_data {
48111b9fd8Schristos 	struct rfkill_config *cfg;
49111b9fd8Schristos 	int fd;
50111b9fd8Schristos 	int blocked;
51*36ebd06eSchristos 	uint32_t idx;
52111b9fd8Schristos };
53111b9fd8Schristos 
54111b9fd8Schristos 
rfkill_receive(int sock,void * eloop_ctx,void * sock_ctx)55111b9fd8Schristos static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
56111b9fd8Schristos {
57111b9fd8Schristos 	struct rfkill_data *rfkill = eloop_ctx;
58111b9fd8Schristos 	struct rfkill_event event;
59111b9fd8Schristos 	ssize_t len;
60111b9fd8Schristos 	int new_blocked;
61111b9fd8Schristos 
62111b9fd8Schristos 	len = read(rfkill->fd, &event, sizeof(event));
63111b9fd8Schristos 	if (len < 0) {
64111b9fd8Schristos 		wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
65111b9fd8Schristos 			   strerror(errno));
66111b9fd8Schristos 		return;
67111b9fd8Schristos 	}
68111b9fd8Schristos 	if (len != RFKILL_EVENT_SIZE_V1) {
69111b9fd8Schristos 		wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
70111b9fd8Schristos 			   "%d (expected %d)",
71111b9fd8Schristos 			   (int) len, RFKILL_EVENT_SIZE_V1);
72111b9fd8Schristos 		return;
73111b9fd8Schristos 	}
74*36ebd06eSchristos 	if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx)
75*36ebd06eSchristos 		return;
76*36ebd06eSchristos 
77111b9fd8Schristos 	wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
78111b9fd8Schristos 		   "op=%u soft=%u hard=%u",
79111b9fd8Schristos 		   event.idx, event.type, event.op, event.soft,
80111b9fd8Schristos 		   event.hard);
81111b9fd8Schristos 
82111b9fd8Schristos 	if (event.hard) {
83111b9fd8Schristos 		wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
84111b9fd8Schristos 		new_blocked = 1;
85111b9fd8Schristos 	} else if (event.soft) {
86111b9fd8Schristos 		wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
87111b9fd8Schristos 		new_blocked = 1;
88111b9fd8Schristos 	} else {
89111b9fd8Schristos 		wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
90111b9fd8Schristos 		new_blocked = 0;
91111b9fd8Schristos 	}
92111b9fd8Schristos 
93111b9fd8Schristos 	if (new_blocked != rfkill->blocked) {
94111b9fd8Schristos 		rfkill->blocked = new_blocked;
95111b9fd8Schristos 		if (new_blocked)
96111b9fd8Schristos 			rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
97111b9fd8Schristos 		else
98111b9fd8Schristos 			rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
99111b9fd8Schristos 	}
100111b9fd8Schristos }
101111b9fd8Schristos 
102111b9fd8Schristos 
rfkill_init(struct rfkill_config * cfg)103111b9fd8Schristos struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
104111b9fd8Schristos {
105111b9fd8Schristos 	struct rfkill_data *rfkill;
106111b9fd8Schristos 	struct rfkill_event event;
107111b9fd8Schristos 	ssize_t len;
108*36ebd06eSchristos 	char *phy = NULL, *rfk_phy;
109*36ebd06eSchristos 	char buf[24 + IFNAMSIZ + 1];
110*36ebd06eSchristos 	char buf2[31 + 11 + 1];
111*36ebd06eSchristos 	int found = 0;
112111b9fd8Schristos 
113111b9fd8Schristos 	rfkill = os_zalloc(sizeof(*rfkill));
114111b9fd8Schristos 	if (rfkill == NULL)
115111b9fd8Schristos 		return NULL;
116111b9fd8Schristos 
117*36ebd06eSchristos 	os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211",
118*36ebd06eSchristos 		    cfg->ifname);
119*36ebd06eSchristos 	phy = realpath(buf, NULL);
120*36ebd06eSchristos 	if (!phy) {
121*36ebd06eSchristos 		wpa_printf(MSG_INFO, "rfkill: Cannot get wiphy information");
122*36ebd06eSchristos 		goto fail;
123*36ebd06eSchristos 	}
124*36ebd06eSchristos 
125111b9fd8Schristos 	rfkill->cfg = cfg;
126111b9fd8Schristos 	rfkill->fd = open("/dev/rfkill", O_RDONLY);
127111b9fd8Schristos 	if (rfkill->fd < 0) {
128111b9fd8Schristos 		wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
129111b9fd8Schristos 			   "device");
130111b9fd8Schristos 		goto fail;
131111b9fd8Schristos 	}
132111b9fd8Schristos 
133111b9fd8Schristos 	if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
134111b9fd8Schristos 		wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
135111b9fd8Schristos 			   "%s", strerror(errno));
136111b9fd8Schristos 		goto fail2;
137111b9fd8Schristos 	}
138111b9fd8Schristos 
139111b9fd8Schristos 	for (;;) {
140111b9fd8Schristos 		len = read(rfkill->fd, &event, sizeof(event));
141111b9fd8Schristos 		if (len < 0) {
142111b9fd8Schristos 			if (errno == EAGAIN)
143111b9fd8Schristos 				break; /* No more entries */
144111b9fd8Schristos 			wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
145111b9fd8Schristos 				   strerror(errno));
146111b9fd8Schristos 			break;
147111b9fd8Schristos 		}
148111b9fd8Schristos 		if (len != RFKILL_EVENT_SIZE_V1) {
149111b9fd8Schristos 			wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
150111b9fd8Schristos 				   "%d (expected %d)",
151111b9fd8Schristos 				   (int) len, RFKILL_EVENT_SIZE_V1);
152111b9fd8Schristos 			continue;
153111b9fd8Schristos 		}
154*36ebd06eSchristos 		if (event.op != RFKILL_OP_ADD ||
155*36ebd06eSchristos 		    event.type != RFKILL_TYPE_WLAN)
156*36ebd06eSchristos 			continue;
157*36ebd06eSchristos 
158*36ebd06eSchristos 		os_snprintf(buf2, sizeof(buf2),
159*36ebd06eSchristos 			    "/sys/class/rfkill/rfkill%d/device", event.idx);
160*36ebd06eSchristos 		rfk_phy = realpath(buf2, NULL);
161*36ebd06eSchristos 		if (!rfk_phy)
162*36ebd06eSchristos 			goto fail2;
163*36ebd06eSchristos 		found = os_strcmp(phy, rfk_phy) == 0;
164*36ebd06eSchristos 		free(rfk_phy);
165*36ebd06eSchristos 
166*36ebd06eSchristos 		if (!found)
167*36ebd06eSchristos 			continue;
168*36ebd06eSchristos 
169111b9fd8Schristos 		wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
170111b9fd8Schristos 			   "op=%u soft=%u hard=%u",
171111b9fd8Schristos 			   event.idx, event.type, event.op, event.soft,
172111b9fd8Schristos 			   event.hard);
173*36ebd06eSchristos 
174*36ebd06eSchristos 		rfkill->idx = event.idx;
175111b9fd8Schristos 		if (event.hard) {
176111b9fd8Schristos 			wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
177111b9fd8Schristos 			rfkill->blocked = 1;
178111b9fd8Schristos 		} else if (event.soft) {
179111b9fd8Schristos 			wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
180111b9fd8Schristos 			rfkill->blocked = 1;
181111b9fd8Schristos 		}
182*36ebd06eSchristos 		break;
183111b9fd8Schristos 	}
184111b9fd8Schristos 
185*36ebd06eSchristos 	if (!found)
186*36ebd06eSchristos 		goto fail2;
187*36ebd06eSchristos 
188*36ebd06eSchristos 	free(phy);
189111b9fd8Schristos 	eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
190111b9fd8Schristos 
191111b9fd8Schristos 	return rfkill;
192111b9fd8Schristos 
193111b9fd8Schristos fail2:
194111b9fd8Schristos 	close(rfkill->fd);
195111b9fd8Schristos fail:
196111b9fd8Schristos 	os_free(rfkill);
197*36ebd06eSchristos 	/* use standard free function to match realpath() */
198*36ebd06eSchristos 	free(phy);
199111b9fd8Schristos 	return NULL;
200111b9fd8Schristos }
201111b9fd8Schristos 
202111b9fd8Schristos 
rfkill_deinit(struct rfkill_data * rfkill)203111b9fd8Schristos void rfkill_deinit(struct rfkill_data *rfkill)
204111b9fd8Schristos {
205111b9fd8Schristos 	if (rfkill == NULL)
206111b9fd8Schristos 		return;
207111b9fd8Schristos 
208111b9fd8Schristos 	if (rfkill->fd >= 0) {
209111b9fd8Schristos 		eloop_unregister_read_sock(rfkill->fd);
210111b9fd8Schristos 		close(rfkill->fd);
211111b9fd8Schristos 	}
212111b9fd8Schristos 
213111b9fd8Schristos 	os_free(rfkill->cfg);
214111b9fd8Schristos 	os_free(rfkill);
215111b9fd8Schristos }
216111b9fd8Schristos 
217111b9fd8Schristos 
rfkill_is_blocked(struct rfkill_data * rfkill)218111b9fd8Schristos int rfkill_is_blocked(struct rfkill_data *rfkill)
219111b9fd8Schristos {
220111b9fd8Schristos 	if (rfkill == NULL)
221111b9fd8Schristos 		return 0;
222111b9fd8Schristos 
223111b9fd8Schristos 	return rfkill->blocked;
224111b9fd8Schristos }
225