Home | History | Annotate | Download | only in drivers
      1 /*
      2  * Linux rfkill helper functions for driver wrappers
      3  * Copyright (c) 2010, Jouni Malinen <j (at) 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 #include <limits.h>
     12 
     13 #include "utils/common.h"
     14 #include "utils/eloop.h"
     15 #include "rfkill.h"
     16 
     17 #define RFKILL_EVENT_SIZE_V1 8
     18 
     19 struct rfkill_event {
     20 	u32 idx;
     21 	u8 type;
     22 	u8 op;
     23 	u8 soft;
     24 	u8 hard;
     25 } STRUCT_PACKED;
     26 
     27 enum rfkill_operation {
     28 	RFKILL_OP_ADD = 0,
     29 	RFKILL_OP_DEL,
     30 	RFKILL_OP_CHANGE,
     31 	RFKILL_OP_CHANGE_ALL,
     32 };
     33 
     34 enum rfkill_type {
     35 	RFKILL_TYPE_ALL = 0,
     36 	RFKILL_TYPE_WLAN,
     37 	RFKILL_TYPE_BLUETOOTH,
     38 	RFKILL_TYPE_UWB,
     39 	RFKILL_TYPE_WIMAX,
     40 	RFKILL_TYPE_WWAN,
     41 	RFKILL_TYPE_GPS,
     42 	RFKILL_TYPE_FM,
     43 	NUM_RFKILL_TYPES,
     44 };
     45 
     46 
     47 struct rfkill_data {
     48 	struct rfkill_config *cfg;
     49 	int fd;
     50 	int blocked;
     51 	uint32_t idx;
     52 };
     53 
     54 
     55 static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx)
     56 {
     57 	struct rfkill_data *rfkill = eloop_ctx;
     58 	struct rfkill_event event;
     59 	ssize_t len;
     60 	int new_blocked;
     61 
     62 	len = read(rfkill->fd, &event, sizeof(event));
     63 	if (len < 0) {
     64 		wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
     65 			   strerror(errno));
     66 		return;
     67 	}
     68 	if (len != RFKILL_EVENT_SIZE_V1) {
     69 		wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
     70 			   "%d (expected %d)",
     71 			   (int) len, RFKILL_EVENT_SIZE_V1);
     72 		return;
     73 	}
     74 	if (event.op != RFKILL_OP_CHANGE || event.idx != rfkill->idx)
     75 		return;
     76 
     77 	wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d "
     78 		   "op=%u soft=%u hard=%u",
     79 		   event.idx, event.type, event.op, event.soft,
     80 		   event.hard);
     81 
     82 	if (event.hard) {
     83 		wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
     84 		new_blocked = 1;
     85 	} else if (event.soft) {
     86 		wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
     87 		new_blocked = 1;
     88 	} else {
     89 		wpa_printf(MSG_INFO, "rfkill: WLAN unblocked");
     90 		new_blocked = 0;
     91 	}
     92 
     93 	if (new_blocked != rfkill->blocked) {
     94 		rfkill->blocked = new_blocked;
     95 		if (new_blocked)
     96 			rfkill->cfg->blocked_cb(rfkill->cfg->ctx);
     97 		else
     98 			rfkill->cfg->unblocked_cb(rfkill->cfg->ctx);
     99 	}
    100 }
    101 
    102 
    103 struct rfkill_data * rfkill_init(struct rfkill_config *cfg)
    104 {
    105 	struct rfkill_data *rfkill;
    106 	struct rfkill_event event;
    107 	ssize_t len;
    108 	char *phy = NULL, *rfk_phy;
    109 	char buf[24 + IFNAMSIZ + 1];
    110 	char buf2[31 + 11 + 1];
    111 	int found = 0;
    112 
    113 	rfkill = os_zalloc(sizeof(*rfkill));
    114 	if (rfkill == NULL)
    115 		return NULL;
    116 
    117 	os_snprintf(buf, sizeof(buf), "/sys/class/net/%s/phy80211",
    118 		    cfg->ifname);
    119 	phy = realpath(buf, NULL);
    120 	if (!phy) {
    121 		wpa_printf(MSG_INFO, "rfkill: Cannot get wiphy information");
    122 		goto fail;
    123 	}
    124 
    125 	rfkill->cfg = cfg;
    126 	rfkill->fd = open("/dev/rfkill", O_RDONLY);
    127 	if (rfkill->fd < 0) {
    128 		wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control "
    129 			   "device");
    130 		goto fail;
    131 	}
    132 
    133 	if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) {
    134 		wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: "
    135 			   "%s", strerror(errno));
    136 		goto fail2;
    137 	}
    138 
    139 	for (;;) {
    140 		len = read(rfkill->fd, &event, sizeof(event));
    141 		if (len < 0) {
    142 			if (errno == EAGAIN)
    143 				break; /* No more entries */
    144 			wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s",
    145 				   strerror(errno));
    146 			break;
    147 		}
    148 		if (len != RFKILL_EVENT_SIZE_V1) {
    149 			wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size "
    150 				   "%d (expected %d)",
    151 				   (int) len, RFKILL_EVENT_SIZE_V1);
    152 			continue;
    153 		}
    154 		if (event.op != RFKILL_OP_ADD ||
    155 		    event.type != RFKILL_TYPE_WLAN)
    156 			continue;
    157 
    158 		os_snprintf(buf2, sizeof(buf2),
    159 			    "/sys/class/rfkill/rfkill%d/device", event.idx);
    160 		rfk_phy = realpath(buf2, NULL);
    161 		if (!rfk_phy)
    162 			goto fail2;
    163 		found = os_strcmp(phy, rfk_phy) == 0;
    164 		free(rfk_phy);
    165 
    166 		if (!found)
    167 			continue;
    168 
    169 		wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d "
    170 			   "op=%u soft=%u hard=%u",
    171 			   event.idx, event.type, event.op, event.soft,
    172 			   event.hard);
    173 
    174 		rfkill->idx = event.idx;
    175 		if (event.hard) {
    176 			wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked");
    177 			rfkill->blocked = 1;
    178 		} else if (event.soft) {
    179 			wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked");
    180 			rfkill->blocked = 1;
    181 		}
    182 		break;
    183 	}
    184 
    185 	if (!found)
    186 		goto fail2;
    187 
    188 	free(phy);
    189 	eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL);
    190 
    191 	return rfkill;
    192 
    193 fail2:
    194 	close(rfkill->fd);
    195 fail:
    196 	os_free(rfkill);
    197 	/* use standard free function to match realpath() */
    198 	free(phy);
    199 	return NULL;
    200 }
    201 
    202 
    203 void rfkill_deinit(struct rfkill_data *rfkill)
    204 {
    205 	if (rfkill == NULL)
    206 		return;
    207 
    208 	if (rfkill->fd >= 0) {
    209 		eloop_unregister_read_sock(rfkill->fd);
    210 		close(rfkill->fd);
    211 	}
    212 
    213 	os_free(rfkill->cfg);
    214 	os_free(rfkill);
    215 }
    216 
    217 
    218 int rfkill_is_blocked(struct rfkill_data *rfkill)
    219 {
    220 	if (rfkill == NULL)
    221 		return 0;
    222 
    223 	return rfkill->blocked;
    224 }
    225