2008-08-21 22:28:10

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH v2 00/19] orinoco: WPA for Agere based cards

This patch series enables WPA on Agere based orinoco devices.

Patchset overview
1-2: General scanning updates
3-9: Agere firmware download to RAM
10-12: Update orinoco to work with new firmware
13-19: WPA functionality

This patchset addresses a few issues raised the last time it was
submitted:
* fix compile/sparse warnings on 64 bit
* scan buffer size increased

The patchset is against wireless-testing (master-2008-08-20), is
sparse clean (UP), and should be bisectable. It is almost checkpatch
clean - the single warning looks like a false positive to me.

To use WPA, you will need an Agere firmware image. You can get the
necessary file at
<http://marc.info/?l=orinoco-devel&m=121078835610877>, just extract
and rename orinoco.fw to agere_sta_fw.bin and place it in
/lib/firmware (or distro equivalent). Alternatively you can try
extract firmware from a Windows driver using the program in
<http://marc.info/?l=orinoco-devel&m=120846933719051>.



Regards,

Dave.


2008-08-21 22:28:14

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH v2 01/19] orinoco: Add ESSID specific scanning for Agere fw

Pass the ESSID to the card.

This allows 'iwlist eth1 scan essid <essid>' to work, and will help
with routers setup not to broadcast the ESSID.

Signed-off-by: David Kilroy <[email protected]>
---
drivers/net/wireless/orinoco.c | 20 +++++++++++++++++++-
1 files changed, 19 insertions(+), 1 deletions(-)

diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 1ebcafe..a60b459 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -2982,6 +2982,11 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev,
range->min_r_time = 0;
range->max_r_time = 65535 * 1000; /* ??? */

+ if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
+ range->scan_capa = IW_SCAN_CAPA_ESSID;
+ else
+ range->scan_capa = IW_SCAN_CAPA_NONE;
+
/* Event capability (kernel) */
IW_EVENT_CAPA_SET_KERNEL(range->event_capa);
/* Event capability (driver) */
@@ -3951,6 +3956,7 @@ static int orinoco_ioctl_setscan(struct net_device *dev,
{
struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw;
+ struct iw_scan_req *si = (struct iw_scan_req *) extra;
int err = 0;
unsigned long flags;

@@ -4012,7 +4018,19 @@ static int orinoco_ioctl_setscan(struct net_device *dev,
}
break;
case FIRMWARE_TYPE_AGERE:
- err = hermes_write_wordrec(hw, USER_BAP,
+ if (priv->scan_mode & IW_SCAN_THIS_ESSID) {
+ struct hermes_idstring idbuf;
+ size_t len = min(sizeof(idbuf.val),
+ (size_t) si->essid_len);
+ idbuf.len = cpu_to_le16(len);
+ memcpy(idbuf.val, si->essid, len);
+
+ err = hermes_write_ltv(hw, USER_BAP,
+ HERMES_RID_CNFSCANSSID_AGERE,
+ HERMES_BYTES_TO_RECLEN(len + 2),
+ &idbuf);
+ } else
+ err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFSCANSSID_AGERE,
0); /* Any ESSID */
if (err)
--
1.5.6.4


2008-08-21 23:06:04

by Dave Kilroy

[permalink] [raw]
Subject: Re: [PATCH v2 00/19] orinoco: WPA for Agere based cards

On 21 Aug, [email protected] wrote:
> This patch series enables WPA on Agere based orinoco devices.
<snip>=20
> To use WPA, you will need an Agere firmware image. You can get the
> necessary file at
> <http://marc.info/?l=3Dorinoco-devel&m=3D121078835610877>, just extra=
ct
> and rename orinoco.fw to agere_sta_fw.bin and place it in
> /lib/firmware (or distro equivalent). Alternatively you can try
> extract firmware from a Windows driver using the program in
> <http://marc.info/?l=3Dorinoco-devel&m=3D120846933719051>.

Hi,

The quoted patchset introduces the capability to load firmware for
a particular variant of the orinoco wireless cards. At the moment
the firmware image is only readily available in an email archive.

Can we include this firmware in the linux-firmware tree? If so
what do I need to do to get it there?

The firmware in the above link was originally distributed by Agere
within a driver with a BSD-style license. The only modification
I've made is to add a header defining offsets into the main image.
The original driver can still be found at
<http://www.xs4all.nl/~pptp/agere/wl_lkm_718_release.tar.gz>. I've
included the text of the license below.


Thanks,

Dave Kilroy.
---
License extracted from hcf/hcf.c

* SOFTWARE LICENSE
*
* This software is provided subject to the following terms and conditi=
ons,
* which you should read carefully before using the software. Using th=
is
* software indicates your acceptance of these terms and conditions. I=
f you do
* not agree with these terms and conditions, do not use the software.
*
* COPYRIGHT =A9 1994 - 1995 by AT&T. All Rights Reserv=
ed
* COPYRIGHT =A9 1996 - 2000 by Lucent Technologies. All Rights Reserv=
ed
* COPYRIGHT =A9 2001 - 2004 by Agere Systems Inc. All Rights Reserv=
ed
* All rights reserved.
*
* Redistribution and use in source or binary forms, with or without
* modifications, are permitted provided that the following conditions =
are met:
*
* . Redistributions of source code must retain the above copyright not=
ice, this
* list of conditions and the following Disclaimer as comments in th=
e code as
* well as in the documentation and/or other materials provided with=
the
* distribution.
*
* . Redistributions in binary form must reproduce the above copyright =
notice,
* this list of conditions and the following Disclaimer in the docum=
entation
* and/or other materials provided with the distribution.
*
* . Neither the name of Agere Systems Inc. nor the names of the contri=
butors
* may be used to endorse or promote products derived from this soft=
ware
* without specific prior written permission.
*
* Disclaimer
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANT=
IES,
* INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTI=
ES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.=
ANY
* USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE =
USERS OWN
* RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE=
FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL D=
AMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR S=
ERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAU=
SED AND
* ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT,=
STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN AN=
Y WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF S=
UCH
* DAMAGE.

2008-08-21 22:28:35

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH v2 06/19] orinoco: Move firmware download functionality into new module

Move the functionality from spectrum_cs to hermes_dld in preparation for
making it more generic and usable by other orinoco drivers.

Signed-off-by: David Kilroy <[email protected]>
---
drivers/net/wireless/Makefile | 2 +-
drivers/net/wireless/hermes_dld.c | 338 ++++++++++++++++++++++++++++++++++++
drivers/net/wireless/hermes_dld.h | 45 +++++
drivers/net/wireless/spectrum_cs.c | 274 +-----------------------------
4 files changed, 385 insertions(+), 274 deletions(-)
create mode 100644 drivers/net/wireless/hermes_dld.c
create mode 100644 drivers/net/wireless/hermes_dld.h

diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index ac3faa8..ba41bce 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -16,7 +16,7 @@ obj-$(CONFIG_WAVELAN) += wavelan.o
obj-$(CONFIG_PCMCIA_NETWAVE) += netwave_cs.o
obj-$(CONFIG_PCMCIA_WAVELAN) += wavelan_cs.o

-obj-$(CONFIG_HERMES) += orinoco.o hermes.o
+obj-$(CONFIG_HERMES) += orinoco.o hermes.o hermes_dld.o
obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o
obj-$(CONFIG_APPLE_AIRPORT) += airport.o
obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o
diff --git a/drivers/net/wireless/hermes_dld.c b/drivers/net/wireless/hermes_dld.c
new file mode 100644
index 0000000..9a8ef30
--- /dev/null
+++ b/drivers/net/wireless/hermes_dld.c
@@ -0,0 +1,338 @@
+/*
+ * Hermes download helper driver.
+ *
+ * This could be entirely merged into hermes.c.
+ *
+ * I'm keeping it separate to minimise the amount of merging between
+ * kernel upgrades. It also means the memory overhead for drivers that
+ * don't need firmware download low.
+ *
+ * This driver:
+ * - is capable of writing to the volatile area of the hermes device
+ * - is currently not capable of writing to non-volatile areas
+ * - provide helpers to identify and update plugin data
+ * - is not capable of interpreting a fw image directly. That is up to
+ * the main card driver.
+ * - deals with Hermes I devices. It can probably be modified to deal
+ * with Hermes II devices
+ *
+ * Copyright (C) 2007, David Kilroy
+ *
+ * Plug data code slightly modified from spectrum_cs driver
+ * Copyright (C) 2002-2005 Pavel Roskin <[email protected]>
+ * Portions based on information in wl_lkm_718 Agere driver
+ * COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License
+ * at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the MPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the MPL or the GPL.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "hermes.h"
+#include "hermes_dld.h"
+
+MODULE_DESCRIPTION("Download helper for Lucent Hermes chipset");
+MODULE_AUTHOR("David Kilroy <[email protected]>");
+MODULE_LICENSE("Dual MPL/GPL");
+
+#define PFX "hermes_dld: "
+
+/*
+ * AUX port access. To unlock the AUX port write the access keys to the
+ * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL
+ * register. Then read it and make sure it's HERMES_AUX_ENABLED.
+ */
+#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */
+#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */
+#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */
+
+#define HERMES_AUX_PW0 0xFE01
+#define HERMES_AUX_PW1 0xDC23
+#define HERMES_AUX_PW2 0xBA45
+
+/* End markers */
+#define PDI_END 0x00000000 /* End of PDA */
+#define BLOCK_END 0xFFFFFFFF /* Last image block */
+
+/*
+ * The following structures have little-endian fields denoted by
+ * the leading underscore. Don't access them directly - use inline
+ * functions defined below.
+ */
+
+/*
+ * The binary image to be downloaded consists of series of data blocks.
+ * Each block has the following structure.
+ */
+struct dblock {
+ __le32 addr; /* adapter address where to write the block */
+ __le16 len; /* length of the data only, in bytes */
+ char data[0]; /* data to be written */
+} __attribute__ ((packed));
+
+/*
+ * Plug Data References are located in in the image after the last data
+ * block. They refer to areas in the adapter memory where the plug data
+ * items with matching ID should be written.
+ */
+struct pdr {
+ __le32 id; /* record ID */
+ __le32 addr; /* adapter address where to write the data */
+ __le32 len; /* expected length of the data, in bytes */
+ char next[0]; /* next PDR starts here */
+} __attribute__ ((packed));
+
+/*
+ * Plug Data Items are located in the EEPROM read from the adapter by
+ * primary firmware. They refer to the device-specific data that should
+ * be plugged into the secondary firmware.
+ */
+struct pdi {
+ __le16 len; /* length of ID and data, in words */
+ __le16 id; /* record ID */
+ char data[0]; /* plug data */
+} __attribute__ ((packed));
+
+/* Functions for access to little-endian data */
+static inline u32
+dblock_addr(const struct dblock *blk)
+{
+ return le32_to_cpu(blk->addr);
+}
+
+static inline u32
+dblock_len(const struct dblock *blk)
+{
+ return le16_to_cpu(blk->len);
+}
+
+static inline u32
+pdr_id(const struct pdr *pdr)
+{
+ return le32_to_cpu(pdr->id);
+}
+
+static inline u32
+pdr_addr(const struct pdr *pdr)
+{
+ return le32_to_cpu(pdr->addr);
+}
+
+static inline u32
+pdr_len(const struct pdr *pdr)
+{
+ return le32_to_cpu(pdr->len);
+}
+
+static inline u32
+pdi_id(const struct pdi *pdi)
+{
+ return le16_to_cpu(pdi->id);
+}
+
+/* Return length of the data only, in bytes */
+static inline u32
+pdi_len(const struct pdi *pdi)
+{
+ return 2 * (le16_to_cpu(pdi->len) - 1);
+}
+
+/* Set address of the auxiliary port */
+static inline void
+spectrum_aux_setaddr(hermes_t *hw, u32 addr)
+{
+ hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
+ hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
+}
+
+/* Open access to the auxiliary port */
+static int
+spectrum_aux_open(hermes_t *hw)
+{
+ int i;
+
+ /* Already open? */
+ if (hermes_read_reg(hw, HERMES_CONTROL) == HERMES_AUX_ENABLED)
+ return 0;
+
+ hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
+ hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
+ hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
+ hermes_write_reg(hw, HERMES_CONTROL, HERMES_AUX_ENABLE);
+
+ for (i = 0; i < 20; i++) {
+ udelay(10);
+ if (hermes_read_reg(hw, HERMES_CONTROL) ==
+ HERMES_AUX_ENABLED)
+ return 0;
+ }
+
+ return -EBUSY;
+}
+
+/*
+ * Scan PDR for the record with the specified RECORD_ID.
+ * If it's not found, return NULL.
+ */
+static struct pdr *
+spectrum_find_pdr(struct pdr *first_pdr, u32 record_id)
+{
+ struct pdr *pdr = first_pdr;
+
+ while (pdr_id(pdr) != PDI_END) {
+ /*
+ * PDR area is currently not terminated by PDI_END.
+ * It's followed by CRC records, which have the type
+ * field where PDR has length. The type can be 0 or 1.
+ */
+ if (pdr_len(pdr) < 2)
+ return NULL;
+
+ /* If the record ID matches, we are done */
+ if (pdr_id(pdr) == record_id)
+ return pdr;
+
+ pdr = (struct pdr *) pdr->next;
+ }
+ return NULL;
+}
+
+/* Process one Plug Data Item - find corresponding PDR and plug it */
+static int
+spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi)
+{
+ struct pdr *pdr;
+
+ /* Find the PDI corresponding to this PDR */
+ pdr = spectrum_find_pdr(first_pdr, pdi_id(pdi));
+
+ /* No match is found, safe to ignore */
+ if (!pdr)
+ return 0;
+
+ /* Lengths of the data in PDI and PDR must match */
+ if (pdi_len(pdi) != pdr_len(pdr))
+ return -EINVAL;
+
+ /* do the actual plugging */
+ spectrum_aux_setaddr(hw, pdr_addr(pdr));
+ hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));
+
+ return 0;
+}
+
+/* Read PDA from the adapter */
+int
+spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len)
+{
+ int ret;
+ int pda_size;
+
+ /* Issue command to read EEPROM */
+ ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
+ if (ret)
+ return ret;
+
+ /* Open auxiliary port */
+ ret = spectrum_aux_open(hw);
+ if (ret)
+ return ret;
+
+ /* read PDA from EEPROM */
+ spectrum_aux_setaddr(hw, PDA_ADDR);
+ hermes_read_words(hw, HERMES_AUXDATA, pda, pda_len / 2);
+
+ /* Check PDA length */
+ pda_size = le16_to_cpu(pda[0]);
+ if (pda_size > pda_len)
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL(spectrum_read_pda);
+
+/* Parse PDA and write the records into the adapter */
+int
+spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block,
+ __le16 *pda)
+{
+ int ret;
+ struct pdi *pdi;
+ struct pdr *first_pdr;
+ const struct dblock *blk = first_block;
+
+ /* Skip all blocks to locate Plug Data References */
+ while (dblock_addr(blk) != BLOCK_END)
+ blk = (struct dblock *) &blk->data[dblock_len(blk)];
+
+ first_pdr = (struct pdr *) blk;
+
+ /* Go through every PDI and plug them into the adapter */
+ pdi = (struct pdi *) (pda + 2);
+ while (pdi_id(pdi) != PDI_END) {
+ ret = spectrum_plug_pdi(hw, first_pdr, pdi);
+ if (ret)
+ return ret;
+
+ /* Increment to the next PDI */
+ pdi = (struct pdi *) &pdi->data[pdi_len(pdi)];
+ }
+ return 0;
+}
+EXPORT_SYMBOL(spectrum_apply_pda);
+
+/* Load firmware blocks into the adapter */
+int
+spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block)
+{
+ const struct dblock *blk;
+ u32 blkaddr;
+ u32 blklen;
+
+ blk = first_block;
+ blkaddr = dblock_addr(blk);
+ blklen = dblock_len(blk);
+
+ while (dblock_addr(blk) != BLOCK_END) {
+ spectrum_aux_setaddr(hw, blkaddr);
+ hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
+ blklen);
+
+ blk = (struct dblock *) &blk->data[blklen];
+ blkaddr = dblock_addr(blk);
+ blklen = dblock_len(blk);
+ }
+ return 0;
+}
+EXPORT_SYMBOL(spectrum_load_blocks);
+
+static int __init init_hermes_dld(void)
+{
+ return 0;
+}
+
+static void __exit exit_hermes_dld(void)
+{
+}
+
+module_init(init_hermes_dld);
+module_exit(exit_hermes_dld);
diff --git a/drivers/net/wireless/hermes_dld.h b/drivers/net/wireless/hermes_dld.h
new file mode 100644
index 0000000..2c8892a
--- /dev/null
+++ b/drivers/net/wireless/hermes_dld.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2007, David Kilroy
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License
+ * at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License version 2 (the "GPL"), in
+ * which case the provisions of the GPL are applicable instead of the
+ * above. If you wish to allow the use of your version of this file
+ * only under the terms of the GPL and not to allow others to use your
+ * version of this file under the MPL, indicate your decision by
+ * deleting the provisions above and replace them with the notice and
+ * other provisions required by the GPL. If you do not delete the
+ * provisions above, a recipient may use your version of this file
+ * under either the MPL or the GPL.
+ */
+#ifndef _HERMES_DLD_H
+#define _HERMES_DLD_H
+
+#include "hermes.h"
+
+/* Position of PDA in the adapter memory */
+#define EEPROM_ADDR 0x3000
+#define EEPROM_LEN 0x200
+#define PDA_OFFSET 0x100
+
+#define PDA_ADDR (EEPROM_ADDR + PDA_OFFSET)
+#define PDA_WORDS ((EEPROM_LEN - PDA_OFFSET) / 2)
+
+struct dblock;
+
+int spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len);
+int spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block,
+ __le16 *pda);
+int spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block);
+
+#endif /* _HERMES_DLD_H */
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c
index 98df9bc..579873d 100644
--- a/drivers/net/wireless/spectrum_cs.c
+++ b/drivers/net/wireless/spectrum_cs.c
@@ -33,6 +33,7 @@
#include <pcmcia/ds.h>

#include "orinoco.h"
+#include "hermes_dld.h"

static const char primary_fw_name[] = "symbol_sp24t_prim_fw";
static const char secondary_fw_name[] = "symbol_sp24t_sec_fw";
@@ -88,144 +89,9 @@ static void spectrum_cs_release(struct pcmcia_device *link);
#define HCR_IDLE 0x0E /* don't run firmware after reset */
#define HCR_MEM16 0x10 /* memory width bit, should be preserved */

-/*
- * AUX port access. To unlock the AUX port write the access keys to the
- * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL
- * register. Then read it and make sure it's HERMES_AUX_ENABLED.
- */
-#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */
-#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */
-#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */
-
-#define HERMES_AUX_PW0 0xFE01
-#define HERMES_AUX_PW1 0xDC23
-#define HERMES_AUX_PW2 0xBA45
-
/* End markers */
-#define PDI_END 0x00000000 /* End of PDA */
-#define BLOCK_END 0xFFFFFFFF /* Last image block */
#define TEXT_END 0x1A /* End of text header */

-/*
- * The following structures have little-endian fields denoted by
- * the leading underscore. Don't access them directly - use inline
- * functions defined below.
- */
-
-/*
- * The binary image to be downloaded consists of series of data blocks.
- * Each block has the following structure.
- */
-struct dblock {
- __le32 addr; /* adapter address where to write the block */
- __le16 len; /* length of the data only, in bytes */
- char data[0]; /* data to be written */
-} __attribute__ ((packed));
-
-/*
- * Plug Data References are located in in the image after the last data
- * block. They refer to areas in the adapter memory where the plug data
- * items with matching ID should be written.
- */
-struct pdr {
- __le32 id; /* record ID */
- __le32 addr; /* adapter address where to write the data */
- __le32 len; /* expected length of the data, in bytes */
- char next[0]; /* next PDR starts here */
-} __attribute__ ((packed));
-
-
-/*
- * Plug Data Items are located in the EEPROM read from the adapter by
- * primary firmware. They refer to the device-specific data that should
- * be plugged into the secondary firmware.
- */
-struct pdi {
- __le16 len; /* length of ID and data, in words */
- __le16 id; /* record ID */
- char data[0]; /* plug data */
-} __attribute__ ((packed));
-
-
-/* Functions for access to little-endian data */
-static inline u32
-dblock_addr(const struct dblock *blk)
-{
- return le32_to_cpu(blk->addr);
-}
-
-static inline u32
-dblock_len(const struct dblock *blk)
-{
- return le16_to_cpu(blk->len);
-}
-
-static inline u32
-pdr_id(const struct pdr *pdr)
-{
- return le32_to_cpu(pdr->id);
-}
-
-static inline u32
-pdr_addr(const struct pdr *pdr)
-{
- return le32_to_cpu(pdr->addr);
-}
-
-static inline u32
-pdr_len(const struct pdr *pdr)
-{
- return le32_to_cpu(pdr->len);
-}
-
-static inline u32
-pdi_id(const struct pdi *pdi)
-{
- return le16_to_cpu(pdi->id);
-}
-
-/* Return length of the data only, in bytes */
-static inline u32
-pdi_len(const struct pdi *pdi)
-{
- return 2 * (le16_to_cpu(pdi->len) - 1);
-}
-
-
-/* Set address of the auxiliary port */
-static inline void
-spectrum_aux_setaddr(hermes_t *hw, u32 addr)
-{
- hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
- hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
-}
-
-
-/* Open access to the auxiliary port */
-static int
-spectrum_aux_open(hermes_t *hw)
-{
- int i;
-
- /* Already open? */
- if (hermes_read_reg(hw, HERMES_CONTROL) == HERMES_AUX_ENABLED)
- return 0;
-
- hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
- hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
- hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
- hermes_write_reg(hw, HERMES_CONTROL, HERMES_AUX_ENABLE);
-
- for (i = 0; i < 20; i++) {
- udelay(10);
- if (hermes_read_reg(hw, HERMES_CONTROL) ==
- HERMES_AUX_ENABLED)
- return 0;
- }
-
- return -EBUSY;
-}
-

#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
@@ -294,144 +160,6 @@ spectrum_reset(struct pcmcia_device *link, int idle)


/*
- * Scan PDR for the record with the specified RECORD_ID.
- * If it's not found, return NULL.
- */
-static struct pdr *
-spectrum_find_pdr(struct pdr *first_pdr, u32 record_id)
-{
- struct pdr *pdr = first_pdr;
-
- while (pdr_id(pdr) != PDI_END) {
- /*
- * PDR area is currently not terminated by PDI_END.
- * It's followed by CRC records, which have the type
- * field where PDR has length. The type can be 0 or 1.
- */
- if (pdr_len(pdr) < 2)
- return NULL;
-
- /* If the record ID matches, we are done */
- if (pdr_id(pdr) == record_id)
- return pdr;
-
- pdr = (struct pdr *) pdr->next;
- }
- return NULL;
-}
-
-
-/* Process one Plug Data Item - find corresponding PDR and plug it */
-static int
-spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi)
-{
- struct pdr *pdr;
-
- /* Find the PDI corresponding to this PDR */
- pdr = spectrum_find_pdr(first_pdr, pdi_id(pdi));
-
- /* No match is found, safe to ignore */
- if (!pdr)
- return 0;
-
- /* Lengths of the data in PDI and PDR must match */
- if (pdi_len(pdi) != pdr_len(pdr))
- return -EINVAL;
-
- /* do the actual plugging */
- spectrum_aux_setaddr(hw, pdr_addr(pdr));
- hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));
-
- return 0;
-}
-
-
-/* Read PDA from the adapter */
-static int
-spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len)
-{
- int ret;
- int pda_size;
-
- /* Issue command to read EEPROM */
- ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
- if (ret)
- return ret;
-
- /* Open auxiliary port */
- ret = spectrum_aux_open(hw);
- if (ret)
- return ret;
-
- /* read PDA from EEPROM */
- spectrum_aux_setaddr(hw, PDA_ADDR);
- hermes_read_words(hw, HERMES_AUXDATA, pda, pda_len / 2);
-
- /* Check PDA length */
- pda_size = le16_to_cpu(pda[0]);
- if (pda_size > pda_len)
- return -EINVAL;
-
- return 0;
-}
-
-
-/* Parse PDA and write the records into the adapter */
-static int
-spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block,
- __le16 *pda)
-{
- int ret;
- struct pdi *pdi;
- struct pdr *first_pdr;
- const struct dblock *blk = first_block;
-
- /* Skip all blocks to locate Plug Data References */
- while (dblock_addr(blk) != BLOCK_END)
- blk = (struct dblock *) &blk->data[dblock_len(blk)];
-
- first_pdr = (struct pdr *) blk;
-
- /* Go through every PDI and plug them into the adapter */
- pdi = (struct pdi *) (pda + 2);
- while (pdi_id(pdi) != PDI_END) {
- ret = spectrum_plug_pdi(hw, first_pdr, pdi);
- if (ret)
- return ret;
-
- /* Increment to the next PDI */
- pdi = (struct pdi *) &pdi->data[pdi_len(pdi)];
- }
- return 0;
-}
-
-
-/* Load firmware blocks into the adapter */
-static int
-spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block)
-{
- const struct dblock *blk;
- u32 blkaddr;
- u32 blklen;
-
- blk = first_block;
- blkaddr = dblock_addr(blk);
- blklen = dblock_len(blk);
-
- while (dblock_addr(blk) != BLOCK_END) {
- spectrum_aux_setaddr(hw, blkaddr);
- hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
- blklen);
-
- blk = (struct dblock *) &blk->data[blklen];
- blkaddr = dblock_addr(blk);
- blklen = dblock_len(blk);
- }
- return 0;
-}
-
-
-/*
* Process a firmware image - stop the card, load the firmware, reset
* the card and make sure it responds. For the secondary firmware take
* care of the PDA - read it and then write it on top of the firmware.
--
1.5.6.4


2008-08-21 22:28:44

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH v2 08/19] orinoco: Extend hermes_dld routines for Agere firmware

Add programming initialisation and termination functions.
Add checks to avoid overrunning the firmware image or PDA areas.
Extra algorithm to program PDA values using defaults where necessary.

Signed-off-by: David Kilroy <[email protected]>
---
drivers/net/wireless/hermes_dld.c | 285 +++++++++++++++++++++++++++++++++++++
drivers/net/wireless/hermes_dld.h | 5 +
2 files changed, 290 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/hermes_dld.c b/drivers/net/wireless/hermes_dld.c
index 22ae79d..d8c626e 100644
--- a/drivers/net/wireless/hermes_dld.c
+++ b/drivers/net/wireless/hermes_dld.c
@@ -70,6 +70,12 @@ MODULE_LICENSE("Dual MPL/GPL");
#define HERMES_AUX_PW1 0xDC23
#define HERMES_AUX_PW2 0xBA45

+/* HERMES_CMD_DOWNLD */
+#define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD)
+#define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD)
+#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD)
+#define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD)
+
/* End markers used in dblocks */
#define PDI_END 0x00000000 /* End of PDA */
#define BLOCK_END 0xFFFFFFFF /* Last image block */
@@ -247,6 +253,23 @@ hermes_find_pdr(struct pdr *first_pdr, u32 record_id)
return NULL;
}

+/* Scan production data items for a particular entry */
+static struct pdi *
+hermes_find_pdi(struct pdi *first_pdi, u32 record_id)
+{
+ struct pdi *pdi = first_pdi;
+
+ while (pdi_id(pdi) != PDI_END) {
+
+ /* If the record ID matches, we are done */
+ if (pdi_id(pdi) == record_id)
+ return pdi;
+
+ pdi = (struct pdi *) &pdi->data[pdi_len(pdi)];
+ }
+ return NULL;
+}
+
/* Process one Plug Data Item - find corresponding PDR and plug it */
static int
hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi)
@@ -290,6 +313,15 @@ int hermes_read_pda(hermes_t *hw,
ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
if (ret)
return ret;
+ } else {
+ /* wl_lkm does not include PDA size in the PDA area.
+ * We will pad the information into pda, so other routines
+ * don't have to be modified */
+ pda[0] = cpu_to_le16(pda_len - 2);
+ /* Includes CFG_PROD_DATA but not itself */
+ pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
+ data_len = pda_len - 4;
+ data = pda + 2;
}

/* Open auxiliary port */
@@ -370,6 +402,94 @@ EXPORT_SYMBOL(hermes_blocks_length);

/*** Hermes programming ***/

+/* About to start programming data (Hermes I)
+ * offset is the entry point
+ *
+ * Spectrum_cs' Symbol fw does not require this
+ * wl_lkm Agere fw does
+ * Don't know about intersil
+ */
+int hermesi_program_init(hermes_t *hw, u32 offset)
+{
+ int err;
+
+ /* Disable interrupts?*/
+ /*hw->inten = 0x0;*/
+ /*hermes_write_regn(hw, INTEN, 0);*/
+ /*hermes_set_irqmask(hw, 0);*/
+
+ /* Acknowledge any outstanding command */
+ hermes_write_regn(hw, EVACK, 0xFFFF);
+
+ /* Using doicmd_wait rather than docmd_wait */
+ err = hermes_doicmd_wait(hw,
+ 0x0100 | HERMES_CMD_INIT,
+ 0, 0, 0, NULL);
+ if (err)
+ return err;
+
+ err = hermes_doicmd_wait(hw,
+ 0x0000 | HERMES_CMD_INIT,
+ 0, 0, 0, NULL);
+ if (err)
+ return err;
+
+ err = hermes_aux_control(hw, 1);
+ printk(KERN_DEBUG PFX "AUX enable returned %d\n", err);
+
+ if (err)
+ return err;
+
+ printk(KERN_DEBUG PFX "Enabling volatile, EP 0x%08x\n", offset);
+ err = hermes_doicmd_wait(hw,
+ HERMES_PROGRAM_ENABLE_VOLATILE,
+ offset & 0xFFFFu,
+ offset >> 16,
+ 0,
+ NULL);
+ printk(KERN_DEBUG PFX "PROGRAM_ENABLE returned %d\n",
+ err);
+
+ return err;
+}
+EXPORT_SYMBOL(hermesi_program_init);
+
+/* Done programming data (Hermes I)
+ *
+ * Spectrum_cs' Symbol fw does not require this
+ * wl_lkm Agere fw does
+ * Don't know about intersil
+ */
+int hermesi_program_end(hermes_t *hw)
+{
+ struct hermes_response resp;
+ int rc = 0;
+ int err;
+
+ rc = hermes_docmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);
+
+ printk(KERN_DEBUG PFX "PROGRAM_DISABLE returned %d, "
+ "r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
+ rc, resp.resp0, resp.resp1, resp.resp2);
+
+ if ((rc == 0) &&
+ ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD))
+ rc = -EIO;
+
+ err = hermes_aux_control(hw, 0);
+ printk(KERN_DEBUG PFX "AUX disable returned %d\n", err);
+
+ /* Acknowledge any outstanding command */
+ hermes_write_regn(hw, EVACK, 0xFFFF);
+
+ /* Reinitialise, ignoring return */
+ (void) hermes_doicmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
+ 0, 0, 0, NULL);
+
+ return rc ? rc : err;
+}
+EXPORT_SYMBOL(hermesi_program_end);
+
/* Program the data blocks */
int hermes_program(hermes_t *hw, const char *first_block, const char *end)
{
@@ -443,3 +563,168 @@ static void __exit exit_hermes_dld(void)

module_init(init_hermes_dld);
module_exit(exit_hermes_dld);
+
+/*** Default plugging data for Hermes I ***/
+/* Values from wl_lkm_718/hcf/dhf.c */
+
+#define DEFINE_DEFAULT_PDR(pid, length, data) \
+static const struct { \
+ __le16 len; \
+ __le16 id; \
+ u8 val[length]; \
+} __attribute__ ((packed)) default_pdr_data_##pid = { \
+ __constant_cpu_to_le16((sizeof(default_pdr_data_##pid)/ \
+ sizeof(__le16)) - 1), \
+ __constant_cpu_to_le16(pid), \
+ data \
+}
+
+#define DEFAULT_PDR(pid) default_pdr_data_##pid
+
+/* HWIF Compatiblity */
+DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00");
+
+/* PPPPSign */
+DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00");
+
+/* PPPPProf */
+DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00");
+
+/* Antenna diversity */
+DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F");
+
+/* Modem VCO band Set-up */
+DEFINE_DEFAULT_PDR(0x0160, 28,
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00");
+
+/* Modem Rx Gain Table Values */
+DEFINE_DEFAULT_PDR(0x0161, 256,
+ "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
+ "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
+ "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
+ "\x3F\x01\x3F\01\x3F\x01\x3F\x01"
+ "\x3F\x01\x3E\01\x3E\x01\x3D\x01"
+ "\x3D\x01\x3C\01\x3C\x01\x3B\x01"
+ "\x3B\x01\x3A\01\x3A\x01\x39\x01"
+ "\x39\x01\x38\01\x38\x01\x37\x01"
+ "\x37\x01\x36\01\x36\x01\x35\x01"
+ "\x35\x01\x34\01\x34\x01\x33\x01"
+ "\x33\x01\x32\x01\x32\x01\x31\x01"
+ "\x31\x01\x30\x01\x30\x01\x7B\x01"
+ "\x7B\x01\x7A\x01\x7A\x01\x79\x01"
+ "\x79\x01\x78\x01\x78\x01\x77\x01"
+ "\x77\x01\x76\x01\x76\x01\x75\x01"
+ "\x75\x01\x74\x01\x74\x01\x73\x01"
+ "\x73\x01\x72\x01\x72\x01\x71\x01"
+ "\x71\x01\x70\x01\x70\x01\x68\x01"
+ "\x68\x01\x67\x01\x67\x01\x66\x01"
+ "\x66\x01\x65\x01\x65\x01\x57\x01"
+ "\x57\x01\x56\x01\x56\x01\x55\x01"
+ "\x55\x01\x54\x01\x54\x01\x53\x01"
+ "\x53\x01\x52\x01\x52\x01\x51\x01"
+ "\x51\x01\x50\x01\x50\x01\x48\x01"
+ "\x48\x01\x47\x01\x47\x01\x46\x01"
+ "\x46\x01\x45\x01\x45\x01\x44\x01"
+ "\x44\x01\x43\x01\x43\x01\x42\x01"
+ "\x42\x01\x41\x01\x41\x01\x40\x01"
+ "\x40\x01\x40\x01\x40\x01\x40\x01"
+ "\x40\x01\x40\x01\x40\x01\x40\x01"
+ "\x40\x01\x40\x01\x40\x01\x40\x01"
+ "\x40\x01\x40\x01\x40\x01\x40\x01");
+
+/* Write PDA according to certain rules.
+ *
+ * For every production data record, look for a previous setting in
+ * the pda, and use that.
+ *
+ * For certain records, use defaults if they are not found in pda.
+ */
+int hermes_apply_pda_with_defaults(hermes_t *hw,
+ const char *first_pdr,
+ const __le16 *pda)
+{
+ const struct pdr *pdr = (const struct pdr *) first_pdr;
+ struct pdi *first_pdi = (struct pdi *) &pda[2];
+ struct pdi *pdi;
+ struct pdi *default_pdi = NULL;
+ struct pdi *outdoor_pdi;
+ void *end = (void *)first_pdr + MAX_PDA_SIZE;
+ int record_id;
+
+ while (((void *)pdr < end) &&
+ (pdr_id(pdr) != PDI_END)) {
+ /*
+ * For spectrum_cs firmwares,
+ * PDR area is currently not terminated by PDI_END.
+ * It's followed by CRC records, which have the type
+ * field where PDR has length. The type can be 0 or 1.
+ */
+ if (pdr_len(pdr) < 2)
+ break;
+ record_id = pdr_id(pdr);
+
+ pdi = hermes_find_pdi(first_pdi, record_id);
+ if (pdi)
+ printk(KERN_DEBUG PFX "Found record 0x%04x at %p\n",
+ record_id, pdi);
+
+ switch (record_id) {
+ case 0x110: /* Modem REFDAC values */
+ case 0x120: /* Modem VGDAC values */
+ outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1);
+ default_pdi = NULL;
+ if (outdoor_pdi) {
+ pdi = outdoor_pdi;
+ printk(KERN_DEBUG PFX
+ "Using outdoor record 0x%04x at %p\n",
+ record_id + 1, pdi);
+ }
+ break;
+ case 0x5: /* HWIF Compatiblity */
+ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005);
+ break;
+ case 0x108: /* PPPPSign */
+ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108);
+ break;
+ case 0x109: /* PPPPProf */
+ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109);
+ break;
+ case 0x150: /* Antenna diversity */
+ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150);
+ break;
+ case 0x160: /* Modem VCO band Set-up */
+ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160);
+ break;
+ case 0x161: /* Modem Rx Gain Table Values */
+ default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161);
+ break;
+ default:
+ default_pdi = NULL;
+ break;
+ }
+ if (!pdi && default_pdi) {
+ /* Use default */
+ pdi = default_pdi;
+ printk(KERN_DEBUG PFX
+ "Using default record 0x%04x at %p\n",
+ record_id, pdi);
+ }
+
+ if (pdi) {
+ /* Lengths of the data in PDI and PDR must match */
+ if (pdi_len(pdi) == pdr_len(pdr)) {
+ /* do the actual plugging */
+ hermes_aux_setaddr(hw, pdr_addr(pdr));
+ hermes_write_bytes(hw, HERMES_AUXDATA,
+ pdi->data, pdi_len(pdi));
+ }
+ }
+
+ pdr++;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(hermes_apply_pda_with_defaults);
diff --git a/drivers/net/wireless/hermes_dld.h b/drivers/net/wireless/hermes_dld.h
index af75c03..6fcb262 100644
--- a/drivers/net/wireless/hermes_dld.h
+++ b/drivers/net/wireless/hermes_dld.h
@@ -27,6 +27,8 @@

#include "hermes.h"

+int hermesi_program_init(hermes_t *hw, u32 offset);
+int hermesi_program_end(hermes_t *hw);
int hermes_program(hermes_t *hw, const char *first_block, const char *end);

int hermes_read_pda(hermes_t *hw,
@@ -37,6 +39,9 @@ int hermes_read_pda(hermes_t *hw,
int hermes_apply_pda(hermes_t *hw,
const char *first_pdr,
const __le16 *pda);
+int hermes_apply_pda_with_defaults(hermes_t *hw,
+ const char *first_pdr,
+ const __le16 *pda);

size_t hermes_blocks_length(const char *first_block);

--
1.5.6.4


2008-08-21 22:28:25

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH v2 04/19] orinoco: Move EXPORT_SYMBOL declarations next to exported function

Signed-off-by: David Kilroy <[email protected]>
---
drivers/net/wireless/hermes.c | 17 +++++++----------
1 files changed, 7 insertions(+), 10 deletions(-)

diff --git a/drivers/net/wireless/hermes.c b/drivers/net/wireless/hermes.c
index 3a6ce5a..240118f 100644
--- a/drivers/net/wireless/hermes.c
+++ b/drivers/net/wireless/hermes.c
@@ -122,6 +122,7 @@ void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing)
hw->reg_spacing = reg_spacing;
hw->inten = 0x0;
}
+EXPORT_SYMBOL(hermes_struct_init);

int hermes_init(hermes_t *hw)
{
@@ -202,6 +203,7 @@ int hermes_init(hermes_t *hw)
out:
return err;
}
+EXPORT_SYMBOL(hermes_init);

/* Issue a command to the chip, and (busy!) wait for it to
* complete.
@@ -272,6 +274,7 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
out:
return err;
}
+EXPORT_SYMBOL(hermes_docmd_wait);

int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)
{
@@ -314,7 +317,7 @@ int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)

return 0;
}
-
+EXPORT_SYMBOL(hermes_allocate);

/* Set up a BAP to read a particular chunk of data from card's internal buffer.
*
@@ -398,6 +401,7 @@ int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
out:
return err;
}
+EXPORT_SYMBOL(hermes_bap_pread);

/* Write a block of data to the chip's buffer, via the
* BAP. Synchronization/serialization is the caller's problem.
@@ -423,6 +427,7 @@ int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
out:
return err;
}
+EXPORT_SYMBOL(hermes_bap_pwrite);

/* Read a Length-Type-Value record from the card.
*
@@ -476,6 +481,7 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize,

return 0;
}
+EXPORT_SYMBOL(hermes_read_ltv);

int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
u16 length, const void *value)
@@ -503,15 +509,6 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,

return err;
}
-
-EXPORT_SYMBOL(hermes_struct_init);
-EXPORT_SYMBOL(hermes_init);
-EXPORT_SYMBOL(hermes_docmd_wait);
-EXPORT_SYMBOL(hermes_allocate);
-
-EXPORT_SYMBOL(hermes_bap_pread);
-EXPORT_SYMBOL(hermes_bap_pwrite);
-EXPORT_SYMBOL(hermes_read_ltv);
EXPORT_SYMBOL(hermes_write_ltv);

static int __init init_hermes(void)
--
1.5.6.4


2008-08-21 22:28:17

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH v2 02/19] orinoco: Update scan translation

Report channel, beacon interval and capabilities.
Use WEXT defines instead of magic numbers.
State quality stats in dB.
Also a few changes to keep line length less than 80.

Signed-off-by: David Kilroy <[email protected]>
---
drivers/net/wireless/orinoco.c | 71 ++++++++++++++++++++++++++--------------
1 files changed, 46 insertions(+), 25 deletions(-)

diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index a60b459..00b1d59 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -3948,7 +3948,7 @@ static int orinoco_ioctl_getrid(struct net_device *dev,
return err;
}

-/* Trigger a scan (look for other cells in the vicinity */
+/* Trigger a scan (look for other cells in the vicinity) */
static int orinoco_ioctl_setscan(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *srq,
@@ -3988,7 +3988,6 @@ static int orinoco_ioctl_setscan(struct net_device *dev,
* we access scan variables in priv is critical.
* o scan_inprogress : not touched by irq handler
* o scan_mode : not touched by irq handler
- * o scan_len : synchronised with scan_result
* Before modifying anything on those variables, please think hard !
* Jean II */

@@ -4054,8 +4053,7 @@ static int orinoco_ioctl_setscan(struct net_device *dev,
#define MAX_CUSTOM_LEN 64

/* Translate scan data returned from the card to a card independant
- * format that the Wireless Tools will understand - Jean II
- * Return message length or -errno for fatal errors */
+ * format that the Wireless Tools will understand - Jean II */
static inline char *orinoco_translate_scan(struct net_device *dev,
struct iw_request_info *info,
char *current_ev,
@@ -4067,9 +4065,10 @@ static inline char *orinoco_translate_scan(struct net_device *dev,
u16 capabilities;
u16 channel;
struct iw_event iwe; /* Temporary buffer */
- char *p;
char custom[MAX_CUSTOM_LEN];

+ memset(&iwe, 0, sizeof(iwe));
+
/* First entry *MUST* be the AP MAC address */
iwe.cmd = SIOCGIWAP;
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
@@ -4091,8 +4090,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev,
/* Add mode */
iwe.cmd = SIOCGIWMODE;
capabilities = le16_to_cpu(bss->a.capabilities);
- if (capabilities & 0x3) {
- if (capabilities & 0x1)
+ if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
+ if (capabilities & WLAN_CAPABILITY_ESS)
iwe.u.mode = IW_MODE_MASTER;
else
iwe.u.mode = IW_MODE_ADHOC;
@@ -4102,17 +4101,22 @@ static inline char *orinoco_translate_scan(struct net_device *dev,

channel = bss->s.channel;
if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
- /* Add frequency */
+ /* Add channel and frequency */
iwe.cmd = SIOCGIWFREQ;
+ iwe.u.freq.m = channel;
+ iwe.u.freq.e = 0;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_FREQ_LEN);
+
iwe.u.freq.m = channel_frequency[channel-1] * 100000;
iwe.u.freq.e = 1;
current_ev = iwe_stream_add_event(info, current_ev, end_buf,
&iwe, IW_EV_FREQ_LEN);
}

- /* Add quality statistics */
+ /* Add quality statistics. level and noise in dB. No link quality */
iwe.cmd = IWEVQUAL;
- iwe.u.qual.updated = 0x10; /* no link quality */
+ iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
iwe.u.qual.level = (__u8) le16_to_cpu(bss->a.level) - 0x95;
iwe.u.qual.noise = (__u8) le16_to_cpu(bss->a.noise) - 0x95;
/* Wireless tools prior to 27.pre22 will show link quality
@@ -4126,25 +4130,13 @@ static inline char *orinoco_translate_scan(struct net_device *dev,

/* Add encryption capability */
iwe.cmd = SIOCGIWENCODE;
- if (capabilities & 0x10)
+ if (capabilities & WLAN_CAPABILITY_PRIVACY)
iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
else
iwe.u.data.flags = IW_ENCODE_DISABLED;
iwe.u.data.length = 0;
current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, bss->a.essid);
-
- /* Add EXTRA: Age to display seconds since last beacon/probe response
- * for given network. */
- iwe.cmd = IWEVCUSTOM;
- p = custom;
- p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
- " Last beacon: %dms ago",
- jiffies_to_msecs(jiffies - last_scanned));
- iwe.u.data.length = p - custom;
- if (iwe.u.data.length)
- current_ev = iwe_stream_add_point(info, current_ev, end_buf,
- &iwe, custom);
+ &iwe, NULL);

/* Bit rate is not available in Lucent/Agere firmwares */
if (priv->firmware_type != FIRMWARE_TYPE_AGERE) {
@@ -4166,7 +4158,8 @@ static inline char *orinoco_translate_scan(struct net_device *dev,
if (bss->p.rates[i] == 0x0)
break;
/* Bit rate given in 500 kb/s units (+ 0x80) */
- iwe.u.bitrate.value = ((bss->p.rates[i] & 0x7f) * 500000);
+ iwe.u.bitrate.value =
+ ((bss->p.rates[i] & 0x7f) * 500000);
current_val = iwe_stream_add_value(info, current_ev,
current_val,
end_buf, &iwe,
@@ -4177,6 +4170,34 @@ static inline char *orinoco_translate_scan(struct net_device *dev,
current_ev = current_val;
}

+ /* Beacon interval */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ "bcn_int=%d",
+ le16_to_cpu(bss->a.beacon_interv));
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
+ /* Capabilites */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ "capab=0x%04x",
+ capabilities);
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
+ /* Add EXTRA: Age to display seconds since last beacon/probe response
+ * for given network. */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ " Last beacon: %dms ago",
+ jiffies_to_msecs(jiffies - last_scanned));
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
return current_ev;
}

--
1.5.6.4


2008-08-21 22:28:29

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH v2 05/19] orinoco: Add function to execute Hermes initialisation commands synchronously

The current synchronous execution function doesn't work
for certain Hermes commands which clear the MAGIC number from
SWSUPPORT0. These commands seem to be related to initialisation or
programming, for example HERMES_CMD_INIT.

Replicate hermes_docmd_wait for commands which clear the MAGIC number
from SWSUPPORT0. This version accepts two extra arguments which are
passed straight to the firmware.

Functionality copied out of hermes_init.

Signed-off-by: David Kilroy <[email protected]>
---
drivers/net/wireless/hermes.c | 94 +++++++++++++++++++++++++----------------
drivers/net/wireless/hermes.h | 3 +
2 files changed, 60 insertions(+), 37 deletions(-)

diff --git a/drivers/net/wireless/hermes.c b/drivers/net/wireless/hermes.c
index 240118f..bfa3753 100644
--- a/drivers/net/wireless/hermes.c
+++ b/drivers/net/wireless/hermes.c
@@ -116,6 +116,61 @@ static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0,
* Function definitions
*/

+/* For doing cmds that wipe the magic constant in SWSUPPORT0 */
+int hermes_doicmd_wait(hermes_t *hw, u16 cmd,
+ u16 parm0, u16 parm1, u16 parm2,
+ struct hermes_response *resp)
+{
+ int err = 0;
+ int k;
+ u16 status, reg;
+
+ err = hermes_issue_cmd(hw, cmd, parm0, parm1, parm2);
+ if (err)
+ return err;
+
+ reg = hermes_read_regn(hw, EVSTAT);
+ k = CMD_INIT_TIMEOUT;
+ while ((!(reg & HERMES_EV_CMD)) && k) {
+ k--;
+ udelay(10);
+ reg = hermes_read_regn(hw, EVSTAT);
+ }
+
+ hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC);
+
+ if (!hermes_present(hw)) {
+ DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n",
+ hw->iobase);
+ err = -ENODEV;
+ goto out;
+ }
+
+ if (!(reg & HERMES_EV_CMD)) {
+ printk(KERN_ERR "hermes @ %p: "
+ "Timeout waiting for card to reset (reg=0x%04x)!\n",
+ hw->iobase, reg);
+ err = -ETIMEDOUT;
+ goto out;
+ }
+
+ status = hermes_read_regn(hw, STATUS);
+ if (resp) {
+ resp->status = status;
+ resp->resp0 = hermes_read_regn(hw, RESP0);
+ resp->resp1 = hermes_read_regn(hw, RESP1);
+ resp->resp2 = hermes_read_regn(hw, RESP2);
+ }
+
+ hermes_write_regn(hw, EVACK, HERMES_EV_CMD);
+
+ if (status & HERMES_STATUS_RESULT)
+ err = -EIO;
+out:
+ return err;
+}
+EXPORT_SYMBOL(hermes_doicmd_wait);
+
void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing)
{
hw->iobase = address;
@@ -126,7 +181,7 @@ EXPORT_SYMBOL(hermes_struct_init);

int hermes_init(hermes_t *hw)
{
- u16 status, reg;
+ u16 reg;
int err = 0;
int k;

@@ -164,43 +219,8 @@ int hermes_init(hermes_t *hw)

/* We don't use hermes_docmd_wait here, because the reset wipes
the magic constant in SWSUPPORT0 away, and it gets confused */
- err = hermes_issue_cmd(hw, HERMES_CMD_INIT, 0, 0, 0);
- if (err)
- return err;
-
- reg = hermes_read_regn(hw, EVSTAT);
- k = CMD_INIT_TIMEOUT;
- while ( (! (reg & HERMES_EV_CMD)) && k) {
- k--;
- udelay(10);
- reg = hermes_read_regn(hw, EVSTAT);
- }
-
- hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC);
-
- if (! hermes_present(hw)) {
- DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n",
- hw->iobase);
- err = -ENODEV;
- goto out;
- }
-
- if (! (reg & HERMES_EV_CMD)) {
- printk(KERN_ERR "hermes @ %p: "
- "Timeout waiting for card to reset (reg=0x%04x)!\n",
- hw->iobase, reg);
- err = -ETIMEDOUT;
- goto out;
- }
-
- status = hermes_read_regn(hw, STATUS);
-
- hermes_write_regn(hw, EVACK, HERMES_EV_CMD);
+ err = hermes_doicmd_wait(hw, HERMES_CMD_INIT, 0, 0, 0, NULL);

- if (status & HERMES_STATUS_RESULT)
- err = -EIO;
-
- out:
return err;
}
EXPORT_SYMBOL(hermes_init);
diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h
index 8e3f0e3..287f536 100644
--- a/drivers/net/wireless/hermes.h
+++ b/drivers/net/wireless/hermes.h
@@ -353,6 +353,9 @@ void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing);
int hermes_init(hermes_t *hw);
int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
struct hermes_response *resp);
+int hermes_doicmd_wait(hermes_t *hw, u16 cmd,
+ u16 parm0, u16 parm1, u16 parm2,
+ struct hermes_response *resp);
int hermes_allocate(hermes_t *hw, u16 size, u16 *fid);

int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
--
1.5.6.4


2008-08-21 22:28:40

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH v2 07/19] orinoco: Make firmware download logic more generic

Ensure PDA read is terminated.
Prevent invalid programming blocks from causing reads outside the
firmware image
Turn off aux stuff when finished.
Option to program in limited block sizes (controlled by macro).
Option to read PDA from EEPROM.

Signed-off-by: David Kilroy <[email protected]>
---
drivers/net/wireless/hermes_dld.c | 209 +++++++++++++++++++++++++++---------
drivers/net/wireless/hermes_dld.h | 22 ++--
drivers/net/wireless/spectrum_cs.c | 23 +++--
3 files changed, 182 insertions(+), 72 deletions(-)

diff --git a/drivers/net/wireless/hermes_dld.c b/drivers/net/wireless/hermes_dld.c
index 9a8ef30..22ae79d 100644
--- a/drivers/net/wireless/hermes_dld.c
+++ b/drivers/net/wireless/hermes_dld.c
@@ -64,14 +64,34 @@ MODULE_LICENSE("Dual MPL/GPL");
#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */
#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */
#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */
+#define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */

#define HERMES_AUX_PW0 0xFE01
#define HERMES_AUX_PW1 0xDC23
#define HERMES_AUX_PW2 0xBA45

-/* End markers */
+/* End markers used in dblocks */
#define PDI_END 0x00000000 /* End of PDA */
#define BLOCK_END 0xFFFFFFFF /* Last image block */
+#define TEXT_END 0x1A /* End of text header */
+
+/*
+ * PDA == Production Data Area
+ *
+ * In principle, the max. size of the PDA is is 4096 words. Currently,
+ * however, only about 500 bytes of this area are used.
+ *
+ * Some USB implementations can't handle sizes in excess of 1016. Note
+ * that PDA is not actually used in those USB environments, but may be
+ * retrieved by common code.
+ */
+#define MAX_PDA_SIZE 1000
+
+/* Limit the amout we try to download in a single shot.
+ * Size is in bytes.
+ */
+#define MAX_DL_SIZE 1024
+#define LIMIT_PROGRAM_SIZE 0

/*
* The following structures have little-endian fields denoted by
@@ -112,7 +132,8 @@ struct pdi {
char data[0]; /* plug data */
} __attribute__ ((packed));

-/* Functions for access to little-endian data */
+/*** FW data block access functions ***/
+
static inline u32
dblock_addr(const struct dblock *blk)
{
@@ -125,6 +146,8 @@ dblock_len(const struct dblock *blk)
return le16_to_cpu(blk->len);
}

+/*** PDR Access functions ***/
+
static inline u32
pdr_id(const struct pdr *pdr)
{
@@ -143,6 +166,8 @@ pdr_len(const struct pdr *pdr)
return le32_to_cpu(pdr->len);
}

+/*** PDI Access functions ***/
+
static inline u32
pdi_id(const struct pdi *pdi)
{
@@ -156,49 +181,55 @@ pdi_len(const struct pdi *pdi)
return 2 * (le16_to_cpu(pdi->len) - 1);
}

-/* Set address of the auxiliary port */
+/*** Hermes AUX control ***/
+
static inline void
-spectrum_aux_setaddr(hermes_t *hw, u32 addr)
+hermes_aux_setaddr(hermes_t *hw, u32 addr)
{
hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
}

-/* Open access to the auxiliary port */
-static int
-spectrum_aux_open(hermes_t *hw)
+static inline int
+hermes_aux_control(hermes_t *hw, int enabled)
{
+ int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
+ int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
int i;

/* Already open? */
- if (hermes_read_reg(hw, HERMES_CONTROL) == HERMES_AUX_ENABLED)
+ if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
return 0;

hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
- hermes_write_reg(hw, HERMES_CONTROL, HERMES_AUX_ENABLE);
+ hermes_write_reg(hw, HERMES_CONTROL, action);

for (i = 0; i < 20; i++) {
udelay(10);
if (hermes_read_reg(hw, HERMES_CONTROL) ==
- HERMES_AUX_ENABLED)
+ desired_state)
return 0;
}

return -EBUSY;
}

+/*** Plug Data Functions ***/
+
/*
* Scan PDR for the record with the specified RECORD_ID.
* If it's not found, return NULL.
*/
static struct pdr *
-spectrum_find_pdr(struct pdr *first_pdr, u32 record_id)
+hermes_find_pdr(struct pdr *first_pdr, u32 record_id)
{
struct pdr *pdr = first_pdr;
+ void *end = (void *)first_pdr + MAX_PDA_SIZE;

- while (pdr_id(pdr) != PDI_END) {
+ while (((void *)pdr < end) &&
+ (pdr_id(pdr) != PDI_END)) {
/*
* PDR area is currently not terminated by PDI_END.
* It's followed by CRC records, which have the type
@@ -218,12 +249,12 @@ spectrum_find_pdr(struct pdr *first_pdr, u32 record_id)

/* Process one Plug Data Item - find corresponding PDR and plug it */
static int
-spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi)
+hermes_plug_pdi(hermes_t *hw, struct pdr *first_pdr, const struct pdi *pdi)
{
struct pdr *pdr;

- /* Find the PDI corresponding to this PDR */
- pdr = spectrum_find_pdr(first_pdr, pdi_id(pdi));
+ /* Find the PDR corresponding to this PDI */
+ pdr = hermes_find_pdr(first_pdr, pdi_id(pdi));

/* No match is found, safe to ignore */
if (!pdr)
@@ -234,96 +265,172 @@ spectrum_plug_pdi(hermes_t *hw, struct pdr *first_pdr, struct pdi *pdi)
return -EINVAL;

/* do the actual plugging */
- spectrum_aux_setaddr(hw, pdr_addr(pdr));
+ hermes_aux_setaddr(hw, pdr_addr(pdr));
hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));

return 0;
}

/* Read PDA from the adapter */
-int
-spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len)
+int hermes_read_pda(hermes_t *hw,
+ __le16 *pda,
+ u32 pda_addr,
+ u16 pda_len,
+ int use_eeprom) /* can we get this into hw? */
{
int ret;
- int pda_size;
+ u16 pda_size;
+ u16 data_len = pda_len;
+ __le16 *data = pda;

- /* Issue command to read EEPROM */
- ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
- if (ret)
- return ret;
+ if (use_eeprom) {
+ /* PDA of spectrum symbol is in eeprom */
+
+ /* Issue command to read EEPROM */
+ ret = hermes_docmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
+ if (ret)
+ return ret;
+ }

/* Open auxiliary port */
- ret = spectrum_aux_open(hw);
+ ret = hermes_aux_control(hw, 1);
+ printk(KERN_DEBUG PFX "AUX enable returned %d\n", ret);
if (ret)
return ret;

/* read PDA from EEPROM */
- spectrum_aux_setaddr(hw, PDA_ADDR);
- hermes_read_words(hw, HERMES_AUXDATA, pda, pda_len / 2);
+ hermes_aux_setaddr(hw, pda_addr);
+ hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
+
+ /* Close aux port */
+ ret = hermes_aux_control(hw, 0);
+ printk(KERN_DEBUG PFX "AUX disable returned %d\n", ret);

/* Check PDA length */
pda_size = le16_to_cpu(pda[0]);
+ printk(KERN_DEBUG PFX "Actual PDA length %d, Max allowed %d\n",
+ pda_size, pda_len);
if (pda_size > pda_len)
return -EINVAL;

return 0;
}
-EXPORT_SYMBOL(spectrum_read_pda);
+EXPORT_SYMBOL(hermes_read_pda);

-/* Parse PDA and write the records into the adapter */
-int
-spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block,
- __le16 *pda)
+/* Parse PDA and write the records into the adapter
+ *
+ * Attempt to write every records that is in the specified pda
+ * which also has a valid production data record for the firmware.
+ */
+int hermes_apply_pda(hermes_t *hw,
+ const char *first_pdr,
+ const __le16 *pda)
{
int ret;
- struct pdi *pdi;
- struct pdr *first_pdr;
- const struct dblock *blk = first_block;
-
- /* Skip all blocks to locate Plug Data References */
- while (dblock_addr(blk) != BLOCK_END)
- blk = (struct dblock *) &blk->data[dblock_len(blk)];
+ const struct pdi *pdi;
+ struct pdr *pdr;

- first_pdr = (struct pdr *) blk;
+ pdr = (struct pdr *) first_pdr;

/* Go through every PDI and plug them into the adapter */
- pdi = (struct pdi *) (pda + 2);
+ pdi = (const struct pdi *) (pda + 2);
while (pdi_id(pdi) != PDI_END) {
- ret = spectrum_plug_pdi(hw, first_pdr, pdi);
+ ret = hermes_plug_pdi(hw, pdr, pdi);
if (ret)
return ret;

/* Increment to the next PDI */
- pdi = (struct pdi *) &pdi->data[pdi_len(pdi)];
+ pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)];
}
return 0;
}
-EXPORT_SYMBOL(spectrum_apply_pda);
+EXPORT_SYMBOL(hermes_apply_pda);
+
+/* Identify the total number of bytes in all blocks
+ * including the header data.
+ */
+size_t
+hermes_blocks_length(const char *first_block)
+{
+ const struct dblock *blk = (const struct dblock *) first_block;
+ int total_len = 0;
+ int len;
+
+ /* Skip all blocks to locate Plug Data References
+ * (Spectrum CS) */
+ while (dblock_addr(blk) != BLOCK_END) {
+ len = dblock_len(blk);
+ total_len += sizeof(*blk) + len;
+ blk = (struct dblock *) &blk->data[len];
+ }
+
+ return total_len;
+}
+EXPORT_SYMBOL(hermes_blocks_length);
+
+/*** Hermes programming ***/

-/* Load firmware blocks into the adapter */
-int
-spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block)
+/* Program the data blocks */
+int hermes_program(hermes_t *hw, const char *first_block, const char *end)
{
const struct dblock *blk;
u32 blkaddr;
u32 blklen;
+#if LIMIT_PROGRAM_SIZE
+ u32 addr;
+ u32 len;
+#endif
+
+ blk = (const struct dblock *) first_block;
+
+ if ((const char *) blk > (end - sizeof(*blk)))
+ return -EIO;

- blk = first_block;
blkaddr = dblock_addr(blk);
blklen = dblock_len(blk);

- while (dblock_addr(blk) != BLOCK_END) {
- spectrum_aux_setaddr(hw, blkaddr);
+ while ((blkaddr != BLOCK_END) &&
+ (((const char *) blk + blklen) <= end)) {
+ printk(KERN_DEBUG PFX
+ "Programming block of length %d to address 0x%08x\n",
+ blklen, blkaddr);
+
+#if !LIMIT_PROGRAM_SIZE
+ /* wl_lkm driver splits this into writes of 2000 bytes */
+ hermes_aux_setaddr(hw, blkaddr);
hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,
blklen);
+#else
+ len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE;
+ addr = blkaddr;
+
+ while (addr < (blkaddr + blklen)) {
+ printk(KERN_DEBUG PFX
+ "Programming subblock of length %d "
+ "to address 0x%08x. Data @ %p\n",
+ len, addr, &blk->data[addr - blkaddr]);
+
+ hermes_aux_setaddr(hw, addr);
+ hermes_write_bytes(hw, HERMES_AUXDATA,
+ &blk->data[addr - blkaddr],
+ len);
+
+ addr += len;
+ len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ?
+ (blkaddr + blklen - addr) : MAX_DL_SIZE;
+ }
+#endif
+ blk = (const struct dblock *) &blk->data[blklen];
+
+ if ((const char *) blk > (end - sizeof(*blk)))
+ return -EIO;

- blk = (struct dblock *) &blk->data[blklen];
blkaddr = dblock_addr(blk);
blklen = dblock_len(blk);
}
return 0;
}
-EXPORT_SYMBOL(spectrum_load_blocks);
+EXPORT_SYMBOL(hermes_program);

static int __init init_hermes_dld(void)
{
diff --git a/drivers/net/wireless/hermes_dld.h b/drivers/net/wireless/hermes_dld.h
index 2c8892a..af75c03 100644
--- a/drivers/net/wireless/hermes_dld.h
+++ b/drivers/net/wireless/hermes_dld.h
@@ -27,19 +27,17 @@

#include "hermes.h"

-/* Position of PDA in the adapter memory */
-#define EEPROM_ADDR 0x3000
-#define EEPROM_LEN 0x200
-#define PDA_OFFSET 0x100
+int hermes_program(hermes_t *hw, const char *first_block, const char *end);

-#define PDA_ADDR (EEPROM_ADDR + PDA_OFFSET)
-#define PDA_WORDS ((EEPROM_LEN - PDA_OFFSET) / 2)
+int hermes_read_pda(hermes_t *hw,
+ __le16 *pda,
+ u32 pda_addr,
+ u16 pda_len,
+ int use_eeprom);
+int hermes_apply_pda(hermes_t *hw,
+ const char *first_pdr,
+ const __le16 *pda);

-struct dblock;
-
-int spectrum_read_pda(hermes_t *hw, __le16 *pda, int pda_len);
-int spectrum_apply_pda(hermes_t *hw, const struct dblock *first_block,
- __le16 *pda);
-int spectrum_load_blocks(hermes_t *hw, const struct dblock *first_block);
+size_t hermes_blocks_length(const char *first_block);

#endif /* _HERMES_DLD_H */
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c
index 579873d..2fb0018 100644
--- a/drivers/net/wireless/spectrum_cs.c
+++ b/drivers/net/wireless/spectrum_cs.c
@@ -166,11 +166,12 @@ spectrum_reset(struct pcmcia_device *link, int idle)
*/
static int
spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link,
- const unsigned char *image, int secondary)
+ const unsigned char *image, const unsigned char *end,
+ int secondary)
{
int ret;
const unsigned char *ptr;
- const struct dblock *first_block;
+ const unsigned char *first_block;

/* Plug Data Area (PDA) */
__le16 pda[PDA_WORDS];
@@ -178,11 +179,11 @@ spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link,
/* Binary block begins after the 0x1A marker */
ptr = image;
while (*ptr++ != TEXT_END);
- first_block = (const struct dblock *) ptr;
+ first_block = ptr;

- /* Read the PDA */
+ /* Read the PDA from EEPROM */
if (secondary) {
- ret = spectrum_read_pda(hw, pda, sizeof(pda));
+ ret = hermes_read_pda(hw, pda, PDA_ADDR, sizeof(pda), 1);
if (ret)
return ret;
}
@@ -193,13 +194,15 @@ spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link,
return ret;

/* Program the adapter with new firmware */
- ret = spectrum_load_blocks(hw, first_block);
+ ret = hermes_program(hw, first_block, end);
if (ret)
return ret;

/* Write the PDA to the adapter */
if (secondary) {
- ret = spectrum_apply_pda(hw, first_block, pda);
+ size_t len = hermes_blocks_length(first_block);
+ ptr = first_block + len;
+ ret = hermes_apply_pda(hw, ptr, pda);
if (ret)
return ret;
}
@@ -242,7 +245,8 @@ spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link)
}

/* Load primary firmware */
- ret = spectrum_dl_image(hw, link, fw_entry->data, 0);
+ ret = spectrum_dl_image(hw, link, fw_entry->data,
+ fw_entry->data + fw_entry->size, 0);
release_firmware(fw_entry);
if (ret) {
printk(KERN_ERR PFX "Primary firmware download failed\n");
@@ -257,7 +261,8 @@ spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link)
}

/* Load secondary firmware */
- ret = spectrum_dl_image(hw, link, fw_entry->data, 1);
+ ret = spectrum_dl_image(hw, link, fw_entry->data,
+ fw_entry->data + fw_entry->size, 1);
release_firmware(fw_entry);
if (ret) {
printk(KERN_ERR PFX "Secondary firmware download failed\n");
--
1.5.6.4


2008-08-21 22:28:22

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH v2 03/19] orinoco: Specify all three parameters to every Hermes command.

hermes_issue_cmd now takes two more parameters.

Signed-off-by: David Kilroy <[email protected]>
---
drivers/net/wireless/hermes.c | 13 +++++++------
1 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/drivers/net/wireless/hermes.c b/drivers/net/wireless/hermes.c
index 2cd86af..3a6ce5a 100644
--- a/drivers/net/wireless/hermes.c
+++ b/drivers/net/wireless/hermes.c
@@ -87,7 +87,8 @@ MODULE_LICENSE("Dual MPL/GPL");

Callable from any context.
*/
-static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0)
+static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0,
+ u16 param1, u16 param2)
{
int k = CMD_BUSY_TIMEOUT;
u16 reg;
@@ -103,8 +104,8 @@ static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0)
return -EBUSY;
}

- hermes_write_regn(hw, PARAM2, 0);
- hermes_write_regn(hw, PARAM1, 0);
+ hermes_write_regn(hw, PARAM2, param2);
+ hermes_write_regn(hw, PARAM1, param1);
hermes_write_regn(hw, PARAM0, param0);
hermes_write_regn(hw, CMD, cmd);

@@ -162,7 +163,7 @@ int hermes_init(hermes_t *hw)

/* We don't use hermes_docmd_wait here, because the reset wipes
the magic constant in SWSUPPORT0 away, and it gets confused */
- err = hermes_issue_cmd(hw, HERMES_CMD_INIT, 0);
+ err = hermes_issue_cmd(hw, HERMES_CMD_INIT, 0, 0, 0);
if (err)
return err;

@@ -216,7 +217,7 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
u16 reg;
u16 status;

- err = hermes_issue_cmd(hw, cmd, parm0);
+ err = hermes_issue_cmd(hw, cmd, parm0, 0, 0);
if (err) {
if (! hermes_present(hw)) {
if (net_ratelimit())
@@ -497,7 +498,7 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,

hermes_write_bytes(hw, dreg, value, count << 1);

- err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE,
+ err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE,
rid, NULL);

return err;
--
1.5.6.4