diff options
-rw-r--r-- | drivers/net/wimax/i2400m/driver.c | 12 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/fw.c | 313 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/i2400m.h | 36 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/rx.c | 22 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/sdio-rx.c | 25 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/usb-notif.c | 32 |
6 files changed, 355 insertions, 85 deletions
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index e3b2c246cad7..73f45ea010a7 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c | |||
@@ -98,6 +98,15 @@ MODULE_PARM_DESC(debug, | |||
98 | "are the different debug submodules and VALUE are the " | 98 | "are the different debug submodules and VALUE are the " |
99 | "initial debug value to set."); | 99 | "initial debug value to set."); |
100 | 100 | ||
101 | static char i2400m_barkers_params[128]; | ||
102 | module_param_string(barkers, i2400m_barkers_params, | ||
103 | sizeof(i2400m_barkers_params), 0644); | ||
104 | MODULE_PARM_DESC(barkers, | ||
105 | "String of comma-separated 32-bit values; each is " | ||
106 | "recognized as the value the device sends as a reboot " | ||
107 | "signal; values are appended to a list--setting one value " | ||
108 | "as zero cleans the existing list and starts a new one."); | ||
109 | |||
101 | /** | 110 | /** |
102 | * i2400m_queue_work - schedule work on a i2400m's queue | 111 | * i2400m_queue_work - schedule work on a i2400m's queue |
103 | * | 112 | * |
@@ -804,7 +813,7 @@ int __init i2400m_driver_init(void) | |||
804 | { | 813 | { |
805 | d_parse_params(D_LEVEL, D_LEVEL_SIZE, i2400m_debug_params, | 814 | d_parse_params(D_LEVEL, D_LEVEL_SIZE, i2400m_debug_params, |
806 | "i2400m.debug"); | 815 | "i2400m.debug"); |
807 | return 0; | 816 | return i2400m_barker_db_init(i2400m_barkers_params); |
808 | } | 817 | } |
809 | module_init(i2400m_driver_init); | 818 | module_init(i2400m_driver_init); |
810 | 819 | ||
@@ -813,6 +822,7 @@ void __exit i2400m_driver_exit(void) | |||
813 | { | 822 | { |
814 | /* for scheds i2400m_dev_reset_handle() */ | 823 | /* for scheds i2400m_dev_reset_handle() */ |
815 | flush_scheduled_work(); | 824 | flush_scheduled_work(); |
825 | i2400m_barker_db_exit(); | ||
816 | return; | 826 | return; |
817 | } | 827 | } |
818 | module_exit(i2400m_driver_exit); | 828 | module_exit(i2400m_driver_exit); |
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c index c962a8d8df7e..798564eb0e99 100644 --- a/drivers/net/wimax/i2400m/fw.c +++ b/drivers/net/wimax/i2400m/fw.c | |||
@@ -40,11 +40,9 @@ | |||
40 | * | 40 | * |
41 | * THE PROCEDURE | 41 | * THE PROCEDURE |
42 | * | 42 | * |
43 | * (this is decribed for USB, but for SDIO is similar) | 43 | * The 2400m and derived devices work in two modes: boot-mode or |
44 | * | 44 | * normal mode. In boot mode we can execute only a handful of commands |
45 | * The 2400m works in two modes: boot-mode or normal mode. In boot | 45 | * targeted at uploading the firmware and launching it. |
46 | * mode we can execute only a handful of commands targeted at | ||
47 | * uploading the firmware and launching it. | ||
48 | * | 46 | * |
49 | * The 2400m enters boot mode when it is first connected to the | 47 | * The 2400m enters boot mode when it is first connected to the |
50 | * system, when it crashes and when you ask it to reboot. There are | 48 | * system, when it crashes and when you ask it to reboot. There are |
@@ -52,18 +50,26 @@ | |||
52 | * firmwares signed with a certain private key, non-signed takes any | 50 | * firmwares signed with a certain private key, non-signed takes any |
53 | * firmware. Normal hardware takes only signed firmware. | 51 | * firmware. Normal hardware takes only signed firmware. |
54 | * | 52 | * |
55 | * Upon entrance to boot mode, the device sends a few zero length | 53 | * On boot mode, in USB, we write to the device using the bulk out |
56 | * packets (ZLPs) on the notification endpoint, then a reboot barker | 54 | * endpoint and read from it in the notification endpoint. In SDIO we |
57 | * (4 le32 words with value I2400M_{S,N}BOOT_BARKER). We ack it by | 55 | * talk to it via the write address and read from the read address. |
58 | * sending the same barker on the bulk out endpoint. The device acks | 56 | * |
59 | * with a reboot ack barker (4 le32 words with value 0xfeedbabe) and | 57 | * Upon entrance to boot mode, the device sends (preceeded with a few |
60 | * then the device is fully rebooted. At this point we can upload the | 58 | * zero length packets (ZLPs) on the notification endpoint in USB) a |
61 | * firmware. | 59 | * reboot barker (4 le32 words with the same value). We ack it by |
60 | * sending the same barker to the device. The device acks with a | ||
61 | * reboot ack barker (4 le32 words with value I2400M_ACK_BARKER) and | ||
62 | * then is fully booted. At this point we can upload the firmware. | ||
63 | * | ||
64 | * Note that different iterations of the device and EEPROM | ||
65 | * configurations will send different [re]boot barkers; these are | ||
66 | * collected in i2400m_barker_db along with the firmware | ||
67 | * characteristics they require. | ||
62 | * | 68 | * |
63 | * This process is accomplished by the i2400m_bootrom_init() | 69 | * This process is accomplished by the i2400m_bootrom_init() |
64 | * function. All the device interaction happens through the | 70 | * function. All the device interaction happens through the |
65 | * i2400m_bm_cmd() [boot mode command]. Special return values will | 71 | * i2400m_bm_cmd() [boot mode command]. Special return values will |
66 | * indicate if the device resets. | 72 | * indicate if the device did reset during the process. |
67 | * | 73 | * |
68 | * After this, we read the MAC address and then (if needed) | 74 | * After this, we read the MAC address and then (if needed) |
69 | * reinitialize the device. We need to read it ahead of time because | 75 | * reinitialize the device. We need to read it ahead of time because |
@@ -101,6 +107,11 @@ | |||
101 | * | 107 | * |
102 | * ROADMAP | 108 | * ROADMAP |
103 | * | 109 | * |
110 | * i2400m_barker_db_init Called by i2400m_driver_init() | ||
111 | * i2400m_barker_db_add | ||
112 | * | ||
113 | * i2400m_barker_db_exit Called by i2400m_driver_exit() | ||
114 | * | ||
104 | * i2400m_dev_bootstrap Called by __i2400m_dev_start() | 115 | * i2400m_dev_bootstrap Called by __i2400m_dev_start() |
105 | * request_firmware | 116 | * request_firmware |
106 | * i2400m_fw_check | 117 | * i2400m_fw_check |
@@ -125,6 +136,7 @@ | |||
125 | * i2400m->bus_bm_cmd_send() | 136 | * i2400m->bus_bm_cmd_send() |
126 | * i2400m->bus_bm_wait_for_ack | 137 | * i2400m->bus_bm_wait_for_ack |
127 | * __i2400m_bm_ack_verify | 138 | * __i2400m_bm_ack_verify |
139 | * i2400m_is_boot_barker | ||
128 | * | 140 | * |
129 | * i2400m_bm_cmd_prepare Used by bus-drivers to prep | 141 | * i2400m_bm_cmd_prepare Used by bus-drivers to prep |
130 | * commands before sending | 142 | * commands before sending |
@@ -175,6 +187,237 @@ EXPORT_SYMBOL_GPL(i2400m_bm_cmd_prepare); | |||
175 | 187 | ||
176 | 188 | ||
177 | /* | 189 | /* |
190 | * Database of known barkers. | ||
191 | * | ||
192 | * A barker is what the device sends indicating he is ready to be | ||
193 | * bootloaded. Different versions of the device will send different | ||
194 | * barkers. Depending on the barker, it might mean the device wants | ||
195 | * some kind of firmware or the other. | ||
196 | */ | ||
197 | static struct i2400m_barker_db { | ||
198 | __le32 data[4]; | ||
199 | } *i2400m_barker_db; | ||
200 | static size_t i2400m_barker_db_used, i2400m_barker_db_size; | ||
201 | |||
202 | |||
203 | static | ||
204 | int i2400m_zrealloc_2x(void **ptr, size_t *_count, size_t el_size, | ||
205 | gfp_t gfp_flags) | ||
206 | { | ||
207 | size_t old_count = *_count, | ||
208 | new_count = old_count ? 2 * old_count : 2, | ||
209 | old_size = el_size * old_count, | ||
210 | new_size = el_size * new_count; | ||
211 | void *nptr = krealloc(*ptr, new_size, gfp_flags); | ||
212 | if (nptr) { | ||
213 | /* zero the other half or the whole thing if old_count | ||
214 | * was zero */ | ||
215 | if (old_size == 0) | ||
216 | memset(nptr, 0, new_size); | ||
217 | else | ||
218 | memset(nptr + old_size, 0, old_size); | ||
219 | *_count = new_count; | ||
220 | *ptr = nptr; | ||
221 | return 0; | ||
222 | } else | ||
223 | return -ENOMEM; | ||
224 | } | ||
225 | |||
226 | |||
227 | /* | ||
228 | * Add a barker to the database | ||
229 | * | ||
230 | * This cannot used outside of this module and only at at module_init | ||
231 | * time. This is to avoid the need to do locking. | ||
232 | */ | ||
233 | static | ||
234 | int i2400m_barker_db_add(u32 barker_id) | ||
235 | { | ||
236 | int result; | ||
237 | |||
238 | struct i2400m_barker_db *barker; | ||
239 | if (i2400m_barker_db_used >= i2400m_barker_db_size) { | ||
240 | result = i2400m_zrealloc_2x( | ||
241 | (void **) &i2400m_barker_db, &i2400m_barker_db_size, | ||
242 | sizeof(i2400m_barker_db[0]), GFP_KERNEL); | ||
243 | if (result < 0) | ||
244 | return result; | ||
245 | } | ||
246 | barker = i2400m_barker_db + i2400m_barker_db_used++; | ||
247 | barker->data[0] = le32_to_cpu(barker_id); | ||
248 | barker->data[1] = le32_to_cpu(barker_id); | ||
249 | barker->data[2] = le32_to_cpu(barker_id); | ||
250 | barker->data[3] = le32_to_cpu(barker_id); | ||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | |||
255 | void i2400m_barker_db_exit(void) | ||
256 | { | ||
257 | kfree(i2400m_barker_db); | ||
258 | i2400m_barker_db = NULL; | ||
259 | i2400m_barker_db_size = 0; | ||
260 | i2400m_barker_db_used = 0; | ||
261 | } | ||
262 | |||
263 | |||
264 | /* | ||
265 | * Helper function to add all the known stable barkers to the barker | ||
266 | * database. | ||
267 | */ | ||
268 | static | ||
269 | int i2400m_barker_db_known_barkers(void) | ||
270 | { | ||
271 | int result; | ||
272 | |||
273 | result = i2400m_barker_db_add(I2400M_NBOOT_BARKER); | ||
274 | if (result < 0) | ||
275 | goto error_add; | ||
276 | result = i2400m_barker_db_add(I2400M_SBOOT_BARKER); | ||
277 | if (result < 0) | ||
278 | goto error_add; | ||
279 | error_add: | ||
280 | return result; | ||
281 | } | ||
282 | |||
283 | |||
284 | /* | ||
285 | * Initialize the barker database | ||
286 | * | ||
287 | * This can only be used from the module_init function for this | ||
288 | * module; this is to avoid the need to do locking. | ||
289 | * | ||
290 | * @options: command line argument with extra barkers to | ||
291 | * recognize. This is a comma-separated list of 32-bit hex | ||
292 | * numbers. They are appended to the existing list. Setting 0 | ||
293 | * cleans the existing list and starts a new one. | ||
294 | */ | ||
295 | int i2400m_barker_db_init(const char *_options) | ||
296 | { | ||
297 | int result; | ||
298 | char *options = NULL, *options_orig, *token; | ||
299 | |||
300 | i2400m_barker_db = NULL; | ||
301 | i2400m_barker_db_size = 0; | ||
302 | i2400m_barker_db_used = 0; | ||
303 | |||
304 | result = i2400m_barker_db_known_barkers(); | ||
305 | if (result < 0) | ||
306 | goto error_add; | ||
307 | /* parse command line options from i2400m.barkers */ | ||
308 | if (_options != NULL) { | ||
309 | unsigned barker; | ||
310 | |||
311 | options_orig = kstrdup(_options, GFP_KERNEL); | ||
312 | if (options_orig == NULL) | ||
313 | goto error_parse; | ||
314 | options = options_orig; | ||
315 | |||
316 | while ((token = strsep(&options, ",")) != NULL) { | ||
317 | if (*token == '\0') /* eat joint commas */ | ||
318 | continue; | ||
319 | if (sscanf(token, "%x", &barker) != 1 | ||
320 | || barker > 0xffffffff) { | ||
321 | printk(KERN_ERR "%s: can't recognize " | ||
322 | "i2400m.barkers value '%s' as " | ||
323 | "a 32-bit number\n", | ||
324 | __func__, token); | ||
325 | result = -EINVAL; | ||
326 | goto error_parse; | ||
327 | } | ||
328 | if (barker == 0) { | ||
329 | /* clean list and start new */ | ||
330 | i2400m_barker_db_exit(); | ||
331 | continue; | ||
332 | } | ||
333 | result = i2400m_barker_db_add(barker); | ||
334 | if (result < 0) | ||
335 | goto error_add; | ||
336 | } | ||
337 | kfree(options_orig); | ||
338 | } | ||
339 | return 0; | ||
340 | |||
341 | error_parse: | ||
342 | error_add: | ||
343 | kfree(i2400m_barker_db); | ||
344 | return result; | ||
345 | } | ||
346 | |||
347 | |||
348 | /* | ||
349 | * Recognize a boot barker | ||
350 | * | ||
351 | * @buf: buffer where the boot barker. | ||
352 | * @buf_size: size of the buffer (has to be 16 bytes). It is passed | ||
353 | * here so the function can check it for the caller. | ||
354 | * | ||
355 | * Note that as a side effect, upon identifying the obtained boot | ||
356 | * barker, this function will set i2400m->barker to point to the right | ||
357 | * barker database entry. Subsequent calls to the function will result | ||
358 | * in verifying that the same type of boot barker is returned when the | ||
359 | * device [re]boots (as long as the same device instance is used). | ||
360 | * | ||
361 | * Return: 0 if @buf matches a known boot barker. -ENOENT if the | ||
362 | * buffer in @buf doesn't match any boot barker in the database or | ||
363 | * -EILSEQ if the buffer doesn't have the right size. | ||
364 | */ | ||
365 | int i2400m_is_boot_barker(struct i2400m *i2400m, | ||
366 | const void *buf, size_t buf_size) | ||
367 | { | ||
368 | int result; | ||
369 | struct device *dev = i2400m_dev(i2400m); | ||
370 | struct i2400m_barker_db *barker; | ||
371 | int i; | ||
372 | |||
373 | result = -ENOENT; | ||
374 | if (buf_size != sizeof(i2400m_barker_db[i].data)) | ||
375 | return result; | ||
376 | |||
377 | /* Short circuit if we have already discovered the barker | ||
378 | * associated with the device. */ | ||
379 | if (i2400m->barker | ||
380 | && !memcmp(buf, i2400m->barker, sizeof(i2400m->barker->data))) { | ||
381 | unsigned index = (i2400m->barker - i2400m_barker_db) | ||
382 | / sizeof(*i2400m->barker); | ||
383 | d_printf(2, dev, "boot barker cache-confirmed #%u/%08x\n", | ||
384 | index, le32_to_cpu(i2400m->barker->data[0])); | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | for (i = 0; i < i2400m_barker_db_used; i++) { | ||
389 | barker = &i2400m_barker_db[i]; | ||
390 | BUILD_BUG_ON(sizeof(barker->data) != 16); | ||
391 | if (memcmp(buf, barker->data, sizeof(barker->data))) | ||
392 | continue; | ||
393 | |||
394 | if (i2400m->barker == NULL) { | ||
395 | i2400m->barker = barker; | ||
396 | d_printf(1, dev, "boot barker set to #%u/%08x\n", | ||
397 | i, le32_to_cpu(barker->data[0])); | ||
398 | if (barker->data[0] == le32_to_cpu(I2400M_NBOOT_BARKER)) | ||
399 | i2400m->sboot = 0; | ||
400 | else | ||
401 | i2400m->sboot = 1; | ||
402 | } else if (i2400m->barker != barker) { | ||
403 | dev_err(dev, "HW inconsistency: device " | ||
404 | "reports a different boot barker " | ||
405 | "than set (from %08x to %08x)\n", | ||
406 | le32_to_cpu(i2400m->barker->data[0]), | ||
407 | le32_to_cpu(barker->data[0])); | ||
408 | result = -EIO; | ||
409 | } else | ||
410 | d_printf(2, dev, "boot barker confirmed #%u/%08x\n", | ||
411 | i, le32_to_cpu(barker->data[0])); | ||
412 | result = 0; | ||
413 | break; | ||
414 | } | ||
415 | return result; | ||
416 | } | ||
417 | EXPORT_SYMBOL_GPL(i2400m_is_boot_barker); | ||
418 | |||
419 | |||
420 | /* | ||
178 | * Verify the ack data received | 421 | * Verify the ack data received |
179 | * | 422 | * |
180 | * Given a reply to a boot mode command, chew it and verify everything | 423 | * Given a reply to a boot mode command, chew it and verify everything |
@@ -204,20 +447,10 @@ ssize_t __i2400m_bm_ack_verify(struct i2400m *i2400m, int opcode, | |||
204 | opcode, ack_size, sizeof(*ack)); | 447 | opcode, ack_size, sizeof(*ack)); |
205 | goto error_ack_short; | 448 | goto error_ack_short; |
206 | } | 449 | } |
207 | if (ack_size == sizeof(i2400m_NBOOT_BARKER) | 450 | result = i2400m_is_boot_barker(i2400m, ack, ack_size); |
208 | && memcmp(ack, i2400m_NBOOT_BARKER, sizeof(*ack)) == 0) { | 451 | if (result >= 0) { |
209 | result = -ERESTARTSYS; | ||
210 | i2400m->sboot = 0; | ||
211 | d_printf(6, dev, "boot-mode cmd %d: " | ||
212 | "HW non-signed boot barker\n", opcode); | ||
213 | goto error_reboot; | ||
214 | } | ||
215 | if (ack_size == sizeof(i2400m_SBOOT_BARKER) | ||
216 | && memcmp(ack, i2400m_SBOOT_BARKER, sizeof(*ack)) == 0) { | ||
217 | result = -ERESTARTSYS; | 452 | result = -ERESTARTSYS; |
218 | i2400m->sboot = 1; | 453 | d_printf(6, dev, "boot-mode cmd %d: HW boot barker\n", opcode); |
219 | d_printf(6, dev, "boot-mode cmd %d: HW signed reboot barker\n", | ||
220 | opcode); | ||
221 | goto error_reboot; | 454 | goto error_reboot; |
222 | } | 455 | } |
223 | if (ack_size == sizeof(i2400m_ACK_BARKER) | 456 | if (ack_size == sizeof(i2400m_ACK_BARKER) |
@@ -590,9 +823,6 @@ int i2400m_dnload_finalize(struct i2400m *i2400m, | |||
590 | * | 823 | * |
591 | * < 0 errno code on error, 0 if ok. | 824 | * < 0 errno code on error, 0 if ok. |
592 | * | 825 | * |
593 | * i2400m->sboot set to 0 for unsecure boot process, 1 for secure | ||
594 | * boot process. | ||
595 | * | ||
596 | * Description: | 826 | * Description: |
597 | * | 827 | * |
598 | * Tries hard enough to put the device in boot-mode. There are two | 828 | * Tries hard enough to put the device in boot-mode. There are two |
@@ -619,7 +849,7 @@ int i2400m_bootrom_init(struct i2400m *i2400m, enum i2400m_bri flags) | |||
619 | int count = i2400m->bus_bm_retries; | 849 | int count = i2400m->bus_bm_retries; |
620 | int ack_timeout_cnt = 1; | 850 | int ack_timeout_cnt = 1; |
621 | 851 | ||
622 | BUILD_BUG_ON(sizeof(*cmd) != sizeof(i2400m_NBOOT_BARKER)); | 852 | BUILD_BUG_ON(sizeof(*cmd) != sizeof(i2400m_barker_db[0].data)); |
623 | BUILD_BUG_ON(sizeof(ack) != sizeof(i2400m_ACK_BARKER)); | 853 | BUILD_BUG_ON(sizeof(ack) != sizeof(i2400m_ACK_BARKER)); |
624 | 854 | ||
625 | d_fnstart(4, dev, "(i2400m %p flags 0x%08x)\n", i2400m, flags); | 855 | d_fnstart(4, dev, "(i2400m %p flags 0x%08x)\n", i2400m, flags); |
@@ -647,8 +877,14 @@ do_reboot: | |||
647 | case -ETIMEDOUT: /* device has timed out, we might be in boot | 877 | case -ETIMEDOUT: /* device has timed out, we might be in boot |
648 | * mode already and expecting an ack, let's try | 878 | * mode already and expecting an ack, let's try |
649 | * that */ | 879 | * that */ |
650 | dev_info(dev, "warm reset timed out, trying an ack\n"); | 880 | if (i2400m->barker == NULL) { |
651 | goto do_reboot_ack; | 881 | dev_info(dev, "warm reset timed out, unknown barker " |
882 | "type, rebooting\n"); | ||
883 | goto do_reboot; | ||
884 | } else { | ||
885 | dev_info(dev, "warm reset timed out, trying an ack\n"); | ||
886 | goto do_reboot_ack; | ||
887 | } | ||
652 | case -EPROTO: | 888 | case -EPROTO: |
653 | case -ESHUTDOWN: /* dev is gone */ | 889 | case -ESHUTDOWN: /* dev is gone */ |
654 | case -EINTR: /* user cancelled */ | 890 | case -EINTR: /* user cancelled */ |
@@ -664,12 +900,7 @@ do_reboot: | |||
664 | * notification and report it as -EISCONN. */ | 900 | * notification and report it as -EISCONN. */ |
665 | do_reboot_ack: | 901 | do_reboot_ack: |
666 | d_printf(4, dev, "device reboot ack: sending ack [%d # left]\n", count); | 902 | d_printf(4, dev, "device reboot ack: sending ack [%d # left]\n", count); |
667 | if (i2400m->sboot == 0) | 903 | memcpy(cmd, i2400m->barker->data, sizeof(i2400m->barker->data)); |
668 | memcpy(cmd, i2400m_NBOOT_BARKER, | ||
669 | sizeof(i2400m_NBOOT_BARKER)); | ||
670 | else | ||
671 | memcpy(cmd, i2400m_SBOOT_BARKER, | ||
672 | sizeof(i2400m_SBOOT_BARKER)); | ||
673 | result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), | 904 | result = i2400m_bm_cmd(i2400m, cmd, sizeof(*cmd), |
674 | &ack, sizeof(ack), I2400M_BM_CMD_RAW); | 905 | &ack, sizeof(ack), I2400M_BM_CMD_RAW); |
675 | switch (result) { | 906 | switch (result) { |
@@ -682,10 +913,8 @@ do_reboot_ack: | |||
682 | d_printf(4, dev, "reboot ack: got ack barker - good\n"); | 913 | d_printf(4, dev, "reboot ack: got ack barker - good\n"); |
683 | break; | 914 | break; |
684 | case -ETIMEDOUT: /* no response, maybe it is the other type? */ | 915 | case -ETIMEDOUT: /* no response, maybe it is the other type? */ |
685 | if (ack_timeout_cnt-- >= 0) { | 916 | if (ack_timeout_cnt-- < 0) { |
686 | d_printf(4, dev, "reboot ack timedout: " | 917 | d_printf(4, dev, "reboot ack timedout: retrying\n"); |
687 | "trying the other type?\n"); | ||
688 | i2400m->sboot = !i2400m->sboot; | ||
689 | goto do_reboot_ack; | 918 | goto do_reboot_ack; |
690 | } else { | 919 | } else { |
691 | dev_err(dev, "reboot ack timedout too long: " | 920 | dev_err(dev, "reboot ack timedout too long: " |
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 73b4e6a15135..bcb1882ed741 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h | |||
@@ -194,6 +194,7 @@ enum i2400m_reset_type { | |||
194 | 194 | ||
195 | struct i2400m_reset_ctx; | 195 | struct i2400m_reset_ctx; |
196 | struct i2400m_roq; | 196 | struct i2400m_roq; |
197 | struct i2400m_barker_db; | ||
197 | 198 | ||
198 | /** | 199 | /** |
199 | * struct i2400m - descriptor for an Intel 2400m | 200 | * struct i2400m - descriptor for an Intel 2400m |
@@ -419,6 +420,12 @@ struct i2400m_roq; | |||
419 | * | 420 | * |
420 | * @fw_version: version of the firmware interface, Major.minor, | 421 | * @fw_version: version of the firmware interface, Major.minor, |
421 | * encoded in the high word and low word (major << 16 | minor). | 422 | * encoded in the high word and low word (major << 16 | minor). |
423 | * | ||
424 | * @barker: barker type that the device uses; this is initialized by | ||
425 | * 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 | ||
427 | * a boot barker is received, it is just verified for it being the | ||
428 | * same. | ||
422 | */ | 429 | */ |
423 | struct i2400m { | 430 | struct i2400m { |
424 | struct wimax_dev wimax_dev; /* FIRST! See doc */ | 431 | struct wimax_dev wimax_dev; /* FIRST! See doc */ |
@@ -484,6 +491,7 @@ struct i2400m { | |||
484 | struct dentry *debugfs_dentry; | 491 | struct dentry *debugfs_dentry; |
485 | const char *fw_name; /* name of the current firmware image */ | 492 | const char *fw_name; /* name of the current firmware image */ |
486 | unsigned long fw_version; /* version of the firmware interface */ | 493 | unsigned long fw_version; /* version of the firmware interface */ |
494 | struct i2400m_barker_db *barker; | ||
487 | }; | 495 | }; |
488 | 496 | ||
489 | 497 | ||
@@ -574,6 +582,14 @@ extern void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *); | |||
574 | extern int i2400m_dev_bootstrap(struct i2400m *, enum i2400m_bri); | 582 | extern int i2400m_dev_bootstrap(struct i2400m *, enum i2400m_bri); |
575 | extern int i2400m_read_mac_addr(struct i2400m *); | 583 | extern int i2400m_read_mac_addr(struct i2400m *); |
576 | extern int i2400m_bootrom_init(struct i2400m *, enum i2400m_bri); | 584 | extern int i2400m_bootrom_init(struct i2400m *, enum i2400m_bri); |
585 | extern int i2400m_is_boot_barker(struct i2400m *, const void *, size_t); | ||
586 | static inline | ||
587 | int i2400m_is_d2h_barker(const void *buf) | ||
588 | { | ||
589 | const __le32 *barker = buf; | ||
590 | return le32_to_cpu(*barker) == I2400M_D2H_MSG_BARKER; | ||
591 | } | ||
592 | extern void i2400m_unknown_barker(struct i2400m *, const void *, size_t); | ||
577 | 593 | ||
578 | /* Make/grok boot-rom header commands */ | 594 | /* Make/grok boot-rom header commands */ |
579 | 595 | ||
@@ -736,20 +752,6 @@ extern int i2400m_rx(struct i2400m *, struct sk_buff *); | |||
736 | extern struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *); | 752 | extern struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *); |
737 | extern void i2400m_tx_msg_sent(struct i2400m *); | 753 | extern void i2400m_tx_msg_sent(struct i2400m *); |
738 | 754 | ||
739 | static const __le32 i2400m_NBOOT_BARKER[4] = { | ||
740 | cpu_to_le32(I2400M_NBOOT_BARKER), | ||
741 | cpu_to_le32(I2400M_NBOOT_BARKER), | ||
742 | cpu_to_le32(I2400M_NBOOT_BARKER), | ||
743 | cpu_to_le32(I2400M_NBOOT_BARKER) | ||
744 | }; | ||
745 | |||
746 | static const __le32 i2400m_SBOOT_BARKER[4] = { | ||
747 | cpu_to_le32(I2400M_SBOOT_BARKER), | ||
748 | cpu_to_le32(I2400M_SBOOT_BARKER), | ||
749 | cpu_to_le32(I2400M_SBOOT_BARKER), | ||
750 | cpu_to_le32(I2400M_SBOOT_BARKER) | ||
751 | }; | ||
752 | |||
753 | extern int i2400m_power_save_disabled; | 755 | extern int i2400m_power_save_disabled; |
754 | 756 | ||
755 | /* | 757 | /* |
@@ -848,6 +850,12 @@ void __i2400m_msleep(unsigned ms) | |||
848 | #endif | 850 | #endif |
849 | } | 851 | } |
850 | 852 | ||
853 | |||
854 | /* module initialization helpers */ | ||
855 | extern int i2400m_barker_db_init(const char *); | ||
856 | extern void i2400m_barker_db_exit(void); | ||
857 | |||
858 | |||
851 | /* Module parameters */ | 859 | /* Module parameters */ |
852 | 860 | ||
853 | extern int i2400m_idle_mode_disabled; | 861 | extern int i2400m_idle_mode_disabled; |
diff --git a/drivers/net/wimax/i2400m/rx.c b/drivers/net/wimax/i2400m/rx.c index 07c32e68909f..bcd411f1a854 100644 --- a/drivers/net/wimax/i2400m/rx.c +++ b/drivers/net/wimax/i2400m/rx.c | |||
@@ -1194,6 +1194,28 @@ error_msg_hdr_check: | |||
1194 | EXPORT_SYMBOL_GPL(i2400m_rx); | 1194 | EXPORT_SYMBOL_GPL(i2400m_rx); |
1195 | 1195 | ||
1196 | 1196 | ||
1197 | void i2400m_unknown_barker(struct i2400m *i2400m, | ||
1198 | const void *buf, size_t size) | ||
1199 | { | ||
1200 | struct device *dev = i2400m_dev(i2400m); | ||
1201 | char prefix[64]; | ||
1202 | const __le32 *barker = buf; | ||
1203 | dev_err(dev, "RX: HW BUG? unknown barker %08x, " | ||
1204 | "dropping %zu bytes\n", le32_to_cpu(*barker), size); | ||
1205 | snprintf(prefix, sizeof(prefix), "%s %s: ", | ||
1206 | dev_driver_string(dev), dev_name(dev)); | ||
1207 | if (size > 64) { | ||
1208 | print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, | ||
1209 | 8, 4, buf, 64, 0); | ||
1210 | printk(KERN_ERR "%s... (only first 64 bytes " | ||
1211 | "dumped)\n", prefix); | ||
1212 | } else | ||
1213 | print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, | ||
1214 | 8, 4, buf, size, 0); | ||
1215 | } | ||
1216 | EXPORT_SYMBOL(i2400m_unknown_barker); | ||
1217 | |||
1218 | |||
1197 | /* | 1219 | /* |
1198 | * Initialize the RX queue and infrastructure | 1220 | * Initialize the RX queue and infrastructure |
1199 | * | 1221 | * |
diff --git a/drivers/net/wimax/i2400m/sdio-rx.c b/drivers/net/wimax/i2400m/sdio-rx.c index 1c9046914bd1..87263be4eedb 100644 --- a/drivers/net/wimax/i2400m/sdio-rx.c +++ b/drivers/net/wimax/i2400m/sdio-rx.c | |||
@@ -53,6 +53,7 @@ | |||
53 | * i2400ms_irq() | 53 | * i2400ms_irq() |
54 | * i2400ms_rx() | 54 | * i2400ms_rx() |
55 | * __i2400ms_rx_get_size() | 55 | * __i2400ms_rx_get_size() |
56 | * i2400m_is_boot_barker() | ||
56 | * i2400m_rx() | 57 | * i2400m_rx() |
57 | * | 58 | * |
58 | * i2400ms_rx_setup() | 59 | * i2400ms_rx_setup() |
@@ -158,7 +159,7 @@ void i2400ms_rx(struct i2400ms *i2400ms) | |||
158 | } | 159 | } |
159 | 160 | ||
160 | rmb(); /* make sure we get boot_mode from dev_reset_handle */ | 161 | rmb(); /* make sure we get boot_mode from dev_reset_handle */ |
161 | if (i2400m->boot_mode == 1) { | 162 | if (unlikely(i2400m->boot_mode == 1)) { |
162 | spin_lock(&i2400m->rx_lock); | 163 | spin_lock(&i2400m->rx_lock); |
163 | i2400ms->bm_ack_size = rx_size; | 164 | i2400ms->bm_ack_size = rx_size; |
164 | spin_unlock(&i2400m->rx_lock); | 165 | spin_unlock(&i2400m->rx_lock); |
@@ -166,17 +167,26 @@ void i2400ms_rx(struct i2400ms *i2400ms) | |||
166 | wake_up(&i2400ms->bm_wfa_wq); | 167 | wake_up(&i2400ms->bm_wfa_wq); |
167 | dev_err(dev, "RX: SDIO boot mode message\n"); | 168 | dev_err(dev, "RX: SDIO boot mode message\n"); |
168 | kfree_skb(skb); | 169 | kfree_skb(skb); |
169 | } else if (unlikely(!memcmp(skb->data, i2400m_NBOOT_BARKER, | 170 | goto out; |
170 | sizeof(i2400m_NBOOT_BARKER)) | 171 | } |
171 | || !memcmp(skb->data, i2400m_SBOOT_BARKER, | 172 | ret = -EIO; |
172 | sizeof(i2400m_SBOOT_BARKER)))) { | 173 | if (unlikely(rx_size < sizeof(__le32))) { |
174 | dev_err(dev, "HW BUG? only %zu bytes received\n", rx_size); | ||
175 | goto error_bad_size; | ||
176 | } | ||
177 | if (likely(i2400m_is_d2h_barker(skb->data))) { | ||
178 | skb_put(skb, rx_size); | ||
179 | i2400m_rx(i2400m, skb); | ||
180 | } else if (unlikely(i2400m_is_boot_barker(i2400m, | ||
181 | skb->data, rx_size))) { | ||
173 | ret = i2400m_dev_reset_handle(i2400m); | 182 | ret = i2400m_dev_reset_handle(i2400m); |
174 | dev_err(dev, "RX: SDIO reboot barker\n"); | 183 | dev_err(dev, "RX: SDIO reboot barker\n"); |
175 | kfree_skb(skb); | 184 | kfree_skb(skb); |
176 | } else { | 185 | } else { |
177 | skb_put(skb, rx_size); | 186 | i2400m_unknown_barker(i2400m, skb->data, rx_size); |
178 | i2400m_rx(i2400m, skb); | 187 | kfree_skb(skb); |
179 | } | 188 | } |
189 | out: | ||
180 | d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms); | 190 | d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms); |
181 | return; | 191 | return; |
182 | 192 | ||
@@ -184,6 +194,7 @@ error_memcpy_fromio: | |||
184 | kfree_skb(skb); | 194 | kfree_skb(skb); |
185 | error_alloc_skb: | 195 | error_alloc_skb: |
186 | error_get_size: | 196 | error_get_size: |
197 | error_bad_size: | ||
187 | d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret); | 198 | d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret); |
188 | return; | 199 | return; |
189 | } | 200 | } |
diff --git a/drivers/net/wimax/i2400m/usb-notif.c b/drivers/net/wimax/i2400m/usb-notif.c index 3e11e35cd696..a0751a347cdc 100644 --- a/drivers/net/wimax/i2400m/usb-notif.c +++ b/drivers/net/wimax/i2400m/usb-notif.c | |||
@@ -51,6 +51,7 @@ | |||
51 | * | 51 | * |
52 | * i2400mu_usb_notification_cb() Called when a URB is ready | 52 | * i2400mu_usb_notification_cb() Called when a URB is ready |
53 | * i2400mu_notif_grok() | 53 | * i2400mu_notif_grok() |
54 | * i2400m_is_boot_barker() | ||
54 | * i2400m_dev_reset_handle() | 55 | * i2400m_dev_reset_handle() |
55 | * i2400mu_rx_kick() | 56 | * i2400mu_rx_kick() |
56 | */ | 57 | */ |
@@ -87,32 +88,21 @@ int i2400mu_notification_grok(struct i2400mu *i2400mu, const void *buf, | |||
87 | d_fnstart(4, dev, "(i2400m %p buf %p buf_len %zu)\n", | 88 | d_fnstart(4, dev, "(i2400m %p buf %p buf_len %zu)\n", |
88 | i2400mu, buf, buf_len); | 89 | i2400mu, buf, buf_len); |
89 | ret = -EIO; | 90 | ret = -EIO; |
90 | if (buf_len < sizeof(i2400m_NBOOT_BARKER)) | 91 | if (buf_len < sizeof(i2400m_ZERO_BARKER)) |
91 | /* Not a bug, just ignore */ | 92 | /* Not a bug, just ignore */ |
92 | goto error_bad_size; | 93 | goto error_bad_size; |
93 | if (!memcmp(i2400m_NBOOT_BARKER, buf, sizeof(i2400m_NBOOT_BARKER)) | 94 | ret = 0; |
94 | || !memcmp(i2400m_SBOOT_BARKER, buf, sizeof(i2400m_SBOOT_BARKER))) | 95 | if (!memcmp(i2400m_ZERO_BARKER, buf, sizeof(i2400m_ZERO_BARKER))) { |
95 | ret = i2400m_dev_reset_handle(i2400m); | ||
96 | else if (!memcmp(i2400m_ZERO_BARKER, buf, sizeof(i2400m_ZERO_BARKER))) { | ||
97 | i2400mu_rx_kick(i2400mu); | 96 | i2400mu_rx_kick(i2400mu); |
98 | ret = 0; | 97 | goto out; |
99 | } else { /* Unknown or unexpected data in the notif message */ | ||
100 | char prefix[64]; | ||
101 | ret = -EIO; | ||
102 | dev_err(dev, "HW BUG? Unknown/unexpected data in notification " | ||
103 | "message (%zu bytes)\n", buf_len); | ||
104 | snprintf(prefix, sizeof(prefix), "%s %s: ", | ||
105 | dev_driver_string(dev), dev_name(dev)); | ||
106 | if (buf_len > 64) { | ||
107 | print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, | ||
108 | 8, 4, buf, 64, 0); | ||
109 | printk(KERN_ERR "%s... (only first 64 bytes " | ||
110 | "dumped)\n", prefix); | ||
111 | } else | ||
112 | print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET, | ||
113 | 8, 4, buf, buf_len, 0); | ||
114 | } | 98 | } |
99 | ret = i2400m_is_boot_barker(i2400m, buf, buf_len); | ||
100 | if (unlikely(ret >= 0)) | ||
101 | ret = i2400m_dev_reset_handle(i2400m); | ||
102 | else /* Unknown or unexpected data in the notif message */ | ||
103 | i2400m_unknown_barker(i2400m, buf, buf_len); | ||
115 | error_bad_size: | 104 | error_bad_size: |
105 | out: | ||
116 | d_fnend(4, dev, "(i2400m %p buf %p buf_len %zu) = %d\n", | 106 | d_fnend(4, dev, "(i2400m %p buf %p buf_len %zu) = %d\n", |
117 | i2400mu, buf, buf_len, ret); | 107 | i2400mu, buf, buf_len, ret); |
118 | return ret; | 108 | return ret; |