Path: blob/main/sys/contrib/dev/mediatek/mt76/mt7603/main.c
48526 views
// SPDX-License-Identifier: ISC12#include <linux/etherdevice.h>3#include <linux/platform_device.h>4#include <linux/pci.h>5#include <linux/module.h>6#include "mt7603.h"7#include "mac.h"8#include "eeprom.h"910static int11mt7603_start(struct ieee80211_hw *hw)12{13struct mt7603_dev *dev = hw->priv;1415mt7603_mac_reset_counters(dev);16mt7603_mac_start(dev);17dev->mphy.survey_time = ktime_get_boottime();18set_bit(MT76_STATE_RUNNING, &dev->mphy.state);19mt7603_mac_work(&dev->mphy.mac_work.work);2021return 0;22}2324static void25mt7603_stop(struct ieee80211_hw *hw, bool suspend)26{27struct mt7603_dev *dev = hw->priv;2829clear_bit(MT76_STATE_RUNNING, &dev->mphy.state);30cancel_delayed_work_sync(&dev->mphy.mac_work);31mt7603_mac_stop(dev);32}3334static int35mt7603_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)36{37struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv;38struct mt7603_dev *dev = hw->priv;39struct mt76_txq *mtxq;40u8 bc_addr[ETH_ALEN];41int idx;42int ret = 0;4344mutex_lock(&dev->mt76.mutex);4546mvif->idx = __ffs64(~dev->mt76.vif_mask);47if (mvif->idx >= MT7603_MAX_INTERFACES) {48ret = -ENOSPC;49goto out;50}5152mt76_wr(dev, MT_MAC_ADDR0(mvif->idx),53get_unaligned_le32(vif->addr));54mt76_wr(dev, MT_MAC_ADDR1(mvif->idx),55(get_unaligned_le16(vif->addr + 4) |56MT_MAC_ADDR1_VALID));5758if (vif->type == NL80211_IFTYPE_AP) {59mt76_wr(dev, MT_BSSID0(mvif->idx),60get_unaligned_le32(vif->addr));61mt76_wr(dev, MT_BSSID1(mvif->idx),62(get_unaligned_le16(vif->addr + 4) |63MT_BSSID1_VALID));64}6566idx = MT7603_WTBL_RESERVED - 1 - mvif->idx;67dev->mt76.vif_mask |= BIT_ULL(mvif->idx);68mvif->sta.wcid.idx = idx;69mvif->sta.vif = mvif;70mt76_wcid_init(&mvif->sta.wcid, 0);7172eth_broadcast_addr(bc_addr);73mt7603_wtbl_init(dev, idx, mvif->idx, bc_addr);7475mtxq = (struct mt76_txq *)vif->txq->drv_priv;76mtxq->wcid = idx;77rcu_assign_pointer(dev->mt76.wcid[idx], &mvif->sta.wcid);7879out:80mutex_unlock(&dev->mt76.mutex);8182return ret;83}8485static void86mt7603_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)87{88struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv;89struct mt7603_sta *msta = &mvif->sta;90struct mt7603_dev *dev = hw->priv;91int idx = msta->wcid.idx;9293mt76_wr(dev, MT_MAC_ADDR0(mvif->idx), 0);94mt76_wr(dev, MT_MAC_ADDR1(mvif->idx), 0);95mt76_wr(dev, MT_BSSID0(mvif->idx), 0);96mt76_wr(dev, MT_BSSID1(mvif->idx), 0);97mt7603_beacon_set_timer(dev, mvif->idx, 0);9899rcu_assign_pointer(dev->mt76.wcid[idx], NULL);100101spin_lock_bh(&dev->mt76.sta_poll_lock);102if (!list_empty(&msta->wcid.poll_list))103list_del_init(&msta->wcid.poll_list);104spin_unlock_bh(&dev->mt76.sta_poll_lock);105106mutex_lock(&dev->mt76.mutex);107dev->mt76.vif_mask &= ~BIT_ULL(mvif->idx);108mutex_unlock(&dev->mt76.mutex);109110mt76_wcid_cleanup(&dev->mt76, &mvif->sta.wcid);111}112113void mt7603_init_edcca(struct mt7603_dev *dev)114{115/* Set lower signal level to -65dBm */116mt76_rmw_field(dev, MT_RXTD(8), MT_RXTD_8_LOWER_SIGNAL, 0x23);117118/* clear previous energy detect monitor results */119mt76_rr(dev, MT_MIB_STAT_ED);120121if (dev->ed_monitor)122mt76_set(dev, MT_MIB_CTL, MT_MIB_CTL_ED_TIME);123else124mt76_clear(dev, MT_MIB_CTL, MT_MIB_CTL_ED_TIME);125126dev->ed_strict_mode = 0xff;127dev->ed_strong_signal = 0;128dev->ed_time = ktime_get_boottime();129130mt7603_edcca_set_strict(dev, false);131}132133int mt7603_set_channel(struct mt76_phy *mphy)134{135struct mt7603_dev *dev = container_of(mphy->dev, struct mt7603_dev, mt76);136struct cfg80211_chan_def *def = &mphy->chandef;137138u8 *rssi_data = (u8 *)dev->mt76.eeprom.data;139int idx, ret;140u8 bw = MT_BW_20;141bool failed = false;142143tasklet_disable(&dev->mt76.pre_tbtt_tasklet);144145mt7603_beacon_set_timer(dev, -1, 0);146mt7603_mac_stop(dev);147148if (def->width == NL80211_CHAN_WIDTH_40)149bw = MT_BW_40;150151mt76_rmw_field(dev, MT_AGG_BWCR, MT_AGG_BWCR_BW, bw);152ret = mt7603_mcu_set_channel(dev);153if (ret) {154failed = true;155goto out;156}157158if (def->chan->band == NL80211_BAND_5GHZ) {159idx = 1;160rssi_data += MT_EE_RSSI_OFFSET_5G;161} else {162idx = 0;163rssi_data += MT_EE_RSSI_OFFSET_2G;164}165166memcpy(dev->rssi_offset, rssi_data, sizeof(dev->rssi_offset));167168idx |= (def->chan -169mt76_hw(dev)->wiphy->bands[def->chan->band]->channels) << 1;170mt76_wr(dev, MT_WF_RMAC_CH_FREQ, idx);171mt7603_mac_set_timing(dev);172mt7603_mac_start(dev);173174ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mphy.mac_work,175msecs_to_jiffies(MT7603_WATCHDOG_TIME));176177/* reset channel stats */178mt76_clear(dev, MT_MIB_CTL, MT_MIB_CTL_READ_CLR_DIS);179mt76_set(dev, MT_MIB_CTL,180MT_MIB_CTL_CCA_NAV_TX | MT_MIB_CTL_PSCCA_TIME);181mt76_rr(dev, MT_MIB_STAT_CCA);182mt7603_cca_stats_reset(dev);183184dev->mphy.survey_time = ktime_get_boottime();185186mt7603_init_edcca(dev);187188out:189if (!mphy->offchannel)190mt7603_beacon_set_timer(dev, -1, dev->mt76.beacon_int);191192tasklet_enable(&dev->mt76.pre_tbtt_tasklet);193194if (failed)195mt7603_mac_work(&dev->mphy.mac_work.work);196197return ret;198}199200static int mt7603_set_sar_specs(struct ieee80211_hw *hw,201const struct cfg80211_sar_specs *sar)202{203struct mt7603_dev *dev = hw->priv;204struct mt76_phy *mphy = &dev->mphy;205int err;206207if (!cfg80211_chandef_valid(&mphy->chandef))208return -EINVAL;209210err = mt76_init_sar_power(hw, sar);211if (err)212return err;213214return mt76_update_channel(mphy);215}216217static int218mt7603_config(struct ieee80211_hw *hw, int radio_idx, u32 changed)219{220struct mt7603_dev *dev = hw->priv;221int ret = 0;222223if (changed & (IEEE80211_CONF_CHANGE_CHANNEL |224IEEE80211_CONF_CHANGE_POWER))225ret = mt76_update_channel(&dev->mphy);226227if (changed & IEEE80211_CONF_CHANGE_MONITOR) {228mutex_lock(&dev->mt76.mutex);229230if (!(hw->conf.flags & IEEE80211_CONF_MONITOR))231dev->rxfilter |= MT_WF_RFCR_DROP_OTHER_UC;232else233dev->rxfilter &= ~MT_WF_RFCR_DROP_OTHER_UC;234235mt76_wr(dev, MT_WF_RFCR, dev->rxfilter);236237mutex_unlock(&dev->mt76.mutex);238}239240return ret;241}242243static void244mt7603_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags,245unsigned int *total_flags, u64 multicast)246{247struct mt7603_dev *dev = hw->priv;248u32 flags = 0;249250#define MT76_FILTER(_flag, _hw) do { \251flags |= *total_flags & FIF_##_flag; \252dev->rxfilter &= ~(_hw); \253dev->rxfilter |= !(flags & FIF_##_flag) * (_hw); \254} while (0)255256dev->rxfilter &= ~(MT_WF_RFCR_DROP_OTHER_BSS |257MT_WF_RFCR_DROP_OTHER_BEACON |258MT_WF_RFCR_DROP_FRAME_REPORT |259MT_WF_RFCR_DROP_PROBEREQ |260MT_WF_RFCR_DROP_MCAST_FILTERED |261MT_WF_RFCR_DROP_MCAST |262MT_WF_RFCR_DROP_BCAST |263MT_WF_RFCR_DROP_DUPLICATE |264MT_WF_RFCR_DROP_A2_BSSID |265MT_WF_RFCR_DROP_UNWANTED_CTL |266MT_WF_RFCR_DROP_STBC_MULTI);267268MT76_FILTER(OTHER_BSS, MT_WF_RFCR_DROP_OTHER_TIM |269MT_WF_RFCR_DROP_A3_MAC |270MT_WF_RFCR_DROP_A3_BSSID);271272MT76_FILTER(FCSFAIL, MT_WF_RFCR_DROP_FCSFAIL);273274MT76_FILTER(CONTROL, MT_WF_RFCR_DROP_CTS |275MT_WF_RFCR_DROP_RTS |276MT_WF_RFCR_DROP_CTL_RSV |277MT_WF_RFCR_DROP_NDPA);278279*total_flags = flags;280mt76_wr(dev, MT_WF_RFCR, dev->rxfilter);281}282283static void284mt7603_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,285struct ieee80211_bss_conf *info, u64 changed)286{287struct mt7603_dev *dev = hw->priv;288struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv;289290mutex_lock(&dev->mt76.mutex);291292if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BSSID)) {293if (vif->cfg.assoc || vif->cfg.ibss_joined) {294mt76_wr(dev, MT_BSSID0(mvif->idx),295get_unaligned_le32(info->bssid));296mt76_wr(dev, MT_BSSID1(mvif->idx),297(get_unaligned_le16(info->bssid + 4) |298MT_BSSID1_VALID));299} else {300mt76_wr(dev, MT_BSSID0(mvif->idx), 0);301mt76_wr(dev, MT_BSSID1(mvif->idx), 0);302}303}304305if (changed & BSS_CHANGED_ERP_SLOT) {306int slottime = info->use_short_slot ? 9 : 20;307308if (slottime != dev->slottime) {309dev->slottime = slottime;310mt7603_mac_set_timing(dev);311}312}313314if (changed & (BSS_CHANGED_BEACON_ENABLED | BSS_CHANGED_BEACON_INT)) {315int beacon_int = !!info->enable_beacon * info->beacon_int;316317tasklet_disable(&dev->mt76.pre_tbtt_tasklet);318mt7603_beacon_set_timer(dev, mvif->idx, beacon_int);319tasklet_enable(&dev->mt76.pre_tbtt_tasklet);320}321322mutex_unlock(&dev->mt76.mutex);323}324325int326mt7603_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,327struct ieee80211_sta *sta)328{329struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76);330struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv;331struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv;332int idx;333int ret = 0;334335idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7603_WTBL_STA - 1);336if (idx < 0)337return -ENOSPC;338339INIT_LIST_HEAD(&msta->wcid.poll_list);340__skb_queue_head_init(&msta->psq);341msta->ps = ~0;342msta->smps = ~0;343msta->wcid.sta = 1;344msta->wcid.idx = idx;345msta->vif = mvif;346mt7603_wtbl_init(dev, idx, mvif->idx, sta->addr);347mt7603_wtbl_set_ps(dev, msta, false);348349if (vif->type == NL80211_IFTYPE_AP)350set_bit(MT_WCID_FLAG_CHECK_PS, &msta->wcid.flags);351352return ret;353}354355int356mt7603_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif,357struct ieee80211_sta *sta, enum mt76_sta_event ev)358{359struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76);360361if (ev == MT76_STA_EVENT_ASSOC) {362mutex_lock(&dev->mt76.mutex);363mt7603_wtbl_update_cap(dev, sta);364mutex_unlock(&dev->mt76.mutex);365}366367return 0;368}369370void371mt7603_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif,372struct ieee80211_sta *sta)373{374struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76);375struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv;376struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv;377struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;378379spin_lock_bh(&dev->ps_lock);380__skb_queue_purge(&msta->psq);381mt7603_filter_tx(dev, mvif->idx, wcid->idx, true);382spin_unlock_bh(&dev->ps_lock);383384spin_lock_bh(&mdev->sta_poll_lock);385if (!list_empty(&msta->wcid.poll_list))386list_del_init(&msta->wcid.poll_list);387spin_unlock_bh(&mdev->sta_poll_lock);388389mt7603_wtbl_clear(dev, wcid->idx);390}391392static void393mt7603_ps_tx_list(struct mt7603_dev *dev, struct sk_buff_head *list)394{395struct sk_buff *skb;396397while ((skb = __skb_dequeue(list)) != NULL) {398int qid = skb_get_queue_mapping(skb);399400mt76_tx_queue_skb_raw(dev, dev->mphy.q_tx[qid], skb, 0);401}402}403404void405mt7603_sta_ps(struct mt76_dev *mdev, struct ieee80211_sta *sta, bool ps)406{407struct mt7603_dev *dev = container_of(mdev, struct mt7603_dev, mt76);408struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv;409struct sk_buff_head list;410411mt76_stop_tx_queues(&dev->mphy, sta, true);412mt7603_wtbl_set_ps(dev, msta, ps);413if (ps)414return;415416__skb_queue_head_init(&list);417418spin_lock_bh(&dev->ps_lock);419skb_queue_splice_tail_init(&msta->psq, &list);420spin_unlock_bh(&dev->ps_lock);421422mt7603_ps_tx_list(dev, &list);423}424425static void426mt7603_ps_set_more_data(struct sk_buff *skb)427{428struct ieee80211_hdr *hdr;429430hdr = (struct ieee80211_hdr *)&skb->data[MT_TXD_SIZE];431hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_MOREDATA);432}433434static void435mt7603_release_buffered_frames(struct ieee80211_hw *hw,436struct ieee80211_sta *sta,437u16 tids, int nframes,438enum ieee80211_frame_release_type reason,439bool more_data)440{441struct mt7603_dev *dev = hw->priv;442struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv;443struct sk_buff_head list;444struct sk_buff *skb, *tmp;445446__skb_queue_head_init(&list);447448mt7603_wtbl_set_ps(dev, msta, false);449450spin_lock_bh(&dev->ps_lock);451skb_queue_walk_safe(&msta->psq, skb, tmp) {452if (!nframes)453break;454455if (!(tids & BIT(skb->priority)))456continue;457458skb_set_queue_mapping(skb, MT_TXQ_PSD);459__skb_unlink(skb, &msta->psq);460mt7603_ps_set_more_data(skb);461__skb_queue_tail(&list, skb);462nframes--;463}464spin_unlock_bh(&dev->ps_lock);465466if (!skb_queue_empty(&list))467ieee80211_sta_eosp(sta);468469mt7603_ps_tx_list(dev, &list);470471if (nframes)472mt76_release_buffered_frames(hw, sta, tids, nframes, reason,473more_data);474}475476static int477mt7603_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,478struct ieee80211_vif *vif, struct ieee80211_sta *sta,479struct ieee80211_key_conf *key)480{481struct mt7603_dev *dev = hw->priv;482struct mt7603_vif *mvif = (struct mt7603_vif *)vif->drv_priv;483struct mt7603_sta *msta = sta ? (struct mt7603_sta *)sta->drv_priv :484&mvif->sta;485struct mt76_wcid *wcid = &msta->wcid;486int idx = key->keyidx;487488/* fall back to sw encryption for unsupported ciphers */489switch (key->cipher) {490case WLAN_CIPHER_SUITE_TKIP:491case WLAN_CIPHER_SUITE_CCMP:492break;493default:494return -EOPNOTSUPP;495}496497/*498* The hardware does not support per-STA RX GTK, fall back499* to software mode for these.500*/501if ((vif->type == NL80211_IFTYPE_ADHOC ||502vif->type == NL80211_IFTYPE_MESH_POINT) &&503(key->cipher == WLAN_CIPHER_SUITE_TKIP ||504key->cipher == WLAN_CIPHER_SUITE_CCMP) &&505!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))506return -EOPNOTSUPP;507508if (cmd != SET_KEY) {509if (idx == wcid->hw_key_idx)510wcid->hw_key_idx = -1;511512return 0;513}514515key->hw_key_idx = wcid->idx;516wcid->hw_key_idx = idx;517mt76_wcid_key_setup(&dev->mt76, wcid, key);518519return mt7603_wtbl_set_key(dev, wcid->idx, key);520}521522static int523mt7603_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif,524unsigned int link_id, u16 queue,525const struct ieee80211_tx_queue_params *params)526{527struct mt7603_dev *dev = hw->priv;528u16 cw_min = (1 << 5) - 1;529u16 cw_max = (1 << 10) - 1;530u32 val;531532queue = dev->mphy.q_tx[queue]->hw_idx;533534if (params->cw_min)535cw_min = params->cw_min;536if (params->cw_max)537cw_max = params->cw_max;538539mutex_lock(&dev->mt76.mutex);540mt7603_mac_stop(dev);541542val = mt76_rr(dev, MT_WMM_TXOP(queue));543val &= ~(MT_WMM_TXOP_MASK << MT_WMM_TXOP_SHIFT(queue));544val |= params->txop << MT_WMM_TXOP_SHIFT(queue);545mt76_wr(dev, MT_WMM_TXOP(queue), val);546547val = mt76_rr(dev, MT_WMM_AIFSN);548val &= ~(MT_WMM_AIFSN_MASK << MT_WMM_AIFSN_SHIFT(queue));549val |= params->aifs << MT_WMM_AIFSN_SHIFT(queue);550mt76_wr(dev, MT_WMM_AIFSN, val);551552val = mt76_rr(dev, MT_WMM_CWMIN);553val &= ~(MT_WMM_CWMIN_MASK << MT_WMM_CWMIN_SHIFT(queue));554val |= cw_min << MT_WMM_CWMIN_SHIFT(queue);555mt76_wr(dev, MT_WMM_CWMIN, val);556557val = mt76_rr(dev, MT_WMM_CWMAX(queue));558val &= ~(MT_WMM_CWMAX_MASK << MT_WMM_CWMAX_SHIFT(queue));559val |= cw_max << MT_WMM_CWMAX_SHIFT(queue);560mt76_wr(dev, MT_WMM_CWMAX(queue), val);561562mt7603_mac_start(dev);563mutex_unlock(&dev->mt76.mutex);564565return 0;566}567568static void569mt7603_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,570u32 queues, bool drop)571{572}573574static int575mt7603_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,576struct ieee80211_ampdu_params *params)577{578enum ieee80211_ampdu_mlme_action action = params->action;579struct mt7603_dev *dev = hw->priv;580struct ieee80211_sta *sta = params->sta;581struct ieee80211_txq *txq = sta->txq[params->tid];582struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv;583u16 tid = params->tid;584u16 ssn = params->ssn;585u8 ba_size = params->buf_size;586struct mt76_txq *mtxq;587int ret = 0;588589if (!txq)590return -EINVAL;591592mtxq = (struct mt76_txq *)txq->drv_priv;593594mutex_lock(&dev->mt76.mutex);595switch (action) {596case IEEE80211_AMPDU_RX_START:597mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn,598params->buf_size);599mt7603_mac_rx_ba_reset(dev, sta->addr, tid);600break;601case IEEE80211_AMPDU_RX_STOP:602mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid);603break;604case IEEE80211_AMPDU_TX_OPERATIONAL:605mtxq->aggr = true;606mtxq->send_bar = false;607mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, ba_size);608break;609case IEEE80211_AMPDU_TX_STOP_FLUSH:610case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:611mtxq->aggr = false;612mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, -1);613break;614case IEEE80211_AMPDU_TX_START:615mtxq->agg_ssn = IEEE80211_SN_TO_SEQ(ssn);616ret = IEEE80211_AMPDU_TX_START_IMMEDIATE;617break;618case IEEE80211_AMPDU_TX_STOP_CONT:619mtxq->aggr = false;620mt7603_mac_tx_ba_reset(dev, msta->wcid.idx, tid, -1);621ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);622break;623}624mutex_unlock(&dev->mt76.mutex);625626return ret;627}628629static void630mt7603_sta_rate_tbl_update(struct ieee80211_hw *hw, struct ieee80211_vif *vif,631struct ieee80211_sta *sta)632{633struct mt7603_dev *dev = hw->priv;634struct mt7603_sta *msta = (struct mt7603_sta *)sta->drv_priv;635struct ieee80211_sta_rates *sta_rates = rcu_dereference(sta->rates);636int i;637638if (!sta_rates)639return;640641spin_lock_bh(&dev->mt76.lock);642for (i = 0; i < ARRAY_SIZE(msta->rates); i++) {643msta->rates[i].idx = sta_rates->rate[i].idx;644msta->rates[i].count = sta_rates->rate[i].count;645msta->rates[i].flags = sta_rates->rate[i].flags;646647if (msta->rates[i].idx < 0 || !msta->rates[i].count)648break;649}650msta->n_rates = i;651mt7603_wtbl_set_rates(dev, msta, NULL, msta->rates);652msta->rate_probe = false;653mt7603_wtbl_set_smps(dev, msta,654sta->deflink.smps_mode == IEEE80211_SMPS_DYNAMIC);655spin_unlock_bh(&dev->mt76.lock);656}657658static void659mt7603_set_coverage_class(struct ieee80211_hw *hw, int radio_idx,660s16 coverage_class)661{662struct mt7603_dev *dev = hw->priv;663664mutex_lock(&dev->mt76.mutex);665dev->coverage_class = max_t(s16, coverage_class, 0);666mt7603_mac_set_timing(dev);667mutex_unlock(&dev->mt76.mutex);668}669670static void mt7603_tx(struct ieee80211_hw *hw,671struct ieee80211_tx_control *control,672struct sk_buff *skb)673{674struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);675struct ieee80211_vif *vif = info->control.vif;676struct mt7603_dev *dev = hw->priv;677struct mt76_wcid *wcid = &dev->global_sta.wcid;678679if (control->sta) {680struct mt7603_sta *msta;681682msta = (struct mt7603_sta *)control->sta->drv_priv;683wcid = &msta->wcid;684} else if (vif) {685struct mt7603_vif *mvif;686687mvif = (struct mt7603_vif *)vif->drv_priv;688wcid = &mvif->sta.wcid;689}690691mt76_tx(&dev->mphy, control->sta, wcid, skb);692}693694const struct ieee80211_ops mt7603_ops = {695.add_chanctx = ieee80211_emulate_add_chanctx,696.remove_chanctx = ieee80211_emulate_remove_chanctx,697.change_chanctx = ieee80211_emulate_change_chanctx,698.switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx,699.tx = mt7603_tx,700.start = mt7603_start,701.stop = mt7603_stop,702.add_interface = mt7603_add_interface,703.remove_interface = mt7603_remove_interface,704.config = mt7603_config,705.configure_filter = mt7603_configure_filter,706.bss_info_changed = mt7603_bss_info_changed,707.sta_state = mt76_sta_state,708.sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,709.set_key = mt7603_set_key,710.conf_tx = mt7603_conf_tx,711.sw_scan_start = mt76_sw_scan,712.sw_scan_complete = mt76_sw_scan_complete,713.flush = mt7603_flush,714.ampdu_action = mt7603_ampdu_action,715.get_txpower = mt76_get_txpower,716.wake_tx_queue = mt76_wake_tx_queue,717.sta_rate_tbl_update = mt7603_sta_rate_tbl_update,718.release_buffered_frames = mt7603_release_buffered_frames,719.set_coverage_class = mt7603_set_coverage_class,720.set_tim = mt76_set_tim,721.get_survey = mt76_get_survey,722.get_antenna = mt76_get_antenna,723.set_sar_specs = mt7603_set_sar_specs,724};725726MODULE_DESCRIPTION("MediaTek MT7603E and MT76x8 wireless driver");727MODULE_LICENSE("Dual BSD/GPL");728729static int __init mt7603_init(void)730{731int ret;732733ret = platform_driver_register(&mt76_wmac_driver);734if (ret)735return ret;736737#ifdef CONFIG_PCI738ret = pci_register_driver(&mt7603_pci_driver);739if (ret)740platform_driver_unregister(&mt76_wmac_driver);741#endif742return ret;743}744745static void __exit mt7603_exit(void)746{747#ifdef CONFIG_PCI748pci_unregister_driver(&mt7603_pci_driver);749#endif750platform_driver_unregister(&mt76_wmac_driver);751}752753module_init(mt7603_init);754module_exit(mt7603_exit);755756757