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#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 19struct rfkill_event { 20 u32 idx; 21 u8 type; 22 u8 op; 23 u8 soft; 24 u8 hard; 25} STRUCT_PACKED; 26 27enum rfkill_operation { 28 RFKILL_OP_ADD = 0, 29 RFKILL_OP_DEL, 30 RFKILL_OP_CHANGE, 31 RFKILL_OP_CHANGE_ALL, 32}; 33 34enum 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 47struct rfkill_data { 48 struct rfkill_config *cfg; 49 int fd; 50 int blocked; 51 uint32_t idx; 52}; 53 54 55static 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 103struct 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 193fail2: 194 close(rfkill->fd); 195fail: 196 os_free(rfkill); 197 /* use standard free function to match realpath() */ 198 free(phy); 199 return NULL; 200} 201 202 203void 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 218int rfkill_is_blocked(struct rfkill_data *rfkill) 219{ 220 if (rfkill == NULL) 221 return 0; 222 223 return rfkill->blocked; 224} 225