Commit 24e5c401 authored by Emmanuel Grumbach's avatar Emmanuel Grumbach Committed by John W. Linville

iwlwifi: better station table maintenance

This patch makes the station table maintenance safer. Two flags are
maintained:

1) if station is present in driver
2) if station is present in uCode

This will allow us in the future to deal with more stations than the
firmware allows.
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: default avatarTomas Winkler <tomas.winkler@intel.com>
Signed-off-by: default avatarZhu Yi <yi.zhu@intel.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent f236a265
...@@ -284,6 +284,10 @@ void iwlcore_clear_stations_table(struct iwl_priv *priv) ...@@ -284,6 +284,10 @@ void iwlcore_clear_stations_table(struct iwl_priv *priv)
spin_lock_irqsave(&priv->sta_lock, flags); spin_lock_irqsave(&priv->sta_lock, flags);
priv->num_stations = 0; priv->num_stations = 0;
if (iwl_is_alive(priv) &&
iwl_send_cmd_pdu_async(priv, REPLY_REMOVE_ALL_STA, 0, NULL, NULL))
IWL_ERROR("Couldn't clear the station table\n");
memset(priv->stations, 0, sizeof(priv->stations)); memset(priv->stations, 0, sizeof(priv->stations));
spin_unlock_irqrestore(&priv->sta_lock, flags); spin_unlock_irqrestore(&priv->sta_lock, flags);
......
...@@ -36,8 +36,8 @@ ...@@ -36,8 +36,8 @@
#include "iwl-helpers.h" #include "iwl-helpers.h"
#define IWL_STA_DRIVER_ACTIVE 0x1 /* ucode entry is active */ #define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */
#define IWL_STA_UCODE_ACTIVE 0x2 /* ucode entry is active */ #define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */
u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr) u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr)
{ {
...@@ -83,10 +83,28 @@ int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) ...@@ -83,10 +83,28 @@ int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr)
} }
EXPORT_SYMBOL(iwl_get_ra_sta_id); EXPORT_SYMBOL(iwl_get_ra_sta_id);
static void iwl_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id)
{
unsigned long flags;
DECLARE_MAC_BUF(mac);
spin_lock_irqsave(&priv->sta_lock, flags);
if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE))
IWL_ERROR("ACTIVATE a non DRIVER active station %d\n", sta_id);
priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE;
IWL_DEBUG_ASSOC("Added STA to Ucode: %s\n",
print_mac(mac, priv->stations[sta_id].sta.sta.addr));
spin_unlock_irqrestore(&priv->sta_lock, flags);
}
static int iwl_add_sta_callback(struct iwl_priv *priv, static int iwl_add_sta_callback(struct iwl_priv *priv,
struct iwl_cmd *cmd, struct sk_buff *skb) struct iwl_cmd *cmd, struct sk_buff *skb)
{ {
struct iwl_rx_packet *res = NULL; struct iwl_rx_packet *res = NULL;
u8 sta_id = cmd->cmd.addsta.sta.sta_id;
if (!skb) { if (!skb) {
IWL_ERROR("Error: Response NULL in REPLY_ADD_STA.\n"); IWL_ERROR("Error: Response NULL in REPLY_ADD_STA.\n");
...@@ -102,8 +120,8 @@ static int iwl_add_sta_callback(struct iwl_priv *priv, ...@@ -102,8 +120,8 @@ static int iwl_add_sta_callback(struct iwl_priv *priv,
switch (res->u.add_sta.status) { switch (res->u.add_sta.status) {
case ADD_STA_SUCCESS_MSK: case ADD_STA_SUCCESS_MSK:
/* FIXME: implement iwl_sta_ucode_activate(priv, addr); */ iwl_sta_ucode_activate(priv, sta_id);
/* fail through */ /* fall through */
default: default:
IWL_DEBUG_HC("Received REPLY_ADD_STA:(0x%08X)\n", IWL_DEBUG_HC("Received REPLY_ADD_STA:(0x%08X)\n",
res->u.add_sta.status); res->u.add_sta.status);
...@@ -147,6 +165,7 @@ int iwl_send_add_sta(struct iwl_priv *priv, ...@@ -147,6 +165,7 @@ int iwl_send_add_sta(struct iwl_priv *priv,
if (ret == 0) { if (ret == 0) {
switch (res->u.add_sta.status) { switch (res->u.add_sta.status) {
case ADD_STA_SUCCESS_MSK: case ADD_STA_SUCCESS_MSK:
iwl_sta_ucode_activate(priv, sta->sta.sta_id);
IWL_DEBUG_INFO("REPLY_ADD_STA PASSED\n"); IWL_DEBUG_INFO("REPLY_ADD_STA PASSED\n");
break; break;
default: default:
...@@ -215,88 +234,92 @@ u8 iwl_add_station_flags(struct iwl_priv *priv, const u8 *addr, int is_ap, ...@@ -215,88 +234,92 @@ u8 iwl_add_station_flags(struct iwl_priv *priv, const u8 *addr, int is_ap,
u8 flags, struct ieee80211_ht_info *ht_info) u8 flags, struct ieee80211_ht_info *ht_info)
{ {
int i; int i;
int index = IWL_INVALID_STATION; int sta_id = IWL_INVALID_STATION;
struct iwl_station_entry *station; struct iwl_station_entry *station;
unsigned long flags_spin; unsigned long flags_spin;
DECLARE_MAC_BUF(mac); DECLARE_MAC_BUF(mac);
spin_lock_irqsave(&priv->sta_lock, flags_spin); spin_lock_irqsave(&priv->sta_lock, flags_spin);
if (is_ap) if (is_ap)
index = IWL_AP_ID; sta_id = IWL_AP_ID;
else if (is_broadcast_ether_addr(addr)) else if (is_broadcast_ether_addr(addr))
index = priv->hw_params.bcast_sta_id; sta_id = priv->hw_params.bcast_sta_id;
else else
for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++) { for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++) {
if (!compare_ether_addr(priv->stations[i].sta.sta.addr, if (!compare_ether_addr(priv->stations[i].sta.sta.addr,
addr)) { addr)) {
index = i; sta_id = i;
break; break;
} }
if (!priv->stations[i].used && if (!priv->stations[i].used &&
index == IWL_INVALID_STATION) sta_id == IWL_INVALID_STATION)
index = i; sta_id = i;
} }
/* These two conditions have the same outcome, but keep them separate /* These two conditions have the same outcome, but keep them separate
since they have different meanings */ since they have different meanings */
if (unlikely(index == IWL_INVALID_STATION)) { if (unlikely(sta_id == IWL_INVALID_STATION)) {
spin_unlock_irqrestore(&priv->sta_lock, flags_spin); spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
return index; return sta_id;
} }
if (priv->stations[index].used && if (priv->stations[sta_id].used &&
!compare_ether_addr(priv->stations[index].sta.sta.addr, addr)) { !compare_ether_addr(priv->stations[sta_id].sta.sta.addr, addr)) {
spin_unlock_irqrestore(&priv->sta_lock, flags_spin); spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
return index; return sta_id;
} }
IWL_DEBUG_ASSOC("Add STA ID %d: %s\n", index, print_mac(mac, addr)); station = &priv->stations[sta_id];
station = &priv->stations[index]; station->used = IWL_STA_DRIVER_ACTIVE;
station->used = 1; IWL_DEBUG_ASSOC("Add STA to driver ID %d: %s\n",
sta_id, print_mac(mac, addr));
priv->num_stations++; priv->num_stations++;
/* Set up the REPLY_ADD_STA command to send to device */ /* Set up the REPLY_ADD_STA command to send to device */
memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd)); memset(&station->sta, 0, sizeof(struct iwl_addsta_cmd));
memcpy(station->sta.sta.addr, addr, ETH_ALEN); memcpy(station->sta.sta.addr, addr, ETH_ALEN);
station->sta.mode = 0; station->sta.mode = 0;
station->sta.sta.sta_id = index; station->sta.sta.sta_id = sta_id;
station->sta.station_flags = 0; station->sta.station_flags = 0;
/* BCAST station and IBSS stations do not work in HT mode */ /* BCAST station and IBSS stations do not work in HT mode */
if (index != priv->hw_params.bcast_sta_id && if (sta_id != priv->hw_params.bcast_sta_id &&
priv->iw_mode != IEEE80211_IF_TYPE_IBSS) priv->iw_mode != IEEE80211_IF_TYPE_IBSS)
iwl_set_ht_add_station(priv, index, ht_info); iwl_set_ht_add_station(priv, sta_id, ht_info);
spin_unlock_irqrestore(&priv->sta_lock, flags_spin); spin_unlock_irqrestore(&priv->sta_lock, flags_spin);
/* Add station to device's station table */ /* Add station to device's station table */
iwl_send_add_sta(priv, &station->sta, flags); iwl_send_add_sta(priv, &station->sta, flags);
return index; return sta_id;
} }
EXPORT_SYMBOL(iwl_add_station_flags); EXPORT_SYMBOL(iwl_add_station_flags);
static int iwl_sta_ucode_deactivate(struct iwl_priv *priv, const char *addr) static void iwl_sta_ucode_deactivate(struct iwl_priv *priv, const char *addr)
{ {
unsigned long flags; unsigned long flags;
u8 sta_id;
DECLARE_MAC_BUF(mac); DECLARE_MAC_BUF(mac);
sta_id = iwl_find_station(priv, addr); u8 sta_id = iwl_find_station(priv, addr);
if (sta_id != IWL_INVALID_STATION) {
IWL_DEBUG_ASSOC("Removed STA from Ucode: %s\n", BUG_ON(sta_id == IWL_INVALID_STATION);
print_mac(mac, addr));
spin_lock_irqsave(&priv->sta_lock, flags); IWL_DEBUG_ASSOC("Removed STA from Ucode: %s\n",
priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; print_mac(mac, addr));
memset(&priv->stations[sta_id], 0,
sizeof(struct iwl_station_entry)); spin_lock_irqsave(&priv->sta_lock, flags);
spin_unlock_irqrestore(&priv->sta_lock, flags);
return 0; /* Ucode must be active and driver must be non active */
} if (priv->stations[sta_id].used != IWL_STA_UCODE_ACTIVE)
return -EINVAL; IWL_ERROR("removed non active STA %d\n", sta_id);
priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE;
memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry));
spin_unlock_irqrestore(&priv->sta_lock, flags);
} }
static int iwl_remove_sta_callback(struct iwl_priv *priv, static int iwl_remove_sta_callback(struct iwl_priv *priv,
...@@ -322,6 +345,7 @@ static int iwl_remove_sta_callback(struct iwl_priv *priv, ...@@ -322,6 +345,7 @@ static int iwl_remove_sta_callback(struct iwl_priv *priv,
iwl_sta_ucode_deactivate(priv, addr); iwl_sta_ucode_deactivate(priv, addr);
break; break;
default: default:
IWL_ERROR("REPLY_REMOVE_STA failed\n");
break; break;
} }
...@@ -386,44 +410,63 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr, ...@@ -386,44 +410,63 @@ static int iwl_send_remove_station(struct iwl_priv *priv, const u8 *addr,
/** /**
* iwl_remove_station - Remove driver's knowledge of station. * iwl_remove_station - Remove driver's knowledge of station.
*/ */
u8 iwl_remove_station(struct iwl_priv *priv, const u8 *addr, int is_ap) int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, int is_ap)
{ {
int index = IWL_INVALID_STATION; int sta_id = IWL_INVALID_STATION;
int i; int i, ret = -EINVAL;
unsigned long flags; unsigned long flags;
DECLARE_MAC_BUF(mac);
spin_lock_irqsave(&priv->sta_lock, flags); spin_lock_irqsave(&priv->sta_lock, flags);
if (is_ap) if (is_ap)
index = IWL_AP_ID; sta_id = IWL_AP_ID;
else if (is_broadcast_ether_addr(addr)) else if (is_broadcast_ether_addr(addr))
index = priv->hw_params.bcast_sta_id; sta_id = priv->hw_params.bcast_sta_id;
else else
for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++) for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++)
if (priv->stations[i].used && if (priv->stations[i].used &&
!compare_ether_addr(priv->stations[i].sta.sta.addr, !compare_ether_addr(priv->stations[i].sta.sta.addr,
addr)) { addr)) {
index = i; sta_id = i;
break; break;
} }
if (unlikely(index == IWL_INVALID_STATION)) if (unlikely(sta_id == IWL_INVALID_STATION))
goto out; goto out;
if (priv->stations[index].used) { IWL_DEBUG_ASSOC("Removing STA from driver:%d %s\n",
priv->stations[index].used = 0; sta_id, print_mac(mac, addr));
priv->num_stations--;
if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) {
IWL_ERROR("Removing %s but non DRIVER active\n",
print_mac(mac, addr));
goto out;
}
if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) {
IWL_ERROR("Removing %s but non UCODE active\n",
print_mac(mac, addr));
goto out;
} }
priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE;
priv->num_stations--;
BUG_ON(priv->num_stations < 0); BUG_ON(priv->num_stations < 0);
spin_unlock_irqrestore(&priv->sta_lock, flags); spin_unlock_irqrestore(&priv->sta_lock, flags);
iwl_send_remove_station(priv, addr, CMD_ASYNC);
return index; ret = iwl_send_remove_station(priv, addr, CMD_ASYNC);
return ret;
out: out:
spin_unlock_irqrestore(&priv->sta_lock, flags); spin_unlock_irqrestore(&priv->sta_lock, flags);
return 0; return ret;
} }
EXPORT_SYMBOL(iwl_remove_station); EXPORT_SYMBOL(iwl_remove_station);
static int iwl_get_free_ucode_key_index(struct iwl_priv *priv) static int iwl_get_free_ucode_key_index(struct iwl_priv *priv)
{ {
int i; int i;
...@@ -845,6 +888,7 @@ static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, int is_ap) ...@@ -845,6 +888,7 @@ static void iwl_sta_init_lq(struct iwl_priv *priv, const u8 *addr, int is_ap)
iwl_send_cmd_pdu_async(priv, REPLY_TX_LINK_QUALITY_CMD, iwl_send_cmd_pdu_async(priv, REPLY_TX_LINK_QUALITY_CMD,
sizeof(link_cmd), &link_cmd, NULL); sizeof(link_cmd), &link_cmd, NULL);
} }
/** /**
* iwl_rxon_add_station - add station into station table. * iwl_rxon_add_station - add station into station table.
* *
......
...@@ -48,7 +48,7 @@ int iwl_set_dynamic_key(struct iwl_priv *priv, ...@@ -48,7 +48,7 @@ int iwl_set_dynamic_key(struct iwl_priv *priv,
int iwl_remove_dynamic_key(struct iwl_priv *priv, int iwl_remove_dynamic_key(struct iwl_priv *priv,
struct ieee80211_key_conf *key, u8 sta_id); struct ieee80211_key_conf *key, u8 sta_id);
int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, int is_ap); int iwl_rxon_add_station(struct iwl_priv *priv, const u8 *addr, int is_ap);
u8 iwl_remove_station(struct iwl_priv *priv, const u8 *addr, int is_ap); int iwl_remove_station(struct iwl_priv *priv, const u8 *addr, int is_ap);
int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr); int iwl_get_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr);
void iwl_sta_modify_enable_tid_tx(struct iwl_priv *priv, int sta_id, int tid); void iwl_sta_modify_enable_tid_tx(struct iwl_priv *priv, int sta_id, int tid);
int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr); int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr);
......
...@@ -315,7 +315,6 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv) ...@@ -315,7 +315,6 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv)
return rc; return rc;
} }
iwl_remove_station(priv, iwl_bcast_addr, 0);
iwlcore_clear_stations_table(priv); iwlcore_clear_stations_table(priv);
if (!priv->error_recovering) if (!priv->error_recovering)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment