diff options
Diffstat (limited to 'drivers/net/wireless/mediatek/mt76/mt7615/mcu.c')
-rw-r--r-- | drivers/net/wireless/mediatek/mt76/mt7615/mcu.c | 2165 |
1 files changed, 1360 insertions, 805 deletions
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c index 7218a3041ead..50c98913d81d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c @@ -29,8 +29,37 @@ struct mt7615_fw_trailer { __le32 len; } __packed; +#define FW_V3_COMMON_TAILER_SIZE 36 +#define FW_V3_REGION_TAILER_SIZE 40 +#define FW_START_OVERRIDE BIT(0) +#define FW_START_DLYCAL BIT(1) +#define FW_START_WORKING_PDA_CR4 BIT(2) + +struct mt7663_fw_trailer { + u8 chip_id; + u8 eco_code; + u8 n_region; + u8 format_ver; + u8 format_flag; + u8 reserv[2]; + char fw_ver[10]; + char build_date[15]; + u32 crc; +} __packed; + +struct mt7663_fw_buf { + u32 crc; + u32 d_img_size; + u32 block_size; + u8 rsv[4]; + u32 img_dest_addr; + u32 img_size; + u8 feature_set; +}; + #define MT7615_PATCH_ADDRESS 0x80000 #define MT7622_PATCH_ADDRESS 0x9c000 +#define MT7663_PATCH_ADDRESS 0xdc000 #define N9_REGION_NUM 2 #define CR4_REGION_NUM 1 @@ -44,29 +73,32 @@ struct mt7615_fw_trailer { #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_MODE_VALID_RAM_ENTRY BIT(5) #define DL_MODE_NEED_RSP BIT(31) #define FW_START_OVERRIDE BIT(0) #define FW_START_WORKING_PDA_CR4 BIT(2) -static int __mt7615_mcu_msg_send(struct mt7615_dev *dev, struct sk_buff *skb, - int cmd, int *wait_seq) +void mt7615_mcu_fill_msg(struct mt7615_dev *dev, struct sk_buff *skb, + int cmd, int *wait_seq) { + int txd_len, mcu_cmd = cmd & MCU_CMD_MASK; + struct mt7615_uni_txd *uni_txd; struct mt7615_mcu_txd *mcu_txd; u8 seq, q_idx, pkt_fmt; - enum mt76_txq_id qid; - u32 val; __le32 *txd; + u32 val; seq = ++dev->mt76.mcu.msg_seq & 0xf; if (!seq) seq = ++dev->mt76.mcu.msg_seq & 0xf; + if (wait_seq) + *wait_seq = seq; - mcu_txd = (struct mt7615_mcu_txd *)skb_push(skb, - sizeof(struct mt7615_mcu_txd)); - memset(mcu_txd, 0, sizeof(struct mt7615_mcu_txd)); + txd_len = cmd & MCU_UNI_PREFIX ? sizeof(*uni_txd) : sizeof(*mcu_txd); + txd = (__le32 *)skb_push(skb, txd_len); - if (cmd != -MCU_CMD_FW_SCATTER) { + if (cmd != MCU_CMD_FW_SCATTER) { q_idx = MT_TX_MCU_PORT_RX_Q0; pkt_fmt = MT_TX_TYPE_CMD; } else { @@ -74,8 +106,6 @@ static int __mt7615_mcu_msg_send(struct mt7615_dev *dev, struct sk_buff *skb, pkt_fmt = MT_TX_TYPE_FW; } - txd = mcu_txd->txd; - val = FIELD_PREP(MT_TXD0_TX_BYTES, skb->len) | FIELD_PREP(MT_TXD0_P_IDX, MT_TX_PORT_IDX_MCU) | FIELD_PREP(MT_TXD0_Q_IDX, q_idx); @@ -86,25 +116,42 @@ static int __mt7615_mcu_msg_send(struct mt7615_dev *dev, struct sk_buff *skb, FIELD_PREP(MT_TXD1_PKT_FMT, pkt_fmt); txd[1] = cpu_to_le32(val); + if (cmd & MCU_UNI_PREFIX) { + uni_txd = (struct mt7615_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->s2d_index = MCU_S2D_H2N; + uni_txd->pkt_type = MCU_PKT_ID; + uni_txd->cid = mcu_cmd; + uni_txd->seq = seq; + + return; + } + + mcu_txd = (struct mt7615_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, q_idx)); + mcu_txd->s2d_index = MCU_S2D_H2N; mcu_txd->pkt_type = MCU_PKT_ID; mcu_txd->seq = seq; - if (cmd < 0) { + if (cmd & MCU_FW_PREFIX) { mcu_txd->set_query = MCU_Q_NA; - mcu_txd->cid = -cmd; + mcu_txd->cid = mcu_cmd; } else { mcu_txd->cid = MCU_CMD_EXT_CID; mcu_txd->set_query = MCU_Q_SET; mcu_txd->ext_cid = cmd; mcu_txd->ext_cid_ack = 1; } - mcu_txd->s2d_index = MCU_S2D_H2N; +} - if (wait_seq) - *wait_seq = seq; +static int __mt7615_mcu_msg_send(struct mt7615_dev *dev, struct sk_buff *skb, + int cmd, int *wait_seq) +{ + enum mt76_txq_id qid; + mt7615_mcu_fill_msg(dev, skb, cmd, wait_seq); if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) qid = MT_TXQ_MCU; else @@ -124,7 +171,7 @@ mt7615_mcu_parse_response(struct mt7615_dev *dev, int cmd, return -EAGAIN; switch (cmd) { - case -MCU_CMD_PATCH_SEM_CONTROL: + case MCU_CMD_PATCH_SEM_CONTROL: skb_pull(skb, sizeof(*rxd) - 4); ret = *skb->data; break; @@ -140,32 +187,18 @@ mt7615_mcu_parse_response(struct mt7615_dev *dev, int cmd, return ret; } -static int -mt7615_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data, - int len, bool wait_resp) +int mt7615_mcu_wait_response(struct mt7615_dev *dev, int cmd, int seq) { - struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); unsigned long expires = jiffies + 20 * HZ; struct sk_buff *skb; - int ret, seq; - - skb = mt7615_mcu_msg_alloc(data, len); - if (!skb) - return -ENOMEM; - - mutex_lock(&mdev->mcu.mutex); - - ret = __mt7615_mcu_msg_send(dev, skb, cmd, &seq); - if (ret) - goto out; + int ret = 0; - while (wait_resp) { - skb = mt76_mcu_get_response(mdev, expires); + while (true) { + skb = mt76_mcu_get_response(&dev->mt76, expires); if (!skb) { - dev_err(mdev->dev, "Message %d (seq %d) timeout\n", - cmd, seq); - ret = -ETIMEDOUT; - break; + dev_err(dev->mt76.dev, "Message %ld (seq %d) timeout\n", + cmd & MCU_CMD_MASK, seq); + return -ETIMEDOUT; } ret = mt7615_mcu_parse_response(dev, cmd, skb, seq); @@ -173,12 +206,44 @@ mt7615_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data, break; } + return ret; +} + +static int +mt7615_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, + int cmd, bool wait_resp) +{ + struct mt7615_dev *dev = container_of(mdev, struct mt7615_dev, mt76); + int ret, seq; + + mutex_lock(&mdev->mcu.mutex); + + ret = __mt7615_mcu_msg_send(dev, skb, cmd, &seq); + if (ret) + goto out; + + if (wait_resp) + ret = mt7615_mcu_wait_response(dev, cmd, seq); + out: mutex_unlock(&mdev->mcu.mutex); return ret; } +static int +mt7615_mcu_msg_send(struct mt76_dev *mdev, int cmd, const void *data, + int len, bool wait_resp) +{ + struct sk_buff *skb; + + skb = mt7615_mcu_msg_alloc(data, len); + if (!skb) + return -ENOMEM; + + return __mt76_mcu_skb_send_msg(mdev, skb, cmd, wait_resp); +} + static void mt7615_mcu_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif) { @@ -287,10 +352,1095 @@ static int mt7615_mcu_init_download(struct mt7615_dev *dev, u32 addr, .mode = cpu_to_le32(mode), }; - return __mt76_mcu_send_msg(&dev->mt76, -MCU_CMD_TARGET_ADDRESS_LEN_REQ, + return __mt76_mcu_send_msg(&dev->mt76, MCU_CMD_TARGET_ADDRESS_LEN_REQ, + &req, sizeof(req), true); +} + +static int +mt7615_mcu_add_dev(struct mt7615_dev *dev, struct ieee80211_vif *vif, + bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_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->omac_idx, + .band_idx = mvif->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->band_idx, + }, + }; + + 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 int +mt7615_mcu_add_beacon_offload(struct mt7615_dev *dev, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + struct ieee80211_mutable_offsets offs; + struct ieee80211_tx_info *info; + struct req { + u8 omac_idx; + u8 enable; + u8 wlan_idx; + u8 band_idx; + u8 pkt_type; + u8 need_pre_tbtt_int; + __le16 csa_ie_pos; + __le16 pkt_len; + __le16 tim_ie_pos; + u8 pkt[512]; + u8 csa_cnt; + /* bss color change */ + u8 bcc_cnt; + __le16 bcc_ie_pos; + } __packed req = { + .omac_idx = mvif->omac_idx, + .enable = enable, + .wlan_idx = wcid->idx, + .band_idx = mvif->band_idx, + }; + struct sk_buff *skb; + + skb = ieee80211_beacon_get_template(hw, vif, &offs); + if (!skb) + return -EINVAL; + + if (skb->len > 512 - MT_TXD_SIZE) { + dev_err(dev->mt76.dev, "Bcn size limit exceed\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + if (mvif->band_idx) { + info = IEEE80211_SKB_CB(skb); + info->hw_queue |= MT_TX_HW_QUEUE_EXT_PHY; + } + + mt7615_mac_write_txwi(dev, (__le32 *)(req.pkt), skb, wcid, NULL, + 0, NULL, true); + memcpy(req.pkt + MT_TXD_SIZE, skb->data, skb->len); + req.pkt_len = cpu_to_le16(MT_TXD_SIZE + skb->len); + req.tim_ie_pos = cpu_to_le16(MT_TXD_SIZE + offs.tim_offset); + if (offs.csa_counter_offs[0]) { + u16 csa_offs; + + csa_offs = MT_TXD_SIZE + offs.csa_counter_offs[0] - 4; + req.csa_ie_pos = cpu_to_le16(csa_offs); + req.csa_cnt = skb->data[offs.csa_counter_offs[0]]; + } + dev_kfree_skb(skb); + + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_BCN_OFFLOAD, + &req, sizeof(req), true); +} + +static int +mt7615_mcu_ctrl_pm_state(struct mt7615_dev *dev, int band, int state) +{ +#define ENTER_PM_STATE 1 +#define EXIT_PM_STATE 2 + struct { + u8 pm_number; + u8 pm_state; + u8 bssid[ETH_ALEN]; + u8 dtim_period; + u8 wlan_idx; + __le16 bcn_interval; + __le32 aid; + __le32 rx_filter; + u8 band_idx; + u8 rsv[3]; + __le32 feature; + u8 omac_idx; + u8 wmm_idx; + u8 bcn_loss_cnt; + u8 bcn_sp_duration; + } __packed req = { + .pm_number = 5, + .pm_state = state ? ENTER_PM_STATE : EXIT_PM_STATE, + .band_idx = band, + }; + + return __mt76_mcu_send_msg(&dev->mt76, MCU_EXT_CMD_PM_STATE_CTRL, &req, sizeof(req), true); } +static struct sk_buff * +mt7615_mcu_alloc_sta_req(struct mt7615_vif *mvif, struct mt7615_sta *msta) +{ + struct sta_req_hdr hdr = { + .bss_idx = mvif->idx, + .wlan_idx = msta ? msta->wcid.idx : 0, + .muar_idx = msta ? mvif->omac_idx : 0, + .is_tlv_append = 1, + }; + struct sk_buff *skb; + + skb = mt7615_mcu_msg_alloc(NULL, MT7615_STA_UPDATE_MAX_SIZE); + if (!skb) + return ERR_PTR(-ENOMEM); + + skb_put_data(skb, &hdr, sizeof(hdr)); + + return skb; +} + +static struct wtbl_req_hdr * +mt7615_mcu_alloc_wtbl_req(struct mt7615_sta *msta, int cmd, + void *sta_wtbl, struct sk_buff **skb) +{ + struct tlv *sta_hdr = sta_wtbl; + struct wtbl_req_hdr hdr = { + .wlan_idx = msta->wcid.idx, + .operation = cmd, + }; + struct sk_buff *nskb = *skb; + + if (!nskb) { + nskb = mt7615_mcu_msg_alloc(NULL, MT7615_WTBL_UPDATE_BA_SIZE); + if (!nskb) + return ERR_PTR(-ENOMEM); + + *skb = nskb; + } + + if (sta_hdr) + sta_hdr->len = cpu_to_le16(sizeof(hdr)); + + return skb_put_data(nskb, &hdr, sizeof(hdr)); +} + +static struct tlv * +mt7615_mcu_add_nested_tlv(struct sk_buff *skb, int tag, int len, + void *sta_ntlv, void *sta_wtbl) +{ + struct sta_ntlv_hdr *ntlv_hdr = sta_ntlv; + struct tlv *sta_hdr = sta_wtbl; + struct tlv *ptlv, tlv = { + .tag = cpu_to_le16(tag), + .len = cpu_to_le16(len), + }; + u16 ntlv; + + ptlv = skb_put(skb, len); + memcpy(ptlv, &tlv, sizeof(tlv)); + + ntlv = le16_to_cpu(ntlv_hdr->tlv_num); + ntlv_hdr->tlv_num = cpu_to_le16(ntlv + 1); + + if (sta_hdr) { + u16 size = le16_to_cpu(sta_hdr->len); + + sta_hdr->len = cpu_to_le16(size + len); + } + + return ptlv; +} + +static struct tlv * +mt7615_mcu_add_tlv(struct sk_buff *skb, int tag, int len) +{ + return mt7615_mcu_add_nested_tlv(skb, tag, len, skb->data, NULL); +} + +static int +mt7615_mcu_bss_basic_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct bss_info_basic *bss; + u8 wlan_idx = mvif->sta.wcid.idx; + u32 type = NETWORK_INFRA; + struct tlv *tlv; + + tlv = mt7615_mcu_add_tlv(skb, BSS_INFO_BASIC, sizeof(*bss)); + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + break; + case NL80211_IFTYPE_STATION: + /* TODO: enable BSS_INFO_UAPSD & BSS_INFO_PM */ + if (enable) { + struct ieee80211_sta *sta; + struct mt7615_sta *msta; + + rcu_read_lock(); + sta = ieee80211_find_sta(vif, vif->bss_conf.bssid); + if (!sta) { + rcu_read_unlock(); + return -EINVAL; + } + + msta = (struct mt7615_sta *)sta->drv_priv; + wlan_idx = msta->wcid.idx; + rcu_read_unlock(); + } + break; + case NL80211_IFTYPE_ADHOC: + type = NETWORK_IBSS; + break; + default: + WARN_ON(1); + break; + } + + bss = (struct bss_info_basic *)tlv; + memcpy(bss->bssid, vif->bss_conf.bssid, ETH_ALEN); + bss->bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int); + bss->network_type = cpu_to_le32(type); + bss->dtim_period = vif->bss_conf.dtim_period; + bss->bmc_tx_wlan_idx = wlan_idx; + bss->wmm_idx = mvif->wmm_idx; + bss->active = enable; + + return 0; +} + +static void +mt7615_mcu_bss_omac_tlv(struct sk_buff *skb, struct ieee80211_vif *vif) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct bss_info_omac *omac; + struct tlv *tlv; + u32 type = 0; + u8 idx; + + tlv = mt7615_mcu_add_tlv(skb, BSS_INFO_OMAC, sizeof(*omac)); + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + type = CONNECTION_INFRA_AP; + break; + case NL80211_IFTYPE_STATION: + type = CONNECTION_INFRA_STA; + break; + case NL80211_IFTYPE_ADHOC: + type = CONNECTION_IBSS_ADHOC; + break; + default: + WARN_ON(1); + break; + } + + omac = (struct bss_info_omac *)tlv; + idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx; + omac->conn_type = cpu_to_le32(type); + omac->omac_idx = mvif->omac_idx; + omac->band_idx = mvif->band_idx; + omac->hw_bss_idx = idx; +} + +/* SIFS 20us + 512 byte beacon tranmitted by 1Mbps (3906us) */ +#define BCN_TX_ESTIMATE_TIME (4096 + 20) +static void +mt7615_mcu_bss_ext_tlv(struct sk_buff *skb, struct mt7615_vif *mvif) +{ + struct bss_info_ext_bss *ext; + int ext_bss_idx, tsf_offset; + struct tlv *tlv; + + ext_bss_idx = mvif->omac_idx - EXT_BSSID_START; + if (ext_bss_idx < 0) + return; + + tlv = mt7615_mcu_add_tlv(skb, BSS_INFO_EXT_BSS, sizeof(*ext)); + + ext = (struct bss_info_ext_bss *)tlv; + tsf_offset = ext_bss_idx * BCN_TX_ESTIMATE_TIME; + ext->mbss_tsf_offset = cpu_to_le32(tsf_offset); +} + +static void +mt7615_mcu_sta_ba_tlv(struct sk_buff *skb, + struct ieee80211_ampdu_params *params, + bool enable, bool tx) +{ + struct sta_rec_ba *ba; + struct tlv *tlv; + + tlv = mt7615_mcu_add_tlv(skb, STA_REC_BA, sizeof(*ba)); + + ba = (struct sta_rec_ba *)tlv; + ba->ba_type = tx ? MT_BA_TYPE_ORIGINATOR : MT_BA_TYPE_RECIPIENT, + ba->winsize = cpu_to_le16(params->buf_size); + ba->ssn = cpu_to_le16(params->ssn); + ba->ba_en = enable << params->tid; + ba->amsdu = params->amsdu; + ba->tid = params->tid; +} + +static void +mt7615_mcu_sta_basic_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable) +{ + struct sta_rec_basic *basic; + struct tlv *tlv; + + tlv = mt7615_mcu_add_tlv(skb, STA_REC_BASIC, sizeof(*basic)); + + basic = (struct sta_rec_basic *)tlv; + basic->extra_info = cpu_to_le16(EXTRA_INFO_VER); + + if (enable) { + basic->extra_info |= cpu_to_le16(EXTRA_INFO_NEW); + basic->conn_state = CONN_STATE_PORT_SECURE; + } else { + basic->conn_state = CONN_STATE_DISCONNECT; + } + + if (!sta) { + basic->conn_type = cpu_to_le32(CONNECTION_INFRA_BC); + eth_broadcast_addr(basic->peer_addr); + return; + } + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + basic->conn_type = cpu_to_le32(CONNECTION_INFRA_STA); + break; + case NL80211_IFTYPE_STATION: + basic->conn_type = cpu_to_le32(CONNECTION_INFRA_AP); + break; + case NL80211_IFTYPE_ADHOC: + basic->conn_type = cpu_to_le32(CONNECTION_IBSS_ADHOC); + break; + default: + WARN_ON(1); + break; + } + + memcpy(basic->peer_addr, sta->addr, ETH_ALEN); + basic->aid = cpu_to_le16(sta->aid); + basic->qos = sta->wme; +} + +static void +mt7615_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +{ + struct tlv *tlv; + + if (sta->ht_cap.ht_supported) { + struct sta_rec_ht *ht; + + tlv = mt7615_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); + ht = (struct sta_rec_ht *)tlv; + ht->ht_cap = cpu_to_le16(sta->ht_cap.cap); + } + if (sta->vht_cap.vht_supported) { + struct sta_rec_vht *vht; + + tlv = mt7615_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); + vht = (struct sta_rec_vht *)tlv; + vht->vht_rx_mcs_map = sta->vht_cap.vht_mcs.rx_mcs_map; + vht->vht_tx_mcs_map = sta->vht_cap.vht_mcs.tx_mcs_map; + vht->vht_cap = cpu_to_le32(sta->vht_cap.cap); + } +} + +static void +mt7615_mcu_wtbl_ba_tlv(struct sk_buff *skb, + struct ieee80211_ampdu_params *params, + bool enable, bool tx, void *sta_wtbl, + void *wtbl_tlv) +{ + struct wtbl_ba *ba; + struct tlv *tlv; + + tlv = mt7615_mcu_add_nested_tlv(skb, WTBL_BA, sizeof(*ba), + wtbl_tlv, sta_wtbl); + + ba = (struct wtbl_ba *)tlv; + ba->tid = params->tid; + + if (tx) { + ba->ba_type = MT_BA_TYPE_ORIGINATOR; + ba->sn = enable ? cpu_to_le16(params->ssn) : 0; + ba->ba_winsize = cpu_to_le16(params->buf_size); + ba->ba_en = enable; + } else { + memcpy(ba->peer_addr, params->sta->addr, ETH_ALEN); + ba->ba_type = MT_BA_TYPE_RECIPIENT; + ba->rst_ba_tid = params->tid; + ba->rst_ba_sel = RST_BA_MAC_TID_MATCH; + ba->rst_ba_sb = 1; + } + + if (enable && tx) { + u8 ba_range[] = { 4, 8, 12, 24, 36, 48, 54, 64 }; + int i; + + for (i = 7; i > 0; i--) { + if (params->buf_size >= ba_range[i]) + break; + } + ba->ba_winsize_idx = i; + } +} + +static void +mt7615_mcu_wtbl_generic_tlv(struct sk_buff *skb, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, void *sta_wtbl, + void *wtbl_tlv) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct wtbl_generic *generic; + struct wtbl_rx *rx; + struct tlv *tlv; + + tlv = mt7615_mcu_add_nested_tlv(skb, WTBL_GENERIC, sizeof(*generic), + wtbl_tlv, sta_wtbl); + + generic = (struct wtbl_generic *)tlv; + + if (sta) { + memcpy(generic->peer_addr, sta->addr, ETH_ALEN); + generic->partial_aid = cpu_to_le16(sta->aid); + generic->muar_idx = mvif->omac_idx; + generic->qos = sta->wme; + } else { + eth_broadcast_addr(generic->peer_addr); + generic->muar_idx = 0xe; + } + + tlv = mt7615_mcu_add_nested_tlv(skb, WTBL_RX, sizeof(*rx), + wtbl_tlv, sta_wtbl); + + rx = (struct wtbl_rx *)tlv; + rx->rca1 = sta ? vif->type != NL80211_IFTYPE_AP : 1; + rx->rca2 = 1; + rx->rv = 1; +} + +static void +mt7615_mcu_wtbl_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta, + void *sta_wtbl, void *wtbl_tlv) +{ + struct tlv *tlv; + u32 flags = 0; + + if (sta->ht_cap.ht_supported) { + struct wtbl_ht *ht; + + tlv = mt7615_mcu_add_nested_tlv(skb, WTBL_HT, sizeof(*ht), + wtbl_tlv, sta_wtbl); + ht = (struct wtbl_ht *)tlv; + ht->ldpc = sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING; + ht->af = sta->ht_cap.ampdu_factor; + ht->mm = sta->ht_cap.ampdu_density; + ht->ht = 1; + + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) + flags |= MT_WTBL_W5_SHORT_GI_20; + if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + flags |= MT_WTBL_W5_SHORT_GI_40; + } + + if (sta->vht_cap.vht_supported) { + struct wtbl_vht *vht; + + tlv = mt7615_mcu_add_nested_tlv(skb, WTBL_VHT, sizeof(*vht), + wtbl_tlv, sta_wtbl); + vht = (struct wtbl_vht *)tlv; + vht->ldpc = sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC, + vht->vht = 1; + + if (sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) + flags |= MT_WTBL_W5_SHORT_GI_80; + if (sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160) + flags |= MT_WTBL_W5_SHORT_GI_160; + } + + /* wtbl smps */ + if (sta->smps_mode == IEEE80211_SMPS_DYNAMIC) { + struct wtbl_smps *smps; + + tlv = mt7615_mcu_add_nested_tlv(skb, WTBL_SMPS, sizeof(*smps), + wtbl_tlv, sta_wtbl); + smps = (struct wtbl_smps *)tlv; + smps->smps = 1; + } + + if (sta->ht_cap.ht_supported) { + /* sgi */ + u32 msk = MT_WTBL_W5_SHORT_GI_20 | MT_WTBL_W5_SHORT_GI_40 | + MT_WTBL_W5_SHORT_GI_80 | MT_WTBL_W5_SHORT_GI_160; + struct wtbl_raw *raw; + + tlv = mt7615_mcu_add_nested_tlv(skb, WTBL_RAW_DATA, + sizeof(*raw), wtbl_tlv, + sta_wtbl); + raw = (struct wtbl_raw *)tlv; + raw->val = cpu_to_le32(flags); + raw->msk = cpu_to_le32(~msk); + raw->wtbl_idx = 1; + raw->dw = 5; + } +} + +static int +mt7615_mcu_add_bss(struct mt7615_dev *dev, struct ieee80211_vif *vif, + bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct sk_buff *skb; + + skb = mt7615_mcu_alloc_sta_req(mvif, NULL); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + if (enable) + mt7615_mcu_bss_omac_tlv(skb, vif); + + mt7615_mcu_bss_basic_tlv(skb, vif, enable); + + if (enable && mvif->omac_idx > EXT_BSSID_START) + mt7615_mcu_bss_ext_tlv(skb, mvif); + + return __mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD_BSS_INFO_UPDATE, true); +} + +static int +mt7615_mcu_wtbl_tx_ba(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt7615_sta *msta = (struct mt7615_sta *)params->sta->drv_priv; + struct mt7615_vif *mvif = msta->vif; + struct wtbl_req_hdr *wtbl_hdr; + struct sk_buff *skb = NULL; + int err; + + wtbl_hdr = mt7615_mcu_alloc_wtbl_req(msta, WTBL_SET, NULL, &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + mt7615_mcu_wtbl_ba_tlv(skb, params, enable, true, NULL, wtbl_hdr); + + err = __mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD_WTBL_UPDATE, true); + if (err < 0) + return err; + + skb = mt7615_mcu_alloc_sta_req(mvif, msta); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt7615_mcu_sta_ba_tlv(skb, params, enable, true); + + return __mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD_STA_REC_UPDATE, true); +} + +static int +mt7615_mcu_wtbl_rx_ba(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + struct mt7615_sta *msta = (struct mt7615_sta *)params->sta->drv_priv; + struct mt7615_vif *mvif = msta->vif; + struct wtbl_req_hdr *wtbl_hdr; + struct sk_buff *skb; + int err; + + skb = mt7615_mcu_alloc_sta_req(mvif, msta); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt7615_mcu_sta_ba_tlv(skb, params, enable, false); + + err = __mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD_STA_REC_UPDATE, true); + if (err < 0 || !enable) + return err; + + skb = NULL; + wtbl_hdr = mt7615_mcu_alloc_wtbl_req(msta, WTBL_SET, NULL, &skb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + mt7615_mcu_wtbl_ba_tlv(skb, params, enable, false, NULL, wtbl_hdr); + + return __mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD_WTBL_UPDATE, true); +} + +static int +mt7615_mcu_wtbl_sta_add(struct mt7615_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct sk_buff *skb, *sskb, *wskb = NULL; + struct wtbl_req_hdr *wtbl_hdr; + struct mt7615_sta *msta; + int cmd, err; + + msta = sta ? (struct mt7615_sta *)sta->drv_priv : &mvif->sta; + + sskb = mt7615_mcu_alloc_sta_req(mvif, msta); + if (IS_ERR(sskb)) + return PTR_ERR(sskb); + + mt7615_mcu_sta_basic_tlv(sskb, vif, sta, enable); + if (enable && sta) + mt7615_mcu_sta_ht_tlv(sskb, sta); + + wtbl_hdr = mt7615_mcu_alloc_wtbl_req(msta, WTBL_RESET_AND_SET, NULL, + &wskb); + if (IS_ERR(wtbl_hdr)) + return PTR_ERR(wtbl_hdr); + + if (enable) { + mt7615_mcu_wtbl_generic_tlv(wskb, vif, sta, NULL, wtbl_hdr); + if (sta) + mt7615_mcu_wtbl_ht_tlv(wskb, sta, NULL, wtbl_hdr); + } + + cmd = enable ? MCU_EXT_CMD_WTBL_UPDATE : MCU_EXT_CMD_STA_REC_UPDATE; + skb = enable ? wskb : sskb; + + err = __mt76_mcu_skb_send_msg(&dev->mt76, skb, cmd, true); + if (err < 0) + return err; + + cmd = enable ? MCU_EXT_CMD_STA_REC_UPDATE : MCU_EXT_CMD_WTBL_UPDATE; + skb = enable ? sskb : wskb; + + return __mt76_mcu_skb_send_msg(&dev->mt76, skb, cmd, true); +} + +static const struct mt7615_mcu_ops wtbl_update_ops = { + .add_beacon_offload = mt7615_mcu_add_beacon_offload, + .set_pm_state = mt7615_mcu_ctrl_pm_state, + .add_dev_info = mt7615_mcu_add_dev, + .add_bss_info = mt7615_mcu_add_bss, + .add_tx_ba = mt7615_mcu_wtbl_tx_ba, + .add_rx_ba = mt7615_mcu_wtbl_rx_ba, + .sta_add = mt7615_mcu_wtbl_sta_add, +}; + +static int +mt7615_mcu_sta_ba(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable, bool tx) +{ + struct mt7615_sta *msta = (struct mt7615_sta *)params->sta->drv_priv; + struct mt7615_vif *mvif = msta->vif; + struct wtbl_req_hdr *wtbl_hdr; + struct tlv *sta_wtbl; + struct sk_buff *skb; + + skb = mt7615_mcu_alloc_sta_req(mvif, msta); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt7615_mcu_sta_ba_tlv(skb, params, enable, tx); + + sta_wtbl = mt7615_mcu_add_tlv(skb, STA_REC_WTBL, sizeof(struct tlv)); + + wtbl_hdr = mt7615_mcu_alloc_wtbl_req(msta, WTBL_SET, sta_wtbl, &skb); + mt7615_mcu_wtbl_ba_tlv(skb, params, enable, tx, sta_wtbl, wtbl_hdr); + + return __mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_EXT_CMD_STA_REC_UPDATE, true); +} + +static int +mt7615_mcu_sta_tx_ba(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + return mt7615_mcu_sta_ba(dev, params, enable, true); +} + +static int +mt7615_mcu_sta_rx_ba(struct mt7615_dev *dev, + struct ieee80211_ampdu_params *params, + bool enable) +{ + return mt7615_mcu_sta_ba(dev, params, enable, false); +} + +static int +mt7615_mcu_add_sta_cmd(struct mt7615_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable, int cmd) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct wtbl_req_hdr *wtbl_hdr; + struct mt7615_sta *msta; + struct tlv *sta_wtbl; + struct sk_buff *skb; + + msta = sta ? (struct mt7615_sta *)sta->drv_priv : &mvif->sta; + + skb = mt7615_mcu_alloc_sta_req(mvif, msta); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt7615_mcu_sta_basic_tlv(skb, vif, sta, enable); + if (enable && sta) + mt7615_mcu_sta_ht_tlv(skb, sta); + + sta_wtbl = mt7615_mcu_add_tlv(skb, STA_REC_WTBL, sizeof(struct tlv)); + + wtbl_hdr = mt7615_mcu_alloc_wtbl_req(msta, WTBL_RESET_AND_SET, + sta_wtbl, &skb); + if (enable) { + mt7615_mcu_wtbl_generic_tlv(skb, vif, sta, sta_wtbl, wtbl_hdr); + if (sta) + mt7615_mcu_wtbl_ht_tlv(skb, sta, sta_wtbl, wtbl_hdr); + } + + return __mt76_mcu_skb_send_msg(&dev->mt76, skb, cmd, true); +} + +static int +mt7615_mcu_add_sta(struct mt7615_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, bool enable) +{ + return mt7615_mcu_add_sta_cmd(dev, vif, sta, enable, + MCU_EXT_CMD_STA_REC_UPDATE); +} + +static const struct mt7615_mcu_ops sta_update_ops = { + .add_beacon_offload = mt7615_mcu_add_beacon_offload, + .set_pm_state = mt7615_mcu_ctrl_pm_state, + .add_dev_info = mt7615_mcu_add_dev, + .add_bss_info = mt7615_mcu_add_bss, + .add_tx_ba = mt7615_mcu_sta_tx_ba, + .add_rx_ba = mt7615_mcu_sta_rx_ba, + .sta_add = mt7615_mcu_add_sta, +}; + +static int +mt7615_mcu_uni_add_dev(struct mt7615_dev *dev, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct { + struct req_hdr { + u8 omac_idx; + u8 band_idx; + __le16 pad; + } __packed hdr; + struct req_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 pad; + u8 omac_addr[ETH_ALEN]; + } __packed tlv; + } data = { + .hdr = { + .omac_idx = mvif->omac_idx, + .band_idx = mvif->band_idx, + }, + .tlv = { + .tag = cpu_to_le16(DEV_INFO_ACTIVE), + .len = cpu_to_le16(sizeof(struct req_tlv)), + .active = enable, + }, + }; + + memcpy(data.tlv.omac_addr, vif->addr, ETH_ALEN); + + return __mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD_DEV_INFO_UPDATE, + &data, sizeof(data), true); +} + +static int +mt7615_mcu_uni_ctrl_pm_state(struct mt7615_dev *dev, int band, int state) +{ + return 0; +} + +static int +mt7615_mcu_uni_add_bss(struct mt7615_dev *dev, + struct ieee80211_vif *vif, bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct { + struct req_hdr { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct basic_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 omac_idx; + u8 hw_bss_idx; + u8 band_idx; + __le32 conn_type; + u8 conn_state; + u8 wmm_idx; + u8 bssid[ETH_ALEN]; + __le16 bmc_tx_wlan_idx; + __le16 bcn_interval; + u8 dtim_period; + u8 phymode; + __le16 sta_idx; + u8 nonht_basic_phy; + u8 pad[3]; + } __packed basic; + } req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .basic = { + .tag = cpu_to_le16(UNI_BSS_INFO_BASIC), + .len = cpu_to_le16(sizeof(struct basic_tlv)), + .bcn_interval = cpu_to_le16(vif->bss_conf.beacon_int), + .dtim_period = vif->bss_conf.dtim_period, + .omac_idx = mvif->omac_idx, + .band_idx = mvif->band_idx, + .wmm_idx = mvif->wmm_idx, + .active = enable, + }, + }; + u8 idx, tx_wlan_idx = 0; + + idx = mvif->omac_idx > EXT_BSSID_START ? HW_BSSID_0 : mvif->omac_idx; + req.basic.hw_bss_idx = idx; + + switch (vif->type) { + case NL80211_IFTYPE_MESH_POINT: + case NL80211_IFTYPE_AP: + req.basic.conn_type = cpu_to_le32(CONNECTION_INFRA_AP); + tx_wlan_idx = mvif->sta.wcid.idx; + break; + case NL80211_IFTYPE_STATION: + if (enable) { + struct ieee80211_sta *sta; + struct mt7615_sta *msta; + + rcu_read_lock(); + sta = ieee80211_find_sta(vif, vif->bss_conf.bssid); + if (!sta) { + rcu_read_unlock(); + return -EINVAL; + } + + msta = (struct mt7615_sta *)sta->drv_priv; + tx_wlan_idx = msta->wcid.idx; + rcu_read_unlock(); + } + req.basic.conn_type = cpu_to_le32(CONNECTION_INFRA_STA); + break; + default: + WARN_ON(1); + break; + } + + memcpy(req.basic.bssid, vif->bss_conf.bssid, ETH_ALEN); + req.basic.bmc_tx_wlan_idx = cpu_to_le16(tx_wlan_idx); + req.basic.sta_idx = cpu_to_le16(tx_wlan_idx); + req.basic.conn_state = !enable; + + return __mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD_BSS_INFO_UPDATE, + &req, sizeof(req), true); +} + +static int +mt7615_mcu_uni_add_beacon_offload(struct mt7615_dev *dev, + struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + bool enable) +{ + struct mt7615_vif *mvif = (struct mt7615_vif *)vif->drv_priv; + struct mt76_wcid *wcid = &dev->mt76.global_wcid; + struct ieee80211_mutable_offsets offs; + struct { + struct req_hdr { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct bcn_content_tlv { + __le16 tag; + __le16 len; + __le16 tim_ie_pos; + __le16 csa_ie_pos; + __le16 bcc_ie_pos; + /* 0: enable beacon offload + * 1: disable beacon offload + * 2: update probe respond offload + */ + u8 enable; + /* 0: legacy format (TXD + payload) + * 1: only cap field IE + */ + u8 type; + __le16 pkt_len; + u8 pkt[512]; + } __packed beacon_tlv; + } req = { + .hdr = { + .bss_idx = mvif->idx, + }, + .beacon_tlv = { + .tag = cpu_to_le16(UNI_BSS_INFO_BCN_CONTENT), + .len = cpu_to_le16(sizeof(struct bcn_content_tlv)), + .enable = enable, + }, + }; + struct sk_buff *skb; + + skb = ieee80211_beacon_get_template(mt76_hw(dev), vif, &offs); + if (!skb) + return -EINVAL; + + if (skb->len > 512 - MT_TXD_SIZE) { + dev_err(dev->mt76.dev, "beacon size limit exceed\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + + mt7615_mac_write_txwi(dev, (__le32 *)(req.beacon_tlv.pkt), skb, + wcid, NULL, 0, NULL, true); + memcpy(req.beacon_tlv.pkt + MT_TXD_SIZE, skb->da |