diff -Nru pcmcia-cs-3.2.3/include/linux/kernel.h pcmcia-cs-3.2.3-new/include/linux/kernel.h --- pcmcia-cs-3.2.3/include/linux/kernel.h Wed Oct 16 02:17:22 2002 +++ pcmcia-cs-3.2.3-new/include/linux/kernel.h Sun Jan 5 12:47:27 2003 @@ -97,4 +97,8 @@ resetup_one_dev(dev, drive); } while (0); #endif +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + #endif /* _COMPAT_KERNEL_H */ diff -Nru pcmcia-cs-3.2.3/wireless/hermes.c pcmcia-cs-3.2.3-new/wireless/hermes.c --- pcmcia-cs-3.2.3/wireless/hermes.c Wed May 1 23:37:30 2002 +++ pcmcia-cs-3.2.3-new/wireless/hermes.c Sun Jan 5 11:03:57 2003 @@ -45,7 +45,6 @@ #include #include #include -#include #include #include #include @@ -53,7 +52,7 @@ #include "hermes.h" -static char version[] __initdata = "hermes.c: 5 Apr 2002 David Gibson "; +static char version[] __initdata = "hermes.c: 4 Dec 2002 David Gibson "; MODULE_DESCRIPTION("Low-level driver helper for Lucent Hermes chipset and Prism II HFA384x wireless MAC controller"); MODULE_AUTHOR("David Gibson "); #ifdef MODULE_LICENSE @@ -70,14 +69,14 @@ * Debugging helpers */ +#define IO_TYPE(hw) ((hw)->io_space ? "IO " : "MEM ") +#define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ %s0x%x: " , IO_TYPE(hw), hw->iobase); \ + printk(stuff);} while (0) + #undef HERMES_DEBUG #ifdef HERMES_DEBUG - #include -#define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ 0x%x: " , hw->iobase); \ - printk(stuff);} while (0) - #define DEBUG(lvl, stuff...) if ( (lvl) <= HERMES_DEBUG) DMSG(stuff) #else /* ! HERMES_DEBUG */ @@ -86,7 +85,6 @@ #endif /* ! HERMES_DEBUG */ -#define IO_TYPE(hw) ((hw)->io_space ? "IO " : "MEM ") /* * Internal functions @@ -111,7 +109,6 @@ udelay(1); reg = hermes_read_regn(hw, CMD); } - DEBUG(3, "hermes_issue_cmd: did %d retries.\n", CMD_BUSY_TIMEOUT-k); if (reg & HERMES_CMD_BUSY) { return -EBUSY; } @@ -143,7 +140,7 @@ #endif } -int hermes_reset(hermes_t *hw) +int hermes_init(hermes_t *hw) { u16 status, reg; int err = 0; @@ -195,8 +192,6 @@ reg = hermes_read_regn(hw, EVSTAT); } - DEBUG(0, "Reset completed in %d iterations\n", CMD_INIT_TIMEOUT - k); - hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC); if (! hermes_present(hw)) { @@ -303,9 +298,6 @@ err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, NULL); if (err) { - printk(KERN_WARNING "hermes @ %s0x%lx: " - "Frame allocation command failed (0x%X).\n", - IO_TYPE(hw), hw->iobase, err); return err; } @@ -393,12 +385,10 @@ } if (reg & HERMES_OFFSET_BUSY) { - DEBUG(1,"hermes_bap_seek: timeout\n"); return -ETIMEDOUT; } if (reg & HERMES_OFFSET_ERR) { - DEBUG(1,"hermes_bap_seek: BAP error\n"); return -EIO; } @@ -412,7 +402,7 @@ * * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware */ -int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, +int hermes_bap_pread(hermes_t *hw, int bap, void *buf, unsigned len, u16 id, u16 offset) { int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; @@ -438,7 +428,7 @@ * * Returns: < 0 on internal failure (errno), 0 on success, > 0 on error from firmware */ -int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, +int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, unsigned len, u16 id, u16 offset) { int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; @@ -466,12 +456,13 @@ * practice. * * Callable from user or bh context. */ -int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize, +int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned bufsize, u16 *length, void *buf) { int err = 0; int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; u16 rlength, rtype; + unsigned nwords; if ( (bufsize < 0) || (bufsize % 2) ) return -EINVAL; @@ -500,10 +491,9 @@ "(rid=0x%04x, len=0x%04x)\n", IO_TYPE(hw), hw->iobase, HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength); - - /* FIXME: we should read the min of the requested length and - the actual record length */ - hermes_read_words(hw, dreg, buf, bufsize / 2); + + nwords = min((unsigned)rlength - 1, bufsize / 2); + hermes_read_words(hw, dreg, buf, nwords); out: return err; @@ -514,10 +504,10 @@ { int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int err = 0; - int count; - - DEBUG(3, "write_ltv(): bap=%d rid=0x%04x length=%d (value=0x%04x)\n", - bap, rid, length, * ((u16 *)value)); + unsigned count; + + if (length == 0) + return -EINVAL; err = hermes_bap_seek(hw, bap, rid, 0); if (err) @@ -538,7 +528,7 @@ } EXPORT_SYMBOL(hermes_struct_init); -EXPORT_SYMBOL(hermes_reset); +EXPORT_SYMBOL(hermes_init); EXPORT_SYMBOL(hermes_docmd_wait); EXPORT_SYMBOL(hermes_allocate); diff -Nru pcmcia-cs-3.2.3/wireless/hermes.h pcmcia-cs-3.2.3-new/wireless/hermes.h --- pcmcia-cs-3.2.3/wireless/hermes.h Wed May 1 23:32:39 2002 +++ pcmcia-cs-3.2.3-new/wireless/hermes.h Sun Jan 5 11:04:01 2003 @@ -171,6 +171,7 @@ #define HERMES_RXSTAT_BADCRC (0x0001) #define HERMES_RXSTAT_UNDECRYPTABLE (0x0002) #define HERMES_RXSTAT_MACPORT (0x0700) +#define HERMES_RXSTAT_PCF (0x1000) /* Frame was received in CF period */ #define HERMES_RXSTAT_MSGTYPE (0xE000) #define HERMES_RXSTAT_1042 (0x2000) /* RFC-1042 frame */ #define HERMES_RXSTAT_TUNNEL (0x4000) /* bridge-tunnel encoded frame */ @@ -250,6 +251,18 @@ struct hermes_scan_apinfo aps[35]; /* Scan result */ } __attribute__ ((packed)); +#define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000) +#define HERMES_LINKSTATUS_CONNECTED (0x0001) +#define HERMES_LINKSTATUS_DISCONNECTED (0x0002) +#define HERMES_LINKSTATUS_AP_CHANGE (0x0003) +#define HERMES_LINKSTATUS_AP_OUT_OF_RANGE (0x0004) +#define HERMES_LINKSTATUS_AP_IN_RANGE (0x0005) +#define HERMES_LINKSTATUS_ASSOC_FAILED (0x0006) + +struct hermes_linkstatus { + u16 linkstatus; /* Link status */ +} __attribute__ ((packed)); + // #define HERMES_DEBUG_BUFFER 1 #define HERMES_DEBUG_BUFSIZE 4096 struct hermes_debug_entry { @@ -299,15 +312,15 @@ /* Function prototypes */ void hermes_struct_init(hermes_t *hw, ulong address, int io_space, int reg_spacing); -int hermes_reset(hermes_t *hw); +int hermes_init(hermes_t *hw); int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp); int hermes_allocate(hermes_t *hw, u16 size, u16 *fid); -int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, +int hermes_bap_pread(hermes_t *hw, int bap, void *buf, unsigned len, u16 id, u16 offset); -int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, +int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, unsigned len, u16 id, u16 offset); -int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int buflen, +int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, unsigned buflen, u16 *length, void *buf); int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, u16 length, const void *value); @@ -319,12 +332,6 @@ return hermes_read_regn(hw, SWSUPPORT0) == HERMES_MAGIC; } -static inline void hermes_enable_interrupt(hermes_t *hw, u16 events) -{ - hw->inten |= events; - hermes_write_regn(hw, INTEN, hw->inten); -} - static inline void hermes_set_irqmask(hermes_t *hw, u16 events) { hw->inten = events; @@ -350,11 +357,11 @@ return hermes_docmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL); } -#define HERMES_BYTES_TO_RECLEN(n) ( ((n) % 2) ? (((n)+1)/2)+1 : ((n)/2)+1 ) +#define HERMES_BYTES_TO_RECLEN(n) ( (((n)+1)/2) + 1 ) #define HERMES_RECLEN_TO_BYTES(n) ( ((n)-1) * 2 ) /* Note that for the next two, the count is in 16-bit words, not bytes */ -static inline void hermes_read_words(struct hermes *hw, int off, void *buf, int count) +static inline void hermes_read_words(struct hermes *hw, int off, void *buf, unsigned count) { off = off << hw->reg_spacing;; @@ -364,15 +371,17 @@ int i; u16 *p; - /* This need to *not* byteswap (like insw()) but - * readw() does byteswap hence the conversion */ + /* This needs to *not* byteswap (like insw()) but + * readw() does byteswap hence the conversion. I hope + * gcc is smart enough to fold away the two swaps on + * big-endian platforms. */ for (i = 0, p = buf; i < count; i++) { *p++ = cpu_to_le16(readw(hw->iobase + off)); } } } -static inline void hermes_write_words(struct hermes *hw, int off, const void *buf, int count) +static inline void hermes_write_words(struct hermes *hw, int off, const void *buf, unsigned count) { off = off << hw->reg_spacing;; @@ -382,8 +391,10 @@ int i; const u16 *p; - /* This need to *not* byteswap (like outsw()) but - * writew() does byteswap hence the conversion */ + /* This needs to *not* byteswap (like outsw()) but + * writew() does byteswap hence the conversion. I + * hope gcc is smart enough to fold away the two swaps + * on big-endian platforms. */ for (i = 0, p = buf; i < count; i++) { writew(le16_to_cpu(*p++), hw->iobase + off); } diff -Nru pcmcia-cs-3.2.3/wireless/ieee802_11.h pcmcia-cs-3.2.3-new/wireless/ieee802_11.h --- pcmcia-cs-3.2.3/wireless/ieee802_11.h Wed May 1 23:33:48 2002 +++ pcmcia-cs-3.2.3-new/wireless/ieee802_11.h Sun Jan 5 11:04:01 2003 @@ -2,9 +2,13 @@ #define _IEEE802_11_H #define IEEE802_11_DATA_LEN 2304 -/* Actually, the standard seems to be inconsistent about what the - maximum frame size really is. S6.2.1.1.2 says 2304 octets, but the - figure in section 7.1.2 says 2312 octects. */ +/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section + 6.2.1.1.2. + + The figure in section 7.1.2 suggests a body size of up to 2312 + bytes is allowed, which is a bit confusing, I suspect this + represents the 2304 bytes of real data, plus a possible 8 bytes of + WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ #define IEEE802_11_HLEN 30 #define IEEE802_11_FRAME_LEN (IEEE802_11_DATA_LEN + IEEE802_11_HLEN) diff -Nru pcmcia-cs-3.2.3/wireless/orinoco.c pcmcia-cs-3.2.3-new/wireless/orinoco.c --- pcmcia-cs-3.2.3/wireless/orinoco.c Thu Aug 8 02:43:30 2002 +++ pcmcia-cs-3.2.3-new/wireless/orinoco.c Sun Jan 5 15:45:33 2003 @@ -1,4 +1,4 @@ -/* orinoco.c 0.11b - (formerly known as dldwd_cs.c and orinoco_cs.c) +/* orinoco.c 0.13b - (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. @@ -240,7 +240,7 @@ * was never very useful. * o Make Rx errors less noisy. * - * v0.10 -> v0.11 - 5 Apr Mar 2002 - David Gibson + * v0.10 -> v0.11 - 5 Apr 2002 - David Gibson * o Laid the groundwork in hermes.[ch] for devices which map * into PCI memory space rather than IO space. * o Fixed bug in multicast handling (cleared multicast list when @@ -256,7 +256,7 @@ * o Fixes for recent Symbol firmwares which lack AP density * (Pavel Roskin). * - * v0.11 -> v0.11b - 29 Apr 2002 - David Gibson + * v0.11 -> v0.11a - 29 Apr 2002 - David Gibson * o Handle different register spacing, necessary for Prism 2.5 * PCI adaptors (Steve Hill). * o Cleaned up initialization of card structures in orinoco_cs @@ -279,44 +279,96 @@ * o Fixed multiple bad kfree() bugs introduced by the * alloc_orinocodev() changes. * + * v0.11b -> v0.12 - 19 Jun 2002 - David Gibson + * o Support changing the MAC address. + * o Correct display of Intersil firmware revision numbers. + * o Entirely revised locking scheme. Should be both simpler and + * better. + * o Merged some common code in orinoco_plx, orinoco_pci and + * airport by creating orinoco_default_{open,stop,reset}() + * which are used as the dev->open, dev->stop, priv->reset + * callbacks if none are specified when alloc_orinocodev() is + * called. + * o Removed orinoco_plx_interupt() and orinoco_pci_interrupt(). + * They didn't do anything. + * + * v0.12 -> v0.12a - 4 Jul 2002 - David Gibson + * o Some rearrangement of code. + * o Numerous fixups to locking and rest handling, particularly + * for PCMCIA. + * o This allows open and stop net_device methods to be in + * orinoco.c now, rather than in the init modules. + * o In orinoco_cs.c link->priv now points to the struct + * net_device not to the struct orinoco_private. + * o Added a check for undersized SNAP frames, which could cause + * crashes. + * + * v0.12a -> v0.12b - 11 Jul 2002 - David Gibson + * o Fix hw->num_init testing code, so num_init is actually + * incremented. + * o Fix very stupid bug in orinoco_cs which broke compile with + * CONFIG_SMP. + * o Squashed a warning. + * + * v0.12b -> v0.12c - 26 Jul 2002 - David Gibson + * o Change to C9X style designated initializers. + * o Add support for 3Com AirConnect PCI. + * o No longer ignore the hard_reset argument to + * alloc_orinocodev(). Oops. + * + * v0.12c -> v0.13beta1 - 13 Sep 2002 - David Gibson + * o Revert the broken 0.12* locking scheme and go to a new yet + * simpler scheme. + * o Do firmware resets only in orinoco_init() and when waking + * the card from hard sleep. + * + * v0.13beta1 -> v0.13 - 27 Sep 2002 - David Gibson + * o Re-introduced full resets (via schedule_task()) on Tx + * timeout. + * + * v0.13 -> v0.13a - 30 Sep 2002 - David Gibson + * o Minor cleanups to info frame handling. Add basic support + * for linkstatus info frames. + * o Include required kernel headers in orinoco.h, to avoid + * compile problems. + * + * v0.13a -> v0.13b - ???? - David Gibson + * o Implemented hard reset for Airport cards + * o Experimental suspend/resume implementation for orinoco_pci + * o Abolished /proc debugging support, replaced with a debugging + * iwpriv. Now it's ugly and simple instead of ugly and complex. + * o Bugfix in hermes.c if the firmware returned a record length + * of 0, we could go clobbering memory. + * o Bugfix in orinoco_stop() - it used to fail if hw_unavailable + * was set, which was usually true on PCMCIA hot removes. + * * TODO - * o New wireless extensions API + * o New wireless extensions API (patch forthcoming from Moustafa + * Youssef). * o Handle de-encapsulation within network layer, provide 802.11 - * headers + * headers (patch from Thomas 'Dent' Mirlacher) * o Fix possible races in SPY handling. * o Disconnect wireless extensions from fundamental configuration. - * - * o Convert /proc debugging stuff to seqfile - * o Use multiple Tx buffers - */ -/* Notes on locking: - * - * The basic principle of operation is that everything except the - * interrupt handler is serialized through a single spinlock in the - * struct orinoco_private structure, using orinoco_lock() and - * orinoco_unlock() (which in turn use spin_lock_bh() and - * spin_unlock_bh()). - * - * The kernel's IRQ handling stuff ensures that the interrupt handler - * does not re-enter itself. The interrupt handler is written such - * that everything it does is safe without a lock: chiefly this means - * that the Rx path uses one of the Hermes chipset's BAPs while - * everything else uses the other. - * - * Actually, strictly speaking, the updating of the statistics from - * the interrupt handler isn't safe without a lock. However the worst - * that can happen is that we perturb the packet/byte counts slightly. - * We could fix this to use atomic types, but it's probably not worth - * it. - * - * The big exception is that that we don't want the irq handler - * running when we actually reset or shut down the card, because - * strange things might happen (probably the worst would be one packet - * of garbage, but you can't be too careful). For this we use - * __orinoco_stop_irqs() which will set a flag to disable the interrupt - * handler, and wait for any outstanding instances of the handler to - * complete. THIS WILL LOSE INTERRUPTS! so it shouldn't be used except - * for resets, where losing a few interrupts is acceptable. */ + * o (maybe) Software WEP support (patch from Stano Meduna). + * o (maybe) Use multiple Tx buffers - driver handling queue + * rather than firmware. */ + +/* Locking and synchronization: + * + * The basic principle is that everything is serialized through a + * single spinlock, priv->lock. The lock is used in user, bh and irq + * context, so when taken outside hardirq context it should always be + * taken with interrupts disabled. The lock protects both the + * hardware and the struct orinoco_private. + * + * Another flag, priv->hw_unavailable indicates that the hardware is + * unavailable for an extended period of time (e.g. suspended, or in + * the middle of a hard reset). This flag is protected by the + * spinlock. All code which touches the hardware should check the + * flag after taking the lock, and if it is set, give up on whatever + * they are doing and drop the lock again. The orinoco_lock() + * function handles this (it unlocks and returns -EBUSY if + * hw_unavailable is true). */ #include @@ -332,32 +384,20 @@ #include #include #include -#include -#include #include #include #include #include -#include #include "hermes.h" #include "hermes_rid.h" #include "orinoco.h" #include "ieee802_11.h" -/* 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) -#else -#define SPY_NUMBER(priv) 0 -#endif /* WIRELESS_SPY */ +/********************************************************************/ +/* Module information */ +/********************************************************************/ -static char version[] __initdata = "orinoco.c 0.11b (David Gibson and others)"; MODULE_AUTHOR("David Gibson "); MODULE_DESCRIPTION("Driver for Lucent Orinoco, Prism II based and similar wireless cards"); #ifdef MODULE_LICENSE @@ -371,6 +411,29 @@ EXPORT_SYMBOL(orinoco_debug); #endif +/********************************************************************/ +/* Compile time configuration and compatibility stuff */ +/********************************************************************/ + +/* Wireless extensions backwards compatibility */ +#ifndef SIOCIWFIRSTPRIV +#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE +#endif /* SIOCIWFIRSTPRIV */ +#ifndef SIOCIWLASTPRIV +#define SIOCIWLASTPRIV SIOCDEVPRIVATE+0xF +#endif /* SIOCIWLASTPRIV */ + +/* We do this this way to avoid ifdefs in the actual code */ +#ifdef WIRELESS_SPY +#define SPY_NUMBER(priv) (priv->spy_number) +#else +#define SPY_NUMBER(priv) 0 +#endif /* WIRELESS_SPY */ + +/********************************************************************/ +/* Internal constants */ +/********************************************************************/ + #define ORINOCO_MIN_MTU 256 #define ORINOCO_MAX_MTU (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD) @@ -379,13 +442,15 @@ #define IRQ_BAP 1 #define MAX_IRQLOOPS_PER_IRQ 10 #define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of how many events the - device can legitimately generate */ + device could legitimately generate */ #define SMALL_KEY_SIZE 5 #define LARGE_KEY_SIZE 13 #define TX_NICBUF_SIZE_BUG 1585 /* Bug in Symbol firmware */ #define DUMMY_FID 0xFFFF +#define 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) @@ -403,7 +468,7 @@ /* This tables gives the actual meanings of the bitrate IDs returned by the firmware. */ struct { - int bitrate; /* in 100s of kilbits */ + int bitrate; /* in 100s of kilobits */ int automatic; u16 agere_txratectrl; u16 intersil_txratectrl; @@ -419,6 +484,10 @@ }; #define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0])) +/********************************************************************/ +/* Data types */ +/********************************************************************/ + struct header_struct { /* 802.3 */ u8 dest[ETH_ALEN]; @@ -440,27 +509,29 @@ #define TX_TIMEOUT (HZ) /* 1 second timeout */ -/* - * Function prototypes - */ +/********************************************************************/ +/* Function prototypes */ +/********************************************************************/ static void orinoco_stat_gather(struct net_device *dev, - struct sk_buff *skb, - struct hermes_rx_descriptor *desc); + 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_program_rids(struct orinoco_private *priv); + 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]); + 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); + s32 *rates, int max); static void __orinoco_set_multicast_list(struct net_device *dev); static void orinoco_tx_timeout(struct net_device *dev); @@ -475,6 +546,7 @@ static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw); static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw); +/* ioctl() routines */ static int orinoco_ioctl_getiwrange(struct net_device *dev, struct iw_point *rrq); static int orinoco_ioctl_setiwencode(struct net_device *dev, struct iw_point *erq); static int orinoco_ioctl_getiwencode(struct net_device *dev, struct iw_point *erq); @@ -495,171 +567,168 @@ static int orinoco_ioctl_setport3(struct net_device *dev, struct iwreq *wrq); static int orinoco_ioctl_getport3(struct net_device *dev, struct iwreq *wrq); -/* /proc debugging stuff */ -static int orinoco_proc_init(void); -static void orinoco_proc_cleanup(void); - -/* - * Inline functions - */ +static int orinoco_debug_dump_recs(struct orinoco_private *priv); -#ifndef spin_lock_bh -static long __flags; -#define spin_lock_bh(lock) spin_lock_irqsave(lock, __flags) -#define spin_unlock_bh(lock) spin_unlock_irqrestore(lock, __flags) -#endif +/********************************************************************/ +/* Function prototypes */ +/********************************************************************/ -static inline void -orinoco_lock(struct orinoco_private *priv) +int __orinoco_up(struct net_device *dev) { - spin_lock_bh(&priv->lock); -} + struct orinoco_private *priv = dev->priv; + struct hermes *hw = &priv->hw; + int err; -static inline void -orinoco_unlock(struct orinoco_private *priv) -{ - spin_unlock_bh(&priv->lock); -} + err = __orinoco_program_rids(priv); + if (err) { + printk(KERN_ERR "%s: Error %d configuring card\n", + dev->name, err); + return err; + } -static inline int -orinoco_irqs_allowed(struct orinoco_private *priv) -{ - return test_bit(ORINOCO_STATE_DOIRQ, &priv->state); -} + /* 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; + } -static inline void -__orinoco_stop_irqs(struct orinoco_private *priv) -{ - hermes_t *hw = &priv->hw; + netif_device_attach(dev); - hermes_set_irqmask(hw, 0); - clear_bit(ORINOCO_STATE_DOIRQ, &priv->state); - while (test_bit(ORINOCO_STATE_INIRQ, &priv->state)) - ; + return 0; } -static inline void -__orinoco_start_irqs(struct orinoco_private *priv, u16 irqmask) +int __orinoco_down(struct net_device *dev) { - hermes_t *hw = &priv->hw; + struct orinoco_private *priv = dev->priv; + struct hermes *hw = &priv->hw; + int err; - TRACE_ENTER(priv->ndev->name); + netif_device_detach(dev); - __cli(); /* FIXME: is this necessary? */ - set_bit(ORINOCO_STATE_DOIRQ, &priv->state); - hermes_set_irqmask(hw, irqmask); - __sti(); + err = hermes_disable_port(hw, 0); + if (err) { + printk(KERN_ERR "%s: Error %d disabling MAC port\n", + dev->name, err); + return err; + } + hermes_set_irqmask(hw, 0); + hermes_write_regn(hw, EVACK, 0xffff); - TRACE_EXIT(priv->ndev->name); + return 0; } -static inline void -set_port_type(struct orinoco_private *priv) +int orinoco_reinit_firmware(struct net_device *dev) { - switch (priv->iw_mode) { - case IW_MODE_INFRA: - priv->port_type = 1; - priv->allow_ibss = 0; - break; - case IW_MODE_ADHOC: - if (priv->prefer_port3) { - priv->port_type = 3; - priv->allow_ibss = 0; - } else { - priv->port_type = priv->ibss_port; - priv->allow_ibss = 1; - } - break; - default: - printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n", - priv->ndev->name); + struct orinoco_private *priv = dev->priv; + 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"); } -} -static inline int -is_snap(struct header_struct *hdr) -{ - return (hdr->dsap == 0xAA) && (hdr->ssap == 0xAA) && (hdr->ctrl == 0x3); + return err; } -static void -orinoco_set_multicast_list(struct net_device *dev) +static int orinoco_open(struct net_device *dev) { struct orinoco_private *priv = dev->priv; + unsigned long flags; + int err; - orinoco_lock(priv); - __orinoco_set_multicast_list(dev); - orinoco_unlock(priv); -} + err = orinoco_lock(priv, &flags); + if (err) + return err; -/* - * Hardware control routines - */ + priv->open = 1; -void -orinoco_shutdown(struct orinoco_private *priv) + err = __orinoco_up(dev); + + orinoco_unlock(priv, &flags); + + return err; +} + +static int orinoco_stop(struct net_device *dev) { + struct orinoco_private *priv = dev->priv; int err = 0; - TRACE_ENTER(priv->ndev->name); + /* We mustn't use orinoco_lock() here, because we need to be + able to close the interface, even if hw_unavailable is set + (e.g. as we're released after a PC Card removal) */ + spin_lock_irq(&priv->lock); - orinoco_lock(priv); - __orinoco_stop_irqs(priv); + priv->open = 0; - err = hermes_reset(&priv->hw); - if (err && err != -ENODEV) /* If the card is gone, we don't care about shutting it down */ - printk(KERN_ERR "%s: Error %d shutting down Hermes chipset\n", priv->ndev->name, err); + if (! priv->hw_unavailable) + err = __orinoco_down(dev); - orinoco_unlock(priv); + spin_unlock_irq(&priv->lock); - TRACE_EXIT(priv->ndev->name); + return err; } -int -orinoco_reset(struct orinoco_private *priv) +static int __orinoco_program_rids(struct orinoco_private *priv) { struct net_device *dev = priv->ndev; hermes_t *hw = &priv->hw; - int err = 0; + int err; struct hermes_idstring idbuf; - TRACE_ENTER(priv->ndev->name); - - /* Stop other people bothering us */ - orinoco_lock(priv); - __orinoco_stop_irqs(priv); - - /* Check if we need a card reset */ - if (priv->hard_reset) - priv->hard_reset(priv); - - /* Do standard firmware reset if we can */ - err = hermes_reset(hw); - if (err) - goto out; - - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); - if (err == -EIO) { - /* Try workaround for old Symbol firmware bug */ - priv->nicbuf_size = TX_NICBUF_SIZE_BUG; - err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid); - if (err) - goto out; + /* 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; } - /* Now set up all the parameters on the card */ - /* Set up the link mode */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFPORTTYPE, priv->port_type); - if (err) - goto out; + 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->allow_ibss); - if (err) - goto out; - if((strlen(priv->desired_essid) == 0) && (priv->allow_ibss) + 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); @@ -676,13 +745,17 @@ err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNSSID, HERMES_BYTES_TO_RECLEN(strlen(priv->desired_essid)+2), &idbuf); - if (err) - goto out; + 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) - goto out; + 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)); @@ -690,26 +763,29 @@ err = hermes_write_ltv(hw, USER_BAP, HERMES_RID_CNFOWNNAME, HERMES_BYTES_TO_RECLEN(strlen(priv->nick)+2), &idbuf); - if (err) - goto out; - - /* Set the channel/frequency */ - err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFOWNCHANNEL, priv->channel); - if (err) - goto out; + 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) + 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) - goto out; + 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) @@ -720,35 +796,52 @@ err = hermes_write_wordrec(hw, USER_BAP, HERMES_RID_CNFFRAGMENTATIONTHRESHOLD, priv->frag_thresh); - if (err) - goto out; + 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) - goto out; + 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) - goto out; + 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) - goto out; + 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) - goto out; + 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) - goto out; + if (err) { + printk(KERN_ERR "%s: Error %d setting up PM\n", + dev->name, err); + return err; + } } /* Set preamble - only for Symbol so far... */ @@ -757,8 +850,9 @@ HERMES_RID_CNFPREAMBLE_SYMBOL, priv->preamble); if (err) { - printk(KERN_WARNING "%s: Can't set preamble!\n", dev->name); - goto out; + printk(KERN_ERR "%s: Error %d setting preamble\n", + dev->name, err); + return err; } } @@ -766,41 +860,161 @@ if (priv->has_wep) { err = __orinoco_hw_setup_wep(priv); if (err) { - printk(KERN_ERR "%s: Error %d activating WEP.\n", + printk(KERN_ERR "%s: Error %d activating WEP\n", dev->name, err); - goto out; + return err; } } /* Set promiscuity / multicast*/ priv->promiscuous = 0; priv->mc_count = 0; - __orinoco_set_multicast_list(dev); - - __orinoco_start_irqs(priv, HERMES_EV_RX | HERMES_EV_ALLOC | - HERMES_EV_TX | HERMES_EV_TXEXC | - HERMES_EV_WTERR | HERMES_EV_INFO | - HERMES_EV_INFDROP); + __orinoco_set_multicast_list(dev); /* FIXME: what about the xmit_lock */ - err = hermes_enable_port(hw, 0); + return 0; +} + +/* xyzzy */ +static int orinoco_reconfigure(struct orinoco_private *priv) +{ + struct hermes *hw = &priv->hw; + unsigned long flags; + int err = 0; + + orinoco_lock(priv, &flags); + + err = hermes_disable_port(hw, 0); + if (err) { + printk(KERN_ERR "%s: Unable to disable port in orinco_reconfigure()\n", + priv->ndev->name); + goto out; + } + + err = __orinoco_program_rids(priv); if (err) goto out; + err = hermes_enable_port(hw, 0); + if (err) { + printk(KERN_ERR "%s: Unable to enable port in orinco_reconfigure()\n", + priv->ndev->name); + goto out; + } + out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); + return err; - TRACE_EXIT(priv->ndev->name); +} - return err; +/* This must be called from user context, without locks held - use + * schedule_task() */ +static void orinoco_reset(struct net_device *dev) +{ + struct orinoco_private *priv = dev->priv; + int err; + unsigned long flags; + + printk(KERN_INFO "%s: orinoco_reset()\n", dev->name); + + err = orinoco_lock(priv, &flags); + if (err) + return; + + priv->hw_unavailable = 1; + orinoco_unlock(priv, &flags); + + if (priv->hard_reset) + err = (*priv->hard_reset)(priv); + if (err) { + printk(KERN_ERR "%s: orinoco_reset: Error %d performing hard reset\n", + dev->name, err); + /* FIXME: shutdown of some sort */ + return; + } + + err = orinoco_reinit_firmware(dev); + if (err) { + printk(KERN_ERR "%s: orinoco_reset: Error %d re-initializing firmware\n", + dev->name, err); + return; + } + + spin_lock_irqsave(&priv->lock, flags); + + priv->hw_unavailable = 0; + + 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; + + orinoco_unlock(priv, &flags); + + return; } +/********************************************************************/ +/* Internal helper functions */ +/********************************************************************/ + +static inline void +set_port_type(struct orinoco_private *priv) +{ + switch (priv->iw_mode) { + case IW_MODE_INFRA: + priv->port_type = 1; + priv->createibss = 0; + break; + case IW_MODE_ADHOC: + if (priv->prefer_port3) { + priv->port_type = 3; + priv->createibss = 0; + } else { + priv->port_type = priv->ibss_port; + priv->createibss = 1; + } + break; + default: + printk(KERN_ERR "%s: Invalid priv->iw_mode in set_port_type()\n", + priv->ndev->name); + } +} + +static inline int +is_snap(struct header_struct *hdr) +{ + return (hdr->dsap == 0xAA) && (hdr->ssap == 0xAA) && (hdr->ctrl == 0x3); +} + +static void +orinoco_set_multicast_list(struct net_device *dev) +{ + struct orinoco_private *priv = dev->priv; + unsigned long flags; + + if (orinoco_lock(priv, &flags) != 0) { + printk(KERN_DEBUG "%s: orinoco_set_multicast_list() " + "called when hw_unavailable\n", dev->name); + return; + } + + __orinoco_set_multicast_list(dev); + orinoco_unlock(priv, &flags); +} + +/********************************************************************/ +/* Hardware control functions */ +/********************************************************************/ + + static int __orinoco_hw_set_bitrate(struct orinoco_private *priv) { hermes_t *hw = &priv->hw; int err = 0; - TRACE_ENTER(priv->ndev->name); - if (priv->bitratemode >= BITRATE_TABLE_SIZE) { printk(KERN_ERR "%s: BUG: Invalid bitrate mode %d\n", priv->ndev->name, priv->bitratemode); @@ -823,8 +1037,6 @@ BUG(); } - TRACE_EXIT(priv->ndev->name); - return err; } @@ -836,8 +1048,6 @@ int master_wep_flag; int auth_flag; - TRACE_ENTER(priv->ndev->name); - switch (priv->firmware_type) { case FIRMWARE_TYPE_AGERE: /* Agere style WEP */ if (priv->wep_on) { @@ -931,8 +1141,6 @@ } } - TRACE_EXIT(priv->ndev->name); - return 0; } @@ -940,13 +1148,16 @@ { hermes_t *hw = &priv->hw; int err = 0; + unsigned long flags; - orinoco_lock(priv); + 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); + orinoco_unlock(priv, &flags); return err; } @@ -959,10 +1170,11 @@ struct hermes_idstring essidbuf; char *p = (char *)(&essidbuf.val); int len; + unsigned long flags; - TRACE_ENTER(priv->ndev->name); - - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if (strlen(priv->desired_essid) > 0) { /* We read the desired SSID from the hardware rather @@ -999,9 +1211,7 @@ buf[len] = '\0'; fail_unlock: - orinoco_unlock(priv); - - TRACE_EXIT(priv->ndev->name); + orinoco_unlock(priv, &flags); return err; } @@ -1013,13 +1223,22 @@ int err = 0; u16 channel; long freq = 0; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CURRENTCHANNEL, &channel); if (err) goto out; + /* 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) ) { struct net_device *dev = priv->ndev; @@ -1031,7 +1250,7 @@ freq = channel_frequency[channel-1] * 100000; out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); if (err > 0) err = -EBUSY; @@ -1047,11 +1266,15 @@ int err = 0; int num; int i; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); err = hermes_read_ltv(hw, USER_BAP, HERMES_RID_SUPPORTEDDATARATES, sizeof(list), NULL, &list); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); if (err) return err; @@ -1133,7 +1356,7 @@ /* * Interrupt handler */ -void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs) +void orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct orinoco_private *priv = (struct orinoco_private *) dev_id; hermes_t *hw = &priv->hw; @@ -1145,38 +1368,26 @@ * 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 (test_and_set_bit(ORINOCO_STATE_INIRQ, &priv->state)) - BUG(); - - if (! orinoco_irqs_allowed(priv)) { - clear_bit(ORINOCO_STATE_INIRQ, &priv->state); + if (orinoco_lock(priv, &flags) != 0) { + /* If hw is unavailable */ return; } - DEBUG(3, "%s: orinoco_interrupt()\n", priv->ndev->name); - evstat = hermes_read_regn(hw, EVSTAT); events = evstat & hw->inten; -/* if (! events) { */ -/* printk(KERN_WARNING "%s: Null event\n", dev->name); */ -/* } */ - if (jiffies != last_irq_jiffy) loops_this_jiffy = 0; last_irq_jiffy = jiffies; while (events && count--) { - DEBUG(3, "__orinoco_interrupt(): count=%d EVSTAT=0x%04x\n", - count, evstat); - if (++loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY) { printk(KERN_CRIT "%s: IRQ handler is looping too \ much! Shutting down.\n", dev->name); /* Perform an emergency shutdown */ - clear_bit(ORINOCO_STATE_DOIRQ, &priv->state); hermes_set_irqmask(hw, 0); break; } @@ -1210,7 +1421,7 @@ events = evstat & hw->inten; }; - clear_bit(ORINOCO_STATE_INIRQ, &priv->state); + orinoco_unlock(priv, &flags); } static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw) @@ -1222,8 +1433,8 @@ { /* This seems to happen a fair bit under load, but ignoring it seems to work fine...*/ - DEBUG(1, "%s: MAC controller error (WTERR). Ignoring.\n", - priv->ndev->name); + printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n", + priv->ndev->name); } static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw) @@ -1239,6 +1450,7 @@ u16 len; u16 type; } __attribute__ ((packed)) info; + int len, type; int err; /* This is an answer to an INQUIRE command that we did earlier, @@ -1246,8 +1458,6 @@ * The controller return to us a pseudo frame containing * the information in question - Jean II */ infofid = hermes_read_regn(hw, INFOFID); - DEBUG(3, "%s: __orinoco_ev_info(): INFOFID=0x%04x\n", dev->name, - infofid); /* Read the info frame header - don't try too hard */ err = hermes_bap_pread(hw, IRQ_BAP, &info, sizeof(info), @@ -1258,25 +1468,29 @@ return; } - switch (le16_to_cpu(info.type)) { + len = HERMES_RECLEN_TO_BYTES(le16_to_cpu(info.len)); + type = le16_to_cpu(info.type); + + switch (type) { case HERMES_INQ_TALLIES: { struct hermes_tallies_frame tallies; struct iw_statistics *wstats = &priv->wstats; - int len = le16_to_cpu(info.len) - 1; - if (len > (sizeof(tallies) / 2)) { - DEBUG(1, "%s: tallies frame too long.\n", dev->name); - len = sizeof(tallies) / 2; + if (len > sizeof(tallies)) { + printk(KERN_WARNING "%s: Tallies frame too long (%d bytes)\n", + dev->name, len); + len = sizeof(tallies); } /* Read directly the data (no seek) */ - hermes_read_words(hw, HERMES_DATA1, (void *) &tallies, len); + hermes_read_words(hw, HERMES_DATA1, (void *) &tallies, + len / 2); /* FIXME: blech! */ /* Increment our various counters */ /* wstats->discard.nwid - no wrong BSSID stuff */ wstats->discard.code += le16_to_cpu(tallies.RxWEPUndecryptable); - if (len == (sizeof(tallies) / 2)) + if (len == sizeof(tallies)) wstats->discard.code += le16_to_cpu(tallies.RxDiscards_WEPICVError) + le16_to_cpu(tallies.RxDiscards_WEPExcluded); @@ -1288,17 +1502,57 @@ wstats->discard.retries += le16_to_cpu(tallies.TxRetryLimitExceeded); /* wstats->miss.beacon - no match */ -#if ORINOCO_DEBUG > 3 - /* Hack for debugging - should not be taken as an example */ - wstats->discard.nwid += le16_to_cpu(tallies.TxUnicastFrames); - wstats->miss.beacon += le16_to_cpu(tallies.RxUnicastFrames); -#endif #endif /* WIRELESS_EXT > 11 */ } break; + case HERMES_INQ_LINKSTATUS: { + struct hermes_linkstatus linkstatus; + u16 newstatus; + const char *s; + + if (len != sizeof(linkstatus)) { + printk(KERN_WARNING "%s: Unexpected size for linkstatus frame (%d bytes)\n", + dev->name, len); + break; + } + + hermes_read_words(hw, HERMES_DATA1, (void *) &linkstatus, + len / 2); + newstatus = le16_to_cpu(linkstatus.linkstatus); + + switch (newstatus) { + 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, newstatus); + } + break; default: - DEBUG(1, "%s: Unknown information frame received (type %04x).\n", - priv->ndev->name, le16_to_cpu(info.type)); + printk(KERN_DEBUG "%s: Unknown information frame received (type %04x).\n", + dev->name, type); /* We don't actually do anything about it */ break; } @@ -1319,7 +1573,6 @@ int err; rxfid = hermes_read_regn(hw, RXFID); - DEBUG(3, "__orinoco_ev_rx(): RXFID=0x%04x\n", rxfid); err = hermes_bap_pread(hw, IRQ_BAP, &desc, sizeof(desc), rxfid, 0); @@ -1360,11 +1613,11 @@ length = ntohs(hdr.len); /* Sanity checks */ - if (length < sizeof(struct header_struct)) { - printk(KERN_WARNING "%s: Undersized frame received (%d bytes)\n", - dev->name, length); - stats->rx_length_errors++; - stats->rx_errors++; + 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) { @@ -1384,7 +1637,6 @@ if (!skb) { printk(KERN_WARNING "%s: Can't allocate skb for Rx\n", dev->name); - stats->rx_dropped++; goto drop; } @@ -1394,8 +1646,7 @@ * 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 (note : only 3 bytes out of 6). - */ + * So, check ourselves */ if(((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) || ((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) || is_snap(&hdr)) { @@ -1409,6 +1660,12 @@ goto drop; } + if (length < ENCAPS_OVERHEAD) { /* No room for full LLC+SNAP */ + stats->rx_length_errors++; + stats->rx_dropped++; + goto drop; + } + /* Remove SNAP header, reconstruct EthernetII frame */ data_len = length - ENCAPS_OVERHEAD; data_off = HERMES_802_3_OFFSET + sizeof(hdr); @@ -1431,7 +1688,7 @@ err = hermes_bap_pread(hw, IRQ_BAP, p, RUP_EVEN(data_len), rxfid, data_off); if (err) { - printk(KERN_ERR "%s: error %d reading frame header. " + printk(KERN_ERR "%s: error %d reading frame. " "Frame dropped.\n", dev->name, err); stats->rx_errors++; goto drop; @@ -1453,6 +1710,8 @@ return; drop: + stats->rx_dropped++; + if (skb) dev_kfree_skb_irq(skb); return; @@ -1487,11 +1746,7 @@ static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw) { -/* struct net_device *dev = priv->ndev; */ struct net_device_stats *stats = &priv->stats; -/* u16 fid = hermes_read_regn(hw, TXCOMPLFID); */ - -/* DEBUG(2, "%s: Transmit completed (FID=%04X)\n", priv->ndev->name, fid); */ stats->tx_packets++; netif_wake_queue(priv->ndev); @@ -1504,8 +1759,6 @@ struct net_device *dev = priv->ndev; u16 fid = hermes_read_regn(hw, ALLOCFID); - DEBUG(3, "%s: Allocation complete FID=0x%04x\n", priv->ndev->name, fid); - if (fid != priv->txfid) { if (fid != DUMMY_FID) printk(KERN_WARNING "%s: Allocate event on unexpected fid (%04X)\n", @@ -1519,16 +1772,17 @@ } struct sta_id { - u16 id, vendor, major, minor; + u16 id, variant, major, minor; } __attribute__ ((packed)); static int determine_firmware_type(struct net_device *dev, struct sta_id *sta_id) { - u32 firmver = ((u32)sta_id->major << 16) | sta_id->minor; + /* FIXME: this is fundamentally broken */ + unsigned int firmver = ((u32)sta_id->major << 16) | sta_id->minor; - if (sta_id->vendor == 1) + if (sta_id->variant == 1) return FIRMWARE_TYPE_AGERE; - else if ((sta_id->vendor == 2) && + else if ((sta_id->variant == 2) && ((firmver == 0x10001) || (firmver == 0x20001))) return FIRMWARE_TYPE_SYMBOL; else @@ -1541,7 +1795,7 @@ hermes_t *hw = &priv->hw; int err; struct sta_id sta_id; - u32 firmver; + unsigned int firmver; char tmp[SYMBOL_MAX_VER_LEN+1]; /* Get the firmware version */ @@ -1552,14 +1806,12 @@ memset(&sta_id, 0, sizeof(sta_id)); } le16_to_cpus(&sta_id.id); - le16_to_cpus(&sta_id.vendor); + le16_to_cpus(&sta_id.variant); le16_to_cpus(&sta_id.major); le16_to_cpus(&sta_id.minor); - firmver = ((u32)sta_id.major << 16) | sta_id.minor; - printk(KERN_DEBUG "%s: Station identity %04x:%04x:%04x:%04x\n", - dev->name, sta_id.id, sta_id.vendor, + dev->name, sta_id.id, sta_id.variant, sta_id.major, sta_id.minor); if (! priv->firmware_type) @@ -1574,7 +1826,6 @@ priv->has_ibss_any = 0; priv->has_wep = 0; priv->has_big_wep = 0; - priv->broken_cor_reset = 0; /* Determine capabilities from the firmware version */ switch (priv->firmware_type) { @@ -1585,6 +1836,8 @@ "version %d.%02d\n", dev->name, sta_id.major, sta_id.minor); + firmver = ((unsigned long)sta_id.major << 16) | sta_id.minor; + priv->has_ibss = (firmver >= 0x60006); priv->has_ibss_any = (firmver >= 0x60010); priv->has_wep = (firmver >= 0x40020); @@ -1594,8 +1847,6 @@ priv->has_pm = (firmver >= 0x40020); /* Don't work in 7.52 ? */ priv->ibss_port = 1; - /* FIXME: Which firmware really do have a broken reset */ - priv->broken_cor_reset = (firmver < 0x60000); /* Tested with Agere firmware : * 1.16 ; 4.08 ; 4.52 ; 6.04 ; 6.16 ; 7.28 => Jean II * Tested CableTron firmware : 4.32 => Anton */ @@ -1647,18 +1898,21 @@ /* D-Link MAC : 00:40:05:* */ /* Addtron MAC : 00:90:D1:* */ printk(KERN_DEBUG "%s: Looks like an Intersil firmware " - "version %d.%02d\n", dev->name, - sta_id.major, sta_id.minor); + "version %d.%d.%d\n", dev->name, + sta_id.major, sta_id.minor, sta_id.variant); - priv->has_ibss = (firmver >= 0x00007); /* FIXME */ - priv->has_big_wep = priv->has_wep = (firmver >= 0x00008); - priv->has_pm = (firmver >= 0x00007); + firmver = ((unsigned long)sta_id.major << 16) | + ((unsigned long)sta_id.minor << 8) | sta_id.variant; - if (firmver >= 0x00008) + priv->has_ibss = (firmver >= 0x000700); /* FIXME */ + priv->has_big_wep = priv->has_wep = (firmver >= 0x000800); + priv->has_pm = (firmver >= 0x000700); + + if (firmver >= 0x000800) priv->ibss_port = 0; else { printk(KERN_NOTICE "%s: Intersil firmware earlier " - "than v0.08 - several features not supported\n", + "than v0.8.x - several features not supported\n", dev->name); priv->ibss_port = 1; } @@ -1682,16 +1936,16 @@ u16 reclen; int len; - TRACE_ENTER("orinoco"); - - orinoco_lock(priv); + TRACE_ENTER(dev->name); + /* No need to lock, the resetting flag is already set in + * alloc_orinocodev() */ priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN; - /* Do standard firmware reset */ - err = hermes_reset(hw); + /* Initialize the firmware */ + err = hermes_init(hw); if (err != 0) { - printk(KERN_ERR "%s: failed to reset hardware (err = %d)\n", + printk(KERN_ERR "%s: failed to initialize firmware (err = %d)\n", dev->name, err); goto out; } @@ -1820,20 +2074,38 @@ priv->wep_on = 0; priv->tx_key = 0; - printk(KERN_DEBUG "%s: ready\n", dev->name); + priv->hw_unavailable = 0; - out: - orinoco_unlock(priv); + 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; + } - TRACE_EXIT("orinoco"); + 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 = (struct orinoco_private *)dev->priv; + struct orinoco_private *priv = dev->priv; return &priv->stats; } @@ -1841,15 +2113,22 @@ struct iw_statistics * orinoco_get_wireless_stats(struct net_device *dev) { - struct orinoco_private *priv = (struct orinoco_private *)dev->priv; + struct orinoco_private *priv = dev->priv; hermes_t *hw = &priv->hw; struct iw_statistics *wstats = &priv->wstats; int err = 0; + unsigned long flags; - if (! netif_device_present(dev)) - return NULL; /* FIXME: We may be able to do better than this */ + 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? */ + } - orinoco_lock(priv); + 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)); @@ -1869,9 +2148,6 @@ err = HERMES_READ_RECORD(hw, USER_BAP, HERMES_RID_COMMSQUALITY, &cq); - DEBUG(3, "%s: Global stats = %X-%X-%X\n", dev->name, - cq.qual, cq.signal, cq.noise); - 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; @@ -1881,11 +2157,11 @@ /* 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: Hmm.. seems a bit ugly, I wonder if there's a way to - do better - dgibson */ + /* 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); + orinoco_unlock(priv, &flags); if (err) return NULL; @@ -1945,18 +2221,34 @@ struct ethhdr *eh; int len, data_len, data_off; struct hermes_tx_descriptor desc; + unsigned long flags; + + TRACE_ENTER(dev->name); if (! netif_running(dev)) { printk(KERN_ERR "%s: Tx on stopped device!\n", dev->name); + TRACE_EXIT(dev->name); return 1; - } + if (netif_queue_stopped(dev)) { + printk(KERN_DEBUG "%s: Tx while transmitter busy!\n", + dev->name); + TRACE_EXIT(dev->name); + return 1; + } + tx_timeout_check(dev, orinoco_tx_timeout); skb_tx_check(dev, skb); - - orinoco_lock(priv); + + 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; + } /* Length of the packet body */ /* FIXME: what if the skb is smaller than this? */ @@ -2025,14 +2317,17 @@ dev->trans_start = jiffies; add_tx_bytes(stats, data_off + data_len); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); DEV_KFREE_SKB(skb); + TRACE_EXIT(dev->name); + return 0; - fail: +fail: + TRACE_EXIT(dev->name); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2042,27 +2337,21 @@ struct orinoco_private *priv = (struct orinoco_private *)dev->priv; struct net_device_stats *stats = &priv->stats; struct hermes *hw = &priv->hw; - int err = 0; - printk(KERN_WARNING "%s: Tx timeout! Resetting card. ALLOCFID=%04x, TXCOMPLFID=%04x, EVSTAT=%04x\n", dev->name, hermes_read_regn(hw, ALLOCFID), hermes_read_regn(hw, TXCOMPLFID), hermes_read_regn(hw, EVSTAT)); + 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)); stats->tx_errors++; - err = orinoco_reset(priv); - if (err) - printk(KERN_ERR "%s: Error %d resetting card on Tx timeout!\n", - dev->name, err); - else { - dev->trans_start = jiffies; - netif_wake_queue(dev); - } + schedule_task(&priv->timeout_task); } static int orinoco_change_mtu(struct net_device *dev, int new_mtu) { struct orinoco_private *priv = dev->priv; - TRACE_ENTER(dev->name); if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) ) return -EINVAL; @@ -2073,11 +2362,10 @@ dev->mtu = new_mtu; - TRACE_EXIT(dev->name); - return 0; } +/* FIXME: return int? */ static void __orinoco_set_multicast_list(struct net_device *dev) { @@ -2086,17 +2374,6 @@ int err = 0; int promisc, mc_count; - /* We'll wait until it's ready. Anyway, the network doesn't call us - * here until we are open - Jean II */ - /* FIXME: do we need this test at all? */ - if (! netif_device_present(dev)) { - printk(KERN_WARNING "%s: __orinoco_set_multicast_list() called while device " - "not present.\n", dev->name); - return; - } - - TRACE_ENTER(dev->name); - /* 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) || @@ -2154,8 +2431,6 @@ dev->flags |= IFF_PROMISC; else dev->flags &= ~IFF_PROMISC; - - TRACE_EXIT(dev->name); } /********************************************************************/ @@ -2170,6 +2445,7 @@ struct iw_range range; int numrates; int i, k; + unsigned long flags; TRACE_ENTER(dev->name); @@ -2179,9 +2455,12 @@ rrq->length = sizeof(range); - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; + mode = priv->iw_mode; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); memset(&range, 0, sizeof(range)); @@ -2254,7 +2533,9 @@ range.min_frag = 256; range.max_frag = 2346; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if (priv->has_wep) { range.max_encoding_tokens = ORINOCO_MAX_KEYS; @@ -2269,7 +2550,7 @@ range.num_encoding_sizes = 0; range.max_encoding_tokens = 0; } - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); range.min_pmp = 0; range.max_pmp = 65535000; @@ -2311,6 +2592,7 @@ u16 xlen = 0; int err = 0; char keybuf[ORINOCO_MAX_KEY_SIZE]; + unsigned long flags; if (erq->pointer) { /* We actually have a key to set */ @@ -2321,7 +2603,9 @@ return -EFAULT; } - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if (erq->pointer) { if (erq->length > ORINOCO_MAX_KEY_SIZE) { @@ -2385,9 +2669,10 @@ priv->tx_key = setindex; priv->wep_on = enable; priv->wep_restrict = restricted; + out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2398,9 +2683,12 @@ int index = (erq->flags & IW_ENCODE_INDEX) - 1; u16 xlen = 0; char keybuf[ORINOCO_MAX_KEY_SIZE]; - + int err; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if ((index < 0) || (index >= ORINOCO_MAX_KEYS)) index = priv->tx_key; @@ -2426,7 +2714,7 @@ memcpy(keybuf, priv->keys[index].data, ORINOCO_MAX_KEY_SIZE); } - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); if (erq->pointer) { if (copy_to_user(erq->pointer, keybuf, xlen)) @@ -2440,6 +2728,8 @@ { struct orinoco_private *priv = dev->priv; 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 * anyway... - Jean II */ @@ -2456,11 +2746,13 @@ essidbuf[erq->length] = '\0'; } - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; memcpy(priv->desired_essid, essidbuf, sizeof(priv->desired_essid)); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2471,6 +2763,7 @@ char essidbuf[IW_ESSID_MAX_SIZE+1]; int active; int err = 0; + unsigned long flags; TRACE_ENTER(dev->name); @@ -2479,9 +2772,11 @@ if (err) return err; } else { - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; memcpy(essidbuf, priv->desired_essid, sizeof(essidbuf)); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); } erq->flags = 1; @@ -2499,6 +2794,8 @@ { struct orinoco_private *priv = dev->priv; char nickbuf[IW_ESSID_MAX_SIZE+1]; + int err; + unsigned long flags; if (nrq->length > IW_ESSID_MAX_SIZE) return -E2BIG; @@ -2510,11 +2807,13 @@ nickbuf[nrq->length] = '\0'; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; memcpy(priv->nick, nickbuf, sizeof(priv->nick)); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2523,10 +2822,15 @@ { struct orinoco_private *priv = dev->priv; char nickbuf[IW_ESSID_MAX_SIZE+1]; + int err; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE+1); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); nrq->length = strlen(nickbuf)+1; @@ -2540,6 +2844,8 @@ { struct orinoco_private *priv = dev->priv; int chan = -1; + int err; + unsigned long flags; /* We can only use this in Ad-Hoc demo mode to set the operating * frequency, or in IBSS mode to set the frequency where the IBSS @@ -2567,9 +2873,11 @@ ! (priv->channel_mask & (1 << (chan-1)) ) ) return -EINVAL; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; priv->channel = chan; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2580,13 +2888,16 @@ hermes_t *hw = &priv->hw; u16 val; int err; + unsigned long flags; if (!priv->has_sensitivity) return -EOPNOTSUPP; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFSYSTEMSCALE, &val); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); if (err) return err; @@ -2601,6 +2912,8 @@ { struct orinoco_private *priv = dev->priv; int val = srq->value; + int err; + unsigned long flags; if (!priv->has_sensitivity) return -EOPNOTSUPP; @@ -2608,9 +2921,11 @@ if ((val < 1) || (val > 3)) return -EINVAL; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; priv->ap_density = val; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2619,6 +2934,8 @@ { struct orinoco_private *priv = dev->priv; int val = rrq->value; + int err; + unsigned long flags; if (rrq->disabled) val = 2347; @@ -2626,9 +2943,12 @@ if ( (val < 0) || (val > 2347) ) return -EINVAL; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; + priv->rts_thresh = val; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2637,8 +2957,11 @@ { struct orinoco_private *priv = dev->priv; int err = 0; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if (priv->has_mwo) { if (frq->disabled) @@ -2660,7 +2983,7 @@ } } - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2671,8 +2994,11 @@ hermes_t *hw = &priv->hw; int err = 0; u16 val; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if (priv->has_mwo) { err = hermes_read_wordrec(hw, USER_BAP, @@ -2695,7 +3021,7 @@ frq->fixed = 1; } - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2707,6 +3033,7 @@ int ratemode = -1; int bitrate; /* 100s of kilobits */ int i; + unsigned long flags; /* As the user space doesn't know our highest rate, it uses -1 * to ask us to set the highest rate. Test it using "iwconfig @@ -2733,9 +3060,11 @@ if (ratemode == -1) return -EINVAL; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; priv->bitratemode = ratemode; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2748,8 +3077,11 @@ int ratemode; int i; u16 val; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; ratemode = priv->bitratemode; @@ -2799,7 +3131,7 @@ } out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2808,9 +3140,11 @@ { struct orinoco_private *priv = dev->priv; int err = 0; + unsigned long flags; - - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; if (prq->disabled) { priv->pm_on = 0; @@ -2850,7 +3184,7 @@ } out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2861,8 +3195,11 @@ hermes_t *hw = &priv->hw; int err = 0; u16 enable, period, timeout, mcast; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_CNFPMENABLED, &enable); if (err) @@ -2896,7 +3233,7 @@ prq->flags |= IW_POWER_UNICAST_R; out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2908,8 +3245,11 @@ hermes_t *hw = &priv->hw; int err = 0; u16 short_limit, long_limit, lifetime; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; err = hermes_read_wordrec(hw, USER_BAP, HERMES_RID_SHORTRETRYLIMIT, &short_limit); @@ -2946,7 +3286,7 @@ } out: - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -2956,14 +3296,19 @@ { struct orinoco_private *priv = dev->priv; int val = *( (int *) wrq->u.name ); + int err; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); priv->ibss_port = val ; /* Actually update the mode we are using */ set_port_type(priv); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2971,10 +3316,15 @@ { struct orinoco_private *priv = dev->priv; int *val = (int *)wrq->u.name; + int err; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); *val = priv->ibss_port; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -2984,15 +3334,18 @@ struct orinoco_private *priv = dev->priv; int val = *( (int *) wrq->u.name ); int err = 0; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); switch (val) { case 0: /* Try to do IEEE ad-hoc mode */ if (! priv->has_ibss) { err = -EINVAL; break; } - DEBUG(2, "%s: Prefer IBSS Ad-Hoc mode\n", dev->name); priv->prefer_port3 = 0; break; @@ -3002,7 +3355,6 @@ err = -EINVAL; break; } - DEBUG(2, "%s: Prefer Ad-Hoc demo mode\n", dev->name); priv->prefer_port3 = 1; break; @@ -3014,7 +3366,7 @@ /* Actually update the mode we are using */ set_port_type(priv); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -3023,10 +3375,15 @@ { struct orinoco_private *priv = dev->priv; int *val = (int *)wrq->u.name; + int err; + unsigned long flags; + + err = orinoco_lock(priv, &flags); + if (err) + return err; - orinoco_lock(priv); *val = priv->prefer_port3; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return 0; } @@ -3040,6 +3397,7 @@ int number = srq->length; int i; int err = 0; + unsigned long flags; /* Check the number of addresses */ if (number > IW_MAX_SPY) @@ -3053,7 +3411,9 @@ } /* Make sure nobody mess with the structure while we do */ - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; /* orinoco_lock() doesn't disable interrupts, so make sure the * interrupt rx path don't get confused while we copy */ @@ -3071,18 +3431,8 @@ priv->spy_number = number; } - /* Time to show what we have done... */ - DEBUG(0, "%s: New spy list:\n", dev->name); - for (i = 0; i < number; i++) { - DEBUG(0, "%s: %d - %02x:%02x:%02x:%02x:%02x:%02x\n", - dev->name, i+1, - priv->spy_address[i][0], priv->spy_address[i][1], - priv->spy_address[i][2], priv->spy_address[i][3], - priv->spy_address[i][4], priv->spy_address[i][5]); - } - /* Now, let the others play */ - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); return err; } @@ -3094,8 +3444,12 @@ struct iw_quality spy_stat[IW_MAX_SPY]; int number; int i; + int err; + unsigned long flags; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; number = priv->spy_number; if ((number > 0) && (srq->pointer)) { @@ -3115,7 +3469,7 @@ priv->spy_stat[i].updated = 0; } - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); /* Push stuff to user space */ srq->length = number; @@ -3135,39 +3489,39 @@ struct orinoco_private *priv = dev->priv; struct iwreq *wrq = (struct iwreq *)rq; int err = 0; + int tmp; int changed = 0; + unsigned long flags; TRACE_ENTER(dev->name); /* In theory, we could allow most of the the SET stuff to be - * done In practice, the laps of time at startup when the card - * is not ready is very short, so why bother... Note that - * netif_device_present is different from up/down (ifconfig), - * when the device is not yet up, it is usually already - * ready... Jean II */ -// if (! netif_device_present(dev)) -// return -ENODEV; + * done. In practice, the lapse of time at startup when the + * card is not ready is very short, so why bother... Note + * that netif_device_present is different from up/down + * (ifconfig), when the device is not yet up, it is usually + * already ready... Jean II */ + if (! netif_device_present(dev)) + return -ENODEV; switch (cmd) { case SIOCGIWNAME: - DEBUG(1, "%s: SIOCGIWNAME\n", dev->name); strcpy(wrq->u.name, "IEEE 802.11-DS"); break; case SIOCGIWAP: - DEBUG(1, "%s: SIOCGIWAP\n", dev->name); wrq->u.ap_addr.sa_family = ARPHRD_ETHER; err = orinoco_hw_get_bssid(priv, wrq->u.ap_addr.sa_data); break; case SIOCGIWRANGE: - DEBUG(1, "%s: SIOCGIWRANGE\n", dev->name); err = orinoco_ioctl_getiwrange(dev, &wrq->u.data); break; case SIOCSIWMODE: - DEBUG(1, "%s: SIOCSIWMODE\n", dev->name); - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; switch (wrq->u.mode) { case IW_MODE_ADHOC: if (! (priv->has_ibss || priv->has_port3) ) @@ -3188,18 +3542,18 @@ break; } set_port_type(priv); - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); break; case SIOCGIWMODE: - DEBUG(1, "%s: SIOCGIWMODE\n", dev->name); - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; wrq->u.mode = priv->iw_mode; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); break; case SIOCSIWENCODE: - DEBUG(1, "%s: SIOCSIWENCODE\n", dev->name); if (! priv->has_wep) { err = -EOPNOTSUPP; break; @@ -3211,7 +3565,6 @@ break; case SIOCGIWENCODE: - DEBUG(1, "%s: SIOCGIWENCODE\n", dev->name); if (! priv->has_wep) { err = -EOPNOTSUPP; break; @@ -3226,106 +3579,94 @@ break; case SIOCSIWESSID: - DEBUG(1, "%s: SIOCSIWESSID\n", dev->name); err = orinoco_ioctl_setessid(dev, &wrq->u.essid); if (! err) changed = 1; break; case SIOCGIWESSID: - DEBUG(1, "%s: SIOCGIWESSID\n", dev->name); err = orinoco_ioctl_getessid(dev, &wrq->u.essid); break; case SIOCSIWNICKN: - DEBUG(1, "%s: SIOCSIWNICKN\n", dev->name); err = orinoco_ioctl_setnick(dev, &wrq->u.data); if (! err) changed = 1; break; case SIOCGIWNICKN: - DEBUG(1, "%s: SIOCGIWNICKN\n", dev->name); err = orinoco_ioctl_getnick(dev, &wrq->u.data); break; case SIOCGIWFREQ: - DEBUG(1, "%s: SIOCGIWFREQ\n", dev->name); - wrq->u.freq.m = orinoco_hw_get_freq(priv); - wrq->u.freq.e = 1; + tmp = orinoco_hw_get_freq(priv); + if (tmp < 0) { + err = tmp; + } else { + wrq->u.freq.m = tmp; + wrq->u.freq.e = 1; + } break; case SIOCSIWFREQ: - DEBUG(1, "%s: SIOCSIWFREQ\n", dev->name); err = orinoco_ioctl_setfreq(dev, &wrq->u.freq); if (! err) changed = 1; break; case SIOCGIWSENS: - DEBUG(1, "%s: SIOCGIWSENS\n", dev->name); err = orinoco_ioctl_getsens(dev, &wrq->u.sens); break; case SIOCSIWSENS: - DEBUG(1, "%s: SIOCSIWSENS\n", dev->name); err = orinoco_ioctl_setsens(dev, &wrq->u.sens); if (! err) changed = 1; break; case SIOCGIWRTS: - DEBUG(1, "%s: SIOCGIWRTS\n", dev->name); wrq->u.rts.value = priv->rts_thresh; wrq->u.rts.disabled = (wrq->u.rts.value == 2347); wrq->u.rts.fixed = 1; break; case SIOCSIWRTS: - DEBUG(1, "%s: SIOCSIWRTS\n", dev->name); err = orinoco_ioctl_setrts(dev, &wrq->u.rts); if (! err) changed = 1; break; case SIOCSIWFRAG: - DEBUG(1, "%s: SIOCSIWFRAG\n", dev->name); err = orinoco_ioctl_setfrag(dev, &wrq->u.frag); if (! err) changed = 1; break; case SIOCGIWFRAG: - DEBUG(1, "%s: SIOCGIWFRAG\n", dev->name); err = orinoco_ioctl_getfrag(dev, &wrq->u.frag); break; case SIOCSIWRATE: - DEBUG(1, "%s: SIOCSIWRATE\n", dev->name); err = orinoco_ioctl_setrate(dev, &wrq->u.bitrate); if (! err) changed = 1; break; case SIOCGIWRATE: - DEBUG(1, "%s: SIOCGIWRATE\n", dev->name); err = orinoco_ioctl_getrate(dev, &wrq->u.bitrate); break; case SIOCSIWPOWER: - DEBUG(1, "%s: SIOCSIWPOWER\n", dev->name); err = orinoco_ioctl_setpower(dev, &wrq->u.power); if (! err) changed = 1; break; case SIOCGIWPOWER: - DEBUG(1, "%s: SIOCGIWPOWER\n", dev->name); err = orinoco_ioctl_getpower(dev, &wrq->u.power); break; case SIOCGIWTXPOW: - DEBUG(1, "%s: SIOCGIWTXPOW\n", dev->name); /* The card only supports one tx power, so this is easy */ wrq->u.txpower.value = 15; /* dBm */ wrq->u.txpower.fixed = 1; @@ -3335,30 +3676,23 @@ #if WIRELESS_EXT > 10 case SIOCSIWRETRY: - DEBUG(1, "%s: SIOCSIWRETRY\n", dev->name); err = -EOPNOTSUPP; break; case SIOCGIWRETRY: - DEBUG(1, "%s: SIOCGIWRETRY\n", dev->name); err = orinoco_ioctl_getretry(dev, &wrq->u.retry); break; #endif /* WIRELESS_EXT > 10 */ case SIOCSIWSPY: - DEBUG(1, "%s: SIOCSIWSPY\n", dev->name); - err = orinoco_ioctl_setspy(dev, &wrq->u.data); break; case SIOCGIWSPY: - DEBUG(1, "%s: SIOCGIWSPY\n", dev->name); - err = orinoco_ioctl_getspy(dev, &wrq->u.data); break; case SIOCGIWPRIV: - DEBUG(1, "%s: SIOCGIWPRIV\n", dev->name); if (wrq->u.data.pointer) { struct iw_priv_args privtab[] = { { SIOCIWFIRSTPRIV + 0x0, 0, 0, "force_reset" }, @@ -3380,7 +3714,8 @@ 0, "set_ibssport" }, { SIOCIWFIRSTPRIV + 0x7, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, - "get_ibssport" } + "get_ibssport" }, + { SIOCIWLASTPRIV, 0, 0, "dump_recs" }, }; err = verify_area(VERIFY_WRITE, wrq->u.data.pointer, sizeof(privtab)); @@ -3395,25 +3730,17 @@ case SIOCIWFIRSTPRIV + 0x0: /* force_reset */ case SIOCIWFIRSTPRIV + 0x1: /* card_reset */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x0 (force_reset)\n", - dev->name); if (! capable(CAP_NET_ADMIN)) { err = -EPERM; break; } - printk(KERN_DEBUG "%s: Forcing reset!\n", dev->name); + printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name); - /* We need the xmit lock because it protects the - multicast list which orinoco_reset() reads */ - netif_xmit_lock(dev); - orinoco_reset(priv); - netif_xmit_unlock(dev); + schedule_task(&priv->timeout_task); break; case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x2 (set_port3)\n", - dev->name); if (! capable(CAP_NET_ADMIN)) { err = -EPERM; break; @@ -3425,14 +3752,10 @@ break; case SIOCIWFIRSTPRIV + 0x3: /* get_port3 */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x3 (get_port3)\n", - dev->name); err = orinoco_ioctl_getport3(dev, wrq); break; case SIOCIWFIRSTPRIV + 0x4: /* set_preamble */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x4 (set_preamble)\n", - dev->name); if (! capable(CAP_NET_ADMIN)) { err = -EPERM; break; @@ -3446,32 +3769,32 @@ if(priv->has_preamble) { int val = *( (int *) wrq->u.name ); - orinoco_lock(priv); - if(val) + err = orinoco_lock(priv, &flags); + if (err) + return err; + if (val) priv->preamble = 1; else priv->preamble = 0; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); changed = 1; } else err = -EOPNOTSUPP; break; case SIOCIWFIRSTPRIV + 0x5: /* get_preamble */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x5 (get_preamble)\n", - dev->name); if(priv->has_preamble) { int *val = (int *)wrq->u.name; - orinoco_lock(priv); + err = orinoco_lock(priv, &flags); + if (err) + return err; *val = priv->preamble; - orinoco_unlock(priv); + orinoco_unlock(priv, &flags); } else err = -EOPNOTSUPP; break; case SIOCIWFIRSTPRIV + 0x6: /* set_ibssport */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x6 (set_ibssport)\n", - dev->name); if (! capable(CAP_NET_ADMIN)) { err = -EPERM; break; @@ -3483,109 +3806,28 @@ break; case SIOCIWFIRSTPRIV + 0x7: /* get_ibssport */ - DEBUG(1, "%s: SIOCIWFIRSTPRIV + 0x7 (get_ibssport)\n", - dev->name); err = orinoco_ioctl_getibssport(dev, wrq); break; + case SIOCIWLASTPRIV: + err = orinoco_debug_dump_recs(priv); + if (err) + printk(KERN_ERR "%s: Unable to dump records (%d)\n", + dev->name, err); + break; + default: err = -EOPNOTSUPP; } if (! err && changed && netif_running(dev)) { - /* We need the xmit lock because it protects the - multicast list which orinoco_reset() reads */ - netif_xmit_lock(dev); - err = orinoco_reset(priv); - netif_xmit_unlock(dev); - if (err) { - /* Ouch ! What are we supposed to do ? */ - printk(KERN_ERR "orinoco_cs: Failed to set parameters on %s\n", - dev->name); - netif_device_detach(dev); - orinoco_shutdown(priv); - } + err = orinoco_reconfigure(priv); } TRACE_EXIT(dev->name); - - return err; -} - -/********************************************************************/ -/* procfs stuff */ -/********************************************************************/ - -static struct proc_dir_entry *dir_base = NULL; - -#define PROC_LTV_SIZE 128 - -/* - * This function updates the total amount of data printed so far. It then - * determines if the amount of data printed into a buffer has reached the - * offset requested. If it hasn't, then the buffer is shifted over so that - * the next bit of data can be printed over the old bit. If the total - * amount printed so far exceeds the total amount requested, then this - * function returns 1, otherwise 0. - */ -static int -shift_buffer(char *buffer, int requested_offset, int requested_len, - int *total, int *slop, char **buf) -{ - int printed; - - printed = *buf - buffer; - if (*total + printed <= requested_offset) { - *total += printed; - *buf = buffer; - } - else { - if (*total < requested_offset) { - *slop = requested_offset - *total; - } - *total = requested_offset + printed - *slop; - } - if (*total > requested_offset + requested_len) { - return 1; - } - else { - return 0; - } -} - -/* - * This function calculates the actual start of the requested data - * in the buffer. It also calculates actual length of data returned, - * which could be less that the amount of data requested. - */ -#define PROC_BUFFER_SIZE 4096 -#define PROC_SAFE_SIZE 3072 -static int -calc_start_len(char *buffer, char **start, int requested_offset, - int requested_len, int total, char *buf) -{ - int return_len, buffer_len; - - buffer_len = buf - buffer; - if (buffer_len >= PROC_BUFFER_SIZE - 1) { - printk(KERN_ERR "calc_start_len: exceeded /proc buffer size\n"); - } - - /* - * There may be bytes before and after the - * chunk that was actually requested. - */ - return_len = total - requested_offset; - if (return_len < 0) { - return_len = 0; - } - *start = buf - return_len; - if (return_len > requested_len) { - return_len = requested_len; - } - return return_len; + return err; } struct { @@ -3597,140 +3839,134 @@ #define DISPLAY_STRING 2 #define DISPLAY_XSTRING 3 } record_table[] = { -#define PROC_REC(name,type) { HERMES_RID_##name, #name, DISPLAY_##type } - PROC_REC(CNFPORTTYPE,WORDS), - PROC_REC(CNFOWNMACADDR,BYTES), - PROC_REC(CNFDESIREDSSID,STRING), - PROC_REC(CNFOWNCHANNEL,WORDS), - PROC_REC(CNFOWNSSID,STRING), - PROC_REC(CNFOWNATIMWINDOW,WORDS), - PROC_REC(CNFSYSTEMSCALE,WORDS), - PROC_REC(CNFMAXDATALEN,WORDS), - PROC_REC(CNFPMENABLED,WORDS), - PROC_REC(CNFPMEPS,WORDS), - PROC_REC(CNFMULTICASTRECEIVE,WORDS), - PROC_REC(CNFMAXSLEEPDURATION,WORDS), - PROC_REC(CNFPMHOLDOVERDURATION,WORDS), - PROC_REC(CNFOWNNAME,STRING), - PROC_REC(CNFOWNDTIMPERIOD,WORDS), - PROC_REC(CNFMULTICASTPMBUFFERING,WORDS), - PROC_REC(CNFWEPENABLED_AGERE,WORDS), - PROC_REC(CNFMANDATORYBSSID_SYMBOL,WORDS), - PROC_REC(CNFWEPDEFAULTKEYID,WORDS), - PROC_REC(CNFDEFAULTKEY0,BYTES), - PROC_REC(CNFDEFAULTKEY1,BYTES), - PROC_REC(CNFMWOROBUST_AGERE,WORDS), - PROC_REC(CNFDEFAULTKEY2,BYTES), - PROC_REC(CNFDEFAULTKEY3,BYTES), - PROC_REC(CNFWEPFLAGS_INTERSIL,WORDS), - PROC_REC(CNFWEPKEYMAPPINGTABLE,WORDS), - PROC_REC(CNFAUTHENTICATION,WORDS), - PROC_REC(CNFMAXASSOCSTA,WORDS), - PROC_REC(CNFKEYLENGTH_SYMBOL,WORDS), - PROC_REC(CNFTXCONTROL,WORDS), - PROC_REC(CNFROAMINGMODE,WORDS), - PROC_REC(CNFHOSTAUTHENTICATION,WORDS), - PROC_REC(CNFRCVCRCERROR,WORDS), - PROC_REC(CNFMMLIFE,WORDS), - PROC_REC(CNFALTRETRYCOUNT,WORDS), - PROC_REC(CNFBEACONINT,WORDS), - PROC_REC(CNFAPPCFINFO,WORDS), - PROC_REC(CNFSTAPCFINFO,WORDS), - PROC_REC(CNFPRIORITYQUSAGE,WORDS), - PROC_REC(CNFTIMCTRL,WORDS), - PROC_REC(CNFTHIRTY2TALLY,WORDS), - PROC_REC(CNFENHSECURITY,WORDS), - PROC_REC(CNFGROUPADDRESSES,BYTES), - PROC_REC(CNFCREATEIBSS,WORDS), - PROC_REC(CNFFRAGMENTATIONTHRESHOLD,WORDS), - PROC_REC(CNFRTSTHRESHOLD,WORDS), - PROC_REC(CNFTXRATECONTROL,WORDS), - PROC_REC(CNFPROMISCUOUSMODE,WORDS), - PROC_REC(CNFBASICRATES_SYMBOL,WORDS), - PROC_REC(CNFPREAMBLE_SYMBOL,WORDS), - PROC_REC(CNFSHORTPREAMBLE,WORDS), - PROC_REC(CNFWEPKEYS_AGERE,BYTES), - PROC_REC(CNFEXCLUDELONGPREAMBLE,WORDS), - PROC_REC(CNFTXKEY_AGERE,WORDS), - PROC_REC(CNFAUTHENTICATIONRSPTO,WORDS), - PROC_REC(CNFBASICRATES,WORDS), - PROC_REC(CNFSUPPORTEDRATES,WORDS), - PROC_REC(CNFTICKTIME,WORDS), - PROC_REC(CNFSCANREQUEST,WORDS), - PROC_REC(CNFJOINREQUEST,WORDS), - PROC_REC(CNFAUTHENTICATESTATION,WORDS), - PROC_REC(CNFCHANNELINFOREQUEST,WORDS), - PROC_REC(MAXLOADTIME,WORDS), - PROC_REC(DOWNLOADBUFFER,WORDS), - PROC_REC(PRIID,WORDS), - PROC_REC(PRISUPRANGE,WORDS), - PROC_REC(CFIACTRANGES,WORDS), - PROC_REC(NICSERNUM,WORDS), - PROC_REC(NICID,WORDS), - PROC_REC(MFISUPRANGE,WORDS), - PROC_REC(CFISUPRANGE,WORDS), - PROC_REC(CHANNELLIST,WORDS), - PROC_REC(REGULATORYDOMAINS,WORDS), - PROC_REC(TEMPTYPE,WORDS), -/* PROC_REC(CIS,BYTES), */ - PROC_REC(STAID,WORDS), - PROC_REC(CURRENTSSID,STRING), - PROC_REC(CURRENTBSSID,BYTES), - PROC_REC(COMMSQUALITY,WORDS), - PROC_REC(CURRENTTXRATE,WORDS), - PROC_REC(CURRENTBEACONINTERVAL,WORDS), - PROC_REC(CURRENTSCALETHRESHOLDS,WORDS), - PROC_REC(PROTOCOLRSPTIME,WORDS), - PROC_REC(SHORTRETRYLIMIT,WORDS), - PROC_REC(LONGRETRYLIMIT,WORDS), - PROC_REC(MAXTRANSMITLIFETIME,WORDS), - PROC_REC(MAXRECEIVELIFETIME,WORDS), - PROC_REC(CFPOLLABLE,WORDS), - PROC_REC(AUTHENTICATIONALGORITHMS,WORDS), - PROC_REC(PRIVACYOPTIONIMPLEMENTED,WORDS), - PROC_REC(OWNMACADDR,BYTES), - PROC_REC(SCANRESULTSTABLE,WORDS), - PROC_REC(PHYTYPE,WORDS), - PROC_REC(CURRENTCHANNEL,WORDS), - PROC_REC(CURRENTPOWERSTATE,WORDS), - PROC_REC(CCAMODE,WORDS), - PROC_REC(SUPPORTEDDATARATES,WORDS), - PROC_REC(BUILDSEQ,BYTES), - PROC_REC(FWID,XSTRING) -#undef PROC_REC +#define DEBUG_REC(name,type) { HERMES_RID_##name, #name, DISPLAY_##type } + DEBUG_REC(CNFPORTTYPE,WORDS), + DEBUG_REC(CNFOWNMACADDR,BYTES), + DEBUG_REC(CNFDESIREDSSID,STRING), + DEBUG_REC(CNFOWNCHANNEL,WORDS), + DEBUG_REC(CNFOWNSSID,STRING), + DEBUG_REC(CNFOWNATIMWINDOW,WORDS), + DEBUG_REC(CNFSYSTEMSCALE,WORDS), + DEBUG_REC(CNFMAXDATALEN,WORDS), + DEBUG_REC(CNFPMENABLED,WORDS), + DEBUG_REC(CNFPMEPS,WORDS), + DEBUG_REC(CNFMULTICASTRECEIVE,WORDS), + DEBUG_REC(CNFMAXSLEEPDURATION,WORDS), + DEBUG_REC(CNFPMHOLDOVERDURATION,WORDS), + DEBUG_REC(CNFOWNNAME,STRING), + DEBUG_REC(CNFOWNDTIMPERIOD,WORDS), + DEBUG_REC(CNFMULTICASTPMBUFFERING,WORDS), + DEBUG_REC(CNFWEPENABLED_AGERE,WORDS), + DEBUG_REC(CNFMANDATORYBSSID_SYMBOL,WORDS), + DEBUG_REC(CNFWEPDEFAULTKEYID,WORDS), + DEBUG_REC(CNFDEFAULTKEY0,BYTES), + DEBUG_REC(CNFDEFAULTKEY1,BYTES), + DEBUG_REC(CNFMWOROBUST_AGERE,WORDS), + DEBUG_REC(CNFDEFAULTKEY2,BYTES), + DEBUG_REC(CNFDEFAULTKEY3,BYTES), + DEBUG_REC(CNFWEPFLAGS_INTERSIL,WORDS), + DEBUG_REC(CNFWEPKEYMAPPINGTABLE,WORDS), + DEBUG_REC(CNFAUTHENTICATION,WORDS), + DEBUG_REC(CNFMAXASSOCSTA,WORDS), + DEBUG_REC(CNFKEYLENGTH_SYMBOL,WORDS), + DEBUG_REC(CNFTXCONTROL,WORDS), + DEBUG_REC(CNFROAMINGMODE,WORDS), + DEBUG_REC(CNFHOSTAUTHENTICATION,WORDS), + DEBUG_REC(CNFRCVCRCERROR,WORDS), + DEBUG_REC(CNFMMLIFE,WORDS), + DEBUG_REC(CNFALTRETRYCOUNT,WORDS), + DEBUG_REC(CNFBEACONINT,WORDS), + DEBUG_REC(CNFAPPCFINFO,WORDS), + DEBUG_REC(CNFSTAPCFINFO,WORDS), + DEBUG_REC(CNFPRIORITYQUSAGE,WORDS), + DEBUG_REC(CNFTIMCTRL,WORDS), + DEBUG_REC(CNFTHIRTY2TALLY,WORDS), + DEBUG_REC(CNFENHSECURITY,WORDS), + DEBUG_REC(CNFGROUPADDRESSES,BYTES), + DEBUG_REC(CNFCREATEIBSS,WORDS), + DEBUG_REC(CNFFRAGMENTATIONTHRESHOLD,WORDS), + DEBUG_REC(CNFRTSTHRESHOLD,WORDS), + DEBUG_REC(CNFTXRATECONTROL,WORDS), + DEBUG_REC(CNFPROMISCUOUSMODE,WORDS), + DEBUG_REC(CNFBASICRATES_SYMBOL,WORDS), + DEBUG_REC(CNFPREAMBLE_SYMBOL,WORDS), + DEBUG_REC(CNFSHORTPREAMBLE,WORDS), + DEBUG_REC(CNFWEPKEYS_AGERE,BYTES), + DEBUG_REC(CNFEXCLUDELONGPREAMBLE,WORDS), + DEBUG_REC(CNFTXKEY_AGERE,WORDS), + DEBUG_REC(CNFAUTHENTICATIONRSPTO,WORDS), + DEBUG_REC(CNFBASICRATES,WORDS), + DEBUG_REC(CNFSUPPORTEDRATES,WORDS), + DEBUG_REC(CNFTICKTIME,WORDS), + DEBUG_REC(CNFSCANREQUEST,WORDS), + DEBUG_REC(CNFJOINREQUEST,WORDS), + DEBUG_REC(CNFAUTHENTICATESTATION,WORDS), + DEBUG_REC(CNFCHANNELINFOREQUEST,WORDS), + DEBUG_REC(MAXLOADTIME,WORDS), + DEBUG_REC(DOWNLOADBUFFER,WORDS), + DEBUG_REC(PRIID,WORDS), + DEBUG_REC(PRISUPRANGE,WORDS), + DEBUG_REC(CFIACTRANGES,WORDS), + DEBUG_REC(NICSERNUM,WORDS), + DEBUG_REC(NICID,WORDS), + DEBUG_REC(MFISUPRANGE,WORDS), + DEBUG_REC(CFISUPRANGE,WORDS), + DEBUG_REC(CHANNELLIST,WORDS), + DEBUG_REC(REGULATORYDOMAINS,WORDS), + DEBUG_REC(TEMPTYPE,WORDS), +/* DEBUG_REC(CIS,BYTES), */ + DEBUG_REC(STAID,WORDS), + DEBUG_REC(CURRENTSSID,STRING), + DEBUG_REC(CURRENTBSSID,BYTES), + DEBUG_REC(COMMSQUALITY,WORDS), + DEBUG_REC(CURRENTTXRATE,WORDS), + DEBUG_REC(CURRENTBEACONINTERVAL,WORDS), + DEBUG_REC(CURRENTSCALETHRESHOLDS,WORDS), + DEBUG_REC(PROTOCOLRSPTIME,WORDS), + DEBUG_REC(SHORTRETRYLIMIT,WORDS), + DEBUG_REC(LONGRETRYLIMIT,WORDS), + DEBUG_REC(MAXTRANSMITLIFETIME,WORDS), + DEBUG_REC(MAXRECEIVELIFETIME,WORDS), + DEBUG_REC(CFPOLLABLE,WORDS), + DEBUG_REC(AUTHENTICATIONALGORITHMS,WORDS), + DEBUG_REC(PRIVACYOPTIONIMPLEMENTED,WORDS), + DEBUG_REC(OWNMACADDR,BYTES), + DEBUG_REC(SCANRESULTSTABLE,WORDS), + DEBUG_REC(PHYTYPE,WORDS), + DEBUG_REC(CURRENTCHANNEL,WORDS), + DEBUG_REC(CURRENTPOWERSTATE,WORDS), + DEBUG_REC(CCAMODE,WORDS), + DEBUG_REC(SUPPORTEDDATARATES,WORDS), + DEBUG_REC(BUILDSEQ,BYTES), + DEBUG_REC(FWID,XSTRING) +#undef DEBUG_REC }; -#define NUM_RIDS ( sizeof(record_table) / sizeof(record_table[0]) ) -static int -orinoco_proc_get_hermes_recs(char *page, char **start, off_t requested_offset, - int requested_len, int *eof, void *data) +#define DEBUG_LTV_SIZE 128 + +static int orinoco_debug_dump_recs(struct orinoco_private *priv) { - struct orinoco_private *priv = (struct orinoco_private *)data; - struct net_device *dev = priv->ndev; hermes_t *hw = &priv->hw; - char *buf = page; - int total = 0, slop = 0; u8 *val8; u16 *val16; int i,j; u16 length; int err; - if (! netif_device_present(dev)) - return -ENODEV; - - val8 = kmalloc(PROC_LTV_SIZE + 2, GFP_KERNEL); + /* I'm not sure: we might have a lock here, so we'd better go + atomic, just in case. */ + val8 = kmalloc(DEBUG_LTV_SIZE + 2, GFP_ATOMIC); if (! val8) return -ENOMEM; val16 = (u16 *)val8; - for (i = 0; i < NUM_RIDS; i++) { + for (i = 0; i < ARRAY_SIZE(record_table); i++) { u16 rid = record_table[i].rid; int len; - memset(val8, 0, PROC_LTV_SIZE + 2); + memset(val8, 0, DEBUG_LTV_SIZE + 2); - err = hermes_read_ltv(hw, USER_BAP, rid, PROC_LTV_SIZE, + err = hermes_read_ltv(hw, USER_BAP, rid, DEBUG_LTV_SIZE, &length, val8); if (err) { DEBUG(0, "Error %d reading RID 0x%04x\n", err, rid); @@ -3740,210 +3976,46 @@ if (length == 0) continue; - buf += sprintf(buf, "%-15s (0x%04x): length=%d (%d bytes)\tvalue=", record_table[i].name, - rid, length, (length-1)*2); - len = min(((int)length-1)*2, PROC_LTV_SIZE); + printk(KERN_DEBUG "%-15s (0x%04x): length=%d (%d bytes)\tvalue=", + record_table[i].name, + rid, length, (length-1)*2); + len = min(((int)length-1)*2, DEBUG_LTV_SIZE); switch (record_table[i].displaytype) { case DISPLAY_WORDS: - for (j = 0; j < len / 2; j++) { - buf += sprintf(buf, "%04X-", le16_to_cpu(val16[j])); - } - buf--; + for (j = 0; j < len / 2; j++) + printk("%04X-", le16_to_cpu(val16[j])); break; case DISPLAY_BYTES: default: - for (j = 0; j < len; j++) { - buf += sprintf(buf, "%02X:", val8[j]); - } - buf--; + for (j = 0; j < len; j++) + printk("%02X:", val8[j]); break; case DISPLAY_STRING: len = min(len, le16_to_cpu(val16[0])+2); val8[len] = '\0'; - buf += sprintf(buf, "\"%s\"", (char *)&val16[1]); + printk("\"%s\"", (char *)&val16[1]); break; case DISPLAY_XSTRING: - buf += sprintf(buf, "'%s'", (char *)val8); + printk("'%s'", (char *)val8); } - buf += sprintf(buf, "\n"); - - if (shift_buffer(page, requested_offset, requested_len, - &total, &slop, &buf)) - break; - - if ( (buf - page) > PROC_SAFE_SIZE ) - break; + printk("\n"); } kfree(val8); - return calc_start_len(page, start, requested_offset, requested_len, - total, buf); -} - -#ifdef HERMES_DEBUG_BUFFER -static int -orinoco_proc_get_hermes_buf(char *page, char **start, off_t requested_offset, - int requested_len, int *eof, void *data) -{ - struct orinoco_private *priv = (struct orinoco_private *)data; - hermes_t *hw = &priv->hw; - char *buf = page; - int total = 0, slop = 0; - int i; - - for (i = 0; i < min_t(int,hw->dbufp, HERMES_DEBUG_BUFSIZE); i++) { - memcpy(buf, &hw->dbuf[i], sizeof(hw->dbuf[i])); - buf += sizeof(hw->dbuf[i]); - - if (shift_buffer(page, requested_offset, requested_len, - &total, &slop, &buf)) - break; - - if ( (buf - page) > PROC_SAFE_SIZE ) - break; - } - - return calc_start_len(page, start, requested_offset, requested_len, - total, buf); -} - -static int -orinoco_proc_get_hermes_prof(char *page, char **start, off_t requested_offset, - int requested_len, int *eof, void *data) -{ - struct orinoco_private *priv = (struct orinoco_private *)data; - hermes_t *hw = &priv->hw; - char *buf = page; - int total = 0, slop = 0; - int i; - - for (i = 0; i < (HERMES_BAP_BUSY_TIMEOUT+1); i++) { - memcpy(buf, &hw->profile[i], sizeof(hw->profile[i])); - buf += sizeof(hw->profile[i]); - - if (shift_buffer(page, requested_offset, requested_len, - &total, &slop, &buf)) - break; - - if ( (buf - page) > PROC_SAFE_SIZE ) - break; - } - - return calc_start_len(page, start, requested_offset, requested_len, - total, buf); -} -#endif /* HERMES_DEBUG_BUFFER */ - -/* initialise the /proc subsystem for the hermes driver, creating the - * separate entries */ -static int -orinoco_proc_init(void) -{ - int err = 0; - - TRACE_ENTER("orinoco"); - - /* create the directory for it to sit in */ - dir_base = create_proc_entry("hermes", S_IFDIR, &proc_root); - if (dir_base == NULL) { - printk(KERN_ERR "Unable to initialise /proc/hermes.\n"); - orinoco_proc_cleanup(); - err = -ENOMEM; - } - - TRACE_EXIT("orinoco"); - - return err; -} - -int -orinoco_proc_dev_init(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - struct proc_dir_entry *e; - - priv->dir_dev = NULL; - - /* create the directory for it to sit in */ - priv->dir_dev = create_proc_entry(dev->name, S_IFDIR | S_IRUGO | S_IXUGO, - dir_base); - if (! priv->dir_dev) { - printk(KERN_ERR "Unable to initialize /proc/hermes/%s\n", dev->name); - goto fail; - } - - e = create_proc_read_entry("recs", S_IFREG | S_IRUGO, - priv->dir_dev, orinoco_proc_get_hermes_recs, priv); - if (! e) { - printk(KERN_ERR "Unable to initialize /proc/hermes/%s/recs\n", dev->name); - goto fail; - } - -#ifdef HERMES_DEBUG_BUFFER - e = create_proc_read_entry("buf", S_IFREG | S_IRUGO, - priv->dir_dev, orinoco_proc_get_hermes_buf, priv); - if (! e) { - printk(KERN_ERR "Unable to intialize /proc/hermes/%s/buf\n", dev->name); - goto fail; - } - - e = create_proc_read_entry("prof", S_IFREG | S_IRUGO, - priv->dir_dev, orinoco_proc_get_hermes_prof, priv); - if (! e) { - printk(KERN_ERR "Unable to intialize /proc/hermes/%s/prof\n", dev->name); - goto fail; - } -#endif /* HERMES_DEBUG_BUFFER */ - return 0; - fail: - orinoco_proc_dev_cleanup(priv); - return -ENOMEM; -} - -void -orinoco_proc_dev_cleanup(struct orinoco_private *priv) -{ - struct net_device *dev = priv->ndev; - - TRACE_ENTER(priv->ndev->name); - - if (priv->dir_dev) { - remove_proc_entry("prof", priv->dir_dev); - remove_proc_entry("buf", priv->dir_dev); - remove_proc_entry("recs", priv->dir_dev); - remove_proc_entry(dev->name, dir_base); - priv->dir_dev = NULL; - } - - TRACE_EXIT(priv->ndev->name); -} - -static void -orinoco_proc_cleanup(void) -{ - TRACE_ENTER("orinoco"); - - if (dir_base) { - remove_proc_entry("hermes", &proc_root); - dir_base = NULL; - } - - TRACE_EXIT("orinoco"); } -struct net_device *alloc_orinocodev(int sizeof_card) +struct net_device *alloc_orinocodev(int sizeof_card, int (*hard_reset)(struct orinoco_private *)) { struct net_device *dev; struct orinoco_private *priv; - TRACE_ENTER("orinoco"); dev = alloc_etherdev(sizeof(struct orinoco_private) + sizeof_card); priv = (struct orinoco_private *)dev->priv; priv->ndev = dev; @@ -3965,39 +4037,52 @@ dev->change_mtu = orinoco_change_mtu; dev->set_multicast_list = orinoco_set_multicast_list; - dev->open = NULL; /* Caller *must* override these */ - dev->stop = NULL; - - /* Setup the private structure */ + /* Set up default callbacks */ + dev->open = orinoco_open; + dev->stop = orinoco_stop; + priv->hard_reset = hard_reset; spin_lock_init(&priv->lock); - priv->hard_reset = NULL; /* Caller may override */ - - TRACE_EXIT("orinoco"); + priv->open = 0; + priv->hw_unavailable = 1; /* orinoco_init() must clear this + * before anything else touches the + * hardware */ + + priv->timeout_task.next = 0; + priv->timeout_task.sync = 0; + priv->timeout_task.routine = (void (*)(void *))orinoco_reset; + priv->timeout_task.data = dev; + +// INIT_TQUEUE(&priv->timeout_task, (void (*)(void *))orinoco_reset, dev); + return dev; } /********************************************************************/ -/* module bookkeeping */ +/* Module initialization */ /********************************************************************/ EXPORT_SYMBOL(alloc_orinocodev); -EXPORT_SYMBOL(orinoco_shutdown); -EXPORT_SYMBOL(orinoco_reset); -EXPORT_SYMBOL(orinoco_proc_dev_init); -EXPORT_SYMBOL(orinoco_proc_dev_cleanup); + +EXPORT_SYMBOL(__orinoco_up); +EXPORT_SYMBOL(__orinoco_down); +EXPORT_SYMBOL(orinoco_reinit_firmware); + EXPORT_SYMBOL(orinoco_interrupt); +/* Can't be declared "const" or the whole __initdata section will + * become const */ +static char version[] __initdata = "orinoco.c 0.13b (David Gibson and others)"; + static int __init init_orinoco(void) { printk(KERN_DEBUG "%s\n", version); - return orinoco_proc_init(); + return 0; } static void __exit exit_orinoco(void) { - orinoco_proc_cleanup(); } module_init(init_orinoco); diff -Nru pcmcia-cs-3.2.3/wireless/orinoco.h pcmcia-cs-3.2.3-new/wireless/orinoco.h --- pcmcia-cs-3.2.3/wireless/orinoco.h Wed May 1 23:33:21 2002 +++ pcmcia-cs-3.2.3-new/wireless/orinoco.h Sun Jan 5 11:04:01 2003 @@ -7,6 +7,13 @@ #ifndef _ORINOCO_H #define _ORINOCO_H +#include +#include +#include +#include +#include +#include "hermes.h" + /* To enable debug messages */ //#define ORINOCO_DEBUG 3 @@ -18,24 +25,26 @@ #define ORINOCO_MAX_KEY_SIZE 14 #define ORINOCO_MAX_KEYS 4 -typedef struct orinoco_key { - u16 len; /* always store little-endian */ +struct orinoco_key { + u16 len; /* always stored as little-endian */ char data[ORINOCO_MAX_KEY_SIZE]; -} __attribute__ ((packed)) orinoco_key_t; +} __attribute__ ((packed)); -typedef orinoco_key_t orinoco_keys_t[ORINOCO_MAX_KEYS]; +#define ORINOCO_INTEN ( HERMES_EV_RX | HERMES_EV_ALLOC | HERMES_EV_TX | \ + HERMES_EV_TXEXC | HERMES_EV_WTERR | HERMES_EV_INFO | \ + HERMES_EV_INFDROP ) -/*====================================================================*/ struct orinoco_private { void *card; /* Pointer to card dependant structure */ - /* card dependant extra reset code (i.e. bus/interface specific */ int (*hard_reset)(struct orinoco_private *); + /* Synchronisation stuff */ spinlock_t lock; - long state; -#define ORINOCO_STATE_INIRQ 0 -#define ORINOCO_STATE_DOIRQ 1 + int hw_unavailable; + struct tq_struct timeout_task; + + int open; /* Net device stuff */ struct net_device *ndev; @@ -58,14 +67,13 @@ int has_preamble; int has_sensitivity; int nicbuf_size; - int broken_cor_reset; u16 channel_mask; /* Configuration paramaters */ u32 iw_mode; int prefer_port3; u16 wep_on, wep_restrict, tx_key; - orinoco_keys_t keys; + struct orinoco_key keys[ORINOCO_MAX_KEYS]; int bitratemode; char nick[IW_ESSID_MAX_SIZE+1]; char desired_essid[IW_ESSID_MAX_SIZE+1]; @@ -81,38 +89,53 @@ #endif /* Configuration dependent variables */ - int port_type, allow_ibss; + int port_type, createibss; int promiscuous, mc_count; - - - /* /proc based debugging stuff */ - struct proc_dir_entry *dir_dev; }; -/*====================================================================*/ - -extern struct list_head orinoco_instances; - #ifdef ORINOCO_DEBUG extern int orinoco_debug; #define DEBUG(n, args...) do { if (orinoco_debug>(n)) printk(KERN_DEBUG args); } while(0) -#define DEBUGMORE(n, args...) do { if (orinoco_debug>(n)) printk(args); } while (0) #else #define DEBUG(n, args...) do { } while (0) -#define DEBUGMORE(n, args...) do { } while (0) #endif /* ORINOCO_DEBUG */ #define TRACE_ENTER(devname) DEBUG(2, "%s: -> " __FUNCTION__ "()\n", devname); #define TRACE_EXIT(devname) DEBUG(2, "%s: <- " __FUNCTION__ "()\n", devname); -#define RUP_EVEN(a) ( (a) % 2 ? (a) + 1 : (a) ) +extern struct net_device *alloc_orinocodev(int sizeof_card, + int (*hard_reset)(struct orinoco_private *)); +extern int __orinoco_up(struct net_device *dev); +extern int __orinoco_down(struct net_device *dev); +int orinoco_reinit_firmware(struct net_device *dev); -/* utility routines */ -struct net_device *alloc_orinocodev(int sizeof_card); -extern void orinoco_shutdown(struct orinoco_private *dev); -extern int orinoco_reset(struct orinoco_private *dev); -extern int orinoco_proc_dev_init(struct orinoco_private *dev); -extern void orinoco_proc_dev_cleanup(struct orinoco_private *priv); extern void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs); + +/********************************************************************/ +/* Locking and synchronization functions */ +/********************************************************************/ + +/* These functions *must* be inline or they will break horribly on + * SPARC, due to its weird semantics for save/restore flags. extern + * inline should prevent the kernel from linking or module from + * loading if they are not inlined. */ +extern inline int orinoco_lock(struct orinoco_private *priv, + unsigned long *flags) +{ + spin_lock_irqsave(&priv->lock, *flags); + if (priv->hw_unavailable) { + printk(KERN_DEBUG "orinoco_lock() called with hw_unavailable (dev=%p)\n", + priv->ndev); + spin_unlock_irqrestore(&priv->lock, *flags); + return -EBUSY; + } + return 0; +} + +extern inline void orinoco_unlock(struct orinoco_private *priv, + unsigned long *flags) +{ + spin_unlock_irqrestore(&priv->lock, *flags); +} #endif /* _ORINOCO_H */ diff -Nru pcmcia-cs-3.2.3/wireless/orinoco_cs.c pcmcia-cs-3.2.3-new/wireless/orinoco_cs.c --- pcmcia-cs-3.2.3/wireless/orinoco_cs.c Sat Jun 29 02:39:33 2002 +++ pcmcia-cs-3.2.3-new/wireless/orinoco_cs.c Sun Jan 5 12:13:16 2003 @@ -1,4 +1,4 @@ -/* orinoco_cs.c 0.11b - (formerly known as dldwd_cs.c) +/* orinoco_cs.c 0.13b - (formerly known as dldwd_cs.c) * * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ @@ -23,12 +23,10 @@ #include #include #include -#include #include #include #include #include -#include #include #include @@ -38,12 +36,11 @@ #include #include -#include "hermes.h" #include "orinoco.h" -/*====================================================================*/ - -static char version[] __initdata = "orinoco_cs.c 0.11b (David Gibson and others)"; +/********************************************************************/ +/* Module stuff */ +/********************************************************************/ MODULE_AUTHOR("David Gibson "); MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar wireless cards"); @@ -51,240 +48,131 @@ MODULE_LICENSE("Dual MPL/GPL"); #endif -/* Parameters that can be set with 'insmod' */ +/* Module parameters */ /* The old way: bit map of interrupts to choose from */ /* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */ static uint irq_mask = 0xdeb8; /* Newer, simpler way of listing specific interrupts */ static int irq_list[4] = { -1 }; -/* Do a Pcmcia soft reset (may help some cards) */ -static int reset_cor = -1; + /* Some D-Link cards have buggy CIS. They do work at 5v properly, but * don't have any CIS entry for it. This workaround it... */ static int ignore_cis_vcc; /* = 0 */ MODULE_PARM(irq_mask, "i"); MODULE_PARM(irq_list, "1-4i"); -MODULE_PARM(reset_cor, "i"); MODULE_PARM(ignore_cis_vcc, "i"); -/* Pcmcia specific structure */ +/********************************************************************/ +/* Magic constants */ +/********************************************************************/ + +/* + * The dev_info variable is the "key" that is used to match up this + * device driver with appropriate cards, through the card + * configuration database. + */ +static dev_info_t dev_info = "orinoco_cs"; + +/********************************************************************/ +/* Data structures */ +/********************************************************************/ + +/* PCMCIA specific device information (goes in the card field of + * struct orinoco_private */ struct orinoco_pccard { dev_link_t link; dev_node_t node; + + /* Used to handle hard reset */ + wait_queue_head_t hard_reset_queue; + int hard_reset_flag; }; /* - * Function prototypes + * A linked list of "instances" of the device. Each actual PCMCIA + * card corresponds to one device instance, and is described by one + * dev_link_t structure (defined in ds.h). */ +static dev_link_t *dev_list; /* = NULL */ -/* struct net_device methods */ -static int orinoco_cs_open(struct net_device *dev); -static int orinoco_cs_stop(struct net_device *dev); +/********************************************************************/ +/* Function prototypes */ +/********************************************************************/ + +/* device methods */ +static int orinoco_cs_hard_reset(struct orinoco_private *priv); /* PCMCIA gumpf */ static void orinoco_cs_config(dev_link_t * link); static void orinoco_cs_release(u_long arg); static int orinoco_cs_event(event_t event, int priority, - event_callback_args_t * args); + event_callback_args_t * args); static dev_link_t *orinoco_cs_attach(void); static void orinoco_cs_detach(dev_link_t *); -/* - The dev_info variable is the "key" that is used to match up this - device driver with appropriate cards, through the card configuration - database. -*/ -static dev_info_t dev_info = "orinoco_cs"; - -/* - A linked list of "instances" of the dummy device. Each actual - PCMCIA card corresponds to one device instance, and is described - by one dev_link_t structure (defined in ds.h). - - You may not want to use a linked list for this -- for example, the - memory card driver uses an array of dev_link_t pointers, where minor - device numbers are used to derive the corresponding array index. -*/ - -static dev_link_t *dev_list; /* = NULL */ - -/*====================================================================*/ - -static void -cs_error(client_handle_t handle, int func, int ret) -{ - error_info_t err = { func, ret }; - CardServices(ReportError, handle, &err); -} +/********************************************************************/ +/* Device methods */ +/********************************************************************/ static int -orinoco_cs_open(struct net_device *dev) +orinoco_cs_hard_reset(struct orinoco_private *priv) { - struct orinoco_private *priv = (struct orinoco_private *)dev->priv; - struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card; + struct orinoco_pccard *card = priv->card; dev_link_t *link = &card->link; int err; - - TRACE_ENTER(dev->name); - - link->open++; - MOD_INC_USE_COUNT; - netif_device_attach(dev); - - err = orinoco_reset(priv); - if (err) { - orinoco_cs_stop(dev); - } else { - netif_mark_up(dev); - netif_start_queue(dev); - } - TRACE_EXIT(dev->name); + card->hard_reset_flag = 0; - return err; -} + err = CardServices(ResetCard, link->handle, NULL); + if (err) + return err; -static int -orinoco_cs_stop(struct net_device *dev) -{ - struct orinoco_private *priv = (struct orinoco_private *)dev->priv; - struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card; - dev_link_t *link = &card->link; + wait_event_interruptible(card->hard_reset_queue, + card->hard_reset_flag); - TRACE_ENTER(dev->name); - - netif_stop_queue(dev); - netif_mark_down(dev); - - orinoco_shutdown(priv); - - link->open--; - MOD_DEC_USE_COUNT; - - if (link->state & DEV_STALE_CONFIG) - mod_timer(&link->release, jiffies + HZ/20); - - TRACE_EXIT(dev->name); - return 0; } -/* - * Do a soft reset of the Pcmcia card using the Configuration Option Register - * Can't do any harm, and actually may do some good on some cards... - * In fact, this seem necessary for Spectrum cards... - */ -static int -orinoco_cs_cor_reset(struct orinoco_private *priv) -{ - struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card; - dev_link_t *link = &card->link; - conf_reg_t reg; - u_int default_cor; - - TRACE_ENTER(priv->ndev->name); - - /* Doing it if hardware is gone is guaranteed crash */ - if(! (link->state & DEV_CONFIG) ) - return -ENODEV; - - /* Save original COR value */ - reg.Function = 0; - reg.Action = CS_READ; - reg.Offset = CISREG_COR; - reg.Value = 0; - CardServices(AccessConfigurationRegister, link->handle, ®); - default_cor = reg.Value; - - DEBUG(2, "orinoco : orinoco_cs_cor_reset() : cor=0x%X\n", default_cor); - - /* Soft-Reset card */ - reg.Action = CS_WRITE; - reg.Offset = CISREG_COR; - reg.Value = (default_cor | COR_SOFT_RESET); - CardServices(AccessConfigurationRegister, link->handle, ®); - - /* Wait until the card has acknowledged our reset */ - /* FIXME: mdelay() is deprecated -dgibson */ - mdelay(1); - -#if 0 /* This seems to help on Symbol cards, but we're not sure why, - and we don't know what it will do to other cards */ - reg.Action = CS_READ; - reg.Offset = CISREG_CCSR; - CardServices(AccessConfigurationRegister, link->handle, ®); - - /* Write 7 (RUN) to CCSR, but preserve the original bit 4 */ - reg.Action = CS_WRITE; - reg.Offset = CISREG_CCSR; - reg.Value = 7 | (reg.Value & 0x10); - CardServices(AccessConfigurationRegister, link->handle, ®); - mdelay(1); -#endif - - /* Restore original COR configuration index */ - reg.Action = CS_WRITE; - reg.Offset = CISREG_COR; - reg.Value = (default_cor & ~COR_SOFT_RESET); - CardServices(AccessConfigurationRegister, link->handle, ®); - - /* Wait until the card has finished restarting */ - /* FIXME: mdelay() is deprecated -dgibson */ - mdelay(1); - - TRACE_EXIT(priv->ndev->name); - - return 0; -} +/********************************************************************/ +/* PCMCIA stuff */ +/********************************************************************/ -static int -orinoco_cs_hard_reset(struct orinoco_private *priv) +static void +cs_error(client_handle_t handle, int func, int ret) { - if (! priv->broken_cor_reset) - return orinoco_cs_cor_reset(priv); - else - return 0; - -#if 0 /* We'd like to use ResetCard, but we can't for the moment - it sleeps */ - /* Not sure what the second parameter is supposed to be - the - PCMCIA code doesn't actually use it */ - if (in_interrupt()) { - printk("Not resetting card, in_interrupt() is true\n"); - return 0; - } else { - printk("Doing ResetCard\n"); - return CardServices(ResetCard, link->handle, NULL); - } -#endif + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); } + /* Remove zombie instances (card removed, detach pending) */ static void flush_stale_links(void) { dev_link_t *link, *next; - TRACE_ENTER("orinoco"); + + TRACE_ENTER(""); + for (link = dev_list; link; link = next) { next = link->next; - if (link->state & DEV_STALE_LINK) + if (link->state & DEV_STALE_LINK) { orinoco_cs_detach(link); + } } - TRACE_EXIT("orinoco"); + TRACE_EXIT(""); } -/*====================================================================== - orinoco_cs_attach() creates an "instance" of the driver, allocating - local data structures for one device. The device is registered - with Card Services. - - The dev_link structure is initialized, but we don't actually - configure the card at this point -- we wait until we receive a - card insertion event. - ======================================================================*/ - +/* + * This creates an "instance" of the driver, allocating local data + * structures for one device. The device is registered with Card + * Services. + * + * The dev_link structure is initialized, but we don't actually + * configure the card at this point -- we wait until we receive a card + * insertion event. */ static dev_link_t * orinoco_cs_attach(void) { @@ -295,24 +183,19 @@ client_reg_t client_reg; int ret, i; - TRACE_ENTER("orinoco"); /* A bit of cleanup */ flush_stale_links(); - dev = alloc_orinocodev(sizeof(*card)); + dev = alloc_orinocodev(sizeof(*card), orinoco_cs_hard_reset); if (! dev) return NULL; priv = dev->priv; card = priv->card; - /* Overrides */ - dev->open = orinoco_cs_open; - dev->stop = orinoco_cs_stop; - priv->hard_reset = orinoco_cs_hard_reset; - init_dev_name(dev, card->node); + init_waitqueue_head(&card->hard_reset_queue); /* Link both structures together */ link = &card->link; - link->priv = priv; + link->priv = dev; /* Initialize the dev_link_t structure */ link->release.function = &orinoco_cs_release; @@ -328,63 +211,59 @@ link->irq.IRQInfo2 |= 1 << irq_list[i]; link->irq.Handler = NULL; - /* - General socket configuration defaults can go here. In this - client, we assume very little, and rely on the CIS for almost - everything. In most clients, many details (i.e., number, sizes, - and attributes of IO windows) are fixed by the nature of the - device, and can be hard-wired here. - */ + /* General socket configuration defaults can go here. In this + * client, we assume very little, and rely on the CIS for + * almost everything. In most clients, many details (i.e., + * number, sizes, and attributes of IO windows) are fixed by + * the nature of the device, and can be hard-wired here. */ link->conf.Attributes = 0; link->conf.IntType = INT_MEMORY_AND_IO; /* Register with Card Services */ + /* FIXME: need a lock? */ link->next = dev_list; dev_list = link; + client_reg.dev_info = &dev_info; client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; client_reg.EventMask = - CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | - CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | - CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; client_reg.event_handler = &orinoco_cs_event; - client_reg.Version = 0x0210; + client_reg.Version = 0x0210; /* FIXME: what does this mean? */ client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); if (ret != CS_SUCCESS) { cs_error(link->handle, RegisterClient, ret); orinoco_cs_detach(link); - link = NULL; - goto out; + return NULL; } - out: - TRACE_EXIT("orinoco"); return link; } /* orinoco_cs_attach */ -/*====================================================================== - This deletes a driver "instance". The device is de-registered - with Card Services. If it has been released, all local data - structures are freed. Otherwise, the structures will be freed - when the device is released. - ======================================================================*/ - +/* + * This deletes a driver "instance". The device is de-registered with + * Card Services. If it has been released, all local data structures + * are freed. Otherwise, the structures will be freed when the device + * is released. + */ static void orinoco_cs_detach(dev_link_t * link) { dev_link_t **linkp; - struct orinoco_private *priv = link->priv; - struct net_device *dev = priv->ndev; - - TRACE_ENTER("orinoco"); + struct net_device *dev = link->priv; /* Locate device structure */ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) if (*linkp == link) break; - if (*linkp == NULL) - goto out; + if (*linkp == NULL) { + BUG(); + return; + } /* If the device is currently configured and active, we won't @@ -398,7 +277,7 @@ "still locked\n", link->dev->dev_name); #endif link->state |= DEV_STALE_LINK; - goto out; + return; } /* Break the link with Card Services */ @@ -410,50 +289,44 @@ DEBUG(0, "orinoco_cs: detach: link=%p link->dev=%p\n", link, link->dev); if (link->dev) { DEBUG(0, "orinoco_cs: About to unregister net device %p\n", - priv->ndev); + dev); unregister_netdev(dev); } kfree(dev); - - out: - TRACE_EXIT("orinoco"); } /* orinoco_cs_detach */ -/*====================================================================== - orinoco_cs_config() is scheduled to run after a CARD_INSERTION event - is received, to configure the PCMCIA socket, and to make the - device available to the system. - ======================================================================*/ +/* + * orinoco_cs_config() is scheduled to run after a CARD_INSERTION + * event is received, to configure the PCMCIA socket, and to make the + * device available to the system. + */ #define CS_CHECK(fn, args...) \ -while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed + while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed #define CFG_CHECK(fn, args...) \ -if (CardServices(fn, args) != 0) goto next_entry + if (CardServices(fn, args) != 0) goto next_entry static void -orinoco_cs_config(dev_link_t * link) +orinoco_cs_config(dev_link_t *link) { + struct net_device *dev = link->priv; client_handle_t handle = link->handle; - struct orinoco_private *priv = link->priv; - struct orinoco_pccard *card = (struct orinoco_pccard *)priv->card; + struct orinoco_private *priv = dev->priv; + struct orinoco_pccard *card = priv->card; hermes_t *hw = &priv->hw; - struct net_device *ndev = priv->ndev; - tuple_t tuple; - cisparse_t parse; int last_fn, last_ret; u_char buf[64]; config_info_t conf; - cistpl_cftable_entry_t dflt = { 0 }; cisinfo_t info; - - TRACE_ENTER("orinoco"); + tuple_t tuple; + cisparse_t parse; CS_CHECK(ValidateCIS, handle, &info); /* - This reads the card's CONFIG tuple to find its configuration - registers. + * This reads the card's CONFIG tuple to find its + * configuration registers. */ tuple.DesiredTuple = CISTPL_CONFIG; tuple.Attributes = 0; @@ -473,31 +346,29 @@ CS_CHECK(GetConfigurationInfo, handle, &conf); link->conf.Vcc = conf.Vcc; - DEBUG(0, "orinoco_cs_config: ConfigBase = 0x%x link->conf.Vcc = %d\n", - link->conf.ConfigBase, link->conf.Vcc); - /* - In this loop, we scan the CIS for configuration table entries, - each of which describes a valid card configuration, including - voltage, IO window, memory window, and interrupt settings. - - We make no assumptions about the card to be configured: we use - just the information available in the CIS. In an ideal world, - this would work for any PCMCIA card, but it requires a complete - and accurate CIS. In practice, a driver usually "knows" most of - these things without consulting the CIS, and most client drivers - will only use the CIS to fill in implementation-defined details. + * In this loop, we scan the CIS for configuration table + * entries, each of which describes a valid card + * configuration, including voltage, IO window, memory window, + * and interrupt settings. + * + * We make no assumptions about the card to be configured: we + * use just the information available in the CIS. In an ideal + * world, this would work for any PCMCIA card, but it requires + * a complete and accurate CIS. In practice, a driver usually + * "knows" most of these things without consulting the CIS, + * and most client drivers will only use the CIS to fill in + * implementation-defined details. */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; CS_CHECK(GetFirstTuple, handle, &tuple); while (1) { cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); + cistpl_cftable_entry_t dflt = { index: 0 }; + CFG_CHECK(GetTupleData, handle, &tuple); CFG_CHECK(ParseTuple, handle, &tuple, &parse); - DEBUG(0, "orinoco_cs_config: index = 0x%x, flags = 0x%x\n", - cfg->index, cfg->flags); - if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; if (cfg->index == 0) @@ -511,7 +382,7 @@ } /* Use power settings for Vcc and Vpp if present */ - /* Note that the CIS values need to be rescaled */ + /* Note that the CIS values need to be rescaled */ if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) { if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) { DEBUG(2, "orinoco_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n", conf.Vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000); @@ -533,8 +404,6 @@ link->conf.Vpp1 = link->conf.Vpp2 = dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; - DEBUG(0, "orinoco_cs_config: We seem to have configured Vcc and Vpp\n"); - /* Do we need to allocate an interrupt? */ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) link->conf.Attributes |= CONF_ENABLE_IRQ; @@ -583,9 +452,9 @@ } /* - Allocate an interrupt line. Note that this does not assign a - handler to the interrupt, unless the 'Handler' member of the - irq structure is initialized. + * Allocate an interrupt line. Note that this does not assign + * a handler to the interrupt, unless the 'Handler' member of + * the irq structure is initialized. */ if (link->conf.Attributes & CONF_ENABLE_IRQ) { int i; @@ -605,33 +474,43 @@ } /* We initialize the hermes structure before completing PCMCIA - configuration just in case the interrupt handler gets - called. */ + * configuration just in case the interrupt handler gets + * called. */ hermes_struct_init(hw, link->io.BasePort1, HERMES_IO, HERMES_16BIT_REGSPACING); /* - This actually configures the PCMCIA socket -- setting up - the I/O windows and the interrupt mapping, and putting the - card and host interface into "Memory and IO" mode. + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping, and putting the + * card and host interface into "Memory and IO" mode. */ CS_CHECK(RequestConfiguration, link->handle, &link->conf); - ndev->base_addr = link->io.BasePort1; - ndev->irq = link->irq.AssignedIRQ; + /* Ok, we have the configuration, prepare to register the netdev */ + dev->base_addr = link->io.BasePort1; + dev->irq = link->irq.AssignedIRQ; + SET_MODULE_OWNER(dev); + card->node.major = card->node.minor = 0; /* register_netdev will give us an ethX name */ - ndev->name[0] = '\0'; + dev->name[0] = '\0'; /* Tell the stack we exist */ - if (register_netdev(ndev) != 0) { + if (register_netdev(dev) != 0) { printk(KERN_ERR "orinoco_cs: register_netdev() failed\n"); goto failed; } - copy_dev_name(card->node, ndev); + + /* At this point, the dev_node_t structure(s) needs to be + * initialized and arranged in a linked list at link->dev. */ + strcpy(card->node.dev_name, dev->name); + link->dev = &card->node; /* link->dev being non-NULL is also + used to indicate that the + net_device has been registered */ + link->state &= ~DEV_CONFIG_PENDING; /* Finally, report what we've done */ printk(KERN_DEBUG "%s: index 0x%02x: Vcc %d.%d", - ndev->name, link->conf.ConfigIndex, + dev->name, link->conf.ConfigIndex, link->conf.Vcc / 10, link->conf.Vcc % 10); if (link->conf.Vpp1) printk(", Vpp %d.%d", link->conf.Vpp1 / 10, @@ -646,70 +525,39 @@ link->io.BasePort2 + link->io.NumPorts2 - 1); printk("\n"); - /* And give us the proc nodes for debugging */ - if (orinoco_proc_dev_init(priv) != 0) { - printk(KERN_ERR "orinoco_cs: Failed to create /proc node for %s\n", - ndev->name); - goto failed; - } - - /* Note to myself : this replace MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT */ - SET_MODULE_OWNER(ndev); - - /* Let reset_cor parameter override determine_firmware()'s guess */ - if (reset_cor != -1) - priv->broken_cor_reset = ! reset_cor; - - /* - At this point, the dev_node_t structure(s) need to be - initialized and arranged in a linked list at link->dev. - */ - card->node.major = card->node.minor = 0; - link->dev = &card->node; - link->state &= ~DEV_CONFIG_PENDING; - - TRACE_EXIT("orinoco"); - return; cs_failed: cs_error(link->handle, last_fn, last_ret); + failed: orinoco_cs_release((u_long) link); - link->state &= ~DEV_CONFIG_PENDING; - - TRACE_EXIT("orinoco"); } /* orinoco_cs_config */ -/*====================================================================== - After a card is removed, orinoco_cs_release() will unregister the - device, and release the PCMCIA configuration. If the device is - still open, this will be postponed until it is closed. - ======================================================================*/ - +/* + * After a card is removed, orinoco_cs_release() will unregister the + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ static void orinoco_cs_release(u_long arg) { dev_link_t *link = (dev_link_t *) arg; - struct orinoco_private *priv = link->priv; - - TRACE_ENTER(link->dev->dev_name); + struct net_device *dev = link->priv; + struct orinoco_private *priv = dev->priv; /* If the device is currently in use, we won't release until it is actually closed, because until then, we can't be sure that no one will try to access the device or its data structures. */ - if (link->open) { + if (priv->open) { DEBUG(0, "orinoco_cs: release postponed, '%s' still open\n", link->dev->dev_name); link->state |= DEV_STALE_CONFIG; return; } - /* Unregister proc entry */ - orinoco_proc_dev_cleanup(priv); - /* Don't bother checking to see if these succeed or not */ CardServices(ReleaseConfiguration, link->handle); if (link->io.NumPorts1) @@ -717,62 +565,75 @@ if (link->irq.AssignedIRQ) CardServices(ReleaseIRQ, link->handle, &link->irq); link->state &= ~DEV_CONFIG; - - TRACE_EXIT(link->dev->dev_name); } /* orinoco_cs_release */ -/*====================================================================== - The card status event handler. Mostly, this schedules other - stuff to run after an event is received. - - When a CARD_REMOVAL event is received, we immediately set a - private flag to block future accesses to this device. All the - functions that actually access the device should check this flag - to make sure the card is still present. - ======================================================================*/ - +/* + * The card status event handler. Mostly, this schedules other stuff + * to run after an event is received. + */ static int orinoco_cs_event(event_t event, int priority, event_callback_args_t * args) { dev_link_t *link = args->client_data; - struct orinoco_private *priv = (struct orinoco_private *)link->priv; - struct net_device *dev = priv->ndev; - - TRACE_ENTER("orinoco"); + struct net_device *dev = link->priv; + struct orinoco_private *priv = dev->priv; + int err = 0; + unsigned long flags; switch (event) { case CS_EVENT_CARD_REMOVAL: link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) { - netif_stop_queue(dev); - } - orinoco_shutdown(priv); - if (link->state & DEV_CONFIG) { + orinoco_lock(priv, &flags); + netif_device_detach(dev); + priv->hw_unavailable = 1; + + orinoco_unlock(priv, &flags); + +/* if (link->open) */ +/* orinoco_cs_stop(dev); */ + mod_timer(&link->release, jiffies + HZ / 20); } break; + case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; orinoco_cs_config(link); break; - case CS_EVENT_PM_SUSPEND: + case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; /* Fall through... */ case CS_EVENT_RESET_PHYSICAL: - orinoco_shutdown(priv); /* Mark the device as stopped, to block IO until later */ - if (link->state & DEV_CONFIG) { - if (link->open) { - netif_stop_queue(dev); - netif_device_detach(dev); + spin_lock_irqsave(&priv->lock, flags); + err = orinoco_lock(priv, &flags); + if (err) { + printk(KERN_ERR "%s: hw_unavailable on SUSPEND/RESET_PHYSICAL\n", + dev->name); + break; } + + err = __orinoco_down(dev); + if (err) + printk(KERN_WARNING "%s: %s: Error %d downing interface\n", + dev->name, + event == CS_EVENT_PM_SUSPEND ? "SUSPEND" : "RESET_PHYSICAL", + err); + + netif_device_detach(dev); + priv->hw_unavailable = 1; + + orinoco_unlock(priv, &flags); + CardServices(ReleaseConfiguration, link->handle); } break; + case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; /* Fall through... */ @@ -781,36 +642,49 @@ CardServices(RequestConfiguration, link->handle, &link->conf); - if (link->open) { - if (orinoco_reset(priv) == 0) { - netif_device_attach(dev); - netif_start_queue(dev); - } else { - printk(KERN_ERR "%s: Error resetting device on PCMCIA event\n", - dev->name); - orinoco_cs_stop(dev); - } + /* FIXME: should we double check that this is + * the same card as we had before */ + err = orinoco_reinit_firmware(dev); + if (err) { + printk(KERN_ERR "%s: Error %d re-initializing firmware\n", + dev->name, err); + break; } + + spin_lock_irqsave(&priv->lock, flags); + + netif_device_attach(dev); + priv->hw_unavailable = 0; + + if (priv->open) { + err = __orinoco_up(dev); + if (err) + printk(KERN_ERR "%s: Error %d restarting card\n", + dev->name, err); + + } + + orinoco_unlock(priv, &flags); } - /* - In a normal driver, additional code may go here to restore - the device state and restart IO. - */ break; } - TRACE_EXIT("orinoco"); - - return 0; + return err; } /* orinoco_cs_event */ +/********************************************************************/ +/* Module initialization */ +/********************************************************************/ + +/* Can't be declared "const" or the whole __initdata section will + * become const */ +static char version[] __initdata = "orinoco_cs.c 0.13b (David Gibson and others)"; + static int __init init_orinoco_cs(void) { servinfo_t serv; - TRACE_ENTER("orinoco"); - printk(KERN_DEBUG "%s\n", version); CardServices(GetCardServicesInfo, &serv); @@ -822,16 +696,12 @@ register_pccard_driver(&dev_info, &orinoco_cs_attach, &orinoco_cs_detach); - - TRACE_EXIT("orinoco"); return 0; } static void __exit exit_orinoco_cs(void) { - TRACE_ENTER("orinoco"); - unregister_pccard_driver(&dev_info); if (dev_list) @@ -842,9 +712,8 @@ orinoco_cs_release((u_long) dev_list); orinoco_cs_detach(dev_list); } - - TRACE_EXIT("orinoco"); } module_init(init_orinoco_cs); module_exit(exit_orinoco_cs); +