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 12#include "utils/common.h" 13#include "utils/eloop.h" 14#include "rfkill.h" 15 16#define RFKILL_EVENT_SIZE_V1 8 17 18struct rfkill_event { 19 u32 idx; 20 u8 type; 21 u8 op; 22 u8 soft; 23 u8 hard; 24} STRUCT_PACKED; 25 26enum rfkill_operation { 27 RFKILL_OP_ADD = 0, 28 RFKILL_OP_DEL, 29 RFKILL_OP_CHANGE, 30 RFKILL_OP_CHANGE_ALL, 31}; 32 33enum rfkill_type { 34 RFKILL_TYPE_ALL = 0, 35 RFKILL_TYPE_WLAN, 36 RFKILL_TYPE_BLUETOOTH, 37 RFKILL_TYPE_UWB, 38 RFKILL_TYPE_WIMAX, 39 RFKILL_TYPE_WWAN, 40 RFKILL_TYPE_GPS, 41 RFKILL_TYPE_FM, 42 NUM_RFKILL_TYPES, 43}; 44 45 46struct rfkill_data { 47 struct rfkill_config *cfg; 48 int fd; 49 int blocked; 50}; 51 52 53static void rfkill_receive(int sock, void *eloop_ctx, void *sock_ctx) 54{ 55 struct rfkill_data *rfkill = eloop_ctx; 56 struct rfkill_event event; 57 ssize_t len; 58 int new_blocked; 59 60 len = read(rfkill->fd, &event, sizeof(event)); 61 if (len < 0) { 62 wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s", 63 strerror(errno)); 64 return; 65 } 66 if (len != RFKILL_EVENT_SIZE_V1) { 67 wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size " 68 "%d (expected %d)", 69 (int) len, RFKILL_EVENT_SIZE_V1); 70 return; 71 } 72 wpa_printf(MSG_DEBUG, "rfkill: event: idx=%u type=%d " 73 "op=%u soft=%u hard=%u", 74 event.idx, event.type, event.op, event.soft, 75 event.hard); 76 if (event.op != RFKILL_OP_CHANGE || event.type != RFKILL_TYPE_WLAN) 77 return; 78 79 if (event.hard) { 80 wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked"); 81 new_blocked = 1; 82 } else if (event.soft) { 83 wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked"); 84 new_blocked = 1; 85 } else { 86 wpa_printf(MSG_INFO, "rfkill: WLAN unblocked"); 87 new_blocked = 0; 88 } 89 90 if (new_blocked != rfkill->blocked) { 91 rfkill->blocked = new_blocked; 92 if (new_blocked) 93 rfkill->cfg->blocked_cb(rfkill->cfg->ctx); 94 else 95 rfkill->cfg->unblocked_cb(rfkill->cfg->ctx); 96 } 97} 98 99 100struct rfkill_data * rfkill_init(struct rfkill_config *cfg) 101{ 102 struct rfkill_data *rfkill; 103 struct rfkill_event event; 104 ssize_t len; 105 106 rfkill = os_zalloc(sizeof(*rfkill)); 107 if (rfkill == NULL) 108 return NULL; 109 110 rfkill->cfg = cfg; 111 rfkill->fd = open("/dev/rfkill", O_RDONLY); 112 if (rfkill->fd < 0) { 113 wpa_printf(MSG_INFO, "rfkill: Cannot open RFKILL control " 114 "device"); 115 goto fail; 116 } 117 118 if (fcntl(rfkill->fd, F_SETFL, O_NONBLOCK) < 0) { 119 wpa_printf(MSG_ERROR, "rfkill: Cannot set non-blocking mode: " 120 "%s", strerror(errno)); 121 goto fail2; 122 } 123 124 for (;;) { 125 len = read(rfkill->fd, &event, sizeof(event)); 126 if (len < 0) { 127 if (errno == EAGAIN) 128 break; /* No more entries */ 129 wpa_printf(MSG_ERROR, "rfkill: Event read failed: %s", 130 strerror(errno)); 131 break; 132 } 133 if (len != RFKILL_EVENT_SIZE_V1) { 134 wpa_printf(MSG_DEBUG, "rfkill: Unexpected event size " 135 "%d (expected %d)", 136 (int) len, RFKILL_EVENT_SIZE_V1); 137 continue; 138 } 139 wpa_printf(MSG_DEBUG, "rfkill: initial event: idx=%u type=%d " 140 "op=%u soft=%u hard=%u", 141 event.idx, event.type, event.op, event.soft, 142 event.hard); 143 if (event.op != RFKILL_OP_ADD || 144 event.type != RFKILL_TYPE_WLAN) 145 continue; 146 if (event.hard) { 147 wpa_printf(MSG_INFO, "rfkill: WLAN hard blocked"); 148 rfkill->blocked = 1; 149 } else if (event.soft) { 150 wpa_printf(MSG_INFO, "rfkill: WLAN soft blocked"); 151 rfkill->blocked = 1; 152 } 153 } 154 155 eloop_register_read_sock(rfkill->fd, rfkill_receive, rfkill, NULL); 156 157 return rfkill; 158 159fail2: 160 close(rfkill->fd); 161fail: 162 os_free(rfkill); 163 return NULL; 164} 165 166 167void rfkill_deinit(struct rfkill_data *rfkill) 168{ 169 if (rfkill == NULL) 170 return; 171 172 if (rfkill->fd >= 0) { 173 eloop_unregister_read_sock(rfkill->fd); 174 close(rfkill->fd); 175 } 176 177 os_free(rfkill->cfg); 178 os_free(rfkill); 179} 180 181 182int rfkill_is_blocked(struct rfkill_data *rfkill) 183{ 184 if (rfkill == NULL) 185 return 0; 186 187 return rfkill->blocked; 188} 189