xref: /freebsd/contrib/wpa/src/drivers/rfkill.c (revision c1d255d3ffdbe447de3ab875bf4e7d7accc5bfc5)
1*c1d255d3SCy Schubert /*
2*c1d255d3SCy Schubert  * Linux rfkill helper functions for driver wrappers
3*c1d255d3SCy Schubert  * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
4*c1d255d3SCy Schubert  *
5*c1d255d3SCy Schubert  * This software may be distributed under the terms of the BSD license.
6*c1d255d3SCy Schubert  * See README for more details.
7*c1d255d3SCy Schubert  */
8*c1d255d3SCy Schubert 
9*c1d255d3SCy Schubert #include "includes.h"
10*c1d255d3SCy Schubert #include <fcntl.h>
11*c1d255d3SCy Schubert #include <limits.h>
12*c1d255d3SCy Schubert 
13*c1d255d3SCy Schubert #include "utils/common.h"
14*c1d255d3SCy Schubert #include "utils/eloop.h"
15*c1d255d3SCy Schubert #include "rfkill.h"
16*c1d255d3SCy Schubert 
17*c1d255d3SCy Schubert #define RFKILL_EVENT_SIZE_V1 8
18*c1d255d3SCy Schubert 
19*c1d255d3SCy Schubert struct rfkill_event {
20*c1d255d3SCy Schubert 	u32 idx;
21*c1d255d3SCy Schubert 	u8 type;
22*c1d255d3SCy Schubert 	u8 op;
23*c1d255d3SCy Schubert 	u8 soft;
24*c1d255d3SCy Schubert 	u8 hard;
25*c1d255d3SCy Schubert } STRUCT_PACKED;
26*c1d255d3SCy Schubert 
27*c1d255d3SCy Schubert enum rfkill_operation {
28*c1d255d3SCy Schubert 	RFKILL_OP_ADD = 0,
29*c1d255d3SCy Schubert 	RFKILL_OP_DEL,
30*c1d255d3SCy Schubert 	RFKILL_OP_CHANGE,
31*c1d255d3SCy Schubert 	RFKILL_OP_CHANGE_ALL,
32*c1d255d3SCy Schubert };
33*c1d255d3SCy Schubert 
34*c1d255d3SCy Schubert enum rfkill_type {
35*c1d255d3SCy Schubert 	RFKILL_TYPE_ALL = 0,
36*c1d255d3SCy Schubert 	RFKILL_TYPE_WLAN,
37*c1d255d3SCy Schubert 	RFKILL_TYPE_BLUETOOTH,
38*c1d255d3SCy Schubert 	RFKILL_TYPE_UWB,
39*c1d255d3SCy Schubert 	RFKILL_TYPE_WIMAX,
40*c1d255d3SCy Schubert 	RFKILL_TYPE_WWAN,
41*c1d255d3SCy Schubert 	RFKILL_TYPE_GPS,
42*c1d255d3SCy Schubert 	RFKILL_TYPE_FM,
43*c1d255d3SCy Schubert 	NUM_RFKILL_TYPES,
44*c1d255d3SCy Schubert };
45*c1d255d3SCy Schubert 
46*c1d255d3SCy Schubert 
47*c1d255d3SCy Schubert struct rfkill_data {
48*c1d255d3SCy Schubert 	struct rfkill_config *cfg;
49*c1d255d3SCy Schubert 	int fd;
50*c1d255d3SCy Schubert 	int blocked;
51*c1d255d3SCy Schubert 	uint32_t idx;
52*c1d255d3SCy Schubert };
53*c1d255d3SCy Schubert 
54*c1d255d3SCy Schubert 
rfkill_receive(int sock,void * eloop_ctx,void * sock_ctx)55*c1d255d3SCy Schubert static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
56*c1d255d3SCy Schubert {
57*c1d255d3SCy Schubert 	struct rfkill_data *rfkill = eloop_ctx;
58*c1d255d3SCy Schubert 	struct rfkill_event event;
59*c1d255d3SCy Schubert 	ssize_t len;
60*c1d255d3SCy Schubert 	int new_blocked;
61*c1d255d3SCy Schubert 
62*c1d255d3SCy Schubert 	len = read(rfkill->fd, &event, sizeof(event));
63*c1d255d3SCy Schubert 	if (len < 0) {
64*c1d255d3SCy Schubert 		wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
65*c1d255d3SCy Schubert 			   strerror(errno));
66*c1d255d3SCy Schubert 		return;
67*c1d255d3SCy Schubert 	}
68*c1d255d3SCy Schubert 	if (len != RFKILL_EVENT_SIZE_V1) {
69*c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
70*c1d255d3SCy Schubert 			   "%d (expected %d)",
71*c1d255d3SCy Schubert 			   (int) len, RFKILL_EVENT_SIZE_V1);
72*c1d255d3SCy Schubert 		return;
73*c1d255d3SCy Schubert 	}
74*c1d255d3SCy Schubert 	if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx)
75*c1d255d3SCy Schubert 		return;
76*c1d255d3SCy Schubert 
77*c1d255d3SCy Schubert 	wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
78*c1d255d3SCy Schubert 		   "op=%u soft=%u hard=%u",
79*c1d255d3SCy Schubert 		   event.idx, event.type, event.op, event.soft,
80*c1d255d3SCy Schubert 		   event.hard);
81*c1d255d3SCy Schubert 
82*c1d255d3SCy Schubert 	if (event.hard) {
83*c1d255d3SCy Schubert 		wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
84*c1d255d3SCy Schubert 		new_blocked = 1;
85*c1d255d3SCy Schubert 	} else if (event.soft) {
86*c1d255d3SCy Schubert 		wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
87*c1d255d3SCy Schubert 		new_blocked = 1;
88*c1d255d3SCy Schubert 	} else {
89*c1d255d3SCy Schubert 		wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
90*c1d255d3SCy Schubert 		new_blocked = 0;
91*c1d255d3SCy Schubert 	}
92*c1d255d3SCy Schubert 
93*c1d255d3SCy Schubert 	if (new_blocked != rfkill->blocked) {
94*c1d255d3SCy Schubert 		rfkill->blocked = new_blocked;
95*c1d255d3SCy Schubert 		if (new_blocked)
96*c1d255d3SCy Schubert 			rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
97*c1d255d3SCy Schubert 		else
98*c1d255d3SCy Schubert 			rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
99*c1d255d3SCy Schubert 	}
100*c1d255d3SCy Schubert }
101*c1d255d3SCy Schubert 
102*c1d255d3SCy Schubert 
rfkill_init(struct rfkill_config * cfg)103*c1d255d3SCy Schubert struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
104*c1d255d3SCy Schubert {
105*c1d255d3SCy Schubert 	struct rfkill_data *rfkill;
106*c1d255d3SCy Schubert 	struct rfkill_event event;
107*c1d255d3SCy Schubert 	ssize_t len;
108*c1d255d3SCy Schubert 	char *phy = NULL, *rfk_phy;
109*c1d255d3SCy Schubert 	char buf[24 + IFNAMSIZ + 1];
110*c1d255d3SCy Schubert 	char buf2[31 + 11 + 1];
111*c1d255d3SCy Schubert 	int found = 0;
112*c1d255d3SCy Schubert 
113*c1d255d3SCy Schubert 	rfkill = os_zalloc(sizeof(*rfkill));
114*c1d255d3SCy Schubert 	if (rfkill == NULL)
115*c1d255d3SCy Schubert 		return NULL;
116*c1d255d3SCy Schubert 
117*c1d255d3SCy Schubert 	os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211",
118*c1d255d3SCy Schubert 		    cfg->ifname);
119*c1d255d3SCy Schubert 	phy = realpath(buf, NULL);
120*c1d255d3SCy Schubert 	if (!phy) {
121*c1d255d3SCy Schubert 		wpa_printf(MSG_INFO, "rfkill: Cannot get wiphy information");
122*c1d255d3SCy Schubert 		goto fail;
123*c1d255d3SCy Schubert 	}
124*c1d255d3SCy Schubert 
125*c1d255d3SCy Schubert 	rfkill->cfg = cfg;
126*c1d255d3SCy Schubert 	rfkill->fd = open("/dev/rfkill", O_RDONLY);
127*c1d255d3SCy Schubert 	if (rfkill->fd < 0) {
128*c1d255d3SCy Schubert 		wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
129*c1d255d3SCy Schubert 			   "device");
130*c1d255d3SCy Schubert 		goto fail;
131*c1d255d3SCy Schubert 	}
132*c1d255d3SCy Schubert 
133*c1d255d3SCy Schubert 	if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
134*c1d255d3SCy Schubert 		wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
135*c1d255d3SCy Schubert 			   "%s", strerror(errno));
136*c1d255d3SCy Schubert 		goto fail2;
137*c1d255d3SCy Schubert 	}
138*c1d255d3SCy Schubert 
139*c1d255d3SCy Schubert 	for (;;) {
140*c1d255d3SCy Schubert 		len = read(rfkill->fd, &event, sizeof(event));
141*c1d255d3SCy Schubert 		if (len < 0) {
142*c1d255d3SCy Schubert 			if (errno == EAGAIN)
143*c1d255d3SCy Schubert 				break; /* No more entries */
144*c1d255d3SCy Schubert 			wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
145*c1d255d3SCy Schubert 				   strerror(errno));
146*c1d255d3SCy Schubert 			break;
147*c1d255d3SCy Schubert 		}
148*c1d255d3SCy Schubert 		if (len != RFKILL_EVENT_SIZE_V1) {
149*c1d255d3SCy Schubert 			wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
150*c1d255d3SCy Schubert 				   "%d (expected %d)",
151*c1d255d3SCy Schubert 				   (int) len, RFKILL_EVENT_SIZE_V1);
152*c1d255d3SCy Schubert 			continue;
153*c1d255d3SCy Schubert 		}
154*c1d255d3SCy Schubert 		if (event.op != RFKILL_OP_ADD ||
155*c1d255d3SCy Schubert 		    event.type != RFKILL_TYPE_WLAN)
156*c1d255d3SCy Schubert 			continue;
157*c1d255d3SCy Schubert 
158*c1d255d3SCy Schubert 		os_snprintf(buf2, sizeof(buf2),
159*c1d255d3SCy Schubert 			    "/sys/class/rfkill/rfkill%d/device", event.idx);
160*c1d255d3SCy Schubert 		rfk_phy = realpath(buf2, NULL);
161*c1d255d3SCy Schubert 		if (!rfk_phy)
162*c1d255d3SCy Schubert 			goto fail2;
163*c1d255d3SCy Schubert 		found = os_strcmp(phy, rfk_phy) == 0;
164*c1d255d3SCy Schubert 		free(rfk_phy);
165*c1d255d3SCy Schubert 
166*c1d255d3SCy Schubert 		if (!found)
167*c1d255d3SCy Schubert 			continue;
168*c1d255d3SCy Schubert 
169*c1d255d3SCy Schubert 		wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
170*c1d255d3SCy Schubert 			   "op=%u soft=%u hard=%u",
171*c1d255d3SCy Schubert 			   event.idx, event.type, event.op, event.soft,
172*c1d255d3SCy Schubert 			   event.hard);
173*c1d255d3SCy Schubert 
174*c1d255d3SCy Schubert 		rfkill->idx = event.idx;
175*c1d255d3SCy Schubert 		if (event.hard) {
176*c1d255d3SCy Schubert 			wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
177*c1d255d3SCy Schubert 			rfkill->blocked = 1;
178*c1d255d3SCy Schubert 		} else if (event.soft) {
179*c1d255d3SCy Schubert 			wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
180*c1d255d3SCy Schubert 			rfkill->blocked = 1;
181*c1d255d3SCy Schubert 		}
182*c1d255d3SCy Schubert 		break;
183*c1d255d3SCy Schubert 	}
184*c1d255d3SCy Schubert 
185*c1d255d3SCy Schubert 	if (!found)
186*c1d255d3SCy Schubert 		goto fail2;
187*c1d255d3SCy Schubert 
188*c1d255d3SCy Schubert 	free(phy);
189*c1d255d3SCy Schubert 	eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
190*c1d255d3SCy Schubert 
191*c1d255d3SCy Schubert 	return rfkill;
192*c1d255d3SCy Schubert 
193*c1d255d3SCy Schubert fail2:
194*c1d255d3SCy Schubert 	close(rfkill->fd);
195*c1d255d3SCy Schubert fail:
196*c1d255d3SCy Schubert 	os_free(rfkill);
197*c1d255d3SCy Schubert 	/* use standard free function to match realpath() */
198*c1d255d3SCy Schubert 	free(phy);
199*c1d255d3SCy Schubert 	return NULL;
200*c1d255d3SCy Schubert }
201*c1d255d3SCy Schubert 
202*c1d255d3SCy Schubert 
rfkill_deinit(struct rfkill_data * rfkill)203*c1d255d3SCy Schubert void rfkill_deinit(struct rfkill_data *rfkill)
204*c1d255d3SCy Schubert {
205*c1d255d3SCy Schubert 	if (rfkill == NULL)
206*c1d255d3SCy Schubert 		return;
207*c1d255d3SCy Schubert 
208*c1d255d3SCy Schubert 	if (rfkill->fd >= 0) {
209*c1d255d3SCy Schubert 		eloop_unregister_read_sock(rfkill->fd);
210*c1d255d3SCy Schubert 		close(rfkill->fd);
211*c1d255d3SCy Schubert 	}
212*c1d255d3SCy Schubert 
213*c1d255d3SCy Schubert 	os_free(rfkill->cfg);
214*c1d255d3SCy Schubert 	os_free(rfkill);
215*c1d255d3SCy Schubert }
216*c1d255d3SCy Schubert 
217*c1d255d3SCy Schubert 
rfkill_is_blocked(struct rfkill_data * rfkill)218*c1d255d3SCy Schubert int rfkill_is_blocked(struct rfkill_data *rfkill)
219*c1d255d3SCy Schubert {
220*c1d255d3SCy Schubert 	if (rfkill == NULL)
221*c1d255d3SCy Schubert 		return 0;
222*c1d255d3SCy Schubert 
223*c1d255d3SCy Schubert 	return rfkill->blocked;
224*c1d255d3SCy Schubert }
225