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