X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fnet%2Fwireless%2Forinoco.c;h=8f746051af82dad9b97372f1203ce1ca29edb619;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=8161a1c0094933d5c21abdf1096995a0264ab58f;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 8161a1c00..8f746051a 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -1,17 +1,23 @@ -/* orinoco.c 0.13e - (formerly known as dldwd_cs.c and orinoco_cs.c) +/* 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. * - * Copyright (C) 2000 David Gibson, Linuxcare Australia + * Current maintainers (as of 29 September 2003) are: + * Pavel Roskin + * and David Gibson + * + * (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 + * 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 - * http://www.fasta.fh-dortmund.de/users/andy/wvlan/ + * Portions based on wvlan_cs.c 1.0.6, Copyright Andreas Neuhaus + * 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 @@ -24,7 +30,7 @@ * limitations under the License. * * The initial developer of the original code is David A. Hinds - * . Portions created by David + * . Portions created by David * A. Hinds are Copyright (C) 1999 David A. Hinds. All Rights * Reserved. * @@ -58,7 +64,7 @@ * 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 + * 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... @@ -68,8 +74,8 @@ * 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 Prims2 WEP bugs that I introduced in v0.03 :-( - * It work on D-Link *only* after a tcpdump. Weird... + * 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) @@ -82,7 +88,7 @@ * o Clean up RID definitions in hermes.h, other cleanups * * v0.04b -> v0.04c - 24/4/2001 - Jean II - * o Tim Hurley reported a D-Link card + * o Tim Hurley reported a D-Link card * with vendor 02 and firmware 0.08. Added in the capabilities... * o Tested Lucent firmware 7.28, everything works... * @@ -105,15 +111,15 @@ * 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 + * o Workaround to SNAP decapsulate frame from Linksys AP + * original patch from : Dong Liu * (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" , - * "Ian Kinner" - * and "David Smith" + * Original patch from "Hong Lin" , + * "Ian Kinner" + * and "David Smith" * 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) @@ -134,15 +140,15 @@ * * 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 unecessary messages. + * wishes to reduce the number of unnecessary messages. * o Removed bogus message on CRC error. - * o Merged fixeds for v0.08 Prism 2 firmware from William Waghorn - * + * o Merged fixes for v0.08 Prism 2 firmware from William Waghorn + * * 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 metadataa + * o Added some module metadata * * v0.06e -> v0.06f - 14/8/2001 - David Gibson * o Wording fix to license @@ -159,7 +165,7 @@ * 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 for "providing" me + * o Thanks to Jared Valentine 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 @@ -172,7 +178,7 @@ * 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 - * realised that my assumption that the driver's serialization + * 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. @@ -181,12 +187,12 @@ * 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 brdge) cards. - * o Changed to using the new type-facist min/max. + * 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 normalisation of signel level reporting. + * 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. * @@ -342,7 +348,7 @@ * 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 cofusing the firmware), and only + * 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 @@ -397,7 +403,8 @@ * 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. */ + * rather than firmware. + */ /* Locking and synchronization: * @@ -414,7 +421,10 @@ * 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). */ + * hw_unavailable is non-zero). + */ + +#define DRIVER_NAME "orinoco" #include @@ -444,31 +454,24 @@ /* Module information */ /********************************************************************/ -MODULE_AUTHOR("David Gibson "); +MODULE_AUTHOR("Pavel Roskin & David Gibson "); MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based and similar wireless cards"); -#ifdef MODULE_LICENSE MODULE_LICENSE("Dual MPL/GPL"); -#endif /* Level of debugging. Used in the macros in orinoco.h */ #ifdef ORINOCO_DEBUG int orinoco_debug = ORINOCO_DEBUG; -MODULE_PARM(orinoco_debug, "i"); +module_param(orinoco_debug, int, 0); EXPORT_SYMBOL(orinoco_debug); #endif static int suppress_linkstatus; /* = 0 */ -MODULE_PARM(suppress_linkstatus, "i"); +module_param(suppress_linkstatus, bool, 0); /********************************************************************/ /* Compile time configuration and compatibility stuff */ /********************************************************************/ -/* Wireless extensions backwards compatibility */ -#ifndef SIOCIWFIRSTPRIV -#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE -#endif /* SIOCIWFIRSTPRIV */ - /* We do this this way to avoid ifdefs in the actual code */ #ifdef WIRELESS_SPY #define SPY_NUMBER(priv) (priv->spy_number) @@ -497,25 +500,29 @@ MODULE_PARM(suppress_linkstatus, "i"); #define DUMMY_FID 0xFFFF -#define RUP_EVEN(a) (((a) + 1) & (~1)) - /*#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 */ -const long channel_frequency[] = { +static const long channel_frequency[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; -#define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) ) +#define NUM_CHANNELS ARRAY_SIZE(channel_frequency) -/* This tables gives the actual meanings of the bitrate IDs returned by the firmware. */ -struct { +/* 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; @@ -530,7 +537,7 @@ struct { {55, 1, 7, 7}, {110, 0, 5, 8}, }; -#define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0])) +#define BITRATE_TABLE_SIZE ARRAY_SIZE(bitrate_table) /********************************************************************/ /* Data types */ @@ -555,133 +562,54 @@ 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 void orinoco_stat_gather(struct net_device *dev, - struct sk_buff *skb, - struct hermes_rx_descriptor *desc); - -static struct net_device_stats *orinoco_get_stats(struct net_device *dev); -static struct iw_statistics *orinoco_get_wireless_stats(struct net_device *dev); - -/* Hardware control routines */ - +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_hw_set_bitrate(struct orinoco_private *priv); -static int __orinoco_hw_setup_wep(struct orinoco_private *priv); -static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN]); -static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, - char buf[IW_ESSID_MAX_SIZE+1]); -static long orinoco_hw_get_freq(struct orinoco_private *priv); -static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates, - s32 *rates, int max); static void __orinoco_set_multicast_list(struct net_device *dev); - -/* Interrupt handling routines */ -static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw); -static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw); -static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw); -static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw); -static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw); -static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw); -static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw); -static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw); - -/* ioctl() routines */ static int orinoco_debug_dump_recs(struct net_device *dev); /********************************************************************/ -/* Function prototypes */ +/* Internal helper functions */ /********************************************************************/ -int __orinoco_up(struct net_device *dev) -{ - struct orinoco_private *priv = netdev_priv(dev); - struct hermes *hw = &priv->hw; - int err; - - err = __orinoco_program_rids(dev); - if (err) { - printk(KERN_ERR "%s: Error %d configuring card\n", - dev->name, err); - return err; - } - - /* Fire things up again */ - hermes_set_irqmask(hw, ORINOCO_INTEN); - err = hermes_enable_port(hw, 0); - if (err) { - printk(KERN_ERR "%s: Error %d enabling MAC port\n", - dev->name, err); - return err; - } - - netif_start_queue(dev); - - return 0; -} - -int __orinoco_down(struct net_device *dev) +static inline void set_port_type(struct orinoco_private *priv) { - struct orinoco_private *priv = netdev_priv(dev); - struct hermes *hw = &priv->hw; - int err; - - netif_stop_queue(dev); - - if (! priv->hw_unavailable) { - if (! priv->broken_disableport) { - err = hermes_disable_port(hw, 0); - if (err) { - /* Some firmwares (e.g. Intersil 1.3.x) seem - * to have problems disabling the port, oh - * well, too bad. */ - printk(KERN_WARNING "%s: Error %d disabling MAC port\n", - dev->name, err); - priv->broken_disableport = 1; - } + 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; } - hermes_set_irqmask(hw, 0); - hermes_write_regn(hw, EVACK, 0xffff); + break; + default: + printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n", + priv->ndev->name); } - - /* firmware will have to reassociate */ - priv->last_linkstatus = 0xffff; - priv->connected = 0; - - return 0; } -int orinoco_reinit_firmware(struct net_device *dev) -{ - struct orinoco_private *priv = netdev_priv(dev); - struct hermes *hw = &priv->hw; - int err; - - err = hermes_init(hw); - if (err) - return err; - - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); - if (err == -EIO) { - /* Try workaround for old Symbol firmware bug */ - printk(KERN_WARNING "%s: firmware ALLOC bug detected " - "(old Symbol firmware?). Trying to work around... ", - dev->name); - - priv->nicbuf_size = TX_NICBUF_SIZE_BUG; - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); - if (err) - printk("failed!\n"); - else - printk("ok.\n"); - } - - return err; -} +/********************************************************************/ +/* Device methods */ +/********************************************************************/ static int orinoco_open(struct net_device *dev) { @@ -689,9 +617,8 @@ static int orinoco_open(struct net_device *dev) unsigned long flags; int err; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; err = __orinoco_up(dev); @@ -722,845 +649,563 @@ int orinoco_stop(struct net_device *dev) return err; } -static int __orinoco_program_rids(struct net_device *dev) +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; - int err; - struct hermes_idstring idbuf; + struct iw_statistics *wstats = &priv->wstats; + int err = 0; + unsigned long flags; - /* Set the MAC address */ - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, - HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr); - if (err) { - printk(KERN_ERR "%s: Error %d setting MAC address\n", dev->name, err); - return err; + 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? */ } - /* Set up the link mode */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, priv->port_type); - if (err) { - printk(KERN_ERR "%s: Error %d setting port type\n", dev->name, err); - return err; - } - /* Set the channel/frequency */ - if (priv->channel == 0) { - printk(KERN_DEBUG "%s: Channel is 0 in __orinoco_program_rids()\n", dev->name); - if (priv->createibss) - priv->channel = 10; - } - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel); - if (err) { - printk(KERN_ERR "%s: Error %d setting channel\n", dev->name, err); - return err; - } + if (orinoco_lock(priv, &flags) != 0) + return NULL; /* FIXME: Erg, we've been signalled, how + * do we propagate this back up? */ - if (priv->has_ibss) { - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFCREATEIBSS, - priv->createibss); - if (err) { - printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", dev->name, err); - return err; + 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; - if ((strlen(priv->desired_essid) == 0) && (priv->createibss) - && (!priv->has_ibss_any)) { - printk(KERN_WARNING "%s: This firmware requires an \ -ESSID in IBSS-Ad-Hoc mode.\n", dev->name); - /* With wvlan_cs, in this case, we would crash. - * hopefully, this driver will behave better... - * Jean II */ - } + 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; } - /* Set the desired ESSID */ - idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); - memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); - /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */ - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, - HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), - &idbuf); - if (err) { - printk(KERN_ERR "%s: Error %d setting OWNSSID\n", dev->name, err); - return err; - } - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, - HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), - &idbuf); - if (err) { - printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", dev->name, err); - return err; - } + /* 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); - /* Set the station name */ - idbuf.len = cpu_to_le16(strlen(priv->nick)); - memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val)); - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, - HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2), - &idbuf); - if (err) { - printk(KERN_ERR "%s: Error %d setting nickname\n", dev->name, err); - return err; - } + if (err) + return NULL; + + return wstats; +} - /* Set AP density */ - if (priv->has_sensitivity) { - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, - priv->ap_density); - if (err) { - printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. " - "Disabling sensitivity control\n", dev->name, err); +static void orinoco_set_multicast_list(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + unsigned long flags; - priv->has_sensitivity = 0; - } + if (orinoco_lock(priv, &flags) != 0) { + printk(KERN_DEBUG "%s: orinoco_set_multicast_list() " + "called when hw_unavailable\n", dev->name); + return; } - /* Set RTS threshold */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, priv->rts_thresh); - if (err) { - printk(KERN_ERR "%s: Error %d setting RTS threshold\n", dev->name, err); - return err; - } + __orinoco_set_multicast_list(dev); + orinoco_unlock(priv, &flags); +} - /* Set fragmentation threshold or MWO robustness */ - if (priv->has_mwo) - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFMWOROBUST_AGERE, - priv->mwo_robust); - else - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, - priv->frag_thresh); - if (err) { - printk(KERN_ERR "%s: Error %d setting framentation\n", dev->name, err); - return err; - } - - /* Set bitrate */ - err = __orinoco_hw_set_bitrate(priv); - if (err) { - printk(KERN_ERR "%s: Error %d setting bitrate\n", dev->name, err); - return err; - } - - /* Set power management */ - if (priv->has_pm) { - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, - priv->pm_on); - if (err) { - printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); - return err; - } - - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFMULTICASTRECEIVE, - priv->pm_mcast); - if (err) { - printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); - return err; - } - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFMAXSLEEPDURATION, - priv->pm_period); - if (err) { - printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); - return err; - } - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFPMHOLDOVERDURATION, - priv->pm_timeout); - if (err) { - printk(KERN_ERR "%s: Error %d setting up PM\n", - dev->name, err); - return err; - } - } +static int orinoco_change_mtu(struct net_device *dev, int new_mtu) +{ + struct orinoco_private *priv = netdev_priv(dev); - /* Set preamble - only for Symbol so far... */ - if (priv->has_preamble) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFPREAMBLE_SYMBOL, - priv->preamble); - if (err) { - printk(KERN_ERR "%s: Error %d setting preamble\n", - dev->name, err); - return err; - } - } + if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) ) + return -EINVAL; - /* Set up encryption */ - if (priv->has_wep) { - err = __orinoco_hw_setup_wep(priv); - if (err) { - printk(KERN_ERR "%s: Error %d activating WEP\n", - dev->name, err); - return err; - } - } + if ( (new_mtu + ENCAPS_OVERHEAD + IEEE802_11_HLEN) > + (priv->nicbuf_size - ETH_HLEN) ) + return -EINVAL; - /* Set promiscuity / multicast*/ - priv->promiscuous = 0; - priv->mc_count = 0; - __orinoco_set_multicast_list(dev); /* FIXME: what about the xmit_lock */ + dev->mtu = new_mtu; return 0; } -/* xyzzy */ -static int orinoco_reconfigure(struct net_device *dev) +/********************************************************************/ +/* Tx path */ +/********************************************************************/ + +static int orinoco_xmit(struct sk_buff *skb, struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); - struct hermes *hw = &priv->hw; - unsigned long flags; + 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; - if (priv->broken_disableport) { - schedule_work(&priv->reset_work); - return 0; - } - - err = orinoco_lock(priv, &flags); - if (err) - return err; + TRACE_ENTER(dev->name); - - err = hermes_disable_port(hw, 0); - if (err) { - printk(KERN_WARNING "%s: Unable to disable port while reconfiguring card\n", + if (! netif_running(dev)) { + printk(KERN_ERR "%s: Tx on stopped device!\n", dev->name); - priv->broken_disableport = 1; - goto out; + TRACE_EXIT(dev->name); + return 1; } - - err = __orinoco_program_rids(dev); - if (err) { - printk(KERN_WARNING "%s: Unable to reconfigure card\n", + + if (netif_queue_stopped(dev)) { + printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", dev->name); - goto out; + TRACE_EXIT(dev->name); + return 1; } - - err = hermes_enable_port(hw, 0); - if (err) { - printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", + + if (orinoco_lock(priv, &flags) != 0) { + printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n", dev->name); - goto out; + TRACE_EXIT(dev->name); + return 1; } - out: - if (err) { - printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); - schedule_work(&priv->reset_work); - err = 0; + if (! priv->connected) { + /* 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; } - orinoco_unlock(priv, &flags); - return err; - -} - -/* This must be called from user context, without locks held - use - * schedule_work() */ -static void orinoco_reset(struct net_device *dev) -{ - struct orinoco_private *priv = netdev_priv(dev); - struct hermes *hw = &priv->hw; - int err; - unsigned long flags; + /* 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); - err = orinoco_lock(priv, &flags); - if (err) - /* When the hardware becomes available again, whatever - * detects that is responsible for re-initializing - * it. So no need for anything further*/ - return; + eh = (struct ethhdr *)skb->data; - netif_stop_queue(dev); + 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) { + printk(KERN_ERR "%s: Error %d writing Tx descriptor to BAP\n", + dev->name, err); + stats->tx_errors++; + goto fail; + } - /* Shut off interrupts. Depending on what state the hardware - * is in, this might not work, but we'll try anyway */ - hermes_set_irqmask(hw, 0); - hermes_write_regn(hw, EVACK, 0xffff); + /* 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); - priv->hw_unavailable++; - priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */ - priv->connected = 0; + /* 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; - orinoco_unlock(priv, &flags); + /* 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) { + 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; + } - if (priv->hard_reset) - err = (*priv->hard_reset)(priv); + /* 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: orinoco_reset: Error %d performing hard reset\n", + printk(KERN_ERR "%s: Error %d writing packet to BAP\n", dev->name, err); - /* FIXME: shutdown of some sort */ - return; + stats->tx_errors++; + goto fail; } - err = orinoco_reinit_firmware(dev); + /* 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) { - printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", + netif_start_queue(dev); + printk(KERN_ERR "%s: Error %d transmitting packet\n", dev->name, err); - return; + stats->tx_errors++; + goto fail; } - spin_lock_irq(&priv->lock); /* This has to be called from user context */ + dev->trans_start = jiffies; + stats->tx_bytes += data_off + data_len; - priv->hw_unavailable--; + orinoco_unlock(priv, &flags); - /* priv->open or priv->hw_unavailable might have changed while - * we dropped the lock */ - if (priv->open && (! priv->hw_unavailable)) { - err = __orinoco_up(dev); - if (err) { - printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", - dev->name, err); - } else - dev->trans_start = jiffies; - } + dev_kfree_skb(skb); - spin_unlock_irq(&priv->lock); + TRACE_EXIT(dev->name); - return; + return 0; + fail: + TRACE_EXIT(dev->name); + + orinoco_unlock(priv, &flags); + return err; } -/********************************************************************/ -/* Internal helper functions */ -/********************************************************************/ - -static inline void -set_port_type(struct orinoco_private *priv) +static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw) { - 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); + 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; + } else { + netif_wake_queue(dev); } -} -/* Does the frame have a SNAP header indicating it should be - * de-encapsulated to Ethernet-II? */ -static inline int -is_ethersnap(struct header_struct *hdr) -{ - /* We de-encapsulate all packets which, a) have SNAP headers - * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header - * and where b) the OUI of the SNAP header is 00:00:00 or - * 00:00:f8 - we need both because different APs appear to use - * different OUIs for some reason */ - return (memcmp(&hdr->dsap, &encaps_hdr, 5) == 0) - && ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) ); + hermes_write_regn(hw, ALLOCFID, DUMMY_FID); } -static void -orinoco_set_multicast_list(struct net_device *dev) +static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw) { struct orinoco_private *priv = netdev_priv(dev); - unsigned long flags; + struct net_device_stats *stats = &priv->stats; - if (orinoco_lock(priv, &flags) != 0) { - printk(KERN_DEBUG "%s: orinoco_set_multicast_list() " - "called when hw_unavailable\n", dev->name); - return; - } + stats->tx_packets++; - __orinoco_set_multicast_list(dev); - orinoco_unlock(priv, &flags); + hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); } -/********************************************************************/ -/* Hardware control functions */ -/********************************************************************/ - - -static int __orinoco_hw_set_bitrate(struct orinoco_private *priv) +static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw) { - hermes_t *hw = &priv->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 (priv->bitratemode >= BITRATE_TABLE_SIZE) { - printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", - priv->ndev->name, priv->bitratemode); - return -EINVAL; - } + if (fid == DUMMY_FID) + return; /* Nothing's really happened */ - switch (priv->firmware_type) { - case FIRMWARE_TYPE_AGERE: - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFTXRATECONTROL, - bitrate_table[priv->bitratemode].agere_txratectrl); - break; - case FIRMWARE_TYPE_INTERSIL: - case FIRMWARE_TYPE_SYMBOL: - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFTXRATECONTROL, - bitrate_table[priv->bitratemode].intersil_txratectrl); - break; - default: - BUG(); + 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++; - return err; + hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); } - -static int __orinoco_hw_setup_wep(struct orinoco_private *priv) +static void orinoco_tx_timeout(struct net_device *dev) { - hermes_t *hw = &priv->hw; - int err = 0; - int master_wep_flag; - int auth_flag; + struct orinoco_private *priv = netdev_priv(dev); + struct net_device_stats *stats = &priv->stats; + struct hermes *hw = &priv->hw; - switch (priv->firmware_type) { - case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ - if (priv->wep_on) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFTXKEY_AGERE, - priv->tx_key); - if (err) - return err; - - err = HERMES_WRITE_RECORD(hw, USER_BAP, - HERMES_RID_CNFWEPKEYS_AGERE, - &priv->keys); - if (err) - return err; - } - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFWEPENABLED_AGERE, - priv->wep_on); - if (err) - return err; - break; + printk(KERN_WARNING "%s: Tx timeout! " + "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n", + dev->name, hermes_read_regn(hw, ALLOCFID), + hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT)); - case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */ - case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */ - master_wep_flag = 0; /* Off */ - if (priv->wep_on) { - int keylen; - int i; + stats->tx_errors++; - /* Fudge around firmware weirdness */ - keylen = le16_to_cpu(priv->keys[priv->tx_key].len); - - /* Write all 4 keys */ - for(i = 0; i < ORINOCO_MAX_KEYS; i++) { -/* int keylen = le16_to_cpu(priv->keys[i].len); */ - - if (keylen > LARGE_KEY_SIZE) { - printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", - priv->ndev->name, i, keylen); - return -E2BIG; - } + schedule_work(&priv->reset_work); +} - err = hermes_write_ltv(hw, USER_BAP, - HERMES_RID_CNFDEFAULTKEY0 + i, - HERMES_BYTES_TO_RECLEN(keylen), - priv->keys[i].data); - if (err) - return err; - } +/********************************************************************/ +/* Rx path (data frames) */ +/********************************************************************/ - /* Write the index of the key used in transmission */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFWEPDEFAULTKEYID, - priv->tx_key); - if (err) - return err; - - if (priv->wep_restrict) { - auth_flag = 2; - master_wep_flag = 3; - } else { - /* Authentication is where Intersil and Symbol - * firmware differ... */ - auth_flag = 1; - if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL) - master_wep_flag = 3; /* Symbol */ - else - master_wep_flag = 1; /* Intersil */ - } +/* Does the frame have a SNAP header indicating it should be + * de-encapsulated to Ethernet-II? */ +static inline int is_ethersnap(struct header_struct *hdr) +{ + /* We de-encapsulate all packets which, a) have SNAP headers + * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header + * and where b) the OUI of the SNAP header is 00:00:00 or + * 00:00:f8 - we need both because different APs appear to use + * different OUIs for some reason */ + return (memcmp(&hdr->dsap, &encaps_hdr, 5) == 0) + && ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) ); +} +static inline void orinoco_spy_gather(struct net_device *dev, u_char *mac, + int level, int noise) +{ + struct orinoco_private *priv = netdev_priv(dev); + int i; - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFAUTHENTICATION, auth_flag); - if (err) - return err; + /* Gather wireless spy statistics: for each packet, compare the + * source address with out list, and if match, get the stats... */ + for (i = 0; i < priv->spy_number; i++) + if (!memcmp(mac, priv->spy_address[i], ETH_ALEN)) { + priv->spy_stat[i].level = level - 0x95; + priv->spy_stat[i].noise = noise - 0x95; + priv->spy_stat[i].qual = (level > noise) ? (level - noise) : 0; + priv->spy_stat[i].updated = 7; } - - /* Master WEP setting : on/off */ - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFWEPFLAGS_INTERSIL, - master_wep_flag); - if (err) - return err; +} - break; +static void orinoco_stat_gather(struct net_device *dev, + struct sk_buff *skb, + struct hermes_rx_descriptor *desc) +{ + struct orinoco_private *priv = netdev_priv(dev); - default: - if (priv->wep_on) { - printk(KERN_ERR "%s: WEP enabled, although not supported!\n", - priv->ndev->name); - return -EINVAL; - } + /* Using spy support with lots of Rx packets, like in an + * infrastructure (AP), will really slow down everything, because + * the MAC address must be compared to each entry of the spy list. + * If the user really asks for it (set some address in the + * spy list), we do it, but he will pay the price. + * Note that to get here, you need both WIRELESS_SPY + * compiled in AND some addresses in the list !!! + */ + /* Note : gcc will optimise the whole section away if + * WIRELESS_SPY is not defined... - Jean II */ + if (SPY_NUMBER(priv)) { + orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN, + desc->signal, desc->silence); } - - return 0; } -static int orinoco_hw_get_bssid(struct orinoco_private *priv, - char buf[ETH_ALEN]) +static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) { - hermes_t *hw = &priv->hw; - int err = 0; - unsigned long flags; + struct orinoco_private *priv = netdev_priv(dev); + struct net_device_stats *stats = &priv->stats; + struct iw_statistics *wstats = &priv->wstats; + struct sk_buff *skb = NULL; + u16 rxfid, status; + int length, data_len, data_off; + char *p; + struct hermes_rx_descriptor desc; + struct header_struct hdr; + struct ethhdr *eh; + int err; - err = orinoco_lock(priv, &flags); - if (err) - return err; - - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, - ETH_ALEN, NULL, buf); - - orinoco_unlock(priv, &flags); + rxfid = hermes_read_regn(hw, RXFID); - return err; -} + err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), + rxfid, 0); + if (err) { + printk(KERN_ERR "%s: error %d reading Rx descriptor. " + "Frame dropped.\n", dev->name, err); + stats->rx_errors++; + goto drop; + } -static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, - char buf[IW_ESSID_MAX_SIZE+1]) -{ - hermes_t *hw = &priv->hw; - int err = 0; - struct hermes_idstring essidbuf; - char *p = (char *)(&essidbuf.val); - int len; - unsigned long flags; + status = le16_to_cpu(desc.status); - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (status & HERMES_RXSTAT_ERR) { + if (status & HERMES_RXSTAT_UNDECRYPTABLE) { + wstats->discard.code++; + DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n", + dev->name); + } else { + stats->rx_crc_errors++; + DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name); + } + stats->rx_errors++; + goto drop; + } - if (strlen(priv->desired_essid) > 0) { - /* We read the desired SSID from the hardware rather - than from priv->desired_essid, just in case the - firmware is allowed to change it on us. I'm not - sure about this */ - /* My guess is that the OWNSSID should always be whatever - * we set to the card, whereas CURRENT_SSID is the one that - * may change... - Jean II */ - u16 rid; + /* For now we ignore the 802.11 header completely, assuming + that the card's firmware has handled anything vital */ - *active = 1; + err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr), + rxfid, HERMES_802_3_OFFSET); + if (err) { + printk(KERN_ERR "%s: error %d reading frame header. " + "Frame dropped.\n", dev->name, err); + stats->rx_errors++; + goto drop; + } - rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID : - HERMES_RID_CNFDESIREDSSID; - - err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf), - NULL, &essidbuf); - if (err) - goto fail_unlock; - } else { - *active = 0; + length = ntohs(hdr.len); + + /* Sanity checks */ + if (length < 3) { /* No for even an 802.2 LLC header */ + /* At least on Symbol firmware with PCF we get quite a + lot of these legitimately - Poll frames with no + data. */ + stats->rx_dropped++; + goto drop; + } + if (length > IEEE802_11_DATA_LEN) { + printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n", + dev->name, length); + stats->rx_length_errors++; + stats->rx_errors++; + goto drop; + } - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, - sizeof(essidbuf), NULL, &essidbuf); - if (err) - goto fail_unlock; + /* We need space for the packet data itself, plus an ethernet + header, plus 2 bytes so we can align the IP header on a + 32bit boundary, plus 1 byte so we can read in odd length + packets from the card, which has an IO granularity of 16 + bits */ + skb = dev_alloc_skb(length+ETH_HLEN+2+1); + if (!skb) { + printk(KERN_WARNING "%s: Can't allocate skb for Rx\n", + dev->name); + goto drop; } - len = le16_to_cpu(essidbuf.len); + skb_reserve(skb, 2); /* This way the IP header is aligned */ - memset(buf, 0, IW_ESSID_MAX_SIZE+1); - memcpy(buf, p, len); - buf[len] = '\0'; + /* Handle decapsulation + * In most cases, the firmware tell us about SNAP frames. + * For some reason, the SNAP frames sent by LinkSys APs + * are not properly recognised by most firmwares. + * So, check ourselves */ + if (((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || + ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || + is_ethersnap(&hdr)) { + /* These indicate a SNAP within 802.2 LLC within + 802.11 frame which we'll need to de-encapsulate to + the original EthernetII frame. */ - fail_unlock: - orinoco_unlock(priv, &flags); + if (length < ENCAPS_OVERHEAD) { /* No room for full LLC+SNAP */ + stats->rx_length_errors++; + goto drop; + } - return err; -} + /* Remove SNAP header, reconstruct EthernetII frame */ + data_len = length - ENCAPS_OVERHEAD; + data_off = HERMES_802_3_OFFSET + sizeof(hdr); -static long orinoco_hw_get_freq(struct orinoco_private *priv) -{ - - hermes_t *hw = &priv->hw; - int err = 0; - u16 channel; - long freq = 0; - unsigned long flags; + eh = (struct ethhdr *)skb_put(skb, ETH_HLEN); - err = orinoco_lock(priv, &flags); - if (err) - return err; - - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel); - if (err) - goto out; + memcpy(eh, &hdr, 2 * ETH_ALEN); + eh->h_proto = hdr.ethertype; + } else { + /* All other cases indicate a genuine 802.3 frame. No + decapsulation needed. We just throw the whole + thing in, and hope the protocol layer can deal with + it as 802.3 */ + data_len = length; + data_off = HERMES_802_3_OFFSET; + /* FIXME: we re-read from the card data we already read here */ + } - /* Intersil firmware 1.3.5 returns 0 when the interface is down */ - if (channel == 0) { - err = -EBUSY; - goto out; + 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; } - if ( (channel < 1) || (channel > NUM_CHANNELS) ) { - printk(KERN_WARNING "%s: Channel out of range (%d)!\n", - priv->ndev->name, channel); - err = -EBUSY; - goto out; + dev->last_rx = jiffies; + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_NONE; + + /* Process the wireless stats if needed */ + orinoco_stat_gather(dev, skb, &desc); - } - freq = channel_frequency[channel-1] * 100000; + /* Pass the packet to the networking stack */ + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += length; - out: - orinoco_unlock(priv, &flags); + return; - if (err > 0) - err = -EBUSY; - return err ? err : freq; -} + drop: + stats->rx_dropped++; -static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, - int *numrates, s32 *rates, int max) -{ - hermes_t *hw = &priv->hw; - struct hermes_idstring list; - unsigned char *p = (unsigned char *)&list.val; - int err = 0; - int num; - int i; - unsigned long flags; + if (skb) + dev_kfree_skb_irq(skb); + return; +} - err = orinoco_lock(priv, &flags); - if (err) - return err; +/********************************************************************/ +/* Rx path (info frames) */ +/********************************************************************/ - err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, - sizeof(list), NULL, &list); - orinoco_unlock(priv, &flags); +static void print_linkstatus(struct net_device *dev, u16 status) +{ + char * s; - if (err) - return err; - - num = le16_to_cpu(list.len); - *numrates = num; - num = min(num, max); + if (suppress_linkstatus) + return; - for (i = 0; i < num; i++) { - rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */ + switch (status) { + case HERMES_LINKSTATUS_NOT_CONNECTED: + s = "Not Connected"; + break; + case HERMES_LINKSTATUS_CONNECTED: + s = "Connected"; + break; + case HERMES_LINKSTATUS_DISCONNECTED: + s = "Disconnected"; + break; + case HERMES_LINKSTATUS_AP_CHANGE: + s = "AP Changed"; + break; + case HERMES_LINKSTATUS_AP_OUT_OF_RANGE: + s = "AP Out of Range"; + break; + case HERMES_LINKSTATUS_AP_IN_RANGE: + s = "AP In Range"; + break; + case HERMES_LINKSTATUS_ASSOC_FAILED: + s = "Association Failed"; + break; + default: + s = "UNKNOWN"; } - - return 0; + + printk(KERN_INFO "%s: New link status: %s (%04x)\n", + dev->name, s, status); } -#if 0 -static void show_rx_frame(struct orinoco_rxframe_hdr *frame) -{ - printk(KERN_DEBUG "RX descriptor:\n"); - printk(KERN_DEBUG " status = 0x%04x\n", frame->desc.status); - printk(KERN_DEBUG " time = 0x%08x\n", frame->desc.time); - printk(KERN_DEBUG " silence = 0x%02x\n", frame->desc.silence); - printk(KERN_DEBUG " signal = 0x%02x\n", frame->desc.signal); - printk(KERN_DEBUG " rate = 0x%02x\n", frame->desc.rate); - printk(KERN_DEBUG " rxflow = 0x%02x\n", frame->desc.rxflow); - printk(KERN_DEBUG " reserved = 0x%08x\n", frame->desc.reserved); - - printk(KERN_DEBUG "IEEE 802.11 header:\n"); - printk(KERN_DEBUG " frame_ctl = 0x%04x\n", - frame->p80211.frame_ctl); - printk(KERN_DEBUG " duration_id = 0x%04x\n", - frame->p80211.duration_id); - printk(KERN_DEBUG " addr1 = %02x:%02x:%02x:%02x:%02x:%02x\n", - frame->p80211.addr1[0], frame->p80211.addr1[1], - frame->p80211.addr1[2], frame->p80211.addr1[3], - frame->p80211.addr1[4], frame->p80211.addr1[5]); - printk(KERN_DEBUG " addr2 = %02x:%02x:%02x:%02x:%02x:%02x\n", - frame->p80211.addr2[0], frame->p80211.addr2[1], - frame->p80211.addr2[2], frame->p80211.addr2[3], - frame->p80211.addr2[4], frame->p80211.addr2[5]); - printk(KERN_DEBUG " addr3 = %02x:%02x:%02x:%02x:%02x:%02x\n", - frame->p80211.addr3[0], frame->p80211.addr3[1], - frame->p80211.addr3[2], frame->p80211.addr3[3], - frame->p80211.addr3[4], frame->p80211.addr3[5]); - printk(KERN_DEBUG " seq_ctl = 0x%04x\n", - frame->p80211.seq_ctl); - printk(KERN_DEBUG " addr4 = %02x:%02x:%02x:%02x:%02x:%02x\n", - frame->p80211.addr4[0], frame->p80211.addr4[1], - frame->p80211.addr4[2], frame->p80211.addr4[3], - frame->p80211.addr4[4], frame->p80211.addr4[5]); - printk(KERN_DEBUG " data_len = 0x%04x\n", - frame->p80211.data_len); - - printk(KERN_DEBUG "IEEE 802.3 header:\n"); - printk(KERN_DEBUG " dest = %02x:%02x:%02x:%02x:%02x:%02x\n", - frame->p8023.h_dest[0], frame->p8023.h_dest[1], - frame->p8023.h_dest[2], frame->p8023.h_dest[3], - frame->p8023.h_dest[4], frame->p8023.h_dest[5]); - printk(KERN_DEBUG " src = %02x:%02x:%02x:%02x:%02x:%02x\n", - frame->p8023.h_source[0], frame->p8023.h_source[1], - frame->p8023.h_source[2], frame->p8023.h_source[3], - frame->p8023.h_source[4], frame->p8023.h_source[5]); - printk(KERN_DEBUG " len = 0x%04x\n", frame->p8023.h_proto); - - printk(KERN_DEBUG "IEEE 802.2 LLC/SNAP header:\n"); - printk(KERN_DEBUG " DSAP = 0x%02x\n", frame->p8022.dsap); - printk(KERN_DEBUG " SSAP = 0x%02x\n", frame->p8022.ssap); - printk(KERN_DEBUG " ctrl = 0x%02x\n", frame->p8022.ctrl); - printk(KERN_DEBUG " OUI = %02x:%02x:%02x\n", - frame->p8022.oui[0], frame->p8022.oui[1], frame->p8022.oui[2]); - printk(KERN_DEBUG " ethertype = 0x%04x\n", frame->ethertype); -} -#endif /* 0 */ - -/* - * Interrupt handler - */ -irqreturn_t orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct orinoco_private *priv = netdev_priv(dev); - hermes_t *hw = &priv->hw; - int count = MAX_IRQLOOPS_PER_IRQ; - u16 evstat, events; - /* These are used to detect a runaway interrupt situation */ - /* If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy, - * we panic and shut down the hardware */ - static int last_irq_jiffy = 0; /* jiffies value the last time we were called */ - static int loops_this_jiffy = 0; - unsigned long flags; - - if (orinoco_lock(priv, &flags) != 0) { - /* If hw is unavailable - we don't know if the irq was - * for us or not */ - return IRQ_HANDLED; - } - - evstat = hermes_read_regn(hw, EVSTAT); - events = evstat & hw->inten; - if (! events) { - orinoco_unlock(priv, &flags); - return IRQ_NONE; - } - - if (jiffies != last_irq_jiffy) - loops_this_jiffy = 0; - last_irq_jiffy = jiffies; - - while (events && count--) { - if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { - printk(KERN_WARNING "%s: IRQ handler is looping too " - "much! Resetting.\n", dev->name); - /* Disable interrupts for now */ - hermes_set_irqmask(hw, 0); - schedule_work(&priv->reset_work); - break; - } - - /* Check the card hasn't been removed */ - if (! hermes_present(hw)) { - DEBUG(0, "orinoco_interrupt(): card removed\n"); - break; - } - - if (events & HERMES_EV_TICK) - __orinoco_ev_tick(dev, hw); - if (events & HERMES_EV_WTERR) - __orinoco_ev_wterr(dev, hw); - if (events & HERMES_EV_INFDROP) - __orinoco_ev_infdrop(dev, hw); - if (events & HERMES_EV_INFO) - __orinoco_ev_info(dev, hw); - if (events & HERMES_EV_RX) - __orinoco_ev_rx(dev, hw); - if (events & HERMES_EV_TXEXC) - __orinoco_ev_txexc(dev, hw); - if (events & HERMES_EV_TX) - __orinoco_ev_tx(dev, hw); - if (events & HERMES_EV_ALLOC) - __orinoco_ev_alloc(dev, hw); - - hermes_write_regn(hw, EVACK, events); - - evstat = hermes_read_regn(hw, EVSTAT); - events = evstat & hw->inten; - }; - - orinoco_unlock(priv, &flags); - return IRQ_HANDLED; -} - -static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw) -{ - printk(KERN_DEBUG "%s: TICK\n", dev->name); -} - -static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw) -{ - /* This seems to happen a fair bit under load, but ignoring it - seems to work fine...*/ - printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", - dev->name); -} - -static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw) -{ - if (net_ratelimit()) - printk(KERN_WARNING "%s: Information frame lost.\n", dev->name); -} - -static void print_linkstatus(struct net_device *dev, u16 status) -{ - char * s; - - if (suppress_linkstatus) - return; - - switch (status) { - case HERMES_LINKSTATUS_NOT_CONNECTED: - s = "Not Connected"; - break; - case HERMES_LINKSTATUS_CONNECTED: - s = "Connected"; - break; - case HERMES_LINKSTATUS_DISCONNECTED: - s = "Disconnected"; - break; - case HERMES_LINKSTATUS_AP_CHANGE: - s = "AP Changed"; - break; - case HERMES_LINKSTATUS_AP_OUT_OF_RANGE: - s = "AP Out of Range"; - break; - case HERMES_LINKSTATUS_AP_IN_RANGE: - s = "AP In Range"; - break; - case HERMES_LINKSTATUS_ASSOC_FAILED: - s = "Association Failed"; - break; - default: - s = "UNKNOWN"; - } - - printk(KERN_INFO "%s: New link status: %s (%04x)\n", - dev->name, s, status); -} - -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); u16 infofid; @@ -1614,19 +1259,17 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) le16_to_cpu(tallies.RxDiscards_WEPExcluded); wstats->discard.misc += le16_to_cpu(tallies.TxDiscardsWrongSA); -#if WIRELESS_EXT > 11 wstats->discard.fragment += le16_to_cpu(tallies.RxMsgInBadMsgFragments); wstats->discard.retries += le16_to_cpu(tallies.TxRetryLimitExceeded); /* wstats->miss.beacon - no match */ -#endif /* WIRELESS_EXT > 11 */ } break; case HERMES_INQ_LINKSTATUS: { struct hermes_linkstatus linkstatus; u16 newstatus; - + if (len != sizeof(linkstatus)) { printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n", dev->name, len); @@ -1647,226 +1290,730 @@ static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw) || (newstatus == HERMES_LINKSTATUS_ASSOC_FAILED) ) priv->connected = 0; - if (newstatus != priv->last_linkstatus) - print_linkstatus(dev, newstatus); + if (newstatus != priv->last_linkstatus) + print_linkstatus(dev, newstatus); + + priv->last_linkstatus = newstatus; + } + break; + default: + printk(KERN_DEBUG "%s: Unknown information frame received " + "(type %04x).\n", dev->name, type); + /* We don't actually do anything about it */ + break; + } +} + +static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw) +{ + if (net_ratelimit()) + printk(KERN_WARNING "%s: Information frame lost.\n", dev->name); +} + +/********************************************************************/ +/* Internal hardware control routines */ +/********************************************************************/ + +int __orinoco_up(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct hermes *hw = &priv->hw; + int err; + + err = __orinoco_program_rids(dev); + if (err) { + printk(KERN_ERR "%s: Error %d configuring card\n", + dev->name, err); + return err; + } + + /* Fire things up again */ + hermes_set_irqmask(hw, ORINOCO_INTEN); + err = hermes_enable_port(hw, 0); + if (err) { + printk(KERN_ERR "%s: Error %d enabling MAC port\n", + dev->name, err); + return err; + } + + netif_start_queue(dev); + + return 0; +} + +int __orinoco_down(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct hermes *hw = &priv->hw; + int err; + + netif_stop_queue(dev); + + if (! priv->hw_unavailable) { + if (! priv->broken_disableport) { + err = hermes_disable_port(hw, 0); + if (err) { + /* Some firmwares (e.g. Intersil 1.3.x) seem + * to have problems disabling the port, oh + * well, too bad. */ + printk(KERN_WARNING "%s: Error %d disabling MAC port\n", + dev->name, err); + priv->broken_disableport = 1; + } + } + hermes_set_irqmask(hw, 0); + hermes_write_regn(hw, EVACK, 0xffff); + } + + /* firmware will have to reassociate */ + priv->last_linkstatus = 0xffff; + priv->connected = 0; + + return 0; +} + +int orinoco_reinit_firmware(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct hermes *hw = &priv->hw; + int err; + + err = hermes_init(hw); + if (err) + return err; + + err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + if (err == -EIO) { + /* Try workaround for old Symbol firmware bug */ + printk(KERN_WARNING "%s: firmware ALLOC bug detected " + "(old Symbol firmware?). Trying to work around... ", + dev->name); + + priv->nicbuf_size = TX_NICBUF_SIZE_BUG; + err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + if (err) + printk("failed!\n"); + else + printk("ok.\n"); + } + + return err; +} + +static int __orinoco_hw_set_bitrate(struct orinoco_private *priv) +{ + hermes_t *hw = &priv->hw; + int err = 0; + + if (priv->bitratemode >= BITRATE_TABLE_SIZE) { + printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", + priv->ndev->name, priv->bitratemode); + return -EINVAL; + } + + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFTXRATECONTROL, + bitrate_table[priv->bitratemode].agere_txratectrl); + break; + case FIRMWARE_TYPE_INTERSIL: + case FIRMWARE_TYPE_SYMBOL: + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFTXRATECONTROL, + bitrate_table[priv->bitratemode].intersil_txratectrl); + break; + default: + BUG(); + } + + return err; +} + +static int __orinoco_hw_setup_wep(struct orinoco_private *priv) +{ + hermes_t *hw = &priv->hw; + int err = 0; + int master_wep_flag; + int auth_flag; + + switch (priv->firmware_type) { + case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ + if (priv->wep_on) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFTXKEY_AGERE, + priv->tx_key); + if (err) + return err; + + err = HERMES_WRITE_RECORD(hw, USER_BAP, + HERMES_RID_CNFWEPKEYS_AGERE, + &priv->keys); + if (err) + return err; + } + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFWEPENABLED_AGERE, + priv->wep_on); + if (err) + return err; + break; + + case FIRMWARE_TYPE_INTERSIL: /* Intersil style WEP */ + case FIRMWARE_TYPE_SYMBOL: /* Symbol style WEP */ + master_wep_flag = 0; /* Off */ + if (priv->wep_on) { + int keylen; + int i; + + /* Fudge around firmware weirdness */ + keylen = le16_to_cpu(priv->keys[priv->tx_key].len); + + /* Write all 4 keys */ + for(i = 0; i < ORINOCO_MAX_KEYS; i++) { +/* int keylen = le16_to_cpu(priv->keys[i].len); */ + + if (keylen > LARGE_KEY_SIZE) { + printk(KERN_ERR "%s: BUG: Key %d has oversize length %d.\n", + priv->ndev->name, i, keylen); + return -E2BIG; + } + + err = hermes_write_ltv(hw, USER_BAP, + HERMES_RID_CNFDEFAULTKEY0 + i, + HERMES_BYTES_TO_RECLEN(keylen), + priv->keys[i].data); + if (err) + return err; + } + + /* Write the index of the key used in transmission */ + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFWEPDEFAULTKEYID, + priv->tx_key); + if (err) + return err; + + if (priv->wep_restrict) { + auth_flag = 2; + master_wep_flag = 3; + } else { + /* Authentication is where Intersil and Symbol + * firmware differ... */ + auth_flag = 1; + if (priv->firmware_type == FIRMWARE_TYPE_SYMBOL) + master_wep_flag = 3; /* Symbol */ + else + master_wep_flag = 1; /* Intersil */ + } + + + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFAUTHENTICATION, + auth_flag); + if (err) + return err; + } + + /* Master WEP setting : on/off */ + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFWEPFLAGS_INTERSIL, + master_wep_flag); + if (err) + return err; + + break; + + default: + if (priv->wep_on) { + printk(KERN_ERR "%s: WEP enabled, although not supported!\n", + priv->ndev->name); + return -EINVAL; + } + } + + return 0; +} + +static int __orinoco_program_rids(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + hermes_t *hw = &priv->hw; + int err; + struct hermes_idstring idbuf; + + /* Set the MAC address */ + err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, + HERMES_BYTES_TO_RECLEN(ETH_ALEN), dev->dev_addr); + if (err) { + printk(KERN_ERR "%s: Error %d setting MAC address\n", + dev->name, err); + return err; + } + + /* Set up the link mode */ + err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, + priv->port_type); + if (err) { + printk(KERN_ERR "%s: Error %d setting port type\n", + dev->name, err); + return err; + } + /* Set the channel/frequency */ + if (priv->channel == 0) { + printk(KERN_DEBUG "%s: Channel is 0 in __orinoco_program_rids()\n", dev->name); + if (priv->createibss) + priv->channel = 10; + } + err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, + priv->channel); + if (err) { + printk(KERN_ERR "%s: Error %d setting channel\n", + dev->name, err); + return err; + } + + if (priv->has_ibss) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFCREATEIBSS, + priv->createibss); + if (err) { + printk(KERN_ERR "%s: Error %d setting CREATEIBSS\n", dev->name, err); + return err; + } + + if ((strlen(priv->desired_essid) == 0) && (priv->createibss) + && (!priv->has_ibss_any)) { + printk(KERN_WARNING "%s: This firmware requires an " + "ESSID in IBSS-Ad-Hoc mode.\n", dev->name); + /* With wvlan_cs, in this case, we would crash. + * hopefully, this driver will behave better... + * Jean II */ + } + } + + /* Set the desired ESSID */ + idbuf.len = cpu_to_le16(strlen(priv->desired_essid)); + memcpy(&idbuf.val, priv->desired_essid, sizeof(idbuf.val)); + /* WinXP wants partner to configure OWNSSID even in IBSS mode. (jimc) */ + err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, + HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), + &idbuf); + if (err) { + printk(KERN_ERR "%s: Error %d setting OWNSSID\n", + dev->name, err); + return err; + } + err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, + HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), + &idbuf); + if (err) { + printk(KERN_ERR "%s: Error %d setting DESIREDSSID\n", + dev->name, err); + return err; + } + + /* Set the station name */ + idbuf.len = cpu_to_le16(strlen(priv->nick)); + memcpy(&idbuf.val, priv->nick, sizeof(idbuf.val)); + err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, + HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2), + &idbuf); + if (err) { + printk(KERN_ERR "%s: Error %d setting nickname\n", + dev->name, err); + return err; + } + + /* Set AP density */ + if (priv->has_sensitivity) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFSYSTEMSCALE, + priv->ap_density); + if (err) { + printk(KERN_WARNING "%s: Error %d setting SYSTEMSCALE. " + "Disabling sensitivity control\n", + dev->name, err); + + priv->has_sensitivity = 0; + } + } + + /* Set RTS threshold */ + err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, + priv->rts_thresh); + if (err) { + printk(KERN_ERR "%s: Error %d setting RTS threshold\n", + dev->name, err); + return err; + } + + /* Set fragmentation threshold or MWO robustness */ + if (priv->has_mwo) + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFMWOROBUST_AGERE, + priv->mwo_robust); + else + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, + priv->frag_thresh); + if (err) { + printk(KERN_ERR "%s: Error %d setting fragmentation\n", + dev->name, err); + return err; + } + + /* Set bitrate */ + err = __orinoco_hw_set_bitrate(priv); + if (err) { + printk(KERN_ERR "%s: Error %d setting bitrate\n", + dev->name, err); + return err; + } + + /* Set power management */ + if (priv->has_pm) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFPMENABLED, + priv->pm_on); + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFMULTICASTRECEIVE, + priv->pm_mcast); + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFMAXSLEEPDURATION, + priv->pm_period); + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFPMHOLDOVERDURATION, + priv->pm_timeout); + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } + } + + /* Set preamble - only for Symbol so far... */ + if (priv->has_preamble) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFPREAMBLE_SYMBOL, + priv->preamble); + if (err) { + printk(KERN_ERR "%s: Error %d setting preamble\n", + dev->name, err); + return err; + } + } + + /* Set up encryption */ + if (priv->has_wep) { + err = __orinoco_hw_setup_wep(priv); + if (err) { + printk(KERN_ERR "%s: Error %d activating WEP\n", + dev->name, err); + return err; + } + } + + /* Set promiscuity / multicast*/ + priv->promiscuous = 0; + priv->mc_count = 0; + __orinoco_set_multicast_list(dev); /* FIXME: what about the xmit_lock */ + + return 0; +} + +/* FIXME: return int? */ +static void +__orinoco_set_multicast_list(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + hermes_t *hw = &priv->hw; + int err = 0; + int promisc, mc_count; + + /* The Hermes doesn't seem to have an allmulti mode, so we go + * into promiscuous mode and let the upper levels deal. */ + if ( (dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || + (dev->mc_count > MAX_MULTICAST(priv)) ) { + promisc = 1; + mc_count = 0; + } else { + promisc = 0; + mc_count = dev->mc_count; + } + + if (promisc != priv->promiscuous) { + err = hermes_write_wordrec(hw, USER_BAP, + HERMES_RID_CNFPROMISCUOUSMODE, + promisc); + if (err) { + printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n", + dev->name, err); + } else + priv->promiscuous = promisc; + } + + if (! promisc && (mc_count || priv->mc_count) ) { + struct dev_mc_list *p = dev->mc_list; + struct hermes_multicast mclist; + int i; - priv->last_linkstatus = newstatus; - } - break; - default: - printk(KERN_DEBUG "%s: Unknown information frame received (type %04x).\n", - dev->name, type); - /* We don't actually do anything about it */ - break; + for (i = 0; i < mc_count; i++) { + /* paranoia: is list shorter than mc_count? */ + BUG_ON(! p); + /* paranoia: bad address size in list? */ + BUG_ON(p->dmi_addrlen != ETH_ALEN); + + memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN); + p = p->next; + } + + if (p) + printk(KERN_WARNING "Multicast list is longer than mc_count\n"); + + err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES, + HERMES_BYTES_TO_RECLEN(priv->mc_count * ETH_ALEN), + &mclist); + if (err) + printk(KERN_ERR "%s: Error %d setting multicast list.\n", + dev->name, err); + else + priv->mc_count = mc_count; } + + /* Since we can set the promiscuous flag when it wasn't asked + for, make sure the net_device knows about it. */ + if (priv->promiscuous) + dev->flags |= IFF_PROMISC; + else + dev->flags &= ~IFF_PROMISC; } -static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) +static int orinoco_reconfigure(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); - struct net_device_stats *stats = &priv->stats; - struct iw_statistics *wstats = &priv->wstats; - struct sk_buff *skb = NULL; - u16 rxfid, status; - int length, data_len, data_off; - char *p; - struct hermes_rx_descriptor desc; - struct header_struct hdr; - struct ethhdr *eh; - int err; + struct hermes *hw = &priv->hw; + unsigned long flags; + int err = 0; - rxfid = hermes_read_regn(hw, RXFID); + if (priv->broken_disableport) { + schedule_work(&priv->reset_work); + return 0; + } - err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), - rxfid, 0); + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + err = hermes_disable_port(hw, 0); if (err) { - printk(KERN_ERR "%s: error %d reading Rx descriptor. " - "Frame dropped.\n", dev->name, err); - stats->rx_errors++; - goto drop; + printk(KERN_WARNING "%s: Unable to disable port while reconfiguring card\n", + dev->name); + priv->broken_disableport = 1; + goto out; } - status = le16_to_cpu(desc.status); - - if (status & HERMES_RXSTAT_ERR) { - if (status & HERMES_RXSTAT_UNDECRYPTABLE) { - wstats->discard.code++; - DEBUG(1, "%s: Undecryptable frame on Rx. Frame dropped.\n", - dev->name); - } else { - stats->rx_crc_errors++; - DEBUG(1, "%s: Bad CRC on Rx. Frame dropped.\n", dev->name); - } - stats->rx_errors++; - goto drop; + err = __orinoco_program_rids(dev); + if (err) { + printk(KERN_WARNING "%s: Unable to reconfigure card\n", + dev->name); + goto out; } - /* For now we ignore the 802.11 header completely, assuming - that the card's firmware has handled anything vital */ - - err = hermes_bap_pread(hw, IRQ_BAP, &hdr, sizeof(hdr), - rxfid, HERMES_802_3_OFFSET); + err = hermes_enable_port(hw, 0); if (err) { - printk(KERN_ERR "%s: error %d reading frame header. " - "Frame dropped.\n", dev->name, err); - stats->rx_errors++; - goto drop; + printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n", + dev->name); + goto out; } - length = ntohs(hdr.len); - - /* Sanity checks */ - if (length < 3) { /* No for even an 802.2 LLC header */ - /* At least on Symbol firmware with PCF we get quite a - lot of these legitimately - Poll frames with no - data. */ - stats->rx_dropped++; - goto drop; - } - if (length > IEEE802_11_DATA_LEN) { - printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n", - dev->name, length); - stats->rx_length_errors++; - stats->rx_errors++; - goto drop; + out: + if (err) { + printk(KERN_WARNING "%s: Resetting instead...\n", dev->name); + schedule_work(&priv->reset_work); + err = 0; } - /* We need space for the packet data itself, plus an ethernet - header, plus 2 bytes so we can align the IP header on a - 32bit boundary, plus 1 byte so we can read in odd length - packets from the card, which has an IO granularity of 16 - bits */ - skb = dev_alloc_skb(length+ETH_HLEN+2+1); - if (!skb) { - printk(KERN_WARNING "%s: Can't allocate skb for Rx\n", - dev->name); - goto drop; - } + orinoco_unlock(priv, &flags); + return err; - skb_reserve(skb, 2); /* This way the IP header is aligned */ +} - /* Handle decapsulation - * In most cases, the firmware tell us about SNAP frames. - * For some reason, the SNAP frames sent by LinkSys APs - * are not properly recognised by most firmwares. - * So, check ourselves */ - if(((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || - ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || - is_ethersnap(&hdr)) { - /* These indicate a SNAP within 802.2 LLC within - 802.11 frame which we'll need to de-encapsulate to - the original EthernetII frame. */ +/* This must be called from user context, without locks held - use + * schedule_work() */ +static void orinoco_reset(struct net_device *dev) +{ + struct orinoco_private *priv = netdev_priv(dev); + struct hermes *hw = &priv->hw; + int err = 0; + unsigned long flags; - if (length < ENCAPS_OVERHEAD) { /* No room for full LLC+SNAP */ - stats->rx_length_errors++; - goto drop; - } + if (orinoco_lock(priv, &flags) != 0) + /* When the hardware becomes available again, whatever + * detects that is responsible for re-initializing + * it. So no need for anything further */ + return; - /* Remove SNAP header, reconstruct EthernetII frame */ - data_len = length - ENCAPS_OVERHEAD; - data_off = HERMES_802_3_OFFSET + sizeof(hdr); + netif_stop_queue(dev); - eh = (struct ethhdr *)skb_put(skb, ETH_HLEN); + /* Shut off interrupts. Depending on what state the hardware + * is in, this might not work, but we'll try anyway */ + hermes_set_irqmask(hw, 0); + hermes_write_regn(hw, EVACK, 0xffff); - memcpy(eh, &hdr, 2 * ETH_ALEN); - eh->h_proto = hdr.ethertype; - } else { - /* All other cases indicate a genuine 802.3 frame. No - decapsulation needed. We just throw the whole - thing in, and hope the protocol layer can deal with - it as 802.3 */ - data_len = length; - data_off = HERMES_802_3_OFFSET; - /* FIXME: we re-read from the card data we already read here */ - } + priv->hw_unavailable++; + priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */ + priv->connected = 0; - p = skb_put(skb, data_len); - err = hermes_bap_pread(hw, IRQ_BAP, p, RUP_EVEN(data_len), - rxfid, data_off); + orinoco_unlock(priv, &flags); + + if (priv->hard_reset) + err = (*priv->hard_reset)(priv); if (err) { - printk(KERN_ERR "%s: error %d reading frame. " - "Frame dropped.\n", dev->name, err); - stats->rx_errors++; - goto drop; + printk(KERN_ERR "%s: orinoco_reset: Error %d " + "performing hard reset\n", dev->name, err); + /* FIXME: shutdown of some sort */ + return; } - dev->last_rx = jiffies; - skb->dev = dev; - skb->protocol = eth_type_trans(skb, dev); - skb->ip_summed = CHECKSUM_NONE; - - /* Process the wireless stats if needed */ - orinoco_stat_gather(dev, skb, &desc); - - /* Pass the packet to the networking stack */ - netif_rx(skb); - stats->rx_packets++; - stats->rx_bytes += length; - - return; - - drop: - stats->rx_dropped++; - - if (skb) - dev_kfree_skb_irq(skb); - return; -} + err = orinoco_reinit_firmware(dev); + if (err) { + printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", + dev->name, err); + return; + } -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; + spin_lock_irq(&priv->lock); /* This has to be called from user context */ - if (fid == DUMMY_FID) - return; /* Nothing's really happened */ + priv->hw_unavailable--; - 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)); + /* priv->open or priv->hw_unavailable might have changed while + * we dropped the lock */ + if (priv->open && (! priv->hw_unavailable)) { + err = __orinoco_up(dev); + if (err) { + printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n", + dev->name, err); + } else + dev->trans_start = jiffies; } - - stats->tx_errors++; - hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); + spin_unlock_irq(&priv->lock); + + return; } -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; +/********************************************************************/ +/* Interrupt handler */ +/********************************************************************/ - stats->tx_packets++; +static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw) +{ + printk(KERN_DEBUG "%s: TICK\n", dev->name); +} - hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID); +static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw) +{ + /* This seems to happen a fair bit under load, but ignoring it + seems to work fine...*/ + printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", + dev->name); } -static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw) +irqreturn_t orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + struct net_device *dev = (struct net_device *)dev_id; struct orinoco_private *priv = netdev_priv(dev); + hermes_t *hw = &priv->hw; + int count = MAX_IRQLOOPS_PER_IRQ; + u16 evstat, events; + /* These are used to detect a runaway interrupt situation */ + /* If we get more than MAX_IRQLOOPS_PER_JIFFY iterations in a jiffy, + * we panic and shut down the hardware */ + static int last_irq_jiffy = 0; /* jiffies value the last time + * we were called */ + static int loops_this_jiffy = 0; + unsigned long flags; - u16 fid = hermes_read_regn(hw, ALLOCFID); + if (orinoco_lock(priv, &flags) != 0) { + /* If hw is unavailable - we don't know if the irq was + * for us or not */ + return IRQ_HANDLED; + } - if (fid != priv->txfid) { - if (fid != DUMMY_FID) - printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n", - dev->name, fid); - return; - } else { - netif_wake_queue(dev); + evstat = hermes_read_regn(hw, EVSTAT); + events = evstat & hw->inten; + if (! events) { + orinoco_unlock(priv, &flags); + return IRQ_NONE; } + + if (jiffies != last_irq_jiffy) + loops_this_jiffy = 0; + last_irq_jiffy = jiffies; - hermes_write_regn(hw, ALLOCFID, DUMMY_FID); + while (events && count--) { + if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { + printk(KERN_WARNING "%s: IRQ handler is looping too " + "much! Resetting.\n", dev->name); + /* Disable interrupts for now */ + hermes_set_irqmask(hw, 0); + schedule_work(&priv->reset_work); + break; + } + + /* Check the card hasn't been removed */ + if (! hermes_present(hw)) { + DEBUG(0, "orinoco_interrupt(): card removed\n"); + break; + } + + if (events & HERMES_EV_TICK) + __orinoco_ev_tick(dev, hw); + if (events & HERMES_EV_WTERR) + __orinoco_ev_wterr(dev, hw); + if (events & HERMES_EV_INFDROP) + __orinoco_ev_infdrop(dev, hw); + if (events & HERMES_EV_INFO) + __orinoco_ev_info(dev, hw); + if (events & HERMES_EV_RX) + __orinoco_ev_rx(dev, hw); + if (events & HERMES_EV_TXEXC) + __orinoco_ev_txexc(dev, hw); + if (events & HERMES_EV_TX) + __orinoco_ev_tx(dev, hw); + if (events & HERMES_EV_ALLOC) + __orinoco_ev_alloc(dev, hw); + + hermes_write_regn(hw, EVACK, events); + + evstat = hermes_read_regn(hw, EVSTAT); + events = evstat & hw->inten; + }; + + orinoco_unlock(priv, &flags); + return IRQ_HANDLED; } +/********************************************************************/ +/* Initialization */ +/********************************************************************/ + struct sta_id { u16 id, variant, major, minor; } __attribute__ ((packed)); @@ -1901,11 +2048,11 @@ static void determine_firmware(struct net_device *dev) dev->name, err); memset(&sta_id, 0, sizeof(sta_id)); } + le16_to_cpus(&sta_id.id); le16_to_cpus(&sta_id.variant); le16_to_cpus(&sta_id.major); le16_to_cpus(&sta_id.minor); - printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n", dev->name, sta_id.id, sta_id.variant, sta_id.major, sta_id.minor); @@ -2018,12 +2165,7 @@ static void determine_firmware(struct net_device *dev) } } -/* - * struct net_device methods - */ - -static int -orinoco_init(struct net_device *dev) +static int orinoco_init(struct net_device *dev) { struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; @@ -2112,7 +2254,8 @@ orinoco_init(struct net_device *dev) err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFRTSTHRESHOLD, &priv->rts_thresh); if (err) { - printk(KERN_ERR "%s: failed to read RTS threshold!\n", dev->name); + printk(KERN_ERR "%s: failed to read RTS threshold!\n", + dev->name); goto out; } @@ -2125,7 +2268,8 @@ orinoco_init(struct net_device *dev) err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, &priv->frag_thresh); if (err) { - printk(KERN_ERR "%s: failed to read fragmentation settings!\n", dev->name); + printk(KERN_ERR "%s: failed to read fragmentation settings!\n", + dev->name); goto out; } @@ -2153,7 +2297,8 @@ orinoco_init(struct net_device *dev) /* Preamble setup */ if (priv->has_preamble) { - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPREAMBLE_SYMBOL, + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFPREAMBLE_SYMBOL, &priv->preamble); if (err) goto out; @@ -2162,397 +2307,240 @@ orinoco_init(struct net_device *dev) /* Set up the default configuration */ priv->iw_mode = IW_MODE_INFRA; /* By default use IEEE/IBSS ad-hoc mode if we have it */ - priv->prefer_port3 = priv->has_port3 && (! priv->has_ibss); - set_port_type(priv); - priv->channel = 10; /* default channel, more-or-less arbitrary */ - - priv->promiscuous = 0; - priv->wep_on = 0; - priv->tx_key = 0; - - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); - if (err == -EIO) { - /* Try workaround for old Symbol firmware bug */ - printk(KERN_WARNING "%s: firmware ALLOC bug detected " - "(old Symbol firmware?). Trying to work around... ", - dev->name); - - priv->nicbuf_size = TX_NICBUF_SIZE_BUG; - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); - if (err) - printk("failed!\n"); - else - printk("ok.\n"); - } - if (err) { - printk("%s: Error %d allocating Tx buffer\n", dev->name, err); - goto out; - } - - /* Make the hardware available, as long as it hasn't been - * removed elsewhere (e.g. by PCMCIA hot unplug) */ - spin_lock_irq(&priv->lock); - priv->hw_unavailable--; - spin_unlock_irq(&priv->lock); - - printk(KERN_DEBUG "%s: ready\n", dev->name); - - out: - TRACE_EXIT(dev->name); - return err; -} - -struct net_device_stats * -orinoco_get_stats(struct net_device *dev) -{ - struct orinoco_private *priv = netdev_priv(dev); - - return &priv->stats; -} - -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? */ - } - - err = orinoco_lock(priv, &flags); - if (err) - 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 inline void orinoco_spy_gather(struct net_device *dev, u_char *mac, - int level, int noise) -{ - struct orinoco_private *priv = netdev_priv(dev); - int i; - - /* Gather wireless spy statistics: for each packet, compare the - * source address with out list, and if match, get the stats... */ - for (i = 0; i < priv->spy_number; i++) - if (!memcmp(mac, priv->spy_address[i], ETH_ALEN)) { - priv->spy_stat[i].level = level - 0x95; - priv->spy_stat[i].noise = noise - 0x95; - priv->spy_stat[i].qual = (level > noise) ? (level - noise) : 0; - priv->spy_stat[i].updated = 7; - } -} - -void -orinoco_stat_gather(struct net_device *dev, - struct sk_buff *skb, - struct hermes_rx_descriptor *desc) -{ - struct orinoco_private *priv = netdev_priv(dev); - - /* Using spy support with lots of Rx packets, like in an - * infrastructure (AP), will really slow down everything, because - * the MAC address must be compared to each entry of the spy list. - * If the user really asks for it (set some address in the - * spy list), we do it, but he will pay the price. - * Note that to get here, you need both WIRELESS_SPY - * compiled in AND some addresses in the list !!! - */ - /* Note : gcc will optimise the whole section away if - * WIRELESS_SPY is not defined... - Jean II */ - if (SPY_NUMBER(priv)) { - orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN, - desc->signal, desc->silence); - } -} - -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; + priv->prefer_port3 = priv->has_port3 && (! priv->has_ibss); + set_port_type(priv); + priv->channel = 10; /* default channel, more-or-less arbitrary */ - TRACE_ENTER(dev->name); + priv->promiscuous = 0; + priv->wep_on = 0; + priv->tx_key = 0; - 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", + err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + if (err == -EIO) { + /* Try workaround for old Symbol firmware bug */ + printk(KERN_WARNING "%s: firmware ALLOC bug detected " + "(old Symbol firmware?). Trying to work around... ", dev->name); - TRACE_EXIT(dev->name); - return 1; + + priv->nicbuf_size = TX_NICBUF_SIZE_BUG; + err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); + if (err) + printk("failed!\n"); + else + printk("ok.\n"); } - - if (orinoco_lock(priv, &flags) != 0) { - printk(KERN_ERR "%s: orinoco_xmit() called while hw_unavailable\n", - dev->name); - TRACE_EXIT(dev->name); -/* BUG(); */ - return 1; + if (err) { + printk("%s: Error %d allocating Tx buffer\n", dev->name, err); + goto out; } - if (! priv->connected) { - /* 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; - } + /* Make the hardware available, as long as it hasn't been + * removed elsewhere (e.g. by PCMCIA hot unplug) */ + spin_lock_irq(&priv->lock); + priv->hw_unavailable--; + spin_unlock_irq(&priv->lock); - /* 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); + printk(KERN_DEBUG "%s: ready\n", dev->name); - eh = (struct ethhdr *)skb->data; + out: + TRACE_EXIT(dev->name); + return err; +} - 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) { - printk(KERN_ERR "%s: Error %d writing Tx descriptor to BAP\n", - dev->name, err); - stats->tx_errors++; - goto fail; - } +struct net_device *alloc_orinocodev(int sizeof_card, + int (*hard_reset)(struct orinoco_private *)) +{ + struct net_device *dev; + struct orinoco_private *priv; - /* 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); + dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card); + if (! dev) + return NULL; + priv = netdev_priv(dev); + priv->ndev = dev; + if (sizeof_card) + priv->card = (void *)((unsigned long)netdev_priv(dev) + + sizeof(struct orinoco_private)); + else + priv->card = NULL; - /* Encapsulate Ethernet-II frames */ - if (ntohs(eh->h_proto) > 1500) { /* Ethernet-II frame */ - struct header_struct hdr; - data_len = len; - data_off = HERMES_802_3_OFFSET + sizeof(hdr); - p = skb->data + ETH_HLEN; + /* Setup / override net_device fields */ + dev->init = orinoco_init; + dev->hard_start_xmit = orinoco_xmit; + dev->tx_timeout = orinoco_tx_timeout; + dev->watchdog_timeo = HZ; /* 1 second timeout */ + dev->get_stats = orinoco_get_stats; + dev->get_wireless_stats = orinoco_get_wireless_stats; + dev->do_ioctl = orinoco_ioctl; + dev->change_mtu = orinoco_change_mtu; + dev->set_multicast_list = orinoco_set_multicast_list; + /* we use the default eth_mac_addr for setting the MAC addr */ - /* 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) { - 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; - } + /* Set up default callbacks */ + dev->open = orinoco_open; + dev->stop = orinoco_stop; + priv->hard_reset = hard_reset; - /* Round up for odd length packets */ - err = hermes_bap_pwrite(hw, USER_BAP, p, RUP_EVEN(data_len), txfid, data_off); - if (err) { - printk(KERN_ERR "%s: Error %d writing packet to BAP\n", - dev->name, err); - stats->tx_errors++; - goto fail; - } + spin_lock_init(&priv->lock); + priv->open = 0; + priv->hw_unavailable = 1; /* orinoco_init() must clear this + * before anything else touches the + * hardware */ + INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev); - /* Finally, we actually initiate the send */ - netif_stop_queue(dev); + priv->last_linkstatus = 0xffff; + priv->connected = 0; - 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; - } + return dev; - dev->trans_start = jiffies; - stats->tx_bytes += data_off + data_len; +} - orinoco_unlock(priv, &flags); +/********************************************************************/ +/* Wireless extensions */ +/********************************************************************/ - dev_kfree_skb(skb); +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; - TRACE_EXIT(dev->name); + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; - return 0; - fail: - TRACE_EXIT(dev->name); + err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTBSSID, + ETH_ALEN, NULL, buf); orinoco_unlock(priv, &flags); + return err; } -static void -orinoco_tx_timeout(struct net_device *dev) +static int orinoco_hw_get_essid(struct orinoco_private *priv, int *active, + char buf[IW_ESSID_MAX_SIZE+1]) { - struct orinoco_private *priv = netdev_priv(dev); - struct net_device_stats *stats = &priv->stats; - struct hermes *hw = &priv->hw; + hermes_t *hw = &priv->hw; + int err = 0; + struct hermes_idstring essidbuf; + char *p = (char *)(&essidbuf.val); + int len; + unsigned long flags; - printk(KERN_WARNING "%s: Tx timeout! " - "ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n", - dev->name, hermes_read_regn(hw, ALLOCFID), - hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT)); + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; - stats->tx_errors++; + if (strlen(priv->desired_essid) > 0) { + /* We read the desired SSID from the hardware rather + than from priv->desired_essid, just in case the + firmware is allowed to change it on us. I'm not + sure about this */ + /* My guess is that the OWNSSID should always be whatever + * we set to the card, whereas CURRENT_SSID is the one that + * may change... - Jean II */ + u16 rid; - schedule_work(&priv->reset_work); + *active = 1; + + rid = (priv->port_type == 3) ? HERMES_RID_CNFOWNSSID : + HERMES_RID_CNFDESIREDSSID; + + err = hermes_read_ltv(hw, USER_BAP, rid, sizeof(essidbuf), + NULL, &essidbuf); + if (err) + goto fail_unlock; + } else { + *active = 0; + + err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_CURRENTSSID, + sizeof(essidbuf), NULL, &essidbuf); + if (err) + goto fail_unlock; + } + + len = le16_to_cpu(essidbuf.len); + + memset(buf, 0, IW_ESSID_MAX_SIZE+1); + memcpy(buf, p, len); + buf[len] = '\0'; + + fail_unlock: + orinoco_unlock(priv, &flags); + + return err; } -static int -orinoco_change_mtu(struct net_device *dev, int new_mtu) +static long orinoco_hw_get_freq(struct orinoco_private *priv) { - struct orinoco_private *priv = netdev_priv(dev); + + hermes_t *hw = &priv->hw; + int err = 0; + u16 channel; + long freq = 0; + unsigned long flags; - if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) ) - return -EINVAL; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + + err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel); + if (err) + goto out; - if ( (new_mtu + ENCAPS_OVERHEAD + IEEE802_11_HLEN) > - (priv->nicbuf_size - ETH_HLEN) ) - return -EINVAL; + /* Intersil firmware 1.3.5 returns 0 when the interface is down */ + if (channel == 0) { + err = -EBUSY; + goto out; + } + + if ( (channel < 1) || (channel > NUM_CHANNELS) ) { + printk(KERN_WARNING "%s: Channel out of range (%d)!\n", + priv->ndev->name, channel); + err = -EBUSY; + goto out; - dev->mtu = new_mtu; + } + freq = channel_frequency[channel-1] * 100000; - return 0; + out: + orinoco_unlock(priv, &flags); + + if (err > 0) + err = -EBUSY; + return err ? err : freq; } -/* FIXME: return int? */ -static void -__orinoco_set_multicast_list(struct net_device *dev) +static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, + int *numrates, s32 *rates, int max) { - struct orinoco_private *priv = netdev_priv(dev); hermes_t *hw = &priv->hw; + struct hermes_idstring list; + unsigned char *p = (unsigned char *)&list.val; int err = 0; - int promisc, mc_count; - - /* The Hermes doesn't seem to have an allmulti mode, so we go - * into promiscuous mode and let the upper levels deal. */ - if ( (dev->flags & IFF_PROMISC) || (dev->flags & IFF_ALLMULTI) || - (dev->mc_count > MAX_MULTICAST(priv)) ) { - promisc = 1; - mc_count = 0; - } else { - promisc = 0; - mc_count = dev->mc_count; - } + int num; + int i; + unsigned long flags; - if (promisc != priv->promiscuous) { - err = hermes_write_wordrec(hw, USER_BAP, - HERMES_RID_CNFPROMISCUOUSMODE, - promisc); - if (err) { - printk(KERN_ERR "%s: Error %d setting PROMISCUOUSMODE to 1.\n", - dev->name, err); - } else - priv->promiscuous = promisc; - } + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; - if (! promisc && (mc_count || priv->mc_count) ) { - struct dev_mc_list *p = dev->mc_list; - hermes_multicast_t mclist; - int i; + err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, + sizeof(list), NULL, &list); + orinoco_unlock(priv, &flags); - for (i = 0; i < mc_count; i++) { - /* Paranoia: */ - if (! p) - BUG(); /* Multicast list shorter than mc_count */ - if (p->dmi_addrlen != ETH_ALEN) - BUG(); /* Bad address size in multicast list */ - - memcpy(mclist.addr[i], p->dmi_addr, ETH_ALEN); - p = p->next; - } - - if (p) - printk(KERN_WARNING "Multicast list is longer than mc_count\n"); + if (err) + return err; + + num = le16_to_cpu(list.len); + *numrates = num; + num = min(num, max); - err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFGROUPADDRESSES, - HERMES_BYTES_TO_RECLEN(priv->mc_count * ETH_ALEN), - &mclist); - if (err) - printk(KERN_ERR "%s: Error %d setting multicast list.\n", - dev->name, err); - else - priv->mc_count = mc_count; + for (i = 0; i < num; i++) { + rates[i] = (p[i] & 0x7f) * 500000; /* convert to bps */ } - /* Since we can set the promiscuous flag when it wasn't asked - for, make sure the net_device knows about it. */ - if (priv->promiscuous) - dev->flags |= IFF_PROMISC; - else - dev->flags &= ~IFF_PROMISC; + return 0; } -/********************************************************************/ -/* Wireless extensions support */ -/********************************************************************/ - static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq) { struct orinoco_private *priv = netdev_priv(dev); @@ -2571,9 +2559,8 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq rrq->length = sizeof(range); - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; mode = priv->iw_mode; orinoco_unlock(priv, &flags); @@ -2582,10 +2569,8 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq /* Much of this shamelessly taken from wvlan_cs.c. No idea * what it all means -dgibson */ -#if WIRELESS_EXT > 10 range.we_version_compiled = WIRELESS_EXT; range.we_version_source = 11; -#endif /* WIRELESS_EXT > 10 */ range.min_nwid = range.max_nwid = 0; /* We don't use nwids */ @@ -2612,22 +2597,17 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq range.max_qual.qual = 0; range.max_qual.level = 0; range.max_qual.noise = 0; -#if WIRELESS_EXT > 11 range.avg_qual.qual = 0; range.avg_qual.level = 0; range.avg_qual.noise = 0; -#endif /* WIRELESS_EXT > 11 */ - } else { range.max_qual.qual = 0x8b - 0x2f; range.max_qual.level = 0x2f - 0x95 - 1; range.max_qual.noise = 0x2f - 0x95 - 1; -#if WIRELESS_EXT > 11 /* Need to get better values */ range.avg_qual.qual = 0x24; range.avg_qual.level = 0xC2; range.avg_qual.noise = 0x9E; -#endif /* WIRELESS_EXT > 11 */ } err = orinoco_hw_get_bitratelist(priv, &numrates, @@ -2649,9 +2629,8 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq range.min_frag = 256; range.max_frag = 2346; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; if (priv->has_wep) { range.max_encoding_tokens = ORINOCO_MAX_KEYS; @@ -2680,7 +2659,6 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq range.txpower[0] = 15; /* 15dBm */ range.txpower_capa = IW_TXPOW_DBM; -#if WIRELESS_EXT > 10 range.retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME; range.retry_flags = IW_RETRY_LIMIT; range.r_time_flags = IW_RETRY_LIFETIME; @@ -2688,7 +2666,6 @@ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq range.max_retry = 65535; /* ??? */ range.min_r_time = 0; range.max_r_time = 65535 * 1000; /* ??? */ -#endif /* WIRELESS_EXT > 10 */ if (copy_to_user(rrq->pointer, &range, sizeof(range))) return -EFAULT; @@ -2718,10 +2695,9 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er if (copy_from_user(keybuf, erq->pointer, erq->length)) return -EFAULT; } - - err = orinoco_lock(priv, &flags); - if (err) - return err; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; if (erq->pointer) { if (erq->length > ORINOCO_MAX_KEY_SIZE) { @@ -2737,7 +2713,8 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) index = priv->tx_key; - + + /* Adjust key length to a supported value */ if (erq->length > SMALL_KEY_SIZE) { xlen = LARGE_KEY_SIZE; } else if (erq->length > 0) { @@ -2779,14 +2756,14 @@ static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *er if (erq->pointer) { priv->keys[index].len = cpu_to_le16(xlen); - memset(priv->keys[index].data, 0, sizeof(priv->keys[index].data)); + memset(priv->keys[index].data, 0, + sizeof(priv->keys[index].data)); memcpy(priv->keys[index].data, keybuf, erq->length); } priv->tx_key = setindex; priv->wep_on = enable; priv->wep_restrict = restricted; - out: orinoco_unlock(priv, &flags); @@ -2799,12 +2776,10 @@ static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *er int index = (erq->flags & IW_ENCODE_INDEX) - 1; u16 xlen = 0; char keybuf[ORINOCO_MAX_KEY_SIZE]; - int err; unsigned long flags; - - err = orinoco_lock(priv, &flags); - if (err) - return err; + + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) index = priv->tx_key; @@ -2844,7 +2819,6 @@ static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq) { struct orinoco_private *priv = netdev_priv(dev); char essidbuf[IW_ESSID_MAX_SIZE+1]; - int err; unsigned long flags; /* Note : ESSID is ignored in Ad-Hoc demo mode, but we can set it @@ -2862,9 +2836,8 @@ static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq) essidbuf[erq->length] = '\0'; } - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; memcpy(priv->desired_essid, essidbuf, sizeof(priv->desired_essid)); @@ -2888,9 +2861,8 @@ static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq) if (err) return err; } else { - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; memcpy(essidbuf, priv->desired_essid, sizeof(essidbuf)); orinoco_unlock(priv, &flags); } @@ -2910,7 +2882,6 @@ static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq) { struct orinoco_private *priv = netdev_priv(dev); char nickbuf[IW_ESSID_MAX_SIZE+1]; - int err; unsigned long flags; if (nrq->length > IW_ESSID_MAX_SIZE) @@ -2923,9 +2894,8 @@ static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq) nickbuf[nrq->length] = '\0'; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; memcpy(priv->nick, nickbuf, sizeof(priv->nick)); @@ -2938,12 +2908,10 @@ static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq) { struct orinoco_private *priv = netdev_priv(dev); char nickbuf[IW_ESSID_MAX_SIZE+1]; - int err; unsigned long flags; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE+1); orinoco_unlock(priv, &flags); @@ -2960,7 +2928,6 @@ static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq) { struct orinoco_private *priv = netdev_priv(dev); int chan = -1; - int err; unsigned long flags; /* We can only use this in Ad-Hoc demo mode to set the operating @@ -2989,9 +2956,8 @@ static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq) ! (priv->channel_mask & (1 << (chan-1)) ) ) return -EINVAL; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; priv->channel = chan; orinoco_unlock(priv, &flags); @@ -3009,10 +2975,10 @@ static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq) if (!priv->has_sensitivity) return -EOPNOTSUPP; - err = orinoco_lock(priv, &flags); - if (err) - return err; - err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &val); + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; + err = hermes_read_wordrec(hw, USER_BAP, + HERMES_RID_CNFSYSTEMSCALE, &val); orinoco_unlock(priv, &flags); if (err) @@ -3028,7 +2994,6 @@ static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq) { struct orinoco_private *priv = netdev_priv(dev); int val = srq->value; - int err; unsigned long flags; if (!priv->has_sensitivity) @@ -3037,9 +3002,8 @@ static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq) if ((val < 1) || (val > 3)) return -EINVAL; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; priv->ap_density = val; orinoco_unlock(priv, &flags); @@ -3050,7 +3014,6 @@ static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq) { struct orinoco_private *priv = netdev_priv(dev); int val = rrq->value; - int err; unsigned long flags; if (rrq->disabled) @@ -3059,9 +3022,8 @@ static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq) if ( (val < 0) || (val > 2347) ) return -EINVAL; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; priv->rts_thresh = val; orinoco_unlock(priv, &flags); @@ -3075,9 +3037,8 @@ static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq) int err = 0; unsigned long flags; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; if (priv->has_mwo) { if (frq->disabled) @@ -3112,9 +3073,8 @@ static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq) u16 val; unsigned long flags; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; if (priv->has_mwo) { err = hermes_read_wordrec(hw, USER_BAP, @@ -3176,9 +3136,8 @@ static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *rrq) if (ratemode == -1) return -EINVAL; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; priv->bitratemode = ratemode; orinoco_unlock(priv, &flags); @@ -3195,14 +3154,12 @@ static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *rrq) u16 val; unsigned long flags; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; ratemode = priv->bitratemode; - if ( (ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE) ) - BUG(); + BUG_ON((ratemode < 0) || (ratemode >= BITRATE_TABLE_SIZE)); rrq->value = bitrate_table[ratemode].bitrate * 100000; rrq->fixed = ! bitrate_table[ratemode].automatic; @@ -3258,9 +3215,8 @@ static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq) int err = 0; unsigned long flags; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; if (prq->disabled) { priv->pm_on = 0; @@ -3313,9 +3269,8 @@ static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq) u16 enable, period, timeout, mcast; unsigned long flags; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, &enable); if (err) @@ -3354,7 +3309,6 @@ static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq) return err; } -#if WIRELESS_EXT > 10 static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq) { struct orinoco_private *priv = netdev_priv(dev); @@ -3363,9 +3317,8 @@ static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq) u16 short_limit, long_limit, lifetime; unsigned long flags; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT, &short_limit); @@ -3406,18 +3359,15 @@ static int orinoco_ioctl_getretry(struct net_device *dev, struct iw_param *rrq) return err; } -#endif /* WIRELESS_EXT > 10 */ static int orinoco_ioctl_setibssport(struct net_device *dev, struct iwreq *wrq) { struct orinoco_private *priv = netdev_priv(dev); int val = *( (int *) wrq->u.name ); - int err; unsigned long flags; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; priv->ibss_port = val ; @@ -3432,12 +3382,10 @@ static int orinoco_ioctl_getibssport(struct net_device *dev, struct iwreq *wrq) { struct orinoco_private *priv = netdev_priv(dev); int *val = (int *)wrq->u.name; - int err; unsigned long flags; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; *val = priv->ibss_port; orinoco_unlock(priv, &flags); @@ -3452,9 +3400,8 @@ static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq) int err = 0; unsigned long flags; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; switch (val) { case 0: /* Try to do IEEE ad-hoc mode */ @@ -3491,16 +3438,13 @@ static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq) { struct orinoco_private *priv = netdev_priv(dev); int *val = (int *)wrq->u.name; - int err; unsigned long flags; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; *val = priv->prefer_port3; orinoco_unlock(priv, &flags); - return 0; } @@ -3527,9 +3471,8 @@ static int orinoco_ioctl_setspy(struct net_device *dev, struct iw_point *srq) } /* Make sure nobody mess with the structure while we do */ - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; /* orinoco_lock() doesn't disable interrupts, so make sure the * interrupt rx path don't get confused while we copy */ @@ -3560,12 +3503,10 @@ static int orinoco_ioctl_getspy(struct net_device *dev, struct iw_point *srq) struct iw_quality spy_stat[IW_MAX_SPY]; int number; int i; - int err; unsigned long flags; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; number = priv->spy_number; if ((number > 0) && (srq->pointer)) { @@ -3635,9 +3576,8 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) break; case SIOCSIWMODE: - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; switch (wrq->u.mode) { case IW_MODE_ADHOC: if (! (priv->has_ibss || priv->has_port3) ) @@ -3662,9 +3602,8 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) break; case SIOCGIWMODE: - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; wrq->u.mode = priv->iw_mode; orinoco_unlock(priv, &flags); break; @@ -3790,7 +3729,6 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) wrq->u.txpower.flags = IW_TXPOW_DBM; break; -#if WIRELESS_EXT > 10 case SIOCSIWRETRY: err = -EOPNOTSUPP; break; @@ -3798,7 +3736,6 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) case SIOCGIWRETRY: err = orinoco_ioctl_getretry(dev, &wrq->u.retry); break; -#endif /* WIRELESS_EXT > 10 */ case SIOCSIWSPY: err = orinoco_ioctl_setspy(dev, &wrq->u.data); @@ -3881,9 +3818,8 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) if(priv->has_preamble) { int val = *( (int *) wrq->u.name ); - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; if (val) priv->preamble = 1; else @@ -3898,9 +3834,8 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) if(priv->has_preamble) { int *val = (int *)wrq->u.name; - err = orinoco_lock(priv, &flags); - if (err) - return err; + if (orinoco_lock(priv, &flags) != 0) + return -EBUSY; *val = priv->preamble; orinoco_unlock(priv, &flags); } else @@ -4124,51 +4059,68 @@ static int orinoco_debug_dump_recs(struct net_device *dev) return 0; } -struct net_device *alloc_orinocodev(int sizeof_card, int (*hard_reset)(struct orinoco_private *)) -{ - struct net_device *dev; - struct orinoco_private *priv; - - dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card); - if (!dev) - return NULL; - priv = netdev_priv(dev); - priv->ndev = dev; - if (sizeof_card) - priv->card = (void *)((unsigned long)dev->priv + sizeof(struct orinoco_private)); - else - priv->card = NULL; - - /* Setup / override net_device fields */ - dev->init = orinoco_init; - dev->hard_start_xmit = orinoco_xmit; - dev->tx_timeout = orinoco_tx_timeout; - dev->watchdog_timeo = HZ; /* 1 second timeout */ - dev->get_stats = orinoco_get_stats; - dev->get_wireless_stats = orinoco_get_wireless_stats; - dev->do_ioctl = orinoco_ioctl; - dev->change_mtu = orinoco_change_mtu; - dev->set_multicast_list = orinoco_set_multicast_list; - /* we use the default eth_mac_addr for setting the MAC addr */ - - /* Set up default callbacks */ - dev->open = orinoco_open; - dev->stop = orinoco_stop; - priv->hard_reset = hard_reset; +/********************************************************************/ +/* Debugging */ +/********************************************************************/ - spin_lock_init(&priv->lock); - priv->open = 0; - priv->hw_unavailable = 1; /* orinoco_init() must clear this - * before anything else touches the - * hardware */ - INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev); +#if 0 +static void show_rx_frame(struct orinoco_rxframe_hdr *frame) +{ + printk(KERN_DEBUG "RX descriptor:\n"); + printk(KERN_DEBUG " status = 0x%04x\n", frame->desc.status); + printk(KERN_DEBUG " time = 0x%08x\n", frame->desc.time); + printk(KERN_DEBUG " silence = 0x%02x\n", frame->desc.silence); + printk(KERN_DEBUG " signal = 0x%02x\n", frame->desc.signal); + printk(KERN_DEBUG " rate = 0x%02x\n", frame->desc.rate); + printk(KERN_DEBUG " rxflow = 0x%02x\n", frame->desc.rxflow); + printk(KERN_DEBUG " reserved = 0x%08x\n", frame->desc.reserved); - priv->last_linkstatus = 0xffff; - priv->connected = 0; + printk(KERN_DEBUG "IEEE 802.11 header:\n"); + printk(KERN_DEBUG " frame_ctl = 0x%04x\n", + frame->p80211.frame_ctl); + printk(KERN_DEBUG " duration_id = 0x%04x\n", + frame->p80211.duration_id); + printk(KERN_DEBUG " addr1 = %02x:%02x:%02x:%02x:%02x:%02x\n", + frame->p80211.addr1[0], frame->p80211.addr1[1], + frame->p80211.addr1[2], frame->p80211.addr1[3], + frame->p80211.addr1[4], frame->p80211.addr1[5]); + printk(KERN_DEBUG " addr2 = %02x:%02x:%02x:%02x:%02x:%02x\n", + frame->p80211.addr2[0], frame->p80211.addr2[1], + frame->p80211.addr2[2], frame->p80211.addr2[3], + frame->p80211.addr2[4], frame->p80211.addr2[5]); + printk(KERN_DEBUG " addr3 = %02x:%02x:%02x:%02x:%02x:%02x\n", + frame->p80211.addr3[0], frame->p80211.addr3[1], + frame->p80211.addr3[2], frame->p80211.addr3[3], + frame->p80211.addr3[4], frame->p80211.addr3[5]); + printk(KERN_DEBUG " seq_ctl = 0x%04x\n", + frame->p80211.seq_ctl); + printk(KERN_DEBUG " addr4 = %02x:%02x:%02x:%02x:%02x:%02x\n", + frame->p80211.addr4[0], frame->p80211.addr4[1], + frame->p80211.addr4[2], frame->p80211.addr4[3], + frame->p80211.addr4[4], frame->p80211.addr4[5]); + printk(KERN_DEBUG " data_len = 0x%04x\n", + frame->p80211.data_len); - return dev; + printk(KERN_DEBUG "IEEE 802.3 header:\n"); + printk(KERN_DEBUG " dest = %02x:%02x:%02x:%02x:%02x:%02x\n", + frame->p8023.h_dest[0], frame->p8023.h_dest[1], + frame->p8023.h_dest[2], frame->p8023.h_dest[3], + frame->p8023.h_dest[4], frame->p8023.h_dest[5]); + printk(KERN_DEBUG " src = %02x:%02x:%02x:%02x:%02x:%02x\n", + frame->p8023.h_source[0], frame->p8023.h_source[1], + frame->p8023.h_source[2], frame->p8023.h_source[3], + frame->p8023.h_source[4], frame->p8023.h_source[5]); + printk(KERN_DEBUG " len = 0x%04x\n", frame->p8023.h_proto); + printk(KERN_DEBUG "IEEE 802.2 LLC/SNAP header:\n"); + printk(KERN_DEBUG " DSAP = 0x%02x\n", frame->p8022.dsap); + printk(KERN_DEBUG " SSAP = 0x%02x\n", frame->p8022.ssap); + printk(KERN_DEBUG " ctrl = 0x%02x\n", frame->p8022.ctrl); + printk(KERN_DEBUG " OUI = %02x:%02x:%02x\n", + frame->p8022.oui[0], frame->p8022.oui[1], frame->p8022.oui[2]); + printk(KERN_DEBUG " ethertype = 0x%04x\n", frame->ethertype); } +#endif /* 0 */ /********************************************************************/ /* Module initialization */ @@ -4185,7 +4137,9 @@ EXPORT_SYMBOL(orinoco_interrupt); /* Can't be declared "const" or the whole __initdata section will * become const */ -static char version[] __initdata = "orinoco.c 0.13e (David Gibson and others)"; +static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION + " (David Gibson , " + "Pavel Roskin , et al)"; static int __init init_orinoco(void) {