static int iwmct_kick_fw(struct iwmct_priv *priv, bool jump) { struct iwmct_parser *parser = &priv->parser; struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf; int ret; u32 cmd; LOG_TRACE(priv, FW_DOWNLOAD, "-->\n"); memset(parser->buf, 0, parser->buf_size); cmd = IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS; if (jump) { cmd |= IWMC_OPCODE_JUMP << CMD_HDR_OPCODE_POS; hdr->target_addr = cpu_to_le32(parser->entry_point); LOG_INFO(priv, FW_DOWNLOAD, "jump address 0x%x\n", parser->entry_point); } else { cmd |= IWMC_OPCODE_LAST_COMMAND << CMD_HDR_OPCODE_POS; LOG_INFO(priv, FW_DOWNLOAD, "last command\n"); } hdr->cmd = cpu_to_le32(cmd); LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, sizeof(*hdr)); /* send it down */ /* TODO: add more proper sending and error checking */ ret = iwmct_tx(priv, parser->buf, IWMC_SDIO_BLK_SIZE); if (ret) LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d", ret); LOG_TRACE(priv, FW_DOWNLOAD, "<--\n"); return 0; }
int iwmct_send_hcmd(struct iwmct_priv *priv, u8 *cmd, u16 len) { int ret; u8 *buf; LOG_TRACE(priv, FW_MSG, "Sending hcmd:\n"); /* add padding to 256 for IWMC */ ((struct top_msg *)cmd)->hdr.flags |= CMD_FLAG_PADDING_256; LOG_HEXDUMP(FW_MSG, cmd, len); if (len > FW_HCMD_BLOCK_SIZE) { LOG_ERROR(priv, FW_MSG, "size %d exceeded hcmd max size %d\n", len, FW_HCMD_BLOCK_SIZE); return -1; } buf = kzalloc(FW_HCMD_BLOCK_SIZE, GFP_KERNEL); if (!buf) { LOG_ERROR(priv, FW_MSG, "kzalloc error, buf size %d\n", FW_HCMD_BLOCK_SIZE); return -1; } memcpy(buf, cmd, len); ret = iwmct_tx(priv, buf, FW_HCMD_BLOCK_SIZE); kfree(buf); return ret; }
static int iwmct_download_section(struct iwmct_priv *priv, const u8 *p_sec, size_t sec_size, __le32 addr) { struct iwmct_parser *parser = &priv->parser; struct iwmct_fw_load_hdr *hdr = (struct iwmct_fw_load_hdr *)parser->buf; const u8 *cur_block = p_sec; size_t sent = 0; int cnt = 0; int ret = 0; u32 cmd = 0; LOG_TRACE(priv, FW_DOWNLOAD, "-->\n"); LOG_INFO(priv, FW_DOWNLOAD, "Download address 0x%x size 0x%zx\n", addr, sec_size); while (sent < sec_size) { int i; u32 chksm = 0; u32 reset = atomic_read(&priv->reset); /* actual FW data */ u32 data_size = min(parser->buf_size - sizeof(*hdr), sec_size - sent); /* Pad to block size */ u32 trans_size = (data_size + sizeof(*hdr) + IWMC_SDIO_BLK_SIZE - 1) & ~(IWMC_SDIO_BLK_SIZE - 1); ++cnt; /* in case of reset, interrupt FW DOWNLAOD */ if (reset) { LOG_INFO(priv, FW_DOWNLOAD, "Reset detected. Abort FW download!!!"); ret = -ECANCELED; goto exit; } memset(parser->buf, 0, parser->buf_size); cmd |= IWMC_OPCODE_WRITE << CMD_HDR_OPCODE_POS; cmd |= IWMC_CMD_SIGNATURE << CMD_HDR_SIGNATURE_POS; cmd |= (priv->dbg.direct ? 1 : 0) << CMD_HDR_DIRECT_ACCESS_POS; cmd |= (priv->dbg.checksum ? 1 : 0) << CMD_HDR_USE_CHECKSUM_POS; hdr->data_size = cpu_to_le32(data_size); hdr->target_addr = addr; /* checksum is allowed for sizes divisible by 4 */ if (data_size & 0x3) cmd &= ~CMD_HDR_USE_CHECKSUM_MSK; memcpy(hdr->data, cur_block, data_size); if (cmd & CMD_HDR_USE_CHECKSUM_MSK) { chksm = data_size + le32_to_cpu(addr) + cmd; for (i = 0; i < data_size >> 2; i++) chksm += ((u32 *)cur_block)[i]; hdr->block_chksm = cpu_to_le32(chksm); LOG_INFO(priv, FW_DOWNLOAD, "Checksum = 0x%X\n", hdr->block_chksm); } LOG_INFO(priv, FW_DOWNLOAD, "trans#%d, len=%d, sent=%zd, " "sec_size=%zd, startAddress 0x%X\n", cnt, trans_size, sent, sec_size, addr); if (priv->dbg.dump) LOG_HEXDUMP(FW_DOWNLOAD, parser->buf, trans_size); hdr->cmd = cpu_to_le32(cmd); /* send it down */ /* TODO: add more proper sending and error checking */ ret = iwmct_tx(priv, parser->buf, trans_size); if (ret != 0) { LOG_INFO(priv, FW_DOWNLOAD, "iwmct_tx returned %d\n", ret); goto exit; } addr = cpu_to_le32(le32_to_cpu(addr) + data_size); sent += data_size; cur_block = p_sec + sent; if (priv->dbg.blocks && (cnt + 1) >= priv->dbg.blocks) { LOG_INFO(priv, FW_DOWNLOAD, "Block number limit is reached [%d]\n", priv->dbg.blocks); break; } } if (sent < sec_size) ret = -EINVAL; exit: LOG_TRACE(priv, FW_DOWNLOAD, "<--\n"); return ret; }