orinoco: Agere/Lucent firmware download
PATCH 5/5 orinoco: Agere/Lucent firmware download
Enable firmware download for orinoco_cs
Add module parameters download_firmware and firmware. The first option specifies whether or not to attempt a download. The second the name of the firmware file to use.
Signed-off-by David Kilroy <[email protected]>
---
drivers/net/wireless/orinoco_cs.c +128 -0
@@ -17,12 +17,14 @@#include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> +#include <linux/firmware.h> #include <pcmcia/cs_types.h> #include <pcmcia/cs.h> #include <pcmcia/cistpl.h> #include <pcmcia/cisreg.h> #include <pcmcia/ds.h> +#include "hermes_dld.h" #include "orinoco.h" /********************************************************************/
@@ -41,6 +43,17 @@ static int ignore_cis_vcc; /* = 0 */module_param (ignore_cis_vcc, int,0 );MODULE_PARM_DESC (ignore_cis_vcc,"Allow voltage mismatch between card and socket" ); +static char *firmware ="orinoco.fw" ; +module_param (firmware, charp,0644 ); +MODULE_PARM_DESC (firmware,"Name of firmware file to load" ); + +static int download_firmware;/* = 0 */ +module_param (download_firmware, int,0644 ); +MODULE_PARM_DESC (download_firmware,"Attempt firmware download" ); + +#define PDA_ADDR 0x00390000 +#define PDA_SIZE 1000 +/********************************************************************/ /* Data structures */ /********************************************************************/
@@ -57,6 +70,20 @@ struct orinoco_pccard {unsigned long hard_reset_in_progress; }; +/* Structure used to access fields in FW +* Make sure LE decoding macros are used +*/ +struct orinoco_fw_header { +char hdr_vers[6 ];/* ASCII string for header version */ +__le16 headersize;/* Total length of header */ +__le32 entry_point;/* NIC entry point */ +__le32 blocks;/* Number of blocks to program */ +__le32 block_offset;/* Offset of block data from eof header */ +__le32 pdr_offset;/* Offset to PDR data from eof header */ +__le32 pri_offset;/* Offset to primary plug data */ +__le32 compat_offset;/* Offset to compatibility data*/ +char signature[0 ];/* FW signature length headersize-20 */ +} __attribute__ ((packed));/********************************************************************/ /* Function prototypes */
@@ -70,6 +97,94 @@ static void orinoco_cs_detach(struct pcmcia_device *p_dev);/* Device methods */ /********************************************************************/ +/* +* Download the firmware into the card, this also does a PCMCIA soft +* reset on the card, to make sure it's in a sane state. +*/ +static int +orinoco_cs_dl_firmware (hermes_t *hw, +struct pcmcia_device *link) +{ +/* Plug Data Area (PDA) */ +__le16 pda[PDA_SIZE /sizeof (__le16)] = {0 }; + +const struct firmware *fw_entry; +const struct orinoco_fw_header *hdr; +const unsigned char *first_block; +unsigned long len; +int err; + +#if 0 +int i; +for (i =0 ; i <6 ; i++) { +/* wl_lkm driver disables these if configured as an AP */ + err =hermes_disable_port (hw, i); +if (err) + printk(KERN_ERR PFX +"WARNING: can't disable port %d. %d\n" , + i, err); + } +#endif + printk(KERN_DEBUG PFX "Attempting to download firmware %s\n", + firmware); + +/* Read current plug data */ + err =hermes_read_pda (hw, pda,PDA_ADDR ,sizeof (pda),0 ); + printk(KERN_DEBUG PFX "Read PDA returned %d\n", err); +if (err) +return err; + + err =request_firmware (&fw_entry, firmware, &handle_to_dev (link)); +if (err) { + printk(KERN_ERR PFX "Cannot find firmware %s\n", + firmware); +return -ENOENT ; + } + + hdr = (const struct orinoco_fw_header *) fw_entry->data; + len = fw_entry->size; + +/* Enable aux port to allow programming */ + err =hermesi_program_init (hw,le32_to_cpu (hdr->entry_point)); + printk(KERN_DEBUG PFX "Program init returned %d\n", err); +if (err !=0 ) + goto abort; + +/* Program data */ + first_block = (fw_entry->data + +le16_to_cpu (hdr->headersize) + +le32_to_cpu (hdr->block_offset)); + + err =hermes_program (hw, first_block); + printk(KERN_DEBUG PFX "Program returned %d\n", err); +if (err !=0 ) + goto abort; + +/* Update production data */ + first_block = (fw_entry->data + +le16_to_cpu (hdr->headersize) + +le32_to_cpu (hdr->pdr_offset)); + + err =hermes_apply_pda_with_defaults (hw, first_block, pda); + printk(KERN_DEBUG PFX "Apply PDA returned %d\n", err); +if (err) + goto abort; + +/* Tell card we've finished */ + err =hermesi_program_end (hw); + printk(KERN_DEBUG PFX "Program end returned %d\n", err); +if (err !=0 ) + goto abort; + +/* Check if we're running */ + printk(KERN_DEBUG PFX "hermes_present returned %d\n", + hermes_present(hw)); + +abort: +release_firmware (fw_entry); +return err; +} +static int orinoco_cs_hard_reset (struct orinoco_private *priv) {
@@ -85,6 +200,13 @@ orinoco_cs_hard_reset(struct orinoco_private *priv)return err;msleep (100 ); + +/* Download firmware if necessary */ +if (download_firmware) { + err =orinoco_cs_dl_firmware (&priv->hw, link); +if (err) +return err; + }clear_bit (0 , &card->hard_reset_in_progress);return 0 ;
@@ -316,6 +438,12 @@ orinoco_cs_config(struct pcmcia_device *link)SET_MODULE_OWNER (dev); card->node.major = card->node.minor =0 ; +/* Download firmware if necessary */ +if (download_firmware) { +if (orinoco_cs_dl_firmware (&priv->hw, link) !=0 ) + goto failed; + } +SET_NETDEV_DEV (dev, &handle_to_dev (link));/* Tell the stack we exist */ if (register_netdev (dev) !=0 ) {