1 /* 2 * Driver interaction with extended Linux Wireless Extensions 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 "includes.h" 14 #include <sys/ioctl.h> 15 #include <net/if_arp.h> 16 #include <net/if.h> 17 18 #include "wireless_copy.h" 19 #include "common.h" 20 #include "driver.h" 21 #include "eloop.h" 22 #include "priv_netlink.h" 23 #include "driver_wext.h" 24 #include "ieee802_11_defs.h" 25 #include "wpa_common.h" 26 #include "wpa_ctrl.h" 27 #include "wpa_supplicant_i.h" 28 #include "config.h" 29 #include "linux_ioctl.h" 30 #include "scan.h" 31 32 #include "driver_cmd_wext.h" 33 34 /** 35 * wpa_driver_wext_set_scan_timeout - Set scan timeout to report scan completion 36 * @priv: Pointer to private wext data from wpa_driver_wext_init() 37 * 38 * This function can be used to set registered timeout when starting a scan to 39 * generate a scan completed event if the driver does not report this. 40 */ 41 static void wpa_driver_wext_set_scan_timeout(void *priv) 42 { 43 struct wpa_driver_wext_data *drv = priv; 44 int timeout = 10; /* In case scan A and B bands it can be long */ 45 46 /* Not all drivers generate "scan completed" wireless event, so try to 47 * read results after a timeout. */ 48 if (drv->scan_complete_events) { 49 /* 50 * The driver seems to deliver SIOCGIWSCAN events to notify 51 * when scan is complete, so use longer timeout to avoid race 52 * conditions with scanning and following association request. 53 */ 54 timeout = 30; 55 } 56 wpa_printf(MSG_DEBUG, "Scan requested - scan timeout %d seconds", 57 timeout); 58 eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); 59 eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv, 60 drv->ctx); 61 } 62 63 /** 64 * wpa_driver_wext_combo_scan - Request the driver to initiate combo scan 65 * @priv: Pointer to private wext data from wpa_driver_wext_init() 66 * @params: Scan parameters 67 * Returns: 0 on success, -1 on failure 68 */ 69 int wpa_driver_wext_combo_scan(void *priv, struct wpa_driver_scan_params *params) 70 { 71 char buf[WEXT_CSCAN_BUF_LEN]; 72 struct wpa_driver_wext_data *drv = priv; 73 struct iwreq iwr; 74 int ret, bp; 75 unsigned i; 76 struct wpa_supplicant *wpa_s = (struct wpa_supplicant *)(drv->ctx); 77 78 if (!drv->driver_is_started) { 79 wpa_printf(MSG_DEBUG, "%s: Driver stopped", __func__); 80 return 0; 81 } 82 83 wpa_printf(MSG_DEBUG, "%s: Start", __func__); 84 85 /* Set list of SSIDs */ 86 bp = WEXT_CSCAN_HEADER_SIZE; 87 os_memcpy(buf, WEXT_CSCAN_HEADER, bp); 88 for(i=0; i < params->num_ssids; i++) { 89 if ((bp + IW_ESSID_MAX_SIZE + 10) >= (int)sizeof(buf)) 90 break; 91 wpa_printf(MSG_DEBUG, "For Scan: %s", params->ssids[i].ssid); 92 buf[bp++] = WEXT_CSCAN_SSID_SECTION; 93 buf[bp++] = params->ssids[i].ssid_len; 94 os_memcpy(&buf[bp], params->ssids[i].ssid, params->ssids[i].ssid_len); 95 bp += params->ssids[i].ssid_len; 96 } 97 98 /* Set list of channels */ 99 buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION; 100 buf[bp++] = 0; 101 102 /* Set passive dwell time (default is 250) */ 103 buf[bp++] = WEXT_CSCAN_PASV_DWELL_SECTION; 104 buf[bp++] = (u8)WEXT_CSCAN_PASV_DWELL_TIME; 105 buf[bp++] = (u8)(WEXT_CSCAN_PASV_DWELL_TIME >> 8); 106 107 /* Set home dwell time (default is 40) */ 108 buf[bp++] = WEXT_CSCAN_HOME_DWELL_SECTION; 109 buf[bp++] = (u8)WEXT_CSCAN_HOME_DWELL_TIME; 110 buf[bp++] = (u8)(WEXT_CSCAN_HOME_DWELL_TIME >> 8); 111 112 os_memset(&iwr, 0, sizeof(iwr)); 113 os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); 114 iwr.u.data.pointer = buf; 115 iwr.u.data.length = bp; 116 117 if ((ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr)) < 0) { 118 if (!drv->bgscan_enabled) 119 wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (cscan): %d", ret); 120 else 121 ret = 0; /* Hide error in case of bg scan */ 122 } 123 return ret; 124 } 125 126 static int wpa_driver_wext_set_cscan_params(char *buf, size_t buf_len, char *cmd) 127 { 128 char *pasv_ptr; 129 int bp, i; 130 u16 pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_DEF; 131 u8 channel; 132 133 wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd); 134 135 /* Get command parameters */ 136 pasv_ptr = os_strstr(cmd, ",TIME="); 137 if (pasv_ptr) { 138 *pasv_ptr = '\0'; 139 pasv_ptr += 6; 140 pasv_dwell = (u16)atoi(pasv_ptr); 141 if (pasv_dwell == 0) 142 pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_DEF; 143 } 144 channel = (u8)atoi(cmd + 5); 145 146 bp = WEXT_CSCAN_HEADER_SIZE; 147 os_memcpy(buf, WEXT_CSCAN_HEADER, bp); 148 149 /* Set list of channels */ 150 buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION; 151 buf[bp++] = channel; 152 if (channel != 0) { 153 i = (pasv_dwell - 1) / WEXT_CSCAN_PASV_DWELL_TIME_DEF; 154 for (; i > 0; i--) { 155 if ((size_t)(bp + 12) >= buf_len) 156 break; 157 buf[bp++] = WEXT_CSCAN_CHANNEL_SECTION; 158 buf[bp++] = channel; 159 } 160 } else { 161 if (pasv_dwell > WEXT_CSCAN_PASV_DWELL_TIME_MAX) 162 pasv_dwell = WEXT_CSCAN_PASV_DWELL_TIME_MAX; 163 } 164 165 /* Set passive dwell time (default is 250) */ 166 buf[bp++] = WEXT_CSCAN_PASV_DWELL_SECTION; 167 if (channel != 0) { 168 buf[bp++] = (u8)WEXT_CSCAN_PASV_DWELL_TIME_DEF; 169 buf[bp++] = (u8)(WEXT_CSCAN_PASV_DWELL_TIME_DEF >> 8); 170 } else { 171 buf[bp++] = (u8)pasv_dwell; 172 buf[bp++] = (u8)(pasv_dwell >> 8); 173 } 174 175 /* Set home dwell time (default is 40) */ 176 buf[bp++] = WEXT_CSCAN_HOME_DWELL_SECTION; 177 buf[bp++] = (u8)WEXT_CSCAN_HOME_DWELL_TIME; 178 buf[bp++] = (u8)(WEXT_CSCAN_HOME_DWELL_TIME >> 8); 179 180 /* Set cscan type */ 181 buf[bp++] = WEXT_CSCAN_TYPE_SECTION; 182 buf[bp++] = WEXT_CSCAN_TYPE_PASSIVE; 183 return bp; 184 } 185 186 static char *wpa_driver_get_country_code(int channels) 187 { 188 char *country = "US"; /* WEXT_NUMBER_SCAN_CHANNELS_FCC */ 189 190 if (channels == WEXT_NUMBER_SCAN_CHANNELS_ETSI) 191 country = "EU"; 192 else if( channels == WEXT_NUMBER_SCAN_CHANNELS_MKK1) 193 country = "JP"; 194 return country; 195 } 196 197 static int wpa_driver_set_backgroundscan_params(void *priv) 198 { 199 struct wpa_driver_wext_data *drv = priv; 200 struct wpa_supplicant *wpa_s; 201 struct iwreq iwr; 202 int ret = 0, i = 0, bp; 203 char buf[WEXT_PNO_MAX_COMMAND_SIZE]; 204 struct wpa_ssid *ssid_conf; 205 206 if (drv == NULL) { 207 wpa_printf(MSG_ERROR, "%s: drv is NULL. Exiting", __func__); 208 return -1; 209 } 210 if (drv->ctx == NULL) { 211 wpa_printf(MSG_ERROR, "%s: drv->ctx is NULL. Exiting", __func__); 212 return -1; 213 } 214 wpa_s = (struct wpa_supplicant *)(drv->ctx); 215 if (wpa_s->conf == NULL) { 216 wpa_printf(MSG_ERROR, "%s: wpa_s->conf is NULL. Exiting", __func__); 217 return -1; 218 } 219 ssid_conf = wpa_s->conf->ssid; 220 221 bp = WEXT_PNOSETUP_HEADER_SIZE; 222 os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp); 223 buf[bp++] = WEXT_PNO_TLV_PREFIX; 224 buf[bp++] = WEXT_PNO_TLV_VERSION; 225 buf[bp++] = WEXT_PNO_TLV_SUBVERSION; 226 buf[bp++] = WEXT_PNO_TLV_RESERVED; 227 228 while ((i < WEXT_PNO_AMOUNT) && (ssid_conf != NULL)) { 229 /* Check that there is enough space needed for 1 more SSID, the other sections and null termination */ 230 if ((bp + WEXT_PNO_SSID_HEADER_SIZE + IW_ESSID_MAX_SIZE + WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int)sizeof(buf)) 231 break; 232 if ((!ssid_conf->disabled) && (ssid_conf->ssid_len <= IW_ESSID_MAX_SIZE)){ 233 wpa_printf(MSG_DEBUG, "For PNO Scan: %s", ssid_conf->ssid); 234 buf[bp++] = WEXT_PNO_SSID_SECTION; 235 buf[bp++] = ssid_conf->ssid_len; 236 os_memcpy(&buf[bp], ssid_conf->ssid, ssid_conf->ssid_len); 237 bp += ssid_conf->ssid_len; 238 i++; 239 } 240 ssid_conf = ssid_conf->next; 241 } 242 243 buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION; 244 os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x", WEXT_PNO_SCAN_INTERVAL); 245 bp += WEXT_PNO_SCAN_INTERVAL_LENGTH; 246 247 buf[bp++] = WEXT_PNO_REPEAT_SECTION; 248 os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x", WEXT_PNO_REPEAT); 249 bp += WEXT_PNO_REPEAT_LENGTH; 250 251 buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION; 252 os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x", WEXT_PNO_MAX_REPEAT); 253 bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1; 254 255 os_memset(&iwr, 0, sizeof(iwr)); 256 os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); 257 iwr.u.data.pointer = buf; 258 iwr.u.data.length = bp; 259 260 ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); 261 262 if (ret < 0) { 263 wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d", ret); 264 drv->errors++; 265 if (drv->errors > WEXT_NUMBER_SEQUENTIAL_ERRORS) { 266 drv->errors = 0; 267 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); 268 } 269 } else { 270 drv->errors = 0; 271 } 272 return ret; 273 274 } 275 276 int wpa_driver_wext_driver_cmd( void *priv, char *cmd, char *buf, size_t buf_len ) 277 { 278 struct wpa_driver_wext_data *drv = priv; 279 struct wpa_supplicant *wpa_s = (struct wpa_supplicant *)(drv->ctx); 280 struct iwreq iwr; 281 int ret = 0, flags; 282 283 wpa_printf(MSG_DEBUG, "%s %s len = %d", __func__, cmd, buf_len); 284 285 if (!drv->driver_is_started && (os_strcasecmp(cmd, "START") != 0)) { 286 wpa_printf(MSG_ERROR,"WEXT: Driver not initialized yet"); 287 return -1; 288 } 289 290 if (os_strcasecmp(cmd, "RSSI-APPROX") == 0) { 291 os_strncpy(cmd, RSSI_CMD, MAX_DRV_CMD_SIZE); 292 } else if( os_strncasecmp(cmd, "SCAN-CHANNELS", 13) == 0 ) { 293 int no_of_chan; 294 295 no_of_chan = atoi(cmd + 13); 296 os_snprintf(cmd, MAX_DRV_CMD_SIZE, "COUNTRY %s", 297 wpa_driver_get_country_code(no_of_chan)); 298 } else if (os_strcasecmp(cmd, "STOP") == 0) { 299 linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); 300 } else if( os_strcasecmp(cmd, "RELOAD") == 0 ) { 301 wpa_printf(MSG_DEBUG,"Reload command"); 302 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); 303 return ret; 304 } else if( os_strcasecmp(cmd, "BGSCAN-START") == 0 ) { 305 ret = wpa_driver_set_backgroundscan_params(priv); 306 if (ret < 0) { 307 return ret; 308 } 309 os_strncpy(cmd, "PNOFORCE 1", MAX_DRV_CMD_SIZE); 310 drv->bgscan_enabled = 1; 311 } else if( os_strcasecmp(cmd, "BGSCAN-STOP") == 0 ) { 312 os_strncpy(cmd, "PNOFORCE 0", MAX_DRV_CMD_SIZE); 313 drv->bgscan_enabled = 0; 314 } 315 316 os_memset(&iwr, 0, sizeof(iwr)); 317 os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); 318 os_memcpy(buf, cmd, strlen(cmd) + 1); 319 iwr.u.data.pointer = buf; 320 iwr.u.data.length = buf_len; 321 322 if( os_strncasecmp(cmd, "CSCAN", 5) == 0 ) { 323 if (!wpa_s->scanning && ((wpa_s->wpa_state <= WPA_SCANNING) || 324 (wpa_s->wpa_state >= WPA_COMPLETED))) { 325 iwr.u.data.length = wpa_driver_wext_set_cscan_params(buf, buf_len, cmd); 326 } else { 327 wpa_printf(MSG_ERROR, "Ongoing Scan action..."); 328 return ret; 329 } 330 } 331 332 ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr); 333 334 if (ret < 0) { 335 wpa_printf(MSG_ERROR, "%s failed (%d): %s", __func__, ret, cmd); 336 drv->errors++; 337 if (drv->errors > WEXT_NUMBER_SEQUENTIAL_ERRORS) { 338 drv->errors = 0; 339 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED"); 340 } 341 } else { 342 drv->errors = 0; 343 ret = 0; 344 if ((os_strcasecmp(cmd, RSSI_CMD) == 0) || 345 (os_strcasecmp(cmd, LINKSPEED_CMD) == 0) || 346 (os_strcasecmp(cmd, "MACADDR") == 0) || 347 (os_strcasecmp(cmd, "GETPOWER") == 0) || 348 (os_strcasecmp(cmd, "GETBAND") == 0)) { 349 ret = strlen(buf); 350 } else if (os_strcasecmp(cmd, "START") == 0) { 351 drv->driver_is_started = TRUE; 352 linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1); 353 /* os_sleep(0, WPA_DRIVER_WEXT_WAIT_US); 354 wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED"); */ 355 } else if (os_strcasecmp(cmd, "STOP") == 0) { 356 drv->driver_is_started = FALSE; 357 /* wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED"); */ 358 } else if (os_strncasecmp(cmd, "CSCAN", 5) == 0) { 359 wpa_driver_wext_set_scan_timeout(priv); 360 wpa_supplicant_notify_scanning(wpa_s, 1); 361 } 362 wpa_printf(MSG_DEBUG, "%s %s len = %d, %d", __func__, buf, ret, strlen(buf)); 363 } 364 return ret; 365 } 366 367 int wpa_driver_signal_poll(void *priv, struct wpa_signal_info *si) 368 { 369 char buf[MAX_DRV_CMD_SIZE]; 370 struct wpa_driver_wext_data *drv = priv; 371 char *prssi; 372 int res; 373 374 os_memset(si, 0, sizeof(*si)); 375 res = wpa_driver_wext_driver_cmd(priv, RSSI_CMD, buf, sizeof(buf)); 376 /* Answer: SSID rssi -Val */ 377 if (res < 0) 378 return res; 379 prssi = strcasestr(buf, RSSI_CMD); 380 if (!prssi) 381 return -1; 382 si->current_signal = atoi(prssi + strlen(RSSI_CMD) + 1); 383 384 res = wpa_driver_wext_driver_cmd(priv, LINKSPEED_CMD, buf, sizeof(buf)); 385 /* Answer: LinkSpeed Val */ 386 if (res < 0) 387 return res; 388 si->current_txrate = atoi(buf + strlen(LINKSPEED_CMD) + 1) * 1000; 389 390 return 0; 391 } 392