1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 	Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
4 	<http://rt2x00.serialmonkey.com>
5 
6  */
7 
8 /*
9 	Module: rt2x00lib
10 	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 
19 void 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 send
47 	 * 00:00:00:00:00 to the device to clear the address.
48 	 * This will prevent the device being confused when it wants
49 	 * 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 
68 void 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 rt2x00lib_erp erp;
74 
75 	memset(&erp, 0, sizeof(erp));
76 
77 	erp.short_preamble = bss_conf->use_short_preamble;
78 	erp.cts_protection = bss_conf->use_cts_prot;
79 
80 	erp.slot_time = bss_conf->use_short_slot ? SHORT_SLOT_TIME : SLOT_TIME;
81 	erp.sifs = SIFS;
82 	erp.pifs = bss_conf->use_short_slot ? SHORT_PIFS : PIFS;
83 	erp.difs = bss_conf->use_short_slot ? SHORT_DIFS : DIFS;
84 	erp.eifs = bss_conf->use_short_slot ? SHORT_EIFS : EIFS;
85 
86 	erp.basic_rates = bss_conf->basic_rates;
87 	erp.beacon_int = bss_conf->beacon_int;
88 
89 	/* Update the AID, this is needed for dynamic PS support */
90 	rt2x00dev->aid = bss_conf->assoc ? bss_conf->aid : 0;
91 	rt2x00dev->last_beacon = bss_conf->sync_tsf;
92 
93 	/* Update global beacon interval time, this is needed for PS support */
94 	rt2x00dev->beacon_int = bss_conf->beacon_int;
95 
96 	if (changed & BSS_CHANGED_HT)
97 		erp.ht_opmode = bss_conf->ht_operation_mode;
98 
99 	rt2x00dev->ops->lib->config_erp(rt2x00dev, &erp, changed);
100 }
101 
102 void rt2x00lib_config_antenna(struct rt2x00_dev *rt2x00dev,
103 			      struct antenna_setup config)
104 {
105 	struct link_ant *ant = &rt2x00dev->link.ant;
106 	struct antenna_setup *def = &rt2x00dev->default_ant;
107 	struct antenna_setup *active = &rt2x00dev->link.ant.active;
108 
109 	/*
110 	 * When the caller tries to send the SW diversity,
111 	 * we must update the ANTENNA_RX_DIVERSITY flag to
112 	 * enable the antenna diversity in the link tuner.
113 	 *
114 	 * Secondly, we must guarentee we never send the
115 	 * software antenna diversity command to the driver.
116 	 */
117 	if (!(ant->flags & ANTENNA_RX_DIVERSITY)) {
118 		if (config.rx == ANTENNA_SW_DIVERSITY) {
119 			ant->flags |= ANTENNA_RX_DIVERSITY;
120 
121 			if (def->rx == ANTENNA_SW_DIVERSITY)
122 				config.rx = ANTENNA_B;
123 			else
124 				config.rx = def->rx;
125 		}
126 	} else if (config.rx == ANTENNA_SW_DIVERSITY)
127 		config.rx = active->rx;
128 
129 	if (!(ant->flags & ANTENNA_TX_DIVERSITY)) {
130 		if (config.tx == ANTENNA_SW_DIVERSITY) {
131 			ant->flags |= ANTENNA_TX_DIVERSITY;
132 
133 			if (def->tx == ANTENNA_SW_DIVERSITY)
134 				config.tx = ANTENNA_B;
135 			else
136 				config.tx = def->tx;
137 		}
138 	} else if (config.tx == ANTENNA_SW_DIVERSITY)
139 		config.tx = active->tx;
140 
141 	/*
142 	 * Antenna setup changes require the RX to be disabled,
143 	 * else the changes will be ignored by the device.
144 	 */
145 	if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
146 		rt2x00queue_stop_queue(rt2x00dev->rx);
147 
148 	/*
149 	 * Write new antenna setup to device and reset the link tuner.
150 	 * The latter is required since we need to recalibrate the
151 	 * noise-sensitivity ratio for the new setup.
152 	 */
153 	rt2x00dev->ops->lib->config_ant(rt2x00dev, &config);
154 
155 	rt2x00link_reset_tuner(rt2x00dev, true);
156 
157 	memcpy(active, &config, sizeof(config));
158 
159 	if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags))
160 		rt2x00queue_start_queue(rt2x00dev->rx);
161 }
162 
163 static u16 rt2x00ht_center_channel(struct rt2x00_dev *rt2x00dev,
164 				   struct ieee80211_conf *conf)
165 {
166 	struct hw_mode_spec *spec = &rt2x00dev->spec;
167 	int center_channel;
168 	u16 i;
169 
170 	/*
171 	 * Initialize center channel to current channel.
172 	 */
173 	center_channel = spec->channels[conf->chandef.chan->hw_value].channel;
174 
175 	/*
176 	 * Adjust center channel to HT40+ and HT40- operation.
177 	 */
178 	if (conf_is_ht40_plus(conf))
179 		center_channel += 2;
180 	else if (conf_is_ht40_minus(conf))
181 		center_channel -= (center_channel == 14) ? 1 : 2;
182 
183 	for (i = 0; i < spec->num_channels; i++)
184 		if (spec->channels[i].channel == center_channel)
185 			return i;
186 
187 	WARN_ON(1);
188 	return conf->chandef.chan->hw_value;
189 }
190 
191 void rt2x00lib_config(struct rt2x00_dev *rt2x00dev,
192 		      struct ieee80211_conf *conf,
193 		      unsigned int ieee80211_flags)
194 {
195 	struct rt2x00lib_conf libconf;
196 	u16 hw_value;
197 	u16 autowake_timeout;
198 	u16 beacon_int;
199 	u16 beacon_diff;
200 
201 	memset(&libconf, 0, sizeof(libconf));
202 
203 	libconf.conf = conf;
204 
205 	if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) {
206 		if (!conf_is_ht(conf))
207 			set_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags);
208 		else
209 			clear_bit(CONFIG_HT_DISABLED, &rt2x00dev->flags);
210 
211 		if (conf_is_ht40(conf)) {
212 			set_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags);
213 			hw_value = rt2x00ht_center_channel(rt2x00dev, conf);
214 		} else {
215 			clear_bit(CONFIG_CHANNEL_HT40, &rt2x00dev->flags);
216 			hw_value = conf->chandef.chan->hw_value;
217 		}
218 
219 		memcpy(&libconf.rf,
220 		       &rt2x00dev->spec.channels[hw_value],
221 		       sizeof(libconf.rf));
222 
223 		memcpy(&libconf.channel,
224 		       &rt2x00dev->spec.channels_info[hw_value],
225 		       sizeof(libconf.channel));
226 
227 		/* Used for VCO periodic calibration */
228 		rt2x00dev->rf_channel = libconf.rf.channel;
229 	}
230 
231 	if (rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) &&
232 	    (ieee80211_flags & IEEE80211_CONF_CHANGE_PS))
233 		cancel_delayed_work_sync(&rt2x00dev->autowakeup_work);
234 
235 	/*
236 	 * Start configuration.
237 	 */
238 	rt2x00dev->ops->lib->config(rt2x00dev, &libconf, ieee80211_flags);
239 
240 	if (conf->flags & IEEE80211_CONF_PS)
241 		set_bit(CONFIG_POWERSAVING, &rt2x00dev->flags);
242 	else
243 		clear_bit(CONFIG_POWERSAVING, &rt2x00dev->flags);
244 
245 	if (conf->flags & IEEE80211_CONF_MONITOR)
246 		set_bit(CONFIG_MONITORING, &rt2x00dev->flags);
247 	else
248 		clear_bit(CONFIG_MONITORING, &rt2x00dev->flags);
249 
250 	rt2x00dev->curr_band = conf->chandef.chan->band;
251 	rt2x00dev->curr_freq = conf->chandef.chan->center_freq;
252 	rt2x00dev->tx_power = conf->power_level;
253 	rt2x00dev->short_retry = conf->short_frame_max_tx_count;
254 	rt2x00dev->long_retry = conf->long_frame_max_tx_count;
255 
256 	/*
257 	 * Some configuration changes affect the link quality
258 	 * which means we need to reset the link tuner.
259 	 */
260 	if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL)
261 		rt2x00link_reset_tuner(rt2x00dev, false);
262 
263 	if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) &&
264 	    rt2x00_has_cap_flag(rt2x00dev, REQUIRE_PS_AUTOWAKE) &&
265 	    (ieee80211_flags & IEEE80211_CONF_CHANGE_PS) &&
266 	    (conf->flags & IEEE80211_CONF_PS)) {
267 		beacon_diff = (long)jiffies - (long)rt2x00dev->last_beacon;
268 		beacon_int = msecs_to_jiffies(rt2x00dev->beacon_int);
269 
270 		if (beacon_diff > beacon_int)
271 			beacon_diff = 0;
272 
273 		autowake_timeout = (conf->ps_dtim_period * beacon_int) - beacon_diff;
274 		queue_delayed_work(rt2x00dev->workqueue,
275 				   &rt2x00dev->autowakeup_work,
276 				   autowake_timeout - 15);
277 	}
278 }
279