Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[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
12 #include "utils/common.h"
13 #include "utils/eloop.h"
14 #include "rfkill.h"
15
16 #define RFKILL_EVENT_SIZE_V1 8
17
18 struct rfkill_event {
19         u32 idx;
20         u8 type;
21         u8 op;
22         u8 soft;
23         u8 hard;
24 } STRUCT_PACKED;
25
26 enum rfkill_operation {
27         RFKILL_OP_ADD = 0,
28         RFKILL_OP_DEL,
29         RFKILL_OP_CHANGE,
30         RFKILL_OP_CHANGE_ALL,
31 };
32
33 enum rfkill_type {
34         RFKILL_TYPE_ALL = 0,
35         RFKILL_TYPE_WLAN,
36         RFKILL_TYPE_BLUETOOTH,
37         RFKILL_TYPE_UWB,
38         RFKILL_TYPE_WIMAX,
39         RFKILL_TYPE_WWAN,
40         RFKILL_TYPE_GPS,
41         RFKILL_TYPE_FM,
42         NUM_RFKILL_TYPES,
43 };
44
45
46 struct rfkill_data {
47         struct rfkill_config *cfg;
48         int fd;
49         int blocked;
50 };
51
52
53 static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
54 {
55         struct rfkill_data *rfkill = eloop_ctx;
56         struct rfkill_event event;
57         ssize_t len;
58         int new_blocked;
59
60         len = read(rfkill->fd, &event, sizeof(event));
61         if (len < 0) {
62                 wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
63                            strerror(errno));
64                 return;
65         }
66         if (len != RFKILL_EVENT_SIZE_V1) {
67                 wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
68                            "%d (expected %d)",
69                            (int) len, RFKILL_EVENT_SIZE_V1);
70                 return;
71         }
72         wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
73                    "op=%u soft=%u hard=%u",
74                    event.idx, event.type, event.op, event.soft,
75                    event.hard);
76         if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN)
77                 return;
78
79         if (event.hard) {
80                 wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
81                 new_blocked = 1;
82         } else if (event.soft) {
83                 wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
84                 new_blocked = 1;
85         } else {
86                 wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
87                 new_blocked = 0;
88         }
89
90         if (new_blocked != rfkill->blocked) {
91                 rfkill->blocked = new_blocked;
92                 if (new_blocked)
93                         rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
94                 else
95                         rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
96         }
97 }
98
99
100 struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
101 {
102         struct rfkill_data *rfkill;
103         struct rfkill_event event;
104         ssize_t len;
105
106         rfkill = os_zalloc(sizeof(*rfkill));
107         if (rfkill == NULL)
108                 return NULL;
109
110         rfkill->cfg = cfg;
111         rfkill->fd = open("/dev/rfkill", O_RDONLY);
112         if (rfkill->fd < 0) {
113                 wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
114                            "device");
115                 goto fail;
116         }
117
118         if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
119                 wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
120                            "%s", strerror(errno));
121                 goto fail2;
122         }
123
124         for (;;) {
125                 len = read(rfkill->fd, &event, sizeof(event));
126                 if (len < 0) {
127                         if (errno == EAGAIN)
128                                 break; /* No more entries */
129                         wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
130                                    strerror(errno));
131                         break;
132                 }
133                 if (len != RFKILL_EVENT_SIZE_V1) {
134                         wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
135                                    "%d (expected %d)",
136                                    (int) len, RFKILL_EVENT_SIZE_V1);
137                         continue;
138                 }
139                 wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
140                            "op=%u soft=%u hard=%u",
141                            event.idx, event.type, event.op, event.soft,
142                            event.hard);
143                 if (event.op != RFKILL_OP_ADD ||
144                     event.type != RFKILL_TYPE_WLAN)
145                         continue;
146                 if (event.hard) {
147                         wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
148                         rfkill->blocked = 1;
149                 } else if (event.soft) {
150                         wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
151                         rfkill->blocked = 1;
152                 }
153         }
154
155         eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
156
157         return rfkill;
158
159 fail2:
160         close(rfkill->fd);
161 fail:
162         os_free(rfkill);
163         return NULL;
164 }
165
166
167 void rfkill_deinit(struct rfkill_data *rfkill)
168 {
169         if (rfkill == NULL)
170                 return;
171
172         if (rfkill->fd >= 0) {
173                 eloop_unregister_read_sock(rfkill->fd);
174                 close(rfkill->fd);
175         }
176
177         os_free(rfkill->cfg);
178         os_free(rfkill);
179 }
180
181
182 int rfkill_is_blocked(struct rfkill_data *rfkill)
183 {
184         if (rfkill == NULL)
185                 return 0;
186
187         return rfkill->blocked;
188 }