1b1906ceaSLachlan Hodges // SPDX-License-Identifier: GPL-2.0-only
2b1906ceaSLachlan Hodges /*
3b1906ceaSLachlan Hodges * Copyright (c) 2017-2026 Morse Micro
4b1906ceaSLachlan Hodges */
5b1906ceaSLachlan Hodges #include <linux/slab.h>
6b1906ceaSLachlan Hodges #include <linux/timer.h>
7b1906ceaSLachlan Hodges #include "core.h"
8b1906ceaSLachlan Hodges #include "mac.h"
9b1906ceaSLachlan Hodges #include "bus.h"
10b1906ceaSLachlan Hodges #include "rc.h"
11b1906ceaSLachlan Hodges
12b1906ceaSLachlan Hodges #define MM81X_RC_BW_TO_MMRC_BW(X) \
13b1906ceaSLachlan Hodges (((X) == 1) ? MMRC_BW_1MHZ : \
14b1906ceaSLachlan Hodges ((X) == 2) ? MMRC_BW_2MHZ : \
15b1906ceaSLachlan Hodges ((X) == 4) ? MMRC_BW_4MHZ : \
16b1906ceaSLachlan Hodges ((X) == 8) ? MMRC_BW_8MHZ : \
17b1906ceaSLachlan Hodges MMRC_BW_2MHZ)
18b1906ceaSLachlan Hodges
mm81x_rc_work(struct work_struct * work)19b1906ceaSLachlan Hodges static void mm81x_rc_work(struct work_struct *work)
20b1906ceaSLachlan Hodges {
21b1906ceaSLachlan Hodges struct mm81x_rc *mrc = container_of(work, struct mm81x_rc, work);
22b1906ceaSLachlan Hodges struct list_head *pos;
23b1906ceaSLachlan Hodges
24b1906ceaSLachlan Hodges spin_lock_bh(&mrc->lock);
25b1906ceaSLachlan Hodges
26b1906ceaSLachlan Hodges list_for_each(pos, &mrc->stas) {
27b1906ceaSLachlan Hodges struct mm81x_rc_sta *mrc_sta =
28b1906ceaSLachlan Hodges container_of(pos, struct mm81x_rc_sta, list);
29b1906ceaSLachlan Hodges unsigned long now = jiffies;
30b1906ceaSLachlan Hodges
31b1906ceaSLachlan Hodges mrc_sta->last_update = now;
32b1906ceaSLachlan Hodges
33b1906ceaSLachlan Hodges mmrc_update(mrc_sta->tb);
34b1906ceaSLachlan Hodges }
35b1906ceaSLachlan Hodges
36b1906ceaSLachlan Hodges spin_unlock_bh(&mrc->lock);
37b1906ceaSLachlan Hodges
38b1906ceaSLachlan Hodges mod_timer(&mrc->timer, jiffies + msecs_to_jiffies(100));
39b1906ceaSLachlan Hodges }
40b1906ceaSLachlan Hodges
mm81x_rc_timer(struct timer_list * t)41b1906ceaSLachlan Hodges static void mm81x_rc_timer(struct timer_list *t)
42b1906ceaSLachlan Hodges {
43b1906ceaSLachlan Hodges struct mm81x_rc *mrc = timer_container_of(mrc, t, timer);
44b1906ceaSLachlan Hodges struct mm81x *mors = mrc->mors;
45b1906ceaSLachlan Hodges
46b1906ceaSLachlan Hodges queue_work(mors->net_wq, &mors->mrc.work);
47b1906ceaSLachlan Hodges }
48b1906ceaSLachlan Hodges
mm81x_rc_init(struct mm81x * mors)49b1906ceaSLachlan Hodges void mm81x_rc_init(struct mm81x *mors)
50b1906ceaSLachlan Hodges {
51b1906ceaSLachlan Hodges INIT_LIST_HEAD(&mors->mrc.stas);
52b1906ceaSLachlan Hodges spin_lock_init(&mors->mrc.lock);
53b1906ceaSLachlan Hodges
54b1906ceaSLachlan Hodges INIT_WORK(&mors->mrc.work, mm81x_rc_work);
55b1906ceaSLachlan Hodges timer_setup(&mors->mrc.timer, mm81x_rc_timer, 0);
56b1906ceaSLachlan Hodges
57b1906ceaSLachlan Hodges mors->mrc.mors = mors;
58b1906ceaSLachlan Hodges mod_timer(&mors->mrc.timer, jiffies + msecs_to_jiffies(100));
59b1906ceaSLachlan Hodges }
60b1906ceaSLachlan Hodges
mm81x_rc_deinit(struct mm81x * mors)61b1906ceaSLachlan Hodges void mm81x_rc_deinit(struct mm81x *mors)
62b1906ceaSLachlan Hodges {
63*333f7de5SLinmao Li timer_shutdown_sync(&mors->mrc.timer);
64b1906ceaSLachlan Hodges cancel_work_sync(&mors->mrc.work);
65b1906ceaSLachlan Hodges }
66b1906ceaSLachlan Hodges
mm81x_rc_sta_config_guard_per_bw(struct ieee80211_sta * sta,struct mmrc_sta_capabilities * caps)67b1906ceaSLachlan Hodges static void mm81x_rc_sta_config_guard_per_bw(struct ieee80211_sta *sta,
68b1906ceaSLachlan Hodges struct mmrc_sta_capabilities *caps)
69b1906ceaSLachlan Hodges {
70b1906ceaSLachlan Hodges caps->guard = BIT(MMRC_GUARD_LONG);
71b1906ceaSLachlan Hodges
72b1906ceaSLachlan Hodges if (caps->bandwidth & BIT(MMRC_BW_1MHZ)) {
73b1906ceaSLachlan Hodges caps->sgi_per_bw |= SGI_PER_BW(MMRC_BW_1MHZ);
74b1906ceaSLachlan Hodges caps->guard |= BIT(MMRC_GUARD_SHORT);
75b1906ceaSLachlan Hodges }
76b1906ceaSLachlan Hodges
77b1906ceaSLachlan Hodges if (caps->bandwidth & BIT(MMRC_BW_2MHZ)) {
78b1906ceaSLachlan Hodges caps->sgi_per_bw |= SGI_PER_BW(MMRC_BW_2MHZ);
79b1906ceaSLachlan Hodges caps->guard |= BIT(MMRC_GUARD_SHORT);
80b1906ceaSLachlan Hodges }
81b1906ceaSLachlan Hodges
82b1906ceaSLachlan Hodges if (caps->bandwidth & BIT(MMRC_BW_4MHZ)) {
83b1906ceaSLachlan Hodges caps->sgi_per_bw |= SGI_PER_BW(MMRC_BW_4MHZ);
84b1906ceaSLachlan Hodges caps->guard |= BIT(MMRC_GUARD_SHORT);
85b1906ceaSLachlan Hodges }
86b1906ceaSLachlan Hodges
87b1906ceaSLachlan Hodges if (caps->bandwidth & BIT(MMRC_BW_8MHZ)) {
88b1906ceaSLachlan Hodges caps->sgi_per_bw |= SGI_PER_BW(MMRC_BW_8MHZ);
89b1906ceaSLachlan Hodges caps->guard |= BIT(MMRC_GUARD_SHORT);
90b1906ceaSLachlan Hodges }
91b1906ceaSLachlan Hodges }
92b1906ceaSLachlan Hodges
mm81x_rc_sta_add_s1g_sta_caps(struct mm81x * mors,struct mmrc_sta_capabilities * caps,struct ieee80211_sta_s1g_cap * s1g_cap)93b1906ceaSLachlan Hodges static void mm81x_rc_sta_add_s1g_sta_caps(struct mm81x *mors,
94b1906ceaSLachlan Hodges struct mmrc_sta_capabilities *caps,
95b1906ceaSLachlan Hodges struct ieee80211_sta_s1g_cap *s1g_cap)
96b1906ceaSLachlan Hodges {
97b1906ceaSLachlan Hodges int nss_idx = 0;
98b1906ceaSLachlan Hodges u8 rx_mcs = s1g_cap->nss_mcs[0] & 0x3; /* 1SS */
99b1906ceaSLachlan Hodges u8 tx_mcs = (s1g_cap->nss_mcs[2] >> 1) & 0x3; /* 1SS */
100b1906ceaSLachlan Hodges u8 mcs = min(rx_mcs, tx_mcs);
101b1906ceaSLachlan Hodges
102b1906ceaSLachlan Hodges switch (mcs) {
103b1906ceaSLachlan Hodges case IEEE80211_VHT_MCS_SUPPORT_0_9: /* VHT 9 -> S1G 9 */
104b1906ceaSLachlan Hodges caps->rates |= BIT(MMRC_MCS9) | BIT(MMRC_MCS8);
105b1906ceaSLachlan Hodges fallthrough;
106b1906ceaSLachlan Hodges case IEEE80211_VHT_MCS_SUPPORT_0_8: /* VHT 8 -> S1G 7 */
107b1906ceaSLachlan Hodges caps->rates |= BIT(MMRC_MCS7) | BIT(MMRC_MCS6) |
108b1906ceaSLachlan Hodges BIT(MMRC_MCS5) | BIT(MMRC_MCS4) | BIT(MMRC_MCS3);
109b1906ceaSLachlan Hodges fallthrough;
110b1906ceaSLachlan Hodges case IEEE80211_VHT_MCS_SUPPORT_0_7: /* VHT 7 -> S1G 2 */
111b1906ceaSLachlan Hodges caps->rates |= BIT(MMRC_MCS2) | BIT(MMRC_MCS1) |
112b1906ceaSLachlan Hodges BIT(MMRC_MCS0) | BIT(MMRC_MCS10);
113b1906ceaSLachlan Hodges caps->spatial_streams |= (BIT(nss_idx) & 0x0F);
114b1906ceaSLachlan Hodges break;
115b1906ceaSLachlan Hodges
116b1906ceaSLachlan Hodges default:
117b1906ceaSLachlan Hodges dev_warn(mors->dev, "Invalid MCS encoding 0x%02x for stream %d",
118b1906ceaSLachlan Hodges mcs, nss_idx);
119b1906ceaSLachlan Hodges }
120b1906ceaSLachlan Hodges }
121b1906ceaSLachlan Hodges
mm81x_rc_sta_add(struct mm81x * mors,struct ieee80211_vif * vif,struct ieee80211_sta * sta)122b1906ceaSLachlan Hodges int mm81x_rc_sta_add(struct mm81x *mors, struct ieee80211_vif *vif,
123b1906ceaSLachlan Hodges struct ieee80211_sta *sta)
124b1906ceaSLachlan Hodges {
125b1906ceaSLachlan Hodges struct ieee80211_sta_s1g_cap *s1g_cap = &sta->deflink.s1g_cap;
126b1906ceaSLachlan Hodges struct mm81x_sta *msta = (struct mm81x_sta *)sta->drv_priv;
127b1906ceaSLachlan Hodges struct mmrc_sta_capabilities caps;
128b1906ceaSLachlan Hodges int oper_bw_mhz = cfg80211_chandef_get_width(&mors->chandef);
129b1906ceaSLachlan Hodges size_t table_mem_size;
130b1906ceaSLachlan Hodges struct mmrc_table *tb;
131b1906ceaSLachlan Hodges
132b1906ceaSLachlan Hodges memset(&caps, 0, sizeof(caps));
133b1906ceaSLachlan Hodges
134b1906ceaSLachlan Hodges mm81x_rc_sta_add_s1g_sta_caps(mors, &caps, s1g_cap);
135b1906ceaSLachlan Hodges
136b1906ceaSLachlan Hodges /* Configure STA for support up to 8MHZ */
137b1906ceaSLachlan Hodges while (oper_bw_mhz > 0) {
138b1906ceaSLachlan Hodges caps.bandwidth |= BIT(MM81X_RC_BW_TO_MMRC_BW(oper_bw_mhz));
139b1906ceaSLachlan Hodges oper_bw_mhz >>= 1;
140b1906ceaSLachlan Hodges }
141b1906ceaSLachlan Hodges
142b1906ceaSLachlan Hodges /* Configure STA for short and long guard */
143b1906ceaSLachlan Hodges mm81x_rc_sta_config_guard_per_bw(sta, &caps);
144b1906ceaSLachlan Hodges
145b1906ceaSLachlan Hodges /* Set max rates */
146b1906ceaSLachlan Hodges if (mors->hw->max_rates > 0 &&
147b1906ceaSLachlan Hodges mors->hw->max_rates < IEEE80211_TX_MAX_RATES)
148b1906ceaSLachlan Hodges caps.max_rates = mors->hw->max_rates;
149b1906ceaSLachlan Hodges else
150b1906ceaSLachlan Hodges caps.max_rates = IEEE80211_TX_MAX_RATES;
151b1906ceaSLachlan Hodges
152b1906ceaSLachlan Hodges /* Set max reties */
153b1906ceaSLachlan Hodges if (mors->hw->max_rate_tries >= MMRC_MIN_CHAIN_ATTEMPTS &&
154b1906ceaSLachlan Hodges mors->hw->max_rate_tries < MMRC_MAX_CHAIN_ATTEMPTS)
155b1906ceaSLachlan Hodges caps.max_retries = mors->hw->max_rate_tries;
156b1906ceaSLachlan Hodges else
157b1906ceaSLachlan Hodges caps.max_retries = MMRC_MAX_CHAIN_ATTEMPTS;
158b1906ceaSLachlan Hodges
159b1906ceaSLachlan Hodges WARN_ON(msta->rc.tb);
160b1906ceaSLachlan Hodges table_mem_size = mmrc_memory_required_for_caps(&caps);
161b1906ceaSLachlan Hodges tb = kzalloc(table_mem_size, GFP_KERNEL);
162b1906ceaSLachlan Hodges if (!tb)
163b1906ceaSLachlan Hodges return -ENOMEM;
164b1906ceaSLachlan Hodges
165b1906ceaSLachlan Hodges /* Initialise the STA rate control table */
166b1906ceaSLachlan Hodges mmrc_sta_init(tb, &caps, msta->avg_rssi);
167b1906ceaSLachlan Hodges
168b1906ceaSLachlan Hodges spin_lock_bh(&mors->mrc.lock);
169b1906ceaSLachlan Hodges kfree(msta->rc.tb);
170b1906ceaSLachlan Hodges msta->rc.tb = tb;
171b1906ceaSLachlan Hodges list_add(&msta->rc.list, &mors->mrc.stas);
172b1906ceaSLachlan Hodges msta->rc.last_update = jiffies;
173b1906ceaSLachlan Hodges spin_unlock_bh(&mors->mrc.lock);
174b1906ceaSLachlan Hodges
175b1906ceaSLachlan Hodges return 0;
176b1906ceaSLachlan Hodges }
177b1906ceaSLachlan Hodges
mm81x_rc_sta_remove(struct mm81x * mors,struct ieee80211_sta * sta)178b1906ceaSLachlan Hodges void mm81x_rc_sta_remove(struct mm81x *mors, struct ieee80211_sta *sta)
179b1906ceaSLachlan Hodges {
180b1906ceaSLachlan Hodges struct mm81x_sta *msta = (struct mm81x_sta *)sta->drv_priv;
181b1906ceaSLachlan Hodges
182b1906ceaSLachlan Hodges spin_lock_bh(&mors->mrc.lock);
183b1906ceaSLachlan Hodges if (msta->rc.tb) {
184b1906ceaSLachlan Hodges list_del_init(&msta->rc.list);
185b1906ceaSLachlan Hodges kfree(msta->rc.tb);
186b1906ceaSLachlan Hodges msta->rc.tb = NULL;
187b1906ceaSLachlan Hodges }
188b1906ceaSLachlan Hodges spin_unlock_bh(&mors->mrc.lock);
189b1906ceaSLachlan Hodges }
190b1906ceaSLachlan Hodges
mm81x_rc_sta_fill_basic_rates(struct mm81x_skb_tx_info * tx_info,struct ieee80211_tx_info * info,int tx_bw)191b1906ceaSLachlan Hodges static void mm81x_rc_sta_fill_basic_rates(struct mm81x_skb_tx_info *tx_info,
192b1906ceaSLachlan Hodges struct ieee80211_tx_info *info,
193b1906ceaSLachlan Hodges int tx_bw)
194b1906ceaSLachlan Hodges {
195b1906ceaSLachlan Hodges int i;
196b1906ceaSLachlan Hodges enum dot11_bandwidth bw_idx = mm81x_ratecode_bw_mhz_to_bw_index(tx_bw);
197b1906ceaSLachlan Hodges enum mm81x_rate_preamble pream = MM81X_RATE_PREAMBLE_S1G_SHORT;
198b1906ceaSLachlan Hodges
199b1906ceaSLachlan Hodges mm81x_ratecode_mcs_index_set(&tx_info->rates[0].mm81x_ratecode, 0);
200b1906ceaSLachlan Hodges mm81x_ratecode_nss_index_set(&tx_info->rates[0].mm81x_ratecode,
201b1906ceaSLachlan Hodges NSS_TO_NSS_IDX(1));
202b1906ceaSLachlan Hodges mm81x_ratecode_bw_index_set(&tx_info->rates[0].mm81x_ratecode, bw_idx);
203b1906ceaSLachlan Hodges if (bw_idx == DOT11_BANDWIDTH_1MHZ)
204b1906ceaSLachlan Hodges pream = MM81X_RATE_PREAMBLE_S1G_1M;
205b1906ceaSLachlan Hodges mm81x_ratecode_preamble_set(&tx_info->rates[0].mm81x_ratecode, pream);
206b1906ceaSLachlan Hodges tx_info->rates[0].count = 4;
207b1906ceaSLachlan Hodges
208b1906ceaSLachlan Hodges for (i = 1; i < IEEE80211_TX_MAX_RATES; i++)
209b1906ceaSLachlan Hodges tx_info->rates[i].count = 0;
210b1906ceaSLachlan Hodges
211b1906ceaSLachlan Hodges info->control.rates[0].idx = 0;
212b1906ceaSLachlan Hodges info->control.rates[0].count = tx_info->rates[0].count;
213b1906ceaSLachlan Hodges info->control.rates[0].flags = 0;
214b1906ceaSLachlan Hodges info->control.rates[1].idx = -1;
215b1906ceaSLachlan Hodges }
216b1906ceaSLachlan Hodges
mm81x_rc_sta_get_rates(struct mm81x * mors,struct mm81x_sta * msta,struct mmrc_rate_table * rates,size_t size)217b1906ceaSLachlan Hodges static int mm81x_rc_sta_get_rates(struct mm81x *mors, struct mm81x_sta *msta,
218b1906ceaSLachlan Hodges struct mmrc_rate_table *rates, size_t size)
219b1906ceaSLachlan Hodges {
220b1906ceaSLachlan Hodges int ret = -ENOENT;
221b1906ceaSLachlan Hodges struct list_head *pos;
222b1906ceaSLachlan Hodges
223b1906ceaSLachlan Hodges spin_lock_bh(&mors->mrc.lock);
224b1906ceaSLachlan Hodges list_for_each(pos, &mors->mrc.stas) {
225b1906ceaSLachlan Hodges struct mm81x_rc_sta *mrc_sta =
226b1906ceaSLachlan Hodges list_entry(pos, struct mm81x_rc_sta, list);
227b1906ceaSLachlan Hodges
228b1906ceaSLachlan Hodges if (&msta->rc == mrc_sta) {
229b1906ceaSLachlan Hodges ret = 0;
230b1906ceaSLachlan Hodges mmrc_get_rates(msta->rc.tb, rates, size);
231b1906ceaSLachlan Hodges break;
232b1906ceaSLachlan Hodges }
233b1906ceaSLachlan Hodges }
234b1906ceaSLachlan Hodges spin_unlock_bh(&mors->mrc.lock);
235b1906ceaSLachlan Hodges
236b1906ceaSLachlan Hodges return ret;
237b1906ceaSLachlan Hodges }
238b1906ceaSLachlan Hodges
mm81x_rc_use_basic_rates(struct ieee80211_sta * sta,struct sk_buff * skb,struct ieee80211_hdr * hdr)239b1906ceaSLachlan Hodges static bool mm81x_rc_use_basic_rates(struct ieee80211_sta *sta,
240b1906ceaSLachlan Hodges struct sk_buff *skb,
241b1906ceaSLachlan Hodges struct ieee80211_hdr *hdr)
242b1906ceaSLachlan Hodges {
243b1906ceaSLachlan Hodges struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
244b1906ceaSLachlan Hodges
245b1906ceaSLachlan Hodges if (!sta)
246b1906ceaSLachlan Hodges return true;
247b1906ceaSLachlan Hodges
248b1906ceaSLachlan Hodges if (ieee80211_is_qos_nullfunc(hdr->frame_control) ||
249b1906ceaSLachlan Hodges ieee80211_is_nullfunc(hdr->frame_control))
250b1906ceaSLachlan Hodges return true;
251b1906ceaSLachlan Hodges
252b1906ceaSLachlan Hodges if (!ieee80211_is_data_qos(hdr->frame_control))
253b1906ceaSLachlan Hodges return true;
254b1906ceaSLachlan Hodges
255b1906ceaSLachlan Hodges /* Use basic rates for EAPOL exchanges or when instructed */
256b1906ceaSLachlan Hodges if (unlikely((skb->protocol == cpu_to_be16(ETH_P_PAE) ||
257b1906ceaSLachlan Hodges info->flags & IEEE80211_TX_CTL_USE_MINRATE)))
258b1906ceaSLachlan Hodges return true;
259b1906ceaSLachlan Hodges
260b1906ceaSLachlan Hodges return false;
261b1906ceaSLachlan Hodges }
262b1906ceaSLachlan Hodges
mm81x_rc_sta_fill_tx_rates(struct mm81x * mors,struct mm81x_skb_tx_info * tx_info,struct sk_buff * skb,struct ieee80211_sta * sta,int tx_bw,bool rts_allowed)263b1906ceaSLachlan Hodges void mm81x_rc_sta_fill_tx_rates(struct mm81x *mors,
264b1906ceaSLachlan Hodges struct mm81x_skb_tx_info *tx_info,
265b1906ceaSLachlan Hodges struct sk_buff *skb, struct ieee80211_sta *sta,
266b1906ceaSLachlan Hodges int tx_bw, bool rts_allowed)
267b1906ceaSLachlan Hodges {
268b1906ceaSLachlan Hodges int ret, i;
269b1906ceaSLachlan Hodges struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
270b1906ceaSLachlan Hodges struct mm81x_sta *msta;
271b1906ceaSLachlan Hodges struct mmrc_rate_table rates;
272b1906ceaSLachlan Hodges struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
273b1906ceaSLachlan Hodges
274b1906ceaSLachlan Hodges BUILD_BUG_ON((MMRC_BW_1MHZ != (enum mmrc_bw)DOT11_BANDWIDTH_1MHZ ||
275b1906ceaSLachlan Hodges MMRC_BW_2MHZ != (enum mmrc_bw)DOT11_BANDWIDTH_2MHZ ||
276b1906ceaSLachlan Hodges MMRC_BW_4MHZ != (enum mmrc_bw)DOT11_BANDWIDTH_4MHZ ||
277b1906ceaSLachlan Hodges MMRC_BW_16MHZ != (enum mmrc_bw)DOT11_BANDWIDTH_16MHZ));
278b1906ceaSLachlan Hodges
279b1906ceaSLachlan Hodges memset(&info->control.rates, 0, sizeof(info->control.rates));
280b1906ceaSLachlan Hodges memset(&info->status.rates, 0, sizeof(info->status.rates));
281b1906ceaSLachlan Hodges mm81x_rc_sta_fill_basic_rates(tx_info, info, tx_bw);
282b1906ceaSLachlan Hodges
283b1906ceaSLachlan Hodges /* Use basic rates for non data packets */
284b1906ceaSLachlan Hodges if (mm81x_rc_use_basic_rates(sta, skb, hdr))
285b1906ceaSLachlan Hodges return;
286b1906ceaSLachlan Hodges
287b1906ceaSLachlan Hodges msta = (struct mm81x_sta *)sta->drv_priv;
288b1906ceaSLachlan Hodges if (!msta)
289b1906ceaSLachlan Hodges return;
290b1906ceaSLachlan Hodges
291b1906ceaSLachlan Hodges ret = mm81x_rc_sta_get_rates(mors, msta, &rates, skb->len);
292b1906ceaSLachlan Hodges if (ret != 0)
293b1906ceaSLachlan Hodges return;
294b1906ceaSLachlan Hodges
295b1906ceaSLachlan Hodges for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
296b1906ceaSLachlan Hodges info->control.rates[i].flags = 0;
297b1906ceaSLachlan Hodges if (rates.rates[i].rate != MMRC_MCS_UNUSED) {
298b1906ceaSLachlan Hodges u8 mcs = rates.rates[i].rate;
299b1906ceaSLachlan Hodges u8 nss_index = rates.rates[i].ss;
300b1906ceaSLachlan Hodges enum dot11_bandwidth bw_idx =
301b1906ceaSLachlan Hodges (enum dot11_bandwidth)rates.rates[i].bw;
302b1906ceaSLachlan Hodges enum mm81x_rate_preamble pream =
303b1906ceaSLachlan Hodges MM81X_RATE_PREAMBLE_S1G_SHORT;
304b1906ceaSLachlan Hodges
305b1906ceaSLachlan Hodges mm81x_ratecode_bw_index_set(
306b1906ceaSLachlan Hodges &tx_info->rates[i].mm81x_ratecode, bw_idx);
307b1906ceaSLachlan Hodges mm81x_ratecode_mcs_index_set(
308b1906ceaSLachlan Hodges &tx_info->rates[i].mm81x_ratecode, mcs);
309b1906ceaSLachlan Hodges mm81x_ratecode_nss_index_set(
310b1906ceaSLachlan Hodges &tx_info->rates[i].mm81x_ratecode, nss_index);
311b1906ceaSLachlan Hodges if (bw_idx == DOT11_BANDWIDTH_1MHZ)
312b1906ceaSLachlan Hodges pream = MM81X_RATE_PREAMBLE_S1G_1M;
313b1906ceaSLachlan Hodges mm81x_ratecode_preamble_set(
314b1906ceaSLachlan Hodges &tx_info->rates[i].mm81x_ratecode, pream);
315b1906ceaSLachlan Hodges tx_info->rates[i].count = rates.rates[i].attempts;
316b1906ceaSLachlan Hodges
317b1906ceaSLachlan Hodges if (rts_allowed &&
318b1906ceaSLachlan Hodges (rates.rates[i].flags & BIT(MMRC_FLAGS_CTS_RTS))) {
319b1906ceaSLachlan Hodges mm81x_ratecode_enable_rts(
320b1906ceaSLachlan Hodges &tx_info->rates[i].mm81x_ratecode);
321b1906ceaSLachlan Hodges info->control.rates[i].flags |=
322b1906ceaSLachlan Hodges IEEE80211_TX_RC_USE_RTS_CTS;
323b1906ceaSLachlan Hodges }
324b1906ceaSLachlan Hodges
325b1906ceaSLachlan Hodges if (rates.rates[i].guard == MMRC_GUARD_SHORT) {
326b1906ceaSLachlan Hodges mm81x_ratecode_enable_sgi(
327b1906ceaSLachlan Hodges &tx_info->rates[i].mm81x_ratecode);
328b1906ceaSLachlan Hodges info->control.rates[i].flags |=
329b1906ceaSLachlan Hodges IEEE80211_TX_RC_SHORT_GI;
330b1906ceaSLachlan Hodges }
331b1906ceaSLachlan Hodges
332b1906ceaSLachlan Hodges /* Update skb tx_info */
333b1906ceaSLachlan Hodges info->control.rates[i].idx = rates.rates[i].rate;
334b1906ceaSLachlan Hodges info->control.rates[i].count = rates.rates[i].attempts;
335b1906ceaSLachlan Hodges } else {
336b1906ceaSLachlan Hodges info->control.rates[i].idx = -1;
337b1906ceaSLachlan Hodges info->control.rates[i].count = 0;
338b1906ceaSLachlan Hodges tx_info->rates[i].count = 0;
339b1906ceaSLachlan Hodges }
340b1906ceaSLachlan Hodges }
341b1906ceaSLachlan Hodges }
342b1906ceaSLachlan Hodges
mm81x_rc_sta_set_rates(struct mm81x * mors,struct mm81x_sta * msta,struct mmrc_rate_table * rates,int attempts,bool was_aggregated)343b1906ceaSLachlan Hodges static void mm81x_rc_sta_set_rates(struct mm81x *mors, struct mm81x_sta *msta,
344b1906ceaSLachlan Hodges struct mmrc_rate_table *rates, int attempts,
345b1906ceaSLachlan Hodges bool was_aggregated)
346b1906ceaSLachlan Hodges {
347b1906ceaSLachlan Hodges struct list_head *pos;
348b1906ceaSLachlan Hodges
349b1906ceaSLachlan Hodges spin_lock_bh(&mors->mrc.lock);
350b1906ceaSLachlan Hodges list_for_each(pos, &mors->mrc.stas) {
351b1906ceaSLachlan Hodges struct mm81x_rc_sta *mrc_sta =
352b1906ceaSLachlan Hodges list_entry(pos, struct mm81x_rc_sta, list);
353b1906ceaSLachlan Hodges
354b1906ceaSLachlan Hodges if (&msta->rc == mrc_sta) {
355b1906ceaSLachlan Hodges mmrc_feedback(msta->rc.tb, rates, attempts,
356b1906ceaSLachlan Hodges was_aggregated);
357b1906ceaSLachlan Hodges break;
358b1906ceaSLachlan Hodges }
359b1906ceaSLachlan Hodges }
360b1906ceaSLachlan Hodges spin_unlock_bh(&mors->mrc.lock);
361b1906ceaSLachlan Hodges }
362b1906ceaSLachlan Hodges
mm81x_rc_sta_feedback_rates(struct mm81x * mors,struct sk_buff * skb,struct ieee80211_sta * sta,struct mm81x_skb_tx_status * tx_sts,int attempts)363b1906ceaSLachlan Hodges void mm81x_rc_sta_feedback_rates(struct mm81x *mors, struct sk_buff *skb,
364b1906ceaSLachlan Hodges struct ieee80211_sta *sta,
365b1906ceaSLachlan Hodges struct mm81x_skb_tx_status *tx_sts,
366b1906ceaSLachlan Hodges int attempts)
367b1906ceaSLachlan Hodges {
368b1906ceaSLachlan Hodges int i;
369b1906ceaSLachlan Hodges u32 tx_airtime = 0;
370b1906ceaSLachlan Hodges struct mmrc_rate_table rates;
371b1906ceaSLachlan Hodges struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
372b1906ceaSLachlan Hodges struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb);
373b1906ceaSLachlan Hodges struct ieee80211_tx_rate *r = &txi->status.rates[0];
374b1906ceaSLachlan Hodges int count = min_t(int, MM81X_SKB_MAX_RATES, IEEE80211_TX_MAX_RATES);
375b1906ceaSLachlan Hodges struct mm81x_sta *msta = msta = (struct mm81x_sta *)sta->drv_priv;
376b1906ceaSLachlan Hodges
377b1906ceaSLachlan Hodges /* Don't update rate info if basic rates were used */
378b1906ceaSLachlan Hodges if (mm81x_rc_use_basic_rates(sta, skb, hdr))
379b1906ceaSLachlan Hodges goto exit;
380b1906ceaSLachlan Hodges
381b1906ceaSLachlan Hodges if (attempts <= 0)
382b1906ceaSLachlan Hodges /* Did we really send the packet? */
383b1906ceaSLachlan Hodges goto exit;
384b1906ceaSLachlan Hodges
385b1906ceaSLachlan Hodges for (i = 0; i < count; i++) {
386b1906ceaSLachlan Hodges rates.rates[i].rate = mm81x_ratecode_mcs_index_get(
387b1906ceaSLachlan Hodges tx_sts->rates[i].mm81x_ratecode);
388b1906ceaSLachlan Hodges rates.rates[i].ss = mm81x_ratecode_nss_index_get(
389b1906ceaSLachlan Hodges tx_sts->rates[i].mm81x_ratecode);
390b1906ceaSLachlan Hodges rates.rates[i].guard =
391b1906ceaSLachlan Hodges mm81x_ratecode_sgi_get(tx_sts->rates[i].mm81x_ratecode);
392b1906ceaSLachlan Hodges rates.rates[i].bw = mm81x_ratecode_bw_index_get(
393b1906ceaSLachlan Hodges tx_sts->rates[i].mm81x_ratecode);
394b1906ceaSLachlan Hodges rates.rates[i].flags =
395b1906ceaSLachlan Hodges mm81x_ratecode_rts_get(tx_sts->rates[i].mm81x_ratecode);
396b1906ceaSLachlan Hodges rates.rates[i].attempts = tx_sts->rates[i].count;
397b1906ceaSLachlan Hodges
398b1906ceaSLachlan Hodges tx_airtime +=
399b1906ceaSLachlan Hodges mmrc_calculate_rate_tx_time(&rates.rates[i], skb->len);
400b1906ceaSLachlan Hodges }
401b1906ceaSLachlan Hodges
402b1906ceaSLachlan Hodges if (msta) {
403b1906ceaSLachlan Hodges /*
404b1906ceaSLachlan Hodges * Save the rate information. This will be used to update
405b1906ceaSLachlan Hodges * station's tx rate stats
406b1906ceaSLachlan Hodges */
407b1906ceaSLachlan Hodges msta->last_sta_tx_rate.bw = rates.rates[0].bw;
408b1906ceaSLachlan Hodges msta->last_sta_tx_rate.rate = rates.rates[0].rate;
409b1906ceaSLachlan Hodges msta->last_sta_tx_rate.ss = rates.rates[0].ss;
410b1906ceaSLachlan Hodges msta->last_sta_tx_rate.guard = rates.rates[0].guard;
411b1906ceaSLachlan Hodges }
412b1906ceaSLachlan Hodges
413b1906ceaSLachlan Hodges mm81x_rc_sta_set_rates(mors, msta, &rates, attempts,
414b1906ceaSLachlan Hodges !!(le32_to_cpu(tx_sts->flags) &
415b1906ceaSLachlan Hodges MM81X_TX_STATUS_WAS_AGGREGATED));
416b1906ceaSLachlan Hodges
417b1906ceaSLachlan Hodges ieee80211_sta_register_airtime(sta, tx_sts->tid, tx_airtime, 0);
418b1906ceaSLachlan Hodges
419b1906ceaSLachlan Hodges exit:
420b1906ceaSLachlan Hodges ieee80211_tx_info_clear_status(txi);
421b1906ceaSLachlan Hodges
422b1906ceaSLachlan Hodges if (!(le32_to_cpu(tx_sts->flags) & MM81X_TX_STATUS_FLAGS_NO_ACK) &&
423b1906ceaSLachlan Hodges !(txi->flags & IEEE80211_TX_CTL_NO_ACK))
424b1906ceaSLachlan Hodges txi->flags |= IEEE80211_TX_STAT_ACK;
425b1906ceaSLachlan Hodges
426b1906ceaSLachlan Hodges if (le32_to_cpu(tx_sts->flags) & MM81X_TX_STATUS_FLAGS_PS_FILTERED) {
427b1906ceaSLachlan Hodges txi->flags |= IEEE80211_TX_STAT_TX_FILTERED;
428b1906ceaSLachlan Hodges
429b1906ceaSLachlan Hodges /*
430b1906ceaSLachlan Hodges * Clear TX CTL AMPDU flag so that this frame gets rescheduled
431b1906ceaSLachlan Hodges * in ieee80211_handle_filtered_frame(). This flag will get set
432b1906ceaSLachlan Hodges * again by mac80211's tx path on rescheduling.
433b1906ceaSLachlan Hodges */
434b1906ceaSLachlan Hodges txi->flags &= ~IEEE80211_TX_CTL_AMPDU;
435b1906ceaSLachlan Hodges if (msta) {
436b1906ceaSLachlan Hodges if (!msta->tx_ps_filter_en)
437b1906ceaSLachlan Hodges dev_dbg(mors->dev, "TX ps filter set sta[%pM]",
438b1906ceaSLachlan Hodges msta->addr);
439b1906ceaSLachlan Hodges msta->tx_ps_filter_en = true;
440b1906ceaSLachlan Hodges }
441b1906ceaSLachlan Hodges }
442b1906ceaSLachlan Hodges
443b1906ceaSLachlan Hodges for (i = 0; i < count; i++) {
444b1906ceaSLachlan Hodges if (tx_sts->rates[i].count > 0) {
445b1906ceaSLachlan Hodges r[i].count = tx_sts->rates[i].count;
446b1906ceaSLachlan Hodges r[i].flags |= IEEE80211_TX_RC_MCS;
447b1906ceaSLachlan Hodges } else {
448b1906ceaSLachlan Hodges r[i].idx = -1;
449b1906ceaSLachlan Hodges }
450b1906ceaSLachlan Hodges }
451b1906ceaSLachlan Hodges
452b1906ceaSLachlan Hodges /* single packet per A-MPDU (for now) */
453b1906ceaSLachlan Hodges if (txi->flags & IEEE80211_TX_CTL_AMPDU) {
454b1906ceaSLachlan Hodges txi->flags |= IEEE80211_TX_STAT_AMPDU;
455b1906ceaSLachlan Hodges txi->status.ampdu_len = 1;
456b1906ceaSLachlan Hodges txi->status.ampdu_ack_len =
457b1906ceaSLachlan Hodges txi->flags & IEEE80211_TX_STAT_ACK ? 1 : 0;
458b1906ceaSLachlan Hodges }
459b1906ceaSLachlan Hodges
460b1906ceaSLachlan Hodges /*
461b1906ceaSLachlan Hodges * Inform mac80211 that the SP (elicited by a PS-Poll or u-APSD) is
462b1906ceaSLachlan Hodges * over
463b1906ceaSLachlan Hodges */
464b1906ceaSLachlan Hodges if (sta && (txi->flags & IEEE80211_TX_STATUS_EOSP)) {
465b1906ceaSLachlan Hodges txi->flags &= ~IEEE80211_TX_STATUS_EOSP;
466b1906ceaSLachlan Hodges ieee80211_sta_eosp(sta);
467b1906ceaSLachlan Hodges }
468b1906ceaSLachlan Hodges }
469b1906ceaSLachlan Hodges
mm81x_rc_sta_state_check(struct mm81x * mors,struct ieee80211_vif * vif,struct ieee80211_sta * sta,enum ieee80211_sta_state old_state,enum ieee80211_sta_state new_state)470b1906ceaSLachlan Hodges void mm81x_rc_sta_state_check(struct mm81x *mors, struct ieee80211_vif *vif,
471b1906ceaSLachlan Hodges struct ieee80211_sta *sta,
472b1906ceaSLachlan Hodges enum ieee80211_sta_state old_state,
473b1906ceaSLachlan Hodges enum ieee80211_sta_state new_state)
474b1906ceaSLachlan Hodges {
475b1906ceaSLachlan Hodges struct mm81x_sta *msta = (struct mm81x_sta *)sta->drv_priv;
476b1906ceaSLachlan Hodges
477b1906ceaSLachlan Hodges /* Add to Morse RC STA list */
478b1906ceaSLachlan Hodges if (old_state < new_state && new_state == IEEE80211_STA_ASSOC) {
479b1906ceaSLachlan Hodges /* Newly associated, add to RC */
480b1906ceaSLachlan Hodges mm81x_rc_sta_add(mors, vif, sta);
481b1906ceaSLachlan Hodges } else if (old_state > new_state && (old_state == IEEE80211_STA_ASSOC ||
482b1906ceaSLachlan Hodges old_state == IEEE80211_STA_AUTH)) {
483b1906ceaSLachlan Hodges /* Lost or failed association; remove from list */
484b1906ceaSLachlan Hodges mm81x_rc_sta_remove(mors, sta);
485b1906ceaSLachlan Hodges } else if (old_state < new_state && old_state == IEEE80211_STA_NONE &&
486b1906ceaSLachlan Hodges msta->rc.list.prev) {
487b1906ceaSLachlan Hodges /*
488b1906ceaSLachlan Hodges * Special case for driver warning issue causing a sta to be
489b1906ceaSLachlan Hodges * left on the list
490b1906ceaSLachlan Hodges */
491b1906ceaSLachlan Hodges dev_dbg(mors->dev, "Remove stale sta from rc list");
492b1906ceaSLachlan Hodges mm81x_rc_sta_remove(mors, sta);
493b1906ceaSLachlan Hodges }
494b1906ceaSLachlan Hodges }
495