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 "trace.h" | |
17 | #include <linux/etherdevice.h> | |
18 | ||
19 | static void | |
20 | mt76_mac_process_tx_rate(struct ieee80211_tx_rate *txrate, u16 rate) | |
21 | { | |
d43af505 | 22 | u8 idx = FIELD_GET(MT_TXWI_RATE_MCS, rate); |
c869f77d JK |
23 | |
24 | txrate->idx = 0; | |
25 | txrate->flags = 0; | |
26 | txrate->count = 1; | |
27 | ||
d43af505 | 28 | switch (FIELD_GET(MT_TXWI_RATE_PHY_MODE, rate)) { |
c869f77d JK |
29 | case MT_PHY_TYPE_OFDM: |
30 | txrate->idx = idx + 4; | |
31 | return; | |
32 | case MT_PHY_TYPE_CCK: | |
33 | if (idx >= 8) | |
34 | idx -= 8; | |
35 | ||
36 | txrate->idx = idx; | |
37 | return; | |
38 | case MT_PHY_TYPE_HT_GF: | |
39 | txrate->flags |= IEEE80211_TX_RC_GREEN_FIELD; | |
40 | /* fall through */ | |
41 | case MT_PHY_TYPE_HT: | |
42 | txrate->flags |= IEEE80211_TX_RC_MCS; | |
43 | txrate->idx = idx; | |
44 | break; | |
45 | default: | |
46 | WARN_ON(1); | |
47 | return; | |
48 | } | |
49 | ||
d43af505 | 50 | if (FIELD_GET(MT_TXWI_RATE_BW, rate) == MT_PHY_BW_40) |
c869f77d JK |
51 | txrate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; |
52 | ||
53 | if (rate & MT_TXWI_RATE_SGI) | |
54 | txrate->flags |= IEEE80211_TX_RC_SHORT_GI; | |
55 | } | |
56 | ||
57 | static void | |
58 | mt76_mac_fill_tx_status(struct mt7601u_dev *dev, struct ieee80211_tx_info *info, | |
59 | struct mt76_tx_status *st) | |
60 | { | |
61 | struct ieee80211_tx_rate *rate = info->status.rates; | |
62 | int cur_idx, last_rate; | |
63 | int i; | |
64 | ||
65 | last_rate = min_t(int, st->retry, IEEE80211_TX_MAX_RATES - 1); | |
66 | mt76_mac_process_tx_rate(&rate[last_rate], st->rate); | |
67 | if (last_rate < IEEE80211_TX_MAX_RATES - 1) | |
68 | rate[last_rate + 1].idx = -1; | |
69 | ||
70 | cur_idx = rate[last_rate].idx + st->retry; | |
71 | for (i = 0; i <= last_rate; i++) { | |
72 | rate[i].flags = rate[last_rate].flags; | |
73 | rate[i].idx = max_t(int, 0, cur_idx - i); | |
74 | rate[i].count = 1; | |
75 | } | |
76 | ||
77 | if (last_rate > 0) | |
78 | rate[last_rate - 1].count = st->retry + 1 - last_rate; | |
79 | ||
80 | info->status.ampdu_len = 1; | |
81 | info->status.ampdu_ack_len = st->success; | |
82 | ||
83 | if (st->is_probe) | |
84 | info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; | |
85 | ||
86 | if (st->aggr) | |
87 | info->flags |= IEEE80211_TX_CTL_AMPDU | | |
88 | IEEE80211_TX_STAT_AMPDU; | |
89 | ||
90 | if (!st->ack_req) | |
91 | info->flags |= IEEE80211_TX_CTL_NO_ACK; | |
92 | else if (st->success) | |
93 | info->flags |= IEEE80211_TX_STAT_ACK; | |
94 | } | |
95 | ||
96 | u16 mt76_mac_tx_rate_val(struct mt7601u_dev *dev, | |
97 | const struct ieee80211_tx_rate *rate, u8 *nss_val) | |
98 | { | |
99 | u16 rateval; | |
100 | u8 phy, rate_idx; | |
101 | u8 nss = 1; | |
102 | u8 bw = 0; | |
103 | ||
104 | if (rate->flags & IEEE80211_TX_RC_MCS) { | |
105 | rate_idx = rate->idx; | |
106 | nss = 1 + (rate->idx >> 3); | |
107 | phy = MT_PHY_TYPE_HT; | |
108 | if (rate->flags & IEEE80211_TX_RC_GREEN_FIELD) | |
109 | phy = MT_PHY_TYPE_HT_GF; | |
110 | if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) | |
111 | bw = 1; | |
112 | } else { | |
113 | const struct ieee80211_rate *r; | |
114 | int band = dev->chandef.chan->band; | |
115 | u16 val; | |
116 | ||
117 | r = &dev->hw->wiphy->bands[band]->bitrates[rate->idx]; | |
118 | if (rate->flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE) | |
119 | val = r->hw_value_short; | |
120 | else | |
121 | val = r->hw_value; | |
122 | ||
123 | phy = val >> 8; | |
124 | rate_idx = val & 0xff; | |
125 | bw = 0; | |
126 | } | |
127 | ||
d43af505 JK |
128 | rateval = FIELD_PREP(MT_RXWI_RATE_MCS, rate_idx); |
129 | rateval |= FIELD_PREP(MT_RXWI_RATE_PHY, phy); | |
130 | rateval |= FIELD_PREP(MT_RXWI_RATE_BW, bw); | |
c869f77d JK |
131 | if (rate->flags & IEEE80211_TX_RC_SHORT_GI) |
132 | rateval |= MT_RXWI_RATE_SGI; | |
133 | ||
134 | *nss_val = nss; | |
135 | return rateval; | |
136 | } | |
137 | ||
138 | void mt76_mac_wcid_set_rate(struct mt7601u_dev *dev, struct mt76_wcid *wcid, | |
139 | const struct ieee80211_tx_rate *rate) | |
140 | { | |
141 | unsigned long flags; | |
142 | ||
143 | spin_lock_irqsave(&dev->lock, flags); | |
144 | wcid->tx_rate = mt76_mac_tx_rate_val(dev, rate, &wcid->tx_rate_nss); | |
145 | wcid->tx_rate_set = true; | |
146 | spin_unlock_irqrestore(&dev->lock, flags); | |
147 | } | |
148 | ||
149 | struct mt76_tx_status mt7601u_mac_fetch_tx_status(struct mt7601u_dev *dev) | |
150 | { | |
151 | struct mt76_tx_status stat = {}; | |
152 | u32 val; | |
153 | ||
154 | val = mt7601u_rr(dev, MT_TX_STAT_FIFO); | |
155 | stat.valid = !!(val & MT_TX_STAT_FIFO_VALID); | |
156 | stat.success = !!(val & MT_TX_STAT_FIFO_SUCCESS); | |
157 | stat.aggr = !!(val & MT_TX_STAT_FIFO_AGGR); | |
158 | stat.ack_req = !!(val & MT_TX_STAT_FIFO_ACKREQ); | |
d43af505 JK |
159 | stat.pktid = FIELD_GET(MT_TX_STAT_FIFO_PID_TYPE, val); |
160 | stat.wcid = FIELD_GET(MT_TX_STAT_FIFO_WCID, val); | |
161 | stat.rate = FIELD_GET(MT_TX_STAT_FIFO_RATE, val); | |
c869f77d JK |
162 | |
163 | return stat; | |
164 | } | |
165 | ||
166 | void mt76_send_tx_status(struct mt7601u_dev *dev, struct mt76_tx_status *stat) | |
167 | { | |
168 | struct ieee80211_tx_info info = {}; | |
169 | struct ieee80211_sta *sta = NULL; | |
170 | struct mt76_wcid *wcid = NULL; | |
171 | void *msta; | |
172 | ||
173 | rcu_read_lock(); | |
174 | if (stat->wcid < ARRAY_SIZE(dev->wcid)) | |
175 | wcid = rcu_dereference(dev->wcid[stat->wcid]); | |
176 | ||
177 | if (wcid) { | |
178 | msta = container_of(wcid, struct mt76_sta, wcid); | |
179 | sta = container_of(msta, struct ieee80211_sta, | |
180 | drv_priv); | |
181 | } | |
182 | ||
183 | mt76_mac_fill_tx_status(dev, &info, stat); | |
4513493d | 184 | |
78623bfb | 185 | spin_lock_bh(&dev->mac_lock); |
c869f77d | 186 | ieee80211_tx_status_noskb(dev->hw, sta, &info); |
78623bfb | 187 | spin_unlock_bh(&dev->mac_lock); |
4513493d | 188 | |
c869f77d JK |
189 | rcu_read_unlock(); |
190 | } | |
191 | ||
192 | void mt7601u_mac_set_protection(struct mt7601u_dev *dev, bool legacy_prot, | |
193 | int ht_mode) | |
194 | { | |
195 | int mode = ht_mode & IEEE80211_HT_OP_MODE_PROTECTION; | |
196 | bool non_gf = !!(ht_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); | |
197 | u32 prot[6]; | |
198 | bool ht_rts[4] = {}; | |
199 | int i; | |
200 | ||
201 | prot[0] = MT_PROT_NAV_SHORT | | |
202 | MT_PROT_TXOP_ALLOW_ALL | | |
203 | MT_PROT_RTS_THR_EN; | |
204 | prot[1] = prot[0]; | |
205 | if (legacy_prot) | |
206 | prot[1] |= MT_PROT_CTRL_CTS2SELF; | |
207 | ||
208 | prot[2] = prot[4] = MT_PROT_NAV_SHORT | MT_PROT_TXOP_ALLOW_BW20; | |
209 | prot[3] = prot[5] = MT_PROT_NAV_SHORT | MT_PROT_TXOP_ALLOW_ALL; | |
210 | ||
211 | if (legacy_prot) { | |
212 | prot[2] |= MT_PROT_RATE_CCK_11; | |
213 | prot[3] |= MT_PROT_RATE_CCK_11; | |
214 | prot[4] |= MT_PROT_RATE_CCK_11; | |
215 | prot[5] |= MT_PROT_RATE_CCK_11; | |
216 | } else { | |
217 | prot[2] |= MT_PROT_RATE_OFDM_24; | |
218 | prot[3] |= MT_PROT_RATE_DUP_OFDM_24; | |
219 | prot[4] |= MT_PROT_RATE_OFDM_24; | |
220 | prot[5] |= MT_PROT_RATE_DUP_OFDM_24; | |
221 | } | |
222 | ||
223 | switch (mode) { | |
224 | case IEEE80211_HT_OP_MODE_PROTECTION_NONE: | |
225 | break; | |
226 | ||
227 | case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: | |
228 | ht_rts[0] = ht_rts[1] = ht_rts[2] = ht_rts[3] = true; | |
229 | break; | |
230 | ||
231 | case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: | |
232 | ht_rts[1] = ht_rts[3] = true; | |
233 | break; | |
234 | ||
235 | case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: | |
236 | ht_rts[0] = ht_rts[1] = ht_rts[2] = ht_rts[3] = true; | |
237 | break; | |
238 | } | |
239 | ||
240 | if (non_gf) | |
241 | ht_rts[2] = ht_rts[3] = true; | |
242 | ||
243 | for (i = 0; i < 4; i++) | |
244 | if (ht_rts[i]) | |
245 | prot[i + 2] |= MT_PROT_CTRL_RTS_CTS; | |
246 | ||
247 | for (i = 0; i < 6; i++) | |
248 | mt7601u_wr(dev, MT_CCK_PROT_CFG + i * 4, prot[i]); | |
249 | } | |
250 | ||
251 | void mt7601u_mac_set_short_preamble(struct mt7601u_dev *dev, bool short_preamb) | |
252 | { | |
253 | if (short_preamb) | |
254 | mt76_set(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT); | |
255 | else | |
256 | mt76_clear(dev, MT_AUTO_RSP_CFG, MT_AUTO_RSP_PREAMB_SHORT); | |
257 | } | |
258 | ||
259 | void mt7601u_mac_config_tsf(struct mt7601u_dev *dev, bool enable, int interval) | |
260 | { | |
261 | u32 val = mt7601u_rr(dev, MT_BEACON_TIME_CFG); | |
262 | ||
263 | val &= ~(MT_BEACON_TIME_CFG_TIMER_EN | | |
264 | MT_BEACON_TIME_CFG_SYNC_MODE | | |
265 | MT_BEACON_TIME_CFG_TBTT_EN); | |
266 | ||
267 | if (!enable) { | |
268 | mt7601u_wr(dev, MT_BEACON_TIME_CFG, val); | |
269 | return; | |
270 | } | |
271 | ||
272 | val &= ~MT_BEACON_TIME_CFG_INTVAL; | |
d43af505 | 273 | val |= FIELD_PREP(MT_BEACON_TIME_CFG_INTVAL, interval << 4) | |
c869f77d JK |
274 | MT_BEACON_TIME_CFG_TIMER_EN | |
275 | MT_BEACON_TIME_CFG_SYNC_MODE | | |
276 | MT_BEACON_TIME_CFG_TBTT_EN; | |
277 | } | |
278 | ||
279 | static void mt7601u_check_mac_err(struct mt7601u_dev *dev) | |
280 | { | |
281 | u32 val = mt7601u_rr(dev, 0x10f4); | |
282 | ||
283 | if (!(val & BIT(29)) || !(val & (BIT(7) | BIT(5)))) | |
284 | return; | |
285 | ||
286 | dev_err(dev->dev, "Error: MAC specific condition occurred\n"); | |
287 | ||
288 | mt76_set(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_RESET_CSR); | |
289 | udelay(10); | |
290 | mt76_clear(dev, MT_MAC_SYS_CTRL, MT_MAC_SYS_CTRL_RESET_CSR); | |
291 | } | |
292 | ||
293 | void mt7601u_mac_work(struct work_struct *work) | |
294 | { | |
295 | struct mt7601u_dev *dev = container_of(work, struct mt7601u_dev, | |
296 | mac_work.work); | |
297 | struct { | |
298 | u32 addr_base; | |
299 | u32 span; | |
300 | u64 *stat_base; | |
301 | } spans[] = { | |
302 | { MT_RX_STA_CNT0, 3, dev->stats.rx_stat }, | |
303 | { MT_TX_STA_CNT0, 3, dev->stats.tx_stat }, | |
304 | { MT_TX_AGG_STAT, 1, dev->stats.aggr_stat }, | |
305 | { MT_MPDU_DENSITY_CNT, 1, dev->stats.zero_len_del }, | |
306 | { MT_TX_AGG_CNT_BASE0, 8, &dev->stats.aggr_n[0] }, | |
307 | { MT_TX_AGG_CNT_BASE1, 8, &dev->stats.aggr_n[16] }, | |
308 | }; | |
309 | u32 sum, n; | |
310 | int i, j, k; | |
311 | ||
312 | /* Note: using MCU_RANDOM_READ is actually slower then reading all the | |
313 | * registers by hand. MCU takes ca. 20ms to complete read of 24 | |
314 | * registers while reading them one by one will takes roughly | |
315 | * 24*200us =~ 5ms. | |
316 | */ | |
317 | ||
318 | k = 0; | |
319 | n = 0; | |
320 | sum = 0; | |
321 | for (i = 0; i < ARRAY_SIZE(spans); i++) | |
322 | for (j = 0; j < spans[i].span; j++) { | |
323 | u32 val = mt7601u_rr(dev, spans[i].addr_base + j * 4); | |
324 | ||
325 | spans[i].stat_base[j * 2] += val & 0xffff; | |
326 | spans[i].stat_base[j * 2 + 1] += val >> 16; | |
327 | ||
328 | /* Calculate average AMPDU length */ | |
329 | if (spans[i].addr_base != MT_TX_AGG_CNT_BASE0 && | |
330 | spans[i].addr_base != MT_TX_AGG_CNT_BASE1) | |
331 | continue; | |
332 | ||
333 | n += (val >> 16) + (val & 0xffff); | |
334 | sum += (val & 0xffff) * (1 + k * 2) + | |
335 | (val >> 16) * (2 + k * 2); | |
336 | k++; | |
337 | } | |
338 | ||
339 | atomic_set(&dev->avg_ampdu_len, n ? DIV_ROUND_CLOSEST(sum, n) : 1); | |
340 | ||
341 | mt7601u_check_mac_err(dev); | |
342 | ||
343 | ieee80211_queue_delayed_work(dev->hw, &dev->mac_work, 10 * HZ); | |
344 | } | |
345 | ||
346 | void | |
347 | mt7601u_mac_wcid_setup(struct mt7601u_dev *dev, u8 idx, u8 vif_idx, u8 *mac) | |
348 | { | |
349 | u8 zmac[ETH_ALEN] = {}; | |
350 | u32 attr; | |
351 | ||
d43af505 JK |
352 | attr = FIELD_PREP(MT_WCID_ATTR_BSS_IDX, vif_idx & 7) | |
353 | FIELD_PREP(MT_WCID_ATTR_BSS_IDX_EXT, !!(vif_idx & 8)); | |
c869f77d JK |
354 | |
355 | mt76_wr(dev, MT_WCID_ATTR(idx), attr); | |
356 | ||
357 | if (mac) | |
358 | memcpy(zmac, mac, sizeof(zmac)); | |
359 | ||
360 | mt7601u_addr_wr(dev, MT_WCID_ADDR(idx), zmac); | |
361 | } | |
362 | ||
363 | void mt7601u_mac_set_ampdu_factor(struct mt7601u_dev *dev) | |
364 | { | |
365 | struct ieee80211_sta *sta; | |
366 | struct mt76_wcid *wcid; | |
367 | void *msta; | |
368 | u8 min_factor = 3; | |
369 | int i; | |
370 | ||
371 | rcu_read_lock(); | |
372 | for (i = 0; i < ARRAY_SIZE(dev->wcid); i++) { | |
373 | wcid = rcu_dereference(dev->wcid[i]); | |
374 | if (!wcid) | |
375 | continue; | |
376 | ||
377 | msta = container_of(wcid, struct mt76_sta, wcid); | |
378 | sta = container_of(msta, struct ieee80211_sta, drv_priv); | |
379 | ||
380 | min_factor = min(min_factor, sta->ht_cap.ampdu_factor); | |
381 | } | |
382 | rcu_read_unlock(); | |
383 | ||
384 | mt7601u_wr(dev, MT_MAX_LEN_CFG, 0xa0fff | | |
d43af505 | 385 | FIELD_PREP(MT_MAX_LEN_CFG_AMPDU, min_factor)); |
c869f77d JK |
386 | } |
387 | ||
388 | static void | |
389 | mt76_mac_process_rate(struct ieee80211_rx_status *status, u16 rate) | |
390 | { | |
d43af505 | 391 | u8 idx = FIELD_GET(MT_RXWI_RATE_MCS, rate); |
c869f77d | 392 | |
d43af505 | 393 | switch (FIELD_GET(MT_RXWI_RATE_PHY, rate)) { |
c869f77d JK |
394 | case MT_PHY_TYPE_OFDM: |
395 | if (WARN_ON(idx >= 8)) | |
396 | idx = 0; | |
397 | idx += 4; | |
398 | ||
399 | status->rate_idx = idx; | |
400 | return; | |
401 | case MT_PHY_TYPE_CCK: | |
402 | if (idx >= 8) { | |
403 | idx -= 8; | |
404 | status->flag |= RX_FLAG_SHORTPRE; | |
405 | } | |
406 | ||
407 | if (WARN_ON(idx >= 4)) | |
408 | idx = 0; | |
409 | ||
410 | status->rate_idx = idx; | |
411 | return; | |
412 | case MT_PHY_TYPE_HT_GF: | |
413 | status->flag |= RX_FLAG_HT_GF; | |
414 | /* fall through */ | |
415 | case MT_PHY_TYPE_HT: | |
416 | status->flag |= RX_FLAG_HT; | |
417 | status->rate_idx = idx; | |
418 | break; | |
419 | default: | |
420 | WARN_ON(1); | |
421 | return; | |
422 | } | |
423 | ||
424 | if (rate & MT_RXWI_RATE_SGI) | |
425 | status->flag |= RX_FLAG_SHORT_GI; | |
426 | ||
427 | if (rate & MT_RXWI_RATE_STBC) | |
428 | status->flag |= 1 << RX_FLAG_STBC_SHIFT; | |
429 | ||
430 | if (rate & MT_RXWI_RATE_BW) | |
431 | status->flag |= RX_FLAG_40MHZ; | |
432 | } | |
433 | ||
434 | static void | |
435 | mt7601u_rx_monitor_beacon(struct mt7601u_dev *dev, struct mt7601u_rxwi *rxwi, | |
436 | u16 rate, int rssi) | |
437 | { | |
438 | dev->bcn_freq_off = rxwi->freq_off; | |
d43af505 | 439 | dev->bcn_phy_mode = FIELD_GET(MT_RXWI_RATE_PHY, rate); |
c869f77d JK |
440 | dev->avg_rssi = (dev->avg_rssi * 15) / 16 + (rssi << 8); |
441 | } | |
442 | ||
443 | static int | |
444 | mt7601u_rx_is_our_beacon(struct mt7601u_dev *dev, u8 *data) | |
445 | { | |
446 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)data; | |
447 | ||
448 | return ieee80211_is_beacon(hdr->frame_control) && | |
449 | ether_addr_equal(hdr->addr2, dev->ap_bssid); | |
450 | } | |
451 | ||
452 | u32 mt76_mac_process_rx(struct mt7601u_dev *dev, struct sk_buff *skb, | |
453 | u8 *data, void *rxi) | |
454 | { | |
455 | struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); | |
456 | struct mt7601u_rxwi *rxwi = rxi; | |
2af6d21f | 457 | u32 len, ctl = le32_to_cpu(rxwi->ctl); |
c869f77d JK |
458 | u16 rate = le16_to_cpu(rxwi->rate); |
459 | int rssi; | |
460 | ||
d43af505 | 461 | len = FIELD_GET(MT_RXWI_CTL_MPDU_LEN, ctl); |
2af6d21f JK |
462 | if (len < 10) |
463 | return 0; | |
464 | ||
c869f77d JK |
465 | if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_DECRYPT)) { |
466 | status->flag |= RX_FLAG_DECRYPTED; | |
467 | status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; | |
468 | } | |
469 | ||
470 | status->chains = BIT(0); | |
471 | rssi = mt7601u_phy_get_rssi(dev, rxwi, rate); | |
472 | status->chain_signal[0] = status->signal = rssi; | |
473 | status->freq = dev->chandef.chan->center_freq; | |
474 | status->band = dev->chandef.chan->band; | |
475 | ||
476 | mt76_mac_process_rate(status, rate); | |
477 | ||
478 | spin_lock_bh(&dev->con_mon_lock); | |
479 | if (mt7601u_rx_is_our_beacon(dev, data)) | |
480 | mt7601u_rx_monitor_beacon(dev, rxwi, rate, rssi); | |
481 | else if (rxwi->rxinfo & cpu_to_le32(MT_RXINFO_U2M)) | |
482 | dev->avg_rssi = (dev->avg_rssi * 15) / 16 + (rssi << 8); | |
483 | spin_unlock_bh(&dev->con_mon_lock); | |
484 | ||
2af6d21f | 485 | return len; |
c869f77d JK |
486 | } |
487 | ||
488 | static enum mt76_cipher_type | |
489 | mt76_mac_get_key_info(struct ieee80211_key_conf *key, u8 *key_data) | |
490 | { | |
491 | memset(key_data, 0, 32); | |
492 | if (!key) | |
493 | return MT_CIPHER_NONE; | |
494 | ||
495 | if (key->keylen > 32) | |
496 | return MT_CIPHER_NONE; | |
497 | ||
498 | memcpy(key_data, key->key, key->keylen); | |
499 | ||
500 | switch (key->cipher) { | |
501 | case WLAN_CIPHER_SUITE_WEP40: | |
502 | return MT_CIPHER_WEP40; | |
503 | case WLAN_CIPHER_SUITE_WEP104: | |
504 | return MT_CIPHER_WEP104; | |
505 | case WLAN_CIPHER_SUITE_TKIP: | |
506 | return MT_CIPHER_TKIP; | |
507 | case WLAN_CIPHER_SUITE_CCMP: | |
508 | return MT_CIPHER_AES_CCMP; | |
509 | default: | |
510 | return MT_CIPHER_NONE; | |
511 | } | |
512 | } | |
513 | ||
514 | int mt76_mac_wcid_set_key(struct mt7601u_dev *dev, u8 idx, | |
515 | struct ieee80211_key_conf *key) | |
516 | { | |
517 | enum mt76_cipher_type cipher; | |
518 | u8 key_data[32]; | |
519 | u8 iv_data[8]; | |
520 | u32 val; | |
521 | ||
522 | cipher = mt76_mac_get_key_info(key, key_data); | |
523 | if (cipher == MT_CIPHER_NONE && key) | |
524 | return -EINVAL; | |
525 | ||
526 | trace_set_key(dev, idx); | |
527 | ||
528 | mt7601u_wr_copy(dev, MT_WCID_KEY(idx), key_data, sizeof(key_data)); | |
529 | ||
530 | memset(iv_data, 0, sizeof(iv_data)); | |
531 | if (key) { | |
532 | iv_data[3] = key->keyidx << 6; | |
533 | if (cipher >= MT_CIPHER_TKIP) { | |
534 | /* Note: start with 1 to comply with spec, | |
535 | * (see comment on common/cmm_wpa.c:4291). | |
536 | */ | |
537 | iv_data[0] |= 1; | |
538 | iv_data[3] |= 0x20; | |
539 | } | |
540 | } | |
541 | mt7601u_wr_copy(dev, MT_WCID_IV(idx), iv_data, sizeof(iv_data)); | |
542 | ||
543 | val = mt7601u_rr(dev, MT_WCID_ATTR(idx)); | |
544 | val &= ~MT_WCID_ATTR_PKEY_MODE & ~MT_WCID_ATTR_PKEY_MODE_EXT; | |
d43af505 JK |
545 | val |= FIELD_PREP(MT_WCID_ATTR_PKEY_MODE, cipher & 7) | |
546 | FIELD_PREP(MT_WCID_ATTR_PKEY_MODE_EXT, cipher >> 3); | |
c869f77d JK |
547 | val &= ~MT_WCID_ATTR_PAIRWISE; |
548 | val |= MT_WCID_ATTR_PAIRWISE * | |
549 | !!(key && key->flags & IEEE80211_KEY_FLAG_PAIRWISE); | |
550 | mt7601u_wr(dev, MT_WCID_ATTR(idx), val); | |
551 | ||
552 | return 0; | |
553 | } | |
554 | ||
555 | int mt76_mac_shared_key_setup(struct mt7601u_dev *dev, u8 vif_idx, u8 key_idx, | |
556 | struct ieee80211_key_conf *key) | |
557 | { | |
558 | enum mt76_cipher_type cipher; | |
559 | u8 key_data[32]; | |
560 | u32 val; | |
561 | ||
562 | cipher = mt76_mac_get_key_info(key, key_data); | |
563 | if (cipher == MT_CIPHER_NONE && key) | |
564 | return -EINVAL; | |
565 | ||
566 | trace_set_shared_key(dev, vif_idx, key_idx); | |
567 | ||
568 | mt7601u_wr_copy(dev, MT_SKEY(vif_idx, key_idx), | |
569 | key_data, sizeof(key_data)); | |
570 | ||
571 | val = mt76_rr(dev, MT_SKEY_MODE(vif_idx)); | |
572 | val &= ~(MT_SKEY_MODE_MASK << MT_SKEY_MODE_SHIFT(vif_idx, key_idx)); | |
573 | val |= cipher << MT_SKEY_MODE_SHIFT(vif_idx, key_idx); | |
574 | mt76_wr(dev, MT_SKEY_MODE(vif_idx), val); | |
575 | ||
576 | return 0; | |
577 | } |