432 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014 Felix Fietkau <nbd@openwrt.org>4 * Copyright (C) 2015 Jakub Kicinski <kubakici@wp.pl>5 */6 7#include "mt7601u.h"8#include "mac.h"9#include <linux/etherdevice.h>10 11static 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);26out:27 mutex_unlock(&dev->mutex);28 return ret;29}30 31static void mt7601u_stop(struct ieee80211_hw *hw, bool suspend)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 44static 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 offsets54 * - do mac address tricks55 * - shift vif idx56 */57 mvif->idx = idx;58 59 if (!ether_addr_equal(dev->macaddr, vif->addr))60 mt7601u_set_macaddr(dev, vif->addr);61 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 71static 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 81static 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 99static void100mt76_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 116 MT76_FILTER(OTHER_BSS, MT_RX_FILTR_CFG_PROMISC);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 133static void134mt7601u_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,135 struct ieee80211_bss_conf *info, u64 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 changed148 * 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 186static int187mt76_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 208static int209mt7601u_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 233out:234 mutex_unlock(&dev->mutex);235 236 return ret;237}238 239static int240mt7601u_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 258static void259mt7601u_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,260 enum sta_notify_cmd cmd, struct ieee80211_sta *sta)261{262}263 264static void265mt7601u_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 275static void276mt7601u_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);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);289}290 291static int292mt7601u_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 /* 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 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 337static 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 346static int347mt76_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,348 struct ieee80211_ampdu_params *params)349{350 struct mt7601u_dev *dev = hw->priv;351 struct ieee80211_sta *sta = params->sta;352 enum ieee80211_ampdu_mlme_action action = params->action;353 u16 tid = params->tid;354 u16 ssn = params->ssn;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:374 msta->agg_ssn[tid] = ssn << 4;375 return IEEE80211_AMPDU_TX_START_IMMEDIATE;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 384static void385mt76_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 403out:404 rcu_read_unlock();405}406 407const struct ieee80211_ops mt7601u_ops = {408 .add_chanctx = ieee80211_emulate_add_chanctx,409 .remove_chanctx = ieee80211_emulate_remove_chanctx,410 .change_chanctx = ieee80211_emulate_change_chanctx,411 .switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx,412 .tx = mt7601u_tx,413 .wake_tx_queue = ieee80211_handle_wake_tx_queue,414 .start = mt7601u_start,415 .stop = mt7601u_stop,416 .add_interface = mt7601u_add_interface,417 .remove_interface = mt7601u_remove_interface,418 .config = mt7601u_config,419 .configure_filter = mt76_configure_filter,420 .bss_info_changed = mt7601u_bss_info_changed,421 .sta_add = mt7601u_sta_add,422 .sta_remove = mt7601u_sta_remove,423 .sta_notify = mt7601u_sta_notify,424 .set_key = mt7601u_set_key,425 .conf_tx = mt7601u_conf_tx,426 .sw_scan_start = mt7601u_sw_scan,427 .sw_scan_complete = mt7601u_sw_scan_complete,428 .ampdu_action = mt76_ampdu_action,429 .sta_rate_tbl_update = mt76_sta_rate_tbl_update,430 .set_rts_threshold = mt7601u_set_rts_threshold,431};432