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/i2400m/fw.c | |
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/i2400m/fw.c')
-rw-r--r-- | drivers/net/wimax/i2400m/fw.c | 53 |
1 files changed, 33 insertions, 20 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 | } |