162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com> 462306a36Sopenharmony_ci <http://rt2x00.serialmonkey.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci/* 962306a36Sopenharmony_ci Module: rt2x00lib 1062306a36Sopenharmony_ci Abstract: rt2x00 generic configuration routines. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "rt2x00.h" 1762306a36Sopenharmony_ci#include "rt2x00lib.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_civoid rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev, 2062306a36Sopenharmony_ci struct rt2x00_intf *intf, 2162306a36Sopenharmony_ci enum nl80211_iftype type, 2262306a36Sopenharmony_ci const u8 *mac, const u8 *bssid) 2362306a36Sopenharmony_ci{ 2462306a36Sopenharmony_ci struct rt2x00intf_conf conf; 2562306a36Sopenharmony_ci unsigned int flags = 0; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci conf.type = type; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci switch (type) { 3062306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 3162306a36Sopenharmony_ci conf.sync = TSF_SYNC_ADHOC; 3262306a36Sopenharmony_ci break; 3362306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 3462306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 3562306a36Sopenharmony_ci conf.sync = TSF_SYNC_AP_NONE; 3662306a36Sopenharmony_ci break; 3762306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 3862306a36Sopenharmony_ci conf.sync = TSF_SYNC_INFRA; 3962306a36Sopenharmony_ci break; 4062306a36Sopenharmony_ci default: 4162306a36Sopenharmony_ci conf.sync = TSF_SYNC_NONE; 4262306a36Sopenharmony_ci break; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* 4662306a36Sopenharmony_ci * Note that when NULL is passed as address we will send 4762306a36Sopenharmony_ci * 00:00:00:00:00 to the device to clear the address. 4862306a36Sopenharmony_ci * This will prevent the device being confused when it wants 4962306a36Sopenharmony_ci * to ACK frames or considers itself associated. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci memset(conf.mac, 0, sizeof(conf.mac)); 5262306a36Sopenharmony_ci if (mac) 5362306a36Sopenharmony_ci memcpy(conf.mac, mac, ETH_ALEN); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci memset(conf.bssid, 0, sizeof(conf.bssid)); 5662306a36Sopenharmony_ci if (bssid) 5762306a36Sopenharmony_ci memcpy(conf.bssid, bssid, ETH_ALEN); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci flags |= CONFIG_UPDATE_TYPE; 6062306a36Sopenharmony_ci if (mac || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)) 6162306a36Sopenharmony_ci flags |= CONFIG_UPDATE_MAC; 6262306a36Sopenharmony_ci if (bssid || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count)) 6362306a36Sopenharmony_ci flags |= CONFIG_UPDATE_BSSID; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci rt2x00dev->ops->lib->config_intf(rt2x00dev, intf, &conf, flags); 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_civoid rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev, 6962306a36Sopenharmony_ci struct rt2x00_intf *intf, 7062306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf, 7162306a36Sopenharmony_ci u32 changed) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct ieee80211_vif *vif = container_of(bss_conf, struct ieee80211_vif, 7462306a36Sopenharmony_ci bss_conf); 7562306a36Sopenharmony_ci struct rt2x00lib_erp erp; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci memset(&erp, 0, sizeof(erp)); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci erp.short_preamble = bss_conf->use_short_preamble; 8062306a36Sopenharmony_ci erp.cts_protection = bss_conf->use_cts_prot; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci erp.slot_time = bss_conf->use_short_slot ? SHORT_SLOT_TIME : SLOT_TIME; 8362306a36Sopenharmony_ci erp.sifs = SIFS; 8462306a36Sopenharmony_ci erp.pifs = bss_conf->use_short_slot ? SHORT_PIFS : PIFS; 8562306a36Sopenharmony_ci erp.difs = bss_conf->use_short_slot ? SHORT_DIFS : DIFS; 8662306a36Sopenharmony_ci erp.eifs = bss_conf->use_short_slot ? SHORT_EIFS : EIFS; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci erp.basic_rates = bss_conf->basic_rates; 8962306a36Sopenharmony_ci erp.beacon_int = bss_conf->beacon_int; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* Update the AID, this is needed for dynamic PS support */ 9262306a36Sopenharmony_ci rt2x00dev->aid = vif->cfg.assoc ? vif->cfg.aid : 0; 9362306a36Sopenharmony_ci rt2x00dev->last_beacon = bss_conf->sync_tsf; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* Update global beacon interval time, this is needed for PS support */ 9662306a36Sopenharmony_ci rt2x00dev->beacon_int = bss_conf->beacon_int; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (changed & BSS_CHANGED_HT) 9962306a36Sopenharmony_ci erp.ht_opmode = bss_conf->ht_operation_mode; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp, changed); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_civoid rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev, 10562306a36Sopenharmony_ci struct antenna_setup config) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct link_ant *ant = &rt2x00dev->link.ant; 10862306a36Sopenharmony_ci struct antenna_setup *def = &rt2x00dev->default_ant; 10962306a36Sopenharmony_ci struct antenna_setup *active = &rt2x00dev->link.ant.active; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * When the caller tries to send the SW diversity, 11362306a36Sopenharmony_ci * we must update the ANTENNA_RX_DIVERSITY flag to 11462306a36Sopenharmony_ci * enable the antenna diversity in the link tuner. 11562306a36Sopenharmony_ci * 11662306a36Sopenharmony_ci * Secondly, we must guarentee we never send the 11762306a36Sopenharmony_ci * software antenna diversity command to the driver. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci if (!(ant->flags & ANTENNA_RX_DIVERSITY)) { 12062306a36Sopenharmony_ci if (config.rx == ANTENNA_SW_DIVERSITY) { 12162306a36Sopenharmony_ci ant->flags |= ANTENNA_RX_DIVERSITY; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (def->rx == ANTENNA_SW_DIVERSITY) 12462306a36Sopenharmony_ci config.rx = ANTENNA_B; 12562306a36Sopenharmony_ci else 12662306a36Sopenharmony_ci config.rx = def->rx; 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci } else if (config.rx == ANTENNA_SW_DIVERSITY) 12962306a36Sopenharmony_ci config.rx = active->rx; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci if (!(ant->flags & ANTENNA_TX_DIVERSITY)) { 13262306a36Sopenharmony_ci if (config.tx == ANTENNA_SW_DIVERSITY) { 13362306a36Sopenharmony_ci ant->flags |= ANTENNA_TX_DIVERSITY; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (def->tx == ANTENNA_SW_DIVERSITY) 13662306a36Sopenharmony_ci config.tx = ANTENNA_B; 13762306a36Sopenharmony_ci else 13862306a36Sopenharmony_ci config.tx = def->tx; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci } else if (config.tx == ANTENNA_SW_DIVERSITY) 14162306a36Sopenharmony_ci config.tx = active->tx; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * Antenna setup changes require the RX to be disabled, 14562306a36Sopenharmony_ci * else the changes will be ignored by the device. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 14862306a36Sopenharmony_ci rt2x00queue_stop_queue(rt2x00dev->rx); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci /* 15162306a36Sopenharmony_ci * Write new antenna setup to device and reset the link tuner. 15262306a36Sopenharmony_ci * The latter is required since we need to recalibrate the 15362306a36Sopenharmony_ci * noise-sensitivity ratio for the new setup. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci rt2x00dev->ops->lib->config_ant(rt2x00dev, &config); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci rt2x00link_reset_tuner(rt2x00dev, true); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci memcpy(active, &config, sizeof(config)); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) 16262306a36Sopenharmony_ci rt2x00queue_start_queue(rt2x00dev->rx); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic u16 rt2x00ht_center_channel(struct rt2x00_dev *rt2x00dev, 16662306a36Sopenharmony_ci struct ieee80211_conf *conf) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct hw_mode_spec *spec = &rt2x00dev->spec; 16962306a36Sopenharmony_ci int center_channel; 17062306a36Sopenharmony_ci u16 i; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * Initialize center channel to current channel. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_ci center_channel = spec->channels[conf->chandef.chan->hw_value].channel; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * Adjust center channel to HT40+ and HT40- operation. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci if (conf_is_ht40_plus(conf)) 18162306a36Sopenharmony_ci center_channel += 2; 18262306a36Sopenharmony_ci else if (conf_is_ht40_minus(conf)) 18362306a36Sopenharmony_ci center_channel -= (center_channel == 14) ? 1 : 2; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci for (i = 0; i < spec->num_channels; i++) 18662306a36Sopenharmony_ci if (spec->channels[i].channel == center_channel) 18762306a36Sopenharmony_ci return i; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci WARN_ON(1); 19062306a36Sopenharmony_ci return conf->chandef.chan->hw_value; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_civoid rt2x00lib_config(struct rt2x00_dev *rt2x00dev, 19462306a36Sopenharmony_ci struct ieee80211_conf *conf, 19562306a36Sopenharmony_ci unsigned int ieee80211_flags) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct rt2x00lib_conf libconf; 19862306a36Sopenharmony_ci u16 hw_value; 19962306a36Sopenharmony_ci u16 autowake_timeout; 20062306a36Sopenharmony_ci u16 beacon_int; 20162306a36Sopenharmony_ci u16 beacon_diff; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci memset(&libconf, 0, sizeof(libconf)); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci libconf.conf = conf; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) { 20862306a36Sopenharmony_ci if (!conf_is_ht(conf)) 20962306a36Sopenharmony_ci set_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags); 21062306a36Sopenharmony_ci else 21162306a36Sopenharmony_ci clear_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci if (conf_is_ht40(conf)) { 21462306a36Sopenharmony_ci set_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags); 21562306a36Sopenharmony_ci hw_value = rt2x00ht_center_channel(rt2x00dev, conf); 21662306a36Sopenharmony_ci } else { 21762306a36Sopenharmony_ci clear_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags); 21862306a36Sopenharmony_ci hw_value = conf->chandef.chan->hw_value; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci memcpy(&libconf.rf, 22262306a36Sopenharmony_ci &rt2x00dev->spec.channels[hw_value], 22362306a36Sopenharmony_ci sizeof(libconf.rf)); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci memcpy(&libconf.channel, 22662306a36Sopenharmony_ci &rt2x00dev->spec.channels_info[hw_value], 22762306a36Sopenharmony_ci sizeof(libconf.channel)); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Used for VCO periodic calibration */ 23062306a36Sopenharmony_ci rt2x00dev->rf_channel = libconf.rf.channel; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) && 23462306a36Sopenharmony_ci (ieee80211_flags & IEEE80211_CONF_CHANGE_PS)) 23562306a36Sopenharmony_ci cancel_delayed_work_sync(&rt2x00dev->autowakeup_work); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* 23862306a36Sopenharmony_ci * Start configuration. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci rt2x00dev->ops->lib->config(rt2x00dev, &libconf, ieee80211_flags); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (conf->flags & IEEE80211_CONF_PS) 24362306a36Sopenharmony_ci set_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); 24462306a36Sopenharmony_ci else 24562306a36Sopenharmony_ci clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (conf->flags & IEEE80211_CONF_MONITOR) 24862306a36Sopenharmony_ci set_bit(CONFIG_MONITORING, &rt2x00dev->flags); 24962306a36Sopenharmony_ci else 25062306a36Sopenharmony_ci clear_bit(CONFIG_MONITORING, &rt2x00dev->flags); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci rt2x00dev->curr_band = conf->chandef.chan->band; 25362306a36Sopenharmony_ci rt2x00dev->curr_freq = conf->chandef.chan->center_freq; 25462306a36Sopenharmony_ci rt2x00dev->tx_power = conf->power_level; 25562306a36Sopenharmony_ci rt2x00dev->short_retry = conf->short_frame_max_tx_count; 25662306a36Sopenharmony_ci rt2x00dev->long_retry = conf->long_frame_max_tx_count; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* 25962306a36Sopenharmony_ci * Some configuration changes affect the link quality 26062306a36Sopenharmony_ci * which means we need to reset the link tuner. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) 26362306a36Sopenharmony_ci rt2x00link_reset_tuner(rt2x00dev, false); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && 26662306a36Sopenharmony_ci rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) && 26762306a36Sopenharmony_ci (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) && 26862306a36Sopenharmony_ci (conf->flags & IEEE80211_CONF_PS)) { 26962306a36Sopenharmony_ci beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon; 27062306a36Sopenharmony_ci beacon_int = msecs_to_jiffies(rt2x00dev->beacon_int); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (beacon_diff > beacon_int) 27362306a36Sopenharmony_ci beacon_diff = 0; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci autowake_timeout = (conf->ps_dtim_period * beacon_int) - beacon_diff; 27662306a36Sopenharmony_ci queue_delayed_work(rt2x00dev->workqueue, 27762306a36Sopenharmony_ci &rt2x00dev->autowakeup_work, 27862306a36Sopenharmony_ci autowake_timeout - 15); 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci} 281