18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This file is part of wl1251
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 1998-2007 Texas Instruments Incorporated
68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "wl1251.h"
138c2ecf20Sopenharmony_ci#include "reg.h"
148c2ecf20Sopenharmony_ci#include "tx.h"
158c2ecf20Sopenharmony_ci#include "ps.h"
168c2ecf20Sopenharmony_ci#include "io.h"
178c2ecf20Sopenharmony_ci#include "event.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic bool wl1251_tx_double_buffer_busy(struct wl1251 *wl, u32 data_out_count)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	int used, data_in_count;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	data_in_count = wl->data_in_count;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci	if (data_in_count < data_out_count)
268c2ecf20Sopenharmony_ci		/* data_in_count has wrapped */
278c2ecf20Sopenharmony_ci		data_in_count += TX_STATUS_DATA_OUT_COUNT_MASK + 1;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	used = data_in_count - data_out_count;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	WARN_ON(used < 0);
328c2ecf20Sopenharmony_ci	WARN_ON(used > DP_TX_PACKET_RING_CHUNK_NUM);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	if (used >= DP_TX_PACKET_RING_CHUNK_NUM)
358c2ecf20Sopenharmony_ci		return true;
368c2ecf20Sopenharmony_ci	else
378c2ecf20Sopenharmony_ci		return false;
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int wl1251_tx_path_status(struct wl1251 *wl)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	u32 status, addr, data_out_count;
438c2ecf20Sopenharmony_ci	bool busy;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	addr = wl->data_path->tx_control_addr;
468c2ecf20Sopenharmony_ci	status = wl1251_mem_read32(wl, addr);
478c2ecf20Sopenharmony_ci	data_out_count = status & TX_STATUS_DATA_OUT_COUNT_MASK;
488c2ecf20Sopenharmony_ci	busy = wl1251_tx_double_buffer_busy(wl, data_out_count);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	if (busy)
518c2ecf20Sopenharmony_ci		return -EBUSY;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	return 0;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic int wl1251_tx_id(struct wl1251 *wl, struct sk_buff *skb)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	int i;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
618c2ecf20Sopenharmony_ci		if (wl->tx_frames[i] == NULL) {
628c2ecf20Sopenharmony_ci			wl->tx_frames[i] = skb;
638c2ecf20Sopenharmony_ci			return i;
648c2ecf20Sopenharmony_ci		}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	return -EBUSY;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic void wl1251_tx_control(struct tx_double_buffer_desc *tx_hdr,
708c2ecf20Sopenharmony_ci			      struct ieee80211_tx_info *control, u16 fc)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	*(u16 *)&tx_hdr->control = 0;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	tx_hdr->control.rate_policy = 0;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* 802.11 packets */
778c2ecf20Sopenharmony_ci	tx_hdr->control.packet_type = 0;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* Also disable retry and ACK policy for injected packets */
808c2ecf20Sopenharmony_ci	if ((control->flags & IEEE80211_TX_CTL_NO_ACK) ||
818c2ecf20Sopenharmony_ci	    (control->flags & IEEE80211_TX_CTL_INJECTED)) {
828c2ecf20Sopenharmony_ci		tx_hdr->control.rate_policy = 1;
838c2ecf20Sopenharmony_ci		tx_hdr->control.ack_policy = 1;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	tx_hdr->control.tx_complete = 1;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if ((fc & IEEE80211_FTYPE_DATA) &&
898c2ecf20Sopenharmony_ci	    ((fc & IEEE80211_STYPE_QOS_DATA) ||
908c2ecf20Sopenharmony_ci	     (fc & IEEE80211_STYPE_QOS_NULLFUNC)))
918c2ecf20Sopenharmony_ci		tx_hdr->control.qos = 1;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci/* RSN + MIC = 8 + 8 = 16 bytes (worst case - AES). */
958c2ecf20Sopenharmony_ci#define MAX_MSDU_SECURITY_LENGTH      16
968c2ecf20Sopenharmony_ci#define MAX_MPDU_SECURITY_LENGTH      16
978c2ecf20Sopenharmony_ci#define WLAN_QOS_HDR_LEN              26
988c2ecf20Sopenharmony_ci#define MAX_MPDU_HEADER_AND_SECURITY  (MAX_MPDU_SECURITY_LENGTH + \
998c2ecf20Sopenharmony_ci				       WLAN_QOS_HDR_LEN)
1008c2ecf20Sopenharmony_ci#define HW_BLOCK_SIZE                 252
1018c2ecf20Sopenharmony_cistatic void wl1251_tx_frag_block_num(struct tx_double_buffer_desc *tx_hdr)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	u16 payload_len, frag_threshold, mem_blocks;
1048c2ecf20Sopenharmony_ci	u16 num_mpdus, mem_blocks_per_frag;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD;
1078c2ecf20Sopenharmony_ci	tx_hdr->frag_threshold = cpu_to_le16(frag_threshold);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	payload_len = le16_to_cpu(tx_hdr->length) + MAX_MSDU_SECURITY_LENGTH;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (payload_len > frag_threshold) {
1128c2ecf20Sopenharmony_ci		mem_blocks_per_frag =
1138c2ecf20Sopenharmony_ci			((frag_threshold + MAX_MPDU_HEADER_AND_SECURITY) /
1148c2ecf20Sopenharmony_ci			 HW_BLOCK_SIZE) + 1;
1158c2ecf20Sopenharmony_ci		num_mpdus = payload_len / frag_threshold;
1168c2ecf20Sopenharmony_ci		mem_blocks = num_mpdus * mem_blocks_per_frag;
1178c2ecf20Sopenharmony_ci		payload_len -= num_mpdus * frag_threshold;
1188c2ecf20Sopenharmony_ci		num_mpdus++;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	} else {
1218c2ecf20Sopenharmony_ci		mem_blocks_per_frag = 0;
1228c2ecf20Sopenharmony_ci		mem_blocks = 0;
1238c2ecf20Sopenharmony_ci		num_mpdus = 1;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	mem_blocks += (payload_len / HW_BLOCK_SIZE) + 1;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (num_mpdus > 1)
1298c2ecf20Sopenharmony_ci		mem_blocks += min(num_mpdus, mem_blocks_per_frag);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	tx_hdr->num_mem_blocks = mem_blocks;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic int wl1251_tx_fill_hdr(struct wl1251 *wl, struct sk_buff *skb,
1358c2ecf20Sopenharmony_ci			      struct ieee80211_tx_info *control)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct tx_double_buffer_desc *tx_hdr;
1388c2ecf20Sopenharmony_ci	struct ieee80211_rate *rate;
1398c2ecf20Sopenharmony_ci	int id;
1408c2ecf20Sopenharmony_ci	u16 fc;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (!skb)
1438c2ecf20Sopenharmony_ci		return -EINVAL;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	id = wl1251_tx_id(wl, skb);
1468c2ecf20Sopenharmony_ci	if (id < 0)
1478c2ecf20Sopenharmony_ci		return id;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	fc = *(u16 *)skb->data;
1508c2ecf20Sopenharmony_ci	tx_hdr = skb_push(skb, sizeof(*tx_hdr));
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	tx_hdr->length = cpu_to_le16(skb->len - sizeof(*tx_hdr));
1538c2ecf20Sopenharmony_ci	rate = ieee80211_get_tx_rate(wl->hw, control);
1548c2ecf20Sopenharmony_ci	tx_hdr->rate = cpu_to_le16(rate->hw_value);
1558c2ecf20Sopenharmony_ci	tx_hdr->expiry_time = cpu_to_le32(1 << 16);
1568c2ecf20Sopenharmony_ci	tx_hdr->id = id;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	tx_hdr->xmit_queue = wl1251_tx_get_queue(skb_get_queue_mapping(skb));
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	wl1251_tx_control(tx_hdr, control, fc);
1618c2ecf20Sopenharmony_ci	wl1251_tx_frag_block_num(tx_hdr);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci/* We copy the packet to the target */
1678c2ecf20Sopenharmony_cistatic int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb,
1688c2ecf20Sopenharmony_ci				 struct ieee80211_tx_info *control)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct tx_double_buffer_desc *tx_hdr;
1718c2ecf20Sopenharmony_ci	int len;
1728c2ecf20Sopenharmony_ci	u32 addr;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (!skb)
1758c2ecf20Sopenharmony_ci		return -EINVAL;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	tx_hdr = (struct tx_double_buffer_desc *) skb->data;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (control->control.hw_key &&
1808c2ecf20Sopenharmony_ci	    control->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
1818c2ecf20Sopenharmony_ci		int hdrlen;
1828c2ecf20Sopenharmony_ci		__le16 fc;
1838c2ecf20Sopenharmony_ci		u16 length;
1848c2ecf20Sopenharmony_ci		u8 *pos;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		fc = *(__le16 *)(skb->data + sizeof(*tx_hdr));
1878c2ecf20Sopenharmony_ci		length = le16_to_cpu(tx_hdr->length) + WL1251_TKIP_IV_SPACE;
1888c2ecf20Sopenharmony_ci		tx_hdr->length = cpu_to_le16(length);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci		hdrlen = ieee80211_hdrlen(fc);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci		pos = skb_push(skb, WL1251_TKIP_IV_SPACE);
1938c2ecf20Sopenharmony_ci		memmove(pos, pos + WL1251_TKIP_IV_SPACE,
1948c2ecf20Sopenharmony_ci			sizeof(*tx_hdr) + hdrlen);
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* Revisit. This is a workaround for getting non-aligned packets.
1988c2ecf20Sopenharmony_ci	   This happens at least with EAPOL packets from the user space.
1998c2ecf20Sopenharmony_ci	   Our DMA requires packets to be aligned on a 4-byte boundary.
2008c2ecf20Sopenharmony_ci	*/
2018c2ecf20Sopenharmony_ci	if (unlikely((long)skb->data & 0x03)) {
2028c2ecf20Sopenharmony_ci		int offset = (4 - (long)skb->data) & 0x03;
2038c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_TX, "skb offset %d", offset);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci		/* check whether the current skb can be used */
2068c2ecf20Sopenharmony_ci		if (skb_cloned(skb) || (skb_tailroom(skb) < offset)) {
2078c2ecf20Sopenharmony_ci			struct sk_buff *newskb = skb_copy_expand(skb, 0, 3,
2088c2ecf20Sopenharmony_ci								 GFP_KERNEL);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci			if (unlikely(newskb == NULL))
2118c2ecf20Sopenharmony_ci				return -EINVAL;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci			tx_hdr = (struct tx_double_buffer_desc *) newskb->data;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci			dev_kfree_skb_any(skb);
2168c2ecf20Sopenharmony_ci			wl->tx_frames[tx_hdr->id] = skb = newskb;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci			offset = (4 - (long)skb->data) & 0x03;
2198c2ecf20Sopenharmony_ci			wl1251_debug(DEBUG_TX, "new skb offset %d", offset);
2208c2ecf20Sopenharmony_ci		}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		/* align the buffer on a 4-byte boundary */
2238c2ecf20Sopenharmony_ci		if (offset) {
2248c2ecf20Sopenharmony_ci			unsigned char *src = skb->data;
2258c2ecf20Sopenharmony_ci			skb_reserve(skb, offset);
2268c2ecf20Sopenharmony_ci			memmove(skb->data, src, skb->len);
2278c2ecf20Sopenharmony_ci			tx_hdr = (struct tx_double_buffer_desc *) skb->data;
2288c2ecf20Sopenharmony_ci		}
2298c2ecf20Sopenharmony_ci	}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/* Our skb->data at this point includes the HW header */
2328c2ecf20Sopenharmony_ci	len = WL1251_TX_ALIGN(skb->len);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if (wl->data_in_count & 0x1)
2358c2ecf20Sopenharmony_ci		addr = wl->data_path->tx_packet_ring_addr +
2368c2ecf20Sopenharmony_ci			wl->data_path->tx_packet_ring_chunk_size;
2378c2ecf20Sopenharmony_ci	else
2388c2ecf20Sopenharmony_ci		addr = wl->data_path->tx_packet_ring_addr;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	wl1251_mem_write(wl, addr, skb->data, len);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	wl1251_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x "
2438c2ecf20Sopenharmony_ci		     "queue %d", tx_hdr->id, skb, tx_hdr->length,
2448c2ecf20Sopenharmony_ci		     tx_hdr->rate, tx_hdr->xmit_queue);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic void wl1251_tx_trigger(struct wl1251 *wl)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	u32 data, addr;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (wl->data_in_count & 0x1) {
2548c2ecf20Sopenharmony_ci		addr = ACX_REG_INTERRUPT_TRIG_H;
2558c2ecf20Sopenharmony_ci		data = INTR_TRIG_TX_PROC1;
2568c2ecf20Sopenharmony_ci	} else {
2578c2ecf20Sopenharmony_ci		addr = ACX_REG_INTERRUPT_TRIG;
2588c2ecf20Sopenharmony_ci		data = INTR_TRIG_TX_PROC0;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	wl1251_reg_write32(wl, addr, data);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	/* Bumping data in */
2648c2ecf20Sopenharmony_ci	wl->data_in_count = (wl->data_in_count + 1) &
2658c2ecf20Sopenharmony_ci		TX_STATUS_DATA_OUT_COUNT_MASK;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic void enable_tx_for_packet_injection(struct wl1251 *wl)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	int ret;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	ret = wl1251_cmd_join(wl, BSS_TYPE_STA_BSS, wl->channel,
2738c2ecf20Sopenharmony_ci			      wl->beacon_int, wl->dtim_period);
2748c2ecf20Sopenharmony_ci	if (ret < 0) {
2758c2ecf20Sopenharmony_ci		wl1251_warning("join failed");
2768c2ecf20Sopenharmony_ci		return;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	ret = wl1251_event_wait(wl, JOIN_EVENT_COMPLETE_ID, 100);
2808c2ecf20Sopenharmony_ci	if (ret < 0) {
2818c2ecf20Sopenharmony_ci		wl1251_warning("join timeout");
2828c2ecf20Sopenharmony_ci		return;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	wl->joined = true;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci/* caller must hold wl->mutex */
2898c2ecf20Sopenharmony_cistatic int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info;
2928c2ecf20Sopenharmony_ci	int ret = 0;
2938c2ecf20Sopenharmony_ci	u8 idx;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	info = IEEE80211_SKB_CB(skb);
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (info->control.hw_key) {
2988c2ecf20Sopenharmony_ci		if (unlikely(wl->monitor_present))
2998c2ecf20Sopenharmony_ci			return -EINVAL;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci		idx = info->control.hw_key->hw_key_idx;
3028c2ecf20Sopenharmony_ci		if (unlikely(wl->default_key != idx)) {
3038c2ecf20Sopenharmony_ci			ret = wl1251_acx_default_key(wl, idx);
3048c2ecf20Sopenharmony_ci			if (ret < 0)
3058c2ecf20Sopenharmony_ci				return ret;
3068c2ecf20Sopenharmony_ci		}
3078c2ecf20Sopenharmony_ci	}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	/* Enable tx path in monitor mode for packet injection */
3108c2ecf20Sopenharmony_ci	if ((wl->vif == NULL) && !wl->joined)
3118c2ecf20Sopenharmony_ci		enable_tx_for_packet_injection(wl);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	ret = wl1251_tx_path_status(wl);
3148c2ecf20Sopenharmony_ci	if (ret < 0)
3158c2ecf20Sopenharmony_ci		return ret;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	ret = wl1251_tx_fill_hdr(wl, skb, info);
3188c2ecf20Sopenharmony_ci	if (ret < 0)
3198c2ecf20Sopenharmony_ci		return ret;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	ret = wl1251_tx_send_packet(wl, skb, info);
3228c2ecf20Sopenharmony_ci	if (ret < 0)
3238c2ecf20Sopenharmony_ci		return ret;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	wl1251_tx_trigger(wl);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return ret;
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_civoid wl1251_tx_work(struct work_struct *work)
3318c2ecf20Sopenharmony_ci{
3328c2ecf20Sopenharmony_ci	struct wl1251 *wl = container_of(work, struct wl1251, tx_work);
3338c2ecf20Sopenharmony_ci	struct sk_buff *skb;
3348c2ecf20Sopenharmony_ci	bool woken_up = false;
3358c2ecf20Sopenharmony_ci	int ret;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	mutex_lock(&wl->mutex);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (unlikely(wl->state == WL1251_STATE_OFF))
3408c2ecf20Sopenharmony_ci		goto out;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&wl->tx_queue))) {
3438c2ecf20Sopenharmony_ci		if (!woken_up) {
3448c2ecf20Sopenharmony_ci			ret = wl1251_ps_elp_wakeup(wl);
3458c2ecf20Sopenharmony_ci			if (ret < 0)
3468c2ecf20Sopenharmony_ci				goto out;
3478c2ecf20Sopenharmony_ci			woken_up = true;
3488c2ecf20Sopenharmony_ci		}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		ret = wl1251_tx_frame(wl, skb);
3518c2ecf20Sopenharmony_ci		if (ret == -EBUSY) {
3528c2ecf20Sopenharmony_ci			skb_queue_head(&wl->tx_queue, skb);
3538c2ecf20Sopenharmony_ci			goto out;
3548c2ecf20Sopenharmony_ci		} else if (ret < 0) {
3558c2ecf20Sopenharmony_ci			dev_kfree_skb(skb);
3568c2ecf20Sopenharmony_ci			goto out;
3578c2ecf20Sopenharmony_ci		}
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ciout:
3618c2ecf20Sopenharmony_ci	if (woken_up)
3628c2ecf20Sopenharmony_ci		wl1251_ps_elp_sleep(wl);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	mutex_unlock(&wl->mutex);
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic const char *wl1251_tx_parse_status(u8 status)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	/* 8 bit status field, one character per bit plus null */
3708c2ecf20Sopenharmony_ci	static char buf[9];
3718c2ecf20Sopenharmony_ci	int i = 0;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	memset(buf, 0, sizeof(buf));
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (status & TX_DMA_ERROR)
3768c2ecf20Sopenharmony_ci		buf[i++] = 'm';
3778c2ecf20Sopenharmony_ci	if (status & TX_DISABLED)
3788c2ecf20Sopenharmony_ci		buf[i++] = 'd';
3798c2ecf20Sopenharmony_ci	if (status & TX_RETRY_EXCEEDED)
3808c2ecf20Sopenharmony_ci		buf[i++] = 'r';
3818c2ecf20Sopenharmony_ci	if (status & TX_TIMEOUT)
3828c2ecf20Sopenharmony_ci		buf[i++] = 't';
3838c2ecf20Sopenharmony_ci	if (status & TX_KEY_NOT_FOUND)
3848c2ecf20Sopenharmony_ci		buf[i++] = 'k';
3858c2ecf20Sopenharmony_ci	if (status & TX_ENCRYPT_FAIL)
3868c2ecf20Sopenharmony_ci		buf[i++] = 'e';
3878c2ecf20Sopenharmony_ci	if (status & TX_UNAVAILABLE_PRIORITY)
3888c2ecf20Sopenharmony_ci		buf[i++] = 'p';
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* bit 0 is unused apparently */
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return buf;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic void wl1251_tx_packet_cb(struct wl1251 *wl,
3968c2ecf20Sopenharmony_ci				struct tx_result *result)
3978c2ecf20Sopenharmony_ci{
3988c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info;
3998c2ecf20Sopenharmony_ci	struct sk_buff *skb;
4008c2ecf20Sopenharmony_ci	int hdrlen;
4018c2ecf20Sopenharmony_ci	u8 *frame;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	skb = wl->tx_frames[result->id];
4048c2ecf20Sopenharmony_ci	if (skb == NULL) {
4058c2ecf20Sopenharmony_ci		wl1251_error("SKB for packet %d is NULL", result->id);
4068c2ecf20Sopenharmony_ci		return;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	info = IEEE80211_SKB_CB(skb);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
4128c2ecf20Sopenharmony_ci	    !(info->flags & IEEE80211_TX_CTL_INJECTED) &&
4138c2ecf20Sopenharmony_ci	    (result->status == TX_SUCCESS))
4148c2ecf20Sopenharmony_ci		info->flags |= IEEE80211_TX_STAT_ACK;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	info->status.rates[0].count = result->ack_failures + 1;
4178c2ecf20Sopenharmony_ci	wl->stats.retry_count += result->ack_failures;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	/*
4208c2ecf20Sopenharmony_ci	 * We have to remove our private TX header before pushing
4218c2ecf20Sopenharmony_ci	 * the skb back to mac80211.
4228c2ecf20Sopenharmony_ci	 */
4238c2ecf20Sopenharmony_ci	frame = skb_pull(skb, sizeof(struct tx_double_buffer_desc));
4248c2ecf20Sopenharmony_ci	if (info->control.hw_key &&
4258c2ecf20Sopenharmony_ci	    info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
4268c2ecf20Sopenharmony_ci		hdrlen = ieee80211_get_hdrlen_from_skb(skb);
4278c2ecf20Sopenharmony_ci		memmove(frame + WL1251_TKIP_IV_SPACE, frame, hdrlen);
4288c2ecf20Sopenharmony_ci		skb_pull(skb, WL1251_TKIP_IV_SPACE);
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	wl1251_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x"
4328c2ecf20Sopenharmony_ci		     " status 0x%x (%s)",
4338c2ecf20Sopenharmony_ci		     result->id, skb, result->ack_failures, result->rate,
4348c2ecf20Sopenharmony_ci		     result->status, wl1251_tx_parse_status(result->status));
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	ieee80211_tx_status(wl->hw, skb);
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	wl->tx_frames[result->id] = NULL;
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci/* Called upon reception of a TX complete interrupt */
4438c2ecf20Sopenharmony_civoid wl1251_tx_complete(struct wl1251 *wl)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	int i, result_index, num_complete = 0, queue_len;
4468c2ecf20Sopenharmony_ci	struct tx_result result[FW_TX_CMPLT_BLOCK_SIZE], *result_ptr;
4478c2ecf20Sopenharmony_ci	unsigned long flags;
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	if (unlikely(wl->state != WL1251_STATE_ON))
4508c2ecf20Sopenharmony_ci		return;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	/* First we read the result */
4538c2ecf20Sopenharmony_ci	wl1251_mem_read(wl, wl->data_path->tx_complete_addr,
4548c2ecf20Sopenharmony_ci			    result, sizeof(result));
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	result_index = wl->next_tx_complete;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(result); i++) {
4598c2ecf20Sopenharmony_ci		result_ptr = &result[result_index];
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci		if (result_ptr->done_1 == 1 &&
4628c2ecf20Sopenharmony_ci		    result_ptr->done_2 == 1) {
4638c2ecf20Sopenharmony_ci			wl1251_tx_packet_cb(wl, result_ptr);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci			result_ptr->done_1 = 0;
4668c2ecf20Sopenharmony_ci			result_ptr->done_2 = 0;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci			result_index = (result_index + 1) &
4698c2ecf20Sopenharmony_ci				(FW_TX_CMPLT_BLOCK_SIZE - 1);
4708c2ecf20Sopenharmony_ci			num_complete++;
4718c2ecf20Sopenharmony_ci		} else {
4728c2ecf20Sopenharmony_ci			break;
4738c2ecf20Sopenharmony_ci		}
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	queue_len = skb_queue_len(&wl->tx_queue);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	if ((num_complete > 0) && (queue_len > 0)) {
4798c2ecf20Sopenharmony_ci		/* firmware buffer has space, reschedule tx_work */
4808c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_TX, "tx_complete: reschedule tx_work");
4818c2ecf20Sopenharmony_ci		ieee80211_queue_work(wl->hw, &wl->tx_work);
4828c2ecf20Sopenharmony_ci	}
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (wl->tx_queue_stopped &&
4858c2ecf20Sopenharmony_ci	    queue_len <= WL1251_TX_QUEUE_LOW_WATERMARK) {
4868c2ecf20Sopenharmony_ci		/* tx_queue has space, restart queues */
4878c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_TX, "tx_complete: waking queues");
4888c2ecf20Sopenharmony_ci		spin_lock_irqsave(&wl->wl_lock, flags);
4898c2ecf20Sopenharmony_ci		ieee80211_wake_queues(wl->hw);
4908c2ecf20Sopenharmony_ci		wl->tx_queue_stopped = false;
4918c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&wl->wl_lock, flags);
4928c2ecf20Sopenharmony_ci	}
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	/* Every completed frame needs to be acknowledged */
4958c2ecf20Sopenharmony_ci	if (num_complete) {
4968c2ecf20Sopenharmony_ci		/*
4978c2ecf20Sopenharmony_ci		 * If we've wrapped, we have to clear
4988c2ecf20Sopenharmony_ci		 * the results in 2 steps.
4998c2ecf20Sopenharmony_ci		 */
5008c2ecf20Sopenharmony_ci		if (result_index > wl->next_tx_complete) {
5018c2ecf20Sopenharmony_ci			/* Only 1 write is needed */
5028c2ecf20Sopenharmony_ci			wl1251_mem_write(wl,
5038c2ecf20Sopenharmony_ci					 wl->data_path->tx_complete_addr +
5048c2ecf20Sopenharmony_ci					 (wl->next_tx_complete *
5058c2ecf20Sopenharmony_ci					  sizeof(struct tx_result)),
5068c2ecf20Sopenharmony_ci					 &result[wl->next_tx_complete],
5078c2ecf20Sopenharmony_ci					 num_complete *
5088c2ecf20Sopenharmony_ci					 sizeof(struct tx_result));
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci		} else if (result_index < wl->next_tx_complete) {
5128c2ecf20Sopenharmony_ci			/* 2 writes are needed */
5138c2ecf20Sopenharmony_ci			wl1251_mem_write(wl,
5148c2ecf20Sopenharmony_ci					 wl->data_path->tx_complete_addr +
5158c2ecf20Sopenharmony_ci					 (wl->next_tx_complete *
5168c2ecf20Sopenharmony_ci					  sizeof(struct tx_result)),
5178c2ecf20Sopenharmony_ci					 &result[wl->next_tx_complete],
5188c2ecf20Sopenharmony_ci					 (FW_TX_CMPLT_BLOCK_SIZE -
5198c2ecf20Sopenharmony_ci					  wl->next_tx_complete) *
5208c2ecf20Sopenharmony_ci					 sizeof(struct tx_result));
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci			wl1251_mem_write(wl,
5238c2ecf20Sopenharmony_ci					 wl->data_path->tx_complete_addr,
5248c2ecf20Sopenharmony_ci					 result,
5258c2ecf20Sopenharmony_ci					 (num_complete -
5268c2ecf20Sopenharmony_ci					  FW_TX_CMPLT_BLOCK_SIZE +
5278c2ecf20Sopenharmony_ci					  wl->next_tx_complete) *
5288c2ecf20Sopenharmony_ci					 sizeof(struct tx_result));
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci		} else {
5318c2ecf20Sopenharmony_ci			/* We have to write the whole array */
5328c2ecf20Sopenharmony_ci			wl1251_mem_write(wl,
5338c2ecf20Sopenharmony_ci					 wl->data_path->tx_complete_addr,
5348c2ecf20Sopenharmony_ci					 result,
5358c2ecf20Sopenharmony_ci					 FW_TX_CMPLT_BLOCK_SIZE *
5368c2ecf20Sopenharmony_ci					 sizeof(struct tx_result));
5378c2ecf20Sopenharmony_ci		}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	wl->next_tx_complete = result_index;
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci/* caller must hold wl->mutex */
5458c2ecf20Sopenharmony_civoid wl1251_tx_flush(struct wl1251 *wl)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	int i;
5488c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5498c2ecf20Sopenharmony_ci	struct ieee80211_tx_info *info;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	/* TX failure */
5528c2ecf20Sopenharmony_ci/* 	control->flags = 0; FIXME */
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	while ((skb = skb_dequeue(&wl->tx_queue))) {
5558c2ecf20Sopenharmony_ci		info = IEEE80211_SKB_CB(skb);
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci		wl1251_debug(DEBUG_TX, "flushing skb 0x%p", skb);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci		if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS))
5608c2ecf20Sopenharmony_ci				continue;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci		ieee80211_tx_status(wl->hw, skb);
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++)
5668c2ecf20Sopenharmony_ci		if (wl->tx_frames[i] != NULL) {
5678c2ecf20Sopenharmony_ci			skb = wl->tx_frames[i];
5688c2ecf20Sopenharmony_ci			info = IEEE80211_SKB_CB(skb);
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci			if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS))
5718c2ecf20Sopenharmony_ci				continue;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci			ieee80211_tx_status(wl->hw, skb);
5748c2ecf20Sopenharmony_ci			wl->tx_frames[i] = NULL;
5758c2ecf20Sopenharmony_ci		}
5768c2ecf20Sopenharmony_ci}
577