driver_cmd_nl80211.c revision 44892ad834705ca618cc6e6b224a1e9b222ed1c4
1/* 2 * Driver interaction with extended Linux CFG8021 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 * 8 * Alternatively, this software may be distributed under the terms of BSD 9 * license. 10 * 11 */ 12 13#include "driver_nl80211.h" 14#include "wpa_supplicant_i.h" 15#include "config.h" 16#ifdef ANDROID 17#include "android_drv.h" 18#endif 19 20#define WPA_PS_ENABLED 0 21#define WPA_PS_DISABLED 1 22 23#define MAX_WPSP2PIE_CMD_SIZE 512 24 25typedef struct android_wifi_priv_cmd { 26 char *buf; 27 int used_len; 28 int total_len; 29} android_wifi_priv_cmd; 30 31int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg, 32 int (*valid_handler)(struct nl_msg *, void *), 33 void *valid_data); 34 35static int drv_errors = 0; 36 37static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv) 38{ 39 drv_errors++; 40 if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) { 41 drv_errors = 0; 42 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); 43 } 44} 45 46static int wpa_driver_set_power_save(void *priv, int state) 47{ 48 struct i802_bss *bss = priv; 49 struct wpa_driver_nl80211_data *drv = bss->drv; 50 struct nl_msg *msg; 51 int ret = -1; 52 enum nl80211_ps_state ps_state; 53 54 msg = nlmsg_alloc(); 55 if (!msg) 56 return -1; 57 58 genlmsg_put(msg, 0, 0, drv->global->nl80211_id, 0, 0, 59 NL80211_CMD_SET_POWER_SAVE, 0); 60 61 if (state == WPA_PS_ENABLED) 62 ps_state = NL80211_PS_ENABLED; 63 else 64 ps_state = NL80211_PS_DISABLED; 65 66 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); 67 NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state); 68 69 ret = send_and_recv_msgs(drv, msg, NULL, NULL); 70 msg = NULL; 71 if (ret < 0) 72 wpa_printf(MSG_ERROR, "nl80211: Set power mode fail: %d", ret); 73nla_put_failure: 74 nlmsg_free(msg); 75 return ret; 76} 77 78static int get_power_mode_handler(struct nl_msg *msg, void *arg) 79{ 80 struct nlattr *tb[NL80211_ATTR_MAX + 1]; 81 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); 82 int *state = (int *)arg; 83 84 nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), 85 genlmsg_attrlen(gnlh, 0), NULL); 86 87 if (!tb[NL80211_ATTR_PS_STATE]) 88 return NL_SKIP; 89 90 if (state) { 91 *state = (int)nla_get_u32(tb[NL80211_ATTR_PS_STATE]); 92 wpa_printf(MSG_DEBUG, "nl80211: Get power mode = %d", *state); 93 *state = (*state == NL80211_PS_ENABLED) ? 94 WPA_PS_ENABLED : WPA_PS_DISABLED; 95 } 96 97 return NL_SKIP; 98} 99 100static int wpa_driver_get_power_save(void *priv, int *state) 101{ 102 struct i802_bss *bss = priv; 103 struct wpa_driver_nl80211_data *drv = bss->drv; 104 struct nl_msg *msg; 105 int ret = -1; 106 enum nl80211_ps_state ps_state; 107 108 msg = nlmsg_alloc(); 109 if (!msg) 110 return -1; 111 112 genlmsg_put(msg, 0, 0, drv->global->nl80211_id, 0, 0, 113 NL80211_CMD_GET_POWER_SAVE, 0); 114 115 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); 116 117 ret = send_and_recv_msgs(drv, msg, get_power_mode_handler, state); 118 msg = NULL; 119 if (ret < 0) 120 wpa_printf(MSG_ERROR, "nl80211: Get power mode fail: %d", ret); 121nla_put_failure: 122 nlmsg_free(msg); 123 return ret; 124} 125 126int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf, 127 size_t buf_len ) 128{ 129 struct i802_bss *bss = priv; 130 struct wpa_driver_nl80211_data *drv = bss->drv; 131 struct ifreq ifr; 132 android_wifi_priv_cmd priv_cmd; 133 int ret = 0; 134 135 if (os_strcasecmp(cmd, "STOP") == 0) { 136 linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0); 137 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); 138 } else if (os_strcasecmp(cmd, "START") == 0) { 139 linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1); 140 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); 141 } else if (os_strcasecmp(cmd, "MACADDR") == 0) { 142 u8 macaddr[ETH_ALEN] = {}; 143 144 ret = linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, macaddr); 145 if (!ret) 146 ret = os_snprintf(buf, buf_len, 147 "Macaddr = " MACSTR "\n", MAC2STR(macaddr)); 148 } else if (os_strcasecmp(cmd, "RELOAD") == 0) { 149 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); 150 } else if (os_strncasecmp(cmd, "POWERMODE ", 10) == 0) { 151 int state; 152 153 state = atoi(cmd + 10); 154 ret = wpa_driver_set_power_save(priv, state); 155 if (ret < 0) 156 wpa_driver_send_hang_msg(drv); 157 else 158 drv_errors = 0; 159 } else if (os_strncasecmp(cmd, "GETPOWER", 8) == 0) { 160 int state = -1; 161 162 ret = wpa_driver_get_power_save(priv, &state); 163 if (!ret && (state != -1)) { 164 ret = os_snprintf(buf, buf_len, "POWERMODE = %d\n", state); 165 drv_errors = 0; 166 } else { 167 wpa_driver_send_hang_msg(drv); 168 } 169 } else { /* Use private command */ 170 memset(&ifr, 0, sizeof(ifr)); 171 memset(&priv_cmd, 0, sizeof(priv_cmd)); 172 os_memcpy(buf, cmd, strlen(cmd) + 1); 173 os_strncpy(ifr.ifr_name, bss->ifname, IFNAMSIZ); 174 175 priv_cmd.buf = buf; 176 priv_cmd.used_len = buf_len; 177 priv_cmd.total_len = buf_len; 178 ifr.ifr_data = &priv_cmd; 179 180 if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr)) < 0) { 181 wpa_printf(MSG_ERROR, "%s: failed to issue private commands\n", __func__); 182 } else { 183 drv_errors = 0; 184 ret = 0; 185 if ((os_strcasecmp(cmd, "LINKSPEED") == 0) || 186 (os_strcasecmp(cmd, "RSSI") == 0) || 187 (os_strcasecmp(cmd, "GETBAND") == 0) ) 188 ret = strlen(buf); 189 else if (os_strcasecmp(cmd, "COUNTRY") == 0) 190 wpa_supplicant_event(drv->ctx, 191 EVENT_CHANNEL_LIST_CHANGED, NULL); 192 else if (os_strncasecmp(cmd, "SETBAND", 7) == 0) 193 wpa_printf(MSG_DEBUG, "%s: %s ", __func__, cmd); 194 else if (os_strcasecmp(cmd, "P2P_DEV_ADDR") == 0) 195 wpa_printf(MSG_DEBUG, "%s: P2P: Device address ("MACSTR")", 196 __func__, MAC2STR(buf)); 197 else if (os_strcasecmp(cmd, "P2P_SET_PS") == 0) 198 wpa_printf(MSG_DEBUG, "%s: P2P: %s ", __func__, buf); 199 else if (os_strcasecmp(cmd, "P2P_SET_NOA") == 0) 200 wpa_printf(MSG_DEBUG, "%s: P2P: %s ", __func__, buf); 201 else 202 wpa_printf(MSG_DEBUG, "%s %s len = %d, %d", __func__, buf, ret, strlen(buf)); 203 } 204 } 205 return ret; 206} 207 208int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration) 209{ 210 char buf[MAX_DRV_CMD_SIZE]; 211 212 memset(buf, 0, sizeof(buf)); 213 wpa_printf(MSG_DEBUG, "%s: Entry", __func__); 214 snprintf(buf, sizeof(buf), "P2P_SET_NOA %d %d %d", count, start, duration); 215 return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf)+1); 216} 217 218int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len) 219{ 220 /* Return 0 till we handle p2p_presence request completely in the driver */ 221 return 0; 222} 223 224int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow) 225{ 226 char buf[MAX_DRV_CMD_SIZE]; 227 228 memset(buf, 0, sizeof(buf)); 229 wpa_printf(MSG_DEBUG, "%s: Entry", __func__); 230 snprintf(buf, sizeof(buf), "P2P_SET_PS %d %d %d", legacy_ps, opp_ps, ctwindow); 231 return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf) + 1); 232} 233 234