aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/wireless/p54/txrx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/p54/txrx.c')
-rw-r--r--drivers/net/wireless/p54/txrx.c80
1 files changed, 57 insertions, 23 deletions
diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c
index 6426d2cae6d..0d589d68e54 100644
--- a/drivers/net/wireless/p54/txrx.c
+++ b/drivers/net/wireless/p54/txrx.c
@@ -87,9 +87,6 @@ static int p54_assign_address(struct p54_common *priv, struct sk_buff *skb)
u32 target_addr = priv->rx_start;
u16 len = priv->headroom + skb->len + priv->tailroom + 3;
- if (unlikely(WARN_ON(!skb || !priv)))
- return -EINVAL;
-
info = IEEE80211_SKB_CB(skb);
range = (void *) info->rate_driver_data;
len = (range->extra_len + len) & ~0x3;
@@ -111,11 +108,6 @@ static int p54_assign_address(struct p54_common *priv, struct sk_buff *skb)
range = (void *) info->rate_driver_data;
hole_size = range->start_addr - last_addr;
- if (!entry->next) {
- spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
- return -ENOSPC;
- }
-
if (!target_skb && hole_size >= len) {
target_skb = entry->prev;
hole_size -= len;
@@ -142,9 +134,13 @@ static int p54_assign_address(struct p54_common *priv, struct sk_buff *skb)
range = (void *) info->rate_driver_data;
range->start_addr = target_addr;
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);
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
- data->req_id = cpu_to_le32(target_addr + priv->headroom);
return 0;
}
@@ -153,9 +149,6 @@ static void p54_tx_pending(struct p54_common *priv)
struct sk_buff *skb;
int ret;
- if (unlikely(WARN_ON(!priv)))
- return ;
-
skb = skb_dequeue(&priv->tx_pending);
if (unlikely(!skb))
return ;
@@ -219,14 +212,20 @@ static int p54_tx_qos_accounting_alloc(struct p54_common *priv,
static void p54_tx_qos_accounting_free(struct p54_common *priv,
struct sk_buff *skb)
{
- if (skb && IS_DATA_FRAME(skb)) {
- struct p54_hdr *hdr = (void *) skb->data;
- struct p54_tx_data *data = (void *) hdr->data;
+ if (IS_DATA_FRAME(skb)) {
unsigned long 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);
+
+ 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);
}
@@ -266,9 +265,6 @@ static struct sk_buff *p54_find_and_unlink_skb(struct p54_common *priv,
void p54_tx(struct p54_common *priv, struct sk_buff *skb)
{
- if (unlikely(WARN_ON(!priv)))
- return ;
-
skb_queue_tail(&priv->tx_pending, skb);
p54_tx_pending(priv);
}
@@ -288,6 +284,45 @@ static int p54_rssi_to_dbm(struct p54_common *priv, int rssi)
priv->rssical_db[band].add) / 4;
}
+/*
+ * Even if the firmware is capable of dealing with incoming traffic,
+ * while dozing, we have to prepared in case mac80211 uses PS-POLL
+ * to retrieve outstanding frames from our AP.
+ * (see comment in net/mac80211/mlme.c @ line 1993)
+ */
+static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb)
+{
+ struct ieee80211_hdr *hdr = (void *) skb->data;
+ struct ieee80211_tim_ie *tim_ie;
+ u8 *tim;
+ u8 tim_len;
+ bool new_psm;
+
+ /* only beacons have a TIM IE */
+ if (!ieee80211_is_beacon(hdr->frame_control))
+ return;
+
+ if (!priv->aid)
+ return;
+
+ /* only consider beacons from the associated BSSID */
+ if (compare_ether_addr(hdr->addr3, priv->bssid))
+ return;
+
+ tim = p54_find_ie(skb, WLAN_EID_TIM);
+ if (!tim)
+ return;
+
+ tim_len = tim[1];
+ tim_ie = (struct ieee80211_tim_ie *) &tim[2];
+
+ new_psm = ieee80211_check_tim(tim_ie, tim_len, priv->aid);
+ if (new_psm != priv->powersave_override) {
+ priv->powersave_override = new_psm;
+ p54_set_ps(priv);
+ }
+}
+
static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb)
{
struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data;
@@ -340,6 +375,9 @@ static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb)
skb_pull(skb, header_len);
skb_trim(skb, le16_to_cpu(hdr->len));
+ if (unlikely(priv->hw->conf.flags & IEEE80211_CONF_PS))
+ p54_pspoll_workaround(priv, skb);
+
ieee80211_rx_irqsafe(priv->hw, skb);
queue_delayed_work(priv->hw->workqueue, &priv->work,
@@ -375,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.
*/
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);
return ;
}