Commit 2e3d689f authored by David Gibson's avatar David Gibson Committed by David S. Miller

[PATCH] Update orinoco driver to 0.13e

This updates the orinoco driver, fixing many bugs and adding some minor
features.  It also adds a new module, orinoco_tmd for devices based on
the TMD7168 PCI<->PCMCIA adaptor.
parent 63aff461
......@@ -203,7 +203,7 @@ config PLX_HERMES
depends on PCI && HERMES && EXPERIMENTAL
help
Enable support for PCMCIA cards supported by the "Hermes" (aka
orinoco_cs) driver when used in PLX9052 based PCI adaptors. These
orinoco) driver when used in PLX9052 based PCI adaptors. These
adaptors are not a full PCMCIA controller but act as a more limited
PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that
802.11b PCMCIA cards can be used in desktop machines. The Netgear
......@@ -212,6 +212,19 @@ config PLX_HERMES
Support for these adaptors is so far still incomplete and buggy.
You have been warned.
config TMD_HERMES
tristate "Hermes in TMD7160 based PCI adaptor support (EXPERIMENTAL)"
depends on PCI && HERMES && EXPERIMENTAL
help
Enable support for PCMCIA cards supported by the "Hermes" (aka
orinoco) driver when used in TMD7160 based PCI adaptors. These
adaptors are not a full PCMCIA controller but act as a more limited
PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that
802.11b PCMCIA cards can be used in desktop machines.
Support for these adaptors is so far still incomplete and buggy.
You have been warned.
config PCI_HERMES
tristate "Prism 2.5 PCI 802.11b adaptor support (EXPERIMENTAL)"
depends on PCI && HERMES && EXPERIMENTAL
......
......@@ -15,6 +15,7 @@ obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o
obj-$(CONFIG_APPLE_AIRPORT) += airport.o
obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o
obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o
obj-$(CONFIG_TMD_HERMES) += orinoco_tmd.o
obj-$(CONFIG_AIRO) += airo.o
obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o
......
/* airport.c 0.13a
/* airport.c 0.13e
*
* A driver for "Hermes" chipset based Apple Airport wireless
* card.
......@@ -12,6 +12,7 @@
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
......@@ -20,7 +21,6 @@
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/proc_fs.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
......@@ -95,7 +95,7 @@ airport_sleep_notify(struct pmu_sleep_notifier *self, int when)
netif_device_detach(dev);
priv->hw_unavailable = 1;
priv->hw_unavailable++;
orinoco_unlock(priv, &flags);
......@@ -121,14 +121,15 @@ airport_sleep_notify(struct pmu_sleep_notifier *self, int when)
netif_device_attach(dev);
if (priv->open) {
priv->hw_unavailable--;
if (priv->open && (! priv->hw_unavailable)) {
err = __orinoco_up(dev);
if (err)
printk(KERN_ERR "%s: Error %d restarting card on PBOOK_WAKE\n",
dev->name, err);
}
priv->hw_unavailable = 0;
spin_unlock_irqrestore(&priv->lock, flags);
......@@ -138,6 +139,37 @@ airport_sleep_notify(struct pmu_sleep_notifier *self, int when)
}
#endif /* CONFIG_PMAC_PBOOK */
static int airport_hard_reset(struct orinoco_private *priv)
{
/* It would be nice to power cycle the Airport for a real hard
* reset, but for some reason although it appears to
* re-initialize properly, it falls in a screaming heap
* shortly afterwards. */
#if 0
struct net_device *dev = priv->ndev;
struct airport *card = priv->card;
/* Vitally important. If we don't do this it seems we get an
* interrupt somewhere during the power cycle, since
* hw_unavailable is already set it doesn't get ACKed, we get
* into an interrupt loop and the the PMU decides to turn us
* off. */
disable_irq(dev->irq);
pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0);
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ);
pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 1);
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ);
enable_irq(dev->irq);
schedule_timeout(HZ);
#endif
return 0;
}
static struct net_device *
airport_attach(struct device_node *of_node)
{
......@@ -153,7 +185,7 @@ airport_attach(struct device_node *of_node)
}
/* Allocate space for private device-specific data */
dev = alloc_orinocodev(sizeof(*card), NULL);
dev = alloc_orinocodev(sizeof(*card), airport_hard_reset);
if (! dev) {
printk(KERN_ERR "airport: can't allocate device datas\n");
return NULL;
......@@ -195,7 +227,7 @@ airport_attach(struct device_node *of_node)
/* Reset it before we get the interrupt */
hermes_init(hw);
if (request_irq(dev->irq, orinoco_interrupt, 0, "Airport", (void *)priv)) {
if (request_irq(dev->irq, orinoco_interrupt, 0, "Airport", dev)) {
printk(KERN_ERR "airport: Couldn't get IRQ %d\n", dev->irq);
goto failed;
}
......@@ -209,11 +241,6 @@ airport_attach(struct device_node *of_node)
printk(KERN_DEBUG "airport: card registered for interface %s\n", dev->name);
card->ndev_registered = 1;
/* And give us the proc nodes for debugging */
if (orinoco_proc_dev_init(dev) != 0)
printk(KERN_ERR "airport: Failed to create /proc node for %s\n",
dev->name);
#ifdef CONFIG_PMAC_PBOOK
pmu_register_sleep_notifier(&airport_sleep_notifier);
#endif
......@@ -234,9 +261,6 @@ airport_detach(struct net_device *dev)
struct orinoco_private *priv = dev->priv;
struct airport *card = priv->card;
/* Unregister proc entry */
orinoco_proc_dev_cleanup(dev);
#ifdef CONFIG_PMAC_PBOOK
pmu_unregister_sleep_notifier(&airport_sleep_notifier);
#endif
......@@ -245,7 +269,7 @@ airport_detach(struct net_device *dev)
card->ndev_registered = 0;
if (card->irq_requested)
free_irq(dev->irq, priv);
free_irq(dev->irq, dev);
card->irq_requested = 0;
if (card->vaddr)
......@@ -263,7 +287,7 @@ airport_detach(struct net_device *dev)
kfree(dev);
} /* airport_detach */
static char version[] __initdata = "airport.c 0.13a (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
static char version[] __initdata = "airport.c 0.13e (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("Driver for the Apple Airport wireless card.");
MODULE_LICENSE("Dual MPL/GPL");
......
......@@ -52,7 +52,6 @@
#include "hermes.h"
static char version[] __initdata = "hermes.c: 4 Jul 2002 David Gibson <hermes@gibson.dropbear.id.au>";
MODULE_DESCRIPTION("Low-level driver helper for Lucent Hermes chipset and Prism II HFA384x wireless MAC controller");
MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
#ifdef MODULE_LICENSE
......@@ -226,7 +225,8 @@ int hermes_init(hermes_t *hw)
* Returns: < 0 on internal error, 0 on success, > 0 on error returned by the firmware
*
* Callable from any context, but locking is your problem. */
int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp)
int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0,
hermes_response_t *resp)
{
int err;
int k;
......@@ -402,7 +402,7 @@ static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset)
*
* 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;
......@@ -428,7 +428,7 @@ int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
*
* 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;
......@@ -456,26 +456,30 @@ int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
* 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;
int nwords;
unsigned nwords;
if ( (bufsize < 0) || (bufsize % 2) )
return -EINVAL;
err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL);
if (err)
goto out;
return err;
err = hermes_bap_seek(hw, bap, rid, 0);
if (err)
goto out;
return err;
rlength = hermes_read_reg(hw, dreg);
if (! rlength)
return -ENOENT;
rtype = hermes_read_reg(hw, dreg);
if (length)
......@@ -492,11 +496,10 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize,
IO_TYPE(hw), hw->iobase,
HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength);
nwords = min_t(int, rlength - 1, bufsize / 2);
nwords = min((unsigned)rlength - 1, bufsize / 2);
hermes_read_words(hw, dreg, buf, nwords);
out:
return err;
return 0;
}
int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
......@@ -504,11 +507,14 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
{
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
int err = 0;
int count;
unsigned count;
if (length == 0)
return -EINVAL;
err = hermes_bap_seek(hw, bap, rid, 0);
if (err)
goto out;
return err;
hermes_write_reg(hw, dreg, length);
hermes_write_reg(hw, dreg, rid);
......@@ -520,7 +526,6 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE,
rid, NULL);
out:
return err;
}
......@@ -536,9 +541,12 @@ EXPORT_SYMBOL(hermes_write_ltv);
static int __init init_hermes(void)
{
printk(KERN_DEBUG "%s\n", version);
return 0;
}
static void __exit exit_hermes(void)
{
}
module_init(init_hermes);
module_exit(exit_hermes);
......@@ -250,7 +250,6 @@ struct hermes_scan_frame {
u16 scanreason; /* ??? */
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)
......@@ -278,7 +277,7 @@ struct hermes_debug_entry {
/* Basic control structure */
typedef struct hermes {
ulong iobase;
unsigned long iobase;
int io_space; /* 1 if we IO-mapped IO, 0 for memory-mapped IO? */
#define HERMES_IO 1
#define HERMES_MEM 0
......@@ -316,11 +315,11 @@ 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);
......@@ -361,42 +360,61 @@ static inline int hermes_inquire(hermes_t *hw, u16 rid)
#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;;
if (hw->io_space) {
insw(hw->iobase + off, buf, count);
} else {
int i;
unsigned 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;;
if (hw->io_space) {
outsw(hw->iobase + off, buf, count);
} else {
int i;
unsigned 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);
}
}
}
static inline void hermes_clear_words(struct hermes *hw, int off, unsigned count)
{
unsigned i;
off = off << hw->reg_spacing;;
if (hw->io_space) {
for (i = 0; i < count; i++)
outw(0, hw->iobase + off);
} else {
for (i = 0; i < count; i++)
writew(0, hw->iobase + off);
}
}
#define HERMES_READ_RECORD(hw, bap, rid, buf) \
(hermes_read_ltv((hw),(bap),(rid), sizeof(*buf), NULL, (buf)))
#define HERMES_WRITE_RECORD(hw, bap, rid, buf) \
......
......@@ -2,9 +2,15 @@
#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. Section 6.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)
......
/* orinoco.c 0.13a - (formerly known as dldwd_cs.c and orinoco_cs.c)
/* orinoco.c 0.13e - (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.
......@@ -323,7 +323,7 @@
* the card from hard sleep.
*
* v0.13beta1 -> v0.13 - 27 Sep 2002 - David Gibson
* o Re-introduced full resets (via schedule_work()) on Tx
* o Re-introduced full resets (via schedule_task()) on Tx
* timeout.
*
* v0.13 -> v0.13a - 30 Sep 2002 - David Gibson
......@@ -332,15 +332,70 @@
* o Include required kernel headers in orinoco.h, to avoid
* compile problems.
*
* v0.13a -> v0.13b - 10 Feb 2003 - David Gibson
* o Implemented hard reset for Airport cards
* o Experimental suspend/resume implementation for orinoco_pci
* o Abolished /proc debugging support, replaced with a debugging
* iwpriv. Now it's ugly and simple instead of ugly and complex.
* o Bugfix in hermes.c if the firmware returned a record length
* of 0, we could go clobbering memory.
* o Bugfix in orinoco_stop() - it used to fail if hw_unavailable
* was set, which was usually true on PCMCIA hot removes.
* o Track LINKSTATUS messages, silently drop Tx packets before
* we are connected (avoids cofusing the firmware), and only
* give LINKSTATUS printk()s if the status has changed.
*
* v0.13b -> v0.13c - 11 Mar 2003 - David Gibson
* o Cleanup: use dev instead of priv in various places.
* o Bug fix: Don't ReleaseConfiguration on RESET_PHYSICAL event
* if we're in the middle of a (driver initiated) hard reset.
* o Bug fix: ETH_ZLEN is supposed to include the header
* (Dionysus Blazakis & Manish Karir)
* o Convert to using workqueues instead of taskqueues (and
* backwards compatibility macros for pre 2.5.41 kernels).
* o Drop redundant (I think...) MOD_{INC,DEC}_USE_COUNT in
* airport.c
* o New orinoco_tmd.c init module from Joerg Dorchain for
* TMD7160 based PCI to PCMCIA bridges (similar to
* orinoco_plx.c).
*
* v0.13c -> v0.13d - 22 Apr 2003 - David Gibson
* o Make hw_unavailable a counter, rather than just a flag, this
* is necessary to avoid some races (such as a card being
* removed in the middle of orinoco_reset().
* o Restore Release/RequestConfiguration in the PCMCIA event handler
* when dealing with a driver initiated hard reset. This is
* necessary to prevent hangs due to a spurious interrupt while
* the reset is in progress.
* o Clear the 802.11 header when transmitting, even though we
* don't use it. This fixes a long standing bug on some
* firmwares, which seem to get confused if that isn't done.
* o Be less eager to de-encapsulate SNAP frames, only do so if
* the OUI is 00:00:00 or 00:00:f8, leave others alone. The old
* behaviour broke CDP (Cisco Discovery Protocol).
* o Use dev instead of priv for free_irq() as well as
* request_irq() (oops).
* o Attempt to reset rather than giving up if we get too many
* IRQs.
* o Changed semantics of __orinoco_down() so it can be called
* safely with hw_unavailable set. It also now clears the
* linkstatus (since we're going to have to reassociate).
*
* v0.13d -> v0.13e - 12 May 2003 - David Gibson
* o Support for post-2.5.68 return values from irq handler.
* o Fixed bug where underlength packets would be double counted
* in the rx_dropped statistics.
* o Provided a module parameter to suppress linkstatus messages.
*
* TODO
* o New wireless extensions API (patch forthcoming from Moustafa
* Youssef).
* o New wireless extensions API (patch from Moustafa
* Youssef, updated by Jim Carter and Pavel Roskin).
* o Handle de-encapsulation within network layer, provide 802.11
* headers (patch from Thomas 'Dent' Mirlacher)
* o RF monitor mode support
* o Fix possible races in SPY handling.
* o Disconnect wireless extensions from fundamental configuration.
* o (maybe) Software WEP support (patch from Stano Meduna).
* o (maybe) Convert /proc debugging stuff to seqfile
* o (maybe) Use multiple Tx buffers - driver handling queue
* rather than firmware. */
......@@ -357,7 +412,9 @@
* 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. */
* they are doing and drop the lock again. The orinoco_lock()
* function handles this (it unlocks and returns -EBUSY if
* hw_unavailable is non-zero). */
#include <linux/config.h>
......@@ -369,12 +426,10 @@
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/proc_fs.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
#include <linux/wireless.h>
#include <linux/workqueue.h>
#include <asm/uaccess.h>
#include <asm/io.h>
......@@ -402,6 +457,9 @@ MODULE_PARM(orinoco_debug, "i");
EXPORT_SYMBOL(orinoco_debug);
#endif
static int suppress_linkstatus; /* = 0 */
MODULE_PARM(suppress_linkstatus, "i");
/********************************************************************/
/* Compile time configuration and compatibility stuff */
/********************************************************************/
......@@ -429,8 +487,10 @@ EXPORT_SYMBOL(orinoco_debug);
#define USER_BAP 0
#define IRQ_BAP 1
#define MAX_IRQLOOPS_PER_IRQ 10
#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of how many events the
device can legitimately generate */
#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* Based on a guestimate of
* how many events the
* device could
* legitimately generate */
#define SMALL_KEY_SIZE 5
#define LARGE_KEY_SIZE 13
#define TX_NICBUF_SIZE_BUG 1585 /* Bug in Symbol firmware */
......@@ -456,7 +516,7 @@ const long channel_frequency[] = {
/* 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;
......@@ -508,7 +568,7 @@ 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_program_rids(struct net_device *dev);
static int __orinoco_hw_set_bitrate(struct orinoco_private *priv);
static int __orinoco_hw_setup_wep(struct orinoco_private *priv);
......@@ -521,39 +581,21 @@ static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrate
static void __orinoco_set_multicast_list(struct net_device *dev);
/* Interrupt handling routines */
static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw);
static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw);
static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw);
static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw);
static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw);
static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw);
static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw);
static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw);
static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw);
static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw);
/* ioctl() routines */
static int orinoco_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);
static int orinoco_ioctl_setessid(struct net_device *dev, struct iw_point *erq);
static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq);
static int orinoco_ioctl_setnick(struct net_device *dev, struct iw_point *nrq);
static int orinoco_ioctl_getnick(struct net_device *dev, struct iw_point *nrq);
static int orinoco_ioctl_setfreq(struct net_device *dev, struct iw_freq *frq);
static int orinoco_ioctl_getsens(struct net_device *dev, struct iw_param *srq);
static int orinoco_ioctl_setsens(struct net_device *dev, struct iw_param *srq);
static int orinoco_ioctl_setrts(struct net_device *dev, struct iw_param *rrq);
static int orinoco_ioctl_setfrag(struct net_device *dev, struct iw_param *frq);
static int orinoco_ioctl_getfrag(struct net_device *dev, struct iw_param *frq);
static int orinoco_ioctl_setrate(struct net_device *dev, struct iw_param *frq);
static int orinoco_ioctl_getrate(struct net_device *dev, struct iw_param *frq);
static int orinoco_ioctl_setpower(struct net_device *dev, struct iw_param *prq);
static int orinoco_ioctl_getpower(struct net_device *dev, struct iw_param *prq);
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);
static int orinoco_debug_dump_recs(struct net_device *dev);
/********************************************************************/
/* Function prototypes */
/********************************************************************/
int __orinoco_up(struct net_device *dev)
{
......@@ -561,7 +603,7 @@ int __orinoco_up(struct net_device *dev)
struct hermes *hw = &priv->hw;
int err;
err = __orinoco_program_rids(priv);
err = __orinoco_program_rids(dev);
if (err) {
printk(KERN_ERR "%s: Error %d configuring card\n",
dev->name, err);
......@@ -590,14 +632,25 @@ int __orinoco_down(struct net_device *dev)
netif_stop_queue(dev);
if (! priv->hw_unavailable) {
if (! priv->broken_disableport) {
err = hermes_disable_port(hw, 0);
if (err) {
printk(KERN_ERR "%s: Error %d disabling MAC port\n",
/* Some firmwares (e.g. Intersil 1.3.x) seem
* to have problems disabling the port, oh
* well, too bad. */
printk(KERN_WARNING "%s: Error %d disabling MAC port\n",
dev->name, err);
return err;
priv->broken_disableport = 1;
}
}
hermes_set_irqmask(hw, 0);
hermes_write_regn(hw, EVACK, 0xffff);
}
/* firmware will have to reassociate */
priv->last_linkstatus = 0xffff;
priv->connected = 0;
return 0;
}
......@@ -640,37 +693,38 @@ static int orinoco_open(struct net_device *dev)
if (err)
return err;
priv->open = 1;
err = __orinoco_up(dev);
if (! err)
priv->open = 1;
orinoco_unlock(priv, &flags);
return err;
}
static int orinoco_stop(struct net_device *dev)
int orinoco_stop(struct net_device *dev)
{
struct orinoco_private *priv = dev->priv;
unsigned long flags;
int err;
int err = 0;
err = orinoco_lock(priv, &flags);
if (err)
return err;
/* We mustn't use orinoco_lock() here, because we need to be
able to close the interface even if hw_unavailable is set
(e.g. as we're released after a PC Card removal) */
spin_lock_irq(&priv->lock);
priv->open = 0;
err = __orinoco_down(dev);
orinoco_unlock(priv, &flags);
spin_unlock_irq(&priv->lock);
return err;
}
static int __orinoco_program_rids(struct orinoco_private *priv)
static int __orinoco_program_rids(struct net_device *dev)
{
struct net_device *dev = priv->ndev;
struct orinoco_private *priv = dev->priv;
hermes_t *hw = &priv->hw;
int err;
struct hermes_idstring idbuf;
......@@ -856,33 +910,52 @@ ESSID in IBSS-Ad-Hoc mode.\n", dev->name);
}
/* xyzzy */
static int orinoco_reconfigure(struct orinoco_private *priv)
static int orinoco_reconfigure(struct net_device *dev)
{
struct orinoco_private *priv = dev->priv;
struct hermes *hw = &priv->hw;
unsigned long flags;
int err = 0;
orinoco_lock(priv, &flags);
if (priv->broken_disableport) {
schedule_work(&priv->reset_work);
return 0;
}
err = orinoco_lock(priv, &flags);
if (err)
return err;
err = hermes_disable_port(hw, 0);
if (err) {
printk(KERN_ERR "%s: Unable to disable port in orinco_reconfigure()\n",
priv->ndev->name);
printk(KERN_WARNING "%s: Unable to disable port while reconfiguring card\n",
dev->name);
priv->broken_disableport = 1;
goto out;
}
err = __orinoco_program_rids(priv);
if (err)
err = __orinoco_program_rids(dev);
if (err) {
printk(KERN_WARNING "%s: Unable to reconfigure card\n",
dev->name);
goto out;
}
err = hermes_enable_port(hw, 0);
if (err) {
printk(KERN_ERR "%s: Unable to enable port in orinco_reconfigure()\n",
priv->ndev->name);
printk(KERN_WARNING "%s: Unable to enable port while reconfiguring card\n",
dev->name);
goto out;
}
out:
if (err) {
printk(KERN_WARNING "%s: Resetting instead...\n", dev->name);
schedule_work(&priv->reset_work);
err = 0;
}
orinoco_unlock(priv, &flags);
return err;
......@@ -893,16 +966,28 @@ static int orinoco_reconfigure(struct orinoco_private *priv)
static void orinoco_reset(struct net_device *dev)
{
struct orinoco_private *priv = dev->priv;
struct hermes *hw = &priv->hw;
int err;
unsigned long flags;
printk(KERN_INFO "%s: orinoco_reset()\n", dev->name);
err = orinoco_lock(priv, &flags);
if (err)
/* When the hardware becomes available again, whatever
* detects that is responsible for re-initializing
* it. So no need for anything further*/
return;
priv->hw_unavailable = 1;
netif_stop_queue(dev);
/* Shut off interrupts. Depending on what state the hardware
* is in, this might not work, but we'll try anyway */
hermes_set_irqmask(hw, 0);
hermes_write_regn(hw, EVACK, 0xffff);
priv->hw_unavailable++;
priv->last_linkstatus = 0xffff; /* firmware will have to reassociate */
priv->connected = 0;
orinoco_unlock(priv, &flags);
if (priv->hard_reset)
......@@ -921,18 +1006,22 @@ static void orinoco_reset(struct net_device *dev)
return;
}
spin_lock_irqsave(&priv->lock, flags);
spin_lock_irq(&priv->lock); /* This has to be called from user context */
priv->hw_unavailable = 0;
priv->hw_unavailable--;
/* priv->open or priv->hw_unavailable might have changed while
* we dropped the lock */
if (priv->open && (! priv->hw_unavailable)) {
err = __orinoco_up(dev);
if (err) {
printk(KERN_ERR "%s: orinoco_reset: Error %d reenabling card\n",
dev->name, err);
} else
dev->trans_start = jiffies;
}
orinoco_unlock(priv, &flags);
spin_unlock_irq(&priv->lock);
return;
}
......@@ -964,10 +1053,18 @@ set_port_type(struct orinoco_private *priv)
}
}
/* Does the frame have a SNAP header indicating it should be
* de-encapsulated to Ethernet-II? */
static inline int
is_snap(struct header_struct *hdr)
is_ethersnap(struct header_struct *hdr)
{
return (hdr->dsap == 0xAA) && (hdr->ssap == 0xAA) && (hdr->ctrl == 0x3);
/* We de-encapsulate all packets which, a) have SNAP headers
* (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header
* and where b) the OUI of the SNAP header is 00:00:00 or
* 00:00:f8 - we need both because different APs appear to use
* different OUIs for some reason */
return (memcmp(&hdr->dsap, &encaps_hdr, 5) == 0)
&& ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) );
}
static void
......@@ -1125,7 +1222,8 @@ static int __orinoco_hw_setup_wep(struct orinoco_private *priv)
return 0;
}
static int orinoco_hw_get_bssid(struct orinoco_private *priv, char buf[ETH_ALEN])
static int orinoco_hw_get_bssid(struct orinoco_private *priv,
char buf[ETH_ALEN])
{
hermes_t *hw = &priv->hw;
int err = 0;
......@@ -1221,9 +1319,8 @@ static long orinoco_hw_get_freq(struct orinoco_private *priv)
}
if ( (channel < 1) || (channel > NUM_CHANNELS) ) {
struct net_device *dev = priv->ndev;
printk(KERN_WARNING "%s: Channel out of range (%d)!\n", dev->name, channel);
printk(KERN_WARNING "%s: Channel out of range (%d)!\n",
priv->ndev->name, channel);
err = -EBUSY;
goto out;
......@@ -1238,8 +1335,8 @@ static long orinoco_hw_get_freq(struct orinoco_private *priv)
return err ? err : freq;
}
static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrates,
s32 *rates, int max)
static int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
int *numrates, s32 *rates, int max)
{
hermes_t *hw = &priv->hw;
struct hermes_idstring list;
......@@ -1272,9 +1369,6 @@ static int orinoco_hw_get_bitratelist(struct orinoco_private *priv, int *numrate
}
#if 0
#ifndef ORINOCO_DEBUG
static inline void show_rx_frame(struct orinoco_rxframe_hdr *frame) {}
#else
static void show_rx_frame(struct orinoco_rxframe_hdr *frame)
{
printk(KERN_DEBUG "RX descriptor:\n");
......@@ -1331,17 +1425,16 @@ static void show_rx_frame(struct orinoco_rxframe_hdr *frame)
frame->p8022.oui[0], frame->p8022.oui[1], frame->p8022.oui[2]);
printk(KERN_DEBUG " ethertype = 0x%04x\n", frame->ethertype);
}
#endif
#endif
#endif /* 0 */
/*
* Interrupt handler
*/
irqreturn_t orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct orinoco_private *priv = (struct orinoco_private *) dev_id;
struct net_device *dev = (struct net_device *)dev_id;
struct orinoco_private *priv = dev->priv;
hermes_t *hw = &priv->hw;
struct net_device *dev = priv->ndev;
int count = MAX_IRQLOOPS_PER_IRQ;
u16 evstat, events;
/* These are used to detect a runaway interrupt situation */
......@@ -1352,12 +1445,17 @@ irqreturn_t orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs)
unsigned long flags;
if (orinoco_lock(priv, &flags) != 0) {
/* If hw is unavailable */
return IRQ_NONE;
/* If hw is unavailable - we don't know if the irq was
* for us or not */
return IRQ_HANDLED;
}
evstat = hermes_read_regn(hw, EVSTAT);
events = evstat & hw->inten;
if (! events) {
orinoco_unlock(priv, &flags);
return IRQ_NONE;
}
if (jiffies != last_irq_jiffy)
loops_this_jiffy = 0;
......@@ -1365,11 +1463,11 @@ irqreturn_t orinoco_interrupt(int irq, void *dev_id, struct pt_regs *regs)
while (events && count--) {
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 */
printk(KERN_WARNING "%s: IRQ handler is looping too "
"much! Resetting.\n", dev->name);
/* Disable interrupts for now */
hermes_set_irqmask(hw, 0);
schedule_work(&priv->reset_work);
break;
}
......@@ -1380,21 +1478,21 @@ much! Shutting down.\n",
}
if (events & HERMES_EV_TICK)
__orinoco_ev_tick(priv, hw);
__orinoco_ev_tick(dev, hw);
if (events & HERMES_EV_WTERR)
__orinoco_ev_wterr(priv, hw);
__orinoco_ev_wterr(dev, hw);
if (events & HERMES_EV_INFDROP)
__orinoco_ev_infdrop(priv, hw);
__orinoco_ev_infdrop(dev, hw);
if (events & HERMES_EV_INFO)
__orinoco_ev_info(priv, hw);
__orinoco_ev_info(dev, hw);
if (events & HERMES_EV_RX)
__orinoco_ev_rx(priv, hw);
__orinoco_ev_rx(dev, hw);
if (events & HERMES_EV_TXEXC)
__orinoco_ev_txexc(priv, hw);
__orinoco_ev_txexc(dev, hw);
if (events & HERMES_EV_TX)
__orinoco_ev_tx(priv, hw);
__orinoco_ev_tx(dev, hw);
if (events & HERMES_EV_ALLOC)
__orinoco_ev_alloc(priv, hw);
__orinoco_ev_alloc(dev, hw);
hermes_write_regn(hw, EVACK, events);
......@@ -1403,31 +1501,67 @@ much! Shutting down.\n",
};
orinoco_unlock(priv, &flags);
return IRQ_HANDLED;
}
static void __orinoco_ev_tick(struct orinoco_private *priv, hermes_t *hw)
static void __orinoco_ev_tick(struct net_device *dev, hermes_t *hw)
{
printk(KERN_DEBUG "%s: TICK\n", priv->ndev->name);
printk(KERN_DEBUG "%s: TICK\n", dev->name);
}
static void __orinoco_ev_wterr(struct orinoco_private *priv, hermes_t *hw)
static void __orinoco_ev_wterr(struct net_device *dev, hermes_t *hw)
{
/* This seems to happen a fair bit under load, but ignoring it
seems to work fine...*/
printk(KERN_DEBUG "%s: MAC controller error (WTERR). Ignoring.\n",
priv->ndev->name);
dev->name);
}
static void __orinoco_ev_infdrop(struct orinoco_private *priv, hermes_t *hw)
static void __orinoco_ev_infdrop(struct net_device *dev, hermes_t *hw)
{
printk(KERN_WARNING "%s: Information frame lost.\n", priv->ndev->name);
printk(KERN_WARNING "%s: Information frame lost.\n", dev->name);
}
static void print_linkstatus(struct net_device *dev, u16 status)
{
char * s;
if (suppress_linkstatus)
return;
switch (status) {
case HERMES_LINKSTATUS_NOT_CONNECTED:
s = "Not Connected";
break;
case HERMES_LINKSTATUS_CONNECTED:
s = "Connected";
break;
case HERMES_LINKSTATUS_DISCONNECTED:
s = "Disconnected";
break;
case HERMES_LINKSTATUS_AP_CHANGE:
s = "AP Changed";
break;
case HERMES_LINKSTATUS_AP_OUT_OF_RANGE:
s = "AP Out of Range";
break;
case HERMES_LINKSTATUS_AP_IN_RANGE:
s = "AP In Range";
break;
case HERMES_LINKSTATUS_ASSOC_FAILED:
s = "Association Failed";
break;
default:
s = "UNKNOWN";
}
printk(KERN_INFO "%s: New link status: %s (%04x)\n",
dev->name, s, status);
}
static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw)
static void __orinoco_ev_info(struct net_device *dev, hermes_t *hw)
{
struct net_device *dev = priv->ndev;
struct orinoco_private *priv = dev->priv;
u16 infofid;
struct {
u16 len;
......@@ -1491,7 +1625,6 @@ static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw)
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",
......@@ -1503,34 +1636,20 @@ static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw)
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";
}
if ( (newstatus == HERMES_LINKSTATUS_CONNECTED)
|| (newstatus == HERMES_LINKSTATUS_AP_CHANGE)
|| (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE) )
priv->connected = 1;
else if ( (newstatus == HERMES_LINKSTATUS_NOT_CONNECTED)
|| (newstatus == HERMES_LINKSTATUS_DISCONNECTED)
|| (newstatus == HERMES_LINKSTATUS_AP_OUT_OF_RANGE)
|| (newstatus == HERMES_LINKSTATUS_ASSOC_FAILED) )
priv->connected = 0;
printk(KERN_INFO "%s: New link status: %s (%04x)\n",
dev->name, s, newstatus);
if (newstatus != priv->last_linkstatus)
print_linkstatus(dev, newstatus);
priv->last_linkstatus = newstatus;
}
break;
default:
......@@ -1541,9 +1660,9 @@ static void __orinoco_ev_info(struct orinoco_private *priv, hermes_t *hw)
}
}
static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw)
static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw)
{
struct net_device *dev = priv->ndev;
struct orinoco_private *priv = dev->priv;
struct net_device_stats *stats = &priv->stats;
struct iw_statistics *wstats = &priv->wstats;
struct sk_buff *skb = NULL;
......@@ -1632,14 +1751,13 @@ static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw)
* So, check ourselves */
if(((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_1042) ||
((status & HERMES_RXSTAT_MSGTYPE) == HERMES_RXSTAT_TUNNEL) ||
is_snap(&hdr)) {
is_ethersnap(&hdr)) {
/* These indicate a SNAP within 802.2 LLC within
802.11 frame which we'll need to de-encapsulate to
the original EthernetII frame. */
if (length < ENCAPS_OVERHEAD) { /* No room for full LLC+SNAP */
stats->rx_length_errors++;
stats->rx_dropped++;
goto drop;
}
......@@ -1694,9 +1812,9 @@ static void __orinoco_ev_rx(struct orinoco_private *priv, hermes_t *hw)
return;
}
static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw)
static void __orinoco_ev_txexc(struct net_device *dev, hermes_t *hw)
{
struct net_device *dev = priv->ndev;
struct orinoco_private *priv = dev->priv;
struct net_device_stats *stats = &priv->stats;
u16 fid = hermes_read_regn(hw, TXCOMPLFID);
struct hermes_tx_descriptor desc;
......@@ -1720,8 +1838,9 @@ static void __orinoco_ev_txexc(struct orinoco_private *priv, hermes_t *hw)
hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
}
static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw)
static void __orinoco_ev_tx(struct net_device *dev, hermes_t *hw)
{
struct orinoco_private *priv = dev->priv;
struct net_device_stats *stats = &priv->stats;
stats->tx_packets++;
......@@ -1729,9 +1848,10 @@ static void __orinoco_ev_tx(struct orinoco_private *priv, hermes_t *hw)
hermes_write_regn(hw, TXCOMPLFID, DUMMY_FID);
}
static void __orinoco_ev_alloc(struct orinoco_private *priv, hermes_t *hw)
static void __orinoco_ev_alloc(struct net_device *dev, hermes_t *hw)
{
struct net_device *dev = priv->ndev;
struct orinoco_private *priv = dev->priv;
u16 fid = hermes_read_regn(hw, ALLOCFID);
if (fid != priv->txfid) {
......@@ -1913,7 +2033,7 @@ orinoco_init(struct net_device *dev)
TRACE_ENTER(dev->name);
/* No need to lock, the resetting flag is already set in
/* No need to lock, the hw_unavailable flag is already set in
* alloc_orinocodev() */
priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN;
......@@ -2049,8 +2169,6 @@ orinoco_init(struct net_device *dev)
priv->wep_on = 0;
priv->tx_key = 0;
priv->hw_unavailable = 0;
err = hermes_allocate(hw, priv->nicbuf_size, &priv->txfid);
if (err == -EIO) {
/* Try workaround for old Symbol firmware bug */
......@@ -2070,6 +2188,12 @@ orinoco_init(struct net_device *dev)
goto out;
}
/* Make the hardware available, as long as it hasn't been
* removed elsewhere (e.g. by PCMCIA hot unplug) */
spin_lock_irq(&priv->lock);
priv->hw_unavailable--;
spin_unlock_irq(&priv->lock);
printk(KERN_DEBUG "%s: ready\n", dev->name);
out:
......@@ -2222,9 +2346,20 @@ orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
return 1;
}
if (! priv->connected) {
/* Oops, the firmware hasn't established a connection,
silently drop the packet (this seems to be the
safest approach). */
stats->tx_errors++;
orinoco_unlock(priv, &flags);
dev_kfree_skb(skb);
TRACE_EXIT(dev->name);
return 0;
}
/* Length of the packet body */
/* FIXME: what if the skb is smaller than this? */
len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN);
len = max_t(int,skb->len - ETH_HLEN, ETH_ZLEN - ETH_HLEN);
eh = (struct ethhdr *)skb->data;
......@@ -2238,6 +2373,12 @@ orinoco_xmit(struct sk_buff *skb, struct net_device *dev)
goto fail;
}
/* Clear the 802.11 header and data length fields - some
* firmwares (e.g. Lucent/Agere 8.xx) appear to get confused
* if this isn't done. */
hermes_clear_words(hw, HERMES_DATA0,
HERMES_802_3_OFFSET - HERMES_802_11_OFFSET);
/* Encapsulate Ethernet-II frames */
if (ntohs(eh->h_proto) > 1500) { /* Ethernet-II frame */
struct header_struct hdr;
......@@ -2319,7 +2460,7 @@ orinoco_tx_timeout(struct net_device *dev)
stats->tx_errors++;
schedule_work(&priv->timeout_task);
schedule_work(&priv->reset_work);
}
static int
......@@ -2756,7 +2897,7 @@ static int orinoco_ioctl_getessid(struct net_device *dev, struct iw_point *erq)
erq->flags = 1;
erq->length = strlen(essidbuf) + 1;
if (erq->pointer)
if ( copy_to_user(erq->pointer, essidbuf, erq->length) )
if (copy_to_user(erq->pointer, essidbuf, erq->length))
return -EFAULT;
TRACE_EXIT(dev->name);
......@@ -3688,7 +3829,8 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
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));
......@@ -3710,7 +3852,7 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name);
schedule_work(&priv->timeout_task);
schedule_work(&priv->reset_work);
break;
case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */
......@@ -3782,13 +3924,20 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
err = orinoco_ioctl_getibssport(dev, wrq);
break;
case SIOCIWLASTPRIV:
err = orinoco_debug_dump_recs(dev);
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)) {
err = orinoco_reconfigure(priv);
err = orinoco_reconfigure(dev);
}
TRACE_EXIT(dev->name);
......@@ -3796,81 +3945,6 @@ orinoco_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
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;
}
struct {
u16 rid;
char *name;
......@@ -3880,140 +3954,135 @@ struct {
#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,XSTRING),
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 net_device *dev)
{
struct orinoco_private *priv = (struct orinoco_private *)data;
struct net_device *dev = priv->ndev;
struct orinoco_private *priv = dev->priv;
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);
......@@ -4023,190 +4092,39 @@ orinoco_proc_get_hermes_recs(char *page, char **start, off_t requested_offset,
if (length == 0)
continue;
buf += sprintf(buf, "%-15s (0x%04x): length=%d (%d bytes)\tvalue=", record_table[i].name,
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, PROC_LTV_SIZE);
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;
/* 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;
}
return err;
}
int
orinoco_proc_dev_init(struct net_device *dev)
{
struct orinoco_private *priv = dev->priv;
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 initialize /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(dev);
return -ENOMEM;
}
void
orinoco_proc_dev_cleanup(struct net_device *dev)
{
struct orinoco_private *priv = dev->priv;
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;
}
}
static void
orinoco_proc_cleanup(void)
{
if (dir_base) {
remove_proc_entry("hermes", &proc_root);
dir_base = NULL;
}
}
struct net_device *alloc_orinocodev(int sizeof_card, int (*hard_reset)(struct orinoco_private *))
......@@ -4232,6 +4150,7 @@ struct net_device *alloc_orinocodev(int sizeof_card, int (*hard_reset)(struct or
dev->do_ioctl = orinoco_ioctl;
dev->change_mtu = orinoco_change_mtu;
dev->set_multicast_list = orinoco_set_multicast_list;
/* we use the default eth_mac_addr for setting the MAC addr */
/* Set up default callbacks */
dev->open = orinoco_open;
......@@ -4243,7 +4162,10 @@ struct net_device *alloc_orinocodev(int sizeof_card, int (*hard_reset)(struct or
priv->hw_unavailable = 1; /* orinoco_init() must clear this
* before anything else touches the
* hardware */
INIT_WORK(&priv->timeout_task, (void (*)(void *))orinoco_reset, dev);
INIT_WORK(&priv->reset_work, (void (*)(void *))orinoco_reset, dev);
priv->last_linkstatus = 0xffff;
priv->connected = 0;
return dev;
......@@ -4257,25 +4179,23 @@ EXPORT_SYMBOL(alloc_orinocodev);
EXPORT_SYMBOL(__orinoco_up);
EXPORT_SYMBOL(__orinoco_down);
EXPORT_SYMBOL(orinoco_stop);
EXPORT_SYMBOL(orinoco_reinit_firmware);
EXPORT_SYMBOL(orinoco_proc_dev_init);
EXPORT_SYMBOL(orinoco_proc_dev_cleanup);
EXPORT_SYMBOL(orinoco_interrupt);
/* Can't be declared "const" or the whole __initdata section will
* become const */
static char version[] __initdata = "orinoco.c 0.13a (David Gibson <hermes@gibson.dropbear.id.au> and others)";
static char version[] __initdata = "orinoco.c 0.13e (David Gibson <hermes@gibson.dropbear.id.au> 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);
......
......@@ -11,9 +11,29 @@
#include <linux/spinlock.h>
#include <linux/netdevice.h>
#include <linux/wireless.h>
#include <linux/workqueue.h>
#include <linux/version.h>
#include "hermes.h"
/* Workqueue / task queue backwards compatibility stuff */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
#include <linux/workqueue.h>
#else
#include <linux/tqueue.h>
#define work_struct tq_struct
#define INIT_WORK INIT_TQUEUE
#define schedule_work schedule_task
#endif
/* Interrupt handler backwards compatibility stuff */
#ifndef IRQ_NONE
#define IRQ_NONE
#define IRQ_HANDLED
typedef void irqreturn_t;
#endif
/* To enable debug messages */
//#define ORINOCO_DEBUG 3
......@@ -42,9 +62,12 @@ struct orinoco_private {
/* Synchronisation stuff */
spinlock_t lock;
int hw_unavailable;
struct work_struct timeout_task;
struct work_struct reset_work;
/* driver state */
int open;
u16 last_linkstatus;
int connected;
/* Net device stuff */
struct net_device *ndev;
......@@ -55,6 +78,7 @@ struct orinoco_private {
hermes_t hw;
u16 txfid;
/* Capabilities of the hardware/firmware */
int firmware_type;
#define FIRMWARE_TYPE_AGERE 1
......@@ -68,6 +92,7 @@ struct orinoco_private {
int has_sensitivity;
int nicbuf_size;
u16 channel_mask;
int broken_disableport;
/* Configuration paramaters */
u32 iw_mode;
......@@ -91,9 +116,6 @@ struct orinoco_private {
/* Configuration dependent variables */
int port_type, createibss;
int promiscuous, mc_count;
/* /proc based debugging stuff */
struct proc_dir_entry *dir_dev;
};
#ifdef ORINOCO_DEBUG
......@@ -110,10 +132,8 @@ 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);
extern int orinoco_proc_dev_init(struct net_device *dev);
extern void orinoco_proc_dev_cleanup(struct net_device *dev);
extern int orinoco_stop(struct net_device *dev);
extern int orinoco_reinit_firmware(struct net_device *dev);
extern irqreturn_t orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs);
/********************************************************************/
......
/* orinoco_cs.c 0.13a - (formerly known as dldwd_cs.c)
/* orinoco_cs.c 0.13e - (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/
......@@ -14,6 +14,7 @@
#ifdef __IN_PCMCIA_PACKAGE__
#include <pcmcia/k_compat.h>
#endif /* __IN_PCMCIA_PACKAGE__ */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
......@@ -21,9 +22,7 @@
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/proc_fs.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
......@@ -90,8 +89,9 @@ struct orinoco_pccard {
dev_node_t node;
/* Used to handle hard reset */
wait_queue_head_t hard_reset_queue;
int hard_reset_flag;
/* yuck, we need this hack to work around the insanity of the
* PCMCIA layer */
unsigned long hard_reset_in_progress;
};
/*
......@@ -128,14 +128,14 @@ orinoco_cs_hard_reset(struct orinoco_private *priv)
dev_link_t *link = &card->link;
int err;
card->hard_reset_flag = 0;
/* We need atomic ops here, because we're not holding the lock */
set_bit(0, &card->hard_reset_in_progress);
err = CardServices(ResetCard, link->handle, NULL);
if (err)
return err;
wait_event_interruptible(card->hard_reset_queue,
card->hard_reset_flag);
clear_bit(0, &card->hard_reset_in_progress);
return 0;
}
......@@ -144,6 +144,16 @@ orinoco_cs_hard_reset(struct orinoco_private *priv)
/* PCMCIA stuff */
/********************************************************************/
/* In 2.5 (as of 2.5.69 at least) there is a cs_error exported which
* does this, but it's not in 2.4 so we do our own for now. */
static void
orinoco_cs_error(client_handle_t handle, int func, int ret)
{
error_info_t err = { func, ret };
CardServices(ReportError, handle, &err);
}
/* Remove zombie instances (card removed, detach pending) */
static void
flush_stale_links(void)
......@@ -187,7 +197,6 @@ orinoco_cs_attach(void)
return NULL;
priv = dev->priv;
card = priv->card;
init_waitqueue_head(&card->hard_reset_queue);
/* Link both structures together */
link = &card->link;
......@@ -233,7 +242,7 @@ orinoco_cs_attach(void)
ret = CardServices(RegisterClient, &link->handle, &client_reg);
if (ret != CS_SUCCESS) {
cs_error(link->handle, RegisterClient, ret);
orinoco_cs_error(link->handle, RegisterClient, ret);
orinoco_cs_detach(link);
return NULL;
}
......@@ -262,20 +271,13 @@ orinoco_cs_detach(dev_link_t * link)
return;
}
/*
If the device is currently configured and active, we won't
actually delete it yet. Instead, it is marked so that when
the release() function is called, that will trigger a proper
detach().
*/
if (link->state & DEV_CONFIG) {
#ifdef PCMCIA_DEBUG
printk(KERN_DEBUG "orinoco_cs: detach postponed, '%s' "
"still locked\n", link->dev->dev_name);
#endif
orinoco_cs_release((u_long)link);
if (link->state & DEV_CONFIG) {
link->state |= DEV_STALE_LINK;
return;
}
}
/* Break the link with Card Services */
if (link->handle)
......@@ -465,7 +467,7 @@ orinoco_cs_config(dev_link_t *link)
link->irq.IRQInfo2 |= 1 << irq_list[i];
link->irq.Handler = orinoco_interrupt;
link->irq.Instance = priv;
link->irq.Instance = dev;
CS_CHECK(RequestIRQ, link->handle, &link->irq);
}
......@@ -522,18 +524,10 @@ orinoco_cs_config(dev_link_t *link)
link->io.BasePort2 + link->io.NumPorts2 - 1);
printk("\n");
/* And give us the proc nodes for debugging */
if (orinoco_proc_dev_init(dev) != 0) {
printk(KERN_ERR "orinoco_cs: Failed to create /proc node for %s\n",
dev->name);
goto failed;
}
return;
cs_failed:
cs_error(link->handle, last_fn, last_ret);
orinoco_cs_error(link->handle, last_fn, last_ret);
failed:
orinoco_cs_release((u_long) link);
......@@ -550,21 +544,13 @@ orinoco_cs_release(u_long arg)
dev_link_t *link = (dev_link_t *) arg;
struct net_device *dev = link->priv;
struct orinoco_private *priv = dev->priv;
unsigned long flags;
/*
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 (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(dev);
/* We're committed to taking the device away now, so mark the
* hardware as unavailable */
spin_lock_irqsave(&priv->lock, flags);
priv->hw_unavailable++;
spin_unlock_irqrestore(&priv->lock, flags);
/* Don't bother checking to see if these succeed or not */
CardServices(ReleaseConfiguration, link->handle);
......@@ -586,6 +572,7 @@ orinoco_cs_event(event_t event, int priority,
dev_link_t *link = args->client_data;
struct net_device *dev = link->priv;
struct orinoco_private *priv = dev->priv;
struct orinoco_pccard *card = priv->card;
int err = 0;
unsigned long flags;
......@@ -596,14 +583,9 @@ orinoco_cs_event(event_t event, int priority,
orinoco_lock(priv, &flags);
netif_device_detach(dev);
priv->hw_unavailable = 1;
priv->hw_unavailable++;
orinoco_unlock(priv, &flags);
/* if (link->open) */
/* orinoco_cs_stop(dev); */
mod_timer(&link->release, jiffies + HZ / 20);
}
break;
......@@ -618,12 +600,11 @@ orinoco_cs_event(event_t event, int priority,
case CS_EVENT_RESET_PHYSICAL:
/* Mark the device as stopped, to block IO until later */
if (link->state & DEV_CONFIG) {
err = orinoco_lock(priv, &flags);
if (err) {
printk(KERN_ERR "%s: hw_unavailable on SUSPEND/RESET_PHYSICAL\n",
dev->name);
break;
}
/* This is probably racy, but I can't think of
a better way, short of rewriting the PCMCIA
layer to not suck :-( */
if (! test_bit(0, &card->hard_reset_in_progress)) {
spin_lock_irqsave(&priv->lock, flags);
err = __orinoco_down(dev);
if (err)
......@@ -633,9 +614,10 @@ orinoco_cs_event(event_t event, int priority,
err);
netif_device_detach(dev);
priv->hw_unavailable = 1;
priv->hw_unavailable++;
orinoco_unlock(priv, &flags);
spin_unlock_irqrestore(&priv->lock, flags);
}
CardServices(ReleaseConfiguration, link->handle);
}
......@@ -646,11 +628,12 @@ orinoco_cs_event(event_t event, int priority,
/* Fall through... */
case CS_EVENT_CARD_RESET:
if (link->state & DEV_CONFIG) {
/* FIXME: should we double check that this is
* the same card as we had before */
CardServices(RequestConfiguration, link->handle,
&link->conf);
/* FIXME: should we double check that this is
* the same card as we had before */
if (! test_bit(0, &card->hard_reset_in_progress)) {
err = orinoco_reinit_firmware(dev);
if (err) {
printk(KERN_ERR "%s: Error %d re-initializing firmware\n",
......@@ -661,9 +644,9 @@ orinoco_cs_event(event_t event, int priority,
spin_lock_irqsave(&priv->lock, flags);
netif_device_attach(dev);
priv->hw_unavailable = 0;
priv->hw_unavailable--;
if (priv->open) {
if (priv->open && ! priv->hw_unavailable) {
err = __orinoco_up(dev);
if (err)
printk(KERN_ERR "%s: Error %d restarting card\n",
......@@ -671,7 +654,8 @@ orinoco_cs_event(event_t event, int priority,
}
orinoco_unlock(priv, &flags);
spin_unlock_irqrestore(&priv->lock, flags);
}
}
break;
}
......@@ -685,7 +669,7 @@ orinoco_cs_event(event_t event, int priority,
/* Can't be declared "const" or the whole __initdata section will
* become const */
static char version[] __initdata = "orinoco_cs.c 0.13a (David Gibson <hermes@gibson.dropbear.id.au> and others)";
static char version[] __initdata = "orinoco_cs.c 0.13e (David Gibson <hermes@gibson.dropbear.id.au> and others)";
static struct pcmcia_driver orinoco_driver = {
.owner = THIS_MODULE,
......@@ -712,7 +696,6 @@ exit_orinoco_cs(void)
if (dev_list)
DEBUG(0, "orinoco_cs: Removing leftover devices.\n");
while (dev_list != NULL) {
del_timer(&dev_list->release);
if (dev_list->state & DEV_CONFIG)
orinoco_cs_release((u_long) dev_list);
orinoco_cs_detach(dev_list);
......
/* orinoco_pci.c 0.13a
/* orinoco_pci.c 0.13e
*
* Driver for Prism II devices that have a direct PCI interface
* (i.e., not in a Pcmcia or PLX bridge)
......@@ -10,8 +10,10 @@
* Some of this code is "inspired" by linux-wlan-ng-0.1.10, but nothing
* has been copied from it. linux-wlan-ng-0.1.10 is originally :
* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
* The rest is :
* This file originally written by:
* Copyright (C) 2001 Jean Tourrilhes <jt@hpl.hp.com>
* And is now maintained by:
* Copyright (C) 2002 David Gibson, IBM Corporation <herme@gibson.dropbear.id.au>
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
......@@ -84,6 +86,7 @@
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
......@@ -93,7 +96,6 @@
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <linux/proc_fs.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
......@@ -142,8 +144,6 @@ orinoco_pci_cor_reset(struct orinoco_private *priv)
unsigned long timeout;
u16 reg;
TRACE_ENTER(priv->ndev->name);
/* Assert the reset until the card notice */
hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK);
printk(KERN_NOTICE "Reset done");
......@@ -180,8 +180,6 @@ orinoco_pci_cor_reset(struct orinoco_private *priv)
}
printk(KERN_NOTICE "pci_cor : reg = 0x%X - %lX - %lX\n", reg, timeout, jiffies);
TRACE_EXIT(priv->ndev->name);
return 0;
}
......@@ -197,7 +195,6 @@ static int orinoco_pci_init_one(struct pci_dev *pdev,
unsigned long pci_iolen;
struct orinoco_private *priv = NULL;
struct net_device *dev = NULL;
int netdev_registered = 0;
err = pci_enable_device(pdev);
if (err)
......@@ -218,9 +215,9 @@ static int orinoco_pci_init_one(struct pci_dev *pdev,
}
priv = dev->priv;
dev->base_addr = (int) pci_ioaddr;
dev->mem_start = (unsigned long) pci_iorange;
dev->mem_end = ((unsigned long) pci_iorange) + pci_iolen - 1;
dev->base_addr = (unsigned long) pci_ioaddr;
dev->mem_start = pci_iorange;
dev->mem_end = pci_iorange + pci_iolen - 1;
SET_MODULE_OWNER(dev);
......@@ -228,12 +225,15 @@ static int orinoco_pci_init_one(struct pci_dev *pdev,
"Detected Orinoco/Prism2 PCI device at %s, mem:0x%lX to 0x%lX -> 0x%p, irq:%d\n",
pdev->slot_name, dev->mem_start, dev->mem_end, pci_ioaddr, pdev->irq);
hermes_struct_init(&(priv->hw), dev->base_addr, HERMES_MEM, HERMES_32BIT_REGSPACING);
hermes_struct_init(&priv->hw, dev->base_addr,
HERMES_MEM, HERMES_32BIT_REGSPACING);
pci_set_drvdata(pdev, dev);
err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, priv);
err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ,
dev->name, dev);
if (err) {
printk(KERN_ERR "orinoco_pci: Error allocating IRQ %d.\n", pdev->irq);
printk(KERN_ERR "orinoco_pci: Error allocating IRQ %d.\n",
pdev->irq);
err = -EBUSY;
goto fail;
}
......@@ -254,24 +254,12 @@ static int orinoco_pci_init_one(struct pci_dev *pdev,
printk(KERN_ERR "%s: Failed to register net device\n", dev->name);
goto fail;
}
netdev_registered = 1;
err = orinoco_proc_dev_init(dev);
if (err) {
printk(KERN_ERR "%s: Failed to create /proc node\n", dev->name);
err = -EIO;
goto fail;
}
return 0; /* succeeded */
fail:
if (dev) {
orinoco_proc_dev_cleanup(dev);
if (netdev_registered)
unregister_netdev(dev);
if (dev->irq)
free_irq(dev->irq, priv);
free_irq(dev->irq, dev);
kfree(dev);
}
......@@ -279,6 +267,8 @@ static int orinoco_pci_init_one(struct pci_dev *pdev,
if (pci_ioaddr)
iounmap(pci_ioaddr);
pci_disable_device(pdev);
return err;
}
......@@ -290,21 +280,85 @@ static void __devexit orinoco_pci_remove_one(struct pci_dev *pdev)
if (! dev)
BUG();
orinoco_proc_dev_cleanup(dev);
unregister_netdev(dev);
if (dev->irq)
free_irq(dev->irq, priv);
free_irq(dev->irq, dev);
if (priv->hw.iobase)
iounmap((unsigned char *) priv->hw.iobase);
pci_set_drvdata(pdev, NULL);
kfree(dev);
pci_disable_device(pdev);
}
static int orinoco_pci_suspend(struct pci_dev *pdev, u32 state)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct orinoco_private *priv = dev->priv;
unsigned long flags;
int err;
printk(KERN_DEBUG "%s: Orinoco-PCI entering sleep mode (state=%d)\n",
dev->name, state);
err = orinoco_lock(priv, &flags);
if (err) {
printk(KERN_ERR "%s: hw_unavailable on orinoco_pci_suspend\n",
dev->name);
return err;
}
err = __orinoco_down(dev);
if (err)
printk(KERN_WARNING "%s: orinoco_pci_suspend(): Error %d downing interface\n",
dev->name, err);
netif_device_detach(dev);
priv->hw_unavailable++;
orinoco_unlock(priv, &flags);
return 0;
}
static int orinoco_pci_resume(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct orinoco_private *priv = dev->priv;
unsigned long flags;
int err;
printk(KERN_DEBUG "%s: Orinoco-PCI waking up\n", dev->name);
err = orinoco_reinit_firmware(dev);
if (err) {
printk(KERN_ERR "%s: Error %d re-initializing firmware on orinoco_pci_resume()\n",
dev->name, err);
return err;
}
spin_lock_irqsave(&priv->lock, flags);
netif_device_attach(dev);
priv->hw_unavailable--;
if (priv->open && (! priv->hw_unavailable)) {
err = __orinoco_up(dev);
if (err)
printk(KERN_ERR "%s: Error %d restarting card on orinoco_pci_resume()\n",
dev->name, err);
}
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static struct pci_device_id orinoco_pci_pci_id_table[] __devinitdata = {
{0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,},
{0,},
......@@ -317,12 +371,12 @@ static struct pci_driver orinoco_pci_driver = {
.id_table = orinoco_pci_pci_id_table,
.probe = orinoco_pci_init_one,
.remove = __devexit_p(orinoco_pci_remove_one),
.suspend = 0,
.resume = 0
.suspend = orinoco_pci_suspend,
.resume = orinoco_pci_resume,
};
static char version[] __initdata = "orinoco_pci.c 0.13a (Jean Tourrilhes <jt@hpl.hp.com>)";
MODULE_AUTHOR("Jean Tourrilhes <jt@hpl.hp.com>");
static char version[] __initdata = "orinoco_pci.c 0.13e (David Gibson <hermes@gibson.dropbear.id.au> & Jean Tourrilhes <jt@hpl.hp.com>)";
MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
MODULE_DESCRIPTION("Driver for wireless LAN cards using direct PCI interface");
MODULE_LICENSE("Dual MPL/GPL");
......
/* orinoco_plx.c 0.13a
/* orinoco_plx.c 0.13e
*
* Driver for Prism II devices which would usually be driven by orinoco_cs,
* but are connected to the PCI bus by a PLX9052.
......@@ -119,7 +119,6 @@ not have time for a while..
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/system.h>
#include <linux/proc_fs.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
......@@ -156,7 +155,6 @@ static int orinoco_plx_init_one(struct pci_dev *pdev,
unsigned long pccard_ioaddr = 0;
unsigned long pccard_iolen = 0;
struct net_device *dev = NULL;
int netdev_registered = 0;
int i;
err = pci_enable_device(pdev);
......@@ -201,7 +199,7 @@ static int orinoco_plx_init_one(struct pci_dev *pdev,
addr = pci_resource_start(pdev, 1);
reg = 0;
reg = inl(addr+PLX_INTCSR);
if(reg & PLX_INTCSR_INTEN)
if (reg & PLX_INTCSR_INTEN)
printk(KERN_DEBUG "orinoco_plx: "
"Local Interrupt already enabled\n");
else {
......@@ -244,7 +242,7 @@ static int orinoco_plx_init_one(struct pci_dev *pdev,
HERMES_IO, HERMES_16BIT_REGSPACING);
pci_set_drvdata(pdev, dev);
err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, priv);
err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name, dev);
if (err) {
printk(KERN_ERR "orinoco_plx: Error allocating IRQ %d.\n", pdev->irq);
err = -EBUSY;
......@@ -253,11 +251,6 @@ static int orinoco_plx_init_one(struct pci_dev *pdev,
dev->irq = pdev->irq;
err = register_netdev(dev);
if (err)
goto fail;
netdev_registered = 1;
err = orinoco_proc_dev_init(dev);
if (err)
goto fail;
......@@ -266,16 +259,11 @@ static int orinoco_plx_init_one(struct pci_dev *pdev,
fail:
printk(KERN_DEBUG "orinoco_plx: init_one(), FAIL!\n");
if (priv) {
orinoco_proc_dev_cleanup(dev);
if (netdev_registered)
unregister_netdev(dev);
if (dev) {
if (dev->irq)
free_irq(dev->irq, priv);
free_irq(dev->irq, dev);
kfree(priv);
kfree(dev);
}
if (pccard_ioaddr)
......@@ -292,17 +280,16 @@ static int orinoco_plx_init_one(struct pci_dev *pdev,
static void __devexit orinoco_plx_remove_one(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
struct orinoco_private *priv = dev->priv;
if (! dev)
BUG();
orinoco_proc_dev_cleanup(dev);
unregister_netdev(dev);
if (dev->irq)
free_irq(dev->irq, priv);
free_irq(dev->irq, dev);
pci_set_drvdata(pdev, NULL);
kfree(dev);
......@@ -341,7 +328,7 @@ static struct pci_driver orinoco_plx_driver = {
.resume = 0,
};
static char version[] __initdata = "orinoco_plx.c 0.13a (Daniel Barlow <dan@telent.net>, David Gibson <hermes@gibson.dropbear.id.au>)";
static char version[] __initdata = "orinoco_plx.c 0.13e (Daniel Barlow <dan@telent.net>, David Gibson <hermes@gibson.dropbear.id.au>)";
MODULE_AUTHOR("Daniel Barlow <dan@telent.net>");
MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge");
#ifdef MODULE_LICENSE
......
/* orinoco_tmd.c 0.01
*
* Driver for Prism II devices which would usually be driven by orinoco_cs,
* but are connected to the PCI bus by a TMD7160.
*
* Copyright (C) 2003 Joerg Dorchain <joerg@dorchain.net>
* based heavily upon orinoco_plx.c Copyright (C) 2001 Daniel Barlow <dan@telent.net>
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License
* at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and
* limitations under the License.
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU General Public License version 2 (the "GPL"), in
* which case the provisions of the GPL are applicable instead of the
* above. If you wish to allow the use of your version of this file
* only under the terms of the GPL and not to allow others to use your
* version of this file under the MPL, indicate your decision by
* deleting the provisions above and replace them with the notice and
* other provisions required by the GPL. If you do not delete the
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
* Caution: this is experimental and probably buggy. For success and
* failure reports for different cards and adaptors, see
* orinoco_tmd_pci_id_table near the end of the file. If you have a
* card we don't have the PCI id for, and looks like it should work,
* drop me mail with the id and "it works"/"it doesn't work".
*
* Note: if everything gets detected fine but it doesn't actually send
* or receive packets, your first port of call should probably be to
* try newer firmware in the card. Especially if you're doing Ad-Hoc
* modes
*
* The actual driving is done by orinoco.c, this is just resource
* allocation stuff.
*
* This driver is modeled after the orinoco_plx driver. The main
* difference is that the TMD chip has only IO port ranges and no
* memory space, i.e. no access to the CIS. Compared to the PLX chip,
* the io range functionalities are exchanged.
*
* Pheecom sells cards with the TMD chip as "ASIC version"
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/system.h>
#include <linux/netdevice.h>
#include <linux/if_arp.h>
#include <linux/etherdevice.h>
#include <linux/wireless.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/wireless.h>
#include <linux/fcntl.h>
#include <pcmcia/cisreg.h>
#include "hermes.h"
#include "orinoco.h"
static char dev_info[] = "orinoco_tmd";
#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA | COR_FUNC_ENA) /* Enable PC card with level triggered irqs and irq requests */
static int orinoco_tmd_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int err = 0;
u32 reg, addr;
struct orinoco_private *priv = NULL;
unsigned long pccard_ioaddr = 0;
unsigned long pccard_iolen = 0;
struct net_device *dev = NULL;
err = pci_enable_device(pdev);
if (err)
return -EIO;
printk(KERN_DEBUG "TMD setup\n");
pccard_ioaddr = pci_resource_start(pdev, 2);
pccard_iolen = pci_resource_len(pdev, 2);
if (! request_region(pccard_ioaddr, pccard_iolen, dev_info)) {
printk(KERN_ERR "orinoco_tmd: I/O resource at 0x%lx len 0x%lx busy\n",
pccard_ioaddr, pccard_iolen);
pccard_ioaddr = 0;
err = -EBUSY;
goto fail;
}
addr = pci_resource_start(pdev, 1);
outb(COR_VALUE, addr);
mdelay(1);
reg = inb(addr);
if (reg != COR_VALUE) {
printk(KERN_ERR "orinoco_tmd: Error setting TMD COR values %x should be %x\n", reg, COR_VALUE);
err = -EIO;
goto fail;
}
dev = alloc_orinocodev(0, NULL);
if (! dev) {
err = -ENOMEM;
goto fail;
}
priv = dev->priv;
dev->base_addr = pccard_ioaddr;
SET_MODULE_OWNER(dev);
printk(KERN_DEBUG
"Detected Orinoco/Prism2 TMD device at %s irq:%d, io addr:0x%lx\n",
pdev->slot_name, pdev->irq, pccard_ioaddr);
hermes_struct_init(&(priv->hw), dev->base_addr,
HERMES_IO, HERMES_16BIT_REGSPACING);
pci_set_drvdata(pdev, dev);
err = request_irq(pdev->irq, orinoco_interrupt, SA_SHIRQ, dev->name,
dev);
if (err) {
printk(KERN_ERR "orinoco_tmd: Error allocating IRQ %d.\n",
pdev->irq);
err = -EBUSY;
goto fail;
}
dev->irq = pdev->irq;
err = register_netdev(dev);
if (err)
goto fail;
return 0; /* succeeded */
fail:
printk(KERN_DEBUG "orinoco_tmd: init_one(), FAIL!\n");
if (dev) {
if (dev->irq)
free_irq(dev->irq, dev);
kfree(dev);
}
if (pccard_ioaddr)
release_region(pccard_ioaddr, pccard_iolen);
pci_disable_device(pdev);
return err;
}
static void __devexit orinoco_tmd_remove_one(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
if (! dev)
BUG();
unregister_netdev(dev);
if (dev->irq)
free_irq(dev->irq, dev);
pci_set_drvdata(pdev, NULL);
kfree(dev);
release_region(pci_resource_start(pdev, 2), pci_resource_len(pdev, 2));
pci_disable_device(pdev);
}
static struct pci_device_id orinoco_tmd_pci_id_table[] __devinitdata = {
{0x15e8, 0x0131, PCI_ANY_ID, PCI_ANY_ID,}, /* NDC and OEMs, e.g. pheecom */
{0,},
};
MODULE_DEVICE_TABLE(pci, orinoco_tmd_pci_id_table);
static struct pci_driver orinoco_tmd_driver = {
.name = "orinoco_tmd",
.id_table = orinoco_tmd_pci_id_table,
.probe = orinoco_tmd_init_one,
.remove = __devexit_p(orinoco_tmd_remove_one),
.suspend = 0,
.resume = 0,
};
static char version[] __initdata = "orinoco_tmd.c 0.01 (Joerg Dorchain <joerg@dorchain.net>)";
MODULE_AUTHOR("Joerg Dorchain <joerg@dorchain.net>");
MODULE_DESCRIPTION("Driver for wireless LAN cards using the TMD7160 PCI bridge");
#ifdef MODULE_LICENSE
MODULE_LICENSE("Dual MPL/GPL");
#endif
static int __init orinoco_tmd_init(void)
{
printk(KERN_DEBUG "%s\n", version);
return pci_module_init(&orinoco_tmd_driver);
}
extern void __exit orinoco_tmd_exit(void)
{
pci_unregister_driver(&orinoco_tmd_driver);
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ);
}
module_init(orinoco_tmd_init);
module_exit(orinoco_tmd_exit);
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment