Updated to hostap_2_6
[mech_eap.git] / libeap / src / drivers / rfkill.c
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
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
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
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 }