]>
Commit | Line | Data |
---|---|---|
1802d0be | 1 | // SPDX-License-Identifier: GPL-2.0-only |
c869f77d JK |
2 | /* |
3 | * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org> | |
4 | * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl> | |
c869f77d JK |
5 | */ |
6 | ||
7 | #include "mt7601u.h" | |
8 | #include "mac.h" | |
9 | #include <linux/etherdevice.h> | |
c869f77d JK |
10 | |
11 | static int mt7601u_start(struct ieee80211_hw *hw) | |
12 | { | |
13 | struct mt7601u_dev *dev = hw->priv; | |
14 | int ret; | |
15 | ||
16 | mutex_lock(&dev->mutex); | |
17 | ||
18 | ret = mt7601u_mac_start(dev); | |
19 | if (ret) | |
20 | goto out; | |
21 | ||
22 | ieee80211_queue_delayed_work(dev->hw, &dev->mac_work, | |
23 | MT_CALIBRATE_INTERVAL); | |
24 | ieee80211_queue_delayed_work(dev->hw, &dev->cal_work, | |
25 | MT_CALIBRATE_INTERVAL); | |
26 | out: | |
27 | mutex_unlock(&dev->mutex); | |
28 | return ret; | |
29 | } | |
30 | ||
31 | static void mt7601u_stop(struct ieee80211_hw *hw) | |
32 | { | |
33 | struct mt7601u_dev *dev = hw->priv; | |
34 | ||
35 | mutex_lock(&dev->mutex); | |
36 | ||
37 | cancel_delayed_work_sync(&dev->cal_work); | |
38 | cancel_delayed_work_sync(&dev->mac_work); | |
39 | mt7601u_mac_stop(dev); | |
40 | ||
41 | mutex_unlock(&dev->mutex); | |
42 | } | |
43 | ||
44 | static int mt7601u_add_interface(struct ieee80211_hw *hw, | |
45 | struct ieee80211_vif *vif) | |
46 | { | |
47 | struct mt7601u_dev *dev = hw->priv; | |
48 | struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv; | |
49 | unsigned int idx = 0; | |
50 | unsigned int wcid = GROUP_WCID(idx); | |
51 | ||
52 | /* Note: for AP do the AP-STA things mt76 does: | |
53 | * - beacon offsets | |
54 | * - do mac address tricks | |
55 | * - shift vif idx | |
56 | */ | |
57 | mvif->idx = idx; | |
58 | ||
032a552e LB |
59 | if (!ether_addr_equal(dev->macaddr, vif->addr)) |
60 | mt7601u_set_macaddr(dev, vif->addr); | |
61 | ||
c869f77d JK |
62 | if (dev->wcid_mask[wcid / BITS_PER_LONG] & BIT(wcid % BITS_PER_LONG)) |
63 | return -ENOSPC; | |
64 | dev->wcid_mask[wcid / BITS_PER_LONG] |= BIT(wcid % BITS_PER_LONG); | |
65 | mvif->group_wcid.idx = wcid; | |
66 | mvif->group_wcid.hw_key_idx = -1; | |
67 | ||
68 | return 0; | |
69 | } | |
70 | ||
71 | static void mt7601u_remove_interface(struct ieee80211_hw *hw, | |
72 | struct ieee80211_vif *vif) | |
73 | { | |
74 | struct mt7601u_dev *dev = hw->priv; | |
75 | struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv; | |
76 | unsigned int wcid = mvif->group_wcid.idx; | |
77 | ||
78 | dev->wcid_mask[wcid / BITS_PER_LONG] &= ~BIT(wcid % BITS_PER_LONG); | |
79 | } | |
80 | ||
81 | static int mt7601u_config(struct ieee80211_hw *hw, u32 changed) | |
82 | { | |
83 | struct mt7601u_dev *dev = hw->priv; | |
84 | int ret = 0; | |
85 | ||
86 | mutex_lock(&dev->mutex); | |
87 | ||
88 | if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { | |
89 | ieee80211_stop_queues(hw); | |
90 | ret = mt7601u_phy_set_channel(dev, &hw->conf.chandef); | |
91 | ieee80211_wake_queues(hw); | |
92 | } | |
93 | ||
94 | mutex_unlock(&dev->mutex); | |
95 | ||
96 | return ret; | |
97 | } | |
98 | ||
99 | static void | |
100 | mt76_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, | |
101 | unsigned int *total_flags, u64 multicast) | |
102 | { | |
103 | struct mt7601u_dev *dev = hw->priv; | |
104 | u32 flags = 0; | |
105 | ||
106 | #define MT76_FILTER(_flag, _hw) do { \ | |
107 | flags |= *total_flags & FIF_##_flag; \ | |
108 | dev->rxfilter &= ~(_hw); \ | |
109 | dev->rxfilter |= !(flags & FIF_##_flag) * (_hw); \ | |
110 | } while (0) | |
111 | ||
112 | mutex_lock(&dev->mutex); | |
113 | ||
114 | dev->rxfilter &= ~MT_RX_FILTR_CFG_OTHER_BSS; | |
115 | ||
2dea58f6 | 116 | MT76_FILTER(OTHER_BSS, MT_RX_FILTR_CFG_PROMISC); |
c869f77d JK |
117 | MT76_FILTER(FCSFAIL, MT_RX_FILTR_CFG_CRC_ERR); |
118 | MT76_FILTER(PLCPFAIL, MT_RX_FILTR_CFG_PHY_ERR); | |
119 | MT76_FILTER(CONTROL, MT_RX_FILTR_CFG_ACK | | |
120 | MT_RX_FILTR_CFG_CTS | | |
121 | MT_RX_FILTR_CFG_CFEND | | |
122 | MT_RX_FILTR_CFG_CFACK | | |
123 | MT_RX_FILTR_CFG_BA | | |
124 | MT_RX_FILTR_CFG_CTRL_RSV); | |
125 | MT76_FILTER(PSPOLL, MT_RX_FILTR_CFG_PSPOLL); | |
126 | ||
127 | *total_flags = flags; | |
128 | mt76_wr(dev, MT_RX_FILTR_CFG, dev->rxfilter); | |
129 | ||
130 | mutex_unlock(&dev->mutex); | |
131 | } | |
132 | ||
133 | static void | |
134 | mt7601u_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
135 | struct ieee80211_bss_conf *info, u32 changed) | |
136 | { | |
137 | struct mt7601u_dev *dev = hw->priv; | |
138 | ||
139 | mutex_lock(&dev->mutex); | |
140 | ||
141 | if (changed & BSS_CHANGED_ASSOC) | |
142 | mt7601u_phy_con_cal_onoff(dev, info); | |
143 | ||
144 | if (changed & BSS_CHANGED_BSSID) { | |
145 | mt7601u_addr_wr(dev, MT_MAC_BSSID_DW0, info->bssid); | |
146 | ||
147 | /* Note: this is a hack because beacon_int is not changed | |
148 | * on leave nor is any more appropriate event generated. | |
149 | * rt2x00 doesn't seem to be bothered though. | |
150 | */ | |
151 | if (is_zero_ether_addr(info->bssid)) | |
152 | mt7601u_mac_config_tsf(dev, false, 0); | |
153 | } | |
154 | ||
155 | if (changed & BSS_CHANGED_BASIC_RATES) { | |
156 | mt7601u_wr(dev, MT_LEGACY_BASIC_RATE, info->basic_rates); | |
157 | mt7601u_wr(dev, MT_HT_FBK_CFG0, 0x65432100); | |
158 | mt7601u_wr(dev, MT_HT_FBK_CFG1, 0xedcba980); | |
159 | mt7601u_wr(dev, MT_LG_FBK_CFG0, 0xedcba988); | |
160 | mt7601u_wr(dev, MT_LG_FBK_CFG1, 0x00002100); | |
161 | } | |
162 | ||
163 | if (changed & BSS_CHANGED_BEACON_INT) | |
164 | mt7601u_mac_config_tsf(dev, true, info->beacon_int); | |
165 | ||
166 | if (changed & BSS_CHANGED_HT || changed & BSS_CHANGED_ERP_CTS_PROT) | |
167 | mt7601u_mac_set_protection(dev, info->use_cts_prot, | |
168 | info->ht_operation_mode); | |
169 | ||
170 | if (changed & BSS_CHANGED_ERP_PREAMBLE) | |
171 | mt7601u_mac_set_short_preamble(dev, info->use_short_preamble); | |
172 | ||
173 | if (changed & BSS_CHANGED_ERP_SLOT) { | |
174 | int slottime = info->use_short_slot ? 9 : 20; | |
175 | ||
176 | mt76_rmw_field(dev, MT_BKOFF_SLOT_CFG, | |
177 | MT_BKOFF_SLOT_CFG_SLOTTIME, slottime); | |
178 | } | |
179 | ||
180 | if (changed & BSS_CHANGED_ASSOC) | |
181 | mt7601u_phy_recalibrate_after_assoc(dev); | |
182 | ||
183 | mutex_unlock(&dev->mutex); | |
184 | } | |
185 | ||
186 | static int | |
187 | mt76_wcid_alloc(struct mt7601u_dev *dev) | |
188 | { | |
189 | int i, idx = 0; | |
190 | ||
191 | for (i = 0; i < ARRAY_SIZE(dev->wcid_mask); i++) { | |
192 | idx = ffs(~dev->wcid_mask[i]); | |
193 | if (!idx) | |
194 | continue; | |
195 | ||
196 | idx--; | |
197 | dev->wcid_mask[i] |= BIT(idx); | |
198 | break; | |
199 | } | |
200 | ||
201 | idx = i * BITS_PER_LONG + idx; | |
202 | if (idx > 119) | |
203 | return -1; | |
204 | ||
205 | return idx; | |
206 | } | |
207 | ||
208 | static int | |
209 | mt7601u_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
210 | struct ieee80211_sta *sta) | |
211 | { | |
212 | struct mt7601u_dev *dev = hw->priv; | |
213 | struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; | |
214 | struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv; | |
215 | int ret = 0; | |
216 | int idx = 0; | |
217 | ||
218 | mutex_lock(&dev->mutex); | |
219 | ||
220 | idx = mt76_wcid_alloc(dev); | |
221 | if (idx < 0) { | |
222 | ret = -ENOSPC; | |
223 | goto out; | |
224 | } | |
225 | ||
226 | msta->wcid.idx = idx; | |
227 | msta->wcid.hw_key_idx = -1; | |
228 | mt7601u_mac_wcid_setup(dev, idx, mvif->idx, sta->addr); | |
229 | mt76_clear(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx)); | |
230 | rcu_assign_pointer(dev->wcid[idx], &msta->wcid); | |
231 | mt7601u_mac_set_ampdu_factor(dev); | |
232 | ||
233 | out: | |
234 | mutex_unlock(&dev->mutex); | |
235 | ||
236 | return ret; | |
237 | } | |
238 | ||
239 | static int | |
240 | mt7601u_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
241 | struct ieee80211_sta *sta) | |
242 | { | |
243 | struct mt7601u_dev *dev = hw->priv; | |
244 | struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; | |
245 | int idx = msta->wcid.idx; | |
246 | ||
247 | mutex_lock(&dev->mutex); | |
248 | rcu_assign_pointer(dev->wcid[idx], NULL); | |
249 | mt76_set(dev, MT_WCID_DROP(idx), MT_WCID_DROP_MASK(idx)); | |
250 | dev->wcid_mask[idx / BITS_PER_LONG] &= ~BIT(idx % BITS_PER_LONG); | |
251 | mt7601u_mac_wcid_setup(dev, idx, 0, NULL); | |
252 | mt7601u_mac_set_ampdu_factor(dev); | |
253 | mutex_unlock(&dev->mutex); | |
254 | ||
255 | return 0; | |
256 | } | |
257 | ||
258 | static void | |
259 | mt7601u_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
260 | enum sta_notify_cmd cmd, struct ieee80211_sta *sta) | |
261 | { | |
262 | } | |
263 | ||
264 | static void | |
265 | mt7601u_sw_scan(struct ieee80211_hw *hw, | |
266 | struct ieee80211_vif *vif, | |
267 | const u8 *mac_addr) | |
268 | { | |
269 | struct mt7601u_dev *dev = hw->priv; | |
270 | ||
271 | mt7601u_agc_save(dev); | |
272 | set_bit(MT7601U_STATE_SCANNING, &dev->state); | |
273 | } | |
274 | ||
275 | static void | |
276 | mt7601u_sw_scan_complete(struct ieee80211_hw *hw, | |
277 | struct ieee80211_vif *vif) | |
278 | { | |
279 | struct mt7601u_dev *dev = hw->priv; | |
280 | ||
281 | mt7601u_agc_restore(dev); | |
282 | clear_bit(MT7601U_STATE_SCANNING, &dev->state); | |
2019c39a SG |
283 | |
284 | ieee80211_queue_delayed_work(dev->hw, &dev->cal_work, | |
285 | MT_CALIBRATE_INTERVAL); | |
286 | if (dev->freq_cal.enabled) | |
287 | ieee80211_queue_delayed_work(dev->hw, &dev->freq_cal.work, | |
288 | MT_FREQ_CAL_INIT_DELAY); | |
c869f77d JK |
289 | } |
290 | ||
291 | static int | |
292 | mt7601u_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, | |
293 | struct ieee80211_vif *vif, struct ieee80211_sta *sta, | |
294 | struct ieee80211_key_conf *key) | |
295 | { | |
296 | struct mt7601u_dev *dev = hw->priv; | |
297 | struct mt76_vif *mvif = (struct mt76_vif *) vif->drv_priv; | |
298 | struct mt76_sta *msta = sta ? (struct mt76_sta *) sta->drv_priv : NULL; | |
299 | struct mt76_wcid *wcid = msta ? &msta->wcid : &mvif->group_wcid; | |
300 | int idx = key->keyidx; | |
301 | int ret; | |
302 | ||
03d677c5 LB |
303 | /* fall back to sw encryption for unsupported ciphers */ |
304 | switch (key->cipher) { | |
305 | case WLAN_CIPHER_SUITE_WEP40: | |
306 | case WLAN_CIPHER_SUITE_WEP104: | |
307 | case WLAN_CIPHER_SUITE_TKIP: | |
308 | case WLAN_CIPHER_SUITE_CCMP: | |
309 | break; | |
310 | default: | |
311 | return -EOPNOTSUPP; | |
312 | } | |
313 | ||
c869f77d JK |
314 | if (cmd == SET_KEY) { |
315 | key->hw_key_idx = wcid->idx; | |
316 | wcid->hw_key_idx = idx; | |
317 | } else { | |
318 | if (idx == wcid->hw_key_idx) | |
319 | wcid->hw_key_idx = -1; | |
320 | ||
321 | key = NULL; | |
322 | } | |
323 | ||
324 | if (!msta) { | |
325 | if (key || wcid->hw_key_idx == idx) { | |
326 | ret = mt76_mac_wcid_set_key(dev, wcid->idx, key); | |
327 | if (ret) | |
328 | return ret; | |
329 | } | |
330 | ||
331 | return mt76_mac_shared_key_setup(dev, mvif->idx, idx, key); | |
332 | } | |
333 | ||
334 | return mt76_mac_wcid_set_key(dev, msta->wcid.idx, key); | |
335 | } | |
336 | ||
337 | static int mt7601u_set_rts_threshold(struct ieee80211_hw *hw, u32 value) | |
338 | { | |
339 | struct mt7601u_dev *dev = hw->priv; | |
340 | ||
341 | mt76_rmw_field(dev, MT_TX_RTS_CFG, MT_TX_RTS_CFG_THRESH, value); | |
342 | ||
343 | return 0; | |
344 | } | |
345 | ||
346 | static int | |
347 | mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
50ea05ef | 348 | struct ieee80211_ampdu_params *params) |
c869f77d JK |
349 | { |
350 | struct mt7601u_dev *dev = hw->priv; | |
50ea05ef SS |
351 | struct ieee80211_sta *sta = params->sta; |
352 | enum ieee80211_ampdu_mlme_action action = params->action; | |
353 | u16 tid = params->tid; | |
f0248ec4 | 354 | u16 ssn = params->ssn; |
c869f77d JK |
355 | struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; |
356 | ||
357 | WARN_ON(msta->wcid.idx > GROUP_WCID(0)); | |
358 | ||
359 | switch (action) { | |
360 | case IEEE80211_AMPDU_RX_START: | |
361 | mt76_set(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, BIT(16 + tid)); | |
362 | break; | |
363 | case IEEE80211_AMPDU_RX_STOP: | |
364 | mt76_clear(dev, MT_WCID_ADDR(msta->wcid.idx) + 4, | |
365 | BIT(16 + tid)); | |
366 | break; | |
367 | case IEEE80211_AMPDU_TX_OPERATIONAL: | |
368 | ieee80211_send_bar(vif, sta->addr, tid, msta->agg_ssn[tid]); | |
369 | break; | |
370 | case IEEE80211_AMPDU_TX_STOP_FLUSH: | |
371 | case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: | |
372 | break; | |
373 | case IEEE80211_AMPDU_TX_START: | |
f0248ec4 | 374 | msta->agg_ssn[tid] = ssn << 4; |
2ce113de | 375 | return IEEE80211_AMPDU_TX_START_IMMEDIATE; |
c869f77d JK |
376 | case IEEE80211_AMPDU_TX_STOP_CONT: |
377 | ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); | |
378 | break; | |
379 | } | |
380 | ||
381 | return 0; | |
382 | } | |
383 | ||
384 | static void | |
385 | mt76_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif, | |
386 | struct ieee80211_sta *sta) | |
387 | { | |
388 | struct mt7601u_dev *dev = hw->priv; | |
389 | struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv; | |
390 | struct ieee80211_sta_rates *rates; | |
391 | struct ieee80211_tx_rate rate = {}; | |
392 | ||
393 | rcu_read_lock(); | |
394 | rates = rcu_dereference(sta->rates); | |
395 | ||
396 | if (!rates) | |
397 | goto out; | |
398 | ||
399 | rate.idx = rates->rate[0].idx; | |
400 | rate.flags = rates->rate[0].flags; | |
401 | mt76_mac_wcid_set_rate(dev, &msta->wcid, &rate); | |
402 | ||
403 | out: | |
404 | rcu_read_unlock(); | |
405 | } | |
406 | ||
407 | const struct ieee80211_ops mt7601u_ops = { | |
408 | .tx = mt7601u_tx, | |
409 | .start = mt7601u_start, | |
410 | .stop = mt7601u_stop, | |
411 | .add_interface = mt7601u_add_interface, | |
412 | .remove_interface = mt7601u_remove_interface, | |
413 | .config = mt7601u_config, | |
414 | .configure_filter = mt76_configure_filter, | |
415 | .bss_info_changed = mt7601u_bss_info_changed, | |
416 | .sta_add = mt7601u_sta_add, | |
417 | .sta_remove = mt7601u_sta_remove, | |
418 | .sta_notify = mt7601u_sta_notify, | |
419 | .set_key = mt7601u_set_key, | |
420 | .conf_tx = mt7601u_conf_tx, | |
421 | .sw_scan_start = mt7601u_sw_scan, | |
422 | .sw_scan_complete = mt7601u_sw_scan_complete, | |
423 | .ampdu_action = mt76_ampdu_action, | |
424 | .sta_rate_tbl_update = mt76_sta_rate_tbl_update, | |
425 | .set_rts_threshold = mt7601u_set_rts_threshold, | |
426 | }; |