13ff40c12SJohn Marino /*
23ff40c12SJohn Marino * Linux rfkill helper functions for driver wrappers
33ff40c12SJohn Marino * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
43ff40c12SJohn Marino *
53ff40c12SJohn Marino * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino * See README for more details.
73ff40c12SJohn Marino */
83ff40c12SJohn Marino
93ff40c12SJohn Marino #include "includes.h"
103ff40c12SJohn Marino #include <fcntl.h>
11*a1157835SDaniel Fojt #include <limits.h>
123ff40c12SJohn Marino
133ff40c12SJohn Marino #include "utils/common.h"
143ff40c12SJohn Marino #include "utils/eloop.h"
153ff40c12SJohn Marino #include "rfkill.h"
163ff40c12SJohn Marino
173ff40c12SJohn Marino #define RFKILL_EVENT_SIZE_V1 8
183ff40c12SJohn Marino
193ff40c12SJohn Marino struct rfkill_event {
203ff40c12SJohn Marino u32 idx;
213ff40c12SJohn Marino u8 type;
223ff40c12SJohn Marino u8 op;
233ff40c12SJohn Marino u8 soft;
243ff40c12SJohn Marino u8 hard;
253ff40c12SJohn Marino } STRUCT_PACKED;
263ff40c12SJohn Marino
273ff40c12SJohn Marino enum rfkill_operation {
283ff40c12SJohn Marino RFKILL_OP_ADD = 0,
293ff40c12SJohn Marino RFKILL_OP_DEL,
303ff40c12SJohn Marino RFKILL_OP_CHANGE,
313ff40c12SJohn Marino RFKILL_OP_CHANGE_ALL,
323ff40c12SJohn Marino };
333ff40c12SJohn Marino
343ff40c12SJohn Marino enum rfkill_type {
353ff40c12SJohn Marino RFKILL_TYPE_ALL = 0,
363ff40c12SJohn Marino RFKILL_TYPE_WLAN,
373ff40c12SJohn Marino RFKILL_TYPE_BLUETOOTH,
383ff40c12SJohn Marino RFKILL_TYPE_UWB,
393ff40c12SJohn Marino RFKILL_TYPE_WIMAX,
403ff40c12SJohn Marino RFKILL_TYPE_WWAN,
413ff40c12SJohn Marino RFKILL_TYPE_GPS,
423ff40c12SJohn Marino RFKILL_TYPE_FM,
433ff40c12SJohn Marino NUM_RFKILL_TYPES,
443ff40c12SJohn Marino };
453ff40c12SJohn Marino
463ff40c12SJohn Marino
473ff40c12SJohn Marino struct rfkill_data {
483ff40c12SJohn Marino struct rfkill_config *cfg;
493ff40c12SJohn Marino int fd;
503ff40c12SJohn Marino int blocked;
51*a1157835SDaniel Fojt uint32_t idx;
523ff40c12SJohn Marino };
533ff40c12SJohn Marino
543ff40c12SJohn Marino
rfkill_receive(int sock,void * eloop_ctx,void * sock_ctx)553ff40c12SJohn Marino static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
563ff40c12SJohn Marino {
573ff40c12SJohn Marino struct rfkill_data *rfkill = eloop_ctx;
583ff40c12SJohn Marino struct rfkill_event event;
593ff40c12SJohn Marino ssize_t len;
603ff40c12SJohn Marino int new_blocked;
613ff40c12SJohn Marino
623ff40c12SJohn Marino len = read(rfkill->fd, &event, sizeof(event));
633ff40c12SJohn Marino if (len < 0) {
643ff40c12SJohn Marino wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
653ff40c12SJohn Marino strerror(errno));
663ff40c12SJohn Marino return;
673ff40c12SJohn Marino }
683ff40c12SJohn Marino if (len != RFKILL_EVENT_SIZE_V1) {
693ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
703ff40c12SJohn Marino "%d (expected %d)",
713ff40c12SJohn Marino (int) len, RFKILL_EVENT_SIZE_V1);
723ff40c12SJohn Marino return;
733ff40c12SJohn Marino }
74*a1157835SDaniel Fojt if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx)
75*a1157835SDaniel Fojt return;
76*a1157835SDaniel Fojt
773ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
783ff40c12SJohn Marino "op=%u soft=%u hard=%u",
793ff40c12SJohn Marino event.idx, event.type, event.op, event.soft,
803ff40c12SJohn Marino event.hard);
813ff40c12SJohn Marino
823ff40c12SJohn Marino if (event.hard) {
833ff40c12SJohn Marino wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
843ff40c12SJohn Marino new_blocked = 1;
853ff40c12SJohn Marino } else if (event.soft) {
863ff40c12SJohn Marino wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
873ff40c12SJohn Marino new_blocked = 1;
883ff40c12SJohn Marino } else {
893ff40c12SJohn Marino wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
903ff40c12SJohn Marino new_blocked = 0;
913ff40c12SJohn Marino }
923ff40c12SJohn Marino
933ff40c12SJohn Marino if (new_blocked != rfkill->blocked) {
943ff40c12SJohn Marino rfkill->blocked = new_blocked;
953ff40c12SJohn Marino if (new_blocked)
963ff40c12SJohn Marino rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
973ff40c12SJohn Marino else
983ff40c12SJohn Marino rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
993ff40c12SJohn Marino }
1003ff40c12SJohn Marino }
1013ff40c12SJohn Marino
1023ff40c12SJohn Marino
rfkill_init(struct rfkill_config * cfg)1033ff40c12SJohn Marino struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
1043ff40c12SJohn Marino {
1053ff40c12SJohn Marino struct rfkill_data *rfkill;
1063ff40c12SJohn Marino struct rfkill_event event;
1073ff40c12SJohn Marino ssize_t len;
108*a1157835SDaniel Fojt char *phy = NULL, *rfk_phy;
109*a1157835SDaniel Fojt char buf[24 + IFNAMSIZ + 1];
110*a1157835SDaniel Fojt char buf2[31 + 11 + 1];
111*a1157835SDaniel Fojt int found = 0;
1123ff40c12SJohn Marino
1133ff40c12SJohn Marino rfkill = os_zalloc(sizeof(*rfkill));
1143ff40c12SJohn Marino if (rfkill == NULL)
1153ff40c12SJohn Marino return NULL;
1163ff40c12SJohn Marino
117*a1157835SDaniel Fojt os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211",
118*a1157835SDaniel Fojt cfg->ifname);
119*a1157835SDaniel Fojt phy = realpath(buf, NULL);
120*a1157835SDaniel Fojt if (!phy) {
121*a1157835SDaniel Fojt wpa_printf(MSG_INFO, "rfkill: Cannot get wiphy information");
122*a1157835SDaniel Fojt goto fail;
123*a1157835SDaniel Fojt }
124*a1157835SDaniel Fojt
1253ff40c12SJohn Marino rfkill->cfg = cfg;
1263ff40c12SJohn Marino rfkill->fd = open("/dev/rfkill", O_RDONLY);
1273ff40c12SJohn Marino if (rfkill->fd < 0) {
1283ff40c12SJohn Marino wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
1293ff40c12SJohn Marino "device");
1303ff40c12SJohn Marino goto fail;
1313ff40c12SJohn Marino }
1323ff40c12SJohn Marino
1333ff40c12SJohn Marino if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
1343ff40c12SJohn Marino wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
1353ff40c12SJohn Marino "%s", strerror(errno));
1363ff40c12SJohn Marino goto fail2;
1373ff40c12SJohn Marino }
1383ff40c12SJohn Marino
1393ff40c12SJohn Marino for (;;) {
1403ff40c12SJohn Marino len = read(rfkill->fd, &event, sizeof(event));
1413ff40c12SJohn Marino if (len < 0) {
1423ff40c12SJohn Marino if (errno == EAGAIN)
1433ff40c12SJohn Marino break; /* No more entries */
1443ff40c12SJohn Marino wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
1453ff40c12SJohn Marino strerror(errno));
1463ff40c12SJohn Marino break;
1473ff40c12SJohn Marino }
1483ff40c12SJohn Marino if (len != RFKILL_EVENT_SIZE_V1) {
1493ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
1503ff40c12SJohn Marino "%d (expected %d)",
1513ff40c12SJohn Marino (int) len, RFKILL_EVENT_SIZE_V1);
1523ff40c12SJohn Marino continue;
1533ff40c12SJohn Marino }
154*a1157835SDaniel Fojt if (event.op != RFKILL_OP_ADD ||
155*a1157835SDaniel Fojt event.type != RFKILL_TYPE_WLAN)
156*a1157835SDaniel Fojt continue;
157*a1157835SDaniel Fojt
158*a1157835SDaniel Fojt os_snprintf(buf2, sizeof(buf2),
159*a1157835SDaniel Fojt "/sys/class/rfkill/rfkill%d/device", event.idx);
160*a1157835SDaniel Fojt rfk_phy = realpath(buf2, NULL);
161*a1157835SDaniel Fojt if (!rfk_phy)
162*a1157835SDaniel Fojt goto fail2;
163*a1157835SDaniel Fojt found = os_strcmp(phy, rfk_phy) == 0;
164*a1157835SDaniel Fojt free(rfk_phy);
165*a1157835SDaniel Fojt
166*a1157835SDaniel Fojt if (!found)
167*a1157835SDaniel Fojt continue;
168*a1157835SDaniel Fojt
1693ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
1703ff40c12SJohn Marino "op=%u soft=%u hard=%u",
1713ff40c12SJohn Marino event.idx, event.type, event.op, event.soft,
1723ff40c12SJohn Marino event.hard);
173*a1157835SDaniel Fojt
174*a1157835SDaniel Fojt rfkill->idx = event.idx;
1753ff40c12SJohn Marino if (event.hard) {
1763ff40c12SJohn Marino wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
1773ff40c12SJohn Marino rfkill->blocked = 1;
1783ff40c12SJohn Marino } else if (event.soft) {
1793ff40c12SJohn Marino wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
1803ff40c12SJohn Marino rfkill->blocked = 1;
1813ff40c12SJohn Marino }
182*a1157835SDaniel Fojt break;
1833ff40c12SJohn Marino }
1843ff40c12SJohn Marino
185*a1157835SDaniel Fojt if (!found)
186*a1157835SDaniel Fojt goto fail2;
187*a1157835SDaniel Fojt
188*a1157835SDaniel Fojt free(phy);
1893ff40c12SJohn Marino eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
1903ff40c12SJohn Marino
1913ff40c12SJohn Marino return rfkill;
1923ff40c12SJohn Marino
1933ff40c12SJohn Marino fail2:
1943ff40c12SJohn Marino close(rfkill->fd);
1953ff40c12SJohn Marino fail:
1963ff40c12SJohn Marino os_free(rfkill);
197*a1157835SDaniel Fojt /* use standard free function to match realpath() */
198*a1157835SDaniel Fojt free(phy);
1993ff40c12SJohn Marino return NULL;
2003ff40c12SJohn Marino }
2013ff40c12SJohn Marino
2023ff40c12SJohn Marino
rfkill_deinit(struct rfkill_data * rfkill)2033ff40c12SJohn Marino void rfkill_deinit(struct rfkill_data *rfkill)
2043ff40c12SJohn Marino {
2053ff40c12SJohn Marino if (rfkill == NULL)
2063ff40c12SJohn Marino return;
2073ff40c12SJohn Marino
2083ff40c12SJohn Marino if (rfkill->fd >= 0) {
2093ff40c12SJohn Marino eloop_unregister_read_sock(rfkill->fd);
2103ff40c12SJohn Marino close(rfkill->fd);
2113ff40c12SJohn Marino }
2123ff40c12SJohn Marino
2133ff40c12SJohn Marino os_free(rfkill->cfg);
2143ff40c12SJohn Marino os_free(rfkill);
2153ff40c12SJohn Marino }
2163ff40c12SJohn Marino
2173ff40c12SJohn Marino
rfkill_is_blocked(struct rfkill_data * rfkill)2183ff40c12SJohn Marino int rfkill_is_blocked(struct rfkill_data *rfkill)
2193ff40c12SJohn Marino {
2203ff40c12SJohn Marino if (rfkill == NULL)
2213ff40c12SJohn Marino return 0;
2223ff40c12SJohn Marino
2233ff40c12SJohn Marino return rfkill->blocked;
2243ff40c12SJohn Marino }
225