2007-09-12 21:05:02

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 05/05] 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]>
---
diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c
index d1e5022..db3cc95 100644
--- a/drivers/net/wireless/orinoco_cs.c
+++ b/drivers/net/wireless/orinoco_cs.c
@@ -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) {