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
L: linux-sound@vger.kernel.org
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
P: Phil Blundell
M: Philip.Blundell@pobox.com
......
......@@ -55,6 +55,14 @@ CONFIG_PLX_HERMES
Support for these adaptors is so far still incomplete and buggy.
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
A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
......
......@@ -20,6 +20,7 @@ fi
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 ' Prism 2.5 PCI 802.11b adaptor support' CONFIG_PCI_HERMES $CONFIG_HERMES $CONFIG_EXPERIMENTAL
fi
# If Pcmcia is compiled in, offer Pcmcia cards...
......
......@@ -23,6 +23,7 @@ obj-$(CONFIG_HERMES) += orinoco.o hermes.o
obj-$(CONFIG_PCMCIA_HERMES) += orinoco_cs.o
obj-$(CONFIG_APPLE_AIRPORT) += airport.o
obj-$(CONFIG_PLX_HERMES) += orinoco_plx.o
obj-$(CONFIG_PCI_HERMES) += orinoco_pci.o
obj-$(CONFIG_AIRO) += 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
* card.
......@@ -11,6 +11,8 @@
* 0.06 : fix possible hang on powerup, add sleep support
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
......@@ -33,26 +35,28 @@
#include <linux/pmu.h>
#include <asm/prom.h>
#include <asm/feature.h>
#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/irq.h>
#include "hermes.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_DESCRIPTION("Driver for the Apple Airport wireless card.");
MODULE_LICENSE("Dual MPL/GPL");
EXPORT_NO_SYMBOLS;
typedef struct dldwd_card {
#define AIRPORT_IO_LEN (0x1000) /* one page */
struct airport {
struct device_node* node;
void *vaddr;
int irq_requested;
int ndev_registered;
int open;
/* Common structure (fully included), see orinoco.h */
struct dldwd_priv priv;
} dldwd_card_t;
};
#ifdef CONFIG_PMAC_PBOOK
static int airport_sleep_notify(struct pmu_sleep_notifier *self, int when);
......@@ -65,9 +69,8 @@ static struct pmu_sleep_notifier airport_sleep_notifier = {
* Function prototypes
*/
static dldwd_priv_t* airport_attach(struct device_node *of_node);
static void airport_detach(dldwd_priv_t* priv);
static int airport_init(struct net_device *dev);
static struct net_device *airport_attach(struct device_node *of_node);
static void airport_detach(struct net_device *dev);
static int airport_open(struct net_device *dev);
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.
*/
static dldwd_priv_t *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 struct net_device *airport_dev;
static int
airport_open(struct net_device *dev)
{
dldwd_priv_t *priv = dev->priv;
dldwd_card_t* card = (dldwd_card_t *)priv->card;
struct orinoco_private *priv = dev->priv;
struct airport* card = (struct airport *)priv->card;
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)
airport_stop(dev);
else {
card->open = 1;
netif_device_attach(dev);
netif_start_queue(dev);
}
// TRACE_EXIT(priv->ndev.name);
TRACE_EXIT(dev->name);
return rc;
}
......@@ -126,16 +113,16 @@ airport_open(struct net_device *dev)
static int
airport_stop(struct net_device *dev)
{
dldwd_priv_t *priv = dev->priv;
dldwd_card_t* card = (dldwd_card_t *)priv->card;
struct orinoco_private *priv = dev->priv;
struct airport* card = (struct airport *)priv->card;
TRACE_ENTER(priv->ndev.name);
TRACE_ENTER(dev->name);
netif_stop_queue(dev);
dldwd_shutdown(priv);
orinoco_shutdown(priv);
card->open = 0;
TRACE_EXIT(priv->ndev.name);
TRACE_EXIT(dev->name);
return 0;
}
......@@ -144,16 +131,14 @@ airport_stop(struct net_device *dev)
static int
airport_sleep_notify(struct pmu_sleep_notifier *self, int when)
{
dldwd_priv_t *priv;
struct net_device *ndev;
dldwd_card_t* card;
struct net_device *dev = airport_dev;
struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
struct hermes *hw = &priv->hw;
struct airport* card = (struct airport *)priv->card;
int rc;
if (!airport_dev)
if (! airport_dev)
return PBOOK_SLEEP_OK;
priv = airport_dev;
ndev = &priv->ndev;
card = (dldwd_card_t *)priv->card;
switch (when) {
case PBOOK_SLEEP_REQUEST:
......@@ -161,41 +146,42 @@ airport_sleep_notify(struct pmu_sleep_notifier *self, int when)
case PBOOK_SLEEP_REJECT:
break;
case PBOOK_SLEEP_NOW:
printk(KERN_INFO "%s: Airport entering sleep mode\n", ndev->name);
netif_device_detach(ndev);
if (card->open)
dldwd_shutdown(priv);
disable_irq(ndev->irq);
feature_set_airport_power(card->node, 0);
priv->hw_ready = 0;
printk(KERN_INFO "%s: Airport entering sleep mode\n", dev->name);
if (card->open) {
netif_stop_queue(dev);
orinoco_shutdown(priv);
netif_device_detach(dev);
}
disable_irq(dev->irq);
pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 0);
break;
case PBOOK_WAKE:
printk(KERN_INFO "%s: Airport waking up\n", ndev->name);
feature_set_airport_power(card->node, 1);
printk(KERN_INFO "%s: Airport waking up\n", dev->name);
pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 1);
mdelay(200);
hermes_reset(&priv->hw);
priv->hw_ready = 1;
rc = dldwd_reset(priv);
hermes_reset(hw);
rc = orinoco_reset(priv);
if (rc)
printk(KERN_ERR "airport: Error %d re-initing card !\n", rc);
else if (card->open)
netif_device_attach(ndev);
enable_irq(ndev->irq);
netif_device_attach(dev);
enable_irq(dev->irq);
break;
}
return PBOOK_SLEEP_OK;
}
#endif /* CONFIG_PMAC_PBOOK */
static dldwd_priv_t*
static struct net_device *
airport_attach(struct device_node* of_node)
{
dldwd_priv_t *priv;
struct net_device *ndev;
dldwd_card_t* card;
struct orinoco_private *priv;
struct net_device *dev;
struct airport *card;
unsigned long phys_addr;
hermes_t *hw;
TRACE_ENTER("dldwd");
TRACE_ENTER("orinoco");
if (of_node->n_addrs < 1 || of_node->n_intrs < 1) {
printk(KERN_ERR "airport: wrong interrupt/addresses in OF tree\n");
......@@ -203,74 +189,77 @@ airport_attach(struct device_node* of_node)
}
/* Allocate space for private device-specific data */
card = kmalloc(sizeof(*card), GFP_KERNEL);
if (!card) {
dev = alloc_orinocodev(sizeof(*card));
if (! dev) {
printk(KERN_ERR "airport: can't allocate device datas\n");
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;
card->node = of_node;
/* Setup the common part */
if (dldwd_setup(priv) < 0) {
kfree(card);
if (! request_OF_resource(of_node, 0, " (airport)")) {
printk(KERN_ERR "airport: can't request IO resource !\n");
kfree(dev);
return NULL;
}
dev->name[0] = '\0'; /* register_netdev will give us an ethX name */
SET_MODULE_OWNER(dev);
/* Overrides */
ndev->init = airport_init;
ndev->open = airport_open;
ndev->stop = airport_stop;
dev->open = airport_open;
dev->stop = airport_stop;
/* Setup interrupts & base address */
ndev->irq = of_node->intrs[0].line;
ndev->base_addr = (unsigned long)ioremap(of_node->addrs[0].address, 0x1000) - _IO_BASE;
dev->irq = of_node->intrs[0].line;
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 */
feature_set_airport_power(card->node, 1);
pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE, card->node, 0, 1);
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ);
/* Reset it before we get the interrupt */
hermes_reset(hw);
if (request_irq(ndev->irq, dldwd_interrupt, 0, "Airport", (void *)priv)) {
printk(KERN_ERR "airport: Couldn't get IRQ %d\n", ndev->irq);
if (request_irq(dev->irq, orinoco_interrupt, 0, "Airport", (void *)priv)) {
printk(KERN_ERR "airport: Couldn't get IRQ %d\n", dev->irq);
goto failed;
}
card->irq_requested = 1;
/* register_netdev will give us an ethX name */
ndev->name[0] = '\0';
/* Tell the stack we exist */
if (register_netdev(ndev) != 0) {
if (register_netdev(dev) != 0) {
printk(KERN_ERR "airport: register_netdev() failed\n");
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;
SET_MODULE_OWNER(ndev);
/* 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",
ndev->name);
dev->name);
#ifdef CONFIG_PMAC_PBOOK
pmu_register_sleep_notifier(&airport_sleep_notifier);
#endif
return priv;
return dev;
failed:
airport_detach(priv);
airport_detach(dev);
return NULL;
} /* airport_attach */
......@@ -279,32 +268,34 @@ airport_attach(struct device_node* of_node)
======================================================================*/
static void
airport_detach(dldwd_priv_t *priv)
airport_detach(struct net_device *dev)
{
dldwd_card_t* card = (dldwd_card_t *)priv->card;
priv->hw_ready = 0;
struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
struct airport *card = (struct airport *)priv->card;
/* Unregister proc entry */
dldwd_proc_dev_cleanup(priv);
orinoco_proc_dev_cleanup(priv);
#ifdef CONFIG_PMAC_PBOOK
pmu_unregister_sleep_notifier(&airport_sleep_notifier);
#endif
if (card->ndev_registered)
unregister_netdev(&priv->ndev);
unregister_netdev(dev);
card->ndev_registered = 0;
if (card->irq_requested)
free_irq(priv->ndev.irq, priv);
free_irq(dev->irq, priv);
card->irq_requested = 0;
// FIXME
// if (ndev->base_addr)
// iounmap(ndev->base_addr + _IO_BASE);
// ndev->base_addr = 0;
if (card->vaddr)
iounmap(card->vaddr);
card->vaddr = 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;
schedule_timeout(HZ);
......
......@@ -53,17 +53,18 @@
#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_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
#ifdef MODULE_LICENSE
MODULE_LICENSE("Dual MPL/GPL");
#endif
/* These are maximum timeouts. Most often, card wil react much faster */
#define CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */
#define CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */
#define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */
#define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */
#define BAP_BUSY_TIMEOUT (500) /* In iterations of ~1us */
/*
* Debugging helpers
......@@ -75,9 +76,9 @@ MODULE_LICENSE("Dual MPL/GPL");
#include <stdarg.h>
#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 */
......@@ -85,6 +86,8 @@ MODULE_LICENSE("Dual MPL/GPL");
#endif /* ! HERMES_DEBUG */
#define IO_TYPE(hw) ((hw)->io_space ? "IO " : "MEM ")
/*
* Internal functions
*/
......@@ -98,10 +101,17 @@ MODULE_LICENSE("Dual MPL/GPL");
*/
static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0)
{
int k = CMD_BUSY_TIMEOUT;
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);
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) {
return -EBUSY;
}
......@@ -118,10 +128,19 @@ static int hermes_issue_cmd(hermes_t *hw, u16 cmd, u16 param0)
* 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;
#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)
......@@ -188,8 +207,9 @@ int hermes_reset(hermes_t *hw)
}
if (! (reg & HERMES_EV_CMD)) {
printk(KERN_ERR "hermes @ 0x%x: Timeout waiting for card to reset (reg=0x%04x)!\n",
hw->iobase, reg);
printk(KERN_ERR "hermes @ %s0x%lx: "
"Timeout waiting for card to reset (reg=0x%04x)!\n",
IO_TYPE(hw), hw->iobase, reg);
err = -ETIMEDOUT;
goto out;
}
......@@ -198,7 +218,8 @@ int hermes_reset(hermes_t *hw)
hermes_write_regn(hw, EVACK, HERMES_EV_CMD);
err = status & HERMES_STATUS_RESULT;
if (status & HERMES_STATUS_RESULT)
err = -EIO;
out:
return err;
......@@ -215,16 +236,18 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp)
int err;
int k;
u16 reg;
u16 status;
err = hermes_issue_cmd(hw, cmd, parm0);
if (err) {
if (! hermes_present(hw)) {
printk(KERN_WARNING "hermes @ 0x%x: Card removed while issuing command.\n",
hw->iobase);
printk(KERN_WARNING "hermes @ %s0x%lx: "
"Card removed while issuing command.\n",
IO_TYPE(hw), hw->iobase);
err = -ENODEV;
} else
printk(KERN_ERR "hermes @ 0x%x: CMD register busy in hermes_issue_command().\n",
hw->iobase);
printk(KERN_ERR "hermes @ %s0x%lx: Error %d issuing command.\n",
IO_TYPE(hw), hw->iobase, err);
goto out;
}
......@@ -237,27 +260,33 @@ int hermes_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp)
}
if (! hermes_present(hw)) {
printk(KERN_WARNING "hermes @ 0x%x: Card removed while waiting for command completion.\n",
hw->iobase);
printk(KERN_WARNING "hermes @ %s0x%lx: "
"Card removed while waiting for command completion.\n",
IO_TYPE(hw), hw->iobase);
err = -ENODEV;
goto out;
}
if (! (reg & HERMES_EV_CMD)) {
printk(KERN_ERR "hermes @ 0x%x: Timeout waiting for command completion.\n",
hw->iobase);
printk(KERN_ERR "hermes @ %s0x%lx: "
"Timeout waiting for command completion.\n",
IO_TYPE(hw), hw->iobase);
err = -ETIMEDOUT;
goto out;
}
resp->status = hermes_read_regn(hw, STATUS);
status = hermes_read_regn(hw, STATUS);
if (resp) {
resp->status = status;
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);
err = resp->status & HERMES_STATUS_RESULT;
if (status & HERMES_STATUS_RESULT)
err = -EIO;
out:
return err;
......@@ -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 err = 0;
hermes_response_t resp;
int k;
u16 reg;
if ( (size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX) )
return -EINVAL;
err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, &resp);
err = hermes_docmd_wait(hw, HERMES_CMD_ALLOC, size, NULL);
if (err) {
printk(KERN_WARNING "hermes @ 0x%x: Frame allocation command failed (0x%X).\n",
hw->iobase, err);
printk(KERN_WARNING "hermes @ %s0x%lx: "
"Frame allocation command failed (0x%X).\n",
IO_TYPE(hw), hw->iobase, err);
return err;
}
......@@ -289,14 +318,16 @@ int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)
}
if (! hermes_present(hw)) {
printk(KERN_WARNING "hermes @ 0x%x: Card removed waiting for frame allocation.\n",
hw->iobase);
printk(KERN_WARNING "hermes @ %s0x%lx: "
"Card removed waiting for frame allocation.\n",
IO_TYPE(hw), hw->iobase);
return -ENODEV;
}
if (! (reg & HERMES_EV_ALLOC)) {
printk(KERN_ERR "hermes @ 0x%x: Timeout waiting for frame allocation\n",
hw->iobase);
printk(KERN_ERR "hermes @ %s0x%lx: "
"Timeout waiting for frame allocation\n",
IO_TYPE(hw), hw->iobase);
return -ETIMEDOUT;
}
......@@ -306,13 +337,14 @@ int hermes_allocate(hermes_t *hw, u16 size, u16 *fid)
return 0;
}
/* 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
* from firmware
*
* 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 oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0;
......@@ -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) )
return -EINVAL;
k = BAP_BUSY_TIMEOUT;
k = HERMES_BAP_BUSY_TIMEOUT;
reg = hermes_read_reg(hw, oreg);
while ((reg & HERMES_OFFSET_BUSY) & k) {
while ((reg & HERMES_OFFSET_BUSY) && k) {
k--;
udelay(1);
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)
return -ETIMEDOUT;
......@@ -339,7 +384,7 @@ int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset)
hermes_write_reg(hw, oreg, offset);
/* Wait for the BAP to be ready */
k = BAP_BUSY_TIMEOUT;
k = HERMES_BAP_BUSY_TIMEOUT;
reg = hermes_read_reg(hw, oreg);
while ( (reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) {
k--;
......@@ -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 err = 0;
if (len % 2)
if ( (len < 0) || (len % 2) )
return -EINVAL;
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,
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
int err = 0;
if (len % 2)
if ( (len < 0) || (len % 2) )
return -EINVAL;
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,
int err = 0;
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
u16 rlength, rtype;
hermes_response_t resp;
if (bufsize % 2)
if ( (bufsize < 0) || (bufsize % 2) )
return -EINVAL;
err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, &resp);
err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL);
if (err)
goto out;
......@@ -447,11 +491,14 @@ int hermes_read_ltv(hermes_t *hw, int bap, u16 rid, int bufsize,
*length = rlength;
if (rtype != rid)
printk(KERN_WARNING "hermes_read_ltv(): rid (0x%04x) does "
"not match type (0x%04x)\n", rid, rtype);
printk(KERN_WARNING "hermes @ %s0x%lx: "
"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)
printk(KERN_WARNING "hermes @ 0x%x: Truncating LTV record from %d to %d bytes. "
"(rid=0x%04x, len=0x%04x)\n", hw->iobase,
printk(KERN_WARNING "hermes @ %s0x%lx: "
"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);
/* 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,
{
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
int err = 0;
hermes_response_t resp;
int count;
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,
hermes_write_words(hw, dreg, value, count);
err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE,
rid, &resp);
rid, NULL);
out:
return err;
......
......@@ -48,7 +48,6 @@
#define HERMES_PDA_LEN_MAX (1024) /* in bytes, from EK */
#define HERMES_SCANRESULT_MAX (35)
#define HERMES_CHINFORESULT_MAX (8)
#define HERMES_FRAME_LEN_MAX (2304)
#define HERMES_MAX_MULTICAST (16)
#define HERMES_MAGIC (0x7d1f)
......@@ -138,7 +137,7 @@
/*--- Regulate Commands --------------------------*/
#define HERMES_CMD_NOTIFY (0x0010)
#define HERMES_CMD_INQ (0x0011)
#define HERMES_CMD_INQUIRE (0x0011)
/*--- Configure Commands --------------------------*/
#define HERMES_CMD_ACCESS (0x0021)
......@@ -149,127 +148,161 @@
#define HERMES_MONITOR_ENABLE (0x000b)
#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
*/
typedef struct hermes_frame_desc {
/* Hermes - i.e. little-endian byte-order */
#define HERMES_DESCRIPTOR_OFFSET 0
#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;
u16 res1, res2;
u16 q_info;
u16 res3, res4;
u16 tx_ctl;
} __attribute__ ((packed)) hermes_frame_desc_t;
u32 time;
u8 silence;
u8 signal;
u8 rate;
u8 rxflow;
u32 reserved;
} __attribute__ ((packed));
#define HERMES_RXSTAT_ERR (0x0003)
#define HERMES_RXSTAT_MACPORT (0x0700)
#define HERMES_RXSTAT_MSGTYPE (0xE000)
#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 */
/* RFC-1042 encoded frame */
#define HERMES_RXSTAT_1042 (0x2000)
/* Bridge-tunnel encoded frame */
#define HERMES_RXSTAT_TUNNEL (0x4000)
/* Wavelan-II Management Protocol frame */
#define HERMES_RXSTAT_WMP (0x6000)
struct hermes_tx_descriptor {
u16 status;
u16 reserved1;
u16 reserved2;
u32 sw_support;
u8 retry_count;
u8 tx_rate;
u16 tx_control;
} __attribute__ ((packed));
#define HERMES_TXSTAT_RETRYERR (0x0001)
#define HERMES_TXSTAT_AGEDERR (0x0002)
#define HERMES_TXSTAT_DISCON (0x0004)
#define HERMES_TXSTAT_FORMERR (0x0008)
#define HERMES_TXCTRL_TX_OK (0x0002) /* ?? interrupt on Tx complete */
#define HERMES_TXCTRL_TX_EX (0x0004) /* ?? interrupt on Tx exception */
#define HERMES_TXCTRL_802_11 (0x0008) /* We supply 802.11 header */
#define HERMES_TXCTRL_ALT_RTRY (0x0020)
/* 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__
/* Timeouts */
#define HERMES_BAP_BUSY_TIMEOUT (500) /* In iterations of ~1us */
/* Basic control structure */
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? */
#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;
typedef struct hermes_response {
u16 status, resp0, resp1, resp2;
} 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 */
#define hermes_read_reg(hw, off) (inw((hw)->iobase + (off)))
#define hermes_write_reg(hw, off, val) (outw_p((val), (hw)->iobase + (off)))
#define hermes_read_reg(hw, off) ((hw)->io_space ? \
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_write_regn(hw, name, val) (hermes_write_reg((hw), HERMES_##name, (val)))
/* 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_docmd_wait(hermes_t *hw, u16 cmd, u16 parm0, hermes_response_t *resp);
int hermes_allocate(hermes_t *hw, u16 size, u16 *fid);
int hermes_bap_seek(hermes_t *hw, int bap, u16 id, u16 offset);
int hermes_bap_pread(hermes_t *hw, int bap, void *buf, int len,
u16 id, u16 offset);
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)
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),
0, &resp);
0, NULL);
}
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),
0, &resp);
/* Initiate an INQUIRE command (tallies or scan). The result will come as an
* 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_RECLEN_TO_BYTES(n) ( ((n)-1) * 2 )
/* 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)))
#define hermes_write_words(hw, off, buf, count) (outsw((hw)->iobase + (off), (buf), (count)))
static inline void hermes_read_words(struct hermes *hw, int off, void *buf, int 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) \
(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 @@
#define _ORINOCO_H
/* To enable debug messages */
/* #define ORINOCO_DEBUG 3 */
//#define ORINOCO_DEBUG 3
#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) */
#define WIRELESS_SPY // enable iwspy support
#define ORINOCO_MAX_KEY_SIZE 14
#define ORINOCO_MAX_KEYS 4
#define DLDWD_MIN_MTU 256
#define DLDWD_MAX_MTU (HERMES_FRAME_LEN_MAX - ENCAPS_OVERHEAD)
typedef struct orinoco_key {
u16 len; /* always store little-endian */
char data[ORINOCO_MAX_KEY_SIZE];
} __attribute__ ((packed)) orinoco_key_t;
#define LTV_BUF_SIZE 128
#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];
typedef orinoco_key_t orinoco_keys_t[ORINOCO_MAX_KEYS];
/*====================================================================*/
typedef struct dldwd_priv {
void* card; /* Pointer to card dependant structure */
struct orinoco_private {
void *card; /* Pointer to card dependant structure */
/* 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;
long state;
#define DLDWD_STATE_INIRQ 0
#define DLDWD_STATE_DOIRQ 1
int hw_ready; /* HW may be suspended by platform */
#define ORINOCO_STATE_INIRQ 0
#define ORINOCO_STATE_DOIRQ 1
/* Net device stuff */
struct net_device ndev;
struct net_device *ndev;
struct net_device_stats stats;
struct iw_statistics wstats;
/* Hardware control variables */
hermes_t hw;
uint16_t txfid;
u16 txfid;
/* Capabilities of the hardware/firmware */
int firmware_type;
#define FIRMWARE_TYPE_LUCENT 1
#define FIRMWARE_TYPE_AGERE 1
#define FIRMWARE_TYPE_INTERSIL 2
#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_mwo;
int has_pm;
int has_preamble;
int need_card_reset, broken_reset, broken_allocate;
uint16_t channel_mask;
/* Current configuration */
uint32_t iw_mode;
int port_type, allow_ibss;
uint16_t wep_on, wep_restrict, tx_key;
dldwd_keys_t keys;
int has_sensitivity;
int nicbuf_size;
int broken_cor_reset;
u16 channel_mask;
/* Configuration paramaters */
u32 iw_mode;
int prefer_port3;
u16 wep_on, wep_restrict, tx_key;
orinoco_keys_t keys;
int bitratemode;
char nick[IW_ESSID_MAX_SIZE+1];
char desired_essid[IW_ESSID_MAX_SIZE+1];
uint16_t frag_thresh, mwo_robust;
uint16_t channel;
uint16_t ap_density, rts_thresh;
uint16_t tx_rate_ctrl;
uint16_t pm_on, pm_mcast, pm_period, pm_timeout;
uint16_t preamble;
int promiscuous, allmulti, mc_count;
u16 frag_thresh, mwo_robust;
u16 channel;
u16 ap_density, rts_thresh;
u16 pm_on, pm_mcast, pm_period, pm_timeout;
u16 preamble;
#ifdef WIRELESS_SPY
int spy_number;
u_char spy_address[IW_MAX_SPY][ETH_ALEN];
struct iw_quality spy_stat[IW_MAX_SPY];
#endif
/* Configuration dependent variables */
int port_type, allow_ibss;
int promiscuous, mc_count;
/* /proc based debugging stuff */
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
extern int dldwd_debug;
#define DEBUG(n, args...) do { if (dldwd_debug>(n)) printk(KERN_DEBUG args); } while(0)
#define DEBUGMORE(n, args...) do { if (dldwd_debug>(n)) printk(args); } while (0)
extern int orinoco_debug;
#define DEBUG(n, args...) do { if (orinoco_debug>(n)) printk(KERN_DEBUG args); } while(0)
#define DEBUGMORE(n, args...) do { if (orinoco_debug>(n)) printk(args); } while (0)
#else
#define DEBUG(n, args...) do { } while (0)
#define DEBUGMORE(n, args...) do { } while (0)
......@@ -122,21 +107,12 @@ extern int dldwd_debug;
#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 */
extern void dldwd_shutdown(dldwd_priv_t *dev);
extern int dldwd_reset(dldwd_priv_t *dev);
extern int dldwd_setup(dldwd_priv_t* priv);
extern int dldwd_proc_dev_init(dldwd_priv_t *dev);
extern void dldwd_proc_dev_cleanup(dldwd_priv_t *priv);
extern void dldwd_interrupt(int irq, void * dev_id, struct pt_regs *regs);
#endif
struct net_device *alloc_orinocodev(int sizeof_card);
extern void orinoco_shutdown(struct orinoco_private *dev);
extern int orinoco_reset(struct orinoco_private *dev);
extern int orinoco_proc_dev_init(struct orinoco_private *dev);
extern void orinoco_proc_dev_cleanup(struct orinoco_private *priv);
extern void orinoco_interrupt(int irq, void * dev_id, struct pt_regs *regs);
#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
* as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
......@@ -11,6 +11,9 @@
*/
#include <linux/config.h>
#ifdef __IN_PCMCIA_PACKAGE__
#include <pcmcia/k_compat.h>
#endif /* __IN_PCMCIA_PACKAGE__ */
#include <linux/module.h>
#include <linux/kernel.h>
......@@ -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_DESCRIPTION("Driver for PCMCIA Lucent Orinoco, Prism II based and similar wireless cards");
#ifdef MODULE_LICENSE
MODULE_LICENSE("Dual MPL/GPL");
#endif
/* Parameters that can be set with 'insmod' */
......@@ -58,7 +63,7 @@ static uint irq_mask = 0xdeb8;
/* Newer, simpler way of listing specific interrupts */
static int irq_list[4] = { -1 };
/* Do a Pcmcia soft reset (may help some cards) */
static int reset_cor = 0;
static int reset_cor = -1;
/* Some D-Link cards have buggy CIS. They do work at 5v properly, but
* don't have any CIS entry for it. This workaround it... */
static int ignore_cis_vcc; /* = 0 */
......@@ -68,40 +73,34 @@ MODULE_PARM(irq_list, "1-4i");
MODULE_PARM(reset_cor, "i");
MODULE_PARM(ignore_cis_vcc, "i");
/* Pcmcia specific structure */
typedef struct dldwd_card {
struct orinoco_pccard {
dev_link_t link;
dev_node_t node;
int instance;
/* Common structure (fully included), see orinoco.h */
struct dldwd_priv priv;
} dldwd_card_t;
};
/*
* Function prototypes
*/
/* struct net_device methods */
static int dldwd_cs_open(struct net_device *dev);
static int dldwd_cs_stop(struct net_device *dev);
static int orinoco_cs_open(struct net_device *dev);
static int orinoco_cs_stop(struct net_device *dev);
/* PCMCIA gumpf */
static void dldwd_cs_config(dev_link_t * link);
static void dldwd_cs_release(u_long arg);
static int dldwd_cs_event(event_t event, int priority,
static void orinoco_cs_config(dev_link_t * link);
static void orinoco_cs_release(u_long arg);
static int orinoco_cs_event(event_t event, int priority,
event_callback_args_t * args);
static dev_link_t *dldwd_cs_attach(void);
static void dldwd_cs_detach(dev_link_t *);
static dev_link_t *orinoco_cs_attach(void);
static void orinoco_cs_detach(dev_link_t *);
/*
The dev_info variable is the "key" that is used to match up this
device driver with appropriate cards, through the card configuration
database.
*/
static dev_info_t dev_info = "orinoco_cs";
/*
......@@ -115,7 +114,6 @@ static dev_info_t dev_info = "orinoco_cs";
*/
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)
}
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;
dldwd_card_t* card = (dldwd_card_t *)priv->card;
struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card;
dev_link_t *link = &card->link;
int err;
TRACE_ENTER(priv->ndev.name);
TRACE_ENTER(dev->name);
link->open++;
netif_device_attach(dev);
err = dldwd_reset(priv);
err = orinoco_reset(priv);
if (err)
dldwd_cs_stop(dev);
orinoco_cs_stop(dev);
else
netif_start_queue(dev);
TRACE_EXIT(priv->ndev.name);
TRACE_EXIT(dev->name);
return err;
}
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;
dldwd_card_t* card = (dldwd_card_t *)priv->card;
struct orinoco_private *priv = (struct orinoco_private *)dev->priv;
struct orinoco_pccard* card = (struct orinoco_pccard *)priv->card;
dev_link_t *link = &card->link;
TRACE_ENTER(priv->ndev.name);
TRACE_ENTER(dev->name);
netif_stop_queue(dev);
dldwd_shutdown(priv);
orinoco_shutdown(priv);
link->open--;
if (link->state & DEV_STALE_CONFIG)
mod_timer(&link->release, jiffies + HZ/20);
TRACE_EXIT(priv->ndev.name);
TRACE_EXIT(dev->name);
return 0;
}
......@@ -179,18 +177,18 @@ dldwd_cs_stop(struct net_device *dev)
* In fact, this seem necessary for Spectrum cards...
*/
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;
conf_reg_t reg;
u_int default_cor;
TRACE_ENTER(priv->ndev.name);
TRACE_ENTER(priv->ndev->name);
/* Doing it if hardware is gone is guaranteed crash */
if(!priv->hw_ready)
return(0);
if(! (link->state & DEV_CONFIG) )
return -ENODEV;
/* Save original COR value */
reg.Function = 0;
......@@ -200,7 +198,7 @@ dldwd_cs_cor_reset(dldwd_priv_t *priv)
CardServices(AccessConfigurationRegister, link->handle, &reg);
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 */
reg.Action = CS_WRITE;
......@@ -209,18 +207,57 @@ dldwd_cs_cor_reset(dldwd_priv_t *priv)
CardServices(AccessConfigurationRegister, link->handle, &reg);
/* Wait until the card has acknowledged our reset */
/* FIXME: mdelay() is deprecated -dgibson */
mdelay(1);
#if 0 /* This seems to help on Symbol cards, but we're not sure why,
and we don't know what it will do to other cards */
reg.Action = CS_READ;
reg.Offset = CISREG_CCSR;
CardServices(AccessConfigurationRegister, link->handle, &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 */
reg.Action = CS_WRITE;
reg.Offset = CISREG_COR;
reg.Value = (default_cor & ~COR_SOFT_RESET);
CardServices(AccessConfigurationRegister, link->handle, &reg);
/* Wait until the card has finished restarting */
/* FIXME: mdelay() is deprecated -dgibson */
mdelay(1);
TRACE_EXIT(priv->ndev.name);
TRACE_EXIT(priv->ndev->name);
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;
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) */
......@@ -228,17 +265,17 @@ static void
flush_stale_links(void)
{
dev_link_t *link, *next;
TRACE_ENTER("dldwd");
TRACE_ENTER("orinoco");
for (link = dev_list; link; link = next) {
next = link->next;
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
with Card Services.
......@@ -248,37 +285,35 @@ flush_stale_links(void)
======================================================================*/
static dev_link_t *
dldwd_cs_attach(void)
orinoco_cs_attach(void)
{
dldwd_card_t *card;
dldwd_priv_t *priv;
struct net_device *dev;
struct orinoco_private *priv;
struct orinoco_pccard *card;
dev_link_t *link;
struct net_device *ndev;
client_reg_t client_reg;
int ret, i;
TRACE_ENTER("dldwd");
TRACE_ENTER("orinoco");
/* A bit of cleanup */
flush_stale_links();
/* Allocate space for private device-specific data */
card = kmalloc(sizeof(*card), GFP_KERNEL);
if (! card) {
link = NULL;
goto out;
}
memset(card, 0, sizeof(*card));
dev = alloc_orinocodev(sizeof(*card));
if (! dev)
return NULL;
priv = dev->priv;
card = priv->card;
/* Overrides */
dev->open = orinoco_cs_open;
dev->stop = orinoco_cs_stop;
priv->hard_reset = orinoco_cs_hard_reset;
/* Link both structure together */
priv = &(card->priv);
priv->card = card;
card->instance = num_instances++; /* FIXME: Racy? */
/* Link both structures together */
link = &card->link;
ndev = &priv->ndev;
link->priv = priv;
/* Initialize the dev_link_t structure */
link->release.function = &dldwd_cs_release;
link->release.function = &orinoco_cs_release;
link->release.data = (u_long) link;
/* Interrupt setup */
......@@ -301,17 +336,6 @@ dldwd_cs_attach(void)
link->conf.Attributes = 0;
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 */
link->next = dev_list;
dev_list = link;
......@@ -321,21 +345,21 @@ dldwd_cs_attach(void)
CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
client_reg.event_handler = &dldwd_cs_event;
client_reg.event_handler = &orinoco_cs_event;
client_reg.Version = 0x0210;
client_reg.event_callback_args.client_data = link;
ret = CardServices(RegisterClient, &link->handle, &client_reg);
if (ret != CS_SUCCESS) {
cs_error(link->handle, RegisterClient, ret);
dldwd_cs_detach(link);
orinoco_cs_detach(link);
link = NULL;
goto out;
}
out:
TRACE_EXIT("dldwd");
TRACE_EXIT("orinoco");
return link;
} /* dldwd_cs_attach */
} /* orinoco_cs_attach */
/*======================================================================
This deletes a driver "instance". The device is de-registered
......@@ -345,12 +369,12 @@ dldwd_cs_attach(void)
======================================================================*/
static void
dldwd_cs_detach(dev_link_t * link)
orinoco_cs_detach(dev_link_t * link)
{
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 */
for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
......@@ -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);
if (link->dev) {
DEBUG(0, "orinoco_cs: About to unregister net device %p\n",
&priv->ndev);
unregister_netdev(&priv->ndev);
priv->ndev);
unregister_netdev(priv->ndev);
}
kfree(priv->card);
num_instances--; /* FIXME: Racy? */
out:
TRACE_EXIT("dldwd");
} /* dldwd_cs_detach */
TRACE_EXIT("orinoco");
} /* 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
device available to the system.
======================================================================*/
......@@ -407,13 +429,13 @@ while ((last_ret=CardServices(last_fn=(fn),args))!=0) goto cs_failed
if (CardServices(fn, args) != 0) goto next_entry
static void
dldwd_cs_config(dev_link_t * link)
orinoco_cs_config(dev_link_t * link)
{
client_handle_t handle = link->handle;
dldwd_priv_t *priv = link->priv;
dldwd_card_t *card = (dldwd_card_t *)priv->card;
struct orinoco_private *priv = link->priv;
struct orinoco_pccard *card = (struct orinoco_pccard *)priv->card;
hermes_t *hw = &priv->hw;
struct net_device *ndev = &priv->ndev;
struct net_device *ndev = priv->ndev;
tuple_t tuple;
cisparse_t parse;
int last_fn, last_ret;
......@@ -422,7 +444,7 @@ dldwd_cs_config(dev_link_t * link)
cistpl_cftable_entry_t dflt = { 0 };
cisinfo_t info;
TRACE_ENTER("dldwd");
TRACE_ENTER("orinoco");
CS_CHECK(ValidateCIS, handle, &info);
......@@ -448,7 +470,7 @@ dldwd_cs_config(dev_link_t * link)
CS_CHECK(GetConfigurationInfo, handle, &conf);
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);
/*
......@@ -470,7 +492,7 @@ dldwd_cs_config(dev_link_t * link)
CFG_CHECK(GetTupleData, handle, &tuple);
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);
if (cfg->flags & CISTPL_CFTABLE_DEFAULT)
......@@ -488,16 +510,14 @@ dldwd_cs_config(dev_link_t * link)
/* Use power settings for Vcc and Vpp if present */
/* Note that the CIS values need to be rescaled */
if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) {
if (conf.Vcc !=
cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
DEBUG(2, "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 (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / 10000) {
DEBUG(2, "orinoco_cs_config: Vcc mismatch (conf.Vcc = %d, CIS = %d)\n", conf.Vcc, cfg->vcc.param[CISTPL_POWER_VNOM] / 10000);
if (!ignore_cis_vcc)
goto next_entry;
}
} else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) {
if (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 (conf.Vcc != 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);
if(!ignore_cis_vcc)
goto next_entry;
}
......@@ -510,7 +530,7 @@ dldwd_cs_config(dev_link_t * link)
link->conf.Vpp1 = link->conf.Vpp2 =
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? */
if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
......@@ -551,7 +571,12 @@ dldwd_cs_config(dev_link_t * link)
next_entry:
if (link->io.NumPorts1)
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)
for (i=0; i<4; i++)
link->irq.IRQInfo2 |= 1 << irq_list[i];
link->irq.Handler = dldwd_interrupt;
link->irq.Handler = orinoco_interrupt;
link->irq.Instance = priv;
CS_CHECK(RequestIRQ, link->handle, &link->irq);
......@@ -579,7 +604,8 @@ dldwd_cs_config(dev_link_t * link)
/* We initialize the hermes structure before completing PCMCIA
configuration just in case the interrupt handler gets
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
......@@ -618,7 +644,7 @@ dldwd_cs_config(dev_link_t * link)
printk("\n");
/* 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",
ndev->name);
goto failed;
......@@ -627,12 +653,9 @@ dldwd_cs_config(dev_link_t * link)
/* Note to myself : this replace MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT */
SET_MODULE_OWNER(ndev);
/* Allow cor_reset, /proc & ioctls to act */
priv->hw_ready = 1;
/* Do a Pcmcia soft reset of the card (optional) */
if(reset_cor)
dldwd_cs_cor_reset(priv);
/* Let reset_cor parameter override determine_firmware()'s guess */
if (reset_cor != -1)
priv->broken_cor_reset = ! reset_cor;
/*
At this point, the dev_node_t structure(s) need to be
......@@ -642,29 +665,29 @@ dldwd_cs_config(dev_link_t * link)
link->dev = &card->node;
link->state &= ~DEV_CONFIG_PENDING;
TRACE_EXIT("dldwd");
TRACE_EXIT("orinoco");
return;
cs_failed:
cs_error(link->handle, last_fn, last_ret);
failed:
dldwd_cs_release((u_long) link);
orinoco_cs_release((u_long) link);
TRACE_EXIT("dldwd");
} /* dldwd_cs_config */
TRACE_EXIT("orinoco");
} /* 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
still open, this will be postponed until it is closed.
======================================================================*/
static void
dldwd_cs_release(u_long arg)
orinoco_cs_release(u_long 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);
......@@ -681,7 +704,7 @@ dldwd_cs_release(u_long arg)
}
/* Unregister proc entry */
dldwd_proc_dev_cleanup(priv);
orinoco_proc_dev_cleanup(priv);
/* Don't bother checking to see if these succeed or not */
CardServices(ReleaseConfiguration, link->handle);
......@@ -692,7 +715,7 @@ dldwd_cs_release(u_long arg)
link->state &= ~DEV_CONFIG;
TRACE_EXIT(link->dev->dev_name);
} /* dldwd_cs_release */
} /* orinoco_cs_release */
/*======================================================================
The card status event handler. Mostly, this schedules other
......@@ -705,39 +728,37 @@ dldwd_cs_release(u_long arg)
======================================================================*/
static int
dldwd_cs_event(event_t event, int priority,
orinoco_cs_event(event_t event, int priority,
event_callback_args_t * args)
{
dev_link_t *link = args->client_data;
dldwd_priv_t *priv = (dldwd_priv_t *)link->priv;
struct net_device *dev = &priv->ndev;
struct orinoco_private *priv = (struct orinoco_private *)link->priv;
struct net_device *dev = priv->ndev;
TRACE_ENTER("dldwd");
TRACE_ENTER("orinoco");
switch (event) {
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;
if (link->state & DEV_CONFIG) {
netif_stop_queue(dev);
}
orinoco_shutdown(priv);
if (link->state & DEV_CONFIG) {
netif_device_detach(dev);
mod_timer(&link->release, jiffies + HZ / 20);
}
break;
case CS_EVENT_CARD_INSERTION:
link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
dldwd_cs_config(link);
orinoco_cs_config(link);
break;
case CS_EVENT_PM_SUSPEND:
link->state |= DEV_SUSPEND;
/* Fall through... */
case CS_EVENT_RESET_PHYSICAL:
dldwd_shutdown(priv);
orinoco_shutdown(priv);
/* Mark the device as stopped, to block IO until later */
if (link->state & DEV_CONFIG) {
......@@ -757,13 +778,13 @@ dldwd_cs_event(event_t event, int priority,
&link->conf);
if (link->open) {
if (dldwd_reset(priv) == 0) {
if (orinoco_reset(priv) == 0) {
netif_device_attach(dev);
netif_start_queue(dev);
} else {
printk(KERN_ERR "%s: Error resetting device on PCMCIA event\n",
dev->name);
dldwd_cs_stop(dev);
orinoco_cs_stop(dev);
}
}
}
......@@ -774,17 +795,17 @@ dldwd_cs_event(event_t event, int priority,
break;
}
TRACE_EXIT("dldwd");
TRACE_EXIT("orinoco");
return 0;
} /* dldwd_cs_event */
} /* orinoco_cs_event */
static int __init
init_dldwd_cs(void)
init_orinoco_cs(void)
{
servinfo_t serv;
TRACE_ENTER("dldwd");
TRACE_ENTER("orinoco");
printk(KERN_DEBUG "%s\n", version);
......@@ -795,17 +816,17 @@ init_dldwd_cs(void)
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;
}
static void __exit
exit_dldwd_cs(void)
exit_orinoco_cs(void)
{
TRACE_ENTER("dldwd");
TRACE_ENTER("orinoco");
unregister_pccard_driver(&dev_info);
......@@ -814,12 +835,12 @@ exit_dldwd_cs(void)
while (dev_list != NULL) {
del_timer(&dev_list->release);
if (dev_list->state & DEV_CONFIG)
dldwd_cs_release((u_long) dev_list);
dldwd_cs_detach(dev_list);
orinoco_cs_release((u_long) dev_list);
orinoco_cs_detach(dev_list);
}
TRACE_EXIT("dldwd");
TRACE_EXIT("orinoco");
}
module_init(init_dldwd_cs);
module_exit(exit_dldwd_cs);
module_init(init_orinoco_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,
* 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>
*
* The contents of this file are subject to the Mozilla Public License
......@@ -34,6 +26,22 @@
* provisions above, a recipient may use your version of this file
* under either the MPL or the GPL.
* Caution: this is experimental and probably buggy. For success and
* failure reports for different cards and adaptors, see
* orinoco_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
beast than the usual PCMCIA-based PRISM2 configuration expected by
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
not have time for a while..
---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>
......@@ -129,36 +129,36 @@ not have time for a while..
#include <linux/wireless.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/ds.h>
#include <pcmcia/bus_ops.h>
#include "hermes.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_DESCRIPTION("Driver for wireless LAN cards using the PLX9052 PCI bridge");
#ifdef MODULE_LICENSE
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_VALUE 0x41 /* Enable PC card with interrupt in level trigger */
#define COR_OFFSET (0x3e0 / 2) /* COR attribute offset of Prism2 PC card */
#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)
{
dldwd_priv_t *priv = (dldwd_priv_t *) dev->priv;
struct orinoco_private *priv = (struct orinoco_private *) dev->priv;
int err;
netif_device_attach(dev);
err = dldwd_reset(priv);
err = orinoco_reset(priv);
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);
else
netif_start_queue(dev);
......@@ -168,110 +168,204 @@ static int orinoco_plx_open(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);
dldwd_shutdown(priv);
orinoco_shutdown(priv);
return 0;
}
static void
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,
const struct pci_device_id *ent)
{
struct net_device *dev;
unsigned long pccard_ioaddr;
int err = 0;
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 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;
/* Resource 2 is mapped to the PCMCIA space */
attr_mem = ioremap(pci_resource_start(pdev, 2), 0x1000);
/* and 3 to the PCMCIA slot I/O address space */
pccard_ioaddr = pci_resource_start(pdev, 3);
attr_mem = ioremap(pci_resource_start(pdev, 2), PAGE_SIZE);
if (! attr_mem)
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 */
if (attr_mem[0] != 0x01 || attr_mem[2] != 0x03 ||
attr_mem[4] != 0x00 || attr_mem[6] != 0x00 ||
attr_mem[8] != 0xFF || attr_mem[10] != 0x17 ||
attr_mem[12] != 0x04 || attr_mem[14] != 0x67) {
/* FIXME: we probably need to be smarted about this */
if (memcmp(attr_mem, cis_magic, sizeof(cis_magic)) != 0) {
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
* enable I/O mode and select level-triggered interrupts */
attr_mem[COR_OFFSET] = COR_VALUE;
mdelay(1);
reg = attr_mem[COR_OFFSET];
/* assert(reg==COR_VALUE); doesn't work */
iounmap(attr_mem); /* done with this now, it seems */
if (!request_region(pccard_ioaddr,
pci_resource_len(pdev, 3), dev_info)) {
if (reg != COR_VALUE) {
printk(KERN_ERR "orinoco_plx: Error setting COR value (reg=%x)\n", reg);
goto fail;
}
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",
pci_resource_len(pdev, 3), pccard_ioaddr);
return -EBUSY;
pccard_iolen, pccard_ioaddr);
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->irq = pdev->irq;
dev = alloc_orinocodev(0);
if (! dev) {
err = -ENOMEM;
goto fail;
}
priv = dev->priv;
dev->base_addr = pccard_ioaddr;
dev->open = orinoco_plx_open;
dev->stop = orinoco_plx_stop;
priv->card_reset_handler = NULL; /* We have no reset handler */
SET_MODULE_OWNER(dev);
printk(KERN_DEBUG
"Detected Orinoco/Prism2 PCI device at %s, mem:0x%lx, irq:%d, io addr:0x%lx\n",
pdev->slot_name, (long) attr_mem, pdev->irq, pccard_ioaddr);
hermes_struct_init(&(priv->hw), dev->base_addr); /* XXX */
dev->name[0] = '\0'; /* name defaults to ethX */
register_netdev(dev);
request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name,
dev);
if (dldwd_proc_dev_init(priv) != 0) {
printk(KERN_ERR "%s: Failed to create /proc node\n", dev->name);
return -EIO;
"Detected Orinoco/Prism2 PLX device at %s irq:%d, io addr:0x%lx\n",
pdev->slot_name, pdev->irq, pccard_ioaddr);
hermes_struct_init(&(priv->hw), dev->base_addr,
HERMES_IO, HERMES_16BIT_REGSPACING);
pci_set_drvdata(pdev, priv);
err = request_irq(pdev->irq, orinoco_plx_interrupt, SA_SHIRQ, dev->name, priv);
if (err) {
printk(KERN_ERR "orinoco_plx: Error allocating IRQ %d.\n", pdev->irq);
err = -EBUSY;
goto fail;
}
dev->irq = pdev->irq;
SET_MODULE_OWNER(dev);
priv->hw_ready = 1;
err = register_netdev(dev);
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 */
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)
{
struct net_device *dev = pci_get_drvdata(pdev);
dldwd_priv_t *priv = dev->priv;
struct orinoco_private *priv = pci_get_drvdata(pdev);
struct net_device *dev = priv->ndev;
if (!dev)
TRACE_ENTER("orinoco_plx");
if (!priv)
BUG();
dldwd_proc_dev_cleanup(priv);
free_irq(dev->irq, dev);
orinoco_proc_dev_cleanup(priv);
unregister_netdev(dev);
release_region(dev->base_addr, 0x40);
kfree(dev->priv);
pci_set_drvdata(pdev, NULL);
if (dev->irq)
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 = {
{0x111a, 0x1023, PCI_ANY_ID, PCI_ANY_ID,}, /* Siemens SpeedStream SS1023 */
{0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,}, /* Netgear MA301 */
#if 0
{0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,}, /* Correga */
#endif
{0x15e8, 0x0130, PCI_ANY_ID, PCI_ANY_ID,}, /* Correga - does this work? */
{0x1638, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* SMC EZConnect SMC2602W,
Eumitcom PCI WL11000,
Addtron AWA-100*/
......@@ -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, 0x1102, PCI_ANY_ID, PCI_ANY_ID,}, /* Linksys WDT11 */
{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,},
};
MODULE_DEVICE_TABLE(pci, orinoco_plx_pci_id_table);
static struct pci_driver orinoco_plx_driver = {
name: "orinoco_plx",
id_table: orinoco_plx_pci_id_table,
probe: orinoco_plx_init_one,
remove: __devexit_p(orinoco_plx_remove_one),
name:"orinoco_plx",
id_table:orinoco_plx_pci_id_table,
probe:orinoco_plx_init_one,
remove:orinoco_plx_remove_one,
suspend:0,
resume:0
};
static int __init orinoco_plx_init(void)
{
printk(KERN_DEBUG "%s\n", version);
return pci_module_init(&orinoco_plx_driver);
}
extern void __exit orinoco_plx_exit(void)
{
pci_unregister_driver(&orinoco_plx_driver);
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ);
}
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