Skip to content

Commit

Permalink
iwlwifi: rely on API version read from firmware
Browse files Browse the repository at this point in the history
This adds the infrastructure to support older firmware APIs.
The API version number is stored as part of the filename, we first try to
load the most recent firmware and progressively try lower versions.
The API version is also read from the firmware self and stored as part
of the iwl_priv structure. Only firmware that is supported by driver will
be loaded. The version number read from firmware is compared
to supported versions in the driver not the API version used as part of
filename.

An example using this new infrastrucure:
   if (IWL_UCODE_API(priv->ucode_ver) >= 2) {
        Driver interacts with Firmware API version >= 2.
   } else {
        Driver interacts with Firmware API version 1.
   }

Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Reinette Chatre authored and John W. Linville committed Dec 5, 2008
1 parent c02b3ac commit a0987a8
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 56 deletions.
26 changes: 25 additions & 1 deletion drivers/net/wireless/iwlwifi/iwl-3945-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,33 @@
#define IWL_SKU_G 0x1
#define IWL_SKU_A 0x2

/**
* struct iwl_3945_cfg
* @fw_name_pre: Firmware filename prefix. The api version and extension
* (.ucode) will be added to filename before loading from disk. The
* filename is constructed as fw_name_pre<api>.ucode.
* @ucode_api_max: Highest version of uCode API supported by driver.
* @ucode_api_min: Lowest version of uCode API supported by driver.
*
* We enable the driver to be backward compatible wrt API version. The
* driver specifies which APIs it supports (with @ucode_api_max being the
* highest and @ucode_api_min the lowest). Firmware will only be loaded if
* it has a supported API version. The firmware's API version will be
* stored in @iwl_priv, enabling the driver to make runtime changes based
* on firmware version used.
*
* For example,
* if (IWL_UCODE_API(priv->ucode_ver) >= 2) {
* Driver interacts with Firmware API version >= 2.
* } else {
* Driver interacts with Firmware API version 1.
* }
*/
struct iwl_3945_cfg {
const char *name;
const char *fw_name;
const char *fw_name_pre;
const unsigned int ucode_api_max;
const unsigned int ucode_api_min;
unsigned int sku;
};

Expand Down
8 changes: 6 additions & 2 deletions drivers/net/wireless/iwlwifi/iwl-3945.c
Original file line number Diff line number Diff line change
Expand Up @@ -2508,13 +2508,17 @@ void iwl3945_hw_cancel_deferred_work(struct iwl3945_priv *priv)

static struct iwl_3945_cfg iwl3945_bg_cfg = {
.name = "3945BG",
.fw_name = "iwlwifi-3945" IWL3945_UCODE_API ".ucode",
.fw_name_pre = IWL3945_FW_PRE,
.ucode_api_max = IWL3945_UCODE_API_MAX,
.ucode_api_min = IWL3945_UCODE_API_MIN,
.sku = IWL_SKU_G,
};

static struct iwl_3945_cfg iwl3945_abg_cfg = {
.name = "3945ABG",
.fw_name = "iwlwifi-3945" IWL3945_UCODE_API ".ucode",
.fw_name_pre = IWL3945_FW_PRE,
.ucode_api_max = IWL3945_UCODE_API_MAX,
.ucode_api_min = IWL3945_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G,
};

Expand Down
14 changes: 9 additions & 5 deletions drivers/net/wireless/iwlwifi/iwl-3945.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,15 @@ extern struct pci_device_id iwl3945_hw_card_ids[];
#include "iwl-3945-debug.h"
#include "iwl-3945-led.h"

/* Change firmware file name, using "-" and incrementing number,
* *only* when uCode interface or architecture changes so that it
* is not compatible with earlier drivers.
* This number will also appear in << 8 position of 1st dword of uCode file */
#define IWL3945_UCODE_API "-1"
/* Highest firmware API version supported */
#define IWL3945_UCODE_API_MAX 1

/* Lowest firmware API version supported */
#define IWL3945_UCODE_API_MIN 1

#define IWL3945_FW_PRE "iwlwifi-3945-"
#define _IWL3945_MODULE_FIRMWARE(api) IWL3945_FW_PRE #api ".ucode"
#define IWL3945_MODULE_FIRMWARE(api) _IWL3945_MODULE_FIRMWARE(api)

/* Default noise level to report when noise measurement is not available.
* This may be because we're:
Expand Down
21 changes: 13 additions & 8 deletions drivers/net/wireless/iwlwifi/iwl-4965.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,15 @@
static int iwl4965_send_tx_power(struct iwl_priv *priv);
static int iwl4965_hw_get_temperature(const struct iwl_priv *priv);

/* Change firmware file name, using "-" and incrementing number,
* *only* when uCode interface or architecture changes so that it
* is not compatible with earlier drivers.
* This number will also appear in << 8 position of 1st dword of uCode file */
#define IWL4965_UCODE_API "-2"
#define IWL4965_MODULE_FIRMWARE "iwlwifi-4965" IWL4965_UCODE_API ".ucode"
/* Highest firmware API version supported */
#define IWL4965_UCODE_API_MAX 2

/* Lowest firmware API version supported */
#define IWL4965_UCODE_API_MIN 2

#define IWL4965_FW_PRE "iwlwifi-4965-"
#define _IWL4965_MODULE_FIRMWARE(api) IWL4965_FW_PRE #api ".ucode"
#define IWL4965_MODULE_FIRMWARE(api) _IWL4965_MODULE_FIRMWARE(api)


/* module parameters */
Expand Down Expand Up @@ -2336,7 +2339,9 @@ static struct iwl_ops iwl4965_ops = {

struct iwl_cfg iwl4965_agn_cfg = {
.name = "4965AGN",
.fw_name = IWL4965_MODULE_FIRMWARE,
.fw_name_pre = IWL4965_FW_PRE,
.ucode_api_max = IWL4965_UCODE_API_MAX,
.ucode_api_min = IWL4965_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
.eeprom_size = IWL4965_EEPROM_IMG_SIZE,
.eeprom_ver = EEPROM_4965_EEPROM_VERSION,
Expand All @@ -2346,7 +2351,7 @@ struct iwl_cfg iwl4965_agn_cfg = {
};

/* Module firmware */
MODULE_FIRMWARE(IWL4965_MODULE_FIRMWARE);
MODULE_FIRMWARE(IWL4965_MODULE_FIRMWARE(IWL4965_UCODE_API_MAX));

module_param_named(antenna, iwl4965_mod_params.antenna, int, 0444);
MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])");
Expand Down
46 changes: 34 additions & 12 deletions drivers/net/wireless/iwlwifi/iwl-5000.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,21 @@
#include "iwl-helpers.h"
#include "iwl-5000-hw.h"

#define IWL5000_UCODE_API "-1"
#define IWL5150_UCODE_API "-1"
/* Highest firmware API version supported */
#define IWL5000_UCODE_API_MAX 1
#define IWL5150_UCODE_API_MAX 1

#define IWL5000_MODULE_FIRMWARE "iwlwifi-5000" IWL5000_UCODE_API ".ucode"
#define IWL5150_MODULE_FIRMWARE "iwlwifi-5150" IWL5150_UCODE_API ".ucode"
/* Lowest firmware API version supported */
#define IWL5000_UCODE_API_MIN 1
#define IWL5150_UCODE_API_MIN 1

#define IWL5000_FW_PRE "iwlwifi-5000-"
#define _IWL5000_MODULE_FIRMWARE(api) IWL5000_FW_PRE #api ".ucode"
#define IWL5000_MODULE_FIRMWARE(api) _IWL5000_MODULE_FIRMWARE(api)

#define IWL5150_FW_PRE "iwlwifi-5150-"
#define _IWL5150_MODULE_FIRMWARE(api) IWL5150_FW_PRE #api ".ucode"
#define IWL5150_MODULE_FIRMWARE(api) _IWL5150_MODULE_FIRMWARE(api)

static const u16 iwl5000_default_queue_to_tx_fifo[] = {
IWL_TX_FIFO_AC3,
Expand Down Expand Up @@ -1532,7 +1542,9 @@ static struct iwl_mod_params iwl50_mod_params = {

struct iwl_cfg iwl5300_agn_cfg = {
.name = "5300AGN",
.fw_name = IWL5000_MODULE_FIRMWARE,
.fw_name_pre = IWL5000_FW_PRE,
.ucode_api_max = IWL5000_UCODE_API_MAX,
.ucode_api_min = IWL5000_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
.ops = &iwl5000_ops,
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
Expand All @@ -1543,7 +1555,9 @@ struct iwl_cfg iwl5300_agn_cfg = {

struct iwl_cfg iwl5100_bg_cfg = {
.name = "5100BG",
.fw_name = IWL5000_MODULE_FIRMWARE,
.fw_name_pre = IWL5000_FW_PRE,
.ucode_api_max = IWL5000_UCODE_API_MAX,
.ucode_api_min = IWL5000_UCODE_API_MIN,
.sku = IWL_SKU_G,
.ops = &iwl5000_ops,
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
Expand All @@ -1554,7 +1568,9 @@ struct iwl_cfg iwl5100_bg_cfg = {

struct iwl_cfg iwl5100_abg_cfg = {
.name = "5100ABG",
.fw_name = IWL5000_MODULE_FIRMWARE,
.fw_name_pre = IWL5000_FW_PRE,
.ucode_api_max = IWL5000_UCODE_API_MAX,
.ucode_api_min = IWL5000_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G,
.ops = &iwl5000_ops,
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
Expand All @@ -1565,7 +1581,9 @@ struct iwl_cfg iwl5100_abg_cfg = {

struct iwl_cfg iwl5100_agn_cfg = {
.name = "5100AGN",
.fw_name = IWL5000_MODULE_FIRMWARE,
.fw_name_pre = IWL5000_FW_PRE,
.ucode_api_max = IWL5000_UCODE_API_MAX,
.ucode_api_min = IWL5000_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
.ops = &iwl5000_ops,
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
Expand All @@ -1576,7 +1594,9 @@ struct iwl_cfg iwl5100_agn_cfg = {

struct iwl_cfg iwl5350_agn_cfg = {
.name = "5350AGN",
.fw_name = IWL5000_MODULE_FIRMWARE,
.fw_name_pre = IWL5000_FW_PRE,
.ucode_api_max = IWL5000_UCODE_API_MAX,
.ucode_api_min = IWL5000_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
.ops = &iwl5000_ops,
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
Expand All @@ -1587,7 +1607,9 @@ struct iwl_cfg iwl5350_agn_cfg = {

struct iwl_cfg iwl5150_agn_cfg = {
.name = "5150AGN",
.fw_name = IWL5150_MODULE_FIRMWARE,
.fw_name_pre = IWL5150_FW_PRE,
.ucode_api_max = IWL5150_UCODE_API_MAX,
.ucode_api_min = IWL5150_UCODE_API_MIN,
.sku = IWL_SKU_A|IWL_SKU_G|IWL_SKU_N,
.ops = &iwl5000_ops,
.eeprom_size = IWL_5000_EEPROM_IMG_SIZE,
Expand All @@ -1596,8 +1618,8 @@ struct iwl_cfg iwl5150_agn_cfg = {
.mod_params = &iwl50_mod_params,
};

MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE);
MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE);
MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));
MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_MAX));

module_param_named(disable50, iwl50_mod_params.disable, int, 0444);
MODULE_PARM_DESC(disable50,
Expand Down
62 changes: 49 additions & 13 deletions drivers/net/wireless/iwlwifi/iwl-agn.c
Original file line number Diff line number Diff line change
Expand Up @@ -1570,24 +1570,40 @@ static void iwl_nic_start(struct iwl_priv *priv)
static int iwl_read_ucode(struct iwl_priv *priv)
{
struct iwl_ucode *ucode;
int ret;
int ret = -EINVAL, index;
const struct firmware *ucode_raw;
const char *name = priv->cfg->fw_name;
const char *name_pre = priv->cfg->fw_name_pre;
const unsigned int api_max = priv->cfg->ucode_api_max;
const unsigned int api_min = priv->cfg->ucode_api_min;
char buf[25];
u8 *src;
size_t len;
u32 inst_size, data_size, init_size, init_data_size, boot_size;
u32 api_ver, inst_size, data_size, init_size, init_data_size, boot_size;

/* Ask kernel firmware_class module to get the boot firmware off disk.
* request_firmware() is synchronous, file is in memory on return. */
ret = request_firmware(&ucode_raw, name, &priv->pci_dev->dev);
if (ret < 0) {
IWL_ERROR("%s firmware file req failed: Reason %d\n",
name, ret);
goto error;
for (index = api_max; index >= api_min; index--) {
sprintf(buf, "%s%d%s", name_pre, index, ".ucode");
ret = request_firmware(&ucode_raw, buf, &priv->pci_dev->dev);
if (ret < 0) {
IWL_ERROR("%s firmware file req failed: Reason %d\n",
buf, ret);
if (ret == -ENOENT)
continue;
else
goto error;
} else {
if (index < api_max)
IWL_ERROR("Loaded firmware %s, which is deprecated. Please use API v%u instead.\n",
buf, api_max);
IWL_DEBUG_INFO("Got firmware '%s' file (%zd bytes) from disk\n",
buf, ucode_raw->size);
break;
}
}

IWL_DEBUG_INFO("Got firmware '%s' file (%zd bytes) from disk\n",
name, ucode_raw->size);
if (ret < 0)
goto error;

/* Make sure that we got at least our header! */
if (ucode_raw->size < sizeof(*ucode)) {
Expand All @@ -1600,19 +1616,39 @@ static int iwl_read_ucode(struct iwl_priv *priv)
ucode = (void *)ucode_raw->data;

priv->ucode_ver = le32_to_cpu(ucode->ver);
api_ver = IWL_UCODE_API(priv->ucode_ver);
inst_size = le32_to_cpu(ucode->inst_size);
data_size = le32_to_cpu(ucode->data_size);
init_size = le32_to_cpu(ucode->init_size);
init_data_size = le32_to_cpu(ucode->init_data_size);
boot_size = le32_to_cpu(ucode->boot_size);

IWL_DEBUG_INFO("f/w package hdr ucode version raw = 0x%x\n",
priv->ucode_ver);
IWL_DEBUG_INFO("f/w package hdr ucode version = %u.%u.%u.%u\n",
/* 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 firware header from here on forward */

if (api_ver < api_min || api_ver > api_max) {
IWL_ERROR("Driver unable to support your firmware API. "
"Driver supports v%u, firmware is v%u.\n",
api_max, api_ver);
priv->ucode_ver = 0;
ret = -EINVAL;
goto err_release;
}
if (api_ver != api_max)
IWL_ERROR("Firmware has old API version. Expected v%u, "
"got v%u. New firmware can be obtained "
"from http://www.intellinuxwireless.org.\n",
api_max, api_ver);

printk(KERN_INFO DRV_NAME " loaded firmware version %u.%u.%u.%u\n",
IWL_UCODE_MAJOR(priv->ucode_ver),
IWL_UCODE_MINOR(priv->ucode_ver),
IWL_UCODE_API(priv->ucode_ver),
IWL_UCODE_SERIAL(priv->ucode_ver));

IWL_DEBUG_INFO("f/w package hdr ucode version raw = 0x%x\n",
priv->ucode_ver);
IWL_DEBUG_INFO("f/w package hdr runtime inst size = %u\n",
inst_size);
IWL_DEBUG_INFO("f/w package hdr runtime data size = %u\n",
Expand Down
32 changes: 31 additions & 1 deletion drivers/net/wireless/iwlwifi/iwl-core.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,39 @@ struct iwl_mod_params {
int restart_fw; /* def: 1 = restart firmware */
};

/**
* struct iwl_cfg
* @fw_name_pre: Firmware filename prefix. The api version and extension
* (.ucode) will be added to filename before loading from disk. The
* filename is constructed as fw_name_pre<api>.ucode.
* @ucode_api_max: Highest version of uCode API supported by driver.
* @ucode_api_min: Lowest version of uCode API supported by driver.
*
* We enable the driver to be backward compatible wrt API version. The
* driver specifies which APIs it supports (with @ucode_api_max being the
* highest and @ucode_api_min the lowest). Firmware will only be loaded if
* it has a supported API version. The firmware's API version will be
* stored in @iwl_priv, enabling the driver to make runtime changes based
* on firmware version used.
*
* For example,
* if (IWL_UCODE_API(priv->ucode_ver) >= 2) {
* Driver interacts with Firmware API version >= 2.
* } else {
* Driver interacts with Firmware API version 1.
* }
*
* The ideal usage of this infrastructure is to treat a new ucode API
* release as a new hardware revision. That is, through utilizing the
* iwl_hcmd_utils_ops etc. we accommodate different command structures
* and flows between hardware versions (4965/5000) as well as their API
* versions.
*/
struct iwl_cfg {
const char *name;
const char *fw_name;
const char *fw_name_pre;
const unsigned int ucode_api_max;
const unsigned int ucode_api_min;
unsigned int sku;
int eeprom_size;
u16 eeprom_ver;
Expand Down
Loading

0 comments on commit a0987a8

Please sign in to comment.