// SPDX-License-Identifier: ISC
/* Copyright (C) 2020 MediaTek Inc. */
#include <linux/fs.h>
#include "mt7915.h"
#include "mcu.h"
#include "mac.h"
#include "eeprom.h"
#define fw_name(_dev, name, ...) ({ \
char *_fw; \
switch (mt76_chip(&(_dev)->mt76)) { \
case 0x7915: \
_fw = MT7915_## name; \
break ; \
case 0x7981: \
_fw = MT7981_## name; \
break ; \
case 0x7986: \
_fw = MT7986_## name## __VA_ARGS__; \
break ; \
default : \
_fw = MT7916_## name; \
break ; \
} \
_fw; \
})
#define fw_name_var(_dev, name) (mt7915_check_adie(dev, false ) ? \
fw_name(_dev, name) : \
fw_name(_dev, name, _MT7975))
#define MCU_PATCH_ADDRESS 0x200000
#define HE_PHY(p, c) u8_get_bits(c, IEEE80211_HE_PHY_## p)
#define HE_MAC(m, c) u8_get_bits(c, IEEE80211_HE_MAC_## m)
static bool sr_scene_detect = true ;
module_param(sr_scene_detect, bool , 0644);
MODULE_PARM_DESC(sr_scene_detect, "Enable firmware scene detection algorithm" );
static u8
mt7915_mcu_get_sta_nss(u16 mcs_map)
{
u8 nss;
for (nss = 8; nss > 0; nss--) {
u8 nss_mcs = (mcs_map >> (2 * (nss - 1))) & 3;
if (nss_mcs != IEEE80211_VHT_MCS_NOT_SUPPORTED)
break ;
}
return nss - 1;
}
static void
mt7915_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs,
u16 mcs_map)
{
struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
struct mt7915_dev *dev = msta->vif->phy->dev;
enum nl80211_band band = msta->vif->phy->mt76->chandef.chan->band;
const u16 *mask = msta->vif->bitrate_mask.control[band].he_mcs;
int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
for (nss = 0; nss < max_nss; nss++) {
int mcs;
switch ((mcs_map >> (2 * nss)) & 0x3) {
case IEEE80211_HE_MCS_SUPPORT_0_11:
mcs = GENMASK(11, 0);
break ;
case IEEE80211_HE_MCS_SUPPORT_0_9:
mcs = GENMASK(9, 0);
break ;
case IEEE80211_HE_MCS_SUPPORT_0_7:
mcs = GENMASK(7, 0);
break ;
default :
mcs = 0;
}
mcs = mcs ? fls(mcs & mask[nss]) - 1 : -1;
switch (mcs) {
case 0 ... 7:
mcs = IEEE80211_HE_MCS_SUPPORT_0_7;
break ;
case 8 ... 9:
mcs = IEEE80211_HE_MCS_SUPPORT_0_9;
break ;
case 10 ... 11:
mcs = IEEE80211_HE_MCS_SUPPORT_0_11;
break ;
default :
mcs = IEEE80211_HE_MCS_NOT_SUPPORTED;
break ;
}
mcs_map &= ~(0x3 << (nss * 2));
mcs_map |= mcs << (nss * 2);
/* only support 2ss on 160MHz for mt7915 */
if (is_mt7915(&dev->mt76) && nss > 1 &&
sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
break ;
}
*he_mcs = cpu_to_le16(mcs_map);
}
static void
mt7915_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs,
const u16 *mask)
{
struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
struct mt7915_dev *dev = msta->vif->phy->dev;
u16 mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map);
int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
u16 mcs;
for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) {
switch (mcs_map & 0x3) {
case IEEE80211_VHT_MCS_SUPPORT_0_9:
mcs = GENMASK(9, 0);
break ;
case IEEE80211_VHT_MCS_SUPPORT_0_8:
mcs = GENMASK(8, 0);
break ;
case IEEE80211_VHT_MCS_SUPPORT_0_7:
mcs = GENMASK(7, 0);
break ;
default :
mcs = 0;
}
vht_mcs[nss] = cpu_to_le16(mcs & mask[nss]);
/* only support 2ss on 160MHz for mt7915 */
if (is_mt7915(&dev->mt76) && nss > 1 &&
sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
break ;
}
}
static void
mt7915_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs,
const u8 *mask)
{
int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss;
for (nss = 0; nss < max_nss; nss++)
ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss];
}
static int
mt7915_mcu_parse_response(struct mt76_dev *mdev, int cmd,
struct sk_buff *skb, int seq)
{
struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
struct mt76_connac2_mcu_rxd *rxd;
int ret = 0;
if (!skb) {
dev_err(mdev->dev, "Message %08x (seq %d) timeout\n" ,
cmd, seq);
if (!test_and_set_bit(MT76_MCU_RESET, &dev->mphy.state)) {
dev->recovery.restart = true ;
wake_up(&dev->mt76.mcu.wait);
queue_work(dev->mt76.wq, &dev->reset_work);
wake_up(&dev->reset_wait);
}
return -ETIMEDOUT;
}
rxd = (struct mt76_connac2_mcu_rxd *)skb->data;
if (seq != rxd->seq &&
!(rxd->eid == MCU_CMD_EXT_CID &&
rxd->ext_eid == MCU_EXT_EVENT_WA_TX_STAT))
return -EAGAIN;
if (cmd == MCU_CMD(PATCH_SEM_CONTROL)) {
skb_pull(skb, sizeof (*rxd) - 4);
ret = *skb->data;
} else if (cmd == MCU_EXT_CMD(THERMAL_CTRL)) {
skb_pull(skb, sizeof (*rxd) + 4);
ret = le32_to_cpu(*(__le32 *)skb->data);
} else {
skb_pull(skb, sizeof (struct mt76_connac2_mcu_rxd));
}
return ret;
}
static void
mt7915_mcu_set_timeout(struct mt76_dev *mdev, int cmd)
{
mdev->mcu.timeout = 5 * HZ;
if ((cmd & __MCU_CMD_FIELD_ID) != MCU_CMD_EXT_CID)
return ;
switch (FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd)) {
case MCU_EXT_CMD_THERMAL_CTRL:
case MCU_EXT_CMD_GET_MIB_INFO:
case MCU_EXT_CMD_PHY_STAT_INFO:
case MCU_EXT_CMD_STA_REC_UPDATE:
case MCU_EXT_CMD_BSS_INFO_UPDATE:
mdev->mcu.timeout = 2 * HZ;
return ;
case MCU_EXT_CMD_EFUSE_BUFFER_MODE:
mdev->mcu.timeout = 10 * HZ;
return ;
default :
break ;
}
}
static int
mt7915_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb,
int cmd, int *wait_seq)
{
struct mt7915_dev *dev = container_of(mdev, struct mt7915_dev, mt76);
enum mt76_mcuq_id qid;
if (cmd == MCU_CMD(FW_SCATTER))
qid = MT_MCUQ_FWDL;
else if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state))
qid = MT_MCUQ_WA;
else
qid = MT_MCUQ_WM;
mt7915_mcu_set_timeout(mdev, cmd);
return mt76_tx_queue_skb_raw(dev, mdev->q_mcu[qid], skb, 0);
}
int mt7915_mcu_wa_cmd(struct mt7915_dev *dev, int cmd, u32 a1, u32 a2, u32 a3)
{
struct {
__le32 args[3];
} req = {
.args = {
cpu_to_le32(a1),
cpu_to_le32(a2),
cpu_to_le32(a3),
},
};
return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof (req), false );
}
static void
mt7915_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
{
if (!vif->bss_conf.csa_active || vif->type == NL80211_IFTYPE_STATION)
return ;
ieee80211_csa_finish(vif, 0);
}
static void
mt7915_mcu_rx_csa_notify(struct mt7915_dev *dev, struct sk_buff *skb)
{
struct mt76_phy *mphy = &dev->mt76.phy;
struct mt7915_mcu_csa_notify *c;
c = (struct mt7915_mcu_csa_notify *)skb->data;
if (c->band_idx > MT_BAND1)
return ;
if ((c->band_idx && !dev->phy.mt76->band_idx) &&
dev->mt76.phys[MT_BAND1])
mphy = dev->mt76.phys[MT_BAND1];
ieee80211_iterate_active_interfaces_atomic(mphy->hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7915_mcu_csa_finish, mphy->hw);
}
static void
mt7915_mcu_rx_thermal_notify(struct mt7915_dev *dev, struct sk_buff *skb)
{
struct mt76_phy *mphy = &dev->mt76.phy;
struct mt7915_mcu_thermal_notify *t;
struct mt7915_phy *phy;
t = (struct mt7915_mcu_thermal_notify *)skb->data;
if (t->ctrl.ctrl_id != THERMAL_PROTECT_ENABLE)
return ;
if (t->ctrl.band_idx > MT_BAND1)
return ;
if ((t->ctrl.band_idx && !dev->phy.mt76->band_idx) &&
dev->mt76.phys[MT_BAND1])
mphy = dev->mt76.phys[MT_BAND1];
phy = mphy->priv;
phy->throttle_state = t->ctrl.duty.duty_cycle;
}
static void
mt7915_mcu_rx_radar_detected(struct mt7915_dev *dev, struct sk_buff *skb)
{
struct mt76_phy *mphy = &dev->mt76.phy;
struct mt7915_mcu_rdd_report *r;
u32 sku;
r = (struct mt7915_mcu_rdd_report *)skb->data;
switch (r->rdd_idx) {
case MT_RDD_IDX_BAND0:
break ;
case MT_RDD_IDX_BAND1:
sku = mt7915_check_adie(dev, true );
/* the main phy is bound to band 1 for this sku */
if (is_mt7986(&dev->mt76) &&
(sku == MT7975_ONE_ADIE || sku == MT7976_ONE_ADIE))
break ;
mphy = dev->mt76.phys[MT_BAND1];
break ;
case MT_RDD_IDX_BACKGROUND:
if (!dev->rdd2_phy)
return ;
mphy = dev->rdd2_phy->mt76;
break ;
default :
dev_err(dev->mt76.dev, "Unknown RDD idx %d\n" , r->rdd_idx);
return ;
}
if (!mphy)
return ;
if (r->rdd_idx == MT_RDD_IDX_BACKGROUND)
cfg80211_background_radar_event(mphy->hw->wiphy,
&dev->rdd2_chandef,
GFP_ATOMIC);
else
ieee80211_radar_detected(mphy->hw, NULL);
dev->hw_pattern++;
}
static void
mt7915_mcu_rx_log_message(struct mt7915_dev *dev, struct sk_buff *skb)
{
struct mt76_connac2_mcu_rxd *rxd;
int len = skb->len - sizeof (*rxd);
const char *data, *type;
rxd = (struct mt76_connac2_mcu_rxd *)skb->data;
data = (char *)&rxd[1];
switch (rxd->s2d_index) {
case 0:
if (mt7915_debugfs_rx_log(dev, data, len))
return ;
type = "WM" ;
break ;
case 2:
type = "WA" ;
break ;
default :
type = "unknown" ;
break ;
}
wiphy_info(mt76_hw(dev)->wiphy, "%s: %.*s" , type, len, data);
}
static void
mt7915_mcu_cca_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
{
if (!vif->bss_conf.color_change_active || vif->type == NL80211_IFTYPE_STATION)
return ;
ieee80211_color_change_finish(vif, 0);
}
static void
mt7915_mcu_rx_bcc_notify(struct mt7915_dev *dev, struct sk_buff *skb)
{
struct mt76_phy *mphy = &dev->mt76.phy;
struct mt7915_mcu_bcc_notify *b;
b = (struct mt7915_mcu_bcc_notify *)skb->data;
if (b->band_idx > MT_BAND1)
return ;
if ((b->band_idx && !dev->phy.mt76->band_idx) &&
dev->mt76.phys[MT_BAND1])
mphy = dev->mt76.phys[MT_BAND1];
ieee80211_iterate_active_interfaces_atomic(mphy->hw,
IEEE80211_IFACE_ITER_RESUME_ALL,
mt7915_mcu_cca_finish, mphy->hw);
}
static void
mt7915_mcu_rx_ext_event(struct mt7915_dev *dev, struct sk_buff *skb)
{
struct mt76_connac2_mcu_rxd *rxd;
rxd = (struct mt76_connac2_mcu_rxd *)skb->data;
switch (rxd->ext_eid) {
case MCU_EXT_EVENT_THERMAL_PROTECT:
mt7915_mcu_rx_thermal_notify(dev, skb);
break ;
case MCU_EXT_EVENT_RDD_REPORT:
mt7915_mcu_rx_radar_detected(dev, skb);
break ;
case MCU_EXT_EVENT_CSA_NOTIFY:
mt7915_mcu_rx_csa_notify(dev, skb);
break ;
case MCU_EXT_EVENT_FW_LOG_2_HOST:
mt7915_mcu_rx_log_message(dev, skb);
break ;
case MCU_EXT_EVENT_BCC_NOTIFY:
mt7915_mcu_rx_bcc_notify(dev, skb);
break ;
default :
break ;
}
}
static void
mt7915_mcu_rx_unsolicited_event(struct mt7915_dev *dev, struct sk_buff *skb)
{
struct mt76_connac2_mcu_rxd *rxd;
rxd = (struct mt76_connac2_mcu_rxd *)skb->data;
switch (rxd->eid) {
case MCU_EVENT_EXT:
mt7915_mcu_rx_ext_event(dev, skb);
break ;
default :
break ;
}
dev_kfree_skb(skb);
}
void mt7915_mcu_rx_event(struct mt7915_dev *dev, struct sk_buff *skb)
{
struct mt76_connac2_mcu_rxd *rxd;
rxd = (struct mt76_connac2_mcu_rxd *)skb->data;
if ((rxd->ext_eid == MCU_EXT_EVENT_THERMAL_PROTECT ||
rxd->ext_eid == MCU_EXT_EVENT_FW_LOG_2_HOST ||
rxd->ext_eid == MCU_EXT_EVENT_ASSERT_DUMP ||
rxd->ext_eid == MCU_EXT_EVENT_PS_SYNC ||
rxd->ext_eid == MCU_EXT_EVENT_BCC_NOTIFY ||
!rxd->seq) &&
!(rxd->eid == MCU_CMD_EXT_CID &&
rxd->ext_eid == MCU_EXT_EVENT_WA_TX_STAT))
mt7915_mcu_rx_unsolicited_event(dev, skb);
else
mt76_mcu_rx_event(&dev->mt76, skb);
}
static struct tlv *
mt7915_mcu_add_nested_subtlv(struct sk_buff *skb, int sub_tag, int sub_len,
__le16 *sub_ntlv, __le16 *len)
{
struct tlv *ptlv, tlv = {
.tag = cpu_to_le16(sub_tag),
.len = cpu_to_le16(sub_len),
};
ptlv = skb_put_zero(skb, sub_len);
memcpy(ptlv, &tlv, sizeof (tlv));
le16_add_cpu(sub_ntlv, 1);
le16_add_cpu(len, sub_len);
return ptlv;
}
/** bss info **/
struct mt7915_he_obss_narrow_bw_ru_data {
bool tolerated;
};
static void mt7915_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy,
struct cfg80211_bss *bss,
void *_data)
{
struct mt7915_he_obss_narrow_bw_ru_data *data = _data;
const struct element *elem;
rcu_read_lock();
elem = ieee80211_bss_get_elem(bss, WLAN_EID_EXT_CAPABILITY);
if (!elem || elem->datalen <= 10 ||
!(elem->data[10] &
WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT))
data->tolerated = false ;
rcu_read_unlock();
}
static bool mt7915_check_he_obss_narrow_bw_ru(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
{
struct mt7915_he_obss_narrow_bw_ru_data iter_data = {
.tolerated = true ,
};
if (!(vif->bss_conf.chanreq.oper.chan->flags & IEEE80211_CHAN_RADAR))
return false ;
cfg80211_bss_iter(hw->wiphy, &vif->bss_conf.chanreq.oper,
mt7915_check_he_obss_narrow_bw_ru_iter,
&iter_data);
/*
* If there is at least one AP on radar channel that cannot
* tolerate 26-tone RU UL OFDMA transmissions using HE TB PPDU.
*/
return !iter_data.tolerated;
}
static void
mt7915_mcu_bss_rfch_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
struct mt7915_phy *phy)
{
struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
struct bss_info_rf_ch *ch;
struct tlv *tlv;
int freq1 = chandef->center_freq1;
tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_RF_CH, sizeof (*ch));
ch = (struct bss_info_rf_ch *)tlv;
ch->pri_ch = chandef->chan->hw_value;
ch->center_ch0 = ieee80211_frequency_to_channel(freq1);
ch->bw = mt76_connac_chan_bw(chandef);
if (chandef->width == NL80211_CHAN_WIDTH_80P80) {
int freq2 = chandef->center_freq2;
ch->center_ch1 = ieee80211_frequency_to_channel(freq2);
}
if (vif->bss_conf.he_support && vif->type == NL80211_IFTYPE_STATION) {
struct mt76_phy *mphy = phy->mt76;
ch->he_ru26_block =
mt7915_check_he_obss_narrow_bw_ru(mphy->hw, vif);
ch->he_all_disable = false ;
} else {
ch->he_all_disable = true ;
}
}
static void
mt7915_mcu_bss_ra_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
struct mt7915_phy *phy)
{
int max_nss = hweight8(phy->mt76->antenna_mask);
struct bss_info_ra *ra;
struct tlv *tlv;
tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_RA, sizeof (*ra));
ra = (struct bss_info_ra *)tlv;
ra->op_mode = vif->type == NL80211_IFTYPE_AP;
ra->adhoc_en = vif->type == NL80211_IFTYPE_ADHOC;
ra->short_preamble = true ;
ra->tx_streams = max_nss;
ra->rx_streams = max_nss;
ra->algo = 4;
ra->train_up_rule = 2;
ra->train_up_high_thres = 110;
ra->train_up_rule_rssi = -70;
ra->low_traffic_thres = 2;
ra->phy_cap = cpu_to_le32(0xfdf);
ra->interval = cpu_to_le32(500);
ra->fast_interval = cpu_to_le32(100);
}
static void
mt7915_mcu_bss_he_tlv(struct sk_buff *skb, struct ieee80211_vif *vif,
struct mt7915_phy *phy)
{
#define DEFAULT_HE_PE_DURATION 4
#define DEFAULT_HE_DURATION_RTS_THRES 1023
const struct ieee80211_sta_he_cap *cap;
struct bss_info_he *he;
struct tlv *tlv;
cap = mt76_connac_get_he_phy_cap(phy->mt76, vif);
tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HE_BASIC, sizeof (*he));
he = (struct bss_info_he *)tlv;
he->he_pe_duration = vif->bss_conf.htc_trig_based_pkt_ext;
if (!he->he_pe_duration)
he->he_pe_duration = DEFAULT_HE_PE_DURATION;
he->he_rts_thres = cpu_to_le16(vif->bss_conf.frame_time_rts_th);
if (!he->he_rts_thres)
he->he_rts_thres = cpu_to_le16(DEFAULT_HE_DURATION_RTS_THRES);
he->max_nss_mcs[CMD_HE_MCS_BW80] = cap->he_mcs_nss_supp.tx_mcs_80;
he->max_nss_mcs[CMD_HE_MCS_BW160] = cap->he_mcs_nss_supp.tx_mcs_160;
he->max_nss_mcs[CMD_HE_MCS_BW8080] = cap->he_mcs_nss_supp.tx_mcs_80p80;
}
static void
mt7915_mcu_bss_hw_amsdu_tlv(struct sk_buff *skb)
{
#define TXD_CMP_MAP1 GENMASK(15, 0)
#define TXD_CMP_MAP2 (GENMASK(31, 0) & ~BIT(23))
struct bss_info_hw_amsdu *amsdu;
struct tlv *tlv;
tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_HW_AMSDU, sizeof (*amsdu));
amsdu = (struct bss_info_hw_amsdu *)tlv;
amsdu->cmp_bitmap_0 = cpu_to_le32(TXD_CMP_MAP1);
amsdu->cmp_bitmap_1 = cpu_to_le32(TXD_CMP_MAP2);
amsdu->trig_thres = cpu_to_le16(2);
amsdu->enable = true ;
}
static void
mt7915_mcu_bss_bmc_tlv(struct sk_buff *skb, struct mt7915_phy *phy)
{
struct bss_info_bmc_rate *bmc;
struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
enum nl80211_band band = chandef->chan->band;
struct tlv *tlv;
tlv = mt76_connac_mcu_add_tlv(skb, BSS_INFO_BMC_RATE, sizeof (*bmc));
bmc = (struct bss_info_bmc_rate *)tlv;
if (band == NL80211_BAND_2GHZ) {
bmc->short_preamble = true ;
} else {
bmc->bc_trans = cpu_to_le16(0x2000);
bmc->mc_trans = cpu_to_le16(0x2080);
}
}
static int
mt7915_mcu_muar_config(struct mt7915_phy *phy, struct ieee80211_vif *vif,
bool bssid, bool enable)
{
struct mt7915_dev *dev = phy->dev;
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
u32 idx = mvif->mt76.omac_idx - REPEATER_BSSID_START;
u32 mask = phy->omac_mask >> 32 & ~BIT(idx);
const u8 *addr = vif->addr;
struct {
u8 mode;
u8 force_clear;
u8 clear_bitmap[8];
u8 entry_count;
u8 write;
u8 band;
u8 index;
u8 bssid;
u8 addr[ETH_ALEN];
} __packed req = {
.mode = !!mask || enable,
.entry_count = 1,
.write = 1,
.band = phy->mt76->band_idx,
.index = idx * 2 + bssid,
};
if (bssid)
addr = vif->bss_conf.bssid;
if (enable)
ether_addr_copy(req.addr, addr);
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MUAR_UPDATE), &req,
sizeof (req), true );
}
int mt7915_mcu_add_bss_info(struct mt7915_phy *phy,
struct ieee80211_vif *vif, int enable)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt7915_dev *dev = phy->dev;
struct sk_buff *skb;
if (mvif->mt76.omac_idx >= REPEATER_BSSID_START) {
mt7915_mcu_muar_config(phy, vif, false , enable);
mt7915_mcu_muar_config(phy, vif, true , enable);
}
skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, NULL,
MT7915_BSS_UPDATE_MAX_SIZE);
if (IS_ERR(skb))
return PTR_ERR(skb);
/* bss_omac must be first */
if (enable)
mt76_connac_mcu_bss_omac_tlv(skb, vif);
mt76_connac_mcu_bss_basic_tlv(skb, vif, NULL, phy->mt76,
mvif->sta.wcid.idx, enable);
if (vif->type == NL80211_IFTYPE_MONITOR)
goto out;
if (enable) {
mt7915_mcu_bss_rfch_tlv(skb, vif, phy);
mt7915_mcu_bss_bmc_tlv(skb, phy);
mt7915_mcu_bss_ra_tlv(skb, vif, phy);
mt7915_mcu_bss_hw_amsdu_tlv(skb);
if (vif->bss_conf.he_support)
mt7915_mcu_bss_he_tlv(skb, vif, phy);
if (mvif->mt76.omac_idx >= EXT_BSSID_START &&
mvif->mt76.omac_idx < REPEATER_BSSID_START)
mt76_connac_mcu_bss_ext_tlv(skb, &mvif->mt76);
}
out:
return mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_EXT_CMD(BSS_INFO_UPDATE), true );
}
/** starec & wtbl **/
int mt7915_mcu_add_tx_ba(struct mt7915_dev *dev,
struct ieee80211_ampdu_params *params,
bool enable)
{
struct mt7915_sta *msta = (struct mt7915_sta *)params->sta->drv_priv;
struct mt7915_vif *mvif = msta->vif;
int ret;
mt76_worker_disable(&dev->mt76.tx_worker);
if (enable && !params->amsdu)
msta->wcid.amsdu = false ;
ret = mt76_connac_mcu_sta_ba(&dev->mt76, &mvif->mt76, params,
MCU_EXT_CMD(STA_REC_UPDATE),
enable, true );
mt76_worker_enable(&dev->mt76.tx_worker);
return ret;
}
int mt7915_mcu_add_rx_ba(struct mt7915_dev *dev,
struct ieee80211_ampdu_params *params,
bool enable)
{
struct mt7915_sta *msta = (struct mt7915_sta *)params->sta->drv_priv;
struct mt7915_vif *mvif = msta->vif;
return mt76_connac_mcu_sta_ba(&dev->mt76, &mvif->mt76, params,
MCU_EXT_CMD(STA_REC_UPDATE),
enable, false );
}
static void
mt7915_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta,
struct ieee80211_vif *vif)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
struct ieee80211_he_mcs_nss_supp mcs_map;
struct sta_rec_he *he;
struct tlv *tlv;
u32 cap = 0;
if (!sta->deflink.he_cap.has_he)
return ;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE, sizeof (*he));
he = (struct sta_rec_he *)tlv;
if (elem->mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_HTC_HE)
cap |= STA_REC_HE_CAP_HTC;
if (elem->mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR)
cap |= STA_REC_HE_CAP_BSR;
if (elem->mac_cap_info[3] & IEEE80211_HE_MAC_CAP3_OMI_CONTROL)
cap |= STA_REC_HE_CAP_OM;
if (elem->mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_AMSDU_IN_AMPDU)
cap |= STA_REC_HE_CAP_AMSDU_IN_AMPDU;
if (elem->mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR)
cap |= STA_REC_HE_CAP_BQR;
if (elem->phy_cap_info[0] &
(IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_2G |
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_RU_MAPPING_IN_5G))
cap |= STA_REC_HE_CAP_BW20_RU242_SUPPORT;
if (mvif->cap.he_ldpc &&
(elem->phy_cap_info[1] &
IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD))
cap |= STA_REC_HE_CAP_LDPC;
if (elem->phy_cap_info[1] &
IEEE80211_HE_PHY_CAP1_HE_LTF_AND_GI_FOR_HE_PPDUS_0_8US)
cap |= STA_REC_HE_CAP_SU_PPDU_1LTF_8US_GI;
if (elem->phy_cap_info[2] &
IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US)
cap |= STA_REC_HE_CAP_NDP_4LTF_3DOT2MS_GI;
if (elem->phy_cap_info[2] &
IEEE80211_HE_PHY_CAP2_STBC_TX_UNDER_80MHZ)
cap |= STA_REC_HE_CAP_LE_EQ_80M_TX_STBC;
if (elem->phy_cap_info[2] &
IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ)
cap |= STA_REC_HE_CAP_LE_EQ_80M_RX_STBC;
if (elem->phy_cap_info[6] &
IEEE80211_HE_PHY_CAP6_TRIG_CQI_FB)
cap |= STA_REC_HE_CAP_TRIG_CQI_FK;
if (elem->phy_cap_info[6] &
IEEE80211_HE_PHY_CAP6_PARTIAL_BW_EXT_RANGE)
cap |= STA_REC_HE_CAP_PARTIAL_BW_EXT_RANGE;
if (elem->phy_cap_info[7] &
IEEE80211_HE_PHY_CAP7_HE_SU_MU_PPDU_4XLTF_AND_08_US_GI)
cap |= STA_REC_HE_CAP_SU_MU_PPDU_4LTF_8US_GI;
if (elem->phy_cap_info[7] &
IEEE80211_HE_PHY_CAP7_STBC_TX_ABOVE_80MHZ)
cap |= STA_REC_HE_CAP_GT_80M_TX_STBC;
if (elem->phy_cap_info[7] &
IEEE80211_HE_PHY_CAP7_STBC_RX_ABOVE_80MHZ)
cap |= STA_REC_HE_CAP_GT_80M_RX_STBC;
if (elem->phy_cap_info[8] &
IEEE80211_HE_PHY_CAP8_HE_ER_SU_PPDU_4XLTF_AND_08_US_GI)
cap |= STA_REC_HE_CAP_ER_SU_PPDU_4LTF_8US_GI;
if (elem->phy_cap_info[8] &
IEEE80211_HE_PHY_CAP8_HE_ER_SU_1XLTF_AND_08_US_GI)
cap |= STA_REC_HE_CAP_ER_SU_PPDU_1LTF_8US_GI;
if (elem->phy_cap_info[9] &
IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU)
cap |= STA_REC_HE_CAP_TX_1024QAM_UNDER_RU242;
if (elem->phy_cap_info[9] &
IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU)
cap |= STA_REC_HE_CAP_RX_1024QAM_UNDER_RU242;
he->he_cap = cpu_to_le32(cap);
mcs_map = sta->deflink.he_cap.he_mcs_nss_supp;
switch (sta->deflink.bandwidth) {
case IEEE80211_STA_RX_BW_160:
if (elem->phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G)
mt7915_mcu_set_sta_he_mcs(sta,
&he->max_nss_mcs[CMD_HE_MCS_BW8080],
le16_to_cpu(mcs_map.rx_mcs_80p80));
mt7915_mcu_set_sta_he_mcs(sta,
&he->max_nss_mcs[CMD_HE_MCS_BW160],
le16_to_cpu(mcs_map.rx_mcs_160));
fallthrough;
default :
mt7915_mcu_set_sta_he_mcs(sta,
&he->max_nss_mcs[CMD_HE_MCS_BW80],
le16_to_cpu(mcs_map.rx_mcs_80));
break ;
}
he->t_frame_dur =
HE_MAC(CAP1_TF_MAC_PAD_DUR_MASK, elem->mac_cap_info[1]);
he->max_ampdu_exp =
HE_MAC(CAP3_MAX_AMPDU_LEN_EXP_MASK, elem->mac_cap_info[3]);
he->bw_set =
HE_PHY(CAP0_CHANNEL_WIDTH_SET_MASK, elem->phy_cap_info[0]);
he->device_class =
HE_PHY(CAP1_DEVICE_CLASS_A, elem->phy_cap_info[1]);
he->punc_pream_rx =
HE_PHY(CAP1_PREAMBLE_PUNC_RX_MASK, elem->phy_cap_info[1]);
he->dcm_tx_mode =
HE_PHY(CAP3_DCM_MAX_CONST_TX_MASK, elem->phy_cap_info[3]);
he->dcm_tx_max_nss =
HE_PHY(CAP3_DCM_MAX_TX_NSS_2, elem->phy_cap_info[3]);
he->dcm_rx_mode =
HE_PHY(CAP3_DCM_MAX_CONST_RX_MASK, elem->phy_cap_info[3]);
he->dcm_rx_max_nss =
HE_PHY(CAP3_DCM_MAX_RX_NSS_2, elem->phy_cap_info[3]);
he->dcm_rx_max_nss =
HE_PHY(CAP8_DCM_MAX_RU_MASK, elem->phy_cap_info[8]);
he->pkt_ext = 2;
}
static void
mt7915_mcu_sta_muru_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
struct ieee80211_sta *sta, struct ieee80211_vif *vif)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem;
struct sta_rec_muru *muru;
struct tlv *tlv;
if (vif->type != NL80211_IFTYPE_STATION &&
vif->type != NL80211_IFTYPE_AP)
return ;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof (*muru));
muru = (struct sta_rec_muru *)tlv;
muru->cfg.mimo_dl_en = mvif->cap.he_mu_ebfer ||
mvif->cap.vht_mu_ebfer ||
mvif->cap.vht_mu_ebfee;
if (!is_mt7915(&dev->mt76))
muru->cfg.mimo_ul_en = true ;
muru->cfg.ofdma_dl_en = true ;
if (sta->deflink.vht_cap.vht_supported)
muru->mimo_dl.vht_mu_bfee =
!!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE);
if (!sta->deflink.he_cap.has_he)
return ;
muru->mimo_dl.partial_bw_dl_mimo =
HE_PHY(CAP6_PARTIAL_BANDWIDTH_DL_MUMIMO, elem->phy_cap_info[6]);
muru->mimo_ul.full_ul_mimo =
HE_PHY(CAP2_UL_MU_FULL_MU_MIMO, elem->phy_cap_info[2]);
muru->mimo_ul.partial_ul_mimo =
HE_PHY(CAP2_UL_MU_PARTIAL_MU_MIMO, elem->phy_cap_info[2]);
muru->ofdma_dl.punc_pream_rx =
HE_PHY(CAP1_PREAMBLE_PUNC_RX_MASK, elem->phy_cap_info[1]);
muru->ofdma_dl.he_20m_in_40m_2g =
HE_PHY(CAP8_20MHZ_IN_40MHZ_HE_PPDU_IN_2G, elem->phy_cap_info[8]);
muru->ofdma_dl.he_20m_in_160m =
HE_PHY(CAP8_20MHZ_IN_160MHZ_HE_PPDU, elem->phy_cap_info[8]);
muru->ofdma_dl.he_80m_in_160m =
HE_PHY(CAP8_80MHZ_IN_160MHZ_HE_PPDU, elem->phy_cap_info[8]);
muru->ofdma_ul.t_frame_dur =
HE_MAC(CAP1_TF_MAC_PAD_DUR_MASK, elem->mac_cap_info[1]);
muru->ofdma_ul.mu_cascading =
HE_MAC(CAP2_MU_CASCADING, elem->mac_cap_info[2]);
muru->ofdma_ul.uo_ra =
HE_MAC(CAP3_OFDMA_RA, elem->mac_cap_info[3]);
muru->ofdma_ul.rx_ctrl_frame_to_mbss =
HE_MAC(CAP3_RX_CTRL_FRAME_TO_MULTIBSS, elem->mac_cap_info[3]);
}
static void
mt7915_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
{
struct sta_rec_ht *ht;
struct tlv *tlv;
if (!sta->deflink.ht_cap.ht_supported)
return ;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof (*ht));
ht = (struct sta_rec_ht *)tlv;
ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap);
}
static void
mt7915_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta)
{
struct sta_rec_vht *vht;
struct tlv *tlv;
if (!sta->deflink.vht_cap.vht_supported)
return ;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof (*vht));
vht = (struct sta_rec_vht *)tlv;
vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap);
vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map;
}
static void
mt7915_mcu_sta_amsdu_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
struct ieee80211_vif *vif, struct ieee80211_sta *sta)
{
struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
struct sta_rec_amsdu *amsdu;
struct tlv *tlv;
if (vif->type != NL80211_IFTYPE_STATION &&
vif->type != NL80211_IFTYPE_AP)
return ;
if (!sta->deflink.agg.max_amsdu_len)
return ;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof (*amsdu));
amsdu = (struct sta_rec_amsdu *)tlv;
amsdu->max_amsdu_num = 8;
amsdu->amsdu_en = true ;
msta->wcid.amsdu = true ;
switch (sta->deflink.agg.max_amsdu_len) {
case IEEE80211_MAX_MPDU_LEN_VHT_11454:
if (!is_mt7915(&dev->mt76)) {
amsdu->max_mpdu_size =
IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454;
return ;
}
fallthrough;
case IEEE80211_MAX_MPDU_LEN_HT_7935:
case IEEE80211_MAX_MPDU_LEN_VHT_7991:
amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_7991;
return ;
default :
amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895;
return ;
}
}
static int
mt7915_mcu_sta_wtbl_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
struct ieee80211_vif *vif, struct ieee80211_sta *sta)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt7915_sta *msta;
struct wtbl_req_hdr *wtbl_hdr;
struct mt76_wcid *wcid;
struct tlv *tlv;
msta = sta ? (struct mt7915_sta *)sta->drv_priv : &mvif->sta;
wcid = sta ? &msta->wcid : NULL;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL, sizeof (struct tlv));
wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid,
WTBL_RESET_AND_SET, tlv,
&skb);
if (IS_ERR(wtbl_hdr))
return PTR_ERR(wtbl_hdr);
mt76_connac_mcu_wtbl_generic_tlv(&dev->mt76, skb, vif, sta, tlv,
wtbl_hdr);
mt76_connac_mcu_wtbl_hdr_trans_tlv(skb, vif, wcid, tlv, wtbl_hdr);
if (sta)
mt76_connac_mcu_wtbl_ht_tlv(&dev->mt76, skb, sta, tlv,
wtbl_hdr, mvif->cap.ht_ldpc,
mvif->cap.vht_ldpc);
return 0;
}
static inline bool
mt7915_is_ebf_supported(struct mt7915_phy *phy, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, bool bfee)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
int sts = hweight16(phy->mt76->chainmask);
if (vif->type != NL80211_IFTYPE_STATION &&
vif->type != NL80211_IFTYPE_AP)
return false ;
if (!bfee && sts < 2)
return false ;
if (sta->deflink.he_cap.has_he) {
struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
if (bfee)
return mvif->cap.he_su_ebfee &&
HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]);
else
return mvif->cap.he_su_ebfer &&
HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]);
}
if (sta->deflink.vht_cap.vht_supported) {
u32 cap = sta->deflink.vht_cap.cap;
if (bfee)
return mvif->cap.vht_su_ebfee &&
(cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE);
else
return mvif->cap.vht_su_ebfer &&
(cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE);
}
return false ;
}
static void
mt7915_mcu_sta_sounding_rate(struct sta_rec_bf *bf)
{
bf->sounding_phy = MT_PHY_TYPE_OFDM;
bf->ndp_rate = 0; /* mcs0 */
bf->ndpa_rate = MT7915_CFEND_RATE_DEFAULT; /* ofdm 24m */
bf->rept_poll_rate = MT7915_CFEND_RATE_DEFAULT; /* ofdm 24m */
}
static void
mt7915_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7915_phy *phy,
struct sta_rec_bf *bf)
{
struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs;
u8 n = 0;
bf->tx_mode = MT_PHY_TYPE_HT;
if ((mcs->tx_params & IEEE80211_HT_MCS_TX_RX_DIFF) &&
(mcs->tx_params & IEEE80211_HT_MCS_TX_DEFINED))
n = FIELD_GET(IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK,
mcs->tx_params);
else if (mcs->rx_mask[3])
n = 3;
else if (mcs->rx_mask[2])
n = 2;
else if (mcs->rx_mask[1])
n = 1;
bf->nrow = hweight8(phy->mt76->chainmask) - 1;
bf->ncol = min_t(u8, bf->nrow, n);
bf->ibf_ncol = n;
}
static void
mt7915_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7915_phy *phy,
struct sta_rec_bf *bf, bool explicit )
{
struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap;
struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap;
u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map);
u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map);
u8 tx_ant = hweight8(phy->mt76->chainmask) - 1;
bf->tx_mode = MT_PHY_TYPE_VHT;
if (explicit ) {
u8 sts, snd_dim;
mt7915_mcu_sta_sounding_rate(bf);
sts = FIELD_GET(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK,
pc->cap);
snd_dim = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK,
vc->cap);
bf->nrow = min_t(u8, min_t(u8, snd_dim, sts), tx_ant);
bf->ncol = min_t(u8, nss_mcs, bf->nrow);
bf->ibf_ncol = bf->ncol;
if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
bf->nrow = 1;
} else {
bf->nrow = tx_ant;
bf->ncol = min_t(u8, nss_mcs, bf->nrow);
bf->ibf_ncol = nss_mcs;
if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160)
bf->ibf_nrow = 1;
}
}
static void
mt7915_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
struct mt7915_phy *phy, struct sta_rec_bf *bf)
{
struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap;
struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem;
const struct ieee80211_sta_he_cap *vc =
mt76_connac_get_he_phy_cap(phy->mt76, vif);
const struct ieee80211_he_cap_elem *ve = &vc->he_cap_elem;
u16 mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80);
u8 nss_mcs = mt7915_mcu_get_sta_nss(mcs_map);
u8 snd_dim, sts;
bf->tx_mode = MT_PHY_TYPE_HE_SU;
mt7915_mcu_sta_sounding_rate(bf);
bf->trigger_su = HE_PHY(CAP6_TRIG_SU_BEAMFORMING_FB,
pe->phy_cap_info[6]);
bf->trigger_mu = HE_PHY(CAP6_TRIG_MU_BEAMFORMING_PARTIAL_BW_FB,
pe->phy_cap_info[6]);
snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
ve->phy_cap_info[5]);
sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_MASK,
pe->phy_cap_info[4]);
bf->nrow = min_t(u8, snd_dim, sts);
bf->ncol = min_t(u8, nss_mcs, bf->nrow);
bf->ibf_ncol = bf->ncol;
if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160)
return ;
/* go over for 160MHz and 80p80 */
if (pe->phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G) {
mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_160);
nss_mcs = mt7915_mcu_get_sta_nss(mcs_map);
bf->ncol_gt_bw80 = nss_mcs;
}
if (pe->phy_cap_info[0] &
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) {
mcs_map = le16_to_cpu(pc->he_mcs_nss_supp.rx_mcs_80p80);
nss_mcs = mt7915_mcu_get_sta_nss(mcs_map);
if (bf->ncol_gt_bw80)
bf->ncol_gt_bw80 = min_t(u8, bf->ncol_gt_bw80, nss_mcs);
else
bf->ncol_gt_bw80 = nss_mcs;
}
snd_dim = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_ABOVE_80MHZ_MASK,
ve->phy_cap_info[5]);
sts = HE_PHY(CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_MASK,
pe->phy_cap_info[4]);
bf->nrow_gt_bw80 = min_t(int , snd_dim, sts);
}
static void
mt7915_mcu_sta_bfer_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
struct ieee80211_vif *vif, struct ieee80211_sta *sta)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt7915_phy *phy = mvif->phy;
int tx_ant = hweight8(phy->mt76->chainmask) - 1;
struct sta_rec_bf *bf;
struct tlv *tlv;
static const u8 matrix[4][4] = {
{0, 0, 0, 0},
{1, 1, 0, 0}, /* 2x1, 2x2, 2x3, 2x4 */
{2, 4, 4, 0}, /* 3x1, 3x2, 3x3, 3x4 */
{3, 5, 6, 0} /* 4x1, 4x2, 4x3, 4x4 */
};
bool ebf;
if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he))
return ;
ebf = mt7915_is_ebf_supported(phy, vif, sta, false );
if (!ebf && !dev->ibf)
return ;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BF, sizeof (*bf));
bf = (struct sta_rec_bf *)tlv;
/* he: eBF only, in accordance with spec
* vht: support eBF and iBF
* ht: iBF only, since mac80211 lacks of eBF support
*/
if (sta->deflink.he_cap.has_he && ebf)
mt7915_mcu_sta_bfer_he(sta, vif, phy, bf);
else if (sta->deflink.vht_cap.vht_supported)
mt7915_mcu_sta_bfer_vht(sta, phy, bf, ebf);
else if (sta->deflink.ht_cap.ht_supported)
mt7915_mcu_sta_bfer_ht(sta, phy, bf);
else
return ;
bf->bf_cap = ebf ? ebf : dev->ibf << 1;
bf->bw = sta->deflink.bandwidth;
bf->ibf_dbw = sta->deflink.bandwidth;
bf->ibf_nrow = tx_ant;
if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol)
bf->ibf_timeout = 0x48;
else
bf->ibf_timeout = 0x18;
if (ebf && bf->nrow != tx_ant)
bf->mem_20m = matrix[tx_ant][bf->ncol];
else
bf->mem_20m = matrix[bf->nrow][bf->ncol];
switch (sta->deflink.bandwidth) {
case IEEE80211_STA_RX_BW_160:
case IEEE80211_STA_RX_BW_80:
bf->mem_total = bf->mem_20m * 2;
break ;
case IEEE80211_STA_RX_BW_40:
bf->mem_total = bf->mem_20m;
break ;
case IEEE80211_STA_RX_BW_20:
default :
break ;
}
}
static void
mt7915_mcu_sta_bfee_tlv(struct mt7915_dev *dev, struct sk_buff *skb,
struct ieee80211_vif *vif, struct ieee80211_sta *sta)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt7915_phy *phy = mvif->phy;
int tx_ant = hweight8(phy->mt76->chainmask) - 1;
struct sta_rec_bfee *bfee;
struct tlv *tlv;
u8 nrow = 0;
if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he))
return ;
if (!mt7915_is_ebf_supported(phy, vif, sta, true ))
return ;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof (*bfee));
bfee = (struct sta_rec_bfee *)tlv;
if (sta->deflink.he_cap.has_he) {
struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem;
nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK,
pe->phy_cap_info[5]);
} else if (sta->deflink.vht_cap.vht_supported) {
struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap;
nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK,
pc->cap);
}
/* reply with identity matrix to avoid 2x2 BF negative gain */
bfee->fb_identity_matrix = (nrow == 1 && tx_ant == 2);
}
static enum mcu_mmps_mode
mt7915_mcu_get_mmps_mode(enum ieee80211_smps_mode smps)
{
switch (smps) {
case IEEE80211_SMPS_OFF:
return MCU_MMPS_DISABLE;
case IEEE80211_SMPS_STATIC:
return MCU_MMPS_STATIC;
case IEEE80211_SMPS_DYNAMIC:
return MCU_MMPS_DYNAMIC;
default :
return MCU_MMPS_DISABLE;
}
}
int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
void *data, u32 field)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
struct sta_phy *phy = data;
struct sta_rec_ra_fixed *ra;
struct sk_buff *skb;
struct tlv *tlv;
skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
&msta->wcid);
if (IS_ERR(skb))
return PTR_ERR(skb);
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA_UPDATE, sizeof (*ra));
ra = (struct sta_rec_ra_fixed *)tlv;
switch (field) {
case RATE_PARAM_AUTO:
break ;
case RATE_PARAM_FIXED:
case RATE_PARAM_FIXED_MCS:
case RATE_PARAM_FIXED_GI:
case RATE_PARAM_FIXED_HE_LTF:
if (phy)
ra->phy = *phy;
break ;
case RATE_PARAM_MMPS_UPDATE:
ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->deflink.smps_mode);
break ;
case RATE_PARAM_SPE_UPDATE:
ra->spe_idx = *(u8 *)data;
break ;
default :
break ;
}
ra->field = cpu_to_le32(field);
return mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_EXT_CMD(STA_REC_UPDATE), true );
}
int mt7915_mcu_add_smps(struct mt7915_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
struct wtbl_req_hdr *wtbl_hdr;
struct tlv *sta_wtbl;
struct sk_buff *skb;
int ret;
skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
&msta->wcid);
if (IS_ERR(skb))
return PTR_ERR(skb);
sta_wtbl = mt76_connac_mcu_add_tlv(skb, STA_REC_WTBL,
sizeof (struct tlv));
wtbl_hdr = mt76_connac_mcu_alloc_wtbl_req(&dev->mt76, &msta->wcid,
WTBL_SET, sta_wtbl, &skb);
if (IS_ERR(wtbl_hdr))
return PTR_ERR(wtbl_hdr);
mt76_connac_mcu_wtbl_smps_tlv(skb, sta, sta_wtbl, wtbl_hdr);
ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_EXT_CMD(STA_REC_UPDATE), true );
if (ret)
return ret;
return mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, NULL,
RATE_PARAM_MMPS_UPDATE);
}
static int
mt7915_mcu_set_spe_idx(struct mt7915_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt76_phy *mphy = mvif->phy->mt76;
u8 spe_idx = mt76_connac_spe_idx(mphy->antenna_mask);
return mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &spe_idx,
RATE_PARAM_SPE_UPDATE);
}
static int
mt7915_mcu_add_rate_ctrl_fixed(struct mt7915_dev *dev,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef;
struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask;
enum nl80211_band band = chandef->chan->band;
struct sta_phy phy = {};
int ret, nrates = 0;
#define __sta_phy_bitrate_mask_check(_mcs, _gi, _ht, _he) \
do { \
u8 i, gi = mask->control[band]._gi; \
gi = (_he) ? gi : gi == NL80211_TXRATE_FORCE_SGI; \
for (i = 0; i <= sta->deflink.bandwidth; i++) { \
phy.sgi |= gi << (i << (_he)); \
phy.he_ltf |= mask->control[band].he_ltf << (i << (_he));\
} \
for (i = 0; i < ARRAY_SIZE(mask->control[band]._mcs); i++) { \
if (!mask->control[band]._mcs[i]) \
continue ; \
nrates += hweight16(mask->control[band]._mcs[i]); \
phy.mcs = ffs(mask->control[band]._mcs[i]) - 1; \
if (_ht) \
phy.mcs += 8 * i; \
} \
} while (0)
if (sta->deflink.he_cap.has_he) {
__sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1);
} else if (sta->deflink.vht_cap.vht_supported) {
__sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0);
} else if (sta->deflink.ht_cap.ht_supported) {
__sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0);
} else {
nrates = hweight32(mask->control[band].legacy);
phy.mcs = ffs(mask->control[band].legacy) - 1;
}
#undef __sta_phy_bitrate_mask_check
/* fall back to auto rate control */
if (mask->control[band].gi == NL80211_TXRATE_DEFAULT_GI &&
mask->control[band].he_gi == GENMASK(7, 0) &&
mask->control[band].he_ltf == GENMASK(7, 0) &&
nrates != 1)
return 0;
/* fixed single rate */
if (nrates == 1) {
ret = mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &phy,
RATE_PARAM_FIXED_MCS);
if (ret)
return ret;
}
/* fixed GI */
if (mask->control[band].gi != NL80211_TXRATE_DEFAULT_GI ||
mask->control[band].he_gi != GENMASK(7, 0)) {
struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
u32 addr;
/* firmware updates only TXCMD but doesn't take WTBL into
* account, so driver should update here to reflect the
* actual txrate hardware sends out.
*/
addr = mt7915_mac_wtbl_lmac_addr(dev, msta->wcid.idx, 7);
if (sta->deflink.he_cap.has_he)
mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi);
else
mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi);
ret = mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &phy,
RATE_PARAM_FIXED_GI);
if (ret)
return ret;
}
/* fixed HE_LTF */
if (mask->control[band].he_ltf != GENMASK(7, 0)) {
ret = mt7915_mcu_set_fixed_rate_ctrl(dev, vif, sta, &phy,
RATE_PARAM_FIXED_HE_LTF);
if (ret)
return ret;
}
return mt7915_mcu_set_spe_idx(dev, vif, sta);
}
static void
mt7915_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7915_dev *dev,
struct ieee80211_vif *vif, struct ieee80211_sta *sta)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt76_phy *mphy = mvif->phy->mt76;
struct cfg80211_chan_def *chandef = &mphy->chandef;
struct cfg80211_bitrate_mask *mask = &mvif->bitrate_mask;
enum nl80211_band band = chandef->chan->band;
struct sta_rec_ra *ra;
struct tlv *tlv;
u32 supp_rate = sta->deflink.supp_rates[band];
u32 cap = sta->wme ? STA_CAP_WMM : 0;
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof (*ra));
ra = (struct sta_rec_ra *)tlv;
ra->valid = true ;
ra->auto_rate = true ;
ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, &sta->deflink);
ra->channel = chandef->chan->hw_value;
ra->bw = sta->deflink.bandwidth;
ra->phy.bw = sta->deflink.bandwidth;
ra->mmps_mode = mt7915_mcu_get_mmps_mode(sta->deflink.smps_mode);
if (supp_rate) {
supp_rate &= mask->control[band].legacy;
ra->rate_len = hweight32(supp_rate);
if (band == NL80211_BAND_2GHZ) {
ra->supp_mode = MODE_CCK;
ra->supp_cck_rate = supp_rate & GENMASK(3, 0);
if (ra->rate_len > 4) {
ra->supp_mode |= MODE_OFDM;
ra->supp_ofdm_rate = supp_rate >> 4;
}
} else {
ra->supp_mode = MODE_OFDM;
ra->supp_ofdm_rate = supp_rate;
}
}
if (sta->deflink.ht_cap.ht_supported) {
ra->supp_mode |= MODE_HT;
ra->af = sta->deflink.ht_cap.ampdu_factor;
ra->ht_gf = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD);
cap |= STA_CAP_HT;
if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20)
cap |= STA_CAP_SGI_20;
if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40)
cap |= STA_CAP_SGI_40;
if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC)
cap |= STA_CAP_TX_STBC;
if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC)
cap |= STA_CAP_RX_STBC;
if (mvif->cap.ht_ldpc &&
(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING))
cap |= STA_CAP_LDPC;
mt7915_mcu_set_sta_ht_mcs(sta, ra->ht_mcs,
mask->control[band].ht_mcs);
ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs;
}
if (sta->deflink.vht_cap.vht_supported) {
u8 af;
ra->supp_mode |= MODE_VHT;
af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK,
sta->deflink.vht_cap.cap);
ra->af = max_t(u8, ra->af, af);
cap |= STA_CAP_VHT;
if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80)
cap |= STA_CAP_VHT_SGI_80;
if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160)
cap |= STA_CAP_VHT_SGI_160;
if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC)
cap |= STA_CAP_VHT_TX_STBC;
if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1)
cap |= STA_CAP_VHT_RX_STBC;
if (mvif->cap.vht_ldpc &&
(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC))
cap |= STA_CAP_VHT_LDPC;
mt7915_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs,
mask->control[band].vht_mcs);
}
if (sta->deflink.he_cap.has_he) {
ra->supp_mode |= MODE_HE;
cap |= STA_CAP_HE;
if (sta->deflink.he_6ghz_capa.capa)
ra->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa,
IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP);
}
ra->sta_cap = cpu_to_le32(cap);
}
int mt7915_mcu_add_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, bool changed)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt7915_sta *msta = (struct mt7915_sta *)sta->drv_priv;
struct sk_buff *skb;
int ret;
skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
&msta->wcid);
if (IS_ERR(skb))
return PTR_ERR(skb);
/* firmware rc algorithm refers to sta_rec_he for HE control.
* once dev->rc_work changes the settings driver should also
* update sta_rec_he here.
*/
if (changed)
mt7915_mcu_sta_he_tlv(skb, sta, vif);
/* sta_rec_ra accommodates BW, NSS and only MCS range format
* i.e 0-{7,8,9} for VHT.
*/
mt7915_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta);
ret = mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_EXT_CMD(STA_REC_UPDATE), true );
if (ret)
return ret;
/* sta_rec_ra_fixed accommodates single rate, (HE)GI and HE_LTE,
* and updates as peer fixed rate parameters, which overrides
* sta_rec_ra and firmware rate control algorithm.
*/
return mt7915_mcu_add_rate_ctrl_fixed(dev, vif, sta);
}
static int
mt7915_mcu_add_group(struct mt7915_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
#define MT_STA_BSS_GROUP 1
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct mt7915_sta *msta;
struct {
__le32 action;
u8 wlan_idx_lo;
u8 status;
u8 wlan_idx_hi;
u8 rsv0[5];
__le32 val;
u8 rsv1[8];
} __packed req = {
.action = cpu_to_le32(MT_STA_BSS_GROUP),
.val = cpu_to_le32(mvif->mt76.idx % 16),
};
msta = sta ? (struct mt7915_sta *)sta->drv_priv : &mvif->sta;
req.wlan_idx_lo = to_wcid_lo(msta->wcid.idx);
req.wlan_idx_hi = to_wcid_hi(msta->wcid.idx);
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(SET_DRR_CTRL), &req,
sizeof (req), true );
}
int mt7915_mcu_add_sta(struct mt7915_dev *dev, struct ieee80211_vif *vif,
struct ieee80211_sta *sta, int conn_state, bool newly)
{
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct ieee80211_link_sta *link_sta;
struct mt7915_sta *msta;
struct sk_buff *skb;
int ret;
msta = sta ? (struct mt7915_sta *)sta->drv_priv : &mvif->sta;
link_sta = sta ? &sta->deflink : NULL;
skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
&msta->wcid);
if (IS_ERR(skb))
return PTR_ERR(skb);
/* starec basic */
mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, &vif->bss_conf, link_sta,
conn_state, newly);
/* tag order is in accordance with firmware dependency. */
if (sta && conn_state != CONN_STATE_DISCONNECT) {
/* starec bfer */
mt7915_mcu_sta_bfer_tlv(dev, skb, vif, sta);
/* starec ht */
mt7915_mcu_sta_ht_tlv(skb, sta);
/* starec vht */
mt7915_mcu_sta_vht_tlv(skb, sta);
/* starec uapsd */
mt76_connac_mcu_sta_uapsd(skb, vif, sta);
}
if (newly || conn_state != CONN_STATE_DISCONNECT) {
ret = mt7915_mcu_sta_wtbl_tlv(dev, skb, vif, sta);
if (ret) {
dev_kfree_skb(skb);
return ret;
}
}
if (conn_state == CONN_STATE_DISCONNECT)
goto out;
if (sta) {
/* starec amsdu */
mt7915_mcu_sta_amsdu_tlv(dev, skb, vif, sta);
/* starec he */
mt7915_mcu_sta_he_tlv(skb, sta, vif);
/* starec muru */
mt7915_mcu_sta_muru_tlv(dev, skb, sta, vif);
/* starec bfee */
mt7915_mcu_sta_bfee_tlv(dev, skb, vif, sta);
}
ret = mt7915_mcu_add_group(dev, vif, sta);
if (ret) {
dev_kfree_skb(skb);
return ret;
}
out:
ret = mt76_connac_mcu_sta_wed_update(&dev->mt76, skb);
if (ret)
return ret;
return mt76_mcu_skb_send_msg(&dev->mt76, skb,
MCU_EXT_CMD(STA_REC_UPDATE), true );
}
int mt7915_mcu_wed_enable_rx_stats(struct mt7915_dev *dev)
{
#ifdef CONFIG_NET_MEDIATEK_SOC_WED
struct mtk_wed_device *wed = &dev->mt76.mmio.wed;
struct {
__le32 args[2];
} req = {
.args[0] = cpu_to_le32(1),
.args[1] = cpu_to_le32(6),
};
return mtk_wed_device_update_msg(wed, MTK_WED_WO_CMD_RXCNT_CTRL,
&req, sizeof (req));
#else
return 0;
#endif
}
int mt7915_mcu_add_dev_info(struct mt7915_phy *phy,
struct ieee80211_vif *vif, bool enable)
{
struct mt7915_dev *dev = phy->dev;
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct {
struct req_hdr {
u8 omac_idx;
u8 band_idx;
__le16 tlv_num;
u8 is_tlv_append;
u8 rsv[3];
} __packed hdr;
struct req_tlv {
__le16 tag;
__le16 len;
u8 active;
u8 band_idx;
u8 omac_addr[ETH_ALEN];
} __packed tlv;
} data = {
.hdr = {
.omac_idx = mvif->mt76.omac_idx,
.band_idx = mvif->mt76.band_idx,
.tlv_num = cpu_to_le16(1),
.is_tlv_append = 1,
},
.tlv = {
.tag = cpu_to_le16(DEV_INFO_ACTIVE),
.len = cpu_to_le16(sizeof (struct req_tlv)),
.active = enable,
.band_idx = mvif->mt76.band_idx,
},
};
if (mvif->mt76.omac_idx >= REPEATER_BSSID_START)
return mt7915_mcu_muar_config(phy, vif, false , enable);
memcpy(data.tlv.omac_addr, vif->addr, ETH_ALEN);
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(DEV_INFO_UPDATE),
&data, sizeof (data), true );
}
static void
mt7915_mcu_beacon_cntdwn(struct ieee80211_vif *vif, struct sk_buff *rskb,
struct sk_buff *skb, struct bss_info_bcn *bcn,
struct ieee80211_mutable_offsets *offs)
{
struct bss_info_bcn_cntdwn *info;
struct tlv *tlv;
int sub_tag;
if (!offs->cntdwn_counter_offs[0])
return ;
sub_tag = vif->bss_conf.csa_active ? BSS_INFO_BCN_CSA : BSS_INFO_BCN_BCC;
tlv = mt7915_mcu_add_nested_subtlv(rskb, sub_tag, sizeof (*info),
&bcn->sub_ntlv, &bcn->len);
info = (struct bss_info_bcn_cntdwn *)tlv;
info->cnt = skb->data[offs->cntdwn_counter_offs[0]];
}
static void
mt7915_mcu_beacon_mbss(struct sk_buff *rskb, struct sk_buff *skb,
struct ieee80211_vif *vif, struct bss_info_bcn *bcn,
struct ieee80211_mutable_offsets *offs)
{
struct bss_info_bcn_mbss *mbss;
const struct element *elem;
struct tlv *tlv;
if (!vif->bss_conf.bssid_indicator)
return ;
tlv = mt7915_mcu_add_nested_subtlv(rskb, BSS_INFO_BCN_MBSSID,
sizeof (*mbss), &bcn->sub_ntlv,
&bcn->len);
mbss = (struct bss_info_bcn_mbss *)tlv;
mbss->offset[0] = cpu_to_le16(offs->tim_offset);
mbss->bitmap = cpu_to_le32(1);
for_each_element_id(elem, WLAN_EID_MULTIPLE_BSSID,
&skb->data[offs->mbssid_off],
skb->len - offs->mbssid_off) {
const struct element *sub_elem;
if (elem->datalen < 2)
continue ;
for_each_element(sub_elem, elem->data + 1, elem->datalen - 1) {
const struct ieee80211_bssid_index *idx;
const u8 *idx_ie;
if (sub_elem->id || sub_elem->datalen < 4)
continue ; /* not a valid BSS profile */
/* Find WLAN_EID_MULTI_BSSID_IDX
* in the merged nontransmitted profile
*/
idx_ie = cfg80211_find_ie(WLAN_EID_MULTI_BSSID_IDX,
sub_elem->data,
sub_elem->datalen);
if (!idx_ie || idx_ie[1] < sizeof (*idx))
continue ;
idx = (void *)(idx_ie + 2);
if (!idx->bssid_index || idx->bssid_index > 31)
continue ;
mbss->offset[idx->bssid_index] =
cpu_to_le16(idx_ie - skb->data);
mbss->bitmap |= cpu_to_le32(BIT(idx->bssid_index));
}
}
}
static void
mt7915_mcu_beacon_cont(struct mt7915_dev *dev, struct ieee80211_vif *vif,
struct sk_buff *rskb, struct sk_buff *skb,
struct bss_info_bcn *bcn,
struct ieee80211_mutable_offsets *offs)
{
struct mt76_wcid *wcid = &dev->mt76.global_wcid;
struct bss_info_bcn_cont *cont;
struct tlv *tlv;
u8 *buf;
int len = sizeof (*cont) + MT_TXD_SIZE + skb->len;
len = (len & 0x3) ? ((len | 0x3) + 1) : len;
tlv = mt7915_mcu_add_nested_subtlv(rskb, BSS_INFO_BCN_CONTENT,
len, &bcn->sub_ntlv, &bcn->len);
cont = (struct bss_info_bcn_cont *)tlv;
cont->pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len);
cont->tim_ofs = cpu_to_le16(offs->tim_offset);
if (offs->cntdwn_counter_offs[0]) {
u16 offset = offs->cntdwn_counter_offs[0];
if (vif->bss_conf.csa_active)
cont->csa_ofs = cpu_to_le16(offset - 4);
if (vif->bss_conf.color_change_active)
cont->bcc_ofs = cpu_to_le16(offset - 3);
}
buf = (u8 *)tlv + sizeof (*cont);
mt7915_mac_write_txwi(&dev->mt76, (__le32 *)buf, skb, wcid, 0, NULL,
0, BSS_CHANGED_BEACON);
memcpy(buf + MT_TXD_SIZE, skb->data, skb->len);
}
int
mt7915_mcu_add_inband_discov(struct mt7915_dev *dev, struct ieee80211_vif *vif,
u32 changed)
{
#define OFFLOAD_TX_MODE_SU BIT(0)
#define OFFLOAD_TX_MODE_MU BIT(1)
struct ieee80211_hw *hw = mt76_hw(dev);
struct mt7915_phy *phy = mt7915_hw_phy(hw);
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct cfg80211_chan_def *chandef = &mvif->phy->mt76->chandef;
enum nl80211_band band = chandef->chan->band;
struct mt76_wcid *wcid = &dev->mt76.global_wcid;
struct bss_info_bcn *bcn;
struct bss_info_inband_discovery *discov;
struct ieee80211_tx_info *info;
struct sk_buff *rskb, *skb = NULL;
struct tlv *tlv, *sub_tlv;
bool ext_phy = phy != &dev->phy;
u8 *buf, interval;
int len;
if (vif->bss_conf.nontransmitted)
return 0;
rskb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76, NULL,
MT7915_MAX_BSS_OFFLOAD_SIZE);
if (IS_ERR(rskb))
return PTR_ERR(rskb);
tlv = mt76_connac_mcu_add_tlv(rskb, BSS_INFO_OFFLOAD, sizeof (*bcn));
bcn = (struct bss_info_bcn *)tlv;
bcn->enable = true ;
if (changed & BSS_CHANGED_FILS_DISCOVERY) {
interval = vif->bss_conf.fils_discovery.max_interval;
skb = ieee80211_get_fils_discovery_tmpl(hw, vif);
} else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP &&
vif->bss_conf.unsol_bcast_probe_resp_interval) {
interval = vif->bss_conf.unsol_bcast_probe_resp_interval;
skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif);
}
if (!skb) {
dev_kfree_skb(rskb);
return -EINVAL;
}
info = IEEE80211_SKB_CB(skb);
info->control.vif = vif;
info->band = band;
info->hw_queue |= FIELD_PREP(MT_TX_HW_QUEUE_PHY, ext_phy);
len = sizeof (*discov) + MT_TXD_SIZE + skb->len;
len = (len & 0x3) ? ((len | 0x3) + 1) : len;
if (skb->len > MT7915_MAX_BEACON_SIZE) {
dev_err(dev->mt76.dev, "inband discovery size limit exceed\n" );
dev_kfree_skb(rskb);
dev_kfree_skb(skb);
return -EINVAL;
}
sub_tlv = mt7915_mcu_add_nested_subtlv(rskb, BSS_INFO_BCN_DISCOV,
len, &bcn->sub_ntlv, &bcn->len);
discov = (struct bss_info_inband_discovery *)sub_tlv;
discov->tx_mode = OFFLOAD_TX_MODE_SU;
/* 0: UNSOL PROBE RESP, 1: FILS DISCOV */
discov->tx_type = !!(changed & BSS_CHANGED_FILS_DISCOVERY);
discov->tx_interval = interval;
discov->prob_rsp_len = cpu_to_le16(MT_TXD_SIZE + skb->len);
discov->enable = !!interval;
buf = (u8 *)sub_tlv + sizeof (*discov);
mt7915_mac_write_txwi(&dev->mt76, (__le32 *)buf, skb, wcid, 0, NULL,
0, changed);
memcpy(buf + MT_TXD_SIZE, skb->data, skb->len);
dev_kfree_skb(skb);
return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
MCU_EXT_CMD(BSS_INFO_UPDATE), true );
}
int mt7915_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
int en, u32 changed)
{
struct mt7915_dev *dev = mt7915_hw_dev(hw);
struct mt7915_phy *phy = mt7915_hw_phy(hw);
struct mt7915_vif *mvif = (struct mt7915_vif *)vif->drv_priv;
struct ieee80211_mutable_offsets offs;
struct ieee80211_tx_info *info;
struct sk_buff *skb, *rskb;
struct tlv *tlv;
struct bss_info_bcn *bcn;
int len = MT7915_MAX_BSS_OFFLOAD_SIZE;
bool ext_phy = phy != &dev->phy;
if (vif->bss_conf.nontransmitted)
return 0;
rskb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->mt76,
NULL, len);
if (IS_ERR(rskb))
return PTR_ERR(rskb);
tlv = mt76_connac_mcu_add_tlv(rskb, BSS_INFO_OFFLOAD, sizeof (*bcn));
bcn = (struct bss_info_bcn *)tlv;
bcn->enable = en;
if (!en)
goto out;
skb = ieee80211_beacon_get_template(hw, vif, &offs, 0);
if (!skb) {
dev_kfree_skb(rskb);
return -EINVAL;
}
if (skb->len > MT7915_MAX_BEACON_SIZE) {
dev_err(dev->mt76.dev, "Bcn size limit exceed\n" );
dev_kfree_skb(rskb);
dev_kfree_skb(skb);
return -EINVAL;
}
info = IEEE80211_SKB_CB(skb);
info->hw_queue = FIELD_PREP(MT_TX_HW_QUEUE_PHY, ext_phy);
mt7915_mcu_beacon_cntdwn(vif, rskb, skb, bcn, &offs);
mt7915_mcu_beacon_mbss(rskb, skb, vif, bcn, &offs);
mt7915_mcu_beacon_cont(dev, vif, rskb, skb, bcn, &offs);
dev_kfree_skb(skb);
out:
return mt76_mcu_skb_send_msg(&phy->dev->mt76, rskb,
MCU_EXT_CMD(BSS_INFO_UPDATE), true );
}
static int mt7915_driver_own(struct mt7915_dev *dev, u8 band)
{
mt76_wr(dev, MT_TOP_LPCR_HOST_BAND(band), MT_TOP_LPCR_HOST_DRV_OWN);
if (!mt76_poll_msec(dev, MT_TOP_LPCR_HOST_BAND(band),
MT_TOP_LPCR_HOST_FW_OWN_STAT, 0, 500)) {
dev_err(dev->mt76.dev, "Timeout for driver own\n" );
return -EIO;
}
/* clear irq when the driver own success */
mt76_wr(dev, MT_TOP_LPCR_HOST_BAND_IRQ_STAT(band),
MT_TOP_LPCR_HOST_BAND_STAT);
return 0;
}
static int
mt7915_firmware_state(struct mt7915_dev *dev, bool wa)
{
u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE,
wa ? FW_STATE_RDY : FW_STATE_FW_DOWNLOAD);
if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE,
state, 1000)) {
dev_err(dev->mt76.dev, "Timeout for initializing firmware\n" );
return -EIO;
}
return 0;
}
static int mt7915_load_firmware(struct mt7915_dev *dev)
{
int ret;
/* Release Semaphore if taken by previous failed attempt */
ret = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, false );
if (ret != PATCH_REL_SEM_SUCCESS) {
dev_err(dev->mt76.dev, "Could not release semaphore\n" );
/* Continue anyways */
}
/* Always restart MCU firmware */
mt76_connac_mcu_restart(&dev->mt76);
/* Check if MCU is ready */
ret = mt7915_firmware_state(dev, false );
if (ret) {
dev_err(dev->mt76.dev, "Firmware did not enter download state\n" );
return ret;
}
ret = mt76_connac2_load_patch(&dev->mt76, fw_name_var(dev, ROM_PATCH));
if (ret)
return ret;
ret = mt76_connac2_load_ram(&dev->mt76, fw_name_var(dev, FIRMWARE_WM),
fw_name(dev, FIRMWARE_WA));
if (ret)
return ret;
ret = mt7915_firmware_state(dev, true );
if (ret)
return ret;
mt76_queue_tx_cleanup(dev, dev->mt76.q_mcu[MT_MCUQ_FWDL], false );
dev_dbg(dev->mt76.dev, "Firmware init done\n" );
return 0;
}
int mt7915_mcu_fw_log_2_host(struct mt7915_dev *dev, u8 type, u8 ctrl)
{
struct {
u8 ctrl_val;
u8 pad[3];
} data = {
.ctrl_val = ctrl
};
if (type == MCU_FW_LOG_WA)
return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(FW_LOG_2_HOST),
&data, sizeof (data), true );
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(FW_LOG_2_HOST), &data,
sizeof (data), true );
}
int mt7915_mcu_fw_dbg_ctrl(struct mt7915_dev *dev, u32 module, u8 level)
{
struct {
u8 ver;
u8 pad;
__le16 len;
u8 level;
u8 rsv[3];
__le32 module_idx;
} data = {
.module_idx = cpu_to_le32(module),
.level = level,
};
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(FW_DBG_CTRL), &data,
sizeof (data), false );
}
int mt7915_mcu_muru_debug_set(struct mt7915_dev *dev, bool enabled)
{
struct {
__le32 cmd;
u8 enable;
} data = {
.cmd = cpu_to_le32(MURU_SET_TXC_TX_STATS_EN),
.enable = enabled,
};
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &data,
sizeof (data), false );
}
int mt7915_mcu_muru_debug_get(struct mt7915_phy *phy)
{
struct mt7915_dev *dev = phy->dev;
struct sk_buff *skb;
struct mt7915_mcu_muru_stats *mu_stats;
int ret;
struct {
__le32 cmd;
u8 band_idx;
} req = {
.cmd = cpu_to_le32(MURU_GET_TXC_TX_STATS),
.band_idx = phy->mt76->band_idx,
};
ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL),
&req, sizeof (req), true , &skb);
if (ret)
return ret;
mu_stats = (struct mt7915_mcu_muru_stats *)(skb->data);
/* accumulate stats, these are clear-on-read */
#define __dl_u32(s) phy->mib.dl_## s += le32_to_cpu(mu_stats->dl.s)
#define __ul_u32(s) phy->mib.ul_## s += le32_to_cpu(mu_stats->ul.s)
__dl_u32(cck_cnt);
__dl_u32(ofdm_cnt);
__dl_u32(htmix_cnt);
__dl_u32(htgf_cnt);
__dl_u32(vht_su_cnt);
__dl_u32(vht_2mu_cnt);
__dl_u32(vht_3mu_cnt);
__dl_u32(vht_4mu_cnt);
__dl_u32(he_su_cnt);
__dl_u32(he_2ru_cnt);
__dl_u32(he_2mu_cnt);
__dl_u32(he_3ru_cnt);
__dl_u32(he_3mu_cnt);
__dl_u32(he_4ru_cnt);
__dl_u32(he_4mu_cnt);
__dl_u32(he_5to8ru_cnt);
__dl_u32(he_9to16ru_cnt);
__dl_u32(he_gtr16ru_cnt);
__ul_u32(hetrig_su_cnt);
__ul_u32(hetrig_2ru_cnt);
__ul_u32(hetrig_3ru_cnt);
__ul_u32(hetrig_4ru_cnt);
__ul_u32(hetrig_5to8ru_cnt);
__ul_u32(hetrig_9to16ru_cnt);
__ul_u32(hetrig_gtr16ru_cnt);
__ul_u32(hetrig_2mu_cnt);
__ul_u32(hetrig_3mu_cnt);
__ul_u32(hetrig_4mu_cnt);
#undef __dl_u32
#undef __ul_u32
dev_kfree_skb(skb);
return 0;
}
static int mt7915_mcu_set_mwds(struct mt7915_dev *dev, bool enabled)
{
struct {
u8 enable;
u8 _rsv[3];
} __packed req = {
.enable = enabled
};
return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(MWDS_SUPPORT), &req,
sizeof (req), false );
}
int mt7915_mcu_set_muru_ctrl(struct mt7915_dev *dev, u32 cmd, u32 val)
{
struct {
__le32 cmd;
u8 val[4];
} __packed req = {
.cmd = cpu_to_le32(cmd),
};
put_unaligned_le32(val, req.val);
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(MURU_CTRL), &req,
sizeof (req), false );
}
static int
mt7915_mcu_init_rx_airtime(struct mt7915_dev *dev)
{
#define RX_AIRTIME_FEATURE_CTRL 1
#define RX_AIRTIME_BITWISE_CTRL 2
#define RX_AIRTIME_CLEAR_EN 1
struct {
__le16 field;
__le16 sub_field;
__le32 set_status;
__le32 get_status;
u8 _rsv[12];
bool airtime_en;
bool mibtime_en;
bool earlyend_en;
u8 _rsv1[9];
bool airtime_clear;
bool mibtime_clear;
u8 _rsv2[98];
} __packed req = {
.field = cpu_to_le16(RX_AIRTIME_BITWISE_CTRL),
.sub_field = cpu_to_le16(RX_AIRTIME_CLEAR_EN),
.airtime_clear = true ,
};
int ret;
ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RX_AIRTIME_CTRL), &req,
sizeof (req), true );
if (ret)
return ret;
req.field = cpu_to_le16(RX_AIRTIME_FEATURE_CTRL);
req.sub_field = cpu_to_le16(RX_AIRTIME_CLEAR_EN);
req.airtime_en = true ;
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RX_AIRTIME_CTRL), &req,
sizeof (req), true );
}
static int mt7915_red_set_watermark(struct mt7915_dev *dev)
{
#define RED_GLOBAL_TOKEN_WATERMARK 2
struct {
__le32 args[3];
u8 cmd;
u8 version;
u8 __rsv1[4];
__le16 len;
__le16 high_mark;
__le16 low_mark;
u8 __rsv2[12];
} __packed req = {
.args[0] = cpu_to_le32(MCU_WA_PARAM_RED_SETTING),
.cmd = RED_GLOBAL_TOKEN_WATERMARK,
.len = cpu_to_le16(sizeof (req) - sizeof (req.args)),
.high_mark = cpu_to_le16(MT7915_HW_TOKEN_SIZE - 256),
.low_mark = cpu_to_le16(MT7915_HW_TOKEN_SIZE - 256 - 1536),
};
return mt76_mcu_send_msg(&dev->mt76, MCU_WA_PARAM_CMD(SET), &req,
sizeof (req), false );
}
static int mt7915_mcu_set_red(struct mt7915_dev *dev, bool enabled)
{
#define RED_DISABLE 0
#define RED_BY_WA_ENABLE 2
int ret;
u32 red_type = enabled ? RED_BY_WA_ENABLE : RED_DISABLE;
__le32 req = cpu_to_le32(red_type);
if (enabled) {
ret = mt7915_red_set_watermark(dev);
if (ret < 0)
return ret;
}
ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(RED_ENABLE), &req,
sizeof (req), false );
if (ret < 0)
return ret;
return mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(SET),
MCU_WA_PARAM_RED, enabled, 0);
}
int mt7915_mcu_init_firmware(struct mt7915_dev *dev)
{
int ret;
/* force firmware operation mode into normal state,
* which should be set before firmware download stage.
*/
mt76_wr(dev, MT_SWDEF_MODE, MT_SWDEF_NORMAL_MODE);
ret = mt7915_driver_own(dev, 0);
if (ret)
return ret;
/* set driver own for band1 when two hif exist */
if (dev->hif2) {
ret = mt7915_driver_own(dev, 1);
if (ret)
return ret;
}
ret = mt7915_load_firmware(dev);
if (ret)
return ret;
set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
ret = mt7915_mcu_fw_log_2_host(dev, MCU_FW_LOG_WM, 0);
if (ret)
return ret;
ret = mt7915_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 0);
if (ret)
return ret;
mt76_connac_mcu_del_wtbl_all(&dev->mt76);
if ((mtk_wed_device_active(&dev->mt76.mmio.wed) &&
is_mt7915(&dev->mt76)) ||
!mtk_wed_get_rx_capa(&dev->mt76.mmio.wed))
mt7915_mcu_wa_cmd(dev, MCU_WA_PARAM_CMD(CAPABILITY), 0, 0, 0);
ret = mt7915_mcu_set_mwds(dev, 1);
if (ret)
return ret;
ret = mt7915_mcu_set_muru_ctrl(dev, MURU_SET_PLATFORM_TYPE,
MURU_PLATFORM_TYPE_PERF_LEVEL_2);
if (ret)
return ret;
ret = mt7915_mcu_init_rx_airtime(dev);
if (ret)
return ret;
return mt7915_mcu_set_red(dev, mtk_wed_device_active(&dev->mt76.mmio.wed));
}
int mt7915_mcu_init(struct mt7915_dev *dev)
{
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=98 H=96 G=96
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland