Commit 9758d0f0 authored by Jeff Garzik's avatar Jeff Garzik Committed by Jeff Garzik

Merge /spare/repo/netdev-2.6/ branch 'orinoco'

parents 0dd3c781 1a9fe638
...@@ -46,382 +46,9 @@ ...@@ -46,382 +46,9 @@
* under either the MPL or the GPL. */ * 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 * 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 * o Handle de-encapsulation within network layer, provide 802.11
* headers (patch from Thomas 'Dent' Mirlacher) * headers (patch from Thomas 'Dent' Mirlacher)
* o RF monitor mode support
* o Fix possible races in SPY handling. * o Fix possible races in SPY handling.
* o Disconnect wireless extensions from fundamental configuration. * o Disconnect wireless extensions from fundamental configuration.
* o (maybe) Software WEP support (patch from Stano Meduna). * o (maybe) Software WEP support (patch from Stano Meduna).
...@@ -462,7 +89,10 @@ ...@@ -462,7 +89,10 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <linux/etherdevice.h> #include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/wireless.h> #include <linux/wireless.h>
#include <net/iw_handler.h>
#include <net/ieee80211.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -496,6 +126,10 @@ static int ignore_disconnect; /* = 0 */ ...@@ -496,6 +126,10 @@ static int ignore_disconnect; /* = 0 */
module_param(ignore_disconnect, int, 0644); module_param(ignore_disconnect, int, 0644);
MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer"); 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 */ /* Compile time configuration and compatibility stuff */
/********************************************************************/ /********************************************************************/
...@@ -511,6 +145,10 @@ MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer ...@@ -511,6 +145,10 @@ MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer
/* Internal constants */ /* Internal constants */
/********************************************************************/ /********************************************************************/
/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */
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_MIN_MTU 256
#define ORINOCO_MAX_MTU (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD) #define ORINOCO_MAX_MTU (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD)
...@@ -537,6 +175,11 @@ MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer ...@@ -537,6 +175,11 @@ MODULE_PARM_DESC(ignore_disconnect, "Don't report lost link to the network layer
| HERMES_EV_WTERR | HERMES_EV_INFO \ | HERMES_EV_WTERR | HERMES_EV_INFO \
| HERMES_EV_INFDROP ) | HERMES_EV_INFDROP )
#define MAX_RID_LEN 1024
static const struct iw_handler_def orinoco_handler_def;
static struct ethtool_ops orinoco_ethtool_ops;
/********************************************************************/ /********************************************************************/
/* Data tables */ /* Data tables */
/********************************************************************/ /********************************************************************/
...@@ -571,26 +214,45 @@ static struct { ...@@ -571,26 +214,45 @@ static struct {
/* Data types */ /* Data types */
/********************************************************************/ /********************************************************************/
struct header_struct { /* Used in Event handling.
/* 802.3 */ * We avoid nested structres as they break on ARM -- Moustafa */
u8 dest[ETH_ALEN]; struct hermes_tx_descriptor_802_11 {
u8 src[ETH_ALEN]; /* hermes_tx_descriptor */
u16 len; u16 status;
/* 802.2 */ u16 reserved1;
u16 reserved2;
u32 sw_support;
u8 retry_count;
u8 tx_rate;
u16 tx_control;
/* ieee802_11_hdr */
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];
u16 data_len;
/* ethhdr */
unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
unsigned char h_source[ETH_ALEN]; /* source ether addr */
unsigned short h_proto; /* packet type ID field */
/* p8022_hdr */
u8 dsap; u8 dsap;
u8 ssap; u8 ssap;
u8 ctrl; u8 ctrl;
/* SNAP */
u8 oui[3]; u8 oui[3];
u16 ethertype; u16 ethertype;
} __attribute__ ((packed)); } __attribute__ ((packed));
/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ /* Rx frame header except compatibility 802.3 header */
u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00};
#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2)
struct hermes_rx_descriptor { struct hermes_rx_descriptor {
/* Control */
u16 status; u16 status;
u32 time; u32 time;
u8 silence; u8 silence;
...@@ -598,13 +260,24 @@ struct hermes_rx_descriptor { ...@@ -598,13 +260,24 @@ struct hermes_rx_descriptor {
u8 rate; u8 rate;
u8 rxflow; u8 rxflow;
u32 reserved; 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)); } __attribute__ ((packed));
/********************************************************************/ /********************************************************************/
/* Function prototypes */ /* Function prototypes */
/********************************************************************/ /********************************************************************/
static int orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static int __orinoco_program_rids(struct net_device *dev); static int __orinoco_program_rids(struct net_device *dev);
static void __orinoco_set_multicast_list(struct net_device *dev); static void __orinoco_set_multicast_list(struct net_device *dev);
...@@ -628,6 +301,10 @@ static inline void set_port_type(struct orinoco_private *priv) ...@@ -628,6 +301,10 @@ static inline void set_port_type(struct orinoco_private *priv)
priv->createibss = 1; priv->createibss = 1;
} }
break; break;
case IW_MODE_MONITOR:
priv->port_type = 3;
priv->createibss = 0;
break;
default: default:
printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n", printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n",
priv->ndev->name); priv->ndev->name);
...@@ -814,7 +491,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -814,7 +491,7 @@ static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
return 1; 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, /* Oops, the firmware hasn't established a connection,
silently drop the packet (this seems to be the silently drop the packet (this seems to be the
safest approach). */ safest approach). */
...@@ -951,26 +628,55 @@ static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) ...@@ -951,26 +628,55 @@ static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
struct net_device_stats *stats = &priv->stats; struct net_device_stats *stats = &priv->stats;
u16 fid = hermes_read_regn(hw, TXCOMPLFID); u16 fid = hermes_read_regn(hw, TXCOMPLFID);
struct hermes_tx_descriptor desc; struct hermes_tx_descriptor_802_11 hdr;
int err = 0; int err = 0;
if (fid == DUMMY_FID) if (fid == DUMMY_FID)
return; /* Nothing's really happened */ return; /* Nothing's really happened */
err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), fid, 0); /* Read the frame header */
err = hermes_bap_pread(hw, IRQ_BAP, &hdr,
sizeof(struct hermes_tx_descriptor) +
sizeof(struct ieee80211_hdr),
fid, 0);
hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
stats->tx_errors++;
if (err) { if (err) {
printk(KERN_WARNING "%s: Unable to read descriptor on Tx error " printk(KERN_WARNING "%s: Unable to read descriptor on Tx error "
"(FID=%04X error %d)\n", "(FID=%04X error %d)\n",
dev->name, fid, err); dev->name, fid, err);
} else { return;
DEBUG(1, "%s: Tx error, status %d\n",
dev->name, le16_to_cpu(desc.status));
} }
stats->tx_errors++; DEBUG(1, "%s: Tx error, err %d (FID=%04X)\n", dev->name,
err, fid);
/* We produce a TXDROP event only for retry or lifetime
* exceeded, because that's the only status that really mean
* that this particular node went away.
* Other errors means that *we* screwed up. - Jean II */
hdr.status = le16_to_cpu(hdr.status);
if (hdr.status & (HERMES_TXSTAT_RETRYERR | HERMES_TXSTAT_AGEDERR)) {
union iwreq_data wrqu;
/* Copy 802.11 dest address.
* We use the 802.11 header because the frame may
* not be 802.3 or may be mangled...
* In Ad-Hoc mode, it will be the node address.
* In managed mode, it will be most likely the AP addr
* User space will figure out how to convert it to
* whatever it needs (IP address or else).
* - Jean II */
memcpy(wrqu.addr.sa_data, hdr.addr1, ETH_ALEN);
wrqu.addr.sa_family = ARPHRD_ETHER;
/* Send event to user space */
wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL);
}
netif_wake_queue(dev); netif_wake_queue(dev);
hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
} }
static void orinoco_tx_timeout(struct net_device *dev) static void orinoco_tx_timeout(struct net_device *dev)
...@@ -1047,18 +753,127 @@ static void orinoco_stat_gather(struct net_device *dev, ...@@ -1047,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) static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
struct net_device_stats *stats = &priv->stats; struct net_device_stats *stats = &priv->stats;
struct iw_statistics *wstats = &priv->wstats; struct iw_statistics *wstats = &priv->wstats;
struct sk_buff *skb = NULL; struct sk_buff *skb = NULL;
u16 rxfid, status; u16 rxfid, status, fc;
int length, data_len, data_off; int length;
char *p;
struct hermes_rx_descriptor desc; struct hermes_rx_descriptor desc;
struct header_struct hdr; struct ethhdr *hdr;
struct ethhdr *eh;
int err; int err;
rxfid = hermes_read_regn(hw, RXFID); rxfid = hermes_read_regn(hw, RXFID);
...@@ -1068,53 +883,46 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) ...@@ -1068,53 +883,46 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
if (err) { if (err) {
printk(KERN_ERR "%s: error %d reading Rx descriptor. " printk(KERN_ERR "%s: error %d reading Rx descriptor. "
"Frame dropped.\n", dev->name, err); "Frame dropped.\n", dev->name, err);
stats->rx_errors++; goto update_stats;
goto drop;
} }
status = le16_to_cpu(desc.status); status = le16_to_cpu(desc.status);
if (status & HERMES_RXSTAT_ERR) { if (status & HERMES_RXSTAT_BADCRC) {
if (status & HERMES_RXSTAT_UNDECRYPTABLE) { DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n",
wstats->discard.code++; dev->name);
DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n", stats->rx_crc_errors++;
dev->name); goto update_stats;
} else {
stats->rx_crc_errors++;
DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name);
}
stats->rx_errors++;
goto drop;
} }
/* For now we ignore the 802.11 header completely, assuming /* Handle frames in monitor mode */
that the card's firmware has handled anything vital */ if (priv->iw_mode == IW_MODE_MONITOR) {
orinoco_rx_monitor(dev, rxfid, &desc);
return;
}
err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr), if (status & HERMES_RXSTAT_UNDECRYPTABLE) {
rxfid, HERMES_802_3_OFFSET); DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n",
if (err) { dev->name);
printk(KERN_ERR "%s: error %d reading frame header. " wstats->discard.code++;
"Frame dropped.\n", dev->name, err); goto update_stats;
stats->rx_errors++;
goto drop;
} }
length = ntohs(hdr.len); length = le16_to_cpu(desc.data_len);
fc = le16_to_cpu(desc.frame_ctl);
/* Sanity checks */ /* Sanity checks */
if (length < 3) { /* No for even an 802.2 LLC header */ if (length < 3) { /* No for even an 802.2 LLC header */
/* At least on Symbol firmware with PCF we get quite a /* At least on Symbol firmware with PCF we get quite a
lot of these legitimately - Poll frames with no lot of these legitimately - Poll frames with no
data. */ data. */
stats->rx_dropped++; return;
goto drop;
} }
if (length > IEEE802_11_DATA_LEN) { if (length > IEEE802_11_DATA_LEN) {
printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n", printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n",
dev->name, length); dev->name, length);
stats->rx_length_errors++; stats->rx_length_errors++;
stats->rx_errors++; goto update_stats;
goto drop;
} }
/* We need space for the packet data itself, plus an ethernet /* We need space for the packet data itself, plus an ethernet
...@@ -1126,60 +934,53 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) ...@@ -1126,60 +934,53 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
if (!skb) { if (!skb) {
printk(KERN_WARNING "%s: Can't allocate skb for Rx\n", printk(KERN_WARNING "%s: Can't allocate skb for Rx\n",
dev->name); 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 /* Handle decapsulation
* In most cases, the firmware tell us about SNAP frames. * In most cases, the firmware tell us about SNAP frames.
* For some reason, the SNAP frames sent by LinkSys APs * For some reason, the SNAP frames sent by LinkSys APs
* are not properly recognised by most firmwares. * are not properly recognised by most firmwares.
* So, check ourselves */ * So, check ourselves */
if (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || if (length >= ENCAPS_OVERHEAD &&
((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
is_ethersnap(&hdr)) { ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
is_ethersnap(skb->data))) {
/* These indicate a SNAP within 802.2 LLC within /* These indicate a SNAP within 802.2 LLC within
802.11 frame which we'll need to de-encapsulate to 802.11 frame which we'll need to de-encapsulate to
the original EthernetII frame. */ the original EthernetII frame. */
hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN - ENCAPS_OVERHEAD);
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;
} else { } else {
/* All other cases indicate a genuine 802.3 frame. No /* 802.3 frame - prepend 802.3 header as is */
decapsulation needed. We just throw the whole hdr = (struct ethhdr *)skb_push(skb, ETH_HLEN);
thing in, and hope the protocol layer can deal with hdr->h_proto = htons(length);
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;
} }
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; dev->last_rx = jiffies;
skb->dev = dev; skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev); skb->protocol = eth_type_trans(skb, dev);
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = CHECKSUM_NONE;
if (fc & IEEE80211_FCTL_TODS)
skb->pkt_type = PACKET_OTHERHOST;
/* Process the wireless stats if needed */ /* Process the wireless stats if needed */
orinoco_stat_gather(dev, skb, &desc); orinoco_stat_gather(dev, skb, &desc);
...@@ -1192,11 +993,10 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) ...@@ -1192,11 +993,10 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
return; return;
drop: drop:
dev_kfree_skb_irq(skb);
update_stats:
stats->rx_errors++;
stats->rx_dropped++; stats->rx_dropped++;
if (skb)
dev_kfree_skb_irq(skb);
return;
} }
/********************************************************************/ /********************************************************************/
...@@ -1240,6 +1040,99 @@ static void print_linkstatus(struct net_device *dev, u16 status) ...@@ -1240,6 +1040,99 @@ static void print_linkstatus(struct net_device *dev, u16 status)
dev->name, s, status); dev->name, s, status);
} }
/* Search scan results for requested BSSID, join it if found */
static void orinoco_join_ap(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);
struct hermes *hw = &priv->hw;
int err;
unsigned long flags;
struct join_req {
u8 bssid[ETH_ALEN];
u16 channel;
} __attribute__ ((packed)) req;
const int atom_len = offsetof(struct prism2_scan_apinfo, atim);
struct prism2_scan_apinfo *atom;
int offset = 4;
u8 *buf;
u16 len;
/* Allocate buffer for scan results */
buf = kmalloc(MAX_SCAN_LEN, GFP_KERNEL);
if (! buf)
return;
if (orinoco_lock(priv, &flags) != 0)
goto out;
/* Sanity checks in case user changed something in the meantime */
if (! priv->bssid_fixed)
goto out;
if (strlen(priv->desired_essid) == 0)
goto out;
/* Read scan results from the firmware */
err = hermes_read_ltv(hw, USER_BAP,
HERMES_RID_SCANRESULTSTABLE,
MAX_SCAN_LEN, &len, buf);
if (err) {
printk(KERN_ERR "%s: Cannot read scan results\n",
dev->name);
goto out;
}
len = HERMES_RECLEN_TO_BYTES(len);
/* Go through the scan results looking for the channel of the AP
* we were requested to join */
for (; offset + atom_len <= len; offset += atom_len) {
atom = (struct prism2_scan_apinfo *) (buf + offset);
if (memcmp(&atom->bssid, priv->desired_bssid, ETH_ALEN) == 0)
goto found;
}
DEBUG(1, "%s: Requested AP not found in scan results\n",
dev->name);
goto out;
found:
memcpy(req.bssid, priv->desired_bssid, ETH_ALEN);
req.channel = atom->channel; /* both are little-endian */
err = HERMES_WRITE_RECORD(hw, USER_BAP, HERMES_RID_CNFJOINREQUEST,
&req);
if (err)
printk(KERN_ERR "%s: Error issuing join request\n", dev->name);
out:
kfree(buf);
orinoco_unlock(priv, &flags);
}
/* Send new BSSID to userspace */
static void orinoco_send_wevents(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);
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)
return;
wrqu.ap_addr.sa_family = ARPHRD_ETHER;
/* Send event to user space */
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
orinoco_unlock(priv, &flags);
}
static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
...@@ -1307,6 +1200,9 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) ...@@ -1307,6 +1200,9 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
u16 newstatus; u16 newstatus;
int connected; int connected;
if (priv->iw_mode == IW_MODE_MONITOR)
break;
if (len != sizeof(linkstatus)) { if (len != sizeof(linkstatus)) {
printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n", printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n",
dev->name, len); dev->name, len);
...@@ -1319,6 +1215,15 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) ...@@ -1319,6 +1215,15 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
break; break;
newstatus = le16_to_cpu(linkstatus.linkstatus); newstatus = le16_to_cpu(linkstatus.linkstatus);
/* Symbol firmware uses "out of range" to signal that
* the hostscan frame can be requested. */
if (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE &&
priv->firmware_type == FIRMWARE_TYPE_SYMBOL &&
priv->has_hostscan && priv->scan_inprogress) {
hermes_inquire(hw, HERMES_INQ_HOSTSCAN_SYMBOL);
break;
}
connected = (newstatus == HERMES_LINKSTATUS_CONNECTED) connected = (newstatus == HERMES_LINKSTATUS_CONNECTED)
|| (newstatus == HERMES_LINKSTATUS_AP_CHANGE) || (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
|| (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE); || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE);
...@@ -1328,12 +1233,89 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) ...@@ -1328,12 +1233,89 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
else if (!ignore_disconnect) else if (!ignore_disconnect)
netif_carrier_off(dev); netif_carrier_off(dev);
if (newstatus != priv->last_linkstatus) if (newstatus != priv->last_linkstatus) {
priv->last_linkstatus = newstatus;
print_linkstatus(dev, newstatus); print_linkstatus(dev, newstatus);
/* The info frame contains only one word which is the
* status (see hermes.h). The status is pretty boring
* in itself, that's why we export the new BSSID...
* Jean II */
schedule_work(&priv->wevent_work);
}
}
break;
case HERMES_INQ_SCAN:
if (!priv->scan_inprogress && priv->bssid_fixed &&
priv->firmware_type == FIRMWARE_TYPE_INTERSIL) {
schedule_work(&priv->join_work);
break;
}
/* fall through */
case HERMES_INQ_HOSTSCAN:
case HERMES_INQ_HOSTSCAN_SYMBOL: {
/* Result of a scanning. Contains information about
* cells in the vicinity - Jean II */
union iwreq_data wrqu;
unsigned char *buf;
/* Sanity check */
if (len > 4096) {
printk(KERN_WARNING "%s: Scan results too large (%d bytes)\n",
dev->name, len);
break;
}
/* We are a strict producer. If the previous scan results
* have not been consumed, we just have to drop this
* frame. We can't remove the previous results ourselves,
* that would be *very* racy... Jean II */
if (priv->scan_result != NULL) {
printk(KERN_WARNING "%s: Previous scan results not consumed, dropping info frame.\n", dev->name);
break;
}
/* Allocate buffer for results */
buf = kmalloc(len, GFP_ATOMIC);
if (buf == NULL)
/* No memory, so can't printk()... */
break;
priv->last_linkstatus = newstatus; /* Read scan data */
err = hermes_bap_pread(hw, IRQ_BAP, (void *) buf, len,
infofid, sizeof(info));
if (err)
break;
#ifdef ORINOCO_DEBUG
{
int i;
printk(KERN_DEBUG "Scan result [%02X", buf[0]);
for(i = 1; i < (len * 2); i++)
printk(":%02X", buf[i]);
printk("]\n");
}
#endif /* ORINOCO_DEBUG */
/* Allow the clients to access the results */
priv->scan_len = len;
priv->scan_result = buf;
/* Send an empty event to user space.
* We don't send the received data on the event because
* it would require us to do complex transcoding, and
* we want to minimise the work done in the irq handler
* Use a request to extract the data - Jean II */
wrqu.data.length = 0;
wrqu.data.flags = 0;
wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
} }
break; break;
case HERMES_INQ_SEC_STAT_AGERE:
/* Security status (Agere specific) */
/* Ignore this frame for now */
if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
break;
/* fall through */
default: default:
printk(KERN_DEBUG "%s: Unknown information frame received: " printk(KERN_DEBUG "%s: Unknown information frame received: "
"type 0x%04x, length %d\n", dev->name, type, len); "type 0x%04x, length %d\n", dev->name, type, len);
...@@ -1470,6 +1452,36 @@ static int __orinoco_hw_set_bitrate(struct orinoco_private *priv) ...@@ -1470,6 +1452,36 @@ static int __orinoco_hw_set_bitrate(struct orinoco_private *priv)
return err; return err;
} }
/* Set fixed AP address */
static int __orinoco_hw_set_wap(struct orinoco_private *priv)
{
int roaming_flag;
int err = 0;
hermes_t *hw = &priv->hw;
switch (priv->firmware_type) {
case FIRMWARE_TYPE_AGERE:
/* not supported */
break;
case FIRMWARE_TYPE_INTERSIL:
if (priv->bssid_fixed)
roaming_flag = 2;
else
roaming_flag = 1;
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFROAMINGMODE,
roaming_flag);
break;
case FIRMWARE_TYPE_SYMBOL:
err = HERMES_WRITE_RECORD(hw, USER_BAP,
HERMES_RID_CNFMANDATORYBSSID_SYMBOL,
&priv->desired_bssid);
break;
}
return err;
}
/* Change the WEP keys and/or the current keys. Can be called /* 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_wep() or directly from
* orinoco_ioctl_setiwencode(). In the later case the association * orinoco_ioctl_setiwencode(). In the later case the association
...@@ -1655,6 +1667,13 @@ static int __orinoco_program_rids(struct net_device *dev) ...@@ -1655,6 +1667,13 @@ static int __orinoco_program_rids(struct net_device *dev)
} }
} }
/* Set the desired BSSID */
err = __orinoco_hw_set_wap(priv);
if (err) {
printk(KERN_ERR "%s: Error %d setting AP address\n",
dev->name, err);
return err;
}
/* Set the desired ESSID */ /* Set the desired ESSID */
idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); idbuf.len = cpu_to_le16(strlen(priv->desired_essid));
memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val));
...@@ -1793,7 +1812,21 @@ static int __orinoco_program_rids(struct net_device *dev) ...@@ -1793,7 +1812,21 @@ static int __orinoco_program_rids(struct net_device *dev)
} }
} }
/* Set promiscuity / multicast*/ 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->promiscuous = 0;
priv->mc_count = 0; priv->mc_count = 0;
__orinoco_set_multicast_list(dev); /* FIXME: what about the xmit_lock */ __orinoco_set_multicast_list(dev); /* FIXME: what about the xmit_lock */
...@@ -1869,55 +1902,6 @@ __orinoco_set_multicast_list(struct net_device *dev) ...@@ -1869,55 +1902,6 @@ __orinoco_set_multicast_list(struct net_device *dev)
dev->flags &= ~IFF_PROMISC; dev->flags &= ~IFF_PROMISC;
} }
static int orinoco_reconfigure(struct net_device *dev)
{
struct orinoco_private *priv = netdev_priv(dev);
struct hermes *hw = &priv->hw;
unsigned long flags;
int err = 0;
if (priv->broken_disableport) {
schedule_work(&priv->reset_work);
return 0;
}
if (orinoco_lock(priv, &flags) != 0)
return -EBUSY;
err = hermes_disable_port(hw, 0);
if (err) {
printk(KERN_WARNING "%s: Unable to disable port while reconfiguring card\n",
dev->name);
priv->broken_disableport = 1;
goto out;
}
err = __orinoco_program_rids(dev);
if (err) {
printk(KERN_WARNING "%s: Unable to reconfigure card\n",
dev->name);
goto out;
}
err = hermes_enable_port(hw, 0);
if (err) {
printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n",
dev->name);
goto out;
}
out:
if (err) {
printk(KERN_WARNING "%s: Resetting instead...\n", dev->name);
schedule_work(&priv->reset_work);
err = 0;
}
orinoco_unlock(priv, &flags);
return err;
}
/* This must be called from user context, without locks held - use /* This must be called from user context, without locks held - use
* schedule_work() */ * schedule_work() */
static void orinoco_reset(struct net_device *dev) static void orinoco_reset(struct net_device *dev)
...@@ -1946,6 +1930,11 @@ static void orinoco_reset(struct net_device *dev) ...@@ -1946,6 +1930,11 @@ static void orinoco_reset(struct net_device *dev)
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
/* Scanning support: Cleanup of driver struct */
kfree(priv->scan_result);
priv->scan_result = NULL;
priv->scan_inprogress = 0;
if (priv->hard_reset) { if (priv->hard_reset) {
err = (*priv->hard_reset)(priv); err = (*priv->hard_reset)(priv);
if (err) { if (err) {
...@@ -2184,6 +2173,8 @@ static int determine_firmware(struct net_device *dev) ...@@ -2184,6 +2173,8 @@ static int determine_firmware(struct net_device *dev)
priv->has_mwo = (firmver >= 0x60000); priv->has_mwo = (firmver >= 0x60000);
priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */
priv->ibss_port = 1; priv->ibss_port = 1;
priv->has_hostscan = (firmver >= 0x8000a);
priv->broken_monitor = (firmver >= 0x80000);
/* Tested with Agere firmware : /* Tested with Agere firmware :
* 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II
...@@ -2229,6 +2220,8 @@ static int determine_firmware(struct net_device *dev) ...@@ -2229,6 +2220,8 @@ static int determine_firmware(struct net_device *dev)
priv->ibss_port = 4; priv->ibss_port = 4;
priv->broken_disableport = (firmver == 0x25013) || priv->broken_disableport = (firmver == 0x25013) ||
(firmver >= 0x30000 && firmver <= 0x31000); (firmver >= 0x30000 && firmver <= 0x31000);
priv->has_hostscan = (firmver >= 0x31001) ||
(firmver >= 0x29057 && firmver < 0x30000);
/* Tested with Intel firmware : 0x20015 => Jean II */ /* Tested with Intel firmware : 0x20015 => Jean II */
/* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */ /* Tested with 3Com firmware : 0x15012 & 0x22001 => Jean II */
break; break;
...@@ -2248,6 +2241,7 @@ static int determine_firmware(struct net_device *dev) ...@@ -2248,6 +2241,7 @@ static int determine_firmware(struct net_device *dev)
priv->has_ibss = (firmver >= 0x000700); /* FIXME */ priv->has_ibss = (firmver >= 0x000700); /* FIXME */
priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); priv->has_big_wep = priv->has_wep = (firmver >= 0x000800);
priv->has_pm = (firmver >= 0x000700); priv->has_pm = (firmver >= 0x000700);
priv->has_hostscan = (firmver >= 0x010301);
if (firmver >= 0x000800) if (firmver >= 0x000800)
priv->ibss_port = 0; priv->ibss_port = 0;
...@@ -2456,8 +2450,9 @@ struct net_device *alloc_orinocodev(int sizeof_card, ...@@ -2456,8 +2450,9 @@ struct net_device *alloc_orinocodev(int sizeof_card,
dev->tx_timeout = orinoco_tx_timeout; dev->tx_timeout = orinoco_tx_timeout;
dev->watchdog_timeo = HZ; /* 1 second timeout */ dev->watchdog_timeo = HZ; /* 1 second timeout */
dev->get_stats = orinoco_get_stats; dev->get_stats = orinoco_get_stats;
dev->ethtool_ops = &orinoco_ethtool_ops;
dev->get_wireless_stats = orinoco_get_wireless_stats; dev->get_wireless_stats = orinoco_get_wireless_stats;
dev->do_ioctl = orinoco_ioctl; dev->wireless_handlers = (struct iw_handler_def *)&orinoco_handler_def;
dev->change_mtu = orinoco_change_mtu; dev->change_mtu = orinoco_change_mtu;
dev->set_multicast_list = orinoco_set_multicast_list; dev->set_multicast_list = orinoco_set_multicast_list;
/* we use the default eth_mac_addr for setting the MAC addr */ /* we use the default eth_mac_addr for setting the MAC addr */
...@@ -2473,6 +2468,8 @@ struct net_device *alloc_orinocodev(int sizeof_card, ...@@ -2473,6 +2468,8 @@ struct net_device *alloc_orinocodev(int sizeof_card,
* before anything else touches the * before anything else touches the
* hardware */ * hardware */
INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev); INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev);
INIT_WORK(&priv->join_work, (void (*)(void *))orinoco_join_ap, dev);
INIT_WORK(&priv->wevent_work, (void (*)(void *))orinoco_send_wevents, dev);
netif_carrier_off(dev); netif_carrier_off(dev);
priv->last_linkstatus = 0xffff; priv->last_linkstatus = 0xffff;
...@@ -2483,6 +2480,9 @@ struct net_device *alloc_orinocodev(int sizeof_card, ...@@ -2483,6 +2480,9 @@ struct net_device *alloc_orinocodev(int sizeof_card,
void free_orinocodev(struct net_device *dev) void free_orinocodev(struct net_device *dev)
{ {
struct orinoco_private *priv = netdev_priv(dev);
kfree(priv->scan_result);
free_netdev(dev); free_netdev(dev);
} }
...@@ -2490,24 +2490,6 @@ void free_orinocodev(struct net_device *dev) ...@@ -2490,24 +2490,6 @@ void free_orinocodev(struct net_device *dev)
/* Wireless extensions */ /* Wireless extensions */
/********************************************************************/ /********************************************************************/
static int orinoco_hw_get_bssid(struct orinoco_private *priv,
char buf[ETH_ALEN])
{
hermes_t *hw = &priv->hw;
int err = 0;
unsigned long flags;
if (orinoco_lock(priv, &flags) != 0)
return -EBUSY;
err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
ETH_ALEN, NULL, buf);
orinoco_unlock(priv, &flags);
return err;
}
static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
char buf[IW_ESSID_MAX_SIZE+1]) char buf[IW_ESSID_MAX_SIZE+1])
{ {
...@@ -2633,140 +2615,271 @@ static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, ...@@ -2633,140 +2615,271 @@ static int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
return 0; return 0;
} }
static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq) static int orinoco_ioctl_getname(struct net_device *dev,
struct iw_request_info *info,
char *name,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int err = 0;
int mode;
struct iw_range range;
int numrates; int numrates;
int i, k; int err;
err = orinoco_hw_get_bitratelist(priv, &numrates, NULL, 0);
if (!err && (numrates > 2))
strcpy(name, "IEEE 802.11b");
else
strcpy(name, "IEEE 802.11-DS");
return 0;
}
static int orinoco_ioctl_setwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
int err = -EINPROGRESS; /* Call commit handler */
unsigned long flags; unsigned long flags;
static const u8 off_addr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
static const u8 any_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
TRACE_ENTER(dev->name); if (orinoco_lock(priv, &flags) != 0)
return -EBUSY;
/* Enable automatic roaming - no sanity checks are needed */
if (memcmp(&ap_addr->sa_data, off_addr, ETH_ALEN) == 0 ||
memcmp(&ap_addr->sa_data, any_addr, ETH_ALEN) == 0) {
priv->bssid_fixed = 0;
memset(priv->desired_bssid, 0, ETH_ALEN);
/* "off" means keep existing connection */
if (ap_addr->sa_data[0] == 0) {
__orinoco_hw_set_wap(priv);
err = 0;
}
goto out;
}
if (priv->firmware_type == FIRMWARE_TYPE_AGERE) {
printk(KERN_WARNING "%s: Lucent/Agere firmware doesn't "
"support manual roaming\n",
dev->name);
err = -EOPNOTSUPP;
goto out;
}
if (priv->iw_mode != IW_MODE_INFRA) {
printk(KERN_WARNING "%s: Manual roaming supported only in "
"managed mode\n", dev->name);
err = -EOPNOTSUPP;
goto out;
}
/* Intersil firmware hangs without Desired ESSID */
if (priv->firmware_type == FIRMWARE_TYPE_INTERSIL &&
strlen(priv->desired_essid) == 0) {
printk(KERN_WARNING "%s: Desired ESSID must be set for "
"manual roaming\n", dev->name);
err = -EOPNOTSUPP;
goto out;
}
/* Finally, enable manual roaming */
priv->bssid_fixed = 1;
memcpy(priv->desired_bssid, &ap_addr->sa_data, ETH_ALEN);
out:
orinoco_unlock(priv, &flags);
return err;
}
static int orinoco_ioctl_getwap(struct net_device *dev,
struct iw_request_info *info,
struct sockaddr *ap_addr,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw;
int err = 0;
unsigned long flags;
if (orinoco_lock(priv, &flags) != 0)
return -EBUSY;
ap_addr->sa_family = ARPHRD_ETHER;
err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID,
ETH_ALEN, NULL, ap_addr->sa_data);
orinoco_unlock(priv, &flags);
return err;
}
if (!access_ok(VERIFY_WRITE, rrq->pointer, sizeof(range))) static int orinoco_ioctl_setmode(struct net_device *dev,
return -EFAULT; struct iw_request_info *info,
u32 *mode,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
int err = -EINPROGRESS; /* Call commit handler */
unsigned long flags;
rrq->length = sizeof(range); if (priv->iw_mode == *mode)
return 0;
if (orinoco_lock(priv, &flags) != 0) if (orinoco_lock(priv, &flags) != 0)
return -EBUSY; return -EBUSY;
mode = priv->iw_mode; switch (*mode) {
case IW_MODE_ADHOC:
if (!priv->has_ibss && !priv->has_port3)
err = -EOPNOTSUPP;
break;
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;
}
if (err == -EINPROGRESS) {
priv->iw_mode = *mode;
set_port_type(priv);
}
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
memset(&range, 0, sizeof(range)); return err;
}
/* Much of this shamelessly taken from wvlan_cs.c. No idea static int orinoco_ioctl_getmode(struct net_device *dev,
* what it all means -dgibson */ struct iw_request_info *info,
range.we_version_compiled = WIRELESS_EXT; u32 *mode,
range.we_version_source = 11; char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
range.min_nwid = range.max_nwid = 0; /* We don't use nwids */ *mode = priv->iw_mode;
return 0;
}
static int orinoco_ioctl_getiwrange(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *rrq,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
int err = 0;
struct iw_range *range = (struct iw_range *) extra;
int numrates;
int i, k;
TRACE_ENTER(dev->name);
rrq->length = sizeof(struct iw_range);
memset(range, 0, sizeof(struct iw_range));
range->we_version_compiled = WIRELESS_EXT;
range->we_version_source = 14;
/* Set available channels/frequencies */ /* Set available channels/frequencies */
range.num_channels = NUM_CHANNELS; range->num_channels = NUM_CHANNELS;
k = 0; k = 0;
for (i = 0; i < NUM_CHANNELS; i++) { for (i = 0; i < NUM_CHANNELS; i++) {
if (priv->channel_mask & (1 << i)) { if (priv->channel_mask & (1 << i)) {
range.freq[k].i = i + 1; range->freq[k].i = i + 1;
range.freq[k].m = channel_frequency[i] * 100000; range->freq[k].m = channel_frequency[i] * 100000;
range.freq[k].e = 1; range->freq[k].e = 1;
k++; k++;
} }
if (k >= IW_MAX_FREQUENCIES) if (k >= IW_MAX_FREQUENCIES)
break; break;
} }
range.num_frequency = k; range->num_frequency = k;
range->sensitivity = 3;
if (priv->has_wep) {
range->max_encoding_tokens = ORINOCO_MAX_KEYS;
range->encoding_size[0] = SMALL_KEY_SIZE;
range->num_encoding_sizes = 1;
range.sensitivity = 3; if (priv->has_big_wep) {
range->encoding_size[1] = LARGE_KEY_SIZE;
range->num_encoding_sizes = 2;
}
}
if ((mode == IW_MODE_ADHOC) && (priv->spy_number == 0)){ if ((priv->iw_mode == IW_MODE_ADHOC) && (priv->spy_number == 0)){
/* Quality stats meaningless in ad-hoc mode */ /* Quality stats meaningless in ad-hoc mode */
range.max_qual.qual = 0;
range.max_qual.level = 0;
range.max_qual.noise = 0;
range.avg_qual.qual = 0;
range.avg_qual.level = 0;
range.avg_qual.noise = 0;
} else { } else {
range.max_qual.qual = 0x8b - 0x2f; range->max_qual.qual = 0x8b - 0x2f;
range.max_qual.level = 0x2f - 0x95 - 1; range->max_qual.level = 0x2f - 0x95 - 1;
range.max_qual.noise = 0x2f - 0x95 - 1; range->max_qual.noise = 0x2f - 0x95 - 1;
/* Need to get better values */ /* Need to get better values */
range.avg_qual.qual = 0x24; range->avg_qual.qual = 0x24;
range.avg_qual.level = 0xC2; range->avg_qual.level = 0xC2;
range.avg_qual.noise = 0x9E; range->avg_qual.noise = 0x9E;
} }
err = orinoco_hw_get_bitratelist(priv, &numrates, err = orinoco_hw_get_bitratelist(priv, &numrates,
range.bitrate, IW_MAX_BITRATES); range->bitrate, IW_MAX_BITRATES);
if (err) if (err)
return err; return err;
range.num_bitrates = numrates; range->num_bitrates = numrates;
/* Set an indication of the max TCP throughput in bit/s that we can /* Set an indication of the max TCP throughput in bit/s that we can
* expect using this interface. May be use for QoS stuff... * expect using this interface. May be use for QoS stuff...
* Jean II */ * Jean II */
if(numrates > 2) if (numrates > 2)
range.throughput = 5 * 1000 * 1000; /* ~5 Mb/s */ range->throughput = 5 * 1000 * 1000; /* ~5 Mb/s */
else else
range.throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */ range->throughput = 1.5 * 1000 * 1000; /* ~1.5 Mb/s */
range.min_rts = 0; range->min_rts = 0;
range.max_rts = 2347; range->max_rts = 2347;
range.min_frag = 256; range->min_frag = 256;
range.max_frag = 2346; range->max_frag = 2346;
if (orinoco_lock(priv, &flags) != 0) range->min_pmp = 0;
return -EBUSY; range->max_pmp = 65535000;
if (priv->has_wep) { range->min_pmt = 0;
range.max_encoding_tokens = ORINOCO_MAX_KEYS; range->max_pmt = 65535 * 1000; /* ??? */
range->pmp_flags = IW_POWER_PERIOD;
range.encoding_size[0] = SMALL_KEY_SIZE; range->pmt_flags = IW_POWER_TIMEOUT;
range.num_encoding_sizes = 1; range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R;
if (priv->has_big_wep) { range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
range.encoding_size[1] = LARGE_KEY_SIZE; range->retry_flags = IW_RETRY_LIMIT;
range.num_encoding_sizes = 2; range->r_time_flags = IW_RETRY_LIFETIME;
} range->min_retry = 0;
} else { range->max_retry = 65535; /* ??? */
range.num_encoding_sizes = 0; range->min_r_time = 0;
range.max_encoding_tokens = 0; range->max_r_time = 65535 * 1000; /* ??? */
}
orinoco_unlock(priv, &flags);
range.min_pmp = 0;
range.max_pmp = 65535000;
range.min_pmt = 0;
range.max_pmt = 65535 * 1000; /* ??? */
range.pmp_flags = IW_POWER_PERIOD;
range.pmt_flags = IW_POWER_TIMEOUT;
range.pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R;
range.num_txpower = 1;
range.txpower[0] = 15; /* 15dBm */
range.txpower_capa = IW_TXPOW_DBM;
range.retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
range.retry_flags = IW_RETRY_LIMIT;
range.r_time_flags = IW_RETRY_LIFETIME;
range.min_retry = 0;
range.max_retry = 65535; /* ??? */
range.min_r_time = 0;
range.max_r_time = 65535 * 1000; /* ??? */
if (copy_to_user(rrq->pointer, &range, sizeof(range)))
return -EFAULT;
TRACE_EXIT(dev->name); TRACE_EXIT(dev->name);
return 0; return 0;
} }
static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq) static int orinoco_ioctl_setiwencode(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *erq,
char *keybuf)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int index = (erq->flags & IW_ENCODE_INDEX) - 1; int index = (erq->flags & IW_ENCODE_INDEX) - 1;
...@@ -2774,8 +2887,7 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er ...@@ -2774,8 +2887,7 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er
int enable = priv->wep_on; int enable = priv->wep_on;
int restricted = priv->wep_restrict; int restricted = priv->wep_restrict;
u16 xlen = 0; u16 xlen = 0;
int err = 0; int err = -EINPROGRESS; /* Call commit handler */
char keybuf[ORINOCO_MAX_KEY_SIZE];
unsigned long flags; unsigned long flags;
if (! priv->has_wep) if (! priv->has_wep)
...@@ -2788,9 +2900,6 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er ...@@ -2788,9 +2900,6 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er
if ( (erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep ) if ( (erq->length > SMALL_KEY_SIZE) && !priv->has_big_wep )
return -E2BIG; return -E2BIG;
if (copy_from_user(keybuf, erq->pointer, erq->length))
return -EFAULT;
} }
if (orinoco_lock(priv, &flags) != 0) if (orinoco_lock(priv, &flags) != 0)
...@@ -2864,12 +2973,14 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er ...@@ -2864,12 +2973,14 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er
return err; return err;
} }
static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq) static int orinoco_ioctl_getiwencode(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *erq,
char *keybuf)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int index = (erq->flags & IW_ENCODE_INDEX) - 1; int index = (erq->flags & IW_ENCODE_INDEX) - 1;
u16 xlen = 0; u16 xlen = 0;
char keybuf[ORINOCO_MAX_KEY_SIZE];
unsigned long flags; unsigned long flags;
if (! priv->has_wep) if (! priv->has_wep)
...@@ -2898,51 +3009,47 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *er ...@@ -2898,51 +3009,47 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *er
memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE); memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE);
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
if (erq->pointer) {
if (copy_to_user(erq->pointer, keybuf, xlen))
return -EFAULT;
}
return 0; return 0;
} }
static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq) static int orinoco_ioctl_setessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *erq,
char *essidbuf)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
char essidbuf[IW_ESSID_MAX_SIZE+1];
unsigned long flags; unsigned long flags;
/* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it
* anyway... - Jean II */ * anyway... - Jean II */
memset(&essidbuf, 0, sizeof(essidbuf)); /* Hum... Should not use Wireless Extension constant (may change),
* should use our own... - Jean II */
if (erq->flags) { if (erq->length > IW_ESSID_MAX_SIZE)
/* iwconfig includes the NUL in the specified length */ return -E2BIG;
if (erq->length > IW_ESSID_MAX_SIZE+1)
return -E2BIG;
if (copy_from_user(&essidbuf, erq->pointer, erq->length))
return -EFAULT;
essidbuf[IW_ESSID_MAX_SIZE] = '\0';
}
if (orinoco_lock(priv, &flags) != 0) if (orinoco_lock(priv, &flags) != 0)
return -EBUSY; return -EBUSY;
memcpy(priv->desired_essid, essidbuf, sizeof(priv->desired_essid)); /* NULL the string (for NULL termination & ESSID = ANY) - Jean II */
memset(priv->desired_essid, 0, sizeof(priv->desired_essid));
/* If not ANY, get the new ESSID */
if (erq->flags) {
memcpy(priv->desired_essid, essidbuf, erq->length);
}
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
return 0; return -EINPROGRESS; /* Call commit handler */
} }
static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq) static int orinoco_ioctl_getessid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *erq,
char *essidbuf)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
char essidbuf[IW_ESSID_MAX_SIZE+1];
int active; int active;
int err = 0; int err = 0;
unsigned long flags; unsigned long flags;
...@@ -2956,51 +3063,46 @@ static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq) ...@@ -2956,51 +3063,46 @@ static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq)
} else { } else {
if (orinoco_lock(priv, &flags) != 0) if (orinoco_lock(priv, &flags) != 0)
return -EBUSY; return -EBUSY;
memcpy(essidbuf, priv->desired_essid, sizeof(essidbuf)); memcpy(essidbuf, priv->desired_essid, IW_ESSID_MAX_SIZE + 1);
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
} }
erq->flags = 1; erq->flags = 1;
erq->length = strlen(essidbuf) + 1; erq->length = strlen(essidbuf) + 1;
if (erq->pointer)
if (copy_to_user(erq->pointer, essidbuf, erq->length))
return -EFAULT;
TRACE_EXIT(dev->name); TRACE_EXIT(dev->name);
return 0; return 0;
} }
static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq) static int orinoco_ioctl_setnick(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *nrq,
char *nickbuf)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
char nickbuf[IW_ESSID_MAX_SIZE+1];
unsigned long flags; unsigned long flags;
if (nrq->length > IW_ESSID_MAX_SIZE) if (nrq->length > IW_ESSID_MAX_SIZE)
return -E2BIG; return -E2BIG;
memset(nickbuf, 0, sizeof(nickbuf));
if (copy_from_user(nickbuf, nrq->pointer, nrq->length))
return -EFAULT;
nickbuf[nrq->length] = '\0';
if (orinoco_lock(priv, &flags) != 0) if (orinoco_lock(priv, &flags) != 0)
return -EBUSY; return -EBUSY;
memcpy(priv->nick, nickbuf, sizeof(priv->nick)); memset(priv->nick, 0, sizeof(priv->nick));
memcpy(priv->nick, nickbuf, nrq->length);
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
return 0; return -EINPROGRESS; /* Call commit handler */
} }
static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq) static int orinoco_ioctl_getnick(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *nrq,
char *nickbuf)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
char nickbuf[IW_ESSID_MAX_SIZE+1];
unsigned long flags; unsigned long flags;
if (orinoco_lock(priv, &flags) != 0) if (orinoco_lock(priv, &flags) != 0)
...@@ -3011,23 +3113,22 @@ static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq) ...@@ -3011,23 +3113,22 @@ static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq)
nrq->length = strlen(nickbuf)+1; nrq->length = strlen(nickbuf)+1;
if (copy_to_user(nrq->pointer, nickbuf, sizeof(nickbuf)))
return -EFAULT;
return 0; return 0;
} }
static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq) static int orinoco_ioctl_setfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *frq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int chan = -1; int chan = -1;
unsigned long flags; unsigned long flags;
int err = -EINPROGRESS; /* Call commit handler */
/* We can only use this in Ad-Hoc demo mode to set the operating /* In infrastructure mode the AP sets the channel */
* frequency, or in IBSS mode to set the frequency where the IBSS if (priv->iw_mode == IW_MODE_INFRA)
* will be created - Jean II */ return -EBUSY;
if (priv->iw_mode != IW_MODE_ADHOC)
return -EOPNOTSUPP;
if ( (frq->e == 0) && (frq->m <= 1000) ) { if ( (frq->e == 0) && (frq->m <= 1000) ) {
/* Setting by channel number */ /* Setting by channel number */
...@@ -3051,13 +3152,44 @@ static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq) ...@@ -3051,13 +3152,44 @@ static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq)
if (orinoco_lock(priv, &flags) != 0) if (orinoco_lock(priv, &flags) != 0)
return -EBUSY; return -EBUSY;
priv->channel = chan; 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); orinoco_unlock(priv, &flags);
return err;
}
static int orinoco_ioctl_getfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *frq,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
int tmp;
/* Locking done in there */
tmp = orinoco_hw_get_freq(priv);
if (tmp < 0) {
return tmp;
}
frq->m = tmp;
frq->e = 1;
return 0; return 0;
} }
static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq) static int orinoco_ioctl_getsens(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *srq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw; hermes_t *hw = &priv->hw;
...@@ -3083,7 +3215,10 @@ static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq) ...@@ -3083,7 +3215,10 @@ static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq)
return 0; return 0;
} }
static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq) static int orinoco_ioctl_setsens(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *srq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int val = srq->value; int val = srq->value;
...@@ -3100,10 +3235,13 @@ static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq) ...@@ -3100,10 +3235,13 @@ static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq)
priv->ap_density = val; priv->ap_density = val;
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
return 0; return -EINPROGRESS; /* Call commit handler */
} }
static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq) static int orinoco_ioctl_setrts(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rrq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int val = rrq->value; int val = rrq->value;
...@@ -3121,13 +3259,30 @@ static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq) ...@@ -3121,13 +3259,30 @@ static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq)
priv->rts_thresh = val; priv->rts_thresh = val;
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
return -EINPROGRESS; /* Call commit handler */
}
static int orinoco_ioctl_getrts(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rrq,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
rrq->value = priv->rts_thresh;
rrq->disabled = (rrq->value == 2347);
rrq->fixed = 1;
return 0; return 0;
} }
static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq) static int orinoco_ioctl_setfrag(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *frq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int err = 0; int err = -EINPROGRESS; /* Call commit handler */
unsigned long flags; unsigned long flags;
if (orinoco_lock(priv, &flags) != 0) if (orinoco_lock(priv, &flags) != 0)
...@@ -3159,11 +3314,14 @@ static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq) ...@@ -3159,11 +3314,14 @@ static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq)
return err; return err;
} }
static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq) static int orinoco_ioctl_getfrag(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *frq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw; hermes_t *hw = &priv->hw;
int err = 0; int err;
u16 val; u16 val;
unsigned long flags; unsigned long flags;
...@@ -3196,10 +3354,12 @@ static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq) ...@@ -3196,10 +3354,12 @@ static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq)
return err; return err;
} }
static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *rrq) static int orinoco_ioctl_setrate(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rrq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int err = 0;
int ratemode = -1; int ratemode = -1;
int bitrate; /* 100s of kilobits */ int bitrate; /* 100s of kilobits */
int i; int i;
...@@ -3235,10 +3395,13 @@ static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *rrq) ...@@ -3235,10 +3395,13 @@ static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *rrq)
priv->bitratemode = ratemode; priv->bitratemode = ratemode;
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
return err; return -EINPROGRESS;
} }
static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *rrq) static int orinoco_ioctl_getrate(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rrq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw; hermes_t *hw = &priv->hw;
...@@ -3303,10 +3466,13 @@ static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *rrq) ...@@ -3303,10 +3466,13 @@ static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *rrq)
return err; return err;
} }
static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq) static int orinoco_ioctl_setpower(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *prq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int err = 0; int err = -EINPROGRESS; /* Call commit handler */
unsigned long flags; unsigned long flags;
if (orinoco_lock(priv, &flags) != 0) if (orinoco_lock(priv, &flags) != 0)
...@@ -3355,7 +3521,10 @@ static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq) ...@@ -3355,7 +3521,10 @@ static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq)
return err; return err;
} }
static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq) static int orinoco_ioctl_getpower(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *prq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw; hermes_t *hw = &priv->hw;
...@@ -3403,7 +3572,10 @@ static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq) ...@@ -3403,7 +3572,10 @@ static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq)
return err; return err;
} }
static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq) static int orinoco_ioctl_getretry(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *rrq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw; hermes_t *hw = &priv->hw;
...@@ -3454,10 +3626,38 @@ static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq) ...@@ -3454,10 +3626,38 @@ static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq)
return err; return err;
} }
static int orinoco_ioctl_setibssport(struct net_device *dev, struct iwreq *wrq) static int orinoco_ioctl_reset(struct net_device *dev,
struct iw_request_info *info,
void *wrqu,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
if (! capable(CAP_NET_ADMIN))
return -EPERM;
if (info->cmd == (SIOCIWFIRSTPRIV + 0x1)) {
printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name);
/* Firmware reset */
orinoco_reset(dev);
} else {
printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name);
schedule_work(&priv->reset_work);
}
return 0;
}
static int orinoco_ioctl_setibssport(struct net_device *dev,
struct iw_request_info *info,
void *wrqu,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int val = *( (int *) wrq->u.name ); int val = *( (int *) extra );
unsigned long flags; unsigned long flags;
if (orinoco_lock(priv, &flags) != 0) if (orinoco_lock(priv, &flags) != 0)
...@@ -3469,28 +3669,28 @@ static int orinoco_ioctl_setibssport(struct net_device *dev, struct iwreq *wrq) ...@@ -3469,28 +3669,28 @@ static int orinoco_ioctl_setibssport(struct net_device *dev, struct iwreq *wrq)
set_port_type(priv); set_port_type(priv);
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
return 0; return -EINPROGRESS; /* Call commit handler */
} }
static int orinoco_ioctl_getibssport(struct net_device *dev, struct iwreq *wrq) static int orinoco_ioctl_getibssport(struct net_device *dev,
struct iw_request_info *info,
void *wrqu,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int *val = (int *)wrq->u.name; int *val = (int *) extra;
unsigned long flags;
if (orinoco_lock(priv, &flags) != 0)
return -EBUSY;
*val = priv->ibss_port; *val = priv->ibss_port;
orinoco_unlock(priv, &flags);
return 0; return 0;
} }
static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq) static int orinoco_ioctl_setport3(struct net_device *dev,
struct iw_request_info *info,
void *wrqu,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int val = *( (int *) wrq->u.name ); int val = *( (int *) extra );
int err = 0; int err = 0;
unsigned long flags; unsigned long flags;
...@@ -3519,51 +3719,131 @@ static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq) ...@@ -3519,51 +3719,131 @@ static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq)
err = -EINVAL; err = -EINVAL;
} }
if (! err) if (! err) {
/* Actually update the mode we are using */ /* Actually update the mode we are using */
set_port_type(priv); set_port_type(priv);
err = -EINPROGRESS;
}
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
return err; return err;
} }
static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq) static int orinoco_ioctl_getport3(struct net_device *dev,
struct iw_request_info *info,
void *wrqu,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
int *val = (int *) extra;
*val = priv->prefer_port3;
return 0;
}
static int orinoco_ioctl_setpreamble(struct net_device *dev,
struct iw_request_info *info,
void *wrqu,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
int *val = (int *)wrq->u.name;
unsigned long flags; unsigned long flags;
int val;
if (! priv->has_preamble)
return -EOPNOTSUPP;
/* 802.11b has recently defined some short preamble.
* Basically, the Phy header has been reduced in size.
* This increase performance, especially at high rates
* (the preamble is transmitted at 1Mb/s), unfortunately
* this give compatibility troubles... - Jean II */
val = *( (int *) extra );
if (orinoco_lock(priv, &flags) != 0) if (orinoco_lock(priv, &flags) != 0)
return -EBUSY; return -EBUSY;
*val = priv->prefer_port3; if (val)
priv->preamble = 1;
else
priv->preamble = 0;
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
return -EINPROGRESS; /* Call commit handler */
}
static int orinoco_ioctl_getpreamble(struct net_device *dev,
struct iw_request_info *info,
void *wrqu,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
int *val = (int *) extra;
if (! priv->has_preamble)
return -EOPNOTSUPP;
*val = priv->preamble;
return 0; return 0;
} }
/* ioctl interface to hermes_read_ltv()
* To use with iwpriv, pass the RID as the token argument, e.g.
* iwpriv get_rid [0xfc00]
* At least Wireless Tools 25 is required to use iwpriv.
* For Wireless Tools 25 and 26 append "dummy" are the end. */
static int orinoco_ioctl_getrid(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *data,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
hermes_t *hw = &priv->hw;
int rid = data->flags;
u16 length;
int err;
unsigned long flags;
/* It's a "get" function, but we don't want users to access the
* WEP key and other raw firmware data */
if (! capable(CAP_NET_ADMIN))
return -EPERM;
if (rid < 0xfc00 || rid > 0xffff)
return -EINVAL;
if (orinoco_lock(priv, &flags) != 0)
return -EBUSY;
err = hermes_read_ltv(hw, USER_BAP, rid, MAX_RID_LEN, &length,
extra);
if (err)
goto out;
data->length = min_t(u16, HERMES_RECLEN_TO_BYTES(length),
MAX_RID_LEN);
out:
orinoco_unlock(priv, &flags);
return err;
}
/* Spy is used for link quality/strength measurements in Ad-Hoc mode /* Spy is used for link quality/strength measurements in Ad-Hoc mode
* Jean II */ * Jean II */
static int orinoco_ioctl_setspy(struct net_device *dev, struct iw_point *srq) static int orinoco_ioctl_setspy(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *srq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
struct sockaddr address[IW_MAX_SPY]; struct sockaddr *address = (struct sockaddr *) extra;
int number = srq->length; int number = srq->length;
int i; int i;
int err = 0;
unsigned long flags; unsigned long flags;
/* Check the number of addresses */
if (number > IW_MAX_SPY)
return -E2BIG;
/* Get the data in the driver */
if (srq->pointer) {
if (copy_from_user(address, srq->pointer,
sizeof(struct sockaddr) * number))
return -EFAULT;
}
/* Make sure nobody mess with the structure while we do */ /* Make sure nobody mess with the structure while we do */
if (orinoco_lock(priv, &flags) != 0) if (orinoco_lock(priv, &flags) != 0)
return -EBUSY; return -EBUSY;
...@@ -3587,14 +3867,17 @@ static int orinoco_ioctl_setspy(struct net_device *dev, struct iw_point *srq) ...@@ -3587,14 +3867,17 @@ static int orinoco_ioctl_setspy(struct net_device *dev, struct iw_point *srq)
/* Now, let the others play */ /* Now, let the others play */
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
return err; /* Do NOT call commit handler */
return 0;
} }
static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq) static int orinoco_ioctl_getspy(struct net_device *dev,
struct iw_request_info *info,
struct iw_point *srq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
struct sockaddr address[IW_MAX_SPY]; struct sockaddr *address = (struct sockaddr *) extra;
struct iw_quality spy_stat[IW_MAX_SPY];
int number; int number;
int i; int i;
unsigned long flags; unsigned long flags;
...@@ -3603,7 +3886,12 @@ static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq) ...@@ -3603,7 +3886,12 @@ static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq)
return -EBUSY; return -EBUSY;
number = priv->spy_number; number = priv->spy_number;
if ((number > 0) && (srq->pointer)) { /* Create address struct */
for (i = 0; i < number; i++) {
memcpy(address[i].sa_data, priv->spy_address[i], ETH_ALEN);
address[i].sa_family = AF_UNIX;
}
if (number > 0) {
/* Create address struct */ /* Create address struct */
for (i = 0; i < number; i++) { for (i = 0; i < number; i++) {
memcpy(address[i].sa_data, priv->spy_address[i], memcpy(address[i].sa_data, priv->spy_address[i],
...@@ -3614,344 +3902,503 @@ static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq) ...@@ -3614,344 +3902,503 @@ static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq)
/* In theory, we should disable irqs while copying the stats /* In theory, we should disable irqs while copying the stats
* because the rx path might update it in the middle... * because the rx path might update it in the middle...
* Bah, who care ? - Jean II */ * Bah, who care ? - Jean II */
memcpy(&spy_stat, priv->spy_stat, memcpy(extra + (sizeof(struct sockaddr) * number),
sizeof(struct iw_quality) * IW_MAX_SPY); priv->spy_stat, sizeof(struct iw_quality) * number);
for (i=0; i < number; i++)
priv->spy_stat[i].updated = 0;
} }
/* Reset updated flags. */
for (i = 0; i < number; i++)
priv->spy_stat[i].updated = 0;
orinoco_unlock(priv, &flags); orinoco_unlock(priv, &flags);
/* Push stuff to user space */
srq->length = number; srq->length = number;
if(copy_to_user(srq->pointer, address,
sizeof(struct sockaddr) * number))
return -EFAULT;
if(copy_to_user(srq->pointer + (sizeof(struct sockaddr)*number),
&spy_stat, sizeof(struct iw_quality) * number))
return -EFAULT;
return 0; return 0;
} }
static int /* Trigger a scan (look for other cells in the vicinity */
orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static int orinoco_ioctl_setscan(struct net_device *dev,
struct iw_request_info *info,
struct iw_param *srq,
char *extra)
{ {
struct orinoco_private *priv = netdev_priv(dev); struct orinoco_private *priv = netdev_priv(dev);
struct iwreq *wrq = (struct iwreq *)rq; hermes_t *hw = &priv->hw;
int err = 0; int err = 0;
int tmp;
int changed = 0;
unsigned long flags; unsigned long flags;
TRACE_ENTER(dev->name); /* Note : you may have realised that, as this is a SET operation,
* this is priviledged and therefore a normal user can't
* perform scanning.
* This is not an error, while the device perform scanning,
* traffic doesn't flow, so it's a perfect DoS...
* Jean II */
/* In theory, we could allow most of the the SET stuff to be if (orinoco_lock(priv, &flags) != 0)
* done. In practice, the lapse of time at startup when the return -EBUSY;
* card is not ready is very short, so why bother... Note
* that netif_device_present is different from up/down
* (ifconfig), when the device is not yet up, it is usually
* already ready... Jean II */
if (! netif_device_present(dev))
return -ENODEV;
switch (cmd) { /* Scanning with port 0 disabled would fail */
case SIOCGIWNAME: if (!netif_running(dev)) {
strcpy(wrq->u.name, "IEEE 802.11-DS"); err = -ENETDOWN;
break; goto out;
}
case SIOCGIWAP:
wrq->u.ap_addr.sa_family = ARPHRD_ETHER;
err = orinoco_hw_get_bssid(priv, wrq->u.ap_addr.sa_data);
break;
case SIOCGIWRANGE: /* In monitor mode, the scan results are always empty.
err = orinoco_ioctl_getiwrange(dev, &wrq->u.data); * Probe responses are passed to the driver as received
break; * frames and could be processed in software. */
if (priv->iw_mode == IW_MODE_MONITOR) {
err = -EOPNOTSUPP;
goto out;
}
case SIOCSIWMODE: /* Note : because we don't lock out the irq handler, the way
if (orinoco_lock(priv, &flags) != 0) * we access scan variables in priv is critical.
return -EBUSY; * o scan_inprogress : not touched by irq handler
switch (wrq->u.mode) { * o scan_mode : not touched by irq handler
case IW_MODE_ADHOC: * o scan_result : irq is strict producer, non-irq is strict
if (! (priv->has_ibss || priv->has_port3) ) * consumer.
err = -EINVAL; * o scan_len : synchronised with scan_result
else { * Before modifying anything on those variables, please think hard !
priv->iw_mode = IW_MODE_ADHOC; * Jean II */
changed = 1;
}
break;
case IW_MODE_INFRA: /* If there is still some left-over scan results, get rid of it */
priv->iw_mode = IW_MODE_INFRA; if (priv->scan_result != NULL) {
changed = 1; /* What's likely is that a client did crash or was killed
break; * between triggering the scan request and reading the
* results, so we need to reset everything.
* Some clients that are too slow may suffer from that...
* Jean II */
kfree(priv->scan_result);
priv->scan_result = NULL;
}
default: /* Save flags */
err = -EINVAL; priv->scan_mode = srq->flags;
break;
}
set_port_type(priv);
orinoco_unlock(priv, &flags);
break;
case SIOCGIWMODE: /* Always trigger scanning, even if it's in progress.
if (orinoco_lock(priv, &flags) != 0) * This way, if the info frame get lost, we will recover somewhat
return -EBUSY; * gracefully - Jean II */
wrq->u.mode = priv->iw_mode;
orinoco_unlock(priv, &flags);
break;
case SIOCSIWENCODE: if (priv->has_hostscan) {
err = orinoco_ioctl_setiwencode(dev, &wrq->u.encoding); switch (priv->firmware_type) {
if (! err) case FIRMWARE_TYPE_SYMBOL:
changed = 1; err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFHOSTSCAN_SYMBOL,
HERMES_HOSTSCAN_SYMBOL_ONCE |
HERMES_HOSTSCAN_SYMBOL_BCAST);
break;
case FIRMWARE_TYPE_INTERSIL: {
u16 req[3];
req[0] = cpu_to_le16(0x3fff); /* All channels */
req[1] = cpu_to_le16(0x0001); /* rate 1 Mbps */
req[2] = 0; /* Any ESSID */
err = HERMES_WRITE_RECORD(hw, USER_BAP,
HERMES_RID_CNFHOSTSCAN, &req);
}
break; break;
case FIRMWARE_TYPE_AGERE:
err = hermes_write_wordrec(hw, USER_BAP,
HERMES_RID_CNFSCANSSID_AGERE,
0); /* Any ESSID */
if (err)
break;
case SIOCGIWENCODE: err = hermes_inquire(hw, HERMES_INQ_SCAN);
if (! capable(CAP_NET_ADMIN)) {
err = -EPERM;
break; break;
} }
} else
err = hermes_inquire(hw, HERMES_INQ_SCAN);
err = orinoco_ioctl_getiwencode(dev, &wrq->u.encoding); /* One more client */
break; if (! err)
priv->scan_inprogress = 1;
case SIOCSIWESSID:
err = orinoco_ioctl_setessid(dev, &wrq->u.essid);
if (! err)
changed = 1;
break;
case SIOCGIWESSID: out:
err = orinoco_ioctl_getessid(dev, &wrq->u.essid); orinoco_unlock(priv, &flags);
break; return err;
}
case SIOCSIWNICKN: /* Translate scan data returned from the card to a card independant
err = orinoco_ioctl_setnick(dev, &wrq->u.data); * format that the Wireless Tools will understand - Jean II */
if (! err) static inline int orinoco_translate_scan(struct net_device *dev,
changed = 1; char *buffer,
break; char *scan,
int scan_len)
{
struct orinoco_private *priv = netdev_priv(dev);
int offset; /* In the scan data */
union hermes_scan_info *atom;
int atom_len;
u16 capabilities;
u16 channel;
struct iw_event iwe; /* Temporary buffer */
char * current_ev = buffer;
char * end_buf = buffer + IW_SCAN_MAX_DATA;
case SIOCGIWNICKN: switch (priv->firmware_type) {
err = orinoco_ioctl_getnick(dev, &wrq->u.data); case FIRMWARE_TYPE_AGERE:
atom_len = sizeof(struct agere_scan_apinfo);
offset = 0;
break; break;
case FIRMWARE_TYPE_SYMBOL:
case SIOCGIWFREQ: /* Lack of documentation necessitates this hack.
tmp = orinoco_hw_get_freq(priv); * Different firmwares have 68 or 76 byte long atoms.
if (tmp < 0) { * We try modulo first. If the length divides by both,
err = tmp; * we check what would be the channel in the second
} else { * frame for a 68-byte atom. 76-byte atoms have 0 there.
wrq->u.freq.m = tmp; * Valid channel cannot be 0. */
wrq->u.freq.e = 1; if (scan_len % 76)
} atom_len = 68;
else if (scan_len % 68)
atom_len = 76;
else if (scan_len >= 1292 && scan[68] == 0)
atom_len = 76;
else
atom_len = 68;
offset = 0;
break; break;
case FIRMWARE_TYPE_INTERSIL:
case SIOCSIWFREQ: offset = 4;
err = orinoco_ioctl_setfreq(dev, &wrq->u.freq); if (priv->has_hostscan)
if (! err) atom_len = scan[0] + (scan[1] << 8);
changed = 1; else
atom_len = offsetof(struct prism2_scan_apinfo, atim);
break; break;
default:
return 0;
}
case SIOCGIWSENS: /* Check that we got an whole number of atoms */
err = orinoco_ioctl_getsens(dev, &wrq->u.sens); if ((scan_len - offset) % atom_len) {
break; printk(KERN_ERR "%s: Unexpected scan data length %d, "
"atom_len %d, offset %d\n", dev->name, scan_len,
atom_len, offset);
return 0;
}
case SIOCSIWSENS: /* Read the entries one by one */
err = orinoco_ioctl_setsens(dev, &wrq->u.sens); for (; offset + atom_len <= scan_len; offset += atom_len) {
if (! err) /* Get next atom */
changed = 1; atom = (union hermes_scan_info *) (scan + offset);
break;
/* 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, atom->a.bssid, ETH_ALEN);
current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
/* Other entries will be displayed in the order we give them */
/* Add the ESSID */
iwe.u.data.length = le16_to_cpu(atom->a.essid_len);
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(current_ev, end_buf, &iwe, atom->a.essid);
/* Add mode */
iwe.cmd = SIOCGIWMODE;
capabilities = le16_to_cpu(atom->a.capabilities);
if (capabilities & 0x3) {
if (capabilities & 0x1)
iwe.u.mode = IW_MODE_MASTER;
else
iwe.u.mode = IW_MODE_ADHOC;
current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN);
}
case SIOCGIWRTS: channel = atom->s.channel;
wrq->u.rts.value = priv->rts_thresh; if ( (channel >= 1) && (channel <= NUM_CHANNELS) ) {
wrq->u.rts.disabled = (wrq->u.rts.value == 2347); /* Add frequency */
wrq->u.rts.fixed = 1; iwe.cmd = SIOCGIWFREQ;
break; iwe.u.freq.m = channel_frequency[channel-1] * 100000;
iwe.u.freq.e = 1;
current_ev = iwe_stream_add_event(current_ev, end_buf,
&iwe, IW_EV_FREQ_LEN);
}
case SIOCSIWRTS: /* Add quality statistics */
err = orinoco_ioctl_setrts(dev, &wrq->u.rts); iwe.cmd = IWEVQUAL;
if (! err) iwe.u.qual.updated = 0x10; /* no link quality */
changed = 1; iwe.u.qual.level = (__u8) le16_to_cpu(atom->a.level) - 0x95;
break; iwe.u.qual.noise = (__u8) le16_to_cpu(atom->a.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(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
case SIOCSIWFRAG: /* Add encryption capability */
err = orinoco_ioctl_setfrag(dev, &wrq->u.frag); iwe.cmd = SIOCGIWENCODE;
if (! err) if (capabilities & 0x10)
changed = 1; iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
break; else
iwe.u.data.flags = IW_ENCODE_DISABLED;
iwe.u.data.length = 0;
current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, atom->a.essid);
/* Bit rate is not available in Lucent/Agere firmwares */
if (priv->firmware_type != FIRMWARE_TYPE_AGERE) {
char * current_val = current_ev + IW_EV_LCP_LEN;
int i;
int step;
if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL)
step = 2;
else
step = 1;
iwe.cmd = SIOCGIWRATE;
/* Those two flags are ignored... */
iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
/* Max 10 values */
for (i = 0; i < 10; i += step) {
/* NULL terminated */
if (atom->p.rates[i] == 0x0)
break;
/* Bit rate given in 500 kb/s units (+ 0x80) */
iwe.u.bitrate.value = ((atom->p.rates[i] & 0x7f) * 500000);
current_val = iwe_stream_add_value(current_ev, current_val,
end_buf, &iwe,
IW_EV_PARAM_LEN);
}
/* Check if we added any event */
if ((current_val - current_ev) > IW_EV_LCP_LEN)
current_ev = current_val;
}
case SIOCGIWFRAG: /* The other data in the scan result are not really
err = orinoco_ioctl_getfrag(dev, &wrq->u.frag); * interesting, so for now drop it - Jean II */
break; }
return current_ev - buffer;
}
case SIOCSIWRATE: /* Return results of a scan */
err = orinoco_ioctl_setrate(dev, &wrq->u.bitrate); static int orinoco_ioctl_getscan(struct net_device *dev,
if (! err) struct iw_request_info *info,
changed = 1; struct iw_point *srq,
break; char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
int err = 0;
unsigned long flags;
case SIOCGIWRATE: if (orinoco_lock(priv, &flags) != 0)
err = orinoco_ioctl_getrate(dev, &wrq->u.bitrate); return -EBUSY;
break;
case SIOCSIWPOWER: /* If no results yet, ask to try again later */
err = orinoco_ioctl_setpower(dev, &wrq->u.power); if (priv->scan_result == NULL) {
if (! err) if (priv->scan_inprogress)
changed = 1; /* Important note : we don't want to block the caller
break; * until results are ready for various reasons.
* First, managing wait queues is complex and racy.
* Second, we grab some rtnetlink lock before comming
* here (in dev_ioctl()).
* Third, we generate an Wireless Event, so the
* caller can wait itself on that - Jean II */
err = -EAGAIN;
else
/* Client error, no scan results...
* The caller need to restart the scan. */
err = -ENODATA;
} else {
/* 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;
/* Results are here, so scan no longer in progress */
priv->scan_inprogress = 0;
/* 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;
#endif /* SCAN_SINGLE_READ */
/* Here, if too much time has elapsed since last scan,
* we may want to clean up scan results... - Jean II */
}
orinoco_unlock(priv, &flags);
return err;
}
case SIOCGIWPOWER: /* Commit handler, called after set operations */
err = orinoco_ioctl_getpower(dev, &wrq->u.power); static int orinoco_ioctl_commit(struct net_device *dev,
break; struct iw_request_info *info,
void *wrqu,
char *extra)
{
struct orinoco_private *priv = netdev_priv(dev);
struct hermes *hw = &priv->hw;
unsigned long flags;
int err = 0;
case SIOCGIWTXPOW: if (!priv->open)
/* The card only supports one tx power, so this is easy */ return 0;
wrq->u.txpower.value = 15; /* dBm */
wrq->u.txpower.fixed = 1;
wrq->u.txpower.disabled = 0;
wrq->u.txpower.flags = IW_TXPOW_DBM;
break;
case SIOCSIWRETRY: if (priv->broken_disableport) {
err = -EOPNOTSUPP; orinoco_reset(dev);
break; return 0;
}
case SIOCGIWRETRY: if (orinoco_lock(priv, &flags) != 0)
err = orinoco_ioctl_getretry(dev, &wrq->u.retry); return err;
break;
case SIOCSIWSPY: err = hermes_disable_port(hw, 0);
err = orinoco_ioctl_setspy(dev, &wrq->u.data); if (err) {
break; printk(KERN_WARNING "%s: Unable to disable port "
"while reconfiguring card\n", dev->name);
priv->broken_disableport = 1;
goto out;
}
case SIOCGIWSPY: err = __orinoco_program_rids(dev);
err = orinoco_ioctl_getspy(dev, &wrq->u.data); if (err) {
break; printk(KERN_WARNING "%s: Unable to reconfigure card\n",
dev->name);
goto out;
}
case SIOCGIWPRIV: err = hermes_enable_port(hw, 0);
if (wrq->u.data.pointer) { if (err) {
struct iw_priv_args privtab[] = { printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n",
{ SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, dev->name);
{ SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" }, goto out;
{ SIOCIWFIRSTPRIV + 0x2, }
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0, "set_port3" },
{ SIOCIWFIRSTPRIV + 0x3, 0,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"get_port3" },
{ SIOCIWFIRSTPRIV + 0x4,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0, "set_preamble" },
{ SIOCIWFIRSTPRIV + 0x5, 0,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"get_preamble" },
{ SIOCIWFIRSTPRIV + 0x6,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0, "set_ibssport" },
{ SIOCIWFIRSTPRIV + 0x7, 0,
IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"get_ibssport" },
};
wrq->u.data.length = sizeof(privtab) / sizeof(privtab[0]);
if (copy_to_user(wrq->u.data.pointer, privtab, sizeof(privtab)))
err = -EFAULT;
}
break;
case SIOCIWFIRSTPRIV + 0x0: /* force_reset */
case SIOCIWFIRSTPRIV + 0x1: /* card_reset */
if (! capable(CAP_NET_ADMIN)) {
err = -EPERM;
break;
}
printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name);
out:
if (err) {
printk(KERN_WARNING "%s: Resetting instead...\n", dev->name);
schedule_work(&priv->reset_work); schedule_work(&priv->reset_work);
break; err = 0;
}
case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */
if (! capable(CAP_NET_ADMIN)) {
err = -EPERM;
break;
}
err = orinoco_ioctl_setport3(dev, wrq);
if (! err)
changed = 1;
break;
case SIOCIWFIRSTPRIV + 0x3: /* get_port3 */ orinoco_unlock(priv, &flags);
err = orinoco_ioctl_getport3(dev, wrq); return err;
break; }
case SIOCIWFIRSTPRIV + 0x4: /* set_preamble */ static const struct iw_priv_args orinoco_privtab[] = {
if (! capable(CAP_NET_ADMIN)) { { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" },
err = -EPERM; { SIOCIWFIRSTPRIV + 0x1, 0, 0, "card_reset" },
break; { SIOCIWFIRSTPRIV + 0x2, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
} 0, "set_port3" },
{ SIOCIWFIRSTPRIV + 0x3, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"get_port3" },
{ SIOCIWFIRSTPRIV + 0x4, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0, "set_preamble" },
{ SIOCIWFIRSTPRIV + 0x5, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"get_preamble" },
{ SIOCIWFIRSTPRIV + 0x6, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
0, "set_ibssport" },
{ SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
"get_ibssport" },
{ SIOCIWFIRSTPRIV + 0x9, 0, IW_PRIV_TYPE_BYTE | MAX_RID_LEN,
"get_rid" },
};
/* 802.11b has recently defined some short preamble.
* Basically, the Phy header has been reduced in size.
* This increase performance, especially at high rates
* (the preamble is transmitted at 1Mb/s), unfortunately
* this give compatibility troubles... - Jean II */
if(priv->has_preamble) {
int val = *( (int *) wrq->u.name );
if (orinoco_lock(priv, &flags) != 0)
return -EBUSY;
if (val)
priv->preamble = 1;
else
priv->preamble = 0;
orinoco_unlock(priv, &flags);
changed = 1;
} else
err = -EOPNOTSUPP;
break;
case SIOCIWFIRSTPRIV + 0x5: /* get_preamble */ /*
if(priv->has_preamble) { * Structures to export the Wireless Handlers
int *val = (int *)wrq->u.name; */
if (orinoco_lock(priv, &flags) != 0) static const iw_handler orinoco_handler[] = {
return -EBUSY; [SIOCSIWCOMMIT-SIOCIWFIRST] (iw_handler) orinoco_ioctl_commit,
*val = priv->preamble; [SIOCGIWNAME -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getname,
orinoco_unlock(priv, &flags); [SIOCSIWFREQ -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setfreq,
} else [SIOCGIWFREQ -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getfreq,
err = -EOPNOTSUPP; [SIOCSIWMODE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setmode,
break; [SIOCGIWMODE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getmode,
case SIOCIWFIRSTPRIV + 0x6: /* set_ibssport */ [SIOCSIWSENS -SIOCIWFIRST] (iw_handler) orinoco_ioctl_setsens,
if (! capable(CAP_NET_ADMIN)) { [SIOCGIWSENS -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getsens,
err = -EPERM; [SIOCGIWRANGE -SIOCIWFIRST] (iw_handler) orinoco_ioctl_getiwrange,
break; [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,
};
err = orinoco_ioctl_setibssport(dev, wrq);
if (! err)
changed = 1;
break;
case SIOCIWFIRSTPRIV + 0x7: /* get_ibssport */ /*
err = orinoco_ioctl_getibssport(dev, wrq); Added typecasting since we no longer use iwreq_data -- Moustafa
break; */
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,
};
default: static const struct iw_handler_def orinoco_handler_def = {
err = -EOPNOTSUPP; .num_standard = ARRAY_SIZE(orinoco_handler),
} .num_private = ARRAY_SIZE(orinoco_private_handler),
.num_private_args = ARRAY_SIZE(orinoco_privtab),
if (! err && changed && netif_running(dev)) { .standard = orinoco_handler,
err = orinoco_reconfigure(dev); .private = orinoco_private_handler,
} .private_args = orinoco_privtab,
};
TRACE_EXIT(dev->name); static void orinoco_get_drvinfo(struct net_device *dev,
struct ethtool_drvinfo *info)
{
struct orinoco_private *priv = netdev_priv(dev);
return err; strncpy(info->driver, DRIVER_NAME, sizeof(info->driver) - 1);
strncpy(info->version, DRIVER_VERSION, sizeof(info->version) - 1);
strncpy(info->fw_version, priv->fw_name, sizeof(info->fw_version) - 1);
if (dev->class_dev.dev)
strncpy(info->bus_info, dev->class_dev.dev->bus_id,
sizeof(info->bus_info) - 1);
else
snprintf(info->bus_info, sizeof(info->bus_info) - 1,
"PCMCIA %p", priv->hw.iobase);
} }
static struct ethtool_ops orinoco_ethtool_ops = {
.get_drvinfo = orinoco_get_drvinfo,
.get_link = ethtool_op_get_link,
};
/********************************************************************/ /********************************************************************/
/* Debugging */ /* Debugging */
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#ifndef _ORINOCO_H #ifndef _ORINOCO_H
#define _ORINOCO_H #define _ORINOCO_H
#define DRIVER_VERSION "0.14alpha2" #define DRIVER_VERSION "0.15rc2"
#include <linux/types.h> #include <linux/types.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#define WIRELESS_SPY // enable iwspy support #define WIRELESS_SPY // enable iwspy support
#define MAX_SCAN_LEN 4096
#define ORINOCO_MAX_KEY_SIZE 14 #define ORINOCO_MAX_KEY_SIZE 14
#define ORINOCO_MAX_KEYS 4 #define ORINOCO_MAX_KEYS 4
...@@ -30,6 +32,20 @@ struct orinoco_key { ...@@ -30,6 +32,20 @@ struct orinoco_key {
char data[ORINOCO_MAX_KEY_SIZE]; char data[ORINOCO_MAX_KEY_SIZE];
} __attribute__ ((packed)); } __attribute__ ((packed));
struct header_struct {
/* 802.3 */
u8 dest[ETH_ALEN];
u8 src[ETH_ALEN];
u16 len;
/* 802.2 */
u8 dsap;
u8 ssap;
u8 ctrl;
/* SNAP */
u8 oui[3];
u16 ethertype;
} __attribute__ ((packed));
typedef enum { typedef enum {
FIRMWARE_TYPE_AGERE, FIRMWARE_TYPE_AGERE,
FIRMWARE_TYPE_INTERSIL, FIRMWARE_TYPE_INTERSIL,
...@@ -48,6 +64,8 @@ struct orinoco_private { ...@@ -48,6 +64,8 @@ struct orinoco_private {
/* driver state */ /* driver state */
int open; int open;
u16 last_linkstatus; u16 last_linkstatus;
struct work_struct join_work;
struct work_struct wevent_work;
/* Net device stuff */ /* Net device stuff */
struct net_device *ndev; struct net_device *ndev;
...@@ -74,7 +92,9 @@ struct orinoco_private { ...@@ -74,7 +92,9 @@ struct orinoco_private {
unsigned int has_pm:1; unsigned int has_pm:1;
unsigned int has_preamble:1; unsigned int has_preamble:1;
unsigned int has_sensitivity:1; unsigned int has_sensitivity:1;
unsigned int has_hostscan:1;
unsigned int broken_disableport:1; unsigned int broken_disableport:1;
unsigned int broken_monitor:1;
/* Configuration paramaters */ /* Configuration paramaters */
u32 iw_mode; u32 iw_mode;
...@@ -84,6 +104,8 @@ struct orinoco_private { ...@@ -84,6 +104,8 @@ struct orinoco_private {
int bitratemode; int bitratemode;
char nick[IW_ESSID_MAX_SIZE+1]; char nick[IW_ESSID_MAX_SIZE+1];
char desired_essid[IW_ESSID_MAX_SIZE+1]; char desired_essid[IW_ESSID_MAX_SIZE+1];
char desired_bssid[ETH_ALEN];
int bssid_fixed;
u16 frag_thresh, mwo_robust; u16 frag_thresh, mwo_robust;
u16 channel; u16 channel;
u16 ap_density, rts_thresh; u16 ap_density, rts_thresh;
...@@ -98,6 +120,12 @@ struct orinoco_private { ...@@ -98,6 +120,12 @@ struct orinoco_private {
/* Configuration dependent variables */ /* Configuration dependent variables */
int port_type, createibss; int port_type, createibss;
int promiscuous, mc_count; int promiscuous, mc_count;
/* Scanning support */
int scan_inprogress; /* Scan pending... */
u32 scan_mode; /* Type of scan done */
char * scan_result; /* Result of previous scan */
int scan_len; /* Lenght of result */
}; };
#ifdef ORINOCO_DEBUG #ifdef ORINOCO_DEBUG
......
/*
* Merged with mainline ieee80211.h in Aug 2004. Original ieee802_11
* remains copyright by the original authors
*
* Portions of the merged code are based on Host AP (software wireless
* LAN access point) driver for Intersil Prism2/2.5/3.
*
* Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen
* <jkmaline@cc.hut.fi>
* Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi>
*
* Adaption to a generic IEEE 802.11 stack by James Ketrenos
* <jketreno@linux.intel.com>
* Copyright (c) 2004, Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. See README and COPYING for
* more details.
*/
#ifndef IEEE80211_H
#define IEEE80211_H
#include <linux/if_ether.h> /* ETH_ALEN */
#include <linux/kernel.h> /* ARRAY_SIZE */
#if WIRELESS_EXT < 17
#define IW_QUAL_QUAL_INVALID 0x10
#define IW_QUAL_LEVEL_INVALID 0x20
#define IW_QUAL_NOISE_INVALID 0x40
#define IW_QUAL_QUAL_UPDATED 0x1
#define IW_QUAL_LEVEL_UPDATED 0x2
#define IW_QUAL_NOISE_UPDATED 0x4
#endif
#define IEEE80211_DATA_LEN 2304
/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section
6.2.1.1.2.
The figure in section 7.1.2 suggests a body size of up to 2312
bytes is allowed, which is a bit confusing, I suspect this
represents the 2304 bytes of real data, plus a possible 8 bytes of
WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */
#define IEEE80211_HLEN 30
#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN)
struct ieee80211_hdr {
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];
} __attribute__ ((packed));
struct ieee80211_hdr_3addr {
u16 frame_ctl;
u16 duration_id;
u8 addr1[ETH_ALEN];
u8 addr2[ETH_ALEN];
u8 addr3[ETH_ALEN];
u16 seq_ctl;
} __attribute__ ((packed));
enum eap_type {
EAP_PACKET = 0,
EAPOL_START,
EAPOL_LOGOFF,
EAPOL_KEY,
EAPOL_ENCAP_ASF_ALERT
};
static const char *eap_types[] = {
[EAP_PACKET] = "EAP-Packet",
[EAPOL_START] = "EAPOL-Start",
[EAPOL_LOGOFF] = "EAPOL-Logoff",
[EAPOL_KEY] = "EAPOL-Key",
[EAPOL_ENCAP_ASF_ALERT] = "EAPOL-Encap-ASF-Alert"
};
static inline const char *eap_get_type(int type)
{
return (type >= ARRAY_SIZE(eap_types)) ? "Unknown" : eap_types[type];
}
struct eapol {
u8 snap[6];
u16 ethertype;
u8 version;
u8 type;
u16 length;
} __attribute__ ((packed));
#define IEEE80211_3ADDR_LEN 24
#define IEEE80211_4ADDR_LEN 30
#define IEEE80211_FCS_LEN 4
#define MIN_FRAG_THRESHOLD 256U
#define MAX_FRAG_THRESHOLD 2346U
/* Frame control field constants */
#define IEEE80211_FCTL_VERS 0x0002
#define IEEE80211_FCTL_FTYPE 0x000c
#define IEEE80211_FCTL_STYPE 0x00f0
#define IEEE80211_FCTL_TODS 0x0100
#define IEEE80211_FCTL_FROMDS 0x0200
#define IEEE80211_FCTL_MOREFRAGS 0x0400
#define IEEE80211_FCTL_RETRY 0x0800
#define IEEE80211_FCTL_PM 0x1000
#define IEEE80211_FCTL_MOREDATA 0x2000
#define IEEE80211_FCTL_WEP 0x4000
#define IEEE80211_FCTL_ORDER 0x8000
#define IEEE80211_FTYPE_MGMT 0x0000
#define IEEE80211_FTYPE_CTL 0x0004
#define IEEE80211_FTYPE_DATA 0x0008
/* management */
#define IEEE80211_STYPE_ASSOC_REQ 0x0000
#define IEEE80211_STYPE_ASSOC_RESP 0x0010
#define IEEE80211_STYPE_REASSOC_REQ 0x0020
#define IEEE80211_STYPE_REASSOC_RESP 0x0030
#define IEEE80211_STYPE_PROBE_REQ 0x0040
#define IEEE80211_STYPE_PROBE_RESP 0x0050
#define IEEE80211_STYPE_BEACON 0x0080
#define IEEE80211_STYPE_ATIM 0x0090
#define IEEE80211_STYPE_DISASSOC 0x00A0
#define IEEE80211_STYPE_AUTH 0x00B0
#define IEEE80211_STYPE_DEAUTH 0x00C0
/* control */
#define IEEE80211_STYPE_PSPOLL 0x00A0
#define IEEE80211_STYPE_RTS 0x00B0
#define IEEE80211_STYPE_CTS 0x00C0
#define IEEE80211_STYPE_ACK 0x00D0
#define IEEE80211_STYPE_CFEND 0x00E0
#define IEEE80211_STYPE_CFENDACK 0x00F0
/* data */
#define IEEE80211_STYPE_DATA 0x0000
#define IEEE80211_STYPE_DATA_CFACK 0x0010
#define IEEE80211_STYPE_DATA_CFPOLL 0x0020
#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030
#define IEEE80211_STYPE_NULLFUNC 0x0040
#define IEEE80211_STYPE_CFACK 0x0050
#define IEEE80211_STYPE_CFPOLL 0x0060
#define IEEE80211_STYPE_CFACKPOLL 0x0070
#define IEEE80211_SCTL_FRAG 0x000F
#define IEEE80211_SCTL_SEQ 0xFFF0
/* debug macros */
#ifdef CONFIG_IEEE80211_DEBUG
extern u32 ieee80211_debug_level;
#define IEEE80211_DEBUG(level, fmt, args...) \
do { if (ieee80211_debug_level & (level)) \
printk(KERN_DEBUG "ieee80211: %c %s " fmt, \
in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0)
#else
#define IEEE80211_DEBUG(level, fmt, args...) do {} while (0)
#endif /* CONFIG_IEEE80211_DEBUG */
/*
* To use the debug system;
*
* If you are defining a new debug classification, simply add it to the #define
* list here in the form of:
*
* #define IEEE80211_DL_xxxx VALUE
*
* shifting value to the left one bit from the previous entry. xxxx should be
* the name of the classification (for example, WEP)
*
* You then need to either add a IEEE80211_xxxx_DEBUG() macro definition for your
* classification, or use IEEE80211_DEBUG(IEEE80211_DL_xxxx, ...) whenever you want
* to send output to that classification.
*
* To add your debug level to the list of levels seen when you perform
*
* % cat /proc/net/ipw/debug_level
*
* you simply need to add your entry to the ipw_debug_levels array.
*
* If you do not see debug_level in /proc/net/ipw then you do not have
* CONFIG_IEEE80211_DEBUG defined in your kernel configuration
*
*/
#define IEEE80211_DL_INFO (1<<0)
#define IEEE80211_DL_WX (1<<1)
#define IEEE80211_DL_SCAN (1<<2)
#define IEEE80211_DL_STATE (1<<3)
#define IEEE80211_DL_MGMT (1<<4)
#define IEEE80211_DL_FRAG (1<<5)
#define IEEE80211_DL_EAP (1<<6)
#define IEEE80211_DL_DROP (1<<7)
#define IEEE80211_DL_TX (1<<8)
#define IEEE80211_DL_RX (1<<9)
#define IEEE80211_ERROR(f, a...) printk(KERN_ERR "ieee80211: " f, ## a)
#define IEEE80211_WARNING(f, a...) printk(KERN_WARNING "ieee80211: " f, ## a)
#define IEEE80211_DEBUG_INFO(f, a...) IEEE80211_DEBUG(IEEE80211_DL_INFO, f, ## a)
#define IEEE80211_DEBUG_WX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_WX, f, ## a)
#define IEEE80211_DEBUG_SCAN(f, a...) IEEE80211_DEBUG(IEEE80211_DL_SCAN, f, ## a)
#define IEEE80211_DEBUG_STATE(f, a...) IEEE80211_DEBUG(IEEE80211_DL_STATE, f, ## a)
#define IEEE80211_DEBUG_MGMT(f, a...) IEEE80211_DEBUG(IEEE80211_DL_MGMT, f, ## a)
#define IEEE80211_DEBUG_FRAG(f, a...) IEEE80211_DEBUG(IEEE80211_DL_FRAG, f, ## a)
#define IEEE80211_DEBUG_EAP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_EAP, f, ## a)
#define IEEE80211_DEBUG_DROP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_DROP, f, ## a)
#define IEEE80211_DEBUG_TX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_TX, f, ## a)
#define IEEE80211_DEBUG_RX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_RX, f, ## a)
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <linux/if_arp.h> /* ARPHRD_ETHER */
#ifndef WIRELESS_SPY
#define WIRELESS_SPY // enable iwspy support
#endif
#include <net/iw_handler.h> // new driver API
#ifndef ETH_P_PAE
#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
#endif /* ETH_P_PAE */
#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
#ifndef ETH_P_80211_RAW
#define ETH_P_80211_RAW (ETH_P_ECONET + 1)
#endif
/* IEEE 802.11 defines */
#define P80211_OUI_LEN 3
struct ieee80211_snap_hdr {
u8 dsap; /* always 0xAA */
u8 ssap; /* always 0xAA */
u8 ctrl; /* always 0x03 */
u8 oui[P80211_OUI_LEN]; /* organizational universal id */
} __attribute__ ((packed));
#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr)
#define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE)
#define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE)
#define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG)
#define WLAN_GET_SEQ_SEQ(seq) ((seq) & IEEE80211_SCTL_SEQ)
/* Authentication algorithms */
#define WLAN_AUTH_OPEN 0
#define WLAN_AUTH_SHARED_KEY 1
#define WLAN_AUTH_CHALLENGE_LEN 128
#define WLAN_CAPABILITY_BSS (1<<0)
#define WLAN_CAPABILITY_IBSS (1<<1)
#define WLAN_CAPABILITY_CF_POLLABLE (1<<2)
#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3)
#define WLAN_CAPABILITY_PRIVACY (1<<4)
#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5)
#define WLAN_CAPABILITY_PBCC (1<<6)
#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7)
/* Status codes */
#define WLAN_STATUS_SUCCESS 0
#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
#define WLAN_STATUS_CAPS_UNSUPPORTED 10
#define WLAN_STATUS_REASSOC_NO_ASSOC 11
#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
#define WLAN_STATUS_CHALLENGE_FAIL 15
#define WLAN_STATUS_AUTH_TIMEOUT 16
#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
#define WLAN_STATUS_ASSOC_DENIED_RATES 18
/* 802.11b */
#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
/* Reason codes */
#define WLAN_REASON_UNSPECIFIED 1
#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
#define WLAN_REASON_DEAUTH_LEAVING 3
#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
#define WLAN_REASON_DISASSOC_AP_BUSY 5
#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
/* Information Element IDs */
#define WLAN_EID_SSID 0
#define WLAN_EID_SUPP_RATES 1
#define WLAN_EID_FH_PARAMS 2
#define WLAN_EID_DS_PARAMS 3
#define WLAN_EID_CF_PARAMS 4
#define WLAN_EID_TIM 5
#define WLAN_EID_IBSS_PARAMS 6
#define WLAN_EID_CHALLENGE 16
#define WLAN_EID_RSN 48
#define WLAN_EID_GENERIC 221
#define IEEE80211_MGMT_HDR_LEN 24
#define IEEE80211_DATA_HDR3_LEN 24
#define IEEE80211_DATA_HDR4_LEN 30
#define IEEE80211_STATMASK_SIGNAL (1<<0)
#define IEEE80211_STATMASK_RSSI (1<<1)
#define IEEE80211_STATMASK_NOISE (1<<2)
#define IEEE80211_STATMASK_RATE (1<<3)
#define IEEE80211_STATMASK_WEMASK 0x7
#define IEEE80211_CCK_MODULATION (1<<0)
#define IEEE80211_OFDM_MODULATION (1<<1)
#define IEEE80211_24GHZ_BAND (1<<0)
#define IEEE80211_52GHZ_BAND (1<<1)
#define IEEE80211_CCK_RATE_1MB 0x02
#define IEEE80211_CCK_RATE_2MB 0x04
#define IEEE80211_CCK_RATE_5MB 0x0B
#define IEEE80211_CCK_RATE_11MB 0x16
#define IEEE80211_OFDM_RATE_6MB 0x0C
#define IEEE80211_OFDM_RATE_9MB 0x12
#define IEEE80211_OFDM_RATE_12MB 0x18
#define IEEE80211_OFDM_RATE_18MB 0x24
#define IEEE80211_OFDM_RATE_24MB 0x30
#define IEEE80211_OFDM_RATE_36MB 0x48
#define IEEE80211_OFDM_RATE_48MB 0x60
#define IEEE80211_OFDM_RATE_54MB 0x6C
#define IEEE80211_BASIC_RATE_MASK 0x80
#define IEEE80211_CCK_RATE_1MB_MASK (1<<0)
#define IEEE80211_CCK_RATE_2MB_MASK (1<<1)
#define IEEE80211_CCK_RATE_5MB_MASK (1<<2)
#define IEEE80211_CCK_RATE_11MB_MASK (1<<3)
#define IEEE80211_OFDM_RATE_6MB_MASK (1<<4)
#define IEEE80211_OFDM_RATE_9MB_MASK (1<<5)
#define IEEE80211_OFDM_RATE_12MB_MASK (1<<6)
#define IEEE80211_OFDM_RATE_18MB_MASK (1<<7)
#define IEEE80211_OFDM_RATE_24MB_MASK (1<<8)
#define IEEE80211_OFDM_RATE_36MB_MASK (1<<9)
#define IEEE80211_OFDM_RATE_48MB_MASK (1<<10)
#define IEEE80211_OFDM_RATE_54MB_MASK (1<<11)
#define IEEE80211_CCK_RATES_MASK 0x0000000F
#define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \
IEEE80211_CCK_RATE_2MB_MASK)
#define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \
IEEE80211_CCK_RATE_5MB_MASK | \
IEEE80211_CCK_RATE_11MB_MASK)
#define IEEE80211_OFDM_RATES_MASK 0x00000FF0
#define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \
IEEE80211_OFDM_RATE_12MB_MASK | \
IEEE80211_OFDM_RATE_24MB_MASK)
#define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \
IEEE80211_OFDM_RATE_9MB_MASK | \
IEEE80211_OFDM_RATE_18MB_MASK | \
IEEE80211_OFDM_RATE_36MB_MASK | \
IEEE80211_OFDM_RATE_48MB_MASK | \
IEEE80211_OFDM_RATE_54MB_MASK)
#define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \
IEEE80211_CCK_DEFAULT_RATES_MASK)
#define IEEE80211_NUM_OFDM_RATES 8
#define IEEE80211_NUM_CCK_RATES 4
#define IEEE80211_OFDM_SHIFT_MASK_A 4
/* NOTE: This data is for statistical purposes; not all hardware provides this
* information for frames received. Not setting these will not cause
* any adverse affects. */
struct ieee80211_rx_stats {
u32 mac_time;
s8 rssi;
u8 signal;
u8 noise;
u16 rate; /* in 100 kbps */
u8 received_channel;
u8 control;
u8 mask;
u8 freq;
u16 len;
};
/* IEEE 802.11 requires that STA supports concurrent reception of at least
* three fragmented frames. This define can be increased to support more
* concurrent frames, but it should be noted that each entry can consume about
* 2 kB of RAM and increasing cache size will slow down frame reassembly. */
#define IEEE80211_FRAG_CACHE_LEN 4
struct ieee80211_frag_entry {
unsigned long first_frag_time;
unsigned int seq;
unsigned int last_frag;
struct sk_buff *skb;
u8 src_addr[ETH_ALEN];
u8 dst_addr[ETH_ALEN];
};
struct ieee80211_stats {
unsigned int tx_unicast_frames;
unsigned int tx_multicast_frames;
unsigned int tx_fragments;
unsigned int tx_unicast_octets;
unsigned int tx_multicast_octets;
unsigned int tx_deferred_transmissions;
unsigned int tx_single_retry_frames;
unsigned int tx_multiple_retry_frames;
unsigned int tx_retry_limit_exceeded;
unsigned int tx_discards;
unsigned int rx_unicast_frames;
unsigned int rx_multicast_frames;
unsigned int rx_fragments;
unsigned int rx_unicast_octets;
unsigned int rx_multicast_octets;
unsigned int rx_fcs_errors;
unsigned int rx_discards_no_buffer;
unsigned int tx_discards_wrong_sa;
unsigned int rx_discards_undecryptable;
unsigned int rx_message_in_msg_fragments;
unsigned int rx_message_in_bad_msg_fragments;
};
struct ieee80211_device;
#define SEC_KEY_1 (1<<0)
#define SEC_KEY_2 (1<<1)
#define SEC_KEY_3 (1<<2)
#define SEC_KEY_4 (1<<3)
#define SEC_ACTIVE_KEY (1<<4)
#define SEC_AUTH_MODE (1<<5)
#define SEC_UNICAST_GROUP (1<<6)
#define SEC_LEVEL (1<<7)
#define SEC_ENABLED (1<<8)
#define SEC_LEVEL_0 0 /* None */
#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */
#define SEC_LEVEL_2 2 /* Level 1 + TKIP */
#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */
#define SEC_LEVEL_3 4 /* Level 2 + CCMP */
#define WEP_KEYS 4
#define WEP_KEY_LEN 13
struct ieee80211_security {
u16 active_key:2,
enabled:1,
auth_mode:2,
auth_algo:4,
unicast_uses_group:1;
u8 key_sizes[WEP_KEYS];
u8 keys[WEP_KEYS][WEP_KEY_LEN];
u8 level;
u16 flags;
} __attribute__ ((packed));
/*
802.11 data frame from AP
,-------------------------------------------------------------------.
Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 |
|------|------|---------|---------|---------|------|---------|------|
Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs |
| | tion | (BSSID) | | | ence | data | |
`-------------------------------------------------------------------'
Total: 28-2340 bytes
*/
struct ieee80211_header_data {
u16 frame_ctl;
u16 duration_id;
u8 addr1[6];
u8 addr2[6];
u8 addr3[6];
u16 seq_ctrl;
};
#define BEACON_PROBE_SSID_ID_POSITION 12
/* Management Frame Information Element Types */
#define MFIE_TYPE_SSID 0
#define MFIE_TYPE_RATES 1
#define MFIE_TYPE_FH_SET 2
#define MFIE_TYPE_DS_SET 3
#define MFIE_TYPE_CF_SET 4
#define MFIE_TYPE_TIM 5
#define MFIE_TYPE_IBSS_SET 6
#define MFIE_TYPE_CHALLENGE 16
#define MFIE_TYPE_RSN 48
#define MFIE_TYPE_RATES_EX 50
#define MFIE_TYPE_GENERIC 221
struct ieee80211_info_element_hdr {
u8 id;
u8 len;
} __attribute__ ((packed));
struct ieee80211_info_element {
u8 id;
u8 len;
u8 data[0];
} __attribute__ ((packed));
/*
* These are the data types that can make up management packets
*
u16 auth_algorithm;
u16 auth_sequence;
u16 beacon_interval;
u16 capability;
u8 current_ap[ETH_ALEN];
u16 listen_interval;
struct {
u16 association_id:14, reserved:2;
} __attribute__ ((packed));
u32 time_stamp[2];
u16 reason;
u16 status;
*/
struct ieee80211_authentication {
struct ieee80211_header_data header;
u16 algorithm;
u16 transaction;
u16 status;
struct ieee80211_info_element info_element;
} __attribute__ ((packed));
struct ieee80211_probe_response {
struct ieee80211_header_data header;
u32 time_stamp[2];
u16 beacon_interval;
u16 capability;
struct ieee80211_info_element info_element;
} __attribute__ ((packed));
struct ieee80211_assoc_request_frame {
u16 capability;
u16 listen_interval;
u8 current_ap[ETH_ALEN];
struct ieee80211_info_element info_element;
} __attribute__ ((packed));
struct ieee80211_assoc_response_frame {
struct ieee80211_hdr_3addr header;
u16 capability;
u16 status;
u16 aid;
struct ieee80211_info_element info_element; /* supported rates */
} __attribute__ ((packed));
struct ieee80211_txb {
u8 nr_frags;
u8 encrypted;
u16 reserved;
u16 frag_size;
u16 payload_size;
struct sk_buff *fragments[0];
};
/* SWEEP TABLE ENTRIES NUMBER*/
#define MAX_SWEEP_TAB_ENTRIES 42
#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7
/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs
* only use 8, and then use extended rates for the remaining supported
* rates. Other APs, however, stick all of their supported rates on the
* main rates information element... */
#define MAX_RATES_LENGTH ((u8)12)
#define MAX_RATES_EX_LENGTH ((u8)16)
#define MAX_NETWORK_COUNT 128
#define CRC_LENGTH 4U
#define MAX_WPA_IE_LEN 64
#define NETWORK_EMPTY_ESSID (1<<0)
#define NETWORK_HAS_OFDM (1<<1)
#define NETWORK_HAS_CCK (1<<2)
struct ieee80211_network {
/* These entries are used to identify a unique network */
u8 bssid[ETH_ALEN];
u8 channel;
/* Ensure null-terminated for any debug msgs */
u8 ssid[IW_ESSID_MAX_SIZE + 1];
u8 ssid_len;
/* These are network statistics */
struct ieee80211_rx_stats stats;
u16 capability;
u8 rates[MAX_RATES_LENGTH];
u8 rates_len;
u8 rates_ex[MAX_RATES_EX_LENGTH];
u8 rates_ex_len;
unsigned long last_scanned;
u8 mode;
u8 flags;
u32 last_associate;
u32 time_stamp[2];
u16 beacon_interval;
u16 listen_interval;
u16 atim_window;
u8 wpa_ie[MAX_WPA_IE_LEN];
size_t wpa_ie_len;
u8 rsn_ie[MAX_WPA_IE_LEN];
size_t rsn_ie_len;
struct list_head list;
};
enum ieee80211_state {
IEEE80211_UNINITIALIZED = 0,
IEEE80211_INITIALIZED,
IEEE80211_ASSOCIATING,
IEEE80211_ASSOCIATED,
IEEE80211_AUTHENTICATING,
IEEE80211_AUTHENTICATED,
IEEE80211_SHUTDOWN
};
#define DEFAULT_MAX_SCAN_AGE (15 * HZ)
#define DEFAULT_FTS 2346
#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5]
extern inline int is_broadcast_ether_addr(const u8 *addr)
{
return ((addr[0] == 0xff) && (addr[1] == 0xff) && (addr[2] == 0xff) && \
(addr[3] == 0xff) && (addr[4] == 0xff) && (addr[5] == 0xff));
}
#define CFG_IEEE80211_RESERVE_FCS (1<<0)
#define CFG_IEEE80211_COMPUTE_FCS (1<<1)
struct ieee80211_device {
struct net_device *dev;
/* Bookkeeping structures */
struct net_device_stats stats;
struct ieee80211_stats ieee_stats;
/* Probe / Beacon management */
struct list_head network_free_list;
struct list_head network_list;
struct ieee80211_network *networks;
int scans;
int scan_age;
int iw_mode; /* operating mode (IW_MODE_*) */
spinlock_t lock;
int tx_headroom; /* Set to size of any additional room needed at front
* of allocated Tx SKBs */
u32 config;
/* WEP and other encryption related settings at the device level */
int open_wep; /* Set to 1 to allow unencrypted frames */
int reset_on_keychange; /* Set to 1 if the HW needs to be reset on
* WEP key changes */
/* If the host performs {en,de}cryption, then set to 1 */
int host_encrypt;
int host_decrypt;
int ieee802_1x; /* is IEEE 802.1X used */
/* WPA data */
int wpa_enabled;
int drop_unencrypted;
int tkip_countermeasures;
int privacy_invoked;
size_t wpa_ie_len;
u8 *wpa_ie;
struct list_head crypt_deinit_list;
struct ieee80211_crypt_data *crypt[WEP_KEYS];
int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */
struct timer_list crypt_deinit_timer;
int bcrx_sta_key; /* use individual keys to override default keys even
* with RX of broad/multicast frames */
/* Fragmentation structures */
struct ieee80211_frag_entry frag_cache[IEEE80211_FRAG_CACHE_LEN];
unsigned int frag_next_idx;
u16 fts; /* Fragmentation Threshold */
/* Association info */
u8 bssid[ETH_ALEN];
enum ieee80211_state state;
int mode; /* A, B, G */
int modulation; /* CCK, OFDM */
int freq_band; /* 2.4Ghz, 5.2Ghz, Mixed */
int abg_ture; /* ABG flag */
/* Callback functions */
void (*set_security)(struct net_device *dev,
struct ieee80211_security *sec);
int (*hard_start_xmit)(struct ieee80211_txb *txb,
struct net_device *dev);
int (*reset_port)(struct net_device *dev);
/* This must be the last item so that it points to the data
* allocated beyond this structure by alloc_ieee80211 */
u8 priv[0];
};
#define IEEE_A (1<<0)
#define IEEE_B (1<<1)
#define IEEE_G (1<<2)
#define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G)
extern inline void *ieee80211_priv(struct net_device *dev)
{
return ((struct ieee80211_device *)netdev_priv(dev))->priv;
}
extern inline int ieee80211_is_empty_essid(const char *essid, int essid_len)
{
/* Single white space is for Linksys APs */
if (essid_len == 1 && essid[0] == ' ')
return 1;
/* Otherwise, if the entire essid is 0, we assume it is hidden */
while (essid_len) {
essid_len--;
if (essid[essid_len] != '\0')
return 0;
}
return 1;
}
extern inline int ieee80211_is_valid_mode(struct ieee80211_device *ieee, int mode)
{
/*
* It is possible for both access points and our device to support
* combinations of modes, so as long as there is one valid combination
* of ap/device supported modes, then return success
*
*/
if ((mode & IEEE_A) &&
(ieee->modulation & IEEE80211_OFDM_MODULATION) &&
(ieee->freq_band & IEEE80211_52GHZ_BAND))
return 1;
if ((mode & IEEE_G) &&
(ieee->modulation & IEEE80211_OFDM_MODULATION) &&
(ieee->freq_band & IEEE80211_24GHZ_BAND))
return 1;
if ((mode & IEEE_B) &&
(ieee->modulation & IEEE80211_CCK_MODULATION) &&
(ieee->freq_band & IEEE80211_24GHZ_BAND))
return 1;
return 0;
}
extern inline int ieee80211_get_hdrlen(u16 fc)
{
int hdrlen = 24;
switch (WLAN_FC_GET_TYPE(fc)) {
case IEEE80211_FTYPE_DATA:
if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS))
hdrlen = 30; /* Addr4 */
break;
case IEEE80211_FTYPE_CTL:
switch (WLAN_FC_GET_STYPE(fc)) {
case IEEE80211_STYPE_CTS:
case IEEE80211_STYPE_ACK:
hdrlen = 10;
break;
default:
hdrlen = 16;
break;
}
break;
}
return hdrlen;
}
/* ieee80211.c */
extern void free_ieee80211(struct net_device *dev);
extern struct net_device *alloc_ieee80211(int sizeof_priv);
extern int ieee80211_set_encryption(struct ieee80211_device *ieee);
/* ieee80211_tx.c */
extern int ieee80211_xmit(struct sk_buff *skb,
struct net_device *dev);
extern void ieee80211_txb_free(struct ieee80211_txb *);
/* ieee80211_rx.c */
extern int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb,
struct ieee80211_rx_stats *rx_stats);
extern void ieee80211_rx_mgt(struct ieee80211_device *ieee,
struct ieee80211_hdr *header,
struct ieee80211_rx_stats *stats);
/* iee80211_wx.c */
extern int ieee80211_wx_get_scan(struct ieee80211_device *ieee,
struct iw_request_info *info,
union iwreq_data *wrqu, char *key);
extern int ieee80211_wx_set_encode(struct ieee80211_device *ieee,
struct iw_request_info *info,
union iwreq_data *wrqu, char *key);
extern int ieee80211_wx_get_encode(struct ieee80211_device *ieee,
struct iw_request_info *info,
union iwreq_data *wrqu, char *key);
extern inline void ieee80211_increment_scans(struct ieee80211_device *ieee)
{
ieee->scans++;
}
extern inline int ieee80211_get_scans(struct ieee80211_device *ieee)
{
return ieee->scans;
}
static inline const char *escape_essid(const char *essid, u8 essid_len) {
static char escaped[IW_ESSID_MAX_SIZE * 2 + 1];
const char *s = essid;
char *d = escaped;
if (ieee80211_is_empty_essid(essid, essid_len)) {
memcpy(escaped, "<hidden>", sizeof("<hidden>"));
return escaped;
}
essid_len = min(essid_len, (u8)IW_ESSID_MAX_SIZE);
while (essid_len--) {
if (*s == '\0') {
*d++ = '\\';
*d++ = '0';
s++;
} else {
*d++ = *s++;
}
}
*d = '\0';
return escaped;
}
#endif /* IEEE80211_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment