Home | History | Annotate | Download | only in wpa_supplicant_8_lib
      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 
     25 typedef struct android_wifi_priv_cmd {
     26 	char *buf;
     27 	int used_len;
     28 	int total_len;
     29 } android_wifi_priv_cmd;
     30 
     31 int 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 
     35 static int drv_errors = 0;
     36 
     37 static 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 
     46 static 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);
     73 nla_put_failure:
     74 	nlmsg_free(msg);
     75 	return ret;
     76 }
     77 
     78 static 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 
    100 static 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);
    121 nla_put_failure:
    122 	nlmsg_free(msg);
    123 	return ret;
    124 }
    125 
    126 static int wpa_driver_set_backgroundscan_params(void *priv)
    127 {
    128 	struct i802_bss *bss = priv;
    129 	struct wpa_driver_nl80211_data *drv = bss->drv;
    130 	struct wpa_supplicant *wpa_s;
    131 	struct ifreq ifr;
    132 	android_wifi_priv_cmd priv_cmd;
    133 	int ret = 0, i = 0, bp;
    134 	char buf[WEXT_PNO_MAX_COMMAND_SIZE];
    135 	struct wpa_ssid *ssid_conf;
    136 
    137 	if (drv == NULL) {
    138 		wpa_printf(MSG_ERROR, "%s: drv is NULL. Exiting", __func__);
    139 		return -1;
    140 	}
    141 	if (drv->ctx == NULL) {
    142 		wpa_printf(MSG_ERROR, "%s: drv->ctx is NULL. Exiting", __func__);
    143 		return -1;
    144 	}
    145 	wpa_s = (struct wpa_supplicant *)(drv->ctx);
    146 	if (wpa_s->conf == NULL) {
    147 		wpa_printf(MSG_ERROR, "%s: wpa_s->conf is NULL. Exiting", __func__);
    148 		return -1;
    149 	}
    150 	ssid_conf = wpa_s->conf->ssid;
    151 
    152 	bp = WEXT_PNOSETUP_HEADER_SIZE;
    153 	os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp);
    154 	buf[bp++] = WEXT_PNO_TLV_PREFIX;
    155 	buf[bp++] = WEXT_PNO_TLV_VERSION;
    156 	buf[bp++] = WEXT_PNO_TLV_SUBVERSION;
    157 	buf[bp++] = WEXT_PNO_TLV_RESERVED;
    158 
    159 	while ((i < WEXT_PNO_AMOUNT) && (ssid_conf != NULL)) {
    160 		/* Check that there is enough space needed for 1 more SSID, the other sections and null termination */
    161 		if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int)sizeof(buf))
    162 			break;
    163 		if ((!ssid_conf->disabled) && (ssid_conf->ssid_len <= MAX_SSID_LEN)){
    164 			wpa_printf(MSG_DEBUG, "For PNO Scan: %s", ssid_conf->ssid);
    165 			buf[bp++] = WEXT_PNO_SSID_SECTION;
    166 			buf[bp++] = ssid_conf->ssid_len;
    167 			os_memcpy(&buf[bp], ssid_conf->ssid, ssid_conf->ssid_len);
    168 			bp += ssid_conf->ssid_len;
    169 			i++;
    170 		}
    171 		ssid_conf = ssid_conf->next;
    172 	}
    173 
    174 	buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION;
    175 	os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", WEXT_PNO_SCAN_INTERVAL);
    176 	bp += WEXT_PNO_SCAN_INTERVAL_LENGTH;
    177 
    178 	buf[bp++] = WEXT_PNO_REPEAT_SECTION;
    179 	os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", WEXT_PNO_REPEAT);
    180 	bp += WEXT_PNO_REPEAT_LENGTH;
    181 
    182 	buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION;
    183 	os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", WEXT_PNO_MAX_REPEAT);
    184 	bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1;
    185 
    186 	memset(&ifr, 0, sizeof(ifr));
    187 	memset(&priv_cmd, 0, sizeof(priv_cmd));
    188 	os_strncpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
    189 
    190 	priv_cmd.buf = buf;
    191 	priv_cmd.used_len = bp;
    192 	priv_cmd.total_len = bp;
    193 	ifr.ifr_data = &priv_cmd;
    194 
    195 	ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
    196 
    197 	if (ret < 0) {
    198 		wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", ret);
    199 		wpa_driver_send_hang_msg(drv);
    200 	} else {
    201 		drv_errors = 0;
    202 	}
    203 	return ret;
    204 }
    205 
    206 int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
    207 				  size_t buf_len )
    208 {
    209 	struct i802_bss *bss = priv;
    210 	struct wpa_driver_nl80211_data *drv = bss->drv;
    211 	struct ifreq ifr;
    212 	android_wifi_priv_cmd priv_cmd;
    213 	int ret = 0;
    214 
    215 	if (os_strcasecmp(cmd, "STOP") == 0) {
    216 		linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0);
    217 		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED");
    218 	} else if (os_strcasecmp(cmd, "START") == 0) {
    219 		linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
    220 		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED");
    221 	} else if (os_strcasecmp(cmd, "MACADDR") == 0) {
    222 		u8 macaddr[ETH_ALEN] = {};
    223 
    224 		ret = linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname, macaddr);
    225 		if (!ret)
    226 			ret = os_snprintf(buf, buf_len,
    227 					  "Macaddr = " MACSTR "\n", MAC2STR(macaddr));
    228 	} else if (os_strcasecmp(cmd, "RELOAD") == 0) {
    229 		wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
    230 	} else if (os_strncasecmp(cmd, "POWERMODE ", 10) == 0) {
    231 		int state;
    232 
    233 		state = atoi(cmd + 10);
    234 		ret = wpa_driver_set_power_save(priv, state);
    235 		if (ret < 0)
    236 			wpa_driver_send_hang_msg(drv);
    237 		else
    238 			drv_errors = 0;
    239 	} else if (os_strncasecmp(cmd, "GETPOWER", 8) == 0) {
    240 		int state = -1;
    241 
    242 		ret = wpa_driver_get_power_save(priv, &state);
    243 		if (!ret && (state != -1)) {
    244 			ret = os_snprintf(buf, buf_len, "POWERMODE = %d\n", state);
    245 			drv_errors = 0;
    246 		} else {
    247 			wpa_driver_send_hang_msg(drv);
    248 		}
    249 	} else { /* Use private command */
    250 		if (os_strcasecmp(cmd, "BGSCAN-START") == 0) {
    251 			ret = wpa_driver_set_backgroundscan_params(priv);
    252 			if (ret < 0) {
    253 				return ret;
    254 			}
    255 			os_memcpy(buf, "PNOFORCE 1", 11);
    256 		} else if (os_strcasecmp(cmd, "BGSCAN-STOP") == 0) {
    257 			os_memcpy(buf, "PNOFORCE 0", 11);
    258 		} else {
    259 			os_memcpy(buf, cmd, strlen(cmd) + 1);
    260 		}
    261 		memset(&ifr, 0, sizeof(ifr));
    262 		memset(&priv_cmd, 0, sizeof(priv_cmd));
    263 		os_strncpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
    264 
    265 		priv_cmd.buf = buf;
    266 		priv_cmd.used_len = buf_len;
    267 		priv_cmd.total_len = buf_len;
    268 		ifr.ifr_data = &priv_cmd;
    269 
    270 		if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr)) < 0) {
    271 			wpa_printf(MSG_ERROR, "%s: failed to issue private commands\n", __func__);
    272 			wpa_driver_send_hang_msg(drv);
    273 		} else {
    274 			drv_errors = 0;
    275 			ret = 0;
    276 			if ((os_strcasecmp(cmd, "LINKSPEED") == 0) ||
    277 			    (os_strcasecmp(cmd, "RSSI") == 0) ||
    278 			    (os_strcasecmp(cmd, "GETBAND") == 0) )
    279 				ret = strlen(buf);
    280 
    281 			wpa_printf(MSG_DEBUG, "%s %s len = %d, %d", __func__, buf, ret, strlen(buf));
    282 		}
    283 	}
    284 	return ret;
    285 }
    286 
    287 int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration)
    288 {
    289 	char buf[MAX_DRV_CMD_SIZE];
    290 
    291 	memset(buf, 0, sizeof(buf));
    292 	wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
    293 	snprintf(buf, sizeof(buf), "P2P_SET_NOA %d %d %d", count, start, duration);
    294 	return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf)+1);
    295 }
    296 
    297 int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len)
    298 {
    299 	/* Return 0 till we handle p2p_presence request completely in the driver */
    300 	return 0;
    301 }
    302 
    303 int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow)
    304 {
    305 	char buf[MAX_DRV_CMD_SIZE];
    306 
    307 	memset(buf, 0, sizeof(buf));
    308 	wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
    309 	snprintf(buf, sizeof(buf), "P2P_SET_PS %d %d %d", legacy_ps, opp_ps, ctwindow);
    310 	return wpa_driver_nl80211_driver_cmd(priv, buf, buf, strlen(buf) + 1);
    311 }
    312 
    313 int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
    314 				 const struct wpabuf *proberesp,
    315 				 const struct wpabuf *assocresp)
    316 {
    317 	char buf[MAX_WPSP2PIE_CMD_SIZE];
    318 	struct wpabuf *ap_wps_p2p_ie = NULL;
    319 	char *_cmd = "SET_AP_WPS_P2P_IE";
    320 	char *pbuf;
    321 	int ret = 0;
    322 	int i;
    323 	struct cmd_desc {
    324 		int cmd;
    325 		const struct wpabuf *src;
    326 	} cmd_arr[] = {
    327 		{0x1, beacon},
    328 		{0x2, proberesp},
    329 		{0x4, assocresp},
    330 		{-1, NULL}
    331 	};
    332 
    333 	wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
    334 	for (i = 0; cmd_arr[i].cmd != -1; i++) {
    335 		os_memset(buf, 0, sizeof(buf));
    336 		pbuf = buf;
    337 		pbuf += sprintf(pbuf, "%s %d", _cmd, cmd_arr[i].cmd);
    338 		*pbuf++ = '\0';
    339 		ap_wps_p2p_ie = cmd_arr[i].src ?
    340 			wpabuf_dup(cmd_arr[i].src) : NULL;
    341 		if (ap_wps_p2p_ie) {
    342 			os_memcpy(pbuf, wpabuf_head(ap_wps_p2p_ie), wpabuf_len(ap_wps_p2p_ie));
    343 			ret = wpa_driver_nl80211_driver_cmd(priv, buf, buf,
    344 				strlen(_cmd) + 3 + wpabuf_len(ap_wps_p2p_ie));
    345 			wpabuf_free(ap_wps_p2p_ie);
    346 			if (ret < 0)
    347 				break;
    348 		}
    349 	}
    350 
    351 	return ret;
    352 }
    353