xref: /freebsd/sys/contrib/dev/rtw88/fw.c (revision 11c53278a8a3e86e14377f09bbaa7bad193d3713)
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"
1790aac0d8SBjoern A. Zeeb #include "phy.h"
1890aac0d8SBjoern A. Zeeb #include "mac.h"
192774f206SBjoern A. Zeeb 
20*11c53278SBjoern A. Zeeb static const struct rtw_hw_reg_desc fw_h2c_regs[] = {
21*11c53278SBjoern A. Zeeb 	{REG_FWIMR, MASKDWORD, "FWIMR"},
22*11c53278SBjoern A. Zeeb 	{REG_FWIMR, BIT_FS_H2CCMD_INT_EN, "FWIMR enable"},
23*11c53278SBjoern A. Zeeb 	{REG_FWISR, MASKDWORD, "FWISR"},
24*11c53278SBjoern A. Zeeb 	{REG_FWISR, BIT_FS_H2CCMD_INT, "FWISR enable"},
25*11c53278SBjoern A. Zeeb 	{REG_HMETFR, BIT_INT_BOX_ALL, "BoxBitMap"},
26*11c53278SBjoern A. Zeeb 	{REG_HMEBOX0, MASKDWORD, "MSG 0"},
27*11c53278SBjoern A. Zeeb 	{REG_HMEBOX0_EX, MASKDWORD, "MSG_EX 0"},
28*11c53278SBjoern A. Zeeb 	{REG_HMEBOX1, MASKDWORD, "MSG 1"},
29*11c53278SBjoern A. Zeeb 	{REG_HMEBOX1_EX, MASKDWORD, "MSG_EX 1"},
30*11c53278SBjoern A. Zeeb 	{REG_HMEBOX2, MASKDWORD, "MSG 2"},
31*11c53278SBjoern A. Zeeb 	{REG_HMEBOX2_EX, MASKDWORD, "MSG_EX 2"},
32*11c53278SBjoern A. Zeeb 	{REG_HMEBOX3, MASKDWORD, "MSG 3"},
33*11c53278SBjoern A. Zeeb 	{REG_HMEBOX3_EX, MASKDWORD, "MSG_EX 3"},
34*11c53278SBjoern A. Zeeb 	{REG_FT1IMR, MASKDWORD, "FT1IMR"},
35*11c53278SBjoern A. Zeeb 	{REG_FT1IMR, BIT_FS_H2C_CMD_OK_INT_EN, "FT1IMR enable"},
36*11c53278SBjoern A. Zeeb 	{REG_FT1ISR, MASKDWORD, "FT1ISR"},
37*11c53278SBjoern A. Zeeb 	{REG_FT1ISR, BIT_FS_H2C_CMD_OK_INT, "FT1ISR enable "},
38*11c53278SBjoern A. Zeeb };
39*11c53278SBjoern A. Zeeb 
40*11c53278SBjoern A. Zeeb static const struct rtw_hw_reg_desc fw_c2h_regs[] = {
41*11c53278SBjoern A. Zeeb 	{REG_FWIMR, MASKDWORD, "FWIMR"},
42*11c53278SBjoern A. Zeeb 	{REG_FWIMR, BIT_FS_H2CCMD_INT_EN, "CPWM"},
43*11c53278SBjoern A. Zeeb 	{REG_FWIMR, BIT_FS_HRCV_INT_EN, "HRECV"},
44*11c53278SBjoern A. Zeeb 	{REG_FWISR, MASKDWORD, "FWISR"},
45*11c53278SBjoern A. Zeeb 	{REG_FWISR, BIT_FS_H2CCMD_INT, "CPWM"},
46*11c53278SBjoern A. Zeeb 	{REG_FWISR, BIT_FS_HRCV_INT, "HRECV"},
47*11c53278SBjoern A. Zeeb 	{REG_CPWM, MASKDWORD, "REG_CPWM"},
48*11c53278SBjoern A. Zeeb };
49*11c53278SBjoern A. Zeeb 
50*11c53278SBjoern A. Zeeb static const struct rtw_hw_reg_desc fw_core_regs[] = {
51*11c53278SBjoern A. Zeeb 	{REG_ARFR2_V1, MASKDWORD, "EPC"},
52*11c53278SBjoern A. Zeeb 	{REG_ARFRH2_V1, MASKDWORD, "BADADDR"},
53*11c53278SBjoern A. Zeeb 	{REG_ARFR3_V1, MASKDWORD, "CAUSE"},
54*11c53278SBjoern A. Zeeb 	{REG_ARFR3_V1, BIT_EXC_CODE, "ExcCode"},
55*11c53278SBjoern A. Zeeb 	{REG_ARFRH3_V1, MASKDWORD, "Status"},
56*11c53278SBjoern A. Zeeb 	{REG_ARFR4, MASKDWORD, "SP"},
57*11c53278SBjoern A. Zeeb 	{REG_ARFRH4, MASKDWORD, "RA"},
58*11c53278SBjoern A. Zeeb 	{REG_FW_DBG6, MASKDWORD, "DBG 6"},
59*11c53278SBjoern A. Zeeb 	{REG_FW_DBG7, MASKDWORD, "DBG 7"},
60*11c53278SBjoern A. Zeeb };
61*11c53278SBjoern A. Zeeb 
62*11c53278SBjoern A. Zeeb static void _rtw_fw_dump_dbg_info(struct rtw_dev *rtwdev,
63*11c53278SBjoern A. Zeeb 				  const struct rtw_hw_reg_desc regs[], u32 size)
64*11c53278SBjoern A. Zeeb {
65*11c53278SBjoern A. Zeeb 	const struct rtw_hw_reg_desc *reg;
66*11c53278SBjoern A. Zeeb 	u32 val;
67*11c53278SBjoern A. Zeeb 	int i;
68*11c53278SBjoern A. Zeeb 
69*11c53278SBjoern A. Zeeb 	for (i = 0;  i < size; i++) {
70*11c53278SBjoern A. Zeeb 		reg = &regs[i];
71*11c53278SBjoern A. Zeeb 		val = rtw_read32_mask(rtwdev, reg->addr, reg->mask);
72*11c53278SBjoern A. Zeeb 
73*11c53278SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_FW, "[%s]addr:0x%x mask:0x%x value:0x%x\n",
74*11c53278SBjoern A. Zeeb 			reg->desc, reg->addr, reg->mask, val);
75*11c53278SBjoern A. Zeeb 	}
76*11c53278SBjoern A. Zeeb }
77*11c53278SBjoern A. Zeeb 
78*11c53278SBjoern A. Zeeb void rtw_fw_dump_dbg_info(struct rtw_dev *rtwdev)
79*11c53278SBjoern A. Zeeb {
80*11c53278SBjoern A. Zeeb 	int i;
81*11c53278SBjoern A. Zeeb 
82*11c53278SBjoern A. Zeeb 	if (!rtw_dbg_is_enabled(rtwdev, RTW_DBG_FW))
83*11c53278SBjoern A. Zeeb 		return;
84*11c53278SBjoern A. Zeeb 
85*11c53278SBjoern A. Zeeb 	_rtw_fw_dump_dbg_info(rtwdev, fw_h2c_regs, ARRAY_SIZE(fw_h2c_regs));
86*11c53278SBjoern A. Zeeb 	_rtw_fw_dump_dbg_info(rtwdev, fw_c2h_regs, ARRAY_SIZE(fw_c2h_regs));
87*11c53278SBjoern A. Zeeb 	for (i = 0 ; i < RTW_DEBUG_DUMP_TIMES; i++) {
88*11c53278SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_FW, "Firmware Coredump %dth\n", i + 1);
89*11c53278SBjoern A. Zeeb 		_rtw_fw_dump_dbg_info(rtwdev, fw_core_regs, ARRAY_SIZE(fw_core_regs));
90*11c53278SBjoern A. Zeeb 	}
91*11c53278SBjoern A. Zeeb }
92*11c53278SBjoern A. Zeeb 
932774f206SBjoern A. Zeeb static void rtw_fw_c2h_cmd_handle_ext(struct rtw_dev *rtwdev,
942774f206SBjoern A. Zeeb 				      struct sk_buff *skb)
952774f206SBjoern A. Zeeb {
962774f206SBjoern A. Zeeb 	struct rtw_c2h_cmd *c2h;
972774f206SBjoern A. Zeeb 	u8 sub_cmd_id;
982774f206SBjoern A. Zeeb 
992774f206SBjoern A. Zeeb 	c2h = get_c2h_from_skb(skb);
1002774f206SBjoern A. Zeeb 	sub_cmd_id = c2h->payload[0];
1012774f206SBjoern A. Zeeb 
1022774f206SBjoern A. Zeeb 	switch (sub_cmd_id) {
1032774f206SBjoern A. Zeeb 	case C2H_CCX_RPT:
1042774f206SBjoern A. Zeeb 		rtw_tx_report_handle(rtwdev, skb, C2H_CCX_RPT);
1052774f206SBjoern A. Zeeb 		break;
1062774f206SBjoern A. Zeeb 	case C2H_SCAN_STATUS_RPT:
1072774f206SBjoern A. Zeeb 		rtw_hw_scan_status_report(rtwdev, skb);
1082774f206SBjoern A. Zeeb 		break;
1092774f206SBjoern A. Zeeb 	case C2H_CHAN_SWITCH:
1102774f206SBjoern A. Zeeb 		rtw_hw_scan_chan_switch(rtwdev, skb);
1112774f206SBjoern A. Zeeb 		break;
1122774f206SBjoern A. Zeeb 	default:
1132774f206SBjoern A. Zeeb 		break;
1142774f206SBjoern A. Zeeb 	}
1152774f206SBjoern A. Zeeb }
1162774f206SBjoern A. Zeeb 
1172774f206SBjoern A. Zeeb static u16 get_max_amsdu_len(u32 bit_rate)
1182774f206SBjoern A. Zeeb {
1192774f206SBjoern A. Zeeb 	/* lower than ofdm, do not aggregate */
1202774f206SBjoern A. Zeeb 	if (bit_rate < 550)
1212774f206SBjoern A. Zeeb 		return 1;
1222774f206SBjoern A. Zeeb 
1232774f206SBjoern A. Zeeb 	/* lower than 20M 2ss mcs8, make it small */
1242774f206SBjoern A. Zeeb 	if (bit_rate < 1800)
1252774f206SBjoern A. Zeeb 		return 1200;
1262774f206SBjoern A. Zeeb 
1272774f206SBjoern A. Zeeb 	/* lower than 40M 2ss mcs9, make it medium */
1282774f206SBjoern A. Zeeb 	if (bit_rate < 4000)
1292774f206SBjoern A. Zeeb 		return 2600;
1302774f206SBjoern A. Zeeb 
1312774f206SBjoern A. Zeeb 	/* not yet 80M 2ss mcs8/9, make it twice regular packet size */
1322774f206SBjoern A. Zeeb 	if (bit_rate < 7000)
1332774f206SBjoern A. Zeeb 		return 3500;
1342774f206SBjoern A. Zeeb 
1352774f206SBjoern A. Zeeb 	/* unlimited */
1362774f206SBjoern A. Zeeb 	return 0;
1372774f206SBjoern A. Zeeb }
1382774f206SBjoern A. Zeeb 
1392774f206SBjoern A. Zeeb struct rtw_fw_iter_ra_data {
1402774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
1412774f206SBjoern A. Zeeb 	u8 *payload;
1422774f206SBjoern A. Zeeb };
1432774f206SBjoern A. Zeeb 
1442774f206SBjoern A. Zeeb static void rtw_fw_ra_report_iter(void *data, struct ieee80211_sta *sta)
1452774f206SBjoern A. Zeeb {
1462774f206SBjoern A. Zeeb 	struct rtw_fw_iter_ra_data *ra_data = data;
1472774f206SBjoern A. Zeeb 	struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv;
1482774f206SBjoern A. Zeeb 	u8 mac_id, rate, sgi, bw;
1492774f206SBjoern A. Zeeb 	u8 mcs, nss;
1502774f206SBjoern A. Zeeb 	u32 bit_rate;
1512774f206SBjoern A. Zeeb 
1522774f206SBjoern A. Zeeb 	mac_id = GET_RA_REPORT_MACID(ra_data->payload);
1532774f206SBjoern A. Zeeb 	if (si->mac_id != mac_id)
1542774f206SBjoern A. Zeeb 		return;
1552774f206SBjoern A. Zeeb 
1562774f206SBjoern A. Zeeb 	si->ra_report.txrate.flags = 0;
1572774f206SBjoern A. Zeeb 
1582774f206SBjoern A. Zeeb 	rate = GET_RA_REPORT_RATE(ra_data->payload);
1592774f206SBjoern A. Zeeb 	sgi = GET_RA_REPORT_SGI(ra_data->payload);
1602774f206SBjoern A. Zeeb 	bw = GET_RA_REPORT_BW(ra_data->payload);
1612774f206SBjoern A. Zeeb 
1622774f206SBjoern A. Zeeb 	if (rate < DESC_RATEMCS0) {
1632774f206SBjoern A. Zeeb 		si->ra_report.txrate.legacy = rtw_desc_to_bitrate(rate);
1642774f206SBjoern A. Zeeb 		goto legacy;
1652774f206SBjoern A. Zeeb 	}
1662774f206SBjoern A. Zeeb 
1672774f206SBjoern A. Zeeb 	rtw_desc_to_mcsrate(rate, &mcs, &nss);
1682774f206SBjoern A. Zeeb 	if (rate >= DESC_RATEVHT1SS_MCS0)
1692774f206SBjoern A. Zeeb 		si->ra_report.txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
1702774f206SBjoern A. Zeeb 	else if (rate >= DESC_RATEMCS0)
1712774f206SBjoern A. Zeeb 		si->ra_report.txrate.flags |= RATE_INFO_FLAGS_MCS;
1722774f206SBjoern A. Zeeb 
1732774f206SBjoern A. Zeeb 	if (rate >= DESC_RATEMCS0) {
1742774f206SBjoern A. Zeeb 		si->ra_report.txrate.mcs = mcs;
1752774f206SBjoern A. Zeeb 		si->ra_report.txrate.nss = nss;
1762774f206SBjoern A. Zeeb 	}
1772774f206SBjoern A. Zeeb 
1782774f206SBjoern A. Zeeb 	if (sgi)
1792774f206SBjoern A. Zeeb 		si->ra_report.txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
1802774f206SBjoern A. Zeeb 
1812774f206SBjoern A. Zeeb 	if (bw == RTW_CHANNEL_WIDTH_80)
1822774f206SBjoern A. Zeeb 		si->ra_report.txrate.bw = RATE_INFO_BW_80;
1832774f206SBjoern A. Zeeb 	else if (bw == RTW_CHANNEL_WIDTH_40)
1842774f206SBjoern A. Zeeb 		si->ra_report.txrate.bw = RATE_INFO_BW_40;
1852774f206SBjoern A. Zeeb 	else
1862774f206SBjoern A. Zeeb 		si->ra_report.txrate.bw = RATE_INFO_BW_20;
1872774f206SBjoern A. Zeeb 
1882774f206SBjoern A. Zeeb legacy:
1892774f206SBjoern A. Zeeb 	bit_rate = cfg80211_calculate_bitrate(&si->ra_report.txrate);
1902774f206SBjoern A. Zeeb 
1912774f206SBjoern A. Zeeb 	si->ra_report.desc_rate = rate;
1922774f206SBjoern A. Zeeb 	si->ra_report.bit_rate = bit_rate;
1932774f206SBjoern A. Zeeb 
19490aac0d8SBjoern A. Zeeb 	sta->deflink.agg.max_rc_amsdu_len = get_max_amsdu_len(bit_rate);
1952774f206SBjoern A. Zeeb }
1962774f206SBjoern A. Zeeb 
1972774f206SBjoern A. Zeeb static void rtw_fw_ra_report_handle(struct rtw_dev *rtwdev, u8 *payload,
1982774f206SBjoern A. Zeeb 				    u8 length)
1992774f206SBjoern A. Zeeb {
2002774f206SBjoern A. Zeeb 	struct rtw_fw_iter_ra_data ra_data;
2012774f206SBjoern A. Zeeb 
2022774f206SBjoern A. Zeeb 	if (WARN(length < 7, "invalid ra report c2h length\n"))
2032774f206SBjoern A. Zeeb 		return;
2042774f206SBjoern A. Zeeb 
2052774f206SBjoern A. Zeeb 	rtwdev->dm_info.tx_rate = GET_RA_REPORT_RATE(payload);
2062774f206SBjoern A. Zeeb 	ra_data.rtwdev = rtwdev;
2072774f206SBjoern A. Zeeb 	ra_data.payload = payload;
2082774f206SBjoern A. Zeeb 	rtw_iterate_stas_atomic(rtwdev, rtw_fw_ra_report_iter, &ra_data);
2092774f206SBjoern A. Zeeb }
2102774f206SBjoern A. Zeeb 
2112774f206SBjoern A. Zeeb struct rtw_beacon_filter_iter_data {
2122774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev;
2132774f206SBjoern A. Zeeb 	u8 *payload;
2142774f206SBjoern A. Zeeb };
2152774f206SBjoern A. Zeeb 
21690aac0d8SBjoern A. Zeeb static void rtw_fw_bcn_filter_notify_vif_iter(void *data,
2172774f206SBjoern A. Zeeb 					      struct ieee80211_vif *vif)
2182774f206SBjoern A. Zeeb {
2192774f206SBjoern A. Zeeb 	struct rtw_beacon_filter_iter_data *iter_data = data;
2202774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = iter_data->rtwdev;
2212774f206SBjoern A. Zeeb 	u8 *payload = iter_data->payload;
2222774f206SBjoern A. Zeeb 	u8 type = GET_BCN_FILTER_NOTIFY_TYPE(payload);
2232774f206SBjoern A. Zeeb 	u8 event = GET_BCN_FILTER_NOTIFY_EVENT(payload);
2242774f206SBjoern A. Zeeb 	s8 sig = (s8)GET_BCN_FILTER_NOTIFY_RSSI(payload);
2252774f206SBjoern A. Zeeb 
2262774f206SBjoern A. Zeeb 	switch (type) {
2272774f206SBjoern A. Zeeb 	case BCN_FILTER_NOTIFY_SIGNAL_CHANGE:
2282774f206SBjoern A. Zeeb 		event = event ? NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH :
2292774f206SBjoern A. Zeeb 			NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW;
2302774f206SBjoern A. Zeeb 		ieee80211_cqm_rssi_notify(vif, event, sig, GFP_KERNEL);
2312774f206SBjoern A. Zeeb 		break;
2322774f206SBjoern A. Zeeb 	case BCN_FILTER_CONNECTION_LOSS:
2332774f206SBjoern A. Zeeb 		ieee80211_connection_loss(vif);
2342774f206SBjoern A. Zeeb 		break;
2352774f206SBjoern A. Zeeb 	case BCN_FILTER_CONNECTED:
2362774f206SBjoern A. Zeeb 		rtwdev->beacon_loss = false;
2372774f206SBjoern A. Zeeb 		break;
2382774f206SBjoern A. Zeeb 	case BCN_FILTER_NOTIFY_BEACON_LOSS:
2392774f206SBjoern A. Zeeb 		rtwdev->beacon_loss = true;
2402774f206SBjoern A. Zeeb 		rtw_leave_lps(rtwdev);
2412774f206SBjoern A. Zeeb 		break;
2422774f206SBjoern A. Zeeb 	}
2432774f206SBjoern A. Zeeb }
2442774f206SBjoern A. Zeeb 
2452774f206SBjoern A. Zeeb static void rtw_fw_bcn_filter_notify(struct rtw_dev *rtwdev, u8 *payload,
2462774f206SBjoern A. Zeeb 				     u8 length)
2472774f206SBjoern A. Zeeb {
2482774f206SBjoern A. Zeeb 	struct rtw_beacon_filter_iter_data dev_iter_data;
2492774f206SBjoern A. Zeeb 
2502774f206SBjoern A. Zeeb 	dev_iter_data.rtwdev = rtwdev;
2512774f206SBjoern A. Zeeb 	dev_iter_data.payload = payload;
2522774f206SBjoern A. Zeeb 	rtw_iterate_vifs(rtwdev, rtw_fw_bcn_filter_notify_vif_iter,
2532774f206SBjoern A. Zeeb 			 &dev_iter_data);
2542774f206SBjoern A. Zeeb }
2552774f206SBjoern A. Zeeb 
2562774f206SBjoern A. Zeeb static void rtw_fw_scan_result(struct rtw_dev *rtwdev, u8 *payload,
2572774f206SBjoern A. Zeeb 			       u8 length)
2582774f206SBjoern A. Zeeb {
2592774f206SBjoern A. Zeeb 	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
2602774f206SBjoern A. Zeeb 
2612774f206SBjoern A. Zeeb 	dm_info->scan_density = payload[0];
2622774f206SBjoern A. Zeeb 
2632774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "scan.density = %x\n",
2642774f206SBjoern A. Zeeb 		dm_info->scan_density);
2652774f206SBjoern A. Zeeb }
2662774f206SBjoern A. Zeeb 
2672774f206SBjoern A. Zeeb static void rtw_fw_adaptivity_result(struct rtw_dev *rtwdev, u8 *payload,
2682774f206SBjoern A. Zeeb 				     u8 length)
2692774f206SBjoern A. Zeeb {
2702774f206SBjoern A. Zeeb 	struct rtw_hw_reg_offset *edcca_th = rtwdev->chip->edcca_th;
2712774f206SBjoern A. Zeeb 	struct rtw_c2h_adaptivity *result = (struct rtw_c2h_adaptivity *)payload;
2722774f206SBjoern A. Zeeb 
2732774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY,
2742774f206SBjoern A. Zeeb 		"Adaptivity: density %x igi %x l2h_th_init %x l2h %x h2l %x option %x\n",
2752774f206SBjoern A. Zeeb 		result->density, result->igi, result->l2h_th_init, result->l2h,
2762774f206SBjoern A. Zeeb 		result->h2l, result->option);
2772774f206SBjoern A. Zeeb 
2782774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "Reg Setting: L2H %x H2L %x\n",
2792774f206SBjoern A. Zeeb 		rtw_read32_mask(rtwdev, edcca_th[EDCCA_TH_L2H_IDX].hw_reg.addr,
2802774f206SBjoern A. Zeeb 				edcca_th[EDCCA_TH_L2H_IDX].hw_reg.mask),
2812774f206SBjoern A. Zeeb 		rtw_read32_mask(rtwdev, edcca_th[EDCCA_TH_H2L_IDX].hw_reg.addr,
2822774f206SBjoern A. Zeeb 				edcca_th[EDCCA_TH_H2L_IDX].hw_reg.mask));
2832774f206SBjoern A. Zeeb 
2842774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "EDCCA Flag %s\n",
2852774f206SBjoern A. Zeeb 		rtw_read32_mask(rtwdev, REG_EDCCA_REPORT, BIT_EDCCA_FLAG) ?
2862774f206SBjoern A. Zeeb 		"Set" : "Unset");
2872774f206SBjoern A. Zeeb }
2882774f206SBjoern A. Zeeb 
2892774f206SBjoern A. Zeeb void rtw_fw_c2h_cmd_handle(struct rtw_dev *rtwdev, struct sk_buff *skb)
2902774f206SBjoern A. Zeeb {
2912774f206SBjoern A. Zeeb 	struct rtw_c2h_cmd *c2h;
2922774f206SBjoern A. Zeeb 	u32 pkt_offset;
2932774f206SBjoern A. Zeeb 	u8 len;
2942774f206SBjoern A. Zeeb 
2952774f206SBjoern A. Zeeb 	pkt_offset = *((u32 *)skb->cb);
2962774f206SBjoern A. Zeeb 	c2h = (struct rtw_c2h_cmd *)(skb->data + pkt_offset);
2972774f206SBjoern A. Zeeb 	len = skb->len - pkt_offset - 2;
2982774f206SBjoern A. Zeeb 
2992774f206SBjoern A. Zeeb 	mutex_lock(&rtwdev->mutex);
3002774f206SBjoern A. Zeeb 
3012774f206SBjoern A. Zeeb 	if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags))
3022774f206SBjoern A. Zeeb 		goto unlock;
3032774f206SBjoern A. Zeeb 
3042774f206SBjoern A. Zeeb 	switch (c2h->id) {
3052774f206SBjoern A. Zeeb 	case C2H_CCX_TX_RPT:
3062774f206SBjoern A. Zeeb 		rtw_tx_report_handle(rtwdev, skb, C2H_CCX_TX_RPT);
3072774f206SBjoern A. Zeeb 		break;
3082774f206SBjoern A. Zeeb 	case C2H_BT_INFO:
3092774f206SBjoern A. Zeeb 		rtw_coex_bt_info_notify(rtwdev, c2h->payload, len);
3102774f206SBjoern A. Zeeb 		break;
3119c951734SBjoern A. Zeeb 	case C2H_BT_HID_INFO:
3129c951734SBjoern A. Zeeb 		rtw_coex_bt_hid_info_notify(rtwdev, c2h->payload, len);
3139c951734SBjoern A. Zeeb 		break;
3142774f206SBjoern A. Zeeb 	case C2H_WLAN_INFO:
3152774f206SBjoern A. Zeeb 		rtw_coex_wl_fwdbginfo_notify(rtwdev, c2h->payload, len);
3162774f206SBjoern A. Zeeb 		break;
3172774f206SBjoern A. Zeeb 	case C2H_BCN_FILTER_NOTIFY:
3182774f206SBjoern A. Zeeb 		rtw_fw_bcn_filter_notify(rtwdev, c2h->payload, len);
3192774f206SBjoern A. Zeeb 		break;
3202774f206SBjoern A. Zeeb 	case C2H_HALMAC:
3212774f206SBjoern A. Zeeb 		rtw_fw_c2h_cmd_handle_ext(rtwdev, skb);
3222774f206SBjoern A. Zeeb 		break;
3232774f206SBjoern A. Zeeb 	case C2H_RA_RPT:
3242774f206SBjoern A. Zeeb 		rtw_fw_ra_report_handle(rtwdev, c2h->payload, len);
3252774f206SBjoern A. Zeeb 		break;
3262774f206SBjoern A. Zeeb 	default:
3272774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_FW, "C2H 0x%x isn't handled\n", c2h->id);
3282774f206SBjoern A. Zeeb 		break;
3292774f206SBjoern A. Zeeb 	}
3302774f206SBjoern A. Zeeb 
3312774f206SBjoern A. Zeeb unlock:
3322774f206SBjoern A. Zeeb 	mutex_unlock(&rtwdev->mutex);
3332774f206SBjoern A. Zeeb }
3342774f206SBjoern A. Zeeb 
3352774f206SBjoern A. Zeeb void rtw_fw_c2h_cmd_rx_irqsafe(struct rtw_dev *rtwdev, u32 pkt_offset,
3362774f206SBjoern A. Zeeb 			       struct sk_buff *skb)
3372774f206SBjoern A. Zeeb {
3382774f206SBjoern A. Zeeb 	struct rtw_c2h_cmd *c2h;
3392774f206SBjoern A. Zeeb 	u8 len;
3402774f206SBjoern A. Zeeb 
3412774f206SBjoern A. Zeeb 	c2h = (struct rtw_c2h_cmd *)(skb->data + pkt_offset);
3422774f206SBjoern A. Zeeb 	len = skb->len - pkt_offset - 2;
3432774f206SBjoern A. Zeeb 	*((u32 *)skb->cb) = pkt_offset;
3442774f206SBjoern A. Zeeb 
3452774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "recv C2H, id=0x%02x, seq=0x%02x, len=%d\n",
3462774f206SBjoern A. Zeeb 		c2h->id, c2h->seq, len);
3472774f206SBjoern A. Zeeb 
3482774f206SBjoern A. Zeeb 	switch (c2h->id) {
3492774f206SBjoern A. Zeeb 	case C2H_BT_MP_INFO:
3502774f206SBjoern A. Zeeb 		rtw_coex_info_response(rtwdev, skb);
3512774f206SBjoern A. Zeeb 		break;
3522774f206SBjoern A. Zeeb 	case C2H_WLAN_RFON:
3532774f206SBjoern A. Zeeb 		complete(&rtwdev->lps_leave_check);
3542774f206SBjoern A. Zeeb 		dev_kfree_skb_any(skb);
3552774f206SBjoern A. Zeeb 		break;
3562774f206SBjoern A. Zeeb 	case C2H_SCAN_RESULT:
3572774f206SBjoern A. Zeeb 		complete(&rtwdev->fw_scan_density);
3582774f206SBjoern A. Zeeb 		rtw_fw_scan_result(rtwdev, c2h->payload, len);
3592774f206SBjoern A. Zeeb 		dev_kfree_skb_any(skb);
3602774f206SBjoern A. Zeeb 		break;
3612774f206SBjoern A. Zeeb 	case C2H_ADAPTIVITY:
3622774f206SBjoern A. Zeeb 		rtw_fw_adaptivity_result(rtwdev, c2h->payload, len);
3632774f206SBjoern A. Zeeb 		dev_kfree_skb_any(skb);
3642774f206SBjoern A. Zeeb 		break;
3652774f206SBjoern A. Zeeb 	default:
3662774f206SBjoern A. Zeeb 		/* pass offset for further operation */
3672774f206SBjoern A. Zeeb 		*((u32 *)skb->cb) = pkt_offset;
3682774f206SBjoern A. Zeeb 		skb_queue_tail(&rtwdev->c2h_queue, skb);
3692774f206SBjoern A. Zeeb 		ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work);
3702774f206SBjoern A. Zeeb 		break;
3712774f206SBjoern A. Zeeb 	}
3722774f206SBjoern A. Zeeb }
3732774f206SBjoern A. Zeeb EXPORT_SYMBOL(rtw_fw_c2h_cmd_rx_irqsafe);
3742774f206SBjoern A. Zeeb 
3752774f206SBjoern A. Zeeb void rtw_fw_c2h_cmd_isr(struct rtw_dev *rtwdev)
3762774f206SBjoern A. Zeeb {
3772774f206SBjoern A. Zeeb 	if (rtw_read8(rtwdev, REG_MCU_TST_CFG) == VAL_FW_TRIGGER)
3782774f206SBjoern A. Zeeb 		rtw_fw_recovery(rtwdev);
3792774f206SBjoern A. Zeeb 	else
3802774f206SBjoern A. Zeeb 		rtw_warn(rtwdev, "unhandled firmware c2h interrupt\n");
3812774f206SBjoern A. Zeeb }
3822774f206SBjoern A. Zeeb EXPORT_SYMBOL(rtw_fw_c2h_cmd_isr);
3832774f206SBjoern A. Zeeb 
38490aac0d8SBjoern A. Zeeb static void rtw_fw_send_h2c_command_register(struct rtw_dev *rtwdev,
38590aac0d8SBjoern A. Zeeb 					     struct rtw_h2c_register *h2c)
3862774f206SBjoern A. Zeeb {
3872774f206SBjoern A. Zeeb 	u32 box_reg, box_ex_reg;
38890aac0d8SBjoern A. Zeeb 	u8 box_state, box;
3892774f206SBjoern A. Zeeb 	int ret;
3902774f206SBjoern A. Zeeb 
39190aac0d8SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "send H2C content %08x %08x\n", h2c->w0,
39290aac0d8SBjoern A. Zeeb 		h2c->w1);
3932774f206SBjoern A. Zeeb 
39490aac0d8SBjoern A. Zeeb 	lockdep_assert_held(&rtwdev->mutex);
3952774f206SBjoern A. Zeeb 
3962774f206SBjoern A. Zeeb 	box = rtwdev->h2c.last_box_num;
3972774f206SBjoern A. Zeeb 	switch (box) {
3982774f206SBjoern A. Zeeb 	case 0:
3992774f206SBjoern A. Zeeb 		box_reg = REG_HMEBOX0;
4002774f206SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX0_EX;
4012774f206SBjoern A. Zeeb 		break;
4022774f206SBjoern A. Zeeb 	case 1:
4032774f206SBjoern A. Zeeb 		box_reg = REG_HMEBOX1;
4042774f206SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX1_EX;
4052774f206SBjoern A. Zeeb 		break;
4062774f206SBjoern A. Zeeb 	case 2:
4072774f206SBjoern A. Zeeb 		box_reg = REG_HMEBOX2;
4082774f206SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX2_EX;
4092774f206SBjoern A. Zeeb 		break;
4102774f206SBjoern A. Zeeb 	case 3:
4112774f206SBjoern A. Zeeb 		box_reg = REG_HMEBOX3;
4122774f206SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX3_EX;
4132774f206SBjoern A. Zeeb 		break;
4142774f206SBjoern A. Zeeb 	default:
4152774f206SBjoern A. Zeeb 		WARN(1, "invalid h2c mail box number\n");
41690aac0d8SBjoern A. Zeeb 		return;
4172774f206SBjoern A. Zeeb 	}
4182774f206SBjoern A. Zeeb 
4192774f206SBjoern A. Zeeb 	ret = read_poll_timeout_atomic(rtw_read8, box_state,
4202774f206SBjoern A. Zeeb 				       !((box_state >> box) & 0x1), 100, 3000,
4212774f206SBjoern A. Zeeb 				       false, rtwdev, REG_HMETFR);
4222774f206SBjoern A. Zeeb 
4232774f206SBjoern A. Zeeb 	if (ret) {
4242774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to send h2c command\n");
425*11c53278SBjoern A. Zeeb 		rtw_fw_dump_dbg_info(rtwdev);
42690aac0d8SBjoern A. Zeeb 		return;
4272774f206SBjoern A. Zeeb 	}
4282774f206SBjoern A. Zeeb 
42990aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, box_ex_reg, h2c->w1);
43090aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, box_reg, h2c->w0);
4312774f206SBjoern A. Zeeb 
4322774f206SBjoern A. Zeeb 	if (++rtwdev->h2c.last_box_num >= 4)
4332774f206SBjoern A. Zeeb 		rtwdev->h2c.last_box_num = 0;
43490aac0d8SBjoern A. Zeeb }
4352774f206SBjoern A. Zeeb 
43690aac0d8SBjoern A. Zeeb static void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev,
43790aac0d8SBjoern A. Zeeb 				    u8 *h2c)
43890aac0d8SBjoern A. Zeeb {
43990aac0d8SBjoern A. Zeeb 	struct rtw_h2c_cmd *h2c_cmd = (struct rtw_h2c_cmd *)h2c;
44090aac0d8SBjoern A. Zeeb 	u8 box;
44190aac0d8SBjoern A. Zeeb 	u8 box_state;
44290aac0d8SBjoern A. Zeeb 	u32 box_reg, box_ex_reg;
44390aac0d8SBjoern A. Zeeb 	int ret;
44490aac0d8SBjoern A. Zeeb 
44590aac0d8SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW,
44690aac0d8SBjoern A. Zeeb 		"send H2C content %02x%02x%02x%02x %02x%02x%02x%02x\n",
44790aac0d8SBjoern A. Zeeb 		h2c[3], h2c[2], h2c[1], h2c[0],
44890aac0d8SBjoern A. Zeeb 		h2c[7], h2c[6], h2c[5], h2c[4]);
44990aac0d8SBjoern A. Zeeb 
45090aac0d8SBjoern A. Zeeb 	lockdep_assert_held(&rtwdev->mutex);
45190aac0d8SBjoern A. Zeeb 
45290aac0d8SBjoern A. Zeeb 	box = rtwdev->h2c.last_box_num;
45390aac0d8SBjoern A. Zeeb 	switch (box) {
45490aac0d8SBjoern A. Zeeb 	case 0:
45590aac0d8SBjoern A. Zeeb 		box_reg = REG_HMEBOX0;
45690aac0d8SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX0_EX;
45790aac0d8SBjoern A. Zeeb 		break;
45890aac0d8SBjoern A. Zeeb 	case 1:
45990aac0d8SBjoern A. Zeeb 		box_reg = REG_HMEBOX1;
46090aac0d8SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX1_EX;
46190aac0d8SBjoern A. Zeeb 		break;
46290aac0d8SBjoern A. Zeeb 	case 2:
46390aac0d8SBjoern A. Zeeb 		box_reg = REG_HMEBOX2;
46490aac0d8SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX2_EX;
46590aac0d8SBjoern A. Zeeb 		break;
46690aac0d8SBjoern A. Zeeb 	case 3:
46790aac0d8SBjoern A. Zeeb 		box_reg = REG_HMEBOX3;
46890aac0d8SBjoern A. Zeeb 		box_ex_reg = REG_HMEBOX3_EX;
46990aac0d8SBjoern A. Zeeb 		break;
47090aac0d8SBjoern A. Zeeb 	default:
47190aac0d8SBjoern A. Zeeb 		WARN(1, "invalid h2c mail box number\n");
47290aac0d8SBjoern A. Zeeb 		return;
47390aac0d8SBjoern A. Zeeb 	}
47490aac0d8SBjoern A. Zeeb 
47590aac0d8SBjoern A. Zeeb 	ret = read_poll_timeout_atomic(rtw_read8, box_state,
47690aac0d8SBjoern A. Zeeb 				       !((box_state >> box) & 0x1), 100, 3000,
47790aac0d8SBjoern A. Zeeb 				       false, rtwdev, REG_HMETFR);
47890aac0d8SBjoern A. Zeeb 
47990aac0d8SBjoern A. Zeeb 	if (ret) {
48090aac0d8SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to send h2c command\n");
48190aac0d8SBjoern A. Zeeb 		return;
48290aac0d8SBjoern A. Zeeb 	}
48390aac0d8SBjoern A. Zeeb 
48490aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, box_ex_reg, le32_to_cpu(h2c_cmd->msg_ext));
48590aac0d8SBjoern A. Zeeb 	rtw_write32(rtwdev, box_reg, le32_to_cpu(h2c_cmd->msg));
48690aac0d8SBjoern A. Zeeb 
48790aac0d8SBjoern A. Zeeb 	if (++rtwdev->h2c.last_box_num >= 4)
48890aac0d8SBjoern A. Zeeb 		rtwdev->h2c.last_box_num = 0;
4892774f206SBjoern A. Zeeb }
4902774f206SBjoern A. Zeeb 
4912774f206SBjoern A. Zeeb void rtw_fw_h2c_cmd_dbg(struct rtw_dev *rtwdev, u8 *h2c)
4922774f206SBjoern A. Zeeb {
4932774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c);
4942774f206SBjoern A. Zeeb }
4952774f206SBjoern A. Zeeb 
4962774f206SBjoern A. Zeeb static void rtw_fw_send_h2c_packet(struct rtw_dev *rtwdev, u8 *h2c_pkt)
4972774f206SBjoern A. Zeeb {
4982774f206SBjoern A. Zeeb 	int ret;
4992774f206SBjoern A. Zeeb 
50090aac0d8SBjoern A. Zeeb 	lockdep_assert_held(&rtwdev->mutex);
5012774f206SBjoern A. Zeeb 
5022774f206SBjoern A. Zeeb 	FW_OFFLOAD_H2C_SET_SEQ_NUM(h2c_pkt, rtwdev->h2c.seq);
5032774f206SBjoern A. Zeeb 	ret = rtw_hci_write_data_h2c(rtwdev, h2c_pkt, H2C_PKT_SIZE);
5042774f206SBjoern A. Zeeb 	if (ret)
5052774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to send h2c packet\n");
5062774f206SBjoern A. Zeeb 	rtwdev->h2c.seq++;
5072774f206SBjoern A. Zeeb }
5082774f206SBjoern A. Zeeb 
5092774f206SBjoern A. Zeeb void
5102774f206SBjoern A. Zeeb rtw_fw_send_general_info(struct rtw_dev *rtwdev)
5112774f206SBjoern A. Zeeb {
5122774f206SBjoern A. Zeeb 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
5132774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
5142774f206SBjoern A. Zeeb 	u16 total_size = H2C_PKT_HDR_SIZE + 4;
5152774f206SBjoern A. Zeeb 
5162774f206SBjoern A. Zeeb 	if (rtw_chip_wcpu_11n(rtwdev))
5172774f206SBjoern A. Zeeb 		return;
5182774f206SBjoern A. Zeeb 
5192774f206SBjoern A. Zeeb 	rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_GENERAL_INFO);
5202774f206SBjoern A. Zeeb 
5212774f206SBjoern A. Zeeb 	SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size);
5222774f206SBjoern A. Zeeb 
5232774f206SBjoern A. Zeeb 	GENERAL_INFO_SET_FW_TX_BOUNDARY(h2c_pkt,
5242774f206SBjoern A. Zeeb 					fifo->rsvd_fw_txbuf_addr -
5252774f206SBjoern A. Zeeb 					fifo->rsvd_boundary);
5262774f206SBjoern A. Zeeb 
5272774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
5282774f206SBjoern A. Zeeb }
5292774f206SBjoern A. Zeeb 
5302774f206SBjoern A. Zeeb void
5312774f206SBjoern A. Zeeb rtw_fw_send_phydm_info(struct rtw_dev *rtwdev)
5322774f206SBjoern A. Zeeb {
5332774f206SBjoern A. Zeeb 	struct rtw_hal *hal = &rtwdev->hal;
5342774f206SBjoern A. Zeeb 	struct rtw_efuse *efuse = &rtwdev->efuse;
5352774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
5362774f206SBjoern A. Zeeb 	u16 total_size = H2C_PKT_HDR_SIZE + 8;
5372774f206SBjoern A. Zeeb 	u8 fw_rf_type = 0;
5382774f206SBjoern A. Zeeb 
5392774f206SBjoern A. Zeeb 	if (rtw_chip_wcpu_11n(rtwdev))
5402774f206SBjoern A. Zeeb 		return;
5412774f206SBjoern A. Zeeb 
5422774f206SBjoern A. Zeeb 	if (hal->rf_type == RF_1T1R)
5432774f206SBjoern A. Zeeb 		fw_rf_type = FW_RF_1T1R;
5442774f206SBjoern A. Zeeb 	else if (hal->rf_type == RF_2T2R)
5452774f206SBjoern A. Zeeb 		fw_rf_type = FW_RF_2T2R;
5462774f206SBjoern A. Zeeb 
5472774f206SBjoern A. Zeeb 	rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_PHYDM_INFO);
5482774f206SBjoern A. Zeeb 
5492774f206SBjoern A. Zeeb 	SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size);
5502774f206SBjoern A. Zeeb 	PHYDM_INFO_SET_REF_TYPE(h2c_pkt, efuse->rfe_option);
5512774f206SBjoern A. Zeeb 	PHYDM_INFO_SET_RF_TYPE(h2c_pkt, fw_rf_type);
5522774f206SBjoern A. Zeeb 	PHYDM_INFO_SET_CUT_VER(h2c_pkt, hal->cut_version);
5532774f206SBjoern A. Zeeb 	PHYDM_INFO_SET_RX_ANT_STATUS(h2c_pkt, hal->antenna_tx);
5542774f206SBjoern A. Zeeb 	PHYDM_INFO_SET_TX_ANT_STATUS(h2c_pkt, hal->antenna_rx);
5552774f206SBjoern A. Zeeb 
5562774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
5572774f206SBjoern A. Zeeb }
5582774f206SBjoern A. Zeeb 
5592774f206SBjoern A. Zeeb void rtw_fw_do_iqk(struct rtw_dev *rtwdev, struct rtw_iqk_para *para)
5602774f206SBjoern A. Zeeb {
5612774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
5622774f206SBjoern A. Zeeb 	u16 total_size = H2C_PKT_HDR_SIZE + 1;
5632774f206SBjoern A. Zeeb 
5642774f206SBjoern A. Zeeb 	rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_IQK);
5652774f206SBjoern A. Zeeb 	SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size);
5662774f206SBjoern A. Zeeb 	IQK_SET_CLEAR(h2c_pkt, para->clear);
5672774f206SBjoern A. Zeeb 	IQK_SET_SEGMENT_IQK(h2c_pkt, para->segment_iqk);
5682774f206SBjoern A. Zeeb 
5692774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
5702774f206SBjoern A. Zeeb }
5712774f206SBjoern A. Zeeb EXPORT_SYMBOL(rtw_fw_do_iqk);
5722774f206SBjoern A. Zeeb 
5732774f206SBjoern A. Zeeb void rtw_fw_inform_rfk_status(struct rtw_dev *rtwdev, bool start)
5742774f206SBjoern A. Zeeb {
5752774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
5762774f206SBjoern A. Zeeb 
5772774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WIFI_CALIBRATION);
5782774f206SBjoern A. Zeeb 
5792774f206SBjoern A. Zeeb 	RFK_SET_INFORM_START(h2c_pkt, start);
5802774f206SBjoern A. Zeeb 
5812774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
5822774f206SBjoern A. Zeeb }
5832774f206SBjoern A. Zeeb EXPORT_SYMBOL(rtw_fw_inform_rfk_status);
5842774f206SBjoern A. Zeeb 
5852774f206SBjoern A. Zeeb void rtw_fw_query_bt_info(struct rtw_dev *rtwdev)
5862774f206SBjoern A. Zeeb {
5872774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
5882774f206SBjoern A. Zeeb 
5892774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_INFO);
5902774f206SBjoern A. Zeeb 
5912774f206SBjoern A. Zeeb 	SET_QUERY_BT_INFO(h2c_pkt, true);
5922774f206SBjoern A. Zeeb 
5932774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
5942774f206SBjoern A. Zeeb }
5952774f206SBjoern A. Zeeb 
59690aac0d8SBjoern A. Zeeb void rtw_fw_default_port(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif)
59790aac0d8SBjoern A. Zeeb {
59890aac0d8SBjoern A. Zeeb 	struct rtw_h2c_register h2c = {};
59990aac0d8SBjoern A. Zeeb 
60090aac0d8SBjoern A. Zeeb 	if (rtwvif->net_type != RTW_NET_MGD_LINKED)
60190aac0d8SBjoern A. Zeeb 		return;
60290aac0d8SBjoern A. Zeeb 
60390aac0d8SBjoern A. Zeeb 	/* Leave LPS before default port H2C so FW timer is correct */
60490aac0d8SBjoern A. Zeeb 	rtw_leave_lps(rtwdev);
60590aac0d8SBjoern A. Zeeb 
60690aac0d8SBjoern A. Zeeb 	h2c.w0 = u32_encode_bits(H2C_CMD_DEFAULT_PORT, RTW_H2C_W0_CMDID) |
60790aac0d8SBjoern A. Zeeb 		 u32_encode_bits(rtwvif->port, RTW_H2C_DEFAULT_PORT_W0_PORTID) |
60890aac0d8SBjoern A. Zeeb 		 u32_encode_bits(rtwvif->mac_id, RTW_H2C_DEFAULT_PORT_W0_MACID);
60990aac0d8SBjoern A. Zeeb 
61090aac0d8SBjoern A. Zeeb 	rtw_fw_send_h2c_command_register(rtwdev, &h2c);
61190aac0d8SBjoern A. Zeeb }
61290aac0d8SBjoern A. Zeeb 
6132774f206SBjoern A. Zeeb void rtw_fw_wl_ch_info(struct rtw_dev *rtwdev, u8 link, u8 ch, u8 bw)
6142774f206SBjoern A. Zeeb {
6152774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6162774f206SBjoern A. Zeeb 
6172774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WL_CH_INFO);
6182774f206SBjoern A. Zeeb 
6192774f206SBjoern A. Zeeb 	SET_WL_CH_INFO_LINK(h2c_pkt, link);
6202774f206SBjoern A. Zeeb 	SET_WL_CH_INFO_CHNL(h2c_pkt, ch);
6212774f206SBjoern A. Zeeb 	SET_WL_CH_INFO_BW(h2c_pkt, bw);
6222774f206SBjoern A. Zeeb 
6232774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
6242774f206SBjoern A. Zeeb }
6252774f206SBjoern A. Zeeb 
6262774f206SBjoern A. Zeeb void rtw_fw_query_bt_mp_info(struct rtw_dev *rtwdev,
6272774f206SBjoern A. Zeeb 			     struct rtw_coex_info_req *req)
6282774f206SBjoern A. Zeeb {
6292774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6302774f206SBjoern A. Zeeb 
6312774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_MP_INFO);
6322774f206SBjoern A. Zeeb 
6332774f206SBjoern A. Zeeb 	SET_BT_MP_INFO_SEQ(h2c_pkt, req->seq);
6342774f206SBjoern A. Zeeb 	SET_BT_MP_INFO_OP_CODE(h2c_pkt, req->op_code);
6352774f206SBjoern A. Zeeb 	SET_BT_MP_INFO_PARA1(h2c_pkt, req->para1);
6362774f206SBjoern A. Zeeb 	SET_BT_MP_INFO_PARA2(h2c_pkt, req->para2);
6372774f206SBjoern A. Zeeb 	SET_BT_MP_INFO_PARA3(h2c_pkt, req->para3);
6382774f206SBjoern A. Zeeb 
6392774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
6402774f206SBjoern A. Zeeb }
6412774f206SBjoern A. Zeeb 
6422774f206SBjoern A. Zeeb void rtw_fw_force_bt_tx_power(struct rtw_dev *rtwdev, u8 bt_pwr_dec_lvl)
6432774f206SBjoern A. Zeeb {
6442774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6452774f206SBjoern A. Zeeb 	u8 index = 0 - bt_pwr_dec_lvl;
6462774f206SBjoern A. Zeeb 
6472774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_FORCE_BT_TX_POWER);
6482774f206SBjoern A. Zeeb 
6492774f206SBjoern A. Zeeb 	SET_BT_TX_POWER_INDEX(h2c_pkt, index);
6502774f206SBjoern A. Zeeb 
6512774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
6522774f206SBjoern A. Zeeb }
6532774f206SBjoern A. Zeeb 
6542774f206SBjoern A. Zeeb void rtw_fw_bt_ignore_wlan_action(struct rtw_dev *rtwdev, bool enable)
6552774f206SBjoern A. Zeeb {
6562774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6572774f206SBjoern A. Zeeb 
6582774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_IGNORE_WLAN_ACTION);
6592774f206SBjoern A. Zeeb 
6602774f206SBjoern A. Zeeb 	SET_IGNORE_WLAN_ACTION_EN(h2c_pkt, enable);
6612774f206SBjoern A. Zeeb 
6622774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
6632774f206SBjoern A. Zeeb }
6642774f206SBjoern A. Zeeb 
6652774f206SBjoern A. Zeeb void rtw_fw_coex_tdma_type(struct rtw_dev *rtwdev,
6662774f206SBjoern A. Zeeb 			   u8 para1, u8 para2, u8 para3, u8 para4, u8 para5)
6672774f206SBjoern A. Zeeb {
6682774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6692774f206SBjoern A. Zeeb 
6702774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_COEX_TDMA_TYPE);
6712774f206SBjoern A. Zeeb 
6722774f206SBjoern A. Zeeb 	SET_COEX_TDMA_TYPE_PARA1(h2c_pkt, para1);
6732774f206SBjoern A. Zeeb 	SET_COEX_TDMA_TYPE_PARA2(h2c_pkt, para2);
6742774f206SBjoern A. Zeeb 	SET_COEX_TDMA_TYPE_PARA3(h2c_pkt, para3);
6752774f206SBjoern A. Zeeb 	SET_COEX_TDMA_TYPE_PARA4(h2c_pkt, para4);
6762774f206SBjoern A. Zeeb 	SET_COEX_TDMA_TYPE_PARA5(h2c_pkt, para5);
6772774f206SBjoern A. Zeeb 
6782774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
6792774f206SBjoern A. Zeeb }
6802774f206SBjoern A. Zeeb 
6819c951734SBjoern A. Zeeb void rtw_fw_coex_query_hid_info(struct rtw_dev *rtwdev, u8 sub_id, u8 data)
6829c951734SBjoern A. Zeeb {
6839c951734SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6849c951734SBjoern A. Zeeb 
6859c951734SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_HID_INFO);
6869c951734SBjoern A. Zeeb 
6879c951734SBjoern A. Zeeb 	SET_COEX_QUERY_HID_INFO_SUBID(h2c_pkt, sub_id);
6889c951734SBjoern A. Zeeb 	SET_COEX_QUERY_HID_INFO_DATA1(h2c_pkt, data);
6899c951734SBjoern A. Zeeb 
6909c951734SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
6919c951734SBjoern A. Zeeb }
6929c951734SBjoern A. Zeeb 
6932774f206SBjoern A. Zeeb void rtw_fw_bt_wifi_control(struct rtw_dev *rtwdev, u8 op_code, u8 *data)
6942774f206SBjoern A. Zeeb {
6952774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
6962774f206SBjoern A. Zeeb 
6972774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BT_WIFI_CONTROL);
6982774f206SBjoern A. Zeeb 
6992774f206SBjoern A. Zeeb 	SET_BT_WIFI_CONTROL_OP_CODE(h2c_pkt, op_code);
7002774f206SBjoern A. Zeeb 
7012774f206SBjoern A. Zeeb 	SET_BT_WIFI_CONTROL_DATA1(h2c_pkt, *data);
7022774f206SBjoern A. Zeeb 	SET_BT_WIFI_CONTROL_DATA2(h2c_pkt, *(data + 1));
7032774f206SBjoern A. Zeeb 	SET_BT_WIFI_CONTROL_DATA3(h2c_pkt, *(data + 2));
7042774f206SBjoern A. Zeeb 	SET_BT_WIFI_CONTROL_DATA4(h2c_pkt, *(data + 3));
7052774f206SBjoern A. Zeeb 	SET_BT_WIFI_CONTROL_DATA5(h2c_pkt, *(data + 4));
7062774f206SBjoern A. Zeeb 
7072774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
7082774f206SBjoern A. Zeeb }
7092774f206SBjoern A. Zeeb 
7102774f206SBjoern A. Zeeb void rtw_fw_send_rssi_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si)
7112774f206SBjoern A. Zeeb {
7122774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
7132774f206SBjoern A. Zeeb 	u8 rssi = ewma_rssi_read(&si->avg_rssi);
7142774f206SBjoern A. Zeeb 	bool stbc_en = si->stbc_en ? true : false;
7152774f206SBjoern A. Zeeb 
7162774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RSSI_MONITOR);
7172774f206SBjoern A. Zeeb 
7182774f206SBjoern A. Zeeb 	SET_RSSI_INFO_MACID(h2c_pkt, si->mac_id);
7192774f206SBjoern A. Zeeb 	SET_RSSI_INFO_RSSI(h2c_pkt, rssi);
7202774f206SBjoern A. Zeeb 	SET_RSSI_INFO_STBC(h2c_pkt, stbc_en);
7212774f206SBjoern A. Zeeb 
7222774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
7232774f206SBjoern A. Zeeb }
7242774f206SBjoern A. Zeeb 
7259c951734SBjoern A. Zeeb void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si,
7269c951734SBjoern A. Zeeb 			 bool reset_ra_mask)
7272774f206SBjoern A. Zeeb {
7282774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
7292774f206SBjoern A. Zeeb 	bool disable_pt = true;
7302774f206SBjoern A. Zeeb 
7312774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RA_INFO);
7322774f206SBjoern A. Zeeb 
7332774f206SBjoern A. Zeeb 	SET_RA_INFO_MACID(h2c_pkt, si->mac_id);
7342774f206SBjoern A. Zeeb 	SET_RA_INFO_RATE_ID(h2c_pkt, si->rate_id);
7352774f206SBjoern A. Zeeb 	SET_RA_INFO_INIT_RA_LVL(h2c_pkt, si->init_ra_lv);
7362774f206SBjoern A. Zeeb 	SET_RA_INFO_SGI_EN(h2c_pkt, si->sgi_enable);
7372774f206SBjoern A. Zeeb 	SET_RA_INFO_BW_MODE(h2c_pkt, si->bw_mode);
7382774f206SBjoern A. Zeeb 	SET_RA_INFO_LDPC(h2c_pkt, !!si->ldpc_en);
7399c951734SBjoern A. Zeeb 	SET_RA_INFO_NO_UPDATE(h2c_pkt, !reset_ra_mask);
7402774f206SBjoern A. Zeeb 	SET_RA_INFO_VHT_EN(h2c_pkt, si->vht_enable);
7412774f206SBjoern A. Zeeb 	SET_RA_INFO_DIS_PT(h2c_pkt, disable_pt);
7422774f206SBjoern A. Zeeb 	SET_RA_INFO_RA_MASK0(h2c_pkt, (si->ra_mask & 0xff));
7432774f206SBjoern A. Zeeb 	SET_RA_INFO_RA_MASK1(h2c_pkt, (si->ra_mask & 0xff00) >> 8);
7442774f206SBjoern A. Zeeb 	SET_RA_INFO_RA_MASK2(h2c_pkt, (si->ra_mask & 0xff0000) >> 16);
7452774f206SBjoern A. Zeeb 	SET_RA_INFO_RA_MASK3(h2c_pkt, (si->ra_mask & 0xff000000) >> 24);
7462774f206SBjoern A. Zeeb 
7472774f206SBjoern A. Zeeb 	si->init_ra_lv = 0;
7482774f206SBjoern A. Zeeb 
7492774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
7502774f206SBjoern A. Zeeb }
7512774f206SBjoern A. Zeeb 
7522774f206SBjoern A. Zeeb void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool connect)
7532774f206SBjoern A. Zeeb {
7542774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
7552774f206SBjoern A. Zeeb 
7562774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_MEDIA_STATUS_RPT);
7572774f206SBjoern A. Zeeb 	MEDIA_STATUS_RPT_SET_OP_MODE(h2c_pkt, connect);
7582774f206SBjoern A. Zeeb 	MEDIA_STATUS_RPT_SET_MACID(h2c_pkt, mac_id);
7592774f206SBjoern A. Zeeb 
7602774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
7612774f206SBjoern A. Zeeb }
7622774f206SBjoern A. Zeeb 
7632774f206SBjoern A. Zeeb void rtw_fw_update_wl_phy_info(struct rtw_dev *rtwdev)
7642774f206SBjoern A. Zeeb {
7652774f206SBjoern A. Zeeb 	struct rtw_traffic_stats *stats = &rtwdev->stats;
7662774f206SBjoern A. Zeeb 	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
7672774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
7682774f206SBjoern A. Zeeb 
7692774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WL_PHY_INFO);
7702774f206SBjoern A. Zeeb 	SET_WL_PHY_INFO_TX_TP(h2c_pkt, stats->tx_throughput);
7712774f206SBjoern A. Zeeb 	SET_WL_PHY_INFO_RX_TP(h2c_pkt, stats->rx_throughput);
7722774f206SBjoern A. Zeeb 	SET_WL_PHY_INFO_TX_RATE_DESC(h2c_pkt, dm_info->tx_rate);
7732774f206SBjoern A. Zeeb 	SET_WL_PHY_INFO_RX_RATE_DESC(h2c_pkt, dm_info->curr_rx_rate);
7742774f206SBjoern A. Zeeb 	SET_WL_PHY_INFO_RX_EVM(h2c_pkt, dm_info->rx_evm_dbm[RF_PATH_A]);
7752774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
7762774f206SBjoern A. Zeeb }
7772774f206SBjoern A. Zeeb 
7782774f206SBjoern A. Zeeb void rtw_fw_beacon_filter_config(struct rtw_dev *rtwdev, bool connect,
7792774f206SBjoern A. Zeeb 				 struct ieee80211_vif *vif)
7802774f206SBjoern A. Zeeb {
7812774f206SBjoern A. Zeeb 	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
7822774f206SBjoern A. Zeeb 	struct ieee80211_sta *sta = ieee80211_find_sta(vif, bss_conf->bssid);
7832774f206SBjoern A. Zeeb 	static const u8 rssi_min = 0, rssi_max = 100, rssi_offset = 100;
7842774f206SBjoern A. Zeeb 	struct rtw_sta_info *si =
7852774f206SBjoern A. Zeeb 		sta ? (struct rtw_sta_info *)sta->drv_priv : NULL;
786*11c53278SBjoern A. Zeeb 	s32 thold = RTW_DEFAULT_CQM_THOLD;
787*11c53278SBjoern A. Zeeb 	u32 hyst = RTW_DEFAULT_CQM_HYST;
7882774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
7892774f206SBjoern A. Zeeb 
7909c951734SBjoern A. Zeeb 	if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_BCN_FILTER))
7912774f206SBjoern A. Zeeb 		return;
7922774f206SBjoern A. Zeeb 
793*11c53278SBjoern A. Zeeb 	if (bss_conf->cqm_rssi_thold)
794*11c53278SBjoern A. Zeeb 		thold = bss_conf->cqm_rssi_thold;
795*11c53278SBjoern A. Zeeb 	if (bss_conf->cqm_rssi_hyst)
796*11c53278SBjoern A. Zeeb 		hyst = bss_conf->cqm_rssi_hyst;
797*11c53278SBjoern A. Zeeb 
7982774f206SBjoern A. Zeeb 	if (!connect) {
7992774f206SBjoern A. Zeeb 		SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BCN_FILTER_OFFLOAD_P1);
8002774f206SBjoern A. Zeeb 		SET_BCN_FILTER_OFFLOAD_P1_ENABLE(h2c_pkt, connect);
8012774f206SBjoern A. Zeeb 		rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
8022774f206SBjoern A. Zeeb 
8032774f206SBjoern A. Zeeb 		return;
8042774f206SBjoern A. Zeeb 	}
8059c951734SBjoern A. Zeeb 
8069c951734SBjoern A. Zeeb 	if (!si)
8079c951734SBjoern A. Zeeb 		return;
8089c951734SBjoern A. Zeeb 
8092774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BCN_FILTER_OFFLOAD_P0);
8102774f206SBjoern A. Zeeb 	ether_addr_copy(&h2c_pkt[1], bss_conf->bssid);
8112774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
8122774f206SBjoern A. Zeeb 
8132774f206SBjoern A. Zeeb 	memset(h2c_pkt, 0, sizeof(h2c_pkt));
814*11c53278SBjoern A. Zeeb 	thold = clamp_t(s32, thold + rssi_offset, rssi_min, rssi_max);
8152774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BCN_FILTER_OFFLOAD_P1);
8162774f206SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_ENABLE(h2c_pkt, connect);
8172774f206SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_OFFLOAD_MODE(h2c_pkt,
8182774f206SBjoern A. Zeeb 					       BCN_FILTER_OFFLOAD_MODE_DEFAULT);
819*11c53278SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_THRESHOLD(h2c_pkt, thold);
8202774f206SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_BCN_LOSS_CNT(h2c_pkt, BCN_LOSS_CNT);
8212774f206SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_MACID(h2c_pkt, si->mac_id);
822*11c53278SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_HYST(h2c_pkt, hyst);
8232774f206SBjoern A. Zeeb 	SET_BCN_FILTER_OFFLOAD_P1_BCN_INTERVAL(h2c_pkt, bss_conf->beacon_int);
8242774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
8252774f206SBjoern A. Zeeb }
8262774f206SBjoern A. Zeeb 
8272774f206SBjoern A. Zeeb void rtw_fw_set_pwr_mode(struct rtw_dev *rtwdev)
8282774f206SBjoern A. Zeeb {
8292774f206SBjoern A. Zeeb 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
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_SET_PWR_MODE);
8332774f206SBjoern A. Zeeb 
8342774f206SBjoern A. Zeeb 	SET_PWR_MODE_SET_MODE(h2c_pkt, conf->mode);
8352774f206SBjoern A. Zeeb 	SET_PWR_MODE_SET_RLBM(h2c_pkt, conf->rlbm);
8362774f206SBjoern A. Zeeb 	SET_PWR_MODE_SET_SMART_PS(h2c_pkt, conf->smart_ps);
8372774f206SBjoern A. Zeeb 	SET_PWR_MODE_SET_AWAKE_INTERVAL(h2c_pkt, conf->awake_interval);
8382774f206SBjoern A. Zeeb 	SET_PWR_MODE_SET_PORT_ID(h2c_pkt, conf->port_id);
8392774f206SBjoern A. Zeeb 	SET_PWR_MODE_SET_PWR_STATE(h2c_pkt, conf->state);
8402774f206SBjoern A. Zeeb 
8412774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
8422774f206SBjoern A. Zeeb }
8432774f206SBjoern A. Zeeb 
8442774f206SBjoern A. Zeeb void rtw_fw_set_keep_alive_cmd(struct rtw_dev *rtwdev, bool enable)
8452774f206SBjoern A. Zeeb {
8462774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
8472774f206SBjoern A. Zeeb 	struct rtw_fw_wow_keep_alive_para mode = {
8482774f206SBjoern A. Zeeb 		.adopt = true,
8492774f206SBjoern A. Zeeb 		.pkt_type = KEEP_ALIVE_NULL_PKT,
8502774f206SBjoern A. Zeeb 		.period = 5,
8512774f206SBjoern A. Zeeb 	};
8522774f206SBjoern A. Zeeb 
8532774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_KEEP_ALIVE);
8542774f206SBjoern A. Zeeb 	SET_KEEP_ALIVE_ENABLE(h2c_pkt, enable);
8552774f206SBjoern A. Zeeb 	SET_KEEP_ALIVE_ADOPT(h2c_pkt, mode.adopt);
8562774f206SBjoern A. Zeeb 	SET_KEEP_ALIVE_PKT_TYPE(h2c_pkt, mode.pkt_type);
8572774f206SBjoern A. Zeeb 	SET_KEEP_ALIVE_CHECK_PERIOD(h2c_pkt, mode.period);
8582774f206SBjoern A. Zeeb 
8592774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
8602774f206SBjoern A. Zeeb }
8612774f206SBjoern A. Zeeb 
8622774f206SBjoern A. Zeeb void rtw_fw_set_disconnect_decision_cmd(struct rtw_dev *rtwdev, bool enable)
8632774f206SBjoern A. Zeeb {
8642774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
8652774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
8662774f206SBjoern A. Zeeb 	struct rtw_fw_wow_disconnect_para mode = {
8672774f206SBjoern A. Zeeb 		.adopt = true,
8682774f206SBjoern A. Zeeb 		.period = 30,
8692774f206SBjoern A. Zeeb 		.retry_count = 5,
8702774f206SBjoern A. Zeeb 	};
8712774f206SBjoern A. Zeeb 
8722774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_DISCONNECT_DECISION);
8732774f206SBjoern A. Zeeb 
8742774f206SBjoern A. Zeeb 	if (test_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) {
8752774f206SBjoern A. Zeeb 		SET_DISCONNECT_DECISION_ENABLE(h2c_pkt, enable);
8762774f206SBjoern A. Zeeb 		SET_DISCONNECT_DECISION_ADOPT(h2c_pkt, mode.adopt);
8772774f206SBjoern A. Zeeb 		SET_DISCONNECT_DECISION_CHECK_PERIOD(h2c_pkt, mode.period);
8782774f206SBjoern A. Zeeb 		SET_DISCONNECT_DECISION_TRY_PKT_NUM(h2c_pkt, mode.retry_count);
8792774f206SBjoern A. Zeeb 	}
8802774f206SBjoern A. Zeeb 
8812774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
8822774f206SBjoern A. Zeeb }
8832774f206SBjoern A. Zeeb 
8842774f206SBjoern A. Zeeb void rtw_fw_set_wowlan_ctrl_cmd(struct rtw_dev *rtwdev, bool enable)
8852774f206SBjoern A. Zeeb {
8862774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
8872774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
8882774f206SBjoern A. Zeeb 
8892774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WOWLAN);
8902774f206SBjoern A. Zeeb 
8912774f206SBjoern A. Zeeb 	SET_WOWLAN_FUNC_ENABLE(h2c_pkt, enable);
8922774f206SBjoern A. Zeeb 	if (rtw_wow_mgd_linked(rtwdev)) {
8932774f206SBjoern A. Zeeb 		if (test_bit(RTW_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags))
8942774f206SBjoern A. Zeeb 			SET_WOWLAN_MAGIC_PKT_ENABLE(h2c_pkt, enable);
8952774f206SBjoern A. Zeeb 		if (test_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags))
8962774f206SBjoern A. Zeeb 			SET_WOWLAN_DEAUTH_WAKEUP_ENABLE(h2c_pkt, enable);
8972774f206SBjoern A. Zeeb 		if (test_bit(RTW_WOW_FLAG_EN_REKEY_PKT, rtw_wow->flags))
8982774f206SBjoern A. Zeeb 			SET_WOWLAN_REKEY_WAKEUP_ENABLE(h2c_pkt, enable);
8992774f206SBjoern A. Zeeb 		if (rtw_wow->pattern_cnt)
9002774f206SBjoern A. Zeeb 			SET_WOWLAN_PATTERN_MATCH_ENABLE(h2c_pkt, enable);
9012774f206SBjoern A. Zeeb 	}
9022774f206SBjoern A. Zeeb 
9032774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
9042774f206SBjoern A. Zeeb }
9052774f206SBjoern A. Zeeb 
9062774f206SBjoern A. Zeeb void rtw_fw_set_aoac_global_info_cmd(struct rtw_dev *rtwdev,
9072774f206SBjoern A. Zeeb 				     u8 pairwise_key_enc,
9082774f206SBjoern A. Zeeb 				     u8 group_key_enc)
9092774f206SBjoern A. Zeeb {
9102774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
9112774f206SBjoern A. Zeeb 
9122774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_AOAC_GLOBAL_INFO);
9132774f206SBjoern A. Zeeb 
9142774f206SBjoern A. Zeeb 	SET_AOAC_GLOBAL_INFO_PAIRWISE_ENC_ALG(h2c_pkt, pairwise_key_enc);
9152774f206SBjoern A. Zeeb 	SET_AOAC_GLOBAL_INFO_GROUP_ENC_ALG(h2c_pkt, group_key_enc);
9162774f206SBjoern A. Zeeb 
9172774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
9182774f206SBjoern A. Zeeb }
9192774f206SBjoern A. Zeeb 
9202774f206SBjoern A. Zeeb void rtw_fw_set_remote_wake_ctrl_cmd(struct rtw_dev *rtwdev, bool enable)
9212774f206SBjoern A. Zeeb {
9222774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
9232774f206SBjoern A. Zeeb 
9242774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_REMOTE_WAKE_CTRL);
9252774f206SBjoern A. Zeeb 
9262774f206SBjoern A. Zeeb 	SET_REMOTE_WAKECTRL_ENABLE(h2c_pkt, enable);
9272774f206SBjoern A. Zeeb 
9282774f206SBjoern A. Zeeb 	if (rtw_wow_no_link(rtwdev))
9292774f206SBjoern A. Zeeb 		SET_REMOTE_WAKE_CTRL_NLO_OFFLOAD_EN(h2c_pkt, enable);
9302774f206SBjoern A. Zeeb 
9312774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
9322774f206SBjoern A. Zeeb }
9332774f206SBjoern A. Zeeb 
9342774f206SBjoern A. Zeeb static u8 rtw_get_rsvd_page_location(struct rtw_dev *rtwdev,
9352774f206SBjoern A. Zeeb 				     enum rtw_rsvd_packet_type type)
9362774f206SBjoern A. Zeeb {
9372774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
9382774f206SBjoern A. Zeeb 	u8 location = 0;
9392774f206SBjoern A. Zeeb 
9402774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) {
9412774f206SBjoern A. Zeeb 		if (type == rsvd_pkt->type)
9422774f206SBjoern A. Zeeb 			location = rsvd_pkt->page;
9432774f206SBjoern A. Zeeb 	}
9442774f206SBjoern A. Zeeb 
9452774f206SBjoern A. Zeeb 	return location;
9462774f206SBjoern A. Zeeb }
9472774f206SBjoern A. Zeeb 
9482774f206SBjoern A. Zeeb void rtw_fw_set_nlo_info(struct rtw_dev *rtwdev, bool enable)
9492774f206SBjoern A. Zeeb {
9502774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
9512774f206SBjoern A. Zeeb 	u8 loc_nlo;
9522774f206SBjoern A. Zeeb 
9532774f206SBjoern A. Zeeb 	loc_nlo = rtw_get_rsvd_page_location(rtwdev, RSVD_NLO_INFO);
9542774f206SBjoern A. Zeeb 
9552774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_NLO_INFO);
9562774f206SBjoern A. Zeeb 
9572774f206SBjoern A. Zeeb 	SET_NLO_FUN_EN(h2c_pkt, enable);
9582774f206SBjoern A. Zeeb 	if (enable) {
9592774f206SBjoern A. Zeeb 		if (rtw_get_lps_deep_mode(rtwdev) != LPS_DEEP_MODE_NONE)
9602774f206SBjoern A. Zeeb 			SET_NLO_PS_32K(h2c_pkt, enable);
9612774f206SBjoern A. Zeeb 		SET_NLO_IGNORE_SECURITY(h2c_pkt, enable);
9622774f206SBjoern A. Zeeb 		SET_NLO_LOC_NLO_INFO(h2c_pkt, loc_nlo);
9632774f206SBjoern A. Zeeb 	}
9642774f206SBjoern A. Zeeb 
9652774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
9662774f206SBjoern A. Zeeb }
9672774f206SBjoern A. Zeeb 
96890aac0d8SBjoern A. Zeeb void rtw_fw_set_recover_bt_device(struct rtw_dev *rtwdev)
96990aac0d8SBjoern A. Zeeb {
97090aac0d8SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
97190aac0d8SBjoern A. Zeeb 
97290aac0d8SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RECOVER_BT_DEV);
97390aac0d8SBjoern A. Zeeb 	SET_RECOVER_BT_DEV_EN(h2c_pkt, 1);
97490aac0d8SBjoern A. Zeeb 
97590aac0d8SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
97690aac0d8SBjoern A. Zeeb }
97790aac0d8SBjoern A. Zeeb 
9782774f206SBjoern A. Zeeb void rtw_fw_set_pg_info(struct rtw_dev *rtwdev)
9792774f206SBjoern A. Zeeb {
9802774f206SBjoern A. Zeeb 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
9812774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
9822774f206SBjoern A. Zeeb 	u8 loc_pg, loc_dpk;
9832774f206SBjoern A. Zeeb 
9842774f206SBjoern A. Zeeb 	loc_pg = rtw_get_rsvd_page_location(rtwdev, RSVD_LPS_PG_INFO);
9852774f206SBjoern A. Zeeb 	loc_dpk = rtw_get_rsvd_page_location(rtwdev, RSVD_LPS_PG_DPK);
9862774f206SBjoern A. Zeeb 
9872774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_LPS_PG_INFO);
9882774f206SBjoern A. Zeeb 
9892774f206SBjoern A. Zeeb 	LPS_PG_INFO_LOC(h2c_pkt, loc_pg);
9902774f206SBjoern A. Zeeb 	LPS_PG_DPK_LOC(h2c_pkt, loc_dpk);
9912774f206SBjoern A. Zeeb 	LPS_PG_SEC_CAM_EN(h2c_pkt, conf->sec_cam_backup);
9922774f206SBjoern A. Zeeb 	LPS_PG_PATTERN_CAM_EN(h2c_pkt, conf->pattern_cam_backup);
9932774f206SBjoern A. Zeeb 
9942774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
9952774f206SBjoern A. Zeeb }
9962774f206SBjoern A. Zeeb 
9972774f206SBjoern A. Zeeb static u8 rtw_get_rsvd_page_probe_req_location(struct rtw_dev *rtwdev,
9982774f206SBjoern A. Zeeb 					       struct cfg80211_ssid *ssid)
9992774f206SBjoern A. Zeeb {
10002774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
10012774f206SBjoern A. Zeeb 	u8 location = 0;
10022774f206SBjoern A. Zeeb 
10032774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) {
10042774f206SBjoern A. Zeeb 		if (rsvd_pkt->type != RSVD_PROBE_REQ)
10052774f206SBjoern A. Zeeb 			continue;
10062774f206SBjoern A. Zeeb 		if ((!ssid && !rsvd_pkt->ssid) ||
1007*11c53278SBjoern A. Zeeb 		    cfg80211_ssid_eq(rsvd_pkt->ssid, ssid))
10082774f206SBjoern A. Zeeb 			location = rsvd_pkt->page;
10092774f206SBjoern A. Zeeb 	}
10102774f206SBjoern A. Zeeb 
10112774f206SBjoern A. Zeeb 	return location;
10122774f206SBjoern A. Zeeb }
10132774f206SBjoern A. Zeeb 
10142774f206SBjoern A. Zeeb static u16 rtw_get_rsvd_page_probe_req_size(struct rtw_dev *rtwdev,
10152774f206SBjoern A. Zeeb 					    struct cfg80211_ssid *ssid)
10162774f206SBjoern A. Zeeb {
10172774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
10182774f206SBjoern A. Zeeb 	u16 size = 0;
10192774f206SBjoern A. Zeeb 
10202774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) {
10212774f206SBjoern A. Zeeb 		if (rsvd_pkt->type != RSVD_PROBE_REQ)
10222774f206SBjoern A. Zeeb 			continue;
10232774f206SBjoern A. Zeeb 		if ((!ssid && !rsvd_pkt->ssid) ||
1024*11c53278SBjoern A. Zeeb 		    cfg80211_ssid_eq(rsvd_pkt->ssid, ssid))
10252774f206SBjoern A. Zeeb 			size = rsvd_pkt->probe_req_size;
10262774f206SBjoern A. Zeeb 	}
10272774f206SBjoern A. Zeeb 
10282774f206SBjoern A. Zeeb 	return size;
10292774f206SBjoern A. Zeeb }
10302774f206SBjoern A. Zeeb 
10312774f206SBjoern A. Zeeb void rtw_send_rsvd_page_h2c(struct rtw_dev *rtwdev)
10322774f206SBjoern A. Zeeb {
10332774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
10342774f206SBjoern A. Zeeb 	u8 location = 0;
10352774f206SBjoern A. Zeeb 
10362774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RSVD_PAGE);
10372774f206SBjoern A. Zeeb 
10382774f206SBjoern A. Zeeb 	location = rtw_get_rsvd_page_location(rtwdev, RSVD_PROBE_RESP);
10392774f206SBjoern A. Zeeb 	*(h2c_pkt + 1) = location;
10402774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_PROBE_RESP loc: %d\n", location);
10412774f206SBjoern A. Zeeb 
10422774f206SBjoern A. Zeeb 	location = rtw_get_rsvd_page_location(rtwdev, RSVD_PS_POLL);
10432774f206SBjoern A. Zeeb 	*(h2c_pkt + 2) = location;
10442774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_PS_POLL loc: %d\n", location);
10452774f206SBjoern A. Zeeb 
10462774f206SBjoern A. Zeeb 	location = rtw_get_rsvd_page_location(rtwdev, RSVD_NULL);
10472774f206SBjoern A. Zeeb 	*(h2c_pkt + 3) = location;
10482774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_NULL loc: %d\n", location);
10492774f206SBjoern A. Zeeb 
10502774f206SBjoern A. Zeeb 	location = rtw_get_rsvd_page_location(rtwdev, RSVD_QOS_NULL);
10512774f206SBjoern A. Zeeb 	*(h2c_pkt + 4) = location;
10522774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_QOS_NULL loc: %d\n", location);
10532774f206SBjoern A. Zeeb 
10542774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
10552774f206SBjoern A. Zeeb }
10562774f206SBjoern A. Zeeb 
10572774f206SBjoern A. Zeeb static struct sk_buff *rtw_nlo_info_get(struct ieee80211_hw *hw)
10582774f206SBjoern A. Zeeb {
10592774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = hw->priv;
106090aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
10612774f206SBjoern A. Zeeb 	struct rtw_pno_request *pno_req = &rtwdev->wow.pno_req;
10622774f206SBjoern A. Zeeb 	struct rtw_nlo_info_hdr *nlo_hdr;
10632774f206SBjoern A. Zeeb 	struct cfg80211_ssid *ssid;
10642774f206SBjoern A. Zeeb 	struct sk_buff *skb;
10652774f206SBjoern A. Zeeb 	u8 *pos, loc;
10662774f206SBjoern A. Zeeb 	u32 size;
10672774f206SBjoern A. Zeeb 	int i;
10682774f206SBjoern A. Zeeb 
10692774f206SBjoern A. Zeeb 	if (!pno_req->inited || !pno_req->match_set_cnt)
10702774f206SBjoern A. Zeeb 		return NULL;
10712774f206SBjoern A. Zeeb 
10722774f206SBjoern A. Zeeb 	size = sizeof(struct rtw_nlo_info_hdr) + pno_req->match_set_cnt *
10732774f206SBjoern A. Zeeb 		      IEEE80211_MAX_SSID_LEN + chip->tx_pkt_desc_sz;
10742774f206SBjoern A. Zeeb 
10752774f206SBjoern A. Zeeb 	skb = alloc_skb(size, GFP_KERNEL);
10762774f206SBjoern A. Zeeb 	if (!skb)
10772774f206SBjoern A. Zeeb 		return NULL;
10782774f206SBjoern A. Zeeb 
10792774f206SBjoern A. Zeeb 	skb_reserve(skb, chip->tx_pkt_desc_sz);
10802774f206SBjoern A. Zeeb 
10812774f206SBjoern A. Zeeb 	nlo_hdr = skb_put_zero(skb, sizeof(struct rtw_nlo_info_hdr));
10822774f206SBjoern A. Zeeb 
10832774f206SBjoern A. Zeeb 	nlo_hdr->nlo_count = pno_req->match_set_cnt;
10842774f206SBjoern A. Zeeb 	nlo_hdr->hidden_ap_count = pno_req->match_set_cnt;
10852774f206SBjoern A. Zeeb 
10862774f206SBjoern A. Zeeb 	/* pattern check for firmware */
10872774f206SBjoern A. Zeeb 	memset(nlo_hdr->pattern_check, 0xA5, FW_NLO_INFO_CHECK_SIZE);
10882774f206SBjoern A. Zeeb 
10892774f206SBjoern A. Zeeb 	for (i = 0; i < pno_req->match_set_cnt; i++)
10902774f206SBjoern A. Zeeb 		nlo_hdr->ssid_len[i] = pno_req->match_sets[i].ssid.ssid_len;
10912774f206SBjoern A. Zeeb 
10922774f206SBjoern A. Zeeb 	for (i = 0; i < pno_req->match_set_cnt; i++) {
10932774f206SBjoern A. Zeeb 		ssid = &pno_req->match_sets[i].ssid;
10942774f206SBjoern A. Zeeb 		loc  = rtw_get_rsvd_page_probe_req_location(rtwdev, ssid);
10952774f206SBjoern A. Zeeb 		if (!loc) {
10962774f206SBjoern A. Zeeb 			rtw_err(rtwdev, "failed to get probe req rsvd loc\n");
10972774f206SBjoern A. Zeeb 			kfree_skb(skb);
10982774f206SBjoern A. Zeeb 			return NULL;
10992774f206SBjoern A. Zeeb 		}
11002774f206SBjoern A. Zeeb 		nlo_hdr->location[i] = loc;
11012774f206SBjoern A. Zeeb 	}
11022774f206SBjoern A. Zeeb 
11032774f206SBjoern A. Zeeb 	for (i = 0; i < pno_req->match_set_cnt; i++) {
11042774f206SBjoern A. Zeeb 		pos = skb_put_zero(skb, IEEE80211_MAX_SSID_LEN);
11052774f206SBjoern A. Zeeb 		memcpy(pos, pno_req->match_sets[i].ssid.ssid,
11062774f206SBjoern A. Zeeb 		       pno_req->match_sets[i].ssid.ssid_len);
11072774f206SBjoern A. Zeeb 	}
11082774f206SBjoern A. Zeeb 
11092774f206SBjoern A. Zeeb 	return skb;
11102774f206SBjoern A. Zeeb }
11112774f206SBjoern A. Zeeb 
11122774f206SBjoern A. Zeeb static struct sk_buff *rtw_cs_channel_info_get(struct ieee80211_hw *hw)
11132774f206SBjoern A. Zeeb {
11142774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = hw->priv;
111590aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
11162774f206SBjoern A. Zeeb 	struct rtw_pno_request *pno_req = &rtwdev->wow.pno_req;
11172774f206SBjoern A. Zeeb 	struct ieee80211_channel *channels = pno_req->channels;
11182774f206SBjoern A. Zeeb 	struct sk_buff *skb;
11192774f206SBjoern A. Zeeb 	int count =  pno_req->channel_cnt;
11202774f206SBjoern A. Zeeb 	u8 *pos;
11212774f206SBjoern A. Zeeb 	int i = 0;
11222774f206SBjoern A. Zeeb 
11232774f206SBjoern A. Zeeb 	skb = alloc_skb(4 * count + chip->tx_pkt_desc_sz, GFP_KERNEL);
11242774f206SBjoern A. Zeeb 	if (!skb)
11252774f206SBjoern A. Zeeb 		return NULL;
11262774f206SBjoern A. Zeeb 
11272774f206SBjoern A. Zeeb 	skb_reserve(skb, chip->tx_pkt_desc_sz);
11282774f206SBjoern A. Zeeb 
11292774f206SBjoern A. Zeeb 	for (i = 0; i < count; i++) {
11302774f206SBjoern A. Zeeb 		pos = skb_put_zero(skb, 4);
11312774f206SBjoern A. Zeeb 
11322774f206SBjoern A. Zeeb 		CHSW_INFO_SET_CH(pos, channels[i].hw_value);
11332774f206SBjoern A. Zeeb 
11342774f206SBjoern A. Zeeb 		if (channels[i].flags & IEEE80211_CHAN_RADAR)
11352774f206SBjoern A. Zeeb 			CHSW_INFO_SET_ACTION_ID(pos, 0);
11362774f206SBjoern A. Zeeb 		else
11372774f206SBjoern A. Zeeb 			CHSW_INFO_SET_ACTION_ID(pos, 1);
11382774f206SBjoern A. Zeeb 		CHSW_INFO_SET_TIMEOUT(pos, 1);
11392774f206SBjoern A. Zeeb 		CHSW_INFO_SET_PRI_CH_IDX(pos, 1);
11402774f206SBjoern A. Zeeb 		CHSW_INFO_SET_BW(pos, 0);
11412774f206SBjoern A. Zeeb 	}
11422774f206SBjoern A. Zeeb 
11432774f206SBjoern A. Zeeb 	return skb;
11442774f206SBjoern A. Zeeb }
11452774f206SBjoern A. Zeeb 
11462774f206SBjoern A. Zeeb static struct sk_buff *rtw_lps_pg_dpk_get(struct ieee80211_hw *hw)
11472774f206SBjoern A. Zeeb {
11482774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = hw->priv;
114990aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
11502774f206SBjoern A. Zeeb 	struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info;
11512774f206SBjoern A. Zeeb 	struct rtw_lps_pg_dpk_hdr *dpk_hdr;
11522774f206SBjoern A. Zeeb 	struct sk_buff *skb;
11532774f206SBjoern A. Zeeb 	u32 size;
11542774f206SBjoern A. Zeeb 
11552774f206SBjoern A. Zeeb 	size = chip->tx_pkt_desc_sz + sizeof(*dpk_hdr);
11562774f206SBjoern A. Zeeb 	skb = alloc_skb(size, GFP_KERNEL);
11572774f206SBjoern A. Zeeb 	if (!skb)
11582774f206SBjoern A. Zeeb 		return NULL;
11592774f206SBjoern A. Zeeb 
11602774f206SBjoern A. Zeeb 	skb_reserve(skb, chip->tx_pkt_desc_sz);
11612774f206SBjoern A. Zeeb 	dpk_hdr = skb_put_zero(skb, sizeof(*dpk_hdr));
11622774f206SBjoern A. Zeeb 	dpk_hdr->dpk_ch = dpk_info->dpk_ch;
11632774f206SBjoern A. Zeeb 	dpk_hdr->dpk_path_ok = dpk_info->dpk_path_ok[0];
11642774f206SBjoern A. Zeeb 	memcpy(dpk_hdr->dpk_txagc, dpk_info->dpk_txagc, 2);
11652774f206SBjoern A. Zeeb 	memcpy(dpk_hdr->dpk_gs, dpk_info->dpk_gs, 4);
11662774f206SBjoern A. Zeeb 	memcpy(dpk_hdr->coef, dpk_info->coef, 160);
11672774f206SBjoern A. Zeeb 
11682774f206SBjoern A. Zeeb 	return skb;
11692774f206SBjoern A. Zeeb }
11702774f206SBjoern A. Zeeb 
11712774f206SBjoern A. Zeeb static struct sk_buff *rtw_lps_pg_info_get(struct ieee80211_hw *hw)
11722774f206SBjoern A. Zeeb {
11732774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = hw->priv;
117490aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
11752774f206SBjoern A. Zeeb 	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
11762774f206SBjoern A. Zeeb 	struct rtw_lps_pg_info_hdr *pg_info_hdr;
11772774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
11782774f206SBjoern A. Zeeb 	struct sk_buff *skb;
11792774f206SBjoern A. Zeeb 	u32 size;
11802774f206SBjoern A. Zeeb 
11812774f206SBjoern A. Zeeb 	size = chip->tx_pkt_desc_sz + sizeof(*pg_info_hdr);
11822774f206SBjoern A. Zeeb 	skb = alloc_skb(size, GFP_KERNEL);
11832774f206SBjoern A. Zeeb 	if (!skb)
11842774f206SBjoern A. Zeeb 		return NULL;
11852774f206SBjoern A. Zeeb 
11862774f206SBjoern A. Zeeb 	skb_reserve(skb, chip->tx_pkt_desc_sz);
11872774f206SBjoern A. Zeeb 	pg_info_hdr = skb_put_zero(skb, sizeof(*pg_info_hdr));
11882774f206SBjoern A. Zeeb 	pg_info_hdr->tx_bu_page_count = rtwdev->fifo.rsvd_drv_pg_num;
11892774f206SBjoern A. Zeeb 	pg_info_hdr->macid = find_first_bit(rtwdev->mac_id_map, RTW_MAX_MAC_ID_NUM);
11902774f206SBjoern A. Zeeb 	pg_info_hdr->sec_cam_count =
11912774f206SBjoern A. Zeeb 		rtw_sec_cam_pg_backup(rtwdev, pg_info_hdr->sec_cam);
11922774f206SBjoern A. Zeeb 	pg_info_hdr->pattern_count = rtw_wow->pattern_cnt;
11932774f206SBjoern A. Zeeb 
11942774f206SBjoern A. Zeeb 	conf->sec_cam_backup = pg_info_hdr->sec_cam_count != 0;
11952774f206SBjoern A. Zeeb 	conf->pattern_cam_backup = rtw_wow->pattern_cnt != 0;
11962774f206SBjoern A. Zeeb 
11972774f206SBjoern A. Zeeb 	return skb;
11982774f206SBjoern A. Zeeb }
11992774f206SBjoern A. Zeeb 
12002774f206SBjoern A. Zeeb static struct sk_buff *rtw_get_rsvd_page_skb(struct ieee80211_hw *hw,
12012774f206SBjoern A. Zeeb 					     struct rtw_rsvd_page *rsvd_pkt)
12022774f206SBjoern A. Zeeb {
12032774f206SBjoern A. Zeeb 	struct ieee80211_vif *vif;
12042774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif;
12052774f206SBjoern A. Zeeb 	struct sk_buff *skb_new;
12062774f206SBjoern A. Zeeb 	struct cfg80211_ssid *ssid;
12079c951734SBjoern A. Zeeb 	u16 tim_offset = 0;
12082774f206SBjoern A. Zeeb 
12092774f206SBjoern A. Zeeb 	if (rsvd_pkt->type == RSVD_DUMMY) {
12102774f206SBjoern A. Zeeb 		skb_new = alloc_skb(1, GFP_KERNEL);
12112774f206SBjoern A. Zeeb 		if (!skb_new)
12122774f206SBjoern A. Zeeb 			return NULL;
12132774f206SBjoern A. Zeeb 
12142774f206SBjoern A. Zeeb 		skb_put(skb_new, 1);
12152774f206SBjoern A. Zeeb 		return skb_new;
12162774f206SBjoern A. Zeeb 	}
12172774f206SBjoern A. Zeeb 
12182774f206SBjoern A. Zeeb 	rtwvif = rsvd_pkt->rtwvif;
12192774f206SBjoern A. Zeeb 	if (!rtwvif)
12202774f206SBjoern A. Zeeb 		return NULL;
12212774f206SBjoern A. Zeeb 
12222774f206SBjoern A. Zeeb 	vif = rtwvif_to_vif(rtwvif);
12232774f206SBjoern A. Zeeb 
12242774f206SBjoern A. Zeeb 	switch (rsvd_pkt->type) {
12252774f206SBjoern A. Zeeb 	case RSVD_BEACON:
1226549198b1SBjoern A. Zeeb 		skb_new = ieee80211_beacon_get_tim(hw, vif, &tim_offset, NULL, 0);
12279c951734SBjoern A. Zeeb 		rsvd_pkt->tim_offset = tim_offset;
12282774f206SBjoern A. Zeeb 		break;
12292774f206SBjoern A. Zeeb 	case RSVD_PS_POLL:
12302774f206SBjoern A. Zeeb 		skb_new = ieee80211_pspoll_get(hw, vif);
12312774f206SBjoern A. Zeeb 		break;
12322774f206SBjoern A. Zeeb 	case RSVD_PROBE_RESP:
12332774f206SBjoern A. Zeeb 		skb_new = ieee80211_proberesp_get(hw, vif);
12342774f206SBjoern A. Zeeb 		break;
12352774f206SBjoern A. Zeeb 	case RSVD_NULL:
123690aac0d8SBjoern A. Zeeb 		skb_new = ieee80211_nullfunc_get(hw, vif, -1, false);
12372774f206SBjoern A. Zeeb 		break;
12382774f206SBjoern A. Zeeb 	case RSVD_QOS_NULL:
123990aac0d8SBjoern A. Zeeb 		skb_new = ieee80211_nullfunc_get(hw, vif, -1, true);
12402774f206SBjoern A. Zeeb 		break;
12412774f206SBjoern A. Zeeb 	case RSVD_LPS_PG_DPK:
12422774f206SBjoern A. Zeeb 		skb_new = rtw_lps_pg_dpk_get(hw);
12432774f206SBjoern A. Zeeb 		break;
12442774f206SBjoern A. Zeeb 	case RSVD_LPS_PG_INFO:
12452774f206SBjoern A. Zeeb 		skb_new = rtw_lps_pg_info_get(hw);
12462774f206SBjoern A. Zeeb 		break;
12472774f206SBjoern A. Zeeb 	case RSVD_PROBE_REQ:
12482774f206SBjoern A. Zeeb 		ssid = (struct cfg80211_ssid *)rsvd_pkt->ssid;
12492774f206SBjoern A. Zeeb 		if (ssid)
12502774f206SBjoern A. Zeeb 			skb_new = ieee80211_probereq_get(hw, vif->addr,
12512774f206SBjoern A. Zeeb 							 ssid->ssid,
12522774f206SBjoern A. Zeeb 							 ssid->ssid_len, 0);
12532774f206SBjoern A. Zeeb 		else
12542774f206SBjoern A. Zeeb 			skb_new = ieee80211_probereq_get(hw, vif->addr, NULL, 0, 0);
12552774f206SBjoern A. Zeeb 		if (skb_new)
12562774f206SBjoern A. Zeeb 			rsvd_pkt->probe_req_size = (u16)skb_new->len;
12572774f206SBjoern A. Zeeb 		break;
12582774f206SBjoern A. Zeeb 	case RSVD_NLO_INFO:
12592774f206SBjoern A. Zeeb 		skb_new = rtw_nlo_info_get(hw);
12602774f206SBjoern A. Zeeb 		break;
12612774f206SBjoern A. Zeeb 	case RSVD_CH_INFO:
12622774f206SBjoern A. Zeeb 		skb_new = rtw_cs_channel_info_get(hw);
12632774f206SBjoern A. Zeeb 		break;
12642774f206SBjoern A. Zeeb 	default:
12652774f206SBjoern A. Zeeb 		return NULL;
12662774f206SBjoern A. Zeeb 	}
12672774f206SBjoern A. Zeeb 
12682774f206SBjoern A. Zeeb 	if (!skb_new)
12692774f206SBjoern A. Zeeb 		return NULL;
12702774f206SBjoern A. Zeeb 
12712774f206SBjoern A. Zeeb 	return skb_new;
12722774f206SBjoern A. Zeeb }
12732774f206SBjoern A. Zeeb 
12742774f206SBjoern A. Zeeb static void rtw_fill_rsvd_page_desc(struct rtw_dev *rtwdev, struct sk_buff *skb,
12752774f206SBjoern A. Zeeb 				    enum rtw_rsvd_packet_type type)
12762774f206SBjoern A. Zeeb {
12772774f206SBjoern A. Zeeb 	struct rtw_tx_pkt_info pkt_info = {0};
127890aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
12792774f206SBjoern A. Zeeb 	u8 *pkt_desc;
12802774f206SBjoern A. Zeeb 
12812774f206SBjoern A. Zeeb 	rtw_tx_rsvd_page_pkt_info_update(rtwdev, &pkt_info, skb, type);
12822774f206SBjoern A. Zeeb 	pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz);
12832774f206SBjoern A. Zeeb 	memset(pkt_desc, 0, chip->tx_pkt_desc_sz);
12842774f206SBjoern A. Zeeb 	rtw_tx_fill_tx_desc(&pkt_info, skb);
12852774f206SBjoern A. Zeeb }
12862774f206SBjoern A. Zeeb 
12872774f206SBjoern A. Zeeb static inline u8 rtw_len_to_page(unsigned int len, u8 page_size)
12882774f206SBjoern A. Zeeb {
12892774f206SBjoern A. Zeeb 	return DIV_ROUND_UP(len, page_size);
12902774f206SBjoern A. Zeeb }
12912774f206SBjoern A. Zeeb 
12922774f206SBjoern A. Zeeb static void rtw_rsvd_page_list_to_buf(struct rtw_dev *rtwdev, u8 page_size,
12932774f206SBjoern A. Zeeb 				      u8 page_margin, u32 page, u8 *buf,
12942774f206SBjoern A. Zeeb 				      struct rtw_rsvd_page *rsvd_pkt)
12952774f206SBjoern A. Zeeb {
12962774f206SBjoern A. Zeeb 	struct sk_buff *skb = rsvd_pkt->skb;
12972774f206SBjoern A. Zeeb 
12982774f206SBjoern A. Zeeb 	if (page >= 1)
12992774f206SBjoern A. Zeeb 		memcpy(buf + page_margin + page_size * (page - 1),
13002774f206SBjoern A. Zeeb 		       skb->data, skb->len);
13012774f206SBjoern A. Zeeb 	else
13022774f206SBjoern A. Zeeb 		memcpy(buf, skb->data, skb->len);
13032774f206SBjoern A. Zeeb }
13042774f206SBjoern A. Zeeb 
13052774f206SBjoern A. Zeeb static struct rtw_rsvd_page *rtw_alloc_rsvd_page(struct rtw_dev *rtwdev,
13062774f206SBjoern A. Zeeb 						 enum rtw_rsvd_packet_type type,
13072774f206SBjoern A. Zeeb 						 bool txdesc)
13082774f206SBjoern A. Zeeb {
13092774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt = NULL;
13102774f206SBjoern A. Zeeb 
13112774f206SBjoern A. Zeeb 	rsvd_pkt = kzalloc(sizeof(*rsvd_pkt), GFP_KERNEL);
13122774f206SBjoern A. Zeeb 
13132774f206SBjoern A. Zeeb 	if (!rsvd_pkt)
13142774f206SBjoern A. Zeeb 		return NULL;
13152774f206SBjoern A. Zeeb 
13162774f206SBjoern A. Zeeb 	INIT_LIST_HEAD(&rsvd_pkt->vif_list);
13172774f206SBjoern A. Zeeb 	INIT_LIST_HEAD(&rsvd_pkt->build_list);
13182774f206SBjoern A. Zeeb 	rsvd_pkt->type = type;
13192774f206SBjoern A. Zeeb 	rsvd_pkt->add_txdesc = txdesc;
13202774f206SBjoern A. Zeeb 
13212774f206SBjoern A. Zeeb 	return rsvd_pkt;
13222774f206SBjoern A. Zeeb }
13232774f206SBjoern A. Zeeb 
13242774f206SBjoern A. Zeeb static void rtw_insert_rsvd_page(struct rtw_dev *rtwdev,
13252774f206SBjoern A. Zeeb 				 struct rtw_vif *rtwvif,
13262774f206SBjoern A. Zeeb 				 struct rtw_rsvd_page *rsvd_pkt)
13272774f206SBjoern A. Zeeb {
13282774f206SBjoern A. Zeeb 	lockdep_assert_held(&rtwdev->mutex);
13292774f206SBjoern A. Zeeb 
13302774f206SBjoern A. Zeeb 	list_add_tail(&rsvd_pkt->vif_list, &rtwvif->rsvd_page_list);
13312774f206SBjoern A. Zeeb }
13322774f206SBjoern A. Zeeb 
13332774f206SBjoern A. Zeeb static void rtw_add_rsvd_page(struct rtw_dev *rtwdev,
13342774f206SBjoern A. Zeeb 			      struct rtw_vif *rtwvif,
13352774f206SBjoern A. Zeeb 			      enum rtw_rsvd_packet_type type,
13362774f206SBjoern A. Zeeb 			      bool txdesc)
13372774f206SBjoern A. Zeeb {
13382774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
13392774f206SBjoern A. Zeeb 
13402774f206SBjoern A. Zeeb 	rsvd_pkt = rtw_alloc_rsvd_page(rtwdev, type, txdesc);
13412774f206SBjoern A. Zeeb 	if (!rsvd_pkt) {
13422774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to alloc rsvd page %d\n", type);
13432774f206SBjoern A. Zeeb 		return;
13442774f206SBjoern A. Zeeb 	}
13452774f206SBjoern A. Zeeb 
13462774f206SBjoern A. Zeeb 	rsvd_pkt->rtwvif = rtwvif;
13472774f206SBjoern A. Zeeb 	rtw_insert_rsvd_page(rtwdev, rtwvif, rsvd_pkt);
13482774f206SBjoern A. Zeeb }
13492774f206SBjoern A. Zeeb 
13502774f206SBjoern A. Zeeb static void rtw_add_rsvd_page_probe_req(struct rtw_dev *rtwdev,
13512774f206SBjoern A. Zeeb 					struct rtw_vif *rtwvif,
13522774f206SBjoern A. Zeeb 					struct cfg80211_ssid *ssid)
13532774f206SBjoern A. Zeeb {
13542774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
13552774f206SBjoern A. Zeeb 
13562774f206SBjoern A. Zeeb 	rsvd_pkt = rtw_alloc_rsvd_page(rtwdev, RSVD_PROBE_REQ, true);
13572774f206SBjoern A. Zeeb 	if (!rsvd_pkt) {
13582774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to alloc probe req rsvd page\n");
13592774f206SBjoern A. Zeeb 		return;
13602774f206SBjoern A. Zeeb 	}
13612774f206SBjoern A. Zeeb 
13622774f206SBjoern A. Zeeb 	rsvd_pkt->rtwvif = rtwvif;
13632774f206SBjoern A. Zeeb 	rsvd_pkt->ssid = ssid;
13642774f206SBjoern A. Zeeb 	rtw_insert_rsvd_page(rtwdev, rtwvif, rsvd_pkt);
13652774f206SBjoern A. Zeeb }
13662774f206SBjoern A. Zeeb 
13672774f206SBjoern A. Zeeb void rtw_remove_rsvd_page(struct rtw_dev *rtwdev,
13682774f206SBjoern A. Zeeb 			  struct rtw_vif *rtwvif)
13692774f206SBjoern A. Zeeb {
13702774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt, *tmp;
13712774f206SBjoern A. Zeeb 
13722774f206SBjoern A. Zeeb 	lockdep_assert_held(&rtwdev->mutex);
13732774f206SBjoern A. Zeeb 
13742774f206SBjoern A. Zeeb 	/* remove all of the rsvd pages for vif */
13752774f206SBjoern A. Zeeb 	list_for_each_entry_safe(rsvd_pkt, tmp, &rtwvif->rsvd_page_list,
13762774f206SBjoern A. Zeeb 				 vif_list) {
13772774f206SBjoern A. Zeeb 		list_del(&rsvd_pkt->vif_list);
13782774f206SBjoern A. Zeeb 		if (!list_empty(&rsvd_pkt->build_list))
13792774f206SBjoern A. Zeeb 			list_del(&rsvd_pkt->build_list);
13802774f206SBjoern A. Zeeb 		kfree(rsvd_pkt);
13812774f206SBjoern A. Zeeb 	}
13822774f206SBjoern A. Zeeb }
13832774f206SBjoern A. Zeeb 
13842774f206SBjoern A. Zeeb void rtw_add_rsvd_page_bcn(struct rtw_dev *rtwdev,
13852774f206SBjoern A. Zeeb 			   struct rtw_vif *rtwvif)
13862774f206SBjoern A. Zeeb {
13872774f206SBjoern A. Zeeb 	struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
13882774f206SBjoern A. Zeeb 
13892774f206SBjoern A. Zeeb 	if (vif->type != NL80211_IFTYPE_AP &&
13902774f206SBjoern A. Zeeb 	    vif->type != NL80211_IFTYPE_ADHOC &&
13912774f206SBjoern A. Zeeb 	    vif->type != NL80211_IFTYPE_MESH_POINT) {
13922774f206SBjoern A. Zeeb 		rtw_warn(rtwdev, "Cannot add beacon rsvd page for %d\n",
13932774f206SBjoern A. Zeeb 			 vif->type);
13942774f206SBjoern A. Zeeb 		return;
13952774f206SBjoern A. Zeeb 	}
13962774f206SBjoern A. Zeeb 
13972774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_BEACON, false);
13982774f206SBjoern A. Zeeb }
13992774f206SBjoern A. Zeeb 
14002774f206SBjoern A. Zeeb void rtw_add_rsvd_page_pno(struct rtw_dev *rtwdev,
14012774f206SBjoern A. Zeeb 			   struct rtw_vif *rtwvif)
14022774f206SBjoern A. Zeeb {
14032774f206SBjoern A. Zeeb 	struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
14042774f206SBjoern A. Zeeb 	struct rtw_wow_param *rtw_wow = &rtwdev->wow;
14052774f206SBjoern A. Zeeb 	struct rtw_pno_request *rtw_pno_req = &rtw_wow->pno_req;
14062774f206SBjoern A. Zeeb 	struct cfg80211_ssid *ssid;
14072774f206SBjoern A. Zeeb 	int i;
14082774f206SBjoern A. Zeeb 
14092774f206SBjoern A. Zeeb 	if (vif->type != NL80211_IFTYPE_STATION) {
14102774f206SBjoern A. Zeeb 		rtw_warn(rtwdev, "Cannot add PNO rsvd page for %d\n",
14112774f206SBjoern A. Zeeb 			 vif->type);
14122774f206SBjoern A. Zeeb 		return;
14132774f206SBjoern A. Zeeb 	}
14142774f206SBjoern A. Zeeb 
14152774f206SBjoern A. Zeeb 	for (i = 0 ; i < rtw_pno_req->match_set_cnt; i++) {
14162774f206SBjoern A. Zeeb 		ssid = &rtw_pno_req->match_sets[i].ssid;
14172774f206SBjoern A. Zeeb 		rtw_add_rsvd_page_probe_req(rtwdev, rtwvif, ssid);
14182774f206SBjoern A. Zeeb 	}
14192774f206SBjoern A. Zeeb 
14202774f206SBjoern A. Zeeb 	rtw_add_rsvd_page_probe_req(rtwdev, rtwvif, NULL);
14212774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_NLO_INFO, false);
14222774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_CH_INFO, true);
14232774f206SBjoern A. Zeeb }
14242774f206SBjoern A. Zeeb 
14252774f206SBjoern A. Zeeb void rtw_add_rsvd_page_sta(struct rtw_dev *rtwdev,
14262774f206SBjoern A. Zeeb 			   struct rtw_vif *rtwvif)
14272774f206SBjoern A. Zeeb {
14282774f206SBjoern A. Zeeb 	struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
14292774f206SBjoern A. Zeeb 
14302774f206SBjoern A. Zeeb 	if (vif->type != NL80211_IFTYPE_STATION) {
14312774f206SBjoern A. Zeeb 		rtw_warn(rtwdev, "Cannot add sta rsvd page for %d\n",
14322774f206SBjoern A. Zeeb 			 vif->type);
14332774f206SBjoern A. Zeeb 		return;
14342774f206SBjoern A. Zeeb 	}
14352774f206SBjoern A. Zeeb 
14362774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_PS_POLL, true);
14372774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_QOS_NULL, true);
14382774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_NULL, true);
14392774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_LPS_PG_DPK, true);
14402774f206SBjoern A. Zeeb 	rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_LPS_PG_INFO, true);
14412774f206SBjoern A. Zeeb }
14422774f206SBjoern A. Zeeb 
14432774f206SBjoern A. Zeeb int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr,
14442774f206SBjoern A. Zeeb 				u8 *buf, u32 size)
14452774f206SBjoern A. Zeeb {
14462774f206SBjoern A. Zeeb 	u8 bckp[2];
14472774f206SBjoern A. Zeeb 	u8 val;
14482774f206SBjoern A. Zeeb 	u16 rsvd_pg_head;
14492774f206SBjoern A. Zeeb 	u32 bcn_valid_addr;
14502774f206SBjoern A. Zeeb 	u32 bcn_valid_mask;
14512774f206SBjoern A. Zeeb 	int ret;
14522774f206SBjoern A. Zeeb 
14532774f206SBjoern A. Zeeb 	lockdep_assert_held(&rtwdev->mutex);
14542774f206SBjoern A. Zeeb 
14552774f206SBjoern A. Zeeb 	if (!size)
14562774f206SBjoern A. Zeeb 		return -EINVAL;
14572774f206SBjoern A. Zeeb 
14582774f206SBjoern A. Zeeb 	if (rtw_chip_wcpu_11n(rtwdev)) {
14592774f206SBjoern A. Zeeb 		rtw_write32_set(rtwdev, REG_DWBCN0_CTRL, BIT_BCN_VALID);
14602774f206SBjoern A. Zeeb 	} else {
14612774f206SBjoern A. Zeeb 		pg_addr &= BIT_MASK_BCN_HEAD_1_V1;
14622774f206SBjoern A. Zeeb 		pg_addr |= BIT_BCN_VALID_V1;
14632774f206SBjoern A. Zeeb 		rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, pg_addr);
14642774f206SBjoern A. Zeeb 	}
14652774f206SBjoern A. Zeeb 
14662774f206SBjoern A. Zeeb 	val = rtw_read8(rtwdev, REG_CR + 1);
14672774f206SBjoern A. Zeeb 	bckp[0] = val;
14682774f206SBjoern A. Zeeb 	val |= BIT_ENSWBCN >> 8;
14692774f206SBjoern A. Zeeb 	rtw_write8(rtwdev, REG_CR + 1, val);
14702774f206SBjoern A. Zeeb 
14712774f206SBjoern A. Zeeb 	val = rtw_read8(rtwdev, REG_FWHW_TXQ_CTRL + 2);
14722774f206SBjoern A. Zeeb 	bckp[1] = val;
14732774f206SBjoern A. Zeeb 	val &= ~(BIT_EN_BCNQ_DL >> 16);
14742774f206SBjoern A. Zeeb 	rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, val);
14752774f206SBjoern A. Zeeb 
14762774f206SBjoern A. Zeeb 	ret = rtw_hci_write_data_rsvd_page(rtwdev, buf, size);
14772774f206SBjoern A. Zeeb 	if (ret) {
14782774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to write data to rsvd page\n");
14792774f206SBjoern A. Zeeb 		goto restore;
14802774f206SBjoern A. Zeeb 	}
14812774f206SBjoern A. Zeeb 
14822774f206SBjoern A. Zeeb 	if (rtw_chip_wcpu_11n(rtwdev)) {
14832774f206SBjoern A. Zeeb 		bcn_valid_addr = REG_DWBCN0_CTRL;
14842774f206SBjoern A. Zeeb 		bcn_valid_mask = BIT_BCN_VALID;
14852774f206SBjoern A. Zeeb 	} else {
14862774f206SBjoern A. Zeeb 		bcn_valid_addr = REG_FIFOPAGE_CTRL_2;
14872774f206SBjoern A. Zeeb 		bcn_valid_mask = BIT_BCN_VALID_V1;
14882774f206SBjoern A. Zeeb 	}
14892774f206SBjoern A. Zeeb 
14902774f206SBjoern A. Zeeb 	if (!check_hw_ready(rtwdev, bcn_valid_addr, bcn_valid_mask, 1)) {
14912774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "error beacon valid\n");
14922774f206SBjoern A. Zeeb 		ret = -EBUSY;
14932774f206SBjoern A. Zeeb 	}
14942774f206SBjoern A. Zeeb 
14952774f206SBjoern A. Zeeb restore:
14962774f206SBjoern A. Zeeb 	rsvd_pg_head = rtwdev->fifo.rsvd_boundary;
14972774f206SBjoern A. Zeeb 	rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2,
14982774f206SBjoern A. Zeeb 		    rsvd_pg_head | BIT_BCN_VALID_V1);
14992774f206SBjoern A. Zeeb 	rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, bckp[1]);
15002774f206SBjoern A. Zeeb 	rtw_write8(rtwdev, REG_CR + 1, bckp[0]);
15012774f206SBjoern A. Zeeb 
15022774f206SBjoern A. Zeeb 	return ret;
15032774f206SBjoern A. Zeeb }
15042774f206SBjoern A. Zeeb 
15052774f206SBjoern A. Zeeb static int rtw_download_drv_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size)
15062774f206SBjoern A. Zeeb {
15072774f206SBjoern A. Zeeb 	u32 pg_size;
15082774f206SBjoern A. Zeeb 	u32 pg_num = 0;
15092774f206SBjoern A. Zeeb 	u16 pg_addr = 0;
15102774f206SBjoern A. Zeeb 
15112774f206SBjoern A. Zeeb 	pg_size = rtwdev->chip->page_size;
15122774f206SBjoern A. Zeeb 	pg_num = size / pg_size + ((size & (pg_size - 1)) ? 1 : 0);
15132774f206SBjoern A. Zeeb 	if (pg_num > rtwdev->fifo.rsvd_drv_pg_num)
15142774f206SBjoern A. Zeeb 		return -ENOMEM;
15152774f206SBjoern A. Zeeb 
15162774f206SBjoern A. Zeeb 	pg_addr = rtwdev->fifo.rsvd_drv_addr;
15172774f206SBjoern A. Zeeb 
15182774f206SBjoern A. Zeeb 	return rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, size);
15192774f206SBjoern A. Zeeb }
15202774f206SBjoern A. Zeeb 
15212774f206SBjoern A. Zeeb static void __rtw_build_rsvd_page_reset(struct rtw_dev *rtwdev)
15222774f206SBjoern A. Zeeb {
15232774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt, *tmp;
15242774f206SBjoern A. Zeeb 
15252774f206SBjoern A. Zeeb 	list_for_each_entry_safe(rsvd_pkt, tmp, &rtwdev->rsvd_page_list,
15262774f206SBjoern A. Zeeb 				 build_list) {
15272774f206SBjoern A. Zeeb 		list_del_init(&rsvd_pkt->build_list);
15282774f206SBjoern A. Zeeb 
15292774f206SBjoern A. Zeeb 		/* Don't free except for the dummy rsvd page,
15302774f206SBjoern A. Zeeb 		 * others will be freed when removing vif
15312774f206SBjoern A. Zeeb 		 */
15322774f206SBjoern A. Zeeb 		if (rsvd_pkt->type == RSVD_DUMMY)
15332774f206SBjoern A. Zeeb 			kfree(rsvd_pkt);
15342774f206SBjoern A. Zeeb 	}
15352774f206SBjoern A. Zeeb }
15362774f206SBjoern A. Zeeb 
15372774f206SBjoern A. Zeeb static void rtw_build_rsvd_page_iter(void *data, u8 *mac,
15382774f206SBjoern A. Zeeb 				     struct ieee80211_vif *vif)
15392774f206SBjoern A. Zeeb {
15402774f206SBjoern A. Zeeb 	struct rtw_dev *rtwdev = data;
15412774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
15422774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
15432774f206SBjoern A. Zeeb 
154490aac0d8SBjoern A. Zeeb 	/* AP not yet started, don't gather its rsvd pages */
154590aac0d8SBjoern A. Zeeb 	if (vif->type == NL80211_IFTYPE_AP && !rtwdev->ap_active)
154690aac0d8SBjoern A. Zeeb 		return;
154790aac0d8SBjoern A. Zeeb 
15482774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwvif->rsvd_page_list, vif_list) {
15492774f206SBjoern A. Zeeb 		if (rsvd_pkt->type == RSVD_BEACON)
15502774f206SBjoern A. Zeeb 			list_add(&rsvd_pkt->build_list,
15512774f206SBjoern A. Zeeb 				 &rtwdev->rsvd_page_list);
15522774f206SBjoern A. Zeeb 		else
15532774f206SBjoern A. Zeeb 			list_add_tail(&rsvd_pkt->build_list,
15542774f206SBjoern A. Zeeb 				      &rtwdev->rsvd_page_list);
15552774f206SBjoern A. Zeeb 	}
15562774f206SBjoern A. Zeeb }
15572774f206SBjoern A. Zeeb 
15582774f206SBjoern A. Zeeb static int  __rtw_build_rsvd_page_from_vifs(struct rtw_dev *rtwdev)
15592774f206SBjoern A. Zeeb {
15602774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
15612774f206SBjoern A. Zeeb 
15622774f206SBjoern A. Zeeb 	__rtw_build_rsvd_page_reset(rtwdev);
15632774f206SBjoern A. Zeeb 
15642774f206SBjoern A. Zeeb 	/* gather rsvd page from vifs */
15652774f206SBjoern A. Zeeb 	rtw_iterate_vifs_atomic(rtwdev, rtw_build_rsvd_page_iter, rtwdev);
15662774f206SBjoern A. Zeeb 
15672774f206SBjoern A. Zeeb 	rsvd_pkt = list_first_entry_or_null(&rtwdev->rsvd_page_list,
15682774f206SBjoern A. Zeeb 					    struct rtw_rsvd_page, build_list);
15692774f206SBjoern A. Zeeb 	if (!rsvd_pkt) {
15702774f206SBjoern A. Zeeb 		WARN(1, "Should not have an empty reserved page\n");
15712774f206SBjoern A. Zeeb 		return -EINVAL;
15722774f206SBjoern A. Zeeb 	}
15732774f206SBjoern A. Zeeb 
15742774f206SBjoern A. Zeeb 	/* the first rsvd should be beacon, otherwise add a dummy one */
15752774f206SBjoern A. Zeeb 	if (rsvd_pkt->type != RSVD_BEACON) {
15762774f206SBjoern A. Zeeb 		struct rtw_rsvd_page *dummy_pkt;
15772774f206SBjoern A. Zeeb 
15782774f206SBjoern A. Zeeb 		dummy_pkt = rtw_alloc_rsvd_page(rtwdev, RSVD_DUMMY, false);
15792774f206SBjoern A. Zeeb 		if (!dummy_pkt) {
15802774f206SBjoern A. Zeeb 			rtw_err(rtwdev, "failed to alloc dummy rsvd page\n");
15812774f206SBjoern A. Zeeb 			return -ENOMEM;
15822774f206SBjoern A. Zeeb 		}
15832774f206SBjoern A. Zeeb 
15842774f206SBjoern A. Zeeb 		list_add(&dummy_pkt->build_list, &rtwdev->rsvd_page_list);
15852774f206SBjoern A. Zeeb 	}
15862774f206SBjoern A. Zeeb 
15872774f206SBjoern A. Zeeb 	return 0;
15882774f206SBjoern A. Zeeb }
15892774f206SBjoern A. Zeeb 
15902774f206SBjoern A. Zeeb static u8 *rtw_build_rsvd_page(struct rtw_dev *rtwdev, u32 *size)
15912774f206SBjoern A. Zeeb {
15922774f206SBjoern A. Zeeb 	struct ieee80211_hw *hw = rtwdev->hw;
159390aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
15942774f206SBjoern A. Zeeb 	struct sk_buff *iter;
15952774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
15962774f206SBjoern A. Zeeb 	u32 page = 0;
15972774f206SBjoern A. Zeeb 	u8 total_page = 0;
15982774f206SBjoern A. Zeeb 	u8 page_size, page_margin, tx_desc_sz;
15992774f206SBjoern A. Zeeb 	u8 *buf;
16002774f206SBjoern A. Zeeb 	int ret;
16012774f206SBjoern A. Zeeb 
16022774f206SBjoern A. Zeeb 	page_size = chip->page_size;
16032774f206SBjoern A. Zeeb 	tx_desc_sz = chip->tx_pkt_desc_sz;
16042774f206SBjoern A. Zeeb 	page_margin = page_size - tx_desc_sz;
16052774f206SBjoern A. Zeeb 
16062774f206SBjoern A. Zeeb 	ret = __rtw_build_rsvd_page_from_vifs(rtwdev);
16072774f206SBjoern A. Zeeb 	if (ret) {
16082774f206SBjoern A. Zeeb 		rtw_err(rtwdev,
16092774f206SBjoern A. Zeeb 			"failed to build rsvd page from vifs, ret %d\n", ret);
16102774f206SBjoern A. Zeeb 		return NULL;
16112774f206SBjoern A. Zeeb 	}
16122774f206SBjoern A. Zeeb 
16132774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) {
16142774f206SBjoern A. Zeeb 		iter = rtw_get_rsvd_page_skb(hw, rsvd_pkt);
16152774f206SBjoern A. Zeeb 		if (!iter) {
16162774f206SBjoern A. Zeeb 			rtw_err(rtwdev, "failed to build rsvd packet\n");
16172774f206SBjoern A. Zeeb 			goto release_skb;
16182774f206SBjoern A. Zeeb 		}
16192774f206SBjoern A. Zeeb 
16202774f206SBjoern A. Zeeb 		/* Fill the tx_desc for the rsvd pkt that requires one.
16212774f206SBjoern A. Zeeb 		 * And iter->len will be added with size of tx_desc_sz.
16222774f206SBjoern A. Zeeb 		 */
16232774f206SBjoern A. Zeeb 		if (rsvd_pkt->add_txdesc)
16242774f206SBjoern A. Zeeb 			rtw_fill_rsvd_page_desc(rtwdev, iter, rsvd_pkt->type);
16252774f206SBjoern A. Zeeb 
16262774f206SBjoern A. Zeeb 		rsvd_pkt->skb = iter;
16272774f206SBjoern A. Zeeb 		rsvd_pkt->page = total_page;
16282774f206SBjoern A. Zeeb 
16292774f206SBjoern A. Zeeb 		/* Reserved page is downloaded via TX path, and TX path will
16302774f206SBjoern A. Zeeb 		 * generate a tx_desc at the header to describe length of
16312774f206SBjoern A. Zeeb 		 * the buffer. If we are not counting page numbers with the
16322774f206SBjoern A. Zeeb 		 * size of tx_desc added at the first rsvd_pkt (usually a
16332774f206SBjoern A. Zeeb 		 * beacon, firmware default refer to the first page as the
16342774f206SBjoern A. Zeeb 		 * content of beacon), we could generate a buffer which size
16352774f206SBjoern A. Zeeb 		 * is smaller than the actual size of the whole rsvd_page
16362774f206SBjoern A. Zeeb 		 */
16372774f206SBjoern A. Zeeb 		if (total_page == 0) {
16382774f206SBjoern A. Zeeb 			if (rsvd_pkt->type != RSVD_BEACON &&
16392774f206SBjoern A. Zeeb 			    rsvd_pkt->type != RSVD_DUMMY) {
16402774f206SBjoern A. Zeeb 				rtw_err(rtwdev, "first page should be a beacon\n");
16412774f206SBjoern A. Zeeb 				goto release_skb;
16422774f206SBjoern A. Zeeb 			}
16432774f206SBjoern A. Zeeb 			total_page += rtw_len_to_page(iter->len + tx_desc_sz,
16442774f206SBjoern A. Zeeb 						      page_size);
16452774f206SBjoern A. Zeeb 		} else {
16462774f206SBjoern A. Zeeb 			total_page += rtw_len_to_page(iter->len, page_size);
16472774f206SBjoern A. Zeeb 		}
16482774f206SBjoern A. Zeeb 	}
16492774f206SBjoern A. Zeeb 
16502774f206SBjoern A. Zeeb 	if (total_page > rtwdev->fifo.rsvd_drv_pg_num) {
16512774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "rsvd page over size: %d\n", total_page);
16522774f206SBjoern A. Zeeb 		goto release_skb;
16532774f206SBjoern A. Zeeb 	}
16542774f206SBjoern A. Zeeb 
16552774f206SBjoern A. Zeeb 	*size = (total_page - 1) * page_size + page_margin;
16562774f206SBjoern A. Zeeb 	buf = kzalloc(*size, GFP_KERNEL);
16572774f206SBjoern A. Zeeb 	if (!buf)
16582774f206SBjoern A. Zeeb 		goto release_skb;
16592774f206SBjoern A. Zeeb 
16602774f206SBjoern A. Zeeb 	/* Copy the content of each rsvd_pkt to the buf, and they should
16612774f206SBjoern A. Zeeb 	 * be aligned to the pages.
16622774f206SBjoern A. Zeeb 	 *
16632774f206SBjoern A. Zeeb 	 * Note that the first rsvd_pkt is a beacon no matter what vif->type.
16642774f206SBjoern A. Zeeb 	 * And that rsvd_pkt does not require tx_desc because when it goes
16652774f206SBjoern A. Zeeb 	 * through TX path, the TX path will generate one for it.
16662774f206SBjoern A. Zeeb 	 */
16672774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) {
16682774f206SBjoern A. Zeeb 		rtw_rsvd_page_list_to_buf(rtwdev, page_size, page_margin,
16692774f206SBjoern A. Zeeb 					  page, buf, rsvd_pkt);
16702774f206SBjoern A. Zeeb 		if (page == 0)
16712774f206SBjoern A. Zeeb 			page += rtw_len_to_page(rsvd_pkt->skb->len +
16722774f206SBjoern A. Zeeb 						tx_desc_sz, page_size);
16732774f206SBjoern A. Zeeb 		else
16742774f206SBjoern A. Zeeb 			page += rtw_len_to_page(rsvd_pkt->skb->len, page_size);
16752774f206SBjoern A. Zeeb 
16762774f206SBjoern A. Zeeb 		kfree_skb(rsvd_pkt->skb);
16772774f206SBjoern A. Zeeb 		rsvd_pkt->skb = NULL;
16782774f206SBjoern A. Zeeb 	}
16792774f206SBjoern A. Zeeb 
16802774f206SBjoern A. Zeeb 	return buf;
16812774f206SBjoern A. Zeeb 
16822774f206SBjoern A. Zeeb release_skb:
16832774f206SBjoern A. Zeeb 	list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) {
16842774f206SBjoern A. Zeeb 		kfree_skb(rsvd_pkt->skb);
16852774f206SBjoern A. Zeeb 		rsvd_pkt->skb = NULL;
16862774f206SBjoern A. Zeeb 	}
16872774f206SBjoern A. Zeeb 
16882774f206SBjoern A. Zeeb 	return NULL;
16892774f206SBjoern A. Zeeb }
16902774f206SBjoern A. Zeeb 
16912774f206SBjoern A. Zeeb static int rtw_download_beacon(struct rtw_dev *rtwdev)
16922774f206SBjoern A. Zeeb {
16932774f206SBjoern A. Zeeb 	struct ieee80211_hw *hw = rtwdev->hw;
16942774f206SBjoern A. Zeeb 	struct rtw_rsvd_page *rsvd_pkt;
16952774f206SBjoern A. Zeeb 	struct sk_buff *skb;
16962774f206SBjoern A. Zeeb 	int ret = 0;
16972774f206SBjoern A. Zeeb 
16982774f206SBjoern A. Zeeb 	rsvd_pkt = list_first_entry_or_null(&rtwdev->rsvd_page_list,
16992774f206SBjoern A. Zeeb 					    struct rtw_rsvd_page, build_list);
17002774f206SBjoern A. Zeeb 	if (!rsvd_pkt) {
17012774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to get rsvd page from build list\n");
17022774f206SBjoern A. Zeeb 		return -ENOENT;
17032774f206SBjoern A. Zeeb 	}
17042774f206SBjoern A. Zeeb 
17052774f206SBjoern A. Zeeb 	if (rsvd_pkt->type != RSVD_BEACON &&
17062774f206SBjoern A. Zeeb 	    rsvd_pkt->type != RSVD_DUMMY) {
17072774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "invalid rsvd page type %d, should be beacon or dummy\n",
17082774f206SBjoern A. Zeeb 			rsvd_pkt->type);
17092774f206SBjoern A. Zeeb 		return -EINVAL;
17102774f206SBjoern A. Zeeb 	}
17112774f206SBjoern A. Zeeb 
17122774f206SBjoern A. Zeeb 	skb = rtw_get_rsvd_page_skb(hw, rsvd_pkt);
17132774f206SBjoern A. Zeeb 	if (!skb) {
17142774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to get beacon skb\n");
17152774f206SBjoern A. Zeeb 		return -ENOMEM;
17162774f206SBjoern A. Zeeb 	}
17172774f206SBjoern A. Zeeb 
17182774f206SBjoern A. Zeeb 	ret = rtw_download_drv_rsvd_page(rtwdev, skb->data, skb->len);
17192774f206SBjoern A. Zeeb 	if (ret)
17202774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to download drv rsvd page\n");
17212774f206SBjoern A. Zeeb 
17222774f206SBjoern A. Zeeb 	dev_kfree_skb(skb);
17232774f206SBjoern A. Zeeb 
17242774f206SBjoern A. Zeeb 	return ret;
17252774f206SBjoern A. Zeeb }
17262774f206SBjoern A. Zeeb 
17272774f206SBjoern A. Zeeb int rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev)
17282774f206SBjoern A. Zeeb {
17292774f206SBjoern A. Zeeb 	u8 *buf;
17302774f206SBjoern A. Zeeb 	u32 size;
17312774f206SBjoern A. Zeeb 	int ret;
17322774f206SBjoern A. Zeeb 
17332774f206SBjoern A. Zeeb 	buf = rtw_build_rsvd_page(rtwdev, &size);
17342774f206SBjoern A. Zeeb 	if (!buf) {
17352774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to build rsvd page pkt\n");
17362774f206SBjoern A. Zeeb 		return -ENOMEM;
17372774f206SBjoern A. Zeeb 	}
17382774f206SBjoern A. Zeeb 
17392774f206SBjoern A. Zeeb 	ret = rtw_download_drv_rsvd_page(rtwdev, buf, size);
17402774f206SBjoern A. Zeeb 	if (ret) {
17412774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to download drv rsvd page\n");
17422774f206SBjoern A. Zeeb 		goto free;
17432774f206SBjoern A. Zeeb 	}
17442774f206SBjoern A. Zeeb 
17452774f206SBjoern A. Zeeb 	/* The last thing is to download the *ONLY* beacon again, because
17462774f206SBjoern A. Zeeb 	 * the previous tx_desc is to describe the total rsvd page. Download
17472774f206SBjoern A. Zeeb 	 * the beacon again to replace the TX desc header, and we will get
17482774f206SBjoern A. Zeeb 	 * a correct tx_desc for the beacon in the rsvd page.
17492774f206SBjoern A. Zeeb 	 */
17502774f206SBjoern A. Zeeb 	ret = rtw_download_beacon(rtwdev);
17512774f206SBjoern A. Zeeb 	if (ret) {
17522774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to download beacon\n");
17532774f206SBjoern A. Zeeb 		goto free;
17542774f206SBjoern A. Zeeb 	}
17552774f206SBjoern A. Zeeb 
17562774f206SBjoern A. Zeeb free:
17572774f206SBjoern A. Zeeb 	kfree(buf);
17582774f206SBjoern A. Zeeb 
17592774f206SBjoern A. Zeeb 	return ret;
17602774f206SBjoern A. Zeeb }
17612774f206SBjoern A. Zeeb 
17629c951734SBjoern A. Zeeb void rtw_fw_update_beacon_work(struct work_struct *work)
17639c951734SBjoern A. Zeeb {
17649c951734SBjoern A. Zeeb 	struct rtw_dev *rtwdev = container_of(work, struct rtw_dev,
17659c951734SBjoern A. Zeeb 					      update_beacon_work);
17669c951734SBjoern A. Zeeb 
17679c951734SBjoern A. Zeeb 	mutex_lock(&rtwdev->mutex);
17689c951734SBjoern A. Zeeb 	rtw_fw_download_rsvd_page(rtwdev);
176990aac0d8SBjoern A. Zeeb 	rtw_send_rsvd_page_h2c(rtwdev);
17709c951734SBjoern A. Zeeb 	mutex_unlock(&rtwdev->mutex);
17719c951734SBjoern A. Zeeb }
17729c951734SBjoern A. Zeeb 
17732774f206SBjoern A. Zeeb static void rtw_fw_read_fifo_page(struct rtw_dev *rtwdev, u32 offset, u32 size,
17742774f206SBjoern A. Zeeb 				  u32 *buf, u32 residue, u16 start_pg)
17752774f206SBjoern A. Zeeb {
17762774f206SBjoern A. Zeeb 	u32 i;
17772774f206SBjoern A. Zeeb 	u16 idx = 0;
17782774f206SBjoern A. Zeeb 	u16 ctl;
17792774f206SBjoern A. Zeeb 
17802774f206SBjoern A. Zeeb 	ctl = rtw_read16(rtwdev, REG_PKTBUF_DBG_CTRL) & 0xf000;
17812774f206SBjoern A. Zeeb 	/* disable rx clock gate */
17822774f206SBjoern A. Zeeb 	rtw_write32_set(rtwdev, REG_RCR, BIT_DISGCLK);
17832774f206SBjoern A. Zeeb 
17842774f206SBjoern A. Zeeb 	do {
17852774f206SBjoern A. Zeeb 		rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, start_pg | ctl);
17862774f206SBjoern A. Zeeb 
17872774f206SBjoern A. Zeeb 		for (i = FIFO_DUMP_ADDR + residue;
17882774f206SBjoern A. Zeeb 		     i < FIFO_DUMP_ADDR + FIFO_PAGE_SIZE; i += 4) {
17892774f206SBjoern A. Zeeb 			buf[idx++] = rtw_read32(rtwdev, i);
17902774f206SBjoern A. Zeeb 			size -= 4;
17912774f206SBjoern A. Zeeb 			if (size == 0)
17922774f206SBjoern A. Zeeb 				goto out;
17932774f206SBjoern A. Zeeb 		}
17942774f206SBjoern A. Zeeb 
17952774f206SBjoern A. Zeeb 		residue = 0;
17962774f206SBjoern A. Zeeb 		start_pg++;
17972774f206SBjoern A. Zeeb 	} while (size);
17982774f206SBjoern A. Zeeb 
17992774f206SBjoern A. Zeeb out:
18002774f206SBjoern A. Zeeb 	rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, ctl);
18012774f206SBjoern A. Zeeb 	/* restore rx clock gate */
18022774f206SBjoern A. Zeeb 	rtw_write32_clr(rtwdev, REG_RCR, BIT_DISGCLK);
18032774f206SBjoern A. Zeeb }
18042774f206SBjoern A. Zeeb 
18052774f206SBjoern A. Zeeb static void rtw_fw_read_fifo(struct rtw_dev *rtwdev, enum rtw_fw_fifo_sel sel,
18062774f206SBjoern A. Zeeb 			     u32 offset, u32 size, u32 *buf)
18072774f206SBjoern A. Zeeb {
180890aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
18092774f206SBjoern A. Zeeb 	u32 start_pg, residue;
18102774f206SBjoern A. Zeeb 
18112774f206SBjoern A. Zeeb 	if (sel >= RTW_FW_FIFO_MAX) {
18122774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_FW, "wrong fw fifo sel\n");
18132774f206SBjoern A. Zeeb 		return;
18142774f206SBjoern A. Zeeb 	}
18152774f206SBjoern A. Zeeb 	if (sel == RTW_FW_FIFO_SEL_RSVD_PAGE)
18162774f206SBjoern A. Zeeb 		offset += rtwdev->fifo.rsvd_boundary << TX_PAGE_SIZE_SHIFT;
18172774f206SBjoern A. Zeeb 	residue = offset & (FIFO_PAGE_SIZE - 1);
18182774f206SBjoern A. Zeeb 	start_pg = (offset >> FIFO_PAGE_SIZE_SHIFT) + chip->fw_fifo_addr[sel];
18192774f206SBjoern A. Zeeb 
18202774f206SBjoern A. Zeeb 	rtw_fw_read_fifo_page(rtwdev, offset, size, buf, residue, start_pg);
18212774f206SBjoern A. Zeeb }
18222774f206SBjoern A. Zeeb 
18232774f206SBjoern A. Zeeb static bool rtw_fw_dump_check_size(struct rtw_dev *rtwdev,
18242774f206SBjoern A. Zeeb 				   enum rtw_fw_fifo_sel sel,
18252774f206SBjoern A. Zeeb 				   u32 start_addr, u32 size)
18262774f206SBjoern A. Zeeb {
18272774f206SBjoern A. Zeeb 	switch (sel) {
18282774f206SBjoern A. Zeeb 	case RTW_FW_FIFO_SEL_TX:
18292774f206SBjoern A. Zeeb 	case RTW_FW_FIFO_SEL_RX:
18302774f206SBjoern A. Zeeb 		if ((start_addr + size) > rtwdev->chip->fw_fifo_addr[sel])
18312774f206SBjoern A. Zeeb 			return false;
18322774f206SBjoern A. Zeeb 		fallthrough;
18332774f206SBjoern A. Zeeb 	default:
18342774f206SBjoern A. Zeeb 		return true;
18352774f206SBjoern A. Zeeb 	}
18362774f206SBjoern A. Zeeb }
18372774f206SBjoern A. Zeeb 
18382774f206SBjoern A. Zeeb int rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size,
18392774f206SBjoern A. Zeeb 		     u32 *buffer)
18402774f206SBjoern A. Zeeb {
18412774f206SBjoern A. Zeeb 	if (!rtwdev->chip->fw_fifo_addr[0]) {
18422774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_FW, "chip not support dump fw fifo\n");
18432774f206SBjoern A. Zeeb 		return -ENOTSUPP;
18442774f206SBjoern A. Zeeb 	}
18452774f206SBjoern A. Zeeb 
18462774f206SBjoern A. Zeeb 	if (size == 0 || !buffer)
18472774f206SBjoern A. Zeeb 		return -EINVAL;
18482774f206SBjoern A. Zeeb 
18492774f206SBjoern A. Zeeb 	if (size & 0x3) {
18502774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_FW, "not 4byte alignment\n");
18512774f206SBjoern A. Zeeb 		return -EINVAL;
18522774f206SBjoern A. Zeeb 	}
18532774f206SBjoern A. Zeeb 
18542774f206SBjoern A. Zeeb 	if (!rtw_fw_dump_check_size(rtwdev, fifo_sel, addr, size)) {
18552774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_FW, "fw fifo dump size overflow\n");
18562774f206SBjoern A. Zeeb 		return -EINVAL;
18572774f206SBjoern A. Zeeb 	}
18582774f206SBjoern A. Zeeb 
18592774f206SBjoern A. Zeeb 	rtw_fw_read_fifo(rtwdev, fifo_sel, addr, size, buffer);
18602774f206SBjoern A. Zeeb 
18612774f206SBjoern A. Zeeb 	return 0;
18622774f206SBjoern A. Zeeb }
18632774f206SBjoern A. Zeeb 
18642774f206SBjoern A. Zeeb static void __rtw_fw_update_pkt(struct rtw_dev *rtwdev, u8 pkt_id, u16 size,
18652774f206SBjoern A. Zeeb 				u8 location)
18662774f206SBjoern A. Zeeb {
186790aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
18682774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
18692774f206SBjoern A. Zeeb 	u16 total_size = H2C_PKT_HDR_SIZE + H2C_PKT_UPDATE_PKT_LEN;
18702774f206SBjoern A. Zeeb 
18712774f206SBjoern A. Zeeb 	rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_UPDATE_PKT);
18722774f206SBjoern A. Zeeb 
18732774f206SBjoern A. Zeeb 	SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size);
18742774f206SBjoern A. Zeeb 	UPDATE_PKT_SET_PKT_ID(h2c_pkt, pkt_id);
18752774f206SBjoern A. Zeeb 	UPDATE_PKT_SET_LOCATION(h2c_pkt, location);
18762774f206SBjoern A. Zeeb 
18772774f206SBjoern A. Zeeb 	/* include txdesc size */
18782774f206SBjoern A. Zeeb 	size += chip->tx_pkt_desc_sz;
18792774f206SBjoern A. Zeeb 	UPDATE_PKT_SET_SIZE(h2c_pkt, size);
18802774f206SBjoern A. Zeeb 
18812774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
18822774f206SBjoern A. Zeeb }
18832774f206SBjoern A. Zeeb 
18842774f206SBjoern A. Zeeb void rtw_fw_update_pkt_probe_req(struct rtw_dev *rtwdev,
18852774f206SBjoern A. Zeeb 				 struct cfg80211_ssid *ssid)
18862774f206SBjoern A. Zeeb {
18872774f206SBjoern A. Zeeb 	u8 loc;
18882774f206SBjoern A. Zeeb 	u16 size;
18892774f206SBjoern A. Zeeb 
18902774f206SBjoern A. Zeeb 	loc = rtw_get_rsvd_page_probe_req_location(rtwdev, ssid);
18912774f206SBjoern A. Zeeb 	if (!loc) {
18922774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to get probe_req rsvd loc\n");
18932774f206SBjoern A. Zeeb 		return;
18942774f206SBjoern A. Zeeb 	}
18952774f206SBjoern A. Zeeb 
18962774f206SBjoern A. Zeeb 	size = rtw_get_rsvd_page_probe_req_size(rtwdev, ssid);
18972774f206SBjoern A. Zeeb 	if (!size) {
18982774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "failed to get probe_req rsvd size\n");
18992774f206SBjoern A. Zeeb 		return;
19002774f206SBjoern A. Zeeb 	}
19012774f206SBjoern A. Zeeb 
19022774f206SBjoern A. Zeeb 	__rtw_fw_update_pkt(rtwdev, RTW_PACKET_PROBE_REQ, size, loc);
19032774f206SBjoern A. Zeeb }
19042774f206SBjoern A. Zeeb 
19052774f206SBjoern A. Zeeb void rtw_fw_channel_switch(struct rtw_dev *rtwdev, bool enable)
19062774f206SBjoern A. Zeeb {
19072774f206SBjoern A. Zeeb 	struct rtw_pno_request *rtw_pno_req = &rtwdev->wow.pno_req;
19082774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
19092774f206SBjoern A. Zeeb 	u16 total_size = H2C_PKT_HDR_SIZE + H2C_PKT_CH_SWITCH_LEN;
19102774f206SBjoern A. Zeeb 	u8 loc_ch_info;
19112774f206SBjoern A. Zeeb 	const struct rtw_ch_switch_option cs_option = {
19122774f206SBjoern A. Zeeb 		.dest_ch_en = 1,
19132774f206SBjoern A. Zeeb 		.dest_ch = 1,
19142774f206SBjoern A. Zeeb 		.periodic_option = 2,
19152774f206SBjoern A. Zeeb 		.normal_period = 5,
19162774f206SBjoern A. Zeeb 		.normal_period_sel = 0,
19172774f206SBjoern A. Zeeb 		.normal_cycle = 10,
19182774f206SBjoern A. Zeeb 		.slow_period = 1,
19192774f206SBjoern A. Zeeb 		.slow_period_sel = 1,
19202774f206SBjoern A. Zeeb 	};
19212774f206SBjoern A. Zeeb 
19222774f206SBjoern A. Zeeb 	rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_CH_SWITCH);
19232774f206SBjoern A. Zeeb 	SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size);
19242774f206SBjoern A. Zeeb 
19252774f206SBjoern A. Zeeb 	CH_SWITCH_SET_START(h2c_pkt, enable);
19262774f206SBjoern A. Zeeb 	CH_SWITCH_SET_DEST_CH_EN(h2c_pkt, cs_option.dest_ch_en);
19272774f206SBjoern A. Zeeb 	CH_SWITCH_SET_DEST_CH(h2c_pkt, cs_option.dest_ch);
19282774f206SBjoern A. Zeeb 	CH_SWITCH_SET_NORMAL_PERIOD(h2c_pkt, cs_option.normal_period);
19292774f206SBjoern A. Zeeb 	CH_SWITCH_SET_NORMAL_PERIOD_SEL(h2c_pkt, cs_option.normal_period_sel);
19302774f206SBjoern A. Zeeb 	CH_SWITCH_SET_SLOW_PERIOD(h2c_pkt, cs_option.slow_period);
19312774f206SBjoern A. Zeeb 	CH_SWITCH_SET_SLOW_PERIOD_SEL(h2c_pkt, cs_option.slow_period_sel);
19322774f206SBjoern A. Zeeb 	CH_SWITCH_SET_NORMAL_CYCLE(h2c_pkt, cs_option.normal_cycle);
19332774f206SBjoern A. Zeeb 	CH_SWITCH_SET_PERIODIC_OPT(h2c_pkt, cs_option.periodic_option);
19342774f206SBjoern A. Zeeb 
19352774f206SBjoern A. Zeeb 	CH_SWITCH_SET_CH_NUM(h2c_pkt, rtw_pno_req->channel_cnt);
19362774f206SBjoern A. Zeeb 	CH_SWITCH_SET_INFO_SIZE(h2c_pkt, rtw_pno_req->channel_cnt * 4);
19372774f206SBjoern A. Zeeb 
19382774f206SBjoern A. Zeeb 	loc_ch_info = rtw_get_rsvd_page_location(rtwdev, RSVD_CH_INFO);
19392774f206SBjoern A. Zeeb 	CH_SWITCH_SET_INFO_LOC(h2c_pkt, loc_ch_info);
19402774f206SBjoern A. Zeeb 
19412774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
19422774f206SBjoern A. Zeeb }
19432774f206SBjoern A. Zeeb 
19442774f206SBjoern A. Zeeb void rtw_fw_adaptivity(struct rtw_dev *rtwdev)
19452774f206SBjoern A. Zeeb {
19462774f206SBjoern A. Zeeb 	struct rtw_dm_info *dm_info = &rtwdev->dm_info;
19472774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
19482774f206SBjoern A. Zeeb 
19492774f206SBjoern A. Zeeb 	if (!rtw_edcca_enabled) {
19502774f206SBjoern A. Zeeb 		dm_info->edcca_mode = RTW_EDCCA_NORMAL;
19512774f206SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY,
19522774f206SBjoern A. Zeeb 			"EDCCA disabled by debugfs\n");
19532774f206SBjoern A. Zeeb 	}
19542774f206SBjoern A. Zeeb 
19552774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_ADAPTIVITY);
19562774f206SBjoern A. Zeeb 	SET_ADAPTIVITY_MODE(h2c_pkt, dm_info->edcca_mode);
19579c951734SBjoern A. Zeeb 	SET_ADAPTIVITY_OPTION(h2c_pkt, 1);
19582774f206SBjoern A. Zeeb 	SET_ADAPTIVITY_IGI(h2c_pkt, dm_info->igi_history[0]);
19592774f206SBjoern A. Zeeb 	SET_ADAPTIVITY_L2H(h2c_pkt, dm_info->l2h_th_ini);
19602774f206SBjoern A. Zeeb 	SET_ADAPTIVITY_DENSITY(h2c_pkt, dm_info->scan_density);
19612774f206SBjoern A. Zeeb 
19622774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
19632774f206SBjoern A. Zeeb }
19642774f206SBjoern A. Zeeb 
19652774f206SBjoern A. Zeeb void rtw_fw_scan_notify(struct rtw_dev *rtwdev, bool start)
19662774f206SBjoern A. Zeeb {
19672774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
19682774f206SBjoern A. Zeeb 
19692774f206SBjoern A. Zeeb 	SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_SCAN);
19702774f206SBjoern A. Zeeb 	SET_SCAN_START(h2c_pkt, start);
19712774f206SBjoern A. Zeeb 
19722774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_command(rtwdev, h2c_pkt);
19732774f206SBjoern A. Zeeb }
19742774f206SBjoern A. Zeeb 
19759c951734SBjoern A. Zeeb static int rtw_append_probe_req_ie(struct rtw_dev *rtwdev, struct sk_buff *skb,
19769c951734SBjoern A. Zeeb 				   struct sk_buff_head *list, u8 *bands,
19772774f206SBjoern A. Zeeb 				   struct rtw_vif *rtwvif)
19782774f206SBjoern A. Zeeb {
197990aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
19802774f206SBjoern A. Zeeb 	struct ieee80211_scan_ies *ies = rtwvif->scan_ies;
19812774f206SBjoern A. Zeeb 	struct sk_buff *new;
19822774f206SBjoern A. Zeeb 	u8 idx;
19832774f206SBjoern A. Zeeb 
19842774f206SBjoern A. Zeeb 	for (idx = NL80211_BAND_2GHZ; idx < NUM_NL80211_BANDS; idx++) {
19852774f206SBjoern A. Zeeb 		if (!(BIT(idx) & chip->band))
19862774f206SBjoern A. Zeeb 			continue;
19872774f206SBjoern A. Zeeb 		new = skb_copy(skb, GFP_KERNEL);
19889c951734SBjoern A. Zeeb 		if (!new)
19899c951734SBjoern A. Zeeb 			return -ENOMEM;
19902774f206SBjoern A. Zeeb 		skb_put_data(new, ies->ies[idx], ies->len[idx]);
19912774f206SBjoern A. Zeeb 		skb_put_data(new, ies->common_ies, ies->common_ie_len);
19922774f206SBjoern A. Zeeb 		skb_queue_tail(list, new);
19939c951734SBjoern A. Zeeb 		(*bands)++;
19942774f206SBjoern A. Zeeb 	}
19952774f206SBjoern A. Zeeb 
19969c951734SBjoern A. Zeeb 	return 0;
19979c951734SBjoern A. Zeeb }
19989c951734SBjoern A. Zeeb 
19999c951734SBjoern A. Zeeb static int _rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev, u8 num_probes,
20002774f206SBjoern A. Zeeb 					 struct sk_buff_head *probe_req_list)
20012774f206SBjoern A. Zeeb {
200290aac0d8SBjoern A. Zeeb 	const struct rtw_chip_info *chip = rtwdev->chip;
20032774f206SBjoern A. Zeeb 	struct sk_buff *skb, *tmp;
20042774f206SBjoern A. Zeeb 	u8 page_offset = 1, *buf, page_size = chip->page_size;
20052774f206SBjoern A. Zeeb 	u16 pg_addr = rtwdev->fifo.rsvd_h2c_info_addr, loc;
20062774f206SBjoern A. Zeeb 	u16 buf_offset = page_size * page_offset;
20072774f206SBjoern A. Zeeb 	u8 tx_desc_sz = chip->tx_pkt_desc_sz;
200890aac0d8SBjoern A. Zeeb 	u8 page_cnt, pages;
20092774f206SBjoern A. Zeeb 	unsigned int pkt_len;
20102774f206SBjoern A. Zeeb 	int ret;
20112774f206SBjoern A. Zeeb 
201290aac0d8SBjoern A. Zeeb 	if (rtw_fw_feature_ext_check(&rtwdev->fw, FW_FEATURE_EXT_OLD_PAGE_NUM))
201390aac0d8SBjoern A. Zeeb 		page_cnt = RTW_OLD_PROBE_PG_CNT;
201490aac0d8SBjoern A. Zeeb 	else
201590aac0d8SBjoern A. Zeeb 		page_cnt = RTW_PROBE_PG_CNT;
201690aac0d8SBjoern A. Zeeb 
201790aac0d8SBjoern A. Zeeb 	pages = page_offset + num_probes * page_cnt;
201890aac0d8SBjoern A. Zeeb 
20192774f206SBjoern A. Zeeb 	buf = kzalloc(page_size * pages, GFP_KERNEL);
20202774f206SBjoern A. Zeeb 	if (!buf)
20212774f206SBjoern A. Zeeb 		return -ENOMEM;
20222774f206SBjoern A. Zeeb 
20232774f206SBjoern A. Zeeb 	buf_offset -= tx_desc_sz;
20242774f206SBjoern A. Zeeb 	skb_queue_walk_safe(probe_req_list, skb, tmp) {
20252774f206SBjoern A. Zeeb 		skb_unlink(skb, probe_req_list);
20262774f206SBjoern A. Zeeb 		rtw_fill_rsvd_page_desc(rtwdev, skb, RSVD_PROBE_REQ);
202790aac0d8SBjoern A. Zeeb 		if (skb->len > page_size * page_cnt) {
2028728afa88SBjoern A. Zeeb #if defined(__FreeBSD__)
2029728afa88SBjoern A. Zeeb 			kfree_skb(skb);
2030728afa88SBjoern A. Zeeb #endif
20312774f206SBjoern A. Zeeb 			ret = -EINVAL;
20322774f206SBjoern A. Zeeb 			goto out;
20332774f206SBjoern A. Zeeb 		}
20342774f206SBjoern A. Zeeb 
20352774f206SBjoern A. Zeeb 		memcpy(buf + buf_offset, skb->data, skb->len);
20362774f206SBjoern A. Zeeb 		pkt_len = skb->len - tx_desc_sz;
20372774f206SBjoern A. Zeeb 		loc = pg_addr - rtwdev->fifo.rsvd_boundary + page_offset;
20382774f206SBjoern A. Zeeb 		__rtw_fw_update_pkt(rtwdev, RTW_PACKET_PROBE_REQ, pkt_len, loc);
20392774f206SBjoern A. Zeeb 
204090aac0d8SBjoern A. Zeeb 		buf_offset += page_cnt * page_size;
204190aac0d8SBjoern A. Zeeb 		page_offset += page_cnt;
20422774f206SBjoern A. Zeeb 		kfree_skb(skb);
20432774f206SBjoern A. Zeeb 	}
20442774f206SBjoern A. Zeeb 
20452774f206SBjoern A. Zeeb 	ret = rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, buf_offset);
20462774f206SBjoern A. Zeeb 	if (ret) {
20472774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "Download probe request to firmware failed\n");
20482774f206SBjoern A. Zeeb 		goto out;
20492774f206SBjoern A. Zeeb 	}
20502774f206SBjoern A. Zeeb 
20512774f206SBjoern A. Zeeb 	rtwdev->scan_info.probe_pg_size = page_offset;
20522774f206SBjoern A. Zeeb out:
20532774f206SBjoern A. Zeeb 	kfree(buf);
20549c951734SBjoern A. Zeeb 	skb_queue_walk_safe(probe_req_list, skb, tmp)
20559c951734SBjoern A. Zeeb 		kfree_skb(skb);
20562774f206SBjoern A. Zeeb 
20572774f206SBjoern A. Zeeb 	return ret;
20582774f206SBjoern A. Zeeb }
20592774f206SBjoern A. Zeeb 
20602774f206SBjoern A. Zeeb static int rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev,
20612774f206SBjoern A. Zeeb 					struct rtw_vif *rtwvif)
20622774f206SBjoern A. Zeeb {
20632774f206SBjoern A. Zeeb 	struct cfg80211_scan_request *req = rtwvif->scan_req;
20642774f206SBjoern A. Zeeb 	struct sk_buff_head list;
20659c951734SBjoern A. Zeeb 	struct sk_buff *skb, *tmp;
20669c951734SBjoern A. Zeeb 	u8 num = req->n_ssids, i, bands = 0;
20679c951734SBjoern A. Zeeb 	int ret;
20682774f206SBjoern A. Zeeb 
20692774f206SBjoern A. Zeeb 	skb_queue_head_init(&list);
20702774f206SBjoern A. Zeeb 	for (i = 0; i < num; i++) {
20712774f206SBjoern A. Zeeb 		skb = ieee80211_probereq_get(rtwdev->hw, rtwvif->mac_addr,
20722774f206SBjoern A. Zeeb 					     req->ssids[i].ssid,
20732774f206SBjoern A. Zeeb 					     req->ssids[i].ssid_len,
20742774f206SBjoern A. Zeeb 					     req->ie_len);
20759c951734SBjoern A. Zeeb 		if (!skb) {
20769c951734SBjoern A. Zeeb 			ret = -ENOMEM;
20779c951734SBjoern A. Zeeb 			goto out;
20789c951734SBjoern A. Zeeb 		}
20799c951734SBjoern A. Zeeb 		ret = rtw_append_probe_req_ie(rtwdev, skb, &list, &bands,
20809c951734SBjoern A. Zeeb 					      rtwvif);
20819c951734SBjoern A. Zeeb 		if (ret)
20829c951734SBjoern A. Zeeb 			goto out;
20839c951734SBjoern A. Zeeb 
20842774f206SBjoern A. Zeeb 		kfree_skb(skb);
20852774f206SBjoern A. Zeeb 	}
20862774f206SBjoern A. Zeeb 
20879c951734SBjoern A. Zeeb 	return _rtw_hw_scan_update_probe_req(rtwdev, num * bands, &list);
20889c951734SBjoern A. Zeeb 
20899c951734SBjoern A. Zeeb out:
20909c951734SBjoern A. Zeeb 	skb_queue_walk_safe(&list, skb, tmp)
20919c951734SBjoern A. Zeeb 		kfree_skb(skb);
20929c951734SBjoern A. Zeeb 
20939c951734SBjoern A. Zeeb 	return ret;
20942774f206SBjoern A. Zeeb }
20952774f206SBjoern A. Zeeb 
20962774f206SBjoern A. Zeeb static int rtw_add_chan_info(struct rtw_dev *rtwdev, struct rtw_chan_info *info,
20972774f206SBjoern A. Zeeb 			     struct rtw_chan_list *list, u8 *buf)
20982774f206SBjoern A. Zeeb {
20992774f206SBjoern A. Zeeb 	u8 *chan = &buf[list->size];
21002774f206SBjoern A. Zeeb 	u8 info_size = RTW_CH_INFO_SIZE;
21012774f206SBjoern A. Zeeb 
21022774f206SBjoern A. Zeeb 	if (list->size > list->buf_size)
21032774f206SBjoern A. Zeeb 		return -ENOMEM;
21042774f206SBjoern A. Zeeb 
21052774f206SBjoern A. Zeeb 	CH_INFO_SET_CH(chan, info->channel);
21062774f206SBjoern A. Zeeb 	CH_INFO_SET_PRI_CH_IDX(chan, info->pri_ch_idx);
21072774f206SBjoern A. Zeeb 	CH_INFO_SET_BW(chan, info->bw);
21082774f206SBjoern A. Zeeb 	CH_INFO_SET_TIMEOUT(chan, info->timeout);
21092774f206SBjoern A. Zeeb 	CH_INFO_SET_ACTION_ID(chan, info->action_id);
21102774f206SBjoern A. Zeeb 	CH_INFO_SET_EXTRA_INFO(chan, info->extra_info);
21112774f206SBjoern A. Zeeb 	if (info->extra_info) {
21122774f206SBjoern A. Zeeb 		EXTRA_CH_INFO_SET_ID(chan, RTW_SCAN_EXTRA_ID_DFS);
21132774f206SBjoern A. Zeeb 		EXTRA_CH_INFO_SET_INFO(chan, RTW_SCAN_EXTRA_ACTION_SCAN);
21142774f206SBjoern A. Zeeb 		EXTRA_CH_INFO_SET_SIZE(chan, RTW_EX_CH_INFO_SIZE -
21152774f206SBjoern A. Zeeb 				       RTW_EX_CH_INFO_HDR_SIZE);
21162774f206SBjoern A. Zeeb 		EXTRA_CH_INFO_SET_DFS_EXT_TIME(chan, RTW_DFS_CHAN_TIME);
21172774f206SBjoern A. Zeeb 		info_size += RTW_EX_CH_INFO_SIZE;
21182774f206SBjoern A. Zeeb 	}
21192774f206SBjoern A. Zeeb 	list->size += info_size;
21202774f206SBjoern A. Zeeb 	list->ch_num++;
21212774f206SBjoern A. Zeeb 
21222774f206SBjoern A. Zeeb 	return 0;
21232774f206SBjoern A. Zeeb }
21242774f206SBjoern A. Zeeb 
21252774f206SBjoern A. Zeeb static int rtw_add_chan_list(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif,
21262774f206SBjoern A. Zeeb 			     struct rtw_chan_list *list, u8 *buf)
21272774f206SBjoern A. Zeeb {
21282774f206SBjoern A. Zeeb 	struct cfg80211_scan_request *req = rtwvif->scan_req;
21292774f206SBjoern A. Zeeb 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
21302774f206SBjoern A. Zeeb 	struct ieee80211_channel *channel;
21312774f206SBjoern A. Zeeb 	int i, ret = 0;
21322774f206SBjoern A. Zeeb 
21332774f206SBjoern A. Zeeb 	for (i = 0; i < req->n_channels; i++) {
21342774f206SBjoern A. Zeeb 		struct rtw_chan_info ch_info = {0};
21352774f206SBjoern A. Zeeb 
21362774f206SBjoern A. Zeeb 		channel = req->channels[i];
21372774f206SBjoern A. Zeeb 		ch_info.channel = channel->hw_value;
21382774f206SBjoern A. Zeeb 		ch_info.bw = RTW_SCAN_WIDTH;
21392774f206SBjoern A. Zeeb 		ch_info.pri_ch_idx = RTW_PRI_CH_IDX;
21402774f206SBjoern A. Zeeb 		ch_info.timeout = req->duration_mandatory ?
21412774f206SBjoern A. Zeeb 				  req->duration : RTW_CHANNEL_TIME;
21422774f206SBjoern A. Zeeb 
21432774f206SBjoern A. Zeeb 		if (channel->flags & (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IR)) {
21442774f206SBjoern A. Zeeb 			ch_info.action_id = RTW_CHANNEL_RADAR;
21452774f206SBjoern A. Zeeb 			ch_info.extra_info = 1;
21462774f206SBjoern A. Zeeb 			/* Overwrite duration for passive scans if necessary */
21472774f206SBjoern A. Zeeb 			ch_info.timeout = ch_info.timeout > RTW_PASS_CHAN_TIME ?
21482774f206SBjoern A. Zeeb 					  ch_info.timeout : RTW_PASS_CHAN_TIME;
21492774f206SBjoern A. Zeeb 		} else {
21502774f206SBjoern A. Zeeb 			ch_info.action_id = RTW_CHANNEL_ACTIVE;
21512774f206SBjoern A. Zeeb 		}
21522774f206SBjoern A. Zeeb 
21532774f206SBjoern A. Zeeb 		ret = rtw_add_chan_info(rtwdev, &ch_info, list, buf);
21542774f206SBjoern A. Zeeb 		if (ret)
21552774f206SBjoern A. Zeeb 			return ret;
21562774f206SBjoern A. Zeeb 	}
21572774f206SBjoern A. Zeeb 
21582774f206SBjoern A. Zeeb 	if (list->size > fifo->rsvd_pg_num << TX_PAGE_SIZE_SHIFT) {
21592774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "List exceeds rsvd page total size\n");
21602774f206SBjoern A. Zeeb 		return -EINVAL;
21612774f206SBjoern A. Zeeb 	}
21622774f206SBjoern A. Zeeb 
21632774f206SBjoern A. Zeeb 	list->addr = fifo->rsvd_h2c_info_addr + rtwdev->scan_info.probe_pg_size;
21642774f206SBjoern A. Zeeb 	ret = rtw_fw_write_data_rsvd_page(rtwdev, list->addr, buf, list->size);
21652774f206SBjoern A. Zeeb 	if (ret)
21662774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "Download channel list failed\n");
21672774f206SBjoern A. Zeeb 
21682774f206SBjoern A. Zeeb 	return ret;
21692774f206SBjoern A. Zeeb }
21702774f206SBjoern A. Zeeb 
21712774f206SBjoern A. Zeeb static void rtw_fw_set_scan_offload(struct rtw_dev *rtwdev,
21722774f206SBjoern A. Zeeb 				    struct rtw_ch_switch_option *opt,
21732774f206SBjoern A. Zeeb 				    struct rtw_vif *rtwvif,
21742774f206SBjoern A. Zeeb 				    struct rtw_chan_list *list)
21752774f206SBjoern A. Zeeb {
21762774f206SBjoern A. Zeeb 	struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
21772774f206SBjoern A. Zeeb 	struct cfg80211_scan_request *req = rtwvif->scan_req;
21782774f206SBjoern A. Zeeb 	struct rtw_fifo_conf *fifo = &rtwdev->fifo;
21792774f206SBjoern A. Zeeb 	/* reserve one dummy page at the beginning for tx descriptor */
21802774f206SBjoern A. Zeeb 	u8 pkt_loc = fifo->rsvd_h2c_info_addr - fifo->rsvd_boundary + 1;
21812774f206SBjoern A. Zeeb 	bool random_seq = req->flags & NL80211_SCAN_FLAG_RANDOM_SN;
21822774f206SBjoern A. Zeeb 	u8 h2c_pkt[H2C_PKT_SIZE] = {0};
21832774f206SBjoern A. Zeeb 
21842774f206SBjoern A. Zeeb 	rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_SCAN_OFFLOAD);
21852774f206SBjoern A. Zeeb 	SET_PKT_H2C_TOTAL_LEN(h2c_pkt, H2C_PKT_CH_SWITCH_LEN);
21862774f206SBjoern A. Zeeb 
21872774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_START(h2c_pkt, opt->switch_en);
21882774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_BACK_OP_EN(h2c_pkt, opt->back_op_en);
21892774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_RANDOM_SEQ_EN(h2c_pkt, random_seq);
21902774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_NO_CCK_EN(h2c_pkt, req->no_cck);
21912774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_CH_NUM(h2c_pkt, list->ch_num);
21922774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_CH_INFO_SIZE(h2c_pkt, list->size);
21932774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_CH_INFO_LOC(h2c_pkt, list->addr - fifo->rsvd_boundary);
21942774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_OP_CH(h2c_pkt, scan_info->op_chan);
21952774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_OP_PRI_CH_IDX(h2c_pkt, scan_info->op_pri_ch_idx);
21962774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_OP_BW(h2c_pkt, scan_info->op_bw);
21972774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_OP_PORT_ID(h2c_pkt, rtwvif->port);
21982774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_OP_DWELL_TIME(h2c_pkt, req->duration_mandatory ?
21992774f206SBjoern A. Zeeb 				       req->duration : RTW_CHANNEL_TIME);
22002774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_OP_GAP_TIME(h2c_pkt, RTW_OFF_CHAN_TIME);
22012774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_SSID_NUM(h2c_pkt, req->n_ssids);
22022774f206SBjoern A. Zeeb 	SCAN_OFFLOAD_SET_PKT_LOC(h2c_pkt, pkt_loc);
22032774f206SBjoern A. Zeeb 
22042774f206SBjoern A. Zeeb 	rtw_fw_send_h2c_packet(rtwdev, h2c_pkt);
22052774f206SBjoern A. Zeeb }
22062774f206SBjoern A. Zeeb 
22072774f206SBjoern A. Zeeb void rtw_hw_scan_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
22082774f206SBjoern A. Zeeb 		       struct ieee80211_scan_request *scan_req)
22092774f206SBjoern A. Zeeb {
22102774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
22112774f206SBjoern A. Zeeb 	struct cfg80211_scan_request *req = &scan_req->req;
22122774f206SBjoern A. Zeeb 	u8 mac_addr[ETH_ALEN];
22132774f206SBjoern A. Zeeb 
22142774f206SBjoern A. Zeeb 	rtwdev->scan_info.scanning_vif = vif;
22152774f206SBjoern A. Zeeb 	rtwvif->scan_ies = &scan_req->ies;
22162774f206SBjoern A. Zeeb 	rtwvif->scan_req = req;
22172774f206SBjoern A. Zeeb 
22182774f206SBjoern A. Zeeb 	ieee80211_stop_queues(rtwdev->hw);
221990aac0d8SBjoern A. Zeeb 	rtw_leave_lps_deep(rtwdev);
222090aac0d8SBjoern A. Zeeb 	rtw_hci_flush_all_queues(rtwdev, false);
222190aac0d8SBjoern A. Zeeb 	rtw_mac_flush_all_queues(rtwdev, false);
22222774f206SBjoern A. Zeeb 	if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR)
22232774f206SBjoern A. Zeeb 		get_random_mask_addr(mac_addr, req->mac_addr,
22242774f206SBjoern A. Zeeb 				     req->mac_addr_mask);
22252774f206SBjoern A. Zeeb 	else
22262774f206SBjoern A. Zeeb 		ether_addr_copy(mac_addr, vif->addr);
22272774f206SBjoern A. Zeeb 
22282774f206SBjoern A. Zeeb 	rtw_core_scan_start(rtwdev, rtwvif, mac_addr, true);
22292774f206SBjoern A. Zeeb 
22302774f206SBjoern A. Zeeb 	rtwdev->hal.rcr &= ~BIT_CBSSID_BCN;
22312774f206SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
22322774f206SBjoern A. Zeeb }
22332774f206SBjoern A. Zeeb 
22342774f206SBjoern A. Zeeb void rtw_hw_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
22352774f206SBjoern A. Zeeb 			  bool aborted)
22362774f206SBjoern A. Zeeb {
22372774f206SBjoern A. Zeeb 	struct cfg80211_scan_info info = {
22382774f206SBjoern A. Zeeb 		.aborted = aborted,
22392774f206SBjoern A. Zeeb 	};
22409c951734SBjoern A. Zeeb 	struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
22419c951734SBjoern A. Zeeb 	struct rtw_hal *hal = &rtwdev->hal;
22422774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif;
22439c951734SBjoern A. Zeeb 	u8 chan = scan_info->op_chan;
22442774f206SBjoern A. Zeeb 
22452774f206SBjoern A. Zeeb 	if (!vif)
22462774f206SBjoern A. Zeeb 		return;
22472774f206SBjoern A. Zeeb 
22482774f206SBjoern A. Zeeb 	rtwdev->hal.rcr |= BIT_CBSSID_BCN;
22492774f206SBjoern A. Zeeb 	rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr);
22502774f206SBjoern A. Zeeb 
22519c951734SBjoern A. Zeeb 	rtw_core_scan_complete(rtwdev, vif, true);
22522774f206SBjoern A. Zeeb 
22539c951734SBjoern A. Zeeb 	rtwvif = (struct rtw_vif *)vif->drv_priv;
225490aac0d8SBjoern A. Zeeb 	if (chan)
225590aac0d8SBjoern A. Zeeb 		rtw_store_op_chan(rtwdev, false);
225690aac0d8SBjoern A. Zeeb 	rtw_phy_set_tx_power_level(rtwdev, hal->current_channel);
22572774f206SBjoern A. Zeeb 	ieee80211_wake_queues(rtwdev->hw);
22582774f206SBjoern A. Zeeb 	ieee80211_scan_completed(rtwdev->hw, &info);
22592774f206SBjoern A. Zeeb 
22602774f206SBjoern A. Zeeb 	rtwvif->scan_req = NULL;
22612774f206SBjoern A. Zeeb 	rtwvif->scan_ies = NULL;
22622774f206SBjoern A. Zeeb 	rtwdev->scan_info.scanning_vif = NULL;
22632774f206SBjoern A. Zeeb }
22642774f206SBjoern A. Zeeb 
22652774f206SBjoern A. Zeeb static int rtw_hw_scan_prehandle(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif,
22662774f206SBjoern A. Zeeb 				 struct rtw_chan_list *list)
22672774f206SBjoern A. Zeeb {
22682774f206SBjoern A. Zeeb 	struct cfg80211_scan_request *req = rtwvif->scan_req;
22692774f206SBjoern A. Zeeb 	int size = req->n_channels * (RTW_CH_INFO_SIZE + RTW_EX_CH_INFO_SIZE);
22702774f206SBjoern A. Zeeb 	u8 *buf;
22712774f206SBjoern A. Zeeb 	int ret;
22722774f206SBjoern A. Zeeb 
22732774f206SBjoern A. Zeeb 	buf = kmalloc(size, GFP_KERNEL);
22742774f206SBjoern A. Zeeb 	if (!buf)
22752774f206SBjoern A. Zeeb 		return -ENOMEM;
22762774f206SBjoern A. Zeeb 
22772774f206SBjoern A. Zeeb 	ret = rtw_hw_scan_update_probe_req(rtwdev, rtwvif);
22782774f206SBjoern A. Zeeb 	if (ret) {
22792774f206SBjoern A. Zeeb 		rtw_err(rtwdev, "Update probe request failed\n");
22802774f206SBjoern A. Zeeb 		goto out;
22812774f206SBjoern A. Zeeb 	}
22822774f206SBjoern A. Zeeb 
22832774f206SBjoern A. Zeeb 	list->buf_size = size;
22842774f206SBjoern A. Zeeb 	list->size = 0;
22852774f206SBjoern A. Zeeb 	list->ch_num = 0;
22862774f206SBjoern A. Zeeb 	ret = rtw_add_chan_list(rtwdev, rtwvif, list, buf);
22872774f206SBjoern A. Zeeb out:
22882774f206SBjoern A. Zeeb 	kfree(buf);
22892774f206SBjoern A. Zeeb 
22902774f206SBjoern A. Zeeb 	return ret;
22912774f206SBjoern A. Zeeb }
22922774f206SBjoern A. Zeeb 
22932774f206SBjoern A. Zeeb int rtw_hw_scan_offload(struct rtw_dev *rtwdev, struct ieee80211_vif *vif,
22942774f206SBjoern A. Zeeb 			bool enable)
22952774f206SBjoern A. Zeeb {
22962774f206SBjoern A. Zeeb 	struct rtw_vif *rtwvif = vif ? (struct rtw_vif *)vif->drv_priv : NULL;
229790aac0d8SBjoern A. Zeeb 	struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
22982774f206SBjoern A. Zeeb 	struct rtw_ch_switch_option cs_option = {0};
22992774f206SBjoern A. Zeeb 	struct rtw_chan_list chan_list = {0};
23002774f206SBjoern A. Zeeb 	int ret = 0;
23012774f206SBjoern A. Zeeb 
23022774f206SBjoern A. Zeeb 	if (!rtwvif)
23032774f206SBjoern A. Zeeb 		return -EINVAL;
23042774f206SBjoern A. Zeeb 
23052774f206SBjoern A. Zeeb 	cs_option.switch_en = enable;
230690aac0d8SBjoern A. Zeeb 	cs_option.back_op_en = scan_info->op_chan != 0;
23072774f206SBjoern A. Zeeb 	if (enable) {
23082774f206SBjoern A. Zeeb 		ret = rtw_hw_scan_prehandle(rtwdev, rtwvif, &chan_list);
23092774f206SBjoern A. Zeeb 		if (ret)
23102774f206SBjoern A. Zeeb 			goto out;
23112774f206SBjoern A. Zeeb 	}
23122774f206SBjoern A. Zeeb 	rtw_fw_set_scan_offload(rtwdev, &cs_option, rtwvif, &chan_list);
23132774f206SBjoern A. Zeeb out:
231490aac0d8SBjoern A. Zeeb 	if (rtwdev->ap_active) {
231590aac0d8SBjoern A. Zeeb 		ret = rtw_download_beacon(rtwdev);
231690aac0d8SBjoern A. Zeeb 		if (ret)
231790aac0d8SBjoern A. Zeeb 			rtw_err(rtwdev, "HW scan download beacon failed\n");
231890aac0d8SBjoern A. Zeeb 	}
231990aac0d8SBjoern A. Zeeb 
23202774f206SBjoern A. Zeeb 	return ret;
23212774f206SBjoern A. Zeeb }
23222774f206SBjoern A. Zeeb 
232390aac0d8SBjoern A. Zeeb void rtw_hw_scan_abort(struct rtw_dev *rtwdev)
23242774f206SBjoern A. Zeeb {
232590aac0d8SBjoern A. Zeeb 	struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif;
232690aac0d8SBjoern A. Zeeb 
23272774f206SBjoern A. Zeeb 	if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD))
23282774f206SBjoern A. Zeeb 		return;
23292774f206SBjoern A. Zeeb 
23302774f206SBjoern A. Zeeb 	rtw_hw_scan_offload(rtwdev, vif, false);
23312774f206SBjoern A. Zeeb 	rtw_hw_scan_complete(rtwdev, vif, true);
23322774f206SBjoern A. Zeeb }
23332774f206SBjoern A. Zeeb 
23342774f206SBjoern A. Zeeb void rtw_hw_scan_status_report(struct rtw_dev *rtwdev, struct sk_buff *skb)
23352774f206SBjoern A. Zeeb {
23362774f206SBjoern A. Zeeb 	struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif;
23372774f206SBjoern A. Zeeb 	struct rtw_c2h_cmd *c2h;
23382774f206SBjoern A. Zeeb 	bool aborted;
23392774f206SBjoern A. Zeeb 	u8 rc;
23402774f206SBjoern A. Zeeb 
23412774f206SBjoern A. Zeeb 	if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
23422774f206SBjoern A. Zeeb 		return;
23432774f206SBjoern A. Zeeb 
23442774f206SBjoern A. Zeeb 	c2h = get_c2h_from_skb(skb);
23452774f206SBjoern A. Zeeb 	rc = GET_SCAN_REPORT_RETURN_CODE(c2h->payload);
23462774f206SBjoern A. Zeeb 	aborted = rc != RTW_SCAN_REPORT_SUCCESS;
23472774f206SBjoern A. Zeeb 	rtw_hw_scan_complete(rtwdev, vif, aborted);
23482774f206SBjoern A. Zeeb 
23492774f206SBjoern A. Zeeb 	if (aborted)
23509c951734SBjoern A. Zeeb 		rtw_dbg(rtwdev, RTW_DBG_HW_SCAN, "HW scan aborted with code: %d\n", rc);
23512774f206SBjoern A. Zeeb }
23522774f206SBjoern A. Zeeb 
235390aac0d8SBjoern A. Zeeb void rtw_store_op_chan(struct rtw_dev *rtwdev, bool backup)
23542774f206SBjoern A. Zeeb {
23552774f206SBjoern A. Zeeb 	struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
23562774f206SBjoern A. Zeeb 	struct rtw_hal *hal = &rtwdev->hal;
235790aac0d8SBjoern A. Zeeb 	u8 band;
23582774f206SBjoern A. Zeeb 
235990aac0d8SBjoern A. Zeeb 	if (backup) {
23602774f206SBjoern A. Zeeb 		scan_info->op_chan = hal->current_channel;
23612774f206SBjoern A. Zeeb 		scan_info->op_bw = hal->current_band_width;
23622774f206SBjoern A. Zeeb 		scan_info->op_pri_ch_idx = hal->current_primary_channel_index;
236390aac0d8SBjoern A. Zeeb 		scan_info->op_pri_ch = hal->primary_channel;
236490aac0d8SBjoern A. Zeeb 	} else {
236590aac0d8SBjoern A. Zeeb 		band = scan_info->op_chan > 14 ? RTW_BAND_5G : RTW_BAND_2G;
236690aac0d8SBjoern A. Zeeb 		rtw_update_channel(rtwdev, scan_info->op_chan,
236790aac0d8SBjoern A. Zeeb 				   scan_info->op_pri_ch,
236890aac0d8SBjoern A. Zeeb 				   band, scan_info->op_bw);
236990aac0d8SBjoern A. Zeeb 	}
237090aac0d8SBjoern A. Zeeb }
237190aac0d8SBjoern A. Zeeb 
237290aac0d8SBjoern A. Zeeb void rtw_clear_op_chan(struct rtw_dev *rtwdev)
237390aac0d8SBjoern A. Zeeb {
237490aac0d8SBjoern A. Zeeb 	struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
237590aac0d8SBjoern A. Zeeb 
237690aac0d8SBjoern A. Zeeb 	scan_info->op_chan = 0;
237790aac0d8SBjoern A. Zeeb 	scan_info->op_bw = 0;
237890aac0d8SBjoern A. Zeeb 	scan_info->op_pri_ch_idx = 0;
237990aac0d8SBjoern A. Zeeb 	scan_info->op_pri_ch = 0;
23802774f206SBjoern A. Zeeb }
23812774f206SBjoern A. Zeeb 
23822774f206SBjoern A. Zeeb static bool rtw_is_op_chan(struct rtw_dev *rtwdev, u8 channel)
23832774f206SBjoern A. Zeeb {
23842774f206SBjoern A. Zeeb 	struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info;
23852774f206SBjoern A. Zeeb 
23862774f206SBjoern A. Zeeb 	return channel == scan_info->op_chan;
23872774f206SBjoern A. Zeeb }
23882774f206SBjoern A. Zeeb 
23892774f206SBjoern A. Zeeb void rtw_hw_scan_chan_switch(struct rtw_dev *rtwdev, struct sk_buff *skb)
23902774f206SBjoern A. Zeeb {
23912774f206SBjoern A. Zeeb 	struct rtw_hal *hal = &rtwdev->hal;
23922774f206SBjoern A. Zeeb 	struct rtw_c2h_cmd *c2h;
23932774f206SBjoern A. Zeeb 	enum rtw_scan_notify_id id;
239490aac0d8SBjoern A. Zeeb 	u8 chan, band, status;
23952774f206SBjoern A. Zeeb 
23969c951734SBjoern A. Zeeb 	if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
23979c951734SBjoern A. Zeeb 		return;
23989c951734SBjoern A. Zeeb 
23992774f206SBjoern A. Zeeb 	c2h = get_c2h_from_skb(skb);
24002774f206SBjoern A. Zeeb 	chan = GET_CHAN_SWITCH_CENTRAL_CH(c2h->payload);
24012774f206SBjoern A. Zeeb 	id = GET_CHAN_SWITCH_ID(c2h->payload);
24022774f206SBjoern A. Zeeb 	status = GET_CHAN_SWITCH_STATUS(c2h->payload);
24032774f206SBjoern A. Zeeb 
24042774f206SBjoern A. Zeeb 	if (id == RTW_SCAN_NOTIFY_ID_POSTSWITCH) {
240590aac0d8SBjoern A. Zeeb 		band = chan > 14 ? RTW_BAND_5G : RTW_BAND_2G;
240690aac0d8SBjoern A. Zeeb 		rtw_update_channel(rtwdev, chan, chan, band,
240790aac0d8SBjoern A. Zeeb 				   RTW_CHANNEL_WIDTH_20);
240890aac0d8SBjoern A. Zeeb 		if (rtw_is_op_chan(rtwdev, chan)) {
240990aac0d8SBjoern A. Zeeb 			rtw_store_op_chan(rtwdev, false);
24102774f206SBjoern A. Zeeb 			ieee80211_wake_queues(rtwdev->hw);
241190aac0d8SBjoern A. Zeeb 			rtw_core_enable_beacon(rtwdev, true);
241290aac0d8SBjoern A. Zeeb 		}
24132774f206SBjoern A. Zeeb 	} else if (id == RTW_SCAN_NOTIFY_ID_PRESWITCH) {
24142774f206SBjoern A. Zeeb 		if (IS_CH_5G_BAND(chan)) {
24152774f206SBjoern A. Zeeb 			rtw_coex_switchband_notify(rtwdev, COEX_SWITCH_TO_5G);
24162774f206SBjoern A. Zeeb 		} else if (IS_CH_2G_BAND(chan)) {
24172774f206SBjoern A. Zeeb 			u8 chan_type;
24182774f206SBjoern A. Zeeb 
24192774f206SBjoern A. Zeeb 			if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags))
24202774f206SBjoern A. Zeeb 				chan_type = COEX_SWITCH_TO_24G;
24212774f206SBjoern A. Zeeb 			else
24222774f206SBjoern A. Zeeb 				chan_type = COEX_SWITCH_TO_24G_NOFORSCAN;
24232774f206SBjoern A. Zeeb 			rtw_coex_switchband_notify(rtwdev, chan_type);
24242774f206SBjoern A. Zeeb 		}
242590aac0d8SBjoern A. Zeeb 		/* The channel of C2H RTW_SCAN_NOTIFY_ID_PRESWITCH is next
242690aac0d8SBjoern A. Zeeb 		 * channel that hardware will switch. We need to stop queue
242790aac0d8SBjoern A. Zeeb 		 * if next channel is non-op channel.
242890aac0d8SBjoern A. Zeeb 		 */
242990aac0d8SBjoern A. Zeeb 		if (!rtw_is_op_chan(rtwdev, chan) &&
243090aac0d8SBjoern A. Zeeb 		    rtw_is_op_chan(rtwdev, hal->current_channel)) {
243190aac0d8SBjoern A. Zeeb 			rtw_core_enable_beacon(rtwdev, false);
24322774f206SBjoern A. Zeeb 			ieee80211_stop_queues(rtwdev->hw);
24332774f206SBjoern A. Zeeb 		}
243490aac0d8SBjoern A. Zeeb 	}
24352774f206SBjoern A. Zeeb 
24362774f206SBjoern A. Zeeb 	rtw_dbg(rtwdev, RTW_DBG_HW_SCAN,
24372774f206SBjoern A. Zeeb 		"Chan switch: %x, id: %x, status: %x\n", chan, id, status);
24382774f206SBjoern A. Zeeb }
2439