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