1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
22c8dccc7SJohannes Berg /*
32c8dccc7SJohannes Berg * Copyright 2002-2005, Instant802 Networks, Inc.
42c8dccc7SJohannes Berg * Copyright 2005-2006, Devicescape Software, Inc.
52c8dccc7SJohannes Berg * Copyright (c) 2006 Jiri Benc <jbenc@suse.cz>
6e8e4f528SJohannes Berg * Copyright 2017 Intel Deutschland GmbH
7d0a9123eSJohannes Berg * Copyright (C) 2022 Intel Corporation
82c8dccc7SJohannes Berg */
92c8dccc7SJohannes Berg
102c8dccc7SJohannes Berg #include <linux/kernel.h>
112c8dccc7SJohannes Berg #include <linux/rtnetlink.h>
123a9a231dSPaul Gortmaker #include <linux/module.h>
13cc01f9b5SJohannes Berg #include <linux/slab.h>
142c8dccc7SJohannes Berg #include "rate.h"
152c8dccc7SJohannes Berg #include "ieee80211_i.h"
164b7679a5SJohannes Berg #include "debugfs.h"
172c8dccc7SJohannes Berg
182c8dccc7SJohannes Berg struct rate_control_alg {
192c8dccc7SJohannes Berg struct list_head list;
20631ad703SJohannes Berg const struct rate_control_ops *ops;
212c8dccc7SJohannes Berg };
222c8dccc7SJohannes Berg
232c8dccc7SJohannes Berg static LIST_HEAD(rate_ctrl_algs);
242c8dccc7SJohannes Berg static DEFINE_MUTEX(rate_ctrl_mutex);
252c8dccc7SJohannes Berg
262c8dccc7SJohannes Berg static char *ieee80211_default_rc_algo = CONFIG_MAC80211_RC_DEFAULT;
272c8dccc7SJohannes Berg module_param(ieee80211_default_rc_algo, charp, 0644);
282c8dccc7SJohannes Berg MODULE_PARM_DESC(ieee80211_default_rc_algo,
292c8dccc7SJohannes Berg "Default rate control algorithm for mac80211 to use");
302c8dccc7SJohannes Berg
rate_control_rate_init(struct sta_info * sta)31eb6d9293SDenys Vlasenko void rate_control_rate_init(struct sta_info *sta)
32eb6d9293SDenys Vlasenko {
33eb6d9293SDenys Vlasenko struct ieee80211_local *local = sta->sdata->local;
34eb6d9293SDenys Vlasenko struct rate_control_ref *ref = sta->rate_ctrl;
35eb6d9293SDenys Vlasenko struct ieee80211_sta *ista = &sta->sta;
36eb6d9293SDenys Vlasenko void *priv_sta = sta->rate_ctrl_priv;
37eb6d9293SDenys Vlasenko struct ieee80211_supported_band *sband;
38eb6d9293SDenys Vlasenko struct ieee80211_chanctx_conf *chanctx_conf;
39eb6d9293SDenys Vlasenko
4086e7bdc5SJohannes Berg ieee80211_sta_init_nss(&sta->deflink);
41eb6d9293SDenys Vlasenko
42eb6d9293SDenys Vlasenko if (!ref)
43eb6d9293SDenys Vlasenko return;
44eb6d9293SDenys Vlasenko
45eb6d9293SDenys Vlasenko rcu_read_lock();
46eb6d9293SDenys Vlasenko
47d0a9123eSJohannes Berg chanctx_conf = rcu_dereference(sta->sdata->vif.bss_conf.chanctx_conf);
48eb6d9293SDenys Vlasenko if (WARN_ON(!chanctx_conf)) {
49eb6d9293SDenys Vlasenko rcu_read_unlock();
50eb6d9293SDenys Vlasenko return;
51eb6d9293SDenys Vlasenko }
52eb6d9293SDenys Vlasenko
53eb6d9293SDenys Vlasenko sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
54eb6d9293SDenys Vlasenko
55cac8c526SThomas Pedersen /* TODO: check for minstrel_s1g ? */
56cac8c526SThomas Pedersen if (sband->band == NL80211_BAND_S1GHZ) {
5712bf8fadSThomas Pedersen ieee80211_s1g_sta_rate_init(sta);
58cac8c526SThomas Pedersen rcu_read_unlock();
59cac8c526SThomas Pedersen return;
60cac8c526SThomas Pedersen }
61cac8c526SThomas Pedersen
62eb6d9293SDenys Vlasenko spin_lock_bh(&sta->rate_ctrl_lock);
63eb6d9293SDenys Vlasenko ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
64eb6d9293SDenys Vlasenko priv_sta);
65eb6d9293SDenys Vlasenko spin_unlock_bh(&sta->rate_ctrl_lock);
66eb6d9293SDenys Vlasenko rcu_read_unlock();
67eb6d9293SDenys Vlasenko set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
68eb6d9293SDenys Vlasenko }
69eb6d9293SDenys Vlasenko
rate_control_tx_status(struct ieee80211_local * local,struct ieee80211_tx_status * st)7018fb84d9SFelix Fietkau void rate_control_tx_status(struct ieee80211_local *local,
7118fb84d9SFelix Fietkau struct ieee80211_tx_status *st)
7218fb84d9SFelix Fietkau {
7318fb84d9SFelix Fietkau struct rate_control_ref *ref = local->rate_ctrl;
7418fb84d9SFelix Fietkau struct sta_info *sta = container_of(st->sta, struct sta_info, sta);
7518fb84d9SFelix Fietkau void *priv_sta = sta->rate_ctrl_priv;
76e5c0ee01SJohannes Berg struct ieee80211_supported_band *sband;
7718fb84d9SFelix Fietkau
7818fb84d9SFelix Fietkau if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
7918fb84d9SFelix Fietkau return;
8018fb84d9SFelix Fietkau
81e5c0ee01SJohannes Berg sband = local->hw.wiphy->bands[st->info->band];
82e5c0ee01SJohannes Berg
8318fb84d9SFelix Fietkau spin_lock_bh(&sta->rate_ctrl_lock);
8418fb84d9SFelix Fietkau if (ref->ops->tx_status_ext)
8518fb84d9SFelix Fietkau ref->ops->tx_status_ext(ref->priv, sband, priv_sta, st);
8618fb84d9SFelix Fietkau else if (st->skb)
8718fb84d9SFelix Fietkau ref->ops->tx_status(ref->priv, sband, st->sta, priv_sta, st->skb);
8818fb84d9SFelix Fietkau else
8918fb84d9SFelix Fietkau WARN_ON_ONCE(1);
9018fb84d9SFelix Fietkau
9118fb84d9SFelix Fietkau spin_unlock_bh(&sta->rate_ctrl_lock);
9218fb84d9SFelix Fietkau }
9318fb84d9SFelix Fietkau
rate_control_rate_update(struct ieee80211_local * local,struct ieee80211_supported_band * sband,struct sta_info * sta,unsigned int link_id,u32 changed)94eb6d9293SDenys Vlasenko void rate_control_rate_update(struct ieee80211_local *local,
95eb6d9293SDenys Vlasenko struct ieee80211_supported_band *sband,
96b4f85443SJohannes Berg struct sta_info *sta, unsigned int link_id,
97b4f85443SJohannes Berg u32 changed)
98eb6d9293SDenys Vlasenko {
99eb6d9293SDenys Vlasenko struct rate_control_ref *ref = local->rate_ctrl;
100eb6d9293SDenys Vlasenko struct ieee80211_sta *ista = &sta->sta;
101eb6d9293SDenys Vlasenko void *priv_sta = sta->rate_ctrl_priv;
102eb6d9293SDenys Vlasenko struct ieee80211_chanctx_conf *chanctx_conf;
103eb6d9293SDenys Vlasenko
104b4f85443SJohannes Berg WARN_ON(link_id != 0);
105b4f85443SJohannes Berg
106eb6d9293SDenys Vlasenko if (ref && ref->ops->rate_update) {
107eb6d9293SDenys Vlasenko rcu_read_lock();
108eb6d9293SDenys Vlasenko
109d0a9123eSJohannes Berg chanctx_conf = rcu_dereference(sta->sdata->vif.bss_conf.chanctx_conf);
110eb6d9293SDenys Vlasenko if (WARN_ON(!chanctx_conf)) {
111eb6d9293SDenys Vlasenko rcu_read_unlock();
112eb6d9293SDenys Vlasenko return;
113eb6d9293SDenys Vlasenko }
114eb6d9293SDenys Vlasenko
115eb6d9293SDenys Vlasenko spin_lock_bh(&sta->rate_ctrl_lock);
116eb6d9293SDenys Vlasenko ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
117eb6d9293SDenys Vlasenko ista, priv_sta, changed);
118eb6d9293SDenys Vlasenko spin_unlock_bh(&sta->rate_ctrl_lock);
119eb6d9293SDenys Vlasenko rcu_read_unlock();
120eb6d9293SDenys Vlasenko }
121b4f85443SJohannes Berg
122d6e4c77bSFelix Fietkau if (sta->uploaded)
123eb6d9293SDenys Vlasenko drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
124eb6d9293SDenys Vlasenko }
125eb6d9293SDenys Vlasenko
ieee80211_rate_control_register(const struct rate_control_ops * ops)126631ad703SJohannes Berg int ieee80211_rate_control_register(const struct rate_control_ops *ops)
1272c8dccc7SJohannes Berg {
1282c8dccc7SJohannes Berg struct rate_control_alg *alg;
1292c8dccc7SJohannes Berg
1302c8dccc7SJohannes Berg if (!ops->name)
1312c8dccc7SJohannes Berg return -EINVAL;
1322c8dccc7SJohannes Berg
1332c8dccc7SJohannes Berg mutex_lock(&rate_ctrl_mutex);
1342c8dccc7SJohannes Berg list_for_each_entry(alg, &rate_ctrl_algs, list) {
1352c8dccc7SJohannes Berg if (!strcmp(alg->ops->name, ops->name)) {
1362c8dccc7SJohannes Berg /* don't register an algorithm twice */
1372c8dccc7SJohannes Berg WARN_ON(1);
1382c8dccc7SJohannes Berg mutex_unlock(&rate_ctrl_mutex);
1392c8dccc7SJohannes Berg return -EALREADY;
1402c8dccc7SJohannes Berg }
1412c8dccc7SJohannes Berg }
1422c8dccc7SJohannes Berg
1432c8dccc7SJohannes Berg alg = kzalloc(sizeof(*alg), GFP_KERNEL);
1442c8dccc7SJohannes Berg if (alg == NULL) {
1452c8dccc7SJohannes Berg mutex_unlock(&rate_ctrl_mutex);
1462c8dccc7SJohannes Berg return -ENOMEM;
1472c8dccc7SJohannes Berg }
1482c8dccc7SJohannes Berg alg->ops = ops;
1492c8dccc7SJohannes Berg
1502c8dccc7SJohannes Berg list_add_tail(&alg->list, &rate_ctrl_algs);
1512c8dccc7SJohannes Berg mutex_unlock(&rate_ctrl_mutex);
1522c8dccc7SJohannes Berg
1532c8dccc7SJohannes Berg return 0;
1542c8dccc7SJohannes Berg }
1552c8dccc7SJohannes Berg EXPORT_SYMBOL(ieee80211_rate_control_register);
1562c8dccc7SJohannes Berg
ieee80211_rate_control_unregister(const struct rate_control_ops * ops)157631ad703SJohannes Berg void ieee80211_rate_control_unregister(const struct rate_control_ops *ops)
1582c8dccc7SJohannes Berg {
1592c8dccc7SJohannes Berg struct rate_control_alg *alg;
1602c8dccc7SJohannes Berg
1612c8dccc7SJohannes Berg mutex_lock(&rate_ctrl_mutex);
1622c8dccc7SJohannes Berg list_for_each_entry(alg, &rate_ctrl_algs, list) {
1632c8dccc7SJohannes Berg if (alg->ops == ops) {
1642c8dccc7SJohannes Berg list_del(&alg->list);
1652c8dccc7SJohannes Berg kfree(alg);
1662c8dccc7SJohannes Berg break;
1672c8dccc7SJohannes Berg }
1682c8dccc7SJohannes Berg }
1692c8dccc7SJohannes Berg mutex_unlock(&rate_ctrl_mutex);
1702c8dccc7SJohannes Berg }
1712c8dccc7SJohannes Berg EXPORT_SYMBOL(ieee80211_rate_control_unregister);
1722c8dccc7SJohannes Berg
173631ad703SJohannes Berg static const struct rate_control_ops *
ieee80211_try_rate_control_ops_get(const char * name)1742c8dccc7SJohannes Berg ieee80211_try_rate_control_ops_get(const char *name)
1752c8dccc7SJohannes Berg {
1762c8dccc7SJohannes Berg struct rate_control_alg *alg;
177631ad703SJohannes Berg const struct rate_control_ops *ops = NULL;
1782c8dccc7SJohannes Berg
1792c8dccc7SJohannes Berg if (!name)
1802c8dccc7SJohannes Berg return NULL;
1812c8dccc7SJohannes Berg
1822c8dccc7SJohannes Berg mutex_lock(&rate_ctrl_mutex);
1832c8dccc7SJohannes Berg list_for_each_entry(alg, &rate_ctrl_algs, list) {
184cc01f9b5SJohannes Berg if (!strcmp(alg->ops->name, name)) {
1852c8dccc7SJohannes Berg ops = alg->ops;
1862c8dccc7SJohannes Berg break;
1872c8dccc7SJohannes Berg }
1882c8dccc7SJohannes Berg }
1892c8dccc7SJohannes Berg mutex_unlock(&rate_ctrl_mutex);
1902c8dccc7SJohannes Berg return ops;
1912c8dccc7SJohannes Berg }
1922c8dccc7SJohannes Berg
1932c8dccc7SJohannes Berg /* Get the rate control algorithm. */
194631ad703SJohannes Berg static const struct rate_control_ops *
ieee80211_rate_control_ops_get(const char * name)1952c8dccc7SJohannes Berg ieee80211_rate_control_ops_get(const char *name)
1962c8dccc7SJohannes Berg {
197631ad703SJohannes Berg const struct rate_control_ops *ops;
1982c8dccc7SJohannes Berg const char *alg_name;
1992c8dccc7SJohannes Berg
200b51d23e4SDan Streetman kernel_param_lock(THIS_MODULE);
2012c8dccc7SJohannes Berg if (!name)
2022c8dccc7SJohannes Berg alg_name = ieee80211_default_rc_algo;
2032c8dccc7SJohannes Berg else
2042c8dccc7SJohannes Berg alg_name = name;
2052c8dccc7SJohannes Berg
2062c8dccc7SJohannes Berg ops = ieee80211_try_rate_control_ops_get(alg_name);
2072c8dccc7SJohannes Berg if (!ops && name)
2082c8dccc7SJohannes Berg /* try default if specific alg requested but not found */
2092c8dccc7SJohannes Berg ops = ieee80211_try_rate_control_ops_get(ieee80211_default_rc_algo);
2102c8dccc7SJohannes Berg
21193f56de2SMatthias Kaehlcke /* Note: check for > 0 is intentional to avoid clang warning */
21293f56de2SMatthias Kaehlcke if (!ops && (strlen(CONFIG_MAC80211_RC_DEFAULT) > 0))
2132c8dccc7SJohannes Berg /* try built-in one if specific alg requested but not found */
2142c8dccc7SJohannes Berg ops = ieee80211_try_rate_control_ops_get(CONFIG_MAC80211_RC_DEFAULT);
21593f56de2SMatthias Kaehlcke
216b51d23e4SDan Streetman kernel_param_unlock(THIS_MODULE);
2172c8dccc7SJohannes Berg
2182c8dccc7SJohannes Berg return ops;
2192c8dccc7SJohannes Berg }
2202c8dccc7SJohannes Berg
2214b7679a5SJohannes Berg #ifdef CONFIG_MAC80211_DEBUGFS
rcname_read(struct file * file,char __user * userbuf,size_t count,loff_t * ppos)2224b7679a5SJohannes Berg static ssize_t rcname_read(struct file *file, char __user *userbuf,
2234b7679a5SJohannes Berg size_t count, loff_t *ppos)
2244b7679a5SJohannes Berg {
2254b7679a5SJohannes Berg struct rate_control_ref *ref = file->private_data;
2264b7679a5SJohannes Berg int len = strlen(ref->ops->name);
2274b7679a5SJohannes Berg
2284b7679a5SJohannes Berg return simple_read_from_buffer(userbuf, count, ppos,
2294b7679a5SJohannes Berg ref->ops->name, len);
2304b7679a5SJohannes Berg }
2314b7679a5SJohannes Berg
2326cb5f3eaSJohannes Berg const struct file_operations rcname_ops = {
2334b7679a5SJohannes Berg .read = rcname_read,
234234e3405SStephen Boyd .open = simple_open,
2356038f373SArnd Bergmann .llseek = default_llseek,
2364b7679a5SJohannes Berg };
2374b7679a5SJohannes Berg #endif
2384b7679a5SJohannes Berg
2396cb5f3eaSJohannes Berg static struct rate_control_ref *
rate_control_alloc(const char * name,struct ieee80211_local * local)2406cb5f3eaSJohannes Berg rate_control_alloc(const char *name, struct ieee80211_local *local)
2412c8dccc7SJohannes Berg {
2422c8dccc7SJohannes Berg struct rate_control_ref *ref;
2432c8dccc7SJohannes Berg
2442c8dccc7SJohannes Berg ref = kmalloc(sizeof(struct rate_control_ref), GFP_KERNEL);
2452c8dccc7SJohannes Berg if (!ref)
246cc01f9b5SJohannes Berg return NULL;
2472c8dccc7SJohannes Berg ref->ops = ieee80211_rate_control_ops_get(name);
2482c8dccc7SJohannes Berg if (!ref->ops)
249cc01f9b5SJohannes Berg goto free;
2504b7679a5SJohannes Berg
2516cb5f3eaSJohannes Berg ref->priv = ref->ops->alloc(&local->hw);
2522c8dccc7SJohannes Berg if (!ref->priv)
253cc01f9b5SJohannes Berg goto free;
2542c8dccc7SJohannes Berg return ref;
2552c8dccc7SJohannes Berg
256cc01f9b5SJohannes Berg free:
2572c8dccc7SJohannes Berg kfree(ref);
2582c8dccc7SJohannes Berg return NULL;
2592c8dccc7SJohannes Berg }
2602c8dccc7SJohannes Berg
rate_control_free(struct ieee80211_local * local,struct rate_control_ref * ctrl_ref)261a858958bSJohannes Berg static void rate_control_free(struct ieee80211_local *local,
262a858958bSJohannes Berg struct rate_control_ref *ctrl_ref)
2632c8dccc7SJohannes Berg {
2642c8dccc7SJohannes Berg ctrl_ref->ops->free(ctrl_ref->priv);
2654b7679a5SJohannes Berg
2664b7679a5SJohannes Berg #ifdef CONFIG_MAC80211_DEBUGFS
267a858958bSJohannes Berg debugfs_remove_recursive(local->debugfs.rcdir);
268a858958bSJohannes Berg local->debugfs.rcdir = NULL;
2694b7679a5SJohannes Berg #endif
2704b7679a5SJohannes Berg
2712c8dccc7SJohannes Berg kfree(ctrl_ref);
2722c8dccc7SJohannes Berg }
2732c8dccc7SJohannes Berg
ieee80211_check_rate_mask(struct ieee80211_link_data * link)274de03f8acSJohannes Berg void ieee80211_check_rate_mask(struct ieee80211_link_data *link)
275e8e4f528SJohannes Berg {
276de03f8acSJohannes Berg struct ieee80211_sub_if_data *sdata = link->sdata;
277e8e4f528SJohannes Berg struct ieee80211_local *local = sdata->local;
278e8e4f528SJohannes Berg struct ieee80211_supported_band *sband;
279de03f8acSJohannes Berg u32 user_mask, basic_rates = link->conf->basic_rates;
280e8e4f528SJohannes Berg enum nl80211_band band;
281e8e4f528SJohannes Berg
282de03f8acSJohannes Berg if (WARN_ON(!link->conf->chandef.chan))
283e8e4f528SJohannes Berg return;
284e8e4f528SJohannes Berg
285de03f8acSJohannes Berg band = link->conf->chandef.chan->band;
2861821f8b3SThomas Pedersen if (band == NL80211_BAND_S1GHZ) {
2871821f8b3SThomas Pedersen /* TODO */
2881821f8b3SThomas Pedersen return;
2891821f8b3SThomas Pedersen }
2901821f8b3SThomas Pedersen
291e8e4f528SJohannes Berg if (WARN_ON_ONCE(!basic_rates))
292e8e4f528SJohannes Berg return;
293e8e4f528SJohannes Berg
294e8e4f528SJohannes Berg user_mask = sdata->rc_rateidx_mask[band];
295e8e4f528SJohannes Berg sband = local->hw.wiphy->bands[band];
296e8e4f528SJohannes Berg
297e8e4f528SJohannes Berg if (user_mask & basic_rates)
298e8e4f528SJohannes Berg return;
299e8e4f528SJohannes Berg
300e8e4f528SJohannes Berg sdata_dbg(sdata,
301e8e4f528SJohannes Berg "no overlap between basic rates (0x%x) and user mask (0x%x on band %d) - clearing the latter",
302e8e4f528SJohannes Berg basic_rates, user_mask, band);
303e8e4f528SJohannes Berg sdata->rc_rateidx_mask[band] = (1 << sband->n_bitrates) - 1;
304e8e4f528SJohannes Berg }
305e8e4f528SJohannes Berg
rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control * txrc)306b6f35301SRajkumar Manoharan static bool rc_no_data_or_no_ack_use_min(struct ieee80211_tx_rate_control *txrc)
3074c6d4f5cSLuis R. Rodriguez {
3084c6d4f5cSLuis R. Rodriguez struct sk_buff *skb = txrc->skb;
3094c6d4f5cSLuis R. Rodriguez struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
3104c6d4f5cSLuis R. Rodriguez
311b6f35301SRajkumar Manoharan return (info->flags & (IEEE80211_TX_CTL_NO_ACK |
312b6f35301SRajkumar Manoharan IEEE80211_TX_CTL_USE_MINRATE)) ||
3134e6c78bdSPhilipp Borgers !ieee80211_is_tx_data(skb);
3144c6d4f5cSLuis R. Rodriguez }
3154c6d4f5cSLuis R. Rodriguez
rc_send_low_basicrate(struct ieee80211_tx_rate * rate,u32 basic_rates,struct ieee80211_supported_band * sband)3161821f8b3SThomas Pedersen static void rc_send_low_basicrate(struct ieee80211_tx_rate *rate,
3171821f8b3SThomas Pedersen u32 basic_rates,
3188f0729b1SFelix Fietkau struct ieee80211_supported_band *sband)
319e00cfce0SJouni Malinen {
320e00cfce0SJouni Malinen u8 i;
321e00cfce0SJouni Malinen
3221821f8b3SThomas Pedersen if (sband->band == NL80211_BAND_S1GHZ) {
3231821f8b3SThomas Pedersen /* TODO */
3241821f8b3SThomas Pedersen rate->flags |= IEEE80211_TX_RC_S1G_MCS;
3251821f8b3SThomas Pedersen rate->idx = 0;
3261821f8b3SThomas Pedersen return;
3271821f8b3SThomas Pedersen }
3281821f8b3SThomas Pedersen
329e00cfce0SJouni Malinen if (basic_rates == 0)
330e00cfce0SJouni Malinen return; /* assume basic rates unknown and accept rate */
3311821f8b3SThomas Pedersen if (rate->idx < 0)
332e00cfce0SJouni Malinen return;
3331821f8b3SThomas Pedersen if (basic_rates & (1 << rate->idx))
334e00cfce0SJouni Malinen return; /* selected rate is a basic rate */
335e00cfce0SJouni Malinen
3361821f8b3SThomas Pedersen for (i = rate->idx + 1; i <= sband->n_bitrates; i++) {
337e00cfce0SJouni Malinen if (basic_rates & (1 << i)) {
3381821f8b3SThomas Pedersen rate->idx = i;
339e00cfce0SJouni Malinen return;
340e00cfce0SJouni Malinen }
341e00cfce0SJouni Malinen }
342e00cfce0SJouni Malinen
343e00cfce0SJouni Malinen /* could not find a basic rate; use original selection */
344e00cfce0SJouni Malinen }
345e00cfce0SJouni Malinen
__rate_control_send_low(struct ieee80211_hw * hw,struct ieee80211_supported_band * sband,struct ieee80211_sta * sta,struct ieee80211_tx_info * info,u32 rate_mask)3460d528d85SFelix Fietkau static void __rate_control_send_low(struct ieee80211_hw *hw,
3470d528d85SFelix Fietkau struct ieee80211_supported_band *sband,
3480d528d85SFelix Fietkau struct ieee80211_sta *sta,
3491d2d350bSAndrei Otcheretianski struct ieee80211_tx_info *info,
3501d2d350bSAndrei Otcheretianski u32 rate_mask)
3510d528d85SFelix Fietkau {
3522103dec1SSimon Wunderlich int i;
3532103dec1SSimon Wunderlich u32 rate_flags =
3542103dec1SSimon Wunderlich ieee80211_chandef_rate_flags(&hw->conf.chandef);
3552103dec1SSimon Wunderlich
3561821f8b3SThomas Pedersen if (sband->band == NL80211_BAND_S1GHZ) {
3571821f8b3SThomas Pedersen info->control.rates[0].flags |= IEEE80211_TX_RC_S1G_MCS;
3581821f8b3SThomas Pedersen info->control.rates[0].idx = 0;
3591821f8b3SThomas Pedersen return;
3601821f8b3SThomas Pedersen }
3611821f8b3SThomas Pedersen
36257fbcce3SJohannes Berg if ((sband->band == NL80211_BAND_2GHZ) &&
3632103dec1SSimon Wunderlich (info->flags & IEEE80211_TX_CTL_NO_CCK_RATE))
3642103dec1SSimon Wunderlich rate_flags |= IEEE80211_RATE_ERP_G;
3652103dec1SSimon Wunderlich
3662103dec1SSimon Wunderlich info->control.rates[0].idx = 0;
3672103dec1SSimon Wunderlich for (i = 0; i < sband->n_bitrates; i++) {
3681d2d350bSAndrei Otcheretianski if (!(rate_mask & BIT(i)))
3691d2d350bSAndrei Otcheretianski continue;
3701d2d350bSAndrei Otcheretianski
3711431fcb7SAndrei Otcheretianski if ((rate_flags & sband->bitrates[i].flags) != rate_flags)
3721431fcb7SAndrei Otcheretianski continue;
3731431fcb7SAndrei Otcheretianski
3742103dec1SSimon Wunderlich if (!rate_supported(sta, sband->band, i))
3752103dec1SSimon Wunderlich continue;
3762103dec1SSimon Wunderlich
3772103dec1SSimon Wunderlich info->control.rates[0].idx = i;
3782103dec1SSimon Wunderlich break;
3792103dec1SSimon Wunderlich }
3800e5c371aSJohannes Berg WARN_ONCE(i == sband->n_bitrates,
381163a7cddSJohannes Berg "no supported rates for sta %pM (0x%x, band %d) in rate_mask 0x%x with flags 0x%x\n",
382163a7cddSJohannes Berg sta ? sta->addr : NULL,
383046d2e7cSSriram R sta ? sta->deflink.supp_rates[sband->band] : -1,
384163a7cddSJohannes Berg sband->band,
3850e5c371aSJohannes Berg rate_mask, rate_flags);
3860d528d85SFelix Fietkau
3870d528d85SFelix Fietkau info->control.rates[0].count =
3880d528d85SFelix Fietkau (info->flags & IEEE80211_TX_CTL_NO_ACK) ?
3890d528d85SFelix Fietkau 1 : hw->max_rate_tries;
3900d528d85SFelix Fietkau
3910d528d85SFelix Fietkau info->control.skip_table = 1;
3920d528d85SFelix Fietkau }
3930d528d85SFelix Fietkau
394aad14cebSRajkumar Manoharan
rate_control_send_low(struct ieee80211_sta * pubsta,struct ieee80211_tx_rate_control * txrc)3951e87fec9SJohannes Berg static bool rate_control_send_low(struct ieee80211_sta *pubsta,
3964c6d4f5cSLuis R. Rodriguez struct ieee80211_tx_rate_control *txrc)
3974c6d4f5cSLuis R. Rodriguez {
3984c6d4f5cSLuis R. Rodriguez struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
399dd5b4cc7SFelix Fietkau struct ieee80211_supported_band *sband = txrc->sband;
40006703079SChun-Yeow Yeoh struct sta_info *sta;
401dd5b4cc7SFelix Fietkau int mcast_rate;
40206703079SChun-Yeow Yeoh bool use_basicrate = false;
4034c6d4f5cSLuis R. Rodriguez
4041e87fec9SJohannes Berg if (!pubsta || rc_no_data_or_no_ack_use_min(txrc)) {
4051d2d350bSAndrei Otcheretianski __rate_control_send_low(txrc->hw, sband, pubsta, info,
4061d2d350bSAndrei Otcheretianski txrc->rate_idx_mask);
4070d528d85SFelix Fietkau
40806703079SChun-Yeow Yeoh if (!pubsta && txrc->bss) {
409dd5b4cc7SFelix Fietkau mcast_rate = txrc->bss_conf->mcast_rate[sband->band];
410dd5b4cc7SFelix Fietkau if (mcast_rate > 0) {
411dd5b4cc7SFelix Fietkau info->control.rates[0].idx = mcast_rate - 1;
412dd5b4cc7SFelix Fietkau return true;
413dd5b4cc7SFelix Fietkau }
41406703079SChun-Yeow Yeoh use_basicrate = true;
41506703079SChun-Yeow Yeoh } else if (pubsta) {
41606703079SChun-Yeow Yeoh sta = container_of(pubsta, struct sta_info, sta);
41706703079SChun-Yeow Yeoh if (ieee80211_vif_is_mesh(&sta->sdata->vif))
41806703079SChun-Yeow Yeoh use_basicrate = true;
41906703079SChun-Yeow Yeoh }
420dd5b4cc7SFelix Fietkau
42106703079SChun-Yeow Yeoh if (use_basicrate)
4221821f8b3SThomas Pedersen rc_send_low_basicrate(&info->control.rates[0],
423e00cfce0SJouni Malinen txrc->bss_conf->basic_rates,
424dd5b4cc7SFelix Fietkau sband);
42506703079SChun-Yeow Yeoh
4264c6d4f5cSLuis R. Rodriguez return true;
4274c6d4f5cSLuis R. Rodriguez }
4284c6d4f5cSLuis R. Rodriguez return false;
4294c6d4f5cSLuis R. Rodriguez }
4304c6d4f5cSLuis R. Rodriguez
rate_idx_match_legacy_mask(s8 * rate_idx,int n_bitrates,u32 mask)43190c66bd2SLorenzo Bianconi static bool rate_idx_match_legacy_mask(s8 *rate_idx, int n_bitrates, u32 mask)
43237eb0b16SJouni Malinen {
43337eb0b16SJouni Malinen int j;
43437eb0b16SJouni Malinen
43537eb0b16SJouni Malinen /* See whether the selected rate or anything below it is allowed. */
43690c66bd2SLorenzo Bianconi for (j = *rate_idx; j >= 0; j--) {
43737eb0b16SJouni Malinen if (mask & (1 << j)) {
43837eb0b16SJouni Malinen /* Okay, found a suitable rate. Use it. */
43990c66bd2SLorenzo Bianconi *rate_idx = j;
44019468413SSimon Wunderlich return true;
44137eb0b16SJouni Malinen }
44237eb0b16SJouni Malinen }
44337eb0b16SJouni Malinen
44437eb0b16SJouni Malinen /* Try to find a higher rate that would be allowed */
44590c66bd2SLorenzo Bianconi for (j = *rate_idx + 1; j < n_bitrates; j++) {
44637eb0b16SJouni Malinen if (mask & (1 << j)) {
44737eb0b16SJouni Malinen /* Okay, found a suitable rate. Use it. */
44890c66bd2SLorenzo Bianconi *rate_idx = j;
44919468413SSimon Wunderlich return true;
45019468413SSimon Wunderlich }
45119468413SSimon Wunderlich }
45219468413SSimon Wunderlich return false;
45319468413SSimon Wunderlich }
45419468413SSimon Wunderlich
rate_idx_match_mcs_mask(s8 * rate_idx,u8 * mcs_mask)45590c66bd2SLorenzo Bianconi static bool rate_idx_match_mcs_mask(s8 *rate_idx, u8 *mcs_mask)
45619468413SSimon Wunderlich {
45719468413SSimon Wunderlich int i, j;
45819468413SSimon Wunderlich int ridx, rbit;
45919468413SSimon Wunderlich
46090c66bd2SLorenzo Bianconi ridx = *rate_idx / 8;
46190c66bd2SLorenzo Bianconi rbit = *rate_idx % 8;
46219468413SSimon Wunderlich
46319468413SSimon Wunderlich /* sanity check */
464910570b5SDan Carpenter if (ridx < 0 || ridx >= IEEE80211_HT_MCS_MASK_LEN)
46519468413SSimon Wunderlich return false;
46619468413SSimon Wunderlich
46719468413SSimon Wunderlich /* See whether the selected rate or anything below it is allowed. */
46819468413SSimon Wunderlich for (i = ridx; i >= 0; i--) {
46919468413SSimon Wunderlich for (j = rbit; j >= 0; j--)
47019468413SSimon Wunderlich if (mcs_mask[i] & BIT(j)) {
47190c66bd2SLorenzo Bianconi *rate_idx = i * 8 + j;
47219468413SSimon Wunderlich return true;
47319468413SSimon Wunderlich }
47419468413SSimon Wunderlich rbit = 7;
47519468413SSimon Wunderlich }
47619468413SSimon Wunderlich
47719468413SSimon Wunderlich /* Try to find a higher rate that would be allowed */
47890c66bd2SLorenzo Bianconi ridx = (*rate_idx + 1) / 8;
47990c66bd2SLorenzo Bianconi rbit = (*rate_idx + 1) % 8;
48019468413SSimon Wunderlich
48119468413SSimon Wunderlich for (i = ridx; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
48219468413SSimon Wunderlich for (j = rbit; j < 8; j++)
48319468413SSimon Wunderlich if (mcs_mask[i] & BIT(j)) {
48490c66bd2SLorenzo Bianconi *rate_idx = i * 8 + j;
48519468413SSimon Wunderlich return true;
48619468413SSimon Wunderlich }
48719468413SSimon Wunderlich rbit = 0;
48819468413SSimon Wunderlich }
48919468413SSimon Wunderlich return false;
49019468413SSimon Wunderlich }
49119468413SSimon Wunderlich
rate_idx_match_vht_mcs_mask(s8 * rate_idx,u16 * vht_mask)492b119ad6eSLorenzo Bianconi static bool rate_idx_match_vht_mcs_mask(s8 *rate_idx, u16 *vht_mask)
493b119ad6eSLorenzo Bianconi {
494b119ad6eSLorenzo Bianconi int i, j;
495b119ad6eSLorenzo Bianconi int ridx, rbit;
49619468413SSimon Wunderlich
497b119ad6eSLorenzo Bianconi ridx = *rate_idx >> 4;
498b119ad6eSLorenzo Bianconi rbit = *rate_idx & 0xf;
499b119ad6eSLorenzo Bianconi
500b119ad6eSLorenzo Bianconi if (ridx < 0 || ridx >= NL80211_VHT_NSS_MAX)
501b119ad6eSLorenzo Bianconi return false;
502b119ad6eSLorenzo Bianconi
503b119ad6eSLorenzo Bianconi /* See whether the selected rate or anything below it is allowed. */
504b119ad6eSLorenzo Bianconi for (i = ridx; i >= 0; i--) {
505b119ad6eSLorenzo Bianconi for (j = rbit; j >= 0; j--) {
506b119ad6eSLorenzo Bianconi if (vht_mask[i] & BIT(j)) {
507b119ad6eSLorenzo Bianconi *rate_idx = (i << 4) | j;
508b119ad6eSLorenzo Bianconi return true;
509b119ad6eSLorenzo Bianconi }
510b119ad6eSLorenzo Bianconi }
511b119ad6eSLorenzo Bianconi rbit = 15;
512b119ad6eSLorenzo Bianconi }
513b119ad6eSLorenzo Bianconi
514b119ad6eSLorenzo Bianconi /* Try to find a higher rate that would be allowed */
515b119ad6eSLorenzo Bianconi ridx = (*rate_idx + 1) >> 4;
516b119ad6eSLorenzo Bianconi rbit = (*rate_idx + 1) & 0xf;
517b119ad6eSLorenzo Bianconi
518b119ad6eSLorenzo Bianconi for (i = ridx; i < NL80211_VHT_NSS_MAX; i++) {
519b119ad6eSLorenzo Bianconi for (j = rbit; j < 16; j++) {
520b119ad6eSLorenzo Bianconi if (vht_mask[i] & BIT(j)) {
521b119ad6eSLorenzo Bianconi *rate_idx = (i << 4) | j;
522b119ad6eSLorenzo Bianconi return true;
523b119ad6eSLorenzo Bianconi }
524b119ad6eSLorenzo Bianconi }
525b119ad6eSLorenzo Bianconi rbit = 0;
526b119ad6eSLorenzo Bianconi }
527b119ad6eSLorenzo Bianconi return false;
528b119ad6eSLorenzo Bianconi }
52919468413SSimon Wunderlich
rate_idx_match_mask(s8 * rate_idx,u16 * rate_flags,struct ieee80211_supported_band * sband,enum nl80211_chan_width chan_width,u32 mask,u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN],u16 vht_mask[NL80211_VHT_NSS_MAX])53090c66bd2SLorenzo Bianconi static void rate_idx_match_mask(s8 *rate_idx, u16 *rate_flags,
5310d528d85SFelix Fietkau struct ieee80211_supported_band *sband,
5320d528d85SFelix Fietkau enum nl80211_chan_width chan_width,
53319468413SSimon Wunderlich u32 mask,
534b119ad6eSLorenzo Bianconi u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN],
535b119ad6eSLorenzo Bianconi u16 vht_mask[NL80211_VHT_NSS_MAX])
53619468413SSimon Wunderlich {
537b119ad6eSLorenzo Bianconi if (*rate_flags & IEEE80211_TX_RC_VHT_MCS) {
538b119ad6eSLorenzo Bianconi /* handle VHT rates */
539b119ad6eSLorenzo Bianconi if (rate_idx_match_vht_mcs_mask(rate_idx, vht_mask))
540b119ad6eSLorenzo Bianconi return;
541b119ad6eSLorenzo Bianconi
542b119ad6eSLorenzo Bianconi *rate_idx = 0;
543b119ad6eSLorenzo Bianconi /* keep protection flags */
544b119ad6eSLorenzo Bianconi *rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
545b119ad6eSLorenzo Bianconi IEEE80211_TX_RC_USE_CTS_PROTECT |
546b119ad6eSLorenzo Bianconi IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
547b119ad6eSLorenzo Bianconi
548b119ad6eSLorenzo Bianconi *rate_flags |= IEEE80211_TX_RC_MCS;
549b119ad6eSLorenzo Bianconi if (chan_width == NL80211_CHAN_WIDTH_40)
550b119ad6eSLorenzo Bianconi *rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
551b119ad6eSLorenzo Bianconi
552b119ad6eSLorenzo Bianconi if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
553b119ad6eSLorenzo Bianconi return;
554b119ad6eSLorenzo Bianconi
555b119ad6eSLorenzo Bianconi /* also try the legacy rates. */
556b119ad6eSLorenzo Bianconi *rate_flags &= ~(IEEE80211_TX_RC_MCS |
557b119ad6eSLorenzo Bianconi IEEE80211_TX_RC_40_MHZ_WIDTH);
558b119ad6eSLorenzo Bianconi if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
559b119ad6eSLorenzo Bianconi mask))
560b119ad6eSLorenzo Bianconi return;
561b119ad6eSLorenzo Bianconi } else if (*rate_flags & IEEE80211_TX_RC_MCS) {
56219468413SSimon Wunderlich /* handle HT rates */
56390c66bd2SLorenzo Bianconi if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
56419468413SSimon Wunderlich return;
56519468413SSimon Wunderlich
56619468413SSimon Wunderlich /* also try the legacy rates. */
56790c66bd2SLorenzo Bianconi *rate_idx = 0;
56819468413SSimon Wunderlich /* keep protection flags */
56990c66bd2SLorenzo Bianconi *rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
57019468413SSimon Wunderlich IEEE80211_TX_RC_USE_CTS_PROTECT |
57119468413SSimon Wunderlich IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
57290c66bd2SLorenzo Bianconi if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
57390c66bd2SLorenzo Bianconi mask))
57419468413SSimon Wunderlich return;
575b119ad6eSLorenzo Bianconi } else {
57619468413SSimon Wunderlich /* handle legacy rates */
57790c66bd2SLorenzo Bianconi if (rate_idx_match_legacy_mask(rate_idx, sband->n_bitrates,
57890c66bd2SLorenzo Bianconi mask))
57919468413SSimon Wunderlich return;
58019468413SSimon Wunderlich
58119468413SSimon Wunderlich /* if HT BSS, and we handle a data frame, also try HT rates */
5820418a445SSimon Wunderlich switch (chan_width) {
5830418a445SSimon Wunderlich case NL80211_CHAN_WIDTH_20_NOHT:
5840418a445SSimon Wunderlich case NL80211_CHAN_WIDTH_5:
5850418a445SSimon Wunderlich case NL80211_CHAN_WIDTH_10:
58619468413SSimon Wunderlich return;
5870418a445SSimon Wunderlich default:
5880418a445SSimon Wunderlich break;
5890418a445SSimon Wunderlich }
59019468413SSimon Wunderlich
59190c66bd2SLorenzo Bianconi *rate_idx = 0;
59219468413SSimon Wunderlich /* keep protection flags */
59390c66bd2SLorenzo Bianconi *rate_flags &= (IEEE80211_TX_RC_USE_RTS_CTS |
59419468413SSimon Wunderlich IEEE80211_TX_RC_USE_CTS_PROTECT |
59519468413SSimon Wunderlich IEEE80211_TX_RC_USE_SHORT_PREAMBLE);
59619468413SSimon Wunderlich
59790c66bd2SLorenzo Bianconi *rate_flags |= IEEE80211_TX_RC_MCS;
59819468413SSimon Wunderlich
5990d528d85SFelix Fietkau if (chan_width == NL80211_CHAN_WIDTH_40)
60090c66bd2SLorenzo Bianconi *rate_flags |= IEEE80211_TX_RC_40_MHZ_WIDTH;
60119468413SSimon Wunderlich
60290c66bd2SLorenzo Bianconi if (rate_idx_match_mcs_mask(rate_idx, mcs_mask))
60337eb0b16SJouni Malinen return;
60437eb0b16SJouni Malinen }
60537eb0b16SJouni Malinen
60637eb0b16SJouni Malinen /*
60737eb0b16SJouni Malinen * Uh.. No suitable rate exists. This should not really happen with
60837eb0b16SJouni Malinen * sane TX rate mask configurations. However, should someone manage to
60937eb0b16SJouni Malinen * configure supported rates and TX rate mask in incompatible way,
61037eb0b16SJouni Malinen * allow the frame to be transmitted with whatever the rate control
61137eb0b16SJouni Malinen * selected.
61237eb0b16SJouni Malinen */
61337eb0b16SJouni Malinen }
61437eb0b16SJouni Malinen
rate_fixup_ratelist(struct ieee80211_vif * vif,struct ieee80211_supported_band * sband,struct ieee80211_tx_info * info,struct ieee80211_tx_rate * rates,int max_rates)6150d528d85SFelix Fietkau static void rate_fixup_ratelist(struct ieee80211_vif *vif,
6160d528d85SFelix Fietkau struct ieee80211_supported_band *sband,
6170d528d85SFelix Fietkau struct ieee80211_tx_info *info,
6180d528d85SFelix Fietkau struct ieee80211_tx_rate *rates,
6190d528d85SFelix Fietkau int max_rates)
6200d528d85SFelix Fietkau {
6210d528d85SFelix Fietkau struct ieee80211_rate *rate;
6220d528d85SFelix Fietkau bool inval = false;
6230d528d85SFelix Fietkau int i;
6240d528d85SFelix Fietkau
6250d528d85SFelix Fietkau /*
6260d528d85SFelix Fietkau * Set up the RTS/CTS rate as the fastest basic rate
6270d528d85SFelix Fietkau * that is not faster than the data rate unless there
6280d528d85SFelix Fietkau * is no basic rate slower than the data rate, in which
6290d528d85SFelix Fietkau * case we pick the slowest basic rate
6300d528d85SFelix Fietkau *
6310d528d85SFelix Fietkau * XXX: Should this check all retry rates?
6320d528d85SFelix Fietkau */
633336004e2SFelix Fietkau if (!(rates[0].flags &
634336004e2SFelix Fietkau (IEEE80211_TX_RC_MCS | IEEE80211_TX_RC_VHT_MCS))) {
6350d528d85SFelix Fietkau u32 basic_rates = vif->bss_conf.basic_rates;
636c7abf25aSKarl Beldan s8 baserate = basic_rates ? ffs(basic_rates) - 1 : 0;
6370d528d85SFelix Fietkau
6380d528d85SFelix Fietkau rate = &sband->bitrates[rates[0].idx];
6390d528d85SFelix Fietkau
6400d528d85SFelix Fietkau for (i = 0; i < sband->n_bitrates; i++) {
6410d528d85SFelix Fietkau /* must be a basic rate */
6420d528d85SFelix Fietkau if (!(basic_rates & BIT(i)))
6430d528d85SFelix Fietkau continue;
6440d528d85SFelix Fietkau /* must not be faster than the data rate */
6450d528d85SFelix Fietkau if (sband->bitrates[i].bitrate > rate->bitrate)
6460d528d85SFelix Fietkau continue;
6470d528d85SFelix Fietkau /* maximum */
6480d528d85SFelix Fietkau if (sband->bitrates[baserate].bitrate <
6490d528d85SFelix Fietkau sband->bitrates[i].bitrate)
6500d528d85SFelix Fietkau baserate = i;
6510d528d85SFelix Fietkau }
6520d528d85SFelix Fietkau
6530d528d85SFelix Fietkau info->control.rts_cts_rate_idx = baserate;
6540d528d85SFelix Fietkau }
6550d528d85SFelix Fietkau
6560d528d85SFelix Fietkau for (i = 0; i < max_rates; i++) {
6570d528d85SFelix Fietkau /*
6580d528d85SFelix Fietkau * make sure there's no valid rate following
6590d528d85SFelix Fietkau * an invalid one, just in case drivers don't
6600d528d85SFelix Fietkau * take the API seriously to stop at -1.
6610d528d85SFelix Fietkau */
6620d528d85SFelix Fietkau if (inval) {
6630d528d85SFelix Fietkau rates[i].idx = -1;
6640d528d85SFelix Fietkau continue;
6650d528d85SFelix Fietkau }
6660d528d85SFelix Fietkau if (rates[i].idx < 0) {
6670d528d85SFelix Fietkau inval = true;
6680d528d85SFelix Fietkau continue;
6690d528d85SFelix Fietkau }
6700d528d85SFelix Fietkau
6710d528d85SFelix Fietkau /*
6720d528d85SFelix Fietkau * For now assume MCS is already set up correctly, this
6730d528d85SFelix Fietkau * needs to be fixed.
6740d528d85SFelix Fietkau */
6750d528d85SFelix Fietkau if (rates[i].flags & IEEE80211_TX_RC_MCS) {
6760d528d85SFelix Fietkau WARN_ON(rates[i].idx > 76);
6770d528d85SFelix Fietkau
6780d528d85SFelix Fietkau if (!(rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) &&
6790d528d85SFelix Fietkau info->control.use_cts_prot)
6800d528d85SFelix Fietkau rates[i].flags |=
6810d528d85SFelix Fietkau IEEE80211_TX_RC_USE_CTS_PROTECT;
6820d528d85SFelix Fietkau continue;
6830d528d85SFelix Fietkau }
6840d528d85SFelix Fietkau
6850d528d85SFelix Fietkau if (rates[i].flags & IEEE80211_TX_RC_VHT_MCS) {
6860d528d85SFelix Fietkau WARN_ON(ieee80211_rate_get_vht_mcs(&rates[i]) > 9);
6870d528d85SFelix Fietkau continue;
6880d528d85SFelix Fietkau }
6890d528d85SFelix Fietkau
6900d528d85SFelix Fietkau /* set up RTS protection if desired */
6910d528d85SFelix Fietkau if (info->control.use_rts) {
6920d528d85SFelix Fietkau rates[i].flags |= IEEE80211_TX_RC_USE_RTS_CTS;
6930d528d85SFelix Fietkau info->control.use_cts_prot = false;
6940d528d85SFelix Fietkau }
6950d528d85SFelix Fietkau
6960d528d85SFelix Fietkau /* RC is busted */
6970d528d85SFelix Fietkau if (WARN_ON_ONCE(rates[i].idx >= sband->n_bitrates)) {
6980d528d85SFelix Fietkau rates[i].idx = -1;
6990d528d85SFelix Fietkau continue;
7000d528d85SFelix Fietkau }
7010d528d85SFelix Fietkau
7020d528d85SFelix Fietkau rate = &sband->bitrates[rates[i].idx];
7030d528d85SFelix Fietkau
7040d528d85SFelix Fietkau /* set up short preamble */
7050d528d85SFelix Fietkau if (info->control.short_preamble &&
7060d528d85SFelix Fietkau rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
7070d528d85SFelix Fietkau rates[i].flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE;
7080d528d85SFelix Fietkau
7090d528d85SFelix Fietkau /* set up G protection */
7100d528d85SFelix Fietkau if (!(rates[i].flags & IEEE80211_TX_RC_USE_RTS_CTS) &&
7110d528d85SFelix Fietkau info->control.use_cts_prot &&
7120d528d85SFelix Fietkau rate->flags & IEEE80211_RATE_ERP_G)
7130d528d85SFelix Fietkau rates[i].flags |= IEEE80211_TX_RC_USE_CTS_PROTECT;
7140d528d85SFelix Fietkau }
7150d528d85SFelix Fietkau }
7160d528d85SFelix Fietkau
7170d528d85SFelix Fietkau
rate_control_fill_sta_table(struct ieee80211_sta * sta,struct ieee80211_tx_info * info,struct ieee80211_tx_rate * rates,int max_rates)7180d528d85SFelix Fietkau static void rate_control_fill_sta_table(struct ieee80211_sta *sta,
7190d528d85SFelix Fietkau struct ieee80211_tx_info *info,
7200d528d85SFelix Fietkau struct ieee80211_tx_rate *rates,
7210d528d85SFelix Fietkau int max_rates)
7220d528d85SFelix Fietkau {
7230d528d85SFelix Fietkau struct ieee80211_sta_rates *ratetbl = NULL;
7240d528d85SFelix Fietkau int i;
7250d528d85SFelix Fietkau
7260d528d85SFelix Fietkau if (sta && !info->control.skip_table)
7270d528d85SFelix Fietkau ratetbl = rcu_dereference(sta->rates);
7280d528d85SFelix Fietkau
7290d528d85SFelix Fietkau /* Fill remaining rate slots with data from the sta rate table. */
7300d528d85SFelix Fietkau max_rates = min_t(int, max_rates, IEEE80211_TX_RATE_TABLE_SIZE);
7310d528d85SFelix Fietkau for (i = 0; i < max_rates; i++) {
7320d528d85SFelix Fietkau if (i < ARRAY_SIZE(info->control.rates) &&
7330d528d85SFelix Fietkau info->control.rates[i].idx >= 0 &&
7340d528d85SFelix Fietkau info->control.rates[i].count) {
7350d528d85SFelix Fietkau if (rates != info->control.rates)
7360d528d85SFelix Fietkau rates[i] = info->control.rates[i];
7370d528d85SFelix Fietkau } else if (ratetbl) {
7380d528d85SFelix Fietkau rates[i].idx = ratetbl->rate[i].idx;
7390d528d85SFelix Fietkau rates[i].flags = ratetbl->rate[i].flags;
7400d528d85SFelix Fietkau if (info->control.use_rts)
7410d528d85SFelix Fietkau rates[i].count = ratetbl->rate[i].count_rts;
7420d528d85SFelix Fietkau else if (info->control.use_cts_prot)
7430d528d85SFelix Fietkau rates[i].count = ratetbl->rate[i].count_cts;
7440d528d85SFelix Fietkau else
7450d528d85SFelix Fietkau rates[i].count = ratetbl->rate[i].count;
7460d528d85SFelix Fietkau } else {
7470d528d85SFelix Fietkau rates[i].idx = -1;
7480d528d85SFelix Fietkau rates[i].count = 0;
7490d528d85SFelix Fietkau }
7500d528d85SFelix Fietkau
7510d528d85SFelix Fietkau if (rates[i].idx < 0 || !rates[i].count)
7520d528d85SFelix Fietkau break;
7530d528d85SFelix Fietkau }
7540d528d85SFelix Fietkau }
7550d528d85SFelix Fietkau
rate_control_cap_mask(struct ieee80211_sub_if_data * sdata,struct ieee80211_supported_band * sband,struct ieee80211_sta * sta,u32 * mask,u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN],u16 vht_mask[NL80211_VHT_NSS_MAX])75690c66bd2SLorenzo Bianconi static bool rate_control_cap_mask(struct ieee80211_sub_if_data *sdata,
75790c66bd2SLorenzo Bianconi struct ieee80211_supported_band *sband,
75890c66bd2SLorenzo Bianconi struct ieee80211_sta *sta, u32 *mask,
759b119ad6eSLorenzo Bianconi u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN],
760b119ad6eSLorenzo Bianconi u16 vht_mask[NL80211_VHT_NSS_MAX])
76190c66bd2SLorenzo Bianconi {
76290c66bd2SLorenzo Bianconi u32 i, flags;
76390c66bd2SLorenzo Bianconi
76490c66bd2SLorenzo Bianconi *mask = sdata->rc_rateidx_mask[sband->band];
76590c66bd2SLorenzo Bianconi flags = ieee80211_chandef_rate_flags(&sdata->vif.bss_conf.chandef);
76690c66bd2SLorenzo Bianconi for (i = 0; i < sband->n_bitrates; i++) {
76790c66bd2SLorenzo Bianconi if ((flags & sband->bitrates[i].flags) != flags)
76890c66bd2SLorenzo Bianconi *mask &= ~BIT(i);
76990c66bd2SLorenzo Bianconi }
77090c66bd2SLorenzo Bianconi
77190c66bd2SLorenzo Bianconi if (*mask == (1 << sband->n_bitrates) - 1 &&
772b119ad6eSLorenzo Bianconi !sdata->rc_has_mcs_mask[sband->band] &&
773b119ad6eSLorenzo Bianconi !sdata->rc_has_vht_mcs_mask[sband->band])
77490c66bd2SLorenzo Bianconi return false;
77590c66bd2SLorenzo Bianconi
77690c66bd2SLorenzo Bianconi if (sdata->rc_has_mcs_mask[sband->band])
77790c66bd2SLorenzo Bianconi memcpy(mcs_mask, sdata->rc_rateidx_mcs_mask[sband->band],
77890c66bd2SLorenzo Bianconi IEEE80211_HT_MCS_MASK_LEN);
77990c66bd2SLorenzo Bianconi else
78090c66bd2SLorenzo Bianconi memset(mcs_mask, 0xff, IEEE80211_HT_MCS_MASK_LEN);
78190c66bd2SLorenzo Bianconi
782b119ad6eSLorenzo Bianconi if (sdata->rc_has_vht_mcs_mask[sband->band])
783b119ad6eSLorenzo Bianconi memcpy(vht_mask, sdata->rc_rateidx_vht_mcs_mask[sband->band],
784b119ad6eSLorenzo Bianconi sizeof(u16) * NL80211_VHT_NSS_MAX);
785b119ad6eSLorenzo Bianconi else
786b119ad6eSLorenzo Bianconi memset(vht_mask, 0xff, sizeof(u16) * NL80211_VHT_NSS_MAX);
787b119ad6eSLorenzo Bianconi
78890c66bd2SLorenzo Bianconi if (sta) {
789b119ad6eSLorenzo Bianconi __le16 sta_vht_cap;
790b119ad6eSLorenzo Bianconi u16 sta_vht_mask[NL80211_VHT_NSS_MAX];
791b119ad6eSLorenzo Bianconi
79290c66bd2SLorenzo Bianconi /* Filter out rates that the STA does not support */
793046d2e7cSSriram R *mask &= sta->deflink.supp_rates[sband->band];
79498a1f828SThierry Reding for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
795046d2e7cSSriram R mcs_mask[i] &= sta->deflink.ht_cap.mcs.rx_mask[i];
796b119ad6eSLorenzo Bianconi
797046d2e7cSSriram R sta_vht_cap = sta->deflink.vht_cap.vht_mcs.rx_mcs_map;
798b119ad6eSLorenzo Bianconi ieee80211_get_vht_mask_from_cap(sta_vht_cap, sta_vht_mask);
799b119ad6eSLorenzo Bianconi for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
800b119ad6eSLorenzo Bianconi vht_mask[i] &= sta_vht_mask[i];
80190c66bd2SLorenzo Bianconi }
80290c66bd2SLorenzo Bianconi
80390c66bd2SLorenzo Bianconi return true;
80490c66bd2SLorenzo Bianconi }
80590c66bd2SLorenzo Bianconi
806e910867bSLorenzo Bianconi static void
rate_control_apply_mask_ratetbl(struct sta_info * sta,struct ieee80211_supported_band * sband,struct ieee80211_sta_rates * rates)807e910867bSLorenzo Bianconi rate_control_apply_mask_ratetbl(struct sta_info *sta,
808e910867bSLorenzo Bianconi struct ieee80211_supported_band *sband,
809e910867bSLorenzo Bianconi struct ieee80211_sta_rates *rates)
810e910867bSLorenzo Bianconi {
811e910867bSLorenzo Bianconi int i;
812e910867bSLorenzo Bianconi u32 mask;
813e910867bSLorenzo Bianconi u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
814b119ad6eSLorenzo Bianconi u16 vht_mask[NL80211_VHT_NSS_MAX];
815e910867bSLorenzo Bianconi enum nl80211_chan_width chan_width;
816e910867bSLorenzo Bianconi
817e910867bSLorenzo Bianconi if (!rate_control_cap_mask(sta->sdata, sband, &sta->sta, &mask,
818b119ad6eSLorenzo Bianconi mcs_mask, vht_mask))
819e910867bSLorenzo Bianconi return;
820e910867bSLorenzo Bianconi
821e910867bSLorenzo Bianconi chan_width = sta->sdata->vif.bss_conf.chandef.width;
822e910867bSLorenzo Bianconi for (i = 0; i < IEEE80211_TX_RATE_TABLE_SIZE; i++) {
823e910867bSLorenzo Bianconi if (rates->rate[i].idx < 0)
824e910867bSLorenzo Bianconi break;
825e910867bSLorenzo Bianconi
826e910867bSLorenzo Bianconi rate_idx_match_mask(&rates->rate[i].idx, &rates->rate[i].flags,
827b119ad6eSLorenzo Bianconi sband, chan_width, mask, mcs_mask,
828b119ad6eSLorenzo Bianconi vht_mask);
829e910867bSLorenzo Bianconi }
830e910867bSLorenzo Bianconi }
831e910867bSLorenzo Bianconi
rate_control_apply_mask(struct ieee80211_sub_if_data * sdata,struct ieee80211_sta * sta,struct ieee80211_supported_band * sband,struct ieee80211_tx_rate * rates,int max_rates)8320d528d85SFelix Fietkau static void rate_control_apply_mask(struct ieee80211_sub_if_data *sdata,
8330d528d85SFelix Fietkau struct ieee80211_sta *sta,
8340d528d85SFelix Fietkau struct ieee80211_supported_band *sband,
8350d528d85SFelix Fietkau struct ieee80211_tx_rate *rates,
8360d528d85SFelix Fietkau int max_rates)
8370d528d85SFelix Fietkau {
8380d528d85SFelix Fietkau enum nl80211_chan_width chan_width;
8390d528d85SFelix Fietkau u8 mcs_mask[IEEE80211_HT_MCS_MASK_LEN];
8400d528d85SFelix Fietkau u32 mask;
841b119ad6eSLorenzo Bianconi u16 rate_flags, vht_mask[NL80211_VHT_NSS_MAX];
8420d528d85SFelix Fietkau int i;
8430d528d85SFelix Fietkau
8440d528d85SFelix Fietkau /*
8450d528d85SFelix Fietkau * Try to enforce the rateidx mask the user wanted. skip this if the
8460d528d85SFelix Fietkau * default mask (allow all rates) is used to save some processing for
8470d528d85SFelix Fietkau * the common case.
8480d528d85SFelix Fietkau */
849b119ad6eSLorenzo Bianconi if (!rate_control_cap_mask(sdata, sband, sta, &mask, mcs_mask,
850b119ad6eSLorenzo Bianconi vht_mask))
8510d528d85SFelix Fietkau return;
8520d528d85SFelix Fietkau
8530d528d85SFelix Fietkau /*
8540d528d85SFelix Fietkau * Make sure the rate index selected for each TX rate is
8550d528d85SFelix Fietkau * included in the configured mask and change the rate indexes
8560d528d85SFelix Fietkau * if needed.
8570d528d85SFelix Fietkau */
8580d528d85SFelix Fietkau chan_width = sdata->vif.bss_conf.chandef.width;
8590d528d85SFelix Fietkau for (i = 0; i < max_rates; i++) {
8600d528d85SFelix Fietkau /* Skip invalid rates */
8610d528d85SFelix Fietkau if (rates[i].idx < 0)
8620d528d85SFelix Fietkau break;
8630d528d85SFelix Fietkau
86490c66bd2SLorenzo Bianconi rate_flags = rates[i].flags;
86590c66bd2SLorenzo Bianconi rate_idx_match_mask(&rates[i].idx, &rate_flags, sband,
866b119ad6eSLorenzo Bianconi chan_width, mask, mcs_mask, vht_mask);
86790c66bd2SLorenzo Bianconi rates[i].flags = rate_flags;
8680d528d85SFelix Fietkau }
8690d528d85SFelix Fietkau }
8700d528d85SFelix Fietkau
ieee80211_get_tx_rates(struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct sk_buff * skb,struct ieee80211_tx_rate * dest,int max_rates)8710d528d85SFelix Fietkau void ieee80211_get_tx_rates(struct ieee80211_vif *vif,
8720d528d85SFelix Fietkau struct ieee80211_sta *sta,
8730d528d85SFelix Fietkau struct sk_buff *skb,
8740d528d85SFelix Fietkau struct ieee80211_tx_rate *dest,
8750d528d85SFelix Fietkau int max_rates)
8760d528d85SFelix Fietkau {
8770d528d85SFelix Fietkau struct ieee80211_sub_if_data *sdata;
8780d528d85SFelix Fietkau struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
8790d528d85SFelix Fietkau struct ieee80211_supported_band *sband;
8800cfadb49SJohannes Berg u32 mask = ~0;
8810d528d85SFelix Fietkau
8820d528d85SFelix Fietkau rate_control_fill_sta_table(sta, info, dest, max_rates);
8830d528d85SFelix Fietkau
8840d528d85SFelix Fietkau if (!vif)
8850d528d85SFelix Fietkau return;
8860d528d85SFelix Fietkau
8870d528d85SFelix Fietkau sdata = vif_to_sdata(vif);
8880d528d85SFelix Fietkau sband = sdata->local->hw.wiphy->bands[info->band];
8890d528d85SFelix Fietkau
8903187ba0cSRyder Lee if (ieee80211_is_tx_data(skb))
89135225eb7SLorenzo Bianconi rate_control_apply_mask(sdata, sta, sband, dest, max_rates);
8920d528d85SFelix Fietkau
893*d54455a3SPing-Ke Shih if (!(info->control.flags & IEEE80211_TX_CTRL_DONT_USE_RATE_MASK))
8940cfadb49SJohannes Berg mask = sdata->rc_rateidx_mask[info->band];
8950cfadb49SJohannes Berg
8960d528d85SFelix Fietkau if (dest[0].idx < 0)
8971d2d350bSAndrei Otcheretianski __rate_control_send_low(&sdata->local->hw, sband, sta, info,
8980cfadb49SJohannes Berg mask);
8990d528d85SFelix Fietkau
9000d528d85SFelix Fietkau if (sta)
9010d528d85SFelix Fietkau rate_fixup_ratelist(vif, sband, info, dest, max_rates);
9020d528d85SFelix Fietkau }
9030d528d85SFelix Fietkau EXPORT_SYMBOL(ieee80211_get_tx_rates);
9040d528d85SFelix Fietkau
rate_control_get_rate(struct ieee80211_sub_if_data * sdata,struct sta_info * sta,struct ieee80211_tx_rate_control * txrc)9054b7679a5SJohannes Berg void rate_control_get_rate(struct ieee80211_sub_if_data *sdata,
906e6a9854bSJohannes Berg struct sta_info *sta,
907e6a9854bSJohannes Berg struct ieee80211_tx_rate_control *txrc)
9082c8dccc7SJohannes Berg {
9094b7679a5SJohannes Berg struct rate_control_ref *ref = sdata->local->rate_ctrl;
9104b7679a5SJohannes Berg void *priv_sta = NULL;
9114b7679a5SJohannes Berg struct ieee80211_sta *ista = NULL;
912e6a9854bSJohannes Berg struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb);
9132c8dccc7SJohannes Berg int i;
9142c8dccc7SJohannes Berg
915e6a9854bSJohannes Berg for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
916e6a9854bSJohannes Berg info->control.rates[i].idx = -1;
917e6a9854bSJohannes Berg info->control.rates[i].flags = 0;
9188617b093SMohammed Shafi Shajakhan info->control.rates[i].count = 0;
919e6a9854bSJohannes Berg }
920e6a9854bSJohannes Berg
921583a7a34SJohannes Berg if (rate_control_send_low(sta ? &sta->sta : NULL, txrc))
9225affcd6bSJuuso Oikarinen return;
9235affcd6bSJuuso Oikarinen
924583a7a34SJohannes Berg if (ieee80211_hw_check(&sdata->local->hw, HAS_RATE_CONTROL))
9251e87fec9SJohannes Berg return;
9261e87fec9SJohannes Berg
927bd718fc1SJohannes Berg if (sta && test_sta_flag(sta, WLAN_STA_RATE_CONTROL)) {
928bd718fc1SJohannes Berg ista = &sta->sta;
929bd718fc1SJohannes Berg priv_sta = sta->rate_ctrl_priv;
930bd718fc1SJohannes Berg }
931bd718fc1SJohannes Berg
93235c347acSJohannes Berg if (ista) {
93335c347acSJohannes Berg spin_lock_bh(&sta->rate_ctrl_lock);
934e6a9854bSJohannes Berg ref->ops->get_rate(ref->priv, ista, priv_sta, txrc);
93535c347acSJohannes Berg spin_unlock_bh(&sta->rate_ctrl_lock);
93635c347acSJohannes Berg } else {
9371e87fec9SJohannes Berg rate_control_send_low(NULL, txrc);
93835c347acSJohannes Berg }
9394b7679a5SJohannes Berg
94030686bf7SJohannes Berg if (ieee80211_hw_check(&sdata->local->hw, SUPPORTS_RC_TABLE))
9410d528d85SFelix Fietkau return;
9422ffbe6d3SFelix Fietkau
9430d528d85SFelix Fietkau ieee80211_get_tx_rates(&sdata->vif, ista, txrc->skb,
9440d528d85SFelix Fietkau info->control.rates,
9450d528d85SFelix Fietkau ARRAY_SIZE(info->control.rates));
9462c8dccc7SJohannes Berg }
947e6a9854bSJohannes Berg
rate_control_set_rates(struct ieee80211_hw * hw,struct ieee80211_sta * pubsta,struct ieee80211_sta_rates * rates)9480d528d85SFelix Fietkau int rate_control_set_rates(struct ieee80211_hw *hw,
9490d528d85SFelix Fietkau struct ieee80211_sta *pubsta,
9500d528d85SFelix Fietkau struct ieee80211_sta_rates *rates)
9510d528d85SFelix Fietkau {
952f815e2b3SJohannes Berg struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
953f6b3d85fSFelix Fietkau struct ieee80211_sta_rates *old;
954e910867bSLorenzo Bianconi struct ieee80211_supported_band *sband;
9550d528d85SFelix Fietkau
95621a8e9ddSMohammed Shafi Shajakhan sband = ieee80211_get_sband(sta->sdata);
95721a8e9ddSMohammed Shafi Shajakhan if (!sband)
95821a8e9ddSMohammed Shafi Shajakhan return -EINVAL;
959e910867bSLorenzo Bianconi rate_control_apply_mask_ratetbl(sta, sband, rates);
960f6b3d85fSFelix Fietkau /*
961f6b3d85fSFelix Fietkau * mac80211 guarantees that this function will not be called
962f6b3d85fSFelix Fietkau * concurrently, so the following RCU access is safe, even without
963f6b3d85fSFelix Fietkau * extra locking. This can not be checked easily, so we just set
964f6b3d85fSFelix Fietkau * the condition to true.
965f6b3d85fSFelix Fietkau */
966f6b3d85fSFelix Fietkau old = rcu_dereference_protected(pubsta->rates, true);
9670d528d85SFelix Fietkau rcu_assign_pointer(pubsta->rates, rates);
9680d528d85SFelix Fietkau if (old)
9690d528d85SFelix Fietkau kfree_rcu(old, rcu_head);
9700d528d85SFelix Fietkau
97118fe0faeSFelix Fietkau if (sta->uploaded)
972f815e2b3SJohannes Berg drv_sta_rate_tbl_update(hw_to_local(hw), sta->sdata, pubsta);
973f815e2b3SJohannes Berg
974484a54c2SToke Høiland-Jørgensen ieee80211_sta_set_expected_throughput(pubsta, sta_get_expected_throughput(sta));
975484a54c2SToke Høiland-Jørgensen
9760d528d85SFelix Fietkau return 0;
9772c8dccc7SJohannes Berg }
9780d528d85SFelix Fietkau EXPORT_SYMBOL(rate_control_set_rates);
9792c8dccc7SJohannes Berg
ieee80211_init_rate_ctrl_alg(struct ieee80211_local * local,const char * name)9802c8dccc7SJohannes Berg int ieee80211_init_rate_ctrl_alg(struct ieee80211_local *local,
9812c8dccc7SJohannes Berg const char *name)
9822c8dccc7SJohannes Berg {
983889cbb91SJohannes Berg struct rate_control_ref *ref;
9842c8dccc7SJohannes Berg
9852c8dccc7SJohannes Berg ASSERT_RTNL();
986af65cd96SJohannes Berg
9873b8d81e0SJohannes Berg if (local->open_count)
9882c8dccc7SJohannes Berg return -EBUSY;
9892c8dccc7SJohannes Berg
99030686bf7SJohannes Berg if (ieee80211_hw_check(&local->hw, HAS_RATE_CONTROL)) {
991af65cd96SJohannes Berg if (WARN_ON(!local->ops->set_rts_threshold))
992af65cd96SJohannes Berg return -EINVAL;
993af65cd96SJohannes Berg return 0;
994af65cd96SJohannes Berg }
995af65cd96SJohannes Berg
9962c8dccc7SJohannes Berg ref = rate_control_alloc(name, local);
9972c8dccc7SJohannes Berg if (!ref) {
9980fb9a9ecSJoe Perches wiphy_warn(local->hw.wiphy,
9990fb9a9ecSJoe Perches "Failed to select rate control algorithm\n");
10002c8dccc7SJohannes Berg return -ENOENT;
10012c8dccc7SJohannes Berg }
10022c8dccc7SJohannes Berg
1003889cbb91SJohannes Berg WARN_ON(local->rate_ctrl);
10042c8dccc7SJohannes Berg local->rate_ctrl = ref;
10052c8dccc7SJohannes Berg
10060fb9a9ecSJoe Perches wiphy_debug(local->hw.wiphy, "Selected rate control algorithm '%s'\n",
10072c8dccc7SJohannes Berg ref->ops->name);
10082c8dccc7SJohannes Berg
10092c8dccc7SJohannes Berg return 0;
10102c8dccc7SJohannes Berg }
10112c8dccc7SJohannes Berg
rate_control_deinitialize(struct ieee80211_local * local)10122c8dccc7SJohannes Berg void rate_control_deinitialize(struct ieee80211_local *local)
10132c8dccc7SJohannes Berg {
10142c8dccc7SJohannes Berg struct rate_control_ref *ref;
10152c8dccc7SJohannes Berg
10162c8dccc7SJohannes Berg ref = local->rate_ctrl;
1017af65cd96SJohannes Berg
1018af65cd96SJohannes Berg if (!ref)
1019af65cd96SJohannes Berg return;
1020af65cd96SJohannes Berg
10212c8dccc7SJohannes Berg local->rate_ctrl = NULL;
1022a858958bSJohannes Berg rate_control_free(local, ref);
10232c8dccc7SJohannes Berg }
1024