653 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 4 Broadcom B43legacy wireless driver5 6 Transmission (TX/RX) related functions.7 8 Copyright (C) 2005 Martin Langer <martin-langer@gmx.de>9 Copyright (C) 2005 Stefano Brivio <stefano.brivio@polimi.it>10 Copyright (C) 2005, 2006 Michael Buesch <m@bues.ch>11 Copyright (C) 2005 Danny van Dyk <kugelfang@gentoo.org>12 Copyright (C) 2005 Andreas Jaggi <andreas.jaggi@waterwave.ch>13 Copyright (C) 2007 Larry Finger <Larry.Finger@lwfinger.net>14 15 16*/17 18#include <net/dst.h>19 20#include "xmit.h"21#include "phy.h"22#include "dma.h"23#include "pio.h"24 25 26/* Extract the bitrate out of a CCK PLCP header. */27static u8 b43legacy_plcp_get_bitrate_idx_cck(struct b43legacy_plcp_hdr6 *plcp)28{29 switch (plcp->raw[0]) {30 case 0x0A:31 return 0;32 case 0x14:33 return 1;34 case 0x37:35 return 2;36 case 0x6E:37 return 3;38 }39 B43legacy_BUG_ON(1);40 return -1;41}42 43/* Extract the bitrate out of an OFDM PLCP header. */44static u8 b43legacy_plcp_get_bitrate_idx_ofdm(struct b43legacy_plcp_hdr6 *plcp,45 bool aphy)46{47 int base = aphy ? 0 : 4;48 49 switch (plcp->raw[0] & 0xF) {50 case 0xB:51 return base + 0;52 case 0xF:53 return base + 1;54 case 0xA:55 return base + 2;56 case 0xE:57 return base + 3;58 case 0x9:59 return base + 4;60 case 0xD:61 return base + 5;62 case 0x8:63 return base + 6;64 case 0xC:65 return base + 7;66 }67 B43legacy_BUG_ON(1);68 return -1;69}70 71u8 b43legacy_plcp_get_ratecode_cck(const u8 bitrate)72{73 switch (bitrate) {74 case B43legacy_CCK_RATE_1MB:75 return 0x0A;76 case B43legacy_CCK_RATE_2MB:77 return 0x14;78 case B43legacy_CCK_RATE_5MB:79 return 0x37;80 case B43legacy_CCK_RATE_11MB:81 return 0x6E;82 }83 B43legacy_BUG_ON(1);84 return 0;85}86 87u8 b43legacy_plcp_get_ratecode_ofdm(const u8 bitrate)88{89 switch (bitrate) {90 case B43legacy_OFDM_RATE_6MB:91 return 0xB;92 case B43legacy_OFDM_RATE_9MB:93 return 0xF;94 case B43legacy_OFDM_RATE_12MB:95 return 0xA;96 case B43legacy_OFDM_RATE_18MB:97 return 0xE;98 case B43legacy_OFDM_RATE_24MB:99 return 0x9;100 case B43legacy_OFDM_RATE_36MB:101 return 0xD;102 case B43legacy_OFDM_RATE_48MB:103 return 0x8;104 case B43legacy_OFDM_RATE_54MB:105 return 0xC;106 }107 B43legacy_BUG_ON(1);108 return 0;109}110 111void b43legacy_generate_plcp_hdr(struct b43legacy_plcp_hdr4 *plcp,112 const u16 octets, const u8 bitrate)113{114 __le32 *data = &(plcp->data);115 __u8 *raw = plcp->raw;116 117 if (b43legacy_is_ofdm_rate(bitrate)) {118 u16 d;119 120 d = b43legacy_plcp_get_ratecode_ofdm(bitrate);121 B43legacy_WARN_ON(octets & 0xF000);122 d |= (octets << 5);123 *data = cpu_to_le32(d);124 } else {125 u32 plen;126 127 plen = octets * 16 / bitrate;128 if ((octets * 16 % bitrate) > 0) {129 plen++;130 if ((bitrate == B43legacy_CCK_RATE_11MB)131 && ((octets * 8 % 11) < 4))132 raw[1] = 0x84;133 else134 raw[1] = 0x04;135 } else136 raw[1] = 0x04;137 *data |= cpu_to_le32(plen << 16);138 raw[0] = b43legacy_plcp_get_ratecode_cck(bitrate);139 }140}141 142static u8 b43legacy_calc_fallback_rate(u8 bitrate)143{144 switch (bitrate) {145 case B43legacy_CCK_RATE_1MB:146 return B43legacy_CCK_RATE_1MB;147 case B43legacy_CCK_RATE_2MB:148 return B43legacy_CCK_RATE_1MB;149 case B43legacy_CCK_RATE_5MB:150 return B43legacy_CCK_RATE_2MB;151 case B43legacy_CCK_RATE_11MB:152 return B43legacy_CCK_RATE_5MB;153 case B43legacy_OFDM_RATE_6MB:154 return B43legacy_CCK_RATE_5MB;155 case B43legacy_OFDM_RATE_9MB:156 return B43legacy_OFDM_RATE_6MB;157 case B43legacy_OFDM_RATE_12MB:158 return B43legacy_OFDM_RATE_9MB;159 case B43legacy_OFDM_RATE_18MB:160 return B43legacy_OFDM_RATE_12MB;161 case B43legacy_OFDM_RATE_24MB:162 return B43legacy_OFDM_RATE_18MB;163 case B43legacy_OFDM_RATE_36MB:164 return B43legacy_OFDM_RATE_24MB;165 case B43legacy_OFDM_RATE_48MB:166 return B43legacy_OFDM_RATE_36MB;167 case B43legacy_OFDM_RATE_54MB:168 return B43legacy_OFDM_RATE_48MB;169 }170 B43legacy_BUG_ON(1);171 return 0;172}173 174static int generate_txhdr_fw3(struct b43legacy_wldev *dev,175 struct b43legacy_txhdr_fw3 *txhdr,176 const unsigned char *fragment_data,177 unsigned int fragment_len,178 struct ieee80211_tx_info *info,179 u16 cookie)180{181 const struct ieee80211_hdr *wlhdr;182 int use_encryption = !!info->control.hw_key;183 u8 rate;184 struct ieee80211_rate *rate_fb;185 int rate_ofdm;186 int rate_fb_ofdm;187 unsigned int plcp_fragment_len;188 u32 mac_ctl = 0;189 u16 phy_ctl = 0;190 struct ieee80211_rate *tx_rate;191 struct ieee80211_tx_rate *rates;192 193 wlhdr = (const struct ieee80211_hdr *)fragment_data;194 195 memset(txhdr, 0, sizeof(*txhdr));196 197 tx_rate = ieee80211_get_tx_rate(dev->wl->hw, info);198 199 rate = tx_rate->hw_value;200 rate_ofdm = b43legacy_is_ofdm_rate(rate);201 rate_fb = ieee80211_get_alt_retry_rate(dev->wl->hw, info, 0) ? : tx_rate;202 rate_fb_ofdm = b43legacy_is_ofdm_rate(rate_fb->hw_value);203 204 txhdr->mac_frame_ctl = wlhdr->frame_control;205 memcpy(txhdr->tx_receiver, wlhdr->addr1, ETH_ALEN);206 207 /* Calculate duration for fallback rate */208 if ((rate_fb->hw_value == rate) ||209 (wlhdr->duration_id & cpu_to_le16(0x8000)) ||210 (wlhdr->duration_id == cpu_to_le16(0))) {211 /* If the fallback rate equals the normal rate or the212 * dur_id field contains an AID, CFP magic or 0,213 * use the original dur_id field. */214 txhdr->dur_fb = wlhdr->duration_id;215 } else {216 txhdr->dur_fb = ieee80211_generic_frame_duration(dev->wl->hw,217 info->control.vif,218 info->band,219 fragment_len,220 rate_fb);221 }222 223 plcp_fragment_len = fragment_len + FCS_LEN;224 if (use_encryption) {225 u8 key_idx = info->control.hw_key->hw_key_idx;226 struct b43legacy_key *key;227 int wlhdr_len;228 size_t iv_len;229 230 B43legacy_WARN_ON(key_idx >= dev->max_nr_keys);231 key = &(dev->key[key_idx]);232 233 if (key->enabled) {234 /* Hardware appends ICV. */235 plcp_fragment_len += info->control.hw_key->icv_len;236 237 key_idx = b43legacy_kidx_to_fw(dev, key_idx);238 mac_ctl |= (key_idx << B43legacy_TX4_MAC_KEYIDX_SHIFT) &239 B43legacy_TX4_MAC_KEYIDX;240 mac_ctl |= (key->algorithm <<241 B43legacy_TX4_MAC_KEYALG_SHIFT) &242 B43legacy_TX4_MAC_KEYALG;243 wlhdr_len = ieee80211_hdrlen(wlhdr->frame_control);244 iv_len = min_t(size_t, info->control.hw_key->iv_len,245 ARRAY_SIZE(txhdr->iv));246 memcpy(txhdr->iv, ((u8 *)wlhdr) + wlhdr_len, iv_len);247 } else {248 /* This key is invalid. This might only happen249 * in a short timeframe after machine resume before250 * we were able to reconfigure keys.251 * Drop this packet completely. Do not transmit it252 * unencrypted to avoid leaking information. */253 return -ENOKEY;254 }255 }256 b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *)257 (&txhdr->plcp), plcp_fragment_len,258 rate);259 b43legacy_generate_plcp_hdr(&txhdr->plcp_fb, plcp_fragment_len,260 rate_fb->hw_value);261 262 /* PHY TX Control word */263 if (rate_ofdm)264 phy_ctl |= B43legacy_TX4_PHY_ENC_OFDM;265 if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)266 phy_ctl |= B43legacy_TX4_PHY_SHORTPRMBL;267 phy_ctl |= B43legacy_TX4_PHY_ANTLAST;268 269 /* MAC control */270 rates = info->control.rates;271 if (!(info->flags & IEEE80211_TX_CTL_NO_ACK))272 mac_ctl |= B43legacy_TX4_MAC_ACK;273 if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ)274 mac_ctl |= B43legacy_TX4_MAC_HWSEQ;275 if (info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT)276 mac_ctl |= B43legacy_TX4_MAC_STMSDU;277 if (rate_fb_ofdm)278 mac_ctl |= B43legacy_TX4_MAC_FALLBACKOFDM;279 280 /* Overwrite rates[0].count to make the retry calculation281 * in the tx status easier. need the actual retry limit to282 * detect whether the fallback rate was used.283 */284 if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||285 (rates[0].count <= dev->wl->hw->conf.long_frame_max_tx_count)) {286 rates[0].count = dev->wl->hw->conf.long_frame_max_tx_count;287 mac_ctl |= B43legacy_TX4_MAC_LONGFRAME;288 } else {289 rates[0].count = dev->wl->hw->conf.short_frame_max_tx_count;290 }291 292 /* Generate the RTS or CTS-to-self frame */293 if ((rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) ||294 (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT)) {295 unsigned int len;296 struct ieee80211_hdr *hdr;297 int rts_rate;298 int rts_rate_fb;299 int rts_rate_fb_ofdm;300 301 rts_rate = ieee80211_get_rts_cts_rate(dev->wl->hw, info)->hw_value;302 rts_rate_fb = b43legacy_calc_fallback_rate(rts_rate);303 rts_rate_fb_ofdm = b43legacy_is_ofdm_rate(rts_rate_fb);304 if (rts_rate_fb_ofdm)305 mac_ctl |= B43legacy_TX4_MAC_CTSFALLBACKOFDM;306 307 if (rates[0].flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {308 ieee80211_ctstoself_get(dev->wl->hw,309 info->control.vif,310 fragment_data,311 fragment_len, info,312 (struct ieee80211_cts *)313 (txhdr->rts_frame));314 mac_ctl |= B43legacy_TX4_MAC_SENDCTS;315 len = sizeof(struct ieee80211_cts);316 } else {317 ieee80211_rts_get(dev->wl->hw,318 info->control.vif,319 fragment_data, fragment_len, info,320 (struct ieee80211_rts *)321 (txhdr->rts_frame));322 mac_ctl |= B43legacy_TX4_MAC_SENDRTS;323 len = sizeof(struct ieee80211_rts);324 }325 len += FCS_LEN;326 b43legacy_generate_plcp_hdr((struct b43legacy_plcp_hdr4 *)327 (&txhdr->rts_plcp),328 len, rts_rate);329 b43legacy_generate_plcp_hdr(&txhdr->rts_plcp_fb,330 len, rts_rate_fb);331 hdr = (struct ieee80211_hdr *)(&txhdr->rts_frame);332 txhdr->rts_dur_fb = hdr->duration_id;333 }334 335 /* Magic cookie */336 txhdr->cookie = cpu_to_le16(cookie);337 338 /* Apply the bitfields */339 txhdr->mac_ctl = cpu_to_le32(mac_ctl);340 txhdr->phy_ctl = cpu_to_le16(phy_ctl);341 342 return 0;343}344 345int b43legacy_generate_txhdr(struct b43legacy_wldev *dev,346 u8 *txhdr,347 const unsigned char *fragment_data,348 unsigned int fragment_len,349 struct ieee80211_tx_info *info,350 u16 cookie)351{352 return generate_txhdr_fw3(dev, (struct b43legacy_txhdr_fw3 *)txhdr,353 fragment_data, fragment_len,354 info, cookie);355}356 357static s8 b43legacy_rssi_postprocess(struct b43legacy_wldev *dev,358 u8 in_rssi, int ofdm,359 int adjust_2053, int adjust_2050)360{361 struct b43legacy_phy *phy = &dev->phy;362 s32 tmp;363 364 switch (phy->radio_ver) {365 case 0x2050:366 if (ofdm) {367 tmp = in_rssi;368 if (tmp > 127)369 tmp -= 256;370 tmp *= 73;371 tmp /= 64;372 if (adjust_2050)373 tmp += 25;374 else375 tmp -= 3;376 } else {377 if (dev->dev->bus->sprom.boardflags_lo378 & B43legacy_BFL_RSSI) {379 if (in_rssi > 63)380 in_rssi = 63;381 tmp = phy->nrssi_lt[in_rssi];382 tmp = 31 - tmp;383 tmp *= -131;384 tmp /= 128;385 tmp -= 57;386 } else {387 tmp = in_rssi;388 tmp = 31 - tmp;389 tmp *= -149;390 tmp /= 128;391 tmp -= 68;392 }393 if (phy->type == B43legacy_PHYTYPE_G &&394 adjust_2050)395 tmp += 25;396 }397 break;398 case 0x2060:399 if (in_rssi > 127)400 tmp = in_rssi - 256;401 else402 tmp = in_rssi;403 break;404 default:405 tmp = in_rssi;406 tmp -= 11;407 tmp *= 103;408 tmp /= 64;409 if (adjust_2053)410 tmp -= 109;411 else412 tmp -= 83;413 }414 415 return (s8)tmp;416}417 418void b43legacy_rx(struct b43legacy_wldev *dev,419 struct sk_buff *skb,420 const void *_rxhdr)421{422 struct ieee80211_rx_status status;423 struct b43legacy_plcp_hdr6 *plcp;424 struct ieee80211_hdr *wlhdr;425 const struct b43legacy_rxhdr_fw3 *rxhdr = _rxhdr;426 __le16 fctl;427 u16 phystat0;428 u16 phystat3;429 u16 chanstat;430 u16 mactime;431 u32 macstat;432 u16 chanid;433 u8 jssi;434 int padding;435 436 memset(&status, 0, sizeof(status));437 438 /* Get metadata about the frame from the header. */439 phystat0 = le16_to_cpu(rxhdr->phy_status0);440 phystat3 = le16_to_cpu(rxhdr->phy_status3);441 jssi = rxhdr->jssi;442 macstat = le16_to_cpu(rxhdr->mac_status);443 mactime = le16_to_cpu(rxhdr->mac_time);444 chanstat = le16_to_cpu(rxhdr->channel);445 446 if (macstat & B43legacy_RX_MAC_FCSERR)447 dev->wl->ieee_stats.dot11FCSErrorCount++;448 449 /* Skip PLCP and padding */450 padding = (macstat & B43legacy_RX_MAC_PADDING) ? 2 : 0;451 if (unlikely(skb->len < (sizeof(struct b43legacy_plcp_hdr6) +452 padding))) {453 b43legacydbg(dev->wl, "RX: Packet size underrun (1)\n");454 goto drop;455 }456 plcp = (struct b43legacy_plcp_hdr6 *)(skb->data + padding);457 skb_pull(skb, sizeof(struct b43legacy_plcp_hdr6) + padding);458 /* The skb contains the Wireless Header + payload data now */459 if (unlikely(skb->len < (2+2+6/*minimum hdr*/ + FCS_LEN))) {460 b43legacydbg(dev->wl, "RX: Packet size underrun (2)\n");461 goto drop;462 }463 wlhdr = (struct ieee80211_hdr *)(skb->data);464 fctl = wlhdr->frame_control;465 466 if ((macstat & B43legacy_RX_MAC_DEC) &&467 !(macstat & B43legacy_RX_MAC_DECERR)) {468 unsigned int keyidx;469 int wlhdr_len;470 int iv_len;471 int icv_len;472 473 keyidx = ((macstat & B43legacy_RX_MAC_KEYIDX)474 >> B43legacy_RX_MAC_KEYIDX_SHIFT);475 /* We must adjust the key index here. We want the "physical"476 * key index, but the ucode passed it slightly different.477 */478 keyidx = b43legacy_kidx_to_raw(dev, keyidx);479 B43legacy_WARN_ON(keyidx >= dev->max_nr_keys);480 481 if (dev->key[keyidx].algorithm != B43legacy_SEC_ALGO_NONE) {482 /* Remove PROTECTED flag to mark it as decrypted. */483 B43legacy_WARN_ON(!ieee80211_has_protected(fctl));484 fctl &= ~cpu_to_le16(IEEE80211_FCTL_PROTECTED);485 wlhdr->frame_control = fctl;486 487 wlhdr_len = ieee80211_hdrlen(fctl);488 if (unlikely(skb->len < (wlhdr_len + 3))) {489 b43legacydbg(dev->wl, "RX: Packet size"490 " underrun3\n");491 goto drop;492 }493 if (skb->data[wlhdr_len + 3] & (1 << 5)) {494 /* The Ext-IV Bit is set in the "KeyID"495 * octet of the IV.496 */497 iv_len = 8;498 icv_len = 8;499 } else {500 iv_len = 4;501 icv_len = 4;502 }503 if (unlikely(skb->len < (wlhdr_len + iv_len +504 icv_len))) {505 b43legacydbg(dev->wl, "RX: Packet size"506 " underrun4\n");507 goto drop;508 }509 /* Remove the IV */510 memmove(skb->data + iv_len, skb->data, wlhdr_len);511 skb_pull(skb, iv_len);512 /* Remove the ICV */513 skb_trim(skb, skb->len - icv_len);514 515 status.flag |= RX_FLAG_DECRYPTED;516 }517 }518 519 status.signal = b43legacy_rssi_postprocess(dev, jssi,520 (phystat0 & B43legacy_RX_PHYST0_OFDM),521 (phystat0 & B43legacy_RX_PHYST0_GAINCTL),522 (phystat3 & B43legacy_RX_PHYST3_TRSTATE));523 /* change to support A PHY */524 if (phystat0 & B43legacy_RX_PHYST0_OFDM)525 status.rate_idx = b43legacy_plcp_get_bitrate_idx_ofdm(plcp, false);526 else527 status.rate_idx = b43legacy_plcp_get_bitrate_idx_cck(plcp);528 status.antenna = !!(phystat0 & B43legacy_RX_PHYST0_ANT);529 530 /*531 * All frames on monitor interfaces and beacons always need a full532 * 64-bit timestamp. Monitor interfaces need it for diagnostic533 * purposes and beacons for IBSS merging.534 * This code assumes we get to process the packet within 16 bits535 * of timestamp, i.e. about 65 milliseconds after the PHY received536 * the first symbol.537 */538 if (ieee80211_is_beacon(fctl) || dev->wl->radiotap_enabled) {539 u16 low_mactime_now;540 541 b43legacy_tsf_read(dev, &status.mactime);542 low_mactime_now = status.mactime;543 status.mactime = status.mactime & ~0xFFFFULL;544 status.mactime += mactime;545 if (low_mactime_now <= mactime)546 status.mactime -= 0x10000;547 status.flag |= RX_FLAG_MACTIME_START;548 }549 550 chanid = (chanstat & B43legacy_RX_CHAN_ID) >>551 B43legacy_RX_CHAN_ID_SHIFT;552 switch (chanstat & B43legacy_RX_CHAN_PHYTYPE) {553 case B43legacy_PHYTYPE_B:554 case B43legacy_PHYTYPE_G:555 status.band = NL80211_BAND_2GHZ;556 status.freq = chanid + 2400;557 break;558 default:559 b43legacywarn(dev->wl, "Unexpected value for chanstat (0x%X)\n",560 chanstat);561 goto drop;562 }563 564 memcpy(IEEE80211_SKB_RXCB(skb), &status, sizeof(status));565 ieee80211_rx_irqsafe(dev->wl->hw, skb);566 567 return;568drop:569 b43legacydbg(dev->wl, "RX: Packet dropped\n");570 dev_kfree_skb_any(skb);571}572 573void b43legacy_handle_txstatus(struct b43legacy_wldev *dev,574 const struct b43legacy_txstatus *status)575{576 b43legacy_debugfs_log_txstat(dev, status);577 578 if (status->intermediate)579 return;580 if (status->for_ampdu)581 return;582 if (!status->acked)583 dev->wl->ieee_stats.dot11ACKFailureCount++;584 if (status->rts_count) {585 if (status->rts_count == 0xF) /* FIXME */586 dev->wl->ieee_stats.dot11RTSFailureCount++;587 else588 dev->wl->ieee_stats.dot11RTSSuccessCount++;589 }590 591 if (b43legacy_using_pio(dev))592 b43legacy_pio_handle_txstatus(dev, status);593 else594 b43legacy_dma_handle_txstatus(dev, status);595}596 597/* Handle TX status report as received through DMA/PIO queues */598void b43legacy_handle_hwtxstatus(struct b43legacy_wldev *dev,599 const struct b43legacy_hwtxstatus *hw)600{601 struct b43legacy_txstatus status;602 u8 tmp;603 604 status.cookie = le16_to_cpu(hw->cookie);605 status.seq = le16_to_cpu(hw->seq);606 status.phy_stat = hw->phy_stat;607 tmp = hw->count;608 status.frame_count = (tmp >> 4);609 status.rts_count = (tmp & 0x0F);610 tmp = hw->flags << 1;611 status.supp_reason = ((tmp & 0x1C) >> 2);612 status.pm_indicated = !!(tmp & 0x80);613 status.intermediate = !!(tmp & 0x40);614 status.for_ampdu = !!(tmp & 0x20);615 status.acked = !!(tmp & 0x02);616 617 b43legacy_handle_txstatus(dev, &status);618}619 620/* Stop any TX operation on the device (suspend the hardware queues) */621void b43legacy_tx_suspend(struct b43legacy_wldev *dev)622{623 if (b43legacy_using_pio(dev))624 b43legacy_pio_freeze_txqueues(dev);625 else626 b43legacy_dma_tx_suspend(dev);627}628 629/* Resume any TX operation on the device (resume the hardware queues) */630void b43legacy_tx_resume(struct b43legacy_wldev *dev)631{632 if (b43legacy_using_pio(dev))633 b43legacy_pio_thaw_txqueues(dev);634 else635 b43legacy_dma_tx_resume(dev);636}637 638/* Initialize the QoS parameters */639void b43legacy_qos_init(struct b43legacy_wldev *dev)640{641 /* FIXME: This function must probably be called from the mac80211642 * config callback. */643return;644 645 b43legacy_hf_write(dev, b43legacy_hf_read(dev) | B43legacy_HF_EDCF);646 /* FIXME kill magic */647 b43legacy_write16(dev, 0x688,648 b43legacy_read16(dev, 0x688) | 0x4);649 650 651 /*TODO: We might need some stack support here to get the values. */652}653