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 40static int g_drv_errors = 0; 41static int g_power_mode = 0; 42 43int 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 47static 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 56static 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 102static 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); 126nla_put_failure: 127 nlmsg_free(msg); 128 return ret; 129} 130 131static 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 146static 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); 173nla_put_failure: 174 nlmsg_free(msg); 175 return ret; 176} 177 178int 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