162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci Broadcom B43legacy wireless driver 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci Transmission (TX/RX) related functions. 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci Copyright (C) 2005 Martin Langer <martin-langer@gmx.de> 962306a36Sopenharmony_ci Copyright (C) 2005 Stefano Brivio <stefano.brivio@polimi.it> 1062306a36Sopenharmony_ci Copyright (C) 2005, 2006 Michael Buesch <m@bues.ch> 1162306a36Sopenharmony_ci Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org> 1262306a36Sopenharmony_ci Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch> 1362306a36Sopenharmony_ci Copyright (C) 2007 Larry Finger <Larry.Finger@lwfinger.net> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci*/ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <net/dst.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "xmit.h" 2162306a36Sopenharmony_ci#include "phy.h" 2262306a36Sopenharmony_ci#include "dma.h" 2362306a36Sopenharmony_ci#include "pio.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Extract the bitrate out of a CCK PLCP header. */ 2762306a36Sopenharmony_cistatic u8 b43legacy_plcp_get_bitrate_idx_cck(struct b43legacy_plcp_hdr6 *plcp) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci switch (plcp->raw[0]) { 3062306a36Sopenharmony_ci case 0x0A: 3162306a36Sopenharmony_ci return 0; 3262306a36Sopenharmony_ci case 0x14: 3362306a36Sopenharmony_ci return 1; 3462306a36Sopenharmony_ci case 0x37: 3562306a36Sopenharmony_ci return 2; 3662306a36Sopenharmony_ci case 0x6E: 3762306a36Sopenharmony_ci return 3; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci B43legacy_BUG_ON(1); 4062306a36Sopenharmony_ci return -1; 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* Extract the bitrate out of an OFDM PLCP header. */ 4462306a36Sopenharmony_cistatic u8 b43legacy_plcp_get_bitrate_idx_ofdm(struct b43legacy_plcp_hdr6 *plcp, 4562306a36Sopenharmony_ci bool aphy) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci int base = aphy ? 0 : 4; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci switch (plcp->raw[0] & 0xF) { 5062306a36Sopenharmony_ci case 0xB: 5162306a36Sopenharmony_ci return base + 0; 5262306a36Sopenharmony_ci case 0xF: 5362306a36Sopenharmony_ci return base + 1; 5462306a36Sopenharmony_ci case 0xA: 5562306a36Sopenharmony_ci return base + 2; 5662306a36Sopenharmony_ci case 0xE: 5762306a36Sopenharmony_ci return base + 3; 5862306a36Sopenharmony_ci case 0x9: 5962306a36Sopenharmony_ci return base + 4; 6062306a36Sopenharmony_ci case 0xD: 6162306a36Sopenharmony_ci return base + 5; 6262306a36Sopenharmony_ci case 0x8: 6362306a36Sopenharmony_ci return base + 6; 6462306a36Sopenharmony_ci case 0xC: 6562306a36Sopenharmony_ci return base + 7; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci B43legacy_BUG_ON(1); 6862306a36Sopenharmony_ci return -1; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ciu8 b43legacy_plcp_get_ratecode_cck(const u8 bitrate) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci switch (bitrate) { 7462306a36Sopenharmony_ci case B43legacy_CCK_RATE_1MB: 7562306a36Sopenharmony_ci return 0x0A; 7662306a36Sopenharmony_ci case B43legacy_CCK_RATE_2MB: 7762306a36Sopenharmony_ci return 0x14; 7862306a36Sopenharmony_ci case B43legacy_CCK_RATE_5MB: 7962306a36Sopenharmony_ci return 0x37; 8062306a36Sopenharmony_ci case B43legacy_CCK_RATE_11MB: 8162306a36Sopenharmony_ci return 0x6E; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci B43legacy_BUG_ON(1); 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ciu8 b43legacy_plcp_get_ratecode_ofdm(const u8 bitrate) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci switch (bitrate) { 9062306a36Sopenharmony_ci case B43legacy_OFDM_RATE_6MB: 9162306a36Sopenharmony_ci return 0xB; 9262306a36Sopenharmony_ci case B43legacy_OFDM_RATE_9MB: 9362306a36Sopenharmony_ci return 0xF; 9462306a36Sopenharmony_ci case B43legacy_OFDM_RATE_12MB: 9562306a36Sopenharmony_ci return 0xA; 9662306a36Sopenharmony_ci case B43legacy_OFDM_RATE_18MB: 9762306a36Sopenharmony_ci return 0xE; 9862306a36Sopenharmony_ci case B43legacy_OFDM_RATE_24MB: 9962306a36Sopenharmony_ci return 0x9; 10062306a36Sopenharmony_ci case B43legacy_OFDM_RATE_36MB: 10162306a36Sopenharmony_ci return 0xD; 10262306a36Sopenharmony_ci case B43legacy_OFDM_RATE_48MB: 10362306a36Sopenharmony_ci return 0x8; 10462306a36Sopenharmony_ci case B43legacy_OFDM_RATE_54MB: 10562306a36Sopenharmony_ci return 0xC; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci B43legacy_BUG_ON(1); 10862306a36Sopenharmony_ci return 0; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_civoid b43legacy_generate_plcp_hdr(struct b43legacy_plcp_hdr4 *plcp, 11262306a36Sopenharmony_ci const u16 octets, const u8 bitrate) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci __le32 *data = &(plcp->data); 11562306a36Sopenharmony_ci __u8 *raw = plcp->raw; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (b43legacy_is_ofdm_rate(bitrate)) { 11862306a36Sopenharmony_ci u16 d; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci d = b43legacy_plcp_get_ratecode_ofdm(bitrate); 12162306a36Sopenharmony_ci B43legacy_WARN_ON(octets & 0xF000); 12262306a36Sopenharmony_ci d |= (octets << 5); 12362306a36Sopenharmony_ci *data = cpu_to_le32(d); 12462306a36Sopenharmony_ci } else { 12562306a36Sopenharmony_ci u32 plen; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci plen = octets * 16 / bitrate; 12862306a36Sopenharmony_ci if ((octets * 16 % bitrate) > 0) { 12962306a36Sopenharmony_ci plen++; 13062306a36Sopenharmony_ci if ((bitrate == B43legacy_CCK_RATE_11MB) 13162306a36Sopenharmony_ci && ((octets * 8 % 11) < 4)) 13262306a36Sopenharmony_ci raw[1] = 0x84; 13362306a36Sopenharmony_ci else 13462306a36Sopenharmony_ci raw[1] = 0x04; 13562306a36Sopenharmony_ci } else 13662306a36Sopenharmony_ci raw[1] = 0x04; 13762306a36Sopenharmony_ci *data |= cpu_to_le32(plen << 16); 13862306a36Sopenharmony_ci raw[0] = b43legacy_plcp_get_ratecode_cck(bitrate); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic u8 b43legacy_calc_fallback_rate(u8 bitrate) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci switch (bitrate) { 14562306a36Sopenharmony_ci case B43legacy_CCK_RATE_1MB: 14662306a36Sopenharmony_ci return B43legacy_CCK_RATE_1MB; 14762306a36Sopenharmony_ci case B43legacy_CCK_RATE_2MB: 14862306a36Sopenharmony_ci return B43legacy_CCK_RATE_1MB; 14962306a36Sopenharmony_ci case B43legacy_CCK_RATE_5MB: 15062306a36Sopenharmony_ci return B43legacy_CCK_RATE_2MB; 15162306a36Sopenharmony_ci case B43legacy_CCK_RATE_11MB: 15262306a36Sopenharmony_ci return B43legacy_CCK_RATE_5MB; 15362306a36Sopenharmony_ci case B43legacy_OFDM_RATE_6MB: 15462306a36Sopenharmony_ci return B43legacy_CCK_RATE_5MB; 15562306a36Sopenharmony_ci case B43legacy_OFDM_RATE_9MB: 15662306a36Sopenharmony_ci return B43legacy_OFDM_RATE_6MB; 15762306a36Sopenharmony_ci case B43legacy_OFDM_RATE_12MB: 15862306a36Sopenharmony_ci return B43legacy_OFDM_RATE_9MB; 15962306a36Sopenharmony_ci case B43legacy_OFDM_RATE_18MB: 16062306a36Sopenharmony_ci return B43legacy_OFDM_RATE_12MB; 16162306a36Sopenharmony_ci case B43legacy_OFDM_RATE_24MB: 16262306a36Sopenharmony_ci return B43legacy_OFDM_RATE_18MB; 16362306a36Sopenharmony_ci case B43legacy_OFDM_RATE_36MB: 16462306a36Sopenharmony_ci return B43legacy_OFDM_RATE_24MB; 16562306a36Sopenharmony_ci case B43legacy_OFDM_RATE_48MB: 16662306a36Sopenharmony_ci return B43legacy_OFDM_RATE_36MB; 16762306a36Sopenharmony_ci case B43legacy_OFDM_RATE_54MB: 16862306a36Sopenharmony_ci return B43legacy_OFDM_RATE_48MB; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci B43legacy_BUG_ON(1); 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic int generate_txhdr_fw3(struct b43legacy_wldev *dev, 17562306a36Sopenharmony_ci struct b43legacy_txhdr_fw3 *txhdr, 17662306a36Sopenharmony_ci const unsigned char *fragment_data, 17762306a36Sopenharmony_ci unsigned int fragment_len, 17862306a36Sopenharmony_ci struct ieee80211_tx_info *info, 17962306a36Sopenharmony_ci u16 cookie) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci const struct ieee80211_hdr *wlhdr; 18262306a36Sopenharmony_ci int use_encryption = !!info->control.hw_key; 18362306a36Sopenharmony_ci u8 rate; 18462306a36Sopenharmony_ci struct ieee80211_rate *rate_fb; 18562306a36Sopenharmony_ci int rate_ofdm; 18662306a36Sopenharmony_ci int rate_fb_ofdm; 18762306a36Sopenharmony_ci unsigned int plcp_fragment_len; 18862306a36Sopenharmony_ci u32 mac_ctl = 0; 18962306a36Sopenharmony_ci u16 phy_ctl = 0; 19062306a36Sopenharmony_ci struct ieee80211_rate *tx_rate; 19162306a36Sopenharmony_ci struct ieee80211_tx_rate *rates; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci wlhdr = (const struct ieee80211_hdr *)fragment_data; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci memset(txhdr, 0, sizeof(*txhdr)); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci tx_rate = ieee80211_get_tx_rate(dev->wl->hw, info); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci rate = tx_rate->hw_value; 20062306a36Sopenharmony_ci rate_ofdm = b43legacy_is_ofdm_rate(rate); 20162306a36Sopenharmony_ci rate_fb = ieee80211_get_alt_retry_rate(dev->wl->hw, info, 0) ? : tx_rate; 20262306a36Sopenharmony_ci rate_fb_ofdm = b43legacy_is_ofdm_rate(rate_fb->hw_value); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci txhdr->mac_frame_ctl = wlhdr->frame_control; 20562306a36Sopenharmony_ci memcpy(txhdr->tx_receiver, wlhdr->addr1, ETH_ALEN); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* Calculate duration for fallback rate */ 20862306a36Sopenharmony_ci if ((rate_fb->hw_value == rate) || 20962306a36Sopenharmony_ci (wlhdr->duration_id & cpu_to_le16(0x8000)) || 21062306a36Sopenharmony_ci (wlhdr->duration_id == cpu_to_le16(0))) { 21162306a36Sopenharmony_ci /* If the fallback rate equals the normal rate or the 21262306a36Sopenharmony_ci * dur_id field contains an AID, CFP magic or 0, 21362306a36Sopenharmony_ci * use the original dur_id field. */ 21462306a36Sopenharmony_ci txhdr->dur_fb = wlhdr->duration_id; 21562306a36Sopenharmony_ci } else { 21662306a36Sopenharmony_ci txhdr->dur_fb = ieee80211_generic_frame_duration(dev->wl->hw, 21762306a36Sopenharmony_ci info->control.vif, 21862306a36Sopenharmony_ci info->band, 21962306a36Sopenharmony_ci fragment_len, 22062306a36Sopenharmony_ci rate_fb); 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci plcp_fragment_len = fragment_len + FCS_LEN; 22462306a36Sopenharmony_ci if (use_encryption) { 22562306a36Sopenharmony_ci u8 key_idx = info->control.hw_key->hw_key_idx; 22662306a36Sopenharmony_ci struct b43legacy_key *key; 22762306a36Sopenharmony_ci int wlhdr_len; 22862306a36Sopenharmony_ci size_t iv_len; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci B43legacy_WARN_ON(key_idx >= dev->max_nr_keys); 23162306a36Sopenharmony_ci key = &(dev->key[key_idx]); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (key->enabled) { 23462306a36Sopenharmony_ci /* Hardware appends ICV. */ 23562306a36Sopenharmony_ci plcp_fragment_len += info->control.hw_key->icv_len; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci key_idx = b43legacy_kidx_to_fw(dev, key_idx); 23862306a36Sopenharmony_ci mac_ctl |= (key_idx << B43legacy_TX4_MAC_KEYIDX_SHIFT) & 23962306a36Sopenharmony_ci B43legacy_TX4_MAC_KEYIDX; 24062306a36Sopenharmony_ci mac_ctl |= (key->algorithm << 24162306a36Sopenharmony_ci B43legacy_TX4_MAC_KEYALG_SHIFT) & 24262306a36Sopenharmony_ci B43legacy_TX4_MAC_KEYALG; 24362306a36Sopenharmony_ci wlhdr_len = ieee80211_hdrlen(wlhdr->frame_control); 24462306a36Sopenharmony_ci iv_len = min_t(size_t, info->control.hw_key->iv_len, 24562306a36Sopenharmony_ci ARRAY_SIZE(txhdr->iv)); 24662306a36Sopenharmony_ci memcpy(txhdr->iv, ((u8 *)wlhdr) + wlhdr_len, iv_len); 24762306a36Sopenharmony_ci } else { 24862306a36Sopenharmony_ci /* This key is invalid. This might only happen 24962306a36Sopenharmony_ci * in a short timeframe after machine resume before 25062306a36Sopenharmony_ci * we were able to reconfigure keys. 25162306a36Sopenharmony_ci * Drop this packet completely. Do not transmit it 25262306a36Sopenharmony_ci * unencrypted to avoid leaking information. */ 25362306a36Sopenharmony_ci return -ENOKEY; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) 25762306a36Sopenharmony_ci (&txhdr->plcp), plcp_fragment_len, 25862306a36Sopenharmony_ci rate); 25962306a36Sopenharmony_ci b43legacy_generate_plcp_hdr(&txhdr->plcp_fb, plcp_fragment_len, 26062306a36Sopenharmony_ci rate_fb->hw_value); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* PHY TX Control word */ 26362306a36Sopenharmony_ci if (rate_ofdm) 26462306a36Sopenharmony_ci phy_ctl |= B43legacy_TX4_PHY_ENC_OFDM; 26562306a36Sopenharmony_ci if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) 26662306a36Sopenharmony_ci phy_ctl |= B43legacy_TX4_PHY_SHORTPRMBL; 26762306a36Sopenharmony_ci phy_ctl |= B43legacy_TX4_PHY_ANTLAST; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* MAC control */ 27062306a36Sopenharmony_ci rates = info->control.rates; 27162306a36Sopenharmony_ci if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) 27262306a36Sopenharmony_ci mac_ctl |= B43legacy_TX4_MAC_ACK; 27362306a36Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) 27462306a36Sopenharmony_ci mac_ctl |= B43legacy_TX4_MAC_HWSEQ; 27562306a36Sopenharmony_ci if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) 27662306a36Sopenharmony_ci mac_ctl |= B43legacy_TX4_MAC_STMSDU; 27762306a36Sopenharmony_ci if (rate_fb_ofdm) 27862306a36Sopenharmony_ci mac_ctl |= B43legacy_TX4_MAC_FALLBACKOFDM; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Overwrite rates[0].count to make the retry calculation 28162306a36Sopenharmony_ci * in the tx status easier. need the actual retry limit to 28262306a36Sopenharmony_ci * detect whether the fallback rate was used. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_ci if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || 28562306a36Sopenharmony_ci (rates[0].count <= dev->wl->hw->conf.long_frame_max_tx_count)) { 28662306a36Sopenharmony_ci rates[0].count = dev->wl->hw->conf.long_frame_max_tx_count; 28762306a36Sopenharmony_ci mac_ctl |= B43legacy_TX4_MAC_LONGFRAME; 28862306a36Sopenharmony_ci } else { 28962306a36Sopenharmony_ci rates[0].count = dev->wl->hw->conf.short_frame_max_tx_count; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Generate the RTS or CTS-to-self frame */ 29362306a36Sopenharmony_ci if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) || 29462306a36Sopenharmony_ci (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) { 29562306a36Sopenharmony_ci unsigned int len; 29662306a36Sopenharmony_ci struct ieee80211_hdr *hdr; 29762306a36Sopenharmony_ci int rts_rate; 29862306a36Sopenharmony_ci int rts_rate_fb; 29962306a36Sopenharmony_ci int rts_rate_fb_ofdm; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci rts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info)->hw_value; 30262306a36Sopenharmony_ci rts_rate_fb = b43legacy_calc_fallback_rate(rts_rate); 30362306a36Sopenharmony_ci rts_rate_fb_ofdm = b43legacy_is_ofdm_rate(rts_rate_fb); 30462306a36Sopenharmony_ci if (rts_rate_fb_ofdm) 30562306a36Sopenharmony_ci mac_ctl |= B43legacy_TX4_MAC_CTSFALLBACKOFDM; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) { 30862306a36Sopenharmony_ci ieee80211_ctstoself_get(dev->wl->hw, 30962306a36Sopenharmony_ci info->control.vif, 31062306a36Sopenharmony_ci fragment_data, 31162306a36Sopenharmony_ci fragment_len, info, 31262306a36Sopenharmony_ci (struct ieee80211_cts *) 31362306a36Sopenharmony_ci (txhdr->rts_frame)); 31462306a36Sopenharmony_ci mac_ctl |= B43legacy_TX4_MAC_SENDCTS; 31562306a36Sopenharmony_ci len = sizeof(struct ieee80211_cts); 31662306a36Sopenharmony_ci } else { 31762306a36Sopenharmony_ci ieee80211_rts_get(dev->wl->hw, 31862306a36Sopenharmony_ci info->control.vif, 31962306a36Sopenharmony_ci fragment_data, fragment_len, info, 32062306a36Sopenharmony_ci (struct ieee80211_rts *) 32162306a36Sopenharmony_ci (txhdr->rts_frame)); 32262306a36Sopenharmony_ci mac_ctl |= B43legacy_TX4_MAC_SENDRTS; 32362306a36Sopenharmony_ci len = sizeof(struct ieee80211_rts); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci len += FCS_LEN; 32662306a36Sopenharmony_ci b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *) 32762306a36Sopenharmony_ci (&txhdr->rts_plcp), 32862306a36Sopenharmony_ci len, rts_rate); 32962306a36Sopenharmony_ci b43legacy_generate_plcp_hdr(&txhdr->rts_plcp_fb, 33062306a36Sopenharmony_ci len, rts_rate_fb); 33162306a36Sopenharmony_ci hdr = (struct ieee80211_hdr *)(&txhdr->rts_frame); 33262306a36Sopenharmony_ci txhdr->rts_dur_fb = hdr->duration_id; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Magic cookie */ 33662306a36Sopenharmony_ci txhdr->cookie = cpu_to_le16(cookie); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* Apply the bitfields */ 33962306a36Sopenharmony_ci txhdr->mac_ctl = cpu_to_le32(mac_ctl); 34062306a36Sopenharmony_ci txhdr->phy_ctl = cpu_to_le16(phy_ctl); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ciint b43legacy_generate_txhdr(struct b43legacy_wldev *dev, 34662306a36Sopenharmony_ci u8 *txhdr, 34762306a36Sopenharmony_ci const unsigned char *fragment_data, 34862306a36Sopenharmony_ci unsigned int fragment_len, 34962306a36Sopenharmony_ci struct ieee80211_tx_info *info, 35062306a36Sopenharmony_ci u16 cookie) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci return generate_txhdr_fw3(dev, (struct b43legacy_txhdr_fw3 *)txhdr, 35362306a36Sopenharmony_ci fragment_data, fragment_len, 35462306a36Sopenharmony_ci info, cookie); 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic s8 b43legacy_rssi_postprocess(struct b43legacy_wldev *dev, 35862306a36Sopenharmony_ci u8 in_rssi, int ofdm, 35962306a36Sopenharmony_ci int adjust_2053, int adjust_2050) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct b43legacy_phy *phy = &dev->phy; 36262306a36Sopenharmony_ci s32 tmp; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci switch (phy->radio_ver) { 36562306a36Sopenharmony_ci case 0x2050: 36662306a36Sopenharmony_ci if (ofdm) { 36762306a36Sopenharmony_ci tmp = in_rssi; 36862306a36Sopenharmony_ci if (tmp > 127) 36962306a36Sopenharmony_ci tmp -= 256; 37062306a36Sopenharmony_ci tmp *= 73; 37162306a36Sopenharmony_ci tmp /= 64; 37262306a36Sopenharmony_ci if (adjust_2050) 37362306a36Sopenharmony_ci tmp += 25; 37462306a36Sopenharmony_ci else 37562306a36Sopenharmony_ci tmp -= 3; 37662306a36Sopenharmony_ci } else { 37762306a36Sopenharmony_ci if (dev->dev->bus->sprom.boardflags_lo 37862306a36Sopenharmony_ci & B43legacy_BFL_RSSI) { 37962306a36Sopenharmony_ci if (in_rssi > 63) 38062306a36Sopenharmony_ci in_rssi = 63; 38162306a36Sopenharmony_ci tmp = phy->nrssi_lt[in_rssi]; 38262306a36Sopenharmony_ci tmp = 31 - tmp; 38362306a36Sopenharmony_ci tmp *= -131; 38462306a36Sopenharmony_ci tmp /= 128; 38562306a36Sopenharmony_ci tmp -= 57; 38662306a36Sopenharmony_ci } else { 38762306a36Sopenharmony_ci tmp = in_rssi; 38862306a36Sopenharmony_ci tmp = 31 - tmp; 38962306a36Sopenharmony_ci tmp *= -149; 39062306a36Sopenharmony_ci tmp /= 128; 39162306a36Sopenharmony_ci tmp -= 68; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci if (phy->type == B43legacy_PHYTYPE_G && 39462306a36Sopenharmony_ci adjust_2050) 39562306a36Sopenharmony_ci tmp += 25; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci case 0x2060: 39962306a36Sopenharmony_ci if (in_rssi > 127) 40062306a36Sopenharmony_ci tmp = in_rssi - 256; 40162306a36Sopenharmony_ci else 40262306a36Sopenharmony_ci tmp = in_rssi; 40362306a36Sopenharmony_ci break; 40462306a36Sopenharmony_ci default: 40562306a36Sopenharmony_ci tmp = in_rssi; 40662306a36Sopenharmony_ci tmp -= 11; 40762306a36Sopenharmony_ci tmp *= 103; 40862306a36Sopenharmony_ci tmp /= 64; 40962306a36Sopenharmony_ci if (adjust_2053) 41062306a36Sopenharmony_ci tmp -= 109; 41162306a36Sopenharmony_ci else 41262306a36Sopenharmony_ci tmp -= 83; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return (s8)tmp; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_civoid b43legacy_rx(struct b43legacy_wldev *dev, 41962306a36Sopenharmony_ci struct sk_buff *skb, 42062306a36Sopenharmony_ci const void *_rxhdr) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct ieee80211_rx_status status; 42362306a36Sopenharmony_ci struct b43legacy_plcp_hdr6 *plcp; 42462306a36Sopenharmony_ci struct ieee80211_hdr *wlhdr; 42562306a36Sopenharmony_ci const struct b43legacy_rxhdr_fw3 *rxhdr = _rxhdr; 42662306a36Sopenharmony_ci __le16 fctl; 42762306a36Sopenharmony_ci u16 phystat0; 42862306a36Sopenharmony_ci u16 phystat3; 42962306a36Sopenharmony_ci u16 chanstat; 43062306a36Sopenharmony_ci u16 mactime; 43162306a36Sopenharmony_ci u32 macstat; 43262306a36Sopenharmony_ci u16 chanid; 43362306a36Sopenharmony_ci u8 jssi; 43462306a36Sopenharmony_ci int padding; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci memset(&status, 0, sizeof(status)); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Get metadata about the frame from the header. */ 43962306a36Sopenharmony_ci phystat0 = le16_to_cpu(rxhdr->phy_status0); 44062306a36Sopenharmony_ci phystat3 = le16_to_cpu(rxhdr->phy_status3); 44162306a36Sopenharmony_ci jssi = rxhdr->jssi; 44262306a36Sopenharmony_ci macstat = le16_to_cpu(rxhdr->mac_status); 44362306a36Sopenharmony_ci mactime = le16_to_cpu(rxhdr->mac_time); 44462306a36Sopenharmony_ci chanstat = le16_to_cpu(rxhdr->channel); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci if (macstat & B43legacy_RX_MAC_FCSERR) 44762306a36Sopenharmony_ci dev->wl->ieee_stats.dot11FCSErrorCount++; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci /* Skip PLCP and padding */ 45062306a36Sopenharmony_ci padding = (macstat & B43legacy_RX_MAC_PADDING) ? 2 : 0; 45162306a36Sopenharmony_ci if (unlikely(skb->len < (sizeof(struct b43legacy_plcp_hdr6) + 45262306a36Sopenharmony_ci padding))) { 45362306a36Sopenharmony_ci b43legacydbg(dev->wl, "RX: Packet size underrun (1)\n"); 45462306a36Sopenharmony_ci goto drop; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci plcp = (struct b43legacy_plcp_hdr6 *)(skb->data + padding); 45762306a36Sopenharmony_ci skb_pull(skb, sizeof(struct b43legacy_plcp_hdr6) + padding); 45862306a36Sopenharmony_ci /* The skb contains the Wireless Header + payload data now */ 45962306a36Sopenharmony_ci if (unlikely(skb->len < (2+2+6/*minimum hdr*/ + FCS_LEN))) { 46062306a36Sopenharmony_ci b43legacydbg(dev->wl, "RX: Packet size underrun (2)\n"); 46162306a36Sopenharmony_ci goto drop; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci wlhdr = (struct ieee80211_hdr *)(skb->data); 46462306a36Sopenharmony_ci fctl = wlhdr->frame_control; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if ((macstat & B43legacy_RX_MAC_DEC) && 46762306a36Sopenharmony_ci !(macstat & B43legacy_RX_MAC_DECERR)) { 46862306a36Sopenharmony_ci unsigned int keyidx; 46962306a36Sopenharmony_ci int wlhdr_len; 47062306a36Sopenharmony_ci int iv_len; 47162306a36Sopenharmony_ci int icv_len; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci keyidx = ((macstat & B43legacy_RX_MAC_KEYIDX) 47462306a36Sopenharmony_ci >> B43legacy_RX_MAC_KEYIDX_SHIFT); 47562306a36Sopenharmony_ci /* We must adjust the key index here. We want the "physical" 47662306a36Sopenharmony_ci * key index, but the ucode passed it slightly different. 47762306a36Sopenharmony_ci */ 47862306a36Sopenharmony_ci keyidx = b43legacy_kidx_to_raw(dev, keyidx); 47962306a36Sopenharmony_ci B43legacy_WARN_ON(keyidx >= dev->max_nr_keys); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (dev->key[keyidx].algorithm != B43legacy_SEC_ALGO_NONE) { 48262306a36Sopenharmony_ci /* Remove PROTECTED flag to mark it as decrypted. */ 48362306a36Sopenharmony_ci B43legacy_WARN_ON(!ieee80211_has_protected(fctl)); 48462306a36Sopenharmony_ci fctl &= ~cpu_to_le16(IEEE80211_FCTL_PROTECTED); 48562306a36Sopenharmony_ci wlhdr->frame_control = fctl; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci wlhdr_len = ieee80211_hdrlen(fctl); 48862306a36Sopenharmony_ci if (unlikely(skb->len < (wlhdr_len + 3))) { 48962306a36Sopenharmony_ci b43legacydbg(dev->wl, "RX: Packet size" 49062306a36Sopenharmony_ci " underrun3\n"); 49162306a36Sopenharmony_ci goto drop; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci if (skb->data[wlhdr_len + 3] & (1 << 5)) { 49462306a36Sopenharmony_ci /* The Ext-IV Bit is set in the "KeyID" 49562306a36Sopenharmony_ci * octet of the IV. 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_ci iv_len = 8; 49862306a36Sopenharmony_ci icv_len = 8; 49962306a36Sopenharmony_ci } else { 50062306a36Sopenharmony_ci iv_len = 4; 50162306a36Sopenharmony_ci icv_len = 4; 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci if (unlikely(skb->len < (wlhdr_len + iv_len + 50462306a36Sopenharmony_ci icv_len))) { 50562306a36Sopenharmony_ci b43legacydbg(dev->wl, "RX: Packet size" 50662306a36Sopenharmony_ci " underrun4\n"); 50762306a36Sopenharmony_ci goto drop; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci /* Remove the IV */ 51062306a36Sopenharmony_ci memmove(skb->data + iv_len, skb->data, wlhdr_len); 51162306a36Sopenharmony_ci skb_pull(skb, iv_len); 51262306a36Sopenharmony_ci /* Remove the ICV */ 51362306a36Sopenharmony_ci skb_trim(skb, skb->len - icv_len); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci status.flag |= RX_FLAG_DECRYPTED; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci status.signal = b43legacy_rssi_postprocess(dev, jssi, 52062306a36Sopenharmony_ci (phystat0 & B43legacy_RX_PHYST0_OFDM), 52162306a36Sopenharmony_ci (phystat0 & B43legacy_RX_PHYST0_GAINCTL), 52262306a36Sopenharmony_ci (phystat3 & B43legacy_RX_PHYST3_TRSTATE)); 52362306a36Sopenharmony_ci /* change to support A PHY */ 52462306a36Sopenharmony_ci if (phystat0 & B43legacy_RX_PHYST0_OFDM) 52562306a36Sopenharmony_ci status.rate_idx = b43legacy_plcp_get_bitrate_idx_ofdm(plcp, false); 52662306a36Sopenharmony_ci else 52762306a36Sopenharmony_ci status.rate_idx = b43legacy_plcp_get_bitrate_idx_cck(plcp); 52862306a36Sopenharmony_ci status.antenna = !!(phystat0 & B43legacy_RX_PHYST0_ANT); 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* 53162306a36Sopenharmony_ci * All frames on monitor interfaces and beacons always need a full 53262306a36Sopenharmony_ci * 64-bit timestamp. Monitor interfaces need it for diagnostic 53362306a36Sopenharmony_ci * purposes and beacons for IBSS merging. 53462306a36Sopenharmony_ci * This code assumes we get to process the packet within 16 bits 53562306a36Sopenharmony_ci * of timestamp, i.e. about 65 milliseconds after the PHY received 53662306a36Sopenharmony_ci * the first symbol. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci if (ieee80211_is_beacon(fctl) || dev->wl->radiotap_enabled) { 53962306a36Sopenharmony_ci u16 low_mactime_now; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci b43legacy_tsf_read(dev, &status.mactime); 54262306a36Sopenharmony_ci low_mactime_now = status.mactime; 54362306a36Sopenharmony_ci status.mactime = status.mactime & ~0xFFFFULL; 54462306a36Sopenharmony_ci status.mactime += mactime; 54562306a36Sopenharmony_ci if (low_mactime_now <= mactime) 54662306a36Sopenharmony_ci status.mactime -= 0x10000; 54762306a36Sopenharmony_ci status.flag |= RX_FLAG_MACTIME_START; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci chanid = (chanstat & B43legacy_RX_CHAN_ID) >> 55162306a36Sopenharmony_ci B43legacy_RX_CHAN_ID_SHIFT; 55262306a36Sopenharmony_ci switch (chanstat & B43legacy_RX_CHAN_PHYTYPE) { 55362306a36Sopenharmony_ci case B43legacy_PHYTYPE_B: 55462306a36Sopenharmony_ci case B43legacy_PHYTYPE_G: 55562306a36Sopenharmony_ci status.band = NL80211_BAND_2GHZ; 55662306a36Sopenharmony_ci status.freq = chanid + 2400; 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci default: 55962306a36Sopenharmony_ci b43legacywarn(dev->wl, "Unexpected value for chanstat (0x%X)\n", 56062306a36Sopenharmony_ci chanstat); 56162306a36Sopenharmony_ci goto drop; 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status)); 56562306a36Sopenharmony_ci ieee80211_rx_irqsafe(dev->wl->hw, skb); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci return; 56862306a36Sopenharmony_cidrop: 56962306a36Sopenharmony_ci b43legacydbg(dev->wl, "RX: Packet dropped\n"); 57062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_civoid b43legacy_handle_txstatus(struct b43legacy_wldev *dev, 57462306a36Sopenharmony_ci const struct b43legacy_txstatus *status) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci b43legacy_debugfs_log_txstat(dev, status); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (status->intermediate) 57962306a36Sopenharmony_ci return; 58062306a36Sopenharmony_ci if (status->for_ampdu) 58162306a36Sopenharmony_ci return; 58262306a36Sopenharmony_ci if (!status->acked) 58362306a36Sopenharmony_ci dev->wl->ieee_stats.dot11ACKFailureCount++; 58462306a36Sopenharmony_ci if (status->rts_count) { 58562306a36Sopenharmony_ci if (status->rts_count == 0xF) /* FIXME */ 58662306a36Sopenharmony_ci dev->wl->ieee_stats.dot11RTSFailureCount++; 58762306a36Sopenharmony_ci else 58862306a36Sopenharmony_ci dev->wl->ieee_stats.dot11RTSSuccessCount++; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci if (b43legacy_using_pio(dev)) 59262306a36Sopenharmony_ci b43legacy_pio_handle_txstatus(dev, status); 59362306a36Sopenharmony_ci else 59462306a36Sopenharmony_ci b43legacy_dma_handle_txstatus(dev, status); 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci/* Handle TX status report as received through DMA/PIO queues */ 59862306a36Sopenharmony_civoid b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev, 59962306a36Sopenharmony_ci const struct b43legacy_hwtxstatus *hw) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci struct b43legacy_txstatus status; 60262306a36Sopenharmony_ci u8 tmp; 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci status.cookie = le16_to_cpu(hw->cookie); 60562306a36Sopenharmony_ci status.seq = le16_to_cpu(hw->seq); 60662306a36Sopenharmony_ci status.phy_stat = hw->phy_stat; 60762306a36Sopenharmony_ci tmp = hw->count; 60862306a36Sopenharmony_ci status.frame_count = (tmp >> 4); 60962306a36Sopenharmony_ci status.rts_count = (tmp & 0x0F); 61062306a36Sopenharmony_ci tmp = hw->flags << 1; 61162306a36Sopenharmony_ci status.supp_reason = ((tmp & 0x1C) >> 2); 61262306a36Sopenharmony_ci status.pm_indicated = !!(tmp & 0x80); 61362306a36Sopenharmony_ci status.intermediate = !!(tmp & 0x40); 61462306a36Sopenharmony_ci status.for_ampdu = !!(tmp & 0x20); 61562306a36Sopenharmony_ci status.acked = !!(tmp & 0x02); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci b43legacy_handle_txstatus(dev, &status); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci/* Stop any TX operation on the device (suspend the hardware queues) */ 62162306a36Sopenharmony_civoid b43legacy_tx_suspend(struct b43legacy_wldev *dev) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci if (b43legacy_using_pio(dev)) 62462306a36Sopenharmony_ci b43legacy_pio_freeze_txqueues(dev); 62562306a36Sopenharmony_ci else 62662306a36Sopenharmony_ci b43legacy_dma_tx_suspend(dev); 62762306a36Sopenharmony_ci} 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci/* Resume any TX operation on the device (resume the hardware queues) */ 63062306a36Sopenharmony_civoid b43legacy_tx_resume(struct b43legacy_wldev *dev) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci if (b43legacy_using_pio(dev)) 63362306a36Sopenharmony_ci b43legacy_pio_thaw_txqueues(dev); 63462306a36Sopenharmony_ci else 63562306a36Sopenharmony_ci b43legacy_dma_tx_resume(dev); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/* Initialize the QoS parameters */ 63962306a36Sopenharmony_civoid b43legacy_qos_init(struct b43legacy_wldev *dev) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci /* FIXME: This function must probably be called from the mac80211 64262306a36Sopenharmony_ci * config callback. */ 64362306a36Sopenharmony_cireturn; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci b43legacy_hf_write(dev, b43legacy_hf_read(dev) | B43legacy_HF_EDCF); 64662306a36Sopenharmony_ci /* FIXME kill magic */ 64762306a36Sopenharmony_ci b43legacy_write16(dev, 0x688, 64862306a36Sopenharmony_ci b43legacy_read16(dev, 0x688) | 0x4); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /*TODO: We might need some stack support here to get the values. */ 65262306a36Sopenharmony_ci} 653