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