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