diff options
Diffstat (limited to 'drivers/net/wireless/realtek/rtw89/fw.c')
-rw-r--r-- | drivers/net/wireless/realtek/rtw89/fw.c | 790 |
1 files changed, 758 insertions, 32 deletions
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index d57e3610fb88..de1f23779fc6 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -11,6 +11,9 @@ #include "phy.h" #include "reg.h" +static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev, + struct sk_buff *skb); + static struct sk_buff *rtw89_fw_h2c_alloc_skb(struct rtw89_dev *rtwdev, u32 len, bool header) { @@ -85,15 +88,31 @@ static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev, const u8 *fw, u32 len, { struct rtw89_fw_hdr_section_info *section_info; const u8 *fw_end = fw + len; + const u8 *fwdynhdr; const u8 *bin; + u32 base_hdr_len; u32 i; if (!info) return -EINVAL; info->section_num = GET_FW_HDR_SEC_NUM(fw); - info->hdr_len = RTW89_FW_HDR_SIZE + - info->section_num * RTW89_FW_SECTION_HDR_SIZE; + base_hdr_len = RTW89_FW_HDR_SIZE + + info->section_num * RTW89_FW_SECTION_HDR_SIZE; + info->dynamic_hdr_en = GET_FW_HDR_DYN_HDR(fw); + + if (info->dynamic_hdr_en) { + info->hdr_len = GET_FW_HDR_LEN(fw); + info->dynamic_hdr_len = info->hdr_len - base_hdr_len; + fwdynhdr = fw + base_hdr_len; + if (GET_FW_DYNHDR_LEN(fwdynhdr) != info->dynamic_hdr_len) { + rtw89_err(rtwdev, "[ERR]invalid fw dynamic header len\n"); + return -EINVAL; + } + } else { + info->hdr_len = base_hdr_len; + info->dynamic_hdr_len = 0; + } bin = fw + info->hdr_len; @@ -254,29 +273,42 @@ static void rtw89_fw_recognize_features(struct rtw89_dev *rtwdev) } } -void rtw89_early_fw_feature_recognize(struct device *device, - const struct rtw89_chip_info *chip, - u32 *early_feat_map) +const struct firmware * +rtw89_early_fw_feature_recognize(struct device *device, + const struct rtw89_chip_info *chip, + u32 *early_feat_map) { - union { - struct rtw89_mfw_hdr mfw_hdr; - u8 fw_hdr[RTW89_FW_HDR_SIZE]; - } buf = {}; + union rtw89_compat_fw_hdr buf = {}; const struct firmware *firmware; + bool full_req = false; u32 ver_code; int ret; int i; - ret = request_partial_firmware_into_buf(&firmware, chip->fw_name, - device, &buf, sizeof(buf), 0); + /* If SECURITY_LOADPIN_ENFORCE is enabled, reading partial files will + * be denied (-EPERM). Then, we don't get right firmware things as + * expected. So, in this case, we have to request full firmware here. + */ + if (IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENFORCE)) + full_req = true; + + if (full_req) + ret = request_firmware(&firmware, chip->fw_name, device); + else + ret = request_partial_firmware_into_buf(&firmware, chip->fw_name, + device, &buf, sizeof(buf), + 0); + if (ret) { dev_err(device, "failed to early request firmware: %d\n", ret); - return; + return NULL; } - ver_code = buf.mfw_hdr.sig != RTW89_MFW_SIG ? - RTW89_FW_HDR_VER_CODE(&buf.fw_hdr) : - RTW89_MFW_HDR_VER_CODE(&buf.mfw_hdr); + if (full_req) + ver_code = rtw89_compat_fw_hdr_ver_code(firmware->data); + else + ver_code = rtw89_compat_fw_hdr_ver_code(&buf); + if (!ver_code) goto out; @@ -291,7 +323,11 @@ void rtw89_early_fw_feature_recognize(struct device *device, } out: + if (full_req) + return firmware; + release_firmware(firmware); + return NULL; } int rtw89_fw_recognize(struct rtw89_dev *rtwdev) @@ -515,6 +551,11 @@ int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type) u8 val; int ret; + rtw89_mac_disable_cpu(rtwdev); + ret = rtw89_mac_enable_cpu(rtwdev, 0, true); + if (ret) + return ret; + if (!fw || !len) { rtw89_err(rtwdev, "fw type %d isn't recognized\n", type); return -ENOENT; @@ -534,7 +575,7 @@ int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type) goto fwdl_err; } - ret = rtw89_fw_download_hdr(rtwdev, fw, info.hdr_len); + ret = rtw89_fw_download_hdr(rtwdev, fw, info.hdr_len - info.dynamic_hdr_len); if (ret) { ret = -EBUSY; goto fwdl_err; @@ -593,6 +634,13 @@ int rtw89_load_firmware(struct rtw89_dev *rtwdev) fw->rtwdev = rtwdev; init_completion(&fw->completion); + if (fw->firmware) { + rtw89_debug(rtwdev, RTW89_DBG_FW, + "full firmware has been early requested\n"); + complete_all(&fw->completion); + return 0; + } + ret = request_firmware_nowait(THIS_MODULE, true, fw_name, rtwdev->dev, GFP_KERNEL, fw, rtw89_load_firmware_cb); if (ret) { @@ -609,8 +657,14 @@ void rtw89_unload_firmware(struct rtw89_dev *rtwdev) rtw89_wait_firmware_completion(rtwdev); - if (fw->firmware) + if (fw->firmware) { release_firmware(fw->firmware); + + /* assign NULL back in case rtw89_free_ieee80211_hw() + * try to release the same one again. + */ + fw->firmware = NULL; + } } #define H2C_CAM_LEN 60 @@ -848,6 +902,56 @@ fail: return ret; } +static int rtw89_fw_h2c_add_wow_fw_ofld(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + enum rtw89_fw_pkt_ofld_type type, + u8 *id) +{ + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct rtw89_pktofld_info *info; + struct sk_buff *skb; + int ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + switch (type) { + case RTW89_PKT_OFLD_TYPE_PS_POLL: + skb = ieee80211_pspoll_get(rtwdev->hw, vif); + break; + case RTW89_PKT_OFLD_TYPE_PROBE_RSP: + skb = ieee80211_proberesp_get(rtwdev->hw, vif); + break; + case RTW89_PKT_OFLD_TYPE_NULL_DATA: + skb = ieee80211_nullfunc_get(rtwdev->hw, vif, -1, false); + break; + case RTW89_PKT_OFLD_TYPE_QOS_NULL: + skb = ieee80211_nullfunc_get(rtwdev->hw, vif, -1, true); + break; + default: + goto err; + } + + if (!skb) + goto err; + + list_add_tail(&info->list, &rtw_wow->pkt_list); + ret = rtw89_fw_h2c_add_pkt_offload(rtwdev, &info->id, skb); + kfree_skb(skb); + + if (ret) + return ret; + + *id = info->id; + return 0; + +err: + kfree(info); + return -ENOMEM; +} + #define H2C_GENERAL_PKT_LEN 6 #define H2C_GENERAL_PKT_ID_UND 0xff int rtw89_fw_h2c_general_pkt(struct rtw89_dev *rtwdev, u8 macid) @@ -2192,7 +2296,7 @@ fail: int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev) { const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0); - struct rtw89_mcc_info *mcc_info = &rtwdev->mcc; + struct rtw89_rfk_mcc_info *rfk_mcc = &rtwdev->rfk_mcc; struct rtw89_fw_h2c_rf_get_mccch *mccch; struct sk_buff *skb; int ret; @@ -2205,10 +2309,10 @@ int rtw89_fw_h2c_rf_ntfy_mcc(struct rtw89_dev *rtwdev) skb_put(skb, sizeof(*mccch)); mccch = (struct rtw89_fw_h2c_rf_get_mccch *)skb->data; - mccch->ch_0 = cpu_to_le32(mcc_info->ch[0]); - mccch->ch_1 = cpu_to_le32(mcc_info->ch[1]); - mccch->band_0 = cpu_to_le32(mcc_info->band[0]); - mccch->band_1 = cpu_to_le32(mcc_info->band[1]); + mccch->ch_0 = cpu_to_le32(rfk_mcc->ch[0]); + mccch->ch_1 = cpu_to_le32(rfk_mcc->ch[1]); + mccch->band_0 = cpu_to_le32(rfk_mcc->band[0]); + mccch->band_1 = cpu_to_le32(rfk_mcc->band[1]); mccch->current_channel = cpu_to_le32(chan->channel); mccch->current_band_type = cpu_to_le32(chan->band_type); @@ -2311,8 +2415,43 @@ void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) mutex_unlock(&rtwdev->mutex); } +static void rtw89_fw_c2h_parse_attr(struct sk_buff *c2h) +{ + struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(c2h); + + attr->category = RTW89_GET_C2H_CATEGORY(c2h->data); + attr->class = RTW89_GET_C2H_CLASS(c2h->data); + attr->func = RTW89_GET_C2H_FUNC(c2h->data); + attr->len = RTW89_GET_C2H_LEN(c2h->data); +} + +static bool rtw89_fw_c2h_chk_atomic(struct rtw89_dev *rtwdev, + struct sk_buff *c2h) +{ + struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(c2h); + u8 category = attr->category; + u8 class = attr->class; + u8 func = attr->func; + + switch (category) { + default: + return false; + case RTW89_C2H_CAT_MAC: + return rtw89_mac_c2h_chk_atomic(rtwdev, class, func); + } +} + void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h) { + rtw89_fw_c2h_parse_attr(c2h); + if (!rtw89_fw_c2h_chk_atomic(rtwdev, c2h)) + goto enqueue; + + rtw89_fw_c2h_cmd_handle(rtwdev, c2h); + dev_kfree_skb_any(c2h); + return; + +enqueue: skb_queue_tail(&rtwdev->c2h_queue, c2h); ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work); } @@ -2320,10 +2459,11 @@ void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h) static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb) { - u8 category = RTW89_GET_C2H_CATEGORY(skb->data); - u8 class = RTW89_GET_C2H_CLASS(skb->data); - u8 func = RTW89_GET_C2H_FUNC(skb->data); - u16 len = RTW89_GET_C2H_LEN(skb->data); + struct rtw89_fw_c2h_attr *attr = RTW89_SKB_C2H_CB(skb); + u8 category = attr->category; + u8 class = attr->class; + u8 func = attr->func; + u16 len = attr->len; bool dump = true; if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) @@ -2565,6 +2705,9 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, struct rtw89_mac_chinfo *ch_info) { struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct ieee80211_vif *vif = rtwdev->scan_info.scanning_vif; + struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv; + struct cfg80211_scan_request *req = rtwvif->scan_req; struct rtw89_pktofld_info *info; u8 band, probe_count = 0; @@ -2576,13 +2719,13 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, ch_info->tx_pwr_idx = 0; ch_info->tx_null = false; ch_info->pause_data = false; + ch_info->probe_id = RTW89_SCANOFLD_PKT_NONE; if (ssid_num) { ch_info->num_pkt = ssid_num; band = rtw89_hw_to_nl80211_band(ch_info->ch_band); list_for_each_entry(info, &scan_info->pkt_list[band], list) { - ch_info->probe_id = info->id; ch_info->pkt_id[probe_count] = info->id; if (++probe_count >= ssid_num) break; @@ -2591,9 +2734,16 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, rtw89_err(rtwdev, "SSID num differs from list len\n"); } + if (ch_info->ch_band == RTW89_BAND_6G) { + if (ssid_num == 1 && req->ssids[0].ssid_len == 0) { + ch_info->tx_pkt = false; + if (!req->duration_mandatory) + ch_info->period -= RTW89_DWELL_TIME; + } + } + switch (chan_type) { case RTW89_CHAN_OPERATE: - ch_info->probe_id = RTW89_SCANOFLD_PKT_NONE; ch_info->central_ch = scan_info->op_chan; ch_info->pri_ch = scan_info->op_pri_ch; ch_info->ch_band = scan_info->op_band; @@ -2602,8 +2752,9 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, ch_info->num_pkt = 0; break; case RTW89_CHAN_DFS: - ch_info->period = max_t(u8, ch_info->period, - RTW89_DFS_CHAN_TIME); + if (ch_info->ch_band != RTW89_BAND_6G) + ch_info->period = max_t(u8, ch_info->period, + RTW89_DFS_CHAN_TIME); ch_info->dwell_time = RTW89_DWELL_TIME; break; case RTW89_CHAN_ACTIVE: @@ -2637,8 +2788,13 @@ static int rtw89_hw_scan_add_chan_list(struct rtw89_dev *rtwdev, goto out; } - ch_info->period = req->duration_mandatory ? - req->duration : RTW89_CHANNEL_TIME; + if (req->duration_mandatory) + ch_info->period = req->duration; + else if (channel->band == NL80211_BAND_6GHZ) + ch_info->period = RTW89_CHANNEL_TIME_6G + RTW89_DWELL_TIME; + else + ch_info->period = RTW89_CHANNEL_TIME; + ch_info->ch_band = rtw89_nl80211_to_hw_band(channel->band); ch_info->central_ch = channel->hw_value; ch_info->pri_ch = channel->hw_value; @@ -2757,6 +2913,7 @@ void rtw89_hw_scan_complete(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, if (rtwvif->net_type != RTW89_NET_TYPE_NO_LINK) rtw89_store_op_chan(rtwdev, false); + rtw89_set_channel(rtwdev); } void rtw89_hw_scan_abort(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif) @@ -2862,6 +3019,7 @@ int rtw89_fw_h2c_pkt_drop(struct rtw89_dev *rtwdev, case RTW89_PKT_DROP_SEL_MACID_BK_ONCE: case RTW89_PKT_DROP_SEL_MACID_VI_ONCE: case RTW89_PKT_DROP_SEL_MACID_VO_ONCE: + case RTW89_PKT_DROP_SEL_BAND_ONCE: break; default: rtw89_debug(rtwdev, RTW89_DBG_FW, @@ -2877,6 +3035,14 @@ int rtw89_fw_h2c_pkt_drop(struct rtw89_dev *rtwdev, RTW89_SET_FWCMD_PKT_DROP_PORT(skb->data, params->port); RTW89_SET_FWCMD_PKT_DROP_MBSSID(skb->data, params->mbssid); RTW89_SET_FWCMD_PKT_DROP_ROLE_A_INFO_TF_TRS(skb->data, params->tf_trs); + RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_0(skb->data, + params->macid_band_sel[0]); + RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_1(skb->data, + params->macid_band_sel[1]); + RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_2(skb->data, + params->macid_band_sel[2]); + RTW89_SET_FWCMD_PKT_DROP_MACID_BAND_SEL_3(skb->data, + params->macid_band_sel[3]); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, @@ -2896,3 +3062,563 @@ fail: dev_kfree_skb_any(skb); return ret; } + +#define H2C_KEEP_ALIVE_LEN 4 +int rtw89_fw_h2c_keep_alive(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, + bool enable) +{ + struct sk_buff *skb; + u8 pkt_id = 0; + int ret; + + if (enable) { + ret = rtw89_fw_h2c_add_wow_fw_ofld(rtwdev, rtwvif, + RTW89_PKT_OFLD_TYPE_NULL_DATA, &pkt_id); + if (ret) + return -EPERM; + } + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_KEEP_ALIVE_LEN); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for keep alive\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_KEEP_ALIVE_LEN); + + RTW89_SET_KEEP_ALIVE_ENABLE(skb->data, enable); + RTW89_SET_KEEP_ALIVE_PKT_NULL_ID(skb->data, pkt_id); + RTW89_SET_KEEP_ALIVE_PERIOD(skb->data, 5); + RTW89_SET_KEEP_ALIVE_MACID(skb->data, rtwvif->mac_id); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_WOW, + H2C_FUNC_KEEP_ALIVE, 0, 1, + H2C_KEEP_ALIVE_LEN); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; + +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +#define H2C_DISCONNECT_DETECT_LEN 8 +int rtw89_fw_h2c_disconnect_detect(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, bool enable) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct sk_buff *skb; + u8 macid = rtwvif->mac_id; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_DISCONNECT_DETECT_LEN); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for keep alive\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_DISCONNECT_DETECT_LEN); + + if (test_bit(RTW89_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) { + RTW89_SET_DISCONNECT_DETECT_ENABLE(skb->data, enable); + RTW89_SET_DISCONNECT_DETECT_DISCONNECT(skb->data, !enable); + RTW89_SET_DISCONNECT_DETECT_MAC_ID(skb->data, macid); + RTW89_SET_DISCONNECT_DETECT_CHECK_PERIOD(skb->data, 100); + RTW89_SET_DISCONNECT_DETECT_TRY_PKT_COUNT(skb->data, 5); + } + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_WOW, + H2C_FUNC_DISCONNECT_DETECT, 0, 1, + H2C_DISCONNECT_DETECT_LEN); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; + +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +#define H2C_WOW_GLOBAL_LEN 8 +int rtw89_fw_h2c_wow_global(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, + bool enable) +{ + struct sk_buff *skb; + u8 macid = rtwvif->mac_id; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_WOW_GLOBAL_LEN); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for keep alive\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_WOW_GLOBAL_LEN); + + RTW89_SET_WOW_GLOBAL_ENABLE(skb->data, enable); + RTW89_SET_WOW_GLOBAL_MAC_ID(skb->data, macid); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_WOW, + H2C_FUNC_WOW_GLOBAL, 0, 1, + H2C_WOW_GLOBAL_LEN); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; + +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +#define H2C_WAKEUP_CTRL_LEN 4 +int rtw89_fw_h2c_wow_wakeup_ctrl(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif, + bool enable) +{ + struct rtw89_wow_param *rtw_wow = &rtwdev->wow; + struct sk_buff *skb; + u8 macid = rtwvif->mac_id; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_WAKEUP_CTRL_LEN); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for keep alive\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_WAKEUP_CTRL_LEN); + + if (rtw_wow->pattern_cnt) + RTW89_SET_WOW_WAKEUP_CTRL_PATTERN_MATCH_ENABLE(skb->data, enable); + if (test_bit(RTW89_WOW_FLAG_EN_MAGIC_PKT, rtw_wow->flags)) + RTW89_SET_WOW_WAKEUP_CTRL_MAGIC_ENABLE(skb->data, enable); + if (test_bit(RTW89_WOW_FLAG_EN_DISCONNECT, rtw_wow->flags)) + RTW89_SET_WOW_WAKEUP_CTRL_DEAUTH_ENABLE(skb->data, enable); + + RTW89_SET_WOW_WAKEUP_CTRL_MAC_ID(skb->data, macid); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_WOW, + H2C_FUNC_WAKEUP_CTRL, 0, 1, + H2C_WAKEUP_CTRL_LEN); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; + +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +#define H2C_WOW_CAM_UPD_LEN 24 +int rtw89_fw_wow_cam_update(struct rtw89_dev *rtwdev, + struct rtw89_wow_cam_info *cam_info) +{ + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_WOW_CAM_UPD_LEN); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for keep alive\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_WOW_CAM_UPD_LEN); + + RTW89_SET_WOW_CAM_UPD_R_W(skb->data, cam_info->r_w); + RTW89_SET_WOW_CAM_UPD_IDX(skb->data, cam_info->idx); + if (cam_info->valid) { + RTW89_SET_WOW_CAM_UPD_WKFM1(skb->data, cam_info->mask[0]); + RTW89_SET_WOW_CAM_UPD_WKFM2(skb->data, cam_info->mask[1]); + RTW89_SET_WOW_CAM_UPD_WKFM3(skb->data, cam_info->mask[2]); + RTW89_SET_WOW_CAM_UPD_WKFM4(skb->data, cam_info->mask[3]); + RTW89_SET_WOW_CAM_UPD_CRC(skb->data, cam_info->crc); + RTW89_SET_WOW_CAM_UPD_NEGATIVE_PATTERN_MATCH(skb->data, + cam_info->negative_pattern_match); + RTW89_SET_WOW_CAM_UPD_SKIP_MAC_HDR(skb->data, + cam_info->skip_mac_hdr); + RTW89_SET_WOW_CAM_UPD_UC(skb->data, cam_info->uc); + RTW89_SET_WOW_CAM_UPD_MC(skb->data, cam_info->mc); + RTW89_SET_WOW_CAM_UPD_BC(skb->data, cam_info->bc); + } + RTW89_SET_WOW_CAM_UPD_VALID(skb->data, cam_info->valid); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MAC_WOW, + H2C_FUNC_WOW_CAM_UPD, 0, 1, + H2C_WOW_CAM_UPD_LEN); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} + +static int rtw89_h2c_tx_and_wait(struct rtw89_dev *rtwdev, struct sk_buff *skb, + struct rtw89_wait_info *wait, unsigned int cond) +{ + int ret; + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + dev_kfree_skb_any(skb); + return -EBUSY; + } + + return rtw89_wait_for_cond(wait, cond); +} + +#define H2C_ADD_MCC_LEN 16 +int rtw89_fw_h2c_add_mcc(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_add_req *p) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_ADD_MCC_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for add mcc\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_ADD_MCC_LEN); + RTW89_SET_FWCMD_ADD_MCC_MACID(skb->data, p->macid); + RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG0(skb->data, p->central_ch_seg0); + RTW89_SET_FWCMD_ADD_MCC_CENTRAL_CH_SEG1(skb->data, p->central_ch_seg1); + RTW89_SET_FWCMD_ADD_MCC_PRIMARY_CH(skb->data, p->primary_ch); + RTW89_SET_FWCMD_ADD_MCC_BANDWIDTH(skb->data, p->bandwidth); + RTW89_SET_FWCMD_ADD_MCC_GROUP(skb->data, p->group); + RTW89_SET_FWCMD_ADD_MCC_C2H_RPT(skb->data, p->c2h_rpt); + RTW89_SET_FWCMD_ADD_MCC_DIS_TX_NULL(skb->data, p->dis_tx_null); + RTW89_SET_FWCMD_ADD_MCC_DIS_SW_RETRY(skb->data, p->dis_sw_retry); + RTW89_SET_FWCMD_ADD_MCC_IN_CURR_CH(skb->data, p->in_curr_ch); + RTW89_SET_FWCMD_ADD_MCC_SW_RETRY_COUNT(skb->data, p->sw_retry_count); + RTW89_SET_FWCMD_ADD_MCC_TX_NULL_EARLY(skb->data, p->tx_null_early); + RTW89_SET_FWCMD_ADD_MCC_BTC_IN_2G(skb->data, p->btc_in_2g); + RTW89_SET_FWCMD_ADD_MCC_PTA_EN(skb->data, p->pta_en); + RTW89_SET_FWCMD_ADD_MCC_RFK_BY_PASS(skb->data, p->rfk_by_pass); + RTW89_SET_FWCMD_ADD_MCC_CH_BAND_TYPE(skb->data, p->ch_band_type); + RTW89_SET_FWCMD_ADD_MCC_DURATION(skb->data, p->duration); + RTW89_SET_FWCMD_ADD_MCC_COURTESY_EN(skb->data, p->courtesy_en); + RTW89_SET_FWCMD_ADD_MCC_COURTESY_NUM(skb->data, p->courtesy_num); + RTW89_SET_FWCMD_ADD_MCC_COURTESY_TARGET(skb->data, p->courtesy_target); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_ADD_MCC, 0, 0, + H2C_ADD_MCC_LEN); + + cond = RTW89_MCC_WAIT_COND(p->group, H2C_FUNC_ADD_MCC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_START_MCC_LEN 12 +int rtw89_fw_h2c_start_mcc(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_start_req *p) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_START_MCC_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for start mcc\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_START_MCC_LEN); + RTW89_SET_FWCMD_START_MCC_GROUP(skb->data, p->group); + RTW89_SET_FWCMD_START_MCC_BTC_IN_GROUP(skb->data, p->btc_in_group); + RTW89_SET_FWCMD_START_MCC_OLD_GROUP_ACTION(skb->data, p->old_group_action); + RTW89_SET_FWCMD_START_MCC_OLD_GROUP(skb->data, p->old_group); + RTW89_SET_FWCMD_START_MCC_NOTIFY_CNT(skb->data, p->notify_cnt); + RTW89_SET_FWCMD_START_MCC_NOTIFY_RXDBG_EN(skb->data, p->notify_rxdbg_en); + RTW89_SET_FWCMD_START_MCC_MACID(skb->data, p->macid); + RTW89_SET_FWCMD_START_MCC_TSF_LOW(skb->data, p->tsf_low); + RTW89_SET_FWCMD_START_MCC_TSF_HIGH(skb->data, p->tsf_high); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_START_MCC, 0, 0, + H2C_START_MCC_LEN); + + cond = RTW89_MCC_WAIT_COND(p->group, H2C_FUNC_START_MCC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_STOP_MCC_LEN 4 +int rtw89_fw_h2c_stop_mcc(struct rtw89_dev *rtwdev, u8 group, u8 macid, + bool prev_groups) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_STOP_MCC_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for stop mcc\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_STOP_MCC_LEN); + RTW89_SET_FWCMD_STOP_MCC_MACID(skb->data, macid); + RTW89_SET_FWCMD_STOP_MCC_GROUP(skb->data, group); + RTW89_SET_FWCMD_STOP_MCC_PREV_GROUPS(skb->data, prev_groups); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_STOP_MCC, 0, 0, + H2C_STOP_MCC_LEN); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_STOP_MCC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_DEL_MCC_GROUP_LEN 4 +int rtw89_fw_h2c_del_mcc_group(struct rtw89_dev *rtwdev, u8 group, + bool prev_groups) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_DEL_MCC_GROUP_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for del mcc group\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_DEL_MCC_GROUP_LEN); + RTW89_SET_FWCMD_DEL_MCC_GROUP_GROUP(skb->data, group); + RTW89_SET_FWCMD_DEL_MCC_GROUP_PREV_GROUPS(skb->data, prev_groups); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_DEL_MCC_GROUP, 0, 0, + H2C_DEL_MCC_GROUP_LEN); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_DEL_MCC_GROUP); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_RESET_MCC_GROUP_LEN 4 +int rtw89_fw_h2c_reset_mcc_group(struct rtw89_dev *rtwdev, u8 group) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_RESET_MCC_GROUP_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for reset mcc group\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_RESET_MCC_GROUP_LEN); + RTW89_SET_FWCMD_RESET_MCC_GROUP_GROUP(skb->data, group); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_RESET_MCC_GROUP, 0, 0, + H2C_RESET_MCC_GROUP_LEN); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_RESET_MCC_GROUP); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_MCC_REQ_TSF_LEN 4 +int rtw89_fw_h2c_mcc_req_tsf(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_tsf_req *req, + struct rtw89_mac_mcc_tsf_rpt *rpt) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct rtw89_mac_mcc_tsf_rpt *tmp; + struct sk_buff *skb; + unsigned int cond; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_MCC_REQ_TSF_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for mcc req tsf\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_MCC_REQ_TSF_LEN); + RTW89_SET_FWCMD_MCC_REQ_TSF_GROUP(skb->data, req->group); + RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_X(skb->data, req->macid_x); + RTW89_SET_FWCMD_MCC_REQ_TSF_MACID_Y(skb->data, req->macid_y); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_MCC_REQ_TSF, 0, 0, + H2C_MCC_REQ_TSF_LEN); + + cond = RTW89_MCC_WAIT_COND(req->group, H2C_FUNC_MCC_REQ_TSF); + ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); + if (ret) + return ret; + + tmp = (struct rtw89_mac_mcc_tsf_rpt *)wait->data.buf; + *rpt = *tmp; + + return 0; +} + +#define H2C_MCC_MACID_BITMAP_DSC_LEN 4 +int rtw89_fw_h2c_mcc_macid_bitamp(struct rtw89_dev *rtwdev, u8 group, u8 macid, + u8 *bitmap) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + u8 map_len; + u8 h2c_len; + + BUILD_BUG_ON(RTW89_MAX_MAC_ID_NUM % 8); + map_len = RTW89_MAX_MAC_ID_NUM / 8; + h2c_len = H2C_MCC_MACID_BITMAP_DSC_LEN + map_len; + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, h2c_len); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for mcc macid bitmap\n"); + return -ENOMEM; + } + + skb_put(skb, h2c_len); + RTW89_SET_FWCMD_MCC_MACID_BITMAP_GROUP(skb->data, group); + RTW89_SET_FWCMD_MCC_MACID_BITMAP_MACID(skb->data, macid); + RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP_LENGTH(skb->data, map_len); + RTW89_SET_FWCMD_MCC_MACID_BITMAP_BITMAP(skb->data, bitmap, map_len); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_MCC_MACID_BITMAP, 0, 0, + h2c_len); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_MCC_MACID_BITMAP); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_MCC_SYNC_LEN 4 +int rtw89_fw_h2c_mcc_sync(struct rtw89_dev *rtwdev, u8 group, u8 source, + u8 target, u8 offset) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_MCC_SYNC_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for mcc sync\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_MCC_SYNC_LEN); + RTW89_SET_FWCMD_MCC_SYNC_GROUP(skb->data, group); + RTW89_SET_FWCMD_MCC_SYNC_MACID_SOURCE(skb->data, source); + RTW89_SET_FWCMD_MCC_SYNC_MACID_TARGET(skb->data, target); + RTW89_SET_FWCMD_MCC_SYNC_SYNC_OFFSET(skb->data, offset); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_MCC_SYNC, 0, 0, + H2C_MCC_SYNC_LEN); + + cond = RTW89_MCC_WAIT_COND(group, H2C_FUNC_MCC_SYNC); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} + +#define H2C_MCC_SET_DURATION_LEN 20 +int rtw89_fw_h2c_mcc_set_duration(struct rtw89_dev *rtwdev, + const struct rtw89_fw_mcc_duration *p) +{ + struct rtw89_wait_info *wait = &rtwdev->mcc.wait; + struct sk_buff *skb; + unsigned int cond; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_MCC_SET_DURATION_LEN); + if (!skb) { + rtw89_err(rtwdev, + "failed to alloc skb for mcc set duration\n"); + return -ENOMEM; + } + + skb_put(skb, H2C_MCC_SET_DURATION_LEN); + RTW89_SET_FWCMD_MCC_SET_DURATION_GROUP(skb->data, p->group); + RTW89_SET_FWCMD_MCC_SET_DURATION_BTC_IN_GROUP(skb->data, p->btc_in_group); + RTW89_SET_FWCMD_MCC_SET_DURATION_START_MACID(skb->data, p->start_macid); + RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_X(skb->data, p->macid_x); + RTW89_SET_FWCMD_MCC_SET_DURATION_MACID_Y(skb->data, p->macid_y); + RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_LOW(skb->data, + p->start_tsf_low); + RTW89_SET_FWCMD_MCC_SET_DURATION_START_TSF_HIGH(skb->data, + p->start_tsf_high); + RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_X(skb->data, p->duration_x); + RTW89_SET_FWCMD_MCC_SET_DURATION_DURATION_Y(skb->data, p->duration_y); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MCC, + H2C_FUNC_MCC_SET_DURATION, 0, 0, + H2C_MCC_SET_DURATION_LEN); + + cond = RTW89_MCC_WAIT_COND(p->group, H2C_FUNC_MCC_SET_DURATION); + return rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); +} |