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 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 | |
104 | void 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 to |
114 | * enable the antenna diversity in the link tuner. |
115 | * |
116 | * Secondly, we must guarentee we never send the |
117 | * 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 | else |
126 | 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 | else |
138 | 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(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 the |
153 | * noise-sensitivity ratio for the new setup. |
154 | */ |
155 | rt2x00dev->ops->lib->config_ant(rt2x00dev, &config); |
156 | |
157 | rt2x00link_reset_tuner(rt2x00dev, antenna: true); |
158 | |
159 | memcpy(active, &config, sizeof(config)); |
160 | |
161 | if (test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) |
162 | rt2x00queue_start_queue(queue: rt2x00dev->rx); |
163 | } |
164 | |
165 | static 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 | |
193 | void 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(nr: CONFIG_HT_DISABLED, addr: &rt2x00dev->flags); |
210 | else |
211 | clear_bit(nr: CONFIG_HT_DISABLED, addr: &rt2x00dev->flags); |
212 | |
213 | if (conf_is_ht40(conf)) { |
214 | set_bit(nr: CONFIG_CHANNEL_HT40, addr: &rt2x00dev->flags); |
215 | hw_value = rt2x00ht_center_channel(rt2x00dev, conf); |
216 | } else { |
217 | clear_bit(nr: CONFIG_CHANNEL_HT40, addr: &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, cap_flag: REQUIRE_PS_AUTOWAKE) && |
234 | (ieee80211_flags & IEEE80211_CONF_CHANGE_PS)) |
235 | cancel_delayed_work_sync(dwork: &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(nr: CONFIG_POWERSAVING, addr: &rt2x00dev->flags); |
244 | else |
245 | clear_bit(nr: CONFIG_POWERSAVING, addr: &rt2x00dev->flags); |
246 | |
247 | if (conf->flags & IEEE80211_CONF_MONITOR) |
248 | set_bit(nr: CONFIG_MONITORING, addr: &rt2x00dev->flags); |
249 | else |
250 | clear_bit(nr: CONFIG_MONITORING, addr: &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 quality |
260 | * which means we need to reset the link tuner. |
261 | */ |
262 | if (ieee80211_flags & IEEE80211_CONF_CHANGE_CHANNEL) |
263 | rt2x00link_reset_tuner(rt2x00dev, antenna: false); |
264 | |
265 | if (test_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags) && |
266 | rt2x00_has_cap_flag(rt2x00dev, cap_flag: 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(m: 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(wq: rt2x00dev->workqueue, |
277 | dwork: &rt2x00dev->autowakeup_work, |
278 | delay: autowake_timeout - 15); |
279 | } |
280 | } |
281 | |