Home | History | Annotate | Download | only in wpa_supplicant_lib
      1 #include "includes.h"
      2 #include <sys/ioctl.h>
      3 #include <net/if_arp.h>
      4 #include <net/if.h>
      5 #include <sys/stat.h>
      6 #include <fcntl.h>
      7 #include <sys/types.h>
      8 #include <sys/wait.h>
      9 #include <netlink/genl/genl.h>
     10 #include <netlink/genl/family.h>
     11 #include <netlink/genl/ctrl.h>
     12 #include <netlink/msg.h>
     13 #include <netlink/attr.h>
     14 
     15 #include "linux_wext.h"
     16 #include "common.h"
     17 #include "driver.h"
     18 #include "eloop.h"
     19 #include "driver_wext.h"
     20 #include "ieee802_11_defs.h"
     21 #include "wpa_common.h"
     22 #include "wpa_ctrl.h"
     23 #include "wpa_supplicant_i.h"
     24 #include "config_ssid.h"
     25 #include "wpa_debug.h"
     26 #include "linux_ioctl.h"
     27 #include "driver_nl80211.h"
     28 
     29 #define WPA_EVENT_DRIVER_STATE          "CTRL-EVENT-DRIVER-STATE "
     30 #define DRV_NUMBER_SEQUENTIAL_ERRORS     4
     31 
     32 #define WPA_PS_ENABLED   0
     33 #define WPA_PS_DISABLED  1
     34 
     35 #define BLUETOOTH_COEXISTENCE_MODE_ENABLED   0
     36 #define BLUETOOTH_COEXISTENCE_MODE_DISABLED  1
     37 #define BLUETOOTH_COEXISTENCE_MODE_SENSE     2
     38 
     39 
     40 static int g_drv_errors = 0;
     41 static int g_power_mode = 0;
     42 
     43 int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg,
     44                        int (*valid_handler)(struct nl_msg *, void *),
     45                        void *valid_data);
     46 
     47 static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
     48 {
     49 	g_drv_errors++;
     50 	if (g_drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
     51 		g_drv_errors = 0;
     52 		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
     53 	}
     54 }
     55 
     56 static int get_link_signal(struct nl_msg *msg, void *arg)
     57 {
     58 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
     59 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
     60 	struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
     61 	static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
     62 		[NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
     63 	};
     64 	struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
     65 	static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
     66 		[NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
     67 		[NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
     68 		[NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
     69 		[NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
     70 	};
     71 	struct wpa_signal_info *sig_change = arg;
     72 
     73 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
     74 		  genlmsg_attrlen(gnlh, 0), NULL);
     75 	if (!tb[NL80211_ATTR_STA_INFO] ||
     76 	    nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
     77 			     tb[NL80211_ATTR_STA_INFO], policy))
     78 		return NL_SKIP;
     79 	if (!sinfo[NL80211_STA_INFO_SIGNAL])
     80 		return NL_SKIP;
     81 
     82 	sig_change->current_signal =
     83 		(s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
     84 
     85 	if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
     86 		if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
     87 				     sinfo[NL80211_STA_INFO_TX_BITRATE],
     88 				     rate_policy)) {
     89 			sig_change->current_txrate = 0;
     90 		} else {
     91 			if (rinfo[NL80211_RATE_INFO_BITRATE]) {
     92 				sig_change->current_txrate =
     93 					nla_get_u16(rinfo[
     94 					     NL80211_RATE_INFO_BITRATE]) * 100;
     95 			}
     96 		}
     97 	}
     98 
     99 	return NL_SKIP;
    100 }
    101 
    102 static int wpa_driver_get_link_signal(void *priv, struct wpa_signal_info *sig)
    103 {
    104 	struct i802_bss *bss = priv;
    105 	struct wpa_driver_nl80211_data *drv = bss->drv;
    106 	struct nl_msg *msg;
    107 	int ret = -1;
    108 
    109 	sig->current_signal = -9999;
    110 	sig->current_txrate = 0;
    111 
    112 	msg = nlmsg_alloc();
    113 	if (!msg)
    114 		return -1;
    115 
    116 	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
    117 		    0, NL80211_CMD_GET_STATION, 0);
    118 
    119 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
    120 	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
    121 
    122 	ret = send_and_recv_msgs(drv, msg, get_link_signal, sig);
    123 	msg = NULL;
    124 	if (ret < 0)
    125 		wpa_printf(MSG_ERROR, "nl80211: get link signal fail: %d", ret);
    126 nla_put_failure:
    127 	nlmsg_free(msg);
    128 	return ret;
    129 }
    130 
    131 static int wpa_driver_toggle_btcoex_state(char state)
    132 {
    133 	int ret;
    134 	int fd = open("/sys/devices/platform/wl1271/bt_coex_state", O_RDWR, 0);
    135 	if (fd == -1)
    136 		return -1;
    137 
    138 	ret = write(fd, &state, sizeof(state));
    139 	close(fd);
    140 
    141 	wpa_printf(MSG_DEBUG, "%s:  set btcoex state to '%c' result = %d", __func__,
    142 		   state, ret);
    143 	return ret;
    144 }
    145 
    146 static int wpa_driver_set_power_save(void *priv, int state)
    147 {
    148 	struct i802_bss *bss = priv;
    149 	struct wpa_driver_nl80211_data *drv = bss->drv;
    150 	struct nl_msg *msg;
    151 	int ret = -1;
    152 	enum nl80211_ps_state ps_state;
    153 
    154 	msg = nlmsg_alloc();
    155 	if (!msg)
    156 		return -1;
    157 
    158 	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
    159 		    NL80211_CMD_SET_POWER_SAVE, 0);
    160 
    161 	if (state == WPA_PS_ENABLED)
    162 		ps_state = NL80211_PS_ENABLED;
    163 	else
    164 		ps_state = NL80211_PS_DISABLED;
    165 
    166 	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
    167 	NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
    168 
    169 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
    170 	msg = NULL;
    171 	if (ret < 0)
    172 		wpa_printf(MSG_ERROR, "nl80211: Set power mode fail: %d", ret);
    173 nla_put_failure:
    174 	nlmsg_free(msg);
    175 	return ret;
    176 }
    177 
    178 int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
    179 				  size_t buf_len )
    180 {
    181 	struct i802_bss *bss = priv;
    182 	struct wpa_driver_nl80211_data *drv = bss->drv;
    183 	struct ifreq ifr;
    184 	int ret = 0;
    185 
    186 	if (os_strcasecmp(cmd, "STOP") == 0) {
    187 		linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0);
    188 		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED");
    189 	} else if (os_strcasecmp(cmd, "START") == 0) {
    190 		linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1);
    191 		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED");
    192 	} else if (os_strcasecmp(cmd, "RELOAD") == 0) {
    193 		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
    194 	} else if (os_strncasecmp(cmd, "POWERMODE ", 10) == 0) {
    195 		int mode;
    196 		mode = atoi(cmd + 10);
    197 		ret = wpa_driver_set_power_save(priv, mode);
    198 		if (ret < 0) {
    199 			wpa_driver_send_hang_msg(drv);
    200 		} else {
    201 			g_power_mode = mode;
    202 			g_drv_errors = 0;
    203 		}
    204 	} else if (os_strncasecmp(cmd, "GETPOWER", 8) == 0) {
    205 		ret = os_snprintf(buf, buf_len, "POWERMODE = %d\n", g_power_mode);
    206 	} else if (os_strncasecmp(cmd, "BTCOEXMODE ", 11) == 0) {
    207 		int mode = atoi(cmd + 11);
    208 		if (mode == BLUETOOTH_COEXISTENCE_MODE_DISABLED) { /* disable BT-coex */
    209 			ret = wpa_driver_toggle_btcoex_state('0');
    210 		} else if (mode == BLUETOOTH_COEXISTENCE_MODE_SENSE) { /* enable BT-coex */
    211 			ret = wpa_driver_toggle_btcoex_state('1');
    212 		} else {
    213 			wpa_printf(MSG_DEBUG, "invalid btcoex mode: %d", mode);
    214 			ret = -1;
    215 		}
    216 	} else if ((os_strcasecmp(cmd, "RSSI") == 0) || (os_strcasecmp(cmd, "RSSI-APPROX") == 0)) {
    217 		struct wpa_signal_info sig;
    218 		int rssi;
    219 
    220 		if (!drv->associated)
    221 			return -1;
    222 
    223 		ret = wpa_driver_get_link_signal(priv, &sig);
    224 		if (ret < 0) {
    225 			wpa_driver_send_hang_msg(drv);
    226 		} else {
    227 			rssi = sig.current_signal;
    228 			wpa_printf(MSG_DEBUG, "%s rssi %d\n", drv->ssid, rssi);
    229 			ret = os_snprintf(buf, buf_len, "%s rssi %d\n", drv->ssid, rssi);
    230 		}
    231 	} else if (os_strcasecmp(cmd, "LINKSPEED") == 0) {
    232 		struct wpa_signal_info sig;
    233 		int linkspeed;
    234 
    235 		if (!drv->associated)
    236 			return -1;
    237 
    238 		ret = wpa_driver_get_link_signal(priv, &sig);
    239 		if (ret < 0) {
    240 			wpa_driver_send_hang_msg(drv);
    241 		} else {
    242 			linkspeed = sig.current_txrate / 1000;
    243 			wpa_printf(MSG_DEBUG, "LinkSpeed %d\n", linkspeed);
    244 			ret = os_snprintf(buf, buf_len, "LinkSpeed %d\n", linkspeed);
    245 		}
    246 	} else if (os_strcasecmp(cmd, "MACADDR") == 0) {
    247 		u8 macaddr[ETH_ALEN] = {};
    248 
    249 		ret = linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, macaddr);
    250 		if (!ret)
    251 			ret = os_snprintf(buf, buf_len,
    252 					  "Macaddr = " MACSTR "\n", MAC2STR(macaddr));
    253 	} else {
    254 		wpa_printf(MSG_INFO, "%s: Unsupported command %s", __func__, cmd);
    255 	}
    256 	return ret;
    257 }
    258