281 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>4 <http://rt2x00.serialmonkey.com>5 6 */7 8/*9 Module: rt2x00lib10 Abstract: rt2x00 generic configuration routines.11 */12 13#include <linux/kernel.h>14#include <linux/module.h>15 16#include "rt2x00.h"17#include "rt2x00lib.h"18 19void rt2x00lib_config_intf(struct rt2x00_dev *rt2x00dev,20 struct rt2x00_intf *intf,21 enum nl80211_iftype type,22 const u8 *mac, const u8 *bssid)23{24 struct rt2x00intf_conf conf;25 unsigned int flags = 0;26 27 conf.type = type;28 29 switch (type) {30 case NL80211_IFTYPE_ADHOC:31 conf.sync = TSF_SYNC_ADHOC;32 break;33 case NL80211_IFTYPE_AP:34 case NL80211_IFTYPE_MESH_POINT:35 conf.sync = TSF_SYNC_AP_NONE;36 break;37 case NL80211_IFTYPE_STATION:38 conf.sync = TSF_SYNC_INFRA;39 break;40 default:41 conf.sync = TSF_SYNC_NONE;42 break;43 }44 45 /*46 * Note that when NULL is passed as address we will send47 * 00:00:00:00:00 to the device to clear the address.48 * This will prevent the device being confused when it wants49 * to ACK frames or considers itself associated.50 */51 memset(conf.mac, 0, sizeof(conf.mac));52 if (mac)53 memcpy(conf.mac, mac, ETH_ALEN);54 55 memset(conf.bssid, 0, sizeof(conf.bssid));56 if (bssid)57 memcpy(conf.bssid, bssid, ETH_ALEN);58 59 flags |= CONFIG_UPDATE_TYPE;60 if (mac || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count))61 flags |= CONFIG_UPDATE_MAC;62 if (bssid || (!rt2x00dev->intf_ap_count && !rt2x00dev->intf_sta_count))63 flags |= CONFIG_UPDATE_BSSID;64 65 rt2x00dev->ops->lib->config_intf(rt2x00dev, intf, &conf, flags);66}67 68void rt2x00lib_config_erp(struct rt2x00_dev *rt2x00dev,69 struct rt2x00_intf *intf,70 struct ieee80211_bss_conf *bss_conf,71 u32 changed)72{73 struct ieee80211_vif *vif = container_of(bss_conf, struct ieee80211_vif,74 bss_conf);75 struct rt2x00lib_erp erp;76 77 memset(&erp, 0, sizeof(erp));78 79 erp.short_preamble = bss_conf->use_short_preamble;80 erp.cts_protection = bss_conf->use_cts_prot;81 82 erp.slot_time = bss_conf->use_short_slot ? SHORT_SLOT_TIME : SLOT_TIME;83 erp.sifs = SIFS;84 erp.pifs = bss_conf->use_short_slot ? SHORT_PIFS : PIFS;85 erp.difs = bss_conf->use_short_slot ? SHORT_DIFS : DIFS;86 erp.eifs = bss_conf->use_short_slot ? SHORT_EIFS : EIFS;87 88 erp.basic_rates = bss_conf->basic_rates;89 erp.beacon_int = bss_conf->beacon_int;90 91 /* Update the AID, this is needed for dynamic PS support */92 rt2x00dev->aid = vif->cfg.assoc ? vif->cfg.aid : 0;93 rt2x00dev->last_beacon = bss_conf->sync_tsf;94 95 /* Update global beacon interval time, this is needed for PS support */96 rt2x00dev->beacon_int = bss_conf->beacon_int;97 98 if (changed & BSS_CHANGED_HT)99 erp.ht_opmode = bss_conf->ht_operation_mode;100 101 rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp, changed);102}103 104void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,105 struct antenna_setup config)106{107 struct link_ant *ant = &rt2x00dev->link.ant;108 struct antenna_setup *def = &rt2x00dev->default_ant;109 struct antenna_setup *active = &rt2x00dev->link.ant.active;110 111 /*112 * When the caller tries to send the SW diversity,113 * we must update the ANTENNA_RX_DIVERSITY flag to114 * enable the antenna diversity in the link tuner.115 *116 * Secondly, we must guarentee we never send the117 * software antenna diversity command to the driver.118 */119 if (!(ant->flags & ANTENNA_RX_DIVERSITY)) {120 if (config.rx == ANTENNA_SW_DIVERSITY) {121 ant->flags |= ANTENNA_RX_DIVERSITY;122 123 if (def->rx == ANTENNA_SW_DIVERSITY)124 config.rx = ANTENNA_B;125 else126 config.rx = def->rx;127 }128 } else if (config.rx == ANTENNA_SW_DIVERSITY)129 config.rx = active->rx;130 131 if (!(ant->flags & ANTENNA_TX_DIVERSITY)) {132 if (config.tx == ANTENNA_SW_DIVERSITY) {133 ant->flags |= ANTENNA_TX_DIVERSITY;134 135 if (def->tx == ANTENNA_SW_DIVERSITY)136 config.tx = ANTENNA_B;137 else138 config.tx = def->tx;139 }140 } else if (config.tx == ANTENNA_SW_DIVERSITY)141 config.tx = active->tx;142 143 /*144 * Antenna setup changes require the RX to be disabled,145 * else the changes will be ignored by the device.146 */147 if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))148 rt2x00queue_stop_queue(rt2x00dev->rx);149 150 /*151 * Write new antenna setup to device and reset the link tuner.152 * The latter is required since we need to recalibrate the153 * noise-sensitivity ratio for the new setup.154 */155 rt2x00dev->ops->lib->config_ant(rt2x00dev, &config);156 157 rt2x00link_reset_tuner(rt2x00dev, true);158 159 memcpy(active, &config, sizeof(config));160 161 if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))162 rt2x00queue_start_queue(rt2x00dev->rx);163}164 165static u16 rt2x00ht_center_channel(struct rt2x00_dev *rt2x00dev,166 struct ieee80211_conf *conf)167{168 struct hw_mode_spec *spec = &rt2x00dev->spec;169 int center_channel;170 u16 i;171 172 /*173 * Initialize center channel to current channel.174 */175 center_channel = spec->channels[conf->chandef.chan->hw_value].channel;176 177 /*178 * Adjust center channel to HT40+ and HT40- operation.179 */180 if (conf_is_ht40_plus(conf))181 center_channel += 2;182 else if (conf_is_ht40_minus(conf))183 center_channel -= (center_channel == 14) ? 1 : 2;184 185 for (i = 0; i < spec->num_channels; i++)186 if (spec->channels[i].channel == center_channel)187 return i;188 189 WARN_ON(1);190 return conf->chandef.chan->hw_value;191}192 193void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,194 struct ieee80211_conf *conf,195 unsigned int ieee80211_flags)196{197 struct rt2x00lib_conf libconf;198 u16 hw_value;199 u16 autowake_timeout;200 u16 beacon_int;201 u16 beacon_diff;202 203 memset(&libconf, 0, sizeof(libconf));204 205 libconf.conf = conf;206 207 if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) {208 if (!conf_is_ht(conf))209 set_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags);210 else211 clear_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags);212 213 if (conf_is_ht40(conf)) {214 set_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags);215 hw_value = rt2x00ht_center_channel(rt2x00dev, conf);216 } else {217 clear_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags);218 hw_value = conf->chandef.chan->hw_value;219 }220 221 memcpy(&libconf.rf,222 &rt2x00dev->spec.channels[hw_value],223 sizeof(libconf.rf));224 225 memcpy(&libconf.channel,226 &rt2x00dev->spec.channels_info[hw_value],227 sizeof(libconf.channel));228 229 /* Used for VCO periodic calibration */230 rt2x00dev->rf_channel = libconf.rf.channel;231 }232 233 if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) &&234 (ieee80211_flags & IEEE80211_CONF_CHANGE_PS))235 cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);236 237 /*238 * Start configuration.239 */240 rt2x00dev->ops->lib->config(rt2x00dev, &libconf, ieee80211_flags);241 242 if (conf->flags & IEEE80211_CONF_PS)243 set_bit(CONFIG_POWERSAVING, &rt2x00dev->flags);244 else245 clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags);246 247 if (conf->flags & IEEE80211_CONF_MONITOR)248 set_bit(CONFIG_MONITORING, &rt2x00dev->flags);249 else250 clear_bit(CONFIG_MONITORING, &rt2x00dev->flags);251 252 rt2x00dev->curr_band = conf->chandef.chan->band;253 rt2x00dev->curr_freq = conf->chandef.chan->center_freq;254 rt2x00dev->tx_power = conf->power_level;255 rt2x00dev->short_retry = conf->short_frame_max_tx_count;256 rt2x00dev->long_retry = conf->long_frame_max_tx_count;257 258 /*259 * Some configuration changes affect the link quality260 * which means we need to reset the link tuner.261 */262 if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL)263 rt2x00link_reset_tuner(rt2x00dev, false);264 265 if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&266 rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) &&267 (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) &&268 (conf->flags & IEEE80211_CONF_PS)) {269 beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon;270 beacon_int = msecs_to_jiffies(rt2x00dev->beacon_int);271 272 if (beacon_diff > beacon_int)273 beacon_diff = 0;274 275 autowake_timeout = (conf->ps_dtim_period * beacon_int) - beacon_diff;276 queue_delayed_work(rt2x00dev->workqueue,277 &rt2x00dev->autowakeup_work,278 autowake_timeout - 15);279 }280}281