xref: /openbmc/linux/drivers/net/wireless/realtek/rtw88/mac80211.c (revision 1188f7f111c61394ec56beb8e30322305a8220b6)
1e3037485SYan-Hsuan Chuang // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2e3037485SYan-Hsuan Chuang /* Copyright(c) 2018-2019  Realtek Corporation
3e3037485SYan-Hsuan Chuang  */
4e3037485SYan-Hsuan Chuang 
5e3037485SYan-Hsuan Chuang #include "main.h"
6e3037485SYan-Hsuan Chuang #include "sec.h"
7e3037485SYan-Hsuan Chuang #include "tx.h"
8e3037485SYan-Hsuan Chuang #include "fw.h"
9e3037485SYan-Hsuan Chuang #include "mac.h"
104136214fSYan-Hsuan Chuang #include "coex.h"
11e3037485SYan-Hsuan Chuang #include "ps.h"
12e3037485SYan-Hsuan Chuang #include "reg.h"
130bd95573STzu-En Huang #include "bf.h"
14e3037485SYan-Hsuan Chuang #include "debug.h"
1544bc17f7SChin-Yen Lee #include "wow.h"
168704d0beSZong-Zhe Yang #include "sar.h"
17e3037485SYan-Hsuan Chuang 
rtw_ops_tx(struct ieee80211_hw * hw,struct ieee80211_tx_control * control,struct sk_buff * skb)18e3037485SYan-Hsuan Chuang static void rtw_ops_tx(struct ieee80211_hw *hw,
19e3037485SYan-Hsuan Chuang 		       struct ieee80211_tx_control *control,
20e3037485SYan-Hsuan Chuang 		       struct sk_buff *skb)
21e3037485SYan-Hsuan Chuang {
22e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
23e3037485SYan-Hsuan Chuang 
243745d3e5SYan-Hsuan Chuang 	if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags)) {
253745d3e5SYan-Hsuan Chuang 		ieee80211_free_txskb(hw, skb);
263745d3e5SYan-Hsuan Chuang 		return;
273745d3e5SYan-Hsuan Chuang 	}
28e3037485SYan-Hsuan Chuang 
293745d3e5SYan-Hsuan Chuang 	rtw_tx(rtwdev, control, skb);
303745d3e5SYan-Hsuan Chuang }
31e3037485SYan-Hsuan Chuang 
rtw_ops_wake_tx_queue(struct ieee80211_hw * hw,struct ieee80211_txq * txq)323745d3e5SYan-Hsuan Chuang static void rtw_ops_wake_tx_queue(struct ieee80211_hw *hw,
333745d3e5SYan-Hsuan Chuang 				  struct ieee80211_txq *txq)
343745d3e5SYan-Hsuan Chuang {
353745d3e5SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
363745d3e5SYan-Hsuan Chuang 	struct rtw_txq *rtwtxq = (struct rtw_txq *)txq->drv_priv;
37e3037485SYan-Hsuan Chuang 
383c519605SYan-Hsuan Chuang 	if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags))
39e3037485SYan-Hsuan Chuang 		return;
40e3037485SYan-Hsuan Chuang 
413745d3e5SYan-Hsuan Chuang 	spin_lock_bh(&rtwdev->txq_lock);
423745d3e5SYan-Hsuan Chuang 	if (list_empty(&rtwtxq->list))
433745d3e5SYan-Hsuan Chuang 		list_add_tail(&rtwtxq->list, &rtwdev->txqs);
443745d3e5SYan-Hsuan Chuang 	spin_unlock_bh(&rtwdev->txq_lock);
453745d3e5SYan-Hsuan Chuang 
4667d7f24bSChih-Kang Chang 	/* ensure to dequeue EAPOL (4/4) at the right time */
4767d7f24bSChih-Kang Chang 	if (txq->ac == IEEE80211_AC_VO)
4867d7f24bSChih-Kang Chang 		__rtw_tx_work(rtwdev);
4967d7f24bSChih-Kang Chang 	else
50fe101716SPo-Hao Huang 		queue_work(rtwdev->tx_wq, &rtwdev->tx_work);
51e3037485SYan-Hsuan Chuang }
52e3037485SYan-Hsuan Chuang 
rtw_ops_start(struct ieee80211_hw * hw)53e3037485SYan-Hsuan Chuang static int rtw_ops_start(struct ieee80211_hw *hw)
54e3037485SYan-Hsuan Chuang {
55e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
56e3037485SYan-Hsuan Chuang 	int ret;
57e3037485SYan-Hsuan Chuang 
58e3037485SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
59e3037485SYan-Hsuan Chuang 	ret = rtw_core_start(rtwdev);
60e3037485SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
61e3037485SYan-Hsuan Chuang 
62e3037485SYan-Hsuan Chuang 	return ret;
63e3037485SYan-Hsuan Chuang }
64e3037485SYan-Hsuan Chuang 
rtw_ops_stop(struct ieee80211_hw * hw)65e3037485SYan-Hsuan Chuang static void rtw_ops_stop(struct ieee80211_hw *hw)
66e3037485SYan-Hsuan Chuang {
67e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
68e3037485SYan-Hsuan Chuang 
69e3037485SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
70e3037485SYan-Hsuan Chuang 	rtw_core_stop(rtwdev);
71e3037485SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
72e3037485SYan-Hsuan Chuang }
73e3037485SYan-Hsuan Chuang 
rtw_ops_config(struct ieee80211_hw * hw,u32 changed)74e3037485SYan-Hsuan Chuang static int rtw_ops_config(struct ieee80211_hw *hw, u32 changed)
75e3037485SYan-Hsuan Chuang {
76e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
77e3037485SYan-Hsuan Chuang 	int ret = 0;
78e3037485SYan-Hsuan Chuang 
79c17f2716SPo-Hao Huang 	/* let previous ips work finish to ensure we don't leave ips twice */
80c17f2716SPo-Hao Huang 	cancel_work_sync(&rtwdev->ips_work);
81c17f2716SPo-Hao Huang 
82e3037485SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
83e3037485SYan-Hsuan Chuang 
8427e117e4SYan-Hsuan Chuang 	rtw_leave_lps_deep(rtwdev);
8527e117e4SYan-Hsuan Chuang 
86398b9bdaSPing-Ke Shih 	if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
87398b9bdaSPing-Ke Shih 	    !(hw->conf.flags & IEEE80211_CONF_IDLE)) {
88e3037485SYan-Hsuan Chuang 		ret = rtw_leave_ips(rtwdev);
89e3037485SYan-Hsuan Chuang 		if (ret) {
90e3037485SYan-Hsuan Chuang 			rtw_err(rtwdev, "failed to leave idle state\n");
91e3037485SYan-Hsuan Chuang 			goto out;
92e3037485SYan-Hsuan Chuang 		}
93e3037485SYan-Hsuan Chuang 	}
94e3037485SYan-Hsuan Chuang 
95e3037485SYan-Hsuan Chuang 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL)
96e3037485SYan-Hsuan Chuang 		rtw_set_channel(rtwdev);
97e3037485SYan-Hsuan Chuang 
98398b9bdaSPing-Ke Shih 	if ((changed & IEEE80211_CONF_CHANGE_IDLE) &&
997dad3e39SChih-Kang Chang 	    (hw->conf.flags & IEEE80211_CONF_IDLE) &&
1007dad3e39SChih-Kang Chang 	    !test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
101398b9bdaSPing-Ke Shih 		rtw_enter_ips(rtwdev);
102398b9bdaSPing-Ke Shih 
103e3037485SYan-Hsuan Chuang out:
104e3037485SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
105e3037485SYan-Hsuan Chuang 	return ret;
106e3037485SYan-Hsuan Chuang }
107e3037485SYan-Hsuan Chuang 
108e3037485SYan-Hsuan Chuang static const struct rtw_vif_port rtw_vif_port[] = {
109e3037485SYan-Hsuan Chuang 	[0] = {
110e3037485SYan-Hsuan Chuang 		.mac_addr	= {.addr = 0x0610},
111e3037485SYan-Hsuan Chuang 		.bssid		= {.addr = 0x0618},
112e3037485SYan-Hsuan Chuang 		.net_type	= {.addr = 0x0100, .mask = 0x30000},
113e3037485SYan-Hsuan Chuang 		.aid		= {.addr = 0x06a8, .mask = 0x7ff},
1146fabdc4aSChin-Yen Lee 		.bcn_ctrl	= {.addr = 0x0550, .mask = 0xff},
115e3037485SYan-Hsuan Chuang 	},
116e3037485SYan-Hsuan Chuang 	[1] = {
117e3037485SYan-Hsuan Chuang 		.mac_addr	= {.addr = 0x0700},
118e3037485SYan-Hsuan Chuang 		.bssid		= {.addr = 0x0708},
119e3037485SYan-Hsuan Chuang 		.net_type	= {.addr = 0x0100, .mask = 0xc0000},
120e3037485SYan-Hsuan Chuang 		.aid		= {.addr = 0x0710, .mask = 0x7ff},
1216fabdc4aSChin-Yen Lee 		.bcn_ctrl	= {.addr = 0x0551, .mask = 0xff},
122e3037485SYan-Hsuan Chuang 	},
123e3037485SYan-Hsuan Chuang 	[2] = {
124e3037485SYan-Hsuan Chuang 		.mac_addr	= {.addr = 0x1620},
125e3037485SYan-Hsuan Chuang 		.bssid		= {.addr = 0x1628},
126e3037485SYan-Hsuan Chuang 		.net_type	= {.addr = 0x1100, .mask = 0x3},
127e3037485SYan-Hsuan Chuang 		.aid		= {.addr = 0x1600, .mask = 0x7ff},
1286fabdc4aSChin-Yen Lee 		.bcn_ctrl	= {.addr = 0x0578, .mask = 0xff},
129e3037485SYan-Hsuan Chuang 	},
130e3037485SYan-Hsuan Chuang 	[3] = {
131e3037485SYan-Hsuan Chuang 		.mac_addr	= {.addr = 0x1630},
132e3037485SYan-Hsuan Chuang 		.bssid		= {.addr = 0x1638},
133e3037485SYan-Hsuan Chuang 		.net_type	= {.addr = 0x1100, .mask = 0xc},
134e3037485SYan-Hsuan Chuang 		.aid		= {.addr = 0x1604, .mask = 0x7ff},
1356fabdc4aSChin-Yen Lee 		.bcn_ctrl	= {.addr = 0x0579, .mask = 0xff},
136e3037485SYan-Hsuan Chuang 	},
137e3037485SYan-Hsuan Chuang 	[4] = {
138e3037485SYan-Hsuan Chuang 		.mac_addr	= {.addr = 0x1640},
139e3037485SYan-Hsuan Chuang 		.bssid		= {.addr = 0x1648},
140e3037485SYan-Hsuan Chuang 		.net_type	= {.addr = 0x1100, .mask = 0x30},
141e3037485SYan-Hsuan Chuang 		.aid		= {.addr = 0x1608, .mask = 0x7ff},
1426fabdc4aSChin-Yen Lee 		.bcn_ctrl	= {.addr = 0x057a, .mask = 0xff},
143e3037485SYan-Hsuan Chuang 	},
144e3037485SYan-Hsuan Chuang };
145e3037485SYan-Hsuan Chuang 
rtw_ops_add_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)146e3037485SYan-Hsuan Chuang static int rtw_ops_add_interface(struct ieee80211_hw *hw,
147e3037485SYan-Hsuan Chuang 				 struct ieee80211_vif *vif)
148e3037485SYan-Hsuan Chuang {
149e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
150e3037485SYan-Hsuan Chuang 	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
151e3037485SYan-Hsuan Chuang 	enum rtw_net_type net_type;
152e3037485SYan-Hsuan Chuang 	u32 config = 0;
153f0e741e4SPo-Hao Huang 	u8 port;
1546fabdc4aSChin-Yen Lee 	u8 bcn_ctrl = 0;
155e3037485SYan-Hsuan Chuang 
1569a711831SChin-Yen Lee 	if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_BCN_FILTER))
157cd96e22bSPo-Hao Huang 		vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER |
158cd96e22bSPo-Hao Huang 				     IEEE80211_VIF_SUPPORTS_CQM_RSSI;
159e3037485SYan-Hsuan Chuang 	rtwvif->stats.tx_unicast = 0;
160e3037485SYan-Hsuan Chuang 	rtwvif->stats.rx_unicast = 0;
161e3037485SYan-Hsuan Chuang 	rtwvif->stats.tx_cnt = 0;
162e3037485SYan-Hsuan Chuang 	rtwvif->stats.rx_cnt = 0;
16310d162b2SPo-Hao Huang 	rtwvif->scan_req = NULL;
1640bd95573STzu-En Huang 	memset(&rtwvif->bfee, 0, sizeof(struct rtw_bfee));
1653745d3e5SYan-Hsuan Chuang 	rtw_txq_init(rtwdev, vif->txq);
166895c096dSYan-Hsuan Chuang 	INIT_LIST_HEAD(&rtwvif->rsvd_page_list);
167e3037485SYan-Hsuan Chuang 
168e3037485SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
169e3037485SYan-Hsuan Chuang 
170f0e741e4SPo-Hao Huang 	port = find_first_zero_bit(rtwdev->hw_port, RTW_PORT_NUM);
171e2ff1181SDan Carpenter 	if (port >= RTW_PORT_NUM) {
172e2ff1181SDan Carpenter 		mutex_unlock(&rtwdev->mutex);
173f0e741e4SPo-Hao Huang 		return -EINVAL;
174e2ff1181SDan Carpenter 	}
175f0e741e4SPo-Hao Huang 	set_bit(port, rtwdev->hw_port);
176f0e741e4SPo-Hao Huang 
177f0e741e4SPo-Hao Huang 	rtwvif->port = port;
178f0e741e4SPo-Hao Huang 	rtwvif->conf = &rtw_vif_port[port];
17927e117e4SYan-Hsuan Chuang 	rtw_leave_lps_deep(rtwdev);
18027e117e4SYan-Hsuan Chuang 
181e3037485SYan-Hsuan Chuang 	switch (vif->type) {
182e3037485SYan-Hsuan Chuang 	case NL80211_IFTYPE_AP:
183e3037485SYan-Hsuan Chuang 	case NL80211_IFTYPE_MESH_POINT:
184895c096dSYan-Hsuan Chuang 		rtw_add_rsvd_page_bcn(rtwdev, rtwvif);
185e3037485SYan-Hsuan Chuang 		net_type = RTW_NET_AP_MODE;
1866fabdc4aSChin-Yen Lee 		bcn_ctrl = BIT_EN_BCN_FUNCTION | BIT_DIS_TSF_UDT;
187e3037485SYan-Hsuan Chuang 		break;
188e3037485SYan-Hsuan Chuang 	case NL80211_IFTYPE_ADHOC:
189895c096dSYan-Hsuan Chuang 		rtw_add_rsvd_page_bcn(rtwdev, rtwvif);
190e3037485SYan-Hsuan Chuang 		net_type = RTW_NET_AD_HOC;
1916fabdc4aSChin-Yen Lee 		bcn_ctrl = BIT_EN_BCN_FUNCTION | BIT_DIS_TSF_UDT;
192e3037485SYan-Hsuan Chuang 		break;
193e3037485SYan-Hsuan Chuang 	case NL80211_IFTYPE_STATION:
194895c096dSYan-Hsuan Chuang 		rtw_add_rsvd_page_sta(rtwdev, rtwvif);
195e3037485SYan-Hsuan Chuang 		net_type = RTW_NET_NO_LINK;
1966fabdc4aSChin-Yen Lee 		bcn_ctrl = BIT_EN_BCN_FUNCTION;
197e3037485SYan-Hsuan Chuang 		break;
198895c096dSYan-Hsuan Chuang 	default:
199895c096dSYan-Hsuan Chuang 		WARN_ON(1);
200f0e741e4SPo-Hao Huang 		clear_bit(rtwvif->port, rtwdev->hw_port);
201895c096dSYan-Hsuan Chuang 		mutex_unlock(&rtwdev->mutex);
202895c096dSYan-Hsuan Chuang 		return -EINVAL;
203e3037485SYan-Hsuan Chuang 	}
204e3037485SYan-Hsuan Chuang 
205e3037485SYan-Hsuan Chuang 	ether_addr_copy(rtwvif->mac_addr, vif->addr);
206e3037485SYan-Hsuan Chuang 	config |= PORT_SET_MAC_ADDR;
207e3037485SYan-Hsuan Chuang 	rtwvif->net_type = net_type;
208e3037485SYan-Hsuan Chuang 	config |= PORT_SET_NET_TYPE;
2096fabdc4aSChin-Yen Lee 	rtwvif->bcn_ctrl = bcn_ctrl;
2106fabdc4aSChin-Yen Lee 	config |= PORT_SET_BCN_CTRL;
211e3037485SYan-Hsuan Chuang 	rtw_vif_port_config(rtwdev, rtwvif, config);
212ccf73f6eSPo-Hao Huang 	rtw_core_port_switch(rtwdev, vif);
2133918dd01SPing-Ke Shih 	rtw_recalc_lps(rtwdev, vif);
214e3037485SYan-Hsuan Chuang 
215e3037485SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
216e3037485SYan-Hsuan Chuang 
217a0061be4SPing-Ke Shih 	rtw_dbg(rtwdev, RTW_DBG_STATE, "start vif %pM on port %d\n", vif->addr, rtwvif->port);
218e3037485SYan-Hsuan Chuang 	return 0;
219e3037485SYan-Hsuan Chuang }
220e3037485SYan-Hsuan Chuang 
rtw_ops_remove_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif)221e3037485SYan-Hsuan Chuang static void rtw_ops_remove_interface(struct ieee80211_hw *hw,
222e3037485SYan-Hsuan Chuang 				     struct ieee80211_vif *vif)
223e3037485SYan-Hsuan Chuang {
224e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
225e3037485SYan-Hsuan Chuang 	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
226e3037485SYan-Hsuan Chuang 	u32 config = 0;
227e3037485SYan-Hsuan Chuang 
228a0061be4SPing-Ke Shih 	rtw_dbg(rtwdev, RTW_DBG_STATE, "stop vif %pM on port %d\n", vif->addr, rtwvif->port);
229e3037485SYan-Hsuan Chuang 
230e3037485SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
231e3037485SYan-Hsuan Chuang 
23227e117e4SYan-Hsuan Chuang 	rtw_leave_lps_deep(rtwdev);
23327e117e4SYan-Hsuan Chuang 
2343745d3e5SYan-Hsuan Chuang 	rtw_txq_cleanup(rtwdev, vif->txq);
235895c096dSYan-Hsuan Chuang 	rtw_remove_rsvd_page(rtwdev, rtwvif);
2363745d3e5SYan-Hsuan Chuang 
237e3037485SYan-Hsuan Chuang 	eth_zero_addr(rtwvif->mac_addr);
238e3037485SYan-Hsuan Chuang 	config |= PORT_SET_MAC_ADDR;
239e3037485SYan-Hsuan Chuang 	rtwvif->net_type = RTW_NET_NO_LINK;
240e3037485SYan-Hsuan Chuang 	config |= PORT_SET_NET_TYPE;
2416fabdc4aSChin-Yen Lee 	rtwvif->bcn_ctrl = 0;
2426fabdc4aSChin-Yen Lee 	config |= PORT_SET_BCN_CTRL;
243e3037485SYan-Hsuan Chuang 	rtw_vif_port_config(rtwdev, rtwvif, config);
244f0e741e4SPo-Hao Huang 	clear_bit(rtwvif->port, rtwdev->hw_port);
2453918dd01SPing-Ke Shih 	rtw_recalc_lps(rtwdev, NULL);
246e3037485SYan-Hsuan Chuang 
247e3037485SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
248e3037485SYan-Hsuan Chuang }
249e3037485SYan-Hsuan Chuang 
rtw_ops_change_interface(struct ieee80211_hw * hw,struct ieee80211_vif * vif,enum nl80211_iftype type,bool p2p)25040b788d1SYan-Hsuan Chuang static int rtw_ops_change_interface(struct ieee80211_hw *hw,
25140b788d1SYan-Hsuan Chuang 				    struct ieee80211_vif *vif,
25240b788d1SYan-Hsuan Chuang 				    enum nl80211_iftype type, bool p2p)
25340b788d1SYan-Hsuan Chuang {
25440b788d1SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
25540b788d1SYan-Hsuan Chuang 
256a0061be4SPing-Ke Shih 	rtw_dbg(rtwdev, RTW_DBG_STATE, "change vif %pM (%d)->(%d), p2p (%d)->(%d)\n",
25740b788d1SYan-Hsuan Chuang 		vif->addr, vif->type, type, vif->p2p, p2p);
25840b788d1SYan-Hsuan Chuang 
25940b788d1SYan-Hsuan Chuang 	rtw_ops_remove_interface(hw, vif);
26040b788d1SYan-Hsuan Chuang 
26140b788d1SYan-Hsuan Chuang 	vif->type = type;
26240b788d1SYan-Hsuan Chuang 	vif->p2p = p2p;
26340b788d1SYan-Hsuan Chuang 
26440b788d1SYan-Hsuan Chuang 	return rtw_ops_add_interface(hw, vif);
26540b788d1SYan-Hsuan Chuang }
26640b788d1SYan-Hsuan Chuang 
rtw_ops_configure_filter(struct ieee80211_hw * hw,unsigned int changed_flags,unsigned int * new_flags,u64 multicast)267e3037485SYan-Hsuan Chuang static void rtw_ops_configure_filter(struct ieee80211_hw *hw,
268e3037485SYan-Hsuan Chuang 				     unsigned int changed_flags,
269e3037485SYan-Hsuan Chuang 				     unsigned int *new_flags,
270e3037485SYan-Hsuan Chuang 				     u64 multicast)
271e3037485SYan-Hsuan Chuang {
272e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
273e3037485SYan-Hsuan Chuang 
274e3037485SYan-Hsuan Chuang 	*new_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_FCSFAIL |
275e3037485SYan-Hsuan Chuang 		      FIF_BCN_PRBRESP_PROMISC;
276e3037485SYan-Hsuan Chuang 
277e3037485SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
278e3037485SYan-Hsuan Chuang 
27927e117e4SYan-Hsuan Chuang 	rtw_leave_lps_deep(rtwdev);
28027e117e4SYan-Hsuan Chuang 
281e3037485SYan-Hsuan Chuang 	if (changed_flags & FIF_ALLMULTI) {
282e3037485SYan-Hsuan Chuang 		if (*new_flags & FIF_ALLMULTI)
283*153267f9SChih-Kang Chang 			rtwdev->hal.rcr |= BIT_AM;
284e3037485SYan-Hsuan Chuang 		else
285*153267f9SChih-Kang Chang 			rtwdev->hal.rcr &= ~(BIT_AM);
286e3037485SYan-Hsuan Chuang 	}
287e3037485SYan-Hsuan Chuang 	if (changed_flags & FIF_FCSFAIL) {
288e3037485SYan-Hsuan Chuang 		if (*new_flags & FIF_FCSFAIL)
289e3037485SYan-Hsuan Chuang 			rtwdev->hal.rcr |= BIT_ACRC32;
290e3037485SYan-Hsuan Chuang 		else
291e3037485SYan-Hsuan Chuang 			rtwdev->hal.rcr &= ~(BIT_ACRC32);
292e3037485SYan-Hsuan Chuang 	}
293e3037485SYan-Hsuan Chuang 	if (changed_flags & FIF_OTHER_BSS) {
294e3037485SYan-Hsuan Chuang 		if (*new_flags & FIF_OTHER_BSS)
295e3037485SYan-Hsuan Chuang 			rtwdev->hal.rcr |= BIT_AAP;
296e3037485SYan-Hsuan Chuang 		else
297e3037485SYan-Hsuan Chuang 			rtwdev->hal.rcr &= ~(BIT_AAP);
298e3037485SYan-Hsuan Chuang 	}
299e3037485SYan-Hsuan Chuang 	if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
300e3037485SYan-Hsuan Chuang 		if (*new_flags & FIF_BCN_PRBRESP_PROMISC)
301e3037485SYan-Hsuan Chuang 			rtwdev->hal.rcr &= ~(BIT_CBSSID_BCN | BIT_CBSSID_DATA);
302e3037485SYan-Hsuan Chuang 		else
303e3037485SYan-Hsuan Chuang 			rtwdev->hal.rcr |= BIT_CBSSID_BCN;
304e3037485SYan-Hsuan Chuang 	}
305e3037485SYan-Hsuan Chuang 
306e3037485SYan-Hsuan Chuang 	rtw_dbg(rtwdev, RTW_DBG_RX,
307e3037485SYan-Hsuan Chuang 		"config rx filter, changed=0x%08x, new=0x%08x, rcr=0x%08x\n",
308e3037485SYan-Hsuan Chuang 		changed_flags, *new_flags, rtwdev->hal.rcr);
309e3037485SYan-Hsuan Chuang 
310e3037485SYan-Hsuan Chuang 	rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
311e3037485SYan-Hsuan Chuang 
312e3037485SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
313e3037485SYan-Hsuan Chuang }
314e3037485SYan-Hsuan Chuang 
315bf06c7ecSYan-Hsuan Chuang /* Only have one group of EDCA parameters now */
316bf06c7ecSYan-Hsuan Chuang static const u32 ac_to_edca_param[IEEE80211_NUM_ACS] = {
317bf06c7ecSYan-Hsuan Chuang 	[IEEE80211_AC_VO] = REG_EDCA_VO_PARAM,
318bf06c7ecSYan-Hsuan Chuang 	[IEEE80211_AC_VI] = REG_EDCA_VI_PARAM,
319bf06c7ecSYan-Hsuan Chuang 	[IEEE80211_AC_BE] = REG_EDCA_BE_PARAM,
320bf06c7ecSYan-Hsuan Chuang 	[IEEE80211_AC_BK] = REG_EDCA_BK_PARAM,
321bf06c7ecSYan-Hsuan Chuang };
322bf06c7ecSYan-Hsuan Chuang 
rtw_aifsn_to_aifs(struct rtw_dev * rtwdev,struct rtw_vif * rtwvif,u8 aifsn)323bf06c7ecSYan-Hsuan Chuang static u8 rtw_aifsn_to_aifs(struct rtw_dev *rtwdev,
324bf06c7ecSYan-Hsuan Chuang 			    struct rtw_vif *rtwvif, u8 aifsn)
325bf06c7ecSYan-Hsuan Chuang {
326bf06c7ecSYan-Hsuan Chuang 	struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
327bf06c7ecSYan-Hsuan Chuang 	u8 slot_time;
328bf06c7ecSYan-Hsuan Chuang 	u8 sifs;
329bf06c7ecSYan-Hsuan Chuang 
330bf06c7ecSYan-Hsuan Chuang 	slot_time = vif->bss_conf.use_short_slot ? 9 : 20;
331bf06c7ecSYan-Hsuan Chuang 	sifs = rtwdev->hal.current_band_type == RTW_BAND_5G ? 16 : 10;
332bf06c7ecSYan-Hsuan Chuang 
333bf06c7ecSYan-Hsuan Chuang 	return aifsn * slot_time + sifs;
334bf06c7ecSYan-Hsuan Chuang }
335bf06c7ecSYan-Hsuan Chuang 
__rtw_conf_tx(struct rtw_dev * rtwdev,struct rtw_vif * rtwvif,u16 ac)336bf06c7ecSYan-Hsuan Chuang static void __rtw_conf_tx(struct rtw_dev *rtwdev,
337bf06c7ecSYan-Hsuan Chuang 			  struct rtw_vif *rtwvif, u16 ac)
338bf06c7ecSYan-Hsuan Chuang {
339bf06c7ecSYan-Hsuan Chuang 	struct ieee80211_tx_queue_params *params = &rtwvif->tx_params[ac];
340bf06c7ecSYan-Hsuan Chuang 	u32 edca_param = ac_to_edca_param[ac];
341bf06c7ecSYan-Hsuan Chuang 	u8 ecw_max, ecw_min;
342bf06c7ecSYan-Hsuan Chuang 	u8 aifs;
343bf06c7ecSYan-Hsuan Chuang 
344bf06c7ecSYan-Hsuan Chuang 	/* 2^ecw - 1 = cw; ecw = log2(cw + 1) */
345bf06c7ecSYan-Hsuan Chuang 	ecw_max = ilog2(params->cw_max + 1);
346bf06c7ecSYan-Hsuan Chuang 	ecw_min = ilog2(params->cw_min + 1);
347bf06c7ecSYan-Hsuan Chuang 	aifs = rtw_aifsn_to_aifs(rtwdev, rtwvif, params->aifs);
348bf06c7ecSYan-Hsuan Chuang 	rtw_write32_mask(rtwdev, edca_param, BIT_MASK_TXOP_LMT, params->txop);
349bf06c7ecSYan-Hsuan Chuang 	rtw_write32_mask(rtwdev, edca_param, BIT_MASK_CWMAX, ecw_max);
350bf06c7ecSYan-Hsuan Chuang 	rtw_write32_mask(rtwdev, edca_param, BIT_MASK_CWMIN, ecw_min);
351bf06c7ecSYan-Hsuan Chuang 	rtw_write32_mask(rtwdev, edca_param, BIT_MASK_AIFS, aifs);
352bf06c7ecSYan-Hsuan Chuang }
353bf06c7ecSYan-Hsuan Chuang 
rtw_conf_tx(struct rtw_dev * rtwdev,struct rtw_vif * rtwvif)354bf06c7ecSYan-Hsuan Chuang static void rtw_conf_tx(struct rtw_dev *rtwdev,
355bf06c7ecSYan-Hsuan Chuang 			struct rtw_vif *rtwvif)
356bf06c7ecSYan-Hsuan Chuang {
357bf06c7ecSYan-Hsuan Chuang 	u16 ac;
358bf06c7ecSYan-Hsuan Chuang 
359bf06c7ecSYan-Hsuan Chuang 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++)
360bf06c7ecSYan-Hsuan Chuang 		__rtw_conf_tx(rtwdev, rtwvif, ac);
361bf06c7ecSYan-Hsuan Chuang }
362bf06c7ecSYan-Hsuan Chuang 
rtw_ops_bss_info_changed(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * conf,u64 changed)363e3037485SYan-Hsuan Chuang static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw,
364e3037485SYan-Hsuan Chuang 				     struct ieee80211_vif *vif,
365e3037485SYan-Hsuan Chuang 				     struct ieee80211_bss_conf *conf,
3667b7090b4SJohannes Berg 				     u64 changed)
367e3037485SYan-Hsuan Chuang {
368e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
369e3037485SYan-Hsuan Chuang 	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
3707ed9e062SChing-Te Ku 	struct rtw_coex *coex = &rtwdev->coex;
3717ed9e062SChing-Te Ku 	struct rtw_coex_stat *coex_stat = &coex->stat;
372e3037485SYan-Hsuan Chuang 	u32 config = 0;
373e3037485SYan-Hsuan Chuang 
374e3037485SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
375e3037485SYan-Hsuan Chuang 
37627e117e4SYan-Hsuan Chuang 	rtw_leave_lps_deep(rtwdev);
37727e117e4SYan-Hsuan Chuang 
378e3037485SYan-Hsuan Chuang 	if (changed & BSS_CHANGED_ASSOC) {
3795c831644STzu-En Huang 		rtw_vif_assoc_changed(rtwvif, conf);
380f276e20bSJohannes Berg 		if (vif->cfg.assoc) {
3814136214fSYan-Hsuan Chuang 			rtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_FINISH);
382e3037485SYan-Hsuan Chuang 
383895c096dSYan-Hsuan Chuang 			rtw_fw_download_rsvd_page(rtwdev);
384e3037485SYan-Hsuan Chuang 			rtw_send_rsvd_page_h2c(rtwdev);
38528c11c29SPo-Hao Huang 			rtw_fw_default_port(rtwdev, rtwvif);
386f276e20bSJohannes Berg 			rtw_coex_media_status_notify(rtwdev, vif->cfg.assoc);
3870bd95573STzu-En Huang 			if (rtw_bf_support)
3880bd95573STzu-En Huang 				rtw_bf_assoc(rtwdev, vif, conf);
389e3037485SYan-Hsuan Chuang 		} else {
39027e117e4SYan-Hsuan Chuang 			rtw_leave_lps(rtwdev);
3910bd95573STzu-En Huang 			rtw_bf_disassoc(rtwdev, vif, conf);
39210d162b2SPo-Hao Huang 			/* Abort ongoing scan if cancel_scan isn't issued
39310d162b2SPo-Hao Huang 			 * when disconnected by peer
39410d162b2SPo-Hao Huang 			 */
39510d162b2SPo-Hao Huang 			if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
396a1b8015dSPo-Hao Huang 				rtw_hw_scan_abort(rtwdev);
397a1b8015dSPo-Hao Huang 
398e3037485SYan-Hsuan Chuang 		}
399e3037485SYan-Hsuan Chuang 
400e3037485SYan-Hsuan Chuang 		config |= PORT_SET_NET_TYPE;
401e3037485SYan-Hsuan Chuang 		config |= PORT_SET_AID;
402e3037485SYan-Hsuan Chuang 	}
403e3037485SYan-Hsuan Chuang 
404e3037485SYan-Hsuan Chuang 	if (changed & BSS_CHANGED_BSSID) {
405e3037485SYan-Hsuan Chuang 		ether_addr_copy(rtwvif->bssid, conf->bssid);
406e3037485SYan-Hsuan Chuang 		config |= PORT_SET_BSSID;
40796fbb84dSPo-Hao Huang 		if (!rtw_core_check_sta_active(rtwdev))
40879ba1062SChih-Kang Chang 			rtw_clear_op_chan(rtwdev);
40979ba1062SChih-Kang Chang 		else
41068c53914SChih-Kang Chang 			rtw_store_op_chan(rtwdev, true);
411e3037485SYan-Hsuan Chuang 	}
412e3037485SYan-Hsuan Chuang 
4137ed9e062SChing-Te Ku 	if (changed & BSS_CHANGED_BEACON_INT) {
4147ed9e062SChing-Te Ku 		if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION)
4157ed9e062SChing-Te Ku 			coex_stat->wl_beacon_interval = conf->beacon_int;
4167ed9e062SChing-Te Ku 	}
4177ed9e062SChing-Te Ku 
418f2217968SPo-Hao Huang 	if (changed & BSS_CHANGED_BEACON) {
419f2217968SPo-Hao Huang 		rtw_set_dtim_period(rtwdev, conf->dtim_period);
420895c096dSYan-Hsuan Chuang 		rtw_fw_download_rsvd_page(rtwdev);
421982f4a20SPo-Hao Huang 		rtw_send_rsvd_page_h2c(rtwdev);
422f2217968SPo-Hao Huang 	}
423e3037485SYan-Hsuan Chuang 
424752310edSYan-Hsuan Chuang 	if (changed & BSS_CHANGED_BEACON_ENABLED) {
425752310edSYan-Hsuan Chuang 		if (conf->enable_beacon)
426752310edSYan-Hsuan Chuang 			rtw_write32_set(rtwdev, REG_FWHW_TXQ_CTRL,
427752310edSYan-Hsuan Chuang 					BIT_EN_BCNQ_DL);
428752310edSYan-Hsuan Chuang 		else
429752310edSYan-Hsuan Chuang 			rtw_write32_clr(rtwdev, REG_FWHW_TXQ_CTRL,
430752310edSYan-Hsuan Chuang 					BIT_EN_BCNQ_DL);
431752310edSYan-Hsuan Chuang 	}
432cd96e22bSPo-Hao Huang 	if (changed & BSS_CHANGED_CQM)
433cd96e22bSPo-Hao Huang 		rtw_fw_beacon_filter_config(rtwdev, true, vif);
434752310edSYan-Hsuan Chuang 
43593ae973fSPing-Ke Shih 	if (changed & BSS_CHANGED_MU_GROUPS)
43693ae973fSPing-Ke Shih 		rtw_chip_set_gid_table(rtwdev, vif, conf);
4370bd95573STzu-En Huang 
438bf06c7ecSYan-Hsuan Chuang 	if (changed & BSS_CHANGED_ERP_SLOT)
439bf06c7ecSYan-Hsuan Chuang 		rtw_conf_tx(rtwdev, rtwvif);
440bf06c7ecSYan-Hsuan Chuang 
4413918dd01SPing-Ke Shih 	if (changed & BSS_CHANGED_PS)
4423918dd01SPing-Ke Shih 		rtw_recalc_lps(rtwdev, NULL);
4433918dd01SPing-Ke Shih 
444e3037485SYan-Hsuan Chuang 	rtw_vif_port_config(rtwdev, rtwvif, config);
445e3037485SYan-Hsuan Chuang 
446e3037485SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
447e3037485SYan-Hsuan Chuang }
448e3037485SYan-Hsuan Chuang 
rtw_ops_start_ap(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)449ae7ba17bSShaul Triebitz static int rtw_ops_start_ap(struct ieee80211_hw *hw,
450b327c84cSGregory Greenman 			    struct ieee80211_vif *vif,
451b327c84cSGregory Greenman 			    struct ieee80211_bss_conf *link_conf)
452f5207c12SPing-Ke Shih {
453f5207c12SPing-Ke Shih 	struct rtw_dev *rtwdev = hw->priv;
454dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
455f5207c12SPing-Ke Shih 
456f5207c12SPing-Ke Shih 	mutex_lock(&rtwdev->mutex);
457076f786aSPo-Hao Huang 	rtw_write32_set(rtwdev, REG_TCR, BIT_TCR_UPDATE_HGQMD);
4585ec69129SPo-Hao Huang 	rtwdev->ap_active = true;
45996fbb84dSPo-Hao Huang 	rtw_store_op_chan(rtwdev, true);
460f5207c12SPing-Ke Shih 	chip->ops->phy_calibration(rtwdev);
461f5207c12SPing-Ke Shih 	mutex_unlock(&rtwdev->mutex);
462f5207c12SPing-Ke Shih 
463f5207c12SPing-Ke Shih 	return 0;
464f5207c12SPing-Ke Shih }
465f5207c12SPing-Ke Shih 
rtw_ops_stop_ap(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_bss_conf * link_conf)4665ec69129SPo-Hao Huang static void rtw_ops_stop_ap(struct ieee80211_hw *hw,
4675ec69129SPo-Hao Huang 			    struct ieee80211_vif *vif,
4685ec69129SPo-Hao Huang 			    struct ieee80211_bss_conf *link_conf)
4695ec69129SPo-Hao Huang {
4705ec69129SPo-Hao Huang 	struct rtw_dev *rtwdev = hw->priv;
4715ec69129SPo-Hao Huang 
4725ec69129SPo-Hao Huang 	mutex_lock(&rtwdev->mutex);
473076f786aSPo-Hao Huang 	rtw_write32_clr(rtwdev, REG_TCR, BIT_TCR_UPDATE_HGQMD);
4745ec69129SPo-Hao Huang 	rtwdev->ap_active = false;
47596fbb84dSPo-Hao Huang 	if (!rtw_core_check_sta_active(rtwdev))
47696fbb84dSPo-Hao Huang 		rtw_clear_op_chan(rtwdev);
4775ec69129SPo-Hao Huang 	mutex_unlock(&rtwdev->mutex);
4785ec69129SPo-Hao Huang }
4795ec69129SPo-Hao Huang 
rtw_ops_conf_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,unsigned int link_id,u16 ac,const struct ieee80211_tx_queue_params * params)480bf06c7ecSYan-Hsuan Chuang static int rtw_ops_conf_tx(struct ieee80211_hw *hw,
481b3e2130bSJohannes Berg 			   struct ieee80211_vif *vif,
482b3e2130bSJohannes Berg 			   unsigned int link_id, u16 ac,
483bf06c7ecSYan-Hsuan Chuang 			   const struct ieee80211_tx_queue_params *params)
484bf06c7ecSYan-Hsuan Chuang {
485bf06c7ecSYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
486bf06c7ecSYan-Hsuan Chuang 	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
487bf06c7ecSYan-Hsuan Chuang 
488bf06c7ecSYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
489bf06c7ecSYan-Hsuan Chuang 
490bf06c7ecSYan-Hsuan Chuang 	rtw_leave_lps_deep(rtwdev);
491bf06c7ecSYan-Hsuan Chuang 
492bf06c7ecSYan-Hsuan Chuang 	rtwvif->tx_params[ac] = *params;
493bf06c7ecSYan-Hsuan Chuang 	__rtw_conf_tx(rtwdev, rtwvif, ac);
494bf06c7ecSYan-Hsuan Chuang 
495bf06c7ecSYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
496bf06c7ecSYan-Hsuan Chuang 
497bf06c7ecSYan-Hsuan Chuang 	return 0;
498bf06c7ecSYan-Hsuan Chuang }
499bf06c7ecSYan-Hsuan Chuang 
rtw_ops_sta_add(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)500e3037485SYan-Hsuan Chuang static int rtw_ops_sta_add(struct ieee80211_hw *hw,
501e3037485SYan-Hsuan Chuang 			   struct ieee80211_vif *vif,
502e3037485SYan-Hsuan Chuang 			   struct ieee80211_sta *sta)
503e3037485SYan-Hsuan Chuang {
504e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
505e3037485SYan-Hsuan Chuang 	int ret = 0;
506e3037485SYan-Hsuan Chuang 
507e3037485SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
5085c831644STzu-En Huang 	ret = rtw_sta_add(rtwdev, sta, vif);
509e3037485SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
5105c831644STzu-En Huang 
511e3037485SYan-Hsuan Chuang 	return ret;
512e3037485SYan-Hsuan Chuang }
513e3037485SYan-Hsuan Chuang 
rtw_ops_sta_remove(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta)514e3037485SYan-Hsuan Chuang static int rtw_ops_sta_remove(struct ieee80211_hw *hw,
515e3037485SYan-Hsuan Chuang 			      struct ieee80211_vif *vif,
516e3037485SYan-Hsuan Chuang 			      struct ieee80211_sta *sta)
517e3037485SYan-Hsuan Chuang {
518e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
519e3037485SYan-Hsuan Chuang 
520e3037485SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
52169020957SSascha Hauer 	rtw_fw_beacon_filter_config(rtwdev, false, vif);
5225c831644STzu-En Huang 	rtw_sta_remove(rtwdev, sta, true);
523e3037485SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
5245c831644STzu-En Huang 
525e3037485SYan-Hsuan Chuang 	return 0;
526e3037485SYan-Hsuan Chuang }
527e3037485SYan-Hsuan Chuang 
rtw_ops_set_tim(struct ieee80211_hw * hw,struct ieee80211_sta * sta,bool set)528f2217968SPo-Hao Huang static int rtw_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
529f2217968SPo-Hao Huang 			   bool set)
530f2217968SPo-Hao Huang {
531f2217968SPo-Hao Huang 	struct rtw_dev *rtwdev = hw->priv;
532f2217968SPo-Hao Huang 
5337711fe71SPing-Ke Shih 	ieee80211_queue_work(hw, &rtwdev->update_beacon_work);
534f2217968SPo-Hao Huang 
535f2217968SPo-Hao Huang 	return 0;
536f2217968SPo-Hao Huang }
537f2217968SPo-Hao Huang 
rtw_ops_set_key(struct ieee80211_hw * hw,enum set_key_cmd cmd,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct ieee80211_key_conf * key)538e3037485SYan-Hsuan Chuang static int rtw_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
539e3037485SYan-Hsuan Chuang 			   struct ieee80211_vif *vif, struct ieee80211_sta *sta,
540e3037485SYan-Hsuan Chuang 			   struct ieee80211_key_conf *key)
541e3037485SYan-Hsuan Chuang {
542e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
543e3037485SYan-Hsuan Chuang 	struct rtw_sec_desc *sec = &rtwdev->sec;
544e3037485SYan-Hsuan Chuang 	u8 hw_key_type;
545e3037485SYan-Hsuan Chuang 	u8 hw_key_idx;
546e3037485SYan-Hsuan Chuang 	int ret = 0;
547e3037485SYan-Hsuan Chuang 
548e3037485SYan-Hsuan Chuang 	switch (key->cipher) {
549e3037485SYan-Hsuan Chuang 	case WLAN_CIPHER_SUITE_WEP40:
550e3037485SYan-Hsuan Chuang 		hw_key_type = RTW_CAM_WEP40;
551e3037485SYan-Hsuan Chuang 		break;
552e3037485SYan-Hsuan Chuang 	case WLAN_CIPHER_SUITE_WEP104:
553e3037485SYan-Hsuan Chuang 		hw_key_type = RTW_CAM_WEP104;
554e3037485SYan-Hsuan Chuang 		break;
555e3037485SYan-Hsuan Chuang 	case WLAN_CIPHER_SUITE_TKIP:
556e3037485SYan-Hsuan Chuang 		hw_key_type = RTW_CAM_TKIP;
557e3037485SYan-Hsuan Chuang 		key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
558e3037485SYan-Hsuan Chuang 		break;
559e3037485SYan-Hsuan Chuang 	case WLAN_CIPHER_SUITE_CCMP:
560e3037485SYan-Hsuan Chuang 		hw_key_type = RTW_CAM_AES;
561e3037485SYan-Hsuan Chuang 		key->flags |= IEEE80211_KEY_FLAG_SW_MGMT_TX;
562e3037485SYan-Hsuan Chuang 		break;
563e3037485SYan-Hsuan Chuang 	case WLAN_CIPHER_SUITE_AES_CMAC:
564e3037485SYan-Hsuan Chuang 	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
565e3037485SYan-Hsuan Chuang 	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
566e3037485SYan-Hsuan Chuang 	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
56755cc8442SPing-Ke Shih 	case WLAN_CIPHER_SUITE_CCMP_256:
56855cc8442SPing-Ke Shih 	case WLAN_CIPHER_SUITE_GCMP:
56955cc8442SPing-Ke Shih 	case WLAN_CIPHER_SUITE_GCMP_256:
570e3037485SYan-Hsuan Chuang 		/* suppress error messages */
571e3037485SYan-Hsuan Chuang 		return -EOPNOTSUPP;
572e3037485SYan-Hsuan Chuang 	default:
573e3037485SYan-Hsuan Chuang 		return -ENOTSUPP;
574e3037485SYan-Hsuan Chuang 	}
575e3037485SYan-Hsuan Chuang 
576e3037485SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
577e3037485SYan-Hsuan Chuang 
57827e117e4SYan-Hsuan Chuang 	rtw_leave_lps_deep(rtwdev);
57927e117e4SYan-Hsuan Chuang 
580e3037485SYan-Hsuan Chuang 	if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
581e3037485SYan-Hsuan Chuang 		hw_key_idx = rtw_sec_get_free_cam(sec);
582e3037485SYan-Hsuan Chuang 	} else {
583e3037485SYan-Hsuan Chuang 		/* multiple interfaces? */
584e3037485SYan-Hsuan Chuang 		hw_key_idx = key->keyidx;
585e3037485SYan-Hsuan Chuang 	}
586e3037485SYan-Hsuan Chuang 
587e3037485SYan-Hsuan Chuang 	if (hw_key_idx > sec->total_cam_num) {
588e3037485SYan-Hsuan Chuang 		ret = -ENOSPC;
589e3037485SYan-Hsuan Chuang 		goto out;
590e3037485SYan-Hsuan Chuang 	}
591e3037485SYan-Hsuan Chuang 
592e3037485SYan-Hsuan Chuang 	switch (cmd) {
593e3037485SYan-Hsuan Chuang 	case SET_KEY:
594e3037485SYan-Hsuan Chuang 		/* need sw generated IV */
595e3037485SYan-Hsuan Chuang 		key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
596e3037485SYan-Hsuan Chuang 		key->hw_key_idx = hw_key_idx;
597e3037485SYan-Hsuan Chuang 		rtw_sec_write_cam(rtwdev, sec, sta, key,
598e3037485SYan-Hsuan Chuang 				  hw_key_type, hw_key_idx);
599e3037485SYan-Hsuan Chuang 		break;
600e3037485SYan-Hsuan Chuang 	case DISABLE_KEY:
6017b33ec8bSZong-Zhe Yang 		rtw_hci_flush_all_queues(rtwdev, false);
6025dc32b8aSTzu-En Huang 		rtw_mac_flush_all_queues(rtwdev, false);
603e3037485SYan-Hsuan Chuang 		rtw_sec_clear_cam(rtwdev, sec, key->hw_key_idx);
604e3037485SYan-Hsuan Chuang 		break;
605e3037485SYan-Hsuan Chuang 	}
606e3037485SYan-Hsuan Chuang 
60704b786e0SYan-Hsuan Chuang 	/* download new cam settings for PG to backup */
608fc3ac64aSChin-Yen Lee 	if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG)
609895c096dSYan-Hsuan Chuang 		rtw_fw_download_rsvd_page(rtwdev);
61004b786e0SYan-Hsuan Chuang 
611e3037485SYan-Hsuan Chuang out:
612e3037485SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
613e3037485SYan-Hsuan Chuang 
614e3037485SYan-Hsuan Chuang 	return ret;
615e3037485SYan-Hsuan Chuang }
616e3037485SYan-Hsuan Chuang 
rtw_ops_ampdu_action(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_ampdu_params * params)617e3037485SYan-Hsuan Chuang static int rtw_ops_ampdu_action(struct ieee80211_hw *hw,
618e3037485SYan-Hsuan Chuang 				struct ieee80211_vif *vif,
619e3037485SYan-Hsuan Chuang 				struct ieee80211_ampdu_params *params)
620e3037485SYan-Hsuan Chuang {
621e3037485SYan-Hsuan Chuang 	struct ieee80211_sta *sta = params->sta;
622e3037485SYan-Hsuan Chuang 	u16 tid = params->tid;
62346ebb174SYan-Hsuan Chuang 	struct ieee80211_txq *txq = sta->txq[tid];
62446ebb174SYan-Hsuan Chuang 	struct rtw_txq *rtwtxq = (struct rtw_txq *)txq->drv_priv;
625e3037485SYan-Hsuan Chuang 
626e3037485SYan-Hsuan Chuang 	switch (params->action) {
627e3037485SYan-Hsuan Chuang 	case IEEE80211_AMPDU_TX_START:
6282ce113deSJohannes Berg 		return IEEE80211_AMPDU_TX_START_IMMEDIATE;
629e3037485SYan-Hsuan Chuang 	case IEEE80211_AMPDU_TX_STOP_CONT:
630e3037485SYan-Hsuan Chuang 	case IEEE80211_AMPDU_TX_STOP_FLUSH:
631e3037485SYan-Hsuan Chuang 	case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
63246ebb174SYan-Hsuan Chuang 		clear_bit(RTW_TXQ_AMPDU, &rtwtxq->flags);
633e3037485SYan-Hsuan Chuang 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
634e3037485SYan-Hsuan Chuang 		break;
635e3037485SYan-Hsuan Chuang 	case IEEE80211_AMPDU_TX_OPERATIONAL:
63646ebb174SYan-Hsuan Chuang 		set_bit(RTW_TXQ_AMPDU, &rtwtxq->flags);
63746ebb174SYan-Hsuan Chuang 		break;
638e3037485SYan-Hsuan Chuang 	case IEEE80211_AMPDU_RX_START:
639e3037485SYan-Hsuan Chuang 	case IEEE80211_AMPDU_RX_STOP:
640e3037485SYan-Hsuan Chuang 		break;
641e3037485SYan-Hsuan Chuang 	default:
642e3037485SYan-Hsuan Chuang 		WARN_ON(1);
643e3037485SYan-Hsuan Chuang 		return -ENOTSUPP;
644e3037485SYan-Hsuan Chuang 	}
645e3037485SYan-Hsuan Chuang 
646e3037485SYan-Hsuan Chuang 	return 0;
647e3037485SYan-Hsuan Chuang }
648e3037485SYan-Hsuan Chuang 
rtw_ops_can_aggregate_in_amsdu(struct ieee80211_hw * hw,struct sk_buff * head,struct sk_buff * skb)64974c3d72cSYan-Hsuan Chuang static bool rtw_ops_can_aggregate_in_amsdu(struct ieee80211_hw *hw,
65074c3d72cSYan-Hsuan Chuang 					   struct sk_buff *head,
65174c3d72cSYan-Hsuan Chuang 					   struct sk_buff *skb)
65274c3d72cSYan-Hsuan Chuang {
65374c3d72cSYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
65474c3d72cSYan-Hsuan Chuang 	struct rtw_hal *hal = &rtwdev->hal;
65574c3d72cSYan-Hsuan Chuang 
65674c3d72cSYan-Hsuan Chuang 	/* we don't want to enable TX AMSDU on 2.4G */
65774c3d72cSYan-Hsuan Chuang 	if (hal->current_band_type == RTW_BAND_2G)
65874c3d72cSYan-Hsuan Chuang 		return false;
65974c3d72cSYan-Hsuan Chuang 
66074c3d72cSYan-Hsuan Chuang 	return true;
66174c3d72cSYan-Hsuan Chuang }
66274c3d72cSYan-Hsuan Chuang 
rtw_ops_sw_scan_start(struct ieee80211_hw * hw,struct ieee80211_vif * vif,const u8 * mac_addr)663e3037485SYan-Hsuan Chuang static void rtw_ops_sw_scan_start(struct ieee80211_hw *hw,
664e3037485SYan-Hsuan Chuang 				  struct ieee80211_vif *vif,
665e3037485SYan-Hsuan Chuang 				  const u8 *mac_addr)
666e3037485SYan-Hsuan Chuang {
667e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
668e3037485SYan-Hsuan Chuang 	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
669e3037485SYan-Hsuan Chuang 
67044cc4c63SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
67110d162b2SPo-Hao Huang 	rtw_core_scan_start(rtwdev, rtwvif, mac_addr, false);
67244cc4c63SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
673e3037485SYan-Hsuan Chuang }
674e3037485SYan-Hsuan Chuang 
rtw_ops_sw_scan_complete(struct ieee80211_hw * hw,struct ieee80211_vif * vif)675e3037485SYan-Hsuan Chuang static void rtw_ops_sw_scan_complete(struct ieee80211_hw *hw,
676e3037485SYan-Hsuan Chuang 				     struct ieee80211_vif *vif)
677e3037485SYan-Hsuan Chuang {
678e3037485SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
67944cc4c63SYan-Hsuan Chuang 
68044cc4c63SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
681c17f2716SPo-Hao Huang 	rtw_core_scan_complete(rtwdev, vif, false);
6824136214fSYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
6834136214fSYan-Hsuan Chuang }
6844136214fSYan-Hsuan Chuang 
rtw_ops_mgd_prepare_tx(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_prep_tx_info * info)6854136214fSYan-Hsuan Chuang static void rtw_ops_mgd_prepare_tx(struct ieee80211_hw *hw,
6864136214fSYan-Hsuan Chuang 				   struct ieee80211_vif *vif,
68715fae341SJohannes Berg 				   struct ieee80211_prep_tx_info *info)
6884136214fSYan-Hsuan Chuang {
6894136214fSYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
6904136214fSYan-Hsuan Chuang 
6914136214fSYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
69227e117e4SYan-Hsuan Chuang 	rtw_leave_lps_deep(rtwdev);
6934136214fSYan-Hsuan Chuang 	rtw_coex_connect_notify(rtwdev, COEX_ASSOCIATE_START);
6947a242fb6SPing-Ke Shih 	rtw_chip_prepare_tx(rtwdev);
69544cc4c63SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
696e3037485SYan-Hsuan Chuang }
697e3037485SYan-Hsuan Chuang 
rtw_ops_set_rts_threshold(struct ieee80211_hw * hw,u32 value)698942e2a5dSYan-Hsuan Chuang static int rtw_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
699942e2a5dSYan-Hsuan Chuang {
700942e2a5dSYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
701942e2a5dSYan-Hsuan Chuang 
702942e2a5dSYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
703942e2a5dSYan-Hsuan Chuang 	rtwdev->rts_threshold = value;
704942e2a5dSYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
705942e2a5dSYan-Hsuan Chuang 
706942e2a5dSYan-Hsuan Chuang 	return 0;
707942e2a5dSYan-Hsuan Chuang }
708942e2a5dSYan-Hsuan Chuang 
rtw_ops_sta_statistics(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,struct station_info * sinfo)709699c7730STzu-En Huang static void rtw_ops_sta_statistics(struct ieee80211_hw *hw,
710699c7730STzu-En Huang 				   struct ieee80211_vif *vif,
711699c7730STzu-En Huang 				   struct ieee80211_sta *sta,
712699c7730STzu-En Huang 				   struct station_info *sinfo)
713699c7730STzu-En Huang {
714699c7730STzu-En Huang 	struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
715699c7730STzu-En Huang 
716699c7730STzu-En Huang 	sinfo->txrate = si->ra_report.txrate;
717699c7730STzu-En Huang 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
718699c7730STzu-En Huang }
719699c7730STzu-En Huang 
rtw_ops_flush(struct ieee80211_hw * hw,struct ieee80211_vif * vif,u32 queues,bool drop)7201131ad7fSYan-Hsuan Chuang static void rtw_ops_flush(struct ieee80211_hw *hw,
7211131ad7fSYan-Hsuan Chuang 			  struct ieee80211_vif *vif,
7221131ad7fSYan-Hsuan Chuang 			  u32 queues, bool drop)
7231131ad7fSYan-Hsuan Chuang {
7241131ad7fSYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
7251131ad7fSYan-Hsuan Chuang 
7261131ad7fSYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
7271131ad7fSYan-Hsuan Chuang 	rtw_leave_lps_deep(rtwdev);
7281131ad7fSYan-Hsuan Chuang 
7297b33ec8bSZong-Zhe Yang 	rtw_hci_flush_queues(rtwdev, queues, drop);
7301131ad7fSYan-Hsuan Chuang 	rtw_mac_flush_queues(rtwdev, queues, drop);
7311131ad7fSYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
7321131ad7fSYan-Hsuan Chuang }
7331131ad7fSYan-Hsuan Chuang 
734f39e9bd4STzu-En Huang struct rtw_iter_bitrate_mask_data {
735f39e9bd4STzu-En Huang 	struct rtw_dev *rtwdev;
736f39e9bd4STzu-En Huang 	struct ieee80211_vif *vif;
737f39e9bd4STzu-En Huang 	const struct cfg80211_bitrate_mask *mask;
738f39e9bd4STzu-En Huang };
739f39e9bd4STzu-En Huang 
rtw_ra_mask_info_update_iter(void * data,struct ieee80211_sta * sta)740f39e9bd4STzu-En Huang static void rtw_ra_mask_info_update_iter(void *data, struct ieee80211_sta *sta)
741f39e9bd4STzu-En Huang {
742f39e9bd4STzu-En Huang 	struct rtw_iter_bitrate_mask_data *br_data = data;
743f39e9bd4STzu-En Huang 	struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
744f39e9bd4STzu-En Huang 
745f39e9bd4STzu-En Huang 	if (si->vif != br_data->vif)
746f39e9bd4STzu-En Huang 		return;
747f39e9bd4STzu-En Huang 
748f39e9bd4STzu-En Huang 	/* free previous mask setting */
749f39e9bd4STzu-En Huang 	kfree(si->mask);
750f39e9bd4STzu-En Huang 	si->mask = kmemdup(br_data->mask, sizeof(struct cfg80211_bitrate_mask),
751f39e9bd4STzu-En Huang 			   GFP_ATOMIC);
752f39e9bd4STzu-En Huang 	if (!si->mask) {
753f39e9bd4STzu-En Huang 		si->use_cfg_mask = false;
754f39e9bd4STzu-En Huang 		return;
755f39e9bd4STzu-En Huang 	}
756f39e9bd4STzu-En Huang 
757f39e9bd4STzu-En Huang 	si->use_cfg_mask = true;
758c1edc864SPo-Hao Huang 	rtw_update_sta_info(br_data->rtwdev, si, true);
759f39e9bd4STzu-En Huang }
760f39e9bd4STzu-En Huang 
rtw_ra_mask_info_update(struct rtw_dev * rtwdev,struct ieee80211_vif * vif,const struct cfg80211_bitrate_mask * mask)761f39e9bd4STzu-En Huang static void rtw_ra_mask_info_update(struct rtw_dev *rtwdev,
762f39e9bd4STzu-En Huang 				    struct ieee80211_vif *vif,
763f39e9bd4STzu-En Huang 				    const struct cfg80211_bitrate_mask *mask)
764f39e9bd4STzu-En Huang {
765f39e9bd4STzu-En Huang 	struct rtw_iter_bitrate_mask_data br_data;
766f39e9bd4STzu-En Huang 
767f39e9bd4STzu-En Huang 	br_data.rtwdev = rtwdev;
768f39e9bd4STzu-En Huang 	br_data.vif = vif;
769f39e9bd4STzu-En Huang 	br_data.mask = mask;
7702931978cSMartin Blumenstingl 	rtw_iterate_stas(rtwdev, rtw_ra_mask_info_update_iter, &br_data);
771f39e9bd4STzu-En Huang }
772f39e9bd4STzu-En Huang 
rtw_ops_set_bitrate_mask(struct ieee80211_hw * hw,struct ieee80211_vif * vif,const struct cfg80211_bitrate_mask * mask)773f39e9bd4STzu-En Huang static int rtw_ops_set_bitrate_mask(struct ieee80211_hw *hw,
774f39e9bd4STzu-En Huang 				    struct ieee80211_vif *vif,
775f39e9bd4STzu-En Huang 				    const struct cfg80211_bitrate_mask *mask)
776f39e9bd4STzu-En Huang {
777f39e9bd4STzu-En Huang 	struct rtw_dev *rtwdev = hw->priv;
778f39e9bd4STzu-En Huang 
7792931978cSMartin Blumenstingl 	mutex_lock(&rtwdev->mutex);
780f39e9bd4STzu-En Huang 	rtw_ra_mask_info_update(rtwdev, vif, mask);
7812931978cSMartin Blumenstingl 	mutex_unlock(&rtwdev->mutex);
782f39e9bd4STzu-En Huang 
783f39e9bd4STzu-En Huang 	return 0;
784f39e9bd4STzu-En Huang }
785f39e9bd4STzu-En Huang 
rtw_ops_set_antenna(struct ieee80211_hw * hw,u32 tx_antenna,u32 rx_antenna)786297bcf82SYan-Hsuan Chuang static int rtw_ops_set_antenna(struct ieee80211_hw *hw,
787297bcf82SYan-Hsuan Chuang 			       u32 tx_antenna,
788297bcf82SYan-Hsuan Chuang 			       u32 rx_antenna)
789297bcf82SYan-Hsuan Chuang {
790297bcf82SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
791dcbf179cSPing-Ke Shih 	const struct rtw_chip_info *chip = rtwdev->chip;
792297bcf82SYan-Hsuan Chuang 	int ret;
793297bcf82SYan-Hsuan Chuang 
794297bcf82SYan-Hsuan Chuang 	if (!chip->ops->set_antenna)
795297bcf82SYan-Hsuan Chuang 		return -EOPNOTSUPP;
796297bcf82SYan-Hsuan Chuang 
797297bcf82SYan-Hsuan Chuang 	mutex_lock(&rtwdev->mutex);
798297bcf82SYan-Hsuan Chuang 	ret = chip->ops->set_antenna(rtwdev, tx_antenna, rx_antenna);
799297bcf82SYan-Hsuan Chuang 	mutex_unlock(&rtwdev->mutex);
800297bcf82SYan-Hsuan Chuang 
801297bcf82SYan-Hsuan Chuang 	return ret;
802297bcf82SYan-Hsuan Chuang }
803297bcf82SYan-Hsuan Chuang 
rtw_ops_get_antenna(struct ieee80211_hw * hw,u32 * tx_antenna,u32 * rx_antenna)804297bcf82SYan-Hsuan Chuang static int rtw_ops_get_antenna(struct ieee80211_hw *hw,
805297bcf82SYan-Hsuan Chuang 			       u32 *tx_antenna,
806297bcf82SYan-Hsuan Chuang 			       u32 *rx_antenna)
807297bcf82SYan-Hsuan Chuang {
808297bcf82SYan-Hsuan Chuang 	struct rtw_dev *rtwdev = hw->priv;
809297bcf82SYan-Hsuan Chuang 	struct rtw_hal *hal = &rtwdev->hal;
810297bcf82SYan-Hsuan Chuang 
811297bcf82SYan-Hsuan Chuang 	*tx_antenna = hal->antenna_tx;
812297bcf82SYan-Hsuan Chuang 	*rx_antenna = hal->antenna_rx;
813297bcf82SYan-Hsuan Chuang 
814297bcf82SYan-Hsuan Chuang 	return 0;
815297bcf82SYan-Hsuan Chuang }
816297bcf82SYan-Hsuan Chuang 
81744bc17f7SChin-Yen Lee #ifdef CONFIG_PM
rtw_ops_suspend(struct ieee80211_hw * hw,struct cfg80211_wowlan * wowlan)81844bc17f7SChin-Yen Lee static int rtw_ops_suspend(struct ieee80211_hw *hw,
81944bc17f7SChin-Yen Lee 			   struct cfg80211_wowlan *wowlan)
82044bc17f7SChin-Yen Lee {
82144bc17f7SChin-Yen Lee 	struct rtw_dev *rtwdev = hw->priv;
82244bc17f7SChin-Yen Lee 	int ret;
82344bc17f7SChin-Yen Lee 
82444bc17f7SChin-Yen Lee 	mutex_lock(&rtwdev->mutex);
82544bc17f7SChin-Yen Lee 	ret = rtw_wow_suspend(rtwdev, wowlan);
82644bc17f7SChin-Yen Lee 	if (ret)
82744bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to suspend for wow %d\n", ret);
82844bc17f7SChin-Yen Lee 	mutex_unlock(&rtwdev->mutex);
82944bc17f7SChin-Yen Lee 
83044bc17f7SChin-Yen Lee 	return ret ? 1 : 0;
83144bc17f7SChin-Yen Lee }
83244bc17f7SChin-Yen Lee 
rtw_ops_resume(struct ieee80211_hw * hw)83344bc17f7SChin-Yen Lee static int rtw_ops_resume(struct ieee80211_hw *hw)
83444bc17f7SChin-Yen Lee {
83544bc17f7SChin-Yen Lee 	struct rtw_dev *rtwdev = hw->priv;
83644bc17f7SChin-Yen Lee 	int ret;
83744bc17f7SChin-Yen Lee 
83844bc17f7SChin-Yen Lee 	mutex_lock(&rtwdev->mutex);
83944bc17f7SChin-Yen Lee 	ret = rtw_wow_resume(rtwdev);
84044bc17f7SChin-Yen Lee 	if (ret)
84144bc17f7SChin-Yen Lee 		rtw_err(rtwdev, "failed to resume for wow %d\n", ret);
84244bc17f7SChin-Yen Lee 	mutex_unlock(&rtwdev->mutex);
84344bc17f7SChin-Yen Lee 
84444bc17f7SChin-Yen Lee 	return ret ? 1 : 0;
84544bc17f7SChin-Yen Lee }
84644bc17f7SChin-Yen Lee 
rtw_ops_set_wakeup(struct ieee80211_hw * hw,bool enabled)84744bc17f7SChin-Yen Lee static void rtw_ops_set_wakeup(struct ieee80211_hw *hw, bool enabled)
84844bc17f7SChin-Yen Lee {
84944bc17f7SChin-Yen Lee 	struct rtw_dev *rtwdev = hw->priv;
85044bc17f7SChin-Yen Lee 
85144bc17f7SChin-Yen Lee 	device_set_wakeup_enable(rtwdev->dev, enabled);
85244bc17f7SChin-Yen Lee }
85344bc17f7SChin-Yen Lee #endif
85444bc17f7SChin-Yen Lee 
rtw_reconfig_complete(struct ieee80211_hw * hw,enum ieee80211_reconfig_type reconfig_type)8555c831644STzu-En Huang static void rtw_reconfig_complete(struct ieee80211_hw *hw,
8565c831644STzu-En Huang 				  enum ieee80211_reconfig_type reconfig_type)
8575c831644STzu-En Huang {
8585c831644STzu-En Huang 	struct rtw_dev *rtwdev = hw->priv;
8595c831644STzu-En Huang 
8605c831644STzu-En Huang 	mutex_lock(&rtwdev->mutex);
8615c831644STzu-En Huang 	if (reconfig_type == IEEE80211_RECONFIG_TYPE_RESTART)
8625c831644STzu-En Huang 		clear_bit(RTW_FLAG_RESTARTING, rtwdev->flags);
8635c831644STzu-En Huang 	mutex_unlock(&rtwdev->mutex);
8645c831644STzu-En Huang }
8655c831644STzu-En Huang 
rtw_ops_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_scan_request * req)86610d162b2SPo-Hao Huang static int rtw_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
86710d162b2SPo-Hao Huang 			   struct ieee80211_scan_request *req)
86810d162b2SPo-Hao Huang {
86910d162b2SPo-Hao Huang 	struct rtw_dev *rtwdev = hw->priv;
87010d162b2SPo-Hao Huang 	int ret;
87110d162b2SPo-Hao Huang 
87210d162b2SPo-Hao Huang 	if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD))
87310d162b2SPo-Hao Huang 		return 1;
87410d162b2SPo-Hao Huang 
87510d162b2SPo-Hao Huang 	if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
87610d162b2SPo-Hao Huang 		return -EBUSY;
87710d162b2SPo-Hao Huang 
87810d162b2SPo-Hao Huang 	mutex_lock(&rtwdev->mutex);
87910d162b2SPo-Hao Huang 	rtw_hw_scan_start(rtwdev, vif, req);
88010d162b2SPo-Hao Huang 	ret = rtw_hw_scan_offload(rtwdev, vif, true);
88110d162b2SPo-Hao Huang 	if (ret) {
882a1b8015dSPo-Hao Huang 		rtw_hw_scan_abort(rtwdev);
88310d162b2SPo-Hao Huang 		rtw_err(rtwdev, "HW scan failed with status: %d\n", ret);
88410d162b2SPo-Hao Huang 	}
88510d162b2SPo-Hao Huang 	mutex_unlock(&rtwdev->mutex);
88610d162b2SPo-Hao Huang 
88710d162b2SPo-Hao Huang 	return ret;
88810d162b2SPo-Hao Huang }
88910d162b2SPo-Hao Huang 
rtw_ops_cancel_hw_scan(struct ieee80211_hw * hw,struct ieee80211_vif * vif)89010d162b2SPo-Hao Huang static void rtw_ops_cancel_hw_scan(struct ieee80211_hw *hw,
89110d162b2SPo-Hao Huang 				   struct ieee80211_vif *vif)
89210d162b2SPo-Hao Huang {
89310d162b2SPo-Hao Huang 	struct rtw_dev *rtwdev = hw->priv;
89410d162b2SPo-Hao Huang 
89510d162b2SPo-Hao Huang 	if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD))
89610d162b2SPo-Hao Huang 		return;
89710d162b2SPo-Hao Huang 
89810d162b2SPo-Hao Huang 	if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
89910d162b2SPo-Hao Huang 		return;
90010d162b2SPo-Hao Huang 
90110d162b2SPo-Hao Huang 	mutex_lock(&rtwdev->mutex);
902a1b8015dSPo-Hao Huang 	rtw_hw_scan_abort(rtwdev);
90310d162b2SPo-Hao Huang 	mutex_unlock(&rtwdev->mutex);
90410d162b2SPo-Hao Huang }
90510d162b2SPo-Hao Huang 
rtw_ops_set_sar_specs(struct ieee80211_hw * hw,const struct cfg80211_sar_specs * sar)9068704d0beSZong-Zhe Yang static int rtw_ops_set_sar_specs(struct ieee80211_hw *hw,
9078704d0beSZong-Zhe Yang 				 const struct cfg80211_sar_specs *sar)
9088704d0beSZong-Zhe Yang {
9098704d0beSZong-Zhe Yang 	struct rtw_dev *rtwdev = hw->priv;
9108704d0beSZong-Zhe Yang 
9119a72db41SChih-Kang Chang 	mutex_lock(&rtwdev->mutex);
9128704d0beSZong-Zhe Yang 	rtw_set_sar_specs(rtwdev, sar);
9139a72db41SChih-Kang Chang 	mutex_unlock(&rtwdev->mutex);
9148704d0beSZong-Zhe Yang 
9158704d0beSZong-Zhe Yang 	return 0;
9168704d0beSZong-Zhe Yang }
9178704d0beSZong-Zhe Yang 
rtw_ops_sta_rc_update(struct ieee80211_hw * hw,struct ieee80211_vif * vif,struct ieee80211_sta * sta,u32 changed)918c1edc864SPo-Hao Huang static void rtw_ops_sta_rc_update(struct ieee80211_hw *hw,
919c1edc864SPo-Hao Huang 				  struct ieee80211_vif *vif,
920c1edc864SPo-Hao Huang 				  struct ieee80211_sta *sta, u32 changed)
921c1edc864SPo-Hao Huang {
922c1edc864SPo-Hao Huang 	struct rtw_dev *rtwdev = hw->priv;
923c1edc864SPo-Hao Huang 	struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
924c1edc864SPo-Hao Huang 
925c1edc864SPo-Hao Huang 	if (changed & IEEE80211_RC_BW_CHANGED)
926bcafcb95SPing-Ke Shih 		ieee80211_queue_work(rtwdev->hw, &si->rc_work);
927c1edc864SPo-Hao Huang }
928c1edc864SPo-Hao Huang 
929e3037485SYan-Hsuan Chuang const struct ieee80211_ops rtw_ops = {
930e3037485SYan-Hsuan Chuang 	.tx			= rtw_ops_tx,
9313745d3e5SYan-Hsuan Chuang 	.wake_tx_queue		= rtw_ops_wake_tx_queue,
932e3037485SYan-Hsuan Chuang 	.start			= rtw_ops_start,
933e3037485SYan-Hsuan Chuang 	.stop			= rtw_ops_stop,
934e3037485SYan-Hsuan Chuang 	.config			= rtw_ops_config,
935e3037485SYan-Hsuan Chuang 	.add_interface		= rtw_ops_add_interface,
936e3037485SYan-Hsuan Chuang 	.remove_interface	= rtw_ops_remove_interface,
93740b788d1SYan-Hsuan Chuang 	.change_interface	= rtw_ops_change_interface,
938e3037485SYan-Hsuan Chuang 	.configure_filter	= rtw_ops_configure_filter,
939e3037485SYan-Hsuan Chuang 	.bss_info_changed	= rtw_ops_bss_info_changed,
940f5207c12SPing-Ke Shih 	.start_ap		= rtw_ops_start_ap,
9415ec69129SPo-Hao Huang 	.stop_ap		= rtw_ops_stop_ap,
942bf06c7ecSYan-Hsuan Chuang 	.conf_tx		= rtw_ops_conf_tx,
943e3037485SYan-Hsuan Chuang 	.sta_add		= rtw_ops_sta_add,
944e3037485SYan-Hsuan Chuang 	.sta_remove		= rtw_ops_sta_remove,
945f2217968SPo-Hao Huang 	.set_tim		= rtw_ops_set_tim,
946e3037485SYan-Hsuan Chuang 	.set_key		= rtw_ops_set_key,
947e3037485SYan-Hsuan Chuang 	.ampdu_action		= rtw_ops_ampdu_action,
94874c3d72cSYan-Hsuan Chuang 	.can_aggregate_in_amsdu	= rtw_ops_can_aggregate_in_amsdu,
949e3037485SYan-Hsuan Chuang 	.sw_scan_start		= rtw_ops_sw_scan_start,
950e3037485SYan-Hsuan Chuang 	.sw_scan_complete	= rtw_ops_sw_scan_complete,
9514136214fSYan-Hsuan Chuang 	.mgd_prepare_tx		= rtw_ops_mgd_prepare_tx,
952942e2a5dSYan-Hsuan Chuang 	.set_rts_threshold	= rtw_ops_set_rts_threshold,
953699c7730STzu-En Huang 	.sta_statistics		= rtw_ops_sta_statistics,
9541131ad7fSYan-Hsuan Chuang 	.flush			= rtw_ops_flush,
955f39e9bd4STzu-En Huang 	.set_bitrate_mask	= rtw_ops_set_bitrate_mask,
956297bcf82SYan-Hsuan Chuang 	.set_antenna		= rtw_ops_set_antenna,
957297bcf82SYan-Hsuan Chuang 	.get_antenna		= rtw_ops_get_antenna,
9585c831644STzu-En Huang 	.reconfig_complete	= rtw_reconfig_complete,
95910d162b2SPo-Hao Huang 	.hw_scan		= rtw_ops_hw_scan,
96010d162b2SPo-Hao Huang 	.cancel_hw_scan		= rtw_ops_cancel_hw_scan,
961c1edc864SPo-Hao Huang 	.sta_rc_update		= rtw_ops_sta_rc_update,
9628704d0beSZong-Zhe Yang 	.set_sar_specs          = rtw_ops_set_sar_specs,
96344bc17f7SChin-Yen Lee #ifdef CONFIG_PM
96444bc17f7SChin-Yen Lee 	.suspend		= rtw_ops_suspend,
96544bc17f7SChin-Yen Lee 	.resume			= rtw_ops_resume,
96644bc17f7SChin-Yen Lee 	.set_wakeup		= rtw_ops_set_wakeup,
96744bc17f7SChin-Yen Lee #endif
968e3037485SYan-Hsuan Chuang };
969e3037485SYan-Hsuan Chuang EXPORT_SYMBOL(rtw_ops);
970