[bug 667] Fix the notorious "No space for Tx" bug.

We send SYSTEM_CONFIG command after the TGI_KEY command if hardware
encryption is enabled. It sometimes causes a firmware stall (firmware
doesn't respond to any request) and finally bungs up the Tx send queue.
The solution is to send SYSTEM_CONFIG command in the post association
stage from a workqueue.

Signed-off-by: James Ketrenos <jketreno@linux.intel.com>
This commit is contained in:
Zhu Yi 2005-07-13 12:25:38 -05:00 committed by James Ketrenos
parent f57ce7ce9c
commit d8bad6df04
2 changed files with 24 additions and 23 deletions

View File

@ -3589,6 +3589,12 @@ static void ipw_bg_disassociate(void *data)
up(&priv->sem); up(&priv->sem);
} }
static void ipw_system_config(void *data)
{
struct ipw_priv *priv = data;
ipw_send_system_config(priv, &priv->sys_config);
}
struct ipw_status_code { struct ipw_status_code {
u16 status; u16 status;
const char *reason; const char *reason;
@ -4060,6 +4066,8 @@ static inline void ipw_rx_notification(struct ipw_priv *priv,
priv->status &= ~STATUS_ASSOCIATING; priv->status &= ~STATUS_ASSOCIATING;
priv->status |= STATUS_ASSOCIATED; priv->status |= STATUS_ASSOCIATED;
queue_work(priv->workqueue,
&priv->system_config);
#ifdef CONFIG_IPW_QOS #ifdef CONFIG_IPW_QOS
#define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \ #define IPW_GET_PACKET_STYPE(x) WLAN_FC_GET_STYPE( \
@ -5553,45 +5561,36 @@ static void ipw_set_hwcrypto_keys(struct ipw_priv *priv)
{ {
switch (priv->ieee->sec.level) { switch (priv->ieee->sec.level) {
case SEC_LEVEL_3: case SEC_LEVEL_3:
if (!(priv->ieee->sec.flags & SEC_ACTIVE_KEY)) if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
break; ipw_send_tgi_tx_key(priv,
DCT_FLAG_EXT_SECURITY_CCM,
priv->ieee->sec.active_key);
ipw_send_tgi_tx_key(priv, DCT_FLAG_EXT_SECURITY_CCM,
priv->ieee->sec.active_key);
ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM); ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_CCM);
priv->sys_config.disable_unicast_decryption = 0; priv->sys_config.disable_unicast_decryption = 0;
priv->sys_config.disable_multicast_decryption = 0; priv->sys_config.disable_multicast_decryption = 0;
priv->ieee->host_decrypt = 0; priv->ieee->host_decrypt = 0;
if (ipw_send_system_config(priv, &priv->sys_config))
IPW_ERROR("ipw_send_system_config failed\n");
break; break;
case SEC_LEVEL_2: case SEC_LEVEL_2:
if (!(priv->ieee->sec.flags & SEC_ACTIVE_KEY)) if (priv->ieee->sec.flags & SEC_ACTIVE_KEY)
break; ipw_send_tgi_tx_key(priv,
DCT_FLAG_EXT_SECURITY_TKIP,
ipw_send_tgi_tx_key(priv, DCT_FLAG_EXT_SECURITY_TKIP, priv->ieee->sec.active_key);
priv->ieee->sec.active_key);
priv->sys_config.disable_unicast_decryption = 1; priv->sys_config.disable_unicast_decryption = 1;
priv->sys_config.disable_multicast_decryption = 1; priv->sys_config.disable_multicast_decryption = 1;
priv->ieee->host_decrypt = 1; priv->ieee->host_decrypt = 1;
if (ipw_send_system_config(priv, &priv->sys_config))
IPW_ERROR("ipw_send_system_config failed\n");
break; break;
case SEC_LEVEL_1: case SEC_LEVEL_1:
ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP); ipw_send_wep_keys(priv, DCW_WEP_KEY_SEC_TYPE_WEP);
priv->sys_config.disable_unicast_decryption = 0; priv->sys_config.disable_unicast_decryption = 0;
priv->sys_config.disable_multicast_decryption = 0; priv->sys_config.disable_multicast_decryption = 0;
priv->ieee->host_decrypt = 0; priv->ieee->host_decrypt = 0;
if (ipw_send_system_config(priv, &priv->sys_config))
IPW_ERROR("ipw_send_system_config failed\n");
break; break;
case SEC_LEVEL_0: case SEC_LEVEL_0:
priv->sys_config.disable_unicast_decryption = 1;
priv->sys_config.disable_multicast_decryption = 1;
break;
default: default:
break; break;
} }
@ -10113,6 +10112,7 @@ static int ipw_setup_deferred_work(struct ipw_priv *priv)
INIT_WORK(&priv->adhoc_check, ipw_bg_adhoc_check, priv); INIT_WORK(&priv->adhoc_check, ipw_bg_adhoc_check, priv);
INIT_WORK(&priv->associate, ipw_bg_associate, priv); INIT_WORK(&priv->associate, ipw_bg_associate, priv);
INIT_WORK(&priv->disassociate, ipw_bg_disassociate, priv); INIT_WORK(&priv->disassociate, ipw_bg_disassociate, priv);
INIT_WORK(&priv->system_config, ipw_system_config, priv);
INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish, priv); INIT_WORK(&priv->rx_replenish, ipw_bg_rx_queue_replenish, priv);
INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart, priv); INIT_WORK(&priv->adapter_restart, ipw_bg_adapter_restart, priv);
INIT_WORK(&priv->rf_kill, ipw_bg_rf_kill, priv); INIT_WORK(&priv->rf_kill, ipw_bg_rf_kill, priv);
@ -10206,10 +10206,10 @@ static void shim__set_security(struct net_device *dev,
priv->ieee->sec.level = sec->level; priv->ieee->sec.level = sec->level;
priv->ieee->sec.flags |= SEC_LEVEL; priv->ieee->sec.flags |= SEC_LEVEL;
priv->status |= STATUS_SECURITY_UPDATED; priv->status |= STATUS_SECURITY_UPDATED;
}
if (!priv->ieee->host_encrypt) if (!priv->ieee->host_encrypt && (sec->flags & SEC_ENCRYPT))
ipw_set_hwcrypto_keys(priv); ipw_set_hwcrypto_keys(priv);
}
/* To match current functionality of ipw2100 (which works well w/ /* To match current functionality of ipw2100 (which works well w/
* various supplicants, we don't force a disassociate if the * various supplicants, we don't force a disassociate if the

View File

@ -1205,6 +1205,7 @@ struct ipw_priv {
struct work_struct adhoc_check; struct work_struct adhoc_check;
struct work_struct associate; struct work_struct associate;
struct work_struct disassociate; struct work_struct disassociate;
struct work_struct system_config;
struct work_struct rx_replenish; struct work_struct rx_replenish;
struct work_struct request_scan; struct work_struct request_scan;
struct work_struct adapter_restart; struct work_struct adapter_restart;