2008-08-02 10:14:48

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 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 is against wireless-testing (master-2008-07-16), 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-06 13:48:23

by Pavel Roskin

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

On Wed, 2008-08-06 at 09:13 -0400, Dan Williams wrote:
> On Tue, 2008-08-05 at 18:48 -0400, Pavel Roskin wrote:
> > > Scanning for a specific SSID never has "filtered" the results, nor
> > > should it. It just probe-scans the requested SSID and return any new
> > > results in with the cached ones. You requested an SSID scan, thus you
> > > must know the SSID, thus you can do the filtering yourself?
> >
> > Perhaps I used a wrong word. If requesting a scan, I expect to get
> > results from a scan with the parameters I supplied. If the scan results
> > are from a scan with different parameters, I don't want them. I'd
> > rather see the driver return EAGAIN than results of a scan with
> > different parameters.
>
> I'm not quite sure what you mean here. You mean to say that if you
> request a specific SSID scan, and the driver does not find that SSID,
> that it should return EAGAIN? I think that would be overloading EAGAIN
> too much, since for scans EAGAIN means "I'm not done with your scan
> request yet".

I mean, if we request a scan with some parameters, and the cached
results were received with different parameters, and real scanning is
impossible now for whatever reason, then return empty results and EAGAIN
code.

Anyway, we should have a policy for all drivers, so let's stick to that
policy.

--
Regards,
Pavel Roskin

2008-08-05 22:38:40

by Pavel Roskin

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

On Tue, 2008-08-05 at 00:09 +0100, Dave wrote:
> > However,
> > sparse finds two issues on x86_64, both due to sizeof() returning
> > size_t, which is wider than int.
> >
> > Here's the fix. Please integrate it into the patches that introduce the
> > code.
>
> Will do. I obviously need to upgrade my version of sparse, since it isn't complaining to me :(

It's not just sparse. gcc will complain too. But you'll need to
compile for a 64-bit kernel to see it.

> >> 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>
> > I tested it on my WPA2 AP and could not associate:
>
> I'm not familiar with the difference between WPA/WPA2. Is that expected to work?

WPA2 uses a different IE format. It is also required to support CCMP,
unlike WPA1, which defaults to TKIP.

I could associate to a WPA1 AP with TKIP, but only on a 32-bit system.
It still fails on the 64-bit system. wpa_supplicant prints this in a
loop:

RTM_NEWLINK: operstate=0 ifi_flags=0x11003 ([UP][LOWER_UP])
RTM_NEWLINK, IFLA_IFNAME: Interface 'eth1' added
RTM_NEWLINK: operstate=0 ifi_flags=0x11003 ([UP][LOWER_UP])
Wireless event: cmd=0x8c07 len=40
AssocReq IE wireless event - hexdump(len=24): dd 16 00 50 f2 01 01 00 00 50 f2 02 01 00 00 50 f2 02 01 00 00 50 f2 02
RTM_NEWLINK: operstate=0 ifi_flags=0x11003 ([UP][LOWER_UP])
Wireless event: cmd=0x8b15 len=24
Wireless event: new AP: 00:0f:66:2f:ef:59
Association info event
req_ies - hexdump(len=24): dd 16 00 50 f2 01 01 00 00 50 f2 02 01 00 00 50 f2 02 01 00 00 50 f2 02
WPA: set own WPA/RSN IE - hexdump(len=24): dd 16 00 50 f2 01 01 00 00 50 f2 02 01 00 00 50 f2 02 01 00 00 50 f2 02
State: ASSOCIATED -> ASSOCIATED
wpa_driver_wext_set_operstate: operstate 0->0 (DORMANT)
WEXT: Operstate: linkmode=-1, operstate=5
Associated with 00:0f:66:2f:ef:59
WPA: Association event - clear replay counter
WPA: Clear old PTK
Setting authentication timeout: 10 sec 0 usec
Cancelling scan request

And the kernel log is:

[ 7729.202964] eth1: New link status: Connected (0001)
[ 7738.072382] eth1: New link status: Disconnected (0002)
[ 7739.238773] eth1: New link status: Connected (0001)
[ 7748.527988] eth1: New link status: Disconnected (0002)
[ 7749.683346] eth1: New link status: Connected (0001)
[ 7758.983565] eth1: New link status: Disconnected (0002)

I'm sorry, I'm really limited in time right now to do more tests, and
I'm afraid I'll be mostly offline for the next 3 weeks.

--
Regards,
Pavel Roskin

2008-08-06 19:27:00

by Dave Kilroy

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

Pavel Roskin wrote:
> I mean, if we request a scan with some parameters, and the cached
> results were received with different parameters, and real scanning is
> impossible now for whatever reason, then return empty results and EAGAIN
> code.
>
> Anyway, we should have a policy for all drivers, so let's stick to that
> policy.

I've had a look at mac80211, since that seems to be a good indicator of policy (mlme.c). It caches BSSs in dev->ieee80211_ptr->local->sta_bss_list. It accumulates BSSs, and doesn't do anything to 'pick out' specific ssid results after a specific ssid scan is initiated, or clear the list before initiating the scan.

Without a mac80211 (or any other) card to check, it seems that the orinoco behaviour introduced by this patch is consistent with mac80211.


Regards,

Dave.

2008-08-06 18:33:25

by Dave Kilroy

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

Pavel Roskin wrote:
> On Mon, 2008-08-04 at 19:28 -0400, Dan Williams wrote:
>>> I'm not familiar with the difference between WPA/WPA2. Is that expected to work?
>>
>> But you might be able to get away with WPA2/RSN + TKIP if the AP allows
>> this configuration. In that configuration, the only difference between
>> WPA and WPA2/RSN would be the information element IDs, really. But if
>> the firmware itself doesn't say it supports WPA on whatever website it
>> came from, then likely the card won't do WPA2/RSN either.

The firmware supports WPA, but makes no mention of WPA2.

> I tried association to hostapd with madwifi, and the only working
> configuration is WPA1 only with TKIP. Even enabling WPA1 and WPA2 and
> TKIP makes the connection fail. Forcing WPA1 and TKIP in
> wpa_supplicant.conf doesn't help.
>
> I looked at the patches. They have references to TKIP, but not to CCMP.
> Yet it would be nice if we could support WPA1+WPA2, as we cannot require
> that access points stop supporting WPA2, which is the 802.11i standard.
> It's possible that we have an issue outside the driver.

I don't believe the firmware supports CCMP. It has support for CCX/CKIP (Cisco specific TKIP-alike), but I'm guess we don't care about that.

I suggest that we leave figuring out how to associate with a WPA2+TKIP AP to another day.

>>>> [185219.617236] eth1: Ext scan results too large (272 bytes).
>> Truncating
>>>> results to 270 bytes.

> I tried increasing the "data" size from 200 to 300 in hermes.h, and the
> message went away. I was able to associate to D-Link DIR-615 when it
> was set to WPA1.
>
> I think it should be safe to increase the side of "data" and remove the
> unused "flags" filed at the end. Let's make "data" 256 bytes to make it a nice
> round number.

Thanks for checking that. I'll set data to 316 bytes. This makes the agere_ext_scan_info 384 (256+128) bytes. That copes with the worst case you saw, and gives a nice alignment to the array of scan structures (total size now a round 24k).

> I'm sorry, I'm going to be offline soon, and I really cannot do any more
> tests.

Thank you for the feedback and testing, not to mention past maintainership of the driver.


Regards,

Dave.


2008-08-02 10:15:27

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 09/19] 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 <[email protected]>
---
drivers/net/wireless/Kconfig | 2 +-
drivers/net/wireless/airport.c | 3 +-
drivers/net/wireless/orinoco.c | 314 ++++++++++++++++++++++++++++++++-
drivers/net/wireless/orinoco.h | 9 +-
drivers/net/wireless/orinoco_cs.c | 3 +-
drivers/net/wireless/orinoco_nortel.c | 3 +-
drivers/net/wireless/orinoco_pci.c | 3 +-
drivers/net/wireless/orinoco_plx.c | 3 +-
drivers/net/wireless/orinoco_tmd.c | 3 +-
drivers/net/wireless/spectrum_cs.c | 159 ++---------------
10 files changed, 346 insertions(+), 156 deletions(-)

diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 5e0eda0..cff47f7 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -298,6 +298,7 @@ config HERMES
tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)"
depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211
select WIRELESS_EXT
+ select FW_LOADER
---help---
A driver for 802.11b wireless cards based on the "Hermes" or
Intersil HFA384x (Prism 2) MAC controller. This includes the vast
@@ -387,7 +388,6 @@ config PCMCIA_HERMES
config PCMCIA_SPECTRUM
tristate "Symbol Spectrum24 Trilogy PCMCIA card support"
depends on PCMCIA && HERMES
- select FW_LOADER
---help---

This is a driver for 802.11b cards using RAM-loadable Symbol
diff --git a/drivers/net/wireless/airport.c b/drivers/net/wireless/airport.c
index 6f7eb9f..ce03a2e 100644
--- a/drivers/net/wireless/airport.c
+++ b/drivers/net/wireless/airport.c
@@ -180,7 +180,8 @@ airport_attach(struct macio_dev *mdev, const struct of_device_id *match)
}

/* Allocate space for private device-specific data */
- dev = alloc_orinocodev(sizeof(*card), airport_hard_reset);
+ dev = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev,
+ airport_hard_reset, NULL);
if (! dev) {
printk(KERN_ERR PFX "Cannot allocate network device\n");
return -ENODEV;
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 655d030..401ef32 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -82,12 +82,14 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
+#include <linux/firmware.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <net/iw_handler.h>
#include <net/ieee80211.h>

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

/********************************************************************/
@@ -301,6 +303,272 @@ static void orinoco_bss_data_init(struct orinoco_private *priv)
list_add_tail(&priv->bss_data[i].list, &priv->bss_free_list);
}

+
+/********************************************************************/
+/* Download functionality */
+/********************************************************************/
+
+struct fw_info {
+ char *pri_fw;
+ char *sta_fw;
+ char *ap_fw;
+ unsigned int pda_addr;
+ unsigned int pda_size;
+};
+
+const static struct fw_info orinoco_fw[] = {
+ { "", "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 },
+ { "", "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 },
+ { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", "", 0x00003100, 0x100 }
+};
+
+/* 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));
+
+/* Download either STA or AP firmware into the card. */
+static int
+orinoco_dl_firmware(struct orinoco_private *priv,
+ const struct fw_info *fw,
+ int ap)
+{
+ /* Plug Data Area (PDA) */
+ __le16 pda[512] = { 0 };
+
+ hermes_t *hw = &priv->hw;
+ const struct firmware *fw_entry;
+ const struct orinoco_fw_header *hdr;
+ const unsigned char *first_block;
+ const unsigned char *end;
+ const char *firmware;
+ struct net_device *dev = priv->ndev;
+ int err;
+
+ if (ap)
+ firmware = fw->ap_fw;
+ else
+ firmware = fw->sta_fw;
+
+ printk(KERN_DEBUG "%s: Attempting to download firmware %s\n",
+ dev->name, firmware);
+
+ /* Read current plug data */
+ err = hermes_read_pda(hw, pda, fw->pda_addr,
+ min(fw->pda_size, sizeof(pda)), 0);
+ printk(KERN_DEBUG "%s: Read PDA returned %d\n", dev->name, err);
+ if (err)
+ return err;
+
+ err = request_firmware(&fw_entry, firmware, priv->dev);
+ if (err) {
+ printk(KERN_ERR "%s: Cannot find firmware %s\n",
+ dev->name, firmware);
+ return -ENOENT;
+ }
+
+ hdr = (const struct orinoco_fw_header *) fw_entry->data;
+
+ /* Enable aux port to allow programming */
+ err = hermesi_program_init(hw, le32_to_cpu(hdr->entry_point));
+ printk(KERN_DEBUG "%s: Program init returned %d\n", dev->name, err);
+ if (err != 0)
+ goto abort;
+
+ /* Program data */
+ first_block = (fw_entry->data +
+ le16_to_cpu(hdr->headersize) +
+ le32_to_cpu(hdr->block_offset));
+ end = fw_entry->data + fw_entry->size;
+
+ err = hermes_program(hw, first_block, end);
+ printk(KERN_DEBUG "%s: Program returned %d\n", dev->name, 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 "%s: Apply PDA returned %d\n", dev->name, err);
+ if (err)
+ goto abort;
+
+ /* Tell card we've finished */
+ err = hermesi_program_end(hw);
+ printk(KERN_DEBUG "%s: Program end returned %d\n", dev->name, err);
+ if (err != 0)
+ goto abort;
+
+ /* Check if we're running */
+ printk(KERN_DEBUG "%s: hermes_present returned %d\n",
+ dev->name, hermes_present(hw));
+
+abort:
+ release_firmware(fw_entry);
+ return err;
+}
+
+/* End markers */
+#define TEXT_END 0x1A /* End of text header */
+
+/*
+ * 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.
+ */
+static int
+symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
+ const unsigned char *image, const unsigned char *end,
+ int secondary)
+{
+ hermes_t *hw = &priv->hw;
+ int ret;
+ const unsigned char *ptr;
+ const unsigned char *first_block;
+
+ /* Plug Data Area (PDA) */
+ __le16 pda[256];
+
+ /* Binary block begins after the 0x1A marker */
+ ptr = image;
+ while (*ptr++ != TEXT_END);
+ first_block = ptr;
+
+ /* Read the PDA from EEPROM */
+ if (secondary) {
+ ret = hermes_read_pda(hw, pda, fw->pda_addr, sizeof(pda), 1);
+ if (ret)
+ return ret;
+ }
+
+ /* Stop the firmware, so that it can be safely rewritten */
+ if (priv->stop_fw) {
+ ret = priv->stop_fw(priv, 1);
+ if (ret)
+ return ret;
+ }
+
+ /* Program the adapter with new firmware */
+ ret = hermes_program(hw, first_block, end);
+ if (ret)
+ return ret;
+
+ /* Write the PDA to the adapter */
+ if (secondary) {
+ size_t len = hermes_blocks_length(first_block);
+ ptr = first_block + len;
+ ret = hermes_apply_pda(hw, ptr, pda);
+ if (ret)
+ return ret;
+ }
+
+ /* Run the firmware */
+ if (priv->stop_fw) {
+ ret = priv->stop_fw(priv, 0);
+ if (ret)
+ return ret;
+ }
+
+ /* Reset hermes chip and make sure it responds */
+ ret = hermes_init(hw);
+
+ /* hermes_reset() should return 0 with the secondary firmware */
+ if (secondary && ret != 0)
+ return -ENODEV;
+
+ /* And this should work with any firmware */
+ if (!hermes_present(hw))
+ return -ENODEV;
+
+ return 0;
+}
+
+
+/*
+ * 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
+symbol_dl_firmware(struct orinoco_private *priv,
+ const struct fw_info *fw)
+{
+ struct net_device *dev = priv->ndev;
+ int ret;
+ const struct firmware *fw_entry;
+
+ if (request_firmware(&fw_entry, fw->pri_fw,
+ priv->dev) != 0) {
+ printk(KERN_ERR "%s: Cannot find firmware: %s\n",
+ dev->name, fw->pri_fw);
+ return -ENOENT;
+ }
+
+ /* Load primary firmware */
+ ret = symbol_dl_image(priv, fw, fw_entry->data,
+ fw_entry->data + fw_entry->size, 0);
+ release_firmware(fw_entry);
+ if (ret) {
+ printk(KERN_ERR "%s: Primary firmware download failed\n",
+ dev->name);
+ return ret;
+ }
+
+ if (request_firmware(&fw_entry, fw->sta_fw,
+ priv->dev) != 0) {
+ printk(KERN_ERR "%s: Cannot find firmware: %s\n",
+ dev->name, fw->sta_fw);
+ return -ENOENT;
+ }
+
+ /* Load secondary firmware */
+ ret = symbol_dl_image(priv, fw, fw_entry->data,
+ fw_entry->data + fw_entry->size, 1);
+ release_firmware(fw_entry);
+ if (ret) {
+ printk(KERN_ERR "%s: Secondary firmware download failed\n",
+ dev->name);
+ }
+
+ return ret;
+}
+
+static int orinoco_download(struct orinoco_private *priv)
+{
+ int err = 0;
+ /* Reload firmware */
+ switch (priv->firmware_type) {
+ case FIRMWARE_TYPE_AGERE:
+ /* case FIRMWARE_TYPE_INTERSIL: */
+ err = orinoco_dl_firmware(priv,
+ &orinoco_fw[priv->firmware_type], 0);
+ break;
+
+ case FIRMWARE_TYPE_SYMBOL:
+ err = symbol_dl_firmware(priv,
+ &orinoco_fw[priv->firmware_type]);
+ break;
+ case FIRMWARE_TYPE_INTERSIL:
+ break;
+ }
+ /* TODO: if we fail we probably need to reinitialise
+ * the driver */
+
+ return err;
+}
+
/********************************************************************/
/* Device methods */
/********************************************************************/
@@ -2050,6 +2318,12 @@ static void orinoco_reset(struct work_struct *work)
}
}

+ if (priv->do_fw_download) {
+ err = orinoco_download(priv);
+ if (err)
+ priv->do_fw_download = 0;
+ }
+
err = orinoco_reinit_firmware(dev);
if (err) {
printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n",
@@ -2261,6 +2535,7 @@ static int determine_firmware(struct net_device *dev)
priv->has_ibss = 1;
priv->has_wep = 0;
priv->has_big_wep = 0;
+ priv->do_fw_download = 0;

/* Determine capabilities from the firmware version */
switch (priv->firmware_type) {
@@ -2280,6 +2555,7 @@ static int determine_firmware(struct net_device *dev)
priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
priv->ibss_port = 1;
priv->has_hostscan = (firmver >= 0x8000a);
+ priv->do_fw_download = 1;
priv->broken_monitor = (firmver >= 0x80000);

/* Tested with Agere firmware :
@@ -2324,6 +2600,21 @@ static int determine_firmware(struct net_device *dev)
firmver >= 0x31000;
priv->has_preamble = (firmver >= 0x20000);
priv->ibss_port = 4;
+
+ /* Symbol firmware is found on various cards, but
+ * there has been no attempt to check firmware
+ * download on non-spectrum_cs based cards.
+ *
+ * Given that the Agere firmware download works
+ * differently, we should avoid doing a firmware
+ * download with the Symbol algorithm on non-spectrum
+ * cards.
+ *
+ * For now we can identify a spectrum_cs based card
+ * because it has a firmware reset function.
+ */
+ priv->do_fw_download = (priv->stop_fw != NULL);
+
priv->broken_disableport = (firmver == 0x25013) ||
(firmver >= 0x30000 && firmver <= 0x31000);
priv->has_hostscan = (firmver >= 0x31001) ||
@@ -2394,6 +2685,20 @@ static int orinoco_init(struct net_device *dev)
goto out;
}

+ if (priv->do_fw_download) {
+ err = orinoco_download(priv);
+ if (err)
+ priv->do_fw_download = 0;
+
+ /* Check firmware version again */
+ err = determine_firmware(dev);
+ if (err != 0) {
+ printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
+ dev->name);
+ goto out;
+ }
+ }
+
if (priv->has_port3)
printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n", dev->name);
if (priv->has_ibss)
@@ -2536,8 +2841,11 @@ static int orinoco_init(struct net_device *dev)
return err;
}

-struct net_device *alloc_orinocodev(int sizeof_card,
- int (*hard_reset)(struct orinoco_private *))
+struct net_device
+*alloc_orinocodev(int sizeof_card,
+ struct device *device,
+ int (*hard_reset)(struct orinoco_private *),
+ int (*stop_fw)(struct orinoco_private *, int))
{
struct net_device *dev;
struct orinoco_private *priv;
@@ -2552,6 +2860,7 @@ struct net_device *alloc_orinocodev(int sizeof_card,
+ sizeof(struct orinoco_private));
else
priv->card = NULL;
+ priv->dev = device;

if (orinoco_bss_data_allocate(priv))
goto err_out_free;
@@ -2577,6 +2886,7 @@ struct net_device *alloc_orinocodev(int sizeof_card,
dev->open = orinoco_open;
dev->stop = orinoco_stop;
priv->hard_reset = hard_reset;
+ priv->stop_fw = stop_fw;

spin_lock_init(&priv->lock);
priv->open = 0;
diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index c6b1858..e0acb63 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -44,7 +44,9 @@ typedef struct {

struct orinoco_private {
void *card; /* Pointer to card dependent structure */
+ struct device *dev;
int (*hard_reset)(struct orinoco_private *);
+ int (*stop_fw)(struct orinoco_private *, int);

/* Synchronisation stuff */
spinlock_t lock;
@@ -83,6 +85,7 @@ struct orinoco_private {
unsigned int has_preamble:1;
unsigned int has_sensitivity:1;
unsigned int has_hostscan:1;
+ unsigned int do_fw_download:1;
unsigned int broken_disableport:1;
unsigned int broken_monitor:1;

@@ -130,8 +133,10 @@ extern int orinoco_debug;
/* Exported prototypes */
/********************************************************************/

-extern struct net_device *alloc_orinocodev(int sizeof_card,
- int (*hard_reset)(struct orinoco_private *));
+extern struct net_device *alloc_orinocodev(
+ int sizeof_card, struct device *device,
+ int (*hard_reset)(struct orinoco_private *),
+ int (*stop_fw)(struct orinoco_private *, int));
extern void free_orinocodev(struct net_device *dev);
extern int __orinoco_up(struct net_device *dev);
extern int __orinoco_down(struct net_device *dev);
diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c
index 1c216e0..1ccf5a4 100644
--- a/drivers/net/wireless/orinoco_cs.c
+++ b/drivers/net/wireless/orinoco_cs.c
@@ -109,7 +109,8 @@ orinoco_cs_probe(struct pcmcia_device *link)
struct orinoco_private *priv;
struct orinoco_pccard *card;

- dev = alloc_orinocodev(sizeof(*card), orinoco_cs_hard_reset);
+ dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link),
+ orinoco_cs_hard_reset, NULL);
if (! dev)
return -ENOMEM;
priv = netdev_priv(dev);
diff --git a/drivers/net/wireless/orinoco_nortel.c b/drivers/net/wireless/orinoco_nortel.c
index 35ec5fc..2fc8659 100644
--- a/drivers/net/wireless/orinoco_nortel.c
+++ b/drivers/net/wireless/orinoco_nortel.c
@@ -182,7 +182,8 @@ static int orinoco_nortel_init_one(struct pci_dev *pdev,
}

/* Allocate network device */
- dev = alloc_orinocodev(sizeof(*card), orinoco_nortel_cor_reset);
+ dev = alloc_orinocodev(sizeof(*card), &pdev->dev,
+ orinoco_nortel_cor_reset, NULL);
if (!dev) {
printk(KERN_ERR PFX "Cannot allocate network device\n");
err = -ENOMEM;
diff --git a/drivers/net/wireless/orinoco_pci.c b/drivers/net/wireless/orinoco_pci.c
index 2547d5d..4ebd638 100644
--- a/drivers/net/wireless/orinoco_pci.c
+++ b/drivers/net/wireless/orinoco_pci.c
@@ -139,7 +139,8 @@ static int orinoco_pci_init_one(struct pci_dev *pdev,
}

/* Allocate network device */
- dev = alloc_orinocodev(sizeof(*card), orinoco_pci_cor_reset);
+ dev = alloc_orinocodev(sizeof(*card), &pdev->dev,
+ orinoco_pci_cor_reset, NULL);
if (!dev) {
printk(KERN_ERR PFX "Cannot allocate network device\n");
err = -ENOMEM;
diff --git a/drivers/net/wireless/orinoco_plx.c b/drivers/net/wireless/orinoco_plx.c
index 98fe165..ef76185 100644
--- a/drivers/net/wireless/orinoco_plx.c
+++ b/drivers/net/wireless/orinoco_plx.c
@@ -221,7 +221,8 @@ static int orinoco_plx_init_one(struct pci_dev *pdev,
}

/* Allocate network device */
- dev = alloc_orinocodev(sizeof(*card), orinoco_plx_cor_reset);
+ dev = alloc_orinocodev(sizeof(*card), &pdev->dev,
+ orinoco_plx_cor_reset, NULL);
if (!dev) {
printk(KERN_ERR PFX "Cannot allocate network device\n");
err = -ENOMEM;
diff --git a/drivers/net/wireless/orinoco_tmd.c b/drivers/net/wireless/orinoco_tmd.c
index df49318..ede24ec 100644
--- a/drivers/net/wireless/orinoco_tmd.c
+++ b/drivers/net/wireless/orinoco_tmd.c
@@ -124,7 +124,8 @@ static int orinoco_tmd_init_one(struct pci_dev *pdev,
}

/* Allocate network device */
- dev = alloc_orinocodev(sizeof(*card), orinoco_tmd_cor_reset);
+ dev = alloc_orinocodev(sizeof(*card), &pdev->dev,
+ orinoco_tmd_cor_reset, NULL);
if (!dev) {
printk(KERN_ERR PFX "Cannot allocate network device\n");
err = -ENOMEM;
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c
index 2fb0018..e368759 100644
--- a/drivers/net/wireless/spectrum_cs.c
+++ b/drivers/net/wireless/spectrum_cs.c
@@ -25,7 +25,6 @@
#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>
@@ -33,10 +32,6 @@
#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";

/********************************************************************/
/* Module stuff */
@@ -72,26 +67,11 @@ struct orinoco_pccard {
static int spectrum_cs_config(struct pcmcia_device *link);
static void spectrum_cs_release(struct pcmcia_device *link);

-/********************************************************************/
-/* Firmware downloader */
-/********************************************************************/
-
-/* 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)
-
/* Constants for the CISREG_CCSR register */
#define HCR_RUN 0x07 /* run firmware after reset */
#define HCR_IDLE 0x0E /* don't run firmware after reset */
#define HCR_MEM16 0x10 /* memory width bit, should be preserved */

-/* End markers */
-#define TEXT_END 0x1A /* End of text header */
-

#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
@@ -158,142 +138,29 @@ spectrum_reset(struct pcmcia_device *link, int idle)
return -ENODEV;
}

+/********************************************************************/
+/* Device methods */
+/********************************************************************/

-/*
- * 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.
- */
static int
-spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link,
- const unsigned char *image, const unsigned char *end,
- int secondary)
+spectrum_cs_hard_reset(struct orinoco_private *priv)
{
- int ret;
- const unsigned char *ptr;
- const unsigned char *first_block;
-
- /* Plug Data Area (PDA) */
- __le16 pda[PDA_WORDS];
-
- /* Binary block begins after the 0x1A marker */
- ptr = image;
- while (*ptr++ != TEXT_END);
- first_block = ptr;
-
- /* Read the PDA from EEPROM */
- if (secondary) {
- ret = hermes_read_pda(hw, pda, PDA_ADDR, sizeof(pda), 1);
- if (ret)
- return ret;
- }
-
- /* Stop the firmware, so that it can be safely rewritten */
- ret = spectrum_reset(link, 1);
- if (ret)
- return ret;
-
- /* Program the adapter with new firmware */
- ret = hermes_program(hw, first_block, end);
- if (ret)
- return ret;
-
- /* Write the PDA to the adapter */
- if (secondary) {
- size_t len = hermes_blocks_length(first_block);
- ptr = first_block + len;
- ret = hermes_apply_pda(hw, ptr, pda);
- if (ret)
- return ret;
- }
-
- /* Run the firmware */
- ret = spectrum_reset(link, 0);
- if (ret)
- return ret;
-
- /* Reset hermes chip and make sure it responds */
- ret = hermes_init(hw);
-
- /* hermes_reset() should return 0 with the secondary firmware */
- if (secondary && ret != 0)
- return -ENODEV;
+ struct orinoco_pccard *card = priv->card;
+ struct pcmcia_device *link = card->p_dev;

- /* And this should work with any firmware */
- if (!hermes_present(hw))
- return -ENODEV;
+ /* Soft reset using COR and HCR */
+ spectrum_reset(link, 0);

return 0;
}

-
-/*
- * 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
-spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link)
-{
- int ret;
- const struct firmware *fw_entry;
-
- if (request_firmware(&fw_entry, primary_fw_name,
- &handle_to_dev(link)) != 0) {
- printk(KERN_ERR PFX "Cannot find firmware: %s\n",
- primary_fw_name);
- return -ENOENT;
- }
-
- /* Load primary firmware */
- 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");
- return ret;
- }
-
- if (request_firmware(&fw_entry, secondary_fw_name,
- &handle_to_dev(link)) != 0) {
- printk(KERN_ERR PFX "Cannot find firmware: %s\n",
- secondary_fw_name);
- return -ENOENT;
- }
-
- /* Load secondary firmware */
- 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");
- }
-
- return ret;
-}
-
-/********************************************************************/
-/* Device methods */
-/********************************************************************/
-
-static int
-spectrum_cs_hard_reset(struct orinoco_private *priv)
+spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle)
{
struct orinoco_pccard *card = priv->card;
struct pcmcia_device *link = card->p_dev;
- int err;

- if (!hermes_present(&priv->hw)) {
- /* The firmware needs to be reloaded */
- if (spectrum_dl_firmware(&priv->hw, link) != 0) {
- printk(KERN_ERR PFX "Firmware download failed\n");
- err = -ENODEV;
- }
- } else {
- /* Soft reset using COR and HCR */
- spectrum_reset(link, 0);
- }
-
- return 0;
+ return spectrum_reset(link, idle);
}

/********************************************************************/
@@ -315,7 +182,9 @@ spectrum_cs_probe(struct pcmcia_device *link)
struct orinoco_private *priv;
struct orinoco_pccard *card;

- dev = alloc_orinocodev(sizeof(*card), spectrum_cs_hard_reset);
+ dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link),
+ spectrum_cs_hard_reset,
+ spectrum_cs_stop_firmware);
if (! dev)
return -ENOMEM;
priv = netdev_priv(dev);
@@ -517,7 +386,7 @@ spectrum_cs_config(struct pcmcia_device *link)
dev->irq = link->irq.AssignedIRQ;
card->node.major = card->node.minor = 0;

- /* Reset card and download firmware */
+ /* Reset card */
if (spectrum_cs_hard_reset(priv) != 0) {
goto failed;
}
--
1.5.4.5


2008-08-02 10:15:47

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 14/19] orinoco: Split wevent work thread from wevent sending

This allows us to send more wevents from the work thread. We will need
to do this to support WPA.

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

diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 013af6e..b91b6cb 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -1424,34 +1424,38 @@ static void orinoco_join_ap(struct work_struct *work)
}

/* Send new BSSID to userspace */
-static void orinoco_send_wevents(struct work_struct *work)
+static void orinoco_send_bssid_wevent(struct orinoco_private *priv)
{
- struct orinoco_private *priv =
- container_of(work, struct orinoco_private, wevent_work);
struct net_device *dev = priv->ndev;
struct hermes *hw = &priv->hw;
union iwreq_data wrqu;
int err;
- unsigned long flags;
-
- if (orinoco_lock(priv, &flags) != 0)
- return;

err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENTBSSID,
ETH_ALEN, NULL, wrqu.ap_addr.sa_data);
if (err != 0)
- goto out;
+ return;

wrqu.ap_addr.sa_family = ARPHRD_ETHER;

/* Send event to user space */
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+}
+
+static void orinoco_send_wevents(struct work_struct *work)
+{
+ struct orinoco_private *priv =
+ container_of(work, struct orinoco_private, wevent_work);
+ unsigned long flags;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return;
+
+ orinoco_send_bssid_wevent(priv);

- out:
orinoco_unlock(priv, &flags);
}

-
static inline void orinoco_clear_scan_results(struct orinoco_private *priv,
unsigned long scan_age)
{
--
1.5.4.5


2008-08-06 21:05:43

by Dan Williams

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

On Wed, 2008-08-06 at 16:56 -0400, Dan Williams wrote:
> On Wed, 2008-08-06 at 20:29 +0100, Dave wrote:
> > Dan Williams wrote:
> > > On Tue, 2008-08-05 at 18:48 -0400, Pavel Roskin wrote:
> > >> The userspace is welcome to keep a pool of all APs found by any scans,
> > >> but I don't think drivers should do it.
> > >
> > > Drivers need to keep a reasonably complete list of APs internally for
> > > association anyway. You don't want to have to do a full scan just to
> > > associate if you have results from 10 seconds ago that are still valid.
> >
> > This is not an issue for orinoco cards, as the firmware selects the AP to use independently of any driver scanning. The requirement to use ap_scan=2 means that wpa_supplicant doesn't need to look at the scan results either (except maybe to verify the configuration matches).
>
> Well, since the driver supports SSID scanning, we can use ap_scan=1
> anyway. ap_scan=2 is actually pretty evil since it depends on WEXT
> ordering and whatnot.

As a follow-up, ap_scan is in practice more about hidden SSID support,
and less about roaming. Most firmware handles roaming internally
anyway, even if ap_scan=2 is used. The only way to actually disable
firmware/driver roaming is to lock the BSSID with SIOCSIWAP.

Dan

> Dan
>
> > > Furthermore, drivers need to keep cached results so that two processes
> > > can request results of a scan after the scan is complete.
> >
> > But this does apply to orinoco.
> >
> >
> > Regards,
> >
> > Dave.
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> > the body of a message to [email protected]
> > More majordomo info at http://vger.kernel.org/majordomo-info.html
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html


2008-08-02 10:15:04

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 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 5504155..aa95349 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.4.5


2008-08-02 10:15:57

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 16/19] orinoco: Add WE-18 ioctls for WPA

Includes basic plumbing to get the data into firmware, and retrieve it.

SIOCxIWGENIE simply record (and return) the IE, and do not act on it.

SIOCxIWENCODEEXT, SIOCxIWAUTH and SIOCSIWMLME should be as functional as
the driver will support.

Signed-off-by: David Kilroy <[email protected]>
---
drivers/net/wireless/hermes_rid.h | 16 ++
drivers/net/wireless/orinoco.c | 526 ++++++++++++++++++++++++++++++++++++-
drivers/net/wireless/orinoco.h | 20 ++
3 files changed, 554 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/hermes_rid.h b/drivers/net/wireless/hermes_rid.h
index bcd9c82..42eb67d 100644
--- a/drivers/net/wireless/hermes_rid.h
+++ b/drivers/net/wireless/hermes_rid.h
@@ -30,6 +30,7 @@
#define HERMES_RID_CNFWEPENABLED_AGERE 0xFC20
#define HERMES_RID_CNFAUTHENTICATION_AGERE 0xFC21
#define HERMES_RID_CNFMANDATORYBSSID_SYMBOL 0xFC21
+#define HERMES_RID_CNFDROPUNENCRYPTED 0xFC22
#define HERMES_RID_CNFWEPDEFAULTKEYID 0xFC23
#define HERMES_RID_CNFDEFAULTKEY0 0xFC24
#define HERMES_RID_CNFDEFAULTKEY1 0xFC25
@@ -85,7 +86,16 @@
#define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2
#define HERMES_RID_CNFBASICRATES 0xFCB3
#define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4
+#define HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE 0xFCB4
+#define HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE 0xFCB5
+#define HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE 0xFCB6
+#define HERMES_RID_CNFADDMAPPEDTKIPKEY_AGERE 0xFCB7
+#define HERMES_RID_CNFREMMAPPEDTKIPKEY_AGERE 0xFCB8
+#define HERMES_RID_CNFSETWPACAPABILITIES_AGERE 0xFCB9
+#define HERMES_RID_CNFCACHEDPMKADDRESS 0xFCBA
+#define HERMES_RID_CNFREMOVEPMKADDRESS 0xFCBB
#define HERMES_RID_CNFSCANCHANNELS2GHZ 0xFCC2
+#define HERMES_RID_CNFDISASSOCIATE 0xFCC8
#define HERMES_RID_CNFTICKTIME 0xFCE0
#define HERMES_RID_CNFSCANREQUEST 0xFCE1
#define HERMES_RID_CNFJOINREQUEST 0xFCE2
@@ -138,6 +148,12 @@
#define HERMES_RID_CURRENTTXRATE6 0xFD85
#define HERMES_RID_OWNMACADDR 0xFD86
#define HERMES_RID_SCANRESULTSTABLE 0xFD88
+#define HERMES_RID_CURRENT_COUNTRY_INFO 0xFD89
+#define HERMES_RID_CURRENT_WPA_IE 0xFD8A
+#define HERMES_RID_CURRENT_TKIP_IV 0xFD8B
+#define HERMES_RID_CURRENT_ASSOC_REQ_INFO 0xFD8C
+#define HERMES_RID_CURRENT_ASSOC_RESP_INFO 0xFD8D
+#define HERMES_RID_TXQUEUEEMPTY 0xFD91
#define HERMES_RID_PHYTYPE 0xFDC0
#define HERMES_RID_CURRENTCHANNEL 0xFDC1
#define HERMES_RID_CURRENTPOWERSTATE 0xFDC2
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 59720f9..7054c80 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -79,6 +79,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
@@ -2038,7 +2039,7 @@ static int __orinoco_hw_set_wap(struct orinoco_private *priv)
}

/* Change the WEP keys and/or the current keys. Can be called
- * either from __orinoco_hw_setup_wep() or directly from
+ * either from __orinoco_hw_setup_enc() or directly from
* orinoco_ioctl_setiwencode(). In the later case the association
* with the AP is not broken (if the firmware can handle it),
* which is needed for 802.1x implementations. */
@@ -2098,7 +2099,7 @@ static int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv)
return 0;
}

-static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
+static int __orinoco_hw_setup_enc(struct orinoco_private *priv)
{
hermes_t *hw = &priv->hw;
int err = 0;
@@ -2106,7 +2107,8 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
int auth_flag;
int enc_flag;

- if (priv->encode_alg == IW_ENCODE_ALG_WEP)
+ /* Setup WEP keys for WEP and WPA */
+ if (priv->encode_alg)
__orinoco_hw_setup_wepkeys(priv);

if (priv->wep_restrict)
@@ -2114,7 +2116,9 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
else
auth_flag = HERMES_AUTH_OPEN;

- if (priv->encode_alg == IW_ENCODE_ALG_WEP)
+ if (priv->wpa_enabled)
+ enc_flag = 2;
+ else if (priv->encode_alg == IW_ENCODE_ALG_WEP)
enc_flag = 1;
else
enc_flag = 0;
@@ -2132,6 +2136,16 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
enc_flag);
if (err)
return err;
+
+ if (priv->has_wpa) {
+ /* Set WPA key management */
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE,
+ priv->key_mgmt);
+ if (err)
+ return err;
+ }
+
break;

case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
@@ -2168,6 +2182,84 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
return 0;
}

+/* key must be 32 bytes, including the tx and rx MIC keys.
+ * rsc must be 8 bytes
+ * tsc must be 8 bytes or NULL
+ */
+static int __orinoco_hw_set_tkip_key(hermes_t *hw, int key_idx, int set_tx,
+ u8 *key, u8 *rsc, u8 *tsc)
+{
+ struct {
+ __le16 idx;
+ u8 rsc[IW_ENCODE_SEQ_MAX_SIZE];
+ u8 key[TKIP_KEYLEN];
+ u8 tx_mic[MIC_KEYLEN];
+ u8 rx_mic[MIC_KEYLEN];
+ u8 tsc[IW_ENCODE_SEQ_MAX_SIZE];
+ } __attribute__ ((packed)) buf;
+ int ret;
+ int err;
+ int k;
+ u16 xmitting;
+
+ key_idx &= 0x3;
+
+ if (set_tx)
+ key_idx |= 0x8000;
+
+ buf.idx = cpu_to_le16(key_idx);
+ memcpy(buf.key, key,
+ sizeof(buf.key) + sizeof(buf.tx_mic) + sizeof(buf.rx_mic));
+
+ if (rsc == NULL)
+ memset(buf.rsc, 0, sizeof(buf.rsc));
+ else
+ memcpy(buf.rsc, rsc, sizeof(buf.rsc));
+
+ if (tsc == NULL) {
+ memset(buf.tsc, 0, sizeof(buf.tsc));
+ buf.tsc[4] = 0x10;
+ } else {
+ memcpy(buf.tsc, tsc, sizeof(buf.tsc));
+ }
+
+ /* Wait upto 100ms for tx queue to empty */
+ k = 100;
+ do {
+ k--;
+ udelay(1000);
+ ret = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_TXQUEUEEMPTY,
+ &xmitting);
+ if (ret)
+ break;
+ } while ((k > 0) && xmitting);
+
+ if (k == 0)
+ ret = -ETIMEDOUT;
+
+ err = HERMES_WRITE_RECORD(hw, USER_BAP,
+ HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE,
+ &buf);
+
+ return ret ? ret : err;
+}
+
+static int orinoco_clear_tkip_key(struct orinoco_private *priv,
+ int key_idx)
+{
+ hermes_t *hw = &priv->hw;
+ int err;
+
+ memset(&priv->tkip_key[key_idx], 0, sizeof(priv->tkip_key[key_idx]));
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE,
+ key_idx);
+ if (err)
+ printk(KERN_WARNING "%s: Error %d clearing TKIP key %d\n",
+ priv->ndev->name, err, key_idx);
+ return err;
+}
+
static int __orinoco_program_rids(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);
@@ -2364,10 +2456,10 @@ static int __orinoco_program_rids(struct net_device *dev)
}

/* Set up encryption */
- if (priv->has_wep) {
- err = __orinoco_hw_setup_wep(priv);
+ if (priv->has_wep || priv->has_wpa) {
+ err = __orinoco_hw_setup_enc(priv);
if (err) {
- printk(KERN_ERR "%s: Error %d activating WEP\n",
+ printk(KERN_ERR "%s: Error %d activating encryption\n",
dev->name, err);
return err;
}
@@ -2727,6 +2819,7 @@ static int determine_firmware(struct net_device *dev)
priv->has_big_wep = 0;
priv->has_alt_txcntl = 0;
priv->has_ext_scan = 0;
+ priv->has_wpa = 0;
priv->do_fw_download = 0;

/* Determine capabilities from the firmware version */
@@ -2751,6 +2844,7 @@ static int determine_firmware(struct net_device *dev)
priv->broken_monitor = (firmver >= 0x80000);
priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
+ priv->has_wpa = (firmver >= 0x9002a);
/* Tested with Agere firmware :
* 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
* Tested CableTron firmware : 4.32 => Anton */
@@ -2904,6 +2998,8 @@ static int orinoco_init(struct net_device *dev)
else
printk("40-bit key\n");
}
+ if (priv->has_wpa)
+ printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name);

/* Now we have the firmware capabilities, allocate appropiate
* sized scan buffers */
@@ -3027,6 +3123,11 @@ static int orinoco_init(struct net_device *dev)
priv->promiscuous = 0;
priv->encode_alg = IW_ENCODE_ALG_NONE;
priv->tx_key = 0;
+ priv->wpa_enabled = 0;
+ priv->tkip_cm_active = 0;
+ priv->key_mgmt = 0;
+ priv->wpa_ie_len = 0;
+ priv->wpa_ie = NULL;

/* Make the hardware available, as long as it hasn't been
* removed elsewhere (e.g. by PCMCIA hot unplug) */
@@ -3102,6 +3203,8 @@ void free_orinocodev(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);

+ priv->wpa_ie_len = 0;
+ kfree(priv->wpa_ie);
orinoco_bss_data_free(priv);
free_netdev(dev);
}
@@ -3413,7 +3516,7 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev,
memset(range, 0, sizeof(struct iw_range));

range->we_version_compiled = WIRELESS_EXT;
- range->we_version_source = 14;
+ range->we_version_source = 22;

/* Set available channels/frequencies */
range->num_channels = NUM_CHANNELS;
@@ -3443,6 +3546,9 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev,
}
}

+ if (priv->has_wpa)
+ range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_CIPHER_TKIP;
+
if ((priv->iw_mode == IW_MODE_ADHOC) && (!SPY_NUMBER(priv))){
/* Quality stats meaningless in ad-hoc mode */
} else {
@@ -3535,6 +3641,10 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev,
if (orinoco_lock(priv, &flags) != 0)
return -EBUSY;

+ /* Clear any TKIP key we have */
+ if ((priv->has_wpa) && (priv->encode_alg == IW_ENCODE_ALG_TKIP))
+ (void) orinoco_clear_tkip_key(priv, setindex);
+
if (erq->length > 0) {
if ((index < 0) || (index >= ORINOCO_MAX_KEYS))
index = priv->tx_key;
@@ -4199,6 +4309,399 @@ static int orinoco_ioctl_getpower(struct net_device *dev,
return err;
}

+static int orinoco_ioctl_set_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int idx, alg = ext->alg, set_key = 1;
+ unsigned long flags;
+ int err = -EINVAL;
+ u16 key_len;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ /* Determine and validate the key index */
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if ((idx < 1) || (idx > WEP_KEYS))
+ goto out;
+ idx--;
+ } else
+ idx = priv->tx_key;
+
+ if (encoding->flags & IW_ENCODE_DISABLED)
+ alg = IW_ENCODE_ALG_NONE;
+
+ if (priv->has_wpa && (alg != IW_ENCODE_ALG_TKIP)) {
+ /* Clear any TKIP TX key we had */
+ (void) orinoco_clear_tkip_key(priv, priv->tx_key);
+ }
+
+ if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) {
+ priv->tx_key = idx;
+ set_key = ((alg == IW_ENCODE_ALG_TKIP) ||
+ (ext->key_len > 0)) ? 1 : 0;
+ }
+
+ if (set_key) {
+ /* Set the requested key first */
+ switch (alg) {
+ case IW_ENCODE_ALG_NONE:
+ priv->encode_alg = alg;
+ priv->keys[idx].len = 0;
+ break;
+
+ case IW_ENCODE_ALG_WEP:
+ if (ext->key_len > SMALL_KEY_SIZE)
+ key_len = LARGE_KEY_SIZE;
+ else if (ext->key_len > 0)
+ key_len = SMALL_KEY_SIZE;
+ else
+ goto out;
+
+ priv->encode_alg = alg;
+ priv->keys[idx].len = cpu_to_le16(key_len);
+
+ key_len = min(ext->key_len, key_len);
+
+ memset(priv->keys[idx].data, 0, ORINOCO_MAX_KEY_SIZE);
+ memcpy(priv->keys[idx].data, ext->key, key_len);
+ break;
+
+ case IW_ENCODE_ALG_TKIP:
+ {
+ hermes_t *hw = &priv->hw;
+ u8 *tkip_iv = NULL;
+
+ if (!priv->has_wpa ||
+ (ext->key_len > sizeof(priv->tkip_key[0])))
+ goto out;
+
+ priv->encode_alg = alg;
+ memset(&priv->tkip_key[idx], 0,
+ sizeof(priv->tkip_key[idx]));
+ memcpy(&priv->tkip_key[idx], ext->key, ext->key_len);
+
+ if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID)
+ tkip_iv = &ext->rx_seq[0];
+
+ err = __orinoco_hw_set_tkip_key(hw, idx,
+ ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY,
+ (u8 *) &priv->tkip_key[idx],
+ tkip_iv, NULL);
+ if (err)
+ printk(KERN_ERR "%s: Error %d setting TKIP key"
+ "\n", dev->name, err);
+
+ goto out;
+ }
+ default:
+ goto out;
+ }
+ }
+ err = -EINPROGRESS;
+ out:
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_get_encodeext(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu,
+ char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct iw_point *encoding = &wrqu->encoding;
+ struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+ int idx, max_key_len;
+ unsigned long flags;
+ int err;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ err = -EINVAL;
+ max_key_len = encoding->length - sizeof(*ext);
+ if (max_key_len < 0)
+ goto out;
+
+ idx = encoding->flags & IW_ENCODE_INDEX;
+ if (idx) {
+ if ((idx < 1) || (idx > WEP_KEYS))
+ goto out;
+ idx--;
+ } else
+ idx = priv->tx_key;
+
+ encoding->flags = idx + 1;
+ memset(ext, 0, sizeof(*ext));
+
+ ext->alg = priv->encode_alg;
+ switch (priv->encode_alg) {
+ case IW_ENCODE_ALG_NONE:
+ ext->key_len = 0;
+ encoding->flags |= IW_ENCODE_DISABLED;
+ break;
+ case IW_ENCODE_ALG_WEP:
+ ext->key_len = min(le16_to_cpu(priv->keys[idx].len),
+ (u16) max_key_len);
+ memcpy(ext->key, priv->keys[idx].data, ext->key_len);
+ encoding->flags |= IW_ENCODE_ENABLED;
+ break;
+ case IW_ENCODE_ALG_TKIP:
+ ext->key_len = min((u16) sizeof(struct orinoco_tkip_key),
+ (u16) max_key_len);
+ memcpy(ext->key, &priv->tkip_key[idx], ext->key_len);
+ encoding->flags |= IW_ENCODE_ENABLED;
+ break;
+ }
+
+ err = 0;
+ out:
+ orinoco_unlock(priv, &flags);
+
+ return err;
+}
+
+static int orinoco_ioctl_set_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ struct iw_param *param = &wrqu->param;
+ unsigned long flags;
+ int ret = -EINPROGRESS;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_WPA_VERSION:
+ case IW_AUTH_CIPHER_PAIRWISE:
+ case IW_AUTH_CIPHER_GROUP:
+ case IW_AUTH_RX_UNENCRYPTED_EAPOL:
+ case IW_AUTH_PRIVACY_INVOKED:
+ case IW_AUTH_DROP_UNENCRYPTED:
+ /*
+ * orinoco does not use these parameters
+ */
+ break;
+
+ case IW_AUTH_KEY_MGMT:
+ /* wl_lkm implies value 2 == PSK for Hermes I
+ * which ties in with WEXT
+ * no other hints tho :(
+ */
+ priv->key_mgmt = param->value;
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ /* When countermeasures are enabled, shut down the
+ * card; when disabled, re-enable the card. This must
+ * take effect immediately.
+ *
+ * TODO: Make sure that the EAPOL message is getting
+ * out before card disabled
+ */
+ if (param->value) {
+ priv->tkip_cm_active = 1;
+ ret = hermes_enable_port(hw, 0);
+ } else {
+ priv->tkip_cm_active = 0;
+ ret = hermes_disable_port(hw, 0);
+ }
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ if (param->value & IW_AUTH_ALG_SHARED_KEY)
+ priv->wep_restrict = 1;
+ else if (param->value & IW_AUTH_ALG_OPEN_SYSTEM)
+ priv->wep_restrict = 0;
+ else
+ ret = -EINVAL;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ if (priv->has_wpa) {
+ priv->wpa_enabled = param->value ? 1 : 0;
+ } else {
+ if (param->value)
+ ret = -EOPNOTSUPP;
+ /* else silently accept disable of WPA */
+ priv->wpa_enabled = 0;
+ }
+ break;
+
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ orinoco_unlock(priv, &flags);
+ return ret;
+}
+
+static int orinoco_ioctl_get_auth(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct iw_param *param = &wrqu->param;
+ unsigned long flags;
+ int ret = 0;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ switch (param->flags & IW_AUTH_INDEX) {
+ case IW_AUTH_KEY_MGMT:
+ param->value = priv->key_mgmt;
+ break;
+
+ case IW_AUTH_TKIP_COUNTERMEASURES:
+ param->value = priv->tkip_cm_active;
+ break;
+
+ case IW_AUTH_80211_AUTH_ALG:
+ if (priv->wep_restrict)
+ param->value = IW_AUTH_ALG_SHARED_KEY;
+ else
+ param->value = IW_AUTH_ALG_OPEN_SYSTEM;
+ break;
+
+ case IW_AUTH_WPA_ENABLED:
+ param->value = priv->wpa_enabled;
+ break;
+
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ orinoco_unlock(priv, &flags);
+ return ret;
+}
+
+static int orinoco_ioctl_set_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ u8 *buf;
+ unsigned long flags;
+ int err = 0;
+
+ if ((wrqu->data.length > MAX_WPA_IE_LEN) ||
+ (wrqu->data.length && (extra == NULL)))
+ return -EINVAL;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ if (wrqu->data.length) {
+ buf = kmalloc(wrqu->data.length, GFP_KERNEL);
+ if (buf == NULL) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ memcpy(buf, extra, wrqu->data.length);
+ kfree(priv->wpa_ie);
+ priv->wpa_ie = buf;
+ priv->wpa_ie_len = wrqu->data.length;
+ } else {
+ kfree(priv->wpa_ie);
+ priv->wpa_ie = NULL;
+ priv->wpa_ie_len = 0;
+ }
+
+ if (priv->wpa_ie) {
+ /* Looks like wl_lkm wants to check the auth alg, and
+ * somehow pass it to the firmware.
+ * Instead it just calls the key mgmt rid
+ * - we do this in set auth.
+ */
+ }
+
+out:
+ orinoco_unlock(priv, &flags);
+ return err;
+}
+
+static int orinoco_ioctl_get_genie(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ unsigned long flags;
+ int err = 0;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ if ((priv->wpa_ie_len == 0) || (priv->wpa_ie == NULL)) {
+ wrqu->data.length = 0;
+ goto out;
+ }
+
+ if (wrqu->data.length < priv->wpa_ie_len) {
+ err = -E2BIG;
+ goto out;
+ }
+
+ wrqu->data.length = priv->wpa_ie_len;
+ memcpy(extra, priv->wpa_ie, priv->wpa_ie_len);
+
+out:
+ orinoco_unlock(priv, &flags);
+ return err;
+}
+
+static int orinoco_ioctl_set_mlme(struct net_device *dev,
+ struct iw_request_info *info,
+ union iwreq_data *wrqu, char *extra)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ hermes_t *hw = &priv->hw;
+ struct iw_mlme *mlme = (struct iw_mlme *)extra;
+ unsigned long flags;
+ int ret = 0;
+
+ if (orinoco_lock(priv, &flags) != 0)
+ return -EBUSY;
+
+ switch (mlme->cmd) {
+ case IW_MLME_DEAUTH:
+ /* silently ignore */
+ break;
+
+ case IW_MLME_DISASSOC:
+ {
+ struct {
+ u8 addr[ETH_ALEN];
+ __le16 reason_code;
+ } __attribute__ ((packed)) buf;
+
+ memcpy(buf.addr, mlme->addr.sa_data, ETH_ALEN);
+ buf.reason_code = cpu_to_le16(mlme->reason_code);
+ ret = HERMES_WRITE_RECORD(hw, USER_BAP,
+ HERMES_RID_CNFDISASSOCIATE,
+ &buf);
+ break;
+ }
+ default:
+ ret = -EOPNOTSUPP;
+ }
+
+ orinoco_unlock(priv, &flags);
+ return ret;
+}
+
static int orinoco_ioctl_getretry(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rrq,
@@ -5085,6 +5588,13 @@ static const iw_handler orinoco_handler[] = {
STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode),
STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower),
STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower),
+ STD_IW_HANDLER(SIOCSIWGENIE, orinoco_ioctl_set_genie),
+ STD_IW_HANDLER(SIOCGIWGENIE, orinoco_ioctl_get_genie),
+ STD_IW_HANDLER(SIOCSIWMLME, orinoco_ioctl_set_mlme),
+ STD_IW_HANDLER(SIOCSIWAUTH, orinoco_ioctl_set_auth),
+ STD_IW_HANDLER(SIOCGIWAUTH, orinoco_ioctl_get_auth),
+ STD_IW_HANDLER(SIOCSIWENCODEEXT, orinoco_ioctl_set_encodeext),
+ STD_IW_HANDLER(SIOCGIWENCODEEXT, orinoco_ioctl_get_encodeext),
};


diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index 5605fd3..bfab88f 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -30,6 +30,15 @@ struct orinoco_key {
char data[ORINOCO_MAX_KEY_SIZE];
} __attribute__ ((packed));

+#define TKIP_KEYLEN 16
+#define MIC_KEYLEN 8
+
+struct orinoco_tkip_key {
+ u8 tkip[TKIP_KEYLEN];
+ u8 tx_mic[MIC_KEYLEN];
+ u8 rx_mic[MIC_KEYLEN];
+};
+
typedef enum {
FIRMWARE_TYPE_AGERE,
FIRMWARE_TYPE_INTERSIL,
@@ -93,6 +102,7 @@ struct orinoco_private {
unsigned int has_hostscan:1;
unsigned int has_alt_txcntl:1;
unsigned int has_ext_scan:1;
+ unsigned int has_wpa:1;
unsigned int do_fw_download:1;
unsigned int broken_disableport:1;
unsigned int broken_monitor:1;
@@ -128,6 +138,16 @@ struct orinoco_private {

int scan_inprogress; /* Scan pending... */
u32 scan_mode; /* Type of scan done */
+
+ /* WPA support */
+ u8 *wpa_ie;
+ int wpa_ie_len;
+
+ struct orinoco_tkip_key tkip_key[ORINOCO_MAX_KEYS];
+
+ unsigned int wpa_enabled:1;
+ unsigned int tkip_cm_active:1;
+ unsigned int key_mgmt:3;
};

#ifdef ORINOCO_DEBUG
--
1.5.4.5


2008-08-07 02:50:58

by Dan Williams

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

On Wed, 2008-08-06 at 22:08 +0100, Dave wrote:
> Dan Williams wrote:
> > On Wed, 2008-08-06 at 20:29 +0100, Dave wrote:
> >> Dan Williams wrote:
> >>> On Tue, 2008-08-05 at 18:48 -0400, Pavel Roskin wrote:
> >>>> The userspace is welcome to keep a pool of all APs found by any scans,
> >>>> but I don't think drivers should do it.
> >>> Drivers need to keep a reasonably complete list of APs internally for
> >>> association anyway. You don't want to have to do a full scan just to
> >>> associate if you have results from 10 seconds ago that are still valid.
>
> >> This is not an issue for orinoco cards, as the firmware selects
> >> the AP to use independently of any driver scanning. The requirement to
> >> use ap_scan=2 means that wpa_supplicant doesn't need to look at the scan
> >> results either (except maybe to verify the configuration matches).
>
> > Well, since the driver supports SSID scanning, we can use ap_scan=1
> > anyway. ap_scan=2 is actually pretty evil since it depends on WEXT
> > ordering and whatnot.
>
> ap_scan=1 requires the use of SIOCSIWWAP (to set desired BSSID), which
> the Agere firmware doesn't support. The firmware only supports setting a
> desired SSID (via SIOCSIWESSID), so Agere based orinoco is stuck with
> ap_scan=2 :(
>
> Symbol and Intersil don't have this problem.

In practice that shouldn't be a problem because drivers (at least
ipw2200 does this) usually roam BSSIDs anyway. Technically a bug I
think. But honestly, the driver needs to work with ap_scan=1 and I
don't see why it couldn't.

Dan


2008-08-06 21:28:13

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 12/19] orinoco: Use extended Agere scans available on 9.x series firmwares

This provides more information than the standard Agere scan, including
the WPA IE.

Signed-off-by: David Kilroy <[email protected]>
---
This patch is modified from the original to address the following points
raised by Pavel:

1. printf specified for sizeof should be %zd to avoid 64 bit warnings

2. Increase scan buffer size to accomodate more IE data. IE buffer
changed from 200 to 316 bytes, giving a structure size of 384 bytes.
This makes the pre-allocated scan structures for WPA enabled Agere
firmwares total 24kB

drivers/net/wireless/hermes.h | 34 +++
drivers/net/wireless/hermes_rid.h | 1 +
drivers/net/wireless/orinoco.c | 448 +++++++++++++++++++++++++++++++++----
drivers/net/wireless/orinoco.h | 9 +-
4 files changed, 448 insertions(+), 44 deletions(-)

diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h
index 1d0c584..113f3f6 100644
--- a/drivers/net/wireless/hermes.h
+++ b/drivers/net/wireless/hermes.h
@@ -303,6 +303,40 @@ union hermes_scan_info {
struct symbol_scan_apinfo s;
};

+/* Extended scan struct for HERMES_INQ_CHANNELINFO.
+ * wl_lkm calls this an ACS scan (Automatic Channel Select).
+ * Keep out of union hermes_scan_info because it is much bigger than
+ * the older scan structures. */
+struct agere_ext_scan_info {
+ __le16 reserved0;
+
+ u8 noise;
+ u8 level;
+ u8 rx_flow;
+ u8 rate;
+ __le16 reserved1[2];
+
+ __le16 frame_control;
+ __le16 dur_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ __le16 sequence;
+ u8 addr4[ETH_ALEN];
+
+ __le16 data_length;
+
+ /* Next 3 fields do not get filled in. */
+ u8 daddr[ETH_ALEN];
+ u8 saddr[ETH_ALEN];
+ __le16 len_type;
+
+ __le64 timestamp;
+ __le16 beacon_interval;
+ __le16 capabilities;
+ u8 data[316];
+} __attribute__ ((packed));
+
#define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000)
#define HERMES_LINKSTATUS_CONNECTED (0x0001)
#define HERMES_LINKSTATUS_DISCONNECTED (0x0002)
diff --git a/drivers/net/wireless/hermes_rid.h b/drivers/net/wireless/hermes_rid.h
index 4f46b48..bcd9c82 100644
--- a/drivers/net/wireless/hermes_rid.h
+++ b/drivers/net/wireless/hermes_rid.h
@@ -85,6 +85,7 @@
#define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2
#define HERMES_RID_CNFBASICRATES 0xFCB3
#define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4
+#define HERMES_RID_CNFSCANCHANNELS2GHZ 0xFCC2
#define HERMES_RID_CNFTICKTIME 0xFCE0
#define HERMES_RID_CNFSCANREQUEST 0xFCE1
#define HERMES_RID_CNFJOINREQUEST 0xFCE2
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 0b5b082..bca31f4 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -275,13 +275,19 @@ static inline void set_port_type(struct orinoco_private *priv)
#define ORINOCO_MAX_BSS_COUNT 64
static int orinoco_bss_data_allocate(struct orinoco_private *priv)
{
- if (priv->bss_data)
+ if (priv->bss_xbss_data)
return 0;

- priv->bss_data =
- kzalloc(ORINOCO_MAX_BSS_COUNT * sizeof(struct bss_element),
- GFP_KERNEL);
- if (!priv->bss_data) {
+ if (priv->has_ext_scan)
+ priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT *
+ sizeof(struct xbss_element),
+ GFP_KERNEL);
+ else
+ priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT *
+ sizeof(struct bss_element),
+ GFP_KERNEL);
+
+ if (!priv->bss_xbss_data) {
printk(KERN_WARNING "Out of memory allocating beacons");
return -ENOMEM;
}
@@ -290,18 +296,53 @@ static int orinoco_bss_data_allocate(struct orinoco_private *priv)

static void orinoco_bss_data_free(struct orinoco_private *priv)
{
- kfree(priv->bss_data);
- priv->bss_data = NULL;
+ kfree(priv->bss_xbss_data);
+ priv->bss_xbss_data = NULL;
}

+#define PRIV_BSS ((struct bss_element *)priv->bss_xbss_data)
+#define PRIV_XBSS ((struct xbss_element *)priv->bss_xbss_data)
static void orinoco_bss_data_init(struct orinoco_private *priv)
{
int i;

INIT_LIST_HEAD(&priv->bss_free_list);
INIT_LIST_HEAD(&priv->bss_list);
- for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
- list_add_tail(&priv->bss_data[i].list, &priv->bss_free_list);
+ if (priv->has_ext_scan)
+ for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
+ list_add_tail(&(PRIV_XBSS[i].list),
+ &priv->bss_free_list);
+ else
+ for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
+ list_add_tail(&(PRIV_BSS[i].list),
+ &priv->bss_free_list);
+
+}
+
+static inline u8 *orinoco_get_ie(u8 *data, size_t len,
+ enum ieee80211_mfie eid)
+{
+ u8 *p = data;
+ while ((p + 2) < (data + len)) {
+ if (p[0] == eid)
+ return p;
+ p += p[1] + 2;
+ }
+ return NULL;
+}
+
+#define WPA_OUI_TYPE "\x00\x50\xF2\x01"
+#define WPA_SELECTOR_LEN 4
+static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len)
+{
+ u8 *p = data;
+ while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) {
+ if ((p[0] == MFIE_TYPE_GENERIC) &&
+ (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0))
+ return p;
+ p += p[1] + 2;
+ }
+ return NULL;
}


@@ -1414,18 +1455,72 @@ static void orinoco_send_wevents(struct work_struct *work)
static inline void orinoco_clear_scan_results(struct orinoco_private *priv,
unsigned long scan_age)
{
- struct bss_element *bss;
- struct bss_element *tmp_bss;
-
- /* Blow away current list of scan results */
- list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
- if (!scan_age ||
- time_after(jiffies, bss->last_scanned + scan_age)) {
- list_move_tail(&bss->list, &priv->bss_free_list);
- /* Don't blow away ->list, just BSS data */
- memset(bss, 0, sizeof(bss->bss));
- bss->last_scanned = 0;
+ if (priv->has_ext_scan) {
+ struct xbss_element *bss;
+ struct xbss_element *tmp_bss;
+
+ /* Blow away current list of scan results */
+ list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
+ if (!scan_age ||
+ time_after(jiffies, bss->last_scanned + scan_age)) {
+ list_move_tail(&bss->list,
+ &priv->bss_free_list);
+ /* Don't blow away ->list, just BSS data */
+ memset(&bss->bss, 0, sizeof(bss->bss));
+ bss->last_scanned = 0;
+ }
}
+ } else {
+ struct bss_element *bss;
+ struct bss_element *tmp_bss;
+
+ /* Blow away current list of scan results */
+ list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
+ if (!scan_age ||
+ time_after(jiffies, bss->last_scanned + scan_age)) {
+ list_move_tail(&bss->list,
+ &priv->bss_free_list);
+ /* Don't blow away ->list, just BSS data */
+ memset(&bss->bss, 0, sizeof(bss->bss));
+ bss->last_scanned = 0;
+ }
+ }
+ }
+}
+
+static void orinoco_add_ext_scan_result(struct orinoco_private *priv,
+ struct agere_ext_scan_info *atom)
+{
+ struct xbss_element *bss = NULL;
+ int found = 0;
+
+ /* Try to update an existing bss first */
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ if (compare_ether_addr(bss->bss.bssid, atom->bssid))
+ continue;
+ /* ESSID lengths */
+ if (bss->bss.data[1] != atom->data[1])
+ continue;
+ if (memcmp(&bss->bss.data[2], &atom->data[2],
+ atom->data[1]))
+ continue;
+ found = 1;
+ break;
+ }
+
+ /* Grab a bss off the free list */
+ if (!found && !list_empty(&priv->bss_free_list)) {
+ bss = list_entry(priv->bss_free_list.next,
+ struct xbss_element, list);
+ list_del(priv->bss_free_list.next);
+
+ list_add_tail(&bss->list, &priv->bss_list);
+ }
+
+ if (bss) {
+ /* Always update the BSS to get latest beacon info */
+ memcpy(&bss->bss, atom, sizeof(bss->bss));
+ bss->last_scanned = jiffies;
}
}

@@ -1700,6 +1795,63 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
kfree(buf);
}
break;
+ case HERMES_INQ_CHANNELINFO:
+ {
+ struct agere_ext_scan_info *bss;
+
+ if (!priv->scan_inprogress) {
+ printk(KERN_DEBUG "%s: Got chaninfo without scan, "
+ "len=%d\n", dev->name, len);
+ break;
+ }
+
+ /* An empty result indicates that the scan is complete */
+ if (len == 0) {
+ union iwreq_data wrqu;
+
+ /* Scan is no longer in progress */
+ priv->scan_inprogress = 0;
+
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
+ break;
+ }
+
+ /* Sanity check */
+ else if (len > sizeof(*bss)) {
+ printk(KERN_WARNING
+ "%s: Ext scan results too large (%d bytes). "
+ "Truncating results to %zd bytes.\n",
+ dev->name, len, sizeof(*bss));
+ len = sizeof(*bss);
+ } else if (len < (offsetof(struct agere_ext_scan_info,
+ data) + 2)) {
+ /* Drop this result now so we don't have to
+ * keep checking later */
+ printk(KERN_WARNING
+ "%s: Ext scan results too short (%d bytes)\n",
+ dev->name, len);
+ break;
+ }
+
+ bss = kmalloc(sizeof(*bss), GFP_ATOMIC);
+ if (bss == NULL)
+ break;
+
+ /* Read scan data */
+ err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len,
+ infofid, sizeof(info));
+ if (err) {
+ kfree(bss);
+ break;
+ }
+
+ orinoco_add_ext_scan_result(priv, bss);
+
+ kfree(bss);
+ break;
+ }
case HERMES_INQ_SEC_STAT_AGERE:
/* Security status (Agere specific) */
/* Ignore this frame for now */
@@ -2564,6 +2716,7 @@ static int determine_firmware(struct net_device *dev)
priv->has_wep = 0;
priv->has_big_wep = 0;
priv->has_alt_txcntl = 0;
+ priv->has_ext_scan = 0;
priv->do_fw_download = 0;

/* Determine capabilities from the firmware version */
@@ -2587,7 +2740,7 @@ static int determine_firmware(struct net_device *dev)
priv->do_fw_download = 1;
priv->broken_monitor = (firmver >= 0x80000);
priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
-
+ priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
/* Tested with Agere firmware :
* 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
* Tested CableTron firmware : 4.32 => Anton */
@@ -2742,6 +2895,12 @@ static int orinoco_init(struct net_device *dev)
printk("40-bit key\n");
}

+ /* Now we have the firmware capabilities, allocate appropiate
+ * sized scan buffers */
+ if (orinoco_bss_data_allocate(priv))
+ goto out;
+ orinoco_bss_data_init(priv);
+
/* Get the MAC address */
err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
ETH_ALEN, NULL, dev->dev_addr);
@@ -2892,10 +3051,6 @@ struct net_device
priv->card = NULL;
priv->dev = device;

- if (orinoco_bss_data_allocate(priv))
- goto err_out_free;
- orinoco_bss_data_init(priv);
-
/* Setup / override net_device fields */
dev->init = orinoco_init;
dev->hard_start_xmit = orinoco_xmit;
@@ -2931,10 +3086,6 @@ struct net_device
priv->last_linkstatus = 0xffff;

return dev;
-
-err_out_free:
- free_netdev(dev);
- return NULL;
}

void free_orinocodev(struct net_device *dev)
@@ -4382,7 +4533,25 @@ static int orinoco_ioctl_setscan(struct net_device *dev,
if (err)
break;

- err = hermes_inquire(hw, HERMES_INQ_SCAN);
+ if (priv->has_ext_scan) {
+ /* Clear scan results at the start of
+ * an extended scan */
+ orinoco_clear_scan_results(priv,
+ msecs_to_jiffies(15000));
+
+ /* TODO: Is this available on older firmware?
+ * Can we use it to scan specific channels
+ * for IW_SCAN_THIS_FREQ? */
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFSCANCHANNELS2GHZ,
+ 0x7FFF);
+ if (err)
+ goto out;
+
+ err = hermes_inquire(hw,
+ HERMES_INQ_CHANNELINFO);
+ } else
+ err = hermes_inquire(hw, HERMES_INQ_SCAN);
break;
}
} else
@@ -4548,6 +4717,171 @@ static inline char *orinoco_translate_scan(struct net_device *dev,
return current_ev;
}

+static inline char *orinoco_translate_ext_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ char *current_ev,
+ char *end_buf,
+ struct agere_ext_scan_info *bss,
+ unsigned int last_scanned)
+{
+ u16 capabilities;
+ u16 channel;
+ struct iw_event iwe; /* Temporary buffer */
+ char custom[MAX_CUSTOM_LEN];
+ u8 *ie;
+
+ memset(&iwe, 0, sizeof(iwe));
+
+ /* First entry *MUST* be the AP MAC address */
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_ADDR_LEN);
+
+ /* Other entries will be displayed in the order we give them */
+
+ /* Add the ESSID */
+ ie = bss->data;
+ iwe.u.data.length = ie[1];
+ if (iwe.u.data.length) {
+ if (iwe.u.data.length > 32)
+ iwe.u.data.length = 32;
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, &ie[2]);
+ }
+
+ /* Add mode */
+ capabilities = le16_to_cpu(bss->capabilities);
+ if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
+ iwe.cmd = SIOCGIWMODE;
+ if (capabilities & WLAN_CAPABILITY_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_UINT_LEN);
+ }
+
+ ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_DS_SET);
+ channel = ie ? ie[2] : 0;
+ if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
+ /* 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. level and noise in dB. No link quality */
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
+ iwe.u.qual.level = bss->level - 0x95;
+ iwe.u.qual.noise = bss->noise - 0x95;
+ /* Wireless tools prior to 27.pre22 will show link quality
+ * anyway, so we provide a reasonable value. */
+ if (iwe.u.qual.level > iwe.u.qual.noise)
+ iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
+ else
+ iwe.u.qual.qual = 0;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_QUAL_LEN);
+
+ /* Add encryption capability */
+ iwe.cmd = SIOCGIWENCODE;
+ 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, NULL);
+
+ /* WPA IE */
+ ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data));
+ if (ie) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie[1] + 2;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, ie);
+ }
+
+ /* RSN IE */
+ ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_RSN);
+ if (ie) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie[1] + 2;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, ie);
+ }
+
+ ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_RATES);
+ if (ie) {
+ char *p = current_ev + iwe_stream_lcp_len(info);
+ int i;
+
+ iwe.cmd = SIOCGIWRATE;
+ /* Those two flags are ignored... */
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+
+ for (i = 2; i < (ie[1] + 2); i++) {
+ iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000);
+ p = iwe_stream_add_value(info, current_ev, p, end_buf,
+ &iwe, IW_EV_PARAM_LEN);
+ }
+ /* Check if we added any event */
+ if (p > (current_ev + iwe_stream_lcp_len(info)))
+ current_ev = p;
+ }
+
+ /* Timestamp */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ "tsf=%016llx",
+ le64_to_cpu(bss->timestamp));
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
+ /* Beacon interval */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ "bcn_int=%d",
+ le16_to_cpu(bss->beacon_interval));
+ 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;
+}
+
/* Return results of a scan */
static int orinoco_ioctl_getscan(struct net_device *dev,
struct iw_request_info *info,
@@ -4555,7 +4889,6 @@ static int orinoco_ioctl_getscan(struct net_device *dev,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
- struct bss_element *bss;
int err = 0;
unsigned long flags;
char *current_ev = extra;
@@ -4575,18 +4908,47 @@ static int orinoco_ioctl_getscan(struct net_device *dev,
goto out;
}

- list_for_each_entry(bss, &priv->bss_list, list) {
- /* Translate to WE format this entry */
- current_ev = orinoco_translate_scan(dev, info, current_ev,
- extra + srq->length,
- &bss->bss,
- bss->last_scanned);
-
- /* Check if there is space for one more entry */
- if ((extra + srq->length - current_ev) <= IW_EV_ADDR_LEN) {
- /* Ask user space to try again with a bigger buffer */
- err = -E2BIG;
- goto out;
+ if (priv->has_ext_scan) {
+ struct xbss_element *bss;
+
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ /* Translate this entry to WE format */
+ current_ev =
+ orinoco_translate_ext_scan(dev, info,
+ current_ev,
+ extra + srq->length,
+ &bss->bss,
+ bss->last_scanned);
+
+ /* Check if there is space for one more entry */
+ if ((extra + srq->length - current_ev)
+ <= IW_EV_ADDR_LEN) {
+ /* Ask user space to try again with a
+ * bigger buffer */
+ err = -E2BIG;
+ goto out;
+ }
+ }
+
+ } else {
+ struct bss_element *bss;
+
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ /* Translate this entry to WE format */
+ current_ev = orinoco_translate_scan(dev, info,
+ current_ev,
+ extra + srq->length,
+ &bss->bss,
+ bss->last_scanned);
+
+ /* Check if there is space for one more entry */
+ if ((extra + srq->length - current_ev)
+ <= IW_EV_ADDR_LEN) {
+ /* Ask user space to try again with a
+ * bigger buffer */
+ err = -E2BIG;
+ goto out;
+ }
}
}

diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index 584d8c9..f510994 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -42,6 +42,12 @@ struct bss_element {
struct list_head list;
};

+struct xbss_element {
+ struct agere_ext_scan_info bss;
+ unsigned long last_scanned;
+ struct list_head list;
+};
+
struct orinoco_private {
void *card; /* Pointer to card dependent structure */
struct device *dev;
@@ -86,6 +92,7 @@ struct orinoco_private {
unsigned int has_sensitivity:1;
unsigned int has_hostscan:1;
unsigned int has_alt_txcntl:1;
+ unsigned int has_ext_scan:1;
unsigned int do_fw_download:1;
unsigned int broken_disableport:1;
unsigned int broken_monitor:1;
@@ -117,7 +124,7 @@ struct orinoco_private {
/* Scanning support */
struct list_head bss_list;
struct list_head bss_free_list;
- struct bss_element *bss_data;
+ void *bss_xbss_data;

int scan_inprogress; /* Scan pending... */
u32 scan_mode; /* Type of scan done */
--
1.5.4.5


2008-08-06 19:29:27

by Dave Kilroy

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

Dan Williams wrote:
> On Tue, 2008-08-05 at 18:48 -0400, Pavel Roskin wrote:
>> The userspace is welcome to keep a pool of all APs found by any scans,
>> but I don't think drivers should do it.
>
> Drivers need to keep a reasonably complete list of APs internally for
> association anyway. You don't want to have to do a full scan just to
> associate if you have results from 10 seconds ago that are still valid.

This is not an issue for orinoco cards, as the firmware selects the AP to use independently of any driver scanning. The requirement to use ap_scan=2 means that wpa_supplicant doesn't need to look at the scan results either (except maybe to verify the configuration matches).

> Furthermore, drivers need to keep cached results so that two processes
> can request results of a scan after the scan is complete.

But this does apply to orinoco.


Regards,

Dave.

2008-08-02 10:16:10

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 19/19] orinoco: Add MIC on TX and check on RX

Use the MIC algorithm from the crypto subsystem.

Signed-off-by: David Kilroy <[email protected]>
---
drivers/net/wireless/Kconfig | 2 +
drivers/net/wireless/hermes.h | 7 ++
drivers/net/wireless/orinoco.c | 234 +++++++++++++++++++++++++++++++++++++---
drivers/net/wireless/orinoco.h | 2 +
4 files changed, 229 insertions(+), 16 deletions(-)

diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index cff47f7..5506a75 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -299,6 +299,8 @@ config HERMES
depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211
select WIRELESS_EXT
select FW_LOADER
+ select CRYPTO
+ select CRYPTO_MICHAEL_MIC
---help---
A driver for 802.11b wireless cards based on the "Hermes" or
Intersil HFA384x (Prism 2) MAC controller. This includes the vast
diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h
index f4c47da..35cb3ce 100644
--- a/drivers/net/wireless/hermes.h
+++ b/drivers/net/wireless/hermes.h
@@ -184,13 +184,18 @@
#define HERMES_RXSTAT_ERR (0x0003)
#define HERMES_RXSTAT_BADCRC (0x0001)
#define HERMES_RXSTAT_UNDECRYPTABLE (0x0002)
+#define HERMES_RXSTAT_MIC (0x0010) /* Frame contains MIC */
#define HERMES_RXSTAT_MACPORT (0x0700)
#define HERMES_RXSTAT_PCF (0x1000) /* Frame was received in CF period */
+#define HERMES_RXSTAT_MIC_KEY_ID (0x1800) /* MIC key used */
#define HERMES_RXSTAT_MSGTYPE (0xE000)
#define HERMES_RXSTAT_1042 (0x2000) /* RFC-1042 frame */
#define HERMES_RXSTAT_TUNNEL (0x4000) /* bridge-tunnel encoded frame */
#define HERMES_RXSTAT_WMP (0x6000) /* Wavelan-II Management Protocol frame */

+/* Shift amount for key ID in RXSTAT and TXCTRL */
+#define HERMES_MIC_KEY_ID_SHIFT 11
+
struct hermes_tx_descriptor {
__le16 status;
__le16 reserved1;
@@ -209,6 +214,8 @@ struct hermes_tx_descriptor {
#define HERMES_TXCTRL_TX_OK (0x0002) /* ?? interrupt on Tx complete */
#define HERMES_TXCTRL_TX_EX (0x0004) /* ?? interrupt on Tx exception */
#define HERMES_TXCTRL_802_11 (0x0008) /* We supply 802.11 header */
+#define HERMES_TXCTRL_MIC (0x0010) /* 802.3 + TKIP */
+#define HERMES_TXCTRL_MIC_KEY_ID (0x1800) /* MIC Key ID mask */
#define HERMES_TXCTRL_ALT_RTRY (0x0020)

/* Inquiry constants and data types */
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 5d1955f..dca725c 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -89,6 +89,9 @@
#include <net/iw_handler.h>
#include <net/ieee80211.h>

+#include <linux/scatterlist.h>
+#include <linux/crypto.h>
+
#include "hermes_rid.h"
#include "hermes_dld.h"
#include "orinoco.h"
@@ -244,6 +247,74 @@ static int __orinoco_program_rids(struct net_device *dev);
static void __orinoco_set_multicast_list(struct net_device *dev);

/********************************************************************/
+/* Michael MIC crypto setup */
+/********************************************************************/
+#define MICHAEL_MIC_LEN 8
+static int orinoco_mic_init(struct orinoco_private *priv)
+{
+ priv->tx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0);
+ if (IS_ERR(priv->tx_tfm_mic)) {
+ printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
+ "crypto API michael_mic\n");
+ priv->tx_tfm_mic = NULL;
+ return -ENOMEM;
+ }
+
+ priv->rx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0);
+ if (IS_ERR(priv->rx_tfm_mic)) {
+ printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
+ "crypto API michael_mic\n");
+ priv->rx_tfm_mic = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void orinoco_mic_free(struct orinoco_private *priv)
+{
+ if (priv->tx_tfm_mic)
+ crypto_free_hash(priv->tx_tfm_mic);
+ if (priv->rx_tfm_mic)
+ crypto_free_hash(priv->rx_tfm_mic);
+}
+
+static int michael_mic(struct crypto_hash *tfm_michael, u8 *key,
+ u8 *da, u8 *sa, u8 priority,
+ u8 *data, size_t data_len, u8 *mic)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[2];
+ u8 hdr[ETH_HLEN + 2]; /* size of header + padding */
+
+ if (tfm_michael == NULL) {
+ printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n");
+ return -1;
+ }
+
+ /* Copy header into buffer. We need the padding on the end zeroed */
+ memcpy(&hdr[0], da, ETH_ALEN);
+ memcpy(&hdr[ETH_ALEN], sa, ETH_ALEN);
+ hdr[ETH_ALEN*2] = priority;
+ hdr[ETH_ALEN*2+1] = 0;
+ hdr[ETH_ALEN*2+2] = 0;
+ hdr[ETH_ALEN*2+3] = 0;
+
+ /* Use scatter gather to MIC header and data in one go */
+ sg_init_table(sg, 2);
+ sg_set_buf(&sg[0], hdr, sizeof(hdr));
+ sg_set_buf(&sg[1], data, data_len);
+
+ if (crypto_hash_setkey(tfm_michael, key, MIC_KEYLEN))
+ return -1;
+
+ desc.tfm = tfm_michael;
+ desc.flags = 0;
+ return crypto_hash_digest(&desc, sg, data_len + sizeof(hdr),
+ mic);
+}
+
+/********************************************************************/
/* Internal helper functions */
/********************************************************************/

@@ -764,7 +835,6 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
int err = 0;
u16 txfid = priv->txfid;
struct ethhdr *eh;
- int data_off;
int tx_control;
unsigned long flags;

@@ -797,10 +867,12 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
if (skb->len < ETH_HLEN)
goto drop;

- eh = (struct ethhdr *)skb->data;
-
tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;

+ if (priv->encode_alg == IW_ENCODE_ALG_TKIP)
+ tx_control |= (priv->tx_key << HERMES_MIC_KEY_ID_SHIFT) |
+ HERMES_TXCTRL_MIC;
+
if (priv->has_alt_txcntl) {
/* WPA enabled firmwares have tx_cntl at the end of
* the 802.11 header. So write zeroed descriptor and
@@ -842,6 +914,8 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
}

+ eh = (struct ethhdr *)skb->data;
+
/* Encapsulate Ethernet-II frames */
if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
struct header_struct {
@@ -851,33 +925,65 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)

/* Strip destination and source from the data */
skb_pull(skb, 2 * ETH_ALEN);
- data_off = HERMES_802_2_OFFSET + sizeof(encaps_hdr);

/* And move them to a separate header */
memcpy(&hdr.eth, eh, 2 * ETH_ALEN);
hdr.eth.h_proto = htons(sizeof(encaps_hdr) + skb->len);
memcpy(hdr.encap, encaps_hdr, sizeof(encaps_hdr));

- err = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr),
- txfid, HERMES_802_3_OFFSET);
- if (err) {
- if (net_ratelimit())
- printk(KERN_ERR "%s: Error %d writing packet "
- "header to BAP\n", dev->name, err);
- goto busy;
+ /* Insert the SNAP header */
+ if (skb_headroom(skb) < sizeof(hdr)) {
+ printk(KERN_ERR
+ "%s: Not enough headroom for 802.2 headers %d\n",
+ dev->name, skb_headroom(skb));
+ goto drop;
}
- } else { /* IEEE 802.3 frame */
- data_off = HERMES_802_3_OFFSET;
+ eh = (struct ethhdr *) skb_push(skb, sizeof(hdr));
+ memcpy(eh, &hdr, sizeof(hdr));
}

err = hermes_bap_pwrite(hw, USER_BAP, skb->data, skb->len,
- txfid, data_off);
+ txfid, HERMES_802_3_OFFSET);
if (err) {
printk(KERN_ERR "%s: Error %d writing packet to BAP\n",
dev->name, err);
goto busy;
}

+ /* Calculate Michael MIC */
+ if (priv->encode_alg == IW_ENCODE_ALG_TKIP) {
+ u8 mic_buf[MICHAEL_MIC_LEN + 1];
+ u8 *mic;
+ size_t offset;
+ size_t len;
+
+ if (skb->len % 2) {
+ /* MIC start is on an odd boundary */
+ mic_buf[0] = skb->data[skb->len - 1];
+ mic = &mic_buf[1];
+ offset = skb->len - 1;
+ len = MICHAEL_MIC_LEN + 1;
+ } else {
+ mic = &mic_buf[0];
+ offset = skb->len;
+ len = MICHAEL_MIC_LEN;
+ }
+
+ michael_mic(priv->tx_tfm_mic,
+ priv->tkip_key[priv->tx_key].tx_mic,
+ eh->h_dest, eh->h_source, 0 /* priority */,
+ skb->data + ETH_HLEN, skb->len - ETH_HLEN, mic);
+
+ /* Write the MIC */
+ err = hermes_bap_pwrite(hw, USER_BAP, &mic_buf[0], len,
+ txfid, HERMES_802_3_OFFSET + offset);
+ if (err) {
+ printk(KERN_ERR "%s: Error %d writing MIC to BAP\n",
+ dev->name, err);
+ goto busy;
+ }
+ }
+
/* Finally, we actually initiate the send */
netif_stop_queue(dev);

@@ -892,7 +998,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
}

dev->trans_start = jiffies;
- stats->tx_bytes += data_off + skb->len;
+ stats->tx_bytes += HERMES_802_3_OFFSET + skb->len;
goto ok;

drop:
@@ -1172,6 +1278,25 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
stats->rx_dropped++;
}

+/* Get tsc from the firmware */
+static int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key,
+ u8 *tsc)
+{
+ hermes_t *hw = &priv->hw;
+ int err = 0;
+ u8 tsc_arr[4][IW_ENCODE_SEQ_MAX_SIZE];
+
+ if ((key < 0) || (key > 4))
+ return -EINVAL;
+
+ err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENT_TKIP_IV,
+ sizeof(tsc_arr), NULL, &tsc_arr);
+ if (!err)
+ memcpy(tsc, &tsc_arr[key][0], sizeof(tsc_arr[0]));
+
+ return err;
+}
+
static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
{
struct orinoco_private *priv = netdev_priv(dev);
@@ -1240,6 +1365,11 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
goto update_stats;
}

+ /* Payload size does not include Michael MIC. Increase payload
+ * size to read it together with the data. */
+ if (status & HERMES_RXSTAT_MIC)
+ length += MICHAEL_MIC_LEN;
+
/* We need space for the packet data itself, plus an ethernet
header, plus 2 bytes so we can align the IP header on a
32bit boundary, plus 1 byte so we can read in odd length
@@ -1303,6 +1433,63 @@ static void orinoco_rx(struct net_device *dev,
length = le16_to_cpu(desc->data_len);
fc = le16_to_cpu(desc->frame_ctl);

+ /* Calculate and check MIC */
+ if (status & HERMES_RXSTAT_MIC) {
+ int key_id = ((status & HERMES_RXSTAT_MIC_KEY_ID) >>
+ HERMES_MIC_KEY_ID_SHIFT);
+ u8 mic[MICHAEL_MIC_LEN];
+ u8 *rxmic;
+ u8 *src = (fc & IEEE80211_FCTL_FROMDS) ?
+ desc->addr3 : desc->addr2;
+
+ /* Extract Michael MIC from payload */
+ rxmic = skb->data + skb->len - MICHAEL_MIC_LEN;
+
+ skb_trim(skb, skb->len - MICHAEL_MIC_LEN);
+ length -= MICHAEL_MIC_LEN;
+
+ michael_mic(priv->rx_tfm_mic,
+ priv->tkip_key[key_id].rx_mic,
+ desc->addr1,
+ src,
+ 0, /* priority or QoS? */
+ skb->data,
+ skb->len,
+ &mic[0]);
+
+ if (memcmp(mic, rxmic,
+ MICHAEL_MIC_LEN)) {
+ union iwreq_data wrqu;
+ struct iw_michaelmicfailure wxmic;
+ DECLARE_MAC_BUF(mac);
+
+ printk(KERN_WARNING "%s: "
+ "Invalid Michael MIC in data frame from %s, "
+ "using key %i\n",
+ dev->name, print_mac(mac, src), key_id);
+
+ /* TODO: update stats */
+
+ /* Notify userspace */
+ memset(&wxmic, 0, sizeof(wxmic));
+ wxmic.flags = key_id & IW_MICFAILURE_KEY_ID;
+ wxmic.flags |= (desc->addr1[0] & 1) ?
+ IW_MICFAILURE_GROUP : IW_MICFAILURE_PAIRWISE;
+ wxmic.src_addr.sa_family = ARPHRD_ETHER;
+ memcpy(wxmic.src_addr.sa_data, src, ETH_ALEN);
+
+ (void) orinoco_hw_get_tkip_iv(priv, key_id,
+ &wxmic.tsc[0]);
+
+ memset(&wrqu, 0, sizeof(wrqu));
+ wrqu.data.length = sizeof(wxmic);
+ wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu,
+ (char *) &wxmic);
+
+ goto drop;
+ }
+ }
+
/* Handle decapsulation
* In most cases, the firmware tell us about SNAP frames.
* For some reason, the SNAP frames sent by LinkSys APs
@@ -1342,6 +1529,11 @@ static void orinoco_rx(struct net_device *dev,
stats->rx_bytes += length;

return;
+
+ drop:
+ dev_kfree_skb(skb);
+ stats->rx_errors++;
+ stats->rx_dropped++;
}

static void orinoco_rx_isr_tasklet(unsigned long data)
@@ -3119,8 +3311,14 @@ static int orinoco_init(struct net_device *dev)
else
printk("40-bit key\n");
}
- if (priv->has_wpa)
+ if (priv->has_wpa) {
printk(KERN_DEBUG "%s: WPA-PSK supported\n", dev->name);
+ if (orinoco_mic_init(priv)) {
+ printk(KERN_ERR "%s: Failed to setup MIC crypto "
+ "algorithm. Disabling WPA support\n", dev->name);
+ priv->has_wpa = 0;
+ }
+ }

/* Now we have the firmware capabilities, allocate appropiate
* sized scan buffers */
@@ -3299,6 +3497,9 @@ struct net_device
dev->set_multicast_list = orinoco_set_multicast_list;
/* we use the default eth_mac_addr for setting the MAC addr */

+ /* Reserve space in skb for the SNAP header */
+ dev->hard_header_len += ENCAPS_OVERHEAD;
+
/* Set up default callbacks */
dev->open = orinoco_open;
dev->stop = orinoco_stop;
@@ -3334,6 +3535,7 @@ void free_orinocodev(struct net_device *dev)
tasklet_kill(&priv->rx_tasklet);
priv->wpa_ie_len = 0;
kfree(priv->wpa_ie);
+ orinoco_mic_free(priv);
orinoco_bss_data_free(priv);
free_netdev(dev);
}
diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index e0c9be3..981570b 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -158,6 +158,8 @@ struct orinoco_private {
int wpa_ie_len;

struct orinoco_tkip_key tkip_key[ORINOCO_MAX_KEYS];
+ struct crypto_hash *rx_tfm_mic;
+ struct crypto_hash *tx_tfm_mic;

unsigned int wpa_enabled:1;
unsigned int tkip_cm_active:1;
--
1.5.4.5


2008-08-06 20:59:41

by Dan Williams

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

On Wed, 2008-08-06 at 19:33 +0100, Dave wrote:
> Pavel Roskin wrote:
> > On Mon, 2008-08-04 at 19:28 -0400, Dan Williams wrote:
> >>> I'm not familiar with the difference between WPA/WPA2. Is that expected to work?
> >>
> >> But you might be able to get away with WPA2/RSN + TKIP if the AP allows
> >> this configuration. In that configuration, the only difference between
> >> WPA and WPA2/RSN would be the information element IDs, really. But if
> >> the firmware itself doesn't say it supports WPA on whatever website it
> >> came from, then likely the card won't do WPA2/RSN either.
>
> The firmware supports WPA, but makes no mention of WPA2.

It might support WPA2+TKIP since there's not a lot of difference there,
but that would require more testing.

> > I tried association to hostapd with madwifi, and the only working
> > configuration is WPA1 only with TKIP. Even enabling WPA1 and WPA2 and
> > TKIP makes the connection fail. Forcing WPA1 and TKIP in
> > wpa_supplicant.conf doesn't help.
> >
> > I looked at the patches. They have references to TKIP, but not to CCMP.
> > Yet it would be nice if we could support WPA1+WPA2, as we cannot require
> > that access points stop supporting WPA2, which is the 802.11i standard.
> > It's possible that we have an issue outside the driver.
>
> I don't believe the firmware supports CCMP. It has support for CCX/CKIP (Cisco specific TKIP-alike), but I'm guess we don't care about that.
>
> I suggest that we leave figuring out how to associate with a WPA2+TKIP AP to another day.

That's probably OK. The driver doesn't say it supports WPA2+TKIP, so
you're in the clear here. Either the supplicant has to be fixed to
respect driver capabilities, or the user has to know. I vote for fixing
the supplicant.

Dan

> >>>> [185219.617236] eth1: Ext scan results too large (272 bytes).
> >> Truncating
> >>>> results to 270 bytes.
>
> > I tried increasing the "data" size from 200 to 300 in hermes.h, and the
> > message went away. I was able to associate to D-Link DIR-615 when it
> > was set to WPA1.
> >
> > I think it should be safe to increase the side of "data" and remove the
> > unused "flags" filed at the end. Let's make "data" 256 bytes to make it a nice
> > round number.
>
> Thanks for checking that. I'll set data to 316 bytes. This makes the agere_ext_scan_info 384 (256+128) bytes. That copes with the worst case you saw, and gives a nice alignment to the array of scan structures (total size now a round 24k).
>
> > I'm sorry, I'm going to be offline soon, and I really cannot do any more
> > tests.
>
> Thank you for the feedback and testing, not to mention past maintainership of the driver.
>
>
> Regards,
>
> Dave.
>


2008-08-02 10:15:51

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 15/19] orinoco: Use a macro to define wireless handlers

The macro identifiers for the various ioctls required for WPA support
are longer than those currently used by the driver. This makes it messy
to keep line length below 80 character.

By defining a macro to initialise the handler table, we recover the
common text.

Signed-off-by: David Kilroy <kilroyd@gmail>
---
drivers/net/wireless/orinoco.c | 66 ++++++++++++++++++++-------------------
1 files changed, 34 insertions(+), 32 deletions(-)

diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index b91b6cb..59720f9 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -5050,39 +5050,41 @@ static const struct iw_priv_args orinoco_privtab[] = {
* Structures to export the Wireless Handlers
*/

+#define STD_IW_HANDLER(id, func) \
+ [IW_IOCTL_IDX(id)] = (iw_handler) func
static const iw_handler orinoco_handler[] = {
- [SIOCSIWCOMMIT-SIOCIWFIRST] = (iw_handler) orinoco_ioctl_commit,
- [SIOCGIWNAME -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getname,
- [SIOCSIWFREQ -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setfreq,
- [SIOCGIWFREQ -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getfreq,
- [SIOCSIWMODE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setmode,
- [SIOCGIWMODE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getmode,
- [SIOCSIWSENS -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setsens,
- [SIOCGIWSENS -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getsens,
- [SIOCGIWRANGE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getiwrange,
- [SIOCSIWSPY -SIOCIWFIRST] = (iw_handler) iw_handler_set_spy,
- [SIOCGIWSPY -SIOCIWFIRST] = (iw_handler) iw_handler_get_spy,
- [SIOCSIWTHRSPY-SIOCIWFIRST] = (iw_handler) iw_handler_set_thrspy,
- [SIOCGIWTHRSPY-SIOCIWFIRST] = (iw_handler) iw_handler_get_thrspy,
- [SIOCSIWAP -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setwap,
- [SIOCGIWAP -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getwap,
- [SIOCSIWSCAN -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setscan,
- [SIOCGIWSCAN -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getscan,
- [SIOCSIWESSID -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setessid,
- [SIOCGIWESSID -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getessid,
- [SIOCSIWNICKN -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setnick,
- [SIOCGIWNICKN -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getnick,
- [SIOCSIWRATE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setrate,
- [SIOCGIWRATE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getrate,
- [SIOCSIWRTS -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setrts,
- [SIOCGIWRTS -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getrts,
- [SIOCSIWFRAG -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setfrag,
- [SIOCGIWFRAG -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getfrag,
- [SIOCGIWRETRY -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getretry,
- [SIOCSIWENCODE-SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setiwencode,
- [SIOCGIWENCODE-SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getiwencode,
- [SIOCSIWPOWER -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setpower,
- [SIOCGIWPOWER -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getpower,
+ STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit),
+ STD_IW_HANDLER(SIOCGIWNAME, orinoco_ioctl_getname),
+ STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq),
+ STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq),
+ STD_IW_HANDLER(SIOCSIWMODE, orinoco_ioctl_setmode),
+ STD_IW_HANDLER(SIOCGIWMODE, orinoco_ioctl_getmode),
+ STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens),
+ STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens),
+ STD_IW_HANDLER(SIOCGIWRANGE, orinoco_ioctl_getiwrange),
+ STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
+ STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
+ STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
+ STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
+ STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap),
+ STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap),
+ STD_IW_HANDLER(SIOCSIWSCAN, orinoco_ioctl_setscan),
+ STD_IW_HANDLER(SIOCGIWSCAN, orinoco_ioctl_getscan),
+ STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid),
+ STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid),
+ STD_IW_HANDLER(SIOCSIWNICKN, orinoco_ioctl_setnick),
+ STD_IW_HANDLER(SIOCGIWNICKN, orinoco_ioctl_getnick),
+ STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate),
+ STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate),
+ STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts),
+ STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts),
+ STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag),
+ STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag),
+ STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry),
+ STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode),
+ STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode),
+ STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower),
+ STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower),
};


--
1.5.4.5


2008-08-02 10:14:56

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 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 e9dca96..655d030 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -3955,7 +3955,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,
@@ -3995,7 +3995,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 */

@@ -4061,8 +4060,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,
@@ -4074,9 +4072,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;
@@ -4098,8 +4097,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;
@@ -4109,17 +4108,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
@@ -4133,25 +4137,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) {
@@ -4173,7 +4165,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,
@@ -4184,6 +4177,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.4.5


2008-08-20 21:23:03

by Johannes Berg

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

On Wed, 2008-08-20 at 23:07 +0200, Johannes Berg wrote:
> > At the moment this is only documented in email threads (on
> > orinoco-devel) - I lack access to any web/file hosting to put
> > something more permanent up. This obviously ought to be corrected.
>
> *points to wireless.kernel.org*
>
> feel free to create a driver page (though I'd prefer to not host
> firmware of unclear origin and would probably just delete such files if
> you upload them, talk to the firmware tree if you can redistribute it
> legally)

Oh and if you go create a new driver page, say
http://wireless.kernel.org/en/users/Drivers/orinoco, it gives you a
template in the left column that you can use (the DriverPageTemplate) to
get a page that looks similar to all the other ones.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2008-08-02 10:15:41

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 12/19] orinoco: Use extended Agere scans available on 9.x series firmwares

This provides more information than the standard Agere scan, including
the WPA IE.

Signed-off-by: David Kilroy <[email protected]>
---
drivers/net/wireless/hermes.h | 35 +++
drivers/net/wireless/hermes_rid.h | 1 +
drivers/net/wireless/orinoco.c | 448 +++++++++++++++++++++++++++++++++----
drivers/net/wireless/orinoco.h | 9 +-
4 files changed, 449 insertions(+), 44 deletions(-)

diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h
index 1d0c584..f4c47da 100644
--- a/drivers/net/wireless/hermes.h
+++ b/drivers/net/wireless/hermes.h
@@ -303,6 +303,41 @@ union hermes_scan_info {
struct symbol_scan_apinfo s;
};

+/* Extended scan struct for HERMES_INQ_CHANNELINFO.
+ * wl_lkm calls this an ACS scan (Automatic Channel Select).
+ * Keep out of union hermes_scan_info because it is much bigger than
+ * the older scan structures. */
+struct agere_ext_scan_info {
+ __le16 reserved0;
+
+ u8 noise;
+ u8 level;
+ u8 rx_flow;
+ u8 rate;
+ __le16 reserved1[2];
+
+ __le16 frame_control;
+ __le16 dur_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ __le16 sequence;
+ u8 addr4[ETH_ALEN];
+
+ __le16 data_length;
+
+ /* Next 3 fields do not get filled in. */
+ u8 daddr[ETH_ALEN];
+ u8 saddr[ETH_ALEN];
+ __le16 len_type;
+
+ __le64 timestamp;
+ __le16 beacon_interval;
+ __le16 capabilities;
+ u8 data[200];
+ __le16 flags;
+} __attribute__ ((packed));
+
#define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000)
#define HERMES_LINKSTATUS_CONNECTED (0x0001)
#define HERMES_LINKSTATUS_DISCONNECTED (0x0002)
diff --git a/drivers/net/wireless/hermes_rid.h b/drivers/net/wireless/hermes_rid.h
index 4f46b48..bcd9c82 100644
--- a/drivers/net/wireless/hermes_rid.h
+++ b/drivers/net/wireless/hermes_rid.h
@@ -85,6 +85,7 @@
#define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2
#define HERMES_RID_CNFBASICRATES 0xFCB3
#define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4
+#define HERMES_RID_CNFSCANCHANNELS2GHZ 0xFCC2
#define HERMES_RID_CNFTICKTIME 0xFCE0
#define HERMES_RID_CNFSCANREQUEST 0xFCE1
#define HERMES_RID_CNFJOINREQUEST 0xFCE2
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 07e118c..3b5c018 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -275,13 +275,19 @@ static inline void set_port_type(struct orinoco_private *priv)
#define ORINOCO_MAX_BSS_COUNT 64
static int orinoco_bss_data_allocate(struct orinoco_private *priv)
{
- if (priv->bss_data)
+ if (priv->bss_xbss_data)
return 0;

- priv->bss_data =
- kzalloc(ORINOCO_MAX_BSS_COUNT * sizeof(struct bss_element),
- GFP_KERNEL);
- if (!priv->bss_data) {
+ if (priv->has_ext_scan)
+ priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT *
+ sizeof(struct xbss_element),
+ GFP_KERNEL);
+ else
+ priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT *
+ sizeof(struct bss_element),
+ GFP_KERNEL);
+
+ if (!priv->bss_xbss_data) {
printk(KERN_WARNING "Out of memory allocating beacons");
return -ENOMEM;
}
@@ -290,18 +296,53 @@ static int orinoco_bss_data_allocate(struct orinoco_private *priv)

static void orinoco_bss_data_free(struct orinoco_private *priv)
{
- kfree(priv->bss_data);
- priv->bss_data = NULL;
+ kfree(priv->bss_xbss_data);
+ priv->bss_xbss_data = NULL;
}

+#define PRIV_BSS ((struct bss_element *)priv->bss_xbss_data)
+#define PRIV_XBSS ((struct xbss_element *)priv->bss_xbss_data)
static void orinoco_bss_data_init(struct orinoco_private *priv)
{
int i;

INIT_LIST_HEAD(&priv->bss_free_list);
INIT_LIST_HEAD(&priv->bss_list);
- for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
- list_add_tail(&priv->bss_data[i].list, &priv->bss_free_list);
+ if (priv->has_ext_scan)
+ for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
+ list_add_tail(&(PRIV_XBSS[i].list),
+ &priv->bss_free_list);
+ else
+ for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
+ list_add_tail(&(PRIV_BSS[i].list),
+ &priv->bss_free_list);
+
+}
+
+static inline u8 *orinoco_get_ie(u8 *data, size_t len,
+ enum ieee80211_mfie eid)
+{
+ u8 *p = data;
+ while ((p + 2) < (data + len)) {
+ if (p[0] == eid)
+ return p;
+ p += p[1] + 2;
+ }
+ return NULL;
+}
+
+#define WPA_OUI_TYPE "\x00\x50\xF2\x01"
+#define WPA_SELECTOR_LEN 4
+static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len)
+{
+ u8 *p = data;
+ while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) {
+ if ((p[0] == MFIE_TYPE_GENERIC) &&
+ (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0))
+ return p;
+ p += p[1] + 2;
+ }
+ return NULL;
}


@@ -1414,18 +1455,72 @@ static void orinoco_send_wevents(struct work_struct *work)
static inline void orinoco_clear_scan_results(struct orinoco_private *priv,
unsigned long scan_age)
{
- struct bss_element *bss;
- struct bss_element *tmp_bss;
-
- /* Blow away current list of scan results */
- list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
- if (!scan_age ||
- time_after(jiffies, bss->last_scanned + scan_age)) {
- list_move_tail(&bss->list, &priv->bss_free_list);
- /* Don't blow away ->list, just BSS data */
- memset(bss, 0, sizeof(bss->bss));
- bss->last_scanned = 0;
+ if (priv->has_ext_scan) {
+ struct xbss_element *bss;
+ struct xbss_element *tmp_bss;
+
+ /* Blow away current list of scan results */
+ list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
+ if (!scan_age ||
+ time_after(jiffies, bss->last_scanned + scan_age)) {
+ list_move_tail(&bss->list,
+ &priv->bss_free_list);
+ /* Don't blow away ->list, just BSS data */
+ memset(&bss->bss, 0, sizeof(bss->bss));
+ bss->last_scanned = 0;
+ }
}
+ } else {
+ struct bss_element *bss;
+ struct bss_element *tmp_bss;
+
+ /* Blow away current list of scan results */
+ list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
+ if (!scan_age ||
+ time_after(jiffies, bss->last_scanned + scan_age)) {
+ list_move_tail(&bss->list,
+ &priv->bss_free_list);
+ /* Don't blow away ->list, just BSS data */
+ memset(&bss->bss, 0, sizeof(bss->bss));
+ bss->last_scanned = 0;
+ }
+ }
+ }
+}
+
+static void orinoco_add_ext_scan_result(struct orinoco_private *priv,
+ struct agere_ext_scan_info *atom)
+{
+ struct xbss_element *bss = NULL;
+ int found = 0;
+
+ /* Try to update an existing bss first */
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ if (compare_ether_addr(bss->bss.bssid, atom->bssid))
+ continue;
+ /* ESSID lengths */
+ if (bss->bss.data[1] != atom->data[1])
+ continue;
+ if (memcmp(&bss->bss.data[2], &atom->data[2],
+ atom->data[1]))
+ continue;
+ found = 1;
+ break;
+ }
+
+ /* Grab a bss off the free list */
+ if (!found && !list_empty(&priv->bss_free_list)) {
+ bss = list_entry(priv->bss_free_list.next,
+ struct xbss_element, list);
+ list_del(priv->bss_free_list.next);
+
+ list_add_tail(&bss->list, &priv->bss_list);
+ }
+
+ if (bss) {
+ /* Always update the BSS to get latest beacon info */
+ memcpy(&bss->bss, atom, sizeof(bss->bss));
+ bss->last_scanned = jiffies;
}
}

@@ -1700,6 +1795,63 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
kfree(buf);
}
break;
+ case HERMES_INQ_CHANNELINFO:
+ {
+ struct agere_ext_scan_info *bss;
+
+ if (!priv->scan_inprogress) {
+ printk(KERN_DEBUG "%s: Got chaninfo without scan, "
+ "len=%d\n", dev->name, len);
+ break;
+ }
+
+ /* An empty result indicates that the scan is complete */
+ if (len == 0) {
+ union iwreq_data wrqu;
+
+ /* Scan is no longer in progress */
+ priv->scan_inprogress = 0;
+
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
+ break;
+ }
+
+ /* Sanity check */
+ else if (len > sizeof(*bss)) {
+ printk(KERN_WARNING
+ "%s: Ext scan results too large (%d bytes). "
+ "Truncating results to %d bytes.\n",
+ dev->name, len, sizeof(*bss));
+ len = sizeof(*bss);
+ } else if (len < (offsetof(struct agere_ext_scan_info,
+ data) + 2)) {
+ /* Drop this result now so we don't have to
+ * keep checking later */
+ printk(KERN_WARNING
+ "%s: Ext scan results too short (%d bytes)\n",
+ dev->name, len);
+ break;
+ }
+
+ bss = kmalloc(sizeof(*bss), GFP_ATOMIC);
+ if (bss == NULL)
+ break;
+
+ /* Read scan data */
+ err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len,
+ infofid, sizeof(info));
+ if (err) {
+ kfree(bss);
+ break;
+ }
+
+ orinoco_add_ext_scan_result(priv, bss);
+
+ kfree(bss);
+ break;
+ }
case HERMES_INQ_SEC_STAT_AGERE:
/* Security status (Agere specific) */
/* Ignore this frame for now */
@@ -2564,6 +2716,7 @@ static int determine_firmware(struct net_device *dev)
priv->has_wep = 0;
priv->has_big_wep = 0;
priv->has_alt_txcntl = 0;
+ priv->has_ext_scan = 0;
priv->do_fw_download = 0;

/* Determine capabilities from the firmware version */
@@ -2587,7 +2740,7 @@ static int determine_firmware(struct net_device *dev)
priv->do_fw_download = 1;
priv->broken_monitor = (firmver >= 0x80000);
priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
-
+ priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
/* Tested with Agere firmware :
* 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
* Tested CableTron firmware : 4.32 => Anton */
@@ -2742,6 +2895,12 @@ static int orinoco_init(struct net_device *dev)
printk("40-bit key\n");
}

+ /* Now we have the firmware capabilities, allocate appropiate
+ * sized scan buffers */
+ if (orinoco_bss_data_allocate(priv))
+ goto out;
+ orinoco_bss_data_init(priv);
+
/* Get the MAC address */
err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
ETH_ALEN, NULL, dev->dev_addr);
@@ -2892,10 +3051,6 @@ struct net_device
priv->card = NULL;
priv->dev = device;

- if (orinoco_bss_data_allocate(priv))
- goto err_out_free;
- orinoco_bss_data_init(priv);
-
/* Setup / override net_device fields */
dev->init = orinoco_init;
dev->hard_start_xmit = orinoco_xmit;
@@ -2931,10 +3086,6 @@ struct net_device
priv->last_linkstatus = 0xffff;

return dev;
-
-err_out_free:
- free_netdev(dev);
- return NULL;
}

void free_orinocodev(struct net_device *dev)
@@ -4382,7 +4533,25 @@ static int orinoco_ioctl_setscan(struct net_device *dev,
if (err)
break;

- err = hermes_inquire(hw, HERMES_INQ_SCAN);
+ if (priv->has_ext_scan) {
+ /* Clear scan results at the start of
+ * an extended scan */
+ orinoco_clear_scan_results(priv,
+ msecs_to_jiffies(15000));
+
+ /* TODO: Is this available on older firmware?
+ * Can we use it to scan specific channels
+ * for IW_SCAN_THIS_FREQ? */
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFSCANCHANNELS2GHZ,
+ 0x7FFF);
+ if (err)
+ goto out;
+
+ err = hermes_inquire(hw,
+ HERMES_INQ_CHANNELINFO);
+ } else
+ err = hermes_inquire(hw, HERMES_INQ_SCAN);
break;
}
} else
@@ -4548,6 +4717,171 @@ static inline char *orinoco_translate_scan(struct net_device *dev,
return current_ev;
}

+static inline char *orinoco_translate_ext_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ char *current_ev,
+ char *end_buf,
+ struct agere_ext_scan_info *bss,
+ unsigned int last_scanned)
+{
+ u16 capabilities;
+ u16 channel;
+ struct iw_event iwe; /* Temporary buffer */
+ char custom[MAX_CUSTOM_LEN];
+ u8 *ie;
+
+ memset(&iwe, 0, sizeof(iwe));
+
+ /* First entry *MUST* be the AP MAC address */
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_ADDR_LEN);
+
+ /* Other entries will be displayed in the order we give them */
+
+ /* Add the ESSID */
+ ie = bss->data;
+ iwe.u.data.length = ie[1];
+ if (iwe.u.data.length) {
+ if (iwe.u.data.length > 32)
+ iwe.u.data.length = 32;
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, &ie[2]);
+ }
+
+ /* Add mode */
+ capabilities = le16_to_cpu(bss->capabilities);
+ if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
+ iwe.cmd = SIOCGIWMODE;
+ if (capabilities & WLAN_CAPABILITY_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_UINT_LEN);
+ }
+
+ ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_DS_SET);
+ channel = ie ? ie[2] : 0;
+ if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
+ /* 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. level and noise in dB. No link quality */
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
+ iwe.u.qual.level = bss->level - 0x95;
+ iwe.u.qual.noise = bss->noise - 0x95;
+ /* Wireless tools prior to 27.pre22 will show link quality
+ * anyway, so we provide a reasonable value. */
+ if (iwe.u.qual.level > iwe.u.qual.noise)
+ iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
+ else
+ iwe.u.qual.qual = 0;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_QUAL_LEN);
+
+ /* Add encryption capability */
+ iwe.cmd = SIOCGIWENCODE;
+ 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, NULL);
+
+ /* WPA IE */
+ ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data));
+ if (ie) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie[1] + 2;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, ie);
+ }
+
+ /* RSN IE */
+ ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_RSN);
+ if (ie) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie[1] + 2;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, ie);
+ }
+
+ ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_RATES);
+ if (ie) {
+ char *p = current_ev + iwe_stream_lcp_len(info);
+ int i;
+
+ iwe.cmd = SIOCGIWRATE;
+ /* Those two flags are ignored... */
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+
+ for (i = 2; i < (ie[1] + 2); i++) {
+ iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000);
+ p = iwe_stream_add_value(info, current_ev, p, end_buf,
+ &iwe, IW_EV_PARAM_LEN);
+ }
+ /* Check if we added any event */
+ if (p > (current_ev + iwe_stream_lcp_len(info)))
+ current_ev = p;
+ }
+
+ /* Timestamp */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ "tsf=%016llx",
+ le64_to_cpu(bss->timestamp));
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
+ /* Beacon interval */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ "bcn_int=%d",
+ le16_to_cpu(bss->beacon_interval));
+ 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;
+}
+
/* Return results of a scan */
static int orinoco_ioctl_getscan(struct net_device *dev,
struct iw_request_info *info,
@@ -4555,7 +4889,6 @@ static int orinoco_ioctl_getscan(struct net_device *dev,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
- struct bss_element *bss;
int err = 0;
unsigned long flags;
char *current_ev = extra;
@@ -4575,18 +4908,47 @@ static int orinoco_ioctl_getscan(struct net_device *dev,
goto out;
}

- list_for_each_entry(bss, &priv->bss_list, list) {
- /* Translate to WE format this entry */
- current_ev = orinoco_translate_scan(dev, info, current_ev,
- extra + srq->length,
- &bss->bss,
- bss->last_scanned);
-
- /* Check if there is space for one more entry */
- if ((extra + srq->length - current_ev) <= IW_EV_ADDR_LEN) {
- /* Ask user space to try again with a bigger buffer */
- err = -E2BIG;
- goto out;
+ if (priv->has_ext_scan) {
+ struct xbss_element *bss;
+
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ /* Translate this entry to WE format */
+ current_ev =
+ orinoco_translate_ext_scan(dev, info,
+ current_ev,
+ extra + srq->length,
+ &bss->bss,
+ bss->last_scanned);
+
+ /* Check if there is space for one more entry */
+ if ((extra + srq->length - current_ev)
+ <= IW_EV_ADDR_LEN) {
+ /* Ask user space to try again with a
+ * bigger buffer */
+ err = -E2BIG;
+ goto out;
+ }
+ }
+
+ } else {
+ struct bss_element *bss;
+
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ /* Translate this entry to WE format */
+ current_ev = orinoco_translate_scan(dev, info,
+ current_ev,
+ extra + srq->length,
+ &bss->bss,
+ bss->last_scanned);
+
+ /* Check if there is space for one more entry */
+ if ((extra + srq->length - current_ev)
+ <= IW_EV_ADDR_LEN) {
+ /* Ask user space to try again with a
+ * bigger buffer */
+ err = -E2BIG;
+ goto out;
+ }
}
}

diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index 584d8c9..f510994 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -42,6 +42,12 @@ struct bss_element {
struct list_head list;
};

+struct xbss_element {
+ struct agere_ext_scan_info bss;
+ unsigned long last_scanned;
+ struct list_head list;
+};
+
struct orinoco_private {
void *card; /* Pointer to card dependent structure */
struct device *dev;
@@ -86,6 +92,7 @@ struct orinoco_private {
unsigned int has_sensitivity:1;
unsigned int has_hostscan:1;
unsigned int has_alt_txcntl:1;
+ unsigned int has_ext_scan:1;
unsigned int do_fw_download:1;
unsigned int broken_disableport:1;
unsigned int broken_monitor:1;
@@ -117,7 +124,7 @@ struct orinoco_private {
/* Scanning support */
struct list_head bss_list;
struct list_head bss_free_list;
- struct bss_element *bss_data;
+ void *bss_xbss_data;

int scan_inprogress; /* Scan pending... */
u32 scan_mode; /* Type of scan done */
--
1.5.4.5


2008-08-02 10:14:52

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 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 b047306..e9dca96 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -2989,6 +2989,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) */
@@ -3958,6 +3963,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;

@@ -4019,7 +4025,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.4.5


2008-08-05 22:22:55

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 12/19] orinoco: Use extended Agere scans available on 9.x series firmwares

This provides more information than the standard Agere scan, including
the WPA IE.

Signed-off-by: David Kilroy <[email protected]>
---
This patch is modified to declare the sizeof print specifier as %zd.

Note that I updated sparse to git HEAD, but it still isn't complaining
to me - I assume I actually have to invoke it in a 64 bit environment to
check whether the issues are indeed addressed.

drivers/net/wireless/hermes.h | 35 +++
drivers/net/wireless/hermes_rid.h | 1 +
drivers/net/wireless/orinoco.c | 448 +++++++++++++++++++++++++++++++++----
drivers/net/wireless/orinoco.h | 9 +-
4 files changed, 449 insertions(+), 44 deletions(-)

diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h
index 1d0c584..f4c47da 100644
--- a/drivers/net/wireless/hermes.h
+++ b/drivers/net/wireless/hermes.h
@@ -303,6 +303,41 @@ union hermes_scan_info {
struct symbol_scan_apinfo s;
};

+/* Extended scan struct for HERMES_INQ_CHANNELINFO.
+ * wl_lkm calls this an ACS scan (Automatic Channel Select).
+ * Keep out of union hermes_scan_info because it is much bigger than
+ * the older scan structures. */
+struct agere_ext_scan_info {
+ __le16 reserved0;
+
+ u8 noise;
+ u8 level;
+ u8 rx_flow;
+ u8 rate;
+ __le16 reserved1[2];
+
+ __le16 frame_control;
+ __le16 dur_id;
+ u8 addr1[ETH_ALEN];
+ u8 addr2[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ __le16 sequence;
+ u8 addr4[ETH_ALEN];
+
+ __le16 data_length;
+
+ /* Next 3 fields do not get filled in. */
+ u8 daddr[ETH_ALEN];
+ u8 saddr[ETH_ALEN];
+ __le16 len_type;
+
+ __le64 timestamp;
+ __le16 beacon_interval;
+ __le16 capabilities;
+ u8 data[200];
+ __le16 flags;
+} __attribute__ ((packed));
+
#define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000)
#define HERMES_LINKSTATUS_CONNECTED (0x0001)
#define HERMES_LINKSTATUS_DISCONNECTED (0x0002)
diff --git a/drivers/net/wireless/hermes_rid.h b/drivers/net/wireless/hermes_rid.h
index 4f46b48..bcd9c82 100644
--- a/drivers/net/wireless/hermes_rid.h
+++ b/drivers/net/wireless/hermes_rid.h
@@ -85,6 +85,7 @@
#define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2
#define HERMES_RID_CNFBASICRATES 0xFCB3
#define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4
+#define HERMES_RID_CNFSCANCHANNELS2GHZ 0xFCC2
#define HERMES_RID_CNFTICKTIME 0xFCE0
#define HERMES_RID_CNFSCANREQUEST 0xFCE1
#define HERMES_RID_CNFJOINREQUEST 0xFCE2
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 0b5b082..bca31f4 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -275,13 +275,19 @@ static inline void set_port_type(struct orinoco_private *priv)
#define ORINOCO_MAX_BSS_COUNT 64
static int orinoco_bss_data_allocate(struct orinoco_private *priv)
{
- if (priv->bss_data)
+ if (priv->bss_xbss_data)
return 0;

- priv->bss_data =
- kzalloc(ORINOCO_MAX_BSS_COUNT * sizeof(struct bss_element),
- GFP_KERNEL);
- if (!priv->bss_data) {
+ if (priv->has_ext_scan)
+ priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT *
+ sizeof(struct xbss_element),
+ GFP_KERNEL);
+ else
+ priv->bss_xbss_data = kzalloc(ORINOCO_MAX_BSS_COUNT *
+ sizeof(struct bss_element),
+ GFP_KERNEL);
+
+ if (!priv->bss_xbss_data) {
printk(KERN_WARNING "Out of memory allocating beacons");
return -ENOMEM;
}
@@ -290,18 +296,53 @@ static int orinoco_bss_data_allocate(struct orinoco_private *priv)

static void orinoco_bss_data_free(struct orinoco_private *priv)
{
- kfree(priv->bss_data);
- priv->bss_data = NULL;
+ kfree(priv->bss_xbss_data);
+ priv->bss_xbss_data = NULL;
}

+#define PRIV_BSS ((struct bss_element *)priv->bss_xbss_data)
+#define PRIV_XBSS ((struct xbss_element *)priv->bss_xbss_data)
static void orinoco_bss_data_init(struct orinoco_private *priv)
{
int i;

INIT_LIST_HEAD(&priv->bss_free_list);
INIT_LIST_HEAD(&priv->bss_list);
- for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
- list_add_tail(&priv->bss_data[i].list, &priv->bss_free_list);
+ if (priv->has_ext_scan)
+ for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
+ list_add_tail(&(PRIV_XBSS[i].list),
+ &priv->bss_free_list);
+ else
+ for (i = 0; i < ORINOCO_MAX_BSS_COUNT; i++)
+ list_add_tail(&(PRIV_BSS[i].list),
+ &priv->bss_free_list);
+
+}
+
+static inline u8 *orinoco_get_ie(u8 *data, size_t len,
+ enum ieee80211_mfie eid)
+{
+ u8 *p = data;
+ while ((p + 2) < (data + len)) {
+ if (p[0] == eid)
+ return p;
+ p += p[1] + 2;
+ }
+ return NULL;
+}
+
+#define WPA_OUI_TYPE "\x00\x50\xF2\x01"
+#define WPA_SELECTOR_LEN 4
+static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len)
+{
+ u8 *p = data;
+ while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) {
+ if ((p[0] == MFIE_TYPE_GENERIC) &&
+ (memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0))
+ return p;
+ p += p[1] + 2;
+ }
+ return NULL;
}


@@ -1414,18 +1455,72 @@ static void orinoco_send_wevents(struct work_struct *work)
static inline void orinoco_clear_scan_results(struct orinoco_private *priv,
unsigned long scan_age)
{
- struct bss_element *bss;
- struct bss_element *tmp_bss;
-
- /* Blow away current list of scan results */
- list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
- if (!scan_age ||
- time_after(jiffies, bss->last_scanned + scan_age)) {
- list_move_tail(&bss->list, &priv->bss_free_list);
- /* Don't blow away ->list, just BSS data */
- memset(bss, 0, sizeof(bss->bss));
- bss->last_scanned = 0;
+ if (priv->has_ext_scan) {
+ struct xbss_element *bss;
+ struct xbss_element *tmp_bss;
+
+ /* Blow away current list of scan results */
+ list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
+ if (!scan_age ||
+ time_after(jiffies, bss->last_scanned + scan_age)) {
+ list_move_tail(&bss->list,
+ &priv->bss_free_list);
+ /* Don't blow away ->list, just BSS data */
+ memset(&bss->bss, 0, sizeof(bss->bss));
+ bss->last_scanned = 0;
+ }
}
+ } else {
+ struct bss_element *bss;
+ struct bss_element *tmp_bss;
+
+ /* Blow away current list of scan results */
+ list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
+ if (!scan_age ||
+ time_after(jiffies, bss->last_scanned + scan_age)) {
+ list_move_tail(&bss->list,
+ &priv->bss_free_list);
+ /* Don't blow away ->list, just BSS data */
+ memset(&bss->bss, 0, sizeof(bss->bss));
+ bss->last_scanned = 0;
+ }
+ }
+ }
+}
+
+static void orinoco_add_ext_scan_result(struct orinoco_private *priv,
+ struct agere_ext_scan_info *atom)
+{
+ struct xbss_element *bss = NULL;
+ int found = 0;
+
+ /* Try to update an existing bss first */
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ if (compare_ether_addr(bss->bss.bssid, atom->bssid))
+ continue;
+ /* ESSID lengths */
+ if (bss->bss.data[1] != atom->data[1])
+ continue;
+ if (memcmp(&bss->bss.data[2], &atom->data[2],
+ atom->data[1]))
+ continue;
+ found = 1;
+ break;
+ }
+
+ /* Grab a bss off the free list */
+ if (!found && !list_empty(&priv->bss_free_list)) {
+ bss = list_entry(priv->bss_free_list.next,
+ struct xbss_element, list);
+ list_del(priv->bss_free_list.next);
+
+ list_add_tail(&bss->list, &priv->bss_list);
+ }
+
+ if (bss) {
+ /* Always update the BSS to get latest beacon info */
+ memcpy(&bss->bss, atom, sizeof(bss->bss));
+ bss->last_scanned = jiffies;
}
}

@@ -1700,6 +1795,63 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
kfree(buf);
}
break;
+ case HERMES_INQ_CHANNELINFO:
+ {
+ struct agere_ext_scan_info *bss;
+
+ if (!priv->scan_inprogress) {
+ printk(KERN_DEBUG "%s: Got chaninfo without scan, "
+ "len=%d\n", dev->name, len);
+ break;
+ }
+
+ /* An empty result indicates that the scan is complete */
+ if (len == 0) {
+ union iwreq_data wrqu;
+
+ /* Scan is no longer in progress */
+ priv->scan_inprogress = 0;
+
+ wrqu.data.length = 0;
+ wrqu.data.flags = 0;
+ wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
+ break;
+ }
+
+ /* Sanity check */
+ else if (len > sizeof(*bss)) {
+ printk(KERN_WARNING
+ "%s: Ext scan results too large (%d bytes). "
+ "Truncating results to %zd bytes.\n",
+ dev->name, len, sizeof(*bss));
+ len = sizeof(*bss);
+ } else if (len < (offsetof(struct agere_ext_scan_info,
+ data) + 2)) {
+ /* Drop this result now so we don't have to
+ * keep checking later */
+ printk(KERN_WARNING
+ "%s: Ext scan results too short (%d bytes)\n",
+ dev->name, len);
+ break;
+ }
+
+ bss = kmalloc(sizeof(*bss), GFP_ATOMIC);
+ if (bss == NULL)
+ break;
+
+ /* Read scan data */
+ err = hermes_bap_pread(hw, IRQ_BAP, (void *) bss, len,
+ infofid, sizeof(info));
+ if (err) {
+ kfree(bss);
+ break;
+ }
+
+ orinoco_add_ext_scan_result(priv, bss);
+
+ kfree(bss);
+ break;
+ }
case HERMES_INQ_SEC_STAT_AGERE:
/* Security status (Agere specific) */
/* Ignore this frame for now */
@@ -2564,6 +2716,7 @@ static int determine_firmware(struct net_device *dev)
priv->has_wep = 0;
priv->has_big_wep = 0;
priv->has_alt_txcntl = 0;
+ priv->has_ext_scan = 0;
priv->do_fw_download = 0;

/* Determine capabilities from the firmware version */
@@ -2587,7 +2740,7 @@ static int determine_firmware(struct net_device *dev)
priv->do_fw_download = 1;
priv->broken_monitor = (firmver >= 0x80000);
priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */
-
+ priv->has_ext_scan = (firmver >= 0x90000); /* All 9.x ? */
/* Tested with Agere firmware :
* 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
* Tested CableTron firmware : 4.32 => Anton */
@@ -2742,6 +2895,12 @@ static int orinoco_init(struct net_device *dev)
printk("40-bit key\n");
}

+ /* Now we have the firmware capabilities, allocate appropiate
+ * sized scan buffers */
+ if (orinoco_bss_data_allocate(priv))
+ goto out;
+ orinoco_bss_data_init(priv);
+
/* Get the MAC address */
err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR,
ETH_ALEN, NULL, dev->dev_addr);
@@ -2892,10 +3051,6 @@ struct net_device
priv->card = NULL;
priv->dev = device;

- if (orinoco_bss_data_allocate(priv))
- goto err_out_free;
- orinoco_bss_data_init(priv);
-
/* Setup / override net_device fields */
dev->init = orinoco_init;
dev->hard_start_xmit = orinoco_xmit;
@@ -2931,10 +3086,6 @@ struct net_device
priv->last_linkstatus = 0xffff;

return dev;
-
-err_out_free:
- free_netdev(dev);
- return NULL;
}

void free_orinocodev(struct net_device *dev)
@@ -4382,7 +4533,25 @@ static int orinoco_ioctl_setscan(struct net_device *dev,
if (err)
break;

- err = hermes_inquire(hw, HERMES_INQ_SCAN);
+ if (priv->has_ext_scan) {
+ /* Clear scan results at the start of
+ * an extended scan */
+ orinoco_clear_scan_results(priv,
+ msecs_to_jiffies(15000));
+
+ /* TODO: Is this available on older firmware?
+ * Can we use it to scan specific channels
+ * for IW_SCAN_THIS_FREQ? */
+ err = hermes_write_wordrec(hw, USER_BAP,
+ HERMES_RID_CNFSCANCHANNELS2GHZ,
+ 0x7FFF);
+ if (err)
+ goto out;
+
+ err = hermes_inquire(hw,
+ HERMES_INQ_CHANNELINFO);
+ } else
+ err = hermes_inquire(hw, HERMES_INQ_SCAN);
break;
}
} else
@@ -4548,6 +4717,171 @@ static inline char *orinoco_translate_scan(struct net_device *dev,
return current_ev;
}

+static inline char *orinoco_translate_ext_scan(struct net_device *dev,
+ struct iw_request_info *info,
+ char *current_ev,
+ char *end_buf,
+ struct agere_ext_scan_info *bss,
+ unsigned int last_scanned)
+{
+ u16 capabilities;
+ u16 channel;
+ struct iw_event iwe; /* Temporary buffer */
+ char custom[MAX_CUSTOM_LEN];
+ u8 *ie;
+
+ memset(&iwe, 0, sizeof(iwe));
+
+ /* First entry *MUST* be the AP MAC address */
+ iwe.cmd = SIOCGIWAP;
+ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+ memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_ADDR_LEN);
+
+ /* Other entries will be displayed in the order we give them */
+
+ /* Add the ESSID */
+ ie = bss->data;
+ iwe.u.data.length = ie[1];
+ if (iwe.u.data.length) {
+ if (iwe.u.data.length > 32)
+ iwe.u.data.length = 32;
+ iwe.cmd = SIOCGIWESSID;
+ iwe.u.data.flags = 1;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, &ie[2]);
+ }
+
+ /* Add mode */
+ capabilities = le16_to_cpu(bss->capabilities);
+ if (capabilities & (WLAN_CAPABILITY_ESS | WLAN_CAPABILITY_IBSS)) {
+ iwe.cmd = SIOCGIWMODE;
+ if (capabilities & WLAN_CAPABILITY_ESS)
+ iwe.u.mode = IW_MODE_MASTER;
+ else
+ iwe.u.mode = IW_MODE_ADHOC;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_UINT_LEN);
+ }
+
+ ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_DS_SET);
+ channel = ie ? ie[2] : 0;
+ if ((channel >= 1) && (channel <= NUM_CHANNELS)) {
+ /* 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. level and noise in dB. No link quality */
+ iwe.cmd = IWEVQUAL;
+ iwe.u.qual.updated = IW_QUAL_DBM | IW_QUAL_QUAL_INVALID;
+ iwe.u.qual.level = bss->level - 0x95;
+ iwe.u.qual.noise = bss->noise - 0x95;
+ /* Wireless tools prior to 27.pre22 will show link quality
+ * anyway, so we provide a reasonable value. */
+ if (iwe.u.qual.level > iwe.u.qual.noise)
+ iwe.u.qual.qual = iwe.u.qual.level - iwe.u.qual.noise;
+ else
+ iwe.u.qual.qual = 0;
+ current_ev = iwe_stream_add_event(info, current_ev, end_buf,
+ &iwe, IW_EV_QUAL_LEN);
+
+ /* Add encryption capability */
+ iwe.cmd = SIOCGIWENCODE;
+ 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, NULL);
+
+ /* WPA IE */
+ ie = orinoco_get_wpa_ie(bss->data, sizeof(bss->data));
+ if (ie) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie[1] + 2;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, ie);
+ }
+
+ /* RSN IE */
+ ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_RSN);
+ if (ie) {
+ iwe.cmd = IWEVGENIE;
+ iwe.u.data.length = ie[1] + 2;
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, ie);
+ }
+
+ ie = orinoco_get_ie(bss->data, sizeof(bss->data), MFIE_TYPE_RATES);
+ if (ie) {
+ char *p = current_ev + iwe_stream_lcp_len(info);
+ int i;
+
+ iwe.cmd = SIOCGIWRATE;
+ /* Those two flags are ignored... */
+ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
+
+ for (i = 2; i < (ie[1] + 2); i++) {
+ iwe.u.bitrate.value = ((ie[i] & 0x7F) * 500000);
+ p = iwe_stream_add_value(info, current_ev, p, end_buf,
+ &iwe, IW_EV_PARAM_LEN);
+ }
+ /* Check if we added any event */
+ if (p > (current_ev + iwe_stream_lcp_len(info)))
+ current_ev = p;
+ }
+
+ /* Timestamp */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ "tsf=%016llx",
+ le64_to_cpu(bss->timestamp));
+ if (iwe.u.data.length)
+ current_ev = iwe_stream_add_point(info, current_ev, end_buf,
+ &iwe, custom);
+
+ /* Beacon interval */
+ iwe.cmd = IWEVCUSTOM;
+ iwe.u.data.length = snprintf(custom, MAX_CUSTOM_LEN,
+ "bcn_int=%d",
+ le16_to_cpu(bss->beacon_interval));
+ 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;
+}
+
/* Return results of a scan */
static int orinoco_ioctl_getscan(struct net_device *dev,
struct iw_request_info *info,
@@ -4555,7 +4889,6 @@ static int orinoco_ioctl_getscan(struct net_device *dev,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
- struct bss_element *bss;
int err = 0;
unsigned long flags;
char *current_ev = extra;
@@ -4575,18 +4908,47 @@ static int orinoco_ioctl_getscan(struct net_device *dev,
goto out;
}

- list_for_each_entry(bss, &priv->bss_list, list) {
- /* Translate to WE format this entry */
- current_ev = orinoco_translate_scan(dev, info, current_ev,
- extra + srq->length,
- &bss->bss,
- bss->last_scanned);
-
- /* Check if there is space for one more entry */
- if ((extra + srq->length - current_ev) <= IW_EV_ADDR_LEN) {
- /* Ask user space to try again with a bigger buffer */
- err = -E2BIG;
- goto out;
+ if (priv->has_ext_scan) {
+ struct xbss_element *bss;
+
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ /* Translate this entry to WE format */
+ current_ev =
+ orinoco_translate_ext_scan(dev, info,
+ current_ev,
+ extra + srq->length,
+ &bss->bss,
+ bss->last_scanned);
+
+ /* Check if there is space for one more entry */
+ if ((extra + srq->length - current_ev)
+ <= IW_EV_ADDR_LEN) {
+ /* Ask user space to try again with a
+ * bigger buffer */
+ err = -E2BIG;
+ goto out;
+ }
+ }
+
+ } else {
+ struct bss_element *bss;
+
+ list_for_each_entry(bss, &priv->bss_list, list) {
+ /* Translate this entry to WE format */
+ current_ev = orinoco_translate_scan(dev, info,
+ current_ev,
+ extra + srq->length,
+ &bss->bss,
+ bss->last_scanned);
+
+ /* Check if there is space for one more entry */
+ if ((extra + srq->length - current_ev)
+ <= IW_EV_ADDR_LEN) {
+ /* Ask user space to try again with a
+ * bigger buffer */
+ err = -E2BIG;
+ goto out;
+ }
}
}

diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index 584d8c9..f510994 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -42,6 +42,12 @@ struct bss_element {
struct list_head list;
};

+struct xbss_element {
+ struct agere_ext_scan_info bss;
+ unsigned long last_scanned;
+ struct list_head list;
+};
+
struct orinoco_private {
void *card; /* Pointer to card dependent structure */
struct device *dev;
@@ -86,6 +92,7 @@ struct orinoco_private {
unsigned int has_sensitivity:1;
unsigned int has_hostscan:1;
unsigned int has_alt_txcntl:1;
+ unsigned int has_ext_scan:1;
unsigned int do_fw_download:1;
unsigned int broken_disableport:1;
unsigned int broken_monitor:1;
@@ -117,7 +124,7 @@ struct orinoco_private {
/* Scanning support */
struct list_head bss_list;
struct list_head bss_free_list;
- struct bss_element *bss_data;
+ void *bss_xbss_data;

int scan_inprogress; /* Scan pending... */
u32 scan_mode; /* Type of scan done */
--
1.5.4.5


2008-08-06 21:08:55

by Dave Kilroy

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

Dan Williams wrote:
> On Wed, 2008-08-06 at 20:29 +0100, Dave wrote:
>> Dan Williams wrote:
>>> On Tue, 2008-08-05 at 18:48 -0400, Pavel Roskin wrote:
>>>> The userspace is welcome to keep a pool of all APs found by any scans,
>>>> but I don't think drivers should do it.
>>> Drivers need to keep a reasonably complete list of APs internally for
>>> association anyway. You don't want to have to do a full scan just to
>>> associate if you have results from 10 seconds ago that are still valid.

>> This is not an issue for orinoco cards, as the firmware selects
>> the AP to use independently of any driver scanning. The requirement to
>> use ap_scan=2 means that wpa_supplicant doesn't need to look at the scan
>> results either (except maybe to verify the configuration matches).

> Well, since the driver supports SSID scanning, we can use ap_scan=1
> anyway. ap_scan=2 is actually pretty evil since it depends on WEXT
> ordering and whatnot.

ap_scan=1 requires the use of SIOCSIWWAP (to set desired BSSID), which
the Agere firmware doesn't support. The firmware only supports setting a
desired SSID (via SIOCSIWESSID), so Agere based orinoco is stuck with
ap_scan=2 :(

Symbol and Intersil don't have this problem.

Dave.

2008-08-02 10:15:21

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 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 454452f..4f11363 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 a39a482..c13a1c2 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.4.5


2008-08-20 21:06:26

by Larry Finger

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

Dave wrote:
>
> At the moment this is only documented in email threads (on
> orinoco-devel) - I lack access to any web/file hosting to put something
> more permanent up. This obviously ought to be corrected.

The wiki at http://linuxwireless.org/en/users would be a good place.

Larry


2008-08-02 10:15:30

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 10/19] orinoco: Fix transmit for Agere/Lucent with fw 9.x

The tx control word has moved into the 802.11 header area on these
firmwares.

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

diff --git a/drivers/net/wireless/hermes.h b/drivers/net/wireless/hermes.h
index 287f536..1d0c584 100644
--- a/drivers/net/wireless/hermes.h
+++ b/drivers/net/wireless/hermes.h
@@ -179,6 +179,7 @@
#define HERMES_802_11_OFFSET (14)
#define HERMES_802_3_OFFSET (14+32)
#define HERMES_802_2_OFFSET (14+32+14)
+#define HERMES_TXCNTL2_OFFSET (HERMES_802_3_OFFSET - 2)

#define HERMES_RXSTAT_ERR (0x0003)
#define HERMES_RXSTAT_BADCRC (0x0001)
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 401ef32..64db579 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -722,7 +722,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
u16 txfid = priv->txfid;
struct ethhdr *eh;
int data_off;
- struct hermes_tx_descriptor desc;
+ int tx_control;
unsigned long flags;

if (! netif_running(dev)) {
@@ -756,21 +756,48 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)

eh = (struct ethhdr *)skb->data;

- memset(&desc, 0, sizeof(desc));
- desc.tx_control = cpu_to_le16(HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX);
- err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), txfid, 0);
- if (err) {
- if (net_ratelimit())
- printk(KERN_ERR "%s: Error %d writing Tx descriptor "
- "to BAP\n", dev->name, err);
- goto busy;
- }
+ tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX;

- /* Clear the 802.11 header and data length fields - some
- * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused
- * if this isn't done. */
- hermes_clear_words(hw, HERMES_DATA0,
- HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
+ if (priv->has_alt_txcntl) {
+ /* WPA enabled firmwares have tx_cntl at the end of
+ * the 802.11 header. So write zeroed descriptor and
+ * 802.11 header at the same time
+ */
+ char desc[HERMES_802_3_OFFSET];
+ __le16 *txcntl = (__le16 *) &desc[HERMES_TXCNTL2_OFFSET];
+
+ memset(&desc, 0, sizeof(desc));
+
+ *txcntl = cpu_to_le16(tx_control);
+ err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
+ txfid, 0);
+ if (err) {
+ if (net_ratelimit())
+ printk(KERN_ERR "%s: Error %d writing Tx "
+ "descriptor to BAP\n", dev->name, err);
+ goto busy;
+ }
+ } else {
+ struct hermes_tx_descriptor desc;
+
+ memset(&desc, 0, sizeof(desc));
+
+ desc.tx_control = cpu_to_le16(tx_control);
+ err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc),
+ txfid, 0);
+ if (err) {
+ if (net_ratelimit())
+ printk(KERN_ERR "%s: Error %d writing Tx "
+ "descriptor to BAP\n", dev->name, err);
+ goto busy;
+ }
+
+ /* Clear the 802.11 header and data length fields - some
+ * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused
+ * if this isn't done. */
+ hermes_clear_words(hw, HERMES_DATA0,
+ HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
+ }

/* Encapsulate Ethernet-II frames */
if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */
@@ -2535,6 +2562,7 @@ static int determine_firmware(struct net_device *dev)
priv->has_ibss = 1;
priv->has_wep = 0;
priv->has_big_wep = 0;
+ priv->has_alt_txcntl = 0;
priv->do_fw_download = 0;

/* Determine capabilities from the firmware version */
@@ -2557,6 +2585,7 @@ static int determine_firmware(struct net_device *dev)
priv->has_hostscan = (firmver >= 0x8000a);
priv->do_fw_download = 1;
priv->broken_monitor = (firmver >= 0x80000);
+ priv->has_alt_txcntl = (firmver >= 0x90000); /* All 9.x ? */

/* Tested with Agere firmware :
* 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index e0acb63..f93752f 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -85,6 +85,7 @@ struct orinoco_private {
unsigned int has_preamble:1;
unsigned int has_sensitivity:1;
unsigned int has_hostscan:1;
+ unsigned int has_alt_txcntl:1;
unsigned int do_fw_download:1;
unsigned int broken_disableport:1;
unsigned int broken_monitor:1;
--
1.5.4.5


2008-08-05 22:48:13

by Pavel Roskin

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

On Tue, 2008-08-05 at 17:55 -0400, Dan Williams wrote:
> On Tue, 2008-08-05 at 17:15 -0400, Pavel Roskin wrote:

> > If I use 32-bit kernel with 32-bit userspace, essid filtering works, but
> > only to a degree. Old scan results are cached somewhere, so if I scan
> > with essid and then without essid, I get filtered results. Likewise, If
> > I don't use filtering the first time, but use it the second time, I get
> > unfiltered results.
>
> Scanning for a specific SSID never has "filtered" the results, nor
> should it. It just probe-scans the requested SSID and return any new
> results in with the cached ones. You requested an SSID scan, thus you
> must know the SSID, thus you can do the filtering yourself?

Perhaps I used a wrong word. If requesting a scan, I expect to get
results from a scan with the parameters I supplied. If the scan results
are from a scan with different parameters, I don't want them. I'd
rather see the driver return EAGAIN than results of a scan with
different parameters.

The userspace is welcome to keep a pool of all APs found by any scans,
but I don't think drivers should do it.

I believe it was discussed before. If there was any decision how we
want drivers to behave, let's stick with it.

--
Regards,
Pavel Roskin

2008-08-05 21:58:52

by Dan Williams

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

On Tue, 2008-08-05 at 17:15 -0400, Pavel Roskin wrote:
> On Mon, 2008-08-04 at 11:34 -0400, Dan Williams wrote:
>
> > Maybe the wireless-tools breakage you're experiencing is causing this to
> > fail on x86-64? Are you using 64-bit wireless-tools and a 64-bit
> > kernel, or 32-bit wireless tools with a 64-bit kernel?
>
> I'm using 64-bit kernel with 64-bit userspace.
>
> If I use 32-bit kernel with 32-bit userspace, essid filtering works, but
> only to a degree. Old scan results are cached somewhere, so if I scan
> with essid and then without essid, I get filtered results. Likewise, If
> I don't use filtering the first time, but use it the second time, I get
> unfiltered results.

Scanning for a specific SSID never has "filtered" the results, nor
should it. It just probe-scans the requested SSID and return any new
results in with the cached ones. You requested an SSID scan, thus you
must know the SSID, thus you can do the filtering yourself?

Dan



2008-08-07 19:40:29

by Dan Williams

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

On Thu, 2008-08-07 at 19:43 +0100, Dave wrote:
> Dan Williams wrote:
> > On Wed, 2008-08-06 at 22:08 +0100, Dave wrote:
> >> Dan Williams wrote:
> >>> Well, since the driver supports SSID scanning, we can use ap_scan=1
> >>> anyway. ap_scan=2 is actually pretty evil since it depends on WEXT
> >>> ordering and whatnot.
> >>
> >> ap_scan=1 requires the use of SIOCSIWWAP (to set desired BSSID), which
> >> the Agere firmware doesn't support. The firmware only supports setting a
> >> desired SSID (via SIOCSIWESSID), so Agere based orinoco is stuck with
> >> ap_scan=2 :(
> >>
> >> Symbol and Intersil don't have this problem.
> >
> > In practice that shouldn't be a problem because drivers (at least
> > ipw2200 does this) usually roam BSSIDs anyway. Technically a bug I
> > think. But honestly, the driver needs to work with ap_scan=1 and I
> > don't see why it couldn't.
>
> I had a reread of <http://hostap.epitest.fi/wpa_supplicant/devel/driver_wrapper.html>.
>
> As a FullMAC driver without support for selecting the BSSID to associate with (or frequency in Managed mode), the only way I can see to make the driver work with ap_scan=1 is to silently ignore the SIOCSIWFREQ and SIOCSIWAP. I don't think this is an approach I would want to pursue.

Well, the current orinoco driver returns "success" (0) for SIOCSIWAP on
Agere firmware. Thus, ap_scan=1 should work just fine for you because
the supplicant won't complain when setting the BSS fails, and the driver
will happily do whatever it wants to with the SSID anyway (just like
ipw2200 currently does I might add).

> That said, what's wrong with the ap_scan=2 mode? You've stated it's not great (and I'm prepared to believe it), but what is the actual problem?

The whole ap_scan thing is a bunch of crap necessitated by previously
bad drivers. Since things are a lot better now, IMO there's not much of
a point to having ap_scan at all. We should just be using ap_scan=1
+scan_ssid=1 everywhere and fix the drivers that don't work.

But the problem with ap_scan=2 is really about the failure window.
ap_scan=2 basically dumps a load of options on the driver, and unless
the options _exactly_ match the configuration of the AP, you won't
connect. The supplicant isn't able to make intelligent choices about
which networks in its config file match the scan result, thus there's a
lot more potential for failure unless you know exactly what your network
is set up to do, and these capabilities aren't always exposed through
beacons. So ap_scan=2 just opens up a huge window of failure and stuff
can't ever Just Work because no intelligence can be applied.

Dan



2008-08-07 20:17:49

by Dave Kilroy

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

Dan Williams wrote:
> On Thu, 2008-08-07 at 19:43 +0100, Dave wrote:
>> That said, what's wrong with the ap_scan=2 mode? You've stated it's
>> not great (and I'm prepared to believe it), but what is the actual problem?

<snip explanation>

Thanks for that.

>> I had a reread of <http://hostap.epitest.fi/wpa_supplicant/devel/driver_wrapper.html>.
>>
>> As a FullMAC driver without support for selecting the BSSID to associate
>> with (or frequency in Managed mode), the only way I can see to make the
>> driver work with ap_scan=1 is to silently ignore the SIOCSIWFREQ and
>> SIOCSIWAP. I don't think this is an approach I would want to pursue.
>
> Well, the current orinoco driver returns "success" (0) for SIOCSIWAP on
> Agere firmware.

borken dkilroy # dmesg | grep eth1
eth1: Hardware identity 0001:0001:0004:0002
eth1: Station identity 001f:0001:0008:0048
eth1: Firmware determined as Lucent/Agere 8.72
eth1: Attempting to download firmware agere_sta_fw.bin
<snip fw download>
eth1: Hardware identity 0001:0001:0004:0002
eth1: Station identity 001f:0002:0009:0030
eth1: Firmware determined as Lucent/Agere 9.48
<snip capabilities>
eth1: ready
eth1: orinoco_cs at 0.0, irq 11, io 0x0100-0x013f
eth1: New link status: Connected (0001)

borken dkilroy # iwlist eth1 scan essid MONTY
eth1 Scan completed :
Cell 01 - Address: 00:18:4D:06:45:76
ESSID:"MONTY"
Mode:Master
Channel:5
<snip detail>

borken dkilroy # iwconfig eth1 ap 00:18:4d:06:45:76
Error for wireless request "Set AP Address" (8B14) :
SET failed on device eth1 ; Operation not supported.

borken dkilroy # iwconfig eth1 ap off
borken dkilroy #


The above is running with my patchset, but I haven't touched the SIOCSIWAP handler. Honest.

I'll have a think.


Dave.

2008-08-02 10:15:44

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 13/19] orinoco: Don't use boolean parameter to record encoding type

For WPA support we need to encode NONE, WEP and TKIP in the encoding
parameter. In anticipation of this we need to change the usage away from
the current boolean usage.

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

diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 3b5c018..013af6e 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -2100,8 +2100,9 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
int err = 0;
int master_wep_flag;
int auth_flag;
+ int enc_flag;

- if (priv->wep_on)
+ if (priv->encode_alg == IW_ENCODE_ALG_WEP)
__orinoco_hw_setup_wepkeys(priv);

if (priv->wep_restrict)
@@ -2109,9 +2110,14 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
else
auth_flag = HERMES_AUTH_OPEN;

+ if (priv->encode_alg == IW_ENCODE_ALG_WEP)
+ enc_flag = 1;
+ else
+ enc_flag = 0;
+
switch (priv->firmware_type) {
case FIRMWARE_TYPE_AGERE: /* Agere style WEP */
- if (priv->wep_on) {
+ if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
/* Enable the shared-key authentication. */
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFAUTHENTICATION_AGERE,
@@ -2119,14 +2125,14 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
}
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFWEPENABLED_AGERE,
- priv->wep_on);
+ enc_flag);
if (err)
return err;
break;

case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */
case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */
- if (priv->wep_on) {
+ if (priv->encode_alg == IW_ENCODE_ALG_WEP) {
if (priv->wep_restrict ||
(priv->firmware_type == FIRMWARE_TYPE_SYMBOL))
master_wep_flag = HERMES_WEP_PRIVACY_INVOKED |
@@ -3015,7 +3021,7 @@ static int orinoco_init(struct net_device *dev)
priv->channel = 0; /* use firmware default */

priv->promiscuous = 0;
- priv->wep_on = 0;
+ priv->encode_alg = IW_ENCODE_ALG_NONE;
priv->tx_key = 0;

/* Make the hardware available, as long as it hasn't been
@@ -3504,7 +3510,7 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev,
struct orinoco_private *priv = netdev_priv(dev);
int index = (erq->flags & IW_ENCODE_INDEX) - 1;
int setindex = priv->tx_key;
- int enable = priv->wep_on;
+ int encode_alg = priv->encode_alg;
int restricted = priv->wep_restrict;
u16 xlen = 0;
int err = -EINPROGRESS; /* Call commit handler */
@@ -3538,9 +3544,9 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev,
xlen = 0;

/* Switch on WEP if off */
- if ((!enable) && (xlen > 0)) {
+ if ((encode_alg != IW_ENCODE_ALG_WEP) && (xlen > 0)) {
setindex = index;
- enable = 1;
+ encode_alg = IW_ENCODE_ALG_WEP;
}
} else {
/* Important note : if the user do "iwconfig eth0 enc off",
@@ -3562,7 +3568,7 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev,
}

if (erq->flags & IW_ENCODE_DISABLED)
- enable = 0;
+ encode_alg = IW_ENCODE_ALG_NONE;
if (erq->flags & IW_ENCODE_OPEN)
restricted = 0;
if (erq->flags & IW_ENCODE_RESTRICTED)
@@ -3577,14 +3583,15 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev,
priv->tx_key = setindex;

/* Try fast key change if connected and only keys are changed */
- if (priv->wep_on && enable && (priv->wep_restrict == restricted) &&
+ if ((priv->encode_alg == encode_alg) &&
+ (priv->wep_restrict == restricted) &&
netif_carrier_ok(dev)) {
err = __orinoco_hw_setup_wepkeys(priv);
/* No need to commit if successful */
goto out;
}

- priv->wep_on = enable;
+ priv->encode_alg = encode_alg;
priv->wep_restrict = restricted;

out:
@@ -3613,7 +3620,7 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev,
index = priv->tx_key;

erq->flags = 0;
- if (! priv->wep_on)
+ if (!priv->encode_alg)
erq->flags |= IW_ENCODE_DISABLED;
erq->flags |= index + 1;

diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index f510994..5605fd3 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -100,7 +100,7 @@ struct orinoco_private {
/* Configuration paramaters */
u32 iw_mode;
int prefer_port3;
- u16 wep_on, wep_restrict, tx_key;
+ u16 encode_alg, wep_restrict, tx_key;
struct orinoco_key keys[ORINOCO_MAX_KEYS];
int bitratemode;
char nick[IW_ESSID_MAX_SIZE+1];
--
1.5.4.5


2008-08-04 15:38:27

by Dan Williams

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

On Mon, 2008-08-04 at 00:48 -0400, Pavel Roskin wrote:
> On Sat, 2008-08-02 at 11:14 +0100, [email protected] wrote:
> > 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.
>
> That's a nice thing to have, but it doesn't seem to work on x86_64 (I
> didn't try other platforms). I tried wireless-tools 30.pre6 instead of
> 29, but it broke the output completely:
>
> # iwlist eth1 scan
> *** Please report to [email protected] your platform details
> *** and the following line :
> *** IW_EV_LCP_PK2_LEN = 4 ; IW_EV_POINT_PK2_LEN = 8
>
> eth1 Scan completed :
> Cell 01 - Address: 00:19:5B:56:FC:73
> ESSID:off/any/hidden
> Mode:Master
> Frequency=2.437 GHz (Channel 6)
> Signal level:-29 dBm Noise level:-81 dBm
> Encryption key:E0
> Extra:
> Cell 02 - Address: 00:15:6D:53:9A:CA
> ESSID:off/any/hidden
> Mode:Master
> Frequency:2.462 GHz (Channel 11)
> Signal level:-49 dBm Noise level:-80 dBm
> Encryption key:E0
> Extra:
>
>
> > + if (priv->scan_mode & IW_SCAN_THIS_ESSID) {
>
> The debug output shows that the about condition is never true with
> either version of iwlist. The command is:
>
> iwlist eth1 scan essid foo

That code looks right, since srq->flags is getting copied into
priv->scan_mode.

Maybe the wireless-tools breakage you're experiencing is causing this to
fail on x86-64? Are you using 64-bit wireless-tools and a 64-bit
kernel, or 32-bit wireless tools with a 64-bit kernel?

Dan


2008-08-07 18:43:54

by Dave Kilroy

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

Dan Williams wrote:
> On Wed, 2008-08-06 at 22:08 +0100, Dave wrote:
>> Dan Williams wrote:
>>> Well, since the driver supports SSID scanning, we can use ap_scan=1
>>> anyway. ap_scan=2 is actually pretty evil since it depends on WEXT
>>> ordering and whatnot.
>>
>> ap_scan=1 requires the use of SIOCSIWWAP (to set desired BSSID), which
>> the Agere firmware doesn't support. The firmware only supports setting a
>> desired SSID (via SIOCSIWESSID), so Agere based orinoco is stuck with
>> ap_scan=2 :(
>>
>> Symbol and Intersil don't have this problem.
>
> In practice that shouldn't be a problem because drivers (at least
> ipw2200 does this) usually roam BSSIDs anyway. Technically a bug I
> think. But honestly, the driver needs to work with ap_scan=1 and I
> don't see why it couldn't.

I had a reread of <http://hostap.epitest.fi/wpa_supplicant/devel/driver_wrapper.html>.

As a FullMAC driver without support for selecting the BSSID to associate with (or frequency in Managed mode), the only way I can see to make the driver work with ap_scan=1 is to silently ignore the SIOCSIWFREQ and SIOCSIWAP. I don't think this is an approach I would want to pursue.

That said, what's wrong with the ap_scan=2 mode? You've stated it's not great (and I'm prepared to believe it), but what is the actual problem?



Dave.

2008-08-21 06:43:24

by Johannes Berg

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

On Thu, 2008-08-21 at 00:07 +0100, Dave wrote:

> Thanks Larry/Johannes. I thought that might be where it should go, but
> it looks quite mac80211 oriented at the moment.

It is, but mostly because all the new drivers except libertas are
mac80211 drivers, and all the old drivers used to already have
information somewhere...

> Anyway, I've added an initial orinoco page
> <http://wireless.kernel.org/en/users/Drivers/orinoco> and linked to it
> on <http://wireless.kernel.org/en/users/Drivers>.

Great. I've just removed all the italics, I think I need to change my
template :) If you want, you can sign up for notifications on that page
to keep track of changes others make.

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2008-08-05 16:22:40

by Jean Tourrilhes

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

On Mon, Aug 04, 2008 at 11:34:20AM -0400, Dan Williams wrote:
> On Mon, 2008-08-04 at 00:48 -0400, Pavel Roskin wrote:
> > On Sat, 2008-08-02 at 11:14 +0100, [email protected] wrote:
> > > 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.
> >
> > That's a nice thing to have, but it doesn't seem to work on x86_64 (I
> > didn't try other platforms). I tried wireless-tools 30.pre6 instead of
> > 29, but it broke the output completely:
> >
> > # iwlist eth1 scan
> > *** Please report to [email protected] your platform details
> > *** and the following line :
> > *** IW_EV_LCP_PK2_LEN = 4 ; IW_EV_POINT_PK2_LEN = 8
> >
> > eth1 Scan completed :
> > Cell 01 - Address: 00:19:5B:56:FC:73
> > ESSID:off/any/hidden
> > Mode:Master
> > Frequency=2.437 GHz (Channel 6)
> > Signal level:-29 dBm Noise level:-81 dBm
> > Encryption key:E0
> > Extra:
> > Cell 02 - Address: 00:15:6D:53:9A:CA
> > ESSID:off/any/hidden
> > Mode:Master
> > Frequency:2.462 GHz (Channel 11)
> > Signal level:-49 dBm Noise level:-80 dBm
> > Encryption key:E0
> > Extra:
> >
> >
> > > + if (priv->scan_mode & IW_SCAN_THIS_ESSID) {
> >
> > The debug output shows that the about condition is never true with
> > either version of iwlist. The command is:
> >
> > iwlist eth1 scan essid foo
>
> That code looks right, since srq->flags is getting copied into
> priv->scan_mode.
>
> Maybe the wireless-tools breakage you're experiencing is causing this to
> fail on x86-64? Are you using 64-bit wireless-tools and a 64-bit
> kernel, or 32-bit wireless tools with a 64-bit kernel?
>
> Dan

I still need to look into those 64 bit issues. That's the
reason why WT-30 is still pending. The good news is that I now have a
64 bit box. The bad news is work and family.
Have fun...

Jean


2008-08-02 10:15:34

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 11/19] orinoco: address checkpatch typedef warning

Just sprinkle the necessary structs around...

Signed-off-by: David Kilroy <[email protected]>
---
drivers/net/wireless/orinoco.c | 13 +++++++------
drivers/net/wireless/orinoco.h | 6 +++---
2 files changed, 10 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 64db579..07e118c 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -279,7 +279,8 @@ static int orinoco_bss_data_allocate(struct orinoco_private *priv)
return 0;

priv->bss_data =
- kzalloc(ORINOCO_MAX_BSS_COUNT * sizeof(bss_element), GFP_KERNEL);
+ kzalloc(ORINOCO_MAX_BSS_COUNT * sizeof(struct bss_element),
+ GFP_KERNEL);
if (!priv->bss_data) {
printk(KERN_WARNING "Out of memory allocating beacons");
return -ENOMEM;
@@ -1413,8 +1414,8 @@ static void orinoco_send_wevents(struct work_struct *work)
static inline void orinoco_clear_scan_results(struct orinoco_private *priv,
unsigned long scan_age)
{
- bss_element *bss;
- bss_element *tmp_bss;
+ struct bss_element *bss;
+ struct bss_element *tmp_bss;

/* Blow away current list of scan results */
list_for_each_entry_safe(bss, tmp_bss, &priv->bss_list, list) {
@@ -1489,7 +1490,7 @@ static int orinoco_process_scan_results(struct net_device *dev,
/* Read the entries one by one */
for (; offset + atom_len <= len; offset += atom_len) {
int found = 0;
- bss_element *bss = NULL;
+ struct bss_element *bss = NULL;

/* Get next atom */
atom = (union hermes_scan_info *) (buf + offset);
@@ -1511,7 +1512,7 @@ static int orinoco_process_scan_results(struct net_device *dev,
/* Grab a bss off the free list */
if (!found && !list_empty(&priv->bss_free_list)) {
bss = list_entry(priv->bss_free_list.next,
- bss_element, list);
+ struct bss_element, list);
list_del(priv->bss_free_list.next);

list_add_tail(&bss->list, &priv->bss_list);
@@ -4554,7 +4555,7 @@ static int orinoco_ioctl_getscan(struct net_device *dev,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
- bss_element *bss;
+ struct bss_element *bss;
int err = 0;
unsigned long flags;
char *current_ev = extra;
diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index f93752f..584d8c9 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -36,11 +36,11 @@ typedef enum {
FIRMWARE_TYPE_SYMBOL
} fwtype_t;

-typedef struct {
+struct bss_element {
union hermes_scan_info bss;
unsigned long last_scanned;
struct list_head list;
-} bss_element;
+};

struct orinoco_private {
void *card; /* Pointer to card dependent structure */
@@ -117,7 +117,7 @@ struct orinoco_private {
/* Scanning support */
struct list_head bss_list;
struct list_head bss_free_list;
- bss_element *bss_data;
+ struct bss_element *bss_data;

int scan_inprogress; /* Scan pending... */
u32 scan_mode; /* Type of scan done */
--
1.5.4.5


2008-08-07 08:07:18

by Jouni Malinen

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

On Wed, Aug 06, 2008 at 05:01:21PM -0400, Dan Williams wrote:

> That's probably OK. The driver doesn't say it supports WPA2+TKIP, so
> you're in the clear here. Either the supplicant has to be fixed to
> respect driver capabilities, or the user has to know. I vote for fixing
> the supplicant.

I have been postponing this since most drivers did not use to report
their capabilities correctly. Anyway, if the driver is indeed reporting
its capabilities, I would agree that wpa_supplicant should follow what
is reported. I think that there's already code for reading the
capabilities, but the code that acts on them (i.e., rejects some options
in scan results) has not yet been written. I need to make sure that all
driver wrappers default to enabling everything if the driver does not
report capabilities, so that we can start filtering available options
without breaking something that is working in the current version.

--
Jouni Malinen PGP id EFC895FA

2008-08-20 20:49:56

by Dave Kilroy

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

John W. Linville wrote:
> On Sat, Aug 02, 2008 at 11:14:14AM +0100, [email protected] wrote:
>> 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 is against wireless-testing (master-2008-07-16), is
>> sparse clean (UP), and should be bisectable. It is almost checkpatch
>> clean - the single warning looks like a false positive to me.
>
> Are we satisfied w/ this patch series? If so, could I ask Dave to
> repost them as a single series (rather than having to randomly replace
> some of them) in the middle?

I believe I have addressed all the issues that I can. The main outstanding one is why Pavel was seeing issues on his 64 bit kernel/64 bit userspace setup.

I will repost the full series shortly.

>> 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>.
>
> Is the above documented anywhere other than this email thread?

At the moment this is only documented in email threads (on orinoco-devel) - I lack access to any web/file hosting to put something more permanent up. This obviously ought to be corrected.


Regards,

Dave.

2008-08-02 10:15:12

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 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 c3e45bd..fedb00b 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -15,7 +15,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.4.5


2008-08-02 10:15:00

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 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 29d3910..5504155 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.4.5


2008-08-05 22:59:28

by Pavel Roskin

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

On Tue, 2008-08-05 at 00:09 +0100, Dave wrote:
> Pavel Roskin wrote:
> >> It is almost checkpatch
> >> clean - the single warning looks like a false positive to me.
> >
> > If you mean orinoco_ioctl_getnick(), that's a false positive.
>
> Actually I meant the DECLARE_DEFAULT_PDA macro in hermes_dld.c:
>
> dkilroy@borken /usr/src/wireless-testing $ git diff HEAD~20 | scripts/checkpatch.pl -
> ERROR: Macros with multiple statements should be enclosed in a do - while loop
> #933: FILE: drivers/net/wireless/hermes_dld.c:570:
> +#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 \
> +}

I suggest that you take the structure definition outside the macro and
give it a reasonable name.

--
Regards,
Pavel Roskin

2008-08-06 00:37:33

by Pavel Roskin

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

On Mon, 2008-08-04 at 19:28 -0400, Dan Williams wrote:
> > I'm not familiar with the difference between WPA/WPA2. Is that expected to work?
>
> Depends; if it's WPA2/RSN + AES-CCMP, then most likely not, because I'm
> pretty sure the cards won't have onboard AES crypto acceleration
> hardware. Most cards built before WPA2/RSN was standardized don't have
> the horsepower to do AES in firmware purely in software either.
>
> But you might be able to get away with WPA2/RSN + TKIP if the AP allows
> this configuration. In that configuration, the only difference between
> WPA and WPA2/RSN would be the information element IDs, really. But if
> the firmware itself doesn't say it supports WPA on whatever website it
> came from, then likely the card won't do WPA2/RSN either.
>
> In fact, you already did the right thing by _NOT_ adding
> IW_ENC_CAPA_WPA2 and IW_ENC_CAPA_CIPHER_CCMP in the GIWRANGE handler.
> Thus, you have not signaled that WPA2 is supported by the driver, and
> thus Pavel should not have expected it to work in the first place :)

Perhaps wpa_supplicant should have told me that.

I tried association to hostapd with madwifi, and the only working
configuration is WPA1 only with TKIP. Even enabling WPA1 and WPA2 and
TKIP makes the connection fail. Forcing WPA1 and TKIP in
wpa_supplicant.conf doesn't help.

I looked at the patches. They have references to TKIP, but not to CCMP.
Yet it would be nice if we could support WPA1+WPA2, as we cannot require
that access points stop supporting WPA2, which is the 802.11i standard.
It's possible that we have an issue outside the driver.

> > > [185219.617236] eth1: Ext scan results too large (272 bytes).
> Truncating
> > > results to 270 bytes.

It can even be 296 bytes if I enable WPA1 and WPA2. But this only
happens with an 802.11n router (D-Link DIR-615). There are no such
complaints about hostapd+madwifi. Perhaps 802.11n provides more data in
beacons.

I tried increasing the "data" size from 200 to 300 in hermes.h, and the
message went away. I was able to associate to D-Link DIR-615 when it
was set to WPA1. Setting it to WPA1+WPA2 caused connection to fail,
just like it happened with hostapd. Scanning confirms that the cipher
for WPA1 is TKIP.

I think it should be safe to increase the side of "data" and remove the
unused "flags" filed at the end. After all, we should trust the
firmware, not the unmaintained Agere driver. If the firmware says it
has 296 bytes for us (header + "data"), why mistrust it? I understand
if the firmware tells us it has 4 gigabytes of data from the beacon,
then it's clearly lying. Let's make "data" 256 bytes to make it a nice
round number. It would gives us 56 extra bytes, and we need extra 36
bytes in the worst case I could produce.

I'm sorry, I'm going to be offline soon, and I really cannot do any more
tests.

--
Regards,
Pavel Roskin

2008-08-04 04:48:28

by Pavel Roskin

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

On Sat, 2008-08-02 at 11:14 +0100, [email protected] wrote:
> 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.

That's a nice thing to have, but it doesn't seem to work on x86_64 (I
didn't try other platforms). I tried wireless-tools 30.pre6 instead of
29, but it broke the output completely:

# iwlist eth1 scan
*** Please report to [email protected] your platform details
*** and the following line :
*** IW_EV_LCP_PK2_LEN = 4 ; IW_EV_POINT_PK2_LEN = 8

eth1 Scan completed :
Cell 01 - Address: 00:19:5B:56:FC:73
ESSID:off/any/hidden
Mode:Master
Frequency=2.437 GHz (Channel 6)
Signal level:-29 dBm Noise level:-81 dBm
Encryption key:E0
Extra:
Cell 02 - Address: 00:15:6D:53:9A:CA
ESSID:off/any/hidden
Mode:Master
Frequency:2.462 GHz (Channel 11)
Signal level:-49 dBm Noise level:-80 dBm
Encryption key:E0
Extra:


> + if (priv->scan_mode & IW_SCAN_THIS_ESSID) {

The debug output shows that the about condition is never true with
either version of iwlist. The command is:

iwlist eth1 scan essid foo

Copying Jean regarding the message.

--
Regards,
Pavel Roskin

2008-08-04 23:09:35

by Dave Kilroy

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

Pavel Roskin wrote:
>> It is almost checkpatch
>> clean - the single warning looks like a false positive to me.
>
> If you mean orinoco_ioctl_getnick(), that's a false positive.

Actually I meant the DECLARE_DEFAULT_PDA macro in hermes_dld.c:

dkilroy@borken /usr/src/wireless-testing $ git diff HEAD~20 | scripts/checkpatch.pl -
ERROR: Macros with multiple statements should be enclosed in a do - while loop
#933: FILE: drivers/net/wireless/hermes_dld.c:570:
+#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 \
+}

> However,
> sparse finds two issues on x86_64, both due to sizeof() returning
> size_t, which is wider than int.
>
> Here's the fix. Please integrate it into the patches that introduce the
> code.

Will do. I obviously need to upgrade my version of sparse, since it isn't complaining 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>
> I tested it on my WPA2 AP and could not associate:

I'm not familiar with the difference between WPA/WPA2. Is that expected to work?

> # wpa_supplicant -c /etc/wpa_supplicant/wpa_main.conf -D wext -i eth1
> CTRL-EVENT-SCAN-RESULTS
> Trying to associate with 00:19:5b:56:fc:73 (SSID='mike' freq=2437 MHz)
> ioctl[SIOCSIWFREQ]: Device or resource busy
> ioctl[SIOCSIWAP]: Operation not supported
> Association request to the driver failed

The 'Device or resource busy' definitely looks wrong. We only return -EBUSY here if the device was in infrastructure mode or during initialisation/reset.

The SIOCSIWAP response is expected if you have wpa_supplicant configured with ap_scan=1 (I think). The driver isn't able to tell the firmware which bssid to associate with - instead after the SIOCSIWENCODEEXT the driver selects a bssid belonging to the specified ssid. Setting ap_scan=2 should avoid the SIOCSIWAP and it should work better.

> And that's from the kernel log:
>
> [185185.284523] eth1: Hardware identity 0001:0004:0005:0000
> [185185.284686] eth1: Station identity 001f:0002:0009:002a
> [185185.284722] eth1: Firmware determined as Lucent/Agere 9.42
> [185185.284758] eth1: Ad-hoc demo mode supported
> [185185.284793] eth1: IEEE standard IBSS ad-hoc mode supported
> [185185.284829] eth1: WEP supported, 104-bit key
> [185185.284872] eth1: WPA-PSK supported
> [185185.285010] eth1: MAC address 00:02:2d:8b:56:87
> [185185.285153] eth1: Station name "HERMES I"
> [185185.285811] eth1: ready
> [185185.287298] eth1: orinoco_cs at 0.0, irq 18, io 0x1100-0x113f
> [185213.396202] eth1: New link status: Connected (0001)
> [185214.469964] eth1: Ext scan results too large (272 bytes). Truncating
> results to 270 bytes.
> [185214.575811] eth1: Lucent/Agere firmware doesn't support manual
> roaming
> [185219.617236] eth1: Ext scan results too large (272 bytes). Truncating
> results to 270 bytes.

The truncation of scan results in this case is expected and is due to the IEs being reported by your access point (previously I dropped the scan result completely). I'm hoping the last two bytes aren't significant. The results from my AP (and hopefully most others) are shorter and are not truncated.

Regards,

Dave.

2008-08-05 22:22:50

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 09/19] 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 <[email protected]>
---
This patch is modified to address the min(pda_len, sizeof()) issue.

Note that I updated sparse to git HEAD, but it still isn't complaining
to me - I assume I actually have to invoke it in a 64 bit environment to
check whether the issues are indeed addressed.

drivers/net/wireless/Kconfig | 2 +-
drivers/net/wireless/airport.c | 3 +-
drivers/net/wireless/orinoco.c | 314 ++++++++++++++++++++++++++++++++-
drivers/net/wireless/orinoco.h | 9 +-
drivers/net/wireless/orinoco_cs.c | 3 +-
drivers/net/wireless/orinoco_nortel.c | 3 +-
drivers/net/wireless/orinoco_pci.c | 3 +-
drivers/net/wireless/orinoco_plx.c | 3 +-
drivers/net/wireless/orinoco_tmd.c | 3 +-
drivers/net/wireless/spectrum_cs.c | 159 ++---------------
10 files changed, 346 insertions(+), 156 deletions(-)

diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 5e0eda0..cff47f7 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -298,6 +298,7 @@ config HERMES
tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)"
depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211
select WIRELESS_EXT
+ select FW_LOADER
---help---
A driver for 802.11b wireless cards based on the "Hermes" or
Intersil HFA384x (Prism 2) MAC controller. This includes the vast
@@ -387,7 +388,6 @@ config PCMCIA_HERMES
config PCMCIA_SPECTRUM
tristate "Symbol Spectrum24 Trilogy PCMCIA card support"
depends on PCMCIA && HERMES
- select FW_LOADER
---help---

This is a driver for 802.11b cards using RAM-loadable Symbol
diff --git a/drivers/net/wireless/airport.c b/drivers/net/wireless/airport.c
index 6f7eb9f..ce03a2e 100644
--- a/drivers/net/wireless/airport.c
+++ b/drivers/net/wireless/airport.c
@@ -180,7 +180,8 @@ airport_attach(struct macio_dev *mdev, const struct of_device_id *match)
}

/* Allocate space for private device-specific data */
- dev = alloc_orinocodev(sizeof(*card), airport_hard_reset);
+ dev = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev,
+ airport_hard_reset, NULL);
if (! dev) {
printk(KERN_ERR PFX "Cannot allocate network device\n");
return -ENODEV;
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 655d030..7611433 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -82,12 +82,14 @@
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
+#include <linux/firmware.h>
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <net/iw_handler.h>
#include <net/ieee80211.h>

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

/********************************************************************/
@@ -301,6 +303,272 @@ static void orinoco_bss_data_init(struct orinoco_private *priv)
list_add_tail(&priv->bss_data[i].list, &priv->bss_free_list);
}

+
+/********************************************************************/
+/* Download functionality */
+/********************************************************************/
+
+struct fw_info {
+ char *pri_fw;
+ char *sta_fw;
+ char *ap_fw;
+ u32 pda_addr;
+ u16 pda_size;
+};
+
+const static struct fw_info orinoco_fw[] = {
+ { "", "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 },
+ { "", "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 },
+ { "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", "", 0x00003100, 0x100 }
+};
+
+/* 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));
+
+/* Download either STA or AP firmware into the card. */
+static int
+orinoco_dl_firmware(struct orinoco_private *priv,
+ const struct fw_info *fw,
+ int ap)
+{
+ /* Plug Data Area (PDA) */
+ __le16 pda[512] = { 0 };
+
+ hermes_t *hw = &priv->hw;
+ const struct firmware *fw_entry;
+ const struct orinoco_fw_header *hdr;
+ const unsigned char *first_block;
+ const unsigned char *end;
+ const char *firmware;
+ struct net_device *dev = priv->ndev;
+ int err;
+
+ if (ap)
+ firmware = fw->ap_fw;
+ else
+ firmware = fw->sta_fw;
+
+ printk(KERN_DEBUG "%s: Attempting to download firmware %s\n",
+ dev->name, firmware);
+
+ /* Read current plug data */
+ err = hermes_read_pda(hw, pda, fw->pda_addr,
+ min_t(u16, fw->pda_size, sizeof(pda)), 0);
+ printk(KERN_DEBUG "%s: Read PDA returned %d\n", dev->name, err);
+ if (err)
+ return err;
+
+ err = request_firmware(&fw_entry, firmware, priv->dev);
+ if (err) {
+ printk(KERN_ERR "%s: Cannot find firmware %s\n",
+ dev->name, firmware);
+ return -ENOENT;
+ }
+
+ hdr = (const struct orinoco_fw_header *) fw_entry->data;
+
+ /* Enable aux port to allow programming */
+ err = hermesi_program_init(hw, le32_to_cpu(hdr->entry_point));
+ printk(KERN_DEBUG "%s: Program init returned %d\n", dev->name, err);
+ if (err != 0)
+ goto abort;
+
+ /* Program data */
+ first_block = (fw_entry->data +
+ le16_to_cpu(hdr->headersize) +
+ le32_to_cpu(hdr->block_offset));
+ end = fw_entry->data + fw_entry->size;
+
+ err = hermes_program(hw, first_block, end);
+ printk(KERN_DEBUG "%s: Program returned %d\n", dev->name, 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 "%s: Apply PDA returned %d\n", dev->name, err);
+ if (err)
+ goto abort;
+
+ /* Tell card we've finished */
+ err = hermesi_program_end(hw);
+ printk(KERN_DEBUG "%s: Program end returned %d\n", dev->name, err);
+ if (err != 0)
+ goto abort;
+
+ /* Check if we're running */
+ printk(KERN_DEBUG "%s: hermes_present returned %d\n",
+ dev->name, hermes_present(hw));
+
+abort:
+ release_firmware(fw_entry);
+ return err;
+}
+
+/* End markers */
+#define TEXT_END 0x1A /* End of text header */
+
+/*
+ * 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.
+ */
+static int
+symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
+ const unsigned char *image, const unsigned char *end,
+ int secondary)
+{
+ hermes_t *hw = &priv->hw;
+ int ret;
+ const unsigned char *ptr;
+ const unsigned char *first_block;
+
+ /* Plug Data Area (PDA) */
+ __le16 pda[256];
+
+ /* Binary block begins after the 0x1A marker */
+ ptr = image;
+ while (*ptr++ != TEXT_END);
+ first_block = ptr;
+
+ /* Read the PDA from EEPROM */
+ if (secondary) {
+ ret = hermes_read_pda(hw, pda, fw->pda_addr, sizeof(pda), 1);
+ if (ret)
+ return ret;
+ }
+
+ /* Stop the firmware, so that it can be safely rewritten */
+ if (priv->stop_fw) {
+ ret = priv->stop_fw(priv, 1);
+ if (ret)
+ return ret;
+ }
+
+ /* Program the adapter with new firmware */
+ ret = hermes_program(hw, first_block, end);
+ if (ret)
+ return ret;
+
+ /* Write the PDA to the adapter */
+ if (secondary) {
+ size_t len = hermes_blocks_length(first_block);
+ ptr = first_block + len;
+ ret = hermes_apply_pda(hw, ptr, pda);
+ if (ret)
+ return ret;
+ }
+
+ /* Run the firmware */
+ if (priv->stop_fw) {
+ ret = priv->stop_fw(priv, 0);
+ if (ret)
+ return ret;
+ }
+
+ /* Reset hermes chip and make sure it responds */
+ ret = hermes_init(hw);
+
+ /* hermes_reset() should return 0 with the secondary firmware */
+ if (secondary && ret != 0)
+ return -ENODEV;
+
+ /* And this should work with any firmware */
+ if (!hermes_present(hw))
+ return -ENODEV;
+
+ return 0;
+}
+
+
+/*
+ * 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
+symbol_dl_firmware(struct orinoco_private *priv,
+ const struct fw_info *fw)
+{
+ struct net_device *dev = priv->ndev;
+ int ret;
+ const struct firmware *fw_entry;
+
+ if (request_firmware(&fw_entry, fw->pri_fw,
+ priv->dev) != 0) {
+ printk(KERN_ERR "%s: Cannot find firmware: %s\n",
+ dev->name, fw->pri_fw);
+ return -ENOENT;
+ }
+
+ /* Load primary firmware */
+ ret = symbol_dl_image(priv, fw, fw_entry->data,
+ fw_entry->data + fw_entry->size, 0);
+ release_firmware(fw_entry);
+ if (ret) {
+ printk(KERN_ERR "%s: Primary firmware download failed\n",
+ dev->name);
+ return ret;
+ }
+
+ if (request_firmware(&fw_entry, fw->sta_fw,
+ priv->dev) != 0) {
+ printk(KERN_ERR "%s: Cannot find firmware: %s\n",
+ dev->name, fw->sta_fw);
+ return -ENOENT;
+ }
+
+ /* Load secondary firmware */
+ ret = symbol_dl_image(priv, fw, fw_entry->data,
+ fw_entry->data + fw_entry->size, 1);
+ release_firmware(fw_entry);
+ if (ret) {
+ printk(KERN_ERR "%s: Secondary firmware download failed\n",
+ dev->name);
+ }
+
+ return ret;
+}
+
+static int orinoco_download(struct orinoco_private *priv)
+{
+ int err = 0;
+ /* Reload firmware */
+ switch (priv->firmware_type) {
+ case FIRMWARE_TYPE_AGERE:
+ /* case FIRMWARE_TYPE_INTERSIL: */
+ err = orinoco_dl_firmware(priv,
+ &orinoco_fw[priv->firmware_type], 0);
+ break;
+
+ case FIRMWARE_TYPE_SYMBOL:
+ err = symbol_dl_firmware(priv,
+ &orinoco_fw[priv->firmware_type]);
+ break;
+ case FIRMWARE_TYPE_INTERSIL:
+ break;
+ }
+ /* TODO: if we fail we probably need to reinitialise
+ * the driver */
+
+ return err;
+}
+
/********************************************************************/
/* Device methods */
/********************************************************************/
@@ -2050,6 +2318,12 @@ static void orinoco_reset(struct work_struct *work)
}
}

+ if (priv->do_fw_download) {
+ err = orinoco_download(priv);
+ if (err)
+ priv->do_fw_download = 0;
+ }
+
err = orinoco_reinit_firmware(dev);
if (err) {
printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n",
@@ -2261,6 +2535,7 @@ static int determine_firmware(struct net_device *dev)
priv->has_ibss = 1;
priv->has_wep = 0;
priv->has_big_wep = 0;
+ priv->do_fw_download = 0;

/* Determine capabilities from the firmware version */
switch (priv->firmware_type) {
@@ -2280,6 +2555,7 @@ static int determine_firmware(struct net_device *dev)
priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
priv->ibss_port = 1;
priv->has_hostscan = (firmver >= 0x8000a);
+ priv->do_fw_download = 1;
priv->broken_monitor = (firmver >= 0x80000);

/* Tested with Agere firmware :
@@ -2324,6 +2600,21 @@ static int determine_firmware(struct net_device *dev)
firmver >= 0x31000;
priv->has_preamble = (firmver >= 0x20000);
priv->ibss_port = 4;
+
+ /* Symbol firmware is found on various cards, but
+ * there has been no attempt to check firmware
+ * download on non-spectrum_cs based cards.
+ *
+ * Given that the Agere firmware download works
+ * differently, we should avoid doing a firmware
+ * download with the Symbol algorithm on non-spectrum
+ * cards.
+ *
+ * For now we can identify a spectrum_cs based card
+ * because it has a firmware reset function.
+ */
+ priv->do_fw_download = (priv->stop_fw != NULL);
+
priv->broken_disableport = (firmver == 0x25013) ||
(firmver >= 0x30000 && firmver <= 0x31000);
priv->has_hostscan = (firmver >= 0x31001) ||
@@ -2394,6 +2685,20 @@ static int orinoco_init(struct net_device *dev)
goto out;
}

+ if (priv->do_fw_download) {
+ err = orinoco_download(priv);
+ if (err)
+ priv->do_fw_download = 0;
+
+ /* Check firmware version again */
+ err = determine_firmware(dev);
+ if (err != 0) {
+ printk(KERN_ERR "%s: Incompatible firmware, aborting\n",
+ dev->name);
+ goto out;
+ }
+ }
+
if (priv->has_port3)
printk(KERN_DEBUG "%s: Ad-hoc demo mode supported\n", dev->name);
if (priv->has_ibss)
@@ -2536,8 +2841,11 @@ static int orinoco_init(struct net_device *dev)
return err;
}

-struct net_device *alloc_orinocodev(int sizeof_card,
- int (*hard_reset)(struct orinoco_private *))
+struct net_device
+*alloc_orinocodev(int sizeof_card,
+ struct device *device,
+ int (*hard_reset)(struct orinoco_private *),
+ int (*stop_fw)(struct orinoco_private *, int))
{
struct net_device *dev;
struct orinoco_private *priv;
@@ -2552,6 +2860,7 @@ struct net_device *alloc_orinocodev(int sizeof_card,
+ sizeof(struct orinoco_private));
else
priv->card = NULL;
+ priv->dev = device;

if (orinoco_bss_data_allocate(priv))
goto err_out_free;
@@ -2577,6 +2886,7 @@ struct net_device *alloc_orinocodev(int sizeof_card,
dev->open = orinoco_open;
dev->stop = orinoco_stop;
priv->hard_reset = hard_reset;
+ priv->stop_fw = stop_fw;

spin_lock_init(&priv->lock);
priv->open = 0;
diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index c6b1858..e0acb63 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -44,7 +44,9 @@ typedef struct {

struct orinoco_private {
void *card; /* Pointer to card dependent structure */
+ struct device *dev;
int (*hard_reset)(struct orinoco_private *);
+ int (*stop_fw)(struct orinoco_private *, int);

/* Synchronisation stuff */
spinlock_t lock;
@@ -83,6 +85,7 @@ struct orinoco_private {
unsigned int has_preamble:1;
unsigned int has_sensitivity:1;
unsigned int has_hostscan:1;
+ unsigned int do_fw_download:1;
unsigned int broken_disableport:1;
unsigned int broken_monitor:1;

@@ -130,8 +133,10 @@ extern int orinoco_debug;
/* Exported prototypes */
/********************************************************************/

-extern struct net_device *alloc_orinocodev(int sizeof_card,
- int (*hard_reset)(struct orinoco_private *));
+extern struct net_device *alloc_orinocodev(
+ int sizeof_card, struct device *device,
+ int (*hard_reset)(struct orinoco_private *),
+ int (*stop_fw)(struct orinoco_private *, int));
extern void free_orinocodev(struct net_device *dev);
extern int __orinoco_up(struct net_device *dev);
extern int __orinoco_down(struct net_device *dev);
diff --git a/drivers/net/wireless/orinoco_cs.c b/drivers/net/wireless/orinoco_cs.c
index 1c216e0..1ccf5a4 100644
--- a/drivers/net/wireless/orinoco_cs.c
+++ b/drivers/net/wireless/orinoco_cs.c
@@ -109,7 +109,8 @@ orinoco_cs_probe(struct pcmcia_device *link)
struct orinoco_private *priv;
struct orinoco_pccard *card;

- dev = alloc_orinocodev(sizeof(*card), orinoco_cs_hard_reset);
+ dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link),
+ orinoco_cs_hard_reset, NULL);
if (! dev)
return -ENOMEM;
priv = netdev_priv(dev);
diff --git a/drivers/net/wireless/orinoco_nortel.c b/drivers/net/wireless/orinoco_nortel.c
index 35ec5fc..2fc8659 100644
--- a/drivers/net/wireless/orinoco_nortel.c
+++ b/drivers/net/wireless/orinoco_nortel.c
@@ -182,7 +182,8 @@ static int orinoco_nortel_init_one(struct pci_dev *pdev,
}

/* Allocate network device */
- dev = alloc_orinocodev(sizeof(*card), orinoco_nortel_cor_reset);
+ dev = alloc_orinocodev(sizeof(*card), &pdev->dev,
+ orinoco_nortel_cor_reset, NULL);
if (!dev) {
printk(KERN_ERR PFX "Cannot allocate network device\n");
err = -ENOMEM;
diff --git a/drivers/net/wireless/orinoco_pci.c b/drivers/net/wireless/orinoco_pci.c
index 2547d5d..4ebd638 100644
--- a/drivers/net/wireless/orinoco_pci.c
+++ b/drivers/net/wireless/orinoco_pci.c
@@ -139,7 +139,8 @@ static int orinoco_pci_init_one(struct pci_dev *pdev,
}

/* Allocate network device */
- dev = alloc_orinocodev(sizeof(*card), orinoco_pci_cor_reset);
+ dev = alloc_orinocodev(sizeof(*card), &pdev->dev,
+ orinoco_pci_cor_reset, NULL);
if (!dev) {
printk(KERN_ERR PFX "Cannot allocate network device\n");
err = -ENOMEM;
diff --git a/drivers/net/wireless/orinoco_plx.c b/drivers/net/wireless/orinoco_plx.c
index 98fe165..ef76185 100644
--- a/drivers/net/wireless/orinoco_plx.c
+++ b/drivers/net/wireless/orinoco_plx.c
@@ -221,7 +221,8 @@ static int orinoco_plx_init_one(struct pci_dev *pdev,
}

/* Allocate network device */
- dev = alloc_orinocodev(sizeof(*card), orinoco_plx_cor_reset);
+ dev = alloc_orinocodev(sizeof(*card), &pdev->dev,
+ orinoco_plx_cor_reset, NULL);
if (!dev) {
printk(KERN_ERR PFX "Cannot allocate network device\n");
err = -ENOMEM;
diff --git a/drivers/net/wireless/orinoco_tmd.c b/drivers/net/wireless/orinoco_tmd.c
index df49318..ede24ec 100644
--- a/drivers/net/wireless/orinoco_tmd.c
+++ b/drivers/net/wireless/orinoco_tmd.c
@@ -124,7 +124,8 @@ static int orinoco_tmd_init_one(struct pci_dev *pdev,
}

/* Allocate network device */
- dev = alloc_orinocodev(sizeof(*card), orinoco_tmd_cor_reset);
+ dev = alloc_orinocodev(sizeof(*card), &pdev->dev,
+ orinoco_tmd_cor_reset, NULL);
if (!dev) {
printk(KERN_ERR PFX "Cannot allocate network device\n");
err = -ENOMEM;
diff --git a/drivers/net/wireless/spectrum_cs.c b/drivers/net/wireless/spectrum_cs.c
index 2fb0018..e368759 100644
--- a/drivers/net/wireless/spectrum_cs.c
+++ b/drivers/net/wireless/spectrum_cs.c
@@ -25,7 +25,6 @@
#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>
@@ -33,10 +32,6 @@
#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";

/********************************************************************/
/* Module stuff */
@@ -72,26 +67,11 @@ struct orinoco_pccard {
static int spectrum_cs_config(struct pcmcia_device *link);
static void spectrum_cs_release(struct pcmcia_device *link);

-/********************************************************************/
-/* Firmware downloader */
-/********************************************************************/
-
-/* 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)
-
/* Constants for the CISREG_CCSR register */
#define HCR_RUN 0x07 /* run firmware after reset */
#define HCR_IDLE 0x0E /* don't run firmware after reset */
#define HCR_MEM16 0x10 /* memory width bit, should be preserved */

-/* End markers */
-#define TEXT_END 0x1A /* End of text header */
-

#define CS_CHECK(fn, ret) \
do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
@@ -158,142 +138,29 @@ spectrum_reset(struct pcmcia_device *link, int idle)
return -ENODEV;
}

+/********************************************************************/
+/* Device methods */
+/********************************************************************/

-/*
- * 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.
- */
static int
-spectrum_dl_image(hermes_t *hw, struct pcmcia_device *link,
- const unsigned char *image, const unsigned char *end,
- int secondary)
+spectrum_cs_hard_reset(struct orinoco_private *priv)
{
- int ret;
- const unsigned char *ptr;
- const unsigned char *first_block;
-
- /* Plug Data Area (PDA) */
- __le16 pda[PDA_WORDS];
-
- /* Binary block begins after the 0x1A marker */
- ptr = image;
- while (*ptr++ != TEXT_END);
- first_block = ptr;
-
- /* Read the PDA from EEPROM */
- if (secondary) {
- ret = hermes_read_pda(hw, pda, PDA_ADDR, sizeof(pda), 1);
- if (ret)
- return ret;
- }
-
- /* Stop the firmware, so that it can be safely rewritten */
- ret = spectrum_reset(link, 1);
- if (ret)
- return ret;
-
- /* Program the adapter with new firmware */
- ret = hermes_program(hw, first_block, end);
- if (ret)
- return ret;
-
- /* Write the PDA to the adapter */
- if (secondary) {
- size_t len = hermes_blocks_length(first_block);
- ptr = first_block + len;
- ret = hermes_apply_pda(hw, ptr, pda);
- if (ret)
- return ret;
- }
-
- /* Run the firmware */
- ret = spectrum_reset(link, 0);
- if (ret)
- return ret;
-
- /* Reset hermes chip and make sure it responds */
- ret = hermes_init(hw);
-
- /* hermes_reset() should return 0 with the secondary firmware */
- if (secondary && ret != 0)
- return -ENODEV;
+ struct orinoco_pccard *card = priv->card;
+ struct pcmcia_device *link = card->p_dev;

- /* And this should work with any firmware */
- if (!hermes_present(hw))
- return -ENODEV;
+ /* Soft reset using COR and HCR */
+ spectrum_reset(link, 0);

return 0;
}

-
-/*
- * 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
-spectrum_dl_firmware(hermes_t *hw, struct pcmcia_device *link)
-{
- int ret;
- const struct firmware *fw_entry;
-
- if (request_firmware(&fw_entry, primary_fw_name,
- &handle_to_dev(link)) != 0) {
- printk(KERN_ERR PFX "Cannot find firmware: %s\n",
- primary_fw_name);
- return -ENOENT;
- }
-
- /* Load primary firmware */
- 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");
- return ret;
- }
-
- if (request_firmware(&fw_entry, secondary_fw_name,
- &handle_to_dev(link)) != 0) {
- printk(KERN_ERR PFX "Cannot find firmware: %s\n",
- secondary_fw_name);
- return -ENOENT;
- }
-
- /* Load secondary firmware */
- 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");
- }
-
- return ret;
-}
-
-/********************************************************************/
-/* Device methods */
-/********************************************************************/
-
-static int
-spectrum_cs_hard_reset(struct orinoco_private *priv)
+spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle)
{
struct orinoco_pccard *card = priv->card;
struct pcmcia_device *link = card->p_dev;
- int err;

- if (!hermes_present(&priv->hw)) {
- /* The firmware needs to be reloaded */
- if (spectrum_dl_firmware(&priv->hw, link) != 0) {
- printk(KERN_ERR PFX "Firmware download failed\n");
- err = -ENODEV;
- }
- } else {
- /* Soft reset using COR and HCR */
- spectrum_reset(link, 0);
- }
-
- return 0;
+ return spectrum_reset(link, idle);
}

/********************************************************************/
@@ -315,7 +182,9 @@ spectrum_cs_probe(struct pcmcia_device *link)
struct orinoco_private *priv;
struct orinoco_pccard *card;

- dev = alloc_orinocodev(sizeof(*card), spectrum_cs_hard_reset);
+ dev = alloc_orinocodev(sizeof(*card), &handle_to_dev(link),
+ spectrum_cs_hard_reset,
+ spectrum_cs_stop_firmware);
if (! dev)
return -ENOMEM;
priv = netdev_priv(dev);
@@ -517,7 +386,7 @@ spectrum_cs_config(struct pcmcia_device *link)
dev->irq = link->irq.AssignedIRQ;
card->node.major = card->node.minor = 0;

- /* Reset card and download firmware */
+ /* Reset card */
if (spectrum_cs_hard_reset(priv) != 0) {
goto failed;
}
--
1.5.4.5


2008-08-02 10:16:00

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 17/19] orinoco: Send association events to userspace

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

diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 7054c80..300920e 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -1443,6 +1443,66 @@ static void orinoco_send_bssid_wevent(struct orinoco_private *priv)
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
}

+static void orinoco_send_assocreqie_wevent(struct orinoco_private *priv)
+{
+ struct net_device *dev = priv->ndev;
+ struct hermes *hw = &priv->hw;
+ union iwreq_data wrqu;
+ int err;
+ u8 buf[88];
+ u8 *ie;
+
+ if (!priv->has_wpa)
+ return;
+
+ err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENT_ASSOC_REQ_INFO,
+ sizeof(buf), NULL, &buf);
+ if (err != 0)
+ return;
+
+ ie = orinoco_get_wpa_ie(buf, sizeof(buf));
+ if (ie) {
+ int rem = sizeof(buf) - (ie - &buf[0]);
+ wrqu.data.length = ie[1] + 2;
+ if (wrqu.data.length > rem)
+ wrqu.data.length = rem;
+
+ if (wrqu.data.length)
+ /* Send event to user space */
+ wireless_send_event(dev, IWEVASSOCREQIE, &wrqu, ie);
+ }
+}
+
+static void orinoco_send_assocrespie_wevent(struct orinoco_private *priv)
+{
+ struct net_device *dev = priv->ndev;
+ struct hermes *hw = &priv->hw;
+ union iwreq_data wrqu;
+ int err;
+ u8 buf[88]; /* TODO: verify max size or IW_GENERIC_IE_MAX */
+ u8 *ie;
+
+ if (!priv->has_wpa)
+ return;
+
+ err = hermes_read_ltv(hw, IRQ_BAP, HERMES_RID_CURRENT_ASSOC_RESP_INFO,
+ sizeof(buf), NULL, &buf);
+ if (err != 0)
+ return;
+
+ ie = orinoco_get_wpa_ie(buf, sizeof(buf));
+ if (ie) {
+ int rem = sizeof(buf) - (ie - &buf[0]);
+ wrqu.data.length = ie[1] + 2;
+ if (wrqu.data.length > rem)
+ wrqu.data.length = rem;
+
+ if (wrqu.data.length)
+ /* Send event to user space */
+ wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, ie);
+ }
+}
+
static void orinoco_send_wevents(struct work_struct *work)
{
struct orinoco_private *priv =
@@ -1452,6 +1512,8 @@ static void orinoco_send_wevents(struct work_struct *work)
if (orinoco_lock(priv, &flags) != 0)
return;

+ orinoco_send_assocreqie_wevent(priv);
+ orinoco_send_assocrespie_wevent(priv);
orinoco_send_bssid_wevent(priv);

orinoco_unlock(priv, &flags);
--
1.5.4.5


2008-08-05 23:53:51

by Dave Kilroy

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

Pavel Roskin wrote:
> On Tue, 2008-08-05 at 00:09 +0100, Dave wrote:
>> Actually I meant the DECLARE_DEFAULT_PDA macro in hermes_dld.c:
^^ DEFINE obviously.

>> dkilroy@borken /usr/src/wireless-testing $ git diff HEAD~20 | scripts/checkpatch.pl -
>> ERROR: Macros with multiple statements should be enclosed in a do - while loop
>> #933: FILE: drivers/net/wireless/hermes_dld.c:570:
>> +#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 \
>> +}
>
> I suggest that you take the structure definition outside the macro and
> give it a reasonable name.

The reason for defining it in this manner is to ensure the data is consistent. For those that are interested, this code is at the end of patch 08/19.

It allows (using pid 0x0005 and dummy data as an example throughout):

DEFINE_DEFAULT_PDR(0x0005, 7, "\x00\x01\x02\x03\x04\x05\x06");

instead of:

#define DEFINE_PDR(pid, len, data) \
__constant_cpu_to_le16((len/ \
sizeof(__le16)) - 1), \
__constant_cpu_to_le16(pid), \
data

struct {
__le16 len;
__le16 id;
u8 val[7];
} __attribute__ ((packed)) named_pdr = {
DEFINE_PDR(0x0005, 7, "\x00\x01\x02\x03\x04\x05\x06")
};

or

#define DECLARE_PDA(len)
struct { \
__le16 len; \
__le16 id; \
u8 val[7]; \
} __attribute__ ((packed))

DECLARE_PDA(7) named_pdr = {
DEFINE_PDR(0x0005, 7, "\x00\x01\x02\x03\x04\x05\x06")
};

There are 5 or 6 different structures declared in this manner, one for each pid. In one case the data element is 256 bytes. I think that the style in the patch is easier on the eye, and less likely to become inconsistent over time. It would be even better if I could omit the length and just provide pid and data, but I can't see how to do that.

The user only needs to know the variable name if trying to find it in a debugger or map file, since there is a

#define DEFAULT_PDR(pid) default_pdr_data_##pid

The mapping from pid to functionality is then entirely restricted to where the definition occurs. The switch statement in hermes_apply_pdr_with_defaults is a straight pid to pid mapping, with comments just to keep track. If desired I can add

#define HWIF_COMPAT 0x0005

to use in place of wherever I've used 0x0005. The variable will end up being called default_pdr_data_HWIF_COMPAT, and everything else is unchanged.


Please provide an example of how you think the above could be done better - I've tried several things, and I still think that what is in the patch is the neatest.


Regards,

Dave.

2008-08-02 10:15:08

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 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 aa95349..13072d0 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.4.5


2008-08-07 20:50:29

by Dan Williams

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

On Thu, 2008-08-07 at 21:17 +0100, Dave wrote:
> Dan Williams wrote:
> > On Thu, 2008-08-07 at 19:43 +0100, Dave wrote:
> >> That said, what's wrong with the ap_scan=2 mode? You've stated it's
> >> not great (and I'm prepared to believe it), but what is the actual problem?
>
> <snip explanation>
>
> Thanks for that.
>
> >> I had a reread of <http://hostap.epitest.fi/wpa_supplicant/devel/driver_wrapper.html>.
> >>
> >> As a FullMAC driver without support for selecting the BSSID to associate
> >> with (or frequency in Managed mode), the only way I can see to make the
> >> driver work with ap_scan=1 is to silently ignore the SIOCSIWFREQ and
> >> SIOCSIWAP. I don't think this is an approach I would want to pursue.
> >
> > Well, the current orinoco driver returns "success" (0) for SIOCSIWAP on
> > Agere firmware.
>
> borken dkilroy # dmesg | grep eth1
> eth1: Hardware identity 0001:0001:0004:0002
> eth1: Station identity 001f:0001:0008:0048
> eth1: Firmware determined as Lucent/Agere 8.72
> eth1: Attempting to download firmware agere_sta_fw.bin
> <snip fw download>
> eth1: Hardware identity 0001:0001:0004:0002
> eth1: Station identity 001f:0002:0009:0030
> eth1: Firmware determined as Lucent/Agere 9.48
> <snip capabilities>
> eth1: ready
> eth1: orinoco_cs at 0.0, irq 11, io 0x0100-0x013f
> eth1: New link status: Connected (0001)
>
> borken dkilroy # iwlist eth1 scan essid MONTY
> eth1 Scan completed :
> Cell 01 - Address: 00:18:4D:06:45:76
> ESSID:"MONTY"
> Mode:Master
> Channel:5
> <snip detail>
>
> borken dkilroy # iwconfig eth1 ap 00:18:4d:06:45:76
> Error for wireless request "Set AP Address" (8B14) :
> SET failed on device eth1 ; Operation not supported.
>
> borken dkilroy # iwconfig eth1 ap off
> borken dkilroy #
>
>
> The above is running with my patchset, but I haven't touched the SIOCSIWAP handler. Honest.

No, you're right. I was looking at __orinoco_hw_set_wap() instead.
Thought required on my part about how to handle this then.

Dan


2008-08-05 21:50:16

by Dave Kilroy

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

Pavel Roskin wrote:
> On Mon, 2008-08-04 at 11:34 -0400, Dan Williams wrote:
>
>> Maybe the wireless-tools breakage you're experiencing is causing this to
>> fail on x86-64? Are you using 64-bit wireless-tools and a 64-bit
>> kernel, or 32-bit wireless tools with a 64-bit kernel?
>
> I'm using 64-bit kernel with 64-bit userspace.

I'm unsure as how to handle the 64 bit issues that you're seeing. Unless Jean gets back with a suggestion as to how to modify the code, I don't propose to address this. I am curious as to why this would only be affecting the orinoco driver though.

> If I use 32-bit kernel with 32-bit userspace, essid filtering works, but
> only to a degree. Old scan results are cached somewhere, so if I scan
> with essid and then without essid, I get filtered results. Likewise, If
> I don't use filtering the first time, but use it the second time, I get
> unfiltered results.

I know what you're talking about, but I don't think the description above gets it across very well. To be clear, the behaviour of the current patch (with each command run within 10s of each other):

iwlist scan eth0 essid hiddenAP
<results for hiddenAP>

iwlist scan eth0
<old results for hiddenAP>
<results for bcastAP>

iwlist scan eth0 essid hiddenAP
<results for hiddenAP>
<old results for bcastAP>

If you wait 10s between calls the cache should get cleared, and you don't see the old results.

This is the case because:
a) It doesn't require storing the state of the previous request in the driver
b) It doesn't require handling the case of simultaneous calls to iwlist from multiple processes.
c) I'm not clear on what the desired output from the API/iwlist is, especially how it should interact with the caching logic Dan implemented in the driver.

For my purposes the above is sufficient because when I want to check a hiddenAP I get the results for it. I also don't have a problem with the hidden/broadcast AP results leaking because I'm only ever inspecting the results visually.

If the desired effect is that iwlist scan essid only returns results for the specific essid, and the non essid version does not return results for hidden APs; then I believe the driver must only accept a single scan request at a time, and -EBUSY further requests until it has received results AND passed them to the calling process. It should also track what the user asked for last time and clear the cache if it is not the same as before.

I don't think this prevents other userspace processes from catching the SIOGIWCSCAN and picking up the results intended for another process anyway.

The alternative is to ditch the caching.

> I would prefer that we don't merge unreliable functionality. It's not
> needed for WPA support. And it don't look like and improvement in its
> present form.

Agreed, this patch is not required for WPA, though I think it is an improvement.


Regards,

Dave.

2008-08-05 22:22:46

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 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]>
---

The min(pda_len, sizeof()) Pavel pointed out highlighted that pda_addr
and pda_len were int, which they really shouldn't be. This patch is
modified to take u32 and u16 for pda_addr and pda_len respectively in
hermes_read_pda.

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.4.5


2008-08-05 21:15:47

by Pavel Roskin

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

On Mon, 2008-08-04 at 11:34 -0400, Dan Williams wrote:

> Maybe the wireless-tools breakage you're experiencing is causing this to
> fail on x86-64? Are you using 64-bit wireless-tools and a 64-bit
> kernel, or 32-bit wireless tools with a 64-bit kernel?

I'm using 64-bit kernel with 64-bit userspace.

If I use 32-bit kernel with 32-bit userspace, essid filtering works, but
only to a degree. Old scan results are cached somewhere, so if I scan
with essid and then without essid, I get filtered results. Likewise, If
I don't use filtering the first time, but use it the second time, I get
unfiltered results.

I cannot imagine how this bug could have evaded even most basic testing.

I would prefer that we don't merge unreliable functionality. It's not
needed for WPA support. And it don't look like and improvement in its
present form.

--
Regards,
Pavel Roskin

2008-08-02 10:16:05

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 18/19] orinoco: Process bulk of receive interrupt in a tasklet

Read the packet data off the hardware and straight into an skb in the
interrupt. We have to do this in case we don't process the tasklet in
time.

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

diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 300920e..5d1955f 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -1178,15 +1178,23 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
struct net_device_stats *stats = &priv->stats;
struct iw_statistics *wstats = &priv->wstats;
struct sk_buff *skb = NULL;
- u16 rxfid, status, fc;
+ u16 rxfid, status;
int length;
- struct hermes_rx_descriptor desc;
- struct ethhdr *hdr;
+ struct hermes_rx_descriptor *desc;
+ struct orinoco_rx_data *rx_data;
int err;

+ desc = kmalloc(sizeof(*desc), GFP_ATOMIC);
+ if (!desc) {
+ printk(KERN_WARNING
+ "%s: Can't allocate space for RX descriptor\n",
+ dev->name);
+ goto update_stats;
+ }
+
rxfid = hermes_read_regn(hw, RXFID);

- err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc),
+ err = hermes_bap_pread(hw, IRQ_BAP, desc, sizeof(*desc),
rxfid, 0);
if (err) {
printk(KERN_ERR "%s: error %d reading Rx descriptor. "
@@ -1194,7 +1202,7 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
goto update_stats;
}

- status = le16_to_cpu(desc.status);
+ status = le16_to_cpu(desc->status);

if (status & HERMES_RXSTAT_BADCRC) {
DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n",
@@ -1205,8 +1213,8 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)

/* Handle frames in monitor mode */
if (priv->iw_mode == IW_MODE_MONITOR) {
- orinoco_rx_monitor(dev, rxfid, &desc);
- return;
+ orinoco_rx_monitor(dev, rxfid, desc);
+ goto out;
}

if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
@@ -1216,15 +1224,14 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
goto update_stats;
}

- length = le16_to_cpu(desc.data_len);
- fc = le16_to_cpu(desc.frame_ctl);
+ length = le16_to_cpu(desc->data_len);

/* Sanity checks */
if (length < 3) { /* No for even an 802.2 LLC header */
/* At least on Symbol firmware with PCF we get quite a
lot of these legitimately - Poll frames with no
data. */
- return;
+ goto out;
}
if (length > IEEE80211_DATA_LEN) {
printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
@@ -1259,6 +1266,43 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
goto drop;
}

+ /* Add desc and skb to rx queue */
+ rx_data = kzalloc(sizeof(*rx_data), GFP_ATOMIC);
+ if (!rx_data) {
+ printk(KERN_WARNING "%s: Can't allocate RX packet\n",
+ dev->name);
+ goto drop;
+ }
+ rx_data->desc = desc;
+ rx_data->skb = skb;
+ list_add_tail(&rx_data->list, &priv->rx_list);
+ tasklet_schedule(&priv->rx_tasklet);
+
+ return;
+
+drop:
+ dev_kfree_skb_irq(skb);
+update_stats:
+ stats->rx_errors++;
+ stats->rx_dropped++;
+out:
+ kfree(desc);
+}
+
+static void orinoco_rx(struct net_device *dev,
+ struct hermes_rx_descriptor *desc,
+ struct sk_buff *skb)
+{
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &priv->stats;
+ u16 status, fc;
+ int length;
+ struct ethhdr *hdr;
+
+ status = le16_to_cpu(desc->status);
+ length = le16_to_cpu(desc->data_len);
+ fc = le16_to_cpu(desc->frame_ctl);
+
/* Handle decapsulation
* In most cases, the firmware tell us about SNAP frames.
* For some reason, the SNAP frames sent by LinkSys APs
@@ -1277,11 +1321,11 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN);
hdr->h_proto = htons(length);
}
- memcpy(hdr->h_dest, desc.addr1, ETH_ALEN);
+ memcpy(hdr->h_dest, desc->addr1, ETH_ALEN);
if (fc & IEEE80211_FCTL_FROMDS)
- memcpy(hdr->h_source, desc.addr3, ETH_ALEN);
+ memcpy(hdr->h_source, desc->addr3, ETH_ALEN);
else
- memcpy(hdr->h_source, desc.addr2, ETH_ALEN);
+ memcpy(hdr->h_source, desc->addr2, ETH_ALEN);

dev->last_rx = jiffies;
skb->protocol = eth_type_trans(skb, dev);
@@ -1290,7 +1334,7 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
skb->pkt_type = PACKET_OTHERHOST;

/* Process the wireless stats if needed */
- orinoco_stat_gather(dev, skb, &desc);
+ orinoco_stat_gather(dev, skb, desc);

/* Pass the packet to the networking stack */
netif_rx(skb);
@@ -1298,12 +1342,27 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
stats->rx_bytes += length;

return;
+}

- drop:
- dev_kfree_skb_irq(skb);
- update_stats:
- stats->rx_errors++;
- stats->rx_dropped++;
+static void orinoco_rx_isr_tasklet(unsigned long data)
+{
+ struct net_device *dev = (struct net_device *) data;
+ struct orinoco_private *priv = netdev_priv(dev);
+ struct orinoco_rx_data *rx_data, *temp;
+ struct hermes_rx_descriptor *desc;
+ struct sk_buff *skb;
+
+ /* extract desc and skb from queue */
+ list_for_each_entry_safe(rx_data, temp, &priv->rx_list, list) {
+ desc = rx_data->desc;
+ skb = rx_data->skb;
+ list_del(&rx_data->list);
+ kfree(rx_data);
+
+ orinoco_rx(dev, desc, skb);
+
+ kfree(desc);
+ }
}

/********************************************************************/
@@ -3255,6 +3314,10 @@ struct net_device
INIT_WORK(&priv->join_work, orinoco_join_ap);
INIT_WORK(&priv->wevent_work, orinoco_send_wevents);

+ INIT_LIST_HEAD(&priv->rx_list);
+ tasklet_init(&priv->rx_tasklet, orinoco_rx_isr_tasklet,
+ (unsigned long) dev);
+
netif_carrier_off(dev);
priv->last_linkstatus = 0xffff;

@@ -3265,6 +3328,10 @@ void free_orinocodev(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);

+ /* No need to empty priv->rx_list: if the tasklet is scheduled
+ * when we call tasklet_kill it will run one final time,
+ * emptying the list */
+ tasklet_kill(&priv->rx_tasklet);
priv->wpa_ie_len = 0;
kfree(priv->wpa_ie);
orinoco_bss_data_free(priv);
diff --git a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
index bfab88f..e0c9be3 100644
--- a/drivers/net/wireless/orinoco.h
+++ b/drivers/net/wireless/orinoco.h
@@ -9,6 +9,7 @@

#define DRIVER_VERSION "0.15"

+#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <net/iw_handler.h>
@@ -57,6 +58,14 @@ struct xbss_element {
struct list_head list;
};

+struct hermes_rx_descriptor;
+
+struct orinoco_rx_data {
+ struct hermes_rx_descriptor *desc;
+ struct sk_buff *skb;
+ struct list_head list;
+};
+
struct orinoco_private {
void *card; /* Pointer to card dependent structure */
struct device *dev;
@@ -68,6 +77,11 @@ struct orinoco_private {
int hw_unavailable;
struct work_struct reset_work;

+ /* Interrupt tasklets */
+ struct tasklet_struct rx_tasklet;
+ struct list_head rx_list;
+ struct orinoco_rx_data *rx_data;
+
/* driver state */
int open;
u16 last_linkstatus;
--
1.5.4.5


2008-08-08 14:50:38

by Dan Williams

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

On Thu, 2008-08-07 at 22:08 +0100, Dave wrote:
> Dan Williams wrote:
> > On Thu, 2008-08-07 at 19:43 +0100, Dave wrote:
> >> That said, what's wrong with the ap_scan=2 mode? You've stated it's not
> >> great (and I'm prepared to believe it), but what is the actual problem?
>
> > But the problem with ap_scan=2 is really about the failure window.
> > ap_scan=2 basically dumps a load of options on the driver, and unless
> > the options _exactly_ match the configuration of the AP, you won't
> > connect. The supplicant isn't able to make intelligent choices about
> > which networks in its config file match the scan result, thus there's a
> > lot more potential for failure unless you know exactly what your network
> > is set up to do, and these capabilities aren't always exposed through
> > beacons. So ap_scan=2 just opens up a huge window of failure and stuff
> > can't ever Just Work because no intelligence can be applied.
>
> Wouldn't a different mode of operation in wpa_supplicant solve this (see below)? Then get rid of the mode selection via wpa_supplicant.conf by selecting mode based on drivers error responses (and/or reported capabilities).

Maybe; as long as the modes are not actually exposed or configurable in
any way. However, since drivers are so horribly inconsistent and since
WEXT doesn't promote consistency, I tend to think having automatically
chosen modes like this would cause more problems than we have now...

> ap_scan=3:
> * wpa_supplicant requests scan with SIOCSIWSCAN
> * driver reports scan complete with wireless event SIOCGIWSCAN
> * wpa_supplicant reads scan results with SIOCGIWSCAN (multiple call if a larget buffer is needed)
> * wpa_supplicant decides which SSID to use based on scan results
> * wpa_supplicant configures driver to associate with an SSID (SIOCSIWMODE, SIOCSIWGENIE, SIOCSIWAUTH, SIOCSIWESSID)
>
> SIOCSIWSCAN not supported? => ap_scan=2

If a driver doesn't support SIOCSIWSCAN it'll get a big fat NAK for
kernel inclusion. Any driver that doesn't support scan shouldn't be
used.

I assume you mean "specific SSID scanning" though, right? That's
basically what NM does right now; if the driver doesn't support SSID
scan capabilities, NM requests that the supplicant use ap_scan=2.

Basically, I don't think any of this is really a problem any more. The
supplicant should probably ignore a result of EOPNOTSUPP when calling
SIOCSIWAP during the association run. ap_scan certainly shouldn't be
overloaded more than it is now. I don't think the supplicant locking in
the BSSID and controlling roaming in the supplicant based on BSSID is
all that useful since the firmware or stack usually does what it wants
anyway. Besides, it's sort of insane to specify 10 different network
blocks with a common SSID but different everything else. That's an
indication of a horribly broken network for starters, and secondly you
simply shouldn't need to lock multiple network blocks with a common SSID
down to different BSSIDs in the first place really.

If we really want to handle roaming in the supplicant, we shouldn't be
overloading ap_scan any more. We should be thinking about this from the
ground up and not trying to shove a round peg into a square hole.
ap_scan just isn't suitable for actually handling roaming.

Dan


2008-08-02 10:22:14

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 15/19] orinoco: Use a macro to define wireless handlers

The macro identifiers for the various ioctls required for WPA support
are longer than those currently used by the driver. This makes it messy
to keep line length below 80 character.

By defining a macro to initialise the handler table, we recover the
common text.

Signed-off-by: David Kilroy <[email protected]>
---
Just fixing the signed-off-by.

drivers/net/wireless/orinoco.c | 66 ++++++++++++++++++++-------------------
1 files changed, 34 insertions(+), 32 deletions(-)

diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index b91b6cb..59720f9 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -5050,39 +5050,41 @@ static const struct iw_priv_args orinoco_privtab[] = {
* Structures to export the Wireless Handlers
*/

+#define STD_IW_HANDLER(id, func) \
+ [IW_IOCTL_IDX(id)] = (iw_handler) func
static const iw_handler orinoco_handler[] = {
- [SIOCSIWCOMMIT-SIOCIWFIRST] = (iw_handler) orinoco_ioctl_commit,
- [SIOCGIWNAME -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getname,
- [SIOCSIWFREQ -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setfreq,
- [SIOCGIWFREQ -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getfreq,
- [SIOCSIWMODE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setmode,
- [SIOCGIWMODE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getmode,
- [SIOCSIWSENS -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setsens,
- [SIOCGIWSENS -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getsens,
- [SIOCGIWRANGE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getiwrange,
- [SIOCSIWSPY -SIOCIWFIRST] = (iw_handler) iw_handler_set_spy,
- [SIOCGIWSPY -SIOCIWFIRST] = (iw_handler) iw_handler_get_spy,
- [SIOCSIWTHRSPY-SIOCIWFIRST] = (iw_handler) iw_handler_set_thrspy,
- [SIOCGIWTHRSPY-SIOCIWFIRST] = (iw_handler) iw_handler_get_thrspy,
- [SIOCSIWAP -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setwap,
- [SIOCGIWAP -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getwap,
- [SIOCSIWSCAN -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setscan,
- [SIOCGIWSCAN -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getscan,
- [SIOCSIWESSID -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setessid,
- [SIOCGIWESSID -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getessid,
- [SIOCSIWNICKN -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setnick,
- [SIOCGIWNICKN -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getnick,
- [SIOCSIWRATE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setrate,
- [SIOCGIWRATE -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getrate,
- [SIOCSIWRTS -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setrts,
- [SIOCGIWRTS -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getrts,
- [SIOCSIWFRAG -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setfrag,
- [SIOCGIWFRAG -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getfrag,
- [SIOCGIWRETRY -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getretry,
- [SIOCSIWENCODE-SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setiwencode,
- [SIOCGIWENCODE-SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getiwencode,
- [SIOCSIWPOWER -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_setpower,
- [SIOCGIWPOWER -SIOCIWFIRST] = (iw_handler) orinoco_ioctl_getpower,
+ STD_IW_HANDLER(SIOCSIWCOMMIT, orinoco_ioctl_commit),
+ STD_IW_HANDLER(SIOCGIWNAME, orinoco_ioctl_getname),
+ STD_IW_HANDLER(SIOCSIWFREQ, orinoco_ioctl_setfreq),
+ STD_IW_HANDLER(SIOCGIWFREQ, orinoco_ioctl_getfreq),
+ STD_IW_HANDLER(SIOCSIWMODE, orinoco_ioctl_setmode),
+ STD_IW_HANDLER(SIOCGIWMODE, orinoco_ioctl_getmode),
+ STD_IW_HANDLER(SIOCSIWSENS, orinoco_ioctl_setsens),
+ STD_IW_HANDLER(SIOCGIWSENS, orinoco_ioctl_getsens),
+ STD_IW_HANDLER(SIOCGIWRANGE, orinoco_ioctl_getiwrange),
+ STD_IW_HANDLER(SIOCSIWSPY, iw_handler_set_spy),
+ STD_IW_HANDLER(SIOCGIWSPY, iw_handler_get_spy),
+ STD_IW_HANDLER(SIOCSIWTHRSPY, iw_handler_set_thrspy),
+ STD_IW_HANDLER(SIOCGIWTHRSPY, iw_handler_get_thrspy),
+ STD_IW_HANDLER(SIOCSIWAP, orinoco_ioctl_setwap),
+ STD_IW_HANDLER(SIOCGIWAP, orinoco_ioctl_getwap),
+ STD_IW_HANDLER(SIOCSIWSCAN, orinoco_ioctl_setscan),
+ STD_IW_HANDLER(SIOCGIWSCAN, orinoco_ioctl_getscan),
+ STD_IW_HANDLER(SIOCSIWESSID, orinoco_ioctl_setessid),
+ STD_IW_HANDLER(SIOCGIWESSID, orinoco_ioctl_getessid),
+ STD_IW_HANDLER(SIOCSIWNICKN, orinoco_ioctl_setnick),
+ STD_IW_HANDLER(SIOCGIWNICKN, orinoco_ioctl_getnick),
+ STD_IW_HANDLER(SIOCSIWRATE, orinoco_ioctl_setrate),
+ STD_IW_HANDLER(SIOCGIWRATE, orinoco_ioctl_getrate),
+ STD_IW_HANDLER(SIOCSIWRTS, orinoco_ioctl_setrts),
+ STD_IW_HANDLER(SIOCGIWRTS, orinoco_ioctl_getrts),
+ STD_IW_HANDLER(SIOCSIWFRAG, orinoco_ioctl_setfrag),
+ STD_IW_HANDLER(SIOCGIWFRAG, orinoco_ioctl_getfrag),
+ STD_IW_HANDLER(SIOCGIWRETRY, orinoco_ioctl_getretry),
+ STD_IW_HANDLER(SIOCSIWENCODE, orinoco_ioctl_setiwencode),
+ STD_IW_HANDLER(SIOCGIWENCODE, orinoco_ioctl_getiwencode),
+ STD_IW_HANDLER(SIOCSIWPOWER, orinoco_ioctl_setpower),
+ STD_IW_HANDLER(SIOCGIWPOWER, orinoco_ioctl_getpower),
};


--
1.5.4.5


2008-08-06 00:41:06

by Pavel Roskin

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

On Wed, 2008-08-06 at 00:46 +0100, Dave wrote:
> >> +#define DEFINE_DEFAULT_PDR(pid, length, data) \
> >> +static const struct { \
> >> + __le16 len; \
> >> + __le16 id; \
> >> + u8 val[length]; \

I see, the length is variable.

> The reason for defining it in this manner is to ensure the data is
> consistent. For those that are interested, this code is at the end of
> patch 08/19.
>
> It allows (using pid 0x0005 and dummy data as an example throughout):
>
> DEFINE_DEFAULT_PDR(0x0005, 7, "\x00\x01\x02\x03\x04\x05\x06");
...
> Please provide an example of how you think the above could be done
> better - I've tried several things, and I still think that what is in
> the patch is the neatest.

OK, it's a good idea.

--
Regards,
Pavel Roskin

2008-08-04 23:26:57

by Dan Williams

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

On Tue, 2008-08-05 at 00:09 +0100, Dave wrote:
> Pavel Roskin wrote:
> >> It is almost checkpatch
> >> clean - the single warning looks like a false positive to me.
> >
> > If you mean orinoco_ioctl_getnick(), that's a false positive.
>
> Actually I meant the DECLARE_DEFAULT_PDA macro in hermes_dld.c:
>
> dkilroy@borken /usr/src/wireless-testing $ git diff HEAD~20 | scripts/checkpatch.pl -
> ERROR: Macros with multiple statements should be enclosed in a do - while loop
> #933: FILE: drivers/net/wireless/hermes_dld.c:570:
> +#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 \
> +}
>
> > However,
> > sparse finds two issues on x86_64, both due to sizeof() returning
> > size_t, which is wider than int.
> >
> > Here's the fix. Please integrate it into the patches that introduce the
> > code.
>
> Will do. I obviously need to upgrade my version of sparse, since it isn't complaining 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>
> > I tested it on my WPA2 AP and could not associate:
>
> I'm not familiar with the difference between WPA/WPA2. Is that expected to work?

Depends; if it's WPA2/RSN + AES-CCMP, then most likely not, because I'm
pretty sure the cards won't have onboard AES crypto acceleration
hardware. Most cards built before WPA2/RSN was standardized don't have
the horsepower to do AES in firmware purely in software either.

But you might be able to get away with WPA2/RSN + TKIP if the AP allows
this configuration. In that configuration, the only difference between
WPA and WPA2/RSN would be the information element IDs, really. But if
the firmware itself doesn't say it supports WPA on whatever website it
came from, then likely the card won't do WPA2/RSN either.

In fact, you already did the right thing by _NOT_ adding
IW_ENC_CAPA_WPA2 and IW_ENC_CAPA_CIPHER_CCMP in the GIWRANGE handler.
Thus, you have not signaled that WPA2 is supported by the driver, and
thus Pavel should not have expected it to work in the first place :)

Dan

> > # wpa_supplicant -c /etc/wpa_supplicant/wpa_main.conf -D wext -i eth1
> > CTRL-EVENT-SCAN-RESULTS
> > Trying to associate with 00:19:5b:56:fc:73 (SSID='mike' freq=2437 MHz)
> > ioctl[SIOCSIWFREQ]: Device or resource busy
> > ioctl[SIOCSIWAP]: Operation not supported
> > Association request to the driver failed
>
> The 'Device or resource busy' definitely looks wrong. We only return -EBUSY here if the device was in infrastructure mode or during initialisation/reset.
>
> The SIOCSIWAP response is expected if you have wpa_supplicant configured with ap_scan=1 (I think). The driver isn't able to tell the firmware which bssid to associate with - instead after the SIOCSIWENCODEEXT the driver selects a bssid belonging to the specified ssid. Setting ap_scan=2 should avoid the SIOCSIWAP and it should work better.
>
> > And that's from the kernel log:
> >
> > [185185.284523] eth1: Hardware identity 0001:0004:0005:0000
> > [185185.284686] eth1: Station identity 001f:0002:0009:002a
> > [185185.284722] eth1: Firmware determined as Lucent/Agere 9.42
> > [185185.284758] eth1: Ad-hoc demo mode supported
> > [185185.284793] eth1: IEEE standard IBSS ad-hoc mode supported
> > [185185.284829] eth1: WEP supported, 104-bit key
> > [185185.284872] eth1: WPA-PSK supported
> > [185185.285010] eth1: MAC address 00:02:2d:8b:56:87
> > [185185.285153] eth1: Station name "HERMES I"
> > [185185.285811] eth1: ready
> > [185185.287298] eth1: orinoco_cs at 0.0, irq 18, io 0x1100-0x113f
> > [185213.396202] eth1: New link status: Connected (0001)
> > [185214.469964] eth1: Ext scan results too large (272 bytes). Truncating
> > results to 270 bytes.
> > [185214.575811] eth1: Lucent/Agere firmware doesn't support manual
> > roaming
> > [185219.617236] eth1: Ext scan results too large (272 bytes). Truncating
> > results to 270 bytes.
>
> The truncation of scan results in this case is expected and is due to the IEs being reported by your access point (previously I dropped the scan result completely). I'm hoping the last two bytes aren't significant. The results from my AP (and hopefully most others) are shorter and are not truncated.
>
> Regards,
>
> Dave.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html


2008-08-06 13:39:34

by Dan Williams

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

On Tue, 2008-08-05 at 18:48 -0400, Pavel Roskin wrote:
> On Tue, 2008-08-05 at 17:55 -0400, Dan Williams wrote:
> > On Tue, 2008-08-05 at 17:15 -0400, Pavel Roskin wrote:
>
> > > If I use 32-bit kernel with 32-bit userspace, essid filtering works, but
> > > only to a degree. Old scan results are cached somewhere, so if I scan
> > > with essid and then without essid, I get filtered results. Likewise, If
> > > I don't use filtering the first time, but use it the second time, I get
> > > unfiltered results.
> >
> > Scanning for a specific SSID never has "filtered" the results, nor
> > should it. It just probe-scans the requested SSID and return any new
> > results in with the cached ones. You requested an SSID scan, thus you
> > must know the SSID, thus you can do the filtering yourself?
>
> Perhaps I used a wrong word. If requesting a scan, I expect to get
> results from a scan with the parameters I supplied. If the scan results
> are from a scan with different parameters, I don't want them. I'd
> rather see the driver return EAGAIN than results of a scan with
> different parameters.

I'm not quite sure what you mean here. You mean to say that if you
request a specific SSID scan, and the driver does not find that SSID,
that it should return EAGAIN? I think that would be overloading EAGAIN
too much, since for scans EAGAIN means "I'm not done with your scan
request yet".

> The userspace is welcome to keep a pool of all APs found by any scans,
> but I don't think drivers should do it.

Drivers need to keep a reasonably complete list of APs internally for
association anyway. You don't want to have to do a full scan just to
associate if you have results from 10 seconds ago that are still valid.
Furthermore, drivers need to keep cached results so that two processes
can request results of a scan after the scan is complete. The previous
situation where the driver would clear the scan list whenever GIWSCAN
was called just sucked because then two processes would race for the
results after an IWSCAN event and one would get nothing.

A scan is a global result, not a result specific to the request. Thus,
since any process can read the results, and since only one process is
privy to the details of how the scan was performed with WEXT, you have
to return a complete list of results for each call to GIWSCAN.

Honestly, I don't think it's that much of a problem for the userspace
process that actually requested the scan to keep it's scan parameters
around and filter the results that come back using those parameters.

Dan


2008-08-08 00:02:42

by Dave Kilroy

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

Pavel Roskin wrote:
> I'm sorry, I'm really limited in time right now to do more tests, and
> I'm afraid I'll be mostly offline for the next 3 weeks.

Not a problem. Again thanks for testing. I'm just replying here for completeness.

> I could associate to a WPA1 AP with TKIP, but only on a 32-bit system.
> It still fails on the 64-bit system. wpa_supplicant prints this in a
> loop:
>
> RTM_NEWLINK: operstate=0 ifi_flags=0x11003 ([UP][LOWER_UP])
> RTM_NEWLINK, IFLA_IFNAME: Interface 'eth1' added
> RTM_NEWLINK: operstate=0 ifi_flags=0x11003 ([UP][LOWER_UP])
> Wireless event: cmd=0x8c07 len=40
> AssocReq IE wireless event - hexdump(len=24): dd 16 00 50 f2 01 01 00 00 50 f2 02 01 00 00 50 f2 02 01 00 00 50 f2 02
> RTM_NEWLINK: operstate=0 ifi_flags=0x11003 ([UP][LOWER_UP])
> Wireless event: cmd=0x8b15 len=24
> Wireless event: new AP: 00:0f:66:2f:ef:59
> Association info event
> req_ies - hexdump(len=24): dd 16 00 50 f2 01 01 00 00 50 f2 02 01 00 00 50 f2 02 01 00 00 50 f2 02
> WPA: set own WPA/RSN IE - hexdump(len=24): dd 16 00 50 f2 01 01 00 00 50 f2 02 01 00 00 50 f2 02 01 00 00 50 f2 02
> State: ASSOCIATED -> ASSOCIATED
> wpa_driver_wext_set_operstate: operstate 0->0 (DORMANT)
> WEXT: Operstate: linkmode=-1, operstate=5
> Associated with 00:0f:66:2f:ef:59
> WPA: Association event - clear replay counter
> WPA: Clear old PTK
> Setting authentication timeout: 10 sec 0 usec
> Cancelling scan request

This association looks like the usual aftermath of the SIOCSIWESSID. Comparing to what I see, wpa_supplicant should be moving on to identify the required network configuration, we should receive an RX EAPOL, and a 4WAY handshake should kick off.

I assume this problem is related to the 64 bit userspace/kernel issue that you're seeing, so wpa_supplicant somehow misidentifies the AP or its configuration.

> And the kernel log is:
>
> [ 7729.202964] eth1: New link status: Connected (0001)
> [ 7738.072382] eth1: New link status: Disconnected (0002)
> [ 7739.238773] eth1: New link status: Connected (0001)
> [ 7748.527988] eth1: New link status: Disconnected (0002)
> [ 7749.683346] eth1: New link status: Connected (0001)
> [ 7758.983565] eth1: New link status: Disconnected (0002)

That looks like the AP kicking us off after we miss the handshake.


Regards,

Dave.

2008-08-02 10:15:17

by Dave Kilroy

[permalink] [raw]
Subject: [PATCH 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 | 207 +++++++++++++++++++++++++++---------
drivers/net/wireless/hermes_dld.h | 22 ++--
drivers/net/wireless/spectrum_cs.c | 23 +++--
3 files changed, 181 insertions(+), 71 deletions(-)

diff --git a/drivers/net/wireless/hermes_dld.c b/drivers/net/wireless/hermes_dld.c
index 9a8ef30..454452f 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,
+ int pda_addr,
+ int pda_len,
+ int use_eeprom) /* can we get this into hw? */
{
int ret;
int pda_size;
+ int 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..a39a482 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,
+ int pda_addr,
+ int 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.4.5


2008-08-07 21:08:48

by Dave Kilroy

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

Dan Williams wrote:
> On Thu, 2008-08-07 at 19:43 +0100, Dave wrote:
>> That said, what's wrong with the ap_scan=2 mode? You've stated it's not
>> great (and I'm prepared to believe it), but what is the actual problem?

> But the problem with ap_scan=2 is really about the failure window.
> ap_scan=2 basically dumps a load of options on the driver, and unless
> the options _exactly_ match the configuration of the AP, you won't
> connect. The supplicant isn't able to make intelligent choices about
> which networks in its config file match the scan result, thus there's a
> lot more potential for failure unless you know exactly what your network
> is set up to do, and these capabilities aren't always exposed through
> beacons. So ap_scan=2 just opens up a huge window of failure and stuff
> can't ever Just Work because no intelligence can be applied.

Wouldn't a different mode of operation in wpa_supplicant solve this (see below)? Then get rid of the mode selection via wpa_supplicant.conf by selecting mode based on drivers error responses (and/or reported capabilities).

ap_scan=3:
* wpa_supplicant requests scan with SIOCSIWSCAN
* driver reports scan complete with wireless event SIOCGIWSCAN
* wpa_supplicant reads scan results with SIOCGIWSCAN (multiple call if a larget buffer is needed)
* wpa_supplicant decides which SSID to use based on scan results
* wpa_supplicant configures driver to associate with an SSID (SIOCSIWMODE, SIOCSIWGENIE, SIOCSIWAUTH, SIOCSIWESSID)

SIOCSIWSCAN not supported? => ap_scan=2
SIOCSIWFREQ or SIOCSIWAP not supported? => ap_scan=3
else ap_scan=1


Regards,

Dave.

2008-08-20 23:08:20

by Dave Kilroy

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

Johannes Berg wrote:
> On Wed, 2008-08-20 at 23:07 +0200, Johannes Berg wrote:
>>> At the moment this is only documented in email threads (on
>>> orinoco-devel) - I lack access to any web/file hosting to put
>>> something more permanent up. This obviously ought to be corrected.
>> *points to wireless.kernel.org*
>>
>> feel free to create a driver page (though I'd prefer to not host
>> firmware of unclear origin and would probably just delete such files if
>> you upload them, talk to the firmware tree if you can redistribute it
>> legally)
>
> Oh and if you go create a new driver page, say
> http://wireless.kernel.org/en/users/Drivers/orinoco, it gives you a
> template in the left column that you can use (the DriverPageTemplate) to
> get a page that looks similar to all the other ones.

Thanks Larry/Johannes. I thought that might be where it should go, but it looks quite mac80211 oriented at the moment.

Anyway, I've added an initial orinoco page <http://wireless.kernel.org/en/users/Drivers/orinoco> and linked to it on <http://wireless.kernel.org/en/users/Drivers>.

I have avoided attaching anything for now.


Regards,

Dave.

2008-08-06 20:58:38

by Dan Williams

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

On Wed, 2008-08-06 at 20:29 +0100, Dave wrote:
> Dan Williams wrote:
> > On Tue, 2008-08-05 at 18:48 -0400, Pavel Roskin wrote:
> >> The userspace is welcome to keep a pool of all APs found by any scans,
> >> but I don't think drivers should do it.
> >
> > Drivers need to keep a reasonably complete list of APs internally for
> > association anyway. You don't want to have to do a full scan just to
> > associate if you have results from 10 seconds ago that are still valid.
>
> This is not an issue for orinoco cards, as the firmware selects the AP to use independently of any driver scanning. The requirement to use ap_scan=2 means that wpa_supplicant doesn't need to look at the scan results either (except maybe to verify the configuration matches).

Well, since the driver supports SSID scanning, we can use ap_scan=1
anyway. ap_scan=2 is actually pretty evil since it depends on WEXT
ordering and whatnot.

Dan

> > Furthermore, drivers need to keep cached results so that two processes
> > can request results of a scan after the scan is complete.
>
> But this does apply to orinoco.
>
>
> Regards,
>
> Dave.
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html


2008-08-20 21:08:35

by Johannes Berg

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


> At the moment this is only documented in email threads (on
> orinoco-devel) - I lack access to any web/file hosting to put
> something more permanent up. This obviously ought to be corrected.

*points to wireless.kernel.org*

feel free to create a driver page (though I'd prefer to not host
firmware of unclear origin and would probably just delete such files if
you upload them, talk to the firmware tree if you can redistribute it
legally)

johannes


Attachments:
signature.asc (836.00 B)
This is a digitally signed message part

2008-08-20 19:32:55

by John W. Linville

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

On Sat, Aug 02, 2008 at 11:14:14AM +0100, [email protected] wrote:
> 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 is against wireless-testing (master-2008-07-16), is
> sparse clean (UP), and should be bisectable. It is almost checkpatch
> clean - the single warning looks like a false positive to me.

Are we satisfied w/ this patch series? If so, could I ask Dave to
repost them as a single series (rather than having to randomly replace
some of them) in the middle?

> 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>.

Is the above documented anywhere other than this email thread?

John
--
John W. Linville
[email protected]

2008-08-04 03:57:14

by Pavel Roskin

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

On Sat, 2008-08-02 at 11:14 +0100, [email protected] wrote:
> 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 is against wireless-testing (master-2008-07-16), is
> sparse clean (UP), and should be bisectable. It is almost checkpatch
> clean - the single warning looks like a false positive to me.

If you mean orinoco_ioctl_getnick(), that's a false positive. However,
sparse finds two issues on x86_64, both due to sizeof() returning
size_t, which is wider than int.

Here's the fix. Please integrate it into the patches that introduce the
code.

diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index dca725c..3ab7822 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -479,7 +479,7 @@ orinoco_dl_firmware(struct orinoco_private *priv,

/* Read current plug data */
err = hermes_read_pda(hw, pda, fw->pda_addr,
- min(fw->pda_size, sizeof(pda)), 0);
+ min_t(int, fw->pda_size, sizeof(pda)), 0);
printk(KERN_DEBUG "%s: Read PDA returned %d\n", dev->name, err);
if (err)
return err;
@@ -2140,7 +2140,7 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
else if (len > sizeof(*bss)) {
printk(KERN_WARNING
"%s: Ext scan results too large (%d bytes). "
- "Truncating results to %d bytes.\n",
+ "Truncating results to %zd bytes.\n",
dev->name, len, sizeof(*bss));
len = sizeof(*bss);
} else if (len < (offsetof(struct agere_ext_scan_info,

> 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>.

I tested it on my WPA2 AP and could not associate:

# wpa_supplicant -c /etc/wpa_supplicant/wpa_main.conf -D wext -i eth1
CTRL-EVENT-SCAN-RESULTS
Trying to associate with 00:19:5b:56:fc:73 (SSID='mike' freq=2437 MHz)
ioctl[SIOCSIWFREQ]: Device or resource busy
ioctl[SIOCSIWAP]: Operation not supported
Association request to the driver failed

And that's from the kernel log:

[185185.284523] eth1: Hardware identity 0001:0004:0005:0000
[185185.284686] eth1: Station identity 001f:0002:0009:002a
[185185.284722] eth1: Firmware determined as Lucent/Agere 9.42
[185185.284758] eth1: Ad-hoc demo mode supported
[185185.284793] eth1: IEEE standard IBSS ad-hoc mode supported
[185185.284829] eth1: WEP supported, 104-bit key
[185185.284872] eth1: WPA-PSK supported
[185185.285010] eth1: MAC address 00:02:2d:8b:56:87
[185185.285153] eth1: Station name "HERMES I"
[185185.285811] eth1: ready
[185185.287298] eth1: orinoco_cs at 0.0, irq 18, io 0x1100-0x113f
[185213.396202] eth1: New link status: Connected (0001)
[185214.469964] eth1: Ext scan results too large (272 bytes). Truncating
results to 270 bytes.
[185214.575811] eth1: Lucent/Agere firmware doesn't support manual
roaming
[185219.617236] eth1: Ext scan results too large (272 bytes). Truncating
results to 270 bytes.
[185219.619871] eth1: Ext scan results too large (272 bytes). Truncating
results to 270 bytes.

--
Regards,
Pavel Roskin

2008-09-09 19:38:46

by Jean Tourrilhes

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

On Tue, Sep 09, 2008 at 07:37:22PM +0100, Dave wrote:
>
> Thanks for finding that Jean! I've double checked, and agree it's the right
> fix.
>
> FWIW, I've verified this compiles and (still) works as expected on my 32
> bit system.

My pleasure. Sorry for beeing so late...
By the way, I would not mind people looking at Wireless Tools
v30.pre7 so that I can get feedback and make a final release.

> Dave.

Regards,

Jean

2008-09-15 21:20:13

by Jean Tourrilhes

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

On Sat, Sep 13, 2008 at 12:17:55AM -0400, Pavel Roskin wrote:
> On Mon, 2008-09-08 at 11:32 -0700, Jean Tourrilhes wrote:
>
> > Try the attached patch.
>
> It's working on x86_64! Thank you!

Great, so it's not only me ;-)

> The same problem exists in airo and atmel, but they don't use that
> argument. Several drivers, including mac80211 use union iwreq_data,
> which is fragile. Fortunately, most drivers don't use that argument or
> use the "data" field, which is correct.
>
> The only incorrect use of union iwreq_data is in rndis_wlan. It would
> ignore ESSID on x86_64.
>
> I think the use of union iwreq_data is dangerous and should be avoided.

Note, as soon as you are using the data for real, as opposed
to only the flags, you would have noticed that something was
amiss. So, in essence it should be self correcting.
The way I found the cause is that I wanted to dump the ESSID
in the driver for debugging purpose. You can't access the ESSID
through a iw_param ;-)
Note that as you mentionned, the other faulty drivers are not
using the arguments, and all drivers using the argument seems to have
the right bits.

> Regards,
> Pavel Roskin

Thanks, and have fun...

Jean

2008-09-08 16:52:06

by Jean Tourrilhes

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

On Mon, Sep 08, 2008 at 08:48:30AM -0400, Pavel Roskin wrote:
> On Tue, 2008-09-02 at 16:06 -0700, Jean Tourrilhes wrote:
>
> > By the way, the breakage of iwlist scan in Wireless Tools 30
> > on 64 bits should be fixed in 30.pre7.
>
> No, it's still not fixed on x86_64.

I was talking about the other breakages you were
seeing. Anyway.

> scanflags in print_scanning_info()
> in iwlist.c is 2, but srq->flags is 0 in orinoco_ioctl_setscan() in
> orinoco.c.
>
> On i386, srq->flags is 2 as expected and the scan results are correctly
> influenced by the ESSID.

Hmm... My gut reaction would be that fs/compat_ioctl.c would
be wrong, but it seems to have the proper define. I'll try to check.

> Regards,
> Pavel Roskin

Thanks...

Jean

2008-09-08 18:35:05

by Jean Tourrilhes

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

On Mon, Sep 08, 2008 at 08:48:30AM -0400, Pavel Roskin wrote:
> On Tue, 2008-09-02 at 16:06 -0700, Jean Tourrilhes wrote:
>
> > By the way, the breakage of iwlist scan in Wireless Tools 30
> > on 64 bits should be fixed in 30.pre7.
>
> No, it's still not fixed on x86_64. scanflags in print_scanning_info()
> in iwlist.c is 2, but srq->flags is 0 in orinoco_ioctl_setscan() in
> orinoco.c.
>
> On i386, srq->flags is 2 as expected and the scan results are correctly
> influenced by the ESSID.
>
> --
> Regards,
> Pavel Roskin

Try the attached patch.

Jean

---------------------------------------------------

diff -u -p linux/drivers/net/wireless/orinoco.p1.c linux/drivers/net/wireless/orinoco.c
--- linux/drivers/net/wireless/orinoco.p1.c 2008-09-08 11:28:01.000000000 -0700
+++ linux/drivers/net/wireless/orinoco.c 2008-09-08 11:29:59.000000000 -0700
@@ -3953,7 +3953,7 @@ static int orinoco_ioctl_getrid(struct n
/* 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,
+ struct iw_point *srq,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);

2008-09-09 21:46:49

by Jean Tourrilhes

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

On Wed, Sep 10, 2008 at 12:20:26AM +0300, Tomas Winkler wrote:
> On Tue, Sep 9, 2008 at 10:33 PM, Jean Tourrilhes <[email protected]> wrote:
> > On Tue, Sep 09, 2008 at 07:37:22PM +0100, Dave wrote:
> >>
> >> Thanks for finding that Jean! I've double checked, and agree it's the right
> >> fix.
> >>
> >> FWIW, I've verified this compiles and (still) works as expected on my 32
> >> bit system.
> >
> > My pleasure. Sorry for beeing so late...
> > By the way, I would not mind people looking at Wireless Tools
> > v30.pre7 so that I can get feedback and make a final release.
> >
> My little report :)
> I've tested the power (save) command. It fixes the issues in 'saving'
> parameter that are in v29.
> Tomas

Thanks for testing. The fact that this bug went into v29 show
how many people are using that feature...

Jean

2008-09-09 21:20:28

by Tomas Winkler

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

On Tue, Sep 9, 2008 at 10:33 PM, Jean Tourrilhes <[email protected]> wrote:
> On Tue, Sep 09, 2008 at 07:37:22PM +0100, Dave wrote:
>>
>> Thanks for finding that Jean! I've double checked, and agree it's the right
>> fix.
>>
>> FWIW, I've verified this compiles and (still) works as expected on my 32
>> bit system.
>
> My pleasure. Sorry for beeing so late...
> By the way, I would not mind people looking at Wireless Tools
> v30.pre7 so that I can get feedback and make a final release.
>
My little report :)
I've tested the power (save) command. It fixes the issues in 'saving'
parameter that are in v29.
Tomas

2008-09-08 12:48:33

by Pavel Roskin

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

On Tue, 2008-09-02 at 16:06 -0700, Jean Tourrilhes wrote:

> By the way, the breakage of iwlist scan in Wireless Tools 30
> on 64 bits should be fixed in 30.pre7.

No, it's still not fixed on x86_64. scanflags in print_scanning_info()
in iwlist.c is 2, but srq->flags is 0 in orinoco_ioctl_setscan() in
orinoco.c.

On i386, srq->flags is 2 as expected and the scan results are correctly
influenced by the ESSID.

--
Regards,
Pavel Roskin

2008-09-02 23:30:10

by Jean Tourrilhes

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

On Tue, Aug 05, 2008 at 09:22:38AM -0700, jt wrote:
> On Mon, Aug 04, 2008 at 11:34:20AM -0400, Dan Williams wrote:
> > On Mon, 2008-08-04 at 00:48 -0400, Pavel Roskin wrote:
> > > On Sat, 2008-08-02 at 11:14 +0100, [email protected] wrote:
> > > > 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.
> > >
> > > That's a nice thing to have, but it doesn't seem to work on x86_64 (I
> > > didn't try other platforms). I tried wireless-tools 30.pre6 instead of
> > > 29, but it broke the output completely:
> > >
> > > # iwlist eth1 scan
> > > *** Please report to [email protected] your platform details
> > > *** and the following line :
> > > *** IW_EV_LCP_PK2_LEN = 4 ; IW_EV_POINT_PK2_LEN = 8
> > >
> > > eth1 Scan completed :
> > > Cell 01 - Address: 00:19:5B:56:FC:73
> > > ESSID:off/any/hidden
> > > Mode:Master
> > > Frequency=2.437 GHz (Channel 6)
> > > Signal level:-29 dBm Noise level:-81 dBm
> > > Encryption key:E0
> > > Extra:
> > > Cell 02 - Address: 00:15:6D:53:9A:CA
> > > ESSID:off/any/hidden
> > > Mode:Master
> > > Frequency:2.462 GHz (Channel 11)
> > > Signal level:-49 dBm Noise level:-80 dBm
> > > Encryption key:E0
> > > Extra:
> > >
> > >
> > > > + if (priv->scan_mode & IW_SCAN_THIS_ESSID) {
> > >
> > > The debug output shows that the about condition is never true with
> > > either version of iwlist. The command is:
> > >
> > > iwlist eth1 scan essid foo
> >
> > That code looks right, since srq->flags is getting copied into
> > priv->scan_mode.
> >
> > Maybe the wireless-tools breakage you're experiencing is causing this to
> > fail on x86-64? Are you using 64-bit wireless-tools and a 64-bit
> > kernel, or 32-bit wireless tools with a 64-bit kernel?
> >
> > Dan
>
> I still need to look into those 64 bit issues. That's the
> reason why WT-30 is still pending. The good news is that I now have a
> 64 bit box. The bad news is work and family.
> Have fun...
>
> Jean

By the way, the breakage of iwlist scan in Wireless Tools 30
on 64 bits should be fixed in 30.pre7.
Have fun...

Jean


2008-09-09 18:37:33

by Dave Kilroy

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

Jean Tourrilhes wrote:
> On Mon, Sep 08, 2008 at 08:48:30AM -0400, Pavel Roskin wrote:
>> No, it's still not fixed on x86_64. scanflags in print_scanning_info()
>> in iwlist.c is 2, but srq->flags is 0 in orinoco_ioctl_setscan() in
>> orinoco.c.
>>
>> On i386, srq->flags is 2 as expected and the scan results are correctly
>> influenced by the ESSID.
>
> Try the attached patch.
>
> static int orinoco_ioctl_setscan(struct net_device *dev,
> struct iw_request_info *info,
> - struct iw_param *srq,
> + struct iw_point *srq,
> char *extra)

Thanks for finding that Jean! I've double checked, and agree it's the right fix.

FWIW, I've verified this compiles and (still) works as expected on my 32 bit system.



Dave.

2008-09-13 04:17:58

by Pavel Roskin

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

On Mon, 2008-09-08 at 11:32 -0700, Jean Tourrilhes wrote:

> Try the attached patch.

It's working on x86_64! Thank you!

> diff -u -p linux/drivers/net/wireless/orinoco.p1.c linux/drivers/net/wireless/orinoco.c
> --- linux/drivers/net/wireless/orinoco.p1.c 2008-09-08 11:28:01.000000000 -0700
> +++ linux/drivers/net/wireless/orinoco.c 2008-09-08 11:29:59.000000000 -0700
> @@ -3953,7 +3953,7 @@ static int orinoco_ioctl_getrid(struct n
> /* 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,
> + struct iw_point *srq,
> char *extra)

The same problem exists in airo and atmel, but they don't use that
argument. Several drivers, including mac80211 use union iwreq_data,
which is fragile. Fortunately, most drivers don't use that argument or
use the "data" field, which is correct.

The only incorrect use of union iwreq_data is in rndis_wlan. It would
ignore ESSID on x86_64.

I think the use of union iwreq_data is dangerous and should be avoided.

--
Regards,
Pavel Roskin