162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * This file is part of wl1251 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 1998-2007 Texas Instruments Incorporated 662306a36Sopenharmony_ci * Copyright (C) 2008 Nokia Corporation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "wl1251.h" 1362306a36Sopenharmony_ci#include "reg.h" 1462306a36Sopenharmony_ci#include "tx.h" 1562306a36Sopenharmony_ci#include "ps.h" 1662306a36Sopenharmony_ci#include "io.h" 1762306a36Sopenharmony_ci#include "event.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic bool wl1251_tx_double_buffer_busy(struct wl1251 *wl, u32 data_out_count) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci int used, data_in_count; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci data_in_count = wl->data_in_count; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci if (data_in_count < data_out_count) 2662306a36Sopenharmony_ci /* data_in_count has wrapped */ 2762306a36Sopenharmony_ci data_in_count += TX_STATUS_DATA_OUT_COUNT_MASK + 1; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci used = data_in_count - data_out_count; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci WARN_ON(used < 0); 3262306a36Sopenharmony_ci WARN_ON(used > DP_TX_PACKET_RING_CHUNK_NUM); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci if (used >= DP_TX_PACKET_RING_CHUNK_NUM) 3562306a36Sopenharmony_ci return true; 3662306a36Sopenharmony_ci else 3762306a36Sopenharmony_ci return false; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int wl1251_tx_path_status(struct wl1251 *wl) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci u32 status, addr, data_out_count; 4362306a36Sopenharmony_ci bool busy; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci addr = wl->data_path->tx_control_addr; 4662306a36Sopenharmony_ci status = wl1251_mem_read32(wl, addr); 4762306a36Sopenharmony_ci data_out_count = status & TX_STATUS_DATA_OUT_COUNT_MASK; 4862306a36Sopenharmony_ci busy = wl1251_tx_double_buffer_busy(wl, data_out_count); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (busy) 5162306a36Sopenharmony_ci return -EBUSY; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return 0; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int wl1251_tx_id(struct wl1251 *wl, struct sk_buff *skb) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci int i; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) 6162306a36Sopenharmony_ci if (wl->tx_frames[i] == NULL) { 6262306a36Sopenharmony_ci wl->tx_frames[i] = skb; 6362306a36Sopenharmony_ci return i; 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return -EBUSY; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic void wl1251_tx_control(struct tx_double_buffer_desc *tx_hdr, 7062306a36Sopenharmony_ci struct ieee80211_tx_info *control, u16 fc) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci *(u16 *)&tx_hdr->control = 0; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci tx_hdr->control.rate_policy = 0; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* 802.11 packets */ 7762306a36Sopenharmony_ci tx_hdr->control.packet_type = 0; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Also disable retry and ACK policy for injected packets */ 8062306a36Sopenharmony_ci if ((control->flags & IEEE80211_TX_CTL_NO_ACK) || 8162306a36Sopenharmony_ci (control->flags & IEEE80211_TX_CTL_INJECTED)) { 8262306a36Sopenharmony_ci tx_hdr->control.rate_policy = 1; 8362306a36Sopenharmony_ci tx_hdr->control.ack_policy = 1; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci tx_hdr->control.tx_complete = 1; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if ((fc & IEEE80211_FTYPE_DATA) && 8962306a36Sopenharmony_ci ((fc & IEEE80211_STYPE_QOS_DATA) || 9062306a36Sopenharmony_ci (fc & IEEE80211_STYPE_QOS_NULLFUNC))) 9162306a36Sopenharmony_ci tx_hdr->control.qos = 1; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* RSN + MIC = 8 + 8 = 16 bytes (worst case - AES). */ 9562306a36Sopenharmony_ci#define MAX_MSDU_SECURITY_LENGTH 16 9662306a36Sopenharmony_ci#define MAX_MPDU_SECURITY_LENGTH 16 9762306a36Sopenharmony_ci#define WLAN_QOS_HDR_LEN 26 9862306a36Sopenharmony_ci#define MAX_MPDU_HEADER_AND_SECURITY (MAX_MPDU_SECURITY_LENGTH + \ 9962306a36Sopenharmony_ci WLAN_QOS_HDR_LEN) 10062306a36Sopenharmony_ci#define HW_BLOCK_SIZE 252 10162306a36Sopenharmony_cistatic void wl1251_tx_frag_block_num(struct tx_double_buffer_desc *tx_hdr) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci u16 payload_len, frag_threshold, mem_blocks; 10462306a36Sopenharmony_ci u16 num_mpdus, mem_blocks_per_frag; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD; 10762306a36Sopenharmony_ci tx_hdr->frag_threshold = cpu_to_le16(frag_threshold); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci payload_len = le16_to_cpu(tx_hdr->length) + MAX_MSDU_SECURITY_LENGTH; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (payload_len > frag_threshold) { 11262306a36Sopenharmony_ci mem_blocks_per_frag = 11362306a36Sopenharmony_ci ((frag_threshold + MAX_MPDU_HEADER_AND_SECURITY) / 11462306a36Sopenharmony_ci HW_BLOCK_SIZE) + 1; 11562306a36Sopenharmony_ci num_mpdus = payload_len / frag_threshold; 11662306a36Sopenharmony_ci mem_blocks = num_mpdus * mem_blocks_per_frag; 11762306a36Sopenharmony_ci payload_len -= num_mpdus * frag_threshold; 11862306a36Sopenharmony_ci num_mpdus++; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci } else { 12162306a36Sopenharmony_ci mem_blocks_per_frag = 0; 12262306a36Sopenharmony_ci mem_blocks = 0; 12362306a36Sopenharmony_ci num_mpdus = 1; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci mem_blocks += (payload_len / HW_BLOCK_SIZE) + 1; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (num_mpdus > 1) 12962306a36Sopenharmony_ci mem_blocks += min(num_mpdus, mem_blocks_per_frag); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci tx_hdr->num_mem_blocks = mem_blocks; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int wl1251_tx_fill_hdr(struct wl1251 *wl, struct sk_buff *skb, 13562306a36Sopenharmony_ci struct ieee80211_tx_info *control) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct tx_double_buffer_desc *tx_hdr; 13862306a36Sopenharmony_ci struct ieee80211_rate *rate; 13962306a36Sopenharmony_ci int id; 14062306a36Sopenharmony_ci u16 fc; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (!skb) 14362306a36Sopenharmony_ci return -EINVAL; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci id = wl1251_tx_id(wl, skb); 14662306a36Sopenharmony_ci if (id < 0) 14762306a36Sopenharmony_ci return id; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci fc = *(u16 *)skb->data; 15062306a36Sopenharmony_ci tx_hdr = skb_push(skb, sizeof(*tx_hdr)); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci tx_hdr->length = cpu_to_le16(skb->len - sizeof(*tx_hdr)); 15362306a36Sopenharmony_ci rate = ieee80211_get_tx_rate(wl->hw, control); 15462306a36Sopenharmony_ci tx_hdr->rate = cpu_to_le16(rate->hw_value); 15562306a36Sopenharmony_ci tx_hdr->expiry_time = cpu_to_le32(1 << 16); 15662306a36Sopenharmony_ci tx_hdr->id = id; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci tx_hdr->xmit_queue = wl1251_tx_get_queue(skb_get_queue_mapping(skb)); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci wl1251_tx_control(tx_hdr, control, fc); 16162306a36Sopenharmony_ci wl1251_tx_frag_block_num(tx_hdr); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* We copy the packet to the target */ 16762306a36Sopenharmony_cistatic int wl1251_tx_send_packet(struct wl1251 *wl, struct sk_buff *skb, 16862306a36Sopenharmony_ci struct ieee80211_tx_info *control) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct tx_double_buffer_desc *tx_hdr; 17162306a36Sopenharmony_ci int len; 17262306a36Sopenharmony_ci u32 addr; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (!skb) 17562306a36Sopenharmony_ci return -EINVAL; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci tx_hdr = (struct tx_double_buffer_desc *) skb->data; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (control->control.hw_key && 18062306a36Sopenharmony_ci control->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { 18162306a36Sopenharmony_ci int hdrlen; 18262306a36Sopenharmony_ci __le16 fc; 18362306a36Sopenharmony_ci u16 length; 18462306a36Sopenharmony_ci u8 *pos; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci fc = *(__le16 *)(skb->data + sizeof(*tx_hdr)); 18762306a36Sopenharmony_ci length = le16_to_cpu(tx_hdr->length) + WL1251_TKIP_IV_SPACE; 18862306a36Sopenharmony_ci tx_hdr->length = cpu_to_le16(length); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci hdrlen = ieee80211_hdrlen(fc); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci pos = skb_push(skb, WL1251_TKIP_IV_SPACE); 19362306a36Sopenharmony_ci memmove(pos, pos + WL1251_TKIP_IV_SPACE, 19462306a36Sopenharmony_ci sizeof(*tx_hdr) + hdrlen); 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Revisit. This is a workaround for getting non-aligned packets. 19862306a36Sopenharmony_ci This happens at least with EAPOL packets from the user space. 19962306a36Sopenharmony_ci Our DMA requires packets to be aligned on a 4-byte boundary. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci if (unlikely((long)skb->data & 0x03)) { 20262306a36Sopenharmony_ci int offset = (4 - (long)skb->data) & 0x03; 20362306a36Sopenharmony_ci wl1251_debug(DEBUG_TX, "skb offset %d", offset); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* check whether the current skb can be used */ 20662306a36Sopenharmony_ci if (skb_cloned(skb) || (skb_tailroom(skb) < offset)) { 20762306a36Sopenharmony_ci struct sk_buff *newskb = skb_copy_expand(skb, 0, 3, 20862306a36Sopenharmony_ci GFP_KERNEL); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (unlikely(newskb == NULL)) 21162306a36Sopenharmony_ci return -EINVAL; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci tx_hdr = (struct tx_double_buffer_desc *) newskb->data; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 21662306a36Sopenharmony_ci wl->tx_frames[tx_hdr->id] = skb = newskb; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci offset = (4 - (long)skb->data) & 0x03; 21962306a36Sopenharmony_ci wl1251_debug(DEBUG_TX, "new skb offset %d", offset); 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* align the buffer on a 4-byte boundary */ 22362306a36Sopenharmony_ci if (offset) { 22462306a36Sopenharmony_ci unsigned char *src = skb->data; 22562306a36Sopenharmony_ci skb_reserve(skb, offset); 22662306a36Sopenharmony_ci memmove(skb->data, src, skb->len); 22762306a36Sopenharmony_ci tx_hdr = (struct tx_double_buffer_desc *) skb->data; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Our skb->data at this point includes the HW header */ 23262306a36Sopenharmony_ci len = WL1251_TX_ALIGN(skb->len); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci if (wl->data_in_count & 0x1) 23562306a36Sopenharmony_ci addr = wl->data_path->tx_packet_ring_addr + 23662306a36Sopenharmony_ci wl->data_path->tx_packet_ring_chunk_size; 23762306a36Sopenharmony_ci else 23862306a36Sopenharmony_ci addr = wl->data_path->tx_packet_ring_addr; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci wl1251_mem_write(wl, addr, skb->data, len); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci wl1251_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u rate 0x%x " 24362306a36Sopenharmony_ci "queue %d", tx_hdr->id, skb, tx_hdr->length, 24462306a36Sopenharmony_ci tx_hdr->rate, tx_hdr->xmit_queue); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return 0; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic void wl1251_tx_trigger(struct wl1251 *wl) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci u32 data, addr; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (wl->data_in_count & 0x1) { 25462306a36Sopenharmony_ci addr = ACX_REG_INTERRUPT_TRIG_H; 25562306a36Sopenharmony_ci data = INTR_TRIG_TX_PROC1; 25662306a36Sopenharmony_ci } else { 25762306a36Sopenharmony_ci addr = ACX_REG_INTERRUPT_TRIG; 25862306a36Sopenharmony_ci data = INTR_TRIG_TX_PROC0; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci wl1251_reg_write32(wl, addr, data); 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Bumping data in */ 26462306a36Sopenharmony_ci wl->data_in_count = (wl->data_in_count + 1) & 26562306a36Sopenharmony_ci TX_STATUS_DATA_OUT_COUNT_MASK; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic void enable_tx_for_packet_injection(struct wl1251 *wl) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci int ret; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci ret = wl1251_cmd_join(wl, BSS_TYPE_STA_BSS, wl->channel, 27362306a36Sopenharmony_ci wl->beacon_int, wl->dtim_period); 27462306a36Sopenharmony_ci if (ret < 0) { 27562306a36Sopenharmony_ci wl1251_warning("join failed"); 27662306a36Sopenharmony_ci return; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci ret = wl1251_event_wait(wl, JOIN_EVENT_COMPLETE_ID, 100); 28062306a36Sopenharmony_ci if (ret < 0) { 28162306a36Sopenharmony_ci wl1251_warning("join timeout"); 28262306a36Sopenharmony_ci return; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci wl->joined = true; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* caller must hold wl->mutex */ 28962306a36Sopenharmony_cistatic int wl1251_tx_frame(struct wl1251 *wl, struct sk_buff *skb) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct ieee80211_tx_info *info; 29262306a36Sopenharmony_ci int ret = 0; 29362306a36Sopenharmony_ci u8 idx; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (info->control.hw_key) { 29862306a36Sopenharmony_ci if (unlikely(wl->monitor_present)) 29962306a36Sopenharmony_ci return -EINVAL; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci idx = info->control.hw_key->hw_key_idx; 30262306a36Sopenharmony_ci if (unlikely(wl->default_key != idx)) { 30362306a36Sopenharmony_ci ret = wl1251_acx_default_key(wl, idx); 30462306a36Sopenharmony_ci if (ret < 0) 30562306a36Sopenharmony_ci return ret; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* Enable tx path in monitor mode for packet injection */ 31062306a36Sopenharmony_ci if ((wl->vif == NULL) && !wl->joined) 31162306a36Sopenharmony_ci enable_tx_for_packet_injection(wl); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci ret = wl1251_tx_path_status(wl); 31462306a36Sopenharmony_ci if (ret < 0) 31562306a36Sopenharmony_ci return ret; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci ret = wl1251_tx_fill_hdr(wl, skb, info); 31862306a36Sopenharmony_ci if (ret < 0) 31962306a36Sopenharmony_ci return ret; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ret = wl1251_tx_send_packet(wl, skb, info); 32262306a36Sopenharmony_ci if (ret < 0) 32362306a36Sopenharmony_ci return ret; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci wl1251_tx_trigger(wl); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return ret; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_civoid wl1251_tx_work(struct work_struct *work) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct wl1251 *wl = container_of(work, struct wl1251, tx_work); 33362306a36Sopenharmony_ci struct sk_buff *skb; 33462306a36Sopenharmony_ci bool woken_up = false; 33562306a36Sopenharmony_ci int ret; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci mutex_lock(&wl->mutex); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (unlikely(wl->state == WL1251_STATE_OFF)) 34062306a36Sopenharmony_ci goto out; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci while ((skb = skb_dequeue(&wl->tx_queue))) { 34362306a36Sopenharmony_ci if (!woken_up) { 34462306a36Sopenharmony_ci ret = wl1251_ps_elp_wakeup(wl); 34562306a36Sopenharmony_ci if (ret < 0) 34662306a36Sopenharmony_ci goto out; 34762306a36Sopenharmony_ci woken_up = true; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci ret = wl1251_tx_frame(wl, skb); 35162306a36Sopenharmony_ci if (ret == -EBUSY) { 35262306a36Sopenharmony_ci skb_queue_head(&wl->tx_queue, skb); 35362306a36Sopenharmony_ci goto out; 35462306a36Sopenharmony_ci } else if (ret < 0) { 35562306a36Sopenharmony_ci dev_kfree_skb(skb); 35662306a36Sopenharmony_ci goto out; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ciout: 36162306a36Sopenharmony_ci if (woken_up) 36262306a36Sopenharmony_ci wl1251_ps_elp_sleep(wl); 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci mutex_unlock(&wl->mutex); 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic const char *wl1251_tx_parse_status(u8 status) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci /* 8 bit status field, one character per bit plus null */ 37062306a36Sopenharmony_ci static char buf[9]; 37162306a36Sopenharmony_ci int i = 0; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci memset(buf, 0, sizeof(buf)); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci if (status & TX_DMA_ERROR) 37662306a36Sopenharmony_ci buf[i++] = 'm'; 37762306a36Sopenharmony_ci if (status & TX_DISABLED) 37862306a36Sopenharmony_ci buf[i++] = 'd'; 37962306a36Sopenharmony_ci if (status & TX_RETRY_EXCEEDED) 38062306a36Sopenharmony_ci buf[i++] = 'r'; 38162306a36Sopenharmony_ci if (status & TX_TIMEOUT) 38262306a36Sopenharmony_ci buf[i++] = 't'; 38362306a36Sopenharmony_ci if (status & TX_KEY_NOT_FOUND) 38462306a36Sopenharmony_ci buf[i++] = 'k'; 38562306a36Sopenharmony_ci if (status & TX_ENCRYPT_FAIL) 38662306a36Sopenharmony_ci buf[i++] = 'e'; 38762306a36Sopenharmony_ci if (status & TX_UNAVAILABLE_PRIORITY) 38862306a36Sopenharmony_ci buf[i++] = 'p'; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* bit 0 is unused apparently */ 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return buf; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic void wl1251_tx_packet_cb(struct wl1251 *wl, 39662306a36Sopenharmony_ci struct tx_result *result) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct ieee80211_tx_info *info; 39962306a36Sopenharmony_ci struct sk_buff *skb; 40062306a36Sopenharmony_ci int hdrlen; 40162306a36Sopenharmony_ci u8 *frame; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci skb = wl->tx_frames[result->id]; 40462306a36Sopenharmony_ci if (skb == NULL) { 40562306a36Sopenharmony_ci wl1251_error("SKB for packet %d is NULL", result->id); 40662306a36Sopenharmony_ci return; 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) && 41262306a36Sopenharmony_ci !(info->flags & IEEE80211_TX_CTL_INJECTED) && 41362306a36Sopenharmony_ci (result->status == TX_SUCCESS)) 41462306a36Sopenharmony_ci info->flags |= IEEE80211_TX_STAT_ACK; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci info->status.rates[0].count = result->ack_failures + 1; 41762306a36Sopenharmony_ci wl->stats.retry_count += result->ack_failures; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* 42062306a36Sopenharmony_ci * We have to remove our private TX header before pushing 42162306a36Sopenharmony_ci * the skb back to mac80211. 42262306a36Sopenharmony_ci */ 42362306a36Sopenharmony_ci frame = skb_pull(skb, sizeof(struct tx_double_buffer_desc)); 42462306a36Sopenharmony_ci if (info->control.hw_key && 42562306a36Sopenharmony_ci info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) { 42662306a36Sopenharmony_ci hdrlen = ieee80211_get_hdrlen_from_skb(skb); 42762306a36Sopenharmony_ci memmove(frame + WL1251_TKIP_IV_SPACE, frame, hdrlen); 42862306a36Sopenharmony_ci skb_pull(skb, WL1251_TKIP_IV_SPACE); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci wl1251_debug(DEBUG_TX, "tx status id %u skb 0x%p failures %u rate 0x%x" 43262306a36Sopenharmony_ci " status 0x%x (%s)", 43362306a36Sopenharmony_ci result->id, skb, result->ack_failures, result->rate, 43462306a36Sopenharmony_ci result->status, wl1251_tx_parse_status(result->status)); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci ieee80211_tx_status(wl->hw, skb); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci wl->tx_frames[result->id] = NULL; 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci/* Called upon reception of a TX complete interrupt */ 44362306a36Sopenharmony_civoid wl1251_tx_complete(struct wl1251 *wl) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci int i, result_index, num_complete = 0, queue_len; 44662306a36Sopenharmony_ci struct tx_result *result, *result_ptr; 44762306a36Sopenharmony_ci unsigned long flags; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci if (unlikely(wl->state != WL1251_STATE_ON)) 45062306a36Sopenharmony_ci return; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci result = kmalloc_array(FW_TX_CMPLT_BLOCK_SIZE, sizeof(*result), GFP_KERNEL); 45362306a36Sopenharmony_ci if (!result) { 45462306a36Sopenharmony_ci wl1251_error("can not allocate result buffer"); 45562306a36Sopenharmony_ci return; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* First we read the result */ 45962306a36Sopenharmony_ci wl1251_mem_read(wl, wl->data_path->tx_complete_addr, result, 46062306a36Sopenharmony_ci FW_TX_CMPLT_BLOCK_SIZE * sizeof(*result)); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci result_index = wl->next_tx_complete; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) { 46562306a36Sopenharmony_ci result_ptr = &result[result_index]; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (result_ptr->done_1 == 1 && 46862306a36Sopenharmony_ci result_ptr->done_2 == 1) { 46962306a36Sopenharmony_ci wl1251_tx_packet_cb(wl, result_ptr); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci result_ptr->done_1 = 0; 47262306a36Sopenharmony_ci result_ptr->done_2 = 0; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci result_index = (result_index + 1) & 47562306a36Sopenharmony_ci (FW_TX_CMPLT_BLOCK_SIZE - 1); 47662306a36Sopenharmony_ci num_complete++; 47762306a36Sopenharmony_ci } else { 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci queue_len = skb_queue_len(&wl->tx_queue); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if ((num_complete > 0) && (queue_len > 0)) { 48562306a36Sopenharmony_ci /* firmware buffer has space, reschedule tx_work */ 48662306a36Sopenharmony_ci wl1251_debug(DEBUG_TX, "tx_complete: reschedule tx_work"); 48762306a36Sopenharmony_ci ieee80211_queue_work(wl->hw, &wl->tx_work); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (wl->tx_queue_stopped && 49162306a36Sopenharmony_ci queue_len <= WL1251_TX_QUEUE_LOW_WATERMARK) { 49262306a36Sopenharmony_ci /* tx_queue has space, restart queues */ 49362306a36Sopenharmony_ci wl1251_debug(DEBUG_TX, "tx_complete: waking queues"); 49462306a36Sopenharmony_ci spin_lock_irqsave(&wl->wl_lock, flags); 49562306a36Sopenharmony_ci ieee80211_wake_queues(wl->hw); 49662306a36Sopenharmony_ci wl->tx_queue_stopped = false; 49762306a36Sopenharmony_ci spin_unlock_irqrestore(&wl->wl_lock, flags); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Every completed frame needs to be acknowledged */ 50162306a36Sopenharmony_ci if (num_complete) { 50262306a36Sopenharmony_ci /* 50362306a36Sopenharmony_ci * If we've wrapped, we have to clear 50462306a36Sopenharmony_ci * the results in 2 steps. 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_ci if (result_index > wl->next_tx_complete) { 50762306a36Sopenharmony_ci /* Only 1 write is needed */ 50862306a36Sopenharmony_ci wl1251_mem_write(wl, 50962306a36Sopenharmony_ci wl->data_path->tx_complete_addr + 51062306a36Sopenharmony_ci (wl->next_tx_complete * 51162306a36Sopenharmony_ci sizeof(struct tx_result)), 51262306a36Sopenharmony_ci &result[wl->next_tx_complete], 51362306a36Sopenharmony_ci num_complete * 51462306a36Sopenharmony_ci sizeof(struct tx_result)); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci } else if (result_index < wl->next_tx_complete) { 51862306a36Sopenharmony_ci /* 2 writes are needed */ 51962306a36Sopenharmony_ci wl1251_mem_write(wl, 52062306a36Sopenharmony_ci wl->data_path->tx_complete_addr + 52162306a36Sopenharmony_ci (wl->next_tx_complete * 52262306a36Sopenharmony_ci sizeof(struct tx_result)), 52362306a36Sopenharmony_ci &result[wl->next_tx_complete], 52462306a36Sopenharmony_ci (FW_TX_CMPLT_BLOCK_SIZE - 52562306a36Sopenharmony_ci wl->next_tx_complete) * 52662306a36Sopenharmony_ci sizeof(struct tx_result)); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci wl1251_mem_write(wl, 52962306a36Sopenharmony_ci wl->data_path->tx_complete_addr, 53062306a36Sopenharmony_ci result, 53162306a36Sopenharmony_ci (num_complete - 53262306a36Sopenharmony_ci FW_TX_CMPLT_BLOCK_SIZE + 53362306a36Sopenharmony_ci wl->next_tx_complete) * 53462306a36Sopenharmony_ci sizeof(struct tx_result)); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci } else { 53762306a36Sopenharmony_ci /* We have to write the whole array */ 53862306a36Sopenharmony_ci wl1251_mem_write(wl, 53962306a36Sopenharmony_ci wl->data_path->tx_complete_addr, 54062306a36Sopenharmony_ci result, 54162306a36Sopenharmony_ci FW_TX_CMPLT_BLOCK_SIZE * 54262306a36Sopenharmony_ci sizeof(struct tx_result)); 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci kfree(result); 54862306a36Sopenharmony_ci wl->next_tx_complete = result_index; 54962306a36Sopenharmony_ci} 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci/* caller must hold wl->mutex */ 55262306a36Sopenharmony_civoid wl1251_tx_flush(struct wl1251 *wl) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci int i; 55562306a36Sopenharmony_ci struct sk_buff *skb; 55662306a36Sopenharmony_ci struct ieee80211_tx_info *info; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* TX failure */ 55962306a36Sopenharmony_ci/* control->flags = 0; FIXME */ 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci while ((skb = skb_dequeue(&wl->tx_queue))) { 56262306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci wl1251_debug(DEBUG_TX, "flushing skb 0x%p", skb); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) 56762306a36Sopenharmony_ci continue; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci ieee80211_tx_status(wl->hw, skb); 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci for (i = 0; i < FW_TX_CMPLT_BLOCK_SIZE; i++) 57362306a36Sopenharmony_ci if (wl->tx_frames[i] != NULL) { 57462306a36Sopenharmony_ci skb = wl->tx_frames[i]; 57562306a36Sopenharmony_ci info = IEEE80211_SKB_CB(skb); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (!(info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)) 57862306a36Sopenharmony_ci continue; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci ieee80211_tx_status(wl->hw, skb); 58162306a36Sopenharmony_ci wl->tx_frames[i] = NULL; 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci} 584