blob: cbce8582659eb20e4a12d1c749700fe29fc2055b [file] [log] [blame]
/*
* Driver interaction with extended Linux Wireless Extensions
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Alternatively, this software may be distributed under the terms of BSD
* license.
*
*/
#include "includes.h"
#include <sys/ioctl.h>
#include <net/if_arp.h>
#include <net/if.h>
#include "linux/wireless.h"
#include "common.h"
#include "driver.h"
#include "eloop.h"
#include "priv_netlink.h"
#include "driver_wext.h"
#include "ieee802_11_defs.h"
#include "wpa_common.h"
#include "wpa_ctrl.h"
#include "wpa_supplicant_i.h"
#include "config.h"
#include "linux_ioctl.h"
#include "scan.h"
#include "driver_cmd_wext.h"
#include "driver_cmd_common.h"
/**
* wpa_driver_wext_set_scan_timeout - Set scan timeout to report scan completion
* @priv: Pointer to private wext data from wpa_driver_wext_init()
*
* This function can be used to set registered timeout when starting a scan to
* generate a scan completed event if the driver does not report this.
*/
static void wpa_driver_wext_set_scan_timeout(void *priv)
{
struct wpa_driver_wext_data *drv = priv;
int timeout = 10; /* In case scan A and B bands it can be long */
/* Not all drivers generate "scan completed" wireless event, so try to
* read results after a timeout. */
if (drv->scan_complete_events) {
/*
* The driver seems to deliver SIOCGIWSCAN events to notify
* when scan is complete, so use longer timeout to avoid race
* conditions with scanning and following association request.
*/
timeout = 30;
}
wpa_printf(MSG_DEBUG, "Scan requested - scan timeout %d seconds",
timeout);
eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv,
drv->ctx);
}
/**
* wpa_driver_wext_combo_scan - Request the driver to initiate combo scan
* @priv: Pointer to private wext data from wpa_driver_wext_init()
* @params: Scan parameters
* Returns: 0 on success, -1 on failure
*/
int wpa_driver_wext_combo_scan(void *priv, struct wpa_driver_scan_params *params)
{
char buf[WEXT_CSCAN_BUF_LEN];
struct wpa_driver_wext_data *drv = priv;
struct iwreq iwr;
int ret, bp;
unsigned i;
const u8 *ssid=(params->ssids[0]).ssid;
size_t ssid_len=(params->ssids[0]).ssid_len;
struct wpa_supplicant *wpa_s = (struct wpa_supplicant *)(drv->ctx);
if (!drv->driver_is_started) {
wpa_printf(MSG_DEBUG, "%s: Driver stopped", __func__);
return 0;
}
wpa_printf(MSG_DEBUG, "%s: Start", __func__);
struct iw_scan_req req;
int scan_probe_flag = 0;
if (ssid_len > IW_ESSID_MAX_SIZE) {
wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)",
__FUNCTION__, (unsigned long) ssid_len);
return -1;
}
os_memset(&iwr, 0, sizeof(iwr));
os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
#ifdef ANDROID
if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
scan_probe_flag = wpa_s->prev_scan_ssid->scan_ssid;
}
wpa_printf(MSG_DEBUG, "%s: specific scan = %d", __func__,
(scan_probe_flag && (ssid && ssid_len)) ? 1 : 0);
if (scan_probe_flag && (ssid && ssid_len)) {
#else
if (ssid && ssid_len) {
#endif
os_memset(&req, 0, sizeof(req));
req.essid_len = ssid_len;
req.bssid.sa_family = ARPHRD_ETHER;
os_memset(req.bssid.sa_data, 0xff, ETH_ALEN);
os_memcpy(req.essid, ssid, ssid_len);
iwr.u.data.pointer = (caddr_t) &req;
iwr.u.data.length = sizeof(req);
iwr.u.data.flags = IW_SCAN_THIS_ESSID;
}
ret = ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr);
if (ret < 0) {
wpa_printf(MSG_ERROR, "ioctl[SIOCSIWSCAN] ret = ",ret);
ret = -1;
}
return ret;
}
int wpa_driver_wext_driver_cmd( void *priv, char *cmd, char *buf, size_t buf_len )
{
struct wpa_driver_wext_data *drv = priv;
struct wpa_supplicant *wpa_s = (struct wpa_supplicant *)(drv->ctx);
struct iwreq iwr;
int ret = 0, flags;
wpa_printf(MSG_DEBUG, "%s %s len = %d", __func__, cmd, buf_len);
if (!drv->driver_is_started && (os_strcasecmp(cmd, "START") != 0)) {
wpa_printf(MSG_ERROR,"WEXT: Driver not initialized yet");
return -1;
}
if (os_strcasecmp(cmd, "start") == 0) {
wpa_printf(MSG_DEBUG,"Start command");
return -1;
}
if (os_strcasecmp(cmd, "macaddr") == 0) {
struct ifreq ifr;
os_memset(&ifr, 0, sizeof(ifr));
os_strncpy(ifr.ifr_name, drv->ifname, IFNAMSIZ);
if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr) < 0) {
perror("ioctl[SIOCGIFHWADDR]");
ret = -1;
} else {
u8 *macaddr = (u8 *) ifr.ifr_hwaddr.sa_data;
ret = snprintf(buf, buf_len, "Macaddr = " MACSTR "\n",
MAC2STR(macaddr));
}
return ret;
}
else if (os_strcasecmp(cmd, "scan-passive") == 0) {
wpa_printf(MSG_DEBUG,"Scan Passive command");
return 0;
}
else if (os_strcasecmp(cmd, "SETBAND") == 0) {
wpa_printf(MSG_DEBUG,"Setband command");
ret=0;
return 0;
}
else if (os_strcasecmp(cmd, "scan-active") == 0) {
wpa_printf(MSG_DEBUG,"Scan Active command");
ret=0;
return 0;
}
else if (os_strcasecmp(cmd, "linkspeed") == 0) {
struct iwreq wrq;
unsigned int linkspeed;
os_strncpy(wrq.ifr_name, drv->ifname, IFNAMSIZ);
if (ioctl(drv->ioctl_sock, SIOCGIWRATE, &wrq) < 0) {
perror("ioctl[SIOCGIWRATE]");
ret = -1;
} else {
linkspeed = wrq.u.bitrate.value / 1000000;
ret = snprintf(buf, buf_len, "LinkSpeed %d\n", linkspeed);
wpa_printf(MSG_DEBUG, "[REPLY]: %s", buf);
}
return 0;
}
else if (os_strncasecmp(cmd, "scan-channels", 13) == 0) {
}
else if (os_strncasecmp(cmd, "rssi", 4) == 0) {
/* Matches both rssi and rssi-approx */
struct iwreq wrq;
struct iw_statistics stats;
signed int rssi;
wrq.u.data.pointer = (caddr_t) &stats;
wrq.u.data.length = sizeof(stats);
wrq.u.data.flags = 1; /* Clear updated flag */
strncpy(wrq.ifr_name, drv->ifname, IFNAMSIZ);
if (ioctl(drv->ioctl_sock, SIOCGIWSTATS, &wrq) < 0) {
perror("ioctl[SIOCGIWSTATS]");
ret = -1;
} else {
if (stats.qual.updated & IW_QUAL_DBM) {
/* Values in dBm, stored in u8 with range 63 : -192 */
rssi = ( stats.qual.level > 63 ) ?
stats.qual.level - 0x100 :
stats.qual.level;
} else {
rssi = stats.qual.level;
}
if (wpa_s->conf->ssid->ssid_len != 0 && wpa_s->conf->ssid->ssid_len < buf_len) {
os_memcpy((void *) buf, (void *) (wpa_s->conf->ssid->ssid),
wpa_s->conf->ssid->ssid_len );
ret = wpa_s->conf->ssid->ssid_len;
ret += snprintf(&buf[ret], buf_len-ret,
" rssi %d\n", rssi);
wpa_printf(MSG_DEBUG, "[REPLY]: %s", buf);
if (ret < (int)buf_len) {
return ret;
}
} else {
ret = -1;
}
}
}
else if (os_strncasecmp(cmd, "powermode", 9) == 0) {
}
else if (os_strncasecmp(cmd, "getpower", 8) == 0) {
}
else if (os_strncasecmp(cmd, "get-rts-threshold", 17) == 0) {
struct iwreq wrq;
unsigned int rtsThreshold;
strncpy(wrq.ifr_name, drv->ifname, IFNAMSIZ);
if (ioctl(drv->ioctl_sock, SIOCGIWRTS, &wrq) < 0) {
perror("ioctl[SIOCGIWRTS]");
ret = -1;
} else {
rtsThreshold = wrq.u.rts.value;
wpa_printf(MSG_DEBUG,"Get RTS Threshold command = %d",
rtsThreshold);
ret = snprintf(buf, buf_len, "rts-threshold = %u\n",
rtsThreshold);
if (ret < (int)buf_len) {
return ret;
}
}
}
else if (os_strncasecmp(cmd, "set-rts-threshold", 17) == 0) {
struct iwreq wrq;
unsigned int rtsThreshold;
char *cp = cmd + 17;
char *endp;
strncpy(wrq.ifr_name, drv->ifname, IFNAMSIZ);
if (*cp != '\0') {
rtsThreshold = (unsigned int)strtol(cp, &endp, 0);
if (endp != cp) {
wrq.u.rts.value = rtsThreshold;
wrq.u.rts.fixed = 1;
wrq.u.rts.disabled = 0;
if (ioctl(drv->ioctl_sock, SIOCSIWRTS, &wrq) < 0) {
perror("ioctl[SIOCGIWRTS]");
ret = -1;
} else {
rtsThreshold = wrq.u.rts.value;
wpa_printf(MSG_DEBUG,"Set RTS Threshold command = %d", rtsThreshold);
ret = 0;
}
}
}
}
else if (os_strcasecmp(cmd, "btcoexscan-start") == 0) {
}
else if (os_strcasecmp(cmd, "btcoexscan-stop") == 0) {
}
else if (os_strcasecmp(cmd, "rxfilter-start") == 0) {
wpa_printf(MSG_DEBUG,"Rx Data Filter Start command");
}
else if (os_strcasecmp(cmd, "rxfilter-stop") == 0) {
wpa_printf(MSG_DEBUG,"Rx Data Filter Stop command");
}
else if (os_strcasecmp(cmd, "rxfilter-statistics") == 0) {
}
else if (os_strncasecmp(cmd, "rxfilter-add", 12) == 0 ) {
}
else if (os_strncasecmp(cmd, "rxfilter-remove",15) == 0) {
}
else if (os_strcasecmp(cmd, "snr") == 0) {
struct iwreq wrq;
struct iw_statistics stats;
int snr, rssi, noise;
wrq.u.data.pointer = (caddr_t) &stats;
wrq.u.data.length = sizeof(stats);
wrq.u.data.flags = 1; /* Clear updated flag */
strncpy(wrq.ifr_name, drv->ifname, IFNAMSIZ);
if (ioctl(drv->ioctl_sock, SIOCGIWSTATS, &wrq) < 0) {
perror("ioctl[SIOCGIWSTATS]");
ret = -1;
} else {
if (stats.qual.updated & IW_QUAL_DBM) {
/* Values in dBm, stored in u8 with range 63 : -192 */
rssi = ( stats.qual.level > 63 ) ?
stats.qual.level - 0x100 :
stats.qual.level;
noise = ( stats.qual.noise > 63 ) ?
stats.qual.noise - 0x100 :
stats.qual.noise;
} else {
rssi = stats.qual.level;
noise = stats.qual.noise;
}
snr = rssi - noise;
ret = snprintf(buf, buf_len, "snr = %u\n", (unsigned int)snr);
if (ret < (int)buf_len) {
return ret;
}
}
}
else if (os_strncasecmp(cmd, "btcoexmode", 10) == 0) {
}
else if( os_strcasecmp(cmd, "btcoexstat") == 0 ) {
}
else {
wpa_printf(MSG_DEBUG,"Unsupported command");
}
return ret;
}
int wpa_driver_signal_poll(void *priv, struct wpa_signal_info *si)
{
char buf[MAX_DRV_CMD_SIZE];
struct wpa_driver_wext_data *drv = priv;
char *prssi;
int res;
os_memset(si, 0, sizeof(*si));
res = wpa_driver_wext_driver_cmd(priv, RSSI_CMD, buf, sizeof(buf));
/* Answer: SSID rssi -Val */
if (res < 0)
return res;
prssi = strcasestr(buf, RSSI_CMD);
if (!prssi)
return -1;
si->current_signal = atoi(prssi + strlen(RSSI_CMD) + 1);
res = wpa_driver_wext_driver_cmd(priv, LINKSPEED_CMD, buf, sizeof(buf));
/* Answer: LinkSpeed Val */
if (res < 0)
return res;
si->current_txrate = atoi(buf + strlen(LINKSPEED_CMD) + 1) * 1000;
return 0;
}