/* Copyright (C) 2006, Red Hat, Inc. */
-#include <linux/bitops.h>
-#include <net/ieee80211.h>
+#include <linux/types.h>
#include <linux/etherdevice.h>
+#include <linux/ieee80211.h>
+#include <linux/if_arp.h>
+#include <net/lib80211.h>
#include "assoc.h"
-#include "join.h"
#include "decl.h"
-#include "hostcmd.h"
#include "host.h"
+#include "scan.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 const u8 bssid_any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-static const u8 bssid_off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+/* The firmware needs the following bits masked out of the beacon-derived
+ * capability field when associating/joining to a BSS:
+ * 9 (QoS), 11 (APSD), 12 (unused), 14 (unused), 15 (unused)
+ */
+#define CAPINFO_MASK (~(0xda00))
+
+/**
+ * 802.11b/g supported bitrates (in 500Kb/s units)
+ */
+u8 lbs_bg_rates[MAX_RATES] =
+ { 0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
+0x00, 0x00 };
+
+
+/**
+ * @brief This function finds common rates between rates and card rates.
+ *
+ * It will fill common rates in rates as output if found.
+ *
+ * NOTE: Setting the MSB of the basic rates need to be taken
+ * care, either before or after calling this function
+ *
+ * @param priv A pointer to struct lbs_private structure
+ * @param rates the buffer which keeps input and output
+ * @param rates_size the size of rates buffer; new size of buffer on return,
+ * which will be less than or equal to original rates_size
+ *
+ * @return 0 on success, or -1 on error
+ */
+static int get_common_rates(struct lbs_private *priv,
+ u8 *rates,
+ u16 *rates_size)
+{
+ int i, j;
+ u8 intersection[MAX_RATES];
+ u16 intersection_size;
+ u16 num_rates = 0;
+
+ intersection_size = min_t(u16, *rates_size, ARRAY_SIZE(intersection));
+
+ /* Allow each rate from 'rates' that is supported by the hardware */
+ for (i = 0; i < ARRAY_SIZE(lbs_bg_rates) && lbs_bg_rates[i]; i++) {
+ for (j = 0; j < intersection_size && rates[j]; j++) {
+ if (rates[j] == lbs_bg_rates[i])
+ intersection[num_rates++] = rates[j];
+ }
+ }
+
+ lbs_deb_hex(LBS_DEB_JOIN, "AP rates ", rates, *rates_size);
+ lbs_deb_hex(LBS_DEB_JOIN, "card rates ", lbs_bg_rates,
+ ARRAY_SIZE(lbs_bg_rates));
+ lbs_deb_hex(LBS_DEB_JOIN, "common rates", intersection, num_rates);
+ lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate);
+
+ if (!priv->enablehwauto) {
+ for (i = 0; i < num_rates; i++) {
+ if (intersection[i] == priv->cur_rate)
+ goto done;
+ }
+ lbs_pr_alert("Previously set fixed data rate %#x isn't "
+ "compatible with the network.\n", priv->cur_rate);
+ return -1;
+ }
+
+done:
+ memset(rates, 0, *rates_size);
+ *rates_size = num_rates;
+ memcpy(rates, intersection, num_rates);
+ return 0;
+}
+
+
+/**
+ * @brief Sets the MSB on basic rates as the firmware requires
+ *
+ * Scan through an array and set the MSB for basic data rates.
+ *
+ * @param rates buffer of data rates
+ * @param len size of buffer
+ */
+static void lbs_set_basic_rate_flags(u8 *rates, size_t len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (rates[i] == 0x02 || rates[i] == 0x04 ||
+ rates[i] == 0x0b || rates[i] == 0x16)
+ rates[i] |= 0x80;
+ }
+}
+
+
+static u8 iw_auth_to_ieee_auth(u8 auth)
+{
+ if (auth == IW_AUTH_ALG_OPEN_SYSTEM)
+ return 0x00;
+ else if (auth == IW_AUTH_ALG_SHARED_KEY)
+ return 0x01;
+ else if (auth == IW_AUTH_ALG_LEAP)
+ return 0x80;
+
+ lbs_deb_join("%s: invalid auth alg 0x%X\n", __func__, auth);
+ return 0;
+}
+
+/**
+ * @brief This function prepares the authenticate command. AUTHENTICATE only
+ * sets the authentication suite for future associations, as the firmware
+ * handles authentication internally during the ASSOCIATE command.
+ *
+ * @param priv A pointer to struct lbs_private structure
+ * @param bssid The peer BSSID with which to authenticate
+ * @param auth The authentication mode to use (from wireless.h)
+ *
+ * @return 0 or -1
+ */
+static int lbs_set_authentication(struct lbs_private *priv, u8 bssid[6], u8 auth)
+{
+ struct cmd_ds_802_11_authenticate cmd;
+ int ret = -1;
+
+ lbs_deb_enter(LBS_DEB_JOIN);
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ memcpy(cmd.bssid, bssid, ETH_ALEN);
+
+ cmd.authtype = iw_auth_to_ieee_auth(auth);
+
+ lbs_deb_join("AUTH_CMD: BSSID %pM, auth 0x%x\n", bssid, cmd.authtype);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_AUTHENTICATE, &cmd);
+
+ lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
+ return ret;
+}
+
+
+int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
+ struct assoc_request *assoc)
+{
+ struct cmd_ds_802_11_set_wep cmd;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.command = cpu_to_le16(CMD_802_11_SET_WEP);
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+ cmd.action = cpu_to_le16(cmd_action);
+
+ if (cmd_action == CMD_ACT_ADD) {
+ int i;
+
+ /* default tx key index */
+ cmd.keyindex = cpu_to_le16(assoc->wep_tx_keyidx &
+ CMD_WEP_KEY_INDEX_MASK);
+
+ /* Copy key types and material to host command structure */
+ for (i = 0; i < 4; i++) {
+ struct enc_key *pkey = &assoc->wep_keys[i];
+
+ switch (pkey->len) {
+ case KEY_LEN_WEP_40:
+ cmd.keytype[i] = CMD_TYPE_WEP_40_BIT;
+ memmove(cmd.keymaterial[i], pkey->key, pkey->len);
+ lbs_deb_cmd("SET_WEP: add key %d (40 bit)\n", i);
+ break;
+ case KEY_LEN_WEP_104:
+ cmd.keytype[i] = CMD_TYPE_WEP_104_BIT;
+ memmove(cmd.keymaterial[i], pkey->key, pkey->len);
+ lbs_deb_cmd("SET_WEP: add key %d (104 bit)\n", i);
+ break;
+ case 0:
+ break;
+ default:
+ lbs_deb_cmd("SET_WEP: invalid key %d, length %d\n",
+ i, pkey->len);
+ ret = -1;
+ goto done;
+ break;
+ }
+ }
+ } else if (cmd_action == CMD_ACT_REMOVE) {
+ /* ACT_REMOVE clears _all_ WEP keys */
+
+ /* default tx key index */
+ cmd.keyindex = cpu_to_le16(priv->wep_tx_keyidx &
+ CMD_WEP_KEY_INDEX_MASK);
+ lbs_deb_cmd("SET_WEP: remove key %d\n", priv->wep_tx_keyidx);
+ }
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_SET_WEP, &cmd);
+done:
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ return ret;
+}
+
+int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv, uint16_t cmd_action,
+ uint16_t *enable)
+{
+ struct cmd_ds_802_11_enable_rsn cmd;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ cmd.action = cpu_to_le16(cmd_action);
+
+ if (cmd_action == CMD_ACT_GET)
+ cmd.enable = 0;
+ else {
+ if (*enable)
+ cmd.enable = cpu_to_le16(CMD_ENABLE_RSN);
+ else
+ cmd.enable = cpu_to_le16(CMD_DISABLE_RSN);
+ lbs_deb_cmd("ENABLE_RSN: %d\n", *enable);
+ }
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_ENABLE_RSN, &cmd);
+ if (!ret && cmd_action == CMD_ACT_GET)
+ *enable = le16_to_cpu(cmd.enable);
+
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ return ret;
+}
+
+static void set_one_wpa_key(struct MrvlIEtype_keyParamSet *keyparam,
+ struct enc_key *key)
+{
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ if (key->flags & KEY_INFO_WPA_ENABLED)
+ keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED);
+ if (key->flags & KEY_INFO_WPA_UNICAST)
+ keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST);
+ if (key->flags & KEY_INFO_WPA_MCAST)
+ keyparam->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST);
+
+ keyparam->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
+ keyparam->keytypeid = cpu_to_le16(key->type);
+ keyparam->keylen = cpu_to_le16(key->len);
+ memcpy(keyparam->key, key->key, key->len);
+
+ /* Length field doesn't include the {type,length} header */
+ keyparam->length = cpu_to_le16(sizeof(*keyparam) - 4);
+ lbs_deb_leave(LBS_DEB_CMD);
+}
+
+int lbs_cmd_802_11_key_material(struct lbs_private *priv, uint16_t cmd_action,
+ struct assoc_request *assoc)
+{
+ struct cmd_ds_802_11_key_material cmd;
+ int ret = 0;
+ int index = 0;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ cmd.action = cpu_to_le16(cmd_action);
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+ if (cmd_action == CMD_ACT_GET) {
+ cmd.hdr.size = cpu_to_le16(S_DS_GEN + 2);
+ } else {
+ memset(cmd.keyParamSet, 0, sizeof(cmd.keyParamSet));
+
+ if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc->flags)) {
+ set_one_wpa_key(&cmd.keyParamSet[index],
+ &assoc->wpa_unicast_key);
+ index++;
+ }
+
+ if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc->flags)) {
+ set_one_wpa_key(&cmd.keyParamSet[index],
+ &assoc->wpa_mcast_key);
+ index++;
+ }
+
+ /* The common header and as many keys as we included */
+ cmd.hdr.size = cpu_to_le16(offsetof(typeof(cmd),
+ keyParamSet[index]));
+ }
+ ret = lbs_cmd_with_response(priv, CMD_802_11_KEY_MATERIAL, &cmd);
+ /* Copy the returned key to driver private data */
+ if (!ret && cmd_action == CMD_ACT_GET) {
+ void *buf_ptr = cmd.keyParamSet;
+ void *resp_end = &(&cmd)[1];
+
+ while (buf_ptr < resp_end) {
+ struct MrvlIEtype_keyParamSet *keyparam = buf_ptr;
+ struct enc_key *key;
+ uint16_t param_set_len = le16_to_cpu(keyparam->length);
+ uint16_t key_len = le16_to_cpu(keyparam->keylen);
+ uint16_t key_flags = le16_to_cpu(keyparam->keyinfo);
+ uint16_t key_type = le16_to_cpu(keyparam->keytypeid);
+ void *end;
+
+ end = (void *)keyparam + sizeof(keyparam->type)
+ + sizeof(keyparam->length) + param_set_len;
+
+ /* Make sure we don't access past the end of the IEs */
+ if (end > resp_end)
+ break;
+
+ if (key_flags & KEY_INFO_WPA_UNICAST)
+ key = &priv->wpa_unicast_key;
+ else if (key_flags & KEY_INFO_WPA_MCAST)
+ key = &priv->wpa_mcast_key;
+ else
+ break;
+
+ /* Copy returned key into driver */
+ memset(key, 0, sizeof(struct enc_key));
+ if (key_len > sizeof(key->key))
+ break;
+ key->type = key_type;
+ key->flags = key_flags;
+ key->len = key_len;
+ memcpy(key->key, keyparam->key, key->len);
+
+ buf_ptr = end + 1;
+ }
+ }
+
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ return ret;
+}
+
+static __le16 lbs_rate_to_fw_bitmap(int rate, int lower_rates_ok)
+{
+/* Bit Rate
+* 15:13 Reserved
+* 12 54 Mbps
+* 11 48 Mbps
+* 10 36 Mbps
+* 9 24 Mbps
+* 8 18 Mbps
+* 7 12 Mbps
+* 6 9 Mbps
+* 5 6 Mbps
+* 4 Reserved
+* 3 11 Mbps
+* 2 5.5 Mbps
+* 1 2 Mbps
+* 0 1 Mbps
+**/
+
+ uint16_t ratemask;
+ int i = lbs_data_rate_to_fw_index(rate);
+ if (lower_rates_ok)
+ ratemask = (0x1fef >> (12 - i));
+ else
+ ratemask = (1 << i);
+ return cpu_to_le16(ratemask);
+}
+
+int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv,
+ uint16_t cmd_action)
+{
+ struct cmd_ds_802_11_rate_adapt_rateset cmd;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ if (!priv->cur_rate && !priv->enablehwauto)
+ return -EINVAL;
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+ cmd.action = cpu_to_le16(cmd_action);
+ cmd.enablehwauto = cpu_to_le16(priv->enablehwauto);
+ cmd.bitmap = lbs_rate_to_fw_bitmap(priv->cur_rate, priv->enablehwauto);
+ ret = lbs_cmd_with_response(priv, CMD_802_11_RATE_ADAPT_RATESET, &cmd);
+ if (!ret && cmd_action == CMD_ACT_GET) {
+ priv->ratebitmap = le16_to_cpu(cmd.bitmap);
+ priv->enablehwauto = le16_to_cpu(cmd.enablehwauto);
+ }
+
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ return ret;
+}
+
+/**
+ * @brief Set the data rate
+ *
+ * @param priv A pointer to struct lbs_private structure
+ * @param rate The desired data rate, or 0 to clear a locked rate
+ *
+ * @return 0 on success, error on failure
+ */
+int lbs_set_data_rate(struct lbs_private *priv, u8 rate)
+{
+ struct cmd_ds_802_11_data_rate cmd;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+ if (rate > 0) {
+ cmd.action = cpu_to_le16(CMD_ACT_SET_TX_FIX_RATE);
+ cmd.rates[0] = lbs_data_rate_to_fw_index(rate);
+ if (cmd.rates[0] == 0) {
+ lbs_deb_cmd("DATA_RATE: invalid requested rate of"
+ " 0x%02X\n", rate);
+ ret = 0;
+ goto out;
+ }
+ lbs_deb_cmd("DATA_RATE: set fixed 0x%02X\n", cmd.rates[0]);
+ } else {
+ cmd.action = cpu_to_le16(CMD_ACT_SET_TX_AUTO);
+ lbs_deb_cmd("DATA_RATE: setting auto\n");
+ }
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_DATA_RATE, &cmd);
+ if (ret)
+ goto out;
+
+ lbs_deb_hex(LBS_DEB_CMD, "DATA_RATE_RESP", (u8 *) &cmd, sizeof(cmd));
+
+ /* FIXME: get actual rates FW can do if this command actually returns
+ * all data rates supported.
+ */
+ priv->cur_rate = lbs_fw_index_to_data_rate(cmd.rates[0]);
+ lbs_deb_cmd("DATA_RATE: current rate is 0x%02x\n", priv->cur_rate);
+
+out:
+ lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+ return ret;
+}
+
+
+int lbs_cmd_802_11_rssi(struct lbs_private *priv,
+ struct cmd_ds_command *cmd)
+{
+
+ lbs_deb_enter(LBS_DEB_CMD);
+ cmd->command = cpu_to_le16(CMD_802_11_RSSI);
+ cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + S_DS_GEN);
+ cmd->params.rssi.N = cpu_to_le16(DEFAULT_BCN_AVG_FACTOR);
+
+ /* reset Beacon SNR/NF/RSSI values */
+ priv->SNR[TYPE_BEACON][TYPE_NOAVG] = 0;
+ priv->SNR[TYPE_BEACON][TYPE_AVG] = 0;
+ priv->NF[TYPE_BEACON][TYPE_NOAVG] = 0;
+ priv->NF[TYPE_BEACON][TYPE_AVG] = 0;
+ priv->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0;
+ priv->RSSI[TYPE_BEACON][TYPE_AVG] = 0;
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return 0;
+}
+
+int lbs_ret_802_11_rssi(struct lbs_private *priv,
+ struct cmd_ds_command *resp)
+{
+ struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ /* store the non average value */
+ priv->SNR[TYPE_BEACON][TYPE_NOAVG] = get_unaligned_le16(&rssirsp->SNR);
+ priv->NF[TYPE_BEACON][TYPE_NOAVG] =
+ get_unaligned_le16(&rssirsp->noisefloor);
+
+ priv->SNR[TYPE_BEACON][TYPE_AVG] = get_unaligned_le16(&rssirsp->avgSNR);
+ priv->NF[TYPE_BEACON][TYPE_AVG] =
+ get_unaligned_le16(&rssirsp->avgnoisefloor);
+
+ priv->RSSI[TYPE_BEACON][TYPE_NOAVG] =
+ CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG],
+ priv->NF[TYPE_BEACON][TYPE_NOAVG]);
+
+ priv->RSSI[TYPE_BEACON][TYPE_AVG] =
+ CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE,
+ priv->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE);
+
+ lbs_deb_cmd("RSSI: beacon %d, avg %d\n",
+ priv->RSSI[TYPE_BEACON][TYPE_NOAVG],
+ priv->RSSI[TYPE_BEACON][TYPE_AVG]);
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return 0;
+}
+
+
+int lbs_cmd_bcn_ctrl(struct lbs_private *priv,
+ struct cmd_ds_command *cmd,
+ u16 cmd_action)
+{
+ struct cmd_ds_802_11_beacon_control
+ *bcn_ctrl = &cmd->params.bcn_ctrl;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+ cmd->size =
+ cpu_to_le16(sizeof(struct cmd_ds_802_11_beacon_control)
+ + S_DS_GEN);
+ cmd->command = cpu_to_le16(CMD_802_11_BEACON_CTRL);
+
+ bcn_ctrl->action = cpu_to_le16(cmd_action);
+ bcn_ctrl->beacon_enable = cpu_to_le16(priv->beacon_enable);
+ bcn_ctrl->beacon_period = cpu_to_le16(priv->beacon_period);
+
+ lbs_deb_leave(LBS_DEB_CMD);
+ return 0;
+}
+
+int lbs_ret_802_11_bcn_ctrl(struct lbs_private *priv,
+ struct cmd_ds_command *resp)
+{
+ struct cmd_ds_802_11_beacon_control *bcn_ctrl =
+ &resp->params.bcn_ctrl;
+
+ lbs_deb_enter(LBS_DEB_CMD);
+
+ if (bcn_ctrl->action == CMD_ACT_GET) {
+ priv->beacon_enable = (u8) le16_to_cpu(bcn_ctrl->beacon_enable);
+ priv->beacon_period = le16_to_cpu(bcn_ctrl->beacon_period);
+ }
+
+ lbs_deb_enter(LBS_DEB_CMD);
+ return 0;
+}
+
+
+
+static int lbs_assoc_post(struct lbs_private *priv,
+ struct cmd_ds_802_11_associate_response *resp)
+{
+ int ret = 0;
+ union iwreq_data wrqu;
+ struct bss_descriptor *bss;
+ u16 status_code;
+
+ lbs_deb_enter(LBS_DEB_ASSOC);
+
+ if (!priv->in_progress_assoc_req) {
+ lbs_deb_assoc("ASSOC_RESP: no in-progress assoc request\n");
+ ret = -1;
+ goto done;
+ }
+ bss = &priv->in_progress_assoc_req->bss;
+
+ /*
+ * Older FW versions map the IEEE 802.11 Status Code in the association
+ * response to the following values returned in resp->statuscode:
+ *
+ * IEEE Status Code Marvell Status Code
+ * 0 -> 0x0000 ASSOC_RESULT_SUCCESS
+ * 13 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
+ * 14 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
+ * 15 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
+ * 16 -> 0x0004 ASSOC_RESULT_AUTH_REFUSED
+ * others -> 0x0003 ASSOC_RESULT_REFUSED
+ *
+ * Other response codes:
+ * 0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused)
+ * 0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for
+ * association response from the AP)
+ */
+
+ status_code = le16_to_cpu(resp->statuscode);
+ if (priv->fwrelease < 0x09000000) {
+ switch (status_code) {
+ case 0x00:
+ break;
+ case 0x01:
+ lbs_deb_assoc("ASSOC_RESP: invalid parameters\n");
+ break;
+ case 0x02:
+ lbs_deb_assoc("ASSOC_RESP: internal timer "
+ "expired while waiting for the AP\n");
+ break;
+ case 0x03:
+ lbs_deb_assoc("ASSOC_RESP: association "
+ "refused by AP\n");
+ break;
+ case 0x04:
+ lbs_deb_assoc("ASSOC_RESP: authentication "
+ "refused by AP\n");
+ break;
+ default:
+ lbs_deb_assoc("ASSOC_RESP: failure reason 0x%02x "
+ " unknown\n", status_code);
+ break;
+ }
+ } else {
+ /* v9+ returns the AP's association response */
+ lbs_deb_assoc("ASSOC_RESP: failure reason 0x%02x\n", status_code);
+ }
+
+ if (status_code) {
+ lbs_mac_event_disconnected(priv);
+ ret = -1;
+ goto done;
+ }
+
+ lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_RESP",
+ (void *) (resp + sizeof (resp->hdr)),
+ le16_to_cpu(resp->hdr.size) - sizeof (resp->hdr));
+
+ /* Send a Media Connected event, according to the Spec */
+ priv->connect_status = LBS_CONNECTED;
+
+ /* Update current SSID and BSSID */
+ memcpy(&priv->curbssparams.ssid, &bss->ssid, IEEE80211_MAX_SSID_LEN);
+ priv->curbssparams.ssid_len = bss->ssid_len;
+ memcpy(priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
+
+ priv->SNR[TYPE_RXPD][TYPE_AVG] = 0;
+ priv->NF[TYPE_RXPD][TYPE_AVG] = 0;
+
+ memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR));
+ memset(priv->rawNF, 0x00, sizeof(priv->rawNF));
+ priv->nextSNRNF = 0;
+ priv->numSNRNF = 0;
+
+ netif_carrier_on(priv->dev);
+ if (!priv->tx_pending_len)
+ netif_wake_queue(priv->dev);
+
+ memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+
+done:
+ lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+ return ret;
+}
+
+/**
+ * @brief This function prepares an association-class command.
+ *
+ * @param priv A pointer to struct lbs_private structure
+ * @param assoc_req The association request describing the BSS to associate
+ * or reassociate with
+ * @param command The actual command, either CMD_802_11_ASSOCIATE or
+ * CMD_802_11_REASSOCIATE
+ *
+ * @return 0 or -1
+ */
+static int lbs_associate(struct lbs_private *priv,
+ struct assoc_request *assoc_req,
+ u16 command)
+{
+ struct cmd_ds_802_11_associate cmd;
+ int ret = 0;
+ struct bss_descriptor *bss = &assoc_req->bss;
+ u8 *pos = &(cmd.iebuf[0]);
+ u16 tmpcap, tmplen, tmpauth;
+ struct mrvl_ie_ssid_param_set *ssid;
+ struct mrvl_ie_ds_param_set *ds;
+ struct mrvl_ie_cf_param_set *cf;
+ struct mrvl_ie_rates_param_set *rates;
+ struct mrvl_ie_rsn_param_set *rsn;
+ struct mrvl_ie_auth_type *auth;
+
+ lbs_deb_enter(LBS_DEB_ASSOC);
+
+ BUG_ON((command != CMD_802_11_ASSOCIATE) &&
+ (command != CMD_802_11_REASSOCIATE));
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.command = cpu_to_le16(command);
+
+ /* Fill in static fields */
+ memcpy(cmd.bssid, bss->bssid, ETH_ALEN);
+ cmd.listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL);
+
+ /* Capability info */
+ tmpcap = (bss->capability & CAPINFO_MASK);
+ if (bss->mode == IW_MODE_INFRA)
+ tmpcap |= WLAN_CAPABILITY_ESS;
+ cmd.capability = cpu_to_le16(tmpcap);
+ lbs_deb_assoc("ASSOC_CMD: capability 0x%04x\n", tmpcap);
+
+ /* SSID */
+ ssid = (struct mrvl_ie_ssid_param_set *) pos;
+ ssid->header.type = cpu_to_le16(TLV_TYPE_SSID);
+ tmplen = bss->ssid_len;
+ ssid->header.len = cpu_to_le16(tmplen);
+ memcpy(ssid->ssid, bss->ssid, tmplen);
+ pos += sizeof(ssid->header) + tmplen;
+
+ ds = (struct mrvl_ie_ds_param_set *) pos;
+ ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS);
+ ds->header.len = cpu_to_le16(1);
+ ds->channel = bss->phy.ds.channel;
+ pos += sizeof(ds->header) + 1;
+
+ cf = (struct mrvl_ie_cf_param_set *) pos;
+ cf->header.type = cpu_to_le16(TLV_TYPE_CF);
+ tmplen = sizeof(*cf) - sizeof (cf->header);
+ cf->header.len = cpu_to_le16(tmplen);
+ /* IE payload should be zeroed, firmware fills it in for us */
+ pos += sizeof(*cf);
+
+ rates = (struct mrvl_ie_rates_param_set *) pos;
+ rates->header.type = cpu_to_le16(TLV_TYPE_RATES);
+ tmplen = min_t(u16, ARRAY_SIZE(bss->rates), MAX_RATES);
+ memcpy(&rates->rates, &bss->rates, tmplen);
+ if (get_common_rates(priv, rates->rates, &tmplen)) {
+ ret = -1;
+ goto done;
+ }
+ pos += sizeof(rates->header) + tmplen;
+ rates->header.len = cpu_to_le16(tmplen);
+ lbs_deb_assoc("ASSOC_CMD: num rates %u\n", tmplen);
+
+ /* Copy the infra. association rates into Current BSS state structure */
+ memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
+ memcpy(&priv->curbssparams.rates, &rates->rates, tmplen);
+
+ /* Set MSB on basic rates as the firmware requires, but _after_
+ * copying to current bss rates.
+ */
+ lbs_set_basic_rate_flags(rates->rates, tmplen);
+
+ /* Firmware v9+ indicate authentication suites as a TLV */
+ if (priv->fwrelease >= 0x09000000) {
+ auth = (struct mrvl_ie_auth_type *) pos;
+ auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
+ auth->header.len = cpu_to_le16(2);
+ tmpauth = iw_auth_to_ieee_auth(priv->secinfo.auth_mode);
+ auth->auth = cpu_to_le16(tmpauth);
+ pos += sizeof(auth->header) + 2;
+
+ lbs_deb_join("AUTH_CMD: BSSID %pM, auth 0x%x\n",
+ bss->bssid, priv->secinfo.auth_mode);
+ }
+
+ /* WPA/WPA2 IEs */
+ if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
+ rsn = (struct mrvl_ie_rsn_param_set *) pos;
+ /* WPA_IE or WPA2_IE */
+ rsn->header.type = cpu_to_le16((u16) assoc_req->wpa_ie[0]);
+ tmplen = (u16) assoc_req->wpa_ie[1];
+ rsn->header.len = cpu_to_le16(tmplen);
+ memcpy(rsn->rsnie, &assoc_req->wpa_ie[2], tmplen);
+ lbs_deb_hex(LBS_DEB_JOIN, "ASSOC_CMD: WPA/RSN IE", (u8 *) rsn,
+ sizeof(rsn->header) + tmplen);
+ pos += sizeof(rsn->header) + tmplen;
+ }
+
+ cmd.hdr.size = cpu_to_le16((sizeof(cmd) - sizeof(cmd.iebuf)) +
+ (u16)(pos - (u8 *) &cmd.iebuf));
+
+ /* update curbssparams */
+ priv->channel = bss->phy.ds.channel;
+
+ ret = lbs_cmd_with_response(priv, command, &cmd);
+ if (ret == 0) {
+ ret = lbs_assoc_post(priv,
+ (struct cmd_ds_802_11_associate_response *) &cmd);
+ }
+
+done:
+ lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+ return ret;
+}
+
+/**
+ * @brief Associate to a specific BSS discovered in a scan
+ *
+ * @param priv A pointer to struct lbs_private structure
+ * @param assoc_req The association request describing the BSS to associate with
+ *
+ * @return 0-success, otherwise fail
+ */
+static int lbs_try_associate(struct lbs_private *priv,
+ struct assoc_request *assoc_req)
+{
+ int ret;
+ u8 preamble = RADIO_PREAMBLE_LONG;
+
+ lbs_deb_enter(LBS_DEB_ASSOC);
+
+ /* FW v9 and higher indicate authentication suites as a TLV in the
+ * association command, not as a separate authentication command.
+ */
+ if (priv->fwrelease < 0x09000000) {
+ ret = lbs_set_authentication(priv, assoc_req->bss.bssid,
+ priv->secinfo.auth_mode);
+ if (ret)
+ goto out;
+ }
+
+ /* Use short preamble only when both the BSS and firmware support it */
+ if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
+ (assoc_req->bss.capability & WLAN_CAPABILITY_SHORT_PREAMBLE))
+ preamble = RADIO_PREAMBLE_SHORT;
+
+ ret = lbs_set_radio(priv, preamble, 1);
+ if (ret)
+ goto out;
+
+ ret = lbs_associate(priv, assoc_req, CMD_802_11_ASSOCIATE);
+
+out:
+ lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+ return ret;
+}
+
+static int lbs_adhoc_post(struct lbs_private *priv,
+ struct cmd_ds_802_11_ad_hoc_result *resp)
+{
+ int ret = 0;
+ u16 command = le16_to_cpu(resp->hdr.command);
+ u16 result = le16_to_cpu(resp->hdr.result);
+ union iwreq_data wrqu;
+ struct bss_descriptor *bss;
+ DECLARE_SSID_BUF(ssid);
+
+ lbs_deb_enter(LBS_DEB_JOIN);
+
+ if (!priv->in_progress_assoc_req) {
+ lbs_deb_join("ADHOC_RESP: no in-progress association "
+ "request\n");
+ ret = -1;
+ goto done;
+ }
+ bss = &priv->in_progress_assoc_req->bss;
+
+ /*
+ * Join result code 0 --> SUCCESS
+ */
+ if (result) {
+ lbs_deb_join("ADHOC_RESP: failed (result 0x%X)\n", result);
+ if (priv->connect_status == LBS_CONNECTED)
+ lbs_mac_event_disconnected(priv);
+ ret = -1;
+ goto done;
+ }
+
+ /* Send a Media Connected event, according to the Spec */
+ priv->connect_status = LBS_CONNECTED;
+
+ if (command == CMD_RET(CMD_802_11_AD_HOC_START)) {
+ /* Update the created network descriptor with the new BSSID */
+ memcpy(bss->bssid, resp->bssid, ETH_ALEN);
+ }
+
+ /* Set the BSSID from the joined/started descriptor */
+ memcpy(&priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
+
+ /* Set the new SSID to current SSID */
+ memcpy(&priv->curbssparams.ssid, &bss->ssid, IEEE80211_MAX_SSID_LEN);
+ priv->curbssparams.ssid_len = bss->ssid_len;
+
+ netif_carrier_on(priv->dev);
+ if (!priv->tx_pending_len)
+ netif_wake_queue(priv->dev);
+
+ memset(&wrqu, 0, sizeof(wrqu));
+ memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+
+ lbs_deb_join("ADHOC_RESP: Joined/started '%s', BSSID %pM, channel %d\n",
+ print_ssid(ssid, bss->ssid, bss->ssid_len),
+ priv->curbssparams.bssid,
+ priv->channel);
+
+done:
+ lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
+ return ret;
+}
+
+/**
+ * @brief Join an adhoc network found in a previous scan
+ *
+ * @param priv A pointer to struct lbs_private structure
+ * @param assoc_req The association request describing the BSS to join
+ *
+ * @return 0 on success, error on failure
+ */
+static int lbs_adhoc_join(struct lbs_private *priv,
+ struct assoc_request *assoc_req)
+{
+ struct cmd_ds_802_11_ad_hoc_join cmd;
+ struct bss_descriptor *bss = &assoc_req->bss;
+ u8 preamble = RADIO_PREAMBLE_LONG;
+ DECLARE_SSID_BUF(ssid);
+ u16 ratesize = 0;
+ int ret = 0;
+
+ lbs_deb_enter(LBS_DEB_ASSOC);
+
+ lbs_deb_join("current SSID '%s', ssid length %u\n",
+ print_ssid(ssid, priv->curbssparams.ssid,
+ priv->curbssparams.ssid_len),
+ priv->curbssparams.ssid_len);
+ lbs_deb_join("requested ssid '%s', ssid length %u\n",
+ print_ssid(ssid, bss->ssid, bss->ssid_len),
+ bss->ssid_len);
+
+ /* check if the requested SSID is already joined */
+ if (priv->curbssparams.ssid_len &&
+ !lbs_ssid_cmp(priv->curbssparams.ssid,
+ priv->curbssparams.ssid_len,
+ bss->ssid, bss->ssid_len) &&
+ (priv->mode == IW_MODE_ADHOC) &&
+ (priv->connect_status == LBS_CONNECTED)) {
+ union iwreq_data wrqu;
+
+ lbs_deb_join("ADHOC_J_CMD: New ad-hoc SSID is the same as "
+ "current, not attempting to re-join");
+
+ /* Send the re-association event though, because the association
+ * request really was successful, even if just a null-op.
+ */
+ memset(&wrqu, 0, sizeof(wrqu));
+ memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid,
+ ETH_ALEN);
+ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+ wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+ goto out;
+ }
+
+ /* Use short preamble only when both the BSS and firmware support it */
+ if ((priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) &&
+ (bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)) {
+ lbs_deb_join("AdhocJoin: Short preamble\n");
+ preamble = RADIO_PREAMBLE_SHORT;
+ }
+
+ ret = lbs_set_radio(priv, preamble, 1);
+ if (ret)
+ goto out;
+
+ lbs_deb_join("AdhocJoin: channel = %d\n", assoc_req->channel);
+ lbs_deb_join("AdhocJoin: band = %c\n", assoc_req->band);
+
+ priv->adhoccreate = 0;
+ priv->channel = bss->channel;
+
+ /* Build the join command */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+ cmd.bss.type = CMD_BSS_TYPE_IBSS;
+ cmd.bss.beaconperiod = cpu_to_le16(bss->beaconperiod);
+
+ memcpy(&cmd.bss.bssid, &bss->bssid, ETH_ALEN);
+ memcpy(&cmd.bss.ssid, &bss->ssid, bss->ssid_len);
+
+ memcpy(&cmd.bss.ds, &bss->phy.ds, sizeof(struct ieee_ie_ds_param_set));
+
+ memcpy(&cmd.bss.ibss, &bss->ss.ibss,
+ sizeof(struct ieee_ie_ibss_param_set));
+
+ cmd.bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK);
+ lbs_deb_join("ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n",
+ bss->capability, CAPINFO_MASK);
+
+ /* information on BSSID descriptor passed to FW */
+ lbs_deb_join("ADHOC_J_CMD: BSSID = %pM, SSID = '%s'\n",
+ cmd.bss.bssid, cmd.bss.ssid);
+
+ /* Only v8 and below support setting these */
+ if (priv->fwrelease < 0x09000000) {
+ /* failtimeout */
+ cmd.failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT);
+ /* probedelay */
+ cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
+ }
+
+ /* Copy Data rates from the rates recorded in scan response */
+ memset(cmd.bss.rates, 0, sizeof(cmd.bss.rates));
+ ratesize = min_t(u16, ARRAY_SIZE(cmd.bss.rates), ARRAY_SIZE (bss->rates));
+ memcpy(cmd.bss.rates, bss->rates, ratesize);
+ if (get_common_rates(priv, cmd.bss.rates, &ratesize)) {
+ lbs_deb_join("ADHOC_JOIN: get_common_rates returned error.\n");
+ ret = -1;
+ goto out;
+ }
-/* From ieee80211_module.c */
-static const char *libertas_escape_essid(const char *essid, u8 essid_len)
+ /* Copy the ad-hoc creation rates into Current BSS state structure */
+ memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
+ memcpy(&priv->curbssparams.rates, cmd.bss.rates, ratesize);
+
+ /* Set MSB on basic rates as the firmware requires, but _after_
+ * copying to current bss rates.
+ */
+ lbs_set_basic_rate_flags(cmd.bss.rates, ratesize);
+
+ cmd.bss.ibss.atimwindow = bss->atimwindow;
+
+ if (assoc_req->secinfo.wep_enabled) {
+ u16 tmp = le16_to_cpu(cmd.bss.capability);
+ tmp |= WLAN_CAPABILITY_PRIVACY;
+ cmd.bss.capability = cpu_to_le16(tmp);
+ }
+
+ if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
+ __le32 local_ps_mode = cpu_to_le32(LBS802_11POWERMODECAM);
+
+ /* wake up first */
+ ret = lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE,
+ CMD_ACT_SET, 0, 0,
+ &local_ps_mode);
+ if (ret) {
+ ret = -1;
+ goto out;
+ }
+ }
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_JOIN, &cmd);
+ if (ret == 0) {
+ ret = lbs_adhoc_post(priv,
+ (struct cmd_ds_802_11_ad_hoc_result *)&cmd);
+ }
+
+out:
+ lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+ return ret;
+}
+
+/**
+ * @brief Start an Adhoc Network
+ *
+ * @param priv A pointer to struct lbs_private structure
+ * @param assoc_req The association request describing the BSS to start
+ *
+ * @return 0 on success, error on failure
+ */
+static int lbs_adhoc_start(struct lbs_private *priv,
+ struct assoc_request *assoc_req)
{
- static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
- const char *s = essid;
- char *d = escaped;
+ struct cmd_ds_802_11_ad_hoc_start cmd;
+ u8 preamble = RADIO_PREAMBLE_LONG;
+ size_t ratesize = 0;
+ u16 tmpcap = 0;
+ int ret = 0;
+ DECLARE_SSID_BUF(ssid);
- if (ieee80211_is_empty_essid(essid, essid_len))
- return "";
+ lbs_deb_enter(LBS_DEB_ASSOC);
- essid_len = min(essid_len, (u8) IW_ESSID_MAX_SIZE);
- while (essid_len--) {
- if (*s == '\0') {
- *d++ = '\\';
- *d++ = '0';
- s++;
- } else {
- *d++ = *s++;
+ if (priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) {
+ lbs_deb_join("ADHOC_START: Will use short preamble\n");
+ preamble = RADIO_PREAMBLE_SHORT;
+ }
+
+ ret = lbs_set_radio(priv, preamble, 1);
+ if (ret)
+ goto out;
+
+ /* Build the start command */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+ memcpy(cmd.ssid, assoc_req->ssid, assoc_req->ssid_len);
+
+ lbs_deb_join("ADHOC_START: SSID '%s', ssid length %u\n",
+ print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len),
+ assoc_req->ssid_len);
+
+ cmd.bsstype = CMD_BSS_TYPE_IBSS;
+
+ if (priv->beacon_period == 0)
+ priv->beacon_period = MRVDRV_BEACON_INTERVAL;
+ cmd.beaconperiod = cpu_to_le16(priv->beacon_period);
+
+ WARN_ON(!assoc_req->channel);
+
+ /* set Physical parameter set */
+ cmd.ds.header.id = WLAN_EID_DS_PARAMS;
+ cmd.ds.header.len = 1;
+ cmd.ds.channel = assoc_req->channel;
+
+ /* set IBSS parameter set */
+ cmd.ibss.header.id = WLAN_EID_IBSS_PARAMS;
+ cmd.ibss.header.len = 2;
+ cmd.ibss.atimwindow = cpu_to_le16(0);
+
+ /* set capability info */
+ tmpcap = WLAN_CAPABILITY_IBSS;
+ if (assoc_req->secinfo.wep_enabled ||
+ assoc_req->secinfo.WPAenabled ||
+ assoc_req->secinfo.WPA2enabled) {
+ lbs_deb_join("ADHOC_START: WEP/WPA enabled, privacy on\n");
+ tmpcap |= WLAN_CAPABILITY_PRIVACY;
+ } else
+ lbs_deb_join("ADHOC_START: WEP disabled, privacy off\n");
+
+ cmd.capability = cpu_to_le16(tmpcap);
+
+ /* Only v8 and below support setting probe delay */
+ if (priv->fwrelease < 0x09000000)
+ cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
+
+ ratesize = min(sizeof(cmd.rates), sizeof(lbs_bg_rates));
+ memcpy(cmd.rates, lbs_bg_rates, ratesize);
+
+ /* Copy the ad-hoc creating rates into Current BSS state structure */
+ memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
+ memcpy(&priv->curbssparams.rates, &cmd.rates, ratesize);
+
+ /* Set MSB on basic rates as the firmware requires, but _after_
+ * copying to current bss rates.
+ */
+ lbs_set_basic_rate_flags(cmd.rates, ratesize);
+
+ lbs_deb_join("ADHOC_START: rates=%02x %02x %02x %02x\n",
+ cmd.rates[0], cmd.rates[1], cmd.rates[2], cmd.rates[3]);
+
+ lbs_deb_join("ADHOC_START: Starting Ad-Hoc BSS on channel %d, band %d\n",
+ assoc_req->channel, assoc_req->band);
+
+ priv->adhoccreate = 1;
+ priv->mode = IW_MODE_ADHOC;
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_START, &cmd);
+ if (ret == 0)
+ ret = lbs_adhoc_post(priv,
+ (struct cmd_ds_802_11_ad_hoc_result *)&cmd);
+
+out:
+ lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+ return ret;
+}
+
+/**
+ * @brief Stop and Ad-Hoc network and exit Ad-Hoc mode
+ *
+ * @param priv A pointer to struct lbs_private structure
+ * @return 0 on success, or an error
+ */
+int lbs_adhoc_stop(struct lbs_private *priv)
+{
+ struct cmd_ds_802_11_ad_hoc_stop cmd;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_JOIN);
+
+ memset(&cmd, 0, sizeof (cmd));
+ cmd.hdr.size = cpu_to_le16 (sizeof (cmd));
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_AD_HOC_STOP, &cmd);
+
+ /* Clean up everything even if there was an error */
+ lbs_mac_event_disconnected(priv);
+
+ lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+ return ret;
+}
+
+static inline int match_bss_no_security(struct lbs_802_11_security *secinfo,
+ struct bss_descriptor *match_bss)
+{
+ if (!secinfo->wep_enabled && !secinfo->WPAenabled
+ && !secinfo->WPA2enabled
+ && match_bss->wpa_ie[0] != WLAN_EID_GENERIC
+ && match_bss->rsn_ie[0] != WLAN_EID_RSN
+ && !(match_bss->capability & WLAN_CAPABILITY_PRIVACY))
+ return 1;
+ else
+ return 0;
+}
+
+static inline int match_bss_static_wep(struct lbs_802_11_security *secinfo,
+ struct bss_descriptor *match_bss)
+{
+ if (secinfo->wep_enabled && !secinfo->WPAenabled
+ && !secinfo->WPA2enabled
+ && (match_bss->capability & WLAN_CAPABILITY_PRIVACY))
+ return 1;
+ else
+ return 0;
+}
+
+static inline int match_bss_wpa(struct lbs_802_11_security *secinfo,
+ struct bss_descriptor *match_bss)
+{
+ if (!secinfo->wep_enabled && secinfo->WPAenabled
+ && (match_bss->wpa_ie[0] == WLAN_EID_GENERIC)
+ /* privacy bit may NOT be set in some APs like LinkSys WRT54G
+ && (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */
+ )
+ return 1;
+ else
+ return 0;
+}
+
+static inline int match_bss_wpa2(struct lbs_802_11_security *secinfo,
+ struct bss_descriptor *match_bss)
+{
+ if (!secinfo->wep_enabled && secinfo->WPA2enabled &&
+ (match_bss->rsn_ie[0] == WLAN_EID_RSN)
+ /* privacy bit may NOT be set in some APs like LinkSys WRT54G
+ (match_bss->capability & WLAN_CAPABILITY_PRIVACY) */
+ )
+ return 1;
+ else
+ return 0;
+}
+
+static inline int match_bss_dynamic_wep(struct lbs_802_11_security *secinfo,
+ struct bss_descriptor *match_bss)
+{
+ if (!secinfo->wep_enabled && !secinfo->WPAenabled
+ && !secinfo->WPA2enabled
+ && (match_bss->wpa_ie[0] != WLAN_EID_GENERIC)
+ && (match_bss->rsn_ie[0] != WLAN_EID_RSN)
+ && (match_bss->capability & WLAN_CAPABILITY_PRIVACY))
+ return 1;
+ else
+ return 0;
+}
+
+/**
+ * @brief Check if a scanned network compatible with the driver settings
+ *
+ * WEP WPA WPA2 ad-hoc encrypt Network
+ * enabled enabled enabled AES mode privacy WPA WPA2 Compatible
+ * 0 0 0 0 NONE 0 0 0 yes No security
+ * 1 0 0 0 NONE 1 0 0 yes Static WEP
+ * 0 1 0 0 x 1x 1 x yes WPA
+ * 0 0 1 0 x 1x x 1 yes WPA2
+ * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES
+ * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP
+ *
+ *
+ * @param priv A pointer to struct lbs_private
+ * @param index Index in scantable to check against current driver settings
+ * @param mode Network mode: Infrastructure or IBSS
+ *
+ * @return Index in scantable, or error code if negative
+ */
+static int is_network_compatible(struct lbs_private *priv,
+ struct bss_descriptor *bss, uint8_t mode)
+{
+ int matched = 0;
+
+ lbs_deb_enter(LBS_DEB_SCAN);
+
+ if (bss->mode != mode)
+ goto done;
+
+ matched = match_bss_no_security(&priv->secinfo, bss);
+ if (matched)
+ goto done;
+ matched = match_bss_static_wep(&priv->secinfo, bss);
+ if (matched)
+ goto done;
+ matched = match_bss_wpa(&priv->secinfo, bss);
+ if (matched) {
+ lbs_deb_scan("is_network_compatible() WPA: wpa_ie 0x%x "
+ "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s "
+ "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0],
+ priv->secinfo.wep_enabled ? "e" : "d",
+ priv->secinfo.WPAenabled ? "e" : "d",
+ priv->secinfo.WPA2enabled ? "e" : "d",
+ (bss->capability & WLAN_CAPABILITY_PRIVACY));
+ goto done;
+ }
+ matched = match_bss_wpa2(&priv->secinfo, bss);
+ if (matched) {
+ lbs_deb_scan("is_network_compatible() WPA2: wpa_ie 0x%x "
+ "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s "
+ "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0],
+ priv->secinfo.wep_enabled ? "e" : "d",
+ priv->secinfo.WPAenabled ? "e" : "d",
+ priv->secinfo.WPA2enabled ? "e" : "d",
+ (bss->capability & WLAN_CAPABILITY_PRIVACY));
+ goto done;
+ }
+ matched = match_bss_dynamic_wep(&priv->secinfo, bss);
+ if (matched) {
+ lbs_deb_scan("is_network_compatible() dynamic WEP: "
+ "wpa_ie 0x%x wpa2_ie 0x%x privacy 0x%x\n",
+ bss->wpa_ie[0], bss->rsn_ie[0],
+ (bss->capability & WLAN_CAPABILITY_PRIVACY));
+ goto done;
+ }
+
+ /* bss security settings don't match those configured on card */
+ lbs_deb_scan("is_network_compatible() FAILED: wpa_ie 0x%x "
+ "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s privacy 0x%x\n",
+ bss->wpa_ie[0], bss->rsn_ie[0],
+ priv->secinfo.wep_enabled ? "e" : "d",
+ priv->secinfo.WPAenabled ? "e" : "d",
+ priv->secinfo.WPA2enabled ? "e" : "d",
+ (bss->capability & WLAN_CAPABILITY_PRIVACY));
+
+done:
+ lbs_deb_leave_args(LBS_DEB_SCAN, "matched: %d", matched);
+ return matched;
+}
+
+/**
+ * @brief This function finds a specific compatible BSSID in the scan list
+ *
+ * Used in association code
+ *
+ * @param priv A pointer to struct lbs_private
+ * @param bssid BSSID to find in the scan list
+ * @param mode Network mode: Infrastructure or IBSS
+ *
+ * @return index in BSSID list, or error return code (< 0)
+ */
+static struct bss_descriptor *lbs_find_bssid_in_list(struct lbs_private *priv,
+ uint8_t *bssid, uint8_t mode)
+{
+ struct bss_descriptor *iter_bss;
+ struct bss_descriptor *found_bss = NULL;
+
+ lbs_deb_enter(LBS_DEB_SCAN);
+
+ if (!bssid)
+ goto out;
+
+ lbs_deb_hex(LBS_DEB_SCAN, "looking for", bssid, ETH_ALEN);
+
+ /* Look through the scan table for a compatible match. The loop will
+ * continue past a matched bssid that is not compatible in case there
+ * is an AP with multiple SSIDs assigned to the same BSSID
+ */
+ mutex_lock(&priv->lock);
+ list_for_each_entry(iter_bss, &priv->network_list, list) {
+ if (compare_ether_addr(iter_bss->bssid, bssid))
+ continue; /* bssid doesn't match */
+ switch (mode) {
+ case IW_MODE_INFRA:
+ case IW_MODE_ADHOC:
+ if (!is_network_compatible(priv, iter_bss, mode))
+ break;
+ found_bss = iter_bss;
+ break;
+ default:
+ found_bss = iter_bss;
+ break;
}
}
- *d = '\0';
- return escaped;
+ mutex_unlock(&priv->lock);
+
+out:
+ lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss);
+ return found_bss;
}
-static void print_assoc_req(const char * extra, struct assoc_request * assoc_req)
+/**
+ * @brief This function finds ssid in ssid list.
+ *
+ * Used in association code
+ *
+ * @param priv A pointer to struct lbs_private
+ * @param ssid SSID to find in the list
+ * @param bssid BSSID to qualify the SSID selection (if provided)
+ * @param mode Network mode: Infrastructure or IBSS
+ *
+ * @return index in BSSID list
+ */
+static struct bss_descriptor *lbs_find_ssid_in_list(struct lbs_private *priv,
+ uint8_t *ssid, uint8_t ssid_len,
+ uint8_t *bssid, uint8_t mode,
+ int channel)
{
- lbs_deb_assoc(
- "#### Association Request: %s\n"
- " flags: 0x%08lX\n"
- " SSID: '%s'\n"
- " channel: %d\n"
- " band: %d\n"
- " mode: %d\n"
- " BSSID: " MAC_FMT "\n"
- " WPA: %d\n"
- " WPA2: %d\n"
- " WEP status: %d\n"
- " auth: %d\n"
- " auth_alg: %d\n"
- " encmode: %d\n",
- extra, assoc_req->flags,
- libertas_escape_essid(assoc_req->ssid.ssid, assoc_req->ssid.ssidlength),
- assoc_req->channel, assoc_req->band, assoc_req->mode,
- MAC_ARG(assoc_req->bssid), assoc_req->secinfo.WPAenabled,
- assoc_req->secinfo.WPA2enabled, assoc_req->secinfo.WEPstatus,
- assoc_req->secinfo.authmode, assoc_req->secinfo.auth1xalg,
- assoc_req->secinfo.Encryptionmode);
-}
-
-
-static int assoc_helper_essid(wlan_private *priv,
+ u32 bestrssi = 0;
+ struct bss_descriptor *iter_bss = NULL;
+ struct bss_descriptor *found_bss = NULL;
+ struct bss_descriptor *tmp_oldest = NULL;
+
+ lbs_deb_enter(LBS_DEB_SCAN);
+
+ mutex_lock(&priv->lock);
+
+ list_for_each_entry(iter_bss, &priv->network_list, list) {
+ if (!tmp_oldest ||
+ (iter_bss->last_scanned < tmp_oldest->last_scanned))
+ tmp_oldest = iter_bss;
+
+ if (lbs_ssid_cmp(iter_bss->ssid, iter_bss->ssid_len,
+ ssid, ssid_len) != 0)
+ continue; /* ssid doesn't match */
+ if (bssid && compare_ether_addr(iter_bss->bssid, bssid) != 0)
+ continue; /* bssid doesn't match */
+ if ((channel > 0) && (iter_bss->channel != channel))
+ continue; /* channel doesn't match */
+
+ switch (mode) {
+ case IW_MODE_INFRA:
+ case IW_MODE_ADHOC:
+ if (!is_network_compatible(priv, iter_bss, mode))
+ break;
+
+ if (bssid) {
+ /* Found requested BSSID */
+ found_bss = iter_bss;
+ goto out;
+ }
+
+ if (SCAN_RSSI(iter_bss->rssi) > bestrssi) {
+ bestrssi = SCAN_RSSI(iter_bss->rssi);
+ found_bss = iter_bss;
+ }
+ break;
+ case IW_MODE_AUTO:
+ default:
+ if (SCAN_RSSI(iter_bss->rssi) > bestrssi) {
+ bestrssi = SCAN_RSSI(iter_bss->rssi);
+ found_bss = iter_bss;
+ }
+ break;
+ }
+ }
+
+out:
+ mutex_unlock(&priv->lock);
+ lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss);
+ return found_bss;
+}
+
+static int assoc_helper_essid(struct lbs_private *priv,
struct assoc_request * assoc_req)
{
- wlan_adapter *adapter = priv->adapter;
int ret = 0;
struct bss_descriptor * bss;
int channel = -1;
+ DECLARE_SSID_BUF(ssid);
lbs_deb_enter(LBS_DEB_ASSOC);
if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
channel = assoc_req->channel;
- lbs_deb_assoc("New SSID requested: %s\n", assoc_req->ssid.ssid);
+ lbs_deb_assoc("SSID '%s' requested\n",
+ print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len));
if (assoc_req->mode == IW_MODE_INFRA) {
- if (adapter->prescan) {
- libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 0);
- }
+ lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
+ assoc_req->ssid_len);
- bss = libertas_find_SSID_in_list(adapter, &assoc_req->ssid,
- NULL, IW_MODE_INFRA, channel);
+ bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
+ assoc_req->ssid_len, NULL, IW_MODE_INFRA, channel);
if (bss != NULL) {
- lbs_deb_assoc("SSID found in scan list, associating\n");
memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
- ret = wlan_associate(priv, assoc_req);
+ ret = lbs_try_associate(priv, assoc_req);
} else {
- lbs_deb_assoc("SSID '%s' not found; cannot associate\n",
- assoc_req->ssid.ssid);
+ 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
*/
- libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 1);
+ lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
+ assoc_req->ssid_len);
/* Search for the requested SSID in the scan table */
- bss = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, NULL,
- IW_MODE_ADHOC, channel);
+ 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 joining\n");
+ lbs_deb_assoc("SSID found, will join\n");
memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
- libertas_join_adhoc_network(priv, assoc_req);
+ lbs_adhoc_join(priv, assoc_req);
} else {
/* else send START command */
- lbs_deb_assoc("SSID not found in list, so creating adhoc"
- " with SSID '%s'\n", assoc_req->ssid.ssid);
+ lbs_deb_assoc("SSID not found, creating adhoc network\n");
memcpy(&assoc_req->bss.ssid, &assoc_req->ssid,
- sizeof(struct WLAN_802_11_SSID));
- libertas_start_adhoc_network(priv, assoc_req);
+ IEEE80211_MAX_SSID_LEN);
+ assoc_req->bss.ssid_len = assoc_req->ssid_len;
+ lbs_adhoc_start(priv, assoc_req);
}
}
}
-static int assoc_helper_bssid(wlan_private *priv,
+static int assoc_helper_bssid(struct lbs_private *priv,
struct assoc_request * assoc_req)
{
- wlan_adapter *adapter = priv->adapter;
int ret = 0;
struct bss_descriptor * bss;
- lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID" MAC_FMT "\n",
- MAC_ARG(assoc_req->bssid));
+ lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %pM", assoc_req->bssid);
/* Search for index position in list for requested MAC */
- bss = libertas_find_BSSID_in_list(adapter, assoc_req->bssid,
+ bss = lbs_find_bssid_in_list(priv, assoc_req->bssid,
assoc_req->mode);
if (bss == NULL) {
- lbs_deb_assoc("ASSOC: WAP: BSSID " MAC_FMT " not found, "
- "cannot associate.\n", MAC_ARG(assoc_req->bssid));
+ lbs_deb_assoc("ASSOC: WAP: BSSID %pM not found, "
+ "cannot associate.\n", assoc_req->bssid);
goto out;
}
memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
if (assoc_req->mode == IW_MODE_INFRA) {
- ret = wlan_associate(priv, assoc_req);
- lbs_deb_assoc("ASSOC: wlan_associate(bssid) returned %d\n", ret);
+ ret = lbs_try_associate(priv, assoc_req);
+ lbs_deb_assoc("ASSOC: lbs_try_associate(bssid) returned %d\n",
+ ret);
} else if (assoc_req->mode == IW_MODE_ADHOC) {
- libertas_join_adhoc_network(priv, assoc_req);
+ lbs_adhoc_join(priv, assoc_req);
}
out:
}
-static int assoc_helper_associate(wlan_private *priv,
+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)) {
&& compare_ether_addr(bssid_off, assoc_req->bssid)) {
ret = assoc_helper_bssid(priv, assoc_req);
done = 1;
- if (ret) {
- lbs_deb_assoc("ASSOC: bssid: ret = %d\n", ret);
- }
}
}
if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
ret = assoc_helper_essid(priv, assoc_req);
- if (ret) {
- lbs_deb_assoc("ASSOC: bssid: ret = %d\n", ret);
- }
}
+ lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
return ret;
}
-static int assoc_helper_mode(wlan_private *priv,
+static int assoc_helper_mode(struct lbs_private *priv,
struct assoc_request * assoc_req)
{
- wlan_adapter *adapter = priv->adapter;
int ret = 0;
lbs_deb_enter(LBS_DEB_ASSOC);
- if (assoc_req->mode == adapter->mode)
+ if (assoc_req->mode == priv->mode)
goto done;
if (assoc_req->mode == IW_MODE_INFRA) {
- if (adapter->psstate != PS_STATE_FULL_POWER)
- libertas_ps_wakeup(priv, cmd_option_waitforrsp);
- adapter->psmode = wlan802_11powermodecam;
+ if (priv->psstate != PS_STATE_FULL_POWER)
+ lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
+ priv->psmode = LBS802_11POWERMODECAM;
}
- adapter->mode = assoc_req->mode;
- ret = libertas_prepare_and_send_command(priv,
- cmd_802_11_snmp_mib,
- 0, cmd_option_waitforrsp,
- OID_802_11_INFRASTRUCTURE_MODE,
- (void *) (size_t) assoc_req->mode);
+ priv->mode = assoc_req->mode;
+ ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, assoc_req->mode);
done:
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
return ret;
}
-
-static int update_channel(wlan_private * priv)
-{
- /* the channel in f/w could be out of sync, get the current channel */
- return libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel,
- cmd_opt_802_11_rf_channel_get,
- cmd_option_waitforrsp, 0, NULL);
-}
-
-static int assoc_helper_channel(wlan_private *priv,
+static int assoc_helper_channel(struct lbs_private *priv,
struct assoc_request * assoc_req)
{
- wlan_adapter *adapter = priv->adapter;
int ret = 0;
lbs_deb_enter(LBS_DEB_ASSOC);
- ret = update_channel(priv);
- if (ret < 0) {
- lbs_deb_assoc("ASSOC: channel: error getting channel.");
+ ret = lbs_update_channel(priv);
+ if (ret) {
+ lbs_deb_assoc("ASSOC: channel: error getting channel.\n");
+ goto done;
}
- if (assoc_req->channel == adapter->curbssparams.channel)
+ if (assoc_req->channel == priv->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, CMD_ACT_MESH_CONFIG_STOP,
+ assoc_req->channel);
+ }
+
lbs_deb_assoc("ASSOC: channel: %d -> %d\n",
- adapter->curbssparams.channel, assoc_req->channel);
+ priv->channel, assoc_req->channel);
- ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel,
- cmd_opt_802_11_rf_channel_set,
- cmd_option_waitforrsp, 0, &assoc_req->channel);
- if (ret < 0) {
- lbs_deb_assoc("ASSOC: channel: error setting channel.");
- }
+ ret = lbs_set_channel(priv, assoc_req->channel);
+ if (ret < 0)
+ lbs_deb_assoc("ASSOC: channel: error setting channel.\n");
- ret = update_channel(priv);
- if (ret < 0) {
- lbs_deb_assoc("ASSOC: channel: error getting channel.");
+ /* 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 != adapter->curbssparams.channel) {
- lbs_deb_assoc("ASSOC: channel: failed to update channel to %d",
+ if (assoc_req->channel != priv->channel) {
+ lbs_deb_assoc("ASSOC: channel: failed to update channel to %d\n",
assoc_req->channel);
- goto done;
+ goto restore_mesh;
}
if ( assoc_req->secinfo.wep_enabled
}
/* Must restart/rejoin adhoc networks after channel change */
- set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
+ set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
-done:
+ restore_mesh:
+ if (priv->mesh_dev)
+ lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
+ priv->channel);
+
+ done:
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
return ret;
}
-static int assoc_helper_wep_keys(wlan_private *priv,
- struct assoc_request * assoc_req)
+static int assoc_helper_wep_keys(struct lbs_private *priv,
+ struct assoc_request *assoc_req)
{
- wlan_adapter *adapter = priv->adapter;
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 = libertas_prepare_and_send_command(priv,
- cmd_802_11_set_wep,
- cmd_act_add,
- cmd_option_waitforrsp,
- 0, assoc_req);
- } else {
- ret = libertas_prepare_and_send_command(priv,
- cmd_802_11_set_wep,
- cmd_act_remove,
- cmd_option_waitforrsp,
- 0, NULL);
- }
+ 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)
- adapter->currentpacketfilter |= cmd_act_mac_wep_enable;
+ priv->mac_control |= CMD_ACT_MAC_WEP_ENABLE;
else
- adapter->currentpacketfilter &= ~cmd_act_mac_wep_enable;
- ret = libertas_set_mac_packet_filter(priv);
- if (ret)
- goto out;
+ priv->mac_control &= ~CMD_ACT_MAC_WEP_ENABLE;
+
+ lbs_set_mac_control(priv);
- mutex_lock(&adapter->lock);
+ mutex_lock(&priv->lock);
- /* Copy WEP keys into adapter wep key fields */
+ /* Copy WEP keys into priv wep key fields */
for (i = 0; i < 4; i++) {
- memcpy(&adapter->wep_keys[i], &assoc_req->wep_keys[i],
- sizeof(struct WLAN_802_11_KEY));
+ memcpy(&priv->wep_keys[i], &assoc_req->wep_keys[i],
+ sizeof(struct enc_key));
}
- adapter->wep_tx_keyidx = assoc_req->wep_tx_keyidx;
+ priv->wep_tx_keyidx = assoc_req->wep_tx_keyidx;
- mutex_unlock(&adapter->lock);
+ mutex_unlock(&priv->lock);
out:
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
return ret;
}
-static int assoc_helper_secinfo(wlan_private *priv,
+static int assoc_helper_secinfo(struct lbs_private *priv,
struct assoc_request * assoc_req)
{
- wlan_adapter *adapter = priv->adapter;
int ret = 0;
+ uint16_t do_wpa;
+ uint16_t rsn = 0;
lbs_deb_enter(LBS_DEB_ASSOC);
- memcpy(&adapter->secinfo, &assoc_req->secinfo,
- sizeof(struct wlan_802_11_security));
+ memcpy(&priv->secinfo, &assoc_req->secinfo,
+ sizeof(struct lbs_802_11_security));
+
+ lbs_set_mac_control(priv);
+
+ /* 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;
- ret = libertas_set_mac_packet_filter(priv);
+ /* 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(wlan_private *priv,
+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);
- /* enable/Disable RSN */
- ret = libertas_prepare_and_send_command(priv,
- cmd_802_11_enable_rsn,
- cmd_act_set,
- cmd_option_waitforrsp,
- 0, assoc_req);
+ /* 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;
- ret = libertas_prepare_and_send_command(priv,
- cmd_802_11_key_material,
- cmd_act_set,
- cmd_option_waitforrsp,
- 0, assoc_req);
+ memcpy(&priv->wpa_unicast_key, &assoc_req->wpa_unicast_key,
+ sizeof(struct enc_key));
+
+ 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;
+
+ memcpy(&priv->wpa_mcast_key, &assoc_req->wpa_mcast_key,
+ sizeof(struct enc_key));
+ }
out:
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
}
-static int assoc_helper_wpa_ie(wlan_private *priv,
+static int assoc_helper_wpa_ie(struct lbs_private *priv,
struct assoc_request * assoc_req)
{
- wlan_adapter *adapter = priv->adapter;
int ret = 0;
lbs_deb_enter(LBS_DEB_ASSOC);
if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
- memcpy(&adapter->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len);
- adapter->wpa_ie_len = assoc_req->wpa_ie_len;
+ memcpy(&priv->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len);
+ priv->wpa_ie_len = assoc_req->wpa_ie_len;
} else {
- memset(&adapter->wpa_ie, 0, MAX_WPA_IE_LEN);
- adapter->wpa_ie_len = 0;
+ memset(&priv->wpa_ie, 0, MAX_WPA_IE_LEN);
+ priv->wpa_ie_len = 0;
}
lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
}
-static int should_deauth_infrastructure(wlan_adapter *adapter,
+static int should_deauth_infrastructure(struct lbs_private *priv,
struct assoc_request * assoc_req)
{
- if (adapter->connect_status != libertas_connected)
+ 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 in "
- " configuration request.\n");
- return 1;
+ lbs_deb_assoc("Deauthenticating due to new SSID\n");
+ ret = 1;
+ goto out;
}
if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
- if (adapter->secinfo.auth_mode != assoc_req->secinfo.auth_mode) {
- lbs_deb_assoc("Deauthenticating due to updated security "
- "info in configuration request.\n");
- return 1;
+ 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 in "
- " configuration request.\n");
- return 1;
+ 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)
- return 1;
+ if (assoc_req->mode != IW_MODE_INFRA) {
+ lbs_deb_assoc("Deauthenticating due to leaving "
+ "infra mode\n");
+ ret = 1;
+ goto out;
+ }
}
- return 0;
+out:
+ lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+ return ret;
}
-static int should_stop_adhoc(wlan_adapter *adapter,
+static int should_stop_adhoc(struct lbs_private *priv,
struct assoc_request * assoc_req)
{
- if (adapter->connect_status != libertas_connected)
+ lbs_deb_enter(LBS_DEB_ASSOC);
+
+ if (priv->connect_status != LBS_CONNECTED)
return 0;
- if (adapter->curbssparams.ssid.ssidlength != assoc_req->ssid.ssidlength)
- return 1;
- if (memcmp(adapter->curbssparams.ssid.ssid, assoc_req->ssid.ssid,
- adapter->curbssparams.ssid.ssidlength))
+ 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_CHANNEL, &assoc_req->flags)) {
- if (assoc_req->channel != adapter->curbssparams.channel)
+ if (assoc_req->channel != priv->channel)
return 1;
}
+ lbs_deb_leave(LBS_DEB_ASSOC);
return 0;
}
-void libertas_association_worker(struct work_struct *work)
+/**
+ * @brief This function finds the best SSID in the Scan List
+ *
+ * Search the scan table for the best SSID that also matches the current
+ * adapter network preference (infrastructure or adhoc)
+ *
+ * @param priv A pointer to struct lbs_private
+ *
+ * @return index in BSSID list
+ */
+static struct bss_descriptor *lbs_find_best_ssid_in_list(
+ struct lbs_private *priv, uint8_t mode)
+{
+ uint8_t bestrssi = 0;
+ struct bss_descriptor *iter_bss;
+ struct bss_descriptor *best_bss = NULL;
+
+ lbs_deb_enter(LBS_DEB_SCAN);
+
+ mutex_lock(&priv->lock);
+
+ list_for_each_entry(iter_bss, &priv->network_list, list) {
+ switch (mode) {
+ case IW_MODE_INFRA:
+ case IW_MODE_ADHOC:
+ if (!is_network_compatible(priv, iter_bss, mode))
+ break;
+ if (SCAN_RSSI(iter_bss->rssi) <= bestrssi)
+ break;
+ bestrssi = SCAN_RSSI(iter_bss->rssi);
+ best_bss = iter_bss;
+ break;
+ case IW_MODE_AUTO:
+ default:
+ if (SCAN_RSSI(iter_bss->rssi) <= bestrssi)
+ break;
+ bestrssi = SCAN_RSSI(iter_bss->rssi);
+ best_bss = iter_bss;
+ break;
+ }
+ }
+
+ mutex_unlock(&priv->lock);
+ lbs_deb_leave_args(LBS_DEB_SCAN, "best_bss %p", best_bss);
+ return best_bss;
+}
+
+/**
+ * @brief Find the best AP
+ *
+ * Used from association worker.
+ *
+ * @param priv A pointer to struct lbs_private structure
+ * @param pSSID A pointer to AP's ssid
+ *
+ * @return 0--success, otherwise--fail
+ */
+static int lbs_find_best_network_ssid(struct lbs_private *priv,
+ uint8_t *out_ssid, uint8_t *out_ssid_len, uint8_t preferred_mode,
+ uint8_t *out_mode)
+{
+ int ret = -1;
+ struct bss_descriptor *found;
+
+ lbs_deb_enter(LBS_DEB_SCAN);
+
+ priv->scan_ssid_len = 0;
+ lbs_scan_networks(priv, 1);
+ if (priv->surpriseremoved)
+ goto out;
+
+ found = lbs_find_best_ssid_in_list(priv, preferred_mode);
+ if (found && (found->ssid_len > 0)) {
+ memcpy(out_ssid, &found->ssid, IEEE80211_MAX_SSID_LEN);
+ *out_ssid_len = found->ssid_len;
+ *out_mode = found->mode;
+ ret = 0;
+ }
+
+out:
+ lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
+ return ret;
+}
+
+
+void lbs_association_worker(struct work_struct *work)
{
- wlan_private *priv = container_of(work, wlan_private, assoc_work.work);
- wlan_adapter *adapter = priv->adapter;
+ 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_SSID_BUF(ssid);
lbs_deb_enter(LBS_DEB_ASSOC);
- mutex_lock(&adapter->lock);
- assoc_req = adapter->pending_assoc_req;
- adapter->pending_assoc_req = NULL;
- adapter->in_progress_assoc_req = assoc_req;
- mutex_unlock(&adapter->lock);
+ 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;
- print_assoc_req(__func__, assoc_req);
+ lbs_deb_assoc(
+ "Association Request:\n"
+ " flags: 0x%08lx\n"
+ " SSID: '%s'\n"
+ " chann: %d\n"
+ " band: %d\n"
+ " mode: %d\n"
+ " BSSID: %pM\n"
+ " secinfo: %s%s%s\n"
+ " auth_mode: %d\n",
+ assoc_req->flags,
+ print_ssid(ssid, assoc_req->ssid, assoc_req->ssid_len),
+ assoc_req->channel, assoc_req->band, assoc_req->mode,
+ 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.ssidlength)
+ && !assoc_req->ssid_len)
find_any_ssid = 1;
/* But don't use 'any' SSID if there's a valid locked BSSID to use */
}
if (find_any_ssid) {
- u8 new_mode;
+ u8 new_mode = assoc_req->mode;
- ret = libertas_find_best_network_SSID(priv, &assoc_req->ssid,
- assoc_req->mode, &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;
* Check if the attributes being changing require deauthentication
* from the currently associated infrastructure access point.
*/
- if (adapter->mode == IW_MODE_INFRA) {
- if (should_deauth_infrastructure(adapter, assoc_req)) {
- ret = libertas_send_deauthentication(priv);
+ if (priv->mode == IW_MODE_INFRA) {
+ if (should_deauth_infrastructure(priv, assoc_req)) {
+ ret = lbs_cmd_80211_deauthenticate(priv,
+ priv->curbssparams.bssid,
+ WLAN_REASON_DEAUTH_LEAVING);
if (ret) {
lbs_deb_assoc("Deauthentication due to new "
"configuration request failed: %d\n",
ret);
}
}
- } else if (adapter->mode == IW_MODE_ADHOC) {
- if (should_stop_adhoc(adapter, assoc_req)) {
- ret = libertas_stop_adhoc_network(priv);
+ } else if (priv->mode == IW_MODE_ADHOC) {
+ if (should_stop_adhoc(priv, assoc_req)) {
+ ret = lbs_adhoc_stop(priv);
if (ret) {
lbs_deb_assoc("Teardown of AdHoc network due to "
"new configuration request failed: %d\n",
/* 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) {
-lbs_deb_assoc("ASSOC(:%d) mode: ret = %d\n", __LINE__, ret);
+ if (ret)
goto out;
- }
}
if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
ret = assoc_helper_channel(priv, assoc_req);
- if (ret) {
- lbs_deb_assoc("ASSOC(:%d) channel: ret = %d\n",
- __LINE__, ret);
+ 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) {
-lbs_deb_assoc("ASSOC(:%d) wep_keys: ret = %d\n", __LINE__, ret);
+ if (ret)
goto out;
- }
}
if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
ret = assoc_helper_secinfo(priv, assoc_req);
- if (ret) {
-lbs_deb_assoc("ASSOC(:%d) secinfo: ret = %d\n", __LINE__, ret);
+ if (ret)
goto out;
- }
}
if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
ret = assoc_helper_wpa_ie(priv, assoc_req);
- if (ret) {
-lbs_deb_assoc("ASSOC(:%d) wpa_ie: ret = %d\n", __LINE__, ret);
+ 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) {
-lbs_deb_assoc("ASSOC(:%d) wpa_keys: ret = %d\n", __LINE__, ret);
+ if (ret)
goto out;
- }
}
/* SSID/BSSID should be the _last_ config option set, because they
ret = assoc_helper_associate(priv, assoc_req);
if (ret) {
- lbs_deb_assoc("ASSOC: association attempt unsuccessful: %d\n",
+ lbs_deb_assoc("ASSOC: association unsuccessful: %d\n",
ret);
success = 0;
}
- if (adapter->connect_status != libertas_connected) {
- lbs_deb_assoc("ASSOC: assoication attempt unsuccessful, "
- "not connected.\n");
+ if (priv->connect_status != LBS_CONNECTED) {
+ lbs_deb_assoc("ASSOC: association unsuccessful, "
+ "not connected\n");
success = 0;
}
if (success) {
- lbs_deb_assoc("ASSOC: association attempt successful. "
- "Associated to '%s' (" MAC_FMT ")\n",
- libertas_escape_essid(adapter->curbssparams.ssid.ssid,
- adapter->curbssparams.ssid.ssidlength),
- MAC_ARG(adapter->curbssparams.bssid));
- libertas_prepare_and_send_command(priv,
- cmd_802_11_rssi,
- 0, cmd_option_waitforrsp, 0, NULL);
-
- libertas_prepare_and_send_command(priv,
- cmd_802_11_get_log,
- 0, cmd_option_waitforrsp, 0, NULL);
+ lbs_deb_assoc("associated to %pM\n",
+ priv->curbssparams.bssid);
+ lbs_prepare_and_send_command(priv,
+ CMD_802_11_RSSI,
+ 0, CMD_OPTION_WAITFORRSP, 0, NULL);
} else {
-
ret = -1;
}
}
ret);
}
- mutex_lock(&adapter->lock);
- adapter->in_progress_assoc_req = NULL;
- mutex_unlock(&adapter->lock);
+ mutex_lock(&priv->lock);
+ priv->in_progress_assoc_req = NULL;
+ mutex_unlock(&priv->lock);
kfree(assoc_req);
done:
/*
* Caller MUST hold any necessary locks
*/
-struct assoc_request * wlan_get_association_request(wlan_adapter *adapter)
+struct assoc_request *lbs_get_association_request(struct lbs_private *priv)
{
struct assoc_request * assoc_req;
- if (!adapter->pending_assoc_req) {
- adapter->pending_assoc_req = kzalloc(sizeof(struct assoc_request),
+ lbs_deb_enter(LBS_DEB_ASSOC);
+ if (!priv->pending_assoc_req) {
+ priv->pending_assoc_req = kzalloc(sizeof(struct assoc_request),
GFP_KERNEL);
- if (!adapter->pending_assoc_req) {
+ 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 = adapter->pending_assoc_req;
+ assoc_req = priv->pending_assoc_req;
if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
- memcpy(&assoc_req->ssid, &adapter->curbssparams.ssid,
- sizeof(struct WLAN_802_11_SSID));
+ memcpy(&assoc_req->ssid, &priv->curbssparams.ssid,
+ IEEE80211_MAX_SSID_LEN);
+ assoc_req->ssid_len = priv->curbssparams.ssid_len;
}
if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
- assoc_req->channel = adapter->curbssparams.channel;
+ assoc_req->channel = priv->channel;
if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags))
- assoc_req->band = adapter->curbssparams.band;
+ assoc_req->band = priv->curbssparams.band;
if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags))
- assoc_req->mode = adapter->mode;
+ assoc_req->mode = priv->mode;
if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
- memcpy(&assoc_req->bssid, adapter->curbssparams.bssid,
+ 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], &adapter->wep_keys[i],
- sizeof(struct WLAN_802_11_KEY));
+ 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 = adapter->wep_tx_keyidx;
+ 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, &adapter->wpa_mcast_key,
- sizeof(struct WLAN_802_11_KEY));
+ 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, &adapter->wpa_unicast_key,
- sizeof(struct WLAN_802_11_KEY));
+ 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, &adapter->secinfo,
- sizeof(struct wlan_802_11_security));
+ 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, &adapter->wpa_ie,
+ memcpy(&assoc_req->wpa_ie, &priv->wpa_ie,
MAX_WPA_IE_LEN);
- assoc_req->wpa_ie_len = adapter->wpa_ie_len;
+ assoc_req->wpa_ie_len = priv->wpa_ie_len;
}
- print_assoc_req(__func__, assoc_req);
-
+ lbs_deb_leave(LBS_DEB_ASSOC);
return assoc_req;
}
+
+
+/**
+ * @brief Deauthenticate from a specific BSS
+ *
+ * @param priv A pointer to struct lbs_private structure
+ * @param bssid The specific BSS to deauthenticate from
+ * @param reason The 802.11 sec. 7.3.1.7 Reason Code for deauthenticating
+ *
+ * @return 0 on success, error on failure
+ */
+int lbs_cmd_80211_deauthenticate(struct lbs_private *priv, u8 bssid[ETH_ALEN],
+ u16 reason)
+{
+ struct cmd_ds_802_11_deauthenticate cmd;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_JOIN);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+ memcpy(cmd.macaddr, &bssid[0], ETH_ALEN);
+ cmd.reasoncode = cpu_to_le16(reason);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd);
+
+ /* Clean up everything even if there was an error; can't assume that
+ * we're still authenticated to the AP after trying to deauth.
+ */
+ lbs_mac_event_disconnected(priv);
+
+ lbs_deb_leave(LBS_DEB_JOIN);
+ return ret;
+}
+