xref: /netbsd-src/external/bsd/wpa/dist/src/drivers/rfkill.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
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 }
195