[PATCH] orinoco: Fix memory leak on error in processing hostscan frames.
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / orinoco.c
index 38fd8623a444e01c773d0ea6fa563af3b8373f7e..95eb05abc44ac61623e1860516386c45a766c5ce 100644 (file)
  * under either the MPL or the GPL.  */
 
 /*
- * v0.01 -> v0.02 - 21/3/2001 - Jean II
- *     o Allow to use regular ethX device name instead of dldwdX
- *     o Warning on IBSS with ESSID=any for firmware 6.06
- *     o Put proper range.throughput values (optimistic)
- *     o IWSPY support (IOCTL and stat gather in Rx path)
- *     o Allow setting frequency in Ad-Hoc mode
- *     o Disable WEP setting if !has_wep to work on old firmware
- *     o Fix txpower range
- *     o Start adding support for Samsung/Compaq firmware
- *
- * v0.02 -> v0.03 - 23/3/2001 - Jean II
- *     o Start adding Symbol support - need to check all that
- *     o Fix Prism2/Symbol WEP to accept 128 bits keys
- *     o Add Symbol WEP (add authentication type)
- *     o Add Prism2/Symbol rate
- *     o Add PM timeout (holdover duration)
- *     o Enable "iwconfig eth0 key off" and friends (toggle flags)
- *     o Enable "iwconfig eth0 power unicast/all" (toggle flags)
- *     o Try with an Intel card. It report firmware 1.01, behave like
- *       an antiquated firmware, however on windows it says 2.00. Yuck !
- *     o Workaround firmware bug in allocate buffer (Intel 1.01)
- *     o Finish external renaming to orinoco...
- *     o Testing with various Wavelan firmwares
- *
- * v0.03 -> v0.04 - 30/3/2001 - Jean II
- *     o Update to Wireless 11 -> add retry limit/lifetime support
- *     o Tested with a D-Link DWL 650 card, fill in firmware support
- *     o Warning on Vcc mismatch (D-Link 3.3v card in Lucent 5v only slot)
- *     o Fixed the Prism2 WEP bugs that I introduced in v0.03 :-(
- *       It works on D-Link *only* after a tcpdump. Weird...
- *       And still doesn't work on Intel card. Grrrr...
- *     o Update the mode after a setport3
- *     o Add preamble setting for Symbol cards (not yet enabled)
- *     o Don't complain as much about Symbol cards...
- *
- * v0.04 -> v0.04b - 22/4/2001 - David Gibson
- *      o Removed the 'eth' parameter - always use ethXX as the
- *        interface name instead of dldwdXX.  The other was racy
- *        anyway.
- *     o Clean up RID definitions in hermes.h, other cleanups
- *
- * v0.04b -> v0.04c - 24/4/2001 - Jean II
- *     o Tim Hurley <timster AT seiki.bliztech.com> reported a D-Link card
- *       with vendor 02 and firmware 0.08. Added in the capabilities...
- *     o Tested Lucent firmware 7.28, everything works...
- *
- * v0.04c -> v0.05 - 3/5/2001 - Benjamin Herrenschmidt
- *     o Spin-off Pcmcia code. This file is renamed orinoco.c,
- *       and orinoco_cs.c now contains only the Pcmcia specific stuff
- *     o Add Airport driver support on top of orinoco.c (see airport.c)
- *
- * v0.05 -> v0.05a - 4/5/2001 - Jean II
- *     o Revert to old Pcmcia code to fix breakage of Ben's changes...
- *
- * v0.05a -> v0.05b - 4/5/2001 - Jean II
- *     o add module parameter 'ignore_cis_vcc' for D-Link @ 5V
- *     o D-Link firmware doesn't support multicast. We just print a few
- *       error messages, but otherwise everything works...
- *     o For David : set/getport3 works fine, just upgrade iwpriv...
- *
- * v0.05b -> v0.05c - 5/5/2001 - Benjamin Herrenschmidt
- *     o Adapt airport.c to latest changes in orinoco.c
- *     o Remove deferred power enabling code
- *
- * v0.05c -> v0.05d - 5/5/2001 - Jean II
- *     o Workaround to SNAP decapsulate frame from Linksys AP
- *       original patch from : Dong Liu <dliu AT research.bell-labs.com>
- *       (note : the memcmp bug was mine - fixed)
- *     o Remove set_retry stuff, no firmware support it (bloat--).
- *
- * v0.05d -> v0.06 - 25/5/2001 - Jean II
- *             Original patch from "Hong Lin" <alin AT redhat.com>,
- *             "Ian Kinner" <ikinner AT redhat.com>
- *             and "David Smith" <dsmith AT redhat.com>
- *     o Init of priv->tx_rate_ctrl in firmware specific section.
- *     o Prism2/Symbol rate, upto should be 0xF and not 0x15. Doh !
- *     o Spectrum card always need cor_reset (for every reset)
- *     o Fix cor_reset to not lose bit 7 in the register
- *     o flush_stale_links to remove zombie Pcmcia instances
- *     o Ack previous hermes event before reset
- *             Me (with my little hands)
- *     o Allow orinoco.c to call cor_reset via priv->card_reset_handler
- *     o Add priv->need_card_reset to toggle this feature
- *     o Fix various buglets when setting WEP in Symbol firmware
- *       Now, encryption is fully functional on Symbol cards. Youpi !
- *
- * v0.06 -> v0.06b - 25/5/2001 - Jean II
- *     o IBSS on Symbol use port_mode = 4. Please don't ask...
- *
- * v0.06b -> v0.06c - 29/5/2001 - Jean II
- *     o Show first spy address in /proc/net/wireless for IBSS mode as well
- *
- * v0.06c -> v0.06d - 6/7/2001 - David Gibson
- *      o Change a bunch of KERN_INFO messages to KERN_DEBUG, as per Linus'
- *        wishes to reduce the number of unnecessary messages.
- *     o Removed bogus message on CRC error.
- *     o Merged fixes for v0.08 Prism 2 firmware from William Waghorn
- *       <willwaghorn AT yahoo.co.uk>
- *     o Slight cleanup/re-arrangement of firmware detection code.
- *
- * v0.06d -> v0.06e - 1/8/2001 - David Gibson
- *     o Removed some redundant global initializers (orinoco_cs.c).
- *     o Added some module metadata
- *
- * v0.06e -> v0.06f - 14/8/2001 - David Gibson
- *     o Wording fix to license
- *     o Added a 'use_alternate_encaps' module parameter for APs which need an
- *       oui of 00:00:00.  We really need a better way of handling this, but
- *       the module flag is better than nothing for now.
- *
- * v0.06f -> v0.07 - 20/8/2001 - David Gibson
- *     o Removed BAP error retries from hermes_bap_seek().  For Tx we now
- *       let the upper layers handle the retry, we retry explicitly in the
- *       Rx path, but don't make as much noise about it.
- *     o Firmware detection cleanups.
- *
- * v0.07 -> v0.07a - 1/10/3001 - Jean II
- *     o Add code to read Symbol firmware revision, inspired by latest code
- *       in Spectrum24 by Lee John Keyser-Allen - Thanks Lee !
- *     o Thanks to Jared Valentine <hidden AT xmission.com> for "providing" me
- *       a 3Com card with a recent firmware, fill out Symbol firmware
- *       capabilities of latest rev (2.20), as well as older Symbol cards.
- *     o Disable Power Management in newer Symbol firmware, the API 
- *       has changed (documentation needed).
- *
- * v0.07a -> v0.08 - 3/10/2001 - David Gibson
- *     o Fixed a possible buffer overrun found by the Stanford checker (in
- *       dldwd_ioctl_setiwencode()).  Can only be called by root anyway, so not
- *       a big problem.
- *     o Turned has_big_wep on for Intersil cards.  That's not true for all of
- *       them but we should at least let the capable ones try.
- *     o Wait for BUSY to clear at the beginning of hermes_bap_seek().  I
- *       realized that my assumption that the driver's serialization
- *       would prevent the BAP being busy on entry was possibly false, because
- *       things other than seeks may make the BAP busy.
- *     o Use "alternate" (oui 00:00:00) encapsulation by default.
- *       Setting use_old_encaps will mimic the old behaviour, but I think we
- *       will be able to eliminate this.
- *     o Don't try to make __initdata const (the version string).  This can't
- *       work because of the way the __initdata sectioning works.
- *     o Added MODULE_LICENSE tags.
- *     o Support for PLX (transparent PCMCIA->PCI bridge) cards.
- *     o Changed to using the new type-fascist min/max.
- *
- * v0.08 -> v0.08a - 9/10/2001 - David Gibson
- *     o Inserted some missing acknowledgements/info into the Changelog.
- *     o Fixed some bugs in the normalization of signal level reporting.
- *     o Fixed bad bug in WEP key handling on Intersil and Symbol firmware,
- *       which led to an instant crash on big-endian machines.
- *
- * v0.08a -> v0.08b - 20/11/2001 - David Gibson
- *     o Lots of cleanup and bugfixes in orinoco_plx.c
- *     o Cleanup to handling of Tx rate setting.
- *     o Removed support for old encapsulation method.
- *     o Removed old "dldwd" names.
- *     o Split RID constants into a new file hermes_rid.h
- *     o Renamed RID constants to match linux-wlan-ng and prism2.o
- *     o Bugfixes in hermes.c
- *     o Poke the PLX's INTCSR register, so it actually starts
- *       generating interrupts.  These cards might actually work now.
- *     o Update to wireless extensions v12 (Jean II)
- *     o Support for tallies and inquire command (Jean II)
- *     o Airport updates for newer PPC kernels (BenH)
- *
- * v0.08b -> v0.09 - 21/12/2001 - David Gibson
- *     o Some new PCI IDs for PLX cards.
- *     o Removed broken attempt to do ALLMULTI reception.  Just use
- *       promiscuous mode instead
- *     o Preliminary work for list-AP (Jean II)
- *     o Airport updates from (BenH)
- *     o Eliminated racy hw_ready stuff
- *     o Fixed generation of fake events in irq handler.  This should
- *       finally kill the EIO problems (Jean II & dgibson)
- *     o Fixed breakage of bitrate set/get on Agere firmware (Jean II)
- *
- * v0.09 -> v0.09a - 2/1/2002 - David Gibson
- *     o Fixed stupid mistake in multicast list handling, triggering
- *       a BUG()
- *
- * v0.09a -> v0.09b - 16/1/2002 - David Gibson
- *     o Fixed even stupider mistake in new interrupt handling, which
- *       seriously broke things on big-endian machines.
- *     o Removed a bunch of redundant includes and exports.
- *     o Removed a redundant MOD_{INC,DEC}_USE_COUNT pair in airport.c
- *     o Don't attempt to do hardware level multicast reception on
- *       Intersil firmware, just go promisc instead.
- *     o Typo fixed in hermes_issue_cmd()
- *     o Eliminated WIRELESS_SPY #ifdefs
- *     o Status code reported on Tx exceptions
- *     o Moved netif_wake_queue() from ALLOC interrupts to TX and TXEXC
- *       interrupts, which should fix the timeouts we're seeing.
- *
- * v0.09b -> v0.10 - 25 Feb 2002 - David Gibson
- *     o Removed nested structures used for header parsing, so the
- *       driver should now work without hackery on ARM
- *     o Fix for WEP handling on Intersil (Hawk Newton)
- *     o Eliminated the /proc/hermes/ethXX/regs debugging file.  It
- *       was never very useful.
- *     o Make Rx errors less noisy.
- *
- * v0.10 -> v0.11 - 5 Apr 2002 - David Gibson
- *     o Laid the groundwork in hermes.[ch] for devices which map
- *       into PCI memory space rather than IO space.
- *     o Fixed bug in multicast handling (cleared multicast list when
- *       leaving promiscuous mode).
- *     o Relegated Tx error messages to debug.
- *     o Cleaned up / corrected handling of allocation lengths.
- *     o Set OWNSSID in IBSS mode for WinXP interoperability (jimc).
- *     o Change to using alloc_etherdev() for structure allocations. 
- *     o Check for and drop undersized packets.
- *     o Fixed a race in stopping/waking the queue.  This should fix
- *       the timeout problems (Pavel Roskin)
- *     o Reverted to netif_wake_queue() on the ALLOC event.
- *     o Fixes for recent Symbol firmwares which lack AP density
- *       (Pavel Roskin).
- *
- * v0.11 -> v0.11a - 29 Apr 2002 - David Gibson
- *     o Handle different register spacing, necessary for Prism 2.5
- *       PCI adaptors (Steve Hill).
- *     o Cleaned up initialization of card structures in orinoco_cs
- *       and airport.  Removed card->priv field.
- *     o Make response structure optional for hermes_docmd_wait()
- *       Pavel Roskin)
- *     o Added PCI id for Nortel emobility to orinoco_plx.c.
- *     o Cleanup to handling of Symbol's allocation bug. (Pavel Roskin)
- *     o Cleanups to firmware capability detection.
- *     o Arrange for orinoco_pci.c to override firmware detection.
- *       We should be able to support the PCI Intersil cards now.
- *     o Cleanup handling of reset_cor and hard_reset (Pavel Roskin).
- *     o Remove erroneous use of USER_BAP in the TxExc handler (Jouni
- *       Malinen).
- *     o Makefile changes for better integration into David Hinds
- *       pcmcia-cs package.
- *
- * v0.11a -> v0.11b - 1 May 2002 - David Gibson
- *     o Better error reporting in orinoco_plx_init_one()
- *     o Fixed multiple bad kfree() bugs introduced by the
- *       alloc_orinocodev() changes.
- *
- * v0.11b -> v0.12 - 19 Jun 2002 - David Gibson
- *     o Support changing the MAC address.
- *     o Correct display of Intersil firmware revision numbers.
- *     o Entirely revised locking scheme.  Should be both simpler and
- *        better.
- *     o Merged some common code in orinoco_plx, orinoco_pci and
- *       airport by creating orinoco_default_{open,stop,reset}()
- *       which are used as the dev->open, dev->stop, priv->reset
- *       callbacks if none are specified when alloc_orinocodev() is
- *       called.
- *     o Removed orinoco_plx_interrupt() and orinoco_pci_interrupt().
- *       They didn't do anything.
- *
- * v0.12 -> v0.12a - 4 Jul 2002 - David Gibson
- *     o Some rearrangement of code.
- *     o Numerous fixups to locking and rest handling, particularly
- *       for PCMCIA.
- *     o This allows open and stop net_device methods to be in
- *       orinoco.c now, rather than in the init modules.
- *     o In orinoco_cs.c link->priv now points to the struct
- *       net_device not to the struct orinoco_private.
- *     o Added a check for undersized SNAP frames, which could cause
- *       crashes.
- *
- * v0.12a -> v0.12b - 11 Jul 2002 - David Gibson
- *     o Fix hw->num_init testing code, so num_init is actually
- *       incremented.
- *     o Fix very stupid bug in orinoco_cs which broke compile with
- *       CONFIG_SMP.
- *     o Squashed a warning.
- *
- * v0.12b -> v0.12c - 26 Jul 2002 - David Gibson
- *     o Change to C9X style designated initializers.
- *     o Add support for 3Com AirConnect PCI.
- *     o No longer ignore the hard_reset argument to
- *       alloc_orinocodev().  Oops.
- *
- * v0.12c -> v0.13beta1 - 13 Sep 2002 - David Gibson
- *     o Revert the broken 0.12* locking scheme and go to a new yet
- *       simpler scheme.
- *     o Do firmware resets only in orinoco_init() and when waking
- *       the card from hard sleep.
- *
- * v0.13beta1 -> v0.13 - 27 Sep 2002 - David Gibson
- *     o Re-introduced full resets (via schedule_task()) on Tx
- *       timeout.
- *
- * v0.13 -> v0.13a - 30 Sep 2002 - David Gibson
- *     o Minor cleanups to info frame handling.  Add basic support
- *       for linkstatus info frames.
- *     o Include required kernel headers in orinoco.h, to avoid
- *       compile problems.
- *
- * v0.13a -> v0.13b - 10 Feb 2003 - David Gibson
- *     o Implemented hard reset for Airport cards
- *     o Experimental suspend/resume implementation for orinoco_pci
- *     o Abolished /proc debugging support, replaced with a debugging
- *       iwpriv.  Now it's ugly and simple instead of ugly and complex.
- *     o Bugfix in hermes.c if the firmware returned a record length
- *       of 0, we could go clobbering memory.
- *     o Bugfix in orinoco_stop() - it used to fail if hw_unavailable
- *       was set, which was usually true on PCMCIA hot removes.
- *     o Track LINKSTATUS messages, silently drop Tx packets before
- *       we are connected (avoids confusing the firmware), and only
- *       give LINKSTATUS printk()s if the status has changed.
- *
- * v0.13b -> v0.13c - 11 Mar 2003 - David Gibson
- *     o Cleanup: use dev instead of priv in various places.
- *     o Bug fix: Don't ReleaseConfiguration on RESET_PHYSICAL event
- *       if we're in the middle of a (driver initiated) hard reset.
- *     o Bug fix: ETH_ZLEN is supposed to include the header
- *       (Dionysus Blazakis & Manish Karir)
- *     o Convert to using workqueues instead of taskqueues (and
- *       backwards compatibility macros for pre 2.5.41 kernels).
- *     o Drop redundant (I think...) MOD_{INC,DEC}_USE_COUNT in
- *       airport.c
- *     o New orinoco_tmd.c init module from Joerg Dorchain for
- *       TMD7160 based PCI to PCMCIA bridges (similar to
- *       orinoco_plx.c).
- *
- * v0.13c -> v0.13d - 22 Apr 2003 - David Gibson
- *     o Make hw_unavailable a counter, rather than just a flag, this
- *       is necessary to avoid some races (such as a card being
- *       removed in the middle of orinoco_reset().
- *     o Restore Release/RequestConfiguration in the PCMCIA event handler
- *       when dealing with a driver initiated hard reset.  This is
- *       necessary to prevent hangs due to a spurious interrupt while
- *       the reset is in progress.
- *     o Clear the 802.11 header when transmitting, even though we
- *       don't use it.  This fixes a long standing bug on some
- *       firmwares, which seem to get confused if that isn't done.
- *     o Be less eager to de-encapsulate SNAP frames, only do so if
- *       the OUI is 00:00:00 or 00:00:f8, leave others alone.  The old
- *       behaviour broke CDP (Cisco Discovery Protocol).
- *     o Use dev instead of priv for free_irq() as well as
- *       request_irq() (oops).
- *     o Attempt to reset rather than giving up if we get too many
- *       IRQs.
- *     o Changed semantics of __orinoco_down() so it can be called
- *       safely with hw_unavailable set.  It also now clears the
- *       linkstatus (since we're going to have to reassociate).
- *
- * v0.13d -> v0.13e - 12 May 2003 - David Gibson
- *     o Support for post-2.5.68 return values from irq handler.
- *     o Fixed bug where underlength packets would be double counted
- *       in the rx_dropped statistics.
- *     o Provided a module parameter to suppress linkstatus messages.
- *
- * v0.13e -> v0.14alpha1 - 30 Sep 2003 - David Gibson
- *     o Replaced priv->connected logic with netif_carrier_on/off()
- *       calls.
- *     o Remove has_ibss_any and never set the CREATEIBSS RID when
- *       the ESSID is empty.  Too many firmwares break if we do.
- *     o 2.6 merges: Replace pdev->slot_name with pci_name(), remove
- *       __devinitdata from PCI ID tables, use free_netdev().
- *     o Enabled shared-key authentication for Agere firmware (from
- *       Robert J. Moore <Robert.J.Moore AT allanbank.com>
- *     o Move netif_wake_queue() (back) to the Tx completion from the
- *       ALLOC event.  This seems to prevent/mitigate the rolling
- *       error -110 problems at least on some Intersil firmwares.
- *       Theoretically reduces performance, but I can't measure it.
- *       Patch from Andrew Tridgell <tridge AT samba.org>
- *
- * v0.14alpha1 -> v0.14alpha2 - 20 Oct 2003 - David Gibson
- *     o Correctly turn off shared-key authentication when requested
- *       (bugfix from Robert J. Moore).
- *     o Correct airport sleep interfaces for current 2.6 kernels.
- *     o Add code for key change without disabling/enabling the MAC
- *       port.  This is supposed to allow 802.1x to work sanely, but
- *       doesn't seem to yet.
- *
  * TODO
- *     o New wireless extensions API (patch from Moustafa
- *       Youssef, updated by Jim Carter and Pavel Roskin).
  *     o Handle de-encapsulation within network layer, provide 802.11
  *       headers (patch from Thomas 'Dent' Mirlacher)
- *     o RF monitor mode support
  *     o Fix possible races in SPY handling.
  *     o Disconnect wireless extensions from fundamental configuration.
  *     o (maybe) Software WEP support (patch from Stano Meduna).
 #include <net/iw_handler.h>
 #include <net/ieee80211.h>
 
+#include <net/ieee80211.h>
+
 #include <asm/uaccess.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include "hermes.h"
 #include "hermes_rid.h"
 #include "orinoco.h"
-#include "ieee802_11.h"
 
 /********************************************************************/
 /* Module information                                               */
@@ -499,6 +127,10 @@ static int ignore_disconnect; /* = 0 */
 module_param(ignore_disconnect, int, 0644);
 MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer");
 
+static int force_monitor; /* = 0 */
+module_param(force_monitor, int, 0644);
+MODULE_PARM_DESC(force_monitor, "Allow monitor mode for all firmware versions");
+
 /********************************************************************/
 /* Compile time configuration and compatibility stuff               */
 /********************************************************************/
@@ -519,7 +151,7 @@ static const u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
 #define ENCAPS_OVERHEAD                (sizeof(encaps_hdr) + 2)
 
 #define ORINOCO_MIN_MTU                256
-#define ORINOCO_MAX_MTU                (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD)
+#define ORINOCO_MAX_MTU                (IEEE80211_DATA_LEN - ENCAPS_OVERHEAD)
 
 #define SYMBOL_MAX_VER_LEN     (14)
 #define USER_BAP               0
@@ -619,7 +251,9 @@ struct hermes_tx_descriptor_802_11 {
        u16 ethertype;
 } __attribute__ ((packed));
 
+/* Rx frame header except compatibility 802.3 header */
 struct hermes_rx_descriptor {
+       /* Control */
        u16 status;
        u32 time;
        u8 silence;
@@ -627,6 +261,18 @@ struct hermes_rx_descriptor {
        u8 rate;
        u8 rxflow;
        u32 reserved;
+
+       /* 802.11 header */
+       u16 frame_ctl;
+       u16 duration_id;
+       u8 addr1[ETH_ALEN];
+       u8 addr2[ETH_ALEN];
+       u8 addr3[ETH_ALEN];
+       u16 seq_ctl;
+       u8 addr4[ETH_ALEN];
+
+       /* Data length */
+       u16 data_len;
 } __attribute__ ((packed));
 
 /********************************************************************/
@@ -656,6 +302,10 @@ static inline void set_port_type(struct orinoco_private *priv)
                        priv->createibss = 1;
                }
                break;
+       case IW_MODE_MONITOR:
+               priv->port_type = 3;
+               priv->createibss = 0;
+               break;
        default:
                printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
                       priv->ndev->name);
@@ -793,7 +443,7 @@ static int orinoco_change_mtu(struct net_device *dev, int new_mtu)
        if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) )
                return -EINVAL;
 
-       if ( (new_mtu + ENCAPS_OVERHEAD + IEEE802_11_HLEN) >
+       if ( (new_mtu + ENCAPS_OVERHEAD + IEEE80211_HLEN) >
             (priv->nicbuf_size - ETH_HLEN) )
                return -EINVAL;
 
@@ -842,7 +492,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
                return 1;
        }
 
-       if (! netif_carrier_ok(dev)) {
+       if (! netif_carrier_ok(dev) || (priv->iw_mode == IW_MODE_MONITOR)) {
                /* Oops, the firmware hasn't established a connection,
                    silently drop the packet (this seems to be the
                    safest approach). */
@@ -1104,18 +754,127 @@ static void orinoco_stat_gather(struct net_device *dev,
        }
 }
 
+/*
+ * orinoco_rx_monitor - handle received monitor frames.
+ *
+ * Arguments:
+ *     dev             network device
+ *     rxfid           received FID
+ *     desc            rx descriptor of the frame
+ *
+ * Call context: interrupt
+ */
+static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid,
+                              struct hermes_rx_descriptor *desc)
+{
+       u32 hdrlen = 30;        /* return full header by default */
+       u32 datalen = 0;
+       u16 fc;
+       int err;
+       int len;
+       struct sk_buff *skb;
+       struct orinoco_private *priv = netdev_priv(dev);
+       struct net_device_stats *stats = &priv->stats;
+       hermes_t *hw = &priv->hw;
+
+       len = le16_to_cpu(desc->data_len);
+
+       /* Determine the size of the header and the data */
+       fc = le16_to_cpu(desc->frame_ctl);
+       switch (fc & IEEE80211_FCTL_FTYPE) {
+       case IEEE80211_FTYPE_DATA:
+               if ((fc & IEEE80211_FCTL_TODS)
+                   && (fc & IEEE80211_FCTL_FROMDS))
+                       hdrlen = 30;
+               else
+                       hdrlen = 24;
+               datalen = len;
+               break;
+       case IEEE80211_FTYPE_MGMT:
+               hdrlen = 24;
+               datalen = len;
+               break;
+       case IEEE80211_FTYPE_CTL:
+               switch (fc & IEEE80211_FCTL_STYPE) {
+               case IEEE80211_STYPE_PSPOLL:
+               case IEEE80211_STYPE_RTS:
+               case IEEE80211_STYPE_CFEND:
+               case IEEE80211_STYPE_CFENDACK:
+                       hdrlen = 16;
+                       break;
+               case IEEE80211_STYPE_CTS:
+               case IEEE80211_STYPE_ACK:
+                       hdrlen = 10;
+                       break;
+               }
+               break;
+       default:
+               /* Unknown frame type */
+               break;
+       }
+
+       /* sanity check the length */
+       if (datalen > IEEE80211_DATA_LEN + 12) {
+               printk(KERN_DEBUG "%s: oversized monitor frame, "
+                      "data length = %d\n", dev->name, datalen);
+               err = -EIO;
+               stats->rx_length_errors++;
+               goto update_stats;
+       }
+
+       skb = dev_alloc_skb(hdrlen + datalen);
+       if (!skb) {
+               printk(KERN_WARNING "%s: Cannot allocate skb for monitor frame\n",
+                      dev->name);
+               err = -ENOMEM;
+               goto drop;
+       }
+
+       /* Copy the 802.11 header to the skb */
+       memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen);
+       skb->mac.raw = skb->data;
+
+       /* If any, copy the data from the card to the skb */
+       if (datalen > 0) {
+               err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, datalen),
+                                      ALIGN(datalen, 2), rxfid,
+                                      HERMES_802_2_OFFSET);
+               if (err) {
+                       printk(KERN_ERR "%s: error %d reading monitor frame\n",
+                              dev->name, err);
+                       goto drop;
+               }
+       }
+
+       skb->dev = dev;
+       skb->ip_summed = CHECKSUM_NONE;
+       skb->pkt_type = PACKET_OTHERHOST;
+       skb->protocol = __constant_htons(ETH_P_802_2);
+       
+       dev->last_rx = jiffies;
+       stats->rx_packets++;
+       stats->rx_bytes += skb->len;
+
+       netif_rx(skb);
+       return;
+
+ drop:
+       dev_kfree_skb_irq(skb);
+ update_stats:
+       stats->rx_errors++;
+       stats->rx_dropped++;
+}
+
 static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
 {
        struct orinoco_private *priv = netdev_priv(dev);
        struct net_device_stats *stats = &priv->stats;
        struct iw_statistics *wstats = &priv->wstats;
        struct sk_buff *skb = NULL;
-       u16 rxfid, status;
-       int length, data_len, data_off;
-       char *p;
+       u16 rxfid, status, fc;
+       int length;
        struct hermes_rx_descriptor desc;
-       struct header_struct hdr;
-       struct ethhdr *eh;
+       struct ethhdr *hdr;
        int err;
 
        rxfid = hermes_read_regn(hw, RXFID);
@@ -1125,53 +884,46 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
        if (err) {
                printk(KERN_ERR "%s: error %d reading Rx descriptor. "
                       "Frame dropped.\n", dev->name, err);
-               stats->rx_errors++;
-               goto drop;
+               goto update_stats;
        }
 
        status = le16_to_cpu(desc.status);
 
-       if (status & HERMES_RXSTAT_ERR) {
-               if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
-                       wstats->discard.code++;
-                       DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
-                              dev->name);
-               } else {
-                       stats->rx_crc_errors++;
-                       DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name);
-               }
-               stats->rx_errors++;
-               goto drop;
+       if (status & HERMES_RXSTAT_BADCRC) {
+               DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n",
+                     dev->name);
+               stats->rx_crc_errors++;
+               goto update_stats;
        }
 
-       /* For now we ignore the 802.11 header completely, assuming
-           that the card's firmware has handled anything vital */
+       /* Handle frames in monitor mode */
+       if (priv->iw_mode == IW_MODE_MONITOR) {
+               orinoco_rx_monitor(dev, rxfid, &desc);
+               return;
+       }
 
-       err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr),
-                              rxfid, HERMES_802_3_OFFSET);
-       if (err) {
-               printk(KERN_ERR "%s: error %d reading frame header. "
-                      "Frame dropped.\n", dev->name, err);
-               stats->rx_errors++;
-               goto drop;
+       if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
+               DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
+                     dev->name);
+               wstats->discard.code++;
+               goto update_stats;
        }
 
-       length = ntohs(hdr.len);
-       
+       length = le16_to_cpu(desc.data_len);
+       fc = le16_to_cpu(desc.frame_ctl);
+
        /* 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. */
-               stats->rx_dropped++;
-               goto drop;
+               return;
        }
-       if (length > IEEE802_11_DATA_LEN) {
+       if (length > IEEE80211_DATA_LEN) {
                printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
                       dev->name, length);
                stats->rx_length_errors++;
-               stats->rx_errors++;
-               goto drop;
+               goto update_stats;
        }
 
        /* We need space for the packet data itself, plus an ethernet
@@ -1183,60 +935,53 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
        if (!skb) {
                printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
                       dev->name);
-               goto drop;
+               goto update_stats;
        }
 
-       skb_reserve(skb, 2); /* This way the IP header is aligned */
+       /* We'll prepend the header, so reserve space for it.  The worst
+          case is no decapsulation, when 802.3 header is prepended and
+          nothing is removed.  2 is for aligning the IP header.  */
+       skb_reserve(skb, ETH_HLEN + 2);
+
+       err = hermes_bap_pread(hw, IRQ_BAP, skb_put(skb, length),
+                              ALIGN(length, 2), rxfid,
+                              HERMES_802_2_OFFSET);
+       if (err) {
+               printk(KERN_ERR "%s: error %d reading frame. "
+                      "Frame dropped.\n", dev->name, err);
+               goto drop;
+       }
 
        /* Handle decapsulation
         * In most cases, the firmware tell us about SNAP frames.
         * For some reason, the SNAP frames sent by LinkSys APs
         * are not properly recognised by most firmwares.
         * So, check ourselves */
-       if (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
-           ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
-           is_ethersnap(&hdr)) {
+       if (length >= ENCAPS_OVERHEAD &&
+           (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
+            ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
+            is_ethersnap(skb->data))) {
                /* These indicate a SNAP within 802.2 LLC within
                   802.11 frame which we'll need to de-encapsulate to
                   the original EthernetII frame. */
-
-               if (length < ENCAPS_OVERHEAD) { /* No room for full LLC+SNAP */
-                       stats->rx_length_errors++;
-                       goto drop;
-               }
-
-               /* Remove SNAP header, reconstruct EthernetII frame */
-               data_len = length - ENCAPS_OVERHEAD;
-               data_off = HERMES_802_3_OFFSET + sizeof(hdr);
-
-               eh = (struct ethhdr *)skb_put(skb, ETH_HLEN);
-
-               memcpy(eh, &hdr, 2 * ETH_ALEN);
-               eh->h_proto = hdr.ethertype;
+               hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN - ENCAPS_OVERHEAD);
        } else {
-               /* All other cases indicate a genuine 802.3 frame.  No
-                  decapsulation needed.  We just throw the whole
-                  thing in, and hope the protocol layer can deal with
-                  it as 802.3 */
-               data_len = length;
-               data_off = HERMES_802_3_OFFSET;
-               /* FIXME: we re-read from the card data we already read here */
-       }
-
-       p = skb_put(skb, data_len);
-       err = hermes_bap_pread(hw, IRQ_BAP, p, ALIGN(data_len, 2),
-                              rxfid, data_off);
-       if (err) {
-               printk(KERN_ERR "%s: error %d reading frame. "
-                      "Frame dropped.\n", dev->name, err);
-               stats->rx_errors++;
-               goto drop;
+               /* 802.3 frame - prepend 802.3 header as is */
+               hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN);
+               hdr->h_proto = htons(length);
        }
+       memcpy(hdr->h_dest, desc.addr1, ETH_ALEN);
+       if (fc & IEEE80211_FCTL_FROMDS)
+               memcpy(hdr->h_source, desc.addr3, ETH_ALEN);
+       else
+               memcpy(hdr->h_source, desc.addr2, ETH_ALEN);
 
        dev->last_rx = jiffies;
        skb->dev = dev;
        skb->protocol = eth_type_trans(skb, dev);
        skb->ip_summed = CHECKSUM_NONE;
+       if (fc & IEEE80211_FCTL_TODS)
+               skb->pkt_type = PACKET_OTHERHOST;
        
        /* Process the wireless stats if needed */
        orinoco_stat_gather(dev, skb, &desc);
@@ -1249,11 +994,10 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
        return;
 
  drop: 
+       dev_kfree_skb_irq(skb);
+ update_stats:
+       stats->rx_errors++;
        stats->rx_dropped++;
-
-       if (skb)
-               dev_kfree_skb_irq(skb);
-       return;
 }
 
 /********************************************************************/
@@ -1457,6 +1201,9 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
                u16 newstatus;
                int connected;
 
+               if (priv->iw_mode == IW_MODE_MONITOR)
+                       break;
+
                if (len != sizeof(linkstatus)) {
                        printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n",
                               dev->name, len);
@@ -1537,8 +1284,10 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
                /* Read scan data */
                err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len,
                                       infofid, sizeof(info));
-               if (err)
+               if (err) {
+                       kfree(buf);
                        break;
+               }
 
 #ifdef ORINOCO_DEBUG
                {
@@ -2066,6 +1815,20 @@ static int __orinoco_program_rids(struct net_device *dev)
                }
        }
 
+       if (priv->iw_mode == IW_MODE_MONITOR) {
+               /* Enable monitor mode */
+               dev->type = ARPHRD_IEEE80211;
+               err = hermes_docmd_wait(hw, HERMES_CMD_TEST | 
+                                           HERMES_TEST_MONITOR, 0, NULL);
+       } else {
+               /* Disable monitor mode */
+               dev->type = ARPHRD_ETHER;
+               err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+                                           HERMES_TEST_STOP, 0, NULL);
+       }
+       if (err)
+               return err;
+
        /* Set promiscuity / multicast*/
        priv->promiscuous = 0;
        priv->mc_count = 0;
@@ -2414,6 +2177,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->broken_monitor = (firmver >= 0x80000);
 
                /* Tested with Agere firmware :
                 *      1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
@@ -2511,7 +2275,7 @@ static int orinoco_init(struct net_device *dev)
 
        /* No need to lock, the hw_unavailable flag is already set in
         * alloc_orinocodev() */
-       priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN;
+       priv->nicbuf_size = IEEE80211_FRAME_LEN + ETH_HLEN;
 
        /* Initialize the firmware */
        err = orinoco_reinit_firmware(dev);
@@ -2981,6 +2745,15 @@ static int orinoco_ioctl_setmode(struct net_device *dev,
        case IW_MODE_INFRA:
                break;
 
+       case IW_MODE_MONITOR:
+               if (priv->broken_monitor && !force_monitor) {
+                       printk(KERN_WARNING "%s: Monitor mode support is "
+                              "buggy in this firmware, not enabling\n",
+                              dev->name);
+                       err = -EOPNOTSUPP;
+               }
+               break;
+
        default:
                err = -EOPNOTSUPP;
                break;
@@ -3356,11 +3129,9 @@ static int orinoco_ioctl_setfreq(struct net_device *dev,
        unsigned long flags;
        int err = -EINPROGRESS;         /* Call commit handler */
 
-       /* We can only use this in Ad-Hoc demo mode to set the operating
-        * frequency, or in IBSS mode to set the frequency where the IBSS
-        * will be created - Jean II */
-       if (priv->iw_mode != IW_MODE_ADHOC)
-               return -EOPNOTSUPP;
+       /* In infrastructure mode the AP sets the channel */
+       if (priv->iw_mode == IW_MODE_INFRA)
+               return -EBUSY;
 
        if ( (frq->e == 0) && (frq->m <= 1000) ) {
                /* Setting by channel number */
@@ -3384,7 +3155,15 @@ static int orinoco_ioctl_setfreq(struct net_device *dev,
 
        if (orinoco_lock(priv, &flags) != 0)
                return -EBUSY;
+
        priv->channel = chan;
+       if (priv->iw_mode == IW_MODE_MONITOR) {
+               /* Fast channel change - no commit if successful */
+               hermes_t *hw = &priv->hw;
+               err = hermes_docmd_wait(hw, HERMES_CMD_TEST |
+                                           HERMES_TEST_SET_CHANNEL,
+                                       chan, NULL);
+       }
        orinoco_unlock(priv, &flags);
 
        return err;
@@ -4244,7 +4023,8 @@ static int orinoco_ioctl_setscan(struct net_device *dev,
 }
 
 /* Translate scan data returned from the card to a card independant
- * format that the Wireless Tools will understand - Jean II */
+ * format that the Wireless Tools will understand - Jean II
+ * Return message length or -errno for fatal errors */
 static inline int orinoco_translate_scan(struct net_device *dev,
                                         char *buffer,
                                         char *scan,
@@ -4284,13 +4064,19 @@ static inline int orinoco_translate_scan(struct net_device *dev,
                break;
        case FIRMWARE_TYPE_INTERSIL:
                offset = 4;
-               if (priv->has_hostscan)
-                       atom_len = scan[0] + (scan[1] << 8);
-               else
+               if (priv->has_hostscan) {
+                       atom_len = le16_to_cpup((u16 *)scan);
+                       /* Sanity check for atom_len */
+                       if (atom_len < sizeof(struct prism2_scan_apinfo)) {
+                               printk(KERN_ERR "%s: Invalid atom_len in scan data: %d\n",
+                               dev->name, atom_len);
+                               return -EIO;
+                       }
+               } else
                        atom_len = offsetof(struct prism2_scan_apinfo, atim);
                break;
        default:
-               return 0;
+               return -EOPNOTSUPP;
        }
 
        /* Check that we got an whole number of atoms */
@@ -4298,7 +4084,7 @@ static inline int orinoco_translate_scan(struct net_device *dev,
                printk(KERN_ERR "%s: Unexpected scan data length %d, "
                       "atom_len %d, offset %d\n", dev->name, scan_len,
                       atom_len, offset);
-               return 0;
+               return -EIO;
        }
 
        /* Read the entries one by one */
@@ -4433,33 +4219,41 @@ static int orinoco_ioctl_getscan(struct net_device *dev,
                /* We have some results to push back to user space */
 
                /* Translate to WE format */
-               srq->length = orinoco_translate_scan(dev, extra,
-                                                    priv->scan_result,
-                                                    priv->scan_len);
-
-               /* Return flags */
-               srq->flags = (__u16) priv->scan_mode;
+               int ret = orinoco_translate_scan(dev, extra,
+                                                priv->scan_result,
+                                                priv->scan_len);
+
+               if (ret < 0) {
+                       err = ret;
+                       kfree(priv->scan_result);
+                       priv->scan_result = NULL;
+               } else {
+                       srq->length = ret;
 
-               /* Results are here, so scan no longer in progress */
-               priv->scan_inprogress = 0;
+                       /* Return flags */
+                       srq->flags = (__u16) priv->scan_mode;
 
-               /* In any case, Scan results will be cleaned up in the
-                * reset function and when exiting the driver.
-                * The person triggering the scanning may never come to
-                * pick the results, so we need to do it in those places.
-                * Jean II */
+                       /* In any case, Scan results will be cleaned up in the
+                        * reset function and when exiting the driver.
+                        * The person triggering the scanning may never come to
+                        * pick the results, so we need to do it in those places.
+                        * Jean II */
 
 #ifdef SCAN_SINGLE_READ
-               /* If you enable this option, only one client (the first
-                * one) will be able to read the result (and only one
-                * time). If there is multiple concurent clients that
-                * want to read scan results, this behavior is not
-                * advisable - Jean II */
-               kfree(priv->scan_result);
-               priv->scan_result = NULL;
+                       /* If you enable this option, only one client (the first
+                        * one) will be able to read the result (and only one
+                        * time). If there is multiple concurent clients that
+                        * want to read scan results, this behavior is not
+                        * advisable - Jean II */
+                       kfree(priv->scan_result);
+                       priv->scan_result = NULL;
 #endif /* SCAN_SINGLE_READ */
-               /* Here, if too much time has elapsed since last scan,
-                * we may want to clean up scan results... - Jean II */
+                       /* Here, if too much time has elapsed since last scan,
+                        * we may want to clean up scan results... - Jean II */
+               }
+
+               /* Scan is no longer in progress */
+               priv->scan_inprogress = 0;
        }
          
        orinoco_unlock(priv, &flags);
@@ -4546,36 +4340,36 @@ static const struct iw_priv_args orinoco_privtab[] = {
  */
 
 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) orinoco_ioctl_setspy,
-       [SIOCGIWSPY   -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getspy,
-       [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,
+       [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) orinoco_ioctl_setspy,
+       [SIOCGIWSPY   -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getspy,
+       [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,
 };
 
 
@@ -4583,15 +4377,15 @@ static const iw_handler orinoco_handler[] = {
   Added typecasting since we no longer use iwreq_data -- Moustafa
  */
 static const iw_handler        orinoco_private_handler[] = {
-       [0] (iw_handler) orinoco_ioctl_reset,
-       [1] (iw_handler) orinoco_ioctl_reset,
-       [2] (iw_handler) orinoco_ioctl_setport3,
-       [3] (iw_handler) orinoco_ioctl_getport3,
-       [4] (iw_handler) orinoco_ioctl_setpreamble,
-       [5] (iw_handler) orinoco_ioctl_getpreamble,
-       [6] (iw_handler) orinoco_ioctl_setibssport,
-       [7] (iw_handler) orinoco_ioctl_getibssport,
-       [9] (iw_handler) orinoco_ioctl_getrid,
+       [0] (iw_handler) orinoco_ioctl_reset,
+       [1] (iw_handler) orinoco_ioctl_reset,
+       [2] (iw_handler) orinoco_ioctl_setport3,
+       [3] (iw_handler) orinoco_ioctl_getport3,
+       [4] (iw_handler) orinoco_ioctl_setpreamble,
+       [5] (iw_handler) orinoco_ioctl_getpreamble,
+       [6] (iw_handler) orinoco_ioctl_setibssport,
+       [7] (iw_handler) orinoco_ioctl_getibssport,
+       [9] (iw_handler) orinoco_ioctl_getrid,
 };
 
 static const struct iw_handler_def orinoco_handler_def = {