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
18struct rfkill_event {
19	u32 idx;
20	u8 type;
21	u8 op;
22	u8 soft;
23	u8 hard;
24} STRUCT_PACKED;
25
26enum rfkill_operation {
27	RFKILL_OP_ADD = 0,
28	RFKILL_OP_DEL,
29	RFKILL_OP_CHANGE,
30	RFKILL_OP_CHANGE_ALL,
31};
32
33enum 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
46struct rfkill_data {
47	struct rfkill_config *cfg;
48	int fd;
49	int blocked;
50};
51
52
53static 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
100struct 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
159fail2:
160	close(rfkill->fd);
161fail:
162	os_free(rfkill);
163	return NULL;
164}
165
166
167void 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
182int rfkill_is_blocked(struct rfkill_data *rfkill)
183{
184	if (rfkill == NULL)
185		return 0;
186
187	return rfkill->blocked;
188}
189