aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/p54
diff options
context:
space:
mode:
authorChristian Lamparter <chunkeey@web.de>2009-07-16 14:03:47 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-07-24 15:05:21 -0400
commit46df10ae44b4488176bae16da0b31541eb0f8f48 (patch)
tree0f562347c429808ee8ba0ec151ea77189d5be744 /drivers/net/wireless/p54
parent12f49a79cd32d97a11f864a7b67660438ee3e6c8 (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.h4
-rw-r--r--drivers/net/wireless/p54/main.c31
-rw-r--r--drivers/net/wireless/p54/p54.h1
-rw-r--r--drivers/net/wireless/p54/txrx.c22
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 }