cregit-Linux how code gets into the kernel

Release 4.11 drivers/net/wireless/ath/wil6210/fw_inc.c

/*
 * Copyright (c) 2014-2017 Qualcomm Atheros, Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/* Algorithmic part of the firmware download.
 * To be included in the container file providing framework
 */


#define wil_err_fw(wil, fmt, arg...) wil_err(wil, "ERR[ FW ]" fmt, ##arg)

#define wil_dbg_fw(wil, fmt, arg...) wil_dbg(wil, "DBG[ FW ]" fmt, ##arg)

#define wil_hex_dump_fw(prefix_str, prefix_type, rowsize,               \
			groupsize, buf, len, ascii)			\
			print_hex_dump_debug("DBG[ FW ]" prefix_str,    \
                                             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)

/**
 * wil_fw_verify - verify firmware file validity
 *
 * perform various checks for the firmware file header.
 * records are not validated.
 *
 * Return file size or negative error
 */

static int wil_fw_verify(struct wil6210_priv *wil, const u8 *data, size_t size) { const struct wil_fw_record_head *hdr = (const void *)data; struct wil_fw_record_file_header fh; const struct wil_fw_record_file_header *fh_; u32 crc; u32 dlen; if (size % 4) { wil_err_fw(wil, "image size not aligned: %zu\n", size); return -EINVAL; } /* have enough data for the file header? */ if (size < sizeof(*hdr) + sizeof(fh)) { wil_err_fw(wil, "file too short: %zu bytes\n", size); return -EINVAL; } /* start with the file header? */ if (le16_to_cpu(hdr->type) != wil_fw_type_file_header) { wil_err_fw(wil, "no file header\n"); return -EINVAL; } /* data_len */ fh_ = (struct wil_fw_record_file_header *)&hdr[1]; dlen = le32_to_cpu(fh_->data_len); if (dlen % 4) { wil_err_fw(wil, "data length not aligned: %lu\n", (ulong)dlen); return -EINVAL; } if (size < dlen) { wil_err_fw(wil, "file truncated at %zu/%lu\n", size, (ulong)dlen); return -EINVAL; } if (dlen < sizeof(*hdr) + sizeof(fh)) { wil_err_fw(wil, "data length too short: %lu\n", (ulong)dlen); return -EINVAL; } /* signature */ if (le32_to_cpu(fh_->signature) != WIL_FW_SIGNATURE) { wil_err_fw(wil, "bad header signature: 0x%08x\n", le32_to_cpu(fh_->signature)); return -EINVAL; } /* version */ if (le32_to_cpu(fh_->version) > WIL_FW_FMT_VERSION) { wil_err_fw(wil, "unsupported header version: %d\n", le32_to_cpu(fh_->version)); return -EINVAL; } /* checksum. ~crc32(~0, data, size) when fh.crc set to 0*/ fh = *fh_; fh.crc = 0; crc = crc32_le(~0, (unsigned char const *)hdr, sizeof(*hdr)); crc = crc32_le(crc, (unsigned char const *)&fh, sizeof(fh)); crc = crc32_le(crc, (unsigned char const *)&fh_[1], dlen - sizeof(*hdr) - sizeof(fh)); crc = ~crc; if (crc != le32_to_cpu(fh_->crc)) { wil_err_fw(wil, "checksum mismatch:" " calculated for %lu bytes 0x%08x != 0x%08x\n", (ulong)dlen, crc, le32_to_cpu(fh_->crc)); return -EINVAL; } return (int)dlen; }

Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev432100.00%1100.00%
Total432100.00%1100.00%


static int fw_ignore_section(struct wil6210_priv *wil, const void *data, size_t size) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lior David22100.00%1100.00%
Total22100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev39100.00%1100.00%
Total39100.00%1100.00%


static int fw_handle_capabilities(struct wil6210_priv *wil, const void *data, size_t size) { 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) return 0; capa_size = size - offsetof(struct wil_fw_record_capabilities, capabilities); bitmap_zero(wil->fw_capabilities, WMI_FW_CAPABILITY_MAX); memcpy(wil->fw_capabilities, rec->capabilities, min(sizeof(wil->fw_capabilities), capa_size)); wil_hex_dump_fw("CAPA", DUMP_PREFIX_OFFSET, 16, 1, rec->capabilities, capa_size, false); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Lior David118100.00%1100.00%
Total118100.00%1100.00%


static int fw_handle_data(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_data *d = data; void __iomem *dst; size_t s = size - sizeof(*d); if (size < sizeof(*d) + sizeof(u32)) { wil_err_fw(wil, "data record too short: %zu\n", size); 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); wil_memcpy_toio_32(dst, d->data, s); wmb(); /* finish before processing next record */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev118100.00%1100.00%
Total118100.00%1100.00%


static int fw_handle_fill(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_fill *d = data; void __iomem *dst; u32 v; size_t s = (size_t)le32_to_cpu(d->size); if (size != sizeof(*d)) { wil_err_fw(wil, "bad size for fill record: %zu\n", size); return -EINVAL; } if (s < sizeof(u32)) { wil_err_fw(wil, "fill size too short: %zu\n", s); return -EINVAL; } if (s % sizeof(u32)) { wil_err_fw(wil, "fill size not aligned: %zu\n", s); return -EINVAL; } FW_ADDR_CHECK(dst, d->addr, "address"); v = le32_to_cpu(d->value); wil_dbg_fw(wil, "fill [0x%08x] <== 0x%08x, %zu bytes\n", le32_to_cpu(d->addr), v, s); wil_memset_toio_32(dst, v, s); wmb(); /* finish before processing next record */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev175100.00%1100.00%
Total175100.00%1100.00%


static int fw_handle_file_header(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_file_header *d = data; if (size != sizeof(*d)) { wil_err_fw(wil, "file header length incorrect: %zu\n", size); return -EINVAL; } wil_dbg_fw(wil, "new file, ver. %d, %i bytes\n", d->version, d->data_len); wil_hex_dump_fw("", DUMP_PREFIX_OFFSET, 16, 1, d->comment, sizeof(d->comment), true); if (!memcmp(d->comment, WIL_FW_VERSION_PREFIX, WIL_FW_VERSION_PREFIX_LEN)) memcpy(wil->fw_version, d->comment + WIL_FW_VERSION_PREFIX_LEN, min(sizeof(d->comment) - WIL_FW_VERSION_PREFIX_LEN, sizeof(wil->fw_version) - 1)); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev9466.20%150.00%
Lior David4833.80%150.00%
Total142100.00%2100.00%


static int fw_handle_direct_write(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_direct_write *d = data; const struct wil_fw_data_dwrite *block = d->data; int n, i; 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; FW_ADDR_CHECK(dst, block[i].addr, "address"); 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 */ } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev210100.00%2100.00%
Total210100.00%2100.00%


static int gw_write(struct wil6210_priv *wil, void __iomem *gwa_addr, void __iomem *gwa_cmd, void __iomem *gwa_ctl, u32 gw_cmd, u32 a) { unsigned delay = 0; writel(a, gwa_addr); writel(gw_cmd, gwa_cmd); wmb(); /* finish before activate gw */ writel(WIL_FW_GW_CTL_RUN, gwa_ctl); /* activate gw */ do { udelay(1); /* typical time is few usec */ if (delay++ > 100) { wil_err_fw(wil, "gw timeout\n"); return -EINVAL; } } while (readl(gwa_ctl) & WIL_FW_GW_CTL_BUSY); /* gw done? */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev106100.00%2100.00%
Total106100.00%2100.00%


static int fw_handle_gateway_data(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_gateway_data *d = data; const struct wil_fw_data_gw *block = d->data; void __iomem *gwa_addr; void __iomem *gwa_val; void __iomem *gwa_cmd; void __iomem *gwa_ctl; u32 gw_cmd; int n, i; if (size < sizeof(*d) + sizeof(*block)) { wil_err_fw(wil, "gateway record too short: %zu\n", size); return -EINVAL; } if ((size - sizeof(*d)) % sizeof(*block)) { wil_err_fw(wil, "gateway record data size" " not aligned on %zu: %zu\n", sizeof(*block), size - sizeof(*d)); return -EINVAL; } n = (size - sizeof(*d)) / sizeof(*block); gw_cmd = le32_to_cpu(d->command); 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"); wil_dbg_fw(wil, "gw addresses: addr 0x%08x val 0x%08x" " cmd 0x%08x ctl 0x%08x\n", le32_to_cpu(d->gateway_addr_addr), le32_to_cpu(d->gateway_value_addr), le32_to_cpu(d->gateway_cmd_addr), le32_to_cpu(d->gateway_ctrl_address)); for (i = 0; i < n; i++) { int rc; u32 a = le32_to_cpu(block[i].addr); u32 v = le32_to_cpu(block[i].value); wil_dbg_fw(wil, " gw write[%3d] [0x%08x] <== 0x%08x\n", i, a, v); writel(v, gwa_val); rc = gw_write(wil, gwa_addr, gwa_cmd, gwa_ctl, gw_cmd, a); if (rc) return rc; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev351100.00%2100.00%
Total351100.00%2100.00%


static int fw_handle_gateway_data4(struct wil6210_priv *wil, const void *data, size_t size) { const struct wil_fw_record_gateway_data4 *d = data; const struct wil_fw_data_gw4 *block = d->data; void __iomem *gwa_addr; void __iomem *gwa_val[ARRAY_SIZE(block->value)]; void __iomem *gwa_cmd; void __iomem *gwa_ctl; u32 gw_cmd; int n, i, k; if (size < sizeof(*d) + sizeof(*block)) { wil_err_fw(wil, "gateway4 record too short: %zu\n", size); return -EINVAL; } if ((size - sizeof(*d)) % sizeof(*block)) { wil_err_fw(wil, "gateway4 record data size" " not aligned on %zu: %zu\n", sizeof(*block), size - sizeof(*d)); return -EINVAL; } n = (size - sizeof(*d)) / sizeof(*block); gw_cmd = le32_to_cpu(d->command); 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"); 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"); wil_dbg_fw(wil, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n", le32_to_cpu(d->gateway_addr_addr), le32_to_cpu(d->gateway_cmd_addr), le32_to_cpu(d->gateway_ctrl_address)); wil_hex_dump_fw("val addresses: ", DUMP_PREFIX_NONE, 16, 4, d->gateway_value_addr, sizeof(d->gateway_value_addr), false); for (i = 0; i < n; i++) { int rc; u32 a = le32_to_cpu(block[i].addr); u32 v[ARRAY_SIZE(block->value)]; for (k = 0; k < ARRAY_SIZE(block->value); k++) v[k] = le32_to_cpu(block[i].value[k]); wil_dbg_fw(wil, " gw4 write[%3d] [0x%08x] <==\n", i, a); wil_hex_dump_fw(" val ", DUMP_PREFIX_NONE, 16, 4, v, sizeof(v), false); for (k = 0; k < ARRAY_SIZE(block->value); k++) writel(v[k], gwa_val[k]); rc = gw_write(wil, gwa_addr, gwa_cmd, gwa_ctl, gw_cmd, a); if (rc) return rc; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev477100.00%2100.00%
Total477100.00%2100.00%

static const struct { int type; int (*load_handler)(struct wil6210_priv *wil, const void *data, size_t size); 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_data, fw_handle_data, fw_ignore_section}, {wil_fw_type_fill, fw_handle_fill, fw_ignore_section}, /* wil_fw_type_action */ /* wil_fw_type_verify */ {wil_fw_type_file_header, fw_handle_file_header, fw_handle_file_header}, {wil_fw_type_direct_write, fw_handle_direct_write, fw_ignore_section}, {wil_fw_type_gateway_data, fw_handle_gateway_data, fw_ignore_section}, {wil_fw_type_gateway_data4, fw_handle_gateway_data4, fw_ignore_section}, };
static int wil_fw_handle_record(struct wil6210_priv *wil, int type, const void *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; }

Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev7979.80%150.00%
Lior David2020.20%150.00%
Total99100.00%2100.00%

/** * 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 */
static int wil_fw_process(struct wil6210_priv *wil, const void *data, size_t size, bool load) { int rc = 0; const struct wil_fw_record_head *hdr; size_t s, hdr_sz; for (hdr = data;; hdr = (const void *)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)((const void *)hdr - data), le16_to_cpu(hdr->type), hdr_sz); } return -EINVAL; } return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev21097.22%266.67%
Lior David62.78%133.33%
Total216100.00%3100.00%

/** * 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, const char *name, bool load) { int rc, rc1; const struct firmware *fw; size_t sz; const void *d; rc = request_firmware(&fw, name, wil_to_dev(wil)); if (rc) { wil_err_fw(wil, "Failed to load firmware %s\n", name); return rc; } wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size); for (sz = fw->size, d = fw->data; sz; sz -= rc1, d += rc1) { rc1 = wil_fw_verify(wil, d, sz); if (rc1 < 0) { rc = rc1; goto out; } rc = wil_fw_process(wil, d, rc1, load); if (rc < 0) goto out; } out: release_firmware(fw); return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev16196.41%266.67%
Lior David63.59%133.33%
Total167100.00%3100.00%

/** * wil_fw_verify_file_exists - checks if firmware file exist * * @wil: driver context * @name: firmware file name * * return value - boolean, true for success, false for failure */
bool wil_fw_verify_file_exists(struct wil6210_priv *wil, const char *name) { const struct firmware *fw; int rc; rc = request_firmware(&fw, name, wil_to_dev(wil)); if (!rc) release_firmware(fw); return rc != -ENOENT; }

Contributors

PersonTokensPropCommitsCommitProp
Lazar Alexei55100.00%1100.00%
Total55100.00%1100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Vladimir Kondratiev258589.17%457.14%
Lior David2578.87%228.57%
Lazar Alexei571.97%114.29%
Total2899100.00%7100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.