18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci	Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
48c2ecf20Sopenharmony_ci	<http://rt2x00.serialmonkey.com>
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci	Module: rt2x00lib
108c2ecf20Sopenharmony_ci	Abstract: rt2x00 generic configuration routines.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "rt2x00.h"
178c2ecf20Sopenharmony_ci#include "rt2x00lib.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_civoid rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev,
208c2ecf20Sopenharmony_ci			   struct rt2x00_intf *intf,
218c2ecf20Sopenharmony_ci			   enum nl80211_iftype type,
228c2ecf20Sopenharmony_ci			   const u8 *mac, const u8 *bssid)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	struct rt2x00intf_conf conf;
258c2ecf20Sopenharmony_ci	unsigned int flags = 0;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	conf.type = type;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	switch (type) {
308c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
318c2ecf20Sopenharmony_ci		conf.sync = TSF_SYNC_ADHOC;
328c2ecf20Sopenharmony_ci		break;
338c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_AP:
348c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
358c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_WDS:
368c2ecf20Sopenharmony_ci		conf.sync = TSF_SYNC_AP_NONE;
378c2ecf20Sopenharmony_ci		break;
388c2ecf20Sopenharmony_ci	case NL80211_IFTYPE_STATION:
398c2ecf20Sopenharmony_ci		conf.sync = TSF_SYNC_INFRA;
408c2ecf20Sopenharmony_ci		break;
418c2ecf20Sopenharmony_ci	default:
428c2ecf20Sopenharmony_ci		conf.sync = TSF_SYNC_NONE;
438c2ecf20Sopenharmony_ci		break;
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	/*
478c2ecf20Sopenharmony_ci	 * Note that when NULL is passed as address we will send
488c2ecf20Sopenharmony_ci	 * 00:00:00:00:00 to the device to clear the address.
498c2ecf20Sopenharmony_ci	 * This will prevent the device being confused when it wants
508c2ecf20Sopenharmony_ci	 * to ACK frames or considers itself associated.
518c2ecf20Sopenharmony_ci	 */
528c2ecf20Sopenharmony_ci	memset(conf.mac, 0, sizeof(conf.mac));
538c2ecf20Sopenharmony_ci	if (mac)
548c2ecf20Sopenharmony_ci		memcpy(conf.mac, mac, ETH_ALEN);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	memset(conf.bssid, 0, sizeof(conf.bssid));
578c2ecf20Sopenharmony_ci	if (bssid)
588c2ecf20Sopenharmony_ci		memcpy(conf.bssid, bssid, ETH_ALEN);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	flags |= CONFIG_UPDATE_TYPE;
618c2ecf20Sopenharmony_ci	if (mac || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count))
628c2ecf20Sopenharmony_ci		flags |= CONFIG_UPDATE_MAC;
638c2ecf20Sopenharmony_ci	if (bssid || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count))
648c2ecf20Sopenharmony_ci		flags |= CONFIG_UPDATE_BSSID;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	rt2x00dev->ops->lib->config_intf(rt2x00dev, intf, &conf, flags);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_civoid rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,
708c2ecf20Sopenharmony_ci			  struct rt2x00_intf *intf,
718c2ecf20Sopenharmony_ci			  struct ieee80211_bss_conf *bss_conf,
728c2ecf20Sopenharmony_ci			  u32 changed)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct rt2x00lib_erp erp;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	memset(&erp, 0, sizeof(erp));
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	erp.short_preamble = bss_conf->use_short_preamble;
798c2ecf20Sopenharmony_ci	erp.cts_protection = bss_conf->use_cts_prot;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	erp.slot_time = bss_conf->use_short_slot ? SHORT_SLOT_TIME : SLOT_TIME;
828c2ecf20Sopenharmony_ci	erp.sifs = SIFS;
838c2ecf20Sopenharmony_ci	erp.pifs = bss_conf->use_short_slot ? SHORT_PIFS : PIFS;
848c2ecf20Sopenharmony_ci	erp.difs = bss_conf->use_short_slot ? SHORT_DIFS : DIFS;
858c2ecf20Sopenharmony_ci	erp.eifs = bss_conf->use_short_slot ? SHORT_EIFS : EIFS;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	erp.basic_rates = bss_conf->basic_rates;
888c2ecf20Sopenharmony_ci	erp.beacon_int = bss_conf->beacon_int;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* Update the AID, this is needed for dynamic PS support */
918c2ecf20Sopenharmony_ci	rt2x00dev->aid = bss_conf->assoc ? bss_conf->aid : 0;
928c2ecf20Sopenharmony_ci	rt2x00dev->last_beacon = bss_conf->sync_tsf;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* Update global beacon interval time, this is needed for PS support */
958c2ecf20Sopenharmony_ci	rt2x00dev->beacon_int = bss_conf->beacon_int;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (changed & BSS_CHANGED_HT)
988c2ecf20Sopenharmony_ci		erp.ht_opmode = bss_conf->ht_operation_mode;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp, changed);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_civoid rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
1048c2ecf20Sopenharmony_ci			      struct antenna_setup config)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct link_ant *ant = &rt2x00dev->link.ant;
1078c2ecf20Sopenharmony_ci	struct antenna_setup *def = &rt2x00dev->default_ant;
1088c2ecf20Sopenharmony_ci	struct antenna_setup *active = &rt2x00dev->link.ant.active;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/*
1118c2ecf20Sopenharmony_ci	 * When the caller tries to send the SW diversity,
1128c2ecf20Sopenharmony_ci	 * we must update the ANTENNA_RX_DIVERSITY flag to
1138c2ecf20Sopenharmony_ci	 * enable the antenna diversity in the link tuner.
1148c2ecf20Sopenharmony_ci	 *
1158c2ecf20Sopenharmony_ci	 * Secondly, we must guarentee we never send the
1168c2ecf20Sopenharmony_ci	 * software antenna diversity command to the driver.
1178c2ecf20Sopenharmony_ci	 */
1188c2ecf20Sopenharmony_ci	if (!(ant->flags & ANTENNA_RX_DIVERSITY)) {
1198c2ecf20Sopenharmony_ci		if (config.rx == ANTENNA_SW_DIVERSITY) {
1208c2ecf20Sopenharmony_ci			ant->flags |= ANTENNA_RX_DIVERSITY;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci			if (def->rx == ANTENNA_SW_DIVERSITY)
1238c2ecf20Sopenharmony_ci				config.rx = ANTENNA_B;
1248c2ecf20Sopenharmony_ci			else
1258c2ecf20Sopenharmony_ci				config.rx = def->rx;
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci	} else if (config.rx == ANTENNA_SW_DIVERSITY)
1288c2ecf20Sopenharmony_ci		config.rx = active->rx;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (!(ant->flags & ANTENNA_TX_DIVERSITY)) {
1318c2ecf20Sopenharmony_ci		if (config.tx == ANTENNA_SW_DIVERSITY) {
1328c2ecf20Sopenharmony_ci			ant->flags |= ANTENNA_TX_DIVERSITY;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci			if (def->tx == ANTENNA_SW_DIVERSITY)
1358c2ecf20Sopenharmony_ci				config.tx = ANTENNA_B;
1368c2ecf20Sopenharmony_ci			else
1378c2ecf20Sopenharmony_ci				config.tx = def->tx;
1388c2ecf20Sopenharmony_ci		}
1398c2ecf20Sopenharmony_ci	} else if (config.tx == ANTENNA_SW_DIVERSITY)
1408c2ecf20Sopenharmony_ci		config.tx = active->tx;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/*
1438c2ecf20Sopenharmony_ci	 * Antenna setup changes require the RX to be disabled,
1448c2ecf20Sopenharmony_ci	 * else the changes will be ignored by the device.
1458c2ecf20Sopenharmony_ci	 */
1468c2ecf20Sopenharmony_ci	if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
1478c2ecf20Sopenharmony_ci		rt2x00queue_stop_queue(rt2x00dev->rx);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/*
1508c2ecf20Sopenharmony_ci	 * Write new antenna setup to device and reset the link tuner.
1518c2ecf20Sopenharmony_ci	 * The latter is required since we need to recalibrate the
1528c2ecf20Sopenharmony_ci	 * noise-sensitivity ratio for the new setup.
1538c2ecf20Sopenharmony_ci	 */
1548c2ecf20Sopenharmony_ci	rt2x00dev->ops->lib->config_ant(rt2x00dev, &config);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	rt2x00link_reset_tuner(rt2x00dev, true);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	memcpy(active, &config, sizeof(config));
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
1618c2ecf20Sopenharmony_ci		rt2x00queue_start_queue(rt2x00dev->rx);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic u16 rt2x00ht_center_channel(struct rt2x00_dev *rt2x00dev,
1658c2ecf20Sopenharmony_ci				   struct ieee80211_conf *conf)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	struct hw_mode_spec *spec = &rt2x00dev->spec;
1688c2ecf20Sopenharmony_ci	int center_channel;
1698c2ecf20Sopenharmony_ci	u16 i;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/*
1728c2ecf20Sopenharmony_ci	 * Initialize center channel to current channel.
1738c2ecf20Sopenharmony_ci	 */
1748c2ecf20Sopenharmony_ci	center_channel = spec->channels[conf->chandef.chan->hw_value].channel;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/*
1778c2ecf20Sopenharmony_ci	 * Adjust center channel to HT40+ and HT40- operation.
1788c2ecf20Sopenharmony_ci	 */
1798c2ecf20Sopenharmony_ci	if (conf_is_ht40_plus(conf))
1808c2ecf20Sopenharmony_ci		center_channel += 2;
1818c2ecf20Sopenharmony_ci	else if (conf_is_ht40_minus(conf))
1828c2ecf20Sopenharmony_ci		center_channel -= (center_channel == 14) ? 1 : 2;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	for (i = 0; i < spec->num_channels; i++)
1858c2ecf20Sopenharmony_ci		if (spec->channels[i].channel == center_channel)
1868c2ecf20Sopenharmony_ci			return i;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	WARN_ON(1);
1898c2ecf20Sopenharmony_ci	return conf->chandef.chan->hw_value;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_civoid rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
1938c2ecf20Sopenharmony_ci		      struct ieee80211_conf *conf,
1948c2ecf20Sopenharmony_ci		      unsigned int ieee80211_flags)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct rt2x00lib_conf libconf;
1978c2ecf20Sopenharmony_ci	u16 hw_value;
1988c2ecf20Sopenharmony_ci	u16 autowake_timeout;
1998c2ecf20Sopenharmony_ci	u16 beacon_int;
2008c2ecf20Sopenharmony_ci	u16 beacon_diff;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	memset(&libconf, 0, sizeof(libconf));
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	libconf.conf = conf;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) {
2078c2ecf20Sopenharmony_ci		if (!conf_is_ht(conf))
2088c2ecf20Sopenharmony_ci			set_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags);
2098c2ecf20Sopenharmony_ci		else
2108c2ecf20Sopenharmony_ci			clear_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags);
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		if (conf_is_ht40(conf)) {
2138c2ecf20Sopenharmony_ci			set_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags);
2148c2ecf20Sopenharmony_ci			hw_value = rt2x00ht_center_channel(rt2x00dev, conf);
2158c2ecf20Sopenharmony_ci		} else {
2168c2ecf20Sopenharmony_ci			clear_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags);
2178c2ecf20Sopenharmony_ci			hw_value = conf->chandef.chan->hw_value;
2188c2ecf20Sopenharmony_ci		}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		memcpy(&libconf.rf,
2218c2ecf20Sopenharmony_ci		       &rt2x00dev->spec.channels[hw_value],
2228c2ecf20Sopenharmony_ci		       sizeof(libconf.rf));
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		memcpy(&libconf.channel,
2258c2ecf20Sopenharmony_ci		       &rt2x00dev->spec.channels_info[hw_value],
2268c2ecf20Sopenharmony_ci		       sizeof(libconf.channel));
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		/* Used for VCO periodic calibration */
2298c2ecf20Sopenharmony_ci		rt2x00dev->rf_channel = libconf.rf.channel;
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) &&
2338c2ecf20Sopenharmony_ci	    (ieee80211_flags & IEEE80211_CONF_CHANGE_PS))
2348c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/*
2378c2ecf20Sopenharmony_ci	 * Start configuration.
2388c2ecf20Sopenharmony_ci	 */
2398c2ecf20Sopenharmony_ci	rt2x00dev->ops->lib->config(rt2x00dev, &libconf, ieee80211_flags);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (conf->flags & IEEE80211_CONF_PS)
2428c2ecf20Sopenharmony_ci		set_bit(CONFIG_POWERSAVING, &rt2x00dev->flags);
2438c2ecf20Sopenharmony_ci	else
2448c2ecf20Sopenharmony_ci		clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	if (conf->flags & IEEE80211_CONF_MONITOR)
2478c2ecf20Sopenharmony_ci		set_bit(CONFIG_MONITORING, &rt2x00dev->flags);
2488c2ecf20Sopenharmony_ci	else
2498c2ecf20Sopenharmony_ci		clear_bit(CONFIG_MONITORING, &rt2x00dev->flags);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	rt2x00dev->curr_band = conf->chandef.chan->band;
2528c2ecf20Sopenharmony_ci	rt2x00dev->curr_freq = conf->chandef.chan->center_freq;
2538c2ecf20Sopenharmony_ci	rt2x00dev->tx_power = conf->power_level;
2548c2ecf20Sopenharmony_ci	rt2x00dev->short_retry = conf->short_frame_max_tx_count;
2558c2ecf20Sopenharmony_ci	rt2x00dev->long_retry = conf->long_frame_max_tx_count;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/*
2588c2ecf20Sopenharmony_ci	 * Some configuration changes affect the link quality
2598c2ecf20Sopenharmony_ci	 * which means we need to reset the link tuner.
2608c2ecf20Sopenharmony_ci	 */
2618c2ecf20Sopenharmony_ci	if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL)
2628c2ecf20Sopenharmony_ci		rt2x00link_reset_tuner(rt2x00dev, false);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
2658c2ecf20Sopenharmony_ci	    rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) &&
2668c2ecf20Sopenharmony_ci	    (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) &&
2678c2ecf20Sopenharmony_ci	    (conf->flags & IEEE80211_CONF_PS)) {
2688c2ecf20Sopenharmony_ci		beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon;
2698c2ecf20Sopenharmony_ci		beacon_int = msecs_to_jiffies(rt2x00dev->beacon_int);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci		if (beacon_diff > beacon_int)
2728c2ecf20Sopenharmony_ci			beacon_diff = 0;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		autowake_timeout = (conf->ps_dtim_period * beacon_int) - beacon_diff;
2758c2ecf20Sopenharmony_ci		queue_delayed_work(rt2x00dev->workqueue,
2768c2ecf20Sopenharmony_ci				   &rt2x00dev->autowakeup_work,
2778c2ecf20Sopenharmony_ci				   autowake_timeout - 15);
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci}
280