diff options
-rw-r--r-- | drivers/net/wimax/i2400m/fw.c | 189 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/i2400m.h | 4 |
2 files changed, 134 insertions, 59 deletions
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c index 1fd2fee4c6c2..897e0be698c6 100644 --- a/drivers/net/wimax/i2400m/fw.c +++ b/drivers/net/wimax/i2400m/fw.c | |||
@@ -78,11 +78,11 @@ | |||
78 | * | 78 | * |
79 | * We can then upload the firmware file. The file is composed of a BCF | 79 | * We can then upload the firmware file. The file is composed of a BCF |
80 | * header (basic data, keys and signatures) and a list of write | 80 | * header (basic data, keys and signatures) and a list of write |
81 | * commands and payloads. We first upload the header | 81 | * commands and payloads. Optionally more BCF headers might follow the |
82 | * [i2400m_dnload_init()] and then pass the commands and payloads | 82 | * main payload. We first upload the header [i2400m_dnload_init()] and |
83 | * verbatim to the i2400m_bm_cmd() function | 83 | * then pass the commands and payloads verbatim to the i2400m_bm_cmd() |
84 | * [i2400m_dnload_bcf()]. Then we tell the device to jump to the new | 84 | * function [i2400m_dnload_bcf()]. Then we tell the device to jump to |
85 | * firmware [i2400m_dnload_finalize()]. | 85 | * the new firmware [i2400m_dnload_finalize()]. |
86 | * | 86 | * |
87 | * Once firmware is uploaded, we are good to go :) | 87 | * Once firmware is uploaded, we are good to go :) |
88 | * | 88 | * |
@@ -115,6 +115,7 @@ | |||
115 | * i2400m_dev_bootstrap Called by __i2400m_dev_start() | 115 | * i2400m_dev_bootstrap Called by __i2400m_dev_start() |
116 | * request_firmware | 116 | * request_firmware |
117 | * i2400m_fw_check | 117 | * i2400m_fw_check |
118 | * i2400m_fw_hdr_check | ||
118 | * i2400m_fw_dnload | 119 | * i2400m_fw_dnload |
119 | * release_firmware | 120 | * release_firmware |
120 | * | 121 | * |
@@ -1151,75 +1152,142 @@ int i2400m_dnload_init(struct i2400m *i2400m, const struct i2400m_bcf_hdr *bcf) | |||
1151 | 1152 | ||
1152 | 1153 | ||
1153 | /* | 1154 | /* |
1154 | * Run quick consistency tests on the firmware file | 1155 | * Run consistency tests on the firmware file and load up headers |
1155 | * | 1156 | * |
1156 | * Check for the firmware being made for the i2400m device, | 1157 | * Check for the firmware being made for the i2400m device, |
1157 | * etc...These checks are mostly informative, as the device will make | 1158 | * etc...These checks are mostly informative, as the device will make |
1158 | * them too; but the driver's response is more informative on what | 1159 | * them too; but the driver's response is more informative on what |
1159 | * went wrong. | 1160 | * went wrong. |
1161 | * | ||
1162 | * This will also look at all the headers present on the firmware | ||
1163 | * file, and update i2400m->fw_bcf_hdr to point to them. | ||
1160 | */ | 1164 | */ |
1161 | static | 1165 | static |
1162 | int i2400m_fw_check(struct i2400m *i2400m, | 1166 | int i2400m_fw_hdr_check(struct i2400m *i2400m, |
1163 | const struct i2400m_bcf_hdr *bcf, | 1167 | const struct i2400m_bcf_hdr *bcf_hdr, |
1164 | size_t bcf_size) | 1168 | size_t index, size_t offset) |
1165 | { | 1169 | { |
1166 | int result; | ||
1167 | struct device *dev = i2400m_dev(i2400m); | 1170 | struct device *dev = i2400m_dev(i2400m); |
1171 | |||
1168 | unsigned module_type, header_len, major_version, minor_version, | 1172 | unsigned module_type, header_len, major_version, minor_version, |
1169 | module_id, module_vendor, date, size; | 1173 | module_id, module_vendor, date, size; |
1170 | 1174 | ||
1171 | /* Check hard errors */ | 1175 | module_type = bcf_hdr->module_type; |
1172 | result = -EINVAL; | 1176 | header_len = sizeof(u32) * le32_to_cpu(bcf_hdr->header_len); |
1173 | if (bcf_size < sizeof(*bcf)) { /* big enough header? */ | 1177 | major_version = (le32_to_cpu(bcf_hdr->header_version) & 0xffff0000) |
1174 | dev_err(dev, "firmware %s too short: " | 1178 | >> 16; |
1175 | "%zu B vs %zu (at least) expected\n", | 1179 | minor_version = le32_to_cpu(bcf_hdr->header_version) & 0x0000ffff; |
1176 | i2400m->fw_name, bcf_size, sizeof(*bcf)); | 1180 | module_id = le32_to_cpu(bcf_hdr->module_id); |
1177 | goto error; | 1181 | module_vendor = le32_to_cpu(bcf_hdr->module_vendor); |
1178 | } | 1182 | date = le32_to_cpu(bcf_hdr->date); |
1179 | 1183 | size = sizeof(u32) * le32_to_cpu(bcf_hdr->size); | |
1180 | module_type = bcf->module_type; | 1184 | |
1181 | header_len = sizeof(u32) * le32_to_cpu(bcf->header_len); | 1185 | d_printf(1, dev, "firmware %s #%d@%08x: BCF header " |
1182 | major_version = (le32_to_cpu(bcf->header_version) & 0xffff0000) >> 16; | 1186 | "type:vendor:id 0x%x:%x:%x v%u.%u (%zu/%zu B) built %08x\n", |
1183 | minor_version = le32_to_cpu(bcf->header_version) & 0x0000ffff; | 1187 | i2400m->fw_name, index, offset, |
1184 | module_id = le32_to_cpu(bcf->module_id); | 1188 | module_type, module_vendor, module_id, |
1185 | module_vendor = le32_to_cpu(bcf->module_vendor); | 1189 | major_version, minor_version, header_len, size, date); |
1186 | date = le32_to_cpu(bcf->date); | 1190 | |
1187 | size = sizeof(u32) * le32_to_cpu(bcf->size); | 1191 | /* Hard errors */ |
1188 | 1192 | if (major_version != 1) { | |
1189 | if (bcf_size != size) { /* annoyingly paranoid */ | 1193 | dev_err(dev, "firmware %s #%d@%08x: major header version " |
1190 | dev_err(dev, "firmware %s: bad size, got " | 1194 | "v%u.%u not supported\n", |
1191 | "%zu B vs %u expected\n", | 1195 | i2400m->fw_name, index, offset, |
1192 | i2400m->fw_name, bcf_size, size); | 1196 | major_version, minor_version); |
1193 | goto error; | 1197 | return -EBADF; |
1194 | } | 1198 | } |
1195 | 1199 | ||
1196 | d_printf(2, dev, "type 0x%x id 0x%x vendor 0x%x; header v%u.%u (%zu B) " | ||
1197 | "date %08x (%zu B)\n", | ||
1198 | module_type, module_id, module_vendor, | ||
1199 | major_version, minor_version, (size_t) header_len, | ||
1200 | date, (size_t) size); | ||
1201 | |||
1202 | if (module_type != 6) { /* built for the right hardware? */ | 1200 | if (module_type != 6) { /* built for the right hardware? */ |
1203 | dev_err(dev, "bad fw %s: unexpected module type 0x%x; " | 1201 | dev_err(dev, "firmware %s #%d@%08x: unexpected module " |
1204 | "aborting\n", i2400m->fw_name, module_type); | 1202 | "type 0x%x; aborting\n", |
1205 | goto error; | 1203 | i2400m->fw_name, index, offset, |
1204 | module_type); | ||
1205 | return -EBADF; | ||
1206 | } | 1206 | } |
1207 | 1207 | ||
1208 | if (major_version != 1) { | 1208 | if (module_vendor != 0x8086) { |
1209 | dev_err(dev, "%s: major header version v%u.%u not supported\n", | 1209 | dev_err(dev, "firmware %s #%d@%08x: unexpected module " |
1210 | i2400m->fw_name, major_version, minor_version); | 1210 | "vendor 0x%x; aborting\n", |
1211 | goto error; | 1211 | i2400m->fw_name, index, offset, module_vendor); |
1212 | return -EBADF; | ||
1212 | } | 1213 | } |
1213 | 1214 | ||
1214 | /* Check soft-er errors */ | ||
1215 | result = 0; | ||
1216 | if (module_vendor != 0x8086) | ||
1217 | dev_err(dev, "bad fw %s? unexpected vendor 0x%04x\n", | ||
1218 | i2400m->fw_name, module_vendor); | ||
1219 | if (date < 0x20080300) | 1215 | if (date < 0x20080300) |
1220 | dev_err(dev, "bad fw %s? build date too old %08x\n", | 1216 | dev_warn(dev, "firmware %s #%d@%08x: build date %08x " |
1221 | i2400m->fw_name, date); | 1217 | "too old; unsupported\n", |
1222 | error: | 1218 | i2400m->fw_name, index, offset, date); |
1219 | return 0; | ||
1220 | } | ||
1221 | |||
1222 | |||
1223 | /* | ||
1224 | * Run consistency tests on the firmware file and load up headers | ||
1225 | * | ||
1226 | * Check for the firmware being made for the i2400m device, | ||
1227 | * etc...These checks are mostly informative, as the device will make | ||
1228 | * them too; but the driver's response is more informative on what | ||
1229 | * went wrong. | ||
1230 | * | ||
1231 | * This will also look at all the headers present on the firmware | ||
1232 | * file, and update i2400m->fw_hdrs to point to them. | ||
1233 | */ | ||
1234 | static | ||
1235 | int i2400m_fw_check(struct i2400m *i2400m, const void *bcf, size_t bcf_size) | ||
1236 | { | ||
1237 | int result; | ||
1238 | struct device *dev = i2400m_dev(i2400m); | ||
1239 | size_t headers = 0; | ||
1240 | const struct i2400m_bcf_hdr *bcf_hdr; | ||
1241 | const void *itr, *next, *top; | ||
1242 | unsigned slots = 0, used_slots = 0; | ||
1243 | |||
1244 | for (itr = bcf, top = itr + bcf_size; | ||
1245 | itr < top; | ||
1246 | headers++, itr = next) { | ||
1247 | size_t leftover, offset, header_len, size; | ||
1248 | |||
1249 | leftover = top - itr; | ||
1250 | offset = itr - (const void *) bcf; | ||
1251 | if (leftover <= sizeof(*bcf_hdr)) { | ||
1252 | dev_err(dev, "firmware %s: %zu B left at @%x, " | ||
1253 | "not enough for BCF header\n", | ||
1254 | i2400m->fw_name, leftover, offset); | ||
1255 | break; | ||
1256 | } | ||
1257 | bcf_hdr = itr; | ||
1258 | /* Only the first header is supposed to be followed by | ||
1259 | * payload */ | ||
1260 | header_len = sizeof(u32) * le32_to_cpu(bcf_hdr->header_len); | ||
1261 | size = sizeof(u32) * le32_to_cpu(bcf_hdr->size); | ||
1262 | if (headers == 0) | ||
1263 | next = itr + size; | ||
1264 | else | ||
1265 | next = itr + header_len; | ||
1266 | |||
1267 | result = i2400m_fw_hdr_check(i2400m, bcf_hdr, headers, offset); | ||
1268 | if (result < 0) | ||
1269 | continue; | ||
1270 | if (used_slots + 1 >= slots) { | ||
1271 | /* +1 -> we need to account for the one we'll | ||
1272 | * occupy and at least an extra one for | ||
1273 | * always being NULL */ | ||
1274 | result = i2400m_zrealloc_2x( | ||
1275 | (void **) &i2400m->fw_hdrs, &slots, | ||
1276 | sizeof(i2400m->fw_hdrs[0]), | ||
1277 | GFP_KERNEL); | ||
1278 | if (result < 0) | ||
1279 | goto error_zrealloc; | ||
1280 | } | ||
1281 | i2400m->fw_hdrs[used_slots] = bcf_hdr; | ||
1282 | used_slots++; | ||
1283 | } | ||
1284 | if (headers == 0) { | ||
1285 | dev_err(dev, "firmware %s: no usable headers found\n", | ||
1286 | i2400m->fw_name); | ||
1287 | result = -EBADF; | ||
1288 | } else | ||
1289 | result = 0; | ||
1290 | error_zrealloc: | ||
1223 | return result; | 1291 | return result; |
1224 | } | 1292 | } |
1225 | 1293 | ||
@@ -1359,13 +1427,16 @@ int i2400m_dev_bootstrap(struct i2400m *i2400m, enum i2400m_bri flags) | |||
1359 | bcf = (void *) fw->data; | 1427 | bcf = (void *) fw->data; |
1360 | i2400m->fw_name = fw_name; | 1428 | i2400m->fw_name = fw_name; |
1361 | ret = i2400m_fw_check(i2400m, bcf, fw->size); | 1429 | ret = i2400m_fw_check(i2400m, bcf, fw->size); |
1362 | if (ret >= 0) { | 1430 | if (ret >= 0) |
1363 | ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags); | 1431 | ret = i2400m_fw_dnload(i2400m, bcf, fw->size, flags); |
1364 | if (ret >= 0) | 1432 | if (ret < 0) |
1365 | break; | 1433 | dev_err(dev, "%s: cannot use: %d, skipping\n", |
1366 | } else | 1434 | fw_name, ret); |
1367 | dev_err(dev, "%s: cannot use, skipping\n", fw_name); | 1435 | kfree(i2400m->fw_hdrs); |
1436 | i2400m->fw_hdrs = NULL; | ||
1368 | release_firmware(fw); | 1437 | release_firmware(fw); |
1438 | if (ret >= 0) /* firmware loaded succesfully */ | ||
1439 | break; | ||
1369 | } | 1440 | } |
1370 | d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); | 1441 | d_fnend(5, dev, "(i2400m %p) = %d\n", i2400m, ret); |
1371 | return ret; | 1442 | return ret; |
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index bcb1882ed741..5ac2cce88ba0 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h | |||
@@ -421,6 +421,9 @@ struct i2400m_barker_db; | |||
421 | * @fw_version: version of the firmware interface, Major.minor, | 421 | * @fw_version: version of the firmware interface, Major.minor, |
422 | * encoded in the high word and low word (major << 16 | minor). | 422 | * encoded in the high word and low word (major << 16 | minor). |
423 | * | 423 | * |
424 | * @fw_hdrs: NULL terminated array of pointers to the firmware | ||
425 | * headers. This is only available during firmware load time. | ||
426 | * | ||
424 | * @barker: barker type that the device uses; this is initialized by | 427 | * @barker: barker type that the device uses; this is initialized by |
425 | * i2400m_is_boot_barker() the first time it is called. Then it | 428 | * i2400m_is_boot_barker() the first time it is called. Then it |
426 | * won't change during the life cycle of the device and everytime | 429 | * won't change during the life cycle of the device and everytime |
@@ -491,6 +494,7 @@ struct i2400m { | |||
491 | struct dentry *debugfs_dentry; | 494 | struct dentry *debugfs_dentry; |
492 | const char *fw_name; /* name of the current firmware image */ | 495 | const char *fw_name; /* name of the current firmware image */ |
493 | unsigned long fw_version; /* version of the firmware interface */ | 496 | unsigned long fw_version; /* version of the firmware interface */ |
497 | const struct i2400m_bcf_hdr **fw_hdrs; | ||
494 | struct i2400m_barker_db *barker; | 498 | struct i2400m_barker_db *barker; |
495 | }; | 499 | }; |
496 | 500 | ||