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