Fix the false positive suspend error that may occur on mt7921s with enlarging mcu timeout value. The reason why we have to enlarge mcu timeout from HZ / 3 to HZ is we should consider the additional overhead caused by running concurrently with btmtksdio (a MT7921 bluetooth SDIO driver) that would compete for the same SDIO bus in process context to complete the suspend procedure. Fixes: 48fab5bbef40 ("mt76: mt7921: introduce mt7921s support") Signed-off-by: Sean Wang <sean.wang@mediatek.com> Signed-off-by: Felix Fietkau <nbd@nbd.name>
1354 lines
31 KiB
C
1354 lines
31 KiB
C
// SPDX-License-Identifier: ISC
|
|
/* Copyright (C) 2020 MediaTek Inc. */
|
|
|
|
#include <linux/firmware.h>
|
|
#include <linux/fs.h>
|
|
#include "mt7921.h"
|
|
#include "mt7921_trace.h"
|
|
#include "mcu.h"
|
|
#include "mac.h"
|
|
|
|
struct mt7921_patch_hdr {
|
|
char build_date[16];
|
|
char platform[4];
|
|
__be32 hw_sw_ver;
|
|
__be32 patch_ver;
|
|
__be16 checksum;
|
|
u16 reserved;
|
|
struct {
|
|
__be32 patch_ver;
|
|
__be32 subsys;
|
|
__be32 feature;
|
|
__be32 n_region;
|
|
__be32 crc;
|
|
u32 reserved[11];
|
|
} desc;
|
|
} __packed;
|
|
|
|
struct mt7921_patch_sec {
|
|
__be32 type;
|
|
__be32 offs;
|
|
__be32 size;
|
|
union {
|
|
__be32 spec[13];
|
|
struct {
|
|
__be32 addr;
|
|
__be32 len;
|
|
__be32 sec_key_idx;
|
|
__be32 align_len;
|
|
u32 reserved[9];
|
|
} info;
|
|
};
|
|
} __packed;
|
|
|
|
struct mt7921_fw_trailer {
|
|
u8 chip_id;
|
|
u8 eco_code;
|
|
u8 n_region;
|
|
u8 format_ver;
|
|
u8 format_flag;
|
|
u8 reserved[2];
|
|
char fw_ver[10];
|
|
char build_date[15];
|
|
u32 crc;
|
|
} __packed;
|
|
|
|
struct mt7921_fw_region {
|
|
__le32 decomp_crc;
|
|
__le32 decomp_len;
|
|
__le32 decomp_blk_sz;
|
|
u8 reserved[4];
|
|
__le32 addr;
|
|
__le32 len;
|
|
u8 feature_set;
|
|
u8 reserved1[15];
|
|
} __packed;
|
|
|
|
#define MT_STA_BFER BIT(0)
|
|
#define MT_STA_BFEE BIT(1)
|
|
|
|
#define FW_FEATURE_SET_ENCRYPT BIT(0)
|
|
#define FW_FEATURE_SET_KEY_IDX GENMASK(2, 1)
|
|
#define FW_FEATURE_ENCRY_MODE BIT(4)
|
|
#define FW_FEATURE_OVERRIDE_ADDR BIT(5)
|
|
|
|
#define DL_MODE_ENCRYPT BIT(0)
|
|
#define DL_MODE_KEY_IDX GENMASK(2, 1)
|
|
#define DL_MODE_RESET_SEC_IV BIT(3)
|
|
#define DL_MODE_WORKING_PDA_CR4 BIT(4)
|
|
#define DL_CONFIG_ENCRY_MODE_SEL BIT(6)
|
|
#define DL_MODE_NEED_RSP BIT(31)
|
|
|
|
#define FW_START_OVERRIDE BIT(0)
|
|
#define FW_START_WORKING_PDA_CR4 BIT(2)
|
|
|
|
#define PATCH_SEC_NOT_SUPPORT GENMASK(31, 0)
|
|
#define PATCH_SEC_TYPE_MASK GENMASK(15, 0)
|
|
#define PATCH_SEC_TYPE_INFO 0x2
|
|
|
|
#define PATCH_SEC_ENC_TYPE_MASK GENMASK(31, 24)
|
|
#define PATCH_SEC_ENC_TYPE_PLAIN 0x00
|
|
#define PATCH_SEC_ENC_TYPE_AES 0x01
|
|
#define PATCH_SEC_ENC_TYPE_SCRAMBLE 0x02
|
|
#define PATCH_SEC_ENC_SCRAMBLE_INFO_MASK GENMASK(15, 0)
|
|
#define PATCH_SEC_ENC_AES_KEY_MASK GENMASK(7, 0)
|
|
|
|
static enum mcu_cipher_type
|
|
mt7921_mcu_get_cipher(int cipher)
|
|
{
|
|
switch (cipher) {
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
return MCU_CIPHER_WEP40;
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
return MCU_CIPHER_WEP104;
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
return MCU_CIPHER_TKIP;
|
|
case WLAN_CIPHER_SUITE_AES_CMAC:
|
|
return MCU_CIPHER_BIP_CMAC_128;
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
return MCU_CIPHER_AES_CCMP;
|
|
case WLAN_CIPHER_SUITE_CCMP_256:
|
|
return MCU_CIPHER_CCMP_256;
|
|
case WLAN_CIPHER_SUITE_GCMP:
|
|
return MCU_CIPHER_GCMP;
|
|
case WLAN_CIPHER_SUITE_GCMP_256:
|
|
return MCU_CIPHER_GCMP_256;
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
return MCU_CIPHER_WAPI;
|
|
default:
|
|
return MCU_CIPHER_NONE;
|
|
}
|
|
}
|
|
|
|
static u8 mt7921_mcu_chan_bw(struct cfg80211_chan_def *chandef)
|
|
{
|
|
static const u8 width_to_bw[] = {
|
|
[NL80211_CHAN_WIDTH_40] = CMD_CBW_40MHZ,
|
|
[NL80211_CHAN_WIDTH_80] = CMD_CBW_80MHZ,
|
|
[NL80211_CHAN_WIDTH_80P80] = CMD_CBW_8080MHZ,
|
|
[NL80211_CHAN_WIDTH_160] = CMD_CBW_160MHZ,
|
|
[NL80211_CHAN_WIDTH_5] = CMD_CBW_5MHZ,
|
|
[NL80211_CHAN_WIDTH_10] = CMD_CBW_10MHZ,
|
|
[NL80211_CHAN_WIDTH_20] = CMD_CBW_20MHZ,
|
|
[NL80211_CHAN_WIDTH_20_NOHT] = CMD_CBW_20MHZ,
|
|
};
|
|
|
|
if (chandef->width >= ARRAY_SIZE(width_to_bw))
|
|
return 0;
|
|
|
|
return width_to_bw[chandef->width];
|
|
}
|
|
|
|
static int
|
|
mt7921_mcu_parse_eeprom(struct mt76_dev *dev, struct sk_buff *skb)
|
|
{
|
|
struct mt7921_mcu_eeprom_info *res;
|
|
u8 *buf;
|
|
|
|
if (!skb)
|
|
return -EINVAL;
|
|
|
|
skb_pull(skb, sizeof(struct mt7921_mcu_rxd));
|
|
|
|
res = (struct mt7921_mcu_eeprom_info *)skb->data;
|
|
buf = dev->eeprom.data + le32_to_cpu(res->addr);
|
|
memcpy(buf, res->data, 16);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mt7921_mcu_parse_response(struct mt76_dev *mdev, int cmd,
|
|
struct sk_buff *skb, int seq)
|
|
{
|
|
int mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd);
|
|
struct mt7921_mcu_rxd *rxd;
|
|
int ret = 0;
|
|
|
|
if (!skb) {
|
|
dev_err(mdev->dev, "Message %08x (seq %d) timeout\n",
|
|
cmd, seq);
|
|
mt7921_reset(mdev);
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
rxd = (struct mt7921_mcu_rxd *)skb->data;
|
|
if (seq != rxd->seq)
|
|
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 if (cmd == MCU_EXT_CMD(EFUSE_ACCESS)) {
|
|
ret = mt7921_mcu_parse_eeprom(mdev, skb);
|
|
} else if (cmd == MCU_UNI_CMD(DEV_INFO_UPDATE) ||
|
|
cmd == MCU_UNI_CMD(BSS_INFO_UPDATE) ||
|
|
cmd == MCU_UNI_CMD(STA_REC_UPDATE) ||
|
|
cmd == MCU_UNI_CMD(HIF_CTRL) ||
|
|
cmd == MCU_UNI_CMD(OFFLOAD) ||
|
|
cmd == MCU_UNI_CMD(SUSPEND)) {
|
|
struct mt7921_mcu_uni_event *event;
|
|
|
|
skb_pull(skb, sizeof(*rxd));
|
|
event = (struct mt7921_mcu_uni_event *)skb->data;
|
|
ret = le32_to_cpu(event->status);
|
|
/* skip invalid event */
|
|
if (mcu_cmd != event->cid)
|
|
ret = -EAGAIN;
|
|
} else if (cmd == MCU_CE_QUERY(REG_READ)) {
|
|
struct mt7921_mcu_reg_event *event;
|
|
|
|
skb_pull(skb, sizeof(*rxd));
|
|
event = (struct mt7921_mcu_reg_event *)skb->data;
|
|
ret = (int)le32_to_cpu(event->val);
|
|
} else {
|
|
skb_pull(skb, sizeof(struct mt7921_mcu_rxd));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt7921_mcu_parse_response);
|
|
|
|
int mt7921_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb,
|
|
int cmd, int *wait_seq)
|
|
{
|
|
struct mt7921_dev *dev = container_of(mdev, struct mt7921_dev, mt76);
|
|
int txd_len, mcu_cmd = FIELD_GET(__MCU_CMD_FIELD_ID, cmd);
|
|
struct mt7921_uni_txd *uni_txd;
|
|
struct mt7921_mcu_txd *mcu_txd;
|
|
__le32 *txd;
|
|
u32 val;
|
|
u8 seq;
|
|
|
|
if (cmd == MCU_UNI_CMD(HIF_CTRL) ||
|
|
cmd == MCU_UNI_CMD(SUSPEND) ||
|
|
cmd == MCU_UNI_CMD(OFFLOAD))
|
|
mdev->mcu.timeout = HZ;
|
|
else
|
|
mdev->mcu.timeout = 3 * HZ;
|
|
|
|
seq = ++dev->mt76.mcu.msg_seq & 0xf;
|
|
if (!seq)
|
|
seq = ++dev->mt76.mcu.msg_seq & 0xf;
|
|
|
|
if (cmd == MCU_CMD(FW_SCATTER))
|
|
goto exit;
|
|
|
|
txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd);
|
|
txd = (__le32 *)skb_push(skb, txd_len);
|
|
|
|
val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) |
|
|
FIELD_PREP(MT_TXD0_PKT_FMT, MT_TX_TYPE_CMD) |
|
|
FIELD_PREP(MT_TXD0_Q_IDX, MT_TX_MCU_PORT_RX_Q0);
|
|
txd[0] = cpu_to_le32(val);
|
|
|
|
val = MT_TXD1_LONG_FORMAT |
|
|
FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_CMD);
|
|
txd[1] = cpu_to_le32(val);
|
|
|
|
if (cmd & __MCU_CMD_FIELD_UNI) {
|
|
uni_txd = (struct mt7921_uni_txd *)txd;
|
|
uni_txd->len = cpu_to_le16(skb->len - sizeof(uni_txd->txd));
|
|
uni_txd->option = MCU_CMD_UNI_EXT_ACK;
|
|
uni_txd->cid = cpu_to_le16(mcu_cmd);
|
|
uni_txd->s2d_index = MCU_S2D_H2N;
|
|
uni_txd->pkt_type = MCU_PKT_ID;
|
|
uni_txd->seq = seq;
|
|
|
|
goto exit;
|
|
}
|
|
|
|
mcu_txd = (struct mt7921_mcu_txd *)txd;
|
|
mcu_txd->len = cpu_to_le16(skb->len - sizeof(mcu_txd->txd));
|
|
mcu_txd->pq_id = cpu_to_le16(MCU_PQ_ID(MT_TX_PORT_IDX_MCU,
|
|
MT_TX_MCU_PORT_RX_Q0));
|
|
mcu_txd->pkt_type = MCU_PKT_ID;
|
|
mcu_txd->seq = seq;
|
|
mcu_txd->cid = mcu_cmd;
|
|
mcu_txd->s2d_index = MCU_S2D_H2N;
|
|
mcu_txd->ext_cid = FIELD_GET(__MCU_CMD_FIELD_EXT_ID, cmd);
|
|
|
|
if (mcu_txd->ext_cid || (cmd & __MCU_CMD_FIELD_CE)) {
|
|
if (cmd & __MCU_CMD_FIELD_QUERY)
|
|
mcu_txd->set_query = MCU_Q_QUERY;
|
|
else
|
|
mcu_txd->set_query = MCU_Q_SET;
|
|
mcu_txd->ext_cid_ack = !!mcu_txd->ext_cid;
|
|
} else {
|
|
mcu_txd->set_query = MCU_Q_NA;
|
|
}
|
|
|
|
exit:
|
|
if (wait_seq)
|
|
*wait_seq = seq;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt7921_mcu_fill_message);
|
|
|
|
static void
|
|
mt7921_mcu_scan_event(struct mt7921_dev *dev, struct sk_buff *skb)
|
|
{
|
|
struct mt76_phy *mphy = &dev->mt76.phy;
|
|
struct mt7921_phy *phy = (struct mt7921_phy *)mphy->priv;
|
|
|
|
spin_lock_bh(&dev->mt76.lock);
|
|
__skb_queue_tail(&phy->scan_event_list, skb);
|
|
spin_unlock_bh(&dev->mt76.lock);
|
|
|
|
ieee80211_queue_delayed_work(mphy->hw, &phy->scan_work,
|
|
MT7921_HW_SCAN_TIMEOUT);
|
|
}
|
|
|
|
static void
|
|
mt7921_mcu_connection_loss_iter(void *priv, u8 *mac,
|
|
struct ieee80211_vif *vif)
|
|
{
|
|
struct mt76_vif *mvif = (struct mt76_vif *)vif->drv_priv;
|
|
struct mt76_connac_beacon_loss_event *event = priv;
|
|
|
|
if (mvif->idx != event->bss_idx)
|
|
return;
|
|
|
|
if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER))
|
|
return;
|
|
|
|
ieee80211_connection_loss(vif);
|
|
}
|
|
|
|
static void
|
|
mt7921_mcu_connection_loss_event(struct mt7921_dev *dev, struct sk_buff *skb)
|
|
{
|
|
struct mt76_connac_beacon_loss_event *event;
|
|
struct mt76_phy *mphy = &dev->mt76.phy;
|
|
|
|
skb_pull(skb, sizeof(struct mt7921_mcu_rxd));
|
|
event = (struct mt76_connac_beacon_loss_event *)skb->data;
|
|
|
|
ieee80211_iterate_active_interfaces_atomic(mphy->hw,
|
|
IEEE80211_IFACE_ITER_RESUME_ALL,
|
|
mt7921_mcu_connection_loss_iter, event);
|
|
}
|
|
|
|
static void
|
|
mt7921_mcu_bss_event(struct mt7921_dev *dev, struct sk_buff *skb)
|
|
{
|
|
struct mt76_phy *mphy = &dev->mt76.phy;
|
|
struct mt76_connac_mcu_bss_event *event;
|
|
|
|
skb_pull(skb, sizeof(struct mt7921_mcu_rxd));
|
|
event = (struct mt76_connac_mcu_bss_event *)skb->data;
|
|
if (event->is_absent)
|
|
ieee80211_stop_queues(mphy->hw);
|
|
else
|
|
ieee80211_wake_queues(mphy->hw);
|
|
}
|
|
|
|
static void
|
|
mt7921_mcu_debug_msg_event(struct mt7921_dev *dev, struct sk_buff *skb)
|
|
{
|
|
struct mt7921_debug_msg {
|
|
__le16 id;
|
|
u8 type;
|
|
u8 flag;
|
|
__le32 value;
|
|
__le16 len;
|
|
u8 content[512];
|
|
} __packed * msg;
|
|
|
|
skb_pull(skb, sizeof(struct mt7921_mcu_rxd));
|
|
msg = (struct mt7921_debug_msg *)skb->data;
|
|
|
|
if (msg->type == 3) { /* fw log */
|
|
u16 len = min_t(u16, le16_to_cpu(msg->len), 512);
|
|
int i;
|
|
|
|
for (i = 0 ; i < len; i++) {
|
|
if (!msg->content[i])
|
|
msg->content[i] = ' ';
|
|
}
|
|
wiphy_info(mt76_hw(dev)->wiphy, "%.*s", len, msg->content);
|
|
}
|
|
}
|
|
|
|
static void
|
|
mt7921_mcu_low_power_event(struct mt7921_dev *dev, struct sk_buff *skb)
|
|
{
|
|
struct mt7921_mcu_lp_event {
|
|
u8 state;
|
|
u8 reserved[3];
|
|
} __packed * event;
|
|
|
|
skb_pull(skb, sizeof(struct mt7921_mcu_rxd));
|
|
event = (struct mt7921_mcu_lp_event *)skb->data;
|
|
|
|
trace_lp_event(dev, event->state);
|
|
}
|
|
|
|
static void
|
|
mt7921_mcu_tx_done_event(struct mt7921_dev *dev, struct sk_buff *skb)
|
|
{
|
|
struct mt7921_mcu_tx_done_event *event;
|
|
|
|
skb_pull(skb, sizeof(struct mt7921_mcu_rxd));
|
|
event = (struct mt7921_mcu_tx_done_event *)skb->data;
|
|
|
|
mt7921_mac_add_txs(dev, event->txs);
|
|
}
|
|
|
|
static void
|
|
mt7921_mcu_rx_unsolicited_event(struct mt7921_dev *dev, struct sk_buff *skb)
|
|
{
|
|
struct mt7921_mcu_rxd *rxd = (struct mt7921_mcu_rxd *)skb->data;
|
|
|
|
switch (rxd->eid) {
|
|
case MCU_EVENT_BSS_BEACON_LOSS:
|
|
mt7921_mcu_connection_loss_event(dev, skb);
|
|
break;
|
|
case MCU_EVENT_SCHED_SCAN_DONE:
|
|
case MCU_EVENT_SCAN_DONE:
|
|
mt7921_mcu_scan_event(dev, skb);
|
|
return;
|
|
case MCU_EVENT_BSS_ABSENCE:
|
|
mt7921_mcu_bss_event(dev, skb);
|
|
break;
|
|
case MCU_EVENT_DBG_MSG:
|
|
mt7921_mcu_debug_msg_event(dev, skb);
|
|
break;
|
|
case MCU_EVENT_COREDUMP:
|
|
dev->fw_assert = true;
|
|
mt76_connac_mcu_coredump_event(&dev->mt76, skb,
|
|
&dev->coredump);
|
|
return;
|
|
case MCU_EVENT_LP_INFO:
|
|
mt7921_mcu_low_power_event(dev, skb);
|
|
break;
|
|
case MCU_EVENT_TX_DONE:
|
|
mt7921_mcu_tx_done_event(dev, skb);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
dev_kfree_skb(skb);
|
|
}
|
|
|
|
void mt7921_mcu_rx_event(struct mt7921_dev *dev, struct sk_buff *skb)
|
|
{
|
|
struct mt7921_mcu_rxd *rxd;
|
|
|
|
if (skb_linearize(skb))
|
|
return;
|
|
|
|
rxd = (struct mt7921_mcu_rxd *)skb->data;
|
|
|
|
if (rxd->eid == 0x6) {
|
|
mt76_mcu_rx_event(&dev->mt76, skb);
|
|
return;
|
|
}
|
|
|
|
if (rxd->ext_eid == MCU_EXT_EVENT_RATE_REPORT ||
|
|
rxd->eid == MCU_EVENT_BSS_BEACON_LOSS ||
|
|
rxd->eid == MCU_EVENT_SCHED_SCAN_DONE ||
|
|
rxd->eid == MCU_EVENT_BSS_ABSENCE ||
|
|
rxd->eid == MCU_EVENT_SCAN_DONE ||
|
|
rxd->eid == MCU_EVENT_TX_DONE ||
|
|
rxd->eid == MCU_EVENT_DBG_MSG ||
|
|
rxd->eid == MCU_EVENT_COREDUMP ||
|
|
rxd->eid == MCU_EVENT_LP_INFO ||
|
|
!rxd->seq)
|
|
mt7921_mcu_rx_unsolicited_event(dev, skb);
|
|
else
|
|
mt76_mcu_rx_event(&dev->mt76, skb);
|
|
}
|
|
|
|
/** starec & wtbl **/
|
|
static int
|
|
mt7921_mcu_sta_key_tlv(struct mt7921_sta *msta, struct sk_buff *skb,
|
|
struct ieee80211_key_conf *key, enum set_key_cmd cmd)
|
|
{
|
|
struct mt7921_sta_key_conf *bip = &msta->bip;
|
|
struct sta_rec_sec *sec;
|
|
struct tlv *tlv;
|
|
u32 len = sizeof(*sec);
|
|
|
|
tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_KEY_V2, sizeof(*sec));
|
|
|
|
sec = (struct sta_rec_sec *)tlv;
|
|
sec->add = cmd;
|
|
|
|
if (cmd == SET_KEY) {
|
|
struct sec_key *sec_key;
|
|
u8 cipher;
|
|
|
|
cipher = mt7921_mcu_get_cipher(key->cipher);
|
|
if (cipher == MCU_CIPHER_NONE)
|
|
return -EOPNOTSUPP;
|
|
|
|
sec_key = &sec->key[0];
|
|
sec_key->cipher_len = sizeof(*sec_key);
|
|
|
|
if (cipher == MCU_CIPHER_BIP_CMAC_128) {
|
|
sec_key->cipher_id = MCU_CIPHER_AES_CCMP;
|
|
sec_key->key_id = bip->keyidx;
|
|
sec_key->key_len = 16;
|
|
memcpy(sec_key->key, bip->key, 16);
|
|
|
|
sec_key = &sec->key[1];
|
|
sec_key->cipher_id = MCU_CIPHER_BIP_CMAC_128;
|
|
sec_key->cipher_len = sizeof(*sec_key);
|
|
sec_key->key_len = 16;
|
|
memcpy(sec_key->key, key->key, 16);
|
|
|
|
sec->n_cipher = 2;
|
|
} else {
|
|
sec_key->cipher_id = cipher;
|
|
sec_key->key_id = key->keyidx;
|
|
sec_key->key_len = key->keylen;
|
|
memcpy(sec_key->key, key->key, key->keylen);
|
|
|
|
if (cipher == MCU_CIPHER_TKIP) {
|
|
/* Rx/Tx MIC keys are swapped */
|
|
memcpy(sec_key->key + 16, key->key + 24, 8);
|
|
memcpy(sec_key->key + 24, key->key + 16, 8);
|
|
}
|
|
|
|
/* store key_conf for BIP batch update */
|
|
if (cipher == MCU_CIPHER_AES_CCMP) {
|
|
memcpy(bip->key, key->key, key->keylen);
|
|
bip->keyidx = key->keyidx;
|
|
}
|
|
|
|
len -= sizeof(*sec_key);
|
|
sec->n_cipher = 1;
|
|
}
|
|
} else {
|
|
len -= sizeof(sec->key);
|
|
sec->n_cipher = 0;
|
|
}
|
|
sec->len = cpu_to_le16(len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mt7921_mcu_add_key(struct mt7921_dev *dev, struct ieee80211_vif *vif,
|
|
struct mt7921_sta *msta, struct ieee80211_key_conf *key,
|
|
enum set_key_cmd cmd)
|
|
{
|
|
struct mt7921_vif *mvif = (struct mt7921_vif *)vif->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);
|
|
|
|
ret = mt7921_mcu_sta_key_tlv(msta, skb, key, cmd);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return mt76_mcu_skb_send_msg(&dev->mt76, skb,
|
|
MCU_UNI_CMD(STA_REC_UPDATE), true);
|
|
}
|
|
|
|
int mt7921_mcu_uni_tx_ba(struct mt7921_dev *dev,
|
|
struct ieee80211_ampdu_params *params,
|
|
bool enable)
|
|
{
|
|
struct mt7921_sta *msta = (struct mt7921_sta *)params->sta->drv_priv;
|
|
|
|
if (enable && !params->amsdu)
|
|
msta->wcid.amsdu = false;
|
|
|
|
return mt76_connac_mcu_sta_ba(&dev->mt76, &msta->vif->mt76, params,
|
|
enable, true);
|
|
}
|
|
|
|
int mt7921_mcu_uni_rx_ba(struct mt7921_dev *dev,
|
|
struct ieee80211_ampdu_params *params,
|
|
bool enable)
|
|
{
|
|
struct mt7921_sta *msta = (struct mt7921_sta *)params->sta->drv_priv;
|
|
|
|
return mt76_connac_mcu_sta_ba(&dev->mt76, &msta->vif->mt76, params,
|
|
enable, false);
|
|
}
|
|
|
|
int mt7921_mcu_restart(struct mt76_dev *dev)
|
|
{
|
|
struct {
|
|
u8 power_mode;
|
|
u8 rsv[3];
|
|
} req = {
|
|
.power_mode = 1,
|
|
};
|
|
|
|
return mt76_mcu_send_msg(dev, MCU_CMD(NIC_POWER_CTRL), &req,
|
|
sizeof(req), false);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt7921_mcu_restart);
|
|
|
|
static u32 mt7921_get_data_mode(struct mt7921_dev *dev, u32 info)
|
|
{
|
|
u32 mode = DL_MODE_NEED_RSP;
|
|
|
|
if (info == PATCH_SEC_NOT_SUPPORT)
|
|
return mode;
|
|
|
|
switch (FIELD_GET(PATCH_SEC_ENC_TYPE_MASK, info)) {
|
|
case PATCH_SEC_ENC_TYPE_PLAIN:
|
|
break;
|
|
case PATCH_SEC_ENC_TYPE_AES:
|
|
mode |= DL_MODE_ENCRYPT;
|
|
mode |= FIELD_PREP(DL_MODE_KEY_IDX,
|
|
(info & PATCH_SEC_ENC_AES_KEY_MASK)) & DL_MODE_KEY_IDX;
|
|
mode |= DL_MODE_RESET_SEC_IV;
|
|
break;
|
|
case PATCH_SEC_ENC_TYPE_SCRAMBLE:
|
|
mode |= DL_MODE_ENCRYPT;
|
|
mode |= DL_CONFIG_ENCRY_MODE_SEL;
|
|
mode |= DL_MODE_RESET_SEC_IV;
|
|
break;
|
|
default:
|
|
dev_err(dev->mt76.dev, "Encryption type not support!\n");
|
|
}
|
|
|
|
return mode;
|
|
}
|
|
|
|
static char *mt7921_patch_name(struct mt7921_dev *dev)
|
|
{
|
|
char *ret;
|
|
|
|
if (is_mt7922(&dev->mt76))
|
|
ret = MT7922_ROM_PATCH;
|
|
else
|
|
ret = MT7921_ROM_PATCH;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mt7921_load_patch(struct mt7921_dev *dev)
|
|
{
|
|
const struct mt7921_patch_hdr *hdr;
|
|
const struct firmware *fw = NULL;
|
|
int i, ret, sem, max_len;
|
|
|
|
max_len = mt76_is_sdio(&dev->mt76) ? 2048 : 4096;
|
|
|
|
sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, true);
|
|
switch (sem) {
|
|
case PATCH_IS_DL:
|
|
return 0;
|
|
case PATCH_NOT_DL_SEM_SUCCESS:
|
|
break;
|
|
default:
|
|
dev_err(dev->mt76.dev, "Failed to get patch semaphore\n");
|
|
return -EAGAIN;
|
|
}
|
|
|
|
ret = request_firmware(&fw, mt7921_patch_name(dev), dev->mt76.dev);
|
|
if (ret)
|
|
goto out;
|
|
|
|
if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
|
|
dev_err(dev->mt76.dev, "Invalid firmware\n");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
hdr = (const struct mt7921_patch_hdr *)(fw->data);
|
|
|
|
dev_info(dev->mt76.dev, "HW/SW Version: 0x%x, Build Time: %.16s\n",
|
|
be32_to_cpu(hdr->hw_sw_ver), hdr->build_date);
|
|
|
|
for (i = 0; i < be32_to_cpu(hdr->desc.n_region); i++) {
|
|
struct mt7921_patch_sec *sec;
|
|
const u8 *dl;
|
|
u32 len, addr, mode;
|
|
u32 sec_info = 0;
|
|
|
|
sec = (struct mt7921_patch_sec *)(fw->data + sizeof(*hdr) +
|
|
i * sizeof(*sec));
|
|
if ((be32_to_cpu(sec->type) & PATCH_SEC_TYPE_MASK) !=
|
|
PATCH_SEC_TYPE_INFO) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
addr = be32_to_cpu(sec->info.addr);
|
|
len = be32_to_cpu(sec->info.len);
|
|
dl = fw->data + be32_to_cpu(sec->offs);
|
|
sec_info = be32_to_cpu(sec->info.sec_key_idx);
|
|
mode = mt7921_get_data_mode(dev, sec_info);
|
|
|
|
ret = mt76_connac_mcu_init_download(&dev->mt76, addr, len,
|
|
mode);
|
|
if (ret) {
|
|
dev_err(dev->mt76.dev, "Download request failed\n");
|
|
goto out;
|
|
}
|
|
|
|
ret = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER),
|
|
dl, len, max_len);
|
|
if (ret) {
|
|
dev_err(dev->mt76.dev, "Failed to send patch\n");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ret = mt76_connac_mcu_start_patch(&dev->mt76);
|
|
if (ret)
|
|
dev_err(dev->mt76.dev, "Failed to start patch\n");
|
|
|
|
if (mt76_is_sdio(&dev->mt76)) {
|
|
/* activate again */
|
|
ret = __mt7921_mcu_fw_pmctrl(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = __mt7921_mcu_drv_pmctrl(dev);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
out:
|
|
sem = mt76_connac_mcu_patch_sem_ctrl(&dev->mt76, false);
|
|
switch (sem) {
|
|
case PATCH_REL_SEM_SUCCESS:
|
|
break;
|
|
default:
|
|
ret = -EAGAIN;
|
|
dev_err(dev->mt76.dev, "Failed to release patch semaphore\n");
|
|
break;
|
|
}
|
|
release_firmware(fw);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static u32 mt7921_mcu_gen_dl_mode(u8 feature_set, bool is_wa)
|
|
{
|
|
u32 ret = 0;
|
|
|
|
ret |= (feature_set & FW_FEATURE_SET_ENCRYPT) ?
|
|
(DL_MODE_ENCRYPT | DL_MODE_RESET_SEC_IV) : 0;
|
|
ret |= (feature_set & FW_FEATURE_ENCRY_MODE) ?
|
|
DL_CONFIG_ENCRY_MODE_SEL : 0;
|
|
ret |= FIELD_PREP(DL_MODE_KEY_IDX,
|
|
FIELD_GET(FW_FEATURE_SET_KEY_IDX, feature_set));
|
|
ret |= DL_MODE_NEED_RSP;
|
|
ret |= is_wa ? DL_MODE_WORKING_PDA_CR4 : 0;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
mt7921_mcu_send_ram_firmware(struct mt7921_dev *dev,
|
|
const struct mt7921_fw_trailer *hdr,
|
|
const u8 *data, bool is_wa)
|
|
{
|
|
int i, offset = 0, max_len;
|
|
u32 override = 0, option = 0;
|
|
|
|
max_len = mt76_is_sdio(&dev->mt76) ? 2048 : 4096;
|
|
|
|
for (i = 0; i < hdr->n_region; i++) {
|
|
const struct mt7921_fw_region *region;
|
|
int err;
|
|
u32 len, addr, mode;
|
|
|
|
region = (const struct mt7921_fw_region *)((const u8 *)hdr -
|
|
(hdr->n_region - i) * sizeof(*region));
|
|
mode = mt7921_mcu_gen_dl_mode(region->feature_set, is_wa);
|
|
len = le32_to_cpu(region->len);
|
|
addr = le32_to_cpu(region->addr);
|
|
|
|
if (region->feature_set & FW_FEATURE_OVERRIDE_ADDR)
|
|
override = addr;
|
|
|
|
err = mt76_connac_mcu_init_download(&dev->mt76, addr, len,
|
|
mode);
|
|
if (err) {
|
|
dev_err(dev->mt76.dev, "Download request failed\n");
|
|
return err;
|
|
}
|
|
|
|
err = __mt76_mcu_send_firmware(&dev->mt76, MCU_CMD(FW_SCATTER),
|
|
data + offset, len, max_len);
|
|
if (err) {
|
|
dev_err(dev->mt76.dev, "Failed to send firmware.\n");
|
|
return err;
|
|
}
|
|
|
|
offset += len;
|
|
}
|
|
|
|
if (override)
|
|
option |= FW_START_OVERRIDE;
|
|
|
|
if (is_wa)
|
|
option |= FW_START_WORKING_PDA_CR4;
|
|
|
|
return mt76_connac_mcu_start_firmware(&dev->mt76, override, option);
|
|
}
|
|
|
|
static char *mt7921_ram_name(struct mt7921_dev *dev)
|
|
{
|
|
char *ret;
|
|
|
|
if (is_mt7922(&dev->mt76))
|
|
ret = MT7922_FIRMWARE_WM;
|
|
else
|
|
ret = MT7921_FIRMWARE_WM;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mt7921_load_ram(struct mt7921_dev *dev)
|
|
{
|
|
const struct mt7921_fw_trailer *hdr;
|
|
const struct firmware *fw;
|
|
int ret;
|
|
|
|
ret = request_firmware(&fw, mt7921_ram_name(dev), dev->mt76.dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!fw || !fw->data || fw->size < sizeof(*hdr)) {
|
|
dev_err(dev->mt76.dev, "Invalid firmware\n");
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
hdr = (const struct mt7921_fw_trailer *)(fw->data + fw->size -
|
|
sizeof(*hdr));
|
|
|
|
dev_info(dev->mt76.dev, "WM Firmware Version: %.10s, Build Time: %.15s\n",
|
|
hdr->fw_ver, hdr->build_date);
|
|
|
|
ret = mt7921_mcu_send_ram_firmware(dev, hdr, fw->data, false);
|
|
if (ret) {
|
|
dev_err(dev->mt76.dev, "Failed to start WM firmware\n");
|
|
goto out;
|
|
}
|
|
|
|
snprintf(dev->mt76.hw->wiphy->fw_version,
|
|
sizeof(dev->mt76.hw->wiphy->fw_version),
|
|
"%.10s-%.15s", hdr->fw_ver, hdr->build_date);
|
|
|
|
out:
|
|
release_firmware(fw);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int mt7921_load_firmware(struct mt7921_dev *dev)
|
|
{
|
|
int ret;
|
|
|
|
ret = mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY);
|
|
if (ret && mt76_is_mmio(&dev->mt76)) {
|
|
dev_dbg(dev->mt76.dev, "Firmware is already download\n");
|
|
goto fw_loaded;
|
|
}
|
|
|
|
ret = mt7921_load_patch(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = mt7921_load_ram(dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!mt76_poll_msec(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY,
|
|
MT_TOP_MISC2_FW_N9_RDY, 1500)) {
|
|
dev_err(dev->mt76.dev, "Timeout for initializing firmware\n");
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
fw_loaded:
|
|
|
|
#ifdef CONFIG_PM
|
|
dev->mt76.hw->wiphy->wowlan = &mt76_connac_wowlan_support;
|
|
#endif /* CONFIG_PM */
|
|
|
|
dev_dbg(dev->mt76.dev, "Firmware init done\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mt7921_mcu_fw_log_2_host(struct mt7921_dev *dev, u8 ctrl)
|
|
{
|
|
struct {
|
|
u8 ctrl_val;
|
|
u8 pad[3];
|
|
} data = {
|
|
.ctrl_val = ctrl
|
|
};
|
|
|
|
return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(FWLOG_2_HOST),
|
|
&data, sizeof(data), false);
|
|
}
|
|
|
|
int mt7921_run_firmware(struct mt7921_dev *dev)
|
|
{
|
|
int err;
|
|
|
|
err = mt7921_load_firmware(dev);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mt76_connac_mcu_get_nic_capability(&dev->mphy);
|
|
if (err)
|
|
return err;
|
|
|
|
set_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state);
|
|
return mt7921_mcu_fw_log_2_host(dev, 1);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt7921_run_firmware);
|
|
|
|
void mt7921_mcu_exit(struct mt7921_dev *dev)
|
|
{
|
|
skb_queue_purge(&dev->mt76.mcu.res_q);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt7921_mcu_exit);
|
|
|
|
int mt7921_mcu_set_tx(struct mt7921_dev *dev, struct ieee80211_vif *vif)
|
|
{
|
|
#define WMM_AIFS_SET BIT(0)
|
|
#define WMM_CW_MIN_SET BIT(1)
|
|
#define WMM_CW_MAX_SET BIT(2)
|
|
#define WMM_TXOP_SET BIT(3)
|
|
#define WMM_PARAM_SET GENMASK(3, 0)
|
|
#define TX_CMD_MODE 1
|
|
struct edca {
|
|
u8 queue;
|
|
u8 set;
|
|
u8 aifs;
|
|
u8 cw_min;
|
|
__le16 cw_max;
|
|
__le16 txop;
|
|
};
|
|
struct mt7921_mcu_tx {
|
|
u8 total;
|
|
u8 action;
|
|
u8 valid;
|
|
u8 mode;
|
|
|
|
struct edca edca[IEEE80211_NUM_ACS];
|
|
} __packed req = {
|
|
.valid = true,
|
|
.mode = TX_CMD_MODE,
|
|
.total = IEEE80211_NUM_ACS,
|
|
};
|
|
struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
|
|
struct mu_edca {
|
|
u8 cw_min;
|
|
u8 cw_max;
|
|
u8 aifsn;
|
|
u8 acm;
|
|
u8 timer;
|
|
u8 padding[3];
|
|
};
|
|
struct mt7921_mcu_mu_tx {
|
|
u8 ver;
|
|
u8 pad0;
|
|
__le16 len;
|
|
u8 bss_idx;
|
|
u8 qos;
|
|
u8 wmm_idx;
|
|
u8 pad1;
|
|
struct mu_edca edca[IEEE80211_NUM_ACS];
|
|
u8 pad3[32];
|
|
} __packed req_mu = {
|
|
.bss_idx = mvif->mt76.idx,
|
|
.qos = vif->bss_conf.qos,
|
|
.wmm_idx = mvif->mt76.wmm_idx,
|
|
};
|
|
int ac, ret;
|
|
|
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
|
struct ieee80211_tx_queue_params *q = &mvif->queue_params[ac];
|
|
struct edca *e = &req.edca[ac];
|
|
|
|
e->set = WMM_PARAM_SET;
|
|
e->queue = ac + mvif->mt76.wmm_idx * MT7921_MAX_WMM_SETS;
|
|
e->aifs = q->aifs;
|
|
e->txop = cpu_to_le16(q->txop);
|
|
|
|
if (q->cw_min)
|
|
e->cw_min = fls(q->cw_min);
|
|
else
|
|
e->cw_min = 5;
|
|
|
|
if (q->cw_max)
|
|
e->cw_max = cpu_to_le16(fls(q->cw_max));
|
|
else
|
|
e->cw_max = cpu_to_le16(10);
|
|
}
|
|
|
|
ret = mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EDCA_UPDATE),
|
|
&req, sizeof(req), true);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!vif->bss_conf.he_support)
|
|
return 0;
|
|
|
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
|
struct ieee80211_he_mu_edca_param_ac_rec *q;
|
|
struct mu_edca *e;
|
|
int to_aci[] = {1, 0, 2, 3};
|
|
|
|
if (!mvif->queue_params[ac].mu_edca)
|
|
break;
|
|
|
|
q = &mvif->queue_params[ac].mu_edca_param_rec;
|
|
e = &(req_mu.edca[to_aci[ac]]);
|
|
|
|
e->cw_min = q->ecw_min_max & 0xf;
|
|
e->cw_max = (q->ecw_min_max & 0xf0) >> 4;
|
|
e->aifsn = q->aifsn;
|
|
e->timer = q->mu_edca_timer;
|
|
}
|
|
|
|
return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_MU_EDCA_PARMS),
|
|
&req_mu, sizeof(req_mu), false);
|
|
}
|
|
|
|
int mt7921_mcu_set_chan_info(struct mt7921_phy *phy, int cmd)
|
|
{
|
|
struct mt7921_dev *dev = phy->dev;
|
|
struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
|
|
int freq1 = chandef->center_freq1;
|
|
struct {
|
|
u8 control_ch;
|
|
u8 center_ch;
|
|
u8 bw;
|
|
u8 tx_streams_num;
|
|
u8 rx_streams; /* mask or num */
|
|
u8 switch_reason;
|
|
u8 band_idx;
|
|
u8 center_ch2; /* for 80+80 only */
|
|
__le16 cac_case;
|
|
u8 channel_band;
|
|
u8 rsv0;
|
|
__le32 outband_freq;
|
|
u8 txpower_drop;
|
|
u8 ap_bw;
|
|
u8 ap_center_ch;
|
|
u8 rsv1[57];
|
|
} __packed req = {
|
|
.control_ch = chandef->chan->hw_value,
|
|
.center_ch = ieee80211_frequency_to_channel(freq1),
|
|
.bw = mt7921_mcu_chan_bw(chandef),
|
|
.tx_streams_num = hweight8(phy->mt76->antenna_mask),
|
|
.rx_streams = phy->mt76->antenna_mask,
|
|
.band_idx = phy != &dev->phy,
|
|
};
|
|
|
|
if (chandef->chan->band == NL80211_BAND_6GHZ)
|
|
req.channel_band = 2;
|
|
else
|
|
req.channel_band = chandef->chan->band;
|
|
|
|
if (dev->mt76.hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)
|
|
req.switch_reason = CH_SWITCH_SCAN_BYPASS_DPD;
|
|
else if ((chandef->chan->flags & IEEE80211_CHAN_RADAR) &&
|
|
chandef->chan->dfs_state != NL80211_DFS_AVAILABLE)
|
|
req.switch_reason = CH_SWITCH_DFS;
|
|
else
|
|
req.switch_reason = CH_SWITCH_NORMAL;
|
|
|
|
if (cmd == MCU_EXT_CMD(CHANNEL_SWITCH))
|
|
req.rx_streams = hweight8(req.rx_streams);
|
|
|
|
if (chandef->width == NL80211_CHAN_WIDTH_80P80) {
|
|
int freq2 = chandef->center_freq2;
|
|
|
|
req.center_ch2 = ieee80211_frequency_to_channel(freq2);
|
|
}
|
|
|
|
return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), true);
|
|
}
|
|
|
|
int mt7921_mcu_set_eeprom(struct mt7921_dev *dev)
|
|
{
|
|
struct req_hdr {
|
|
u8 buffer_mode;
|
|
u8 format;
|
|
__le16 len;
|
|
} __packed req = {
|
|
.buffer_mode = EE_MODE_EFUSE,
|
|
.format = EE_FORMAT_WHOLE,
|
|
};
|
|
|
|
return mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD(EFUSE_BUFFER_MODE),
|
|
&req, sizeof(req), true);
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt7921_mcu_set_eeprom);
|
|
|
|
int mt7921_mcu_get_eeprom(struct mt7921_dev *dev, u32 offset)
|
|
{
|
|
struct mt7921_mcu_eeprom_info req = {
|
|
.addr = cpu_to_le32(round_down(offset, 16)),
|
|
};
|
|
struct mt7921_mcu_eeprom_info *res;
|
|
struct sk_buff *skb;
|
|
int ret;
|
|
u8 *buf;
|
|
|
|
ret = mt76_mcu_send_and_get_msg(&dev->mt76,
|
|
MCU_EXT_QUERY(EFUSE_ACCESS),
|
|
&req, sizeof(req), true, &skb);
|
|
if (ret)
|
|
return ret;
|
|
|
|
res = (struct mt7921_mcu_eeprom_info *)skb->data;
|
|
buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr);
|
|
memcpy(buf, res->data, 16);
|
|
dev_kfree_skb(skb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mt7921_mcu_uni_bss_ps(struct mt7921_dev *dev, struct ieee80211_vif *vif)
|
|
{
|
|
struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
|
|
struct {
|
|
struct {
|
|
u8 bss_idx;
|
|
u8 pad[3];
|
|
} __packed hdr;
|
|
struct ps_tlv {
|
|
__le16 tag;
|
|
__le16 len;
|
|
u8 ps_state; /* 0: device awake
|
|
* 1: static power save
|
|
* 2: dynamic power saving
|
|
* 3: enter TWT power saving
|
|
* 4: leave TWT power saving
|
|
*/
|
|
u8 pad[3];
|
|
} __packed ps;
|
|
} __packed ps_req = {
|
|
.hdr = {
|
|
.bss_idx = mvif->mt76.idx,
|
|
},
|
|
.ps = {
|
|
.tag = cpu_to_le16(UNI_BSS_INFO_PS),
|
|
.len = cpu_to_le16(sizeof(struct ps_tlv)),
|
|
.ps_state = vif->bss_conf.ps ? 2 : 0,
|
|
},
|
|
};
|
|
|
|
if (vif->type != NL80211_IFTYPE_STATION)
|
|
return -EOPNOTSUPP;
|
|
|
|
return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE),
|
|
&ps_req, sizeof(ps_req), true);
|
|
}
|
|
|
|
static int
|
|
mt7921_mcu_uni_bss_bcnft(struct mt7921_dev *dev, struct ieee80211_vif *vif,
|
|
bool enable)
|
|
{
|
|
struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
|
|
struct {
|
|
struct {
|
|
u8 bss_idx;
|
|
u8 pad[3];
|
|
} __packed hdr;
|
|
struct bcnft_tlv {
|
|
__le16 tag;
|
|
__le16 len;
|
|
__le16 bcn_interval;
|
|
u8 dtim_period;
|
|
u8 pad;
|
|
} __packed bcnft;
|
|
} __packed bcnft_req = {
|
|
.hdr = {
|
|
.bss_idx = mvif->mt76.idx,
|
|
},
|
|
.bcnft = {
|
|
.tag = cpu_to_le16(UNI_BSS_INFO_BCNFT),
|
|
.len = cpu_to_le16(sizeof(struct bcnft_tlv)),
|
|
.bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int),
|
|
.dtim_period = vif->bss_conf.dtim_period,
|
|
},
|
|
};
|
|
|
|
if (vif->type != NL80211_IFTYPE_STATION)
|
|
return 0;
|
|
|
|
return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(BSS_INFO_UPDATE),
|
|
&bcnft_req, sizeof(bcnft_req), true);
|
|
}
|
|
|
|
static int
|
|
mt7921_mcu_set_bss_pm(struct mt7921_dev *dev, struct ieee80211_vif *vif,
|
|
bool enable)
|
|
{
|
|
struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
|
|
struct {
|
|
u8 bss_idx;
|
|
u8 dtim_period;
|
|
__le16 aid;
|
|
__le16 bcn_interval;
|
|
__le16 atim_window;
|
|
u8 uapsd;
|
|
u8 bmc_delivered_ac;
|
|
u8 bmc_triggered_ac;
|
|
u8 pad;
|
|
} req = {
|
|
.bss_idx = mvif->mt76.idx,
|
|
.aid = cpu_to_le16(vif->bss_conf.aid),
|
|
.dtim_period = vif->bss_conf.dtim_period,
|
|
.bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int),
|
|
};
|
|
struct {
|
|
u8 bss_idx;
|
|
u8 pad[3];
|
|
} req_hdr = {
|
|
.bss_idx = mvif->mt76.idx,
|
|
};
|
|
int err;
|
|
|
|
if (vif->type != NL80211_IFTYPE_STATION)
|
|
return 0;
|
|
|
|
err = mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_BSS_ABORT),
|
|
&req_hdr, sizeof(req_hdr), false);
|
|
if (err < 0 || !enable)
|
|
return err;
|
|
|
|
return mt76_mcu_send_msg(&dev->mt76, MCU_CE_CMD(SET_BSS_CONNECTED),
|
|
&req, sizeof(req), false);
|
|
}
|
|
|
|
int mt7921_mcu_sta_update(struct mt7921_dev *dev, struct ieee80211_sta *sta,
|
|
struct ieee80211_vif *vif, bool enable,
|
|
enum mt76_sta_info_state state)
|
|
{
|
|
struct mt7921_vif *mvif = (struct mt7921_vif *)vif->drv_priv;
|
|
int rssi = -ewma_rssi_read(&mvif->rssi);
|
|
struct mt76_sta_cmd_info info = {
|
|
.sta = sta,
|
|
.vif = vif,
|
|
.enable = enable,
|
|
.cmd = MCU_UNI_CMD(STA_REC_UPDATE),
|
|
.state = state,
|
|
.offload_fw = true,
|
|
.rcpi = to_rcpi(rssi),
|
|
};
|
|
struct mt7921_sta *msta;
|
|
|
|
msta = sta ? (struct mt7921_sta *)sta->drv_priv : NULL;
|
|
info.wcid = msta ? &msta->wcid : &mvif->sta.wcid;
|
|
info.newly = msta ? state != MT76_STA_INFO_STATE_ASSOC : true;
|
|
|
|
return mt76_connac_mcu_sta_cmd(&dev->mphy, &info);
|
|
}
|
|
|
|
int mt7921_mcu_drv_pmctrl(struct mt7921_dev *dev)
|
|
{
|
|
struct mt76_phy *mphy = &dev->mt76.phy;
|
|
struct mt76_connac_pm *pm = &dev->pm;
|
|
int err = 0;
|
|
|
|
mutex_lock(&pm->mutex);
|
|
|
|
if (!test_bit(MT76_STATE_PM, &mphy->state))
|
|
goto out;
|
|
|
|
err = __mt7921_mcu_drv_pmctrl(dev);
|
|
out:
|
|
mutex_unlock(&pm->mutex);
|
|
|
|
if (err)
|
|
mt7921_reset(&dev->mt76);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt7921_mcu_drv_pmctrl);
|
|
|
|
int mt7921_mcu_fw_pmctrl(struct mt7921_dev *dev)
|
|
{
|
|
struct mt76_phy *mphy = &dev->mt76.phy;
|
|
struct mt76_connac_pm *pm = &dev->pm;
|
|
int err = 0;
|
|
|
|
mutex_lock(&pm->mutex);
|
|
|
|
if (mt76_connac_skip_fw_pmctrl(mphy, pm))
|
|
goto out;
|
|
|
|
err = __mt7921_mcu_fw_pmctrl(dev);
|
|
out:
|
|
mutex_unlock(&pm->mutex);
|
|
|
|
if (err)
|
|
mt7921_reset(&dev->mt76);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mt7921_mcu_fw_pmctrl);
|
|
|
|
int mt7921_mcu_set_beacon_filter(struct mt7921_dev *dev,
|
|
struct ieee80211_vif *vif,
|
|
bool enable)
|
|
{
|
|
struct ieee80211_hw *hw = mt76_hw(dev);
|
|
int err;
|
|
|
|
if (enable) {
|
|
err = mt7921_mcu_uni_bss_bcnft(dev, vif, true);
|
|
if (err)
|
|
return err;
|
|
|
|
vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
|
|
ieee80211_hw_set(hw, CONNECTION_MONITOR);
|
|
mt76_set(dev, MT_WF_RFCR(0), MT_WF_RFCR_DROP_OTHER_BEACON);
|
|
|
|
return 0;
|
|
}
|
|
|
|
err = mt7921_mcu_set_bss_pm(dev, vif, false);
|
|
if (err)
|
|
return err;
|
|
|
|
vif->driver_flags &= ~IEEE80211_VIF_BEACON_FILTER;
|
|
__clear_bit(IEEE80211_HW_CONNECTION_MONITOR, hw->flags);
|
|
mt76_clear(dev, MT_WF_RFCR(0), MT_WF_RFCR_DROP_OTHER_BEACON);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mt7921_get_txpwr_info(struct mt7921_dev *dev, struct mt7921_txpwr *txpwr)
|
|
{
|
|
struct mt7921_txpwr_event *event;
|
|
struct mt7921_txpwr_req req = {
|
|
.dbdc_idx = 0,
|
|
};
|
|
struct sk_buff *skb;
|
|
int ret;
|
|
|
|
ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_CE_CMD(GET_TXPWR),
|
|
&req, sizeof(req), true, &skb);
|
|
if (ret)
|
|
return ret;
|
|
|
|
event = (struct mt7921_txpwr_event *)skb->data;
|
|
WARN_ON(skb->len != le16_to_cpu(event->len));
|
|
memcpy(txpwr, &event->txpwr, sizeof(event->txpwr));
|
|
|
|
dev_kfree_skb(skb);
|
|
|
|
return 0;
|
|
}
|