rfkill.c revision 8d520ff1dc2da35cdca849e982051b86468016d8
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
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}
195