forked from Minki/linux
52933d815b
After my bit scan re-writing the SIOCSIWSCAN wext ioctl no longer supported scanning for a specific SSID. However, wpa_supplicant is a possible user of this ioctl, so here is code that add's this. While passing, removed even more of the debugfs-based scanning. You can (and should) the SIOCSIWSCAN to ask for scans, so there is no need for proprietary interfaces for scanning. And, besides, the scan result couldn't be used further, e.g. not for associating. Signed-off-by: Holger Schurig <hs4233@mail.mn-solutions.de> Acked-by: Dan Williams <dcbw@redhat.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
733 lines
19 KiB
C
733 lines
19 KiB
C
/* Copyright (C) 2006, Red Hat, Inc. */
|
|
|
|
#include <linux/bitops.h>
|
|
#include <net/ieee80211.h>
|
|
#include <linux/etherdevice.h>
|
|
|
|
#include "assoc.h"
|
|
#include "join.h"
|
|
#include "decl.h"
|
|
#include "hostcmd.h"
|
|
#include "host.h"
|
|
#include "cmd.h"
|
|
|
|
|
|
static const u8 bssid_any[ETH_ALEN] __attribute__ ((aligned (2))) =
|
|
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
|
static const u8 bssid_off[ETH_ALEN] __attribute__ ((aligned (2))) =
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
|
|
static int assoc_helper_essid(struct lbs_private *priv,
|
|
struct assoc_request * assoc_req)
|
|
{
|
|
int ret = 0;
|
|
struct bss_descriptor * bss;
|
|
int channel = -1;
|
|
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
|
|
/* FIXME: take channel into account when picking SSIDs if a channel
|
|
* is set.
|
|
*/
|
|
|
|
if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
|
|
channel = assoc_req->channel;
|
|
|
|
lbs_deb_assoc("SSID '%s' requested\n",
|
|
escape_essid(assoc_req->ssid, assoc_req->ssid_len));
|
|
if (assoc_req->mode == IW_MODE_INFRA) {
|
|
lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
|
|
assoc_req->ssid_len);
|
|
|
|
bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
|
|
assoc_req->ssid_len, NULL, IW_MODE_INFRA, channel);
|
|
if (bss != NULL) {
|
|
memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
|
|
ret = lbs_associate(priv, assoc_req);
|
|
} else {
|
|
lbs_deb_assoc("SSID not found; cannot associate\n");
|
|
}
|
|
} else if (assoc_req->mode == IW_MODE_ADHOC) {
|
|
/* Scan for the network, do not save previous results. Stale
|
|
* scan data will cause us to join a non-existant adhoc network
|
|
*/
|
|
lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
|
|
assoc_req->ssid_len);
|
|
|
|
/* Search for the requested SSID in the scan table */
|
|
bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
|
|
assoc_req->ssid_len, NULL, IW_MODE_ADHOC, channel);
|
|
if (bss != NULL) {
|
|
lbs_deb_assoc("SSID found, will join\n");
|
|
memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
|
|
lbs_join_adhoc_network(priv, assoc_req);
|
|
} else {
|
|
/* else send START command */
|
|
lbs_deb_assoc("SSID not found, creating adhoc network\n");
|
|
memcpy(&assoc_req->bss.ssid, &assoc_req->ssid,
|
|
IW_ESSID_MAX_SIZE);
|
|
assoc_req->bss.ssid_len = assoc_req->ssid_len;
|
|
lbs_start_adhoc_network(priv, assoc_req);
|
|
}
|
|
}
|
|
|
|
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int assoc_helper_bssid(struct lbs_private *priv,
|
|
struct assoc_request * assoc_req)
|
|
{
|
|
int ret = 0;
|
|
struct bss_descriptor * bss;
|
|
DECLARE_MAC_BUF(mac);
|
|
|
|
lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %s",
|
|
print_mac(mac, assoc_req->bssid));
|
|
|
|
/* Search for index position in list for requested MAC */
|
|
bss = lbs_find_bssid_in_list(priv, assoc_req->bssid,
|
|
assoc_req->mode);
|
|
if (bss == NULL) {
|
|
lbs_deb_assoc("ASSOC: WAP: BSSID %s not found, "
|
|
"cannot associate.\n", print_mac(mac, assoc_req->bssid));
|
|
goto out;
|
|
}
|
|
|
|
memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
|
|
if (assoc_req->mode == IW_MODE_INFRA) {
|
|
ret = lbs_associate(priv, assoc_req);
|
|
lbs_deb_assoc("ASSOC: lbs_associate(bssid) returned %d\n", ret);
|
|
} else if (assoc_req->mode == IW_MODE_ADHOC) {
|
|
lbs_join_adhoc_network(priv, assoc_req);
|
|
}
|
|
|
|
out:
|
|
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int assoc_helper_associate(struct lbs_private *priv,
|
|
struct assoc_request * assoc_req)
|
|
{
|
|
int ret = 0, done = 0;
|
|
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
|
|
/* If we're given and 'any' BSSID, try associating based on SSID */
|
|
|
|
if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
|
|
if (compare_ether_addr(bssid_any, assoc_req->bssid)
|
|
&& compare_ether_addr(bssid_off, assoc_req->bssid)) {
|
|
ret = assoc_helper_bssid(priv, assoc_req);
|
|
done = 1;
|
|
}
|
|
}
|
|
|
|
if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
|
|
ret = assoc_helper_essid(priv, assoc_req);
|
|
}
|
|
|
|
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int assoc_helper_mode(struct lbs_private *priv,
|
|
struct assoc_request * assoc_req)
|
|
{
|
|
int ret = 0;
|
|
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
|
|
if (assoc_req->mode == priv->mode)
|
|
goto done;
|
|
|
|
if (assoc_req->mode == IW_MODE_INFRA) {
|
|
if (priv->psstate != PS_STATE_FULL_POWER)
|
|
lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
|
|
priv->psmode = LBS802_11POWERMODECAM;
|
|
}
|
|
|
|
priv->mode = assoc_req->mode;
|
|
ret = lbs_prepare_and_send_command(priv,
|
|
CMD_802_11_SNMP_MIB,
|
|
0, CMD_OPTION_WAITFORRSP,
|
|
OID_802_11_INFRASTRUCTURE_MODE,
|
|
/* Shoot me now */ (void *) (size_t) assoc_req->mode);
|
|
|
|
done:
|
|
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int lbs_update_channel(struct lbs_private *priv)
|
|
{
|
|
int ret;
|
|
|
|
/* the channel in f/w could be out of sync; get the current channel */
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
|
|
ret = lbs_get_channel(priv);
|
|
if (ret > 0) {
|
|
priv->curbssparams.channel = ret;
|
|
ret = 0;
|
|
}
|
|
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int assoc_helper_channel(struct lbs_private *priv,
|
|
struct assoc_request * assoc_req)
|
|
{
|
|
int ret = 0;
|
|
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
|
|
ret = lbs_update_channel(priv);
|
|
if (ret) {
|
|
lbs_deb_assoc("ASSOC: channel: error getting channel.\n");
|
|
goto done;
|
|
}
|
|
|
|
if (assoc_req->channel == priv->curbssparams.channel)
|
|
goto done;
|
|
|
|
if (priv->mesh_dev) {
|
|
/* Change mesh channel first; 21.p21 firmware won't let
|
|
you change channel otherwise (even though it'll return
|
|
an error to this */
|
|
lbs_mesh_config(priv, 0, assoc_req->channel);
|
|
}
|
|
|
|
lbs_deb_assoc("ASSOC: channel: %d -> %d\n",
|
|
priv->curbssparams.channel, assoc_req->channel);
|
|
|
|
ret = lbs_set_channel(priv, assoc_req->channel);
|
|
if (ret < 0)
|
|
lbs_deb_assoc("ASSOC: channel: error setting channel.\n");
|
|
|
|
/* FIXME: shouldn't need to grab the channel _again_ after setting
|
|
* it since the firmware is supposed to return the new channel, but
|
|
* whatever... */
|
|
ret = lbs_update_channel(priv);
|
|
if (ret) {
|
|
lbs_deb_assoc("ASSOC: channel: error getting channel.\n");
|
|
goto done;
|
|
}
|
|
|
|
if (assoc_req->channel != priv->curbssparams.channel) {
|
|
lbs_deb_assoc("ASSOC: channel: failed to update channel to %d\n",
|
|
assoc_req->channel);
|
|
goto restore_mesh;
|
|
}
|
|
|
|
if ( assoc_req->secinfo.wep_enabled
|
|
&& (assoc_req->wep_keys[0].len
|
|
|| assoc_req->wep_keys[1].len
|
|
|| assoc_req->wep_keys[2].len
|
|
|| assoc_req->wep_keys[3].len)) {
|
|
/* Make sure WEP keys are re-sent to firmware */
|
|
set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
|
|
}
|
|
|
|
/* Must restart/rejoin adhoc networks after channel change */
|
|
set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
|
|
|
|
restore_mesh:
|
|
if (priv->mesh_dev)
|
|
lbs_mesh_config(priv, 1, priv->curbssparams.channel);
|
|
|
|
done:
|
|
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int assoc_helper_wep_keys(struct lbs_private *priv,
|
|
struct assoc_request *assoc_req)
|
|
{
|
|
int i;
|
|
int ret = 0;
|
|
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
|
|
/* Set or remove WEP keys */
|
|
if (assoc_req->wep_keys[0].len || assoc_req->wep_keys[1].len ||
|
|
assoc_req->wep_keys[2].len || assoc_req->wep_keys[3].len)
|
|
ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_ADD, assoc_req);
|
|
else
|
|
ret = lbs_cmd_802_11_set_wep(priv, CMD_ACT_REMOVE, assoc_req);
|
|
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* enable/disable the MAC's WEP packet filter */
|
|
if (assoc_req->secinfo.wep_enabled)
|
|
priv->currentpacketfilter |= CMD_ACT_MAC_WEP_ENABLE;
|
|
else
|
|
priv->currentpacketfilter &= ~CMD_ACT_MAC_WEP_ENABLE;
|
|
|
|
ret = lbs_set_mac_packet_filter(priv);
|
|
if (ret)
|
|
goto out;
|
|
|
|
mutex_lock(&priv->lock);
|
|
|
|
/* Copy WEP keys into priv wep key fields */
|
|
for (i = 0; i < 4; i++) {
|
|
memcpy(&priv->wep_keys[i], &assoc_req->wep_keys[i],
|
|
sizeof(struct enc_key));
|
|
}
|
|
priv->wep_tx_keyidx = assoc_req->wep_tx_keyidx;
|
|
|
|
mutex_unlock(&priv->lock);
|
|
|
|
out:
|
|
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
static int assoc_helper_secinfo(struct lbs_private *priv,
|
|
struct assoc_request * assoc_req)
|
|
{
|
|
int ret = 0;
|
|
uint16_t do_wpa;
|
|
uint16_t rsn = 0;
|
|
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
|
|
memcpy(&priv->secinfo, &assoc_req->secinfo,
|
|
sizeof(struct lbs_802_11_security));
|
|
|
|
ret = lbs_set_mac_packet_filter(priv);
|
|
if (ret)
|
|
goto out;
|
|
|
|
/* If RSN is already enabled, don't try to enable it again, since
|
|
* ENABLE_RSN resets internal state machines and will clobber the
|
|
* 4-way WPA handshake.
|
|
*/
|
|
|
|
/* Get RSN enabled/disabled */
|
|
ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_GET, &rsn);
|
|
if (ret) {
|
|
lbs_deb_assoc("Failed to get RSN status: %d\n", ret);
|
|
goto out;
|
|
}
|
|
|
|
/* Don't re-enable RSN if it's already enabled */
|
|
do_wpa = assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled;
|
|
if (do_wpa == rsn)
|
|
goto out;
|
|
|
|
/* Set RSN enabled/disabled */
|
|
ret = lbs_cmd_802_11_enable_rsn(priv, CMD_ACT_SET, &do_wpa);
|
|
|
|
out:
|
|
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int assoc_helper_wpa_keys(struct lbs_private *priv,
|
|
struct assoc_request * assoc_req)
|
|
{
|
|
int ret = 0;
|
|
unsigned int flags = assoc_req->flags;
|
|
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
|
|
/* Work around older firmware bug where WPA unicast and multicast
|
|
* keys must be set independently. Seen in SDIO parts with firmware
|
|
* version 5.0.11p0.
|
|
*/
|
|
|
|
if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
|
|
clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
|
|
ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
|
|
assoc_req->flags = flags;
|
|
}
|
|
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
|
|
clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
|
|
|
|
ret = lbs_cmd_802_11_key_material(priv, CMD_ACT_SET, assoc_req);
|
|
assoc_req->flags = flags;
|
|
}
|
|
|
|
out:
|
|
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int assoc_helper_wpa_ie(struct lbs_private *priv,
|
|
struct assoc_request * assoc_req)
|
|
{
|
|
int ret = 0;
|
|
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
|
|
if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
|
|
memcpy(&priv->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len);
|
|
priv->wpa_ie_len = assoc_req->wpa_ie_len;
|
|
} else {
|
|
memset(&priv->wpa_ie, 0, MAX_WPA_IE_LEN);
|
|
priv->wpa_ie_len = 0;
|
|
}
|
|
|
|
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int should_deauth_infrastructure(struct lbs_private *priv,
|
|
struct assoc_request * assoc_req)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (priv->connect_status != LBS_CONNECTED)
|
|
return 0;
|
|
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
|
|
lbs_deb_assoc("Deauthenticating due to new SSID\n");
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
|
|
if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
|
|
if (priv->secinfo.auth_mode != assoc_req->secinfo.auth_mode) {
|
|
lbs_deb_assoc("Deauthenticating due to new security\n");
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
|
|
lbs_deb_assoc("Deauthenticating due to new BSSID\n");
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
|
|
if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
|
|
lbs_deb_assoc("Deauthenticating due to channel switch\n");
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
|
|
/* FIXME: deal with 'auto' mode somehow */
|
|
if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
|
|
if (assoc_req->mode != IW_MODE_INFRA) {
|
|
lbs_deb_assoc("Deauthenticating due to leaving "
|
|
"infra mode\n");
|
|
ret = 1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int should_stop_adhoc(struct lbs_private *priv,
|
|
struct assoc_request * assoc_req)
|
|
{
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
|
|
if (priv->connect_status != LBS_CONNECTED)
|
|
return 0;
|
|
|
|
if (lbs_ssid_cmp(priv->curbssparams.ssid,
|
|
priv->curbssparams.ssid_len,
|
|
assoc_req->ssid, assoc_req->ssid_len) != 0)
|
|
return 1;
|
|
|
|
/* FIXME: deal with 'auto' mode somehow */
|
|
if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
|
|
if (assoc_req->mode != IW_MODE_ADHOC)
|
|
return 1;
|
|
}
|
|
|
|
if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
|
|
if (assoc_req->channel != priv->curbssparams.channel)
|
|
return 1;
|
|
}
|
|
|
|
lbs_deb_leave(LBS_DEB_ASSOC);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void lbs_association_worker(struct work_struct *work)
|
|
{
|
|
struct lbs_private *priv = container_of(work, struct lbs_private,
|
|
assoc_work.work);
|
|
struct assoc_request * assoc_req = NULL;
|
|
int ret = 0;
|
|
int find_any_ssid = 0;
|
|
DECLARE_MAC_BUF(mac);
|
|
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
|
|
mutex_lock(&priv->lock);
|
|
assoc_req = priv->pending_assoc_req;
|
|
priv->pending_assoc_req = NULL;
|
|
priv->in_progress_assoc_req = assoc_req;
|
|
mutex_unlock(&priv->lock);
|
|
|
|
if (!assoc_req)
|
|
goto done;
|
|
|
|
lbs_deb_assoc(
|
|
"Association Request:\n"
|
|
" flags: 0x%08lx\n"
|
|
" SSID: '%s'\n"
|
|
" chann: %d\n"
|
|
" band: %d\n"
|
|
" mode: %d\n"
|
|
" BSSID: %s\n"
|
|
" secinfo: %s%s%s\n"
|
|
" auth_mode: %d\n",
|
|
assoc_req->flags,
|
|
escape_essid(assoc_req->ssid, assoc_req->ssid_len),
|
|
assoc_req->channel, assoc_req->band, assoc_req->mode,
|
|
print_mac(mac, assoc_req->bssid),
|
|
assoc_req->secinfo.WPAenabled ? " WPA" : "",
|
|
assoc_req->secinfo.WPA2enabled ? " WPA2" : "",
|
|
assoc_req->secinfo.wep_enabled ? " WEP" : "",
|
|
assoc_req->secinfo.auth_mode);
|
|
|
|
/* If 'any' SSID was specified, find an SSID to associate with */
|
|
if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)
|
|
&& !assoc_req->ssid_len)
|
|
find_any_ssid = 1;
|
|
|
|
/* But don't use 'any' SSID if there's a valid locked BSSID to use */
|
|
if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
|
|
if (compare_ether_addr(assoc_req->bssid, bssid_any)
|
|
&& compare_ether_addr(assoc_req->bssid, bssid_off))
|
|
find_any_ssid = 0;
|
|
}
|
|
|
|
if (find_any_ssid) {
|
|
u8 new_mode;
|
|
|
|
ret = lbs_find_best_network_ssid(priv, assoc_req->ssid,
|
|
&assoc_req->ssid_len, assoc_req->mode, &new_mode);
|
|
if (ret) {
|
|
lbs_deb_assoc("Could not find best network\n");
|
|
ret = -ENETUNREACH;
|
|
goto out;
|
|
}
|
|
|
|
/* Ensure we switch to the mode of the AP */
|
|
if (assoc_req->mode == IW_MODE_AUTO) {
|
|
set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
|
|
assoc_req->mode = new_mode;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if the attributes being changing require deauthentication
|
|
* from the currently associated infrastructure access point.
|
|
*/
|
|
if (priv->mode == IW_MODE_INFRA) {
|
|
if (should_deauth_infrastructure(priv, assoc_req)) {
|
|
ret = lbs_send_deauthentication(priv);
|
|
if (ret) {
|
|
lbs_deb_assoc("Deauthentication due to new "
|
|
"configuration request failed: %d\n",
|
|
ret);
|
|
}
|
|
}
|
|
} else if (priv->mode == IW_MODE_ADHOC) {
|
|
if (should_stop_adhoc(priv, assoc_req)) {
|
|
ret = lbs_stop_adhoc_network(priv);
|
|
if (ret) {
|
|
lbs_deb_assoc("Teardown of AdHoc network due to "
|
|
"new configuration request failed: %d\n",
|
|
ret);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/* Send the various configuration bits to the firmware */
|
|
if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
|
|
ret = assoc_helper_mode(priv, assoc_req);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
|
|
ret = assoc_helper_channel(priv, assoc_req);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
if ( test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)
|
|
|| test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) {
|
|
ret = assoc_helper_wep_keys(priv, assoc_req);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
|
|
ret = assoc_helper_secinfo(priv, assoc_req);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
|
|
ret = assoc_helper_wpa_ie(priv, assoc_req);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)
|
|
|| test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
|
|
ret = assoc_helper_wpa_keys(priv, assoc_req);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
/* SSID/BSSID should be the _last_ config option set, because they
|
|
* trigger the association attempt.
|
|
*/
|
|
if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)
|
|
|| test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
|
|
int success = 1;
|
|
|
|
ret = assoc_helper_associate(priv, assoc_req);
|
|
if (ret) {
|
|
lbs_deb_assoc("ASSOC: association unsuccessful: %d\n",
|
|
ret);
|
|
success = 0;
|
|
}
|
|
|
|
if (priv->connect_status != LBS_CONNECTED) {
|
|
lbs_deb_assoc("ASSOC: association unsuccessful, "
|
|
"not connected\n");
|
|
success = 0;
|
|
}
|
|
|
|
if (success) {
|
|
lbs_deb_assoc("associated to %s\n",
|
|
print_mac(mac, priv->curbssparams.bssid));
|
|
lbs_prepare_and_send_command(priv,
|
|
CMD_802_11_RSSI,
|
|
0, CMD_OPTION_WAITFORRSP, 0, NULL);
|
|
|
|
lbs_prepare_and_send_command(priv,
|
|
CMD_802_11_GET_LOG,
|
|
0, CMD_OPTION_WAITFORRSP, 0, NULL);
|
|
} else {
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
out:
|
|
if (ret) {
|
|
lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n",
|
|
ret);
|
|
}
|
|
|
|
mutex_lock(&priv->lock);
|
|
priv->in_progress_assoc_req = NULL;
|
|
mutex_unlock(&priv->lock);
|
|
kfree(assoc_req);
|
|
|
|
done:
|
|
lbs_deb_leave(LBS_DEB_ASSOC);
|
|
}
|
|
|
|
|
|
/*
|
|
* Caller MUST hold any necessary locks
|
|
*/
|
|
struct assoc_request *lbs_get_association_request(struct lbs_private *priv)
|
|
{
|
|
struct assoc_request * assoc_req;
|
|
|
|
lbs_deb_enter(LBS_DEB_ASSOC);
|
|
if (!priv->pending_assoc_req) {
|
|
priv->pending_assoc_req = kzalloc(sizeof(struct assoc_request),
|
|
GFP_KERNEL);
|
|
if (!priv->pending_assoc_req) {
|
|
lbs_pr_info("Not enough memory to allocate association"
|
|
" request!\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Copy current configuration attributes to the association request,
|
|
* but don't overwrite any that are already set.
|
|
*/
|
|
assoc_req = priv->pending_assoc_req;
|
|
if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
|
|
memcpy(&assoc_req->ssid, &priv->curbssparams.ssid,
|
|
IW_ESSID_MAX_SIZE);
|
|
assoc_req->ssid_len = priv->curbssparams.ssid_len;
|
|
}
|
|
|
|
if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
|
|
assoc_req->channel = priv->curbssparams.channel;
|
|
|
|
if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags))
|
|
assoc_req->band = priv->curbssparams.band;
|
|
|
|
if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags))
|
|
assoc_req->mode = priv->mode;
|
|
|
|
if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
|
|
memcpy(&assoc_req->bssid, priv->curbssparams.bssid,
|
|
ETH_ALEN);
|
|
}
|
|
|
|
if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) {
|
|
int i;
|
|
for (i = 0; i < 4; i++) {
|
|
memcpy(&assoc_req->wep_keys[i], &priv->wep_keys[i],
|
|
sizeof(struct enc_key));
|
|
}
|
|
}
|
|
|
|
if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags))
|
|
assoc_req->wep_tx_keyidx = priv->wep_tx_keyidx;
|
|
|
|
if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
|
|
memcpy(&assoc_req->wpa_mcast_key, &priv->wpa_mcast_key,
|
|
sizeof(struct enc_key));
|
|
}
|
|
|
|
if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
|
|
memcpy(&assoc_req->wpa_unicast_key, &priv->wpa_unicast_key,
|
|
sizeof(struct enc_key));
|
|
}
|
|
|
|
if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
|
|
memcpy(&assoc_req->secinfo, &priv->secinfo,
|
|
sizeof(struct lbs_802_11_security));
|
|
}
|
|
|
|
if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
|
|
memcpy(&assoc_req->wpa_ie, &priv->wpa_ie,
|
|
MAX_WPA_IE_LEN);
|
|
assoc_req->wpa_ie_len = priv->wpa_ie_len;
|
|
}
|
|
|
|
lbs_deb_leave(LBS_DEB_ASSOC);
|
|
return assoc_req;
|
|
}
|