diff options
author | David Kilroy <kilroyd@gmail.com> | 2008-08-21 18:27:54 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-08-22 19:28:05 -0400 |
commit | 3994d502017a2239e30152d1231843ad05d04a7b (patch) | |
tree | c4f15d1b95783d035d954173167ff08e65665461 /drivers/net/wireless/orinoco.c | |
parent | 8f5ae73c5366128d3800cf9765507422bcf1ef96 (diff) |
orinoco: Invoke firmware download in main driver
Firmware download is enabled for Agere in orinoco_cs. Symbol firmware
download has been moved out of spectrum_cs into orinoco_cs. Firmware
download is not enabled for Intersil.
Symbol based firmware is restricted to only download on spectrum_cs
based cards.
The firmware names are hardcoded for each firmware type.
Signed-off-by: David Kilroy <kilroyd@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/orinoco.c')
-rw-r--r-- | drivers/net/wireless/orinoco.c | 314 |
1 files changed, 312 insertions, 2 deletions
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 00b1d595fa3c..306697aa3330 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c | |||
@@ -82,12 +82,14 @@ | |||
82 | #include <linux/netdevice.h> | 82 | #include <linux/netdevice.h> |
83 | #include <linux/etherdevice.h> | 83 | #include <linux/etherdevice.h> |
84 | #include <linux/ethtool.h> | 84 | #include <linux/ethtool.h> |
85 | #include <linux/firmware.h> | ||
85 | #include <linux/if_arp.h> | 86 | #include <linux/if_arp.h> |
86 | #include <linux/wireless.h> | 87 | #include <linux/wireless.h> |
87 | #include <net/iw_handler.h> | 88 | #include <net/iw_handler.h> |
88 | #include <net/ieee80211.h> | 89 | #include <net/ieee80211.h> |
89 | 90 | ||
90 | #include "hermes_rid.h" | 91 | #include "hermes_rid.h" |
92 | #include "hermes_dld.h" | ||
91 | #include "orinoco.h" | 93 | #include "orinoco.h" |
92 | 94 | ||
93 | /********************************************************************/ | 95 | /********************************************************************/ |
@@ -301,6 +303,272 @@ static void orinoco_bss_data_init(struct orinoco_private *priv) | |||
301 | list_add_tail(&priv->bss_data[i].list, &priv->bss_free_list); | 303 | list_add_tail(&priv->bss_data[i].list, &priv->bss_free_list); |
302 | } | 304 | } |
303 | 305 | ||
306 | |||
307 | /********************************************************************/ | ||
308 | /* Download functionality */ | ||
309 | /********************************************************************/ | ||
310 | |||
311 | struct fw_info { | ||
312 | char *pri_fw; | ||
313 | char *sta_fw; | ||
314 | char *ap_fw; | ||
315 | u32 pda_addr; | ||
316 | u16 pda_size; | ||
317 | }; | ||
318 | |||
319 | const static struct fw_info orinoco_fw[] = { | ||
320 | { "", "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 }, | ||
321 | { "", "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 }, | ||
322 | { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", "", 0x00003100, 0x100 } | ||
323 | }; | ||
324 | |||
325 | /* Structure used to access fields in FW | ||
326 | * Make sure LE decoding macros are used | ||
327 | */ | ||
328 | struct orinoco_fw_header { | ||
329 | char hdr_vers[6]; /* ASCII string for header version */ | ||
330 | __le16 headersize; /* Total length of header */ | ||
331 | __le32 entry_point; /* NIC entry point */ | ||
332 | __le32 blocks; /* Number of blocks to program */ | ||
333 | __le32 block_offset; /* Offset of block data from eof header */ | ||
334 | __le32 pdr_offset; /* Offset to PDR data from eof header */ | ||
335 | __le32 pri_offset; /* Offset to primary plug data */ | ||
336 | __le32 compat_offset; /* Offset to compatibility data*/ | ||
337 | char signature[0]; /* FW signature length headersize-20 */ | ||
338 | } __attribute__ ((packed)); | ||
339 | |||
340 | /* Download either STA or AP firmware into the card. */ | ||
341 | static int | ||
342 | orinoco_dl_firmware(struct orinoco_private *priv, | ||
343 | const struct fw_info *fw, | ||
344 | int ap) | ||
345 | { | ||
346 | /* Plug Data Area (PDA) */ | ||
347 | __le16 pda[512] = { 0 }; | ||
348 | |||
349 | hermes_t *hw = &priv->hw; | ||
350 | const struct firmware *fw_entry; | ||
351 | const struct orinoco_fw_header *hdr; | ||
352 | const unsigned char *first_block; | ||
353 | const unsigned char *end; | ||
354 | const char *firmware; | ||
355 | struct net_device *dev = priv->ndev; | ||
356 | int err; | ||
357 | |||
358 | if (ap) | ||
359 | firmware = fw->ap_fw; | ||
360 | else | ||
361 | firmware = fw->sta_fw; | ||
362 | |||
363 | printk(KERN_DEBUG "%s: Attempting to download firmware %s\n", | ||
364 | dev->name, firmware); | ||
365 | |||
366 | /* Read current plug data */ | ||
367 | err = hermes_read_pda(hw, pda, fw->pda_addr, | ||
368 | min_t(u16, fw->pda_size, sizeof(pda)), 0); | ||
369 | printk(KERN_DEBUG "%s: Read PDA returned %d\n", dev->name, err); | ||
370 | if (err) | ||
371 | return err; | ||
372 | |||
373 | err = request_firmware(&fw_entry, firmware, priv->dev); | ||
374 | if (err) { | ||
375 | printk(KERN_ERR "%s: Cannot find firmware %s\n", | ||
376 | dev->name, firmware); | ||
377 | return -ENOENT; | ||
378 | } | ||
379 | |||
380 | hdr = (const struct orinoco_fw_header *) fw_entry->data; | ||
381 | |||
382 | /* Enable aux port to allow programming */ | ||
383 | err = hermesi_program_init(hw, le32_to_cpu(hdr->entry_point)); | ||
384 | printk(KERN_DEBUG "%s: Program init returned %d\n", dev->name, err); | ||
385 | if (err != 0) | ||
386 | goto abort; | ||
387 | |||
388 | /* Program data */ | ||
389 | first_block = (fw_entry->data + | ||
390 | le16_to_cpu(hdr->headersize) + | ||
391 | le32_to_cpu(hdr->block_offset)); | ||
392 | end = fw_entry->data + fw_entry->size; | ||
393 | |||
394 | err = hermes_program(hw, first_block, end); | ||
395 | printk(KERN_DEBUG "%s: Program returned %d\n", dev->name, err); | ||
396 | if (err != 0) | ||
397 | goto abort; | ||
398 | |||
399 | /* Update production data */ | ||
400 | first_block = (fw_entry->data + | ||
401 | le16_to_cpu(hdr->headersize) + | ||
402 | le32_to_cpu(hdr->pdr_offset)); | ||
403 | |||
404 | err = hermes_apply_pda_with_defaults(hw, first_block, pda); | ||
405 | printk(KERN_DEBUG "%s: Apply PDA returned %d\n", dev->name, err); | ||
406 | if (err) | ||
407 | goto abort; | ||
408 | |||
409 | /* Tell card we've finished */ | ||
410 | err = hermesi_program_end(hw); | ||
411 | printk(KERN_DEBUG "%s: Program end returned %d\n", dev->name, err); | ||
412 | if (err != 0) | ||
413 | goto abort; | ||
414 | |||
415 | /* Check if we're running */ | ||
416 | printk(KERN_DEBUG "%s: hermes_present returned %d\n", | ||
417 | dev->name, hermes_present(hw)); | ||
418 | |||
419 | abort: | ||
420 | release_firmware(fw_entry); | ||
421 | return err; | ||
422 | } | ||
423 | |||
424 | /* End markers */ | ||
425 | #define TEXT_END 0x1A /* End of text header */ | ||
426 | |||
427 | /* | ||
428 | * Process a firmware image - stop the card, load the firmware, reset | ||
429 | * the card and make sure it responds. For the secondary firmware take | ||
430 | * care of the PDA - read it and then write it on top of the firmware. | ||
431 | */ | ||
432 | static int | ||
433 | symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw, | ||
434 | const unsigned char *image, const unsigned char *end, | ||
435 | int secondary) | ||
436 | { | ||
437 | hermes_t *hw = &priv->hw; | ||
438 | int ret; | ||
439 | const unsigned char *ptr; | ||
440 | const unsigned char *first_block; | ||
441 | |||
442 | /* Plug Data Area (PDA) */ | ||
443 | __le16 pda[256]; | ||
444 | |||
445 | /* Binary block begins after the 0x1A marker */ | ||
446 | ptr = image; | ||
447 | while (*ptr++ != TEXT_END); | ||
448 | first_block = ptr; | ||
449 | |||
450 | /* Read the PDA from EEPROM */ | ||
451 | if (secondary) { | ||
452 | ret = hermes_read_pda(hw, pda, fw->pda_addr, sizeof(pda), 1); | ||
453 | if (ret) | ||
454 | return ret; | ||
455 | } | ||
456 | |||
457 | /* Stop the firmware, so that it can be safely rewritten */ | ||
458 | if (priv->stop_fw) { | ||
459 | ret = priv->stop_fw(priv, 1); | ||
460 | if (ret) | ||
461 | return ret; | ||
462 | } | ||
463 | |||
464 | /* Program the adapter with new firmware */ | ||
465 | ret = hermes_program(hw, first_block, end); | ||
466 | if (ret) | ||
467 | return ret; | ||
468 | |||
469 | /* Write the PDA to the adapter */ | ||
470 | if (secondary) { | ||
471 | size_t len = hermes_blocks_length(first_block); | ||
472 | ptr = first_block + len; | ||
473 | ret = hermes_apply_pda(hw, ptr, pda); | ||
474 | if (ret) | ||
475 | return ret; | ||
476 | } | ||
477 | |||
478 | /* Run the firmware */ | ||
479 | if (priv->stop_fw) { | ||
480 | ret = priv->stop_fw(priv, 0); | ||
481 | if (ret) | ||
482 | return ret; | ||
483 | } | ||
484 | |||
485 | /* Reset hermes chip and make sure it responds */ | ||
486 | ret = hermes_init(hw); | ||
487 | |||
488 | /* hermes_reset() should return 0 with the secondary firmware */ | ||
489 | if (secondary && ret != 0) | ||
490 | return -ENODEV; | ||
491 | |||
492 | /* And this should work with any firmware */ | ||
493 | if (!hermes_present(hw)) | ||
494 | return -ENODEV; | ||
495 | |||
496 | return 0; | ||
497 | } | ||
498 | |||
499 | |||
500 | /* | ||
501 | * Download the firmware into the card, this also does a PCMCIA soft | ||
502 | * reset on the card, to make sure it's in a sane state. | ||
503 | */ | ||
504 | static int | ||
505 | symbol_dl_firmware(struct orinoco_private *priv, | ||
506 | const struct fw_info *fw) | ||
507 | { | ||
508 | struct net_device *dev = priv->ndev; | ||
509 | int ret; | ||
510 | const struct firmware *fw_entry; | ||
511 | |||
512 | if (request_firmware(&fw_entry, fw->pri_fw, | ||
513 | priv->dev) != 0) { | ||
514 | printk(KERN_ERR "%s: Cannot find firmware: %s\n", | ||
515 | dev->name, fw->pri_fw); | ||
516 | return -ENOENT; | ||
517 | } | ||
518 | |||
519 | /* Load primary firmware */ | ||
520 | ret = symbol_dl_image(priv, fw, fw_entry->data, | ||
521 | fw_entry->data + fw_entry->size, 0); | ||
522 | release_firmware(fw_entry); | ||
523 | if (ret) { | ||
524 | printk(KERN_ERR "%s: Primary firmware download failed\n", | ||
525 | dev->name); | ||
526 | return ret; | ||
527 | } | ||
528 | |||
529 | if (request_firmware(&fw_entry, fw->sta_fw, | ||
530 | priv->dev) != 0) { | ||
531 | printk(KERN_ERR "%s: Cannot find firmware: %s\n", | ||
532 | dev->name, fw->sta_fw); | ||
533 | return -ENOENT; | ||
534 | } | ||
535 | |||
536 | /* Load secondary firmware */ | ||
537 | ret = symbol_dl_image(priv, fw, fw_entry->data, | ||
538 | fw_entry->data + fw_entry->size, 1); | ||
539 | release_firmware(fw_entry); | ||
540 | if (ret) { | ||
541 | printk(KERN_ERR "%s: Secondary firmware download failed\n", | ||
542 | dev->name); | ||
543 | } | ||
544 | |||
545 | return ret; | ||
546 | } | ||
547 | |||
548 | static int orinoco_download(struct orinoco_private *priv) | ||
549 | { | ||
550 | int err = 0; | ||
551 | /* Reload firmware */ | ||
552 | switch (priv->firmware_type) { | ||
553 | case FIRMWARE_TYPE_AGERE: | ||
554 | /* case FIRMWARE_TYPE_INTERSIL: */ | ||
555 | err = orinoco_dl_firmware(priv, | ||
556 | &orinoco_fw[priv->firmware_type], 0); | ||
557 | break; | ||
558 | |||
559 | case FIRMWARE_TYPE_SYMBOL: | ||
560 | err = symbol_dl_firmware(priv, | ||
561 | &orinoco_fw[priv->firmware_type]); | ||
562 | break; | ||
563 | case FIRMWARE_TYPE_INTERSIL: | ||
564 | break; | ||
565 | } | ||
566 | /* TODO: if we fail we probably need to reinitialise | ||
567 | * the driver */ | ||
568 | |||
569 | return err; | ||
570 | } | ||
571 | |||
304 | /********************************************************************/ | 572 | /********************************************************************/ |
305 | /* Device methods */ | 573 | /* Device methods */ |
306 | /********************************************************************/ | 574 | /********************************************************************/ |
@@ -2043,6 +2311,12 @@ static void orinoco_reset(struct work_struct *work) | |||
2043 | } | 2311 | } |
2044 | } | 2312 | } |
2045 | 2313 | ||
2314 | if (priv->do_fw_download) { | ||
2315 | err = orinoco_download(priv); | ||
2316 | if (err) | ||
2317 | priv->do_fw_download = 0; | ||
2318 | } | ||
2319 | |||
2046 | err = orinoco_reinit_firmware(dev); | 2320 | err = orinoco_reinit_firmware(dev); |
2047 | if (err) { | 2321 | if (err) { |
2048 | printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", | 2322 | printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", |
@@ -2254,6 +2528,7 @@ static int determine_firmware(struct net_device *dev) | |||
2254 | priv->has_ibss = 1; | 2528 | priv->has_ibss = 1; |
2255 | priv->has_wep = 0; | 2529 | priv->has_wep = 0; |
2256 | priv->has_big_wep = 0; | 2530 | priv->has_big_wep = 0; |
2531 | priv->do_fw_download = 0; | ||
2257 | 2532 | ||
2258 | /* Determine capabilities from the firmware version */ | 2533 | /* Determine capabilities from the firmware version */ |
2259 | switch (priv->firmware_type) { | 2534 | switch (priv->firmware_type) { |
@@ -2273,6 +2548,7 @@ static int determine_firmware(struct net_device *dev) | |||
2273 | priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ | 2548 | priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ |
2274 | priv->ibss_port = 1; | 2549 | priv->ibss_port = 1; |
2275 | priv->has_hostscan = (firmver >= 0x8000a); | 2550 | priv->has_hostscan = (firmver >= 0x8000a); |
2551 | priv->do_fw_download = 1; | ||
2276 | priv->broken_monitor = (firmver >= 0x80000); | 2552 | priv->broken_monitor = (firmver >= 0x80000); |
2277 | 2553 | ||
2278 | /* Tested with Agere firmware : | 2554 | /* Tested with Agere firmware : |
@@ -2317,6 +2593,21 @@ static int determine_firmware(struct net_device *dev) | |||
2317 | firmver >= 0x31000; | 2593 | firmver >= 0x31000; |
2318 | priv->has_preamble = (firmver >= 0x20000); | 2594 | priv->has_preamble = (firmver >= 0x20000); |
2319 | priv->ibss_port = 4; | 2595 | priv->ibss_port = 4; |
2596 | |||
2597 | /* Symbol firmware is found on various cards, but | ||
2598 | * there has been no attempt to check firmware | ||
2599 | * download on non-spectrum_cs based cards. | ||
2600 | * | ||
2601 | * Given that the Agere firmware download works | ||
2602 | * differently, we should avoid doing a firmware | ||
2603 | * download with the Symbol algorithm on non-spectrum | ||
2604 | * cards. | ||
2605 | * | ||
2606 | * For now we can identify a spectrum_cs based card | ||
2607 | * because it has a firmware reset function. | ||
2608 | */ | ||
2609 | priv->do_fw_download = (priv->stop_fw != NULL); | ||
2610 | |||
2320 | priv->broken_disableport = (firmver == 0x25013) || | 2611 | priv->broken_disableport = (firmver == 0x25013) || |
2321 | (firmver >= 0x30000 && firmver <= 0x31000); | 2612 | (firmver >= 0x30000 && firmver <= 0x31000); |
2322 | priv->has_hostscan = (firmver >= 0x31001) || | 2613 | priv->has_hostscan = (firmver >= 0x31001) || |
@@ -2387,6 +2678,20 @@ static int orinoco_init(struct net_device *dev) | |||
2387 | goto out; | 2678 | goto out; |
2388 | } | 2679 | } |
2389 | 2680 | ||
2681 | if (priv->do_fw_download) { | ||
2682 | err = orinoco_download(priv); | ||
2683 | if (err) | ||
2684 | priv->do_fw_download = 0; | ||
2685 | |||
2686 | /* Check firmware version again */ | ||
2687 | err = determine_firmware(dev); | ||
2688 | if (err != 0) { | ||
2689 | printk(KERN_ERR "%s: Incompatible firmware, aborting\n", | ||
2690 | dev->name); | ||
2691 | goto out; | ||
2692 | } | ||
2693 | } | ||
2694 | |||
2390 | if (priv->has_port3) | 2695 | if (priv->has_port3) |
2391 | printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n", dev->name); | 2696 | printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n", dev->name); |
2392 | if (priv->has_ibss) | 2697 | if (priv->has_ibss) |
@@ -2529,8 +2834,11 @@ static int orinoco_init(struct net_device *dev) | |||
2529 | return err; | 2834 | return err; |
2530 | } | 2835 | } |
2531 | 2836 | ||
2532 | struct net_device *alloc_orinocodev(int sizeof_card, | 2837 | struct net_device |
2533 | int (*hard_reset)(struct orinoco_private *)) | 2838 | *alloc_orinocodev(int sizeof_card, |
2839 | struct device *device, | ||
2840 | int (*hard_reset)(struct orinoco_private *), | ||
2841 | int (*stop_fw)(struct orinoco_private *, int)) | ||
2534 | { | 2842 | { |
2535 | struct net_device *dev; | 2843 | struct net_device *dev; |
2536 | struct orinoco_private *priv; | 2844 | struct orinoco_private *priv; |
@@ -2545,6 +2853,7 @@ struct net_device *alloc_orinocodev(int sizeof_card, | |||
2545 | + sizeof(struct orinoco_private)); | 2853 | + sizeof(struct orinoco_private)); |
2546 | else | 2854 | else |
2547 | priv->card = NULL; | 2855 | priv->card = NULL; |
2856 | priv->dev = device; | ||
2548 | 2857 | ||
2549 | if (orinoco_bss_data_allocate(priv)) | 2858 | if (orinoco_bss_data_allocate(priv)) |
2550 | goto err_out_free; | 2859 | goto err_out_free; |
@@ -2570,6 +2879,7 @@ struct net_device *alloc_orinocodev(int sizeof_card, | |||
2570 | dev->open = orinoco_open; | 2879 | dev->open = orinoco_open; |
2571 | dev->stop = orinoco_stop; | 2880 | dev->stop = orinoco_stop; |
2572 | priv->hard_reset = hard_reset; | 2881 | priv->hard_reset = hard_reset; |
2882 | priv->stop_fw = stop_fw; | ||
2573 | 2883 | ||
2574 | spin_lock_init(&priv->lock); | 2884 | spin_lock_init(&priv->lock); |
2575 | priv->open = 0; | 2885 | priv->open = 0; |