xref: /linux/drivers/net/wireless/mediatek/mt76/mac80211.c (revision 2c2bdd2372afcfcf24fe11c65ebe3361b7e1cd9f)
10e3d6777SRyder Lee // SPDX-License-Identifier: ISC
217f1de56SFelix Fietkau /*
317f1de56SFelix Fietkau  * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
417f1de56SFelix Fietkau  */
5781eef5bSFelix Fietkau #include <linux/sched.h>
617f1de56SFelix Fietkau #include <linux/of.h>
717f1de56SFelix Fietkau #include "mt76.h"
817f1de56SFelix Fietkau 
917f1de56SFelix Fietkau #define CHAN2G(_idx, _freq) {			\
1017f1de56SFelix Fietkau 	.band = NL80211_BAND_2GHZ,		\
1117f1de56SFelix Fietkau 	.center_freq = (_freq),			\
1217f1de56SFelix Fietkau 	.hw_value = (_idx),			\
1317f1de56SFelix Fietkau 	.max_power = 30,			\
1417f1de56SFelix Fietkau }
1517f1de56SFelix Fietkau 
1617f1de56SFelix Fietkau #define CHAN5G(_idx, _freq) {			\
1717f1de56SFelix Fietkau 	.band = NL80211_BAND_5GHZ,		\
1817f1de56SFelix Fietkau 	.center_freq = (_freq),			\
1917f1de56SFelix Fietkau 	.hw_value = (_idx),			\
2017f1de56SFelix Fietkau 	.max_power = 30,			\
2117f1de56SFelix Fietkau }
2217f1de56SFelix Fietkau 
2317f1de56SFelix Fietkau static const struct ieee80211_channel mt76_channels_2ghz[] = {
2417f1de56SFelix Fietkau 	CHAN2G(1, 2412),
2517f1de56SFelix Fietkau 	CHAN2G(2, 2417),
2617f1de56SFelix Fietkau 	CHAN2G(3, 2422),
2717f1de56SFelix Fietkau 	CHAN2G(4, 2427),
2817f1de56SFelix Fietkau 	CHAN2G(5, 2432),
2917f1de56SFelix Fietkau 	CHAN2G(6, 2437),
3017f1de56SFelix Fietkau 	CHAN2G(7, 2442),
3117f1de56SFelix Fietkau 	CHAN2G(8, 2447),
3217f1de56SFelix Fietkau 	CHAN2G(9, 2452),
3317f1de56SFelix Fietkau 	CHAN2G(10, 2457),
3417f1de56SFelix Fietkau 	CHAN2G(11, 2462),
3517f1de56SFelix Fietkau 	CHAN2G(12, 2467),
3617f1de56SFelix Fietkau 	CHAN2G(13, 2472),
3717f1de56SFelix Fietkau 	CHAN2G(14, 2484),
3817f1de56SFelix Fietkau };
3917f1de56SFelix Fietkau 
4017f1de56SFelix Fietkau static const struct ieee80211_channel mt76_channels_5ghz[] = {
4117f1de56SFelix Fietkau 	CHAN5G(36, 5180),
4217f1de56SFelix Fietkau 	CHAN5G(40, 5200),
4317f1de56SFelix Fietkau 	CHAN5G(44, 5220),
4417f1de56SFelix Fietkau 	CHAN5G(48, 5240),
4517f1de56SFelix Fietkau 
4617f1de56SFelix Fietkau 	CHAN5G(52, 5260),
4717f1de56SFelix Fietkau 	CHAN5G(56, 5280),
4817f1de56SFelix Fietkau 	CHAN5G(60, 5300),
4917f1de56SFelix Fietkau 	CHAN5G(64, 5320),
5017f1de56SFelix Fietkau 
5117f1de56SFelix Fietkau 	CHAN5G(100, 5500),
5217f1de56SFelix Fietkau 	CHAN5G(104, 5520),
5317f1de56SFelix Fietkau 	CHAN5G(108, 5540),
5417f1de56SFelix Fietkau 	CHAN5G(112, 5560),
5517f1de56SFelix Fietkau 	CHAN5G(116, 5580),
5617f1de56SFelix Fietkau 	CHAN5G(120, 5600),
5717f1de56SFelix Fietkau 	CHAN5G(124, 5620),
5817f1de56SFelix Fietkau 	CHAN5G(128, 5640),
5917f1de56SFelix Fietkau 	CHAN5G(132, 5660),
6017f1de56SFelix Fietkau 	CHAN5G(136, 5680),
6117f1de56SFelix Fietkau 	CHAN5G(140, 5700),
629da82fb7SMarkus Theil 	CHAN5G(144, 5720),
6317f1de56SFelix Fietkau 
6417f1de56SFelix Fietkau 	CHAN5G(149, 5745),
6517f1de56SFelix Fietkau 	CHAN5G(153, 5765),
6617f1de56SFelix Fietkau 	CHAN5G(157, 5785),
6717f1de56SFelix Fietkau 	CHAN5G(161, 5805),
6817f1de56SFelix Fietkau 	CHAN5G(165, 5825),
699da82fb7SMarkus Theil 	CHAN5G(169, 5845),
709da82fb7SMarkus Theil 	CHAN5G(173, 5865),
7117f1de56SFelix Fietkau };
7217f1de56SFelix Fietkau 
7317f1de56SFelix Fietkau static const struct ieee80211_tpt_blink mt76_tpt_blink[] = {
7417f1de56SFelix Fietkau 	{ .throughput =   0 * 1024, .blink_time = 334 },
7517f1de56SFelix Fietkau 	{ .throughput =   1 * 1024, .blink_time = 260 },
7617f1de56SFelix Fietkau 	{ .throughput =   5 * 1024, .blink_time = 220 },
7717f1de56SFelix Fietkau 	{ .throughput =  10 * 1024, .blink_time = 190 },
7817f1de56SFelix Fietkau 	{ .throughput =  20 * 1024, .blink_time = 170 },
7917f1de56SFelix Fietkau 	{ .throughput =  50 * 1024, .blink_time = 150 },
8017f1de56SFelix Fietkau 	{ .throughput =  70 * 1024, .blink_time = 130 },
8117f1de56SFelix Fietkau 	{ .throughput = 100 * 1024, .blink_time = 110 },
8217f1de56SFelix Fietkau 	{ .throughput = 200 * 1024, .blink_time =  80 },
8317f1de56SFelix Fietkau 	{ .throughput = 300 * 1024, .blink_time =  50 },
8417f1de56SFelix Fietkau };
8517f1de56SFelix Fietkau 
8617f1de56SFelix Fietkau static int mt76_led_init(struct mt76_dev *dev)
8717f1de56SFelix Fietkau {
8817f1de56SFelix Fietkau 	struct device_node *np = dev->dev->of_node;
8917f1de56SFelix Fietkau 	struct ieee80211_hw *hw = dev->hw;
9017f1de56SFelix Fietkau 	int led_pin;
9117f1de56SFelix Fietkau 
9217f1de56SFelix Fietkau 	if (!dev->led_cdev.brightness_set && !dev->led_cdev.blink_set)
9317f1de56SFelix Fietkau 		return 0;
9417f1de56SFelix Fietkau 
9517f1de56SFelix Fietkau 	snprintf(dev->led_name, sizeof(dev->led_name),
9617f1de56SFelix Fietkau 		 "mt76-%s", wiphy_name(hw->wiphy));
9717f1de56SFelix Fietkau 
9817f1de56SFelix Fietkau 	dev->led_cdev.name = dev->led_name;
9917f1de56SFelix Fietkau 	dev->led_cdev.default_trigger =
10017f1de56SFelix Fietkau 		ieee80211_create_tpt_led_trigger(hw,
10117f1de56SFelix Fietkau 					IEEE80211_TPT_LEDTRIG_FL_RADIO,
10217f1de56SFelix Fietkau 					mt76_tpt_blink,
10317f1de56SFelix Fietkau 					ARRAY_SIZE(mt76_tpt_blink));
10417f1de56SFelix Fietkau 
10517f1de56SFelix Fietkau 	np = of_get_child_by_name(np, "led");
10617f1de56SFelix Fietkau 	if (np) {
10717f1de56SFelix Fietkau 		if (!of_property_read_u32(np, "led-sources", &led_pin))
10817f1de56SFelix Fietkau 			dev->led_pin = led_pin;
10917f1de56SFelix Fietkau 		dev->led_al = of_property_read_bool(np, "led-active-low");
11017f1de56SFelix Fietkau 	}
11117f1de56SFelix Fietkau 
11236f7e2b2SFelix Fietkau 	return led_classdev_register(dev->dev, &dev->led_cdev);
11336f7e2b2SFelix Fietkau }
11436f7e2b2SFelix Fietkau 
11536f7e2b2SFelix Fietkau static void mt76_led_cleanup(struct mt76_dev *dev)
11636f7e2b2SFelix Fietkau {
11736f7e2b2SFelix Fietkau 	if (!dev->led_cdev.brightness_set && !dev->led_cdev.blink_set)
11836f7e2b2SFelix Fietkau 		return;
11936f7e2b2SFelix Fietkau 
12036f7e2b2SFelix Fietkau 	led_classdev_unregister(&dev->led_cdev);
12117f1de56SFelix Fietkau }
12217f1de56SFelix Fietkau 
123bb3e3fecSRyder Lee static void mt76_init_stream_cap(struct mt76_phy *phy,
124551e1ef4SLorenzo Bianconi 				 struct ieee80211_supported_band *sband,
125551e1ef4SLorenzo Bianconi 				 bool vht)
126551e1ef4SLorenzo Bianconi {
127551e1ef4SLorenzo Bianconi 	struct ieee80211_sta_ht_cap *ht_cap = &sband->ht_cap;
128bb3e3fecSRyder Lee 	int i, nstream = hweight8(phy->antenna_mask);
129551e1ef4SLorenzo Bianconi 	struct ieee80211_sta_vht_cap *vht_cap;
130551e1ef4SLorenzo Bianconi 	u16 mcs_map = 0;
131551e1ef4SLorenzo Bianconi 
132551e1ef4SLorenzo Bianconi 	if (nstream > 1)
133551e1ef4SLorenzo Bianconi 		ht_cap->cap |= IEEE80211_HT_CAP_TX_STBC;
134551e1ef4SLorenzo Bianconi 	else
135551e1ef4SLorenzo Bianconi 		ht_cap->cap &= ~IEEE80211_HT_CAP_TX_STBC;
136551e1ef4SLorenzo Bianconi 
137551e1ef4SLorenzo Bianconi 	for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
138551e1ef4SLorenzo Bianconi 		ht_cap->mcs.rx_mask[i] = i < nstream ? 0xff : 0;
139551e1ef4SLorenzo Bianconi 
140551e1ef4SLorenzo Bianconi 	if (!vht)
141551e1ef4SLorenzo Bianconi 		return;
142551e1ef4SLorenzo Bianconi 
143551e1ef4SLorenzo Bianconi 	vht_cap = &sband->vht_cap;
144551e1ef4SLorenzo Bianconi 	if (nstream > 1)
145551e1ef4SLorenzo Bianconi 		vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC;
146551e1ef4SLorenzo Bianconi 	else
147551e1ef4SLorenzo Bianconi 		vht_cap->cap &= ~IEEE80211_VHT_CAP_TXSTBC;
148551e1ef4SLorenzo Bianconi 
149551e1ef4SLorenzo Bianconi 	for (i = 0; i < 8; i++) {
150551e1ef4SLorenzo Bianconi 		if (i < nstream)
151551e1ef4SLorenzo Bianconi 			mcs_map |= (IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2));
152551e1ef4SLorenzo Bianconi 		else
153551e1ef4SLorenzo Bianconi 			mcs_map |=
154551e1ef4SLorenzo Bianconi 				(IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2));
155551e1ef4SLorenzo Bianconi 	}
156551e1ef4SLorenzo Bianconi 	vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map);
157551e1ef4SLorenzo Bianconi 	vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map);
158551e1ef4SLorenzo Bianconi }
159551e1ef4SLorenzo Bianconi 
160bb3e3fecSRyder Lee void mt76_set_stream_caps(struct mt76_phy *phy, bool vht)
1615ebdc3e0SLorenzo Bianconi {
16248dbce5cSLorenzo Bianconi 	if (phy->cap.has_2ghz)
163bb3e3fecSRyder Lee 		mt76_init_stream_cap(phy, &phy->sband_2g.sband, false);
16448dbce5cSLorenzo Bianconi 	if (phy->cap.has_5ghz)
165bb3e3fecSRyder Lee 		mt76_init_stream_cap(phy, &phy->sband_5g.sband, vht);
1665ebdc3e0SLorenzo Bianconi }
1675ebdc3e0SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76_set_stream_caps);
1685ebdc3e0SLorenzo Bianconi 
16917f1de56SFelix Fietkau static int
17077af762eSLorenzo Bianconi mt76_init_sband(struct mt76_phy *phy, struct mt76_sband *msband,
17117f1de56SFelix Fietkau 		const struct ieee80211_channel *chan, int n_chan,
17217f1de56SFelix Fietkau 		struct ieee80211_rate *rates, int n_rates, bool vht)
17317f1de56SFelix Fietkau {
17417f1de56SFelix Fietkau 	struct ieee80211_supported_band *sband = &msband->sband;
17517f1de56SFelix Fietkau 	struct ieee80211_sta_vht_cap *vht_cap;
17677af762eSLorenzo Bianconi 	struct ieee80211_sta_ht_cap *ht_cap;
17777af762eSLorenzo Bianconi 	struct mt76_dev *dev = phy->dev;
17817f1de56SFelix Fietkau 	void *chanlist;
17917f1de56SFelix Fietkau 	int size;
18017f1de56SFelix Fietkau 
18117f1de56SFelix Fietkau 	size = n_chan * sizeof(*chan);
18217f1de56SFelix Fietkau 	chanlist = devm_kmemdup(dev->dev, chan, size, GFP_KERNEL);
18317f1de56SFelix Fietkau 	if (!chanlist)
18417f1de56SFelix Fietkau 		return -ENOMEM;
18517f1de56SFelix Fietkau 
186a86854d0SKees Cook 	msband->chan = devm_kcalloc(dev->dev, n_chan, sizeof(*msband->chan),
18717f1de56SFelix Fietkau 				    GFP_KERNEL);
18817f1de56SFelix Fietkau 	if (!msband->chan)
18917f1de56SFelix Fietkau 		return -ENOMEM;
19017f1de56SFelix Fietkau 
19117f1de56SFelix Fietkau 	sband->channels = chanlist;
19217f1de56SFelix Fietkau 	sband->n_channels = n_chan;
19317f1de56SFelix Fietkau 	sband->bitrates = rates;
19417f1de56SFelix Fietkau 	sband->n_bitrates = n_rates;
19517f1de56SFelix Fietkau 
19617f1de56SFelix Fietkau 	ht_cap = &sband->ht_cap;
19717f1de56SFelix Fietkau 	ht_cap->ht_supported = true;
19817f1de56SFelix Fietkau 	ht_cap->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
19917f1de56SFelix Fietkau 		       IEEE80211_HT_CAP_GRN_FLD |
20017f1de56SFelix Fietkau 		       IEEE80211_HT_CAP_SGI_20 |
20117f1de56SFelix Fietkau 		       IEEE80211_HT_CAP_SGI_40 |
20217f1de56SFelix Fietkau 		       (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT);
20317f1de56SFelix Fietkau 
20417f1de56SFelix Fietkau 	ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
20517f1de56SFelix Fietkau 	ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
20617f1de56SFelix Fietkau 
20777af762eSLorenzo Bianconi 	mt76_init_stream_cap(phy, sband, vht);
208551e1ef4SLorenzo Bianconi 
20917f1de56SFelix Fietkau 	if (!vht)
21017f1de56SFelix Fietkau 		return 0;
21117f1de56SFelix Fietkau 
21217f1de56SFelix Fietkau 	vht_cap = &sband->vht_cap;
21317f1de56SFelix Fietkau 	vht_cap->vht_supported = true;
21417f1de56SFelix Fietkau 	vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC |
21517f1de56SFelix Fietkau 			IEEE80211_VHT_CAP_RXSTBC_1 |
21649149d3fSFelix Fietkau 			IEEE80211_VHT_CAP_SHORT_GI_80 |
217f1103fa6SRyder Lee 			IEEE80211_VHT_CAP_RX_ANTENNA_PATTERN |
218f1103fa6SRyder Lee 			IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN |
21949149d3fSFelix Fietkau 			(3 << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT);
22017f1de56SFelix Fietkau 
22117f1de56SFelix Fietkau 	return 0;
22217f1de56SFelix Fietkau }
22317f1de56SFelix Fietkau 
22417f1de56SFelix Fietkau static int
22577af762eSLorenzo Bianconi mt76_init_sband_2g(struct mt76_phy *phy, struct ieee80211_rate *rates,
22617f1de56SFelix Fietkau 		   int n_rates)
22717f1de56SFelix Fietkau {
22877af762eSLorenzo Bianconi 	phy->hw->wiphy->bands[NL80211_BAND_2GHZ] = &phy->sband_2g.sband;
22917f1de56SFelix Fietkau 
23077af762eSLorenzo Bianconi 	return mt76_init_sband(phy, &phy->sband_2g, mt76_channels_2ghz,
23177af762eSLorenzo Bianconi 			       ARRAY_SIZE(mt76_channels_2ghz), rates,
23277af762eSLorenzo Bianconi 			       n_rates, false);
23317f1de56SFelix Fietkau }
23417f1de56SFelix Fietkau 
23517f1de56SFelix Fietkau static int
23677af762eSLorenzo Bianconi mt76_init_sband_5g(struct mt76_phy *phy, struct ieee80211_rate *rates,
23717f1de56SFelix Fietkau 		   int n_rates, bool vht)
23817f1de56SFelix Fietkau {
23977af762eSLorenzo Bianconi 	phy->hw->wiphy->bands[NL80211_BAND_5GHZ] = &phy->sband_5g.sband;
24017f1de56SFelix Fietkau 
24177af762eSLorenzo Bianconi 	return mt76_init_sband(phy, &phy->sband_5g, mt76_channels_5ghz,
24277af762eSLorenzo Bianconi 			       ARRAY_SIZE(mt76_channels_5ghz), rates,
24377af762eSLorenzo Bianconi 			       n_rates, vht);
24417f1de56SFelix Fietkau }
24517f1de56SFelix Fietkau 
24617f1de56SFelix Fietkau static void
247c89d3625SFelix Fietkau mt76_check_sband(struct mt76_phy *phy, struct mt76_sband *msband,
248c89d3625SFelix Fietkau 		 enum nl80211_band band)
24917f1de56SFelix Fietkau {
250c89d3625SFelix Fietkau 	struct ieee80211_supported_band *sband = &msband->sband;
25117f1de56SFelix Fietkau 	bool found = false;
25217f1de56SFelix Fietkau 	int i;
25317f1de56SFelix Fietkau 
25417f1de56SFelix Fietkau 	if (!sband)
25517f1de56SFelix Fietkau 		return;
25617f1de56SFelix Fietkau 
25717f1de56SFelix Fietkau 	for (i = 0; i < sband->n_channels; i++) {
25817f1de56SFelix Fietkau 		if (sband->channels[i].flags & IEEE80211_CHAN_DISABLED)
25917f1de56SFelix Fietkau 			continue;
26017f1de56SFelix Fietkau 
26117f1de56SFelix Fietkau 		found = true;
26217f1de56SFelix Fietkau 		break;
26317f1de56SFelix Fietkau 	}
26417f1de56SFelix Fietkau 
265c89d3625SFelix Fietkau 	if (found) {
266c89d3625SFelix Fietkau 		phy->chandef.chan = &sband->channels[0];
267c89d3625SFelix Fietkau 		phy->chan_state = &msband->chan[0];
26817f1de56SFelix Fietkau 		return;
269c89d3625SFelix Fietkau 	}
27017f1de56SFelix Fietkau 
27117f1de56SFelix Fietkau 	sband->n_channels = 0;
272c89d3625SFelix Fietkau 	phy->hw->wiphy->bands[band] = NULL;
27317f1de56SFelix Fietkau }
27417f1de56SFelix Fietkau 
275c89d3625SFelix Fietkau static void
27698df2baeSLorenzo Bianconi mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw)
277c89d3625SFelix Fietkau {
27898df2baeSLorenzo Bianconi 	struct mt76_dev *dev = phy->dev;
279c89d3625SFelix Fietkau 	struct wiphy *wiphy = hw->wiphy;
280c89d3625SFelix Fietkau 
281c89d3625SFelix Fietkau 	SET_IEEE80211_DEV(hw, dev->dev);
28298df2baeSLorenzo Bianconi 	SET_IEEE80211_PERM_ADDR(hw, phy->macaddr);
283c89d3625SFelix Fietkau 
284c89d3625SFelix Fietkau 	wiphy->features |= NL80211_FEATURE_ACTIVE_MONITOR;
285dd89a013SLorenzo Bianconi 	wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH |
286b807b368SLorenzo Bianconi 			WIPHY_FLAG_SUPPORTS_TDLS |
287b807b368SLorenzo Bianconi 			WIPHY_FLAG_AP_UAPSD;
288c89d3625SFelix Fietkau 
289c89d3625SFelix Fietkau 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
290c89d3625SFelix Fietkau 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS);
291d9c54264SFelix Fietkau 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AQL);
292c89d3625SFelix Fietkau 
293c89d3625SFelix Fietkau 	wiphy->available_antennas_tx = dev->phy.antenna_mask;
294c89d3625SFelix Fietkau 	wiphy->available_antennas_rx = dev->phy.antenna_mask;
295c89d3625SFelix Fietkau 
296c89d3625SFelix Fietkau 	hw->txq_data_size = sizeof(struct mt76_txq);
297b807b368SLorenzo Bianconi 	hw->uapsd_max_sp_len = IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL;
298c9619dfaSShayne Chen 
299c9619dfaSShayne Chen 	if (!hw->max_tx_fragments)
300c89d3625SFelix Fietkau 		hw->max_tx_fragments = 16;
301c89d3625SFelix Fietkau 
302c89d3625SFelix Fietkau 	ieee80211_hw_set(hw, SIGNAL_DBM);
303c89d3625SFelix Fietkau 	ieee80211_hw_set(hw, AMPDU_AGGREGATION);
304c89d3625SFelix Fietkau 	ieee80211_hw_set(hw, SUPPORTS_RC_TABLE);
305c89d3625SFelix Fietkau 	ieee80211_hw_set(hw, SUPPORT_FAST_XMIT);
306c89d3625SFelix Fietkau 	ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
307c89d3625SFelix Fietkau 	ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
308ed89b893SFelix Fietkau 	ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
309b443e55fSRyder Lee 
310b443e55fSRyder Lee 	if (!(dev->drv->drv_flags & MT_DRV_AMSDU_OFFLOAD)) {
311c89d3625SFelix Fietkau 		ieee80211_hw_set(hw, TX_AMSDU);
312c89d3625SFelix Fietkau 		ieee80211_hw_set(hw, TX_FRAG_LIST);
313b443e55fSRyder Lee 	}
314d39b52e3SSean Wang 
315c89d3625SFelix Fietkau 	ieee80211_hw_set(hw, MFP_CAPABLE);
316c89d3625SFelix Fietkau 	ieee80211_hw_set(hw, AP_LINK_PS);
317c89d3625SFelix Fietkau 	ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
318c89d3625SFelix Fietkau 
319c89d3625SFelix Fietkau 	wiphy->flags |= WIPHY_FLAG_IBSS_RSN;
320c89d3625SFelix Fietkau 	wiphy->interface_modes =
321c89d3625SFelix Fietkau 		BIT(NL80211_IFTYPE_STATION) |
322c89d3625SFelix Fietkau 		BIT(NL80211_IFTYPE_AP) |
323c89d3625SFelix Fietkau #ifdef CONFIG_MAC80211_MESH
324c89d3625SFelix Fietkau 		BIT(NL80211_IFTYPE_MESH_POINT) |
325c89d3625SFelix Fietkau #endif
32650eb0a88SLorenzo Bianconi 		BIT(NL80211_IFTYPE_P2P_CLIENT) |
32750eb0a88SLorenzo Bianconi 		BIT(NL80211_IFTYPE_P2P_GO) |
328c89d3625SFelix Fietkau 		BIT(NL80211_IFTYPE_ADHOC);
329c89d3625SFelix Fietkau }
330c89d3625SFelix Fietkau 
331c89d3625SFelix Fietkau struct mt76_phy *
332c89d3625SFelix Fietkau mt76_alloc_phy(struct mt76_dev *dev, unsigned int size,
333c89d3625SFelix Fietkau 	       const struct ieee80211_ops *ops)
334c89d3625SFelix Fietkau {
335c89d3625SFelix Fietkau 	struct ieee80211_hw *hw;
336db78a791SLorenzo Bianconi 	unsigned int phy_size;
337c89d3625SFelix Fietkau 	struct mt76_phy *phy;
338c89d3625SFelix Fietkau 
339c89d3625SFelix Fietkau 	phy_size = ALIGN(sizeof(*phy), 8);
340db78a791SLorenzo Bianconi 	hw = ieee80211_alloc_hw(size + phy_size, ops);
341c89d3625SFelix Fietkau 	if (!hw)
342c89d3625SFelix Fietkau 		return NULL;
343c89d3625SFelix Fietkau 
344c89d3625SFelix Fietkau 	phy = hw->priv;
345c89d3625SFelix Fietkau 	phy->dev = dev;
346c89d3625SFelix Fietkau 	phy->hw = hw;
347db78a791SLorenzo Bianconi 	phy->priv = hw->priv + phy_size;
348c89d3625SFelix Fietkau 
349c89d3625SFelix Fietkau 	return phy;
350c89d3625SFelix Fietkau }
351c89d3625SFelix Fietkau EXPORT_SYMBOL_GPL(mt76_alloc_phy);
352c89d3625SFelix Fietkau 
353db78a791SLorenzo Bianconi int mt76_register_phy(struct mt76_phy *phy, bool vht,
354db78a791SLorenzo Bianconi 		      struct ieee80211_rate *rates, int n_rates)
355c89d3625SFelix Fietkau {
356c89d3625SFelix Fietkau 	int ret;
357c89d3625SFelix Fietkau 
35898df2baeSLorenzo Bianconi 	mt76_phy_init(phy, phy->hw);
359db78a791SLorenzo Bianconi 
360db78a791SLorenzo Bianconi 	if (phy->cap.has_2ghz) {
361db78a791SLorenzo Bianconi 		ret = mt76_init_sband_2g(phy, rates, n_rates);
362db78a791SLorenzo Bianconi 		if (ret)
363db78a791SLorenzo Bianconi 			return ret;
364db78a791SLorenzo Bianconi 	}
365db78a791SLorenzo Bianconi 
366db78a791SLorenzo Bianconi 	if (phy->cap.has_5ghz) {
367db78a791SLorenzo Bianconi 		ret = mt76_init_sband_5g(phy, rates + 4, n_rates - 4, vht);
368db78a791SLorenzo Bianconi 		if (ret)
369db78a791SLorenzo Bianconi 			return ret;
370db78a791SLorenzo Bianconi 	}
371db78a791SLorenzo Bianconi 
372db78a791SLorenzo Bianconi 	wiphy_read_of_freq_limits(phy->hw->wiphy);
373db78a791SLorenzo Bianconi 	mt76_check_sband(phy, &phy->sband_2g, NL80211_BAND_2GHZ);
374db78a791SLorenzo Bianconi 	mt76_check_sband(phy, &phy->sband_5g, NL80211_BAND_5GHZ);
375db78a791SLorenzo Bianconi 
376c89d3625SFelix Fietkau 	ret = ieee80211_register_hw(phy->hw);
377c89d3625SFelix Fietkau 	if (ret)
378c89d3625SFelix Fietkau 		return ret;
379c89d3625SFelix Fietkau 
380c89d3625SFelix Fietkau 	phy->dev->phy2 = phy;
381db78a791SLorenzo Bianconi 
382c89d3625SFelix Fietkau 	return 0;
383c89d3625SFelix Fietkau }
384c89d3625SFelix Fietkau EXPORT_SYMBOL_GPL(mt76_register_phy);
385c89d3625SFelix Fietkau 
386db78a791SLorenzo Bianconi void mt76_unregister_phy(struct mt76_phy *phy)
387c89d3625SFelix Fietkau {
388c89d3625SFelix Fietkau 	struct mt76_dev *dev = phy->dev;
389c89d3625SFelix Fietkau 
390c89d3625SFelix Fietkau 	mt76_tx_status_check(dev, NULL, true);
391c89d3625SFelix Fietkau 	ieee80211_unregister_hw(phy->hw);
39294b6df08SFelix Fietkau 	dev->phy2 = NULL;
393c89d3625SFelix Fietkau }
394c89d3625SFelix Fietkau EXPORT_SYMBOL_GPL(mt76_unregister_phy);
395c89d3625SFelix Fietkau 
396a85b590cSFelix Fietkau struct mt76_dev *
397c0f7b25aSLorenzo Bianconi mt76_alloc_device(struct device *pdev, unsigned int size,
398c0f7b25aSLorenzo Bianconi 		  const struct ieee80211_ops *ops,
399c0f7b25aSLorenzo Bianconi 		  const struct mt76_driver_ops *drv_ops)
400a85b590cSFelix Fietkau {
401a85b590cSFelix Fietkau 	struct ieee80211_hw *hw;
402ac24dd35SFelix Fietkau 	struct mt76_phy *phy;
403a85b590cSFelix Fietkau 	struct mt76_dev *dev;
404e5443256SFelix Fietkau 	int i;
405a85b590cSFelix Fietkau 
406a85b590cSFelix Fietkau 	hw = ieee80211_alloc_hw(size, ops);
407a85b590cSFelix Fietkau 	if (!hw)
408a85b590cSFelix Fietkau 		return NULL;
409a85b590cSFelix Fietkau 
410a85b590cSFelix Fietkau 	dev = hw->priv;
411a85b590cSFelix Fietkau 	dev->hw = hw;
412c0f7b25aSLorenzo Bianconi 	dev->dev = pdev;
413c0f7b25aSLorenzo Bianconi 	dev->drv = drv_ops;
414c0f7b25aSLorenzo Bianconi 
415ac24dd35SFelix Fietkau 	phy = &dev->phy;
416ac24dd35SFelix Fietkau 	phy->dev = dev;
417ac24dd35SFelix Fietkau 	phy->hw = hw;
418ac24dd35SFelix Fietkau 
419a85b590cSFelix Fietkau 	spin_lock_init(&dev->rx_lock);
420a85b590cSFelix Fietkau 	spin_lock_init(&dev->lock);
421a85b590cSFelix Fietkau 	spin_lock_init(&dev->cc_lock);
422108a4861SStanislaw Gruszka 	mutex_init(&dev->mutex);
42326e40d4cSFelix Fietkau 	init_waitqueue_head(&dev->tx_wait);
42488046b2cSFelix Fietkau 	skb_queue_head_init(&dev->status_list);
425a85b590cSFelix Fietkau 
42609872957SLorenzo Bianconi 	skb_queue_head_init(&dev->mcu.res_q);
42709872957SLorenzo Bianconi 	init_waitqueue_head(&dev->mcu.wait);
42809872957SLorenzo Bianconi 	mutex_init(&dev->mcu.mutex);
429781eef5bSFelix Fietkau 	dev->tx_worker.fn = mt76_tx_worker;
43009872957SLorenzo Bianconi 
43151252cc5SLorenzo Bianconi 	spin_lock_init(&dev->token_lock);
43251252cc5SLorenzo Bianconi 	idr_init(&dev->token);
43351252cc5SLorenzo Bianconi 
434e5443256SFelix Fietkau 	INIT_LIST_HEAD(&dev->txwi_cache);
435e5443256SFelix Fietkau 
436e5443256SFelix Fietkau 	for (i = 0; i < ARRAY_SIZE(dev->q_rx); i++)
437e5443256SFelix Fietkau 		skb_queue_head_init(&dev->rx_skb[i]);
438e5443256SFelix Fietkau 
439a86f1d01SLorenzo Bianconi 	dev->wq = alloc_ordered_workqueue("mt76", 0);
440a86f1d01SLorenzo Bianconi 	if (!dev->wq) {
441a86f1d01SLorenzo Bianconi 		ieee80211_free_hw(hw);
442a86f1d01SLorenzo Bianconi 		return NULL;
443a86f1d01SLorenzo Bianconi 	}
444a86f1d01SLorenzo Bianconi 
445a85b590cSFelix Fietkau 	return dev;
446a85b590cSFelix Fietkau }
447a85b590cSFelix Fietkau EXPORT_SYMBOL_GPL(mt76_alloc_device);
448a85b590cSFelix Fietkau 
44917f1de56SFelix Fietkau int mt76_register_device(struct mt76_dev *dev, bool vht,
45017f1de56SFelix Fietkau 			 struct ieee80211_rate *rates, int n_rates)
45117f1de56SFelix Fietkau {
45217f1de56SFelix Fietkau 	struct ieee80211_hw *hw = dev->hw;
453c89d3625SFelix Fietkau 	struct mt76_phy *phy = &dev->phy;
45417f1de56SFelix Fietkau 	int ret;
45517f1de56SFelix Fietkau 
45617f1de56SFelix Fietkau 	dev_set_drvdata(dev->dev, dev);
45798df2baeSLorenzo Bianconi 	mt76_phy_init(phy, hw);
45817f1de56SFelix Fietkau 
45948dbce5cSLorenzo Bianconi 	if (phy->cap.has_2ghz) {
46077af762eSLorenzo Bianconi 		ret = mt76_init_sband_2g(phy, rates, n_rates);
46117f1de56SFelix Fietkau 		if (ret)
46217f1de56SFelix Fietkau 			return ret;
46317f1de56SFelix Fietkau 	}
46417f1de56SFelix Fietkau 
46548dbce5cSLorenzo Bianconi 	if (phy->cap.has_5ghz) {
46677af762eSLorenzo Bianconi 		ret = mt76_init_sband_5g(phy, rates + 4, n_rates - 4, vht);
46717f1de56SFelix Fietkau 		if (ret)
46817f1de56SFelix Fietkau 			return ret;
46917f1de56SFelix Fietkau 	}
47017f1de56SFelix Fietkau 
471c89d3625SFelix Fietkau 	wiphy_read_of_freq_limits(hw->wiphy);
472c89d3625SFelix Fietkau 	mt76_check_sband(&dev->phy, &phy->sband_2g, NL80211_BAND_2GHZ);
473c89d3625SFelix Fietkau 	mt76_check_sband(&dev->phy, &phy->sband_5g, NL80211_BAND_5GHZ);
47417f1de56SFelix Fietkau 
475b374e868SArnd Bergmann 	if (IS_ENABLED(CONFIG_MT76_LEDS)) {
47617f1de56SFelix Fietkau 		ret = mt76_led_init(dev);
47717f1de56SFelix Fietkau 		if (ret)
47817f1de56SFelix Fietkau 			return ret;
479b374e868SArnd Bergmann 	}
48017f1de56SFelix Fietkau 
481781eef5bSFelix Fietkau 	ret = ieee80211_register_hw(hw);
482781eef5bSFelix Fietkau 	if (ret)
483781eef5bSFelix Fietkau 		return ret;
484781eef5bSFelix Fietkau 
485781eef5bSFelix Fietkau 	WARN_ON(mt76_worker_setup(hw, &dev->tx_worker, NULL, "tx"));
486781eef5bSFelix Fietkau 	sched_set_fifo_low(dev->tx_worker.task);
487781eef5bSFelix Fietkau 
488781eef5bSFelix Fietkau 	return 0;
48917f1de56SFelix Fietkau }
49017f1de56SFelix Fietkau EXPORT_SYMBOL_GPL(mt76_register_device);
49117f1de56SFelix Fietkau 
49217f1de56SFelix Fietkau void mt76_unregister_device(struct mt76_dev *dev)
49317f1de56SFelix Fietkau {
49417f1de56SFelix Fietkau 	struct ieee80211_hw *hw = dev->hw;
49517f1de56SFelix Fietkau 
496d68f4e43SArnd Bergmann 	if (IS_ENABLED(CONFIG_MT76_LEDS))
49736f7e2b2SFelix Fietkau 		mt76_led_cleanup(dev);
49879d1c94cSFelix Fietkau 	mt76_tx_status_check(dev, NULL, true);
49917f1de56SFelix Fietkau 	ieee80211_unregister_hw(hw);
50017f1de56SFelix Fietkau }
50117f1de56SFelix Fietkau EXPORT_SYMBOL_GPL(mt76_unregister_device);
50217f1de56SFelix Fietkau 
503def34a2fSLorenzo Bianconi void mt76_free_device(struct mt76_dev *dev)
504def34a2fSLorenzo Bianconi {
505781eef5bSFelix Fietkau 	mt76_worker_teardown(&dev->tx_worker);
506a86f1d01SLorenzo Bianconi 	if (dev->wq) {
507a86f1d01SLorenzo Bianconi 		destroy_workqueue(dev->wq);
508a86f1d01SLorenzo Bianconi 		dev->wq = NULL;
509a86f1d01SLorenzo Bianconi 	}
510def34a2fSLorenzo Bianconi 	ieee80211_free_hw(dev->hw);
511def34a2fSLorenzo Bianconi }
512def34a2fSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76_free_device);
513def34a2fSLorenzo Bianconi 
514cc4b3c13SLorenzo Bianconi static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q)
515cc4b3c13SLorenzo Bianconi {
516cc4b3c13SLorenzo Bianconi 	struct sk_buff *skb = phy->rx_amsdu[q].head;
517*2c2bdd23SFelix Fietkau 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
518cc4b3c13SLorenzo Bianconi 	struct mt76_dev *dev = phy->dev;
519cc4b3c13SLorenzo Bianconi 
520cc4b3c13SLorenzo Bianconi 	phy->rx_amsdu[q].head = NULL;
521cc4b3c13SLorenzo Bianconi 	phy->rx_amsdu[q].tail = NULL;
522*2c2bdd23SFelix Fietkau 
523*2c2bdd23SFelix Fietkau 	/*
524*2c2bdd23SFelix Fietkau 	 * Validate if the amsdu has a proper first subframe.
525*2c2bdd23SFelix Fietkau 	 * A single MSDU can be parsed as A-MSDU when the unauthenticated A-MSDU
526*2c2bdd23SFelix Fietkau 	 * flag of the QoS header gets flipped. In such cases, the first
527*2c2bdd23SFelix Fietkau 	 * subframe has a LLC/SNAP header in the location of the destination
528*2c2bdd23SFelix Fietkau 	 * address.
529*2c2bdd23SFelix Fietkau 	 */
530*2c2bdd23SFelix Fietkau 	if (skb_shinfo(skb)->frag_list) {
531*2c2bdd23SFelix Fietkau 		int offset = 0;
532*2c2bdd23SFelix Fietkau 
533*2c2bdd23SFelix Fietkau 		if (!(status->flag & RX_FLAG_8023)) {
534*2c2bdd23SFelix Fietkau 			offset = ieee80211_get_hdrlen_from_skb(skb);
535*2c2bdd23SFelix Fietkau 
536*2c2bdd23SFelix Fietkau 			if ((status->flag &
537*2c2bdd23SFelix Fietkau 			     (RX_FLAG_DECRYPTED | RX_FLAG_IV_STRIPPED)) ==
538*2c2bdd23SFelix Fietkau 			    RX_FLAG_DECRYPTED)
539*2c2bdd23SFelix Fietkau 				offset += 8;
540*2c2bdd23SFelix Fietkau 		}
541*2c2bdd23SFelix Fietkau 
542*2c2bdd23SFelix Fietkau 		if (ether_addr_equal(skb->data + offset, rfc1042_header)) {
543*2c2bdd23SFelix Fietkau 			dev_kfree_skb(skb);
544*2c2bdd23SFelix Fietkau 			return;
545*2c2bdd23SFelix Fietkau 		}
546*2c2bdd23SFelix Fietkau 	}
547cc4b3c13SLorenzo Bianconi 	__skb_queue_tail(&dev->rx_skb[q], skb);
548cc4b3c13SLorenzo Bianconi }
549cc4b3c13SLorenzo Bianconi 
550cc4b3c13SLorenzo Bianconi static void mt76_rx_release_burst(struct mt76_phy *phy, enum mt76_rxq_id q,
551cc4b3c13SLorenzo Bianconi 				  struct sk_buff *skb)
552cc4b3c13SLorenzo Bianconi {
553cc4b3c13SLorenzo Bianconi 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
554cc4b3c13SLorenzo Bianconi 
555cc4b3c13SLorenzo Bianconi 	if (phy->rx_amsdu[q].head &&
556cc4b3c13SLorenzo Bianconi 	    (!status->amsdu || status->first_amsdu ||
557cc4b3c13SLorenzo Bianconi 	     status->seqno != phy->rx_amsdu[q].seqno))
558cc4b3c13SLorenzo Bianconi 		mt76_rx_release_amsdu(phy, q);
559cc4b3c13SLorenzo Bianconi 
560cc4b3c13SLorenzo Bianconi 	if (!phy->rx_amsdu[q].head) {
561cc4b3c13SLorenzo Bianconi 		phy->rx_amsdu[q].tail = &skb_shinfo(skb)->frag_list;
562cc4b3c13SLorenzo Bianconi 		phy->rx_amsdu[q].seqno = status->seqno;
563cc4b3c13SLorenzo Bianconi 		phy->rx_amsdu[q].head = skb;
564cc4b3c13SLorenzo Bianconi 	} else {
565cc4b3c13SLorenzo Bianconi 		*phy->rx_amsdu[q].tail = skb;
566cc4b3c13SLorenzo Bianconi 		phy->rx_amsdu[q].tail = &skb->next;
567cc4b3c13SLorenzo Bianconi 	}
568cc4b3c13SLorenzo Bianconi 
569cc4b3c13SLorenzo Bianconi 	if (!status->amsdu || status->last_amsdu)
570cc4b3c13SLorenzo Bianconi 		mt76_rx_release_amsdu(phy, q);
571cc4b3c13SLorenzo Bianconi }
572cc4b3c13SLorenzo Bianconi 
57317f1de56SFelix Fietkau void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
57417f1de56SFelix Fietkau {
575011849e0SFelix Fietkau 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
576011849e0SFelix Fietkau 	struct mt76_phy *phy = mt76_dev_phy(dev, status->ext_phy);
577011849e0SFelix Fietkau 
578011849e0SFelix Fietkau 	if (!test_bit(MT76_STATE_RUNNING, &phy->state)) {
57917f1de56SFelix Fietkau 		dev_kfree_skb(skb);
58017f1de56SFelix Fietkau 		return;
58117f1de56SFelix Fietkau 	}
58217f1de56SFelix Fietkau 
583f0efa862SFelix Fietkau #ifdef CONFIG_NL80211_TESTMODE
584c918c74dSShayne Chen 	if (phy->test.state == MT76_TM_STATE_RX_FRAMES) {
585c918c74dSShayne Chen 		phy->test.rx_stats.packets[q]++;
586f0efa862SFelix Fietkau 		if (status->flag & RX_FLAG_FAILED_FCS_CRC)
587c918c74dSShayne Chen 			phy->test.rx_stats.fcs_error[q]++;
588f0efa862SFelix Fietkau 	}
589f0efa862SFelix Fietkau #endif
590cc4b3c13SLorenzo Bianconi 
591cc4b3c13SLorenzo Bianconi 	mt76_rx_release_burst(phy, q, skb);
59217f1de56SFelix Fietkau }
59317f1de56SFelix Fietkau EXPORT_SYMBOL_GPL(mt76_rx);
59417f1de56SFelix Fietkau 
5955a95ca41SFelix Fietkau bool mt76_has_tx_pending(struct mt76_phy *phy)
59626e40d4cSFelix Fietkau {
597af005f26SLorenzo Bianconi 	struct mt76_queue *q;
59891990519SLorenzo Bianconi 	int i;
5995a95ca41SFelix Fietkau 
6005a95ca41SFelix Fietkau 	for (i = 0; i < __MT_TXQ_MAX; i++) {
60191990519SLorenzo Bianconi 		q = phy->q_tx[i];
602af005f26SLorenzo Bianconi 		if (q && q->queued)
60326e40d4cSFelix Fietkau 			return true;
60426e40d4cSFelix Fietkau 	}
60526e40d4cSFelix Fietkau 
60626e40d4cSFelix Fietkau 	return false;
60726e40d4cSFelix Fietkau }
60839d501d9SStanislaw Gruszka EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
60926e40d4cSFelix Fietkau 
6100fd0eb54SFelix Fietkau static struct mt76_channel_state *
61196747a51SFelix Fietkau mt76_channel_state(struct mt76_phy *phy, struct ieee80211_channel *c)
6120fd0eb54SFelix Fietkau {
6130fd0eb54SFelix Fietkau 	struct mt76_sband *msband;
6140fd0eb54SFelix Fietkau 	int idx;
6150fd0eb54SFelix Fietkau 
6160fd0eb54SFelix Fietkau 	if (c->band == NL80211_BAND_2GHZ)
61796747a51SFelix Fietkau 		msband = &phy->sband_2g;
6180fd0eb54SFelix Fietkau 	else
61996747a51SFelix Fietkau 		msband = &phy->sband_5g;
6200fd0eb54SFelix Fietkau 
6210fd0eb54SFelix Fietkau 	idx = c - &msband->sband.channels[0];
6220fd0eb54SFelix Fietkau 	return &msband->chan[idx];
6230fd0eb54SFelix Fietkau }
6240fd0eb54SFelix Fietkau 
62504414240SLorenzo Bianconi void mt76_update_survey_active_time(struct mt76_phy *phy, ktime_t time)
62696747a51SFelix Fietkau {
62796747a51SFelix Fietkau 	struct mt76_channel_state *state = phy->chan_state;
62896747a51SFelix Fietkau 
62996747a51SFelix Fietkau 	state->cc_active += ktime_to_us(ktime_sub(time,
63096747a51SFelix Fietkau 						  phy->survey_time));
63196747a51SFelix Fietkau 	phy->survey_time = time;
63296747a51SFelix Fietkau }
63304414240SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76_update_survey_active_time);
63496747a51SFelix Fietkau 
6355ce09c1aSFelix Fietkau void mt76_update_survey(struct mt76_dev *dev)
6365ce09c1aSFelix Fietkau {
637aec65e48SFelix Fietkau 	ktime_t cur_time;
638aec65e48SFelix Fietkau 
6395ce09c1aSFelix Fietkau 	if (dev->drv->update_survey)
6405ce09c1aSFelix Fietkau 		dev->drv->update_survey(dev);
6415ce09c1aSFelix Fietkau 
642aec65e48SFelix Fietkau 	cur_time = ktime_get_boottime();
64396747a51SFelix Fietkau 	mt76_update_survey_active_time(&dev->phy, cur_time);
64496747a51SFelix Fietkau 	if (dev->phy2)
64596747a51SFelix Fietkau 		mt76_update_survey_active_time(dev->phy2, cur_time);
646aec65e48SFelix Fietkau 
6475ce09c1aSFelix Fietkau 	if (dev->drv->drv_flags & MT_DRV_SW_RX_AIRTIME) {
64896747a51SFelix Fietkau 		struct mt76_channel_state *state = dev->phy.chan_state;
64996747a51SFelix Fietkau 
650237312c5SLorenzo Bianconi 		spin_lock_bh(&dev->cc_lock);
6515ce09c1aSFelix Fietkau 		state->cc_bss_rx += dev->cur_cc_bss_rx;
6525ce09c1aSFelix Fietkau 		dev->cur_cc_bss_rx = 0;
653237312c5SLorenzo Bianconi 		spin_unlock_bh(&dev->cc_lock);
6545ce09c1aSFelix Fietkau 	}
6555ce09c1aSFelix Fietkau }
6565ce09c1aSFelix Fietkau EXPORT_SYMBOL_GPL(mt76_update_survey);
6575ce09c1aSFelix Fietkau 
65896747a51SFelix Fietkau void mt76_set_channel(struct mt76_phy *phy)
65917f1de56SFelix Fietkau {
66096747a51SFelix Fietkau 	struct mt76_dev *dev = phy->dev;
66196747a51SFelix Fietkau 	struct ieee80211_hw *hw = phy->hw;
66217f1de56SFelix Fietkau 	struct cfg80211_chan_def *chandef = &hw->conf.chandef;
66317f1de56SFelix Fietkau 	bool offchannel = hw->conf.flags & IEEE80211_CONF_OFFCHANNEL;
66426e40d4cSFelix Fietkau 	int timeout = HZ / 5;
66517f1de56SFelix Fietkau 
6665a95ca41SFelix Fietkau 	wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(phy), timeout);
6675ce09c1aSFelix Fietkau 	mt76_update_survey(dev);
66817f1de56SFelix Fietkau 
66996747a51SFelix Fietkau 	phy->chandef = *chandef;
67096747a51SFelix Fietkau 	phy->chan_state = mt76_channel_state(phy, chandef->chan);
67117f1de56SFelix Fietkau 
67217f1de56SFelix Fietkau 	if (!offchannel)
67396747a51SFelix Fietkau 		phy->main_chan = chandef->chan;
67417f1de56SFelix Fietkau 
67596747a51SFelix Fietkau 	if (chandef->chan != phy->main_chan)
67696747a51SFelix Fietkau 		memset(phy->chan_state, 0, sizeof(*phy->chan_state));
67717f1de56SFelix Fietkau }
67817f1de56SFelix Fietkau EXPORT_SYMBOL_GPL(mt76_set_channel);
67917f1de56SFelix Fietkau 
68017f1de56SFelix Fietkau int mt76_get_survey(struct ieee80211_hw *hw, int idx,
68117f1de56SFelix Fietkau 		    struct survey_info *survey)
68217f1de56SFelix Fietkau {
68396747a51SFelix Fietkau 	struct mt76_phy *phy = hw->priv;
68496747a51SFelix Fietkau 	struct mt76_dev *dev = phy->dev;
68517f1de56SFelix Fietkau 	struct mt76_sband *sband;
68617f1de56SFelix Fietkau 	struct ieee80211_channel *chan;
68717f1de56SFelix Fietkau 	struct mt76_channel_state *state;
68817f1de56SFelix Fietkau 	int ret = 0;
68917f1de56SFelix Fietkau 
690237312c5SLorenzo Bianconi 	mutex_lock(&dev->mutex);
69117f1de56SFelix Fietkau 	if (idx == 0 && dev->drv->update_survey)
6925ce09c1aSFelix Fietkau 		mt76_update_survey(dev);
69317f1de56SFelix Fietkau 
69496747a51SFelix Fietkau 	sband = &phy->sband_2g;
69517f1de56SFelix Fietkau 	if (idx >= sband->sband.n_channels) {
69617f1de56SFelix Fietkau 		idx -= sband->sband.n_channels;
69796747a51SFelix Fietkau 		sband = &phy->sband_5g;
69817f1de56SFelix Fietkau 	}
69917f1de56SFelix Fietkau 
700237312c5SLorenzo Bianconi 	if (idx >= sband->sband.n_channels) {
701237312c5SLorenzo Bianconi 		ret = -ENOENT;
702237312c5SLorenzo Bianconi 		goto out;
703237312c5SLorenzo Bianconi 	}
70417f1de56SFelix Fietkau 
70517f1de56SFelix Fietkau 	chan = &sband->sband.channels[idx];
70696747a51SFelix Fietkau 	state = mt76_channel_state(phy, chan);
70717f1de56SFelix Fietkau 
70817f1de56SFelix Fietkau 	memset(survey, 0, sizeof(*survey));
70917f1de56SFelix Fietkau 	survey->channel = chan;
71017f1de56SFelix Fietkau 	survey->filled = SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY;
711ea565833SFelix Fietkau 	survey->filled |= dev->drv->survey_flags;
712e5051965SFelix Fietkau 	if (state->noise)
713e5051965SFelix Fietkau 		survey->filled |= SURVEY_INFO_NOISE_DBM;
714e5051965SFelix Fietkau 
71596747a51SFelix Fietkau 	if (chan == phy->main_chan) {
71617f1de56SFelix Fietkau 		survey->filled |= SURVEY_INFO_IN_USE;
71717f1de56SFelix Fietkau 
7185ce09c1aSFelix Fietkau 		if (dev->drv->drv_flags & MT_DRV_SW_RX_AIRTIME)
7195ce09c1aSFelix Fietkau 			survey->filled |= SURVEY_INFO_TIME_BSS_RX;
7205ce09c1aSFelix Fietkau 	}
7215ce09c1aSFelix Fietkau 
72217f1de56SFelix Fietkau 	survey->time_busy = div_u64(state->cc_busy, 1000);
7236bfa6e38SLorenzo Bianconi 	survey->time_rx = div_u64(state->cc_rx, 1000);
724237312c5SLorenzo Bianconi 	survey->time = div_u64(state->cc_active, 1000);
725e5051965SFelix Fietkau 	survey->noise = state->noise;
726237312c5SLorenzo Bianconi 
727237312c5SLorenzo Bianconi 	spin_lock_bh(&dev->cc_lock);
728237312c5SLorenzo Bianconi 	survey->time_bss_rx = div_u64(state->cc_bss_rx, 1000);
729ea565833SFelix Fietkau 	survey->time_tx = div_u64(state->cc_tx, 1000);
73017f1de56SFelix Fietkau 	spin_unlock_bh(&dev->cc_lock);
73117f1de56SFelix Fietkau 
732237312c5SLorenzo Bianconi out:
733237312c5SLorenzo Bianconi 	mutex_unlock(&dev->mutex);
734237312c5SLorenzo Bianconi 
73517f1de56SFelix Fietkau 	return ret;
73617f1de56SFelix Fietkau }
73717f1de56SFelix Fietkau EXPORT_SYMBOL_GPL(mt76_get_survey);
73817f1de56SFelix Fietkau 
73930ce7f44SFelix Fietkau void mt76_wcid_key_setup(struct mt76_dev *dev, struct mt76_wcid *wcid,
74030ce7f44SFelix Fietkau 			 struct ieee80211_key_conf *key)
74130ce7f44SFelix Fietkau {
74230ce7f44SFelix Fietkau 	struct ieee80211_key_seq seq;
74330ce7f44SFelix Fietkau 	int i;
74430ce7f44SFelix Fietkau 
74530ce7f44SFelix Fietkau 	wcid->rx_check_pn = false;
74630ce7f44SFelix Fietkau 
74730ce7f44SFelix Fietkau 	if (!key)
74830ce7f44SFelix Fietkau 		return;
74930ce7f44SFelix Fietkau 
75001cfc1b4SLorenzo Bianconi 	if (key->cipher != WLAN_CIPHER_SUITE_CCMP)
75101cfc1b4SLorenzo Bianconi 		return;
75230ce7f44SFelix Fietkau 
75301cfc1b4SLorenzo Bianconi 	wcid->rx_check_pn = true;
75430ce7f44SFelix Fietkau 	for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
75530ce7f44SFelix Fietkau 		ieee80211_get_key_rx_seq(key, i, &seq);
75630ce7f44SFelix Fietkau 		memcpy(wcid->rx_key_pn[i], seq.ccmp.pn, sizeof(seq.ccmp.pn));
75730ce7f44SFelix Fietkau 	}
75830ce7f44SFelix Fietkau }
75930ce7f44SFelix Fietkau EXPORT_SYMBOL(mt76_wcid_key_setup);
76030ce7f44SFelix Fietkau 
761bfc394ddSFelix Fietkau static void
762bfc394ddSFelix Fietkau mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
763bfc394ddSFelix Fietkau 		struct ieee80211_hw **hw,
764bfc394ddSFelix Fietkau 		struct ieee80211_sta **sta)
7654e34249eSFelix Fietkau {
7664e34249eSFelix Fietkau 	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
7674e34249eSFelix Fietkau 	struct mt76_rx_status mstat;
7684e34249eSFelix Fietkau 
7694e34249eSFelix Fietkau 	mstat = *((struct mt76_rx_status *)skb->cb);
7704e34249eSFelix Fietkau 	memset(status, 0, sizeof(*status));
7714e34249eSFelix Fietkau 
7724e34249eSFelix Fietkau 	status->flag = mstat.flag;
7734e34249eSFelix Fietkau 	status->freq = mstat.freq;
7744e34249eSFelix Fietkau 	status->enc_flags = mstat.enc_flags;
7754e34249eSFelix Fietkau 	status->encoding = mstat.encoding;
7764e34249eSFelix Fietkau 	status->bw = mstat.bw;
777af4a2f2fSRyder Lee 	status->he_ru = mstat.he_ru;
778af4a2f2fSRyder Lee 	status->he_gi = mstat.he_gi;
779af4a2f2fSRyder Lee 	status->he_dcm = mstat.he_dcm;
7804e34249eSFelix Fietkau 	status->rate_idx = mstat.rate_idx;
7814e34249eSFelix Fietkau 	status->nss = mstat.nss;
7824e34249eSFelix Fietkau 	status->band = mstat.band;
7834e34249eSFelix Fietkau 	status->signal = mstat.signal;
7844e34249eSFelix Fietkau 	status->chains = mstat.chains;
785d515fdcaSFelix Fietkau 	status->ampdu_reference = mstat.ampdu_ref;
7860fda6d7bSRyder Lee 	status->device_timestamp = mstat.timestamp;
7870fda6d7bSRyder Lee 	status->mactime = mstat.timestamp;
7884e34249eSFelix Fietkau 
7894e34249eSFelix Fietkau 	BUILD_BUG_ON(sizeof(mstat) > sizeof(skb->cb));
79013381dcdSRyder Lee 	BUILD_BUG_ON(sizeof(status->chain_signal) !=
79113381dcdSRyder Lee 		     sizeof(mstat.chain_signal));
79213381dcdSRyder Lee 	memcpy(status->chain_signal, mstat.chain_signal,
79313381dcdSRyder Lee 	       sizeof(mstat.chain_signal));
7949c68a57bSFelix Fietkau 
795bfc394ddSFelix Fietkau 	*sta = wcid_to_sta(mstat.wcid);
796bfc394ddSFelix Fietkau 	*hw = mt76_phy_hw(dev, mstat.ext_phy);
7974e34249eSFelix Fietkau }
7984e34249eSFelix Fietkau 
79930ce7f44SFelix Fietkau static int
80030ce7f44SFelix Fietkau mt76_check_ccmp_pn(struct sk_buff *skb)
80130ce7f44SFelix Fietkau {
80230ce7f44SFelix Fietkau 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
80330ce7f44SFelix Fietkau 	struct mt76_wcid *wcid = status->wcid;
80430ce7f44SFelix Fietkau 	struct ieee80211_hdr *hdr;
805e195dad1SFelix Fietkau 	u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK;
80630ce7f44SFelix Fietkau 	int ret;
80730ce7f44SFelix Fietkau 
80830ce7f44SFelix Fietkau 	if (!(status->flag & RX_FLAG_DECRYPTED))
80930ce7f44SFelix Fietkau 		return 0;
81030ce7f44SFelix Fietkau 
81130ce7f44SFelix Fietkau 	if (!wcid || !wcid->rx_check_pn)
81230ce7f44SFelix Fietkau 		return 0;
81330ce7f44SFelix Fietkau 
81430ce7f44SFelix Fietkau 	if (!(status->flag & RX_FLAG_IV_STRIPPED)) {
81530ce7f44SFelix Fietkau 		/*
81630ce7f44SFelix Fietkau 		 * Validate the first fragment both here and in mac80211
81730ce7f44SFelix Fietkau 		 * All further fragments will be validated by mac80211 only.
81830ce7f44SFelix Fietkau 		 */
81977ae1d5eSRyder Lee 		hdr = mt76_skb_get_hdr(skb);
82030ce7f44SFelix Fietkau 		if (ieee80211_is_frag(hdr) &&
82130ce7f44SFelix Fietkau 		    !ieee80211_is_first_frag(hdr->frame_control))
82230ce7f44SFelix Fietkau 			return 0;
82330ce7f44SFelix Fietkau 	}
82430ce7f44SFelix Fietkau 
82530ce7f44SFelix Fietkau 	BUILD_BUG_ON(sizeof(status->iv) != sizeof(wcid->rx_key_pn[0]));
826e195dad1SFelix Fietkau 	ret = memcmp(status->iv, wcid->rx_key_pn[tidno],
82730ce7f44SFelix Fietkau 		     sizeof(status->iv));
82830ce7f44SFelix Fietkau 	if (ret <= 0)
82930ce7f44SFelix Fietkau 		return -EINVAL; /* replay */
83030ce7f44SFelix Fietkau 
831e195dad1SFelix Fietkau 	memcpy(wcid->rx_key_pn[tidno], status->iv, sizeof(status->iv));
83230ce7f44SFelix Fietkau 
83330ce7f44SFelix Fietkau 	if (status->flag & RX_FLAG_IV_STRIPPED)
83430ce7f44SFelix Fietkau 		status->flag |= RX_FLAG_PN_VALIDATED;
83530ce7f44SFelix Fietkau 
83630ce7f44SFelix Fietkau 	return 0;
83730ce7f44SFelix Fietkau }
83830ce7f44SFelix Fietkau 
839d71ef286SFelix Fietkau static void
8405ce09c1aSFelix Fietkau mt76_airtime_report(struct mt76_dev *dev, struct mt76_rx_status *status,
8415ce09c1aSFelix Fietkau 		    int len)
8425ce09c1aSFelix Fietkau {
8435ce09c1aSFelix Fietkau 	struct mt76_wcid *wcid = status->wcid;
84485b7a5d0SLorenzo Bianconi 	struct ieee80211_rx_status info = {
84585b7a5d0SLorenzo Bianconi 		.enc_flags = status->enc_flags,
84685b7a5d0SLorenzo Bianconi 		.rate_idx = status->rate_idx,
84785b7a5d0SLorenzo Bianconi 		.encoding = status->encoding,
84885b7a5d0SLorenzo Bianconi 		.band = status->band,
84985b7a5d0SLorenzo Bianconi 		.nss = status->nss,
85085b7a5d0SLorenzo Bianconi 		.bw = status->bw,
85185b7a5d0SLorenzo Bianconi 	};
8525ce09c1aSFelix Fietkau 	struct ieee80211_sta *sta;
8535ce09c1aSFelix Fietkau 	u32 airtime;
854e195dad1SFelix Fietkau 	u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK;
8555ce09c1aSFelix Fietkau 
85685b7a5d0SLorenzo Bianconi 	airtime = ieee80211_calc_rx_airtime(dev->hw, &info, len);
857237312c5SLorenzo Bianconi 	spin_lock(&dev->cc_lock);
8585ce09c1aSFelix Fietkau 	dev->cur_cc_bss_rx += airtime;
859237312c5SLorenzo Bianconi 	spin_unlock(&dev->cc_lock);
8605ce09c1aSFelix Fietkau 
8615ce09c1aSFelix Fietkau 	if (!wcid || !wcid->sta)
8625ce09c1aSFelix Fietkau 		return;
8635ce09c1aSFelix Fietkau 
8645ce09c1aSFelix Fietkau 	sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
865e195dad1SFelix Fietkau 	ieee80211_sta_register_airtime(sta, tidno, 0, airtime);
8665ce09c1aSFelix Fietkau }
8675ce09c1aSFelix Fietkau 
8685ce09c1aSFelix Fietkau static void
8695ce09c1aSFelix Fietkau mt76_airtime_flush_ampdu(struct mt76_dev *dev)
8705ce09c1aSFelix Fietkau {
8715ce09c1aSFelix Fietkau 	struct mt76_wcid *wcid;
8725ce09c1aSFelix Fietkau 	int wcid_idx;
8735ce09c1aSFelix Fietkau 
8745ce09c1aSFelix Fietkau 	if (!dev->rx_ampdu_len)
8755ce09c1aSFelix Fietkau 		return;
8765ce09c1aSFelix Fietkau 
8775ce09c1aSFelix Fietkau 	wcid_idx = dev->rx_ampdu_status.wcid_idx;
878bf5238b2SFelix Fietkau 	if (wcid_idx < ARRAY_SIZE(dev->wcid))
8795ce09c1aSFelix Fietkau 		wcid = rcu_dereference(dev->wcid[wcid_idx]);
8805ce09c1aSFelix Fietkau 	else
8815ce09c1aSFelix Fietkau 		wcid = NULL;
8825ce09c1aSFelix Fietkau 	dev->rx_ampdu_status.wcid = wcid;
8835ce09c1aSFelix Fietkau 
8845ce09c1aSFelix Fietkau 	mt76_airtime_report(dev, &dev->rx_ampdu_status, dev->rx_ampdu_len);
8855ce09c1aSFelix Fietkau 
8865ce09c1aSFelix Fietkau 	dev->rx_ampdu_len = 0;
8875ce09c1aSFelix Fietkau 	dev->rx_ampdu_ref = 0;
8885ce09c1aSFelix Fietkau }
8895ce09c1aSFelix Fietkau 
8905ce09c1aSFelix Fietkau static void
8915ce09c1aSFelix Fietkau mt76_airtime_check(struct mt76_dev *dev, struct sk_buff *skb)
8925ce09c1aSFelix Fietkau {
8935ce09c1aSFelix Fietkau 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
8945ce09c1aSFelix Fietkau 	struct mt76_wcid *wcid = status->wcid;
8955ce09c1aSFelix Fietkau 
8965ce09c1aSFelix Fietkau 	if (!(dev->drv->drv_flags & MT_DRV_SW_RX_AIRTIME))
8975ce09c1aSFelix Fietkau 		return;
8985ce09c1aSFelix Fietkau 
8995ce09c1aSFelix Fietkau 	if (!wcid || !wcid->sta) {
900e195dad1SFelix Fietkau 		struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
901e195dad1SFelix Fietkau 
902e195dad1SFelix Fietkau 		if (status->flag & RX_FLAG_8023)
903e195dad1SFelix Fietkau 			return;
904e195dad1SFelix Fietkau 
90598df2baeSLorenzo Bianconi 		if (!ether_addr_equal(hdr->addr1, dev->phy.macaddr))
9065ce09c1aSFelix Fietkau 			return;
9075ce09c1aSFelix Fietkau 
9085ce09c1aSFelix Fietkau 		wcid = NULL;
9095ce09c1aSFelix Fietkau 	}
9105ce09c1aSFelix Fietkau 
9115ce09c1aSFelix Fietkau 	if (!(status->flag & RX_FLAG_AMPDU_DETAILS) ||
9125ce09c1aSFelix Fietkau 	    status->ampdu_ref != dev->rx_ampdu_ref)
9135ce09c1aSFelix Fietkau 		mt76_airtime_flush_ampdu(dev);
9145ce09c1aSFelix Fietkau 
9155ce09c1aSFelix Fietkau 	if (status->flag & RX_FLAG_AMPDU_DETAILS) {
9165ce09c1aSFelix Fietkau 		if (!dev->rx_ampdu_len ||
9175ce09c1aSFelix Fietkau 		    status->ampdu_ref != dev->rx_ampdu_ref) {
9185ce09c1aSFelix Fietkau 			dev->rx_ampdu_status = *status;
9195ce09c1aSFelix Fietkau 			dev->rx_ampdu_status.wcid_idx = wcid ? wcid->idx : 0xff;
9205ce09c1aSFelix Fietkau 			dev->rx_ampdu_ref = status->ampdu_ref;
9215ce09c1aSFelix Fietkau 		}
9225ce09c1aSFelix Fietkau 
9235ce09c1aSFelix Fietkau 		dev->rx_ampdu_len += skb->len;
9245ce09c1aSFelix Fietkau 		return;
9255ce09c1aSFelix Fietkau 	}
9265ce09c1aSFelix Fietkau 
9275ce09c1aSFelix Fietkau 	mt76_airtime_report(dev, status, skb->len);
9285ce09c1aSFelix Fietkau }
9295ce09c1aSFelix Fietkau 
9305ce09c1aSFelix Fietkau static void
931ef13edc0SFelix Fietkau mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
932d71ef286SFelix Fietkau {
933d71ef286SFelix Fietkau 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
93477ae1d5eSRyder Lee 	struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
935d71ef286SFelix Fietkau 	struct ieee80211_sta *sta;
936bfc394ddSFelix Fietkau 	struct ieee80211_hw *hw;
937d71ef286SFelix Fietkau 	struct mt76_wcid *wcid = status->wcid;
938e195dad1SFelix Fietkau 	u8 tidno = status->qos_ctl & IEEE80211_QOS_CTL_TID_MASK;
939d71ef286SFelix Fietkau 	bool ps;
940d71ef286SFelix Fietkau 
941bfc394ddSFelix Fietkau 	hw = mt76_phy_hw(dev, status->ext_phy);
942e195dad1SFelix Fietkau 	if (ieee80211_is_pspoll(hdr->frame_control) && !wcid &&
943e195dad1SFelix Fietkau 	    !(status->flag & RX_FLAG_8023)) {
944bfc394ddSFelix Fietkau 		sta = ieee80211_find_sta_by_ifaddr(hw, hdr->addr2, NULL);
94536d91096SFelix Fietkau 		if (sta)
94636d91096SFelix Fietkau 			wcid = status->wcid = (struct mt76_wcid *)sta->drv_priv;
94736d91096SFelix Fietkau 	}
94836d91096SFelix Fietkau 
9495ce09c1aSFelix Fietkau 	mt76_airtime_check(dev, skb);
9505ce09c1aSFelix Fietkau 
951d71ef286SFelix Fietkau 	if (!wcid || !wcid->sta)
952d71ef286SFelix Fietkau 		return;
953d71ef286SFelix Fietkau 
954d71ef286SFelix Fietkau 	sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv);
955d71ef286SFelix Fietkau 
95602e5a769SFelix Fietkau 	if (status->signal <= 0)
95702e5a769SFelix Fietkau 		ewma_signal_add(&wcid->rssi, -status->signal);
95802e5a769SFelix Fietkau 
959ef13edc0SFelix Fietkau 	wcid->inactive_count = 0;
960ef13edc0SFelix Fietkau 
961e195dad1SFelix Fietkau 	if (status->flag & RX_FLAG_8023)
962e195dad1SFelix Fietkau 		return;
963e195dad1SFelix Fietkau 
964d71ef286SFelix Fietkau 	if (!test_bit(MT_WCID_FLAG_CHECK_PS, &wcid->flags))
965d71ef286SFelix Fietkau 		return;
966d71ef286SFelix Fietkau 
967d71ef286SFelix Fietkau 	if (ieee80211_is_pspoll(hdr->frame_control)) {
968d71ef286SFelix Fietkau 		ieee80211_sta_pspoll(sta);
969d71ef286SFelix Fietkau 		return;
970d71ef286SFelix Fietkau 	}
971d71ef286SFelix Fietkau 
972d71ef286SFelix Fietkau 	if (ieee80211_has_morefrags(hdr->frame_control) ||
973d71ef286SFelix Fietkau 	    !(ieee80211_is_mgmt(hdr->frame_control) ||
974d71ef286SFelix Fietkau 	      ieee80211_is_data(hdr->frame_control)))
975d71ef286SFelix Fietkau 		return;
976d71ef286SFelix Fietkau 
977d71ef286SFelix Fietkau 	ps = ieee80211_has_pm(hdr->frame_control);
978d71ef286SFelix Fietkau 
979d71ef286SFelix Fietkau 	if (ps && (ieee80211_is_data_qos(hdr->frame_control) ||
980d71ef286SFelix Fietkau 		   ieee80211_is_qos_nullfunc(hdr->frame_control)))
981e195dad1SFelix Fietkau 		ieee80211_sta_uapsd_trigger(sta, tidno);
982d71ef286SFelix Fietkau 
983d71ef286SFelix Fietkau 	if (!!test_bit(MT_WCID_FLAG_PS, &wcid->flags) == ps)
984d71ef286SFelix Fietkau 		return;
985d71ef286SFelix Fietkau 
98611b2a25fSFelix Fietkau 	if (ps)
987d71ef286SFelix Fietkau 		set_bit(MT_WCID_FLAG_PS, &wcid->flags);
98811b2a25fSFelix Fietkau 	else
989d71ef286SFelix Fietkau 		clear_bit(MT_WCID_FLAG_PS, &wcid->flags);
990d71ef286SFelix Fietkau 
991d71ef286SFelix Fietkau 	dev->drv->sta_ps(dev, sta, ps);
9929f67c277SFelix Fietkau 	ieee80211_sta_ps_transition(sta, ps);
993d71ef286SFelix Fietkau }
994d71ef286SFelix Fietkau 
9959d9d738bSFelix Fietkau void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
99681e850efSLorenzo Bianconi 		      struct napi_struct *napi)
99717f1de56SFelix Fietkau {
9989c68a57bSFelix Fietkau 	struct ieee80211_sta *sta;
999bfc394ddSFelix Fietkau 	struct ieee80211_hw *hw;
10003298b1f8SFelix Fietkau 	struct sk_buff *skb, *tmp;
10013298b1f8SFelix Fietkau 	LIST_HEAD(list);
10029d9d738bSFelix Fietkau 
1003c3d7c82aSFelix Fietkau 	spin_lock(&dev->rx_lock);
10049d9d738bSFelix Fietkau 	while ((skb = __skb_dequeue(frames)) != NULL) {
1005cc4b3c13SLorenzo Bianconi 		struct sk_buff *nskb = skb_shinfo(skb)->frag_list;
1006cc4b3c13SLorenzo Bianconi 
100730ce7f44SFelix Fietkau 		if (mt76_check_ccmp_pn(skb)) {
100830ce7f44SFelix Fietkau 			dev_kfree_skb(skb);
100930ce7f44SFelix Fietkau 			continue;
101030ce7f44SFelix Fietkau 		}
101130ce7f44SFelix Fietkau 
1012cc4b3c13SLorenzo Bianconi 		skb_shinfo(skb)->frag_list = NULL;
1013bfc394ddSFelix Fietkau 		mt76_rx_convert(dev, skb, &hw, &sta);
10143298b1f8SFelix Fietkau 		ieee80211_rx_list(hw, sta, skb, &list);
1015cc4b3c13SLorenzo Bianconi 
1016cc4b3c13SLorenzo Bianconi 		/* subsequent amsdu frames */
1017cc4b3c13SLorenzo Bianconi 		while (nskb) {
1018cc4b3c13SLorenzo Bianconi 			skb = nskb;
1019cc4b3c13SLorenzo Bianconi 			nskb = nskb->next;
1020cc4b3c13SLorenzo Bianconi 			skb->next = NULL;
1021cc4b3c13SLorenzo Bianconi 
1022cc4b3c13SLorenzo Bianconi 			mt76_rx_convert(dev, skb, &hw, &sta);
1023cc4b3c13SLorenzo Bianconi 			ieee80211_rx_list(hw, sta, skb, &list);
1024cc4b3c13SLorenzo Bianconi 		}
10259d9d738bSFelix Fietkau 	}
1026c3d7c82aSFelix Fietkau 	spin_unlock(&dev->rx_lock);
10273298b1f8SFelix Fietkau 
10283298b1f8SFelix Fietkau 	if (!napi) {
10293298b1f8SFelix Fietkau 		netif_receive_skb_list(&list);
10303298b1f8SFelix Fietkau 		return;
10313298b1f8SFelix Fietkau 	}
10323298b1f8SFelix Fietkau 
10333298b1f8SFelix Fietkau 	list_for_each_entry_safe(skb, tmp, &list, list) {
10343298b1f8SFelix Fietkau 		skb_list_del_init(skb);
10353298b1f8SFelix Fietkau 		napi_gro_receive(napi, skb);
10363298b1f8SFelix Fietkau 	}
10379d9d738bSFelix Fietkau }
10389d9d738bSFelix Fietkau 
103981e850efSLorenzo Bianconi void mt76_rx_poll_complete(struct mt76_dev *dev, enum mt76_rxq_id q,
104081e850efSLorenzo Bianconi 			   struct napi_struct *napi)
10419d9d738bSFelix Fietkau {
1042aee5b8cfSFelix Fietkau 	struct sk_buff_head frames;
104317f1de56SFelix Fietkau 	struct sk_buff *skb;
104417f1de56SFelix Fietkau 
1045aee5b8cfSFelix Fietkau 	__skb_queue_head_init(&frames);
1046aee5b8cfSFelix Fietkau 
1047d71ef286SFelix Fietkau 	while ((skb = __skb_dequeue(&dev->rx_skb[q])) != NULL) {
1048ef13edc0SFelix Fietkau 		mt76_check_sta(dev, skb);
1049aee5b8cfSFelix Fietkau 		mt76_rx_aggr_reorder(skb, &frames);
1050d71ef286SFelix Fietkau 	}
1051aee5b8cfSFelix Fietkau 
105281e850efSLorenzo Bianconi 	mt76_rx_complete(dev, &frames, napi);
10534e34249eSFelix Fietkau }
105481e850efSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76_rx_poll_complete);
1055723b90dcSFelix Fietkau 
1056e28487eaSFelix Fietkau static int
1057e28487eaSFelix Fietkau mt76_sta_add(struct mt76_dev *dev, struct ieee80211_vif *vif,
1058426e8e41SFelix Fietkau 	     struct ieee80211_sta *sta, bool ext_phy)
1059e28487eaSFelix Fietkau {
1060e28487eaSFelix Fietkau 	struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
1061e28487eaSFelix Fietkau 	int ret;
1062e28487eaSFelix Fietkau 	int i;
1063e28487eaSFelix Fietkau 
1064e28487eaSFelix Fietkau 	mutex_lock(&dev->mutex);
1065e28487eaSFelix Fietkau 
1066e28487eaSFelix Fietkau 	ret = dev->drv->sta_add(dev, vif, sta);
1067e28487eaSFelix Fietkau 	if (ret)
1068e28487eaSFelix Fietkau 		goto out;
1069e28487eaSFelix Fietkau 
1070e28487eaSFelix Fietkau 	for (i = 0; i < ARRAY_SIZE(sta->txq); i++) {
1071e28487eaSFelix Fietkau 		struct mt76_txq *mtxq;
1072e28487eaSFelix Fietkau 
1073e28487eaSFelix Fietkau 		if (!sta->txq[i])
1074e28487eaSFelix Fietkau 			continue;
1075e28487eaSFelix Fietkau 
1076e28487eaSFelix Fietkau 		mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv;
1077e28487eaSFelix Fietkau 		mtxq->wcid = wcid;
1078e28487eaSFelix Fietkau 	}
1079e28487eaSFelix Fietkau 
1080ef13edc0SFelix Fietkau 	ewma_signal_init(&wcid->rssi);
1081426e8e41SFelix Fietkau 	if (ext_phy)
1082426e8e41SFelix Fietkau 		mt76_wcid_mask_set(dev->wcid_phy_mask, wcid->idx);
1083c7d2d631SFelix Fietkau 	wcid->ext_phy = ext_phy;
1084e28487eaSFelix Fietkau 	rcu_assign_pointer(dev->wcid[wcid->idx], wcid);
1085e28487eaSFelix Fietkau 
1086e28487eaSFelix Fietkau out:
1087e28487eaSFelix Fietkau 	mutex_unlock(&dev->mutex);
1088e28487eaSFelix Fietkau 
1089e28487eaSFelix Fietkau 	return ret;
1090e28487eaSFelix Fietkau }
1091e28487eaSFelix Fietkau 
109213f61dfcSLorenzo Bianconi void __mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
1093723b90dcSFelix Fietkau 		       struct ieee80211_sta *sta)
1094723b90dcSFelix Fietkau {
1095723b90dcSFelix Fietkau 	struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
109613f61dfcSLorenzo Bianconi 	int i, idx = wcid->idx;
1097723b90dcSFelix Fietkau 
109858bab0d4SFelix Fietkau 	for (i = 0; i < ARRAY_SIZE(wcid->aggr); i++)
109958bab0d4SFelix Fietkau 		mt76_rx_aggr_stop(dev, wcid, i);
110058bab0d4SFelix Fietkau 
1101e28487eaSFelix Fietkau 	if (dev->drv->sta_remove)
1102e28487eaSFelix Fietkau 		dev->drv->sta_remove(dev, vif, sta);
1103e28487eaSFelix Fietkau 
1104723b90dcSFelix Fietkau 	mt76_tx_status_check(dev, wcid, true);
1105426e8e41SFelix Fietkau 	mt76_wcid_mask_clear(dev->wcid_mask, idx);
1106426e8e41SFelix Fietkau 	mt76_wcid_mask_clear(dev->wcid_phy_mask, idx);
110713f61dfcSLorenzo Bianconi }
110813f61dfcSLorenzo Bianconi EXPORT_SYMBOL_GPL(__mt76_sta_remove);
1109e28487eaSFelix Fietkau 
111013f61dfcSLorenzo Bianconi static void
111113f61dfcSLorenzo Bianconi mt76_sta_remove(struct mt76_dev *dev, struct ieee80211_vif *vif,
111213f61dfcSLorenzo Bianconi 		struct ieee80211_sta *sta)
111313f61dfcSLorenzo Bianconi {
111413f61dfcSLorenzo Bianconi 	mutex_lock(&dev->mutex);
111513f61dfcSLorenzo Bianconi 	__mt76_sta_remove(dev, vif, sta);
1116723b90dcSFelix Fietkau 	mutex_unlock(&dev->mutex);
1117723b90dcSFelix Fietkau }
1118e28487eaSFelix Fietkau 
1119e28487eaSFelix Fietkau int mt76_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
1120e28487eaSFelix Fietkau 		   struct ieee80211_sta *sta,
1121e28487eaSFelix Fietkau 		   enum ieee80211_sta_state old_state,
1122e28487eaSFelix Fietkau 		   enum ieee80211_sta_state new_state)
1123e28487eaSFelix Fietkau {
1124426e8e41SFelix Fietkau 	struct mt76_phy *phy = hw->priv;
1125426e8e41SFelix Fietkau 	struct mt76_dev *dev = phy->dev;
1126426e8e41SFelix Fietkau 	bool ext_phy = phy != &dev->phy;
1127e28487eaSFelix Fietkau 
1128e28487eaSFelix Fietkau 	if (old_state == IEEE80211_STA_NOTEXIST &&
1129e28487eaSFelix Fietkau 	    new_state == IEEE80211_STA_NONE)
1130426e8e41SFelix Fietkau 		return mt76_sta_add(dev, vif, sta, ext_phy);
1131e28487eaSFelix Fietkau 
11329c193de5SFelix Fietkau 	if (old_state == IEEE80211_STA_AUTH &&
11339c193de5SFelix Fietkau 	    new_state == IEEE80211_STA_ASSOC &&
11349c193de5SFelix Fietkau 	    dev->drv->sta_assoc)
11359c193de5SFelix Fietkau 		dev->drv->sta_assoc(dev, vif, sta);
11369c193de5SFelix Fietkau 
1137e28487eaSFelix Fietkau 	if (old_state == IEEE80211_STA_NONE &&
1138e28487eaSFelix Fietkau 	    new_state == IEEE80211_STA_NOTEXIST)
1139e28487eaSFelix Fietkau 		mt76_sta_remove(dev, vif, sta);
1140e28487eaSFelix Fietkau 
1141e28487eaSFelix Fietkau 	return 0;
1142e28487eaSFelix Fietkau }
1143e28487eaSFelix Fietkau EXPORT_SYMBOL_GPL(mt76_sta_state);
11449313faacSFelix Fietkau 
114543ba1922SFelix Fietkau void mt76_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
114643ba1922SFelix Fietkau 			     struct ieee80211_sta *sta)
114743ba1922SFelix Fietkau {
114843ba1922SFelix Fietkau 	struct mt76_phy *phy = hw->priv;
114943ba1922SFelix Fietkau 	struct mt76_dev *dev = phy->dev;
115043ba1922SFelix Fietkau 	struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
115143ba1922SFelix Fietkau 
115243ba1922SFelix Fietkau 	mutex_lock(&dev->mutex);
115343ba1922SFelix Fietkau 	rcu_assign_pointer(dev->wcid[wcid->idx], NULL);
115443ba1922SFelix Fietkau 	mutex_unlock(&dev->mutex);
115543ba1922SFelix Fietkau }
115643ba1922SFelix Fietkau EXPORT_SYMBOL_GPL(mt76_sta_pre_rcu_remove);
115743ba1922SFelix Fietkau 
11589313faacSFelix Fietkau int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
11599313faacSFelix Fietkau 		     int *dbm)
11609313faacSFelix Fietkau {
1161beaaeb6bSFelix Fietkau 	struct mt76_phy *phy = hw->priv;
1162beaaeb6bSFelix Fietkau 	int n_chains = hweight8(phy->antenna_mask);
116307cda406SFelix Fietkau 	int delta = mt76_tx_power_nss_delta(n_chains);
11649313faacSFelix Fietkau 
116507cda406SFelix Fietkau 	*dbm = DIV_ROUND_UP(phy->txpower_cur + delta, 2);
11669313faacSFelix Fietkau 
11679313faacSFelix Fietkau 	return 0;
11689313faacSFelix Fietkau }
11699313faacSFelix Fietkau EXPORT_SYMBOL_GPL(mt76_get_txpower);
1170e7173858SFelix Fietkau 
1171e7173858SFelix Fietkau static void
1172e7173858SFelix Fietkau __mt76_csa_finish(void *priv, u8 *mac, struct ieee80211_vif *vif)
1173e7173858SFelix Fietkau {
11748552a434SJohn Crispin 	if (vif->csa_active && ieee80211_beacon_cntdwn_is_complete(vif))
1175e7173858SFelix Fietkau 		ieee80211_csa_finish(vif);
1176e7173858SFelix Fietkau }
1177e7173858SFelix Fietkau 
1178e7173858SFelix Fietkau void mt76_csa_finish(struct mt76_dev *dev)
1179e7173858SFelix Fietkau {
1180e7173858SFelix Fietkau 	if (!dev->csa_complete)
1181e7173858SFelix Fietkau 		return;
1182e7173858SFelix Fietkau 
1183e7173858SFelix Fietkau 	ieee80211_iterate_active_interfaces_atomic(dev->hw,
1184e7173858SFelix Fietkau 		IEEE80211_IFACE_ITER_RESUME_ALL,
1185e7173858SFelix Fietkau 		__mt76_csa_finish, dev);
1186e7173858SFelix Fietkau 
1187e7173858SFelix Fietkau 	dev->csa_complete = 0;
1188e7173858SFelix Fietkau }
1189e7173858SFelix Fietkau EXPORT_SYMBOL_GPL(mt76_csa_finish);
1190e7173858SFelix Fietkau 
1191e7173858SFelix Fietkau static void
1192e7173858SFelix Fietkau __mt76_csa_check(void *priv, u8 *mac, struct ieee80211_vif *vif)
1193e7173858SFelix Fietkau {
1194e7173858SFelix Fietkau 	struct mt76_dev *dev = priv;
1195e7173858SFelix Fietkau 
1196e7173858SFelix Fietkau 	if (!vif->csa_active)
1197e7173858SFelix Fietkau 		return;
1198e7173858SFelix Fietkau 
11998552a434SJohn Crispin 	dev->csa_complete |= ieee80211_beacon_cntdwn_is_complete(vif);
1200e7173858SFelix Fietkau }
1201e7173858SFelix Fietkau 
1202e7173858SFelix Fietkau void mt76_csa_check(struct mt76_dev *dev)
1203e7173858SFelix Fietkau {
1204e7173858SFelix Fietkau 	ieee80211_iterate_active_interfaces_atomic(dev->hw,
1205e7173858SFelix Fietkau 		IEEE80211_IFACE_ITER_RESUME_ALL,
1206e7173858SFelix Fietkau 		__mt76_csa_check, dev);
1207e7173858SFelix Fietkau }
1208e7173858SFelix Fietkau EXPORT_SYMBOL_GPL(mt76_csa_check);
120987d53103SStanislaw Gruszka 
121087d53103SStanislaw Gruszka int
121187d53103SStanislaw Gruszka mt76_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
121287d53103SStanislaw Gruszka {
121387d53103SStanislaw Gruszka 	return 0;
121487d53103SStanislaw Gruszka }
121587d53103SStanislaw Gruszka EXPORT_SYMBOL_GPL(mt76_set_tim);
1216eadfd98fSLorenzo Bianconi 
1217eadfd98fSLorenzo Bianconi void mt76_insert_ccmp_hdr(struct sk_buff *skb, u8 key_id)
1218eadfd98fSLorenzo Bianconi {
1219eadfd98fSLorenzo Bianconi 	struct mt76_rx_status *status = (struct mt76_rx_status *)skb->cb;
1220eadfd98fSLorenzo Bianconi 	int hdr_len = ieee80211_get_hdrlen_from_skb(skb);
1221eadfd98fSLorenzo Bianconi 	u8 *hdr, *pn = status->iv;
1222eadfd98fSLorenzo Bianconi 
1223eadfd98fSLorenzo Bianconi 	__skb_push(skb, 8);
1224eadfd98fSLorenzo Bianconi 	memmove(skb->data, skb->data + 8, hdr_len);
1225eadfd98fSLorenzo Bianconi 	hdr = skb->data + hdr_len;
1226eadfd98fSLorenzo Bianconi 
1227eadfd98fSLorenzo Bianconi 	hdr[0] = pn[5];
1228eadfd98fSLorenzo Bianconi 	hdr[1] = pn[4];
1229eadfd98fSLorenzo Bianconi 	hdr[2] = 0;
1230eadfd98fSLorenzo Bianconi 	hdr[3] = 0x20 | (key_id << 6);
1231eadfd98fSLorenzo Bianconi 	hdr[4] = pn[3];
1232eadfd98fSLorenzo Bianconi 	hdr[5] = pn[2];
1233eadfd98fSLorenzo Bianconi 	hdr[6] = pn[1];
1234eadfd98fSLorenzo Bianconi 	hdr[7] = pn[0];
1235eadfd98fSLorenzo Bianconi 
1236eadfd98fSLorenzo Bianconi 	status->flag &= ~RX_FLAG_IV_STRIPPED;
1237eadfd98fSLorenzo Bianconi }
1238eadfd98fSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76_insert_ccmp_hdr);
1239d2679d65SLorenzo Bianconi 
1240d2679d65SLorenzo Bianconi int mt76_get_rate(struct mt76_dev *dev,
1241d2679d65SLorenzo Bianconi 		  struct ieee80211_supported_band *sband,
1242d2679d65SLorenzo Bianconi 		  int idx, bool cck)
1243d2679d65SLorenzo Bianconi {
1244d2679d65SLorenzo Bianconi 	int i, offset = 0, len = sband->n_bitrates;
1245d2679d65SLorenzo Bianconi 
1246d2679d65SLorenzo Bianconi 	if (cck) {
124796747a51SFelix Fietkau 		if (sband == &dev->phy.sband_5g.sband)
1248d2679d65SLorenzo Bianconi 			return 0;
1249d2679d65SLorenzo Bianconi 
1250d2679d65SLorenzo Bianconi 		idx &= ~BIT(2); /* short preamble */
125196747a51SFelix Fietkau 	} else if (sband == &dev->phy.sband_2g.sband) {
1252d2679d65SLorenzo Bianconi 		offset = 4;
1253d2679d65SLorenzo Bianconi 	}
1254d2679d65SLorenzo Bianconi 
1255d2679d65SLorenzo Bianconi 	for (i = offset; i < len; i++) {
1256d2679d65SLorenzo Bianconi 		if ((sband->bitrates[i].hw_value & GENMASK(7, 0)) == idx)
1257d2679d65SLorenzo Bianconi 			return i;
1258d2679d65SLorenzo Bianconi 	}
1259d2679d65SLorenzo Bianconi 
1260d2679d65SLorenzo Bianconi 	return 0;
1261d2679d65SLorenzo Bianconi }
1262d2679d65SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76_get_rate);
12638b8ab5c2SLorenzo Bianconi 
12648b8ab5c2SLorenzo Bianconi void mt76_sw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
12658b8ab5c2SLorenzo Bianconi 		  const u8 *mac)
12668b8ab5c2SLorenzo Bianconi {
1267011849e0SFelix Fietkau 	struct mt76_phy *phy = hw->priv;
12688b8ab5c2SLorenzo Bianconi 
1269011849e0SFelix Fietkau 	set_bit(MT76_SCANNING, &phy->state);
12708b8ab5c2SLorenzo Bianconi }
12718b8ab5c2SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76_sw_scan);
12728b8ab5c2SLorenzo Bianconi 
12738b8ab5c2SLorenzo Bianconi void mt76_sw_scan_complete(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
12748b8ab5c2SLorenzo Bianconi {
1275011849e0SFelix Fietkau 	struct mt76_phy *phy = hw->priv;
12768b8ab5c2SLorenzo Bianconi 
1277011849e0SFelix Fietkau 	clear_bit(MT76_SCANNING, &phy->state);
12788b8ab5c2SLorenzo Bianconi }
12798b8ab5c2SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76_sw_scan_complete);
1280e49c76d4SLorenzo Bianconi 
1281e49c76d4SLorenzo Bianconi int mt76_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant)
1282e49c76d4SLorenzo Bianconi {
1283beaaeb6bSFelix Fietkau 	struct mt76_phy *phy = hw->priv;
1284beaaeb6bSFelix Fietkau 	struct mt76_dev *dev = phy->dev;
1285e49c76d4SLorenzo Bianconi 
1286e49c76d4SLorenzo Bianconi 	mutex_lock(&dev->mutex);
1287beaaeb6bSFelix Fietkau 	*tx_ant = phy->antenna_mask;
1288beaaeb6bSFelix Fietkau 	*rx_ant = phy->antenna_mask;
1289e49c76d4SLorenzo Bianconi 	mutex_unlock(&dev->mutex);
1290e49c76d4SLorenzo Bianconi 
1291e49c76d4SLorenzo Bianconi 	return 0;
1292e49c76d4SLorenzo Bianconi }
1293e49c76d4SLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76_get_antenna);
1294b671da33SLorenzo Bianconi 
1295b1cb42adSLorenzo Bianconi struct mt76_queue *
1296b1cb42adSLorenzo Bianconi mt76_init_queue(struct mt76_dev *dev, int qid, int idx, int n_desc,
1297b1cb42adSLorenzo Bianconi 		int ring_base)
1298b671da33SLorenzo Bianconi {
1299b671da33SLorenzo Bianconi 	struct mt76_queue *hwq;
1300b671da33SLorenzo Bianconi 	int err;
1301b671da33SLorenzo Bianconi 
1302b671da33SLorenzo Bianconi 	hwq = devm_kzalloc(dev->dev, sizeof(*hwq), GFP_KERNEL);
1303b671da33SLorenzo Bianconi 	if (!hwq)
1304b1cb42adSLorenzo Bianconi 		return ERR_PTR(-ENOMEM);
1305b671da33SLorenzo Bianconi 
1306b671da33SLorenzo Bianconi 	err = dev->queue_ops->alloc(dev, hwq, idx, n_desc, 0, ring_base);
1307b671da33SLorenzo Bianconi 	if (err < 0)
1308b1cb42adSLorenzo Bianconi 		return ERR_PTR(err);
1309b671da33SLorenzo Bianconi 
1310b1cb42adSLorenzo Bianconi 	return hwq;
1311b671da33SLorenzo Bianconi }
1312b1cb42adSLorenzo Bianconi EXPORT_SYMBOL_GPL(mt76_init_queue);
1313