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 
     12 #include "utils/common.h"
     13 #include "utils/eloop.h"
     14 #include "rfkill.h"
     15 
     16 #define RFKILL_EVENT_SIZE_V1 8
     17 
     18 struct rfkill_event {
     19 	u32 idx;
     20 	u8 type;
     21 	u8 op;
     22 	u8 soft;
     23 	u8 hard;
     24 } STRUCT_PACKED;
     25 
     26 enum rfkill_operation {
     27 	RFKILL_OP_ADD = 0,
     28 	RFKILL_OP_DEL,
     29 	RFKILL_OP_CHANGE,
     30 	RFKILL_OP_CHANGE_ALL,
     31 };
     32 
     33 enum 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 
     46 struct rfkill_data {
     47 	struct rfkill_config *cfg;
     48 	int fd;
     49 	int blocked;
     50 };
     51 
     52 
     53 static 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 
    100 struct 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 
    159 fail2:
    160 	close(rfkill->fd);
    161 fail:
    162 	os_free(rfkill);
    163 	return NULL;
    164 }
    165 
    166 
    167 void 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 
    182 int rfkill_is_blocked(struct rfkill_data *rfkill)
    183 {
    184 	if (rfkill == NULL)
    185 		return 0;
    186 
    187 	return rfkill->blocked;
    188 }
    189