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