|
@@ -541,11 +541,18 @@ static int ath10k_download_fw(struct ath10k *ar, enum ath10k_firmware_mode mode)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-static void ath10k_core_free_firmware_files(struct ath10k *ar)
|
|
|
+static void ath10k_core_free_board_files(struct ath10k *ar)
|
|
|
{
|
|
|
if (!IS_ERR(ar->board))
|
|
|
release_firmware(ar->board);
|
|
|
|
|
|
+ ar->board = NULL;
|
|
|
+ ar->board_data = NULL;
|
|
|
+ ar->board_len = 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void ath10k_core_free_firmware_files(struct ath10k *ar)
|
|
|
+{
|
|
|
if (!IS_ERR(ar->otp))
|
|
|
release_firmware(ar->otp);
|
|
|
|
|
@@ -557,10 +564,6 @@ static void ath10k_core_free_firmware_files(struct ath10k *ar)
|
|
|
|
|
|
ath10k_swap_code_seg_release(ar);
|
|
|
|
|
|
- ar->board = NULL;
|
|
|
- ar->board_data = NULL;
|
|
|
- ar->board_len = 0;
|
|
|
-
|
|
|
ar->otp = NULL;
|
|
|
ar->otp_data = NULL;
|
|
|
ar->otp_len = 0;
|
|
@@ -591,68 +594,241 @@ static int ath10k_fetch_cal_file(struct ath10k *ar)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int ath10k_core_fetch_spec_board_file(struct ath10k *ar)
|
|
|
+static int ath10k_core_fetch_board_data_api_1(struct ath10k *ar)
|
|
|
{
|
|
|
- char filename[100];
|
|
|
-
|
|
|
- scnprintf(filename, sizeof(filename), "board-%s-%s.bin",
|
|
|
- ath10k_bus_str(ar->hif.bus), ar->spec_board_id);
|
|
|
+ if (!ar->hw_params.fw.board) {
|
|
|
+ ath10k_err(ar, "failed to find board file fw entry\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
- ar->board = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, filename);
|
|
|
+ ar->board = ath10k_fetch_fw_file(ar,
|
|
|
+ ar->hw_params.fw.dir,
|
|
|
+ ar->hw_params.fw.board);
|
|
|
if (IS_ERR(ar->board))
|
|
|
return PTR_ERR(ar->board);
|
|
|
|
|
|
ar->board_data = ar->board->data;
|
|
|
ar->board_len = ar->board->size;
|
|
|
- ar->spec_board_loaded = true;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int ath10k_core_fetch_generic_board_file(struct ath10k *ar)
|
|
|
+static int ath10k_core_parse_bd_ie_board(struct ath10k *ar,
|
|
|
+ const void *buf, size_t buf_len,
|
|
|
+ const char *boardname)
|
|
|
{
|
|
|
- if (!ar->hw_params.fw.board) {
|
|
|
- ath10k_err(ar, "failed to find board file fw entry\n");
|
|
|
- return -EINVAL;
|
|
|
+ const struct ath10k_fw_ie *hdr;
|
|
|
+ bool name_match_found;
|
|
|
+ int ret, board_ie_id;
|
|
|
+ size_t board_ie_len;
|
|
|
+ const void *board_ie_data;
|
|
|
+
|
|
|
+ name_match_found = false;
|
|
|
+
|
|
|
+ /* go through ATH10K_BD_IE_BOARD_ elements */
|
|
|
+ while (buf_len > sizeof(struct ath10k_fw_ie)) {
|
|
|
+ hdr = buf;
|
|
|
+ board_ie_id = le32_to_cpu(hdr->id);
|
|
|
+ board_ie_len = le32_to_cpu(hdr->len);
|
|
|
+ board_ie_data = hdr->data;
|
|
|
+
|
|
|
+ buf_len -= sizeof(*hdr);
|
|
|
+ buf += sizeof(*hdr);
|
|
|
+
|
|
|
+ if (buf_len < ALIGN(board_ie_len, 4)) {
|
|
|
+ ath10k_err(ar, "invalid ATH10K_BD_IE_BOARD length: %zu < %zu\n",
|
|
|
+ buf_len, ALIGN(board_ie_len, 4));
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (board_ie_id) {
|
|
|
+ case ATH10K_BD_IE_BOARD_NAME:
|
|
|
+ ath10k_dbg_dump(ar, ATH10K_DBG_BOOT, "board name", "",
|
|
|
+ board_ie_data, board_ie_len);
|
|
|
+
|
|
|
+ if (board_ie_len != strlen(boardname))
|
|
|
+ break;
|
|
|
+
|
|
|
+ ret = memcmp(board_ie_data, boardname, strlen(boardname));
|
|
|
+ if (ret)
|
|
|
+ break;
|
|
|
+
|
|
|
+ name_match_found = true;
|
|
|
+ ath10k_dbg(ar, ATH10K_DBG_BOOT,
|
|
|
+ "boot found match for name '%s'",
|
|
|
+ boardname);
|
|
|
+ break;
|
|
|
+ case ATH10K_BD_IE_BOARD_DATA:
|
|
|
+ if (!name_match_found)
|
|
|
+ /* no match found */
|
|
|
+ break;
|
|
|
+
|
|
|
+ ath10k_dbg(ar, ATH10K_DBG_BOOT,
|
|
|
+ "boot found board data for '%s'",
|
|
|
+ boardname);
|
|
|
+
|
|
|
+ ar->board_data = board_ie_data;
|
|
|
+ ar->board_len = board_ie_len;
|
|
|
+
|
|
|
+ ret = 0;
|
|
|
+ goto out;
|
|
|
+ default:
|
|
|
+ ath10k_warn(ar, "unknown ATH10K_BD_IE_BOARD found: %d\n",
|
|
|
+ board_ie_id);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* jump over the padding */
|
|
|
+ board_ie_len = ALIGN(board_ie_len, 4);
|
|
|
+
|
|
|
+ buf_len -= board_ie_len;
|
|
|
+ buf += board_ie_len;
|
|
|
}
|
|
|
|
|
|
- ar->board = ath10k_fetch_fw_file(ar,
|
|
|
- ar->hw_params.fw.dir,
|
|
|
- ar->hw_params.fw.board);
|
|
|
+ /* no match found */
|
|
|
+ ret = -ENOENT;
|
|
|
+
|
|
|
+out:
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int ath10k_core_fetch_board_data_api_n(struct ath10k *ar,
|
|
|
+ const char *boardname,
|
|
|
+ const char *filename)
|
|
|
+{
|
|
|
+ size_t len, magic_len, ie_len;
|
|
|
+ struct ath10k_fw_ie *hdr;
|
|
|
+ const u8 *data;
|
|
|
+ int ret, ie_id;
|
|
|
+
|
|
|
+ ar->board = ath10k_fetch_fw_file(ar, ar->hw_params.fw.dir, filename);
|
|
|
if (IS_ERR(ar->board))
|
|
|
return PTR_ERR(ar->board);
|
|
|
|
|
|
- ar->board_data = ar->board->data;
|
|
|
- ar->board_len = ar->board->size;
|
|
|
- ar->spec_board_loaded = false;
|
|
|
+ data = ar->board->data;
|
|
|
+ len = ar->board->size;
|
|
|
+
|
|
|
+ /* magic has extra null byte padded */
|
|
|
+ magic_len = strlen(ATH10K_BOARD_MAGIC) + 1;
|
|
|
+ if (len < magic_len) {
|
|
|
+ ath10k_err(ar, "failed to find magic value in %s/%s, file too short: %zu\n",
|
|
|
+ ar->hw_params.fw.dir, filename, len);
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (memcmp(data, ATH10K_BOARD_MAGIC, magic_len)) {
|
|
|
+ ath10k_err(ar, "found invalid board magic\n");
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* magic is padded to 4 bytes */
|
|
|
+ magic_len = ALIGN(magic_len, 4);
|
|
|
+ if (len < magic_len) {
|
|
|
+ ath10k_err(ar, "failed: %s/%s too small to contain board data, len: %zu\n",
|
|
|
+ ar->hw_params.fw.dir, filename, len);
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ data += magic_len;
|
|
|
+ len -= magic_len;
|
|
|
+
|
|
|
+ while (len > sizeof(struct ath10k_fw_ie)) {
|
|
|
+ hdr = (struct ath10k_fw_ie *)data;
|
|
|
+ ie_id = le32_to_cpu(hdr->id);
|
|
|
+ ie_len = le32_to_cpu(hdr->len);
|
|
|
+
|
|
|
+ len -= sizeof(*hdr);
|
|
|
+ data = hdr->data;
|
|
|
+
|
|
|
+ if (len < ALIGN(ie_len, 4)) {
|
|
|
+ ath10k_err(ar, "invalid length for board ie_id %d ie_len %zu len %zu\n",
|
|
|
+ ie_id, ie_len, len);
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ switch (ie_id) {
|
|
|
+ case ATH10K_BD_IE_BOARD:
|
|
|
+ ret = ath10k_core_parse_bd_ie_board(ar, data, ie_len,
|
|
|
+ boardname);
|
|
|
+ if (ret == -ENOENT)
|
|
|
+ /* no match found, continue */
|
|
|
+ break;
|
|
|
+ else if (ret)
|
|
|
+ /* there was an error, bail out */
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ /* board data found */
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* jump over the padding */
|
|
|
+ ie_len = ALIGN(ie_len, 4);
|
|
|
+
|
|
|
+ len -= ie_len;
|
|
|
+ data += ie_len;
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ if (!ar->board_data || !ar->board_len) {
|
|
|
+ ath10k_err(ar,
|
|
|
+ "failed to fetch board data for %s from %s/%s\n",
|
|
|
+ ar->hw_params.fw.dir, boardname, filename);
|
|
|
+ ret = -ENODATA;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err:
|
|
|
+ ath10k_core_free_board_files(ar);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+static int ath10k_core_create_board_name(struct ath10k *ar, char *name,
|
|
|
+ size_t name_len)
|
|
|
+{
|
|
|
+ scnprintf(name, name_len,
|
|
|
+ "bus=%s,vendor=%04x,device=%04x,subsystem-vendor=%04x,subsystem-device=%04x",
|
|
|
+ ath10k_bus_str(ar->hif.bus),
|
|
|
+ ar->id.vendor, ar->id.device,
|
|
|
+ ar->id.subsystem_vendor, ar->id.subsystem_device);
|
|
|
+
|
|
|
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot using board name '%s'\n", name);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static int ath10k_core_fetch_board_file(struct ath10k *ar)
|
|
|
{
|
|
|
+ char boardname[100];
|
|
|
int ret;
|
|
|
|
|
|
- if (strlen(ar->spec_board_id) > 0) {
|
|
|
- ret = ath10k_core_fetch_spec_board_file(ar);
|
|
|
- if (ret) {
|
|
|
- ath10k_info(ar, "failed to load spec board file, falling back to generic: %d\n",
|
|
|
- ret);
|
|
|
- goto generic;
|
|
|
- }
|
|
|
-
|
|
|
- ath10k_dbg(ar, ATH10K_DBG_BOOT, "found specific board file for %s\n",
|
|
|
- ar->spec_board_id);
|
|
|
- return 0;
|
|
|
+ ret = ath10k_core_create_board_name(ar, boardname, sizeof(boardname));
|
|
|
+ if (ret) {
|
|
|
+ ath10k_err(ar, "failed to create board name: %d", ret);
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
-generic:
|
|
|
- ret = ath10k_core_fetch_generic_board_file(ar);
|
|
|
+ ar->bd_api = 2;
|
|
|
+ ret = ath10k_core_fetch_board_data_api_n(ar, boardname,
|
|
|
+ ATH10K_BOARD_API2_FILE);
|
|
|
+ if (!ret)
|
|
|
+ goto success;
|
|
|
+
|
|
|
+ ar->bd_api = 1;
|
|
|
+ ret = ath10k_core_fetch_board_data_api_1(ar);
|
|
|
if (ret) {
|
|
|
- ath10k_err(ar, "failed to fetch generic board data: %d\n", ret);
|
|
|
+ ath10k_err(ar, "failed to fetch board data\n");
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+success:
|
|
|
+ ath10k_dbg(ar, ATH10K_DBG_BOOT, "using board api %d\n", ar->bd_api);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1626,6 +1802,7 @@ void ath10k_core_unregister(struct ath10k *ar)
|
|
|
ath10k_testmode_destroy(ar);
|
|
|
|
|
|
ath10k_core_free_firmware_files(ar);
|
|
|
+ ath10k_core_free_board_files(ar);
|
|
|
|
|
|
ath10k_debug_unregister(ar);
|
|
|
}
|