• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
rfkill_receive(int sock,void * eloop_ctx,void * sock_ctx)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 
rfkill_init(struct rfkill_config * cfg)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 
rfkill_deinit(struct rfkill_data * rfkill)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 
rfkill_is_blocked(struct rfkill_data * rfkill)182 int rfkill_is_blocked(struct rfkill_data *rfkill)
183 {
184 	if (rfkill == NULL)
185 		return 0;
186 
187 	return rfkill->blocked;
188 }
189