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