diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/wireless/orinoco.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/net/wireless/orinoco.c')
-rw-r--r-- | drivers/net/wireless/orinoco.c | 4243 |
1 files changed, 4243 insertions, 0 deletions
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c new file mode 100644 index 00000000000..a3a32430ae9 --- /dev/null +++ b/drivers/net/wireless/orinoco.c @@ -0,0 +1,4243 @@ +/* orinoco.c - (formerly known as dldwd_cs.c and orinoco_cs.c) + * + * A driver for Hermes or Prism 2 chipset based PCMCIA wireless + * adaptors, with Lucent/Agere, Intersil or Symbol firmware. + * + * Current maintainers (as of 29 September 2003) are: + * Pavel Roskin <proski AT gnu.org> + * and David Gibson <hermes AT gibson.dropbear.id.au> + * + * (C) Copyright David Gibson, IBM Corporation 2001-2003. + * Copyright (C) 2000 David Gibson, Linuxcare Australia. + * With some help from : + * Copyright (C) 2001 Jean Tourrilhes, HP Labs + * Copyright (C) 2001 Benjamin Herrenschmidt + * + * Based on dummy_cs.c 1.27 2000/06/12 21:27:25 + * + * Portions based on wvlan_cs.c 1.0.6, Copyright Andreas Neuhaus <andy + * AT fasta.fh-dortmund.de> + * http://www.stud.fh-dortmund.de/~andy/wvlan/ + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License + * at http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and + * limitations under the License. + * + * The initial developer of the original code is David A. Hinds + * <dahinds AT users.sourceforge.net>. Portions created by David + * A. Hinds are Copyright (C) 1999 David A. Hinds. All Rights + * Reserved. + * + * Alternatively, the contents of this file may be used under the + * terms of the GNU General Public License version 2 (the "GPL"), in + * which case the provisions of the GPL are applicable instead of the + * above. If you wish to allow the use of your version of this file + * only under the terms of the GPL and not to allow others to use your + * version of this file under the MPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the GPL. If you do not delete the + * provisions above, a recipient may use your version of this file + * under either the MPL or the GPL. */ + +/* + * v0.01 -> v0.02 - 21/3/2001 - Jean II + * o Allow to use regular ethX device name instead of dldwdX + * o Warning on IBSS with ESSID=any for firmware 6.06 + * o Put proper range.throughput values (optimistic) + * o IWSPY support (IOCTL and stat gather in Rx path) + * o Allow setting frequency in Ad-Hoc mode + * o Disable WEP setting if !has_wep to work on old firmware + * o Fix txpower range + * o Start adding support for Samsung/Compaq firmware + * + * v0.02 -> v0.03 - 23/3/2001 - Jean II + * o Start adding Symbol support - need to check all that + * o Fix Prism2/Symbol WEP to accept 128 bits keys + * o Add Symbol WEP (add authentication type) + * o Add Prism2/Symbol rate + * o Add PM timeout (holdover duration) + * o Enable "iwconfig eth0 key off" and friends (toggle flags) + * o Enable "iwconfig eth0 power unicast/all" (toggle flags) + * o Try with an Intel card. It report firmware 1.01, behave like + * an antiquated firmware, however on windows it says 2.00. Yuck ! + * o Workaround firmware bug in allocate buffer (Intel 1.01) + * o Finish external renaming to orinoco... + * o Testing with various Wavelan firmwares + * + * v0.03 -> v0.04 - 30/3/2001 - Jean II + * o Update to Wireless 11 -> add retry limit/lifetime support + * o Tested with a D-Link DWL 650 card, fill in firmware support + * o Warning on Vcc mismatch (D-Link 3.3v card in Lucent 5v only slot) + * o Fixed the Prism2 WEP bugs that I introduced in v0.03 :-( + * It works on D-Link *only* after a tcpdump. Weird... + * And still doesn't work on Intel card. Grrrr... + * o Update the mode after a setport3 + * o Add preamble setting for Symbol cards (not yet enabled) + * o Don't complain as much about Symbol cards... + * + * v0.04 -> v0.04b - 22/4/2001 - David Gibson + * o Removed the 'eth' parameter - always use ethXX as the + * interface name instead of dldwdXX. The other was racy + * anyway. + * o Clean up RID definitions in hermes.h, other cleanups + * + * v0.04b -> v0.04c - 24/4/2001 - Jean II + * o Tim Hurley <timster AT seiki.bliztech.com> reported a D-Link card + * with vendor 02 and firmware 0.08. Added in the capabilities... + * o Tested Lucent firmware 7.28, everything works... + * + * v0.04c -> v0.05 - 3/5/2001 - Benjamin Herrenschmidt + * o Spin-off Pcmcia code. This file is renamed orinoco.c, + * and orinoco_cs.c now contains only the Pcmcia specific stuff + * o Add Airport driver support on top of orinoco.c (see airport.c) + * + * v0.05 -> v0.05a - 4/5/2001 - Jean II + * o Revert to old Pcmcia code to fix breakage of Ben's changes... + * + * v0.05a -> v0.05b - 4/5/2001 - Jean II + * o add module parameter 'ignore_cis_vcc' for D-Link @ 5V + * o D-Link firmware doesn't support multicast. We just print a few + * error messages, but otherwise everything works... + * o For David : set/getport3 works fine, just upgrade iwpriv... + * + * v0.05b -> v0.05c - 5/5/2001 - Benjamin Herrenschmidt + * o Adapt airport.c to latest changes in orinoco.c + * o Remove deferred power enabling code + * + * v0.05c -> v0.05d - 5/5/2001 - Jean II + * o Workaround to SNAP decapsulate frame from Linksys AP + * original patch from : Dong Liu <dliu AT research.bell-labs.com> + * (note : the memcmp bug was mine - fixed) + * o Remove set_retry stuff, no firmware support it (bloat--). + * + * v0.05d -> v0.06 - 25/5/2001 - Jean II + * Original patch from "Hong Lin" <alin AT redhat.com>, + * "Ian Kinner" <ikinner AT redhat.com> + * and "David Smith" <dsmith AT redhat.com> + * o Init of priv->tx_rate_ctrl in firmware specific section. + * o Prism2/Symbol rate, upto should be 0xF and not 0x15. Doh ! + * o Spectrum card always need cor_reset (for every reset) + * o Fix cor_reset to not lose bit 7 in the register + * o flush_stale_links to remove zombie Pcmcia instances + * o Ack previous hermes event before reset + * Me (with my little hands) + * o Allow orinoco.c to call cor_reset via priv->card_reset_handler + * o Add priv->need_card_reset to toggle this feature + * o Fix various buglets when setting WEP in Symbol firmware + * Now, encryption is fully functional on Symbol cards. Youpi ! + * + * v0.06 -> v0.06b - 25/5/2001 - Jean II + * o IBSS on Symbol use port_mode = 4. Please don't ask... + * + * v0.06b -> v0.06c - 29/5/2001 - Jean II + * o Show first spy address in /proc/net/wireless for IBSS mode as well + * + * v0.06c -> v0.06d - 6/7/2001 - David Gibson + * o Change a bunch of KERN_INFO messages to KERN_DEBUG, as per Linus' + * wishes to reduce the number of unnecessary messages. + * o Removed bogus message on CRC error. + * o Merged fixes for v0.08 Prism 2 firmware from William Waghorn + * <willwaghorn AT yahoo.co.uk> + * o Slight cleanup/re-arrangement of firmware detection code. + * + * v0.06d -> v0.06e - 1/8/2001 - David Gibson + * o Removed some redundant global initializers (orinoco_cs.c). + * o Added some module metadata + * + * v0.06e -> v0.06f - 14/8/2001 - David Gibson + * o Wording fix to license + * o Added a 'use_alternate_encaps' module parameter for APs which need an + * oui of 00:00:00. We really need a better way of handling this, but + * the module flag is better than nothing for now. + * + * v0.06f -> v0.07 - 20/8/2001 - David Gibson + * o Removed BAP error retries from hermes_bap_seek(). For Tx we now + * let the upper layers handle the retry, we retry explicitly in the + * Rx path, but don't make as much noise about it. + * o Firmware detection cleanups. + * + * v0.07 -> v0.07a - 1/10/3001 - Jean II + * o Add code to read Symbol firmware revision, inspired by latest code + * in Spectrum24 by Lee John Keyser-Allen - Thanks Lee ! + * o Thanks to Jared Valentine <hidden AT xmission.com> for "providing" me + * a 3Com card with a recent firmware, fill out Symbol firmware + * capabilities of latest rev (2.20), as well as older Symbol cards. + * o Disable Power Management in newer Symbol firmware, the API + * has changed (documentation needed). + * + * v0.07a -> v0.08 - 3/10/2001 - David Gibson + * o Fixed a possible buffer overrun found by the Stanford checker (in + * dldwd_ioctl_setiwencode()). Can only be called by root anyway, so not + * a big problem. + * o Turned has_big_wep on for Intersil cards. That's not true for all of + * them but we should at least let the capable ones try. + * o Wait for BUSY to clear at the beginning of hermes_bap_seek(). I + * realized that my assumption that the driver's serialization + * would prevent the BAP being busy on entry was possibly false, because + * things other than seeks may make the BAP busy. + * o Use "alternate" (oui 00:00:00) encapsulation by default. + * Setting use_old_encaps will mimic the old behaviour, but I think we + * will be able to eliminate this. + * o Don't try to make __initdata const (the version string). This can't + * work because of the way the __initdata sectioning works. + * o Added MODULE_LICENSE tags. + * o Support for PLX (transparent PCMCIA->PCI bridge) cards. + * o Changed to using the new type-fascist min/max. + * + * v0.08 -> v0.08a - 9/10/2001 - David Gibson + * o Inserted some missing acknowledgements/info into the Changelog. + * o Fixed some bugs in the normalization of signal level reporting. + * o Fixed bad bug in WEP key handling on Intersil and Symbol firmware, + * which led to an instant crash on big-endian machines. + * + * v0.08a -> v0.08b - 20/11/2001 - David Gibson + * o Lots of cleanup and bugfixes in orinoco_plx.c + * o Cleanup to handling of Tx rate setting. + * o Removed support for old encapsulation method. + * o Removed old "dldwd" names. + * o Split RID constants into a new file hermes_rid.h + * o Renamed RID constants to match linux-wlan-ng and prism2.o + * o Bugfixes in hermes.c + * o Poke the PLX's INTCSR register, so it actually starts + * generating interrupts. These cards might actually work now. + * o Update to wireless extensions v12 (Jean II) + * o Support for tallies and inquire command (Jean II) + * o Airport updates for newer PPC kernels (BenH) + * + * v0.08b -> v0.09 - 21/12/2001 - David Gibson + * o Some new PCI IDs for PLX cards. + * o Removed broken attempt to do ALLMULTI reception. Just use + * promiscuous mode instead + * o Preliminary work for list-AP (Jean II) + * o Airport updates from (BenH) + * o Eliminated racy hw_ready stuff + * o Fixed generation of fake events in irq handler. This should + * finally kill the EIO problems (Jean II & dgibson) + * o Fixed breakage of bitrate set/get on Agere firmware (Jean II) + * + * v0.09 -> v0.09a - 2/1/2002 - David Gibson + * o Fixed stupid mistake in multicast list handling, triggering + * a BUG() + * + * v0.09a -> v0.09b - 16/1/2002 - David Gibson + * o Fixed even stupider mistake in new interrupt handling, which + * seriously broke things on big-endian machines. + * o Removed a bunch of redundant includes and exports. + * o Removed a redundant MOD_{INC,DEC}_USE_COUNT pair in airport.c + * o Don't attempt to do hardware level multicast reception on + * Intersil firmware, just go promisc instead. + * o Typo fixed in hermes_issue_cmd() + * o Eliminated WIRELESS_SPY #ifdefs + * o Status code reported on Tx exceptions + * o Moved netif_wake_queue() from ALLOC interrupts to TX and TXEXC + * interrupts, which should fix the timeouts we're seeing. + * + * v0.09b -> v0.10 - 25 Feb 2002 - David Gibson + * o Removed nested structures used for header parsing, so the + * driver should now work without hackery on ARM + * o Fix for WEP handling on Intersil (Hawk Newton) + * o Eliminated the /proc/hermes/ethXX/regs debugging file. It + * was never very useful. + * o Make Rx errors less noisy. + * + * v0.10 -> v0.11 - 5 Apr 2002 - David Gibson + * o Laid the groundwork in hermes.[ch] for devices which map + * into PCI memory space rather than IO space. + * o Fixed bug in multicast handling (cleared multicast list when + * leaving promiscuous mode). + * o Relegated Tx error messages to debug. + * o Cleaned up / corrected handling of allocation lengths. + * o Set OWNSSID in IBSS mode for WinXP interoperability (jimc). + * o Change to using alloc_etherdev() for structure allocations. + * o Check for and drop undersized packets. + * o Fixed a race in stopping/waking the queue. This should fix + * the timeout problems (Pavel Roskin) + * o Reverted to netif_wake_queue() on the ALLOC event. + * o Fixes for recent Symbol firmwares which lack AP density + * (Pavel Roskin). + * + * v0.11 -> v0.11a - 29 Apr 2002 - David Gibson + * o Handle different register spacing, necessary for Prism 2.5 + * PCI adaptors (Steve Hill). + * o Cleaned up initialization of card structures in orinoco_cs + * and airport. Removed card->priv field. + * o Make response structure optional for hermes_docmd_wait() + * Pavel Roskin) + * o Added PCI id for Nortel emobility to orinoco_plx.c. + * o Cleanup to handling of Symbol's allocation bug. (Pavel Roskin) + * o Cleanups to firmware capability detection. + * o Arrange for orinoco_pci.c to override firmware detection. + * We should be able to support the PCI Intersil cards now. + * o Cleanup handling of reset_cor and hard_reset (Pavel Roskin). + * o Remove erroneous use of USER_BAP in the TxExc handler (Jouni + * Malinen). + * o Makefile changes for better integration into David Hinds + * pcmcia-cs package. + * + * v0.11a -> v0.11b - 1 May 2002 - David Gibson + * o Better error reporting in orinoco_plx_init_one() + * o Fixed multiple bad kfree() bugs introduced by the + * alloc_orinocodev() changes. + * + * v0.11b -> v0.12 - 19 Jun 2002 - David Gibson + * o Support changing the MAC address. + * o Correct display of Intersil firmware revision numbers. + * o Entirely revised locking scheme. Should be both simpler and + * better. + * o Merged some common code in orinoco_plx, orinoco_pci and + * airport by creating orinoco_default_{open,stop,reset}() + * which are used as the dev->open, dev->stop, priv->reset + * callbacks if none are specified when alloc_orinocodev() is + * called. + * o Removed orinoco_plx_interrupt() and orinoco_pci_interrupt(). + * They didn't do anything. + * + * v0.12 -> v0.12a - 4 Jul 2002 - David Gibson + * o Some rearrangement of code. + * o Numerous fixups to locking and rest handling, particularly + * for PCMCIA. + * o This allows open and stop net_device methods to be in + * orinoco.c now, rather than in the init modules. + * o In orinoco_cs.c link->priv now points to the struct + * net_device not to the struct orinoco_private. + * o Added a check for undersized SNAP frames, which could cause + * crashes. + * + * v0.12a -> v0.12b - 11 Jul 2002 - David Gibson + * o Fix hw->num_init testing code, so num_init is actually + * incremented. + * o Fix very stupid bug in orinoco_cs which broke compile with + * CONFIG_SMP. + * o Squashed a warning. + * + * v0.12b -> v0.12c - 26 Jul 2002 - David Gibson + * o Change to C9X style designated initializers. + * o Add support for 3Com AirConnect PCI. + * o No longer ignore the hard_reset argument to + * alloc_orinocodev(). Oops. + * + * v0.12c -> v0.13beta1 - 13 Sep 2002 - David Gibson + * o Revert the broken 0.12* locking scheme and go to a new yet + * simpler scheme. + * o Do firmware resets only in orinoco_init() and when waking + * the card from hard sleep. + * + * v0.13beta1 -> v0.13 - 27 Sep 2002 - David Gibson + * o Re-introduced full resets (via schedule_task()) on Tx + * timeout. + * + * v0.13 -> v0.13a - 30 Sep 2002 - David Gibson + * o Minor cleanups to info frame handling. Add basic support + * for linkstatus info frames. + * o Include required kernel headers in orinoco.h, to avoid + * compile problems. + * + * v0.13a -> v0.13b - 10 Feb 2003 - David Gibson + * o Implemented hard reset for Airport cards + * o Experimental suspend/resume implementation for orinoco_pci + * o Abolished /proc debugging support, replaced with a debugging + * iwpriv. Now it's ugly and simple instead of ugly and complex. + * o Bugfix in hermes.c if the firmware returned a record length + * of 0, we could go clobbering memory. + * o Bugfix in orinoco_stop() - it used to fail if hw_unavailable + * was set, which was usually true on PCMCIA hot removes. + * o Track LINKSTATUS messages, silently drop Tx packets before + * we are connected (avoids confusing the firmware), and only + * give LINKSTATUS printk()s if the status has changed. + * + * v0.13b -> v0.13c - 11 Mar 2003 - David Gibson + * o Cleanup: use dev instead of priv in various places. + * o Bug fix: Don't ReleaseConfiguration on RESET_PHYSICAL event + * if we're in the middle of a (driver initiated) hard reset. + * o Bug fix: ETH_ZLEN is supposed to include the header + * (Dionysus Blazakis & Manish Karir) + * o Convert to using workqueues instead of taskqueues (and + * backwards compatibility macros for pre 2.5.41 kernels). + * o Drop redundant (I think...) MOD_{INC,DEC}_USE_COUNT in + * airport.c + * o New orinoco_tmd.c init module from Joerg Dorchain for + * TMD7160 based PCI to PCMCIA bridges (similar to + * orinoco_plx.c). + * + * v0.13c -> v0.13d - 22 Apr 2003 - David Gibson + * o Make hw_unavailable a counter, rather than just a flag, this + * is necessary to avoid some races (such as a card being + * removed in the middle of orinoco_reset(). + * o Restore Release/RequestConfiguration in the PCMCIA event handler + * when dealing with a driver initiated hard reset. This is + * necessary to prevent hangs due to a spurious interrupt while + * the reset is in progress. + * o Clear the 802.11 header when transmitting, even though we + * don't use it. This fixes a long standing bug on some + * firmwares, which seem to get confused if that isn't done. + * o Be less eager to de-encapsulate SNAP frames, only do so if + * the OUI is 00:00:00 or 00:00:f8, leave others alone. The old + * behaviour broke CDP (Cisco Discovery Protocol). + * o Use dev instead of priv for free_irq() as well as + * request_irq() (oops). + * o Attempt to reset rather than giving up if we get too many + * IRQs. + * o Changed semantics of __orinoco_down() so it can be called + * safely with hw_unavailable set. It also now clears the + * linkstatus (since we're going to have to reassociate). + * + * v0.13d -> v0.13e - 12 May 2003 - David Gibson + * o Support for post-2.5.68 return values from irq handler. + * o Fixed bug where underlength packets would be double counted + * in the rx_dropped statistics. + * o Provided a module parameter to suppress linkstatus messages. + * + * v0.13e -> v0.14alpha1 - 30 Sep 2003 - David Gibson + * o Replaced priv->connected logic with netif_carrier_on/off() + * calls. + * o Remove has_ibss_any and never set the CREATEIBSS RID when + * the ESSID is empty. Too many firmwares break if we do. + * o 2.6 merges: Replace pdev->slot_name with pci_name(), remove + * __devinitdata from PCI ID tables, use free_netdev(). + * o Enabled shared-key authentication for Agere firmware (from + * Robert J. Moore <Robert.J.Moore AT allanbank.com> + * o Move netif_wake_queue() (back) to the Tx completion from the + * ALLOC event. This seems to prevent/mitigate the rolling + * error -110 problems at least on some Intersil firmwares. + * Theoretically reduces performance, but I can't measure it. + * Patch from Andrew Tridgell <tridge AT samba.org> + * + * v0.14alpha1 -> v0.14alpha2 - 20 Oct 2003 - David Gibson + * o Correctly turn off shared-key authentication when requested + * (bugfix from Robert J. Moore). + * o Correct airport sleep interfaces for current 2.6 kernels. + * o Add code for key change without disabling/enabling the MAC + * port. This is supposed to allow 802.1x to work sanely, but + * doesn't seem to yet. + * + * TODO + * o New wireless extensions API (patch from Moustafa + * Youssef, updated by Jim Carter and Pavel Roskin). + * o Handle de-encapsulation within network layer, provide 802.11 + * headers (patch from Thomas 'Dent' Mirlacher) + * o RF monitor mode support + * o Fix possible races in SPY handling. + * o Disconnect wireless extensions from fundamental configuration. + * o (maybe) Software WEP support (patch from Stano Meduna). + * o (maybe) Use multiple Tx buffers - driver handling queue + * rather than firmware. + */ + +/* Locking and synchronization: + * + * The basic principle is that everything is serialized through a + * single spinlock, priv->lock. The lock is used in user, bh and irq + * context, so when taken outside hardirq context it should always be + * taken with interrupts disabled. The lock protects both the + * hardware and the struct orinoco_private. + * + * Another flag, priv->hw_unavailable indicates that the hardware is + * unavailable for an extended period of time (e.g. suspended, or in + * the middle of a hard reset). This flag is protected by the + * spinlock. All code which touches the hardware should check the + * flag after taking the lock, and if it is set, give up on whatever + * they are doing and drop the lock again. The orinoco_lock() + * function handles this (it unlocks and returns -EBUSY if + * hw_unavailable is non-zero). + */ + +#define DRIVER_NAME "orinoco" + +#include <linux/config.h> + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/ioport.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/etherdevice.h> +#include <linux/wireless.h> + +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/system.h> + +#include "hermes.h" +#include "hermes_rid.h" +#include "orinoco.h" +#include "ieee802_11.h" + +/********************************************************************/ +/* Module information */ +/********************************************************************/ + +MODULE_AUTHOR("Pavel Roskin <proski@gnu.org> & David Gibson <hermes@gibson.dropbear.id.au>"); +MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based and similar wireless cards"); +MODULE_LICENSE("Dual MPL/GPL"); + +/* Level of debugging. Used in the macros in orinoco.h */ +#ifdef ORINOCO_DEBUG +int orinoco_debug = ORINOCO_DEBUG; +module_param(orinoco_debug, int, 0644); +MODULE_PARM_DESC(orinoco_debug, "Debug level"); +EXPORT_SYMBOL(orinoco_debug); +#endif + +static int suppress_linkstatus; /* = 0 */ +module_param(suppress_linkstatus, bool, 0644); +MODULE_PARM_DESC(suppress_linkstatus, "Don't log link status changes"); + +/********************************************************************/ +/* Compile time configuration and compatibility stuff */ +/********************************************************************/ + +/* We do this this way to avoid ifdefs in the actual code */ +#ifdef WIRELESS_SPY +#define SPY_NUMBER(priv) (priv->spy_number) +#else +#define SPY_NUMBER(priv) 0 +#endif /* WIRELESS_SPY */ + +/********************************************************************/ +/* Internal constants */ +/********************************************************************/ + +#define ORINOCO_MIN_MTU 256 +#define ORINOCO_MAX_MTU (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD) + +#define SYMBOL_MAX_VER_LEN (14) +#define USER_BAP 0 +#define IRQ_BAP 1 +#define MAX_IRQLOOPS_PER_IRQ 10 +#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of + * how many events the + * device could + * legitimately generate */ +#define SMALL_KEY_SIZE 5 +#define LARGE_KEY_SIZE 13 +#define TX_NICBUF_SIZE_BUG 1585 /* Bug in Symbol firmware */ + +#define DUMMY_FID 0xFFFF + +/*#define MAX_MULTICAST(priv) (priv->firmware_type == FIRMWARE_TYPE_AGERE ? \ + HERMES_MAX_MULTICAST : 0)*/ +#define MAX_MULTICAST(priv) (HERMES_MAX_MULTICAST) + +#define ORINOCO_INTEN (HERMES_EV_RX | HERMES_EV_ALLOC \ + | HERMES_EV_TX | HERMES_EV_TXEXC \ + | HERMES_EV_WTERR | HERMES_EV_INFO \ + | HERMES_EV_INFDROP ) + +/********************************************************************/ +/* Data tables */ +/********************************************************************/ + +/* The frequency of each channel in MHz */ +static const long channel_frequency[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 +}; +#define NUM_CHANNELS ARRAY_SIZE(channel_frequency) + +/* This tables gives the actual meanings of the bitrate IDs returned + * by the firmware. */ +static struct { + int bitrate; /* in 100s of kilobits */ + int automatic; + u16 agere_txratectrl; + u16 intersil_txratectrl; +} bitrate_table[] = { + {110, 1, 3, 15}, /* Entry 0 is the default */ + {10, 0, 1, 1}, + {10, 1, 1, 1}, + {20, 0, 2, 2}, + {20, 1, 6, 3}, + {55, 0, 4, 4}, + {55, 1, 7, 7}, + {110, 0, 5, 8}, +}; +#define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table) + +/********************************************************************/ +/* Data types */ +/********************************************************************/ + +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)); + +/* 802.2 LLC/SNAP header used for Ethernet encapsulation over 802.11 */ +u8 encaps_hdr[] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; + +#define ENCAPS_OVERHEAD (sizeof(encaps_hdr) + 2) + +struct hermes_rx_descriptor { + u16 status; + u32 time; + u8 silence; + u8 signal; + u8 rate; + u8 rxflow; + u32 reserved; +} __attribute__ ((packed)); + +/********************************************************************/ +/* 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 void __orinoco_set_multicast_list(struct net_device *dev); +static int orinoco_debug_dump_recs(struct net_device *dev); + +/********************************************************************/ +/* Internal helper functions */ +/********************************************************************/ + +static inline void set_port_type(struct orinoco_private *priv) +{ + switch (priv->iw_mode) { + case IW_MODE_INFRA: + priv->port_type = 1; + priv->createibss = 0; + break; + case IW_MODE_ADHOC: + if (priv->prefer_port3) { + priv->port_type = 3; + priv->createibss = 0; + } else { + priv->port_type = priv->ibss_port; + priv->createibss = 1; + } + break; + default: + printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n", + priv->ndev->name); + } +} + +/********************************************************************/ +/* Device methods */ +/********************************************************************/ + +static int orinoco_open(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + unsigned long flags; + int err; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + err = __orinoco_up(dev); + + if (! err) + priv->open = 1; + + orinoco_unlock(priv, &flags); + + return err; +} + +int orinoco_stop(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + int err = 0; + + /* We mustn't use orinoco_lock() here, because we need to be + able to close the interface even if hw_unavailable is set + (e.g. as we're released after a PC Card removal) */ + spin_lock_irq(&priv->lock); + + priv->open = 0; + + err = __orinoco_down(dev); + + spin_unlock_irq(&priv->lock); + + return err; +} + +static struct net_device_stats *orinoco_get_stats(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + + return &priv->stats; +} + +static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + hermes_t *hw = &priv->hw; + struct iw_statistics *wstats = &priv->wstats; + int err = 0; + unsigned long flags; + + if (! netif_device_present(dev)) { + printk(KERN_WARNING "%s: get_wireless_stats() called while device not present\n", + dev->name); + return NULL; /* FIXME: Can we do better than this? */ + } + + if (orinoco_lock(priv, &flags) != 0) + return NULL; /* FIXME: Erg, we've been signalled, how + * do we propagate this back up? */ + + if (priv->iw_mode == IW_MODE_ADHOC) { + memset(&wstats->qual, 0, sizeof(wstats->qual)); + /* If a spy address is defined, we report stats of the + * first spy address - Jean II */ + if (SPY_NUMBER(priv)) { + wstats->qual.qual = priv->spy_stat[0].qual; + wstats->qual.level = priv->spy_stat[0].level; + wstats->qual.noise = priv->spy_stat[0].noise; + wstats->qual.updated = priv->spy_stat[0].updated; + } + } else { + struct { + u16 qual, signal, noise; + } __attribute__ ((packed)) cq; + + err = HERMES_READ_RECORD(hw, USER_BAP, + HERMES_RID_COMMSQUALITY, &cq); + + wstats->qual.qual = (int)le16_to_cpu(cq.qual); + wstats->qual.level = (int)le16_to_cpu(cq.signal) - 0x95; + wstats->qual.noise = (int)le16_to_cpu(cq.noise) - 0x95; + wstats->qual.updated = 7; + } + + /* We can't really wait for the tallies inquiry command to + * complete, so we just use the previous results and trigger + * a new tallies inquiry command for next time - Jean II */ + /* FIXME: We're in user context (I think?), so we should just + wait for the tallies to come through */ + err = hermes_inquire(hw, HERMES_INQ_TALLIES); + + orinoco_unlock(priv, &flags); + + if (err) + return NULL; + + return wstats; +} + +static void orinoco_set_multicast_list(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) { + printk(KERN_DEBUG "%s: orinoco_set_multicast_list() " + "called when hw_unavailable\n", dev->name); + return; + } + + __orinoco_set_multicast_list(dev); + orinoco_unlock(priv, &flags); +} + +static int orinoco_change_mtu(struct net_device *dev, int new_mtu) +{ + struct orinoco_private *priv = netdev_priv(dev); + + if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) ) + return -EINVAL; + + if ( (new_mtu + ENCAPS_OVERHEAD + IEEE802_11_HLEN) > + (priv->nicbuf_size - ETH_HLEN) ) + return -EINVAL; + + dev->mtu = new_mtu; + + return 0; +} + +/********************************************************************/ +/* Tx path */ +/********************************************************************/ + +static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct net_device_stats *stats = &priv->stats; + hermes_t *hw = &priv->hw; + int err = 0; + u16 txfid = priv->txfid; + char *p; + struct ethhdr *eh; + int len, data_len, data_off; + struct hermes_tx_descriptor desc; + unsigned long flags; + + TRACE_ENTER(dev->name); + + if (! netif_running(dev)) { + printk(KERN_ERR "%s: Tx on stopped device!\n", + dev->name); + TRACE_EXIT(dev->name); + return 1; + } + + if (netif_queue_stopped(dev)) { + printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", + dev->name); + TRACE_EXIT(dev->name); + return 1; + } + + if (orinoco_lock(priv, &flags) != 0) { + printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n", + dev->name); + TRACE_EXIT(dev->name); + return 1; + } + + if (! netif_carrier_ok(dev)) { + /* Oops, the firmware hasn't established a connection, + silently drop the packet (this seems to be the + safest approach). */ + stats->tx_errors++; + orinoco_unlock(priv, &flags); + dev_kfree_skb(skb); + TRACE_EXIT(dev->name); + return 0; + } + + /* Length of the packet body */ + /* FIXME: what if the skb is smaller than this? */ + len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN); + + eh = (struct ethhdr *)skb->data; + + memset(&desc, 0, sizeof(desc)); + desc.tx_control = cpu_to_le16(HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX); + err = hermes_bap_pwrite(hw, USER_BAP, &desc, sizeof(desc), txfid, 0); + if (err) { + if (net_ratelimit()) + printk(KERN_ERR "%s: Error %d writing Tx descriptor " + "to BAP\n", dev->name, err); + stats->tx_errors++; + goto fail; + } + + /* Clear the 802.11 header and data length fields - some + * firmwares (e.g. Lucent/Agere 8.xx) appear to get confused + * if this isn't done. */ + hermes_clear_words(hw, HERMES_DATA0, + HERMES_802_3_OFFSET - HERMES_802_11_OFFSET); + + /* Encapsulate Ethernet-II frames */ + if (ntohs(eh->h_proto) > ETH_DATA_LEN) { /* Ethernet-II frame */ + struct header_struct hdr; + data_len = len; + data_off = HERMES_802_3_OFFSET + sizeof(hdr); + p = skb->data + ETH_HLEN; + + /* 802.3 header */ + memcpy(hdr.dest, eh->h_dest, ETH_ALEN); + memcpy(hdr.src, eh->h_source, ETH_ALEN); + hdr.len = htons(data_len + ENCAPS_OVERHEAD); + + /* 802.2 header */ + memcpy(&hdr.dsap, &encaps_hdr, sizeof(encaps_hdr)); + + hdr.ethertype = eh->h_proto; + err = hermes_bap_pwrite(hw, USER_BAP, &hdr, sizeof(hdr), + txfid, HERMES_802_3_OFFSET); + if (err) { + if (net_ratelimit()) + printk(KERN_ERR "%s: Error %d writing packet " + "header to BAP\n", dev->name, err); + stats->tx_errors++; + goto fail; + } + } else { /* IEEE 802.3 frame */ + data_len = len + ETH_HLEN; + data_off = HERMES_802_3_OFFSET; + p = skb->data; + } + + /* Round up for odd length packets */ + err = hermes_bap_pwrite(hw, USER_BAP, p, ALIGN(data_len, 2), + txfid, data_off); + if (err) { + printk(KERN_ERR "%s: Error %d writing packet to BAP\n", + dev->name, err); + stats->tx_errors++; + goto fail; + } + + /* Finally, we actually initiate the send */ + netif_stop_queue(dev); + + err = hermes_docmd_wait(hw, HERMES_CMD_TX | HERMES_CMD_RECL, + txfid, NULL); + if (err) { + netif_start_queue(dev); + printk(KERN_ERR "%s: Error %d transmitting packet\n", + dev->name, err); + stats->tx_errors++; + goto fail; + } + + dev->trans_start = jiffies; + stats->tx_bytes += data_off + data_len; + + orinoco_unlock(priv, &flags); + + dev_kfree_skb(skb); + + TRACE_EXIT(dev->name); + + return 0; + fail: + TRACE_EXIT(dev->name); + + orinoco_unlock(priv, &flags); + return err; +} + +static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw) +{ + struct orinoco_private *priv = netdev_priv(dev); + u16 fid = hermes_read_regn(hw, ALLOCFID); + + if (fid != priv->txfid) { + if (fid != DUMMY_FID) + printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n", + dev->name, fid); + return; + } + + hermes_write_regn(hw, ALLOCFID, DUMMY_FID); +} + +static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct net_device_stats *stats = &priv->stats; + + stats->tx_packets++; + + netif_wake_queue(dev); + + hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); +} + +static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct net_device_stats *stats = &priv->stats; + u16 fid = hermes_read_regn(hw, TXCOMPLFID); + struct hermes_tx_descriptor desc; + int err = 0; + + if (fid == DUMMY_FID) + return; /* Nothing's really happened */ + + err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), fid, 0); + if (err) { + printk(KERN_WARNING "%s: Unable to read descriptor on Tx error " + "(FID=%04X error %d)\n", + dev->name, fid, err); + } else { + DEBUG(1, "%s: Tx error, status %d\n", + dev->name, le16_to_cpu(desc.status)); + } + + stats->tx_errors++; + |