diff options
author | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-02-28 18:42:47 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-03-02 06:10:23 -0500 |
commit | 1039abbc5b1bfa943b6daabbe9de1499037a90c0 (patch) | |
tree | 7c8e18a228750d59fba2941cca87850fb603af6c /drivers/net/wimax | |
parent | 86739fb96e8c8269fc5b3d300c959bede272a6f6 (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.c | 53 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/i2400m.h | 21 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/sdio.c | 11 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/usb.c | 14 |
4 files changed, 64 insertions, 35 deletions
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c index ecd0cfaefdcc..675c6ce810c0 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); |
958 | error: | 958 | error: |
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; |
1036 | error_dnload_finalize: | 1036 | error_dnload_finalize: |
1037 | error_dnload_bcf: | 1037 | error_dnload_bcf: |
@@ -1067,28 +1067,41 @@ error_dev_rebooted: | |||
1067 | */ | 1067 | */ |
1068 | int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags) | 1068 | int 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); |
1089 | error_fw_bad: | 1102 | error_fw_bad: |
1090 | release_firmware(fw); | 1103 | release_firmware(fw); |
1091 | error_fw_req: | 1104 | error_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 f9e55397ee88..ad71ad1086ea 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 | */ |
368 | struct i2400m { | 370 | struct 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 123a5f8db6ad..5ac5e76701cd 100644 --- a/drivers/net/wimax/i2400m/sdio.c +++ b/drivers/net/wimax/i2400m/sdio.c | |||
@@ -70,8 +70,13 @@ | |||
70 | static int ioe_timeout = 2; | 70 | static int ioe_timeout = 2; |
71 | module_param(ioe_timeout, int, 0); | 71 | module_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" | 74 | static 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 7c28610da6f3..ca4151a9e222 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" | 76 | static 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 | ||
78 | static | 85 | static |
79 | int i2400mu_bus_dev_start(struct i2400m *i2400m) | 86 | int 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); | |||
594 | MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); | 601 | MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>"); |
595 | MODULE_DESCRIPTION("Intel 2400M WiMAX networking for USB"); | 602 | MODULE_DESCRIPTION("Intel 2400M WiMAX networking for USB"); |
596 | MODULE_LICENSE("GPL"); | 603 | MODULE_LICENSE("GPL"); |
597 | MODULE_FIRMWARE(I2400MU_FW_FILE_NAME); | 604 | MODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_4); |
605 | MODULE_FIRMWARE(I2400MU_FW_FILE_NAME_v1_3); | ||