summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/realtek/rtw89/fw.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/realtek/rtw89/fw.c')
-rw-r--r--drivers/net/wireless/realtek/rtw89/fw.c690
1 files changed, 589 insertions, 101 deletions
diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c
index 9637f5e48d84..df1dc2f43c86 100644
--- a/drivers/net/wireless/realtek/rtw89/fw.c
+++ b/drivers/net/wireless/realtek/rtw89/fw.c
@@ -9,6 +9,7 @@
#include "fw.h"
#include "mac.h"
#include "phy.h"
+#include "ps.h"
#include "reg.h"
#include "util.h"
@@ -86,8 +87,8 @@ int rtw89_fw_check_rdy(struct rtw89_dev *rtwdev)
return 0;
}
-static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev, const u8 *fw, u32 len,
- struct rtw89_fw_bin_info *info)
+static int rtw89_fw_hdr_parser_v0(struct rtw89_dev *rtwdev, const u8 *fw, u32 len,
+ struct rtw89_fw_bin_info *info)
{
const struct rtw89_fw_hdr *fw_hdr = (const struct rtw89_fw_hdr *)fw;
struct rtw89_fw_hdr_section_info *section_info;
@@ -154,6 +155,94 @@ static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev, const u8 *fw, u32 len,
return 0;
}
+static int rtw89_fw_hdr_parser_v1(struct rtw89_dev *rtwdev, const u8 *fw, u32 len,
+ struct rtw89_fw_bin_info *info)
+{
+ const struct rtw89_fw_hdr_v1 *fw_hdr = (const struct rtw89_fw_hdr_v1 *)fw;
+ struct rtw89_fw_hdr_section_info *section_info;
+ const struct rtw89_fw_dynhdr_hdr *fwdynhdr;
+ const struct rtw89_fw_hdr_section_v1 *section;
+ const u8 *fw_end = fw + len;
+ const u8 *bin;
+ u32 base_hdr_len;
+ u32 mssc_len = 0;
+ u32 i;
+
+ info->section_num = le32_get_bits(fw_hdr->w6, FW_HDR_V1_W6_SEC_NUM);
+ base_hdr_len = struct_size(fw_hdr, sections, info->section_num);
+ info->dynamic_hdr_en = le32_get_bits(fw_hdr->w7, FW_HDR_V1_W7_DYN_HDR);
+
+ if (info->dynamic_hdr_en) {
+ info->hdr_len = le32_get_bits(fw_hdr->w5, FW_HDR_V1_W5_HDR_SIZE);
+ info->dynamic_hdr_len = info->hdr_len - base_hdr_len;
+ fwdynhdr = (const struct rtw89_fw_dynhdr_hdr *)(fw + base_hdr_len);
+ if (le32_to_cpu(fwdynhdr->hdr_len) != 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;
+
+ /* jump to section header */
+ section_info = info->section_info;
+ for (i = 0; i < info->section_num; i++) {
+ section = &fw_hdr->sections[i];
+ section_info->type =
+ le32_get_bits(section->w1, FWSECTION_HDR_V1_W1_SECTIONTYPE);
+ if (section_info->type == FWDL_SECURITY_SECTION_TYPE) {
+ section_info->mssc =
+ le32_get_bits(section->w2, FWSECTION_HDR_V1_W2_MSSC);
+ mssc_len += section_info->mssc * FWDL_SECURITY_SIGLEN;
+ } else {
+ section_info->mssc = 0;
+ }
+
+ section_info->len =
+ le32_get_bits(section->w1, FWSECTION_HDR_V1_W1_SEC_SIZE);
+ if (le32_get_bits(section->w1, FWSECTION_HDR_V1_W1_CHECKSUM))
+ section_info->len += FWDL_SECTION_CHKSUM_LEN;
+ section_info->redl = le32_get_bits(section->w1, FWSECTION_HDR_V1_W1_REDL);
+ section_info->dladdr =
+ le32_get_bits(section->w0, FWSECTION_HDR_V1_W0_DL_ADDR);
+ section_info->addr = bin;
+ bin += section_info->len;
+ section_info++;
+ }
+
+ if (fw_end != bin + mssc_len) {
+ rtw89_err(rtwdev, "[ERR]fw bin size\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_suit *fw_suit,
+ struct rtw89_fw_bin_info *info)
+{
+ const u8 *fw = fw_suit->data;
+ u32 len = fw_suit->size;
+
+ if (!fw || !len) {
+ rtw89_err(rtwdev, "fw type %d isn't recognized\n", fw_suit->type);
+ return -ENOENT;
+ }
+
+ switch (fw_suit->hdr_ver) {
+ case 0:
+ return rtw89_fw_hdr_parser_v0(rtwdev, fw, len, info);
+ case 1:
+ return rtw89_fw_hdr_parser_v1(rtwdev, fw, len, info);
+ default:
+ return -ENOENT;
+ }
+}
+
static
int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
struct rtw89_fw_suit *fw_suit, bool nowarn)
@@ -178,42 +267,110 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
for (i = 0; i < mfw_hdr->fw_nr; i++) {
mfw_info = &mfw_hdr->info[i];
- if (mfw_info->cv != rtwdev->hal.cv ||
- mfw_info->type != type ||
- mfw_info->mp)
- continue;
-
- fw_suit->data = mfw + le32_to_cpu(mfw_info->shift);
- fw_suit->size = le32_to_cpu(mfw_info->size);
- return 0;
+ if (mfw_info->type == type) {
+ if (mfw_info->cv == rtwdev->hal.cv && !mfw_info->mp)
+ goto found;
+ if (type == RTW89_FW_LOGFMT)
+ goto found;
+ }
}
if (!nowarn)
rtw89_err(rtwdev, "no suitable firmware found\n");
return -ENOENT;
+
+found:
+ fw_suit->data = mfw + le32_to_cpu(mfw_info->shift);
+ fw_suit->size = le32_to_cpu(mfw_info->size);
+ return 0;
}
-static void rtw89_fw_update_ver(struct rtw89_dev *rtwdev,
- enum rtw89_fw_type type,
- struct rtw89_fw_suit *fw_suit)
+static u32 rtw89_mfw_get_size(struct rtw89_dev *rtwdev)
{
- const struct rtw89_fw_hdr *hdr = (const struct rtw89_fw_hdr *)fw_suit->data;
+ struct rtw89_fw_info *fw_info = &rtwdev->fw;
+ const struct firmware *firmware = fw_info->req.firmware;
+ const struct rtw89_mfw_hdr *mfw_hdr =
+ (const struct rtw89_mfw_hdr *)firmware->data;
+ const struct rtw89_mfw_info *mfw_info;
+ u32 size;
+
+ if (mfw_hdr->sig != RTW89_MFW_SIG) {
+ rtw89_warn(rtwdev, "not mfw format\n");
+ return 0;
+ }
+
+ mfw_info = &mfw_hdr->info[mfw_hdr->fw_nr - 1];
+ size = le32_to_cpu(mfw_info->shift) + le32_to_cpu(mfw_info->size);
+ return size;
+}
+
+static void rtw89_fw_update_ver_v0(struct rtw89_dev *rtwdev,
+ struct rtw89_fw_suit *fw_suit,
+ const struct rtw89_fw_hdr *hdr)
+{
fw_suit->major_ver = le32_get_bits(hdr->w1, FW_HDR_W1_MAJOR_VERSION);
fw_suit->minor_ver = le32_get_bits(hdr->w1, FW_HDR_W1_MINOR_VERSION);
fw_suit->sub_ver = le32_get_bits(hdr->w1, FW_HDR_W1_SUBVERSION);
fw_suit->sub_idex = le32_get_bits(hdr->w1, FW_HDR_W1_SUBINDEX);
+ fw_suit->commitid = le32_get_bits(hdr->w2, FW_HDR_W2_COMMITID);
fw_suit->build_year = le32_get_bits(hdr->w5, FW_HDR_W5_YEAR);
fw_suit->build_mon = le32_get_bits(hdr->w4, FW_HDR_W4_MONTH);
fw_suit->build_date = le32_get_bits(hdr->w4, FW_HDR_W4_DATE);
fw_suit->build_hour = le32_get_bits(hdr->w4, FW_HDR_W4_HOUR);
fw_suit->build_min = le32_get_bits(hdr->w4, FW_HDR_W4_MIN);
fw_suit->cmd_ver = le32_get_bits(hdr->w7, FW_HDR_W7_CMD_VERSERION);
+}
+
+static void rtw89_fw_update_ver_v1(struct rtw89_dev *rtwdev,
+ struct rtw89_fw_suit *fw_suit,
+ const struct rtw89_fw_hdr_v1 *hdr)
+{
+ fw_suit->major_ver = le32_get_bits(hdr->w1, FW_HDR_V1_W1_MAJOR_VERSION);
+ fw_suit->minor_ver = le32_get_bits(hdr->w1, FW_HDR_V1_W1_MINOR_VERSION);
+ fw_suit->sub_ver = le32_get_bits(hdr->w1, FW_HDR_V1_W1_SUBVERSION);
+ fw_suit->sub_idex = le32_get_bits(hdr->w1, FW_HDR_V1_W1_SUBINDEX);
+ fw_suit->commitid = le32_get_bits(hdr->w2, FW_HDR_V1_W2_COMMITID);
+ fw_suit->build_year = le32_get_bits(hdr->w5, FW_HDR_V1_W5_YEAR);
+ fw_suit->build_mon = le32_get_bits(hdr->w4, FW_HDR_V1_W4_MONTH);
+ fw_suit->build_date = le32_get_bits(hdr->w4, FW_HDR_V1_W4_DATE);
+ fw_suit->build_hour = le32_get_bits(hdr->w4, FW_HDR_V1_W4_HOUR);
+ fw_suit->build_min = le32_get_bits(hdr->w4, FW_HDR_V1_W4_MIN);
+ fw_suit->cmd_ver = le32_get_bits(hdr->w7, FW_HDR_V1_W3_CMD_VERSERION);
+}
+
+static int rtw89_fw_update_ver(struct rtw89_dev *rtwdev,
+ enum rtw89_fw_type type,
+ struct rtw89_fw_suit *fw_suit)
+{
+ const struct rtw89_fw_hdr *v0 = (const struct rtw89_fw_hdr *)fw_suit->data;
+ const struct rtw89_fw_hdr_v1 *v1 = (const struct rtw89_fw_hdr_v1 *)fw_suit->data;
+
+ if (type == RTW89_FW_LOGFMT)
+ return 0;
+
+ fw_suit->type = type;
+ fw_suit->hdr_ver = le32_get_bits(v0->w3, FW_HDR_W3_HDR_VER);
+
+ switch (fw_suit->hdr_ver) {
+ case 0:
+ rtw89_fw_update_ver_v0(rtwdev, fw_suit, v0);
+ break;
+ case 1:
+ rtw89_fw_update_ver_v1(rtwdev, fw_suit, v1);
+ break;
+ default:
+ rtw89_err(rtwdev, "Unknown firmware header version %u\n",
+ fw_suit->hdr_ver);
+ return -ENOENT;
+ }
rtw89_info(rtwdev,
- "Firmware version %u.%u.%u.%u, cmd version %u, type %u\n",
+ "Firmware version %u.%u.%u.%u (%08x), cmd version %u, type %u\n",
fw_suit->major_ver, fw_suit->minor_ver, fw_suit->sub_ver,
- fw_suit->sub_idex, fw_suit->cmd_ver, type);
+ fw_suit->sub_idex, fw_suit->commitid, fw_suit->cmd_ver, type);
+
+ return 0;
}
static
@@ -227,9 +384,22 @@ int __rtw89_fw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
if (ret)
return ret;
- rtw89_fw_update_ver(rtwdev, type, fw_suit);
+ return rtw89_fw_update_ver(rtwdev, type, fw_suit);
+}
- return 0;
+static
+int __rtw89_fw_recognize_from_elm(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_element_hdr *elm,
+ const void *data)
+{
+ enum rtw89_fw_type type = (enum rtw89_fw_type)data;
+ struct rtw89_fw_suit *fw_suit;
+
+ fw_suit = rtw89_fw_suit_get(rtwdev, type);
+ fw_suit->data = elm->u.common.contents;
+ fw_suit->size = le32_to_cpu(elm->size);
+
+ return rtw89_fw_update_ver(rtwdev, type, fw_suit);
}
#define __DEF_FW_FEAT_COND(__cond, __op) \
@@ -312,31 +482,17 @@ rtw89_early_fw_feature_recognize(struct device *device,
struct rtw89_fw_info *early_fw,
int *used_fw_format)
{
- union rtw89_compat_fw_hdr buf = {};
const struct firmware *firmware;
- bool full_req = false;
char fw_name[64];
int fw_format;
u32 ver_code;
int ret;
- /* 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;
-
for (fw_format = chip->fw_format_max; fw_format >= 0; fw_format--) {
rtw89_fw_get_filename(fw_name, sizeof(fw_name),
chip->fw_basename, fw_format);
- if (full_req)
- ret = request_firmware(&firmware, fw_name, device);
- else
- ret = request_partial_firmware_into_buf(&firmware, fw_name,
- device, &buf, sizeof(buf),
- 0);
+ ret = request_firmware(&firmware, fw_name, device);
if (!ret) {
dev_info(device, "loaded firmware %s\n", fw_name);
*used_fw_format = fw_format;
@@ -349,10 +505,7 @@ rtw89_early_fw_feature_recognize(struct device *device,
return NULL;
}
- if (full_req)
- ver_code = rtw89_compat_fw_hdr_ver_code(firmware->data);
- else
- ver_code = rtw89_compat_fw_hdr_ver_code(&buf);
+ ver_code = rtw89_compat_fw_hdr_ver_code(firmware->data);
if (!ver_code)
goto out;
@@ -360,11 +513,7 @@ rtw89_early_fw_feature_recognize(struct device *device,
rtw89_fw_iterate_feature_cfg(early_fw, chip, ver_code);
out:
- if (full_req)
- return firmware;
-
- release_firmware(firmware);
- return NULL;
+ return firmware;
}
int rtw89_fw_recognize(struct rtw89_dev *rtwdev)
@@ -386,6 +535,9 @@ normal_done:
/* It still works if wowlan firmware isn't existing. */
__rtw89_fw_recognize(rtwdev, RTW89_FW_WOWLAN, false);
+ /* It still works if log format file isn't existing. */
+ __rtw89_fw_recognize(rtwdev, RTW89_FW_LOGFMT, true);
+
rtw89_fw_recognize_features(rtwdev);
rtw89_coex_recognize_ver(rtwdev);
@@ -393,6 +545,153 @@ normal_done:
return 0;
}
+static
+int rtw89_build_phy_tbl_from_elm(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_element_hdr *elm,
+ const void *data)
+{
+ struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
+ struct rtw89_phy_table *tbl;
+ struct rtw89_reg2_def *regs;
+ enum rtw89_rf_path rf_path;
+ u32 n_regs, i;
+ u8 idx;
+
+ tbl = kzalloc(sizeof(*tbl), GFP_KERNEL);
+ if (!tbl)
+ return -ENOMEM;
+
+ switch (le32_to_cpu(elm->id)) {
+ case RTW89_FW_ELEMENT_ID_BB_REG:
+ elm_info->bb_tbl = tbl;
+ break;
+ case RTW89_FW_ELEMENT_ID_BB_GAIN:
+ elm_info->bb_gain = tbl;
+ break;
+ case RTW89_FW_ELEMENT_ID_RADIO_A:
+ case RTW89_FW_ELEMENT_ID_RADIO_B:
+ case RTW89_FW_ELEMENT_ID_RADIO_C:
+ case RTW89_FW_ELEMENT_ID_RADIO_D:
+ rf_path = (enum rtw89_rf_path)data;
+ idx = elm->u.reg2.idx;
+
+ elm_info->rf_radio[idx] = tbl;
+ tbl->rf_path = rf_path;
+ tbl->config = rtw89_phy_config_rf_reg_v1;
+ break;
+ case RTW89_FW_ELEMENT_ID_RF_NCTL:
+ elm_info->rf_nctl = tbl;
+ break;
+ default:
+ kfree(tbl);
+ return -ENOENT;
+ }
+
+ n_regs = le32_to_cpu(elm->size) / sizeof(tbl->regs[0]);
+ regs = kcalloc(n_regs, sizeof(tbl->regs[0]), GFP_KERNEL);
+ if (!regs)
+ goto out;
+
+ for (i = 0; i < n_regs; i++) {
+ regs[i].addr = le32_to_cpu(elm->u.reg2.regs[i].addr);
+ regs[i].data = le32_to_cpu(elm->u.reg2.regs[i].data);
+ }
+
+ tbl->n_regs = n_regs;
+ tbl->regs = regs;
+
+ return 0;
+
+out:
+ kfree(tbl);
+ return -ENOMEM;
+}
+
+struct rtw89_fw_element_handler {
+ int (*fn)(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_element_hdr *elm, const void *data);
+ const void *data;
+ const char *name;
+};
+
+static const struct rtw89_fw_element_handler __fw_element_handlers[] = {
+ [RTW89_FW_ELEMENT_ID_BBMCU0] = {__rtw89_fw_recognize_from_elm,
+ (const void *)RTW89_FW_BBMCU0, NULL},
+ [RTW89_FW_ELEMENT_ID_BBMCU1] = {__rtw89_fw_recognize_from_elm,
+ (const void *)RTW89_FW_BBMCU1, NULL},
+ [RTW89_FW_ELEMENT_ID_BB_REG] = {rtw89_build_phy_tbl_from_elm, NULL, "BB"},
+ [RTW89_FW_ELEMENT_ID_BB_GAIN] = {rtw89_build_phy_tbl_from_elm, NULL, NULL},
+ [RTW89_FW_ELEMENT_ID_RADIO_A] = {rtw89_build_phy_tbl_from_elm,
+ (const void *)RF_PATH_A, "radio A"},
+ [RTW89_FW_ELEMENT_ID_RADIO_B] = {rtw89_build_phy_tbl_from_elm,
+ (const void *)RF_PATH_B, NULL},
+ [RTW89_FW_ELEMENT_ID_RADIO_C] = {rtw89_build_phy_tbl_from_elm,
+ (const void *)RF_PATH_C, NULL},
+ [RTW89_FW_ELEMENT_ID_RADIO_D] = {rtw89_build_phy_tbl_from_elm,
+ (const void *)RF_PATH_D, NULL},
+ [RTW89_FW_ELEMENT_ID_RF_NCTL] = {rtw89_build_phy_tbl_from_elm, NULL, "NCTL"},
+};
+
+int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_fw_info *fw_info = &rtwdev->fw;
+ const struct firmware *firmware = fw_info->req.firmware;
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ u32 unrecognized_elements = chip->needed_fw_elms;
+ const struct rtw89_fw_element_handler *handler;
+ const struct rtw89_fw_element_hdr *hdr;
+ u32 elm_size;
+ u32 elem_id;
+ u32 offset;
+ int ret;
+
+ BUILD_BUG_ON(sizeof(chip->needed_fw_elms) * 8 < RTW89_FW_ELEMENT_ID_NUM);
+
+ offset = rtw89_mfw_get_size(rtwdev);
+ offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);
+ if (offset == 0)
+ return -EINVAL;
+
+ while (offset + sizeof(*hdr) < firmware->size) {
+ hdr = (const struct rtw89_fw_element_hdr *)(firmware->data + offset);
+
+ elm_size = le32_to_cpu(hdr->size);
+ if (offset + elm_size >= firmware->size) {
+ rtw89_warn(rtwdev, "firmware element size exceeds\n");
+ break;
+ }
+
+ elem_id = le32_to_cpu(hdr->id);
+ if (elem_id >= ARRAY_SIZE(__fw_element_handlers))
+ goto next;
+
+ handler = &__fw_element_handlers[elem_id];
+ if (!handler->fn)
+ goto next;
+
+ ret = handler->fn(rtwdev, hdr, handler->data);
+ if (ret)
+ return ret;
+
+ if (handler->name)
+ rtw89_info(rtwdev, "Firmware element %s version: %4ph\n",
+ handler->name, hdr->ver);
+
+ unrecognized_elements &= ~BIT(elem_id);
+next:
+ offset += sizeof(*hdr) + elm_size;
+ offset = ALIGN(offset, RTW89_FW_ELEMENT_ALIGN);
+ }
+
+ if (unrecognized_elements) {
+ rtw89_err(rtwdev, "Firmware elements 0x%08x are unrecognized\n",
+ unrecognized_elements);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
void rtw89_h2c_pkt_set_hdr(struct rtw89_dev *rtwdev, struct sk_buff *skb,
u8 type, u8 cat, u8 class, u8 func,
bool rack, bool dack, u32 len)
@@ -593,8 +892,6 @@ int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type)
struct rtw89_fw_info *fw_info = &rtwdev->fw;
struct rtw89_fw_suit *fw_suit = rtw89_fw_suit_get(rtwdev, type);
struct rtw89_fw_bin_info info;
- const u8 *fw = fw_suit->data;
- u32 len = fw_suit->size;
u8 val;
int ret;
@@ -603,12 +900,7 @@ int rtw89_fw_download(struct rtw89_dev *rtwdev, enum rtw89_fw_type type)
if (ret)
return ret;
- if (!fw || !len) {
- rtw89_err(rtwdev, "fw type %d isn't recognized\n", type);
- return -ENOENT;
- }
-
- ret = rtw89_fw_hdr_parser(rtwdev, fw, len, &info);
+ ret = rtw89_fw_hdr_parser(rtwdev, fw_suit, &info);
if (ret) {
rtw89_err(rtwdev, "parse fw header fail\n");
goto fwdl_err;
@@ -622,13 +914,14 @@ 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 - info.dynamic_hdr_len);
+ ret = rtw89_fw_download_hdr(rtwdev, fw_suit->data, info.hdr_len -
+ info.dynamic_hdr_len);
if (ret) {
ret = -EBUSY;
goto fwdl_err;
}
- ret = rtw89_fw_download_main(rtwdev, fw, &info);
+ ret = rtw89_fw_download_main(rtwdev, fw_suit->data, &info);
if (ret) {
ret = -EBUSY;
goto fwdl_err;
@@ -695,6 +988,27 @@ void rtw89_load_firmware_work(struct work_struct *work)
rtw89_load_firmware_req(rtwdev, &rtwdev->fw.req, fw_name, false);
}
+static void rtw89_free_phy_tbl_from_elm(struct rtw89_phy_table *tbl)
+{
+ if (!tbl)
+ return;
+
+ kfree(tbl->regs);
+ kfree(tbl);
+}
+
+static void rtw89_unload_firmware_elements(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info;
+ int i;
+
+ rtw89_free_phy_tbl_from_elm(elm_info->bb_tbl);
+ rtw89_free_phy_tbl_from_elm(elm_info->bb_gain);
+ for (i = 0; i < ARRAY_SIZE(elm_info->rf_radio); i++)
+ rtw89_free_phy_tbl_from_elm(elm_info->rf_radio[i]);
+ rtw89_free_phy_tbl_from_elm(elm_info->rf_nctl);
+}
+
void rtw89_unload_firmware(struct rtw89_dev *rtwdev)
{
struct rtw89_fw_info *fw = &rtwdev->fw;
@@ -709,6 +1023,151 @@ void rtw89_unload_firmware(struct rtw89_dev *rtwdev)
*/
fw->req.firmware = NULL;
}
+
+ kfree(fw->log.fmts);
+ rtw89_unload_firmware_elements(rtwdev);
+}
+
+static u32 rtw89_fw_log_get_fmt_idx(struct rtw89_dev *rtwdev, u32 fmt_id)
+{
+ struct rtw89_fw_log *fw_log = &rtwdev->fw.log;
+ u32 i;
+
+ if (fmt_id > fw_log->last_fmt_id)
+ return 0;
+
+ for (i = 0; i < fw_log->fmt_count; i++) {
+ if (le32_to_cpu(fw_log->fmt_ids[i]) == fmt_id)
+ return i;
+ }
+ return 0;
+}
+
+static int rtw89_fw_log_create_fmts_dict(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_fw_log *log = &rtwdev->fw.log;
+ const struct rtw89_fw_logsuit_hdr *suit_hdr;
+ struct rtw89_fw_suit *suit = &log->suit;
+ const void *fmts_ptr, *fmts_end_ptr;
+ u32 fmt_count;
+ int i;
+
+ suit_hdr = (const struct rtw89_fw_logsuit_hdr *)suit->data;
+ fmt_count = le32_to_cpu(suit_hdr->count);
+ log->fmt_ids = suit_hdr->ids;
+ fmts_ptr = &suit_hdr->ids[fmt_count];
+ fmts_end_ptr = suit->data + suit->size;
+ log->fmts = kcalloc(fmt_count, sizeof(char *), GFP_KERNEL);
+ if (!log->fmts)
+ return -ENOMEM;
+
+ for (i = 0; i < fmt_count; i++) {
+ fmts_ptr = memchr_inv(fmts_ptr, 0, fmts_end_ptr - fmts_ptr);
+ if (!fmts_ptr)
+ break;
+
+ (*log->fmts)[i] = fmts_ptr;
+ log->last_fmt_id = le32_to_cpu(log->fmt_ids[i]);
+ log->fmt_count++;
+ fmts_ptr += strlen(fmts_ptr);
+ }
+
+ return 0;
+}
+
+int rtw89_fw_log_prepare(struct rtw89_dev *rtwdev)
+{
+ struct rtw89_fw_log *log = &rtwdev->fw.log;
+ struct rtw89_fw_suit *suit = &log->suit;
+
+ if (!suit || !suit->data) {
+ rtw89_debug(rtwdev, RTW89_DBG_FW, "no log format file\n");
+ return -EINVAL;
+ }
+ if (log->fmts)
+ return 0;
+
+ return rtw89_fw_log_create_fmts_dict(rtwdev);
+}
+
+static void rtw89_fw_log_dump_data(struct rtw89_dev *rtwdev,
+ const struct rtw89_fw_c2h_log_fmt *log_fmt,
+ u32 fmt_idx, u8 para_int, bool raw_data)
+{
+ const char *(*fmts)[] = rtwdev->fw.log.fmts;
+ char str_buf[RTW89_C2H_FW_LOG_STR_BUF_SIZE];
+ u32 args[RTW89_C2H_FW_LOG_MAX_PARA_NUM] = {0};
+ int i;
+
+ if (log_fmt->argc > RTW89_C2H_FW_LOG_MAX_PARA_NUM) {
+ rtw89_warn(rtwdev, "C2H log: Arg count is unexpected %d\n",
+ log_fmt->argc);
+ return;
+ }
+
+ if (para_int)
+ for (i = 0 ; i < log_fmt->argc; i++)
+ args[i] = le32_to_cpu(log_fmt->u.argv[i]);
+
+ if (raw_data) {
+ if (para_int)
+ snprintf(str_buf, RTW89_C2H_FW_LOG_STR_BUF_SIZE,
+ "fw_enc(%d, %d, %d) %*ph", le32_to_cpu(log_fmt->fmt_id),
+ para_int, log_fmt->argc, (int)sizeof(args), args);
+ else
+ snprintf(str_buf, RTW89_C2H_FW_LOG_STR_BUF_SIZE,
+ "fw_enc(%d, %d, %d, %s)", le32_to_cpu(log_fmt->fmt_id),
+ para_int, log_fmt->argc, log_fmt->u.raw);
+ } else {
+ snprintf(str_buf, RTW89_C2H_FW_LOG_STR_BUF_SIZE, (*fmts)[fmt_idx],
+ args[0x0], args[0x1], args[0x2], args[0x3], args[0x4],
+ args[0x5], args[0x6], args[0x7], args[0x8], args[0x9],
+ args[0xa], args[0xb], args[0xc], args[0xd], args[0xe],
+ args[0xf]);
+ }
+
+ rtw89_info(rtwdev, "C2H log: %s", str_buf);
+}
+
+void rtw89_fw_log_dump(struct rtw89_dev *rtwdev, u8 *buf, u32 len)
+{
+ const struct rtw89_fw_c2h_log_fmt *log_fmt;
+ u8 para_int;
+ u32 fmt_idx;
+
+ if (len < RTW89_C2H_HEADER_LEN) {
+ rtw89_err(rtwdev, "c2h log length is wrong!\n");
+ return;
+ }
+
+ buf += RTW89_C2H_HEADER_LEN;
+ len -= RTW89_C2H_HEADER_LEN;
+ log_fmt = (const struct rtw89_fw_c2h_log_fmt *)buf;
+
+ if (len < RTW89_C2H_FW_FORMATTED_LOG_MIN_LEN)
+ goto plain_log;
+
+ if (log_fmt->signature != cpu_to_le16(RTW89_C2H_FW_LOG_SIGNATURE))
+ goto plain_log;
+
+ if (!rtwdev->fw.log.fmts)
+ return;
+
+ para_int = u8_get_bits(log_fmt->feature, RTW89_C2H_FW_LOG_FEATURE_PARA_INT);
+ fmt_idx = rtw89_fw_log_get_fmt_idx(rtwdev, le32_to_cpu(log_fmt->fmt_id));
+
+ if (!para_int && log_fmt->argc != 0 && fmt_idx != 0)
+ rtw89_info(rtwdev, "C2H log: %s%s",
+ (*rtwdev->fw.log.fmts)[fmt_idx], log_fmt->u.raw);
+ else if (fmt_idx != 0 && para_int)
+ rtw89_fw_log_dump_data(rtwdev, log_fmt, fmt_idx, para_int, false);
+ else
+ rtw89_fw_log_dump_data(rtwdev, log_fmt, fmt_idx, para_int, true);
+ return;
+
+plain_log:
+ rtw89_info(rtwdev, "C2H log: %.*s", len, buf);
+
}
#define H2C_CAM_LEN 60
@@ -922,7 +1381,7 @@ int rtw89_fw_h2c_fw_log(struct rtw89_dev *rtwdev, bool enable)
}
skb_put(skb, H2C_LOG_CFG_LEN);
- SET_LOG_CFG_LEVEL(skb->data, RTW89_FW_LOG_LEVEL_SER);
+ SET_LOG_CFG_LEVEL(skb->data, RTW89_FW_LOG_LEVEL_LOUD);
SET_LOG_CFG_PATH(skb->data, BIT(RTW89_FW_LOG_LEVEL_C2H));
SET_LOG_CFG_COMP(skb->data, comp);
SET_LOG_CFG_COMP_EXT(skb->data, 0);
@@ -1300,7 +1759,8 @@ int rtw89_fw_h2c_assoc_cmac_tbl(struct rtw89_dev *rtwdev,
const struct rtw89_chip_info *chip = rtwdev->chip;
struct rtw89_sta *rtwsta = sta_to_rtwsta_safe(sta);
struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
- const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0);
+ const struct rtw89_chan *chan = rtw89_chan_get(rtwdev,
+ rtwvif->sub_entity_idx);
struct sk_buff *skb;
u8 pads[RTW89_PPE_BW_NUM];
u8 mac_id = rtwsta ? rtwsta->mac_id : rtwvif->mac_id;
@@ -1457,12 +1917,15 @@ int rtw89_fw_h2c_update_beacon(struct rtw89_dev *rtwdev,
struct rtw89_vif *rtwvif)
{
struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif);
- const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_SUB_ENTITY_0);
+ const struct rtw89_chan *chan = rtw89_chan_get(rtwdev,
+ rtwvif->sub_entity_idx);
struct sk_buff *skb;
struct sk_buff *skb_beacon;
u16 tim_offset;
int bcn_total_len;
u16 beacon_rate;
+ void *noa_data;
+ u8 noa_len;
int ret;
if (vif->p2p)
@@ -1479,6 +1942,13 @@ int rtw89_fw_h2c_update_beacon(struct rtw89_dev *rtwdev,
return -ENOMEM;
}
+ noa_len = rtw89_p2p_noa_fetch(rtwvif, &noa_data);
+ if (noa_len &&
+ (noa_len <= skb_tailroom(skb_beacon) ||
+ pskb_expand_head(skb_beacon, 0, noa_len, GFP_KERNEL) == 0)) {
+ skb_put_data(skb_beacon, noa_data, noa_len);
+ }
+
bcn_total_len = H2C_BCN_BASE_LEN + skb_beacon->len;
skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, bcn_total_len);
if (!skb) {
@@ -1903,61 +2373,76 @@ fail:
return ret;
}
-#define H2C_RA_LEN 16
int rtw89_fw_h2c_ra(struct rtw89_dev *rtwdev, struct rtw89_ra_info *ra, bool csi)
{
+ const struct rtw89_chip_info *chip = rtwdev->chip;
+ struct rtw89_h2c_ra_v1 *h2c_v1;
+ struct rtw89_h2c_ra *h2c;
+ u32 len = sizeof(*h2c);
+ bool format_v1 = false;
struct sk_buff *skb;
- u8 *cmd;
int ret;
- skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_RA_LEN);
+ if (chip->chip_gen == RTW89_CHIP_BE) {
+ len = sizeof(*h2c_v1);
+ format_v1 = true;
+ }
+
+ skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len);
if (!skb) {
rtw89_err(rtwdev, "failed to alloc skb for h2c join\n");
return -ENOMEM;
}
- skb_put(skb, H2C_RA_LEN);
- cmd = skb->data;
+ skb_put(skb, len);
+ h2c = (struct rtw89_h2c_ra *)skb->data;
rtw89_debug(rtwdev, RTW89_DBG_RA,
"ra cmd msk: %llx ", ra->ra_mask);
- RTW89_SET_FWCMD_RA_MODE(cmd, ra->mode_ctrl);
- RTW89_SET_FWCMD_RA_BW_CAP(cmd, ra->bw_cap);
- RTW89_SET_FWCMD_RA_MACID(cmd, ra->macid);
- RTW89_SET_FWCMD_RA_DCM(cmd, ra->dcm_cap);
- RTW89_SET_FWCMD_RA_ER(cmd, ra->er_cap);
- RTW89_SET_FWCMD_RA_INIT_RATE_LV(cmd, ra->init_rate_lv);
- RTW89_SET_FWCMD_RA_UPD_ALL(cmd, ra->upd_all);
- RTW89_SET_FWCMD_RA_SGI(cmd, ra->en_sgi);
- RTW89_SET_FWCMD_RA_LDPC(cmd, ra->ldpc_cap);
- RTW89_SET_FWCMD_RA_STBC(cmd, ra->stbc_cap);
- RTW89_SET_FWCMD_RA_SS_NUM(cmd, ra->ss_num);
- RTW89_SET_FWCMD_RA_GILTF(cmd, ra->giltf);
- RTW89_SET_FWCMD_RA_UPD_BW_NSS_MASK(cmd, ra->upd_bw_nss_mask);
- RTW89_SET_FWCMD_RA_UPD_MASK(cmd, ra->upd_mask);
- RTW89_SET_FWCMD_RA_MASK_0(cmd, FIELD_GET(MASKBYTE0, ra->ra_mask));
- RTW89_SET_FWCMD_RA_MASK_1(cmd, FIELD_GET(MASKBYTE1, ra->ra_mask));
- RTW89_SET_FWCMD_RA_MASK_2(cmd, FIELD_GET(MASKBYTE2, ra->ra_mask));
- RTW89_SET_FWCMD_RA_MASK_3(cmd, FIELD_GET(MASKBYTE3, ra->ra_mask));
- RTW89_SET_FWCMD_RA_MASK_4(cmd, FIELD_GET(MASKBYTE4, ra->ra_mask));
- RTW89_SET_FWCMD_RA_FIX_GILTF_EN(cmd, ra->fix_giltf_en);
- RTW89_SET_FWCMD_RA_FIX_GILTF(cmd, ra->fix_giltf);
-
- if (csi) {
- RTW89_SET_FWCMD_RA_BFEE_CSI_CTL(cmd, 1);
- RTW89_SET_FWCMD_RA_BAND_NUM(cmd, ra->band_num);
- RTW89_SET_FWCMD_RA_CR_TBL_SEL(cmd, ra->cr_tbl_sel);
- RTW89_SET_FWCMD_RA_FIXED_CSI_RATE_EN(cmd, ra->fixed_csi_rate_en);
- RTW89_SET_FWCMD_RA_RA_CSI_RATE_EN(cmd, ra->ra_csi_rate_en);
- RTW89_SET_FWCMD_RA_FIXED_CSI_MCS_SS_IDX(cmd, ra->csi_mcs_ss_idx);
- RTW89_SET_FWCMD_RA_FIXED_CSI_MODE(cmd, ra->csi_mode);
- RTW89_SET_FWCMD_RA_FIXED_CSI_GI_LTF(cmd, ra->csi_gi_ltf);
- RTW89_SET_FWCMD_RA_FIXED_CSI_BW(cmd, ra->csi_bw);
- }
-
+ h2c->w0 = le32_encode_bits(ra->mode_ctrl, RTW89_H2C_RA_W0_MODE) |
+ le32_encode_bits(ra->bw_cap, RTW89_H2C_RA_W0_BW_CAP) |
+ le32_encode_bits(ra->macid, RTW89_H2C_RA_W0_MACID) |
+ le32_encode_bits(ra->dcm_cap, RTW89_H2C_RA_W0_DCM) |
+ le32_encode_bits(ra->er_cap, RTW89_H2C_RA_W0_ER) |
+ le32_encode_bits(ra->init_rate_lv, RTW89_H2C_RA_W0_INIT_RATE_LV) |
+ le32_encode_bits(ra->upd_all, RTW89_H2C_RA_W0_UPD_ALL) |
+ le32_encode_bits(ra->en_sgi, RTW89_H2C_RA_W0_SGI) |
+ le32_encode_bits(ra->ldpc_cap, RTW89_H2C_RA_W0_LDPC) |
+ le32_encode_bits(ra->stbc_cap, RTW89_H2C_RA_W0_STBC) |
+ le32_encode_bits(ra->ss_num, RTW89_H2C_RA_W0_SS_NUM) |
+ le32_encode_bits(ra->giltf, RTW89_H2C_RA_W0_GILTF) |
+ le32_encode_bits(ra->upd_bw_nss_mask, RTW89_H2C_RA_W0_UPD_BW_NSS_MASK) |
+ le32_encode_bits(ra->upd_mask, RTW89_H2C_RA_W0_UPD_MASK);
+ h2c->w1 = le32_encode_bits(ra->ra_mask, RTW89_H2C_RA_W1_RAMASK_LO32);
+ h2c->w2 = le32_encode_bits(ra->ra_mask >> 32, RTW89_H2C_RA_W2_RAMASK_HI32);
+ h2c->w3 = le32_encode_bits(ra->fix_giltf_en, RTW89_H2C_RA_W3_FIX_GILTF_EN) |
+ le32_encode_bits(ra->fix_giltf, RTW89_H2C_RA_W3_FIX_GILTF);
+
+ if (!format_v1)
+ goto csi;
+
+ h2c_v1 = (struct rtw89_h2c_ra_v1 *)h2c;
+ h2c_v1->w4 = le32_encode_bits(ra->mode_ctrl, RTW89_H2C_RA_V1_W4_MODE_EHT) |
+ le32_encode_bits(ra->bw_cap, RTW89_H2C_RA_V1_W4_BW_EHT);
+
+csi:
+ if (!csi)
+ goto done;
+
+ h2c->w2 |= le32_encode_bits(1, RTW89_H2C_RA_W2_BFEE_CSI_CTL);
+ h2c->w3 |= le32_encode_bits(ra->band_num, RTW89_H2C_RA_W3_BAND_NUM) |
+ le32_encode_bits(ra->cr_tbl_sel, RTW89_H2C_RA_W3_CR_TBL_SEL) |
+ le32_encode_bits(ra->fixed_csi_rate_en, RTW89_H2C_RA_W3_FIXED_CSI_RATE_EN) |
+ le32_encode_bits(ra->ra_csi_rate_en, RTW89_H2C_RA_W3_RA_CSI_RATE_EN) |
+ le32_encode_bits(ra->csi_mcs_ss_idx, RTW89_H2C_RA_W3_FIXED_CSI_MCS_SS_IDX) |
+ le32_encode_bits(ra->csi_mode, RTW89_H2C_RA_W3_FIXED_CSI_MODE) |
+ le32_encode_bits(ra->csi_gi_ltf, RTW89_H2C_RA_W3_FIXED_CSI_GI_LTF) |
+ le32_encode_bits(ra->csi_bw, RTW89_H2C_RA_W3_FIXED_CSI_BW);
+
+done:
rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C,
H2C_CAT_OUTSRC, H2C_CL_OUTSRC_RA,
H2C_FUNC_OUTSRC_RA_MACIDCFG, 0, 0,
- H2C_RA_LEN);
+ len);
ret = rtw89_h2c_tx(rtwdev, skb, false);
if (ret) {
@@ -2815,12 +3300,13 @@ void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev)
static void rtw89_fw_c2h_parse_attr(struct sk_buff *c2h)
{
+ const struct rtw89_c2h_hdr *hdr = (const struct rtw89_c2h_hdr *)c2h->data;
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);
+ attr->category = le32_get_bits(hdr->w0, RTW89_C2H_HDR_W0_CATEGORY);
+ attr->class = le32_get_bits(hdr->w0, RTW89_C2H_HDR_W0_CLASS);
+ attr->func = le32_get_bits(hdr->w0, RTW89_C2H_HDR_W0_FUNC);
+ attr->len = le32_get_bits(hdr->w1, RTW89_C2H_HDR_W1_LEN);
}
static bool rtw89_fw_c2h_chk_atomic(struct rtw89_dev *rtwdev,
@@ -3377,6 +3863,7 @@ void rtw89_hw_scan_start(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
struct ieee80211_scan_request *scan_req)
{
struct rtw89_vif *rtwvif = (struct rtw89_vif *)vif->drv_priv;
+ const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
struct cfg80211_scan_request *req = &scan_req->req;
u32 rx_fltr = rtwdev->hal.rx_fltr;
u8 mac_addr[ETH_ALEN];
@@ -3399,7 +3886,7 @@ void rtw89_hw_scan_start(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
rx_fltr &= ~B_AX_A_BC;
rx_fltr &= ~B_AX_A_A1_MATCH;
rtw89_write32_mask(rtwdev,
- rtw89_mac_reg_by_idx(R_AX_RX_FLTR_OPT, RTW89_MAC_0),
+ rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, RTW89_MAC_0),
B_AX_RX_FLTR_CFG_MASK,
rx_fltr);
}
@@ -3407,6 +3894,7 @@ void rtw89_hw_scan_start(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
void rtw89_hw_scan_complete(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
bool aborted)
{
+ const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info;
struct cfg80211_scan_info info = {
.aborted = aborted,
@@ -3417,7 +3905,7 @@ void rtw89_hw_scan_complete(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif,
return;
rtw89_write32_mask(rtwdev,
- rtw89_mac_reg_by_idx(R_AX_RX_FLTR_OPT, RTW89_MAC_0),
+ rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, RTW89_MAC_0),
B_AX_RX_FLTR_CFG_MASK,
rtwdev->hal.rx_fltr);