summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/ti/wlcore/tx.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/ti/wlcore/tx.c')
-rw-r--r--drivers/net/wireless/ti/wlcore/tx.c112
1 files changed, 67 insertions, 45 deletions
diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c
index f0081f7..a90d3cd 100644
--- a/drivers/net/wireless/ti/wlcore/tx.c
+++ b/drivers/net/wireless/ti/wlcore/tx.c
@@ -130,16 +130,13 @@ bool wl12xx_is_dummy_packet(struct wl1271 *wl, struct sk_buff *skb)
}
EXPORT_SYMBOL(wl12xx_is_dummy_packet);
-u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- struct sk_buff *skb)
+static u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
+ struct sk_buff *skb, struct ieee80211_sta *sta)
{
- struct ieee80211_tx_info *control = IEEE80211_SKB_CB(skb);
-
- if (control->control.sta) {
+ if (sta) {
struct wl1271_station *wl_sta;
- wl_sta = (struct wl1271_station *)
- control->control.sta->drv_priv;
+ wl_sta = (struct wl1271_station *)sta->drv_priv;
return wl_sta->hlid;
} else {
struct ieee80211_hdr *hdr;
@@ -156,7 +153,7 @@ u8 wl12xx_tx_get_hlid_ap(struct wl1271 *wl, struct wl12xx_vif *wlvif,
}
u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- struct sk_buff *skb)
+ struct sk_buff *skb, struct ieee80211_sta *sta)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
@@ -164,7 +161,7 @@ u8 wl12xx_tx_get_hlid(struct wl1271 *wl, struct wl12xx_vif *wlvif,
return wl->system_hlid;
if (wlvif->bss_type == BSS_TYPE_AP_BSS)
- return wl12xx_tx_get_hlid_ap(wl, wlvif, skb);
+ return wl12xx_tx_get_hlid_ap(wl, wlvif, skb, sta);
if ((test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags) ||
test_bit(WLVIF_FLAG_IBSS_JOINED, &wlvif->flags)) &&
@@ -196,7 +193,7 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct wl12xx_vif *wlvif,
int id, ret = -EBUSY, ac;
u32 spare_blocks;
- if (buf_offset + total_len > WL1271_AGGR_BUFFER_SIZE)
+ if (buf_offset + total_len > wl->aggr_buf_size)
return -EAGAIN;
spare_blocks = wlcore_hw_get_spare_blocks(wl, is_gem);
@@ -322,8 +319,12 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
if (hlid == wlvif->ap.global_hlid)
rate_idx = wlvif->ap.mgmt_rate_idx;
else if (hlid == wlvif->ap.bcast_hlid ||
- skb->protocol == cpu_to_be16(ETH_P_PAE))
- /* send AP bcast and EAPOLs using the min basic rate */
+ skb->protocol == cpu_to_be16(ETH_P_PAE) ||
+ !ieee80211_is_data(frame_control))
+ /*
+ * send non-data, bcast and EAPOLs using the
+ * min basic rate
+ */
rate_idx = wlvif->ap.bcast_rate_idx;
else
rate_idx = wlvif->ap.ucast_rate_idx[ac];
@@ -344,13 +345,12 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct wl12xx_vif *wlvif,
/* caller must hold wl->mutex */
static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- struct sk_buff *skb, u32 buf_offset)
+ struct sk_buff *skb, u32 buf_offset, u8 hlid)
{
struct ieee80211_tx_info *info;
u32 extra = 0;
int ret = 0;
u32 total_len;
- u8 hlid;
bool is_dummy;
bool is_gem = false;
@@ -359,9 +359,13 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
return -EINVAL;
}
+ if (hlid == WL12XX_INVALID_LINK_ID) {
+ wl1271_error("invalid hlid. dropping skb 0x%p", skb);
+ return -EINVAL;
+ }
+
info = IEEE80211_SKB_CB(skb);
- /* TODO: handle dummy packets on multi-vifs */
is_dummy = wl12xx_is_dummy_packet(wl, skb);
if ((wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE) &&
@@ -386,11 +390,6 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct wl12xx_vif *wlvif,
is_gem = (cipher == WL1271_CIPHER_SUITE_GEM);
}
- hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
- if (hlid == WL12XX_INVALID_LINK_ID) {
- wl1271_error("invalid hlid. dropping skb 0x%p", skb);
- return -EINVAL;
- }
ret = wl1271_tx_allocate(wl, wlvif, skb, extra, buf_offset, hlid,
is_gem);
@@ -517,7 +516,8 @@ static struct sk_buff *wl12xx_lnk_skb_dequeue(struct wl1271 *wl,
}
static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
- struct wl12xx_vif *wlvif)
+ struct wl12xx_vif *wlvif,
+ u8 *hlid)
{
struct sk_buff *skb = NULL;
int i, h, start_hlid;
@@ -544,10 +544,11 @@ static struct sk_buff *wl12xx_vif_skb_dequeue(struct wl1271 *wl,
if (!skb)
wlvif->last_tx_hlid = 0;
+ *hlid = wlvif->last_tx_hlid;
return skb;
}
-static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
+static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl, u8 *hlid)
{
unsigned long flags;
struct wl12xx_vif *wlvif = wl->last_wlvif;
@@ -556,7 +557,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
/* continue from last wlvif (round robin) */
if (wlvif) {
wl12xx_for_each_wlvif_continue(wl, wlvif) {
- skb = wl12xx_vif_skb_dequeue(wl, wlvif);
+ skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
if (skb) {
wl->last_wlvif = wlvif;
break;
@@ -565,13 +566,15 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
}
/* dequeue from the system HLID before the restarting wlvif list */
- if (!skb)
+ if (!skb) {
skb = wl12xx_lnk_skb_dequeue(wl, &wl->links[wl->system_hlid]);
+ *hlid = wl->system_hlid;
+ }
/* do a new pass over the wlvif list */
if (!skb) {
wl12xx_for_each_wlvif(wl, wlvif) {
- skb = wl12xx_vif_skb_dequeue(wl, wlvif);
+ skb = wl12xx_vif_skb_dequeue(wl, wlvif, hlid);
if (skb) {
wl->last_wlvif = wlvif;
break;
@@ -591,6 +594,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
int q;
skb = wl->dummy_packet;
+ *hlid = wl->system_hlid;
q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
spin_lock_irqsave(&wl->wl_lock, flags);
WARN_ON_ONCE(wl->tx_queue_count[q] <= 0);
@@ -602,7 +606,7 @@ static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl)
}
static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
- struct sk_buff *skb)
+ struct sk_buff *skb, u8 hlid)
{
unsigned long flags;
int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb));
@@ -610,7 +614,6 @@ static void wl1271_skb_queue_head(struct wl1271 *wl, struct wl12xx_vif *wlvif,
if (wl12xx_is_dummy_packet(wl, skb)) {
set_bit(WL1271_FLAG_DUMMY_PACKET_PENDING, &wl->flags);
} else {
- u8 hlid = wl12xx_tx_get_hlid(wl, wlvif, skb);
skb_queue_head(&wl->links[hlid].tx_queue[q], skb);
/* make sure we dequeue the same packet next time */
@@ -686,26 +689,30 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
int ret = 0;
int bus_ret = 0;
+ u8 hlid;
- if (unlikely(wl->state == WL1271_STATE_OFF))
+ if (unlikely(wl->state != WLCORE_STATE_ON))
return 0;
- while ((skb = wl1271_skb_dequeue(wl))) {
+ while ((skb = wl1271_skb_dequeue(wl, &hlid))) {
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
bool has_data = false;
wlvif = NULL;
if (!wl12xx_is_dummy_packet(wl, skb) && info->control.vif)
wlvif = wl12xx_vif_to_data(info->control.vif);
+ else
+ hlid = wl->system_hlid;
has_data = wlvif && wl1271_tx_is_data_present(skb);
- ret = wl1271_prepare_tx_frame(wl, wlvif, skb, buf_offset);
+ ret = wl1271_prepare_tx_frame(wl, wlvif, skb, buf_offset,
+ hlid);
if (ret == -EAGAIN) {
/*
* Aggregation buffer is full.
* Flush buffer and try again.
*/
- wl1271_skb_queue_head(wl, wlvif, skb);
+ wl1271_skb_queue_head(wl, wlvif, skb, hlid);
buf_offset = wlcore_hw_pre_pkt_send(wl, buf_offset,
last_len);
@@ -722,7 +729,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
* Firmware buffer is full.
* Queue back last skb, and stop aggregating.
*/
- wl1271_skb_queue_head(wl, wlvif, skb);
+ wl1271_skb_queue_head(wl, wlvif, skb, hlid);
/* No work left, avoid scheduling redundant tx work */
set_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags);
goto out_ack;
@@ -732,7 +739,7 @@ int wlcore_tx_work_locked(struct wl1271 *wl)
* fw still expects dummy packet,
* so re-enqueue it
*/
- wl1271_skb_queue_head(wl, wlvif, skb);
+ wl1271_skb_queue_head(wl, wlvif, skb, hlid);
else
ieee80211_free_txskb(wl->hw, skb);
goto out_ack;
@@ -1069,39 +1076,54 @@ void wl12xx_tx_reset(struct wl1271 *wl)
/* caller must *NOT* hold wl->mutex */
void wl1271_tx_flush(struct wl1271 *wl)
{
- unsigned long timeout;
+ unsigned long timeout, start_time;
int i;
- timeout = jiffies + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT);
+ start_time = jiffies;
+ timeout = start_time + usecs_to_jiffies(WL1271_TX_FLUSH_TIMEOUT);
/* only one flush should be in progress, for consistent queue state */
mutex_lock(&wl->flush_mutex);
+ mutex_lock(&wl->mutex);
+ if (wl->tx_frames_cnt == 0 && wl1271_tx_total_queue_count(wl) == 0) {
+ mutex_unlock(&wl->mutex);
+ goto out;
+ }
+
wlcore_stop_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH);
while (!time_after(jiffies, timeout)) {
- mutex_lock(&wl->mutex);
- wl1271_debug(DEBUG_TX, "flushing tx buffer: %d %d",
+ wl1271_debug(DEBUG_MAC80211, "flushing tx buffer: %d %d",
wl->tx_frames_cnt,
wl1271_tx_total_queue_count(wl));
+
+ /* force Tx and give the driver some time to flush data */
+ mutex_unlock(&wl->mutex);
+ if (wl1271_tx_total_queue_count(wl))
+ wl1271_tx_work(&wl->tx_work);
+ msleep(20);
+ mutex_lock(&wl->mutex);
+
if ((wl->tx_frames_cnt == 0) &&
(wl1271_tx_total_queue_count(wl) == 0)) {
- mutex_unlock(&wl->mutex);
- goto out;
+ wl1271_debug(DEBUG_MAC80211, "tx flush took %d ms",
+ jiffies_to_msecs(jiffies - start_time));
+ goto out_wake;
}
- mutex_unlock(&wl->mutex);
- msleep(1);
}
- wl1271_warning("Unable to flush all TX buffers, timed out.");
+ wl1271_warning("Unable to flush all TX buffers, "
+ "timed out (timeout %d ms",
+ WL1271_TX_FLUSH_TIMEOUT / 1000);
/* forcibly flush all Tx buffers on our queues */
- mutex_lock(&wl->mutex);
for (i = 0; i < WL12XX_MAX_LINKS; i++)
wl1271_tx_reset_link_queues(wl, i);
- mutex_unlock(&wl->mutex);
-out:
+out_wake:
wlcore_wake_queues(wl, WLCORE_QUEUE_STOP_REASON_FLUSH);
+ mutex_unlock(&wl->mutex);
+out:
mutex_unlock(&wl->flush_mutex);
}
EXPORT_SYMBOL_GPL(wl1271_tx_flush);
OpenPOWER on IntegriCloud