12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
290921014SLuciano Coelho /*
390921014SLuciano Coelho * This file is part of wl1251
490921014SLuciano Coelho *
590921014SLuciano Coelho * Copyright (c) 1998-2007 Texas Instruments Incorporated
690921014SLuciano Coelho * Copyright (C) 2008 Nokia Corporation
790921014SLuciano Coelho */
890921014SLuciano Coelho
990921014SLuciano Coelho #include <linux/kernel.h>
1090921014SLuciano Coelho #include <linux/module.h>
1190921014SLuciano Coelho
1290921014SLuciano Coelho #include "wl1251.h"
1390921014SLuciano Coelho #include "reg.h"
1490921014SLuciano Coelho #include "tx.h"
1590921014SLuciano Coelho #include "ps.h"
1690921014SLuciano Coelho #include "io.h"
17c8909e5aSDavid Gnedt #include "event.h"
1890921014SLuciano Coelho
wl1251_tx_double_buffer_busy(struct wl1251 * wl,u32 data_out_count)1990921014SLuciano Coelho static bool wl1251_tx_double_buffer_busy(struct wl1251 *wl, u32 data_out_count)
2090921014SLuciano Coelho {
2190921014SLuciano Coelho int used, data_in_count;
2290921014SLuciano Coelho
2390921014SLuciano Coelho data_in_count = wl->data_in_count;
2490921014SLuciano Coelho
2590921014SLuciano Coelho if (data_in_count < data_out_count)
2690921014SLuciano Coelho /* data_in_count has wrapped */
2790921014SLuciano Coelho data_in_count += TX_STATUS_DATA_OUT_COUNT_MASK + 1;
2890921014SLuciano Coelho
2990921014SLuciano Coelho used = data_in_count - data_out_count;
3090921014SLuciano Coelho
3190921014SLuciano Coelho WARN_ON(used < 0);
3290921014SLuciano Coelho WARN_ON(used > DP_TX_PACKET_RING_CHUNK_NUM);
3390921014SLuciano Coelho
3490921014SLuciano Coelho if (used >= DP_TX_PACKET_RING_CHUNK_NUM)
3590921014SLuciano Coelho return true;
3690921014SLuciano Coelho else
3790921014SLuciano Coelho return false;
3890921014SLuciano Coelho }
3990921014SLuciano Coelho
wl1251_tx_path_status(struct wl1251 * wl)4090921014SLuciano Coelho static int wl1251_tx_path_status(struct wl1251 *wl)
4190921014SLuciano Coelho {
4290921014SLuciano Coelho u32 status, addr, data_out_count;
4390921014SLuciano Coelho bool busy;
4490921014SLuciano Coelho
4590921014SLuciano Coelho addr = wl->data_path->tx_control_addr;
4690921014SLuciano Coelho status = wl1251_mem_read32(wl, addr);
4790921014SLuciano Coelho data_out_count = status & TX_STATUS_DATA_OUT_COUNT_MASK;
4890921014SLuciano Coelho busy = wl1251_tx_double_buffer_busy(wl, data_out_count);
4990921014SLuciano Coelho
5090921014SLuciano Coelho if (busy)
5190921014SLuciano Coelho return -EBUSY;
5290921014SLuciano Coelho
5390921014SLuciano Coelho return 0;
5490921014SLuciano Coelho }
5590921014SLuciano Coelho
wl1251_tx_id(struct wl1251 * wl,struct sk_buff * skb)5690921014SLuciano Coelho static int wl1251_tx_id(struct wl1251 *wl, struct sk_buff *skb)
5790921014SLuciano Coelho {
5890921014SLuciano Coelho int i;
5990921014SLuciano Coelho
6090921014SLuciano Coelho for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
6190921014SLuciano Coelho if (wl->tx_frames[i] == NULL) {
6290921014SLuciano Coelho wl->tx_frames[i] = skb;
6390921014SLuciano Coelho return i;
6490921014SLuciano Coelho }
6590921014SLuciano Coelho
6690921014SLuciano Coelho return -EBUSY;
6790921014SLuciano Coelho }
6890921014SLuciano Coelho
wl1251_tx_control(struct tx_double_buffer_desc * tx_hdr,struct ieee80211_tx_info * control,u16 fc)6990921014SLuciano Coelho static void wl1251_tx_control(struct tx_double_buffer_desc *tx_hdr,
7090921014SLuciano Coelho struct ieee80211_tx_info *control, u16 fc)
7190921014SLuciano Coelho {
7290921014SLuciano Coelho *(u16 *)&tx_hdr->control = 0;
7390921014SLuciano Coelho
7490921014SLuciano Coelho tx_hdr->control.rate_policy = 0;
7590921014SLuciano Coelho
7690921014SLuciano Coelho /* 802.11 packets */
7790921014SLuciano Coelho tx_hdr->control.packet_type = 0;
7890921014SLuciano Coelho
793d49da74SDavid Gnedt /* Also disable retry and ACK policy for injected packets */
803d49da74SDavid Gnedt if ((control->flags & IEEE80211_TX_CTL_NO_ACK) ||
813d49da74SDavid Gnedt (control->flags & IEEE80211_TX_CTL_INJECTED)) {
823d49da74SDavid Gnedt tx_hdr->control.rate_policy = 1;
8390921014SLuciano Coelho tx_hdr->control.ack_policy = 1;
843d49da74SDavid Gnedt }
8590921014SLuciano Coelho
8690921014SLuciano Coelho tx_hdr->control.tx_complete = 1;
8790921014SLuciano Coelho
8890921014SLuciano Coelho if ((fc & IEEE80211_FTYPE_DATA) &&
8990921014SLuciano Coelho ((fc & IEEE80211_STYPE_QOS_DATA) ||
9090921014SLuciano Coelho (fc & IEEE80211_STYPE_QOS_NULLFUNC)))
9190921014SLuciano Coelho tx_hdr->control.qos = 1;
9290921014SLuciano Coelho }
9390921014SLuciano Coelho
9490921014SLuciano Coelho /* RSN + MIC = 8 + 8 = 16 bytes (worst case - AES). */
9590921014SLuciano Coelho #define MAX_MSDU_SECURITY_LENGTH 16
9690921014SLuciano Coelho #define MAX_MPDU_SECURITY_LENGTH 16
9790921014SLuciano Coelho #define WLAN_QOS_HDR_LEN 26
9890921014SLuciano Coelho #define MAX_MPDU_HEADER_AND_SECURITY (MAX_MPDU_SECURITY_LENGTH + \
9990921014SLuciano Coelho WLAN_QOS_HDR_LEN)
10090921014SLuciano Coelho #define HW_BLOCK_SIZE 252
wl1251_tx_frag_block_num(struct tx_double_buffer_desc * tx_hdr)10190921014SLuciano Coelho static void wl1251_tx_frag_block_num(struct tx_double_buffer_desc *tx_hdr)
10290921014SLuciano Coelho {
10390921014SLuciano Coelho u16 payload_len, frag_threshold, mem_blocks;
10490921014SLuciano Coelho u16 num_mpdus, mem_blocks_per_frag;
10590921014SLuciano Coelho
10690921014SLuciano Coelho frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
10790921014SLuciano Coelho tx_hdr->frag_threshold = cpu_to_le16(frag_threshold);
10890921014SLuciano Coelho
10990921014SLuciano Coelho payload_len = le16_to_cpu(tx_hdr->length) + MAX_MSDU_SECURITY_LENGTH;
11090921014SLuciano Coelho
11190921014SLuciano Coelho if (payload_len > frag_threshold) {
11290921014SLuciano Coelho mem_blocks_per_frag =
11390921014SLuciano Coelho ((frag_threshold + MAX_MPDU_HEADER_AND_SECURITY) /
11490921014SLuciano Coelho HW_BLOCK_SIZE) + 1;
11590921014SLuciano Coelho num_mpdus = payload_len / frag_threshold;
11690921014SLuciano Coelho mem_blocks = num_mpdus * mem_blocks_per_frag;
11790921014SLuciano Coelho payload_len -= num_mpdus * frag_threshold;
11890921014SLuciano Coelho num_mpdus++;
11990921014SLuciano Coelho
12090921014SLuciano Coelho } else {
12190921014SLuciano Coelho mem_blocks_per_frag = 0;
12290921014SLuciano Coelho mem_blocks = 0;
12390921014SLuciano Coelho num_mpdus = 1;
12490921014SLuciano Coelho }
12590921014SLuciano Coelho
12690921014SLuciano Coelho mem_blocks += (payload_len / HW_BLOCK_SIZE) + 1;
12790921014SLuciano Coelho
12890921014SLuciano Coelho if (num_mpdus > 1)
12990921014SLuciano Coelho mem_blocks += min(num_mpdus, mem_blocks_per_frag);
13090921014SLuciano Coelho
13190921014SLuciano Coelho tx_hdr->num_mem_blocks = mem_blocks;
13290921014SLuciano Coelho }
13390921014SLuciano Coelho
wl1251_tx_fill_hdr(struct wl1251 * wl,struct sk_buff * skb,struct ieee80211_tx_info * control)13490921014SLuciano Coelho static int wl1251_tx_fill_hdr(struct wl1251 *wl, struct sk_buff *skb,
13590921014SLuciano Coelho struct ieee80211_tx_info *control)
13690921014SLuciano Coelho {
13790921014SLuciano Coelho struct tx_double_buffer_desc *tx_hdr;
13890921014SLuciano Coelho struct ieee80211_rate *rate;
13990921014SLuciano Coelho int id;
14090921014SLuciano Coelho u16 fc;
14190921014SLuciano Coelho
14290921014SLuciano Coelho if (!skb)
14390921014SLuciano Coelho return -EINVAL;
14490921014SLuciano Coelho
14590921014SLuciano Coelho id = wl1251_tx_id(wl, skb);
14690921014SLuciano Coelho if (id < 0)
14790921014SLuciano Coelho return id;
14890921014SLuciano Coelho
14990921014SLuciano Coelho fc = *(u16 *)skb->data;
150d58ff351SJohannes Berg tx_hdr = skb_push(skb, sizeof(*tx_hdr));
15190921014SLuciano Coelho
15290921014SLuciano Coelho tx_hdr->length = cpu_to_le16(skb->len - sizeof(*tx_hdr));
15390921014SLuciano Coelho rate = ieee80211_get_tx_rate(wl->hw, control);
15490921014SLuciano Coelho tx_hdr->rate = cpu_to_le16(rate->hw_value);
15590921014SLuciano Coelho tx_hdr->expiry_time = cpu_to_le32(1 << 16);
15690921014SLuciano Coelho tx_hdr->id = id;
15790921014SLuciano Coelho
15890921014SLuciano Coelho tx_hdr->xmit_queue = wl1251_tx_get_queue(skb_get_queue_mapping(skb));
15990921014SLuciano Coelho
16090921014SLuciano Coelho wl1251_tx_control(tx_hdr, control, fc);
16190921014SLuciano Coelho wl1251_tx_frag_block_num(tx_hdr);
16290921014SLuciano Coelho
16390921014SLuciano Coelho return 0;
16490921014SLuciano Coelho }
16590921014SLuciano Coelho
16690921014SLuciano Coelho /* We copy the packet to the target */
wl1251_tx_send_packet(struct wl1251 * wl,struct sk_buff * skb,struct ieee80211_tx_info * control)16790921014SLuciano Coelho static int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb,
16890921014SLuciano Coelho struct ieee80211_tx_info *control)
16990921014SLuciano Coelho {
17090921014SLuciano Coelho struct tx_double_buffer_desc *tx_hdr;
17190921014SLuciano Coelho int len;
17290921014SLuciano Coelho u32 addr;
17390921014SLuciano Coelho
17490921014SLuciano Coelho if (!skb)
17590921014SLuciano Coelho return -EINVAL;
17690921014SLuciano Coelho
17790921014SLuciano Coelho tx_hdr = (struct tx_double_buffer_desc *) skb->data;
17890921014SLuciano Coelho
17990921014SLuciano Coelho if (control->control.hw_key &&
18090921014SLuciano Coelho control->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
18190921014SLuciano Coelho int hdrlen;
18290921014SLuciano Coelho __le16 fc;
18390921014SLuciano Coelho u16 length;
18490921014SLuciano Coelho u8 *pos;
18590921014SLuciano Coelho
18690921014SLuciano Coelho fc = *(__le16 *)(skb->data + sizeof(*tx_hdr));
18790921014SLuciano Coelho length = le16_to_cpu(tx_hdr->length) + WL1251_TKIP_IV_SPACE;
18890921014SLuciano Coelho tx_hdr->length = cpu_to_le16(length);
18990921014SLuciano Coelho
19090921014SLuciano Coelho hdrlen = ieee80211_hdrlen(fc);
19190921014SLuciano Coelho
19290921014SLuciano Coelho pos = skb_push(skb, WL1251_TKIP_IV_SPACE);
19390921014SLuciano Coelho memmove(pos, pos + WL1251_TKIP_IV_SPACE,
19490921014SLuciano Coelho sizeof(*tx_hdr) + hdrlen);
19590921014SLuciano Coelho }
19690921014SLuciano Coelho
19790921014SLuciano Coelho /* Revisit. This is a workaround for getting non-aligned packets.
19890921014SLuciano Coelho This happens at least with EAPOL packets from the user space.
19990921014SLuciano Coelho Our DMA requires packets to be aligned on a 4-byte boundary.
20090921014SLuciano Coelho */
20190921014SLuciano Coelho if (unlikely((long)skb->data & 0x03)) {
20290921014SLuciano Coelho int offset = (4 - (long)skb->data) & 0x03;
20390921014SLuciano Coelho wl1251_debug(DEBUG_TX, "skb offset %d", offset);
20490921014SLuciano Coelho
20590921014SLuciano Coelho /* check whether the current skb can be used */
20690921014SLuciano Coelho if (skb_cloned(skb) || (skb_tailroom(skb) < offset)) {
20790921014SLuciano Coelho struct sk_buff *newskb = skb_copy_expand(skb, 0, 3,
20890921014SLuciano Coelho GFP_KERNEL);
20990921014SLuciano Coelho
2100c3d5a96SJoe Perches if (unlikely(newskb == NULL))
21190921014SLuciano Coelho return -EINVAL;
21290921014SLuciano Coelho
21390921014SLuciano Coelho tx_hdr = (struct tx_double_buffer_desc *) newskb->data;
21490921014SLuciano Coelho
21590921014SLuciano Coelho dev_kfree_skb_any(skb);
21690921014SLuciano Coelho wl->tx_frames[tx_hdr->id] = skb = newskb;
21790921014SLuciano Coelho
21890921014SLuciano Coelho offset = (4 - (long)skb->data) & 0x03;
21990921014SLuciano Coelho wl1251_debug(DEBUG_TX, "new skb offset %d", offset);
22090921014SLuciano Coelho }
22190921014SLuciano Coelho
22290921014SLuciano Coelho /* align the buffer on a 4-byte boundary */
22390921014SLuciano Coelho if (offset) {
22490921014SLuciano Coelho unsigned char *src = skb->data;
22590921014SLuciano Coelho skb_reserve(skb, offset);
22690921014SLuciano Coelho memmove(skb->data, src, skb->len);
22790921014SLuciano Coelho tx_hdr = (struct tx_double_buffer_desc *) skb->data;
22890921014SLuciano Coelho }
22990921014SLuciano Coelho }
23090921014SLuciano Coelho
23190921014SLuciano Coelho /* Our skb->data at this point includes the HW header */
23290921014SLuciano Coelho len = WL1251_TX_ALIGN(skb->len);
23390921014SLuciano Coelho
23490921014SLuciano Coelho if (wl->data_in_count & 0x1)
23590921014SLuciano Coelho addr = wl->data_path->tx_packet_ring_addr +
23690921014SLuciano Coelho wl->data_path->tx_packet_ring_chunk_size;
23790921014SLuciano Coelho else
23890921014SLuciano Coelho addr = wl->data_path->tx_packet_ring_addr;
23990921014SLuciano Coelho
24090921014SLuciano Coelho wl1251_mem_write(wl, addr, skb->data, len);
24190921014SLuciano Coelho
24290921014SLuciano Coelho wl1251_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x "
24390921014SLuciano Coelho "queue %d", tx_hdr->id, skb, tx_hdr->length,
24490921014SLuciano Coelho tx_hdr->rate, tx_hdr->xmit_queue);
24590921014SLuciano Coelho
24690921014SLuciano Coelho return 0;
24790921014SLuciano Coelho }
24890921014SLuciano Coelho
wl1251_tx_trigger(struct wl1251 * wl)24990921014SLuciano Coelho static void wl1251_tx_trigger(struct wl1251 *wl)
25090921014SLuciano Coelho {
25190921014SLuciano Coelho u32 data, addr;
25290921014SLuciano Coelho
25390921014SLuciano Coelho if (wl->data_in_count & 0x1) {
25490921014SLuciano Coelho addr = ACX_REG_INTERRUPT_TRIG_H;
25590921014SLuciano Coelho data = INTR_TRIG_TX_PROC1;
25690921014SLuciano Coelho } else {
25790921014SLuciano Coelho addr = ACX_REG_INTERRUPT_TRIG;
25890921014SLuciano Coelho data = INTR_TRIG_TX_PROC0;
25990921014SLuciano Coelho }
26090921014SLuciano Coelho
26190921014SLuciano Coelho wl1251_reg_write32(wl, addr, data);
26290921014SLuciano Coelho
26390921014SLuciano Coelho /* Bumping data in */
26490921014SLuciano Coelho wl->data_in_count = (wl->data_in_count + 1) &
26590921014SLuciano Coelho TX_STATUS_DATA_OUT_COUNT_MASK;
26690921014SLuciano Coelho }
26790921014SLuciano Coelho
enable_tx_for_packet_injection(struct wl1251 * wl)268c8909e5aSDavid Gnedt static void enable_tx_for_packet_injection(struct wl1251 *wl)
269c8909e5aSDavid Gnedt {
270c8909e5aSDavid Gnedt int ret;
271c8909e5aSDavid Gnedt
272c8909e5aSDavid Gnedt ret = wl1251_cmd_join(wl, BSS_TYPE_STA_BSS, wl->channel,
273c8909e5aSDavid Gnedt wl->beacon_int, wl->dtim_period);
274c8909e5aSDavid Gnedt if (ret < 0) {
275c8909e5aSDavid Gnedt wl1251_warning("join failed");
276c8909e5aSDavid Gnedt return;
277c8909e5aSDavid Gnedt }
278c8909e5aSDavid Gnedt
279c8909e5aSDavid Gnedt ret = wl1251_event_wait(wl, JOIN_EVENT_COMPLETE_ID, 100);
280c8909e5aSDavid Gnedt if (ret < 0) {
281c8909e5aSDavid Gnedt wl1251_warning("join timeout");
282c8909e5aSDavid Gnedt return;
283c8909e5aSDavid Gnedt }
284c8909e5aSDavid Gnedt
285c8909e5aSDavid Gnedt wl->joined = true;
286c8909e5aSDavid Gnedt }
287c8909e5aSDavid Gnedt
28890921014SLuciano Coelho /* caller must hold wl->mutex */
wl1251_tx_frame(struct wl1251 * wl,struct sk_buff * skb)28990921014SLuciano Coelho static int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb)
29090921014SLuciano Coelho {
29190921014SLuciano Coelho struct ieee80211_tx_info *info;
29290921014SLuciano Coelho int ret = 0;
29390921014SLuciano Coelho u8 idx;
29490921014SLuciano Coelho
29590921014SLuciano Coelho info = IEEE80211_SKB_CB(skb);
29690921014SLuciano Coelho
29790921014SLuciano Coelho if (info->control.hw_key) {
2984d09b537SDavid Gnedt if (unlikely(wl->monitor_present))
2994d09b537SDavid Gnedt return -EINVAL;
3004d09b537SDavid Gnedt
30190921014SLuciano Coelho idx = info->control.hw_key->hw_key_idx;
30290921014SLuciano Coelho if (unlikely(wl->default_key != idx)) {
30390921014SLuciano Coelho ret = wl1251_acx_default_key(wl, idx);
30490921014SLuciano Coelho if (ret < 0)
30590921014SLuciano Coelho return ret;
30690921014SLuciano Coelho }
30790921014SLuciano Coelho }
30890921014SLuciano Coelho
309c8909e5aSDavid Gnedt /* Enable tx path in monitor mode for packet injection */
310c8909e5aSDavid Gnedt if ((wl->vif == NULL) && !wl->joined)
311c8909e5aSDavid Gnedt enable_tx_for_packet_injection(wl);
312c8909e5aSDavid Gnedt
31390921014SLuciano Coelho ret = wl1251_tx_path_status(wl);
31490921014SLuciano Coelho if (ret < 0)
31590921014SLuciano Coelho return ret;
31690921014SLuciano Coelho
31790921014SLuciano Coelho ret = wl1251_tx_fill_hdr(wl, skb, info);
31890921014SLuciano Coelho if (ret < 0)
31990921014SLuciano Coelho return ret;
32090921014SLuciano Coelho
32190921014SLuciano Coelho ret = wl1251_tx_send_packet(wl, skb, info);
32290921014SLuciano Coelho if (ret < 0)
32390921014SLuciano Coelho return ret;
32490921014SLuciano Coelho
32590921014SLuciano Coelho wl1251_tx_trigger(wl);
32690921014SLuciano Coelho
32790921014SLuciano Coelho return ret;
32890921014SLuciano Coelho }
32990921014SLuciano Coelho
wl1251_tx_work(struct work_struct * work)33090921014SLuciano Coelho void wl1251_tx_work(struct work_struct *work)
33190921014SLuciano Coelho {
33290921014SLuciano Coelho struct wl1251 *wl = container_of(work, struct wl1251, tx_work);
33390921014SLuciano Coelho struct sk_buff *skb;
33490921014SLuciano Coelho bool woken_up = false;
33590921014SLuciano Coelho int ret;
33690921014SLuciano Coelho
33790921014SLuciano Coelho mutex_lock(&wl->mutex);
33890921014SLuciano Coelho
33990921014SLuciano Coelho if (unlikely(wl->state == WL1251_STATE_OFF))
34090921014SLuciano Coelho goto out;
34190921014SLuciano Coelho
34290921014SLuciano Coelho while ((skb = skb_dequeue(&wl->tx_queue))) {
34390921014SLuciano Coelho if (!woken_up) {
34490921014SLuciano Coelho ret = wl1251_ps_elp_wakeup(wl);
34590921014SLuciano Coelho if (ret < 0)
34690921014SLuciano Coelho goto out;
34790921014SLuciano Coelho woken_up = true;
34890921014SLuciano Coelho }
34990921014SLuciano Coelho
35090921014SLuciano Coelho ret = wl1251_tx_frame(wl, skb);
35190921014SLuciano Coelho if (ret == -EBUSY) {
35290921014SLuciano Coelho skb_queue_head(&wl->tx_queue, skb);
35390921014SLuciano Coelho goto out;
35490921014SLuciano Coelho } else if (ret < 0) {
35590921014SLuciano Coelho dev_kfree_skb(skb);
35690921014SLuciano Coelho goto out;
35790921014SLuciano Coelho }
35890921014SLuciano Coelho }
35990921014SLuciano Coelho
36090921014SLuciano Coelho out:
36190921014SLuciano Coelho if (woken_up)
36290921014SLuciano Coelho wl1251_ps_elp_sleep(wl);
36390921014SLuciano Coelho
36490921014SLuciano Coelho mutex_unlock(&wl->mutex);
36590921014SLuciano Coelho }
36690921014SLuciano Coelho
wl1251_tx_parse_status(u8 status)36790921014SLuciano Coelho static const char *wl1251_tx_parse_status(u8 status)
36890921014SLuciano Coelho {
36990921014SLuciano Coelho /* 8 bit status field, one character per bit plus null */
37090921014SLuciano Coelho static char buf[9];
37190921014SLuciano Coelho int i = 0;
37290921014SLuciano Coelho
37390921014SLuciano Coelho memset(buf, 0, sizeof(buf));
37490921014SLuciano Coelho
37590921014SLuciano Coelho if (status & TX_DMA_ERROR)
37690921014SLuciano Coelho buf[i++] = 'm';
37790921014SLuciano Coelho if (status & TX_DISABLED)
37890921014SLuciano Coelho buf[i++] = 'd';
37990921014SLuciano Coelho if (status & TX_RETRY_EXCEEDED)
38090921014SLuciano Coelho buf[i++] = 'r';
38190921014SLuciano Coelho if (status & TX_TIMEOUT)
38290921014SLuciano Coelho buf[i++] = 't';
38390921014SLuciano Coelho if (status & TX_KEY_NOT_FOUND)
38490921014SLuciano Coelho buf[i++] = 'k';
38590921014SLuciano Coelho if (status & TX_ENCRYPT_FAIL)
38690921014SLuciano Coelho buf[i++] = 'e';
38790921014SLuciano Coelho if (status & TX_UNAVAILABLE_PRIORITY)
38890921014SLuciano Coelho buf[i++] = 'p';
38990921014SLuciano Coelho
39090921014SLuciano Coelho /* bit 0 is unused apparently */
39190921014SLuciano Coelho
39290921014SLuciano Coelho return buf;
39390921014SLuciano Coelho }
39490921014SLuciano Coelho
wl1251_tx_packet_cb(struct wl1251 * wl,struct tx_result * result)39590921014SLuciano Coelho static void wl1251_tx_packet_cb(struct wl1251 *wl,
39690921014SLuciano Coelho struct tx_result *result)
39790921014SLuciano Coelho {
39890921014SLuciano Coelho struct ieee80211_tx_info *info;
39990921014SLuciano Coelho struct sk_buff *skb;
40090921014SLuciano Coelho int hdrlen;
40190921014SLuciano Coelho u8 *frame;
40290921014SLuciano Coelho
40390921014SLuciano Coelho skb = wl->tx_frames[result->id];
40490921014SLuciano Coelho if (skb == NULL) {
40590921014SLuciano Coelho wl1251_error("SKB for packet %d is NULL", result->id);
40690921014SLuciano Coelho return;
40790921014SLuciano Coelho }
40890921014SLuciano Coelho
40990921014SLuciano Coelho info = IEEE80211_SKB_CB(skb);
41090921014SLuciano Coelho
41190921014SLuciano Coelho if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
4123d49da74SDavid Gnedt !(info->flags & IEEE80211_TX_CTL_INJECTED) &&
41390921014SLuciano Coelho (result->status == TX_SUCCESS))
41490921014SLuciano Coelho info->flags |= IEEE80211_TX_STAT_ACK;
41590921014SLuciano Coelho
41690921014SLuciano Coelho info->status.rates[0].count = result->ack_failures + 1;
41790921014SLuciano Coelho wl->stats.retry_count += result->ack_failures;
41890921014SLuciano Coelho
41990921014SLuciano Coelho /*
42090921014SLuciano Coelho * We have to remove our private TX header before pushing
42190921014SLuciano Coelho * the skb back to mac80211.
42290921014SLuciano Coelho */
42390921014SLuciano Coelho frame = skb_pull(skb, sizeof(struct tx_double_buffer_desc));
42490921014SLuciano Coelho if (info->control.hw_key &&
42590921014SLuciano Coelho info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
42690921014SLuciano Coelho hdrlen = ieee80211_get_hdrlen_from_skb(skb);
42790921014SLuciano Coelho memmove(frame + WL1251_TKIP_IV_SPACE, frame, hdrlen);
42890921014SLuciano Coelho skb_pull(skb, WL1251_TKIP_IV_SPACE);
42990921014SLuciano Coelho }
43090921014SLuciano Coelho
43190921014SLuciano Coelho wl1251_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x"
43290921014SLuciano Coelho " status 0x%x (%s)",
43390921014SLuciano Coelho result->id, skb, result->ack_failures, result->rate,
43490921014SLuciano Coelho result->status, wl1251_tx_parse_status(result->status));
43590921014SLuciano Coelho
43690921014SLuciano Coelho
43790921014SLuciano Coelho ieee80211_tx_status(wl->hw, skb);
43890921014SLuciano Coelho
43990921014SLuciano Coelho wl->tx_frames[result->id] = NULL;
44090921014SLuciano Coelho }
44190921014SLuciano Coelho
44290921014SLuciano Coelho /* Called upon reception of a TX complete interrupt */
wl1251_tx_complete(struct wl1251 * wl)44390921014SLuciano Coelho void wl1251_tx_complete(struct wl1251 *wl)
44490921014SLuciano Coelho {
44590921014SLuciano Coelho int i, result_index, num_complete = 0, queue_len;
446*45474475SH. Nikolaus Schaller struct tx_result *result, *result_ptr;
44790921014SLuciano Coelho unsigned long flags;
44890921014SLuciano Coelho
44990921014SLuciano Coelho if (unlikely(wl->state != WL1251_STATE_ON))
45090921014SLuciano Coelho return;
45190921014SLuciano Coelho
452*45474475SH. Nikolaus Schaller result = kmalloc_array(FW_TX_CMPLT_BLOCK_SIZE, sizeof(*result), GFP_KERNEL);
453*45474475SH. Nikolaus Schaller if (!result) {
454*45474475SH. Nikolaus Schaller wl1251_error("can not allocate result buffer");
455*45474475SH. Nikolaus Schaller return;
456*45474475SH. Nikolaus Schaller }
457*45474475SH. Nikolaus Schaller
45890921014SLuciano Coelho /* First we read the result */
459*45474475SH. Nikolaus Schaller wl1251_mem_read(wl, wl->data_path->tx_complete_addr, result,
460*45474475SH. Nikolaus Schaller FW_TX_CMPLT_BLOCK_SIZE * sizeof(*result));
46190921014SLuciano Coelho
46290921014SLuciano Coelho result_index = wl->next_tx_complete;
46390921014SLuciano Coelho
464*45474475SH. Nikolaus Schaller for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) {
46590921014SLuciano Coelho result_ptr = &result[result_index];
46690921014SLuciano Coelho
46790921014SLuciano Coelho if (result_ptr->done_1 == 1 &&
46890921014SLuciano Coelho result_ptr->done_2 == 1) {
46990921014SLuciano Coelho wl1251_tx_packet_cb(wl, result_ptr);
47090921014SLuciano Coelho
47190921014SLuciano Coelho result_ptr->done_1 = 0;
47290921014SLuciano Coelho result_ptr->done_2 = 0;
47390921014SLuciano Coelho
47490921014SLuciano Coelho result_index = (result_index + 1) &
47590921014SLuciano Coelho (FW_TX_CMPLT_BLOCK_SIZE - 1);
47690921014SLuciano Coelho num_complete++;
47790921014SLuciano Coelho } else {
47890921014SLuciano Coelho break;
47990921014SLuciano Coelho }
48090921014SLuciano Coelho }
48190921014SLuciano Coelho
48290921014SLuciano Coelho queue_len = skb_queue_len(&wl->tx_queue);
48390921014SLuciano Coelho
48490921014SLuciano Coelho if ((num_complete > 0) && (queue_len > 0)) {
48590921014SLuciano Coelho /* firmware buffer has space, reschedule tx_work */
48690921014SLuciano Coelho wl1251_debug(DEBUG_TX, "tx_complete: reschedule tx_work");
48790921014SLuciano Coelho ieee80211_queue_work(wl->hw, &wl->tx_work);
48890921014SLuciano Coelho }
48990921014SLuciano Coelho
49090921014SLuciano Coelho if (wl->tx_queue_stopped &&
49190921014SLuciano Coelho queue_len <= WL1251_TX_QUEUE_LOW_WATERMARK) {
49290921014SLuciano Coelho /* tx_queue has space, restart queues */
49390921014SLuciano Coelho wl1251_debug(DEBUG_TX, "tx_complete: waking queues");
49490921014SLuciano Coelho spin_lock_irqsave(&wl->wl_lock, flags);
49590921014SLuciano Coelho ieee80211_wake_queues(wl->hw);
49690921014SLuciano Coelho wl->tx_queue_stopped = false;
49790921014SLuciano Coelho spin_unlock_irqrestore(&wl->wl_lock, flags);
49890921014SLuciano Coelho }
49990921014SLuciano Coelho
50090921014SLuciano Coelho /* Every completed frame needs to be acknowledged */
50190921014SLuciano Coelho if (num_complete) {
50290921014SLuciano Coelho /*
50390921014SLuciano Coelho * If we've wrapped, we have to clear
50490921014SLuciano Coelho * the results in 2 steps.
50590921014SLuciano Coelho */
50690921014SLuciano Coelho if (result_index > wl->next_tx_complete) {
50790921014SLuciano Coelho /* Only 1 write is needed */
50890921014SLuciano Coelho wl1251_mem_write(wl,
50990921014SLuciano Coelho wl->data_path->tx_complete_addr +
51090921014SLuciano Coelho (wl->next_tx_complete *
51190921014SLuciano Coelho sizeof(struct tx_result)),
51290921014SLuciano Coelho &result[wl->next_tx_complete],
51390921014SLuciano Coelho num_complete *
51490921014SLuciano Coelho sizeof(struct tx_result));
51590921014SLuciano Coelho
51690921014SLuciano Coelho
51790921014SLuciano Coelho } else if (result_index < wl->next_tx_complete) {
51890921014SLuciano Coelho /* 2 writes are needed */
51990921014SLuciano Coelho wl1251_mem_write(wl,
52090921014SLuciano Coelho wl->data_path->tx_complete_addr +
52190921014SLuciano Coelho (wl->next_tx_complete *
52290921014SLuciano Coelho sizeof(struct tx_result)),
52390921014SLuciano Coelho &result[wl->next_tx_complete],
52490921014SLuciano Coelho (FW_TX_CMPLT_BLOCK_SIZE -
52590921014SLuciano Coelho wl->next_tx_complete) *
52690921014SLuciano Coelho sizeof(struct tx_result));
52790921014SLuciano Coelho
52890921014SLuciano Coelho wl1251_mem_write(wl,
52990921014SLuciano Coelho wl->data_path->tx_complete_addr,
53090921014SLuciano Coelho result,
53190921014SLuciano Coelho (num_complete -
53290921014SLuciano Coelho FW_TX_CMPLT_BLOCK_SIZE +
53390921014SLuciano Coelho wl->next_tx_complete) *
53490921014SLuciano Coelho sizeof(struct tx_result));
53590921014SLuciano Coelho
53690921014SLuciano Coelho } else {
53790921014SLuciano Coelho /* We have to write the whole array */
53890921014SLuciano Coelho wl1251_mem_write(wl,
53990921014SLuciano Coelho wl->data_path->tx_complete_addr,
54090921014SLuciano Coelho result,
54190921014SLuciano Coelho FW_TX_CMPLT_BLOCK_SIZE *
54290921014SLuciano Coelho sizeof(struct tx_result));
54390921014SLuciano Coelho }
54490921014SLuciano Coelho
54590921014SLuciano Coelho }
54690921014SLuciano Coelho
547*45474475SH. Nikolaus Schaller kfree(result);
54890921014SLuciano Coelho wl->next_tx_complete = result_index;
54990921014SLuciano Coelho }
55090921014SLuciano Coelho
55190921014SLuciano Coelho /* caller must hold wl->mutex */
wl1251_tx_flush(struct wl1251 * wl)55290921014SLuciano Coelho void wl1251_tx_flush(struct wl1251 *wl)
55390921014SLuciano Coelho {
55490921014SLuciano Coelho int i;
55590921014SLuciano Coelho struct sk_buff *skb;
55690921014SLuciano Coelho struct ieee80211_tx_info *info;
55790921014SLuciano Coelho
55890921014SLuciano Coelho /* TX failure */
55990921014SLuciano Coelho /* control->flags = 0; FIXME */
56090921014SLuciano Coelho
56190921014SLuciano Coelho while ((skb = skb_dequeue(&wl->tx_queue))) {
56290921014SLuciano Coelho info = IEEE80211_SKB_CB(skb);
56390921014SLuciano Coelho
56490921014SLuciano Coelho wl1251_debug(DEBUG_TX, "flushing skb 0x%p", skb);
56590921014SLuciano Coelho
56690921014SLuciano Coelho if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS))
56790921014SLuciano Coelho continue;
56890921014SLuciano Coelho
56990921014SLuciano Coelho ieee80211_tx_status(wl->hw, skb);
57090921014SLuciano Coelho }
57190921014SLuciano Coelho
57290921014SLuciano Coelho for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
57390921014SLuciano Coelho if (wl->tx_frames[i] != NULL) {
57490921014SLuciano Coelho skb = wl->tx_frames[i];
57590921014SLuciano Coelho info = IEEE80211_SKB_CB(skb);
57690921014SLuciano Coelho
57790921014SLuciano Coelho if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS))
57890921014SLuciano Coelho continue;
57990921014SLuciano Coelho
58090921014SLuciano Coelho ieee80211_tx_status(wl->hw, skb);
58190921014SLuciano Coelho wl->tx_frames[i] = NULL;
58290921014SLuciano Coelho }
58390921014SLuciano Coelho }
584