root/drivers/net/wireless/mediatek/mt7601u/main.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mt7601u_start
  2. mt7601u_stop
  3. mt7601u_add_interface
  4. mt7601u_remove_interface
  5. mt7601u_config
  6. mt76_configure_filter
  7. mt7601u_bss_info_changed
  8. mt76_wcid_alloc
  9. mt7601u_sta_add
  10. mt7601u_sta_remove
  11. mt7601u_sta_notify
  12. mt7601u_sw_scan
  13. mt7601u_sw_scan_complete
  14. mt7601u_set_key
  15. mt7601u_set_rts_threshold
  16. mt76_ampdu_action
  17. mt76_sta_rate_tbl_update

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   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 
  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 
  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 
  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 
 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 
 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);
 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 
 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         /* 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 
 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,
 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                 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 376                 break;
 377         case IEEE80211_AMPDU_TX_STOP_CONT:
 378                 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
 379                 break;
 380         }
 381 
 382         return 0;
 383 }
 384 
 385 static void
 386 mt76_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 387                          struct ieee80211_sta *sta)
 388 {
 389         struct mt7601u_dev *dev = hw->priv;
 390         struct mt76_sta *msta = (struct mt76_sta *) sta->drv_priv;
 391         struct ieee80211_sta_rates *rates;
 392         struct ieee80211_tx_rate rate = {};
 393 
 394         rcu_read_lock();
 395         rates = rcu_dereference(sta->rates);
 396 
 397         if (!rates)
 398                 goto out;
 399 
 400         rate.idx = rates->rate[0].idx;
 401         rate.flags = rates->rate[0].flags;
 402         mt76_mac_wcid_set_rate(dev, &msta->wcid, &rate);
 403 
 404 out:
 405         rcu_read_unlock();
 406 }
 407 
 408 const struct ieee80211_ops mt7601u_ops = {
 409         .tx = mt7601u_tx,
 410         .start = mt7601u_start,
 411         .stop = mt7601u_stop,
 412         .add_interface = mt7601u_add_interface,
 413         .remove_interface = mt7601u_remove_interface,
 414         .config = mt7601u_config,
 415         .configure_filter = mt76_configure_filter,
 416         .bss_info_changed = mt7601u_bss_info_changed,
 417         .sta_add = mt7601u_sta_add,
 418         .sta_remove = mt7601u_sta_remove,
 419         .sta_notify = mt7601u_sta_notify,
 420         .set_key = mt7601u_set_key,
 421         .conf_tx = mt7601u_conf_tx,
 422         .sw_scan_start = mt7601u_sw_scan,
 423         .sw_scan_complete = mt7601u_sw_scan_complete,
 424         .ampdu_action = mt76_ampdu_action,
 425         .sta_rate_tbl_update = mt76_sta_rate_tbl_update,
 426         .set_rts_threshold = mt7601u_set_rts_threshold,
 427 };

/* [<][>][^][v][top][bottom][index][help] */