xref: /linux/drivers/net/wireless/morsemicro/mm81x/mac.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
1b1906ceaSLachlan Hodges // SPDX-License-Identifier: GPL-2.0-only
2b1906ceaSLachlan Hodges /*
3b1906ceaSLachlan Hodges  * Copyright (c) 2017-2026 Morse Micro
4b1906ceaSLachlan Hodges  */
5b1906ceaSLachlan Hodges #include "core.h"
6b1906ceaSLachlan Hodges #include <linux/slab.h>
7b1906ceaSLachlan Hodges #include <linux/jiffies.h>
8b1906ceaSLachlan Hodges #include <linux/crc32.h>
9b1906ceaSLachlan Hodges #include <net/mac80211.h>
10b1906ceaSLachlan Hodges #include <asm/div64.h>
11b1906ceaSLachlan Hodges #include <linux/kernel.h>
12b1906ceaSLachlan Hodges #include "hif.h"
13b1906ceaSLachlan Hodges #include "mac.h"
14b1906ceaSLachlan Hodges #include "bus.h"
15b1906ceaSLachlan Hodges #include "ps.h"
16b1906ceaSLachlan Hodges #include "rc.h"
17b1906ceaSLachlan Hodges 
18b1906ceaSLachlan Hodges /*
19b1906ceaSLachlan Hodges  * Arbitrary size limit for the filter command address list, to ensure that
20b1906ceaSLachlan Hodges  * the command does not exceed page/MTU size. This will be far greater than
21b1906ceaSLachlan Hodges  * the number of filters supported by the firmware.
22b1906ceaSLachlan Hodges  */
23b1906ceaSLachlan Hodges #define MCAST_FILTER_COUNT_MAX (1024 / sizeof(filter->addr_list[0]))
24b1906ceaSLachlan Hodges 
25b1906ceaSLachlan Hodges /* Calculate average RSSI for Rx status */
26b1906ceaSLachlan Hodges #define CALC_AVG_RSSI(_avg, _sample) ((((_avg) * 9 + (_sample)) / 10))
27b1906ceaSLachlan Hodges 
28b1906ceaSLachlan Hodges /*
29b1906ceaSLachlan Hodges  * When automatically trying MCS0 before MCS10, this is how many
30b1906ceaSLachlan Hodges  * MCS0 attempts to make
31b1906ceaSLachlan Hodges  */
32b1906ceaSLachlan Hodges #define MCS0_BEFORE_MCS10_COUNT (1)
33b1906ceaSLachlan Hodges 
34b1906ceaSLachlan Hodges /* Maximum TX power (default) */
35b1906ceaSLachlan Hodges #define MAX_TX_POWER_MBM (2200)
36b1906ceaSLachlan Hodges 
37b1906ceaSLachlan Hodges /*
38b1906ceaSLachlan Hodges  * Since S1G runs at 1/10th the clockrate of VHT, the worst-case
39b1906ceaSLachlan Hodges  * transmission time is significantly longer then that of non-S1G
40b1906ceaSLachlan Hodges  * PHYs.
41b1906ceaSLachlan Hodges  */
42b1906ceaSLachlan Hodges #define MM81X_FLUSH_TIMEOUT (16 * HZ)
43b1906ceaSLachlan Hodges 
44b1906ceaSLachlan Hodges /* Default queue count */
45b1906ceaSLachlan Hodges #define MM81X_HW_QUEUE_COUNT (4)
46b1906ceaSLachlan Hodges 
47b1906ceaSLachlan Hodges /* Max rates per skb */
48b1906ceaSLachlan Hodges #define MM81X_HW_MAX_RATES (4)
49b1906ceaSLachlan Hodges 
50b1906ceaSLachlan Hodges /* Max reported rates */
51b1906ceaSLachlan Hodges #define MM81X_HW_MAX_REPORT_RATES (4)
52b1906ceaSLachlan Hodges 
53b1906ceaSLachlan Hodges /* Max rate attempts */
54b1906ceaSLachlan Hodges #define MM81X_HW_MAX_RATE_TRIES (1)
55b1906ceaSLachlan Hodges 
56b1906ceaSLachlan Hodges /* Max sk pacing shift */
57b1906ceaSLachlan Hodges #define MM81X_HW_TX_SK_PACING_SHIFT (3)
58b1906ceaSLachlan Hodges 
59b1906ceaSLachlan Hodges /* NSS/MCS map values */
60b1906ceaSLachlan Hodges #define MM81X_NSS_MCS_BYTE_0 0xfe /* 1SS */
61b1906ceaSLachlan Hodges #define MM81X_NSS_MCS_BYTE_1 0x00
62b1906ceaSLachlan Hodges #define MM81X_NSS_MCS_BYTE_2 0xfc /* 1SS */
63b1906ceaSLachlan Hodges #define MM81X_NSS_MCS_BYTE_3 0x01
64b1906ceaSLachlan Hodges #define MM81X_NSS_MCS_BYTE_4 0x00
65b1906ceaSLachlan Hodges 
66b1906ceaSLachlan Hodges /* HW restart delay time before terminating hardware IF work items */
67b1906ceaSLachlan Hodges #define MM81X_HW_RESTART_DELAY_MS 20
68b1906ceaSLachlan Hodges 
69b1906ceaSLachlan Hodges /* clang-format off */
70b1906ceaSLachlan Hodges 
71b1906ceaSLachlan Hodges /* mm81x chips do not support 16MHz */
72b1906ceaSLachlan Hodges #define CHANS1G(channel, frequency, offset, chan_flags)		\
73b1906ceaSLachlan Hodges {								\
74b1906ceaSLachlan Hodges 	.band = NL80211_BAND_S1GHZ,				\
75b1906ceaSLachlan Hodges 	.center_freq = (frequency),				\
76b1906ceaSLachlan Hodges 	.freq_offset = (offset),				\
77b1906ceaSLachlan Hodges 	.hw_value = (channel),					\
78b1906ceaSLachlan Hodges 	.flags = ((chan_flags) | IEEE80211_CHAN_NO_16MHZ),	\
79b1906ceaSLachlan Hodges 	.max_antenna_gain = 0,					\
80b1906ceaSLachlan Hodges 	.max_power = 30,					\
81b1906ceaSLachlan Hodges }
82b1906ceaSLachlan Hodges 
83b1906ceaSLachlan Hodges static struct ieee80211_channel mors_s1ghz_channels[] = {
84b1906ceaSLachlan Hodges 	CHANS1G(1, 902, 500, IEEE80211_CHAN_S1G_NO_PRIMARY),
85b1906ceaSLachlan Hodges 	CHANS1G(3, 903, 500, 0),
86b1906ceaSLachlan Hodges 	CHANS1G(5, 904, 500, 0),
87b1906ceaSLachlan Hodges 	CHANS1G(7, 905, 500, 0),
88b1906ceaSLachlan Hodges 	CHANS1G(9, 906, 500, 0),
89b1906ceaSLachlan Hodges 	CHANS1G(11, 907, 500, 0),
90b1906ceaSLachlan Hodges 	CHANS1G(13, 908, 500, 0),
91b1906ceaSLachlan Hodges 	CHANS1G(15, 909, 500, 0),
92b1906ceaSLachlan Hodges 	CHANS1G(17, 910, 500, 0),
93b1906ceaSLachlan Hodges 	CHANS1G(19, 911, 500, 0),
94b1906ceaSLachlan Hodges 	CHANS1G(21, 912, 500, 0),
95b1906ceaSLachlan Hodges 	CHANS1G(23, 913, 500, 0),
96b1906ceaSLachlan Hodges 	CHANS1G(25, 914, 500, 0),
97b1906ceaSLachlan Hodges 	CHANS1G(27, 915, 500, 0),
98b1906ceaSLachlan Hodges 	CHANS1G(29, 916, 500, 0),
99b1906ceaSLachlan Hodges 	CHANS1G(31, 917, 500, 0),
100b1906ceaSLachlan Hodges 	CHANS1G(33, 918, 500, 0),
101b1906ceaSLachlan Hodges 	CHANS1G(35, 919, 500, 0),
102b1906ceaSLachlan Hodges 	CHANS1G(37, 920, 500, 0),
103b1906ceaSLachlan Hodges 	CHANS1G(39, 921, 500, 0),
104b1906ceaSLachlan Hodges 	CHANS1G(41, 922, 500, 0),
105b1906ceaSLachlan Hodges 	CHANS1G(43, 923, 500, 0),
106b1906ceaSLachlan Hodges 	CHANS1G(45, 924, 500, 0),
107b1906ceaSLachlan Hodges 	CHANS1G(47, 925, 500, 0),
108b1906ceaSLachlan Hodges 	CHANS1G(49, 926, 500, 0),
109b1906ceaSLachlan Hodges 	CHANS1G(51, 927, 500, IEEE80211_CHAN_S1G_NO_PRIMARY),
110b1906ceaSLachlan Hodges };
111b1906ceaSLachlan Hodges 
112b1906ceaSLachlan Hodges /* clang-format on */
113b1906ceaSLachlan Hodges 
114b1906ceaSLachlan Hodges static struct ieee80211_supported_band mors_band_s1ghz = {
115b1906ceaSLachlan Hodges 	.band = NL80211_BAND_S1GHZ,
116b1906ceaSLachlan Hodges 	.s1g_cap.s1g = true,
117b1906ceaSLachlan Hodges 	.channels = mors_s1ghz_channels,
118b1906ceaSLachlan Hodges 	.n_channels = ARRAY_SIZE(mors_s1ghz_channels),
119b1906ceaSLachlan Hodges 	.bitrates = NULL,
120b1906ceaSLachlan Hodges 	.n_bitrates = 0,
121b1906ceaSLachlan Hodges 	.s1g_cap.cap[4] = 0x80 /* STA type sensor only for AP & STA */
122b1906ceaSLachlan Hodges };
123b1906ceaSLachlan Hodges 
124b1906ceaSLachlan Hodges static struct ieee80211_iface_limit mors_if_limits[] = {
125b1906ceaSLachlan Hodges 	{
126b1906ceaSLachlan Hodges 		.max = MM81X_MAX_IF,
127b1906ceaSLachlan Hodges 		.types = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP),
128b1906ceaSLachlan Hodges 	},
129b1906ceaSLachlan Hodges };
130b1906ceaSLachlan Hodges 
131b1906ceaSLachlan Hodges static struct ieee80211_iface_combination mors_if_combs[] = {
132b1906ceaSLachlan Hodges 	{
133b1906ceaSLachlan Hodges 		.limits = mors_if_limits,
134b1906ceaSLachlan Hodges 		.n_limits = ARRAY_SIZE(mors_if_limits),
135b1906ceaSLachlan Hodges 		.max_interfaces = MM81X_MAX_IF,
136b1906ceaSLachlan Hodges 		.num_different_channels = 1,
137b1906ceaSLachlan Hodges 	},
138b1906ceaSLachlan Hodges };
139b1906ceaSLachlan Hodges 
140b1906ceaSLachlan Hodges /* Convert from a time in time units (1024us) to us */
141b1906ceaSLachlan Hodges #define MM81X_TU_TO_US(x) ((x) * 1024UL)
142b1906ceaSLachlan Hodges 
143b1906ceaSLachlan Hodges /* Convert from a time in time units (1024us) to ms */
144b1906ceaSLachlan Hodges #define MM81X_TU_TO_MS(x) (MM81X_TU_TO_US(x) / 1000UL)
145b1906ceaSLachlan Hodges 
146b1906ceaSLachlan Hodges /* Default time to dwell on a scan channel */
147b1906ceaSLachlan Hodges #define MM81X_HWSCAN_DEFAULT_DWELL_TIME_MS (30)
148b1906ceaSLachlan Hodges 
149b1906ceaSLachlan Hodges /* Default time to dwell on a scan channel for passive scan */
150b1906ceaSLachlan Hodges #define MM81X_HWSCAN_DEFAULT_PASSIVE_DWELL_TIME_MS (110)
151b1906ceaSLachlan Hodges 
152b1906ceaSLachlan Hodges /* Default time to dwell on home channel, in between scan channels */
153b1906ceaSLachlan Hodges #define MM81X_HWSCAN_DEFAULT_DWELL_ON_HOME_MS (200)
154b1906ceaSLachlan Hodges 
155b1906ceaSLachlan Hodges /* Typical time it takes to send the probe */
156b1906ceaSLachlan Hodges #define MM81X_HWSCAN_PROBE_DELAY_MS (30)
157b1906ceaSLachlan Hodges 
158b1906ceaSLachlan Hodges /* A margin to account for event/command processing */
159b1906ceaSLachlan Hodges #define MM81X_HWSCAN_TIMEOUT_OVERHEAD_MS (2000)
160b1906ceaSLachlan Hodges 
161b1906ceaSLachlan Hodges /* Scan channel frequency mask */
162b1906ceaSLachlan Hodges #define HW_SCAN_CH_LIST_FREQ_KHZ GENMASK(19, 0)
163b1906ceaSLachlan Hodges 
164b1906ceaSLachlan Hodges /*
165b1906ceaSLachlan Hodges  * Scan channel bandwidth mask.
166b1906ceaSLachlan Hodges  * Encoded as: 0 = 1MHz, 1 = 2MHz, 2 = 4MHz, 3 = 8MHz
167b1906ceaSLachlan Hodges  */
168b1906ceaSLachlan Hodges #define HW_SCAN_CH_LIST_OP_BW GENMASK(21, 20)
169b1906ceaSLachlan Hodges 
170b1906ceaSLachlan Hodges /*
171b1906ceaSLachlan Hodges  * Scan channel primary channel width.
172b1906ceaSLachlan Hodges  * Encoded as: 0 = 1MHz, 1 = 2MHz
173b1906ceaSLachlan Hodges  */
174b1906ceaSLachlan Hodges #define HW_SCAN_CH_LIST_PRIM_CH_WIDTH BIT(22)
175b1906ceaSLachlan Hodges 
176b1906ceaSLachlan Hodges /* Index into power_list for tx power of channel */
177b1906ceaSLachlan Hodges #define HW_SCAN_CH_LIST_PWR_LIST_IDX GENMASK(31, 26)
178b1906ceaSLachlan Hodges 
179b1906ceaSLachlan Hodges struct hw_scan_tlv_hdr {
180b1906ceaSLachlan Hodges 	__le16 tag;
181b1906ceaSLachlan Hodges 	__le16 len;
182b1906ceaSLachlan Hodges } __packed;
183b1906ceaSLachlan Hodges 
184b1906ceaSLachlan Hodges struct hw_scan_tlv_channel_list {
185b1906ceaSLachlan Hodges 	struct hw_scan_tlv_hdr hdr;
186b1906ceaSLachlan Hodges 	__le32 channels[];
187b1906ceaSLachlan Hodges } __packed;
188b1906ceaSLachlan Hodges 
189b1906ceaSLachlan Hodges struct hw_scan_tlv_power_list {
190b1906ceaSLachlan Hodges 	struct hw_scan_tlv_hdr hdr;
191b1906ceaSLachlan Hodges 	s32 tx_power_qdbm[];
192b1906ceaSLachlan Hodges } __packed;
193b1906ceaSLachlan Hodges 
194b1906ceaSLachlan Hodges struct hw_scan_tlv_probe_req {
195b1906ceaSLachlan Hodges 	struct hw_scan_tlv_hdr hdr;
196b1906ceaSLachlan Hodges 	/* Probe request frame template (including SSIDs) */
197b1906ceaSLachlan Hodges 	u8 buf[];
198b1906ceaSLachlan Hodges } __packed;
199b1906ceaSLachlan Hodges 
200b1906ceaSLachlan Hodges struct hw_scan_tlv_dwell_on_home {
201b1906ceaSLachlan Hodges 	struct hw_scan_tlv_hdr hdr;
202b1906ceaSLachlan Hodges 	/* Time to dwell on home between scan channels */
203b1906ceaSLachlan Hodges 	__le32 home_dwell_time_ms;
204b1906ceaSLachlan Hodges } __packed;
205b1906ceaSLachlan Hodges 
206b1906ceaSLachlan Hodges #define DOT11AH_BA_MAX_MPDU_PER_AMPDU (32)
207b1906ceaSLachlan Hodges 
208b1906ceaSLachlan Hodges /* wiphy scan params */
209b1906ceaSLachlan Hodges #define MM81X_MAX_SCAN_IE_LEN 512
210b1906ceaSLachlan Hodges #define MM81X_MAX_SCAN_SSIDS 1
211b1906ceaSLachlan Hodges #define MM81X_MAX_REMAIN_ON_CHAN_DURATION 10000
212b1906ceaSLachlan Hodges 
mm81x_reg_h_cc_equal(const char * cc1,const char * cc2)213b1906ceaSLachlan Hodges static bool mm81x_reg_h_cc_equal(const char *cc1, const char *cc2)
214b1906ceaSLachlan Hodges {
215b1906ceaSLachlan Hodges 	return (cc1[0] == cc2[0]) && (cc1[1] == cc2[1]);
216b1906ceaSLachlan Hodges }
217b1906ceaSLachlan Hodges 
mm81x_tx_h_pkt_over_rts_threshold(struct mm81x * mors,struct ieee80211_tx_info * info,struct sk_buff * skb)218b1906ceaSLachlan Hodges static bool mm81x_tx_h_pkt_over_rts_threshold(struct mm81x *mors,
219b1906ceaSLachlan Hodges 					      struct ieee80211_tx_info *info,
220b1906ceaSLachlan Hodges 					      struct sk_buff *skb)
221b1906ceaSLachlan Hodges {
222b1906ceaSLachlan Hodges 	u8 ccmp_len;
223b1906ceaSLachlan Hodges 
224b1906ceaSLachlan Hodges 	if (!info->control.hw_key)
225b1906ceaSLachlan Hodges 		return ((skb->len + FCS_LEN) > mors->rts_threshold);
226b1906ceaSLachlan Hodges 
227b1906ceaSLachlan Hodges 	if (info->control.hw_key->keylen == 32)
228b1906ceaSLachlan Hodges 		ccmp_len =
229b1906ceaSLachlan Hodges 			IEEE80211_CCMP_256_HDR_LEN + IEEE80211_CCMP_256_MIC_LEN;
230b1906ceaSLachlan Hodges 	else if (info->control.hw_key->keylen == 16)
231b1906ceaSLachlan Hodges 		ccmp_len = IEEE80211_CCMP_HDR_LEN + IEEE80211_CCMP_MIC_LEN;
232b1906ceaSLachlan Hodges 	else
233b1906ceaSLachlan Hodges 		ccmp_len = 0;
234b1906ceaSLachlan Hodges 
235b1906ceaSLachlan Hodges 	return ((skb->len + FCS_LEN + ccmp_len) > mors->rts_threshold);
236b1906ceaSLachlan Hodges }
237b1906ceaSLachlan Hodges 
mm81x_tx_h_ps_filtered_for_sta(struct mm81x * mors,struct sk_buff * skb,struct ieee80211_sta * sta)238b1906ceaSLachlan Hodges static bool mm81x_tx_h_ps_filtered_for_sta(struct mm81x *mors,
239b1906ceaSLachlan Hodges 					   struct sk_buff *skb,
240b1906ceaSLachlan Hodges 					   struct ieee80211_sta *sta)
241b1906ceaSLachlan Hodges {
242b1906ceaSLachlan Hodges 	struct mm81x_sta *mors_sta;
243b1906ceaSLachlan Hodges 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
244b1906ceaSLachlan Hodges 
245b1906ceaSLachlan Hodges 	if (!sta)
246b1906ceaSLachlan Hodges 		return false;
247b1906ceaSLachlan Hodges 
248b1906ceaSLachlan Hodges 	mors_sta = (struct mm81x_sta *)sta->drv_priv;
249b1906ceaSLachlan Hodges 
250b1906ceaSLachlan Hodges 	if (!mors_sta->tx_ps_filter_en)
251b1906ceaSLachlan Hodges 		return false;
252b1906ceaSLachlan Hodges 
253b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "Frame for sta[%pM] PS filtered", mors_sta->addr);
254b1906ceaSLachlan Hodges 
255b1906ceaSLachlan Hodges 	info->flags |= IEEE80211_TX_STAT_TX_FILTERED;
256b1906ceaSLachlan Hodges 	info->flags &= ~IEEE80211_TX_CTL_AMPDU;
257b1906ceaSLachlan Hodges 
258b1906ceaSLachlan Hodges 	ieee80211_tx_status_skb(mors->hw, skb);
259b1906ceaSLachlan Hodges 	return true;
260b1906ceaSLachlan Hodges }
261b1906ceaSLachlan Hodges 
mm81x_mac_check_fw_disabled_chans(struct ieee80211_hw * hw)262b1906ceaSLachlan Hodges static void mm81x_mac_check_fw_disabled_chans(struct ieee80211_hw *hw)
263b1906ceaSLachlan Hodges {
264b1906ceaSLachlan Hodges 	int ret = 0;
265b1906ceaSLachlan Hodges 	u32 i;
266b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
267b1906ceaSLachlan Hodges 	struct host_cmd_resp_get_disabled_channels *resp;
268b1906ceaSLachlan Hodges 	u32 resp_len = sizeof(struct host_cmd_disabled_channel_entry) *
269b1906ceaSLachlan Hodges 			       ARRAY_SIZE(mors_s1ghz_channels) +
270b1906ceaSLachlan Hodges 		       sizeof(*resp);
271b1906ceaSLachlan Hodges 
272b1906ceaSLachlan Hodges 	resp = kzalloc(resp_len, GFP_KERNEL);
273b1906ceaSLachlan Hodges 	if (!resp) {
274b1906ceaSLachlan Hodges 		ret = -ENOMEM;
275b1906ceaSLachlan Hodges 		goto out;
276b1906ceaSLachlan Hodges 	}
277b1906ceaSLachlan Hodges 
278b1906ceaSLachlan Hodges 	ret = mm81x_cmd_get_disabled_channels(mors, resp, resp_len);
279b1906ceaSLachlan Hodges 	if (ret)
280b1906ceaSLachlan Hodges 		goto out;
281b1906ceaSLachlan Hodges 
282b1906ceaSLachlan Hodges 	for (i = 0; i < ARRAY_SIZE(mors_s1ghz_channels); i++) {
283b1906ceaSLachlan Hodges 		struct ieee80211_channel *ch = &mors_s1ghz_channels[i];
284b1906ceaSLachlan Hodges 
285b1906ceaSLachlan Hodges 		if (ch->flags & IEEE80211_CHAN_DISABLED)
286b1906ceaSLachlan Hodges 			continue;
287b1906ceaSLachlan Hodges 
288b1906ceaSLachlan Hodges 		ch->flags &= ~IEEE80211_CHAN_S1G_NO_PRIMARY;
289b1906ceaSLachlan Hodges 	}
290b1906ceaSLachlan Hodges 
291b1906ceaSLachlan Hodges 	for (i = 0; i < le32_to_cpu(resp->n_channels); i++) {
292b1906ceaSLachlan Hodges 		struct ieee80211_channel *ch;
293b1906ceaSLachlan Hodges 		struct host_cmd_disabled_channel_entry *entry =
294b1906ceaSLachlan Hodges 			&resp->channels[i];
295b1906ceaSLachlan Hodges 
296b1906ceaSLachlan Hodges 		if (entry->bw_mhz != 1)
297b1906ceaSLachlan Hodges 			continue;
298b1906ceaSLachlan Hodges 
299b1906ceaSLachlan Hodges 		ch = ieee80211_get_channel_khz(
300b1906ceaSLachlan Hodges 			hw->wiphy,
301b1906ceaSLachlan Hodges 			KHZ100_TO_KHZ(le16_to_cpu(entry->freq_100khz)));
302b1906ceaSLachlan Hodges 		if (!ch)
303b1906ceaSLachlan Hodges 			continue;
304b1906ceaSLachlan Hodges 
305b1906ceaSLachlan Hodges 		ch->flags |= IEEE80211_CHAN_S1G_NO_PRIMARY;
306b1906ceaSLachlan Hodges 		dev_dbg(mors->dev, "set NO_PRIMARY on %u KHz",
307b1906ceaSLachlan Hodges 			ieee80211_channel_to_khz(ch));
308b1906ceaSLachlan Hodges 	}
309b1906ceaSLachlan Hodges 
310b1906ceaSLachlan Hodges out:
311b1906ceaSLachlan Hodges 	if (ret)
312b1906ceaSLachlan Hodges 		dev_err(mors->dev, "failed to set disabled primary channels");
313b1906ceaSLachlan Hodges 
314b1906ceaSLachlan Hodges 	kfree(resp);
315b1906ceaSLachlan Hodges }
316b1906ceaSLachlan Hodges 
mm81x_mac_ops_start(struct ieee80211_hw * hw)317b1906ceaSLachlan Hodges static int mm81x_mac_ops_start(struct ieee80211_hw *hw)
318b1906ceaSLachlan Hodges {
319b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
320b1906ceaSLachlan Hodges 
321b1906ceaSLachlan Hodges 	mors->started = true;
322b1906ceaSLachlan Hodges 	return 0;
323b1906ceaSLachlan Hodges }
324b1906ceaSLachlan Hodges 
mm81x_tx_h_get_max_bw(struct mm81x * mors)325b1906ceaSLachlan Hodges static int mm81x_tx_h_get_max_bw(struct mm81x *mors)
326b1906ceaSLachlan Hodges {
327b1906ceaSLachlan Hodges 	return MM81X_FW_SUPP(&mors->fw_caps, 8MHZ) ? 8 :
328b1906ceaSLachlan Hodges 	       MM81X_FW_SUPP(&mors->fw_caps, 4MHZ) ? 4 :
329b1906ceaSLachlan Hodges 	       MM81X_FW_SUPP(&mors->fw_caps, 2MHZ) ? 2 :
330b1906ceaSLachlan Hodges 						     1;
331b1906ceaSLachlan Hodges }
332b1906ceaSLachlan Hodges 
mm81x_mac_caps_init(struct mm81x * mors)333b1906ceaSLachlan Hodges static void mm81x_mac_caps_init(struct mm81x *mors)
334b1906ceaSLachlan Hodges {
335b1906ceaSLachlan Hodges 	struct mm81x_fw_caps *fw_caps = &mors->fw_caps;
336b1906ceaSLachlan Hodges 	struct ieee80211_sta_s1g_cap *s1g = &mors_band_s1ghz.s1g_cap;
337b1906ceaSLachlan Hodges 
338b1906ceaSLachlan Hodges #define __FW_CAP_N(_n, _cap, _bit)                \
339b1906ceaSLachlan Hodges 	do {                                      \
340b1906ceaSLachlan Hodges 		if (MM81X_FW_SUPP(fw_caps, _cap)) \
341b1906ceaSLachlan Hodges 			s1g->cap[_n] |= (_bit);   \
342b1906ceaSLachlan Hodges 	} while (0)
343b1906ceaSLachlan Hodges 
344b1906ceaSLachlan Hodges #define FW_CAP0(_cap, _bit) __FW_CAP_N(0, _cap, _bit)
345b1906ceaSLachlan Hodges #define FW_CAP3(_cap, _bit) __FW_CAP_N(3, _cap, _bit)
346b1906ceaSLachlan Hodges #define FW_CAP5(_cap, _bit) __FW_CAP_N(5, _cap, _bit)
347b1906ceaSLachlan Hodges #define FW_CAP6(_cap, _bit) __FW_CAP_N(6, _cap, _bit)
348b1906ceaSLachlan Hodges #define FW_CAP7(_cap, _bit) __FW_CAP_N(7, _cap, _bit)
349b1906ceaSLachlan Hodges #define FW_CAP8(_cap, _bit) __FW_CAP_N(8, _cap, _bit)
350b1906ceaSLachlan Hodges #define FW_CAP9(_cap, _bit) __FW_CAP_N(9, _cap, _bit)
351b1906ceaSLachlan Hodges 
352b1906ceaSLachlan Hodges 	FW_CAP0(S1G_LONG, S1G_CAP0_S1G_LONG);
353b1906ceaSLachlan Hodges 
354b1906ceaSLachlan Hodges 	s1g->cap[0] |= S1G_CAP0_SGI_1MHZ;
355b1906ceaSLachlan Hodges 	if (MM81X_FW_SUPP(fw_caps, SGI)) {
356b1906ceaSLachlan Hodges 		FW_CAP0(2MHZ, S1G_CAP0_SGI_2MHZ);
357b1906ceaSLachlan Hodges 		FW_CAP0(4MHZ, S1G_CAP0_SGI_4MHZ);
358b1906ceaSLachlan Hodges 		FW_CAP0(8MHZ, S1G_CAP0_SGI_8MHZ);
359b1906ceaSLachlan Hodges 	}
360b1906ceaSLachlan Hodges 
361b1906ceaSLachlan Hodges 	if (MM81X_FW_SUPP(fw_caps, 8MHZ))
362b1906ceaSLachlan Hodges 		s1g->cap[0] |= S1G_SUPP_CH_WIDTH_8;
363b1906ceaSLachlan Hodges 	else if (MM81X_FW_SUPP(fw_caps, 4MHZ))
364b1906ceaSLachlan Hodges 		s1g->cap[0] |= S1G_SUPP_CH_WIDTH_4;
365b1906ceaSLachlan Hodges 	else if (MM81X_FW_SUPP(fw_caps, 2MHZ))
366b1906ceaSLachlan Hodges 		s1g->cap[0] |= S1G_SUPP_CH_WIDTH_2;
367b1906ceaSLachlan Hodges 
368b1906ceaSLachlan Hodges 	FW_CAP3(RD_RESPONDER, S1G_CAP3_RD_RESPONDER);
369b1906ceaSLachlan Hodges 	FW_CAP3(LONG_MPDU, S1G_CAP3_MAX_MPDU_LEN);
370b1906ceaSLachlan Hodges 
371b1906ceaSLachlan Hodges 	FW_CAP5(AMSDU, S1G_CAP5_AMSDU);
372b1906ceaSLachlan Hodges 	FW_CAP5(AMPDU, S1G_CAP5_AMPDU);
373b1906ceaSLachlan Hodges 	FW_CAP5(ASYMMETRIC_BA_SUPPORT, S1G_CAP5_ASYMMETRIC_BA);
374b1906ceaSLachlan Hodges 	FW_CAP5(FLOW_CONTROL, S1G_CAP5_FLOW_CONTROL);
375b1906ceaSLachlan Hodges 
376b1906ceaSLachlan Hodges 	FW_CAP6(OBSS_MITIGATION, S1G_CAP6_OBSS_MITIGATION);
377b1906ceaSLachlan Hodges 	FW_CAP6(FRAGMENT_BA, S1G_CAP6_FRAGMENT_BA);
378b1906ceaSLachlan Hodges 	FW_CAP6(NDP_PSPOLL, S1G_CAP6_NDP_PS_POLL);
379b1906ceaSLachlan Hodges 	FW_CAP6(TXOP_SHARING_IMPLICIT_ACK, S1G_CAP6_TXOP_SHARING_IMP_ACK);
380b1906ceaSLachlan Hodges 	FW_CAP6(HTC_VHT_MFB, S1G_CAP6_VHT_LINK_ADAPT);
381b1906ceaSLachlan Hodges 
382b1906ceaSLachlan Hodges 	FW_CAP7(TACK_AS_PSPOLL, S1G_CAP7_TACK_AS_PS_POLL);
383b1906ceaSLachlan Hodges 	FW_CAP7(DUPLICATE_1MHZ, S1G_CAP7_DUP_1MHZ);
384b1906ceaSLachlan Hodges 	FW_CAP7(MCS_NEGOTIATION, S1G_CAP7_MCS_NEGOTIATION);
385b1906ceaSLachlan Hodges 	FW_CAP7(1MHZ_CONTROL_RESPONSE_PREAMBLE,
386b1906ceaSLachlan Hodges 		S1G_CAP7_1MHZ_CTL_RESPONSE_PREAMBLE);
387b1906ceaSLachlan Hodges 	FW_CAP7(SECTOR_TRAINING, S1G_CAP7_SECTOR_TRAINING_OPERATION);
388b1906ceaSLachlan Hodges 	FW_CAP7(TMP_PS_MODE_SWITCH, S1G_CAP7_TEMP_PS_MODE_SWITCH);
389b1906ceaSLachlan Hodges 
390b1906ceaSLachlan Hodges 	FW_CAP8(BDT, S1G_CAP8_BDT);
391b1906ceaSLachlan Hodges 
392b1906ceaSLachlan Hodges 	FW_CAP9(LINK_ADAPTATION_WO_NDP_CMAC,
393b1906ceaSLachlan Hodges 		S1G_CAP9_LINK_ADAPT_PER_CONTROL_RESPONSE);
394b1906ceaSLachlan Hodges 
395b1906ceaSLachlan Hodges 	/* 1SS MCS 9 for Rx / Tx map */
396b1906ceaSLachlan Hodges 	s1g->nss_mcs[0] = MM81X_NSS_MCS_BYTE_0;
397b1906ceaSLachlan Hodges 	s1g->nss_mcs[1] = MM81X_NSS_MCS_BYTE_1;
398b1906ceaSLachlan Hodges 	s1g->nss_mcs[2] = MM81X_NSS_MCS_BYTE_2;
399b1906ceaSLachlan Hodges 	s1g->nss_mcs[3] = MM81X_NSS_MCS_BYTE_3;
400b1906ceaSLachlan Hodges 	s1g->nss_mcs[4] = MM81X_NSS_MCS_BYTE_4;
401b1906ceaSLachlan Hodges 
402b1906ceaSLachlan Hodges #undef FW_CAP0
403b1906ceaSLachlan Hodges #undef FW_CAP3
404b1906ceaSLachlan Hodges #undef FW_CAP5
405b1906ceaSLachlan Hodges #undef FW_CAP6
406b1906ceaSLachlan Hodges #undef FW_CAP7
407b1906ceaSLachlan Hodges #undef FW_CAP8
408b1906ceaSLachlan Hodges #undef FW_CAP9
409b1906ceaSLachlan Hodges #undef __FW_CAP_N
410b1906ceaSLachlan Hodges }
411b1906ceaSLachlan Hodges 
mm81x_mac_beacon_irq_enable(struct mm81x_vif * mors_vif,bool enable)412b1906ceaSLachlan Hodges static void mm81x_mac_beacon_irq_enable(struct mm81x_vif *mors_vif, bool enable)
413b1906ceaSLachlan Hodges {
414b1906ceaSLachlan Hodges 	struct mm81x *mors = mm81x_vif_to_mors(mors_vif);
415b1906ceaSLachlan Hodges 	u8 beacon_irq_num = MM81X_INT_BEACON_BASE_NUM + mors_vif->id;
416b1906ceaSLachlan Hodges 
417b1906ceaSLachlan Hodges 	enable ? set_bit(beacon_irq_num, &mors->beacon_irqs_enabled) :
418b1906ceaSLachlan Hodges 		 clear_bit(beacon_irq_num, &mors->beacon_irqs_enabled);
419b1906ceaSLachlan Hodges 
420b1906ceaSLachlan Hodges 	mm81x_hw_irq_enable(mors, beacon_irq_num, enable);
421b1906ceaSLachlan Hodges }
422b1906ceaSLachlan Hodges 
mm81x_beacon_h_fill_tx_info(struct mm81x * mors,struct mm81x_skb_tx_info * tx_info,struct mm81x_vif * mors_vif,int tx_bw_mhz)423b1906ceaSLachlan Hodges static void mm81x_beacon_h_fill_tx_info(struct mm81x *mors,
424b1906ceaSLachlan Hodges 					struct mm81x_skb_tx_info *tx_info,
425b1906ceaSLachlan Hodges 					struct mm81x_vif *mors_vif,
426b1906ceaSLachlan Hodges 					int tx_bw_mhz)
427b1906ceaSLachlan Hodges {
428b1906ceaSLachlan Hodges 	enum dot11_bandwidth bw_idx =
429b1906ceaSLachlan Hodges 		mm81x_ratecode_bw_mhz_to_bw_index(tx_bw_mhz);
430b1906ceaSLachlan Hodges 	enum mm81x_rate_preamble pream = MM81X_RATE_PREAMBLE_S1G_SHORT;
431b1906ceaSLachlan Hodges 
432b1906ceaSLachlan Hodges 	tx_info->flags |=
433b1906ceaSLachlan Hodges 		cpu_to_le32(MM81X_TX_CONF_FLAGS_VIF_ID_SET(mors_vif->id));
434b1906ceaSLachlan Hodges 
435b1906ceaSLachlan Hodges 	if (bw_idx == DOT11_BANDWIDTH_1MHZ)
436b1906ceaSLachlan Hodges 		pream = MM81X_RATE_PREAMBLE_S1G_1M;
437b1906ceaSLachlan Hodges 
438b1906ceaSLachlan Hodges 	tx_info->rates[0].count = 1;
439b1906ceaSLachlan Hodges 	tx_info->rates[1].count = 0;
440b1906ceaSLachlan Hodges 	tx_info->rates[0].mm81x_ratecode =
441b1906ceaSLachlan Hodges 		mm81x_ratecode_init(bw_idx, 0, 0, pream);
442b1906ceaSLachlan Hodges 
443b1906ceaSLachlan Hodges 	if (mors->fw_flags & MM81X_FW_FLAGS_REPORTS_TX_BEACON_COMPLETION)
444b1906ceaSLachlan Hodges 		tx_info->flags |=
445b1906ceaSLachlan Hodges 			cpu_to_le32(MM81X_TX_CONF_FLAGS_IMMEDIATE_REPORT);
446b1906ceaSLachlan Hodges }
447b1906ceaSLachlan Hodges 
mm81x_mac_beacon_work(struct work_struct * work)448b1906ceaSLachlan Hodges static void mm81x_mac_beacon_work(struct work_struct *work)
449b1906ceaSLachlan Hodges {
450b1906ceaSLachlan Hodges 	struct mm81x_vif *mors_vif =
451b1906ceaSLachlan Hodges 		from_work(mors_vif, work, u.ap.beacon_work);
452b1906ceaSLachlan Hodges 	struct mm81x *mors = mm81x_vif_to_mors(mors_vif);
453b1906ceaSLachlan Hodges 	struct mm81x_skbq *mq;
454b1906ceaSLachlan Hodges 	struct sk_buff *beacon;
455b1906ceaSLachlan Hodges 	struct ieee80211_vif *vif = mm81x_vif_to_ieee80211_vif(mors_vif);
456b1906ceaSLachlan Hodges 	struct mm81x_skb_tx_info tx_info = { 0 };
457b1906ceaSLachlan Hodges 	int num_bcn_vifs = atomic_read(&mors->num_bcn_vifs);
458b1906ceaSLachlan Hodges 
459b1906ceaSLachlan Hodges 	mq = mm81x_hif_get_tx_beacon_queue(mors);
460b1906ceaSLachlan Hodges 	if (!mq) {
461b1906ceaSLachlan Hodges 		dev_err(mors->dev, "no matching beacon Q found");
462b1906ceaSLachlan Hodges 		return;
463b1906ceaSLachlan Hodges 	}
464b1906ceaSLachlan Hodges 
465b1906ceaSLachlan Hodges 	if (mm81x_skbq_count(mq) >= num_bcn_vifs) {
466b1906ceaSLachlan Hodges 		dev_err(mors->dev,
467b1906ceaSLachlan Hodges 			"previous beacon not consumed, dropping req [id:%d]",
468b1906ceaSLachlan Hodges 			mors_vif->id);
469b1906ceaSLachlan Hodges 		return;
470b1906ceaSLachlan Hodges 	}
471b1906ceaSLachlan Hodges 
472b1906ceaSLachlan Hodges 	beacon = ieee80211_beacon_get(mors->hw, vif, false);
473b1906ceaSLachlan Hodges 	if (!beacon)
474b1906ceaSLachlan Hodges 		return;
475b1906ceaSLachlan Hodges 
476b1906ceaSLachlan Hodges 	mm81x_beacon_h_fill_tx_info(mors, &tx_info, mors_vif,
477b1906ceaSLachlan Hodges 				    cfg80211_chandef_s1g_pri_width(&mors->chandef));
478b1906ceaSLachlan Hodges 	mm81x_skbq_skb_tx(mq, &beacon, &tx_info, MM81X_SKB_CHAN_BEACON);
479b1906ceaSLachlan Hodges }
480b1906ceaSLachlan Hodges 
mm81x_mac_beacon_irq_handle(struct mm81x * mors,u32 status)481b1906ceaSLachlan Hodges void mm81x_mac_beacon_irq_handle(struct mm81x *mors, u32 status)
482b1906ceaSLachlan Hodges {
483b1906ceaSLachlan Hodges 	int vif_id;
484b1906ceaSLachlan Hodges 	unsigned long masked_status = (status & mors->beacon_irqs_enabled) >>
485b1906ceaSLachlan Hodges 				      MM81X_INT_BEACON_BASE_NUM;
486b1906ceaSLachlan Hodges 
487b1906ceaSLachlan Hodges 	guard(rcu)();
488b1906ceaSLachlan Hodges 	for_each_set_bit(vif_id, &masked_status, MM81X_MAX_IF) {
489b1906ceaSLachlan Hodges 		struct mm81x_vif *mors_vif;
490b1906ceaSLachlan Hodges 		struct ieee80211_vif *vif;
491b1906ceaSLachlan Hodges 
492b1906ceaSLachlan Hodges 		vif = mm81x_rcu_dereference_vif_id(mors, vif_id, true);
493b1906ceaSLachlan Hodges 		if (vif) {
494b1906ceaSLachlan Hodges 			mors_vif = ieee80211_vif_to_mors_vif(vif);
495b1906ceaSLachlan Hodges 			queue_work(system_bh_wq, &mors_vif->u.ap.beacon_work);
496b1906ceaSLachlan Hodges 		}
497b1906ceaSLachlan Hodges 	}
498b1906ceaSLachlan Hodges }
499b1906ceaSLachlan Hodges 
mm81x_mac_beacon_init(struct mm81x_vif * mors_vif)500b1906ceaSLachlan Hodges static void mm81x_mac_beacon_init(struct mm81x_vif *mors_vif)
501b1906ceaSLachlan Hodges {
502b1906ceaSLachlan Hodges 	struct mm81x *mors = mm81x_vif_to_mors(mors_vif);
503b1906ceaSLachlan Hodges 
504b1906ceaSLachlan Hodges 	INIT_WORK(&mors_vif->u.ap.beacon_work, mm81x_mac_beacon_work);
505b1906ceaSLachlan Hodges 	mm81x_mac_beacon_irq_enable(mors_vif, true);
506b1906ceaSLachlan Hodges 	atomic_inc(&mors->num_bcn_vifs);
507b1906ceaSLachlan Hodges }
508b1906ceaSLachlan Hodges 
mm81x_hw_scan_h_pack_tlv_hdr(u16 tag,u16 len)509b1906ceaSLachlan Hodges static struct hw_scan_tlv_hdr mm81x_hw_scan_h_pack_tlv_hdr(u16 tag, u16 len)
510b1906ceaSLachlan Hodges {
511b1906ceaSLachlan Hodges 	struct hw_scan_tlv_hdr hdr = { .tag = cpu_to_le16(tag),
512b1906ceaSLachlan Hodges 				       .len = cpu_to_le16(len) };
513b1906ceaSLachlan Hodges 	return hdr;
514b1906ceaSLachlan Hodges }
515b1906ceaSLachlan Hodges 
mm81x_hw_scan_h_pack_channel(struct ieee80211_channel * chan,u8 pwr_idx)516b1906ceaSLachlan Hodges static __le32 mm81x_hw_scan_h_pack_channel(struct ieee80211_channel *chan,
517b1906ceaSLachlan Hodges 					   u8 pwr_idx)
518b1906ceaSLachlan Hodges {
519b1906ceaSLachlan Hodges 	__le32 packed = 0;
520b1906ceaSLachlan Hodges 	u32 freq_khz = ieee80211_channel_to_khz(chan);
521b1906ceaSLachlan Hodges 
522b1906ceaSLachlan Hodges 	packed |= le32_encode_bits(freq_khz, HW_SCAN_CH_LIST_FREQ_KHZ);
523b1906ceaSLachlan Hodges 	packed |= le32_encode_bits(mm81x_ratecode_bw_mhz_to_bw_index(1),
524b1906ceaSLachlan Hodges 				   HW_SCAN_CH_LIST_OP_BW);
525b1906ceaSLachlan Hodges 	packed |= le32_encode_bits(mm81x_ratecode_bw_mhz_to_bw_index(1),
526b1906ceaSLachlan Hodges 				   HW_SCAN_CH_LIST_PRIM_CH_WIDTH);
527b1906ceaSLachlan Hodges 	packed |= le32_encode_bits(pwr_idx, HW_SCAN_CH_LIST_PWR_LIST_IDX);
528b1906ceaSLachlan Hodges 
529b1906ceaSLachlan Hodges 	return packed;
530b1906ceaSLachlan Hodges }
531b1906ceaSLachlan Hodges 
532b1906ceaSLachlan Hodges static u8 *
mm81x_hw_scan_h_add_channel_list_tlv(u8 * buf,struct mm81x_hw_scan_params * params)533b1906ceaSLachlan Hodges mm81x_hw_scan_h_add_channel_list_tlv(u8 *buf,
534b1906ceaSLachlan Hodges 				     struct mm81x_hw_scan_params *params)
535b1906ceaSLachlan Hodges {
536b1906ceaSLachlan Hodges 	int i;
537b1906ceaSLachlan Hodges 	struct hw_scan_tlv_channel_list *ch_list =
538b1906ceaSLachlan Hodges 		(struct hw_scan_tlv_channel_list *)buf;
539b1906ceaSLachlan Hodges 
540b1906ceaSLachlan Hodges 	ch_list->hdr = mm81x_hw_scan_h_pack_tlv_hdr(
541b1906ceaSLachlan Hodges 		HOST_CMD_HW_SCAN_TLV_TAG_CHAN_LIST,
542b1906ceaSLachlan Hodges 		params->num_chans * sizeof(ch_list->channels[0]));
543b1906ceaSLachlan Hodges 
544b1906ceaSLachlan Hodges 	for (i = 0; i < params->num_chans; i++) {
545b1906ceaSLachlan Hodges 		struct ieee80211_channel *chan = params->channels[i].channel;
546b1906ceaSLachlan Hodges 
547b1906ceaSLachlan Hodges 		ch_list->channels[i] = mm81x_hw_scan_h_pack_channel(
548b1906ceaSLachlan Hodges 			chan, params->channels[i].power_idx);
549b1906ceaSLachlan Hodges 	}
550b1906ceaSLachlan Hodges 
551b1906ceaSLachlan Hodges 	return (u8 *)&ch_list->channels[i];
552b1906ceaSLachlan Hodges }
553b1906ceaSLachlan Hodges 
554b1906ceaSLachlan Hodges static u8 *
mm81x_hw_scan_h_add_power_list_tlv(u8 * buf,struct mm81x_hw_scan_params * params)555b1906ceaSLachlan Hodges mm81x_hw_scan_h_add_power_list_tlv(u8 *buf, struct mm81x_hw_scan_params *params)
556b1906ceaSLachlan Hodges {
557b1906ceaSLachlan Hodges 	int i;
558b1906ceaSLachlan Hodges 	struct hw_scan_tlv_power_list *pwr_list =
559b1906ceaSLachlan Hodges 		(struct hw_scan_tlv_power_list *)buf;
560b1906ceaSLachlan Hodges 	size_t size = sizeof(pwr_list->tx_power_qdbm[0]) * params->n_powers;
561b1906ceaSLachlan Hodges 
562b1906ceaSLachlan Hodges 	pwr_list->hdr = mm81x_hw_scan_h_pack_tlv_hdr(
563b1906ceaSLachlan Hodges 		HOST_CMD_HW_SCAN_TLV_TAG_POWER_LIST, size);
564b1906ceaSLachlan Hodges 
565b1906ceaSLachlan Hodges 	for (i = 0; i < params->n_powers; i++)
566b1906ceaSLachlan Hodges 		pwr_list->tx_power_qdbm[i] = params->powers_qdbm[i];
567b1906ceaSLachlan Hodges 
568b1906ceaSLachlan Hodges 	return (u8 *)&pwr_list->tx_power_qdbm[i];
569b1906ceaSLachlan Hodges }
570b1906ceaSLachlan Hodges 
571b1906ceaSLachlan Hodges static u8 *
mm81x_hw_scan_h_add_probe_req_tlv(u8 * buf,struct mm81x_hw_scan_params * params)572b1906ceaSLachlan Hodges mm81x_hw_scan_h_add_probe_req_tlv(u8 *buf, struct mm81x_hw_scan_params *params)
573b1906ceaSLachlan Hodges {
574b1906ceaSLachlan Hodges 	struct sk_buff *skb = params->probe_req;
575b1906ceaSLachlan Hodges 	struct hw_scan_tlv_probe_req *probe_req =
576b1906ceaSLachlan Hodges 		(struct hw_scan_tlv_probe_req *)buf;
577b1906ceaSLachlan Hodges 
578b1906ceaSLachlan Hodges 	probe_req->hdr = mm81x_hw_scan_h_pack_tlv_hdr(
579b1906ceaSLachlan Hodges 		HOST_CMD_HW_SCAN_TLV_TAG_PROBE_REQ, skb->len);
580b1906ceaSLachlan Hodges 	memcpy(probe_req->buf, skb->data, skb->len);
581b1906ceaSLachlan Hodges 
582b1906ceaSLachlan Hodges 	return buf + sizeof(*probe_req) + skb->len;
583b1906ceaSLachlan Hodges }
584b1906ceaSLachlan Hodges 
585b1906ceaSLachlan Hodges static u8 *
mm81x_hw_scan_h_insert_dwell_time_tlv(u8 * buf,struct mm81x_hw_scan_params * params)586b1906ceaSLachlan Hodges mm81x_hw_scan_h_insert_dwell_time_tlv(u8 *buf,
587b1906ceaSLachlan Hodges 				      struct mm81x_hw_scan_params *params)
588b1906ceaSLachlan Hodges {
589b1906ceaSLachlan Hodges 	struct hw_scan_tlv_dwell_on_home *dwell =
590b1906ceaSLachlan Hodges 		(struct hw_scan_tlv_dwell_on_home *)buf;
591b1906ceaSLachlan Hodges 
592b1906ceaSLachlan Hodges 	dwell->hdr = mm81x_hw_scan_h_pack_tlv_hdr(
593b1906ceaSLachlan Hodges 		HOST_CMD_HW_SCAN_TLV_TAG_DWELL_ON_HOME,
594b1906ceaSLachlan Hodges 		sizeof(*dwell) - sizeof(dwell->hdr));
595b1906ceaSLachlan Hodges 	dwell->home_dwell_time_ms = cpu_to_le32(params->dwell_on_home_ms);
596b1906ceaSLachlan Hodges 
597b1906ceaSLachlan Hodges 	return buf + sizeof(*dwell);
598b1906ceaSLachlan Hodges }
599b1906ceaSLachlan Hodges 
__mm81x_hw_scan_h_init_probe_req(struct mm81x_hw_scan_params * params,u8 * ssid,u8 ssid_len,struct ieee80211_scan_ies * ies)600b1906ceaSLachlan Hodges static int __mm81x_hw_scan_h_init_probe_req(struct mm81x_hw_scan_params *params,
601b1906ceaSLachlan Hodges 					    u8 *ssid, u8 ssid_len,
602b1906ceaSLachlan Hodges 					    struct ieee80211_scan_ies *ies)
603b1906ceaSLachlan Hodges {
604b1906ceaSLachlan Hodges 	u8 *pos;
605b1906ceaSLachlan Hodges 	struct sk_buff *probe_req;
606b1906ceaSLachlan Hodges 	struct ieee80211_tx_info *info;
607b1906ceaSLachlan Hodges 	u16 ies_len = ies->len[NL80211_BAND_S1GHZ] + ies->common_ie_len;
608b1906ceaSLachlan Hodges 
609b1906ceaSLachlan Hodges 	probe_req = ieee80211_probereq_get(params->hw, params->vif->addr, ssid,
610b1906ceaSLachlan Hodges 					   ssid_len, ies_len);
611b1906ceaSLachlan Hodges 	if (!probe_req)
612b1906ceaSLachlan Hodges 		return -ENOMEM;
613b1906ceaSLachlan Hodges 
614b1906ceaSLachlan Hodges 	pos = skb_put(probe_req, ies_len);
615b1906ceaSLachlan Hodges 	memcpy(pos, ies->common_ies, ies->common_ie_len);
616b1906ceaSLachlan Hodges 	pos += ies->common_ie_len;
617b1906ceaSLachlan Hodges 	memcpy(pos, ies->ies[NL80211_BAND_S1GHZ], ies->len[NL80211_BAND_S1GHZ]);
618b1906ceaSLachlan Hodges 
619b1906ceaSLachlan Hodges 	info = IEEE80211_SKB_CB(probe_req);
620b1906ceaSLachlan Hodges 	info->control.vif = params->vif;
621b1906ceaSLachlan Hodges 	params->probe_req = probe_req;
622b1906ceaSLachlan Hodges 
623b1906ceaSLachlan Hodges 	return 0;
624b1906ceaSLachlan Hodges }
625b1906ceaSLachlan Hodges 
mm81x_hw_scan_h_init_ssid(struct mm81x * mors,struct cfg80211_ssid * ssids,int n_ssids,u8 ** out_ssid,u8 * out_ssid_len)626b1906ceaSLachlan Hodges static void mm81x_hw_scan_h_init_ssid(struct mm81x *mors,
627b1906ceaSLachlan Hodges 				      struct cfg80211_ssid *ssids, int n_ssids,
628b1906ceaSLachlan Hodges 				      u8 **out_ssid, u8 *out_ssid_len)
629b1906ceaSLachlan Hodges {
630b1906ceaSLachlan Hodges 	*out_ssid = NULL;
631b1906ceaSLachlan Hodges 	*out_ssid_len = 0;
632b1906ceaSLachlan Hodges 
633b1906ceaSLachlan Hodges 	if (n_ssids > 0) {
634b1906ceaSLachlan Hodges 		if (n_ssids > 1) {
635b1906ceaSLachlan Hodges 			dev_warn(
636b1906ceaSLachlan Hodges 				mors->dev,
637b1906ceaSLachlan Hodges 				"Multiple SSIDs found when only one supported. Using the first only.");
638b1906ceaSLachlan Hodges 		}
639b1906ceaSLachlan Hodges 		*out_ssid_len = ssids[0].ssid_len;
640b1906ceaSLachlan Hodges 		*out_ssid = ssids[0].ssid;
641b1906ceaSLachlan Hodges 	}
642b1906ceaSLachlan Hodges }
643b1906ceaSLachlan Hodges 
644b1906ceaSLachlan Hodges static int
mm81x_hw_scan_h_init_probe_req(struct mm81x_hw_scan_params * params,struct ieee80211_scan_request * scan_req)645b1906ceaSLachlan Hodges mm81x_hw_scan_h_init_probe_req(struct mm81x_hw_scan_params *params,
646b1906ceaSLachlan Hodges 			       struct ieee80211_scan_request *scan_req)
647b1906ceaSLachlan Hodges {
648b1906ceaSLachlan Hodges 	struct mm81x *mors = params->hw->priv;
649b1906ceaSLachlan Hodges 	struct cfg80211_scan_request *req = &scan_req->req;
650b1906ceaSLachlan Hodges 	struct ieee80211_scan_ies *ies = &scan_req->ies;
651b1906ceaSLachlan Hodges 	u8 ssid_len = 0;
652b1906ceaSLachlan Hodges 	u8 *ssid = NULL;
653b1906ceaSLachlan Hodges 
654b1906ceaSLachlan Hodges 	mm81x_hw_scan_h_init_ssid(mors, req->ssids, req->n_ssids, &ssid,
655b1906ceaSLachlan Hodges 				  &ssid_len);
656b1906ceaSLachlan Hodges 
657b1906ceaSLachlan Hodges 	return __mm81x_hw_scan_h_init_probe_req(params, ssid, ssid_len, ies);
658b1906ceaSLachlan Hodges }
659b1906ceaSLachlan Hodges 
660b1906ceaSLachlan Hodges static bool
mm81x_hw_scan_h_is_chan_present(const struct mm81x_hw_scan_params * params,const struct ieee80211_channel * chan)661b1906ceaSLachlan Hodges mm81x_hw_scan_h_is_chan_present(const struct mm81x_hw_scan_params *params,
662b1906ceaSLachlan Hodges 				const struct ieee80211_channel *chan)
663b1906ceaSLachlan Hodges {
664b1906ceaSLachlan Hodges 	int channel;
665b1906ceaSLachlan Hodges 
666b1906ceaSLachlan Hodges 	for (channel = 0; channel < params->num_chans; channel++) {
667b1906ceaSLachlan Hodges 		if (params->channels[channel].channel == chan)
668b1906ceaSLachlan Hodges 			return true;
669b1906ceaSLachlan Hodges 	}
670b1906ceaSLachlan Hodges 
671b1906ceaSLachlan Hodges 	return false;
672b1906ceaSLachlan Hodges }
673b1906ceaSLachlan Hodges 
mm81x_hw_scan_h_insert_chan(struct mm81x_hw_scan_params * params,struct ieee80211_channel * chan)674b1906ceaSLachlan Hodges static int mm81x_hw_scan_h_insert_chan(struct mm81x_hw_scan_params *params,
675b1906ceaSLachlan Hodges 				       struct ieee80211_channel *chan)
676b1906ceaSLachlan Hodges {
677b1906ceaSLachlan Hodges 	if (!params->channels)
678b1906ceaSLachlan Hodges 		return -EFAULT;
679b1906ceaSLachlan Hodges 
680b1906ceaSLachlan Hodges 	if (!chan)
681b1906ceaSLachlan Hodges 		return -EFAULT;
682b1906ceaSLachlan Hodges 
683b1906ceaSLachlan Hodges 	if (params->num_chans >= params->allocated_chans)
684b1906ceaSLachlan Hodges 		return -ENOMEM;
685b1906ceaSLachlan Hodges 
686b1906ceaSLachlan Hodges 	if (mm81x_hw_scan_h_is_chan_present(params, chan))
687b1906ceaSLachlan Hodges 		return 0;
688b1906ceaSLachlan Hodges 
689b1906ceaSLachlan Hodges 	params->channels[params->num_chans].channel = chan;
690b1906ceaSLachlan Hodges 	params->num_chans++;
691b1906ceaSLachlan Hodges 	return 0;
692b1906ceaSLachlan Hodges }
693b1906ceaSLachlan Hodges 
mm81x_hw_scan_h_init_chan_list(struct mm81x_hw_scan_params * params,struct ieee80211_channel ** chans,u32 n_channels)694b1906ceaSLachlan Hodges static int mm81x_hw_scan_h_init_chan_list(struct mm81x_hw_scan_params *params,
695b1906ceaSLachlan Hodges 					  struct ieee80211_channel **chans,
696b1906ceaSLachlan Hodges 					  u32 n_channels)
697b1906ceaSLachlan Hodges {
698b1906ceaSLachlan Hodges 	int i, j;
699b1906ceaSLachlan Hodges 	int num_pwrs_coarse = 0;
700b1906ceaSLachlan Hodges 	int last_pwr = INT_MIN;
701b1906ceaSLachlan Hodges 	int chans_to_allocate = 0;
702b1906ceaSLachlan Hodges 
703b1906ceaSLachlan Hodges 	for (i = 0; i < n_channels; i++)
704b1906ceaSLachlan Hodges 		if (chans[i])
705b1906ceaSLachlan Hodges 			chans_to_allocate++;
706b1906ceaSLachlan Hodges 
707b1906ceaSLachlan Hodges 	params->num_chans = 0;
708b1906ceaSLachlan Hodges 	params->allocated_chans = 0;
709b1906ceaSLachlan Hodges 	params->channels = kcalloc(chans_to_allocate, sizeof(*params->channels),
710b1906ceaSLachlan Hodges 				   GFP_KERNEL);
711b1906ceaSLachlan Hodges 	if (!params->channels)
712b1906ceaSLachlan Hodges 		return -ENOMEM;
713b1906ceaSLachlan Hodges 
714b1906ceaSLachlan Hodges 	params->allocated_chans = chans_to_allocate;
715b1906ceaSLachlan Hodges 
716b1906ceaSLachlan Hodges 	for (i = 0; i < n_channels; i++)
717b1906ceaSLachlan Hodges 		if (chans[i])
718b1906ceaSLachlan Hodges 			mm81x_hw_scan_h_insert_chan(params, chans[i]);
719b1906ceaSLachlan Hodges 
720b1906ceaSLachlan Hodges 	/*
721b1906ceaSLachlan Hodges 	 * Calculate a rough estimate of number of different channel
722b1906ceaSLachlan Hodges 	 * powers required
723b1906ceaSLachlan Hodges 	 */
724b1906ceaSLachlan Hodges 	for (i = 0; i < params->num_chans; i++) {
725b1906ceaSLachlan Hodges 		if (chans[i]->max_reg_power != last_pwr) {
726b1906ceaSLachlan Hodges 			last_pwr = chans[i]->max_reg_power;
727b1906ceaSLachlan Hodges 			num_pwrs_coarse++;
728b1906ceaSLachlan Hodges 		}
729b1906ceaSLachlan Hodges 	}
730b1906ceaSLachlan Hodges 
731b1906ceaSLachlan Hodges 	params->powers_qdbm = kmalloc_array(
732b1906ceaSLachlan Hodges 		num_pwrs_coarse, sizeof(*params->powers_qdbm), GFP_KERNEL);
733b1906ceaSLachlan Hodges 	if (!params->powers_qdbm)
734b1906ceaSLachlan Hodges 		return -ENOMEM;
735b1906ceaSLachlan Hodges 
736b1906ceaSLachlan Hodges 	params->n_powers = 0;
737b1906ceaSLachlan Hodges 
738b1906ceaSLachlan Hodges 	for (i = 0; i < params->num_chans; i++) {
739b1906ceaSLachlan Hodges 		s32 power_qdbm =
740b1906ceaSLachlan Hodges 			MBM_TO_QDBM(DBM_TO_MBM(chans[i]->max_reg_power));
741b1906ceaSLachlan Hodges 
742b1906ceaSLachlan Hodges 		/* Try and find the power in the list */
743b1906ceaSLachlan Hodges 		for (j = 0; j < params->n_powers; j++)
744b1906ceaSLachlan Hodges 			if (params->powers_qdbm[j] == power_qdbm)
745b1906ceaSLachlan Hodges 				break;
746b1906ceaSLachlan Hodges 
747b1906ceaSLachlan Hodges 		/* Reached the end of the list - add the new power option */
748b1906ceaSLachlan Hodges 		if (j == params->n_powers) {
749b1906ceaSLachlan Hodges 			params->powers_qdbm[j] = power_qdbm;
750b1906ceaSLachlan Hodges 			params->n_powers++;
751b1906ceaSLachlan Hodges 			if (params->n_powers > num_pwrs_coarse) {
752b1906ceaSLachlan Hodges 				WARN_ON(1);
753b1906ceaSLachlan Hodges 				return -EFAULT;
754b1906ceaSLachlan Hodges 			}
755b1906ceaSLachlan Hodges 		}
756b1906ceaSLachlan Hodges 
757b1906ceaSLachlan Hodges 		/* Give the index of the power level to the channel */
758b1906ceaSLachlan Hodges 		params->channels[i].power_idx = j;
759b1906ceaSLachlan Hodges 	}
760b1906ceaSLachlan Hodges 	return 0;
761b1906ceaSLachlan Hodges }
762b1906ceaSLachlan Hodges 
mm81x_hw_scan_h_clean_params(struct mm81x_hw_scan_params * params)763b1906ceaSLachlan Hodges static void mm81x_hw_scan_h_clean_params(struct mm81x_hw_scan_params *params)
764b1906ceaSLachlan Hodges {
765b1906ceaSLachlan Hodges 	if (params->probe_req)
766b1906ceaSLachlan Hodges 		dev_kfree_skb_any(params->probe_req);
767b1906ceaSLachlan Hodges 	kfree(params->channels);
768b1906ceaSLachlan Hodges 	kfree(params->powers_qdbm);
769b1906ceaSLachlan Hodges 
770b1906ceaSLachlan Hodges 	params->num_chans = 0;
771b1906ceaSLachlan Hodges 	params->allocated_chans = 0;
772b1906ceaSLachlan Hodges }
773b1906ceaSLachlan Hodges 
mm81x_hw_scan_h_get_cmd_size(struct mm81x_hw_scan_params * params)774b1906ceaSLachlan Hodges size_t mm81x_hw_scan_h_get_cmd_size(struct mm81x_hw_scan_params *params)
775b1906ceaSLachlan Hodges {
776b1906ceaSLachlan Hodges 	struct hw_scan_tlv_channel_list *ch_list;
777b1906ceaSLachlan Hodges 	struct hw_scan_tlv_power_list *pwr_list;
778b1906ceaSLachlan Hodges 	struct hw_scan_tlv_probe_req *probe_req;
779b1906ceaSLachlan Hodges 	struct hw_scan_tlv_dwell_on_home *dwell;
780b1906ceaSLachlan Hodges 	struct host_cmd_req_hw_scan *req;
781b1906ceaSLachlan Hodges 	size_t cmd_size = sizeof(*req);
782b1906ceaSLachlan Hodges 
783b1906ceaSLachlan Hodges 	/* No TLVs if simple abort command */
784b1906ceaSLachlan Hodges 	if (params->operation != MM81X_HW_SCAN_OP_START)
785b1906ceaSLachlan Hodges 		return cmd_size;
786b1906ceaSLachlan Hodges 
787b1906ceaSLachlan Hodges 	cmd_size += struct_size(ch_list, channels, params->num_chans);
788b1906ceaSLachlan Hodges 	cmd_size += struct_size(pwr_list, tx_power_qdbm, params->n_powers);
789b1906ceaSLachlan Hodges 
790b1906ceaSLachlan Hodges 	if (params->probe_req)
791b1906ceaSLachlan Hodges 		cmd_size += struct_size(probe_req, buf, params->probe_req->len);
792b1906ceaSLachlan Hodges 	if (params->dwell_on_home_ms)
793b1906ceaSLachlan Hodges 		cmd_size += sizeof(*dwell);
794b1906ceaSLachlan Hodges 
795b1906ceaSLachlan Hodges 	return cmd_size;
796b1906ceaSLachlan Hodges }
797b1906ceaSLachlan Hodges 
mm81x_hw_scan_h_insert_tlvs(struct mm81x_hw_scan_params * params,u8 * buf)798b1906ceaSLachlan Hodges u8 *mm81x_hw_scan_h_insert_tlvs(struct mm81x_hw_scan_params *params, u8 *buf)
799b1906ceaSLachlan Hodges {
800b1906ceaSLachlan Hodges 	buf = mm81x_hw_scan_h_add_channel_list_tlv(buf, params);
801b1906ceaSLachlan Hodges 	buf = mm81x_hw_scan_h_add_power_list_tlv(buf, params);
802b1906ceaSLachlan Hodges 
803b1906ceaSLachlan Hodges 	if (params->dwell_on_home_ms)
804b1906ceaSLachlan Hodges 		buf = mm81x_hw_scan_h_insert_dwell_time_tlv(buf, params);
805b1906ceaSLachlan Hodges 	if (params->probe_req)
806b1906ceaSLachlan Hodges 		buf = mm81x_hw_scan_h_add_probe_req_tlv(buf, params);
807b1906ceaSLachlan Hodges 
808b1906ceaSLachlan Hodges 	return buf;
809b1906ceaSLachlan Hodges }
810b1906ceaSLachlan Hodges 
mm81x_hw_scan_h_get_dwell_on_home(struct mm81x * mors,struct ieee80211_vif * vif)811b1906ceaSLachlan Hodges static u32 mm81x_hw_scan_h_get_dwell_on_home(struct mm81x *mors,
812b1906ceaSLachlan Hodges 					     struct ieee80211_vif *vif)
813b1906ceaSLachlan Hodges {
814b1906ceaSLachlan Hodges 	if (vif->type == NL80211_IFTYPE_STATION && vif->cfg.assoc)
815b1906ceaSLachlan Hodges 		return mors->hw_scan.home_dwell_ms;
816b1906ceaSLachlan Hodges 	return 0;
817b1906ceaSLachlan Hodges }
818b1906ceaSLachlan Hodges 
819b1906ceaSLachlan Hodges static struct mm81x_hw_scan_params *
__mm81x_hw_scan_h_init_params(struct mm81x * mors)820b1906ceaSLachlan Hodges __mm81x_hw_scan_h_init_params(struct mm81x *mors)
821b1906ceaSLachlan Hodges {
822b1906ceaSLachlan Hodges 	struct mm81x_hw_scan_params *params = mors->hw_scan.params;
823b1906ceaSLachlan Hodges 
824b1906ceaSLachlan Hodges 	if (!params) {
825b1906ceaSLachlan Hodges 		params = kzalloc_obj(*params, GFP_KERNEL);
826b1906ceaSLachlan Hodges 		if (params)
827b1906ceaSLachlan Hodges 			mors->hw_scan.params = params;
828b1906ceaSLachlan Hodges 	} else {
829b1906ceaSLachlan Hodges 		mm81x_hw_scan_h_clean_params(params);
830b1906ceaSLachlan Hodges 		memset(params, 0, sizeof(*params));
831b1906ceaSLachlan Hodges 	}
832b1906ceaSLachlan Hodges 
833b1906ceaSLachlan Hodges 	return params;
834b1906ceaSLachlan Hodges }
835b1906ceaSLachlan Hodges 
mm81x_hw_scan_h_init_params(struct mm81x * mors,struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct cfg80211_scan_request * req)836b1906ceaSLachlan Hodges static int mm81x_hw_scan_h_init_params(struct mm81x *mors,
837b1906ceaSLachlan Hodges 				       struct ieee80211_hw *hw,
838b1906ceaSLachlan Hodges 				       struct ieee80211_vif *vif,
839b1906ceaSLachlan Hodges 				       struct cfg80211_scan_request *req)
840b1906ceaSLachlan Hodges {
841b1906ceaSLachlan Hodges 	struct mm81x_hw_scan_params *params = mors->hw_scan.params;
842b1906ceaSLachlan Hodges 
843b1906ceaSLachlan Hodges 	params = __mm81x_hw_scan_h_init_params(mors);
844b1906ceaSLachlan Hodges 	if (!params) {
845b1906ceaSLachlan Hodges 		mors->hw_scan.state = HW_SCAN_STATE_IDLE;
846b1906ceaSLachlan Hodges 		return -ENOMEM;
847b1906ceaSLachlan Hodges 	}
848b1906ceaSLachlan Hodges 
849b1906ceaSLachlan Hodges 	params->hw = hw;
850b1906ceaSLachlan Hodges 	params->vif = vif;
851b1906ceaSLachlan Hodges 	params->has_directed_ssid = (req->ssids && req->ssids[0].ssid_len > 0);
852b1906ceaSLachlan Hodges 	params->operation = MM81X_HW_SCAN_OP_START;
853b1906ceaSLachlan Hodges 	params->dwell_on_home_ms = mm81x_hw_scan_h_get_dwell_on_home(mors, vif);
854b1906ceaSLachlan Hodges 
855b1906ceaSLachlan Hodges 	if (req->duration)
856b1906ceaSLachlan Hodges 		params->dwell_time_ms = MM81X_TU_TO_MS(req->duration);
857b1906ceaSLachlan Hodges 	else if (req->n_ssids == 0)
858b1906ceaSLachlan Hodges 		params->dwell_time_ms =
859b1906ceaSLachlan Hodges 			MM81X_HWSCAN_DEFAULT_PASSIVE_DWELL_TIME_MS;
860b1906ceaSLachlan Hodges 	else
861b1906ceaSLachlan Hodges 		params->dwell_time_ms = MM81X_HWSCAN_DEFAULT_DWELL_TIME_MS;
862b1906ceaSLachlan Hodges 
863b1906ceaSLachlan Hodges 	return 0;
864b1906ceaSLachlan Hodges }
865b1906ceaSLachlan Hodges 
mm81x_hw_scan_h_calc_timeout(struct mm81x_hw_scan_params * params)866b1906ceaSLachlan Hodges static u32 mm81x_hw_scan_h_calc_timeout(struct mm81x_hw_scan_params *params)
867b1906ceaSLachlan Hodges {
868b1906ceaSLachlan Hodges 	u32 ret = 0;
869b1906ceaSLachlan Hodges 
870b1906ceaSLachlan Hodges 	ret = params->dwell_time_ms + params->dwell_on_home_ms;
871b1906ceaSLachlan Hodges 	if (params->probe_req)
872b1906ceaSLachlan Hodges 		ret += MM81X_HWSCAN_PROBE_DELAY_MS;
873b1906ceaSLachlan Hodges 
874b1906ceaSLachlan Hodges 	ret *= params->num_chans;
875b1906ceaSLachlan Hodges 	ret += MM81X_HWSCAN_TIMEOUT_OVERHEAD_MS;
876b1906ceaSLachlan Hodges 
877b1906ceaSLachlan Hodges 	return ret;
878b1906ceaSLachlan Hodges }
879b1906ceaSLachlan Hodges 
mm81x_mac_ops_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_scan_request * hw_req)880b1906ceaSLachlan Hodges static int mm81x_mac_ops_hw_scan(struct ieee80211_hw *hw,
881b1906ceaSLachlan Hodges 				 struct ieee80211_vif *vif,
882b1906ceaSLachlan Hodges 				 struct ieee80211_scan_request *hw_req)
883b1906ceaSLachlan Hodges {
884b1906ceaSLachlan Hodges 	int ret = 0;
885b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
886b1906ceaSLachlan Hodges 	struct cfg80211_scan_request *req = &hw_req->req;
887b1906ceaSLachlan Hodges 	struct mm81x_hw_scan_params *params;
888b1906ceaSLachlan Hodges 	struct ieee80211_channel **chans = hw_req->req.channels;
889b1906ceaSLachlan Hodges 
890b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "state %d", mors->hw_scan.state);
891b1906ceaSLachlan Hodges 
892b1906ceaSLachlan Hodges 	if (!mors->started) {
893b1906ceaSLachlan Hodges 		dev_warn(mors->dev, "device not ready");
894b1906ceaSLachlan Hodges 		ret = -ENODEV;
895b1906ceaSLachlan Hodges 		goto exit;
896b1906ceaSLachlan Hodges 	}
897b1906ceaSLachlan Hodges 
898b1906ceaSLachlan Hodges 	switch (mors->hw_scan.state) {
899b1906ceaSLachlan Hodges 	case HW_SCAN_STATE_IDLE:
900b1906ceaSLachlan Hodges 		mors->hw_scan.state = HW_SCAN_STATE_RUNNING;
901b1906ceaSLachlan Hodges 		reinit_completion(&mors->hw_scan.scan_done);
902b1906ceaSLachlan Hodges 		break;
903b1906ceaSLachlan Hodges 	case HW_SCAN_STATE_RUNNING:
904b1906ceaSLachlan Hodges 	case HW_SCAN_STATE_ABORTING:
905b1906ceaSLachlan Hodges 		ret = -EBUSY;
906b1906ceaSLachlan Hodges 		goto exit;
907b1906ceaSLachlan Hodges 	}
908b1906ceaSLachlan Hodges 
909b1906ceaSLachlan Hodges 	ret = mm81x_hw_scan_h_init_params(mors, hw, vif, req);
910b1906ceaSLachlan Hodges 	if (ret)
911b1906ceaSLachlan Hodges 		goto exit;
912b1906ceaSLachlan Hodges 
913b1906ceaSLachlan Hodges 	params = mors->hw_scan.params;
914b1906ceaSLachlan Hodges 
915b1906ceaSLachlan Hodges 	ret = mm81x_hw_scan_h_init_chan_list(params, chans,
916b1906ceaSLachlan Hodges 					     hw_req->req.n_channels);
917b1906ceaSLachlan Hodges 	if (ret)
918b1906ceaSLachlan Hodges 		goto exit;
919b1906ceaSLachlan Hodges 
920b1906ceaSLachlan Hodges 	/* Only init the probe request template if this is an active scan */
921b1906ceaSLachlan Hodges 	if (req->n_ssids > 0) {
922b1906ceaSLachlan Hodges 		ret = mm81x_hw_scan_h_init_probe_req(params, hw_req);
923b1906ceaSLachlan Hodges 		if (ret) {
924b1906ceaSLachlan Hodges 			dev_err(mors->dev, "Failed to init probe req %d", ret);
925b1906ceaSLachlan Hodges 			goto exit;
926b1906ceaSLachlan Hodges 		}
927b1906ceaSLachlan Hodges 	}
928b1906ceaSLachlan Hodges 
929b1906ceaSLachlan Hodges 	ret = mm81x_cmd_hw_scan(mors, params, false);
930b1906ceaSLachlan Hodges 	if (ret) {
931b1906ceaSLachlan Hodges 		mors->hw_scan.state = HW_SCAN_STATE_IDLE;
932b1906ceaSLachlan Hodges 		goto exit;
933b1906ceaSLachlan Hodges 	}
934b1906ceaSLachlan Hodges 
935b1906ceaSLachlan Hodges 	ieee80211_queue_delayed_work(
936b1906ceaSLachlan Hodges 		mors->hw, &mors->hw_scan.timeout,
937b1906ceaSLachlan Hodges 		msecs_to_jiffies(mm81x_hw_scan_h_calc_timeout(params)));
938b1906ceaSLachlan Hodges exit:
939b1906ceaSLachlan Hodges 	return ret;
940b1906ceaSLachlan Hodges }
941b1906ceaSLachlan Hodges 
mm81x_hw_scan_abort(struct mm81x * mors)942b1906ceaSLachlan Hodges static void mm81x_hw_scan_abort(struct mm81x *mors)
943b1906ceaSLachlan Hodges {
944b1906ceaSLachlan Hodges 	int ret;
945b1906ceaSLachlan Hodges 	struct mm81x_hw_scan_params params = { 0 };
946b1906ceaSLachlan Hodges 
947b1906ceaSLachlan Hodges 	switch (mors->hw_scan.state) {
948b1906ceaSLachlan Hodges 	case HW_SCAN_STATE_IDLE:
949b1906ceaSLachlan Hodges 	case HW_SCAN_STATE_ABORTING:
950b1906ceaSLachlan Hodges 		/* scan not running */
951b1906ceaSLachlan Hodges 		return;
952b1906ceaSLachlan Hodges 	case HW_SCAN_STATE_RUNNING:
953b1906ceaSLachlan Hodges 		mors->hw_scan.state = HW_SCAN_STATE_ABORTING;
954b1906ceaSLachlan Hodges 		break;
955b1906ceaSLachlan Hodges 	}
956b1906ceaSLachlan Hodges 
957b1906ceaSLachlan Hodges 	params.operation = MM81X_HW_SCAN_OP_STOP;
958b1906ceaSLachlan Hodges 
959b1906ceaSLachlan Hodges 	ret = mm81x_cmd_hw_scan(mors, &params, false);
960b1906ceaSLachlan Hodges 
961b1906ceaSLachlan Hodges 	if (ret || !mors->started ||
962b1906ceaSLachlan Hodges 	    !wait_for_completion_timeout(&mors->hw_scan.scan_done, 1 * HZ)) {
963b1906ceaSLachlan Hodges 		/*
964b1906ceaSLachlan Hodges 		 * We may have lost the event on the bus, the chip could be
965b1906ceaSLachlan Hodges 		 * wedged, or the cmd failed for another reason. Nevertheless,
966b1906ceaSLachlan Hodges 		 * we should call the done event so mac80211 knows to unblock
967b1906ceaSLachlan Hodges 		 * itself.
968b1906ceaSLachlan Hodges 		 */
969b1906ceaSLachlan Hodges 		struct cfg80211_scan_info info = { .aborted = true };
970b1906ceaSLachlan Hodges 
971b1906ceaSLachlan Hodges 		ieee80211_scan_completed(mors->hw, &info);
972b1906ceaSLachlan Hodges 		mors->hw_scan.state = HW_SCAN_STATE_IDLE;
973b1906ceaSLachlan Hodges 	}
974b1906ceaSLachlan Hodges }
975b1906ceaSLachlan Hodges 
mm81x_mac_ops_cancel_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif)976b1906ceaSLachlan Hodges static void mm81x_mac_ops_cancel_hw_scan(struct ieee80211_hw *hw,
977b1906ceaSLachlan Hodges 					 struct ieee80211_vif *vif)
978b1906ceaSLachlan Hodges {
979b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
980b1906ceaSLachlan Hodges 
981b1906ceaSLachlan Hodges 	cancel_delayed_work_sync(&mors->hw_scan.timeout);
982b1906ceaSLachlan Hodges 	mm81x_hw_scan_abort(mors);
983b1906ceaSLachlan Hodges }
984b1906ceaSLachlan Hodges 
mm81x_mac_hw_scan_done_event(struct ieee80211_hw * hw)985b1906ceaSLachlan Hodges static void mm81x_mac_hw_scan_done_event(struct ieee80211_hw *hw)
986b1906ceaSLachlan Hodges {
987b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
988b1906ceaSLachlan Hodges 	struct cfg80211_scan_info info = { 0 };
989b1906ceaSLachlan Hodges 
990b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "completing hw scan");
991b1906ceaSLachlan Hodges 
992b1906ceaSLachlan Hodges 	switch (mors->hw_scan.state) {
993b1906ceaSLachlan Hodges 	case HW_SCAN_STATE_IDLE:
994b1906ceaSLachlan Hodges 		/* Scan has already been stopped. Just continue */
995b1906ceaSLachlan Hodges 		goto exit;
996b1906ceaSLachlan Hodges 	case HW_SCAN_STATE_RUNNING:
997b1906ceaSLachlan Hodges 	case HW_SCAN_STATE_ABORTING:
998b1906ceaSLachlan Hodges 		info.aborted = (mors->hw_scan.state == HW_SCAN_STATE_ABORTING);
999b1906ceaSLachlan Hodges 		mors->hw_scan.state = HW_SCAN_STATE_IDLE;
1000b1906ceaSLachlan Hodges 	}
1001b1906ceaSLachlan Hodges 
1002b1906ceaSLachlan Hodges 	ieee80211_scan_completed(mors->hw, &info);
1003b1906ceaSLachlan Hodges exit:
1004b1906ceaSLachlan Hodges 	complete(&mors->hw_scan.scan_done);
1005b1906ceaSLachlan Hodges 	cancel_delayed_work_sync(&mors->hw_scan.timeout);
1006b1906ceaSLachlan Hodges }
1007b1906ceaSLachlan Hodges 
mm81x_mac_hw_scan_timeout_work(struct work_struct * work)1008b1906ceaSLachlan Hodges static void mm81x_mac_hw_scan_timeout_work(struct work_struct *work)
1009b1906ceaSLachlan Hodges {
1010b1906ceaSLachlan Hodges 	struct mm81x *mors =
1011b1906ceaSLachlan Hodges 		container_of(work, struct mm81x, hw_scan.timeout.work);
1012b1906ceaSLachlan Hodges 
1013b1906ceaSLachlan Hodges 	dev_err(mors->dev, "hw scan timed out, aborting");
1014b1906ceaSLachlan Hodges 	mm81x_hw_scan_abort(mors);
1015b1906ceaSLachlan Hodges }
1016b1906ceaSLachlan Hodges 
mm81x_mac_hw_scan_init(struct mm81x * mors)1017b1906ceaSLachlan Hodges static void mm81x_mac_hw_scan_init(struct mm81x *mors)
1018b1906ceaSLachlan Hodges {
1019b1906ceaSLachlan Hodges 	mors->hw_scan.state = HW_SCAN_STATE_IDLE;
1020b1906ceaSLachlan Hodges 	mors->hw_scan.params = NULL;
1021b1906ceaSLachlan Hodges 	mors->hw_scan.home_dwell_ms = MM81X_HWSCAN_DEFAULT_DWELL_ON_HOME_MS;
1022b1906ceaSLachlan Hodges 
1023b1906ceaSLachlan Hodges 	init_completion(&mors->hw_scan.scan_done);
1024b1906ceaSLachlan Hodges 	INIT_DELAYED_WORK(&mors->hw_scan.timeout,
1025b1906ceaSLachlan Hodges 			  mm81x_mac_hw_scan_timeout_work);
1026b1906ceaSLachlan Hodges }
1027b1906ceaSLachlan Hodges 
mm81x_mac_hw_scan_destroy(struct mm81x * mors)1028b1906ceaSLachlan Hodges static void mm81x_mac_hw_scan_destroy(struct mm81x *mors)
1029b1906ceaSLachlan Hodges {
1030b1906ceaSLachlan Hodges 	cancel_delayed_work_sync(&mors->hw_scan.timeout);
1031b1906ceaSLachlan Hodges 	if (mors->hw_scan.params)
1032b1906ceaSLachlan Hodges 		mm81x_hw_scan_h_clean_params(mors->hw_scan.params);
1033b1906ceaSLachlan Hodges 	kfree(mors->hw_scan.params);
1034b1906ceaSLachlan Hodges 	mors->hw_scan.params = NULL;
1035b1906ceaSLachlan Hodges }
1036b1906ceaSLachlan Hodges 
mm81x_mac_hw_scan_finish(struct mm81x * mors)1037b1906ceaSLachlan Hodges static void mm81x_mac_hw_scan_finish(struct mm81x *mors)
1038b1906ceaSLachlan Hodges {
1039b1906ceaSLachlan Hodges 	struct cfg80211_scan_info info = {
1040b1906ceaSLachlan Hodges 		.aborted = true,
1041b1906ceaSLachlan Hodges 	};
1042b1906ceaSLachlan Hodges 
1043b1906ceaSLachlan Hodges 	if (mors->hw_scan.state == HW_SCAN_STATE_IDLE)
1044b1906ceaSLachlan Hodges 		return;
1045b1906ceaSLachlan Hodges 
1046b1906ceaSLachlan Hodges 	ieee80211_scan_completed(mors->hw, &info);
1047b1906ceaSLachlan Hodges 	complete(&mors->hw_scan.scan_done);
1048b1906ceaSLachlan Hodges 	mors->hw_scan.state = HW_SCAN_STATE_IDLE;
1049b1906ceaSLachlan Hodges 	cancel_delayed_work_sync(&mors->hw_scan.timeout);
1050b1906ceaSLachlan Hodges }
1051b1906ceaSLachlan Hodges 
mm81x_mac_event_recv(struct mm81x * mors,struct sk_buff * skb)1052b1906ceaSLachlan Hodges int mm81x_mac_event_recv(struct mm81x *mors, struct sk_buff *skb)
1053b1906ceaSLachlan Hodges {
1054b1906ceaSLachlan Hodges 	struct host_cmd_event *event = (struct host_cmd_event *)(skb->data);
1055b1906ceaSLachlan Hodges 	u16 event_id = le16_to_cpu(event->hdr.message_id);
1056b1906ceaSLachlan Hodges 	u16 event_iid = le16_to_cpu(event->hdr.host_id);
1057b1906ceaSLachlan Hodges 	u16 vif_id = le16_to_cpu(event->hdr.vif_id);
1058b1906ceaSLachlan Hodges 	struct ieee80211_vif *vif;
1059b1906ceaSLachlan Hodges 
1060b1906ceaSLachlan Hodges 	if (!HOST_CMD_IS_EVT(event) || event_iid != 0)
1061b1906ceaSLachlan Hodges 		return -EINVAL;
1062b1906ceaSLachlan Hodges 
1063b1906ceaSLachlan Hodges 	switch (event_id) {
1064b1906ceaSLachlan Hodges 	case HOST_CMD_ID_EVT_HW_SCAN_DONE:
1065b1906ceaSLachlan Hodges 		dev_dbg(mors->dev,
1066b1906ceaSLachlan Hodges 			"Event: HOST_CMD_ID_EVT_HW_SCAN_DONE Received.");
1067b1906ceaSLachlan Hodges 		mm81x_mac_hw_scan_done_event(mors->hw);
1068b1906ceaSLachlan Hodges 		break;
1069b1906ceaSLachlan Hodges 	case HOST_CMD_ID_EVT_BEACON_LOSS:
1070b1906ceaSLachlan Hodges 		dev_dbg(mors->dev,
1071b1906ceaSLachlan Hodges 			"Event: HOST_CMD_ID_EVT_BEACON_LOSS Received");
1072b1906ceaSLachlan Hodges 		scoped_guard(rcu) {
1073b1906ceaSLachlan Hodges 			vif = mm81x_rcu_dereference_vif_id(mors, vif_id, true);
1074b1906ceaSLachlan Hodges 			if (vif)
1075b1906ceaSLachlan Hodges 				ieee80211_beacon_loss(vif);
1076b1906ceaSLachlan Hodges 		}
1077b1906ceaSLachlan Hodges 		break;
1078b1906ceaSLachlan Hodges 	default:
1079b1906ceaSLachlan Hodges 		break;
1080b1906ceaSLachlan Hodges 	}
1081b1906ceaSLachlan Hodges 
1082b1906ceaSLachlan Hodges 	return 0;
1083b1906ceaSLachlan Hodges }
1084b1906ceaSLachlan Hodges 
mm81x_tx_h_apply_mcs10(struct mm81x * mors,struct mm81x_skb_tx_info * tx_info)1085b1906ceaSLachlan Hodges static void mm81x_tx_h_apply_mcs10(struct mm81x *mors,
1086b1906ceaSLachlan Hodges 				   struct mm81x_skb_tx_info *tx_info)
1087b1906ceaSLachlan Hodges {
1088b1906ceaSLachlan Hodges 	u8 i;
1089b1906ceaSLachlan Hodges 	u8 j;
1090b1906ceaSLachlan Hodges 	int mcs0_first_idx = -1;
1091b1906ceaSLachlan Hodges 	int mcs0_last_idx = -1;
1092b1906ceaSLachlan Hodges 
1093b1906ceaSLachlan Hodges 	/* Find out where our first and last MCS0 entries are. */
1094b1906ceaSLachlan Hodges 	for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
1095b1906ceaSLachlan Hodges 		enum dot11_bandwidth bw_idx = mm81x_ratecode_bw_index_get(
1096b1906ceaSLachlan Hodges 			tx_info->rates[i].mm81x_ratecode);
1097b1906ceaSLachlan Hodges 
1098b1906ceaSLachlan Hodges 		if (bw_idx == DOT11_BANDWIDTH_1MHZ) {
1099b1906ceaSLachlan Hodges 			mcs0_last_idx = i;
1100b1906ceaSLachlan Hodges 			if (mcs0_first_idx == -1)
1101b1906ceaSLachlan Hodges 				mcs0_first_idx = i;
1102b1906ceaSLachlan Hodges 		}
1103b1906ceaSLachlan Hodges 
1104b1906ceaSLachlan Hodges 		/*
1105b1906ceaSLachlan Hodges 		 * If the count is 0 then we are at the end of the table.
1106b1906ceaSLachlan Hodges 		 * Break to allow us to reuse i indicating the end of the
1107b1906ceaSLachlan Hodges 		 * table.
1108b1906ceaSLachlan Hodges 		 */
1109b1906ceaSLachlan Hodges 		if (tx_info->rates[i].count == 0)
1110b1906ceaSLachlan Hodges 			break;
1111b1906ceaSLachlan Hodges 	}
1112b1906ceaSLachlan Hodges 
1113b1906ceaSLachlan Hodges 	/* If there aren't any MCS0 (at 1MHz) entries we are done. */
1114b1906ceaSLachlan Hodges 	if (mcs0_first_idx < 0)
1115b1906ceaSLachlan Hodges 		return;
1116b1906ceaSLachlan Hodges 
1117b1906ceaSLachlan Hodges 	/*
1118b1906ceaSLachlan Hodges 	 * If we are in MCS10_MODE_AUTO add MCS10 counts to the table if they
1119b1906ceaSLachlan Hodges 	 * will fit. There should be three cases:
1120b1906ceaSLachlan Hodges 	 *
1121b1906ceaSLachlan Hodges 	 * - There is one MSC0 entry and the table is full -> do nothing
1122b1906ceaSLachlan Hodges 	 * - There is one MSC0 entry and the table has space -> adjust MSC0
1123b1906ceaSLachlan Hodges 	 *   down and add MCS 10
1124b1906ceaSLachlan Hodges 	 * - There are multiple MCS0 entries -> replace entries after the first
1125b1906ceaSLachlan Hodges 	 *   with MCS 10
1126b1906ceaSLachlan Hodges 	 */
1127b1906ceaSLachlan Hodges 	/* Case 3 - replace additional entries. */
1128b1906ceaSLachlan Hodges 	if (mcs0_last_idx > mcs0_first_idx) {
1129b1906ceaSLachlan Hodges 		for (j = mcs0_first_idx + 1; j < i; j++) {
1130b1906ceaSLachlan Hodges 			enum dot11_bandwidth bw_idx =
1131b1906ceaSLachlan Hodges 				mm81x_ratecode_bw_index_get(
1132b1906ceaSLachlan Hodges 					tx_info->rates[j].mm81x_ratecode);
1133b1906ceaSLachlan Hodges 			u8 mcs_index = mm81x_ratecode_mcs_index_get(
1134b1906ceaSLachlan Hodges 				tx_info->rates[j].mm81x_ratecode);
1135b1906ceaSLachlan Hodges 			if (mcs_index == 0 && bw_idx == DOT11_BANDWIDTH_1MHZ) {
1136b1906ceaSLachlan Hodges 				mm81x_ratecode_mcs_index_set(
1137b1906ceaSLachlan Hodges 					&tx_info->rates[j].mm81x_ratecode, 10);
1138b1906ceaSLachlan Hodges 			}
1139b1906ceaSLachlan Hodges 		}
1140b1906ceaSLachlan Hodges 		/* Case 2 - add additional MCS10 entry. */
1141b1906ceaSLachlan Hodges 	} else if (mcs0_last_idx == mcs0_first_idx &&
1142b1906ceaSLachlan Hodges 		   i < (IEEE80211_TX_MAX_RATES)) {
1143b1906ceaSLachlan Hodges 		int pre_mcs10_mcs0_count =
1144b1906ceaSLachlan Hodges 			min_t(u8, tx_info->rates[mcs0_last_idx].count,
1145b1906ceaSLachlan Hodges 			      MCS0_BEFORE_MCS10_COUNT);
1146b1906ceaSLachlan Hodges 		int mcs10_count = tx_info->rates[mcs0_last_idx].count -
1147b1906ceaSLachlan Hodges 				  pre_mcs10_mcs0_count;
1148b1906ceaSLachlan Hodges 
1149b1906ceaSLachlan Hodges 		/*
1150b1906ceaSLachlan Hodges 		 * If there were less retries than our desired minimum MCS0 we
1151b1906ceaSLachlan Hodges 		 * don't add MCS10 retries.
1152b1906ceaSLachlan Hodges 		 */
1153b1906ceaSLachlan Hodges 		if (mcs10_count > 0) {
1154b1906ceaSLachlan Hodges 			/* Use the same flags for MCS10 as MCS0. */
1155b1906ceaSLachlan Hodges 			tx_info->rates[i].mm81x_ratecode =
1156b1906ceaSLachlan Hodges 				tx_info->rates[mcs0_last_idx].mm81x_ratecode;
1157b1906ceaSLachlan Hodges 			mm81x_ratecode_mcs_index_set(
1158b1906ceaSLachlan Hodges 				&tx_info->rates[i].mm81x_ratecode, 10);
1159b1906ceaSLachlan Hodges 			tx_info->rates[mcs0_last_idx].count =
1160b1906ceaSLachlan Hodges 				pre_mcs10_mcs0_count;
1161b1906ceaSLachlan Hodges 			tx_info->rates[i].count = mcs10_count;
1162b1906ceaSLachlan Hodges 		}
1163b1906ceaSLachlan Hodges 	}
1164b1906ceaSLachlan Hodges }
1165b1906ceaSLachlan Hodges 
mm81x_tx_h_check_aggr(struct ieee80211_sta * pubsta,struct sk_buff * skb)1166b1906ceaSLachlan Hodges void mm81x_tx_h_check_aggr(struct ieee80211_sta *pubsta, struct sk_buff *skb)
1167b1906ceaSLachlan Hodges {
1168b1906ceaSLachlan Hodges 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
1169b1906ceaSLachlan Hodges 	struct mm81x_sta *mors_sta = (struct mm81x_sta *)pubsta->drv_priv;
1170b1906ceaSLachlan Hodges 	u8 tid = ieee80211_get_tid(hdr);
1171b1906ceaSLachlan Hodges 
1172b1906ceaSLachlan Hodges 	/* we are already aggregating */
1173b1906ceaSLachlan Hodges 	if (mors_sta->tid_tx[tid] || mors_sta->tid_start_tx[tid])
1174b1906ceaSLachlan Hodges 		return;
1175b1906ceaSLachlan Hodges 
1176b1906ceaSLachlan Hodges 	if (mors_sta->state < IEEE80211_STA_AUTHORIZED)
1177b1906ceaSLachlan Hodges 		return;
1178b1906ceaSLachlan Hodges 
1179b1906ceaSLachlan Hodges 	if (skb_get_queue_mapping(skb) == IEEE80211_AC_VO)
1180b1906ceaSLachlan Hodges 		return;
1181b1906ceaSLachlan Hodges 
1182b1906ceaSLachlan Hodges 	if (unlikely(!ieee80211_is_data_qos(hdr->frame_control)))
1183b1906ceaSLachlan Hodges 		return;
1184b1906ceaSLachlan Hodges 
1185b1906ceaSLachlan Hodges 	if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))
1186b1906ceaSLachlan Hodges 		return;
1187b1906ceaSLachlan Hodges 
1188b1906ceaSLachlan Hodges 	mors_sta->tid_start_tx[tid] = true;
1189b1906ceaSLachlan Hodges 	ieee80211_start_tx_ba_session(pubsta, tid, 0);
1190b1906ceaSLachlan Hodges }
1191b1906ceaSLachlan Hodges 
mm81x_tx_h_get_attempts(struct mm81x * mors,struct mm81x_skb_tx_status * tx_sts)1192b1906ceaSLachlan Hodges int mm81x_tx_h_get_attempts(struct mm81x *mors,
1193b1906ceaSLachlan Hodges 			    struct mm81x_skb_tx_status *tx_sts)
1194b1906ceaSLachlan Hodges {
1195b1906ceaSLachlan Hodges 	int attempts = 0;
1196b1906ceaSLachlan Hodges 	int i;
1197b1906ceaSLachlan Hodges 	int count = min_t(int, MM81X_SKB_MAX_RATES, IEEE80211_TX_MAX_RATES);
1198b1906ceaSLachlan Hodges 
1199b1906ceaSLachlan Hodges 	for (i = 0; i < count; i++) {
1200b1906ceaSLachlan Hodges 		if (tx_sts->rates[i].count > 0)
1201b1906ceaSLachlan Hodges 			attempts += tx_sts->rates[i].count;
1202b1906ceaSLachlan Hodges 		else
1203b1906ceaSLachlan Hodges 			break;
1204b1906ceaSLachlan Hodges 	}
1205b1906ceaSLachlan Hodges 
1206b1906ceaSLachlan Hodges 	return attempts;
1207b1906ceaSLachlan Hodges }
1208b1906ceaSLachlan Hodges 
mm81x_tx_h_fill_info(struct mm81x * mors,struct mm81x_skb_tx_info * tx_info,struct sk_buff * skb,struct ieee80211_vif * vif,int tx_bw_mhz,struct ieee80211_sta * sta)1209b1906ceaSLachlan Hodges static void mm81x_tx_h_fill_info(struct mm81x *mors,
1210b1906ceaSLachlan Hodges 				 struct mm81x_skb_tx_info *tx_info,
1211b1906ceaSLachlan Hodges 				 struct sk_buff *skb, struct ieee80211_vif *vif,
1212b1906ceaSLachlan Hodges 				 int tx_bw_mhz, struct ieee80211_sta *sta)
1213b1906ceaSLachlan Hodges {
1214b1906ceaSLachlan Hodges 	int i;
1215b1906ceaSLachlan Hodges 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
1216b1906ceaSLachlan Hodges 	struct mm81x_vif *mors_vif = ieee80211_vif_to_mors_vif(vif);
1217b1906ceaSLachlan Hodges 	struct mm81x_sta *mors_sta = NULL;
1218b1906ceaSLachlan Hodges 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
1219b1906ceaSLachlan Hodges 	int op_bw_mhz = cfg80211_chandef_get_width(&mors->chandef);
1220b1906ceaSLachlan Hodges 	u8 tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
1221b1906ceaSLachlan Hodges 	bool rts_allowed = op_bw_mhz < 8;
1222b1906ceaSLachlan Hodges 
1223b1906ceaSLachlan Hodges 	if (sta)
1224b1906ceaSLachlan Hodges 		mors_sta = (struct mm81x_sta *)sta->drv_priv;
1225b1906ceaSLachlan Hodges 
1226b1906ceaSLachlan Hodges 	rts_allowed &= mm81x_tx_h_pkt_over_rts_threshold(mors, info, skb);
1227b1906ceaSLachlan Hodges 
1228b1906ceaSLachlan Hodges 	mm81x_rc_sta_fill_tx_rates(mors, tx_info, skb, sta, tx_bw_mhz,
1229b1906ceaSLachlan Hodges 				   rts_allowed);
1230b1906ceaSLachlan Hodges 
1231b1906ceaSLachlan Hodges 	for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
1232b1906ceaSLachlan Hodges 		if (rts_allowed)
1233b1906ceaSLachlan Hodges 			mm81x_ratecode_enable_rts(
1234b1906ceaSLachlan Hodges 				&tx_info->rates[i].mm81x_ratecode);
1235b1906ceaSLachlan Hodges 
1236b1906ceaSLachlan Hodges 		if (info->control.rates[i].flags & IEEE80211_TX_RC_SHORT_GI)
1237b1906ceaSLachlan Hodges 			mm81x_ratecode_enable_sgi(
1238b1906ceaSLachlan Hodges 				&tx_info->rates[i].mm81x_ratecode);
1239b1906ceaSLachlan Hodges 	}
1240b1906ceaSLachlan Hodges 
1241b1906ceaSLachlan Hodges 	/* Apply change of MCS0 to MCS10 if required. */
1242b1906ceaSLachlan Hodges 	mm81x_tx_h_apply_mcs10(mors, tx_info);
1243b1906ceaSLachlan Hodges 
1244b1906ceaSLachlan Hodges 	tx_info->flags |=
1245b1906ceaSLachlan Hodges 		cpu_to_le32(MM81X_TX_CONF_FLAGS_VIF_ID_SET(mors_vif->id));
1246b1906ceaSLachlan Hodges 
1247b1906ceaSLachlan Hodges 	if (info->flags & IEEE80211_TX_CTL_AMPDU)
1248b1906ceaSLachlan Hodges 		tx_info->flags |= cpu_to_le32(MM81X_TX_CONF_FLAGS_CTL_AMPDU);
1249b1906ceaSLachlan Hodges 
1250b1906ceaSLachlan Hodges 	if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM)
1251b1906ceaSLachlan Hodges 		tx_info->flags |=
1252b1906ceaSLachlan Hodges 			cpu_to_le32(MM81X_TX_CONF_FLAGS_SEND_AFTER_DTIM);
1253b1906ceaSLachlan Hodges 
1254b1906ceaSLachlan Hodges 	if (info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER) {
1255b1906ceaSLachlan Hodges 		tx_info->flags |= cpu_to_le32(MM81X_TX_CONF_NO_PS_BUFFER);
1256b1906ceaSLachlan Hodges 
1257b1906ceaSLachlan Hodges 		if (info->flags & IEEE80211_TX_STATUS_EOSP)
1258b1906ceaSLachlan Hodges 			tx_info->flags |= cpu_to_le32(
1259b1906ceaSLachlan Hodges 				MM81X_TX_CONF_FLAGS_IMMEDIATE_REPORT);
1260b1906ceaSLachlan Hodges 	} else if (ieee80211_is_mgmt(hdr->frame_control) &&
1261b1906ceaSLachlan Hodges 		   !ieee80211_is_bufferable_mmpdu(skb)) {
1262b1906ceaSLachlan Hodges 		tx_info->flags |= cpu_to_le32(MM81X_TX_CONF_NO_PS_BUFFER);
1263b1906ceaSLachlan Hodges 	}
1264b1906ceaSLachlan Hodges 
1265b1906ceaSLachlan Hodges 	if (info->control.hw_key) {
1266b1906ceaSLachlan Hodges 		tx_info->flags |= cpu_to_le32(MM81X_TX_CONF_FLAGS_HW_ENCRYPT);
1267b1906ceaSLachlan Hodges 		tx_info->flags |= cpu_to_le32(MM81X_TX_CONF_FLAGS_KEY_IDX_SET(
1268b1906ceaSLachlan Hodges 			info->control.hw_key->hw_key_idx));
1269b1906ceaSLachlan Hodges 	}
1270b1906ceaSLachlan Hodges 
1271b1906ceaSLachlan Hodges 	tx_info->tid = tid;
1272b1906ceaSLachlan Hodges 	if (mors_sta) {
1273b1906ceaSLachlan Hodges 		tx_info->tid_params = mors_sta->tid_params[tid];
1274b1906ceaSLachlan Hodges 
1275b1906ceaSLachlan Hodges 		if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT) {
1276b1906ceaSLachlan Hodges 			if (mors_sta->tx_ps_filter_en)
1277b1906ceaSLachlan Hodges 				dev_dbg(mors->dev,
1278b1906ceaSLachlan Hodges 					"TX ps filter cleared sta[%pM]",
1279b1906ceaSLachlan Hodges 					mors_sta->addr);
1280b1906ceaSLachlan Hodges 			mors_sta->tx_ps_filter_en = false;
1281b1906ceaSLachlan Hodges 		}
1282b1906ceaSLachlan Hodges 	}
1283b1906ceaSLachlan Hodges }
1284b1906ceaSLachlan Hodges 
mm81x_mac_ops_tx(struct ieee80211_hw * hw,struct ieee80211_tx_control * control,struct sk_buff * skb)1285b1906ceaSLachlan Hodges static void mm81x_mac_ops_tx(struct ieee80211_hw *hw,
1286b1906ceaSLachlan Hodges 			     struct ieee80211_tx_control *control,
1287b1906ceaSLachlan Hodges 			     struct sk_buff *skb)
1288b1906ceaSLachlan Hodges {
1289b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1290b1906ceaSLachlan Hodges 	struct mm81x_skbq *mq = NULL;
1291b1906ceaSLachlan Hodges 	struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
1292b1906ceaSLachlan Hodges 	struct ieee80211_vif *vif = info->control.vif;
1293b1906ceaSLachlan Hodges 	struct mm81x_skb_tx_info tx_info = { 0 };
1294b1906ceaSLachlan Hodges 	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
1295b1906ceaSLachlan Hodges 	bool is_mgmt = ieee80211_is_mgmt(hdr->frame_control);
1296b1906ceaSLachlan Hodges 	int tx_bw_mhz = cfg80211_chandef_get_width(&mors->chandef);
1297b1906ceaSLachlan Hodges 	struct ieee80211_sta *sta = control->sta;
1298b1906ceaSLachlan Hodges 	int max_tx_bw = 0, sta_max_bw_mhz = 0;
1299b1906ceaSLachlan Hodges 
1300b1906ceaSLachlan Hodges 	if (sta) {
1301b1906ceaSLachlan Hodges 		struct mm81x_sta *mors_sta = (struct mm81x_sta *)sta->drv_priv;
1302b1906ceaSLachlan Hodges 
1303b1906ceaSLachlan Hodges 		sta_max_bw_mhz = mors_sta->max_bw_mhz;
1304b1906ceaSLachlan Hodges 	}
1305b1906ceaSLachlan Hodges 
1306b1906ceaSLachlan Hodges 	max_tx_bw = mm81x_tx_h_get_max_bw(mors);
1307b1906ceaSLachlan Hodges 	tx_bw_mhz = min(max_tx_bw, tx_bw_mhz);
1308b1906ceaSLachlan Hodges 
1309b1906ceaSLachlan Hodges 	if (is_mgmt)
1310b1906ceaSLachlan Hodges 		tx_bw_mhz = cfg80211_chandef_s1g_pri_width(&mors->chandef);
1311b1906ceaSLachlan Hodges 	if (sta_max_bw_mhz)
1312b1906ceaSLachlan Hodges 		tx_bw_mhz = min(tx_bw_mhz, sta_max_bw_mhz);
1313b1906ceaSLachlan Hodges 	if (ieee80211_is_probe_resp(hdr->frame_control))
1314b1906ceaSLachlan Hodges 		tx_bw_mhz = 1;
1315b1906ceaSLachlan Hodges 
1316b1906ceaSLachlan Hodges 	mm81x_tx_h_fill_info(mors, &tx_info, skb, vif, tx_bw_mhz, sta);
1317b1906ceaSLachlan Hodges 
1318b1906ceaSLachlan Hodges 	if (mm81x_tx_h_ps_filtered_for_sta(mors, skb, sta))
1319b1906ceaSLachlan Hodges 		return;
1320b1906ceaSLachlan Hodges 
1321b1906ceaSLachlan Hodges 	if (is_mgmt)
1322b1906ceaSLachlan Hodges 		mq = mm81x_hif_get_tx_mgmt_queue(mors);
1323b1906ceaSLachlan Hodges 	else
1324b1906ceaSLachlan Hodges 		mq = mm81x_hif_get_tx_data_queue(mors,
1325b1906ceaSLachlan Hodges 						 dot11_tid_to_ac(tx_info.tid));
1326b1906ceaSLachlan Hodges 
1327b1906ceaSLachlan Hodges 	mm81x_skbq_skb_tx(mq, &skb, &tx_info,
1328b1906ceaSLachlan Hodges 			  (is_mgmt) ? MM81X_SKB_CHAN_MGMT :
1329b1906ceaSLachlan Hodges 				      MM81X_SKB_CHAN_DATA);
1330b1906ceaSLachlan Hodges }
1331b1906ceaSLachlan Hodges 
mm81x_mac_ops_stop(struct ieee80211_hw * hw,bool suspend)1332b1906ceaSLachlan Hodges static void mm81x_mac_ops_stop(struct ieee80211_hw *hw, bool suspend)
1333b1906ceaSLachlan Hodges {
1334b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1335b1906ceaSLachlan Hodges 
1336b1906ceaSLachlan Hodges 	mors->started = false;
1337b1906ceaSLachlan Hodges }
1338b1906ceaSLachlan Hodges 
mm81x_mac_beacon_finish(struct mm81x_vif * mors_vif)1339b1906ceaSLachlan Hodges static void mm81x_mac_beacon_finish(struct mm81x_vif *mors_vif)
1340b1906ceaSLachlan Hodges {
1341b1906ceaSLachlan Hodges 	struct mm81x *mors = mm81x_vif_to_mors(mors_vif);
1342b1906ceaSLachlan Hodges 
1343b1906ceaSLachlan Hodges 	mm81x_mac_beacon_irq_enable(mors_vif, false);
1344b1906ceaSLachlan Hodges 	cancel_work_sync(&mors_vif->u.ap.beacon_work);
1345b1906ceaSLachlan Hodges 	/*
1346b1906ceaSLachlan Hodges 	 * Side effect of the restarting required when
1347b1906ceaSLachlan Hodges 	 * reacting to regdom changes...
1348b1906ceaSLachlan Hodges 	 */
1349b1906ceaSLachlan Hodges 	atomic_add_unless(&mors->num_bcn_vifs, -1, 0);
1350b1906ceaSLachlan Hodges }
1351b1906ceaSLachlan Hodges 
mm81x_mac_ops_remove_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)1352b1906ceaSLachlan Hodges static void mm81x_mac_ops_remove_interface(struct ieee80211_hw *hw,
1353b1906ceaSLachlan Hodges 					   struct ieee80211_vif *vif)
1354b1906ceaSLachlan Hodges {
1355b1906ceaSLachlan Hodges 	int ret;
1356b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1357b1906ceaSLachlan Hodges 	struct mm81x_vif *mors_vif = (struct mm81x_vif *)vif->drv_priv;
1358b1906ceaSLachlan Hodges 
1359b1906ceaSLachlan Hodges 	ret = mm81x_cmd_rm_if(mors, mors_vif->id);
1360b1906ceaSLachlan Hodges 	if (ret)
1361b1906ceaSLachlan Hodges 		dev_err(mors->dev, "mm81x_cmd_rm_if failed %d", ret);
1362b1906ceaSLachlan Hodges 
1363b1906ceaSLachlan Hodges 	RCU_INIT_POINTER(mors->vifs[mors_vif->id], NULL);
1364b1906ceaSLachlan Hodges }
1365b1906ceaSLachlan Hodges 
mm81x_mac_get_max_txpower(struct mm81x * mors)1366b1906ceaSLachlan Hodges static s32 mm81x_mac_get_max_txpower(struct mm81x *mors)
1367b1906ceaSLachlan Hodges {
1368b1906ceaSLachlan Hodges 	int ret;
1369b1906ceaSLachlan Hodges 	s32 power_mbm;
1370b1906ceaSLachlan Hodges 
1371b1906ceaSLachlan Hodges 	/* Retrieve maximum TX power the chip can transmit */
1372b1906ceaSLachlan Hodges 	ret = mm81x_cmd_get_max_txpower(mors, &power_mbm);
1373b1906ceaSLachlan Hodges 	if (ret) {
1374b1906ceaSLachlan Hodges 		dev_err(mors->dev, "using default tx max power %d mBm",
1375b1906ceaSLachlan Hodges 			MAX_TX_POWER_MBM);
1376b1906ceaSLachlan Hodges 		return MAX_TX_POWER_MBM;
1377b1906ceaSLachlan Hodges 	}
1378b1906ceaSLachlan Hodges 
1379b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "Max tx power detected %d mBm", power_mbm);
1380b1906ceaSLachlan Hodges 	return power_mbm;
1381b1906ceaSLachlan Hodges }
1382b1906ceaSLachlan Hodges 
mm81x_mac_set_txpower(struct mm81x * mors,s32 power_mbm)1383b1906ceaSLachlan Hodges static s32 mm81x_mac_set_txpower(struct mm81x *mors, s32 power_mbm)
1384b1906ceaSLachlan Hodges {
1385b1906ceaSLachlan Hodges 	int ret;
1386b1906ceaSLachlan Hodges 	s32 out_power_mbm;
1387b1906ceaSLachlan Hodges 
1388b1906ceaSLachlan Hodges 	if (mors->tx_max_power_mbm == INT_MAX)
1389b1906ceaSLachlan Hodges 		mors->tx_max_power_mbm = mm81x_mac_get_max_txpower(mors);
1390b1906ceaSLachlan Hodges 
1391b1906ceaSLachlan Hodges 	power_mbm = min(power_mbm, mors->tx_max_power_mbm);
1392b1906ceaSLachlan Hodges 	if (power_mbm == mors->tx_power_mbm)
1393b1906ceaSLachlan Hodges 		return mors->tx_power_mbm;
1394b1906ceaSLachlan Hodges 
1395b1906ceaSLachlan Hodges 	ret = mm81x_cmd_set_txpower(mors, &out_power_mbm, power_mbm);
1396b1906ceaSLachlan Hodges 	if (ret) {
1397b1906ceaSLachlan Hodges 		dev_err(mors->dev, "failed, power %d mBm ret %d", power_mbm,
1398b1906ceaSLachlan Hodges 			ret);
1399b1906ceaSLachlan Hodges 		return mors->tx_power_mbm;
1400b1906ceaSLachlan Hodges 	}
1401b1906ceaSLachlan Hodges 
1402b1906ceaSLachlan Hodges 	if (out_power_mbm != mors->tx_power_mbm) {
1403b1906ceaSLachlan Hodges 		dev_dbg(mors->dev, "%d -> %d mBm", mors->tx_power_mbm,
1404b1906ceaSLachlan Hodges 			out_power_mbm);
1405b1906ceaSLachlan Hodges 		mors->tx_power_mbm = out_power_mbm;
1406b1906ceaSLachlan Hodges 	}
1407b1906ceaSLachlan Hodges 
1408b1906ceaSLachlan Hodges 	return mors->tx_power_mbm;
1409b1906ceaSLachlan Hodges }
1410b1906ceaSLachlan Hodges 
mm81x_mac_set_channel(struct mm81x * mors,u32 op_chan_freq_hz,u8 pri_1mhz_chan_idx,u8 op_bw_mhz,u8 pri_bw_mhz)1411b1906ceaSLachlan Hodges static int mm81x_mac_set_channel(struct mm81x *mors, u32 op_chan_freq_hz,
1412b1906ceaSLachlan Hodges 				 u8 pri_1mhz_chan_idx, u8 op_bw_mhz,
1413b1906ceaSLachlan Hodges 				 u8 pri_bw_mhz)
1414b1906ceaSLachlan Hodges {
1415b1906ceaSLachlan Hodges 	int ret;
1416b1906ceaSLachlan Hodges 
1417b1906ceaSLachlan Hodges 	ret = mm81x_cmd_set_channel(mors, op_chan_freq_hz, pri_1mhz_chan_idx,
1418b1906ceaSLachlan Hodges 				    op_bw_mhz, pri_bw_mhz, &mors->tx_power_mbm);
1419b1906ceaSLachlan Hodges 	if (ret) {
1420b1906ceaSLachlan Hodges 		dev_err(mors->dev, "mm81x_cmd_set_channel() failed, ret %d",
1421b1906ceaSLachlan Hodges 			ret);
1422b1906ceaSLachlan Hodges 		return ret;
1423b1906ceaSLachlan Hodges 	}
1424b1906ceaSLachlan Hodges 
1425b1906ceaSLachlan Hodges 	mm81x_mac_set_txpower(mors, mors->tx_power_mbm);
1426b1906ceaSLachlan Hodges 	return 0;
1427b1906ceaSLachlan Hodges }
1428b1906ceaSLachlan Hodges 
mm81x_mac_pri_chan_to_index(const struct cfg80211_chan_def * chandef)1429b1906ceaSLachlan Hodges static u8 mm81x_mac_pri_chan_to_index(const struct cfg80211_chan_def *chandef)
1430b1906ceaSLachlan Hodges {
1431b1906ceaSLachlan Hodges 	u32 bw_mhz = cfg80211_chandef_get_width(chandef);
1432b1906ceaSLachlan Hodges 	u32 op_center_khz = ieee80211_chandef_to_khz(chandef);
1433b1906ceaSLachlan Hodges 	u32 first_1mhz_center_khz = op_center_khz - (bw_mhz * 500) + 500;
1434b1906ceaSLachlan Hodges 	u32 pri_1mhz_khz = ieee80211_channel_to_khz(chandef->chan);
1435b1906ceaSLachlan Hodges 
1436b1906ceaSLachlan Hodges 	return (pri_1mhz_khz - first_1mhz_center_khz) / 1000;
1437b1906ceaSLachlan Hodges }
1438b1906ceaSLachlan Hodges 
mm81x_mac_ops_change_channel(struct ieee80211_hw * hw,struct cfg80211_chan_def * chandef)1439b1906ceaSLachlan Hodges static int mm81x_mac_ops_change_channel(struct ieee80211_hw *hw,
1440b1906ceaSLachlan Hodges 					struct cfg80211_chan_def *chandef)
1441b1906ceaSLachlan Hodges {
1442b1906ceaSLachlan Hodges 	int ret;
1443b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1444b1906ceaSLachlan Hodges 	u64 freq_hz = KHZ_TO_HZ(ieee80211_chandef_to_khz(chandef));
1445b1906ceaSLachlan Hodges 	u8 op_bw_mhz = cfg80211_chandef_get_width(chandef);
1446b1906ceaSLachlan Hodges 	u8 pri_1mhz_idx = mm81x_mac_pri_chan_to_index(chandef);
1447b1906ceaSLachlan Hodges 	int pri_chan_width_mhz = cfg80211_chandef_s1g_pri_width(chandef);
1448b1906ceaSLachlan Hodges 
1449b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "ch: freq=%llu Hz bw=%u pri_idx=%d pri_bw=%d",
1450b1906ceaSLachlan Hodges 		freq_hz, op_bw_mhz, pri_1mhz_idx, pri_chan_width_mhz);
1451b1906ceaSLachlan Hodges 
1452b1906ceaSLachlan Hodges 	ret = mm81x_mac_set_channel(mors, freq_hz, (u8)pri_1mhz_idx, op_bw_mhz,
1453b1906ceaSLachlan Hodges 				    pri_chan_width_mhz);
1454b1906ceaSLachlan Hodges 	if (ret)
1455b1906ceaSLachlan Hodges 		return ret;
1456b1906ceaSLachlan Hodges 
1457b1906ceaSLachlan Hodges 	memcpy(&mors->chandef, chandef, sizeof(mors->chandef));
1458b1906ceaSLachlan Hodges 	return 0;
1459b1906ceaSLachlan Hodges }
1460b1906ceaSLachlan Hodges 
mm81x_mac_ops_config(struct ieee80211_hw * hw,int radio_idx,u32 changed)1461b1906ceaSLachlan Hodges static int mm81x_mac_ops_config(struct ieee80211_hw *hw, int radio_idx,
1462b1906ceaSLachlan Hodges 				u32 changed)
1463b1906ceaSLachlan Hodges {
1464b1906ceaSLachlan Hodges 	int ret;
1465b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1466b1906ceaSLachlan Hodges 	struct ieee80211_conf *conf = &hw->conf;
1467b1906ceaSLachlan Hodges 	struct ieee80211_channel *channel = conf->chandef.chan;
1468b1906ceaSLachlan Hodges 
1469b1906ceaSLachlan Hodges 	if (!mors->started)
1470b1906ceaSLachlan Hodges 		return 0;
1471b1906ceaSLachlan Hodges 
1472b1906ceaSLachlan Hodges 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1473b1906ceaSLachlan Hodges 		ret = mm81x_mac_ops_change_channel(hw, &conf->chandef);
1474b1906ceaSLachlan Hodges 		if (ret < 0)
1475b1906ceaSLachlan Hodges 			return ret;
1476b1906ceaSLachlan Hodges 	}
1477b1906ceaSLachlan Hodges 
1478b1906ceaSLachlan Hodges 	if ((changed & IEEE80211_CONF_CHANGE_POWER) &&
1479b1906ceaSLachlan Hodges 	    !(changed & IEEE80211_CONF_CHANGE_CHANNEL) &&
1480b1906ceaSLachlan Hodges 	    !(conf->flags & IEEE80211_CONF_MONITOR)) {
1481b1906ceaSLachlan Hodges 		s32 power_mbm = DBM_TO_MBM(conf->power_level);
1482b1906ceaSLachlan Hodges 
1483b1906ceaSLachlan Hodges 		power_mbm = min(DBM_TO_MBM(channel->max_reg_power), power_mbm);
1484b1906ceaSLachlan Hodges 		power_mbm = mm81x_mac_set_txpower(mors, power_mbm);
1485b1906ceaSLachlan Hodges 		conf->power_level = MBM_TO_DBM(power_mbm);
1486b1906ceaSLachlan Hodges 	}
1487b1906ceaSLachlan Hodges 
1488b1906ceaSLachlan Hodges 	return 0;
1489b1906ceaSLachlan Hodges }
1490b1906ceaSLachlan Hodges 
mm81x_mac_ops_get_txpower(struct ieee80211_hw * hw,struct ieee80211_vif * vif,unsigned int link_id,int * dbm)1491b1906ceaSLachlan Hodges static int mm81x_mac_ops_get_txpower(struct ieee80211_hw *hw,
1492b1906ceaSLachlan Hodges 				     struct ieee80211_vif *vif,
1493b1906ceaSLachlan Hodges 				     unsigned int link_id, int *dbm)
1494b1906ceaSLachlan Hodges {
1495b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1496b1906ceaSLachlan Hodges 	struct ieee80211_chanctx_conf *chanctx_conf;
1497b1906ceaSLachlan Hodges 	struct cfg80211_chan_def *chandef = &vif->bss_conf.chanreq.oper;
1498b1906ceaSLachlan Hodges 
1499b1906ceaSLachlan Hodges 	scoped_guard(rcu) {
1500b1906ceaSLachlan Hodges 		chanctx_conf = rcu_access_pointer(vif->bss_conf.chanctx_conf);
1501b1906ceaSLachlan Hodges 		if (!chanctx_conf ||
1502b1906ceaSLachlan Hodges 		    !cfg80211_chandef_identical(chandef, &chanctx_conf->def))
1503b1906ceaSLachlan Hodges 			return -ENODATA;
1504b1906ceaSLachlan Hodges 	}
1505b1906ceaSLachlan Hodges 
1506b1906ceaSLachlan Hodges 	*dbm = MBM_TO_DBM(mors->tx_power_mbm);
1507b1906ceaSLachlan Hodges 	return 0;
1508b1906ceaSLachlan Hodges }
1509b1906ceaSLachlan Hodges 
mm81x_mac_config_ps(struct mm81x * mors,struct ieee80211_vif * vif)1510b1906ceaSLachlan Hodges static void mm81x_mac_config_ps(struct mm81x *mors, struct ieee80211_vif *vif)
1511b1906ceaSLachlan Hodges {
1512b1906ceaSLachlan Hodges 	bool en_ps = vif->cfg.ps;
1513b1906ceaSLachlan Hodges 
1514b1906ceaSLachlan Hodges 	if (vif->type == NL80211_IFTYPE_AP || !mors->ps.enable)
1515b1906ceaSLachlan Hodges 		return;
1516b1906ceaSLachlan Hodges 
1517b1906ceaSLachlan Hodges 	if (mors->config_ps == en_ps)
1518b1906ceaSLachlan Hodges 		return;
1519b1906ceaSLachlan Hodges 
1520b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "change powersave mode: %d (current %d)", en_ps,
1521b1906ceaSLachlan Hodges 		mors->config_ps);
1522b1906ceaSLachlan Hodges 
1523b1906ceaSLachlan Hodges 	mors->config_ps = en_ps;
1524b1906ceaSLachlan Hodges 
1525b1906ceaSLachlan Hodges 	if (en_ps) {
1526b1906ceaSLachlan Hodges 		mm81x_cmd_set_ps(mors, true);
1527b1906ceaSLachlan Hodges 		mm81x_ps_enable(mors);
1528b1906ceaSLachlan Hodges 	} else {
1529b1906ceaSLachlan Hodges 		mm81x_ps_disable(mors);
1530b1906ceaSLachlan Hodges 		mm81x_cmd_set_ps(mors, false);
1531b1906ceaSLachlan Hodges 	}
1532b1906ceaSLachlan Hodges }
1533b1906ceaSLachlan Hodges 
mm81x_mac_ops_bss_info_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * info,u64 changed)1534b1906ceaSLachlan Hodges static void mm81x_mac_ops_bss_info_changed(struct ieee80211_hw *hw,
1535b1906ceaSLachlan Hodges 					   struct ieee80211_vif *vif,
1536b1906ceaSLachlan Hodges 					   struct ieee80211_bss_conf *info,
1537b1906ceaSLachlan Hodges 					   u64 changed)
1538b1906ceaSLachlan Hodges {
1539b1906ceaSLachlan Hodges 	int ret;
1540b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1541b1906ceaSLachlan Hodges 	struct mm81x_vif *mors_vif = (struct mm81x_vif *)vif->drv_priv;
1542b1906ceaSLachlan Hodges 
1543b1906ceaSLachlan Hodges 	if (changed & BSS_CHANGED_PS)
1544b1906ceaSLachlan Hodges 		mm81x_mac_config_ps(mors, vif);
1545b1906ceaSLachlan Hodges 
1546b1906ceaSLachlan Hodges 	if (changed & BSS_CHANGED_BEACON_ENABLED) {
1547b1906ceaSLachlan Hodges 		mm81x_cmd_config_beacon_timer(mors, mors_vif,
1548b1906ceaSLachlan Hodges 					      info->enable_beacon);
1549b1906ceaSLachlan Hodges 
1550b1906ceaSLachlan Hodges 		if (!info->enable_beacon)
1551b1906ceaSLachlan Hodges 			mm81x_mac_beacon_finish(mors_vif);
1552b1906ceaSLachlan Hodges 	}
1553b1906ceaSLachlan Hodges 
1554b1906ceaSLachlan Hodges 	if (changed & BSS_CHANGED_BEACON_INT || changed & BSS_CHANGED_SSID) {
1555b1906ceaSLachlan Hodges 		ret = mm81x_cmd_cfg_bss(mors, mors_vif->id, info->beacon_int,
1556b1906ceaSLachlan Hodges 					info->dtim_period,
1557b1906ceaSLachlan Hodges 					mm81x_vif_generate_cssid(vif));
1558b1906ceaSLachlan Hodges 		if (ret)
1559b1906ceaSLachlan Hodges 			dev_err(mors->dev, "mm81x_cmd_cfg_bss failed %d", ret);
1560b1906ceaSLachlan Hodges 	}
1561b1906ceaSLachlan Hodges }
1562b1906ceaSLachlan Hodges 
mm81x_mac_ops_prepare_multicast(struct ieee80211_hw * hw,struct netdev_hw_addr_list * mc_list)1563b1906ceaSLachlan Hodges static u64 mm81x_mac_ops_prepare_multicast(struct ieee80211_hw *hw,
1564b1906ceaSLachlan Hodges 					   struct netdev_hw_addr_list *mc_list)
1565b1906ceaSLachlan Hodges {
1566b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1567b1906ceaSLachlan Hodges 	struct mcast_filter *filter;
1568b1906ceaSLachlan Hodges 	struct netdev_hw_addr *addr;
1569b1906ceaSLachlan Hodges 	u16 addr_count = netdev_hw_addr_list_count(mc_list);
1570b1906ceaSLachlan Hodges 	u16 len = sizeof(*filter) + addr_count * sizeof(filter->addr_list[0]);
1571b1906ceaSLachlan Hodges 
1572b1906ceaSLachlan Hodges 	filter = kzalloc(len, GFP_ATOMIC);
1573b1906ceaSLachlan Hodges 	if (!filter)
1574b1906ceaSLachlan Hodges 		return 0;
1575b1906ceaSLachlan Hodges 
1576b1906ceaSLachlan Hodges 	if (addr_count > MCAST_FILTER_COUNT_MAX) {
1577b1906ceaSLachlan Hodges 		dev_warn(
1578b1906ceaSLachlan Hodges 			mors->dev,
1579b1906ceaSLachlan Hodges 			"Multicast filtering disabled - too many groups (%d) > %u",
1580b1906ceaSLachlan Hodges 			addr_count, (u16)MCAST_FILTER_COUNT_MAX);
1581b1906ceaSLachlan Hodges 		filter->count = 0;
1582b1906ceaSLachlan Hodges 	} else {
1583b1906ceaSLachlan Hodges 		netdev_hw_addr_list_for_each(addr, mc_list) {
1584b1906ceaSLachlan Hodges 			dev_dbg(mors->dev, "mcast whitelist (%d): %pM",
1585b1906ceaSLachlan Hodges 				filter->count, addr->addr);
1586b1906ceaSLachlan Hodges 			filter->addr_list[filter->count++] =
1587b1906ceaSLachlan Hodges 				mac2le32(addr->addr);
1588b1906ceaSLachlan Hodges 		}
1589b1906ceaSLachlan Hodges 	}
1590b1906ceaSLachlan Hodges 
1591b1906ceaSLachlan Hodges 	return (u64)(unsigned long)filter;
1592b1906ceaSLachlan Hodges }
1593b1906ceaSLachlan Hodges 
mm81x_mac_ops_configure_filter(struct ieee80211_hw * hw,unsigned int changed_flags,unsigned int * total_flags,u64 multicast)1594b1906ceaSLachlan Hodges static void mm81x_mac_ops_configure_filter(struct ieee80211_hw *hw,
1595b1906ceaSLachlan Hodges 					   unsigned int changed_flags,
1596b1906ceaSLachlan Hodges 					   unsigned int *total_flags,
1597b1906ceaSLachlan Hodges 					   u64 multicast)
1598b1906ceaSLachlan Hodges {
1599b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1600b1906ceaSLachlan Hodges 	struct mcast_filter *cmd = (void *)(unsigned long)multicast;
1601b1906ceaSLachlan Hodges 	struct mm81x_vif *mors_vif = NULL;
1602b1906ceaSLachlan Hodges 	struct ieee80211_vif *vif = NULL;
1603b1906ceaSLachlan Hodges 	int vif_id = 0;
1604b1906ceaSLachlan Hodges 	int ret = 0;
1605b1906ceaSLachlan Hodges 
1606b1906ceaSLachlan Hodges 	if (!cmd)
1607b1906ceaSLachlan Hodges 		goto out;
1608b1906ceaSLachlan Hodges 
1609b1906ceaSLachlan Hodges 	kfree(mors->mcast_filter);
1610b1906ceaSLachlan Hodges 	mors->mcast_filter = cmd;
1611b1906ceaSLachlan Hodges 
1612b1906ceaSLachlan Hodges 	for (vif_id = 0; vif_id < ARRAY_SIZE(mors->vifs); vif_id++) {
1613b1906ceaSLachlan Hodges 		vif = mm81x_rcu_dereference_vif_id(mors, vif_id, false);
1614b1906ceaSLachlan Hodges 		if (!vif)
1615b1906ceaSLachlan Hodges 			continue;
1616b1906ceaSLachlan Hodges 
1617b1906ceaSLachlan Hodges 		mors_vif = ieee80211_vif_to_mors_vif(vif);
1618b1906ceaSLachlan Hodges 
1619b1906ceaSLachlan Hodges 		ret = mm81x_cmd_cfg_multicast_filter(mors, mors_vif);
1620b1906ceaSLachlan Hodges 		if (!ret)
1621b1906ceaSLachlan Hodges 			continue;
1622b1906ceaSLachlan Hodges 
1623b1906ceaSLachlan Hodges 		dev_err(mors->dev, "Multicast filtering failed - rc=%d", ret);
1624b1906ceaSLachlan Hodges 		mors->mcast_filter = NULL;
1625b1906ceaSLachlan Hodges 		kfree(cmd);
1626b1906ceaSLachlan Hodges 		break;
1627b1906ceaSLachlan Hodges 	}
1628b1906ceaSLachlan Hodges 
1629b1906ceaSLachlan Hodges out:
1630b1906ceaSLachlan Hodges 	*total_flags &= 0;
1631b1906ceaSLachlan Hodges }
1632b1906ceaSLachlan Hodges 
mm81x_mac_ops_conf_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,unsigned int link_id,u16 ac,const struct ieee80211_tx_queue_params * params)1633b1906ceaSLachlan Hodges static int mm81x_mac_ops_conf_tx(struct ieee80211_hw *hw,
1634b1906ceaSLachlan Hodges 				 struct ieee80211_vif *vif,
1635b1906ceaSLachlan Hodges 				 unsigned int link_id, u16 ac,
1636b1906ceaSLachlan Hodges 				 const struct ieee80211_tx_queue_params *params)
1637b1906ceaSLachlan Hodges {
1638b1906ceaSLachlan Hodges 	int ret;
1639b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1640b1906ceaSLachlan Hodges 	struct mm81x_queue_params mqp;
1641b1906ceaSLachlan Hodges 
1642b1906ceaSLachlan Hodges 	mqp.aci = map_mac80211q_2_mm81x_aci(ac);
1643b1906ceaSLachlan Hodges 	mqp.aifs = params->aifs;
1644b1906ceaSLachlan Hodges 	mqp.cw_max = params->cw_max;
1645b1906ceaSLachlan Hodges 	mqp.cw_min = params->cw_min;
1646b1906ceaSLachlan Hodges 	mqp.uapsd = params->uapsd;
1647b1906ceaSLachlan Hodges 	mqp.txop = params->txop << 5;
1648b1906ceaSLachlan Hodges 
1649b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "queue:%d txop:%d cw_min:%d cw_max:%d aifs:%d",
1650b1906ceaSLachlan Hodges 		mqp.aci, mqp.txop, mqp.cw_min, mqp.cw_max, mqp.aifs);
1651b1906ceaSLachlan Hodges 
1652b1906ceaSLachlan Hodges 	ret = mm81x_cmd_cfg_qos(mors, &mqp);
1653b1906ceaSLachlan Hodges 	if (ret)
1654b1906ceaSLachlan Hodges 		dev_dbg(mors->dev, "mm81x_cmd_cfg_qos failed %d", ret);
1655b1906ceaSLachlan Hodges 	return ret;
1656b1906ceaSLachlan Hodges }
1657b1906ceaSLachlan Hodges 
mm81x_mac_ops_sta_state(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,enum ieee80211_sta_state old_state,enum ieee80211_sta_state new_state)1658b1906ceaSLachlan Hodges static int mm81x_mac_ops_sta_state(struct ieee80211_hw *hw,
1659b1906ceaSLachlan Hodges 				   struct ieee80211_vif *vif,
1660b1906ceaSLachlan Hodges 				   struct ieee80211_sta *sta,
1661b1906ceaSLachlan Hodges 				   enum ieee80211_sta_state old_state,
1662b1906ceaSLachlan Hodges 				   enum ieee80211_sta_state new_state)
1663b1906ceaSLachlan Hodges {
1664b1906ceaSLachlan Hodges 	u16 aid;
1665b1906ceaSLachlan Hodges 	int ret;
1666b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1667b1906ceaSLachlan Hodges 	struct mm81x_vif *mors_vif = (struct mm81x_vif *)vif->drv_priv;
1668b1906ceaSLachlan Hodges 	struct mm81x_sta *mors_sta = (struct mm81x_sta *)sta->drv_priv;
1669b1906ceaSLachlan Hodges 
1670b1906ceaSLachlan Hodges 	/* Ignore both NOTEXIST to NONE and NONE to NOTEXIST */
1671b1906ceaSLachlan Hodges 	if ((old_state == IEEE80211_STA_NOTEXIST &&
1672b1906ceaSLachlan Hodges 	     new_state == IEEE80211_STA_NONE) ||
1673b1906ceaSLachlan Hodges 	    (old_state == IEEE80211_STA_NONE &&
1674b1906ceaSLachlan Hodges 	     new_state == IEEE80211_STA_NOTEXIST))
1675b1906ceaSLachlan Hodges 		return 0;
1676b1906ceaSLachlan Hodges 
1677b1906ceaSLachlan Hodges 	if (vif->type == NL80211_IFTYPE_STATION)
1678b1906ceaSLachlan Hodges 		aid = vif->cfg.aid;
1679b1906ceaSLachlan Hodges 	else
1680b1906ceaSLachlan Hodges 		aid = sta->aid;
1681b1906ceaSLachlan Hodges 
1682b1906ceaSLachlan Hodges 	ret = mm81x_cmd_sta_state(mors, mors_vif, aid, sta, new_state);
1683b1906ceaSLachlan Hodges 	if (ret < 0)
1684b1906ceaSLachlan Hodges 		goto exit;
1685b1906ceaSLachlan Hodges 
1686b1906ceaSLachlan Hodges 	ether_addr_copy(mors_sta->addr, sta->addr);
1687b1906ceaSLachlan Hodges 	mors_sta->state = new_state;
1688b1906ceaSLachlan Hodges 
1689b1906ceaSLachlan Hodges 	if (new_state > old_state && new_state == IEEE80211_STA_ASSOC) {
1690b1906ceaSLachlan Hodges 		if (vif->type == NL80211_IFTYPE_AP)
1691b1906ceaSLachlan Hodges 			mors_vif->u.ap.num_stas++;
1692b1906ceaSLachlan Hodges 		else if (vif->type == NL80211_IFTYPE_STATION)
1693b1906ceaSLachlan Hodges 			mors_vif->u.sta.is_assoc = true;
1694b1906ceaSLachlan Hodges 	}
1695b1906ceaSLachlan Hodges 
1696b1906ceaSLachlan Hodges 	if (new_state < old_state && new_state == IEEE80211_STA_NONE) {
1697b1906ceaSLachlan Hodges 		if (vif->type == NL80211_IFTYPE_AP)
1698b1906ceaSLachlan Hodges 			mors_vif->u.ap.num_stas--;
1699b1906ceaSLachlan Hodges 		else if (vif->type == NL80211_IFTYPE_STATION)
1700b1906ceaSLachlan Hodges 			mors_vif->u.sta.is_assoc = false;
1701b1906ceaSLachlan Hodges 	}
1702b1906ceaSLachlan Hodges 
1703b1906ceaSLachlan Hodges exit:
1704b1906ceaSLachlan Hodges 	/*
1705b1906ceaSLachlan Hodges 	 * Always update our mmrc sta state even on failure to ensure
1706b1906ceaSLachlan Hodges 	 * we don't hold a dangling sta on error
1707b1906ceaSLachlan Hodges 	 */
1708b1906ceaSLachlan Hodges 	mm81x_rc_sta_state_check(mors, vif, sta, old_state, new_state);
1709b1906ceaSLachlan Hodges 	return new_state < old_state ? 0 : ret;
1710b1906ceaSLachlan Hodges }
1711b1906ceaSLachlan Hodges 
mm81x_mac_ops_ampdu_action(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_ampdu_params * params)1712b1906ceaSLachlan Hodges static int mm81x_mac_ops_ampdu_action(struct ieee80211_hw *hw,
1713b1906ceaSLachlan Hodges 				      struct ieee80211_vif *vif,
1714b1906ceaSLachlan Hodges 				      struct ieee80211_ampdu_params *params)
1715b1906ceaSLachlan Hodges {
1716b1906ceaSLachlan Hodges 	u16 tid = params->tid;
1717b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1718b1906ceaSLachlan Hodges 	struct ieee80211_sta *sta = params->sta;
1719b1906ceaSLachlan Hodges 	struct mm81x_sta *mors_sta = (struct mm81x_sta *)sta->drv_priv;
1720b1906ceaSLachlan Hodges 	u16 buf_size =
1721b1906ceaSLachlan Hodges 		min_t(u16, params->buf_size, DOT11AH_BA_MAX_MPDU_PER_AMPDU);
1722b1906ceaSLachlan Hodges 
1723b1906ceaSLachlan Hodges 	switch (params->action) {
1724b1906ceaSLachlan Hodges 	case IEEE80211_AMPDU_TX_START:
1725b1906ceaSLachlan Hodges 		dev_dbg(mors->dev, "%pM.%d A-MPDU TX start", mors_sta->addr,
1726b1906ceaSLachlan Hodges 			tid);
1727b1906ceaSLachlan Hodges 		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1728b1906ceaSLachlan Hodges 		break;
1729b1906ceaSLachlan Hodges 	case IEEE80211_AMPDU_TX_STOP_CONT:
1730b1906ceaSLachlan Hodges 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
1731b1906ceaSLachlan Hodges 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
1732b1906ceaSLachlan Hodges 		dev_dbg(mors->dev, "%pM.%d A-MPDU TX flush", mors_sta->addr,
1733b1906ceaSLachlan Hodges 			tid);
1734b1906ceaSLachlan Hodges 		mors_sta->tid_start_tx[tid] = false;
1735b1906ceaSLachlan Hodges 		mors_sta->tid_tx[tid] = false;
1736b1906ceaSLachlan Hodges 		mors_sta->tid_params[tid] = 0;
1737b1906ceaSLachlan Hodges 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1738b1906ceaSLachlan Hodges 		break;
1739b1906ceaSLachlan Hodges 	case IEEE80211_AMPDU_TX_OPERATIONAL:
1740b1906ceaSLachlan Hodges 		dev_dbg(mors->dev, "%pM.%d A-MPDU TX oper", mors_sta->addr,
1741b1906ceaSLachlan Hodges 			tid);
1742b1906ceaSLachlan Hodges 		mors_sta->tid_tx[tid] = true;
1743b1906ceaSLachlan Hodges 		if (!buf_size) {
1744b1906ceaSLachlan Hodges 			dev_err(mors->dev, "%pM.%d A-MPDU Invalid buf size",
1745b1906ceaSLachlan Hodges 				mors_sta->addr, tid);
1746b1906ceaSLachlan Hodges 			break;
1747b1906ceaSLachlan Hodges 		}
1748b1906ceaSLachlan Hodges 		mors_sta->tid_params[tid] =
1749b1906ceaSLachlan Hodges 			u8_encode_bits(buf_size - 1,
1750b1906ceaSLachlan Hodges 				       TX_INFO_TID_PARAMS_MAX_REORDER_BUF) |
1751b1906ceaSLachlan Hodges 			u8_encode_bits(1, TX_INFO_TID_PARAMS_AMPDU_ENABLED) |
1752b1906ceaSLachlan Hodges 			u8_encode_bits(params->amsdu,
1753b1906ceaSLachlan Hodges 				       TX_INFO_TID_PARAMS_AMSDU_SUPPORTED);
1754b1906ceaSLachlan Hodges 		break;
1755b1906ceaSLachlan Hodges 	default:
1756b1906ceaSLachlan Hodges 		break;
1757b1906ceaSLachlan Hodges 	}
1758b1906ceaSLachlan Hodges 
1759b1906ceaSLachlan Hodges 	return 0;
1760b1906ceaSLachlan Hodges }
1761b1906ceaSLachlan Hodges 
mm81x_mac_ops_set_key(struct ieee80211_hw * hw,enum set_key_cmd cmd,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key)1762b1906ceaSLachlan Hodges static int mm81x_mac_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
1763b1906ceaSLachlan Hodges 				 struct ieee80211_vif *vif,
1764b1906ceaSLachlan Hodges 				 struct ieee80211_sta *sta,
1765b1906ceaSLachlan Hodges 				 struct ieee80211_key_conf *key)
1766b1906ceaSLachlan Hodges {
1767b1906ceaSLachlan Hodges 	u16 aid;
1768b1906ceaSLachlan Hodges 	int ret = -EOPNOTSUPP;
1769b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1770b1906ceaSLachlan Hodges 	struct mm81x_vif *mors_vif = (struct mm81x_vif *)vif->drv_priv;
1771b1906ceaSLachlan Hodges 	enum host_cmd_key_cipher cipher;
1772b1906ceaSLachlan Hodges 	enum host_cmd_aes_key_len length;
1773b1906ceaSLachlan Hodges 
1774b1906ceaSLachlan Hodges 	if (vif->type == NL80211_IFTYPE_STATION) {
1775b1906ceaSLachlan Hodges 		aid = vif->cfg.aid;
1776b1906ceaSLachlan Hodges 	} else if (sta) {
1777b1906ceaSLachlan Hodges 		aid = sta->aid;
1778b1906ceaSLachlan Hodges 	} else {
1779b1906ceaSLachlan Hodges 		/* Is a group key - AID is unused */
1780b1906ceaSLachlan Hodges 		WARN_ON(key->flags & IEEE80211_KEY_FLAG_PAIRWISE);
1781b1906ceaSLachlan Hodges 		aid = 0;
1782b1906ceaSLachlan Hodges 	}
1783b1906ceaSLachlan Hodges 
1784b1906ceaSLachlan Hodges 	switch (cmd) {
1785b1906ceaSLachlan Hodges 	case SET_KEY: {
1786b1906ceaSLachlan Hodges 		switch (key->cipher) {
1787b1906ceaSLachlan Hodges 		case WLAN_CIPHER_SUITE_CCMP:
1788b1906ceaSLachlan Hodges 		case WLAN_CIPHER_SUITE_CCMP_256:
1789b1906ceaSLachlan Hodges 			cipher = HOST_CMD_KEY_CIPHER_AES_CCM;
1790b1906ceaSLachlan Hodges 			break;
1791b1906ceaSLachlan Hodges 		case WLAN_CIPHER_SUITE_GCMP:
1792b1906ceaSLachlan Hodges 		case WLAN_CIPHER_SUITE_GCMP_256:
1793b1906ceaSLachlan Hodges 			cipher = HOST_CMD_KEY_CIPHER_AES_GCM;
1794b1906ceaSLachlan Hodges 			break;
1795b1906ceaSLachlan Hodges 		default:
1796b1906ceaSLachlan Hodges 			/* Cipher suite currently not supported */
1797b1906ceaSLachlan Hodges 			ret = -EOPNOTSUPP;
1798b1906ceaSLachlan Hodges 			goto exit;
1799b1906ceaSLachlan Hodges 		}
1800b1906ceaSLachlan Hodges 
1801b1906ceaSLachlan Hodges 		switch (key->keylen) {
1802b1906ceaSLachlan Hodges 		case 16:
1803b1906ceaSLachlan Hodges 			length = HOST_CMD_AES_KEY_LEN_LENGTH_128;
1804b1906ceaSLachlan Hodges 			break;
1805b1906ceaSLachlan Hodges 		case 32:
1806b1906ceaSLachlan Hodges 			length = HOST_CMD_AES_KEY_LEN_LENGTH_256;
1807b1906ceaSLachlan Hodges 			break;
1808b1906ceaSLachlan Hodges 		default:
1809b1906ceaSLachlan Hodges 			/* Key length not supported */
1810b1906ceaSLachlan Hodges 			ret = -EOPNOTSUPP;
1811b1906ceaSLachlan Hodges 			goto exit;
1812b1906ceaSLachlan Hodges 		}
1813b1906ceaSLachlan Hodges 
1814b1906ceaSLachlan Hodges 		ret = mm81x_cmd_install_key(mors, mors_vif, aid, key, cipher,
1815b1906ceaSLachlan Hodges 					    length);
1816b1906ceaSLachlan Hodges 		break;
1817b1906ceaSLachlan Hodges 	}
1818b1906ceaSLachlan Hodges 	case DISABLE_KEY:
1819b1906ceaSLachlan Hodges 		ret = mm81x_cmd_disable_key(mors, mors_vif, aid, key);
1820b1906ceaSLachlan Hodges 		if (ret) {
1821b1906ceaSLachlan Hodges 			/* Must return 0 */
1822b1906ceaSLachlan Hodges 			dev_warn(mors->dev, "Failed to remove key");
1823b1906ceaSLachlan Hodges 			ret = 0;
1824b1906ceaSLachlan Hodges 		}
1825b1906ceaSLachlan Hodges 		break;
1826b1906ceaSLachlan Hodges 	default:
1827b1906ceaSLachlan Hodges 		WARN_ON(1);
1828b1906ceaSLachlan Hodges 	}
1829b1906ceaSLachlan Hodges 
1830b1906ceaSLachlan Hodges 	if (ret) {
1831b1906ceaSLachlan Hodges 		dev_dbg(mors->dev, "Falling back to software crypto");
1832b1906ceaSLachlan Hodges 		ret = 1;
1833b1906ceaSLachlan Hodges 	}
1834b1906ceaSLachlan Hodges 
1835b1906ceaSLachlan Hodges exit:
1836b1906ceaSLachlan Hodges 	return ret;
1837b1906ceaSLachlan Hodges }
1838b1906ceaSLachlan Hodges 
mm81x_mac_set_frag_threshold(struct ieee80211_hw * hw,int radio_idx,u32 value)1839b1906ceaSLachlan Hodges static int mm81x_mac_set_frag_threshold(struct ieee80211_hw *hw, int radio_idx,
1840b1906ceaSLachlan Hodges 					u32 value)
1841b1906ceaSLachlan Hodges {
1842b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
1843b1906ceaSLachlan Hodges 
1844b1906ceaSLachlan Hodges 	return mm81x_cmd_set_frag_threshold(mors, value);
1845b1906ceaSLachlan Hodges }
1846b1906ceaSLachlan Hodges 
mm81x_rx_h_rc_bw_to_rx_bw(__le32 ratecode)1847b1906ceaSLachlan Hodges static u8 mm81x_rx_h_rc_bw_to_rx_bw(__le32 ratecode)
1848b1906ceaSLachlan Hodges {
1849b1906ceaSLachlan Hodges 	enum dot11_bandwidth bw = mm81x_ratecode_bw_index_get(ratecode);
1850b1906ceaSLachlan Hodges 
1851b1906ceaSLachlan Hodges 	switch (bw) {
1852b1906ceaSLachlan Hodges 	case DOT11_BANDWIDTH_1MHZ:
1853b1906ceaSLachlan Hodges 		return RATE_INFO_BW_1;
1854b1906ceaSLachlan Hodges 	case DOT11_BANDWIDTH_2MHZ:
1855b1906ceaSLachlan Hodges 		return RATE_INFO_BW_2;
1856b1906ceaSLachlan Hodges 	case DOT11_BANDWIDTH_4MHZ:
1857b1906ceaSLachlan Hodges 		return RATE_INFO_BW_4;
1858b1906ceaSLachlan Hodges 	case DOT11_BANDWIDTH_8MHZ:
1859b1906ceaSLachlan Hodges 		return RATE_INFO_BW_8;
1860b1906ceaSLachlan Hodges 	default:
1861b1906ceaSLachlan Hodges 		return RATE_INFO_BW_1;
1862b1906ceaSLachlan Hodges 	}
1863b1906ceaSLachlan Hodges }
1864b1906ceaSLachlan Hodges 
mm81x_rx_h_fill_status(struct mm81x * mors,struct mm81x_skb_rx_status * hdr_rx_status,struct ieee80211_rx_status * rx_status,struct sk_buff * skb)1865b1906ceaSLachlan Hodges static void mm81x_rx_h_fill_status(struct mm81x *mors,
1866b1906ceaSLachlan Hodges 				   struct mm81x_skb_rx_status *hdr_rx_status,
1867b1906ceaSLachlan Hodges 				   struct ieee80211_rx_status *rx_status,
1868b1906ceaSLachlan Hodges 				   struct sk_buff *skb)
1869b1906ceaSLachlan Hodges {
1870b1906ceaSLachlan Hodges 	u32 flags = le32_to_cpu(hdr_rx_status->flags);
1871b1906ceaSLachlan Hodges 	u16 freq_100khz = le16_to_cpu(hdr_rx_status->freq_100khz);
1872b1906ceaSLachlan Hodges 	__le32 ratecode = hdr_rx_status->mm81x_ratecode;
1873b1906ceaSLachlan Hodges 
1874b1906ceaSLachlan Hodges 	rx_status->signal = le16_to_cpu(hdr_rx_status->rssi);
1875b1906ceaSLachlan Hodges 	rx_status->encoding = RX_ENC_S1G;
1876b1906ceaSLachlan Hodges 	rx_status->band = NL80211_BAND_S1GHZ;
1877b1906ceaSLachlan Hodges 	rx_status->freq = KHZ100_TO_MHZ(freq_100khz);
1878b1906ceaSLachlan Hodges 	rx_status->freq_offset = (freq_100khz % 10) ? 1 : 0;
1879b1906ceaSLachlan Hodges 	rx_status->nss = NSS_IDX_TO_NSS(mm81x_ratecode_nss_index_get(ratecode));
1880b1906ceaSLachlan Hodges 
1881b1906ceaSLachlan Hodges 	if (flags & MM81X_RX_STATUS_FLAGS_DECRYPTED)
1882b1906ceaSLachlan Hodges 		rx_status->flag |= RX_FLAG_DECRYPTED;
1883b1906ceaSLachlan Hodges 
1884b1906ceaSLachlan Hodges 	rx_status->rate_idx = mm81x_ratecode_mcs_index_get(ratecode);
1885b1906ceaSLachlan Hodges 	rx_status->bw = mm81x_rx_h_rc_bw_to_rx_bw(ratecode);
1886b1906ceaSLachlan Hodges 
1887b1906ceaSLachlan Hodges 	if (mm81x_ratecode_sgi_get(ratecode))
1888b1906ceaSLachlan Hodges 		rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI;
1889b1906ceaSLachlan Hodges }
1890b1906ceaSLachlan Hodges 
mm81x_rx_h_update_sta(struct ieee80211_vif * vif,struct ieee80211_hdr * hdr,struct ieee80211_rx_status * rx_status)1891b1906ceaSLachlan Hodges static void mm81x_rx_h_update_sta(struct ieee80211_vif *vif,
1892b1906ceaSLachlan Hodges 				  struct ieee80211_hdr *hdr,
1893b1906ceaSLachlan Hodges 				  struct ieee80211_rx_status *rx_status)
1894b1906ceaSLachlan Hodges {
1895b1906ceaSLachlan Hodges 	struct ieee80211_sta *sta;
1896b1906ceaSLachlan Hodges 	struct mm81x_sta *msta;
1897b1906ceaSLachlan Hodges 	u8 *lookup = ieee80211_is_s1g_beacon(hdr->frame_control) ? hdr->addr1 :
1898b1906ceaSLachlan Hodges 								   hdr->addr2;
1899b1906ceaSLachlan Hodges 
1900b1906ceaSLachlan Hodges 	lockdep_assert_in_rcu_read_lock();
1901b1906ceaSLachlan Hodges 
1902b1906ceaSLachlan Hodges 	sta = ieee80211_find_sta(vif, lookup);
1903b1906ceaSLachlan Hodges 	if (!sta)
1904b1906ceaSLachlan Hodges 		return;
1905b1906ceaSLachlan Hodges 
1906b1906ceaSLachlan Hodges 	msta = (void *)sta->drv_priv;
1907b1906ceaSLachlan Hodges 	if (msta->avg_rssi) {
1908b1906ceaSLachlan Hodges 		msta->avg_rssi =
1909b1906ceaSLachlan Hodges 			CALC_AVG_RSSI(msta->avg_rssi, rx_status->signal);
1910b1906ceaSLachlan Hodges 	} else {
1911b1906ceaSLachlan Hodges 		msta->avg_rssi = rx_status->signal;
1912b1906ceaSLachlan Hodges 	}
1913b1906ceaSLachlan Hodges }
1914b1906ceaSLachlan Hodges 
1915b1906ceaSLachlan Hodges static struct ieee80211_vif *
mm81x_rx_h_skb_get_vif(struct mm81x * mors,struct sk_buff * skb,struct mm81x_skb_rx_status * hdr_rx_status)1916b1906ceaSLachlan Hodges mm81x_rx_h_skb_get_vif(struct mm81x *mors, struct sk_buff *skb,
1917b1906ceaSLachlan Hodges 		       struct mm81x_skb_rx_status *hdr_rx_status)
1918b1906ceaSLachlan Hodges {
1919b1906ceaSLachlan Hodges 	u8 vif_id = u32_get_bits(le32_to_cpu(hdr_rx_status->flags),
1920b1906ceaSLachlan Hodges 				 MM81X_RX_STATUS_FLAGS_VIF_ID);
1921b1906ceaSLachlan Hodges 
1922b1906ceaSLachlan Hodges 	lockdep_assert_in_rcu_read_lock();
1923b1906ceaSLachlan Hodges 
1924b1906ceaSLachlan Hodges 	if (vif_id == INVALID_VIF_INDEX)
1925b1906ceaSLachlan Hodges 		return NULL;
1926b1906ceaSLachlan Hodges 
1927b1906ceaSLachlan Hodges 	return mm81x_rcu_dereference_vif_id(mors, vif_id, true);
1928b1906ceaSLachlan Hodges }
1929b1906ceaSLachlan Hodges 
mm81x_mac_rx_skb(struct mm81x * mors,struct sk_buff * skb,struct mm81x_skb_rx_status * hdr_rx_status)1930b1906ceaSLachlan Hodges void mm81x_mac_rx_skb(struct mm81x *mors, struct sk_buff *skb,
1931b1906ceaSLachlan Hodges 		      struct mm81x_skb_rx_status *hdr_rx_status)
1932b1906ceaSLachlan Hodges {
1933b1906ceaSLachlan Hodges 	struct ieee80211_vif *vif;
1934b1906ceaSLachlan Hodges 	struct ieee80211_hw *hw = mors->hw;
1935b1906ceaSLachlan Hodges 	struct ieee80211_rx_status rx_status;
1936b1906ceaSLachlan Hodges 	struct ieee80211_hdr *hdr = (void *)skb->data;
1937b1906ceaSLachlan Hodges 
1938b1906ceaSLachlan Hodges 	memset(&rx_status, 0, sizeof(rx_status));
1939b1906ceaSLachlan Hodges 
1940b1906ceaSLachlan Hodges 	if (!mors->started || !skb->data || !skb->len) {
1941b1906ceaSLachlan Hodges 		dev_kfree_skb_any(skb);
1942b1906ceaSLachlan Hodges 		return;
1943b1906ceaSLachlan Hodges 	}
1944b1906ceaSLachlan Hodges 
1945b1906ceaSLachlan Hodges 	mm81x_rx_h_fill_status(mors, hdr_rx_status, &rx_status, skb);
1946b1906ceaSLachlan Hodges 
1947b1906ceaSLachlan Hodges 	scoped_guard(rcu) {
1948b1906ceaSLachlan Hodges 		vif = mm81x_rx_h_skb_get_vif(mors, skb, hdr_rx_status);
1949b1906ceaSLachlan Hodges 		if (!vif)
1950b1906ceaSLachlan Hodges 			goto rx;
1951b1906ceaSLachlan Hodges 
1952b1906ceaSLachlan Hodges 		mm81x_rx_h_update_sta(vif, hdr, &rx_status);
1953b1906ceaSLachlan Hodges 	}
1954b1906ceaSLachlan Hodges 
1955b1906ceaSLachlan Hodges rx:
1956b1906ceaSLachlan Hodges 	memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
1957b1906ceaSLachlan Hodges 	ieee80211_rx_ni(hw, skb);
1958b1906ceaSLachlan Hodges }
1959b1906ceaSLachlan Hodges 
mm81x_mac_flush_queues(struct mm81x * mors)1960b1906ceaSLachlan Hodges static void mm81x_mac_flush_queues(struct mm81x *mors)
1961b1906ceaSLachlan Hodges {
1962b1906ceaSLachlan Hodges 	/*
1963b1906ceaSLachlan Hodges 	 * No need to call mm81x_skbq_stop_tx_queues as mac80211
1964b1906ceaSLachlan Hodges 	 * has already cancelled each queue prior to calling .flush()
1965b1906ceaSLachlan Hodges 	 */
1966b1906ceaSLachlan Hodges 	mm81x_skbq_data_traffic_pause(mors);
1967b1906ceaSLachlan Hodges 
1968b1906ceaSLachlan Hodges 	flush_work(&mors->hif_work);
1969b1906ceaSLachlan Hodges 	flush_work(&mors->tx_stale_work);
1970b1906ceaSLachlan Hodges 
1971b1906ceaSLachlan Hodges 	mm81x_hif_clear_events(mors);
1972b1906ceaSLachlan Hodges 	mm81x_hif_flush_tx_data(mors);
1973b1906ceaSLachlan Hodges 	mm81x_hif_flush_cmds(mors);
1974b1906ceaSLachlan Hodges 
1975b1906ceaSLachlan Hodges 	/* Re-enable data, not that there will be any */
1976b1906ceaSLachlan Hodges 	mm81x_skbq_data_traffic_resume(mors);
1977b1906ceaSLachlan Hodges }
1978b1906ceaSLachlan Hodges 
mm81x_mac_has_tx_pending(struct mm81x * mors)1979b1906ceaSLachlan Hodges static bool mm81x_mac_has_tx_pending(struct mm81x *mors)
1980b1906ceaSLachlan Hodges {
1981b1906ceaSLachlan Hodges 	struct mm81x_skbq *mgmt_q = mm81x_hif_get_tx_mgmt_queue(mors);
1982b1906ceaSLachlan Hodges 	struct mm81x_skbq *tx_qs;
1983b1906ceaSLachlan Hodges 	int num_qs, i;
1984b1906ceaSLachlan Hodges 
1985b1906ceaSLachlan Hodges 	mm81x_hif_skbq_get_tx_qs(mors, &tx_qs, &num_qs);
1986b1906ceaSLachlan Hodges 	for (i = 0; i < num_qs; i++)
1987b1906ceaSLachlan Hodges 		if (mm81x_skbq_count(&tx_qs[i]) ||
1988b1906ceaSLachlan Hodges 		    mm81x_skbq_pending_count(&tx_qs[i]))
1989b1906ceaSLachlan Hodges 			return true;
1990b1906ceaSLachlan Hodges 
1991b1906ceaSLachlan Hodges 	if (mm81x_skbq_count(mgmt_q) || mm81x_skbq_pending_count(mgmt_q))
1992b1906ceaSLachlan Hodges 		return true;
1993b1906ceaSLachlan Hodges 
1994b1906ceaSLachlan Hodges 	return false;
1995b1906ceaSLachlan Hodges }
1996b1906ceaSLachlan Hodges 
mm81x_mac_wait_queues(struct mm81x * mors)1997b1906ceaSLachlan Hodges static void mm81x_mac_wait_queues(struct mm81x *mors)
1998b1906ceaSLachlan Hodges {
1999b1906ceaSLachlan Hodges 	if (!wait_event_timeout(mors->tx_empty_waitq,
2000b1906ceaSLachlan Hodges 				!mm81x_mac_has_tx_pending(mors),
2001b1906ceaSLachlan Hodges 				MM81X_FLUSH_TIMEOUT))
2002b1906ceaSLachlan Hodges 		dev_warn(mors->dev, "Unable to empty queues before timeout");
2003b1906ceaSLachlan Hodges }
2004b1906ceaSLachlan Hodges 
mm81x_mac_ops_flush(struct ieee80211_hw * hw,struct ieee80211_vif * vif,u32 queues,bool drop)2005b1906ceaSLachlan Hodges static void mm81x_mac_ops_flush(struct ieee80211_hw *hw,
2006b1906ceaSLachlan Hodges 				struct ieee80211_vif *vif, u32 queues,
2007b1906ceaSLachlan Hodges 				bool drop)
2008b1906ceaSLachlan Hodges {
2009b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
2010b1906ceaSLachlan Hodges 
2011b1906ceaSLachlan Hodges 	/* We don't support IEEE80211_HW_QUEUE_CONTROL so flush all queues */
2012b1906ceaSLachlan Hodges 	if (drop)
2013b1906ceaSLachlan Hodges 		mm81x_mac_flush_queues(mors);
2014b1906ceaSLachlan Hodges 	else
2015b1906ceaSLachlan Hodges 		mm81x_mac_wait_queues(mors);
2016b1906ceaSLachlan Hodges }
2017b1906ceaSLachlan Hodges 
mm81x_mac_ops_set_rts_threshold(struct ieee80211_hw * hw,int radio_idx,u32 value)2018b1906ceaSLachlan Hodges static int mm81x_mac_ops_set_rts_threshold(struct ieee80211_hw *hw,
2019b1906ceaSLachlan Hodges 					   int radio_idx, u32 value)
2020b1906ceaSLachlan Hodges {
2021b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
2022b1906ceaSLachlan Hodges 
2023b1906ceaSLachlan Hodges 	mors->rts_threshold = value;
2024b1906ceaSLachlan Hodges 	return 0;
2025b1906ceaSLachlan Hodges }
2026b1906ceaSLachlan Hodges 
mm81x_mac_ops_sta_statistics(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct station_info * sinfo)2027b1906ceaSLachlan Hodges static void mm81x_mac_ops_sta_statistics(struct ieee80211_hw *hw,
2028b1906ceaSLachlan Hodges 					 struct ieee80211_vif *vif,
2029b1906ceaSLachlan Hodges 					 struct ieee80211_sta *sta,
2030b1906ceaSLachlan Hodges 					 struct station_info *sinfo)
2031b1906ceaSLachlan Hodges {
2032b1906ceaSLachlan Hodges 	struct mm81x_sta *msta = (struct mm81x_sta *)sta->drv_priv;
2033b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
2034b1906ceaSLachlan Hodges 	const struct mmrc_table *tb = msta->rc.tb;
2035b1906ceaSLachlan Hodges 	struct mmrc_rate rate;
2036b1906ceaSLachlan Hodges 
2037b1906ceaSLachlan Hodges 	if (!tb || tb->best_tp.rate == MMRC_MCS_UNUSED) {
2038b1906ceaSLachlan Hodges 		sinfo->filled &= ~BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
2039b1906ceaSLachlan Hodges 		return;
2040b1906ceaSLachlan Hodges 	}
2041b1906ceaSLachlan Hodges 
2042b1906ceaSLachlan Hodges 	rate = tb->best_tp;
2043b1906ceaSLachlan Hodges 	sinfo->txrate.mcs = rate.rate;
2044b1906ceaSLachlan Hodges 	sinfo->txrate.nss = NSS_IDX_TO_NSS(rate.ss);
2045b1906ceaSLachlan Hodges 	sinfo->txrate.flags = RATE_INFO_FLAGS_S1G_MCS;
2046b1906ceaSLachlan Hodges 	switch (rate.bw) {
2047b1906ceaSLachlan Hodges 	case MMRC_BW_1MHZ:
2048b1906ceaSLachlan Hodges 		sinfo->txrate.bw = RATE_INFO_BW_1;
2049b1906ceaSLachlan Hodges 		break;
2050b1906ceaSLachlan Hodges 	case MMRC_BW_2MHZ:
2051b1906ceaSLachlan Hodges 		sinfo->txrate.bw = RATE_INFO_BW_2;
2052b1906ceaSLachlan Hodges 		break;
2053b1906ceaSLachlan Hodges 	case MMRC_BW_4MHZ:
2054b1906ceaSLachlan Hodges 		sinfo->txrate.bw = RATE_INFO_BW_4;
2055b1906ceaSLachlan Hodges 		break;
2056b1906ceaSLachlan Hodges 	case MMRC_BW_8MHZ:
2057b1906ceaSLachlan Hodges 		sinfo->txrate.bw = RATE_INFO_BW_8;
2058b1906ceaSLachlan Hodges 		break;
2059b1906ceaSLachlan Hodges 	default:
2060b1906ceaSLachlan Hodges 		break;
2061b1906ceaSLachlan Hodges 	}
2062b1906ceaSLachlan Hodges 
2063b1906ceaSLachlan Hodges 	if (rate.guard == MMRC_GUARD_SHORT)
2064b1906ceaSLachlan Hodges 		sinfo->txrate.flags |= (RATE_INFO_FLAGS_SHORT_GI);
2065b1906ceaSLachlan Hodges 
2066b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "mcs: %d, bw: %d, flag: 0x%x", rate.rate, rate.bw,
2067b1906ceaSLachlan Hodges 		sinfo->txrate.flags);
2068b1906ceaSLachlan Hodges 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
2069b1906ceaSLachlan Hodges }
2070b1906ceaSLachlan Hodges 
mm81x_get_expected_throughput(struct ieee80211_hw * hw,struct ieee80211_sta * sta)2071b1906ceaSLachlan Hodges static u32 mm81x_get_expected_throughput(struct ieee80211_hw *hw,
2072b1906ceaSLachlan Hodges 					 struct ieee80211_sta *sta)
2073b1906ceaSLachlan Hodges {
2074b1906ceaSLachlan Hodges 	struct mm81x_sta *msta = (struct mm81x_sta *)sta->drv_priv;
2075b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
2076b1906ceaSLachlan Hodges 	const struct mmrc_table *tb = msta->rc.tb;
2077b1906ceaSLachlan Hodges 	struct mmrc_rate rate;
2078b1906ceaSLachlan Hodges 	u32 tput;
2079b1906ceaSLachlan Hodges 
2080b1906ceaSLachlan Hodges 	if (!tb || tb->best_tp.rate == MMRC_MCS_UNUSED)
2081b1906ceaSLachlan Hodges 		return 0;
2082b1906ceaSLachlan Hodges 
2083b1906ceaSLachlan Hodges 	rate = tb->best_tp;
2084b1906ceaSLachlan Hodges 	tput = BPS_TO_KBPS(mmrc_calculate_theoretical_throughput(rate));
2085b1906ceaSLachlan Hodges 	dev_dbg(mors->dev, "Throughput: MCS: %d, BW: %d, GI: %d -> %u",
2086b1906ceaSLachlan Hodges 		rate.rate, 1 << rate.bw, rate.guard, tput);
2087b1906ceaSLachlan Hodges 
2088b1906ceaSLachlan Hodges 	return tput;
2089b1906ceaSLachlan Hodges }
2090b1906ceaSLachlan Hodges 
mm81x_mac_restart_cleanup_iter(void * data,u8 * mac,struct ieee80211_vif * vif)2091b1906ceaSLachlan Hodges static void mm81x_mac_restart_cleanup_iter(void *data, u8 *mac,
2092b1906ceaSLachlan Hodges 					   struct ieee80211_vif *vif)
2093b1906ceaSLachlan Hodges {
2094b1906ceaSLachlan Hodges 	if (vif->type == NL80211_IFTYPE_AP)
2095b1906ceaSLachlan Hodges 		mm81x_mac_beacon_finish((struct mm81x_vif *)vif->drv_priv);
2096b1906ceaSLachlan Hodges }
2097b1906ceaSLachlan Hodges 
mm81x_mac_restart_cleanup(struct mm81x * mors)2098b1906ceaSLachlan Hodges static void mm81x_mac_restart_cleanup(struct mm81x *mors)
2099b1906ceaSLachlan Hodges {
2100b1906ceaSLachlan Hodges 	ieee80211_iterate_active_interfaces(mors->hw,
2101b1906ceaSLachlan Hodges 					    IEEE80211_IFACE_ITER_NORMAL,
2102b1906ceaSLachlan Hodges 					    mm81x_mac_restart_cleanup_iter,
2103b1906ceaSLachlan Hodges 					    NULL);
2104b1906ceaSLachlan Hodges 	mm81x_mac_hw_scan_finish(mors);
2105b1906ceaSLachlan Hodges }
2106b1906ceaSLachlan Hodges 
mm81x_mac_restart(struct mm81x * mors)2107b1906ceaSLachlan Hodges static int mm81x_mac_restart(struct mm81x *mors)
2108b1906ceaSLachlan Hodges {
2109b1906ceaSLachlan Hodges 	int ret;
2110b1906ceaSLachlan Hodges 	u32 chip_id;
2111b1906ceaSLachlan Hodges 
2112b1906ceaSLachlan Hodges 	mors->started = false;
2113b1906ceaSLachlan Hodges 	mm81x_ps_disable(mors);
2114b1906ceaSLachlan Hodges 	mm81x_bus_set_irq(mors, false);
2115b1906ceaSLachlan Hodges 	mm81x_hw_irq_clear(mors);
2116b1906ceaSLachlan Hodges 	ieee80211_stop_queues(mors->hw);
2117b1906ceaSLachlan Hodges 
2118b1906ceaSLachlan Hodges 	set_bit(MM81X_STATE_DATA_TX_STOPPED, &mors->state_flags);
2119b1906ceaSLachlan Hodges 	set_bit(MM81X_STATE_DATA_QS_STOPPED, &mors->state_flags);
2120b1906ceaSLachlan Hodges 
2121b1906ceaSLachlan Hodges 	/* Allow time for in-transit tx/rx packets to settle */
2122b1906ceaSLachlan Hodges 	mdelay(MM81X_HW_RESTART_DELAY_MS);
2123b1906ceaSLachlan Hodges 	flush_work(&mors->hif_work);
2124b1906ceaSLachlan Hodges 	flush_work(&mors->tx_stale_work);
2125b1906ceaSLachlan Hodges 	mm81x_hif_clear_events(mors);
2126b1906ceaSLachlan Hodges 	mm81x_hif_flush_tx_data(mors);
2127b1906ceaSLachlan Hodges 	mm81x_hif_flush_cmds(mors);
2128b1906ceaSLachlan Hodges 
2129b1906ceaSLachlan Hodges 	mm81x_claim_bus(mors);
2130b1906ceaSLachlan Hodges 	ret = mm81x_reg32_read(mors, MM81X_REG_CHIP_ID(mors), &chip_id);
2131b1906ceaSLachlan Hodges 	mm81x_release_bus(mors);
2132b1906ceaSLachlan Hodges 
2133b1906ceaSLachlan Hodges 	if (ret < 0) {
2134b1906ceaSLachlan Hodges 		dev_err(mors->dev, "Failed to access HW: %d", ret);
2135b1906ceaSLachlan Hodges 		goto exit;
2136b1906ceaSLachlan Hodges 	}
2137b1906ceaSLachlan Hodges 
2138b1906ceaSLachlan Hodges 	mm81x_mac_restart_cleanup(mors);
2139b1906ceaSLachlan Hodges 
2140b1906ceaSLachlan Hodges 	ret = mm81x_fw_init(mors, true);
2141b1906ceaSLachlan Hodges 	if (ret < 0) {
2142b1906ceaSLachlan Hodges 		dev_err(mors->dev, "Failed to init firmware: %d", ret);
2143b1906ceaSLachlan Hodges 		goto exit;
2144b1906ceaSLachlan Hodges 	}
2145b1906ceaSLachlan Hodges 
2146b1906ceaSLachlan Hodges 	mm81x_hw_irq_enable(mors, MM81X_INT_HW_STOP_NOTIFICATION_NUM, true);
2147b1906ceaSLachlan Hodges 
2148b1906ceaSLachlan Hodges 	ret = mm81x_fw_parse_ext_host_tbl(mors);
2149b1906ceaSLachlan Hodges 	if (ret) {
2150b1906ceaSLachlan Hodges 		dev_err(mors->dev, "failed to parse extended host table: %d",
2151b1906ceaSLachlan Hodges 			ret);
2152b1906ceaSLachlan Hodges 		goto exit;
2153b1906ceaSLachlan Hodges 	}
2154b1906ceaSLachlan Hodges 
2155b1906ceaSLachlan Hodges 	mm81x_mac_caps_init(mors);
2156b1906ceaSLachlan Hodges 
2157b1906ceaSLachlan Hodges 	mm81x_bus_set_irq(mors, true);
2158b1906ceaSLachlan Hodges 	clear_bit(MM81X_STATE_DATA_TX_STOPPED, &mors->state_flags);
2159b1906ceaSLachlan Hodges 	clear_bit(MM81X_STATE_DATA_QS_STOPPED, &mors->state_flags);
2160b1906ceaSLachlan Hodges 	clear_bit(MM81X_STATE_CHIP_UNRESPONSIVE, &mors->state_flags);
2161b1906ceaSLachlan Hodges 	clear_bit(MM81X_STATE_RELOAD_FW_AFTER_START, &mors->state_flags);
2162b1906ceaSLachlan Hodges 	mm81x_mac_check_fw_disabled_chans(mors->hw);
2163b1906ceaSLachlan Hodges 	ieee80211_restart_hw(mors->hw);
2164b1906ceaSLachlan Hodges 
2165b1906ceaSLachlan Hodges exit:
2166b1906ceaSLachlan Hodges 	mm81x_ps_enable(mors);
2167b1906ceaSLachlan Hodges 	return ret;
2168b1906ceaSLachlan Hodges }
2169b1906ceaSLachlan Hodges 
mm81x_mac_ops_add_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)2170b1906ceaSLachlan Hodges static int mm81x_mac_ops_add_interface(struct ieee80211_hw *hw,
2171b1906ceaSLachlan Hodges 				       struct ieee80211_vif *vif)
2172b1906ceaSLachlan Hodges {
2173b1906ceaSLachlan Hodges 	int ret = 0;
2174b1906ceaSLachlan Hodges 	struct mm81x *mors = hw->priv;
2175b1906ceaSLachlan Hodges 	struct mm81x_vif *mors_vif = (struct mm81x_vif *)vif->drv_priv;
2176b1906ceaSLachlan Hodges 
2177b1906ceaSLachlan Hodges 	if (test_bit(MM81X_STATE_RELOAD_FW_AFTER_START, &mors->state_flags)) {
2178b1906ceaSLachlan Hodges 		dev_info(mors->dev, "Restarting chip with regdom: %s",
2179b1906ceaSLachlan Hodges 			 mors->country);
2180b1906ceaSLachlan Hodges 
2181b1906ceaSLachlan Hodges 		ret = mm81x_mac_restart(mors);
2182b1906ceaSLachlan Hodges 		if (ret) {
2183b1906ceaSLachlan Hodges 			dev_err(mors->dev, "Failed to restart chip");
2184b1906ceaSLachlan Hodges 			return ret;
2185b1906ceaSLachlan Hodges 		}
2186b1906ceaSLachlan Hodges 
2187b1906ceaSLachlan Hodges 		/*
2188b1906ceaSLachlan Hodges 		 * mac_restart will trigger ieee80211_hw_restart and
2189b1906ceaSLachlan Hodges 		 * add_interface will re-enter. just exit here instead.
2190b1906ceaSLachlan Hodges 		 */
2191b1906ceaSLachlan Hodges 		return 0;
2192b1906ceaSLachlan Hodges 	}
2193b1906ceaSLachlan Hodges 
2194b1906ceaSLachlan Hodges 	vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
2195b1906ceaSLachlan Hodges 	mors_vif->mors = mors;
2196b1906ceaSLachlan Hodges 
2197b1906ceaSLachlan Hodges 	ret = mm81x_cmd_add_if(mors, &mors_vif->id, vif->addr, vif->type);
2198b1906ceaSLachlan Hodges 	if (ret) {
2199b1906ceaSLachlan Hodges 		dev_err(mors->dev, "mm81x_cmd_add_if failed %d", ret);
2200b1906ceaSLachlan Hodges 		return ret;
2201b1906ceaSLachlan Hodges 	}
2202b1906ceaSLachlan Hodges 
2203b1906ceaSLachlan Hodges 	if (mors_vif->id >= ARRAY_SIZE(mors->vifs)) {
2204b1906ceaSLachlan Hodges 		dev_err(mors->dev, "vif_id is too large %u", mors_vif->id);
2205b1906ceaSLachlan Hodges 		ret = -EOPNOTSUPP;
2206b1906ceaSLachlan Hodges 		return ret;
2207b1906ceaSLachlan Hodges 	}
2208b1906ceaSLachlan Hodges 
2209b1906ceaSLachlan Hodges 	if (mors_vif->id != (mors_vif->id & MM81X_TX_CONF_FLAGS_VIF_ID_MASK)) {
2210b1906ceaSLachlan Hodges 		dev_err(mors->dev, "invalid vif_id %u", mors_vif->id);
2211b1906ceaSLachlan Hodges 		ret = -EOPNOTSUPP;
2212b1906ceaSLachlan Hodges 		return ret;
2213b1906ceaSLachlan Hodges 	}
2214b1906ceaSLachlan Hodges 
2215b1906ceaSLachlan Hodges 	rcu_assign_pointer(mors->vifs[mors_vif->id], vif);
2216b1906ceaSLachlan Hodges 
2217b1906ceaSLachlan Hodges 	if (vif->type == NL80211_IFTYPE_AP)
2218b1906ceaSLachlan Hodges 		mm81x_mac_beacon_init(mors_vif);
2219b1906ceaSLachlan Hodges 
2220b1906ceaSLachlan Hodges 	ret = mm81x_cmd_get_capabilities(mors, mors_vif->id, &mors->fw_caps);
2221b1906ceaSLachlan Hodges 	if (ret) {
2222b1906ceaSLachlan Hodges 		dev_err(mors->dev,
2223b1906ceaSLachlan Hodges 			"mm81x_cmd_get_capabilities failed for vif %d",
2224b1906ceaSLachlan Hodges 			mors_vif->id);
2225b1906ceaSLachlan Hodges 		return ret;
2226b1906ceaSLachlan Hodges 	}
2227b1906ceaSLachlan Hodges 
2228b1906ceaSLachlan Hodges 	ieee80211_wake_queues(mors->hw);
2229b1906ceaSLachlan Hodges 	return ret;
2230b1906ceaSLachlan Hodges }
2231b1906ceaSLachlan Hodges 
2232b1906ceaSLachlan Hodges static const struct ieee80211_ops mm81x_ops = {
2233b1906ceaSLachlan Hodges 	.start = mm81x_mac_ops_start,
2234b1906ceaSLachlan Hodges 	.stop = mm81x_mac_ops_stop,
2235b1906ceaSLachlan Hodges 	.config = mm81x_mac_ops_config,
2236b1906ceaSLachlan Hodges 	.wake_tx_queue = ieee80211_handle_wake_tx_queue,
2237b1906ceaSLachlan Hodges 	.tx = mm81x_mac_ops_tx,
2238b1906ceaSLachlan Hodges 	.add_interface = mm81x_mac_ops_add_interface,
2239b1906ceaSLachlan Hodges 	.remove_interface = mm81x_mac_ops_remove_interface,
2240b1906ceaSLachlan Hodges 	.configure_filter = mm81x_mac_ops_configure_filter,
2241b1906ceaSLachlan Hodges 	.sta_state = mm81x_mac_ops_sta_state,
2242b1906ceaSLachlan Hodges 	.flush = mm81x_mac_ops_flush,
2243b1906ceaSLachlan Hodges 	.set_frag_threshold = mm81x_mac_set_frag_threshold,
2244b1906ceaSLachlan Hodges 	.set_rts_threshold = mm81x_mac_ops_set_rts_threshold,
2245b1906ceaSLachlan Hodges 	.sta_statistics = mm81x_mac_ops_sta_statistics,
2246b1906ceaSLachlan Hodges 	.get_expected_throughput = mm81x_get_expected_throughput,
2247b1906ceaSLachlan Hodges 	.hw_scan = mm81x_mac_ops_hw_scan,
2248b1906ceaSLachlan Hodges 	.cancel_hw_scan = mm81x_mac_ops_cancel_hw_scan,
2249b1906ceaSLachlan Hodges 	.get_txpower = mm81x_mac_ops_get_txpower,
2250b1906ceaSLachlan Hodges 	.bss_info_changed = mm81x_mac_ops_bss_info_changed,
2251b1906ceaSLachlan Hodges 	.prepare_multicast = mm81x_mac_ops_prepare_multicast,
2252b1906ceaSLachlan Hodges 	.conf_tx = mm81x_mac_ops_conf_tx,
2253b1906ceaSLachlan Hodges 	.ampdu_action = mm81x_mac_ops_ampdu_action,
2254b1906ceaSLachlan Hodges 	.set_key = mm81x_mac_ops_set_key,
2255b1906ceaSLachlan Hodges 	.add_chanctx = ieee80211_emulate_add_chanctx,
2256b1906ceaSLachlan Hodges 	.remove_chanctx = ieee80211_emulate_remove_chanctx,
2257b1906ceaSLachlan Hodges 	.change_chanctx = ieee80211_emulate_change_chanctx,
2258b1906ceaSLachlan Hodges 	.switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx,
2259b1906ceaSLachlan Hodges };
2260b1906ceaSLachlan Hodges 
mm81x_reg_notifier(struct wiphy * wiphy,struct regulatory_request * request)2261b1906ceaSLachlan Hodges static void mm81x_reg_notifier(struct wiphy *wiphy,
2262b1906ceaSLachlan Hodges 			       struct regulatory_request *request)
2263b1906ceaSLachlan Hodges {
2264b1906ceaSLachlan Hodges 	int ret;
2265b1906ceaSLachlan Hodges 	struct mm81x *mors = wiphy_to_ieee80211_hw(wiphy)->priv;
2266b1906ceaSLachlan Hodges 
2267b1906ceaSLachlan Hodges 	if (mm81x_reg_h_cc_equal(request->alpha2, "00") ||
2268b1906ceaSLachlan Hodges 	    mm81x_reg_h_cc_equal(request->alpha2, mors->country))
2269b1906ceaSLachlan Hodges 		return;
2270b1906ceaSLachlan Hodges 
2271b1906ceaSLachlan Hodges 	memcpy(mors->country, request->alpha2, sizeof(mors->country));
2272b1906ceaSLachlan Hodges 
2273b1906ceaSLachlan Hodges 	ret = mm81x_mac_restart(mors);
2274b1906ceaSLachlan Hodges 	if (ret)
2275b1906ceaSLachlan Hodges 		dev_err(mors->dev, "Failed to restart chip: %d", ret);
2276b1906ceaSLachlan Hodges }
2277b1906ceaSLachlan Hodges 
mm81x_mac_config_hw(struct mm81x * mors)2278b1906ceaSLachlan Hodges static void mm81x_mac_config_hw(struct mm81x *mors)
2279b1906ceaSLachlan Hodges {
2280b1906ceaSLachlan Hodges 	int i;
2281b1906ceaSLachlan Hodges 	struct ieee80211_hw *hw = mors->hw;
2282b1906ceaSLachlan Hodges 	struct wiphy *wiphy;
2283b1906ceaSLachlan Hodges 
2284b1906ceaSLachlan Hodges 	for (i = 0; i < NUM_NL80211_BANDS; i++)
2285b1906ceaSLachlan Hodges 		hw->wiphy->bands[i] = NULL;
2286b1906ceaSLachlan Hodges 
2287b1906ceaSLachlan Hodges 	hw->wiphy->bands[NL80211_BAND_S1GHZ] = &mors_band_s1ghz;
2288b1906ceaSLachlan Hodges 	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_AP) |
2289b1906ceaSLachlan Hodges 				     BIT(NL80211_IFTYPE_STATION);
2290b1906ceaSLachlan Hodges 	hw->wiphy->reg_notifier = mm81x_reg_notifier;
2291b1906ceaSLachlan Hodges 	hw->queues = MM81X_HW_QUEUE_COUNT;
2292b1906ceaSLachlan Hodges 	hw->max_rates = MM81X_HW_MAX_RATES;
2293b1906ceaSLachlan Hodges 	hw->max_report_rates = MM81X_HW_MAX_REPORT_RATES;
2294b1906ceaSLachlan Hodges 	hw->max_rate_tries = MM81X_HW_MAX_RATE_TRIES;
2295b1906ceaSLachlan Hodges 	hw->tx_sk_pacing_shift = MM81X_HW_TX_SK_PACING_SHIFT;
2296b1906ceaSLachlan Hodges 	hw->vif_data_size = sizeof(struct mm81x_vif);
2297b1906ceaSLachlan Hodges 	hw->sta_data_size = sizeof(struct mm81x_sta);
2298b1906ceaSLachlan Hodges 	hw->extra_tx_headroom =
2299b1906ceaSLachlan Hodges 		sizeof(struct mm81x_skb_hdr) + mm81x_bus_get_alignment(mors);
2300b1906ceaSLachlan Hodges 
2301b1906ceaSLachlan Hodges 	mors->wiphy = hw->wiphy;
2302b1906ceaSLachlan Hodges 
2303b1906ceaSLachlan Hodges 	ieee80211_hw_set(hw, SIGNAL_DBM);
2304b1906ceaSLachlan Hodges 	ieee80211_hw_set(hw, MFP_CAPABLE);
2305b1906ceaSLachlan Hodges 	ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
2306b1906ceaSLachlan Hodges 	ieee80211_hw_set(hw, AMPDU_AGGREGATION);
2307b1906ceaSLachlan Hodges 	ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
2308b1906ceaSLachlan Hodges 	ieee80211_hw_set(hw, HAS_RATE_CONTROL);
2309b1906ceaSLachlan Hodges 	ieee80211_hw_set(hw, SUPPORTS_PS);
2310b1906ceaSLachlan Hodges 	ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
2311b1906ceaSLachlan Hodges 	ieee80211_hw_set(hw, PS_NULLFUNC_STACK);
2312b1906ceaSLachlan Hodges 	ieee80211_hw_set(hw, SUPPORTS_TX_FRAG);
2313b1906ceaSLachlan Hodges 	ieee80211_hw_set(hw, SUPPORTS_NDP_BLOCKACK);
2314b1906ceaSLachlan Hodges 
2315b1906ceaSLachlan Hodges 	SET_IEEE80211_PERM_ADDR(hw, mors->macaddr);
2316b1906ceaSLachlan Hodges 
2317b1906ceaSLachlan Hodges 	wiphy = mors->wiphy;
2318b1906ceaSLachlan Hodges 
2319b1906ceaSLachlan Hodges 	wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
2320b1906ceaSLachlan Hodges 	wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
2321b1906ceaSLachlan Hodges 
2322b1906ceaSLachlan Hodges 	if (!mors->ps.enable)
2323b1906ceaSLachlan Hodges 		wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
2324b1906ceaSLachlan Hodges 
2325b1906ceaSLachlan Hodges 	wiphy->features |= NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE |
2326b1906ceaSLachlan Hodges 			   NL80211_FEATURE_TX_POWER_INSERTION;
2327b1906ceaSLachlan Hodges 
2328b1906ceaSLachlan Hodges 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS);
2329b1906ceaSLachlan Hodges 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL);
2330b1906ceaSLachlan Hodges 
2331b1906ceaSLachlan Hodges 	wiphy->iface_combinations = mors_if_combs;
2332b1906ceaSLachlan Hodges 	wiphy->n_iface_combinations = ARRAY_SIZE(mors_if_combs);
2333b1906ceaSLachlan Hodges 	wiphy->max_scan_ie_len = MM81X_MAX_SCAN_IE_LEN;
2334b1906ceaSLachlan Hodges 	wiphy->max_scan_ssids = MM81X_MAX_SCAN_SSIDS;
2335b1906ceaSLachlan Hodges 	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
2336b1906ceaSLachlan Hodges 	wiphy->max_remain_on_channel_duration =
2337b1906ceaSLachlan Hodges 		MM81X_MAX_REMAIN_ON_CHAN_DURATION;
2338b1906ceaSLachlan Hodges }
2339b1906ceaSLachlan Hodges 
mm81x_stale_tx_status_timer(struct timer_list * t)2340b1906ceaSLachlan Hodges static void mm81x_stale_tx_status_timer(struct timer_list *t)
2341b1906ceaSLachlan Hodges {
2342b1906ceaSLachlan Hodges 	struct mm81x *mors = timer_container_of(mors, t, stale_status.timer);
2343b1906ceaSLachlan Hodges 
2344b1906ceaSLachlan Hodges 	spin_lock_bh(&mors->stale_status.lock);
2345b1906ceaSLachlan Hodges 	if (mm81x_hif_get_tx_status_pending_count(mors))
2346b1906ceaSLachlan Hodges 		queue_work(mors->net_wq, &mors->tx_stale_work);
2347b1906ceaSLachlan Hodges 	spin_unlock_bh(&mors->stale_status.lock);
2348b1906ceaSLachlan Hodges }
2349b1906ceaSLachlan Hodges 
mm81x_stale_tx_status_timer_finish(struct mm81x * mors)2350b1906ceaSLachlan Hodges static void mm81x_stale_tx_status_timer_finish(struct mm81x *mors)
2351b1906ceaSLachlan Hodges {
2352*333f7de5SLinmao Li 	timer_shutdown_sync(&mors->stale_status.timer);
2353b1906ceaSLachlan Hodges }
2354b1906ceaSLachlan Hodges 
mm81x_mac_stale_tx_status_timer_init(struct mm81x * mors)2355b1906ceaSLachlan Hodges static void mm81x_mac_stale_tx_status_timer_init(struct mm81x *mors)
2356b1906ceaSLachlan Hodges {
2357b1906ceaSLachlan Hodges 	spin_lock_init(&mors->stale_status.lock);
2358b1906ceaSLachlan Hodges 	timer_setup(&mors->stale_status.timer, mm81x_stale_tx_status_timer, 0);
2359b1906ceaSLachlan Hodges }
2360b1906ceaSLachlan Hodges 
mm81x_mac_register(struct mm81x * mors)2361b1906ceaSLachlan Hodges int mm81x_mac_register(struct mm81x *mors)
2362b1906ceaSLachlan Hodges {
2363b1906ceaSLachlan Hodges 	int ret;
2364b1906ceaSLachlan Hodges 	struct ieee80211_hw *hw = mors->hw;
2365b1906ceaSLachlan Hodges 
2366b1906ceaSLachlan Hodges 	mors->tx_power_mbm = INT_MAX;
2367b1906ceaSLachlan Hodges 	mors->tx_max_power_mbm = INT_MAX;
2368b1906ceaSLachlan Hodges 	mors->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
2369b1906ceaSLachlan Hodges 
2370b1906ceaSLachlan Hodges 	ret = mm81x_ps_init(mors);
2371b1906ceaSLachlan Hodges 	if (ret)
2372b1906ceaSLachlan Hodges 		return ret;
2373b1906ceaSLachlan Hodges 
2374b1906ceaSLachlan Hodges 	mm81x_mac_config_hw(mors);
2375b1906ceaSLachlan Hodges 	mm81x_mac_hw_scan_init(mors);
2376b1906ceaSLachlan Hodges 	mm81x_mac_stale_tx_status_timer_init(mors);
2377b1906ceaSLachlan Hodges 
2378b1906ceaSLachlan Hodges 	ret = ieee80211_register_hw(hw);
2379b1906ceaSLachlan Hodges 	if (ret) {
2380b1906ceaSLachlan Hodges 		dev_err(mors->dev, "ieee80211_register_hw failed %d", ret);
2381b1906ceaSLachlan Hodges 		mm81x_mac_unregister(mors);
2382b1906ceaSLachlan Hodges 		return ret;
2383b1906ceaSLachlan Hodges 	}
2384b1906ceaSLachlan Hodges 
2385b1906ceaSLachlan Hodges 	mm81x_rc_init(mors);
2386b1906ceaSLachlan Hodges 
2387b1906ceaSLachlan Hodges 	/*
2388b1906ceaSLachlan Hodges 	 * At this stage, we know bus and pager system interrupts are enabled.
2389b1906ceaSLachlan Hodges 	 * Trigger the receive workqueue to drain any incoming chip-to-host
2390b1906ceaSLachlan Hodges 	 * pending packets been pushed in the period between the firmware
2391b1906ceaSLachlan Hodges 	 * initialization and interrupts being enabled.
2392b1906ceaSLachlan Hodges 	 */
2393b1906ceaSLachlan Hodges 	set_bit(MM81X_HIF_EVT_RX_PEND, &mors->hif.event_flags);
2394b1906ceaSLachlan Hodges 	queue_work(mors->chip_wq, &mors->hif_work);
2395b1906ceaSLachlan Hodges 
2396b1906ceaSLachlan Hodges 	return ret;
2397b1906ceaSLachlan Hodges }
2398b1906ceaSLachlan Hodges 
mm81x_mac_unregister(struct mm81x * mors)2399b1906ceaSLachlan Hodges void mm81x_mac_unregister(struct mm81x *mors)
2400b1906ceaSLachlan Hodges {
2401b1906ceaSLachlan Hodges 	mm81x_ps_disable(mors);
2402b1906ceaSLachlan Hodges 	mm81x_rc_deinit(mors);
2403b1906ceaSLachlan Hodges 	mm81x_mac_hw_scan_destroy(mors);
2404b1906ceaSLachlan Hodges 
2405b1906ceaSLachlan Hodges 	ieee80211_stop_queues(mors->hw);
2406b1906ceaSLachlan Hodges 	ieee80211_unregister_hw(mors->hw);
2407b1906ceaSLachlan Hodges 
2408b1906ceaSLachlan Hodges 	mm81x_hif_flush_tx_data(mors);
2409b1906ceaSLachlan Hodges 	mm81x_hif_flush_cmds(mors);
2410b1906ceaSLachlan Hodges 	mm81x_stale_tx_status_timer_finish(mors);
2411b1906ceaSLachlan Hodges 	mm81x_ps_finish(mors);
2412b1906ceaSLachlan Hodges 
2413b1906ceaSLachlan Hodges 	kfree(mors->mcast_filter);
2414b1906ceaSLachlan Hodges }
2415b1906ceaSLachlan Hodges 
mm81x_mac_alloc(size_t priv_size,struct device * dev)2416b1906ceaSLachlan Hodges struct mm81x *mm81x_mac_alloc(size_t priv_size, struct device *dev)
2417b1906ceaSLachlan Hodges {
2418b1906ceaSLachlan Hodges 	struct ieee80211_hw *hw;
2419b1906ceaSLachlan Hodges 	struct mm81x *mors;
2420b1906ceaSLachlan Hodges 
2421b1906ceaSLachlan Hodges 	hw = ieee80211_alloc_hw(sizeof(*mors) + priv_size, &mm81x_ops);
2422b1906ceaSLachlan Hodges 	if (!hw) {
2423b1906ceaSLachlan Hodges 		dev_err(dev, "ieee80211_alloc_hw failed\r\n");
2424b1906ceaSLachlan Hodges 		return NULL;
2425b1906ceaSLachlan Hodges 	}
2426b1906ceaSLachlan Hodges 
2427b1906ceaSLachlan Hodges 	SET_IEEE80211_DEV(hw, dev);
2428b1906ceaSLachlan Hodges 	memset(hw->priv, 0, sizeof(*mors));
2429b1906ceaSLachlan Hodges 
2430b1906ceaSLachlan Hodges 	mors = hw->priv;
2431b1906ceaSLachlan Hodges 	mors->hw = hw;
2432b1906ceaSLachlan Hodges 	mors->dev = dev;
2433b1906ceaSLachlan Hodges 	mutex_init(&mors->cmd_lock);
2434b1906ceaSLachlan Hodges 	mutex_init(&mors->cmd_wait);
2435b1906ceaSLachlan Hodges 	init_waitqueue_head(&mors->tx_empty_waitq);
2436b1906ceaSLachlan Hodges 
2437b1906ceaSLachlan Hodges 	return mors;
2438b1906ceaSLachlan Hodges }
2439b1906ceaSLachlan Hodges 
mm81x_mac_free(struct mm81x * mors)2440b1906ceaSLachlan Hodges void mm81x_mac_free(struct mm81x *mors)
2441b1906ceaSLachlan Hodges {
2442b1906ceaSLachlan Hodges 	ieee80211_free_hw(mors->hw);
2443b1906ceaSLachlan Hodges }
2444