Line data Source code
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 :
55 7281 : static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
56 : {
57 7281 : struct rfkill_data *rfkill = eloop_ctx;
58 : struct rfkill_event event;
59 : ssize_t len;
60 : int new_blocked;
61 :
62 7281 : len = read(rfkill->fd, &event, sizeof(event));
63 7281 : if (len < 0) {
64 0 : wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
65 0 : strerror(errno));
66 0 : return;
67 : }
68 7281 : if (len != RFKILL_EVENT_SIZE_V1) {
69 0 : wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
70 : "%d (expected %d)",
71 : (int) len, RFKILL_EVENT_SIZE_V1);
72 0 : return;
73 : }
74 7281 : if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx)
75 7251 : return;
76 :
77 120 : wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
78 : "op=%u soft=%u hard=%u",
79 90 : event.idx, event.type, event.op, event.soft,
80 30 : event.hard);
81 :
82 30 : if (event.hard) {
83 0 : wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
84 0 : new_blocked = 1;
85 30 : } else if (event.soft) {
86 15 : wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
87 15 : new_blocked = 1;
88 : } else {
89 15 : wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
90 15 : new_blocked = 0;
91 : }
92 :
93 30 : if (new_blocked != rfkill->blocked) {
94 30 : rfkill->blocked = new_blocked;
95 30 : if (new_blocked)
96 15 : rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
97 : else
98 15 : rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
99 : }
100 : }
101 :
102 :
103 2663 : 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 2663 : char *phy = NULL, *rfk_phy;
109 : char buf[24 + IFNAMSIZ + 1];
110 : char buf2[31 + 11 + 1];
111 2663 : int found = 0;
112 :
113 2663 : rfkill = os_zalloc(sizeof(*rfkill));
114 2663 : if (rfkill == NULL)
115 0 : return NULL;
116 :
117 2663 : os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211",
118 2663 : cfg->ifname);
119 2663 : phy = realpath(buf, NULL);
120 2663 : if (!phy) {
121 0 : wpa_printf(MSG_INFO, "rfkill: Cannot get wiphy information");
122 0 : goto fail;
123 : }
124 :
125 2663 : rfkill->cfg = cfg;
126 2663 : rfkill->fd = open("/dev/rfkill", O_RDONLY);
127 2663 : if (rfkill->fd < 0) {
128 0 : wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
129 : "device");
130 0 : goto fail;
131 : }
132 :
133 2663 : if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
134 0 : wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
135 0 : "%s", strerror(errno));
136 0 : goto fail2;
137 : }
138 :
139 : for (;;) {
140 11939 : len = read(rfkill->fd, &event, sizeof(event));
141 11939 : if (len < 0) {
142 0 : if (errno == EAGAIN)
143 0 : break; /* No more entries */
144 0 : wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
145 0 : strerror(errno));
146 0 : break;
147 : }
148 11939 : if (len != RFKILL_EVENT_SIZE_V1) {
149 0 : wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
150 : "%d (expected %d)",
151 : (int) len, RFKILL_EVENT_SIZE_V1);
152 0 : continue;
153 : }
154 23878 : if (event.op != RFKILL_OP_ADD ||
155 11939 : event.type != RFKILL_TYPE_WLAN)
156 0 : continue;
157 :
158 11939 : os_snprintf(buf2, sizeof(buf2),
159 : "/sys/class/rfkill/rfkill%d/device", event.idx);
160 11939 : rfk_phy = realpath(buf2, NULL);
161 11939 : if (!rfk_phy)
162 0 : goto fail2;
163 11939 : found = os_strcmp(phy, rfk_phy) == 0;
164 11939 : free(rfk_phy);
165 :
166 11939 : if (!found)
167 9276 : continue;
168 :
169 10652 : wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
170 : "op=%u soft=%u hard=%u",
171 7989 : event.idx, event.type, event.op, event.soft,
172 2663 : event.hard);
173 :
174 2663 : rfkill->idx = event.idx;
175 2663 : if (event.hard) {
176 0 : wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
177 0 : rfkill->blocked = 1;
178 2663 : } else if (event.soft) {
179 2 : wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
180 2 : rfkill->blocked = 1;
181 : }
182 2663 : break;
183 9276 : }
184 :
185 2663 : if (!found)
186 0 : goto fail2;
187 :
188 2663 : free(phy);
189 2663 : eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
190 :
191 2663 : return rfkill;
192 :
193 : fail2:
194 0 : close(rfkill->fd);
195 : fail:
196 0 : os_free(rfkill);
197 : /* use standard free function to match realpath() */
198 0 : free(phy);
199 0 : return NULL;
200 : }
201 :
202 :
203 2666 : void rfkill_deinit(struct rfkill_data *rfkill)
204 : {
205 2666 : if (rfkill == NULL)
206 2669 : return;
207 :
208 2663 : if (rfkill->fd >= 0) {
209 2663 : eloop_unregister_read_sock(rfkill->fd);
210 2663 : close(rfkill->fd);
211 : }
212 :
213 2663 : os_free(rfkill->cfg);
214 2663 : os_free(rfkill);
215 : }
216 :
217 :
218 2654 : int rfkill_is_blocked(struct rfkill_data *rfkill)
219 : {
220 2654 : if (rfkill == NULL)
221 0 : return 0;
222 :
223 2654 : return rfkill->blocked;
224 : }
|