diff options
author | Christian Lamparter <chunkeey@web.de> | 2009-07-16 14:03:47 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-07-24 15:05:21 -0400 |
commit | 46df10ae44b4488176bae16da0b31541eb0f8f48 (patch) | |
tree | 0f562347c429808ee8ba0ec151ea77189d5be744 /drivers/net/wireless/p54 | |
parent | 12f49a79cd32d97a11f864a7b67660438ee3e6c8 (diff) |
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>
Diffstat (limited to 'drivers/net/wireless/p54')
-rw-r--r-- | drivers/net/wireless/p54/lmac.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/p54/main.c | 31 | ||||
-rw-r--r-- | drivers/net/wireless/p54/p54.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/p54/txrx.c | 22 |
4 files changed, 38 insertions, 20 deletions
diff --git a/drivers/net/wireless/p54/lmac.h b/drivers/net/wireless/p54/lmac.h index af35cfcd4fe3..04b63ec80fa4 100644 --- a/drivers/net/wireless/p54/lmac.h +++ b/drivers/net/wireless/p54/lmac.h | |||
@@ -98,6 +98,10 @@ struct p54_hdr { | |||
98 | (!((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \ | 98 | (!((((struct p54_hdr *) ((struct sk_buff *) skb)->data)-> \ |
99 | flags) & cpu_to_le16(P54_HDR_FLAG_CONTROL))) | 99 | flags) & cpu_to_le16(P54_HDR_FLAG_CONTROL))) |
100 | 100 | ||
101 | #define GET_HW_QUEUE(skb) \ | ||
102 | (((struct p54_tx_data *)((struct p54_hdr *) \ | ||
103 | skb->data)->data)->hw_queue) | ||
104 | |||
101 | /* | 105 | /* |
102 | * shared interface ID definitions | 106 | * shared interface ID definitions |
103 | * The interface ID is a unique identification of a specific interface. | 107 | * The interface ID is a unique identification of a specific interface. |
diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index f19add2c3cac..955f6d7ec16a 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c | |||
@@ -130,7 +130,6 @@ static int p54_beacon_update(struct p54_common *priv, | |||
130 | struct ieee80211_vif *vif) | 130 | struct ieee80211_vif *vif) |
131 | { | 131 | { |
132 | struct sk_buff *beacon; | 132 | struct sk_buff *beacon; |
133 | __le32 old_beacon_req_id; | ||
134 | int ret; | 133 | int ret; |
135 | 134 | ||
136 | beacon = ieee80211_beacon_get(priv->hw, vif); | 135 | beacon = ieee80211_beacon_get(priv->hw, vif); |
@@ -140,15 +139,16 @@ static int p54_beacon_update(struct p54_common *priv, | |||
140 | if (ret) | 139 | if (ret) |
141 | return ret; | 140 | return ret; |
142 | 141 | ||
143 | old_beacon_req_id = priv->beacon_req_id; | 142 | /* |
144 | priv->beacon_req_id = GET_REQ_ID(beacon); | 143 | * During operation, the firmware takes care of beaconing. |
145 | 144 | * The driver only needs to upload a new beacon template, once | |
146 | ret = p54_tx_80211(priv->hw, beacon); | 145 | * the template was changed by the stack or userspace. |
147 | if (ret) { | 146 | * |
148 | priv->beacon_req_id = old_beacon_req_id; | 147 | * LMAC API 3.2.2 also specifies that the driver does not need |
149 | return -ENOSPC; | 148 | * to cancel the old beacon template by hand, instead the firmware |
150 | } | 149 | * will release the previous one through the feedback mechanism. |
151 | 150 | */ | |
151 | WARN_ON(p54_tx_80211(priv->hw, beacon)); | ||
152 | priv->tsf_high32 = 0; | 152 | priv->tsf_high32 = 0; |
153 | priv->tsf_low32 = 0; | 153 | priv->tsf_low32 = 0; |
154 | 154 | ||
@@ -253,9 +253,14 @@ static void p54_remove_interface(struct ieee80211_hw *dev, | |||
253 | 253 | ||
254 | mutex_lock(&priv->conf_mutex); | 254 | mutex_lock(&priv->conf_mutex); |
255 | priv->vif = NULL; | 255 | priv->vif = NULL; |
256 | if (priv->beacon_req_id) { | 256 | |
257 | /* | ||
258 | * LMAC API 3.2.2 states that any active beacon template must be | ||
259 | * canceled by the driver before attempting a mode transition. | ||
260 | */ | ||
261 | if (le32_to_cpu(priv->beacon_req_id) != 0) { | ||
257 | p54_tx_cancel(priv, priv->beacon_req_id); | 262 | p54_tx_cancel(priv, priv->beacon_req_id); |
258 | priv->beacon_req_id = cpu_to_le32(0); | 263 | wait_for_completion_interruptible_timeout(&priv->beacon_comp, HZ); |
259 | } | 264 | } |
260 | priv->mode = NL80211_IFTYPE_MONITOR; | 265 | priv->mode = NL80211_IFTYPE_MONITOR; |
261 | memset(priv->mac_addr, 0, ETH_ALEN); | 266 | memset(priv->mac_addr, 0, ETH_ALEN); |
@@ -544,6 +549,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) | |||
544 | BIT(NL80211_IFTYPE_MESH_POINT); | 549 | BIT(NL80211_IFTYPE_MESH_POINT); |
545 | 550 | ||
546 | dev->channel_change_time = 1000; /* TODO: find actual value */ | 551 | dev->channel_change_time = 1000; /* TODO: find actual value */ |
552 | priv->beacon_req_id = cpu_to_le32(0); | ||
547 | priv->tx_stats[P54_QUEUE_BEACON].limit = 1; | 553 | priv->tx_stats[P54_QUEUE_BEACON].limit = 1; |
548 | priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1; | 554 | priv->tx_stats[P54_QUEUE_FWSCAN].limit = 1; |
549 | priv->tx_stats[P54_QUEUE_MGMT].limit = 3; | 555 | priv->tx_stats[P54_QUEUE_MGMT].limit = 3; |
@@ -567,6 +573,7 @@ struct ieee80211_hw *p54_init_common(size_t priv_data_len) | |||
567 | mutex_init(&priv->conf_mutex); | 573 | mutex_init(&priv->conf_mutex); |
568 | mutex_init(&priv->eeprom_mutex); | 574 | mutex_init(&priv->eeprom_mutex); |
569 | init_completion(&priv->eeprom_comp); | 575 | init_completion(&priv->eeprom_comp); |
576 | init_completion(&priv->beacon_comp); | ||
570 | INIT_DELAYED_WORK(&priv->work, p54_work); | 577 | INIT_DELAYED_WORK(&priv->work, p54_work); |
571 | 578 | ||
572 | return dev; | 579 | return dev; |
diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h index 584b1560aff0..1afc39410e85 100644 --- a/drivers/net/wireless/p54/p54.h +++ b/drivers/net/wireless/p54/p54.h | |||
@@ -211,6 +211,7 @@ struct p54_common { | |||
211 | u16 aid; | 211 | u16 aid; |
212 | bool powersave_override; | 212 | bool powersave_override; |
213 | __le32 beacon_req_id; | 213 | __le32 beacon_req_id; |
214 | struct completion beacon_comp; | ||
214 | 215 | ||
215 | /* cryptographic engine information */ | 216 | /* cryptographic engine information */ |
216 | u8 privacy_caps; | 217 | u8 privacy_caps; |
diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 416400c4ad03..0d589d68e547 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c | |||
@@ -134,9 +134,13 @@ static int p54_assign_address(struct p54_common *priv, struct sk_buff *skb) | |||
134 | range = (void *) info->rate_driver_data; | 134 | range = (void *) info->rate_driver_data; |
135 | range->start_addr = target_addr; | 135 | range->start_addr = target_addr; |
136 | range->end_addr = target_addr + len; | 136 | range->end_addr = target_addr + len; |
137 | data->req_id = cpu_to_le32(target_addr + priv->headroom); | ||
138 | if (IS_DATA_FRAME(skb) && | ||
139 | unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) | ||
140 | priv->beacon_req_id = data->req_id; | ||
141 | |||
137 | __skb_queue_after(&priv->tx_queue, target_skb, skb); | 142 | __skb_queue_after(&priv->tx_queue, target_skb, skb); |
138 | spin_unlock_irqrestore(&priv->tx_queue.lock, flags); | 143 | spin_unlock_irqrestore(&priv->tx_queue.lock, flags); |
139 | data->req_id = cpu_to_le32(target_addr + priv->headroom); | ||
140 | return 0; | 144 | return 0; |
141 | } | 145 | } |
142 | 146 | ||
@@ -209,13 +213,19 @@ static void p54_tx_qos_accounting_free(struct p54_common *priv, | |||
209 | struct sk_buff *skb) | 213 | struct sk_buff *skb) |
210 | { | 214 | { |
211 | if (IS_DATA_FRAME(skb)) { | 215 | if (IS_DATA_FRAME(skb)) { |
212 | struct p54_hdr *hdr = (void *) skb->data; | ||
213 | struct p54_tx_data *data = (void *) hdr->data; | ||
214 | unsigned long flags; | 216 | unsigned long flags; |
215 | 217 | ||
216 | spin_lock_irqsave(&priv->tx_stats_lock, flags); | 218 | spin_lock_irqsave(&priv->tx_stats_lock, flags); |
217 | priv->tx_stats[data->hw_queue].len--; | 219 | priv->tx_stats[GET_HW_QUEUE(skb)].len--; |
218 | spin_unlock_irqrestore(&priv->tx_stats_lock, flags); | 220 | spin_unlock_irqrestore(&priv->tx_stats_lock, flags); |
221 | |||
222 | if (unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) { | ||
223 | if (priv->beacon_req_id == GET_REQ_ID(skb)) { | ||
224 | /* this is the active beacon set anymore */ | ||
225 | priv->beacon_req_id = 0; | ||
226 | } | ||
227 | complete(&priv->beacon_comp); | ||
228 | } | ||
219 | } | 229 | } |
220 | p54_wake_queues(priv); | 230 | p54_wake_queues(priv); |
221 | } | 231 | } |
@@ -403,10 +413,6 @@ static void p54_rx_frame_sent(struct p54_common *priv, struct sk_buff *skb) | |||
403 | * and we don't want to confuse the mac80211 stack. | 413 | * and we don't want to confuse the mac80211 stack. |
404 | */ | 414 | */ |
405 | if (unlikely(entry_data->hw_queue < P54_QUEUE_FWSCAN)) { | 415 | if (unlikely(entry_data->hw_queue < P54_QUEUE_FWSCAN)) { |
406 | if (entry_data->hw_queue == P54_QUEUE_BEACON && | ||
407 | hdr->req_id == priv->beacon_req_id) | ||
408 | priv->beacon_req_id = cpu_to_le32(0); | ||
409 | |||
410 | dev_kfree_skb_any(entry); | 416 | dev_kfree_skb_any(entry); |
411 | return ; | 417 | return ; |
412 | } | 418 | } |