Commit 8cd05eff authored by David Gibson's avatar David Gibson Committed by Linus Torvalds

[PATCH] orinoco driver update

The following patch against 2.5.11 updates the orinoco driver.  As well
as miscellaneous updates to the driver core it adds a new module
supporting Prism 2.5 based PCI wireless cards, and adds a MAINTAINERS
entry for the driver.
parent 3f64554f
...@@ -1180,6 +1180,12 @@ M: zwane@commfireservices.com ...@@ -1180,6 +1180,12 @@ M: zwane@commfireservices.com
L: linux-sound@vger.kernel.org L: linux-sound@vger.kernel.org
S: Maintained S: Maintained
ORINOCO DRIVER
P: David Gibson
M: hermes@gibson.dropbear.id.au
W: http://www.ozlabs.org/people/dgibson/dldwd
S: Maintained
PARALLEL PORT SUPPORT PARALLEL PORT SUPPORT
P: Phil Blundell P: Phil Blundell
M: Philip.Blundell@pobox.com M: Philip.Blundell@pobox.com
......
...@@ -55,6 +55,14 @@ CONFIG_PLX_HERMES ...@@ -55,6 +55,14 @@ CONFIG_PLX_HERMES
Support for these adaptors is so far still incomplete and buggy. Support for these adaptors is so far still incomplete and buggy.
You have been warned. You have been warned.
Prism 2.5 PCI 802.11b adaptor support
CONFIG_PCI_HERMES
Enable support for PCI and mini-PCI 802.11b wireless NICs based on
the Prism 2.5 chipset. These are true PCI cards, not the 802.11b
PCMCIA cards bundled with PCI<->PCMCIA adaptors which are also
common. Some of the built-in wireless adaptors in laptops are of
this variety.
CONFIG_PCMCIA_HERMES CONFIG_PCMCIA_HERMES
A driver for "Hermes" chipset based PCMCIA wireless adaptors, such A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
......
...@@ -20,6 +20,7 @@ fi ...@@ -20,6 +20,7 @@ fi
if [ "$CONFIG_PCI" = "y" ]; then if [ "$CONFIG_PCI" = "y" ]; then
dep_tristate ' Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.)' CONFIG_PLX_HERMES $CONFIG_HERMES $CONFIG_EXPERIMENTAL dep_tristate ' Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.)' CONFIG_PLX_HERMES $CONFIG_HERMES $CONFIG_EXPERIMENTAL
dep_tristate ' Prism 2.5 PCI 802.11b adaptor support' CONFIG_PCI_HERMES $CONFIG_HERMES $CONFIG_EXPERIMENTAL
fi fi
# If Pcmcia is compiled in, offer Pcmcia cards... # If Pcmcia is compiled in, offer Pcmcia cards...
......
...@@ -23,6 +23,7 @@ obj-$(CONFIG_HERMES) += orinoco.o hermes.o ...@@ -23,6 +23,7 @@ obj-$(CONFIG_HERMES) += orinoco.o hermes.o
obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o
obj-$(CONFIG_APPLE_AIRPORT) += airport.o obj-$(CONFIG_APPLE_AIRPORT) += airport.o
obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o
obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o
obj-$(CONFIG_AIRO) += airo.o obj-$(CONFIG_AIRO) += airo.o
obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o obj-$(CONFIG_AIRO_CS) += airo_cs.o airo.o
......
/* airport.c 0.06f /* airport.c 0.11a
* *
* A driver for "Hermes" chipset based Apple Airport wireless * A driver for "Hermes" chipset based Apple Airport wireless
* card. * card.
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
* 0.06 : fix possible hang on powerup, add sleep support * 0.06 : fix possible hang on powerup, add sleep support
*/ */
#include <linux/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -33,26 +35,28 @@ ...@@ -33,26 +35,28 @@
#include <linux/pmu.h> #include <linux/pmu.h>
#include <asm/prom.h> #include <asm/prom.h>
#include <asm/feature.h> #include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/irq.h> #include <asm/irq.h>
#include "hermes.h" #include "hermes.h"
#include "orinoco.h" #include "orinoco.h"
static char version[] __initdata = "airport.c 0.06f (Benjamin Herrenschmidt <benh@kernel.crashing.org>)"; static char version[] __initdata = "airport.c 0.11a (Benjamin Herrenschmidt <benh@kernel.crashing.org>)";
MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
MODULE_DESCRIPTION("Driver for the Apple Airport wireless card."); MODULE_DESCRIPTION("Driver for the Apple Airport wireless card.");
MODULE_LICENSE("Dual MPL/GPL"); MODULE_LICENSE("Dual MPL/GPL");
EXPORT_NO_SYMBOLS; EXPORT_NO_SYMBOLS;
typedef struct dldwd_card { #define AIRPORT_IO_LEN (0x1000) /* one page */
struct airport {
struct device_node* node; struct device_node* node;
void *vaddr;
int irq_requested; int irq_requested;
int ndev_registered; int ndev_registered;
int open; int open;
/* Common structure (fully included), see orinoco.h */ };
struct dldwd_priv priv;
} dldwd_card_t;
#ifdef CONFIG_PMAC_PBOOK #ifdef CONFIG_PMAC_PBOOK
static int airport_sleep_notify(struct pmu_sleep_notifier *self, int when); static int airport_sleep_notify(struct pmu_sleep_notifier *self, int when);
...@@ -65,9 +69,8 @@ static struct pmu_sleep_notifier airport_sleep_notifier = { ...@@ -65,9 +69,8 @@ static struct pmu_sleep_notifier airport_sleep_notifier = {
* Function prototypes * Function prototypes
*/ */
static dldwd_priv_t* airport_attach(struct device_node *of_node); static struct net_device *airport_attach(struct device_node *of_node);
static void airport_detach(dldwd_priv_t* priv); static void airport_detach(struct net_device *dev);
static int airport_init(struct net_device *dev);
static int airport_open(struct net_device *dev); static int airport_open(struct net_device *dev);
static int airport_stop(struct net_device *dev); static int airport_stop(struct net_device *dev);
...@@ -81,44 +84,28 @@ static int airport_stop(struct net_device *dev); ...@@ -81,44 +84,28 @@ static int airport_stop(struct net_device *dev);
device numbers are used to derive the corresponding array index. device numbers are used to derive the corresponding array index.
*/ */
static dldwd_priv_t *airport_dev; static struct net_device *airport_dev;
static int airport_init(struct net_device *dev)
{
dldwd_priv_t *priv = dev->priv;
int rc;
TRACE_ENTER(priv->ndev.name);
MOD_INC_USE_COUNT;
rc = dldwd_init(dev);
if (!rc)
priv->hw_ready = 1;
MOD_DEC_USE_COUNT;
return rc;
}
static int static int
airport_open(struct net_device *dev) airport_open(struct net_device *dev)
{ {
dldwd_priv_t *priv = dev->priv; struct orinoco_private *priv = dev->priv;
dldwd_card_t* card = (dldwd_card_t *)priv->card; struct airport* card = (struct airport *)priv->card;
int rc; int rc;
TRACE_ENTER(priv->ndev.name); TRACE_ENTER(dev->name);
rc = dldwd_reset(priv); netif_device_attach(dev);
rc = orinoco_reset(priv);
if (rc) if (rc)
airport_stop(dev); airport_stop(dev);
else { else {
card->open = 1; card->open = 1;
netif_device_attach(dev); netif_start_queue(dev);
} }
// TRACE_EXIT(priv->ndev.name); TRACE_EXIT(dev->name);
return rc; return rc;
} }
...@@ -126,16 +113,16 @@ airport_open(struct net_device *dev) ...@@ -126,16 +113,16 @@ airport_open(struct net_device *dev)
static int static int
airport_stop(struct net_device *dev) airport_stop(struct net_device *dev)
{ {
dldwd_priv_t *priv = dev->priv; struct orinoco_private *priv = dev->priv;
dldwd_card_t* card = (dldwd_card_t *)priv->card; struct airport* card = (struct airport *)priv->card;
TRACE_ENTER(priv->ndev.name); TRACE_ENTER(dev->name);
netif_stop_queue(dev); netif_stop_queue(dev);
dldwd_shutdown(priv); orinoco_shutdown(priv);
card->open = 0; card->open = 0;
TRACE_EXIT(priv->ndev.name); TRACE_EXIT(dev->name);
return 0; return 0;
} }
...@@ -144,16 +131,14 @@ airport_stop(struct net_device *dev) ...@@ -144,16 +131,14 @@ airport_stop(struct net_device *dev)
static int static int
airport_sleep_notify(struct pmu_sleep_notifier *self, int when) airport_sleep_notify(struct pmu_sleep_notifier *self, int when)
{ {
dldwd_priv_t *priv; struct net_device *dev = airport_dev;
struct net_device *ndev; struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
dldwd_card_t* card; struct hermes *hw = &priv->hw;
struct airport* card = (struct airport *)priv->card;
int rc; int rc;
if (!airport_dev) if (! airport_dev)
return PBOOK_SLEEP_OK; return PBOOK_SLEEP_OK;
priv = airport_dev;
ndev = &priv->ndev;
card = (dldwd_card_t *)priv->card;
switch (when) { switch (when) {
case PBOOK_SLEEP_REQUEST: case PBOOK_SLEEP_REQUEST:
...@@ -161,41 +146,42 @@ airport_sleep_notify(struct pmu_sleep_notifier *self, int when) ...@@ -161,41 +146,42 @@ airport_sleep_notify(struct pmu_sleep_notifier *self, int when)
case PBOOK_SLEEP_REJECT: case PBOOK_SLEEP_REJECT:
break; break;
case PBOOK_SLEEP_NOW: case PBOOK_SLEEP_NOW:
printk(KERN_INFO "%s: Airport entering sleep mode\n", ndev->name); printk(KERN_INFO "%s: Airport entering sleep mode\n", dev->name);
netif_device_detach(ndev); if (card->open) {
if (card->open) netif_stop_queue(dev);
dldwd_shutdown(priv); orinoco_shutdown(priv);
disable_irq(ndev->irq); netif_device_detach(dev);
feature_set_airport_power(card->node, 0); }
priv->hw_ready = 0; disable_irq(dev->irq);
pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0);
break; break;
case PBOOK_WAKE: case PBOOK_WAKE:
printk(KERN_INFO "%s: Airport waking up\n", ndev->name); printk(KERN_INFO "%s: Airport waking up\n", dev->name);
feature_set_airport_power(card->node, 1); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 1);
mdelay(200); mdelay(200);
hermes_reset(&priv->hw); hermes_reset(hw);
priv->hw_ready = 1; rc = orinoco_reset(priv);
rc = dldwd_reset(priv);
if (rc) if (rc)
printk(KERN_ERR "airport: Error %d re-initing card !\n", rc); printk(KERN_ERR "airport: Error %d re-initing card !\n", rc);
else if (card->open) else if (card->open)
netif_device_attach(ndev); netif_device_attach(dev);
enable_irq(ndev->irq); enable_irq(dev->irq);
break; break;
} }
return PBOOK_SLEEP_OK; return PBOOK_SLEEP_OK;
} }
#endif /* CONFIG_PMAC_PBOOK */ #endif /* CONFIG_PMAC_PBOOK */
static dldwd_priv_t* static struct net_device *
airport_attach(struct device_node* of_node) airport_attach(struct device_node* of_node)
{ {
dldwd_priv_t *priv; struct orinoco_private *priv;
struct net_device *ndev; struct net_device *dev;
dldwd_card_t* card; struct airport *card;
unsigned long phys_addr;
hermes_t *hw; hermes_t *hw;
TRACE_ENTER("dldwd"); TRACE_ENTER("orinoco");
if (of_node->n_addrs < 1 || of_node->n_intrs < 1) { if (of_node->n_addrs < 1 || of_node->n_intrs < 1) {
printk(KERN_ERR "airport: wrong interrupt/addresses in OF tree\n"); printk(KERN_ERR "airport: wrong interrupt/addresses in OF tree\n");
...@@ -203,74 +189,77 @@ airport_attach(struct device_node* of_node) ...@@ -203,74 +189,77 @@ airport_attach(struct device_node* of_node)
} }
/* Allocate space for private device-specific data */ /* Allocate space for private device-specific data */
card = kmalloc(sizeof(*card), GFP_KERNEL); dev = alloc_orinocodev(sizeof(*card));
if (!card) { if (! dev) {
printk(KERN_ERR "airport: can't allocate device datas\n"); printk(KERN_ERR "airport: can't allocate device datas\n");
return NULL; return NULL;
} }
memset(card, 0, sizeof(*card)); priv = dev->priv;
card = priv->card;
priv = &(card->priv);
priv->card = card;
ndev = &priv->ndev;
hw = &priv->hw; hw = &priv->hw;
card->node = of_node; card->node = of_node;
/* Setup the common part */ if (! request_OF_resource(of_node, 0, " (airport)")) {
if (dldwd_setup(priv) < 0) { printk(KERN_ERR "airport: can't request IO resource !\n");
kfree(card); kfree(dev);
return NULL; return NULL;
} }
dev->name[0] = '\0'; /* register_netdev will give us an ethX name */
SET_MODULE_OWNER(dev);
/* Overrides */ /* Overrides */
ndev->init = airport_init; dev->open = airport_open;
ndev->open = airport_open; dev->stop = airport_stop;
ndev->stop = airport_stop;
/* Setup interrupts & base address */ /* Setup interrupts & base address */
ndev->irq = of_node->intrs[0].line; dev->irq = of_node->intrs[0].line;
ndev->base_addr = (unsigned long)ioremap(of_node->addrs[0].address, 0x1000) - _IO_BASE; phys_addr = of_node->addrs[0].address; /* Physical address */
dev->base_addr = phys_addr;
card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN);
if (! card->vaddr) {
printk("airport: ioremap() failed\n");
goto failed;
}
hermes_struct_init(hw, ndev->base_addr); hermes_struct_init(hw, (ulong)card->vaddr,
HERMES_MEM, HERMES_16BIT_REGSPACING);
/* Power up card */ /* Power up card */
feature_set_airport_power(card->node, 1); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 1);
current->state = TASK_UNINTERRUPTIBLE; current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ); schedule_timeout(HZ);
/* Reset it before we get the interrupt */ /* Reset it before we get the interrupt */
hermes_reset(hw); hermes_reset(hw);
if (request_irq(ndev->irq, dldwd_interrupt, 0, "Airport", (void *)priv)) { if (request_irq(dev->irq, orinoco_interrupt, 0, "Airport", (void *)priv)) {
printk(KERN_ERR "airport: Couldn't get IRQ %d\n", ndev->irq); printk(KERN_ERR "airport: Couldn't get IRQ %d\n", dev->irq);
goto failed; goto failed;
} }
card->irq_requested = 1; card->irq_requested = 1;
/* register_netdev will give us an ethX name */
ndev->name[0] = '\0';
/* Tell the stack we exist */ /* Tell the stack we exist */
if (register_netdev(ndev) != 0) { if (register_netdev(dev) != 0) {
printk(KERN_ERR "airport: register_netdev() failed\n"); printk(KERN_ERR "airport: register_netdev() failed\n");
goto failed; goto failed;
} }
printk(KERN_DEBUG "airport: card registered for interface %s\n", ndev->name); printk(KERN_DEBUG "airport: card registered for interface %s\n", dev->name);
card->ndev_registered = 1; card->ndev_registered = 1;
SET_MODULE_OWNER(ndev);
/* And give us the proc nodes for debugging */ /* And give us the proc nodes for debugging */
if (dldwd_proc_dev_init(priv) != 0) if (orinoco_proc_dev_init(priv) != 0)
printk(KERN_ERR "airport: Failed to create /proc node for %s\n", printk(KERN_ERR "airport: Failed to create /proc node for %s\n",
ndev->name); dev->name);
#ifdef CONFIG_PMAC_PBOOK #ifdef CONFIG_PMAC_PBOOK
pmu_register_sleep_notifier(&airport_sleep_notifier); pmu_register_sleep_notifier(&airport_sleep_notifier);
#endif #endif
return priv; return dev;
failed: failed:
airport_detach(priv); airport_detach(dev);
return NULL; return NULL;
} /* airport_attach */ } /* airport_attach */
...@@ -279,32 +268,34 @@ airport_attach(struct device_node* of_node) ...@@ -279,32 +268,34 @@ airport_attach(struct device_node* of_node)
======================================================================*/ ======================================================================*/
static void static void
airport_detach(dldwd_priv_t *priv) airport_detach(struct net_device *dev)
{ {
dldwd_card_t* card = (dldwd_card_t *)priv->card; struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
struct airport *card = (struct airport *)priv->card;
priv->hw_ready = 0;
/* Unregister proc entry */ /* Unregister proc entry */
dldwd_proc_dev_cleanup(priv); orinoco_proc_dev_cleanup(priv);
#ifdef CONFIG_PMAC_PBOOK #ifdef CONFIG_PMAC_PBOOK
pmu_unregister_sleep_notifier(&airport_sleep_notifier); pmu_unregister_sleep_notifier(&airport_sleep_notifier);
#endif #endif
if (card->ndev_registered) if (card->ndev_registered)
unregister_netdev(&priv->ndev); unregister_netdev(dev);
card->ndev_registered = 0; card->ndev_registered = 0;
if (card->irq_requested) if (card->irq_requested)
free_irq(priv->ndev.irq, priv); free_irq(dev->irq, priv);
card->irq_requested = 0; card->irq_requested = 0;
// FIXME if (card->vaddr)
// if (ndev->base_addr) iounmap(card->vaddr);
// iounmap(ndev->base_addr + _IO_BASE); card->vaddr = 0;
// ndev->base_addr = 0;
dev->base_addr = 0;
release_OF_resource(card->node, 0);
feature_set_airport_power(card->node, 0); pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0);
current->state = TASK_UNINTERRUPTIBLE; current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ); schedule_timeout(HZ);
......
...@@ -53,17 +53,18 @@ ...@@ -53,17 +53,18 @@
#include "hermes.h" #include "hermes.h"
static char version[] __initdata = "hermes.c: 3 Oct 2001 David Gibson <hermes@gibson.dropbear.id.au>"; static char version[] __initdata = "hermes.c: 5 Apr 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_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>"); MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
#ifdef MODULE_LICENSE
MODULE_LICENSE("Dual MPL/GPL"); MODULE_LICENSE("Dual MPL/GPL");
#endif
/* These are maximum timeouts. Most often, card wil react much faster */ /* These are maximum timeouts. Most often, card wil react much faster */
#define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */ #define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */
#define CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */ #define CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */
#define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */ #define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */
#define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */ #define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */
#define BAP_BUSY_TIMEOUT (500) /* In iterations of ~1us */
/* /*
* Debugging helpers * Debugging helpers
...@@ -75,9 +76,9 @@ MODULE_LICENSE("Dual MPL/GPL"); ...@@ -75,9 +76,9 @@ MODULE_LICENSE("Dual MPL/GPL");
#include <stdarg.h> #include <stdarg.h>
#define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ 0x%x: " , hw->iobase); \ #define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ 0x%x: " , hw->iobase); \
printk(#stuff);} while (0) printk(stuff);} while (0)
#define DEBUG(lvl, stuff...) if ( (lvl) <= HERMES_DEBUG) DMSG(#stuff) #define DEBUG(lvl, stuff...) if ( (lvl) <= HERMES_DEBUG) DMSG(stuff)
#else /* ! HERMES_DEBUG */ #else /* ! HERMES_DEBUG */
...@@ -85,6 +86,8 @@ MODULE_LICENSE("Dual MPL/GPL"); ...@@ -85,6 +86,8 @@ MODULE_LICENSE("Dual MPL/GPL");
#endif /* ! HERMES_DEBUG */ #endif /* ! HERMES_DEBUG */
#define IO_TYPE(hw) ((hw)->io_space ? "IO " : "MEM ")
/* /*
* Internal functions * Internal functions
*/ */
...@@ -98,10 +101,17 @@ MODULE_LICENSE("Dual MPL/GPL"); ...@@ -98,10 +101,17 @@ MODULE_LICENSE("Dual MPL/GPL");
*/ */
static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0) static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0)
{ {
int k = CMD_BUSY_TIMEOUT;
u16 reg; u16 reg;
/* First check that the command register is not busy */ /* First wait for the command register to unbusy */
reg = hermes_read_regn(hw, CMD); reg = hermes_read_regn(hw, CMD);
while ( (reg & HERMES_CMD_BUSY) && k ) {
k--;
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) { if (reg & HERMES_CMD_BUSY) {
return -EBUSY; return -EBUSY;
} }
...@@ -118,10 +128,19 @@ static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0) ...@@ -118,10 +128,19 @@ static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0)
* Function definitions * Function definitions
*/ */
void hermes_struct_init(hermes_t *hw, uint io) void hermes_struct_init(hermes_t *hw, ulong address,
int io_space, int reg_spacing)
{ {
hw->iobase = io; hw->iobase = address;
hw->io_space = io_space;
hw->reg_spacing = reg_spacing;
hw->inten = 0x0; hw->inten = 0x0;
#ifdef HERMES_DEBUG_BUFFER
hw->dbufp = 0;
memset(&hw->dbuf, 0xff, sizeof(hw->dbuf));
memset(&hw->profile, 0, sizeof(hw->profile));
#endif
} }
int hermes_reset(hermes_t *hw) int hermes_reset(hermes_t *hw)
...@@ -188,8 +207,9 @@ int hermes_reset(hermes_t *hw) ...@@ -188,8 +207,9 @@ int hermes_reset(hermes_t *hw)
} }
if (! (reg & HERMES_EV_CMD)) { if (! (reg & HERMES_EV_CMD)) {
printk(KERN_ERR "hermes @ 0x%x: Timeout waiting for card to reset (reg=0x%04x)!\n", printk(KERN_ERR "hermes @ %s0x%lx: "
hw->iobase, reg); "Timeout waiting for card to reset (reg=0x%04x)!\n",
IO_TYPE(hw), hw->iobase, reg);
err = -ETIMEDOUT; err = -ETIMEDOUT;
goto out; goto out;
} }
...@@ -198,7 +218,8 @@ int hermes_reset(hermes_t *hw) ...@@ -198,7 +218,8 @@ int hermes_reset(hermes_t *hw)
hermes_write_regn(hw, EVACK, HERMES_EV_CMD); hermes_write_regn(hw, EVACK, HERMES_EV_CMD);
err = status & HERMES_STATUS_RESULT; if (status & HERMES_STATUS_RESULT)
err = -EIO;
out: out:
return err; return err;
...@@ -215,16 +236,18 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp) ...@@ -215,16 +236,18 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp)
int err; int err;
int k; int k;
u16 reg; u16 reg;
u16 status;
err = hermes_issue_cmd(hw, cmd, parm0); err = hermes_issue_cmd(hw, cmd, parm0);
if (err) { if (err) {
if (! hermes_present(hw)) { if (! hermes_present(hw)) {
printk(KERN_WARNING "hermes @ 0x%x: Card removed while issuing command.\n", printk(KERN_WARNING "hermes @ %s0x%lx: "
hw->iobase); "Card removed while issuing command.\n",
IO_TYPE(hw), hw->iobase);
err = -ENODEV; err = -ENODEV;
} else } else
printk(KERN_ERR "hermes @ 0x%x: CMD register busy in hermes_issue_command().\n", printk(KERN_ERR "hermes @ %s0x%lx: Error %d issuing command.\n",
hw->iobase); IO_TYPE(hw), hw->iobase, err);
goto out; goto out;
} }
...@@ -237,27 +260,33 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp) ...@@ -237,27 +260,33 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp)
} }
if (! hermes_present(hw)) { if (! hermes_present(hw)) {
printk(KERN_WARNING "hermes @ 0x%x: Card removed while waiting for command completion.\n", printk(KERN_WARNING "hermes @ %s0x%lx: "
hw->iobase); "Card removed while waiting for command completion.\n",
IO_TYPE(hw), hw->iobase);
err = -ENODEV; err = -ENODEV;
goto out; goto out;
} }
if (! (reg & HERMES_EV_CMD)) { if (! (reg & HERMES_EV_CMD)) {
printk(KERN_ERR "hermes @ 0x%x: Timeout waiting for command completion.\n", printk(KERN_ERR "hermes @ %s0x%lx: "
hw->iobase); "Timeout waiting for command completion.\n",
IO_TYPE(hw), hw->iobase);
err = -ETIMEDOUT; err = -ETIMEDOUT;
goto out; goto out;
} }
resp->status = hermes_read_regn(hw, STATUS); status = hermes_read_regn(hw, STATUS);
resp->resp0 = hermes_read_regn(hw, RESP0); if (resp) {
resp->resp1 = hermes_read_regn(hw, RESP1); resp->status = status;
resp->resp2 = hermes_read_regn(hw, RESP2); resp->resp0 = hermes_read_regn(hw, RESP0);
resp->resp1 = hermes_read_regn(hw, RESP1);
resp->resp2 = hermes_read_regn(hw, RESP2);
}
hermes_write_regn(hw, EVACK, HERMES_EV_CMD); hermes_write_regn(hw, EVACK, HERMES_EV_CMD);
err = resp->status & HERMES_STATUS_RESULT; if (status & HERMES_STATUS_RESULT)
err = -EIO;
out: out:
return err; return err;
...@@ -266,17 +295,17 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp) ...@@ -266,17 +295,17 @@ 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_allocate(hermes_t *hw, u16 size, u16 *fid)
{ {
int err = 0; int err = 0;
hermes_response_t resp;
int k; int k;
u16 reg; u16 reg;
if ( (size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX) ) if ( (size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX) )
return -EINVAL; return -EINVAL;
err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, &resp); err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, NULL);
if (err) { if (err) {
printk(KERN_WARNING "hermes @ 0x%x: Frame allocation command failed (0x%X).\n", printk(KERN_WARNING "hermes @ %s0x%lx: "
hw->iobase, err); "Frame allocation command failed (0x%X).\n",
IO_TYPE(hw), hw->iobase, err);
return err; return err;
} }
...@@ -289,14 +318,16 @@ int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) ...@@ -289,14 +318,16 @@ int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)
} }
if (! hermes_present(hw)) { if (! hermes_present(hw)) {
printk(KERN_WARNING "hermes @ 0x%x: Card removed waiting for frame allocation.\n", printk(KERN_WARNING "hermes @ %s0x%lx: "
hw->iobase); "Card removed waiting for frame allocation.\n",
IO_TYPE(hw), hw->iobase);
return -ENODEV; return -ENODEV;
} }
if (! (reg & HERMES_EV_ALLOC)) { if (! (reg & HERMES_EV_ALLOC)) {
printk(KERN_ERR "hermes @ 0x%x: Timeout waiting for frame allocation\n", printk(KERN_ERR "hermes @ %s0x%lx: "
hw->iobase); "Timeout waiting for frame allocation\n",
IO_TYPE(hw), hw->iobase);
return -ETIMEDOUT; return -ETIMEDOUT;
} }
...@@ -306,13 +337,14 @@ int hermes_allocate(hermes_t *hw, u16 size, u16 *fid) ...@@ -306,13 +337,14 @@ int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)
return 0; return 0;
} }
/* Set up a BAP to read a particular chunk of data from card's internal buffer. /* Set up a BAP to read a particular chunk of data from card's internal buffer.
* *
* Returns: < 0 on internal failure (errno), 0 on success, >0 on error * Returns: < 0 on internal failure (errno), 0 on success, >0 on error
* from firmware * from firmware
* *
* Callable from any context */ * Callable from any context */
int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) static int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset)
{ {
int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0; int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0;
int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0; int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0;
...@@ -323,14 +355,27 @@ int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) ...@@ -323,14 +355,27 @@ int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset)
if ( (offset > HERMES_BAP_OFFSET_MAX) || (offset % 2) ) if ( (offset > HERMES_BAP_OFFSET_MAX) || (offset % 2) )
return -EINVAL; return -EINVAL;
k = BAP_BUSY_TIMEOUT; k = HERMES_BAP_BUSY_TIMEOUT;
reg = hermes_read_reg(hw, oreg); reg = hermes_read_reg(hw, oreg);
while ((reg & HERMES_OFFSET_BUSY) & k) { while ((reg & HERMES_OFFSET_BUSY) && k) {
k--; k--;
udelay(1); udelay(1);
reg = hermes_read_reg(hw, oreg); reg = hermes_read_reg(hw, oreg);
} }
#ifdef HERMES_DEBUG_BUFFER
hw->profile[HERMES_BAP_BUSY_TIMEOUT - k]++;
if (k < HERMES_BAP_BUSY_TIMEOUT) {
struct hermes_debug_entry *e =
&hw->dbuf[(hw->dbufp++) % HERMES_DEBUG_BUFSIZE];
e->bap = bap;
e->id = id;
e->offset = offset;
e->cycles = HERMES_BAP_BUSY_TIMEOUT - k;
}
#endif
if (reg & HERMES_OFFSET_BUSY) if (reg & HERMES_OFFSET_BUSY)
return -ETIMEDOUT; return -ETIMEDOUT;
...@@ -339,7 +384,7 @@ int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset) ...@@ -339,7 +384,7 @@ int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset)
hermes_write_reg(hw, oreg, offset); hermes_write_reg(hw, oreg, offset);
/* Wait for the BAP to be ready */ /* Wait for the BAP to be ready */
k = BAP_BUSY_TIMEOUT; k = HERMES_BAP_BUSY_TIMEOUT;
reg = hermes_read_reg(hw, oreg); reg = hermes_read_reg(hw, oreg);
while ( (reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) { while ( (reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) {
k--; k--;
...@@ -373,7 +418,7 @@ int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, ...@@ -373,7 +418,7 @@ int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
int err = 0; int err = 0;
if (len % 2) if ( (len < 0) || (len % 2) )
return -EINVAL; return -EINVAL;
err = hermes_bap_seek(hw, bap, id, offset); err = hermes_bap_seek(hw, bap, id, offset);
...@@ -399,7 +444,7 @@ int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len, ...@@ -399,7 +444,7 @@ int hermes_bap_pwrite(hermes_t *hw, int bap, const void *buf, int len,
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
int err = 0; int err = 0;
if (len % 2) if ( (len < 0) || (len % 2) )
return -EINVAL; return -EINVAL;
err = hermes_bap_seek(hw, bap, id, offset); err = hermes_bap_seek(hw, bap, id, offset);
...@@ -427,12 +472,11 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize, ...@@ -427,12 +472,11 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize,
int err = 0; int err = 0;
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
u16 rlength, rtype; u16 rlength, rtype;
hermes_response_t resp;
if (bufsize % 2) if ( (bufsize < 0) || (bufsize % 2) )
return -EINVAL; return -EINVAL;
err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, &resp); err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL);
if (err) if (err)
goto out; goto out;
...@@ -447,11 +491,14 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize, ...@@ -447,11 +491,14 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize,
*length = rlength; *length = rlength;
if (rtype != rid) if (rtype != rid)
printk(KERN_WARNING "hermes_read_ltv(): rid (0x%04x) does " printk(KERN_WARNING "hermes @ %s0x%lx: "
"not match type (0x%04x)\n", rid, rtype); "hermes_read_ltv(): rid (0x%04x) does not match type (0x%04x)\n",
IO_TYPE(hw), hw->iobase, rid, rtype);
if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize) if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize)
printk(KERN_WARNING "hermes @ 0x%x: Truncating LTV record from %d to %d bytes. " printk(KERN_WARNING "hermes @ %s0x%lx: "
"(rid=0x%04x, len=0x%04x)\n", hw->iobase, "Truncating LTV record from %d to %d bytes. "
"(rid=0x%04x, len=0x%04x)\n",
IO_TYPE(hw), hw->iobase,
HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength); HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength);
/* FIXME: we should read the min of the requested length and /* FIXME: we should read the min of the requested length and
...@@ -467,7 +514,6 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, ...@@ -467,7 +514,6 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
{ {
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0; int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
int err = 0; int err = 0;
hermes_response_t resp;
int count; int count;
DEBUG(3, "write_ltv(): bap=%d rid=0x%04x length=%d (value=0x%04x)\n", DEBUG(3, "write_ltv(): bap=%d rid=0x%04x length=%d (value=0x%04x)\n",
...@@ -485,7 +531,7 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid, ...@@ -485,7 +531,7 @@ int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
hermes_write_words(hw, dreg, value, count); hermes_write_words(hw, dreg, value, count);
err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE, err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE,
rid, &resp); rid, NULL);
out: out:
return err; return err;
......
...@@ -48,7 +48,6 @@ ...@@ -48,7 +48,6 @@
#define HERMES_PDA_LEN_MAX (1024) /* in bytes, from EK */ #define HERMES_PDA_LEN_MAX (1024) /* in bytes, from EK */
#define HERMES_SCANRESULT_MAX (35) #define HERMES_SCANRESULT_MAX (35)
#define HERMES_CHINFORESULT_MAX (8) #define HERMES_CHINFORESULT_MAX (8)
#define HERMES_FRAME_LEN_MAX (2304)
#define HERMES_MAX_MULTICAST (16) #define HERMES_MAX_MULTICAST (16)
#define HERMES_MAGIC (0x7d1f) #define HERMES_MAGIC (0x7d1f)
...@@ -138,7 +137,7 @@ ...@@ -138,7 +137,7 @@
/*--- Regulate Commands --------------------------*/ /*--- Regulate Commands --------------------------*/
#define HERMES_CMD_NOTIFY (0x0010) #define HERMES_CMD_NOTIFY (0x0010)
#define HERMES_CMD_INQ (0x0011) #define HERMES_CMD_INQUIRE (0x0011)
/*--- Configure Commands --------------------------*/ /*--- Configure Commands --------------------------*/
#define HERMES_CMD_ACCESS (0x0021) #define HERMES_CMD_ACCESS (0x0021)
...@@ -149,127 +148,161 @@ ...@@ -149,127 +148,161 @@
#define HERMES_MONITOR_ENABLE (0x000b) #define HERMES_MONITOR_ENABLE (0x000b)
#define HERMES_MONITOR_DISABLE (0x000f) #define HERMES_MONITOR_DISABLE (0x000f)
/*
* Configuration RIDs
*/
#define HERMES_RID_CNF_PORTTYPE (0xfc00)
#define HERMES_RID_CNF_MACADDR (0xfc01)
#define HERMES_RID_CNF_DESIRED_SSID (0xfc02)
#define HERMES_RID_CNF_CHANNEL (0xfc03)
#define HERMES_RID_CNF_OWN_SSID (0xfc04)
#define HERMES_RID_CNF_SYSTEM_SCALE (0xfc06)
#define HERMES_RID_CNF_MAX_DATA_LEN (0xfc07)
#define HERMES_RID_CNF_PM_ENABLE (0xfc09)
#define HERMES_RID_CNF_PM_MCAST_RX (0xfc0b)
#define HERMES_RID_CNF_PM_PERIOD (0xfc0c)
#define HERMES_RID_CNF_PM_HOLDOVER (0xfc0d)
#define HERMES_RID_CNF_NICKNAME (0xfc0e)
#define HERMES_RID_CNF_WEP_ON (0xfc20)
#define HERMES_RID_CNF_MWO_ROBUST (0xfc25)
#define HERMES_RID_CNF_MULTICAST_LIST (0xfc80)
#define HERMES_RID_CNF_CREATEIBSS (0xfc81)
#define HERMES_RID_CNF_FRAG_THRESH (0xfc82)
#define HERMES_RID_CNF_RTS_THRESH (0xfc83)
#define HERMES_RID_CNF_TX_RATE_CTRL (0xfc84)
#define HERMES_RID_CNF_PROMISCUOUS (0xfc85)
#define HERMES_RID_CNF_KEYS (0xfcb0)
#define HERMES_RID_CNF_TX_KEY (0xfcb1)
#define HERMES_RID_CNF_TICKTIME (0xfce0)
#define HERMES_RID_CNF_INTERSIL_WEP_ON (0xfc28)
#define HERMES_RID_CNF_INTERSIL_TX_KEY (0xfc23)
#define HERMES_RID_CNF_INTERSIL_KEY0 (0xfc24)
#define HERMES_RID_CNF_INTERSIL_KEY1 (0xfc25)
#define HERMES_RID_CNF_INTERSIL_KEY2 (0xfc26)
#define HERMES_RID_CNF_INTERSIL_KEY3 (0xfc27)
#define HERMES_RID_CNF_SYMBOL_MANDATORY_BSSID (0xfc21)
#define HERMES_RID_CNF_SYMBOL_AUTH_TYPE (0xfc2A)
#define HERMES_RID_CNF_SYMBOL_BASIC_RATES (0xfc8A)
#define HERMES_RID_CNF_SYMBOL_PREAMBLE (0xfc8C)
/*
* Information RIDs
*/
#define HERMES_RID_CHANNEL_LIST (0xfd10)
#define HERMES_RID_STAIDENTITY (0xfd20)
#define HERMES_RID_CURRENT_SSID (0xfd41)
#define HERMES_RID_CURRENT_BSSID (0xfd42)
#define HERMES_RID_COMMSQUALITY (0xfd43)
#define HERMES_RID_CURRENT_TX_RATE (0xfd44)
#define HERMES_RID_SHORT_RETRY_LIMIT (0xfd48)
#define HERMES_RID_LONG_RETRY_LIMIT (0xfd49)
#define HERMES_RID_MAX_TX_LIFETIME (0xfd4A)
#define HERMES_RID_WEP_AVAIL (0xfd4f)
#define HERMES_RID_CURRENT_CHANNEL (0xfdc1)
#define HERMES_RID_DATARATES (0xfdc6)
#define HERMES_RID_SYMBOL_SECONDARY_VER (0xfd24)
#define HERMES_RID_SYMBOL_KEY_LENGTH (0xfc2B)
/* /*
* Frame structures and constants * Frame structures and constants
*/ */
typedef struct hermes_frame_desc { #define HERMES_DESCRIPTOR_OFFSET 0
/* Hermes - i.e. little-endian byte-order */ #define HERMES_802_11_OFFSET (14)
#define HERMES_802_3_OFFSET (14+32)
#define HERMES_802_2_OFFSET (14+32+14)
struct hermes_rx_descriptor {
u16 status;
u32 time;
u8 silence;
u8 signal;
u8 rate;
u8 rxflow;
u32 reserved;
} __attribute__ ((packed));
#define HERMES_RXSTAT_ERR (0x0003)
#define HERMES_RXSTAT_BADCRC (0x0001)
#define HERMES_RXSTAT_UNDECRYPTABLE (0x0002)
#define HERMES_RXSTAT_MACPORT (0x0700)
#define HERMES_RXSTAT_MSGTYPE (0xE000)
#define HERMES_RXSTAT_1042 (0x2000) /* RFC-1042 frame */
#define HERMES_RXSTAT_TUNNEL (0x4000) /* bridge-tunnel encoded frame */
#define HERMES_RXSTAT_WMP (0x6000) /* Wavelan-II Management Protocol frame */
struct hermes_tx_descriptor {
u16 status; u16 status;
u16 res1, res2; u16 reserved1;
u16 q_info; u16 reserved2;
u16 res3, res4; u32 sw_support;
u16 tx_ctl; u8 retry_count;
} __attribute__ ((packed)) hermes_frame_desc_t; u8 tx_rate;
u16 tx_control;
#define HERMES_RXSTAT_ERR (0x0003) } __attribute__ ((packed));
#define HERMES_RXSTAT_MACPORT (0x0700)
#define HERMES_RXSTAT_MSGTYPE (0xE000) #define HERMES_TXSTAT_RETRYERR (0x0001)
#define HERMES_TXSTAT_AGEDERR (0x0002)
#define HERMES_RXSTAT_BADCRC (0x0001) #define HERMES_TXSTAT_DISCON (0x0004)
#define HERMES_RXSTAT_UNDECRYPTABLE (0x0002) #define HERMES_TXSTAT_FORMERR (0x0008)
/* RFC-1042 encoded frame */ #define HERMES_TXCTRL_TX_OK (0x0002) /* ?? interrupt on Tx complete */
#define HERMES_RXSTAT_1042 (0x2000) #define HERMES_TXCTRL_TX_EX (0x0004) /* ?? interrupt on Tx exception */
/* Bridge-tunnel encoded frame */ #define HERMES_TXCTRL_802_11 (0x0008) /* We supply 802.11 header */
#define HERMES_RXSTAT_TUNNEL (0x4000) #define HERMES_TXCTRL_ALT_RTRY (0x0020)
/* Wavelan-II Management Protocol frame */
#define HERMES_RXSTAT_WMP (0x6000) /* Inquiry constants and data types */
#define HERMES_INQ_TALLIES (0xF100)
#define HERMES_INQ_SCAN (0xF101)
#define HERMES_INQ_LINKSTATUS (0xF200)
struct hermes_tallies_frame {
u16 TxUnicastFrames;
u16 TxMulticastFrames;
u16 TxFragments;
u16 TxUnicastOctets;
u16 TxMulticastOctets;
u16 TxDeferredTransmissions;
u16 TxSingleRetryFrames;
u16 TxMultipleRetryFrames;
u16 TxRetryLimitExceeded;
u16 TxDiscards;
u16 RxUnicastFrames;
u16 RxMulticastFrames;
u16 RxFragments;
u16 RxUnicastOctets;
u16 RxMulticastOctets;
u16 RxFCSErrors;
u16 RxDiscards_NoBuffer;
u16 TxDiscardsWrongSA;
u16 RxWEPUndecryptable;
u16 RxMsgInMsgFragments;
u16 RxMsgInBadMsgFragments;
/* Those last are probably not available in very old firmwares */
u16 RxDiscards_WEPICVError;
u16 RxDiscards_WEPExcluded;
} __attribute__ ((packed));
/* Grabbed from wlan-ng - Thanks Mark... - Jean II
* This is the result of a scan inquiry command */
/* Structure describing info about an Access Point */
struct hermes_scan_apinfo {
u16 channel; /* Channel where the AP sits */
u16 noise; /* Noise level */
u16 level; /* Signal level */
u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */
u16 beacon_interv; /* Beacon interval ? */
u16 capabilities; /* Capabilities ? */
u8 essid[32]; /* ESSID of the network */
u8 rates[10]; /* Bit rate supported */
u16 proberesp_rate; /* ???? */
} __attribute__ ((packed));
/* Container */
struct hermes_scan_frame {
u16 rsvd; /* ??? */
u16 scanreason; /* ??? */
struct hermes_scan_apinfo aps[35]; /* Scan result */
} __attribute__ ((packed));
// #define HERMES_DEBUG_BUFFER 1
#define HERMES_DEBUG_BUFSIZE 4096
struct hermes_debug_entry {
int bap;
u16 id, offset;
int cycles;
};
#ifdef __KERNEL__ #ifdef __KERNEL__
/* Timeouts */
#define HERMES_BAP_BUSY_TIMEOUT (500) /* In iterations of ~1us */
/* Basic control structure */ /* Basic control structure */
typedef struct hermes { typedef struct hermes {
uint iobase; ulong iobase;
int io_space; /* 1 if we IO-mapped IO, 0 for memory-mapped IO? */
#define HERMES_IO 1
#define HERMES_MEM 0
int reg_spacing;
#define HERMES_16BIT_REGSPACING 0
#define HERMES_32BIT_REGSPACING 1
u16 inten; /* Which interrupts should be enabled? */ u16 inten; /* Which interrupts should be enabled? */
#ifdef HERMES_DEBUG_BUFFER
struct hermes_debug_entry dbuf[HERMES_DEBUG_BUFSIZE];
unsigned long dbufp;
unsigned long profile[HERMES_BAP_BUSY_TIMEOUT+1];
#endif
} hermes_t; } hermes_t;
typedef struct hermes_response { typedef struct hermes_response {
u16 status, resp0, resp1, resp2; u16 status, resp0, resp1, resp2;
} hermes_response_t; } hermes_response_t;
/* "ID" structure - used for ESSID and station nickname */
typedef struct hermes_id {
u16 len;
u16 val[16];
} __attribute__ ((packed)) hermes_id_t;
typedef struct hermes_multicast {
u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN];
} __attribute__ ((packed)) hermes_multicast_t;
/* Register access convenience macros */ /* Register access convenience macros */
#define hermes_read_reg(hw, off) (inw((hw)->iobase + (off))) #define hermes_read_reg(hw, off) ((hw)->io_space ? \
#define hermes_write_reg(hw, off, val) (outw_p((val), (hw)->iobase + (off))) inw((hw)->iobase + ( (off) << (hw)->reg_spacing )) : \
readw((hw)->iobase + ( (off) << (hw)->reg_spacing )))
#define hermes_write_reg(hw, off, val) ((hw)->io_space ? \
outw_p((val), (hw)->iobase + ( (off) << (hw)->reg_spacing )) : \
writew((val), (hw)->iobase + ( (off) << (hw)->reg_spacing )))
#define hermes_read_regn(hw, name) (hermes_read_reg((hw), HERMES_##name)) #define hermes_read_regn(hw, name) (hermes_read_reg((hw), HERMES_##name))
#define hermes_write_regn(hw, name, val) (hermes_write_reg((hw), HERMES_##name, (val))) #define hermes_write_regn(hw, name, val) (hermes_write_reg((hw), HERMES_##name, (val)))
/* Function prototypes */ /* Function prototypes */
void hermes_struct_init(hermes_t *hw, uint io); void hermes_struct_init(hermes_t *hw, ulong address, int io_space, int reg_spacing);
int hermes_reset(hermes_t *hw); int hermes_reset(hermes_t *hw);
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 hermes_allocate(hermes_t *hw, u16 size, u16 *fid); int hermes_allocate(hermes_t *hw, u16 size, u16 *fid);
int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset);
int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len, int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
u16 id, u16 offset); 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, int len,
...@@ -300,26 +333,62 @@ static inline void hermes_set_irqmask(hermes_t *hw, u16 events) ...@@ -300,26 +333,62 @@ static inline void hermes_set_irqmask(hermes_t *hw, u16 events)
static inline int hermes_enable_port(hermes_t *hw, int port) static inline int hermes_enable_port(hermes_t *hw, int port)
{ {
hermes_response_t resp;
return hermes_docmd_wait(hw, HERMES_CMD_ENABLE | (port << 8), return hermes_docmd_wait(hw, HERMES_CMD_ENABLE | (port << 8),
0, &resp); 0, NULL);
} }
static inline int hermes_disable_port(hermes_t *hw, int port) static inline int hermes_disable_port(hermes_t *hw, int port)
{ {
hermes_response_t resp; return hermes_docmd_wait(hw, HERMES_CMD_DISABLE | (port << 8),
0, NULL);
}
return hermes_docmd_wait(hw, HERMES_CMD_ENABLE | (port << 8), /* Initiate an INQUIRE command (tallies or scan). The result will come as an
0, &resp); * information frame in __orinoco_ev_info() */
static inline int hermes_inquire(hermes_t *hw, u16 rid)
{
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) % 2) ? (((n)+1)/2)+1 : ((n)/2)+1 )
#define HERMES_RECLEN_TO_BYTES(n) ( ((n)-1) * 2 ) #define HERMES_RECLEN_TO_BYTES(n) ( ((n)-1) * 2 )
/* Note that for the next two, the count is in 16-bit words, not bytes */ /* Note that for the next two, the count is in 16-bit words, not bytes */
#define hermes_read_words(hw, off, buf, count) (insw((hw)->iobase + (off), (buf), (count))) static inline void hermes_read_words(struct hermes *hw, int off, void *buf, int count)
#define hermes_write_words(hw, off, buf, count) (outsw((hw)->iobase + (off), (buf), (count))) {
off = off << hw->reg_spacing;;
if (hw->io_space) {
insw(hw->iobase + off, buf, count);
} else {
int i;
u16 *p;
/* This need to *not* byteswap (like insw()) but
* readw() does byteswap hence the conversion */
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)
{
off = off << hw->reg_spacing;;
if (hw->io_space) {
outsw(hw->iobase + off, buf, count);
} else {
int i;
const u16 *p;
/* This need to *not* byteswap (like outsw()) but
* writew() does byteswap hence the conversion */
for (i = 0, p = buf; i < count; i++) {
writew(le16_to_cpu(*p++), hw->iobase + off);
}
}
}
#define HERMES_READ_RECORD(hw, bap, rid, buf) \ #define HERMES_READ_RECORD(hw, bap, rid, buf) \
(hermes_read_ltv((hw),(bap),(rid), sizeof(*buf), NULL, (buf))) (hermes_read_ltv((hw),(bap),(rid), sizeof(*buf), NULL, (buf)))
......
#ifndef _HERMES_RID_H
#define _HERMES_RID_H
/*
* Configuration RIDs
*/
#define HERMES_RID_CNFPORTTYPE 0xFC00 /* used */
#define HERMES_RID_CNFOWNMACADDR 0xFC01 /* used */
#define HERMES_RID_CNFDESIREDSSID 0xFC02 /* used */
#define HERMES_RID_CNFOWNCHANNEL 0xFC03 /* used */
#define HERMES_RID_CNFOWNSSID 0xFC04 /* used */
#define HERMES_RID_CNFOWNATIMWINDOW 0xFC05
#define HERMES_RID_CNFSYSTEMSCALE 0xFC06 /* used */
#define HERMES_RID_CNFMAXDATALEN 0xFC07
#define HERMES_RID_CNFWDSADDRESS 0xFC08
#define HERMES_RID_CNFPMENABLED 0xFC09 /* used */
#define HERMES_RID_CNFPMEPS 0xFC0A
#define HERMES_RID_CNFMULTICASTRECEIVE 0xFC0B /* used */
#define HERMES_RID_CNFMAXSLEEPDURATION 0xFC0C /* used */
#define HERMES_RID_CNFPMHOLDOVERDURATION 0xFC0D /* used */
#define HERMES_RID_CNFOWNNAME 0xFC0E /* used */
#define HERMES_RID_CNFOWNDTIMPERIOD 0xFC10
#define HERMES_RID_CNFWDSADDRESS1 0xFC11
#define HERMES_RID_CNFWDSADDRESS2 0xFC12
#define HERMES_RID_CNFWDSADDRESS3 0xFC13
#define HERMES_RID_CNFWDSADDRESS4 0xFC14
#define HERMES_RID_CNFWDSADDRESS5 0xFC15
#define HERMES_RID_CNFWDSADDRESS6 0xFC16
#define HERMES_RID_CNFMULTICASTPMBUFFERING 0xFC17
#define HERMES_RID_CNFWEPENABLED_AGERE 0xFC20 /* used */
#define HERMES_RID_CNFMANDATORYBSSID_SYMBOL 0xFC21
#define HERMES_RID_CNFWEPDEFAULTKEYID 0xFC23 /* used */
#define HERMES_RID_CNFDEFAULTKEY0 0xFC24 /* used */
#define HERMES_RID_CNFDEFAULTKEY1 0xFC25 /* used */
#define HERMES_RID_CNFMWOROBUST_AGERE 0xFC25 /* used */
#define HERMES_RID_CNFDEFAULTKEY2 0xFC26 /* used */
#define HERMES_RID_CNFDEFAULTKEY3 0xFC27 /* used */
#define HERMES_RID_CNFWEPFLAGS_INTERSIL 0xFC28 /* used */
#define HERMES_RID_CNFWEPKEYMAPPINGTABLE 0xFC29
#define HERMES_RID_CNFAUTHENTICATION 0xFC2A /* used */
#define HERMES_RID_CNFMAXASSOCSTA 0xFC2B
#define HERMES_RID_CNFKEYLENGTH_SYMBOL 0xFC2B
#define HERMES_RID_CNFTXCONTROL 0xFC2C
#define HERMES_RID_CNFROAMINGMODE 0xFC2D
#define HERMES_RID_CNFHOSTAUTHENTICATION 0xFC2E
#define HERMES_RID_CNFRCVCRCERROR 0xFC30
#define HERMES_RID_CNFMMLIFE 0xFC31
#define HERMES_RID_CNFALTRETRYCOUNT 0xFC32
#define HERMES_RID_CNFBEACONINT 0xFC33
#define HERMES_RID_CNFAPPCFINFO 0xFC34
#define HERMES_RID_CNFSTAPCFINFO 0xFC35
#define HERMES_RID_CNFPRIORITYQUSAGE 0xFC37
#define HERMES_RID_CNFTIMCTRL 0xFC40
#define HERMES_RID_CNFTHIRTY2TALLY 0xFC42
#define HERMES_RID_CNFENHSECURITY 0xFC43
#define HERMES_RID_CNFGROUPADDRESSES 0xFC80 /* used */
#define HERMES_RID_CNFCREATEIBSS 0xFC81 /* used */
#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD 0xFC82 /* used */
#define HERMES_RID_CNFRTSTHRESHOLD 0xFC83 /* used */
#define HERMES_RID_CNFTXRATECONTROL 0xFC84 /* used */
#define HERMES_RID_CNFPROMISCUOUSMODE 0xFC85 /* used */
#define HERMES_RID_CNFBASICRATES_SYMBOL 0xFC8A
#define HERMES_RID_CNFPREAMBLE_SYMBOL 0xFC8C /* used */
#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD0 0xFC90
#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD1 0xFC91
#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD2 0xFC92
#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD3 0xFC93
#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD4 0xFC94
#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD5 0xFC95
#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD6 0xFC96
#define HERMES_RID_CNFRTSTHRESHOLD0 0xFC97
#define HERMES_RID_CNFRTSTHRESHOLD1 0xFC98
#define HERMES_RID_CNFRTSTHRESHOLD2 0xFC99
#define HERMES_RID_CNFRTSTHRESHOLD3 0xFC9A
#define HERMES_RID_CNFRTSTHRESHOLD4 0xFC9B
#define HERMES_RID_CNFRTSTHRESHOLD5 0xFC9C
#define HERMES_RID_CNFRTSTHRESHOLD6 0xFC9D
#define HERMES_RID_CNFSHORTPREAMBLE 0xFCB0
#define HERMES_RID_CNFWEPKEYS_AGERE 0xFCB0 /* used */
#define HERMES_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1
#define HERMES_RID_CNFTXKEY_AGERE 0xFCB1 /* used */
#define HERMES_RID_CNFAUTHENTICATIONRSPTO 0xFCB2
#define HERMES_RID_CNFBASICRATES 0xFCB3
#define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4
#define HERMES_RID_CNFTICKTIME 0xFCE0 /* used */
#define HERMES_RID_CNFSCANREQUEST 0xFCE1
#define HERMES_RID_CNFJOINREQUEST 0xFCE2
#define HERMES_RID_CNFAUTHENTICATESTATION 0xFCE3
#define HERMES_RID_CNFCHANNELINFOREQUEST 0xFCE4
/*
* Information RIDs
*/
#define HERMES_RID_MAXLOADTIME 0xFD00
#define HERMES_RID_DOWNLOADBUFFER 0xFD01
#define HERMES_RID_PRIID 0xFD02
#define HERMES_RID_PRISUPRANGE 0xFD03
#define HERMES_RID_CFIACTRANGES 0xFD04
#define HERMES_RID_NICSERNUM 0xFD0A
#define HERMES_RID_NICID 0xFD0B
#define HERMES_RID_MFISUPRANGE 0xFD0C
#define HERMES_RID_CFISUPRANGE 0xFD0D
#define HERMES_RID_CHANNELLIST 0xFD10 /* used */
#define HERMES_RID_REGULATORYDOMAINS 0xFD11
#define HERMES_RID_TEMPTYPE 0xFD12
#define HERMES_RID_CIS 0xFD13
#define HERMES_RID_STAID 0xFD20 /* used */
#define HERMES_RID_STASUPRANGE 0xFD21
#define HERMES_RID_MFIACTRANGES 0xFD22
#define HERMES_RID_CFIACTRANGES2 0xFD23
#define HERMES_RID_SECONDARYVERSION_SYMBOL 0xFD24 /* used */
#define HERMES_RID_PORTSTATUS 0xFD40
#define HERMES_RID_CURRENTSSID 0xFD41 /* used */
#define HERMES_RID_CURRENTBSSID 0xFD42 /* used */
#define HERMES_RID_COMMSQUALITY 0xFD43 /* used */
#define HERMES_RID_CURRENTTXRATE 0xFD44 /* used */
#define HERMES_RID_CURRENTBEACONINTERVAL 0xFD45
#define HERMES_RID_CURRENTSCALETHRESHOLDS 0xFD46
#define HERMES_RID_PROTOCOLRSPTIME 0xFD47
#define HERMES_RID_SHORTRETRYLIMIT 0xFD48 /* used */
#define HERMES_RID_LONGRETRYLIMIT 0xFD49 /* used */
#define HERMES_RID_MAXTRANSMITLIFETIME 0xFD4A /* used */
#define HERMES_RID_MAXRECEIVELIFETIME 0xFD4B
#define HERMES_RID_CFPOLLABLE 0xFD4C
#define HERMES_RID_AUTHENTICATIONALGORITHMS 0xFD4D
#define HERMES_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F
#define HERMES_RID_CURRENTTXRATE1 0xFD80
#define HERMES_RID_CURRENTTXRATE2 0xFD81
#define HERMES_RID_CURRENTTXRATE3 0xFD82
#define HERMES_RID_CURRENTTXRATE4 0xFD83
#define HERMES_RID_CURRENTTXRATE5 0xFD84
#define HERMES_RID_CURRENTTXRATE6 0xFD85
#define HERMES_RID_OWNMACADDR 0xFD86
#define HERMES_RID_SCANRESULTSTABLE 0xFD88
#define HERMES_RID_PHYTYPE 0xFDC0
#define HERMES_RID_CURRENTCHANNEL 0xFDC1 /* used */
#define HERMES_RID_CURRENTPOWERSTATE 0xFDC2
#define HERMES_RID_CCAMODE 0xFDC3
#define HERMES_RID_SUPPORTEDDATARATES 0xFDC6 /* used */
#define HERMES_RID_BUILDSEQ 0xFFFE
#define HERMES_RID_FWID 0xFFFF
/* "ID" structure - used for ESSID and station nickname */
struct hermes_idstring {
u16 len;
u16 val[16];
} __attribute__ ((packed));
typedef struct hermes_multicast {
u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN];
} __attribute__ ((packed)) hermes_multicast_t;
#endif
#ifndef _IEEE802_11_H
#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. */
#define IEEE802_11_HLEN 30
#define IEEE802_11_FRAME_LEN (IEEE802_11_DATA_LEN + IEEE802_11_HLEN)
struct ieee802_11_hdr {
u16 frame_ctl;
u16 duration_id;
u8 addr1[ETH_ALEN];
u8 addr2[ETH_ALEN];
u8 addr3[ETH_ALEN];
u16 seq_ctl;
u8 addr4[ETH_ALEN];
} __attribute__ ((packed));
/* Frame control field constants */
#define IEEE802_11_FCTL_VERS 0x0002
#define IEEE802_11_FCTL_FTYPE 0x000c
#define IEEE802_11_FCTL_STYPE 0x00f0
#define IEEE802_11_FCTL_TODS 0x0100
#define IEEE802_11_FCTL_FROMDS 0x0200
#define IEEE802_11_FCTL_MOREFRAGS 0x0400
#define IEEE802_11_FCTL_RETRY 0x0800
#define IEEE802_11_FCTL_PM 0x1000
#define IEEE802_11_FCTL_MOREDATA 0x2000
#define IEEE802_11_FCTL_WEP 0x4000
#define IEEE802_11_FCTL_ORDER 0x8000
#define IEEE802_11_FTYPE_MGMT 0x0000
#define IEEE802_11_FTYPE_CTL 0x0004
#define IEEE802_11_FTYPE_DATA 0x0008
/* management */
#define IEEE802_11_STYPE_ASSOC_REQ 0x0000
#define IEEE802_11_STYPE_ASSOC_RESP 0x0010
#define IEEE802_11_STYPE_REASSOC_REQ 0x0020
#define IEEE802_11_STYPE_REASSOC_RESP 0x0030
#define IEEE802_11_STYPE_PROBE_REQ 0x0040
#define IEEE802_11_STYPE_PROBE_RESP 0x0050
#define IEEE802_11_STYPE_BEACON 0x0080
#define IEEE802_11_STYPE_ATIM 0x0090
#define IEEE802_11_STYPE_DISASSOC 0x00A0
#define IEEE802_11_STYPE_AUTH 0x00B0
#define IEEE802_11_STYPE_DEAUTH 0x00C0
/* control */
#define IEEE802_11_STYPE_PSPOLL 0x00A0
#define IEEE802_11_STYPE_RTS 0x00B0
#define IEEE802_11_STYPE_CTS 0x00C0
#define IEEE802_11_STYPE_ACK 0x00D0
#define IEEE802_11_STYPE_CFEND 0x00E0
#define IEEE802_11_STYPE_CFENDACK 0x00F0
/* data */
#define IEEE802_11_STYPE_DATA 0x0000
#define IEEE802_11_STYPE_DATA_CFACK 0x0010
#define IEEE802_11_STYPE_DATA_CFPOLL 0x0020
#define IEEE802_11_STYPE_DATA_CFACKPOLL 0x0030
#define IEEE802_11_STYPE_NULLFUNC 0x0040
#define IEEE802_11_STYPE_CFACK 0x0050
#define IEEE802_11_STYPE_CFPOLL 0x0060
#define IEEE802_11_STYPE_CFACKPOLL 0x0070
#define IEEE802_11_SCTL_FRAG 0x000F
#define IEEE802_11_SCTL_SEQ 0xFFF0
#endif /* _IEEE802_11_H */
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -8,110 +8,95 @@ ...@@ -8,110 +8,95 @@
#define _ORINOCO_H #define _ORINOCO_H
/* To enable debug messages */ /* To enable debug messages */
/* #define ORINOCO_DEBUG 3 */ //#define ORINOCO_DEBUG 3
#if (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10) #if (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10)
#error "orinoco_cs requires Wireless extensions v10 or later." #error "orinoco driver requires Wireless extensions v10 or later."
#endif /* (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10) */ #endif /* (! defined (WIRELESS_EXT)) || (WIRELESS_EXT < 10) */
#define WIRELESS_SPY // enable iwspy support #define WIRELESS_SPY // enable iwspy support
#define ORINOCO_MAX_KEY_SIZE 14
#define ORINOCO_MAX_KEYS 4
#define DLDWD_MIN_MTU 256 typedef struct orinoco_key {
#define DLDWD_MAX_MTU (HERMES_FRAME_LEN_MAX - ENCAPS_OVERHEAD) u16 len; /* always store little-endian */
char data[ORINOCO_MAX_KEY_SIZE];
} __attribute__ ((packed)) orinoco_key_t;
#define LTV_BUF_SIZE 128 typedef orinoco_key_t orinoco_keys_t[ORINOCO_MAX_KEYS];
#define USER_BAP 0
#define IRQ_BAP 1
#define DLDWD_MACPORT 0
#define IRQ_LOOP_MAX 10
#define TX_NICBUF_SIZE 2048
#define TX_NICBUF_SIZE_BUG 1585 /* Bug in Symbol firmware */
#define MAX_KEYS 4
#define MAX_KEY_SIZE 14
#define LARGE_KEY_SIZE 13
#define SMALL_KEY_SIZE 5
#define MAX_FRAME_SIZE 2304
typedef struct dldwd_key {
uint16_t len; /* always store little-endian */
char data[MAX_KEY_SIZE];
} __attribute__ ((packed)) dldwd_key_t;
typedef dldwd_key_t dldwd_keys_t[MAX_KEYS];
/*====================================================================*/ /*====================================================================*/
struct orinoco_private {
typedef struct dldwd_priv { void *card; /* Pointer to card dependant structure */
void* card; /* Pointer to card dependant structure */
/* card dependant extra reset code (i.e. bus/interface specific */ /* card dependant extra reset code (i.e. bus/interface specific */
int (*card_reset_handler)(struct dldwd_priv *); int (*hard_reset)(struct orinoco_private *);
spinlock_t lock; spinlock_t lock;
long state; long state;
#define DLDWD_STATE_INIRQ 0 #define ORINOCO_STATE_INIRQ 0
#define DLDWD_STATE_DOIRQ 1 #define ORINOCO_STATE_DOIRQ 1
int hw_ready; /* HW may be suspended by platform */
/* Net device stuff */ /* Net device stuff */
struct net_device ndev; struct net_device *ndev;
struct net_device_stats stats; struct net_device_stats stats;
struct iw_statistics wstats; struct iw_statistics wstats;
/* Hardware control variables */ /* Hardware control variables */
hermes_t hw; hermes_t hw;
uint16_t txfid; u16 txfid;
/* Capabilities of the hardware/firmware */ /* Capabilities of the hardware/firmware */
int firmware_type; int firmware_type;
#define FIRMWARE_TYPE_LUCENT 1 #define FIRMWARE_TYPE_AGERE 1
#define FIRMWARE_TYPE_INTERSIL 2 #define FIRMWARE_TYPE_INTERSIL 2
#define FIRMWARE_TYPE_SYMBOL 3 #define FIRMWARE_TYPE_SYMBOL 3
int has_ibss, has_port3, prefer_port3, has_ibss_any, ibss_port; int has_ibss, has_port3, has_ibss_any, ibss_port;
int has_wep, has_big_wep; int has_wep, has_big_wep;
int has_mwo; int has_mwo;
int has_pm; int has_pm;
int has_preamble; int has_preamble;
int need_card_reset, broken_reset, broken_allocate; int has_sensitivity;
uint16_t channel_mask; int nicbuf_size;
int broken_cor_reset;
/* Current configuration */ u16 channel_mask;
uint32_t iw_mode;
int port_type, allow_ibss; /* Configuration paramaters */
uint16_t wep_on, wep_restrict, tx_key; u32 iw_mode;
dldwd_keys_t keys; int prefer_port3;
u16 wep_on, wep_restrict, tx_key;
orinoco_keys_t keys;
int bitratemode;
char nick[IW_ESSID_MAX_SIZE+1]; char nick[IW_ESSID_MAX_SIZE+1];
char desired_essid[IW_ESSID_MAX_SIZE+1]; char desired_essid[IW_ESSID_MAX_SIZE+1];
uint16_t frag_thresh, mwo_robust; u16 frag_thresh, mwo_robust;
uint16_t channel; u16 channel;
uint16_t ap_density, rts_thresh; u16 ap_density, rts_thresh;
uint16_t tx_rate_ctrl; u16 pm_on, pm_mcast, pm_period, pm_timeout;
uint16_t pm_on, pm_mcast, pm_period, pm_timeout; u16 preamble;
uint16_t preamble;
int promiscuous, allmulti, mc_count;
#ifdef WIRELESS_SPY #ifdef WIRELESS_SPY
int spy_number; int spy_number;
u_char spy_address[IW_MAX_SPY][ETH_ALEN]; u_char spy_address[IW_MAX_SPY][ETH_ALEN];
struct iw_quality spy_stat[IW_MAX_SPY]; struct iw_quality spy_stat[IW_MAX_SPY];
#endif #endif
/* Configuration dependent variables */
int port_type, allow_ibss;
int promiscuous, mc_count;
/* /proc based debugging stuff */ /* /proc based debugging stuff */
struct proc_dir_entry *dir_dev; struct proc_dir_entry *dir_dev;
struct proc_dir_entry *dir_regs; };
struct proc_dir_entry *dir_recs;
} dldwd_priv_t;
/*====================================================================*/ /*====================================================================*/
extern struct list_head dldwd_instances; extern struct list_head orinoco_instances;
#ifdef ORINOCO_DEBUG #ifdef ORINOCO_DEBUG
extern int dldwd_debug; extern int orinoco_debug;
#define DEBUG(n, args...) do { if (dldwd_debug>(n)) printk(KERN_DEBUG args); } while(0) #define DEBUG(n, args...) do { if (orinoco_debug>(n)) printk(KERN_DEBUG args); } while(0)
#define DEBUGMORE(n, args...) do { if (dldwd_debug>(n)) printk(args); } while (0) #define DEBUGMORE(n, args...) do { if (orinoco_debug>(n)) printk(args); } while (0)
#else #else
#define DEBUG(n, args...) do { } while (0) #define DEBUG(n, args...) do { } while (0)
#define DEBUGMORE(n, args...) do { } while (0) #define DEBUGMORE(n, args...) do { } while (0)
...@@ -122,21 +107,12 @@ extern int dldwd_debug; ...@@ -122,21 +107,12 @@ extern int dldwd_debug;
#define RUP_EVEN(a) ( (a) % 2 ? (a) + 1 : (a) ) #define RUP_EVEN(a) ( (a) % 2 ? (a) + 1 : (a) )
/* struct net_device methods */
extern int dldwd_init(struct net_device *dev);
extern int dldwd_xmit(struct sk_buff *skb, struct net_device *dev);
extern void dldwd_tx_timeout(struct net_device *dev);
extern int dldwd_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
extern int dldwd_change_mtu(struct net_device *dev, int new_mtu);
extern void dldwd_set_multicast_list(struct net_device *dev);
/* utility routines */ /* utility routines */
extern void dldwd_shutdown(dldwd_priv_t *dev); struct net_device *alloc_orinocodev(int sizeof_card);
extern int dldwd_reset(dldwd_priv_t *dev); extern void orinoco_shutdown(struct orinoco_private *dev);
extern int dldwd_setup(dldwd_priv_t* priv); extern int orinoco_reset(struct orinoco_private *dev);
extern int dldwd_proc_dev_init(dldwd_priv_t *dev); extern int orinoco_proc_dev_init(struct orinoco_private *dev);
extern void dldwd_proc_dev_cleanup(dldwd_priv_t *priv); extern void orinoco_proc_dev_cleanup(struct orinoco_private *priv);
extern void dldwd_interrupt(int irq, void * dev_id, struct pt_regs *regs); extern void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs);
#endif #endif /* _ORINOCO_H */
/* orinoco_cs.c 0.08a - (formerly known as dldwd_cs.c) /* orinoco_cs.c 0.11a - (formerly known as dldwd_cs.c)
* *
* A driver for "Hermes" chipset based PCMCIA wireless adaptors, such * A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
* as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ * as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
...@@ -11,6 +11,9 @@ ...@@ -11,6 +11,9 @@
*/ */
#include <linux/config.h> #include <linux/config.h>
#ifdef __IN_PCMCIA_PACKAGE__
#include <pcmcia/k_compat.h>
#endif /* __IN_PCMCIA_PACKAGE__ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -44,11 +47,13 @@ ...@@ -44,11 +47,13 @@
/*====================================================================*/ /*====================================================================*/
static char version[] __initdata = "orinoco_cs.c 0.08a (David Gibson <hermes@gibson.dropbear.id.au> and others)"; static char version[] __initdata = "orinoco_cs.c 0.11a (David Gibson <hermes@gibson.dropbear.id.au> and others)";
MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>"); MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar wireless cards"); MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar wireless cards");
#ifdef MODULE_LICENSE
MODULE_LICENSE("Dual MPL/GPL"); MODULE_LICENSE("Dual MPL/GPL");
#endif
/* Parameters that can be set with 'insmod' */ /* Parameters that can be set with 'insmod' */
...@@ -58,7 +63,7 @@ static uint irq_mask = 0xdeb8; ...@@ -58,7 +63,7 @@ static uint irq_mask = 0xdeb8;
/* Newer, simpler way of listing specific interrupts */ /* Newer, simpler way of listing specific interrupts */
static int irq_list[4] = { -1 }; static int irq_list[4] = { -1 };
/* Do a Pcmcia soft reset (may help some cards) */ /* Do a Pcmcia soft reset (may help some cards) */
static int reset_cor = 0; static int reset_cor = -1;
/* Some D-Link cards have buggy CIS. They do work at 5v properly, but /* 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... */ * don't have any CIS entry for it. This workaround it... */
static int ignore_cis_vcc; /* = 0 */ static int ignore_cis_vcc; /* = 0 */
...@@ -68,40 +73,34 @@ MODULE_PARM(irq_list, "1-4i"); ...@@ -68,40 +73,34 @@ MODULE_PARM(irq_list, "1-4i");
MODULE_PARM(reset_cor, "i"); MODULE_PARM(reset_cor, "i");
MODULE_PARM(ignore_cis_vcc, "i"); MODULE_PARM(ignore_cis_vcc, "i");
/* Pcmcia specific structure */ /* Pcmcia specific structure */
typedef struct dldwd_card { struct orinoco_pccard {
dev_link_t link; dev_link_t link;
dev_node_t node; dev_node_t node;
int instance; };
/* Common structure (fully included), see orinoco.h */
struct dldwd_priv priv;
} dldwd_card_t;
/* /*
* Function prototypes * Function prototypes
*/ */
/* struct net_device methods */ /* struct net_device methods */
static int dldwd_cs_open(struct net_device *dev); static int orinoco_cs_open(struct net_device *dev);
static int dldwd_cs_stop(struct net_device *dev); static int orinoco_cs_stop(struct net_device *dev);
/* PCMCIA gumpf */ /* PCMCIA gumpf */
static void dldwd_cs_config(dev_link_t * link); static void orinoco_cs_config(dev_link_t * link);
static void dldwd_cs_release(u_long arg); static void orinoco_cs_release(u_long arg);
static int dldwd_cs_event(event_t event, int priority, static int orinoco_cs_event(event_t event, int priority,
event_callback_args_t * args); event_callback_args_t * args);
static dev_link_t *dldwd_cs_attach(void); static dev_link_t *orinoco_cs_attach(void);
static void dldwd_cs_detach(dev_link_t *); static void orinoco_cs_detach(dev_link_t *);
/* /*
The dev_info variable is the "key" that is used to match up this The dev_info variable is the "key" that is used to match up this
device driver with appropriate cards, through the card configuration device driver with appropriate cards, through the card configuration
database. database.
*/ */
static dev_info_t dev_info = "orinoco_cs"; static dev_info_t dev_info = "orinoco_cs";
/* /*
...@@ -115,7 +114,6 @@ static dev_info_t dev_info = "orinoco_cs"; ...@@ -115,7 +114,6 @@ static dev_info_t dev_info = "orinoco_cs";
*/ */
static dev_link_t *dev_list; /* = NULL */ static dev_link_t *dev_list; /* = NULL */
static int num_instances; /* = 0 */
/*====================================================================*/ /*====================================================================*/
...@@ -127,48 +125,48 @@ cs_error(client_handle_t handle, int func, int ret) ...@@ -127,48 +125,48 @@ cs_error(client_handle_t handle, int func, int ret)
} }
static int static int
dldwd_cs_open(struct net_device *dev) orinoco_cs_open(struct net_device *dev)
{ {
dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv; struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
dldwd_card_t* card = (dldwd_card_t *)priv->card; struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card;
dev_link_t *link = &card->link; dev_link_t *link = &card->link;
int err; int err;
TRACE_ENTER(priv->ndev.name); TRACE_ENTER(dev->name);
link->open++; link->open++;
netif_device_attach(dev); netif_device_attach(dev);
err = dldwd_reset(priv); err = orinoco_reset(priv);
if (err) if (err)
dldwd_cs_stop(dev); orinoco_cs_stop(dev);
else else
netif_start_queue(dev); netif_start_queue(dev);
TRACE_EXIT(priv->ndev.name); TRACE_EXIT(dev->name);
return err; return err;
} }
static int static int
dldwd_cs_stop(struct net_device *dev) orinoco_cs_stop(struct net_device *dev)
{ {
dldwd_priv_t *priv = (dldwd_priv_t *)dev->priv; struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
dldwd_card_t* card = (dldwd_card_t *)priv->card; struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card;
dev_link_t *link = &card->link; dev_link_t *link = &card->link;
TRACE_ENTER(priv->ndev.name); TRACE_ENTER(dev->name);
netif_stop_queue(dev); netif_stop_queue(dev);
dldwd_shutdown(priv); orinoco_shutdown(priv);
link->open--; link->open--;
if (link->state & DEV_STALE_CONFIG) if (link->state & DEV_STALE_CONFIG)
mod_timer(&link->release, jiffies + HZ/20); mod_timer(&link->release, jiffies + HZ/20);
TRACE_EXIT(priv->ndev.name); TRACE_EXIT(dev->name);
return 0; return 0;
} }
...@@ -179,18 +177,18 @@ dldwd_cs_stop(struct net_device *dev) ...@@ -179,18 +177,18 @@ dldwd_cs_stop(struct net_device *dev)
* In fact, this seem necessary for Spectrum cards... * In fact, this seem necessary for Spectrum cards...
*/ */
static int static int
dldwd_cs_cor_reset(dldwd_priv_t *priv) orinoco_cs_cor_reset(struct orinoco_private *priv)
{ {
dldwd_card_t* card = (dldwd_card_t *)priv->card; struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card;
dev_link_t *link = &card->link; dev_link_t *link = &card->link;
conf_reg_t reg; conf_reg_t reg;
u_int default_cor; u_int default_cor;
TRACE_ENTER(priv->ndev.name); TRACE_ENTER(priv->ndev->name);
/* Doing it if hardware is gone is guaranteed crash */ /* Doing it if hardware is gone is guaranteed crash */
if(!priv->hw_ready) if(! (link->state & DEV_CONFIG) )
return(0); return -ENODEV;
/* Save original COR value */ /* Save original COR value */
reg.Function = 0; reg.Function = 0;
...@@ -200,7 +198,7 @@ dldwd_cs_cor_reset(dldwd_priv_t *priv) ...@@ -200,7 +198,7 @@ dldwd_cs_cor_reset(dldwd_priv_t *priv)
CardServices(AccessConfigurationRegister, link->handle, &reg); CardServices(AccessConfigurationRegister, link->handle, &reg);
default_cor = reg.Value; default_cor = reg.Value;
DEBUG(2, "dldwd : dldwd_cs_cor_reset() : cor=0x%X\n", default_cor); DEBUG(2, "orinoco : orinoco_cs_cor_reset() : cor=0x%X\n", default_cor);
/* Soft-Reset card */ /* Soft-Reset card */
reg.Action = CS_WRITE; reg.Action = CS_WRITE;
...@@ -209,18 +207,57 @@ dldwd_cs_cor_reset(dldwd_priv_t *priv) ...@@ -209,18 +207,57 @@ dldwd_cs_cor_reset(dldwd_priv_t *priv)
CardServices(AccessConfigurationRegister, link->handle, &reg); CardServices(AccessConfigurationRegister, link->handle, &reg);
/* Wait until the card has acknowledged our reset */ /* Wait until the card has acknowledged our reset */
/* FIXME: mdelay() is deprecated -dgibson */
mdelay(1); 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, &reg);
/* 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, &reg);
mdelay(1);
#endif
/* Restore original COR configuration index */ /* Restore original COR configuration index */
reg.Action = CS_WRITE;
reg.Offset = CISREG_COR;
reg.Value = (default_cor & ~COR_SOFT_RESET); reg.Value = (default_cor & ~COR_SOFT_RESET);
CardServices(AccessConfigurationRegister, link->handle, &reg); CardServices(AccessConfigurationRegister, link->handle, &reg);
/* Wait until the card has finished restarting */ /* Wait until the card has finished restarting */
/* FIXME: mdelay() is deprecated -dgibson */
mdelay(1); mdelay(1);
TRACE_EXIT(priv->ndev.name); TRACE_EXIT(priv->ndev->name);
return(0); return 0;
}
static int
orinoco_cs_hard_reset(struct orinoco_private *priv)
{
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
} }
/* Remove zombie instances (card removed, detach pending) */ /* Remove zombie instances (card removed, detach pending) */
...@@ -228,17 +265,17 @@ static void ...@@ -228,17 +265,17 @@ static void
flush_stale_links(void) flush_stale_links(void)
{ {
dev_link_t *link, *next; dev_link_t *link, *next;
TRACE_ENTER("dldwd"); TRACE_ENTER("orinoco");
for (link = dev_list; link; link = next) { for (link = dev_list; link; link = next) {
next = link->next; next = link->next;
if (link->state & DEV_STALE_LINK) if (link->state & DEV_STALE_LINK)
dldwd_cs_detach(link); orinoco_cs_detach(link);
} }
TRACE_EXIT("dldwd"); TRACE_EXIT("orinoco");
} }
/*====================================================================== /*======================================================================
dldwd_cs_attach() creates an "instance" of the driver, allocating orinoco_cs_attach() creates an "instance" of the driver, allocating
local data structures for one device. The device is registered local data structures for one device. The device is registered
with Card Services. with Card Services.
...@@ -248,37 +285,35 @@ flush_stale_links(void) ...@@ -248,37 +285,35 @@ flush_stale_links(void)
======================================================================*/ ======================================================================*/
static dev_link_t * static dev_link_t *
dldwd_cs_attach(void) orinoco_cs_attach(void)
{ {
dldwd_card_t *card; struct net_device *dev;
dldwd_priv_t *priv; struct orinoco_private *priv;
struct orinoco_pccard *card;
dev_link_t *link; dev_link_t *link;
struct net_device *ndev;
client_reg_t client_reg; client_reg_t client_reg;
int ret, i; int ret, i;
TRACE_ENTER("dldwd"); TRACE_ENTER("orinoco");
/* A bit of cleanup */ /* A bit of cleanup */
flush_stale_links(); flush_stale_links();
/* Allocate space for private device-specific data */ dev = alloc_orinocodev(sizeof(*card));
card = kmalloc(sizeof(*card), GFP_KERNEL); if (! dev)
if (! card) { return NULL;
link = NULL; priv = dev->priv;
goto out; card = priv->card;
} /* Overrides */
memset(card, 0, sizeof(*card)); dev->open = orinoco_cs_open;
dev->stop = orinoco_cs_stop;
priv->hard_reset = orinoco_cs_hard_reset;
/* Link both structure together */ /* Link both structures together */
priv = &(card->priv);
priv->card = card;
card->instance = num_instances++; /* FIXME: Racy? */
link = &card->link; link = &card->link;
ndev = &priv->ndev;
link->priv = priv; link->priv = priv;
/* Initialize the dev_link_t structure */ /* Initialize the dev_link_t structure */
link->release.function = &dldwd_cs_release; link->release.function = &orinoco_cs_release;
link->release.data = (u_long) link; link->release.data = (u_long) link;
/* Interrupt setup */ /* Interrupt setup */
...@@ -301,17 +336,6 @@ dldwd_cs_attach(void) ...@@ -301,17 +336,6 @@ dldwd_cs_attach(void)
link->conf.Attributes = 0; link->conf.Attributes = 0;
link->conf.IntType = INT_MEMORY_AND_IO; link->conf.IntType = INT_MEMORY_AND_IO;
/* Setup the common part */
if(dldwd_setup(priv) < 0) {
kfree(card);
return NULL;
}
/* Overrides */
ndev->open = dldwd_cs_open;
ndev->stop = dldwd_cs_stop;
priv->card_reset_handler = dldwd_cs_cor_reset;
/* Register with Card Services */ /* Register with Card Services */
link->next = dev_list; link->next = dev_list;
dev_list = link; dev_list = link;
...@@ -321,21 +345,21 @@ dldwd_cs_attach(void) ...@@ -321,21 +345,21 @@ dldwd_cs_attach(void)
CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
client_reg.event_handler = &dldwd_cs_event; client_reg.event_handler = &orinoco_cs_event;
client_reg.Version = 0x0210; client_reg.Version = 0x0210;
client_reg.event_callback_args.client_data = link; client_reg.event_callback_args.client_data = link;
ret = CardServices(RegisterClient, &link->handle, &client_reg); ret = CardServices(RegisterClient, &link->handle, &client_reg);
if (ret != CS_SUCCESS) { if (ret != CS_SUCCESS) {
cs_error(link->handle, RegisterClient, ret); cs_error(link->handle, RegisterClient, ret);
dldwd_cs_detach(link); orinoco_cs_detach(link);
link = NULL; link = NULL;
goto out; goto out;
} }
out: out:
TRACE_EXIT("dldwd"); TRACE_EXIT("orinoco");
return link; return link;
} /* dldwd_cs_attach */ } /* orinoco_cs_attach */
/*====================================================================== /*======================================================================
This deletes a driver "instance". The device is de-registered This deletes a driver "instance". The device is de-registered
...@@ -345,12 +369,12 @@ dldwd_cs_attach(void) ...@@ -345,12 +369,12 @@ dldwd_cs_attach(void)
======================================================================*/ ======================================================================*/
static void static void
dldwd_cs_detach(dev_link_t * link) orinoco_cs_detach(dev_link_t * link)
{ {
dev_link_t **linkp; dev_link_t **linkp;
dldwd_priv_t *priv = link->priv; struct orinoco_private *priv = link->priv;
TRACE_ENTER("dldwd"); TRACE_ENTER("orinoco");
/* Locate device structure */ /* Locate device structure */
for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
...@@ -383,19 +407,17 @@ dldwd_cs_detach(dev_link_t * link) ...@@ -383,19 +407,17 @@ dldwd_cs_detach(dev_link_t * link)
DEBUG(0, "orinoco_cs: detach: link=%p link->dev=%p\n", link, link->dev); DEBUG(0, "orinoco_cs: detach: link=%p link->dev=%p\n", link, link->dev);
if (link->dev) { if (link->dev) {
DEBUG(0, "orinoco_cs: About to unregister net device %p\n", DEBUG(0, "orinoco_cs: About to unregister net device %p\n",
&priv->ndev); priv->ndev);
unregister_netdev(&priv->ndev); unregister_netdev(priv->ndev);
} }
kfree(priv->card); kfree(priv->card);
num_instances--; /* FIXME: Racy? */
out: out:
TRACE_EXIT("dldwd"); TRACE_EXIT("orinoco");
} /* dldwd_cs_detach */ } /* orinoco_cs_detach */
/*====================================================================== /*======================================================================
dldwd_cs_config() is scheduled to run after a CARD_INSERTION event orinoco_cs_config() is scheduled to run after a CARD_INSERTION event
is received, to configure the PCMCIA socket, and to make the is received, to configure the PCMCIA socket, and to make the
device available to the system. device available to the system.
======================================================================*/ ======================================================================*/
...@@ -407,13 +429,13 @@ while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed ...@@ -407,13 +429,13 @@ while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed
if (CardServices(fn, args) != 0) goto next_entry if (CardServices(fn, args) != 0) goto next_entry
static void static void
dldwd_cs_config(dev_link_t * link) orinoco_cs_config(dev_link_t * link)
{ {
client_handle_t handle = link->handle; client_handle_t handle = link->handle;
dldwd_priv_t *priv = link->priv; struct orinoco_private *priv = link->priv;
dldwd_card_t *card = (dldwd_card_t *)priv->card; struct orinoco_pccard *card = (struct orinoco_pccard *)priv->card;
hermes_t *hw = &priv->hw; hermes_t *hw = &priv->hw;
struct net_device *ndev = &priv->ndev; struct net_device *ndev = priv->ndev;
tuple_t tuple; tuple_t tuple;
cisparse_t parse; cisparse_t parse;
int last_fn, last_ret; int last_fn, last_ret;
...@@ -422,7 +444,7 @@ dldwd_cs_config(dev_link_t * link) ...@@ -422,7 +444,7 @@ dldwd_cs_config(dev_link_t * link)
cistpl_cftable_entry_t dflt = { 0 }; cistpl_cftable_entry_t dflt = { 0 };
cisinfo_t info; cisinfo_t info;
TRACE_ENTER("dldwd"); TRACE_ENTER("orinoco");
CS_CHECK(ValidateCIS, handle, &info); CS_CHECK(ValidateCIS, handle, &info);
...@@ -448,7 +470,7 @@ dldwd_cs_config(dev_link_t * link) ...@@ -448,7 +470,7 @@ dldwd_cs_config(dev_link_t * link)
CS_CHECK(GetConfigurationInfo, handle, &conf); CS_CHECK(GetConfigurationInfo, handle, &conf);
link->conf.Vcc = conf.Vcc; link->conf.Vcc = conf.Vcc;
DEBUG(0, "dldwd_cs_config: ConfigBase = 0x%x link->conf.Vcc = %d\n", DEBUG(0, "orinoco_cs_config: ConfigBase = 0x%x link->conf.Vcc = %d\n",
link->conf.ConfigBase, link->conf.Vcc); link->conf.ConfigBase, link->conf.Vcc);
/* /*
...@@ -470,7 +492,7 @@ dldwd_cs_config(dev_link_t * link) ...@@ -470,7 +492,7 @@ dldwd_cs_config(dev_link_t * link)
CFG_CHECK(GetTupleData, handle, &tuple); CFG_CHECK(GetTupleData, handle, &tuple);
CFG_CHECK(ParseTuple, handle, &tuple, &parse); CFG_CHECK(ParseTuple, handle, &tuple, &parse);
DEBUG(0, "dldwd_cs_config: index = 0x%x, flags = 0x%x\n", DEBUG(0, "orinoco_cs_config: index = 0x%x, flags = 0x%x\n",
cfg->index, cfg->flags); cfg->index, cfg->flags);
if (cfg->flags & CISTPL_CFTABLE_DEFAULT) if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
...@@ -488,16 +510,14 @@ dldwd_cs_config(dev_link_t * link) ...@@ -488,16 +510,14 @@ dldwd_cs_config(dev_link_t * link)
/* Use power settings for Vcc and Vpp if present */ /* 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 (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
if (conf.Vcc != if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
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);
DEBUG(2, "dldwd_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n", conf.Vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000); if (!ignore_cis_vcc)
if(!ignore_cis_vcc)
goto next_entry; goto next_entry;
} }
} else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) { } else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
if (conf.Vcc != if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] / 10000) {
dflt.vcc.param[CISTPL_POWER_VNOM] / 10000) { DEBUG(2, "orinoco_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n", conf.Vcc, dflt.vcc.param[CISTPL_POWER_VNOM] / 10000);
DEBUG(2, "dldwd_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n", conf.Vcc, dflt.vcc.param[CISTPL_POWER_VNOM] / 10000);
if(!ignore_cis_vcc) if(!ignore_cis_vcc)
goto next_entry; goto next_entry;
} }
...@@ -510,7 +530,7 @@ dldwd_cs_config(dev_link_t * link) ...@@ -510,7 +530,7 @@ dldwd_cs_config(dev_link_t * link)
link->conf.Vpp1 = link->conf.Vpp2 = link->conf.Vpp1 = link->conf.Vpp2 =
dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000;
DEBUG(0, "dldwd_cs_config: We seem to have configured Vcc and Vpp\n"); DEBUG(0, "orinoco_cs_config: We seem to have configured Vcc and Vpp\n");
/* Do we need to allocate an interrupt? */ /* Do we need to allocate an interrupt? */
if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
...@@ -551,7 +571,12 @@ dldwd_cs_config(dev_link_t * link) ...@@ -551,7 +571,12 @@ dldwd_cs_config(dev_link_t * link)
next_entry: next_entry:
if (link->io.NumPorts1) if (link->io.NumPorts1)
CardServices(ReleaseIO, link->handle, &link->io); CardServices(ReleaseIO, link->handle, &link->io);
CS_CHECK(GetNextTuple, handle, &tuple); last_ret = CardServices(GetNextTuple, handle, &tuple);
if (last_ret == CS_NO_MORE_ITEMS) {
printk(KERN_ERR "GetNextTuple(). No matching CIS configuration, "
"maybe you need the ignore_cis_vcc=1 parameter.\n");
goto cs_failed;
}
} }
/* /*
...@@ -570,7 +595,7 @@ dldwd_cs_config(dev_link_t * link) ...@@ -570,7 +595,7 @@ dldwd_cs_config(dev_link_t * link)
for (i=0; i<4; i++) for (i=0; i<4; i++)
link->irq.IRQInfo2 |= 1 << irq_list[i]; link->irq.IRQInfo2 |= 1 << irq_list[i];
link->irq.Handler = dldwd_interrupt; link->irq.Handler = orinoco_interrupt;
link->irq.Instance = priv; link->irq.Instance = priv;
CS_CHECK(RequestIRQ, link->handle, &link->irq); CS_CHECK(RequestIRQ, link->handle, &link->irq);
...@@ -579,7 +604,8 @@ dldwd_cs_config(dev_link_t * link) ...@@ -579,7 +604,8 @@ dldwd_cs_config(dev_link_t * link)
/* We initialize the hermes structure before completing PCMCIA /* We initialize the hermes structure before completing PCMCIA
configuration just in case the interrupt handler gets configuration just in case the interrupt handler gets
called. */ called. */
hermes_struct_init(hw, link->io.BasePort1); hermes_struct_init(hw, link->io.BasePort1,
HERMES_IO, HERMES_16BIT_REGSPACING);
/* /*
This actually configures the PCMCIA socket -- setting up This actually configures the PCMCIA socket -- setting up
...@@ -618,7 +644,7 @@ dldwd_cs_config(dev_link_t * link) ...@@ -618,7 +644,7 @@ dldwd_cs_config(dev_link_t * link)
printk("\n"); printk("\n");
/* And give us the proc nodes for debugging */ /* And give us the proc nodes for debugging */
if (dldwd_proc_dev_init(priv) != 0) { if (orinoco_proc_dev_init(priv) != 0) {
printk(KERN_ERR "orinoco_cs: Failed to create /proc node for %s\n", printk(KERN_ERR "orinoco_cs: Failed to create /proc node for %s\n",
ndev->name); ndev->name);
goto failed; goto failed;
...@@ -627,12 +653,9 @@ dldwd_cs_config(dev_link_t * link) ...@@ -627,12 +653,9 @@ dldwd_cs_config(dev_link_t * link)
/* Note to myself : this replace MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT */ /* Note to myself : this replace MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT */
SET_MODULE_OWNER(ndev); SET_MODULE_OWNER(ndev);
/* Allow cor_reset, /proc & ioctls to act */ /* Let reset_cor parameter override determine_firmware()'s guess */
priv->hw_ready = 1; if (reset_cor != -1)
priv->broken_cor_reset = ! reset_cor;
/* Do a Pcmcia soft reset of the card (optional) */
if(reset_cor)
dldwd_cs_cor_reset(priv);
/* /*
At this point, the dev_node_t structure(s) need to be At this point, the dev_node_t structure(s) need to be
...@@ -642,29 +665,29 @@ dldwd_cs_config(dev_link_t * link) ...@@ -642,29 +665,29 @@ dldwd_cs_config(dev_link_t * link)
link->dev = &card->node; link->dev = &card->node;
link->state &= ~DEV_CONFIG_PENDING; link->state &= ~DEV_CONFIG_PENDING;
TRACE_EXIT("dldwd"); TRACE_EXIT("orinoco");
return; return;
cs_failed: cs_failed:
cs_error(link->handle, last_fn, last_ret); cs_error(link->handle, last_fn, last_ret);
failed: failed:
dldwd_cs_release((u_long) link); orinoco_cs_release((u_long) link);
TRACE_EXIT("dldwd"); TRACE_EXIT("orinoco");
} /* dldwd_cs_config */ } /* orinoco_cs_config */
/*====================================================================== /*======================================================================
After a card is removed, dldwd_cs_release() will unregister the After a card is removed, orinoco_cs_release() will unregister the
device, and release the PCMCIA configuration. If the device is device, and release the PCMCIA configuration. If the device is
still open, this will be postponed until it is closed. still open, this will be postponed until it is closed.
======================================================================*/ ======================================================================*/
static void static void
dldwd_cs_release(u_long arg) orinoco_cs_release(u_long arg)
{ {
dev_link_t *link = (dev_link_t *) arg; dev_link_t *link = (dev_link_t *) arg;
dldwd_priv_t *priv = link->priv; struct orinoco_private *priv = link->priv;
TRACE_ENTER(link->dev->dev_name); TRACE_ENTER(link->dev->dev_name);
...@@ -681,7 +704,7 @@ dldwd_cs_release(u_long arg) ...@@ -681,7 +704,7 @@ dldwd_cs_release(u_long arg)
} }
/* Unregister proc entry */ /* Unregister proc entry */
dldwd_proc_dev_cleanup(priv); orinoco_proc_dev_cleanup(priv);
/* Don't bother checking to see if these succeed or not */ /* Don't bother checking to see if these succeed or not */
CardServices(ReleaseConfiguration, link->handle); CardServices(ReleaseConfiguration, link->handle);
...@@ -692,7 +715,7 @@ dldwd_cs_release(u_long arg) ...@@ -692,7 +715,7 @@ dldwd_cs_release(u_long arg)
link->state &= ~DEV_CONFIG; link->state &= ~DEV_CONFIG;
TRACE_EXIT(link->dev->dev_name); TRACE_EXIT(link->dev->dev_name);
} /* dldwd_cs_release */ } /* orinoco_cs_release */
/*====================================================================== /*======================================================================
The card status event handler. Mostly, this schedules other The card status event handler. Mostly, this schedules other
...@@ -705,39 +728,37 @@ dldwd_cs_release(u_long arg) ...@@ -705,39 +728,37 @@ dldwd_cs_release(u_long arg)
======================================================================*/ ======================================================================*/
static int static int
dldwd_cs_event(event_t event, int priority, orinoco_cs_event(event_t event, int priority,
event_callback_args_t * args) event_callback_args_t * args)
{ {
dev_link_t *link = args->client_data; dev_link_t *link = args->client_data;
dldwd_priv_t *priv = (dldwd_priv_t *)link->priv; struct orinoco_private *priv = (struct orinoco_private *)link->priv;
struct net_device *dev = &priv->ndev; struct net_device *dev = priv->ndev;
TRACE_ENTER("dldwd"); TRACE_ENTER("orinoco");
switch (event) { switch (event) {
case CS_EVENT_CARD_REMOVAL: case CS_EVENT_CARD_REMOVAL:
/* FIXME: Erg.. this whole hw_ready thing looks racy
to me. this may not be fixable without changin the
PCMCIA subsystem, though */
priv->hw_ready = 0;
dldwd_shutdown(priv);
link->state &= ~DEV_PRESENT; link->state &= ~DEV_PRESENT;
if (link->state & DEV_CONFIG) { if (link->state & DEV_CONFIG) {
netif_stop_queue(dev); netif_stop_queue(dev);
}
orinoco_shutdown(priv);
if (link->state & DEV_CONFIG) {
netif_device_detach(dev); netif_device_detach(dev);
mod_timer(&link->release, jiffies + HZ / 20); mod_timer(&link->release, jiffies + HZ / 20);
} }
break; break;
case CS_EVENT_CARD_INSERTION: case CS_EVENT_CARD_INSERTION:
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
dldwd_cs_config(link); orinoco_cs_config(link);
break; break;
case CS_EVENT_PM_SUSPEND: case CS_EVENT_PM_SUSPEND:
link->state |= DEV_SUSPEND; link->state |= DEV_SUSPEND;
/* Fall through... */ /* Fall through... */
case CS_EVENT_RESET_PHYSICAL: case CS_EVENT_RESET_PHYSICAL:
dldwd_shutdown(priv); orinoco_shutdown(priv);
/* Mark the device as stopped, to block IO until later */ /* Mark the device as stopped, to block IO until later */
if (link->state & DEV_CONFIG) { if (link->state & DEV_CONFIG) {
...@@ -757,13 +778,13 @@ dldwd_cs_event(event_t event, int priority, ...@@ -757,13 +778,13 @@ dldwd_cs_event(event_t event, int priority,
&link->conf); &link->conf);
if (link->open) { if (link->open) {
if (dldwd_reset(priv) == 0) { if (orinoco_reset(priv) == 0) {
netif_device_attach(dev); netif_device_attach(dev);
netif_start_queue(dev); netif_start_queue(dev);
} else { } else {
printk(KERN_ERR "%s: Error resetting device on PCMCIA event\n", printk(KERN_ERR "%s: Error resetting device on PCMCIA event\n",
dev->name); dev->name);
dldwd_cs_stop(dev); orinoco_cs_stop(dev);
} }
} }
} }
...@@ -774,17 +795,17 @@ dldwd_cs_event(event_t event, int priority, ...@@ -774,17 +795,17 @@ dldwd_cs_event(event_t event, int priority,
break; break;
} }
TRACE_EXIT("dldwd"); TRACE_EXIT("orinoco");
return 0; return 0;
} /* dldwd_cs_event */ } /* orinoco_cs_event */
static int __init static int __init
init_dldwd_cs(void) init_orinoco_cs(void)
{ {
servinfo_t serv; servinfo_t serv;
TRACE_ENTER("dldwd"); TRACE_ENTER("orinoco");
printk(KERN_DEBUG "%s\n", version); printk(KERN_DEBUG "%s\n", version);
...@@ -795,17 +816,17 @@ init_dldwd_cs(void) ...@@ -795,17 +816,17 @@ init_dldwd_cs(void)
return -1; return -1;
} }
register_pccard_driver(&dev_info, &dldwd_cs_attach, &dldwd_cs_detach); register_pccard_driver(&dev_info, &orinoco_cs_attach, &orinoco_cs_detach);
TRACE_EXIT("dldwd"); TRACE_EXIT("orinoco");
return 0; return 0;
} }
static void __exit static void __exit
exit_dldwd_cs(void) exit_orinoco_cs(void)
{ {
TRACE_ENTER("dldwd"); TRACE_ENTER("orinoco");
unregister_pccard_driver(&dev_info); unregister_pccard_driver(&dev_info);
...@@ -814,12 +835,12 @@ exit_dldwd_cs(void) ...@@ -814,12 +835,12 @@ exit_dldwd_cs(void)
while (dev_list != NULL) { while (dev_list != NULL) {
del_timer(&dev_list->release); del_timer(&dev_list->release);
if (dev_list->state & DEV_CONFIG) if (dev_list->state & DEV_CONFIG)
dldwd_cs_release((u_long) dev_list); orinoco_cs_release((u_long) dev_list);
dldwd_cs_detach(dev_list); orinoco_cs_detach(dev_list);
} }
TRACE_EXIT("dldwd"); TRACE_EXIT("orinoco");
} }
module_init(init_dldwd_cs); module_init(init_orinoco_cs);
module_exit(exit_dldwd_cs); module_exit(exit_orinoco_cs);
/* orinoco_pci.c 0.01
*
* Driver for Prism II devices that have a direct PCI interface
* (i.e., not in a Pcmcia or PLX bridge)
*
* Specifically here we're talking about the Linksys WMP11
*
* Some of this code is borrowed from orinoco_plx.c
* Copyright (C) 2001 Daniel Barlow <dan@telent.net>
* 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 :
* Copyright (C) 2001 Jean Tourrilhes <jt@hpl.hp.com>
*
* 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.
*/
/*
* Theory of operation...
* -------------------
* Maybe you had a look in orinoco_plx. Well, this is totally different...
*
* The card contains only one PCI region, which contains all the usual
* hermes registers.
*
* The driver will memory map this region in normal memory. Because
* the hermes registers are mapped in normal memory and not in ISA I/O
* post space, we can't use the usual inw/outw macros and we need to
* use readw/writew.
* This slight difference force us to compile our own version of
* hermes.c with the register access macro changed. That's a bit
* hackish but works fine.
*
* Note that the PCI region is pretty big (4K). That's much more than
* the usual set of hermes register (0x0 -> 0x3E). I've got a strong
* suspicion that the whole memory space of the adapter is in fact in
* this region. Accessing directly the adapter memory instead of going
* through the usual register would speed up significantely the
* operations...
*
* Finally, the card looks like this :
-----------------------
Bus 0, device 14, function 0:
Network controller: PCI device 1260:3873 (Harris Semiconductor) (rev 1).
IRQ 11.
Master Capable. Latency=248.
Prefetchable 32 bit memory at 0xffbcc000 [0xffbccfff].
-----------------------
00:0e.0 Network controller: Harris Semiconductor: Unknown device 3873 (rev 01)
Subsystem: Unknown device 1737:3874
Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR- FastB2B-
Status: Cap+ 66Mhz- UDF- FastB2B+ ParErr- DEVSEL=medium >TAbort- <TAbort- <MAbort- >SERR- <PERR-
Latency: 248 set, cache line size 08
Interrupt: pin A routed to IRQ 11
Region 0: Memory at ffbcc000 (32-bit, prefetchable) [size=4K]
Capabilities: [dc] Power Management version 2
Flags: PMEClk- AuxPwr- DSI- D1+ D2+ PME+
Status: D0 PME-Enable- DSel=0 DScale=0 PME-
-----------------------
*
* That's all..
*
* Jean II
*/
#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/proc_fs.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 "hermes.h"
#include "orinoco.h"
/* All the magic there is from wlan-ng */
/* Magic offset of the reset register of the PCI card */
#define HERMES_PCI_COR (0x26)
/* Magic bitmask to reset the card */
#define HERMES_PCI_COR_MASK (0x0080)
/* Magic timeouts for doing the reset.
* Those times are straight from wlan-ng, and it is claimed that they
* are necessary. Alan will kill me. Take your time and grab a coffee. */
#define HERMES_PCI_COR_ONT (250) /* ms */
#define HERMES_PCI_COR_OFFT (500) /* ms */
#define HERMES_PCI_COR_BUSYT (500) /* ms */
MODULE_AUTHOR("Jean Tourrilhes <jt@hpl.hp.com>");
MODULE_DESCRIPTION("Driver for wireless LAN cards using direct PCI interface");
MODULE_LICENSE("Dual MPL/GPL");
static int orinoco_pci_open(struct net_device *dev)
{
struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
int err;
netif_device_attach(dev);
err = orinoco_reset(priv);
if (err)
printk(KERN_ERR "%s: orinoco_reset failed in orinoco_pci_open()",
dev->name);
else
netif_start_queue(dev);
return err;
}
static int orinoco_pci_stop(struct net_device *dev)
{
struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
netif_stop_queue(dev);
orinoco_shutdown(priv);
return 0;
}
static void
orinoco_pci_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
orinoco_interrupt(irq, (struct orinoco_private *)dev_id, regs);
}
/*
* Do a soft reset of the PCI card using the Configuration Option Register
* We need this to get going...
* This is the part of the code that is strongly inspired from wlan-ng
*
* Note : This code is done with irq enabled. This mean that many
* interrupts will occur while we are there. This is why we use the
* jiffies to regulate time instead of a straight mdelay(). Usually we
* need only around 245 iteration of the loop to do 250 ms delay.
*
* Note bis : Don't try to access HERMES_CMD during the reset phase.
* It just won't work !
*/
static int
orinoco_pci_cor_reset(struct orinoco_private *priv)
{
hermes_t *hw = &priv->hw;
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");
timeout = jiffies + (HERMES_PCI_COR_ONT * HZ / 1000);
while(time_before(jiffies, timeout)) {
printk(".");
mdelay(1);
}
printk(";\n");
//mdelay(HERMES_PCI_COR_ONT);
/* Give time for the card to recover from this hard effort */
hermes_write_regn(hw, PCI_COR, 0x0000);
printk(KERN_NOTICE "Clear Reset");
timeout = jiffies + (HERMES_PCI_COR_OFFT * HZ / 1000);
while(time_before(jiffies, timeout)) {
printk(".");
mdelay(1);
}
printk(";\n");
//mdelay(HERMES_PCI_COR_OFFT);
/* The card is ready when it's no longer busy */
timeout = jiffies + (HERMES_PCI_COR_BUSYT * HZ / 1000);
reg = hermes_read_regn(hw, CMD);
while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) {
mdelay(1);
reg = hermes_read_regn(hw, CMD);
}
/* Did we timeout ? */
if(time_after_eq(jiffies, timeout)) {
printk(KERN_ERR "orinoco_pci: Busy timeout\n");
return -ETIMEDOUT;
}
printk(KERN_NOTICE "pci_cor : reg = 0x%X - %lX - %lX\n", reg, timeout, jiffies);
TRACE_EXIT(priv->ndev->name);
return 0;
}
/*
* Initialise a card. Mostly similar to PLX code.
*/
static int orinoco_pci_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
int err = 0;
unsigned long pci_iorange;
u16 *pci_ioaddr = NULL;
unsigned long pci_iolen;
struct orinoco_private *priv = NULL;
struct net_device *dev = NULL;
int netdev_registered = 0;
TRACE_ENTER("orinoco_pci");
err = pci_enable_device(pdev);
if (err)
return -EIO;
/* Resource 0 is mapped to the hermes registers */
pci_iorange = pci_resource_start(pdev, 0);
pci_iolen = pci_resource_len(pdev, 0);
pci_ioaddr = ioremap(pci_iorange, pci_iolen);
if (! pci_iorange)
goto fail;
/* Usual setup of structures */
dev = alloc_orinocodev(0);
if (! dev) {
err = -ENOMEM;
goto fail;
}
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->open = orinoco_pci_open;
dev->stop = orinoco_pci_stop;
/* priv->card_reset_handler = orinoco_pci_cor_reset; */
SET_MODULE_OWNER(dev);
printk(KERN_DEBUG
"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);
pci_set_drvdata(pdev, priv);
err = request_irq(pdev->irq, orinoco_pci_interrupt, SA_SHIRQ, dev->name, priv);
if (err) {
printk(KERN_ERR "orinoco_pci: Error allocating IRQ %d.\n", pdev->irq);
err = -EBUSY;
goto fail;
}
dev->irq = pdev->irq;
/* Perform a COR reset to start the card */
if(orinoco_pci_cor_reset(priv) != 0) {
printk(KERN_ERR "%s: Failed to start the card\n", dev->name);
err = -ETIMEDOUT;
goto fail;
}
/* Override the normal firmware detection - the Prism 2.5 PCI
* cards look like Lucent firmware but are actually Intersil */
priv->firmware_type = FIRMWARE_TYPE_INTERSIL;
err = register_netdev(dev);
if (err)
goto fail;
netdev_registered = 1;
err = orinoco_proc_dev_init(priv);
if (err) {
printk(KERN_ERR "%s: Failed to create /proc node\n", dev->name);
err = -EIO;
goto fail;
}
TRACE_EXIT("orinoco_pci");
return 0; /* succeeded */
fail:
printk(KERN_DEBUG "orinoco_pci: init_one(), FAIL!\n");
if (priv) {
orinoco_proc_dev_cleanup(priv);
if (netdev_registered)
unregister_netdev(dev);
if (dev->irq)
free_irq(dev->irq, priv);
kfree(priv);
}
if (pci_ioaddr)
iounmap(pci_ioaddr);
return err;
}
static void __devexit orinoco_pci_remove_one(struct pci_dev *pdev)
{
struct orinoco_private *priv = pci_get_drvdata(pdev);
struct net_device *dev = priv->ndev;
TRACE_ENTER("orinoco_pci");
if (!priv)
BUG();
orinoco_proc_dev_cleanup(priv);
unregister_netdev(dev);
if (dev->irq)
free_irq(dev->irq, priv);
if (priv->hw.iobase)
iounmap((unsigned char *) priv->hw.iobase);
kfree(priv);
pci_disable_device(pdev);
TRACE_EXIT("orinoco_pci");
}
static struct pci_device_id orinoco_pci_pci_id_table[] __devinitdata = {
{0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,},
{0,},
};
MODULE_DEVICE_TABLE(pci, orinoco_pci_pci_id_table);
static struct pci_driver orinoco_pci_driver = {
name:"orinoco_pci",
id_table:orinoco_pci_pci_id_table,
probe:orinoco_pci_init_one,
remove:orinoco_pci_remove_one,
suspend:0,
resume:0
};
static int __init orinoco_pci_init(void)
{
return pci_module_init(&orinoco_pci_driver);
}
extern void __exit orinoco_pci_exit(void)
{
pci_unregister_driver(&orinoco_pci_driver);
}
module_init(orinoco_pci_init);
module_exit(orinoco_pci_exit);
/*
* Local variables:
* c-indent-level: 8
* c-basic-offset: 8
* tab-width: 8
* End:
*/
/* orinoco_plx.c 0.01 /* orinoco_plx.c 0.11a
* *
* Driver for Prism II devices which would usually be driven by orinoco_cs, * Driver for Prism II devices which would usually be driven by orinoco_cs,
* but are connected to the PCI bus by a PLX9052. * but are connected to the PCI bus by a PLX9052.
* *
* Specifically here we're talking about the SMC2602W (EZConnect
* Wireless PCI Adaptor)
*
* The actual driving is done by orinoco.c, this is just resource
* allocation stuff. The explanation below is courtesy of Ryan Niemi
* on the linux-wlan-ng list at
* http://archives.neohapsis.com/archives/dev/linux-wlan/2001-q1/0026.html
*
* Copyright (C) 2001 Daniel Barlow <dan@telent.net> * Copyright (C) 2001 Daniel Barlow <dan@telent.net>
* *
* The contents of this file are subject to the Mozilla Public License * The contents of this file are subject to the Mozilla Public License
...@@ -34,6 +26,22 @@ ...@@ -34,6 +26,22 @@
* provisions above, a recipient may use your version of this file * provisions above, a recipient may use your version of this file
* under either the MPL or the GPL. * 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_plx_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. The explanation below is courtesy of Ryan Niemi
* on the linux-wlan-ng list at
* http://archives.neohapsis.com/archives/dev/linux-wlan/2001-q1/0026.html
The PLX9052-based cards (WL11000 and several others) are a different The PLX9052-based cards (WL11000 and several others) are a different
beast than the usual PCMCIA-based PRISM2 configuration expected by beast than the usual PCMCIA-based PRISM2 configuration expected by
wlan-ng. Here's the general details on how the WL11000 PCI adapter wlan-ng. Here's the general details on how the WL11000 PCI adapter
...@@ -95,14 +103,6 @@ to implement support myself yet, and with the way things are going, might ...@@ -95,14 +103,6 @@ to implement support myself yet, and with the way things are going, might
not have time for a while.. not have time for a while..
---end of mail--- ---end of mail---
Bus 0, device 4, function 0:
Network controller: Unknown vendor Unknown device (rev 2).
Vendor id=1638. Device id=1100.
Medium devsel. Fast back-to-back capable. IRQ 10.
I/O at 0x1000 [0x1001].
Non-prefetchable 32 bit memory at 0x40000000 [0x40000000].
I/O at 0x10c0 [0x10c1].
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -129,36 +129,36 @@ not have time for a while.. ...@@ -129,36 +129,36 @@ not have time for a while..
#include <linux/wireless.h> #include <linux/wireless.h>
#include <linux/fcntl.h> #include <linux/fcntl.h>
#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/cisreg.h> #include <pcmcia/cisreg.h>
#include <pcmcia/ds.h>
#include <pcmcia/bus_ops.h>
#include "hermes.h" #include "hermes.h"
#include "orinoco.h" #include "orinoco.h"
static char version[] __initdata = "orinoco_plx.c 0.11a (Daniel Barlow <dan@telent.net>)";
MODULE_AUTHOR("Daniel Barlow <dan@telent.net>"); MODULE_AUTHOR("Daniel Barlow <dan@telent.net>");
MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge"); MODULE_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge");
#ifdef MODULE_LICENSE
MODULE_LICENSE("Dual MPL/GPL"); MODULE_LICENSE("Dual MPL/GPL");
#endif
static dev_info_t dev_info = "orinoco_plx"; static char dev_info[] = "orinoco_plx";
#define COR_OFFSET 0x3e0 /* COR attribute offset of Prism2 PC card */ #define COR_OFFSET (0x3e0 / 2) /* COR attribute offset of Prism2 PC card */
#define COR_VALUE 0x41 /* Enable PC card with interrupt in level trigger */ #define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */
#define PLX_INTCSR 0x4c /* Interrupt Control and Status Register */
#define PLX_INTCSR_INTEN (1<<6) /* Interrupt Enable bit */
static int orinoco_plx_open(struct net_device *dev) static int orinoco_plx_open(struct net_device *dev)
{ {
dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv; struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
int err; int err;
netif_device_attach(dev); netif_device_attach(dev);
err = dldwd_reset(priv); err = orinoco_reset(priv);
if (err) if (err)
printk(KERN_ERR "%s: dldwd_reset failed in orinoco_plx_open()", printk(KERN_ERR "%s: orinoco_reset failed in orinoco_plx_open()",
dev->name); dev->name);
else else
netif_start_queue(dev); netif_start_queue(dev);
...@@ -168,110 +168,204 @@ static int orinoco_plx_open(struct net_device *dev) ...@@ -168,110 +168,204 @@ static int orinoco_plx_open(struct net_device *dev)
static int orinoco_plx_stop(struct net_device *dev) static int orinoco_plx_stop(struct net_device *dev)
{ {
dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv; struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
netif_stop_queue(dev); netif_stop_queue(dev);
dldwd_shutdown(priv); orinoco_shutdown(priv);
return 0; return 0;
} }
static void static void
orinoco_plx_interrupt(int irq, void *dev_id, struct pt_regs *regs) orinoco_plx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{ {
dldwd_interrupt(irq, ((struct net_device *) dev_id)->priv, regs); orinoco_interrupt(irq, (struct orinoco_private *)dev_id, regs);
} }
static const u16 cis_magic[] = {
0x0001, 0x0003, 0x0000, 0x0000, 0x00ff, 0x0017, 0x0004, 0x0067
};
static int orinoco_plx_init_one(struct pci_dev *pdev, static int orinoco_plx_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent) const struct pci_device_id *ent)
{ {
struct net_device *dev; int err = 0;
unsigned long pccard_ioaddr; u16 *attr_mem = NULL;
u32 reg, addr;
struct orinoco_private *priv = NULL;
unsigned long pccard_ioaddr = 0;
unsigned long pccard_iolen = 0;
struct net_device *dev = NULL;
int netdev_registered = 0;
int i; int i;
int reg;
unsigned char *attr_mem;
dldwd_priv_t *priv;
if ((i = pci_enable_device(pdev))) TRACE_ENTER("orinoco_plx");
err = pci_enable_device(pdev);
if (err)
return -EIO; return -EIO;
/* Resource 2 is mapped to the PCMCIA space */ /* Resource 2 is mapped to the PCMCIA space */
attr_mem = ioremap(pci_resource_start(pdev, 2), 0x1000); attr_mem = ioremap(pci_resource_start(pdev, 2), PAGE_SIZE);
/* and 3 to the PCMCIA slot I/O address space */ if (! attr_mem)
pccard_ioaddr = pci_resource_start(pdev, 3); goto fail;
printk(KERN_DEBUG "orinoco_plx: CIS: ");
for (i = 0; i < 16; i++) {
printk("%02X:", (int)attr_mem[i]);
}
printk("\n");
/* Verify whether PC card is present */ /* Verify whether PC card is present */
if (attr_mem[0] != 0x01 || attr_mem[2] != 0x03 || /* FIXME: we probably need to be smarted about this */
attr_mem[4] != 0x00 || attr_mem[6] != 0x00 || if (memcmp(attr_mem, cis_magic, sizeof(cis_magic)) != 0) {
attr_mem[8] != 0xFF || attr_mem[10] != 0x17 ||
attr_mem[12] != 0x04 || attr_mem[14] != 0x67) {
printk(KERN_ERR "orinoco_plx: The CIS value of Prism2 PC card is invalid.\n"); printk(KERN_ERR "orinoco_plx: The CIS value of Prism2 PC card is invalid.\n");
return -EIO; err = -EIO;
goto fail;
} }
/* PCMCIA COR is the first byte following CIS: this write should /* PCMCIA COR is the first byte following CIS: this write should
* enable I/O mode and select level-triggered interrupts */ * enable I/O mode and select level-triggered interrupts */
attr_mem[COR_OFFSET] = COR_VALUE; attr_mem[COR_OFFSET] = COR_VALUE;
mdelay(1);
reg = attr_mem[COR_OFFSET]; reg = attr_mem[COR_OFFSET];
/* assert(reg==COR_VALUE); doesn't work */ if (reg != COR_VALUE) {
iounmap(attr_mem); /* done with this now, it seems */ printk(KERN_ERR "orinoco_plx: Error setting COR value (reg=%x)\n", reg);
if (!request_region(pccard_ioaddr, goto fail;
pci_resource_len(pdev, 3), dev_info)) { }
iounmap(attr_mem);
attr_mem = NULL; /* done with this now, it seems */
/* bjoern: We need to tell the card to enable interrupts, in
case the serial eprom didn't do this already. See the
PLX9052 data book, p8-1 and 8-24 for reference. */
addr = pci_resource_start(pdev, 1);
reg = 0;
reg = inl(addr+PLX_INTCSR);
if(reg & PLX_INTCSR_INTEN)
printk(KERN_DEBUG "orinoco_plx: "
"Local Interrupt already enabled\n");
else {
reg |= PLX_INTCSR_INTEN;
outl(reg, addr+PLX_INTCSR);
reg = inl(addr+PLX_INTCSR);
if(!(reg & PLX_INTCSR_INTEN)) {
printk(KERN_ERR "orinoco_plx: "
"Couldn't enable Local Interrupts\n");
goto fail;
}
}
/* and 3 to the PCMCIA slot I/O address space */
pccard_ioaddr = pci_resource_start(pdev, 3);
pccard_iolen = pci_resource_len(pdev, 3);
if (! request_region(pccard_ioaddr, pccard_iolen, dev_info)) {
printk(KERN_ERR "orinoco_plx: I/O resource 0x%lx @ 0x%lx busy\n", printk(KERN_ERR "orinoco_plx: I/O resource 0x%lx @ 0x%lx busy\n",
pci_resource_len(pdev, 3), pccard_ioaddr); pccard_iolen, pccard_ioaddr);
return -EBUSY; pccard_ioaddr = 0;
err = -EBUSY;
goto fail;
} }
if (!(priv = kmalloc(sizeof(*priv), GFP_KERNEL)))
return -ENOMEM;
memset(priv, 0, sizeof(*priv));
dev = &priv->ndev;
dldwd_setup(priv); /* XXX clean up if <0 */ dev = alloc_orinocodev(0);
dev->irq = pdev->irq; if (! dev) {
err = -ENOMEM;
goto fail;
}
priv = dev->priv;
dev->base_addr = pccard_ioaddr; dev->base_addr = pccard_ioaddr;
dev->open = orinoco_plx_open; dev->open = orinoco_plx_open;
dev->stop = orinoco_plx_stop; dev->stop = orinoco_plx_stop;
priv->card_reset_handler = NULL; /* We have no reset handler */ SET_MODULE_OWNER(dev);
printk(KERN_DEBUG printk(KERN_DEBUG
"Detected Orinoco/Prism2 PCI device at %s, mem:0x%lx, irq:%d, io addr:0x%lx\n", "Detected Orinoco/Prism2 PLX device at %s irq:%d, io addr:0x%lx\n",
pdev->slot_name, (long) attr_mem, pdev->irq, pccard_ioaddr); pdev->slot_name, pdev->irq, pccard_ioaddr);
hermes_struct_init(&(priv->hw), dev->base_addr); /* XXX */ hermes_struct_init(&(priv->hw), dev->base_addr,
dev->name[0] = '\0'; /* name defaults to ethX */ HERMES_IO, HERMES_16BIT_REGSPACING);
register_netdev(dev); pci_set_drvdata(pdev, priv);
request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name,
dev); err = request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name, priv);
if (dldwd_proc_dev_init(priv) != 0) { if (err) {
printk(KERN_ERR "%s: Failed to create /proc node\n", dev->name); printk(KERN_ERR "orinoco_plx: Error allocating IRQ %d.\n", pdev->irq);
return -EIO; err = -EBUSY;
goto fail;
} }
dev->irq = pdev->irq;
SET_MODULE_OWNER(dev); err = register_netdev(dev);
priv->hw_ready = 1; if (err)
goto fail;
netdev_registered = 1;
err = orinoco_proc_dev_init(priv);
if (err)
goto fail;
TRACE_EXIT("orinoco_plx");
/* if(reset_cor) dldwd_cs_cor_reset(priv); */
return 0; /* succeeded */ return 0; /* succeeded */
fail:
printk(KERN_DEBUG "orinoco_plx: init_one(), FAIL!\n");
if (priv) {
orinoco_proc_dev_cleanup(priv);
if (netdev_registered)
unregister_netdev(dev);
if (dev->irq)
free_irq(dev->irq, priv);
kfree(priv);
}
if (pccard_ioaddr)
release_region(pccard_ioaddr, pccard_iolen);
if (attr_mem)
iounmap(attr_mem);
pci_disable_device(pdev);
TRACE_EXIT("orinoco_plx");
return err;
} }
static void __devexit orinoco_plx_remove_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 = pci_get_drvdata(pdev);
dldwd_priv_t *priv = dev->priv; struct net_device *dev = priv->ndev;
TRACE_ENTER("orinoco_plx");
if (!dev) if (!priv)
BUG(); BUG();
dldwd_proc_dev_cleanup(priv); orinoco_proc_dev_cleanup(priv);
free_irq(dev->irq, dev);
unregister_netdev(dev); unregister_netdev(dev);
release_region(dev->base_addr, 0x40);
kfree(dev->priv); if (dev->irq)
pci_set_drvdata(pdev, NULL); free_irq(dev->irq, priv);
kfree(priv);
release_region(pci_resource_start(pdev, 3), pci_resource_len(pdev, 3));
pci_disable_device(pdev);
TRACE_EXIT("orinoco_plx");
} }
static struct pci_device_id orinoco_plx_pci_id_table[] __devinitdata = { static struct pci_device_id orinoco_plx_pci_id_table[] __devinitdata = {
{0x111a, 0x1023, PCI_ANY_ID, PCI_ANY_ID,}, /* Siemens SpeedStream SS1023 */
{0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,}, /* Netgear MA301 */ {0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,}, /* Netgear MA301 */
#if 0 {0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,}, /* Correga - does this work? */
{0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,}, /* Correga */
#endif
{0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* SMC EZConnect SMC2602W, {0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* SMC EZConnect SMC2602W,
Eumitcom PCI WL11000, Eumitcom PCI WL11000,
Addtron AWA-100*/ Addtron AWA-100*/
...@@ -279,26 +373,34 @@ static struct pci_device_id orinoco_plx_pci_id_table[] __devinitdata = { ...@@ -279,26 +373,34 @@ static struct pci_device_id orinoco_plx_pci_id_table[] __devinitdata = {
{0x16ab, 0x1101, PCI_ANY_ID, PCI_ANY_ID,}, /* Reported working, but unknown */ {0x16ab, 0x1101, PCI_ANY_ID, PCI_ANY_ID,}, /* Reported working, but unknown */
{0x16ab, 0x1102, PCI_ANY_ID, PCI_ANY_ID,}, /* Linksys WDT11 */ {0x16ab, 0x1102, PCI_ANY_ID, PCI_ANY_ID,}, /* Linksys WDT11 */
{0x16ec, 0x3685, PCI_ANY_ID, PCI_ANY_ID,}, /* USR 2415 */ {0x16ec, 0x3685, PCI_ANY_ID, PCI_ANY_ID,}, /* USR 2415 */
{0xec80, 0xec00, PCI_ANY_ID, PCI_ANY_ID,}, /* Belkin F5D6000 */ {0xec80, 0xec00, PCI_ANY_ID, PCI_ANY_ID,}, /* Belkin F5D6000 tested by
Brendan W. McAdams <rit@jacked-in.org> */
{0x126c, 0x8030, PCI_ANY_ID, PCI_ANY_ID,}, /* Nortel emobility */
{0,}, {0,},
}; };
MODULE_DEVICE_TABLE(pci, orinoco_plx_pci_id_table); MODULE_DEVICE_TABLE(pci, orinoco_plx_pci_id_table);
static struct pci_driver orinoco_plx_driver = { static struct pci_driver orinoco_plx_driver = {
name: "orinoco_plx", name:"orinoco_plx",
id_table: orinoco_plx_pci_id_table, id_table:orinoco_plx_pci_id_table,
probe: orinoco_plx_init_one, probe:orinoco_plx_init_one,
remove: __devexit_p(orinoco_plx_remove_one), remove:orinoco_plx_remove_one,
suspend:0,
resume:0
}; };
static int __init orinoco_plx_init(void) static int __init orinoco_plx_init(void)
{ {
printk(KERN_DEBUG "%s\n", version);
return pci_module_init(&orinoco_plx_driver); return pci_module_init(&orinoco_plx_driver);
} }
extern void __exit orinoco_plx_exit(void) extern void __exit orinoco_plx_exit(void)
{ {
pci_unregister_driver(&orinoco_plx_driver); pci_unregister_driver(&orinoco_plx_driver);
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ);
} }
module_init(orinoco_plx_init); module_init(orinoco_plx_init);
......
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