]> err.no Git - linux-2.6/blobdiff - drivers/net/wireless/orinoco.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6
[linux-2.6] / drivers / net / wireless / orinoco.c
index 38fd8623a444e01c773d0ea6fa563af3b8373f7e..aabcdc2be05ea925fee9d685bbc4d38bcd8c5e0f 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).
@@ -499,6 +126,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               */
 /********************************************************************/
@@ -619,7 +250,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 +260,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 +301,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);
@@ -842,7 +491,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 +753,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 +883,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) {
                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 +934,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 +993,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 +1200,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);
@@ -2066,6 +1812,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 +2174,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
@@ -2981,6 +2742,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 +3126,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 +3152,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;