xref: /openbmc/linux/drivers/net/wireless/mediatek/mt76/mt76x02_util.c (revision 26d0dfbb16fcb17d128a79dc70f3020ea6992af0)
10e3d6777SRyder Lee // SPDX-License-Identifier: ISC
2108a4861SStanislaw Gruszka /*
3108a4861SStanislaw Gruszka  * Copyright (C) 2018 Stanislaw Gruszka <stf_xl@wp.pl>
4108a4861SStanislaw Gruszka  * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
5108a4861SStanislaw Gruszka  */
6108a4861SStanislaw Gruszka 
7db6bb5c6SFelix Fietkau #include <linux/module.h>
87a07adcdSLorenzo Bianconi #include "mt76x02.h"
9108a4861SStanislaw Gruszka 
1054b8fdebSLorenzo Bianconi #define MT76x02_CCK_RATE(_idx, _rate) {					\
1158b5eb8cSLorenzo Bianconi 	.bitrate = _rate,					\
1258b5eb8cSLorenzo Bianconi 	.flags = IEEE80211_RATE_SHORT_PREAMBLE,			\
13ff97c52aSRyder Lee 	.hw_value = (MT_PHY_TYPE_CCK << 8) | (_idx),		\
14ff97c52aSRyder Lee 	.hw_value_short = (MT_PHY_TYPE_CCK << 8) | (8 + (_idx)),	\
1558b5eb8cSLorenzo Bianconi }
1658b5eb8cSLorenzo Bianconi 
1758b5eb8cSLorenzo Bianconi struct ieee80211_rate mt76x02_rates[] = {
1854b8fdebSLorenzo Bianconi 	MT76x02_CCK_RATE(0, 10),
1954b8fdebSLorenzo Bianconi 	MT76x02_CCK_RATE(1, 20),
2054b8fdebSLorenzo Bianconi 	MT76x02_CCK_RATE(2, 55),
2154b8fdebSLorenzo Bianconi 	MT76x02_CCK_RATE(3, 110),
2258b5eb8cSLorenzo Bianconi 	OFDM_RATE(0, 60),
2358b5eb8cSLorenzo Bianconi 	OFDM_RATE(1, 90),
2458b5eb8cSLorenzo Bianconi 	OFDM_RATE(2, 120),
2558b5eb8cSLorenzo Bianconi 	OFDM_RATE(3, 180),
2658b5eb8cSLorenzo Bianconi 	OFDM_RATE(4, 240),
2758b5eb8cSLorenzo Bianconi 	OFDM_RATE(5, 360),
2858b5eb8cSLorenzo Bianconi 	OFDM_RATE(6, 480),
2958b5eb8cSLorenzo Bianconi 	OFDM_RATE(7, 540),
3058b5eb8cSLorenzo Bianconi };
3158b5eb8cSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76x02_rates);
3258b5eb8cSLorenzo Bianconi 
335cbace02SLorenzo Bianconi static const struct ieee80211_iface_limit mt76x02_if_limits[] = {
345cbace02SLorenzo Bianconi 	{
355cbace02SLorenzo Bianconi 		.max = 1,
365cbace02SLorenzo Bianconi 		.types = BIT(NL80211_IFTYPE_ADHOC)
375cbace02SLorenzo Bianconi 	}, {
385cbace02SLorenzo Bianconi 		.max = 8,
395cbace02SLorenzo Bianconi 		.types = BIT(NL80211_IFTYPE_STATION) |
405cbace02SLorenzo Bianconi #ifdef CONFIG_MAC80211_MESH
415cbace02SLorenzo Bianconi 			 BIT(NL80211_IFTYPE_MESH_POINT) |
425cbace02SLorenzo Bianconi #endif
4350eb0a88SLorenzo Bianconi 			 BIT(NL80211_IFTYPE_P2P_CLIENT) |
4450eb0a88SLorenzo Bianconi 			 BIT(NL80211_IFTYPE_P2P_GO) |
455cbace02SLorenzo Bianconi 			 BIT(NL80211_IFTYPE_AP)
465cbace02SLorenzo Bianconi 	 },
475cbace02SLorenzo Bianconi };
485cbace02SLorenzo Bianconi 
49f110d1d5SLorenzo Bianconi static const struct ieee80211_iface_limit mt76x02u_if_limits[] = {
50f110d1d5SLorenzo Bianconi 	{
51f110d1d5SLorenzo Bianconi 		.max = 1,
52f110d1d5SLorenzo Bianconi 		.types = BIT(NL80211_IFTYPE_ADHOC)
53f110d1d5SLorenzo Bianconi 	}, {
54f110d1d5SLorenzo Bianconi 		.max = 2,
55f110d1d5SLorenzo Bianconi 		.types = BIT(NL80211_IFTYPE_STATION) |
56f110d1d5SLorenzo Bianconi #ifdef CONFIG_MAC80211_MESH
57f110d1d5SLorenzo Bianconi 			 BIT(NL80211_IFTYPE_MESH_POINT) |
58f110d1d5SLorenzo Bianconi #endif
5950eb0a88SLorenzo Bianconi 			 BIT(NL80211_IFTYPE_P2P_CLIENT) |
6050eb0a88SLorenzo Bianconi 			 BIT(NL80211_IFTYPE_P2P_GO) |
61f110d1d5SLorenzo Bianconi 			 BIT(NL80211_IFTYPE_AP)
62f110d1d5SLorenzo Bianconi 	},
63f110d1d5SLorenzo Bianconi };
64f110d1d5SLorenzo Bianconi 
655cbace02SLorenzo Bianconi static const struct ieee80211_iface_combination mt76x02_if_comb[] = {
665cbace02SLorenzo Bianconi 	{
675cbace02SLorenzo Bianconi 		.limits = mt76x02_if_limits,
685cbace02SLorenzo Bianconi 		.n_limits = ARRAY_SIZE(mt76x02_if_limits),
695cbace02SLorenzo Bianconi 		.max_interfaces = 8,
705cbace02SLorenzo Bianconi 		.num_different_channels = 1,
715cbace02SLorenzo Bianconi 		.beacon_int_infra_match = true,
725cbace02SLorenzo Bianconi 		.radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) |
735cbace02SLorenzo Bianconi 				       BIT(NL80211_CHAN_WIDTH_20) |
745cbace02SLorenzo Bianconi 				       BIT(NL80211_CHAN_WIDTH_40) |
755cbace02SLorenzo Bianconi 				       BIT(NL80211_CHAN_WIDTH_80),
765cbace02SLorenzo Bianconi 	}
775cbace02SLorenzo Bianconi };
785cbace02SLorenzo Bianconi 
79f110d1d5SLorenzo Bianconi static const struct ieee80211_iface_combination mt76x02u_if_comb[] = {
80f110d1d5SLorenzo Bianconi 	{
81f110d1d5SLorenzo Bianconi 		.limits = mt76x02u_if_limits,
82f110d1d5SLorenzo Bianconi 		.n_limits = ARRAY_SIZE(mt76x02u_if_limits),
83f110d1d5SLorenzo Bianconi 		.max_interfaces = 2,
84f110d1d5SLorenzo Bianconi 		.num_different_channels = 1,
85f110d1d5SLorenzo Bianconi 		.beacon_int_infra_match = true,
86f110d1d5SLorenzo Bianconi 	}
87f110d1d5SLorenzo Bianconi };
88f110d1d5SLorenzo Bianconi 
895c9decdfSLorenzo Bianconi static void
mt76x02_led_set_config(struct mt76_phy * mphy,u8 delay_on,u8 delay_off)903abd46ddSLorenzo Bianconi mt76x02_led_set_config(struct mt76_phy *mphy, u8 delay_on, u8 delay_off)
915c9decdfSLorenzo Bianconi {
923abd46ddSLorenzo Bianconi 	struct mt76x02_dev *dev = container_of(mphy->dev, struct mt76x02_dev,
935c9decdfSLorenzo Bianconi 					       mt76);
945c9decdfSLorenzo Bianconi 	u32 val;
955c9decdfSLorenzo Bianconi 
96d1ff4a3cSLorenzo Bianconi 	val = FIELD_PREP(MT_LED_STATUS_DURATION, 0xff) |
97d1ff4a3cSLorenzo Bianconi 	      FIELD_PREP(MT_LED_STATUS_OFF, delay_off) |
98d1ff4a3cSLorenzo Bianconi 	      FIELD_PREP(MT_LED_STATUS_ON, delay_on);
995c9decdfSLorenzo Bianconi 
1003abd46ddSLorenzo Bianconi 	mt76_wr(dev, MT_LED_S0(mphy->leds.pin), val);
1013abd46ddSLorenzo Bianconi 	mt76_wr(dev, MT_LED_S1(mphy->leds.pin), val);
1025c9decdfSLorenzo Bianconi 
1033abd46ddSLorenzo Bianconi 	val = MT_LED_CTRL_REPLAY(mphy->leds.pin) |
1043abd46ddSLorenzo Bianconi 	      MT_LED_CTRL_KICK(mphy->leds.pin);
1053abd46ddSLorenzo Bianconi 	if (mphy->leds.al)
1063abd46ddSLorenzo Bianconi 		val |= MT_LED_CTRL_POLARITY(mphy->leds.pin);
1075c9decdfSLorenzo Bianconi 	mt76_wr(dev, MT_LED_CTRL, val);
1085c9decdfSLorenzo Bianconi }
1095c9decdfSLorenzo Bianconi 
1105c9decdfSLorenzo Bianconi static int
mt76x02_led_set_blink(struct led_classdev * led_cdev,unsigned long * delay_on,unsigned long * delay_off)1115c9decdfSLorenzo Bianconi mt76x02_led_set_blink(struct led_classdev *led_cdev,
1125c9decdfSLorenzo Bianconi 		      unsigned long *delay_on,
1135c9decdfSLorenzo Bianconi 		      unsigned long *delay_off)
1145c9decdfSLorenzo Bianconi {
1153abd46ddSLorenzo Bianconi 	struct mt76_phy *mphy = container_of(led_cdev, struct mt76_phy,
116a00b7910SLorenzo Bianconi 					     leds.cdev);
1175c9decdfSLorenzo Bianconi 	u8 delta_on, delta_off;
1185c9decdfSLorenzo Bianconi 
1195c9decdfSLorenzo Bianconi 	delta_off = max_t(u8, *delay_off / 10, 1);
1205c9decdfSLorenzo Bianconi 	delta_on = max_t(u8, *delay_on / 10, 1);
1215c9decdfSLorenzo Bianconi 
1223abd46ddSLorenzo Bianconi 	mt76x02_led_set_config(mphy, delta_on, delta_off);
1235c9decdfSLorenzo Bianconi 
1245c9decdfSLorenzo Bianconi 	return 0;
1255c9decdfSLorenzo Bianconi }
1265c9decdfSLorenzo Bianconi 
1275c9decdfSLorenzo Bianconi static void
mt76x02_led_set_brightness(struct led_classdev * led_cdev,enum led_brightness brightness)1285c9decdfSLorenzo Bianconi mt76x02_led_set_brightness(struct led_classdev *led_cdev,
1295c9decdfSLorenzo Bianconi 			   enum led_brightness brightness)
1305c9decdfSLorenzo Bianconi {
1313abd46ddSLorenzo Bianconi 	struct mt76_phy *mphy = container_of(led_cdev, struct mt76_phy,
132a00b7910SLorenzo Bianconi 					     leds.cdev);
1335c9decdfSLorenzo Bianconi 
1345c9decdfSLorenzo Bianconi 	if (!brightness)
1353abd46ddSLorenzo Bianconi 		mt76x02_led_set_config(mphy, 0, 0xff);
1365c9decdfSLorenzo Bianconi 	else
1373abd46ddSLorenzo Bianconi 		mt76x02_led_set_config(mphy, 0xff, 0);
1385c9decdfSLorenzo Bianconi }
1395c9decdfSLorenzo Bianconi 
mt76x02_init_device(struct mt76x02_dev * dev)140633f77b5SLorenzo Bianconi int mt76x02_init_device(struct mt76x02_dev *dev)
1415cbace02SLorenzo Bianconi {
1425cbace02SLorenzo Bianconi 	struct ieee80211_hw *hw = mt76_hw(dev);
1435cbace02SLorenzo Bianconi 	struct wiphy *wiphy = hw->wiphy;
1445cbace02SLorenzo Bianconi 
145a782f8bfSLorenzo Bianconi 	INIT_DELAYED_WORK(&dev->mphy.mac_work, mt76x02_mac_work);
1465cbace02SLorenzo Bianconi 
1475cbace02SLorenzo Bianconi 	hw->queues = 4;
1485cbace02SLorenzo Bianconi 	hw->max_rates = 1;
1495cbace02SLorenzo Bianconi 	hw->max_report_rates = 7;
1505cbace02SLorenzo Bianconi 	hw->max_rate_tries = 1;
1515cbace02SLorenzo Bianconi 	hw->extra_tx_headroom = 2;
1525cbace02SLorenzo Bianconi 
15361c51a74SLorenzo Bianconi 	if (mt76_is_usb(&dev->mt76)) {
1545cbace02SLorenzo Bianconi 		hw->extra_tx_headroom += sizeof(struct mt76x02_txwi) +
1555cbace02SLorenzo Bianconi 					 MT_DMA_HDR_LEN;
156f110d1d5SLorenzo Bianconi 		wiphy->iface_combinations = mt76x02u_if_comb;
157f110d1d5SLorenzo Bianconi 		wiphy->n_iface_combinations = ARRAY_SIZE(mt76x02u_if_comb);
1585cbace02SLorenzo Bianconi 	} else {
159c1e0d2beSLorenzo Bianconi 		INIT_DELAYED_WORK(&dev->wdt_work, mt76x02_wdt_work);
160c1e0d2beSLorenzo Bianconi 
161801ccc8aSLorenzo Bianconi 		mt76x02_dfs_init_detector(dev);
162801ccc8aSLorenzo Bianconi 
163801ccc8aSLorenzo Bianconi 		wiphy->reg_notifier = mt76x02_regd_notifier;
1645cbace02SLorenzo Bianconi 		wiphy->iface_combinations = mt76x02_if_comb;
1655cbace02SLorenzo Bianconi 		wiphy->n_iface_combinations = ARRAY_SIZE(mt76x02_if_comb);
166e7173858SFelix Fietkau 
1675c9decdfSLorenzo Bianconi 		/* init led callbacks */
1685c9decdfSLorenzo Bianconi 		if (IS_ENABLED(CONFIG_MT76_LEDS)) {
1693abd46ddSLorenzo Bianconi 			dev->mphy.leds.cdev.brightness_set =
1705c9decdfSLorenzo Bianconi 					mt76x02_led_set_brightness;
1713abd46ddSLorenzo Bianconi 			dev->mphy.leds.cdev.blink_set = mt76x02_led_set_blink;
1725c9decdfSLorenzo Bianconi 		}
1735cbace02SLorenzo Bianconi 	}
1745cbace02SLorenzo Bianconi 
1752bd7f3d2SStanislaw Gruszka 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
1762bd7f3d2SStanislaw Gruszka 
1775cbace02SLorenzo Bianconi 	hw->sta_data_size = sizeof(struct mt76x02_sta);
1785cbace02SLorenzo Bianconi 	hw->vif_data_size = sizeof(struct mt76x02_vif);
1795cbace02SLorenzo Bianconi 
1805cbace02SLorenzo Bianconi 	ieee80211_hw_set(hw, SUPPORTS_HT_CCK_RATES);
181e22d0b89SFelix Fietkau 	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
1827af1ae62SFelix Fietkau 	ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
1835cbace02SLorenzo Bianconi 
1845cbace02SLorenzo Bianconi 	dev->mt76.global_wcid.idx = 255;
1855cbace02SLorenzo Bianconi 	dev->mt76.global_wcid.hw_key_idx = -1;
1865cbace02SLorenzo Bianconi 	dev->slottime = 9;
1875cbace02SLorenzo Bianconi 
1885cbace02SLorenzo Bianconi 	if (is_mt76x2(dev)) {
18996747a51SFelix Fietkau 		dev->mphy.sband_2g.sband.ht_cap.cap |=
1905cbace02SLorenzo Bianconi 				IEEE80211_HT_CAP_LDPC_CODING;
19196747a51SFelix Fietkau 		dev->mphy.sband_5g.sband.ht_cap.cap |=
1925cbace02SLorenzo Bianconi 				IEEE80211_HT_CAP_LDPC_CODING;
193b9027e08SLorenzo Bianconi 		dev->mphy.chainmask = 0x202;
194beaaeb6bSFelix Fietkau 		dev->mphy.antenna_mask = 3;
1955cbace02SLorenzo Bianconi 	} else {
196b9027e08SLorenzo Bianconi 		dev->mphy.chainmask = 0x101;
197beaaeb6bSFelix Fietkau 		dev->mphy.antenna_mask = 1;
1985cbace02SLorenzo Bianconi 	}
199633f77b5SLorenzo Bianconi 
200633f77b5SLorenzo Bianconi 	return 0;
2015cbace02SLorenzo Bianconi }
2025cbace02SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76x02_init_device);
2035cbace02SLorenzo Bianconi 
mt76x02_configure_filter(struct ieee80211_hw * hw,unsigned int changed_flags,unsigned int * total_flags,u64 multicast)204108a4861SStanislaw Gruszka void mt76x02_configure_filter(struct ieee80211_hw *hw,
205108a4861SStanislaw Gruszka 			      unsigned int changed_flags,
206108a4861SStanislaw Gruszka 			      unsigned int *total_flags, u64 multicast)
207108a4861SStanislaw Gruszka {
208d87cf75fSLorenzo Bianconi 	struct mt76x02_dev *dev = hw->priv;
209108a4861SStanislaw Gruszka 	u32 flags = 0;
210108a4861SStanislaw Gruszka 
211108a4861SStanislaw Gruszka #define MT76_FILTER(_flag, _hw) do { \
212108a4861SStanislaw Gruszka 		flags |= *total_flags & FIF_##_flag;			\
213d87cf75fSLorenzo Bianconi 		dev->mt76.rxfilter &= ~(_hw);				\
214d87cf75fSLorenzo Bianconi 		dev->mt76.rxfilter |= !(flags & FIF_##_flag) * (_hw);	\
215108a4861SStanislaw Gruszka 	} while (0)
216108a4861SStanislaw Gruszka 
217d87cf75fSLorenzo Bianconi 	mutex_lock(&dev->mt76.mutex);
218108a4861SStanislaw Gruszka 
219d87cf75fSLorenzo Bianconi 	dev->mt76.rxfilter &= ~MT_RX_FILTR_CFG_OTHER_BSS;
220108a4861SStanislaw Gruszka 
221108a4861SStanislaw Gruszka 	MT76_FILTER(FCSFAIL, MT_RX_FILTR_CFG_CRC_ERR);
222108a4861SStanislaw Gruszka 	MT76_FILTER(PLCPFAIL, MT_RX_FILTR_CFG_PHY_ERR);
223108a4861SStanislaw Gruszka 	MT76_FILTER(CONTROL, MT_RX_FILTR_CFG_ACK |
224108a4861SStanislaw Gruszka 			     MT_RX_FILTR_CFG_CTS |
225108a4861SStanislaw Gruszka 			     MT_RX_FILTR_CFG_CFEND |
226108a4861SStanislaw Gruszka 			     MT_RX_FILTR_CFG_CFACK |
227108a4861SStanislaw Gruszka 			     MT_RX_FILTR_CFG_BA |
228108a4861SStanislaw Gruszka 			     MT_RX_FILTR_CFG_CTRL_RSV);
229108a4861SStanislaw Gruszka 	MT76_FILTER(PSPOLL, MT_RX_FILTR_CFG_PSPOLL);
230108a4861SStanislaw Gruszka 
231108a4861SStanislaw Gruszka 	*total_flags = flags;
232d87cf75fSLorenzo Bianconi 	mt76_wr(dev, MT_RX_FILTR_CFG, dev->mt76.rxfilter);
233108a4861SStanislaw Gruszka 
234d87cf75fSLorenzo Bianconi 	mutex_unlock(&dev->mt76.mutex);
235108a4861SStanislaw Gruszka }
236108a4861SStanislaw Gruszka EXPORT_SYMBOL_GPL(mt76x02_configure_filter);
237108a4861SStanislaw Gruszka 
mt76x02_sta_add(struct mt76_dev * mdev,struct ieee80211_vif * vif,struct ieee80211_sta * sta)238e28487eaSFelix Fietkau int mt76x02_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
239624400e4SStanislaw Gruszka 		    struct ieee80211_sta *sta)
240624400e4SStanislaw Gruszka {
241e28487eaSFelix Fietkau 	struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
242624400e4SStanislaw Gruszka 	struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv;
243624400e4SStanislaw Gruszka 	struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
244624400e4SStanislaw Gruszka 	int idx = 0;
245624400e4SStanislaw Gruszka 
24600496042SFelix Fietkau 	memset(msta, 0, sizeof(*msta));
24700496042SFelix Fietkau 
248238f5d6fSFelix Fietkau 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT76x02_N_WCIDS);
249e28487eaSFelix Fietkau 	if (idx < 0)
250e28487eaSFelix Fietkau 		return -ENOSPC;
251624400e4SStanislaw Gruszka 
252624400e4SStanislaw Gruszka 	msta->vif = mvif;
253624400e4SStanislaw Gruszka 	msta->wcid.sta = 1;
254624400e4SStanislaw Gruszka 	msta->wcid.idx = idx;
255624400e4SStanislaw Gruszka 	msta->wcid.hw_key_idx = -1;
2568d66af49SLorenzo Bianconi 	mt76x02_mac_wcid_setup(dev, idx, mvif->idx, sta->addr);
2578d66af49SLorenzo Bianconi 	mt76x02_mac_wcid_set_drop(dev, idx, false);
258355f8d00SFelix Fietkau 	ewma_pktlen_init(&msta->pktlen);
259624400e4SStanislaw Gruszka 
260624400e4SStanislaw Gruszka 	if (vif->type == NL80211_IFTYPE_AP)
261624400e4SStanislaw Gruszka 		set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags);
262624400e4SStanislaw Gruszka 
263e28487eaSFelix Fietkau 	return 0;
264624400e4SStanislaw Gruszka }
265624400e4SStanislaw Gruszka EXPORT_SYMBOL_GPL(mt76x02_sta_add);
266624400e4SStanislaw Gruszka 
mt76x02_sta_remove(struct mt76_dev * mdev,struct ieee80211_vif * vif,struct ieee80211_sta * sta)267e28487eaSFelix Fietkau void mt76x02_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,
268624400e4SStanislaw Gruszka 			struct ieee80211_sta *sta)
269624400e4SStanislaw Gruszka {
270e28487eaSFelix Fietkau 	struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
271723b90dcSFelix Fietkau 	struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
272723b90dcSFelix Fietkau 	int idx = wcid->idx;
273624400e4SStanislaw Gruszka 
2748d66af49SLorenzo Bianconi 	mt76x02_mac_wcid_set_drop(dev, idx, true);
2758d66af49SLorenzo Bianconi 	mt76x02_mac_wcid_setup(dev, idx, 0, NULL);
276624400e4SStanislaw Gruszka }
277624400e4SStanislaw Gruszka EXPORT_SYMBOL_GPL(mt76x02_sta_remove);
278624400e4SStanislaw Gruszka 
279f9a043c5SStanislaw Gruszka static void
mt76x02_vif_init(struct mt76x02_dev * dev,struct ieee80211_vif * vif,unsigned int idx)280f9a043c5SStanislaw Gruszka mt76x02_vif_init(struct mt76x02_dev *dev, struct ieee80211_vif *vif,
281cab12953SStanislaw Gruszka 		 unsigned int idx)
282cab12953SStanislaw Gruszka {
283cab12953SStanislaw Gruszka 	struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
28454f1bf8aSFelix Fietkau 	struct mt76_txq *mtxq;
285cab12953SStanislaw Gruszka 
28600496042SFelix Fietkau 	memset(mvif, 0, sizeof(*mvif));
28700496042SFelix Fietkau 
288cab12953SStanislaw Gruszka 	mvif->idx = idx;
289cab12953SStanislaw Gruszka 	mvif->group_wcid.idx = MT_VIF_WCID(idx);
290cab12953SStanislaw Gruszka 	mvif->group_wcid.hw_key_idx = -1;
291*d2defcddSFelix Fietkau 	mt76_wcid_init(&mvif->group_wcid);
292bd1e3e7bSLorenzo Bianconi 
29354f1bf8aSFelix Fietkau 	mtxq = (struct mt76_txq *)vif->txq->drv_priv;
29451fb1278SFelix Fietkau 	rcu_assign_pointer(dev->mt76.wcid[MT_VIF_WCID(idx)], &mvif->group_wcid);
29551fb1278SFelix Fietkau 	mtxq->wcid = MT_VIF_WCID(idx);
296cab12953SStanislaw Gruszka }
297cab12953SStanislaw Gruszka 
298212926ebSStanislaw Gruszka int
mt76x02_add_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)299212926ebSStanislaw Gruszka mt76x02_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
300212926ebSStanislaw Gruszka {
301d87cf75fSLorenzo Bianconi 	struct mt76x02_dev *dev = hw->priv;
302212926ebSStanislaw Gruszka 	unsigned int idx = 0;
303212926ebSStanislaw Gruszka 
3043fd0824aSFelix Fietkau 	/* Allow to change address in HW if we create first interface. */
3052ab33b8dSFelix Fietkau 	if (!dev->mt76.vif_mask &&
30698df2baeSLorenzo Bianconi 	    (((vif->addr[0] ^ dev->mphy.macaddr[0]) & ~GENMASK(4, 1)) ||
30798df2baeSLorenzo Bianconi 	     memcmp(vif->addr + 1, dev->mphy.macaddr + 1, ETH_ALEN - 1)))
3083fd0824aSFelix Fietkau 		mt76x02_mac_setaddr(dev, vif->addr);
3093fd0824aSFelix Fietkau 
310212926ebSStanislaw Gruszka 	if (vif->addr[0] & BIT(1))
31198df2baeSLorenzo Bianconi 		idx = 1 + (((dev->mphy.macaddr[0] ^ vif->addr[0]) >> 2) & 7);
312212926ebSStanislaw Gruszka 
313212926ebSStanislaw Gruszka 	/*
314212926ebSStanislaw Gruszka 	 * Client mode typically only has one configurable BSSID register,
315212926ebSStanislaw Gruszka 	 * which is used for bssidx=0. This is linked to the MAC address.
316212926ebSStanislaw Gruszka 	 * Since mac80211 allows changing interface types, and we cannot
317212926ebSStanislaw Gruszka 	 * force the use of the primary MAC address for a station mode
318212926ebSStanislaw Gruszka 	 * interface, we need some other way of configuring a per-interface
319212926ebSStanislaw Gruszka 	 * remote BSSID.
320212926ebSStanislaw Gruszka 	 * The hardware provides an AP-Client feature, where bssidx 0-7 are
321212926ebSStanislaw Gruszka 	 * used for AP mode and bssidx 8-15 for client mode.
322212926ebSStanislaw Gruszka 	 * We shift the station interface bss index by 8 to force the
323212926ebSStanislaw Gruszka 	 * hardware to recognize the BSSID.
324212926ebSStanislaw Gruszka 	 * The resulting bssidx mismatch for unicast frames is ignored by hw.
325212926ebSStanislaw Gruszka 	 */
326212926ebSStanislaw Gruszka 	if (vif->type == NL80211_IFTYPE_STATION)
327212926ebSStanislaw Gruszka 		idx += 8;
328212926ebSStanislaw Gruszka 
3297d288640SMarkus Theil 	/* vif is already set or idx is 8 for AP/Mesh/... */
330b619e013SEvelyn Tsai 	if (dev->mt76.vif_mask & BIT_ULL(idx) ||
3317d288640SMarkus Theil 	    (vif->type != NL80211_IFTYPE_STATION && idx > 7))
33206662264SStanislaw Gruszka 		return -EBUSY;
33306662264SStanislaw Gruszka 
334b619e013SEvelyn Tsai 	dev->mt76.vif_mask |= BIT_ULL(idx);
33506662264SStanislaw Gruszka 
336212926ebSStanislaw Gruszka 	mt76x02_vif_init(dev, vif, idx);
337212926ebSStanislaw Gruszka 	return 0;
338212926ebSStanislaw Gruszka }
339212926ebSStanislaw Gruszka EXPORT_SYMBOL_GPL(mt76x02_add_interface);
340212926ebSStanislaw Gruszka 
mt76x02_remove_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)3410cd47baeSStanislaw Gruszka void mt76x02_remove_interface(struct ieee80211_hw *hw,
3420cd47baeSStanislaw Gruszka 			      struct ieee80211_vif *vif)
3430cd47baeSStanislaw Gruszka {
344d87cf75fSLorenzo Bianconi 	struct mt76x02_dev *dev = hw->priv;
34506662264SStanislaw Gruszka 	struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
3460cd47baeSStanislaw Gruszka 
347b619e013SEvelyn Tsai 	dev->mt76.vif_mask &= ~BIT_ULL(mvif->idx);
34851fb1278SFelix Fietkau 	rcu_assign_pointer(dev->mt76.wcid[mvif->group_wcid.idx], NULL);
349*d2defcddSFelix Fietkau 	mt76_wcid_cleanup(&dev->mt76, &mvif->group_wcid);
3500cd47baeSStanislaw Gruszka }
3510cd47baeSStanislaw Gruszka EXPORT_SYMBOL_GPL(mt76x02_remove_interface);
3520cd47baeSStanislaw Gruszka 
mt76x02_ampdu_action(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_ampdu_params * params)35322c575c4SStanislaw Gruszka int mt76x02_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
35422c575c4SStanislaw Gruszka 			 struct ieee80211_ampdu_params *params)
35522c575c4SStanislaw Gruszka {
35622c575c4SStanislaw Gruszka 	enum ieee80211_ampdu_mlme_action action = params->action;
35722c575c4SStanislaw Gruszka 	struct ieee80211_sta *sta = params->sta;
358d87cf75fSLorenzo Bianconi 	struct mt76x02_dev *dev = hw->priv;
35922c575c4SStanislaw Gruszka 	struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv;
36022c575c4SStanislaw Gruszka 	struct ieee80211_txq *txq = sta->txq[params->tid];
36122c575c4SStanislaw Gruszka 	u16 tid = params->tid;
3625eedd2a5SStanislaw Gruszka 	u16 ssn = params->ssn;
36322c575c4SStanislaw Gruszka 	struct mt76_txq *mtxq;
36405d6c8cfSMarkus Theil 	int ret = 0;
36522c575c4SStanislaw Gruszka 
36622c575c4SStanislaw Gruszka 	if (!txq)
36722c575c4SStanislaw Gruszka 		return -EINVAL;
36822c575c4SStanislaw Gruszka 
36922c575c4SStanislaw Gruszka 	mtxq = (struct mt76_txq *)txq->drv_priv;
37022c575c4SStanislaw Gruszka 
3711a817fa7SFelix Fietkau 	mutex_lock(&dev->mt76.mutex);
37222c575c4SStanislaw Gruszka 	switch (action) {
37322c575c4SStanislaw Gruszka 	case IEEE80211_AMPDU_RX_START:
374d87cf75fSLorenzo Bianconi 		mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid,
3755eedd2a5SStanislaw Gruszka 				   ssn, params->buf_size);
376d87cf75fSLorenzo Bianconi 		mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid));
37722c575c4SStanislaw Gruszka 		break;
37822c575c4SStanislaw Gruszka 	case IEEE80211_AMPDU_RX_STOP:
379d87cf75fSLorenzo Bianconi 		mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid);
380d87cf75fSLorenzo Bianconi 		mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4,
381d87cf75fSLorenzo Bianconi 			   BIT(16 + tid));
38222c575c4SStanislaw Gruszka 		break;
38322c575c4SStanislaw Gruszka 	case IEEE80211_AMPDU_TX_OPERATIONAL:
38422c575c4SStanislaw Gruszka 		mtxq->aggr = true;
38522c575c4SStanislaw Gruszka 		mtxq->send_bar = false;
38622c575c4SStanislaw Gruszka 		ieee80211_send_bar(vif, sta->addr, tid, mtxq->agg_ssn);
38722c575c4SStanislaw Gruszka 		break;
38822c575c4SStanislaw Gruszka 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
38922c575c4SStanislaw Gruszka 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
39022c575c4SStanislaw Gruszka 		mtxq->aggr = false;
39122c575c4SStanislaw Gruszka 		break;
39222c575c4SStanislaw Gruszka 	case IEEE80211_AMPDU_TX_START:
3935eedd2a5SStanislaw Gruszka 		mtxq->agg_ssn = IEEE80211_SN_TO_SEQ(ssn);
39405d6c8cfSMarkus Theil 		ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;
39505d6c8cfSMarkus Theil 		break;
39622c575c4SStanislaw Gruszka 	case IEEE80211_AMPDU_TX_STOP_CONT:
39722c575c4SStanislaw Gruszka 		mtxq->aggr = false;
39822c575c4SStanislaw Gruszka 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
39922c575c4SStanislaw Gruszka 		break;
40022c575c4SStanislaw Gruszka 	}
4011a817fa7SFelix Fietkau 	mutex_unlock(&dev->mt76.mutex);
40222c575c4SStanislaw Gruszka 
40305d6c8cfSMarkus Theil 	return ret;
40422c575c4SStanislaw Gruszka }
40522c575c4SStanislaw Gruszka EXPORT_SYMBOL_GPL(mt76x02_ampdu_action);
40622c575c4SStanislaw Gruszka 
mt76x02_set_key(struct ieee80211_hw * hw,enum set_key_cmd cmd,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key)40760c26859SStanislaw Gruszka int mt76x02_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
40860c26859SStanislaw Gruszka 		    struct ieee80211_vif *vif, struct ieee80211_sta *sta,
40960c26859SStanislaw Gruszka 		    struct ieee80211_key_conf *key)
41060c26859SStanislaw Gruszka {
411d87cf75fSLorenzo Bianconi 	struct mt76x02_dev *dev = hw->priv;
41260c26859SStanislaw Gruszka 	struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
41360c26859SStanislaw Gruszka 	struct mt76x02_sta *msta;
41460c26859SStanislaw Gruszka 	struct mt76_wcid *wcid;
41560c26859SStanislaw Gruszka 	int idx = key->keyidx;
41660c26859SStanislaw Gruszka 	int ret;
41760c26859SStanislaw Gruszka 
41860c26859SStanislaw Gruszka 	/* fall back to sw encryption for unsupported ciphers */
41960c26859SStanislaw Gruszka 	switch (key->cipher) {
42060c26859SStanislaw Gruszka 	case WLAN_CIPHER_SUITE_WEP40:
42160c26859SStanislaw Gruszka 	case WLAN_CIPHER_SUITE_WEP104:
42260c26859SStanislaw Gruszka 	case WLAN_CIPHER_SUITE_TKIP:
42360c26859SStanislaw Gruszka 	case WLAN_CIPHER_SUITE_CCMP:
42460c26859SStanislaw Gruszka 		break;
42560c26859SStanislaw Gruszka 	default:
42660c26859SStanislaw Gruszka 		return -EOPNOTSUPP;
42760c26859SStanislaw Gruszka 	}
42860c26859SStanislaw Gruszka 
42960c26859SStanislaw Gruszka 	/*
43060c26859SStanislaw Gruszka 	 * The hardware does not support per-STA RX GTK, fall back
43160c26859SStanislaw Gruszka 	 * to software mode for these.
43260c26859SStanislaw Gruszka 	 */
43360c26859SStanislaw Gruszka 	if ((vif->type == NL80211_IFTYPE_ADHOC ||
43460c26859SStanislaw Gruszka 	     vif->type == NL80211_IFTYPE_MESH_POINT) &&
43560c26859SStanislaw Gruszka 	    (key->cipher == WLAN_CIPHER_SUITE_TKIP ||
43660c26859SStanislaw Gruszka 	     key->cipher == WLAN_CIPHER_SUITE_CCMP) &&
43760c26859SStanislaw Gruszka 	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
43860c26859SStanislaw Gruszka 		return -EOPNOTSUPP;
43960c26859SStanislaw Gruszka 
440b98558e2SStanislaw Gruszka 	/*
441b98558e2SStanislaw Gruszka 	 * In USB AP mode, broadcast/multicast frames are setup in beacon
442b98558e2SStanislaw Gruszka 	 * data registers and sent via HW beacons engine, they require to
443b98558e2SStanislaw Gruszka 	 * be already encrypted.
444b98558e2SStanislaw Gruszka 	 */
44561c51a74SLorenzo Bianconi 	if (mt76_is_usb(&dev->mt76) &&
446b98558e2SStanislaw Gruszka 	    vif->type == NL80211_IFTYPE_AP &&
447b98558e2SStanislaw Gruszka 	    !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
448b98558e2SStanislaw Gruszka 		return -EOPNOTSUPP;
449b98558e2SStanislaw Gruszka 
4504b36cc6bSDavid Bauer 	/* MT76x0 GTK offloading does not work with more than one VIF */
4514b36cc6bSDavid Bauer 	if (is_mt76x0(dev) && !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
4524b36cc6bSDavid Bauer 		return -EOPNOTSUPP;
4534b36cc6bSDavid Bauer 
45460c26859SStanislaw Gruszka 	msta = sta ? (struct mt76x02_sta *)sta->drv_priv : NULL;
45560c26859SStanislaw Gruszka 	wcid = msta ? &msta->wcid : &mvif->group_wcid;
45660c26859SStanislaw Gruszka 
457e6db67faSFelix Fietkau 	if (cmd != SET_KEY) {
45860c26859SStanislaw Gruszka 		if (idx == wcid->hw_key_idx) {
45960c26859SStanislaw Gruszka 			wcid->hw_key_idx = -1;
460f2f6a47bSFelix Fietkau 			wcid->sw_iv = false;
46160c26859SStanislaw Gruszka 		}
46260c26859SStanislaw Gruszka 
463e6db67faSFelix Fietkau 		return 0;
464e6db67faSFelix Fietkau 	}
465e6db67faSFelix Fietkau 
466e6db67faSFelix Fietkau 	key->hw_key_idx = wcid->idx;
467e6db67faSFelix Fietkau 	wcid->hw_key_idx = idx;
468e6db67faSFelix Fietkau 	if (key->flags & IEEE80211_KEY_FLAG_RX_MGMT) {
469e6db67faSFelix Fietkau 		key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
470e6db67faSFelix Fietkau 		wcid->sw_iv = true;
47160c26859SStanislaw Gruszka 	}
472d87cf75fSLorenzo Bianconi 	mt76_wcid_key_setup(&dev->mt76, wcid, key);
47360c26859SStanislaw Gruszka 
47460c26859SStanislaw Gruszka 	if (!msta) {
47560c26859SStanislaw Gruszka 		if (key || wcid->hw_key_idx == idx) {
4768d66af49SLorenzo Bianconi 			ret = mt76x02_mac_wcid_set_key(dev, wcid->idx, key);
47760c26859SStanislaw Gruszka 			if (ret)
47860c26859SStanislaw Gruszka 				return ret;
47960c26859SStanislaw Gruszka 		}
48060c26859SStanislaw Gruszka 
4818d66af49SLorenzo Bianconi 		return mt76x02_mac_shared_key_setup(dev, mvif->idx, idx, key);
48260c26859SStanislaw Gruszka 	}
48360c26859SStanislaw Gruszka 
4848d66af49SLorenzo Bianconi 	return mt76x02_mac_wcid_set_key(dev, msta->wcid.idx, key);
48560c26859SStanislaw Gruszka }
48660c26859SStanislaw Gruszka EXPORT_SYMBOL_GPL(mt76x02_set_key);
48760c26859SStanislaw Gruszka 
mt76x02_conf_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,unsigned int link_id,u16 queue,const struct ieee80211_tx_queue_params * params)48810337263SStanislaw Gruszka int mt76x02_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
489b3e2130bSJohannes Berg 		    unsigned int link_id, u16 queue,
490b3e2130bSJohannes Berg 		    const struct ieee80211_tx_queue_params *params)
49110337263SStanislaw Gruszka {
492d87cf75fSLorenzo Bianconi 	struct mt76x02_dev *dev = hw->priv;
49310337263SStanislaw Gruszka 	u8 cw_min = 5, cw_max = 10, qid;
49410337263SStanislaw Gruszka 	u32 val;
49510337263SStanislaw Gruszka 
49691990519SLorenzo Bianconi 	qid = dev->mphy.q_tx[queue]->hw_idx;
49710337263SStanislaw Gruszka 
49810337263SStanislaw Gruszka 	if (params->cw_min)
49910337263SStanislaw Gruszka 		cw_min = fls(params->cw_min);
50010337263SStanislaw Gruszka 	if (params->cw_max)
50110337263SStanislaw Gruszka 		cw_max = fls(params->cw_max);
50210337263SStanislaw Gruszka 
50310337263SStanislaw Gruszka 	val = FIELD_PREP(MT_EDCA_CFG_TXOP, params->txop) |
50410337263SStanislaw Gruszka 	      FIELD_PREP(MT_EDCA_CFG_AIFSN, params->aifs) |
50510337263SStanislaw Gruszka 	      FIELD_PREP(MT_EDCA_CFG_CWMIN, cw_min) |
50610337263SStanislaw Gruszka 	      FIELD_PREP(MT_EDCA_CFG_CWMAX, cw_max);
507d87cf75fSLorenzo Bianconi 	mt76_wr(dev, MT_EDCA_CFG_AC(qid), val);
50810337263SStanislaw Gruszka 
509d87cf75fSLorenzo Bianconi 	val = mt76_rr(dev, MT_WMM_TXOP(qid));
51010337263SStanislaw Gruszka 	val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(qid));
51110337263SStanislaw Gruszka 	val |= params->txop << MT_WMM_TXOP_SHIFT(qid);
512d87cf75fSLorenzo Bianconi 	mt76_wr(dev, MT_WMM_TXOP(qid), val);
51310337263SStanislaw Gruszka 
514d87cf75fSLorenzo Bianconi 	val = mt76_rr(dev, MT_WMM_AIFSN);
51510337263SStanislaw Gruszka 	val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(qid));
51610337263SStanislaw Gruszka 	val |= params->aifs << MT_WMM_AIFSN_SHIFT(qid);
517d87cf75fSLorenzo Bianconi 	mt76_wr(dev, MT_WMM_AIFSN, val);
51810337263SStanislaw Gruszka 
519d87cf75fSLorenzo Bianconi 	val = mt76_rr(dev, MT_WMM_CWMIN);
52010337263SStanislaw Gruszka 	val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(qid));
52110337263SStanislaw Gruszka 	val |= cw_min << MT_WMM_CWMIN_SHIFT(qid);
522d87cf75fSLorenzo Bianconi 	mt76_wr(dev, MT_WMM_CWMIN, val);
52310337263SStanislaw Gruszka 
524d87cf75fSLorenzo Bianconi 	val = mt76_rr(dev, MT_WMM_CWMAX);
52510337263SStanislaw Gruszka 	val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(qid));
52610337263SStanislaw Gruszka 	val |= cw_max << MT_WMM_CWMAX_SHIFT(qid);
527d87cf75fSLorenzo Bianconi 	mt76_wr(dev, MT_WMM_CWMAX, val);
52810337263SStanislaw Gruszka 
52910337263SStanislaw Gruszka 	return 0;
53010337263SStanislaw Gruszka }
53110337263SStanislaw Gruszka EXPORT_SYMBOL_GPL(mt76x02_conf_tx);
53210337263SStanislaw Gruszka 
mt76x02_set_tx_ackto(struct mt76x02_dev * dev)53336704051SLorenzo Bianconi void mt76x02_set_tx_ackto(struct mt76x02_dev *dev)
53436704051SLorenzo Bianconi {
53536704051SLorenzo Bianconi 	u8 ackto, sifs, slottime = dev->slottime;
53636704051SLorenzo Bianconi 
53736704051SLorenzo Bianconi 	/* As defined by IEEE 802.11-2007 17.3.8.6 */
53836704051SLorenzo Bianconi 	slottime += 3 * dev->coverage_class;
53936704051SLorenzo Bianconi 	mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG,
54036704051SLorenzo Bianconi 		       MT_BKOFF_SLOT_CFG_SLOTTIME, slottime);
54136704051SLorenzo Bianconi 
54236704051SLorenzo Bianconi 	sifs = mt76_get_field(dev, MT_XIFS_TIME_CFG,
54336704051SLorenzo Bianconi 			      MT_XIFS_TIME_CFG_OFDM_SIFS);
54436704051SLorenzo Bianconi 
54536704051SLorenzo Bianconi 	ackto = slottime + sifs;
54636704051SLorenzo Bianconi 	mt76_rmw_field(dev, MT_TX_TIMEOUT_CFG,
54736704051SLorenzo Bianconi 		       MT_TX_TIMEOUT_CFG_ACKTO, ackto);
54836704051SLorenzo Bianconi }
54936704051SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76x02_set_tx_ackto);
55036704051SLorenzo Bianconi 
mt76x02_set_coverage_class(struct ieee80211_hw * hw,s16 coverage_class)55136704051SLorenzo Bianconi void mt76x02_set_coverage_class(struct ieee80211_hw *hw,
55236704051SLorenzo Bianconi 				s16 coverage_class)
55336704051SLorenzo Bianconi {
55436704051SLorenzo Bianconi 	struct mt76x02_dev *dev = hw->priv;
55536704051SLorenzo Bianconi 
55636704051SLorenzo Bianconi 	mutex_lock(&dev->mt76.mutex);
557bae76a1eSLorenzo Bianconi 	dev->coverage_class = max_t(s16, coverage_class, 0);
55836704051SLorenzo Bianconi 	mt76x02_set_tx_ackto(dev);
55936704051SLorenzo Bianconi 	mutex_unlock(&dev->mt76.mutex);
56036704051SLorenzo Bianconi }
56136704051SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76x02_set_coverage_class);
56236704051SLorenzo Bianconi 
mt76x02_set_rts_threshold(struct ieee80211_hw * hw,u32 val)563317ed42bSLorenzo Bianconi int mt76x02_set_rts_threshold(struct ieee80211_hw *hw, u32 val)
564317ed42bSLorenzo Bianconi {
565317ed42bSLorenzo Bianconi 	struct mt76x02_dev *dev = hw->priv;
566317ed42bSLorenzo Bianconi 
567317ed42bSLorenzo Bianconi 	if (val != ~0 && val > 0xffff)
568317ed42bSLorenzo Bianconi 		return -EINVAL;
569317ed42bSLorenzo Bianconi 
570074b145aSKalle Valo 	mutex_lock(&dev->mt76.mutex);
57120ce270eSStanislaw Gruszka 	mt76x02_mac_set_rts_thresh(dev, val);
572074b145aSKalle Valo 	mutex_unlock(&dev->mt76.mutex);
573317ed42bSLorenzo Bianconi 
574317ed42bSLorenzo Bianconi 	return 0;
575317ed42bSLorenzo Bianconi }
576317ed42bSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76x02_set_rts_threshold);
577317ed42bSLorenzo Bianconi 
mt76x02_sta_rate_tbl_update(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)5785327b5eaSStanislaw Gruszka void mt76x02_sta_rate_tbl_update(struct ieee80211_hw *hw,
5795327b5eaSStanislaw Gruszka 				 struct ieee80211_vif *vif,
5805327b5eaSStanislaw Gruszka 				 struct ieee80211_sta *sta)
5815327b5eaSStanislaw Gruszka {
5828d66af49SLorenzo Bianconi 	struct mt76x02_dev *dev = hw->priv;
5835327b5eaSStanislaw Gruszka 	struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv;
5845327b5eaSStanislaw Gruszka 	struct ieee80211_sta_rates *rates = rcu_dereference(sta->rates);
5855327b5eaSStanislaw Gruszka 	struct ieee80211_tx_rate rate = {};
5865327b5eaSStanislaw Gruszka 
5875327b5eaSStanislaw Gruszka 	if (!rates)
5885327b5eaSStanislaw Gruszka 		return;
5895327b5eaSStanislaw Gruszka 
5905327b5eaSStanislaw Gruszka 	rate.idx = rates->rate[0].idx;
5915327b5eaSStanislaw Gruszka 	rate.flags = rates->rate[0].flags;
5925327b5eaSStanislaw Gruszka 	mt76x02_mac_wcid_set_rate(dev, &msta->wcid, &rate);
5935327b5eaSStanislaw Gruszka }
5945327b5eaSStanislaw Gruszka EXPORT_SYMBOL_GPL(mt76x02_sta_rate_tbl_update);
5955327b5eaSStanislaw Gruszka 
mt76x02_remove_hdr_pad(struct sk_buff * skb,int len)5960e59cba8SStanislaw Gruszka void mt76x02_remove_hdr_pad(struct sk_buff *skb, int len)
5970e59cba8SStanislaw Gruszka {
5980e59cba8SStanislaw Gruszka 	int hdrlen;
5990e59cba8SStanislaw Gruszka 
6000e59cba8SStanislaw Gruszka 	if (!len)
6010e59cba8SStanislaw Gruszka 		return;
6020e59cba8SStanislaw Gruszka 
6030e59cba8SStanislaw Gruszka 	hdrlen = ieee80211_get_hdrlen_from_skb(skb);
6040e59cba8SStanislaw Gruszka 	memmove(skb->data + len, skb->data, hdrlen);
6050e59cba8SStanislaw Gruszka 	skb_pull(skb, len);
6060e59cba8SStanislaw Gruszka }
6070e59cba8SStanislaw Gruszka EXPORT_SYMBOL_GPL(mt76x02_remove_hdr_pad);
6080e59cba8SStanislaw Gruszka 
mt76x02_sw_scan_complete(struct ieee80211_hw * hw,struct ieee80211_vif * vif)609c2756a1cSLorenzo Bianconi void mt76x02_sw_scan_complete(struct ieee80211_hw *hw,
610c2756a1cSLorenzo Bianconi 			      struct ieee80211_vif *vif)
611c2756a1cSLorenzo Bianconi {
612c2756a1cSLorenzo Bianconi 	struct mt76x02_dev *dev = hw->priv;
613c2756a1cSLorenzo Bianconi 
614011849e0SFelix Fietkau 	clear_bit(MT76_SCANNING, &dev->mphy.state);
6154784a3ccSStanislaw Gruszka 	if (dev->cal.gain_init_done) {
6164784a3ccSStanislaw Gruszka 		/* Restore AGC gain and resume calibration after scanning. */
6174784a3ccSStanislaw Gruszka 		dev->cal.low_gain = -1;
618f1b8ee35SStanislaw Gruszka 		ieee80211_queue_delayed_work(hw, &dev->cal_work, 0);
619c2756a1cSLorenzo Bianconi 	}
6204784a3ccSStanislaw Gruszka }
621c2756a1cSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76x02_sw_scan_complete);
622c2756a1cSLorenzo Bianconi 
mt76x02_sta_ps(struct mt76_dev * mdev,struct ieee80211_sta * sta,bool ps)623f7c8a0f2SLorenzo Bianconi void mt76x02_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta,
624f7c8a0f2SLorenzo Bianconi 		    bool ps)
625f7c8a0f2SLorenzo Bianconi {
626f7c8a0f2SLorenzo Bianconi 	struct mt76x02_dev *dev = container_of(mdev, struct mt76x02_dev, mt76);
627f7c8a0f2SLorenzo Bianconi 	struct mt76x02_sta *msta = (struct mt76x02_sta *)sta->drv_priv;
628f7c8a0f2SLorenzo Bianconi 	int idx = msta->wcid.idx;
629f7c8a0f2SLorenzo Bianconi 
63091990519SLorenzo Bianconi 	mt76_stop_tx_queues(&dev->mphy, sta, true);
63161c51a74SLorenzo Bianconi 	if (mt76_is_mmio(mdev))
632f7c8a0f2SLorenzo Bianconi 		mt76x02_mac_wcid_set_drop(dev, idx, ps);
633f7c8a0f2SLorenzo Bianconi }
634f7c8a0f2SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76x02_sta_ps);
635f7c8a0f2SLorenzo Bianconi 
mt76x02_bss_info_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * info,u64 changed)636cc726268SLorenzo Bianconi void mt76x02_bss_info_changed(struct ieee80211_hw *hw,
637cc726268SLorenzo Bianconi 			      struct ieee80211_vif *vif,
638cc726268SLorenzo Bianconi 			      struct ieee80211_bss_conf *info,
6397b7090b4SJohannes Berg 			      u64 changed)
640cc726268SLorenzo Bianconi {
641cc726268SLorenzo Bianconi 	struct mt76x02_vif *mvif = (struct mt76x02_vif *)vif->drv_priv;
642cc726268SLorenzo Bianconi 	struct mt76x02_dev *dev = hw->priv;
643cc726268SLorenzo Bianconi 
644cc726268SLorenzo Bianconi 	mutex_lock(&dev->mt76.mutex);
645cc726268SLorenzo Bianconi 
646cc726268SLorenzo Bianconi 	if (changed & BSS_CHANGED_BSSID)
647cc726268SLorenzo Bianconi 		mt76x02_mac_set_bssid(dev, mvif->idx, info->bssid);
648cc726268SLorenzo Bianconi 
64926a7b547SStanislaw Gruszka 	if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT)
65026a7b547SStanislaw Gruszka 		mt76x02_mac_set_tx_protection(dev, info->use_cts_prot,
65126a7b547SStanislaw Gruszka 					      info->ht_operation_mode);
65226a7b547SStanislaw Gruszka 
653cc726268SLorenzo Bianconi 	if (changed & BSS_CHANGED_BEACON_INT) {
654cc726268SLorenzo Bianconi 		mt76_rmw_field(dev, MT_BEACON_TIME_CFG,
655cc726268SLorenzo Bianconi 			       MT_BEACON_TIME_CFG_INTVAL,
656cc726268SLorenzo Bianconi 			       info->beacon_int << 4);
6573041c445SLorenzo Bianconi 		dev->mt76.beacon_int = info->beacon_int;
658cc726268SLorenzo Bianconi 	}
659cc726268SLorenzo Bianconi 
660ed55c9b9SFelix Fietkau 	if (changed & BSS_CHANGED_BEACON_ENABLED)
661ed55c9b9SFelix Fietkau 		mt76x02_mac_set_beacon_enable(dev, vif, info->enable_beacon);
662ed55c9b9SFelix Fietkau 
663cc726268SLorenzo Bianconi 	if (changed & BSS_CHANGED_ERP_PREAMBLE)
664cc726268SLorenzo Bianconi 		mt76x02_mac_set_short_preamble(dev, info->use_short_preamble);
665cc726268SLorenzo Bianconi 
666cc726268SLorenzo Bianconi 	if (changed & BSS_CHANGED_ERP_SLOT) {
667cc726268SLorenzo Bianconi 		int slottime = info->use_short_slot ? 9 : 20;
668cc726268SLorenzo Bianconi 
669cc726268SLorenzo Bianconi 		dev->slottime = slottime;
670cc726268SLorenzo Bianconi 		mt76x02_set_tx_ackto(dev);
671cc726268SLorenzo Bianconi 	}
672cc726268SLorenzo Bianconi 
673cc726268SLorenzo Bianconi 	mutex_unlock(&dev->mt76.mutex);
674cc726268SLorenzo Bianconi }
675cc726268SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76x02_bss_info_changed);
676cc726268SLorenzo Bianconi 
mt76x02_config_mac_addr_list(struct mt76x02_dev * dev)677269906acSLorenzo Bianconi void mt76x02_config_mac_addr_list(struct mt76x02_dev *dev)
678269906acSLorenzo Bianconi {
679269906acSLorenzo Bianconi 	struct ieee80211_hw *hw = mt76_hw(dev);
680269906acSLorenzo Bianconi 	struct wiphy *wiphy = hw->wiphy;
681269906acSLorenzo Bianconi 	int i;
682269906acSLorenzo Bianconi 
683269906acSLorenzo Bianconi 	for (i = 0; i < ARRAY_SIZE(dev->macaddr_list); i++) {
684269906acSLorenzo Bianconi 		u8 *addr = dev->macaddr_list[i].addr;
685269906acSLorenzo Bianconi 
68698df2baeSLorenzo Bianconi 		memcpy(addr, dev->mphy.macaddr, ETH_ALEN);
687269906acSLorenzo Bianconi 
688269906acSLorenzo Bianconi 		if (!i)
689269906acSLorenzo Bianconi 			continue;
690269906acSLorenzo Bianconi 
691269906acSLorenzo Bianconi 		addr[0] |= BIT(1);
692269906acSLorenzo Bianconi 		addr[0] ^= ((i - 1) << 2);
693269906acSLorenzo Bianconi 	}
694269906acSLorenzo Bianconi 	wiphy->addresses = dev->macaddr_list;
695269906acSLorenzo Bianconi 	wiphy->n_addresses = ARRAY_SIZE(dev->macaddr_list);
696269906acSLorenzo Bianconi }
697269906acSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76x02_config_mac_addr_list);
698a6daf796SLorenzo Bianconi 
699108a4861SStanislaw Gruszka MODULE_LICENSE("Dual BSD/GPL");
700