p54: fix beaconing related firmware crash

This patch fixes a firmware crash which can be provoked by changing
operation mode.

Acked-by: Larry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: Christian Lamparter <chunkeey@web.de>
Tested-by: Larry Finger <Larry.Finger@lwfinger.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Christian Lamparter 2009-07-16 20:03:47 +02:00 committed by John W. Linville
parent 12f49a79cd
commit 46df10ae44
4 changed files with 38 additions and 20 deletions

View file

@ -98,6 +98,10 @@ struct p54_hdr {
(!((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \ (!((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \
flags) & cpu_to_le16(P54_HDR_FLAG_CONTROL))) flags) & cpu_to_le16(P54_HDR_FLAG_CONTROL)))
#define GET_HW_QUEUE(skb) \
(((struct p54_tx_data *)((struct p54_hdr *) \
skb->data)->data)->hw_queue)
/* /*
* shared interface ID definitions * shared interface ID definitions
* The interface ID is a unique identification of a specific interface. * The interface ID is a unique identification of a specific interface.

View file

@ -130,7 +130,6 @@ static int p54_beacon_update(struct p54_common *priv,
struct ieee80211_vif *vif) struct ieee80211_vif *vif)
{ {
struct sk_buff *beacon; struct sk_buff *beacon;
__le32 old_beacon_req_id;
int ret; int ret;
beacon = ieee80211_beacon_get(priv->hw, vif); beacon = ieee80211_beacon_get(priv->hw, vif);
@ -140,15 +139,16 @@ static int p54_beacon_update(struct p54_common *priv,
if (ret) if (ret)
return ret; return ret;
old_beacon_req_id = priv->beacon_req_id; /*
priv->beacon_req_id = GET_REQ_ID(beacon); * During operation, the firmware takes care of beaconing.
* The driver only needs to upload a new beacon template, once
ret = p54_tx_80211(priv->hw, beacon); * the template was changed by the stack or userspace.
if (ret) { *
priv->beacon_req_id = old_beacon_req_id; * LMAC API 3.2.2 also specifies that the driver does not need
return -ENOSPC; * to cancel the old beacon template by hand, instead the firmware
} * will release the previous one through the feedback mechanism.
*/
WARN_ON(p54_tx_80211(priv->hw, beacon));
priv->tsf_high32 = 0; priv->tsf_high32 = 0;
priv->tsf_low32 = 0; priv->tsf_low32 = 0;
@ -253,9 +253,14 @@ static void p54_remove_interface(struct ieee80211_hw *dev,
mutex_lock(&priv->conf_mutex); mutex_lock(&priv->conf_mutex);
priv->vif = NULL; priv->vif = NULL;
if (priv->beacon_req_id) {
/*
* LMAC API 3.2.2 states that any active beacon template must be
* canceled by the driver before attempting a mode transition.
*/
if (le32_to_cpu(priv->beacon_req_id) != 0) {
p54_tx_cancel(priv, priv->beacon_req_id); p54_tx_cancel(priv, priv->beacon_req_id);
priv->beacon_req_id = cpu_to_le32(0); wait_for_completion_interruptible_timeout(&priv->beacon_comp, HZ);
} }
priv->mode = NL80211_IFTYPE_MONITOR; priv->mode = NL80211_IFTYPE_MONITOR;
memset(priv->mac_addr, 0, ETH_ALEN); memset(priv->mac_addr, 0, ETH_ALEN);
@ -544,6 +549,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
BIT(NL80211_IFTYPE_MESH_POINT); BIT(NL80211_IFTYPE_MESH_POINT);
dev->channel_change_time = 1000; /* TODO: find actual value */ dev->channel_change_time = 1000; /* TODO: find actual value */
priv->beacon_req_id = cpu_to_le32(0);
priv->tx_stats[P54_QUEUE_BEACON].limit = 1; priv->tx_stats[P54_QUEUE_BEACON].limit = 1;
priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1; priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1;
priv->tx_stats[P54_QUEUE_MGMT].limit = 3; priv->tx_stats[P54_QUEUE_MGMT].limit = 3;
@ -567,6 +573,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len)
mutex_init(&priv->conf_mutex); mutex_init(&priv->conf_mutex);
mutex_init(&priv->eeprom_mutex); mutex_init(&priv->eeprom_mutex);
init_completion(&priv->eeprom_comp); init_completion(&priv->eeprom_comp);
init_completion(&priv->beacon_comp);
INIT_DELAYED_WORK(&priv->work, p54_work); INIT_DELAYED_WORK(&priv->work, p54_work);
return dev; return dev;

View file

@ -211,6 +211,7 @@ struct p54_common {
u16 aid; u16 aid;
bool powersave_override; bool powersave_override;
__le32 beacon_req_id; __le32 beacon_req_id;
struct completion beacon_comp;
/* cryptographic engine information */ /* cryptographic engine information */
u8 privacy_caps; u8 privacy_caps;

View file

@ -134,9 +134,13 @@ static int p54_assign_address(struct p54_common *priv, struct sk_buff *skb)
range = (void *) info->rate_driver_data; range = (void *) info->rate_driver_data;
range->start_addr = target_addr; range->start_addr = target_addr;
range->end_addr = target_addr + len; range->end_addr = target_addr + len;
data->req_id = cpu_to_le32(target_addr + priv->headroom);
if (IS_DATA_FRAME(skb) &&
unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON))
priv->beacon_req_id = data->req_id;
__skb_queue_after(&priv->tx_queue, target_skb, skb); __skb_queue_after(&priv->tx_queue, target_skb, skb);
spin_unlock_irqrestore(&priv->tx_queue.lock, flags); spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
data->req_id = cpu_to_le32(target_addr + priv->headroom);
return 0; return 0;
} }
@ -209,13 +213,19 @@ static void p54_tx_qos_accounting_free(struct p54_common *priv,
struct sk_buff *skb) struct sk_buff *skb)
{ {
if (IS_DATA_FRAME(skb)) { if (IS_DATA_FRAME(skb)) {
struct p54_hdr *hdr = (void *) skb->data;
struct p54_tx_data *data = (void *) hdr->data;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&priv->tx_stats_lock, flags); spin_lock_irqsave(&priv->tx_stats_lock, flags);
priv->tx_stats[data->hw_queue].len--; priv->tx_stats[GET_HW_QUEUE(skb)].len--;
spin_unlock_irqrestore(&priv->tx_stats_lock, flags); spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
if (unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) {
if (priv->beacon_req_id == GET_REQ_ID(skb)) {
/* this is the active beacon set anymore */
priv->beacon_req_id = 0;
}
complete(&priv->beacon_comp);
}
} }
p54_wake_queues(priv); p54_wake_queues(priv);
} }
@ -403,10 +413,6 @@ static void p54_rx_frame_sent(struct p54_common *priv, struct sk_buff *skb)
* and we don't want to confuse the mac80211 stack. * and we don't want to confuse the mac80211 stack.
*/ */
if (unlikely(entry_data->hw_queue < P54_QUEUE_FWSCAN)) { if (unlikely(entry_data->hw_queue < P54_QUEUE_FWSCAN)) {
if (entry_data->hw_queue == P54_QUEUE_BEACON &&
hdr->req_id == priv->beacon_req_id)
priv->beacon_req_id = cpu_to_le32(0);
dev_kfree_skb_any(entry); dev_kfree_skb_any(entry);
return ; return ;
} }