// SPDX-License-Identifier: ISC /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
*/
/* Algorithmic part of the firmware download. * To be included in the container file providing framework
*/
if (!max_num_ent) {
wil_err(wil, "brd info entries are missing\n"); return -EINVAL;
}
wil->brd_info = kcalloc(max_num_ent, sizeof(struct wil_brd_info),
GFP_KERNEL); if (!wil->brd_info) return -ENOMEM;
for (i = 0; i < max_num_ent; i++) {
wil->brd_info[i].file_addr =
le32_to_cpu(rec->brd_info[i].base_addr);
wil->brd_info[i].file_max_size =
le32_to_cpu(rec->brd_info[i].max_size_bytes);
if (!wil->brd_info[i].file_addr) break;
wil_dbg_fw(wil, "brd info %d: file_addr 0x%x, file_max_size %d\n",
i, wil->brd_info[i].file_addr,
wil->brd_info[i].file_max_size);
}
wil->num_of_brd_entries = i; if (wil->num_of_brd_entries == 0) {
kfree(wil->brd_info);
wil->brd_info = NULL;
wil_dbg_fw(wil, "no valid brd info entries, using brd file addr\n");
} else {
wil_dbg_fw(wil, "num of brd info entries %d\n",
wil->num_of_brd_entries);
}
if (size % sizeof(*block)) {
wil_err_fw(wil, "record size not aligned on %zu: %zu\n", sizeof(*block), size); return -EINVAL;
}
n = size / sizeof(*block);
for (i = 0; i < n; i++) { void __iomem *dst;
u32 m = le32_to_cpu(block[i].mask);
u32 v = le32_to_cpu(block[i].value);
u32 x, y;
if (!wil_fw_addr_check(wil, &dst, block[i].addr, 0, "address")) return -EINVAL;
x = readl(dst);
y = (x & m) | (v & ~m);
wil_dbg_fw(wil, "write [0x%08x] <== 0x%08x " "(old 0x%08x val 0x%08x mask 0x%08x)\n",
le32_to_cpu(block[i].addr), y, x, v, m);
writel(y, dst);
wmb(); /* finish before processing next record */
}
staticint wil_fw_handle_record(struct wil6210_priv *wil, int type, constvoid *data, size_t size, bool load)
{ int i;
for (i = 0; i < ARRAY_SIZE(wil_fw_handlers); i++) if (wil_fw_handlers[i].type == type) return load ?
wil_fw_handlers[i].load_handler(
wil, data, size) :
wil_fw_handlers[i].parse_handler(
wil, data, size);
wil_err_fw(wil, "unknown record type: %d\n", type); return -EINVAL;
}
/** * wil_fw_process - process section from FW file * if load is true: Load the FW and uCode code and data to the * corresponding device memory regions, * otherwise only parse and look for capabilities * * Return error code
*/ staticint wil_fw_process(struct wil6210_priv *wil, constvoid *data,
size_t size, bool load)
{ int rc = 0; conststruct wil_fw_record_head *hdr;
size_t s, hdr_sz;
for (hdr = data;; hdr = (constvoid *)hdr + s, size -= s) { if (size < sizeof(*hdr)) break;
hdr_sz = le32_to_cpu(hdr->size);
s = sizeof(*hdr) + hdr_sz; if (s > size) break; if (hdr_sz % 4) {
wil_err_fw(wil, "unaligned record size: %zu\n",
hdr_sz); return -EINVAL;
}
rc = wil_fw_handle_record(wil, le16_to_cpu(hdr->type),
&hdr[1], hdr_sz, load); if (rc) return rc;
} if (size) {
wil_err_fw(wil, "unprocessed bytes: %zu\n", size); if (size >= sizeof(*hdr)) {
wil_err_fw(wil, "Stop at offset %ld" " record type %d [%zd bytes]\n",
(long)((constvoid *)hdr - data),
le16_to_cpu(hdr->type), hdr_sz);
} return -EINVAL;
}
return rc;
}
/** * wil_request_firmware - Request firmware * * Request firmware image from the file * If load is true, load firmware to device, otherwise * only parse and extract capabilities * * Return error code
*/ int wil_request_firmware(struct wil6210_priv *wil, constchar *name, bool load)
{ int rc, rc1; conststruct firmware *fw;
size_t sz; constvoid *d;
/** * wil_brd_process - process section from BRD file * * Return error code
*/ staticint wil_brd_process(struct wil6210_priv *wil, constvoid *data,
size_t size)
{ int rc = 0; conststruct wil_fw_record_head *hdr = data;
size_t s, hdr_sz = 0;
u16 type; int i = 0;
/* Assuming the board file includes only one file header * and one or several data records. * Each record starts with wil_fw_record_head.
*/ if (size < sizeof(*hdr)) return -EINVAL;
s = sizeof(*hdr) + le32_to_cpu(hdr->size); if (s > size) return -EINVAL;
/* Skip the header record and handle the data records */
size -= s;
for (hdr = data + s;; hdr = (constvoid *)hdr + s, size -= s, i++) { if (size < sizeof(*hdr)) break;
if (i >= wil->num_of_brd_entries) {
wil_err_fw(wil, "Too many brd records: %d, num of expected entries %d\n",
i, wil->num_of_brd_entries); break;
}
hdr_sz = le32_to_cpu(hdr->size);
s = sizeof(*hdr) + hdr_sz; if (wil->brd_info[i].file_max_size &&
hdr_sz > wil->brd_info[i].file_max_size) return -EINVAL; if (sizeof(*hdr) + hdr_sz > size) return -EINVAL; if (hdr_sz % 4) {
wil_err_fw(wil, "unaligned record size: %zu\n",
hdr_sz); return -EINVAL;
}
type = le16_to_cpu(hdr->type); if (type != wil_fw_type_data) {
wil_err_fw(wil, "invalid record type for board file: %d\n",
type); return -EINVAL;
} if (hdr_sz < sizeof(struct wil_fw_record_data)) {
wil_err_fw(wil, "data record too short: %zu\n", hdr_sz); return -EINVAL;
}
wil_dbg_fw(wil, "using info from fw file for record %d: addr[0x%08x], max size %d\n",
i, wil->brd_info[i].file_addr,
wil->brd_info[i].file_max_size);
if (size) {
wil_err_fw(wil, "unprocessed bytes: %zu\n", size); if (size >= sizeof(*hdr)) {
wil_err_fw(wil, "Stop at offset %ld record type %d [%zd bytes]\n",
(long)((constvoid *)hdr - data),
le16_to_cpu(hdr->type), hdr_sz);
} return -EINVAL;
}
return 0;
}
/** * wil_request_board - Request board file * * Request board image from the file * board file address and max size are read from FW file * during initialization. * brd file shall include one header and one data section. * * Return error code
*/ int wil_request_board(struct wil6210_priv *wil, constchar *name)
{ int rc, dlen; conststruct firmware *brd;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.