162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 262306a36Sopenharmony_ci/* Copyright(c) 2018-2019 Realtek Corporation 362306a36Sopenharmony_ci */ 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci#include <linux/iopoll.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "main.h" 862306a36Sopenharmony_ci#include "coex.h" 962306a36Sopenharmony_ci#include "fw.h" 1062306a36Sopenharmony_ci#include "tx.h" 1162306a36Sopenharmony_ci#include "reg.h" 1262306a36Sopenharmony_ci#include "sec.h" 1362306a36Sopenharmony_ci#include "debug.h" 1462306a36Sopenharmony_ci#include "util.h" 1562306a36Sopenharmony_ci#include "wow.h" 1662306a36Sopenharmony_ci#include "ps.h" 1762306a36Sopenharmony_ci#include "phy.h" 1862306a36Sopenharmony_ci#include "mac.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic void rtw_fw_c2h_cmd_handle_ext(struct rtw_dev *rtwdev, 2162306a36Sopenharmony_ci struct sk_buff *skb) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct rtw_c2h_cmd *c2h; 2462306a36Sopenharmony_ci u8 sub_cmd_id; 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci c2h = get_c2h_from_skb(skb); 2762306a36Sopenharmony_ci sub_cmd_id = c2h->payload[0]; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci switch (sub_cmd_id) { 3062306a36Sopenharmony_ci case C2H_CCX_RPT: 3162306a36Sopenharmony_ci rtw_tx_report_handle(rtwdev, skb, C2H_CCX_RPT); 3262306a36Sopenharmony_ci break; 3362306a36Sopenharmony_ci case C2H_SCAN_STATUS_RPT: 3462306a36Sopenharmony_ci rtw_hw_scan_status_report(rtwdev, skb); 3562306a36Sopenharmony_ci break; 3662306a36Sopenharmony_ci case C2H_CHAN_SWITCH: 3762306a36Sopenharmony_ci rtw_hw_scan_chan_switch(rtwdev, skb); 3862306a36Sopenharmony_ci break; 3962306a36Sopenharmony_ci default: 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic u16 get_max_amsdu_len(u32 bit_rate) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci /* lower than ofdm, do not aggregate */ 4762306a36Sopenharmony_ci if (bit_rate < 550) 4862306a36Sopenharmony_ci return 1; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci /* lower than 20M 2ss mcs8, make it small */ 5162306a36Sopenharmony_ci if (bit_rate < 1800) 5262306a36Sopenharmony_ci return 1200; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci /* lower than 40M 2ss mcs9, make it medium */ 5562306a36Sopenharmony_ci if (bit_rate < 4000) 5662306a36Sopenharmony_ci return 2600; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* not yet 80M 2ss mcs8/9, make it twice regular packet size */ 5962306a36Sopenharmony_ci if (bit_rate < 7000) 6062306a36Sopenharmony_ci return 3500; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* unlimited */ 6362306a36Sopenharmony_ci return 0; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistruct rtw_fw_iter_ra_data { 6762306a36Sopenharmony_ci struct rtw_dev *rtwdev; 6862306a36Sopenharmony_ci u8 *payload; 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic void rtw_fw_ra_report_iter(void *data, struct ieee80211_sta *sta) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct rtw_fw_iter_ra_data *ra_data = data; 7462306a36Sopenharmony_ci struct rtw_sta_info *si = (struct rtw_sta_info *)sta->drv_priv; 7562306a36Sopenharmony_ci u8 mac_id, rate, sgi, bw; 7662306a36Sopenharmony_ci u8 mcs, nss; 7762306a36Sopenharmony_ci u32 bit_rate; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci mac_id = GET_RA_REPORT_MACID(ra_data->payload); 8062306a36Sopenharmony_ci if (si->mac_id != mac_id) 8162306a36Sopenharmony_ci return; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci si->ra_report.txrate.flags = 0; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci rate = GET_RA_REPORT_RATE(ra_data->payload); 8662306a36Sopenharmony_ci sgi = GET_RA_REPORT_SGI(ra_data->payload); 8762306a36Sopenharmony_ci bw = GET_RA_REPORT_BW(ra_data->payload); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (rate < DESC_RATEMCS0) { 9062306a36Sopenharmony_ci si->ra_report.txrate.legacy = rtw_desc_to_bitrate(rate); 9162306a36Sopenharmony_ci goto legacy; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci rtw_desc_to_mcsrate(rate, &mcs, &nss); 9562306a36Sopenharmony_ci if (rate >= DESC_RATEVHT1SS_MCS0) 9662306a36Sopenharmony_ci si->ra_report.txrate.flags |= RATE_INFO_FLAGS_VHT_MCS; 9762306a36Sopenharmony_ci else if (rate >= DESC_RATEMCS0) 9862306a36Sopenharmony_ci si->ra_report.txrate.flags |= RATE_INFO_FLAGS_MCS; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (rate >= DESC_RATEMCS0) { 10162306a36Sopenharmony_ci si->ra_report.txrate.mcs = mcs; 10262306a36Sopenharmony_ci si->ra_report.txrate.nss = nss; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (sgi) 10662306a36Sopenharmony_ci si->ra_report.txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (bw == RTW_CHANNEL_WIDTH_80) 10962306a36Sopenharmony_ci si->ra_report.txrate.bw = RATE_INFO_BW_80; 11062306a36Sopenharmony_ci else if (bw == RTW_CHANNEL_WIDTH_40) 11162306a36Sopenharmony_ci si->ra_report.txrate.bw = RATE_INFO_BW_40; 11262306a36Sopenharmony_ci else 11362306a36Sopenharmony_ci si->ra_report.txrate.bw = RATE_INFO_BW_20; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cilegacy: 11662306a36Sopenharmony_ci bit_rate = cfg80211_calculate_bitrate(&si->ra_report.txrate); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci si->ra_report.desc_rate = rate; 11962306a36Sopenharmony_ci si->ra_report.bit_rate = bit_rate; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci sta->deflink.agg.max_rc_amsdu_len = get_max_amsdu_len(bit_rate); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void rtw_fw_ra_report_handle(struct rtw_dev *rtwdev, u8 *payload, 12562306a36Sopenharmony_ci u8 length) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct rtw_fw_iter_ra_data ra_data; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (WARN(length < 7, "invalid ra report c2h length\n")) 13062306a36Sopenharmony_ci return; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci rtwdev->dm_info.tx_rate = GET_RA_REPORT_RATE(payload); 13362306a36Sopenharmony_ci ra_data.rtwdev = rtwdev; 13462306a36Sopenharmony_ci ra_data.payload = payload; 13562306a36Sopenharmony_ci rtw_iterate_stas_atomic(rtwdev, rtw_fw_ra_report_iter, &ra_data); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistruct rtw_beacon_filter_iter_data { 13962306a36Sopenharmony_ci struct rtw_dev *rtwdev; 14062306a36Sopenharmony_ci u8 *payload; 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void rtw_fw_bcn_filter_notify_vif_iter(void *data, 14462306a36Sopenharmony_ci struct ieee80211_vif *vif) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct rtw_beacon_filter_iter_data *iter_data = data; 14762306a36Sopenharmony_ci struct rtw_dev *rtwdev = iter_data->rtwdev; 14862306a36Sopenharmony_ci u8 *payload = iter_data->payload; 14962306a36Sopenharmony_ci u8 type = GET_BCN_FILTER_NOTIFY_TYPE(payload); 15062306a36Sopenharmony_ci u8 event = GET_BCN_FILTER_NOTIFY_EVENT(payload); 15162306a36Sopenharmony_ci s8 sig = (s8)GET_BCN_FILTER_NOTIFY_RSSI(payload); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci switch (type) { 15462306a36Sopenharmony_ci case BCN_FILTER_NOTIFY_SIGNAL_CHANGE: 15562306a36Sopenharmony_ci event = event ? NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH : 15662306a36Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW; 15762306a36Sopenharmony_ci ieee80211_cqm_rssi_notify(vif, event, sig, GFP_KERNEL); 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci case BCN_FILTER_CONNECTION_LOSS: 16062306a36Sopenharmony_ci ieee80211_connection_loss(vif); 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci case BCN_FILTER_CONNECTED: 16362306a36Sopenharmony_ci rtwdev->beacon_loss = false; 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci case BCN_FILTER_NOTIFY_BEACON_LOSS: 16662306a36Sopenharmony_ci rtwdev->beacon_loss = true; 16762306a36Sopenharmony_ci rtw_leave_lps(rtwdev); 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void rtw_fw_bcn_filter_notify(struct rtw_dev *rtwdev, u8 *payload, 17362306a36Sopenharmony_ci u8 length) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct rtw_beacon_filter_iter_data dev_iter_data; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci dev_iter_data.rtwdev = rtwdev; 17862306a36Sopenharmony_ci dev_iter_data.payload = payload; 17962306a36Sopenharmony_ci rtw_iterate_vifs(rtwdev, rtw_fw_bcn_filter_notify_vif_iter, 18062306a36Sopenharmony_ci &dev_iter_data); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void rtw_fw_scan_result(struct rtw_dev *rtwdev, u8 *payload, 18462306a36Sopenharmony_ci u8 length) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct rtw_dm_info *dm_info = &rtwdev->dm_info; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci dm_info->scan_density = payload[0]; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, "scan.density = %x\n", 19162306a36Sopenharmony_ci dm_info->scan_density); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void rtw_fw_adaptivity_result(struct rtw_dev *rtwdev, u8 *payload, 19562306a36Sopenharmony_ci u8 length) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct rtw_hw_reg_offset *edcca_th = rtwdev->chip->edcca_th; 19862306a36Sopenharmony_ci struct rtw_c2h_adaptivity *result = (struct rtw_c2h_adaptivity *)payload; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, 20162306a36Sopenharmony_ci "Adaptivity: density %x igi %x l2h_th_init %x l2h %x h2l %x option %x\n", 20262306a36Sopenharmony_ci result->density, result->igi, result->l2h_th_init, result->l2h, 20362306a36Sopenharmony_ci result->h2l, result->option); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "Reg Setting: L2H %x H2L %x\n", 20662306a36Sopenharmony_ci rtw_read32_mask(rtwdev, edcca_th[EDCCA_TH_L2H_IDX].hw_reg.addr, 20762306a36Sopenharmony_ci edcca_th[EDCCA_TH_L2H_IDX].hw_reg.mask), 20862306a36Sopenharmony_ci rtw_read32_mask(rtwdev, edcca_th[EDCCA_TH_H2L_IDX].hw_reg.addr, 20962306a36Sopenharmony_ci edcca_th[EDCCA_TH_H2L_IDX].hw_reg.mask)); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, "EDCCA Flag %s\n", 21262306a36Sopenharmony_ci rtw_read32_mask(rtwdev, REG_EDCCA_REPORT, BIT_EDCCA_FLAG) ? 21362306a36Sopenharmony_ci "Set" : "Unset"); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_civoid rtw_fw_c2h_cmd_handle(struct rtw_dev *rtwdev, struct sk_buff *skb) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci struct rtw_c2h_cmd *c2h; 21962306a36Sopenharmony_ci u32 pkt_offset; 22062306a36Sopenharmony_ci u8 len; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci pkt_offset = *((u32 *)skb->cb); 22362306a36Sopenharmony_ci c2h = (struct rtw_c2h_cmd *)(skb->data + pkt_offset); 22462306a36Sopenharmony_ci len = skb->len - pkt_offset - 2; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci mutex_lock(&rtwdev->mutex); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (!test_bit(RTW_FLAG_RUNNING, rtwdev->flags)) 22962306a36Sopenharmony_ci goto unlock; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci switch (c2h->id) { 23262306a36Sopenharmony_ci case C2H_CCX_TX_RPT: 23362306a36Sopenharmony_ci rtw_tx_report_handle(rtwdev, skb, C2H_CCX_TX_RPT); 23462306a36Sopenharmony_ci break; 23562306a36Sopenharmony_ci case C2H_BT_INFO: 23662306a36Sopenharmony_ci rtw_coex_bt_info_notify(rtwdev, c2h->payload, len); 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci case C2H_BT_HID_INFO: 23962306a36Sopenharmony_ci rtw_coex_bt_hid_info_notify(rtwdev, c2h->payload, len); 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci case C2H_WLAN_INFO: 24262306a36Sopenharmony_ci rtw_coex_wl_fwdbginfo_notify(rtwdev, c2h->payload, len); 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci case C2H_BCN_FILTER_NOTIFY: 24562306a36Sopenharmony_ci rtw_fw_bcn_filter_notify(rtwdev, c2h->payload, len); 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci case C2H_HALMAC: 24862306a36Sopenharmony_ci rtw_fw_c2h_cmd_handle_ext(rtwdev, skb); 24962306a36Sopenharmony_ci break; 25062306a36Sopenharmony_ci case C2H_RA_RPT: 25162306a36Sopenharmony_ci rtw_fw_ra_report_handle(rtwdev, c2h->payload, len); 25262306a36Sopenharmony_ci break; 25362306a36Sopenharmony_ci default: 25462306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, "C2H 0x%x isn't handled\n", c2h->id); 25562306a36Sopenharmony_ci break; 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ciunlock: 25962306a36Sopenharmony_ci mutex_unlock(&rtwdev->mutex); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_civoid rtw_fw_c2h_cmd_rx_irqsafe(struct rtw_dev *rtwdev, u32 pkt_offset, 26362306a36Sopenharmony_ci struct sk_buff *skb) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct rtw_c2h_cmd *c2h; 26662306a36Sopenharmony_ci u8 len; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci c2h = (struct rtw_c2h_cmd *)(skb->data + pkt_offset); 26962306a36Sopenharmony_ci len = skb->len - pkt_offset - 2; 27062306a36Sopenharmony_ci *((u32 *)skb->cb) = pkt_offset; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, "recv C2H, id=0x%02x, seq=0x%02x, len=%d\n", 27362306a36Sopenharmony_ci c2h->id, c2h->seq, len); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci switch (c2h->id) { 27662306a36Sopenharmony_ci case C2H_BT_MP_INFO: 27762306a36Sopenharmony_ci rtw_coex_info_response(rtwdev, skb); 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci case C2H_WLAN_RFON: 28062306a36Sopenharmony_ci complete(&rtwdev->lps_leave_check); 28162306a36Sopenharmony_ci dev_kfree_skb_any(skb); 28262306a36Sopenharmony_ci break; 28362306a36Sopenharmony_ci case C2H_SCAN_RESULT: 28462306a36Sopenharmony_ci complete(&rtwdev->fw_scan_density); 28562306a36Sopenharmony_ci rtw_fw_scan_result(rtwdev, c2h->payload, len); 28662306a36Sopenharmony_ci dev_kfree_skb_any(skb); 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci case C2H_ADAPTIVITY: 28962306a36Sopenharmony_ci rtw_fw_adaptivity_result(rtwdev, c2h->payload, len); 29062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci default: 29362306a36Sopenharmony_ci /* pass offset for further operation */ 29462306a36Sopenharmony_ci *((u32 *)skb->cb) = pkt_offset; 29562306a36Sopenharmony_ci skb_queue_tail(&rtwdev->c2h_queue, skb); 29662306a36Sopenharmony_ci ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work); 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ciEXPORT_SYMBOL(rtw_fw_c2h_cmd_rx_irqsafe); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_civoid rtw_fw_c2h_cmd_isr(struct rtw_dev *rtwdev) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci if (rtw_read8(rtwdev, REG_MCU_TST_CFG) == VAL_FW_TRIGGER) 30562306a36Sopenharmony_ci rtw_fw_recovery(rtwdev); 30662306a36Sopenharmony_ci else 30762306a36Sopenharmony_ci rtw_warn(rtwdev, "unhandled firmware c2h interrupt\n"); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ciEXPORT_SYMBOL(rtw_fw_c2h_cmd_isr); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_cistatic void rtw_fw_send_h2c_command_register(struct rtw_dev *rtwdev, 31262306a36Sopenharmony_ci struct rtw_h2c_register *h2c) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci u32 box_reg, box_ex_reg; 31562306a36Sopenharmony_ci u8 box_state, box; 31662306a36Sopenharmony_ci int ret; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, "send H2C content %08x %08x\n", h2c->w0, 31962306a36Sopenharmony_ci h2c->w1); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci lockdep_assert_held(&rtwdev->mutex); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci box = rtwdev->h2c.last_box_num; 32462306a36Sopenharmony_ci switch (box) { 32562306a36Sopenharmony_ci case 0: 32662306a36Sopenharmony_ci box_reg = REG_HMEBOX0; 32762306a36Sopenharmony_ci box_ex_reg = REG_HMEBOX0_EX; 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci case 1: 33062306a36Sopenharmony_ci box_reg = REG_HMEBOX1; 33162306a36Sopenharmony_ci box_ex_reg = REG_HMEBOX1_EX; 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case 2: 33462306a36Sopenharmony_ci box_reg = REG_HMEBOX2; 33562306a36Sopenharmony_ci box_ex_reg = REG_HMEBOX2_EX; 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci case 3: 33862306a36Sopenharmony_ci box_reg = REG_HMEBOX3; 33962306a36Sopenharmony_ci box_ex_reg = REG_HMEBOX3_EX; 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci default: 34262306a36Sopenharmony_ci WARN(1, "invalid h2c mail box number\n"); 34362306a36Sopenharmony_ci return; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ret = read_poll_timeout_atomic(rtw_read8, box_state, 34762306a36Sopenharmony_ci !((box_state >> box) & 0x1), 100, 3000, 34862306a36Sopenharmony_ci false, rtwdev, REG_HMETFR); 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (ret) { 35162306a36Sopenharmony_ci rtw_err(rtwdev, "failed to send h2c command\n"); 35262306a36Sopenharmony_ci return; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci rtw_write32(rtwdev, box_ex_reg, h2c->w1); 35662306a36Sopenharmony_ci rtw_write32(rtwdev, box_reg, h2c->w0); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (++rtwdev->h2c.last_box_num >= 4) 35962306a36Sopenharmony_ci rtwdev->h2c.last_box_num = 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic void rtw_fw_send_h2c_command(struct rtw_dev *rtwdev, 36362306a36Sopenharmony_ci u8 *h2c) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct rtw_h2c_cmd *h2c_cmd = (struct rtw_h2c_cmd *)h2c; 36662306a36Sopenharmony_ci u8 box; 36762306a36Sopenharmony_ci u8 box_state; 36862306a36Sopenharmony_ci u32 box_reg, box_ex_reg; 36962306a36Sopenharmony_ci int ret; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, 37262306a36Sopenharmony_ci "send H2C content %02x%02x%02x%02x %02x%02x%02x%02x\n", 37362306a36Sopenharmony_ci h2c[3], h2c[2], h2c[1], h2c[0], 37462306a36Sopenharmony_ci h2c[7], h2c[6], h2c[5], h2c[4]); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci lockdep_assert_held(&rtwdev->mutex); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci box = rtwdev->h2c.last_box_num; 37962306a36Sopenharmony_ci switch (box) { 38062306a36Sopenharmony_ci case 0: 38162306a36Sopenharmony_ci box_reg = REG_HMEBOX0; 38262306a36Sopenharmony_ci box_ex_reg = REG_HMEBOX0_EX; 38362306a36Sopenharmony_ci break; 38462306a36Sopenharmony_ci case 1: 38562306a36Sopenharmony_ci box_reg = REG_HMEBOX1; 38662306a36Sopenharmony_ci box_ex_reg = REG_HMEBOX1_EX; 38762306a36Sopenharmony_ci break; 38862306a36Sopenharmony_ci case 2: 38962306a36Sopenharmony_ci box_reg = REG_HMEBOX2; 39062306a36Sopenharmony_ci box_ex_reg = REG_HMEBOX2_EX; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case 3: 39362306a36Sopenharmony_ci box_reg = REG_HMEBOX3; 39462306a36Sopenharmony_ci box_ex_reg = REG_HMEBOX3_EX; 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci default: 39762306a36Sopenharmony_ci WARN(1, "invalid h2c mail box number\n"); 39862306a36Sopenharmony_ci return; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci ret = read_poll_timeout_atomic(rtw_read8, box_state, 40262306a36Sopenharmony_ci !((box_state >> box) & 0x1), 100, 3000, 40362306a36Sopenharmony_ci false, rtwdev, REG_HMETFR); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci if (ret) { 40662306a36Sopenharmony_ci rtw_err(rtwdev, "failed to send h2c command\n"); 40762306a36Sopenharmony_ci return; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci rtw_write32(rtwdev, box_ex_reg, le32_to_cpu(h2c_cmd->msg_ext)); 41162306a36Sopenharmony_ci rtw_write32(rtwdev, box_reg, le32_to_cpu(h2c_cmd->msg)); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (++rtwdev->h2c.last_box_num >= 4) 41462306a36Sopenharmony_ci rtwdev->h2c.last_box_num = 0; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_civoid rtw_fw_h2c_cmd_dbg(struct rtw_dev *rtwdev, u8 *h2c) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c); 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic void rtw_fw_send_h2c_packet(struct rtw_dev *rtwdev, u8 *h2c_pkt) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci int ret; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci lockdep_assert_held(&rtwdev->mutex); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci FW_OFFLOAD_H2C_SET_SEQ_NUM(h2c_pkt, rtwdev->h2c.seq); 42962306a36Sopenharmony_ci ret = rtw_hci_write_data_h2c(rtwdev, h2c_pkt, H2C_PKT_SIZE); 43062306a36Sopenharmony_ci if (ret) 43162306a36Sopenharmony_ci rtw_err(rtwdev, "failed to send h2c packet\n"); 43262306a36Sopenharmony_ci rtwdev->h2c.seq++; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_civoid 43662306a36Sopenharmony_cirtw_fw_send_general_info(struct rtw_dev *rtwdev) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct rtw_fifo_conf *fifo = &rtwdev->fifo; 43962306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 44062306a36Sopenharmony_ci u16 total_size = H2C_PKT_HDR_SIZE + 4; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (rtw_chip_wcpu_11n(rtwdev)) 44362306a36Sopenharmony_ci return; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_GENERAL_INFO); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci GENERAL_INFO_SET_FW_TX_BOUNDARY(h2c_pkt, 45062306a36Sopenharmony_ci fifo->rsvd_fw_txbuf_addr - 45162306a36Sopenharmony_ci fifo->rsvd_boundary); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci rtw_fw_send_h2c_packet(rtwdev, h2c_pkt); 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_civoid 45762306a36Sopenharmony_cirtw_fw_send_phydm_info(struct rtw_dev *rtwdev) 45862306a36Sopenharmony_ci{ 45962306a36Sopenharmony_ci struct rtw_hal *hal = &rtwdev->hal; 46062306a36Sopenharmony_ci struct rtw_efuse *efuse = &rtwdev->efuse; 46162306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 46262306a36Sopenharmony_ci u16 total_size = H2C_PKT_HDR_SIZE + 8; 46362306a36Sopenharmony_ci u8 fw_rf_type = 0; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (rtw_chip_wcpu_11n(rtwdev)) 46662306a36Sopenharmony_ci return; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (hal->rf_type == RF_1T1R) 46962306a36Sopenharmony_ci fw_rf_type = FW_RF_1T1R; 47062306a36Sopenharmony_ci else if (hal->rf_type == RF_2T2R) 47162306a36Sopenharmony_ci fw_rf_type = FW_RF_2T2R; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_PHYDM_INFO); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size); 47662306a36Sopenharmony_ci PHYDM_INFO_SET_REF_TYPE(h2c_pkt, efuse->rfe_option); 47762306a36Sopenharmony_ci PHYDM_INFO_SET_RF_TYPE(h2c_pkt, fw_rf_type); 47862306a36Sopenharmony_ci PHYDM_INFO_SET_CUT_VER(h2c_pkt, hal->cut_version); 47962306a36Sopenharmony_ci PHYDM_INFO_SET_RX_ANT_STATUS(h2c_pkt, hal->antenna_tx); 48062306a36Sopenharmony_ci PHYDM_INFO_SET_TX_ANT_STATUS(h2c_pkt, hal->antenna_rx); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci rtw_fw_send_h2c_packet(rtwdev, h2c_pkt); 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_civoid rtw_fw_do_iqk(struct rtw_dev *rtwdev, struct rtw_iqk_para *para) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 48862306a36Sopenharmony_ci u16 total_size = H2C_PKT_HDR_SIZE + 1; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_IQK); 49162306a36Sopenharmony_ci SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size); 49262306a36Sopenharmony_ci IQK_SET_CLEAR(h2c_pkt, para->clear); 49362306a36Sopenharmony_ci IQK_SET_SEGMENT_IQK(h2c_pkt, para->segment_iqk); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci rtw_fw_send_h2c_packet(rtwdev, h2c_pkt); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ciEXPORT_SYMBOL(rtw_fw_do_iqk); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_civoid rtw_fw_inform_rfk_status(struct rtw_dev *rtwdev, bool start) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WIFI_CALIBRATION); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci RFK_SET_INFORM_START(h2c_pkt, start); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ciEXPORT_SYMBOL(rtw_fw_inform_rfk_status); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_civoid rtw_fw_query_bt_info(struct rtw_dev *rtwdev) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_INFO); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci SET_QUERY_BT_INFO(h2c_pkt, true); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_civoid rtw_fw_default_port(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct rtw_h2c_register h2c = {}; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (rtwvif->net_type != RTW_NET_MGD_LINKED) 52762306a36Sopenharmony_ci return; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Leave LPS before default port H2C so FW timer is correct */ 53062306a36Sopenharmony_ci rtw_leave_lps(rtwdev); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci h2c.w0 = u32_encode_bits(H2C_CMD_DEFAULT_PORT, RTW_H2C_W0_CMDID) | 53362306a36Sopenharmony_ci u32_encode_bits(rtwvif->port, RTW_H2C_DEFAULT_PORT_W0_PORTID) | 53462306a36Sopenharmony_ci u32_encode_bits(rtwvif->mac_id, RTW_H2C_DEFAULT_PORT_W0_MACID); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci rtw_fw_send_h2c_command_register(rtwdev, &h2c); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_civoid rtw_fw_wl_ch_info(struct rtw_dev *rtwdev, u8 link, u8 ch, u8 bw) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WL_CH_INFO); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci SET_WL_CH_INFO_LINK(h2c_pkt, link); 54662306a36Sopenharmony_ci SET_WL_CH_INFO_CHNL(h2c_pkt, ch); 54762306a36Sopenharmony_ci SET_WL_CH_INFO_BW(h2c_pkt, bw); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_civoid rtw_fw_query_bt_mp_info(struct rtw_dev *rtwdev, 55362306a36Sopenharmony_ci struct rtw_coex_info_req *req) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_MP_INFO); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci SET_BT_MP_INFO_SEQ(h2c_pkt, req->seq); 56062306a36Sopenharmony_ci SET_BT_MP_INFO_OP_CODE(h2c_pkt, req->op_code); 56162306a36Sopenharmony_ci SET_BT_MP_INFO_PARA1(h2c_pkt, req->para1); 56262306a36Sopenharmony_ci SET_BT_MP_INFO_PARA2(h2c_pkt, req->para2); 56362306a36Sopenharmony_ci SET_BT_MP_INFO_PARA3(h2c_pkt, req->para3); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_civoid rtw_fw_force_bt_tx_power(struct rtw_dev *rtwdev, u8 bt_pwr_dec_lvl) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 57162306a36Sopenharmony_ci u8 index = 0 - bt_pwr_dec_lvl; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_FORCE_BT_TX_POWER); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci SET_BT_TX_POWER_INDEX(h2c_pkt, index); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_civoid rtw_fw_bt_ignore_wlan_action(struct rtw_dev *rtwdev, bool enable) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_IGNORE_WLAN_ACTION); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci SET_IGNORE_WLAN_ACTION_EN(h2c_pkt, enable); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_civoid rtw_fw_coex_tdma_type(struct rtw_dev *rtwdev, 59262306a36Sopenharmony_ci u8 para1, u8 para2, u8 para3, u8 para4, u8 para5) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_COEX_TDMA_TYPE); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci SET_COEX_TDMA_TYPE_PARA1(h2c_pkt, para1); 59962306a36Sopenharmony_ci SET_COEX_TDMA_TYPE_PARA2(h2c_pkt, para2); 60062306a36Sopenharmony_ci SET_COEX_TDMA_TYPE_PARA3(h2c_pkt, para3); 60162306a36Sopenharmony_ci SET_COEX_TDMA_TYPE_PARA4(h2c_pkt, para4); 60262306a36Sopenharmony_ci SET_COEX_TDMA_TYPE_PARA5(h2c_pkt, para5); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_civoid rtw_fw_coex_query_hid_info(struct rtw_dev *rtwdev, u8 sub_id, u8 data) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_QUERY_BT_HID_INFO); 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci SET_COEX_QUERY_HID_INFO_SUBID(h2c_pkt, sub_id); 61462306a36Sopenharmony_ci SET_COEX_QUERY_HID_INFO_DATA1(h2c_pkt, data); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_civoid rtw_fw_bt_wifi_control(struct rtw_dev *rtwdev, u8 op_code, u8 *data) 62062306a36Sopenharmony_ci{ 62162306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BT_WIFI_CONTROL); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci SET_BT_WIFI_CONTROL_OP_CODE(h2c_pkt, op_code); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci SET_BT_WIFI_CONTROL_DATA1(h2c_pkt, *data); 62862306a36Sopenharmony_ci SET_BT_WIFI_CONTROL_DATA2(h2c_pkt, *(data + 1)); 62962306a36Sopenharmony_ci SET_BT_WIFI_CONTROL_DATA3(h2c_pkt, *(data + 2)); 63062306a36Sopenharmony_ci SET_BT_WIFI_CONTROL_DATA4(h2c_pkt, *(data + 3)); 63162306a36Sopenharmony_ci SET_BT_WIFI_CONTROL_DATA5(h2c_pkt, *(data + 4)); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_civoid rtw_fw_send_rssi_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 63962306a36Sopenharmony_ci u8 rssi = ewma_rssi_read(&si->avg_rssi); 64062306a36Sopenharmony_ci bool stbc_en = si->stbc_en ? true : false; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RSSI_MONITOR); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci SET_RSSI_INFO_MACID(h2c_pkt, si->mac_id); 64562306a36Sopenharmony_ci SET_RSSI_INFO_RSSI(h2c_pkt, rssi); 64662306a36Sopenharmony_ci SET_RSSI_INFO_STBC(h2c_pkt, stbc_en); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 64962306a36Sopenharmony_ci} 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_civoid rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, 65262306a36Sopenharmony_ci bool reset_ra_mask) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 65562306a36Sopenharmony_ci bool disable_pt = true; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RA_INFO); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci SET_RA_INFO_MACID(h2c_pkt, si->mac_id); 66062306a36Sopenharmony_ci SET_RA_INFO_RATE_ID(h2c_pkt, si->rate_id); 66162306a36Sopenharmony_ci SET_RA_INFO_INIT_RA_LVL(h2c_pkt, si->init_ra_lv); 66262306a36Sopenharmony_ci SET_RA_INFO_SGI_EN(h2c_pkt, si->sgi_enable); 66362306a36Sopenharmony_ci SET_RA_INFO_BW_MODE(h2c_pkt, si->bw_mode); 66462306a36Sopenharmony_ci SET_RA_INFO_LDPC(h2c_pkt, !!si->ldpc_en); 66562306a36Sopenharmony_ci SET_RA_INFO_NO_UPDATE(h2c_pkt, !reset_ra_mask); 66662306a36Sopenharmony_ci SET_RA_INFO_VHT_EN(h2c_pkt, si->vht_enable); 66762306a36Sopenharmony_ci SET_RA_INFO_DIS_PT(h2c_pkt, disable_pt); 66862306a36Sopenharmony_ci SET_RA_INFO_RA_MASK0(h2c_pkt, (si->ra_mask & 0xff)); 66962306a36Sopenharmony_ci SET_RA_INFO_RA_MASK1(h2c_pkt, (si->ra_mask & 0xff00) >> 8); 67062306a36Sopenharmony_ci SET_RA_INFO_RA_MASK2(h2c_pkt, (si->ra_mask & 0xff0000) >> 16); 67162306a36Sopenharmony_ci SET_RA_INFO_RA_MASK3(h2c_pkt, (si->ra_mask & 0xff000000) >> 24); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci si->init_ra_lv = 0; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_civoid rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool connect) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_MEDIA_STATUS_RPT); 68362306a36Sopenharmony_ci MEDIA_STATUS_RPT_SET_OP_MODE(h2c_pkt, connect); 68462306a36Sopenharmony_ci MEDIA_STATUS_RPT_SET_MACID(h2c_pkt, mac_id); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_civoid rtw_fw_update_wl_phy_info(struct rtw_dev *rtwdev) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct rtw_traffic_stats *stats = &rtwdev->stats; 69262306a36Sopenharmony_ci struct rtw_dm_info *dm_info = &rtwdev->dm_info; 69362306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WL_PHY_INFO); 69662306a36Sopenharmony_ci SET_WL_PHY_INFO_TX_TP(h2c_pkt, stats->tx_throughput); 69762306a36Sopenharmony_ci SET_WL_PHY_INFO_RX_TP(h2c_pkt, stats->rx_throughput); 69862306a36Sopenharmony_ci SET_WL_PHY_INFO_TX_RATE_DESC(h2c_pkt, dm_info->tx_rate); 69962306a36Sopenharmony_ci SET_WL_PHY_INFO_RX_RATE_DESC(h2c_pkt, dm_info->curr_rx_rate); 70062306a36Sopenharmony_ci SET_WL_PHY_INFO_RX_EVM(h2c_pkt, dm_info->rx_evm_dbm[RF_PATH_A]); 70162306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_civoid rtw_fw_beacon_filter_config(struct rtw_dev *rtwdev, bool connect, 70562306a36Sopenharmony_ci struct ieee80211_vif *vif) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; 70862306a36Sopenharmony_ci struct ieee80211_sta *sta = ieee80211_find_sta(vif, bss_conf->bssid); 70962306a36Sopenharmony_ci static const u8 rssi_min = 0, rssi_max = 100, rssi_offset = 100; 71062306a36Sopenharmony_ci struct rtw_sta_info *si = 71162306a36Sopenharmony_ci sta ? (struct rtw_sta_info *)sta->drv_priv : NULL; 71262306a36Sopenharmony_ci s32 threshold = bss_conf->cqm_rssi_thold + rssi_offset; 71362306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_BCN_FILTER)) 71662306a36Sopenharmony_ci return; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (!connect) { 71962306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BCN_FILTER_OFFLOAD_P1); 72062306a36Sopenharmony_ci SET_BCN_FILTER_OFFLOAD_P1_ENABLE(h2c_pkt, connect); 72162306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return; 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (!si) 72762306a36Sopenharmony_ci return; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BCN_FILTER_OFFLOAD_P0); 73062306a36Sopenharmony_ci ether_addr_copy(&h2c_pkt[1], bss_conf->bssid); 73162306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci memset(h2c_pkt, 0, sizeof(h2c_pkt)); 73462306a36Sopenharmony_ci threshold = clamp_t(s32, threshold, rssi_min, rssi_max); 73562306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_BCN_FILTER_OFFLOAD_P1); 73662306a36Sopenharmony_ci SET_BCN_FILTER_OFFLOAD_P1_ENABLE(h2c_pkt, connect); 73762306a36Sopenharmony_ci SET_BCN_FILTER_OFFLOAD_P1_OFFLOAD_MODE(h2c_pkt, 73862306a36Sopenharmony_ci BCN_FILTER_OFFLOAD_MODE_DEFAULT); 73962306a36Sopenharmony_ci SET_BCN_FILTER_OFFLOAD_P1_THRESHOLD(h2c_pkt, (u8)threshold); 74062306a36Sopenharmony_ci SET_BCN_FILTER_OFFLOAD_P1_BCN_LOSS_CNT(h2c_pkt, BCN_LOSS_CNT); 74162306a36Sopenharmony_ci SET_BCN_FILTER_OFFLOAD_P1_MACID(h2c_pkt, si->mac_id); 74262306a36Sopenharmony_ci SET_BCN_FILTER_OFFLOAD_P1_HYST(h2c_pkt, bss_conf->cqm_rssi_hyst); 74362306a36Sopenharmony_ci SET_BCN_FILTER_OFFLOAD_P1_BCN_INTERVAL(h2c_pkt, bss_conf->beacon_int); 74462306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_civoid rtw_fw_set_pwr_mode(struct rtw_dev *rtwdev) 74862306a36Sopenharmony_ci{ 74962306a36Sopenharmony_ci struct rtw_lps_conf *conf = &rtwdev->lps_conf; 75062306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_SET_PWR_MODE); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci SET_PWR_MODE_SET_MODE(h2c_pkt, conf->mode); 75562306a36Sopenharmony_ci SET_PWR_MODE_SET_RLBM(h2c_pkt, conf->rlbm); 75662306a36Sopenharmony_ci SET_PWR_MODE_SET_SMART_PS(h2c_pkt, conf->smart_ps); 75762306a36Sopenharmony_ci SET_PWR_MODE_SET_AWAKE_INTERVAL(h2c_pkt, conf->awake_interval); 75862306a36Sopenharmony_ci SET_PWR_MODE_SET_PORT_ID(h2c_pkt, conf->port_id); 75962306a36Sopenharmony_ci SET_PWR_MODE_SET_PWR_STATE(h2c_pkt, conf->state); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_civoid rtw_fw_set_keep_alive_cmd(struct rtw_dev *rtwdev, bool enable) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 76762306a36Sopenharmony_ci struct rtw_fw_wow_keep_alive_para mode = { 76862306a36Sopenharmony_ci .adopt = true, 76962306a36Sopenharmony_ci .pkt_type = KEEP_ALIVE_NULL_PKT, 77062306a36Sopenharmony_ci .period = 5, 77162306a36Sopenharmony_ci }; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_KEEP_ALIVE); 77462306a36Sopenharmony_ci SET_KEEP_ALIVE_ENABLE(h2c_pkt, enable); 77562306a36Sopenharmony_ci SET_KEEP_ALIVE_ADOPT(h2c_pkt, mode.adopt); 77662306a36Sopenharmony_ci SET_KEEP_ALIVE_PKT_TYPE(h2c_pkt, mode.pkt_type); 77762306a36Sopenharmony_ci SET_KEEP_ALIVE_CHECK_PERIOD(h2c_pkt, mode.period); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_civoid rtw_fw_set_disconnect_decision_cmd(struct rtw_dev *rtwdev, bool enable) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct rtw_wow_param *rtw_wow = &rtwdev->wow; 78562306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 78662306a36Sopenharmony_ci struct rtw_fw_wow_disconnect_para mode = { 78762306a36Sopenharmony_ci .adopt = true, 78862306a36Sopenharmony_ci .period = 30, 78962306a36Sopenharmony_ci .retry_count = 5, 79062306a36Sopenharmony_ci }; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_DISCONNECT_DECISION); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (test_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) { 79562306a36Sopenharmony_ci SET_DISCONNECT_DECISION_ENABLE(h2c_pkt, enable); 79662306a36Sopenharmony_ci SET_DISCONNECT_DECISION_ADOPT(h2c_pkt, mode.adopt); 79762306a36Sopenharmony_ci SET_DISCONNECT_DECISION_CHECK_PERIOD(h2c_pkt, mode.period); 79862306a36Sopenharmony_ci SET_DISCONNECT_DECISION_TRY_PKT_NUM(h2c_pkt, mode.retry_count); 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_civoid rtw_fw_set_wowlan_ctrl_cmd(struct rtw_dev *rtwdev, bool enable) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci struct rtw_wow_param *rtw_wow = &rtwdev->wow; 80762306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_WOWLAN); 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci SET_WOWLAN_FUNC_ENABLE(h2c_pkt, enable); 81262306a36Sopenharmony_ci if (rtw_wow_mgd_linked(rtwdev)) { 81362306a36Sopenharmony_ci if (test_bit(RTW_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags)) 81462306a36Sopenharmony_ci SET_WOWLAN_MAGIC_PKT_ENABLE(h2c_pkt, enable); 81562306a36Sopenharmony_ci if (test_bit(RTW_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) 81662306a36Sopenharmony_ci SET_WOWLAN_DEAUTH_WAKEUP_ENABLE(h2c_pkt, enable); 81762306a36Sopenharmony_ci if (test_bit(RTW_WOW_FLAG_EN_REKEY_PKT, rtw_wow->flags)) 81862306a36Sopenharmony_ci SET_WOWLAN_REKEY_WAKEUP_ENABLE(h2c_pkt, enable); 81962306a36Sopenharmony_ci if (rtw_wow->pattern_cnt) 82062306a36Sopenharmony_ci SET_WOWLAN_PATTERN_MATCH_ENABLE(h2c_pkt, enable); 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_civoid rtw_fw_set_aoac_global_info_cmd(struct rtw_dev *rtwdev, 82762306a36Sopenharmony_ci u8 pairwise_key_enc, 82862306a36Sopenharmony_ci u8 group_key_enc) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_AOAC_GLOBAL_INFO); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci SET_AOAC_GLOBAL_INFO_PAIRWISE_ENC_ALG(h2c_pkt, pairwise_key_enc); 83562306a36Sopenharmony_ci SET_AOAC_GLOBAL_INFO_GROUP_ENC_ALG(h2c_pkt, group_key_enc); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 83862306a36Sopenharmony_ci} 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_civoid rtw_fw_set_remote_wake_ctrl_cmd(struct rtw_dev *rtwdev, bool enable) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_REMOTE_WAKE_CTRL); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci SET_REMOTE_WAKECTRL_ENABLE(h2c_pkt, enable); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (rtw_wow_no_link(rtwdev)) 84962306a36Sopenharmony_ci SET_REMOTE_WAKE_CTRL_NLO_OFFLOAD_EN(h2c_pkt, enable); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 85262306a36Sopenharmony_ci} 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_cistatic u8 rtw_get_rsvd_page_location(struct rtw_dev *rtwdev, 85562306a36Sopenharmony_ci enum rtw_rsvd_packet_type type) 85662306a36Sopenharmony_ci{ 85762306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt; 85862306a36Sopenharmony_ci u8 location = 0; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { 86162306a36Sopenharmony_ci if (type == rsvd_pkt->type) 86262306a36Sopenharmony_ci location = rsvd_pkt->page; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci return location; 86662306a36Sopenharmony_ci} 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_civoid rtw_fw_set_nlo_info(struct rtw_dev *rtwdev, bool enable) 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 87162306a36Sopenharmony_ci u8 loc_nlo; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci loc_nlo = rtw_get_rsvd_page_location(rtwdev, RSVD_NLO_INFO); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_NLO_INFO); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci SET_NLO_FUN_EN(h2c_pkt, enable); 87862306a36Sopenharmony_ci if (enable) { 87962306a36Sopenharmony_ci if (rtw_get_lps_deep_mode(rtwdev) != LPS_DEEP_MODE_NONE) 88062306a36Sopenharmony_ci SET_NLO_PS_32K(h2c_pkt, enable); 88162306a36Sopenharmony_ci SET_NLO_IGNORE_SECURITY(h2c_pkt, enable); 88262306a36Sopenharmony_ci SET_NLO_LOC_NLO_INFO(h2c_pkt, loc_nlo); 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_civoid rtw_fw_set_recover_bt_device(struct rtw_dev *rtwdev) 88962306a36Sopenharmony_ci{ 89062306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RECOVER_BT_DEV); 89362306a36Sopenharmony_ci SET_RECOVER_BT_DEV_EN(h2c_pkt, 1); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_civoid rtw_fw_set_pg_info(struct rtw_dev *rtwdev) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci struct rtw_lps_conf *conf = &rtwdev->lps_conf; 90162306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 90262306a36Sopenharmony_ci u8 loc_pg, loc_dpk; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci loc_pg = rtw_get_rsvd_page_location(rtwdev, RSVD_LPS_PG_INFO); 90562306a36Sopenharmony_ci loc_dpk = rtw_get_rsvd_page_location(rtwdev, RSVD_LPS_PG_DPK); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_LPS_PG_INFO); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci LPS_PG_INFO_LOC(h2c_pkt, loc_pg); 91062306a36Sopenharmony_ci LPS_PG_DPK_LOC(h2c_pkt, loc_dpk); 91162306a36Sopenharmony_ci LPS_PG_SEC_CAM_EN(h2c_pkt, conf->sec_cam_backup); 91262306a36Sopenharmony_ci LPS_PG_PATTERN_CAM_EN(h2c_pkt, conf->pattern_cam_backup); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cistatic u8 rtw_get_rsvd_page_probe_req_location(struct rtw_dev *rtwdev, 91862306a36Sopenharmony_ci struct cfg80211_ssid *ssid) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt; 92162306a36Sopenharmony_ci u8 location = 0; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { 92462306a36Sopenharmony_ci if (rsvd_pkt->type != RSVD_PROBE_REQ) 92562306a36Sopenharmony_ci continue; 92662306a36Sopenharmony_ci if ((!ssid && !rsvd_pkt->ssid) || 92762306a36Sopenharmony_ci rtw_ssid_equal(rsvd_pkt->ssid, ssid)) 92862306a36Sopenharmony_ci location = rsvd_pkt->page; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return location; 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic u16 rtw_get_rsvd_page_probe_req_size(struct rtw_dev *rtwdev, 93562306a36Sopenharmony_ci struct cfg80211_ssid *ssid) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt; 93862306a36Sopenharmony_ci u16 size = 0; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { 94162306a36Sopenharmony_ci if (rsvd_pkt->type != RSVD_PROBE_REQ) 94262306a36Sopenharmony_ci continue; 94362306a36Sopenharmony_ci if ((!ssid && !rsvd_pkt->ssid) || 94462306a36Sopenharmony_ci rtw_ssid_equal(rsvd_pkt->ssid, ssid)) 94562306a36Sopenharmony_ci size = rsvd_pkt->probe_req_size; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return size; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_civoid rtw_send_rsvd_page_h2c(struct rtw_dev *rtwdev) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 95462306a36Sopenharmony_ci u8 location = 0; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RSVD_PAGE); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci location = rtw_get_rsvd_page_location(rtwdev, RSVD_PROBE_RESP); 95962306a36Sopenharmony_ci *(h2c_pkt + 1) = location; 96062306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_PROBE_RESP loc: %d\n", location); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci location = rtw_get_rsvd_page_location(rtwdev, RSVD_PS_POLL); 96362306a36Sopenharmony_ci *(h2c_pkt + 2) = location; 96462306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_PS_POLL loc: %d\n", location); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci location = rtw_get_rsvd_page_location(rtwdev, RSVD_NULL); 96762306a36Sopenharmony_ci *(h2c_pkt + 3) = location; 96862306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_NULL loc: %d\n", location); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci location = rtw_get_rsvd_page_location(rtwdev, RSVD_QOS_NULL); 97162306a36Sopenharmony_ci *(h2c_pkt + 4) = location; 97262306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, "RSVD_QOS_NULL loc: %d\n", location); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic struct sk_buff *rtw_nlo_info_get(struct ieee80211_hw *hw) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci struct rtw_dev *rtwdev = hw->priv; 98062306a36Sopenharmony_ci const struct rtw_chip_info *chip = rtwdev->chip; 98162306a36Sopenharmony_ci struct rtw_pno_request *pno_req = &rtwdev->wow.pno_req; 98262306a36Sopenharmony_ci struct rtw_nlo_info_hdr *nlo_hdr; 98362306a36Sopenharmony_ci struct cfg80211_ssid *ssid; 98462306a36Sopenharmony_ci struct sk_buff *skb; 98562306a36Sopenharmony_ci u8 *pos, loc; 98662306a36Sopenharmony_ci u32 size; 98762306a36Sopenharmony_ci int i; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci if (!pno_req->inited || !pno_req->match_set_cnt) 99062306a36Sopenharmony_ci return NULL; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci size = sizeof(struct rtw_nlo_info_hdr) + pno_req->match_set_cnt * 99362306a36Sopenharmony_ci IEEE80211_MAX_SSID_LEN + chip->tx_pkt_desc_sz; 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci skb = alloc_skb(size, GFP_KERNEL); 99662306a36Sopenharmony_ci if (!skb) 99762306a36Sopenharmony_ci return NULL; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci skb_reserve(skb, chip->tx_pkt_desc_sz); 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci nlo_hdr = skb_put_zero(skb, sizeof(struct rtw_nlo_info_hdr)); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci nlo_hdr->nlo_count = pno_req->match_set_cnt; 100462306a36Sopenharmony_ci nlo_hdr->hidden_ap_count = pno_req->match_set_cnt; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci /* pattern check for firmware */ 100762306a36Sopenharmony_ci memset(nlo_hdr->pattern_check, 0xA5, FW_NLO_INFO_CHECK_SIZE); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci for (i = 0; i < pno_req->match_set_cnt; i++) 101062306a36Sopenharmony_ci nlo_hdr->ssid_len[i] = pno_req->match_sets[i].ssid.ssid_len; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci for (i = 0; i < pno_req->match_set_cnt; i++) { 101362306a36Sopenharmony_ci ssid = &pno_req->match_sets[i].ssid; 101462306a36Sopenharmony_ci loc = rtw_get_rsvd_page_probe_req_location(rtwdev, ssid); 101562306a36Sopenharmony_ci if (!loc) { 101662306a36Sopenharmony_ci rtw_err(rtwdev, "failed to get probe req rsvd loc\n"); 101762306a36Sopenharmony_ci kfree_skb(skb); 101862306a36Sopenharmony_ci return NULL; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci nlo_hdr->location[i] = loc; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci for (i = 0; i < pno_req->match_set_cnt; i++) { 102462306a36Sopenharmony_ci pos = skb_put_zero(skb, IEEE80211_MAX_SSID_LEN); 102562306a36Sopenharmony_ci memcpy(pos, pno_req->match_sets[i].ssid.ssid, 102662306a36Sopenharmony_ci pno_req->match_sets[i].ssid.ssid_len); 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci return skb; 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic struct sk_buff *rtw_cs_channel_info_get(struct ieee80211_hw *hw) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci struct rtw_dev *rtwdev = hw->priv; 103562306a36Sopenharmony_ci const struct rtw_chip_info *chip = rtwdev->chip; 103662306a36Sopenharmony_ci struct rtw_pno_request *pno_req = &rtwdev->wow.pno_req; 103762306a36Sopenharmony_ci struct ieee80211_channel *channels = pno_req->channels; 103862306a36Sopenharmony_ci struct sk_buff *skb; 103962306a36Sopenharmony_ci int count = pno_req->channel_cnt; 104062306a36Sopenharmony_ci u8 *pos; 104162306a36Sopenharmony_ci int i = 0; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci skb = alloc_skb(4 * count + chip->tx_pkt_desc_sz, GFP_KERNEL); 104462306a36Sopenharmony_ci if (!skb) 104562306a36Sopenharmony_ci return NULL; 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci skb_reserve(skb, chip->tx_pkt_desc_sz); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci for (i = 0; i < count; i++) { 105062306a36Sopenharmony_ci pos = skb_put_zero(skb, 4); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci CHSW_INFO_SET_CH(pos, channels[i].hw_value); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci if (channels[i].flags & IEEE80211_CHAN_RADAR) 105562306a36Sopenharmony_ci CHSW_INFO_SET_ACTION_ID(pos, 0); 105662306a36Sopenharmony_ci else 105762306a36Sopenharmony_ci CHSW_INFO_SET_ACTION_ID(pos, 1); 105862306a36Sopenharmony_ci CHSW_INFO_SET_TIMEOUT(pos, 1); 105962306a36Sopenharmony_ci CHSW_INFO_SET_PRI_CH_IDX(pos, 1); 106062306a36Sopenharmony_ci CHSW_INFO_SET_BW(pos, 0); 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return skb; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic struct sk_buff *rtw_lps_pg_dpk_get(struct ieee80211_hw *hw) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct rtw_dev *rtwdev = hw->priv; 106962306a36Sopenharmony_ci const struct rtw_chip_info *chip = rtwdev->chip; 107062306a36Sopenharmony_ci struct rtw_dpk_info *dpk_info = &rtwdev->dm_info.dpk_info; 107162306a36Sopenharmony_ci struct rtw_lps_pg_dpk_hdr *dpk_hdr; 107262306a36Sopenharmony_ci struct sk_buff *skb; 107362306a36Sopenharmony_ci u32 size; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci size = chip->tx_pkt_desc_sz + sizeof(*dpk_hdr); 107662306a36Sopenharmony_ci skb = alloc_skb(size, GFP_KERNEL); 107762306a36Sopenharmony_ci if (!skb) 107862306a36Sopenharmony_ci return NULL; 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci skb_reserve(skb, chip->tx_pkt_desc_sz); 108162306a36Sopenharmony_ci dpk_hdr = skb_put_zero(skb, sizeof(*dpk_hdr)); 108262306a36Sopenharmony_ci dpk_hdr->dpk_ch = dpk_info->dpk_ch; 108362306a36Sopenharmony_ci dpk_hdr->dpk_path_ok = dpk_info->dpk_path_ok[0]; 108462306a36Sopenharmony_ci memcpy(dpk_hdr->dpk_txagc, dpk_info->dpk_txagc, 2); 108562306a36Sopenharmony_ci memcpy(dpk_hdr->dpk_gs, dpk_info->dpk_gs, 4); 108662306a36Sopenharmony_ci memcpy(dpk_hdr->coef, dpk_info->coef, 160); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci return skb; 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cistatic struct sk_buff *rtw_lps_pg_info_get(struct ieee80211_hw *hw) 109262306a36Sopenharmony_ci{ 109362306a36Sopenharmony_ci struct rtw_dev *rtwdev = hw->priv; 109462306a36Sopenharmony_ci const struct rtw_chip_info *chip = rtwdev->chip; 109562306a36Sopenharmony_ci struct rtw_lps_conf *conf = &rtwdev->lps_conf; 109662306a36Sopenharmony_ci struct rtw_lps_pg_info_hdr *pg_info_hdr; 109762306a36Sopenharmony_ci struct rtw_wow_param *rtw_wow = &rtwdev->wow; 109862306a36Sopenharmony_ci struct sk_buff *skb; 109962306a36Sopenharmony_ci u32 size; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci size = chip->tx_pkt_desc_sz + sizeof(*pg_info_hdr); 110262306a36Sopenharmony_ci skb = alloc_skb(size, GFP_KERNEL); 110362306a36Sopenharmony_ci if (!skb) 110462306a36Sopenharmony_ci return NULL; 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci skb_reserve(skb, chip->tx_pkt_desc_sz); 110762306a36Sopenharmony_ci pg_info_hdr = skb_put_zero(skb, sizeof(*pg_info_hdr)); 110862306a36Sopenharmony_ci pg_info_hdr->tx_bu_page_count = rtwdev->fifo.rsvd_drv_pg_num; 110962306a36Sopenharmony_ci pg_info_hdr->macid = find_first_bit(rtwdev->mac_id_map, RTW_MAX_MAC_ID_NUM); 111062306a36Sopenharmony_ci pg_info_hdr->sec_cam_count = 111162306a36Sopenharmony_ci rtw_sec_cam_pg_backup(rtwdev, pg_info_hdr->sec_cam); 111262306a36Sopenharmony_ci pg_info_hdr->pattern_count = rtw_wow->pattern_cnt; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci conf->sec_cam_backup = pg_info_hdr->sec_cam_count != 0; 111562306a36Sopenharmony_ci conf->pattern_cam_backup = rtw_wow->pattern_cnt != 0; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci return skb; 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic struct sk_buff *rtw_get_rsvd_page_skb(struct ieee80211_hw *hw, 112162306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci struct ieee80211_vif *vif; 112462306a36Sopenharmony_ci struct rtw_vif *rtwvif; 112562306a36Sopenharmony_ci struct sk_buff *skb_new; 112662306a36Sopenharmony_ci struct cfg80211_ssid *ssid; 112762306a36Sopenharmony_ci u16 tim_offset = 0; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (rsvd_pkt->type == RSVD_DUMMY) { 113062306a36Sopenharmony_ci skb_new = alloc_skb(1, GFP_KERNEL); 113162306a36Sopenharmony_ci if (!skb_new) 113262306a36Sopenharmony_ci return NULL; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci skb_put(skb_new, 1); 113562306a36Sopenharmony_ci return skb_new; 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci rtwvif = rsvd_pkt->rtwvif; 113962306a36Sopenharmony_ci if (!rtwvif) 114062306a36Sopenharmony_ci return NULL; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci vif = rtwvif_to_vif(rtwvif); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci switch (rsvd_pkt->type) { 114562306a36Sopenharmony_ci case RSVD_BEACON: 114662306a36Sopenharmony_ci skb_new = ieee80211_beacon_get_tim(hw, vif, &tim_offset, NULL, 0); 114762306a36Sopenharmony_ci rsvd_pkt->tim_offset = tim_offset; 114862306a36Sopenharmony_ci break; 114962306a36Sopenharmony_ci case RSVD_PS_POLL: 115062306a36Sopenharmony_ci skb_new = ieee80211_pspoll_get(hw, vif); 115162306a36Sopenharmony_ci break; 115262306a36Sopenharmony_ci case RSVD_PROBE_RESP: 115362306a36Sopenharmony_ci skb_new = ieee80211_proberesp_get(hw, vif); 115462306a36Sopenharmony_ci break; 115562306a36Sopenharmony_ci case RSVD_NULL: 115662306a36Sopenharmony_ci skb_new = ieee80211_nullfunc_get(hw, vif, -1, false); 115762306a36Sopenharmony_ci break; 115862306a36Sopenharmony_ci case RSVD_QOS_NULL: 115962306a36Sopenharmony_ci skb_new = ieee80211_nullfunc_get(hw, vif, -1, true); 116062306a36Sopenharmony_ci break; 116162306a36Sopenharmony_ci case RSVD_LPS_PG_DPK: 116262306a36Sopenharmony_ci skb_new = rtw_lps_pg_dpk_get(hw); 116362306a36Sopenharmony_ci break; 116462306a36Sopenharmony_ci case RSVD_LPS_PG_INFO: 116562306a36Sopenharmony_ci skb_new = rtw_lps_pg_info_get(hw); 116662306a36Sopenharmony_ci break; 116762306a36Sopenharmony_ci case RSVD_PROBE_REQ: 116862306a36Sopenharmony_ci ssid = (struct cfg80211_ssid *)rsvd_pkt->ssid; 116962306a36Sopenharmony_ci if (ssid) 117062306a36Sopenharmony_ci skb_new = ieee80211_probereq_get(hw, vif->addr, 117162306a36Sopenharmony_ci ssid->ssid, 117262306a36Sopenharmony_ci ssid->ssid_len, 0); 117362306a36Sopenharmony_ci else 117462306a36Sopenharmony_ci skb_new = ieee80211_probereq_get(hw, vif->addr, NULL, 0, 0); 117562306a36Sopenharmony_ci if (skb_new) 117662306a36Sopenharmony_ci rsvd_pkt->probe_req_size = (u16)skb_new->len; 117762306a36Sopenharmony_ci break; 117862306a36Sopenharmony_ci case RSVD_NLO_INFO: 117962306a36Sopenharmony_ci skb_new = rtw_nlo_info_get(hw); 118062306a36Sopenharmony_ci break; 118162306a36Sopenharmony_ci case RSVD_CH_INFO: 118262306a36Sopenharmony_ci skb_new = rtw_cs_channel_info_get(hw); 118362306a36Sopenharmony_ci break; 118462306a36Sopenharmony_ci default: 118562306a36Sopenharmony_ci return NULL; 118662306a36Sopenharmony_ci } 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci if (!skb_new) 118962306a36Sopenharmony_ci return NULL; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci return skb_new; 119262306a36Sopenharmony_ci} 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_cistatic void rtw_fill_rsvd_page_desc(struct rtw_dev *rtwdev, struct sk_buff *skb, 119562306a36Sopenharmony_ci enum rtw_rsvd_packet_type type) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci struct rtw_tx_pkt_info pkt_info = {0}; 119862306a36Sopenharmony_ci const struct rtw_chip_info *chip = rtwdev->chip; 119962306a36Sopenharmony_ci u8 *pkt_desc; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci rtw_tx_rsvd_page_pkt_info_update(rtwdev, &pkt_info, skb, type); 120262306a36Sopenharmony_ci pkt_desc = skb_push(skb, chip->tx_pkt_desc_sz); 120362306a36Sopenharmony_ci memset(pkt_desc, 0, chip->tx_pkt_desc_sz); 120462306a36Sopenharmony_ci rtw_tx_fill_tx_desc(&pkt_info, skb); 120562306a36Sopenharmony_ci} 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_cistatic inline u8 rtw_len_to_page(unsigned int len, u8 page_size) 120862306a36Sopenharmony_ci{ 120962306a36Sopenharmony_ci return DIV_ROUND_UP(len, page_size); 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistatic void rtw_rsvd_page_list_to_buf(struct rtw_dev *rtwdev, u8 page_size, 121362306a36Sopenharmony_ci u8 page_margin, u32 page, u8 *buf, 121462306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct sk_buff *skb = rsvd_pkt->skb; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (page >= 1) 121962306a36Sopenharmony_ci memcpy(buf + page_margin + page_size * (page - 1), 122062306a36Sopenharmony_ci skb->data, skb->len); 122162306a36Sopenharmony_ci else 122262306a36Sopenharmony_ci memcpy(buf, skb->data, skb->len); 122362306a36Sopenharmony_ci} 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_cistatic struct rtw_rsvd_page *rtw_alloc_rsvd_page(struct rtw_dev *rtwdev, 122662306a36Sopenharmony_ci enum rtw_rsvd_packet_type type, 122762306a36Sopenharmony_ci bool txdesc) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt = NULL; 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci rsvd_pkt = kzalloc(sizeof(*rsvd_pkt), GFP_KERNEL); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci if (!rsvd_pkt) 123462306a36Sopenharmony_ci return NULL; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci INIT_LIST_HEAD(&rsvd_pkt->vif_list); 123762306a36Sopenharmony_ci INIT_LIST_HEAD(&rsvd_pkt->build_list); 123862306a36Sopenharmony_ci rsvd_pkt->type = type; 123962306a36Sopenharmony_ci rsvd_pkt->add_txdesc = txdesc; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci return rsvd_pkt; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic void rtw_insert_rsvd_page(struct rtw_dev *rtwdev, 124562306a36Sopenharmony_ci struct rtw_vif *rtwvif, 124662306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci lockdep_assert_held(&rtwdev->mutex); 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci list_add_tail(&rsvd_pkt->vif_list, &rtwvif->rsvd_page_list); 125162306a36Sopenharmony_ci} 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_cistatic void rtw_add_rsvd_page(struct rtw_dev *rtwdev, 125462306a36Sopenharmony_ci struct rtw_vif *rtwvif, 125562306a36Sopenharmony_ci enum rtw_rsvd_packet_type type, 125662306a36Sopenharmony_ci bool txdesc) 125762306a36Sopenharmony_ci{ 125862306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt; 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci rsvd_pkt = rtw_alloc_rsvd_page(rtwdev, type, txdesc); 126162306a36Sopenharmony_ci if (!rsvd_pkt) { 126262306a36Sopenharmony_ci rtw_err(rtwdev, "failed to alloc rsvd page %d\n", type); 126362306a36Sopenharmony_ci return; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci rsvd_pkt->rtwvif = rtwvif; 126762306a36Sopenharmony_ci rtw_insert_rsvd_page(rtwdev, rtwvif, rsvd_pkt); 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_cistatic void rtw_add_rsvd_page_probe_req(struct rtw_dev *rtwdev, 127162306a36Sopenharmony_ci struct rtw_vif *rtwvif, 127262306a36Sopenharmony_ci struct cfg80211_ssid *ssid) 127362306a36Sopenharmony_ci{ 127462306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci rsvd_pkt = rtw_alloc_rsvd_page(rtwdev, RSVD_PROBE_REQ, true); 127762306a36Sopenharmony_ci if (!rsvd_pkt) { 127862306a36Sopenharmony_ci rtw_err(rtwdev, "failed to alloc probe req rsvd page\n"); 127962306a36Sopenharmony_ci return; 128062306a36Sopenharmony_ci } 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci rsvd_pkt->rtwvif = rtwvif; 128362306a36Sopenharmony_ci rsvd_pkt->ssid = ssid; 128462306a36Sopenharmony_ci rtw_insert_rsvd_page(rtwdev, rtwvif, rsvd_pkt); 128562306a36Sopenharmony_ci} 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_civoid rtw_remove_rsvd_page(struct rtw_dev *rtwdev, 128862306a36Sopenharmony_ci struct rtw_vif *rtwvif) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt, *tmp; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci lockdep_assert_held(&rtwdev->mutex); 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci /* remove all of the rsvd pages for vif */ 129562306a36Sopenharmony_ci list_for_each_entry_safe(rsvd_pkt, tmp, &rtwvif->rsvd_page_list, 129662306a36Sopenharmony_ci vif_list) { 129762306a36Sopenharmony_ci list_del(&rsvd_pkt->vif_list); 129862306a36Sopenharmony_ci if (!list_empty(&rsvd_pkt->build_list)) 129962306a36Sopenharmony_ci list_del(&rsvd_pkt->build_list); 130062306a36Sopenharmony_ci kfree(rsvd_pkt); 130162306a36Sopenharmony_ci } 130262306a36Sopenharmony_ci} 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_civoid rtw_add_rsvd_page_bcn(struct rtw_dev *rtwdev, 130562306a36Sopenharmony_ci struct rtw_vif *rtwvif) 130662306a36Sopenharmony_ci{ 130762306a36Sopenharmony_ci struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci if (vif->type != NL80211_IFTYPE_AP && 131062306a36Sopenharmony_ci vif->type != NL80211_IFTYPE_ADHOC && 131162306a36Sopenharmony_ci vif->type != NL80211_IFTYPE_MESH_POINT) { 131262306a36Sopenharmony_ci rtw_warn(rtwdev, "Cannot add beacon rsvd page for %d\n", 131362306a36Sopenharmony_ci vif->type); 131462306a36Sopenharmony_ci return; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_BEACON, false); 131862306a36Sopenharmony_ci} 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_civoid rtw_add_rsvd_page_pno(struct rtw_dev *rtwdev, 132162306a36Sopenharmony_ci struct rtw_vif *rtwvif) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); 132462306a36Sopenharmony_ci struct rtw_wow_param *rtw_wow = &rtwdev->wow; 132562306a36Sopenharmony_ci struct rtw_pno_request *rtw_pno_req = &rtw_wow->pno_req; 132662306a36Sopenharmony_ci struct cfg80211_ssid *ssid; 132762306a36Sopenharmony_ci int i; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci if (vif->type != NL80211_IFTYPE_STATION) { 133062306a36Sopenharmony_ci rtw_warn(rtwdev, "Cannot add PNO rsvd page for %d\n", 133162306a36Sopenharmony_ci vif->type); 133262306a36Sopenharmony_ci return; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci for (i = 0 ; i < rtw_pno_req->match_set_cnt; i++) { 133662306a36Sopenharmony_ci ssid = &rtw_pno_req->match_sets[i].ssid; 133762306a36Sopenharmony_ci rtw_add_rsvd_page_probe_req(rtwdev, rtwvif, ssid); 133862306a36Sopenharmony_ci } 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci rtw_add_rsvd_page_probe_req(rtwdev, rtwvif, NULL); 134162306a36Sopenharmony_ci rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_NLO_INFO, false); 134262306a36Sopenharmony_ci rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_CH_INFO, true); 134362306a36Sopenharmony_ci} 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_civoid rtw_add_rsvd_page_sta(struct rtw_dev *rtwdev, 134662306a36Sopenharmony_ci struct rtw_vif *rtwvif) 134762306a36Sopenharmony_ci{ 134862306a36Sopenharmony_ci struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci if (vif->type != NL80211_IFTYPE_STATION) { 135162306a36Sopenharmony_ci rtw_warn(rtwdev, "Cannot add sta rsvd page for %d\n", 135262306a36Sopenharmony_ci vif->type); 135362306a36Sopenharmony_ci return; 135462306a36Sopenharmony_ci } 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_PS_POLL, true); 135762306a36Sopenharmony_ci rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_QOS_NULL, true); 135862306a36Sopenharmony_ci rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_NULL, true); 135962306a36Sopenharmony_ci rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_LPS_PG_DPK, true); 136062306a36Sopenharmony_ci rtw_add_rsvd_page(rtwdev, rtwvif, RSVD_LPS_PG_INFO, true); 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ciint rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr, 136462306a36Sopenharmony_ci u8 *buf, u32 size) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci u8 bckp[2]; 136762306a36Sopenharmony_ci u8 val; 136862306a36Sopenharmony_ci u16 rsvd_pg_head; 136962306a36Sopenharmony_ci u32 bcn_valid_addr; 137062306a36Sopenharmony_ci u32 bcn_valid_mask; 137162306a36Sopenharmony_ci int ret; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci lockdep_assert_held(&rtwdev->mutex); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci if (!size) 137662306a36Sopenharmony_ci return -EINVAL; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (rtw_chip_wcpu_11n(rtwdev)) { 137962306a36Sopenharmony_ci rtw_write32_set(rtwdev, REG_DWBCN0_CTRL, BIT_BCN_VALID); 138062306a36Sopenharmony_ci } else { 138162306a36Sopenharmony_ci pg_addr &= BIT_MASK_BCN_HEAD_1_V1; 138262306a36Sopenharmony_ci pg_addr |= BIT_BCN_VALID_V1; 138362306a36Sopenharmony_ci rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, pg_addr); 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci val = rtw_read8(rtwdev, REG_CR + 1); 138762306a36Sopenharmony_ci bckp[0] = val; 138862306a36Sopenharmony_ci val |= BIT_ENSWBCN >> 8; 138962306a36Sopenharmony_ci rtw_write8(rtwdev, REG_CR + 1, val); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci val = rtw_read8(rtwdev, REG_FWHW_TXQ_CTRL + 2); 139262306a36Sopenharmony_ci bckp[1] = val; 139362306a36Sopenharmony_ci val &= ~(BIT_EN_BCNQ_DL >> 16); 139462306a36Sopenharmony_ci rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, val); 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci ret = rtw_hci_write_data_rsvd_page(rtwdev, buf, size); 139762306a36Sopenharmony_ci if (ret) { 139862306a36Sopenharmony_ci rtw_err(rtwdev, "failed to write data to rsvd page\n"); 139962306a36Sopenharmony_ci goto restore; 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci if (rtw_chip_wcpu_11n(rtwdev)) { 140362306a36Sopenharmony_ci bcn_valid_addr = REG_DWBCN0_CTRL; 140462306a36Sopenharmony_ci bcn_valid_mask = BIT_BCN_VALID; 140562306a36Sopenharmony_ci } else { 140662306a36Sopenharmony_ci bcn_valid_addr = REG_FIFOPAGE_CTRL_2; 140762306a36Sopenharmony_ci bcn_valid_mask = BIT_BCN_VALID_V1; 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci if (!check_hw_ready(rtwdev, bcn_valid_addr, bcn_valid_mask, 1)) { 141162306a36Sopenharmony_ci rtw_err(rtwdev, "error beacon valid\n"); 141262306a36Sopenharmony_ci ret = -EBUSY; 141362306a36Sopenharmony_ci } 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_cirestore: 141662306a36Sopenharmony_ci rsvd_pg_head = rtwdev->fifo.rsvd_boundary; 141762306a36Sopenharmony_ci rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, 141862306a36Sopenharmony_ci rsvd_pg_head | BIT_BCN_VALID_V1); 141962306a36Sopenharmony_ci rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, bckp[1]); 142062306a36Sopenharmony_ci rtw_write8(rtwdev, REG_CR + 1, bckp[0]); 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci return ret; 142362306a36Sopenharmony_ci} 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_cistatic int rtw_download_drv_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size) 142662306a36Sopenharmony_ci{ 142762306a36Sopenharmony_ci u32 pg_size; 142862306a36Sopenharmony_ci u32 pg_num = 0; 142962306a36Sopenharmony_ci u16 pg_addr = 0; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci pg_size = rtwdev->chip->page_size; 143262306a36Sopenharmony_ci pg_num = size / pg_size + ((size & (pg_size - 1)) ? 1 : 0); 143362306a36Sopenharmony_ci if (pg_num > rtwdev->fifo.rsvd_drv_pg_num) 143462306a36Sopenharmony_ci return -ENOMEM; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci pg_addr = rtwdev->fifo.rsvd_drv_addr; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci return rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, size); 143962306a36Sopenharmony_ci} 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_cistatic void __rtw_build_rsvd_page_reset(struct rtw_dev *rtwdev) 144262306a36Sopenharmony_ci{ 144362306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt, *tmp; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci list_for_each_entry_safe(rsvd_pkt, tmp, &rtwdev->rsvd_page_list, 144662306a36Sopenharmony_ci build_list) { 144762306a36Sopenharmony_ci list_del_init(&rsvd_pkt->build_list); 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci /* Don't free except for the dummy rsvd page, 145062306a36Sopenharmony_ci * others will be freed when removing vif 145162306a36Sopenharmony_ci */ 145262306a36Sopenharmony_ci if (rsvd_pkt->type == RSVD_DUMMY) 145362306a36Sopenharmony_ci kfree(rsvd_pkt); 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci} 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_cistatic void rtw_build_rsvd_page_iter(void *data, u8 *mac, 145862306a36Sopenharmony_ci struct ieee80211_vif *vif) 145962306a36Sopenharmony_ci{ 146062306a36Sopenharmony_ci struct rtw_dev *rtwdev = data; 146162306a36Sopenharmony_ci struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; 146262306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci /* AP not yet started, don't gather its rsvd pages */ 146562306a36Sopenharmony_ci if (vif->type == NL80211_IFTYPE_AP && !rtwdev->ap_active) 146662306a36Sopenharmony_ci return; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci list_for_each_entry(rsvd_pkt, &rtwvif->rsvd_page_list, vif_list) { 146962306a36Sopenharmony_ci if (rsvd_pkt->type == RSVD_BEACON) 147062306a36Sopenharmony_ci list_add(&rsvd_pkt->build_list, 147162306a36Sopenharmony_ci &rtwdev->rsvd_page_list); 147262306a36Sopenharmony_ci else 147362306a36Sopenharmony_ci list_add_tail(&rsvd_pkt->build_list, 147462306a36Sopenharmony_ci &rtwdev->rsvd_page_list); 147562306a36Sopenharmony_ci } 147662306a36Sopenharmony_ci} 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_cistatic int __rtw_build_rsvd_page_from_vifs(struct rtw_dev *rtwdev) 147962306a36Sopenharmony_ci{ 148062306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci __rtw_build_rsvd_page_reset(rtwdev); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci /* gather rsvd page from vifs */ 148562306a36Sopenharmony_ci rtw_iterate_vifs_atomic(rtwdev, rtw_build_rsvd_page_iter, rtwdev); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci rsvd_pkt = list_first_entry_or_null(&rtwdev->rsvd_page_list, 148862306a36Sopenharmony_ci struct rtw_rsvd_page, build_list); 148962306a36Sopenharmony_ci if (!rsvd_pkt) { 149062306a36Sopenharmony_ci WARN(1, "Should not have an empty reserved page\n"); 149162306a36Sopenharmony_ci return -EINVAL; 149262306a36Sopenharmony_ci } 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci /* the first rsvd should be beacon, otherwise add a dummy one */ 149562306a36Sopenharmony_ci if (rsvd_pkt->type != RSVD_BEACON) { 149662306a36Sopenharmony_ci struct rtw_rsvd_page *dummy_pkt; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci dummy_pkt = rtw_alloc_rsvd_page(rtwdev, RSVD_DUMMY, false); 149962306a36Sopenharmony_ci if (!dummy_pkt) { 150062306a36Sopenharmony_ci rtw_err(rtwdev, "failed to alloc dummy rsvd page\n"); 150162306a36Sopenharmony_ci return -ENOMEM; 150262306a36Sopenharmony_ci } 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci list_add(&dummy_pkt->build_list, &rtwdev->rsvd_page_list); 150562306a36Sopenharmony_ci } 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci return 0; 150862306a36Sopenharmony_ci} 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_cistatic u8 *rtw_build_rsvd_page(struct rtw_dev *rtwdev, u32 *size) 151162306a36Sopenharmony_ci{ 151262306a36Sopenharmony_ci struct ieee80211_hw *hw = rtwdev->hw; 151362306a36Sopenharmony_ci const struct rtw_chip_info *chip = rtwdev->chip; 151462306a36Sopenharmony_ci struct sk_buff *iter; 151562306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt; 151662306a36Sopenharmony_ci u32 page = 0; 151762306a36Sopenharmony_ci u8 total_page = 0; 151862306a36Sopenharmony_ci u8 page_size, page_margin, tx_desc_sz; 151962306a36Sopenharmony_ci u8 *buf; 152062306a36Sopenharmony_ci int ret; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci page_size = chip->page_size; 152362306a36Sopenharmony_ci tx_desc_sz = chip->tx_pkt_desc_sz; 152462306a36Sopenharmony_ci page_margin = page_size - tx_desc_sz; 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci ret = __rtw_build_rsvd_page_from_vifs(rtwdev); 152762306a36Sopenharmony_ci if (ret) { 152862306a36Sopenharmony_ci rtw_err(rtwdev, 152962306a36Sopenharmony_ci "failed to build rsvd page from vifs, ret %d\n", ret); 153062306a36Sopenharmony_ci return NULL; 153162306a36Sopenharmony_ci } 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { 153462306a36Sopenharmony_ci iter = rtw_get_rsvd_page_skb(hw, rsvd_pkt); 153562306a36Sopenharmony_ci if (!iter) { 153662306a36Sopenharmony_ci rtw_err(rtwdev, "failed to build rsvd packet\n"); 153762306a36Sopenharmony_ci goto release_skb; 153862306a36Sopenharmony_ci } 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci /* Fill the tx_desc for the rsvd pkt that requires one. 154162306a36Sopenharmony_ci * And iter->len will be added with size of tx_desc_sz. 154262306a36Sopenharmony_ci */ 154362306a36Sopenharmony_ci if (rsvd_pkt->add_txdesc) 154462306a36Sopenharmony_ci rtw_fill_rsvd_page_desc(rtwdev, iter, rsvd_pkt->type); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci rsvd_pkt->skb = iter; 154762306a36Sopenharmony_ci rsvd_pkt->page = total_page; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci /* Reserved page is downloaded via TX path, and TX path will 155062306a36Sopenharmony_ci * generate a tx_desc at the header to describe length of 155162306a36Sopenharmony_ci * the buffer. If we are not counting page numbers with the 155262306a36Sopenharmony_ci * size of tx_desc added at the first rsvd_pkt (usually a 155362306a36Sopenharmony_ci * beacon, firmware default refer to the first page as the 155462306a36Sopenharmony_ci * content of beacon), we could generate a buffer which size 155562306a36Sopenharmony_ci * is smaller than the actual size of the whole rsvd_page 155662306a36Sopenharmony_ci */ 155762306a36Sopenharmony_ci if (total_page == 0) { 155862306a36Sopenharmony_ci if (rsvd_pkt->type != RSVD_BEACON && 155962306a36Sopenharmony_ci rsvd_pkt->type != RSVD_DUMMY) { 156062306a36Sopenharmony_ci rtw_err(rtwdev, "first page should be a beacon\n"); 156162306a36Sopenharmony_ci goto release_skb; 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci total_page += rtw_len_to_page(iter->len + tx_desc_sz, 156462306a36Sopenharmony_ci page_size); 156562306a36Sopenharmony_ci } else { 156662306a36Sopenharmony_ci total_page += rtw_len_to_page(iter->len, page_size); 156762306a36Sopenharmony_ci } 156862306a36Sopenharmony_ci } 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci if (total_page > rtwdev->fifo.rsvd_drv_pg_num) { 157162306a36Sopenharmony_ci rtw_err(rtwdev, "rsvd page over size: %d\n", total_page); 157262306a36Sopenharmony_ci goto release_skb; 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci *size = (total_page - 1) * page_size + page_margin; 157662306a36Sopenharmony_ci buf = kzalloc(*size, GFP_KERNEL); 157762306a36Sopenharmony_ci if (!buf) 157862306a36Sopenharmony_ci goto release_skb; 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci /* Copy the content of each rsvd_pkt to the buf, and they should 158162306a36Sopenharmony_ci * be aligned to the pages. 158262306a36Sopenharmony_ci * 158362306a36Sopenharmony_ci * Note that the first rsvd_pkt is a beacon no matter what vif->type. 158462306a36Sopenharmony_ci * And that rsvd_pkt does not require tx_desc because when it goes 158562306a36Sopenharmony_ci * through TX path, the TX path will generate one for it. 158662306a36Sopenharmony_ci */ 158762306a36Sopenharmony_ci list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { 158862306a36Sopenharmony_ci rtw_rsvd_page_list_to_buf(rtwdev, page_size, page_margin, 158962306a36Sopenharmony_ci page, buf, rsvd_pkt); 159062306a36Sopenharmony_ci if (page == 0) 159162306a36Sopenharmony_ci page += rtw_len_to_page(rsvd_pkt->skb->len + 159262306a36Sopenharmony_ci tx_desc_sz, page_size); 159362306a36Sopenharmony_ci else 159462306a36Sopenharmony_ci page += rtw_len_to_page(rsvd_pkt->skb->len, page_size); 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci kfree_skb(rsvd_pkt->skb); 159762306a36Sopenharmony_ci rsvd_pkt->skb = NULL; 159862306a36Sopenharmony_ci } 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci return buf; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_cirelease_skb: 160362306a36Sopenharmony_ci list_for_each_entry(rsvd_pkt, &rtwdev->rsvd_page_list, build_list) { 160462306a36Sopenharmony_ci kfree_skb(rsvd_pkt->skb); 160562306a36Sopenharmony_ci rsvd_pkt->skb = NULL; 160662306a36Sopenharmony_ci } 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci return NULL; 160962306a36Sopenharmony_ci} 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_cistatic int rtw_download_beacon(struct rtw_dev *rtwdev) 161262306a36Sopenharmony_ci{ 161362306a36Sopenharmony_ci struct ieee80211_hw *hw = rtwdev->hw; 161462306a36Sopenharmony_ci struct rtw_rsvd_page *rsvd_pkt; 161562306a36Sopenharmony_ci struct sk_buff *skb; 161662306a36Sopenharmony_ci int ret = 0; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci rsvd_pkt = list_first_entry_or_null(&rtwdev->rsvd_page_list, 161962306a36Sopenharmony_ci struct rtw_rsvd_page, build_list); 162062306a36Sopenharmony_ci if (!rsvd_pkt) { 162162306a36Sopenharmony_ci rtw_err(rtwdev, "failed to get rsvd page from build list\n"); 162262306a36Sopenharmony_ci return -ENOENT; 162362306a36Sopenharmony_ci } 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci if (rsvd_pkt->type != RSVD_BEACON && 162662306a36Sopenharmony_ci rsvd_pkt->type != RSVD_DUMMY) { 162762306a36Sopenharmony_ci rtw_err(rtwdev, "invalid rsvd page type %d, should be beacon or dummy\n", 162862306a36Sopenharmony_ci rsvd_pkt->type); 162962306a36Sopenharmony_ci return -EINVAL; 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci skb = rtw_get_rsvd_page_skb(hw, rsvd_pkt); 163362306a36Sopenharmony_ci if (!skb) { 163462306a36Sopenharmony_ci rtw_err(rtwdev, "failed to get beacon skb\n"); 163562306a36Sopenharmony_ci return -ENOMEM; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci ret = rtw_download_drv_rsvd_page(rtwdev, skb->data, skb->len); 163962306a36Sopenharmony_ci if (ret) 164062306a36Sopenharmony_ci rtw_err(rtwdev, "failed to download drv rsvd page\n"); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci dev_kfree_skb(skb); 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci return ret; 164562306a36Sopenharmony_ci} 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ciint rtw_fw_download_rsvd_page(struct rtw_dev *rtwdev) 164862306a36Sopenharmony_ci{ 164962306a36Sopenharmony_ci u8 *buf; 165062306a36Sopenharmony_ci u32 size; 165162306a36Sopenharmony_ci int ret; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci buf = rtw_build_rsvd_page(rtwdev, &size); 165462306a36Sopenharmony_ci if (!buf) { 165562306a36Sopenharmony_ci rtw_err(rtwdev, "failed to build rsvd page pkt\n"); 165662306a36Sopenharmony_ci return -ENOMEM; 165762306a36Sopenharmony_ci } 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci ret = rtw_download_drv_rsvd_page(rtwdev, buf, size); 166062306a36Sopenharmony_ci if (ret) { 166162306a36Sopenharmony_ci rtw_err(rtwdev, "failed to download drv rsvd page\n"); 166262306a36Sopenharmony_ci goto free; 166362306a36Sopenharmony_ci } 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci /* The last thing is to download the *ONLY* beacon again, because 166662306a36Sopenharmony_ci * the previous tx_desc is to describe the total rsvd page. Download 166762306a36Sopenharmony_ci * the beacon again to replace the TX desc header, and we will get 166862306a36Sopenharmony_ci * a correct tx_desc for the beacon in the rsvd page. 166962306a36Sopenharmony_ci */ 167062306a36Sopenharmony_ci ret = rtw_download_beacon(rtwdev); 167162306a36Sopenharmony_ci if (ret) { 167262306a36Sopenharmony_ci rtw_err(rtwdev, "failed to download beacon\n"); 167362306a36Sopenharmony_ci goto free; 167462306a36Sopenharmony_ci } 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_cifree: 167762306a36Sopenharmony_ci kfree(buf); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci return ret; 168062306a36Sopenharmony_ci} 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_civoid rtw_fw_update_beacon_work(struct work_struct *work) 168362306a36Sopenharmony_ci{ 168462306a36Sopenharmony_ci struct rtw_dev *rtwdev = container_of(work, struct rtw_dev, 168562306a36Sopenharmony_ci update_beacon_work); 168662306a36Sopenharmony_ci 168762306a36Sopenharmony_ci mutex_lock(&rtwdev->mutex); 168862306a36Sopenharmony_ci rtw_fw_download_rsvd_page(rtwdev); 168962306a36Sopenharmony_ci rtw_send_rsvd_page_h2c(rtwdev); 169062306a36Sopenharmony_ci mutex_unlock(&rtwdev->mutex); 169162306a36Sopenharmony_ci} 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_cistatic void rtw_fw_read_fifo_page(struct rtw_dev *rtwdev, u32 offset, u32 size, 169462306a36Sopenharmony_ci u32 *buf, u32 residue, u16 start_pg) 169562306a36Sopenharmony_ci{ 169662306a36Sopenharmony_ci u32 i; 169762306a36Sopenharmony_ci u16 idx = 0; 169862306a36Sopenharmony_ci u16 ctl; 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci ctl = rtw_read16(rtwdev, REG_PKTBUF_DBG_CTRL) & 0xf000; 170162306a36Sopenharmony_ci /* disable rx clock gate */ 170262306a36Sopenharmony_ci rtw_write32_set(rtwdev, REG_RCR, BIT_DISGCLK); 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci do { 170562306a36Sopenharmony_ci rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, start_pg | ctl); 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci for (i = FIFO_DUMP_ADDR + residue; 170862306a36Sopenharmony_ci i < FIFO_DUMP_ADDR + FIFO_PAGE_SIZE; i += 4) { 170962306a36Sopenharmony_ci buf[idx++] = rtw_read32(rtwdev, i); 171062306a36Sopenharmony_ci size -= 4; 171162306a36Sopenharmony_ci if (size == 0) 171262306a36Sopenharmony_ci goto out; 171362306a36Sopenharmony_ci } 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci residue = 0; 171662306a36Sopenharmony_ci start_pg++; 171762306a36Sopenharmony_ci } while (size); 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ciout: 172062306a36Sopenharmony_ci rtw_write16(rtwdev, REG_PKTBUF_DBG_CTRL, ctl); 172162306a36Sopenharmony_ci /* restore rx clock gate */ 172262306a36Sopenharmony_ci rtw_write32_clr(rtwdev, REG_RCR, BIT_DISGCLK); 172362306a36Sopenharmony_ci} 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_cistatic void rtw_fw_read_fifo(struct rtw_dev *rtwdev, enum rtw_fw_fifo_sel sel, 172662306a36Sopenharmony_ci u32 offset, u32 size, u32 *buf) 172762306a36Sopenharmony_ci{ 172862306a36Sopenharmony_ci const struct rtw_chip_info *chip = rtwdev->chip; 172962306a36Sopenharmony_ci u32 start_pg, residue; 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci if (sel >= RTW_FW_FIFO_MAX) { 173262306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, "wrong fw fifo sel\n"); 173362306a36Sopenharmony_ci return; 173462306a36Sopenharmony_ci } 173562306a36Sopenharmony_ci if (sel == RTW_FW_FIFO_SEL_RSVD_PAGE) 173662306a36Sopenharmony_ci offset += rtwdev->fifo.rsvd_boundary << TX_PAGE_SIZE_SHIFT; 173762306a36Sopenharmony_ci residue = offset & (FIFO_PAGE_SIZE - 1); 173862306a36Sopenharmony_ci start_pg = (offset >> FIFO_PAGE_SIZE_SHIFT) + chip->fw_fifo_addr[sel]; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci rtw_fw_read_fifo_page(rtwdev, offset, size, buf, residue, start_pg); 174162306a36Sopenharmony_ci} 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_cistatic bool rtw_fw_dump_check_size(struct rtw_dev *rtwdev, 174462306a36Sopenharmony_ci enum rtw_fw_fifo_sel sel, 174562306a36Sopenharmony_ci u32 start_addr, u32 size) 174662306a36Sopenharmony_ci{ 174762306a36Sopenharmony_ci switch (sel) { 174862306a36Sopenharmony_ci case RTW_FW_FIFO_SEL_TX: 174962306a36Sopenharmony_ci case RTW_FW_FIFO_SEL_RX: 175062306a36Sopenharmony_ci if ((start_addr + size) > rtwdev->chip->fw_fifo_addr[sel]) 175162306a36Sopenharmony_ci return false; 175262306a36Sopenharmony_ci fallthrough; 175362306a36Sopenharmony_ci default: 175462306a36Sopenharmony_ci return true; 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci} 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ciint rtw_fw_dump_fifo(struct rtw_dev *rtwdev, u8 fifo_sel, u32 addr, u32 size, 175962306a36Sopenharmony_ci u32 *buffer) 176062306a36Sopenharmony_ci{ 176162306a36Sopenharmony_ci if (!rtwdev->chip->fw_fifo_addr[0]) { 176262306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, "chip not support dump fw fifo\n"); 176362306a36Sopenharmony_ci return -ENOTSUPP; 176462306a36Sopenharmony_ci } 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci if (size == 0 || !buffer) 176762306a36Sopenharmony_ci return -EINVAL; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci if (size & 0x3) { 177062306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, "not 4byte alignment\n"); 177162306a36Sopenharmony_ci return -EINVAL; 177262306a36Sopenharmony_ci } 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci if (!rtw_fw_dump_check_size(rtwdev, fifo_sel, addr, size)) { 177562306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_FW, "fw fifo dump size overflow\n"); 177662306a36Sopenharmony_ci return -EINVAL; 177762306a36Sopenharmony_ci } 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci rtw_fw_read_fifo(rtwdev, fifo_sel, addr, size, buffer); 178062306a36Sopenharmony_ci 178162306a36Sopenharmony_ci return 0; 178262306a36Sopenharmony_ci} 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_cistatic void __rtw_fw_update_pkt(struct rtw_dev *rtwdev, u8 pkt_id, u16 size, 178562306a36Sopenharmony_ci u8 location) 178662306a36Sopenharmony_ci{ 178762306a36Sopenharmony_ci const struct rtw_chip_info *chip = rtwdev->chip; 178862306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 178962306a36Sopenharmony_ci u16 total_size = H2C_PKT_HDR_SIZE + H2C_PKT_UPDATE_PKT_LEN; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_UPDATE_PKT); 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size); 179462306a36Sopenharmony_ci UPDATE_PKT_SET_PKT_ID(h2c_pkt, pkt_id); 179562306a36Sopenharmony_ci UPDATE_PKT_SET_LOCATION(h2c_pkt, location); 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_ci /* include txdesc size */ 179862306a36Sopenharmony_ci size += chip->tx_pkt_desc_sz; 179962306a36Sopenharmony_ci UPDATE_PKT_SET_SIZE(h2c_pkt, size); 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci rtw_fw_send_h2c_packet(rtwdev, h2c_pkt); 180262306a36Sopenharmony_ci} 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_civoid rtw_fw_update_pkt_probe_req(struct rtw_dev *rtwdev, 180562306a36Sopenharmony_ci struct cfg80211_ssid *ssid) 180662306a36Sopenharmony_ci{ 180762306a36Sopenharmony_ci u8 loc; 180862306a36Sopenharmony_ci u16 size; 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci loc = rtw_get_rsvd_page_probe_req_location(rtwdev, ssid); 181162306a36Sopenharmony_ci if (!loc) { 181262306a36Sopenharmony_ci rtw_err(rtwdev, "failed to get probe_req rsvd loc\n"); 181362306a36Sopenharmony_ci return; 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci size = rtw_get_rsvd_page_probe_req_size(rtwdev, ssid); 181762306a36Sopenharmony_ci if (!size) { 181862306a36Sopenharmony_ci rtw_err(rtwdev, "failed to get probe_req rsvd size\n"); 181962306a36Sopenharmony_ci return; 182062306a36Sopenharmony_ci } 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci __rtw_fw_update_pkt(rtwdev, RTW_PACKET_PROBE_REQ, size, loc); 182362306a36Sopenharmony_ci} 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_civoid rtw_fw_channel_switch(struct rtw_dev *rtwdev, bool enable) 182662306a36Sopenharmony_ci{ 182762306a36Sopenharmony_ci struct rtw_pno_request *rtw_pno_req = &rtwdev->wow.pno_req; 182862306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 182962306a36Sopenharmony_ci u16 total_size = H2C_PKT_HDR_SIZE + H2C_PKT_CH_SWITCH_LEN; 183062306a36Sopenharmony_ci u8 loc_ch_info; 183162306a36Sopenharmony_ci const struct rtw_ch_switch_option cs_option = { 183262306a36Sopenharmony_ci .dest_ch_en = 1, 183362306a36Sopenharmony_ci .dest_ch = 1, 183462306a36Sopenharmony_ci .periodic_option = 2, 183562306a36Sopenharmony_ci .normal_period = 5, 183662306a36Sopenharmony_ci .normal_period_sel = 0, 183762306a36Sopenharmony_ci .normal_cycle = 10, 183862306a36Sopenharmony_ci .slow_period = 1, 183962306a36Sopenharmony_ci .slow_period_sel = 1, 184062306a36Sopenharmony_ci }; 184162306a36Sopenharmony_ci 184262306a36Sopenharmony_ci rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_CH_SWITCH); 184362306a36Sopenharmony_ci SET_PKT_H2C_TOTAL_LEN(h2c_pkt, total_size); 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci CH_SWITCH_SET_START(h2c_pkt, enable); 184662306a36Sopenharmony_ci CH_SWITCH_SET_DEST_CH_EN(h2c_pkt, cs_option.dest_ch_en); 184762306a36Sopenharmony_ci CH_SWITCH_SET_DEST_CH(h2c_pkt, cs_option.dest_ch); 184862306a36Sopenharmony_ci CH_SWITCH_SET_NORMAL_PERIOD(h2c_pkt, cs_option.normal_period); 184962306a36Sopenharmony_ci CH_SWITCH_SET_NORMAL_PERIOD_SEL(h2c_pkt, cs_option.normal_period_sel); 185062306a36Sopenharmony_ci CH_SWITCH_SET_SLOW_PERIOD(h2c_pkt, cs_option.slow_period); 185162306a36Sopenharmony_ci CH_SWITCH_SET_SLOW_PERIOD_SEL(h2c_pkt, cs_option.slow_period_sel); 185262306a36Sopenharmony_ci CH_SWITCH_SET_NORMAL_CYCLE(h2c_pkt, cs_option.normal_cycle); 185362306a36Sopenharmony_ci CH_SWITCH_SET_PERIODIC_OPT(h2c_pkt, cs_option.periodic_option); 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_ci CH_SWITCH_SET_CH_NUM(h2c_pkt, rtw_pno_req->channel_cnt); 185662306a36Sopenharmony_ci CH_SWITCH_SET_INFO_SIZE(h2c_pkt, rtw_pno_req->channel_cnt * 4); 185762306a36Sopenharmony_ci 185862306a36Sopenharmony_ci loc_ch_info = rtw_get_rsvd_page_location(rtwdev, RSVD_CH_INFO); 185962306a36Sopenharmony_ci CH_SWITCH_SET_INFO_LOC(h2c_pkt, loc_ch_info); 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci rtw_fw_send_h2c_packet(rtwdev, h2c_pkt); 186262306a36Sopenharmony_ci} 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_civoid rtw_fw_adaptivity(struct rtw_dev *rtwdev) 186562306a36Sopenharmony_ci{ 186662306a36Sopenharmony_ci struct rtw_dm_info *dm_info = &rtwdev->dm_info; 186762306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci if (!rtw_edcca_enabled) { 187062306a36Sopenharmony_ci dm_info->edcca_mode = RTW_EDCCA_NORMAL; 187162306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_ADAPTIVITY, 187262306a36Sopenharmony_ci "EDCCA disabled by debugfs\n"); 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_ADAPTIVITY); 187662306a36Sopenharmony_ci SET_ADAPTIVITY_MODE(h2c_pkt, dm_info->edcca_mode); 187762306a36Sopenharmony_ci SET_ADAPTIVITY_OPTION(h2c_pkt, 1); 187862306a36Sopenharmony_ci SET_ADAPTIVITY_IGI(h2c_pkt, dm_info->igi_history[0]); 187962306a36Sopenharmony_ci SET_ADAPTIVITY_L2H(h2c_pkt, dm_info->l2h_th_ini); 188062306a36Sopenharmony_ci SET_ADAPTIVITY_DENSITY(h2c_pkt, dm_info->scan_density); 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 188362306a36Sopenharmony_ci} 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_civoid rtw_fw_scan_notify(struct rtw_dev *rtwdev, bool start) 188662306a36Sopenharmony_ci{ 188762306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_SCAN); 189062306a36Sopenharmony_ci SET_SCAN_START(h2c_pkt, start); 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci rtw_fw_send_h2c_command(rtwdev, h2c_pkt); 189362306a36Sopenharmony_ci} 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_cistatic int rtw_append_probe_req_ie(struct rtw_dev *rtwdev, struct sk_buff *skb, 189662306a36Sopenharmony_ci struct sk_buff_head *list, u8 *bands, 189762306a36Sopenharmony_ci struct rtw_vif *rtwvif) 189862306a36Sopenharmony_ci{ 189962306a36Sopenharmony_ci const struct rtw_chip_info *chip = rtwdev->chip; 190062306a36Sopenharmony_ci struct ieee80211_scan_ies *ies = rtwvif->scan_ies; 190162306a36Sopenharmony_ci struct sk_buff *new; 190262306a36Sopenharmony_ci u8 idx; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci for (idx = NL80211_BAND_2GHZ; idx < NUM_NL80211_BANDS; idx++) { 190562306a36Sopenharmony_ci if (!(BIT(idx) & chip->band)) 190662306a36Sopenharmony_ci continue; 190762306a36Sopenharmony_ci new = skb_copy(skb, GFP_KERNEL); 190862306a36Sopenharmony_ci if (!new) 190962306a36Sopenharmony_ci return -ENOMEM; 191062306a36Sopenharmony_ci skb_put_data(new, ies->ies[idx], ies->len[idx]); 191162306a36Sopenharmony_ci skb_put_data(new, ies->common_ies, ies->common_ie_len); 191262306a36Sopenharmony_ci skb_queue_tail(list, new); 191362306a36Sopenharmony_ci (*bands)++; 191462306a36Sopenharmony_ci } 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci return 0; 191762306a36Sopenharmony_ci} 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_cistatic int _rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev, u8 num_probes, 192062306a36Sopenharmony_ci struct sk_buff_head *probe_req_list) 192162306a36Sopenharmony_ci{ 192262306a36Sopenharmony_ci const struct rtw_chip_info *chip = rtwdev->chip; 192362306a36Sopenharmony_ci struct sk_buff *skb, *tmp; 192462306a36Sopenharmony_ci u8 page_offset = 1, *buf, page_size = chip->page_size; 192562306a36Sopenharmony_ci u16 pg_addr = rtwdev->fifo.rsvd_h2c_info_addr, loc; 192662306a36Sopenharmony_ci u16 buf_offset = page_size * page_offset; 192762306a36Sopenharmony_ci u8 tx_desc_sz = chip->tx_pkt_desc_sz; 192862306a36Sopenharmony_ci u8 page_cnt, pages; 192962306a36Sopenharmony_ci unsigned int pkt_len; 193062306a36Sopenharmony_ci int ret; 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci if (rtw_fw_feature_ext_check(&rtwdev->fw, FW_FEATURE_EXT_OLD_PAGE_NUM)) 193362306a36Sopenharmony_ci page_cnt = RTW_OLD_PROBE_PG_CNT; 193462306a36Sopenharmony_ci else 193562306a36Sopenharmony_ci page_cnt = RTW_PROBE_PG_CNT; 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci pages = page_offset + num_probes * page_cnt; 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_ci buf = kzalloc(page_size * pages, GFP_KERNEL); 194062306a36Sopenharmony_ci if (!buf) 194162306a36Sopenharmony_ci return -ENOMEM; 194262306a36Sopenharmony_ci 194362306a36Sopenharmony_ci buf_offset -= tx_desc_sz; 194462306a36Sopenharmony_ci skb_queue_walk_safe(probe_req_list, skb, tmp) { 194562306a36Sopenharmony_ci skb_unlink(skb, probe_req_list); 194662306a36Sopenharmony_ci rtw_fill_rsvd_page_desc(rtwdev, skb, RSVD_PROBE_REQ); 194762306a36Sopenharmony_ci if (skb->len > page_size * page_cnt) { 194862306a36Sopenharmony_ci ret = -EINVAL; 194962306a36Sopenharmony_ci goto out; 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_ci memcpy(buf + buf_offset, skb->data, skb->len); 195362306a36Sopenharmony_ci pkt_len = skb->len - tx_desc_sz; 195462306a36Sopenharmony_ci loc = pg_addr - rtwdev->fifo.rsvd_boundary + page_offset; 195562306a36Sopenharmony_ci __rtw_fw_update_pkt(rtwdev, RTW_PACKET_PROBE_REQ, pkt_len, loc); 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci buf_offset += page_cnt * page_size; 195862306a36Sopenharmony_ci page_offset += page_cnt; 195962306a36Sopenharmony_ci kfree_skb(skb); 196062306a36Sopenharmony_ci } 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci ret = rtw_fw_write_data_rsvd_page(rtwdev, pg_addr, buf, buf_offset); 196362306a36Sopenharmony_ci if (ret) { 196462306a36Sopenharmony_ci rtw_err(rtwdev, "Download probe request to firmware failed\n"); 196562306a36Sopenharmony_ci goto out; 196662306a36Sopenharmony_ci } 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci rtwdev->scan_info.probe_pg_size = page_offset; 196962306a36Sopenharmony_ciout: 197062306a36Sopenharmony_ci kfree(buf); 197162306a36Sopenharmony_ci skb_queue_walk_safe(probe_req_list, skb, tmp) 197262306a36Sopenharmony_ci kfree_skb(skb); 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci return ret; 197562306a36Sopenharmony_ci} 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_cistatic int rtw_hw_scan_update_probe_req(struct rtw_dev *rtwdev, 197862306a36Sopenharmony_ci struct rtw_vif *rtwvif) 197962306a36Sopenharmony_ci{ 198062306a36Sopenharmony_ci struct cfg80211_scan_request *req = rtwvif->scan_req; 198162306a36Sopenharmony_ci struct sk_buff_head list; 198262306a36Sopenharmony_ci struct sk_buff *skb, *tmp; 198362306a36Sopenharmony_ci u8 num = req->n_ssids, i, bands = 0; 198462306a36Sopenharmony_ci int ret; 198562306a36Sopenharmony_ci 198662306a36Sopenharmony_ci skb_queue_head_init(&list); 198762306a36Sopenharmony_ci for (i = 0; i < num; i++) { 198862306a36Sopenharmony_ci skb = ieee80211_probereq_get(rtwdev->hw, rtwvif->mac_addr, 198962306a36Sopenharmony_ci req->ssids[i].ssid, 199062306a36Sopenharmony_ci req->ssids[i].ssid_len, 199162306a36Sopenharmony_ci req->ie_len); 199262306a36Sopenharmony_ci if (!skb) { 199362306a36Sopenharmony_ci ret = -ENOMEM; 199462306a36Sopenharmony_ci goto out; 199562306a36Sopenharmony_ci } 199662306a36Sopenharmony_ci ret = rtw_append_probe_req_ie(rtwdev, skb, &list, &bands, 199762306a36Sopenharmony_ci rtwvif); 199862306a36Sopenharmony_ci if (ret) 199962306a36Sopenharmony_ci goto out; 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci kfree_skb(skb); 200262306a36Sopenharmony_ci } 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci return _rtw_hw_scan_update_probe_req(rtwdev, num * bands, &list); 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ciout: 200762306a36Sopenharmony_ci skb_queue_walk_safe(&list, skb, tmp) 200862306a36Sopenharmony_ci kfree_skb(skb); 200962306a36Sopenharmony_ci 201062306a36Sopenharmony_ci return ret; 201162306a36Sopenharmony_ci} 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_cistatic int rtw_add_chan_info(struct rtw_dev *rtwdev, struct rtw_chan_info *info, 201462306a36Sopenharmony_ci struct rtw_chan_list *list, u8 *buf) 201562306a36Sopenharmony_ci{ 201662306a36Sopenharmony_ci u8 *chan = &buf[list->size]; 201762306a36Sopenharmony_ci u8 info_size = RTW_CH_INFO_SIZE; 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci if (list->size > list->buf_size) 202062306a36Sopenharmony_ci return -ENOMEM; 202162306a36Sopenharmony_ci 202262306a36Sopenharmony_ci CH_INFO_SET_CH(chan, info->channel); 202362306a36Sopenharmony_ci CH_INFO_SET_PRI_CH_IDX(chan, info->pri_ch_idx); 202462306a36Sopenharmony_ci CH_INFO_SET_BW(chan, info->bw); 202562306a36Sopenharmony_ci CH_INFO_SET_TIMEOUT(chan, info->timeout); 202662306a36Sopenharmony_ci CH_INFO_SET_ACTION_ID(chan, info->action_id); 202762306a36Sopenharmony_ci CH_INFO_SET_EXTRA_INFO(chan, info->extra_info); 202862306a36Sopenharmony_ci if (info->extra_info) { 202962306a36Sopenharmony_ci EXTRA_CH_INFO_SET_ID(chan, RTW_SCAN_EXTRA_ID_DFS); 203062306a36Sopenharmony_ci EXTRA_CH_INFO_SET_INFO(chan, RTW_SCAN_EXTRA_ACTION_SCAN); 203162306a36Sopenharmony_ci EXTRA_CH_INFO_SET_SIZE(chan, RTW_EX_CH_INFO_SIZE - 203262306a36Sopenharmony_ci RTW_EX_CH_INFO_HDR_SIZE); 203362306a36Sopenharmony_ci EXTRA_CH_INFO_SET_DFS_EXT_TIME(chan, RTW_DFS_CHAN_TIME); 203462306a36Sopenharmony_ci info_size += RTW_EX_CH_INFO_SIZE; 203562306a36Sopenharmony_ci } 203662306a36Sopenharmony_ci list->size += info_size; 203762306a36Sopenharmony_ci list->ch_num++; 203862306a36Sopenharmony_ci 203962306a36Sopenharmony_ci return 0; 204062306a36Sopenharmony_ci} 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_cistatic int rtw_add_chan_list(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, 204362306a36Sopenharmony_ci struct rtw_chan_list *list, u8 *buf) 204462306a36Sopenharmony_ci{ 204562306a36Sopenharmony_ci struct cfg80211_scan_request *req = rtwvif->scan_req; 204662306a36Sopenharmony_ci struct rtw_fifo_conf *fifo = &rtwdev->fifo; 204762306a36Sopenharmony_ci struct ieee80211_channel *channel; 204862306a36Sopenharmony_ci int i, ret = 0; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci for (i = 0; i < req->n_channels; i++) { 205162306a36Sopenharmony_ci struct rtw_chan_info ch_info = {0}; 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci channel = req->channels[i]; 205462306a36Sopenharmony_ci ch_info.channel = channel->hw_value; 205562306a36Sopenharmony_ci ch_info.bw = RTW_SCAN_WIDTH; 205662306a36Sopenharmony_ci ch_info.pri_ch_idx = RTW_PRI_CH_IDX; 205762306a36Sopenharmony_ci ch_info.timeout = req->duration_mandatory ? 205862306a36Sopenharmony_ci req->duration : RTW_CHANNEL_TIME; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci if (channel->flags & (IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IR)) { 206162306a36Sopenharmony_ci ch_info.action_id = RTW_CHANNEL_RADAR; 206262306a36Sopenharmony_ci ch_info.extra_info = 1; 206362306a36Sopenharmony_ci /* Overwrite duration for passive scans if necessary */ 206462306a36Sopenharmony_ci ch_info.timeout = ch_info.timeout > RTW_PASS_CHAN_TIME ? 206562306a36Sopenharmony_ci ch_info.timeout : RTW_PASS_CHAN_TIME; 206662306a36Sopenharmony_ci } else { 206762306a36Sopenharmony_ci ch_info.action_id = RTW_CHANNEL_ACTIVE; 206862306a36Sopenharmony_ci } 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci ret = rtw_add_chan_info(rtwdev, &ch_info, list, buf); 207162306a36Sopenharmony_ci if (ret) 207262306a36Sopenharmony_ci return ret; 207362306a36Sopenharmony_ci } 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci if (list->size > fifo->rsvd_pg_num << TX_PAGE_SIZE_SHIFT) { 207662306a36Sopenharmony_ci rtw_err(rtwdev, "List exceeds rsvd page total size\n"); 207762306a36Sopenharmony_ci return -EINVAL; 207862306a36Sopenharmony_ci } 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci list->addr = fifo->rsvd_h2c_info_addr + rtwdev->scan_info.probe_pg_size; 208162306a36Sopenharmony_ci ret = rtw_fw_write_data_rsvd_page(rtwdev, list->addr, buf, list->size); 208262306a36Sopenharmony_ci if (ret) 208362306a36Sopenharmony_ci rtw_err(rtwdev, "Download channel list failed\n"); 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci return ret; 208662306a36Sopenharmony_ci} 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_cistatic void rtw_fw_set_scan_offload(struct rtw_dev *rtwdev, 208962306a36Sopenharmony_ci struct rtw_ch_switch_option *opt, 209062306a36Sopenharmony_ci struct rtw_vif *rtwvif, 209162306a36Sopenharmony_ci struct rtw_chan_list *list) 209262306a36Sopenharmony_ci{ 209362306a36Sopenharmony_ci struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info; 209462306a36Sopenharmony_ci struct cfg80211_scan_request *req = rtwvif->scan_req; 209562306a36Sopenharmony_ci struct rtw_fifo_conf *fifo = &rtwdev->fifo; 209662306a36Sopenharmony_ci /* reserve one dummy page at the beginning for tx descriptor */ 209762306a36Sopenharmony_ci u8 pkt_loc = fifo->rsvd_h2c_info_addr - fifo->rsvd_boundary + 1; 209862306a36Sopenharmony_ci bool random_seq = req->flags & NL80211_SCAN_FLAG_RANDOM_SN; 209962306a36Sopenharmony_ci u8 h2c_pkt[H2C_PKT_SIZE] = {0}; 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci rtw_h2c_pkt_set_header(h2c_pkt, H2C_PKT_SCAN_OFFLOAD); 210262306a36Sopenharmony_ci SET_PKT_H2C_TOTAL_LEN(h2c_pkt, H2C_PKT_CH_SWITCH_LEN); 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci SCAN_OFFLOAD_SET_START(h2c_pkt, opt->switch_en); 210562306a36Sopenharmony_ci SCAN_OFFLOAD_SET_BACK_OP_EN(h2c_pkt, opt->back_op_en); 210662306a36Sopenharmony_ci SCAN_OFFLOAD_SET_RANDOM_SEQ_EN(h2c_pkt, random_seq); 210762306a36Sopenharmony_ci SCAN_OFFLOAD_SET_NO_CCK_EN(h2c_pkt, req->no_cck); 210862306a36Sopenharmony_ci SCAN_OFFLOAD_SET_CH_NUM(h2c_pkt, list->ch_num); 210962306a36Sopenharmony_ci SCAN_OFFLOAD_SET_CH_INFO_SIZE(h2c_pkt, list->size); 211062306a36Sopenharmony_ci SCAN_OFFLOAD_SET_CH_INFO_LOC(h2c_pkt, list->addr - fifo->rsvd_boundary); 211162306a36Sopenharmony_ci SCAN_OFFLOAD_SET_OP_CH(h2c_pkt, scan_info->op_chan); 211262306a36Sopenharmony_ci SCAN_OFFLOAD_SET_OP_PRI_CH_IDX(h2c_pkt, scan_info->op_pri_ch_idx); 211362306a36Sopenharmony_ci SCAN_OFFLOAD_SET_OP_BW(h2c_pkt, scan_info->op_bw); 211462306a36Sopenharmony_ci SCAN_OFFLOAD_SET_OP_PORT_ID(h2c_pkt, rtwvif->port); 211562306a36Sopenharmony_ci SCAN_OFFLOAD_SET_OP_DWELL_TIME(h2c_pkt, req->duration_mandatory ? 211662306a36Sopenharmony_ci req->duration : RTW_CHANNEL_TIME); 211762306a36Sopenharmony_ci SCAN_OFFLOAD_SET_OP_GAP_TIME(h2c_pkt, RTW_OFF_CHAN_TIME); 211862306a36Sopenharmony_ci SCAN_OFFLOAD_SET_SSID_NUM(h2c_pkt, req->n_ssids); 211962306a36Sopenharmony_ci SCAN_OFFLOAD_SET_PKT_LOC(h2c_pkt, pkt_loc); 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci rtw_fw_send_h2c_packet(rtwdev, h2c_pkt); 212262306a36Sopenharmony_ci} 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_civoid rtw_hw_scan_start(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, 212562306a36Sopenharmony_ci struct ieee80211_scan_request *scan_req) 212662306a36Sopenharmony_ci{ 212762306a36Sopenharmony_ci struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; 212862306a36Sopenharmony_ci struct cfg80211_scan_request *req = &scan_req->req; 212962306a36Sopenharmony_ci u8 mac_addr[ETH_ALEN]; 213062306a36Sopenharmony_ci 213162306a36Sopenharmony_ci rtwdev->scan_info.scanning_vif = vif; 213262306a36Sopenharmony_ci rtwvif->scan_ies = &scan_req->ies; 213362306a36Sopenharmony_ci rtwvif->scan_req = req; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci ieee80211_stop_queues(rtwdev->hw); 213662306a36Sopenharmony_ci rtw_leave_lps_deep(rtwdev); 213762306a36Sopenharmony_ci rtw_hci_flush_all_queues(rtwdev, false); 213862306a36Sopenharmony_ci rtw_mac_flush_all_queues(rtwdev, false); 213962306a36Sopenharmony_ci if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) 214062306a36Sopenharmony_ci get_random_mask_addr(mac_addr, req->mac_addr, 214162306a36Sopenharmony_ci req->mac_addr_mask); 214262306a36Sopenharmony_ci else 214362306a36Sopenharmony_ci ether_addr_copy(mac_addr, vif->addr); 214462306a36Sopenharmony_ci 214562306a36Sopenharmony_ci rtw_core_scan_start(rtwdev, rtwvif, mac_addr, true); 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci rtwdev->hal.rcr &= ~BIT_CBSSID_BCN; 214862306a36Sopenharmony_ci rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr); 214962306a36Sopenharmony_ci} 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_civoid rtw_hw_scan_complete(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, 215262306a36Sopenharmony_ci bool aborted) 215362306a36Sopenharmony_ci{ 215462306a36Sopenharmony_ci struct cfg80211_scan_info info = { 215562306a36Sopenharmony_ci .aborted = aborted, 215662306a36Sopenharmony_ci }; 215762306a36Sopenharmony_ci struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info; 215862306a36Sopenharmony_ci struct rtw_hal *hal = &rtwdev->hal; 215962306a36Sopenharmony_ci struct rtw_vif *rtwvif; 216062306a36Sopenharmony_ci u8 chan = scan_info->op_chan; 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci if (!vif) 216362306a36Sopenharmony_ci return; 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_ci rtwdev->hal.rcr |= BIT_CBSSID_BCN; 216662306a36Sopenharmony_ci rtw_write32(rtwdev, REG_RCR, rtwdev->hal.rcr); 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci rtw_core_scan_complete(rtwdev, vif, true); 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci rtwvif = (struct rtw_vif *)vif->drv_priv; 217162306a36Sopenharmony_ci if (chan) 217262306a36Sopenharmony_ci rtw_store_op_chan(rtwdev, false); 217362306a36Sopenharmony_ci rtw_phy_set_tx_power_level(rtwdev, hal->current_channel); 217462306a36Sopenharmony_ci ieee80211_wake_queues(rtwdev->hw); 217562306a36Sopenharmony_ci ieee80211_scan_completed(rtwdev->hw, &info); 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci rtwvif->scan_req = NULL; 217862306a36Sopenharmony_ci rtwvif->scan_ies = NULL; 217962306a36Sopenharmony_ci rtwdev->scan_info.scanning_vif = NULL; 218062306a36Sopenharmony_ci} 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_cistatic int rtw_hw_scan_prehandle(struct rtw_dev *rtwdev, struct rtw_vif *rtwvif, 218362306a36Sopenharmony_ci struct rtw_chan_list *list) 218462306a36Sopenharmony_ci{ 218562306a36Sopenharmony_ci struct cfg80211_scan_request *req = rtwvif->scan_req; 218662306a36Sopenharmony_ci int size = req->n_channels * (RTW_CH_INFO_SIZE + RTW_EX_CH_INFO_SIZE); 218762306a36Sopenharmony_ci u8 *buf; 218862306a36Sopenharmony_ci int ret; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci buf = kmalloc(size, GFP_KERNEL); 219162306a36Sopenharmony_ci if (!buf) 219262306a36Sopenharmony_ci return -ENOMEM; 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci ret = rtw_hw_scan_update_probe_req(rtwdev, rtwvif); 219562306a36Sopenharmony_ci if (ret) { 219662306a36Sopenharmony_ci rtw_err(rtwdev, "Update probe request failed\n"); 219762306a36Sopenharmony_ci goto out; 219862306a36Sopenharmony_ci } 219962306a36Sopenharmony_ci 220062306a36Sopenharmony_ci list->buf_size = size; 220162306a36Sopenharmony_ci list->size = 0; 220262306a36Sopenharmony_ci list->ch_num = 0; 220362306a36Sopenharmony_ci ret = rtw_add_chan_list(rtwdev, rtwvif, list, buf); 220462306a36Sopenharmony_ciout: 220562306a36Sopenharmony_ci kfree(buf); 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci return ret; 220862306a36Sopenharmony_ci} 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ciint rtw_hw_scan_offload(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, 221162306a36Sopenharmony_ci bool enable) 221262306a36Sopenharmony_ci{ 221362306a36Sopenharmony_ci struct rtw_vif *rtwvif = vif ? (struct rtw_vif *)vif->drv_priv : NULL; 221462306a36Sopenharmony_ci struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info; 221562306a36Sopenharmony_ci struct rtw_ch_switch_option cs_option = {0}; 221662306a36Sopenharmony_ci struct rtw_chan_list chan_list = {0}; 221762306a36Sopenharmony_ci int ret = 0; 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci if (!rtwvif) 222062306a36Sopenharmony_ci return -EINVAL; 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci cs_option.switch_en = enable; 222362306a36Sopenharmony_ci cs_option.back_op_en = scan_info->op_chan != 0; 222462306a36Sopenharmony_ci if (enable) { 222562306a36Sopenharmony_ci ret = rtw_hw_scan_prehandle(rtwdev, rtwvif, &chan_list); 222662306a36Sopenharmony_ci if (ret) 222762306a36Sopenharmony_ci goto out; 222862306a36Sopenharmony_ci } 222962306a36Sopenharmony_ci rtw_fw_set_scan_offload(rtwdev, &cs_option, rtwvif, &chan_list); 223062306a36Sopenharmony_ciout: 223162306a36Sopenharmony_ci if (rtwdev->ap_active) { 223262306a36Sopenharmony_ci ret = rtw_download_beacon(rtwdev); 223362306a36Sopenharmony_ci if (ret) 223462306a36Sopenharmony_ci rtw_err(rtwdev, "HW scan download beacon failed\n"); 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci return ret; 223862306a36Sopenharmony_ci} 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_civoid rtw_hw_scan_abort(struct rtw_dev *rtwdev) 224162306a36Sopenharmony_ci{ 224262306a36Sopenharmony_ci struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif; 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_SCAN_OFFLOAD)) 224562306a36Sopenharmony_ci return; 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci rtw_hw_scan_offload(rtwdev, vif, false); 224862306a36Sopenharmony_ci rtw_hw_scan_complete(rtwdev, vif, true); 224962306a36Sopenharmony_ci} 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_civoid rtw_hw_scan_status_report(struct rtw_dev *rtwdev, struct sk_buff *skb) 225262306a36Sopenharmony_ci{ 225362306a36Sopenharmony_ci struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif; 225462306a36Sopenharmony_ci struct rtw_c2h_cmd *c2h; 225562306a36Sopenharmony_ci bool aborted; 225662306a36Sopenharmony_ci u8 rc; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) 225962306a36Sopenharmony_ci return; 226062306a36Sopenharmony_ci 226162306a36Sopenharmony_ci c2h = get_c2h_from_skb(skb); 226262306a36Sopenharmony_ci rc = GET_SCAN_REPORT_RETURN_CODE(c2h->payload); 226362306a36Sopenharmony_ci aborted = rc != RTW_SCAN_REPORT_SUCCESS; 226462306a36Sopenharmony_ci rtw_hw_scan_complete(rtwdev, vif, aborted); 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci if (aborted) 226762306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_HW_SCAN, "HW scan aborted with code: %d\n", rc); 226862306a36Sopenharmony_ci} 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_civoid rtw_store_op_chan(struct rtw_dev *rtwdev, bool backup) 227162306a36Sopenharmony_ci{ 227262306a36Sopenharmony_ci struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info; 227362306a36Sopenharmony_ci struct rtw_hal *hal = &rtwdev->hal; 227462306a36Sopenharmony_ci u8 band; 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_ci if (backup) { 227762306a36Sopenharmony_ci scan_info->op_chan = hal->current_channel; 227862306a36Sopenharmony_ci scan_info->op_bw = hal->current_band_width; 227962306a36Sopenharmony_ci scan_info->op_pri_ch_idx = hal->current_primary_channel_index; 228062306a36Sopenharmony_ci scan_info->op_pri_ch = hal->primary_channel; 228162306a36Sopenharmony_ci } else { 228262306a36Sopenharmony_ci band = scan_info->op_chan > 14 ? RTW_BAND_5G : RTW_BAND_2G; 228362306a36Sopenharmony_ci rtw_update_channel(rtwdev, scan_info->op_chan, 228462306a36Sopenharmony_ci scan_info->op_pri_ch, 228562306a36Sopenharmony_ci band, scan_info->op_bw); 228662306a36Sopenharmony_ci } 228762306a36Sopenharmony_ci} 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_civoid rtw_clear_op_chan(struct rtw_dev *rtwdev) 229062306a36Sopenharmony_ci{ 229162306a36Sopenharmony_ci struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info; 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci scan_info->op_chan = 0; 229462306a36Sopenharmony_ci scan_info->op_bw = 0; 229562306a36Sopenharmony_ci scan_info->op_pri_ch_idx = 0; 229662306a36Sopenharmony_ci scan_info->op_pri_ch = 0; 229762306a36Sopenharmony_ci} 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_cistatic bool rtw_is_op_chan(struct rtw_dev *rtwdev, u8 channel) 230062306a36Sopenharmony_ci{ 230162306a36Sopenharmony_ci struct rtw_hw_scan_info *scan_info = &rtwdev->scan_info; 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci return channel == scan_info->op_chan; 230462306a36Sopenharmony_ci} 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_civoid rtw_hw_scan_chan_switch(struct rtw_dev *rtwdev, struct sk_buff *skb) 230762306a36Sopenharmony_ci{ 230862306a36Sopenharmony_ci struct rtw_hal *hal = &rtwdev->hal; 230962306a36Sopenharmony_ci struct rtw_c2h_cmd *c2h; 231062306a36Sopenharmony_ci enum rtw_scan_notify_id id; 231162306a36Sopenharmony_ci u8 chan, band, status; 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci if (!test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) 231462306a36Sopenharmony_ci return; 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci c2h = get_c2h_from_skb(skb); 231762306a36Sopenharmony_ci chan = GET_CHAN_SWITCH_CENTRAL_CH(c2h->payload); 231862306a36Sopenharmony_ci id = GET_CHAN_SWITCH_ID(c2h->payload); 231962306a36Sopenharmony_ci status = GET_CHAN_SWITCH_STATUS(c2h->payload); 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci if (id == RTW_SCAN_NOTIFY_ID_POSTSWITCH) { 232262306a36Sopenharmony_ci band = chan > 14 ? RTW_BAND_5G : RTW_BAND_2G; 232362306a36Sopenharmony_ci rtw_update_channel(rtwdev, chan, chan, band, 232462306a36Sopenharmony_ci RTW_CHANNEL_WIDTH_20); 232562306a36Sopenharmony_ci if (rtw_is_op_chan(rtwdev, chan)) { 232662306a36Sopenharmony_ci rtw_store_op_chan(rtwdev, false); 232762306a36Sopenharmony_ci ieee80211_wake_queues(rtwdev->hw); 232862306a36Sopenharmony_ci rtw_core_enable_beacon(rtwdev, true); 232962306a36Sopenharmony_ci } 233062306a36Sopenharmony_ci } else if (id == RTW_SCAN_NOTIFY_ID_PRESWITCH) { 233162306a36Sopenharmony_ci if (IS_CH_5G_BAND(chan)) { 233262306a36Sopenharmony_ci rtw_coex_switchband_notify(rtwdev, COEX_SWITCH_TO_5G); 233362306a36Sopenharmony_ci } else if (IS_CH_2G_BAND(chan)) { 233462306a36Sopenharmony_ci u8 chan_type; 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) 233762306a36Sopenharmony_ci chan_type = COEX_SWITCH_TO_24G; 233862306a36Sopenharmony_ci else 233962306a36Sopenharmony_ci chan_type = COEX_SWITCH_TO_24G_NOFORSCAN; 234062306a36Sopenharmony_ci rtw_coex_switchband_notify(rtwdev, chan_type); 234162306a36Sopenharmony_ci } 234262306a36Sopenharmony_ci /* The channel of C2H RTW_SCAN_NOTIFY_ID_PRESWITCH is next 234362306a36Sopenharmony_ci * channel that hardware will switch. We need to stop queue 234462306a36Sopenharmony_ci * if next channel is non-op channel. 234562306a36Sopenharmony_ci */ 234662306a36Sopenharmony_ci if (!rtw_is_op_chan(rtwdev, chan) && 234762306a36Sopenharmony_ci rtw_is_op_chan(rtwdev, hal->current_channel)) { 234862306a36Sopenharmony_ci rtw_core_enable_beacon(rtwdev, false); 234962306a36Sopenharmony_ci ieee80211_stop_queues(rtwdev->hw); 235062306a36Sopenharmony_ci } 235162306a36Sopenharmony_ci } 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci rtw_dbg(rtwdev, RTW_DBG_HW_SCAN, 235462306a36Sopenharmony_ci "Chan switch: %x, id: %x, status: %x\n", chan, id, status); 235562306a36Sopenharmony_ci} 2356