1111b9fd8Schristos /*
2111b9fd8Schristos * Linux rfkill helper functions for driver wrappers
3111b9fd8Schristos * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
4111b9fd8Schristos *
5e604d861Schristos * This software may be distributed under the terms of the BSD license.
6e604d861Schristos * See README for more details.
7111b9fd8Schristos */
8111b9fd8Schristos
9111b9fd8Schristos #include "includes.h"
10111b9fd8Schristos #include <fcntl.h>
11*36ebd06eSchristos #include <limits.h>
12111b9fd8Schristos
13111b9fd8Schristos #include "utils/common.h"
14111b9fd8Schristos #include "utils/eloop.h"
15111b9fd8Schristos #include "rfkill.h"
16111b9fd8Schristos
17111b9fd8Schristos #define RFKILL_EVENT_SIZE_V1 8
18111b9fd8Schristos
19111b9fd8Schristos struct rfkill_event {
20111b9fd8Schristos u32 idx;
21111b9fd8Schristos u8 type;
22111b9fd8Schristos u8 op;
23111b9fd8Schristos u8 soft;
24111b9fd8Schristos u8 hard;
25111b9fd8Schristos } STRUCT_PACKED;
26111b9fd8Schristos
27111b9fd8Schristos enum rfkill_operation {
28111b9fd8Schristos RFKILL_OP_ADD = 0,
29111b9fd8Schristos RFKILL_OP_DEL,
30111b9fd8Schristos RFKILL_OP_CHANGE,
31111b9fd8Schristos RFKILL_OP_CHANGE_ALL,
32111b9fd8Schristos };
33111b9fd8Schristos
34111b9fd8Schristos enum rfkill_type {
35111b9fd8Schristos RFKILL_TYPE_ALL = 0,
36111b9fd8Schristos RFKILL_TYPE_WLAN,
37111b9fd8Schristos RFKILL_TYPE_BLUETOOTH,
38111b9fd8Schristos RFKILL_TYPE_UWB,
39111b9fd8Schristos RFKILL_TYPE_WIMAX,
40111b9fd8Schristos RFKILL_TYPE_WWAN,
41111b9fd8Schristos RFKILL_TYPE_GPS,
42111b9fd8Schristos RFKILL_TYPE_FM,
43111b9fd8Schristos NUM_RFKILL_TYPES,
44111b9fd8Schristos };
45111b9fd8Schristos
46111b9fd8Schristos
47111b9fd8Schristos struct rfkill_data {
48111b9fd8Schristos struct rfkill_config *cfg;
49111b9fd8Schristos int fd;
50111b9fd8Schristos int blocked;
51*36ebd06eSchristos uint32_t idx;
52111b9fd8Schristos };
53111b9fd8Schristos
54111b9fd8Schristos
rfkill_receive(int sock,void * eloop_ctx,void * sock_ctx)55111b9fd8Schristos static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
56111b9fd8Schristos {
57111b9fd8Schristos struct rfkill_data *rfkill = eloop_ctx;
58111b9fd8Schristos struct rfkill_event event;
59111b9fd8Schristos ssize_t len;
60111b9fd8Schristos int new_blocked;
61111b9fd8Schristos
62111b9fd8Schristos len = read(rfkill->fd, &event, sizeof(event));
63111b9fd8Schristos if (len < 0) {
64111b9fd8Schristos wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
65111b9fd8Schristos strerror(errno));
66111b9fd8Schristos return;
67111b9fd8Schristos }
68111b9fd8Schristos if (len != RFKILL_EVENT_SIZE_V1) {
69111b9fd8Schristos wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
70111b9fd8Schristos "%d (expected %d)",
71111b9fd8Schristos (int) len, RFKILL_EVENT_SIZE_V1);
72111b9fd8Schristos return;
73111b9fd8Schristos }
74*36ebd06eSchristos if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx)
75*36ebd06eSchristos return;
76*36ebd06eSchristos
77111b9fd8Schristos wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
78111b9fd8Schristos "op=%u soft=%u hard=%u",
79111b9fd8Schristos event.idx, event.type, event.op, event.soft,
80111b9fd8Schristos event.hard);
81111b9fd8Schristos
82111b9fd8Schristos if (event.hard) {
83111b9fd8Schristos wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
84111b9fd8Schristos new_blocked = 1;
85111b9fd8Schristos } else if (event.soft) {
86111b9fd8Schristos wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
87111b9fd8Schristos new_blocked = 1;
88111b9fd8Schristos } else {
89111b9fd8Schristos wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
90111b9fd8Schristos new_blocked = 0;
91111b9fd8Schristos }
92111b9fd8Schristos
93111b9fd8Schristos if (new_blocked != rfkill->blocked) {
94111b9fd8Schristos rfkill->blocked = new_blocked;
95111b9fd8Schristos if (new_blocked)
96111b9fd8Schristos rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
97111b9fd8Schristos else
98111b9fd8Schristos rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
99111b9fd8Schristos }
100111b9fd8Schristos }
101111b9fd8Schristos
102111b9fd8Schristos
rfkill_init(struct rfkill_config * cfg)103111b9fd8Schristos struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
104111b9fd8Schristos {
105111b9fd8Schristos struct rfkill_data *rfkill;
106111b9fd8Schristos struct rfkill_event event;
107111b9fd8Schristos ssize_t len;
108*36ebd06eSchristos char *phy = NULL, *rfk_phy;
109*36ebd06eSchristos char buf[24 + IFNAMSIZ + 1];
110*36ebd06eSchristos char buf2[31 + 11 + 1];
111*36ebd06eSchristos int found = 0;
112111b9fd8Schristos
113111b9fd8Schristos rfkill = os_zalloc(sizeof(*rfkill));
114111b9fd8Schristos if (rfkill == NULL)
115111b9fd8Schristos return NULL;
116111b9fd8Schristos
117*36ebd06eSchristos os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211",
118*36ebd06eSchristos cfg->ifname);
119*36ebd06eSchristos phy = realpath(buf, NULL);
120*36ebd06eSchristos if (!phy) {
121*36ebd06eSchristos wpa_printf(MSG_INFO, "rfkill: Cannot get wiphy information");
122*36ebd06eSchristos goto fail;
123*36ebd06eSchristos }
124*36ebd06eSchristos
125111b9fd8Schristos rfkill->cfg = cfg;
126111b9fd8Schristos rfkill->fd = open("/dev/rfkill", O_RDONLY);
127111b9fd8Schristos if (rfkill->fd < 0) {
128111b9fd8Schristos wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
129111b9fd8Schristos "device");
130111b9fd8Schristos goto fail;
131111b9fd8Schristos }
132111b9fd8Schristos
133111b9fd8Schristos if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
134111b9fd8Schristos wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
135111b9fd8Schristos "%s", strerror(errno));
136111b9fd8Schristos goto fail2;
137111b9fd8Schristos }
138111b9fd8Schristos
139111b9fd8Schristos for (;;) {
140111b9fd8Schristos len = read(rfkill->fd, &event, sizeof(event));
141111b9fd8Schristos if (len < 0) {
142111b9fd8Schristos if (errno == EAGAIN)
143111b9fd8Schristos break; /* No more entries */
144111b9fd8Schristos wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
145111b9fd8Schristos strerror(errno));
146111b9fd8Schristos break;
147111b9fd8Schristos }
148111b9fd8Schristos if (len != RFKILL_EVENT_SIZE_V1) {
149111b9fd8Schristos wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
150111b9fd8Schristos "%d (expected %d)",
151111b9fd8Schristos (int) len, RFKILL_EVENT_SIZE_V1);
152111b9fd8Schristos continue;
153111b9fd8Schristos }
154*36ebd06eSchristos if (event.op != RFKILL_OP_ADD ||
155*36ebd06eSchristos event.type != RFKILL_TYPE_WLAN)
156*36ebd06eSchristos continue;
157*36ebd06eSchristos
158*36ebd06eSchristos os_snprintf(buf2, sizeof(buf2),
159*36ebd06eSchristos "/sys/class/rfkill/rfkill%d/device", event.idx);
160*36ebd06eSchristos rfk_phy = realpath(buf2, NULL);
161*36ebd06eSchristos if (!rfk_phy)
162*36ebd06eSchristos goto fail2;
163*36ebd06eSchristos found = os_strcmp(phy, rfk_phy) == 0;
164*36ebd06eSchristos free(rfk_phy);
165*36ebd06eSchristos
166*36ebd06eSchristos if (!found)
167*36ebd06eSchristos continue;
168*36ebd06eSchristos
169111b9fd8Schristos wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
170111b9fd8Schristos "op=%u soft=%u hard=%u",
171111b9fd8Schristos event.idx, event.type, event.op, event.soft,
172111b9fd8Schristos event.hard);
173*36ebd06eSchristos
174*36ebd06eSchristos rfkill->idx = event.idx;
175111b9fd8Schristos if (event.hard) {
176111b9fd8Schristos wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
177111b9fd8Schristos rfkill->blocked = 1;
178111b9fd8Schristos } else if (event.soft) {
179111b9fd8Schristos wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
180111b9fd8Schristos rfkill->blocked = 1;
181111b9fd8Schristos }
182*36ebd06eSchristos break;
183111b9fd8Schristos }
184111b9fd8Schristos
185*36ebd06eSchristos if (!found)
186*36ebd06eSchristos goto fail2;
187*36ebd06eSchristos
188*36ebd06eSchristos free(phy);
189111b9fd8Schristos eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
190111b9fd8Schristos
191111b9fd8Schristos return rfkill;
192111b9fd8Schristos
193111b9fd8Schristos fail2:
194111b9fd8Schristos close(rfkill->fd);
195111b9fd8Schristos fail:
196111b9fd8Schristos os_free(rfkill);
197*36ebd06eSchristos /* use standard free function to match realpath() */
198*36ebd06eSchristos free(phy);
199111b9fd8Schristos return NULL;
200111b9fd8Schristos }
201111b9fd8Schristos
202111b9fd8Schristos
rfkill_deinit(struct rfkill_data * rfkill)203111b9fd8Schristos void rfkill_deinit(struct rfkill_data *rfkill)
204111b9fd8Schristos {
205111b9fd8Schristos if (rfkill == NULL)
206111b9fd8Schristos return;
207111b9fd8Schristos
208111b9fd8Schristos if (rfkill->fd >= 0) {
209111b9fd8Schristos eloop_unregister_read_sock(rfkill->fd);
210111b9fd8Schristos close(rfkill->fd);
211111b9fd8Schristos }
212111b9fd8Schristos
213111b9fd8Schristos os_free(rfkill->cfg);
214111b9fd8Schristos os_free(rfkill);
215111b9fd8Schristos }
216111b9fd8Schristos
217111b9fd8Schristos
rfkill_is_blocked(struct rfkill_data * rfkill)218111b9fd8Schristos int rfkill_is_blocked(struct rfkill_data *rfkill)
219111b9fd8Schristos {
220111b9fd8Schristos if (rfkill == NULL)
221111b9fd8Schristos return 0;
222111b9fd8Schristos
223111b9fd8Schristos return rfkill->blocked;
224111b9fd8Schristos }
225