diff options
Diffstat (limited to 'drivers/net/wireless/ath/wil6210/fw_inc.c')
-rw-r--r-- | drivers/net/wireless/ath/wil6210/fw_inc.c | 234 |
1 files changed, 198 insertions, 36 deletions
diff --git a/drivers/net/wireless/ath/wil6210/fw_inc.c b/drivers/net/wireless/ath/wil6210/fw_inc.c index e01acac88825..914c0106e94b 100644 --- a/drivers/net/wireless/ath/wil6210/fw_inc.c +++ b/drivers/net/wireless/ath/wil6210/fw_inc.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2014-2017 Qualcomm Atheros, Inc. + * Copyright (c) 2018, The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -26,14 +27,17 @@ prefix_type, rowsize, \ groupsize, buf, len, ascii) -#define FW_ADDR_CHECK(ioaddr, val, msg) do { \ - ioaddr = wmi_buffer(wil, val); \ - if (!ioaddr) { \ - wil_err_fw(wil, "bad " msg ": 0x%08x\n", \ - le32_to_cpu(val)); \ - return -EINVAL; \ - } \ - } while (0) +static bool wil_fw_addr_check(struct wil6210_priv *wil, + void __iomem **ioaddr, __le32 val, + u32 size, const char *msg) +{ + *ioaddr = wmi_buffer_block(wil, val, size); + if (!(*ioaddr)) { + wil_err_fw(wil, "bad %s: 0x%08x\n", msg, le32_to_cpu(val)); + return false; + } + return true; +} /** * wil_fw_verify - verify firmware file validity @@ -124,14 +128,6 @@ static int fw_ignore_section(struct wil6210_priv *wil, const void *data, return 0; } -static int fw_handle_comment(struct wil6210_priv *wil, const void *data, - size_t size) -{ - wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, data, size, true); - - return 0; -} - static int fw_handle_capabilities(struct wil6210_priv *wil, const void *data, size_t size) @@ -139,9 +135,11 @@ fw_handle_capabilities(struct wil6210_priv *wil, const void *data, const struct wil_fw_record_capabilities *rec = data; size_t capa_size; - if (size < sizeof(*rec) || - le32_to_cpu(rec->magic) != WIL_FW_CAPABILITIES_MAGIC) + if (size < sizeof(*rec)) { + wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, + data, size, true); return 0; + } capa_size = size - offsetof(struct wil_fw_record_capabilities, capabilities); @@ -153,8 +151,56 @@ fw_handle_capabilities(struct wil6210_priv *wil, const void *data, return 0; } -static int fw_handle_data(struct wil6210_priv *wil, const void *data, - size_t size) +static int +fw_handle_brd_file(struct wil6210_priv *wil, const void *data, + size_t size) +{ + const struct wil_fw_record_brd_file *rec = data; + + if (size < sizeof(*rec)) { + wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, + data, size, true); + return 0; + } + + wil->brd_file_addr = le32_to_cpu(rec->base_addr); + wil->brd_file_max_size = le32_to_cpu(rec->max_size_bytes); + + wil_dbg_fw(wil, "brd_file_addr 0x%x, brd_file_max_size %d\n", + wil->brd_file_addr, wil->brd_file_max_size); + + return 0; +} + +static int +fw_handle_comment(struct wil6210_priv *wil, const void *data, + size_t size) +{ + const struct wil_fw_record_comment_hdr *hdr = data; + u32 magic; + int rc = 0; + + if (size < sizeof(*hdr)) + return 0; + + magic = le32_to_cpu(hdr->magic); + + switch (magic) { + case WIL_FW_CAPABILITIES_MAGIC: + wil_dbg_fw(wil, "magic is WIL_FW_CAPABILITIES_MAGIC\n"); + rc = fw_handle_capabilities(wil, data, size); + break; + case WIL_BRD_FILE_MAGIC: + wil_dbg_fw(wil, "magic is WIL_BRD_FILE_MAGIC\n"); + rc = fw_handle_brd_file(wil, data, size); + break; + } + + return rc; +} + +static int __fw_handle_data(struct wil6210_priv *wil, const void *data, + size_t size, __le32 addr) { const struct wil_fw_record_data *d = data; void __iomem *dst; @@ -165,15 +211,23 @@ static int fw_handle_data(struct wil6210_priv *wil, const void *data, return -EINVAL; } - FW_ADDR_CHECK(dst, d->addr, "address"); - wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d->addr), - s); + if (!wil_fw_addr_check(wil, &dst, addr, s, "address")) + return -EINVAL; + wil_dbg_fw(wil, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(addr), s); wil_memcpy_toio_32(dst, d->data, s); wmb(); /* finish before processing next record */ return 0; } +static int fw_handle_data(struct wil6210_priv *wil, const void *data, + size_t size) +{ + const struct wil_fw_record_data *d = data; + + return __fw_handle_data(wil, data, size, d->addr); +} + static int fw_handle_fill(struct wil6210_priv *wil, const void *data, size_t size) { @@ -197,7 +251,8 @@ static int fw_handle_fill(struct wil6210_priv *wil, const void *data, return -EINVAL; } - FW_ADDR_CHECK(dst, d->addr, "address"); + if (!wil_fw_addr_check(wil, &dst, d->addr, s, "address")) + return -EINVAL; v = le32_to_cpu(d->value); wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n", @@ -253,7 +308,8 @@ static int fw_handle_direct_write(struct wil6210_priv *wil, const void *data, u32 v = le32_to_cpu(block[i].value); u32 x, y; - FW_ADDR_CHECK(dst, block[i].addr, "address"); + if (!wil_fw_addr_check(wil, &dst, block[i].addr, 0, "address")) + return -EINVAL; x = readl(dst); y = (x & m) | (v & ~m); @@ -319,10 +375,15 @@ static int fw_handle_gateway_data(struct wil6210_priv *wil, const void *data, wil_dbg_fw(wil, "gw write record [%3d] blocks, cmd 0x%08x\n", n, gw_cmd); - FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr"); - FW_ADDR_CHECK(gwa_val, d->gateway_value_addr, "gateway_value_addr"); - FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr"); - FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address"); + if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0, + "gateway_addr_addr") || + !wil_fw_addr_check(wil, &gwa_val, d->gateway_value_addr, 0, + "gateway_value_addr") || + !wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0, + "gateway_cmd_addr") || + !wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0, + "gateway_ctrl_address")) + return -EINVAL; wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x" " cmd 0x%08x ctl 0x%08x\n", @@ -378,12 +439,19 @@ static int fw_handle_gateway_data4(struct wil6210_priv *wil, const void *data, wil_dbg_fw(wil, "gw4 write record [%3d] blocks, cmd 0x%08x\n", n, gw_cmd); - FW_ADDR_CHECK(gwa_addr, d->gateway_addr_addr, "gateway_addr_addr"); + if (!wil_fw_addr_check(wil, &gwa_addr, d->gateway_addr_addr, 0, + "gateway_addr_addr")) + return -EINVAL; for (k = 0; k < ARRAY_SIZE(block->value); k++) - FW_ADDR_CHECK(gwa_val[k], d->gateway_value_addr[k], - "gateway_value_addr"); - FW_ADDR_CHECK(gwa_cmd, d->gateway_cmd_addr, "gateway_cmd_addr"); - FW_ADDR_CHECK(gwa_ctl, d->gateway_ctrl_address, "gateway_ctrl_address"); + if (!wil_fw_addr_check(wil, &gwa_val[k], + d->gateway_value_addr[k], + 0, "gateway_value_addr")) + return -EINVAL; + if (!wil_fw_addr_check(wil, &gwa_cmd, d->gateway_cmd_addr, 0, + "gateway_cmd_addr") || + !wil_fw_addr_check(wil, &gwa_ctl, d->gateway_ctrl_address, 0, + "gateway_ctrl_address")) + return -EINVAL; wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n", le32_to_cpu(d->gateway_addr_addr), @@ -422,7 +490,7 @@ static const struct { int (*parse_handler)(struct wil6210_priv *wil, const void *data, size_t size); } wil_fw_handlers[] = { - {wil_fw_type_comment, fw_handle_comment, fw_handle_capabilities}, + {wil_fw_type_comment, fw_handle_comment, fw_handle_comment}, {wil_fw_type_data, fw_handle_data, fw_ignore_section}, {wil_fw_type_fill, fw_handle_fill, fw_ignore_section}, /* wil_fw_type_action */ @@ -517,7 +585,7 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name, rc = request_firmware(&fw, name, wil_to_dev(wil)); if (rc) { - wil_err_fw(wil, "Failed to load firmware %s\n", name); + wil_err_fw(wil, "Failed to load firmware %s rc %d\n", name, rc); return rc; } wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size); @@ -539,6 +607,100 @@ out: } /** + * wil_brd_process - process section from BRD file + * + * Return error code + */ +static int wil_brd_process(struct wil6210_priv *wil, const void *data, + size_t size) +{ + int rc = 0; + const struct wil_fw_record_head *hdr = data; + size_t s, hdr_sz; + u16 type; + + /* Assuming the board file includes only one header record and one data + * record. 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 record */ + hdr = (const void *)hdr + s; + size -= s; + if (size < sizeof(*hdr)) + return -EINVAL; + hdr_sz = le32_to_cpu(hdr->size); + + if (wil->brd_file_max_size && hdr_sz > wil->brd_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 addr from fw file: [0x%08x]\n", + wil->brd_file_addr); + + rc = __fw_handle_data(wil, &hdr[1], hdr_sz, + cpu_to_le32(wil->brd_file_addr)); + + return rc; +} + +/** + * 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, const char *name) +{ + int rc, dlen; + const struct firmware *brd; + + rc = request_firmware(&brd, name, wil_to_dev(wil)); + if (rc) { + wil_err_fw(wil, "Failed to load brd %s\n", name); + return rc; + } + wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, brd->size); + + /* Verify the header */ + dlen = wil_fw_verify(wil, brd->data, brd->size); + if (dlen < 0) { + rc = dlen; + goto out; + } + /* Process the data record */ + rc = wil_brd_process(wil, brd->data, dlen); + +out: + release_firmware(brd); + return rc; +} + +/** * wil_fw_verify_file_exists - checks if firmware file exist * * @wil: driver context |