linux_dsm_epyc7002/drivers/net/wireless/intersil/p54/txrx.c
Linus Torvalds 237f83dfbe Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
 "Some highlights from this development cycle:

   1) Big refactoring of ipv6 route and neigh handling to support
      nexthop objects configurable as units from userspace. From David
      Ahern.

   2) Convert explored_states in BPF verifier into a hash table,
      significantly decreased state held for programs with bpf2bpf
      calls, from Alexei Starovoitov.

   3) Implement bpf_send_signal() helper, from Yonghong Song.

   4) Various classifier enhancements to mvpp2 driver, from Maxime
      Chevallier.

   5) Add aRFS support to hns3 driver, from Jian Shen.

   6) Fix use after free in inet frags by allocating fqdirs dynamically
      and reworking how rhashtable dismantle occurs, from Eric Dumazet.

   7) Add act_ctinfo packet classifier action, from Kevin
      Darbyshire-Bryant.

   8) Add TFO key backup infrastructure, from Jason Baron.

   9) Remove several old and unused ISDN drivers, from Arnd Bergmann.

  10) Add devlink notifications for flash update status to mlxsw driver,
      from Jiri Pirko.

  11) Lots of kTLS offload infrastructure fixes, from Jakub Kicinski.

  12) Add support for mv88e6250 DSA chips, from Rasmus Villemoes.

  13) Various enhancements to ipv6 flow label handling, from Eric
      Dumazet and Willem de Bruijn.

  14) Support TLS offload in nfp driver, from Jakub Kicinski, Dirk van
      der Merwe, and others.

  15) Various improvements to axienet driver including converting it to
      phylink, from Robert Hancock.

  16) Add PTP support to sja1105 DSA driver, from Vladimir Oltean.

  17) Add mqprio qdisc offload support to dpaa2-eth, from Ioana
      Radulescu.

  18) Add devlink health reporting to mlx5, from Moshe Shemesh.

  19) Convert stmmac over to phylink, from Jose Abreu.

  20) Add PTP PHC (Physical Hardware Clock) support to mlxsw, from
      Shalom Toledo.

  21) Add nftables SYNPROXY support, from Fernando Fernandez Mancera.

  22) Convert tcp_fastopen over to use SipHash, from Ard Biesheuvel.

  23) Track spill/fill of constants in BPF verifier, from Alexei
      Starovoitov.

  24) Support bounded loops in BPF, from Alexei Starovoitov.

  25) Various page_pool API fixes and improvements, from Jesper Dangaard
      Brouer.

  26) Just like ipv4, support ref-countless ipv6 route handling. From
      Wei Wang.

  27) Support VLAN offloading in aquantia driver, from Igor Russkikh.

  28) Add AF_XDP zero-copy support to mlx5, from Maxim Mikityanskiy.

  29) Add flower GRE encap/decap support to nfp driver, from Pieter
      Jansen van Vuuren.

  30) Protect against stack overflow when using act_mirred, from John
      Hurley.

  31) Allow devmap map lookups from eBPF, from Toke Høiland-Jørgensen.

  32) Use page_pool API in netsec driver, Ilias Apalodimas.

  33) Add Google gve network driver, from Catherine Sullivan.

  34) More indirect call avoidance, from Paolo Abeni.

  35) Add kTLS TX HW offload support to mlx5, from Tariq Toukan.

  36) Add XDP_REDIRECT support to bnxt_en, from Andy Gospodarek.

  37) Add MPLS manipulation actions to TC, from John Hurley.

  38) Add sending a packet to connection tracking from TC actions, and
      then allow flower classifier matching on conntrack state. From
      Paul Blakey.

  39) Netfilter hw offload support, from Pablo Neira Ayuso"

* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (2080 commits)
  net/mlx5e: Return in default case statement in tx_post_resync_params
  mlx5: Return -EINVAL when WARN_ON_ONCE triggers in mlx5e_tls_resync().
  net: dsa: add support for BRIDGE_MROUTER attribute
  pkt_sched: Include const.h
  net: netsec: remove static declaration for netsec_set_tx_de()
  net: netsec: remove superfluous if statement
  netfilter: nf_tables: add hardware offload support
  net: flow_offload: rename tc_cls_flower_offload to flow_cls_offload
  net: flow_offload: add flow_block_cb_is_busy() and use it
  net: sched: remove tcf block API
  drivers: net: use flow block API
  net: sched: use flow block API
  net: flow_offload: add flow_block_cb_{priv, incref, decref}()
  net: flow_offload: add list handling functions
  net: flow_offload: add flow_block_cb_alloc() and flow_block_cb_free()
  net: flow_offload: rename TCF_BLOCK_BINDER_TYPE_* to FLOW_BLOCK_BINDER_TYPE_*
  net: flow_offload: rename TC_BLOCK_{UN}BIND to FLOW_BLOCK_{UN}BIND
  net: flow_offload: add flow_block_cb_setup_simple()
  net: hisilicon: Add an tx_desc to adapt HI13X1_GMAC
  net: hisilicon: Add an rx_desc to adapt HI13X1_GMAC
  ...
2019-07-11 10:55:49 -07:00

947 lines
25 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Common code for mac80211 Prism54 drivers
*
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
* Copyright (c) 2007-2009, Christian Lamparter <chunkeey@web.de>
* Copyright 2008, Johannes Berg <johannes@sipsolutions.net>
*
* Based on:
* - the islsm (softmac prism54) driver, which is:
* Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
* - stlc45xx driver
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
*/
#include <linux/export.h>
#include <linux/firmware.h>
#include <linux/etherdevice.h>
#include <asm/div64.h>
#include <net/mac80211.h>
#include "p54.h"
#include "lmac.h"
#ifdef P54_MM_DEBUG
static void p54_dump_tx_queue(struct p54_common *priv)
{
unsigned long flags;
struct ieee80211_tx_info *info;
struct p54_tx_info *range;
struct sk_buff *skb;
struct p54_hdr *hdr;
unsigned int i = 0;
u32 prev_addr;
u32 largest_hole = 0, free;
spin_lock_irqsave(&priv->tx_queue.lock, flags);
wiphy_debug(priv->hw->wiphy, "/ --- tx queue dump (%d entries) ---\n",
skb_queue_len(&priv->tx_queue));
prev_addr = priv->rx_start;
skb_queue_walk(&priv->tx_queue, skb) {
info = IEEE80211_SKB_CB(skb);
range = (void *) info->rate_driver_data;
hdr = (void *) skb->data;
free = range->start_addr - prev_addr;
wiphy_debug(priv->hw->wiphy,
"| [%02d] => [skb:%p skb_len:0x%04x "
"hdr:{flags:%02x len:%04x req_id:%04x type:%02x} "
"mem:{start:%04x end:%04x, free:%d}]\n",
i++, skb, skb->len,
le16_to_cpu(hdr->flags), le16_to_cpu(hdr->len),
le32_to_cpu(hdr->req_id), le16_to_cpu(hdr->type),
range->start_addr, range->end_addr, free);
prev_addr = range->end_addr;
largest_hole = max(largest_hole, free);
}
free = priv->rx_end - prev_addr;
largest_hole = max(largest_hole, free);
wiphy_debug(priv->hw->wiphy,
"\\ --- [free: %d], largest free block: %d ---\n",
free, largest_hole);
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
}
#endif /* P54_MM_DEBUG */
/*
* So, the firmware is somewhat stupid and doesn't know what places in its
* memory incoming data should go to. By poking around in the firmware, we
* can find some unused memory to upload our packets to. However, data that we
* want the card to TX needs to stay intact until the card has told us that
* it is done with it. This function finds empty places we can upload to and
* marks allocated areas as reserved if necessary. p54_find_and_unlink_skb or
* p54_free_skb frees allocated areas.
*/
static int p54_assign_address(struct p54_common *priv, struct sk_buff *skb)
{
struct sk_buff *entry, *target_skb = NULL;
struct ieee80211_tx_info *info;
struct p54_tx_info *range;
struct p54_hdr *data = (void *) skb->data;
unsigned long flags;
u32 last_addr = priv->rx_start;
u32 target_addr = priv->rx_start;
u16 len = priv->headroom + skb->len + priv->tailroom + 3;
info = IEEE80211_SKB_CB(skb);
range = (void *) info->rate_driver_data;
len = (range->extra_len + len) & ~0x3;
spin_lock_irqsave(&priv->tx_queue.lock, flags);
if (unlikely(skb_queue_len(&priv->tx_queue) == 32)) {
/*
* The tx_queue is now really full.
*
* TODO: check if the device has crashed and reset it.
*/
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
return -EBUSY;
}
skb_queue_walk(&priv->tx_queue, entry) {
u32 hole_size;
info = IEEE80211_SKB_CB(entry);
range = (void *) info->rate_driver_data;
hole_size = range->start_addr - last_addr;
if (!target_skb && hole_size >= len) {
target_skb = entry->prev;
hole_size -= len;
target_addr = last_addr;
break;
}
last_addr = range->end_addr;
}
if (unlikely(!target_skb)) {
if (priv->rx_end - last_addr >= len) {
target_skb = skb_peek_tail(&priv->tx_queue);
if (target_skb) {
info = IEEE80211_SKB_CB(target_skb);
range = (void *)info->rate_driver_data;
target_addr = range->end_addr;
}
} else {
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
return -ENOSPC;
}
}
info = IEEE80211_SKB_CB(skb);
range = (void *) info->rate_driver_data;
range->start_addr = target_addr;
range->end_addr = target_addr + len;
data->req_id = cpu_to_le32(target_addr + priv->headroom);
if (IS_DATA_FRAME(skb) &&
unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON))
priv->beacon_req_id = data->req_id;
if (target_skb)
__skb_queue_after(&priv->tx_queue, target_skb, skb);
else
__skb_queue_head(&priv->tx_queue, skb);
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
return 0;
}
static void p54_tx_pending(struct p54_common *priv)
{
struct sk_buff *skb;
int ret;
skb = skb_dequeue(&priv->tx_pending);
if (unlikely(!skb))
return ;
ret = p54_assign_address(priv, skb);
if (unlikely(ret))
skb_queue_head(&priv->tx_pending, skb);
else
priv->tx(priv->hw, skb);
}
static void p54_wake_queues(struct p54_common *priv)
{
unsigned long flags;
unsigned int i;
if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
return ;
p54_tx_pending(priv);
spin_lock_irqsave(&priv->tx_stats_lock, flags);
for (i = 0; i < priv->hw->queues; i++) {
if (priv->tx_stats[i + P54_QUEUE_DATA].len <
priv->tx_stats[i + P54_QUEUE_DATA].limit)
ieee80211_wake_queue(priv->hw, i);
}
spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
}
static int p54_tx_qos_accounting_alloc(struct p54_common *priv,
struct sk_buff *skb,
const u16 p54_queue)
{
struct p54_tx_queue_stats *queue;
unsigned long flags;
if (WARN_ON(p54_queue >= P54_QUEUE_NUM))
return -EINVAL;
queue = &priv->tx_stats[p54_queue];
spin_lock_irqsave(&priv->tx_stats_lock, flags);
if (unlikely(queue->len >= queue->limit && IS_QOS_QUEUE(p54_queue))) {
spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
return -ENOSPC;
}
queue->len++;
queue->count++;
if (unlikely(queue->len == queue->limit && IS_QOS_QUEUE(p54_queue))) {
u16 ac_queue = p54_queue - P54_QUEUE_DATA;
ieee80211_stop_queue(priv->hw, ac_queue);
}
spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
return 0;
}
static void p54_tx_qos_accounting_free(struct p54_common *priv,
struct sk_buff *skb)
{
if (IS_DATA_FRAME(skb)) {
unsigned long flags;
spin_lock_irqsave(&priv->tx_stats_lock, flags);
priv->tx_stats[GET_HW_QUEUE(skb)].len--;
spin_unlock_irqrestore(&priv->tx_stats_lock, flags);
if (unlikely(GET_HW_QUEUE(skb) == P54_QUEUE_BEACON)) {
if (priv->beacon_req_id == GET_REQ_ID(skb)) {
/* this is the active beacon set anymore */
priv->beacon_req_id = 0;
}
complete(&priv->beacon_comp);
}
}
p54_wake_queues(priv);
}
void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54_common *priv = dev->priv;
if (unlikely(!skb))
return ;
skb_unlink(skb, &priv->tx_queue);
p54_tx_qos_accounting_free(priv, skb);
ieee80211_free_txskb(dev, skb);
}
EXPORT_SYMBOL_GPL(p54_free_skb);
static struct sk_buff *p54_find_and_unlink_skb(struct p54_common *priv,
const __le32 req_id)
{
struct sk_buff *entry;
unsigned long flags;
spin_lock_irqsave(&priv->tx_queue.lock, flags);
skb_queue_walk(&priv->tx_queue, entry) {
struct p54_hdr *hdr = (struct p54_hdr *) entry->data;
if (hdr->req_id == req_id) {
__skb_unlink(entry, &priv->tx_queue);
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
p54_tx_qos_accounting_free(priv, entry);
return entry;
}
}
spin_unlock_irqrestore(&priv->tx_queue.lock, flags);
return NULL;
}
void p54_tx(struct p54_common *priv, struct sk_buff *skb)
{
skb_queue_tail(&priv->tx_pending, skb);
p54_tx_pending(priv);
}
static int p54_rssi_to_dbm(struct p54_common *priv, int rssi)
{
if (priv->rxhw != 5) {
return ((rssi * priv->cur_rssi->mul) / 64 +
priv->cur_rssi->add) / 4;
} else {
/*
* TODO: find the correct formula
*/
return rssi / 2 - 110;
}
}
/*
* Even if the firmware is capable of dealing with incoming traffic,
* while dozing, we have to prepared in case mac80211 uses PS-POLL
* to retrieve outstanding frames from our AP.
* (see comment in net/mac80211/mlme.c @ line 1993)
*/
static void p54_pspoll_workaround(struct p54_common *priv, struct sk_buff *skb)
{
struct ieee80211_hdr *hdr = (void *) skb->data;
struct ieee80211_tim_ie *tim_ie;
u8 *tim;
u8 tim_len;
bool new_psm;
/* only beacons have a TIM IE */
if (!ieee80211_is_beacon(hdr->frame_control))
return;
if (!priv->aid)
return;
/* only consider beacons from the associated BSSID */
if (!ether_addr_equal_64bits(hdr->addr3, priv->bssid))
return;
tim = p54_find_ie(skb, WLAN_EID_TIM);
if (!tim)
return;
tim_len = tim[1];
tim_ie = (struct ieee80211_tim_ie *) &tim[2];
new_psm = ieee80211_check_tim(tim_ie, tim_len, priv->aid);
if (new_psm != priv->powersave_override) {
priv->powersave_override = new_psm;
p54_set_ps(priv);
}
}
static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb)
{
struct p54_rx_data *hdr = (struct p54_rx_data *) skb->data;
struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
u16 freq = le16_to_cpu(hdr->freq);
size_t header_len = sizeof(*hdr);
u32 tsf32;
__le16 fc;
u8 rate = hdr->rate & 0xf;
/*
* If the device is in a unspecified state we have to
* ignore all data frames. Else we could end up with a
* nasty crash.
*/
if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
return 0;
if (!(hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_IN_FCS_GOOD)))
return 0;
if (hdr->decrypt_status == P54_DECRYPT_OK)
rx_status->flag |= RX_FLAG_DECRYPTED;
if ((hdr->decrypt_status == P54_DECRYPT_FAIL_MICHAEL) ||
(hdr->decrypt_status == P54_DECRYPT_FAIL_TKIP))
rx_status->flag |= RX_FLAG_MMIC_ERROR;
rx_status->signal = p54_rssi_to_dbm(priv, hdr->rssi);
if (hdr->rate & 0x10)
rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE;
if (priv->hw->conf.chandef.chan->band == NL80211_BAND_5GHZ)
rx_status->rate_idx = (rate < 4) ? 0 : rate - 4;
else
rx_status->rate_idx = rate;
rx_status->freq = freq;
rx_status->band = priv->hw->conf.chandef.chan->band;
rx_status->antenna = hdr->antenna;
tsf32 = le32_to_cpu(hdr->tsf32);
if (tsf32 < priv->tsf_low32)
priv->tsf_high32++;
rx_status->mactime = ((u64)priv->tsf_high32) << 32 | tsf32;
priv->tsf_low32 = tsf32;
/* LMAC API Page 10/29 - s_lm_data_in - clock
* "usec accurate timestamp of hardware clock
* at end of frame (before OFDM SIFS EOF padding"
*/
rx_status->flag |= RX_FLAG_MACTIME_END;
if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN))
header_len += hdr->align[0];
skb_pull(skb, header_len);
skb_trim(skb, le16_to_cpu(hdr->len));
fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
if (ieee80211_is_probe_resp(fc) || ieee80211_is_beacon(fc))
rx_status->boottime_ns = ktime_get_boottime_ns();
if (unlikely(priv->hw->conf.flags & IEEE80211_CONF_PS))
p54_pspoll_workaround(priv, skb);
ieee80211_rx_irqsafe(priv->hw, skb);
ieee80211_queue_delayed_work(priv->hw, &priv->work,
msecs_to_jiffies(P54_STATISTICS_UPDATE));
return -1;
}
static void p54_rx_frame_sent(struct p54_common *priv, struct sk_buff *skb)
{
struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
struct p54_frame_sent *payload = (struct p54_frame_sent *) hdr->data;
struct ieee80211_tx_info *info;
struct p54_hdr *entry_hdr;
struct p54_tx_data *entry_data;
struct sk_buff *entry;
unsigned int pad = 0, frame_len;
int count, idx;
entry = p54_find_and_unlink_skb(priv, hdr->req_id);
if (unlikely(!entry))
return ;
frame_len = entry->len;
info = IEEE80211_SKB_CB(entry);
entry_hdr = (struct p54_hdr *) entry->data;
entry_data = (struct p54_tx_data *) entry_hdr->data;
priv->stats.dot11ACKFailureCount += payload->tries - 1;
/*
* Frames in P54_QUEUE_FWSCAN and P54_QUEUE_BEACON are
* generated by the driver. Therefore tx_status is bogus
* and we don't want to confuse the mac80211 stack.
*/
if (unlikely(entry_data->hw_queue < P54_QUEUE_FWSCAN)) {
dev_kfree_skb_any(entry);
return ;
}
/*
* Clear manually, ieee80211_tx_info_clear_status would
* clear the counts too and we need them.
*/
memset(&info->status.ack_signal, 0,
sizeof(struct ieee80211_tx_info) -
offsetof(struct ieee80211_tx_info, status.ack_signal));
BUILD_BUG_ON(offsetof(struct ieee80211_tx_info,
status.ack_signal) != 20);
if (entry_hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN))
pad = entry_data->align[0];
/* walk through the rates array and adjust the counts */
count = payload->tries;
for (idx = 0; idx < 4; idx++) {
if (count >= info->status.rates[idx].count) {
count -= info->status.rates[idx].count;
} else if (count > 0) {
info->status.rates[idx].count = count;
count = 0;
} else {
info->status.rates[idx].idx = -1;
info->status.rates[idx].count = 0;
}
}
if (!(info->flags & IEEE80211_TX_CTL_NO_ACK) &&
!(payload->status & P54_TX_FAILED))
info->flags |= IEEE80211_TX_STAT_ACK;
if (payload->status & P54_TX_PSM_CANCELLED)
info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
info->status.ack_signal = p54_rssi_to_dbm(priv,
(int)payload->ack_rssi);
/* Undo all changes to the frame. */
switch (entry_data->key_type) {
case P54_CRYPTO_TKIPMICHAEL: {
u8 *iv = (u8 *)(entry_data->align + pad +
entry_data->crypt_offset);
/* Restore the original TKIP IV. */
iv[2] = iv[0];
iv[0] = iv[1];
iv[1] = (iv[0] | 0x20) & 0x7f; /* WEPSeed - 8.3.2.2 */
frame_len -= 12; /* remove TKIP_MMIC + TKIP_ICV */
break;
}
case P54_CRYPTO_AESCCMP:
frame_len -= 8; /* remove CCMP_MIC */
break;
case P54_CRYPTO_WEP:
frame_len -= 4; /* remove WEP_ICV */
break;
}
skb_trim(entry, frame_len);
skb_pull(entry, sizeof(*hdr) + pad + sizeof(*entry_data));
ieee80211_tx_status_irqsafe(priv->hw, entry);
}
static void p54_rx_eeprom_readback(struct p54_common *priv,
struct sk_buff *skb)
{
struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
struct p54_eeprom_lm86 *eeprom = (struct p54_eeprom_lm86 *) hdr->data;
struct sk_buff *tmp;
if (!priv->eeprom)
return ;
if (priv->fw_var >= 0x509) {
memcpy(priv->eeprom, eeprom->v2.data,
le16_to_cpu(eeprom->v2.len));
} else {
memcpy(priv->eeprom, eeprom->v1.data,
le16_to_cpu(eeprom->v1.len));
}
priv->eeprom = NULL;
tmp = p54_find_and_unlink_skb(priv, hdr->req_id);
dev_kfree_skb_any(tmp);
complete(&priv->eeprom_comp);
}
static void p54_rx_stats(struct p54_common *priv, struct sk_buff *skb)
{
struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
struct p54_statistics *stats = (struct p54_statistics *) hdr->data;
struct sk_buff *tmp;
struct ieee80211_channel *chan;
unsigned int i, rssi, tx, cca, dtime, dtotal, dcca, dtx, drssi, unit;
u32 tsf32;
if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
return ;
tsf32 = le32_to_cpu(stats->tsf32);
if (tsf32 < priv->tsf_low32)
priv->tsf_high32++;
priv->tsf_low32 = tsf32;
priv->stats.dot11RTSFailureCount = le32_to_cpu(stats->rts_fail);
priv->stats.dot11RTSSuccessCount = le32_to_cpu(stats->rts_success);
priv->stats.dot11FCSErrorCount = le32_to_cpu(stats->rx_bad_fcs);
priv->noise = p54_rssi_to_dbm(priv, le32_to_cpu(stats->noise));
/*
* STSW450X LMAC API page 26 - 3.8 Statistics
* "The exact measurement period can be derived from the
* timestamp member".
*/
dtime = tsf32 - priv->survey_raw.timestamp;
/*
* STSW450X LMAC API page 26 - 3.8.1 Noise histogram
* The LMAC samples RSSI, CCA and transmit state at regular
* periods (typically 8 times per 1k [as in 1024] usec).
*/
cca = le32_to_cpu(stats->sample_cca);
tx = le32_to_cpu(stats->sample_tx);
rssi = 0;
for (i = 0; i < ARRAY_SIZE(stats->sample_noise); i++)
rssi += le32_to_cpu(stats->sample_noise[i]);
dcca = cca - priv->survey_raw.cached_cca;
drssi = rssi - priv->survey_raw.cached_rssi;
dtx = tx - priv->survey_raw.cached_tx;
dtotal = dcca + drssi + dtx;
/*
* update statistics when more than a second is over since the
* last call, or when a update is badly needed.
*/
if (dtotal && (priv->update_stats || dtime >= USEC_PER_SEC) &&
dtime >= dtotal) {
priv->survey_raw.timestamp = tsf32;
priv->update_stats = false;
unit = dtime / dtotal;
if (dcca) {
priv->survey_raw.cca += dcca * unit;
priv->survey_raw.cached_cca = cca;
}
if (dtx) {
priv->survey_raw.tx += dtx * unit;
priv->survey_raw.cached_tx = tx;
}
if (drssi) {
priv->survey_raw.rssi += drssi * unit;
priv->survey_raw.cached_rssi = rssi;
}
/* 1024 usec / 8 times = 128 usec / time */
if (!(priv->phy_ps || priv->phy_idle))
priv->survey_raw.active += dtotal * unit;
else
priv->survey_raw.active += (dcca + dtx) * unit;
}
chan = priv->curchan;
if (chan) {
struct survey_info *survey = &priv->survey[chan->hw_value];
survey->noise = clamp(priv->noise, -128, 127);
survey->time = priv->survey_raw.active;
survey->time_tx = priv->survey_raw.tx;
survey->time_busy = priv->survey_raw.tx +
priv->survey_raw.cca;
do_div(survey->time, 1024);
do_div(survey->time_tx, 1024);
do_div(survey->time_busy, 1024);
}
tmp = p54_find_and_unlink_skb(priv, hdr->req_id);
dev_kfree_skb_any(tmp);
complete(&priv->stat_comp);
}
static void p54_rx_trap(struct p54_common *priv, struct sk_buff *skb)
{
struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
struct p54_trap *trap = (struct p54_trap *) hdr->data;
u16 event = le16_to_cpu(trap->event);
u16 freq = le16_to_cpu(trap->frequency);
switch (event) {
case P54_TRAP_BEACON_TX:
break;
case P54_TRAP_RADAR:
wiphy_info(priv->hw->wiphy, "radar (freq:%d MHz)\n", freq);
break;
case P54_TRAP_NO_BEACON:
if (priv->vif)
ieee80211_beacon_loss(priv->vif);
break;
case P54_TRAP_SCAN:
break;
case P54_TRAP_TBTT:
break;
case P54_TRAP_TIMER:
break;
case P54_TRAP_FAA_RADIO_OFF:
wiphy_rfkill_set_hw_state(priv->hw->wiphy, true);
break;
case P54_TRAP_FAA_RADIO_ON:
wiphy_rfkill_set_hw_state(priv->hw->wiphy, false);
break;
default:
wiphy_info(priv->hw->wiphy, "received event:%x freq:%d\n",
event, freq);
break;
}
}
static int p54_rx_control(struct p54_common *priv, struct sk_buff *skb)
{
struct p54_hdr *hdr = (struct p54_hdr *) skb->data;
switch (le16_to_cpu(hdr->type)) {
case P54_CONTROL_TYPE_TXDONE:
p54_rx_frame_sent(priv, skb);
break;
case P54_CONTROL_TYPE_TRAP:
p54_rx_trap(priv, skb);
break;
case P54_CONTROL_TYPE_BBP:
break;
case P54_CONTROL_TYPE_STAT_READBACK:
p54_rx_stats(priv, skb);
break;
case P54_CONTROL_TYPE_EEPROM_READBACK:
p54_rx_eeprom_readback(priv, skb);
break;
default:
wiphy_debug(priv->hw->wiphy,
"not handling 0x%02x type control frame\n",
le16_to_cpu(hdr->type));
break;
}
return 0;
}
/* returns zero if skb can be reused */
int p54_rx(struct ieee80211_hw *dev, struct sk_buff *skb)
{
struct p54_common *priv = dev->priv;
u16 type = le16_to_cpu(*((__le16 *)skb->data));
if (type & P54_HDR_FLAG_CONTROL)
return p54_rx_control(priv, skb);
else
return p54_rx_data(priv, skb);
}
EXPORT_SYMBOL_GPL(p54_rx);
static void p54_tx_80211_header(struct p54_common *priv, struct sk_buff *skb,
struct ieee80211_tx_info *info,
struct ieee80211_sta *sta,
u8 *queue, u32 *extra_len, u16 *flags, u16 *aid,
bool *burst_possible)
{
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
if (ieee80211_is_data_qos(hdr->frame_control))
*burst_possible = true;
else
*burst_possible = false;
if (!(info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ))
*flags |= P54_HDR_FLAG_DATA_OUT_SEQNR;
if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER)
*flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL;
if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
*flags |= P54_HDR_FLAG_DATA_OUT_NOCANCEL;
*queue = skb_get_queue_mapping(skb) + P54_QUEUE_DATA;
switch (priv->mode) {
case NL80211_IFTYPE_MONITOR:
/*
* We have to set P54_HDR_FLAG_DATA_OUT_PROMISC for
* every frame in promiscuous/monitor mode.
* see STSW45x0C LMAC API - page 12.
*/
*aid = 0;
*flags |= P54_HDR_FLAG_DATA_OUT_PROMISC;
break;
case NL80211_IFTYPE_STATION:
*aid = 1;
break;
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_ADHOC:
case NL80211_IFTYPE_MESH_POINT:
if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) {
*aid = 0;
*queue = P54_QUEUE_CAB;
return;
}
if (unlikely(ieee80211_is_mgmt(hdr->frame_control))) {
if (ieee80211_is_probe_resp(hdr->frame_control)) {
*aid = 0;
*flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP |
P54_HDR_FLAG_DATA_OUT_NOCANCEL;
return;
} else if (ieee80211_is_beacon(hdr->frame_control)) {
*aid = 0;
if (info->flags & IEEE80211_TX_CTL_INJECTED) {
/*
* Injecting beacons on top of a AP is
* not a good idea... nevertheless,
* it should be doable.
*/
return;
}
*flags |= P54_HDR_FLAG_DATA_OUT_TIMESTAMP;
*queue = P54_QUEUE_BEACON;
*extra_len = IEEE80211_MAX_TIM_LEN;
return;
}
}
if (sta)
*aid = sta->aid;
break;
}
}
static u8 p54_convert_algo(u32 cipher)
{
switch (cipher) {
case WLAN_CIPHER_SUITE_WEP40:
case WLAN_CIPHER_SUITE_WEP104:
return P54_CRYPTO_WEP;
case WLAN_CIPHER_SUITE_TKIP:
return P54_CRYPTO_TKIPMICHAEL;
case WLAN_CIPHER_SUITE_CCMP:
return P54_CRYPTO_AESCCMP;
default:
return 0;
}
}
void p54_tx_80211(struct ieee80211_hw *dev,
struct ieee80211_tx_control *control,
struct sk_buff *skb)
{
struct p54_common *priv = dev->priv;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct p54_tx_info *p54info;
struct p54_hdr *hdr;
struct p54_tx_data *txhdr;
unsigned int padding, len, extra_len = 0;
int i, j, ridx;
u16 hdr_flags = 0, aid = 0;
u8 rate, queue = 0, crypt_offset = 0;
u8 cts_rate = 0x20;
u8 rc_flags;
u8 calculated_tries[4];
u8 nrates = 0, nremaining = 8;
bool burst_allowed = false;
p54_tx_80211_header(priv, skb, info, control->sta, &queue, &extra_len,
&hdr_flags, &aid, &burst_allowed);
if (p54_tx_qos_accounting_alloc(priv, skb, queue)) {
ieee80211_free_txskb(dev, skb);
return;
}
padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3;
len = skb->len;
if (info->control.hw_key) {
crypt_offset = ieee80211_get_hdrlen_from_skb(skb);
if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
u8 *iv = (u8 *)(skb->data + crypt_offset);
/*
* The firmware excepts that the IV has to have
* this special format
*/
iv[1] = iv[0];
iv[0] = iv[2];
iv[2] = 0;
}
}
txhdr = skb_push(skb, sizeof(*txhdr) + padding);
hdr = skb_push(skb, sizeof(*hdr));
if (padding)
hdr_flags |= P54_HDR_FLAG_DATA_ALIGN;
hdr->type = cpu_to_le16(aid);
hdr->rts_tries = info->control.rates[0].count;
/*
* we register the rates in perfect order, and
* RTS/CTS won't happen on 5 GHz
*/
cts_rate = info->control.rts_cts_rate_idx;
memset(&txhdr->rateset, 0, sizeof(txhdr->rateset));
/* see how many rates got used */
for (i = 0; i < dev->max_rates; i++) {
if (info->control.rates[i].idx < 0)
break;
nrates++;
}
/* limit tries to 8/nrates per rate */
for (i = 0; i < nrates; i++) {
/*
* The magic expression here is equivalent to 8/nrates for
* all values that matter, but avoids division and jumps.
* Note that nrates can only take the values 1 through 4.
*/
calculated_tries[i] = min_t(int, ((15 >> nrates) | 1) + 1,
info->control.rates[i].count);
nremaining -= calculated_tries[i];
}
/* if there are tries left, distribute from back to front */
for (i = nrates - 1; nremaining > 0 && i >= 0; i--) {
int tmp = info->control.rates[i].count - calculated_tries[i];
if (tmp <= 0)
continue;
/* RC requested more tries at this rate */
tmp = min_t(int, tmp, nremaining);
calculated_tries[i] += tmp;
nremaining -= tmp;
}
ridx = 0;
for (i = 0; i < nrates && ridx < 8; i++) {
/* we register the rates in perfect order */
rate = info->control.rates[i].idx;
if (info->band == NL80211_BAND_5GHZ)
rate += 4;
/* store the count we actually calculated for TX status */
info->control.rates[i].count = calculated_tries[i];
rc_flags = info->control.rates[i].flags;
if (rc_flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) {
rate |= 0x10;
cts_rate |= 0x10;
}
if (rc_flags & IEEE80211_TX_RC_USE_RTS_CTS) {
burst_allowed = false;
rate |= 0x40;
} else if (rc_flags & IEEE80211_TX_RC_USE_CTS_PROTECT) {
rate |= 0x20;
burst_allowed = false;
}
for (j = 0; j < calculated_tries[i] && ridx < 8; j++) {
txhdr->rateset[ridx] = rate;
ridx++;
}
}
if (burst_allowed)
hdr_flags |= P54_HDR_FLAG_DATA_OUT_BURST;
/* TODO: enable bursting */
hdr->flags = cpu_to_le16(hdr_flags);
hdr->tries = ridx;
txhdr->rts_rate_idx = 0;
if (info->control.hw_key) {
txhdr->key_type = p54_convert_algo(info->control.hw_key->cipher);
txhdr->key_len = min((u8)16, info->control.hw_key->keylen);
memcpy(txhdr->key, info->control.hw_key->key, txhdr->key_len);
if (info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) {
/* reserve space for the MIC key */
len += 8;
skb_put_data(skb,
&(info->control.hw_key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY]),
8);
}
/* reserve some space for ICV */
len += info->control.hw_key->icv_len;
skb_put_zero(skb, info->control.hw_key->icv_len);
} else {
txhdr->key_type = 0;
txhdr->key_len = 0;
}
txhdr->crypt_offset = crypt_offset;
txhdr->hw_queue = queue;
txhdr->backlog = priv->tx_stats[queue].len - 1;
memset(txhdr->durations, 0, sizeof(txhdr->durations));
txhdr->tx_antenna = 2 & priv->tx_diversity_mask;
if (priv->rxhw == 5) {
txhdr->longbow.cts_rate = cts_rate;
txhdr->longbow.output_power = cpu_to_le16(priv->output_power);
} else {
txhdr->normal.output_power = priv->output_power;
txhdr->normal.cts_rate = cts_rate;
}
if (padding)
txhdr->align[0] = padding;
hdr->len = cpu_to_le16(len);
/* modifies skb->cb and with it info, so must be last! */
p54info = (void *) info->rate_driver_data;
p54info->extra_len = extra_len;
p54_tx(priv, skb);
}