blob: 88183110f7099f70733a55ea1a351f9bf670b51b [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 *
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
24struct rfkill_event {
25 u32 idx;
26 u8 type;
27 u8 op;
28 u8 soft;
29 u8 hard;
30} STRUCT_PACKED;
31
32enum rfkill_operation {
33 RFKILL_OP_ADD = 0,
34 RFKILL_OP_DEL,
35 RFKILL_OP_CHANGE,
36 RFKILL_OP_CHANGE_ALL,
37};
38
39enum 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
52struct rfkill_data {
53 struct rfkill_config *cfg;
54 int fd;
55 int blocked;
56};
57
58
59static 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
106struct 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
165fail2:
166 close(rfkill->fd);
167fail:
168 os_free(rfkill);
169 return NULL;
170}
171
172
173void 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
188int rfkill_is_blocked(struct rfkill_data *rfkill)
189{
190 if (rfkill == NULL)
191 return 0;
192
193 return rfkill->blocked;
194}