]>
Commit | Line | Data |
---|---|---|
8401a6b0 JM |
1 | /* |
2 | * Linux rfkill helper functions for driver wrappers | |
3 | * Copyright (c) 2010, Jouni Malinen <j@w1.fi> | |
4 | * | |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
8401a6b0 JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | #include <fcntl.h> | |
0e92fb8f | 11 | #include <limits.h> |
8401a6b0 JM |
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; | |
0e92fb8f | 51 | uint32_t idx; |
8401a6b0 JM |
52 | }; |
53 | ||
54 | ||
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 | } | |
0e92fb8f JB |
74 | if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx) |
75 | return; | |
76 | ||
8401a6b0 JM |
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); | |
8401a6b0 JM |
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 | ||
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; | |
0e92fb8f JB |
108 | char *phy = NULL, *rfk_phy; |
109 | char buf[24 + IFNAMSIZ + 1]; | |
110 | char buf2[31 + 11 + 1]; | |
111 | int found = 0; | |
8401a6b0 JM |
112 | |
113 | rfkill = os_zalloc(sizeof(*rfkill)); | |
114 | if (rfkill == NULL) | |
115 | return NULL; | |
116 | ||
0e92fb8f JB |
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 | ||
8401a6b0 JM |
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 | } | |
0e92fb8f JB |
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 | ||
8401a6b0 JM |
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); | |
0e92fb8f JB |
173 | |
174 | rfkill->idx = event.idx; | |
8401a6b0 JM |
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 | } | |
0e92fb8f | 182 | break; |
8401a6b0 JM |
183 | } |
184 | ||
0e92fb8f JB |
185 | if (!found) |
186 | goto fail2; | |
187 | ||
99a17351 | 188 | free(phy); |
8401a6b0 JM |
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); | |
0e92fb8f JB |
197 | /* use standard free function to match realpath() */ |
198 | free(phy); | |
8401a6b0 JM |
199 | return NULL; |
200 | } | |
201 | ||
202 | ||
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 | ||
218 | int rfkill_is_blocked(struct rfkill_data *rfkill) | |
219 | { | |
220 | if (rfkill == NULL) | |
221 | return 0; | |
222 | ||
223 | return rfkill->blocked; | |
224 | } |