blob: 464cf7834a45cbe05e1f8e4d3640dfd01af46205 [file] [log] [blame]
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07001/*
2 * Linux rfkill helper functions for driver wrappers
3 * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
4 *
Dmitry Shmidtc5ec7f52012-03-06 16:33:24 -08005 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -07007 */
8
9#include "includes.h"
10#include <fcntl.h>
Dmitry Shmidt7d56b752015-12-22 10:59:44 -080011#include <limits.h>
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070012
13#include "utils/common.h"
14#include "utils/eloop.h"
15#include "rfkill.h"
16
17#define RFKILL_EVENT_SIZE_V1 8
18
19struct rfkill_event {
20 u32 idx;
21 u8 type;
22 u8 op;
23 u8 soft;
24 u8 hard;
25} STRUCT_PACKED;
26
27enum rfkill_operation {
28 RFKILL_OP_ADD = 0,
29 RFKILL_OP_DEL,
30 RFKILL_OP_CHANGE,
31 RFKILL_OP_CHANGE_ALL,
32};
33
34enum 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
47struct rfkill_data {
48 struct rfkill_config *cfg;
49 int fd;
50 int blocked;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -080051 uint32_t idx;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070052};
53
54
55static 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 }
Dmitry Shmidt7d56b752015-12-22 10:59:44 -080074 if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx)
75 return;
76
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070077 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);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -070081
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
103struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
104{
105 struct rfkill_data *rfkill;
106 struct rfkill_event event;
107 ssize_t len;
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800108 char *phy = NULL, *rfk_phy;
109 char buf[24 + IFNAMSIZ + 1];
110 char buf2[31 + 11 + 1];
111 int found = 0;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700112
113 rfkill = os_zalloc(sizeof(*rfkill));
114 if (rfkill == NULL)
115 return NULL;
116
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800117 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
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700125 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 }
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800154 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
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700169 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);
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800173
174 rfkill->idx = event.idx;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700175 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 }
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800182 break;
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700183 }
184
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800185 if (!found)
186 goto fail2;
187
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700188 eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
189
190 return rfkill;
191
192fail2:
193 close(rfkill->fd);
194fail:
195 os_free(rfkill);
Dmitry Shmidt7d56b752015-12-22 10:59:44 -0800196 /* use standard free function to match realpath() */
197 free(phy);
Dmitry Shmidt8d520ff2011-05-09 14:06:53 -0700198 return NULL;
199}
200
201
202void rfkill_deinit(struct rfkill_data *rfkill)
203{
204 if (rfkill == NULL)
205 return;
206
207 if (rfkill->fd >= 0) {
208 eloop_unregister_read_sock(rfkill->fd);
209 close(rfkill->fd);
210 }
211
212 os_free(rfkill->cfg);
213 os_free(rfkill);
214}
215
216
217int rfkill_is_blocked(struct rfkill_data *rfkill)
218{
219 if (rfkill == NULL)
220 return 0;
221
222 return rfkill->blocked;
223}