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