orinoco: move under intersil vendor directory
Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
This commit is contained in:
142
drivers/net/wireless/intersil/orinoco/Kconfig
Normal file
142
drivers/net/wireless/intersil/orinoco/Kconfig
Normal file
@@ -0,0 +1,142 @@
|
||||
config HERMES
|
||||
tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)"
|
||||
depends on (PPC_PMAC || PCI || PCMCIA)
|
||||
depends on CFG80211
|
||||
select CFG80211_WEXT_EXPORT
|
||||
select WIRELESS_EXT
|
||||
select WEXT_SPY
|
||||
select WEXT_PRIV
|
||||
select FW_LOADER
|
||||
select CRYPTO
|
||||
select CRYPTO_MICHAEL_MIC
|
||||
---help---
|
||||
A driver for 802.11b wireless cards based on the "Hermes" or
|
||||
Intersil HFA384x (Prism 2) MAC controller. This includes the vast
|
||||
majority of the PCMCIA 802.11b cards (which are nearly all rebadges)
|
||||
- except for the Cisco/Aironet cards. Cards supported include the
|
||||
Apple Airport (not a PCMCIA card), WavelanIEEE/Orinoco,
|
||||
Cabletron/EnteraSys Roamabout, ELSA AirLancer, MELCO Buffalo, Avaya,
|
||||
IBM High Rate Wireless, Farralon Syyline, Samsung MagicLAN, Netgear
|
||||
MA401, LinkSys WPC-11, D-Link DWL-650, 3Com AirConnect, Intel
|
||||
IPW2011, and Symbol Spectrum24 High Rate amongst others.
|
||||
|
||||
This option includes the guts of the driver, but in order to
|
||||
actually use a card you will also need to enable support for PCMCIA
|
||||
Hermes cards, PLX9052 based PCI adaptors or the Apple Airport below.
|
||||
|
||||
You will also very likely also need the Wireless Tools in order to
|
||||
configure your card and that /etc/pcmcia/wireless.opts works :
|
||||
<http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>
|
||||
|
||||
config HERMES_PRISM
|
||||
bool "Support Prism 2/2.5 chipset"
|
||||
depends on HERMES
|
||||
---help---
|
||||
|
||||
Say Y to enable support for Prism 2 and 2.5 chipsets. These
|
||||
chipsets are better handled by the hostap driver. This driver
|
||||
would not support WPA or firmware download for Prism chipset.
|
||||
|
||||
If you are not sure, say N.
|
||||
|
||||
config HERMES_CACHE_FW_ON_INIT
|
||||
bool "Cache Hermes firmware on driver initialisation"
|
||||
depends on HERMES
|
||||
default y
|
||||
---help---
|
||||
Say Y to cache any firmware required by the Hermes drivers
|
||||
on startup. The firmware will remain cached until the
|
||||
driver is unloaded. The cache uses 64K of RAM.
|
||||
|
||||
Otherwise load the firmware from userspace as required. In
|
||||
this case the driver should be unloaded and restarted
|
||||
whenever the firmware is changed.
|
||||
|
||||
If you are not sure, say Y.
|
||||
|
||||
config APPLE_AIRPORT
|
||||
tristate "Apple Airport support (built-in)"
|
||||
depends on PPC_PMAC && HERMES
|
||||
help
|
||||
Say Y here to support the Airport 802.11b wireless Ethernet hardware
|
||||
built into the Macintosh iBook and other recent PowerPC-based
|
||||
Macintosh machines. This is essentially a Lucent Orinoco card with
|
||||
a non-standard interface.
|
||||
|
||||
This driver does not support the Airport Extreme (802.11b/g). Use
|
||||
the BCM43xx driver for Airport Extreme cards.
|
||||
|
||||
config PLX_HERMES
|
||||
tristate "Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.)"
|
||||
depends on PCI && HERMES
|
||||
help
|
||||
Enable support for PCMCIA cards supported by the "Hermes" (aka
|
||||
orinoco) driver when used in PLX9052 based PCI adaptors. These
|
||||
adaptors are not a full PCMCIA controller but act as a more limited
|
||||
PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that
|
||||
802.11b PCMCIA cards can be used in desktop machines. The Netgear
|
||||
MA301 is such an adaptor.
|
||||
|
||||
config TMD_HERMES
|
||||
tristate "Hermes in TMD7160 based PCI adaptor support"
|
||||
depends on PCI && HERMES
|
||||
help
|
||||
Enable support for PCMCIA cards supported by the "Hermes" (aka
|
||||
orinoco) driver when used in TMD7160 based PCI adaptors. These
|
||||
adaptors are not a full PCMCIA controller but act as a more limited
|
||||
PCI <-> PCMCIA bridge. Several vendors sell such adaptors so that
|
||||
802.11b PCMCIA cards can be used in desktop machines.
|
||||
|
||||
config NORTEL_HERMES
|
||||
tristate "Nortel emobility PCI adaptor support"
|
||||
depends on PCI && HERMES
|
||||
help
|
||||
Enable support for PCMCIA cards supported by the "Hermes" (aka
|
||||
orinoco) driver when used in Nortel emobility PCI adaptors. These
|
||||
adaptors are not full PCMCIA controllers, but act as a more limited
|
||||
PCI <-> PCMCIA bridge.
|
||||
|
||||
config PCI_HERMES
|
||||
tristate "Prism 2.5 PCI 802.11b adaptor support"
|
||||
depends on PCI && HERMES && HERMES_PRISM
|
||||
help
|
||||
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
|
||||
tristate "Hermes PCMCIA card support"
|
||||
depends on PCMCIA && HERMES && HAS_IOPORT_MAP
|
||||
---help---
|
||||
A driver for "Hermes" chipset based PCMCIA wireless adaptors, such
|
||||
as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/
|
||||
EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and
|
||||
others). It should also be usable on various Prism II based cards
|
||||
such as the Linksys, D-Link and Farallon Skyline. It should also
|
||||
work on Symbol cards such as the 3Com AirConnect and Ericsson WLAN.
|
||||
|
||||
You will very likely need the Wireless Tools in order to
|
||||
configure your card and that /etc/pcmcia/wireless.opts works:
|
||||
<http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
|
||||
|
||||
config PCMCIA_SPECTRUM
|
||||
tristate "Symbol Spectrum24 Trilogy PCMCIA card support"
|
||||
depends on PCMCIA && HERMES && HAS_IOPORT_MAP
|
||||
---help---
|
||||
|
||||
This is a driver for 802.11b cards using RAM-loadable Symbol
|
||||
firmware, such as Symbol Wireless Networker LA4100, CompactFlash
|
||||
cards by Socket Communications and Intel PRO/Wireless 2011B.
|
||||
|
||||
This driver requires firmware download on startup. Utilities
|
||||
for downloading Symbol firmware are available at
|
||||
<http://sourceforge.net/projects/orinoco/>
|
||||
|
||||
config ORINOCO_USB
|
||||
tristate "Agere Orinoco USB support"
|
||||
depends on USB && HERMES
|
||||
select FW_LOADER
|
||||
---help---
|
||||
This driver is for USB versions of the Agere Orinoco card.
|
17
drivers/net/wireless/intersil/orinoco/Makefile
Normal file
17
drivers/net/wireless/intersil/orinoco/Makefile
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# Makefile for the orinoco wireless device drivers.
|
||||
#
|
||||
orinoco-objs := main.o fw.o hw.o mic.o scan.o wext.o hermes_dld.o hermes.o cfg.o
|
||||
|
||||
obj-$(CONFIG_HERMES) += orinoco.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_TMD_HERMES) += orinoco_tmd.o
|
||||
obj-$(CONFIG_NORTEL_HERMES) += orinoco_nortel.o
|
||||
obj-$(CONFIG_PCMCIA_SPECTRUM) += spectrum_cs.o
|
||||
obj-$(CONFIG_ORINOCO_USB) += orinoco_usb.o
|
||||
|
||||
# Orinoco should be endian clean.
|
||||
ccflags-y += -D__CHECK_ENDIAN__
|
267
drivers/net/wireless/intersil/orinoco/airport.c
Normal file
267
drivers/net/wireless/intersil/orinoco/airport.c
Normal file
@@ -0,0 +1,267 @@
|
||||
/* airport.c
|
||||
*
|
||||
* A driver for "Hermes" chipset based Apple Airport wireless
|
||||
* card.
|
||||
*
|
||||
* Copyright notice & release notes in file main.c
|
||||
*
|
||||
* Note specific to airport stub:
|
||||
*
|
||||
* 0.05 : first version of the new split driver
|
||||
* 0.06 : fix possible hang on powerup, add sleep support
|
||||
*/
|
||||
|
||||
#define DRIVER_NAME "airport"
|
||||
#define PFX DRIVER_NAME ": "
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/pmac_feature.h>
|
||||
|
||||
#include "orinoco.h"
|
||||
|
||||
#define AIRPORT_IO_LEN (0x1000) /* one page */
|
||||
|
||||
struct airport {
|
||||
struct macio_dev *mdev;
|
||||
void __iomem *vaddr;
|
||||
unsigned int irq;
|
||||
int irq_requested;
|
||||
int ndev_registered;
|
||||
};
|
||||
|
||||
static int
|
||||
airport_suspend(struct macio_dev *mdev, pm_message_t state)
|
||||
{
|
||||
struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev);
|
||||
struct net_device *dev = priv->ndev;
|
||||
struct airport *card = priv->card;
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
printk(KERN_DEBUG "%s: Airport entering sleep mode\n", dev->name);
|
||||
|
||||
err = orinoco_lock(priv, &flags);
|
||||
if (err) {
|
||||
printk(KERN_ERR "%s: hw_unavailable on PBOOK_SLEEP_NOW\n",
|
||||
dev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
orinoco_down(priv);
|
||||
orinoco_unlock(priv, &flags);
|
||||
|
||||
disable_irq(card->irq);
|
||||
pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE,
|
||||
macio_get_of_node(mdev), 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
airport_resume(struct macio_dev *mdev)
|
||||
{
|
||||
struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev);
|
||||
struct net_device *dev = priv->ndev;
|
||||
struct airport *card = priv->card;
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
printk(KERN_DEBUG "%s: Airport waking up\n", dev->name);
|
||||
|
||||
pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE,
|
||||
macio_get_of_node(mdev), 0, 1);
|
||||
msleep(200);
|
||||
|
||||
enable_irq(card->irq);
|
||||
|
||||
priv->hw.ops->lock_irqsave(&priv->lock, &flags);
|
||||
err = orinoco_up(priv);
|
||||
priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
airport_detach(struct macio_dev *mdev)
|
||||
{
|
||||
struct orinoco_private *priv = dev_get_drvdata(&mdev->ofdev.dev);
|
||||
struct airport *card = priv->card;
|
||||
|
||||
if (card->ndev_registered)
|
||||
orinoco_if_del(priv);
|
||||
card->ndev_registered = 0;
|
||||
|
||||
if (card->irq_requested)
|
||||
free_irq(card->irq, priv);
|
||||
card->irq_requested = 0;
|
||||
|
||||
if (card->vaddr)
|
||||
iounmap(card->vaddr);
|
||||
card->vaddr = NULL;
|
||||
|
||||
macio_release_resource(mdev, 0);
|
||||
|
||||
pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE,
|
||||
macio_get_of_node(mdev), 0, 0);
|
||||
ssleep(1);
|
||||
|
||||
macio_set_drvdata(mdev, NULL);
|
||||
free_orinocodev(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int airport_hard_reset(struct orinoco_private *priv)
|
||||
{
|
||||
/* It would be nice to power cycle the Airport for a real hard
|
||||
* reset, but for some reason although it appears to
|
||||
* re-initialize properly, it falls in a screaming heap
|
||||
* shortly afterwards. */
|
||||
#if 0
|
||||
struct airport *card = priv->card;
|
||||
|
||||
/* Vitally important. If we don't do this it seems we get an
|
||||
* interrupt somewhere during the power cycle, since
|
||||
* hw_unavailable is already set it doesn't get ACKed, we get
|
||||
* into an interrupt loop and the PMU decides to turn us
|
||||
* off. */
|
||||
disable_irq(card->irq);
|
||||
|
||||
pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE,
|
||||
macio_get_of_node(card->mdev), 0, 0);
|
||||
ssleep(1);
|
||||
pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE,
|
||||
macio_get_of_node(card->mdev), 0, 1);
|
||||
ssleep(1);
|
||||
|
||||
enable_irq(card->irq);
|
||||
ssleep(1);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
airport_attach(struct macio_dev *mdev, const struct of_device_id *match)
|
||||
{
|
||||
struct orinoco_private *priv;
|
||||
struct airport *card;
|
||||
unsigned long phys_addr;
|
||||
struct hermes *hw;
|
||||
|
||||
if (macio_resource_count(mdev) < 1 || macio_irq_count(mdev) < 1) {
|
||||
printk(KERN_ERR PFX "Wrong interrupt/addresses in OF tree\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate space for private device-specific data */
|
||||
priv = alloc_orinocodev(sizeof(*card), &mdev->ofdev.dev,
|
||||
airport_hard_reset, NULL);
|
||||
if (!priv) {
|
||||
printk(KERN_ERR PFX "Cannot allocate network device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
card = priv->card;
|
||||
|
||||
hw = &priv->hw;
|
||||
card->mdev = mdev;
|
||||
|
||||
if (macio_request_resource(mdev, 0, DRIVER_NAME)) {
|
||||
printk(KERN_ERR PFX "can't request IO resource !\n");
|
||||
free_orinocodev(priv);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
macio_set_drvdata(mdev, priv);
|
||||
|
||||
/* Setup interrupts & base address */
|
||||
card->irq = macio_irq(mdev, 0);
|
||||
phys_addr = macio_resource_start(mdev, 0); /* Physical address */
|
||||
printk(KERN_DEBUG PFX "Physical address %lx\n", phys_addr);
|
||||
card->vaddr = ioremap(phys_addr, AIRPORT_IO_LEN);
|
||||
if (!card->vaddr) {
|
||||
printk(KERN_ERR PFX "ioremap() failed\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
hermes_struct_init(hw, card->vaddr, HERMES_16BIT_REGSPACING);
|
||||
|
||||
/* Power up card */
|
||||
pmac_call_feature(PMAC_FTR_AIRPORT_ENABLE,
|
||||
macio_get_of_node(mdev), 0, 1);
|
||||
ssleep(1);
|
||||
|
||||
/* Reset it before we get the interrupt */
|
||||
hw->ops->init(hw);
|
||||
|
||||
if (request_irq(card->irq, orinoco_interrupt, 0, DRIVER_NAME, priv)) {
|
||||
printk(KERN_ERR PFX "Couldn't get IRQ %d\n", card->irq);
|
||||
goto failed;
|
||||
}
|
||||
card->irq_requested = 1;
|
||||
|
||||
/* Initialise the main driver */
|
||||
if (orinoco_init(priv) != 0) {
|
||||
printk(KERN_ERR PFX "orinoco_init() failed\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Register an interface with the stack */
|
||||
if (orinoco_if_add(priv, phys_addr, card->irq, NULL) != 0) {
|
||||
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
|
||||
goto failed;
|
||||
}
|
||||
card->ndev_registered = 1;
|
||||
return 0;
|
||||
failed:
|
||||
airport_detach(mdev);
|
||||
return -ENODEV;
|
||||
} /* airport_attach */
|
||||
|
||||
|
||||
static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
|
||||
" (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");
|
||||
|
||||
static const struct of_device_id airport_match[] = {
|
||||
{
|
||||
.name = "radio",
|
||||
},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, airport_match);
|
||||
|
||||
static struct macio_driver airport_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = airport_match,
|
||||
},
|
||||
.probe = airport_attach,
|
||||
.remove = airport_detach,
|
||||
.suspend = airport_suspend,
|
||||
.resume = airport_resume,
|
||||
};
|
||||
|
||||
static int __init
|
||||
init_airport(void)
|
||||
{
|
||||
printk(KERN_DEBUG "%s\n", version);
|
||||
|
||||
return macio_register_driver(&airport_driver);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
exit_airport(void)
|
||||
{
|
||||
macio_unregister_driver(&airport_driver);
|
||||
}
|
||||
|
||||
module_init(init_airport);
|
||||
module_exit(exit_airport);
|
291
drivers/net/wireless/intersil/orinoco/cfg.c
Normal file
291
drivers/net/wireless/intersil/orinoco/cfg.c
Normal file
@@ -0,0 +1,291 @@
|
||||
/* cfg80211 support
|
||||
*
|
||||
* See copyright notice in main.c
|
||||
*/
|
||||
#include <linux/ieee80211.h>
|
||||
#include <net/cfg80211.h>
|
||||
#include "hw.h"
|
||||
#include "main.h"
|
||||
#include "orinoco.h"
|
||||
|
||||
#include "cfg.h"
|
||||
|
||||
/* Supported bitrates. Must agree with hw.c */
|
||||
static struct ieee80211_rate orinoco_rates[] = {
|
||||
{ .bitrate = 10 },
|
||||
{ .bitrate = 20 },
|
||||
{ .bitrate = 55 },
|
||||
{ .bitrate = 110 },
|
||||
};
|
||||
|
||||
static const void * const orinoco_wiphy_privid = &orinoco_wiphy_privid;
|
||||
|
||||
/* Called after orinoco_private is allocated. */
|
||||
void orinoco_wiphy_init(struct wiphy *wiphy)
|
||||
{
|
||||
struct orinoco_private *priv = wiphy_priv(wiphy);
|
||||
|
||||
wiphy->privid = orinoco_wiphy_privid;
|
||||
|
||||
set_wiphy_dev(wiphy, priv->dev);
|
||||
}
|
||||
|
||||
/* Called after firmware is initialised */
|
||||
int orinoco_wiphy_register(struct wiphy *wiphy)
|
||||
{
|
||||
struct orinoco_private *priv = wiphy_priv(wiphy);
|
||||
int i, channels = 0;
|
||||
|
||||
if (priv->firmware_type == FIRMWARE_TYPE_AGERE)
|
||||
wiphy->max_scan_ssids = 1;
|
||||
else
|
||||
wiphy->max_scan_ssids = 0;
|
||||
|
||||
wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
|
||||
|
||||
/* TODO: should we set if we only have demo ad-hoc?
|
||||
* (priv->has_port3)
|
||||
*/
|
||||
if (priv->has_ibss)
|
||||
wiphy->interface_modes |= BIT(NL80211_IFTYPE_ADHOC);
|
||||
|
||||
if (!priv->broken_monitor || force_monitor)
|
||||
wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
|
||||
|
||||
priv->band.bitrates = orinoco_rates;
|
||||
priv->band.n_bitrates = ARRAY_SIZE(orinoco_rates);
|
||||
|
||||
/* Only support channels allowed by the card EEPROM */
|
||||
for (i = 0; i < NUM_CHANNELS; i++) {
|
||||
if (priv->channel_mask & (1 << i)) {
|
||||
priv->channels[i].center_freq =
|
||||
ieee80211_channel_to_frequency(i + 1,
|
||||
IEEE80211_BAND_2GHZ);
|
||||
channels++;
|
||||
}
|
||||
}
|
||||
priv->band.channels = priv->channels;
|
||||
priv->band.n_channels = channels;
|
||||
|
||||
wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band;
|
||||
wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
|
||||
|
||||
i = 0;
|
||||
if (priv->has_wep) {
|
||||
priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP40;
|
||||
i++;
|
||||
|
||||
if (priv->has_big_wep) {
|
||||
priv->cipher_suites[i] = WLAN_CIPHER_SUITE_WEP104;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (priv->has_wpa) {
|
||||
priv->cipher_suites[i] = WLAN_CIPHER_SUITE_TKIP;
|
||||
i++;
|
||||
}
|
||||
wiphy->cipher_suites = priv->cipher_suites;
|
||||
wiphy->n_cipher_suites = i;
|
||||
|
||||
wiphy->rts_threshold = priv->rts_thresh;
|
||||
if (!priv->has_mwo)
|
||||
wiphy->frag_threshold = priv->frag_thresh + 1;
|
||||
wiphy->retry_short = priv->short_retry_limit;
|
||||
wiphy->retry_long = priv->long_retry_limit;
|
||||
|
||||
return wiphy_register(wiphy);
|
||||
}
|
||||
|
||||
static int orinoco_change_vif(struct wiphy *wiphy, struct net_device *dev,
|
||||
enum nl80211_iftype type, u32 *flags,
|
||||
struct vif_params *params)
|
||||
{
|
||||
struct orinoco_private *priv = wiphy_priv(wiphy);
|
||||
int err = 0;
|
||||
unsigned long lock;
|
||||
|
||||
if (orinoco_lock(priv, &lock) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
switch (type) {
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
if (!priv->has_ibss && !priv->has_port3)
|
||||
err = -EINVAL;
|
||||
break;
|
||||
|
||||
case NL80211_IFTYPE_STATION:
|
||||
break;
|
||||
|
||||
case NL80211_IFTYPE_MONITOR:
|
||||
if (priv->broken_monitor && !force_monitor) {
|
||||
wiphy_warn(wiphy,
|
||||
"Monitor mode support is buggy in this firmware, not enabling\n");
|
||||
err = -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
priv->iw_mode = type;
|
||||
set_port_type(priv);
|
||||
err = orinoco_commit(priv);
|
||||
}
|
||||
|
||||
orinoco_unlock(priv, &lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int orinoco_scan(struct wiphy *wiphy,
|
||||
struct cfg80211_scan_request *request)
|
||||
{
|
||||
struct orinoco_private *priv = wiphy_priv(wiphy);
|
||||
int err;
|
||||
|
||||
if (!request)
|
||||
return -EINVAL;
|
||||
|
||||
if (priv->scan_request && priv->scan_request != request)
|
||||
return -EBUSY;
|
||||
|
||||
priv->scan_request = request;
|
||||
|
||||
err = orinoco_hw_trigger_scan(priv, request->ssids);
|
||||
/* On error the we aren't processing the request */
|
||||
if (err)
|
||||
priv->scan_request = NULL;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int orinoco_set_monitor_channel(struct wiphy *wiphy,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
struct orinoco_private *priv = wiphy_priv(wiphy);
|
||||
int err = 0;
|
||||
unsigned long flags;
|
||||
int channel;
|
||||
|
||||
if (!chandef->chan)
|
||||
return -EINVAL;
|
||||
|
||||
if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT)
|
||||
return -EINVAL;
|
||||
|
||||
if (chandef->chan->band != IEEE80211_BAND_2GHZ)
|
||||
return -EINVAL;
|
||||
|
||||
channel = ieee80211_frequency_to_channel(chandef->chan->center_freq);
|
||||
|
||||
if ((channel < 1) || (channel > NUM_CHANNELS) ||
|
||||
!(priv->channel_mask & (1 << (channel - 1))))
|
||||
return -EINVAL;
|
||||
|
||||
if (orinoco_lock(priv, &flags) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
priv->channel = channel;
|
||||
if (priv->iw_mode == NL80211_IFTYPE_MONITOR) {
|
||||
/* Fast channel change - no commit if successful */
|
||||
struct hermes *hw = &priv->hw;
|
||||
err = hw->ops->cmd_wait(hw, HERMES_CMD_TEST |
|
||||
HERMES_TEST_SET_CHANNEL,
|
||||
channel, NULL);
|
||||
}
|
||||
orinoco_unlock(priv, &flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int orinoco_set_wiphy_params(struct wiphy *wiphy, u32 changed)
|
||||
{
|
||||
struct orinoco_private *priv = wiphy_priv(wiphy);
|
||||
int frag_value = -1;
|
||||
int rts_value = -1;
|
||||
int err = 0;
|
||||
|
||||
if (changed & WIPHY_PARAM_RETRY_SHORT) {
|
||||
/* Setting short retry not supported */
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
if (changed & WIPHY_PARAM_RETRY_LONG) {
|
||||
/* Setting long retry not supported */
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
if (changed & WIPHY_PARAM_FRAG_THRESHOLD) {
|
||||
/* Set fragmentation */
|
||||
if (priv->has_mwo) {
|
||||
if (wiphy->frag_threshold == -1)
|
||||
frag_value = 0;
|
||||
else {
|
||||
printk(KERN_WARNING "%s: Fixed fragmentation "
|
||||
"is not supported on this firmware. "
|
||||
"Using MWO robust instead.\n",
|
||||
priv->ndev->name);
|
||||
frag_value = 1;
|
||||
}
|
||||
} else {
|
||||
if (wiphy->frag_threshold == -1)
|
||||
frag_value = 2346;
|
||||
else if ((wiphy->frag_threshold < 257) ||
|
||||
(wiphy->frag_threshold > 2347))
|
||||
err = -EINVAL;
|
||||
else
|
||||
/* cfg80211 value is 257-2347 (odd only)
|
||||
* orinoco rid has range 256-2346 (even only) */
|
||||
frag_value = wiphy->frag_threshold & ~0x1;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed & WIPHY_PARAM_RTS_THRESHOLD) {
|
||||
/* Set RTS.
|
||||
*
|
||||
* Prism documentation suggests default of 2432,
|
||||
* and a range of 0-3000.
|
||||
*
|
||||
* Current implementation uses 2347 as the default and
|
||||
* the upper limit.
|
||||
*/
|
||||
|
||||
if (wiphy->rts_threshold == -1)
|
||||
rts_value = 2347;
|
||||
else if (wiphy->rts_threshold > 2347)
|
||||
err = -EINVAL;
|
||||
else
|
||||
rts_value = wiphy->rts_threshold;
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
unsigned long flags;
|
||||
|
||||
if (orinoco_lock(priv, &flags) != 0)
|
||||
return -EBUSY;
|
||||
|
||||
if (frag_value >= 0) {
|
||||
if (priv->has_mwo)
|
||||
priv->mwo_robust = frag_value;
|
||||
else
|
||||
priv->frag_thresh = frag_value;
|
||||
}
|
||||
if (rts_value >= 0)
|
||||
priv->rts_thresh = rts_value;
|
||||
|
||||
err = orinoco_commit(priv);
|
||||
|
||||
orinoco_unlock(priv, &flags);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
const struct cfg80211_ops orinoco_cfg_ops = {
|
||||
.change_virtual_intf = orinoco_change_vif,
|
||||
.set_monitor_channel = orinoco_set_monitor_channel,
|
||||
.scan = orinoco_scan,
|
||||
.set_wiphy_params = orinoco_set_wiphy_params,
|
||||
};
|
15
drivers/net/wireless/intersil/orinoco/cfg.h
Normal file
15
drivers/net/wireless/intersil/orinoco/cfg.h
Normal file
@@ -0,0 +1,15 @@
|
||||
/* cfg80211 support.
|
||||
*
|
||||
* See copyright notice in main.c
|
||||
*/
|
||||
#ifndef ORINOCO_CFG_H
|
||||
#define ORINOCO_CFG_H
|
||||
|
||||
#include <net/cfg80211.h>
|
||||
|
||||
extern const struct cfg80211_ops orinoco_cfg_ops;
|
||||
|
||||
void orinoco_wiphy_init(struct wiphy *wiphy);
|
||||
int orinoco_wiphy_register(struct wiphy *wiphy);
|
||||
|
||||
#endif /* ORINOCO_CFG_H */
|
387
drivers/net/wireless/intersil/orinoco/fw.c
Normal file
387
drivers/net/wireless/intersil/orinoco/fw.c
Normal file
@@ -0,0 +1,387 @@
|
||||
/* Firmware file reading and download helpers
|
||||
*
|
||||
* See copyright notice in main.c
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "hermes.h"
|
||||
#include "hermes_dld.h"
|
||||
#include "orinoco.h"
|
||||
|
||||
#include "fw.h"
|
||||
|
||||
/* End markers (for Symbol firmware only) */
|
||||
#define TEXT_END 0x1A /* End of text header */
|
||||
|
||||
struct fw_info {
|
||||
char *pri_fw;
|
||||
char *sta_fw;
|
||||
char *ap_fw;
|
||||
u32 pda_addr;
|
||||
u16 pda_size;
|
||||
};
|
||||
|
||||
static const struct fw_info orinoco_fw[] = {
|
||||
{ NULL, "agere_sta_fw.bin", "agere_ap_fw.bin", 0x00390000, 1000 },
|
||||
{ NULL, "prism_sta_fw.bin", "prism_ap_fw.bin", 0, 1024 },
|
||||
{ "symbol_sp24t_prim_fw", "symbol_sp24t_sec_fw", NULL, 0x00003100, 512 }
|
||||
};
|
||||
MODULE_FIRMWARE("agere_sta_fw.bin");
|
||||
MODULE_FIRMWARE("agere_ap_fw.bin");
|
||||
MODULE_FIRMWARE("prism_sta_fw.bin");
|
||||
MODULE_FIRMWARE("prism_ap_fw.bin");
|
||||
MODULE_FIRMWARE("symbol_sp24t_prim_fw");
|
||||
MODULE_FIRMWARE("symbol_sp24t_sec_fw");
|
||||
|
||||
/* Structure used to access fields in FW
|
||||
* Make sure LE decoding macros are used
|
||||
*/
|
||||
struct orinoco_fw_header {
|
||||
char hdr_vers[6]; /* ASCII string for header version */
|
||||
__le16 headersize; /* Total length of header */
|
||||
__le32 entry_point; /* NIC entry point */
|
||||
__le32 blocks; /* Number of blocks to program */
|
||||
__le32 block_offset; /* Offset of block data from eof header */
|
||||
__le32 pdr_offset; /* Offset to PDR data from eof header */
|
||||
__le32 pri_offset; /* Offset to primary plug data */
|
||||
__le32 compat_offset; /* Offset to compatibility data*/
|
||||
char signature[0]; /* FW signature length headersize-20 */
|
||||
} __packed;
|
||||
|
||||
/* Check the range of various header entries. Return a pointer to a
|
||||
* description of the problem, or NULL if everything checks out. */
|
||||
static const char *validate_fw(const struct orinoco_fw_header *hdr, size_t len)
|
||||
{
|
||||
u16 hdrsize;
|
||||
|
||||
if (len < sizeof(*hdr))
|
||||
return "image too small";
|
||||
if (memcmp(hdr->hdr_vers, "HFW", 3) != 0)
|
||||
return "format not recognised";
|
||||
|
||||
hdrsize = le16_to_cpu(hdr->headersize);
|
||||
if (hdrsize > len)
|
||||
return "bad headersize";
|
||||
if ((hdrsize + le32_to_cpu(hdr->block_offset)) > len)
|
||||
return "bad block offset";
|
||||
if ((hdrsize + le32_to_cpu(hdr->pdr_offset)) > len)
|
||||
return "bad PDR offset";
|
||||
if ((hdrsize + le32_to_cpu(hdr->pri_offset)) > len)
|
||||
return "bad PRI offset";
|
||||
if ((hdrsize + le32_to_cpu(hdr->compat_offset)) > len)
|
||||
return "bad compat offset";
|
||||
|
||||
/* TODO: consider adding a checksum or CRC to the firmware format */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP)
|
||||
static inline const struct firmware *
|
||||
orinoco_cached_fw_get(struct orinoco_private *priv, bool primary)
|
||||
{
|
||||
if (primary)
|
||||
return priv->cached_pri_fw;
|
||||
else
|
||||
return priv->cached_fw;
|
||||
}
|
||||
#else
|
||||
#define orinoco_cached_fw_get(priv, primary) (NULL)
|
||||
#endif
|
||||
|
||||
/* Download either STA or AP firmware into the card. */
|
||||
static int
|
||||
orinoco_dl_firmware(struct orinoco_private *priv,
|
||||
const struct fw_info *fw,
|
||||
int ap)
|
||||
{
|
||||
/* Plug Data Area (PDA) */
|
||||
__le16 *pda;
|
||||
|
||||
struct hermes *hw = &priv->hw;
|
||||
const struct firmware *fw_entry;
|
||||
const struct orinoco_fw_header *hdr;
|
||||
const unsigned char *first_block;
|
||||
const void *end;
|
||||
const char *firmware;
|
||||
const char *fw_err;
|
||||
struct device *dev = priv->dev;
|
||||
int err = 0;
|
||||
|
||||
pda = kzalloc(fw->pda_size, GFP_KERNEL);
|
||||
if (!pda)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ap)
|
||||
firmware = fw->ap_fw;
|
||||
else
|
||||
firmware = fw->sta_fw;
|
||||
|
||||
dev_dbg(dev, "Attempting to download firmware %s\n", firmware);
|
||||
|
||||
/* Read current plug data */
|
||||
err = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size);
|
||||
dev_dbg(dev, "Read PDA returned %d\n", err);
|
||||
if (err)
|
||||
goto free;
|
||||
|
||||
if (!orinoco_cached_fw_get(priv, false)) {
|
||||
err = request_firmware(&fw_entry, firmware, priv->dev);
|
||||
|
||||
if (err) {
|
||||
dev_err(dev, "Cannot find firmware %s\n", firmware);
|
||||
err = -ENOENT;
|
||||
goto free;
|
||||
}
|
||||
} else
|
||||
fw_entry = orinoco_cached_fw_get(priv, false);
|
||||
|
||||
hdr = (const struct orinoco_fw_header *) fw_entry->data;
|
||||
|
||||
fw_err = validate_fw(hdr, fw_entry->size);
|
||||
if (fw_err) {
|
||||
dev_warn(dev, "Invalid firmware image detected (%s). "
|
||||
"Aborting download\n", fw_err);
|
||||
err = -EINVAL;
|
||||
goto abort;
|
||||
}
|
||||
|
||||
/* Enable aux port to allow programming */
|
||||
err = hw->ops->program_init(hw, le32_to_cpu(hdr->entry_point));
|
||||
dev_dbg(dev, "Program init returned %d\n", err);
|
||||
if (err != 0)
|
||||
goto abort;
|
||||
|
||||
/* Program data */
|
||||
first_block = (fw_entry->data +
|
||||
le16_to_cpu(hdr->headersize) +
|
||||
le32_to_cpu(hdr->block_offset));
|
||||
end = fw_entry->data + fw_entry->size;
|
||||
|
||||
err = hermes_program(hw, first_block, end);
|
||||
dev_dbg(dev, "Program returned %d\n", err);
|
||||
if (err != 0)
|
||||
goto abort;
|
||||
|
||||
/* Update production data */
|
||||
first_block = (fw_entry->data +
|
||||
le16_to_cpu(hdr->headersize) +
|
||||
le32_to_cpu(hdr->pdr_offset));
|
||||
|
||||
err = hermes_apply_pda_with_defaults(hw, first_block, end, pda,
|
||||
&pda[fw->pda_size / sizeof(*pda)]);
|
||||
dev_dbg(dev, "Apply PDA returned %d\n", err);
|
||||
if (err)
|
||||
goto abort;
|
||||
|
||||
/* Tell card we've finished */
|
||||
err = hw->ops->program_end(hw);
|
||||
dev_dbg(dev, "Program end returned %d\n", err);
|
||||
if (err != 0)
|
||||
goto abort;
|
||||
|
||||
/* Check if we're running */
|
||||
dev_dbg(dev, "hermes_present returned %d\n", hermes_present(hw));
|
||||
|
||||
abort:
|
||||
/* If we requested the firmware, release it. */
|
||||
if (!orinoco_cached_fw_get(priv, false))
|
||||
release_firmware(fw_entry);
|
||||
|
||||
free:
|
||||
kfree(pda);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a firmware image - stop the card, load the firmware, reset
|
||||
* the card and make sure it responds. For the secondary firmware take
|
||||
* care of the PDA - read it and then write it on top of the firmware.
|
||||
*/
|
||||
static int
|
||||
symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
|
||||
const unsigned char *image, const void *end,
|
||||
int secondary)
|
||||
{
|
||||
struct hermes *hw = &priv->hw;
|
||||
int ret = 0;
|
||||
const unsigned char *ptr;
|
||||
const unsigned char *first_block;
|
||||
|
||||
/* Plug Data Area (PDA) */
|
||||
__le16 *pda = NULL;
|
||||
|
||||
/* Binary block begins after the 0x1A marker */
|
||||
ptr = image;
|
||||
while (*ptr++ != TEXT_END);
|
||||
first_block = ptr;
|
||||
|
||||
/* Read the PDA from EEPROM */
|
||||
if (secondary) {
|
||||
pda = kzalloc(fw->pda_size, GFP_KERNEL);
|
||||
if (!pda)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size);
|
||||
if (ret)
|
||||
goto free;
|
||||
}
|
||||
|
||||
/* Stop the firmware, so that it can be safely rewritten */
|
||||
if (priv->stop_fw) {
|
||||
ret = priv->stop_fw(priv, 1);
|
||||
if (ret)
|
||||
goto free;
|
||||
}
|
||||
|
||||
/* Program the adapter with new firmware */
|
||||
ret = hermes_program(hw, first_block, end);
|
||||
if (ret)
|
||||
goto free;
|
||||
|
||||
/* Write the PDA to the adapter */
|
||||
if (secondary) {
|
||||
size_t len = hermes_blocks_length(first_block, end);
|
||||
ptr = first_block + len;
|
||||
ret = hermes_apply_pda(hw, ptr, end, pda,
|
||||
&pda[fw->pda_size / sizeof(*pda)]);
|
||||
kfree(pda);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Run the firmware */
|
||||
if (priv->stop_fw) {
|
||||
ret = priv->stop_fw(priv, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Reset hermes chip and make sure it responds */
|
||||
ret = hw->ops->init(hw);
|
||||
|
||||
/* hermes_reset() should return 0 with the secondary firmware */
|
||||
if (secondary && ret != 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* And this should work with any firmware */
|
||||
if (!hermes_present(hw))
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
|
||||
free:
|
||||
kfree(pda);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Download the firmware into the card, this also does a PCMCIA soft
|
||||
* reset on the card, to make sure it's in a sane state.
|
||||
*/
|
||||
static int
|
||||
symbol_dl_firmware(struct orinoco_private *priv,
|
||||
const struct fw_info *fw)
|
||||
{
|
||||
struct device *dev = priv->dev;
|
||||
int ret;
|
||||
const struct firmware *fw_entry;
|
||||
|
||||
if (!orinoco_cached_fw_get(priv, true)) {
|
||||
if (request_firmware(&fw_entry, fw->pri_fw, priv->dev) != 0) {
|
||||
dev_err(dev, "Cannot find firmware: %s\n", fw->pri_fw);
|
||||
return -ENOENT;
|
||||
}
|
||||
} else
|
||||
fw_entry = orinoco_cached_fw_get(priv, true);
|
||||
|
||||
/* Load primary firmware */
|
||||
ret = symbol_dl_image(priv, fw, fw_entry->data,
|
||||
fw_entry->data + fw_entry->size, 0);
|
||||
|
||||
if (!orinoco_cached_fw_get(priv, true))
|
||||
release_firmware(fw_entry);
|
||||
if (ret) {
|
||||
dev_err(dev, "Primary firmware download failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!orinoco_cached_fw_get(priv, false)) {
|
||||
if (request_firmware(&fw_entry, fw->sta_fw, priv->dev) != 0) {
|
||||
dev_err(dev, "Cannot find firmware: %s\n", fw->sta_fw);
|
||||
return -ENOENT;
|
||||
}
|
||||
} else
|
||||
fw_entry = orinoco_cached_fw_get(priv, false);
|
||||
|
||||
/* Load secondary firmware */
|
||||
ret = symbol_dl_image(priv, fw, fw_entry->data,
|
||||
fw_entry->data + fw_entry->size, 1);
|
||||
if (!orinoco_cached_fw_get(priv, false))
|
||||
release_firmware(fw_entry);
|
||||
if (ret)
|
||||
dev_err(dev, "Secondary firmware download failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int orinoco_download(struct orinoco_private *priv)
|
||||
{
|
||||
int err = 0;
|
||||
/* Reload firmware */
|
||||
switch (priv->firmware_type) {
|
||||
case FIRMWARE_TYPE_AGERE:
|
||||
/* case FIRMWARE_TYPE_INTERSIL: */
|
||||
err = orinoco_dl_firmware(priv,
|
||||
&orinoco_fw[priv->firmware_type], 0);
|
||||
break;
|
||||
|
||||
case FIRMWARE_TYPE_SYMBOL:
|
||||
err = symbol_dl_firmware(priv,
|
||||
&orinoco_fw[priv->firmware_type]);
|
||||
break;
|
||||
case FIRMWARE_TYPE_INTERSIL:
|
||||
break;
|
||||
}
|
||||
/* TODO: if we fail we probably need to reinitialise
|
||||
* the driver */
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP)
|
||||
void orinoco_cache_fw(struct orinoco_private *priv, int ap)
|
||||
{
|
||||
const struct firmware *fw_entry = NULL;
|
||||
const char *pri_fw;
|
||||
const char *fw;
|
||||
|
||||
pri_fw = orinoco_fw[priv->firmware_type].pri_fw;
|
||||
if (ap)
|
||||
fw = orinoco_fw[priv->firmware_type].ap_fw;
|
||||
else
|
||||
fw = orinoco_fw[priv->firmware_type].sta_fw;
|
||||
|
||||
if (pri_fw) {
|
||||
if (request_firmware(&fw_entry, pri_fw, priv->dev) == 0)
|
||||
priv->cached_pri_fw = fw_entry;
|
||||
}
|
||||
|
||||
if (fw) {
|
||||
if (request_firmware(&fw_entry, fw, priv->dev) == 0)
|
||||
priv->cached_fw = fw_entry;
|
||||
}
|
||||
}
|
||||
|
||||
void orinoco_uncache_fw(struct orinoco_private *priv)
|
||||
{
|
||||
release_firmware(priv->cached_pri_fw);
|
||||
release_firmware(priv->cached_fw);
|
||||
priv->cached_pri_fw = NULL;
|
||||
priv->cached_fw = NULL;
|
||||
}
|
||||
#endif
|
21
drivers/net/wireless/intersil/orinoco/fw.h
Normal file
21
drivers/net/wireless/intersil/orinoco/fw.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* Firmware file reading and download helpers
|
||||
*
|
||||
* See copyright notice in main.c
|
||||
*/
|
||||
#ifndef _ORINOCO_FW_H_
|
||||
#define _ORINOCO_FW_H_
|
||||
|
||||
/* Forward declations */
|
||||
struct orinoco_private;
|
||||
|
||||
int orinoco_download(struct orinoco_private *priv);
|
||||
|
||||
#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP)
|
||||
void orinoco_cache_fw(struct orinoco_private *priv, int ap);
|
||||
void orinoco_uncache_fw(struct orinoco_private *priv);
|
||||
#else
|
||||
#define orinoco_cache_fw(priv, ap) do { } while (0)
|
||||
#define orinoco_uncache_fw(priv) do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif /* _ORINOCO_FW_H_ */
|
777
drivers/net/wireless/intersil/orinoco/hermes.c
Normal file
777
drivers/net/wireless/intersil/orinoco/hermes.c
Normal file
@@ -0,0 +1,777 @@
|
||||
/* hermes.c
|
||||
*
|
||||
* Driver core for the "Hermes" wireless MAC controller, as used in
|
||||
* the Lucent Orinoco and Cabletron RoamAbout cards. It should also
|
||||
* work on the hfa3841 and hfa3842 MAC controller chips used in the
|
||||
* Prism II chipsets.
|
||||
*
|
||||
* This is not a complete driver, just low-level access routines for
|
||||
* the MAC controller itself.
|
||||
*
|
||||
* Based on the prism2 driver from Absolute Value Systems' linux-wlan
|
||||
* project, the Linux wvlan_cs driver, Lucent's HCF-Light
|
||||
* (wvlan_hcf.c) library, and the NetBSD wireless driver (in no
|
||||
* particular order).
|
||||
*
|
||||
* Copyright (C) 2000, David Gibson, Linuxcare Australia.
|
||||
* (C) Copyright David Gibson, IBM Corp. 2001-2003.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "hermes.h"
|
||||
|
||||
/* 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 */
|
||||
|
||||
/*
|
||||
* AUX port access. To unlock the AUX port write the access keys to the
|
||||
* PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL
|
||||
* register. Then read it and make sure it's HERMES_AUX_ENABLED.
|
||||
*/
|
||||
#define HERMES_AUX_ENABLE 0x8000 /* Enable auxiliary port access */
|
||||
#define HERMES_AUX_DISABLE 0x4000 /* Disable to auxiliary port access */
|
||||
#define HERMES_AUX_ENABLED 0xC000 /* Auxiliary port is open */
|
||||
#define HERMES_AUX_DISABLED 0x0000 /* Auxiliary port is closed */
|
||||
|
||||
#define HERMES_AUX_PW0 0xFE01
|
||||
#define HERMES_AUX_PW1 0xDC23
|
||||
#define HERMES_AUX_PW2 0xBA45
|
||||
|
||||
/* HERMES_CMD_DOWNLD */
|
||||
#define HERMES_PROGRAM_DISABLE (0x0000 | HERMES_CMD_DOWNLD)
|
||||
#define HERMES_PROGRAM_ENABLE_VOLATILE (0x0100 | HERMES_CMD_DOWNLD)
|
||||
#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD)
|
||||
#define HERMES_PROGRAM_NON_VOLATILE (0x0300 | HERMES_CMD_DOWNLD)
|
||||
|
||||
/*
|
||||
* Debugging helpers
|
||||
*/
|
||||
|
||||
#define DMSG(stuff...) do {printk(KERN_DEBUG "hermes @ %p: " , hw->iobase); \
|
||||
printk(stuff); } while (0)
|
||||
|
||||
#undef HERMES_DEBUG
|
||||
#ifdef HERMES_DEBUG
|
||||
#include <stdarg.h>
|
||||
|
||||
#define DEBUG(lvl, stuff...) if ((lvl) <= HERMES_DEBUG) DMSG(stuff)
|
||||
|
||||
#else /* ! HERMES_DEBUG */
|
||||
|
||||
#define DEBUG(lvl, stuff...) do { } while (0)
|
||||
|
||||
#endif /* ! HERMES_DEBUG */
|
||||
|
||||
static const struct hermes_ops hermes_ops_local;
|
||||
|
||||
/*
|
||||
* Internal functions
|
||||
*/
|
||||
|
||||
/* Issue a command to the chip. Waiting for it to complete is the caller's
|
||||
problem.
|
||||
|
||||
Returns -EBUSY if the command register is busy, 0 on success.
|
||||
|
||||
Callable from any context.
|
||||
*/
|
||||
static int hermes_issue_cmd(struct hermes *hw, u16 cmd, u16 param0,
|
||||
u16 param1, u16 param2)
|
||||
{
|
||||
int k = CMD_BUSY_TIMEOUT;
|
||||
u16 reg;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
if (reg & HERMES_CMD_BUSY)
|
||||
return -EBUSY;
|
||||
|
||||
hermes_write_regn(hw, PARAM2, param2);
|
||||
hermes_write_regn(hw, PARAM1, param1);
|
||||
hermes_write_regn(hw, PARAM0, param0);
|
||||
hermes_write_regn(hw, CMD, cmd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function definitions
|
||||
*/
|
||||
|
||||
/* For doing cmds that wipe the magic constant in SWSUPPORT0 */
|
||||
static int hermes_doicmd_wait(struct hermes *hw, u16 cmd,
|
||||
u16 parm0, u16 parm1, u16 parm2,
|
||||
struct hermes_response *resp)
|
||||
{
|
||||
int err = 0;
|
||||
int k;
|
||||
u16 status, reg;
|
||||
|
||||
err = hermes_issue_cmd(hw, cmd, parm0, parm1, parm2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
reg = hermes_read_regn(hw, EVSTAT);
|
||||
k = CMD_INIT_TIMEOUT;
|
||||
while ((!(reg & HERMES_EV_CMD)) && k) {
|
||||
k--;
|
||||
udelay(10);
|
||||
reg = hermes_read_regn(hw, EVSTAT);
|
||||
}
|
||||
|
||||
hermes_write_regn(hw, SWSUPPORT0, HERMES_MAGIC);
|
||||
|
||||
if (!hermes_present(hw)) {
|
||||
DEBUG(0, "hermes @ 0x%x: Card removed during reset.\n",
|
||||
hw->iobase);
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(reg & HERMES_EV_CMD)) {
|
||||
printk(KERN_ERR "hermes @ %p: "
|
||||
"Timeout waiting for card to reset (reg=0x%04x)!\n",
|
||||
hw->iobase, reg);
|
||||
err = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (status & HERMES_STATUS_RESULT)
|
||||
err = -EIO;
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
void hermes_struct_init(struct hermes *hw, void __iomem *address,
|
||||
int reg_spacing)
|
||||
{
|
||||
hw->iobase = address;
|
||||
hw->reg_spacing = reg_spacing;
|
||||
hw->inten = 0x0;
|
||||
hw->eeprom_pda = false;
|
||||
hw->ops = &hermes_ops_local;
|
||||
}
|
||||
EXPORT_SYMBOL(hermes_struct_init);
|
||||
|
||||
static int hermes_init(struct hermes *hw)
|
||||
{
|
||||
u16 reg;
|
||||
int err = 0;
|
||||
int k;
|
||||
|
||||
/* We don't want to be interrupted while resetting the chipset */
|
||||
hw->inten = 0x0;
|
||||
hermes_write_regn(hw, INTEN, 0);
|
||||
hermes_write_regn(hw, EVACK, 0xffff);
|
||||
|
||||
/* Normally it's a "can't happen" for the command register to
|
||||
be busy when we go to issue a command because we are
|
||||
serializing all commands. However we want to have some
|
||||
chance of resetting the card even if it gets into a stupid
|
||||
state, so we actually wait to see if the command register
|
||||
will unbusy itself here. */
|
||||
k = CMD_BUSY_TIMEOUT;
|
||||
reg = hermes_read_regn(hw, CMD);
|
||||
while (k && (reg & HERMES_CMD_BUSY)) {
|
||||
if (reg == 0xffff) /* Special case - the card has probably been
|
||||
removed, so don't wait for the timeout */
|
||||
return -ENODEV;
|
||||
|
||||
k--;
|
||||
udelay(1);
|
||||
reg = hermes_read_regn(hw, CMD);
|
||||
}
|
||||
|
||||
/* No need to explicitly handle the timeout - if we've timed
|
||||
out hermes_issue_cmd() will probably return -EBUSY below */
|
||||
|
||||
/* According to the documentation, EVSTAT may contain
|
||||
obsolete event occurrence information. We have to acknowledge
|
||||
it by writing EVACK. */
|
||||
reg = hermes_read_regn(hw, EVSTAT);
|
||||
hermes_write_regn(hw, EVACK, reg);
|
||||
|
||||
/* We don't use hermes_docmd_wait here, because the reset wipes
|
||||
the magic constant in SWSUPPORT0 away, and it gets confused */
|
||||
err = hermes_doicmd_wait(hw, HERMES_CMD_INIT, 0, 0, 0, NULL);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Issue a command to the chip, and (busy!) wait for it to
|
||||
* complete.
|
||||
*
|
||||
* Returns:
|
||||
* < 0 on internal error
|
||||
* 0 on success
|
||||
* > 0 on error returned by the firmware
|
||||
*
|
||||
* Callable from any context, but locking is your problem. */
|
||||
static int hermes_docmd_wait(struct hermes *hw, u16 cmd, u16 parm0,
|
||||
struct hermes_response *resp)
|
||||
{
|
||||
int err;
|
||||
int k;
|
||||
u16 reg;
|
||||
u16 status;
|
||||
|
||||
err = hermes_issue_cmd(hw, cmd, parm0, 0, 0);
|
||||
if (err) {
|
||||
if (!hermes_present(hw)) {
|
||||
if (net_ratelimit())
|
||||
printk(KERN_WARNING "hermes @ %p: "
|
||||
"Card removed while issuing command "
|
||||
"0x%04x.\n", hw->iobase, cmd);
|
||||
err = -ENODEV;
|
||||
} else
|
||||
if (net_ratelimit())
|
||||
printk(KERN_ERR "hermes @ %p: "
|
||||
"Error %d issuing command 0x%04x.\n",
|
||||
hw->iobase, err, cmd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
reg = hermes_read_regn(hw, EVSTAT);
|
||||
k = CMD_COMPL_TIMEOUT;
|
||||
while ((!(reg & HERMES_EV_CMD)) && k) {
|
||||
k--;
|
||||
udelay(10);
|
||||
reg = hermes_read_regn(hw, EVSTAT);
|
||||
}
|
||||
|
||||
if (!hermes_present(hw)) {
|
||||
printk(KERN_WARNING "hermes @ %p: Card removed "
|
||||
"while waiting for command 0x%04x completion.\n",
|
||||
hw->iobase, cmd);
|
||||
err = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!(reg & HERMES_EV_CMD)) {
|
||||
printk(KERN_ERR "hermes @ %p: Timeout waiting for "
|
||||
"command 0x%04x completion.\n", hw->iobase, cmd);
|
||||
err = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (status & HERMES_STATUS_RESULT)
|
||||
err = -EIO;
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int hermes_allocate(struct hermes *hw, u16 size, u16 *fid)
|
||||
{
|
||||
int err = 0;
|
||||
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, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
reg = hermes_read_regn(hw, EVSTAT);
|
||||
k = ALLOC_COMPL_TIMEOUT;
|
||||
while ((!(reg & HERMES_EV_ALLOC)) && k) {
|
||||
k--;
|
||||
udelay(10);
|
||||
reg = hermes_read_regn(hw, EVSTAT);
|
||||
}
|
||||
|
||||
if (!hermes_present(hw)) {
|
||||
printk(KERN_WARNING "hermes @ %p: "
|
||||
"Card removed waiting for frame allocation.\n",
|
||||
hw->iobase);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!(reg & HERMES_EV_ALLOC)) {
|
||||
printk(KERN_ERR "hermes @ %p: "
|
||||
"Timeout waiting for frame allocation\n",
|
||||
hw->iobase);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
*fid = hermes_read_regn(hw, ALLOCFID);
|
||||
hermes_write_regn(hw, EVACK, HERMES_EV_ALLOC);
|
||||
|
||||
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 */
|
||||
static int hermes_bap_seek(struct hermes *hw, int bap, u16 id, u16 offset)
|
||||
{
|
||||
int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0;
|
||||
int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0;
|
||||
int k;
|
||||
u16 reg;
|
||||
|
||||
/* Paranoia.. */
|
||||
if ((offset > HERMES_BAP_OFFSET_MAX) || (offset % 2))
|
||||
return -EINVAL;
|
||||
|
||||
k = HERMES_BAP_BUSY_TIMEOUT;
|
||||
reg = hermes_read_reg(hw, oreg);
|
||||
while ((reg & HERMES_OFFSET_BUSY) && k) {
|
||||
k--;
|
||||
udelay(1);
|
||||
reg = hermes_read_reg(hw, oreg);
|
||||
}
|
||||
|
||||
if (reg & HERMES_OFFSET_BUSY)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
/* Now we actually set up the transfer */
|
||||
hermes_write_reg(hw, sreg, id);
|
||||
hermes_write_reg(hw, oreg, offset);
|
||||
|
||||
/* Wait for the BAP to be ready */
|
||||
k = HERMES_BAP_BUSY_TIMEOUT;
|
||||
reg = hermes_read_reg(hw, oreg);
|
||||
while ((reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) {
|
||||
k--;
|
||||
udelay(1);
|
||||
reg = hermes_read_reg(hw, oreg);
|
||||
}
|
||||
|
||||
if (reg != offset) {
|
||||
printk(KERN_ERR "hermes @ %p: BAP%d offset %s: "
|
||||
"reg=0x%x id=0x%x offset=0x%x\n", hw->iobase, bap,
|
||||
(reg & HERMES_OFFSET_BUSY) ? "timeout" : "error",
|
||||
reg, id, offset);
|
||||
|
||||
if (reg & HERMES_OFFSET_BUSY)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return -EIO; /* error or wrong offset */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read a block of data from the chip's buffer, via the
|
||||
* BAP. Synchronization/serialization is the caller's problem. len
|
||||
* must be even.
|
||||
*
|
||||
* Returns:
|
||||
* < 0 on internal failure (errno)
|
||||
* 0 on success
|
||||
* > 0 on error from firmware
|
||||
*/
|
||||
static int hermes_bap_pread(struct hermes *hw, int bap, void *buf, int len,
|
||||
u16 id, u16 offset)
|
||||
{
|
||||
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
|
||||
int err = 0;
|
||||
|
||||
if ((len < 0) || (len % 2))
|
||||
return -EINVAL;
|
||||
|
||||
err = hermes_bap_seek(hw, bap, id, offset);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Actually do the transfer */
|
||||
hermes_read_words(hw, dreg, buf, len / 2);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Write a block of data to the chip's buffer, via the
|
||||
* BAP. Synchronization/serialization is the caller's problem.
|
||||
*
|
||||
* Returns:
|
||||
* < 0 on internal failure (errno)
|
||||
* 0 on success
|
||||
* > 0 on error from firmware
|
||||
*/
|
||||
static int hermes_bap_pwrite(struct hermes *hw, int bap, const void *buf,
|
||||
int len, u16 id, u16 offset)
|
||||
{
|
||||
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
|
||||
int err = 0;
|
||||
|
||||
if (len < 0)
|
||||
return -EINVAL;
|
||||
|
||||
err = hermes_bap_seek(hw, bap, id, offset);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
/* Actually do the transfer */
|
||||
hermes_write_bytes(hw, dreg, buf, len);
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Read a Length-Type-Value record from the card.
|
||||
*
|
||||
* If length is NULL, we ignore the length read from the card, and
|
||||
* read the entire buffer regardless. This is useful because some of
|
||||
* the configuration records appear to have incorrect lengths in
|
||||
* practice.
|
||||
*
|
||||
* Callable from user or bh context. */
|
||||
static int hermes_read_ltv(struct hermes *hw, int bap, u16 rid,
|
||||
unsigned bufsize, u16 *length, void *buf)
|
||||
{
|
||||
int err = 0;
|
||||
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
|
||||
u16 rlength, rtype;
|
||||
unsigned nwords;
|
||||
|
||||
if (bufsize % 2)
|
||||
return -EINVAL;
|
||||
|
||||
err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS, rid, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = hermes_bap_seek(hw, bap, rid, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
rlength = hermes_read_reg(hw, dreg);
|
||||
|
||||
if (!rlength)
|
||||
return -ENODATA;
|
||||
|
||||
rtype = hermes_read_reg(hw, dreg);
|
||||
|
||||
if (length)
|
||||
*length = rlength;
|
||||
|
||||
if (rtype != rid)
|
||||
printk(KERN_WARNING "hermes @ %p: %s(): "
|
||||
"rid (0x%04x) does not match type (0x%04x)\n",
|
||||
hw->iobase, __func__, rid, rtype);
|
||||
if (HERMES_RECLEN_TO_BYTES(rlength) > bufsize)
|
||||
printk(KERN_WARNING "hermes @ %p: "
|
||||
"Truncating LTV record from %d to %d bytes. "
|
||||
"(rid=0x%04x, len=0x%04x)\n", hw->iobase,
|
||||
HERMES_RECLEN_TO_BYTES(rlength), bufsize, rid, rlength);
|
||||
|
||||
nwords = min((unsigned)rlength - 1, bufsize / 2);
|
||||
hermes_read_words(hw, dreg, buf, nwords);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hermes_write_ltv(struct hermes *hw, int bap, u16 rid,
|
||||
u16 length, const void *value)
|
||||
{
|
||||
int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
|
||||
int err = 0;
|
||||
unsigned count;
|
||||
|
||||
if (length == 0)
|
||||
return -EINVAL;
|
||||
|
||||
err = hermes_bap_seek(hw, bap, rid, 0);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
hermes_write_reg(hw, dreg, length);
|
||||
hermes_write_reg(hw, dreg, rid);
|
||||
|
||||
count = length - 1;
|
||||
|
||||
hermes_write_bytes(hw, dreg, value, count << 1);
|
||||
|
||||
err = hermes_docmd_wait(hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE,
|
||||
rid, NULL);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*** Hermes AUX control ***/
|
||||
|
||||
static inline void
|
||||
hermes_aux_setaddr(struct hermes *hw, u32 addr)
|
||||
{
|
||||
hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
|
||||
hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
|
||||
}
|
||||
|
||||
static inline int
|
||||
hermes_aux_control(struct hermes *hw, int enabled)
|
||||
{
|
||||
int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
|
||||
int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
|
||||
int i;
|
||||
|
||||
/* Already open? */
|
||||
if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
|
||||
return 0;
|
||||
|
||||
hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
|
||||
hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
|
||||
hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
|
||||
hermes_write_reg(hw, HERMES_CONTROL, action);
|
||||
|
||||
for (i = 0; i < 20; i++) {
|
||||
udelay(10);
|
||||
if (hermes_read_reg(hw, HERMES_CONTROL) ==
|
||||
desired_state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*** Hermes programming ***/
|
||||
|
||||
/* About to start programming data (Hermes I)
|
||||
* offset is the entry point
|
||||
*
|
||||
* Spectrum_cs' Symbol fw does not require this
|
||||
* wl_lkm Agere fw does
|
||||
* Don't know about intersil
|
||||
*/
|
||||
static int hermesi_program_init(struct hermes *hw, u32 offset)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Disable interrupts?*/
|
||||
/*hw->inten = 0x0;*/
|
||||
/*hermes_write_regn(hw, INTEN, 0);*/
|
||||
/*hermes_set_irqmask(hw, 0);*/
|
||||
|
||||
/* Acknowledge any outstanding command */
|
||||
hermes_write_regn(hw, EVACK, 0xFFFF);
|
||||
|
||||
/* Using init_cmd_wait rather than cmd_wait */
|
||||
err = hw->ops->init_cmd_wait(hw,
|
||||
0x0100 | HERMES_CMD_INIT,
|
||||
0, 0, 0, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = hw->ops->init_cmd_wait(hw,
|
||||
0x0000 | HERMES_CMD_INIT,
|
||||
0, 0, 0, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = hermes_aux_control(hw, 1);
|
||||
pr_debug("AUX enable returned %d\n", err);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pr_debug("Enabling volatile, EP 0x%08x\n", offset);
|
||||
err = hw->ops->init_cmd_wait(hw,
|
||||
HERMES_PROGRAM_ENABLE_VOLATILE,
|
||||
offset & 0xFFFFu,
|
||||
offset >> 16,
|
||||
0,
|
||||
NULL);
|
||||
pr_debug("PROGRAM_ENABLE returned %d\n", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Done programming data (Hermes I)
|
||||
*
|
||||
* Spectrum_cs' Symbol fw does not require this
|
||||
* wl_lkm Agere fw does
|
||||
* Don't know about intersil
|
||||
*/
|
||||
static int hermesi_program_end(struct hermes *hw)
|
||||
{
|
||||
struct hermes_response resp;
|
||||
int rc = 0;
|
||||
int err;
|
||||
|
||||
rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);
|
||||
|
||||
pr_debug("PROGRAM_DISABLE returned %d, "
|
||||
"r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
|
||||
rc, resp.resp0, resp.resp1, resp.resp2);
|
||||
|
||||
if ((rc == 0) &&
|
||||
((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD))
|
||||
rc = -EIO;
|
||||
|
||||
err = hermes_aux_control(hw, 0);
|
||||
pr_debug("AUX disable returned %d\n", err);
|
||||
|
||||
/* Acknowledge any outstanding command */
|
||||
hermes_write_regn(hw, EVACK, 0xFFFF);
|
||||
|
||||
/* Reinitialise, ignoring return */
|
||||
(void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
|
||||
0, 0, 0, NULL);
|
||||
|
||||
return rc ? rc : err;
|
||||
}
|
||||
|
||||
static int hermes_program_bytes(struct hermes *hw, const char *data,
|
||||
u32 addr, u32 len)
|
||||
{
|
||||
/* wl lkm splits the programming into chunks of 2000 bytes.
|
||||
* This restriction appears to come from USB. The PCMCIA
|
||||
* adapters can program the whole lot in one go */
|
||||
hermes_aux_setaddr(hw, addr);
|
||||
hermes_write_bytes(hw, HERMES_AUXDATA, data, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read PDA from the adapter */
|
||||
static int hermes_read_pda(struct hermes *hw, __le16 *pda, u32 pda_addr,
|
||||
u16 pda_len)
|
||||
{
|
||||
int ret;
|
||||
u16 pda_size;
|
||||
u16 data_len = pda_len;
|
||||
__le16 *data = pda;
|
||||
|
||||
if (hw->eeprom_pda) {
|
||||
/* PDA of spectrum symbol is in eeprom */
|
||||
|
||||
/* Issue command to read EEPROM */
|
||||
ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
/* wl_lkm does not include PDA size in the PDA area.
|
||||
* We will pad the information into pda, so other routines
|
||||
* don't have to be modified */
|
||||
pda[0] = cpu_to_le16(pda_len - 2);
|
||||
/* Includes CFG_PROD_DATA but not itself */
|
||||
pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
|
||||
data_len = pda_len - 4;
|
||||
data = pda + 2;
|
||||
}
|
||||
|
||||
/* Open auxiliary port */
|
||||
ret = hermes_aux_control(hw, 1);
|
||||
pr_debug("AUX enable returned %d\n", ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read PDA */
|
||||
hermes_aux_setaddr(hw, pda_addr);
|
||||
hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);
|
||||
|
||||
/* Close aux port */
|
||||
ret = hermes_aux_control(hw, 0);
|
||||
pr_debug("AUX disable returned %d\n", ret);
|
||||
|
||||
/* Check PDA length */
|
||||
pda_size = le16_to_cpu(pda[0]);
|
||||
pr_debug("Actual PDA length %d, Max allowed %d\n",
|
||||
pda_size, pda_len);
|
||||
if (pda_size > pda_len)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hermes_lock_irqsave(spinlock_t *lock,
|
||||
unsigned long *flags) __acquires(lock)
|
||||
{
|
||||
spin_lock_irqsave(lock, *flags);
|
||||
}
|
||||
|
||||
static void hermes_unlock_irqrestore(spinlock_t *lock,
|
||||
unsigned long *flags) __releases(lock)
|
||||
{
|
||||
spin_unlock_irqrestore(lock, *flags);
|
||||
}
|
||||
|
||||
static void hermes_lock_irq(spinlock_t *lock) __acquires(lock)
|
||||
{
|
||||
spin_lock_irq(lock);
|
||||
}
|
||||
|
||||
static void hermes_unlock_irq(spinlock_t *lock) __releases(lock)
|
||||
{
|
||||
spin_unlock_irq(lock);
|
||||
}
|
||||
|
||||
/* Hermes operations for local buses */
|
||||
static const struct hermes_ops hermes_ops_local = {
|
||||
.init = hermes_init,
|
||||
.cmd_wait = hermes_docmd_wait,
|
||||
.init_cmd_wait = hermes_doicmd_wait,
|
||||
.allocate = hermes_allocate,
|
||||
.read_ltv = hermes_read_ltv,
|
||||
.write_ltv = hermes_write_ltv,
|
||||
.bap_pread = hermes_bap_pread,
|
||||
.bap_pwrite = hermes_bap_pwrite,
|
||||
.read_pda = hermes_read_pda,
|
||||
.program_init = hermesi_program_init,
|
||||
.program_end = hermesi_program_end,
|
||||
.program = hermes_program_bytes,
|
||||
.lock_irqsave = hermes_lock_irqsave,
|
||||
.unlock_irqrestore = hermes_unlock_irqrestore,
|
||||
.lock_irq = hermes_lock_irq,
|
||||
.unlock_irq = hermes_unlock_irq,
|
||||
};
|
520
drivers/net/wireless/intersil/orinoco/hermes.h
Normal file
520
drivers/net/wireless/intersil/orinoco/hermes.h
Normal file
@@ -0,0 +1,520 @@
|
||||
/* hermes.h
|
||||
*
|
||||
* Driver core for the "Hermes" wireless MAC controller, as used in
|
||||
* the Lucent Orinoco and Cabletron RoamAbout cards. It should also
|
||||
* work on the hfa3841 and hfa3842 MAC controller chips used in the
|
||||
* Prism I & II chipsets.
|
||||
*
|
||||
* This is not a complete driver, just low-level access routines for
|
||||
* the MAC controller itself.
|
||||
*
|
||||
* Based on the prism2 driver from Absolute Value Systems' linux-wlan
|
||||
* project, the Linux wvlan_cs driver, Lucent's HCF-Light
|
||||
* (wvlan_hcf.c) library, and the NetBSD wireless driver.
|
||||
*
|
||||
* Copyright (C) 2000, David Gibson, Linuxcare Australia.
|
||||
* (C) Copyright David Gibson, IBM Corp. 2001-2003.
|
||||
*
|
||||
* Portions taken from hfa384x.h.
|
||||
* Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
|
||||
*
|
||||
* This file distributed under the GPL, version 2.
|
||||
*/
|
||||
|
||||
#ifndef _HERMES_H
|
||||
#define _HERMES_H
|
||||
|
||||
/* Notes on locking:
|
||||
*
|
||||
* As a module of low level hardware access routines, there is no
|
||||
* locking. Users of this module should ensure that they serialize
|
||||
* access to the hermes structure, and to the hardware
|
||||
*/
|
||||
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
/*
|
||||
* Limits and constants
|
||||
*/
|
||||
#define HERMES_ALLOC_LEN_MIN (4)
|
||||
#define HERMES_ALLOC_LEN_MAX (2400)
|
||||
#define HERMES_LTV_LEN_MAX (34)
|
||||
#define HERMES_BAP_DATALEN_MAX (4096)
|
||||
#define HERMES_BAP_OFFSET_MAX (4096)
|
||||
#define HERMES_PORTID_MAX (7)
|
||||
#define HERMES_NUMPORTS_MAX (HERMES_PORTID_MAX + 1)
|
||||
#define HERMES_PDR_LEN_MAX (260) /* in bytes, from EK */
|
||||
#define HERMES_PDA_RECS_MAX (200) /* a guess */
|
||||
#define HERMES_PDA_LEN_MAX (1024) /* in bytes, from EK */
|
||||
#define HERMES_SCANRESULT_MAX (35)
|
||||
#define HERMES_CHINFORESULT_MAX (8)
|
||||
#define HERMES_MAX_MULTICAST (16)
|
||||
#define HERMES_MAGIC (0x7d1f)
|
||||
|
||||
/*
|
||||
* Hermes register offsets
|
||||
*/
|
||||
#define HERMES_CMD (0x00)
|
||||
#define HERMES_PARAM0 (0x02)
|
||||
#define HERMES_PARAM1 (0x04)
|
||||
#define HERMES_PARAM2 (0x06)
|
||||
#define HERMES_STATUS (0x08)
|
||||
#define HERMES_RESP0 (0x0A)
|
||||
#define HERMES_RESP1 (0x0C)
|
||||
#define HERMES_RESP2 (0x0E)
|
||||
#define HERMES_INFOFID (0x10)
|
||||
#define HERMES_RXFID (0x20)
|
||||
#define HERMES_ALLOCFID (0x22)
|
||||
#define HERMES_TXCOMPLFID (0x24)
|
||||
#define HERMES_SELECT0 (0x18)
|
||||
#define HERMES_OFFSET0 (0x1C)
|
||||
#define HERMES_DATA0 (0x36)
|
||||
#define HERMES_SELECT1 (0x1A)
|
||||
#define HERMES_OFFSET1 (0x1E)
|
||||
#define HERMES_DATA1 (0x38)
|
||||
#define HERMES_EVSTAT (0x30)
|
||||
#define HERMES_INTEN (0x32)
|
||||
#define HERMES_EVACK (0x34)
|
||||
#define HERMES_CONTROL (0x14)
|
||||
#define HERMES_SWSUPPORT0 (0x28)
|
||||
#define HERMES_SWSUPPORT1 (0x2A)
|
||||
#define HERMES_SWSUPPORT2 (0x2C)
|
||||
#define HERMES_AUXPAGE (0x3A)
|
||||
#define HERMES_AUXOFFSET (0x3C)
|
||||
#define HERMES_AUXDATA (0x3E)
|
||||
|
||||
/*
|
||||
* CMD register bitmasks
|
||||
*/
|
||||
#define HERMES_CMD_BUSY (0x8000)
|
||||
#define HERMES_CMD_AINFO (0x7f00)
|
||||
#define HERMES_CMD_MACPORT (0x0700)
|
||||
#define HERMES_CMD_RECL (0x0100)
|
||||
#define HERMES_CMD_WRITE (0x0100)
|
||||
#define HERMES_CMD_PROGMODE (0x0300)
|
||||
#define HERMES_CMD_CMDCODE (0x003f)
|
||||
|
||||
/*
|
||||
* STATUS register bitmasks
|
||||
*/
|
||||
#define HERMES_STATUS_RESULT (0x7f00)
|
||||
#define HERMES_STATUS_CMDCODE (0x003f)
|
||||
|
||||
/*
|
||||
* OFFSET register bitmasks
|
||||
*/
|
||||
#define HERMES_OFFSET_BUSY (0x8000)
|
||||
#define HERMES_OFFSET_ERR (0x4000)
|
||||
#define HERMES_OFFSET_DATAOFF (0x0ffe)
|
||||
|
||||
/*
|
||||
* Event register bitmasks (INTEN, EVSTAT, EVACK)
|
||||
*/
|
||||
#define HERMES_EV_TICK (0x8000)
|
||||
#define HERMES_EV_WTERR (0x4000)
|
||||
#define HERMES_EV_INFDROP (0x2000)
|
||||
#define HERMES_EV_INFO (0x0080)
|
||||
#define HERMES_EV_DTIM (0x0020)
|
||||
#define HERMES_EV_CMD (0x0010)
|
||||
#define HERMES_EV_ALLOC (0x0008)
|
||||
#define HERMES_EV_TXEXC (0x0004)
|
||||
#define HERMES_EV_TX (0x0002)
|
||||
#define HERMES_EV_RX (0x0001)
|
||||
|
||||
/*
|
||||
* Command codes
|
||||
*/
|
||||
/*--- Controller Commands ----------------------------*/
|
||||
#define HERMES_CMD_INIT (0x0000)
|
||||
#define HERMES_CMD_ENABLE (0x0001)
|
||||
#define HERMES_CMD_DISABLE (0x0002)
|
||||
#define HERMES_CMD_DIAG (0x0003)
|
||||
|
||||
/*--- Buffer Mgmt Commands ---------------------------*/
|
||||
#define HERMES_CMD_ALLOC (0x000A)
|
||||
#define HERMES_CMD_TX (0x000B)
|
||||
|
||||
/*--- Regulate Commands ------------------------------*/
|
||||
#define HERMES_CMD_NOTIFY (0x0010)
|
||||
#define HERMES_CMD_INQUIRE (0x0011)
|
||||
|
||||
/*--- Configure Commands -----------------------------*/
|
||||
#define HERMES_CMD_ACCESS (0x0021)
|
||||
#define HERMES_CMD_DOWNLD (0x0022)
|
||||
|
||||
/*--- Serial I/O Commands ----------------------------*/
|
||||
#define HERMES_CMD_READMIF (0x0030)
|
||||
#define HERMES_CMD_WRITEMIF (0x0031)
|
||||
|
||||
/*--- Debugging Commands -----------------------------*/
|
||||
#define HERMES_CMD_TEST (0x0038)
|
||||
|
||||
|
||||
/* Test command arguments */
|
||||
#define HERMES_TEST_SET_CHANNEL 0x0800
|
||||
#define HERMES_TEST_MONITOR 0x0b00
|
||||
#define HERMES_TEST_STOP 0x0f00
|
||||
|
||||
/* Authentication algorithms */
|
||||
#define HERMES_AUTH_OPEN 1
|
||||
#define HERMES_AUTH_SHARED_KEY 2
|
||||
|
||||
/* WEP settings */
|
||||
#define HERMES_WEP_PRIVACY_INVOKED 0x0001
|
||||
#define HERMES_WEP_EXCL_UNENCRYPTED 0x0002
|
||||
#define HERMES_WEP_HOST_ENCRYPT 0x0010
|
||||
#define HERMES_WEP_HOST_DECRYPT 0x0080
|
||||
|
||||
/* Symbol hostscan options */
|
||||
#define HERMES_HOSTSCAN_SYMBOL_5SEC 0x0001
|
||||
#define HERMES_HOSTSCAN_SYMBOL_ONCE 0x0002
|
||||
#define HERMES_HOSTSCAN_SYMBOL_PASSIVE 0x0040
|
||||
#define HERMES_HOSTSCAN_SYMBOL_BCAST 0x0080
|
||||
|
||||
/*
|
||||
* Frame structures and constants
|
||||
*/
|
||||
|
||||
#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)
|
||||
#define HERMES_TXCNTL2_OFFSET (HERMES_802_3_OFFSET - 2)
|
||||
|
||||
#define HERMES_RXSTAT_ERR (0x0003)
|
||||
#define HERMES_RXSTAT_BADCRC (0x0001)
|
||||
#define HERMES_RXSTAT_UNDECRYPTABLE (0x0002)
|
||||
#define HERMES_RXSTAT_MIC (0x0010) /* Frame contains MIC */
|
||||
#define HERMES_RXSTAT_MACPORT (0x0700)
|
||||
#define HERMES_RXSTAT_PCF (0x1000) /* Frame was received in CF period */
|
||||
#define HERMES_RXSTAT_MIC_KEY_ID (0x1800) /* MIC key used */
|
||||
#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 */
|
||||
|
||||
/* Shift amount for key ID in RXSTAT and TXCTRL */
|
||||
#define HERMES_MIC_KEY_ID_SHIFT 11
|
||||
|
||||
struct hermes_tx_descriptor {
|
||||
__le16 status;
|
||||
__le16 reserved1;
|
||||
__le16 reserved2;
|
||||
__le32 sw_support;
|
||||
u8 retry_count;
|
||||
u8 tx_rate;
|
||||
__le16 tx_control;
|
||||
} __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_MIC (0x0010) /* 802.3 + TKIP */
|
||||
#define HERMES_TXCTRL_MIC_KEY_ID (0x1800) /* MIC Key ID mask */
|
||||
#define HERMES_TXCTRL_ALT_RTRY (0x0020)
|
||||
|
||||
/* Inquiry constants and data types */
|
||||
|
||||
#define HERMES_INQ_TALLIES (0xF100)
|
||||
#define HERMES_INQ_SCAN (0xF101)
|
||||
#define HERMES_INQ_CHANNELINFO (0xF102)
|
||||
#define HERMES_INQ_HOSTSCAN (0xF103)
|
||||
#define HERMES_INQ_HOSTSCAN_SYMBOL (0xF104)
|
||||
#define HERMES_INQ_LINKSTATUS (0xF200)
|
||||
#define HERMES_INQ_SEC_STAT_AGERE (0xF202)
|
||||
|
||||
struct hermes_tallies_frame {
|
||||
__le16 TxUnicastFrames;
|
||||
__le16 TxMulticastFrames;
|
||||
__le16 TxFragments;
|
||||
__le16 TxUnicastOctets;
|
||||
__le16 TxMulticastOctets;
|
||||
__le16 TxDeferredTransmissions;
|
||||
__le16 TxSingleRetryFrames;
|
||||
__le16 TxMultipleRetryFrames;
|
||||
__le16 TxRetryLimitExceeded;
|
||||
__le16 TxDiscards;
|
||||
__le16 RxUnicastFrames;
|
||||
__le16 RxMulticastFrames;
|
||||
__le16 RxFragments;
|
||||
__le16 RxUnicastOctets;
|
||||
__le16 RxMulticastOctets;
|
||||
__le16 RxFCSErrors;
|
||||
__le16 RxDiscards_NoBuffer;
|
||||
__le16 TxDiscardsWrongSA;
|
||||
__le16 RxWEPUndecryptable;
|
||||
__le16 RxMsgInMsgFragments;
|
||||
__le16 RxMsgInBadMsgFragments;
|
||||
/* Those last are probably not available in very old firmwares */
|
||||
__le16 RxDiscards_WEPICVError;
|
||||
__le16 RxDiscards_WEPExcluded;
|
||||
} __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 prism2_scan_apinfo {
|
||||
__le16 channel; /* Channel where the AP sits */
|
||||
__le16 noise; /* Noise level */
|
||||
__le16 level; /* Signal level */
|
||||
u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */
|
||||
__le16 beacon_interv; /* Beacon interval */
|
||||
__le16 capabilities; /* Capabilities */
|
||||
__le16 essid_len; /* ESSID length */
|
||||
u8 essid[32]; /* ESSID of the network */
|
||||
u8 rates[10]; /* Bit rate supported */
|
||||
__le16 proberesp_rate; /* Data rate of the response frame */
|
||||
__le16 atim; /* ATIM window time, Kus (hostscan only) */
|
||||
} __packed;
|
||||
|
||||
/* Same stuff for the Lucent/Agere card.
|
||||
* Thanks to h1kari <h1kari AT dachb0den.com> - Jean II */
|
||||
struct agere_scan_apinfo {
|
||||
__le16 channel; /* Channel where the AP sits */
|
||||
__le16 noise; /* Noise level */
|
||||
__le16 level; /* Signal level */
|
||||
u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */
|
||||
__le16 beacon_interv; /* Beacon interval */
|
||||
__le16 capabilities; /* Capabilities */
|
||||
/* bits: 0-ess, 1-ibss, 4-privacy [wep] */
|
||||
__le16 essid_len; /* ESSID length */
|
||||
u8 essid[32]; /* ESSID of the network */
|
||||
} __packed;
|
||||
|
||||
/* Moustafa: Scan structure for Symbol cards */
|
||||
struct symbol_scan_apinfo {
|
||||
u8 channel; /* Channel where the AP sits */
|
||||
u8 unknown1; /* 8 in 2.9x and 3.9x f/w, 0 otherwise */
|
||||
__le16 noise; /* Noise level */
|
||||
__le16 level; /* Signal level */
|
||||
u8 bssid[ETH_ALEN]; /* MAC address of the Access Point */
|
||||
__le16 beacon_interv; /* Beacon interval */
|
||||
__le16 capabilities; /* Capabilities */
|
||||
/* bits: 0-ess, 1-ibss, 4-privacy [wep] */
|
||||
__le16 essid_len; /* ESSID length */
|
||||
u8 essid[32]; /* ESSID of the network */
|
||||
__le16 rates[5]; /* Bit rate supported */
|
||||
__le16 basic_rates; /* Basic rates bitmask */
|
||||
u8 unknown2[6]; /* Always FF:FF:FF:FF:00:00 */
|
||||
u8 unknown3[8]; /* Always 0, appeared in f/w 3.91-68 */
|
||||
} __packed;
|
||||
|
||||
union hermes_scan_info {
|
||||
struct agere_scan_apinfo a;
|
||||
struct prism2_scan_apinfo p;
|
||||
struct symbol_scan_apinfo s;
|
||||
};
|
||||
|
||||
/* Extended scan struct for HERMES_INQ_CHANNELINFO.
|
||||
* wl_lkm calls this an ACS scan (Automatic Channel Select).
|
||||
* Keep out of union hermes_scan_info because it is much bigger than
|
||||
* the older scan structures. */
|
||||
struct agere_ext_scan_info {
|
||||
__le16 reserved0;
|
||||
|
||||
u8 noise;
|
||||
u8 level;
|
||||
u8 rx_flow;
|
||||
u8 rate;
|
||||
__le16 reserved1[2];
|
||||
|
||||
__le16 frame_control;
|
||||
__le16 dur_id;
|
||||
u8 addr1[ETH_ALEN];
|
||||
u8 addr2[ETH_ALEN];
|
||||
u8 bssid[ETH_ALEN];
|
||||
__le16 sequence;
|
||||
u8 addr4[ETH_ALEN];
|
||||
|
||||
__le16 data_length;
|
||||
|
||||
/* Next 3 fields do not get filled in. */
|
||||
u8 daddr[ETH_ALEN];
|
||||
u8 saddr[ETH_ALEN];
|
||||
__le16 len_type;
|
||||
|
||||
__le64 timestamp;
|
||||
__le16 beacon_interval;
|
||||
__le16 capabilities;
|
||||
u8 data[0];
|
||||
} __packed;
|
||||
|
||||
#define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000)
|
||||
#define HERMES_LINKSTATUS_CONNECTED (0x0001)
|
||||
#define HERMES_LINKSTATUS_DISCONNECTED (0x0002)
|
||||
#define HERMES_LINKSTATUS_AP_CHANGE (0x0003)
|
||||
#define HERMES_LINKSTATUS_AP_OUT_OF_RANGE (0x0004)
|
||||
#define HERMES_LINKSTATUS_AP_IN_RANGE (0x0005)
|
||||
#define HERMES_LINKSTATUS_ASSOC_FAILED (0x0006)
|
||||
|
||||
struct hermes_linkstatus {
|
||||
__le16 linkstatus; /* Link status */
|
||||
} __packed;
|
||||
|
||||
struct hermes_response {
|
||||
u16 status, resp0, resp1, resp2;
|
||||
};
|
||||
|
||||
/* "ID" structure - used for ESSID and station nickname */
|
||||
struct hermes_idstring {
|
||||
__le16 len;
|
||||
__le16 val[16];
|
||||
} __packed;
|
||||
|
||||
struct hermes_multicast {
|
||||
u8 addr[HERMES_MAX_MULTICAST][ETH_ALEN];
|
||||
} __packed;
|
||||
|
||||
/* Timeouts */
|
||||
#define HERMES_BAP_BUSY_TIMEOUT (10000) /* In iterations of ~1us */
|
||||
|
||||
struct hermes;
|
||||
|
||||
/* Functions to access hardware */
|
||||
struct hermes_ops {
|
||||
int (*init)(struct hermes *hw);
|
||||
int (*cmd_wait)(struct hermes *hw, u16 cmd, u16 parm0,
|
||||
struct hermes_response *resp);
|
||||
int (*init_cmd_wait)(struct hermes *hw, u16 cmd,
|
||||
u16 parm0, u16 parm1, u16 parm2,
|
||||
struct hermes_response *resp);
|
||||
int (*allocate)(struct hermes *hw, u16 size, u16 *fid);
|
||||
int (*read_ltv)(struct hermes *hw, int bap, u16 rid, unsigned buflen,
|
||||
u16 *length, void *buf);
|
||||
int (*write_ltv)(struct hermes *hw, int bap, u16 rid,
|
||||
u16 length, const void *value);
|
||||
int (*bap_pread)(struct hermes *hw, int bap, void *buf, int len,
|
||||
u16 id, u16 offset);
|
||||
int (*bap_pwrite)(struct hermes *hw, int bap, const void *buf,
|
||||
int len, u16 id, u16 offset);
|
||||
int (*read_pda)(struct hermes *hw, __le16 *pda,
|
||||
u32 pda_addr, u16 pda_len);
|
||||
int (*program_init)(struct hermes *hw, u32 entry_point);
|
||||
int (*program_end)(struct hermes *hw);
|
||||
int (*program)(struct hermes *hw, const char *buf,
|
||||
u32 addr, u32 len);
|
||||
void (*lock_irqsave)(spinlock_t *lock, unsigned long *flags);
|
||||
void (*unlock_irqrestore)(spinlock_t *lock, unsigned long *flags);
|
||||
void (*lock_irq)(spinlock_t *lock);
|
||||
void (*unlock_irq)(spinlock_t *lock);
|
||||
};
|
||||
|
||||
/* Basic control structure */
|
||||
struct hermes {
|
||||
void __iomem *iobase;
|
||||
int reg_spacing;
|
||||
#define HERMES_16BIT_REGSPACING 0
|
||||
#define HERMES_32BIT_REGSPACING 1
|
||||
u16 inten; /* Which interrupts should be enabled? */
|
||||
bool eeprom_pda;
|
||||
const struct hermes_ops *ops;
|
||||
void *priv;
|
||||
};
|
||||
|
||||
/* Register access convenience macros */
|
||||
#define hermes_read_reg(hw, off) \
|
||||
(ioread16((hw)->iobase + ((off) << (hw)->reg_spacing)))
|
||||
#define hermes_write_reg(hw, off, val) \
|
||||
(iowrite16((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(struct hermes *hw, void __iomem *address,
|
||||
int reg_spacing);
|
||||
|
||||
/* Inline functions */
|
||||
|
||||
static inline int hermes_present(struct hermes *hw)
|
||||
{
|
||||
return hermes_read_regn(hw, SWSUPPORT0) == HERMES_MAGIC;
|
||||
}
|
||||
|
||||
static inline void hermes_set_irqmask(struct hermes *hw, u16 events)
|
||||
{
|
||||
hw->inten = events;
|
||||
hermes_write_regn(hw, INTEN, events);
|
||||
}
|
||||
|
||||
static inline int hermes_enable_port(struct hermes *hw, int port)
|
||||
{
|
||||
return hw->ops->cmd_wait(hw, HERMES_CMD_ENABLE | (port << 8),
|
||||
0, NULL);
|
||||
}
|
||||
|
||||
static inline int hermes_disable_port(struct hermes *hw, int port)
|
||||
{
|
||||
return hw->ops->cmd_wait(hw, HERMES_CMD_DISABLE | (port << 8),
|
||||
0, NULL);
|
||||
}
|
||||
|
||||
/* Initiate an INQUIRE command (tallies or scan). The result will come as an
|
||||
* information frame in __orinoco_ev_info() */
|
||||
static inline int hermes_inquire(struct hermes *hw, u16 rid)
|
||||
{
|
||||
return hw->ops->cmd_wait(hw, HERMES_CMD_INQUIRE, rid, NULL);
|
||||
}
|
||||
|
||||
#define HERMES_BYTES_TO_RECLEN(n) ((((n) + 1) / 2) + 1)
|
||||
#define HERMES_RECLEN_TO_BYTES(n) (((n) - 1) * 2)
|
||||
|
||||
/* Note that for the next two, the count is in 16-bit words, not bytes */
|
||||
static inline void hermes_read_words(struct hermes *hw, int off,
|
||||
void *buf, unsigned count)
|
||||
{
|
||||
off = off << hw->reg_spacing;
|
||||
ioread16_rep(hw->iobase + off, buf, count);
|
||||
}
|
||||
|
||||
static inline void hermes_write_bytes(struct hermes *hw, int off,
|
||||
const char *buf, unsigned count)
|
||||
{
|
||||
off = off << hw->reg_spacing;
|
||||
iowrite16_rep(hw->iobase + off, buf, count >> 1);
|
||||
if (unlikely(count & 1))
|
||||
iowrite8(buf[count - 1], hw->iobase + off);
|
||||
}
|
||||
|
||||
static inline void hermes_clear_words(struct hermes *hw, int off,
|
||||
unsigned count)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
off = off << hw->reg_spacing;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
iowrite16(0, hw->iobase + off);
|
||||
}
|
||||
|
||||
#define HERMES_READ_RECORD(hw, bap, rid, buf) \
|
||||
(hw->ops->read_ltv((hw), (bap), (rid), sizeof(*buf), NULL, (buf)))
|
||||
#define HERMES_WRITE_RECORD(hw, bap, rid, buf) \
|
||||
(hw->ops->write_ltv((hw), (bap), (rid), \
|
||||
HERMES_BYTES_TO_RECLEN(sizeof(*buf)), (buf)))
|
||||
|
||||
static inline int hermes_read_wordrec(struct hermes *hw, int bap, u16 rid,
|
||||
u16 *word)
|
||||
{
|
||||
__le16 rec;
|
||||
int err;
|
||||
|
||||
err = HERMES_READ_RECORD(hw, bap, rid, &rec);
|
||||
*word = le16_to_cpu(rec);
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int hermes_write_wordrec(struct hermes *hw, int bap, u16 rid,
|
||||
u16 word)
|
||||
{
|
||||
__le16 rec = cpu_to_le16(word);
|
||||
return HERMES_WRITE_RECORD(hw, bap, rid, &rec);
|
||||
}
|
||||
|
||||
#endif /* _HERMES_H */
|
477
drivers/net/wireless/intersil/orinoco/hermes_dld.c
Normal file
477
drivers/net/wireless/intersil/orinoco/hermes_dld.c
Normal file
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Hermes download helper.
|
||||
*
|
||||
* This helper:
|
||||
* - is capable of writing to the volatile area of the hermes device
|
||||
* - is currently not capable of writing to non-volatile areas
|
||||
* - provide helpers to identify and update plugin data
|
||||
* - is not capable of interpreting a fw image directly. That is up to
|
||||
* the main card driver.
|
||||
* - deals with Hermes I devices. It can probably be modified to deal
|
||||
* with Hermes II devices
|
||||
*
|
||||
* Copyright (C) 2007, David Kilroy
|
||||
*
|
||||
* Plug data code slightly modified from spectrum_cs driver
|
||||
* Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
|
||||
* Portions based on information in wl_lkm_718 Agere driver
|
||||
* COPYRIGHT (C) 2001-2004 by Agere Systems Inc. All Rights Reserved
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include "hermes.h"
|
||||
#include "hermes_dld.h"
|
||||
|
||||
#define PFX "hermes_dld: "
|
||||
|
||||
/* End markers used in dblocks */
|
||||
#define PDI_END 0x00000000 /* End of PDA */
|
||||
#define BLOCK_END 0xFFFFFFFF /* Last image block */
|
||||
#define TEXT_END 0x1A /* End of text header */
|
||||
|
||||
/*
|
||||
* The following structures have little-endian fields denoted by
|
||||
* the leading underscore. Don't access them directly - use inline
|
||||
* functions defined below.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The binary image to be downloaded consists of series of data blocks.
|
||||
* Each block has the following structure.
|
||||
*/
|
||||
struct dblock {
|
||||
__le32 addr; /* adapter address where to write the block */
|
||||
__le16 len; /* length of the data only, in bytes */
|
||||
char data[0]; /* data to be written */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Plug Data References are located in the image after the last data
|
||||
* block. They refer to areas in the adapter memory where the plug data
|
||||
* items with matching ID should be written.
|
||||
*/
|
||||
struct pdr {
|
||||
__le32 id; /* record ID */
|
||||
__le32 addr; /* adapter address where to write the data */
|
||||
__le32 len; /* expected length of the data, in bytes */
|
||||
char next[0]; /* next PDR starts here */
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Plug Data Items are located in the EEPROM read from the adapter by
|
||||
* primary firmware. They refer to the device-specific data that should
|
||||
* be plugged into the secondary firmware.
|
||||
*/
|
||||
struct pdi {
|
||||
__le16 len; /* length of ID and data, in words */
|
||||
__le16 id; /* record ID */
|
||||
char data[0]; /* plug data */
|
||||
} __packed;
|
||||
|
||||
/*** FW data block access functions ***/
|
||||
|
||||
static inline u32
|
||||
dblock_addr(const struct dblock *blk)
|
||||
{
|
||||
return le32_to_cpu(blk->addr);
|
||||
}
|
||||
|
||||
static inline u32
|
||||
dblock_len(const struct dblock *blk)
|
||||
{
|
||||
return le16_to_cpu(blk->len);
|
||||
}
|
||||
|
||||
/*** PDR Access functions ***/
|
||||
|
||||
static inline u32
|
||||
pdr_id(const struct pdr *pdr)
|
||||
{
|
||||
return le32_to_cpu(pdr->id);
|
||||
}
|
||||
|
||||
static inline u32
|
||||
pdr_addr(const struct pdr *pdr)
|
||||
{
|
||||
return le32_to_cpu(pdr->addr);
|
||||
}
|
||||
|
||||
static inline u32
|
||||
pdr_len(const struct pdr *pdr)
|
||||
{
|
||||
return le32_to_cpu(pdr->len);
|
||||
}
|
||||
|
||||
/*** PDI Access functions ***/
|
||||
|
||||
static inline u32
|
||||
pdi_id(const struct pdi *pdi)
|
||||
{
|
||||
return le16_to_cpu(pdi->id);
|
||||
}
|
||||
|
||||
/* Return length of the data only, in bytes */
|
||||
static inline u32
|
||||
pdi_len(const struct pdi *pdi)
|
||||
{
|
||||
return 2 * (le16_to_cpu(pdi->len) - 1);
|
||||
}
|
||||
|
||||
/*** Plug Data Functions ***/
|
||||
|
||||
/*
|
||||
* Scan PDR for the record with the specified RECORD_ID.
|
||||
* If it's not found, return NULL.
|
||||
*/
|
||||
static const struct pdr *
|
||||
hermes_find_pdr(const struct pdr *first_pdr, u32 record_id, const void *end)
|
||||
{
|
||||
const struct pdr *pdr = first_pdr;
|
||||
|
||||
end -= sizeof(struct pdr);
|
||||
|
||||
while (((void *) pdr <= end) &&
|
||||
(pdr_id(pdr) != PDI_END)) {
|
||||
/*
|
||||
* PDR area is currently not terminated by PDI_END.
|
||||
* It's followed by CRC records, which have the type
|
||||
* field where PDR has length. The type can be 0 or 1.
|
||||
*/
|
||||
if (pdr_len(pdr) < 2)
|
||||
return NULL;
|
||||
|
||||
/* If the record ID matches, we are done */
|
||||
if (pdr_id(pdr) == record_id)
|
||||
return pdr;
|
||||
|
||||
pdr = (struct pdr *) pdr->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Scan production data items for a particular entry */
|
||||
static const struct pdi *
|
||||
hermes_find_pdi(const struct pdi *first_pdi, u32 record_id, const void *end)
|
||||
{
|
||||
const struct pdi *pdi = first_pdi;
|
||||
|
||||
end -= sizeof(struct pdi);
|
||||
|
||||
while (((void *) pdi <= end) &&
|
||||
(pdi_id(pdi) != PDI_END)) {
|
||||
|
||||
/* If the record ID matches, we are done */
|
||||
if (pdi_id(pdi) == record_id)
|
||||
return pdi;
|
||||
|
||||
pdi = (struct pdi *) &pdi->data[pdi_len(pdi)];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Process one Plug Data Item - find corresponding PDR and plug it */
|
||||
static int
|
||||
hermes_plug_pdi(struct hermes *hw, const struct pdr *first_pdr,
|
||||
const struct pdi *pdi, const void *pdr_end)
|
||||
{
|
||||
const struct pdr *pdr;
|
||||
|
||||
/* Find the PDR corresponding to this PDI */
|
||||
pdr = hermes_find_pdr(first_pdr, pdi_id(pdi), pdr_end);
|
||||
|
||||
/* No match is found, safe to ignore */
|
||||
if (!pdr)
|
||||
return 0;
|
||||
|
||||
/* Lengths of the data in PDI and PDR must match */
|
||||
if (pdi_len(pdi) != pdr_len(pdr))
|
||||
return -EINVAL;
|
||||
|
||||
/* do the actual plugging */
|
||||
hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse PDA and write the records into the adapter
|
||||
*
|
||||
* Attempt to write every records that is in the specified pda
|
||||
* which also has a valid production data record for the firmware.
|
||||
*/
|
||||
int hermes_apply_pda(struct hermes *hw,
|
||||
const char *first_pdr,
|
||||
const void *pdr_end,
|
||||
const __le16 *pda,
|
||||
const void *pda_end)
|
||||
{
|
||||
int ret;
|
||||
const struct pdi *pdi;
|
||||
const struct pdr *pdr;
|
||||
|
||||
pdr = (const struct pdr *) first_pdr;
|
||||
pda_end -= sizeof(struct pdi);
|
||||
|
||||
/* Go through every PDI and plug them into the adapter */
|
||||
pdi = (const struct pdi *) (pda + 2);
|
||||
while (((void *) pdi <= pda_end) &&
|
||||
(pdi_id(pdi) != PDI_END)) {
|
||||
ret = hermes_plug_pdi(hw, pdr, pdi, pdr_end);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Increment to the next PDI */
|
||||
pdi = (const struct pdi *) &pdi->data[pdi_len(pdi)];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Identify the total number of bytes in all blocks
|
||||
* including the header data.
|
||||
*/
|
||||
size_t
|
||||
hermes_blocks_length(const char *first_block, const void *end)
|
||||
{
|
||||
const struct dblock *blk = (const struct dblock *) first_block;
|
||||
int total_len = 0;
|
||||
int len;
|
||||
|
||||
end -= sizeof(*blk);
|
||||
|
||||
/* Skip all blocks to locate Plug Data References
|
||||
* (Spectrum CS) */
|
||||
while (((void *) blk <= end) &&
|
||||
(dblock_addr(blk) != BLOCK_END)) {
|
||||
len = dblock_len(blk);
|
||||
total_len += sizeof(*blk) + len;
|
||||
blk = (struct dblock *) &blk->data[len];
|
||||
}
|
||||
|
||||
return total_len;
|
||||
}
|
||||
|
||||
/*** Hermes programming ***/
|
||||
|
||||
/* Program the data blocks */
|
||||
int hermes_program(struct hermes *hw, const char *first_block, const void *end)
|
||||
{
|
||||
const struct dblock *blk;
|
||||
u32 blkaddr;
|
||||
u32 blklen;
|
||||
int err = 0;
|
||||
|
||||
blk = (const struct dblock *) first_block;
|
||||
|
||||
if ((void *) blk > (end - sizeof(*blk)))
|
||||
return -EIO;
|
||||
|
||||
blkaddr = dblock_addr(blk);
|
||||
blklen = dblock_len(blk);
|
||||
|
||||
while ((blkaddr != BLOCK_END) &&
|
||||
(((void *) blk + blklen) <= end)) {
|
||||
pr_debug(PFX "Programming block of length %d "
|
||||
"to address 0x%08x\n", blklen, blkaddr);
|
||||
|
||||
err = hw->ops->program(hw, blk->data, blkaddr, blklen);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
blk = (const struct dblock *) &blk->data[blklen];
|
||||
|
||||
if ((void *) blk > (end - sizeof(*blk)))
|
||||
return -EIO;
|
||||
|
||||
blkaddr = dblock_addr(blk);
|
||||
blklen = dblock_len(blk);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/*** Default plugging data for Hermes I ***/
|
||||
/* Values from wl_lkm_718/hcf/dhf.c */
|
||||
|
||||
#define DEFINE_DEFAULT_PDR(pid, length, data) \
|
||||
static const struct { \
|
||||
__le16 len; \
|
||||
__le16 id; \
|
||||
u8 val[length]; \
|
||||
} __packed default_pdr_data_##pid = { \
|
||||
cpu_to_le16((sizeof(default_pdr_data_##pid)/ \
|
||||
sizeof(__le16)) - 1), \
|
||||
cpu_to_le16(pid), \
|
||||
data \
|
||||
}
|
||||
|
||||
#define DEFAULT_PDR(pid) default_pdr_data_##pid
|
||||
|
||||
/* HWIF Compatibility */
|
||||
DEFINE_DEFAULT_PDR(0x0005, 10, "\x00\x00\x06\x00\x01\x00\x01\x00\x01\x00");
|
||||
|
||||
/* PPPPSign */
|
||||
DEFINE_DEFAULT_PDR(0x0108, 4, "\x00\x00\x00\x00");
|
||||
|
||||
/* PPPPProf */
|
||||
DEFINE_DEFAULT_PDR(0x0109, 10, "\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00");
|
||||
|
||||
/* Antenna diversity */
|
||||
DEFINE_DEFAULT_PDR(0x0150, 2, "\x00\x3F");
|
||||
|
||||
/* Modem VCO band Set-up */
|
||||
DEFINE_DEFAULT_PDR(0x0160, 28,
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00");
|
||||
|
||||
/* Modem Rx Gain Table Values */
|
||||
DEFINE_DEFAULT_PDR(0x0161, 256,
|
||||
"\x3F\x01\x3F\01\x3F\x01\x3F\x01"
|
||||
"\x3F\x01\x3F\01\x3F\x01\x3F\x01"
|
||||
"\x3F\x01\x3F\01\x3F\x01\x3F\x01"
|
||||
"\x3F\x01\x3F\01\x3F\x01\x3F\x01"
|
||||
"\x3F\x01\x3E\01\x3E\x01\x3D\x01"
|
||||
"\x3D\x01\x3C\01\x3C\x01\x3B\x01"
|
||||
"\x3B\x01\x3A\01\x3A\x01\x39\x01"
|
||||
"\x39\x01\x38\01\x38\x01\x37\x01"
|
||||
"\x37\x01\x36\01\x36\x01\x35\x01"
|
||||
"\x35\x01\x34\01\x34\x01\x33\x01"
|
||||
"\x33\x01\x32\x01\x32\x01\x31\x01"
|
||||
"\x31\x01\x30\x01\x30\x01\x7B\x01"
|
||||
"\x7B\x01\x7A\x01\x7A\x01\x79\x01"
|
||||
"\x79\x01\x78\x01\x78\x01\x77\x01"
|
||||
"\x77\x01\x76\x01\x76\x01\x75\x01"
|
||||
"\x75\x01\x74\x01\x74\x01\x73\x01"
|
||||
"\x73\x01\x72\x01\x72\x01\x71\x01"
|
||||
"\x71\x01\x70\x01\x70\x01\x68\x01"
|
||||
"\x68\x01\x67\x01\x67\x01\x66\x01"
|
||||
"\x66\x01\x65\x01\x65\x01\x57\x01"
|
||||
"\x57\x01\x56\x01\x56\x01\x55\x01"
|
||||
"\x55\x01\x54\x01\x54\x01\x53\x01"
|
||||
"\x53\x01\x52\x01\x52\x01\x51\x01"
|
||||
"\x51\x01\x50\x01\x50\x01\x48\x01"
|
||||
"\x48\x01\x47\x01\x47\x01\x46\x01"
|
||||
"\x46\x01\x45\x01\x45\x01\x44\x01"
|
||||
"\x44\x01\x43\x01\x43\x01\x42\x01"
|
||||
"\x42\x01\x41\x01\x41\x01\x40\x01"
|
||||
"\x40\x01\x40\x01\x40\x01\x40\x01"
|
||||
"\x40\x01\x40\x01\x40\x01\x40\x01"
|
||||
"\x40\x01\x40\x01\x40\x01\x40\x01"
|
||||
"\x40\x01\x40\x01\x40\x01\x40\x01");
|
||||
|
||||
/* Write PDA according to certain rules.
|
||||
*
|
||||
* For every production data record, look for a previous setting in
|
||||
* the pda, and use that.
|
||||
*
|
||||
* For certain records, use defaults if they are not found in pda.
|
||||
*/
|
||||
int hermes_apply_pda_with_defaults(struct hermes *hw,
|
||||
const char *first_pdr,
|
||||
const void *pdr_end,
|
||||
const __le16 *pda,
|
||||
const void *pda_end)
|
||||
{
|
||||
const struct pdr *pdr = (const struct pdr *) first_pdr;
|
||||
const struct pdi *first_pdi = (const struct pdi *) &pda[2];
|
||||
const struct pdi *pdi;
|
||||
const struct pdi *default_pdi = NULL;
|
||||
const struct pdi *outdoor_pdi;
|
||||
int record_id;
|
||||
|
||||
pdr_end -= sizeof(struct pdr);
|
||||
|
||||
while (((void *) pdr <= pdr_end) &&
|
||||
(pdr_id(pdr) != PDI_END)) {
|
||||
/*
|
||||
* For spectrum_cs firmwares,
|
||||
* PDR area is currently not terminated by PDI_END.
|
||||
* It's followed by CRC records, which have the type
|
||||
* field where PDR has length. The type can be 0 or 1.
|
||||
*/
|
||||
if (pdr_len(pdr) < 2)
|
||||
break;
|
||||
record_id = pdr_id(pdr);
|
||||
|
||||
pdi = hermes_find_pdi(first_pdi, record_id, pda_end);
|
||||
if (pdi)
|
||||
pr_debug(PFX "Found record 0x%04x at %p\n",
|
||||
record_id, pdi);
|
||||
|
||||
switch (record_id) {
|
||||
case 0x110: /* Modem REFDAC values */
|
||||
case 0x120: /* Modem VGDAC values */
|
||||
outdoor_pdi = hermes_find_pdi(first_pdi, record_id + 1,
|
||||
pda_end);
|
||||
default_pdi = NULL;
|
||||
if (outdoor_pdi) {
|
||||
pdi = outdoor_pdi;
|
||||
pr_debug(PFX
|
||||
"Using outdoor record 0x%04x at %p\n",
|
||||
record_id + 1, pdi);
|
||||
}
|
||||
break;
|
||||
case 0x5: /* HWIF Compatibility */
|
||||
default_pdi = (struct pdi *) &DEFAULT_PDR(0x0005);
|
||||
break;
|
||||
case 0x108: /* PPPPSign */
|
||||
default_pdi = (struct pdi *) &DEFAULT_PDR(0x0108);
|
||||
break;
|
||||
case 0x109: /* PPPPProf */
|
||||
default_pdi = (struct pdi *) &DEFAULT_PDR(0x0109);
|
||||
break;
|
||||
case 0x150: /* Antenna diversity */
|
||||
default_pdi = (struct pdi *) &DEFAULT_PDR(0x0150);
|
||||
break;
|
||||
case 0x160: /* Modem VCO band Set-up */
|
||||
default_pdi = (struct pdi *) &DEFAULT_PDR(0x0160);
|
||||
break;
|
||||
case 0x161: /* Modem Rx Gain Table Values */
|
||||
default_pdi = (struct pdi *) &DEFAULT_PDR(0x0161);
|
||||
break;
|
||||
default:
|
||||
default_pdi = NULL;
|
||||
break;
|
||||
}
|
||||
if (!pdi && default_pdi) {
|
||||
/* Use default */
|
||||
pdi = default_pdi;
|
||||
pr_debug(PFX "Using default record 0x%04x at %p\n",
|
||||
record_id, pdi);
|
||||
}
|
||||
|
||||
if (pdi) {
|
||||
/* Lengths of the data in PDI and PDR must match */
|
||||
if ((pdi_len(pdi) == pdr_len(pdr)) &&
|
||||
((void *) pdi->data + pdi_len(pdi) < pda_end)) {
|
||||
/* do the actual plugging */
|
||||
hw->ops->program(hw, pdi->data, pdr_addr(pdr),
|
||||
pdi_len(pdi));
|
||||
}
|
||||
}
|
||||
|
||||
pdr++;
|
||||
}
|
||||
return 0;
|
||||
}
|
52
drivers/net/wireless/intersil/orinoco/hermes_dld.h
Normal file
52
drivers/net/wireless/intersil/orinoco/hermes_dld.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2007, David Kilroy
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef _HERMES_DLD_H
|
||||
#define _HERMES_DLD_H
|
||||
|
||||
#include "hermes.h"
|
||||
|
||||
int hermesi_program_init(struct hermes *hw, u32 offset);
|
||||
int hermesi_program_end(struct hermes *hw);
|
||||
int hermes_program(struct hermes *hw, const char *first_block, const void *end);
|
||||
|
||||
int hermes_read_pda(struct hermes *hw,
|
||||
__le16 *pda,
|
||||
u32 pda_addr,
|
||||
u16 pda_len,
|
||||
int use_eeprom);
|
||||
int hermes_apply_pda(struct hermes *hw,
|
||||
const char *first_pdr,
|
||||
const void *pdr_end,
|
||||
const __le16 *pda,
|
||||
const void *pda_end);
|
||||
int hermes_apply_pda_with_defaults(struct hermes *hw,
|
||||
const char *first_pdr,
|
||||
const void *pdr_end,
|
||||
const __le16 *pda,
|
||||
const void *pda_end);
|
||||
|
||||
size_t hermes_blocks_length(const char *first_block, const void *end);
|
||||
|
||||
#endif /* _HERMES_DLD_H */
|
165
drivers/net/wireless/intersil/orinoco/hermes_rid.h
Normal file
165
drivers/net/wireless/intersil/orinoco/hermes_rid.h
Normal file
@@ -0,0 +1,165 @@
|
||||
#ifndef _HERMES_RID_H
|
||||
#define _HERMES_RID_H
|
||||
|
||||
/*
|
||||
* Configuration RIDs
|
||||
*/
|
||||
#define HERMES_RID_CNFPORTTYPE 0xFC00
|
||||
#define HERMES_RID_CNFOWNMACADDR 0xFC01
|
||||
#define HERMES_RID_CNFDESIREDSSID 0xFC02
|
||||
#define HERMES_RID_CNFOWNCHANNEL 0xFC03
|
||||
#define HERMES_RID_CNFOWNSSID 0xFC04
|
||||
#define HERMES_RID_CNFOWNATIMWINDOW 0xFC05
|
||||
#define HERMES_RID_CNFSYSTEMSCALE 0xFC06
|
||||
#define HERMES_RID_CNFMAXDATALEN 0xFC07
|
||||
#define HERMES_RID_CNFWDSADDRESS 0xFC08
|
||||
#define HERMES_RID_CNFPMENABLED 0xFC09
|
||||
#define HERMES_RID_CNFPMEPS 0xFC0A
|
||||
#define HERMES_RID_CNFMULTICASTRECEIVE 0xFC0B
|
||||
#define HERMES_RID_CNFMAXSLEEPDURATION 0xFC0C
|
||||
#define HERMES_RID_CNFPMHOLDOVERDURATION 0xFC0D
|
||||
#define HERMES_RID_CNFOWNNAME 0xFC0E
|
||||
#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
|
||||
#define HERMES_RID_CNFAUTHENTICATION_AGERE 0xFC21
|
||||
#define HERMES_RID_CNFMANDATORYBSSID_SYMBOL 0xFC21
|
||||
#define HERMES_RID_CNFDROPUNENCRYPTED 0xFC22
|
||||
#define HERMES_RID_CNFWEPDEFAULTKEYID 0xFC23
|
||||
#define HERMES_RID_CNFDEFAULTKEY0 0xFC24
|
||||
#define HERMES_RID_CNFDEFAULTKEY1 0xFC25
|
||||
#define HERMES_RID_CNFMWOROBUST_AGERE 0xFC25
|
||||
#define HERMES_RID_CNFDEFAULTKEY2 0xFC26
|
||||
#define HERMES_RID_CNFDEFAULTKEY3 0xFC27
|
||||
#define HERMES_RID_CNFWEPFLAGS_INTERSIL 0xFC28
|
||||
#define HERMES_RID_CNFWEPKEYMAPPINGTABLE 0xFC29
|
||||
#define HERMES_RID_CNFAUTHENTICATION 0xFC2A
|
||||
#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
|
||||
#define HERMES_RID_CNFCREATEIBSS 0xFC81
|
||||
#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD 0xFC82
|
||||
#define HERMES_RID_CNFRTSTHRESHOLD 0xFC83
|
||||
#define HERMES_RID_CNFTXRATECONTROL 0xFC84
|
||||
#define HERMES_RID_CNFPROMISCUOUSMODE 0xFC85
|
||||
#define HERMES_RID_CNFBASICRATES_SYMBOL 0xFC8A
|
||||
#define HERMES_RID_CNFPREAMBLE_SYMBOL 0xFC8C
|
||||
#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_CNFHOSTSCAN_SYMBOL 0xFCAB
|
||||
#define HERMES_RID_CNFSHORTPREAMBLE 0xFCB0
|
||||
#define HERMES_RID_CNFWEPKEYS_AGERE 0xFCB0
|
||||
#define HERMES_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1
|
||||
#define HERMES_RID_CNFTXKEY_AGERE 0xFCB1
|
||||
#define HERMES_RID_CNFAUTHENTICATIONRSPTO 0xFCB2
|
||||
#define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2
|
||||
#define HERMES_RID_CNFBASICRATES 0xFCB3
|
||||
#define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4
|
||||
#define HERMES_RID_CNFADDDEFAULTTKIPKEY_AGERE 0xFCB4
|
||||
#define HERMES_RID_CNFSETWPAAUTHMGMTSUITE_AGERE 0xFCB5
|
||||
#define HERMES_RID_CNFREMDEFAULTTKIPKEY_AGERE 0xFCB6
|
||||
#define HERMES_RID_CNFADDMAPPEDTKIPKEY_AGERE 0xFCB7
|
||||
#define HERMES_RID_CNFREMMAPPEDTKIPKEY_AGERE 0xFCB8
|
||||
#define HERMES_RID_CNFSETWPACAPABILITIES_AGERE 0xFCB9
|
||||
#define HERMES_RID_CNFCACHEDPMKADDRESS 0xFCBA
|
||||
#define HERMES_RID_CNFREMOVEPMKADDRESS 0xFCBB
|
||||
#define HERMES_RID_CNFSCANCHANNELS2GHZ 0xFCC2
|
||||
#define HERMES_RID_CNFDISASSOCIATE 0xFCC8
|
||||
#define HERMES_RID_CNFTICKTIME 0xFCE0
|
||||
#define HERMES_RID_CNFSCANREQUEST 0xFCE1
|
||||
#define HERMES_RID_CNFJOINREQUEST 0xFCE2
|
||||
#define HERMES_RID_CNFAUTHENTICATESTATION 0xFCE3
|
||||
#define HERMES_RID_CNFCHANNELINFOREQUEST 0xFCE4
|
||||
#define HERMES_RID_CNFHOSTSCAN 0xFCE5
|
||||
|
||||
/*
|
||||
* 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
|
||||
#define HERMES_RID_REGULATORYDOMAINS 0xFD11
|
||||
#define HERMES_RID_TEMPTYPE 0xFD12
|
||||
#define HERMES_RID_CIS 0xFD13
|
||||
#define HERMES_RID_STAID 0xFD20
|
||||
#define HERMES_RID_STASUPRANGE 0xFD21
|
||||
#define HERMES_RID_MFIACTRANGES 0xFD22
|
||||
#define HERMES_RID_CFIACTRANGES2 0xFD23
|
||||
#define HERMES_RID_SECONDARYVERSION_SYMBOL 0xFD24
|
||||
#define HERMES_RID_PORTSTATUS 0xFD40
|
||||
#define HERMES_RID_CURRENTSSID 0xFD41
|
||||
#define HERMES_RID_CURRENTBSSID 0xFD42
|
||||
#define HERMES_RID_COMMSQUALITY 0xFD43
|
||||
#define HERMES_RID_CURRENTTXRATE 0xFD44
|
||||
#define HERMES_RID_CURRENTBEACONINTERVAL 0xFD45
|
||||
#define HERMES_RID_CURRENTSCALETHRESHOLDS 0xFD46
|
||||
#define HERMES_RID_PROTOCOLRSPTIME 0xFD47
|
||||
#define HERMES_RID_SHORTRETRYLIMIT 0xFD48
|
||||
#define HERMES_RID_LONGRETRYLIMIT 0xFD49
|
||||
#define HERMES_RID_MAXTRANSMITLIFETIME 0xFD4A
|
||||
#define HERMES_RID_MAXRECEIVELIFETIME 0xFD4B
|
||||
#define HERMES_RID_CFPOLLABLE 0xFD4C
|
||||
#define HERMES_RID_AUTHENTICATIONALGORITHMS 0xFD4D
|
||||
#define HERMES_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F
|
||||
#define HERMES_RID_DBMCOMMSQUALITY_INTERSIL 0xFD51
|
||||
#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_CURRENT_COUNTRY_INFO 0xFD89
|
||||
#define HERMES_RID_CURRENT_WPA_IE 0xFD8A
|
||||
#define HERMES_RID_CURRENT_TKIP_IV 0xFD8B
|
||||
#define HERMES_RID_CURRENT_ASSOC_REQ_INFO 0xFD8C
|
||||
#define HERMES_RID_CURRENT_ASSOC_RESP_INFO 0xFD8D
|
||||
#define HERMES_RID_TXQUEUEEMPTY 0xFD91
|
||||
#define HERMES_RID_PHYTYPE 0xFDC0
|
||||
#define HERMES_RID_CURRENTCHANNEL 0xFDC1
|
||||
#define HERMES_RID_CURRENTPOWERSTATE 0xFDC2
|
||||
#define HERMES_RID_CCAMODE 0xFDC3
|
||||
#define HERMES_RID_SUPPORTEDDATARATES 0xFDC6
|
||||
#define HERMES_RID_BUILDSEQ 0xFFFE
|
||||
#define HERMES_RID_FWID 0xFFFF
|
||||
|
||||
#endif
|
1356
drivers/net/wireless/intersil/orinoco/hw.c
Normal file
1356
drivers/net/wireless/intersil/orinoco/hw.c
Normal file
File diff suppressed because it is too large
Load Diff
59
drivers/net/wireless/intersil/orinoco/hw.h
Normal file
59
drivers/net/wireless/intersil/orinoco/hw.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* Encapsulate basic setting changes on Hermes hardware
|
||||
*
|
||||
* See copyright notice in main.c
|
||||
*/
|
||||
#ifndef _ORINOCO_HW_H_
|
||||
#define _ORINOCO_HW_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <net/cfg80211.h>
|
||||
|
||||
/* Hardware BAPs */
|
||||
#define USER_BAP 0
|
||||
#define IRQ_BAP 1
|
||||
|
||||
/* WEP key sizes */
|
||||
#define SMALL_KEY_SIZE 5
|
||||
#define LARGE_KEY_SIZE 13
|
||||
|
||||
/* Number of supported channels */
|
||||
#define NUM_CHANNELS 14
|
||||
|
||||
/* Forward declarations */
|
||||
struct orinoco_private;
|
||||
|
||||
int determine_fw_capabilities(struct orinoco_private *priv, char *fw_name,
|
||||
size_t fw_name_len, u32 *hw_ver);
|
||||
int orinoco_hw_read_card_settings(struct orinoco_private *priv, u8 *dev_addr);
|
||||
int orinoco_hw_allocate_fid(struct orinoco_private *priv);
|
||||
int orinoco_get_bitratemode(int bitrate, int automatic);
|
||||
void orinoco_get_ratemode_cfg(int ratemode, int *bitrate, int *automatic);
|
||||
|
||||
int orinoco_hw_program_rids(struct orinoco_private *priv);
|
||||
int orinoco_hw_get_tkip_iv(struct orinoco_private *priv, int key, u8 *tsc);
|
||||
int __orinoco_hw_set_bitrate(struct orinoco_private *priv);
|
||||
int orinoco_hw_get_act_bitrate(struct orinoco_private *priv, int *bitrate);
|
||||
int __orinoco_hw_set_wap(struct orinoco_private *priv);
|
||||
int __orinoco_hw_setup_wepkeys(struct orinoco_private *priv);
|
||||
int __orinoco_hw_setup_enc(struct orinoco_private *priv);
|
||||
int __orinoco_hw_set_tkip_key(struct orinoco_private *priv, int key_idx,
|
||||
int set_tx, const u8 *key, const u8 *rsc,
|
||||
size_t rsc_len, const u8 *tsc, size_t tsc_len);
|
||||
int orinoco_clear_tkip_key(struct orinoco_private *priv, int key_idx);
|
||||
int __orinoco_hw_set_multicast_list(struct orinoco_private *priv,
|
||||
struct net_device *dev,
|
||||
int mc_count, int promisc);
|
||||
int orinoco_hw_get_essid(struct orinoco_private *priv, int *active,
|
||||
char buf[IW_ESSID_MAX_SIZE + 1]);
|
||||
int orinoco_hw_get_freq(struct orinoco_private *priv);
|
||||
int orinoco_hw_get_bitratelist(struct orinoco_private *priv,
|
||||
int *numrates, s32 *rates, int max);
|
||||
int orinoco_hw_trigger_scan(struct orinoco_private *priv,
|
||||
const struct cfg80211_ssid *ssid);
|
||||
int orinoco_hw_disassociate(struct orinoco_private *priv,
|
||||
u8 *addr, u16 reason_code);
|
||||
int orinoco_hw_get_current_bssid(struct orinoco_private *priv,
|
||||
u8 *addr);
|
||||
|
||||
#endif /* _ORINOCO_HW_H_ */
|
2429
drivers/net/wireless/intersil/orinoco/main.c
Normal file
2429
drivers/net/wireless/intersil/orinoco/main.c
Normal file
File diff suppressed because it is too large
Load Diff
50
drivers/net/wireless/intersil/orinoco/main.h
Normal file
50
drivers/net/wireless/intersil/orinoco/main.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* Exports from main to helper modules
|
||||
*
|
||||
* See copyright notice in main.c
|
||||
*/
|
||||
#ifndef _ORINOCO_MAIN_H_
|
||||
#define _ORINOCO_MAIN_H_
|
||||
|
||||
#include <linux/ieee80211.h>
|
||||
#include "orinoco.h"
|
||||
|
||||
/********************************************************************/
|
||||
/* Compile time configuration and compatibility stuff */
|
||||
/********************************************************************/
|
||||
|
||||
/* We do this this way to avoid ifdefs in the actual code */
|
||||
#ifdef WIRELESS_SPY
|
||||
#define SPY_NUMBER(priv) (priv->spy_data.spy_number)
|
||||
#else
|
||||
#define SPY_NUMBER(priv) 0
|
||||
#endif /* WIRELESS_SPY */
|
||||
|
||||
/********************************************************************/
|
||||
|
||||
/* Export module parameter */
|
||||
extern int force_monitor;
|
||||
|
||||
/* Forward declarations */
|
||||
struct net_device;
|
||||
struct work_struct;
|
||||
|
||||
void set_port_type(struct orinoco_private *priv);
|
||||
int orinoco_commit(struct orinoco_private *priv);
|
||||
void orinoco_reset(struct work_struct *work);
|
||||
|
||||
/* Information element helpers - find a home for these... */
|
||||
#define WPA_OUI_TYPE "\x00\x50\xF2\x01"
|
||||
#define WPA_SELECTOR_LEN 4
|
||||
static inline u8 *orinoco_get_wpa_ie(u8 *data, size_t len)
|
||||
{
|
||||
u8 *p = data;
|
||||
while ((p + 2 + WPA_SELECTOR_LEN) < (data + len)) {
|
||||
if ((p[0] == WLAN_EID_VENDOR_SPECIFIC) &&
|
||||
(memcmp(&p[2], WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0))
|
||||
return p;
|
||||
p += p[1] + 2;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* _ORINOCO_MAIN_H_ */
|
79
drivers/net/wireless/intersil/orinoco/mic.c
Normal file
79
drivers/net/wireless/intersil/orinoco/mic.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/* Orinoco MIC helpers
|
||||
*
|
||||
* See copyright notice in main.c
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/crypto.h>
|
||||
|
||||
#include "orinoco.h"
|
||||
#include "mic.h"
|
||||
|
||||
/********************************************************************/
|
||||
/* Michael MIC crypto setup */
|
||||
/********************************************************************/
|
||||
int orinoco_mic_init(struct orinoco_private *priv)
|
||||
{
|
||||
priv->tx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0);
|
||||
if (IS_ERR(priv->tx_tfm_mic)) {
|
||||
printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
|
||||
"crypto API michael_mic\n");
|
||||
priv->tx_tfm_mic = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->rx_tfm_mic = crypto_alloc_hash("michael_mic", 0, 0);
|
||||
if (IS_ERR(priv->rx_tfm_mic)) {
|
||||
printk(KERN_DEBUG "orinoco_mic_init: could not allocate "
|
||||
"crypto API michael_mic\n");
|
||||
priv->rx_tfm_mic = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void orinoco_mic_free(struct orinoco_private *priv)
|
||||
{
|
||||
if (priv->tx_tfm_mic)
|
||||
crypto_free_hash(priv->tx_tfm_mic);
|
||||
if (priv->rx_tfm_mic)
|
||||
crypto_free_hash(priv->rx_tfm_mic);
|
||||
}
|
||||
|
||||
int orinoco_mic(struct crypto_hash *tfm_michael, u8 *key,
|
||||
u8 *da, u8 *sa, u8 priority,
|
||||
u8 *data, size_t data_len, u8 *mic)
|
||||
{
|
||||
struct hash_desc desc;
|
||||
struct scatterlist sg[2];
|
||||
u8 hdr[ETH_HLEN + 2]; /* size of header + padding */
|
||||
|
||||
if (tfm_michael == NULL) {
|
||||
printk(KERN_WARNING "orinoco_mic: tfm_michael == NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Copy header into buffer. We need the padding on the end zeroed */
|
||||
memcpy(&hdr[0], da, ETH_ALEN);
|
||||
memcpy(&hdr[ETH_ALEN], sa, ETH_ALEN);
|
||||
hdr[ETH_ALEN * 2] = priority;
|
||||
hdr[ETH_ALEN * 2 + 1] = 0;
|
||||
hdr[ETH_ALEN * 2 + 2] = 0;
|
||||
hdr[ETH_ALEN * 2 + 3] = 0;
|
||||
|
||||
/* Use scatter gather to MIC header and data in one go */
|
||||
sg_init_table(sg, 2);
|
||||
sg_set_buf(&sg[0], hdr, sizeof(hdr));
|
||||
sg_set_buf(&sg[1], data, data_len);
|
||||
|
||||
if (crypto_hash_setkey(tfm_michael, key, MIC_KEYLEN))
|
||||
return -1;
|
||||
|
||||
desc.tfm = tfm_michael;
|
||||
desc.flags = 0;
|
||||
return crypto_hash_digest(&desc, sg, data_len + sizeof(hdr),
|
||||
mic);
|
||||
}
|
22
drivers/net/wireless/intersil/orinoco/mic.h
Normal file
22
drivers/net/wireless/intersil/orinoco/mic.h
Normal file
@@ -0,0 +1,22 @@
|
||||
/* Orinoco MIC helpers
|
||||
*
|
||||
* See copyright notice in main.c
|
||||
*/
|
||||
#ifndef _ORINOCO_MIC_H_
|
||||
#define _ORINOCO_MIC_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define MICHAEL_MIC_LEN 8
|
||||
|
||||
/* Forward declarations */
|
||||
struct orinoco_private;
|
||||
struct crypto_hash;
|
||||
|
||||
int orinoco_mic_init(struct orinoco_private *priv);
|
||||
void orinoco_mic_free(struct orinoco_private *priv);
|
||||
int orinoco_mic(struct crypto_hash *tfm_michael, u8 *key,
|
||||
u8 *da, u8 *sa, u8 priority,
|
||||
u8 *data, size_t data_len, u8 *mic);
|
||||
|
||||
#endif /* ORINOCO_MIC_H */
|
253
drivers/net/wireless/intersil/orinoco/orinoco.h
Normal file
253
drivers/net/wireless/intersil/orinoco/orinoco.h
Normal file
@@ -0,0 +1,253 @@
|
||||
/* orinoco.h
|
||||
*
|
||||
* Common definitions to all pieces of the various orinoco
|
||||
* drivers
|
||||
*/
|
||||
|
||||
#ifndef _ORINOCO_H
|
||||
#define _ORINOCO_H
|
||||
|
||||
#define DRIVER_VERSION "0.15"
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/wireless.h>
|
||||
#include <net/iw_handler.h>
|
||||
#include <net/cfg80211.h>
|
||||
|
||||
#include "hermes.h"
|
||||
|
||||
/* To enable debug messages */
|
||||
/*#define ORINOCO_DEBUG 3*/
|
||||
|
||||
#define WIRELESS_SPY /* enable iwspy support */
|
||||
|
||||
#define MAX_SCAN_LEN 4096
|
||||
|
||||
#define ORINOCO_SEQ_LEN 8
|
||||
#define ORINOCO_MAX_KEY_SIZE 14
|
||||
#define ORINOCO_MAX_KEYS 4
|
||||
|
||||
struct orinoco_key {
|
||||
__le16 len; /* always stored as little-endian */
|
||||
char data[ORINOCO_MAX_KEY_SIZE];
|
||||
} __packed;
|
||||
|
||||
#define TKIP_KEYLEN 16
|
||||
#define MIC_KEYLEN 8
|
||||
|
||||
struct orinoco_tkip_key {
|
||||
u8 tkip[TKIP_KEYLEN];
|
||||
u8 tx_mic[MIC_KEYLEN];
|
||||
u8 rx_mic[MIC_KEYLEN];
|
||||
};
|
||||
|
||||
enum orinoco_alg {
|
||||
ORINOCO_ALG_NONE,
|
||||
ORINOCO_ALG_WEP,
|
||||
ORINOCO_ALG_TKIP
|
||||
};
|
||||
|
||||
enum fwtype {
|
||||
FIRMWARE_TYPE_AGERE,
|
||||
FIRMWARE_TYPE_INTERSIL,
|
||||
FIRMWARE_TYPE_SYMBOL
|
||||
};
|
||||
|
||||
struct firmware;
|
||||
|
||||
struct orinoco_private {
|
||||
void *card; /* Pointer to card dependent structure */
|
||||
struct device *dev;
|
||||
int (*hard_reset)(struct orinoco_private *);
|
||||
int (*stop_fw)(struct orinoco_private *, int);
|
||||
|
||||
struct ieee80211_supported_band band;
|
||||
struct ieee80211_channel channels[14];
|
||||
u32 cipher_suites[3];
|
||||
|
||||
/* Synchronisation stuff */
|
||||
spinlock_t lock;
|
||||
int hw_unavailable;
|
||||
struct work_struct reset_work;
|
||||
|
||||
/* Interrupt tasklets */
|
||||
struct tasklet_struct rx_tasklet;
|
||||
struct list_head rx_list;
|
||||
|
||||
/* driver state */
|
||||
int open;
|
||||
u16 last_linkstatus;
|
||||
struct work_struct join_work;
|
||||
struct work_struct wevent_work;
|
||||
|
||||
/* Net device stuff */
|
||||
struct net_device *ndev;
|
||||
struct net_device_stats stats;
|
||||
struct iw_statistics wstats;
|
||||
|
||||
/* Hardware control variables */
|
||||
struct hermes hw;
|
||||
u16 txfid;
|
||||
|
||||
/* Capabilities of the hardware/firmware */
|
||||
enum fwtype firmware_type;
|
||||
int ibss_port;
|
||||
int nicbuf_size;
|
||||
u16 channel_mask;
|
||||
|
||||
/* Boolean capabilities */
|
||||
unsigned int has_ibss:1;
|
||||
unsigned int has_port3:1;
|
||||
unsigned int has_wep:1;
|
||||
unsigned int has_big_wep:1;
|
||||
unsigned int has_mwo:1;
|
||||
unsigned int has_pm:1;
|
||||
unsigned int has_preamble:1;
|
||||
unsigned int has_sensitivity:1;
|
||||
unsigned int has_hostscan:1;
|
||||
unsigned int has_alt_txcntl:1;
|
||||
unsigned int has_ext_scan:1;
|
||||
unsigned int has_wpa:1;
|
||||
unsigned int do_fw_download:1;
|
||||
unsigned int broken_disableport:1;
|
||||
unsigned int broken_monitor:1;
|
||||
unsigned int prefer_port3:1;
|
||||
|
||||
/* Configuration paramaters */
|
||||
enum nl80211_iftype iw_mode;
|
||||
enum orinoco_alg encode_alg;
|
||||
u16 wep_restrict, tx_key;
|
||||
struct key_params keys[ORINOCO_MAX_KEYS];
|
||||
|
||||
int bitratemode;
|
||||
char nick[IW_ESSID_MAX_SIZE + 1];
|
||||
char desired_essid[IW_ESSID_MAX_SIZE + 1];
|
||||
char desired_bssid[ETH_ALEN];
|
||||
int bssid_fixed;
|
||||
u16 frag_thresh, mwo_robust;
|
||||
u16 channel;
|
||||
u16 ap_density, rts_thresh;
|
||||
u16 pm_on, pm_mcast, pm_period, pm_timeout;
|
||||
u16 preamble;
|
||||
u16 short_retry_limit, long_retry_limit;
|
||||
u16 retry_lifetime;
|
||||
#ifdef WIRELESS_SPY
|
||||
struct iw_spy_data spy_data; /* iwspy support */
|
||||
struct iw_public_data wireless_data;
|
||||
#endif
|
||||
|
||||
/* Configuration dependent variables */
|
||||
int port_type, createibss;
|
||||
int promiscuous, mc_count;
|
||||
|
||||
/* Scanning support */
|
||||
struct cfg80211_scan_request *scan_request;
|
||||
struct work_struct process_scan;
|
||||
struct list_head scan_list;
|
||||
spinlock_t scan_lock; /* protects the scan list */
|
||||
|
||||
/* WPA support */
|
||||
u8 *wpa_ie;
|
||||
int wpa_ie_len;
|
||||
|
||||
struct crypto_hash *rx_tfm_mic;
|
||||
struct crypto_hash *tx_tfm_mic;
|
||||
|
||||
unsigned int wpa_enabled:1;
|
||||
unsigned int tkip_cm_active:1;
|
||||
unsigned int key_mgmt:3;
|
||||
|
||||
#if defined(CONFIG_HERMES_CACHE_FW_ON_INIT) || defined(CONFIG_PM_SLEEP)
|
||||
/* Cached in memory firmware to use during ->resume. */
|
||||
const struct firmware *cached_pri_fw;
|
||||
const struct firmware *cached_fw;
|
||||
#endif
|
||||
|
||||
struct notifier_block pm_notifier;
|
||||
};
|
||||
|
||||
#ifdef ORINOCO_DEBUG
|
||||
extern int orinoco_debug;
|
||||
#define DEBUG(n, args...) do { \
|
||||
if (orinoco_debug > (n)) \
|
||||
printk(KERN_DEBUG args); \
|
||||
} while (0)
|
||||
#else
|
||||
#define DEBUG(n, args...) do { } while (0)
|
||||
#endif /* ORINOCO_DEBUG */
|
||||
|
||||
/********************************************************************/
|
||||
/* Exported prototypes */
|
||||
/********************************************************************/
|
||||
|
||||
struct orinoco_private *alloc_orinocodev(int sizeof_card, struct device *device,
|
||||
int (*hard_reset)(struct orinoco_private *),
|
||||
int (*stop_fw)(struct orinoco_private *, int));
|
||||
void free_orinocodev(struct orinoco_private *priv);
|
||||
int orinoco_init(struct orinoco_private *priv);
|
||||
int orinoco_if_add(struct orinoco_private *priv, unsigned long base_addr,
|
||||
unsigned int irq, const struct net_device_ops *ops);
|
||||
void orinoco_if_del(struct orinoco_private *priv);
|
||||
int orinoco_up(struct orinoco_private *priv);
|
||||
void orinoco_down(struct orinoco_private *priv);
|
||||
irqreturn_t orinoco_interrupt(int irq, void *dev_id);
|
||||
|
||||
void __orinoco_ev_info(struct net_device *dev, struct hermes *hw);
|
||||
void __orinoco_ev_rx(struct net_device *dev, struct hermes *hw);
|
||||
|
||||
int orinoco_process_xmit_skb(struct sk_buff *skb,
|
||||
struct net_device *dev,
|
||||
struct orinoco_private *priv,
|
||||
int *tx_control,
|
||||
u8 *mic);
|
||||
|
||||
/* Common ndo functions exported for reuse by orinoco_usb */
|
||||
int orinoco_open(struct net_device *dev);
|
||||
int orinoco_stop(struct net_device *dev);
|
||||
struct net_device_stats *orinoco_get_stats(struct net_device *dev);
|
||||
void orinoco_set_multicast_list(struct net_device *dev);
|
||||
int orinoco_change_mtu(struct net_device *dev, int new_mtu);
|
||||
void orinoco_tx_timeout(struct net_device *dev);
|
||||
|
||||
/********************************************************************/
|
||||
/* Locking and synchronization functions */
|
||||
/********************************************************************/
|
||||
|
||||
static inline int orinoco_lock(struct orinoco_private *priv,
|
||||
unsigned long *flags)
|
||||
{
|
||||
priv->hw.ops->lock_irqsave(&priv->lock, flags);
|
||||
if (priv->hw_unavailable) {
|
||||
DEBUG(1, "orinoco_lock() called with hw_unavailable (dev=%p)\n",
|
||||
priv->ndev);
|
||||
priv->hw.ops->unlock_irqrestore(&priv->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void orinoco_unlock(struct orinoco_private *priv,
|
||||
unsigned long *flags)
|
||||
{
|
||||
priv->hw.ops->unlock_irqrestore(&priv->lock, flags);
|
||||
}
|
||||
|
||||
static inline void orinoco_lock_irq(struct orinoco_private *priv)
|
||||
{
|
||||
priv->hw.ops->lock_irq(&priv->lock);
|
||||
}
|
||||
|
||||
static inline void orinoco_unlock_irq(struct orinoco_private *priv)
|
||||
{
|
||||
priv->hw.ops->unlock_irq(&priv->lock);
|
||||
}
|
||||
|
||||
/*** Navigate from net_device to orinoco_private ***/
|
||||
static inline struct orinoco_private *ndev_priv(struct net_device *dev)
|
||||
{
|
||||
struct wireless_dev *wdev = netdev_priv(dev);
|
||||
return wdev_priv(wdev);
|
||||
}
|
||||
#endif /* _ORINOCO_H */
|
341
drivers/net/wireless/intersil/orinoco/orinoco_cs.c
Normal file
341
drivers/net/wireless/intersil/orinoco/orinoco_cs.c
Normal file
@@ -0,0 +1,341 @@
|
||||
/* orinoco_cs.c (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/
|
||||
* EnteraSys RoamAbout 802.11, ELSA Airlancer, Melco Buffalo and others).
|
||||
* It should also be usable on various Prism II based cards such as the
|
||||
* Linksys, D-Link and Farallon Skyline. It should also work on Symbol
|
||||
* cards such as the 3Com AirConnect and Ericsson WLAN.
|
||||
*
|
||||
* Copyright notice & release notes in file main.c
|
||||
*/
|
||||
|
||||
#define DRIVER_NAME "orinoco_cs"
|
||||
#define PFX DRIVER_NAME ": "
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
#include <pcmcia/ds.h>
|
||||
|
||||
#include "orinoco.h"
|
||||
|
||||
/********************************************************************/
|
||||
/* Module stuff */
|
||||
/********************************************************************/
|
||||
|
||||
MODULE_AUTHOR("David Gibson <hermes@gibson.dropbear.id.au>");
|
||||
MODULE_DESCRIPTION("Driver for PCMCIA Lucent Orinoco,"
|
||||
" Prism II based and similar wireless cards");
|
||||
MODULE_LICENSE("Dual MPL/GPL");
|
||||
|
||||
/* Module parameters */
|
||||
|
||||
/* Some D-Link cards have buggy CIS. They do work at 5v properly, but
|
||||
* don't have any CIS entry for it. This workaround it... */
|
||||
static int ignore_cis_vcc; /* = 0 */
|
||||
module_param(ignore_cis_vcc, int, 0);
|
||||
MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket");
|
||||
|
||||
/********************************************************************/
|
||||
/* Data structures */
|
||||
/********************************************************************/
|
||||
|
||||
/* PCMCIA specific device information (goes in the card field of
|
||||
* struct orinoco_private */
|
||||
struct orinoco_pccard {
|
||||
struct pcmcia_device *p_dev;
|
||||
|
||||
/* Used to handle hard reset */
|
||||
/* yuck, we need this hack to work around the insanity of the
|
||||
* PCMCIA layer */
|
||||
unsigned long hard_reset_in_progress;
|
||||
};
|
||||
|
||||
|
||||
/********************************************************************/
|
||||
/* Function prototypes */
|
||||
/********************************************************************/
|
||||
|
||||
static int orinoco_cs_config(struct pcmcia_device *link);
|
||||
static void orinoco_cs_release(struct pcmcia_device *link);
|
||||
static void orinoco_cs_detach(struct pcmcia_device *p_dev);
|
||||
|
||||
/********************************************************************/
|
||||
/* Device methods */
|
||||
/********************************************************************/
|
||||
|
||||
static int
|
||||
orinoco_cs_hard_reset(struct orinoco_private *priv)
|
||||
{
|
||||
struct orinoco_pccard *card = priv->card;
|
||||
struct pcmcia_device *link = card->p_dev;
|
||||
int err;
|
||||
|
||||
/* We need atomic ops here, because we're not holding the lock */
|
||||
set_bit(0, &card->hard_reset_in_progress);
|
||||
|
||||
err = pcmcia_reset_card(link->socket);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
msleep(100);
|
||||
clear_bit(0, &card->hard_reset_in_progress);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************************************************************/
|
||||
/* PCMCIA stuff */
|
||||
/********************************************************************/
|
||||
|
||||
static int
|
||||
orinoco_cs_probe(struct pcmcia_device *link)
|
||||
{
|
||||
struct orinoco_private *priv;
|
||||
struct orinoco_pccard *card;
|
||||
|
||||
priv = alloc_orinocodev(sizeof(*card), &link->dev,
|
||||
orinoco_cs_hard_reset, NULL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
card = priv->card;
|
||||
|
||||
/* Link both structures together */
|
||||
card->p_dev = link;
|
||||
link->priv = priv;
|
||||
|
||||
return orinoco_cs_config(link);
|
||||
} /* orinoco_cs_attach */
|
||||
|
||||
static void orinoco_cs_detach(struct pcmcia_device *link)
|
||||
{
|
||||
struct orinoco_private *priv = link->priv;
|
||||
|
||||
orinoco_if_del(priv);
|
||||
|
||||
orinoco_cs_release(link);
|
||||
|
||||
wiphy_unregister(priv_to_wiphy(priv));
|
||||
free_orinocodev(priv);
|
||||
} /* orinoco_cs_detach */
|
||||
|
||||
static int orinoco_cs_config_check(struct pcmcia_device *p_dev, void *priv_data)
|
||||
{
|
||||
if (p_dev->config_index == 0)
|
||||
return -EINVAL;
|
||||
|
||||
return pcmcia_request_io(p_dev);
|
||||
};
|
||||
|
||||
static int
|
||||
orinoco_cs_config(struct pcmcia_device *link)
|
||||
{
|
||||
struct orinoco_private *priv = link->priv;
|
||||
struct hermes *hw = &priv->hw;
|
||||
int ret;
|
||||
void __iomem *mem;
|
||||
|
||||
link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC |
|
||||
CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
|
||||
if (ignore_cis_vcc)
|
||||
link->config_flags &= ~CONF_AUTO_CHECK_VCC;
|
||||
ret = pcmcia_loop_config(link, orinoco_cs_config_check, NULL);
|
||||
if (ret) {
|
||||
if (!ignore_cis_vcc)
|
||||
printk(KERN_ERR PFX "GetNextTuple(): No matching "
|
||||
"CIS configuration. Maybe you need the "
|
||||
"ignore_cis_vcc=1 parameter.\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
mem = ioport_map(link->resource[0]->start,
|
||||
resource_size(link->resource[0]));
|
||||
if (!mem)
|
||||
goto failed;
|
||||
|
||||
/* We initialize the hermes structure before completing PCMCIA
|
||||
* configuration just in case the interrupt handler gets
|
||||
* called. */
|
||||
hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
|
||||
|
||||
ret = pcmcia_request_irq(link, orinoco_interrupt);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
ret = pcmcia_enable_device(link);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
/* Initialise the main driver */
|
||||
if (orinoco_init(priv) != 0) {
|
||||
printk(KERN_ERR PFX "orinoco_init() failed\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Register an interface with the stack */
|
||||
if (orinoco_if_add(priv, link->resource[0]->start,
|
||||
link->irq, NULL) != 0) {
|
||||
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
orinoco_cs_release(link);
|
||||
return -ENODEV;
|
||||
} /* orinoco_cs_config */
|
||||
|
||||
static void
|
||||
orinoco_cs_release(struct pcmcia_device *link)
|
||||
{
|
||||
struct orinoco_private *priv = link->priv;
|
||||
unsigned long flags;
|
||||
|
||||
/* We're committed to taking the device away now, so mark the
|
||||
* hardware as unavailable */
|
||||
priv->hw.ops->lock_irqsave(&priv->lock, &flags);
|
||||
priv->hw_unavailable++;
|
||||
priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
|
||||
|
||||
pcmcia_disable_device(link);
|
||||
if (priv->hw.iobase)
|
||||
ioport_unmap(priv->hw.iobase);
|
||||
} /* orinoco_cs_release */
|
||||
|
||||
static int orinoco_cs_suspend(struct pcmcia_device *link)
|
||||
{
|
||||
struct orinoco_private *priv = link->priv;
|
||||
struct orinoco_pccard *card = priv->card;
|
||||
|
||||
/* This is probably racy, but I can't think of
|
||||
a better way, short of rewriting the PCMCIA
|
||||
layer to not suck :-( */
|
||||
if (!test_bit(0, &card->hard_reset_in_progress))
|
||||
orinoco_down(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int orinoco_cs_resume(struct pcmcia_device *link)
|
||||
{
|
||||
struct orinoco_private *priv = link->priv;
|
||||
struct orinoco_pccard *card = priv->card;
|
||||
int err = 0;
|
||||
|
||||
if (!test_bit(0, &card->hard_reset_in_progress))
|
||||
err = orinoco_up(priv);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************/
|
||||
/* Module initialization */
|
||||
/********************************************************************/
|
||||
|
||||
static const struct pcmcia_device_id orinoco_cs_ids[] = {
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), /* 3Com AirConnect PCI 777A */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x016b, 0x0001), /* Ericsson WLAN Card C11 */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x01eb, 0x080a), /* Nortel Networks eMobility 802.11 Wireless Adapter */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0261, 0x0002), /* AirWay 802.11 Adapter (PCMCIA) */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0001), /* ARtem Onair */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0268, 0x0003), /* ARtem Onair Comcard 11 */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x026f, 0x0305), /* Buffalo WLI-PCM-S11 */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x02aa, 0x0002), /* ASUS SpaceLink WL-100 */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x0002), /* SpeedStream SS1021 Wireless Adapter */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x02ac, 0x3021), /* SpeedStream Wireless Adapter */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x14ea, 0xb001), /* PLANEX RoadLannerWave GW-NS11H */
|
||||
PCMCIA_DEVICE_PROD_ID12("3Com", "3CRWE737A AirConnect Wireless LAN PC Card", 0x41240e5b, 0x56010af3),
|
||||
PCMCIA_DEVICE_PROD_ID12("Allied Telesyn", "AT-WCL452 Wireless PCMCIA Radio", 0x5cd01705, 0x4271660f),
|
||||
PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11B_CF_CARD_25", 0x78fc06ee, 0x45a50c1e),
|
||||
PCMCIA_DEVICE_PROD_ID12("ASUS", "802_11b_PC_CARD_25", 0x78fc06ee, 0xdb9aa842),
|
||||
PCMCIA_DEVICE_PROD_ID12("Avaya Communication", "Avaya Wireless PC Card", 0xd8a43b78, 0x0d341169),
|
||||
PCMCIA_DEVICE_PROD_ID12("BENQ", "AWL100 PCMCIA ADAPTER", 0x35dadc74, 0x01f7fedb),
|
||||
PCMCIA_DEVICE_PROD_ID12("Cabletron", "RoamAbout 802.11 DS", 0x32d445f5, 0xedeffd90),
|
||||
PCMCIA_DEVICE_PROD_ID12("D-Link Corporation", "D-Link DWL-650H 11Mbps WLAN Adapter", 0xef544d24, 0xcd8ea916),
|
||||
PCMCIA_DEVICE_PROD_ID12("ELSA", "AirLancer MC-11", 0x4507a33a, 0xef54f0e3),
|
||||
PCMCIA_DEVICE_PROD_ID12("HyperLink", "Wireless PC Card 11Mbps", 0x56cc3f1a, 0x0bcf220c),
|
||||
PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless 2011 LAN PC Card", 0x816cc815, 0x07f58077),
|
||||
PCMCIA_DEVICE_PROD_ID12("LeArtery", "SYNCBYAIR 11Mbps Wireless LAN PC Card", 0x7e3b326a, 0x49893e92),
|
||||
PCMCIA_DEVICE_PROD_ID12("Lucent Technologies", "WaveLAN/IEEE", 0x23eb9949, 0xc562e72a),
|
||||
PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11", 0x481e0094, 0x7360e410),
|
||||
PCMCIA_DEVICE_PROD_ID12("MELCO", "WLI-PCM-L11G", 0x481e0094, 0xf57ca4b3),
|
||||
PCMCIA_DEVICE_PROD_ID12("NCR", "WaveLAN/IEEE", 0x24358cd4, 0xc562e72a),
|
||||
PCMCIA_DEVICE_PROD_ID12("Nortel Networks", "emobility 802.11 Wireless LAN PC Card", 0x2d617ea0, 0x88cd5767),
|
||||
PCMCIA_DEVICE_PROD_ID12("OTC", "Wireless AirEZY 2411-PCC WLAN Card", 0x4ac44287, 0x235a6bed),
|
||||
PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PC CARD HARMONY 80211B", 0xc6536a5e, 0x090c3cd9),
|
||||
PCMCIA_DEVICE_PROD_ID12("PROXIM", "LAN PCI CARD HARMONY 80211B", 0xc6536a5e, 0x9f494e26),
|
||||
PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "11Mbps WLAN Card", 0x43d74cb4, 0x579bd91b),
|
||||
PCMCIA_DEVICE_PROD_ID12("Symbol Technologies", "LA4111 Spectrum24 Wireless LAN PC Card", 0x3f02b4d6, 0x3663cb0e),
|
||||
PCMCIA_DEVICE_MANF_CARD_PROD_ID3(0x0156, 0x0002, "Version 01.01", 0xd27deb1a), /* Lucent Orinoco */
|
||||
#ifdef CONFIG_HERMES_PRISM
|
||||
/* Only entries that certainly identify Prism chipset */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7100), /* SonicWALL Long Range Wireless Card */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x000b, 0x7300), /* Sohoware NCP110, Philips 802.11b */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0089, 0x0002), /* AnyPoint(TM) Wireless II PC Card */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), /* PROXIM RangeLAN-DS/LAN PC CARD */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), /* Compaq WL100 11 Mbps Wireless Adapter */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x01ff, 0x0008), /* Intermec MobileLAN 11Mbps 802.11b WLAN Card */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), /* Samsung SWL2000-N 11Mb/s WLAN Card */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), /* Linksys WPC11 Version 2.5 */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1613), /* Linksys WPC11 Version 3 */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0002), /* Compaq HNW-100 11 Mbps Wireless Adapter */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x028a, 0x0673), /* Linksys WCF12 Wireless CompactFlash Card */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300), /* Airvast WN-100 */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x9005, 0x0021), /* Adaptec Ultra Wireless ANW-8030 */
|
||||
PCMCIA_DEVICE_MANF_CARD(0xc001, 0x0008), /* CONTEC FLEXSCAN/FX-DDS110-PCC */
|
||||
PCMCIA_DEVICE_MANF_CARD(0xc250, 0x0002), /* Conceptronic CON11Cpro, EMTAC A2424i */
|
||||
PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), /* Safeway 802.11b, ZCOMAX AirRunner/XI-300 */
|
||||
PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), /* D-Link DCF660, Sandisk Connect SDWCFB-000 */
|
||||
PCMCIA_DEVICE_PROD_ID123("Instant Wireless ", " Network PC CARD", "Version 01.02", 0x11d901af, 0x6e9bd926, 0x4b74baa0),
|
||||
PCMCIA_DEVICE_PROD_ID12("ACTIONTEC", "PRISM Wireless LAN PC Card", 0x393089da, 0xa71e69d5),
|
||||
PCMCIA_DEVICE_PROD_ID12("Addtron", "AWP-100 Wireless PCMCIA", 0xe6ec52ce, 0x08649af2),
|
||||
PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-CF-S11G", 0x2decece3, 0x82067c18),
|
||||
PCMCIA_DEVICE_PROD_ID12("BUFFALO", "WLI-PCM-L11G", 0x2decece3, 0xf57ca4b3),
|
||||
PCMCIA_DEVICE_PROD_ID12("Compaq", "WL200_11Mbps_Wireless_PCI_Card", 0x54f7c49c, 0x15a75e5b),
|
||||
PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCC-11", 0x5261440f, 0xa6405584),
|
||||
PCMCIA_DEVICE_PROD_ID12("corega K.K.", "Wireless LAN PCCA-11", 0x5261440f, 0xdf6115f9),
|
||||
PCMCIA_DEVICE_PROD_ID12("corega_K.K.", "Wireless_LAN_PCCB-11", 0x29e33311, 0xee7a27ae),
|
||||
PCMCIA_DEVICE_PROD_ID12("Digital Data Communications", "WPC-0100", 0xfdd73470, 0xe0b6f146),
|
||||
PCMCIA_DEVICE_PROD_ID12("D", "Link DRC-650 11Mbps WLAN Card", 0x71b18589, 0xf144e3ac),
|
||||
PCMCIA_DEVICE_PROD_ID12("D", "Link DWL-650 11Mbps WLAN Card", 0x71b18589, 0xb6f1b0ab),
|
||||
PCMCIA_DEVICE_PROD_ID12(" ", "IEEE 802.11 Wireless LAN/PC Card", 0x3b6e20c8, 0xefccafe9),
|
||||
PCMCIA_DEVICE_PROD_ID12("INTERSIL", "HFA384x/IEEE", 0x74c5e40d, 0xdb472a18),
|
||||
PCMCIA_DEVICE_PROD_ID12("INTERSIL", "I-GATE 11M PC Card / PC Card plus", 0x74c5e40d, 0x8304ff77),
|
||||
PCMCIA_DEVICE_PROD_ID12("Intersil", "PRISM 2_5 PCMCIA ADAPTER", 0x4b801a17, 0x6345a0bf),
|
||||
PCMCIA_DEVICE_PROD_ID12("Linksys", "Wireless CompactFlash Card", 0x0733cc81, 0x0c52f395),
|
||||
PCMCIA_DEVICE_PROD_ID12("Microsoft", "Wireless Notebook Adapter MN-520", 0x5961bf85, 0x6eec8c01),
|
||||
PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401RA Wireless PC", "Card", 0x0306467f, 0x9762e8f1),
|
||||
PCMCIA_DEVICE_PROD_ID12("NETGEAR MA401 Wireless PC", "Card", 0xa37434e9, 0x9762e8f1),
|
||||
PCMCIA_DEVICE_PROD_ID12("OEM", "PRISM2 IEEE 802.11 PC-Card", 0xfea54c90, 0x48f2bdd6),
|
||||
PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-CF110", 0x209f40ab, 0xd9715264),
|
||||
PCMCIA_DEVICE_PROD_ID12("PLANEX", "GeoWave/GW-NS110", 0x209f40ab, 0x46263178),
|
||||
PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2532W-B EliteConnect Wireless Adapter", 0xc4f8b18b, 0x196bd757),
|
||||
PCMCIA_DEVICE_PROD_ID12("SMC", "SMC2632W", 0xc4f8b18b, 0x474a1f2a),
|
||||
PCMCIA_DEVICE_PROD_ID12("ZoomAir 11Mbps High", "Rate wireless Networking", 0x273fe3db, 0x32a1eaee),
|
||||
PCMCIA_DEVICE_PROD_ID3("HFA3863", 0x355cb092),
|
||||
PCMCIA_DEVICE_PROD_ID3("ISL37100P", 0x630d52b2),
|
||||
PCMCIA_DEVICE_PROD_ID3("ISL37101P-10", 0xdd97a26b),
|
||||
PCMCIA_DEVICE_PROD_ID3("ISL37300P", 0xc9049a39),
|
||||
|
||||
/* This may be Agere or Intersil Firmware */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002),
|
||||
#endif
|
||||
PCMCIA_DEVICE_NULL,
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, orinoco_cs_ids);
|
||||
|
||||
static struct pcmcia_driver orinoco_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRIVER_NAME,
|
||||
.probe = orinoco_cs_probe,
|
||||
.remove = orinoco_cs_detach,
|
||||
.id_table = orinoco_cs_ids,
|
||||
.suspend = orinoco_cs_suspend,
|
||||
.resume = orinoco_cs_resume,
|
||||
};
|
||||
module_pcmcia_driver(orinoco_driver);
|
323
drivers/net/wireless/intersil/orinoco/orinoco_nortel.c
Normal file
323
drivers/net/wireless/intersil/orinoco/orinoco_nortel.c
Normal file
@@ -0,0 +1,323 @@
|
||||
/* orinoco_nortel.c
|
||||
*
|
||||
* Driver for Prism II devices which would usually be driven by orinoco_cs,
|
||||
* but are connected to the PCI bus by a PCI-to-PCMCIA adapter used in
|
||||
* Nortel emobility, Symbol LA-4113 and Symbol LA-4123.
|
||||
*
|
||||
* Copyright (C) 2002 Tobias Hoffmann
|
||||
* (C) 2003 Christoph Jungegger <disdos@traum404.de>
|
||||
*
|
||||
* Some of this code is borrowed from orinoco_plx.c
|
||||
* Copyright (C) 2001 Daniel Barlow
|
||||
* Some of this code is borrowed from orinoco_pci.c
|
||||
* Copyright (C) 2001 Jean Tourrilhes
|
||||
* 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 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.
|
||||
*/
|
||||
|
||||
#define DRIVER_NAME "orinoco_nortel"
|
||||
#define PFX DRIVER_NAME ": "
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
|
||||
#include "orinoco.h"
|
||||
#include "orinoco_pci.h"
|
||||
|
||||
#define COR_OFFSET (0xe0) /* COR attribute offset of Prism2 PC card */
|
||||
#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */
|
||||
|
||||
|
||||
/*
|
||||
* Do a soft reset of the 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 bis : Don't try to access HERMES_CMD during the reset phase.
|
||||
* It just won't work !
|
||||
*/
|
||||
static int orinoco_nortel_cor_reset(struct orinoco_private *priv)
|
||||
{
|
||||
struct orinoco_pci_card *card = priv->card;
|
||||
|
||||
/* Assert the reset until the card notices */
|
||||
iowrite16(8, card->bridge_io + 2);
|
||||
ioread16(card->attr_io + COR_OFFSET);
|
||||
iowrite16(0x80, card->attr_io + COR_OFFSET);
|
||||
mdelay(1);
|
||||
|
||||
/* Give time for the card to recover from this hard effort */
|
||||
iowrite16(0, card->attr_io + COR_OFFSET);
|
||||
iowrite16(0, card->attr_io + COR_OFFSET);
|
||||
mdelay(1);
|
||||
|
||||
/* Set COR as usual */
|
||||
iowrite16(COR_VALUE, card->attr_io + COR_OFFSET);
|
||||
iowrite16(COR_VALUE, card->attr_io + COR_OFFSET);
|
||||
mdelay(1);
|
||||
|
||||
iowrite16(0x228, card->bridge_io + 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int orinoco_nortel_hw_init(struct orinoco_pci_card *card)
|
||||
{
|
||||
int i;
|
||||
u32 reg;
|
||||
|
||||
/* Setup bridge */
|
||||
if (ioread16(card->bridge_io) & 1) {
|
||||
printk(KERN_ERR PFX "brg1 answer1 wrong\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
iowrite16(0x118, card->bridge_io + 2);
|
||||
iowrite16(0x108, card->bridge_io + 2);
|
||||
mdelay(30);
|
||||
iowrite16(0x8, card->bridge_io + 2);
|
||||
for (i = 0; i < 30; i++) {
|
||||
mdelay(30);
|
||||
if (ioread16(card->bridge_io) & 0x10)
|
||||
break;
|
||||
}
|
||||
if (i == 30) {
|
||||
printk(KERN_ERR PFX "brg1 timed out\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
if (ioread16(card->attr_io + COR_OFFSET) & 1) {
|
||||
printk(KERN_ERR PFX "brg2 answer1 wrong\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
if (ioread16(card->attr_io + COR_OFFSET + 2) & 1) {
|
||||
printk(KERN_ERR PFX "brg2 answer2 wrong\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
if (ioread16(card->attr_io + COR_OFFSET + 4) & 1) {
|
||||
printk(KERN_ERR PFX "brg2 answer3 wrong\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Set the PCMCIA COR register */
|
||||
iowrite16(COR_VALUE, card->attr_io + COR_OFFSET);
|
||||
mdelay(1);
|
||||
reg = ioread16(card->attr_io + COR_OFFSET);
|
||||
if (reg != COR_VALUE) {
|
||||
printk(KERN_ERR PFX "Error setting COR value (reg=%x)\n",
|
||||
reg);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Set LEDs */
|
||||
iowrite16(1, card->bridge_io + 10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int orinoco_nortel_init_one(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
int err;
|
||||
struct orinoco_private *priv;
|
||||
struct orinoco_pci_card *card;
|
||||
void __iomem *hermes_io, *bridge_io, *attr_io;
|
||||
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Cannot enable PCI device\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pci_request_regions(pdev, DRIVER_NAME);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
|
||||
goto fail_resources;
|
||||
}
|
||||
|
||||
bridge_io = pci_iomap(pdev, 0, 0);
|
||||
if (!bridge_io) {
|
||||
printk(KERN_ERR PFX "Cannot map bridge registers\n");
|
||||
err = -EIO;
|
||||
goto fail_map_bridge;
|
||||
}
|
||||
|
||||
attr_io = pci_iomap(pdev, 1, 0);
|
||||
if (!attr_io) {
|
||||
printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n");
|
||||
err = -EIO;
|
||||
goto fail_map_attr;
|
||||
}
|
||||
|
||||
hermes_io = pci_iomap(pdev, 2, 0);
|
||||
if (!hermes_io) {
|
||||
printk(KERN_ERR PFX "Cannot map chipset registers\n");
|
||||
err = -EIO;
|
||||
goto fail_map_hermes;
|
||||
}
|
||||
|
||||
/* Allocate network device */
|
||||
priv = alloc_orinocodev(sizeof(*card), &pdev->dev,
|
||||
orinoco_nortel_cor_reset, NULL);
|
||||
if (!priv) {
|
||||
printk(KERN_ERR PFX "Cannot allocate network device\n");
|
||||
err = -ENOMEM;
|
||||
goto fail_alloc;
|
||||
}
|
||||
|
||||
card = priv->card;
|
||||
card->bridge_io = bridge_io;
|
||||
card->attr_io = attr_io;
|
||||
|
||||
hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING);
|
||||
|
||||
err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED,
|
||||
DRIVER_NAME, priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq);
|
||||
err = -EBUSY;
|
||||
goto fail_irq;
|
||||
}
|
||||
|
||||
err = orinoco_nortel_hw_init(card);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Hardware initialization failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = orinoco_nortel_cor_reset(priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Initial reset failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = orinoco_init(priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "orinoco_init() failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = orinoco_if_add(priv, 0, 0, NULL);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
|
||||
goto fail_wiphy;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, priv);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_wiphy:
|
||||
wiphy_unregister(priv_to_wiphy(priv));
|
||||
fail:
|
||||
free_irq(pdev->irq, priv);
|
||||
|
||||
fail_irq:
|
||||
free_orinocodev(priv);
|
||||
|
||||
fail_alloc:
|
||||
pci_iounmap(pdev, hermes_io);
|
||||
|
||||
fail_map_hermes:
|
||||
pci_iounmap(pdev, attr_io);
|
||||
|
||||
fail_map_attr:
|
||||
pci_iounmap(pdev, bridge_io);
|
||||
|
||||
fail_map_bridge:
|
||||
pci_release_regions(pdev);
|
||||
|
||||
fail_resources:
|
||||
pci_disable_device(pdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void orinoco_nortel_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
struct orinoco_private *priv = pci_get_drvdata(pdev);
|
||||
struct orinoco_pci_card *card = priv->card;
|
||||
|
||||
/* Clear LEDs */
|
||||
iowrite16(0, card->bridge_io + 10);
|
||||
|
||||
orinoco_if_del(priv);
|
||||
wiphy_unregister(priv_to_wiphy(priv));
|
||||
free_irq(pdev->irq, priv);
|
||||
free_orinocodev(priv);
|
||||
pci_iounmap(pdev, priv->hw.iobase);
|
||||
pci_iounmap(pdev, card->attr_io);
|
||||
pci_iounmap(pdev, card->bridge_io);
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id orinoco_nortel_id_table[] = {
|
||||
/* Nortel emobility PCI */
|
||||
{0x126c, 0x8030, PCI_ANY_ID, PCI_ANY_ID,},
|
||||
/* Symbol LA-4123 PCI */
|
||||
{0x1562, 0x0001, PCI_ANY_ID, PCI_ANY_ID,},
|
||||
{0,},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, orinoco_nortel_id_table);
|
||||
|
||||
static struct pci_driver orinoco_nortel_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = orinoco_nortel_id_table,
|
||||
.probe = orinoco_nortel_init_one,
|
||||
.remove = orinoco_nortel_remove_one,
|
||||
.suspend = orinoco_pci_suspend,
|
||||
.resume = orinoco_pci_resume,
|
||||
};
|
||||
|
||||
static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
|
||||
" (Tobias Hoffmann & Christoph Jungegger <disdos@traum404.de>)";
|
||||
MODULE_AUTHOR("Christoph Jungegger <disdos@traum404.de>");
|
||||
MODULE_DESCRIPTION("Driver for wireless LAN cards using the Nortel PCI bridge");
|
||||
MODULE_LICENSE("Dual MPL/GPL");
|
||||
|
||||
static int __init orinoco_nortel_init(void)
|
||||
{
|
||||
printk(KERN_DEBUG "%s\n", version);
|
||||
return pci_register_driver(&orinoco_nortel_driver);
|
||||
}
|
||||
|
||||
static void __exit orinoco_nortel_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&orinoco_nortel_driver);
|
||||
}
|
||||
|
||||
module_init(orinoco_nortel_init);
|
||||
module_exit(orinoco_nortel_exit);
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* End:
|
||||
*/
|
266
drivers/net/wireless/intersil/orinoco/orinoco_pci.c
Normal file
266
drivers/net/wireless/intersil/orinoco/orinoco_pci.c
Normal file
@@ -0,0 +1,266 @@
|
||||
/* orinoco_pci.c
|
||||
*
|
||||
* Driver for Prism 2.5/3 devices that have a direct PCI interface
|
||||
* (i.e. these are not PCMCIA cards in a PCMCIA-to-PCI bridge).
|
||||
* The card contains only one PCI region, which contains all the usual
|
||||
* hermes registers, as well as the COR register.
|
||||
*
|
||||
* Current maintainers are:
|
||||
* Pavel Roskin <proski AT gnu.org>
|
||||
* and David Gibson <hermes AT gibson.dropbear.id.au>
|
||||
*
|
||||
* Some of this code is borrowed from orinoco_plx.c
|
||||
* Copyright (C) 2001 Daniel Barlow <dan AT 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.
|
||||
* This file originally written by:
|
||||
* Copyright (C) 2001 Jean Tourrilhes <jt AT hpl.hp.com>
|
||||
* And is now maintained by:
|
||||
* (C) Copyright David Gibson, IBM Corp. 2002-2003.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define DRIVER_NAME "orinoco_pci"
|
||||
#define PFX DRIVER_NAME ": "
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "orinoco.h"
|
||||
#include "orinoco_pci.h"
|
||||
|
||||
/* Offset of the COR register of the PCI card */
|
||||
#define HERMES_PCI_COR (0x26)
|
||||
|
||||
/* 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 */
|
||||
|
||||
/*
|
||||
* Do a soft reset of the 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)
|
||||
{
|
||||
struct hermes *hw = &priv->hw;
|
||||
unsigned long timeout;
|
||||
u16 reg;
|
||||
|
||||
/* Assert the reset until the card notices */
|
||||
hermes_write_regn(hw, PCI_COR, HERMES_PCI_COR_MASK);
|
||||
mdelay(HERMES_PCI_COR_ONT);
|
||||
|
||||
/* Give time for the card to recover from this hard effort */
|
||||
hermes_write_regn(hw, PCI_COR, 0x0000);
|
||||
mdelay(HERMES_PCI_COR_OFFT);
|
||||
|
||||
/* The card is ready when it's no longer busy */
|
||||
timeout = jiffies + msecs_to_jiffies(HERMES_PCI_COR_BUSYT);
|
||||
reg = hermes_read_regn(hw, CMD);
|
||||
while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) {
|
||||
mdelay(1);
|
||||
reg = hermes_read_regn(hw, CMD);
|
||||
}
|
||||
|
||||
/* Still busy? */
|
||||
if (reg & HERMES_CMD_BUSY) {
|
||||
printk(KERN_ERR PFX "Busy timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int orinoco_pci_init_one(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
int err;
|
||||
struct orinoco_private *priv;
|
||||
struct orinoco_pci_card *card;
|
||||
void __iomem *hermes_io;
|
||||
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Cannot enable PCI device\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pci_request_regions(pdev, DRIVER_NAME);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
|
||||
goto fail_resources;
|
||||
}
|
||||
|
||||
hermes_io = pci_iomap(pdev, 0, 0);
|
||||
if (!hermes_io) {
|
||||
printk(KERN_ERR PFX "Cannot remap chipset registers\n");
|
||||
err = -EIO;
|
||||
goto fail_map_hermes;
|
||||
}
|
||||
|
||||
/* Allocate network device */
|
||||
priv = alloc_orinocodev(sizeof(*card), &pdev->dev,
|
||||
orinoco_pci_cor_reset, NULL);
|
||||
if (!priv) {
|
||||
printk(KERN_ERR PFX "Cannot allocate network device\n");
|
||||
err = -ENOMEM;
|
||||
goto fail_alloc;
|
||||
}
|
||||
|
||||
card = priv->card;
|
||||
|
||||
hermes_struct_init(&priv->hw, hermes_io, HERMES_32BIT_REGSPACING);
|
||||
|
||||
err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED,
|
||||
DRIVER_NAME, priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq);
|
||||
err = -EBUSY;
|
||||
goto fail_irq;
|
||||
}
|
||||
|
||||
err = orinoco_pci_cor_reset(priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Initial reset failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = orinoco_init(priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "orinoco_init() failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = orinoco_if_add(priv, 0, 0, NULL);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
|
||||
goto fail_wiphy;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, priv);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_wiphy:
|
||||
wiphy_unregister(priv_to_wiphy(priv));
|
||||
fail:
|
||||
free_irq(pdev->irq, priv);
|
||||
|
||||
fail_irq:
|
||||
free_orinocodev(priv);
|
||||
|
||||
fail_alloc:
|
||||
pci_iounmap(pdev, hermes_io);
|
||||
|
||||
fail_map_hermes:
|
||||
pci_release_regions(pdev);
|
||||
|
||||
fail_resources:
|
||||
pci_disable_device(pdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void orinoco_pci_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
struct orinoco_private *priv = pci_get_drvdata(pdev);
|
||||
|
||||
orinoco_if_del(priv);
|
||||
wiphy_unregister(priv_to_wiphy(priv));
|
||||
free_irq(pdev->irq, priv);
|
||||
free_orinocodev(priv);
|
||||
pci_iounmap(pdev, priv->hw.iobase);
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id orinoco_pci_id_table[] = {
|
||||
/* Intersil Prism 3 */
|
||||
{0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID,},
|
||||
/* Intersil Prism 2.5 */
|
||||
{0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID,},
|
||||
/* Samsung MagicLAN SWL-2210P */
|
||||
{0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID,},
|
||||
{0,},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, orinoco_pci_id_table);
|
||||
|
||||
static struct pci_driver orinoco_pci_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = orinoco_pci_id_table,
|
||||
.probe = orinoco_pci_init_one,
|
||||
.remove = orinoco_pci_remove_one,
|
||||
.suspend = orinoco_pci_suspend,
|
||||
.resume = orinoco_pci_resume,
|
||||
};
|
||||
|
||||
static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
|
||||
" (Pavel Roskin <proski@gnu.org>,"
|
||||
" David Gibson <hermes@gibson.dropbear.id.au> &"
|
||||
" Jean Tourrilhes <jt@hpl.hp.com>)";
|
||||
MODULE_AUTHOR("Pavel Roskin <proski@gnu.org> &"
|
||||
" David Gibson <hermes@gibson.dropbear.id.au>");
|
||||
MODULE_DESCRIPTION("Driver for wireless LAN cards using direct PCI interface");
|
||||
MODULE_LICENSE("Dual MPL/GPL");
|
||||
|
||||
static int __init orinoco_pci_init(void)
|
||||
{
|
||||
printk(KERN_DEBUG "%s\n", version);
|
||||
return pci_register_driver(&orinoco_pci_driver);
|
||||
}
|
||||
|
||||
static 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:
|
||||
*/
|
68
drivers/net/wireless/intersil/orinoco/orinoco_pci.h
Normal file
68
drivers/net/wireless/intersil/orinoco/orinoco_pci.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* orinoco_pci.h
|
||||
*
|
||||
* Common code for all Orinoco drivers for PCI devices, including
|
||||
* both native PCI and PCMCIA-to-PCI bridges.
|
||||
*
|
||||
* Copyright (C) 2005, Pavel Roskin.
|
||||
* See main.c for license.
|
||||
*/
|
||||
|
||||
#ifndef _ORINOCO_PCI_H
|
||||
#define _ORINOCO_PCI_H
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
|
||||
/* Driver specific data */
|
||||
struct orinoco_pci_card {
|
||||
void __iomem *bridge_io;
|
||||
void __iomem *attr_io;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int orinoco_pci_suspend(struct pci_dev *pdev, pm_message_t state)
|
||||
{
|
||||
struct orinoco_private *priv = pci_get_drvdata(pdev);
|
||||
|
||||
orinoco_down(priv);
|
||||
free_irq(pdev->irq, priv);
|
||||
pci_save_state(pdev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, PCI_D3hot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int orinoco_pci_resume(struct pci_dev *pdev)
|
||||
{
|
||||
struct orinoco_private *priv = pci_get_drvdata(pdev);
|
||||
struct net_device *dev = priv->ndev;
|
||||
int err;
|
||||
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
printk(KERN_ERR "%s: pci_enable_device failed on resume\n",
|
||||
dev->name);
|
||||
return err;
|
||||
}
|
||||
pci_restore_state(pdev);
|
||||
|
||||
err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED,
|
||||
dev->name, priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR "%s: cannot re-allocate IRQ on resume\n",
|
||||
dev->name);
|
||||
pci_disable_device(pdev);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
err = orinoco_up(priv);
|
||||
|
||||
return err;
|
||||
}
|
||||
#else
|
||||
#define orinoco_pci_suspend NULL
|
||||
#define orinoco_pci_resume NULL
|
||||
#endif
|
||||
|
||||
#endif /* _ORINOCO_PCI_H */
|
371
drivers/net/wireless/intersil/orinoco/orinoco_plx.c
Normal file
371
drivers/net/wireless/intersil/orinoco/orinoco_plx.c
Normal file
@@ -0,0 +1,371 @@
|
||||
/* orinoco_plx.c
|
||||
*
|
||||
* Driver for Prism II devices which would usually be driven by orinoco_cs,
|
||||
* but are connected to the PCI bus by a PLX9052.
|
||||
*
|
||||
* Current maintainers are:
|
||||
* Pavel Roskin <proski AT gnu.org>
|
||||
* and David Gibson <hermes AT gibson.dropbear.id.au>
|
||||
*
|
||||
* (C) Copyright David Gibson, IBM Corp. 2001-2003.
|
||||
* Copyright (C) 2001 Daniel Barlow
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Here's the general details on how the PLX9052 adapter works:
|
||||
*
|
||||
* - Two PCI I/O address spaces, one 0x80 long which contains the
|
||||
* PLX9052 registers, and one that's 0x40 long mapped to the PCMCIA
|
||||
* slot I/O address space.
|
||||
*
|
||||
* - One PCI memory address space, mapped to the PCMCIA attribute space
|
||||
* (containing the CIS).
|
||||
*
|
||||
* Using the later, you can read through the CIS data to make sure the
|
||||
* card is compatible with the driver. Keep in mind that the PCMCIA
|
||||
* spec specifies the CIS as the lower 8 bits of each word read from
|
||||
* the CIS, so to read the bytes of the CIS, read every other byte
|
||||
* (0,2,4,...). Passing that test, you need to enable the I/O address
|
||||
* space on the PCMCIA card via the PCMCIA COR register. This is the
|
||||
* first byte following the CIS. In my case (which may not have any
|
||||
* relation to what's on the PRISM2 cards), COR was at offset 0x800
|
||||
* within the PCI memory space. Write 0x41 to the COR register to
|
||||
* enable I/O mode and to select level triggered interrupts. To
|
||||
* confirm you actually succeeded, read the COR register back and make
|
||||
* sure it actually got set to 0x41, in case you have an unexpected
|
||||
* card inserted.
|
||||
*
|
||||
* Following that, you can treat the second PCI I/O address space (the
|
||||
* one that's not 0x80 in length) as the PCMCIA I/O space.
|
||||
*
|
||||
* Note that in the Eumitcom's source for their drivers, they register
|
||||
* the interrupt as edge triggered when registering it with the
|
||||
* Windows kernel. I don't recall how to register edge triggered on
|
||||
* Linux (if it can be done at all). But in some experimentation, I
|
||||
* don't see much operational difference between using either
|
||||
* interrupt mode. Don't mess with the interrupt mode in the COR
|
||||
* register though, as the PLX9052 wants level triggers with the way
|
||||
* the serial EEPROM configures it on the WL11000.
|
||||
*
|
||||
* There's some other little quirks related to timing that I bumped
|
||||
* into, but I don't recall right now. Also, there's two variants of
|
||||
* the WL11000 I've seen, revision A1 and T2. These seem to differ
|
||||
* slightly in the timings configured in the wait-state generator in
|
||||
* the PLX9052. There have also been some comments from Eumitcom that
|
||||
* cards shouldn't be hot swapped, apparently due to risk of cooking
|
||||
* the PLX9052. I'm unsure why they believe this, as I can't see
|
||||
* anything in the design that would really cause a problem, except
|
||||
* for crashing drivers not written to expect it. And having developed
|
||||
* drivers for the WL11000, I'd say it's quite tricky to write code
|
||||
* that will successfully deal with a hot unplug. Very odd things
|
||||
* happen on the I/O side of things. But anyway, be warned. Despite
|
||||
* that, I've hot-swapped a number of times during debugging and
|
||||
* driver development for various reasons (stuck WAIT# line after the
|
||||
* radio card's firmware locks up).
|
||||
*/
|
||||
|
||||
#define DRIVER_NAME "orinoco_plx"
|
||||
#define PFX DRIVER_NAME ": "
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
|
||||
#include "orinoco.h"
|
||||
#include "orinoco_pci.h"
|
||||
|
||||
#define COR_OFFSET (0x3e0) /* 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 COR_RESET (0x80) /* reset bit in the COR register */
|
||||
#define PLX_RESET_TIME (500) /* milliseconds */
|
||||
|
||||
#define PLX_INTCSR 0x4c /* Interrupt Control & Status Register */
|
||||
#define PLX_INTCSR_INTEN (1 << 6) /* Interrupt Enable bit */
|
||||
|
||||
/*
|
||||
* Do a soft reset of the card using the Configuration Option Register
|
||||
*/
|
||||
static int orinoco_plx_cor_reset(struct orinoco_private *priv)
|
||||
{
|
||||
struct hermes *hw = &priv->hw;
|
||||
struct orinoco_pci_card *card = priv->card;
|
||||
unsigned long timeout;
|
||||
u16 reg;
|
||||
|
||||
iowrite8(COR_VALUE | COR_RESET, card->attr_io + COR_OFFSET);
|
||||
mdelay(1);
|
||||
|
||||
iowrite8(COR_VALUE, card->attr_io + COR_OFFSET);
|
||||
mdelay(1);
|
||||
|
||||
/* Just in case, wait more until the card is no longer busy */
|
||||
timeout = jiffies + msecs_to_jiffies(PLX_RESET_TIME);
|
||||
reg = hermes_read_regn(hw, CMD);
|
||||
while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) {
|
||||
mdelay(1);
|
||||
reg = hermes_read_regn(hw, CMD);
|
||||
}
|
||||
|
||||
/* Still busy? */
|
||||
if (reg & HERMES_CMD_BUSY) {
|
||||
printk(KERN_ERR PFX "Busy timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int orinoco_plx_hw_init(struct orinoco_pci_card *card)
|
||||
{
|
||||
int i;
|
||||
u32 csr_reg;
|
||||
static const u8 cis_magic[] = {
|
||||
0x01, 0x03, 0x00, 0x00, 0xff, 0x17, 0x04, 0x67
|
||||
};
|
||||
|
||||
printk(KERN_DEBUG PFX "CIS: ");
|
||||
for (i = 0; i < 16; i++)
|
||||
printk("%02X:", ioread8(card->attr_io + (i << 1)));
|
||||
printk("\n");
|
||||
|
||||
/* Verify whether a supported PC card is present */
|
||||
/* FIXME: we probably need to be smarted about this */
|
||||
for (i = 0; i < sizeof(cis_magic); i++) {
|
||||
if (cis_magic[i] != ioread8(card->attr_io + (i << 1))) {
|
||||
printk(KERN_ERR PFX "The CIS value of Prism2 PC "
|
||||
"card is unexpected\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
csr_reg = ioread32(card->bridge_io + PLX_INTCSR);
|
||||
if (!(csr_reg & PLX_INTCSR_INTEN)) {
|
||||
csr_reg |= PLX_INTCSR_INTEN;
|
||||
iowrite32(csr_reg, card->bridge_io + PLX_INTCSR);
|
||||
csr_reg = ioread32(card->bridge_io + PLX_INTCSR);
|
||||
if (!(csr_reg & PLX_INTCSR_INTEN)) {
|
||||
printk(KERN_ERR PFX "Cannot enable interrupts\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int orinoco_plx_init_one(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
int err;
|
||||
struct orinoco_private *priv;
|
||||
struct orinoco_pci_card *card;
|
||||
void __iomem *hermes_io, *attr_io, *bridge_io;
|
||||
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Cannot enable PCI device\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pci_request_regions(pdev, DRIVER_NAME);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
|
||||
goto fail_resources;
|
||||
}
|
||||
|
||||
bridge_io = pci_iomap(pdev, 1, 0);
|
||||
if (!bridge_io) {
|
||||
printk(KERN_ERR PFX "Cannot map bridge registers\n");
|
||||
err = -EIO;
|
||||
goto fail_map_bridge;
|
||||
}
|
||||
|
||||
attr_io = pci_iomap(pdev, 2, 0);
|
||||
if (!attr_io) {
|
||||
printk(KERN_ERR PFX "Cannot map PCMCIA attributes\n");
|
||||
err = -EIO;
|
||||
goto fail_map_attr;
|
||||
}
|
||||
|
||||
hermes_io = pci_iomap(pdev, 3, 0);
|
||||
if (!hermes_io) {
|
||||
printk(KERN_ERR PFX "Cannot map chipset registers\n");
|
||||
err = -EIO;
|
||||
goto fail_map_hermes;
|
||||
}
|
||||
|
||||
/* Allocate network device */
|
||||
priv = alloc_orinocodev(sizeof(*card), &pdev->dev,
|
||||
orinoco_plx_cor_reset, NULL);
|
||||
if (!priv) {
|
||||
printk(KERN_ERR PFX "Cannot allocate network device\n");
|
||||
err = -ENOMEM;
|
||||
goto fail_alloc;
|
||||
}
|
||||
|
||||
card = priv->card;
|
||||
card->bridge_io = bridge_io;
|
||||
card->attr_io = attr_io;
|
||||
|
||||
hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING);
|
||||
|
||||
err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED,
|
||||
DRIVER_NAME, priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq);
|
||||
err = -EBUSY;
|
||||
goto fail_irq;
|
||||
}
|
||||
|
||||
err = orinoco_plx_hw_init(card);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Hardware initialization failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = orinoco_plx_cor_reset(priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Initial reset failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = orinoco_init(priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "orinoco_init() failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = orinoco_if_add(priv, 0, 0, NULL);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
|
||||
goto fail_wiphy;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, priv);
|
||||
|
||||
return 0;
|
||||
|
||||
fail_wiphy:
|
||||
wiphy_unregister(priv_to_wiphy(priv));
|
||||
fail:
|
||||
free_irq(pdev->irq, priv);
|
||||
|
||||
fail_irq:
|
||||
free_orinocodev(priv);
|
||||
|
||||
fail_alloc:
|
||||
pci_iounmap(pdev, hermes_io);
|
||||
|
||||
fail_map_hermes:
|
||||
pci_iounmap(pdev, attr_io);
|
||||
|
||||
fail_map_attr:
|
||||
pci_iounmap(pdev, bridge_io);
|
||||
|
||||
fail_map_bridge:
|
||||
pci_release_regions(pdev);
|
||||
|
||||
fail_resources:
|
||||
pci_disable_device(pdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void orinoco_plx_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
struct orinoco_private *priv = pci_get_drvdata(pdev);
|
||||
struct orinoco_pci_card *card = priv->card;
|
||||
|
||||
orinoco_if_del(priv);
|
||||
wiphy_unregister(priv_to_wiphy(priv));
|
||||
free_irq(pdev->irq, priv);
|
||||
free_orinocodev(priv);
|
||||
pci_iounmap(pdev, priv->hw.iobase);
|
||||
pci_iounmap(pdev, card->attr_io);
|
||||
pci_iounmap(pdev, card->bridge_io);
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id orinoco_plx_id_table[] = {
|
||||
{0x111a, 0x1023, PCI_ANY_ID, PCI_ANY_ID,}, /* Siemens SpeedStream SS1023 */
|
||||
{0x1385, 0x4100, PCI_ANY_ID, PCI_ANY_ID,}, /* Netgear MA301 */
|
||||
{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 */
|
||||
{0x16ab, 0x1100, PCI_ANY_ID, PCI_ANY_ID,}, /* Global Sun Tech GL24110P */
|
||||
{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 tested by
|
||||
Brendan W. McAdams <rit AT jacked-in.org> */
|
||||
{0x10b7, 0x7770, PCI_ANY_ID, PCI_ANY_ID,}, /* 3Com AirConnect PCI tested by
|
||||
Damien Persohn <damien AT persohn.net> */
|
||||
{0,},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, orinoco_plx_id_table);
|
||||
|
||||
static struct pci_driver orinoco_plx_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = orinoco_plx_id_table,
|
||||
.probe = orinoco_plx_init_one,
|
||||
.remove = orinoco_plx_remove_one,
|
||||
.suspend = orinoco_pci_suspend,
|
||||
.resume = orinoco_pci_resume,
|
||||
};
|
||||
|
||||
static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
|
||||
" (Pavel Roskin <proski@gnu.org>,"
|
||||
" David Gibson <hermes@gibson.dropbear.id.au>,"
|
||||
" 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_LICENSE("Dual MPL/GPL");
|
||||
|
||||
static int __init orinoco_plx_init(void)
|
||||
{
|
||||
printk(KERN_DEBUG "%s\n", version);
|
||||
return pci_register_driver(&orinoco_plx_driver);
|
||||
}
|
||||
|
||||
static void __exit orinoco_plx_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&orinoco_plx_driver);
|
||||
}
|
||||
|
||||
module_init(orinoco_plx_init);
|
||||
module_exit(orinoco_plx_exit);
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* End:
|
||||
*/
|
246
drivers/net/wireless/intersil/orinoco/orinoco_tmd.c
Normal file
246
drivers/net/wireless/intersil/orinoco/orinoco_tmd.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/* orinoco_tmd.c
|
||||
*
|
||||
* Driver for Prism II devices which would usually be driven by orinoco_cs,
|
||||
* but are connected to the PCI bus by a TMD7160.
|
||||
*
|
||||
* Copyright (C) 2003 Joerg Dorchain <joerg AT dorchain.net>
|
||||
* based heavily upon orinoco_plx.c Copyright (C) 2001 Daniel Barlow
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The actual driving is done by main.c, this is just resource
|
||||
* allocation stuff.
|
||||
*
|
||||
* This driver is modeled after the orinoco_plx driver. The main
|
||||
* difference is that the TMD chip has only IO port ranges and doesn't
|
||||
* provide access to the PCMCIA attribute space.
|
||||
*
|
||||
* Pheecom sells cards with the TMD chip as "ASIC version"
|
||||
*/
|
||||
|
||||
#define DRIVER_NAME "orinoco_tmd"
|
||||
#define PFX DRIVER_NAME ": "
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
|
||||
#include "orinoco.h"
|
||||
#include "orinoco_pci.h"
|
||||
|
||||
#define COR_VALUE (COR_LEVEL_REQ | COR_FUNC_ENA) /* Enable PC card with interrupt in level trigger */
|
||||
#define COR_RESET (0x80) /* reset bit in the COR register */
|
||||
#define TMD_RESET_TIME (500) /* milliseconds */
|
||||
|
||||
/*
|
||||
* Do a soft reset of the card using the Configuration Option Register
|
||||
*/
|
||||
static int orinoco_tmd_cor_reset(struct orinoco_private *priv)
|
||||
{
|
||||
struct hermes *hw = &priv->hw;
|
||||
struct orinoco_pci_card *card = priv->card;
|
||||
unsigned long timeout;
|
||||
u16 reg;
|
||||
|
||||
iowrite8(COR_VALUE | COR_RESET, card->bridge_io);
|
||||
mdelay(1);
|
||||
|
||||
iowrite8(COR_VALUE, card->bridge_io);
|
||||
mdelay(1);
|
||||
|
||||
/* Just in case, wait more until the card is no longer busy */
|
||||
timeout = jiffies + msecs_to_jiffies(TMD_RESET_TIME);
|
||||
reg = hermes_read_regn(hw, CMD);
|
||||
while (time_before(jiffies, timeout) && (reg & HERMES_CMD_BUSY)) {
|
||||
mdelay(1);
|
||||
reg = hermes_read_regn(hw, CMD);
|
||||
}
|
||||
|
||||
/* Still busy? */
|
||||
if (reg & HERMES_CMD_BUSY) {
|
||||
printk(KERN_ERR PFX "Busy timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int orinoco_tmd_init_one(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
int err;
|
||||
struct orinoco_private *priv;
|
||||
struct orinoco_pci_card *card;
|
||||
void __iomem *hermes_io, *bridge_io;
|
||||
|
||||
err = pci_enable_device(pdev);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Cannot enable PCI device\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = pci_request_regions(pdev, DRIVER_NAME);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Cannot obtain PCI resources\n");
|
||||
goto fail_resources;
|
||||
}
|
||||
|
||||
bridge_io = pci_iomap(pdev, 1, 0);
|
||||
if (!bridge_io) {
|
||||
printk(KERN_ERR PFX "Cannot map bridge registers\n");
|
||||
err = -EIO;
|
||||
goto fail_map_bridge;
|
||||
}
|
||||
|
||||
hermes_io = pci_iomap(pdev, 2, 0);
|
||||
if (!hermes_io) {
|
||||
printk(KERN_ERR PFX "Cannot map chipset registers\n");
|
||||
err = -EIO;
|
||||
goto fail_map_hermes;
|
||||
}
|
||||
|
||||
/* Allocate network device */
|
||||
priv = alloc_orinocodev(sizeof(*card), &pdev->dev,
|
||||
orinoco_tmd_cor_reset, NULL);
|
||||
if (!priv) {
|
||||
printk(KERN_ERR PFX "Cannot allocate network device\n");
|
||||
err = -ENOMEM;
|
||||
goto fail_alloc;
|
||||
}
|
||||
|
||||
card = priv->card;
|
||||
card->bridge_io = bridge_io;
|
||||
|
||||
hermes_struct_init(&priv->hw, hermes_io, HERMES_16BIT_REGSPACING);
|
||||
|
||||
err = request_irq(pdev->irq, orinoco_interrupt, IRQF_SHARED,
|
||||
DRIVER_NAME, priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Cannot allocate IRQ %d\n", pdev->irq);
|
||||
err = -EBUSY;
|
||||
goto fail_irq;
|
||||
}
|
||||
|
||||
err = orinoco_tmd_cor_reset(priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "Initial reset failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = orinoco_init(priv);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "orinoco_init() failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = orinoco_if_add(priv, 0, 0, NULL);
|
||||
if (err) {
|
||||
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, priv);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
free_irq(pdev->irq, priv);
|
||||
|
||||
fail_irq:
|
||||
free_orinocodev(priv);
|
||||
|
||||
fail_alloc:
|
||||
pci_iounmap(pdev, hermes_io);
|
||||
|
||||
fail_map_hermes:
|
||||
pci_iounmap(pdev, bridge_io);
|
||||
|
||||
fail_map_bridge:
|
||||
pci_release_regions(pdev);
|
||||
|
||||
fail_resources:
|
||||
pci_disable_device(pdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void orinoco_tmd_remove_one(struct pci_dev *pdev)
|
||||
{
|
||||
struct orinoco_private *priv = pci_get_drvdata(pdev);
|
||||
struct orinoco_pci_card *card = priv->card;
|
||||
|
||||
orinoco_if_del(priv);
|
||||
free_irq(pdev->irq, priv);
|
||||
free_orinocodev(priv);
|
||||
pci_iounmap(pdev, priv->hw.iobase);
|
||||
pci_iounmap(pdev, card->bridge_io);
|
||||
pci_release_regions(pdev);
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static const struct pci_device_id orinoco_tmd_id_table[] = {
|
||||
{0x15e8, 0x0131, PCI_ANY_ID, PCI_ANY_ID,}, /* NDC and OEMs, e.g. pheecom */
|
||||
{0,},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, orinoco_tmd_id_table);
|
||||
|
||||
static struct pci_driver orinoco_tmd_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = orinoco_tmd_id_table,
|
||||
.probe = orinoco_tmd_init_one,
|
||||
.remove = orinoco_tmd_remove_one,
|
||||
.suspend = orinoco_pci_suspend,
|
||||
.resume = orinoco_pci_resume,
|
||||
};
|
||||
|
||||
static char version[] __initdata = DRIVER_NAME " " DRIVER_VERSION
|
||||
" (Joerg Dorchain <joerg@dorchain.net>)";
|
||||
MODULE_AUTHOR("Joerg Dorchain <joerg@dorchain.net>");
|
||||
MODULE_DESCRIPTION("Driver for wireless LAN cards using the TMD7160 PCI bridge");
|
||||
MODULE_LICENSE("Dual MPL/GPL");
|
||||
|
||||
static int __init orinoco_tmd_init(void)
|
||||
{
|
||||
printk(KERN_DEBUG "%s\n", version);
|
||||
return pci_register_driver(&orinoco_tmd_driver);
|
||||
}
|
||||
|
||||
static void __exit orinoco_tmd_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&orinoco_tmd_driver);
|
||||
}
|
||||
|
||||
module_init(orinoco_tmd_init);
|
||||
module_exit(orinoco_tmd_exit);
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-indent-level: 8
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* End:
|
||||
*/
|
1748
drivers/net/wireless/intersil/orinoco/orinoco_usb.c
Normal file
1748
drivers/net/wireless/intersil/orinoco/orinoco_usb.c
Normal file
File diff suppressed because it is too large
Load Diff
251
drivers/net/wireless/intersil/orinoco/scan.c
Normal file
251
drivers/net/wireless/intersil/orinoco/scan.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/* Helpers for managing scan queues
|
||||
*
|
||||
* See copyright notice in main.c
|
||||
*/
|
||||
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/ieee80211.h>
|
||||
#include <net/cfg80211.h>
|
||||
|
||||
#include "hermes.h"
|
||||
#include "orinoco.h"
|
||||
#include "main.h"
|
||||
|
||||
#include "scan.h"
|
||||
|
||||
#define ZERO_DBM_OFFSET 0x95
|
||||
#define MAX_SIGNAL_LEVEL 0x8A
|
||||
#define MIN_SIGNAL_LEVEL 0x2F
|
||||
|
||||
#define SIGNAL_TO_DBM(x) \
|
||||
(clamp_t(s32, (x), MIN_SIGNAL_LEVEL, MAX_SIGNAL_LEVEL) \
|
||||
- ZERO_DBM_OFFSET)
|
||||
#define SIGNAL_TO_MBM(x) (SIGNAL_TO_DBM(x) * 100)
|
||||
|
||||
static int symbol_build_supp_rates(u8 *buf, const __le16 *rates)
|
||||
{
|
||||
int i;
|
||||
u8 rate;
|
||||
|
||||
buf[0] = WLAN_EID_SUPP_RATES;
|
||||
for (i = 0; i < 5; i++) {
|
||||
rate = le16_to_cpu(rates[i]);
|
||||
/* NULL terminated */
|
||||
if (rate == 0x0)
|
||||
break;
|
||||
buf[i + 2] = rate;
|
||||
}
|
||||
buf[1] = i;
|
||||
|
||||
return i + 2;
|
||||
}
|
||||
|
||||
static int prism_build_supp_rates(u8 *buf, const u8 *rates)
|
||||
{
|
||||
int i;
|
||||
|
||||
buf[0] = WLAN_EID_SUPP_RATES;
|
||||
for (i = 0; i < 8; i++) {
|
||||
/* NULL terminated */
|
||||
if (rates[i] == 0x0)
|
||||
break;
|
||||
buf[i + 2] = rates[i];
|
||||
}
|
||||
buf[1] = i;
|
||||
|
||||
/* We might still have another 2 rates, which need to go in
|
||||
* extended supported rates */
|
||||
if (i == 8 && rates[i] > 0) {
|
||||
buf[10] = WLAN_EID_EXT_SUPP_RATES;
|
||||
for (; i < 10; i++) {
|
||||
/* NULL terminated */
|
||||
if (rates[i] == 0x0)
|
||||
break;
|
||||
buf[i + 2] = rates[i];
|
||||
}
|
||||
buf[11] = i - 8;
|
||||
}
|
||||
|
||||
return (i < 8) ? i + 2 : i + 4;
|
||||
}
|
||||
|
||||
static void orinoco_add_hostscan_result(struct orinoco_private *priv,
|
||||
const union hermes_scan_info *bss)
|
||||
{
|
||||
struct wiphy *wiphy = priv_to_wiphy(priv);
|
||||
struct ieee80211_channel *channel;
|
||||
struct cfg80211_bss *cbss;
|
||||
u8 *ie;
|
||||
u8 ie_buf[46];
|
||||
u64 timestamp;
|
||||
s32 signal;
|
||||
u16 capability;
|
||||
u16 beacon_interval;
|
||||
int ie_len;
|
||||
int freq;
|
||||
int len;
|
||||
|
||||
len = le16_to_cpu(bss->a.essid_len);
|
||||
|
||||
/* Reconstruct SSID and bitrate IEs to pass up */
|
||||
ie_buf[0] = WLAN_EID_SSID;
|
||||
ie_buf[1] = len;
|
||||
memcpy(&ie_buf[2], bss->a.essid, len);
|
||||
|
||||
ie = ie_buf + len + 2;
|
||||
ie_len = ie_buf[1] + 2;
|
||||
switch (priv->firmware_type) {
|
||||
case FIRMWARE_TYPE_SYMBOL:
|
||||
ie_len += symbol_build_supp_rates(ie, bss->s.rates);
|
||||
break;
|
||||
|
||||
case FIRMWARE_TYPE_INTERSIL:
|
||||
ie_len += prism_build_supp_rates(ie, bss->p.rates);
|
||||
break;
|
||||
|
||||
case FIRMWARE_TYPE_AGERE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
freq = ieee80211_channel_to_frequency(
|
||||
le16_to_cpu(bss->a.channel), IEEE80211_BAND_2GHZ);
|
||||
channel = ieee80211_get_channel(wiphy, freq);
|
||||
if (!channel) {
|
||||
printk(KERN_DEBUG "Invalid channel designation %04X(%04X)",
|
||||
bss->a.channel, freq);
|
||||
return; /* Then ignore it for now */
|
||||
}
|
||||
timestamp = 0;
|
||||
capability = le16_to_cpu(bss->a.capabilities);
|
||||
beacon_interval = le16_to_cpu(bss->a.beacon_interv);
|
||||
signal = SIGNAL_TO_MBM(le16_to_cpu(bss->a.level));
|
||||
|
||||
cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN,
|
||||
bss->a.bssid, timestamp, capability,
|
||||
beacon_interval, ie_buf, ie_len, signal,
|
||||
GFP_KERNEL);
|
||||
cfg80211_put_bss(wiphy, cbss);
|
||||
}
|
||||
|
||||
void orinoco_add_extscan_result(struct orinoco_private *priv,
|
||||
struct agere_ext_scan_info *bss,
|
||||
size_t len)
|
||||
{
|
||||
struct wiphy *wiphy = priv_to_wiphy(priv);
|
||||
struct ieee80211_channel *channel;
|
||||
struct cfg80211_bss *cbss;
|
||||
const u8 *ie;
|
||||
u64 timestamp;
|
||||
s32 signal;
|
||||
u16 capability;
|
||||
u16 beacon_interval;
|
||||
size_t ie_len;
|
||||
int chan, freq;
|
||||
|
||||
ie_len = len - sizeof(*bss);
|
||||
ie = cfg80211_find_ie(WLAN_EID_DS_PARAMS, bss->data, ie_len);
|
||||
chan = ie ? ie[2] : 0;
|
||||
freq = ieee80211_channel_to_frequency(chan, IEEE80211_BAND_2GHZ);
|
||||
channel = ieee80211_get_channel(wiphy, freq);
|
||||
|
||||
timestamp = le64_to_cpu(bss->timestamp);
|
||||
capability = le16_to_cpu(bss->capabilities);
|
||||
beacon_interval = le16_to_cpu(bss->beacon_interval);
|
||||
ie = bss->data;
|
||||
signal = SIGNAL_TO_MBM(bss->level);
|
||||
|
||||
cbss = cfg80211_inform_bss(wiphy, channel, CFG80211_BSS_FTYPE_UNKNOWN,
|
||||
bss->bssid, timestamp, capability,
|
||||
beacon_interval, ie, ie_len, signal,
|
||||
GFP_KERNEL);
|
||||
cfg80211_put_bss(wiphy, cbss);
|
||||
}
|
||||
|
||||
void orinoco_add_hostscan_results(struct orinoco_private *priv,
|
||||
unsigned char *buf,
|
||||
size_t len)
|
||||
{
|
||||
int offset; /* In the scan data */
|
||||
size_t atom_len;
|
||||
bool abort = false;
|
||||
|
||||
switch (priv->firmware_type) {
|
||||
case FIRMWARE_TYPE_AGERE:
|
||||
atom_len = sizeof(struct agere_scan_apinfo);
|
||||
offset = 0;
|
||||
break;
|
||||
|
||||
case FIRMWARE_TYPE_SYMBOL:
|
||||
/* Lack of documentation necessitates this hack.
|
||||
* Different firmwares have 68 or 76 byte long atoms.
|
||||
* We try modulo first. If the length divides by both,
|
||||
* we check what would be the channel in the second
|
||||
* frame for a 68-byte atom. 76-byte atoms have 0 there.
|
||||
* Valid channel cannot be 0. */
|
||||
if (len % 76)
|
||||
atom_len = 68;
|
||||
else if (len % 68)
|
||||
atom_len = 76;
|
||||
else if (len >= 1292 && buf[68] == 0)
|
||||
atom_len = 76;
|
||||
else
|
||||
atom_len = 68;
|
||||
offset = 0;
|
||||
break;
|
||||
|
||||
case FIRMWARE_TYPE_INTERSIL:
|
||||
offset = 4;
|
||||
if (priv->has_hostscan) {
|
||||
atom_len = le16_to_cpup((__le16 *)buf);
|
||||
/* Sanity check for atom_len */
|
||||
if (atom_len < sizeof(struct prism2_scan_apinfo)) {
|
||||
printk(KERN_ERR "%s: Invalid atom_len in scan "
|
||||
"data: %zu\n", priv->ndev->name,
|
||||
atom_len);
|
||||
abort = true;
|
||||
goto scan_abort;
|
||||
}
|
||||
} else
|
||||
atom_len = offsetof(struct prism2_scan_apinfo, atim);
|
||||
break;
|
||||
|
||||
default:
|
||||
abort = true;
|
||||
goto scan_abort;
|
||||
}
|
||||
|
||||
/* Check that we got an whole number of atoms */
|
||||
if ((len - offset) % atom_len) {
|
||||
printk(KERN_ERR "%s: Unexpected scan data length %zu, "
|
||||
"atom_len %zu, offset %d\n", priv->ndev->name, len,
|
||||
atom_len, offset);
|
||||
abort = true;
|
||||
goto scan_abort;
|
||||
}
|
||||
|
||||
/* Process the entries one by one */
|
||||
for (; offset + atom_len <= len; offset += atom_len) {
|
||||
union hermes_scan_info *atom;
|
||||
|
||||
atom = (union hermes_scan_info *) (buf + offset);
|
||||
|
||||
orinoco_add_hostscan_result(priv, atom);
|
||||
}
|
||||
|
||||
scan_abort:
|
||||
if (priv->scan_request) {
|
||||
cfg80211_scan_done(priv->scan_request, abort);
|
||||
priv->scan_request = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void orinoco_scan_done(struct orinoco_private *priv, bool abort)
|
||||
{
|
||||
if (priv->scan_request) {
|
||||
cfg80211_scan_done(priv->scan_request, abort);
|
||||
priv->scan_request = NULL;
|
||||
}
|
||||
}
|
21
drivers/net/wireless/intersil/orinoco/scan.h
Normal file
21
drivers/net/wireless/intersil/orinoco/scan.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* Helpers for managing scan queues
|
||||
*
|
||||
* See copyright notice in main.c
|
||||
*/
|
||||
#ifndef _ORINOCO_SCAN_H_
|
||||
#define _ORINOCO_SCAN_H_
|
||||
|
||||
/* Forward declarations */
|
||||
struct orinoco_private;
|
||||
struct agere_ext_scan_info;
|
||||
|
||||
/* Add scan results */
|
||||
void orinoco_add_extscan_result(struct orinoco_private *priv,
|
||||
struct agere_ext_scan_info *atom,
|
||||
size_t len);
|
||||
void orinoco_add_hostscan_results(struct orinoco_private *dev,
|
||||
unsigned char *buf,
|
||||
size_t len);
|
||||
void orinoco_scan_done(struct orinoco_private *priv, bool abort);
|
||||
|
||||
#endif /* _ORINOCO_SCAN_H_ */
|
320
drivers/net/wireless/intersil/orinoco/spectrum_cs.c
Normal file
320
drivers/net/wireless/intersil/orinoco/spectrum_cs.c
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* Driver for 802.11b cards using RAM-loadable Symbol firmware, such as
|
||||
* Symbol Wireless Networker LA4137, CompactFlash cards by Socket
|
||||
* Communications and Intel PRO/Wireless 2011B.
|
||||
*
|
||||
* The driver implements Symbol firmware download. The rest is handled
|
||||
* in hermes.c and main.c.
|
||||
*
|
||||
* Utilities for downloading the Symbol firmware are available at
|
||||
* http://sourceforge.net/projects/orinoco/
|
||||
*
|
||||
* Copyright (C) 2002-2005 Pavel Roskin <proski@gnu.org>
|
||||
* Portions based on orinoco_cs.c:
|
||||
* Copyright (C) David Gibson, Linuxcare Australia
|
||||
* Portions based on Spectrum24tDnld.c from original spectrum24 driver:
|
||||
* Copyright (C) Symbol Technologies.
|
||||
*
|
||||
* See copyright notice in file main.c.
|
||||
*/
|
||||
|
||||
#define DRIVER_NAME "spectrum_cs"
|
||||
#define PFX DRIVER_NAME ": "
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/cisreg.h>
|
||||
#include <pcmcia/ds.h>
|
||||
|
||||
#include "orinoco.h"
|
||||
|
||||
/********************************************************************/
|
||||
/* Module stuff */
|
||||
/********************************************************************/
|
||||
|
||||
MODULE_AUTHOR("Pavel Roskin <proski@gnu.org>");
|
||||
MODULE_DESCRIPTION("Driver for Symbol Spectrum24 Trilogy cards with firmware downloader");
|
||||
MODULE_LICENSE("Dual MPL/GPL");
|
||||
|
||||
/* Module parameters */
|
||||
|
||||
/* Some D-Link cards have buggy CIS. They do work at 5v properly, but
|
||||
* don't have any CIS entry for it. This workaround it... */
|
||||
static int ignore_cis_vcc; /* = 0 */
|
||||
module_param(ignore_cis_vcc, int, 0);
|
||||
MODULE_PARM_DESC(ignore_cis_vcc, "Allow voltage mismatch between card and socket");
|
||||
|
||||
/********************************************************************/
|
||||
/* Data structures */
|
||||
/********************************************************************/
|
||||
|
||||
/* PCMCIA specific device information (goes in the card field of
|
||||
* struct orinoco_private */
|
||||
struct orinoco_pccard {
|
||||
struct pcmcia_device *p_dev;
|
||||
};
|
||||
|
||||
/********************************************************************/
|
||||
/* Function prototypes */
|
||||
/********************************************************************/
|
||||
|
||||
static int spectrum_cs_config(struct pcmcia_device *link);
|
||||
static void spectrum_cs_release(struct pcmcia_device *link);
|
||||
|
||||
/* Constants for the CISREG_CCSR register */
|
||||
#define HCR_RUN 0x07 /* run firmware after reset */
|
||||
#define HCR_IDLE 0x0E /* don't run firmware after reset */
|
||||
#define HCR_MEM16 0x10 /* memory width bit, should be preserved */
|
||||
|
||||
|
||||
/*
|
||||
* Reset the card using configuration registers COR and CCSR.
|
||||
* If IDLE is 1, stop the firmware, so that it can be safely rewritten.
|
||||
*/
|
||||
static int
|
||||
spectrum_reset(struct pcmcia_device *link, int idle)
|
||||
{
|
||||
int ret;
|
||||
u8 save_cor;
|
||||
u8 ccsr;
|
||||
|
||||
/* Doing it if hardware is gone is guaranteed crash */
|
||||
if (!pcmcia_dev_present(link))
|
||||
return -ENODEV;
|
||||
|
||||
/* Save original COR value */
|
||||
ret = pcmcia_read_config_byte(link, CISREG_COR, &save_cor);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
/* Soft-Reset card */
|
||||
ret = pcmcia_write_config_byte(link, CISREG_COR,
|
||||
(save_cor | COR_SOFT_RESET));
|
||||
if (ret)
|
||||
goto failed;
|
||||
udelay(1000);
|
||||
|
||||
/* Read CCSR */
|
||||
ret = pcmcia_read_config_byte(link, CISREG_CCSR, &ccsr);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
/*
|
||||
* Start or stop the firmware. Memory width bit should be
|
||||
* preserved from the value we've just read.
|
||||
*/
|
||||
ccsr = (idle ? HCR_IDLE : HCR_RUN) | (ccsr & HCR_MEM16);
|
||||
ret = pcmcia_write_config_byte(link, CISREG_CCSR, ccsr);
|
||||
if (ret)
|
||||
goto failed;
|
||||
udelay(1000);
|
||||
|
||||
/* Restore original COR configuration index */
|
||||
ret = pcmcia_write_config_byte(link, CISREG_COR,
|
||||
(save_cor & ~COR_SOFT_RESET));
|
||||
if (ret)
|
||||
goto failed;
|
||||
udelay(1000);
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/********************************************************************/
|
||||
/* Device methods */
|
||||
/********************************************************************/
|
||||
|
||||
static int
|
||||
spectrum_cs_hard_reset(struct orinoco_private *priv)
|
||||
{
|
||||
struct orinoco_pccard *card = priv->card;
|
||||
struct pcmcia_device *link = card->p_dev;
|
||||
|
||||
/* Soft reset using COR and HCR */
|
||||
spectrum_reset(link, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
spectrum_cs_stop_firmware(struct orinoco_private *priv, int idle)
|
||||
{
|
||||
struct orinoco_pccard *card = priv->card;
|
||||
struct pcmcia_device *link = card->p_dev;
|
||||
|
||||
return spectrum_reset(link, idle);
|
||||
}
|
||||
|
||||
/********************************************************************/
|
||||
/* PCMCIA stuff */
|
||||
/********************************************************************/
|
||||
|
||||
static int
|
||||
spectrum_cs_probe(struct pcmcia_device *link)
|
||||
{
|
||||
struct orinoco_private *priv;
|
||||
struct orinoco_pccard *card;
|
||||
|
||||
priv = alloc_orinocodev(sizeof(*card), &link->dev,
|
||||
spectrum_cs_hard_reset,
|
||||
spectrum_cs_stop_firmware);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
card = priv->card;
|
||||
|
||||
/* Link both structures together */
|
||||
card->p_dev = link;
|
||||
link->priv = priv;
|
||||
|
||||
return spectrum_cs_config(link);
|
||||
} /* spectrum_cs_attach */
|
||||
|
||||
static void spectrum_cs_detach(struct pcmcia_device *link)
|
||||
{
|
||||
struct orinoco_private *priv = link->priv;
|
||||
|
||||
orinoco_if_del(priv);
|
||||
|
||||
spectrum_cs_release(link);
|
||||
|
||||
free_orinocodev(priv);
|
||||
} /* spectrum_cs_detach */
|
||||
|
||||
static int spectrum_cs_config_check(struct pcmcia_device *p_dev,
|
||||
void *priv_data)
|
||||
{
|
||||
if (p_dev->config_index == 0)
|
||||
return -EINVAL;
|
||||
|
||||
return pcmcia_request_io(p_dev);
|
||||
};
|
||||
|
||||
static int
|
||||
spectrum_cs_config(struct pcmcia_device *link)
|
||||
{
|
||||
struct orinoco_private *priv = link->priv;
|
||||
struct hermes *hw = &priv->hw;
|
||||
int ret;
|
||||
void __iomem *mem;
|
||||
|
||||
link->config_flags |= CONF_AUTO_SET_VPP | CONF_AUTO_CHECK_VCC |
|
||||
CONF_AUTO_SET_IO | CONF_ENABLE_IRQ;
|
||||
if (ignore_cis_vcc)
|
||||
link->config_flags &= ~CONF_AUTO_CHECK_VCC;
|
||||
ret = pcmcia_loop_config(link, spectrum_cs_config_check, NULL);
|
||||
if (ret) {
|
||||
if (!ignore_cis_vcc)
|
||||
printk(KERN_ERR PFX "GetNextTuple(): No matching "
|
||||
"CIS configuration. Maybe you need the "
|
||||
"ignore_cis_vcc=1 parameter.\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
mem = ioport_map(link->resource[0]->start,
|
||||
resource_size(link->resource[0]));
|
||||
if (!mem)
|
||||
goto failed;
|
||||
|
||||
/* We initialize the hermes structure before completing PCMCIA
|
||||
* configuration just in case the interrupt handler gets
|
||||
* called. */
|
||||
hermes_struct_init(hw, mem, HERMES_16BIT_REGSPACING);
|
||||
hw->eeprom_pda = true;
|
||||
|
||||
ret = pcmcia_request_irq(link, orinoco_interrupt);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
ret = pcmcia_enable_device(link);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
/* Reset card */
|
||||
if (spectrum_cs_hard_reset(priv) != 0)
|
||||
goto failed;
|
||||
|
||||
/* Initialise the main driver */
|
||||
if (orinoco_init(priv) != 0) {
|
||||
printk(KERN_ERR PFX "orinoco_init() failed\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* Register an interface with the stack */
|
||||
if (orinoco_if_add(priv, link->resource[0]->start,
|
||||
link->irq, NULL) != 0) {
|
||||
printk(KERN_ERR PFX "orinoco_if_add() failed\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
failed:
|
||||
spectrum_cs_release(link);
|
||||
return -ENODEV;
|
||||
} /* spectrum_cs_config */
|
||||
|
||||
static void
|
||||
spectrum_cs_release(struct pcmcia_device *link)
|
||||
{
|
||||
struct orinoco_private *priv = link->priv;
|
||||
unsigned long flags;
|
||||
|
||||
/* We're committed to taking the device away now, so mark the
|
||||
* hardware as unavailable */
|
||||
priv->hw.ops->lock_irqsave(&priv->lock, &flags);
|
||||
priv->hw_unavailable++;
|
||||
priv->hw.ops->unlock_irqrestore(&priv->lock, &flags);
|
||||
|
||||
pcmcia_disable_device(link);
|
||||
if (priv->hw.iobase)
|
||||
ioport_unmap(priv->hw.iobase);
|
||||
} /* spectrum_cs_release */
|
||||
|
||||
|
||||
static int
|
||||
spectrum_cs_suspend(struct pcmcia_device *link)
|
||||
{
|
||||
struct orinoco_private *priv = link->priv;
|
||||
int err = 0;
|
||||
|
||||
/* Mark the device as stopped, to block IO until later */
|
||||
orinoco_down(priv);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
spectrum_cs_resume(struct pcmcia_device *link)
|
||||
{
|
||||
struct orinoco_private *priv = link->priv;
|
||||
int err = orinoco_up(priv);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/********************************************************************/
|
||||
/* Module initialization */
|
||||
/********************************************************************/
|
||||
|
||||
static const struct pcmcia_device_id spectrum_cs_ids[] = {
|
||||
PCMCIA_DEVICE_MANF_CARD(0x026c, 0x0001), /* Symbol Spectrum24 LA4137 */
|
||||
PCMCIA_DEVICE_MANF_CARD(0x0104, 0x0001), /* Socket Communications CF */
|
||||
PCMCIA_DEVICE_PROD_ID12("Intel", "PRO/Wireless LAN PC Card", 0x816cc815, 0x6fbf459a), /* 2011B, not 2011 */
|
||||
PCMCIA_DEVICE_NULL,
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, spectrum_cs_ids);
|
||||
|
||||
static struct pcmcia_driver orinoco_driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRIVER_NAME,
|
||||
.probe = spectrum_cs_probe,
|
||||
.remove = spectrum_cs_detach,
|
||||
.suspend = spectrum_cs_suspend,
|
||||
.resume = spectrum_cs_resume,
|
||||
.id_table = spectrum_cs_ids,
|
||||
};
|
||||
module_pcmcia_driver(orinoco_driver);
|
1413
drivers/net/wireless/intersil/orinoco/wext.c
Normal file
1413
drivers/net/wireless/intersil/orinoco/wext.c
Normal file
File diff suppressed because it is too large
Load Diff
13
drivers/net/wireless/intersil/orinoco/wext.h
Normal file
13
drivers/net/wireless/intersil/orinoco/wext.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/* Wireless extensions support.
|
||||
*
|
||||
* See copyright notice in main.c
|
||||
*/
|
||||
#ifndef _ORINOCO_WEXT_H_
|
||||
#define _ORINOCO_WEXT_H_
|
||||
|
||||
#include <net/iw_handler.h>
|
||||
|
||||
/* Structure defining all our WEXT handlers */
|
||||
extern const struct iw_handler_def orinoco_handler_def;
|
||||
|
||||
#endif /* _ORINOCO_WEXT_H_ */
|
Reference in New Issue
Block a user