summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/wl12xx/wl1271_tx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/wl12xx/wl1271_tx.c')
-rw-r--r--drivers/net/wireless/wl12xx/wl1271_tx.c94
1 files changed, 49 insertions, 45 deletions
diff --git a/drivers/net/wireless/wl12xx/wl1271_tx.c b/drivers/net/wireless/wl12xx/wl1271_tx.c
index 811e739d05bf..6d109df9a0a0 100644
--- a/drivers/net/wireless/wl12xx/wl1271_tx.c
+++ b/drivers/net/wireless/wl12xx/wl1271_tx.c
@@ -25,7 +25,6 @@
#include <linux/module.h>
#include "wl1271.h"
-#include "wl1271_spi.h"
#include "wl1271_io.h"
#include "wl1271_reg.h"
#include "wl1271_ps.h"
@@ -47,7 +46,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra)
{
struct wl1271_tx_hw_descr *desc;
u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra;
- u32 total_blocks, excluded;
+ u32 total_blocks;
int id, ret = -EBUSY;
/* allocate free identifier for the packet */
@@ -57,12 +56,8 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra)
/* approximate the number of blocks required for this packet
in the firmware */
- /* FIXME: try to figure out what is done here and make it cleaner */
- total_blocks = (total_len + 20) >> TX_HW_BLOCK_SHIFT_DIV;
- excluded = (total_blocks << 2) + ((total_len + 20) & 0xff) + 34;
- total_blocks += (excluded > 252) ? 2 : 1;
- total_blocks += TX_HW_BLOCK_SPARE;
-
+ total_blocks = total_len + TX_HW_BLOCK_SIZE - 1;
+ total_blocks = total_blocks / TX_HW_BLOCK_SIZE + TX_HW_BLOCK_SPARE;
if (total_blocks <= wl->tx_blocks_available) {
desc = (struct wl1271_tx_hw_descr *)skb_push(
skb, total_len - skb->len);
@@ -87,8 +82,10 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra)
static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
u32 extra, struct ieee80211_tx_info *control)
{
+ struct timespec ts;
struct wl1271_tx_hw_descr *desc;
int pad, ac;
+ s64 hosttime;
u16 tx_attr;
desc = (struct wl1271_tx_hw_descr *) skb->data;
@@ -102,8 +99,9 @@ static int wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb,
}
/* configure packet life time */
- desc->start_time = cpu_to_le32(jiffies_to_usecs(jiffies) -
- wl->time_offset);
+ getnstimeofday(&ts);
+ hosttime = (timespec_to_ns(&ts) >> 10);
+ desc->start_time = cpu_to_le32(hosttime - wl->time_offset);
desc->life_time = cpu_to_le16(TX_HW_MGMT_PKT_LIFETIME_TU);
/* configure the tx attributes */
@@ -170,7 +168,6 @@ static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb,
/* write packet new counter into the write access register */
wl->tx_packets_count++;
- wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count);
desc = (struct wl1271_tx_hw_descr *) skb->data;
wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)",
@@ -245,6 +242,7 @@ void wl1271_tx_work(struct work_struct *work)
struct sk_buff *skb;
bool woken_up = false;
u32 sta_rates = 0;
+ u32 prev_tx_packets_count;
int ret;
/* check if the rates supported by the AP have changed */
@@ -261,6 +259,8 @@ void wl1271_tx_work(struct work_struct *work)
if (unlikely(wl->state == WL1271_STATE_OFF))
goto out;
+ prev_tx_packets_count = wl->tx_packets_count;
+
/* if rates have changed, re-configure the rate policy */
if (unlikely(sta_rates)) {
wl->rate_set = wl1271_tx_enabled_rates_get(wl, sta_rates);
@@ -271,31 +271,26 @@ void wl1271_tx_work(struct work_struct *work)
if (!woken_up) {
ret = wl1271_ps_elp_wakeup(wl, false);
if (ret < 0)
- goto out;
+ goto out_ack;
woken_up = true;
}
ret = wl1271_tx_frame(wl, skb);
if (ret == -EBUSY) {
- /* firmware buffer is full, stop queues */
- wl1271_debug(DEBUG_TX, "tx_work: fw buffer full, "
- "stop queues");
- ieee80211_stop_queues(wl->hw);
- set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
+ /* firmware buffer is full, lets stop transmitting. */
skb_queue_head(&wl->tx_queue, skb);
- goto out;
+ goto out_ack;
} else if (ret < 0) {
dev_kfree_skb(skb);
- goto out;
- } else if (test_and_clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED,
- &wl->flags)) {
- /* firmware buffer has space, restart queues */
- wl1271_debug(DEBUG_TX,
- "complete_packet: waking queues");
- ieee80211_wake_queues(wl->hw);
+ goto out_ack;
}
}
+out_ack:
+ /* interrupt the firmware with the new packets */
+ if (prev_tx_packets_count != wl->tx_packets_count)
+ wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count);
+
out:
if (woken_up)
wl1271_ps_elp_sleep(wl);
@@ -308,11 +303,10 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
{
struct ieee80211_tx_info *info;
struct sk_buff *skb;
- u16 seq;
int id = result->id;
/* check for id legality */
- if (id >= ACX_TX_DESCRIPTORS || wl->tx_frames[id] == NULL) {
+ if (unlikely(id >= ACX_TX_DESCRIPTORS || wl->tx_frames[id] == NULL)) {
wl1271_warning("TX result illegal id: %d", id);
return;
}
@@ -336,15 +330,10 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
wl->stats.retry_count += result->ack_failures;
/* update security sequence number */
- seq = wl->tx_security_seq_16 +
- (result->lsb_security_sequence_number -
- wl->tx_security_last_seq);
+ wl->tx_security_seq += (result->lsb_security_sequence_number -
+ wl->tx_security_last_seq);
wl->tx_security_last_seq = result->lsb_security_sequence_number;
- if (seq < wl->tx_security_seq_16)
- wl->tx_security_seq_32++;
- wl->tx_security_seq_16 = seq;
-
/* remove private header from packet */
skb_pull(skb, sizeof(struct wl1271_tx_hw_descr));
@@ -361,29 +350,37 @@ static void wl1271_tx_complete_packet(struct wl1271 *wl,
result->id, skb, result->ack_failures,
result->rate_class_index, result->status);
+ /* FIXME: do we need to tell the stack about the used rate? */
+
/* return the packet to the stack */
ieee80211_tx_status(wl->hw, skb);
wl->tx_frames[result->id] = NULL;
}
/* Called upon reception of a TX complete interrupt */
-void wl1271_tx_complete(struct wl1271 *wl, u32 count)
+void wl1271_tx_complete(struct wl1271 *wl)
{
struct wl1271_acx_mem_map *memmap =
(struct wl1271_acx_mem_map *)wl->target_mem_map;
+ u32 count, fw_counter;
u32 i;
- wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count);
-
/* read the tx results from the chipset */
wl1271_read(wl, le32_to_cpu(memmap->tx_result),
wl->tx_res_if, sizeof(*wl->tx_res_if), false);
+ fw_counter = le32_to_cpu(wl->tx_res_if->tx_result_fw_counter);
+
+ /* write host counter to chipset (to ack) */
+ wl1271_write32(wl, le32_to_cpu(memmap->tx_result) +
+ offsetof(struct wl1271_tx_hw_res_if,
+ tx_result_host_counter), fw_counter);
+
+ count = fw_counter - wl->tx_results_count;
+ wl1271_debug(DEBUG_TX, "tx_complete received, packets: %d", count);
/* verify that the result buffer is not getting overrun */
- if (count > TX_HW_RESULT_QUEUE_LEN) {
+ if (unlikely(count > TX_HW_RESULT_QUEUE_LEN))
wl1271_warning("TX result overflow from chipset: %d", count);
- count = TX_HW_RESULT_QUEUE_LEN;
- }
/* process the results */
for (i = 0; i < count; i++) {
@@ -397,11 +394,18 @@ void wl1271_tx_complete(struct wl1271 *wl, u32 count)
wl->tx_results_count++;
}
- /* write host counter to chipset (to ack) */
- wl1271_write32(wl, le32_to_cpu(memmap->tx_result) +
- offsetof(struct wl1271_tx_hw_res_if,
- tx_result_host_counter),
- le32_to_cpu(wl->tx_res_if->tx_result_fw_counter));
+ if (test_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags) &&
+ skb_queue_len(&wl->tx_queue) <= WL1271_TX_QUEUE_LOW_WATERMARK) {
+ unsigned long flags;
+
+ /* firmware buffer has space, restart queues */
+ wl1271_debug(DEBUG_TX, "tx_complete: waking queues");
+ spin_lock_irqsave(&wl->wl_lock, flags);
+ ieee80211_wake_queues(wl->hw);
+ clear_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags);
+ spin_unlock_irqrestore(&wl->wl_lock, flags);
+ ieee80211_queue_work(wl->hw, &wl->tx_work);
+ }
}
/* caller must hold wl->mutex */