wifi: remove orphaned orinoco driver
Orinoco is a PIO-only ISA/PCMCIA 802.11b device with extra bus interface connections for PCI/Cardbus/mini-PCI and a few pre-2002 Apple PowerMac variants. It supports both wireless extensions and CFG80211, but I could not tell if it requires using both. This device used to be one of the most common ones 20 years ago, but has been orphaned for most of the time since then, and the conversion to cfg80211 has stalled in 2010. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Kalle Valo <kvalo@kernel.org>
This commit is contained in:
parent
757a46c2a7
commit
1535d5962d
@ -16068,13 +16068,6 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/hubcap/linux.git
|
||||
F: Documentation/filesystems/orangefs.rst
|
||||
F: fs/orangefs/
|
||||
|
||||
ORINOCO DRIVER
|
||||
L: linux-wireless@vger.kernel.org
|
||||
S: Orphan
|
||||
W: https://wireless.wiki.kernel.org/en/users/Drivers/orinoco
|
||||
W: http://www.nongnu.org/orinoco/
|
||||
F: drivers/net/wireless/intersil/orinoco/
|
||||
|
||||
OV2659 OMNIVISION SENSOR DRIVER
|
||||
M: "Lad, Prabhakar" <prabhakar.csengg@gmail.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -12,7 +12,6 @@ config WLAN_VENDOR_INTERSIL
|
||||
|
||||
if WLAN_VENDOR_INTERSIL
|
||||
|
||||
source "drivers/net/wireless/intersil/orinoco/Kconfig"
|
||||
source "drivers/net/wireless/intersil/p54/Kconfig"
|
||||
|
||||
endif # WLAN_VENDOR_INTERSIL
|
||||
|
@ -1,3 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_HERMES) += orinoco/
|
||||
obj-$(CONFIG_P54_COMMON) += p54/
|
||||
|
@ -1,143 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
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 :
|
||||
<https://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:
|
||||
<https://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.
|
@ -1,15 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# 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
|
@ -1,268 +0,0 @@
|
||||
/* 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 <linux/mod_devicetable.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);
|
@ -1,291 +0,0 @@
|
||||
/* 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,
|
||||
NL80211_BAND_2GHZ);
|
||||
channels++;
|
||||
}
|
||||
}
|
||||
priv->band.channels = priv->channels;
|
||||
priv->band.n_channels = channels;
|
||||
|
||||
wiphy->bands[NL80211_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,
|
||||
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 != NL80211_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,
|
||||
};
|
@ -1,15 +0,0 @@
|
||||
/* 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 */
|
@ -1,387 +0,0 @@
|
||||
/* 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[]; /* 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
|
@ -1,21 +0,0 @@
|
||||
/* 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_ */
|
@ -1,778 +0,0 @@
|
||||
/* 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/net.h>
|
||||
#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
|
||||
|
||||
#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,
|
||||
.read_ltv_pr = 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,
|
||||
};
|
@ -1,534 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#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[];
|
||||
} __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 (*read_ltv_pr)(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_READ_RECORD_PR(hw, bap, rid, buf) \
|
||||
(hw->ops->read_ltv_pr((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_read_wordrec_pr(struct hermes *hw, int bap, u16 rid,
|
||||
u16 *word)
|
||||
{
|
||||
__le16 rec;
|
||||
int err;
|
||||
|
||||
err = HERMES_READ_RECORD_PR(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 */
|
@ -1,477 +0,0 @@
|
||||
/*
|
||||
* 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[]; /* 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[]; /* 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[]; /* 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;
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
* 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 */
|
@ -1,165 +0,0 @@
|
||||
#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
|
File diff suppressed because it is too large
Load Diff
@ -1,60 +0,0 @@
|
||||
/* 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, size_t key_len,
|
||||
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_ */
|
File diff suppressed because it is too large
Load Diff
@ -1,50 +0,0 @@
|
||||
/* 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_ */
|
@ -1,89 +0,0 @@
|
||||
/* 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 <crypto/hash.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_shash("michael_mic", 0, 0);
|
||||
if (IS_ERR(priv->tx_tfm_mic)) {
|
||||
printk(KERN_DEBUG "%s: could not allocate "
|
||||
"crypto API michael_mic\n", __func__);
|
||||
priv->tx_tfm_mic = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->rx_tfm_mic = crypto_alloc_shash("michael_mic", 0, 0);
|
||||
if (IS_ERR(priv->rx_tfm_mic)) {
|
||||
printk(KERN_DEBUG "%s: could not allocate "
|
||||
"crypto API michael_mic\n", __func__);
|
||||
priv->rx_tfm_mic = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void orinoco_mic_free(struct orinoco_private *priv)
|
||||
{
|
||||
if (priv->tx_tfm_mic)
|
||||
crypto_free_shash(priv->tx_tfm_mic);
|
||||
if (priv->rx_tfm_mic)
|
||||
crypto_free_shash(priv->rx_tfm_mic);
|
||||
}
|
||||
|
||||
int orinoco_mic(struct crypto_shash *tfm_michael, u8 *key,
|
||||
u8 *da, u8 *sa, u8 priority,
|
||||
u8 *data, size_t data_len, u8 *mic)
|
||||
{
|
||||
SHASH_DESC_ON_STACK(desc, tfm_michael);
|
||||
u8 hdr[ETH_HLEN + 2]; /* size of header + padding */
|
||||
int err;
|
||||
|
||||
if (tfm_michael == NULL) {
|
||||
printk(KERN_WARNING "%s: tfm_michael == NULL\n", __func__);
|
||||
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;
|
||||
|
||||
desc->tfm = tfm_michael;
|
||||
|
||||
err = crypto_shash_setkey(tfm_michael, key, MIC_KEYLEN);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = crypto_shash_init(desc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = crypto_shash_update(desc, hdr, sizeof(hdr));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = crypto_shash_update(desc, data, data_len);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = crypto_shash_final(desc, mic);
|
||||
shash_desc_zero(desc);
|
||||
|
||||
return err;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
/* Orinoco MIC helpers
|
||||
*
|
||||
* See copyright notice in main.c
|
||||
*/
|
||||
#ifndef _ORINOCO_MIC_H_
|
||||
#define _ORINOCO_MIC_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <crypto/hash.h>
|
||||
|
||||
#define MICHAEL_MIC_LEN 8
|
||||
|
||||
/* Forward declarations */
|
||||
struct orinoco_private;
|
||||
struct crypto_ahash;
|
||||
|
||||
int orinoco_mic_init(struct orinoco_private *priv);
|
||||
void orinoco_mic_free(struct orinoco_private *priv);
|
||||
int orinoco_mic(struct crypto_shash *tfm_michael, u8 *key,
|
||||
u8 *da, u8 *sa, u8 priority,
|
||||
u8 *data, size_t data_len, u8 *mic);
|
||||
|
||||
#endif /* ORINOCO_MIC_H */
|
@ -1,251 +0,0 @@
|
||||
/* 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 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_shash *rx_tfm_mic;
|
||||
struct crypto_shash *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);
|
||||
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, unsigned int txqueue);
|
||||
|
||||
/********************************************************************/
|
||||
/* 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 */
|
@ -1,350 +0,0 @@
|
||||
/* 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;
|
||||
int ret;
|
||||
|
||||
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;
|
||||
|
||||
ret = orinoco_cs_config(link);
|
||||
if (ret)
|
||||
goto err_free_orinocodev;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_orinocodev:
|
||||
free_orinocodev(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
@ -1,314 +0,0 @@
|
||||
/* 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,
|
||||
.driver.pm = &orinoco_pci_pm_ops,
|
||||
};
|
||||
|
||||
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);
|
@ -1,257 +0,0 @@
|
||||
/* 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,
|
||||
.driver.pm = &orinoco_pci_pm_ops,
|
||||
};
|
||||
|
||||
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);
|
@ -1,54 +0,0 @@
|
||||
/* 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;
|
||||
};
|
||||
|
||||
static int __maybe_unused orinoco_pci_suspend(struct device *dev_d)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev_d);
|
||||
struct orinoco_private *priv = pci_get_drvdata(pdev);
|
||||
|
||||
orinoco_down(priv);
|
||||
free_irq(pdev->irq, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused orinoco_pci_resume(struct device *dev_d)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev_d);
|
||||
struct orinoco_private *priv = pci_get_drvdata(pdev);
|
||||
struct net_device *dev = priv->ndev;
|
||||
int err;
|
||||
|
||||
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);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return orinoco_up(priv);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(orinoco_pci_pm_ops,
|
||||
orinoco_pci_suspend,
|
||||
orinoco_pci_resume);
|
||||
|
||||
#endif /* _ORINOCO_PCI_H */
|
@ -1,362 +0,0 @@
|
||||
/* 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,
|
||||
.driver.pm = &orinoco_pci_pm_ops,
|
||||
};
|
||||
|
||||
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);
|
@ -1,237 +0,0 @@
|
||||
/* 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,
|
||||
.driver.pm = &orinoco_pci_pm_ops,
|
||||
};
|
||||
|
||||
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);
|
File diff suppressed because it is too large
Load Diff
@ -1,259 +0,0 @@
|
||||
/* 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), NL80211_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, NL80211_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) {
|
||||
struct cfg80211_scan_info info = {
|
||||
.aborted = abort,
|
||||
};
|
||||
|
||||
cfg80211_scan_done(priv->scan_request, &info);
|
||||
priv->scan_request = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void orinoco_scan_done(struct orinoco_private *priv, bool abort)
|
||||
{
|
||||
if (priv->scan_request) {
|
||||
struct cfg80211_scan_info info = {
|
||||
.aborted = abort,
|
||||
};
|
||||
|
||||
cfg80211_scan_done(priv->scan_request, &info);
|
||||
priv->scan_request = NULL;
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
/* 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_ */
|
@ -1,328 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
int ret;
|
||||
|
||||
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;
|
||||
|
||||
ret = spectrum_cs_config(link);
|
||||
if (ret)
|
||||
goto err_free_orinocodev;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_orinocodev:
|
||||
free_orinocodev(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* Mark the device as stopped, to block IO until later */
|
||||
orinoco_down(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
File diff suppressed because it is too large
Load Diff
@ -1,13 +0,0 @@
|
||||
/* 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_ */
|
Loading…
Reference in New Issue
Block a user