diff options
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn.c | 291 |
1 files changed, 168 insertions, 123 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 70094bbe485..65ae636eae3 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -1534,6 +1534,91 @@ static int __must_check iwl_request_firmware(struct iwl_priv *priv, bool first) iwl_ucode_callback); } +struct iwlagn_firmware_pieces { + const void *inst, *data, *init, *init_data, *boot; + size_t inst_size, data_size, init_size, init_data_size, boot_size; + + u32 build; +}; + +static int iwlagn_load_legacy_firmware(struct iwl_priv *priv, + const struct firmware *ucode_raw, + struct iwlagn_firmware_pieces *pieces) +{ + struct iwl_ucode_header *ucode = (void *)ucode_raw->data; + u32 api_ver, hdr_size; + const u8 *src; + + priv->ucode_ver = le32_to_cpu(ucode->ver); + api_ver = IWL_UCODE_API(priv->ucode_ver); + + switch (api_ver) { + default: + /* + * 4965 doesn't revision the firmware file format + * along with the API version, it always uses v1 + * file format. + */ + if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != + CSR_HW_REV_TYPE_4965) { + hdr_size = 28; + if (ucode_raw->size < hdr_size) { + IWL_ERR(priv, "File size too small!\n"); + return -EINVAL; + } + pieces->build = le32_to_cpu(ucode->u.v2.build); + pieces->inst_size = le32_to_cpu(ucode->u.v2.inst_size); + pieces->data_size = le32_to_cpu(ucode->u.v2.data_size); + pieces->init_size = le32_to_cpu(ucode->u.v2.init_size); + pieces->init_data_size = le32_to_cpu(ucode->u.v2.init_data_size); + pieces->boot_size = le32_to_cpu(ucode->u.v2.boot_size); + src = ucode->u.v2.data; + break; + } + /* fall through for 4965 */ + case 0: + case 1: + case 2: + hdr_size = 24; + if (ucode_raw->size < hdr_size) { + IWL_ERR(priv, "File size too small!\n"); + return -EINVAL; + } + pieces->build = 0; + pieces->inst_size = le32_to_cpu(ucode->u.v1.inst_size); + pieces->data_size = le32_to_cpu(ucode->u.v1.data_size); + pieces->init_size = le32_to_cpu(ucode->u.v1.init_size); + pieces->init_data_size = le32_to_cpu(ucode->u.v1.init_data_size); + pieces->boot_size = le32_to_cpu(ucode->u.v1.boot_size); + src = ucode->u.v1.data; + break; + } + + /* Verify size of file vs. image size info in file's header */ + if (ucode_raw->size != hdr_size + pieces->inst_size + + pieces->data_size + pieces->init_size + + pieces->init_data_size + pieces->boot_size) { + + IWL_ERR(priv, + "uCode file size %d does not match expected size\n", + (int)ucode_raw->size); + return -EINVAL; + } + + pieces->inst = src; + src += pieces->inst_size; + pieces->data = src; + src += pieces->data_size; + pieces->init = src; + src += pieces->init_size; + pieces->init_data = src; + src += pieces->init_data_size; + pieces->boot = src; + src += pieces->boot_size; + + return 0; +} + /** * iwl_ucode_callback - callback when firmware was loaded * @@ -1544,14 +1629,15 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) { struct iwl_priv *priv = context; struct iwl_ucode_header *ucode; + int err; + struct iwlagn_firmware_pieces pieces; const unsigned int api_max = priv->cfg->ucode_api_max; const unsigned int api_min = priv->cfg->ucode_api_min; - u8 *src; - size_t len; - u32 api_ver, build; - u32 inst_size, data_size, init_size, init_data_size, boot_size; - int err, hdr_size; + u32 api_ver; char buildstr[25]; + u32 build; + + memset(&pieces, 0, sizeof(pieces)); if (!ucode_raw) { IWL_ERR(priv, "request for firmware file '%s' failed.\n", @@ -1571,54 +1657,22 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) /* Data from ucode file: header followed by uCode images */ ucode = (struct iwl_ucode_header *)ucode_raw->data; - priv->ucode_ver = le32_to_cpu(ucode->ver); - api_ver = IWL_UCODE_API(priv->ucode_ver); + if (ucode->ver) + err = iwlagn_load_legacy_firmware(priv, ucode_raw, &pieces); + else + err = -EINVAL; - switch (api_ver) { - default: - /* - * 4965 doesn't revision the firmware file format - * along with the API version, it always uses v1 - * file format. - */ - if (priv->cfg != &iwl4965_agn_cfg) { - hdr_size = 28; - if (ucode_raw->size < hdr_size) { - IWL_ERR(priv, "File size too small!\n"); - goto try_again; - } - build = ucode->u.v2.build; - inst_size = ucode->u.v2.inst_size; - data_size = ucode->u.v2.data_size; - init_size = ucode->u.v2.init_size; - init_data_size = ucode->u.v2.init_data_size; - boot_size = ucode->u.v2.boot_size; - src = ucode->u.v2.data; - break; - } - /* fall through for 4965 */ - case 0: - case 1: - case 2: - hdr_size = 24; - if (ucode_raw->size < hdr_size) { - IWL_ERR(priv, "File size too small!\n"); - goto try_again; - } - build = 0; - inst_size = ucode->u.v1.inst_size; - data_size = ucode->u.v1.data_size; - init_size = ucode->u.v1.init_size; - init_data_size = ucode->u.v1.init_data_size; - boot_size = ucode->u.v1.boot_size; - src = ucode->u.v1.data; - break; - } + if (err) + goto try_again; - /* api_ver should match the api version forming part of the - * firmware filename ... but we don't check for that and only rely - * on the API version read from firmware header from here on forward */ + api_ver = IWL_UCODE_API(priv->ucode_ver); + build = pieces.build; + /* + * api_ver should match the api version forming part of the + * firmware filename ... but we don't check for that and only rely + * on the API version read from firmware header from here on forward + */ if (api_ver < api_min || api_ver > api_max) { IWL_ERR(priv, "Driver unable to support your firmware API. " "Driver supports v%u, firmware is v%u.\n", @@ -1653,75 +1707,69 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) IWL_UCODE_SERIAL(priv->ucode_ver), buildstr); - IWL_DEBUG_INFO(priv, "f/w package hdr ucode version raw = 0x%x\n", - priv->ucode_ver); - IWL_DEBUG_INFO(priv, "f/w package hdr runtime inst size = %u\n", - inst_size); - IWL_DEBUG_INFO(priv, "f/w package hdr runtime data size = %u\n", - data_size); - IWL_DEBUG_INFO(priv, "f/w package hdr init inst size = %u\n", - init_size); - IWL_DEBUG_INFO(priv, "f/w package hdr init data size = %u\n", - init_data_size); - IWL_DEBUG_INFO(priv, "f/w package hdr boot inst size = %u\n", - boot_size); - /* * For any of the failures below (before allocating pci memory) * we will try to load a version with a smaller API -- maybe the * user just got a corrupted version of the latest API. */ - /* Verify size of file vs. image size info in file's header */ - if (ucode_raw->size != hdr_size + inst_size + data_size + init_size + - init_data_size + boot_size) { - - IWL_DEBUG_INFO(priv, - "uCode file size %d does not match expected size\n", - (int)ucode_raw->size); - goto try_again; - } + IWL_DEBUG_INFO(priv, "f/w package hdr ucode version raw = 0x%x\n", + priv->ucode_ver); + IWL_DEBUG_INFO(priv, "f/w package hdr runtime inst size = %Zd\n", + pieces.inst_size); + IWL_DEBUG_INFO(priv, "f/w package hdr runtime data size = %Zd\n", + pieces.data_size); + IWL_DEBUG_INFO(priv, "f/w package hdr init inst size = %Zd\n", + pieces.init_size); + IWL_DEBUG_INFO(priv, "f/w package hdr init data size = %Zd\n", + pieces.init_data_size); + IWL_DEBUG_INFO(priv, "f/w package hdr boot inst size = %Zd\n", + pieces.boot_size); /* Verify that uCode images will fit in card's SRAM */ - if (inst_size > priv->hw_params.max_inst_size) { - IWL_DEBUG_INFO(priv, "uCode instr len %d too large to fit in\n", - inst_size); + if (pieces.inst_size > priv->hw_params.max_inst_size) { + IWL_ERR(priv, "uCode instr len %Zd too large to fit in\n", + pieces.inst_size); goto try_again; } - if (data_size > priv->hw_params.max_data_size) { - IWL_DEBUG_INFO(priv, "uCode data len %d too large to fit in\n", - data_size); + if (pieces.data_size > priv->hw_params.max_data_size) { + IWL_ERR(priv, "uCode data len %Zd too large to fit in\n", + pieces.data_size); goto try_again; } - if (init_size > priv->hw_params.max_inst_size) { - IWL_INFO(priv, "uCode init instr len %d too large to fit in\n", - init_size); + + if (pieces.init_size > priv->hw_params.max_inst_size) { + IWL_ERR(priv, "uCode init instr len %Zd too large to fit in\n", + pieces.init_size); goto try_again; } - if (init_data_size > priv->hw_params.max_data_size) { - IWL_INFO(priv, "uCode init data len %d too large to fit in\n", - init_data_size); + + if (pieces.init_data_size > priv->hw_params.max_data_size) { + IWL_ERR(priv, "uCode init data len %Zd too large to fit in\n", + pieces.init_data_size); goto try_again; } - if (boot_size > priv->hw_params.max_bsm_size) { - IWL_INFO(priv, "uCode boot instr len %d too large to fit in\n", - boot_size); + + if (pieces.boot_size > priv->hw_params.max_bsm_size) { + IWL_ERR(priv, "uCode boot instr len %Zd too large to fit in\n", + pieces.boot_size); goto try_again; } + /* Allocate ucode buffers for card's bus-master loading ... */ /* Runtime instructions and 2 copies of data: * 1) unmodified from disk * 2) backup cache for save/restore during power-downs */ - priv->ucode_code.len = inst_size; + priv->ucode_code.len = pieces.inst_size; iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_code); - priv->ucode_data.len = data_size; + priv->ucode_data.len = pieces.data_size; iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_data); - priv->ucode_data_backup.len = data_size; + priv->ucode_data_backup.len = pieces.data_size; iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_data_backup); if (!priv->ucode_code.v_addr || !priv->ucode_data.v_addr || @@ -1729,11 +1777,11 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) goto err_pci_alloc; /* Initialization instructions and data */ - if (init_size && init_data_size) { - priv->ucode_init.len = init_size; + if (pieces.init_size && pieces.init_data_size) { + priv->ucode_init.len = pieces.init_size; iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_init); - priv->ucode_init_data.len = init_data_size; + priv->ucode_init_data.len = pieces.init_data_size; iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_init_data); if (!priv->ucode_init.v_addr || !priv->ucode_init_data.v_addr) @@ -1741,8 +1789,8 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) } /* Bootstrap (instructions only, no data) */ - if (boot_size) { - priv->ucode_boot.len = boot_size; + if (pieces.boot_size) { + priv->ucode_boot.len = pieces.boot_size; iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_boot); if (!priv->ucode_boot.v_addr) @@ -1752,44 +1800,41 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) /* Copy images into buffers for card's bus-master reads ... */ /* Runtime instructions (first block of data in file) */ - len = inst_size; - IWL_DEBUG_INFO(priv, "Copying (but not loading) uCode instr len %Zd\n", len); - memcpy(priv->ucode_code.v_addr, src, len); - src += len; + IWL_DEBUG_INFO(priv, "Copying (but not loading) uCode instr len %Zd\n", + pieces.inst_size); + memcpy(priv->ucode_code.v_addr, pieces.inst, pieces.inst_size); IWL_DEBUG_INFO(priv, "uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", priv->ucode_code.v_addr, (u32)priv->ucode_code.p_addr); - /* Runtime data (2nd block) - * NOTE: Copy into backup buffer will be done in iwl_up() */ - len = data_size; - IWL_DEBUG_INFO(priv, "Copying (but not loading) uCode data len %Zd\n", len); - memcpy(priv->ucode_data.v_addr, src, len); - memcpy(priv->ucode_data_backup.v_addr, src, len); - src += len; - - /* Initialization instructions (3rd block) */ - if (init_size) { - len = init_size; + /* + * Runtime data + * NOTE: Copy into backup buffer will be done in iwl_up() + */ + IWL_DEBUG_INFO(priv, "Copying (but not loading) uCode data len %Zd\n", + pieces.data_size); + memcpy(priv->ucode_data.v_addr, pieces.data, pieces.data_size); + memcpy(priv->ucode_data_backup.v_addr, pieces.data, pieces.data_size); + + /* Initialization instructions */ + if (pieces.init_size) { IWL_DEBUG_INFO(priv, "Copying (but not loading) init instr len %Zd\n", - len); - memcpy(priv->ucode_init.v_addr, src, len); - src += len; + pieces.init_size); + memcpy(priv->ucode_init.v_addr, pieces.init, pieces.init_size); } - /* Initialization data (4th block) */ - if (init_data_size) { - len = init_data_size; + /* Initialization data */ + if (pieces.init_data_size) { IWL_DEBUG_INFO(priv, "Copying (but not loading) init data len %Zd\n", - len); - memcpy(priv->ucode_init_data.v_addr, src, len); - src += len; + pieces.init_data_size); + memcpy(priv->ucode_init_data.v_addr, pieces.init_data, + pieces.init_data_size); } - /* Bootstrap instructions (5th block) */ - len = boot_size; - IWL_DEBUG_INFO(priv, "Copying (but not loading) boot instr len %Zd\n", len); - memcpy(priv->ucode_boot.v_addr, src, len); + /* Bootstrap instructions */ + IWL_DEBUG_INFO(priv, "Copying (but not loading) boot instr len %Zd\n", + pieces.boot_size); + memcpy(priv->ucode_boot.v_addr, pieces.boot, pieces.boot_size); /************************************************** * This is still part of probe() in a sense... |