aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2009-02-28 18:42:47 -0500
committerDavid S. Miller <davem@davemloft.net>2009-03-02 06:10:23 -0500
commit1039abbc5b1bfa943b6daabbe9de1499037a90c0 (patch)
tree7c8e18a228750d59fba2941cca87850fb603af6c /drivers/net/wimax
parent86739fb96e8c8269fc5b3d300c959bede272a6f6 (diff)
wimax/i2400m: add the ability to fallback to other firmware files if the default is not there
In order to support backwards compatibility with older firmwares when a driver is updated by a new kernel release, the i2400m bus drivers can declare a list of firmware files they can work with (in general these will be each a different version). The firmware loader will try them in sequence until one loads. Thus, if a user doesn't have the latest and greatest firmware that a newly installed kernel would require, the driver would fall back to the firmware from a previous release. To support this, the i2400m->bus_fw_name is changed to be a NULL terminated array firmware file names (and renamed to bus_fw_names) and we add a new entry (i2400m->fw_name) that points to the name of the firmware being currently used. All code that needs to print the firmware file name uses i2400m->fw_name instead of the old i2400m->bus_fw_name. The code in i2400m_dev_bootstrap() that loads the firmware is changed with an iterator over the firmware file name list that tries to load each form user space, using the first one that succeeds in request_firmware() (and thus stopping the iteration). The USB and SDIO bus drivers are updated to take advantage of this and reflect which firmwares they support. Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/wimax')
-rw-r--r--drivers/net/wimax/i2400m/fw.c53
-rw-r--r--drivers/net/wimax/i2400m/i2400m.h21
-rw-r--r--drivers/net/wimax/i2400m/sdio.c11
-rw-r--r--drivers/net/wimax/i2400m/usb.c14
4 files changed, 64 insertions, 35 deletions
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index ecd0cfaefdc..675c6ce810c 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -483,7 +483,7 @@ ssize_t i2400m_dnload_bcf(struct i2400m *i2400m,
483 if (offset + section_size > bcf_len) { 483 if (offset + section_size > bcf_len) {
484 dev_err(dev, "fw %s: bad section #%zu, " 484 dev_err(dev, "fw %s: bad section #%zu, "
485 "end (@%zu) beyond EOF (@%zu)\n", 485 "end (@%zu) beyond EOF (@%zu)\n",
486 i2400m->bus_fw_name, section, 486 i2400m->fw_name, section,
487 offset + section_size, bcf_len); 487 offset + section_size, bcf_len);
488 ret = -EINVAL; 488 ret = -EINVAL;
489 goto error_section_beyond_eof; 489 goto error_section_beyond_eof;
@@ -493,7 +493,7 @@ ssize_t i2400m_dnload_bcf(struct i2400m *i2400m,
493 &ack, sizeof(ack), I2400M_BM_CMD_RAW); 493 &ack, sizeof(ack), I2400M_BM_CMD_RAW);
494 if (ret < 0) { 494 if (ret < 0) {
495 dev_err(dev, "fw %s: section #%zu (@%zu %zu B) " 495 dev_err(dev, "fw %s: section #%zu (@%zu %zu B) "
496 "failed %d\n", i2400m->bus_fw_name, section, 496 "failed %d\n", i2400m->fw_name, section,
497 offset, sizeof(*bh) + data_size, (int) ret); 497 offset, sizeof(*bh) + data_size, (int) ret);
498 goto error_send; 498 goto error_send;
499 } 499 }
@@ -874,7 +874,7 @@ int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf)
874 if (result < 0) 874 if (result < 0)
875 dev_err(dev, "fw %s: non-signed download " 875 dev_err(dev, "fw %s: non-signed download "
876 "initialization failed: %d\n", 876 "initialization failed: %d\n",
877 i2400m->bus_fw_name, result); 877 i2400m->fw_name, result);
878 } else if (i2400m->sboot == 0 878 } else if (i2400m->sboot == 0
879 && (module_id & I2400M_BCF_MOD_ID_POKES)) { 879 && (module_id & I2400M_BCF_MOD_ID_POKES)) {
880 /* non-signed boot process with pokes, nothing to do */ 880 /* non-signed boot process with pokes, nothing to do */
@@ -886,7 +886,7 @@ int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf)
886 if (result < 0) 886 if (result < 0)
887 dev_err(dev, "fw %s: signed boot download " 887 dev_err(dev, "fw %s: signed boot download "
888 "initialization failed: %d\n", 888 "initialization failed: %d\n",
889 i2400m->bus_fw_name, result); 889 i2400m->fw_name, result);
890 } 890 }
891 return result; 891 return result;
892} 892}
@@ -915,7 +915,7 @@ int i2400m_fw_check(struct i2400m *i2400m,
915 if (bcf_size < sizeof(*bcf)) { /* big enough header? */ 915 if (bcf_size < sizeof(*bcf)) { /* big enough header? */
916 dev_err(dev, "firmware %s too short: " 916 dev_err(dev, "firmware %s too short: "
917 "%zu B vs %zu (at least) expected\n", 917 "%zu B vs %zu (at least) expected\n",
918 i2400m->bus_fw_name, bcf_size, sizeof(*bcf)); 918 i2400m->fw_name, bcf_size, sizeof(*bcf));
919 goto error; 919 goto error;
920 } 920 }
921 921
@@ -931,7 +931,7 @@ int i2400m_fw_check(struct i2400m *i2400m,
931 if (bcf_size != size) { /* annoyingly paranoid */ 931 if (bcf_size != size) { /* annoyingly paranoid */
932 dev_err(dev, "firmware %s: bad size, got " 932 dev_err(dev, "firmware %s: bad size, got "
933 "%zu B vs %u expected\n", 933 "%zu B vs %u expected\n",
934 i2400m->bus_fw_name, bcf_size, size); 934 i2400m->fw_name, bcf_size, size);
935 goto error; 935 goto error;
936 } 936 }
937 937
@@ -943,7 +943,7 @@ int i2400m_fw_check(struct i2400m *i2400m,
943 943
944 if (module_type != 6) { /* built for the right hardware? */ 944 if (module_type != 6) { /* built for the right hardware? */
945 dev_err(dev, "bad fw %s: unexpected module type 0x%x; " 945 dev_err(dev, "bad fw %s: unexpected module type 0x%x; "
946 "aborting\n", i2400m->bus_fw_name, module_type); 946 "aborting\n", i2400m->fw_name, module_type);
947 goto error; 947 goto error;
948 } 948 }
949 949
@@ -951,10 +951,10 @@ int i2400m_fw_check(struct i2400m *i2400m,
951 result = 0; 951 result = 0;
952 if (module_vendor != 0x8086) 952 if (module_vendor != 0x8086)
953 dev_err(dev, "bad fw %s? unexpected vendor 0x%04x\n", 953 dev_err(dev, "bad fw %s? unexpected vendor 0x%04x\n",
954 i2400m->bus_fw_name, module_vendor); 954 i2400m->fw_name, module_vendor);
955 if (date < 0x20080300) 955 if (date < 0x20080300)
956 dev_err(dev, "bad fw %s? build date too old %08x\n", 956 dev_err(dev, "bad fw %s? build date too old %08x\n",
957 i2400m->bus_fw_name, date); 957 i2400m->fw_name, date);
958error: 958error:
959 return result; 959 return result;
960} 960}
@@ -1016,7 +1016,7 @@ hw_reboot:
1016 goto error_dev_rebooted; 1016 goto error_dev_rebooted;
1017 if (ret < 0) { 1017 if (ret < 0) {
1018 dev_err(dev, "fw %s: download failed: %d\n", 1018 dev_err(dev, "fw %s: download failed: %d\n",
1019 i2400m->bus_fw_name, ret); 1019 i2400m->fw_name, ret);
1020 goto error_dnload_bcf; 1020 goto error_dnload_bcf;
1021 } 1021 }
1022 1022
@@ -1026,12 +1026,12 @@ hw_reboot:
1026 if (ret < 0) { 1026 if (ret < 0) {
1027 dev_err(dev, "fw %s: " 1027 dev_err(dev, "fw %s: "
1028 "download finalization failed: %d\n", 1028 "download finalization failed: %d\n",
1029 i2400m->bus_fw_name, ret); 1029 i2400m->fw_name, ret);
1030 goto error_dnload_finalize; 1030 goto error_dnload_finalize;
1031 } 1031 }
1032 1032
1033 d_printf(2, dev, "fw %s successfully uploaded\n", 1033 d_printf(2, dev, "fw %s successfully uploaded\n",
1034 i2400m->bus_fw_name); 1034 i2400m->fw_name);
1035 i2400m->boot_mode = 0; 1035 i2400m->boot_mode = 0;
1036error_dnload_finalize: 1036error_dnload_finalize:
1037error_dnload_bcf: 1037error_dnload_bcf:
@@ -1067,28 +1067,41 @@ error_dev_rebooted:
1067 */ 1067 */
1068int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags) 1068int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags)
1069{ 1069{
1070 int ret = 0; 1070 int ret = 0, itr = 0;
1071 struct device *dev = i2400m_dev(i2400m); 1071 struct device *dev = i2400m_dev(i2400m);
1072 const struct firmware *fw; 1072 const struct firmware *fw;
1073 const struct i2400m_bcf_hdr *bcf; /* Firmware data */ 1073 const struct i2400m_bcf_hdr *bcf; /* Firmware data */
1074 const char *fw_name;
1074 1075
1075 d_fnstart(5, dev, "(i2400m %p)\n", i2400m); 1076 d_fnstart(5, dev, "(i2400m %p)\n", i2400m);
1077
1076 /* Load firmware files to memory. */ 1078 /* Load firmware files to memory. */
1077 ret = request_firmware(&fw, i2400m->bus_fw_name, dev); 1079 itr = 0;
1078 if (ret) { 1080 while(1) {
1079 dev_err(dev, "fw %s: request failed: %d\n", 1081 fw_name = i2400m->bus_fw_names[itr];
1080 i2400m->bus_fw_name, ret); 1082 if (fw_name == NULL) {
1081 goto error_fw_req; 1083 dev_err(dev, "Could not find a usable firmware image\n");
1084 ret = -ENOENT;
1085 goto error_no_fw;
1086 }
1087 ret = request_firmware(&fw, fw_name, dev);
1088 if (ret == 0)
1089 break; /* got it */
1090 if (ret < 0)
1091 dev_err(dev, "fw %s: cannot load file: %d\n",
1092 fw_name, ret);
1093 itr++;
1082 } 1094 }
1083 bcf = (void *) fw->data;
1084 1095
1096 bcf = (void *) fw->data;
1097 i2400m->fw_name = fw_name;
1085 ret = i2400m_fw_check(i2400m, bcf, fw->size); 1098 ret = i2400m_fw_check(i2400m, bcf, fw->size);
1086 if (ret < 0) 1099 if (ret < 0)
1087 goto error_fw_bad; 1100 goto error_fw_bad;
1088 ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags); 1101 ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags);
1089error_fw_bad: 1102error_fw_bad:
1090 release_firmware(fw); 1103 release_firmware(fw);
1091error_fw_req: 1104error_no_fw:
1092 d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); 1105 d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret);
1093 return ret; 1106 return ret;
1094} 1107}
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h
index f9e55397ee8..ad71ad1086e 100644
--- a/drivers/net/wimax/i2400m/i2400m.h
+++ b/drivers/net/wimax/i2400m/i2400m.h
@@ -156,10 +156,6 @@ enum {
156}; 156};
157 157
158 158
159/* Firmware version we request when pulling the fw image file */
160#define I2400M_FW_VERSION "1.4"
161
162
163/** 159/**
164 * i2400m_reset_type - methods to reset a device 160 * i2400m_reset_type - methods to reset a device
165 * 161 *
@@ -242,10 +238,14 @@ struct i2400m_reset_ctx;
242 * The caller to this function will check if the response is a 238 * The caller to this function will check if the response is a
243 * barker that indicates the device going into reset mode. 239 * barker that indicates the device going into reset mode.
244 * 240 *
245 * @bus_fw_name: [fill] name of the firmware image (in most cases, 241 * @bus_fw_names: [fill] a NULL-terminated array with the names of the
246 * they are all the same for a single release, except that they 242 * firmware images to try loading. This is made a list so we can
247 * have the type of the bus embedded in the name (eg: 243 * support backward compatibility of firmware releases (eg: if we
248 * i2400m-fw-X-VERSION.sbcf, where X is the bus name). 244 * can't find the default v1.4, we try v1.3). In general, the name
245 * should be i2400m-fw-X-VERSION.sbcf, where X is the bus name.
246 * The list is tried in order and the first one that loads is
247 * used. The fw loader will set i2400m->fw_name to point to the
248 * active firmware image.
249 * 249 *
250 * @bus_bm_mac_addr_impaired: [fill] Set to true if the device's MAC 250 * @bus_bm_mac_addr_impaired: [fill] Set to true if the device's MAC
251 * address provided in boot mode is kind of broken and needs to 251 * address provided in boot mode is kind of broken and needs to
@@ -364,6 +364,8 @@ struct i2400m_reset_ctx;
364 * These have to be in a separate directory, a child of 364 * These have to be in a separate directory, a child of
365 * (wimax_dev->debugfs_dentry) so they can be removed when the 365 * (wimax_dev->debugfs_dentry) so they can be removed when the
366 * module unloads, as we don't keep each dentry. 366 * module unloads, as we don't keep each dentry.
367 *
368 * @fw_name: name of the firmware image that is currently being used.
367 */ 369 */
368struct i2400m { 370struct i2400m {
369 struct wimax_dev wimax_dev; /* FIRST! See doc */ 371 struct wimax_dev wimax_dev; /* FIRST! See doc */
@@ -388,7 +390,7 @@ struct i2400m {
388 size_t, int flags); 390 size_t, int flags);
389 ssize_t (*bus_bm_wait_for_ack)(struct i2400m *, 391 ssize_t (*bus_bm_wait_for_ack)(struct i2400m *,
390 struct i2400m_bootrom_header *, size_t); 392 struct i2400m_bootrom_header *, size_t);
391 const char *bus_fw_name; 393 const char **bus_fw_names;
392 unsigned bus_bm_mac_addr_impaired:1; 394 unsigned bus_bm_mac_addr_impaired:1;
393 395
394 spinlock_t tx_lock; /* protect TX state */ 396 spinlock_t tx_lock; /* protect TX state */
@@ -421,6 +423,7 @@ struct i2400m {
421 struct sk_buff *wake_tx_skb; 423 struct sk_buff *wake_tx_skb;
422 424
423 struct dentry *debugfs_dentry; 425 struct dentry *debugfs_dentry;
426 const char *fw_name; /* name of the current firmware image */
424}; 427};
425 428
426 429
diff --git a/drivers/net/wimax/i2400m/sdio.c b/drivers/net/wimax/i2400m/sdio.c
index 123a5f8db6a..5ac5e76701c 100644
--- a/drivers/net/wimax/i2400m/sdio.c
+++ b/drivers/net/wimax/i2400m/sdio.c
@@ -70,8 +70,13 @@
70static int ioe_timeout = 2; 70static int ioe_timeout = 2;
71module_param(ioe_timeout, int, 0); 71module_param(ioe_timeout, int, 0);
72 72
73/* Our firmware file name */ 73/* Our firmware file name list */
74#define I2400MS_FW_FILE_NAME "i2400m-fw-sdio-" I2400M_FW_VERSION ".sbcf" 74static const char *i2400ms_bus_fw_names[] = {
75#define I2400MS_FW_FILE_NAME "i2400m-fw-sdio-1.3.sbcf"
76 I2400MS_FW_FILE_NAME,
77 NULL
78};
79
75 80
76/* 81/*
77 * Enable the SDIO function 82 * Enable the SDIO function
@@ -401,7 +406,7 @@ int i2400ms_probe(struct sdio_func *func,
401 i2400m->bus_reset = i2400ms_bus_reset; 406 i2400m->bus_reset = i2400ms_bus_reset;
402 i2400m->bus_bm_cmd_send = i2400ms_bus_bm_cmd_send; 407 i2400m->bus_bm_cmd_send = i2400ms_bus_bm_cmd_send;
403 i2400m->bus_bm_wait_for_ack = i2400ms_bus_bm_wait_for_ack; 408 i2400m->bus_bm_wait_for_ack = i2400ms_bus_bm_wait_for_ack;
404 i2400m->bus_fw_name = I2400MS_FW_FILE_NAME; 409 i2400m->bus_fw_names = i2400ms_bus_fw_names;
405 i2400m->bus_bm_mac_addr_impaired = 1; 410 i2400m->bus_bm_mac_addr_impaired = 1;
406 411
407 result = i2400ms_enable_function(i2400ms->func); 412 result = i2400ms_enable_function(i2400ms->func);
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c
index 7c28610da6f..ca4151a9e22 100644
--- a/drivers/net/wimax/i2400m/usb.c
+++ b/drivers/net/wimax/i2400m/usb.c
@@ -73,7 +73,14 @@
73 73
74 74
75/* Our firmware file name */ 75/* Our firmware file name */
76#define I2400MU_FW_FILE_NAME "i2400m-fw-usb-" I2400M_FW_VERSION ".sbcf" 76static const char *i2400mu_bus_fw_names[] = {
77#define I2400MU_FW_FILE_NAME_v1_4 "i2400m-fw-usb-1.4.sbcf"
78 I2400MU_FW_FILE_NAME_v1_4,
79#define I2400MU_FW_FILE_NAME_v1_3 "i2400m-fw-usb-1.3.sbcf"
80 I2400MU_FW_FILE_NAME_v1_3,
81 NULL,
82};
83
77 84
78static 85static
79int i2400mu_bus_dev_start(struct i2400m *i2400m) 86int i2400mu_bus_dev_start(struct i2400m *i2400m)
@@ -394,7 +401,7 @@ int i2400mu_probe(struct usb_interface *iface,
394 i2400m->bus_reset = i2400mu_bus_reset; 401 i2400m->bus_reset = i2400mu_bus_reset;
395 i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send; 402 i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send;
396 i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack; 403 i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack;
397 i2400m->bus_fw_name = I2400MU_FW_FILE_NAME; 404 i2400m->bus_fw_names = i2400mu_bus_fw_names;
398 i2400m->bus_bm_mac_addr_impaired = 0; 405 i2400m->bus_bm_mac_addr_impaired = 0;
399 406
400#ifdef CONFIG_PM 407#ifdef CONFIG_PM
@@ -594,4 +601,5 @@ module_exit(i2400mu_driver_exit);
594MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); 601MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>");
595MODULE_DESCRIPTION("Intel 2400M WiMAX networking for USB"); 602MODULE_DESCRIPTION("Intel 2400M WiMAX networking for USB");
596MODULE_LICENSE("GPL"); 603MODULE_LICENSE("GPL");
597MODULE_FIRMWARE(I2400MU_FW_FILE_NAME); 604MODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_4);
605MODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_3);