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