xref: /freebsd/sys/contrib/dev/rtw88/fw.c (revision 90aac0d83bc9645f51ef0c2aeae6f9c0540bb031)
12774f206SBjoern A. Zeeb // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
22774f206SBjoern A. Zeeb /* Copyright(c) 2018-2019  Realtek Corporation
32774f206SBjoern A. Zeeb  */
42774f206SBjoern A. Zeeb 
52774f206SBjoern A. Zeeb #include <linux/iopoll.h>
62774f206SBjoern A. Zeeb 
72774f206SBjoern A. Zeeb #include "main.h"
82774f206SBjoern A. Zeeb #include "coex.h"
92774f206SBjoern A. Zeeb #include "fw.h"
102774f206SBjoern A. Zeeb #include "tx.h"
112774f206SBjoern A. Zeeb #include "reg.h"
122774f206SBjoern A. Zeeb #include "sec.h"
132774f206SBjoern A. Zeeb #include "debug.h"
142774f206SBjoern A. Zeeb #include "util.h"
152774f206SBjoern A. Zeeb #include "wow.h"
162774f206SBjoern A. Zeeb #include "ps.h"
17*90aac0d8SBjoern A. Zeeb #include "phy.h"
18*90aac0d8SBjoern A. Zeeb #include "mac.h"
192774f206SBjoern A. Zeeb 
202774f206SBjoern A. Zeeb static void rtw_fw_c2h_cmd_handle_ext(struct rtw_dev *rtwdev,
212774f206SBjoern A. Zeeb 				      struct sk_buff *skb)
222774f206SBjoern A. Zeeb {
232774f206SBjoern A. Zeeb 	struct rtw_c2h_cmd *c2h;
242774f206SBjoern A. Zeeb 	u8 sub_cmd_id;
252774f206SBjoern A. Zeeb 
262774f206SBjoern A. Zeeb 	c2h = get_c2h_from_skb(skb);
272774f206SBjoern A. Zeeb 	sub_cmd_id = c2h->payload[0];
282774f206SBjoern A. Zeeb 
292774f206SBjoern A. Zeeb 	switch (sub_cmd_id) {
302774f206SBjoern A. Zeeb 	case C2H_CCX_RPT:
312774f206SBjoern A. Zeeb 		rtw_tx_report_handle(rtwdev, skb, C2H_CCX_RPT);
322774f206SBjoern A. Zeeb 		break;
332774f206SBjoern A. Zeeb 	case C2H_SCAN_STATUS_RPT:
342774f206SBjoern A. Zeeb 		rtw_hw_scan_status_report(rtwdev, skb);
352774f206SBjoern A. Zeeb 		break;
362774f206SBjoern A. Zeeb 	case C2H_CHAN_SWITCH:
372774f206SBjoern A. Zeeb 		rtw_hw_scan_chan_switch(rtwdev, skb);
382774f206SBjoern A. Zeeb 		break;
392774f206SBjoern A. Zeeb 	default:
402774f206SBjoern A. Zeeb 		break;
412774f206SBjoern A. Zeeb 	}
422774f206SBjoern A. Zeeb }
432774f206SBjoern A. Zeeb 
442774f206SBjoern A. Zeeb static u16 get_max_amsdu_len(u32 bit_rate)
452774f206SBjoern A. Zeeb {
462774f206SBjoern A. Zeeb 	/* lower than ofdm, do not aggregate */
472774f206SBjoern A. Zeeb 	if (bit_rate < 550)
482774f206SBjoern A. Zeeb 		return 1;
492774f206SBjoern A. Zeeb 
502774f206SBjoern A. Zeeb 	/* lower than 20M 2ss mcs8, make it small */
512774f206SBjoern A. Zeeb 	if (bit_rate < 1800)
522774f206SBjoern A. Zeeb 		return 1200;
532774f206SBjoern A. Zeeb 
542774f206SBjoern A. Zeeb 	/* lower than 40M 2ss mcs9, make it medium */
552774f206SBjoern A. Zeeb 	if (bit_rate < 4000)
562774f206SBjoern A. Zeeb 		return 2600;
572774f206SBjoern A. Zeeb 
582774f206SBjoern A. Zeeb 	/* not yet 80M 2ss mcs8/9, make it twice regular packet size */
592774f206SBjoern A. Zeeb 	if (bit_rate < 7000)
602774f206SBjoern A. Zeeb 		return 3500;
612774f206SBjoern A. Zeeb 
622774f206SBjoern A. Zeeb 	/* unlimited */
632774f206SBjoern A. Zeeb 	return 0;
642774f206SBjoern A. Zeeb }
652774f206SBjoern A. Zeeb 
662774f206SBjoern A. Zeeb struct rtw_fw_iter_ra_data {
672774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
682774f206SBjoern A. Zeeb 	u8 *payload;
692774f206SBjoern A. Zeeb };
702774f206SBjoern A. Zeeb 
712774f206SBjoern A. Zeeb static void rtw_fw_ra_report_iter(void *data, struct ieee80211_sta *sta)
722774f206SBjoern A. Zeeb {
732774f206SBjoern A. Zeeb 	struct rtw_fw_iter_ra_data *ra_data = data;
742774f206SBjoern A. Zeeb 	struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
752774f206SBjoern A. Zeeb 	u8 mac_id, rate, sgi, bw;
762774f206SBjoern A. Zeeb 	u8 mcs, nss;
772774f206SBjoern A. Zeeb 	u32 bit_rate;
782774f206SBjoern A. Zeeb 
792774f206SBjoern A. Zeeb 	mac_id = GET_RA_REPORT_MACID(ra_data->payload);
802774f206SBjoern A. Zeeb 	if (si->mac_id != mac_id)
812774f206SBjoern A. Zeeb 		return;
822774f206SBjoern A. Zeeb 
832774f206SBjoern A. Zeeb 	si->ra_report.txrate.flags = 0;
842774f206SBjoern A. Zeeb 
852774f206SBjoern A. Zeeb 	rate = GET_RA_REPORT_RATE(ra_data->payload);
862774f206SBjoern A. Zeeb 	sgi = GET_RA_REPORT_SGI(ra_data->payload);
872774f206SBjoern A. Zeeb 	bw = GET_RA_REPORT_BW(ra_data->payload);
882774f206SBjoern A. Zeeb 
892774f206SBjoern A. Zeeb 	if (rate < DESC_RATEMCS0) {
902774f206SBjoern A. Zeeb 		si->ra_report.txrate.legacy = rtw_desc_to_bitrate(rate);
912774f206SBjoern A. Zeeb 		goto legacy;
922774f206SBjoern A. Zeeb 	}
932774f206SBjoern A. Zeeb 
942774f206SBjoern A. Zeeb 	rtw_desc_to_mcsrate(rate, &mcs, &nss);
952774f206SBjoern A. Zeeb 	if (rate >= DESC_RATEVHT1SS_MCS0)
962774f206SBjoern A. Zeeb 		si->ra_report.txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
972774f206SBjoern A. Zeeb 	else if (rate >= DESC_RATEMCS0)
982774f206SBjoern A. Zeeb 		si->ra_report.txrate.flags |= RATE_INFO_FLAGS_MCS;
992774f206SBjoern A. Zeeb 
1002774f206SBjoern A. Zeeb 	if (rate >= DESC_RATEMCS0) {
1012774f206SBjoern A. Zeeb 		si->ra_report.txrate.mcs = mcs;
1022774f206SBjoern A. Zeeb 		si->ra_report.txrate.nss = nss;
1032774f206SBjoern A. Zeeb 	}
1042774f206SBjoern A. Zeeb 
1052774f206SBjoern A. Zeeb 	if (sgi)
1062774f206SBjoern A. Zeeb 		si->ra_report.txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1072774f206SBjoern A. Zeeb 
1082774f206SBjoern A. Zeeb 	if (bw == RTW_CHANNEL_WIDTH_80)
1092774f206SBjoern A. Zeeb 		si->ra_report.txrate.bw = RATE_INFO_BW_80;
1102774f206SBjoern A. Zeeb 	else if (bw == RTW_CHANNEL_WIDTH_40)
1112774f206SBjoern A. Zeeb 		si->ra_report.txrate.bw = RATE_INFO_BW_40;
1122774f206SBjoern A. Zeeb 	else
1132774f206SBjoern A. Zeeb 		si->ra_report.txrate.bw = RATE_INFO_BW_20;
1142774f206SBjoern A. Zeeb 
1152774f206SBjoern A. Zeeb legacy:
1162774f206SBjoern A. Zeeb 	bit_rate = cfg80211_calculate_bitrate(&si->ra_report.txrate);
1172774f206SBjoern A. Zeeb 
1182774f206SBjoern A. Zeeb 	si->ra_report.desc_rate = rate;
1192774f206SBjoern A. Zeeb 	si->ra_report.bit_rate = bit_rate;
1202774f206SBjoern A. Zeeb 
121*90aac0d8SBjoern A. Zeeb 	sta->deflink.agg.max_rc_amsdu_len = get_max_amsdu_len(bit_rate);
1222774f206SBjoern A. Zeeb }
1232774f206SBjoern A. Zeeb 
1242774f206SBjoern A. Zeeb static void rtw_fw_ra_report_handle(struct rtw_dev *rtwdev, u8 *payload,
1252774f206SBjoern A. Zeeb 				    u8 length)
1262774f206SBjoern A. Zeeb {
1272774f206SBjoern A. Zeeb 	struct rtw_fw_iter_ra_data ra_data;
1282774f206SBjoern A. Zeeb 
1292774f206SBjoern A. Zeeb 	if (WARN(length < 7, "invalid ra report c2h length\n"))
1302774f206SBjoern A. Zeeb 		return;
1312774f206SBjoern A. Zeeb 
1322774f206SBjoern A. Zeeb 	rtwdev->dm_info.tx_rate = GET_RA_REPORT_RATE(payload);
1332774f206SBjoern A. Zeeb 	ra_data.rtwdev = rtwdev;
1342774f206SBjoern A. Zeeb 	ra_data.payload = payload;
1352774f206SBjoern A. Zeeb 	rtw_iterate_stas_atomic(rtwdev, rtw_fw_ra_report_iter, &ra_data);
1362774f206SBjoern A. Zeeb }
1372774f206SBjoern A. Zeeb 
1382774f206SBjoern A. Zeeb struct rtw_beacon_filter_iter_data {
1392774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
1402774f206SBjoern A. Zeeb 	u8 *payload;
1412774f206SBjoern A. Zeeb };
1422774f206SBjoern A. Zeeb 
143*90aac0d8SBjoern A. Zeeb static void rtw_fw_bcn_filter_notify_vif_iter(void *data,
1442774f206SBjoern A. Zeeb 					      struct ieee80211_vif *vif)
1452774f206SBjoern A. Zeeb {
1462774f206SBjoern A. Zeeb 	struct rtw_beacon_filter_iter_data *iter_data = data;
1472774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = iter_data->rtwdev;
1482774f206SBjoern A. Zeeb 	u8 *payload = iter_data->payload;
1492774f206SBjoern A. Zeeb 	u8 type = GET_BCN_FILTER_NOTIFY_TYPE(payload);
1502774f206SBjoern A. Zeeb 	u8 event = GET_BCN_FILTER_NOTIFY_EVENT(payload);
1512774f206SBjoern A. Zeeb 	s8 sig = (s8)GET_BCN_FILTER_NOTIFY_RSSI(payload);
1522774f206SBjoern A. Zeeb 
1532774f206SBjoern A. Zeeb 	switch (type) {
1542774f206SBjoern A. Zeeb 	case BCN_FILTER_NOTIFY_SIGNAL_CHANGE:
1552774f206SBjoern A. Zeeb 		event = event ? NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH :
1562774f206SBjoern A. Zeeb 			NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
1572774f206SBjoern A. Zeeb 		ieee80211_cqm_rssi_notify(vif, event, sig, GFP_KERNEL);
1582774f206SBjoern A. Zeeb 		break;
1592774f206SBjoern A. Zeeb 	case BCN_FILTER_CONNECTION_LOSS:
1602774f206SBjoern A. Zeeb 		ieee80211_connection_loss(vif);
1612774f206SBjoern A. Zeeb 		break;
1622774f206SBjoern A. Zeeb 	case BCN_FILTER_CONNECTED:
1632774f206SBjoern A. Zeeb 		rtwdev->beacon_loss = false;
1642774f206SBjoern A. Zeeb 		break;
1652774f206SBjoern A. Zeeb 	case BCN_FILTER_NOTIFY_BEACON_LOSS:
1662774f206SBjoern A. Zeeb 		rtwdev->beacon_loss = true;
1672774f206SBjoern A. Zeeb 		rtw_leave_lps(rtwdev);
1682774f206SBjoern A. Zeeb 		break;
1692774f206SBjoern A. Zeeb 	}
1702774f206SBjoern A. Zeeb }
1712774f206SBjoern A. Zeeb 
1722774f206SBjoern A. Zeeb static void rtw_fw_bcn_filter_notify(struct rtw_dev *rtwdev, u8 *payload,
1732774f206SBjoern A. Zeeb 				     u8 length)
1742774f206SBjoern A. Zeeb {
1752774f206SBjoern A. Zeeb 	struct rtw_beacon_filter_iter_data dev_iter_data;
1762774f206SBjoern A. Zeeb 
1772774f206SBjoern A. Zeeb 	dev_iter_data.rtwdev = rtwdev;
1782774f206SBjoern A. Zeeb 	dev_iter_data.payload = payload;
1792774f206SBjoern A. Zeeb 	rtw_iterate_vifs(rtwdev, rtw_fw_bcn_filter_notify_vif_iter,
1802774f206SBjoern A. Zeeb 			 &dev_iter_data);
1812774f206SBjoern A. Zeeb }
1822774f206SBjoern A. Zeeb 
1832774f206SBjoern A. Zeeb static void rtw_fw_scan_result(struct rtw_dev *rtwdev, u8 *payload,
1842774f206SBjoern A. Zeeb 			       u8 length)
1852774f206SBjoern A. Zeeb {
1862774f206SBjoern A. Zeeb 	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
1872774f206SBjoern A. Zeeb 
1882774f206SBjoern A. Zeeb 	dm_info->scan_density = payload[0];
1892774f206SBjoern A. Zeeb 
1902774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "scan.density = %x\n",
1912774f206SBjoern A. Zeeb 		dm_info->scan_density);
1922774f206SBjoern A. Zeeb }
1932774f206SBjoern A. Zeeb 
1942774f206SBjoern A. Zeeb static void rtw_fw_adaptivity_result(struct rtw_dev *rtwdev, u8 *payload,
1952774f206SBjoern A. Zeeb 				     u8 length)
1962774f206SBjoern A. Zeeb {
1972774f206SBjoern A. Zeeb 	struct rtw_hw_reg_offset *edcca_th = rtwdev->chip->edcca_th;
1982774f206SBjoern A. Zeeb 	struct rtw_c2h_adaptivity *result = (struct rtw_c2h_adaptivity *)payload;
1992774f206SBjoern A. Zeeb 
2002774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY,
2012774f206SBjoern A. Zeeb 		"Adaptivity: density %x igi %x l2h_th_init %x l2h %x h2l %x option %x\n",
2022774f206SBjoern A. Zeeb 		result->density, result->igi, result->l2h_th_init, result->l2h,
2032774f206SBjoern A. Zeeb 		result->h2l, result->option);
2042774f206SBjoern A. Zeeb 
2052774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "Reg Setting: L2H %x H2L %x\n",
2062774f206SBjoern A. Zeeb 		rtw_read32_mask(rtwdev, edcca_th[EDCCA_TH_L2H_IDX].hw_reg.addr,
2072774f206SBjoern A. Zeeb 				edcca_th[EDCCA_TH_L2H_IDX].hw_reg.mask),
2082774f206SBjoern A. Zeeb 		rtw_read32_mask(rtwdev, edcca_th[EDCCA_TH_H2L_IDX].hw_reg.addr,
2092774f206SBjoern A. Zeeb 				edcca_th[EDCCA_TH_H2L_IDX].hw_reg.mask));
2102774f206SBjoern A. Zeeb 
2112774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "EDCCA Flag %s\n",
2122774f206SBjoern A. Zeeb 		rtw_read32_mask(rtwdev, REG_EDCCA_REPORT, BIT_EDCCA_FLAG) ?
2132774f206SBjoern A. Zeeb 		"Set" : "Unset");
2142774f206SBjoern A. Zeeb }
2152774f206SBjoern A. Zeeb 
2162774f206SBjoern A. Zeeb void rtw_fw_c2h_cmd_handle(struct rtw_dev *rtwdev, struct sk_buff *skb)
2172774f206SBjoern A. Zeeb {
2182774f206SBjoern A. Zeeb 	struct rtw_c2h_cmd *c2h;
2192774f206SBjoern A. Zeeb 	u32 pkt_offset;
2202774f206SBjoern A. Zeeb 	u8 len;
2212774f206SBjoern A. Zeeb 
2222774f206SBjoern A. Zeeb 	pkt_offset = *((u32 *)skb->cb);
2232774f206SBjoern A. Zeeb 	c2h = (struct rtw_c2h_cmd *)(skb->data + pkt_offset);
2242774f206SBjoern A. Zeeb 	len = skb->len - pkt_offset - 2;
2252774f206SBjoern A. Zeeb 
2262774f206SBjoern A. Zeeb 	mutex_lock(&rtwdev->mutex);
2272774f206SBjoern A. Zeeb 
2282774f206SBjoern A. Zeeb 	if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags))
2292774f206SBjoern A. Zeeb 		goto unlock;
2302774f206SBjoern A. Zeeb 
2312774f206SBjoern A. Zeeb 	switch (c2h->id) {
2322774f206SBjoern A. Zeeb 	case C2H_CCX_TX_RPT:
2332774f206SBjoern A. Zeeb 		rtw_tx_report_handle(rtwdev, skb, C2H_CCX_TX_RPT);
2342774f206SBjoern A. Zeeb 		break;
2352774f206SBjoern A. Zeeb 	case C2H_BT_INFO:
2362774f206SBjoern A. Zeeb 		rtw_coex_bt_info_notify(rtwdev, c2h->payload, len);
2372774f206SBjoern A. Zeeb 		break;
2389c951734SBjoern A. Zeeb 	case C2H_BT_HID_INFO:
2399c951734SBjoern A. Zeeb 		rtw_coex_bt_hid_info_notify(rtwdev, c2h->payload, len);
2409c951734SBjoern A. Zeeb 		break;
2412774f206SBjoern A. Zeeb 	case C2H_WLAN_INFO:
2422774f206SBjoern A. Zeeb 		rtw_coex_wl_fwdbginfo_notify(rtwdev, c2h->payload, len);
2432774f206SBjoern A. Zeeb 		break;
2442774f206SBjoern A. Zeeb 	case C2H_BCN_FILTER_NOTIFY:
2452774f206SBjoern A. Zeeb 		rtw_fw_bcn_filter_notify(rtwdev, c2h->payload, len);
2462774f206SBjoern A. Zeeb 		break;
2472774f206SBjoern A. Zeeb 	case C2H_HALMAC:
2482774f206SBjoern A. Zeeb 		rtw_fw_c2h_cmd_handle_ext(rtwdev, skb);
2492774f206SBjoern A. Zeeb 		break;
2502774f206SBjoern A. Zeeb 	case C2H_RA_RPT:
2512774f206SBjoern A. Zeeb 		rtw_fw_ra_report_handle(rtwdev, c2h->payload, len);
2522774f206SBjoern A. Zeeb 		break;
2532774f206SBjoern A. Zeeb 	default:
2542774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_FW, "C2H 0x%x isn't handled\n", c2h->id);
2552774f206SBjoern A. Zeeb 		break;
2562774f206SBjoern A. Zeeb 	}
2572774f206SBjoern A. Zeeb 
2582774f206SBjoern A. Zeeb unlock:
2592774f206SBjoern A. Zeeb 	mutex_unlock(&rtwdev->mutex);
2602774f206SBjoern A. Zeeb }
2612774f206SBjoern A. Zeeb 
2622774f206SBjoern A. Zeeb void rtw_fw_c2h_cmd_rx_irqsafe(struct rtw_dev *rtwdev, u32 pkt_offset,
2632774f206SBjoern A. Zeeb 			       struct sk_buff *skb)
2642774f206SBjoern A. Zeeb {
2652774f206SBjoern A. Zeeb 	struct rtw_c2h_cmd *c2h;
2662774f206SBjoern A. Zeeb 	u8 len;
2672774f206SBjoern A. Zeeb 
2682774f206SBjoern A. Zeeb 	c2h = (struct rtw_c2h_cmd *)(skb->data + pkt_offset);
2692774f206SBjoern A. Zeeb 	len = skb->len - pkt_offset - 2;
2702774f206SBjoern A. Zeeb 	*((u32 *)skb->cb) = pkt_offset;
2712774f206SBjoern A. Zeeb 
2722774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "recv C2H, id=0x%02x, seq=0x%02x, len=%d\n",
2732774f206SBjoern A. Zeeb 		c2h->id, c2h->seq, len);
2742774f206SBjoern A. Zeeb 
2752774f206SBjoern A. Zeeb 	switch (c2h->id) {
2762774f206SBjoern A. Zeeb 	case C2H_BT_MP_INFO:
2772774f206SBjoern A. Zeeb 		rtw_coex_info_response(rtwdev, skb);
2782774f206SBjoern A. Zeeb 		break;
2792774f206SBjoern A. Zeeb 	case C2H_WLAN_RFON:
2802774f206SBjoern A. Zeeb 		complete(&rtwdev->lps_leave_check);
2812774f206SBjoern A. Zeeb 		dev_kfree_skb_any(skb);
2822774f206SBjoern A. Zeeb 		break;
2832774f206SBjoern A. Zeeb 	case C2H_SCAN_RESULT:
2842774f206SBjoern A. Zeeb 		complete(&rtwdev->fw_scan_density);
2852774f206SBjoern A. Zeeb 		rtw_fw_scan_result(rtwdev, c2h->payload, len);
2862774f206SBjoern A. Zeeb 		dev_kfree_skb_any(skb);
2872774f206SBjoern A. Zeeb 		break;
2882774f206SBjoern A. Zeeb 	case C2H_ADAPTIVITY:
2892774f206SBjoern A. Zeeb 		rtw_fw_adaptivity_result(rtwdev, c2h->payload, len);
2902774f206SBjoern A. Zeeb 		dev_kfree_skb_any(skb);
2912774f206SBjoern A. Zeeb 		break;
2922774f206SBjoern A. Zeeb 	default:
2932774f206SBjoern A. Zeeb 		/* pass offset for further operation */
2942774f206SBjoern A. Zeeb 		*((u32 *)skb->cb) = pkt_offset;
2952774f206SBjoern A. Zeeb 		skb_queue_tail(&rtwdev->c2h_queue, skb);
2962774f206SBjoern A. Zeeb 		ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work);
2972774f206SBjoern A. Zeeb 		break;
2982774f206SBjoern A. Zeeb 	}
2992774f206SBjoern A. Zeeb }
3002774f206SBjoern A. Zeeb EXPORT_SYMBOL(rtw_fw_c2h_cmd_rx_irqsafe);
3012774f206SBjoern A. Zeeb 
3022774f206SBjoern A. Zeeb void rtw_fw_c2h_cmd_isr(struct rtw_dev *rtwdev)
3032774f206SBjoern A. Zeeb {
3042774f206SBjoern A. Zeeb 	if (rtw_read8(rtwdev, REG_MCU_TST_CFG) == VAL_FW_TRIGGER)
3052774f206SBjoern A. Zeeb 		rtw_fw_recovery(rtwdev);
3062774f206SBjoern A. Zeeb 	else
3072774f206SBjoern A. Zeeb 		rtw_warn(rtwdev, "unhandled firmware c2h interrupt\n");
3082774f206SBjoern A. Zeeb }
3092774f206SBjoern A. Zeeb EXPORT_SYMBOL(rtw_fw_c2h_cmd_isr);
3102774f206SBjoern A. Zeeb 
311*90aac0d8SBjoern A. Zeeb static void rtw_fw_send_h2c_command_register(struct rtw_dev *rtwdev,
312*90aac0d8SBjoern A. Zeeb 					     struct rtw_h2c_register *h2c)
3132774f206SBjoern A. Zeeb {
3142774f206SBjoern A. Zeeb 	u32 box_reg, box_ex_reg;
315*90aac0d8SBjoern A. Zeeb 	u8 box_state, box;
3162774f206SBjoern A. Zeeb 	int ret;
3172774f206SBjoern A. Zeeb 
318*90aac0d8SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "send H2C content %08x %08x\n", h2c->w0,
319*90aac0d8SBjoern A. Zeeb 		h2c->w1);
3202774f206SBjoern A. Zeeb 
321*90aac0d8SBjoern A. Zeeb 	lockdep_assert_held(&rtwdev->mutex);
3222774f206SBjoern A. Zeeb 
3232774f206SBjoern A. Zeeb 	box = rtwdev->h2c.last_box_num;
3242774f206SBjoern A. Zeeb 	switch (box) {
3252774f206SBjoern A. Zeeb 	case 0:
3262774f206SBjoern A. Zeeb 		box_reg = REG_HMEBOX0;
3272774f206SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX0_EX;
3282774f206SBjoern A. Zeeb 		break;
3292774f206SBjoern A. Zeeb 	case 1:
3302774f206SBjoern A. Zeeb 		box_reg = REG_HMEBOX1;
3312774f206SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX1_EX;
3322774f206SBjoern A. Zeeb 		break;
3332774f206SBjoern A. Zeeb 	case 2:
3342774f206SBjoern A. Zeeb 		box_reg = REG_HMEBOX2;
3352774f206SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX2_EX;
3362774f206SBjoern A. Zeeb 		break;
3372774f206SBjoern A. Zeeb 	case 3:
3382774f206SBjoern A. Zeeb 		box_reg = REG_HMEBOX3;
3392774f206SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX3_EX;
3402774f206SBjoern A. Zeeb 		break;
3412774f206SBjoern A. Zeeb 	default:
3422774f206SBjoern A. Zeeb 		WARN(1, "invalid h2c mail box number\n");
343*90aac0d8SBjoern A. Zeeb 		return;
3442774f206SBjoern A. Zeeb 	}
3452774f206SBjoern A. Zeeb 
3462774f206SBjoern A. Zeeb 	ret = read_poll_timeout_atomic(rtw_read8, box_state,
3472774f206SBjoern A. Zeeb 				       !((box_state >> box) & 0x1), 100, 3000,
3482774f206SBjoern A. Zeeb 				       false, rtwdev, REG_HMETFR);
3492774f206SBjoern A. Zeeb 
3502774f206SBjoern A. Zeeb 	if (ret) {
3512774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to send h2c command\n");
352*90aac0d8SBjoern A. Zeeb 		return;
3532774f206SBjoern A. Zeeb 	}
3542774f206SBjoern A. Zeeb 
355*90aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, box_ex_reg, h2c->w1);
356*90aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, box_reg, h2c->w0);
3572774f206SBjoern A. Zeeb 
3582774f206SBjoern A. Zeeb 	if (++rtwdev->h2c.last_box_num >= 4)
3592774f206SBjoern A. Zeeb 		rtwdev->h2c.last_box_num = 0;
360*90aac0d8SBjoern A. Zeeb }
3612774f206SBjoern A. Zeeb 
362*90aac0d8SBjoern A. Zeeb static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev,
363*90aac0d8SBjoern A. Zeeb 				    u8 *h2c)
364*90aac0d8SBjoern A. Zeeb {
365*90aac0d8SBjoern A. Zeeb 	struct rtw_h2c_cmd *h2c_cmd = (struct rtw_h2c_cmd *)h2c;
366*90aac0d8SBjoern A. Zeeb 	u8 box;
367*90aac0d8SBjoern A. Zeeb 	u8 box_state;
368*90aac0d8SBjoern A. Zeeb 	u32 box_reg, box_ex_reg;
369*90aac0d8SBjoern A. Zeeb 	int ret;
370*90aac0d8SBjoern A. Zeeb 
371*90aac0d8SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW,
372*90aac0d8SBjoern A. Zeeb 		"send H2C content %02x%02x%02x%02x %02x%02x%02x%02x\n",
373*90aac0d8SBjoern A. Zeeb 		h2c[3], h2c[2], h2c[1], h2c[0],
374*90aac0d8SBjoern A. Zeeb 		h2c[7], h2c[6], h2c[5], h2c[4]);
375*90aac0d8SBjoern A. Zeeb 
376*90aac0d8SBjoern A. Zeeb 	lockdep_assert_held(&rtwdev->mutex);
377*90aac0d8SBjoern A. Zeeb 
378*90aac0d8SBjoern A. Zeeb 	box = rtwdev->h2c.last_box_num;
379*90aac0d8SBjoern A. Zeeb 	switch (box) {
380*90aac0d8SBjoern A. Zeeb 	case 0:
381*90aac0d8SBjoern A. Zeeb 		box_reg = REG_HMEBOX0;
382*90aac0d8SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX0_EX;
383*90aac0d8SBjoern A. Zeeb 		break;
384*90aac0d8SBjoern A. Zeeb 	case 1:
385*90aac0d8SBjoern A. Zeeb 		box_reg = REG_HMEBOX1;
386*90aac0d8SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX1_EX;
387*90aac0d8SBjoern A. Zeeb 		break;
388*90aac0d8SBjoern A. Zeeb 	case 2:
389*90aac0d8SBjoern A. Zeeb 		box_reg = REG_HMEBOX2;
390*90aac0d8SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX2_EX;
391*90aac0d8SBjoern A. Zeeb 		break;
392*90aac0d8SBjoern A. Zeeb 	case 3:
393*90aac0d8SBjoern A. Zeeb 		box_reg = REG_HMEBOX3;
394*90aac0d8SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX3_EX;
395*90aac0d8SBjoern A. Zeeb 		break;
396*90aac0d8SBjoern A. Zeeb 	default:
397*90aac0d8SBjoern A. Zeeb 		WARN(1, "invalid h2c mail box number\n");
398*90aac0d8SBjoern A. Zeeb 		return;
399*90aac0d8SBjoern A. Zeeb 	}
400*90aac0d8SBjoern A. Zeeb 
401*90aac0d8SBjoern A. Zeeb 	ret = read_poll_timeout_atomic(rtw_read8, box_state,
402*90aac0d8SBjoern A. Zeeb 				       !((box_state >> box) & 0x1), 100, 3000,
403*90aac0d8SBjoern A. Zeeb 				       false, rtwdev, REG_HMETFR);
404*90aac0d8SBjoern A. Zeeb 
405*90aac0d8SBjoern A. Zeeb 	if (ret) {
406*90aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to send h2c command\n");
407*90aac0d8SBjoern A. Zeeb 		return;
408*90aac0d8SBjoern A. Zeeb 	}
409*90aac0d8SBjoern A. Zeeb 
410*90aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, box_ex_reg, le32_to_cpu(h2c_cmd->msg_ext));
411*90aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, box_reg, le32_to_cpu(h2c_cmd->msg));
412*90aac0d8SBjoern A. Zeeb 
413*90aac0d8SBjoern A. Zeeb 	if (++rtwdev->h2c.last_box_num >= 4)
414*90aac0d8SBjoern A. Zeeb 		rtwdev->h2c.last_box_num = 0;
4152774f206SBjoern A. Zeeb }
4162774f206SBjoern A. Zeeb 
4172774f206SBjoern A. Zeeb void rtw_fw_h2c_cmd_dbg(struct rtw_dev *rtwdev, u8 *h2c)
4182774f206SBjoern A. Zeeb {
4192774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c);
4202774f206SBjoern A. Zeeb }
4212774f206SBjoern A. Zeeb 
4222774f206SBjoern A. Zeeb static void rtw_fw_send_h2c_packet(struct rtw_dev *rtwdev, u8 *h2c_pkt)
4232774f206SBjoern A. Zeeb {
4242774f206SBjoern A. Zeeb 	int ret;
4252774f206SBjoern A. Zeeb 
426*90aac0d8SBjoern A. Zeeb 	lockdep_assert_held(&rtwdev->mutex);
4272774f206SBjoern A. Zeeb 
4282774f206SBjoern A. Zeeb 	FW_OFFLOAD_H2C_SET_SEQ_NUM(h2c_pkt, rtwdev->h2c.seq);
4292774f206SBjoern A. Zeeb 	ret = rtw_hci_write_data_h2c(rtwdev, h2c_pkt, H2C_PKT_SIZE);
4302774f206SBjoern A. Zeeb 	if (ret)
4312774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to send h2c packet\n");
4322774f206SBjoern A. Zeeb 	rtwdev->h2c.seq++;
4332774f206SBjoern A. Zeeb }
4342774f206SBjoern A. Zeeb 
4352774f206SBjoern A. Zeeb void
4362774f206SBjoern A. Zeeb rtw_fw_send_general_info(struct rtw_dev *rtwdev)
4372774f206SBjoern A. Zeeb {
4382774f206SBjoern A. Zeeb 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
4392774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
4402774f206SBjoern A. Zeeb 	u16 total_size = H2C_PKT_HDR_SIZE + 4;
4412774f206SBjoern A. Zeeb 
4422774f206SBjoern A. Zeeb 	if (rtw_chip_wcpu_11n(rtwdev))
4432774f206SBjoern A. Zeeb 		return;
4442774f206SBjoern A. Zeeb 
4452774f206SBjoern A. Zeeb 	rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_GENERAL_INFO);
4462774f206SBjoern A. Zeeb 
4472774f206SBjoern A. Zeeb 	SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size);
4482774f206SBjoern A. Zeeb 
4492774f206SBjoern A. Zeeb 	GENERAL_INFO_SET_FW_TX_BOUNDARY(h2c_pkt,
4502774f206SBjoern A. Zeeb 					fifo->rsvd_fw_txbuf_addr -
4512774f206SBjoern A. Zeeb 					fifo->rsvd_boundary);
4522774f206SBjoern A. Zeeb 
4532774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
4542774f206SBjoern A. Zeeb }
4552774f206SBjoern A. Zeeb 
4562774f206SBjoern A. Zeeb void
4572774f206SBjoern A. Zeeb rtw_fw_send_phydm_info(struct rtw_dev *rtwdev)
4582774f206SBjoern A. Zeeb {
4592774f206SBjoern A. Zeeb 	struct rtw_hal *hal = &rtwdev->hal;
4602774f206SBjoern A. Zeeb 	struct rtw_efuse *efuse = &rtwdev->efuse;
4612774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
4622774f206SBjoern A. Zeeb 	u16 total_size = H2C_PKT_HDR_SIZE + 8;
4632774f206SBjoern A. Zeeb 	u8 fw_rf_type = 0;
4642774f206SBjoern A. Zeeb 
4652774f206SBjoern A. Zeeb 	if (rtw_chip_wcpu_11n(rtwdev))
4662774f206SBjoern A. Zeeb 		return;
4672774f206SBjoern A. Zeeb 
4682774f206SBjoern A. Zeeb 	if (hal->rf_type == RF_1T1R)
4692774f206SBjoern A. Zeeb 		fw_rf_type = FW_RF_1T1R;
4702774f206SBjoern A. Zeeb 	else if (hal->rf_type == RF_2T2R)
4712774f206SBjoern A. Zeeb 		fw_rf_type = FW_RF_2T2R;
4722774f206SBjoern A. Zeeb 
4732774f206SBjoern A. Zeeb 	rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_PHYDM_INFO);
4742774f206SBjoern A. Zeeb 
4752774f206SBjoern A. Zeeb 	SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size);
4762774f206SBjoern A. Zeeb 	PHYDM_INFO_SET_REF_TYPE(h2c_pkt, efuse->rfe_option);
4772774f206SBjoern A. Zeeb 	PHYDM_INFO_SET_RF_TYPE(h2c_pkt, fw_rf_type);
4782774f206SBjoern A. Zeeb 	PHYDM_INFO_SET_CUT_VER(h2c_pkt, hal->cut_version);
4792774f206SBjoern A. Zeeb 	PHYDM_INFO_SET_RX_ANT_STATUS(h2c_pkt, hal->antenna_tx);
4802774f206SBjoern A. Zeeb 	PHYDM_INFO_SET_TX_ANT_STATUS(h2c_pkt, hal->antenna_rx);
4812774f206SBjoern A. Zeeb 
4822774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
4832774f206SBjoern A. Zeeb }
4842774f206SBjoern A. Zeeb 
4852774f206SBjoern A. Zeeb void rtw_fw_do_iqk(struct rtw_dev *rtwdev, struct rtw_iqk_para *para)
4862774f206SBjoern A. Zeeb {
4872774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
4882774f206SBjoern A. Zeeb 	u16 total_size = H2C_PKT_HDR_SIZE + 1;
4892774f206SBjoern A. Zeeb 
4902774f206SBjoern A. Zeeb 	rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_IQK);
4912774f206SBjoern A. Zeeb 	SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size);
4922774f206SBjoern A. Zeeb 	IQK_SET_CLEAR(h2c_pkt, para->clear);
4932774f206SBjoern A. Zeeb 	IQK_SET_SEGMENT_IQK(h2c_pkt, para->segment_iqk);
4942774f206SBjoern A. Zeeb 
4952774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
4962774f206SBjoern A. Zeeb }
4972774f206SBjoern A. Zeeb EXPORT_SYMBOL(rtw_fw_do_iqk);
4982774f206SBjoern A. Zeeb 
4992774f206SBjoern A. Zeeb void rtw_fw_inform_rfk_status(struct rtw_dev *rtwdev, bool start)
5002774f206SBjoern A. Zeeb {
5012774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
5022774f206SBjoern A. Zeeb 
5032774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WIFI_CALIBRATION);
5042774f206SBjoern A. Zeeb 
5052774f206SBjoern A. Zeeb 	RFK_SET_INFORM_START(h2c_pkt, start);
5062774f206SBjoern A. Zeeb 
5072774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
5082774f206SBjoern A. Zeeb }
5092774f206SBjoern A. Zeeb EXPORT_SYMBOL(rtw_fw_inform_rfk_status);
5102774f206SBjoern A. Zeeb 
5112774f206SBjoern A. Zeeb void rtw_fw_query_bt_info(struct rtw_dev *rtwdev)
5122774f206SBjoern A. Zeeb {
5132774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
5142774f206SBjoern A. Zeeb 
5152774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_INFO);
5162774f206SBjoern A. Zeeb 
5172774f206SBjoern A. Zeeb 	SET_QUERY_BT_INFO(h2c_pkt, true);
5182774f206SBjoern A. Zeeb 
5192774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
5202774f206SBjoern A. Zeeb }
5212774f206SBjoern A. Zeeb 
522*90aac0d8SBjoern A. Zeeb void rtw_fw_default_port(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif)
523*90aac0d8SBjoern A. Zeeb {
524*90aac0d8SBjoern A. Zeeb 	struct rtw_h2c_register h2c = {};
525*90aac0d8SBjoern A. Zeeb 
526*90aac0d8SBjoern A. Zeeb 	if (rtwvif->net_type != RTW_NET_MGD_LINKED)
527*90aac0d8SBjoern A. Zeeb 		return;
528*90aac0d8SBjoern A. Zeeb 
529*90aac0d8SBjoern A. Zeeb 	/* Leave LPS before default port H2C so FW timer is correct */
530*90aac0d8SBjoern A. Zeeb 	rtw_leave_lps(rtwdev);
531*90aac0d8SBjoern A. Zeeb 
532*90aac0d8SBjoern A. Zeeb 	h2c.w0 = u32_encode_bits(H2C_CMD_DEFAULT_PORT, RTW_H2C_W0_CMDID) |
533*90aac0d8SBjoern A. Zeeb 		 u32_encode_bits(rtwvif->port, RTW_H2C_DEFAULT_PORT_W0_PORTID) |
534*90aac0d8SBjoern A. Zeeb 		 u32_encode_bits(rtwvif->mac_id, RTW_H2C_DEFAULT_PORT_W0_MACID);
535*90aac0d8SBjoern A. Zeeb 
536*90aac0d8SBjoern A. Zeeb 	rtw_fw_send_h2c_command_register(rtwdev, &h2c);
537*90aac0d8SBjoern A. Zeeb }
538*90aac0d8SBjoern A. Zeeb 
5392774f206SBjoern A. Zeeb void rtw_fw_wl_ch_info(struct rtw_dev *rtwdev, u8 link, u8 ch, u8 bw)
5402774f206SBjoern A. Zeeb {
5412774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
5422774f206SBjoern A. Zeeb 
5432774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WL_CH_INFO);
5442774f206SBjoern A. Zeeb 
5452774f206SBjoern A. Zeeb 	SET_WL_CH_INFO_LINK(h2c_pkt, link);
5462774f206SBjoern A. Zeeb 	SET_WL_CH_INFO_CHNL(h2c_pkt, ch);
5472774f206SBjoern A. Zeeb 	SET_WL_CH_INFO_BW(h2c_pkt, bw);
5482774f206SBjoern A. Zeeb 
5492774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
5502774f206SBjoern A. Zeeb }
5512774f206SBjoern A. Zeeb 
5522774f206SBjoern A. Zeeb void rtw_fw_query_bt_mp_info(struct rtw_dev *rtwdev,
5532774f206SBjoern A. Zeeb 			     struct rtw_coex_info_req *req)
5542774f206SBjoern A. Zeeb {
5552774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
5562774f206SBjoern A. Zeeb 
5572774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_MP_INFO);
5582774f206SBjoern A. Zeeb 
5592774f206SBjoern A. Zeeb 	SET_BT_MP_INFO_SEQ(h2c_pkt, req->seq);
5602774f206SBjoern A. Zeeb 	SET_BT_MP_INFO_OP_CODE(h2c_pkt, req->op_code);
5612774f206SBjoern A. Zeeb 	SET_BT_MP_INFO_PARA1(h2c_pkt, req->para1);
5622774f206SBjoern A. Zeeb 	SET_BT_MP_INFO_PARA2(h2c_pkt, req->para2);
5632774f206SBjoern A. Zeeb 	SET_BT_MP_INFO_PARA3(h2c_pkt, req->para3);
5642774f206SBjoern A. Zeeb 
5652774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
5662774f206SBjoern A. Zeeb }
5672774f206SBjoern A. Zeeb 
5682774f206SBjoern A. Zeeb void rtw_fw_force_bt_tx_power(struct rtw_dev *rtwdev, u8 bt_pwr_dec_lvl)
5692774f206SBjoern A. Zeeb {
5702774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
5712774f206SBjoern A. Zeeb 	u8 index = 0 - bt_pwr_dec_lvl;
5722774f206SBjoern A. Zeeb 
5732774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_FORCE_BT_TX_POWER);
5742774f206SBjoern A. Zeeb 
5752774f206SBjoern A. Zeeb 	SET_BT_TX_POWER_INDEX(h2c_pkt, index);
5762774f206SBjoern A. Zeeb 
5772774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
5782774f206SBjoern A. Zeeb }
5792774f206SBjoern A. Zeeb 
5802774f206SBjoern A. Zeeb void rtw_fw_bt_ignore_wlan_action(struct rtw_dev *rtwdev, bool enable)
5812774f206SBjoern A. Zeeb {
5822774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
5832774f206SBjoern A. Zeeb 
5842774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_IGNORE_WLAN_ACTION);
5852774f206SBjoern A. Zeeb 
5862774f206SBjoern A. Zeeb 	SET_IGNORE_WLAN_ACTION_EN(h2c_pkt, enable);
5872774f206SBjoern A. Zeeb 
5882774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
5892774f206SBjoern A. Zeeb }
5902774f206SBjoern A. Zeeb 
5912774f206SBjoern A. Zeeb void rtw_fw_coex_tdma_type(struct rtw_dev *rtwdev,
5922774f206SBjoern A. Zeeb 			   u8 para1, u8 para2, u8 para3, u8 para4, u8 para5)
5932774f206SBjoern A. Zeeb {
5942774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
5952774f206SBjoern A. Zeeb 
5962774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_COEX_TDMA_TYPE);
5972774f206SBjoern A. Zeeb 
5982774f206SBjoern A. Zeeb 	SET_COEX_TDMA_TYPE_PARA1(h2c_pkt, para1);
5992774f206SBjoern A. Zeeb 	SET_COEX_TDMA_TYPE_PARA2(h2c_pkt, para2);
6002774f206SBjoern A. Zeeb 	SET_COEX_TDMA_TYPE_PARA3(h2c_pkt, para3);
6012774f206SBjoern A. Zeeb 	SET_COEX_TDMA_TYPE_PARA4(h2c_pkt, para4);
6022774f206SBjoern A. Zeeb 	SET_COEX_TDMA_TYPE_PARA5(h2c_pkt, para5);
6032774f206SBjoern A. Zeeb 
6042774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
6052774f206SBjoern A. Zeeb }
6062774f206SBjoern A. Zeeb 
6079c951734SBjoern A. Zeeb void rtw_fw_coex_query_hid_info(struct rtw_dev *rtwdev, u8 sub_id, u8 data)
6089c951734SBjoern A. Zeeb {
6099c951734SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6109c951734SBjoern A. Zeeb 
6119c951734SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_HID_INFO);
6129c951734SBjoern A. Zeeb 
6139c951734SBjoern A. Zeeb 	SET_COEX_QUERY_HID_INFO_SUBID(h2c_pkt, sub_id);
6149c951734SBjoern A. Zeeb 	SET_COEX_QUERY_HID_INFO_DATA1(h2c_pkt, data);
6159c951734SBjoern A. Zeeb 
6169c951734SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
6179c951734SBjoern A. Zeeb }
6189c951734SBjoern A. Zeeb 
6192774f206SBjoern A. Zeeb void rtw_fw_bt_wifi_control(struct rtw_dev *rtwdev, u8 op_code, u8 *data)
6202774f206SBjoern A. Zeeb {
6212774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6222774f206SBjoern A. Zeeb 
6232774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BT_WIFI_CONTROL);
6242774f206SBjoern A. Zeeb 
6252774f206SBjoern A. Zeeb 	SET_BT_WIFI_CONTROL_OP_CODE(h2c_pkt, op_code);
6262774f206SBjoern A. Zeeb 
6272774f206SBjoern A. Zeeb 	SET_BT_WIFI_CONTROL_DATA1(h2c_pkt, *data);
6282774f206SBjoern A. Zeeb 	SET_BT_WIFI_CONTROL_DATA2(h2c_pkt, *(data + 1));
6292774f206SBjoern A. Zeeb 	SET_BT_WIFI_CONTROL_DATA3(h2c_pkt, *(data + 2));
6302774f206SBjoern A. Zeeb 	SET_BT_WIFI_CONTROL_DATA4(h2c_pkt, *(data + 3));
6312774f206SBjoern A. Zeeb 	SET_BT_WIFI_CONTROL_DATA5(h2c_pkt, *(data + 4));
6322774f206SBjoern A. Zeeb 
6332774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
6342774f206SBjoern A. Zeeb }
6352774f206SBjoern A. Zeeb 
6362774f206SBjoern A. Zeeb void rtw_fw_send_rssi_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si)
6372774f206SBjoern A. Zeeb {
6382774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6392774f206SBjoern A. Zeeb 	u8 rssi = ewma_rssi_read(&si->avg_rssi);
6402774f206SBjoern A. Zeeb 	bool stbc_en = si->stbc_en ? true : false;
6412774f206SBjoern A. Zeeb 
6422774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RSSI_MONITOR);
6432774f206SBjoern A. Zeeb 
6442774f206SBjoern A. Zeeb 	SET_RSSI_INFO_MACID(h2c_pkt, si->mac_id);
6452774f206SBjoern A. Zeeb 	SET_RSSI_INFO_RSSI(h2c_pkt, rssi);
6462774f206SBjoern A. Zeeb 	SET_RSSI_INFO_STBC(h2c_pkt, stbc_en);
6472774f206SBjoern A. Zeeb 
6482774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
6492774f206SBjoern A. Zeeb }
6502774f206SBjoern A. Zeeb 
6519c951734SBjoern A. Zeeb void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
6529c951734SBjoern A. Zeeb 			 bool reset_ra_mask)
6532774f206SBjoern A. Zeeb {
6542774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6552774f206SBjoern A. Zeeb 	bool disable_pt = true;
6562774f206SBjoern A. Zeeb 
6572774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RA_INFO);
6582774f206SBjoern A. Zeeb 
6592774f206SBjoern A. Zeeb 	SET_RA_INFO_MACID(h2c_pkt, si->mac_id);
6602774f206SBjoern A. Zeeb 	SET_RA_INFO_RATE_ID(h2c_pkt, si->rate_id);
6612774f206SBjoern A. Zeeb 	SET_RA_INFO_INIT_RA_LVL(h2c_pkt, si->init_ra_lv);
6622774f206SBjoern A. Zeeb 	SET_RA_INFO_SGI_EN(h2c_pkt, si->sgi_enable);
6632774f206SBjoern A. Zeeb 	SET_RA_INFO_BW_MODE(h2c_pkt, si->bw_mode);
6642774f206SBjoern A. Zeeb 	SET_RA_INFO_LDPC(h2c_pkt, !!si->ldpc_en);
6659c951734SBjoern A. Zeeb 	SET_RA_INFO_NO_UPDATE(h2c_pkt, !reset_ra_mask);
6662774f206SBjoern A. Zeeb 	SET_RA_INFO_VHT_EN(h2c_pkt, si->vht_enable);
6672774f206SBjoern A. Zeeb 	SET_RA_INFO_DIS_PT(h2c_pkt, disable_pt);
6682774f206SBjoern A. Zeeb 	SET_RA_INFO_RA_MASK0(h2c_pkt, (si->ra_mask & 0xff));
6692774f206SBjoern A. Zeeb 	SET_RA_INFO_RA_MASK1(h2c_pkt, (si->ra_mask & 0xff00) >> 8);
6702774f206SBjoern A. Zeeb 	SET_RA_INFO_RA_MASK2(h2c_pkt, (si->ra_mask & 0xff0000) >> 16);
6712774f206SBjoern A. Zeeb 	SET_RA_INFO_RA_MASK3(h2c_pkt, (si->ra_mask & 0xff000000) >> 24);
6722774f206SBjoern A. Zeeb 
6732774f206SBjoern A. Zeeb 	si->init_ra_lv = 0;
6742774f206SBjoern A. Zeeb 
6752774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
6762774f206SBjoern A. Zeeb }
6772774f206SBjoern A. Zeeb 
6782774f206SBjoern A. Zeeb void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool connect)
6792774f206SBjoern A. Zeeb {
6802774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6812774f206SBjoern A. Zeeb 
6822774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_MEDIA_STATUS_RPT);
6832774f206SBjoern A. Zeeb 	MEDIA_STATUS_RPT_SET_OP_MODE(h2c_pkt, connect);
6842774f206SBjoern A. Zeeb 	MEDIA_STATUS_RPT_SET_MACID(h2c_pkt, mac_id);
6852774f206SBjoern A. Zeeb 
6862774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
6872774f206SBjoern A. Zeeb }
6882774f206SBjoern A. Zeeb 
6892774f206SBjoern A. Zeeb void rtw_fw_update_wl_phy_info(struct rtw_dev *rtwdev)
6902774f206SBjoern A. Zeeb {
6912774f206SBjoern A. Zeeb 	struct rtw_traffic_stats *stats = &rtwdev->stats;
6922774f206SBjoern A. Zeeb 	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
6932774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6942774f206SBjoern A. Zeeb 
6952774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WL_PHY_INFO);
6962774f206SBjoern A. Zeeb 	SET_WL_PHY_INFO_TX_TP(h2c_pkt, stats->tx_throughput);
6972774f206SBjoern A. Zeeb 	SET_WL_PHY_INFO_RX_TP(h2c_pkt, stats->rx_throughput);
6982774f206SBjoern A. Zeeb 	SET_WL_PHY_INFO_TX_RATE_DESC(h2c_pkt, dm_info->tx_rate);
6992774f206SBjoern A. Zeeb 	SET_WL_PHY_INFO_RX_RATE_DESC(h2c_pkt, dm_info->curr_rx_rate);
7002774f206SBjoern A. Zeeb 	SET_WL_PHY_INFO_RX_EVM(h2c_pkt, dm_info->rx_evm_dbm[RF_PATH_A]);
7012774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
7022774f206SBjoern A. Zeeb }
7032774f206SBjoern A. Zeeb 
7042774f206SBjoern A. Zeeb void rtw_fw_beacon_filter_config(struct rtw_dev *rtwdev, bool connect,
7052774f206SBjoern A. Zeeb 				 struct ieee80211_vif *vif)
7062774f206SBjoern A. Zeeb {
7072774f206SBjoern A. Zeeb 	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
7082774f206SBjoern A. Zeeb 	struct ieee80211_sta *sta = ieee80211_find_sta(vif, bss_conf->bssid);
7092774f206SBjoern A. Zeeb 	static const u8 rssi_min = 0, rssi_max = 100, rssi_offset = 100;
7102774f206SBjoern A. Zeeb 	struct rtw_sta_info *si =
7112774f206SBjoern A. Zeeb 		sta ? (struct rtw_sta_info *)sta->drv_priv : NULL;
7122774f206SBjoern A. Zeeb 	s32 threshold = bss_conf->cqm_rssi_thold + rssi_offset;
7132774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
7142774f206SBjoern A. Zeeb 
7159c951734SBjoern A. Zeeb 	if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_BCN_FILTER))
7162774f206SBjoern A. Zeeb 		return;
7172774f206SBjoern A. Zeeb 
7182774f206SBjoern A. Zeeb 	if (!connect) {
7192774f206SBjoern A. Zeeb 		SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BCN_FILTER_OFFLOAD_P1);
7202774f206SBjoern A. Zeeb 		SET_BCN_FILTER_OFFLOAD_P1_ENABLE(h2c_pkt, connect);
7212774f206SBjoern A. Zeeb 		rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
7222774f206SBjoern A. Zeeb 
7232774f206SBjoern A. Zeeb 		return;
7242774f206SBjoern A. Zeeb 	}
7259c951734SBjoern A. Zeeb 
7269c951734SBjoern A. Zeeb 	if (!si)
7279c951734SBjoern A. Zeeb 		return;
7289c951734SBjoern A. Zeeb 
7292774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BCN_FILTER_OFFLOAD_P0);
7302774f206SBjoern A. Zeeb 	ether_addr_copy(&h2c_pkt[1], bss_conf->bssid);
7312774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
7322774f206SBjoern A. Zeeb 
7332774f206SBjoern A. Zeeb 	memset(h2c_pkt, 0, sizeof(h2c_pkt));
7342774f206SBjoern A. Zeeb 	threshold = clamp_t(s32, threshold, rssi_min, rssi_max);
7352774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BCN_FILTER_OFFLOAD_P1);
7362774f206SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_ENABLE(h2c_pkt, connect);
7372774f206SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_OFFLOAD_MODE(h2c_pkt,
7382774f206SBjoern A. Zeeb 					       BCN_FILTER_OFFLOAD_MODE_DEFAULT);
7392774f206SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_THRESHOLD(h2c_pkt, (u8)threshold);
7402774f206SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_BCN_LOSS_CNT(h2c_pkt, BCN_LOSS_CNT);
7412774f206SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_MACID(h2c_pkt, si->mac_id);
7422774f206SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_HYST(h2c_pkt, bss_conf->cqm_rssi_hyst);
7432774f206SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_BCN_INTERVAL(h2c_pkt, bss_conf->beacon_int);
7442774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
7452774f206SBjoern A. Zeeb }
7462774f206SBjoern A. Zeeb 
7472774f206SBjoern A. Zeeb void rtw_fw_set_pwr_mode(struct rtw_dev *rtwdev)
7482774f206SBjoern A. Zeeb {
7492774f206SBjoern A. Zeeb 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
7502774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
7512774f206SBjoern A. Zeeb 
7522774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_SET_PWR_MODE);
7532774f206SBjoern A. Zeeb 
7542774f206SBjoern A. Zeeb 	SET_PWR_MODE_SET_MODE(h2c_pkt, conf->mode);
7552774f206SBjoern A. Zeeb 	SET_PWR_MODE_SET_RLBM(h2c_pkt, conf->rlbm);
7562774f206SBjoern A. Zeeb 	SET_PWR_MODE_SET_SMART_PS(h2c_pkt, conf->smart_ps);
7572774f206SBjoern A. Zeeb 	SET_PWR_MODE_SET_AWAKE_INTERVAL(h2c_pkt, conf->awake_interval);
7582774f206SBjoern A. Zeeb 	SET_PWR_MODE_SET_PORT_ID(h2c_pkt, conf->port_id);
7592774f206SBjoern A. Zeeb 	SET_PWR_MODE_SET_PWR_STATE(h2c_pkt, conf->state);
7602774f206SBjoern A. Zeeb 
7612774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
7622774f206SBjoern A. Zeeb }
7632774f206SBjoern A. Zeeb 
7642774f206SBjoern A. Zeeb void rtw_fw_set_keep_alive_cmd(struct rtw_dev *rtwdev, bool enable)
7652774f206SBjoern A. Zeeb {
7662774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
7672774f206SBjoern A. Zeeb 	struct rtw_fw_wow_keep_alive_para mode = {
7682774f206SBjoern A. Zeeb 		.adopt = true,
7692774f206SBjoern A. Zeeb 		.pkt_type = KEEP_ALIVE_NULL_PKT,
7702774f206SBjoern A. Zeeb 		.period = 5,
7712774f206SBjoern A. Zeeb 	};
7722774f206SBjoern A. Zeeb 
7732774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_KEEP_ALIVE);
7742774f206SBjoern A. Zeeb 	SET_KEEP_ALIVE_ENABLE(h2c_pkt, enable);
7752774f206SBjoern A. Zeeb 	SET_KEEP_ALIVE_ADOPT(h2c_pkt, mode.adopt);
7762774f206SBjoern A. Zeeb 	SET_KEEP_ALIVE_PKT_TYPE(h2c_pkt, mode.pkt_type);
7772774f206SBjoern A. Zeeb 	SET_KEEP_ALIVE_CHECK_PERIOD(h2c_pkt, mode.period);
7782774f206SBjoern A. Zeeb 
7792774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
7802774f206SBjoern A. Zeeb }
7812774f206SBjoern A. Zeeb 
7822774f206SBjoern A. Zeeb void rtw_fw_set_disconnect_decision_cmd(struct rtw_dev *rtwdev, bool enable)
7832774f206SBjoern A. Zeeb {
7842774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
7852774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
7862774f206SBjoern A. Zeeb 	struct rtw_fw_wow_disconnect_para mode = {
7872774f206SBjoern A. Zeeb 		.adopt = true,
7882774f206SBjoern A. Zeeb 		.period = 30,
7892774f206SBjoern A. Zeeb 		.retry_count = 5,
7902774f206SBjoern A. Zeeb 	};
7912774f206SBjoern A. Zeeb 
7922774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_DISCONNECT_DECISION);
7932774f206SBjoern A. Zeeb 
7942774f206SBjoern A. Zeeb 	if (test_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) {
7952774f206SBjoern A. Zeeb 		SET_DISCONNECT_DECISION_ENABLE(h2c_pkt, enable);
7962774f206SBjoern A. Zeeb 		SET_DISCONNECT_DECISION_ADOPT(h2c_pkt, mode.adopt);
7972774f206SBjoern A. Zeeb 		SET_DISCONNECT_DECISION_CHECK_PERIOD(h2c_pkt, mode.period);
7982774f206SBjoern A. Zeeb 		SET_DISCONNECT_DECISION_TRY_PKT_NUM(h2c_pkt, mode.retry_count);
7992774f206SBjoern A. Zeeb 	}
8002774f206SBjoern A. Zeeb 
8012774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
8022774f206SBjoern A. Zeeb }
8032774f206SBjoern A. Zeeb 
8042774f206SBjoern A. Zeeb void rtw_fw_set_wowlan_ctrl_cmd(struct rtw_dev *rtwdev, bool enable)
8052774f206SBjoern A. Zeeb {
8062774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
8072774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
8082774f206SBjoern A. Zeeb 
8092774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WOWLAN);
8102774f206SBjoern A. Zeeb 
8112774f206SBjoern A. Zeeb 	SET_WOWLAN_FUNC_ENABLE(h2c_pkt, enable);
8122774f206SBjoern A. Zeeb 	if (rtw_wow_mgd_linked(rtwdev)) {
8132774f206SBjoern A. Zeeb 		if (test_bit(RTW_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags))
8142774f206SBjoern A. Zeeb 			SET_WOWLAN_MAGIC_PKT_ENABLE(h2c_pkt, enable);
8152774f206SBjoern A. Zeeb 		if (test_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags))
8162774f206SBjoern A. Zeeb 			SET_WOWLAN_DEAUTH_WAKEUP_ENABLE(h2c_pkt, enable);
8172774f206SBjoern A. Zeeb 		if (test_bit(RTW_WOW_FLAG_EN_REKEY_PKT, rtw_wow->flags))
8182774f206SBjoern A. Zeeb 			SET_WOWLAN_REKEY_WAKEUP_ENABLE(h2c_pkt, enable);
8192774f206SBjoern A. Zeeb 		if (rtw_wow->pattern_cnt)
8202774f206SBjoern A. Zeeb 			SET_WOWLAN_PATTERN_MATCH_ENABLE(h2c_pkt, enable);
8212774f206SBjoern A. Zeeb 	}
8222774f206SBjoern A. Zeeb 
8232774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
8242774f206SBjoern A. Zeeb }
8252774f206SBjoern A. Zeeb 
8262774f206SBjoern A. Zeeb void rtw_fw_set_aoac_global_info_cmd(struct rtw_dev *rtwdev,
8272774f206SBjoern A. Zeeb 				     u8 pairwise_key_enc,
8282774f206SBjoern A. Zeeb 				     u8 group_key_enc)
8292774f206SBjoern A. Zeeb {
8302774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
8312774f206SBjoern A. Zeeb 
8322774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_AOAC_GLOBAL_INFO);
8332774f206SBjoern A. Zeeb 
8342774f206SBjoern A. Zeeb 	SET_AOAC_GLOBAL_INFO_PAIRWISE_ENC_ALG(h2c_pkt, pairwise_key_enc);
8352774f206SBjoern A. Zeeb 	SET_AOAC_GLOBAL_INFO_GROUP_ENC_ALG(h2c_pkt, group_key_enc);
8362774f206SBjoern A. Zeeb 
8372774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
8382774f206SBjoern A. Zeeb }
8392774f206SBjoern A. Zeeb 
8402774f206SBjoern A. Zeeb void rtw_fw_set_remote_wake_ctrl_cmd(struct rtw_dev *rtwdev, bool enable)
8412774f206SBjoern A. Zeeb {
8422774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
8432774f206SBjoern A. Zeeb 
8442774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_REMOTE_WAKE_CTRL);
8452774f206SBjoern A. Zeeb 
8462774f206SBjoern A. Zeeb 	SET_REMOTE_WAKECTRL_ENABLE(h2c_pkt, enable);
8472774f206SBjoern A. Zeeb 
8482774f206SBjoern A. Zeeb 	if (rtw_wow_no_link(rtwdev))
8492774f206SBjoern A. Zeeb 		SET_REMOTE_WAKE_CTRL_NLO_OFFLOAD_EN(h2c_pkt, enable);
8502774f206SBjoern A. Zeeb 
8512774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
8522774f206SBjoern A. Zeeb }
8532774f206SBjoern A. Zeeb 
8542774f206SBjoern A. Zeeb static u8 rtw_get_rsvd_page_location(struct rtw_dev *rtwdev,
8552774f206SBjoern A. Zeeb 				     enum rtw_rsvd_packet_type type)
8562774f206SBjoern A. Zeeb {
8572774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
8582774f206SBjoern A. Zeeb 	u8 location = 0;
8592774f206SBjoern A. Zeeb 
8602774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) {
8612774f206SBjoern A. Zeeb 		if (type == rsvd_pkt->type)
8622774f206SBjoern A. Zeeb 			location = rsvd_pkt->page;
8632774f206SBjoern A. Zeeb 	}
8642774f206SBjoern A. Zeeb 
8652774f206SBjoern A. Zeeb 	return location;
8662774f206SBjoern A. Zeeb }
8672774f206SBjoern A. Zeeb 
8682774f206SBjoern A. Zeeb void rtw_fw_set_nlo_info(struct rtw_dev *rtwdev, bool enable)
8692774f206SBjoern A. Zeeb {
8702774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
8712774f206SBjoern A. Zeeb 	u8 loc_nlo;
8722774f206SBjoern A. Zeeb 
8732774f206SBjoern A. Zeeb 	loc_nlo = rtw_get_rsvd_page_location(rtwdev, RSVD_NLO_INFO);
8742774f206SBjoern A. Zeeb 
8752774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_NLO_INFO);
8762774f206SBjoern A. Zeeb 
8772774f206SBjoern A. Zeeb 	SET_NLO_FUN_EN(h2c_pkt, enable);
8782774f206SBjoern A. Zeeb 	if (enable) {
8792774f206SBjoern A. Zeeb 		if (rtw_get_lps_deep_mode(rtwdev) != LPS_DEEP_MODE_NONE)
8802774f206SBjoern A. Zeeb 			SET_NLO_PS_32K(h2c_pkt, enable);
8812774f206SBjoern A. Zeeb 		SET_NLO_IGNORE_SECURITY(h2c_pkt, enable);
8822774f206SBjoern A. Zeeb 		SET_NLO_LOC_NLO_INFO(h2c_pkt, loc_nlo);
8832774f206SBjoern A. Zeeb 	}
8842774f206SBjoern A. Zeeb 
8852774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
8862774f206SBjoern A. Zeeb }
8872774f206SBjoern A. Zeeb 
888*90aac0d8SBjoern A. Zeeb void rtw_fw_set_recover_bt_device(struct rtw_dev *rtwdev)
889*90aac0d8SBjoern A. Zeeb {
890*90aac0d8SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
891*90aac0d8SBjoern A. Zeeb 
892*90aac0d8SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RECOVER_BT_DEV);
893*90aac0d8SBjoern A. Zeeb 	SET_RECOVER_BT_DEV_EN(h2c_pkt, 1);
894*90aac0d8SBjoern A. Zeeb 
895*90aac0d8SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
896*90aac0d8SBjoern A. Zeeb }
897*90aac0d8SBjoern A. Zeeb 
8982774f206SBjoern A. Zeeb void rtw_fw_set_pg_info(struct rtw_dev *rtwdev)
8992774f206SBjoern A. Zeeb {
9002774f206SBjoern A. Zeeb 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
9012774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
9022774f206SBjoern A. Zeeb 	u8 loc_pg, loc_dpk;
9032774f206SBjoern A. Zeeb 
9042774f206SBjoern A. Zeeb 	loc_pg = rtw_get_rsvd_page_location(rtwdev, RSVD_LPS_PG_INFO);
9052774f206SBjoern A. Zeeb 	loc_dpk = rtw_get_rsvd_page_location(rtwdev, RSVD_LPS_PG_DPK);
9062774f206SBjoern A. Zeeb 
9072774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_LPS_PG_INFO);
9082774f206SBjoern A. Zeeb 
9092774f206SBjoern A. Zeeb 	LPS_PG_INFO_LOC(h2c_pkt, loc_pg);
9102774f206SBjoern A. Zeeb 	LPS_PG_DPK_LOC(h2c_pkt, loc_dpk);
9112774f206SBjoern A. Zeeb 	LPS_PG_SEC_CAM_EN(h2c_pkt, conf->sec_cam_backup);
9122774f206SBjoern A. Zeeb 	LPS_PG_PATTERN_CAM_EN(h2c_pkt, conf->pattern_cam_backup);
9132774f206SBjoern A. Zeeb 
9142774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
9152774f206SBjoern A. Zeeb }
9162774f206SBjoern A. Zeeb 
9172774f206SBjoern A. Zeeb static u8 rtw_get_rsvd_page_probe_req_location(struct rtw_dev *rtwdev,
9182774f206SBjoern A. Zeeb 					       struct cfg80211_ssid *ssid)
9192774f206SBjoern A. Zeeb {
9202774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
9212774f206SBjoern A. Zeeb 	u8 location = 0;
9222774f206SBjoern A. Zeeb 
9232774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) {
9242774f206SBjoern A. Zeeb 		if (rsvd_pkt->type != RSVD_PROBE_REQ)
9252774f206SBjoern A. Zeeb 			continue;
9262774f206SBjoern A. Zeeb 		if ((!ssid && !rsvd_pkt->ssid) ||
9272774f206SBjoern A. Zeeb 		    rtw_ssid_equal(rsvd_pkt->ssid, ssid))
9282774f206SBjoern A. Zeeb 			location = rsvd_pkt->page;
9292774f206SBjoern A. Zeeb 	}
9302774f206SBjoern A. Zeeb 
9312774f206SBjoern A. Zeeb 	return location;
9322774f206SBjoern A. Zeeb }
9332774f206SBjoern A. Zeeb 
9342774f206SBjoern A. Zeeb static u16 rtw_get_rsvd_page_probe_req_size(struct rtw_dev *rtwdev,
9352774f206SBjoern A. Zeeb 					    struct cfg80211_ssid *ssid)
9362774f206SBjoern A. Zeeb {
9372774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
9382774f206SBjoern A. Zeeb 	u16 size = 0;
9392774f206SBjoern A. Zeeb 
9402774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) {
9412774f206SBjoern A. Zeeb 		if (rsvd_pkt->type != RSVD_PROBE_REQ)
9422774f206SBjoern A. Zeeb 			continue;
9432774f206SBjoern A. Zeeb 		if ((!ssid && !rsvd_pkt->ssid) ||
9442774f206SBjoern A. Zeeb 		    rtw_ssid_equal(rsvd_pkt->ssid, ssid))
9452774f206SBjoern A. Zeeb 			size = rsvd_pkt->probe_req_size;
9462774f206SBjoern A. Zeeb 	}
9472774f206SBjoern A. Zeeb 
9482774f206SBjoern A. Zeeb 	return size;
9492774f206SBjoern A. Zeeb }
9502774f206SBjoern A. Zeeb 
9512774f206SBjoern A. Zeeb void rtw_send_rsvd_page_h2c(struct rtw_dev *rtwdev)
9522774f206SBjoern A. Zeeb {
9532774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
9542774f206SBjoern A. Zeeb 	u8 location = 0;
9552774f206SBjoern A. Zeeb 
9562774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RSVD_PAGE);
9572774f206SBjoern A. Zeeb 
9582774f206SBjoern A. Zeeb 	location = rtw_get_rsvd_page_location(rtwdev, RSVD_PROBE_RESP);
9592774f206SBjoern A. Zeeb 	*(h2c_pkt + 1) = location;
9602774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_PROBE_RESP loc: %d\n", location);
9612774f206SBjoern A. Zeeb 
9622774f206SBjoern A. Zeeb 	location = rtw_get_rsvd_page_location(rtwdev, RSVD_PS_POLL);
9632774f206SBjoern A. Zeeb 	*(h2c_pkt + 2) = location;
9642774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_PS_POLL loc: %d\n", location);
9652774f206SBjoern A. Zeeb 
9662774f206SBjoern A. Zeeb 	location = rtw_get_rsvd_page_location(rtwdev, RSVD_NULL);
9672774f206SBjoern A. Zeeb 	*(h2c_pkt + 3) = location;
9682774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_NULL loc: %d\n", location);
9692774f206SBjoern A. Zeeb 
9702774f206SBjoern A. Zeeb 	location = rtw_get_rsvd_page_location(rtwdev, RSVD_QOS_NULL);
9712774f206SBjoern A. Zeeb 	*(h2c_pkt + 4) = location;
9722774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_QOS_NULL loc: %d\n", location);
9732774f206SBjoern A. Zeeb 
9742774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
9752774f206SBjoern A. Zeeb }
9762774f206SBjoern A. Zeeb 
9772774f206SBjoern A. Zeeb static struct sk_buff *rtw_nlo_info_get(struct ieee80211_hw *hw)
9782774f206SBjoern A. Zeeb {
9792774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = hw->priv;
980*90aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
9812774f206SBjoern A. Zeeb 	struct rtw_pno_request *pno_req = &rtwdev->wow.pno_req;
9822774f206SBjoern A. Zeeb 	struct rtw_nlo_info_hdr *nlo_hdr;
9832774f206SBjoern A. Zeeb 	struct cfg80211_ssid *ssid;
9842774f206SBjoern A. Zeeb 	struct sk_buff *skb;
9852774f206SBjoern A. Zeeb 	u8 *pos, loc;
9862774f206SBjoern A. Zeeb 	u32 size;
9872774f206SBjoern A. Zeeb 	int i;
9882774f206SBjoern A. Zeeb 
9892774f206SBjoern A. Zeeb 	if (!pno_req->inited || !pno_req->match_set_cnt)
9902774f206SBjoern A. Zeeb 		return NULL;
9912774f206SBjoern A. Zeeb 
9922774f206SBjoern A. Zeeb 	size = sizeof(struct rtw_nlo_info_hdr) + pno_req->match_set_cnt *
9932774f206SBjoern A. Zeeb 		      IEEE80211_MAX_SSID_LEN + chip->tx_pkt_desc_sz;
9942774f206SBjoern A. Zeeb 
9952774f206SBjoern A. Zeeb 	skb = alloc_skb(size, GFP_KERNEL);
9962774f206SBjoern A. Zeeb 	if (!skb)
9972774f206SBjoern A. Zeeb 		return NULL;
9982774f206SBjoern A. Zeeb 
9992774f206SBjoern A. Zeeb 	skb_reserve(skb, chip->tx_pkt_desc_sz);
10002774f206SBjoern A. Zeeb 
10012774f206SBjoern A. Zeeb 	nlo_hdr = skb_put_zero(skb, sizeof(struct rtw_nlo_info_hdr));
10022774f206SBjoern A. Zeeb 
10032774f206SBjoern A. Zeeb 	nlo_hdr->nlo_count = pno_req->match_set_cnt;
10042774f206SBjoern A. Zeeb 	nlo_hdr->hidden_ap_count = pno_req->match_set_cnt;
10052774f206SBjoern A. Zeeb 
10062774f206SBjoern A. Zeeb 	/* pattern check for firmware */
10072774f206SBjoern A. Zeeb 	memset(nlo_hdr->pattern_check, 0xA5, FW_NLO_INFO_CHECK_SIZE);
10082774f206SBjoern A. Zeeb 
10092774f206SBjoern A. Zeeb 	for (i = 0; i < pno_req->match_set_cnt; i++)
10102774f206SBjoern A. Zeeb 		nlo_hdr->ssid_len[i] = pno_req->match_sets[i].ssid.ssid_len;
10112774f206SBjoern A. Zeeb 
10122774f206SBjoern A. Zeeb 	for (i = 0; i < pno_req->match_set_cnt; i++) {
10132774f206SBjoern A. Zeeb 		ssid = &pno_req->match_sets[i].ssid;
10142774f206SBjoern A. Zeeb 		loc  = rtw_get_rsvd_page_probe_req_location(rtwdev, ssid);
10152774f206SBjoern A. Zeeb 		if (!loc) {
10162774f206SBjoern A. Zeeb 			rtw_err(rtwdev, "failed to get probe req rsvd loc\n");
10172774f206SBjoern A. Zeeb 			kfree_skb(skb);
10182774f206SBjoern A. Zeeb 			return NULL;
10192774f206SBjoern A. Zeeb 		}
10202774f206SBjoern A. Zeeb 		nlo_hdr->location[i] = loc;
10212774f206SBjoern A. Zeeb 	}
10222774f206SBjoern A. Zeeb 
10232774f206SBjoern A. Zeeb 	for (i = 0; i < pno_req->match_set_cnt; i++) {
10242774f206SBjoern A. Zeeb 		pos = skb_put_zero(skb, IEEE80211_MAX_SSID_LEN);
10252774f206SBjoern A. Zeeb 		memcpy(pos, pno_req->match_sets[i].ssid.ssid,
10262774f206SBjoern A. Zeeb 		       pno_req->match_sets[i].ssid.ssid_len);
10272774f206SBjoern A. Zeeb 	}
10282774f206SBjoern A. Zeeb 
10292774f206SBjoern A. Zeeb 	return skb;
10302774f206SBjoern A. Zeeb }
10312774f206SBjoern A. Zeeb 
10322774f206SBjoern A. Zeeb static struct sk_buff *rtw_cs_channel_info_get(struct ieee80211_hw *hw)
10332774f206SBjoern A. Zeeb {
10342774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = hw->priv;
1035*90aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
10362774f206SBjoern A. Zeeb 	struct rtw_pno_request *pno_req = &rtwdev->wow.pno_req;
10372774f206SBjoern A. Zeeb 	struct ieee80211_channel *channels = pno_req->channels;
10382774f206SBjoern A. Zeeb 	struct sk_buff *skb;
10392774f206SBjoern A. Zeeb 	int count =  pno_req->channel_cnt;
10402774f206SBjoern A. Zeeb 	u8 *pos;
10412774f206SBjoern A. Zeeb 	int i = 0;
10422774f206SBjoern A. Zeeb 
10432774f206SBjoern A. Zeeb 	skb = alloc_skb(4 * count + chip->tx_pkt_desc_sz, GFP_KERNEL);
10442774f206SBjoern A. Zeeb 	if (!skb)
10452774f206SBjoern A. Zeeb 		return NULL;
10462774f206SBjoern A. Zeeb 
10472774f206SBjoern A. Zeeb 	skb_reserve(skb, chip->tx_pkt_desc_sz);
10482774f206SBjoern A. Zeeb 
10492774f206SBjoern A. Zeeb 	for (i = 0; i < count; i++) {
10502774f206SBjoern A. Zeeb 		pos = skb_put_zero(skb, 4);
10512774f206SBjoern A. Zeeb 
10522774f206SBjoern A. Zeeb 		CHSW_INFO_SET_CH(pos, channels[i].hw_value);
10532774f206SBjoern A. Zeeb 
10542774f206SBjoern A. Zeeb 		if (channels[i].flags & IEEE80211_CHAN_RADAR)
10552774f206SBjoern A. Zeeb 			CHSW_INFO_SET_ACTION_ID(pos, 0);
10562774f206SBjoern A. Zeeb 		else
10572774f206SBjoern A. Zeeb 			CHSW_INFO_SET_ACTION_ID(pos, 1);
10582774f206SBjoern A. Zeeb 		CHSW_INFO_SET_TIMEOUT(pos, 1);
10592774f206SBjoern A. Zeeb 		CHSW_INFO_SET_PRI_CH_IDX(pos, 1);
10602774f206SBjoern A. Zeeb 		CHSW_INFO_SET_BW(pos, 0);
10612774f206SBjoern A. Zeeb 	}
10622774f206SBjoern A. Zeeb 
10632774f206SBjoern A. Zeeb 	return skb;
10642774f206SBjoern A. Zeeb }
10652774f206SBjoern A. Zeeb 
10662774f206SBjoern A. Zeeb static struct sk_buff *rtw_lps_pg_dpk_get(struct ieee80211_hw *hw)
10672774f206SBjoern A. Zeeb {
10682774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = hw->priv;
1069*90aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
10702774f206SBjoern A. Zeeb 	struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info;
10712774f206SBjoern A. Zeeb 	struct rtw_lps_pg_dpk_hdr *dpk_hdr;
10722774f206SBjoern A. Zeeb 	struct sk_buff *skb;
10732774f206SBjoern A. Zeeb 	u32 size;
10742774f206SBjoern A. Zeeb 
10752774f206SBjoern A. Zeeb 	size = chip->tx_pkt_desc_sz + sizeof(*dpk_hdr);
10762774f206SBjoern A. Zeeb 	skb = alloc_skb(size, GFP_KERNEL);
10772774f206SBjoern A. Zeeb 	if (!skb)
10782774f206SBjoern A. Zeeb 		return NULL;
10792774f206SBjoern A. Zeeb 
10802774f206SBjoern A. Zeeb 	skb_reserve(skb, chip->tx_pkt_desc_sz);
10812774f206SBjoern A. Zeeb 	dpk_hdr = skb_put_zero(skb, sizeof(*dpk_hdr));
10822774f206SBjoern A. Zeeb 	dpk_hdr->dpk_ch = dpk_info->dpk_ch;
10832774f206SBjoern A. Zeeb 	dpk_hdr->dpk_path_ok = dpk_info->dpk_path_ok[0];
10842774f206SBjoern A. Zeeb 	memcpy(dpk_hdr->dpk_txagc, dpk_info->dpk_txagc, 2);
10852774f206SBjoern A. Zeeb 	memcpy(dpk_hdr->dpk_gs, dpk_info->dpk_gs, 4);
10862774f206SBjoern A. Zeeb 	memcpy(dpk_hdr->coef, dpk_info->coef, 160);
10872774f206SBjoern A. Zeeb 
10882774f206SBjoern A. Zeeb 	return skb;
10892774f206SBjoern A. Zeeb }
10902774f206SBjoern A. Zeeb 
10912774f206SBjoern A. Zeeb static struct sk_buff *rtw_lps_pg_info_get(struct ieee80211_hw *hw)
10922774f206SBjoern A. Zeeb {
10932774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = hw->priv;
1094*90aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
10952774f206SBjoern A. Zeeb 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
10962774f206SBjoern A. Zeeb 	struct rtw_lps_pg_info_hdr *pg_info_hdr;
10972774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
10982774f206SBjoern A. Zeeb 	struct sk_buff *skb;
10992774f206SBjoern A. Zeeb 	u32 size;
11002774f206SBjoern A. Zeeb 
11012774f206SBjoern A. Zeeb 	size = chip->tx_pkt_desc_sz + sizeof(*pg_info_hdr);
11022774f206SBjoern A. Zeeb 	skb = alloc_skb(size, GFP_KERNEL);
11032774f206SBjoern A. Zeeb 	if (!skb)
11042774f206SBjoern A. Zeeb 		return NULL;
11052774f206SBjoern A. Zeeb 
11062774f206SBjoern A. Zeeb 	skb_reserve(skb, chip->tx_pkt_desc_sz);
11072774f206SBjoern A. Zeeb 	pg_info_hdr = skb_put_zero(skb, sizeof(*pg_info_hdr));
11082774f206SBjoern A. Zeeb 	pg_info_hdr->tx_bu_page_count = rtwdev->fifo.rsvd_drv_pg_num;
11092774f206SBjoern A. Zeeb 	pg_info_hdr->macid = find_first_bit(rtwdev->mac_id_map, RTW_MAX_MAC_ID_NUM);
11102774f206SBjoern A. Zeeb 	pg_info_hdr->sec_cam_count =
11112774f206SBjoern A. Zeeb 		rtw_sec_cam_pg_backup(rtwdev, pg_info_hdr->sec_cam);
11122774f206SBjoern A. Zeeb 	pg_info_hdr->pattern_count = rtw_wow->pattern_cnt;
11132774f206SBjoern A. Zeeb 
11142774f206SBjoern A. Zeeb 	conf->sec_cam_backup = pg_info_hdr->sec_cam_count != 0;
11152774f206SBjoern A. Zeeb 	conf->pattern_cam_backup = rtw_wow->pattern_cnt != 0;
11162774f206SBjoern A. Zeeb 
11172774f206SBjoern A. Zeeb 	return skb;
11182774f206SBjoern A. Zeeb }
11192774f206SBjoern A. Zeeb 
11202774f206SBjoern A. Zeeb static struct sk_buff *rtw_get_rsvd_page_skb(struct ieee80211_hw *hw,
11212774f206SBjoern A. Zeeb 					     struct rtw_rsvd_page *rsvd_pkt)
11222774f206SBjoern A. Zeeb {
11232774f206SBjoern A. Zeeb 	struct ieee80211_vif *vif;
11242774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif;
11252774f206SBjoern A. Zeeb 	struct sk_buff *skb_new;
11262774f206SBjoern A. Zeeb 	struct cfg80211_ssid *ssid;
11279c951734SBjoern A. Zeeb 	u16 tim_offset = 0;
11282774f206SBjoern A. Zeeb 
11292774f206SBjoern A. Zeeb 	if (rsvd_pkt->type == RSVD_DUMMY) {
11302774f206SBjoern A. Zeeb 		skb_new = alloc_skb(1, GFP_KERNEL);
11312774f206SBjoern A. Zeeb 		if (!skb_new)
11322774f206SBjoern A. Zeeb 			return NULL;
11332774f206SBjoern A. Zeeb 
11342774f206SBjoern A. Zeeb 		skb_put(skb_new, 1);
11352774f206SBjoern A. Zeeb 		return skb_new;
11362774f206SBjoern A. Zeeb 	}
11372774f206SBjoern A. Zeeb 
11382774f206SBjoern A. Zeeb 	rtwvif = rsvd_pkt->rtwvif;
11392774f206SBjoern A. Zeeb 	if (!rtwvif)
11402774f206SBjoern A. Zeeb 		return NULL;
11412774f206SBjoern A. Zeeb 
11422774f206SBjoern A. Zeeb 	vif = rtwvif_to_vif(rtwvif);
11432774f206SBjoern A. Zeeb 
11442774f206SBjoern A. Zeeb 	switch (rsvd_pkt->type) {
11452774f206SBjoern A. Zeeb 	case RSVD_BEACON:
1146549198b1SBjoern A. Zeeb 		skb_new = ieee80211_beacon_get_tim(hw, vif, &tim_offset, NULL, 0);
11479c951734SBjoern A. Zeeb 		rsvd_pkt->tim_offset = tim_offset;
11482774f206SBjoern A. Zeeb 		break;
11492774f206SBjoern A. Zeeb 	case RSVD_PS_POLL:
11502774f206SBjoern A. Zeeb 		skb_new = ieee80211_pspoll_get(hw, vif);
11512774f206SBjoern A. Zeeb 		break;
11522774f206SBjoern A. Zeeb 	case RSVD_PROBE_RESP:
11532774f206SBjoern A. Zeeb 		skb_new = ieee80211_proberesp_get(hw, vif);
11542774f206SBjoern A. Zeeb 		break;
11552774f206SBjoern A. Zeeb 	case RSVD_NULL:
1156*90aac0d8SBjoern A. Zeeb 		skb_new = ieee80211_nullfunc_get(hw, vif, -1, false);
11572774f206SBjoern A. Zeeb 		break;
11582774f206SBjoern A. Zeeb 	case RSVD_QOS_NULL:
1159*90aac0d8SBjoern A. Zeeb 		skb_new = ieee80211_nullfunc_get(hw, vif, -1, true);
11602774f206SBjoern A. Zeeb 		break;
11612774f206SBjoern A. Zeeb 	case RSVD_LPS_PG_DPK:
11622774f206SBjoern A. Zeeb 		skb_new = rtw_lps_pg_dpk_get(hw);
11632774f206SBjoern A. Zeeb 		break;
11642774f206SBjoern A. Zeeb 	case RSVD_LPS_PG_INFO:
11652774f206SBjoern A. Zeeb 		skb_new = rtw_lps_pg_info_get(hw);
11662774f206SBjoern A. Zeeb 		break;
11672774f206SBjoern A. Zeeb 	case RSVD_PROBE_REQ:
11682774f206SBjoern A. Zeeb 		ssid = (struct cfg80211_ssid *)rsvd_pkt->ssid;
11692774f206SBjoern A. Zeeb 		if (ssid)
11702774f206SBjoern A. Zeeb 			skb_new = ieee80211_probereq_get(hw, vif->addr,
11712774f206SBjoern A. Zeeb 							 ssid->ssid,
11722774f206SBjoern A. Zeeb 							 ssid->ssid_len, 0);
11732774f206SBjoern A. Zeeb 		else
11742774f206SBjoern A. Zeeb 			skb_new = ieee80211_probereq_get(hw, vif->addr, NULL, 0, 0);
11752774f206SBjoern A. Zeeb 		if (skb_new)
11762774f206SBjoern A. Zeeb 			rsvd_pkt->probe_req_size = (u16)skb_new->len;
11772774f206SBjoern A. Zeeb 		break;
11782774f206SBjoern A. Zeeb 	case RSVD_NLO_INFO:
11792774f206SBjoern A. Zeeb 		skb_new = rtw_nlo_info_get(hw);
11802774f206SBjoern A. Zeeb 		break;
11812774f206SBjoern A. Zeeb 	case RSVD_CH_INFO:
11822774f206SBjoern A. Zeeb 		skb_new = rtw_cs_channel_info_get(hw);
11832774f206SBjoern A. Zeeb 		break;
11842774f206SBjoern A. Zeeb 	default:
11852774f206SBjoern A. Zeeb 		return NULL;
11862774f206SBjoern A. Zeeb 	}
11872774f206SBjoern A. Zeeb 
11882774f206SBjoern A. Zeeb 	if (!skb_new)
11892774f206SBjoern A. Zeeb 		return NULL;
11902774f206SBjoern A. Zeeb 
11912774f206SBjoern A. Zeeb 	return skb_new;
11922774f206SBjoern A. Zeeb }
11932774f206SBjoern A. Zeeb 
11942774f206SBjoern A. Zeeb static void rtw_fill_rsvd_page_desc(struct rtw_dev *rtwdev, struct sk_buff *skb,
11952774f206SBjoern A. Zeeb 				    enum rtw_rsvd_packet_type type)
11962774f206SBjoern A. Zeeb {
11972774f206SBjoern A. Zeeb 	struct rtw_tx_pkt_info pkt_info = {0};
1198*90aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
11992774f206SBjoern A. Zeeb 	u8 *pkt_desc;
12002774f206SBjoern A. Zeeb 
12012774f206SBjoern A. Zeeb 	rtw_tx_rsvd_page_pkt_info_update(rtwdev, &pkt_info, skb, type);
12022774f206SBjoern A. Zeeb 	pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
12032774f206SBjoern A. Zeeb 	memset(pkt_desc, 0, chip->tx_pkt_desc_sz);
12042774f206SBjoern A. Zeeb 	rtw_tx_fill_tx_desc(&pkt_info, skb);
12052774f206SBjoern A. Zeeb }
12062774f206SBjoern A. Zeeb 
12072774f206SBjoern A. Zeeb static inline u8 rtw_len_to_page(unsigned int len, u8 page_size)
12082774f206SBjoern A. Zeeb {
12092774f206SBjoern A. Zeeb 	return DIV_ROUND_UP(len, page_size);
12102774f206SBjoern A. Zeeb }
12112774f206SBjoern A. Zeeb 
12122774f206SBjoern A. Zeeb static void rtw_rsvd_page_list_to_buf(struct rtw_dev *rtwdev, u8 page_size,
12132774f206SBjoern A. Zeeb 				      u8 page_margin, u32 page, u8 *buf,
12142774f206SBjoern A. Zeeb 				      struct rtw_rsvd_page *rsvd_pkt)
12152774f206SBjoern A. Zeeb {
12162774f206SBjoern A. Zeeb 	struct sk_buff *skb = rsvd_pkt->skb;
12172774f206SBjoern A. Zeeb 
12182774f206SBjoern A. Zeeb 	if (page >= 1)
12192774f206SBjoern A. Zeeb 		memcpy(buf + page_margin + page_size * (page - 1),
12202774f206SBjoern A. Zeeb 		       skb->data, skb->len);
12212774f206SBjoern A. Zeeb 	else
12222774f206SBjoern A. Zeeb 		memcpy(buf, skb->data, skb->len);
12232774f206SBjoern A. Zeeb }
12242774f206SBjoern A. Zeeb 
12252774f206SBjoern A. Zeeb static struct rtw_rsvd_page *rtw_alloc_rsvd_page(struct rtw_dev *rtwdev,
12262774f206SBjoern A. Zeeb 						 enum rtw_rsvd_packet_type type,
12272774f206SBjoern A. Zeeb 						 bool txdesc)
12282774f206SBjoern A. Zeeb {
12292774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt = NULL;
12302774f206SBjoern A. Zeeb 
12312774f206SBjoern A. Zeeb 	rsvd_pkt = kzalloc(sizeof(*rsvd_pkt), GFP_KERNEL);
12322774f206SBjoern A. Zeeb 
12332774f206SBjoern A. Zeeb 	if (!rsvd_pkt)
12342774f206SBjoern A. Zeeb 		return NULL;
12352774f206SBjoern A. Zeeb 
12362774f206SBjoern A. Zeeb 	INIT_LIST_HEAD(&rsvd_pkt->vif_list);
12372774f206SBjoern A. Zeeb 	INIT_LIST_HEAD(&rsvd_pkt->build_list);
12382774f206SBjoern A. Zeeb 	rsvd_pkt->type = type;
12392774f206SBjoern A. Zeeb 	rsvd_pkt->add_txdesc = txdesc;
12402774f206SBjoern A. Zeeb 
12412774f206SBjoern A. Zeeb 	return rsvd_pkt;
12422774f206SBjoern A. Zeeb }
12432774f206SBjoern A. Zeeb 
12442774f206SBjoern A. Zeeb static void rtw_insert_rsvd_page(struct rtw_dev *rtwdev,
12452774f206SBjoern A. Zeeb 				 struct rtw_vif *rtwvif,
12462774f206SBjoern A. Zeeb 				 struct rtw_rsvd_page *rsvd_pkt)
12472774f206SBjoern A. Zeeb {
12482774f206SBjoern A. Zeeb 	lockdep_assert_held(&rtwdev->mutex);
12492774f206SBjoern A. Zeeb 
12502774f206SBjoern A. Zeeb 	list_add_tail(&rsvd_pkt->vif_list, &rtwvif->rsvd_page_list);
12512774f206SBjoern A. Zeeb }
12522774f206SBjoern A. Zeeb 
12532774f206SBjoern A. Zeeb static void rtw_add_rsvd_page(struct rtw_dev *rtwdev,
12542774f206SBjoern A. Zeeb 			      struct rtw_vif *rtwvif,
12552774f206SBjoern A. Zeeb 			      enum rtw_rsvd_packet_type type,
12562774f206SBjoern A. Zeeb 			      bool txdesc)
12572774f206SBjoern A. Zeeb {
12582774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
12592774f206SBjoern A. Zeeb 
12602774f206SBjoern A. Zeeb 	rsvd_pkt = rtw_alloc_rsvd_page(rtwdev, type, txdesc);
12612774f206SBjoern A. Zeeb 	if (!rsvd_pkt) {
12622774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to alloc rsvd page %d\n", type);
12632774f206SBjoern A. Zeeb 		return;
12642774f206SBjoern A. Zeeb 	}
12652774f206SBjoern A. Zeeb 
12662774f206SBjoern A. Zeeb 	rsvd_pkt->rtwvif = rtwvif;
12672774f206SBjoern A. Zeeb 	rtw_insert_rsvd_page(rtwdev, rtwvif, rsvd_pkt);
12682774f206SBjoern A. Zeeb }
12692774f206SBjoern A. Zeeb 
12702774f206SBjoern A. Zeeb static void rtw_add_rsvd_page_probe_req(struct rtw_dev *rtwdev,
12712774f206SBjoern A. Zeeb 					struct rtw_vif *rtwvif,
12722774f206SBjoern A. Zeeb 					struct cfg80211_ssid *ssid)
12732774f206SBjoern A. Zeeb {
12742774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
12752774f206SBjoern A. Zeeb 
12762774f206SBjoern A. Zeeb 	rsvd_pkt = rtw_alloc_rsvd_page(rtwdev, RSVD_PROBE_REQ, true);
12772774f206SBjoern A. Zeeb 	if (!rsvd_pkt) {
12782774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to alloc probe req rsvd page\n");
12792774f206SBjoern A. Zeeb 		return;
12802774f206SBjoern A. Zeeb 	}
12812774f206SBjoern A. Zeeb 
12822774f206SBjoern A. Zeeb 	rsvd_pkt->rtwvif = rtwvif;
12832774f206SBjoern A. Zeeb 	rsvd_pkt->ssid = ssid;
12842774f206SBjoern A. Zeeb 	rtw_insert_rsvd_page(rtwdev, rtwvif, rsvd_pkt);
12852774f206SBjoern A. Zeeb }
12862774f206SBjoern A. Zeeb 
12872774f206SBjoern A. Zeeb void rtw_remove_rsvd_page(struct rtw_dev *rtwdev,
12882774f206SBjoern A. Zeeb 			  struct rtw_vif *rtwvif)
12892774f206SBjoern A. Zeeb {
12902774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt, *tmp;
12912774f206SBjoern A. Zeeb 
12922774f206SBjoern A. Zeeb 	lockdep_assert_held(&rtwdev->mutex);
12932774f206SBjoern A. Zeeb 
12942774f206SBjoern A. Zeeb 	/* remove all of the rsvd pages for vif */
12952774f206SBjoern A. Zeeb 	list_for_each_entry_safe(rsvd_pkt, tmp, &rtwvif->rsvd_page_list,
12962774f206SBjoern A. Zeeb 				 vif_list) {
12972774f206SBjoern A. Zeeb 		list_del(&rsvd_pkt->vif_list);
12982774f206SBjoern A. Zeeb 		if (!list_empty(&rsvd_pkt->build_list))
12992774f206SBjoern A. Zeeb 			list_del(&rsvd_pkt->build_list);
13002774f206SBjoern A. Zeeb 		kfree(rsvd_pkt);
13012774f206SBjoern A. Zeeb 	}
13022774f206SBjoern A. Zeeb }
13032774f206SBjoern A. Zeeb 
13042774f206SBjoern A. Zeeb void rtw_add_rsvd_page_bcn(struct rtw_dev *rtwdev,
13052774f206SBjoern A. Zeeb 			   struct rtw_vif *rtwvif)
13062774f206SBjoern A. Zeeb {
13072774f206SBjoern A. Zeeb 	struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
13082774f206SBjoern A. Zeeb 
13092774f206SBjoern A. Zeeb 	if (vif->type != NL80211_IFTYPE_AP &&
13102774f206SBjoern A. Zeeb 	    vif->type != NL80211_IFTYPE_ADHOC &&
13112774f206SBjoern A. Zeeb 	    vif->type != NL80211_IFTYPE_MESH_POINT) {
13122774f206SBjoern A. Zeeb 		rtw_warn(rtwdev, "Cannot add beacon rsvd page for %d\n",
13132774f206SBjoern A. Zeeb 			 vif->type);
13142774f206SBjoern A. Zeeb 		return;
13152774f206SBjoern A. Zeeb 	}
13162774f206SBjoern A. Zeeb 
13172774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_BEACON, false);
13182774f206SBjoern A. Zeeb }
13192774f206SBjoern A. Zeeb 
13202774f206SBjoern A. Zeeb void rtw_add_rsvd_page_pno(struct rtw_dev *rtwdev,
13212774f206SBjoern A. Zeeb 			   struct rtw_vif *rtwvif)
13222774f206SBjoern A. Zeeb {
13232774f206SBjoern A. Zeeb 	struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
13242774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
13252774f206SBjoern A. Zeeb 	struct rtw_pno_request *rtw_pno_req = &rtw_wow->pno_req;
13262774f206SBjoern A. Zeeb 	struct cfg80211_ssid *ssid;
13272774f206SBjoern A. Zeeb 	int i;
13282774f206SBjoern A. Zeeb 
13292774f206SBjoern A. Zeeb 	if (vif->type != NL80211_IFTYPE_STATION) {
13302774f206SBjoern A. Zeeb 		rtw_warn(rtwdev, "Cannot add PNO rsvd page for %d\n",
13312774f206SBjoern A. Zeeb 			 vif->type);
13322774f206SBjoern A. Zeeb 		return;
13332774f206SBjoern A. Zeeb 	}
13342774f206SBjoern A. Zeeb 
13352774f206SBjoern A. Zeeb 	for (i = 0 ; i < rtw_pno_req->match_set_cnt; i++) {
13362774f206SBjoern A. Zeeb 		ssid = &rtw_pno_req->match_sets[i].ssid;
13372774f206SBjoern A. Zeeb 		rtw_add_rsvd_page_probe_req(rtwdev, rtwvif, ssid);
13382774f206SBjoern A. Zeeb 	}
13392774f206SBjoern A. Zeeb 
13402774f206SBjoern A. Zeeb 	rtw_add_rsvd_page_probe_req(rtwdev, rtwvif, NULL);
13412774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_NLO_INFO, false);
13422774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_CH_INFO, true);
13432774f206SBjoern A. Zeeb }
13442774f206SBjoern A. Zeeb 
13452774f206SBjoern A. Zeeb void rtw_add_rsvd_page_sta(struct rtw_dev *rtwdev,
13462774f206SBjoern A. Zeeb 			   struct rtw_vif *rtwvif)
13472774f206SBjoern A. Zeeb {
13482774f206SBjoern A. Zeeb 	struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
13492774f206SBjoern A. Zeeb 
13502774f206SBjoern A. Zeeb 	if (vif->type != NL80211_IFTYPE_STATION) {
13512774f206SBjoern A. Zeeb 		rtw_warn(rtwdev, "Cannot add sta rsvd page for %d\n",
13522774f206SBjoern A. Zeeb 			 vif->type);
13532774f206SBjoern A. Zeeb 		return;
13542774f206SBjoern A. Zeeb 	}
13552774f206SBjoern A. Zeeb 
13562774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_PS_POLL, true);
13572774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_QOS_NULL, true);
13582774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_NULL, true);
13592774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_LPS_PG_DPK, true);
13602774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_LPS_PG_INFO, true);
13612774f206SBjoern A. Zeeb }
13622774f206SBjoern A. Zeeb 
13632774f206SBjoern A. Zeeb int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
13642774f206SBjoern A. Zeeb 				u8 *buf, u32 size)
13652774f206SBjoern A. Zeeb {
13662774f206SBjoern A. Zeeb 	u8 bckp[2];
13672774f206SBjoern A. Zeeb 	u8 val;
13682774f206SBjoern A. Zeeb 	u16 rsvd_pg_head;
13692774f206SBjoern A. Zeeb 	u32 bcn_valid_addr;
13702774f206SBjoern A. Zeeb 	u32 bcn_valid_mask;
13712774f206SBjoern A. Zeeb 	int ret;
13722774f206SBjoern A. Zeeb 
13732774f206SBjoern A. Zeeb 	lockdep_assert_held(&rtwdev->mutex);
13742774f206SBjoern A. Zeeb 
13752774f206SBjoern A. Zeeb 	if (!size)
13762774f206SBjoern A. Zeeb 		return -EINVAL;
13772774f206SBjoern A. Zeeb 
13782774f206SBjoern A. Zeeb 	if (rtw_chip_wcpu_11n(rtwdev)) {
13792774f206SBjoern A. Zeeb 		rtw_write32_set(rtwdev, REG_DWBCN0_CTRL, BIT_BCN_VALID);
13802774f206SBjoern A. Zeeb 	} else {
13812774f206SBjoern A. Zeeb 		pg_addr &= BIT_MASK_BCN_HEAD_1_V1;
13822774f206SBjoern A. Zeeb 		pg_addr |= BIT_BCN_VALID_V1;
13832774f206SBjoern A. Zeeb 		rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, pg_addr);
13842774f206SBjoern A. Zeeb 	}
13852774f206SBjoern A. Zeeb 
13862774f206SBjoern A. Zeeb 	val = rtw_read8(rtwdev, REG_CR + 1);
13872774f206SBjoern A. Zeeb 	bckp[0] = val;
13882774f206SBjoern A. Zeeb 	val |= BIT_ENSWBCN >> 8;
13892774f206SBjoern A. Zeeb 	rtw_write8(rtwdev, REG_CR + 1, val);
13902774f206SBjoern A. Zeeb 
13912774f206SBjoern A. Zeeb 	val = rtw_read8(rtwdev, REG_FWHW_TXQ_CTRL + 2);
13922774f206SBjoern A. Zeeb 	bckp[1] = val;
13932774f206SBjoern A. Zeeb 	val &= ~(BIT_EN_BCNQ_DL >> 16);
13942774f206SBjoern A. Zeeb 	rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, val);
13952774f206SBjoern A. Zeeb 
13962774f206SBjoern A. Zeeb 	ret = rtw_hci_write_data_rsvd_page(rtwdev, buf, size);
13972774f206SBjoern A. Zeeb 	if (ret) {
13982774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to write data to rsvd page\n");
13992774f206SBjoern A. Zeeb 		goto restore;
14002774f206SBjoern A. Zeeb 	}
14012774f206SBjoern A. Zeeb 
14022774f206SBjoern A. Zeeb 	if (rtw_chip_wcpu_11n(rtwdev)) {
14032774f206SBjoern A. Zeeb 		bcn_valid_addr = REG_DWBCN0_CTRL;
14042774f206SBjoern A. Zeeb 		bcn_valid_mask = BIT_BCN_VALID;
14052774f206SBjoern A. Zeeb 	} else {
14062774f206SBjoern A. Zeeb 		bcn_valid_addr = REG_FIFOPAGE_CTRL_2;
14072774f206SBjoern A. Zeeb 		bcn_valid_mask = BIT_BCN_VALID_V1;
14082774f206SBjoern A. Zeeb 	}
14092774f206SBjoern A. Zeeb 
14102774f206SBjoern A. Zeeb 	if (!check_hw_ready(rtwdev, bcn_valid_addr, bcn_valid_mask, 1)) {
14112774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "error beacon valid\n");
14122774f206SBjoern A. Zeeb 		ret = -EBUSY;
14132774f206SBjoern A. Zeeb 	}
14142774f206SBjoern A. Zeeb 
14152774f206SBjoern A. Zeeb restore:
14162774f206SBjoern A. Zeeb 	rsvd_pg_head = rtwdev->fifo.rsvd_boundary;
14172774f206SBjoern A. Zeeb 	rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2,
14182774f206SBjoern A. Zeeb 		    rsvd_pg_head | BIT_BCN_VALID_V1);
14192774f206SBjoern A. Zeeb 	rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, bckp[1]);
14202774f206SBjoern A. Zeeb 	rtw_write8(rtwdev, REG_CR + 1, bckp[0]);
14212774f206SBjoern A. Zeeb 
14222774f206SBjoern A. Zeeb 	return ret;
14232774f206SBjoern A. Zeeb }
14242774f206SBjoern A. Zeeb 
14252774f206SBjoern A. Zeeb static int rtw_download_drv_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size)
14262774f206SBjoern A. Zeeb {
14272774f206SBjoern A. Zeeb 	u32 pg_size;
14282774f206SBjoern A. Zeeb 	u32 pg_num = 0;
14292774f206SBjoern A. Zeeb 	u16 pg_addr = 0;
14302774f206SBjoern A. Zeeb 
14312774f206SBjoern A. Zeeb 	pg_size = rtwdev->chip->page_size;
14322774f206SBjoern A. Zeeb 	pg_num = size / pg_size + ((size & (pg_size - 1)) ? 1 : 0);
14332774f206SBjoern A. Zeeb 	if (pg_num > rtwdev->fifo.rsvd_drv_pg_num)
14342774f206SBjoern A. Zeeb 		return -ENOMEM;
14352774f206SBjoern A. Zeeb 
14362774f206SBjoern A. Zeeb 	pg_addr = rtwdev->fifo.rsvd_drv_addr;
14372774f206SBjoern A. Zeeb 
14382774f206SBjoern A. Zeeb 	return rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, size);
14392774f206SBjoern A. Zeeb }
14402774f206SBjoern A. Zeeb 
14412774f206SBjoern A. Zeeb static void __rtw_build_rsvd_page_reset(struct rtw_dev *rtwdev)
14422774f206SBjoern A. Zeeb {
14432774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt, *tmp;
14442774f206SBjoern A. Zeeb 
14452774f206SBjoern A. Zeeb 	list_for_each_entry_safe(rsvd_pkt, tmp, &rtwdev->rsvd_page_list,
14462774f206SBjoern A. Zeeb 				 build_list) {
14472774f206SBjoern A. Zeeb 		list_del_init(&rsvd_pkt->build_list);
14482774f206SBjoern A. Zeeb 
14492774f206SBjoern A. Zeeb 		/* Don't free except for the dummy rsvd page,
14502774f206SBjoern A. Zeeb 		 * others will be freed when removing vif
14512774f206SBjoern A. Zeeb 		 */
14522774f206SBjoern A. Zeeb 		if (rsvd_pkt->type == RSVD_DUMMY)
14532774f206SBjoern A. Zeeb 			kfree(rsvd_pkt);
14542774f206SBjoern A. Zeeb 	}
14552774f206SBjoern A. Zeeb }
14562774f206SBjoern A. Zeeb 
14572774f206SBjoern A. Zeeb static void rtw_build_rsvd_page_iter(void *data, u8 *mac,
14582774f206SBjoern A. Zeeb 				     struct ieee80211_vif *vif)
14592774f206SBjoern A. Zeeb {
14602774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = data;
14612774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
14622774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
14632774f206SBjoern A. Zeeb 
1464*90aac0d8SBjoern A. Zeeb 	/* AP not yet started, don't gather its rsvd pages */
1465*90aac0d8SBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_AP && !rtwdev->ap_active)
1466*90aac0d8SBjoern A. Zeeb 		return;
1467*90aac0d8SBjoern A. Zeeb 
14682774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwvif->rsvd_page_list, vif_list) {
14692774f206SBjoern A. Zeeb 		if (rsvd_pkt->type == RSVD_BEACON)
14702774f206SBjoern A. Zeeb 			list_add(&rsvd_pkt->build_list,
14712774f206SBjoern A. Zeeb 				 &rtwdev->rsvd_page_list);
14722774f206SBjoern A. Zeeb 		else
14732774f206SBjoern A. Zeeb 			list_add_tail(&rsvd_pkt->build_list,
14742774f206SBjoern A. Zeeb 				      &rtwdev->rsvd_page_list);
14752774f206SBjoern A. Zeeb 	}
14762774f206SBjoern A. Zeeb }
14772774f206SBjoern A. Zeeb 
14782774f206SBjoern A. Zeeb static int  __rtw_build_rsvd_page_from_vifs(struct rtw_dev *rtwdev)
14792774f206SBjoern A. Zeeb {
14802774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
14812774f206SBjoern A. Zeeb 
14822774f206SBjoern A. Zeeb 	__rtw_build_rsvd_page_reset(rtwdev);
14832774f206SBjoern A. Zeeb 
14842774f206SBjoern A. Zeeb 	/* gather rsvd page from vifs */
14852774f206SBjoern A. Zeeb 	rtw_iterate_vifs_atomic(rtwdev, rtw_build_rsvd_page_iter, rtwdev);
14862774f206SBjoern A. Zeeb 
14872774f206SBjoern A. Zeeb 	rsvd_pkt = list_first_entry_or_null(&rtwdev->rsvd_page_list,
14882774f206SBjoern A. Zeeb 					    struct rtw_rsvd_page, build_list);
14892774f206SBjoern A. Zeeb 	if (!rsvd_pkt) {
14902774f206SBjoern A. Zeeb 		WARN(1, "Should not have an empty reserved page\n");
14912774f206SBjoern A. Zeeb 		return -EINVAL;
14922774f206SBjoern A. Zeeb 	}
14932774f206SBjoern A. Zeeb 
14942774f206SBjoern A. Zeeb 	/* the first rsvd should be beacon, otherwise add a dummy one */
14952774f206SBjoern A. Zeeb 	if (rsvd_pkt->type != RSVD_BEACON) {
14962774f206SBjoern A. Zeeb 		struct rtw_rsvd_page *dummy_pkt;
14972774f206SBjoern A. Zeeb 
14982774f206SBjoern A. Zeeb 		dummy_pkt = rtw_alloc_rsvd_page(rtwdev, RSVD_DUMMY, false);
14992774f206SBjoern A. Zeeb 		if (!dummy_pkt) {
15002774f206SBjoern A. Zeeb 			rtw_err(rtwdev, "failed to alloc dummy rsvd page\n");
15012774f206SBjoern A. Zeeb 			return -ENOMEM;
15022774f206SBjoern A. Zeeb 		}
15032774f206SBjoern A. Zeeb 
15042774f206SBjoern A. Zeeb 		list_add(&dummy_pkt->build_list, &rtwdev->rsvd_page_list);
15052774f206SBjoern A. Zeeb 	}
15062774f206SBjoern A. Zeeb 
15072774f206SBjoern A. Zeeb 	return 0;
15082774f206SBjoern A. Zeeb }
15092774f206SBjoern A. Zeeb 
15102774f206SBjoern A. Zeeb static u8 *rtw_build_rsvd_page(struct rtw_dev *rtwdev, u32 *size)
15112774f206SBjoern A. Zeeb {
15122774f206SBjoern A. Zeeb 	struct ieee80211_hw *hw = rtwdev->hw;
1513*90aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
15142774f206SBjoern A. Zeeb 	struct sk_buff *iter;
15152774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
15162774f206SBjoern A. Zeeb 	u32 page = 0;
15172774f206SBjoern A. Zeeb 	u8 total_page = 0;
15182774f206SBjoern A. Zeeb 	u8 page_size, page_margin, tx_desc_sz;
15192774f206SBjoern A. Zeeb 	u8 *buf;
15202774f206SBjoern A. Zeeb 	int ret;
15212774f206SBjoern A. Zeeb 
15222774f206SBjoern A. Zeeb 	page_size = chip->page_size;
15232774f206SBjoern A. Zeeb 	tx_desc_sz = chip->tx_pkt_desc_sz;
15242774f206SBjoern A. Zeeb 	page_margin = page_size - tx_desc_sz;
15252774f206SBjoern A. Zeeb 
15262774f206SBjoern A. Zeeb 	ret = __rtw_build_rsvd_page_from_vifs(rtwdev);
15272774f206SBjoern A. Zeeb 	if (ret) {
15282774f206SBjoern A. Zeeb 		rtw_err(rtwdev,
15292774f206SBjoern A. Zeeb 			"failed to build rsvd page from vifs, ret %d\n", ret);
15302774f206SBjoern A. Zeeb 		return NULL;
15312774f206SBjoern A. Zeeb 	}
15322774f206SBjoern A. Zeeb 
15332774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) {
15342774f206SBjoern A. Zeeb 		iter = rtw_get_rsvd_page_skb(hw, rsvd_pkt);
15352774f206SBjoern A. Zeeb 		if (!iter) {
15362774f206SBjoern A. Zeeb 			rtw_err(rtwdev, "failed to build rsvd packet\n");
15372774f206SBjoern A. Zeeb 			goto release_skb;
15382774f206SBjoern A. Zeeb 		}
15392774f206SBjoern A. Zeeb 
15402774f206SBjoern A. Zeeb 		/* Fill the tx_desc for the rsvd pkt that requires one.
15412774f206SBjoern A. Zeeb 		 * And iter->len will be added with size of tx_desc_sz.
15422774f206SBjoern A. Zeeb 		 */
15432774f206SBjoern A. Zeeb 		if (rsvd_pkt->add_txdesc)
15442774f206SBjoern A. Zeeb 			rtw_fill_rsvd_page_desc(rtwdev, iter, rsvd_pkt->type);
15452774f206SBjoern A. Zeeb 
15462774f206SBjoern A. Zeeb 		rsvd_pkt->skb = iter;
15472774f206SBjoern A. Zeeb 		rsvd_pkt->page = total_page;
15482774f206SBjoern A. Zeeb 
15492774f206SBjoern A. Zeeb 		/* Reserved page is downloaded via TX path, and TX path will
15502774f206SBjoern A. Zeeb 		 * generate a tx_desc at the header to describe length of
15512774f206SBjoern A. Zeeb 		 * the buffer. If we are not counting page numbers with the
15522774f206SBjoern A. Zeeb 		 * size of tx_desc added at the first rsvd_pkt (usually a
15532774f206SBjoern A. Zeeb 		 * beacon, firmware default refer to the first page as the
15542774f206SBjoern A. Zeeb 		 * content of beacon), we could generate a buffer which size
15552774f206SBjoern A. Zeeb 		 * is smaller than the actual size of the whole rsvd_page
15562774f206SBjoern A. Zeeb 		 */
15572774f206SBjoern A. Zeeb 		if (total_page == 0) {
15582774f206SBjoern A. Zeeb 			if (rsvd_pkt->type != RSVD_BEACON &&
15592774f206SBjoern A. Zeeb 			    rsvd_pkt->type != RSVD_DUMMY) {
15602774f206SBjoern A. Zeeb 				rtw_err(rtwdev, "first page should be a beacon\n");
15612774f206SBjoern A. Zeeb 				goto release_skb;
15622774f206SBjoern A. Zeeb 			}
15632774f206SBjoern A. Zeeb 			total_page += rtw_len_to_page(iter->len + tx_desc_sz,
15642774f206SBjoern A. Zeeb 						      page_size);
15652774f206SBjoern A. Zeeb 		} else {
15662774f206SBjoern A. Zeeb 			total_page += rtw_len_to_page(iter->len, page_size);
15672774f206SBjoern A. Zeeb 		}
15682774f206SBjoern A. Zeeb 	}
15692774f206SBjoern A. Zeeb 
15702774f206SBjoern A. Zeeb 	if (total_page > rtwdev->fifo.rsvd_drv_pg_num) {
15712774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "rsvd page over size: %d\n", total_page);
15722774f206SBjoern A. Zeeb 		goto release_skb;
15732774f206SBjoern A. Zeeb 	}
15742774f206SBjoern A. Zeeb 
15752774f206SBjoern A. Zeeb 	*size = (total_page - 1) * page_size + page_margin;
15762774f206SBjoern A. Zeeb 	buf = kzalloc(*size, GFP_KERNEL);
15772774f206SBjoern A. Zeeb 	if (!buf)
15782774f206SBjoern A. Zeeb 		goto release_skb;
15792774f206SBjoern A. Zeeb 
15802774f206SBjoern A. Zeeb 	/* Copy the content of each rsvd_pkt to the buf, and they should
15812774f206SBjoern A. Zeeb 	 * be aligned to the pages.
15822774f206SBjoern A. Zeeb 	 *
15832774f206SBjoern A. Zeeb 	 * Note that the first rsvd_pkt is a beacon no matter what vif->type.
15842774f206SBjoern A. Zeeb 	 * And that rsvd_pkt does not require tx_desc because when it goes
15852774f206SBjoern A. Zeeb 	 * through TX path, the TX path will generate one for it.
15862774f206SBjoern A. Zeeb 	 */
15872774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) {
15882774f206SBjoern A. Zeeb 		rtw_rsvd_page_list_to_buf(rtwdev, page_size, page_margin,
15892774f206SBjoern A. Zeeb 					  page, buf, rsvd_pkt);
15902774f206SBjoern A. Zeeb 		if (page == 0)
15912774f206SBjoern A. Zeeb 			page += rtw_len_to_page(rsvd_pkt->skb->len +
15922774f206SBjoern A. Zeeb 						tx_desc_sz, page_size);
15932774f206SBjoern A. Zeeb 		else
15942774f206SBjoern A. Zeeb 			page += rtw_len_to_page(rsvd_pkt->skb->len, page_size);
15952774f206SBjoern A. Zeeb 
15962774f206SBjoern A. Zeeb 		kfree_skb(rsvd_pkt->skb);
15972774f206SBjoern A. Zeeb 		rsvd_pkt->skb = NULL;
15982774f206SBjoern A. Zeeb 	}
15992774f206SBjoern A. Zeeb 
16002774f206SBjoern A. Zeeb 	return buf;
16012774f206SBjoern A. Zeeb 
16022774f206SBjoern A. Zeeb release_skb:
16032774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) {
16042774f206SBjoern A. Zeeb 		kfree_skb(rsvd_pkt->skb);
16052774f206SBjoern A. Zeeb 		rsvd_pkt->skb = NULL;
16062774f206SBjoern A. Zeeb 	}
16072774f206SBjoern A. Zeeb 
16082774f206SBjoern A. Zeeb 	return NULL;
16092774f206SBjoern A. Zeeb }
16102774f206SBjoern A. Zeeb 
16112774f206SBjoern A. Zeeb static int rtw_download_beacon(struct rtw_dev *rtwdev)
16122774f206SBjoern A. Zeeb {
16132774f206SBjoern A. Zeeb 	struct ieee80211_hw *hw = rtwdev->hw;
16142774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
16152774f206SBjoern A. Zeeb 	struct sk_buff *skb;
16162774f206SBjoern A. Zeeb 	int ret = 0;
16172774f206SBjoern A. Zeeb 
16182774f206SBjoern A. Zeeb 	rsvd_pkt = list_first_entry_or_null(&rtwdev->rsvd_page_list,
16192774f206SBjoern A. Zeeb 					    struct rtw_rsvd_page, build_list);
16202774f206SBjoern A. Zeeb 	if (!rsvd_pkt) {
16212774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to get rsvd page from build list\n");
16222774f206SBjoern A. Zeeb 		return -ENOENT;
16232774f206SBjoern A. Zeeb 	}
16242774f206SBjoern A. Zeeb 
16252774f206SBjoern A. Zeeb 	if (rsvd_pkt->type != RSVD_BEACON &&
16262774f206SBjoern A. Zeeb 	    rsvd_pkt->type != RSVD_DUMMY) {
16272774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "invalid rsvd page type %d, should be beacon or dummy\n",
16282774f206SBjoern A. Zeeb 			rsvd_pkt->type);
16292774f206SBjoern A. Zeeb 		return -EINVAL;
16302774f206SBjoern A. Zeeb 	}
16312774f206SBjoern A. Zeeb 
16322774f206SBjoern A. Zeeb 	skb = rtw_get_rsvd_page_skb(hw, rsvd_pkt);
16332774f206SBjoern A. Zeeb 	if (!skb) {
16342774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to get beacon skb\n");
16352774f206SBjoern A. Zeeb 		return -ENOMEM;
16362774f206SBjoern A. Zeeb 	}
16372774f206SBjoern A. Zeeb 
16382774f206SBjoern A. Zeeb 	ret = rtw_download_drv_rsvd_page(rtwdev, skb->data, skb->len);
16392774f206SBjoern A. Zeeb 	if (ret)
16402774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to download drv rsvd page\n");
16412774f206SBjoern A. Zeeb 
16422774f206SBjoern A. Zeeb 	dev_kfree_skb(skb);
16432774f206SBjoern A. Zeeb 
16442774f206SBjoern A. Zeeb 	return ret;
16452774f206SBjoern A. Zeeb }
16462774f206SBjoern A. Zeeb 
16472774f206SBjoern A. Zeeb int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev)
16482774f206SBjoern A. Zeeb {
16492774f206SBjoern A. Zeeb 	u8 *buf;
16502774f206SBjoern A. Zeeb 	u32 size;
16512774f206SBjoern A. Zeeb 	int ret;
16522774f206SBjoern A. Zeeb 
16532774f206SBjoern A. Zeeb 	buf = rtw_build_rsvd_page(rtwdev, &size);
16542774f206SBjoern A. Zeeb 	if (!buf) {
16552774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to build rsvd page pkt\n");
16562774f206SBjoern A. Zeeb 		return -ENOMEM;
16572774f206SBjoern A. Zeeb 	}
16582774f206SBjoern A. Zeeb 
16592774f206SBjoern A. Zeeb 	ret = rtw_download_drv_rsvd_page(rtwdev, buf, size);
16602774f206SBjoern A. Zeeb 	if (ret) {
16612774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to download drv rsvd page\n");
16622774f206SBjoern A. Zeeb 		goto free;
16632774f206SBjoern A. Zeeb 	}
16642774f206SBjoern A. Zeeb 
16652774f206SBjoern A. Zeeb 	/* The last thing is to download the *ONLY* beacon again, because
16662774f206SBjoern A. Zeeb 	 * the previous tx_desc is to describe the total rsvd page. Download
16672774f206SBjoern A. Zeeb 	 * the beacon again to replace the TX desc header, and we will get
16682774f206SBjoern A. Zeeb 	 * a correct tx_desc for the beacon in the rsvd page.
16692774f206SBjoern A. Zeeb 	 */
16702774f206SBjoern A. Zeeb 	ret = rtw_download_beacon(rtwdev);
16712774f206SBjoern A. Zeeb 	if (ret) {
16722774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to download beacon\n");
16732774f206SBjoern A. Zeeb 		goto free;
16742774f206SBjoern A. Zeeb 	}
16752774f206SBjoern A. Zeeb 
16762774f206SBjoern A. Zeeb free:
16772774f206SBjoern A. Zeeb 	kfree(buf);
16782774f206SBjoern A. Zeeb 
16792774f206SBjoern A. Zeeb 	return ret;
16802774f206SBjoern A. Zeeb }
16812774f206SBjoern A. Zeeb 
16829c951734SBjoern A. Zeeb void rtw_fw_update_beacon_work(struct work_struct *work)
16839c951734SBjoern A. Zeeb {
16849c951734SBjoern A. Zeeb 	struct rtw_dev *rtwdev = container_of(work, struct rtw_dev,
16859c951734SBjoern A. Zeeb 					      update_beacon_work);
16869c951734SBjoern A. Zeeb 
16879c951734SBjoern A. Zeeb 	mutex_lock(&rtwdev->mutex);
16889c951734SBjoern A. Zeeb 	rtw_fw_download_rsvd_page(rtwdev);
1689*90aac0d8SBjoern A. Zeeb 	rtw_send_rsvd_page_h2c(rtwdev);
16909c951734SBjoern A. Zeeb 	mutex_unlock(&rtwdev->mutex);
16919c951734SBjoern A. Zeeb }
16929c951734SBjoern A. Zeeb 
16932774f206SBjoern A. Zeeb static void rtw_fw_read_fifo_page(struct rtw_dev *rtwdev, u32 offset, u32 size,
16942774f206SBjoern A. Zeeb 				  u32 *buf, u32 residue, u16 start_pg)
16952774f206SBjoern A. Zeeb {
16962774f206SBjoern A. Zeeb 	u32 i;
16972774f206SBjoern A. Zeeb 	u16 idx = 0;
16982774f206SBjoern A. Zeeb 	u16 ctl;
16992774f206SBjoern A. Zeeb 
17002774f206SBjoern A. Zeeb 	ctl = rtw_read16(rtwdev, REG_PKTBUF_DBG_CTRL) & 0xf000;
17012774f206SBjoern A. Zeeb 	/* disable rx clock gate */
17022774f206SBjoern A. Zeeb 	rtw_write32_set(rtwdev, REG_RCR, BIT_DISGCLK);
17032774f206SBjoern A. Zeeb 
17042774f206SBjoern A. Zeeb 	do {
17052774f206SBjoern A. Zeeb 		rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, start_pg | ctl);
17062774f206SBjoern A. Zeeb 
17072774f206SBjoern A. Zeeb 		for (i = FIFO_DUMP_ADDR + residue;
17082774f206SBjoern A. Zeeb 		     i < FIFO_DUMP_ADDR + FIFO_PAGE_SIZE; i += 4) {
17092774f206SBjoern A. Zeeb 			buf[idx++] = rtw_read32(rtwdev, i);
17102774f206SBjoern A. Zeeb 			size -= 4;
17112774f206SBjoern A. Zeeb 			if (size == 0)
17122774f206SBjoern A. Zeeb 				goto out;
17132774f206SBjoern A. Zeeb 		}
17142774f206SBjoern A. Zeeb 
17152774f206SBjoern A. Zeeb 		residue = 0;
17162774f206SBjoern A. Zeeb 		start_pg++;
17172774f206SBjoern A. Zeeb 	} while (size);
17182774f206SBjoern A. Zeeb 
17192774f206SBjoern A. Zeeb out:
17202774f206SBjoern A. Zeeb 	rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, ctl);
17212774f206SBjoern A. Zeeb 	/* restore rx clock gate */
17222774f206SBjoern A. Zeeb 	rtw_write32_clr(rtwdev, REG_RCR, BIT_DISGCLK);
17232774f206SBjoern A. Zeeb }
17242774f206SBjoern A. Zeeb 
17252774f206SBjoern A. Zeeb static void rtw_fw_read_fifo(struct rtw_dev *rtwdev, enum rtw_fw_fifo_sel sel,
17262774f206SBjoern A. Zeeb 			     u32 offset, u32 size, u32 *buf)
17272774f206SBjoern A. Zeeb {
1728*90aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
17292774f206SBjoern A. Zeeb 	u32 start_pg, residue;
17302774f206SBjoern A. Zeeb 
17312774f206SBjoern A. Zeeb 	if (sel >= RTW_FW_FIFO_MAX) {
17322774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_FW, "wrong fw fifo sel\n");
17332774f206SBjoern A. Zeeb 		return;
17342774f206SBjoern A. Zeeb 	}
17352774f206SBjoern A. Zeeb 	if (sel == RTW_FW_FIFO_SEL_RSVD_PAGE)
17362774f206SBjoern A. Zeeb 		offset += rtwdev->fifo.rsvd_boundary << TX_PAGE_SIZE_SHIFT;
17372774f206SBjoern A. Zeeb 	residue = offset & (FIFO_PAGE_SIZE - 1);
17382774f206SBjoern A. Zeeb 	start_pg = (offset >> FIFO_PAGE_SIZE_SHIFT) + chip->fw_fifo_addr[sel];
17392774f206SBjoern A. Zeeb 
17402774f206SBjoern A. Zeeb 	rtw_fw_read_fifo_page(rtwdev, offset, size, buf, residue, start_pg);
17412774f206SBjoern A. Zeeb }
17422774f206SBjoern A. Zeeb 
17432774f206SBjoern A. Zeeb static bool rtw_fw_dump_check_size(struct rtw_dev *rtwdev,
17442774f206SBjoern A. Zeeb 				   enum rtw_fw_fifo_sel sel,
17452774f206SBjoern A. Zeeb 				   u32 start_addr, u32 size)
17462774f206SBjoern A. Zeeb {
17472774f206SBjoern A. Zeeb 	switch (sel) {
17482774f206SBjoern A. Zeeb 	case RTW_FW_FIFO_SEL_TX:
17492774f206SBjoern A. Zeeb 	case RTW_FW_FIFO_SEL_RX:
17502774f206SBjoern A. Zeeb 		if ((start_addr + size) > rtwdev->chip->fw_fifo_addr[sel])
17512774f206SBjoern A. Zeeb 			return false;
17522774f206SBjoern A. Zeeb 		fallthrough;
17532774f206SBjoern A. Zeeb 	default:
17542774f206SBjoern A. Zeeb 		return true;
17552774f206SBjoern A. Zeeb 	}
17562774f206SBjoern A. Zeeb }
17572774f206SBjoern A. Zeeb 
17582774f206SBjoern A. Zeeb int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size,
17592774f206SBjoern A. Zeeb 		     u32 *buffer)
17602774f206SBjoern A. Zeeb {
17612774f206SBjoern A. Zeeb 	if (!rtwdev->chip->fw_fifo_addr[0]) {
17622774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_FW, "chip not support dump fw fifo\n");
17632774f206SBjoern A. Zeeb 		return -ENOTSUPP;
17642774f206SBjoern A. Zeeb 	}
17652774f206SBjoern A. Zeeb 
17662774f206SBjoern A. Zeeb 	if (size == 0 || !buffer)
17672774f206SBjoern A. Zeeb 		return -EINVAL;
17682774f206SBjoern A. Zeeb 
17692774f206SBjoern A. Zeeb 	if (size & 0x3) {
17702774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_FW, "not 4byte alignment\n");
17712774f206SBjoern A. Zeeb 		return -EINVAL;
17722774f206SBjoern A. Zeeb 	}
17732774f206SBjoern A. Zeeb 
17742774f206SBjoern A. Zeeb 	if (!rtw_fw_dump_check_size(rtwdev, fifo_sel, addr, size)) {
17752774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_FW, "fw fifo dump size overflow\n");
17762774f206SBjoern A. Zeeb 		return -EINVAL;
17772774f206SBjoern A. Zeeb 	}
17782774f206SBjoern A. Zeeb 
17792774f206SBjoern A. Zeeb 	rtw_fw_read_fifo(rtwdev, fifo_sel, addr, size, buffer);
17802774f206SBjoern A. Zeeb 
17812774f206SBjoern A. Zeeb 	return 0;
17822774f206SBjoern A. Zeeb }
17832774f206SBjoern A. Zeeb 
17842774f206SBjoern A. Zeeb static void __rtw_fw_update_pkt(struct rtw_dev *rtwdev, u8 pkt_id, u16 size,
17852774f206SBjoern A. Zeeb 				u8 location)
17862774f206SBjoern A. Zeeb {
1787*90aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
17882774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
17892774f206SBjoern A. Zeeb 	u16 total_size = H2C_PKT_HDR_SIZE + H2C_PKT_UPDATE_PKT_LEN;
17902774f206SBjoern A. Zeeb 
17912774f206SBjoern A. Zeeb 	rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_UPDATE_PKT);
17922774f206SBjoern A. Zeeb 
17932774f206SBjoern A. Zeeb 	SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size);
17942774f206SBjoern A. Zeeb 	UPDATE_PKT_SET_PKT_ID(h2c_pkt, pkt_id);
17952774f206SBjoern A. Zeeb 	UPDATE_PKT_SET_LOCATION(h2c_pkt, location);
17962774f206SBjoern A. Zeeb 
17972774f206SBjoern A. Zeeb 	/* include txdesc size */
17982774f206SBjoern A. Zeeb 	size += chip->tx_pkt_desc_sz;
17992774f206SBjoern A. Zeeb 	UPDATE_PKT_SET_SIZE(h2c_pkt, size);
18002774f206SBjoern A. Zeeb 
18012774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
18022774f206SBjoern A. Zeeb }
18032774f206SBjoern A. Zeeb 
18042774f206SBjoern A. Zeeb void rtw_fw_update_pkt_probe_req(struct rtw_dev *rtwdev,
18052774f206SBjoern A. Zeeb 				 struct cfg80211_ssid *ssid)
18062774f206SBjoern A. Zeeb {
18072774f206SBjoern A. Zeeb 	u8 loc;
18082774f206SBjoern A. Zeeb 	u16 size;
18092774f206SBjoern A. Zeeb 
18102774f206SBjoern A. Zeeb 	loc = rtw_get_rsvd_page_probe_req_location(rtwdev, ssid);
18112774f206SBjoern A. Zeeb 	if (!loc) {
18122774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to get probe_req rsvd loc\n");
18132774f206SBjoern A. Zeeb 		return;
18142774f206SBjoern A. Zeeb 	}
18152774f206SBjoern A. Zeeb 
18162774f206SBjoern A. Zeeb 	size = rtw_get_rsvd_page_probe_req_size(rtwdev, ssid);
18172774f206SBjoern A. Zeeb 	if (!size) {
18182774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to get probe_req rsvd size\n");
18192774f206SBjoern A. Zeeb 		return;
18202774f206SBjoern A. Zeeb 	}
18212774f206SBjoern A. Zeeb 
18222774f206SBjoern A. Zeeb 	__rtw_fw_update_pkt(rtwdev, RTW_PACKET_PROBE_REQ, size, loc);
18232774f206SBjoern A. Zeeb }
18242774f206SBjoern A. Zeeb 
18252774f206SBjoern A. Zeeb void rtw_fw_channel_switch(struct rtw_dev *rtwdev, bool enable)
18262774f206SBjoern A. Zeeb {
18272774f206SBjoern A. Zeeb 	struct rtw_pno_request *rtw_pno_req = &rtwdev->wow.pno_req;
18282774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
18292774f206SBjoern A. Zeeb 	u16 total_size = H2C_PKT_HDR_SIZE + H2C_PKT_CH_SWITCH_LEN;
18302774f206SBjoern A. Zeeb 	u8 loc_ch_info;
18312774f206SBjoern A. Zeeb 	const struct rtw_ch_switch_option cs_option = {
18322774f206SBjoern A. Zeeb 		.dest_ch_en = 1,
18332774f206SBjoern A. Zeeb 		.dest_ch = 1,
18342774f206SBjoern A. Zeeb 		.periodic_option = 2,
18352774f206SBjoern A. Zeeb 		.normal_period = 5,
18362774f206SBjoern A. Zeeb 		.normal_period_sel = 0,
18372774f206SBjoern A. Zeeb 		.normal_cycle = 10,
18382774f206SBjoern A. Zeeb 		.slow_period = 1,
18392774f206SBjoern A. Zeeb 		.slow_period_sel = 1,
18402774f206SBjoern A. Zeeb 	};
18412774f206SBjoern A. Zeeb 
18422774f206SBjoern A. Zeeb 	rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_CH_SWITCH);
18432774f206SBjoern A. Zeeb 	SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size);
18442774f206SBjoern A. Zeeb 
18452774f206SBjoern A. Zeeb 	CH_SWITCH_SET_START(h2c_pkt, enable);
18462774f206SBjoern A. Zeeb 	CH_SWITCH_SET_DEST_CH_EN(h2c_pkt, cs_option.dest_ch_en);
18472774f206SBjoern A. Zeeb 	CH_SWITCH_SET_DEST_CH(h2c_pkt, cs_option.dest_ch);
18482774f206SBjoern A. Zeeb 	CH_SWITCH_SET_NORMAL_PERIOD(h2c_pkt, cs_option.normal_period);
18492774f206SBjoern A. Zeeb 	CH_SWITCH_SET_NORMAL_PERIOD_SEL(h2c_pkt, cs_option.normal_period_sel);
18502774f206SBjoern A. Zeeb 	CH_SWITCH_SET_SLOW_PERIOD(h2c_pkt, cs_option.slow_period);
18512774f206SBjoern A. Zeeb 	CH_SWITCH_SET_SLOW_PERIOD_SEL(h2c_pkt, cs_option.slow_period_sel);
18522774f206SBjoern A. Zeeb 	CH_SWITCH_SET_NORMAL_CYCLE(h2c_pkt, cs_option.normal_cycle);
18532774f206SBjoern A. Zeeb 	CH_SWITCH_SET_PERIODIC_OPT(h2c_pkt, cs_option.periodic_option);
18542774f206SBjoern A. Zeeb 
18552774f206SBjoern A. Zeeb 	CH_SWITCH_SET_CH_NUM(h2c_pkt, rtw_pno_req->channel_cnt);
18562774f206SBjoern A. Zeeb 	CH_SWITCH_SET_INFO_SIZE(h2c_pkt, rtw_pno_req->channel_cnt * 4);
18572774f206SBjoern A. Zeeb 
18582774f206SBjoern A. Zeeb 	loc_ch_info = rtw_get_rsvd_page_location(rtwdev, RSVD_CH_INFO);
18592774f206SBjoern A. Zeeb 	CH_SWITCH_SET_INFO_LOC(h2c_pkt, loc_ch_info);
18602774f206SBjoern A. Zeeb 
18612774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
18622774f206SBjoern A. Zeeb }
18632774f206SBjoern A. Zeeb 
18642774f206SBjoern A. Zeeb void rtw_fw_adaptivity(struct rtw_dev *rtwdev)
18652774f206SBjoern A. Zeeb {
18662774f206SBjoern A. Zeeb 	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
18672774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
18682774f206SBjoern A. Zeeb 
18692774f206SBjoern A. Zeeb 	if (!rtw_edcca_enabled) {
18702774f206SBjoern A. Zeeb 		dm_info->edcca_mode = RTW_EDCCA_NORMAL;
18712774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY,
18722774f206SBjoern A. Zeeb 			"EDCCA disabled by debugfs\n");
18732774f206SBjoern A. Zeeb 	}
18742774f206SBjoern A. Zeeb 
18752774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_ADAPTIVITY);
18762774f206SBjoern A. Zeeb 	SET_ADAPTIVITY_MODE(h2c_pkt, dm_info->edcca_mode);
18779c951734SBjoern A. Zeeb 	SET_ADAPTIVITY_OPTION(h2c_pkt, 1);
18782774f206SBjoern A. Zeeb 	SET_ADAPTIVITY_IGI(h2c_pkt, dm_info->igi_history[0]);
18792774f206SBjoern A. Zeeb 	SET_ADAPTIVITY_L2H(h2c_pkt, dm_info->l2h_th_ini);
18802774f206SBjoern A. Zeeb 	SET_ADAPTIVITY_DENSITY(h2c_pkt, dm_info->scan_density);
18812774f206SBjoern A. Zeeb 
18822774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
18832774f206SBjoern A. Zeeb }
18842774f206SBjoern A. Zeeb 
18852774f206SBjoern A. Zeeb void rtw_fw_scan_notify(struct rtw_dev *rtwdev, bool start)
18862774f206SBjoern A. Zeeb {
18872774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
18882774f206SBjoern A. Zeeb 
18892774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_SCAN);
18902774f206SBjoern A. Zeeb 	SET_SCAN_START(h2c_pkt, start);
18912774f206SBjoern A. Zeeb 
18922774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
18932774f206SBjoern A. Zeeb }
18942774f206SBjoern A. Zeeb 
18959c951734SBjoern A. Zeeb static int rtw_append_probe_req_ie(struct rtw_dev *rtwdev, struct sk_buff *skb,
18969c951734SBjoern A. Zeeb 				   struct sk_buff_head *list, u8 *bands,
18972774f206SBjoern A. Zeeb 				   struct rtw_vif *rtwvif)
18982774f206SBjoern A. Zeeb {
1899*90aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
19002774f206SBjoern A. Zeeb 	struct ieee80211_scan_ies *ies = rtwvif->scan_ies;
19012774f206SBjoern A. Zeeb 	struct sk_buff *new;
19022774f206SBjoern A. Zeeb 	u8 idx;
19032774f206SBjoern A. Zeeb 
19042774f206SBjoern A. Zeeb 	for (idx = NL80211_BAND_2GHZ; idx < NUM_NL80211_BANDS; idx++) {
19052774f206SBjoern A. Zeeb 		if (!(BIT(idx) & chip->band))
19062774f206SBjoern A. Zeeb 			continue;
19072774f206SBjoern A. Zeeb 		new = skb_copy(skb, GFP_KERNEL);
19089c951734SBjoern A. Zeeb 		if (!new)
19099c951734SBjoern A. Zeeb 			return -ENOMEM;
19102774f206SBjoern A. Zeeb 		skb_put_data(new, ies->ies[idx], ies->len[idx]);
19112774f206SBjoern A. Zeeb 		skb_put_data(new, ies->common_ies, ies->common_ie_len);
19122774f206SBjoern A. Zeeb 		skb_queue_tail(list, new);
19139c951734SBjoern A. Zeeb 		(*bands)++;
19142774f206SBjoern A. Zeeb 	}
19152774f206SBjoern A. Zeeb 
19169c951734SBjoern A. Zeeb 	return 0;
19179c951734SBjoern A. Zeeb }
19189c951734SBjoern A. Zeeb 
19199c951734SBjoern A. Zeeb static int _rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev, u8 num_probes,
19202774f206SBjoern A. Zeeb 					 struct sk_buff_head *probe_req_list)
19212774f206SBjoern A. Zeeb {
1922*90aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
19232774f206SBjoern A. Zeeb 	struct sk_buff *skb, *tmp;
19242774f206SBjoern A. Zeeb 	u8 page_offset = 1, *buf, page_size = chip->page_size;
19252774f206SBjoern A. Zeeb 	u16 pg_addr = rtwdev->fifo.rsvd_h2c_info_addr, loc;
19262774f206SBjoern A. Zeeb 	u16 buf_offset = page_size * page_offset;
19272774f206SBjoern A. Zeeb 	u8 tx_desc_sz = chip->tx_pkt_desc_sz;
1928*90aac0d8SBjoern A. Zeeb 	u8 page_cnt, pages;
19292774f206SBjoern A. Zeeb 	unsigned int pkt_len;
19302774f206SBjoern A. Zeeb 	int ret;
19312774f206SBjoern A. Zeeb 
1932*90aac0d8SBjoern A. Zeeb 	if (rtw_fw_feature_ext_check(&rtwdev->fw, FW_FEATURE_EXT_OLD_PAGE_NUM))
1933*90aac0d8SBjoern A. Zeeb 		page_cnt = RTW_OLD_PROBE_PG_CNT;
1934*90aac0d8SBjoern A. Zeeb 	else
1935*90aac0d8SBjoern A. Zeeb 		page_cnt = RTW_PROBE_PG_CNT;
1936*90aac0d8SBjoern A. Zeeb 
1937*90aac0d8SBjoern A. Zeeb 	pages = page_offset + num_probes * page_cnt;
1938*90aac0d8SBjoern A. Zeeb 
19392774f206SBjoern A. Zeeb 	buf = kzalloc(page_size * pages, GFP_KERNEL);
19402774f206SBjoern A. Zeeb 	if (!buf)
19412774f206SBjoern A. Zeeb 		return -ENOMEM;
19422774f206SBjoern A. Zeeb 
19432774f206SBjoern A. Zeeb 	buf_offset -= tx_desc_sz;
19442774f206SBjoern A. Zeeb 	skb_queue_walk_safe(probe_req_list, skb, tmp) {
19452774f206SBjoern A. Zeeb 		skb_unlink(skb, probe_req_list);
19462774f206SBjoern A. Zeeb 		rtw_fill_rsvd_page_desc(rtwdev, skb, RSVD_PROBE_REQ);
1947*90aac0d8SBjoern A. Zeeb 		if (skb->len > page_size * page_cnt) {
1948728afa88SBjoern A. Zeeb #if defined(__FreeBSD__)
1949728afa88SBjoern A. Zeeb 			kfree_skb(skb);
1950728afa88SBjoern A. Zeeb #endif
19512774f206SBjoern A. Zeeb 			ret = -EINVAL;
19522774f206SBjoern A. Zeeb 			goto out;
19532774f206SBjoern A. Zeeb 		}
19542774f206SBjoern A. Zeeb 
19552774f206SBjoern A. Zeeb 		memcpy(buf + buf_offset, skb->data, skb->len);
19562774f206SBjoern A. Zeeb 		pkt_len = skb->len - tx_desc_sz;
19572774f206SBjoern A. Zeeb 		loc = pg_addr - rtwdev->fifo.rsvd_boundary + page_offset;
19582774f206SBjoern A. Zeeb 		__rtw_fw_update_pkt(rtwdev, RTW_PACKET_PROBE_REQ, pkt_len, loc);
19592774f206SBjoern A. Zeeb 
1960*90aac0d8SBjoern A. Zeeb 		buf_offset += page_cnt * page_size;
1961*90aac0d8SBjoern A. Zeeb 		page_offset += page_cnt;
19622774f206SBjoern A. Zeeb 		kfree_skb(skb);
19632774f206SBjoern A. Zeeb 	}
19642774f206SBjoern A. Zeeb 
19652774f206SBjoern A. Zeeb 	ret = rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, buf_offset);
19662774f206SBjoern A. Zeeb 	if (ret) {
19672774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "Download probe request to firmware failed\n");
19682774f206SBjoern A. Zeeb 		goto out;
19692774f206SBjoern A. Zeeb 	}
19702774f206SBjoern A. Zeeb 
19712774f206SBjoern A. Zeeb 	rtwdev->scan_info.probe_pg_size = page_offset;
19722774f206SBjoern A. Zeeb out:
19732774f206SBjoern A. Zeeb 	kfree(buf);
19749c951734SBjoern A. Zeeb 	skb_queue_walk_safe(probe_req_list, skb, tmp)
19759c951734SBjoern A. Zeeb 		kfree_skb(skb);
19762774f206SBjoern A. Zeeb 
19772774f206SBjoern A. Zeeb 	return ret;
19782774f206SBjoern A. Zeeb }
19792774f206SBjoern A. Zeeb 
19802774f206SBjoern A. Zeeb static int rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev,
19812774f206SBjoern A. Zeeb 					struct rtw_vif *rtwvif)
19822774f206SBjoern A. Zeeb {
19832774f206SBjoern A. Zeeb 	struct cfg80211_scan_request *req = rtwvif->scan_req;
19842774f206SBjoern A. Zeeb 	struct sk_buff_head list;
19859c951734SBjoern A. Zeeb 	struct sk_buff *skb, *tmp;
19869c951734SBjoern A. Zeeb 	u8 num = req->n_ssids, i, bands = 0;
19879c951734SBjoern A. Zeeb 	int ret;
19882774f206SBjoern A. Zeeb 
19892774f206SBjoern A. Zeeb 	skb_queue_head_init(&list);
19902774f206SBjoern A. Zeeb 	for (i = 0; i < num; i++) {
19912774f206SBjoern A. Zeeb 		skb = ieee80211_probereq_get(rtwdev->hw, rtwvif->mac_addr,
19922774f206SBjoern A. Zeeb 					     req->ssids[i].ssid,
19932774f206SBjoern A. Zeeb 					     req->ssids[i].ssid_len,
19942774f206SBjoern A. Zeeb 					     req->ie_len);
19959c951734SBjoern A. Zeeb 		if (!skb) {
19969c951734SBjoern A. Zeeb 			ret = -ENOMEM;
19979c951734SBjoern A. Zeeb 			goto out;
19989c951734SBjoern A. Zeeb 		}
19999c951734SBjoern A. Zeeb 		ret = rtw_append_probe_req_ie(rtwdev, skb, &list, &bands,
20009c951734SBjoern A. Zeeb 					      rtwvif);
20019c951734SBjoern A. Zeeb 		if (ret)
20029c951734SBjoern A. Zeeb 			goto out;
20039c951734SBjoern A. Zeeb 
20042774f206SBjoern A. Zeeb 		kfree_skb(skb);
20052774f206SBjoern A. Zeeb 	}
20062774f206SBjoern A. Zeeb 
20079c951734SBjoern A. Zeeb 	return _rtw_hw_scan_update_probe_req(rtwdev, num * bands, &list);
20089c951734SBjoern A. Zeeb 
20099c951734SBjoern A. Zeeb out:
20109c951734SBjoern A. Zeeb 	skb_queue_walk_safe(&list, skb, tmp)
20119c951734SBjoern A. Zeeb 		kfree_skb(skb);
20129c951734SBjoern A. Zeeb 
20139c951734SBjoern A. Zeeb 	return ret;
20142774f206SBjoern A. Zeeb }
20152774f206SBjoern A. Zeeb 
20162774f206SBjoern A. Zeeb static int rtw_add_chan_info(struct rtw_dev *rtwdev, struct rtw_chan_info *info,
20172774f206SBjoern A. Zeeb 			     struct rtw_chan_list *list, u8 *buf)
20182774f206SBjoern A. Zeeb {
20192774f206SBjoern A. Zeeb 	u8 *chan = &buf[list->size];
20202774f206SBjoern A. Zeeb 	u8 info_size = RTW_CH_INFO_SIZE;
20212774f206SBjoern A. Zeeb 
20222774f206SBjoern A. Zeeb 	if (list->size > list->buf_size)
20232774f206SBjoern A. Zeeb 		return -ENOMEM;
20242774f206SBjoern A. Zeeb 
20252774f206SBjoern A. Zeeb 	CH_INFO_SET_CH(chan, info->channel);
20262774f206SBjoern A. Zeeb 	CH_INFO_SET_PRI_CH_IDX(chan, info->pri_ch_idx);
20272774f206SBjoern A. Zeeb 	CH_INFO_SET_BW(chan, info->bw);
20282774f206SBjoern A. Zeeb 	CH_INFO_SET_TIMEOUT(chan, info->timeout);
20292774f206SBjoern A. Zeeb 	CH_INFO_SET_ACTION_ID(chan, info->action_id);
20302774f206SBjoern A. Zeeb 	CH_INFO_SET_EXTRA_INFO(chan, info->extra_info);
20312774f206SBjoern A. Zeeb 	if (info->extra_info) {
20322774f206SBjoern A. Zeeb 		EXTRA_CH_INFO_SET_ID(chan, RTW_SCAN_EXTRA_ID_DFS);
20332774f206SBjoern A. Zeeb 		EXTRA_CH_INFO_SET_INFO(chan, RTW_SCAN_EXTRA_ACTION_SCAN);
20342774f206SBjoern A. Zeeb 		EXTRA_CH_INFO_SET_SIZE(chan, RTW_EX_CH_INFO_SIZE -
20352774f206SBjoern A. Zeeb 				       RTW_EX_CH_INFO_HDR_SIZE);
20362774f206SBjoern A. Zeeb 		EXTRA_CH_INFO_SET_DFS_EXT_TIME(chan, RTW_DFS_CHAN_TIME);
20372774f206SBjoern A. Zeeb 		info_size += RTW_EX_CH_INFO_SIZE;
20382774f206SBjoern A. Zeeb 	}
20392774f206SBjoern A. Zeeb 	list->size += info_size;
20402774f206SBjoern A. Zeeb 	list->ch_num++;
20412774f206SBjoern A. Zeeb 
20422774f206SBjoern A. Zeeb 	return 0;
20432774f206SBjoern A. Zeeb }
20442774f206SBjoern A. Zeeb 
20452774f206SBjoern A. Zeeb static int rtw_add_chan_list(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif,
20462774f206SBjoern A. Zeeb 			     struct rtw_chan_list *list, u8 *buf)
20472774f206SBjoern A. Zeeb {
20482774f206SBjoern A. Zeeb 	struct cfg80211_scan_request *req = rtwvif->scan_req;
20492774f206SBjoern A. Zeeb 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
20502774f206SBjoern A. Zeeb 	struct ieee80211_channel *channel;
20512774f206SBjoern A. Zeeb 	int i, ret = 0;
20522774f206SBjoern A. Zeeb 
20532774f206SBjoern A. Zeeb 	for (i = 0; i < req->n_channels; i++) {
20542774f206SBjoern A. Zeeb 		struct rtw_chan_info ch_info = {0};
20552774f206SBjoern A. Zeeb 
20562774f206SBjoern A. Zeeb 		channel = req->channels[i];
20572774f206SBjoern A. Zeeb 		ch_info.channel = channel->hw_value;
20582774f206SBjoern A. Zeeb 		ch_info.bw = RTW_SCAN_WIDTH;
20592774f206SBjoern A. Zeeb 		ch_info.pri_ch_idx = RTW_PRI_CH_IDX;
20602774f206SBjoern A. Zeeb 		ch_info.timeout = req->duration_mandatory ?
20612774f206SBjoern A. Zeeb 				  req->duration : RTW_CHANNEL_TIME;
20622774f206SBjoern A. Zeeb 
20632774f206SBjoern A. Zeeb 		if (channel->flags & (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IR)) {
20642774f206SBjoern A. Zeeb 			ch_info.action_id = RTW_CHANNEL_RADAR;
20652774f206SBjoern A. Zeeb 			ch_info.extra_info = 1;
20662774f206SBjoern A. Zeeb 			/* Overwrite duration for passive scans if necessary */
20672774f206SBjoern A. Zeeb 			ch_info.timeout = ch_info.timeout > RTW_PASS_CHAN_TIME ?
20682774f206SBjoern A. Zeeb 					  ch_info.timeout : RTW_PASS_CHAN_TIME;
20692774f206SBjoern A. Zeeb 		} else {
20702774f206SBjoern A. Zeeb 			ch_info.action_id = RTW_CHANNEL_ACTIVE;
20712774f206SBjoern A. Zeeb 		}
20722774f206SBjoern A. Zeeb 
20732774f206SBjoern A. Zeeb 		ret = rtw_add_chan_info(rtwdev, &ch_info, list, buf);
20742774f206SBjoern A. Zeeb 		if (ret)
20752774f206SBjoern A. Zeeb 			return ret;
20762774f206SBjoern A. Zeeb 	}
20772774f206SBjoern A. Zeeb 
20782774f206SBjoern A. Zeeb 	if (list->size > fifo->rsvd_pg_num << TX_PAGE_SIZE_SHIFT) {
20792774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "List exceeds rsvd page total size\n");
20802774f206SBjoern A. Zeeb 		return -EINVAL;
20812774f206SBjoern A. Zeeb 	}
20822774f206SBjoern A. Zeeb 
20832774f206SBjoern A. Zeeb 	list->addr = fifo->rsvd_h2c_info_addr + rtwdev->scan_info.probe_pg_size;
20842774f206SBjoern A. Zeeb 	ret = rtw_fw_write_data_rsvd_page(rtwdev, list->addr, buf, list->size);
20852774f206SBjoern A. Zeeb 	if (ret)
20862774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "Download channel list failed\n");
20872774f206SBjoern A. Zeeb 
20882774f206SBjoern A. Zeeb 	return ret;
20892774f206SBjoern A. Zeeb }
20902774f206SBjoern A. Zeeb 
20912774f206SBjoern A. Zeeb static void rtw_fw_set_scan_offload(struct rtw_dev *rtwdev,
20922774f206SBjoern A. Zeeb 				    struct rtw_ch_switch_option *opt,
20932774f206SBjoern A. Zeeb 				    struct rtw_vif *rtwvif,
20942774f206SBjoern A. Zeeb 				    struct rtw_chan_list *list)
20952774f206SBjoern A. Zeeb {
20962774f206SBjoern A. Zeeb 	struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
20972774f206SBjoern A. Zeeb 	struct cfg80211_scan_request *req = rtwvif->scan_req;
20982774f206SBjoern A. Zeeb 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
20992774f206SBjoern A. Zeeb 	/* reserve one dummy page at the beginning for tx descriptor */
21002774f206SBjoern A. Zeeb 	u8 pkt_loc = fifo->rsvd_h2c_info_addr - fifo->rsvd_boundary + 1;
21012774f206SBjoern A. Zeeb 	bool random_seq = req->flags & NL80211_SCAN_FLAG_RANDOM_SN;
21022774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
21032774f206SBjoern A. Zeeb 
21042774f206SBjoern A. Zeeb 	rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_SCAN_OFFLOAD);
21052774f206SBjoern A. Zeeb 	SET_PKT_H2C_TOTAL_LEN(h2c_pkt, H2C_PKT_CH_SWITCH_LEN);
21062774f206SBjoern A. Zeeb 
21072774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_START(h2c_pkt, opt->switch_en);
21082774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_BACK_OP_EN(h2c_pkt, opt->back_op_en);
21092774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_RANDOM_SEQ_EN(h2c_pkt, random_seq);
21102774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_NO_CCK_EN(h2c_pkt, req->no_cck);
21112774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_CH_NUM(h2c_pkt, list->ch_num);
21122774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_CH_INFO_SIZE(h2c_pkt, list->size);
21132774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_CH_INFO_LOC(h2c_pkt, list->addr - fifo->rsvd_boundary);
21142774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_OP_CH(h2c_pkt, scan_info->op_chan);
21152774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_OP_PRI_CH_IDX(h2c_pkt, scan_info->op_pri_ch_idx);
21162774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_OP_BW(h2c_pkt, scan_info->op_bw);
21172774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_OP_PORT_ID(h2c_pkt, rtwvif->port);
21182774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_OP_DWELL_TIME(h2c_pkt, req->duration_mandatory ?
21192774f206SBjoern A. Zeeb 				       req->duration : RTW_CHANNEL_TIME);
21202774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_OP_GAP_TIME(h2c_pkt, RTW_OFF_CHAN_TIME);
21212774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_SSID_NUM(h2c_pkt, req->n_ssids);
21222774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_PKT_LOC(h2c_pkt, pkt_loc);
21232774f206SBjoern A. Zeeb 
21242774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
21252774f206SBjoern A. Zeeb }
21262774f206SBjoern A. Zeeb 
21272774f206SBjoern A. Zeeb void rtw_hw_scan_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
21282774f206SBjoern A. Zeeb 		       struct ieee80211_scan_request *scan_req)
21292774f206SBjoern A. Zeeb {
21302774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
21312774f206SBjoern A. Zeeb 	struct cfg80211_scan_request *req = &scan_req->req;
21322774f206SBjoern A. Zeeb 	u8 mac_addr[ETH_ALEN];
21332774f206SBjoern A. Zeeb 
21342774f206SBjoern A. Zeeb 	rtwdev->scan_info.scanning_vif = vif;
21352774f206SBjoern A. Zeeb 	rtwvif->scan_ies = &scan_req->ies;
21362774f206SBjoern A. Zeeb 	rtwvif->scan_req = req;
21372774f206SBjoern A. Zeeb 
21382774f206SBjoern A. Zeeb 	ieee80211_stop_queues(rtwdev->hw);
2139*90aac0d8SBjoern A. Zeeb 	rtw_leave_lps_deep(rtwdev);
2140*90aac0d8SBjoern A. Zeeb 	rtw_hci_flush_all_queues(rtwdev, false);
2141*90aac0d8SBjoern A. Zeeb 	rtw_mac_flush_all_queues(rtwdev, false);
21422774f206SBjoern A. Zeeb 	if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
21432774f206SBjoern A. Zeeb 		get_random_mask_addr(mac_addr, req->mac_addr,
21442774f206SBjoern A. Zeeb 				     req->mac_addr_mask);
21452774f206SBjoern A. Zeeb 	else
21462774f206SBjoern A. Zeeb 		ether_addr_copy(mac_addr, vif->addr);
21472774f206SBjoern A. Zeeb 
21482774f206SBjoern A. Zeeb 	rtw_core_scan_start(rtwdev, rtwvif, mac_addr, true);
21492774f206SBjoern A. Zeeb 
21502774f206SBjoern A. Zeeb 	rtwdev->hal.rcr &= ~BIT_CBSSID_BCN;
21512774f206SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
21522774f206SBjoern A. Zeeb }
21532774f206SBjoern A. Zeeb 
21542774f206SBjoern A. Zeeb void rtw_hw_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
21552774f206SBjoern A. Zeeb 			  bool aborted)
21562774f206SBjoern A. Zeeb {
21572774f206SBjoern A. Zeeb 	struct cfg80211_scan_info info = {
21582774f206SBjoern A. Zeeb 		.aborted = aborted,
21592774f206SBjoern A. Zeeb 	};
21609c951734SBjoern A. Zeeb 	struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
21619c951734SBjoern A. Zeeb 	struct rtw_hal *hal = &rtwdev->hal;
21622774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif;
21639c951734SBjoern A. Zeeb 	u8 chan = scan_info->op_chan;
21642774f206SBjoern A. Zeeb 
21652774f206SBjoern A. Zeeb 	if (!vif)
21662774f206SBjoern A. Zeeb 		return;
21672774f206SBjoern A. Zeeb 
21682774f206SBjoern A. Zeeb 	rtwdev->hal.rcr |= BIT_CBSSID_BCN;
21692774f206SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
21702774f206SBjoern A. Zeeb 
21719c951734SBjoern A. Zeeb 	rtw_core_scan_complete(rtwdev, vif, true);
21722774f206SBjoern A. Zeeb 
21739c951734SBjoern A. Zeeb 	rtwvif = (struct rtw_vif *)vif->drv_priv;
2174*90aac0d8SBjoern A. Zeeb 	if (chan)
2175*90aac0d8SBjoern A. Zeeb 		rtw_store_op_chan(rtwdev, false);
2176*90aac0d8SBjoern A. Zeeb 	rtw_phy_set_tx_power_level(rtwdev, hal->current_channel);
21772774f206SBjoern A. Zeeb 	ieee80211_wake_queues(rtwdev->hw);
21782774f206SBjoern A. Zeeb 	ieee80211_scan_completed(rtwdev->hw, &info);
21792774f206SBjoern A. Zeeb 
21802774f206SBjoern A. Zeeb 	rtwvif->scan_req = NULL;
21812774f206SBjoern A. Zeeb 	rtwvif->scan_ies = NULL;
21822774f206SBjoern A. Zeeb 	rtwdev->scan_info.scanning_vif = NULL;
21832774f206SBjoern A. Zeeb }
21842774f206SBjoern A. Zeeb 
21852774f206SBjoern A. Zeeb static int rtw_hw_scan_prehandle(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif,
21862774f206SBjoern A. Zeeb 				 struct rtw_chan_list *list)
21872774f206SBjoern A. Zeeb {
21882774f206SBjoern A. Zeeb 	struct cfg80211_scan_request *req = rtwvif->scan_req;
21892774f206SBjoern A. Zeeb 	int size = req->n_channels * (RTW_CH_INFO_SIZE + RTW_EX_CH_INFO_SIZE);
21902774f206SBjoern A. Zeeb 	u8 *buf;
21912774f206SBjoern A. Zeeb 	int ret;
21922774f206SBjoern A. Zeeb 
21932774f206SBjoern A. Zeeb 	buf = kmalloc(size, GFP_KERNEL);
21942774f206SBjoern A. Zeeb 	if (!buf)
21952774f206SBjoern A. Zeeb 		return -ENOMEM;
21962774f206SBjoern A. Zeeb 
21972774f206SBjoern A. Zeeb 	ret = rtw_hw_scan_update_probe_req(rtwdev, rtwvif);
21982774f206SBjoern A. Zeeb 	if (ret) {
21992774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "Update probe request failed\n");
22002774f206SBjoern A. Zeeb 		goto out;
22012774f206SBjoern A. Zeeb 	}
22022774f206SBjoern A. Zeeb 
22032774f206SBjoern A. Zeeb 	list->buf_size = size;
22042774f206SBjoern A. Zeeb 	list->size = 0;
22052774f206SBjoern A. Zeeb 	list->ch_num = 0;
22062774f206SBjoern A. Zeeb 	ret = rtw_add_chan_list(rtwdev, rtwvif, list, buf);
22072774f206SBjoern A. Zeeb out:
22082774f206SBjoern A. Zeeb 	kfree(buf);
22092774f206SBjoern A. Zeeb 
22102774f206SBjoern A. Zeeb 	return ret;
22112774f206SBjoern A. Zeeb }
22122774f206SBjoern A. Zeeb 
22132774f206SBjoern A. Zeeb int rtw_hw_scan_offload(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
22142774f206SBjoern A. Zeeb 			bool enable)
22152774f206SBjoern A. Zeeb {
22162774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif = vif ? (struct rtw_vif *)vif->drv_priv : NULL;
2217*90aac0d8SBjoern A. Zeeb 	struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
22182774f206SBjoern A. Zeeb 	struct rtw_ch_switch_option cs_option = {0};
22192774f206SBjoern A. Zeeb 	struct rtw_chan_list chan_list = {0};
22202774f206SBjoern A. Zeeb 	int ret = 0;
22212774f206SBjoern A. Zeeb 
22222774f206SBjoern A. Zeeb 	if (!rtwvif)
22232774f206SBjoern A. Zeeb 		return -EINVAL;
22242774f206SBjoern A. Zeeb 
22252774f206SBjoern A. Zeeb 	cs_option.switch_en = enable;
2226*90aac0d8SBjoern A. Zeeb 	cs_option.back_op_en = scan_info->op_chan != 0;
22272774f206SBjoern A. Zeeb 	if (enable) {
22282774f206SBjoern A. Zeeb 		ret = rtw_hw_scan_prehandle(rtwdev, rtwvif, &chan_list);
22292774f206SBjoern A. Zeeb 		if (ret)
22302774f206SBjoern A. Zeeb 			goto out;
22312774f206SBjoern A. Zeeb 	}
22322774f206SBjoern A. Zeeb 	rtw_fw_set_scan_offload(rtwdev, &cs_option, rtwvif, &chan_list);
22332774f206SBjoern A. Zeeb out:
2234*90aac0d8SBjoern A. Zeeb 	if (rtwdev->ap_active) {
2235*90aac0d8SBjoern A. Zeeb 		ret = rtw_download_beacon(rtwdev);
2236*90aac0d8SBjoern A. Zeeb 		if (ret)
2237*90aac0d8SBjoern A. Zeeb 			rtw_err(rtwdev, "HW scan download beacon failed\n");
2238*90aac0d8SBjoern A. Zeeb 	}
2239*90aac0d8SBjoern A. Zeeb 
22402774f206SBjoern A. Zeeb 	return ret;
22412774f206SBjoern A. Zeeb }
22422774f206SBjoern A. Zeeb 
2243*90aac0d8SBjoern A. Zeeb void rtw_hw_scan_abort(struct rtw_dev *rtwdev)
22442774f206SBjoern A. Zeeb {
2245*90aac0d8SBjoern A. Zeeb 	struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif;
2246*90aac0d8SBjoern A. Zeeb 
22472774f206SBjoern A. Zeeb 	if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD))
22482774f206SBjoern A. Zeeb 		return;
22492774f206SBjoern A. Zeeb 
22502774f206SBjoern A. Zeeb 	rtw_hw_scan_offload(rtwdev, vif, false);
22512774f206SBjoern A. Zeeb 	rtw_hw_scan_complete(rtwdev, vif, true);
22522774f206SBjoern A. Zeeb }
22532774f206SBjoern A. Zeeb 
22542774f206SBjoern A. Zeeb void rtw_hw_scan_status_report(struct rtw_dev *rtwdev, struct sk_buff *skb)
22552774f206SBjoern A. Zeeb {
22562774f206SBjoern A. Zeeb 	struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif;
22572774f206SBjoern A. Zeeb 	struct rtw_c2h_cmd *c2h;
22582774f206SBjoern A. Zeeb 	bool aborted;
22592774f206SBjoern A. Zeeb 	u8 rc;
22602774f206SBjoern A. Zeeb 
22612774f206SBjoern A. Zeeb 	if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
22622774f206SBjoern A. Zeeb 		return;
22632774f206SBjoern A. Zeeb 
22642774f206SBjoern A. Zeeb 	c2h = get_c2h_from_skb(skb);
22652774f206SBjoern A. Zeeb 	rc = GET_SCAN_REPORT_RETURN_CODE(c2h->payload);
22662774f206SBjoern A. Zeeb 	aborted = rc != RTW_SCAN_REPORT_SUCCESS;
22672774f206SBjoern A. Zeeb 	rtw_hw_scan_complete(rtwdev, vif, aborted);
22682774f206SBjoern A. Zeeb 
22692774f206SBjoern A. Zeeb 	if (aborted)
22709c951734SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_HW_SCAN, "HW scan aborted with code: %d\n", rc);
22712774f206SBjoern A. Zeeb }
22722774f206SBjoern A. Zeeb 
2273*90aac0d8SBjoern A. Zeeb void rtw_store_op_chan(struct rtw_dev *rtwdev, bool backup)
22742774f206SBjoern A. Zeeb {
22752774f206SBjoern A. Zeeb 	struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
22762774f206SBjoern A. Zeeb 	struct rtw_hal *hal = &rtwdev->hal;
2277*90aac0d8SBjoern A. Zeeb 	u8 band;
22782774f206SBjoern A. Zeeb 
2279*90aac0d8SBjoern A. Zeeb 	if (backup) {
22802774f206SBjoern A. Zeeb 		scan_info->op_chan = hal->current_channel;
22812774f206SBjoern A. Zeeb 		scan_info->op_bw = hal->current_band_width;
22822774f206SBjoern A. Zeeb 		scan_info->op_pri_ch_idx = hal->current_primary_channel_index;
2283*90aac0d8SBjoern A. Zeeb 		scan_info->op_pri_ch = hal->primary_channel;
2284*90aac0d8SBjoern A. Zeeb 	} else {
2285*90aac0d8SBjoern A. Zeeb 		band = scan_info->op_chan > 14 ? RTW_BAND_5G : RTW_BAND_2G;
2286*90aac0d8SBjoern A. Zeeb 		rtw_update_channel(rtwdev, scan_info->op_chan,
2287*90aac0d8SBjoern A. Zeeb 				   scan_info->op_pri_ch,
2288*90aac0d8SBjoern A. Zeeb 				   band, scan_info->op_bw);
2289*90aac0d8SBjoern A. Zeeb 	}
2290*90aac0d8SBjoern A. Zeeb }
2291*90aac0d8SBjoern A. Zeeb 
2292*90aac0d8SBjoern A. Zeeb void rtw_clear_op_chan(struct rtw_dev *rtwdev)
2293*90aac0d8SBjoern A. Zeeb {
2294*90aac0d8SBjoern A. Zeeb 	struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
2295*90aac0d8SBjoern A. Zeeb 
2296*90aac0d8SBjoern A. Zeeb 	scan_info->op_chan = 0;
2297*90aac0d8SBjoern A. Zeeb 	scan_info->op_bw = 0;
2298*90aac0d8SBjoern A. Zeeb 	scan_info->op_pri_ch_idx = 0;
2299*90aac0d8SBjoern A. Zeeb 	scan_info->op_pri_ch = 0;
23002774f206SBjoern A. Zeeb }
23012774f206SBjoern A. Zeeb 
23022774f206SBjoern A. Zeeb static bool rtw_is_op_chan(struct rtw_dev *rtwdev, u8 channel)
23032774f206SBjoern A. Zeeb {
23042774f206SBjoern A. Zeeb 	struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
23052774f206SBjoern A. Zeeb 
23062774f206SBjoern A. Zeeb 	return channel == scan_info->op_chan;
23072774f206SBjoern A. Zeeb }
23082774f206SBjoern A. Zeeb 
23092774f206SBjoern A. Zeeb void rtw_hw_scan_chan_switch(struct rtw_dev *rtwdev, struct sk_buff *skb)
23102774f206SBjoern A. Zeeb {
23112774f206SBjoern A. Zeeb 	struct rtw_hal *hal = &rtwdev->hal;
23122774f206SBjoern A. Zeeb 	struct rtw_c2h_cmd *c2h;
23132774f206SBjoern A. Zeeb 	enum rtw_scan_notify_id id;
2314*90aac0d8SBjoern A. Zeeb 	u8 chan, band, status;
23152774f206SBjoern A. Zeeb 
23169c951734SBjoern A. Zeeb 	if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
23179c951734SBjoern A. Zeeb 		return;
23189c951734SBjoern A. Zeeb 
23192774f206SBjoern A. Zeeb 	c2h = get_c2h_from_skb(skb);
23202774f206SBjoern A. Zeeb 	chan = GET_CHAN_SWITCH_CENTRAL_CH(c2h->payload);
23212774f206SBjoern A. Zeeb 	id = GET_CHAN_SWITCH_ID(c2h->payload);
23222774f206SBjoern A. Zeeb 	status = GET_CHAN_SWITCH_STATUS(c2h->payload);
23232774f206SBjoern A. Zeeb 
23242774f206SBjoern A. Zeeb 	if (id == RTW_SCAN_NOTIFY_ID_POSTSWITCH) {
2325*90aac0d8SBjoern A. Zeeb 		band = chan > 14 ? RTW_BAND_5G : RTW_BAND_2G;
2326*90aac0d8SBjoern A. Zeeb 		rtw_update_channel(rtwdev, chan, chan, band,
2327*90aac0d8SBjoern A. Zeeb 				   RTW_CHANNEL_WIDTH_20);
2328*90aac0d8SBjoern A. Zeeb 		if (rtw_is_op_chan(rtwdev, chan)) {
2329*90aac0d8SBjoern A. Zeeb 			rtw_store_op_chan(rtwdev, false);
23302774f206SBjoern A. Zeeb 			ieee80211_wake_queues(rtwdev->hw);
2331*90aac0d8SBjoern A. Zeeb 			rtw_core_enable_beacon(rtwdev, true);
2332*90aac0d8SBjoern A. Zeeb 		}
23332774f206SBjoern A. Zeeb 	} else if (id == RTW_SCAN_NOTIFY_ID_PRESWITCH) {
23342774f206SBjoern A. Zeeb 		if (IS_CH_5G_BAND(chan)) {
23352774f206SBjoern A. Zeeb 			rtw_coex_switchband_notify(rtwdev, COEX_SWITCH_TO_5G);
23362774f206SBjoern A. Zeeb 		} else if (IS_CH_2G_BAND(chan)) {
23372774f206SBjoern A. Zeeb 			u8 chan_type;
23382774f206SBjoern A. Zeeb 
23392774f206SBjoern A. Zeeb 			if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
23402774f206SBjoern A. Zeeb 				chan_type = COEX_SWITCH_TO_24G;
23412774f206SBjoern A. Zeeb 			else
23422774f206SBjoern A. Zeeb 				chan_type = COEX_SWITCH_TO_24G_NOFORSCAN;
23432774f206SBjoern A. Zeeb 			rtw_coex_switchband_notify(rtwdev, chan_type);
23442774f206SBjoern A. Zeeb 		}
2345*90aac0d8SBjoern A. Zeeb 		/* The channel of C2H RTW_SCAN_NOTIFY_ID_PRESWITCH is next
2346*90aac0d8SBjoern A. Zeeb 		 * channel that hardware will switch. We need to stop queue
2347*90aac0d8SBjoern A. Zeeb 		 * if next channel is non-op channel.
2348*90aac0d8SBjoern A. Zeeb 		 */
2349*90aac0d8SBjoern A. Zeeb 		if (!rtw_is_op_chan(rtwdev, chan) &&
2350*90aac0d8SBjoern A. Zeeb 		    rtw_is_op_chan(rtwdev, hal->current_channel)) {
2351*90aac0d8SBjoern A. Zeeb 			rtw_core_enable_beacon(rtwdev, false);
23522774f206SBjoern A. Zeeb 			ieee80211_stop_queues(rtwdev->hw);
23532774f206SBjoern A. Zeeb 		}
2354*90aac0d8SBjoern A. Zeeb 	}
23552774f206SBjoern A. Zeeb 
23562774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_HW_SCAN,
23572774f206SBjoern A. Zeeb 		"Chan switch: %x, id: %x, status: %x\n", chan, id, status);
23582774f206SBjoern A. Zeeb }
2359